From f9a16f827c5d999c8ef72faab63942837f2d0b80 Mon Sep 17 00:00:00 2001 From: "Hans P. Reiser" Date: Sat, 6 Apr 2019 22:22:08 +0200 Subject: [PATCH 1/2] 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; From 69cb203a4f0425af82958d6bf097ce45d0f5efe0 Mon Sep 17 00:00:00 2001 From: "Hans P. Reiser" Date: Fri, 12 Apr 2019 18:53:29 +0200 Subject: [PATCH 2/2] merging devel changes 0d64f8d..9b0785b --- README.md | 17 ++ RX_FSK/RX_FSK.ino | 364 +++++++++++++++++++++++++++------ RX_FSK/data/index.html | 58 ++++-- RX_FSK/data/networks.txt | 2 + RX_FSK/data/qrg.txt | 8 +- RX_FSK/data/style.css | 47 +++++ Setup.md | 66 ++++++ libraries/SondeLib/Scanner.cpp | 34 ++- libraries/SondeLib/Sonde.cpp | 32 ++- libraries/SondeLib/Sonde.h | 12 +- libraries/SondeLib/aprs.cpp | 286 ++++++++++++++++++++++++++ libraries/SondeLib/aprs.h | 21 ++ 12 files changed, 849 insertions(+), 98 deletions(-) create mode 100644 Setup.md create mode 100644 libraries/SondeLib/aprs.cpp create mode 100644 libraries/SondeLib/aprs.h diff --git a/README.md b/README.md index 0b60bc9..562f1bc 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,20 @@ A SHORT press will switch to the next channel in channels.txt A medium press will active scan the whole band (400..406 MHz) and display a spectrum diagram (each line == 50 kHz) + +## Setup + +Download https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip +and move to your Arduino IDE's libraries directory +Rename to (name without "-master") + +Download https://github.com/me-no-dev/AsyncTCP/archive/master.zip +and move to your Arduino IDE's libraries directory +Rename to (name without "-master") + +Install Arduino ESP32 file system uploader +https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/ +Download https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip +Move to your Arduino IDE's tools directory + + diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index ec03589..f49d51c 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include +#include //#include //#include @@ -26,6 +28,13 @@ int e; AsyncWebServer server(80); +const char * udpAddress = "192.168.179.21"; +const int udpPort = 9002; + +boolean connected = false; +WiFiUDP udp; + + // Set LED GPIO const int ledPin = 2; // Stores LED state @@ -48,58 +57,29 @@ String processor(const String& var){ 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); - }); - - // Route to load style.css file - server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(SPIFFS, "/style.css", "text/css"); - }); +#define MAX_QRG 10 - // 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); - }); - - // Start server - server.begin(); +const String sondeTypeSelect(int activeType) { + String sts = ""; + for(int i=0; i<3; i++) { + sts += "
IDPW
1DinoGastSchokolade
2
"); + for(int i=0; i<10; i++) { + String s = sondeTypeSelect(i>=sonde.nSonde?2:sonde.sondeList[i].type); + sprintf(ptr+strlen(ptr), "" + "" + "", + i+1, + i+1, (i=sonde.nSonde?400.000:sonde.sondeList[i].freq, + i+1, s.c_str()); + } + strcat(ptr,"
IDActiveFreqMode
%d
"); + return message; +} + +const char *handleQRGPost(AsyncWebServerRequest *request) { + char label[10]; + // parameters: a_i, f_1, t_i (active/frequency/type) +#if 1 + File f = SPIFFS.open("/qrg.txt", "w"); + if(!f) { + Serial.println("Error while opening '/qrg.txt' for writing"); + return "Error while opening '/qrg.txt' for writing"; + } +#endif + Serial.println("Handling post request"); +#if 0 + int params = request->params(); + for(int i=0; igetParam(i)->name().c_str()); + } +#endif + for(int i=1; i<=MAX_QRG; i++) { + snprintf(label, 10, "A%d", i); + AsyncWebParameter *active = request->getParam(label, true); + snprintf(label, 10, "F%d", i); + AsyncWebParameter *freq = request->getParam(label, true); + if(!freq) continue; + snprintf(label, 10, "T%d", i); + AsyncWebParameter *type = request->getParam(label, true); + if(!type) continue; + const char *fstr = freq->value().c_str(); + const char *tstr = type->value().c_str(); + Serial.printf("Processing a=%s, f=%s, t=%s\n", active?"YES":"NO", fstr, tstr); + char typech = (tstr[2]=='4'?'4':tstr[3]); // Ugly TODO + f.printf("%3.3f %c %c\n", atof(fstr), typech, active?'+':'-'); + } + f.close(); + setupChannelList(); +} + + +/////////////////// Functions for reading/writing Wifi networks from networks.txt + +#define MAX_WIFI 10 +int nNetworks; +struct { String id; String pw; } networks[MAX_WIFI]; + +// FIXME: For now, we don't uspport wifi networks that contain newline or null characters +// ... would require a more sophisicated file format (currently one line SSID; one line Password +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; j
"); + for(int i=0; i" + "", + i==0?"AP":tmp, + i+1, i"); + return message; +} + +const char *handleWIFIPost(AsyncWebServerRequest *request) { + char label[10]; + // parameters: a_i, f_1, t_i (active/frequency/type) +#if 1 + File f = SPIFFS.open("/networks.txt", "w"); + if(!f) { + Serial.println("Error while opening '/networks.txt' for writing"); + return "Error while opening '/networks.txt' for writing"; + } +#endif + Serial.println("Handling post request"); +#if 0 + int params = request->params(); + for(int i=0; igetParam(i)->name().c_str()); + } +#endif + for(int i=1; i<=MAX_WIFI; i++) { + snprintf(label, 10, "S%d", i); + AsyncWebParameter *ssid = request->getParam(label, true); + if(!ssid) continue; + snprintf(label, 10, "P%d", i); + AsyncWebParameter *pw = request->getParam(label, true); + if(!pw) continue; + const char *sstr = ssid->value().c_str(); + const char *pstr = pw->value().c_str(); + if(strlen(sstr)==0) continue; + Serial.printf("Processing S=%s, P=%s\n", sstr, pstr); + f.printf("%s\n%s\n", sstr, pstr); + } + f.close(); + setupWifiList(); +} + +// Show current status +void addSondeStatus(char *ptr, int i) +{ + SondeInfo *s = &sonde.sondeList[i]; + strcat(ptr, "
NrSSIDPassword
%s
"); + sprintf(ptr+strlen(ptr),"\n", + s->freq, sondeTypeStr[s->type], + s->validID?s->id:"", + s->lat, s->lon, s->hei); + sprintf(ptr+strlen(ptr), "", s->lat, s->lon); + strcat(ptr, "
%3.3f MHz, Type: %s
ID: %s
QTH: %.6f,%.6f h=%.0fm
Geo-Ref -", s->lat, s->lon); + sprintf(ptr+strlen(ptr), "Google map - ", s->lat, s->lon); + sprintf(ptr+strlen(ptr), "OSM

