Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80602dc049 |
@@ -1,7 +1,8 @@
|
||||
RadioSonde Version 0.8.8
|
||||
RadioSonde Version 0.9.2
|
||||
============================
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO6.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO7.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO8.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web6.png" width="20%">
|
||||
@@ -11,66 +12,98 @@
|
||||
Projet basé sur le travail de DL9RDZ
|
||||
====================================
|
||||
|
||||
Pour TTGO LORA 32 esp32 pico D4
|
||||
Décodage de RadioSonde RS41 and DFM06/09 et M10
|
||||
Pour TTGO LORA 32 esp32 pico D4 <br>
|
||||
Décodage de RadioSonde RS41/92 and DFM06/09/17 et M10+/20
|
||||
|
||||
Attention à la version de votre TTGO!
|
||||
vous devez modifier dans config.txt, le port de l'écran OLED
|
||||
- TTGO v1: SDA=4 SCL=15, RST=16
|
||||
- TTGO v2: SDA=21 SCL=22, RST=16
|
||||
Attention à la version de votre TTGO! <br>
|
||||
vous devez modifier dans config.txt, le port de l'écran OLED <br>
|
||||
- TTGO v1: SDA=4 SCL=15, RST=16 <br>
|
||||
- TTGO v2: SDA=21 SCL=22, RST=16 <br>
|
||||
puis pour le GPS:<br>
|
||||
- Lilygo esp32 GPS pin 34 Rx, 12 Tx<br>
|
||||
puis Buzzer<br>
|
||||
- par defaut 12 si pas de GPS si non pin 25<br>
|
||||
|
||||
## Version en production 0.8.5 devel 0.8.7
|
||||
## Version en production 0.9.1 devel 0.9.2
|
||||
|
||||
## 0.8.0
|
||||
travail de refonte et réécriture du code
|
||||
## 0.9.2
|
||||
Correctif Dash SiteWeb config
|
||||
ADD API SondeHub Thank's DL9RDZ
|
||||
|
||||
## 0.9.1
|
||||
Corection RS41 <br>
|
||||
Correction DFM 06/09 <br>
|
||||
Add DFM17 <br>
|
||||
Correction for all trame recived for M10 and M20 1000ms to 1512ms,<br>
|
||||
Correction formulaire QRG, and end RS no save
|
||||
|
||||
## 0.9.0
|
||||
Add M20
|
||||
|
||||
## 0.8.8
|
||||
|
||||
Add M10+ <br>
|
||||
Add Temps restant avant impacte au sol si 99: 0. 0 soit le balon de la sonde n a pas encore éclaté, <br>
|
||||
ou les informations ne sont pas disponible actuellement <br>
|
||||
Add Test Buzzer au démarrage "Arche Perdu" Lol pour des chasseurs de sonde!<br>
|
||||
Compatible Lilygo esp32 GPS inboard pin 34 Rx, 12 Tx
|
||||
|
||||
## 0.8.7
|
||||
|
||||
correction bug Buzzer Off->On->Off <br>
|
||||
Add GainLNA RX SX1278FSK on Web config paramètre <br>
|
||||
Add update OTA Os + DataWeb <br>
|
||||
correction bugs sondmap.html <br>
|
||||
correction text upgrade Os et DataWeb <br>
|
||||
correction texte boussole S et N <br>
|
||||
correction bugs distance 4928Km si lat et lon =0 erroné <br>
|
||||
correction bugs fonction Vbat <br>
|
||||
Add Telemetry width export data.csv <br>
|
||||
Suppression µSD incompatible avec pin SX1278FSK et SPI <br>
|
||||
Add transfert Telemetry To µSD on put SD automatic
|
||||
|
||||
## 0.8.5
|
||||
|
||||
Evolution majeur du système <br>
|
||||
affichage du pourcentage de la batterie en mode scanning <br>
|
||||
création d'une fenetre Batterie, Boussole <br>
|
||||
suppresion lib et code TFT <br>
|
||||
création Azimute, elevation correction de Bugs majeur , mineur <br>
|
||||
Ajout fonction Smetre, Buzzer, QTH, Gps on off ... <br>
|
||||
mise à jour OTA <br>
|
||||
trop de modification pour toutes les expliciter!
|
||||
|
||||
## 0.8.1
|
||||
|
||||
modification de la partie Web
|
||||
|
||||
## 0.8.5
|
||||
## 0.8.0
|
||||
|
||||
Evolution majeur du système
|
||||
affichage du pourcentage de la batterie en mode scanning
|
||||
création d'une fenetre Batterie, Boussole
|
||||
suppresion lib et code TFT
|
||||
création Azimute, elevation correction de Bugs majeur , mineur
|
||||
Ajout fonction Smetre, Buzzer, QTH, Gps on off ...
|
||||
mise à jour OTA
|
||||
trop de modification pour toutes les expliciter!
|
||||
travail de refonte et réécriture du code
|
||||
|
||||
## 0.8.7
|
||||
correction bug Buzzer Off->On->Off
|
||||
Add GainLNA RX SX1278FSK on Web config paramètre
|
||||
Add update OTA Os + DataWeb
|
||||
correction bugs sondmap.html
|
||||
correction text upgrade Os et DataWeb
|
||||
correction texte boussole S et N
|
||||
correction bugs distance 4928Km si lat et lon =0 erroné
|
||||
correction bugs fonction Vbat
|
||||
Add Telemetry width export data.csv
|
||||
Suppression µSD incompatible avec pin SX1278FSK et SPI
|
||||
Add transfert Telemetry To µSD on put SD automatic
|
||||
|
||||
## 0.8.8
|
||||
Add M10+
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
## Les Boutons optionnel à ajouter(souder)
|
||||
sur les GPIO 1002 et 1004
|
||||
sur les GPIO 1002 et 1004 <br>
|
||||
attention:
|
||||
|
||||
+5V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
|
||||
+3.3V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
|
||||
|
||||
- appuie court <1.5 seconds
|
||||
- appuie double court 0.5 seconds
|
||||
- appuie moyen 2-4 seconds
|
||||
- appuie court <1.5 seconds <br>
|
||||
- appuie double court 0.5 seconds <br>
|
||||
- appuie moyen 2-4 seconds <br>
|
||||
- appuie long >5 seconds
|
||||
|
||||
## Buzzer optionnel à ajouter(souder)
|
||||
sur les GPIO 25 ou 12 suivant le modèl
|
||||
|
||||
GPIO --[ BUZZER ]---/
|
||||
|
||||
## Wifi configuration
|
||||
|
||||
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP
|
||||
le SSID et mot de passe par défaut est: Radiosonde
|
||||
en mode AP, il doit être en 192.168.4.1,
|
||||
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP<br>
|
||||
le SSID et mot de passe par défaut est: <b>Radiosonde</b> <br>
|
||||
en mode AP, il doit être en 192.168.4.1, <br>
|
||||
mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi
|
||||
connecté.
|
||||
|
||||
@@ -109,3 +142,142 @@ voir Setup.md pour l'installation!
|
||||
Xavier
|
||||
|
||||
|
||||
|
||||
RadioSonde Version 0.9.1
|
||||
============================
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO6.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/TTGO7.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO8.jpg" width="50%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web6.png" width="20%">
|
||||
<img src="http://xavier.debert.free.fr/RS/Web7.png" width="20%">
|
||||
|
||||
|
||||
Projet basé sur le travail de DL9RDZ
|
||||
====================================
|
||||
|
||||
Pour TTGO LORA 32 esp32 pico D4 <br>
|
||||
Décodage de RadioSonde RS41/92 and DFM06/09/17 et M10+/20
|
||||
|
||||
Attention à la version de votre TTGO! <br>
|
||||
vous devez modifier dans config.txt, le port de l'écran OLED <br>
|
||||
- TTGO v1: SDA=4 SCL=15, RST=16 <br>
|
||||
- TTGO v2: SDA=21 SCL=22, RST=16
|
||||
|
||||
## Version en production 0.9.1 devel 0.9.2
|
||||
|
||||
## 0.9.1
|
||||
Corection RS41 <br>
|
||||
Correction DFM 06/09 <br>
|
||||
Add DFM17 <br>
|
||||
Correction for all trame recived for M10 and M20 1000ms to 1512ms,<br>
|
||||
Correction formulaire QRG, and end RS no save
|
||||
|
||||
## 0.9.0
|
||||
Add M20
|
||||
|
||||
## 0.8.8
|
||||
|
||||
Add M10+ <br>
|
||||
Add Temps restant avant impacte au sol si 99: 0. 0 soit le balon de la sonde n a pas encore éclaté, <br>
|
||||
ou les informations ne sont pas disponible actuellement <br>
|
||||
Add Test Buzzer au démarrage "Arche Perdu" Lol pour des chasseurs de sonde!<br>
|
||||
Compatible Lilygo esp32 GPS inboard pin 34 Rx, 12 Tx
|
||||
|
||||
## 0.8.7
|
||||
|
||||
correction bug Buzzer Off->On->Off <br>
|
||||
Add GainLNA RX SX1278FSK on Web config paramètre <br>
|
||||
Add update OTA Os + DataWeb <br>
|
||||
correction bugs sondmap.html <br>
|
||||
correction text upgrade Os et DataWeb <br>
|
||||
correction texte boussole S et N <br>
|
||||
correction bugs distance 4928Km si lat et lon =0 erroné <br>
|
||||
correction bugs fonction Vbat <br>
|
||||
Add Telemetry width export data.csv <br>
|
||||
Suppression µSD incompatible avec pin SX1278FSK et SPI <br>
|
||||
Add transfert Telemetry To µSD on put SD automatic
|
||||
|
||||
## 0.8.5
|
||||
|
||||
Evolution majeur du système <br>
|
||||
affichage du pourcentage de la batterie en mode scanning <br>
|
||||
création d'une fenetre Batterie, Boussole <br>
|
||||
suppresion lib et code TFT <br>
|
||||
création Azimute, elevation correction de Bugs majeur , mineur <br>
|
||||
Ajout fonction Smetre, Buzzer, QTH, Gps on off ... <br>
|
||||
mise à jour OTA <br>
|
||||
trop de modification pour toutes les expliciter!
|
||||
|
||||
## 0.8.1
|
||||
|
||||
modification de la partie Web
|
||||
|
||||
## 0.8.0
|
||||
|
||||
travail de refonte et réécriture du code
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
## Les Boutons optionnel à ajouter(souder)
|
||||
sur les GPIO 1002 et 1004 <br>
|
||||
attention:
|
||||
|
||||
+3.3V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
|
||||
|
||||
- appuie court <1.5 seconds <br>
|
||||
- appuie double court 0.5 seconds <br>
|
||||
- appuie moyen 2-4 seconds <br>
|
||||
- appuie long >5 seconds
|
||||
|
||||
## Buzzer optionnel à ajouter(souder)
|
||||
sur les GPIO 25 ou 12 suivant le modèl
|
||||
|
||||
GPIO --[ BUZZER ]---/
|
||||
|
||||
## Wifi configuration
|
||||
|
||||
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP<br>
|
||||
le SSID et mot de passe par défaut est: <b>Radiosonde</b> <br>
|
||||
en mode AP, il doit être en 192.168.4.1, <br>
|
||||
mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi
|
||||
connecté.
|
||||
|
||||
|
||||
|
||||
## Mode Scanne
|
||||
|
||||
Le fichier qrg.txt contient la liste par défaut des cannaux.
|
||||
pour y configurer les noms, fréquences et mode [M=M10, 6=DFM06, 9=DFM09 et 4=RS41]
|
||||
|
||||
|
||||
## Mode Réception
|
||||
|
||||
En réception, une seul fréquence est décodé, les infos de la sonde (ID, GPS, RSSI, ...)
|
||||
seront affichées dans plusieur fenetre à choisir ( 0 à 6) à configurer dans la page Web
|
||||
rubrique config.
|
||||
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
|
||||
|
||||
## Mode Analyseur
|
||||
|
||||
Le mode analyseur de spectre (400..406 MHz) est affiché (chaque ligne == 50 kHz)
|
||||
Pour les cartes TTGO sans bouton configurable, il y a un nouveau paramètre dans config.txt:
|
||||
- spectrum=10 // 0=off / 1-99 nombre de seconds pour afficher l'analyseur
|
||||
- timer=1 // 0=off / 1= afficher le compte à rebours du spectre dans l'affichage du spectre
|
||||
- marker=1 // 0=off / 1= afficher la fréquence dans l'affichage du spectre
|
||||
|
||||
## Setup
|
||||
|
||||
voir Setup.md pour l'installation!
|
||||
|
||||
73
|
||||
Xavier
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <SPIFFS.h>
|
||||
//#include <U8x8lib.h>
|
||||
//#include <U8g2lib.h>
|
||||
#include <SPI.h>
|
||||
#include <Update.h>
|
||||
#include <ESPmDNS.h>
|
||||
@@ -24,6 +22,8 @@
|
||||
|
||||
#include "spiffs_sd.h"
|
||||
|
||||
#include <APRS-IS.h>
|
||||
|
||||
int e;
|
||||
enum MainState { ST_DECODER, ST_SPECTRUM, ST_WIFISCAN, ST_UPDATE, ST_TOUCHCALIB };
|
||||
static MainState mainState = ST_WIFISCAN; // ST_WIFISCAN;
|
||||
@@ -42,22 +42,20 @@ String updateBinM = "/RS/radiosonde/update.ino.bin";
|
||||
String updateBinD = "/RS/radiosondedevel/update.ino.bin";
|
||||
String *updateBin = &updateBinM;
|
||||
|
||||
String updateDataWeb = "/RS/dataweb/index.html.txt";
|
||||
String updateDataWeb2 = "/RS/dataweb/sondemap.html.txt";
|
||||
String updateDataWeb3 = "/RS/dataweb/config.txt";
|
||||
String updateDataWeb4 = "/RS/dataweb/screens.txt";
|
||||
String updateDataWeb5 = "/RS/dataweb/style.css.txt";
|
||||
String updateDataWeb6 = "/RS/dataweb/qrg.txt";
|
||||
String updateDataWeb7 = "/RS/dataweb/networks.txt";
|
||||
String updateDataWeb8 = "/RS/dataweb/fontawesome-webfont.eot.txt";
|
||||
String updateDataWeb9 = "/RS/dataweb/fontawesome-webfont.woff.txt";
|
||||
String updateDataWeb = "/RS/dataweb/data.tar";
|
||||
|
||||
#define LOCALUDPPORT 9002
|
||||
#define LOCALUDPPORT 14580
|
||||
|
||||
boolean connected = false;
|
||||
WiFiUDP udp;
|
||||
WiFiClient client;
|
||||
|
||||
//SondeHub
|
||||
#define SONDEHUB_STATION_UPDATE_TIME (60*60*1000) // 60 min
|
||||
#define SONDEHUB_MOBILE_STATION_UPDATE_TIME (30*1000) // 30 sec
|
||||
WiFiClient shclient; // Sondehub v2
|
||||
unsigned long time_last_update = 0;
|
||||
|
||||
// KISS over TCP für communicating with APRSdroid
|
||||
WiFiServer tncserver(14580);
|
||||
WiFiClient tncclient;
|
||||
@@ -164,9 +162,10 @@ void setupChannelList() {
|
||||
sonde.clearSonde();
|
||||
Serial.println("Reading channel config:");
|
||||
while (file.available()) {
|
||||
String line = readLine(file); //file.readStringUntil('\n');
|
||||
//String line = readLine(file); //file.readStringUntil('\n');
|
||||
String line = file.readStringUntil('\n');
|
||||
String sitename;
|
||||
if (!file.available()) break;
|
||||
//if (!file.available()) break;
|
||||
if (line[0] == '#') continue;
|
||||
char *space = strchr(line.c_str(), ' ');
|
||||
if (!space) continue;
|
||||
@@ -184,9 +183,15 @@ void setupChannelList() {
|
||||
else if (space[1] == '6') {
|
||||
type = STYPE_DFM06;
|
||||
}
|
||||
else if (space[1] == 'D') {
|
||||
type = STYPE_DFM;
|
||||
}
|
||||
else if (space[1] == 'M') {
|
||||
type = STYPE_M10;
|
||||
}
|
||||
else if (space[1] == '2') {
|
||||
type = STYPE_M20;
|
||||
}
|
||||
else continue;
|
||||
int active = space[3] == '+' ? 1 : 0;
|
||||
if (space[4] == ' ') {
|
||||
@@ -205,25 +210,55 @@ void setupChannelList() {
|
||||
|
||||
const char *createQRGForm() {
|
||||
char *ptr = message;
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"qrg.html\" method=\"post\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">");
|
||||
strcat(ptr, "<script type=\"text/javascript\">"
|
||||
"let stypes=new Map();"
|
||||
"stypes.set('4', 'RS41');"
|
||||
"stypes.set('R', 'RS92');"
|
||||
"stypes.set('9', 'DFM9');"
|
||||
"stypes.set('6', 'DFM6');"
|
||||
"stypes.set('D', 'DFM All');"
|
||||
"stypes.set('M', 'M10');"
|
||||
"stypes.set('2', 'M20');"
|
||||
"function prep() {"
|
||||
" var stlist=document.querySelectorAll(\"input.stype\");"
|
||||
" for(txt of stlist){"
|
||||
" var val=txt.getAttribute('value'); var nam=txt.getAttribute('name'); "
|
||||
" var sel=document.createElement('select');"
|
||||
" sel.setAttribute('name',nam);"
|
||||
" for(stype of stypes) { "
|
||||
" var opt=document.createElement('option');"
|
||||
" opt.value=stype[0];"
|
||||
" opt.innerHTML=stype[1];"
|
||||
" if(stype[0]==val) { opt.setAttribute('selected','selected'); }"
|
||||
" sel.appendChild(opt);"
|
||||
" } txt.replaceWith(sel); } } "
|
||||
" window.onload = prep; "
|
||||
"</script>");
|
||||
|
||||
strcat(ptr, "</head><body><form action=\"qrg.html\" method=\"post\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
|
||||
for (int i = 0; i < sonde.config.maxsonde; i++) {
|
||||
String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type);
|
||||
//String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type);
|
||||
String site = sonde.sondeList[i].launchsite;
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>%d</td><td><input name=\"A%d\" type=\"checkbox\" %s/></td>"
|
||||
"<td><input name=\"F%d\" type=\"text\" value=\"%3.3f\"></td>"
|
||||
"<td><input name=\"S%d\" type=\"text\" value=\"%s\"></td>"
|
||||
"<td><select name=\"T%d\">%s</select></td>",
|
||||
//"<td><select name=\"T%d\">%s</select></td>",
|
||||
"<td><input class='stype' name='T%d' value='%c'>",
|
||||
i + 1,
|
||||
i + 1, (i < sonde.nSonde && sonde.sondeList[i].active) ? "checked" : "",
|
||||
i + 1, i >= sonde.nSonde ? 400.000 : sonde.sondeList[i].freq,
|
||||
i + 1, i >= sonde.nSonde ? " " : sonde.sondeList[i].launchsite,
|
||||
i + 1, s.c_str());
|
||||
i + 1, i >= sonde.nSonde ? 2 : sondeTypeChar[sonde.sondeList[i].type] );
|
||||
//i + 1, s.c_str());
|
||||
}
|
||||
strcat(ptr, "</table><input type=\"submit\" value=\"Update\"/></form></body></html>");
|
||||
Serial.printf("QRG form: size=%d bytes\n",strlen(message));
|
||||
Serial.printf("QRG form: size=%d bytes\n", strlen(message));
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *handleQRGPost(AsyncWebServerRequest *request) {
|
||||
char label[10];
|
||||
// parameters: a_i, f_1, t_i (active/frequency/type)
|
||||
@@ -260,7 +295,7 @@ const char *handleQRGPost(AsyncWebServerRequest *request) {
|
||||
const char *tstr = tstring.c_str();
|
||||
const char *sstr = sstring.c_str();
|
||||
Serial.printf("Processing a=%s, f=%s, t=%s, site=%s\n", active ? "YES" : "NO", fstr, tstr, sstr);
|
||||
char typech = (tstr[2] == '4' ? '4' : tstr[2] == '9' ? 'R' : tstr[0] == 'M' ? 'M' : tstr[3]); // a bit ugly
|
||||
char typech = tstr[0];
|
||||
file.printf("%3.3f %c %c %s\n", atof(fstr), typech, active ? '+' : '-', sstr);
|
||||
}
|
||||
file.close();
|
||||
@@ -310,7 +345,6 @@ void setupWifiList() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *createWIFIForm() {
|
||||
char *ptr = message;
|
||||
char tmp[4];
|
||||
@@ -374,7 +408,7 @@ void addSondeStatus(char *ptr, int i)
|
||||
strcat(ptr, "<table>");
|
||||
sprintf(ptr + strlen(ptr), "<tr><td id=\"sfreq\">%3.3f MHz, Type: %s</td><tr><td>ID: %s", s->freq, sondeTypeStr[s->type],
|
||||
s->validID ? s->id : "<?""?>");
|
||||
if (s->validID && (s->type == STYPE_DFM06 || s->type == STYPE_DFM09 || s->type == STYPE_M10)) {
|
||||
if (s->validID && (s->type == STYPE_DFM || s->type == STYPE_DFM06 || s->type == STYPE_DFM09 || s->type == STYPE_M10 || s->type == STYPE_M20)) {
|
||||
sprintf(ptr + strlen(ptr), " (ser: %s)", s->ser);
|
||||
}
|
||||
sprintf(ptr + strlen(ptr), "</td></tr><tr><td>QTHSonde: %.6f,%.6f h=%.0fm</td></tr>\n", s->lat, s->lon, s->alt);
|
||||
@@ -387,9 +421,11 @@ void addSondeStatus(char *ptr, int i)
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>Burst-KT=%d Launch-KT=%d Countdown=%d (vor %ds)</td></tr>\n",
|
||||
s->burstKT, s->launchKT, s->countKT, ((uint16_t)s->frame - s->crefKT));
|
||||
}
|
||||
sprintf(ptr + strlen(ptr), "<tr><td><a target=\"_empty\" href=\"geo:%.6f,%.6f\">GEO-App</a> - ", s->lat, s->lon);
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>");
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.fr/maps/dir//%.6f,%.6f\">G-MAPS</a> - ", s->lat, s->lon);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=%.6f,%.6f&q=RS_%s\">Sondehub</a> - ", s->lat, s->lon, s->id);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a></td></tr>", s->lat, s->lon);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a> - ", s->lat, s->lon);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.com/maps/search/?api=1&query=%.6f,%.6f\">Google</a></td></tr>", s->lat, s->lon);
|
||||
strcat(ptr, "</table><p/>\n");
|
||||
}
|
||||
|
||||
@@ -435,7 +471,7 @@ struct st_configitems config_list[] = {
|
||||
|
||||
/* General config settings */
|
||||
{"", "Software configuration", -5, NULL},
|
||||
{"wifi", "Wifi mode (0/1/2/3)", 0, &sonde.config.wifi},
|
||||
{"wifi", "Wifi mode (0[off]/1[On]/2[Apmode]/3[Scan])", 0, &sonde.config.wifi},
|
||||
{"debug", "Debug mode (0/1)", 0, &sonde.config.debug},
|
||||
{"maxsonde", "Maxsonde", 0, &sonde.config.maxsonde},
|
||||
{"display", "Display screens (scan,default,...)", -6, sonde.config.display},
|
||||
@@ -461,7 +497,7 @@ struct st_configitems config_list[] = {
|
||||
{"call", "Call", 8, sonde.config.call},
|
||||
{"passcode", "Passcode", 8, sonde.config.passcode},
|
||||
/* KISS tnc settings */
|
||||
{"kisstnc.active", "KISS TNC (port 14590) (needs reboot)", 0, &sonde.config.kisstnc.active},
|
||||
{"kisstnc.active", "KISS TNC (1 active) (needs reboot)", 0, &sonde.config.kisstnc.active},
|
||||
{"kisstnc.idformat", "KISS TNC ID Format", -2, &sonde.config.kisstnc.idformat},
|
||||
/* AXUDP settings */
|
||||
{"axudp.active", "AXUDP active", -3, &sonde.config.udpfeed.active},
|
||||
@@ -475,6 +511,17 @@ struct st_configitems config_list[] = {
|
||||
{"tcp.port", "APRS TCP Port", 0, &sonde.config.tcpfeed.port},
|
||||
{"tcp.idformat", "DFM ID Format", -2, &sonde.config.tcpfeed.idformat},
|
||||
{"tcp.highrate", "Rate limit", 0, &sonde.config.tcpfeed.highrate},
|
||||
/* Sondehub v2 settings */
|
||||
{"", "Sondehub v2 settings", -5, NULL},
|
||||
{"sondehub.active", "Sondehub reporting active", 0, &sonde.config.sondehub.active},
|
||||
{"sondehub.chase", "Sondehub chase location active", 0, &sonde.config.sondehub.chase},
|
||||
{"sondehub.host", "Sondehub host", 63, &sonde.config.sondehub.host},
|
||||
{"sondehub.callsign", "Callsign", 63, &sonde.config.sondehub.callsign},
|
||||
{"sondehub.lat", "Latitude", 19, &sonde.config.sondehub.lat},
|
||||
{"sondehub.lon", "Longitude", 19, &sonde.config.sondehub.lon},
|
||||
{"sondehub.alt", "Altitude", 19, &sonde.config.sondehub.alt},
|
||||
{"sondehub.antenna", "Antenna", 63, &sonde.config.sondehub.antenna},
|
||||
{"sondehub.email", "Sondehub email", 63, &sonde.config.sondehub.email},
|
||||
/* Hardware dependeing settings */
|
||||
{"", "Hardware configuration (requires reboot)", -5, NULL},
|
||||
{"disptype", "Display type (0=OLED/SSD1306, 1=TFT/ILI9225, 2=OLED/SH1106)", 0, &sonde.config.disptype},
|
||||
@@ -483,7 +530,7 @@ struct st_configitems config_list[] = {
|
||||
{"oled_scl", "OLED SCL CLK", 0, &sonde.config.oled_scl},
|
||||
{"oled_rst", "OLED RST (needs reboot)", 0, &sonde.config.oled_rst},
|
||||
{"oled_orient", "Oled orientation default 1, OLED flip: 3", 0, &sonde.config.oled_orient},
|
||||
{"button_pin", "Button input port", -4, &sonde.config.button_pin},
|
||||
{"button_pin", "Button input port (38)", -4, &sonde.config.button_pin},
|
||||
{"button2_pin", "Button 2 input port", -4, &sonde.config.button2_pin},
|
||||
{"button2_axp", "Use AXP192 PWR as Button 2", 0, &sonde.config.button2_axp},
|
||||
{"touch_thresh", "Touch button threshold<br>(0 for calib mode)", 0, &sonde.config.touch_thresh},
|
||||
@@ -500,7 +547,7 @@ struct st_configitems config_list[] = {
|
||||
{"vbatmin", "VBat Min", 5, sonde.config.vbatmin},
|
||||
{"telemetryOn", "Telemetry (0=Disabled, 1 Enable)", 0, &sonde.config.telemetryOn},
|
||||
{"buzzerOn", "Buzzer (0=Disabled, 1 Enable)", 0, &sonde.config.buzzerOn},
|
||||
{"buzzerPort", "Buzzer Port (12)", 0, &sonde.config.buzzerPort},
|
||||
{"buzzerPort", "Buzzer Port (25)", 0, &sonde.config.buzzerPort},
|
||||
{"buzzerFreq", "Buzzer Frequency (700)", 0, &sonde.config.buzzerFreq},
|
||||
{"dbsmetre", "dB = 0 / Smetre =1)", 0, &sonde.config.dbsmetre},
|
||||
{"degdec", "Lat,Lon Decimal =0 ou Degres =1)", 0, &sonde.config.degdec},
|
||||
@@ -1103,7 +1150,11 @@ char buffer[85];
|
||||
MicroNMEA nmea(buffer, sizeof(buffer));
|
||||
|
||||
int lastCourse = 0;
|
||||
void unkHandler(const MicroNMEA& nmea) {
|
||||
|
||||
/// Arrg. MicroNMEA changes type definition... so lets auto-infer type
|
||||
template<typename T>
|
||||
//void unkHandler(const MicroNMEA& nmea) {
|
||||
void unkHandler(T nmea) {
|
||||
if (strcmp(nmea.getMessageID(), "VTG") == 0) {
|
||||
const char *s = nmea.getSentence();
|
||||
while (*s && *s != ',') s++;
|
||||
@@ -1113,6 +1164,7 @@ void unkHandler(const MicroNMEA& nmea) {
|
||||
Serial.printf("Course update: %d\n", lastCourse);
|
||||
}
|
||||
}
|
||||
|
||||
void gpsTask(void *parameter) {
|
||||
nmea.setUnknownSentenceHandler(unkHandler);
|
||||
|
||||
@@ -1136,10 +1188,31 @@ void gpsTask(void *parameter) {
|
||||
}
|
||||
}
|
||||
|
||||
#define UBX_SYNCH_1 0xB5
|
||||
#define UBX_SYNCH_2 0x62
|
||||
uint8_t ubx_set9k6[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F};
|
||||
uint8_t ubx_factorydef[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0x17, 0x8A };
|
||||
|
||||
void initGPS() {
|
||||
if (sonde.config.gps_rxd < 0) return; // GPS disabled
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
|
||||
if (sonde.config.gps_txd >= 0) { // TX enable, thus try setting baud to 9600 and do a factory reset
|
||||
Serial.println("Trying to reset GPS...");
|
||||
Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_factorydef, sizeof(ubx_factorydef));
|
||||
delay(1000);
|
||||
} else {
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
}
|
||||
xTaskCreate( gpsTask, "gpsTask",
|
||||
5000, /* stack size */
|
||||
NULL, /* paramter */
|
||||
@@ -1457,6 +1530,7 @@ void setup()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Serial.println("Reading initial configuration");
|
||||
setupConfigData(); // configuration must be read first due to OLED ports!!!
|
||||
|
||||
@@ -1663,10 +1737,10 @@ const int melodie[][3] = { {4, 2, 2}, {5, 2, 1}, {7, 2, 3}, {0, 3, 6},
|
||||
setupChannelList();
|
||||
#if 0
|
||||
sonde.clearSonde();
|
||||
sonde.addSonde(402.700, STYPE_RS41);
|
||||
sonde.addSonde(405.700, STYPE_RS41);
|
||||
sonde.addSonde(405.900, STYPE_RS41);
|
||||
sonde.addSonde(403.450, STYPE_DFM09);
|
||||
sonde.addSonde(402.800, STYPE_RS41);
|
||||
sonde.addSonde(402.000, STYPE_M10);
|
||||
sonde.addSonde(404.800, STYPE_RS41);
|
||||
sonde.addSonde(403.000, STYPE_DFM09);
|
||||
Serial.println("No channel config file, using defaults!");
|
||||
Serial.println();
|
||||
#endif
|
||||
@@ -1780,22 +1854,24 @@ void loopDecoder() {
|
||||
Serial.printf("current main is %d, current rxtask is %d\n", sonde.currentSonde, rxtask.currentSonde);
|
||||
}
|
||||
|
||||
if (!tncclient.connected()) {
|
||||
Serial.println("TNC client not connected");
|
||||
tncclient = tncserver.available();
|
||||
if (tncclient.connected()) {
|
||||
Serial.println("new TCP KISS connection");
|
||||
//APRSIs
|
||||
APRS_IS aprs_is(sonde.config.call, sonde.config.passcode, "RadioSonde", version_id);
|
||||
if(!aprs_is.connected())
|
||||
{
|
||||
Serial.print("connecting to server: ");
|
||||
Serial.print(sonde.config.tcpfeed.host);
|
||||
Serial.print(" on port: ");
|
||||
Serial.println(sonde.config.tcpfeed.port);
|
||||
if(!aprs_is.connect(sonde.config.tcpfeed.host, sonde.config.tcpfeed.port, "-t/po"))
|
||||
{
|
||||
Serial.println("Connection failed.");
|
||||
Serial.println("Waiting 5 seconds before retrying...");
|
||||
delay(5000);
|
||||
return;
|
||||
}
|
||||
Serial.println("Connected to server!");
|
||||
}
|
||||
if (tncclient.available()) {
|
||||
Serial.print("TCP KISS socket: recevied ");
|
||||
while (tncclient.available()) {
|
||||
Serial.print(tncclient.read()); // Check if we receive anything from from APRSdroid
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
// wifi (axudp) or bluetooth (bttnc) active => send packet
|
||||
if ((res & 0xff) == 0 && (connected || tncclient.connected() )) {
|
||||
if ((res & 0xff) == 0) {
|
||||
//Send a packet with position information
|
||||
// first check if ID and position lat+lonis ok
|
||||
SondeInfo *s = &sonde.sondeList[rxtask.receiveSonde];
|
||||
@@ -1810,17 +1886,25 @@ void loopDecoder() {
|
||||
udp.write((const uint8_t *)raw, rawlen);
|
||||
udp.endPacket();
|
||||
}
|
||||
if (tncclient.connected()) {
|
||||
if (aprs_is.connected()) {
|
||||
Serial.println("Sending position via TCP");
|
||||
char raw[201];
|
||||
int rawlen = aprsstr_mon2kiss(str, raw, APRS_MAXLEN);
|
||||
Serial.print("sending: "); Serial.println(raw);
|
||||
tncclient.write(raw, rawlen);
|
||||
aprs_is.sendMessage(str);
|
||||
aprs_is.sendMessage(raw);
|
||||
}
|
||||
} else {
|
||||
sondehub_finish_data(&shclient, s, &sonde.config.sondehub);
|
||||
}
|
||||
// also send to web socket
|
||||
//TODO
|
||||
|
||||
// Sonde hub
|
||||
if (sonde.config.sondehub.active) {
|
||||
sondehub_send_data(&shclient, s, &sonde.config.sondehub);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Serial.println("updateDisplay started");
|
||||
if (forceReloadScreenConfig) {
|
||||
disp.initFromFile();
|
||||
@@ -1909,15 +1993,26 @@ String translateEncryptionType(wifi_auth_mode_t encryptionType) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum t_wifi_state { WIFI_DISABLED, WIFI_SCAN, WIFI_CONNECT, WIFI_CONNECTED, WIFI_APMODE };
|
||||
|
||||
static t_wifi_state wifi_state = WIFI_DISABLED;
|
||||
|
||||
|
||||
void enableNetwork(bool enable) {
|
||||
if (enable) {
|
||||
MDNS.begin(sonde.config.mdnsname);
|
||||
SetupAsyncServer();
|
||||
udp.begin(WiFi.localIP(), LOCALUDPPORT);
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService("kisstnc", "tcp", 14570);
|
||||
if (sonde.config.kisstnc.active) {
|
||||
tncserver.begin();
|
||||
}
|
||||
if (sonde.config.sondehub.active && wifi_state != WIFI_APMODE) {
|
||||
sondehub_station_update(&shclient, &sonde.config.sondehub);
|
||||
time_last_update = millis();
|
||||
}
|
||||
connected = true;
|
||||
} else {
|
||||
MDNS.end();
|
||||
@@ -1926,10 +2021,6 @@ void enableNetwork(bool enable) {
|
||||
}
|
||||
|
||||
|
||||
enum t_wifi_state { WIFI_DISABLED, WIFI_SCAN, WIFI_CONNECT, WIFI_CONNECTED, WIFI_APMODE };
|
||||
|
||||
static t_wifi_state wifi_state = WIFI_DISABLED;
|
||||
|
||||
// Events used only for debug output right now
|
||||
void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
@@ -2417,28 +2508,14 @@ void execOTA() {
|
||||
// execOTA();
|
||||
}
|
||||
|
||||
/*
|
||||
//ICI Update Data
|
||||
/*
|
||||
|
||||
// Update DATAWeb!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//Data1 index.html
|
||||
//Data data.tar
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb,"index.html");
|
||||
//Data2 sondemap.html
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb2,"sondemap.html");
|
||||
//Data3 config.txt
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb3,"config.txt");
|
||||
//Data4 screens.txt
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb4,"screens.txt");
|
||||
//Data5 styles.css
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb5,"style.css");
|
||||
//Data6 qrg.txt
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb6,"qrg.txt");
|
||||
//Data7 networks.txt
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb7,"networks.txt");
|
||||
//Data8 fontawesome-webfont.eot
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb8,"fontawesome-webfont.eot");
|
||||
//Data9 fontawesome-webfont.eot
|
||||
updatedataweb(updateHost,updatePort,updateDataWeb9,"fontawesome-webfont.woff");
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
if (Update.end()) {
|
||||
Serial.println("OTA done!");
|
||||
@@ -2525,6 +2602,8 @@ void updatedataweb(String host, int port, String dataweb, String fichier){
|
||||
file.close();
|
||||
}
|
||||
client.flush();
|
||||
//Untar data.tar
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@@ -2567,5 +2646,359 @@ void loop() {
|
||||
lastDisplay = currentDisplay;
|
||||
}
|
||||
Serial.printf("Unused stack: %d\n", uxTaskGetStackHighWaterMark(0));
|
||||
|
||||
//transfert_sd();
|
||||
|
||||
//SondHub
|
||||
if (sonde.config.sondehub.active) {
|
||||
unsigned long time_now = millis();
|
||||
// time_delta will be correct, even if time_now overflows
|
||||
unsigned long time_delta = time_now - time_last_update;
|
||||
if ((sonde.config.sondehub.chase == 0) && (time_delta >= SONDEHUB_STATION_UPDATE_TIME) && (wifi_state != WIFI_APMODE)) { // 60 min
|
||||
sondehub_station_update(&shclient, &sonde.config.sondehub);
|
||||
time_last_update = time_now;
|
||||
}
|
||||
else if ((sonde.config.sondehub.chase == 1) && (time_delta >= SONDEHUB_MOBILE_STATION_UPDATE_TIME) && (wifi_state != WIFI_APMODE)) { // 30 sec
|
||||
sondehub_station_update(&shclient, &sonde.config.sondehub);
|
||||
time_last_update = time_now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sondehub v2 DB related codes
|
||||
/*
|
||||
Update station data to the sondehub v2 DB
|
||||
*/
|
||||
void sondehub_station_update(WiFiClient *client, struct st_sondehub *conf) {
|
||||
#define STATION_DATA_LEN 300
|
||||
char data[STATION_DATA_LEN];
|
||||
char *w;
|
||||
|
||||
Serial.println("sondehub_station_update()");
|
||||
|
||||
if (!client->connected()) {
|
||||
if (!client->connect(conf->host, 80)) {
|
||||
Serial.println("Connection FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
w = data;
|
||||
memset(w, 0, STATION_DATA_LEN);
|
||||
|
||||
sprintf(w,
|
||||
"{"
|
||||
"\"software_name\": \"%s\","
|
||||
"\"software_version\": \"%s\","
|
||||
"\"uploader_callsign\": \"%s\","
|
||||
"\"uploader_contact_email\": \"%s\",",
|
||||
version_name, version_id, conf->callsign, conf->email);
|
||||
w += strlen(w);
|
||||
if ((conf->chase == 0) && (conf->lat[0] != '\0') && (conf->lon[0] != '\0')) {
|
||||
if (conf->alt[0] != '\0') {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%s,%s,%s],"
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
conf->lat, conf->lon, conf->alt, conf->antenna);
|
||||
} else {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%s,%s,null],"
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
conf->lat, conf->lon, conf->antenna);
|
||||
}
|
||||
}
|
||||
else if (sonde.config.gps_Valid && sonde.config.gps_Lat != 0 && sonde.config.gps_Lon != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%d],"
|
||||
"\"uploader_antenna\": \"%s\","
|
||||
"\"mobile\": true"
|
||||
"}",
|
||||
sonde.config.gps_Lat, sonde.config.gps_Lon, sonde.config.gps_Alt, conf->antenna);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
client->println("PUT /listeners HTTP/1.1");
|
||||
client->print("Host: ");
|
||||
client->println(conf->host);
|
||||
client->println("accept: text/plain");
|
||||
client->println("Content-Type: application/json");
|
||||
client->print("Content-Length: ");
|
||||
client->println(strlen(data));
|
||||
client->println();
|
||||
client->println(data);
|
||||
Serial.println(strlen(data));
|
||||
Serial.println(data);
|
||||
Serial.println("Waiting for response");
|
||||
String response = client->readString();
|
||||
Serial.println(response);
|
||||
Serial.println("Response done...");
|
||||
//client->stop();r
|
||||
}
|
||||
|
||||
/*
|
||||
Update sonde data to the sondehub v2 DB
|
||||
*/
|
||||
enum SHState { SH_DISCONNECTED, SH_CONNECTING, SH_CONN_IDLE, SH_CONN_APPENDING, SH_CONN_WAITACK };
|
||||
|
||||
SHState shState = SH_DISCONNECTED;
|
||||
time_t shStart = 0;
|
||||
|
||||
/* Sonde.h: enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_DFM }; */
|
||||
const char *sondeTypeStrSH[NSondeTypes] = { "DFM", "DFM", "RS41", "RS92", "M10", "M20", "DFM" };
|
||||
const char *dfmSubtypeStrSH[16] = { NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
"DFM06", // 0x06
|
||||
"PS15", // 0x07
|
||||
NULL, NULL,
|
||||
"DFM09", // 0x0A
|
||||
"DFM17", // 0x0B
|
||||
"DFM09P", // 0x0C
|
||||
"DFM17", // 0x0D
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
// in hours.... max allowed diff UTC <-> sonde time
|
||||
#define SONDEHUB_TIME_THRESHOLD (3)
|
||||
void sondehub_send_data(WiFiClient *client, SondeInfo *s, struct st_sondehub *conf) {
|
||||
Serial.println("sondehub_send_data()");
|
||||
Serial.printf("shState = %d\n", shState);
|
||||
|
||||
// max age of data in JSON request (in seconds)
|
||||
#define SONDEHUB_MAXAGE 15
|
||||
|
||||
#define MSG_SIZE 550
|
||||
char rs_msg[MSG_SIZE];
|
||||
char *w;
|
||||
struct tm ts;
|
||||
// For DFM, s->time is data from subframe DAT8 (gps date/hh/mm), and sec is from DAT1 (gps sec/usec)
|
||||
// For all others, sec should always be 0 and time the exact time in seconds
|
||||
time_t t = s->time + s->sec;
|
||||
|
||||
while (client->available() > 0) {
|
||||
// data is available from remote server, process it...
|
||||
int cnt = client->readBytesUntil('\n', rs_msg, MSG_SIZE);
|
||||
rs_msg[cnt] = 0;
|
||||
Serial.println(rs_msg);
|
||||
// If something that looks like a valid HTTP response is received, we are ready to send the next data item
|
||||
if (shState == SH_CONN_WAITACK && cnt > 11 && strncmp(rs_msg, "HTTP/1", 6) == 0) {
|
||||
shState = SH_CONN_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
struct tm timeinfo;
|
||||
time_t now;
|
||||
time(&now);
|
||||
gmtime_r(&now, &timeinfo);
|
||||
if (timeinfo.tm_year <= (2016 - 1900)) {
|
||||
Serial.println("Failed to obtain time");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if current sonde data is valid. If not, don't do anything....
|
||||
if (*s->ser == 0) return; // Don't send anything without serial number
|
||||
if (((int)s->lat == 0) && ((int)s->lon == 0)) return; // Sometimes these values are zeroes. Don't send those to the sondehub
|
||||
if ((int)s->alt > 50000) return; // If alt is too high don't send to SondeHub
|
||||
// M20 data does not include #sat information
|
||||
if ( s->type != STYPE_M20 && (int)s->sats < 4) return; // If not enough sats don't send to SondeHub
|
||||
|
||||
// If not connected to sondehub, try reconnecting.
|
||||
// TODO: do this outside of main loop
|
||||
if (!client->connected()) {
|
||||
Serial.println("NO CONNECTION");
|
||||
shState = SH_DISCONNECTED;
|
||||
if (!client->connect(conf->host, 80)) {
|
||||
Serial.println("Connection FAILED");
|
||||
return;
|
||||
}
|
||||
client->Client::setTimeout(0); // does this work?
|
||||
shState = SH_CONN_IDLE;
|
||||
}
|
||||
|
||||
if ( shState == SH_CONN_WAITACK ) {
|
||||
Serial.println("Previous SH-frame not yet ack'ed, not sending new data");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( abs(now - (time_t)s->time) > (3600 * SONDEHUB_TIME_THRESHOLD) ) {
|
||||
Serial.printf("Sonde time %d too far from current UTC time %ld", s->time, now);
|
||||
return;
|
||||
}
|
||||
|
||||
// DFM uses UTC. Most of the other radiosondes use GPS time
|
||||
// SondeHub expect datetime to be the same time sytem as the sonde transmits as time stamp
|
||||
if ( s->type == STYPE_RS41 || s->type == STYPE_RS92 || s->type == STYPE_M20 ) {
|
||||
t += 18; // convert back to GPS time from UTC time +18s
|
||||
}
|
||||
|
||||
gmtime_r(&t, &ts);
|
||||
|
||||
memset(rs_msg, 0, MSG_SIZE);
|
||||
w = rs_msg;
|
||||
|
||||
sprintf(w,
|
||||
" {"
|
||||
"\"software_name\": \"%s\","
|
||||
"\"software_version\": \"%s\","
|
||||
"\"uploader_callsign\": \"%s\","
|
||||
"\"time_received\": \"%04d-%02d-%02dT%02d:%02d:%02d.000Z\","
|
||||
"\"manufacturer\": \"%s\","
|
||||
"\"serial\": \"%s\","
|
||||
"\"datetime\": \"%04d-%02d-%02dT%02d:%02d:%02d.000Z\","
|
||||
"\"lat\": %.6f,"
|
||||
"\"lon\": %.6f,"
|
||||
"\"alt\": %.3f,"
|
||||
"\"frequency\": %.3f,"
|
||||
"\"vel_h\": %.3f,"
|
||||
"\"vel_v\": %.3f,"
|
||||
"\"heading\": %.3f,"
|
||||
"\"rssi\": %.1f,",
|
||||
version_name, version_id, conf->callsign,
|
||||
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
||||
manufacturer_string[s->type], s->ser,
|
||||
ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec + s->sec,
|
||||
(float)s->lat, (float)s->lon, (float)s->alt, (float)s->freq, (float)s->hs, (float)s->vs,
|
||||
(float)s->dir, -((float)s->rssi / 2)
|
||||
);
|
||||
w += strlen(w);
|
||||
|
||||
if (s->type != STYPE_M20) {
|
||||
sprintf(w, "\"sats\": %d,", (int)s->sats);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
if ( TYPE_IS_DFM(s->type) || TYPE_IS_METEO(s->type) ) {
|
||||
// send frame as gps timestamp for these sonde, identical to autorx
|
||||
// For M10, this is real GPS time (seconds since Jqn 6 1980, without adjusting for leap seconds)
|
||||
// DFM and MP3H send real UTC (with leap seconds considered), so for them the frame number actually
|
||||
// is gps time plus number of leap seconds since the beginning of GPS time.
|
||||
int frame = (int)(t - 315964800);
|
||||
if (s->type == STYPE_M10) {
|
||||
frame += 18;
|
||||
};
|
||||
sprintf(w, "\"frame\": %d,", frame);
|
||||
} else {
|
||||
sprintf(w, "\"frame\": %d,", s->frame);
|
||||
}
|
||||
w += strlen(w);
|
||||
|
||||
sprintf(w, "\"type\": \"%s\",", sondeTypeStrSH[s->type]);
|
||||
w += strlen(w);
|
||||
|
||||
/* if there is a subtype (DFM only) */
|
||||
if ( TYPE_IS_DFM(s->type) && s->subtype > 0 && s->subtype < 16 ) {
|
||||
const char *t = dfmSubtypeStrSH[s->subtype];
|
||||
// as in https://github.com/projecthorus/radiosonde_auto_rx/blob/e680221f69a568e1fdb24e76db679233f32cb027/auto_rx/autorx/sonde_specific.py#L84
|
||||
if (t) sprintf(w, "\"subtype\": \"%s\",", t);
|
||||
else sprintf(w, "\"subtype\": \"DFMx%X\",", s->subtype); // Unknown subtype
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
if (((int)s->temperature != 0) && ((int)s->relativeHumidity != 0)) {
|
||||
sprintf(w,
|
||||
"\"temp\": %.1f,"
|
||||
"\"humidity\": %.1f,",
|
||||
float(s->temperature), float(s->relativeHumidity)
|
||||
);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
if ((conf->chase == 0) && (conf->lat[0] != '\0') && (conf->lon[0] != '\0')) {
|
||||
if (conf->alt[0] != '\0') {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%s,%s,%s],"
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
conf->lat, conf->lon, conf->alt, conf->antenna
|
||||
);
|
||||
} else {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%s,%s,null],"
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
conf->lat, conf->lon, conf->antenna
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (sonde.config.gps_Valid && sonde.config.gps_Lat != 0 && sonde.config.gps_Lon != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%d],"
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
sonde.config.gps_Lat, sonde.config.gps_Lon, sonde.config.gps_Alt, conf->antenna
|
||||
);
|
||||
}
|
||||
else {
|
||||
sprintf(w,
|
||||
"\"uploader_antenna\": \"%s\""
|
||||
"}",
|
||||
conf->antenna
|
||||
);
|
||||
}
|
||||
if (shState != SH_CONN_APPENDING) {
|
||||
sondehub_send_header(client, s, conf);
|
||||
sondehub_send_next(client, s, conf, rs_msg, strlen(rs_msg), 1);
|
||||
shState = SH_CONN_APPENDING;
|
||||
shStart = now;
|
||||
} else {
|
||||
sondehub_send_next(client, s, conf, rs_msg, strlen(rs_msg), 0);
|
||||
}
|
||||
if (now - shStart > SONDEHUB_MAXAGE) { // after MAXAGE seconds
|
||||
sondehub_send_last(client, s, conf);
|
||||
shState = SH_CONN_WAITACK;
|
||||
shStart = 0;
|
||||
}
|
||||
//client->println(rs_msg);
|
||||
//Serial.println(rs_msg);
|
||||
//String response = client->readString();
|
||||
//Serial.println(response);
|
||||
}
|
||||
|
||||
void sondehub_finish_data(WiFiClient *client, SondeInfo *s, struct st_sondehub *conf) {
|
||||
// If there is an "old" pending collection of JSON data sets, send it even if no now data is received
|
||||
if (shState == SH_CONN_APPENDING) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (now - shStart > SONDEHUB_MAXAGE + 3) { // after MAXAGE seconds
|
||||
sondehub_send_last(client, s, conf);
|
||||
shState = SH_CONN_WAITACK;
|
||||
shStart = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sondehub_send_header(WiFiClient *client, SondeInfo *s, struct st_sondehub *conf) {
|
||||
Serial.print("PUT /sondes/telemetry HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
Serial.println(conf->host);
|
||||
Serial.println("accept: text/plain\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
client->print("PUT /sondes/telemetry HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
client->println(conf->host);
|
||||
client->println("accept: text/plain\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
}
|
||||
void sondehub_send_next(WiFiClient *client, SondeInfo *s, struct st_sondehub *conf, char *chunk, int chunklen, int first) {
|
||||
// send next chunk of JSON request
|
||||
client->printf("%x\r\n", chunklen + 1);
|
||||
client->write(first ? "[" : ",", 1);
|
||||
client->write(chunk, chunklen);
|
||||
client->print("\r\n");
|
||||
|
||||
Serial.printf("%x\r\n", chunklen + 1);
|
||||
Serial.write(first ? "[" : ",", 1);
|
||||
Serial.write(chunk, chunklen);
|
||||
Serial.print("\r\n");
|
||||
}
|
||||
void sondehub_send_last(WiFiClient *client, SondeInfo *s, struct st_sondehub *conf) {
|
||||
// last chunk. just the closing "]" of the json request
|
||||
client->printf("1\r\n]\r\n0\r\n\r\n");
|
||||
Serial.printf("1\r\n]\r\n0\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#oled_rst=16
|
||||
oled_orient=1
|
||||
gpsOn=0
|
||||
gps_rxd=-1
|
||||
gps_txd=-1
|
||||
gps_rxd=34
|
||||
gps_txd=12
|
||||
gps_lat=43.59169
|
||||
gps_lon=7.10071
|
||||
gps_alt=123
|
||||
@@ -54,7 +54,7 @@ vbatmax=1.86
|
||||
vbatmin=1.64
|
||||
telemetryOn=0
|
||||
buzzerOn=0
|
||||
buzzerPort=12
|
||||
buzzerPort=25
|
||||
buzzerFreq=700
|
||||
dbsmetre=1
|
||||
gainLNA=0
|
||||
@@ -90,8 +90,8 @@ dfm.rxbw=10400
|
||||
# local use only, do not feed to public services
|
||||
# data not sanitized / quality checked, outliers not filtered out
|
||||
axudp.active=1
|
||||
axudp.host=fra1od.fr.to
|
||||
axudp.port=14580
|
||||
axudp.host=fra1od.freeboxos.fr
|
||||
axudp.port=20350
|
||||
axudp.symbol=/O
|
||||
axudp.highrate=1
|
||||
axudp.idformat=0
|
||||
@@ -99,12 +99,25 @@ axudp.idformat=0
|
||||
# maybe some time in the future
|
||||
#-------------------------------#
|
||||
# currently simply not implemented, no need to put anything here anyway
|
||||
tcp.active=0
|
||||
tcp.host=radiosondy.info
|
||||
tcp.port=14590
|
||||
tcp.active=1
|
||||
tcp.host=fra1od.freeboxos.fr
|
||||
tcp.port=14580
|
||||
tcp.symbol=/O
|
||||
tcp.highrate=20
|
||||
tcp.idformat=0
|
||||
#-------------------------------#
|
||||
# Sondehub v2 settings
|
||||
#-------------------------------#
|
||||
# Sondehub v2 DB settings
|
||||
sondehub.active=0
|
||||
sondehub.chase=0
|
||||
sondehub.host=api.v2.sondehub.org
|
||||
sondehub.callsign=FRS2013
|
||||
sondehub.lat=
|
||||
sondehub.lon=
|
||||
sondehub.alt=
|
||||
sondehub.antenna=CJU 403Mhz
|
||||
sondehub.email=14frs2013@free.fr
|
||||
#-------------------------------#
|
||||
# EOF
|
||||
#-------------------------------#
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<br>
|
||||
Autodetect info: %AUTODETECT_INFO%<br>
|
||||
<br>
|
||||
CopyLeft 2020 Modifier par <a href="http://openpmr.cla.fr">FRS2013</a><br>
|
||||
CopyLeft 2021 Modifier par <a href="http://openpmr.cla.fr">FRS2013</a> & Vigor<br>
|
||||
Licence GNU GPL <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
|
||||
for details
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Frequency in Mhz (format nnn.nnn)
|
||||
# Type (4=RS41, R=RS92, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09, M=M10)
|
||||
# Type (4=RS41, R=RS92, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09, D=DFM, M=M10, 2=M20)
|
||||
#
|
||||
400.000 9 + Test(FR)
|
||||
401.500 4 - Santander(ES)
|
||||
@@ -20,5 +20,5 @@
|
||||
405.900 4 - Bergen_2
|
||||
405.100 4 - Meppen_2
|
||||
405.300 4 - Essen
|
||||
405.500 4 - Essen_2
|
||||
405.500 2 - Essen_2
|
||||
# end
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
const char *version_name = "RadioSonde";
|
||||
const char *version_id = "0.8.8";
|
||||
//const int SPIFFS_MAJOR=3;
|
||||
//const int SPIFFS_MINOR=5;
|
||||
const char *version_id = "0.9.2";
|
||||
|
||||
@@ -194,7 +194,7 @@ uint8_t SX1278FSK::setFSK()
|
||||
{
|
||||
uint8_t state = 2;
|
||||
byte st0;
|
||||
byte config1;
|
||||
//byte config1;
|
||||
|
||||
#if (SX1278FSK_debug_mode > 1)
|
||||
Serial.println();
|
||||
|
||||
+320
-10
@@ -1,4 +1,3 @@
|
||||
|
||||
/* DFM decoder functions */
|
||||
#include "DFM.h"
|
||||
#include "SX1278FSK.h"
|
||||
@@ -12,11 +11,26 @@
|
||||
#define DFM_DBG(x)
|
||||
#endif
|
||||
|
||||
int DFM::setup(float frequency, int inv)
|
||||
#define DFM_FRAMELEN 33
|
||||
|
||||
// single data structure, search restarts after decoder change
|
||||
static struct st_dfmstat {
|
||||
int idcnt0;
|
||||
int idcnt1;
|
||||
int lastfrid;
|
||||
int lastfrcnt;
|
||||
uint8_t start[50];
|
||||
uint16_t dat[50*2];
|
||||
uint8_t cnt[50*2];
|
||||
uint8_t nameregok;
|
||||
uint8_t nameregtop;
|
||||
} dfmstate;
|
||||
|
||||
int DFM::setup(float frequency, int type)
|
||||
{
|
||||
inverse = inv;
|
||||
stype = type;
|
||||
#if DFM_DEBUG
|
||||
Serial.printf("Setup sx1278 for DFM sonde (inv=%d)\n",inv);
|
||||
Serial.printf("Setup sx1278 for DFM sonde (type=%d)\n", stype);
|
||||
#endif
|
||||
if(sx1278.ON()!=0) {
|
||||
DFM_DBG(Serial.println("Setting SX1278 power on FAILED"));
|
||||
@@ -44,6 +58,10 @@ int DFM::setup(float frequency, int inv)
|
||||
DFM_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.dfm.rxbw));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(type == STYPE_DFM09 || type == STYPE_DFM06) {
|
||||
// packet mode, old version, misses some frames because chip enables rx too late after
|
||||
// one frame was recevied. TODO: check if this can be fixed by changing parameters
|
||||
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
|
||||
if(sx1278.setRxConf(0x1E)!=0) {
|
||||
DFM_DBG(Serial.println("Setting RX Config FAILED"));
|
||||
@@ -52,12 +70,13 @@ int DFM::setup(float frequency, int inv)
|
||||
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
|
||||
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
|
||||
|
||||
const char *SYNC=inverse?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA";
|
||||
const char *SYNC=(stype==STYPE_DFM09)?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA";
|
||||
if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) {
|
||||
DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
//if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
if(sx1278.setPreambleDetect(0xAA)!=0) {
|
||||
DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
|
||||
return 1;
|
||||
}
|
||||
@@ -68,11 +87,41 @@ int DFM::setup(float frequency, int inv)
|
||||
DFM_DBG(Serial.println("Setting Packet config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
|
||||
} else {
|
||||
// continuous mode
|
||||
// Enable auto-AFC, auto-AGC, RX Trigger by preamble ????
|
||||
if(sx1278.setRxConf(0x1E)!=0) {
|
||||
DFM_DBG(Serial.println("Setting RX Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
// working with continuous RX
|
||||
const char *SYNC="\xAA\xAA";
|
||||
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
|
||||
DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
//if(sx1278.setPreambleDetect(0x9F)!=0) {
|
||||
DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
|
||||
DFM_DBG(Serial.println("Setting Packet config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
sx1278.setPayloadLength(0); // infinite for now...
|
||||
}
|
||||
Serial.print("DFM: setting RX frequency to ");
|
||||
Serial.println(frequency);
|
||||
|
||||
int retval = sx1278.setFrequency(frequency);
|
||||
sx1278.clearIRQFlags();
|
||||
|
||||
// Do this only once in setup in continous mode
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
|
||||
memset((void *)&dfmstate, 0, sizeof(dfmstate));
|
||||
DFM_DBG(Serial.println("Setting SX1278 config for DFM finished\n"); Serial.println());
|
||||
return retval;
|
||||
}
|
||||
@@ -163,8 +212,147 @@ void DFM::printRaw(const char *label, int len, int ret, const uint8_t *data)
|
||||
Serial.print(" ");
|
||||
}
|
||||
|
||||
const char* typestr[16]={
|
||||
"", "", "", "", "", "", // 00..05
|
||||
"DFM6", // 06 => DFM6
|
||||
"PS15", // 07 => PS15 (untested)
|
||||
"", "",
|
||||
"DFM9", // 0A => DFM9
|
||||
"DF17", // 0B => DFM17?
|
||||
"DF9P", // 0C => DFM9P or DFM17 test
|
||||
"DF17", // 0D => DFM17
|
||||
"", ""
|
||||
};
|
||||
|
||||
#define DFMIDTHRESHOLD 2
|
||||
/* inspired by oe5dxl's finddnmae in sondeudp.c of dxlaprs */
|
||||
void DFM::finddfname(uint8_t *b)
|
||||
{
|
||||
uint8_t st;
|
||||
uint32_t thres;
|
||||
uint32_t i;
|
||||
uint8_t ix;
|
||||
uint16_t d;
|
||||
|
||||
st = b[0]; /* frame start byte */
|
||||
ix = b[3]; /* hi/lo part of ser; (LSB due to our bitsToBytes...) */
|
||||
d = (b[1]<<8) + b[2]; /* data byte */
|
||||
/* find highest channel number single frame serial,
|
||||
(2 frame serial will make a single serial too) */
|
||||
if(dfmstate.idcnt0 < DFMIDTHRESHOLD && dfmstate.idcnt1 < DFMIDTHRESHOLD) {
|
||||
uint32_t v = (st<<20) | (d<<4) | ix;
|
||||
if ( st > (dfmstate.lastfrid>>20) ) {
|
||||
dfmstate.lastfrid = v;
|
||||
Serial.print(" MAXCH:"); Serial.print(st);
|
||||
dfmstate.lastfrcnt = 0;
|
||||
} else if ( st == (dfmstate.lastfrid>>20) ) {
|
||||
/* same id found */
|
||||
if (v == dfmstate.lastfrid) {
|
||||
++dfmstate.lastfrcnt;
|
||||
thres = DFMIDTHRESHOLD * 2;
|
||||
/* may be a 2 frame serial so increase safety level */
|
||||
if (ix <= 1) thres *= 2;
|
||||
/* may be not a dfm6 so increase safety level */
|
||||
if ( (st>>4) != 6) thres *= 2;
|
||||
if (dfmstate.lastfrcnt >= thres) {
|
||||
/* id found */
|
||||
if (dfmstate.lastfrcnt == thres) {
|
||||
uint32_t id = ((st&0x0F)<<20) | (d<<4) | ix;
|
||||
uint32_t chkid = id;
|
||||
int i;
|
||||
/* check validity */
|
||||
for(i=0; i<6; i++) {
|
||||
if((chkid&0x0f)>9) { break; /* not ok */ }
|
||||
chkid >>= 4;
|
||||
}
|
||||
if(i==6) {
|
||||
snprintf(sonde.si()->id, 10, "D%x ", id);
|
||||
sonde.si()->validID = true;
|
||||
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
|
||||
return;
|
||||
}
|
||||
dfmstate.lastfrcnt = 0;
|
||||
Serial.print(" NOT NUMERIC SERIAL");
|
||||
}
|
||||
//anonym->idtime = osic_time();
|
||||
} else {
|
||||
Serial.print(" MAXCHCNT/SECLVL:");
|
||||
Serial.print(dfmstate.lastfrcnt);
|
||||
Serial.print("/");
|
||||
Serial.print(thres);
|
||||
}
|
||||
} else {
|
||||
dfmstate.lastfrid = v; /* not stable ser */
|
||||
dfmstate.lastfrcnt = 0UL;
|
||||
}
|
||||
}
|
||||
} /*find highest channel number single frame serial */
|
||||
|
||||
i = 0;
|
||||
while (i<dfmstate.nameregtop && dfmstate.start[i]!=st) i++;
|
||||
Serial.printf(" %02x:i=%d,top=%d", st, i, dfmstate.nameregtop);
|
||||
if (i<dfmstate.nameregtop) {
|
||||
if (ix<=1UL && (dfmstate.cnt[2*i+ix]==0 || dfmstate.dat[2*i+ix]==d)) {
|
||||
dfmstate.dat[2*i+ix] = d;
|
||||
if(dfmstate.cnt[2*i+ix] < 255) dfmstate.cnt[2*i+ix]++;
|
||||
Serial.print(" ID:");
|
||||
Serial.print(st, HEX);
|
||||
Serial.print("[");
|
||||
Serial.print(ix);
|
||||
Serial.print("] CNT:");
|
||||
Serial.print(dfmstate.cnt[2*i]);
|
||||
Serial.print(",");
|
||||
Serial.print(dfmstate.cnt[2*i+1]);
|
||||
Serial.print(",st=");
|
||||
Serial.print(st);
|
||||
Serial.print(",lastfrid=");
|
||||
Serial.print(dfmstate.lastfrid>>20);
|
||||
if( (dfmstate.cnt[2*i]>DFMIDTHRESHOLD && dfmstate.cnt[2*i+1]>DFMIDTHRESHOLD) ||
|
||||
(dfmstate.cnt[2*i]>0 && dfmstate.cnt[2*i+1]>0 && st == (dfmstate.lastfrid>>20) && (st>>4)>6) ) {
|
||||
if(dfmstate.idcnt0 <= 1) {
|
||||
dfmstate.idcnt0 = dfmstate.cnt[2*i];
|
||||
dfmstate.idcnt1 = dfmstate.cnt[2*i+1];
|
||||
dfmstate.nameregok = i;
|
||||
// generate id.....
|
||||
snprintf(sonde.si()->id, 10, "D%d", ((dfmstate.dat[2*i]<<16)|dfmstate.dat[2*i+1])%100000000);
|
||||
Serial.print("\nNEW AUTOID:");
|
||||
Serial.println(sonde.si()->id);
|
||||
sonde.si()->validID = true;
|
||||
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
|
||||
}
|
||||
if(dfmstate.nameregok==i) {
|
||||
Serial.print(" ID OK");
|
||||
// idtime = .... /* TODO */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* data changed so not ser */
|
||||
dfmstate.cnt[2*i] = 0;
|
||||
dfmstate.cnt[2*i+1] = 0;
|
||||
if(dfmstate.nameregok == i) { /* found id wrong */
|
||||
dfmstate.idcnt0 = 0;
|
||||
dfmstate.idcnt1 = 0;
|
||||
}
|
||||
}
|
||||
} else if (ix<=1) { /* add new entry for possible ID */
|
||||
dfmstate.start[dfmstate.nameregtop] = st;
|
||||
dfmstate.cnt[2*dfmstate.nameregtop] = 0;
|
||||
dfmstate.cnt[2*dfmstate.nameregtop+1] = 0;
|
||||
dfmstate.cnt[2*dfmstate.nameregtop+ix] = 1;
|
||||
dfmstate.dat[2*dfmstate.nameregtop+ix] = d;
|
||||
if(dfmstate.nameregtop<49) dfmstate.nameregtop++;
|
||||
}
|
||||
}
|
||||
|
||||
void DFM::decodeCFG(uint8_t *cfg)
|
||||
{
|
||||
#if 1
|
||||
// new ID
|
||||
finddfname(cfg);
|
||||
// new aprs ID (dxlaprs, autorx) is now "D" + serial (8 digits) by consensus
|
||||
memcpy(sonde.si()->ser, sonde.si()->id+1, 9);
|
||||
#else
|
||||
// old ID
|
||||
static int lowid, highid, idgood=0, type=0;
|
||||
if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID
|
||||
lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f);
|
||||
@@ -190,6 +378,7 @@ void DFM::decodeCFG(uint8_t *cfg)
|
||||
sonde.si()->validID = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int bitCount(int x) {
|
||||
@@ -288,20 +477,140 @@ void DFM::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len)
|
||||
bytes[(i-1)/8] &= 0x0F;
|
||||
}
|
||||
|
||||
static int haveNewFrame = 0;
|
||||
|
||||
int DFM::processDFMdata(uint8_t dt) {
|
||||
static uint8_t data[1024];
|
||||
static uint32_t rxdata = 0;
|
||||
static uint8_t rxbitc = 0;
|
||||
static uint8_t rxbyte = 0;
|
||||
static uint8_t rxsearching = 1;
|
||||
static uint8_t rxp;
|
||||
static int rssi=0, fei=0, afc=0;
|
||||
static uint8_t invers = 0;
|
||||
|
||||
for(int i=0; i<8; i++) {
|
||||
uint8_t d = (dt&0x80)?1:0;
|
||||
dt <<= 1;
|
||||
rxdata = (rxdata<<1) | d;
|
||||
if( (rxbitc&1)==0 ) {
|
||||
// "bit1"
|
||||
rxbyte = (rxbyte<<1) | d;
|
||||
} else {
|
||||
// "bit2" ==> 01 or 10 => 1, otherweise => 0
|
||||
// not here: (10=>1, 01=>0)!!! rxbyte = rxbyte ^ d;
|
||||
}
|
||||
//
|
||||
if(rxsearching) {
|
||||
if( rxdata == 0x6566A5AA || rxdata == 0x9A995A55 ) {
|
||||
rxsearching = false;
|
||||
rxbitc = 0;
|
||||
rxp = 0;
|
||||
rxbyte = 0;
|
||||
rssi=sx1278.getRSSI();
|
||||
fei=sx1278.getFEI();
|
||||
afc=sx1278.getAFC();
|
||||
sonde.si()->rssi = rssi;
|
||||
sonde.si()->afc = afc;
|
||||
invers = (rxdata == 0x6566A5AA)?1:0;
|
||||
}
|
||||
} else {
|
||||
rxbitc = (rxbitc+1)%16; // 16;
|
||||
if(rxbitc == 0) { // got 8 data bit
|
||||
if(invers) rxbyte=~rxbyte;
|
||||
data[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
|
||||
if(rxp>=DFM_FRAMELEN) {
|
||||
rxsearching = true;
|
||||
//Serial.println("Got a DFM frame!");
|
||||
Serial.print("[RSSI="); Serial.print(rssi);
|
||||
Serial.print(" FEI="); Serial.print(fei);
|
||||
Serial.print(" AFC="); Serial.print(afc); Serial.print("] ");
|
||||
decodeFrameDFM(data);
|
||||
haveNewFrame = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DFM::receive() {
|
||||
if( stype == STYPE_DFM ) {
|
||||
return receiveNew();
|
||||
} else {
|
||||
return receiveOld();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int DFM::receiveNew() {
|
||||
int rxframes = 4;
|
||||
// tentative continuous RX version...
|
||||
unsigned long t0 = millis();
|
||||
while( ( millis() - t0 ) < 1000 ) {
|
||||
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
if ( bitRead(value, 7) ) {
|
||||
Serial.println("FIFO full");
|
||||
}
|
||||
if ( bitRead(value, 4) ) {
|
||||
Serial.println("FIFO overflow");
|
||||
// new: (maybe clear only overflow??) TODO
|
||||
sx1278.clearIRQFlags();
|
||||
}
|
||||
if ( bitRead(value, 2) == 1 ) {
|
||||
Serial.println("FIFO: payload ready()");
|
||||
// does not make much sence? (from m10): TODO
|
||||
// ??????? sx1278.clearIRQFlags();
|
||||
}
|
||||
if(bitRead(value, 6) == 0) { // while FIFO not empty
|
||||
byte data = sx1278.readRegister(REG_FIFO);
|
||||
processDFMdata(data);
|
||||
value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
} else {
|
||||
#if 0
|
||||
if(headerDetected) {
|
||||
t0 = millis(); // restart timer... don't time out if header detected...
|
||||
headerDetected = 0;
|
||||
}
|
||||
#endif
|
||||
if(haveNewFrame) {
|
||||
//Serial.printf("DFM::receive(): new frame complete after %ldms\n", millis()-t0);
|
||||
haveNewFrame = 0;
|
||||
rxframes--;
|
||||
if(rxframes==0) return RX_OK;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
}
|
||||
return RX_TIMEOUT;
|
||||
}
|
||||
|
||||
int DFM::receiveOld() {
|
||||
byte data[1000]; // pending data from previous mode may write more than 33 bytes. TODO.
|
||||
for(int i=0; i<2; i++) {
|
||||
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
|
||||
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
//int t = millis();
|
||||
int e = sx1278.receivePacketTimeout(1000, data);
|
||||
//Serial.printf("rxPTO done after %d ms", (int)(millis()-t));
|
||||
if(e) { return RX_TIMEOUT; } //if timeout... return 1
|
||||
|
||||
Serial.printf("inverse is %d\b", inverse);
|
||||
if(!inverse) { for(int i=0; i<33; i++) { data[i]^=0xFF; } }
|
||||
if(!(stype==STYPE_DFM09)) { for(int i=0; i<33; i++) { data[i]^=0xFF; } }
|
||||
decodeFrameDFM(data);
|
||||
}
|
||||
return RX_OK;
|
||||
}
|
||||
|
||||
int DFM::decodeFrameDFM(uint8_t *data) {
|
||||
deinterleave(data, 7, hamming_conf);
|
||||
deinterleave(data+7, 13, hamming_dat1);
|
||||
deinterleave(data+20, 13, hamming_dat2);
|
||||
#if 0
|
||||
Serial.print("RAWCFG:");
|
||||
for(int i=0; i<7*8; i++) {
|
||||
Serial.print(hamming_conf[i]?"1":"0");
|
||||
}
|
||||
#endif
|
||||
|
||||
int ret0 = hamming(hamming_conf, 7, block_conf);
|
||||
int ret1 = hamming(hamming_dat1, 13, block_dat1);
|
||||
@@ -318,7 +627,7 @@ int DFM::receive() {
|
||||
decodeCFG(byte_conf);
|
||||
decodeDAT(byte_dat1);
|
||||
decodeDAT(byte_dat2);
|
||||
}
|
||||
Serial.println("");
|
||||
return RX_OK;
|
||||
}
|
||||
|
||||
@@ -345,3 +654,4 @@ int DFM::waitRXcomplete() {
|
||||
}
|
||||
|
||||
DFM dfm = DFM();
|
||||
|
||||
|
||||
@@ -23,16 +23,23 @@
|
||||
class DFM
|
||||
{
|
||||
private:
|
||||
int inverse=0;
|
||||
int stype;
|
||||
char *stypename=NULL;
|
||||
|
||||
void deinterleave(uint8_t *str, int L, uint8_t *block);
|
||||
uint32_t bits2val(const uint8_t *bits, int len);
|
||||
int check(uint8_t code[8]);
|
||||
int hamming(uint8_t *ham, int L, uint8_t *sym);
|
||||
void printRaw(const char *prefix, int len, int ret, const uint8_t* data);
|
||||
void finddfname(uint8_t *cfg);
|
||||
void decodeCFG(uint8_t *cfg);
|
||||
void decodeDAT(uint8_t *dat);
|
||||
void bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
|
||||
int processDFMdata(uint8_t dt);
|
||||
int decodeFrameDFM(uint8_t *data);
|
||||
int receiveOld();
|
||||
int receiveNew();
|
||||
|
||||
|
||||
#define B 8
|
||||
#define S 4
|
||||
@@ -55,7 +62,7 @@ private:
|
||||
public:
|
||||
DFM();
|
||||
// main decoder API
|
||||
int setup(float frequency, int inverse);
|
||||
int setup(float frequency, int type);
|
||||
int receive();
|
||||
int waitRXcomplete();
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ int readLine(Stream &stream, char *buffer, int maxlen);
|
||||
extern const char *version_name;
|
||||
extern const char *version_id;
|
||||
|
||||
#include <../fonts/FreeMono9pt7b.h>
|
||||
#include <../fonts/FreeMono12pt7b.h>
|
||||
#include <../fonts/FreeSans9pt7b.h>
|
||||
#include <../fonts/FreeSans12pt7b.h>
|
||||
#include <../fonts/Picopixel.h>
|
||||
#include <fonts/FreeMono9pt7b.h>
|
||||
#include <fonts/FreeMono12pt7b.h>
|
||||
#include <fonts/FreeSans9pt7b.h>
|
||||
#include <fonts/FreeSans12pt7b.h>
|
||||
#include <fonts/Picopixel.h>
|
||||
|
||||
extern Sonde sonde;
|
||||
|
||||
@@ -27,10 +27,10 @@ extern AXP20X_Class axp;
|
||||
extern bool axp192_found;
|
||||
extern SemaphoreHandle_t axpSemaphore;
|
||||
|
||||
|
||||
SPIClass spiDisp(HSPI);
|
||||
|
||||
const char *sondeTypeStr[NSondeTypes] = { "DFM6", "DFM9", "RS41", "RS92", "M10 " };
|
||||
const char *sondeTypeStr[NSondeTypes] = { "DFM6", "DFM9", "RS41", "RS92", "M10 ", "M20 ", "DFM " };
|
||||
const char sondeTypeChar[NSondeTypes] = { '6', '9', '4', 'R', 'M', '2', 'D' };
|
||||
|
||||
byte myIP_tiles[8*11];
|
||||
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
|
||||
@@ -268,12 +268,12 @@ void U8x8Display::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y
|
||||
void U8x8Display::welcome() {
|
||||
u8x8->clear();
|
||||
setFont(FONT_LARGE);
|
||||
drawString(8 - strlen(version_name) / 2, 1, version_name);
|
||||
//drawString(8 - strlen(version_id) / 2, 2, version_id);
|
||||
drawString(0, 1, version_name);
|
||||
drawString(11, 1, version_id);
|
||||
setFont(FONT_SMALL);
|
||||
drawString(0, 4, "RS41/92,DFM,M10");
|
||||
drawString(0, 6, "by FRS2013 ");
|
||||
drawString(11, 6, version_id);
|
||||
drawString(0, 4, "RS41/92,DFM,M1&2");
|
||||
drawString(0, 6, "by Vigor & Xav ");
|
||||
//drawString(11, 6, version_id);
|
||||
}
|
||||
|
||||
static String previp;
|
||||
@@ -858,7 +858,7 @@ void Display::drawHS(DispEntry *de) {
|
||||
}
|
||||
boolean is_ms = (de->extra && de->extra[0]=='m')?true:false; // m/s or km/h
|
||||
float hs = sonde.si()->hs;
|
||||
if(is_ms) hs = hs / 3.6;
|
||||
if(is_ms) hs = hs * 3.6;
|
||||
boolean has_extra = (de->extra && de->extra[1]!=0)? true: false;
|
||||
snprintf(buf, 16, hs>99?" %3.0f":" %2.1f", hs);
|
||||
if(has_extra) { strcat(buf, de->extra+1); }
|
||||
@@ -1127,6 +1127,7 @@ static int tmpc=0;
|
||||
gpsCourse = lastCourse;
|
||||
}
|
||||
}
|
||||
sonde.config.gps_Valid=gpsValid;
|
||||
sonde.config.gps_Lat=gpsLat;
|
||||
sonde.config.gps_Lon=gpsLon;
|
||||
sonde.config.gps_Alt=gpsAlt;
|
||||
@@ -1214,7 +1215,7 @@ void Display::calcDurVol() {
|
||||
|
||||
}
|
||||
} else {
|
||||
heure=99;
|
||||
heure=0;
|
||||
}
|
||||
sonde.si()->durvolheure=heure;
|
||||
sonde.si()->durvolminute=minute;
|
||||
|
||||
@@ -48,7 +48,7 @@ int M10::setup(float frequency)
|
||||
M10_DBG(Serial.println("Setting FSJ mode FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setBitrate(9600)!=0) {
|
||||
if(sx1278.setBitrate(9616)!=0) {
|
||||
M10_DBG(Serial.println("Setting bitrate 9600bit/s FAILED"));
|
||||
return 1;
|
||||
}
|
||||
@@ -80,12 +80,13 @@ int M10::setup(float frequency)
|
||||
//const char *SYNC="\x99\x9A";
|
||||
#if 1
|
||||
// version 1, working with continuous RX
|
||||
const char *SYNC="\x66\x65";
|
||||
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
|
||||
//const char *SYNC="\x66\x65";
|
||||
|
||||
const char *SYNC="\x99\x99\x4C\x99";
|
||||
if(sx1278.setSyncConf(0x70, 4, (const uint8_t *)SYNC)!=0) {
|
||||
M10_DBG(Serial.println("Setting SYNC Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
//if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
if((sx1278.setPreambleDetect(0x9F)!=0)&&((sx1278.setPreambleDetect(0xAF)!=0))) {
|
||||
M10_DBG(Serial.println("Setting PreambleDetect FAILED"));
|
||||
return 1;
|
||||
@@ -258,7 +259,7 @@ int M10::decodeframeM10(uint8_t *data) {
|
||||
}
|
||||
}
|
||||
Serial.println(crcok?"CRC OK":"CRC NOT OK");
|
||||
//M10 0x9F M10Plus 0xAF M20 0x20
|
||||
//M10 0x9F M10Plus 0xAF
|
||||
if((data[1]==0x9F && data[2]==0x20) || (data[1]==0xAF && data[2]==0x20)) {
|
||||
Serial.println("Decoding...");
|
||||
// Its a M10
|
||||
@@ -381,7 +382,7 @@ void M10::processM10data(uint8_t dt)
|
||||
int M10::receive() {
|
||||
unsigned long t0 = millis();
|
||||
Serial.printf("M10::receive() start at %ld\n",t0);
|
||||
while( millis() - t0 < 1000 ) {
|
||||
while( millis() - t0 < 1512 ) {
|
||||
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
if ( bitRead(value, 7) ) {
|
||||
Serial.println("FIFO full");
|
||||
|
||||
Executable
+535
@@ -0,0 +1,535 @@
|
||||
|
||||
/* M20 decoder functions */
|
||||
#include "M20.h"
|
||||
#include "SX1278FSK.h"
|
||||
#include "rsc.h"
|
||||
#include "Sonde.h"
|
||||
#include <SPIFFS.h>
|
||||
|
||||
// well...
|
||||
//#include "rs92gps.h"
|
||||
|
||||
#define M20_DEBUG 1
|
||||
|
||||
#if M20_DEBUG
|
||||
#define M20_DBG(x) x
|
||||
#else
|
||||
#define M20_DBG(x)
|
||||
#endif
|
||||
|
||||
static byte data1[512];
|
||||
static byte *dataptr=data1;
|
||||
|
||||
static uint8_t rxbitc;
|
||||
static uint16_t rxbyte;
|
||||
static int rxp=0;
|
||||
static int haveNewFrame = 0;
|
||||
static int lastFrame = 0;
|
||||
static int headerDetected = 0;
|
||||
|
||||
int M20::setup(float frequency)
|
||||
{
|
||||
#if M20_DEBUG
|
||||
Serial.println("Setup sx1278 for M20 sonde");
|
||||
#endif
|
||||
//if(!initialized) {
|
||||
//Gencrctab();
|
||||
//initrsc();
|
||||
// not here for now.... get_eph("/brdc.19n");
|
||||
// initialized = true;
|
||||
//}
|
||||
|
||||
if(sx1278.ON()!=0) {
|
||||
M20_DBG(Serial.println("Setting SX1278 power on FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setFSK()!=0) {
|
||||
M20_DBG(Serial.println("Setting FSJ mode FAILED"));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setBitrate(9600)!=0) {
|
||||
M20_DBG(Serial.println("Setting bitrate 9600bit/s FAILED"));
|
||||
return 1;
|
||||
}
|
||||
#if M20_DEBUG
|
||||
float br = sx1278.getBitrate();
|
||||
Serial.print("Exact bitrate is ");
|
||||
Serial.println(br);
|
||||
#endif
|
||||
if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) {
|
||||
M20_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw));
|
||||
return 1;
|
||||
}
|
||||
if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) {
|
||||
M20_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
|
||||
if(sx1278.setRxConf(0x1E)!=0) {
|
||||
M20_DBG(Serial.println("Setting RX Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
|
||||
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
|
||||
|
||||
//const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8";
|
||||
//const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F";
|
||||
// was 0x57
|
||||
//const char *SYNC="\x99\x9A";
|
||||
#if 1
|
||||
// version 1, working with continuous RX
|
||||
//const char *SYNC="\x66\x65";
|
||||
const char *SYNC="\x99\x99\x4C\x99";
|
||||
if(sx1278.setSyncConf(0x70, 4, (const uint8_t *)SYNC)!=0) {
|
||||
M20_DBG(Serial.println("Setting SYNC Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
//if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
if(sx1278.setPreambleDetect(0x20)!=0) {
|
||||
M20_DBG(Serial.println("Setting PreambleDetect FAILED"));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
// version 2, with per-packet rx start, untested
|
||||
// header is 2a 10 65, i.e. with lsb first
|
||||
// 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1
|
||||
// 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01
|
||||
// preamble 0x6A 0x66 0x6A
|
||||
// i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A)
|
||||
// after 2a2a2a2a2a1065
|
||||
if(sx1278.setPreambleDetect(0xA8)!=0) {
|
||||
M20_DBG(Serial.println("Setting PreambleDetect FAILED"));
|
||||
return 1;
|
||||
}
|
||||
// sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52
|
||||
const char *SYNC="\x6A\x66\x69";
|
||||
if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) {
|
||||
M20_DBG(Serial.println("Setting SYNC Config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
// payload length is ((240 - 7)*10 +6)/8 = 292
|
||||
#endif
|
||||
|
||||
// Packet config 1: fixed len, no mancecer, no crc, no address filter
|
||||
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
|
||||
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
|
||||
M20_DBG(Serial.println("Setting Packet config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Serial.print("M20: setting RX frequency to ");
|
||||
Serial.println(frequency);
|
||||
int res = sx1278.setFrequency(frequency);
|
||||
// enable RX
|
||||
sx1278.setPayloadLength(0); // infinite for now...
|
||||
//sx1278.setPayloadLength(292);
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
|
||||
#if M20_DEBUG
|
||||
M20_DBG(Serial.println("Setting SX1278 config for M20 finished\n"); Serial.println());
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int M20::setFrequency(float frequency) {
|
||||
Serial.print("M20: setting RX frequency to ");
|
||||
Serial.println(frequency);
|
||||
int res = sx1278.setFrequency(frequency);
|
||||
// enable RX
|
||||
sx1278.setPayloadLength(0); // infinite for now...
|
||||
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
uint32_t M20::bits2val(const uint8_t *bits, int len) {
|
||||
uint32_t val = 0;
|
||||
for (int j = 0; j < len; j++) {
|
||||
val |= (bits[j] << (len-1-j));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
M20::M20() {
|
||||
}
|
||||
|
||||
#define M20_FRAMELEN 70
|
||||
#define M20_CRCPOS 68
|
||||
|
||||
void M20::printRaw(uint8_t *data, int len)
|
||||
{
|
||||
char buf[3];
|
||||
int i;
|
||||
for(i=0; i<len; i++) {
|
||||
snprintf(buf, 3, "%02X ", data[i]);
|
||||
Serial.print(buf);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
static int update_checkM20(int c, uint8_t b) {
|
||||
int c0, c1, t, t6, t7, s;
|
||||
c1 = c & 0xFF;
|
||||
// B
|
||||
b = (b >> 1) | ((b & 1) << 7);
|
||||
b ^= (b >> 2) & 0xFF;
|
||||
// A1
|
||||
t6 = ( c & 1) ^ ((c >> 2) & 1) ^ ((c >> 4) & 1);
|
||||
t7 = ((c >> 1) & 1) ^ ((c >> 3) & 1) ^ ((c >> 5) & 1);
|
||||
t = (c & 0x3F) | (t6 << 6) | (t7 << 7);
|
||||
// A2
|
||||
s = (c >> 7) & 0xFF;
|
||||
s ^= (s >> 2) & 0xFF;
|
||||
c0 = b ^ t ^ s;
|
||||
return ((c1 << 8) | c0) & 0xFFFF;
|
||||
}
|
||||
static bool checkM20crc(uint8_t *msg) {
|
||||
int i, cs, cs1;
|
||||
cs = 0;
|
||||
for (i = 0; i < M20_CRCPOS; i++) {
|
||||
cs = update_checkM20(cs, msg[i]);
|
||||
}
|
||||
cs = cs & 0xFFFF;
|
||||
cs1 = (msg[M20_CRCPOS] << 8) | msg[M20_CRCPOS+1];
|
||||
return (cs1 == cs);
|
||||
}
|
||||
|
||||
typedef uint32_t SET256[8];
|
||||
static SET256 sondeudp_VARSET = {0x03BBBBF0UL,0x80600000UL,0x06A001A0UL,
|
||||
0x0000001CUL,0x00000000UL,0x00000000UL,0x00000000UL,
|
||||
0x00000000UL};
|
||||
// VARSET=SET256{4..9,11..13,15..17,19..21,23..25,53..54,63,69,71,72,85,87,89,90,98..100};
|
||||
|
||||
static uint8_t fixcnt[M20_FRAMELEN];
|
||||
static uint8_t fixbytes[M20_FRAMELEN];
|
||||
|
||||
static int32_t getint32(uint8_t *data) {
|
||||
return (int32_t)( data[3]|(data[2]<<8)|(data[1]<<16)|(data[0]<<24) );
|
||||
}
|
||||
|
||||
static int32_t getint24(uint8_t *data) {
|
||||
return (int32_t)(data[2]|(data[1]<<8)|(data[0]<<16) );
|
||||
}
|
||||
|
||||
static int16_t getint16(uint8_t *data) {
|
||||
return (int16_t)(data[1]|((uint16_t)data[0]<<8));
|
||||
}
|
||||
|
||||
static int16_t getint16_r(uint8_t *data) {
|
||||
return (int16_t)(((uint16_t)data[1]<<8) |data[0]);
|
||||
}
|
||||
|
||||
static char dez(uint8_t nr) {
|
||||
nr = nr%10;
|
||||
return '0'+nr;
|
||||
}
|
||||
static char hex(uint8_t nr) {
|
||||
nr = nr&0x0f;
|
||||
if(nr<10) return '0'+nr;
|
||||
else return 'A'+nr-10;
|
||||
}
|
||||
const static float DEGMUL = 1.0/0xB60B60;
|
||||
#define VMUL 0.005
|
||||
#define VMUL_M20 0.01
|
||||
#ifndef PI
|
||||
#define PI (3.1415926535897932384626433832795)
|
||||
#endif
|
||||
#define RAD (PI/180)
|
||||
|
||||
|
||||
// ret: 1=frame ok; 2=frame with errors; 0=ignored frame (m20dop-alternativ)
|
||||
int M20::decodeframeM20(uint8_t *data) {
|
||||
int repairstep = 16;
|
||||
int repl = 0;
|
||||
bool crcok;
|
||||
// error correction, inspired by oe5dxl's sondeudp
|
||||
do {
|
||||
crcok = checkM20crc(data);
|
||||
if(crcok) break;
|
||||
repl = 0;
|
||||
for(int i=0; i<M20_CRCPOS; i++) {
|
||||
if( ((sondeudp_VARSET[i/32]&(1<<(i%32))) != 1) && (fixcnt[i]>=repairstep) ) {
|
||||
repl++;
|
||||
data[i] = fixbytes[i];
|
||||
}
|
||||
}
|
||||
repairstep >>= 1;
|
||||
} while(repairstep>0);
|
||||
if(crcok) {
|
||||
for(int i=0; i<M20_CRCPOS; i++) {
|
||||
if(fixbytes[i]==data[i] &&fixcnt[i]<255) fixcnt[i]++;
|
||||
else { fixcnt[i]=0; fixbytes[i]=data[i]; }
|
||||
}
|
||||
}
|
||||
Serial.println(crcok?"CRC OK":"CRC NOT OK");
|
||||
//M20 0x20
|
||||
if(data[1]==0x20) {
|
||||
Serial.println("Decoding...");
|
||||
// Its a M20
|
||||
// getid...
|
||||
char ids[11]={'M','E','0','0','0','0','0','0','0','0','0'};
|
||||
|
||||
ids[0] = 'M';
|
||||
ids[1] = 'E';
|
||||
uint32_t id = getint16(data+18);
|
||||
ids[2] = hex(id/16);
|
||||
ids[3] = hex(id);
|
||||
//
|
||||
id = getint16_r(data+19)/4;
|
||||
ids[4] = (char)((id/10000)%10+48);
|
||||
ids[5] = (char)((id/1000)%10+48);
|
||||
ids[6] = (char)((id/100)%10+48);
|
||||
ids[7] = (char)((id/10)%10+48);
|
||||
ids[8] = (char)(id%10+48);
|
||||
strncpy(sonde.si()->ser, ids, 10);
|
||||
sonde.si()->validID = true;
|
||||
//Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
|
||||
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
|
||||
// position data
|
||||
// 0x1C 4 byte
|
||||
sonde.si()->lat = getint32(data+28) * 1e-6;
|
||||
//0x20 4 byte
|
||||
sonde.si()->lon = getint32(data+32) * 1e-6;
|
||||
//0x08 3 byte
|
||||
sonde.si()->alt = getint24(data+8) * VMUL_M20;
|
||||
//0x0B 2 byte
|
||||
//VMUL_M20 specific
|
||||
float ve = getint16(data+11)*VMUL_M20;
|
||||
//0x0D 2 byte
|
||||
float vn = getint16(data+13)*VMUL_M20;
|
||||
//0x18 2 byte
|
||||
sonde.si()->vs = getint16(data+24) * VMUL_M20;
|
||||
sonde.si()->hs = sqrt(ve*ve+vn*vn);
|
||||
float dir = atan2(vn, ve)*(1.0/RAD);
|
||||
if(dir<0) dir+=360;
|
||||
sonde.si()->dir = dir;
|
||||
sonde.si()->validPos = 0x3f;
|
||||
|
||||
//0x0F 3 byte
|
||||
uint32_t tow = getint24(data+15);
|
||||
uint16_t week = getint16(data+26);
|
||||
sonde.si()->time = (tow+week*604800+315964800)-18;
|
||||
|
||||
sonde.si()->validTime = true;
|
||||
} else {
|
||||
Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]);
|
||||
return 0;
|
||||
}
|
||||
return crcok?1:2;
|
||||
}
|
||||
|
||||
static uint32_t rxdata;
|
||||
static bool rxsearching=true;
|
||||
|
||||
// search for
|
||||
// //101001100110011010011010011001100110100110101010100110101001
|
||||
// //1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9
|
||||
void M20::processM20data(uint8_t dt)
|
||||
{
|
||||
for(int i=0; i<8; i++) {
|
||||
uint8_t d = (dt&0x80)?1:0;
|
||||
dt <<= 1;
|
||||
rxdata = (rxdata<<1) | d;
|
||||
//uint8_t value = ((rxdata>>1)^rxdata)&0x01;
|
||||
//if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + ((value)<<8); } // mancester decoded data
|
||||
//rxbyte = (rxbyte>>1) | (d<<8);
|
||||
if( (rxbitc&1)==0 ) {
|
||||
// "bit1"
|
||||
rxbyte = (rxbyte<<1) | d;
|
||||
} else {
|
||||
// "bit2" ==> 01 or 10 => 1, otherweise => 0
|
||||
rxbyte = rxbyte ^ d;
|
||||
}
|
||||
//
|
||||
if(rxsearching) {
|
||||
if( rxdata == 0xcccca64c || rxdata == 0x333359b3 ) {
|
||||
rxsearching = false;
|
||||
rxbitc = 0;
|
||||
rxp = 0;
|
||||
#if 1
|
||||
int rssi=sx1278.getRSSI();
|
||||
int fei=sx1278.getFEI();
|
||||
int afc=sx1278.getAFC();
|
||||
Serial.print("Test: RSSI="); Serial.print(rssi);
|
||||
Serial.print(" FEI="); Serial.print(fei);
|
||||
Serial.print(" AFC="); Serial.println(afc);
|
||||
sonde.si()->rssi = rssi;
|
||||
sonde.si()->afc = afc;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
rxbitc = (rxbitc+1)%16; // 16;
|
||||
if(rxbitc == 0) { // got 8 data bit
|
||||
//Serial.printf("%03x ",rxbyte);
|
||||
dataptr[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
|
||||
#if 0
|
||||
if(rxp==7 && dataptr[6] != 0x65) {
|
||||
Serial.printf("wrong start: %02x\n",dataptr[6]);
|
||||
rxsearching = true;
|
||||
}
|
||||
#endif
|
||||
if(rxp>=M20_FRAMELEN) {
|
||||
rxsearching = true;
|
||||
haveNewFrame = decodeframeM20(dataptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int M20::receive() {
|
||||
unsigned long t0 = millis();
|
||||
Serial.printf("M20::receive() start at %ld\n",t0);
|
||||
while( millis() - t0 < 1512 ) {
|
||||
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
if ( bitRead(value, 7) ) {
|
||||
Serial.println("FIFO full");
|
||||
}
|
||||
if ( bitRead(value, 4) ) {
|
||||
Serial.println("FIFO overflow");
|
||||
}
|
||||
if ( bitRead(value, 2) == 1 ) {
|
||||
Serial.println("FIFO: ready()");
|
||||
sx1278.clearIRQFlags();
|
||||
}
|
||||
if(bitRead(value, 6) == 0) { // while FIFO not empty
|
||||
byte data = sx1278.readRegister(REG_FIFO);
|
||||
//Serial.printf("%02x",data);
|
||||
processM20data(data);
|
||||
value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
} else {
|
||||
if(headerDetected) {
|
||||
t0 = millis(); // restart timer... don't time out if header detected...
|
||||
headerDetected = 0;
|
||||
}
|
||||
if(haveNewFrame) {
|
||||
Serial.printf("M20::receive(): new frame complete after %ldms\n", millis()-t0);
|
||||
printRaw(dataptr, M20_FRAMELEN);
|
||||
int retval = haveNewFrame==1 ? RX_OK: RX_ERROR;
|
||||
haveNewFrame = 0;
|
||||
return retval;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
}
|
||||
Serial.printf("M20::receive() timed out\n");
|
||||
return RX_TIMEOUT; // TODO RX_OK;
|
||||
}
|
||||
|
||||
#define M20MAXLEN (240)
|
||||
int M20::waitRXcomplete() {
|
||||
// called after complete...
|
||||
#if 0
|
||||
Serial.printf("decoding frame %d\n", lastFrame);
|
||||
print_frame(lastFrame==1?data1:data2, 240);
|
||||
SondeInfo *si = sonde.sondeList+rxtask.receiveSonde;
|
||||
si->lat = gpx.lat;
|
||||
si->lon = gpx.lon;
|
||||
si->alt = gpx.alt;
|
||||
si->vs = gpx.vU;
|
||||
si->hs = gpx.vH;
|
||||
si->dir = gpx.vD;
|
||||
si->validPos = 0x3f;
|
||||
memcpy(si->id, gpx.id, 9);
|
||||
si->validID = true;
|
||||
|
||||
int res=0;
|
||||
uint32_t t0 = millis();
|
||||
while( rxtask.receiveResult == 0xFFFF && millis()-t0 < 2000) { delay(20); }
|
||||
|
||||
if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
|
||||
res = RX_TIMEOUT;
|
||||
} else if ( rxtask.receiveResult==0) {
|
||||
res = RX_OK;
|
||||
} else {
|
||||
res = RX_ERROR;
|
||||
}
|
||||
rxtask.receiveResult = 0xFFFF;
|
||||
Serial.printf("M20::waitRXcomplete returning %d (%s)\n", res, RXstr[res]);
|
||||
return res;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int oldwaitRXcomplete() {
|
||||
Serial.println("M20: receive frame...\n");
|
||||
sx1278receiveData = true;
|
||||
delay(6000); // done in other task....
|
||||
//sx1278receiveData = false;
|
||||
#if 0
|
||||
//sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
|
||||
//sx1278.setPayloadLength(0); // infinite for now...
|
||||
|
||||
////// test code for continuous reception
|
||||
// sx1278.receive(); /// active FSK RX mode -- already done above...
|
||||
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
unsigned long previous = millis();
|
||||
|
||||
byte ready=0;
|
||||
uint32_t wait = 8000;
|
||||
// while not yet done or FIFO not yet empty
|
||||
// bit 6: FIFO Empty
|
||||
// bit 2 payload ready
|
||||
int by=0;
|
||||
while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) )
|
||||
{
|
||||
if( bitRead(value, 7) ) { Serial.println("FIFO full"); }
|
||||
if( bitRead(value, 4) ) { Serial.println("FIFO overflow"); }
|
||||
if( bitRead(value,2)==1 ) ready=1;
|
||||
if( bitRead(value, 6) == 0 ) { // FIFO not empty
|
||||
byte data = sx1278.readRegister(REG_FIFO);
|
||||
process8N1data(data);
|
||||
by++;
|
||||
#if 0
|
||||
if(di==1) {
|
||||
int rssi=getRSSI();
|
||||
int fei=getFEI();
|
||||
int afc=getAFC();
|
||||
Serial.print("Test: RSSI="); Serial.println(rssi);
|
||||
Serial.print("Test: FEI="); Serial.println(fei);
|
||||
Serial.print("Test: AFC="); Serial.println(afc);
|
||||
sonde.si()->rssi = rssi;
|
||||
sonde.si()->afc = afc;
|
||||
}
|
||||
if(di>520) {
|
||||
// TODO
|
||||
Serial.println("TOO MUCH DATA");
|
||||
break;
|
||||
}
|
||||
previous = millis(); // reset timeout after receiving data
|
||||
#endif
|
||||
}
|
||||
value = sx1278.readRegister(REG_IRQ_FLAGS2);
|
||||
}
|
||||
Serial.printf("processed %d bytes before end/timeout\n", by);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/////
|
||||
#if 0
|
||||
int e = sx1278.receivePacketTimeout(1000, data+8);
|
||||
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1
|
||||
|
||||
printRaw(data, M20MAXLEN);
|
||||
//for(int i=0; i<M20MAXLEN; i++) { data[i] = reverse(data[i]); }
|
||||
//printRaw(data, MAXLEN);
|
||||
//for(int i=0; i<M20MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
|
||||
//printRaw(data, MAXLEN);
|
||||
//int res = decode41(data, M20MAXLEN);
|
||||
#endif
|
||||
int res=0;
|
||||
return res==0 ? RX_OK : RX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
M20 m20 = M20();
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* M20.h
|
||||
* Functions for decoding Meteomodem M20 sondes with SX127x chips
|
||||
* Copyright (C) 2019 Hansi Reiser, dl9rdz
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef M20_h
|
||||
#define M20_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#ifndef inttypes_h
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
struct CONTEXTR9 {
|
||||
char calibdata[512];
|
||||
uint32_t calibok;
|
||||
char mesok;
|
||||
char posok;
|
||||
char framesent;
|
||||
double lat;
|
||||
double lon;
|
||||
double heig;
|
||||
double speed;
|
||||
double dir;
|
||||
double climb;
|
||||
double lastlat;
|
||||
double laslong;
|
||||
double lastalt;
|
||||
double lastspeed;
|
||||
double lastdir;
|
||||
double lastclb;
|
||||
float hrmsc;
|
||||
float vrmsc;
|
||||
double hp;
|
||||
double hyg;
|
||||
double temp;
|
||||
double ozontemp;
|
||||
double ozon;
|
||||
uint32_t goodsats;
|
||||
uint32_t timems;
|
||||
uint32_t framenum;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Main class */
|
||||
class M20
|
||||
{
|
||||
private:
|
||||
void printRaw(uint8_t *data, int len);
|
||||
void processM20data(uint8_t data);
|
||||
int decodeframeM20(uint8_t *data);
|
||||
#if 0
|
||||
void stobyte92(uint8_t byte);
|
||||
void dogps(const uint8_t *sf, int sf_len,
|
||||
struct CONTEXTR9 * cont, uint32_t * timems,
|
||||
uint32_t * gpstime);
|
||||
uint32_t bits2val(const uint8_t *bits, int len);
|
||||
int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
|
||||
int decode92(byte *data, int maxlen);
|
||||
|
||||
uint8_t hamming_conf[ 7*8]; // 7*8=56
|
||||
uint8_t hamming_dat1[13*8]; // 13*8=104
|
||||
uint8_t hamming_dat2[13*8];
|
||||
|
||||
uint8_t block_conf[ 7*4]; // 7*4=28
|
||||
uint8_t block_dat1[13*4]; // 13*4=52
|
||||
uint8_t block_dat2[13*4];
|
||||
|
||||
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
|
||||
{{ 0, 1, 1, 1, 1, 0, 0, 0},
|
||||
{ 1, 0, 1, 1, 0, 1, 0, 0},
|
||||
{ 1, 1, 0, 1, 0, 0, 1, 0},
|
||||
{ 1, 1, 1, 0, 0, 0, 0, 1}};
|
||||
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
|
||||
// 1-bit-error-Syndrome
|
||||
boolean initialized = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
M20();
|
||||
int setup(float frequency);
|
||||
int receive();
|
||||
int waitRXcomplete();
|
||||
|
||||
//int use_ecc = 1;
|
||||
};
|
||||
|
||||
extern M20 m20;
|
||||
|
||||
#endif
|
||||
@@ -356,7 +356,8 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
|
||||
if (dir<0.0) dir = 360.0+dir;
|
||||
sonde.si()->dir = dir;
|
||||
Serial.print(" ");
|
||||
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f;
|
||||
//sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f;
|
||||
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve));
|
||||
Serial.print(sonde.si()->hs);
|
||||
Serial.print("km/h ");
|
||||
Serial.print(dir);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "RS92.h"
|
||||
#include "DFM.h"
|
||||
#include "M10.h"
|
||||
#include "M20.h"
|
||||
#include "SX1278FSK.h"
|
||||
#include "Display.h"
|
||||
#include <Wire.h>
|
||||
@@ -19,16 +20,21 @@ const char *evstring[]={"NONE", "KEY1S", "KEY1D", "KEY1M", "KEY1L", "KEY2S", "KE
|
||||
|
||||
const char *RXstr[]={"RX_OK", "RX_TIMEOUT", "RX_ERROR", "RX_UNKNOWN"};
|
||||
|
||||
int fingerprintValue[]={ 17, 31, 64, 4, 55, 48, 23, 128+23, -1 };
|
||||
// Dependency to enum SondeType
|
||||
const char *manufacturer_string[]={"Graw", "Graw", "Vaisala", "Vaisala", "Meteomodem", "Meteomodem", "Graw"};
|
||||
|
||||
int fingerprintValue[]={ 17, 31, 64, 4, 55, 48, 23, 128+23, 119,128+119, -1 };
|
||||
const char *fingerprintText[]={
|
||||
"TTGO T-Beam (new version 1.0), I2C not working after powerup, assuming 0.9\" OLED@21,22",
|
||||
"TTGO LORA32 v2.1_1.6 (0.9\" OLED@21,22)",
|
||||
"TTGO LORA v1.0 (0.9\" OLED@4,15)",
|
||||
"Heltec v1/v2 (0.9\"OLED@4,15)",
|
||||
"TTGO T-Beam (old version), 0.9\" OLED@21,22",
|
||||
"TTGO T-Beam (old version), SPI TFT@4,21,22",
|
||||
"TTGO T-Beam (new version 1.0), 0.9\" OLED@21,22",
|
||||
"TTGO T-Beam (new version 1.0), SPI TFT@4,13,14",
|
||||
"TTGO T-Beam (V0.7), 0.9\" OLED@21,22",
|
||||
"TTGO T-Beam (V0.7), SPI TFT@4,21,22",
|
||||
"TTGO T-Beam (V1.0), 0.9\" OLED@21,22",
|
||||
"TTGO T-Beam (V1.0), SPI TFT@4,13,14",
|
||||
"TTGO T-Beam (V1.1), 0.9\" OLED@21,22",
|
||||
"TTGO T-Beam (V1.1), SPI TFT@4,13,14",
|
||||
};
|
||||
|
||||
int getKeyPressEvent(); /* in RX_FSK.ino */
|
||||
@@ -107,6 +113,7 @@ void Sonde::defaultConfig() {
|
||||
//config.button2_pin = 255;
|
||||
config.button2_axp = 1;
|
||||
config.gps_rxd = 34;
|
||||
config.gps_txd = 12;
|
||||
// Check for I2C-Display@21,22
|
||||
#define SSD1306_ADDRESS 0x3c
|
||||
Wire.begin(21, 22);
|
||||
@@ -345,6 +352,24 @@ void Sonde::setConfig(const char *cfg) {
|
||||
config.tcpfeed.highrate = atoi(val);
|
||||
} else if(strcmp(cfg,"tcp.idformat")==0) {
|
||||
config.tcpfeed.idformat = atoi(val);
|
||||
} else if(strcmp(cfg, "sondehub.active")==0) {
|
||||
config.sondehub.active = atoi(val);
|
||||
} else if(strcmp(cfg,"sondehub.chase")==0) {
|
||||
config.sondehub.chase = atoi(val);
|
||||
} else if(strcmp(cfg, "sondehub.host")==0) {
|
||||
strncpy(config.sondehub.host, val, 63);
|
||||
} else if(strcmp(cfg, "sondehub.callsign")==0) {
|
||||
strncpy(config.sondehub.callsign, val, 63);
|
||||
} else if(strcmp(cfg, "sondehub.lat")==0) {
|
||||
strncpy(config.sondehub.lat, val, 19);
|
||||
} else if(strcmp(cfg, "sondehub.lon")==0) {
|
||||
strncpy(config.sondehub.lon, val, 19);
|
||||
} else if(strcmp(cfg, "sondehub.alt")==0) {
|
||||
strncpy(config.sondehub.alt, val, 19);
|
||||
} else if(strcmp(cfg, "sondehub.antenna")==0) {
|
||||
strncpy(config.sondehub.antenna, val, 63);
|
||||
} else if(strcmp(cfg, "sondehub.email")==0) {
|
||||
strncpy(config.sondehub.email, val, 63);
|
||||
} else {
|
||||
Serial.printf("Invalid config option '%s'=%s \n", cfg, val);
|
||||
}
|
||||
@@ -428,6 +453,7 @@ void Sonde::setup() {
|
||||
break;
|
||||
case STYPE_DFM06:
|
||||
case STYPE_DFM09:
|
||||
case STYPE_DFM:
|
||||
dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type==STYPE_DFM06?0:1 );
|
||||
break;
|
||||
case STYPE_RS92:
|
||||
@@ -436,6 +462,9 @@ void Sonde::setup() {
|
||||
case STYPE_M10:
|
||||
m10.setup( sondeList[rxtask.currentSonde].freq * 1000000);
|
||||
break;
|
||||
case STYPE_M20:
|
||||
m20.setup( sondeList[rxtask.currentSonde].freq * 1000000);
|
||||
break;
|
||||
}
|
||||
// debug
|
||||
float afcbw = sx1278.getAFCBandwidth();
|
||||
@@ -458,6 +487,10 @@ void Sonde::receive() {
|
||||
case STYPE_M10:
|
||||
res = m10.receive();
|
||||
break;
|
||||
case STYPE_M20:
|
||||
res = m20.receive();
|
||||
break;
|
||||
case STYPE_DFM:
|
||||
case STYPE_DFM06:
|
||||
case STYPE_DFM09:
|
||||
res = dfm.receive();
|
||||
@@ -550,6 +583,10 @@ rxloop:
|
||||
case STYPE_M10:
|
||||
m10.waitRXcomplete();
|
||||
break;
|
||||
case STYPE_M20:
|
||||
m20.waitRXcomplete();
|
||||
break;
|
||||
case STYPE_DFM:
|
||||
case STYPE_DFM06:
|
||||
case STYPE_DFM09:
|
||||
dfm.waitRXcomplete();
|
||||
|
||||
@@ -46,16 +46,23 @@ extern const char *RXstr[];
|
||||
// 01000000 => goto sonde -1
|
||||
// 01000001 => goto sonde +1
|
||||
|
||||
#define NSondeTypes 5
|
||||
enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10 };
|
||||
#define NSondeTypes 7
|
||||
enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_DFM };
|
||||
extern const char *sondeTypeStr[NSondeTypes];
|
||||
extern const char sondeTypeChar[NSondeTypes];
|
||||
extern const char *manufacturer_string[NSondeTypes];
|
||||
|
||||
#define TYPE_IS_DFM(t) ( (t)==STYPE_DFM || (t)==STYPE_DFM09 || (t)==STYPE_DFM06 )
|
||||
#define TYPE_IS_METEO(t) ( (t)==STYPE_M10 || (t)==STYPE_M20 )
|
||||
|
||||
typedef struct st_sondeinfo {
|
||||
// receiver configuration
|
||||
bool active;
|
||||
SondeType type;
|
||||
int8_t subtype; /* 0 for none/unknown, hex type for dfm, maybe add 1/2 for M10/M20 as well?*/
|
||||
float freq;
|
||||
// decoded ID
|
||||
char typestr[5]; // decoded type (use type if *typestr==0)
|
||||
char id[10];
|
||||
char ser[12];
|
||||
bool validID;
|
||||
@@ -69,8 +76,8 @@ typedef struct st_sondeinfo {
|
||||
int durvolminute; // duree vol minute
|
||||
int durvolseconde; // duree vol seconde
|
||||
float alt; // altitude
|
||||
float vs; // vertical speed
|
||||
float hs; // horizontal speed
|
||||
float vs; // vertical speed in m/s
|
||||
float hs; // horizontal speed in m/s
|
||||
float dir; // 0..360
|
||||
uint8_t sats; // number of sats
|
||||
uint8_t validPos; // bit pattern for validity of above 7 fields; 0x80: position is old
|
||||
@@ -91,6 +98,11 @@ typedef struct st_sondeinfo {
|
||||
// shut down timers, currently only for RS41; -1=disabled
|
||||
int16_t launchKT, burstKT, countKT;
|
||||
uint16_t crefKT; // frame number in which countKT was last sent
|
||||
// sonde specific extra data, NULL if unused or not yet initialized, currently used for RS41 subframe data (calibration)
|
||||
void *extra;
|
||||
float temperature = -300.0; // platinum resistor temperature
|
||||
float tempRHSensor = -300.0; // temperature of relative humidity sensor
|
||||
float relativeHumidity = -1.0; // relative humidity
|
||||
} SondeInfo;
|
||||
// rxStat: 3=undef[empty] 1=timeout[.] 2=errro[E] 0=ok[|] 4=no valid position[°]
|
||||
|
||||
@@ -149,6 +161,18 @@ struct st_kisstnc {
|
||||
int idformat;
|
||||
};
|
||||
|
||||
//SondeHub
|
||||
struct st_sondehub {
|
||||
int active;
|
||||
int chase;
|
||||
char host[64];
|
||||
char callsign[64];
|
||||
char lat[20];
|
||||
char lon[20];
|
||||
char alt[20];
|
||||
char antenna[64];
|
||||
char email[64];
|
||||
};
|
||||
|
||||
typedef struct st_rdzconfig {
|
||||
// hardware configuration
|
||||
@@ -170,6 +194,7 @@ typedef struct st_rdzconfig {
|
||||
char gps_lon[20]; // QTH no gps longitude
|
||||
int gps_alt; // QTH no gps altitude
|
||||
float gps_Lat,gps_Lon, gps_Alt; // QTH gps OM lat, lon, alt
|
||||
bool gps_Valid;
|
||||
// software configuration
|
||||
int debug; // show port and config options after reboot
|
||||
int wifi; // connect to known WLAN 0=skip
|
||||
@@ -205,6 +230,7 @@ typedef struct st_rdzconfig {
|
||||
struct st_feedinfo udpfeed; // target for AXUDP messages
|
||||
struct st_feedinfo tcpfeed; // target for APRS-IS TCP connections
|
||||
struct st_kisstnc kisstnc; // target for KISS TNC (via TCP, mainly for APRSdroid)
|
||||
struct st_sondehub sondehub;
|
||||
} RDZConfig;
|
||||
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
|
||||
char comm[100];
|
||||
snprintf(comm, 100, "Clb=%.1fm/s %.3fMHz Type=%s", s->vs, s->freq, sondeTypeStr[s->type]);
|
||||
strcat(b, comm);
|
||||
if(s->type==STYPE_M10||s->type==STYPE_DFM06||s->type==STYPE_DFM09) {
|
||||
if(s->type==STYPE_M20||s->type==STYPE_M10||s->type==STYPE_DFM||s->type==STYPE_DFM06||s->type==STYPE_DFM09) {
|
||||
snprintf(comm, 100, " ser=%s", s->ser);
|
||||
strcat(b, comm);
|
||||
}
|
||||
|
||||
@@ -136,11 +136,15 @@ void geteph() {
|
||||
int len=0;
|
||||
while(dclient.connected()) {
|
||||
while(dclient.available()) {
|
||||
char c = dclient.read();
|
||||
int c = dclient.read();
|
||||
if(c==-1) {
|
||||
Serial.println("dclient.read() returned -1 inspite of available() being true?!");
|
||||
} else {
|
||||
fh.write(c);
|
||||
len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.printf("fetched %d bytes\n", len);
|
||||
fh.close();
|
||||
snprintf(buf, 16, "Fetched %d B ",len);
|
||||
|
||||
Executable
BIN
Binary file not shown.
Reference in New Issue
Block a user