From f9a16f827c5d999c8ef72faab63942837f2d0b80 Mon Sep 17 00:00:00 2001 From: "Hans P. Reiser" Date: Sat, 6 Apr 2019 22:22:08 +0200 Subject: [PATCH] v0.1 Some working things... Squashed commit of the following: commit da9d4367be473c0ca1b1a27d5f664314a50d9994 Author: Hans P. Reiser Date: Fri Apr 5 23:32:26 2019 +0200 some minor updates commit e7360f2c1ecdd9fff033c8938f613a52d7e5ef45 Author: Hans P. Reiser Date: Fri Apr 5 20:05:22 2019 +0200 incremental enhancements on train trip commit b3ac2c55064c90adf85d95f45ad2b08dcfe927f4 Author: Hans P. Reiser Date: Fri Apr 5 20:05:18 2019 +0200 incremental enhancements on train trip commit ab4367992efb69aeb645a5c27567522f4c5fb4c6 Author: Hans P. Reiser Date: Thu Apr 4 23:46:48 2019 +0200 some more testing commit d02d687670b5493dc7455b78f3f87c21cd6249ca Author: Hans P. Reiser Date: Thu Apr 4 23:46:37 2019 +0200 some more testing commit 34f9971143445e53919798a52f017dd77eae41cf Author: Hansi Reiser Date: Thu Apr 4 08:11:14 2019 +0200 scanner-test commit a22bb43c892ee1e03f9839190c54db39f9ed187f Author: Hansi Reiser Date: Thu Apr 4 08:10:56 2019 +0200 scanner-test --- README.md | 46 ++- RX_FSK/RX_FSK.ino | 465 +++++++++++++++++++++++------- RX_FSK/data/index.html | 34 +++ RX_FSK/data/networks.txt | 4 + RX_FSK/data/qrg.txt | 8 + RX_FSK/data/style.css | 28 ++ libraries/SX1278FSK/SX1278FSK.cpp | 12 +- libraries/SondeLib/DFM.cpp | 19 +- libraries/SondeLib/RS41.cpp | 33 ++- libraries/SondeLib/Scanner.cpp | 95 ++++++ libraries/SondeLib/Scanner.h | 24 ++ libraries/SondeLib/Sonde.cpp | 158 +++++++++- libraries/SondeLib/Sonde.h | 35 ++- 13 files changed, 817 insertions(+), 144 deletions(-) create mode 100644 RX_FSK/data/index.html create mode 100644 RX_FSK/data/networks.txt create mode 100644 RX_FSK/data/qrg.txt create mode 100644 RX_FSK/data/style.css create mode 100644 libraries/SondeLib/Scanner.cpp create mode 100644 libraries/SondeLib/Scanner.h diff --git a/README.md b/README.md index 274afb7..0b60bc9 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ -# rdz_ttgo_sonde \ No newline at end of file +RDZ_TTGO_SONDE +============== + +This a simple, experimental, not (well) tested, and incomplete decoder for +radiosonde RS41 and DFM06/09 on a TTGO LoRa ESP32 with OLED display board. + +## Button commands +You can use the button on the board (not the reset button, the second one) to +issue some commands. The software distinguishes between several inputs: + +SHORT Short button press (<1.5 seconds) +DOUBLE Short button press, followed by another button press within 0.5 seconds +MID Medium-length button press (2-4 seconds) +LONG Long button press (>5 seconds) + +## Wireless configuration + +On startup, as well as after a LONG button press, the WiFI configuration will +be started. The board will scan available WiFi networks, if the scan results +contains a WiFi network configured with ID and Password in networks.txt, it +will connect to that network in station mode. If no known network is found, or +the connection does not suceed after 5 seconds, it instead starts in access point +mode. In both cases, the ESP32's IP address will be shown in tiny letters in the +bottom line. Then the board will switch to scanning mode. + +## Scanning mode + +In the scanning mode, the board will iterate over all channels configured in +channels.txt, trying to decode a radio sonde on each channel for about 1 second +for RS41, a bit less for DMF06/09. If a valid signal is found, the board switches +to receiving mode on that channel. a SHORT buttong press will also switch to +receiving mode. + +## Receiving mode + +In receiving mode, a single frequency will be decoded, and sonde info (ID, GPS +coordinates, RSSI) will be displayed. The bar above the IP address indicates, +for the last 18 frames, if reception was successfull (|) or failed (.) +A DOUBLE press will switch to scanning mode. +A SHORT press will switch to the next channel in channels.txt + +# Spectrum mode + +A medium press will active scan the whole band (400..406 MHz) and display a +spectrum diagram (each line == 50 kHz) diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 7352a4c..ec03589 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1,14 +1,15 @@ -#include -#include - #include - -#include -#include -#include - +#include +#include +#include #include +#include +#include +#include +//#include +//#include + #define LORA_LED 9 // I2C OLED Display works with SSD1306 driver @@ -22,90 +23,169 @@ U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA //U8G2_SSD1306_128X64_NONAME_F_SW_I2C Display(U8G2_R0, /* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Full framebuffer, SW I2C int e; -char my_packet[100]; -const char* ssid = "DinoGast"; -const char* password = "Schokolade"; +AsyncWebServer server(80); -WiFiServer server(80); - -pthread_t wifithread; +// Set LED GPIO +const int ledPin = 2; +// Stores LED state +String ledState; -int conn = 0; -String currentLine; -WiFiClient client; -unsigned long lastdu; - -void wifiloop(void *arg){ - lastdu=millis(); - while(true) { - if(millis()-lastdu>500) { - // This is too slow to do in main loop - //u8x8.setFont(u8x8_font_chroma48medium8_r); - //u8x8.clearDisplay(); - sonde.updateDisplay(); - lastdu=millis(); +// Replaces placeholder with LED state value +String processor(const String& var){ + Serial.println(var); + if(var == "STATE"){ + if(digitalRead(ledPin)){ + ledState = "ON"; } + else{ + ledState = "OFF"; + } + Serial.print(ledState); + return ledState; + } + return String(); +} +void SetupAsyncServer() { +// Route for root / web page + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", "Hello, world"); + }); + + server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(SPIFFS, "/index.html", String(), false, processor); + }); - delay(1); - if(!conn) { - client = server.available(); // listen for incoming clients - if (client) { // if you get a client, - Serial.println("New Client."); // print a message out the serial port - currentLine = ""; // make a String to hold incoming data from the client - conn = 1; - } - } else { - if(!client.connected()) { // loop while the client's connected - conn = 0; - Serial.println("Client no longer connected"); - continue; - } - while (client.available()) { // if there's bytes to read from the client, - char c = client.read(); // read a byte, then - Serial.write(c); // print it out the serial monitor - if (c == '\n') { // if the byte is a newline character + // Route to load style.css file + server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(SPIFFS, "/style.css", "text/css"); + }); - // if the current line is blank, you got two newline characters in a row. - // that's the end of the client HTTP request, so send a response: - if (currentLine.length() == 0) { - // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) - // and a content-type so the client knows what's coming, then a blank line: - client.println("HTTP/1.1 200 OK"); - client.println("Content-type:text/html"); - client.println(); + // Route to set GPIO to HIGH + server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){ + digitalWrite(ledPin, HIGH); + request->send(SPIFFS, "/index.html", String(), false, processor); + }); + + // Route to set GPIO to LOW + server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){ + digitalWrite(ledPin, LOW); + request->send(SPIFFS, "/index.html", String(), false, processor); + }); - // the content of the HTTP response follows the header: - client.print("Click here to turn the LED on pin 5 on.
"); - client.print("Click here to turn the LED on pin 5 off.
"); + // Start server + server.begin(); +} - // The HTTP response ends with another blank line: - client.println(); - // break out of the while loop: - // close the connection: - client.stop(); - Serial.println("Client Disconnected."); - continue; - } else { // if you got a newline, then clear currentLine: - currentLine = ""; - } - } else if (c != '\r') { // if you got anything else but a carriage return character, - currentLine += c; // add it to the end of the currentLine - } +int nNetworks; +struct { String id; String pw; } networks[20]; - // Check to see if the client request was "GET /H" or "GET /L": - if (currentLine.endsWith("GET /H")) { - digitalWrite(5, HIGH); // GET /H turns the LED on - } - if (currentLine.endsWith("GET /L")) { - digitalWrite(5, LOW); // GET /L turns the LED off - } - - } +void setupWifiList() { + File file = SPIFFS.open("/networks.txt", "r"); + if(!file){ + Serial.println("There was an error opening the file '/networks.txt' for reading"); + return; } + int i=0; + while(file.available()) { + String line = file.readStringUntil('\n'); + if(!file.available()) break; + networks[i].id = line; + networks[i].pw = file.readStringUntil('\n'); + i++; } + nNetworks = i; + Serial.print(i); Serial.println(" networks in networks.txt\n"); + for(int j=0; j1500) { + if(elapsed<4000) { button1.pressed=KP_MID; } + else { button1.pressed=KP_LONG; } + } else { + if(button1.doublepress) button1.pressed=KP_DOUBLE; + else button1.pressed=KP_SHORT; + } + button1.numberKeyPresses += 1; + button1.press_ts = millis(); + } +} + +int getKeyPress() { + KeyPress p = button1.pressed; + button1.pressed = KP_NONE; + return p; +} +int hasKeyPress() { + return button1.pressed; } void setup() @@ -115,26 +195,17 @@ void setup() u8x8.begin(); - pinMode(LORA_LED, OUTPUT); - WiFi.begin(ssid, password); - while(WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - Serial.println(""); - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - - server.begin(); - - xTaskCreatePinnedToCore(wifiloop, "WifiServer", 10240, NULL, 10, NULL, 0); - - - rs41.setup(); + // Initialize SPIFFS + if(!SPIFFS.begin(true)){ + Serial.println("An Error has occurred while mounting SPIFFS"); + return; + } + setupWifiList(); + +#if 0 if(rs41.setFrequency(402700000)==0) { Serial.println(F("Setting freq: SUCCESS ")); } else { @@ -143,8 +214,9 @@ void setup() float f = sx1278.getFrequency(); Serial.print("Frequency set to "); Serial.println(f); +#endif - sx1278.setLNAGain(-48); + sx1278.setLNAGain(0); //-48); int gain = sx1278.getLNAGain(); Serial.print("RX LNA Gain is "); Serial.println(gain); @@ -154,24 +226,203 @@ void setup() Serial.println(); - Serial.println("Setup finished"); - // int returnValue = pthread_create(&wifithread, NULL, wifiloop, (void *)0); + Serial.println("Setup finished"); + // int returnValue = pthread_create(&wifithread, NULL, wifiloop, (void *)0); - // if (returnValue) { - // Serial.println("An error has occurred"); - // } - // xTaskCreate(mainloop, "MainServer", 10240, NULL, 10, NULL); + // if (returnValue) { + // Serial.println("An error has occurred"); + // } + // xTaskCreate(mainloop, "MainServer", 10240, NULL, 10, NULL); + // Handle button press + attachInterrupt(0, buttonISR, CHANGE); + + setupChannelList(); + #if 0 + sonde.clearSonde(); + sonde.addSonde(402.300, STYPE_RS41); + sonde.addSonde(402.700, STYPE_RS41); + sonde.addSonde(403.450, STYPE_DFM09); + #endif + /// not here, done by sonde.setup(): rs41.setup(); + sonde.setup(); } +enum MainState { ST_DECODER, ST_SCANNER, ST_SPECTRUM, ST_WIFISCAN }; + +static MainState mainState = ST_DECODER; + +void enterMode(int mode) { + mainState = (MainState)mode; + sonde.clearDisplay(); +} + +void loopDecoder() { + switch(getKeyPress()) { + case KP_SHORT: + sonde.nextConfig(); + break; + case KP_DOUBLE: + enterMode(ST_SCANNER); + return; + case KP_MID: + enterMode(ST_SPECTRUM); + return; + case KP_LONG: + enterMode(ST_WIFISCAN); + return; + } + // sonde knows the current type and frequency, and delegates to the right decoder + sonde.receiveFrame(); + sonde.updateDisplay(); +} + +#define SCAN_MAXTRIES 1 +void loopScanner() { + sonde.updateDisplayScanner(); + static int tries=0; + switch(getKeyPress()) { + case KP_SHORT: + enterMode(ST_DECODER); + return; + case KP_DOUBLE: break; /* ignored */ + case KP_MID: + enterMode(ST_SPECTRUM); + return; + case KP_LONG: + enterMode(ST_WIFISCAN); + return; + } + // receiveFrame returns 0 on success, 1 on timeout + int res = sonde.receiveFrame(); // Maybe instead of receiveFrame, just detect if right type is present? TODO + Serial.print("Scanner: receiveFrame returned"); + Serial.println(res); + if(res==0) { + enterMode(ST_DECODER); + return; + } + if(++tries>=SCAN_MAXTRIES && !hasKeyPress()) { + sonde.nextConfig(); + tries = 0; + } +} + +void loopSpectrum() { + switch(getKeyPress()) { + case KP_SHORT: /* move selection of peak, TODO */ + sonde.nextConfig(); // TODO: Should be set specific frequency + enterMode(ST_DECODER); + return; + case KP_MID: /* restart, TODO */ break; + case KP_LONG: + enterMode(ST_WIFISCAN); + return; + case KP_DOUBLE: /* ignore */ break; + default: break; + } + scanner.scan(); + scanner.plotResult(); +} + +String translateEncryptionType(wifi_auth_mode_t encryptionType) { + switch (encryptionType) { + case (WIFI_AUTH_OPEN): + return "Open"; + case (WIFI_AUTH_WEP): + return "WEP"; + case (WIFI_AUTH_WPA_PSK): + return "WPA_PSK"; + case (WIFI_AUTH_WPA2_PSK): + return "WPA2_PSK"; + case (WIFI_AUTH_WPA_WPA2_PSK): + return "WPA_WPA2_PSK"; + case (WIFI_AUTH_WPA2_ENTERPRISE): + return "WPA2_ENTERPRISE"; + } +} + +static char* _scan[2]={"/","\\"}; +void loopWifiScan() { + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0,0,"WiFi Scan..."); + int line=0; + int cnt=0; + + WiFi.mode(WIFI_STA); + const char *id, *pw; + int n = WiFi.scanNetworks(); + for (int i = 0; i < n; i++) { + Serial.print("Network name: "); + Serial.println(WiFi.SSID(i)); + u8x8.drawString(0,1+line,WiFi.SSID(i).c_str()); + line = (line+1)%5; + Serial.print("Signal strength: "); + Serial.println(WiFi.RSSI(i)); + Serial.print("MAC address: "); + Serial.println(WiFi.BSSIDstr(i)); + Serial.print("Encryption type: "); + String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i)); + Serial.println(encryptionTypeDescription); + Serial.println("-----------------------"); + id=WiFi.SSID(i).c_str(); + pw=fetchWifiPw(id); + if(pw) break; + } + if(1||!pw) { id="test"; pw="test"; } + Serial.print("Connecting to: "); Serial.println(id); + u8x8.drawString(0,6, "Conn:"); + u8x8.drawString(6,6, id); + WiFi.begin(id, pw); + while(WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + u8x8.drawString(15,7,_scan[cnt&1]); + cnt++; + if(cnt==4) { + WiFi.disconnect(); // retry, for my buggy FritzBox + WiFi.begin(id, pw); + } + if(cnt==10) { + WiFi.disconnect(); + delay(1000); + WiFi.softAP("sonde","sondesonde"); + IPAddress myIP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(myIP); + sonde.setIP(myIP.toString().c_str()); + sonde.updateDisplayIP(); + SetupAsyncServer(); + delay(5000); + enterMode(ST_DECODER); + return; + } + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + sonde.setIP(WiFi.localIP().toString().c_str()); + sonde.updateDisplayIP(); + SetupAsyncServer(); + delay(5000); + enterMode(ST_DECODER); +} + + void loop() { Serial.println("Running main loop"); - - + switch(mainState) { + case ST_DECODER: loopDecoder(); break; + case ST_SCANNER: loopScanner(); break; + case ST_SPECTRUM: loopSpectrum(); break; + case ST_WIFISCAN: loopWifiScan(); break; + } +#if 0 //wifiloop(NULL); //e = dfm.receiveFrame(); - e = rs41.receiveFrame(); - #if 0 + //e = rs41.receiveFrame(); + delay(1000); int rssi = sx1278.getRSSI(); Serial.print(" RSSI: "); Serial.print(rssi); diff --git a/RX_FSK/data/index.html b/RX_FSK/data/index.html new file mode 100644 index 0000000..2f4b4cb --- /dev/null +++ b/RX_FSK/data/index.html @@ -0,0 +1,34 @@ + + + + ESP32 Web Server + + + + + +

