Some working things...

Squashed commit of the following:

commit da9d4367be
Author: Hans P. Reiser <hr@sec.uni-passau.de>
Date:   Fri Apr 5 23:32:26 2019 +0200

    some minor updates

commit e7360f2c1e
Author: Hans P. Reiser <hr@sec.uni-passau.de>
Date:   Fri Apr 5 20:05:22 2019 +0200

    incremental enhancements on train trip

commit b3ac2c5506
Author: Hans P. Reiser <hr@sec.uni-passau.de>
Date:   Fri Apr 5 20:05:18 2019 +0200

    incremental enhancements on train trip

commit ab4367992e
Author: Hans P. Reiser <hr@sec.uni-passau.de>
Date:   Thu Apr 4 23:46:48 2019 +0200

    some more testing

commit d02d687670
Author: Hans P. Reiser <hr@sec.uni-passau.de>
Date:   Thu Apr 4 23:46:37 2019 +0200

    some more testing

commit 34f9971143
Author: Hansi Reiser <dl9rdz@darc.de>
Date:   Thu Apr 4 08:11:14 2019 +0200

    scanner-test

commit a22bb43c89
Author: Hansi Reiser <dl9rdz@darc.de>
Date:   Thu Apr 4 08:10:56 2019 +0200

    scanner-test
pull/4/head
Hans P. Reiser 2019-04-06 22:22:08 +02:00
rodzic c4f3e8d63b
commit f9a16f827c
13 zmienionych plików z 817 dodań i 144 usunięć

Wyświetl plik

@ -1 +1,45 @@
# rdz_ttgo_sonde
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)

Wyświetl plik