\n"); +} + +const char *createStatusForm() { + char *ptr = message; + strcpy(ptr,""); + + for(int i=0; i"); + return message; +} + +const char* PARAM_MESSAGE = "message"; +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); + }); + + server.on("/test.html", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(SPIFFS, "/test.html", String(), false, processor); + }); + + server.on("/qrg.html", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/html", createQRGForm()); + }); + server.on("/qrg.html", HTTP_POST, [](AsyncWebServerRequest *request){ + handleQRGPost(request); + request->send(200, "text/html", createQRGForm()); + }); + + server.on("/wifi.html", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/html", createWIFIForm()); + }); + server.on("/wifi.html", HTTP_POST, [](AsyncWebServerRequest *request){ + handleWIFIPost(request); + request->send(200, "text/html", createWIFIForm()); + }); + + server.on("/status.html", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/html", createStatusForm()); + }); + + // Route to load style.css file + server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(SPIFFS, "/style.css", "text/css"); + }); + + // 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 HIGH + server.on("/test.php", HTTP_POST, [](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); + }); + + // Send a POST request to /post with a form field message set to + server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){ + handleQRGPost(request); + request->send(200, "text/plain", "Hello, POST done"); + }); + + // Start server + server.begin(); +} + + const char *fetchWifiPw(const char *id) { for(int i=0; ivalidID && (sonde.si()->validPos&0x03==0x03)) { + Serial.println("Sending position via UDP"); + SondeInfo *s = sonde.si(); + char raw[201]; + const char *str = aprs_senddata(s->lat, s->lon, s->hei, s->hs, s->dir, s->vs, sondeTypeStr[s->type], s->id, "TE0ST", "EO"); + int rawlen = aprsstr_mon2raw(str, raw, MAXLEN); + Serial.print("Sending: "); Serial.println(raw); + udp.beginPacket(udpAddress,udpPort); + udp.write((const uint8_t *)raw,rawlen); + udp.endPacket(); + } + } sonde.updateDisplay(); } @@ -341,6 +563,25 @@ String translateEncryptionType(wifi_auth_mode_t encryptionType) { } } +//wifi event handler +void WiFiEvent(WiFiEvent_t event){ + switch(event) { + case SYSTEM_EVENT_STA_GOT_IP: + //When connected set + Serial.print("WiFi connected! IP address: "); + Serial.println(WiFi.localIP()); + //initializes the UDP state + //This initializes the transfer buffer + udp.begin(WiFi.localIP(),udpPort); + connected = true; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + Serial.println("WiFi lost connection"); + connected = false; + break; + } +} + static char* _scan[2]={"/","\\"}; void loopWifiScan() { u8x8.setFont(u8x8_font_chroma48medium8_r); @@ -348,6 +589,7 @@ void loopWifiScan() { int line=0; int cnt=0; + WiFi.disconnect(true); WiFi.mode(WIFI_STA); const char *id, *pw; int n = WiFi.scanNetworks(); @@ -368,10 +610,13 @@ void loopWifiScan() { pw=fetchWifiPw(id); if(pw) break; } - if(1||!pw) { id="test"; pw="test"; } + if(!pw) { id="test"; pw="test"; } Serial.print("Connecting to: "); Serial.println(id); u8x8.drawString(0,6, "Conn:"); u8x8.drawString(6,6, id); + //register event handler + WiFi.onEvent(WiFiEvent); + WiFi.begin(id, pw); while(WiFi.status() != WL_CONNECTED) { delay(500); @@ -379,17 +624,20 @@ void loopWifiScan() { u8x8.drawString(15,7,_scan[cnt&1]); cnt++; if(cnt==4) { - WiFi.disconnect(); // retry, for my buggy FritzBox + WiFi.disconnect(true); // retry, for my buggy FritzBox + WiFi.onEvent(WiFiEvent); WiFi.begin(id, pw); } if(cnt==10) { - WiFi.disconnect(); + WiFi.disconnect(true); delay(1000); - WiFi.softAP("sonde","sondesonde"); + WiFi.softAP(networks[0].id.c_str(),networks[0].pw.c_str()); IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); - sonde.setIP(myIP.toString().c_str()); + u8x8.drawString(0,6, "AP: "); + u8x8.drawString(6,6, networks[0].id.c_str()); + sonde.setIP(myIP.toString().c_str(), true); sonde.updateDisplayIP(); SetupAsyncServer(); delay(5000); @@ -402,7 +650,7 @@ void loopWifiScan() { Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); - sonde.setIP(WiFi.localIP().toString().c_str()); + sonde.setIP(WiFi.localIP().toString().c_str(), false); sonde.updateDisplayIP(); SetupAsyncServer(); delay(5000); diff --git a/RX_FSK/data/index.html b/RX_FSK/data/index.html index 2f4b4cb..7e78cf1 100644 --- a/RX_FSK/data/index.html +++ b/RX_FSK/data/index.html @@ -13,22 +13,50 @@

--> - - - - - - - - - - - - - - - +
+ + + + +
+ +
+

QRG

+ +
+ +
+

WIFI

+ +
+ +
+

Data

+ +
+ +
+

About

+ RDZSonde +
+ + diff --git a/RX_FSK/data/networks.txt b/RX_FSK/data/networks.txt index a0b956a..15d1e9b 100644 --- a/RX_FSK/data/networks.txt +++ b/RX_FSK/data/networks.txt @@ -1,3 +1,5 @@ +RDZsonde +RDZsonde DinoGast Schokolade AndroidDD diff --git a/RX_FSK/data/qrg.txt b/RX_FSK/data/qrg.txt index 6bb6ae2..2cadc97 100644 --- a/RX_FSK/data/qrg.txt +++ b/RX_FSK/data/qrg.txt @@ -1,8 +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 +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 index 50f94f8..57ee6d1 100644 --- a/RX_FSK/data/style.css +++ b/RX_FSK/data/style.css @@ -1,3 +1,50 @@ +body, html { + height: 100%; + margin: 0; + font-family: Arial; +} + +table, th, td { + border: 1px solid black; + border-collapse: collapse; + background-color: #ddd +} + +td#sfreq { + background-color: #ccc; +} + +.tab { + overflow: hidden; + border: 1px solid #ccc; +} + +.tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; +} + +.tab button:hover { + background-color: #ddd; +} + +.tab button.active { + background-color: #ccc; +} + +.tabcontent { + display: none; + padding: 6px 12px; + border: 1px solid #ccc; + border-top: none; + height: 100%; +} + html { font-family: Helvetica; display: inline-block; diff --git a/Setup.md b/Setup.md new file mode 100644 index 0000000..ca907fc --- /dev/null +++ b/Setup.md @@ -0,0 +1,66 @@ +# Prerequisites + +## Arduini IDE + +Get the latest Arduino IDE software from arduino.cc/en/Main/Software + +## ESP32 support + +File -> Preferences (or Arduino -> Preferences on MacOS) + +go to "Additional Board Manager URLs" + +Add *https://dl.espressif.com/dl/package_esp32_index.json* and press oK + + +Tool -> Boad -> Boards Manager + +search for "esp32" + +Install "esp32 by Espressif Systems" + +## ESP32 Flash Filesystem Upload support + +Get the zip file of the latest release from +https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/ + +Unzip the content to the tools folder of your Arduino IDE (~/Documents/Arduino/tools on MacOS, +similar on other OS) and restart IDE + +## Additional libraries + +Select Tools -> Library Manager + +Install "U8g2" + +## Additional libraries, part 2 + +From https://github.com/me-no-dev/ESPAsyncWebServer select "Download ZIP", extract to the libraries +folder of your Arduino IDE (~/Documents/Arduino/libraries on MacOS), rename main folder to ESPAsyncWebServer +(remove the "-master") + +From https://github.com/me-no-dev/AsyncTCP select "Download ZIP", extract to the libraries folder +of your Arduino IDE, and rename main folder to AsyncTCP + +## Additional libraries, part 3 + +Copy the libraries/SX1278FSK and libraries/SondeLib folder of this project to your Arduino IDE's libraries +folders, or, alternatively, create symbolic links (MacOS/Linux): + +cd ~/Documents/Arduino/libraries +ln -s /rdz_ttgo_sonde/libraries/SondeLib/ . +ln -s /rdz_ttgo_sonde/libraries/SX1278FSK/ . + +Restart the Arduino IDE + +## Final steps + +In the IDE Tools -> Board: -> +Select "TTGO LoRa32-OLED v1" + +Compile and Upload code + +Upload data to SPIFFS with Tools -> ESP32 Sketch Data Upload + + + diff --git a/libraries/SondeLib/Scanner.cpp b/libraries/SondeLib/Scanner.cpp index 17b21a9..e0e4ac0 100644 --- a/libraries/SondeLib/Scanner.cpp +++ b/libraries/SondeLib/Scanner.cpp @@ -4,15 +4,18 @@ extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8; -#define CHANBW 25 -#define SMOOTH 2 +#define CHANBW 10 +#define PIXSAMPL (50/CHANBW) +#define SMOOTH 4 #define STARTF 400000000 #define NCHAN ((int)(6000/CHANBW)) int scanresult[NCHAN]; -int scandisp[NCHAN/2]; +int scandisp[NCHAN/PIXSAMPL]; #define PLOT_N 120 +#define TICK1 (120/6) +#define TICK2 (TICK1/4) #define PLOT_MIN -220 #define PLOT_SCALE(x) (x>16)!=(frf>>16) ) { @@ -78,16 +84,24 @@ void Scanner::scan() int wait = 20 + 1000*(1<<(SMOOTH+1))/4/CHANBW; delayMicroseconds(wait); int rssi = -(int)sx1278.readRegister(REG_RSSI_VALUE_FSK); - scanresult[i] = rssi; + if(iter==0) { scanresult[i] = rssi; } else { + if(rssi>scanresult[i]) 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]; } + for(int i=0; iscandisp[i/PIXSAMPL]) scandisp[i/PIXSAMPL] = scanresult[i+j]; } Serial.print(scanresult[i]); Serial.print(", "); - if(((i+1)%32) == 0) Serial.println(); + } + Serial.println("\n"); + for(int i=0; i=MAXSONDE) { Serial.println("Cannot add another sonde, MAXSONDE reached"); return; } sondeList[nSonde].type = type; sondeList[nSonde].freq = frequency; + sondeList[nSonde].active = active; 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++; + // Skip non-active entries (but don't loop forever if there are no active ones + for(int i=0; i=nSonde) currentSonde=0; + } + } if(currentSonde>=nSonde) { currentSonde=0; } @@ -160,7 +179,8 @@ void Sonde::updateDisplayRSSI() { 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); + u8x8.drawTile(len,6,1,(sonde.si()->rssi&1)?halfdb_tile1:empty_tile1); + u8x8.drawTile(len,7,1,(sonde.si()->rssi&1)?halfdb_tile2:empty_tile2); } void Sonde::updateStat() { @@ -183,7 +203,7 @@ void Sonde::updateDisplayRXConfig() { } void Sonde::updateDisplayIP() { - u8x8.drawTile(6, 7, 10, myIP_tiles); + u8x8.drawTile(5, 7, 11, myIP_tiles); } // Probing RS41 diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h index 27936cd..9b1b5e2 100644 --- a/libraries/SondeLib/Sonde.h +++ b/libraries/SondeLib/Sonde.h @@ -12,6 +12,7 @@ extern const char *sondeTypeStr[5]; typedef struct st_sondeinfo { // receiver configuration + bool active; SondeType type; float freq; // decoded ID @@ -37,12 +38,13 @@ typedef struct st_sondeinfo { class Sonde { private: - int nSonde; - int currentSonde = 0; - SondeInfo sondeList[MAXSONDE+1]; public: + int currentSonde = 0; + int nSonde; + SondeInfo sondeList[MAXSONDE+1]; + void clearSonde(); - void addSonde(float frequency, SondeType type); + void addSonde(float frequency, SondeType type, int active); void nextConfig(); void setup(); @@ -59,7 +61,7 @@ public: void updateDisplay(); void updateDisplayScanner(); void clearDisplay(); - void setIP(const char *ip); + void setIP(const char *ip, bool isAP); }; extern Sonde sonde; diff --git a/libraries/SondeLib/aprs.cpp b/libraries/SondeLib/aprs.cpp new file mode 100644 index 0000000..7c57310 --- /dev/null +++ b/libraries/SondeLib/aprs.cpp @@ -0,0 +1,286 @@ +/* Copyright (C) Hansi Reiser, dl9rdz + * + * partially based on dxlAPRS toolchain + * + * Copyright (C) Christian Rabler + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include ; +//#include +//#include +#include +#include +#include +#include "aprs.h" + +#if 0 +int openudp(const char *ip, int port, struct sockaddr_in *si) { + int fd; + if((fd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) return -1; + memset((char *)&si, 0, sizeof(si)); + si->sin_family = AF_INET; + si->sin_port = htons(port); + if(inet_aton(ip, &(si->sin_addr))==0) { + return -1; + } + return fd; +} + +int sendudp(int fd, struct sockaddr_in *si, char *frame, int framelen) +{ + if(sendto(fd, frame, framelen, 0, (struct sockaddr *)si, sizeof(struct sockaddr_in))==-1) { + return -1; + } + return 0; +} +#endif + + + +void aprsstr_append(char *b, const char *data) +{ + int blen=strlen(b); + int len=strlen(data); + if(blen+len>MAXLEN) len=MAXLEN-blen; + strncat(b, data, len); +} + +uint32_t realcard(float x) { + if(x<0) return 0; + else return (uint32_t)x; +} + + +/* CRC for AXUDP frames */ + +#define APRSCRC_POLY 0x8408 +static uint8_t CRCL[256]; +static uint8_t CRCH[256]; + +void aprs_gencrctab(void) +{ + uint32_t c; + uint32_t crc; + uint32_t i; + for (c = 0UL; c<=255UL; c++) { + crc = 255UL-c; + for (i = 0UL; i<=7UL; i++) { + if ((crc&1)) crc = (uint32_t)((uint32_t)(crc>>1)^APRSCRC_POLY); + else crc = crc>>1; + } /* end for */ + CRCL[c] = (uint8_t)crc; + CRCH[c] = (uint8_t)(255UL-(crc>>8)); + } /* end for */ +} /* end Gencrctab() */ + +static void aprsstr_appcrc(char frame[], uint32_t frame_len, int32_t size) +{ + uint8_t h; + uint8_t l; + uint8_t b; + int32_t i; + int32_t tmp; + l = 0U; + h = 0U; + tmp = size-1L; + i = 0L; + if (i<=tmp) for (;; i++) { + b = (uint8_t)((uint8_t)(uint8_t)frame[i]^l); + l = CRCL[b]^h; + h = CRCH[b]; + if (i==tmp) break; + } /* end for */ + frame[size] = (char)l; + frame[size+1L] = (char)h; +} /* end aprsstr_appcrc() */ + + +static int mkaprscall(int32_t * p, char raw[], + uint32_t * i, const char mon[], + char sep1, char sep2, char sep3, + uint32_t sbase) +{ + uint32_t s; + uint32_t l; + l = 0UL; + while ((((mon[*i] && mon[*i]!=sep1) && mon[*i]!=sep2) && mon[*i]!=sep3) + && mon[*i]!='-') { + s = (uint32_t)(uint8_t)mon[*i]*2UL&255UL; + if (s<=64UL) return 0; + raw[*p] = (char)s; + ++*p; + ++*i; + ++l; + if (l>=7UL) return 0; + } + while (l<6UL) { + raw[*p] = '@'; + ++*p; + ++l; + } + s = 0UL; + if (mon[*i]=='-') { + ++*i; + while ((uint8_t)mon[*i]>='0' && (uint8_t)mon[*i]<='9') { + s = (s*10UL+(uint32_t)(uint8_t)mon[*i])-48UL; + ++*i; + } + if (s>15UL) return 0; + } + raw[*p] = (char)((s+sbase)*2UL); + ++*p; + return 1; +} /* end call() */ + + +// returns raw len, 0 in case of error +extern int aprsstr_mon2raw(const char *mon, char raw[], int raw_len) +{ + uint32_t r; + uint32_t n; + uint32_t i; + uint32_t tmp; + int p = 7L; + i = 0UL; + fprintf(stderr,"mon2raw for %s\n", mon); + if (!mkaprscall(&p, raw, &i, mon, '>', 0, 0, 48UL)) { + return 0; + } + p = 0L; + if (mon[i]!='>') return 0; + /* ">" */ + ++i; + if (!mkaprscall(&p, raw, &i, mon, ':', ',', 0, 112UL)) { + return 0; + } + p = 14L; + n = 0UL; + while (mon[i]==',') { + ++i; + if (!mkaprscall(&p, raw, &i, mon, ':', ',', '*', 48UL)) { + return 0; + } + ++n; + if (n>8UL) { + return 0; + } + if (mon[i]=='*') { + /* "*" has repeatet sign */ + ++i; + r = (uint32_t)p; + if (r>=21UL) for (tmp = (uint32_t)(r-21UL)/7UL;;) { + raw[r-1UL] = (char)((uint32_t)(uint8_t)raw[r-1UL]+128UL); + /* set "has repeated" flags */ + if (!tmp) break; + --tmp; + r -= 7UL; + } /* end for */ + } + } + if (p==0L || mon[i]!=':') { + return 0; + } + raw[p-1L] = (char)((uint32_t)(uint8_t)raw[p-1L]+1UL); + /* end address field mark */ + raw[p] = '\003'; + ++p; + raw[p] = '\360'; + ++p; + ++i; + n = 256UL; + while (mon[i]) { + /* copy info part */ + if (p>=(int32_t)(raw_len-1)-2L || n==0UL) { + return 0; + } + raw[p] = mon[i]; + ++p; + ++i; + --n; + } + aprsstr_appcrc(raw, raw_len, p); + fprintf(stderr,"results in %s\n",raw); + return p+2; +} /* end mon2raw() */ + + +#define FEET (1.0/0.3048) +#define KNOTS (1.851984) + +char b[201]; +char raw[201]; + +char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym) +{ + *b=0; + aprsstr_append(b, usercall); + aprsstr_append(b, ">"); + const char *destcall="APZRDZ"; + aprsstr_append(b, destcall); + // uncompressed + aprsstr_append(b, ":;"); + char tmp[10]; + snprintf(tmp,10,"%s ",objname); + aprsstr_append(b, tmp); + aprsstr_append(b, "*"); + // TODO: time + //aprsstr_append_data(time, ds); + aprsstr_append(b, "121212z"); + int i = strlen(b); + int lati = abs((int)lat); + int latm = (fabs(lat)-lati)*6000; + snprintf(b+i, MAXLEN-i, "%02d%02d.%02d%c%c", lati, latm/100, latm%100, lat<0?'S':'N', sym[0]); + i = strlen(b); + int loni = abs((int)lon); + int lonm = (fabs(lon)-loni)*6000; + snprintf(b+i, MAXLEN-i, "%03d%02d.%02d%c%c", loni, lonm/100, lonm%100, lon<0?'W':'E', sym[1]); +#if 1 + if(speed>0.5) { + i=strlen(b); + snprintf(b+i, MAXLEN-i, "%03d/%03d", realcard(dir+1.5), realcard(speed*1.0/KNOTS+0.5)); + } +#endif + if(hei>0.5) { + i=strlen(b); + snprintf(b+i, MAXLEN-i, "/A=%06d", realcard(hei*FEET+0.5)); + } +#if 0 + int dao=1; + if(dao) { + i=strlen(b); + snprintf(b+i, MAXLEN-i, "!w%c%c!", 33+dao91(lat), 33+dao91(lon)); + } +#endif + const char *comm="&test"; + strcat(b, comm); + return b; +} + + +#if 0 +int main(int argc, char *argv[]) +{ + Gencrctab(); + + struct sockaddr_in si; + int fd = openudp("127.0.0.1",9002,&si); + if(fd<0) { fprintf(stderr,"open failed\n"); return 1; } + + float lat=48, lon=10; + while(1) { + const char *str = aprs_senddata(lat, lon, 543, 5, 180, 1.5, "RS41", "TE0ST", "TE1ST", "EO"); + int rawlen = aprsstr_mon2raw(str, raw, MAXLEN); + sendudp(fd, raw, rawlen); + + str = "OE3XKC>APMI06,qAR,OE3XLR:;ER-341109*111111z4803.61NE01532.39E0145.650MHz R15k OE3XPA"; + rawlen = aprsstr_mon2raw(str, raw, MAXLEN); + sendudp(fd, &si, raw, rawlen); + lat += 0.002; lon += 0.01; + sleep(5); + } +} +#endif diff --git a/libraries/SondeLib/aprs.h b/libraries/SondeLib/aprs.h new file mode 100644 index 0000000..7b1f26b --- /dev/null +++ b/libraries/SondeLib/aprs.h @@ -0,0 +1,21 @@ + +#ifndef _aprs_h +#define _aprs_h + +enum IDTYPE { ID_DFMDXL, ID_DFMREAL, ID_DFMAUTO }; + +typedef struct st_feedinfo { + bool active; + int type; // 0:UDP(axudp), 1:TCP(aprs.fi) + char *host; + int port; + int rate; + int idformat; // 0: dxl 1: real 2: auto +} +#define MAXLEN 201 +void aprs_gencrctab(void); +int aprsstr_mon2raw(const char *mon, char raw[], int raw_len); +char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym); + + +#endif
IDPW
1DinoGastSchokolade
2