kopia lustrzana https://github.com/dl9rdz/rdz_ttgo_sonde
Merge branch 'dl9rdz:devel' into devel
commit
838c16c5c6
|
@ -13,6 +13,7 @@
|
|||
#include <MicroNMEA.h>
|
||||
#include <Ticker.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/rtc_wdt.h"
|
||||
|
||||
#include "src/SX1278FSK.h"
|
||||
#include "src/Sonde.h"
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include "src/rs92gps.h"
|
||||
#include "src/aprs.h"
|
||||
#include "src/ShFreqImport.h"
|
||||
#include "src/RS41.h"
|
||||
|
||||
#if FEATURE_MQTT
|
||||
#include "src/mqtt.h"
|
||||
|
@ -62,7 +64,8 @@ WiFiClient client;
|
|||
#define SONDEHUB_STATION_UPDATE_TIME (60*60*1000) // 60 min
|
||||
#define SONDEHUB_MOBILE_STATION_UPDATE_TIME (30*1000) // 30 sec
|
||||
WiFiClient shclient; // Sondehub v2
|
||||
char shImportInterval = 0, shImport = 0;
|
||||
int shImportInterval = 0;
|
||||
char shImport = 0;
|
||||
unsigned long time_last_update = 0;
|
||||
/* SH_LOC_OFF: never send position information to SondeHub
|
||||
SH_LOC_FIXED: send fixed position (if specified in config) to sondehub
|
||||
|
@ -73,8 +76,8 @@ unsigned long time_last_update = 0;
|
|||
enum { SH_LOC_OFF, SH_LOC_FIXED, SH_LOC_CHASE, SH_LOC_AUTO };
|
||||
/* auto mode is chase if valid GPS position and (no fixed location entered OR valid GPS position and distance in lat/lon deg to fixed location > threshold) */
|
||||
#define MIN_LOC_AUTO_DIST 200 /* meter */
|
||||
#define SH_LOC_AUTO_IS_CHASE ( gpsPos.valid && ( (isnan(sonde.config.sondehub.lat) || isnan(sonde.config.sondehub.lon) ) || \
|
||||
calcLatLonDist( gpsPos.lat, gpsPos.lon, sonde.config.sondehub.lat, sonde.config.sondehub.lon ) > MIN_LOC_AUTO_DIST ) )
|
||||
#define SH_LOC_AUTO_IS_CHASE ( gpsPos.valid && ( (isnan(sonde.config.rxlat) || isnan(sonde.config.rxlon) ) || \
|
||||
calcLatLonDist( gpsPos.lat, gpsPos.lon, sonde.config.rxlat, sonde.config.rxlon ) > MIN_LOC_AUTO_DIST ) )
|
||||
#endif
|
||||
extern float calcLatLonDist(float lat1, float lon1, float lat2, float lon2);
|
||||
|
||||
|
@ -145,6 +148,18 @@ int readLine(Stream &stream, char *buffer, int maxlen) {
|
|||
// Replaces placeholder with LED state value
|
||||
String processor(const String& var) {
|
||||
Serial.println(var);
|
||||
if (var == "MAPCENTER") {
|
||||
double lat, lon;
|
||||
if(gpsPos.valid) { lat=gpsPos.lat; lon=gpsPos.lon; }
|
||||
else { lat = sonde.config.rxlat; lon = sonde.config.rxlon; }
|
||||
if( !isnan(lat) && !isnan(lon) ) {
|
||||
char p[40];
|
||||
snprintf(p, 40, "%g,%g", lat, lon);
|
||||
return String(p);
|
||||
} else {
|
||||
return String("48,13");
|
||||
}
|
||||
}
|
||||
if (var == "VERSION_NAME") {
|
||||
return String(version_name);
|
||||
}
|
||||
|
@ -153,7 +168,7 @@ String processor(const String& var) {
|
|||
}
|
||||
if (var == "FULLNAMEID") {
|
||||
char tmp[128];
|
||||
snprintf(tmp, 128, "%s-%c%d", version_id, SPIFFS_MAJOR+'A'-1, SPIFFS_MINOR);
|
||||
snprintf(tmp, 128, "%s-%c%d", version_id, SPIFFS_MAJOR + 'A' - 1, SPIFFS_MINOR);
|
||||
return String(tmp);
|
||||
}
|
||||
if (var == "AUTODETECT_INFO") {
|
||||
|
@ -308,13 +323,11 @@ const char *createQRGForm() {
|
|||
const char *handleQRGPost(AsyncWebServerRequest *request) {
|
||||
char label[10];
|
||||
// parameters: a_i, f_1, t_i (active/frequency/type)
|
||||
#if 1
|
||||
File file = SPIFFS.open("/qrg.txt", "w");
|
||||
if (!file) {
|
||||
Serial.println("Error while opening '/qrg.txt' for writing");
|
||||
return "Error while opening '/qrg.txt' for writing";
|
||||
}
|
||||
#endif
|
||||
Serial.println("Handling post request");
|
||||
#if 0
|
||||
int params = request->params();
|
||||
|
@ -347,9 +360,7 @@ const char *handleQRGPost(AsyncWebServerRequest *request) {
|
|||
}
|
||||
file.close();
|
||||
|
||||
Serial.println("Channel setup finished");
|
||||
Serial.println();
|
||||
delay(500);
|
||||
Serial.println("Channel setup finished\n");
|
||||
setupChannelList();
|
||||
return "";
|
||||
}
|
||||
|
@ -422,14 +433,14 @@ const char *createSondeHubMap() {
|
|||
HTMLBODY(ptr, "map.html");
|
||||
if (!sonde.config.sondehub.active) {
|
||||
strcat(ptr, "<div>NOTE: SondeHub uploading is not enabled, detected sonde will not be visable on map</div>");
|
||||
if ((*s->ser == 0) && ( !isnan(sonde.config.sondehub.lat))) {
|
||||
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", sonde.config.sondehub.lat, sonde.config.sondehub.lon);
|
||||
if ((*s->ser == 0) && ( !isnan(sonde.config.rxlat))) {
|
||||
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", sonde.config.rxlat, sonde.config.rxlon);
|
||||
} else {
|
||||
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", s-> ser);
|
||||
}
|
||||
} else {
|
||||
if ((*s->ser == 0) && (!isnan(sonde.config.sondehub.lat))) {
|
||||
sprintf(ptr, "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", sonde.config.sondehub.lat, sonde.config.sondehub.lon);
|
||||
if ((*s->ser == 0) && (!isnan(sonde.config.rxlat))) {
|
||||
sprintf(ptr, "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", sonde.config.rxlat, sonde.config.rxlon);
|
||||
} else {
|
||||
sprintf(ptr, "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", s-> ser);
|
||||
}
|
||||
|
@ -526,11 +537,9 @@ const char *createLiveJson() {
|
|||
strcpy(ptr, "{");
|
||||
|
||||
SondeInfo *s = &sonde.sondeList[sonde.currentSonde];
|
||||
if (s->validID) {
|
||||
sprintf(ptr + strlen(ptr), "\"sonde\": {\"id\": \"%s\", \"freq\": %3.3f, \"type\": \"%s\", \"lat\": %.6f, \"lon\": %.6f, \"alt\": %.0f, \"speed\": %.1f, \"dir\": %.0f, \"climb\": %.1f }", s->id, s->freq, sondeTypeStr[s->type], s->lat, s->lon, s->alt, s->hs, s->dir, s->vs);
|
||||
} else {
|
||||
sprintf(ptr + strlen(ptr), "\"sonde\": {\"launchsite\": \"%s\",\"freq\": %3.3f, \"type\": \"%s\" }", s->launchsite, s->freq, sondeTypeStr[s->type]);
|
||||
}
|
||||
sprintf(ptr + strlen(ptr), "\"res\": %d, \"rssi\": %d, \"sonde\": {\"vframe\": %d, \"time\": %d,\"id\": \"%s\", \"freq\": %3.3f, \"type\": \"%s\","
|
||||
"\"lat\": %.6f, \"lon\": %.6f, \"alt\": %.0f, \"speed\": %.1f, \"dir\": %.0f, \"climb\": %.1f, \"launchsite\": \"%s\" }",
|
||||
s->rxStat[0], s->rssi, s->vframe, s->time, s->id, s->freq, sondeTypeStr[s->type], s->lat, s->lon, s->alt, s->hs, s->dir, s->vs, s->launchsite);
|
||||
|
||||
if (sonde.config.gps_rxd < 0) {
|
||||
// gps disabled
|
||||
|
@ -567,11 +576,12 @@ void setupConfigData() {
|
|||
String line = readLine(file); //file.readStringUntil('\n');
|
||||
sonde.setConfig(line.c_str());
|
||||
}
|
||||
int shII = atoi(sonde.config.sondehub.fimport);
|
||||
if(shImportInterval > shII) shImportInterval = shII;
|
||||
sonde.checkConfig(); // eliminate invalid entries
|
||||
shImportInterval = 5; // refresh now in 5 seconds
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
struct st_configitems config_list[] = {
|
||||
/* General config settings */
|
||||
{"", "Software configuration", -5, NULL},
|
||||
|
@ -672,10 +682,116 @@ struct st_configitems config_list[] = {
|
|||
{"sondehub.antenna", "Antenna (optional, visisble on SondeHub tracker)", 63, &sonde.config.sondehub.antenna},
|
||||
{"sondehub.email", "SondeHub email (optional, only used to contact in case of upload errors)", 63, &sonde.config.sondehub.email},
|
||||
{"sondehub.fimport", "SondeHub freq import (interval/maxdist/maxage [min/km/min])", 18, &sonde.config.sondehub.fimport},
|
||||
};
|
||||
#endif
|
||||
#else
|
||||
struct st_configitems config_list[] = {
|
||||
/* General config settings */
|
||||
{"wifi", 0, &sonde.config.wifi},
|
||||
{"debug", 0, &sonde.config.debug},
|
||||
{"maxsonde", 0, &sonde.config.maxsonde},
|
||||
{"rxlat", -7, &sonde.config.rxlat},
|
||||
{"rxlon", -7, &sonde.config.rxlon},
|
||||
{"rxalt", -7, &sonde.config.rxalt},
|
||||
{"screenfile", 0, &sonde.config.screenfile},
|
||||
{"display", -6, sonde.config.display},
|
||||
/* Spectrum display settings */
|
||||
{"spectrum", 0, &sonde.config.spectrum},
|
||||
{"startfreq", 0, &sonde.config.startfreq},
|
||||
{"channelbw", 0, &sonde.config.channelbw},
|
||||
{"marker", 0, &sonde.config.marker},
|
||||
{"noisefloor", 0, &sonde.config.noisefloor},
|
||||
/* decoder settings */
|
||||
{"freqofs", 0, &sonde.config.freqofs},
|
||||
{"rs41.agcbw", 0, &sonde.config.rs41.agcbw},
|
||||
{"rs41.rxbw", 0, &sonde.config.rs41.rxbw},
|
||||
{"rs92.rxbw", 0, &sonde.config.rs92.rxbw},
|
||||
{"rs92.alt2d", 0, &sonde.config.rs92.alt2d},
|
||||
{"dfm.agcbw", 0, &sonde.config.dfm.agcbw},
|
||||
{"dfm.rxbw", 0, &sonde.config.dfm.rxbw},
|
||||
{"m10m20.agcbw", 0, &sonde.config.m10m20.agcbw},
|
||||
{"m10m20.rxbw", 0, &sonde.config.m10m20.rxbw},
|
||||
{"mp3h.agcbw", 0, &sonde.config.mp3h.agcbw},
|
||||
{"mp3h.rxbw", 0, &sonde.config.mp3h.rxbw},
|
||||
{"ephftp", 39, &sonde.config.ephftp},
|
||||
/* APRS settings */
|
||||
{"call", 8, sonde.config.call},
|
||||
{"passcode", 0, &sonde.config.passcode},
|
||||
/* KISS tnc settings */
|
||||
{"kisstnc.active", 0, &sonde.config.kisstnc.active},
|
||||
{"kisstnc.idformat", -2, &sonde.config.kisstnc.idformat},
|
||||
/* AXUDP settings */
|
||||
{"axudp.active", -3, &sonde.config.udpfeed.active},
|
||||
{"axudp.host", 63, sonde.config.udpfeed.host},
|
||||
{"axudp.port", 0, &sonde.config.udpfeed.port},
|
||||
{"axudp.idformat", -2, &sonde.config.udpfeed.idformat},
|
||||
{"axudp.highrate", 0, &sonde.config.udpfeed.highrate},
|
||||
/* APRS TCP settings, current not used */
|
||||
{"tcp.active", -3, &sonde.config.tcpfeed.active},
|
||||
{"tcp.host", 63, sonde.config.tcpfeed.host},
|
||||
{"tcp.port", 0, &sonde.config.tcpfeed.port},
|
||||
{"tcp.idformat", -2, &sonde.config.tcpfeed.idformat},
|
||||
{"tcp.highrate", 0, &sonde.config.tcpfeed.highrate},
|
||||
|
||||
#if FEATURE_MQTT
|
||||
/* MQTT */
|
||||
{"mqtt.active", 0, &sonde.config.mqtt.active},
|
||||
{"mqtt.id", 63, &sonde.config.mqtt.id},
|
||||
{"mqtt.host", 63, &sonde.config.mqtt.host},
|
||||
{"mqtt.port", 0, &sonde.config.mqtt.port},
|
||||
{"mqtt.username", 63, &sonde.config.mqtt.username},
|
||||
{"mqtt.password", 63, &sonde.config.mqtt.password},
|
||||
{"mqtt.prefix", 63, &sonde.config.mqtt.prefix},
|
||||
#endif
|
||||
|
||||
/* Hardware dependeing settings */
|
||||
{"disptype", 0, &sonde.config.disptype},
|
||||
{"norx_timeout", 0, &sonde.config.norx_timeout},
|
||||
{"oled_sda", 0, &sonde.config.oled_sda},
|
||||
{"oled_scl", 0, &sonde.config.oled_scl},
|
||||
{"oled_rst", 0, &sonde.config.oled_rst},
|
||||
{"tft_rs", 0, &sonde.config.tft_rs},
|
||||
{"tft_cs", 0, &sonde.config.tft_cs},
|
||||
{"tft_orient", 0, &sonde.config.tft_orient},
|
||||
{"tft_spifreq", 0, &sonde.config.tft_spifreq},
|
||||
{"button_pin", -4, &sonde.config.button_pin},
|
||||
{"button2_pin", -4, &sonde.config.button2_pin},
|
||||
{"button2_axp", 0, &sonde.config.button2_axp},
|
||||
{"touch_thresh", 0, &sonde.config.touch_thresh},
|
||||
{"power_pout", 0, &sonde.config.power_pout},
|
||||
{"led_pout", 0, &sonde.config.led_pout},
|
||||
{"gps_rxd", 0, &sonde.config.gps_rxd},
|
||||
{"gps_txd", 0, &sonde.config.gps_txd},
|
||||
{"batt_adc", 0, &sonde.config.batt_adc},
|
||||
#if 1
|
||||
{"sx1278_ss", 0, &sonde.config.sx1278_ss},
|
||||
{"sx1278_miso", 0, &sonde.config.sx1278_miso},
|
||||
{"sx1278_mosi", 0, &sonde.config.sx1278_mosi},
|
||||
{"sx1278_sck", 0, &sonde.config.sx1278_sck},
|
||||
#endif
|
||||
{"mdnsname", 14, &sonde.config.mdnsname},
|
||||
|
||||
#if FEATURE_SONDEHUB
|
||||
/* SondeHub settings */
|
||||
{"sondehub.active", 0, &sonde.config.sondehub.active},
|
||||
{"sondehub.chase", 0, &sonde.config.sondehub.chase},
|
||||
{"sondehub.host", 63, &sonde.config.sondehub.host},
|
||||
{"sondehub.callsign", 63, &sonde.config.sondehub.callsign},
|
||||
{"sondehub.antenna", 63, &sonde.config.sondehub.antenna},
|
||||
{"sondehub.email", 63, &sonde.config.sondehub.email},
|
||||
{"sondehub.fiactive", 0, &sonde.config.sondehub.fiactive},
|
||||
{"sondehub.fiinterval", 0, &sonde.config.sondehub.fiinterval},
|
||||
{"sondehub.fimaxdist", 0, &sonde.config.sondehub.fimaxdist},
|
||||
{"sondehub.fimaxage", 0, &sonde.config.sondehub.fimaxage},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
const int N_CONFIG = (sizeof(config_list) / sizeof(struct st_configitems));
|
||||
|
||||
#if 0
|
||||
// old code, no longer needed (in js now)
|
||||
|
||||
void addConfigStringEntry(char *ptr, int idx, const char *label, int len, char *field) {
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>%s</td><td><input name=\"CFG%d\" type=\"text\" value=\"%s\"/></td></tr>\n",
|
||||
label, idx, field);
|
||||
|
@ -733,11 +849,55 @@ void addConfigInt8List(char *ptr, int idx, const char *label, int8_t *list) {
|
|||
}
|
||||
strcat(ptr, "\"/></td></tr>\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *createConfigForm() {
|
||||
char *ptr = message;
|
||||
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
|
||||
HTMLBODY(ptr, "config.html");
|
||||
strcat(ptr, "<div id=\"cfgtab\"></div>");
|
||||
strcat(ptr, "<script src=\"cfg.js\"></script>");
|
||||
strcat(ptr, "<script>\n");
|
||||
sprintf(ptr + strlen(ptr), "var scr=\"Using /screens%d.txt", Display::getScreenIndex(sonde.config.screenfile));
|
||||
for (int i = 0; i < disp.nLayouts; i++) {
|
||||
sprintf(ptr + strlen(ptr), "<br>%d=%s", i, disp.layouts[i].label);
|
||||
}
|
||||
strcat(ptr, "\";\n");
|
||||
strcat(ptr, "var cf=new Map();\n");
|
||||
for (int i = 0; i < N_CONFIG; i++) {
|
||||
sprintf(ptr + strlen(ptr), "cf.set(\"%s\", \"", config_list[i].name);
|
||||
switch (config_list[i].type) {
|
||||
case -4:
|
||||
case -3:
|
||||
case -2:
|
||||
case 0:
|
||||
sprintf(ptr + strlen(ptr), "%d", *(int *)config_list[i].data);
|
||||
break;
|
||||
case -6: // list
|
||||
{
|
||||
int8_t *l = (int8_t *)config_list[i].data;
|
||||
if (*l == -1) strcat(ptr, "0");
|
||||
else {
|
||||
sprintf(ptr + strlen(ptr), "%d", l[0]);
|
||||
l++;
|
||||
}
|
||||
while (*l != -1) {
|
||||
sprintf(ptr + strlen(ptr), ",%d", *l);
|
||||
l++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case -7: // double
|
||||
if (!isnan(*(double *)config_list[i].data))
|
||||
sprintf(ptr + strlen(ptr), "%g", *(double *)config_list[i].data);
|
||||
break;
|
||||
default: // string
|
||||
strcat(ptr, (char *)config_list[i].data);
|
||||
}
|
||||
strcat(ptr, "\");\n");
|
||||
}
|
||||
strcat(ptr, "configTable();\n </script>");
|
||||
#if 0
|
||||
strcat(ptr, "<table><tr><th>Option</th><th>Value</th></tr>");
|
||||
for (int i = 0; i < N_CONFIG; i++) {
|
||||
Serial.printf("%d: %s -- %d\n", i, config_list[i].label, strlen(ptr));
|
||||
|
@ -773,6 +933,7 @@ const char *createConfigForm() {
|
|||
}
|
||||
strcat(ptr, "</table>");
|
||||
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
|
||||
#endif
|
||||
HTMLSAVEBUTTON(ptr);
|
||||
HTMLBODYEND(ptr);
|
||||
Serial.printf("Config form: size=%d bytes\n", strlen(message));
|
||||
|
@ -791,8 +952,8 @@ const char *handleConfigPost(AsyncWebServerRequest *request) {
|
|||
}
|
||||
#endif
|
||||
Serial.println("File open for writing.");
|
||||
#if 1
|
||||
int params = request->params();
|
||||
#if 0
|
||||
for (int i = 0; i < params; i++) {
|
||||
String param = request->getParam(i)->name();
|
||||
Serial.println(param.c_str());
|
||||
|
@ -801,13 +962,26 @@ const char *handleConfigPost(AsyncWebServerRequest *request) {
|
|||
for (int i = 0; i < params; i++) {
|
||||
String strlabel = request->getParam(i)->name();
|
||||
const char *label = strlabel.c_str();
|
||||
if (label[strlen(label) - 1] == '#') continue;
|
||||
#if 0
|
||||
if (strncmp(label, "CFG", 3) != 0) continue;
|
||||
int idx = atoi(label + 3);
|
||||
Serial.printf("idx is %d\n", idx);
|
||||
if (config_list[idx].type == -1) continue; // skip separator entries, should not happen
|
||||
#endif
|
||||
AsyncWebParameter *value = request->getParam(label, true);
|
||||
if (!value) continue;
|
||||
String strvalue = value->value();
|
||||
if ( strcmp(label, "button_pin") == 0 ||
|
||||
strcmp(label, "button2_pin") == 0) {
|
||||
AsyncWebParameter *touch = request->getParam(strlabel + "#", true);
|
||||
if (touch) {
|
||||
int i = atoi(strvalue.c_str());
|
||||
if (i != -1 && i != 255) i += 128;
|
||||
strvalue = String(i);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
if (config_list[idx].type == -4) { // input button port with "touch" checkbox
|
||||
char tmp[10];
|
||||
snprintf(tmp, 10, "TO%d", idx);
|
||||
|
@ -819,7 +993,10 @@ const char *handleConfigPost(AsyncWebServerRequest *request) {
|
|||
}
|
||||
}
|
||||
Serial.printf("Processing %s=%s\n", config_list[idx].name, strvalue.c_str());
|
||||
int wlen = f.printf("%s=%s\n", config_list[idx].name, strvalue.c_str());
|
||||
#endif
|
||||
Serial.printf("Processing %s=%s\n", label, strvalue.c_str());
|
||||
//int wlen = f.printf("%s=%s\n", config_list[idx].name, strvalue.c_str());
|
||||
int wlen = f.printf("%s=%s\n", label, strvalue.c_str());
|
||||
Serial.printf("Written bytes: %d\n", wlen);
|
||||
}
|
||||
Serial.printf("Flushing file\n");
|
||||
|
@ -1324,7 +1501,7 @@ void SetupAsyncServer() {
|
|||
if (url.endsWith(".gpx"))
|
||||
request->send(200, "application/gpx+xml", sendGPX(request));
|
||||
else {
|
||||
// TODO: set correct type for .js
|
||||
// TODO: set correct type for .js
|
||||
request->send(SPIFFS, url, "text/html");
|
||||
Serial.printf("URL is %s\n", url.c_str());
|
||||
//request->send(404);
|
||||
|
@ -1750,9 +1927,11 @@ void IRAM_ATTR button2ISR() {
|
|||
int getKeyPress() {
|
||||
KeyPress p = button1.pressed;
|
||||
button1.pressed = KP_NONE;
|
||||
#if 0
|
||||
int x = digitalRead(button1.pin);
|
||||
Serial.printf("Debug: bdd1=%ld, bdd2=%ld\b", bdd1, bdd2);
|
||||
Serial.printf("button1 press (dbl:%d) (now:%d): %d at %ld (%d)\n", button1.doublepress, x, p, button1.keydownTime, button1.numberKeyPresses);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -1863,7 +2042,7 @@ void setup()
|
|||
{
|
||||
char buf[12];
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
Serial.begin(921600 /*115200*/);
|
||||
for (int i = 0; i < 39; i++) {
|
||||
int v = gpio_get_level((gpio_num_t)i);
|
||||
Serial.printf("%d:%d ", i, v);
|
||||
|
@ -2111,16 +2290,8 @@ void setup()
|
|||
|
||||
|
||||
// == setup default channel list if qrg.txt read fails =========== //
|
||||
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);
|
||||
Serial.println("No channel config file, using defaults!");
|
||||
Serial.println();
|
||||
#endif
|
||||
setupChannelList();
|
||||
/// not here, done by sonde.setup(): rs41.setup();
|
||||
// == setup default channel list if qrg.txt read fails =========== //
|
||||
#ifndef DISABLE_SX1278
|
||||
|
@ -2242,9 +2413,19 @@ void loopDecoder() {
|
|||
action = (int)(res >> 8);
|
||||
// TODO: update displayed sonde?
|
||||
|
||||
#if 0
|
||||
static int i = 0;
|
||||
if (i++ > 20) {
|
||||
i = 0;
|
||||
rtc_wdt_protect_off();
|
||||
rtc_wdt_disable();
|
||||
heap_caps_dump(MALLOC_CAP_8BIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (action != ACT_NONE) {
|
||||
int newact = sonde.updateState(action);
|
||||
Serial.printf("MAIN: loopDecoder: action %s (%d) => %d [current: main=%d, rxtask=%d]\n", action2text(action), action, newact, sonde.currentSonde, rxtask.currentSonde);
|
||||
Serial.printf("MAIN: loopDecoder: action %02x (%s) => %d [current: main=%d, rxtask=%d]\n", action, action2text(action), newact, sonde.currentSonde, rxtask.currentSonde);
|
||||
action = newact;
|
||||
if (action != 255) {
|
||||
if (action == ACT_DISPLAY_SPECTRUM) {
|
||||
|
@ -2296,7 +2477,7 @@ void loopDecoder() {
|
|||
}
|
||||
|
||||
#if FEATURE_SONDEHUB
|
||||
sondehub_handle_fimport(&shclient);
|
||||
sondehub_reply_handler(&shclient);
|
||||
#endif
|
||||
|
||||
// wifi (axudp) or bluetooth (bttnc) active => send packet
|
||||
|
@ -2905,7 +3086,7 @@ void execOTA() {
|
|||
dispxs = dispys = 1;
|
||||
char uh[17];
|
||||
strncpy(uh, updateHost, 17);
|
||||
uh[16]=0;
|
||||
uh[16] = 0;
|
||||
disp.rdis->drawString(0, 0, uh);
|
||||
} else {
|
||||
disp.rdis->setFont(5);
|
||||
|
@ -2932,34 +3113,42 @@ void execOTA() {
|
|||
|
||||
int type = 0;
|
||||
int res = fetchHTTPheader(&type);
|
||||
if(res < 0) { return; }
|
||||
if (res < 0) {
|
||||
return;
|
||||
}
|
||||
// process data...
|
||||
while(client.available()) {
|
||||
while (client.available()) {
|
||||
// get header...
|
||||
char fn[128];
|
||||
fn[0] = '/';
|
||||
client.readBytesUntil('\n', fn+1, 128);
|
||||
client.readBytesUntil('\n', fn + 1, 128);
|
||||
char *sz = strchr(fn, ' ');
|
||||
if(!sz) { client.stop(); return; }
|
||||
if (!sz) {
|
||||
client.stop();
|
||||
return;
|
||||
}
|
||||
*sz = 0;
|
||||
int len = atoi(sz+1);
|
||||
int len = atoi(sz + 1);
|
||||
Serial.printf("Updating file %s (%d bytes)\n", fn, len);
|
||||
char fnstr[17];
|
||||
memset(fnstr, ' ', 16);
|
||||
strncpy(fnstr, fn, strlen(fn));
|
||||
fnstr[16]=0;
|
||||
fnstr[16] = 0;
|
||||
disp.rdis->drawString(0, 2 * dispys, fnstr);
|
||||
File f = SPIFFS.open(fn, FILE_WRITE);
|
||||
// read sz bytes........
|
||||
while(len>0) {
|
||||
unsigned char buf[1024];
|
||||
int r = client.read(buf, len>1024? 1024:len);
|
||||
if(r==-1) { client.stop(); return; }
|
||||
f.write(buf, r);
|
||||
len -= r;
|
||||
while (len > 0) {
|
||||
unsigned char buf[1024];
|
||||
int r = client.read(buf, len > 1024 ? 1024 : len);
|
||||
if (r == -1) {
|
||||
client.stop();
|
||||
return;
|
||||
}
|
||||
f.write(buf, r);
|
||||
len -= r;
|
||||
}
|
||||
}
|
||||
client.stop();
|
||||
client.stop();
|
||||
|
||||
Serial.print("Connecting to: "); Serial.println(updateHost);
|
||||
// Connect to Update host
|
||||
|
@ -2968,26 +3157,26 @@ void execOTA() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Connection succeeded, fecthing the bin
|
||||
Serial.printf("Fetching bin: %supdate.ino.bin\n", updatePrefix);
|
||||
disp.rdis->drawString(0, 3 * dispys, "Fetching update");
|
||||
// Connection succeeded, fecthing the bin
|
||||
Serial.printf("Fetching bin: %supdate.ino.bin\n", updatePrefix);
|
||||
disp.rdis->drawString(0, 3 * dispys, "Fetching update");
|
||||
|
||||
// Get the contents of the bin file
|
||||
client.printf("GET %supdate.ino.bin HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
updatePrefix, updateHost);
|
||||
// Get the contents of the bin file
|
||||
client.printf("GET %supdate.ino.bin HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
updatePrefix, updateHost);
|
||||
|
||||
// Check what is being sent
|
||||
// Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" +
|
||||
// "Host: " + host + "\r\n" +
|
||||
// "Cache-Control: no-cache\r\n" +
|
||||
// "Connection: close\r\n\r\n");
|
||||
// Check what is being sent
|
||||
// Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" +
|
||||
// "Host: " + host + "\r\n" +
|
||||
// "Cache-Control: no-cache\r\n" +
|
||||
// "Connection: close\r\n\r\n");
|
||||
|
||||
int validType = 0;
|
||||
contentLength = fetchHTTPheader( &validType );
|
||||
if(validType==1) isValidContentType = true;
|
||||
if (validType == 1) isValidContentType = true;
|
||||
|
||||
// Check what is the contentLength and if content type is `application/octet-stream`
|
||||
Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));
|
||||
|
@ -3045,75 +3234,75 @@ void execOTA() {
|
|||
}
|
||||
|
||||
int fetchHTTPheader(int *validType) {
|
||||
int contentLength = -1;
|
||||
unsigned long timeout = millis();
|
||||
while (client.available() == 0) {
|
||||
if (millis() - timeout > 5000) {
|
||||
Serial.println("Client Timeout !");
|
||||
client.stop();
|
||||
int contentLength = -1;
|
||||
unsigned long timeout = millis();
|
||||
while (client.available() == 0) {
|
||||
if (millis() - timeout > 5000) {
|
||||
Serial.println("Client Timeout !");
|
||||
client.stop();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Once the response is available, check stuff
|
||||
|
||||
/*
|
||||
Response Structure
|
||||
HTTP/1.1 200 OK
|
||||
x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0=
|
||||
x-amz-request-id: 2D56B47560B764EC
|
||||
Date: Wed, 14 Jun 2017 03:33:59 GMT
|
||||
Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT
|
||||
ETag: "d2afebbaaebc38cd669ce36727152af9"
|
||||
Accept-Ranges: bytes
|
||||
Content-Type: application/octet-stream
|
||||
Content-Length: 357280
|
||||
Server: AmazonS3
|
||||
|
||||
{{BIN FILE CONTENTS}}
|
||||
|
||||
*/
|
||||
while (client.available()) {
|
||||
// read line till \n
|
||||
String line = client.readStringUntil('\n');
|
||||
// remove space, to check if the line is end of headers
|
||||
line.trim();
|
||||
|
||||
// if the the line is empty,
|
||||
// this is end of headers
|
||||
// break the while and feed the
|
||||
// remaining `client` to the
|
||||
// Update.writeStream();
|
||||
if (!line.length()) {
|
||||
//headers ended
|
||||
break; // and get the OTA started
|
||||
}
|
||||
|
||||
// Check if the HTTP Response is 200
|
||||
// else break and Exit Update
|
||||
if (line.startsWith("HTTP/1.1")) {
|
||||
if (line.indexOf("200") < 0) {
|
||||
Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Once the response is available, check stuff
|
||||
|
||||
/*
|
||||
Response Structure
|
||||
HTTP/1.1 200 OK
|
||||
x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0=
|
||||
x-amz-request-id: 2D56B47560B764EC
|
||||
Date: Wed, 14 Jun 2017 03:33:59 GMT
|
||||
Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT
|
||||
ETag: "d2afebbaaebc38cd669ce36727152af9"
|
||||
Accept-Ranges: bytes
|
||||
Content-Type: application/octet-stream
|
||||
Content-Length: 357280
|
||||
Server: AmazonS3
|
||||
// extract headers here
|
||||
// Start with content length
|
||||
if (line.startsWith("Content-Length: ")) {
|
||||
contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str());
|
||||
Serial.println("Got " + String(contentLength) + " bytes from server");
|
||||
}
|
||||
|
||||
{{BIN FILE CONTENTS}}
|
||||
|
||||
*/
|
||||
while (client.available()) {
|
||||
// read line till \n
|
||||
String line = client.readStringUntil('\n');
|
||||
// remove space, to check if the line is end of headers
|
||||
line.trim();
|
||||
|
||||
// if the the line is empty,
|
||||
// this is end of headers
|
||||
// break the while and feed the
|
||||
// remaining `client` to the
|
||||
// Update.writeStream();
|
||||
if (!line.length()) {
|
||||
//headers ended
|
||||
break; // and get the OTA started
|
||||
}
|
||||
|
||||
// Check if the HTTP Response is 200
|
||||
// else break and Exit Update
|
||||
if (line.startsWith("HTTP/1.1")) {
|
||||
if (line.indexOf("200") < 0) {
|
||||
Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// extract headers here
|
||||
// Start with content length
|
||||
if (line.startsWith("Content-Length: ")) {
|
||||
contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str());
|
||||
Serial.println("Got " + String(contentLength) + " bytes from server");
|
||||
}
|
||||
|
||||
// Next, the content type
|
||||
if (line.startsWith("Content-Type: ")) {
|
||||
String contentType = getHeaderValue(line, "Content-Type: ");
|
||||
Serial.println("Got " + contentType + " payload.");
|
||||
if (contentType == "application/octet-stream") {
|
||||
if(validType) *validType = 1;
|
||||
}
|
||||
// Next, the content type
|
||||
if (line.startsWith("Content-Type: ")) {
|
||||
String contentType = getHeaderValue(line, "Content-Type: ");
|
||||
Serial.println("Got " + contentType + " payload.");
|
||||
if (contentType == "application/octet-stream") {
|
||||
if (validType) *validType = 1;
|
||||
}
|
||||
}
|
||||
return contentLength;
|
||||
}
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3220,17 +3409,13 @@ void sondehub_station_update(WiFiClient *client, struct st_sondehub *conf) {
|
|||
|
||||
// Only send email if provided
|
||||
if (strlen(conf->email) != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_contact_email\": \"%s\",",
|
||||
conf->email);
|
||||
sprintf(w, "\"uploader_contact_email\": \"%s\",", conf->email);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send antenna if provided
|
||||
if (strlen(conf->antenna) != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_antenna\": \"%s\",",
|
||||
conf->antenna);
|
||||
sprintf(w, "\"uploader_antenna\": \"%s\",", conf->antenna);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
|
@ -3248,10 +3433,11 @@ void sondehub_station_update(WiFiClient *client, struct st_sondehub *conf) {
|
|||
}
|
||||
// Otherweise, in FIXED mode we send the fixed position from config (if specified)
|
||||
else if (chase == SH_LOC_FIXED) {
|
||||
if ((!isnan(conf->lat)) && (!isnan(conf->lon))) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%s]",
|
||||
conf->lat, conf->lon, conf->alt[0] ? conf->alt : "null");
|
||||
if ((!isnan(sonde.config.rxlat)) && (!isnan(sonde.config.rxlon))) {
|
||||
if (isnan(sonde.config.rxalt))
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,null]", sonde.config.rxlat, sonde.config.rxlon);
|
||||
else
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]", sonde.config.rxlat, sonde.config.rxlon, (int)sonde.config.rxalt);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
}
|
||||
|
@ -3303,22 +3489,53 @@ const char *dfmSubtypeStrSH[16] = { NULL, NULL, NULL, NULL, NULL, NULL,
|
|||
NULL, NULL
|
||||
};
|
||||
|
||||
void sondehub_handle_fimport(WiFiClient *client) {
|
||||
if (sonde.config.sondehub.fimport[0] != '0') {
|
||||
if (shImport == 0) {
|
||||
sondehub_send_fimport(&shclient);
|
||||
} else if (shImport == 1) {
|
||||
int res = ShFreqImport::shImportHandleReply(&shclient);
|
||||
void sondehub_reply_handler(WiFiClient *client) {
|
||||
// sondehub handler for tasks to be done even if no data is to be sent:
|
||||
// process response messages from sondehub
|
||||
// request frequency list (if active)
|
||||
#define MSG_SIZE 550
|
||||
char rs_msg[MSG_SIZE];
|
||||
|
||||
if(shImport==1) { // we are waiting for a reply to a sondehub frequency import request
|
||||
// while we are waiting, we do nothing else with sondehub...
|
||||
int res = ShFreqImport::shImportHandleReply(&shclient);
|
||||
Serial.printf("ret: %d\n", res);
|
||||
// res==0 means more data is expected, res==1 means complete reply received (or error)
|
||||
if (res == 1) {
|
||||
shImport = 2; // finished
|
||||
shImportInterval = sonde.config.sondehub.fiinterval * 60;
|
||||
}
|
||||
} else if (shImport == 2) {
|
||||
// waiting for next activation...
|
||||
}
|
||||
else {
|
||||
// any reply here belongs to normal telemetry upload, lets just print it.
|
||||
// and wait for a valid HTTP response
|
||||
while(client->available() > 0) {
|
||||
// data is available from remote server, process it...
|
||||
int cnt = client->readBytesUntil('\n', rs_msg, MSG_SIZE - 1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send import requests if needed
|
||||
if (sonde.config.sondehub.fiactive) {
|
||||
if (shImport == 2) {
|
||||
Serial.printf("next sondehub frequncy import in %d seconds\n", shImportInterval);
|
||||
shImportInterval --;
|
||||
if (shImportInterval <= 0) {
|
||||
shImport = 0;
|
||||
}
|
||||
}
|
||||
else if (shImport == 0) {
|
||||
if(shState == SH_CONN_APPENDING || shState == SH_CONN_WAITACK)
|
||||
Serial.printf("Time to request next sondehub import.... but still busy with upload request");
|
||||
else
|
||||
sondehub_send_fimport(&shclient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3330,22 +3547,17 @@ void sondehub_send_fimport(WiFiClient * client) {
|
|||
return;
|
||||
}
|
||||
// It's time to run, so check prerequisites
|
||||
float lat = sonde.config.sondehub.lat, lon = sonde.config.sondehub.lon;
|
||||
float lat = sonde.config.rxlat, lon = sonde.config.rxlon;
|
||||
if (gpsPos.valid) {
|
||||
lat = gpsPos.lat;
|
||||
lon = gpsPos.lon;
|
||||
}
|
||||
|
||||
char *ptr = strchr(sonde.config.sondehub.fimport, '/');
|
||||
shImportInterval = atoi(sonde.config.sondehub.fimport) * 60;
|
||||
int maxdist = 200;
|
||||
int maxage = 60;
|
||||
if (ptr) {
|
||||
maxdist = atoi(ptr + 1);
|
||||
ptr = strchr(ptr + 1, '/');
|
||||
if (ptr) maxage = atoi(ptr + 1);
|
||||
}
|
||||
if ( !isnan(lat) && !isnan(lon) && maxdist > 0 && maxage > 0 && shImportInterval > 0 ) {
|
||||
int maxdist = sonde.config.sondehub.fimaxdist; // km
|
||||
int maxage = sonde.config.sondehub.fimaxage * 60; // fimaxage is hours, shImportSendRequest uses minutes
|
||||
int fiinterval = sonde.config.sondehub.fiinterval;
|
||||
Serial.printf("shimp : %f %f %d %d %d\n", lat, lon, maxdist, maxage, shImportInterval);
|
||||
if ( !isnan(lat) && !isnan(lon) && maxdist > 0 && maxage > 0 && fiinterval > 0 ) {
|
||||
int res = ShFreqImport::shImportSendRequest(&shclient, lat, lon, maxdist, maxage);
|
||||
if (res == 0) shImport = 1; // Request OK: wait for response
|
||||
else shImport = 2; // Request failed: wait interval, then retry
|
||||
|
@ -3361,7 +3573,6 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
|
|||
// 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;
|
||||
|
@ -3381,19 +3592,6 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
|
|||
if (SH_LOC_AUTO_IS_CHASE) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED;
|
||||
}
|
||||
|
||||
// TODO: This should better be called not in sondehub_send_data, but somewhere where it is called even if no new data is decoded
|
||||
// shImport==1: software is waiting for a reply to freq info requst, so reading data is handled elsewhere
|
||||
while (shImport != 1 && client->available() > 0) {
|
||||
// data is available from remote server, process it...
|
||||
int cnt = client->readBytesUntil('\n', rs_msg, MSG_SIZE - 1);
|
||||
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;
|
||||
sondehub_send_fimport(client);
|
||||
}
|
||||
}
|
||||
|
||||
struct tm timeinfo;
|
||||
time_t now;
|
||||
|
@ -3486,32 +3684,42 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
|
|||
if (t) sprintf(w, "\"subtype\": \"%s\",", t);
|
||||
else sprintf(w, "\"subtype\": \"DFMx%X\",", s->subtype); // Unknown subtype
|
||||
w += strlen(w);
|
||||
} else if ( s->type == STYPE_RS41 ) {
|
||||
char buf[11];
|
||||
if(RS41::getSubtype(buf, 11, s)==0) {
|
||||
sprintf(w, "\"subtype\": \"%s\",", buf);
|
||||
w += strlen(w);
|
||||
}
|
||||
}
|
||||
|
||||
// Only send temp & humidity if provided
|
||||
if (((int)s->temperature != 0) && ((int)s->relativeHumidity != 0)) {
|
||||
sprintf(w,
|
||||
"\"temp\": %.3f,"
|
||||
"\"humidity\": %.3f,",
|
||||
float(s->temperature), float(s->relativeHumidity)
|
||||
);
|
||||
// Only send temp if provided
|
||||
if ((int)s->temperature != 0) {
|
||||
sprintf(w, "\"temp\": %.3f,", float(s->temperature));
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send humidity if provided
|
||||
if ((int)s->relativeHumidity != 0) {
|
||||
sprintf(w, "\"humidity\": %.3f,", float(s->relativeHumidity));
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send burst timer if RS41 and not 0
|
||||
if ((realtype == STYPE_RS41) && ((int)s->burstKT != 0)) {
|
||||
sprintf(w, "\"burst_timer\": %d,", (int)s->burstKT);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send antenna if provided
|
||||
if (strlen(conf->antenna) != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_antenna\": \"%s\",",
|
||||
conf->antenna);
|
||||
sprintf(w, "\"uploader_antenna\": \"%s\",", conf->antenna);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// We send GPS position: (a) in CHASE mode, (b) in AUTO mode if no fixed location has been specified in config
|
||||
if (chase == SH_LOC_CHASE) {
|
||||
if (gpsPos.valid && gpsPos.lat != 0 && gpsPos.lon != 0) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%d]",
|
||||
gpsPos.lat, gpsPos.lon, gpsPos.alt);
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]", gpsPos.lat, gpsPos.lon, gpsPos.alt);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
}
|
||||
|
@ -3519,10 +3727,11 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
|
|||
}
|
||||
// Otherweise, in FIXED mode we send the fixed position from config (if specified)
|
||||
else if (chase == SH_LOC_FIXED) {
|
||||
if ((!isnan(conf->lat)) && (!isnan(conf->lon))) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%s]",
|
||||
conf->lat, conf->lon, conf->alt[0] ? conf->alt : "null");
|
||||
if ((!isnan(sonde.config.rxlat)) && (!isnan(sonde.config.rxlon))) {
|
||||
if (isnan(sonde.config.rxalt))
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,null]", sonde.config.rxlat, sonde.config.rxlon);
|
||||
else
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]", sonde.config.rxlat, sonde.config.rxlon, (int)sonde.config.rxalt);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
}
|
||||
|
@ -3575,8 +3784,8 @@ void sondehub_send_header(WiFiClient * client, SondeInfo * s, struct st_sondehub
|
|||
"Host: ");
|
||||
Serial.println(conf->host);
|
||||
Serial.print("accept: text/plain\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
|
||||
client->print("PUT /sondes/telemetry HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
var cfgs = [
|
||||
[ "", "General configuration" ],
|
||||
[ "wifi", "Wifi mode (0=off, 1=client, 2=AP, 3=client or AP autoselect on startup)" ],
|
||||
[ "mdnsname", "Network mDNS name"],
|
||||
[ "ephftp", "FTP server for ephemeris data (RS92 decoder)"],
|
||||
[ "debug", "Debug mode (0/1)" ],
|
||||
[ "maxsonde", "Maxumum number of QRG entries (must be ⋚50)" ],
|
||||
[ "rxlat", "Receiver fixed latitude"],
|
||||
[ "rxlon", "Receiver fixed longitude"],
|
||||
[ "rxalt", "Receiver fixed altitude"],
|
||||
[ "", "OLED/TFT display configuration" ],
|
||||
[ "screenfile", "Screen config (0=automatic; 1-5=predefined; other=custom)" ],
|
||||
[ "display", "Display screens (scan, default, ...)" ],
|
||||
[ "norx_timeout", "No-RX-timeout in seconds (-1=disabled)"],
|
||||
[ "tft_orient", "TFT orientation (0/1/2/3), OLED flip: 3"],
|
||||
[ "", "Spectrum display settings" ],
|
||||
[ "spectrum", "Show spectrum on start (-1=no, 0=forever, >0=time [sec])" ],
|
||||
[ "startfreq", "Start frequency (MHz, default 400)" ],
|
||||
[ "channelbw", "Bandwidth (kHz)" ],
|
||||
[ "marker", "Spectrum MHz marker" ], // maybe remove, assume always ==1?
|
||||
[ "noisefloor", "Spectrum noisefloor" ],
|
||||
[ "", "Receiver configuration" ],
|
||||
[ "freqofs", "RX frequency offset (Hz)"],
|
||||
[ "rs41.agcbw", "RS41 AGC bandwidth"],
|
||||
[ "rs41.rxbw", "RS41 RX bandwidth"],
|
||||
[ "rs92.rxbw", "RS92 RX (and AGC) bandwidth"],
|
||||
[ "rs92.alt2d", "RS92 2D fix default altitude"],
|
||||
[ "dfm.agcbw", "DFM AGC bandwidth"],
|
||||
[ "dfm.rxbw", "DFM RX bandwidth"],
|
||||
[ "m10m20.agcbw", "M10/M20 AGC bandwidth"],
|
||||
[ "m10m20.rxbw", "M10/M20 RX bandwidth"],
|
||||
[ "mp3h.agcbw", "MP3H AGC bandwidth"],
|
||||
[ "mp3h.rxbw", "MP3H RX bandwidth"],
|
||||
[ "", "KISS TNC/AXUDP/AXTCP data feed configuration"],
|
||||
[ "call", "Call"],
|
||||
[ "passcode", "Passcode"],
|
||||
[ "kisstnc.active", "KISS TNC (port 14590) (needs reboot)"],
|
||||
[ "kisstnc.idformat", "KISS TNC ID format"],
|
||||
[ "axudp.active", "AXUDP active"],
|
||||
[ "axudp.host", "AXUDP host"],
|
||||
[ "axudp.port", "AXUDP port"],
|
||||
[ "axudp.idformat", "DFM ID format"],
|
||||
[ "axudp.highrate", "Rate limit"],
|
||||
[ "tcp.active", "APRS TCP active"],
|
||||
[ "tcp.host", "ARPS TCP host"],
|
||||
[ "tcp.port", "APRS TCP port"],
|
||||
[ "tcp.idformat", "DFM ID format"],
|
||||
[ "tcp.highrate", "Rate limit"],
|
||||
[ "", "MQTT data feed configuration"],
|
||||
[ "mqtt.active", "MQTT active (needs reboot)"],
|
||||
[ "mqtt.id", "MQTT client ID"],
|
||||
[ "mqtt.host", "MQTT server hostname"],
|
||||
[ "mqtt.port", "MQTT port"],
|
||||
[ "mqtt.username", "MQTT username"],
|
||||
[ "mqtt.password", "MQTT password"],
|
||||
[ "mqtt.prefix", "MQTT prefix"],
|
||||
[ "", "SondeHub settings"],
|
||||
[ "sondehub.active", "SondeHub reporting (0=disabled, 1=active)"],
|
||||
[ "sondehub.chase", "SondeHub location reporting (0=off, 1=fixed, 2=chase/GPS, 3=auto)"],
|
||||
[ "sondehub.host", "SondeHub host (DO NOT CHANGE)"],
|
||||
[ "sondehub.callsign", "Callsign"],
|
||||
[ "sondehub.antenna", "Antenna (optional, visisble on SondeHub tracker)"],
|
||||
[ "sondehub.email", "SondeHub email (optional, only used to contact in case of upload errors)"],
|
||||
[ "", "SondeHub frequency import" ],
|
||||
[ "sondehub.fiactive", "SondeHub frequency import active (0=disabled, 1=active)" ],
|
||||
[ "sondehub.fiinterval", "Import frequency (minutes, ≥ 5)" ],
|
||||
[ "sondehub.fimaxdist", "Import maximum distance (km, ≤ 500)" ],
|
||||
[ "sondehub.fimaxage", "Import maximum age (hours, ≤ 24)" ],
|
||||
[ "", "Hardware configuration (requires reboot)"],
|
||||
[ "disptype", "Display type (0=OLED/SSD1306, 1=ILI9225, 2=OLED/SH1106, 3=ILI9341, 4=ILI9342)"],
|
||||
[ "oled_sda", "OLED SDA/TFT SDA"],
|
||||
[ "oled_scl", "OLED SCL/TFT CLK"],
|
||||
[ "oled_rst", "OLED RST/TFT RST (needs reboot)"],
|
||||
[ "tft_rs", "TFT RS"],
|
||||
[ "tft_cs", "TFT CS"],
|
||||
[ "tft_spifreq", "TFT SPI speed"],
|
||||
[ "button_pin", "Button input port"],
|
||||
[ "button2_pin", "Button 2 input port"],
|
||||
[ "button2_axp", "Use AXP192 PWR as Button 2"],
|
||||
[ "touch_thresh", "Touch button threshold<br>(0 for calib mode)"],
|
||||
[ "power_pout", "Power control port"],
|
||||
[ "led_pout", "LED output port"],
|
||||
[ "gps_rxd", "GPS RXD pin (-1 to disable)"],
|
||||
[ "gps_txd", "GPS TXD pin (not really needed)"],
|
||||
[ "batt_adc", "Battery measurement pin"],
|
||||
[ "sx1278_ss", "SX1278 SS"],
|
||||
[ "sx1278_miso", "SX1278 MISO"],
|
||||
[ "sx1278_mosi", "SX1278 MOSI"],
|
||||
[ "sx1278_sck", "SX1278 SCK"],
|
||||
];
|
||||
|
||||
function mkcfg(id, key, label, value) {
|
||||
var s = "<tr style=\"visibility: collapse;\" class=\"cfgpanel\"><td>" + label + "</td><td><input name=\"" + key + "\" type=\"text\" value=\"" + value + "\"/></td></tr>\n";
|
||||
return s;
|
||||
}
|
||||
function mkcfgbtn(id, key, label, value) {
|
||||
var touch = "";
|
||||
var v = value;
|
||||
if(v != -1 && (v&128)) {
|
||||
touch = " checked";
|
||||
v = v & 127;
|
||||
}
|
||||
var s = "<tr style=\"visibility:collapse\" class=\"cfgpanel\"><td>" + label + "</td><td><input name=\"" + key + "\" type=\"text\" size=\"3\" value=\"" + v + "\"/>";
|
||||
s += "<input type=\"checkbox\" name=\"" + key + "#\" "+touch+"> Touch</td></tr>\n";
|
||||
return s;
|
||||
}
|
||||
|
||||
function mksep(id,label) {
|
||||
return "<tr class=\"cfgheader\"><th class=\"cfg\" align=\"left\" colspan=\"2\">"+label+"</th></tr>\n";
|
||||
}
|
||||
function rowdisp(id,disp) {
|
||||
var matches = document.querySelectorAll("tr."+id);
|
||||
matches.forEach(function(e) { if(disp) e.hidden=true; else e.removeAttribute('hidden');});
|
||||
hid=id; nid="N"+id;
|
||||
if(!disp) { hid=nid; nid=id; }
|
||||
document.querySelector("span."+hid).hidden=true;
|
||||
document.querySelector("span."+nid).removeAttribute('hidden');
|
||||
}
|
||||
function configTable() {
|
||||
// iterate over cfgs
|
||||
var tab = "<table width=\"100%\"><tr><th>Option</th><th>Value</th></tr>\n";
|
||||
var id=0;
|
||||
for(i=0; i<cfgs.length; i++) {
|
||||
var key = cfgs[i][0];
|
||||
var lbl = cfgs[i][1];
|
||||
if(key) {
|
||||
if(key=="button_pin" || key=="button2_pin") {
|
||||
tab += mkcfgbtn("s"+id, key, lbl, cf.get(key));
|
||||
} else if (key=="display") {
|
||||
tab += mkcfg("s"+id, key, lbl, cf.get(key));
|
||||
tab += "<tr style=\"visibility:collapse\" class=\"cfgpanel\"><td>"+scr+"</td><td></td></tr>"
|
||||
} else {
|
||||
tab += mkcfg("s"+id, key, lbl, cf.get(key));
|
||||
}
|
||||
} else {
|
||||
id++;
|
||||
tab += mksep("s"+id, lbl);
|
||||
}
|
||||
}
|
||||
tab += "</table>";
|
||||
var cfgdiv = document.getElementById("cfgtab");
|
||||
cfgdiv.innerHTML = tab;
|
||||
// enable collapse / expand of items below a header
|
||||
var acc = document.getElementsByClassName("cfgheader");
|
||||
for(i=0; i<acc.length; i++) {
|
||||
acc[i].firstChild.innerHTML = "[+] " + acc[i].firstChild.innerHTML;
|
||||
acc[i].addEventListener("click", function() {
|
||||
achar = "[+]";
|
||||
if(this.classList.toggle("active")) achar = "[\u2212]";
|
||||
this.firstChild.innerHTML = achar + this.firstChild.innerHTML.substring(3);
|
||||
var panel = this;
|
||||
console.log(panel);
|
||||
while( panel = panel.nextElementSibling) {
|
||||
console.log(panel);
|
||||
if ( panel.className!="cfgpanel") { break; }
|
||||
if(panel.style.visibility==="collapse") {
|
||||
panel.style.visibility="visible";
|
||||
} else {
|
||||
console.log("none");
|
||||
panel.style.visibility="collapse";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
acc[0].click();
|
||||
}
|
|
@ -24,6 +24,9 @@
|
|||
#oled_rst=16
|
||||
#tft_rs=2
|
||||
#tft_cs=0
|
||||
rxlat=
|
||||
rxlon=
|
||||
rxalt=
|
||||
tft_orient=1
|
||||
#tft_spifreq=40000000
|
||||
#gps_rxd=-1
|
||||
|
@ -124,12 +127,15 @@ sondehub.active=0
|
|||
sondehub.chase=3
|
||||
sondehub.host=api.v2.sondehub.org
|
||||
sondehub.callsign=CHANGEME_RDZTTGO
|
||||
sondehub.lat=
|
||||
sondehub.lon=
|
||||
sondehub.alt=
|
||||
sondehub.antenna=
|
||||
sondehub.email=
|
||||
sondehub.fimport=0/100/60
|
||||
#-------------------------------#
|
||||
# Sondehub freq import settings
|
||||
#-------------------------------#
|
||||
shfimp.active=0
|
||||
shfimp.interval=60
|
||||
shfimp.maxdist=150
|
||||
shfimp.maxage=6
|
||||
#-------------------------------#
|
||||
# EOF
|
||||
#-------------------------------#
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
||||
<script>var mapcenter=[%MAPCENTER%];</script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet.marker.slideto@0.2.0/Leaflet.Marker.SlideTo.js"></script>
|
||||
|
|
|
@ -40,7 +40,8 @@ $(document).ready(function(){
|
|||
}
|
||||
};
|
||||
|
||||
map.setView([51.163361,10.447683], 5); // Mitte DE
|
||||
if(mapcenter) map.setView(mapcenter, 5);
|
||||
else map.setView([51.163361,10.447683], 5); // Mitte DE
|
||||
|
||||
var reddot = '<span class="ldot rbg"></span>';
|
||||
var yellowdot = '<span class="ldot ybg"></span>';
|
||||
|
@ -69,11 +70,12 @@ $('.leaflet-footer').append(footer);
|
|||
|
||||
var statbar = '';
|
||||
headtxt = function(data,stat) {
|
||||
console.log(data);
|
||||
var staticon = (stat == '1')?greendot:yellowdot;
|
||||
statbar = staticon + statbar;
|
||||
if ((statbar.length) > 10*greendot.length) { statbar = statbar.substring(0,10*greendot.length); }
|
||||
if (data.lat == '0.000000') { return false; }
|
||||
if (data.id) {
|
||||
if (data.res == 0) {
|
||||
$('#sonde_id').html(data.id);
|
||||
$('#sonde_alt').html(data.alt);
|
||||
$('#sonde_climb').html(data.climb);
|
||||
|
@ -154,9 +156,10 @@ headtxt = function(data,stat) {
|
|||
|
||||
draw = function(data) {
|
||||
var stat;
|
||||
console.log(data);
|
||||
if (data.id) {
|
||||
|
||||
if ((data.lat != '0.000000' && data.lon != '0.000000') && (JSON.stringify(data) != JSON.stringify(last_data)) ) {
|
||||
// data.res: 0: ok 1: no rx (timeout), 2: crc err, >2 some other error
|
||||
if ((data.lat != '0.000000' && data.lon != '0.000000') && (data.res==0)) { //JSON.stringify(data) != JSON.stringify(last_data)) ) {
|
||||
var location = [data.lat,data.lon,data.alt];
|
||||
if (!marker) {
|
||||
map.setView(location, 14);
|
||||
|
@ -230,8 +233,10 @@ headtxt = function(data,stat) {
|
|||
|
||||
get_data = function() {
|
||||
$('#status').html(reddot);
|
||||
console.log("get_data called");
|
||||
$.ajax({url: 'live.json', success: (function( data ) {
|
||||
if (typeof data != "object") { data = $.parseJSON(data); }
|
||||
console.log(data);
|
||||
if (data.sonde) {
|
||||
draw(data.sonde);
|
||||
} else {
|
||||
|
@ -321,7 +326,7 @@ headtxt = function(data,stat) {
|
|||
var datetime = m.getUTCFullYear() + "-" + az(m.getUTCMonth()+1) + "-" + az(m.getUTCDate()) + "T" +
|
||||
az(m.getUTCHours()) + ":" + az(m.getUTCMinutes()) + ":" + az(m.getUTCSeconds()) + "Z";
|
||||
var url = 'https://predict.cusf.co.uk/api/v1/';
|
||||
url += '?launch_latitude='+data.lat + '&launch_longitude='+fix_lon(data.lon);
|
||||
url += '?launch_latitude='+data.lat + '&launch_longitude='+tawhiri_lon(data.lon);
|
||||
url += '&launch_altitude='+data.alt + '&launch_datetime='+datetime;
|
||||
url += '&ascent_rate='+ascent + '&burst_altitude=' + burst + '&descent_rate='+descent;
|
||||
|
||||
|
@ -333,11 +338,11 @@ headtxt = function(data,stat) {
|
|||
draw_predict = function(prediction,data) {
|
||||
var ascending = prediction.prediction[0].trajectory;
|
||||
var highest = ascending[ascending.length-1];
|
||||
var highest_location = [highest.latitude,fix_lon(highest.longitude)];
|
||||
var highest_location = [highest.latitude,sanitize_lon(highest.longitude)];
|
||||
|
||||
var descending = prediction.prediction[1].trajectory;
|
||||
var landing = descending[descending.length-1];
|
||||
var landing_location = [landing.latitude,fix_lon(landing.longitude)];
|
||||
var landing_location = [landing.latitude,sanitize_lon(landing.longitude)];
|
||||
|
||||
if (!marker_landing) {
|
||||
marker_landing = L.marker(landing_location,{icon: icon_landing}).addTo(map)
|
||||
|
@ -353,7 +358,7 @@ headtxt = function(data,stat) {
|
|||
dots_predict=[];
|
||||
|
||||
if (data.climb > 0) {
|
||||
ascending.forEach(p => dots_predict.push([p.latitude,fix_lon(p.longitude)]));
|
||||
ascending.forEach(p => dots_predict.push([p.latitude,sanitize_lon(p.longitude)]));
|
||||
|
||||
if (!marker_burst) {
|
||||
marker_burst = L.marker(highest_location,{icon:icon_burst}).addTo(map).bindPopup(poptxt('burst',highest),{closeOnClick:false, autoPan:false});
|
||||
|
@ -365,7 +370,7 @@ headtxt = function(data,stat) {
|
|||
}
|
||||
}
|
||||
|
||||
descending.forEach(p => dots_predict.push([p.latitude,fix_lon(p.longitude)]));
|
||||
descending.forEach(p => dots_predict.push([p.latitude,sanitize_lon(p.longitude)]));
|
||||
line_predict.setLatLngs(dots_predict);
|
||||
|
||||
if (data.climb > 0) {
|
||||
|
@ -378,16 +383,19 @@ headtxt = function(data,stat) {
|
|||
clearTimeout(predictor);
|
||||
predictor = setTimeout(function() {get_predict(last_data);}, predictor_time*1000);
|
||||
};
|
||||
|
||||
fix_lon = function(lon) {
|
||||
|
||||
sanitize_lon = function(lon) {
|
||||
if (lon > 180) { return lon - 360; }
|
||||
return lon;
|
||||
}
|
||||
tawhiri_lon = function(lon) {
|
||||
if (lon < 0) { return lon + 360; }
|
||||
return lon;
|
||||
};
|
||||
}
|
||||
|
||||
poptxt = function(t,i) {
|
||||
var lat_input = (i.id)?i.lat:i.latitude;
|
||||
var lon_input = (i.id)?i.lon:i.longitude;
|
||||
var lon_input = sanitize_lon((i.id)?i.lon:i.longitude);
|
||||
|
||||
var lat = Math.round(lat_input * 1000000) / 1000000;
|
||||
var lon = Math.round(lon_input * 1000000) / 1000000;
|
||||
|
|
|
@ -4,6 +4,16 @@ body, html {
|
|||
font-family: Arial;
|
||||
}
|
||||
|
||||
.active, .cfgheader:hover {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.cfgpanel {
|
||||
}
|
||||
|
||||
th.cfg {
|
||||
padding:5pt
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
|
@ -195,9 +195,6 @@ static void Gencrctab(void)
|
|||
|
||||
int RS41::setup(float frequency)
|
||||
{
|
||||
#if RS41_DEBUG
|
||||
Serial.println("Setup sx1278 for RS41 sonde");
|
||||
#endif
|
||||
if(!initialized) {
|
||||
Gencrctab();
|
||||
initrsc();
|
||||
|
@ -216,11 +213,6 @@ int RS41::setup(float frequency)
|
|||
RS41_DBG(Serial.println("Setting bitrate 4800bit/s FAILED"));
|
||||
return 1;
|
||||
}
|
||||
#if RS41_DEBUG
|
||||
float br = sx1278.getBitrate();
|
||||
Serial.print("Exact bitrate is ");
|
||||
Serial.println(br);
|
||||
#endif
|
||||
|
||||
if(sx1278.setAFCBandwidth(sonde.config.rs41.agcbw)!=0) {
|
||||
RS41_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs41.agcbw));
|
||||
|
@ -255,21 +247,11 @@ int RS41::setup(float frequency)
|
|||
RS41_DBG(Serial.println("Setting Packet config FAILED"));
|
||||
return 1;
|
||||
}
|
||||
#if RS41_DEBUG
|
||||
Serial.print("RS41: setting RX frequency to ");
|
||||
Serial.println(frequency);
|
||||
#endif
|
||||
int retval = sx1278.setFrequency(frequency);
|
||||
dpos = 0;
|
||||
|
||||
#if RS41_DEBUG
|
||||
RS41_DBG(Serial.println("Setting SX1278 config for RS41 finished\n"); Serial.println());
|
||||
#endif
|
||||
sx1278.clearIRQFlags();
|
||||
|
||||
// the following is already done in receivePacketTimeout()
|
||||
// sx1278.setPayloadLength(RS41MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
|
||||
// sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -517,6 +499,7 @@ void ProcessSubframe( byte *subframeBytes, int subframeNumber ) {
|
|||
}
|
||||
memcpy( s->rawData+16*subframeNumber, subframeBytes, 16);
|
||||
s->valid |= (1ULL << subframeNumber);
|
||||
Serial.printf("subframe %d; valid: %x%032x\n", subframeNumber, (uint32_t)(s->valid>>32), (uint32_t)s->valid);
|
||||
// subframeReceived[subframeNumber] = true; // mark this row of the total subframe as complete
|
||||
|
||||
#if 0
|
||||
|
@ -846,11 +829,24 @@ static uint8_t scramble[64] = {150U,131U,62U,81U,177U,73U,8U,152U,50U,5U,89U,
|
|||
int RS41::receive() {
|
||||
sx1278.setPayloadLength(RS41MAXLEN-8);
|
||||
int e = sx1278.receivePacketTimeout(1000, data+8);
|
||||
#if 1
|
||||
if(e) { /*Serial.println("TIMEOUT");*/ return RX_TIMEOUT; }
|
||||
|
||||
for(int i=0; i<RS41MAXLEN; i++) { data[i] = reverse(data[i]); }
|
||||
for(int i=0; i<RS41MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
|
||||
return decode41(data, RS41MAXLEN);
|
||||
#else
|
||||
// FAKE testing data
|
||||
SondeInfo *si = sonde.si();
|
||||
si->lat = 48;
|
||||
si->lon = -100;
|
||||
si->alt = 30000;
|
||||
si->vs = 3.4;
|
||||
si->validPos = 0x7f;
|
||||
si->validID = 1;
|
||||
strcpy(si->id, "A1234");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int RS41::waitRXcomplete() {
|
||||
|
@ -859,4 +855,17 @@ int RS41::waitRXcomplete() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// copy variant string to buf (max buflen chars; buflen should be 11
|
||||
// return 0 if subtype is available, -1 if not
|
||||
int RS41::getSubtype(char *buf, int buflen, SondeInfo *si) {
|
||||
struct subframeBuffer *sf = (struct subframeBuffer *)si->extra;
|
||||
if(!sf) return -1;
|
||||
if( (sf->valid & (3<<21)) != (3<<21) ) return -1; // or 1 instead of 3 for the first 8 chars only, as in autorx?
|
||||
if(buflen>11) buflen=11; // then buflen should be capped at 9 (8+trailing \0)
|
||||
strncpy(buf, sf->value.names.variant, buflen);
|
||||
buf[buflen-1]=0;
|
||||
if(*buf==0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RS41 rs41 = RS41();
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef inttypes_h
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#include "Sonde.h"
|
||||
|
||||
/* Main class */
|
||||
class RS41
|
||||
|
@ -61,6 +62,8 @@ public:
|
|||
int waitRXcomplete();
|
||||
//int receiveFrame();
|
||||
|
||||
static int getSubtype(char *buf, int buflen, SondeInfo *si);
|
||||
|
||||
int use_ecc = 1;
|
||||
};
|
||||
|
||||
|
|
|
@ -114,10 +114,12 @@ void ShFreqImport::cleanup() {
|
|||
#define BUFLEN 128
|
||||
#define VALLEN 20
|
||||
int ShFreqImport::handleChar(char c) {
|
||||
Serial.print(c);
|
||||
switch(importState) {
|
||||
case START:
|
||||
// wait for initial '{'
|
||||
if(c=='{') {
|
||||
Serial.println("{ found");
|
||||
lat = NAN; lon = NAN; freq = NAN; *type = 0;
|
||||
importState++;
|
||||
}
|
||||
|
@ -125,7 +127,11 @@ int ShFreqImport::handleChar(char c) {
|
|||
case BEFOREID:
|
||||
// what for first '"' in { "A1234567" : { ... } }; or detect end
|
||||
if(c=='"') { idpos = 0; importState++; }
|
||||
if(c=='}') { importState = ENDREACHED; }
|
||||
if(c=='}') {
|
||||
importState = ENDREACHED;
|
||||
cleanup();
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case COPYID:
|
||||
// copy ID "A1234567" until second '"' is earched
|
||||
|
@ -196,6 +202,7 @@ int ShFreqImport::handleChar(char c) {
|
|||
else if (c=='}') { importState = ENDREACHED; cleanup(); return 1; }
|
||||
break;
|
||||
case ENDREACHED:
|
||||
Serial.println("REPLY: END REACHED");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -228,6 +235,7 @@ int ShFreqImport::shImportSendRequest(WiFiClient *client, float lat, float lon,
|
|||
|
||||
// return 0 if more data should be read (later), 1 if finished (close connection...)
|
||||
int ShFreqImport::shImportHandleReply(WiFiClient *client) {
|
||||
if(!client->connected()) return 1;
|
||||
while(client->available()) {
|
||||
int res = handleChar(client->read());
|
||||
if(res) return res;
|
||||
|
|
|
@ -80,6 +80,10 @@ void Sonde::defaultConfig() {
|
|||
|
||||
sondeList = (SondeInfo *)malloc((MAXSONDE+1)*sizeof(SondeInfo));
|
||||
memset(sondeList, 0, (MAXSONDE+1)*sizeof(SondeInfo));
|
||||
for(int i=0; i<(MAXSONDE+1); i++) {
|
||||
sondeList[i].freq=400;
|
||||
sondeList[i].type=STYPE_RS41;
|
||||
}
|
||||
config.touch_thresh = 70;
|
||||
config.led_pout = -1;
|
||||
config.power_pout = -1;
|
||||
|
@ -263,6 +267,13 @@ void Sonde::defaultConfig() {
|
|||
extern struct st_configitems config_list[];
|
||||
extern const int N_CONFIG;
|
||||
|
||||
void Sonde::checkConfig() {
|
||||
if(config.maxsonde > MAXSONDE) config.maxsonde = MAXSONDE;
|
||||
if(config.sondehub.fiinterval<5) config.sondehub.fiinterval = 5;
|
||||
if(config.sondehub.fimaxdist>500) config.sondehub.fimaxdist = 500;
|
||||
if(config.sondehub.fimaxdist==0) config.sondehub.fimaxdist = 150;
|
||||
if(config.sondehub.fimaxage==0) config.sondehub.fimaxage = 2;
|
||||
}
|
||||
void Sonde::setConfig(const char *cfg) {
|
||||
while(*cfg==' '||*cfg=='\t') cfg++;
|
||||
if(*cfg=='#') return;
|
||||
|
@ -288,11 +299,14 @@ void Sonde::setConfig(const char *cfg) {
|
|||
case -3: // integer (boolean on/off swith in web form)
|
||||
case -2: // integer (ID type)
|
||||
*(int *)config_list[i].data = atoi(val);
|
||||
if(config.maxsonde > MAXSONDE) config.maxsonde = MAXSONDE;
|
||||
break;
|
||||
case -7: // double
|
||||
*(double *)config_list[i].data = *val==0 ? NAN : atof(val);
|
||||
{
|
||||
double d = atof(val);
|
||||
if(*val == 0 || d==0) d = NAN;
|
||||
*(double *)config_list[i].data = d;
|
||||
break;
|
||||
}
|
||||
case -6: // display list
|
||||
{
|
||||
int idx = 0;
|
||||
|
@ -343,14 +357,17 @@ void Sonde::addSonde(float frequency, SondeType type, int active, char *launchsi
|
|||
Serial.printf("Adding %f - %d - %d - %s\n", frequency, type, active, launchsite);
|
||||
// reset all data if type or frequency has changed
|
||||
if(type != sondeList[nSonde].type || frequency != sondeList[nSonde].freq) {
|
||||
//TODO: Check for potential race condition with decoders
|
||||
// do not clear extra while decoder is potentiall still accessing it!
|
||||
if(sondeList[nSonde].extra) free(sondeList[nSonde].extra);
|
||||
memset(&sondeList[nSonde], 0, sizeof(SondeInfo));
|
||||
sondeList[nSonde].type = type;
|
||||
sondeList[nSonde].typestr[0] = 0;
|
||||
sondeList[nSonde].freq = frequency;
|
||||
memcpy(sondeList[nSonde].rxStat, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", 18); // unknown/undefined
|
||||
}
|
||||
sondeList[nSonde].type = type;
|
||||
sondeList[nSonde].typestr[0] = 0;
|
||||
sondeList[nSonde].freq = frequency;
|
||||
sondeList[nSonde].active = active;
|
||||
strncpy(sondeList[nSonde].launchsite, launchsite, 17);
|
||||
memcpy(sondeList[nSonde].rxStat, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", 18); // unknown/undefined
|
||||
nSonde++;
|
||||
}
|
||||
|
||||
|
@ -379,7 +396,7 @@ void Sonde::nextRxSonde() {
|
|||
if(rxtask.currentSonde>=config.maxsonde) rxtask.currentSonde=0;
|
||||
}
|
||||
}
|
||||
Serial.printf("nextRxSonde: %d\n", rxtask.currentSonde);
|
||||
//Serial.printf("nextRxSonde: %d\n", rxtask.currentSonde);
|
||||
}
|
||||
void Sonde::nextRxFreq(int addkhz) {
|
||||
// last entry is for the variable frequency
|
||||
|
@ -409,7 +426,7 @@ void Sonde::setup() {
|
|||
}
|
||||
|
||||
// update receiver config
|
||||
Serial.print("Sonde.setup() on sonde index ");
|
||||
Serial.print("Sonde::setup() start on index ");
|
||||
Serial.println(rxtask.currentSonde);
|
||||
switch(sondeList[rxtask.currentSonde].type) {
|
||||
case STYPE_RS41:
|
||||
|
@ -433,7 +450,7 @@ void Sonde::setup() {
|
|||
int freq = (int)sx1278.getFrequency();
|
||||
int afcbw = (int)sx1278.getAFCBandwidth();
|
||||
int rxbw = (int)sx1278.getRxBandwidth();
|
||||
Serial.printf("Sonde.setup(): Freq %d, AFC BW: %d, RX BW: %d\n", freq, afcbw, rxbw);
|
||||
Serial.printf("Sonde::setup() done: Type %s Freq %f, AFC BW: %d, RX BW: %d\n", sondeTypeStr[sondeList[rxtask.currentSonde].type], 0.000001*freq, afcbw, rxbw);
|
||||
|
||||
// reset rxtimer / norxtimer state
|
||||
sonde.sondeList[sonde.currentSonde].lastState = -1;
|
||||
|
@ -472,7 +489,7 @@ void Sonde::receive() {
|
|||
}
|
||||
} else { // RX not ok
|
||||
if(res==RX_ERROR) flashLed(100);
|
||||
Serial.printf("RX result %d (%s), laststate was %d\n", res, (res<=3)?RXstr[res]:"?", si->lastState);
|
||||
//Serial.printf("Sonde::receive(): result %d (%s), laststate was %d\n", res, (res<=3)?RXstr[res]:"?", si->lastState);
|
||||
if(si->lastState != 0) {
|
||||
si->norxStart = millis();
|
||||
si->lastState = 0;
|
||||
|
@ -509,8 +526,8 @@ void Sonde::receive() {
|
|||
rxtask.activate = ACT_SONDE(rxtask.currentSonde);
|
||||
}
|
||||
}
|
||||
Serial.printf("Sonde:receive(): result %d (%s), event %02x => action %02x\n", res, (res<=3)?RXstr[res]:"?", event, action);
|
||||
res = (action<<8) | (res&0xff);
|
||||
Serial.printf("Sonde:receive(): Event %02x: action %02x, res %02x => %04x\n", event, action, res&0xff, res);
|
||||
// let waitRXcomplete resume...
|
||||
rxtask.receiveResult = res;
|
||||
}
|
||||
|
@ -574,22 +591,22 @@ uint8_t Sonde::timeoutEvent(SondeInfo *si) {
|
|||
now, si->norxStart, disp.layout->timeouts[2], si->lastState);
|
||||
#endif
|
||||
if(disp.layout->timeouts[0]>=0 && now - si->viewStart >= disp.layout->timeouts[0]) {
|
||||
Serial.println("Sonde.timeoutEvent: View");
|
||||
Serial.println("Sonde::timeoutEvent: View");
|
||||
return EVT_VIEWTO;
|
||||
}
|
||||
if(si->lastState==1 && disp.layout->timeouts[1]>=0 && now - si->rxStart >= disp.layout->timeouts[1]) {
|
||||
Serial.println("Sonde.timeoutEvent: RX");
|
||||
Serial.println("Sonde::timeoutEvent: RX");
|
||||
return EVT_RXTO;
|
||||
}
|
||||
if(si->lastState==0 && disp.layout->timeouts[2]>=0 && now - si->norxStart >= disp.layout->timeouts[2]) {
|
||||
Serial.println("Sonde.timeoutEvent: NORX");
|
||||
Serial.println("Sonde::timeoutEvent: NORX");
|
||||
return EVT_NORXTO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Sonde::updateState(uint8_t event) {
|
||||
Serial.printf("Sonde::updateState for event %d\n", event);
|
||||
//Serial.printf("Sonde::updateState for event %02x\n", event);
|
||||
// No change
|
||||
if(event==ACT_NONE) return 0xFF;
|
||||
|
||||
|
|
|
@ -191,12 +191,12 @@ struct st_sondehub {
|
|||
int chase;
|
||||
char host[64];
|
||||
char callsign[64];
|
||||
double lat;
|
||||
double lon;
|
||||
char alt[20];
|
||||
char antenna[64];
|
||||
char email[64];
|
||||
char fimport[20];
|
||||
int fiactive;
|
||||
int fiinterval;
|
||||
int fimaxdist;
|
||||
int fimaxage;
|
||||
};
|
||||
|
||||
// to be extended
|
||||
|
@ -228,6 +228,9 @@ typedef struct st_rdzconfig {
|
|||
int sx1278_sck; // SPI SCK for sx1278
|
||||
// software configuration
|
||||
int debug; // show port and config options after reboot
|
||||
double rxlat;
|
||||
double rxlon;
|
||||
double rxalt;
|
||||
int wifi; // connect to known WLAN 0=skip
|
||||
int screenfile;
|
||||
int8_t display[30]; // list of display mode (0:scanner, 1:default, 2,... additional modes)
|
||||
|
@ -261,7 +264,7 @@ typedef struct st_rdzconfig {
|
|||
|
||||
struct st_configitems {
|
||||
const char *name;
|
||||
const char *label;
|
||||
// const char *label; => now handled in JS
|
||||
int type; // 0: numeric; i>0 string of length i; -1: separator; -2: type selector
|
||||
void *data;
|
||||
};
|
||||
|
@ -271,7 +274,7 @@ extern struct st_configitems config_list[];
|
|||
extern const int N_CONFIG;
|
||||
|
||||
|
||||
#define MAXSONDE 99
|
||||
#define MAXSONDE 50
|
||||
|
||||
extern int fingerprintValue[];
|
||||
extern const char *fingerprintText[];
|
||||
|
@ -292,6 +295,7 @@ public:
|
|||
|
||||
Sonde();
|
||||
void defaultConfig();
|
||||
void checkConfig();
|
||||
void setConfig(const char *str);
|
||||
|
||||
void clearSonde();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const char *version_name = "rdzTTGOsonde";
|
||||
const char *version_id = "devel20210917";
|
||||
const char *version_id = "devel20210919";
|
||||
const int SPIFFS_MAJOR=2;
|
||||
const int SPIFFS_MINOR=16;
|
||||
|
|
Ładowanie…
Reference in New Issue