diff --git a/.travis.yml b/.travis.yml index 78fe604..e4fcf32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,9 @@ +# safelist +branches: + only: + - master + - devel + language: c dist: focal os: linux @@ -17,20 +23,20 @@ before_install: - tar xf arduino-1.8.16-linux64.tar.xz - sudo mv arduino-1.8.16 /usr/local/share/arduino - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - - wget https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip + - wget --secure-protocol=TLSv1_2 https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip - unzip master.zip - rm master.zip - sudo mv ESPAsyncWebServer-master /usr/local/share/arduino/libraries/ESPAsyncWebServer - - wget https://github.com/me-no-dev/AsyncTCP/archive/master.zip + - wget --secure-protocol=TLSv1_2 https://github.com/me-no-dev/AsyncTCP/archive/master.zip - unzip master.zip - rm master.zip - sudo mv AsyncTCP-master /usr/local/share/arduino/libraries/AsyncTCP - - wget https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip - - wget https://github.com/lewisxhe/AXP202X_Library/archive/refs/tags/V1.1.3.zip + - wget --secure-protocol=TLSv1_2 https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip + - wget --secure-protocol=TLSv1_2 https://github.com/lewisxhe/AXP202X_Library/archive/refs/tags/V1.1.3.zip - unzip V1.1.3.zip - sudo mv AXP202X_Library-1.1.3 /usr/local/share/arduino/libraries/ - - wget https://github.com/dx168b/async-mqtt-client/archive/master.zip + - wget --secure-protocol=TLSv1_2 https://github.com/dx168b/async-mqtt-client/archive/master.zip - unzip master.zip - rm master.zip - sudo mv async-mqtt-client-master /usr/local/share/arduino/libraries/ @@ -53,9 +59,9 @@ install: - arduino --pref "build.path=$PWD/build" --save-prefs - arduino --install-boards esp32:esp32:1.0.6 --save-prefs - ln -s $PWD/libraries/SondeLib /usr/local/share/arduino/libraries/SondeLib - - arduino --install-library "U8g2:2.29.11" + - arduino --install-library "U8g2:2.34.22" - arduino --install-library "MicroNMEA" - - arduino --install-library "GFX Library for Arduino:1.1.5" + - arduino --install-library "GFX Library for Arduino:1.2.9" script: - arduino --board esp32:esp32:t-beam --verify $PWD/RX_FSK/RX_FSK.ino - ESPPATH=`arduino --get-pref runtime.tools.xtensa-esp32-elf-gcc.path` diff --git a/Licenses/Exceptions b/Licenses/Exceptions index f35cddb..94ac495 100644 --- a/Licenses/Exceptions +++ b/Licenses/Exceptions @@ -1,11 +1,51 @@ -GPL License Exceptions: +GPL License Exceptions -- The SX1278FSK library is based on +Code in the repository: + +- The SX1278FSK library (src/SX1278FSK.cpp) is based on https://github.com/pdelmo/lora_shield_arduino.git and licensed under GNU Lesser General Public License, version 2.1 (SPDX short identifier: LGPL-2.1) -- Leaflet sidebar v2 plugin is (c) 2013 Tobias Bieniek based on - https://github.com/Turbo87/sidebar-v2/ - and licensed under - MIT License +- General purpose Reed-Solomon decoder for 8-bit symbols or less + Copyright 2003 Phil Karn, KA9Q + May be used under the terms of the GNU Lesser General Public License (LGPL) - lgpl-2.1.txt + +- some of the decoder code is more or less loosly inspired by + * oe5dxl aprs toolchain (http://oe5dxl.hamspirit.at:8025), licensed under GNU GPL (gpl-3.0.txt) + * Zilogs RS decoder (https://github.com/rs1729/RS), licensed under GNU GPL (gpl-3.0.txt) + +- Fonts in src/fonts (FreeMono*, FreeSans*, Picopixel) taken from Adafruit-GFX-Library + licensed under BSD License + Font src/fonts/Terminal11x16.h taken from https://github.com/Nkawu/TFT_22_ILI9225/ + licensed under GPL-3.0 + +External libraries used by the project: + +- Espressif 32: development platform for PlatformIO + https://github.com/platformio/platform-espressif32 + licensed under Apache-2.0 licsense - apache.txt + +- U8glib library for monochrome displays, version 2 + https://github.com/olikraus/u8g2 + licensed under the new-bsd license (two-clause bsd license) - LICENSE-u8g2.txt + +- MicroNMEA - A compact Arduino library to parse NMEA sentences + https://github.com/stevemarple/MicroNMEA + licsensed under LGPL-2.1 license - lgpl-2.1.txt + +- ESPAsyncWebServer + see https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/src/ESPAsyncWebServer.h#L7-L19 + licsensed under LGPL-2.1 license - lgpl-2.1.txt + +- Arduino GFX developing for various color displays + https://github.com/moononournation/Arduino_GFX + licensed under BSD License - LICENSE-Arduino_GFX.txt + +- asynchronous MQTT client implementation + https://github.com/dx168b/async-mqtt-client + licensed under MIT license - LICENSE-mqtt.txt + +- Time library for Arduino + https://github.com/PaulStoffregen/Time + licensed under LGPL-2.1 licsense - lgpl-2.1txt diff --git a/Licenses/LICENSE-Arduino_GFX.txt b/Licenses/LICENSE-Arduino_GFX.txt new file mode 100644 index 0000000..7492e93 --- /dev/null +++ b/Licenses/LICENSE-Arduino_GFX.txt @@ -0,0 +1,24 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Licenses/LICENSE-mqtt.txt b/Licenses/LICENSE-mqtt.txt new file mode 100644 index 0000000..a6183c6 --- /dev/null +++ b/Licenses/LICENSE-mqtt.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Marvin Roger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Licenses/LICENSE-u8g2.txt b/Licenses/LICENSE-u8g2.txt new file mode 100644 index 0000000..519ca42 --- /dev/null +++ b/Licenses/LICENSE-u8g2.txt @@ -0,0 +1,79 @@ + +The U8g2lib code (https://github.com/olikraus/u8g2) is licensed under the terms of +the new-bsd license (two-clause bsd license). +See also: http://www.opensource.org/licenses/bsd-license.php + +Fonts are licensed under different conditions. +See + https://github.com/olikraus/u8g2/wiki/fntgrp +for detailed information on the licensing conditions for each font. + +The example code in sys/raspi_gpio/hal will use the bcm2835 lib from Mike McCauley +which is licensed under GPL V3: http://www.airspayce.com/mikem/bcm2835/ + +============ X11 Fonts COUR, HELV, NCEN, TIM, SYMB ============ + +For fonts derived from the following files, the license below applies. +COURB08.BDF COURB10.BDF COURB12.BDF COURB14.BDF COURB18.BDF +COURB24.BDF COURR08.BDF COURR10.BDF COURR12.BDF COURR14.BDF +COURR18.BDF COURR24.BDF HELVB08.BDF HELVB10.BDF HELVB12.BDF HELVB14.BDF +HELVB18.BDF HELVB24.BDF HELVR08.BDF HELVR10.BDF HELVR12.BDF HELVR14.BDF +HELVR18.BDF HELVR24.BDF NCENB08.BDF NCENB10.BDF NCENB12.BDF +NCENB14.BDF NCENB18.BDF NCENB24.BDF NCENR08.BDF NCENR10.BDF +NCENR12.BDF NCENR14.BDF NCENR18.BDF NCENR24.BDF SYMB08.BDF SYMB10.BDF +SYMB12.BDF SYMB14.BDF SYMB18.BDF SYMB24.BDF TIMB08.BDF TIMB10.BDF +TIMB12.BDF TIMB14.BDF TIMB18.BDF TIMB24.BDF TIMR08.BDF TIMR10.BDF +TIMR12.BDF TIMR14.BDF TIMR18.BDF TIMR24.BDF + +Copyright 1984-1989, 1994 Adobe Systems Incorporated. +Copyright 1988, 1994 Digital Equipment Corporation. + +Adobe is a trademark of Adobe Systems Incorporated which may be +registered in certain jurisdictions. +Permission to use these trademarks is hereby granted only in +association with the images described in this file. + +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notices appear in all +copies and that both those copyright notices and this permission +notice appear in supporting documentation, and that the names of +Adobe Systems and Digital Equipment Corporation not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. Adobe Systems and +Digital Equipment Corporation make no representations about the +suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + + +============ BSD License for U8g2lib Code ============ + +Universal 8bit Graphics Library (https://github.com/olikraus/u8g2) + +Copyright (c) 2016, olikraus@gmail.com +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Licenses/apache.txt b/Licenses/apache.txt new file mode 100644 index 0000000..2f79a3b --- /dev/null +++ b/Licenses/apache.txt @@ -0,0 +1,174 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index f0904cc..975c6da 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1,7 +1,6 @@ #include "features.h" #include "version.h" -#include "axp20x.h" #include #include #include @@ -9,7 +8,6 @@ #include #include #include -#include #include #include "esp_heap_caps.h" #include "soc/rtc_wdt.h" @@ -18,22 +16,31 @@ #include "src/Sonde.h" #include "src/Display.h" #include "src/Scanner.h" -#include "src/geteph.h" #if FEATURE_RS92 +#include "src/geteph.h" #include "src/rs92gps.h" #endif -#include "src/aprs.h" +// Not needed here, included by connector #include "src/aprs.h" #include "src/ShFreqImport.h" #include "src/RS41.h" #include "src/DFM.h" #include "src/json.h" -#if FEATURE_CHASEMAPPER -#include "src/Chasemapper.h" -#endif -//#include "NimBLEDevice.h" +#include "src/posinfo.h" +#include "src/pmu.h" + +/* Data exchange connectors */ +#if FEATURE_CHASEMAPPER +#include "src/conn-chasemapper.h" +#endif #if FEATURE_MQTT -#include "src/mqtt.h" +#include "src/conn-mqtt.h" +#endif +#if FEATURE_SDCARD +#include "src/conn-sdcard.h" +#endif +#if FEATURE_APRS +#include "src/conn-aprs.h" #endif //#define ESP_MEM_DEBUG 1 @@ -45,11 +52,9 @@ const char *mainStateStr[5] = {"DECODER", "SPECTRUM", "WIFISCAN", "UPDATE", "TOU AsyncWebServer server(80); -AXP20X_Class axp; -#define PMU_IRQ 35 +PMU *pmu = NULL; SemaphoreHandle_t axpSemaphore; -// 0: cleared; 1: set; 2: do not check, also query state of axp via i2c on each loop -uint8_t pmu_irq = 0; +extern uint8_t pmu_irq; const char *updateHost = "rdzsonde.mooo.com"; int updatePort = 80; @@ -72,24 +77,6 @@ WiFiClient client; /* Sonde.h: enum SondeType { STYPE_DFM,, STYPE_RS41, STYPE_RS92, STYPE_M10M20, STYPE_M10, STYPE_M20, STYPE_MP3H }; */ const char *sondeTypeStrSH[NSondeTypes] = { "DFM", "RS41", "RS92", "Mxx"/*never sent*/, "M10", "M20", "MRZ" }; -#if 0 -// not used any more -const char *dfmSubtypeStrSH[16] = { NULL, NULL, NULL, NULL, NULL, NULL, - "DFM06", // 0x06 - "PS15", // 0x07 - NULL, NULL, - "DFM09", // 0x0A - "DFM17", // 0x0B - "DFM09P", // 0x0C - "DFM17", // 0x0D - NULL, NULL - }; -#endif - -// Times in ms, i.e. station: 10 minutes, mobile: 20 seconds -#define APRS_STATION_UPDATE_TIME (10*60*1000) -#define APRS_MOBILE_STATION_UPDATE_TIME (20*1000) -unsigned long time_last_aprs_update = -APRS_STATION_UPDATE_TIME; #if FEATURE_SONDEHUB #define SONDEHUB_STATION_UPDATE_TIME (60*60*1000) // 60 min @@ -99,34 +86,13 @@ int shImportInterval = 0; char shImport = 0; unsigned long time_last_update = 0; #endif -/* SH_LOC_OFF: never send position information to SondeHub - SH_LOC_FIXED: send fixed position (if specified in config) to sondehub - SH_LOC_CHASE: always activate chase mode and send GPS position (if available) - SH_LOC_AUTO: if there is no valid GPS position, or GPS position < MIN_LOC_AUTO_DIST away from known fixed position: use FIXED mode - otherwise, i.e. if there is a valid GPS position and (either no fixed position in config, or GPS position is far away from fixed position), use CHASE mode. -*/ -// same constants used for SondeHub and APRS -enum { SH_LOC_OFF, SH_LOC_FIXED, SH_LOC_CHASE, SH_LOC_AUTO }; -/* auto mode is chase if valid GPS position and (no fixed location entered OR valid GPS position and distance in lat/lon deg to fixed location > threshold) */ -#define MIN_LOC_AUTO_DIST 200 /* meter */ -#define SH_LOC_AUTO_IS_CHASE ( gpsPos.valid && ( (isnan(sonde.config.rxlat) || isnan(sonde.config.rxlon) ) || \ - calcLatLonDist( gpsPos.lat, gpsPos.lon, sonde.config.rxlat, sonde.config.rxlon ) > MIN_LOC_AUTO_DIST ) ) -extern float calcLatLonDist(float lat1, float lon1, float lat2, float lon2); -// KISS over TCP for communicating with APRSdroid -WiFiServer tncserver(14580); -WiFiClient tncclient; // JSON over TCP for communicating with the rdzSonde (rdzwx-go) Android app WiFiServer rdzserver(14570); WiFiClient rdzclient; -// APRS over TCP for radiosondy.info etc -AsyncClient tcpclient; -#if FEATURE_MQTT -unsigned long lastMqttUptime = 0; -boolean mqttEnabled; -MQTT mqttclient; -#endif + + boolean forceReloadScreenConfig = false; enum KeyPress { KP_NONE = 0, KP_SHORT, KP_DOUBLE, KP_MID, KP_LONG }; @@ -153,8 +119,7 @@ static unsigned long specTimer; void enterMode(int mode); void WiFiEvent(WiFiEvent_t event); -char buffer[85]; -MicroNMEA nmea(buffer, sizeof(buffer)); + // Read line from file, independent of line termination (LF or CR LF) String readLine(Stream &stream) { @@ -183,6 +148,7 @@ int readLine(Stream &stream, char *buffer, int maxlen) { String processor(const String& var) { Serial.println(var); if (var == "MAPCENTER") { +#if 0 double lat, lon; if (gpsPos.valid) { lat = gpsPos.lat; @@ -192,9 +158,11 @@ String processor(const String& var) { lat = sonde.config.rxlat; lon = sonde.config.rxlon; } - if ( !isnan(lat) && !isnan(lon) ) { + //if ( !isnan(lat) && !isnan(lon) ) { +#endif + if ( posInfo.valid ) { char p[40]; - snprintf(p, 40, "%g,%g", lat, lon); + snprintf(p, 40, "%g,%g", posInfo.lat, posInfo.lon); return String(p); } else { return String("48,13"); @@ -225,7 +193,11 @@ String processor(const String& var) { return String(tmpstr); } if (var == "EPHSTATE") { +#if FEATURE_RS92 return String(ephtxt[ephstate]); +#else + return String("Not supported"); +#endif } return String(); } @@ -250,7 +222,7 @@ const String sondeTypeSelect(int activeType) { //trying to work around //"assertion "heap != NULL && "free() target pointer is outside heap areas"" failed:" // which happens if request->send is called in createQRGForm!?!?? -char message[10240 * 4-2048]; //needs to be large enough for all forms (not checked in code) +char message[10240 * 3 - 2048]; //needs to be large enough for all forms (not checked in code) // QRG form is currently about 24kb with 100 entries ///////////////////////// Functions for Reading / Writing QRG list from/to qrg.txt @@ -317,7 +289,7 @@ void HTMLBODYEND(char *ptr) { } void HTMLSAVEBUTTON(char *ptr) { strcat(ptr, "
" - "rdzTTGOserver "); + "rdzTTGOserver "); strcat(ptr, version_id); strcat(ptr, ""); } @@ -343,7 +315,7 @@ const char *createQRGForm() { return message; } -const char *handleQRGPost(AsyncWebServerRequest *request) { +const char *handleQRGPost(AsyncWebServerRequest * request) { char label[10]; // parameters: a_i, f_1, t_i (active/frequency/type) File file = SPIFFS.open("/qrg.txt", "w"); @@ -451,7 +423,7 @@ const char *createWIFIForm() { } #if 0 - // moved to map.html (active warning is still TODO +// moved to map.html (active warning is still TODO const char *createSondeHubMap() { SondeInfo *s = &sonde.sondeList[0]; char *ptr = message; @@ -476,7 +448,7 @@ const char *createSondeHubMap() { } #endif -const char *handleWIFIPost(AsyncWebServerRequest *request) { +const char *handleWIFIPost(AsyncWebServerRequest * request) { char label[10]; // parameters: a_i, f_1, t_i (active/frequency/type) #if 1 @@ -557,7 +529,7 @@ const char *createStatusForm() { } } strcat(ptr, "
" - "rdzTTGOserver "); + "rdzTTGOserver "); strcat(ptr, version_id); strcat(ptr, ""); @@ -572,7 +544,7 @@ const char *createLiveJson() { strcpy(ptr, "{\"sonde\": {"); // use the same JSON format here as for MQTT and for the Android App - sonde2json( ptr+strlen(ptr), 1024, s ); + sonde2json( ptr + strlen(ptr), 1024, s ); #if 0 sprintf(ptr + strlen(ptr), "\"sonde\": {\"rssi\": %d, \"vframe\": %d, \"time\": %d,\"id\": \"%s\", \"freq\": %3.3f, \"type\": \"%s\"", s->rssi, s->d.vframe, s->d.time, s->d.id, s->freq, sondeTypeStr[sonde.realType(s)]); @@ -591,15 +563,10 @@ const char *createLiveJson() { sprintf(ptr + strlen(ptr), ", \"launchsite\": \"%s\", \"res\": %d }", s->launchsite, s->rxStat[0]); #endif strcat(ptr, " }"); - if (gpsPos.valid) { - sprintf(ptr + strlen(ptr), ", \"gps\": {\"lat\": %g, \"lon\": %g, \"alt\": %d, \"sat\": %d, \"speed\": %g, \"dir\": %d, \"hdop\": %d }", gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.sat, gpsPos.speed, gpsPos.course, gpsPos.hdop); + + if (posInfo.valid) { + sprintf(ptr + strlen(ptr), ", \"gps\": {\"lat\": %g, \"lon\": %g, \"alt\": %d, \"sat\": %d, \"speed\": %g, \"dir\": %d, \"hdop\": %d }", posInfo.lat, posInfo.lon, posInfo.alt, posInfo.sat, posInfo.speed, posInfo.course, posInfo.hdop); //} - } else { - // no GPS position, but maybe a fixed position - if ((!isnan(sonde.config.rxlat)) && (!isnan(sonde.config.rxlon))) { - int alt = isnan(sonde.config.rxalt) ? 0 : (int)sonde.config.rxalt; - sprintf(ptr + strlen(ptr), ", \"gps\": {\"lat\": %g, \"lon\": %g, \"alt\": %d, \"sat\": 0, \"speed\": 0, \"dir\": 0, \"hdop\": 0 }", sonde.config.rxlat, sonde.config.rxlon, alt); - } } strcat(ptr, "}"); @@ -655,19 +622,21 @@ struct st_configitems config_list[] = { {"m10m20.rxbw", 0, &sonde.config.m10m20.rxbw}, {"mp3h.agcbw", 0, &sonde.config.mp3h.agcbw}, {"mp3h.rxbw", 0, &sonde.config.mp3h.rxbw}, - {"ephftp", 39, &sonde.config.ephftp}, + {"ephftp", 79, &sonde.config.ephftp}, /* APRS settings */ {"call", 9, sonde.config.call}, {"passcode", 0, &sonde.config.passcode}, /* KISS tnc settings */ {"kisstnc.active", 0, &sonde.config.kisstnc.active}, +#if FEATURE_APRS /* AXUDP settings */ {"axudp.active", -3, &sonde.config.udpfeed.active}, {"axudp.host", 63, sonde.config.udpfeed.host}, {"axudp.port", 0, &sonde.config.udpfeed.port}, {"axudp.highrate", 0, &sonde.config.udpfeed.highrate}, - /* APRS TCP settings, current not used */ + /* APRS TCP settings */ {"tcp.active", -3, &sonde.config.tcpfeed.active}, + {"tcp.timeout", 0, &sonde.config.tcpfeed.timeout}, {"tcp.host", 63, sonde.config.tcpfeed.host}, {"tcp.port", 0, &sonde.config.tcpfeed.port}, {"tcp.chase", 0, &sonde.config.chase}, @@ -675,6 +644,7 @@ struct st_configitems config_list[] = { {"tcp.objcall", 9, sonde.config.objcall}, {"tcp.beaconsym", 4, sonde.config.beaconsym}, {"tcp.highrate", 0, &sonde.config.tcpfeed.highrate}, +#endif #if FEATURE_CHASEMAPPER /* Chasemapper settings */ {"cm.active", -3, &sonde.config.cm.active}, @@ -836,6 +806,7 @@ const char *handleConfigPost(AsyncWebServerRequest * request) { f.close(); Serial.printf("Re-reading file file\n"); setupConfigData(); + if (!gpsPos.valid) fixedToPosInfo(); // TODO: Check if this is better done elsewhere? // Use new config (whereever this is feasible without a reboot) disp.setContrast(); @@ -865,7 +836,7 @@ const char *createControlForm() { } } strcat(ptr, "
" - "rdzTTGOserver "); + "rdzTTGOserver "); strcat(ptr, version_id); strcat(ptr, ""); HTMLBODYEND(ptr); @@ -1209,13 +1180,13 @@ void SetupAsyncServer() { }); -// server.on("/map.html", HTTP_GET, [](AsyncWebServerRequest * request) { -// request->send(200, "text/html", createSondeHubMap()); -// }); -// server.on("/map.html", HTTP_POST, [](AsyncWebServerRequest * request) { -// handleWIFIPost(request); -// request->send(200, "text/html", createSondeHubMap()); -// }); + // server.on("/map.html", HTTP_GET, [](AsyncWebServerRequest * request) { + // request->send(200, "text/html", createSondeHubMap()); + // }); + // server.on("/map.html", HTTP_POST, [](AsyncWebServerRequest * request) { + // handleWIFIPost(request); + // request->send(200, "text/html", createSondeHubMap()); + // }); server.on("/config.html", HTTP_GET, [](AsyncWebServerRequest * request) { request->send(200, "text/html", createConfigForm()); @@ -1434,167 +1405,6 @@ void initTouch() { - -/// Arrg. MicroNMEA changes type definition... so lets auto-infer type -template -//void unkHandler(const MicroNMEA& nmea) { -void unkHandler(T nmea) { - if (strcmp(nmea.getMessageID(), "VTG") == 0) { - const char *s = nmea.getSentence(); - while (*s && *s != ',') s++; - if (*s == ',') s++; else return; - if (*s == ',') return; /// no new course data - int lastCourse = nmea.parseFloat(s, 0, NULL); - Serial.printf("Course update: %d\n", lastCourse); - } else if (strcmp(nmea.getMessageID(), "GST") == 0) { - // get horizontal accuracy for android app on devices without gps - // GPGST,time,rms,-,-,-,stdlat,stdlon,stdalt,cs - const char *s = nmea.getSentence(); - while (*s && *s != ',') s++; // #0: GST - if (*s == ',') s++; else return; - while (*s && *s != ',') s++; // #1: time: skip - if (*s == ',') s++; else return; - while (*s && *s != ',') s++; // #1: rms: skip - if (*s == ',') s++; else return; - while (*s && *s != ',') s++; // #1: (-): skip - if (*s == ',') s++; else return; - while (*s && *s != ',') s++; // #1: (-): skip - if (*s == ',') s++; else return; - while (*s && *s != ',') s++; // #1: (-): skip - if (*s == ',') s++; else return; - // stdlat - int stdlat = nmea.parseFloat(s, 1, NULL); - while (*s && *s != ',') s++; - if (*s == ',') s++; else return; - // stdlong - int stdlon = nmea.parseFloat(s, 1, NULL); - // calculate position error as 1-signma horizontal RMS - // I guess that is equivalent to Androids getAccurac()? - int poserr = 0; - if (stdlat < 10000 && stdlon < 10000) { // larger errors: no GPS fix, avoid overflow in * - poserr = (int)(sqrt(0.5 * (stdlat * stdlat + stdlon * stdlon))); - } - //Serial.printf("\nHorizontal accuracy: %d, %d => %.1fm\n", stdlat, stdlon, 0.1*poserr); - gpsPos.accuracy = poserr; - } -} - -//#define DEBUG_GPS -static bool gpsCourseOld; -static int lastCourse; -void gpsTask(void *parameter) { - nmea.setUnknownSentenceHandler(unkHandler); - - while (1) { - while (Serial2.available()) { - char c = Serial2.read(); - //Serial.print(c); - if (nmea.process(c)) { - gpsPos.valid = nmea.isValid(); - if (gpsPos.valid) { - gpsPos.lon = nmea.getLongitude() * 0.000001; - gpsPos.lat = nmea.getLatitude() * 0.000001; - long alt = 0; - nmea.getAltitude(alt); - gpsPos.alt = (int)(alt / 1000); - gpsPos.course = (int)(nmea.getCourse() / 1000); - gpsCourseOld = false; - if (gpsPos.course == 0) { - // either north or not new - if (lastCourse != 0) // use old value... - { - gpsCourseOld = true; - gpsPos.course = lastCourse; - } - } - if(gpsPos.lon == 0 && gpsPos.lat == 0) gpsPos.valid = false; - } - gpsPos.hdop = nmea.getHDOP(); - gpsPos.sat = nmea.getNumSatellites(); - gpsPos.speed = nmea.getSpeed() / 1000.0 * 0.514444; // speed is in m/s nmea.getSpeed is in 0.001 knots -#ifdef DEBUG_GPS - uint8_t hdop = nmea.getHDOP(); - Serial.printf(" =>: valid: %d N %f E %f alt %d course:%d dop:%d\n", gpsPos.valid ? 1 : 0, gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.course, hdop); -#endif - } - } - delay(50); - } -} - -#define UBX_SYNCH_1 0xB5 -#define UBX_SYNCH_2 0x62 -uint8_t ubx_set9k6[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F}; -uint8_t ubx_factorydef[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0x7c }; -uint8_t ubx_hardreset[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x04, 4, 0, 0xff, 0xff, 0, 0, 0x0C, 0x5D }; -// GPGST: Class 0xF0 Id 0x07 -uint8_t ubx_enable_gpgst[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x01, 3, 0, 0xF0, 0x07, 2, 0x03, 0x1F}; - -void dumpGPS() { - while (Serial2.available()) { - char c = Serial2.read(); - Serial.printf("%02x ", (uint8_t)c); - } -} -void initGPS() { - if (sonde.config.gps_rxd < 0) return; // GPS disabled - if (sonde.config.gps_txd >= 0) { // TX enable, thus try setting baud to 9600 and do a factory reset - File testfile = SPIFFS.open("/GPSRESET", FILE_READ); - if (testfile && !testfile.isDirectory()) { - testfile.close(); - Serial.println("GPS resetting baud to 9k6..."); - /* TODO: debug: - Sometimes I have seen the Serial2.begin to cause a reset - Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1) - Backtrace: 0x40081d2f:0x3ffc11b0 0x40087969:0x3ffc11e0 0x4000bfed:0x3ffb1db0 0x4008b7dd:0x3ffb1dc0 0x4017afee:0x3ffb1de0 0x4017b04b:0x3ffb1e20 0x4010722b:0x3ffb1e50 0x40107303:0x3ffb1e70 0x4010782d:0x3ffb1e90 0x40103814:0x3ffb1ed0 0x400d8772:0x3ffb1f10 0x400d9057:0x3ffb1f60 0x40107aca:0x3ffb1fb0 0x4008a63e:0x3ffb1fd0 - #0 0x40081d2f:0x3ffc11b0 in _uart_isr at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 - #1 0x40087969:0x3ffc11e0 in _xt_lowint1 at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/xtensa_vectors.S:1154 - #2 0x4000bfed:0x3ffb1db0 in ?? ??:0 - #3 0x4008b7dd:0x3ffb1dc0 in vTaskExitCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/tasks.c:3507 - #4 0x4017afee:0x3ffb1de0 in esp_intr_alloc_intrstatus at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/intr_alloc.c:784 - #5 0x4017b04b:0x3ffb1e20 in esp_intr_alloc at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/intr_alloc.c:784 - #6 0x4010722b:0x3ffb1e50 in uartEnableInterrupt at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 - #7 0x40107303:0x3ffb1e70 in uartAttachRx at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 - #8 0x4010782d:0x3ffb1e90 in uartBegin at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 - #9 0x40103814:0x3ffb1ed0 in HardwareSerial::begin(unsigned long, unsigned int, signed char, signed char, bool, unsigned long) at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/HardwareSerial.cpp:190 - */ - Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); - delay(200); - Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); - delay(200); - Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); - Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - delay(1000); - dumpGPS(); - Serial.println("GPS factory reset..."); - Serial2.write(ubx_factorydef, sizeof(ubx_factorydef)); - delay(1000); - dumpGPS(); - delay(1000); - dumpGPS(); - delay(1000); - dumpGPS(); - SPIFFS.remove("/GPSRESET"); - } else if (testfile) { - Serial.println("GPS reset file: not found/isdir"); - testfile.close(); - Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - } - // Enable GPGST messages - Serial2.write(ubx_enable_gpgst, sizeof(ubx_enable_gpgst)); - } else { - Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); - } - xTaskCreate( gpsTask, "gpsTask", - 5000, /* stack size */ - NULL, /* paramter */ - 1, /* priority */ - NULL); /* task handle*/ -} - const char *getStateStr(int what) { if (what < 0 || what >= (sizeof(mainStateStr) / sizeof(const char *))) return "--"; @@ -1782,23 +1592,10 @@ int getKeyPress() { void handlePMUirq() { if (sonde.config.button2_axp) { // Use AXP power button as second button - if (pmu_irq) { - Serial.println("PMU_IRQ is set\n"); - xSemaphoreTake( axpSemaphore, portMAX_DELAY ); - axp.readIRQ(); - if (axp.isPEKShortPressIRQ()) { - button2.pressed = KP_SHORT; - button2.keydownTime = my_millis(); - } - if (axp.isPEKLongtPressIRQ()) { - button2.pressed = KP_MID; - button2.keydownTime = my_millis(); - } - if (pmu_irq != 2) { - pmu_irq = 0; - } - axp.clearIRQ(); - xSemaphoreGive( axpSemaphore ); + int key = pmu->handleIRQ(); + if (key > 0) { + button2.pressed = (KeyPress)key; + button2.keydownTime = my_millis(); } } else { Serial.println("handlePMIirq() called. THIS SHOULD NOT HAPPEN w/o button2_axp set"); @@ -1829,7 +1626,7 @@ int getKeyPressEvent() { #define SSD1306_ADDRESS 0x3c bool ssd1306_found = false; -bool axp192_found = false; +bool axp_found = false; int scanI2Cdevice(void) { @@ -1850,9 +1647,9 @@ int scanI2Cdevice(void) ssd1306_found = true; Serial.println("ssd1306 display found"); } - if (addr == AXP192_SLAVE_ADDRESS) { - axp192_found = true; - Serial.println("axp192 PMU found"); + if (addr == AXP192_SLAVE_ADDRESS) { // Same for AXP2101 + axp_found = true; + Serial.println("axp2101 PMU found"); } } else if (err == 4) { Serial.print("Unknow error at address 0x"); @@ -1909,7 +1706,6 @@ void setup() } Serial.println(" (before setup)"); sonde.defaultConfig(); // including autoconfiguration - aprs_gencrctab(); Serial.println("Initializing SPIFFS"); // Initialize SPIFFS @@ -1934,71 +1730,24 @@ void setup() delay(500); scanI2Cdevice(); - if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { - Serial.println("AXP192 Begin PASS"); - } else { - Serial.println("AXP192 Begin FAIL"); - } - axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); - if (sonde.config.type == TYPE_M5_CORE2) { - // Display backlight on M5 Core2 - axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON); - axp.setDCDC3Voltage(3300); - // SetBusPowerMode(0): - // #define AXP192_GPIO0_CTL (0x90) - // #define AXP192_GPIO0_VOL (0x91) - // #define AXP202_LDO234_DC23_CTL (0x12) - // The axp class lacks a functino to set GPIO0 VDO to 3.3V (as is done by original M5Stack software) - // so do this manually (default value 2.8V did not have the expected effect :)) - // data = Read8bit(0x91); - // write1Byte(0x91, (data & 0X0F) | 0XF0); - uint8_t reg; - Wire.beginTransmission((uint8_t)AXP192_SLAVE_ADDRESS); - Wire.write(AXP192_GPIO0_VOL); - Wire.endTransmission(); - Wire.requestFrom(AXP192_SLAVE_ADDRESS, 1); - reg = Wire.read(); - reg = (reg&0x0F) | 0xF0; - Wire.beginTransmission((uint8_t)AXP192_SLAVE_ADDRESS); - Wire.write(AXP192_GPIO0_VOL); - Wire.write(reg); - Wire.endTransmission(); - // data = Read8bit(0x90); - // Write1Byte(0x90, (data & 0XF8) | 0X02) - axp.setGPIOMode(AXP_GPIO_0, AXP_IO_LDO_MODE); // disable AXP supply from VBUS - pmu_irq = 2; // IRQ pin is not connected on Core2 - // data = Read8bit(0x12); //read reg 0x12 - // Write1Byte(0x12, data | 0x40); // enable 3,3V => 5V booster - // this is done below anyway: axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); - - axp.adc1Enable(AXP202_ACIN_VOL_ADC1, 1); - axp.adc1Enable(AXP202_ACIN_CUR_ADC1, 1); - } else { - // GPS on T-Beam, buzzer on M5 Core2 - axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); - axp.adc1Enable(AXP202_VBUS_VOL_ADC1, 1); - axp.adc1Enable(AXP202_VBUS_CUR_ADC1, 1); - } - axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); - axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); - axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); - axp.setDCDC1Voltage(3300); - axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1); - if (sonde.config.button2_axp ) { - if (pmu_irq != 2) { - pinMode(PMU_IRQ, INPUT_PULLUP); - attachInterrupt(PMU_IRQ, [] { - pmu_irq = 1; - }, FALLING); + if (!pmu) { + pmu = PMU::getInstance(Wire); + if (pmu) { + Serial.println("PMU found"); + pmu->init(); + if (sonde.config.button2_axp ) { + //axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1); + //axp.enableIRQ( AXP202_PEK_LONGPRESS_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1 ); + //axp.clearIRQ(); + pmu->disableAllIRQ(); + pmu->enableIRQ(); + } + int ndevices = scanI2Cdevice(); + if (sonde.fingerprint != 17 || ndevices > 0) break; // only retry for fingerprint 17 (startup problems of new t-beam with oled) + delay(500); } - //axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1); - axp.enableIRQ( AXP202_PEK_LONGPRESS_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1 ); - axp.clearIRQ(); } - int ndevices = scanI2Cdevice(); - if (sonde.fingerprint != 17 || ndevices > 0) break; // only retry for fingerprint 17 (startup problems of new t-beam with oled) - delay(500); } } if (sonde.config.batt_adc >= 0) { @@ -2164,7 +1913,11 @@ void setup() NULL); /* task handle*/ #endif sonde.setup(); + fixedToPosInfo(); initGPS(); +#if FEATURE_APRS + connAPRS.init(); +#endif WiFi.onEvent(WiFiEvent); getKeyPress(); // clear key buffer @@ -2190,6 +1943,7 @@ void enterMode(int mode) { } else if (mainState == ST_WIFISCAN) { sonde.clearDisplay(); } + if (mode == ST_DECODER) { // trigger activation of background task // currentSonde should be set before enterMode() @@ -2219,52 +1973,6 @@ static const char *action2text(uint8_t action) { } #define RDZ_DATA_LEN 128 - -void parseGpsJson(char *data) { - char *key = NULL; - char *value = NULL; - // very simple json parser: look for ", then key, then ", then :, then number, then , or } or \0 - for (int i = 0; i < RDZ_DATA_LEN; i++) { - if (key == NULL) { - if (data[i] != '"') continue; - key = data + i + 1; - i += 2; - continue; - } - if (value == NULL) { - if (data[i] != ':') continue; - value = data + i + 1; - i += 2; - continue; - } - if (data[i] == ',' || data[i] == '}' || data[i] == 0) { - // get value - double val = strtod(value, NULL); - // get data - if (strncmp(key, "lat", 3) == 0) { - gpsPos.lat = val; - } - else if (strncmp(key, "lon", 3) == 0) { - gpsPos.lon = val; - } - else if (strncmp(key, "alt", 3) == 0) { - gpsPos.alt = (int)val; - } - else if (strncmp(key, "course", 6) == 0) { - gpsPos.course = (int)val; - } - gpsPos.valid = true; - - // next item: - if (data[i] != ',') break; - key = NULL; - value = NULL; - } - } - if(gpsPos.lat == 0 && gpsPos.lon == 0) gpsPos.valid = false; - Serial.printf("Parse result: lat=%f, lon=%f, alt=%d, valid=%d\n", gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.valid); -} - static char rdzData[RDZ_DATA_LEN]; static int rdzDataPos = 0; @@ -2303,20 +2011,7 @@ void loopDecoder() { } } - if (!tncclient.connected()) { - //Serial.println("TNC client not connected"); - tncclient = tncserver.available(); - if (tncclient.connected()) { - Serial.println("new TCP KISS connection"); - } - } - if (tncclient.available()) { - Serial.print("TCP KISS socket: recevied "); - while (tncclient.available()) { - Serial.print(tncclient.read()); // Check if we receive anything from from APRSdroid - } - Serial.println(""); - } + if (rdzserver.hasClient()) { Serial.println("TCP JSON socket: new connection"); rdzclient.stop(); @@ -2330,7 +2025,7 @@ void loopDecoder() { if (c == '\n' || c == '}' || rdzDataPos >= RDZ_DATA_LEN) { // parse GPS position from phone rdzData[rdzDataPos] = c; - if (rdzDataPos > 2) parseGpsJson(rdzData); + if (rdzDataPos > 2) parseGpsJson(rdzData, rdzDataPos + 1); rdzDataPos = 0; } else { @@ -2351,6 +2046,11 @@ void loopDecoder() { // first check if ID and position lat+lonis ok if (s->d.validID && ((s->d.validPos & 0x03) == 0x03)) { +#if FEATURE_APRS + connAPRS.updateSonde(s); +#endif +#if 0 + // moved to conn-aprs.cpp char *str = aprs_senddata(s, sonde.config.call, sonde.config.objcall, sonde.config.udpfeed.symbol); char raw[201]; int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN); @@ -2373,7 +2073,7 @@ void loopDecoder() { } else if ( tcpclient.connected() ) { unsigned long now = millis(); - Serial.printf("aprs: now-last = %ld\n", (now-lasttcp)); + Serial.printf("aprs: now-last = %ld\n", (now - lasttcp)); if ( (now - lasttcp) > sonde.config.tcpfeed.highrate * 1000L ) { strcat(str, "\r\n"); Serial.print(str); @@ -2382,9 +2082,11 @@ void loopDecoder() { } } } +#endif + #if FEATURE_CHASEMAPPER if (sonde.config.cm.active) { - Chasemapper::send(udp, s); + connChasemapper.updateSonde( s ); } #endif } @@ -2395,21 +2097,26 @@ void loopDecoder() { #endif #if FEATURE_MQTT - // send to MQTT if enabledson - if (connected && mqttEnabled) { - Serial.println("Sending sonde info via MQTT"); - mqttclient.publishPacket(s); - } + connMQTT.updateSonde( s ); // send to MQTT if enabled #endif + } else { #if FEATURE_SONDEHUB sondehub_finish_data(&shclient, s, &sonde.config.sondehub); #endif } + + // Send own position periodically - if (sonde.config.tcpfeed.active) { - aprs_station_update(); - } +#if FEATURE_MQTT + connMQTT.updateStation( NULL ); +#endif +#if FEATURE_APRS + connAPRS.updateStation( NULL ); + //if (sonde.config.tcpfeed.active) { + // aprs_station_update(); + //} +#endif // always send data, even if not valid.... if (rdzclient.connected()) { Serial.println("Sending position via TCP as rdzJSON"); @@ -2431,82 +2138,18 @@ void loopDecoder() { // raw[0] = '{'; // Use same JSON format as for MQTT and HTML map........ - sonde2json(raw+1, 1023, s); - sprintf(raw+strlen(raw), - ",\"active\":%d" - ",\"validId\":%d" - ",\"validPos\":%d" - " %s}\n", - (int)s->active, - s->d.validID, - s->d.validPos, - gps); + sonde2json(raw + 1, 1023, s); + sprintf(raw + strlen(raw), + ",\"active\":%d" + ",\"validId\":%d" + ",\"validPos\":%d" + " %s}\n", + (int)s->active, + s->d.validID, + s->d.validPos, + gps); int len = strlen(raw); -#if 0 - //maintain backwords compatibility - float lat = isnan(s->d.lat) ? 0 : s->d.lat; - float lon = isnan(s->d.lon) ? 0 : s->d.lon; - float alt = isnan(s->d.alt) ? -1 : s->d.alt; - float vs = isnan(s->d.vs) ? 0 : s->d.vs; - float hs = isnan(s->d.hs) ? 0 : s->d.hs; - float dir = isnan(s->d.dir) ? 0 : s->d.dir; - int len = snprintf(raw, 1024, "{" - "\"res\": %d," - "\"type\": \"%s\"," - "\"active\": %d," - "\"freq\": %.2f," - "\"id\": \"%s\"," - "\"ser\": \"%s\"," - "\"validId\": %d," - "\"launchsite\": \"%s\"," - "\"lat\": %.5f," - "\"lon\": %.5f," - "\"alt\": %.1f," - "\"vs\": %.1f," - "\"hs\": %.1f," - "\"dir\": %.1f," - "\"sats\": %d," - "\"validPos\": %d," - "\"time\": %d," - "\"frame\": %d," - "\"validTime\": %d," - "\"rssi\": %d," - "\"afc\": %d," - "\"launchKT\": %d," - "\"burstKT\": %d," - "\"countKT\": %d," - "\"crefKT\": %d" - "%s" - "}\n", - res & 0xff, - typestr, - (int)s->active, - s->freq, - s->d.id, - s->d.ser, - (int)s->d.validID, - s->launchsite, - lat, - lon, - alt, - vs, - hs, - dir, - s->d.sats, - s->d.validPos, - s->d.time, - s->d.frame, - (int)s->d.validTime, - s->rssi, - s->afc, - s->d.launchKT, - s->d.burstKT, - s->d.countKT, - s->d.crefKT, - gps - ); -#endif //Serial.println("Writing rdzclient..."); if (len > 1024) len = 1024; @@ -2619,17 +2262,14 @@ void enableNetwork(bool enable) { SetupAsyncServer(); udp.begin(WiFi.localIP(), LOCALUDPPORT); MDNS.addService("http", "tcp", 80); - MDNS.addService("kiss-tnc", "tcp", 14580); + // => moved to conn-aprs MDNS.addService("kiss-tnc", "tcp", 14580); MDNS.addService("jsonrdz", "tcp", 14570); - if (sonde.config.kisstnc.active) { - tncserver.begin(); - rdzserver.begin(); - } + //if (sonde.config.kisstnc.active) { + // tncserver.begin(); + rdzserver.begin(); + //} #if FEATURE_MQTT - if (sonde.config.mqtt.active && strlen(sonde.config.mqtt.host) > 0) { - mqttEnabled = true; - mqttclient.init(sonde.config.mqtt.host, sonde.config.mqtt.port, sonde.config.mqtt.id, sonde.config.mqtt.username, sonde.config.mqtt.password, sonde.config.mqtt.prefix); - } + connMQTT.netsetup(); #endif #if FEATURE_SONDEHUB if (sonde.config.sondehub.active && wifi_state != WIFI_APMODE) { @@ -2643,15 +2283,11 @@ void enableNetwork(bool enable) { MDNS.end(); connected = false; } - tcpclient.onConnect([](void *arg, AsyncClient * s) { - Serial.write("APRS: TCP connected\n"); - char buf[128]; - snprintf(buf, 128, "user %s pass %d vers %s %s\r\n", sonde.config.call, sonde.config.passcode, version_name, version_id); - s->write(buf, strlen(buf)); - }); - tcpclient.onData([](void *arg, AsyncClient * c, void *data, size_t len) { - Serial.write((const uint8_t *)data, len); - }); + +#if FEATURE_APRS + connAPRS.netsetup(); +#endif + Serial.println("enableNetwork done"); } @@ -3263,58 +2899,11 @@ void loop() { lastDisplay = currentDisplay; } -#if FEATURE_MQTT - int now = millis(); - if (mqttEnabled && (lastMqttUptime == 0 || (lastMqttUptime + 60000 < now) || (lastMqttUptime > now))) { - mqttclient.publishUptime(); - lastMqttUptime = now; - } -#endif - } -void aprs_station_update() { - int chase = sonde.config.chase; - // automatically decided if CHASE or FIXED mode is used (for config AUTO) - if (chase == SH_LOC_AUTO) { - if (SH_LOC_AUTO_IS_CHASE) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; - } - unsigned long time_now = millis(); - unsigned long time_delta = time_now - time_last_aprs_update; - unsigned long update_time = (chase == SH_LOC_CHASE) ? APRS_MOBILE_STATION_UPDATE_TIME : APRS_STATION_UPDATE_TIME; - Serial.printf("aprs_station_update: delta: %ld, update in %ld\n", time_delta, update_time); - if (time_delta < update_time) return; - Serial.println("Update is due!!"); - - float lat, lon; - if (chase == SH_LOC_FIXED) { - // fixed location - lat = sonde.config.rxlat; - lon = sonde.config.rxlon; - if (isnan(lat) || isnan(lon)) return; - } else { - if (gpsPos.valid) { - lat = gpsPos.lat; - lon = gpsPos.lon; - } else { - return; - } - } - Serial.printf("Really updating!! (objcall is %s)", sonde.config.objcall); - char *bcn = aprs_send_beacon(sonde.config.call, lat, lon, sonde.config.beaconsym + ((chase==SH_LOC_CHASE)?2:0), sonde.config.comment); - if ( tcpclient.disconnected()) { - tcpclient.connect(sonde.config.tcpfeed.host, sonde.config.tcpfeed.port); - } - if ( tcpclient.connected() ) { - strcat(bcn, "\r\n"); - Serial.println("****BEACON****"); - Serial.print(bcn); - tcpclient.write(bcn, strlen(bcn)); - time_last_aprs_update = time_now; - } -} #if FEATURE_SONDEHUB + // Sondehub v2 DB related codes /* Update station data to the sondehub v2 DB @@ -3335,7 +2924,7 @@ void sondehub_station_update(WiFiClient * client, struct st_sondehub * conf) { int chase = conf->chase; // automatically decided if CHASE or FIXED mode is used (for config AUTO) if (chase == SH_LOC_AUTO) { - if (SH_LOC_AUTO_IS_CHASE) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; + if (posInfo.chase) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; } // Use 30sec update time in chase mode, 60 min in station mode. @@ -3565,7 +3154,7 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub * int chase = conf->chase; // automatically decided if CHASE or FIXED mode is used (for config AUTO) if (chase == SH_LOC_AUTO) { - if (SH_LOC_AUTO_IS_CHASE) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; + if (posInfo.chase) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; } @@ -3579,7 +3168,7 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub * } // Check if current sonde data is valid. If not, don't do anything.... - if (*s->d.ser == 0 || s->d.validID==0 ) return; // Don't send anything without serial number + if (*s->d.ser == 0 || s->d.validID == 0 ) return; // Don't send anything without serial number if (((int)s->d.lat == 0) && ((int)s->d.lon == 0)) return; // Sometimes these values are zeroes. Don't send those to the sondehub if ((int)s->d.alt > 50000) return; // If alt is too high don't send to SondeHub // M20 data does not include #sat information @@ -3655,12 +3244,12 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub * /* if there is a subtype (DFM only) */ if ( TYPE_IS_DFM(s->type) && s->d.subtype > 0 ) { - if( (s->d.subtype&0xF) != DFM_UNK) { - const char *t = dfmSubtypeLong[s->d.subtype&0xF]; + if ( (s->d.subtype & 0xF) != DFM_UNK) { + const char *t = dfmSubtypeLong[s->d.subtype & 0xF]; sprintf(w, "\"subtype\": \"%s\",", t); } else { - sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype>>4); // Unknown subtype + sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype >> 4); // Unknown subtype } w += strlen(w); } else if ( s->type == STYPE_RS41 ) { diff --git a/RX_FSK/data/cfg.js b/RX_FSK/data/cfg.js index b0abf00..3d9c9d1 100644 --- a/RX_FSK/data/cfg.js +++ b/RX_FSK/data/cfg.js @@ -42,6 +42,7 @@ var cfgs = [ [ "axudp.port", "AXUDP port"], [ "axudp.highrate", "Rate limit"], [ "tcp.active", "APRS TCP active"], +[ "tcp.timeout", "APRS TCP timeout [s] (0=off, 25=on)"], [ "tcp.host", "APRS TCP host"], [ "tcp.port", "APRS TCP port"], [ "tcp.highrate", "Rate limit"], diff --git a/RX_FSK/data/map.html b/RX_FSK/data/map.html index dab510f..89cb171 100644 --- a/RX_FSK/data/map.html +++ b/RX_FSK/data/map.html @@ -15,6 +15,7 @@ document.addEventListener("DOMContentLoaded", function(){ fetch('live.json') .then((response) => response.json()) .then((data) => { + if (data.gps===undefined) { data.gps={} }; console.log(data.gps.lat, data.gps.lon, data.sonde.ser); if( (data.sonde.ser||'')=='' && !(data.gps.lat===undefined) ) { urlarg = maps[maptype][2](data.gps.lat, data.gps.lon); diff --git a/RX_FSK/features.h b/RX_FSK/features.h index b2609ec..3553154 100644 --- a/RX_FSK/features.h +++ b/RX_FSK/features.h @@ -2,12 +2,17 @@ // Configuration flags for including/excluding fuctionality from the compiled binary // set flag to 0 for exclude/1 for include -/* data feed to sondehubv2 */ -/* needs about 4k4 code, 200b data, 200b stack, 200b heap */ +// Selection of data output connectors to be included in firmware +// APRS includes AXUDP (e.g. for aprsmap) and APRS-IS (TCP) (e.g. for wettersonde.net, radiosondy.info) #define FEATURE_SONDEHUB 1 + #define FEATURE_CHASEMAPPER 1 #define FEATURE_MQTT 1 +#define FEATURE_SDCARD 0 +#define FEATURE_APRS 1 + +// Additional optional components #define FEATURE_RS92 1 /* Most recent version support fonts in a dedicated flash parition "fonts". diff --git a/RX_FSK/src/Chasemapper.h b/RX_FSK/src/Chasemapper.h deleted file mode 100644 index 4f537b9..0000000 --- a/RX_FSK/src/Chasemapper.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _CHASEMAPPER_H -#define _CHASEMAPPER_H - -#include "Sonde.h" -//#include -#include -#include -class Chasemapper { -public: - static int send(WiFiUDP &udb, SondeInfo *si); -}; - -#endif diff --git a/RX_FSK/src/Display.cpp b/RX_FSK/src/Display.cpp index ed92aca..dd3584c 100644 --- a/RX_FSK/src/Display.cpp +++ b/RX_FSK/src/Display.cpp @@ -2,10 +2,10 @@ #include #include #include -#include #include #include "Display.h" #include "Sonde.h" +#include "pmu.h" int readLine(Stream &stream, char *buffer, int maxlen); @@ -22,8 +22,7 @@ extern const char *version_id; extern Sonde sonde; -extern AXP20X_Class axp; -extern bool axp192_found; +extern PMU *pmu; extern SemaphoreHandle_t axpSemaphore; extern xSemaphoreHandle globalLock; @@ -33,8 +32,6 @@ extern xSemaphoreHandle globalLock; } while (xSemaphoreTake(globalLock, portMAX_DELAY) != pdPASS) #define SPI_MUTEX_UNLOCK() xSemaphoreGive(globalLock) -struct GpsPos gpsPos; - //SPIClass spiDisp(HSPI); byte myIP_tiles[8*11]; @@ -1670,13 +1667,14 @@ void Display::drawGPS(DispEntry *de) { void Display::drawBatt(DispEntry *de) { float val; char buf[30]; - if (!axp192_found) { + if (!pmu) { if (sonde.config.batt_adc<0) return; switch (de->extra[0]) { case 'V': val = (float)(analogRead(sonde.config.batt_adc)) / 4095 * 2 * 3.3 * 1.1; snprintf(buf, 30, "%.2f%s", val, de->extra + 1); + Serial.printf("Batt: %s", buf); break; default: *buf = 0; @@ -1684,51 +1682,67 @@ void Display::drawBatt(DispEntry *de) { rdis->setFont(de->fmt); drawString(de, buf); } else { + *buf = 0; xSemaphoreTake( axpSemaphore, portMAX_DELAY ); switch(de->extra[0]) { case 'S': - if(!axp.isBatteryConnect()) { - if(axp.isVBUSPlug()) { strcpy(buf, "U"); } + if(!pmu->isBatteryConnected()) { + if(pmu->isVbusIn()) { strcpy(buf, "U"); } else { strcpy(buf, "N"); } // no battary } - else if (axp.isChargeing()) { strcpy(buf, "C"); } // charging + else if (pmu->isCharging()) { strcpy(buf, "C"); } // charging else { strcpy(buf, "B"); } // battery, but not charging + Serial.printf("Battery: %s\n", buf); break; case 'V': - val = axp.getBattVoltage(); + val = pmu->getBattVoltage(); snprintf(buf, 30, "%.2f%s", val/1000, de->extra+1); + Serial.printf("Vbatt: %s\n", buf); break; - case 'C': - val = axp.getBattChargeCurrent(); + } + if(pmu->type==TYPE_AXP192) { + switch(de->extra[0]) { + case 'C': + val = pmu->getBattChargeCurrent(); snprintf(buf, 30, "%.2f%s", val, de->extra+1); + Serial.printf("Icharge: %s\n", buf); break; - case 'D': - val = axp.getBattDischargeCurrent(); + case 'D': + val = pmu->getBattDischargeCurrent(); snprintf(buf, 30, "%.2f%s", val, de->extra+1); + Serial.printf("Idischarge: %s\n", buf); break; - case 'U': + case 'U': if(sonde.config.type == TYPE_M5_CORE2) { - val = axp.getAcinVoltage(); + val = pmu->getAcinVoltage(); } else { - val = axp.getVbusVoltage(); + val = pmu->getVbusVoltage(); } snprintf(buf, 30, "%.2f%s", val/1000, de->extra+1); + Serial.printf("Vbus: %s\n", buf); break; - case 'I': + case 'I': if(sonde.config.type == TYPE_M5_CORE2) { - val = axp.getAcinCurrent(); + val = pmu->getAcinCurrent(); } else { - val = axp.getVbusCurrent(); + val = pmu->getVbusCurrent(); } snprintf(buf, 30, "%.2f%s", val, de->extra+1); + Serial.printf("Ibus: %s\n", buf); break; - case 'T': - val = axp.getTemp(); // fixed in newer versions of libraray: -144.7 no longer needed here! + case 'T': + val = pmu->getTemperature(); snprintf(buf, 30, "%.2f%s", val, de->extra+1); + Serial.printf("temp: %s\n", buf); break; - default: - *buf=0; - } + } + } else if (pmu->type == TYPE_AXP2101) { + *buf = 0; + if(de->extra[0]=='T') { + val = pmu->getTemperature(); + snprintf(buf, 30, "%.2f%s", val, de->extra+1); + } + } xSemaphoreGive( axpSemaphore ); rdis->setFont(de->fmt); drawString(de, buf); diff --git a/RX_FSK/src/Display.h b/RX_FSK/src/Display.h index 23672c3..8a9c3fc 100644 --- a/RX_FSK/src/Display.h +++ b/RX_FSK/src/Display.h @@ -9,18 +9,7 @@ #include #include -struct GpsPos { - double lat; - double lon; - int alt; - int course; - float speed; - int sat; - int accuracy; - int hdop; - int valid; -}; -extern struct GpsPos gpsPos; +#include "posinfo.h" #define WIDTH_AUTO 9999 struct DispEntry { diff --git a/RX_FSK/src/M10M20.cpp b/RX_FSK/src/M10M20.cpp index bfaf0a8..3efc02d 100644 --- a/RX_FSK/src/M10M20.cpp +++ b/RX_FSK/src/M10M20.cpp @@ -304,6 +304,9 @@ int M10M20::decodeframeM10(uint8_t *data) { Serial.println("Decoding..."); //SondeInfo *si = sonde.si(); SondeData *si = &(sonde.si()->d); + // Set type info to M10 + memcpy(si->typestr, "M10 ", 5); + si->subtype = 1; // subtype 1: M10 // Its a M10 // getid... @@ -441,7 +444,7 @@ void M10M20::processM10data(uint8_t dt) rxsearching = false; rxbitc = 0; rxp = 0; - isM20 = false; + //isM20 = false; headerDetected = 1; #if 1 int rssi=sx1278.getRSSI(); @@ -463,17 +466,17 @@ void M10M20::processM10data(uint8_t dt) // 64 9F 20 => M10 // 64 49 0x => M10 (?) -- not used here // 45 20 7x => M20 - if(rxp==2 && dataptr[0]==0x45 && dataptr[1]==0x20) { isM20 = true; } + if(rxp==2) { + // Update: change type only if valid type information in received data + if(dataptr[0]==0x45 && dataptr[1]==0x20) { isM20 = true; } + else if(/*dataptr[0]==0x64 &&*/ dataptr[1]==0x9F) { isM20 = false; } + } if(isM20) { - memcpy(sonde.si()->d.typestr, "M20 ", 5); - sonde.si()->d.subtype = 2; if(rxp>=M20_FRAMELEN) { rxsearching = true; haveNewFrame = decodeframeM20(dataptr); } } else { - memcpy(sonde.si()->d.typestr, "M10 ", 5); - sonde.si()->d.subtype = 1; if(rxp>=M10_FRAMELEN) { rxsearching = true; haveNewFrame = decodeframeM10(dataptr); @@ -578,6 +581,8 @@ int M10M20::decodeframeM20(uint8_t *data) { Serial.println("Decoding..."); // Its a M20 + memcpy(si->typestr, "M20 ", 5); + si->subtype = 2; // subtype 2: M20 // getid... // TODO: Adjust ID calculation and serial number reconstruction char ids[11]={'M','E','0','0','0','0','0','0','0','0','0'}; diff --git a/RX_FSK/src/RS92.cpp b/RX_FSK/src/RS92.cpp index f9f6e52..db549c0 100644 --- a/RX_FSK/src/RS92.cpp +++ b/RX_FSK/src/RS92.cpp @@ -1,3 +1,5 @@ +#include "../features.h" +#if FEATURE_RS92 /* RS92 decoder functions */ #include "RS92.h" @@ -587,3 +589,4 @@ int RS92::waitRXcomplete() { RS92 rs92 = RS92(); +#endif diff --git a/RX_FSK/src/Sonde.cpp b/RX_FSK/src/Sonde.cpp index be54dd7..d3afe3b 100644 --- a/RX_FSK/src/Sonde.cpp +++ b/RX_FSK/src/Sonde.cpp @@ -28,6 +28,7 @@ const char *sondeTypeStr[NSondeTypes] = { "DFM ", "RS41", "RS92", "Mxx ", "M10 " const char *sondeTypeLongStr[NSondeTypes] = { "DFM (all)", "RS41", "RS92", "M10/M20", "M10 ", "M20 ", "MP3-H1" }; const char sondeTypeChar[NSondeTypes] = { 'D', '4', 'R', 'M', 'M', '2', '3' }; const char *manufacturer_string[]={"Graw", "Vaisala", "Vaisala", "Meteomodem", "Meteomodem", "Meteomodem", "Meteo-Radiy"}; +const char *DEFEPH="gssc.esa.int/gnss/data/daily/%1$04d/brdc/brdc%2$03d0.%3$02dn.gz"; int fingerprintValue[]={ 17, 31, 64, 4, 55, 48, 23, 128+23, 119, 128+119, -1 }; const char *fingerprintText[]={ @@ -258,7 +259,7 @@ void Sonde::defaultConfig() { config.tcpfeed.port = 12345; config.tcpfeed.highrate = 10; config.kisstnc.active = 0; - strcpy(config.ephftp,"igs.bkg.bund.de/IGS/BRDC/"); + strcpy(config.ephftp,DEFEPH); config.mqtt.active = 0; strcpy(config.mqtt.id, "rdz_sonde_server"); @@ -278,6 +279,8 @@ void Sonde::checkConfig() { if(config.sondehub.fimaxage>48) config.sondehub.fimaxage = 48; if(config.sondehub.fimaxdist==0) config.sondehub.fimaxdist = 150; if(config.sondehub.fimaxage==0) config.sondehub.fimaxage = 2; + // auto upgrade config to new version with format arguments in string + if(!strchr(sonde.config.ephftp,'%')) strcpy(sonde.config.ephftp,DEFEPH); } void Sonde::setConfig(const char *cfg) { while(*cfg==' '||*cfg=='\t') cfg++; diff --git a/RX_FSK/src/Sonde.h b/RX_FSK/src/Sonde.h index 7d76b11..422d584 100644 --- a/RX_FSK/src/Sonde.h +++ b/RX_FSK/src/Sonde.h @@ -191,6 +191,7 @@ struct st_feedinfo { int lowrate; int highrate; int lowlimit; + int timeout; }; // maybe extend for external Bluetooth interface? @@ -281,7 +282,7 @@ typedef struct st_rdzconfig { struct st_dfmconfig dfm; struct st_m10m20config m10m20; struct st_mp3hconfig mp3h; - char ephftp[40]; + char ephftp[80]; // data feed configuration // for now, one feed for each type is enough, but might get extended to more? char call[10]; // APRS callsign diff --git a/RX_FSK/src/TFT22_ILI9225.cpp b/RX_FSK/src/TFT22_ILI9225.cpp deleted file mode 100644 index 45bc551..0000000 --- a/RX_FSK/src/TFT22_ILI9225.cpp +++ /dev/null @@ -1,1419 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -// original source: https://github.com/Nkawu/TFT22_ILI9225 - - -#include "TFT22_ILI9225.h" - -//#define DEBUG -#ifdef DEBUG - //#define DB_PRINT( x, ... ) { char dbgbuf[60]; sprintf_P( dbgbuf, (const char*) F( x ), __VA_ARGS__ ) ; Serial.print( dbgbuf ); } - #define DB_PRINT( ... ) { char dbgbuf[60]; sprintf( dbgbuf, __VA_ARGS__ ) ; Serial.println( dbgbuf ); } - -#else - #define DB_PRINT( ... ) ; -#endif - - - -#ifndef ARDUINO_STM32_FEATHER - #include "pins_arduino.h" - #ifndef RASPI - #include "wiring_private.h" - #endif -#endif -#include -#ifdef __AVR__ - #include -#elif defined(ESP8266) || defined(ESP32) - #include -#endif - -// Many (but maybe not all) non-AVR board installs define macros -// for compatibility with existing PROGMEM-reading AVR code. -// Do our own checks and defines here for good measure... - -#ifndef pgm_read_byte - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) -#endif -#ifndef pgm_read_word - #define pgm_read_word(addr) (*(const unsigned short *)(addr)) -#endif -#ifndef pgm_read_dword - #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) -#endif - -// Pointers are a peculiar case...typically 16-bit on AVR boards, -// 32 bits elsewhere. Try to accommodate both... - -#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) - #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) -#else - #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) -#endif - -// Control pins - -#ifdef USE_FAST_PINIO - #define SPI_DC_HIGH() *dcport |= dcpinmask - #define SPI_DC_LOW() *dcport &= ~dcpinmask - #define SPI_CS_HIGH() *csport |= cspinmask - #define SPI_CS_LOW() *csport &= ~cspinmask -#else - #define SPI_DC_HIGH() digitalWrite(_rs, HIGH) - #define SPI_DC_LOW() digitalWrite(_rs, LOW) - #define SPI_CS_HIGH() digitalWrite(_cs, HIGH) - #define SPI_CS_LOW() digitalWrite(_cs, LOW) -#endif - -// Software SPI Macros - -#ifdef USE_FAST_PINIO - #define SSPI_MOSI_HIGH() *mosiport |= mosipinmask - #define SSPI_MOSI_LOW() *mosiport &= ~mosipinmask - #define SSPI_SCK_HIGH() *clkport |= clkpinmask - #define SSPI_SCK_LOW() *clkport &= ~clkpinmask -#else - #define SSPI_MOSI_HIGH() digitalWrite(_sdi, HIGH) - #define SSPI_MOSI_LOW() digitalWrite(_sdi, LOW) - #define SSPI_SCK_HIGH() digitalWrite(_clk, HIGH) - #define SSPI_SCK_LOW() digitalWrite(_clk, LOW) -#endif - -#define SSPI_BEGIN_TRANSACTION() -#define SSPI_END_TRANSACTION() -#define SSPI_WRITE(v) _spiWrite(v) -#define SSPI_WRITE16(s) SSPI_WRITE((s) >> 8); SSPI_WRITE(s) -#define SSPI_WRITE32(l) SSPI_WRITE((l) >> 24); SSPI_WRITE((l) >> 16); SSPI_WRITE((l) >> 8); SSPI_WRITE(l) -#define SSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ SSPI_WRITE(((uint8_t*)(c))[i+1]); SSPI_WRITE(((uint8_t*)(c))[i]); } - -// Hardware SPI Macros -#ifndef ESP32 - #ifdef SPI_CHANNEL - extern SPIClass SPI_CHANNEL; - #define SPI_OBJECT SPI_CHANNEL - #else - #define SPI_OBJECT SPI - #endif -#else - #define SPI_OBJECT _spi -#endif - -#if defined (__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(SPI_CLOCK_DIV2); -#elif defined (__arm__) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(11); -#elif defined(ESP8266) || defined(ESP32) - #define HSPI_SET_CLOCK() SPI_OBJECT.setFrequency(SPI_DEFAULT_FREQ); -#elif defined(RASPI) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); -#elif defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(SPI_DEFAULT_FREQ); -#else - #define HSPI_SET_CLOCK() -#endif - -#ifdef SPI_HAS_TRANSACTION - #define HSPI_BEGIN_TRANSACTION() SPI_OBJECT.beginTransaction(SPISettings(SPI_DEFAULT_FREQ, MSBFIRST, SPI_MODE0)) - #define HSPI_END_TRANSACTION() SPI_OBJECT.endTransaction() -#else - #define HSPI_BEGIN_TRANSACTION() HSPI_SET_CLOCK(); SPI_OBJECT.setBitOrder(MSBFIRST); SPI_OBJECT.setDataMode(SPI_MODE0) - #define HSPI_END_TRANSACTION() -#endif - -#ifdef ESP32 - #define SPI_HAS_WRITE_PIXELS -#endif -#if defined(ESP8266) || defined(ESP32) - // Optimized SPI (ESP8266 and ESP32) - #define HSPI_READ() SPI_OBJECT.transfer(0) - #define HSPI_WRITE(b) SPI_OBJECT.write(b) - #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) - #define HSPI_WRITE32(l) SPI_OBJECT.write32(l) - #ifdef SPI_HAS_WRITE_PIXELS - #define SPI_MAX_PIXELS_AT_ONCE 32 - #define HSPI_WRITE_PIXELS(c,l) SPI_OBJECT.writePixels(c,l) - #else - #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<((l)/2); i++){ SPI_WRITE16(((uint16_t*)(c))[i]); } - #endif -#elif defined ( __STM32F1__ ) - #define HSPI_WRITE(b) SPI_OBJECT.write(b) - #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) - -#else - // Standard Byte-by-Byte SPI - - #if defined (__AVR__) || defined(TEENSYDUINO) - static inline uint8_t _avr_spi_read(void) __attribute__((always_inline)); - static inline uint8_t _avr_spi_read(void) { - uint8_t r = 0; - SPDR = r; - while(!(SPSR & _BV(SPIF))); - r = SPDR; - return r; - } - #define HSPI_WRITE(b) {SPDR = (b); while(!(SPSR & _BV(SPIF)));} - // #define HSPI_READ() _avr_spi_read() - #else - #define HSPI_WRITE(b) SPI_OBJECT.transfer((uint8_t)(b)) - // #define HSPI_READ() HSPI_WRITE(0) - #endif - // #define HSPI_WRITE16(s) HSPI_WRITE((s) >> 8); HSPI_WRITE(s) - // #define HSPI_WRITE32(l) HSPI_WRITE((l) >> 24); HSPI_WRITE((l) >> 16); HSPI_WRITE((l) >> 8); HSPI_WRITE(l) - // #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ HSPI_WRITE(((uint8_t*)(c))[i+1]); HSPI_WRITE(((uint8_t*)(c))[i]); } -#endif - -// Final SPI Macros - -#if defined (ARDUINO_ARCH_ARC32) - #define SPI_DEFAULT_FREQ 16000000 -#elif defined (__AVR__) || defined(TEENSYDUINO) - #define SPI_DEFAULT_FREQ 8000000 -#elif defined(ESP8266) || defined(ESP32) - #define SPI_DEFAULT_FREQ 40000000 -#elif defined(RASPI) - #define SPI_DEFAULT_FREQ 80000000 -#elif defined(ARDUINO_ARCH_STM32F1) - #define SPI_DEFAULT_FREQ 18000000 - //#define SPI_DEFAULT_FREQ 36000000 -#else - #define SPI_DEFAULT_FREQ 24000000 -#endif - -#define SPI_BEGIN() if(_clk < 0){SPI_OBJECT.begin();} -#define SPI_BEGIN_TRANSACTION() if(_clk < 0){HSPI_BEGIN_TRANSACTION();} -#define SPI_END_TRANSACTION() if(_clk < 0){HSPI_END_TRANSACTION();} -// #define SPI_WRITE16(s) if(_clk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} -// #define SPI_WRITE32(l) if(_clk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} -// #define SPI_WRITE_PIXELS(c,l) if(_clk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} - -// Constructor when using software SPI. All output pins are configurable. -TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led) { - _rst = rst; - _rs = rs; - _cs = cs; - _sdi = sdi; - _clk = clk; - _led = led; - _brightness = 255; // Set to maximum brightness - hwSPI = false; - writeFunctionLevel = 0; - gfxFont = NULL; - _modeFlip = 0; -} - -// Constructor when using software SPI. All output pins are configurable. Adds backlight brightness 0-255 -TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_t clk, int8_t led, uint8_t brightness) { - _rst = rst; - _rs = rs; - _cs = cs; - _sdi = sdi; - _clk = clk; - _led = led; - _brightness = brightness; - hwSPI = false; - writeFunctionLevel = 0; - gfxFont = NULL; - _modeFlip = 0; -} - -// Constructor when using hardware SPI. Faster, but must use SPI pins -// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) -TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led) { - _rst = rst; - _rs = rs; - _cs = cs; - _sdi = _clk = -1; - _led = led; - _brightness = 255; // Set to maximum brightness - hwSPI = true; - writeFunctionLevel = 0; - gfxFont = NULL; - _modeFlip = 0; -} - -// Constructor when using hardware SPI. Faster, but must use SPI pins -// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) -// Adds backlight brightness 0-255 -TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led, uint8_t brightness) { - _rst = rst; - _rs = rs; - _cs = cs; - _sdi = _clk = -1; - _led = led; - _brightness = brightness; - hwSPI = true; - writeFunctionLevel = 0; - gfxFont = NULL; - _modeFlip = 0; -} - - -#ifdef ESP32 -void TFT22_ILI9225::begin(SPIClass &spi) -#else -void TFT22_ILI9225::begin() -#endif -{ -#ifdef ESP32 - _spi = spi; -#endif - // Set up reset pin - if (_rst > 0) { - pinMode(_rst, OUTPUT); - digitalWrite(_rst, LOW); - } - // Set up backlight pin, turn off initially - if (_led > 0) { - pinMode(_led, OUTPUT); - setBacklight(false); - } - - // Control pins - pinMode(_rs, OUTPUT); - digitalWrite(_rs, LOW); - pinMode(_cs, OUTPUT); - digitalWrite(_cs, HIGH); - - #ifdef USE_FAST_PINIO - csport = portOutputRegister(digitalPinToPort(_cs)); - cspinmask = digitalPinToBitMask(_cs); - dcport = portOutputRegister(digitalPinToPort(_rs)); - dcpinmask = digitalPinToBitMask(_rs); -#endif - - // Software SPI - if(_clk >= 0){ - pinMode(_sdi, OUTPUT); - digitalWrite(_sdi, LOW); - pinMode(_clk, OUTPUT); - digitalWrite(_clk, HIGH); -#ifdef USE_FAST_PINIO - clkport = portOutputRegister(digitalPinToPort(_clk)); - clkpinmask = digitalPinToBitMask(_clk); - mosiport = portOutputRegister(digitalPinToPort(_sdi)); - mosipinmask = digitalPinToBitMask(_sdi); - SSPI_SCK_LOW(); - SSPI_MOSI_LOW(); - } else { - clkport = 0; - clkpinmask = 0; - mosiport = 0; - mosipinmask = 0; -#endif - } - - // Hardware SPI - if(_clk < 0){ - SPI_BEGIN(); - } else { - SPI_OBJECT.begin(_clk, -1, _sdi, _cs); - _clk = -1; // force use of hardware SPI - } - - // Initialization Code - if (_rst > 0) { - digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status - delay(1); - digitalWrite(_rst, LOW); // Pull the reset pin low to reset ILI9225 - delay(10); - digitalWrite(_rst, HIGH); // Pull the reset pin high to release the ILI9225C from the reset status - delay(50); - } - - /* Start Initial Sequence */ - - /* Set SS bit and direction output from S528 to S1 */ - startWrite(); - _writeRegister(ILI9225_POWER_CTRL1, 0x0000); // Set SAP,DSTB,STB - _writeRegister(ILI9225_POWER_CTRL2, 0x0000); // Set APON,PON,AON,VCI1EN,VC - _writeRegister(ILI9225_POWER_CTRL3, 0x0000); // Set BT,DC1,DC2,DC3 - _writeRegister(ILI9225_POWER_CTRL4, 0x0000); // Set GVDD - _writeRegister(ILI9225_POWER_CTRL5, 0x0000); // Set VCOMH/VCOML voltage - endWrite(); - delay(40); - - // Power-on sequence - startWrite(); - _writeRegister(ILI9225_POWER_CTRL2, 0x0018); // Set APON,PON,AON,VCI1EN,VC - _writeRegister(ILI9225_POWER_CTRL3, 0x6121); // Set BT,DC1,DC2,DC3 - _writeRegister(ILI9225_POWER_CTRL4, 0x006F); // Set GVDD /*007F 0088 */ - _writeRegister(ILI9225_POWER_CTRL5, 0x495F); // Set VCOMH/VCOML voltage - _writeRegister(ILI9225_POWER_CTRL1, 0x0800); // Set SAP,DSTB,STB - endWrite(); - delay(10); - startWrite(); - _writeRegister(ILI9225_POWER_CTRL2, 0x103B); // Set APON,PON,AON,VCI1EN,VC - endWrite(); - delay(50); - -#define OCTLFLIP(m) ((m&0xff)<<8) -#define EMODEFLIP(m) ((m>>8)<<3) - startWrite(); - _writeRegister(ILI9225_DRIVER_OUTPUT_CTRL, OCTLFLIP(_modeFlip) ^ 0x011C); // set the display line number and display direction - _writeRegister(ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion - _writeRegister(ILI9225_ENTRY_MODE, EMODEFLIP(_modeFlip) ^ 0x1038); // set GRAM write direction and BGR=1. - _writeRegister(ILI9225_DISP_CTRL1, 0x0000); // Display off - _writeRegister(ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch - _writeRegister(ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line - _writeRegister(ILI9225_INTERFACE_CTRL, 0x0000); // CPU interface - _writeRegister(ILI9225_OSC_CTRL, 0x0D01); // Set Osc /*0e01*/ - _writeRegister(ILI9225_VCI_RECYCLING, 0x0020); // Set VCI recycling - _writeRegister(ILI9225_RAM_ADDR_SET1, 0x0000); // RAM Address - _writeRegister(ILI9225_RAM_ADDR_SET2, 0x0000); // RAM Address - - /* Set GRAM area */ - _writeRegister(ILI9225_GATE_SCAN_CTRL, 0x0000); - _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL1, 0x00DB); - _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL2, 0x0000); - _writeRegister(ILI9225_VERTICAL_SCROLL_CTRL3, 0x0000); - _writeRegister(ILI9225_PARTIAL_DRIVING_POS1, 0x00DB); - _writeRegister(ILI9225_PARTIAL_DRIVING_POS2, 0x0000); - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); - - /* Set GAMMA curve */ - _writeRegister(ILI9225_GAMMA_CTRL1, 0x0000); - _writeRegister(ILI9225_GAMMA_CTRL2, 0x0808); - _writeRegister(ILI9225_GAMMA_CTRL3, 0x080A); - _writeRegister(ILI9225_GAMMA_CTRL4, 0x000A); - _writeRegister(ILI9225_GAMMA_CTRL5, 0x0A08); - _writeRegister(ILI9225_GAMMA_CTRL6, 0x0808); - _writeRegister(ILI9225_GAMMA_CTRL7, 0x0000); - _writeRegister(ILI9225_GAMMA_CTRL8, 0x0A00); - _writeRegister(ILI9225_GAMMA_CTRL9, 0x0710); - _writeRegister(ILI9225_GAMMA_CTRL10, 0x0710); - - _writeRegister(ILI9225_DISP_CTRL1, 0x0012); - endWrite(); - delay(50); - startWrite(); - _writeRegister(ILI9225_DISP_CTRL1, 0x1017); - endWrite(); - - // Turn on backlight - setBacklight(true); - setOrientation(0); - - // Initialize variables - setBackgroundColor( COLOR_BLACK ); - - clear(); -} - - -void TFT22_ILI9225::_spiWrite(uint8_t b) { - if(_clk < 0){ - HSPI_WRITE(b); - return; - } - // Fast SPI bitbang swiped from LPD8806 library - for(uint8_t bit = 0x80; bit; bit >>= 1){ - if((b) & bit){ - SSPI_MOSI_HIGH(); - } else { - SSPI_MOSI_LOW(); - } - SSPI_SCK_HIGH(); - SSPI_SCK_LOW(); - } -} - -void TFT22_ILI9225::_spiWrite16(uint16_t s) -{ - // Attempt to use HSPI_WRITE16 if available - #ifdef HSPI_WRITE16 - if(_clk < 0){ - HSPI_WRITE16(s); - return; - } - #endif - // Fallback to SSPI_WRITE16 if HSPI_WRITE16 not available - SSPI_WRITE16(s); -} - -void TFT22_ILI9225::_spiWriteCommand(uint8_t c) { - SPI_DC_LOW(); - SPI_CS_LOW(); - _spiWrite(c); - SPI_CS_HIGH(); -} - - -void TFT22_ILI9225::_spiWriteData(uint8_t c) { - SPI_DC_HIGH(); - SPI_CS_LOW(); - _spiWrite(c); - SPI_CS_HIGH(); -} - -void TFT22_ILI9225::_orientCoordinates(uint16_t &x1, uint16_t &y1) { - - switch (_orientation) { - case 0: // ok - break; - case 1: // ok - y1 = _maxY - y1 - 1; - _swap(x1, y1); - break; - case 2: // ok - x1 = _maxX - x1 - 1; - y1 = _maxY - y1 - 1; - break; - case 3: // ok - x1 = _maxX - x1 - 1; - _swap(x1, y1); - break; - } -} - -void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { - _setWindow( x0, y0, x1, y1, TopDown_L2R ); // default for drawing characters -} - -void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode) { - DB_PRINT( "setWindows( x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); - // clip to TFT-Dimensions - x0 = min( x0, (uint16_t) (_maxX-1) ); - x1 = min( x1, (uint16_t) (_maxX-1) ); - y0 = min( y0, (uint16_t) (_maxY-1) ); - y1 = min( y1, (uint16_t) (_maxY-1) ); - _orientCoordinates(x0, y0); - _orientCoordinates(x1, y1); - - if (x1 0 ) mode = modeTab[_orientation-1][mode]; - _writeRegister(ILI9225_ENTRY_MODE, EMODEFLIP(_modeFlip) ^ (0x1000 | ( mode<<3)) ); - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1,x1); - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2,x0); - - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1,y1); - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2,y0); - DB_PRINT( "gedreht: x0=%d, y0=%d, x1=%d, y1=%d, mode=%d", x0,y0,x1,y1,mode ); - // starting position within window and increment/decrement direction - switch ( mode>>1 ) { - case 0: - _writeRegister(ILI9225_RAM_ADDR_SET1,x1); - _writeRegister(ILI9225_RAM_ADDR_SET2,y1); - break; - case 1: - _writeRegister(ILI9225_RAM_ADDR_SET1,x0); - _writeRegister(ILI9225_RAM_ADDR_SET2,y1); - break; - case 2: - _writeRegister(ILI9225_RAM_ADDR_SET1,x1); - _writeRegister(ILI9225_RAM_ADDR_SET2,y0); - break; - case 3: - _writeRegister(ILI9225_RAM_ADDR_SET1,x0); - _writeRegister(ILI9225_RAM_ADDR_SET2,y0); - break; - } - _writeCommand16( ILI9225_GRAM_DATA_REG ); - - //_writeRegister(ILI9225_RAM_ADDR_SET1,x0); - //_writeRegister(ILI9225_RAM_ADDR_SET2,y0); - - //_writeCommand(0x00, 0x22); - - endWrite(); -} - - -void TFT22_ILI9225::_resetWindow() { - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1, 0x00AF); - _writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2, 0x0000); - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR1, 0x00DB); - _writeRegister(ILI9225_VERTICAL_WINDOW_ADDR2, 0x0000); - -} - -void TFT22_ILI9225::clear() { - uint8_t old = _orientation; - setOrientation(0); - fillRectangle(0, 0, _maxX - 1, _maxY - 1, COLOR_BLACK); - setOrientation(old); - delay(10); -} - - -void TFT22_ILI9225::invert(boolean flag) { - startWrite(); - _writeCommand16(flag ? ILI9225C_INVON : ILI9225C_INVOFF); - //_writeCommand(0x00, flag ? ILI9225C_INVON : ILI9225C_INVOFF); - endWrite(); -} - - -void TFT22_ILI9225::setBacklight(boolean flag) { - blState = flag; -#ifndef ESP32 - if (_led) analogWrite(_led, blState ? _brightness : 0); -#endif -} - - -void TFT22_ILI9225::setBacklightBrightness(uint8_t brightness) { - _brightness = brightness; - setBacklight(blState); -} - - -void TFT22_ILI9225::setDisplay(boolean flag) { - if (flag) { - startWrite(); - _writeRegister(0x00ff, 0x0000); - _writeRegister(ILI9225_POWER_CTRL1, 0x0000); - endWrite(); - delay(50); - startWrite(); - _writeRegister(ILI9225_DISP_CTRL1, 0x1017); - endWrite(); - delay(200); - } else { - startWrite(); - _writeRegister(0x00ff, 0x0000); - _writeRegister(ILI9225_DISP_CTRL1, 0x0000); - endWrite(); - delay(50); - startWrite(); - _writeRegister(ILI9225_POWER_CTRL1, 0x0003); - endWrite(); - delay(200); - } -} - -void TFT22_ILI9225::setModeFlip(uint16_t m) { - _modeFlip = m; -} - -void TFT22_ILI9225::setOrientation(uint8_t orientation) { - - _orientation = orientation % 4; - - switch (_orientation) { - case 0: - _maxX = ILI9225_LCD_WIDTH; - _maxY = ILI9225_LCD_HEIGHT; - - break; - case 1: - _maxX = ILI9225_LCD_HEIGHT; - _maxY = ILI9225_LCD_WIDTH; - break; - case 2: - _maxX = ILI9225_LCD_WIDTH; - _maxY = ILI9225_LCD_HEIGHT; - break; - case 3: - _maxX = ILI9225_LCD_HEIGHT; - _maxY = ILI9225_LCD_WIDTH; - break; - } -} - - -uint8_t TFT22_ILI9225::getOrientation() { - return _orientation; -} - - -void TFT22_ILI9225::drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - startWrite(); - drawLine(x1, y1, x1, y2, color); - drawLine(x1, y1, x2, y1, color); - drawLine(x1, y2, x2, y2, color); - drawLine(x2, y1, x2, y2, color); - endWrite(); -} - - -void TFT22_ILI9225::fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - - _setWindow(x1, y1, x2, y2); - - startWrite(); - for(uint16_t t=(y2 - y1 + 1) * (x2 - x1 + 1); t > 0; t--) - _writeData16(color); - endWrite(); - _resetWindow(); -} - - -void TFT22_ILI9225::drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) { - - int16_t f = 1 - r; - int16_t ddF_x = 1; - int16_t ddF_y = -2 * r; - int16_t x = 0; - int16_t y = r; - - startWrite(); - - drawPixel(x0, y0 + r, color); - drawPixel(x0, y0- r, color); - drawPixel(x0 + r, y0, color); - drawPixel(x0 - r, y0, color); - - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - - drawPixel(x0 + x, y0 + y, color); - drawPixel(x0 - x, y0 + y, color); - drawPixel(x0 + x, y0 - y, color); - drawPixel(x0 - x, y0 - y, color); - drawPixel(x0 + y, y0 + x, color); - drawPixel(x0 - y, y0 + x, color); - drawPixel(x0 + y, y0 - x, color); - drawPixel(x0 - y, y0 - x, color); - } - endWrite(); -} - - -void TFT22_ILI9225::fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color) { - - int16_t f = 1 - radius; - int16_t ddF_x = 1; - int16_t ddF_y = -2 * radius; - int16_t x = 0; - int16_t y = radius; - - startWrite(); - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - - drawLine(x0 + x, y0 + y, x0 - x, y0 + y, color); // bottom - drawLine(x0 + x, y0 - y, x0 - x, y0 - y, color); // top - drawLine(x0 + y, y0 - x, x0 + y, y0 + x, color); // right - drawLine(x0 - y, y0 - x, x0 - y, y0 + x, color); // left - } - endWrite(); - fillRectangle(x0-x, y0-y, x0+x, y0+y, color); -} - - -void TFT22_ILI9225::drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - - // Classic Bresenham algorithm - int16_t steep = abs((int16_t)(y2 - y1)) > abs((int16_t)(x2 - x1)); - - int16_t dx, dy; - - if (steep) { - _swap(x1, y1); - _swap(x2, y2); - } - - if (x1 > x2) { - _swap(x1, x2); - _swap(y1, y2); - } - - dx = x2 - x1; - dy = abs((int16_t)(y2 - y1)); - - int16_t err = dx / 2; - int16_t ystep; - - if (y1 < y2) ystep = 1; - else ystep = -1; - - startWrite(); - for (; x1<=x2; x1++) { - if (steep) drawPixel(y1, x1, color); - else drawPixel(x1, y1, color); - - err -= dy; - if (err < 0) { - y1 += ystep; - err += dx; - } - } - endWrite(); -} - - -void TFT22_ILI9225::drawPixel(uint16_t x1, uint16_t y1, uint16_t color) { - - if((x1 >= _maxX) || (y1 >= _maxY)) return; -/* - _setWindow(x1, y1, x1+1, y1+1); - _orientCoordinates(x1, y1); - startWrite(); - //_writeData(color >> 8, color); - _writeData16(color); - endWrite(); -*/ - _orientCoordinates(x1, y1); - startWrite(); - _writeRegister(ILI9225_RAM_ADDR_SET1,x1); - _writeRegister(ILI9225_RAM_ADDR_SET2,y1); - _writeRegister(ILI9225_GRAM_DATA_REG,color); - - endWrite(); - - - -} - - -uint16_t TFT22_ILI9225::maxX() { - return _maxX; -} - - -uint16_t TFT22_ILI9225::maxY() { - return _maxY; -} - - -uint16_t TFT22_ILI9225::setColor(uint8_t red8, uint8_t green8, uint8_t blue8) { - // rgb16 = red5 green6 blue5 - return (red8 >> 3) << 11 | (green8 >> 2) << 5 | (blue8 >> 3); -} - - -void TFT22_ILI9225::splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue) { - // rgb16 = red5 green6 blue5 - red = (rgb & 0b1111100000000000) >> 11 << 3; - green = (rgb & 0b0000011111100000) >> 5 << 2; - blue = (rgb & 0b0000000000011111) << 3; -} - - -void TFT22_ILI9225::_swap(uint16_t &a, uint16_t &b) { - uint16_t w = a; - a = b; - b = w; -} - -// Utilities -/*void TFT22_ILI9225::_writeCommand16(uint16_t command) { -# ifdef HSPI_WRITE16 - SPI_DC_LOW(); - SPI_CS_LOW(); - HSPI_WRITE16(command); - SPI_CS_HIGH(); -#else - _spiWriteCommand(command >> 8); - _spiWriteCommand(0x00ff & command); -#endif -} - -void TFT22_ILI9225::_writeData16(uint16_t data) { -# ifdef HSPI_WRITE16 - SPI_DC_HIGH(); - SPI_CS_LOW(); - HSPI_WRITE16(data); - SPI_CS_HIGH(); -#else - _spiWriteData(data >> 8); - _spiWriteData(0x00ff & data); -#endif -} -*/ -void TFT22_ILI9225::_writeCommand16(uint16_t command) { - SPI_DC_LOW(); - SPI_CS_LOW(); - if ( _clk < 0 ) { - # ifdef HSPI_WRITE16 - HSPI_WRITE16(command); - #else - HSPI_WRITE(command >> 8); - HSPI_WRITE(0x00ff & command); - #endif - } else { - // Fast SPI bitbang swiped from LPD8806 library - for(uint16_t bit = 0x8000; bit; bit >>= 1){ - if((command) & bit){ - SSPI_MOSI_HIGH(); - } else { - SSPI_MOSI_LOW(); - } - SSPI_SCK_HIGH(); - SSPI_SCK_LOW(); - } - } - SPI_CS_HIGH(); -} - -void TFT22_ILI9225::_writeData16(uint16_t data) { - SPI_DC_HIGH(); - SPI_CS_LOW(); - if ( _clk < 0 ) { - # ifdef HSPI_WRITE16 - HSPI_WRITE16(data); - #else - HSPI_WRITE(data >> 8); - HSPI_WRITE(0x00ff & data); - #endif - } else { - // Fast SPI bitbang swiped from LPD8806 library - for(uint16_t bit = 0x8000; bit; bit >>= 1){ - if((data) & bit){ - SSPI_MOSI_HIGH(); - } else { - SSPI_MOSI_LOW(); - } - SSPI_SCK_HIGH(); - SSPI_SCK_LOW(); - } - } - SPI_CS_HIGH(); -} -/*void TFT22_ILI9225::_writeData(uint8_t HI, uint8_t LO) { - _spiWriteData(HI); - _spiWriteData(LO); -} - -void TFT22_ILI9225::_writeCommand(uint8_t HI, uint8_t LO) { - _spiWriteCommand(HI); - _spiWriteCommand(LO); -}*/ - - -void TFT22_ILI9225::_writeRegister(uint16_t reg, uint16_t data) { - _writeCommand16(reg); - _writeData16(data); -} - - -void TFT22_ILI9225::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { - startWrite(); - drawLine(x1, y1, x2, y2, color); - drawLine(x2, y2, x3, y3, color); - drawLine(x3, y3, x1, y1, color); - endWrite(); -} - - -void TFT22_ILI9225::fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { - - uint16_t a, b, y, last; - - // Sort coordinates by Y order (y3 >= y2 >= y1) - if (y1 > y2) { - _swap(y1, y2); _swap(x1, x2); - } - if (y2 > y3) { - _swap(y3, y2); _swap(x3, x2); - } - if (y1 > y2) { - _swap(y1, y2); _swap(x1, x2); - } - - startWrite(); - if (y1 == y3) { // Handle awkward all-on-same-line case as its own thing - a = b = x1; - if (x2 < a) a = x2; - else if (x2 > b) b = x2; - if (x3 < a) a = x3; - else if (x3 > b) b = x3; - drawLine(a, y1, b, y1, color); - return; - } - - int16_t dx11 = x2 - x1, - dy11 = y2 - y1, - dx12 = x3 - x1, - dy12 = y3 - y1, - dx22 = x3 - x2, - dy22 = y3 - y2; - int32_t sa = 0, - sb = 0; - - // For upper part of triangle, find scanline crossings for segments - // 0-1 and 0-2. If y2=y3 (flat-bottomed triangle), the scanline y2 - // is included here (and second loop will be skipped, avoiding a /0 - // error there), otherwise scanline y2 is skipped here and handled - // in the second loop...which also avoids a /0 error here if y1=y2 - // (flat-topped triangle). - if (y2 == y3) last = y2; // Include y2 scanline - else last = y2 - 1; // Skip it - - for (y = y1; y <= last; y++) { - a = x1 + sa / dy11; - b = x1 + sb / dy12; - sa += dx11; - sb += dx12; - /* longhand: - a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); - b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); - */ - if (a > b) _swap(a,b); - drawLine(a, y, b, y, color); - } - - // For lower part of triangle, find scanline crossings for segments - // 0-2 and 1-2. This loop is skipped if y2=y3. - sa = dx22 * (y - y2); - sb = dx12 * (y - y1); - for (; y<=y3; y++) { - a = x2 + sa / dy22; - b = x1 + sb / dy12; - sa += dx22; - sb += dx12; - /* longhand: - a = x2 + (x3 - x2) * (y - y2) / (y3 - y2); - b = x1 + (x3 - x1) * (y - y1) / (y3 - y1); - */ - if (a > b) _swap(a,b); - drawLine(a, y, b, y, color); - } - endWrite(); -} - - -void TFT22_ILI9225::setBackgroundColor(uint16_t color) { - _bgColor = color; -} - -void TFT22_ILI9225::setFont(uint8_t* font, bool monoSp) { - - cfont.font = font; - cfont.width = readFontByte(0); - cfont.height = readFontByte(1); - cfont.offset = readFontByte(2); - cfont.numchars = readFontByte(3); - cfont.nbrows = cfont.height / 8; - cfont.monoSp = monoSp; - - if (cfont.height % 8) cfont.nbrows++; // Set number of bytes used by height of font in multiples of 8 -} - -_currentFont TFT22_ILI9225::getFont() { - return cfont; -} - -uint16_t TFT22_ILI9225::drawText(uint16_t x, uint16_t y, STRING s, uint16_t color) { - - uint16_t currx = x; - - // Print every character in string - #ifdef USE_STRING_CLASS - for (uint8_t k = 0; k < s.length(); k++) { - currx += drawChar(currx, y, s.charAt(k), color) + 1; - } - #else - for (uint8_t k = 0; k < strlen(s); k++) { - currx += drawChar(currx, y, s[k], color) + 1; - } - #endif - return currx; -} - -uint16_t TFT22_ILI9225::getTextWidth( STRING s ) { - - uint16_t width = 0; - // Count every character in string ( +1 for spacing ) - #ifdef USE_STRING_CLASS - for (uint8_t k = 0; k < s.length(); k++) { - width += getCharWidth(s.charAt(k) ) + 1; - } - #else - for (uint8_t k = 0; k < strlen(s); k++) { - width += getCharWidth(s[k]) + 1; - } - #endif - return width; -} - -uint16_t TFT22_ILI9225::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color) { - - uint8_t charData, charWidth; - uint8_t h, i, j; - uint16_t charOffset; - bool fastMode; - charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character - charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) - if ( cfont.monoSp ) charWidth = cfont.width; // monospaced: get char width from font - else charWidth = readFontByte(charOffset); // get chracter width from 1st byte - charOffset++; // increment pointer to first character data byte - - startWrite(); - - // use autoincrement/decrement feature, if character fits completely on screen - fastMode = ( (x+charWidth+1) < _maxX && (y+cfont.height-1) < _maxY ) ; - - if ( fastMode )_setWindow( x,y,x+charWidth+1, y+cfont.height-1 ); // set character Window - - for (i = 0; i <= charWidth; i++) { // each font "column" (+1 blank column for spacing) - h = 0; // keep track of char height - for (j = 0; j < cfont.nbrows; j++) { // each column byte - if (i == charWidth) charData = (uint8_t)0x0; // Insert blank column - else charData = readFontByte(charOffset); - charOffset++; - - // Process every row in font character - for (uint8_t k = 0; k < 8; k++) { - if (h >= cfont.height ) break; // No need to process excess bits - if (fastMode ) _writeData16( bitRead(charData, k)?color:_bgColor ); - else drawPixel( x + i, y + (j * 8) + k, bitRead(charData, k)?color:_bgColor ); - h++; - } - } - } - endWrite(); - _resetWindow(); - return charWidth; -} - -uint16_t TFT22_ILI9225::getCharWidth(uint16_t ch) { - uint16_t charOffset; - charOffset = (cfont.width * cfont.nbrows) + 1; // bytes used by each character - charOffset = (charOffset * (ch - cfont.offset)) + FONT_HEADER_SIZE; // char offset (add 4 for font header) - - return readFontByte(charOffset); // get font width from 1st byte -} - -// Draw a 1-bit image (bitmap) at the specified (x,y) position from the -// provided bitmap buffer (must be PROGMEM memory) using the specified -// foreground color (unset bits are transparent). -void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, -const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { - _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, false ); -} - -// Draw a 1-bit image (bitmap) at the specified (x,y) position from the -// provided bitmap buffer (must be PROGMEM memory) using the specified -// foreground (for set bits) and background (for clear bits) colors. -void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, -const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { - _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, false ); -} - -// drawBitmap() variant for RAM-resident (not PROGMEM) bitmaps. -void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, -uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { - _drawBitmap( x, y, bitmap, w, h, color, 0, true, false, false ); -} - -// drawBitmap() variant w/background for RAM-resident (not PROGMEM) bitmaps. -void TFT22_ILI9225::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { - _drawBitmap( x, y, bitmap, w, h, color, bg, false, false, false ); -} - -//Draw XBitMap Files (*.xbm), exported from GIMP, -//Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. -//C Array can be directly used with this function -void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { - _drawBitmap( x, y, bitmap, w, h, color, 0, true, true, true ); -} - -void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { - _drawBitmap( x, y, bitmap, w, h, color, bg, false, true, true ); -} - - -// internal function for drawing bitmaps with/without transparent bg, or from ram or progmem -void TFT22_ILI9225::_drawBitmap(int16_t x, int16_t y, -const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg, bool transparent, bool progmem,bool Xbit) { - bool noAutoInc = false; // Flag set when transparent pixel was 'written' - int16_t i, j, byteWidth = (w + 7) / 8; - int16_t wx0,wy0,wx1,wy1,wh;//,ww; // Window-position and size - uint8_t byte=0, maskBit; - maskBit = Xbit? 0x01:0x80; - // adjust window hight/width to displaydimensions - DB_PRINT( "DrawBitmap.. maxX=%d, maxY=%d", _maxX,_maxY ); - wx0 = x<0?0:x; - wy0 = y<0?0:y; - wx1 = (x+w>_maxX?_maxX:x+w)-1; - wy1 = (y+h>_maxY?_maxY:y+h)-1; - wh = wy1-wy0 +1; - //ww = wx1-wx0 +1; - _setWindow( wx0,wy0,wx1,wy1,L2R_TopDown); - startWrite(); - for (j = y>=0?0:-y; j < (y>=0?0:-y)+wh; j++) { - for (i = 0; i < w; i++ ) { - if (i & 7) { if ( Xbit ) byte >>=1; else byte <<= 1; } - else { if ( progmem ) byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); - else byte = bitmap[j * byteWidth + i / 8]; - } - if ( x+i >= wx0 && x+i <= wx1 ) { - // write only if pixel is within window - if (byte & maskBit) { - if (noAutoInc) { - //there was a transparent area, set pixelkoordinates again - drawPixel(x + i, y + j, color); - noAutoInc = false; - } - else { - _writeData16(color); - } - } - else { - if (transparent) noAutoInc = true; // no autoincrement in transparent area! - else _writeData16( bg); - } - } - } - } - endWrite(); -} - - - -//Draw XBitMap Files (*.xbm), exported from GIMP, -//Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. -//C Array can be directly used with this function -/*void TFT22_ILI9225::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) { - - int16_t i, j, byteWidth = (w + 7) / 8; - uint8_t byte; - - startWrite(); - for (j = 0; j < h; j++) { - for (i = 0; i < w; i++ ) { - if (i & 7) byte >>= 1; - else byte = pgm_read_byte(bitmap + j * byteWidth + i / 8); - if (byte & 0x01) drawPixel(x + i, y + j, color); - } - } - endWrite(); -} -*/ - -//High speed color bitmap -void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, -const uint16_t** bitmap, int16_t w, int16_t h) { - _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); - startWrite(); - SPI_DC_HIGH(); - SPI_CS_LOW(); - for (uint16_t y = 0; y < h; y++) { - #ifdef HSPI_WRITE_PIXELS - if (_clk < 0) { - HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); - continue; - } - #endif - for (uint16_t x = 0; x < w; x++) { - _spiWrite16(bitmap[y][x]); - } - } - SPI_CS_HIGH(); - endWrite(); -} - -//High speed color bitmap -void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, -uint16_t** bitmap, int16_t w, int16_t h) { - _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); - startWrite(); - SPI_DC_HIGH(); - SPI_CS_LOW(); - for (uint16_t y = 0; y < h; y++) { - #ifdef HSPI_WRITE_PIXELS - if (_clk < 0) { - HSPI_WRITE_PIXELS(bitmap[y], w * sizeof(uint16_t)); - continue; - } - #endif - for (uint16_t x = 0; x < w; x++) { - _spiWrite16(bitmap[y][x]); - } - } - SPI_CS_HIGH(); - endWrite(); -} - -//1-D array High speed color bitmap -void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, -const uint16_t* bitmap, int16_t w, int16_t h) { - _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); - startWrite(); - SPI_DC_HIGH(); - SPI_CS_LOW(); - #ifdef HSPI_WRITE_PIXELS - if (_clk < 0) { - HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); - } else - #endif - for (uint16_t i = 0; i < h * w; ++i) { - _spiWrite16(bitmap[i]); - } - SPI_CS_HIGH(); - endWrite(); -} - -//1-D array High speed color bitmap -void TFT22_ILI9225::drawBitmap(uint16_t x1, uint16_t y1, -uint16_t* bitmap, int16_t w, int16_t h) { - _setWindow(x1, y1, x1+w-1, y1+h-1,L2R_TopDown); - startWrite(); - SPI_DC_HIGH(); - SPI_CS_LOW(); - #ifdef HSPI_WRITE_PIXELS - if (_clk < 0) { - HSPI_WRITE_PIXELS(bitmap, w * h * sizeof(uint16_t)); - } else - #endif - for (uint16_t i = 0; i < h * w; ++i) { - _spiWrite16(bitmap[i]); - } - SPI_CS_HIGH(); - endWrite(); -} - -void TFT22_ILI9225::startWrite(void){ - if (writeFunctionLevel++ == 0) { - SPI_BEGIN_TRANSACTION(); - SPI_CS_LOW(); - } -} - - -void TFT22_ILI9225::endWrite(void){ - if (--writeFunctionLevel == 0) { - SPI_CS_HIGH(); - SPI_END_TRANSACTION(); - } -} - - -// TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- - -void TFT22_ILI9225::setGFXFont(const GFXfont *f) { - gfxFont = (GFXfont *)f; -} - - -// Draw a string -void TFT22_ILI9225::drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color) { - - int16_t currx = x; - - if(gfxFont) { - // Print every character in string - #ifdef USE_STRING_CLASS - for (uint8_t k = 0; k < s.length(); k++) { - currx += drawGFXChar(currx, y, s.charAt(k), color) + 1; - } - #else - for (uint8_t k = 0; k < strlen(s); k++) { - currx += drawGFXChar(currx, y, s[k], color) + 1; - } - #endif - } -} - - -// Draw a character -uint16_t TFT22_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color) { - - c -= (uint8_t)pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); - - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits = 0, bit = 0; - - // Add character clipping here one day - - startWrite(); - for(yy=0; yyfirst); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); - - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits = 0, bit = 0; - for(yy=0; yy=bmheight) continue; - if(y+yo+yy<0) continue; // yo can be negative - for(xx=0; xxfirst), - last = pgm_read_byte(&gfxFont->last); - // Char present in this font? - if((c >= first) && (c <= last)) { - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); - *gw = pgm_read_byte(&glyph->width); - *gh = pgm_read_byte(&glyph->height); - *xa = pgm_read_byte(&glyph->xAdvance); - // int8_t xo = pgm_read_byte(&glyph->xOffset), - // yo = pgm_read_byte(&glyph->yOffset); - } -} - - -void TFT22_ILI9225::getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h) { - *w = *h = 0; - #ifdef USE_STRING_CLASS - for (uint8_t k = 0; k < str.length(); k++) { - uint8_t c = str.charAt(k); - #else - for (uint8_t k = 0; k < strlen(str); k++) { - uint8_t c = str[k]; - #endif - int16_t gw, gh, xa; - getGFXCharExtent(c, &gw, &gh, &xa); - if(gh > *h) { - *h = gh; - } - *w += xa + 1; - } - if(*w>0) (*w)--; -} - diff --git a/RX_FSK/src/TFT22_ILI9225.h b/RX_FSK/src/TFT22_ILI9225.h deleted file mode 100644 index ce4acd3..0000000 --- a/RX_FSK/src/TFT22_ILI9225.h +++ /dev/null @@ -1,471 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -// original source: https://github.com/Nkawu/TFT22_ILI9225 - -#ifndef TFT22_ILI9225_h -#define TFT22_ILI9225_h - -#ifdef __STM32F1__ -#define ARDUINO_STM32_FEATHER -#define PROGMEM -// if 'SPI_CHANNEL' is not defined, 'SPI' is used, only valid for STM32F1 -//#define SPI_CHANNEL SPI_2 -#endif - -#define USE_STRING_CLASS - -#ifdef USE_STRING_CLASS - #define STRING String -#else - #define STRING const char * -#endif - -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif -#include -#include "gfxfont.h" - -#if defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1) -typedef volatile uint32 RwReg; -#endif -#if defined(ARDUINO_FEATHER52) -typedef volatile uint32_t RwReg; -#endif - -/* ILI9225 screen size */ -#define ILI9225_LCD_WIDTH 176 -#define ILI9225_LCD_HEIGHT 220 - -/* ILI9225 LCD Registers */ -#define ILI9225_DRIVER_OUTPUT_CTRL (0x01u) // Driver Output Control -#define ILI9225_LCD_AC_DRIVING_CTRL (0x02u) // LCD AC Driving Control -#define ILI9225_ENTRY_MODE (0x03u) // Entry Mode -#define ILI9225_DISP_CTRL1 (0x07u) // Display Control 1 -#define ILI9225_BLANK_PERIOD_CTRL1 (0x08u) // Blank Period Control -#define ILI9225_FRAME_CYCLE_CTRL (0x0Bu) // Frame Cycle Control -#define ILI9225_INTERFACE_CTRL (0x0Cu) // Interface Control -#define ILI9225_OSC_CTRL (0x0Fu) // Osc Control -#define ILI9225_POWER_CTRL1 (0x10u) // Power Control 1 -#define ILI9225_POWER_CTRL2 (0x11u) // Power Control 2 -#define ILI9225_POWER_CTRL3 (0x12u) // Power Control 3 -#define ILI9225_POWER_CTRL4 (0x13u) // Power Control 4 -#define ILI9225_POWER_CTRL5 (0x14u) // Power Control 5 -#define ILI9225_VCI_RECYCLING (0x15u) // VCI Recycling -#define ILI9225_RAM_ADDR_SET1 (0x20u) // Horizontal GRAM Address Set -#define ILI9225_RAM_ADDR_SET2 (0x21u) // Vertical GRAM Address Set -#define ILI9225_GRAM_DATA_REG (0x22u) // GRAM Data Register -#define ILI9225_GATE_SCAN_CTRL (0x30u) // Gate Scan Control Register -#define ILI9225_VERTICAL_SCROLL_CTRL1 (0x31u) // Vertical Scroll Control 1 Register -#define ILI9225_VERTICAL_SCROLL_CTRL2 (0x32u) // Vertical Scroll Control 2 Register -#define ILI9225_VERTICAL_SCROLL_CTRL3 (0x33u) // Vertical Scroll Control 3 Register -#define ILI9225_PARTIAL_DRIVING_POS1 (0x34u) // Partial Driving Position 1 Register -#define ILI9225_PARTIAL_DRIVING_POS2 (0x35u) // Partial Driving Position 2 Register -#define ILI9225_HORIZONTAL_WINDOW_ADDR1 (0x36u) // Horizontal Address Start Position -#define ILI9225_HORIZONTAL_WINDOW_ADDR2 (0x37u) // Horizontal Address End Position -#define ILI9225_VERTICAL_WINDOW_ADDR1 (0x38u) // Vertical Address Start Position -#define ILI9225_VERTICAL_WINDOW_ADDR2 (0x39u) // Vertical Address End Position -#define ILI9225_GAMMA_CTRL1 (0x50u) // Gamma Control 1 -#define ILI9225_GAMMA_CTRL2 (0x51u) // Gamma Control 2 -#define ILI9225_GAMMA_CTRL3 (0x52u) // Gamma Control 3 -#define ILI9225_GAMMA_CTRL4 (0x53u) // Gamma Control 4 -#define ILI9225_GAMMA_CTRL5 (0x54u) // Gamma Control 5 -#define ILI9225_GAMMA_CTRL6 (0x55u) // Gamma Control 6 -#define ILI9225_GAMMA_CTRL7 (0x56u) // Gamma Control 7 -#define ILI9225_GAMMA_CTRL8 (0x57u) // Gamma Control 8 -#define ILI9225_GAMMA_CTRL9 (0x58u) // Gamma Control 9 -#define ILI9225_GAMMA_CTRL10 (0x59u) // Gamma Control 10 - -#define ILI9225C_INVOFF 0x20 -#define ILI9225C_INVON 0x21 - -// autoincrement modes (register ILI9225_ENTRY_MODE, bit 5..3 ) -enum autoIncMode_t { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }; - -/* RGB 16-bit color table definition (RG565) */ -#define COLOR_BLACK 0x0000 /* 0, 0, 0 */ -#define COLOR_WHITE 0xFFFF /* 255, 255, 255 */ -#define COLOR_BLUE 0x001F /* 0, 0, 255 */ -#define COLOR_GREEN 0x07E0 /* 0, 255, 0 */ -#define COLOR_RED 0xF800 /* 255, 0, 0 */ -#define COLOR_NAVY 0x000F /* 0, 0, 128 */ -#define COLOR_DARKBLUE 0x0011 /* 0, 0, 139 */ -#define COLOR_DARKGREEN 0x03E0 /* 0, 128, 0 */ -#define COLOR_DARKCYAN 0x03EF /* 0, 128, 128 */ -#define COLOR_CYAN 0x07FF /* 0, 255, 255 */ -#define COLOR_TURQUOISE 0x471A /* 64, 224, 208 */ -#define COLOR_INDIGO 0x4810 /* 75, 0, 130 */ -#define COLOR_DARKRED 0x8000 /* 128, 0, 0 */ -#define COLOR_OLIVE 0x7BE0 /* 128, 128, 0 */ -#define COLOR_GRAY 0x8410 /* 128, 128, 128 */ -#define COLOR_GREY 0x8410 /* 128, 128, 128 */ -#define COLOR_SKYBLUE 0x867D /* 135, 206, 235 */ -#define COLOR_BLUEVIOLET 0x895C /* 138, 43, 226 */ -#define COLOR_LIGHTGREEN 0x9772 /* 144, 238, 144 */ -#define COLOR_DARKVIOLET 0x901A /* 148, 0, 211 */ -#define COLOR_YELLOWGREEN 0x9E66 /* 154, 205, 50 */ -#define COLOR_BROWN 0xA145 /* 165, 42, 42 */ -#define COLOR_DARKGRAY 0x7BEF /* 128, 128, 128 */ -#define COLOR_DARKGREY 0x7BEF /* 128, 128, 128 */ -#define COLOR_SIENNA 0xA285 /* 160, 82, 45 */ -#define COLOR_LIGHTBLUE 0xAEDC /* 172, 216, 230 */ -#define COLOR_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ -#define COLOR_SILVER 0xC618 /* 192, 192, 192 */ -#define COLOR_LIGHTGRAY 0xC618 /* 192, 192, 192 */ -#define COLOR_LIGHTGREY 0xC618 /* 192, 192, 192 */ -#define COLOR_LIGHTCYAN 0xE7FF /* 224, 255, 255 */ -#define COLOR_VIOLET 0xEC1D /* 238, 130, 238 */ -#define COLOR_AZUR 0xF7FF /* 240, 255, 255 */ -#define COLOR_BEIGE 0xF7BB /* 245, 245, 220 */ -#define COLOR_MAGENTA 0xF81F /* 255, 0, 255 */ -#define COLOR_TOMATO 0xFB08 /* 255, 99, 71 */ -#define COLOR_GOLD 0xFEA0 /* 255, 215, 0 */ -#define COLOR_ORANGE 0xFD20 /* 255, 165, 0 */ -#define COLOR_SNOW 0xFFDF /* 255, 250, 250 */ -#define COLOR_YELLOW 0xFFE0 /* 255, 255, 0 */ - - -/* Font defines */ -#define FONT_HEADER_SIZE 4 // 1: pixel width of 1 font character, 2: pixel height, -#define readFontByte(x) pgm_read_byte(&cfont.font[x]) - -extern uint8_t Terminal6x8[]; -extern uint8_t Terminal11x16[]; -extern uint8_t Terminal12x16[]; -extern uint8_t Trebuchet_MS16x21[]; - -struct _currentFont -{ - uint8_t* font; - uint8_t width; - uint8_t height; - uint8_t offset; - uint8_t numchars; - uint8_t nbrows; - bool monoSp; -}; -#define MONOSPACE 1 - -#if defined (ARDUINO_STM32_FEATHER) - #undef USE_FAST_PINIO -#elif defined (__AVR__) || defined(TEENSYDUINO) || defined(ESP8266) || defined(__arm__) - #define USE_FAST_PINIO -#endif - -/// Main and core class -class TFT22_ILI9225 { - - public: - - TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED); - TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED); - TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t SDI, int8_t CLK, int8_t LED, uint8_t brightness); - TFT22_ILI9225(int8_t RST, int8_t RS, int8_t CS, int8_t LED, uint8_t brightness); - - /// Initialization -#ifndef ESP32 - void begin(void); -#else - void begin(SPIClass &spi=SPI); -#endif - - /// Clear the screen - void clear(void); - - /// Invert screen - /// @param flag true to invert, false for normal screen - void invert(boolean flag); - - /// Switch backlight on or off - /// @param flag true=on, false=off - void setBacklight(boolean flag); - - /// Set backlight brightness - /// @param brightness sets backlight brightness 0-255 - void setBacklightBrightness(uint8_t brightness); - - /// Switch display on or off - /// @param flag true=on, false=off - void setDisplay(boolean flag); - - /// Set orientation - /// @param orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape - void setOrientation(uint8_t orientation); - - /// Get orientation - /// @return orientation orientation, 0=portrait, 1=right rotated landscape, 2=reverse portrait, 3=left rotated landscape - uint8_t getOrientation(void); - - /// Font size, x-axis - /// @return horizontal size of current font, in pixels - // uint8_t fontX(void); - - /// Font size, y-axis - /// @return vertical size of current font, in pixels - // uint8_t fontY(void); - - /// Screen size, x-axis - /// @return horizontal size of the screen, in pixels - /// @note 240 means 240 pixels and thus 0..239 coordinates (decimal) - uint16_t maxX(void); - - /// Screen size, y-axis - /// @return vertical size of the screen, in pixels - /// @note 220 means 220 pixels and thus 0..219 coordinates (decimal) - uint16_t maxY(void); - - /// Draw circle - /// @param x0 center, point coordinate, x-axis - /// @param y0 center, point coordinate, y-axis - /// @param radius radius - /// @param color 16-bit color - void drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color); - - /// Draw solid circle - /// @param x0 center, point coordinate, x-axis - /// @param y0 center, point coordinate, y-axis - /// @param radius radius - /// @param color 16-bit color - void fillCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint16_t color); - - /// Set background color - /// @param color background color, default=black - void setBackgroundColor(uint16_t color = COLOR_BLACK); - - /// Draw line, rectangle coordinates - /// @param x1 start point coordinate, x-axis - /// @param y1 start point coordinate, y-axis - /// @param x2 end point coordinate, x-axis - /// @param y2 end point coordinate, y-axis - /// @param color 16-bit color - void drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); - - /// Draw rectangle, rectangle coordinates - /// @param x1 top left coordinate, x-axis - /// @param y1 top left coordinate, y-axis - /// @param x2 bottom right coordinate, x-axis - /// @param y2 bottom right coordinate, y-axis - /// @param color 16-bit color - void drawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); - - /// Draw solid rectangle, rectangle coordinates - /// @param x1 top left coordinate, x-axis - /// @param y1 top left coordinate, y-axis - /// @param x2 bottom right coordinate, x-axis - /// @param y2 bottom right coordinate, y-axis - /// @param color 16-bit color - void fillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); - - /// Draw pixel - /// @param x1 point coordinate, x-axis - /// @param y1 point coordinate, y-axis - /// @param color 16-bit color - void drawPixel(uint16_t x1, uint16_t y1, uint16_t color); - - /// Draw ASCII Text (pixel coordinates) - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param s text string - /// @param color 16-bit color, default=white - /// @return x-position behind text - uint16_t drawText(uint16_t x, uint16_t y, STRING s, uint16_t color = COLOR_WHITE); - - /// width of an ASCII Text (pixel ) - /// @param s text string - uint16_t getTextWidth( STRING s ) ; - - /// Calculate 16-bit color from 8-bit Red-Green-Blue components - /// @param red red component, 0x00..0xff - /// @param green green component, 0x00..0xff - /// @param blue blue component, 0x00..0xff - /// @return 16-bit color - uint16_t setColor(uint8_t red, uint8_t green, uint8_t blue); - - /// Calculate 8-bit Red-Green-Blue components from 16-bit color - /// @param rgb 16-bit color - /// @param red red component, 0x00..0xff - /// @param green green component, 0x00..0xff - /// @param blue blue component, 0x00..0xff - void splitColor(uint16_t rgb, uint8_t &red, uint8_t &green, uint8_t &blue); - - /// Draw triangle, triangle coordinates - /// @param x1 corner 1 coordinate, x-axis - /// @param y1 corner 1 coordinate, y-axis - /// @param x2 corner 2 coordinate, x-axis - /// @param y2 corner 2 coordinate, y-axis - /// @param x3 corner 3 coordinate, x-axis - /// @param y3 corner 3 coordinate, y-axis - /// @param color 16-bit color - void drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color); - - /// Draw solid triangle, triangle coordinates - /// @param x1 corner 1 coordinate, x-axis - /// @param y1 corner 1 coordinate, y-axis - /// @param x2 corner 2 coordinate, x-axis - /// @param y2 corner 2 coordinate, y-axis - /// @param x3 corner 3 coordinate, x-axis - /// @param y3 corner 3 coordinate, y-axis - /// @param color 16-bit color - void fillTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color); - - /// Set current font - /// @param font Font name - void setFont(uint8_t* font, bool monoSp=false ); // default = proportional - - /// Get current font - _currentFont getFont(); - - /// Draw single character (pixel coordinates) - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param ch ASCII character - /// @param color 16-bit color, default=white - /// @return width of character in display pixels - uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE); - - /// width of an ASCII character (pixel ) - /// @param ch ASCII character - uint16_t getCharWidth( uint16_t ch ) ; - - /// Draw bitmap - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param bitmap - /// @param w width - /// @param h height - /// @param color 16-bit color, default=white - /// @param bg 16-bit color, background - void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); - void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); - void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); - void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); - - void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); - void drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); - - /// Draw bitmap - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param bitmap, 2D 16bit color bitmap - /// @param w width - /// @param h height - void drawBitmap(uint16_t x, uint16_t y, const uint16_t** bitmap, int16_t w, int16_t h); - void drawBitmap(uint16_t x, uint16_t y, uint16_t** bitmap, int16_t w, int16_t h); - - /// Draw bitmap - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param bitmap, 1D 16bit color bitmap - /// @param w width - /// @param h height - void drawBitmap(uint16_t x, uint16_t y, const uint16_t* bitmap, int16_t w, int16_t h); - void drawBitmap(uint16_t x, uint16_t y, uint16_t* bitmap, int16_t w, int16_t h); - - /// Set current GFX font - /// @param f GFX font name defined in include file - void setGFXFont(const GFXfont *f = NULL); - - /// Draw a string with the current GFX font - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param s string to print - /// @param color 16-bit color - void drawGFXText(int16_t x, int16_t y, STRING s, uint16_t color); - - /// Get the width & height of a text string with the current GFX font - /// @param str string to analyze - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param w width in pixels of string - /// @param h height in pixels of string - void getGFXTextExtent(STRING str, int16_t x, int16_t y, int16_t *w, int16_t *h); - - /// Draw a single character with the current GFX font - /// @param x point coordinate, x-axis - /// @param y point coordinate, y-axis - /// @param c character to draw - /// @param color 16-bit color - /// @return width of character in display pixels - uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color); - - uint16_t drawGFXcharBM(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t *bm, int bmwidth, int bmheight); - - void getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa); - - void setModeFlip(uint16_t m); - - private: - - void _spiWrite(uint8_t v); - void _spiWrite16(uint16_t v); - void _spiWriteCommand(uint8_t c); - void _spiWriteData(uint8_t d); - - void _swap(uint16_t &a, uint16_t &b); - void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); - void _setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, autoIncMode_t mode); - void _resetWindow(); - void _drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, - uint16_t color, uint16_t bg, bool transparent, bool progmem, bool Xbit ); - void _orientCoordinates(uint16_t &x1, uint16_t &y1); - void _writeRegister(uint16_t reg, uint16_t data); - void _writeData(uint8_t HI, uint8_t LO); - void _writeData16(uint16_t HILO); - void _writeCommand(uint8_t HI, uint8_t LO); - void _writeCommand16(uint16_t HILO); - uint16_t _maxX, _maxY, _bgColor; - -#if defined (__AVR__) || defined(TEENSYDUINO) - int8_t _rst, _rs, _cs, _sdi, _clk, _led; - #ifdef USE_FAST_PINIO - volatile uint8_t *mosiport, *clkport, *dcport, *rsport, *csport; - uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask; - #endif -#elif defined (__arm__) - int32_t _rst, _rs, _cs, _sdi, _clk, _led; - #ifdef USE_FAST_PINIO - volatile RwReg *mosiport, *clkport, *dcport, *rsport, *csport; - uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask; - #endif -#elif defined (ESP8266) || defined (ESP32) - int8_t _rst, _rs, _cs, _sdi, _clk, _led; - #ifdef USE_FAST_PINIO - volatile uint32_t *mosiport, *clkport, *dcport, *rsport, *csport; - uint32_t mosipinmask, clkpinmask, cspinmask, dcpinmask; - #endif -#else - int8_t _rst, _rs, _cs, _sdi, _clk, _led; -#endif - - uint8_t _orientation, _brightness; - uint16_t _modeFlip; - - // correspondig modes if orientation changed: - const autoIncMode_t modeTab [3][8] = { - // { R2L_BottomUp, BottomUp_R2L, L2R_BottomUp, BottomUp_L2R, R2L_TopDown, TopDown_R2L, L2R_TopDown, TopDown_L2R }// - /* 90° */ { BottomUp_L2R, L2R_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_R2L, R2L_TopDown }, - /*180° */ { L2R_TopDown , TopDown_L2R, R2L_TopDown, TopDown_R2L, L2R_BottomUp, BottomUp_L2R, R2L_BottomUp, BottomUp_R2L}, - /*270° */ { TopDown_R2L , R2L_TopDown, BottomUp_R2L, R2L_BottomUp, TopDown_L2R, L2R_TopDown, BottomUp_L2R, L2R_BottomUp} - }; - - - bool hwSPI, blState; - - _currentFont cfont; - -#ifdef ESP32 - SPIClass _spi; -#endif - - protected: - - uint32_t writeFunctionLevel; - void startWrite(void); - void endWrite(void); - - - GFXfont *gfxFont; -}; - -#endif diff --git a/RX_FSK/src/aprs.cpp b/RX_FSK/src/aprs.cpp index ea6cfdb..492e957 100644 --- a/RX_FSK/src/aprs.cpp +++ b/RX_FSK/src/aprs.cpp @@ -16,6 +16,7 @@ #include #include #include "aprs.h" +#include "RS41.h" extern const char *version_name; extern const char *version_id; diff --git a/RX_FSK/src/aprs.h b/RX_FSK/src/aprs.h index d2e50c5..ca4d515 100644 --- a/RX_FSK/src/aprs.h +++ b/RX_FSK/src/aprs.h @@ -1,8 +1,8 @@ #ifndef _aprs_h #define _aprs_h + #include "Sonde.h" -#include "RS41.h" #define APRS_MAXLEN 201 diff --git a/RX_FSK/src/conn-aprs.cpp b/RX_FSK/src/conn-aprs.cpp new file mode 100644 index 0000000..a4d4d4f --- /dev/null +++ b/RX_FSK/src/conn-aprs.cpp @@ -0,0 +1,320 @@ +#include "../features.h" + +#if FEATURE_APRS + +#include "conn-aprs.h" +#include "aprs.h" +#include "posinfo.h" +#include +#include + +#include +#include + +#include + +// KISS over TCP for communicating with APRSdroid +static WiFiServer tncserver(14580); +static WiFiClient tncclient; + +// APRS over TCP for radiosondy.info etc +static int tcpclient = 0; +enum { TCS_DISCONNECTED, TCS_DNSLOOKUP, TCS_DNSRESOLVED, TCS_CONNECTING, TCS_LOGIN, TCS_CONNECTED }; +static uint8_t tcpclient_state = TCS_DISCONNECTED; +ip_addr_t tcpclient_ipaddr; + +extern const char *version_name; +extern const char *version_id; + +extern WiFiUDP udp; + +static unsigned long last_in = 0; + +void tcpclient_fsm(); + + +void ConnAPRS::init() { + aprs_gencrctab(); +} + +void ConnAPRS::netsetup() { + // Setup for KISS TCP server + if(sonde.config.kisstnc.active) { + MDNS.addService("kiss-tnc", "tcp", 14580); + tncserver.begin(); + } + + if(sonde.config.tcpfeed.active) { + // start the FSM + tcpclient_fsm(); + } +} + +void ConnAPRS::updateSonde( SondeInfo *si ) { + // prepare data (for UDP and TCP output) + char *str = aprs_senddata(si, sonde.config.call, sonde.config.objcall, sonde.config.udpfeed.symbol); + + // Output via AXUDP + if(sonde.config.udpfeed.active) { + char raw[201]; + int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN); + Serial.println("Sending AXUDP"); + //Serial.println(raw); + udp.beginPacket(sonde.config.udpfeed.host, sonde.config.udpfeed.port); + udp.write((const uint8_t *)raw, rawlen); + udp.endPacket(); + } + // KISS via TCP (incoming connection, e.g. from APRSdroid + if (tncclient.connected()) { + Serial.println("Sending position via TCP"); + char raw[201]; + int rawlen = aprsstr_mon2kiss(str, raw, APRS_MAXLEN); + Serial.print("sending: "); Serial.println(raw); + tncclient.write(raw, rawlen); + } + // APRS via TCP (outgoing connection to aprs-is, e.g. radiosonde.info or wettersonde.net + if (sonde.config.tcpfeed.active) { + static unsigned long lasttcp = 0; + tcpclient_fsm(); + if(tcpclient_state == TCS_CONNECTED) { + unsigned long now = millis(); + long tts = sonde.config.tcpfeed.highrate * 1000L - (now-lasttcp); + Serial.printf("aprs: now-last = %ld\n", (now - lasttcp)); + if ( tts < 0 ) { + strcat(str, "\r\n"); + Serial.printf("Sending APRS: %s",str); + write(tcpclient, str, strlen(str)); + lasttcp = now; + } else { + Serial.printf("Sending APRS in %d s\n", (int)(tts/1000)); + } + } + } +} + +#define APRS_TIMEOUT 25000 + +void ConnAPRS::updateStation( PosInfo *pi ) { + // This funciton is called peridocally. + + // We check for stalled connection and possibly close it + Serial.printf("last_in - now: %ld\n", millis() - last_in); + if ( sonde.config.tcpfeed.timeout > 0) { + if ( last_in && ( (millis() - last_in) > sonde.config.tcpfeed.timeout*1000 ) ) { + Serial.println("APRS timeout - closing connection"); + close(tcpclient); + tcpclient_state = TCS_DISCONNECTED; + } + } + + // If available, read data from tcpclient; then send update (if its time for that) + tcpclient_fsm(); + if(sonde.config.tcpfeed.active) { + aprs_station_update(); + } + + // We check for new connections or new data (tnc port) + if (!tncclient.connected()) { + tncclient = tncserver.available(); + if (tncclient.connected()) { + Serial.println("new TCP KISS connection"); + } + } + if (tncclient.available()) { + Serial.print("TCP KISS socket: recevied "); + while (tncclient.available()) { + Serial.print(tncclient.read()); // Check if we receive anything from from APRSdroid + } + Serial.println(""); + } +} + +void ConnAPRS::aprs_station_update() { + int chase = sonde.config.chase; + // automatically decided if CHASE or FIXED mode is used (for config AUTO) + if (chase == SH_LOC_AUTO) { + if (posInfo.chase) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED; + } + unsigned long time_now = millis(); + unsigned long time_delta = time_now - time_last_aprs_update; + unsigned long update_time = (chase == SH_LOC_CHASE) ? APRS_MOBILE_STATION_UPDATE_TIME : APRS_STATION_UPDATE_TIME; + long tts = update_time - time_delta; + Serial.printf("aprs_statio_update due in %d s", (int)(tts/1000)); + if (tts>0) return; + + float lat, lon; + if (chase == SH_LOC_FIXED) { + // fixed location + lat = sonde.config.rxlat; + lon = sonde.config.rxlon; + if (isnan(lat) || isnan(lon)) return; + } else { + if (gpsPos.valid) { + lat = gpsPos.lat; + lon = gpsPos.lon; + } else { + return; + } + } + char *bcn = aprs_send_beacon(sonde.config.call, lat, lon, sonde.config.beaconsym + ((chase == SH_LOC_CHASE) ? 2 : 0), sonde.config.comment); + tcpclient_fsm(); + if(tcpclient_state == TCS_CONNECTED) { + strcat(bcn, "\r\n"); + Serial.printf("APRS TCP BEACON: %s", bcn); + write(tcpclient, bcn, strlen(bcn)); + time_last_aprs_update = time_now; + } +} + +static void _tcp_dns_found(const char * name, const ip_addr_t *ipaddr, void * arg) { + if (ipaddr) { + tcpclient_ipaddr = *ipaddr; + tcpclient_state = TCS_DNSRESOLVED; // DNS lookup success + } else { + memset(&tcpclient_ipaddr, 0, sizeof(tcpclient_ipaddr)); + tcpclient_state = TCS_DISCONNECTED; // DNS lookup failed + } +} + +void tcpclient_sendlogin() { + char buf[128]; + snprintf(buf, 128, "user %s pass %d vers %s %s\r\n", sonde.config.call, sonde.config.passcode, version_name, version_id); + int res = write(tcpclient, buf, strlen(buf)); + Serial.printf("APRS login: %s, res=%d\n", buf, res); + last_in = millis(); + if(res<=0) { + close(tcpclient); + tcpclient_state = TCS_DISCONNECTED; + } +} + +void tcpclient_fsm() { + if(!sonde.config.tcpfeed.active) + return; + Serial.printf("TCS: %d\n", tcpclient_state); + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(tcpclient, &fdset); + fd_set fdeset; + FD_ZERO(&fdeset); + FD_SET(tcpclient, &fdeset); + + struct timeval selto = {0}; + int res; + + switch(tcpclient_state) { + case TCS_DISCONNECTED: + /* We are disconnected. Try to connect, starting with a DNS lookup */ + { + // Restart timeout + last_in = millis(); + err_t res = dns_gethostbyname( sonde.config.tcpfeed.host, &tcpclient_ipaddr, /*(dns_found_callback)*/_tcp_dns_found, NULL ); + + if(res == ERR_OK) { // Returns immediately of host is IP or in cache + tcpclient_state = TCS_DNSRESOLVED; + /* fall through */ + } else if(res == ERR_INPROGRESS) { + tcpclient_state = TCS_DNSLOOKUP; + break; + } else { // failed + tcpclient_state = TCS_DISCONNECTED; + break; + } + } + + case TCS_DNSRESOLVED: + { + /* We have got the IP address, start the connection */ + tcpclient = socket(AF_INET, SOCK_STREAM, 0); + int flags = fcntl(tcpclient, F_GETFL); + if (fcntl(tcpclient, F_SETFL, flags | O_NONBLOCK) == -1) { + Serial.println("Setting O_NONBLOCK failed"); + } + + struct sockaddr_in sock_info; + memset(&sock_info, 0, sizeof(struct sockaddr_in)); + sock_info.sin_family = AF_INET; + sock_info.sin_addr.s_addr = tcpclient_ipaddr.u_addr.ip4.addr; + sock_info.sin_port = htons( sonde.config.tcpfeed.port ); + err_t res = connect(tcpclient, (struct sockaddr *)&sock_info, sizeof(sock_info)); + if(res) { + if (errno == EINPROGRESS) { // Should be the usual case, go to connecting state + tcpclient_state = TCS_CONNECTING; + } else { + close(tcpclient); + tcpclient_state = TCS_DISCONNECTED; + } + } else { + tcpclient_state = TCS_CONNECTED; + tcpclient_sendlogin(); + } + } + break; + case TCS_CONNECTING: + { + // Poll to see if we are now connected + res = select(tcpclient+1, NULL, &fdset, &fdeset, &selto); + if(res<0) { + Serial.println("TNS_CONNECTING: select error"); + goto error; + } else if (res==0) { // still pending + break; + } + // Socket has become ready (or something went wrong, check for error first) + + int sockerr; + socklen_t len = (socklen_t)sizeof(int); + if (getsockopt(tcpclient, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) { + goto error; + } + Serial.printf("select returing %d. isset:%d iseset:%d sockerr:%d\n", res, FD_ISSET(tcpclient, &fdset), FD_ISSET(tcpclient, &fdeset), sockerr); + if(sockerr) { + Serial.printf("APRS connect error: %s\n", strerror(sockerr)); + goto error; + } + tcpclient_state = TCS_CONNECTED; + tcpclient_sendlogin(); + } + break; + + case TCS_CONNECTED: + { + res = select(tcpclient+1, &fdset, NULL, NULL, &selto); + if(res<0) { + Serial.println("TCS_CONNECTING: select error"); + goto error; + } else if (res==0) { // still pending + break; + } + // Read data + char buf[512+1]; + res = read(tcpclient, buf, 512); + if(res<=0) { + close(tcpclient); + tcpclient_state = TCS_DISCONNECTED; + } else { + buf[res] = 0; + Serial.printf("tcpclient data (len=%d):", res); + Serial.write( (uint8_t *)buf, res ); + last_in = millis(); + } + } + break; + + case TCS_DNSLOOKUP: + Serial.println("DNS lookup in progress"); + break; // DNS lookup in progress, do not do anything until callback is called, updating the state + } + return; + +error: + close(tcpclient); + tcpclient_state = TCS_DISCONNECTED; + return; +} + + +ConnAPRS connAPRS; + +#endif diff --git a/RX_FSK/src/conn-aprs.h b/RX_FSK/src/conn-aprs.h new file mode 100644 index 0000000..9d832fd --- /dev/null +++ b/RX_FSK/src/conn-aprs.h @@ -0,0 +1,39 @@ +#include "../features.h" +#if FEATURE_APRS + +#ifndef conn_aprs_h +#define conn_aprs_h + +#include "conn.h" +#include "aprs.h" + +// Times in ms, i.e. station: 10 minutes, mobile: 20 seconds +#define APRS_STATION_UPDATE_TIME (10*60*1000) +#define APRS_MOBILE_STATION_UPDATE_TIME (20*1000) + +static unsigned long time_last_aprs_update = -APRS_STATION_UPDATE_TIME; + + +class ConnAPRS : public Conn +{ +public: + /* Called once on startup */ + void init(); + + /* Called whenever the network becomes available */ + void netsetup(); + + /* Called approx 1x / second (maybe only if good data is available) */ + void updateSonde( SondeInfo *si ); + + /* Called approx 1x / second* */ + void updateStation( PosInfo *pi ); + +private: + void aprs_station_update(); +}; + +extern ConnAPRS connAPRS; +#endif + +#endif diff --git a/RX_FSK/src/Chasemapper.cpp b/RX_FSK/src/conn-chasemapper.cpp similarity index 62% rename from RX_FSK/src/Chasemapper.cpp rename to RX_FSK/src/conn-chasemapper.cpp index 166abaf..0e46eb8 100644 --- a/RX_FSK/src/Chasemapper.cpp +++ b/RX_FSK/src/conn-chasemapper.cpp @@ -1,8 +1,20 @@ -#include "Chasemapper.h" +#include "../features.h" +#if FEATURE_CHASEMAPPER + +#include "conn-chasemapper.h" +#include extern const char *sondeTypeStrSH[]; +extern WiFiUDP udp; -int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { +void ConnChasemapper::init() { +} + +void ConnChasemapper::netsetup() { +} + + +void ConnChasemapper::updateSonde(SondeInfo *si) { char buf[1024]; struct tm tim; time_t t = si->d.time; @@ -11,8 +23,16 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { if (TYPE_IS_METEO(realtype)) { realtype = si->d.subtype == 1 ? STYPE_M10 : STYPE_M20; } + char prefix[10]; + if(realtype == STYPE_RS41) { + prefix[0] = 0; + } + else { + strncpy(prefix, sondeTypeStrSH[realtype], 10); + strcat(prefix, "-"); + } sprintf(buf, "{ \"type\": \"PAYLOAD_SUMMARY\"," - "\"callsign\": \"%s\"," + "\"callsign\": \"%s%s\"," "\"latitude\": %.5f," "\"longitude\": %.5f," "\"altitude\": %d," @@ -21,6 +41,7 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { "\"time\": \"%02d:%02d:%02d\"," "\"model\": \"%s\"," "\"freq\": \"%.3f MHz\"", + prefix, si->d.ser, si->d.lat, si->d.lon, @@ -38,6 +59,10 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { udp.beginPacket(sonde.config.cm.host, sonde.config.cm.port); udp.write((const uint8_t *)buf, strlen(buf)); udp.endPacket(); - return 0; } +void ConnChasemapper::updateStation(PosInfo *pi) { +} + +ConnChasemapper connChasemapper; +#endif diff --git a/RX_FSK/src/conn-chasemapper.h b/RX_FSK/src/conn-chasemapper.h new file mode 100644 index 0000000..e17b307 --- /dev/null +++ b/RX_FSK/src/conn-chasemapper.h @@ -0,0 +1,17 @@ +#ifndef _CHASEMAPPER_H +#define _CHASEMAPPER_H + +#include "Sonde.h" +#include "conn.h" + +class ConnChasemapper : public Conn { +public: + void init(); + void netsetup(); + void updateSonde( SondeInfo *si ); + void updateStation( PosInfo *pi ); +}; + +extern ConnChasemapper connChasemapper; + +#endif diff --git a/RX_FSK/src/conn-mqtt.cpp b/RX_FSK/src/conn-mqtt.cpp new file mode 100644 index 0000000..cc1ef63 --- /dev/null +++ b/RX_FSK/src/conn-mqtt.cpp @@ -0,0 +1,131 @@ +#include "../features.h" +#if FEATURE_MQTT + +#include +#include "conn-mqtt.h" +#include +#include +#include +#include "json.h" + +extern const char *version_name; +extern const char *version_id; + +/* configuration paramters are in the config, no need to duplicate :-) + {"mqtt.active", 0, &sonde.config.mqtt.active}, + {"mqtt.id", 63, &sonde.config.mqtt.id}, + {"mqtt.host", 63, &sonde.config.mqtt.host}, + {"mqtt.port", 0, &sonde.config.mqtt.port}, + {"mqtt.username", 63, &sonde.config.mqtt.username}, + {"mqtt.password", 63, &sonde.config.mqtt.password}, + {"mqtt.prefix", 63, &sonde.config.mqtt.prefix}, + */ + +TimerHandle_t mqttReconnectTimer; + +/* Global initalization (on TTGO startup) */ +void MQTT::init() { +} + + +// Internal helper function for netsetup +void mqttCallback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i=0;iip); + + Serial.println("[MQTT] pubsub client"); + mqttClient.setServer(ip, sonde.config.mqtt.port); + snprintf(clientID, 20, "%s%04d", sonde.config.mqtt.id, (int)random(0, 1000)); + clientID[20] = 0; + Serial.print(clientID); + mqttClient.setClientId(clientID); + if (strlen(sonde.config.mqtt.password) > 0) { + mqttClient.setCredentials(sonde.config.mqtt.username, sonde.config.mqtt.password); + } +} + + +void MQTT::updateSonde( SondeInfo *si ) { + if(!sonde.config.mqtt.active) + return; + + if(1 /*connected*/) { + Serial.println("Sending sonde info via MQTT"); + // TODO: Check if si is good / fresh + publishPacket(si); + } +} + +void MQTT::updateStation( PosInfo *pi ) { + if(!sonde.config.mqtt.active) + return; + + int now = millis(); + if ( (lastMqttUptime == 0 || (lastMqttUptime + 60000 < now) || (lastMqttUptime > now))) { + publishUptime(); + lastMqttUptime = now; + } +} + +// Internal (private) functions +//void MQTT::connectToMqtt() { +// Serial.println("Connecting to MQTT..."); +// mqttClient.connect(); +//} + +void MQTT::publishUptime() +{ + mqttClient.connect(); // ensure we've got connection + + Serial.println("[MQTT] writing"); + char payload[256]; + // maybe TODO: Use dynamic position if GPS is available? + // rxlat, rxlon only if not empty + snprintf(payload, 256, "{\"uptime\": %lu, \"user\": \"%s\", ", millis(), sonde.config.mqtt.username); + if( !isnan(sonde.config.rxlat) && !isnan(sonde.config.rxlon) ) { + snprintf(payload, 256, "%s\"rxlat\": %.5f, \"rxlon\": %.5f, ", payload, sonde.config.rxlat, sonde.config.rxlon); + } + snprintf(payload, 256, "%s\"SW\": \"%s\", \"VER\": \"%s\"}", payload, version_name, version_id); + Serial.println(payload); + char topic[128]; + snprintf(topic, 128, "%s%s", sonde.config.mqtt.prefix, "uptime"); + mqttClient.publish(topic, 1, 1, payload); +} + +void MQTT::publishPacket(SondeInfo *si) +{ + SondeData *s = &(si->d); + mqttClient.connect(); // ensure we've got connection + + char payload[1024]; + payload[0] = '{'; + int n = sonde2json(payload+1, 1023, si); + if(n<0) { + // ERROR + Serial.println("publishPacket: sonde2json failed, string too long"); + } + strcat(payload, "}"); // terminate payload string + + char topic[128]; + snprintf(topic, 128, "%s%s", sonde.config.mqtt.prefix, "packet"); + Serial.print(payload); + mqttClient.publish(topic, 1, 1, payload); +} + +MQTT connMQTT; +#endif diff --git a/RX_FSK/src/conn-mqtt.h b/RX_FSK/src/conn-mqtt.h new file mode 100644 index 0000000..75e41ee --- /dev/null +++ b/RX_FSK/src/conn-mqtt.h @@ -0,0 +1,52 @@ +#include "../features.h" +#if FEATURE_MQTT + +#ifndef MQTT_h +#define MQTT_h + +#include +#include +#include "Sonde.h" +//#include "RS41.h" +#include "conn.h" + +class MQTT : public Conn +{ +public: + /* Called once on startup */ + void init(); + + /* Called whenever the network becomes available */ + void netsetup(); + + /* Called approx 1x / second (maybe only if good data is available) */ + virtual void updateSonde( SondeInfo *si ); + + /* Called approx 1x / second* */ + virtual void updateStation( PosInfo *pi ); + + +private: + WiFiClient mqttWifiClient; + AsyncMqttClient mqttClient; + TimerHandle_t mqttReconnectTimer; + IPAddress ip; + //uint16_t port; + //const char *username; + //const char *password; + //const char *prefix; + char clientID[21]; + + //void init(const char *host, uint16_t port, const char *id, const char *username, const char *password, const char *prefix); + void publishPacket(SondeInfo *s); + void publishUptime(); + //void connectToMqtt(); + + unsigned long lastMqttUptime = 0; + boolean mqttEnabled; +}; + +extern MQTT connMQTT; +#endif + +#endif diff --git a/RX_FSK/src/conn-sdcard.cpp b/RX_FSK/src/conn-sdcard.cpp new file mode 100644 index 0000000..ebeffec --- /dev/null +++ b/RX_FSK/src/conn-sdcard.cpp @@ -0,0 +1,51 @@ +#include "../features.h" + +#if FEATURE_SDCARD + +#include "conn-sdcard.h" + +// TODO: Move into config +#define CS 13 +#define SYNC_INTERVAL 10 + +void ConnSDCard::init() { + /* Initialize SD card */ + initok = SD.begin(CS); + Serial.printf("SD card init: %s\n", initok?"OK":"Failed"); +} + +void ConnSDCard::netsetup() { + /* empty function, we don't use any network here */ +} + +void ConnSDCard::updateSonde( SondeInfo *si ) { + if (!initok) return; + if (!file) { + file = SD.open("/data.csv", FILE_APPEND); + } + if (!file) { + Serial.println("Error opening file"); + return; + } + SondeData *sd = &si->d; + file.printf("%d,%s,%s,%d," + "%f,%f,%f,%f,%f,%f,%d,%d," + "%d,%d,%d,%d\n", + sd->validID, sd->ser, sd->typestr, sd->subtype, + sd->lat, sd->lon, sd->alt, sd->vs, sd->hs, sd->dir, sd->sats, sd->validPos, + sd->time, sd->frame, sd->vframe, sd->validTime); + wcount++; + if(wcount >= SYNC_INTERVAL) { + file.flush(); + wcount = 0; + } +} + + +void ConnSDCard::updateStation( PosInfo *pi ) { +} + + +ConnSDCard connSDCard; + +#endif diff --git a/RX_FSK/src/conn-sdcard.h b/RX_FSK/src/conn-sdcard.h new file mode 100644 index 0000000..487ecd1 --- /dev/null +++ b/RX_FSK/src/conn-sdcard.h @@ -0,0 +1,42 @@ +/* + * conn-sdcard.h + * Data exporter to SD card + * Copyright (c) 2023 Hansi Reiser, dl9rdz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef conn_sdcard_h +#define conn_sdcard_h + +#include "conn.h" + +//#include "FS.h" +#include "SD.h" + +class ConnSDCard : public Conn +{ +public: + /* Called once on startup */ + void init(); + + /* Called whenever the network becomes available */ + void netsetup(); + + + /* Called approx 1x / second (maybe only if good data is available) */ + virtual void updateSonde( SondeInfo *si ); + + /* Called approx 1x / second* */ + virtual void updateStation( PosInfo *pi ); + + +private: + File file; + uint8_t initok = 0; + uint16_t wcount = 0; + +}; + + +#endif diff --git a/RX_FSK/src/conn.h b/RX_FSK/src/conn.h new file mode 100644 index 0000000..cd001e4 --- /dev/null +++ b/RX_FSK/src/conn.h @@ -0,0 +1,38 @@ +/* + * conn.h + * Interface for external data exporters + * Copyright (c) 2023 Hansi Reiser, dl9rdz + */ + +#ifndef conn_h +#define conn_h + +#include "Sonde.h" + + +// to be moved elsewhere +struct PosInfo { +public: + float lat; + float lon; +}; + + +/* Interface for all data exporters */ +class Conn +{ +public: + /* Called once on startup */ + virtual void init(); + + /* Called whenever the network becomes available */ + virtual void netsetup(); + + /* Called approx 1x / second (maybe only if good data is available) */ + virtual void updateSonde( SondeInfo *si ); + + /* Called approx 1x / second* */ + virtual void updateStation( PosInfo *pi ); + +}; +#endif diff --git a/RX_FSK/src/geteph.cpp b/RX_FSK/src/geteph.cpp index c6f1d86..a610e74 100644 --- a/RX_FSK/src/geteph.cpp +++ b/RX_FSK/src/geteph.cpp @@ -1,3 +1,6 @@ +#include "../features.h" +#if FEATURE_RS92 + #include "time.h" #include "geteph.h" #include @@ -14,7 +17,7 @@ extern WiFiClient client; char outbuf[128]; uint8_t ephstate = EPH_NOTUSED; //enum EPHSTATE { EPH_NOTUSED, EPH_PENDING, EPH_TIMEERR, EPH_ERROR, EPH_EPHERROR, EPH_GOOD }; -const char *ephtxt[] = { "Disabled", "Pending", "Time error", "Fetch error", "Read error", "Good" }; +const char *ephtxt[] = { "Disabled (no RS92 in QRG list or Wifi mode not 3)", "Pending", "Time error", "Fetch error", "Read error", "Good" }; uint8_t getreply() { String s = client.readStringUntil('\n'); @@ -83,22 +86,25 @@ void geteph() { Serial.println("cannot open file\n"); return; } - char host[252]; - strcpy(host, sonde.config.ephftp); - char *buf = strchr(host, '/'); - if(!buf) { Serial.println("Invalid FTP host config"); return; } - *buf = 0; - buf++; + char host[100]; + char buf[200]; + char *ptr = strchr(sonde.config.ephftp, '/'); + if(!ptr) { Serial.println("Invalid FTP host config"); return; } + int hlen = ptr - sonde.config.ephftp; + strncpy(host, sonde.config.ephftp, hlen); + host[hlen] = 0; + snprintf(buf, 200, ptr+1, year, day, year-2000); uint8_t dispw, disph, dispxs, dispys; disp.rdis->getDispSize(&disph, &dispw, &dispxs, &dispys); disp.rdis->clear(); disp.rdis->setFont(FONT_SMALL); disp.rdis->drawString(0, 0, host); // fetch rinex from server - char *ptr = buf + strlen(buf); - snprintf(ptr, 128, "%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000); + // char *ptr = buf + strlen(buf); + // snprintf(ptr, 128, "%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000); + // snprintf(ptr, 128, "%04d/brdc/brdc%03d0.%02dn.gz", year, /*day,*/ day, year-2000); Serial.println("running geteph\n"); - disp.rdis->drawString(0, 1*dispys, ptr+9); + disp.rdis->drawString(0, 1*dispys, buf+9); if(!client.connect(host, 21)) { Serial.printf("FTP connection to %s failed\n", host); @@ -236,3 +242,4 @@ void geteph() { file.close(); ofile.close(); } +#endif diff --git a/RX_FSK/src/mqtt.cpp b/RX_FSK/src/mqtt.cpp deleted file mode 100644 index 61e0995..0000000 --- a/RX_FSK/src/mqtt.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include -#include "mqtt.h" -#include -#include -#include -#include "RS41.h" -#include "json.h" - -extern const char *version_name; -extern const char *version_id; - -TimerHandle_t mqttReconnectTimer; - -void mqttCallback(char* topic, byte* payload, unsigned int length) { - Serial.print("Message arrived ["); - Serial.print(topic); - Serial.print("] "); - for (int i=0;iip); - this->port = port; - this->username = username; - this->password = password; - this->prefix = prefix; - - Serial.println("[MQTT] pubsub client"); - mqttClient.setServer(ip, port); - snprintf(buffer, 20, "%s%04d", id, (int)random(0, 1000)); - buffer[20] = 0; - Serial.print(buffer); - mqttClient.setClientId(buffer); - if (strlen(password) > 0) { - mqttClient.setCredentials(username, password); - } -} - -void MQTT::connectToMqtt() { - Serial.println("Connecting to MQTT..."); - mqttClient.connect(); -} - -void MQTT::publishUptime() -{ - mqttClient.connect(); // ensure we've got connection - - Serial.println("[MQTT] writing"); - char payload[256]; - // maybe TODO: Use dynamic position if GPS is available? - // rxlat, rxlon only if not empty - snprintf(payload, 256, "{\"uptime\": %lu, \"user\": \"%s\", ", millis(), username); - if( !isnan(sonde.config.rxlat) && !isnan(sonde.config.rxlon) ) { - snprintf(payload, 256, "%s\"rxlat\": %.5f, \"rxlon\": %.5f, ", payload, sonde.config.rxlat, sonde.config.rxlon); - } - snprintf(payload, 256, "%s\"SW\": \"%s\", \"VER\": \"%s\"}", payload, version_name, version_id); - Serial.println(payload); - char topic[128]; - snprintf(topic, 128, "%s%s", this->prefix, "uptime"); - mqttClient.publish(topic, 1, 1, payload); -} - -void MQTT::publishPacket(SondeInfo *si) -{ - SondeData *s = &(si->d); - mqttClient.connect(); // ensure we've got connection - - char payload[1024]; - payload[0] = '{'; - int n = sonde2json(payload+1, 1023, si); - if(n<0) { - // ERROR - Serial.println("publishPacket: sonde2json failed, string too long"); - } - -#if 0 - snprintf(payload, 1024, "{" - "\"active\": %d," - "\"freq\": %.2f," - "\"id\": \"%s\"," - "\"ser\": \"%s\"," - "\"validId\": %d," - "\"launchsite\": \"%s\"," - "\"lat\": %.5f," - "\"lon\": %.5f," - "\"alt\": %.1f," - "\"vs\": %.1f," - "\"hs\": %.1f," - "\"dir\": %.1f," - "\"sats\": %d," - "\"validPos\": %d," - "\"time\": %u," - "\"frame\": %u," - "\"validTime\": %d," - "\"rssi\": %d," - "\"afc\": %d," - "\"rxStat\": \"%s\"," - "\"rxStart\": %u," - "\"norxStart\": %u," - "\"viewStart\": %u," - "\"lastState\": %d," - "\"launchKT\": %d," - "\"burstKT\": %d," - "\"countKT\": %d," - "\"crefKT\": %d", - (int)si->active, - si->freq, - s->id, - s->ser, - (int)s->validID, - si->launchsite, - s->lat, - s->lon, - s->alt, - s->vs, - s->hs, - s->dir, - s->sats, - s->validPos, - s->time, - s->frame, - (int)s->validTime, - si->rssi, - si->afc, - si->rxStat, - si->rxStart, - si->norxStart, - si->viewStart, - si->lastState, - s->launchKT, - s->burstKT, - s->countKT, - s->crefKT - ); - if ( !isnan( s->temperature ) ) { - snprintf(payload, 1024, "%s%s%.1f", payload, ",\"temp\": ", s->temperature ); - } - if ( !isnan( s->relativeHumidity ) ) { - snprintf(payload, 1024, "%s%s%.1f", payload, ",\"humidity\": ", s->relativeHumidity ); - } - if ( !isnan( s->pressure ) ) { - snprintf(payload, 1024, "%s%s%.1f", payload, ",\"pressure\": ", s->pressure ); - } - if ( !isnan( s->batteryVoltage && s->batteryVoltage > 0 ) ) { - snprintf(payload, 1024, "%s%s%.1f", payload, ",\"batt\": ", s->batteryVoltage ); - } - char subtype[11]; - if ( RS41::getSubtype( subtype, 11, si) == 0 ) { - snprintf(payload, 1024, "%s%s%s%s", payload, ",\"subtype\": \"", subtype, "\"" ); - } - snprintf(payload, 1024, "%s%s", payload, "}" ); // terminate payload string -#endif - strcat(payload, "}"); // terminate payload string - - char topic[128]; - snprintf(topic, 128, "%s%s", this->prefix, "packet"); - Serial.print(payload); - mqttClient.publish(topic, 1, 1, payload); -} diff --git a/RX_FSK/src/mqtt.h b/RX_FSK/src/mqtt.h deleted file mode 100644 index a24a8b1..0000000 --- a/RX_FSK/src/mqtt.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MQTT_h -#define MQTT_h - -#include -#include -#include "Sonde.h" -#include "RS41.h" - -class MQTT -{ -public: - WiFiClient mqttWifiClient; - AsyncMqttClient mqttClient; - TimerHandle_t mqttReconnectTimer; - IPAddress ip; - uint16_t port; - const char *username; - const char *password; - const char *prefix; - - void init(const char *host, uint16_t port, const char *id, const char *username, const char *password, const char *prefix); - void publishPacket(SondeInfo *s); - void publishUptime(); -private: - void connectToMqtt(); -}; - -#endif diff --git a/RX_FSK/src/nav_gps_vel.cpp b/RX_FSK/src/nav_gps_vel.cpp index 174ec5b..cb4b924 100644 --- a/RX_FSK/src/nav_gps_vel.cpp +++ b/RX_FSK/src/nav_gps_vel.cpp @@ -1,3 +1,6 @@ +#include "../features.h" +#if FEATURE_RS92 + /* SPDX-License-Identifier: GPL-3.0 * based on https://github.com/rs1729/RS/blob/master/rs92/nav_gps_vel.c * @@ -1713,3 +1716,4 @@ int NAV_LinV(int N, SAT_t satv[], double pos_ecef[3], return 0; } +#endif diff --git a/RX_FSK/src/pmu.cpp b/RX_FSK/src/pmu.cpp new file mode 100644 index 0000000..0e791ae --- /dev/null +++ b/RX_FSK/src/pmu.cpp @@ -0,0 +1,545 @@ +#include +#include +#include "pmu.h" +#include "Sonde.h" + +// 0: cleared; 1: set; 2: do not check, also query state of axp via i2c on each loop +uint8_t pmu_irq = 0; +#define PMU_IRQ 35 + +#define AXP192_VMIN 1800 +#define AXP192_VSTEP 100 + + +#define AXP192_IC_TYPE (0x03) + +#define AXP192_DC_MIN 700 +#define AXP192_DC_STEPS 25 + +#define AXP192_LDO_MIN (1800) +#define AXP192_LDO_STEPS (100) + +#define AXP192_VOLTREG_DC1 + +// some registers: +#define AXP192_STATUS (0x00) +#define AXP192_MODE_CHGSTATUS (0x01) + +// Power voltage control register +#define AXP192_DC2OUT_VOL (0x23) +#define AXP192_DC1OUT_VOL (0x26) +#define AXP192_DC3OUT_VOL (0x27) +#define AXP192_LDO23OUT_VOL (0x28) +#define AXP192_GPIO0_VOL (0x91) + +// Power enable registers +#define AXP192_LDO23_DC123_EXT_CTL (0x12) + +// ADC control +#define AXP192_ADC_EN1 (0x82) + +// ADC results +#define AXP192_BAT_AVERVOL_H8 (0x78) +#define AXP192_BAT_AVERVOL_L4 (0x79) +#define AXP192_BAT_AVERCHGCUR_H8 (0x7A) +#define AXP192_BAT_AVERCHGCUR_L4 (0x7B) +#define AXP192_BAT_AVERCHGCUR_L5 (0x7B) +#define AXP192_ACIN_VOL_H8 (0x56) +#define AXP192_ACIN_VOL_L4 (0x57) +#define AXP192_ACIN_CUR_H8 (0x58) +#define AXP192_ACIN_CUR_L4 (0x59) +#define AXP192_VBUS_VOL_H8 (0x5A) +#define AXP192_VBUS_VOL_L4 (0x5B) +#define AXP192_VBUS_CUR_H8 (0x5C) +#define AXP192_VBUS_CUR_L4 (0x5D) +#define AXP192_INTERNAL_TEMP_H8 (0x5E) +#define AXP192_INTERNAL_TEMP_L4 (0x5F) +#define AXP192_TS_IN_H8 (0x62) +#define AXP192_TS_IN_L4 (0x63) +#define AXP192_GPIO0_VOL_ADC_H8 (0x64) +#define AXP192_GPIO0_VOL_ADC_L4 (0x65) +#define AXP192_GPIO1_VOL_ADC_H8 (0x66) +#define AXP192_GPIO1_VOL_ADC_L4 (0x67) +#define AXP192_BAT_AVERDISCHGCUR_H8 (0x7C) +#define AXP192_BAT_AVERDISCHGCUR_L5 (0x7D) + + +// Interrupt enable +#define AXP192_INTEN1 (0x40) +#define AXP192_INTEN2 (0x41) +#define AXP192_INTEN3 (0x42) +#define AXP192_INTEN4 (0x43) +#define AXP192_INTEN5 (0x4A) + +// Int clear. +#define AXP192_INTSTS1 (0x44) +#define AXP192_INTSTS2 (0x45) +#define AXP192_INTSTS3 (0x46) +#define AXP192_INTSTS4 (0x47) +#define AXP192_INTSTS5 (0x4D) + +extern SemaphoreHandle_t axpSemaphore; + +///////////////////////////////////////////////////////////////////////////////////// +/// High-level functions +PMU *PMU::getInstance(TwoWire &wire) { + PMU *pmu = NULL; + // Check if there is some AXP192 or AXP2101 present + uint8_t chipid = readRegisterWire(wire, AXP192_IC_TYPE); + // AXP192: 0x03 AXP2101: 0x4A + if(chipid==0x03) { + pmu = new AXP192PMU(wire); + } + else if (chipid==0x4A) { + pmu = new AXP2101PMU(wire); + } + return pmu; +} + + +int PMU::readRegisterWire(TwoWire &wire, uint8_t reg) { + wire.beginTransmission(AXP192_SLAVE_ADDRESS); + wire.write(reg); + if (wire.endTransmission() != 0) { + return -1; + } + wire.requestFrom(AXP192_SLAVE_ADDRESS, 1U); + return wire.read(); +} +int PMU::readRegister(uint8_t reg) { + return readRegisterWire(_wire, reg); +} +uint16_t PMU::readRegisters_8_4(uint8_t regh, uint8_t regl) +{ + uint8_t hi = readRegister(regh); + uint8_t lo = readRegister(regl); + return (hi << 4) | (lo & 0x0F); +} + +uint16_t PMU::readRegisters_8_5(uint8_t regh, uint8_t regl) +{ + uint8_t hi = readRegister(regh); + uint8_t lo = readRegister(regl); + return (hi << 5) | (lo & 0x1F); +} + +int PMU::writeRegister(uint8_t reg, uint8_t val) { + _wire.beginTransmission(AXP192_SLAVE_ADDRESS); + _wire.write(reg); + _wire.write(val); + return (_wire.endTransmission() == 0) ? 0 : -1; +} +int PMU::getRegisterBit(uint8_t reg, uint8_t bit) { + int val = readRegister(reg); + if (val == -1) { return -1; } + return (val >> bit) & 0x01; +} +int PMU::setRegisterBit(uint8_t reg, uint8_t bit) { + int val = readRegister(reg); + if (val == -1) { return -1; } + return writeRegister(reg, (val | (1<>8 ); + _enableIRQ( AXP192_INTEN3, 0x03 ); + //_enableIRQ( AXP192_INTEN4, mask>>24 ); + //_enableIRQ( AXP192_INTEN5, mask>>32 ); +} + +// Functions for setting voltage output levels +int AXP192PMU::setVoltageReg(uint8_t reg, uint8_t regval) { + int val = readRegister(reg); + if (val==-1) return -1; + val &= 0x80; + val |= regval; + return writeRegister(reg, val); +} + +int AXP192PMU::setDC1(uint16_t millivolt) { + return setVoltageReg(AXP192_DC1OUT_VOL, (millivolt-AXP192_DC_MIN)/AXP192_DC_STEPS ); +} +int AXP192PMU::setDC2(uint16_t millivolt) { + return setVoltageReg(AXP192_DC2OUT_VOL, (millivolt-AXP192_DC_MIN)/AXP192_DC_STEPS ); +} +int AXP192PMU::setDC3(uint16_t millivolt) { + return setVoltageReg(AXP192_DC3OUT_VOL , (millivolt-AXP192_DC_MIN)/AXP192_DC_STEPS ); +} +int AXP192PMU::setLDO2(uint16_t millivolt) { + return setVoltageReg(AXP192_LDO23OUT_VOL, (millivolt-AXP192_LDO_MIN)/AXP192_LDO_STEPS); +} +int AXP192PMU::setLDOio(uint16_t millivolt) { + return setVoltageReg(AXP192_GPIO0_VOL, (millivolt-AXP192_LDO_MIN)/AXP192_LDO_STEPS); +} + + +// LDO23_DC123_EXT_CTL +// 0:DC-DC1, 1:DC-DC3, 2:LDO2, 3:LDO3, 4:DC-DC2, 6:EXTEN +int AXP192PMU::enableDC1(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 0) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 0); +} +int AXP192PMU::enableDC3(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 1) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 1); +} +int AXP192PMU::enableLDO2(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 2) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 2); +} +int AXP192PMU::enableLDO3(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 3) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 3); +} +int AXP192PMU::enableDC2(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 4) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 4); +} +int AXP192PMU::enableEXTEN(bool onoff) { + return onoff ? setRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 6) : clearRegisterBit(AXP192_LDO23_DC123_EXT_CTL, 6); +} + +int AXP192PMU::enableADC(uint8_t channels) { + uint8_t val = readRegister(AXP192_ADC_EN1); + return writeRegister(AXP192_ADC_EN1, val | channels ); +} + +int AXP192PMU::isBatteryConnected() { + return getRegisterBit(AXP192_MODE_CHGSTATUS, 5); +} +int AXP192PMU::isVbusIn() { + return getRegisterBit(AXP192_STATUS, 5); +} +int AXP192PMU::isCharging() { + return getRegisterBit(AXP192_MODE_CHGSTATUS, 6); +} + +#define AXP192_BATT_VOLTAGE_STEP (1.1F) +float AXP192PMU::getBattVoltage() { + return readRegisters_8_4(AXP192_BAT_AVERVOL_H8, AXP192_BAT_AVERVOL_L4) * AXP192_BATT_VOLTAGE_STEP; +} + +#define AXP192_BATT_DISCHARGE_CUR_STEP (0.5F) +float AXP192PMU::getBattDischargeCurrent() { + return readRegisters_8_5(AXP192_BAT_AVERDISCHGCUR_H8, AXP192_BAT_AVERDISCHGCUR_L5) * AXP192_BATT_DISCHARGE_CUR_STEP; +} + +#define AXP192_BATT_CHARGE_CUR_STEP (0.5F) +float AXP192PMU::getBattChargeCurrent() { + return readRegisters_8_5(AXP192_BAT_AVERCHGCUR_H8, AXP192_BAT_AVERCHGCUR_L5) * AXP192_BATT_CHARGE_CUR_STEP; +} + +#define AXP192_ACIN_VOLTAGE_STEP (1.7F) +float AXP192PMU::getAcinVoltage() { + return readRegisters_8_4(AXP192_ACIN_VOL_H8, AXP192_ACIN_VOL_L4) * AXP192_ACIN_VOLTAGE_STEP; +} + +#define AXP192_ACIN_CUR_STEP (0.625F) +float AXP192PMU::getAcinCurrent() { + return readRegisters_8_4(AXP192_ACIN_CUR_H8, AXP192_ACIN_CUR_L4) * AXP192_ACIN_CUR_STEP; +} + +#define AXP192_VBUS_VOLTAGE_STEP (1.7F) +float AXP192PMU::getVbusVoltage() { + return readRegisters_8_4(AXP192_VBUS_VOL_H8, AXP192_VBUS_VOL_L4) * AXP192_VBUS_VOLTAGE_STEP; +} + +#define AXP192_VBUS_CUR_STEP (0.375F) +float AXP192PMU::getVbusCurrent() { + return readRegisters_8_4(AXP192_VBUS_CUR_H8, AXP192_VBUS_CUR_L4) * AXP192_VBUS_CUR_STEP; +} + +#define AXP192_INTERNAL_TEMP_STEP (0.1F) +float AXP192PMU::getTemperature() { + return readRegisters_8_4(AXP192_INTERNAL_TEMP_H8, AXP192_INTERNAL_TEMP_L4) * AXP192_INTERNAL_TEMP_STEP - 144.7; +} + +////////////////////////////////////////////////////////////////// + +/////// Functions for AXP2101 + +// Registers +#define AXP2101_CHARGE_GAUGE_WDT_CTRL (0x18) +#define AXP2101_BTN_BAT_CHG_VOL_SET (0x6A) +#define AXP2101_DC_ONOFF_DVM_CTRL (0x80) +#define AXP2101_LDO_ONOFF_CTRL0 (0x90) +#define AXP2101_LDO_ONOFF_CTRL1 (0x91) + +#define AXP2101_LDO_VOL1_CTRL (0x93) +#define AXP2101_LDO_VOL2_CTRL (0x94) + +#define AXP2101_ADC_CHANNEL_CTRL (0x30) + +// vterm_cfg: Bit 2:0, 4.2V = 011 (3) +#define AXP2101_CHG_V_CFG (0x64) +// ICC_CFG: Bit 4:0: constant current charge current limit, 25*N mA (N<=8), 200+100*(N-8) (N>8) +#define AXP2101_ICC_CFG (0x62) + +// Interrupt enable +#define AXP2101_INTEN1 (0x40) +#define AXP2101_INTEN2 (0x41) +#define AXP2101_INTEN3 (0x42) + +// Interrupt status +#define AXP2101_INTSTS1 (0x48) +#define AXP2101_INTSTS2 (0x49) +#define AXP2101_INTSTS3 (0x4A) + +// Constants +#define AXP2101_ALDO_VOL_MIN (500) +#define AXP2101_ALDO_VOL_STEPS (100) + +#define AXP2101_BTN_VOL_MIN (2600) +#define AXP2101_BTN_VOL_STEPS (100) + +// 200 + 100*(11-8) = 500 +#define AXP2101_CHG_CUR_500MA (0x0B) +#define AXP2101_CHG_VOL_4V2 (3) + + +// return 0: ok, -1: error +int AXP2101PMU::init() { + // Initialize AXP2101, for T-BEAM v1.2 + + // Hard-coded for now, disable DC2/3/4/5 ALDO1,4 BLDO1/2 DLDO1/2 + int val = readRegister(AXP2101_DC_ONOFF_DVM_CTRL); + writeRegister(AXP2101_DC_ONOFF_DVM_CTRL, val & (~0x1E)); // clear Bit 1,2,3,4 (DC2/3/4/5) + + // clear bit 0 (aldo1), 3 (aldo4), 4,5(bldo1/2), 7 (dldo1) + val = readRegister(AXP2101_LDO_ONOFF_CTRL0); + writeRegister(AXP2101_LDO_ONOFF_CTRL0, val & (~0xB9)); + + // clear bit 0 (dldo2) + val = readRegister(AXP2101_LDO_ONOFF_CTRL1); + writeRegister(AXP2101_LDO_ONOFF_CTRL1, val & (~0x01)); + + // Set PowerVDD to 3100mV (GNSS RTC) -- reg 6A [MS412FE data sheet: charge volt 2.8-3.3; standard value 3.1] + val = readRegister(AXP2101_BTN_BAT_CHG_VOL_SET); + if (val == -1) return -1; + val &= 0xF8; + val |= (3100 - AXP2101_BTN_VOL_MIN) / AXP2101_BTN_VOL_STEPS; + writeRegister(AXP2101_BTN_BAT_CHG_VOL_SET, val); + + setRegisterBit(AXP2101_CHARGE_GAUGE_WDT_CTRL, 2); + + // ESP32 VDD 3300mV + // No need to set, automatically open , Don't close it + + // LoRa VDD 3300mV on ALDO2 + val = readRegister(AXP2101_LDO_VOL1_CTRL); + if (val == -1) return -1; + val &= 0xE0; + val |= (3300 - AXP2101_ALDO_VOL_MIN) / AXP2101_ALDO_VOL_STEPS; + writeRegister(AXP2101_LDO_VOL1_CTRL, val); + setRegisterBit(AXP2101_LDO_ONOFF_CTRL0, 1); + + // GNSS VDD 3300mV on ALDO3 + val = readRegister(AXP2101_LDO_VOL2_CTRL); + if (val == -1) return -1; + val &= 0xE0; + val |= (3300 - AXP2101_ALDO_VOL_MIN) / AXP2101_ALDO_VOL_STEPS; + writeRegister(AXP2101_LDO_VOL2_CTRL, val); + setRegisterBit(AXP2101_LDO_ONOFF_CTRL0, 2); + + if (pmu_irq != 2) { + pinMode(PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PMU_IRQ, [] { + pmu_irq = 1; + }, FALLING); + } + + // Set charging configuration: 500mA, 4.2V cut off + + // Set constant current charge limit to 500mA (reguster 0x62) + // Data sheep (7.3.3.) tells that default value is 1.024 A (which should be fine??) + // Data sheet (register table) tells that default value is "{EFUSE,0b,EFUSE}", whatever that is + // Let's set this to 500 mA manually to be sure + val = readRegister(AXP2101_ICC_CFG); + if (val == -1) return -1; + val &= 0xE0; + writeRegister(AXP2101_ICC_CFG, val | AXP2101_CHG_CUR_500MA); + +#if 0 + // Set cut-off voltage to 4.2V (register 0x63) + // This is the default value, so setting it should not be needed. + val = readRegister(AXP2101_CHG_V_CFG); + if (val == -1) return -1; + val &= 0xFC; + writeRegister(AXP2101_CHG_V_CFG, val | AXP2101_CHG_VOL_4V2); +#endif + + // Disable TS measurement, enable vsys, vbus, vbat measurement + // Disable TS is important for T-Beam 1.2 (no TS thermistor), otherwise it will not charge. + writeRegister(AXP2101_ADC_CHANNEL_CTRL, 0x0d); + + // Clear all IRQ + getIrqKeyStatus(); + + // precharge current (reg 0x61): default value (0101b) should be fine + // Termination current (125mA default value) should be fine as well +#if 0 + // Just some debug code + Serial.println("All good \n"); + + for(int i=0; i<0x80; i++) { + val = readRegister(i); + Serial.printf("Reg %x: %x (%d)\n", i, val, val); + } +#endif + + return 0; +} + +void AXP2101PMU::disableAllIRQ() { + writeRegister(AXP2101_INTEN1, 0); + writeRegister(AXP2101_INTEN2, 0); + writeRegister(AXP2101_INTEN3, 0); +} + +void AXP2101PMU::_enableIRQ(uint8_t addr, uint8_t mask) { + int data = readRegister(addr); + writeRegister(addr, data | mask); +} + +// we want KP_SHORT and KP_LONG interrupts... +// IRQen1, in req 0x41h, Bit 2(long)+3(short) (10+11 global) +void AXP2101PMU::enableIRQ() { + //_enableIRQ( AXP2101_INTEN1, mask&0xFF ); + _enableIRQ( AXP2101_INTEN2, 0x0C ); + //_enableIRQ( AXP2101_INTEN3, 0x03 ); +} + +int AXP2101PMU::getIrqKeyStatus() { + int status = readRegister(AXP2101_INTSTS2); + + // Also clear IRQ status + writeRegister(AXP2101_INTSTS1, 0xFF); + writeRegister(AXP2101_INTSTS2, 0xFF); + writeRegister(AXP2101_INTSTS3, 0xFF); + + // + if ( status & 0x04 ) return KP_MID; + if ( status & 0x08 ) return KP_SHORT; + return KP_NONE; +} + +int AXP2101PMU::isBatteryConnected() { return -1; } +int AXP2101PMU::isVbusIn() { return -1; } +int AXP2101PMU::isCharging() { return -1; } +float AXP2101PMU::getBattVoltage() { return -1; } +float AXP2101PMU::getBattDischargeCurrent() { return -1; } +float AXP2101PMU::getBattChargeCurrent() { return -1; } +float AXP2101PMU::getAcinVoltage() { return -1; } +float AXP2101PMU::getAcinCurrent() { return -1; } +float AXP2101PMU::getVbusVoltage() { return -1; } +float AXP2101PMU::getVbusCurrent() { return -1; } +float AXP2101PMU::getTemperature() { return -1; } + + diff --git a/RX_FSK/src/pmu.h b/RX_FSK/src/pmu.h new file mode 100644 index 0000000..f56aaf7 --- /dev/null +++ b/RX_FSK/src/pmu.h @@ -0,0 +1,115 @@ +#include +#include + +#define AXP192_SLAVE_ADDRESS 0x34 + +enum { TYPE_NONE=-1, TYPE_UNKNOWN=0, TYPE_AXP192, TYPE_AXP2101 }; + +class PMU { +protected: + PMU(TwoWire &wire) : _wire(wire) { }; + +public: + TwoWire &_wire; + static PMU *getInstance(TwoWire &wire); + int type; + + static int readRegisterWire(TwoWire &wire, uint8_t reg); + int readRegister(uint8_t reg); + uint16_t readRegisters_8_4(uint8_t reghi, uint8_t reglo); + uint16_t readRegisters_8_5(uint8_t reghi, uint8_t reglo); + int writeRegister(uint8_t reg, uint8_t val); + int getRegisterBit(uint8_t register, uint8_t bit); + int setRegisterBit(uint8_t register, uint8_t bit); + int clearRegisterBit(uint8_t register, uint8_t bit); + + int handleIRQ(); + + virtual int init(); + virtual void disableAllIRQ(); + virtual void enableIRQ(); + virtual int getIrqKeyStatus(); + + virtual int isBatteryConnected(); + virtual int isVbusIn(); + virtual int isCharging(); + virtual float getBattVoltage(); + virtual float getBattDischargeCurrent(); + virtual float getBattChargeCurrent(); + virtual float getAcinVoltage(); + virtual float getAcinCurrent(); + virtual float getVbusVoltage(); + virtual float getVbusCurrent(); + virtual float getTemperature(); +}; + +/* Interface */ +class AXP192PMU : public PMU { +public: + AXP192PMU(TwoWire &wire) : PMU(wire) { type = TYPE_AXP192; }; + int init(); + void disableAllIRQ(); + void enableIRQ(); + int getIrqKeyStatus(); + + int isBatteryConnected(); + int isVbusIn(); + int isCharging(); + float getBattVoltage(); + float getBattDischargeCurrent(); + float getBattChargeCurrent(); + float getAcinVoltage(); + float getAcinCurrent(); + float getVbusVoltage(); + float getVbusCurrent(); + float getTemperature(); + +protected: + void _enableIRQ(uint8_t addr, uint8_t mask); + + int setVoltageReg(uint8_t reg, uint8_t regval); + int setDC1(uint16_t millivolt); + int setDC2(uint16_t millivolt); + int setDC3(uint16_t millivolt); + int setLDO2(uint16_t millivolt); + int setLDOio(uint16_t millivolt); + + int enableDC1(bool onff = true); + int enableDC3(bool onoff = true); + int enableLDO2(bool onoff = true); + int enableLDO3(bool onoff = true); + int enableDC2(bool onoff = true); + int enableEXTEN(bool onoff = true); + + int enableADC(uint8_t channels); +}; + +class AXP2101PMU : public PMU { +public: + AXP2101PMU(TwoWire &wire) : PMU(wire) { }; + int init(); + void disableAllIRQ(); + void enableIRQ(); + int getIrqKeyStatus(); + + int isBatteryConnected(); + int isVbusIn(); + int isCharging(); + float getBattVoltage(); + float getBattDischargeCurrent(); + float getBattChargeCurrent(); + float getAcinVoltage(); + float getAcinCurrent(); + float getVbusVoltage(); + float getVbusCurrent(); + float getTemperature(); + +protected: + void _enableIRQ(uint8_t addr, uint8_t mask); + + int setVBACKUP(uint16_t millivolt); + int setDCDC1(uint16_t millivolt); + int setALDO2(uint16_t millivolt); + int setALDO3(uint16_t millivolt); + +}; diff --git a/RX_FSK/src/posinfo.cpp b/RX_FSK/src/posinfo.cpp new file mode 100644 index 0000000..bcc10a0 --- /dev/null +++ b/RX_FSK/src/posinfo.cpp @@ -0,0 +1,271 @@ +#include "posinfo.h" + +#include + + +// Sation position obtained from GPS (if available) +struct StationPos gpsPos; + +// Station position to use (from GPS or fixed) +struct StationPos posInfo; + + +/* SH_LOC_OFF: never send position information to SondeHub + SH_LOC_FIXED: send fixed position (if specified in config) to sondehub + SH_LOC_CHASE: always activate chase mode and send GPS position (if available) + SH_LOC_AUTO: if there is no valid GPS position, or GPS position < MIN_LOC_AUTO_DIST away from known fixed position: use FIXED mode + otherwise, i.e. if there is a valid GPS position and (either no fixed position in config, or GPS position is far away from fixed position), use CHASE mode. +*/ +// same constants used for SondeHub and APRS + +/* auto mode is chase if valid GPS position and (no fixed location entered OR valid GPS position and distance in lat/lon deg to fixed location > threshold) */ +//#define MIN_LOC_AUTO_DIST 200 /* meter */ +//#define SH_LOC_AUTO_IS_CHASE ( gpsPos.valid && ( (isnan(sonde.config.rxlat) || isnan(sonde.config.rxlon) ) || \ +// calcLatLonDist( gpsPos.lat, gpsPos.lon, sonde.config.rxlat, sonde.config.rxlon ) > MIN_LOC_AUTO_DIST ) ) +//extern float calcLatLonDist(float lat1, float lon1, float lat2, float lon2); + +///// +// set fixed position based on config +void fixedToPosInfo() { + memset(&posInfo, 0, sizeof(posInfo)); + if( isnan(sonde.config.rxlat) || isnan(sonde.config.rxlon) ) + return; + posInfo.lat = sonde.config.rxlat; + posInfo.lon = sonde.config.rxlon; + posInfo.alt = sonde.config.rxalt; + posInfo.valid = 1; +} + + +///// GPS handling functions + +static char buffer[85]; +static MicroNMEA nmea(buffer, sizeof(buffer)); + + +/// Arrg. MicroNMEA changes type definition... so lets auto-infer type +template +//void unkHandler(const MicroNMEA& nmea) { +void unkHandler(T nmea) { + if (strcmp(nmea.getMessageID(), "VTG") == 0) { + const char *s = nmea.getSentence(); + while (*s && *s != ',') s++; + if (*s == ',') s++; else return; + if (*s == ',') return; /// no new course data + int lastCourse = nmea.parseFloat(s, 0, NULL); + Serial.printf("Course update: %d\n", lastCourse); + } else if (strcmp(nmea.getMessageID(), "GST") == 0) { + // get horizontal accuracy for android app on devices without gps + // GPGST,time,rms,-,-,-,stdlat,stdlon,stdalt,cs + const char *s = nmea.getSentence(); + while (*s && *s != ',') s++; // #0: GST + if (*s == ',') s++; else return; + while (*s && *s != ',') s++; // #1: time: skip + if (*s == ',') s++; else return; + while (*s && *s != ',') s++; // #1: rms: skip + if (*s == ',') s++; else return; + while (*s && *s != ',') s++; // #1: (-): skip + if (*s == ',') s++; else return; + while (*s && *s != ',') s++; // #1: (-): skip + if (*s == ',') s++; else return; + while (*s && *s != ',') s++; // #1: (-): skip + if (*s == ',') s++; else return; + // stdlat + int stdlat = nmea.parseFloat(s, 1, NULL); + while (*s && *s != ',') s++; + if (*s == ',') s++; else return; + // stdlong + int stdlon = nmea.parseFloat(s, 1, NULL); + // calculate position error as 1-signma horizontal RMS + // I guess that is equivalent to Androids getAccurac()? + int poserr = 0; + if (stdlat < 10000 && stdlon < 10000) { // larger errors: no GPS fix, avoid overflow in * + poserr = (int)(sqrt(0.5 * (stdlat * stdlat + stdlon * stdlon))); + } + //Serial.printf("\nHorizontal accuracy: %d, %d => %.1fm\n", stdlat, stdlon, 0.1*poserr); + gpsPos.accuracy = poserr; + } +} + +// 1 deg = aprox. 100 km ==> approx. 200m +#define AUTO_CHASE_THRESHOLD 0.002 + +//#define DEBUG_GPS +static bool gpsCourseOld; +static int lastCourse; +void gpsTask(void *parameter) { + nmea.setUnknownSentenceHandler(unkHandler); + + while (1) { + while (Serial2.available()) { + char c = Serial2.read(); + //Serial.print(c); + if (nmea.process(c)) { + gpsPos.valid = nmea.isValid(); + if (gpsPos.valid) { + gpsPos.lon = nmea.getLongitude() * 0.000001; + gpsPos.lat = nmea.getLatitude() * 0.000001; + long alt = 0; + nmea.getAltitude(alt); + gpsPos.alt = (int)(alt / 1000); + gpsPos.course = (int)(nmea.getCourse() / 1000); + gpsCourseOld = false; + if (gpsPos.course == 0) { + // either north or not new + if (lastCourse != 0) // use old value... + { + gpsCourseOld = true; + gpsPos.course = lastCourse; + } + } + if (gpsPos.lon == 0 && gpsPos.lat == 0) gpsPos.valid = false; + } + /* Check if home */ + if(gpsPos.valid) { + float d = fabs(gpsPos.lon - sonde.config.rxlon); + d += fabs(gpsPos.lat - sonde.config.rxlat); + if(!posInfo.chase && d > AUTO_CHASE_THRESHOLD) { + posInfo = gpsPos; + posInfo.chase = 1; + } else if ( posInfo.chase && d < AUTO_CHASE_THRESHOLD/2 ) { + fixedToPosInfo(); + } + } + + gpsPos.hdop = nmea.getHDOP(); + gpsPos.sat = nmea.getNumSatellites(); + gpsPos.speed = nmea.getSpeed() / 1000.0 * 0.514444; // speed is in m/s nmea.getSpeed is in 0.001 knots +#ifdef DEBUG_GPS + uint8_t hdop = nmea.getHDOP(); + Serial.printf(" =>: valid: %d N %f E %f alt %d course:%d dop:%d\n", gpsPos.valid ? 1 : 0, gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.course, hdop); +#endif + } + } + delay(50); + } +} + + +#define UBX_SYNCH_1 0xB5 +#define UBX_SYNCH_2 0x62 +uint8_t ubx_set9k6[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F}; +uint8_t ubx_factorydef[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0x7c }; +uint8_t ubx_hardreset[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x04, 4, 0, 0xff, 0xff, 0, 0, 0x0C, 0x5D }; +// GPGST: Class 0xF0 Id 0x07 +uint8_t ubx_enable_gpgst[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x01, 3, 0, 0xF0, 0x07, 2, 0x03, 0x1F}; + +void dumpGPS() { + while (Serial2.available()) { + char c = Serial2.read(); + Serial.printf("%02x ", (uint8_t)c); + } +} + + +void initGPS() { + if (sonde.config.gps_rxd < 0) return; // GPS disabled + if (sonde.config.gps_txd >= 0) { // TX enable, thus try setting baud to 9600 and do a factory reset + File testfile = SPIFFS.open("/GPSRESET", FILE_READ); + if (testfile && !testfile.isDirectory()) { + testfile.close(); + Serial.println("GPS resetting baud to 9k6..."); + /* TODO: debug: + Sometimes I have seen the Serial2.begin to cause a reset + Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1) + Backtrace: 0x40081d2f:0x3ffc11b0 0x40087969:0x3ffc11e0 0x4000bfed:0x3ffb1db0 0x4008b7dd:0x3ffb1dc0 0x4017afee:0x3ffb1de0 0x4017b04b:0x3ffb1e20 0x4010722b:0x3ffb1e50 0x40107303:0x3ffb1e70 0x4010782d:0x3ffb1e90 0x40103814:0x3ffb1ed0 0x400d8772:0x3ffb1f10 0x400d9057:0x3ffb1f60 0x40107aca:0x3ffb1fb0 0x4008a63e:0x3ffb1fd0 + #0 0x40081d2f:0x3ffc11b0 in _uart_isr at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 + #1 0x40087969:0x3ffc11e0 in _xt_lowint1 at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/xtensa_vectors.S:1154 + #2 0x4000bfed:0x3ffb1db0 in ?? ??:0 + #3 0x4008b7dd:0x3ffb1dc0 in vTaskExitCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/tasks.c:3507 + #4 0x4017afee:0x3ffb1de0 in esp_intr_alloc_intrstatus at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/intr_alloc.c:784 + #5 0x4017b04b:0x3ffb1e20 in esp_intr_alloc at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/intr_alloc.c:784 + #6 0x4010722b:0x3ffb1e50 in uartEnableInterrupt at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 + #7 0x40107303:0x3ffb1e70 in uartAttachRx at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 + #8 0x4010782d:0x3ffb1e90 in uartBegin at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-uart.c:464 + #9 0x40103814:0x3ffb1ed0 in HardwareSerial::begin(unsigned long, unsigned int, signed char, signed char, bool, unsigned long) at /Users/hansi/.platformio/packages/framework-arduinoespressif32/cores/esp32/HardwareSerial.cpp:190 + */ + Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); + delay(200); + Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); + delay(200); + Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + Serial2.write(ubx_set9k6, sizeof(ubx_set9k6)); + Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + delay(1000); + dumpGPS(); + Serial.println("GPS factory reset..."); + Serial2.write(ubx_factorydef, sizeof(ubx_factorydef)); + delay(1000); + dumpGPS(); + delay(1000); + dumpGPS(); + delay(1000); + dumpGPS(); + SPIFFS.remove("/GPSRESET"); + } else if (testfile) { + Serial.println("GPS reset file: not found/isdir"); + testfile.close(); + Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + } + // Enable GPGST messages + Serial2.write(ubx_enable_gpgst, sizeof(ubx_enable_gpgst)); + } else { + Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + } + xTaskCreate( gpsTask, "gpsTask", + 5000, /* stack size */ + NULL, /* paramter */ + 1, /* priority */ + NULL); /* task handle*/ +} + + + +// Getting GPS data from App (phone) + +void parseGpsJson(char *data, int len) { + char *key = NULL; + char *value = NULL; + // very simple json parser: look for ", then key, then ", then :, then number, then , or } or \0 + for (int i = 0; i < len; i++) { + if (key == NULL) { + if (data[i] != '"') continue; + key = data + i + 1; + i += 2; + continue; + } + if (value == NULL) { + if (data[i] != ':') continue; + value = data + i + 1; + i += 2; + continue; + } + if (data[i] == ',' || data[i] == '}' || data[i] == 0) { + // get value + double val = strtod(value, NULL); + // get data + if (strncmp(key, "lat", 3) == 0) { + gpsPos.lat = val; + } + else if (strncmp(key, "lon", 3) == 0) { + gpsPos.lon = val; + } + else if (strncmp(key, "alt", 3) == 0) { + gpsPos.alt = (int)val; + } + else if (strncmp(key, "course", 6) == 0) { + gpsPos.course = (int)val; + } + gpsPos.valid = true; + + // next item: + if (data[i] != ',') break; + key = NULL; + value = NULL; + } + } + if (gpsPos.lat == 0 && gpsPos.lon == 0) gpsPos.valid = false; + Serial.printf("Parse result: lat=%f, lon=%f, alt=%d, valid=%d\n", gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.valid); +} diff --git a/RX_FSK/src/posinfo.h b/RX_FSK/src/posinfo.h new file mode 100644 index 0000000..760eb4c --- /dev/null +++ b/RX_FSK/src/posinfo.h @@ -0,0 +1,39 @@ + +#ifndef _posinfo_h +#define _posinfo_h + +#include +#include "Sonde.h" +#include + +enum { SH_LOC_OFF, SH_LOC_FIXED, SH_LOC_CHASE, SH_LOC_AUTO }; + + +// Handling of station position (GPS, fixed location) + +struct StationPos { + double lat; + double lon; + int alt; + float speed; + int16_t course; + int16_t accuracy; + int16_t hdop; + int8_t sat; + int8_t valid; + int8_t chase; +}; + +extern struct StationPos gpsPos, posInfo; + + +// Initialize GPS chip +void initGPS(); + +// Update position from app (if not local GPS chip) +void parseGpsJson(char *data, int len); + +// Update position from static config +void fixedToPosInfo(); + +#endif diff --git a/RX_FSK/src/rs92gps.cpp b/RX_FSK/src/rs92gps.cpp index 4c31707..74a88b9 100644 --- a/RX_FSK/src/rs92gps.cpp +++ b/RX_FSK/src/rs92gps.cpp @@ -1,3 +1,6 @@ +#include "../features.h" +#if FEATURE_RS92 + /* SPDX-License-Identifier: GPL-3.0 * based on https://github.com/rs1729/RS/blob/master/rs92/rs92gps.c * @@ -1202,3 +1205,4 @@ void get_eph(const char *file) { if (!option_der) d_err = 1000; } +#endif diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 5799f20..1caef98 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "master_v0.9.3"; +const char *version_id = "master_v0.9.4"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=17; diff --git a/partitions-esp32v2.csv b/partitions-esp32v2.csv new file mode 100644 index 0000000..6f68ce1 --- /dev/null +++ b/partitions-esp32v2.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x140000, +app1, app, ota_1, 0x150000,0x140000, +spiffs, data, spiffs, 0x290000,0x160000, +coredump, data, coredump,0x3F0000,0x10000, \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index ddabecb..46be205 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,16 +17,17 @@ data_dir = RX_FSK/data lib_deps_builtin = ; src lib_deps_external = - olikraus/U8g2 @ ^2.28.8 - AXP202X_Library + olikraus/U8g2 @ ^2.35.8 stevemarple/MicroNMEA @ ^2.0.5 - me-no-dev/ESP Async WebServer @ ^1.2.3 - https://github.com/moononournation/Arduino_GFX#v1.1.5 + ; This is an old version with regex support unconditionally compiled in (adds >100k of code) + ; me-no-dev/ESP Async WebServer @ ^1.2.3 + https://github.com/me-no-dev/ESPAsyncWebServer/archive/refs/heads/master.zip + ; https://github.com/moononournation/Arduino_GFX#v1.1.5 + https://github.com/moononournation/Arduino_GFX#v1.2.9 https://github.com/dx168b/async-mqtt-client [env:ttgo-lora32] -platform = https://github.com/platformio/platform-espressif32.git#v3.3.2 -#platform = https://github.com/platformio/platform-espressif32.git#v4.4.0 +platform = https://github.com/platformio/platform-espressif32.git#v6.4.0 board = ttgo-lora32-v1 framework = arduino monitor_speed = 115200 @@ -35,6 +36,8 @@ lib_deps = ${extra.lib_deps_external} paulstoffregen/Time@^1.6.0 lib_ignore = Time +; Same as with ArduinoIDE. Saves around 27k code +build_flags = -DCORE_DEBUG_LEVEL=0 ; Add / remove the following two lines for separate fonts partition in flash ; after changes: @@ -46,3 +49,7 @@ lib_ignore = Time ; extra_scripts = post:scripts/makefontpartition.py ;board_build.partitions = partition-fonts.csv + +; Uncomment the following if you want to have the partition scheme used in the ESP32 board version 2.0.x of ArduinoIDE +; board_build.partitions = partitions-esp32v2.csv + diff --git a/scripts/ttgoconfig b/scripts/ttgoconfig old mode 100644 new mode 100755 index a48e878..8fb5a8c --- a/scripts/ttgoconfig +++ b/scripts/ttgoconfig @@ -3,6 +3,7 @@ import requests import sys import os import socket +import tempfile import esptool ttgohost = "rdzsonde.local" @@ -56,6 +57,7 @@ if len(sys.argv)<=2: print("or: ",sys.argv[0]," file {filename}"); print("or: ",sys.argv[0]," update "); print("or: ",sys.argv[0]," file.bin"); + print("or: ",sys.argv[0]," uploadfs directory"); print("\n", " screens is screens1.txt, screens2.txt, screens3.txt"); print(" networks is networks.txt (Wifi ssid and password)") @@ -94,6 +96,52 @@ if sys.argv[1]=="update": esptool.main() exit(0) + +def getpartinfo(partname): + import gen_esp32part as pt + # flash complete file system + # automatically get file system parameters from ESP (i.e. you need to program the partition table first) + if False: + tmpdir = tempfile.mkdtemp() + partbin = os.path.join(tmpdir, "partition.bin") + sys._argv = sys.argv[:] + sys.argv=[sys._argv[0], "--chip", "esp32", "--baud", "921600", "--before", "default_reset", + "--after", "no_reset", "read_flash", "0x8000", "0x1000", partbin] + esptool.main() + else: + # test only + partbin="partitions-esp32v2.csv" + with open(partbin,"rb") as f: + table, input_is_binary = pt.PartitionTable.from_file(f) + print("Partition table:") + tab = table.to_csv() + print(tab) + OFFSET = -1 + SIZE = -1 + for line in tab.split("\n"): + if line.startswith(partname): + l = line.split(",") + OFFSET = int(l[3],0) + SIZE = l[4] + mult = 1 + if SIZE[-1].upper() == 'K': + SIZE = SIZE[:-1] + mult = 1024 + print("SIZE is:"+SIZE+"!") + SIZE = int(SIZE,0) * mult + + print("File system at ",hex(OFFSET)," size=",hex(SIZE)) + return [OFFSET, SIZE] + +#OFFSET="0x3F0000" +#SIZE="0x10000" + +if sys.argv[1]=="uploadfs": + (offset, size) = getpartinfo("spiffs") + print("Using offset ",offset,"; size is ",size) + exit(0) + + addrinfo = socket.gethostbyname(ttgohost) url = "http://"+addrinfo+"/" print("Using URL ",url)