From 6cf330b143462a40205f376b75486167423c3ed7 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Sun, 9 Jul 2023 12:10:53 +0200 Subject: [PATCH] new master version Merging all recent devel updates into master: Fix stupid bug that caused RS92 to not work with last devel version python3 compatibility Experimental: Modified DFM handling (DFM17 vs DFM09P for 0xC), untested Add support for RS41 short measurement frame 0x7F (#353) fix #342: remove bug in code for right-justified text on OLED display fix GD5 (untested) handle display off timeouts > 127 seconds correctly gps from apk: 0/0 as invalid pos set dhcp dns name to mdns name; preparation of configurable map link (dl2mf merge) sync w/ DL2MF: web interface enhancements fatal bug-- --- .travis.yml | 6 +++ RX_FSK/RX_FSK.ino | 114 ++++++++++++++++++++++++----------------- RX_FSK/data/index.html | 20 ++++---- RX_FSK/data/map.html | 32 ++++++++++++ RX_FSK/data/rdz.js | 25 ++++++--- RX_FSK/data/style.css | 42 +++++++++------ RX_FSK/src/DFM.cpp | 66 ++++++++++++++++++++---- RX_FSK/src/DFM.h | 5 ++ RX_FSK/src/Display.cpp | 8 +-- RX_FSK/src/Display.h | 2 +- RX_FSK/src/RS41.cpp | 21 +++++--- RX_FSK/src/Sonde.cpp | 9 ++++ RX_FSK/src/json.cpp | 17 +++--- RX_FSK/src/mqtt.cpp | 18 ++++--- RX_FSK/version.h | 4 +- platformio.ini | 1 + scripts/makeimage.py | 10 ++-- 17 files changed, 278 insertions(+), 122 deletions(-) create mode 100644 RX_FSK/data/map.html diff --git a/.travis.yml b/.travis.yml index 934091c..78fe604 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ language: c +dist: focal +os: linux env: global: + - python /home/travis/.arduino15/packages/esp32/tools/esptool_py/3.0.0/esptool.py - ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools - MKSPIFFS=/home/travis/.arduino15/packages/esp32/tools/mkspiffs/0.2.3/mkspiffs before_install: + - sudo apt-get install python-is-python3 + - pip uninstall pyserial + - pip install pyserial - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - sleep 3 - export DISPLAY=:1.0 diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 7ca701e..f0904cc 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -19,14 +19,18 @@ #include "src/Display.h" #include "src/Scanner.h" #include "src/geteph.h" +#if FEATURE_RS92 #include "src/rs92gps.h" +#endif #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" #if FEATURE_MQTT #include "src/mqtt.h" @@ -67,6 +71,9 @@ 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 @@ -77,6 +84,7 @@ const char *dfmSubtypeStrSH[16] = { NULL, NULL, NULL, NULL, NULL, NULL, "DFM17", // 0x0D NULL, NULL }; +#endif // Times in ms, i.e. station: 10 minutes, mobile: 20 seconds #define APRS_STATION_UPDATE_TIME (10*60*1000) @@ -108,7 +116,7 @@ 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 my kotlin andoird test stuff +// JSON over TCP for communicating with the rdzSonde (rdzwx-go) Android app WiFiServer rdzserver(14570); WiFiClient rdzclient; // APRS over TCP for radiosondy.info etc @@ -308,7 +316,10 @@ void HTMLBODYEND(char *ptr) { strcat(ptr, ""); } void HTMLSAVEBUTTON(char *ptr) { - strcat(ptr, "
"); + strcat(ptr, "
" + "rdzTTGOserver "); + strcat(ptr, version_id); + strcat(ptr, ""); } const char *createQRGForm() { @@ -419,7 +430,8 @@ void setupWifiList() { const char *createWIFIForm() { char *ptr = message; char tmp[4]; - strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + strcpy(ptr, HTMLHEAD); + strcat(ptr, ""); HTMLBODY(ptr, "wifi.html"); strcat(ptr, ""); for (int i = 0; i < MAX_WIFI; i++) { @@ -430,7 +442,7 @@ const char *createWIFIForm() { i + 1, i < nNetworks ? networks[i].id.c_str() : "", i + 1, i < nNetworks ? networks[i].pw.c_str() : ""); } - strcat(ptr, "
NrSSIDPassword
"); + strcat(ptr, ""); //
"); HTMLSAVEBUTTON(ptr); HTMLBODYEND(ptr); @@ -438,6 +450,8 @@ const char *createWIFIForm() { return message; } +#if 0 + // moved to map.html (active warning is still TODO const char *createSondeHubMap() { SondeInfo *s = &sonde.sondeList[0]; char *ptr = message; @@ -460,6 +474,7 @@ const char *createSondeHubMap() { HTMLBODYEND(ptr); return message; } +#endif const char *handleWIFIPost(AsyncWebServerRequest *request) { char label[10]; @@ -504,7 +519,7 @@ void addSondeStatus(char *ptr, int i) { struct tm ts; SondeInfo *s = &sonde.sondeList[i]; - strcat(ptr, ""); + strcat(ptr, "
"); sprintf(ptr + strlen(ptr), "", s->d.lat, s->d.lon); - strcat(ptr, "
%3.3f MHz, Type: %s
ID: %s", s->freq, sondeTypeLongStr[sonde.realType(s)], s->d.validID ? s->d.id : ""); if (s->d.validID && (TYPE_IS_DFM(s->type) || TYPE_IS_METEO(s->type) || s->type == STYPE_MP3H) ) { @@ -525,13 +540,15 @@ void addSondeStatus(char *ptr, int i) sprintf(ptr + strlen(ptr), "OSM - ", s->d.lat, s->d.lon); sprintf(ptr + strlen(ptr), "Google

\n"); + strcat(ptr, "\n"); } const char *createStatusForm() { char *ptr = message; strcpy(ptr, HTMLHEAD); - strcat(ptr, ""); + strcat(ptr, ""); + HTMLBODY(ptr, "status.html"); + strcat(ptr, "

"); for (int i = 0; i < sonde.config.maxsonde; i++) { int snum = (i + sonde.currentSonde) % sonde.config.maxsonde; @@ -539,7 +556,12 @@ const char *createStatusForm() { addSondeStatus(ptr, snum); } } - strcat(ptr, ""); + strcat(ptr, "
" + "rdzTTGOserver "); + strcat(ptr, version_id); + strcat(ptr, ""); + + HTMLBODYEND(ptr); Serial.printf("Status form: size=%d bytes\n", strlen(message)); return message; } @@ -716,7 +738,8 @@ const int N_CONFIG = (sizeof(config_list) / sizeof(struct st_configitems)); const char *createConfigForm() { char *ptr = message; - strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + strcpy(ptr, HTMLHEAD); + strcat(ptr, ""); HTMLBODY(ptr, "config.html"); strcat(ptr, "
"); strcat(ptr, ""); @@ -760,6 +783,7 @@ const char *createConfigForm() { strcat(ptr, "\");\n"); } strcat(ptr, "configTable();\n "); + strcat(ptr, ""); HTMLSAVEBUTTON(ptr); HTMLBODYEND(ptr); Serial.printf("Config form: size=%d bytes\n", strlen(message)); @@ -827,7 +851,8 @@ const char *ctrllabel[] = {"Receiver/next freq. (short keypress)", "Scanner (dou const char *createControlForm() { char *ptr = message; - strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + strcpy(ptr, HTMLHEAD); + strcat(ptr, ""); HTMLBODY(ptr, "control.html"); for (int i = 0; i < 9; i++) { strcat(ptr, "

"); } } + strcat(ptr, "
" + "rdzTTGOserver "); + strcat(ptr, version_id); + strcat(ptr, ""); HTMLBODYEND(ptr); Serial.printf("Control form: size=%d bytes\n", strlen(message)); return message; @@ -1179,13 +1208,14 @@ void SetupAsyncServer() { request->send(200, "text/html", createWIFIForm()); }); - 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()); @@ -1477,6 +1507,7 @@ void gpsTask(void *parameter) { gpsPos.course = lastCourse; } } + if(gpsPos.lon == 0 && gpsPos.lat == 0) gpsPos.valid = false; } gpsPos.hdop = nmea.getHDOP(); gpsPos.sat = nmea.getNumSatellites(); @@ -1855,12 +1886,17 @@ void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const cha void setup() { char buf[12]; + // Open serial communications and wait for port to open: Serial.begin(/*921600 */115200); for (int i = 0; i < 39; i++) { int v = gpio_get_level((gpio_num_t)i); Serial.printf("%d:%d ", i, v); } + + //NimBLEDevice::init("NimBLE-Arduino"); + //NimBLEServer* pServer = NimBLEDevice::createServer();; + Serial.println(""); #ifdef ESP_MEM_DEBUG esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook); @@ -1868,27 +1904,6 @@ void setup() axpSemaphore = xSemaphoreCreateBinary(); xSemaphoreGive(axpSemaphore); -#if 0 - delay(2000); - // temporary test - volatile uint32_t *ioport = portOutputRegister(digitalPinToPort(4)); - uint32_t portmask = digitalPinToBitMask(4); - int t = millis(); - for (int i = 0; i < 10000000; i++) { - digitalWrite(4, LOW); - digitalWrite(4, HIGH); - } - int res = millis() - t; - Serial.printf("Duration w/ digitalWriteo: %d\n", res); - - t = millis(); - for (int i = 0; i < 10000000; i++) { - *ioport |= portmask; - *ioport &= ~portmask; - } - res = millis() - t; - Serial.printf("Duration w/ fast io: %d\n", res); -#endif for (int i = 0; i < 39; i++) { Serial.printf("%d:%d ", i, initlevels[i]); } @@ -1905,6 +1920,7 @@ void setup() Serial.println("Reading initial configuration"); setupConfigData(); // configuration must be read first due to OLED ports!!! + WiFi.setHostname(sonde.config.mdnsname); // NOT TTGO v1 (fingerprint 64) or Heltec v1/v2 board (fingerprint 4) // and NOT TTGO Lora32 v2.1_1.6 (fingerprint 31/63) @@ -2245,6 +2261,7 @@ void parseGpsJson(char *data) { 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); } @@ -2956,6 +2973,7 @@ void loopWifiScan() { sonde.setIP(localIPstr.c_str(), false); sonde.updateDisplayIP(); wifi_state = WIFI_CONNECTED; +#if FEATURE_RS92 bool hasRS92 = false; for (int i = 0; i < MAXSONDE; i++) { if (sonde.sondeList[i].type == STYPE_RS92) hasRS92 = true; @@ -2965,6 +2983,7 @@ void loopWifiScan() { if (ephstate == EPH_PENDING) ephstate = EPH_ERROR; get_eph("/brdc"); } +#endif delay(3000); } enableNetwork(true); @@ -3274,7 +3293,7 @@ void aprs_station_update() { lon = sonde.config.rxlon; if (isnan(lat) || isnan(lon)) return; } else { - if (gpsPos.valid && gpsPos.lat != 0 && gpsPos.lon != 0) { + if (gpsPos.valid) { lat = gpsPos.lat; lon = gpsPos.lon; } else { @@ -3360,7 +3379,7 @@ void sondehub_station_update(WiFiClient * client, struct st_sondehub * conf) { // We send GPS position: (a) in CHASE mode, (b) in AUTO mode if no fixed location has been specified in config if (chase == SH_LOC_CHASE) { - if (gpsPos.valid && gpsPos.lat != 0 && gpsPos.lon != 0) { + if (gpsPos.valid) { sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]," "\"mobile\": true", @@ -3635,11 +3654,14 @@ 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 && s->d.subtype < 16 ) { - const char *t = dfmSubtypeStrSH[s->d.subtype]; - // as in https://github.com/projecthorus/radiosonde_auto_rx/blob/e680221f69a568e1fdb24e76db679233f32cb027/auto_rx/autorx/sonde_specific.py#L84 - if (t) sprintf(w, "\"subtype\": \"%s\",", t); - else sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype); // Unknown subtype + 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]; + sprintf(w, "\"subtype\": \"%s\",", t); + } + else { + sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype>>4); // Unknown subtype + } w += strlen(w); } else if ( s->type == STYPE_RS41 ) { char buf[11]; @@ -3687,7 +3709,7 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub * // We send GPS position: (a) in CHASE mode, (b) in AUTO mode if no fixed location has been specified in config if (chase == SH_LOC_CHASE) { - if (gpsPos.valid && gpsPos.lat != 0 && gpsPos.lon != 0) { + if (gpsPos.valid) { sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]", gpsPos.lat, gpsPos.lon, gpsPos.alt); } else { sprintf(w, "\"uploader_position\": [null,null,null]"); diff --git a/RX_FSK/data/index.html b/RX_FSK/data/index.html index e2280b1..3e13977 100755 --- a/RX_FSK/data/index.html +++ b/RX_FSK/data/index.html @@ -9,16 +9,15 @@
-

rdzTTGOSonde Server

- QRG - WiFi - Data - LiveMap - Map - Config - Control - About + QRG + Data + Map + LiveMap + Control + Config + WiFi + About @@ -52,7 +51,7 @@
%VERSION_NAME%
- Copyright © 2019-2021 by Hansi Reiser, DL9RDZ
+ Copyright © 2019-2022 by Hansi Reiser, DL9RDZ
(version %VERSION_ID%)

Check for update (requires TTGO internet connection via WiFi)

@@ -76,6 +75,7 @@ See https://www.gnu.org/licenses/gpl-2.0.txt for details.
+
diff --git a/RX_FSK/data/map.html b/RX_FSK/data/map.html new file mode 100644 index 0000000..dab510f --- /dev/null +++ b/RX_FSK/data/map.html @@ -0,0 +1,32 @@ + + + + + + + diff --git a/RX_FSK/data/rdz.js b/RX_FSK/data/rdz.js index 28a5446..9f2a133 100644 --- a/RX_FSK/data/rdz.js +++ b/RX_FSK/data/rdz.js @@ -3,10 +3,19 @@ stypes.set('4', 'RS41'); stypes.set('R', 'RS92'); stypes.set('D', 'DFM'); stypes.set('M', 'M10/M20'); -//stypes.set('2', 'M10/M20'); stypes.set('3', 'MP3H'); -/* (no longer) Used by qrg.html in RX_FSK.ino */ +function footer() { + document.addEventListener("DOMContentLoaded", function(){ + var form = document.querySelector(".wrapper"); + form.addEventListener("input", function() { + document.querySelector(".save").disabled = false; + }); + document.querySelector(".save").disabled = true; + }); +} + +/* Used by qrg.html in RX_FSK.ino */ function prep() { var stlist=document.querySelectorAll("input.stype"); for(txt of stlist){ @@ -28,16 +37,18 @@ function prep() { function qrgTable() { var tab=document.getElementById("divTable"); - var table = ""; + var table = "
IDActiveFreqLaunchsiteMode
"; for(i=0; i"; - table += ""; - table += ""; - table += ""; + table += ""; + table += ""; + table += ""; + table += ""; } table += "
ChActiveFrequencyDecoderLaunchsite
" + (i+1) + "
"; tab.innerHTML = table; prep(); + footer(); } + diff --git a/RX_FSK/data/style.css b/RX_FSK/data/style.css index 8d93559..c5d02b7 100755 --- a/RX_FSK/data/style.css +++ b/RX_FSK/data/style.css @@ -13,6 +13,9 @@ body, html { th.cfg { padding:5pt } +table.stat { + margin:0px 0px 5px 0px; +} .hamburger { position: relative; @@ -43,8 +46,18 @@ th.cfg { flex-grow: 1; border: none; margin: 0; padding: 0; } .footer { + background-color: #333; + display: flex; + justify-content: space-between; } +td.ch { + text-align: right; + padding: 0px 8px; +} +td.act { + text-align: center; +} table, th, td { border: 1px solid black; border-collapse: collapse; @@ -71,7 +84,6 @@ td#sfreq { .tabcontent { display: none; flex: 1; - padding: 6px 12px; border-top: none; flex-direction: column; overflow: auto; @@ -90,24 +102,16 @@ h1{ p{ font-size: 1.5rem; } -.canberemoved_button { - display: inline-block; - background-color: #008CBA; - border: none; - border-radius: 4px; - color: white; - padding: 16px 40px; - text-decoration: none; - font-size: 30px; - margin: 2px; - cursor: pointer; -} + .button2 { background-color: #f44336; } +:disabled.save { + opacity: 0.5; +} .save { - background-color: #0F3376; - border: black; + background-color: #CC1111; /* 0F33C6; */ + border: white; border-width: 1; color: white; padding: 8px 30px; @@ -117,6 +121,14 @@ p{ font-size: 14px; margin: 0 } + +.ttgoinfo { + color: white; + padding: 8px 10px; + display: block; + font-size: 14px; + margin: 0 +} .ctlbtn { background-color: #ccc; border: black; diff --git a/RX_FSK/src/DFM.cpp b/RX_FSK/src/DFM.cpp index e93ab05..a2ad762 100644 --- a/RX_FSK/src/DFM.cpp +++ b/RX_FSK/src/DFM.cpp @@ -16,6 +16,13 @@ #define MAXIDAGE 1800 + +const char *dfmSubtypeLong[] = { "", "DFMxx", "DFM06", "DFM06P", "PS15", + "DFM09", "DFM09P", "DFM17", "DFM17P"}; +const char *dfmSubtypeShort[] = { "", "DFMx", "DFM6", "DF6P", "PS15", + "DFM9", "DF9P", "DF17", "D17P"}; + + /* * observed DAT patterns for DFM-9: * A: 0+1; 2+3; 4+5; 6+7; 8+0 => keep frame in shadowFrame @@ -44,7 +51,10 @@ static struct st_dfmstat { uint8_t nameregtop; uint8_t lastdat; uint8_t cycledone; // 0=no; 1=OK, 2=partially/with errors - float meas[5+2]; + float meas[9]; + uint16_t measok; // Bit-mask showing which meas entries have been received + uint8_t ptu_chan; // always the max channel. used as subtype before, but 0xC can be DFM09 (with P) or DFM17 (w/o P) + uint8_t sensP; // P channel in data (0xC DMF09 (but not 0xC DFM17), 0xD DFM17P, 0x8 DFM6P (but not PS15) (0 or 1) } dfmstate; decoderSetupCfg DFMSetupCfg { @@ -280,8 +290,12 @@ void DFM::finddfname(uint8_t *b) snprintf(sd->id, 10, "D%x ", id); memcpy(sd->ser, sd->id+1, 9); sd->validID = true; - sd->subtype = (st>>4)&0x0F; - strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5); + //sd->subtype = (st>>4)&0x0F; + //strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5); + // Subtype is set later, as we need more data to distingish 0xC DFM09P and DFM17 + sd->subtype = 0; + dfmstate.ptu_chan = (st>>4)&0x0F; + strcpy(sd->typestr, "DFM"); return; } dfmstate.lastfrcnt = 0; @@ -332,8 +346,12 @@ void DFM::finddfname(uint8_t *b) Serial.println(sd->id); memcpy(sd->ser, sd->id+1, 9); sd->validID = true; - sd->subtype = (st>>4)&0x0F; - strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5); + //sd->subtype = (st>>4)&0x0F; + //strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5); + // Subtype is set later, as we need more data to distingish 0xC DFM09P and DFM17 + sd->subtype = 0; + dfmstate.ptu_chan = (st>>4)&0x0F; + strcpy(sd->typestr, "DFM"); } if(dfmstate.nameregok==i) { Serial.print(" ID OK"); @@ -361,13 +379,13 @@ void DFM::finddfname(uint8_t *b) static float get_Temp() { SondeData *si = &(sonde.si()->d); - if(!si->validID) { // type not yet known, so don't try to decode + if(!si->subtype) { // type not yet known, so don't try to decode return NAN; } float f = dfmstate.meas[0], f1 = dfmstate.meas[3], f2 = dfmstate.meas[4]; - if(si->subtype >= 0x0C) { + if(dfmstate.sensP) { f = dfmstate.meas[1]; f1 = dfmstate.meas[5]; f2 = dfmstate.meas[6]; @@ -377,7 +395,8 @@ static float get_Temp() { float BB0 = 3260.0; // B/Kelvin, fit -55C..+40C float T0 = 25 + 273.15; // t0=25C float R0 = 5.0e3; // R0=R25=5k - float Rf = 220e3; // Rf = 220k + float Rf = 220e3; // Rf = 220k, for DFM17: 332k + if( si->subtype==DFM_17 || si->subtype==DFM_17P ) Rf = 332e3; float g = f2/Rf; float R = (f-f1) / g; // meas[0,3,4] > 0 ? float T = 0; // T/Kelvin @@ -398,16 +417,41 @@ void DFM::decodeCFG(uint8_t *cfg) finddfname(cfg); // get meas uint8_t conf_id = (*cfg)>>4; - if(conf_id<=6) { + if(conf_id<=8) { uint32_t val = (cfg[1]<<12) | (cfg[2]<<4) | cfg[3]; uint8_t exp = cfg[0] & 0xF; dfmstate.meas[conf_id] = val / (float)(1<validID && si->subtype==0 && (dfmstate.measok & (1<<6)) ) { + switch(dfmstate.ptu_chan) { + case 0x6: si->subtype = DFM_06; break; + case 0x7: case 0x8: + si->subtype = DFM_06P; dfmstate.sensP = 1; break; // (TODO: OR PS15) + case 0xA: si->subtype = DFM_09; break; + case 0xB: si->subtype = DFM_17; break; + case 0xC: // DFM-09P or DFM-17 + if(dfmstate.meas[6]<220e3) { si->subtype = DFM_09P; dfmstate.sensP = 1; } + else si->subtype = DFM_17; + break; + case 0xD: si->subtype = DFM_17P; dfmstate.sensP = 1; break; + default: si->subtype = DFM_UNK; + } + if( si->subtype == DFM_UNK ) { + snprintf(si->typestr, 5, "DFx%x", dfmstate.ptu_chan); + si->subtype |= (dfmstate.ptu_chan<<4); + } else { + strcpy(si->typestr, dfmSubtypeShort[si->subtype]); + } + } + // get batt - if(si->validID && si->subtype>=0x0A) { + if(si->validID && dfmstate.ptu_chan>=0x0A && si->subtype>0 ) { // otherwise don't try, as we might not have the right type yet... - int cid = (si->subtype >= 0x0C) ? 0x7 : 0x5; + int cid = (dfmstate.sensP) ? 0x7 : 0x5; if(conf_id == cid) { uint16_t val = cfg[1]<<8 | cfg[2]; si->batteryVoltage = val / 1000.0; diff --git a/RX_FSK/src/DFM.h b/RX_FSK/src/DFM.h index 0d690bb..db1aee6 100644 --- a/RX_FSK/src/DFM.h +++ b/RX_FSK/src/DFM.h @@ -20,6 +20,11 @@ #define DFM_NORMAL 0 #define DFM_INVERSE 1 +enum DFMSubtype { DFM_UNDEF, DFM_UNK, DFM_06, DFM_06P, DFM_PS15, DFM_09, DFM_09P, DFM_17, DFM_17P }; + +extern const char *dfmSubtypeLong[]; +extern const char *dfmSubtypeShort[]; + /* Main class */ class DFM : public DecoderBase { diff --git a/RX_FSK/src/Display.cpp b/RX_FSK/src/Display.cpp index 10a8a93..ed92aca 100644 --- a/RX_FSK/src/Display.cpp +++ b/RX_FSK/src/Display.cpp @@ -318,7 +318,7 @@ void U8x8Display::drawString(uint16_t x, uint16_t y, const char *s, int16_t widt } if(width<0) { int l = strlen(buf); - memset(buf, ' ', width-l); + memset(buf, ' ', -width-l); utf2latin15(s, buf+l, 50-l); } u8x8->drawString(x, y, buf); @@ -1554,12 +1554,12 @@ void Display::drawGPS(DispEntry *de) { // equirectangular approximation is good enough if( !VALIDPOS(sonde.si()->d.validPos) ) { snprintf(buf, 16, "no pos "); - if(de->extra && *de->extra=='5') buf[5]=0; + if( de->extra[1]=='5') buf[5]=0; } else if( disp.gpsDist < 0 ) { snprintf(buf, 16, "no gps "); - if(de->extra && *de->extra=='5') buf[5]=0; + if( de->extra[1]=='5') buf[5]=0; } else { - if(de->extra && *de->extra=='5') { // 5-character version: ****m / ***km / **e6m + if( de->extra[1]=='5') { // 5-character version: ****m / ***km / **e6m if(disp.gpsDist>999999) snprintf(buf, 16, "%de6m ", (int)(disp.gpsDist/1000000)); if(disp.gpsDist>9999) snprintf(buf, 16, "%dkm ", (int)(disp.gpsDist/1000)); else snprintf(buf, 16, "%dm ", (int)disp.gpsDist); diff --git a/RX_FSK/src/Display.h b/RX_FSK/src/Display.h index 37dd226..23672c3 100644 --- a/RX_FSK/src/Display.h +++ b/RX_FSK/src/Display.h @@ -163,7 +163,7 @@ public: DispInfo *layouts; int nLayouts; static RawDisplay *rdis; - char dispstate; + uint16_t dispstate; Display(); void init(); diff --git a/RX_FSK/src/RS41.cpp b/RX_FSK/src/RS41.cpp index ef6fbf4..cd907ef 100644 --- a/RX_FSK/src/RS41.cpp +++ b/RX_FSK/src/RS41.cpp @@ -790,6 +790,7 @@ int RS41::decode41(byte *data, int maxlen) posrs41(data+p, len, 0); break; case 'z': // 0x7a is character z - 7A-MEAS temperature and humidity frame + case '\x7f': //0x7f - short MEAS, no pressure { uint32_t tempMeasMain = getint24(data, 560, p+0); uint32_t tempMeasRef1 = getint24(data, 560, p+3); @@ -800,15 +801,23 @@ int RS41::decode41(byte *data, int maxlen) uint32_t tempHumiMain = getint24(data, 560, p+18); uint32_t tempHumiRef1 = getint24(data, 560, p+21); uint32_t tempHumiRef2 = getint24(data, 560, p+24); - uint32_t pressureMain = getint24(data, 560, p+27); - uint32_t pressureRef1 = getint24(data, 560, p+30); - uint32_t pressureRef2 = getint24(data, 560, p+33); - int16_t ptraw = getint16(data, 560, p+38); + uint32_t pressureMain; + uint32_t pressureRef1; + uint32_t pressureRef2; + int16_t ptraw; + if (typ == 'z') { + pressureMain = getint24(data, 560, p+27); + pressureRef1 = getint24(data, 560, p+30); + pressureRef2 = getint24(data, 560, p+33); + ptraw = getint16(data, 560, p+38); + } #if 0 Serial.printf( "External temp: tempMeasMain = %ld, tempMeasRef1 = %ld, tempMeasRef2 = %ld\n", tempMeasMain, tempMeasRef1, tempMeasRef2 ); Serial.printf( "Rel Humidity: humidityMain = %ld, humidityRef1 = %ld, humidityRef2 = %ld\n", humidityMain, humidityRef1, humidityRef2 ); Serial.printf( "Humid sensor: tempHumiMain = %ld, tempHumiRef1 = %ld, tempHumiRef2 = %ld\n", tempHumiMain, tempHumiRef1, tempHumiRef2 ); - Serial.printf( "Pressure sens: pressureMain = %ld, pressureRef1 = %ld, pressureRef2 = %ld\n", pressureMain, pressureRef1, pressureRef2 ); + if (typ == 'z') { + Serial.printf( "Pressure sens: pressureMain = %ld, pressureRef1 = %ld, pressureRef2 = %ld\n", pressureMain, pressureRef1, pressureRef2 ); + } #endif struct subframeBuffer *calibration = (struct subframeBuffer *)(sonde.si()->extra); // temp: 0xF8==bits 3..7 : we need refResistorlow/high, taylorT, polyT @@ -818,7 +827,7 @@ int RS41::decode41(byte *data, int maxlen) bool validHumidity = calibration!=NULL && (calibration->valid & 0x7FE2001FFFF8) == 0x7FE2001FFFF8; // pressure: bits 33 and 37..42 (variant; x25..x2a: matrixP) /// CALIB_P is 0x7E200000000) - bool validPressure = calibration!=NULL && (calibration->valid & CALIB_P)==CALIB_P && calibration->value.names.variant[7]=='P'; + bool validPressure = calibration!=NULL && (calibration->valid & CALIB_P)==CALIB_P && calibration->value.names.variant[7]=='P' && (typ == 'z'); if ( validPressure ) { si->pressure = GetRAP( pressureMain, pressureRef1, pressureRef2, ptraw ); diff --git a/RX_FSK/src/Sonde.cpp b/RX_FSK/src/Sonde.cpp index 55f0357..be54dd7 100644 --- a/RX_FSK/src/Sonde.cpp +++ b/RX_FSK/src/Sonde.cpp @@ -1,9 +1,12 @@ #include #include +#include "../features.h" #include "Sonde.h" #include "RS41.h" +#if FEATURE_RS92 #include "RS92.h" +#endif #include "DFM.h" #include "M10M20.h" #include "MP3H.h" @@ -431,7 +434,9 @@ void Sonde::setup() { dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type ); break; case STYPE_RS92: +#if FEATURE_RS92 rs92.setup( sondeList[rxtask.currentSonde].freq * 1000000); +#endif break; case STYPE_M10: case STYPE_M20: @@ -462,7 +467,9 @@ void Sonde::receive() { res = rs41.receive(); break; case STYPE_RS92: +#if FEATURE_RS92 res = rs92.receive(); +#endif break; case STYPE_M10: case STYPE_M20: @@ -562,7 +569,9 @@ rxloop: rs41.waitRXcomplete(); break; case STYPE_RS92: +#if FEATURE_RS92 rs92.waitRXcomplete(); +#endif break; case STYPE_M10: case STYPE_M20: diff --git a/RX_FSK/src/json.cpp b/RX_FSK/src/json.cpp index 15b80da..8049039 100644 --- a/RX_FSK/src/json.cpp +++ b/RX_FSK/src/json.cpp @@ -1,19 +1,22 @@ #include "json.h" #include "RS41.h" +#include "DFM.h" extern const char *sondeTypeStrSH[]; -extern const char *dfmSubtypeStrSH[]; +//extern const char *dfmSubtypeStrSH[]; static char typestr[11]; const char *getType(SondeInfo *si) { if( si->type == STYPE_RS41 ) { if ( RS41::getSubtype(typestr, 11, si) == 0 ) return typestr; - } else if ( TYPE_IS_DFM(si->type) && si->d.subtype > 0 && si->d.subtype < 16 ) { - const char *t = dfmSubtypeStrSH[si->d.subtype]; - if(t) return t; - sprintf(typestr, "DFMx%X", si->d.subtype); - return typestr; + } else if ( TYPE_IS_DFM(si->type) && si->d.subtype > 0 ) { + const char *t = dfmSubtypeLong[si->d.subtype & 0xf]; + if( (si->d.subtype & 0xf) == DFM_UNK) { + sprintf(typestr, "DFMx%X", si->d.subtype>>4); + return typestr; + } + return t; } return sondeTypeStrSH[sonde.realType(si)]; } @@ -96,7 +99,7 @@ int sonde2json(char *buf, int maxlen, SondeInfo *si) // add only if available if(s->batteryVoltage > 0) { - n = snprintf(buf, maxlen, ",\"bat\": %.1f", s->batteryVoltage); + n = snprintf(buf, maxlen, ",\"batt\": %.1f", s->batteryVoltage); if(n>=maxlen) return -1; buf += n; maxlen -= n; } diff --git a/RX_FSK/src/mqtt.cpp b/RX_FSK/src/mqtt.cpp index c2380d1..61e0995 100644 --- a/RX_FSK/src/mqtt.cpp +++ b/RX_FSK/src/mqtt.cpp @@ -6,11 +6,11 @@ #include "RS41.h" #include "json.h" -TimerHandle_t mqttReconnectTimer; - 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); @@ -51,12 +51,14 @@ void MQTT::publishUptime() mqttClient.connect(); // ensure we've got connection Serial.println("[MQTT] writing"); - //char payload[128]; - //snprintf(payload, 12, "%lu", millis()); - //snprintf(payload, 124, "{\"uptime\": %lu," "\"user\": \"%s\"", millis(), username ); - char payload[128]; - snprintf(payload, 128, "{\"uptime\": %ld, \"user\": \"%s\", \"rxlat\": %.5f, \"rxlon\": %.5f, \"ver\": \"%s\", \"sub\": \"%s\"}", - millis(), username, sonde.config.rxlat, sonde.config.rxlon, version_name, version_id); + 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"); diff --git a/RX_FSK/version.h b/RX_FSK/version.h index bc4b24c..5799f20 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.2"; +const char *version_id = "master_v0.9.3"; const int SPIFFS_MAJOR=2; -const int SPIFFS_MINOR=16; +const int SPIFFS_MINOR=17; diff --git a/platformio.ini b/platformio.ini index 1326766..ddabecb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,6 +26,7 @@ lib_deps_external = [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 board = ttgo-lora32-v1 framework = arduino monitor_speed = 115200 diff --git a/scripts/makeimage.py b/scripts/makeimage.py index ef79ac8..7614535 100755 --- a/scripts/makeimage.py +++ b/scripts/makeimage.py @@ -13,7 +13,7 @@ import subprocess #spiffs, data, spiffs, 0x290000,0x170000, MKSPIFFS = os.environ['MKSPIFFS'] -print "mkspiffs is "+MKSPIFFS +print("mkspiffs is "+MKSPIFFS) OFFSET_BOOTLOADER = 0x1000 OFFSET_PARTITIONS = 0x8000 @@ -33,7 +33,7 @@ partition = esp32tools + "/partitions/default.csv" if os.path.isfile("RX_FSK/partitions.csv"): partition = "RX_FSK/partitions.csv" -with open(partition, 'rb') as csvfile: +with open(partition, 'rt') as csvfile: partreader = csv.reader(csvfile, delimiter=',') for row in partreader: if row[0] == "otadata": @@ -44,9 +44,9 @@ with open(partition, 'rb') as csvfile: OFFSET_SPIFFS = int(row[3],16) SIZE_SPIFFS = int(row[4],16) -print "bootapp0: "+hex(OFFSET_BOOTAPP0) -print "app0: "+hex(OFFSET_APPLICATION) -print "spiffs: "+hex(OFFSET_SPIFFS)+" size "+hex(SIZE_SPIFFS) +print("bootapp0: "+hex(OFFSET_BOOTAPP0)) +print("app0: "+hex(OFFSET_APPLICATION)) +print("spiffs: "+hex(OFFSET_SPIFFS)+" size "+hex(SIZE_SPIFFS)) # create binary partition file_part = "/tmp/partition.bin"