@ -1,14 +1,15 @@
#include <U8x8lib.h>
#include <Sonde.h>
#include <WiFi.h>
#include <RS41.h>
#include <SX1278FSK.h>
#include <rsc.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <U8x8lib.h>
#include <SPI.h>
#include <SX1278FSK.h>
#include <Sonde.h>
#include <Scanner.h>
//#include <RS41.h>
//#include <DFM.h>
#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 <a href=\"/H\">here</a> to turn the LED on pin 5 on.<br>");
client.print("Click <a href=\"/L\">here</a> to turn the LED on pin 5 off.<br>");
// 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; j<i; j++) { Serial.print(networks[j].id); Serial.print(": "); Serial.println(networks[j].pw); }
}
void setupChannelList() {
File file = SPIFFS.open("/qrg.txt", "r");
if(!file) {
Serial.println("There was an error opening the file '/qrg.txt' for reading");
return;
}
int i=0;
sonde.clearSonde();
while(file.available()) {
String line = file.readStringUntil('\n');
if(!file.available()) break;
if(line[0] == '#') continue;
char *space = strchr(line.c_str(), ' ');
if(!space) continue;
*space = 0;
float freq = atof(line.c_str());
SondeType type;
if(space[1]=='4') { type=STYPE_RS41; }
else if (space[1]=='9') { type=STYPE_DFM09; }
else if (space[1]=='6') { type=STYPE_DFM06; }
else continue;
Serial.printf("Adding %f with type %d\b",freq,type);
sonde.addSonde(freq, type);
i++;
}
nNetworks = i;
Serial.print(i); Serial.println(" networks in networks.txt\n");
for(int j=0; j<i; j++) { Serial.print(networks[j].id); Serial.print(": "); Serial.println(networks[j].pw); }
}
const char *fetchWifiPw(const char *id) {
for(int i=0; i<nNetworks; i++) {
Serial.print("Comparing '");
Serial.print(id);
Serial.print("' and '");
Serial.print(networks[i].id.c_str());
Serial.println("'");
if(strcmp(id,networks[i].id.c_str())==0) return networks[i].pw.c_str();
}
return NULL;
}
enum KeyPress { KP_NONE=0, KP_SHORT, KP_DOUBLE, KP_MID, KP_LONG };
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
KeyPress pressed;
unsigned long press_ts;
boolean doublepress;
};
Button button1 = {0, 0, KP_NONE, 0, false};
void IRAM_ATTR buttonISR() {
if(digitalRead(0)==0) { // Button down
if(millis()-button1.press_ts<500) {
// Double press
button1.doublepress = true;
} else {
button1.doublepress = false;
}
button1.press_ts = millis();
} else { //Button up
unsigned int elapsed = millis()-button1.press_ts;
if(elapsed>1500) {
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);

Wyświetl plik

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>ESP32 Web Server</h1>
<!--
<p>GPIO state: <strong> %STATE%</strong></p>
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
-->
<table class="KKK">
<tr>
<th></th>
<th style="width:100px">ID</th>
<th style="width:100px">PW</th>
</tr>
<tr>
<td>1</td>
<td contenteditable>DinoGast</td>
<td contenteditable>Schokolade</td>
<tr>
<td>2</td>
<td contenteditable></td>
<td contenteditable></td>
</tr>
</body>
</html>

Wyświetl plik

@ -0,0 +1,4 @@
DinoGast
Schokolade
AndroidDD
dl9rdzhr

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -11,6 +11,7 @@
#include "SX1278FSK.h"
#include "SPI.h"
#include <Sonde.h>
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
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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<MAXLEN; i++) { data[i] = reverse(data[i]); }
//printRaw(data, MAXLEN);
for(int i=0; i<MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
//printRaw(data, MAXLEN);
decode41(data, MAXLEN);
return RX_OK;
}
RS41 rs41 = RS41();

Wyświetl plik

@ -0,0 +1,95 @@
#include "Scanner.h"
#include <SX1278FSK.h>
#include <U8x8lib.h>
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<PLOT_MIN?0:(x-PLOT_MIN)/2)
const byte tilepatterns[9]={0,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF};
void Scanner::fillTiles(uint8_t *row, int value) {
for(int y=0; y<8; y++) {
int nbits = value - 8*(7-y);
if(nbits<0) { row[8*y]=0; continue; }
if(nbits>=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<PLOT_N; i+=8) {
for(int j=0; j<8; j++) {
fillTiles(row+j, PLOT_SCALE(scandisp[i+j]));
}
for(int y=0; y<8; y++) {
u8x8.drawTile(i/8, y, 1, row+8*y);
}
}
}
void Scanner::scan()
{
#if 0
// Test only
for(int i=0; i<PLOT_N; i++) {
scandisp[i] = 30*sin(2*3.1415*i/50)-180;
}
return;
#endif
// Configure
sx1278.writeRegister(REG_PLL_HOP, 0x80); // FastHopOn
sx1278.setRxBandwidth(CHANBW*1000);
sx1278.writeRegister(REG_RSSI_CONFIG, SMOOTH&0x07);
sx1278.setFrequency(STARTF);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
delay(5);
unsigned long start = millis();
uint32_t lastfrf=-1;
for(int i=0; i<NCHAN; i++) {
float freq = STARTF + 1000.0*i*CHANBW;
uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ;
if( (lastfrf>>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; i<NCHAN; i+=2) {
scandisp[i/2] = scanresult[i];
for(int j=1; j<2; j++) { if(scanresult[i+j]>scandisp[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();

Wyświetl plik

@ -0,0 +1,24 @@
#ifndef _SCANNER_H
#define _SCANNER_H
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
class Scanner
{
private:
void fillTiles(uint8_t *row, int value);
public:
void plotResult();
void scan(void);
};
extern Scanner scanner;
#endif

Wyświetl plik

@ -2,10 +2,13 @@
#include <U8g2lib.h>
#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<len; i++) {
if(ip[i]=='.') { myIP_tiles[tp++]=0x40; myIP_tiles[tp++]=0x00; }
else {
int idx = ip[i]-'0';
memcpy(myIP_tiles+tp, &font[idx], 5);
myIP_tiles[tp+5] = 0;
tp+=6;
}
}
while(tp<8*10) { myIP_tiles[tp++]=0; }
}
void Sonde::clearSonde() {
nSonde = 0;
}
void Sonde::addSonde(float frequency, SondeType type) {
if(nSonde>=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();

Wyświetl plik

@ -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;