ESP32 Web Server

+ + + + + + + + + + + + + + + + + + + + diff --git a/RX_FSK/data/networks.txt b/RX_FSK/data/networks.txt new file mode 100644 index 0000000..a0b956a --- /dev/null +++ b/RX_FSK/data/networks.txt @@ -0,0 +1,4 @@ +DinoGast +Schokolade +AndroidDD +dl9rdzhr diff --git a/RX_FSK/data/qrg.txt b/RX_FSK/data/qrg.txt new file mode 100644 index 0000000..6bb6ae2 --- /dev/null +++ b/RX_FSK/data/qrg.txt @@ -0,0 +1,8 @@ +# Frequency in Mhz (format nnn.nnn) +# Type (4=RS41, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09) +# +402.700 4 +402.300 4 +403.450 9 +405.100 4 +# end diff --git a/RX_FSK/data/style.css b/RX_FSK/data/style.css new file mode 100644 index 0000000..50f94f8 --- /dev/null +++ b/RX_FSK/data/style.css @@ -0,0 +1,28 @@ +html { + font-family: Helvetica; + display: inline-block; + margin: 0px auto; + text-align: center; +} +h1{ + color: #0F3376; + padding: 2vh; +} +p{ + font-size: 1.5rem; +} +.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; +} diff --git a/libraries/SX1278FSK/SX1278FSK.cpp b/libraries/SX1278FSK/SX1278FSK.cpp index 5d113c0..058d8fc 100644 --- a/libraries/SX1278FSK/SX1278FSK.cpp +++ b/libraries/SX1278FSK/SX1278FSK.cpp @@ -11,6 +11,7 @@ #include "SX1278FSK.h" #include "SPI.h" +#include SX1278FSK::SX1278FSK() { @@ -98,7 +99,7 @@ byte SX1278FSK::readRegister(byte address) digitalWrite(SX1278_SS,LOW); - delay(1); + //delay(1); bitClear(address, 7); // Bit 7 cleared to write in registers SPI.transfer(address); value = SPI.transfer(0x00); @@ -129,7 +130,7 @@ void SX1278FSK::writeRegister(byte address, byte data) { digitalWrite(SX1278_SS,LOW); - delay(1); + //delay(1); bitSet(address, 7); // Bit 7 set to read from registers SPI.transfer(address); SPI.transfer(data); @@ -648,6 +649,9 @@ uint8_t SX1278FSK::receive() return state; } +// ugly. shouldn't be here in a nice software design +extern int hasKeyPress(); + /* Function: Configures the module to receive a packet Returns: Integer that determines if there has been any error @@ -682,7 +686,7 @@ uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data) value = readRegister(REG_IRQ_FLAGS2); byte ready=0; // while not yet done or FIFO not yet empty - while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) ) + while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) &&(!hasKeyPress()) ) { if( bitRead(value,2)==1 ) ready=1; if( bitRead(value, 6) == 0 ) { // FIFO not empty @@ -690,6 +694,7 @@ uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data) if(di==1) { int rssi=getRSSI(); Serial.print("Test: RSSI="); Serial.println(rssi); + sonde.si()->rssi = rssi; } if(di>520) { // TODO @@ -705,6 +710,7 @@ uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data) Serial.println(F("** The timeout has expired **")); Serial.println(); #endif + sonde.si()->rssi = getRSSI(); writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Setting standby FSK mode return 1; // TIMEOUT } diff --git a/libraries/SondeLib/DFM.cpp b/libraries/SondeLib/DFM.cpp index 1396fc9..1d8828f 100644 --- a/libraries/SondeLib/DFM.cpp +++ b/libraries/SondeLib/DFM.cpp @@ -2,6 +2,7 @@ /* DFM decoder functions */ #include "DFM.h" #include "SX1278FSK.h" +#include "Sonde.h" #define DFM_DEBUG 1 @@ -71,6 +72,8 @@ int DFM::setup(int inverse) } int DFM::setFrequency(float frequency) { + Serial.print("DFM: setting RX frequency to "); + Serial.println(frequency); return sx1278.setFrequency(frequency); } @@ -165,6 +168,8 @@ int DFM::decodeCFG(uint8_t *cfg) if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f); Serial.print("DFM-06 ID: "); Serial.print(lowid, HEX); + snprintf(sonde.si()->id, 10, "%x", lowid); + sonde.si()->validID = true; } if((cfg[0]>>4)==0x0A) { // DMF-9 ID type=9; @@ -178,6 +183,8 @@ int DFM::decodeCFG(uint8_t *cfg) if(idgood==3) { uint32_t dfmid = (highid<<16) | lowid; Serial.print("DFM-09 ID: "); Serial.print(dfmid); + snprintf(sonde.si()->id, 10, "%d", dfmid); + sonde.si()->validID = true; } } } @@ -202,6 +209,9 @@ int DFM::decodeDAT(uint8_t *dat) vh = (dat[4]<<8) + dat[5]; Serial.print("GPS-lat: "); Serial.print(lat*0.0000001); Serial.print(", hor-V: "); Serial.print(vh*0.01); + sonde.si()->lat = lat*0.0000001; + sonde.si()->hs = vh*0.01; + sonde.si()->validPos |= 0x11; } break; case 3: @@ -211,6 +221,9 @@ int DFM::decodeDAT(uint8_t *dat) dir = ((uint16_t)dat[4]<<8) + dat[5]; Serial.print("GPS-lon: "); Serial.print(lon*0.0000001); Serial.print(", dir: "); Serial.print(dir*0.01); + sonde.si()->lon = lon*0.0000001; + sonde.si()->dir = dir*0.01; + sonde.si()->validPos |= 0x42; } break; case 4: @@ -220,6 +233,9 @@ int DFM::decodeDAT(uint8_t *dat) vv = (int16_t)( (dat[4]<<8) | dat[5] ); Serial.print("GPS-height: "); Serial.print(hei*0.01); Serial.print(", vv: "); Serial.print(vv*0.01); + sonde.si()->hei = hei*0.01; + sonde.si()->vs = vv*0.01; + sonde.si()->validPos |= 0x0C; } break; case 8: @@ -256,7 +272,7 @@ int DFM::receiveFrame() { sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); int e = sx1278.receivePacketTimeout(1000, data); - if(e) { return 1; } //if timeout... return 1 + if(e) { return RX_TIMEOUT; } //if timeout... return 1 deinterleave(data, 7, hamming_conf); deinterleave(data+7, 13, hamming_dat1); @@ -277,6 +293,7 @@ int DFM::receiveFrame() { decodeCFG(byte_conf); decodeDAT(byte_dat1); decodeDAT(byte_dat2); + return RX_OK; } DFM dfm = DFM(); diff --git a/libraries/SondeLib/RS41.cpp b/libraries/SondeLib/RS41.cpp index 082e1d8..4feaa55 100644 --- a/libraries/SondeLib/RS41.cpp +++ b/libraries/SondeLib/RS41.cpp @@ -133,6 +133,8 @@ int RS41::setup() } int RS41::setFrequency(float frequency) { + Serial.print("RS41: setting RX frequency to "); + Serial.println(frequency); return sx1278.setFrequency(frequency); } @@ -311,11 +313,11 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) z = (double)getint32(b, b_len, p+8UL)*0.01; wgs84r(x, y, z, &lat, &long0, &heig); Serial.print(" "); - si.lat = (float)(X2C_DIVL(lat,1.7453292519943E-2)); - Serial.print(si.lat); + sonde.si()->lat = (float)(X2C_DIVL(lat,1.7453292519943E-2)); + Serial.print(sonde.si()->lat); Serial.print(" "); - si.lon = (float)(X2C_DIVL(long0,1.7453292519943E-2)); - Serial.print(si.lon); + sonde.si()->lon = (float)(X2C_DIVL(long0,1.7453292519943E-2)); + Serial.print(sonde.si()->lon); if (heig<1.E+5 && heig>(-1.E+5)) { Serial.print(" "); Serial.print((uint32_t)heig); @@ -338,18 +340,18 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) dir = X2C_DIVL(atang2(vn, ve),1.7453292519943E-2); if (dir<0.0) dir = 360.0+dir; Serial.print(" "); - si.hs = sqrt((float)(vn*vn+ve*ve))*3.6f; - Serial.print(si.hs); + sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f; + Serial.print(sonde.si()->hs); Serial.print("km/h "); Serial.print(dir); Serial.print("deg "); Serial.print((float)vu); - si.vs = vu; + sonde.si()->vs = vu; Serial.print("m/s "); Serial.print(getcard16(b, b_len, p+18UL)&255UL); Serial.print("Sats"); - si.hei = heig; - si.validPos = true; + sonde.si()->hei = heig; + sonde.si()->validPos = true; } /* end posrs41() */ @@ -400,10 +402,10 @@ void RS41::decode41(byte *data, int MAXLEN) Serial.print("; RS41 ID "); snprintf(buf, 10, "%.8s ", data+p+2); Serial.print(buf); - strcpy(si.type, "RS41"); - strncpy(si.id, (const char *)(data+p+2), 8); - si.id[8]=0; - si.validID=true; + sonde.si()->type=STYPE_RS41; + strncpy(sonde.si()->id, (const char *)(data+p+2), 8); + sonde.si()->id[8]=0; + sonde.si()->validID=true; } // TODO: some more data break; @@ -463,14 +465,15 @@ int RS41::receiveFrame() { sx1278.setPayloadLength(MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header) sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); - int e = sx1278.receivePacketTimeout(3000, data+8); - if(e) { Serial.println("TIMEOUT"); return 1; } //if timeout... return 1 + int e = sx1278.receivePacketTimeout(1000, data+8); + if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1 for(int i=0; i +#include + +extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8; + +#define CHANBW 25 +#define SMOOTH 2 +#define STARTF 400000000 +#define NCHAN ((int)(6000/CHANBW)) + +int scanresult[NCHAN]; +int scandisp[NCHAN/2]; + +#define PLOT_N 120 +#define PLOT_MIN -220 +#define PLOT_SCALE(x) (x=8) { row[8*y]=255; continue; } + row[8*y] = tilepatterns[nbits]; + } +} +/* + * There are 16*8 columns to plot, NPLOT must be lower than that + * currently, we use 120 * 50kHz channels + * There are 8*8 values to plot; MIN is bottom end, + */ +uint8_t tiles[16] = { 0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0, 1, 3, 7, 15, 31, 63, 127, 255}; +void Scanner::plotResult() +{ + uint8_t row[8*8]; + for(int i=0; i>16)!=(frf>>16) ) { + sx1278.writeRegister(REG_FRF_MSB, (frf&0xff0000)>>16); + } + if( ((lastfrf&0x00ff00)>>8) != ((frf&0x00ff00)>>8) ) { + sx1278.writeRegister(REG_FRF_MID, (frf&0x00ff00)>>8); + } + sx1278.writeRegister(REG_FRF_LSB, (frf&0x0000ff)); + lastfrf = frf; + // Wait TS_HOP (20us) + TS_RSSI ( 2^(SMOOTH+1) / 4 / CHANBW us) + int wait = 20 + 1000*(1<<(SMOOTH+1))/4/CHANBW; + delayMicroseconds(wait); + int rssi = -(int)sx1278.readRegister(REG_RSSI_VALUE_FSK); + scanresult[i] = rssi; + } + unsigned long duration = millis()-start; + Serial.print("Scan time: "); + Serial.println(duration); + for(int i=0; iscandisp[i/2]) scandisp[i/2] = scanresult[i+j]; } + Serial.print(scanresult[i]); Serial.print(", "); + if(((i+1)%32) == 0) Serial.println(); + } + Serial.println("\n"); +} + +Scanner scanner = Scanner(); diff --git a/libraries/SondeLib/Scanner.h b/libraries/SondeLib/Scanner.h new file mode 100644 index 0000000..b8fcd3f --- /dev/null +++ b/libraries/SondeLib/Scanner.h @@ -0,0 +1,24 @@ + + +#ifndef _SCANNER_H +#define _SCANNER_H + +#include +#include +#include +#ifndef inttypes_h + #include +#endif + +class Scanner +{ +private: + void fillTiles(uint8_t *row, int value); + +public: + void plotResult(); + void scan(void); +}; + +extern Scanner scanner; +#endif diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp index 67612ba..86ff2a4 100644 --- a/libraries/SondeLib/Sonde.cpp +++ b/libraries/SondeLib/Sonde.cpp @@ -2,10 +2,13 @@ #include #include "Sonde.h" +#include "RS41.h" +#include "DFM.h" extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8; -SondeInfo si = { "RS41", 403.450, "P1234567", true, 48.1234, 14.9876, 543, 3.97, -0.5, true, 120 }; +//SondeInfo si = { STYPE_RS41, 403.450, "P1234567", true, 48.1234, 14.9876, 543, 3.97, -0.5, true, 120 }; +const char *sondeTypeStr[5] = { "DFM6", "DFM9", "RS41" }; static unsigned char kmh_tiles[] U8X8_PROGMEM = { 0x1F, 0x04, 0x0A, 0x11, 0x00, 0x1F, 0x02, 0x04, 0x42, 0x3F, 0x10, 0x08, 0xFC, 0x22, 0x20, 0xF8 @@ -13,14 +16,108 @@ static unsigned char kmh_tiles[] U8X8_PROGMEM = { static unsigned char ms_tiles[] U8X8_PROGMEM = { 0x1F, 0x02, 0x04, 0x02, 0x1F, 0x40, 0x20, 0x10, 0x08, 0x04, 0x12, 0xA4, 0xA4, 0xA4, 0x40, 0x00 }; +static unsigned char stattiles[4][4] = { + 0x00, 0x00, 0x00, 0x00 , + 0x00, 0x10, 0x10, 0x00 , + 0x1F, 0x15, 0x15, 0x00 , + 0x00, 0x1F, 0x00, 0x00 }; + +byte myIP_tiles[8*10]; + +static const uint8_t font[10][5]={ + 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 + 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x01, 0x01, 0x79, 0x05, 0x03, // 7 + 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x06, 0x49, 0x39, 0x29, 0x1E }; // 9; .=0x40 + +static uint8_t halfdb_tile[8]={0x80, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00}; +static uint8_t empty_tile[8]={0x80, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00}; + + +void Sonde::setIP(const char *ip) { + int tp = 0; + int len = strlen(ip); + for(int i=0; i=MAXSONDE) { + Serial.println("Cannot add another sonde, MAXSONDE reached"); + return; + } + sondeList[nSonde].type = type; + sondeList[nSonde].freq = frequency; + memcpy(sondeList[nSonde].rxStat, "\x00\x01\x2\x3\x2\x1\x1\x2\x0\x3\x0\x0\x1\x2\x3\x1\x0", 18); + nSonde++; +} +void Sonde::nextConfig() { + currentSonde++; + if(currentSonde>=nSonde) { + currentSonde=0; + } + setup(); +} +SondeInfo *Sonde::si() { + return &sondeList[currentSonde]; +} + +void Sonde::setup() { + // Test only: setIP("123.456.789.012"); + // update receiver config: TODO + Serial.print("Setting up receiver on channel "); + Serial.println(currentSonde); + switch(sondeList[currentSonde].type) { + case STYPE_RS41: + rs41.setup(); + rs41.setFrequency(sondeList[currentSonde].freq * 1000000); + break; + case STYPE_DFM06: + case STYPE_DFM09: + dfm.setup( sondeList[currentSonde].type==STYPE_DFM06?0:1 ); + dfm.setFrequency(sondeList[currentSonde].freq * 1000000); + break; + } + // Update display + //updateDisplayRXConfig(); + //updateDisplay(); +} +int Sonde::receiveFrame() { + int ret; + if(sondeList[currentSonde].type == STYPE_RS41) { + ret = rs41.receiveFrame(); + } else { + ret = dfm.receiveFrame(); + } + memmove(sonde.si()->rxStat+1, sonde.si()->rxStat, 17); + sonde.si()->rxStat[0] = ret==0 ? 3 : 1; // OK or Timeout; TODO: add error (2) + return ret; // 0: OK, 1: Timeuot, 2: Other error (TODO) +} void Sonde::updateDisplayPos() { char buf[16]; u8x8.setFont(u8x8_font_7x14_1x2_r); - if(si.validPos) { - snprintf(buf, 16, "%2.5f", si.lat); + if(si()->validPos) { + snprintf(buf, 16, "%2.5f", si()->lat); u8x8.drawString(0,2,buf); - snprintf(buf, 16, "%2.5f", si.lon); + snprintf(buf, 16, "%2.5f", si()->lon); u8x8.drawString(0,4,buf); } else { u8x8.drawString(0,2," "); @@ -31,17 +128,17 @@ void Sonde::updateDisplayPos() { void Sonde::updateDisplayPos2() { char buf[16]; u8x8.setFont(u8x8_font_chroma48medium8_r); - if(!si.validPos) { + if(!si()->validPos) { u8x8.drawString(10,2," "); u8x8.drawString(10,3," "); u8x8.drawString(10,4," "); return; } - snprintf(buf, 16, si.hei>999?"%5.0fm":"%3.1fm", si.hei); + snprintf(buf, 16, si()->hei>999?"%5.0fm":"%3.1fm", si()->hei); u8x8.drawString((10+6-strlen(buf)),2,buf); - snprintf(buf, 16, si.hs>99?"%3.0f":"%2.1f", si.hs); + snprintf(buf, 16, si()->hs>99?"%3.0f":"%2.1f", si()->hs); u8x8.drawString((10+4-strlen(buf)),3,buf); - snprintf(buf, 16, "%+2.1f", si.vs); + snprintf(buf, 16, "%+2.1f", si()->vs); u8x8.drawString((10+4-strlen(buf)),4,buf); u8x8.drawTile(14,3,2,kmh_tiles); u8x8.drawTile(14,4,2,ms_tiles); @@ -49,8 +146,8 @@ void Sonde::updateDisplayPos2() { void Sonde::updateDisplayID() { u8x8.setFont(u8x8_font_chroma48medium8_r); - if(si.validID) { - u8x8.drawString(0,1, si.id); + if(si()->validID) { + u8x8.drawString(0,1, si()->id); } else { u8x8.drawString(0,1, "nnnnnnnn "); } @@ -59,19 +156,48 @@ void Sonde::updateDisplayID() { void Sonde::updateDisplayRSSI() { char buf[16]; u8x8.setFont(u8x8_font_7x14_1x2_r); - snprintf(buf, 16, "%ddB ", si.rssi); + snprintf(buf, 16, "-%d ", sonde.si()->rssi/2); + int len=strlen(buf)-3; + buf[5]=0; u8x8.drawString(0,6,buf); + u8x8.drawTile(len,6,1,(sonde.si()->rssi&1)?halfdb_tile:empty_tile); +} + +void Sonde::updateStat() { + uint8_t *stat = si()->rxStat; + for(int i=0; i<18; i+=2) { + uint8_t tile[8]; + *(uint32_t *)(&tile[0]) = *(uint32_t *)(&(stattiles[stat[i]])); + *(uint32_t *)(&tile[4]) = *(uint32_t *)(&(stattiles[stat[i+1]])); + u8x8.drawTile(7+i/2, 6, 1, tile); + } } void Sonde::updateDisplayRXConfig() { char buf[16]; u8x8.setFont(u8x8_font_chroma48medium8_r); - u8x8.drawString(0,0, si.type); - snprintf(buf, 16, "%3.3f MHz", si.freq); + u8x8.drawString(0,0, sondeTypeStr[si()->type]); + snprintf(buf, 16, "%3.3f MHz", si()->freq); u8x8.drawString(5,0, buf); } +void Sonde::updateDisplayIP() { + u8x8.drawTile(6, 7, 10, myIP_tiles); +} + +// Probing RS41 +// 40x.xxx MHz +void Sonde::updateDisplayScanner() { + char buf[16]; + u8x8.setFont(u8x8_font_7x14_1x2_r); + u8x8.drawString(0, 0, "Probing"); + u8x8.drawString(8, 0, sondeTypeStr[si()->type]); + snprintf(buf, 16, "%3.3f MHz", si()->freq); + u8x8.drawString(0,3, buf); + updateDisplayIP(); +} + void Sonde::updateDisplay() { char buf[16]; @@ -80,6 +206,12 @@ void Sonde::updateDisplay() updateDisplayPos(); updateDisplayPos2(); updateDisplayRSSI(); + updateStat(); + updateDisplayIP(); +} + +void Sonde::clearDisplay() { + u8x8.clearDisplay(); } Sonde sonde = Sonde(); diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h index 18a9510..27936cd 100644 --- a/libraries/SondeLib/Sonde.h +++ b/libraries/SondeLib/Sonde.h @@ -1,11 +1,18 @@ - #ifndef Sonde_h #define Sonde_H +// RX_TIMEOUT: no header detected +// RX_ERROR: header detected, but data not decoded (crc error, etc.) +// RX_OK: header and data ok +enum RxResult { RX_OK, RX_TIMEOUT, RX_ERROR }; + +enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41 }; +extern const char *sondeTypeStr[5]; + typedef struct st_sondeinfo { // receiver configuration - char type[5]; + SondeType type; float freq; // decoded ID char id[10]; @@ -16,23 +23,43 @@ typedef struct st_sondeinfo { float hei; float vs; float hs; - bool validPos; + float dir; // 0..360 + uint8_t validPos; // bit pattern for validity of above 6 fields // RSSI from receiver int rssi; + uint8_t rxStat[20]; } SondeInfo; +// rxState: 0=undef[empty] 1=timeout[.] 2=errro[E] 3=ok[1] -extern SondeInfo si; + +#define MAXSONDE 10 class Sonde { private: + int nSonde; + int currentSonde = 0; + SondeInfo sondeList[MAXSONDE+1]; public: + void clearSonde(); + void addSonde(float frequency, SondeType type); + void nextConfig(); + void setup(); + + int receiveFrame(); + SondeInfo *si(); + void updateDisplayPos(); void updateDisplayPos2(); void updateDisplayID(); void updateDisplayRSSI(); void updateDisplayRXConfig(); + void updateStat(); + void updateDisplayIP(); void updateDisplay(); + void updateDisplayScanner(); + void clearDisplay(); + void setIP(const char *ip); }; extern Sonde sonde;
IDPW
1DinoGastSchokolade
2