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
+
+
+
+ |
+ ID |
+ PW |
+
+
+ 1 |
+ DinoGast |
+ Schokolade |
+
+ 2 |
+ |
+ |
+
+
+
+
+
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;