diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 9f38e7e..64d9755 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -629,6 +629,7 @@ struct st_configitems config_list[] = { {"rxlat", -7, &sonde.config.rxlat}, {"rxlon", -7, &sonde.config.rxlon}, {"rxalt", -7, &sonde.config.rxalt}, + {"b2mute", 0, &sonde.config.b2mute}, {"screenfile", 0, &sonde.config.screenfile}, {"display", -6, sonde.config.display}, {"dispsaver", 0, &sonde.config.dispsaver}, @@ -1514,6 +1515,7 @@ void initTouch() { timerAlarmWrite(timer, 300000, true); timerAlarmEnable(timer); */ + if ( IS_TOUCH(sonde.config.button_pin) ) { touchAttachInterrupt(sonde.config.button_pin & 0x7f, touchISR, sonde.config.touch_thresh); Serial.printf("Initializing touch 1 on pin %d\n", sonde.config.button_pin & 0x7f); @@ -1611,11 +1613,21 @@ static void checkTouchButton(Button & button) { } } +static unsigned long t_muted = (unsigned long)-1; + void ledOffCallback() { digitalWrite(sonde.config.led_pout, LOW); } void flashLed(int ms) { if (sonde.config.led_pout >= 0) { + if(t_muted != -1) { + Serial.printf("Muted at %d\n", t_muted); + // t_muted was set by key press to mute LED / buzzer + if(millis()-t_muted < sonde.config.b2mute * 60000L) return; + else t_muted = -1; + Serial.printf("Unmuted\n"); + } + Serial.println("Not muted"); digitalWrite(sonde.config.led_pout, HIGH); ledFlasher.once_ms(ms, ledOffCallback); } @@ -1740,6 +1752,10 @@ int getKeyPressEvent() { if (p == KP_NONE) return EVT_NONE; Serial.printf("Key 2 was pressed [%d]\n", p + 4); + // maybe not the best place, but easy to do: check for B2 medium keypress to mute LED + if(p == KP_MID && sonde.config.b2mute > 0) { + if(t_muted==-1) t_muted = millis(); else t_muted = -1; + } return p + 4; } Serial.printf("Key 1 was pressed [%d]\n", p); @@ -1973,6 +1989,14 @@ void setup() if (sonde.config.type == TYPE_M5_CORE2) { // Core2 uses Pin 38 for MISO SPI.begin(18, 38, 23, -1); + } else if (sonde.config.type == TYPE_M5_CORE) { + SPI.begin(18, 19, 23, -1); + // GPIO26 is reset + pinMode(26, OUTPUT); + digitalWrite(26, 0); + delay(5); + digitalWrite(26, 1); + delay(5); } else { SPI.begin(); } diff --git a/RX_FSK/data/cfg.js b/RX_FSK/data/cfg.js index ad9e7b9..ee2b225 100644 --- a/RX_FSK/data/cfg.js +++ b/RX_FSK/data/cfg.js @@ -8,6 +8,7 @@ var cfgs = [ [ "rxlat", "Receiver fixed latitude"], [ "rxlon", "Receiver fixed longitude"], [ "rxalt", "Receiver fixed altitude"], +[ "b2mute", "Button 2/medium press mutes LED/Buzzer (minutes)"], [ "", "OLED/TFT display configuration", "https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/Display-configuration" ], [ "screenfile", "Screen config (0=automatic; 1-5=predefined; other=custom)" ], [ "display", "Display screens (scan, default, ...)" ], diff --git a/RX_FSK/data/gpsinit.txt b/RX_FSK/data/gpsinit.txt new file mode 100644 index 0000000..c0167c7 --- /dev/null +++ b/RX_FSK/data/gpsinit.txt @@ -0,0 +1,41 @@ +# gps initialization commmands sent at startup +# '#' lines are ignored (comments) +# bnnnnn sets baud rate to nnnn +# s"blabla" sends text blabla via serial. \xnn is a hex charater with code nn; \\ \" \r \n also supported +# su"blabla" same as s, but prepend ubx sync\append ubx crc at the end +# wnnnnn waits nnnn milliseconds before continung +# wnnnnn,a waits nnnn milliseconds, jumps to label nnn if valid NMEA sentences was received in that time interval +# :x label x +# there should be a wait between sending something and changing baud as the port is asynchronus and there maybe something in the buffer... +# +# no reset requested: jump to label a +na +# wait 1 seconds\if NMEA is received jump to label a +#b9600 +#w1000,a +# factory reset code\as was implemented in firmware before (hard-coded) +# try different baud rates and send command to switch to 9600 baud +b115200 +su"\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x80\x25\x00\x00\x03\x00\x02\x00\x00\x00\x00\x00" +w200 +b38400 +su"\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x80\x25\x00\x00\x03\x00\x02\x00\x00\x00\x00\x00" +w200 +b19200 +su"\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x80\x25\x00\x00\x03\x00\x02\x00\x00\x00\x00\x00" +w200 +b9600 +w800 +# now, send factory reset command and wait 3 seconds +su"\x06\x09\x0d\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff" +w3000 +:a +b9600 +# enable GPGST sentences to get horizontal accuracy (used by app) +su"\x06\x01\x03\x00\xF0\x07\x02" +w200 +# set 38400 baud +su"\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x96\x00\x00\x03\x00\x02\x00\x00\x00\x00\x00" +w200 +b38400 + diff --git a/RX_FSK/src/Display.cpp b/RX_FSK/src/Display.cpp index 4a1b293..d3494b5 100644 --- a/RX_FSK/src/Display.cpp +++ b/RX_FSK/src/Display.cpp @@ -509,9 +509,13 @@ void ILI9225Display::begin() { calc_gfx_offsets(); // On the M5, the display and the Lora chip are on the same SPI interface (VSPI default pins), // we must use the same SPI bus with correct locking +// TODO: Check why there is a hard-coded 38 in here!?!?!? if(sonde.config.type == TYPE_M5_CORE2) { bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, sonde.config.oled_scl, sonde.config.oled_sda, 38, VSPI); + } else if(sonde.config.type == TYPE_M5_CORE2 || sonde.config.type == TYPE_M5_CORE) { + bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, + sonde.config.oled_scl, sonde.config.oled_sda, 19, VSPI); } else { bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, sonde.config.oled_scl, sonde.config.oled_sda, -1, HSPI); @@ -529,7 +533,7 @@ void ILI9225Display::begin() { tft->fillScreen(BLACK); tft->setRotation(sonde.config.tft_orient); tft->setTextWrap(false); - if(sonde.config.type == TYPE_M5_CORE2) + if(sonde.config.type == TYPE_M5_CORE2||sonde.config.type==TYPE_M5_CORE) tft->invertDisplay(true); } diff --git a/RX_FSK/src/Sonde.cpp b/RX_FSK/src/Sonde.cpp index f329250..5a71d5e 100644 --- a/RX_FSK/src/Sonde.cpp +++ b/RX_FSK/src/Sonde.cpp @@ -101,6 +101,7 @@ void Sonde::defaultConfig() { config.led_pout = -1; config.power_pout = -1; config.spectrum=10; + config.b2mute = 360; // Try autodetecting board type config.type = TYPE_TTGO; // Seems like on startup, GPIO4 is 1 on v1 boards, 0 on v2.1 boards? @@ -128,6 +129,7 @@ void Sonde::defaultConfig() { config.tft_rs = -1; config.tft_cs = -1; if(initlevels[16]==0) { + // TODO: Plain M5 Core Gray ends up here (without Lora/GPS board attached) config.oled_sda = 4; config.oled_scl = 15; config.oled_rst = 16; @@ -233,6 +235,39 @@ void Sonde::defaultConfig() { } } } else { + Serial.println("Looks like a TTGO V2.1_1.6, could also be a M5 Core"); + // M5 core has I2C devices 0x16 0x68 0x75 + Wire.begin(21, 22); +#define BMM150 0x10 + Wire.beginTransmission(BMM150); + byte err = Wire.endTransmission(); + if(err) { // try again + delay(400); + Wire.beginTransmission(BMM150); + err = Wire.endTransmission(); + } + if(err==0) { + LOG_I(TAG, "M5stack Core Gray board detected\n"); + config.type = TYPE_M5_CORE; + config.button_pin = 39; + config.button2_pin = 38; + config.button2_axp = 0; + config.disptype = 4; // ILI9342 + config.oled_sda = 23; + config.oled_scl = 18; + config.oled_rst = 33; + config.tft_rs = 27; + config.tft_cs = 14; + config.power_pout = 32+128; + // gpio32 is backlight of tft + config.screenfile = 4; + config.gps_rxd = 16; + config.gps_txd = -1; // 17 + config.sx1278_ss = 5; + config.sx1278_miso = 19; + config.sx1278_mosi = 23; //MOSI; + config.sx1278_sck = 18; // SCK; + } else { // Likely a TTGO V2.1_1.6 config.button_pin = 2 + 128; // GPIO2 / T2 config.button2_pin = 14 + 128; // GPIO14 / T6 @@ -242,6 +277,7 @@ void Sonde::defaultConfig() { config.sd.miso = 2; config.sd.mosi = 15; config.sd.clk = 14; + } } } // diff --git a/RX_FSK/src/Sonde.h b/RX_FSK/src/Sonde.h index a240003..077eb7e 100644 --- a/RX_FSK/src/Sonde.h +++ b/RX_FSK/src/Sonde.h @@ -243,7 +243,7 @@ struct st_sdcard { }; // to be extended -enum { TYPE_TTGO, TYPE_M5_CORE2 }; +enum { TYPE_TTGO, TYPE_M5_CORE2, TYPE_M5_CORE }; typedef struct st_rdzconfig { int type; // autodetected type, TTGO or M5_CORE2 @@ -275,6 +275,7 @@ typedef struct st_rdzconfig { double rxlon; double rxalt; int wifi; // connect to known WLAN 0=skip + uint16_t b2mute; // mute LED/Buzzer for x minutes, 0=0ff int screenfile; int8_t display[30]; // list of display mode (0:scanner, 1:default, 2,... additional modes) int dispsaver; // Turn display on/off (0=always on, 10*n+1: off after n seconds, diff --git a/RX_FSK/src/posinfo.cpp b/RX_FSK/src/posinfo.cpp index 35ddb14..7e9a39c 100644 --- a/RX_FSK/src/posinfo.cpp +++ b/RX_FSK/src/posinfo.cpp @@ -4,6 +4,8 @@ #include #include +#define TAG "GPS" +#include "logger.h" // Sation position obtained from GPS (if available) struct StationPos gpsPos; @@ -45,6 +47,12 @@ void fixedToPosInfo() { static char buffer[85]; static MicroNMEA nmea(buffer, sizeof(buffer)); +static int badNMEA = 0, totalNMEA = 0; + +template +void badChecksumHandler(T nmea) { + badNMEA++; +} /// Arrg. MicroNMEA changes type definition... so lets auto-infer type template @@ -94,7 +102,7 @@ void unkHandler(T nmea) { // 1 deg = aprox. 100 km ==> approx. 200m #define AUTO_CHASE_THRESHOLD 0.002 -#define DEBUG_GPS +//#define DEBUG_GPS static bool gpsCourseOld; static int lastCourse; static char lastnmea[101]; @@ -105,9 +113,15 @@ void gpsTask(void *parameter) { while (Serial2.available()) { if(gotNMEA == 0) gotNMEA = -1; // at least we got *something* char c = Serial2.read(); +#if DEBUG_GPS Serial.print(c); +#endif if (nmea.process(c)) { const char *nmeastring = nmea.getSentence(); + if(nmeastring[0]=='$') { // looks like a nmea string + totalNMEA++; + gotNMEA = 1; + } if(strncmp(nmeastring+3, "GGA", 3)==0 || strncmp(nmeastring+3, "RMC", 3)==0) { strncpy(lastnmea, nmeastring, 100); Serial.printf("GPS: last position nmea: %s\n", lastnmea); @@ -115,7 +129,6 @@ void gpsTask(void *parameter) { else { Serial.printf("GPS: last nmea: %s\n", nmeastring); } - gotNMEA = 1; gpsPos.valid = nmea.isValid(); if (gpsPos.valid) { gpsPos.lon = nmea.getLongitude() * 0.000001; @@ -180,61 +193,118 @@ uint8_t ubx_enable_gpgst[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x01, 3, 0, 0xF0, void dumpGPS() { while (Serial2.available()) { char c = Serial2.read(); - Serial.printf("%02x ", (uint8_t)c); + char d = (c&127)<20 ? '?' : c; + Serial.printf("%02x[%c] ", (uint8_t)c, d); } } +static char h2i(char c) { + if(c>='0'&&c<='9') return c-'0'; + else if (c>='a'&&c<='f') return c-'a'+10; + else if (c>='A'&&c<='F') return c-'A'+10; + else return 0; +} +static uint8_t cs1, cs2; +static void sendChar(char c) { + cs1 += c; + cs2 += cs1; + char d = (c&127)<20 ? '?' : c; + Serial.printf("%02x[%c] ", (uint8_t)c, d); + Serial2.write(c); +} +static void sendString(const char *p) { + bool ubx = false; + if(p[0]=='u') { ubx=true; p++; } + p++; // skip " + if(ubx) { sendChar(UBX_SYNCH_1); sendChar(UBX_SYNCH_2); } + cs1 = cs2 = 0; + while(*p && *p!='"') { + if(*p=='\\') { + p++; + if(*p=='x') { sendChar( (unsigned char)( 16*h2i(p[1]) + h2i(p[2]) ) ); p+=2; } + else if(*p=='n') sendChar('\n'); + else if (*p=='r') sendChar('\r'); + else sendChar(*p); + } + else sendChar(*p); + p++; + } + if(ubx) { + uint8_t _cs1 = cs1, _cs2 = cs2; + sendChar(_cs1); sendChar(_cs2); + } +} + + +static int initGPSfromFile(bool reset) { + File initgps = LittleFS.open("/gpsinit.txt", FILE_READ); + if(!initgps) { LOG_W(TAG, "no gpsinit.txt found, skipping GPS init\n"); return -1; } + + uint16_t lines[100]; // max. 100 lines... (hard coded) + uint8_t labels[10]; // max 10 labels (hard coded) + char jumpto = 0; + while(initgps.available()) { + String l = initgps.readStringUntil('\n'); + const char *p = l.c_str(); + Serial.printf("\nGPSINIT string '%s'\n", p); + if(jumpto > 0) { + Serial.printf("is jumpto? %d, %d vs %d\n", p[0], p[1], jumpto); + if(*p==':' && p[1] == jumpto) jumpto = 0; + continue; + } + switch(p[0]) { + case 'n': // conditional jump if no reset + { + if(!reset) { + jumpto = p[1]; + Serial.printf("Jumping to %c\n", jumpto); + } + break; + } + case 'w': // wait + { + int t = atoi(p+1); + Serial.printf("Waiting %d ms\n", t); + delay(t); + dumpGPS(); + break; + // TODO: JUMP + } + case 'b': // set baud + { + int baud = atoi(p+1); + Serial.printf("Setting baud to %d\n", baud); + Serial2.begin(baud, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); + break; + } + case 's': // send string + { + Serial.printf("Sending string to GPS: "); + sendString(p+1); + break; + }} + } + return 0; +} 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 = LittleFS.open("/GPSRESET", FILE_READ); + bool reset = false; if (testfile && !testfile.isDirectory()) { + reset = true; 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(); LittleFS.remove("/GPSRESET"); } else { Serial.println("GPS reset file: not found/isdir"); - testfile.close(); + if(testfile) testfile.close(); + } + if(initGPSfromFile(reset)<0) { // failed, just set to 9k6 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 { + //TOOD: Also use last baud statement from gps init file... Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); } xTaskCreate( gpsTask, "gpsTask", @@ -312,7 +382,10 @@ String ConnGPS::getStatus() { if(sonde.config.gps_rxd==-1) strlcat(status, "disabled
", 256); else if(gotNMEA==0) strlcat(status, "no data
", 256); else if(gotNMEA<0) strlcat(status, "no NMEA data
", 256); - else strlcat(status, "ok
", 256); + else { + int l = strlen(status); + snprintf(status+l, 256-l, "ok (%d NMEA, %d bad)
", totalNMEA, badNMEA); + } int pos = strlen(status); snprintf(status + pos, 256-pos, "GPS: valid=%d lat=%.6f lon=%.6f alt=%d
", gpsPos.valid, gpsPos.lat, gpsPos.lon, gpsPos.alt); pos = strlen(status); diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 49dcbed..d1d489c 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "dev20240903"; +const char *version_id = "dev20240904"; const int SPIFFS_MAJOR=3; const int SPIFFS_MINOR=3;