kopia lustrzana https://github.com/dl9rdz/rdz_ttgo_sonde
reorg sondehub
rodzic
61b733cf92
commit
af4940c889
|
@ -1,5 +1,6 @@
|
|||
#include "features.h"
|
||||
#include "version.h"
|
||||
#include "core.h"
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
@ -42,6 +43,9 @@
|
|||
#if FEATURE_APRS
|
||||
#include "src/conn-aprs.h"
|
||||
#endif
|
||||
#if FEATURE_SONDEHUB
|
||||
#include "src/conn-sondehub.h"
|
||||
#endif
|
||||
|
||||
//#define ESP_MEM_DEBUG 1
|
||||
//int e;
|
||||
|
@ -78,14 +82,15 @@ WiFiClient client;
|
|||
const char *sondeTypeStrSH[NSondeTypes] = { "DFM", "RS41", "RS92", "Mxx"/*never sent*/, "M10", "M20", "MRZ" };
|
||||
|
||||
|
||||
#if FEATURE_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
|
||||
int shImportInterval = 0;
|
||||
char shImport = 0;
|
||||
unsigned long time_last_update = 0;
|
||||
#endif
|
||||
// moved to connSondehub.cpp
|
||||
//#if FEATURE_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
|
||||
//int shImportInterval = 0;
|
||||
//char shImport = 0;
|
||||
//unsigned long time_last_update = 0;
|
||||
//#endif
|
||||
|
||||
// JSON over TCP for communicating with the rdzSonde (rdzwx-go) Android app
|
||||
WiFiServer rdzserver(14570);
|
||||
|
@ -438,32 +443,6 @@ const char *createWIFIForm() {
|
|||
return message;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// moved to map.html (active warning is still TODO
|
||||
const char *createSondeHubMap() {
|
||||
SondeInfo *s = &sonde.sondeList[0];
|
||||
char *ptr = message;
|
||||
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
|
||||
HTMLBODY(ptr, "map.html");
|
||||
if (!sonde.config.sondehub.active) {
|
||||
strcat(ptr, "<div class=\"warning\">NOTE: SondeHub uploading is not enabled, detected sonde will not be visable on map</div>");
|
||||
if ((*s->d.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->d.ser);
|
||||
}
|
||||
} else {
|
||||
if ((*s->d.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->d.ser);
|
||||
}
|
||||
}
|
||||
HTMLBODYEND(ptr);
|
||||
return message;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *handleWIFIPost(AsyncWebServerRequest * request) {
|
||||
char label[10];
|
||||
// parameters: a_i, f_1, t_i (active/frequency/type)
|
||||
|
@ -602,9 +581,6 @@ void setupConfigData() {
|
|||
sonde.setConfig(line.c_str());
|
||||
}
|
||||
sonde.checkConfig(); // eliminate invalid entries
|
||||
#if FEATURE_SONDEHUB
|
||||
shImportInterval = 5; // refresh now in 5 seconds
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -2062,10 +2038,6 @@ void loopDecoder() {
|
|||
Serial.println("");
|
||||
}
|
||||
|
||||
#if FEATURE_SONDEHUB
|
||||
sondehub_reply_handler(&shclient);
|
||||
#endif
|
||||
|
||||
// wifi active and good packet received => send packet
|
||||
SondeInfo *s = &sonde.sondeList[rxtask.receiveSonde];
|
||||
if ((res & 0xff) == 0 && connected) {
|
||||
|
@ -2076,40 +2048,6 @@ void loopDecoder() {
|
|||
#if FEATURE_APRS
|
||||
connAPRS.updateSonde(s);
|
||||
#endif
|
||||
#if 0
|
||||
// moved to conn-aprs.cpp
|
||||
char *str = aprs_senddata(s, sonde.config.call, sonde.config.objcall, sonde.config.udpfeed.symbol);
|
||||
char raw[201];
|
||||
int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
|
||||
Serial.println("Sending AXUDP");
|
||||
//Serial.println(raw);
|
||||
udp.beginPacket(sonde.config.udpfeed.host, sonde.config.udpfeed.port);
|
||||
udp.write((const uint8_t *)raw, rawlen);
|
||||
udp.endPacket();
|
||||
if (tncclient.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);
|
||||
}
|
||||
if (sonde.config.tcpfeed.active) {
|
||||
static unsigned long lasttcp = 0;
|
||||
if ( tcpclient.disconnected()) {
|
||||
tcpclient.connect(sonde.config.tcpfeed.host, sonde.config.tcpfeed.port);
|
||||
}
|
||||
else if ( tcpclient.connected() ) {
|
||||
unsigned long now = millis();
|
||||
Serial.printf("aprs: now-last = %ld\n", (now - lasttcp));
|
||||
if ( (now - lasttcp) > sonde.config.tcpfeed.highrate * 1000L ) {
|
||||
strcat(str, "\r\n");
|
||||
Serial.print(str);
|
||||
tcpclient.write(str, strlen(str));
|
||||
lasttcp = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FEATURE_CHASEMAPPER
|
||||
if (sonde.config.cm.active) {
|
||||
|
@ -2119,7 +2057,8 @@ void loopDecoder() {
|
|||
}
|
||||
#if FEATURE_SONDEHUB
|
||||
if (sonde.config.sondehub.active) {
|
||||
sondehub_send_data(&shclient, s, &sonde.config.sondehub);
|
||||
connSondehub.updateSonde( s ); // invoke sh_send_data....
|
||||
// sondehub_send_data(&shclient, s, &sonde.config.sondehub);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2129,20 +2068,17 @@ void loopDecoder() {
|
|||
|
||||
} else {
|
||||
#if FEATURE_SONDEHUB
|
||||
sondehub_finish_data(&shclient, s, &sonde.config.sondehub);
|
||||
connSondehub.updateSonde( NULL );
|
||||
// sondehub_finish_data(&shclient, s, &sonde.config.sondehub);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Send own position periodically
|
||||
#if FEATURE_MQTT
|
||||
connMQTT.updateStation( NULL );
|
||||
#endif
|
||||
#if FEATURE_APRS
|
||||
connAPRS.updateStation( NULL );
|
||||
//if (sonde.config.tcpfeed.active) {
|
||||
// aprs_station_update();
|
||||
//}
|
||||
#endif
|
||||
// always send data, even if not valid....
|
||||
if (rdzclient.connected()) {
|
||||
|
@ -2279,9 +2215,10 @@ String translateEncryptionType(wifi_auth_mode_t encryptionType) {
|
|||
}
|
||||
}
|
||||
|
||||
enum t_wifi_state { WIFI_DISABLED, WIFI_SCAN, WIFI_CONNECT, WIFI_CONNECTED, WIFI_APMODE };
|
||||
// in core.h
|
||||
//enum t_wifi_state { WIFI_DISABLED, WIFI_SCAN, WIFI_CONNECT, WIFI_CONNECTED, WIFI_APMODE };
|
||||
|
||||
static t_wifi_state wifi_state = WIFI_DISABLED;
|
||||
t_wifi_state wifi_state = WIFI_DISABLED;
|
||||
|
||||
void enableNetwork(bool enable) {
|
||||
if (enable) {
|
||||
|
@ -2299,21 +2236,22 @@ void enableNetwork(bool enable) {
|
|||
connMQTT.netsetup();
|
||||
#endif
|
||||
#if FEATURE_SONDEHUB
|
||||
if (sonde.config.sondehub.active && wifi_state != WIFI_APMODE) {
|
||||
time_last_update = millis() + 1000; /* force sending update */
|
||||
sondehub_station_update(&shclient, &sonde.config.sondehub);
|
||||
}
|
||||
//if (sonde.config.sondehub.active && wifi_state != WIFI_APMODE) {
|
||||
// time_last_update = millis() + 1000; /* force sending update */
|
||||
// sondehub_station_update(&shclient, &sonde.config.sondehub);
|
||||
//}
|
||||
connSondehub.netsetup();
|
||||
#endif
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
connected = true;
|
||||
#if FEATURE_APRS
|
||||
connAPRS.netsetup();
|
||||
#endif
|
||||
} else {
|
||||
MDNS.end();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
#if FEATURE_APRS
|
||||
connAPRS.netsetup();
|
||||
#endif
|
||||
|
||||
Serial.println("enableNetwork done");
|
||||
}
|
||||
|
@ -2962,7 +2900,8 @@ void loop() {
|
|||
}
|
||||
|
||||
|
||||
#if FEATURE_SONDEHUB
|
||||
#if 0
|
||||
// removed here, now in connSondehub
|
||||
|
||||
// Sondehub v2 DB related codes
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
enum t_wifi_state { WIFI_DISABLED, WIFI_SCAN, WIFI_CONNECT, WIFI_CONNECTED, WIFI_APMODE };
|
||||
extern t_wifi_state wifi_state;
|
||||
|
||||
extern const char *sondeTypeStrSH[];
|
|
@ -129,6 +129,10 @@ void ConnAPRS::updateStation( PosInfo *pi ) {
|
|||
}
|
||||
}
|
||||
|
||||
String ConnAPRS::getStatus() {
|
||||
return String("");
|
||||
}
|
||||
|
||||
void ConnAPRS::aprs_station_update() {
|
||||
int chase = sonde.config.chase;
|
||||
// automatically decided if CHASE or FIXED mode is used (for config AUTO)
|
||||
|
|
|
@ -29,6 +29,8 @@ public:
|
|||
/* Called approx 1x / second* */
|
||||
void updateStation( PosInfo *pi );
|
||||
|
||||
String getStatus();
|
||||
|
||||
private:
|
||||
void aprs_station_update();
|
||||
};
|
||||
|
|
|
@ -64,5 +64,9 @@ void ConnChasemapper::updateSonde(SondeInfo *si) {
|
|||
void ConnChasemapper::updateStation(PosInfo *pi) {
|
||||
}
|
||||
|
||||
String ConnChasemapper::getStatus() {
|
||||
return String("");
|
||||
}
|
||||
|
||||
ConnChasemapper connChasemapper;
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@ public:
|
|||
void netsetup();
|
||||
void updateSonde( SondeInfo *si );
|
||||
void updateStation( PosInfo *pi );
|
||||
String getStatus();
|
||||
};
|
||||
|
||||
extern ConnChasemapper connChasemapper;
|
||||
|
|
|
@ -82,6 +82,10 @@ void MQTT::updateStation( PosInfo *pi ) {
|
|||
}
|
||||
}
|
||||
|
||||
String MQTT::getStatus() {
|
||||
return String("");
|
||||
}
|
||||
|
||||
// Internal (private) functions
|
||||
//void MQTT::connectToMqtt() {
|
||||
// Serial.println("Connecting to MQTT...");
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
/* Called approx 1x / second* */
|
||||
virtual void updateStation( PosInfo *pi );
|
||||
|
||||
virtual String getStatus();
|
||||
|
||||
private:
|
||||
WiFiClient mqttWifiClient;
|
||||
|
|
|
@ -0,0 +1,585 @@
|
|||
#include "../features.h"
|
||||
|
||||
#if FEATURE_SONDEHUB
|
||||
|
||||
#include "conn-sondehub.h"
|
||||
#include "posinfo.h"
|
||||
#include "../core.h"
|
||||
#include "DFM.h"
|
||||
#include "RS41.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ShFreqImport.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
extern const char *version_name;
|
||||
extern const char *version_id;
|
||||
|
||||
#define SONDEHUB_STATION_UPDATE_TIME (60*60*1000) // 60 min
|
||||
#define SONDEHUB_MOBILE_STATION_UPDATE_TIME (30*1000) // 30 sec
|
||||
WiFiClient shclient; // Sondehub v2
|
||||
int shImportInterval = 0;
|
||||
char shImport = 0;
|
||||
unsigned long time_last_update = 0;
|
||||
|
||||
void ConnSondehub::init() {
|
||||
}
|
||||
|
||||
void ConnSondehub::netsetup() {
|
||||
if (sonde.config.sondehub.active && wifi_state != WIFI_APMODE) {
|
||||
time_last_update = millis() + 1000; /* force sending update */
|
||||
sondehub_station_update();
|
||||
|
||||
// SH import: initial refresh on connect, even if configured interval is longer
|
||||
shImportInterval = 5; // refresh now in 5 seconds
|
||||
}
|
||||
}
|
||||
|
||||
// Imitating the old non-modular code
|
||||
// updateSonde is called once per second
|
||||
// old code called
|
||||
// each second: sondehub_reply_handler
|
||||
// each second, if good decode: sondehub_send_data
|
||||
// each second, if no good decode: sondehub_finish_data
|
||||
void ConnSondehub::updateSonde( SondeInfo *si ) {
|
||||
sondehub_reply_handler();
|
||||
if(si==NULL) {
|
||||
sondehub_finish_data();
|
||||
} else {
|
||||
sondehub_send_data(si);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConnSondehub::updateStation( PosInfo *pi ) {
|
||||
// TODO: station_update should be here and not in netsetup()
|
||||
// Currently, interlnal reply_handler does this, using gpsInfo global variable instead of this pi
|
||||
}
|
||||
|
||||
String ConnSondehub::getStatus() {
|
||||
return String("");
|
||||
}
|
||||
|
||||
/*** Code moved from RX_FSK to here ****/
|
||||
|
||||
// Sondehub v2 DB related codes
|
||||
/*
|
||||
Update station data to the sondehub v2 DB
|
||||
*/
|
||||
/* which_pos: 0=none, 1=fixed, 2=gps */
|
||||
void ConnSondehub::sondehub_station_update() {
|
||||
struct st_sondehub *conf = &sonde.config.sondehub;
|
||||
#define STATION_DATA_LEN 300
|
||||
char data[STATION_DATA_LEN];
|
||||
char *w;
|
||||
|
||||
// If there is no connection to some WiFi AP, we cannot upload any data at all....
|
||||
if ( wifi_state != WIFI_CONNECTED ) return;
|
||||
|
||||
unsigned long time_now = millis();
|
||||
// time_delta will be correct, even if time_now overflows
|
||||
unsigned long time_delta = time_now - time_last_update;
|
||||
|
||||
int chase = conf->chase;
|
||||
// automatically decided if CHASE or FIXED mode is used (for config AUTO)
|
||||
if (chase == SH_LOC_AUTO) {
|
||||
if (posInfo.chase) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED;
|
||||
}
|
||||
|
||||
// Use 30sec update time in chase mode, 60 min in station mode.
|
||||
unsigned long update_time = (chase == SH_LOC_CHASE) ? SONDEHUB_MOBILE_STATION_UPDATE_TIME : SONDEHUB_STATION_UPDATE_TIME;
|
||||
|
||||
// If it is not yet time to send another update. do nothing....
|
||||
if ( (time_delta <= update_time) ) return;
|
||||
|
||||
Serial.println("sondehub_station_update()");
|
||||
time_last_update = time_now;
|
||||
|
||||
if (!shclient.connected()) {
|
||||
if (!shclient.connect(conf->host, 80)) {
|
||||
Serial.println("Connection FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
w = data;
|
||||
// not necessary... memset(w, 0, STATION_DATA_LEN);
|
||||
|
||||
sprintf(w,
|
||||
"{"
|
||||
"\"software_name\": \"%s\","
|
||||
"\"software_version\": \"%s\","
|
||||
"\"uploader_callsign\": \"%s\",",
|
||||
version_name, version_id, conf->callsign);
|
||||
w += strlen(w);
|
||||
|
||||
// Only send email if provided
|
||||
if (strlen(conf->email) != 0) {
|
||||
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);
|
||||
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) {
|
||||
sprintf(w,
|
||||
"\"uploader_position\": [%.6f,%.6f,%d],"
|
||||
"\"mobile\": true",
|
||||
gpsPos.lat, gpsPos.lon, gpsPos.alt);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
}
|
||||
w += strlen(w);
|
||||
}
|
||||
// Otherweise, in FIXED mode we send the fixed position from config (if specified)
|
||||
else if (chase == SH_LOC_FIXED) {
|
||||
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]");
|
||||
}
|
||||
w += strlen(w);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// otherwise (in SH_LOC_NONE mode) we dont include any position info
|
||||
sprintf(w, "}");
|
||||
|
||||
shclient.println("PUT /listeners HTTP/1.1");
|
||||
shclient.print("Host: ");
|
||||
shclient.println(conf->host);
|
||||
shclient.println("accept: text/plain");
|
||||
shclient.println("Content-Type: application/json");
|
||||
shclient.print("Content-Length: ");
|
||||
shclient.println(strlen(data));
|
||||
shclient.println();
|
||||
shclient.println(data);
|
||||
Serial.println(strlen(data));
|
||||
Serial.println(data);
|
||||
Serial.println("Waiting for response");
|
||||
// TODO: better do this asynchronously
|
||||
// At least, do this safely. See Notes-on-Using-WiFiClient.txt for details
|
||||
// If any of the shclient.print failed before (remote end closed connection),
|
||||
// then calling client->read will cause a LoadProhibited exception
|
||||
if (shclient.connected()) {
|
||||
String response = shclient.readString();
|
||||
Serial.println(response);
|
||||
Serial.println("Response done...");
|
||||
} else {
|
||||
Serial.println("SH client connection closed\n");
|
||||
}
|
||||
//client->stop();
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
|
||||
|
||||
void ConnSondehub::sondehub_reply_handler() {
|
||||
// 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 1000
|
||||
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 {
|
||||
// any reply here belongs to normal telemetry upload, lets just print it.
|
||||
// and wait for a valid HTTP response
|
||||
int cnt = 0;
|
||||
while (shclient.available() > 0) {
|
||||
// data is available from remote server, process it...
|
||||
// readBytesUntil may wait for up to 1 second if enough data is not available...
|
||||
// int cnt = shclient.readBytesUntil('\n', rs_msg, MSG_SIZE - 1);
|
||||
int c = shclient.read();
|
||||
if (c < 0) break; // should never happen in available() returned >0 right before....
|
||||
rs_msg[cnt++] = c;
|
||||
if (c == '\n') {
|
||||
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;
|
||||
}
|
||||
cnt = 0;
|
||||
}
|
||||
if (cnt >= MSG_SIZE - 1) {
|
||||
cnt = 0;
|
||||
Serial.println("(overlong line from network, ignoring)");
|
||||
}
|
||||
}
|
||||
if (cnt > 0) {
|
||||
rs_msg[cnt + 1] = 0;
|
||||
Serial.println(rs_msg);
|
||||
}
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
// also handle periodic station updates here...
|
||||
// interval check moved to sondehub_station_update to avoid having to calculate distance in auto mode twice
|
||||
if (sonde.config.sondehub.active) {
|
||||
if (shState == SH_CONN_IDLE || shState == SH_DISCONNECTED ) {
|
||||
// (do not set station update while a telemetry report is being sent
|
||||
sondehub_station_update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnSondehub::sondehub_send_fimport() {
|
||||
if (shState == SH_CONN_APPENDING || shState == SH_CONN_WAITACK) {
|
||||
// Currently busy with SondeHub data upload
|
||||
// So do nothing here.
|
||||
// sond_fimport will be re-sent later, when shState becomes SH_CONN_IDLE
|
||||
return;
|
||||
}
|
||||
// It's time to run, so check prerequisites
|
||||
float lat = sonde.config.rxlat, lon = sonde.config.rxlon;
|
||||
if (gpsPos.valid) {
|
||||
lat = gpsPos.lat;
|
||||
lon = gpsPos.lon;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// in hours.... max allowed diff UTC <-> sonde time
|
||||
#define SONDEHUB_TIME_THRESHOLD (3)
|
||||
void ConnSondehub::sondehub_send_data(SondeInfo * s) {
|
||||
struct st_sondehub *conf = &sonde.config.sondehub;
|
||||
|
||||
Serial.println("sondehub_send_data()");
|
||||
Serial.printf("shState = %d\n", shState);
|
||||
|
||||
// max age of data in JSON request (in seconds)
|
||||
#define SONDEHUB_MAXAGE 15
|
||||
|
||||
char rs_msg[MSG_SIZE];
|
||||
char *w;
|
||||
struct tm ts;
|
||||
// config setting M10 and M20 will both decode both types, so use the real type that was decoded
|
||||
uint8_t realtype = sonde.realType(s);
|
||||
|
||||
// For DFM, s->d.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->d.time;
|
||||
|
||||
int chase = conf->chase;
|
||||
// automatically decided if CHASE or FIXED mode is used (for config AUTO)
|
||||
if (chase == SH_LOC_AUTO) {
|
||||
if (posInfo.chase) chase = SH_LOC_CHASE; else chase = SH_LOC_FIXED;
|
||||
}
|
||||
|
||||
|
||||
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->d.ser == 0 || s->d.validID == 0 ) return; // Don't send anything without serial number
|
||||
if (((int)s->d.lat == 0) && ((int)s->d.lon == 0)) return; // Sometimes these values are zeroes. Don't send those to the sondehub
|
||||
if ((int)s->d.alt > 50000) return; // If alt is too high don't send to SondeHub
|
||||
// M20 data does not include #sat information
|
||||
if ( realtype != STYPE_M20 && (int)s->d.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 (!shclient.connected()) {
|
||||
Serial.println("NO CONNECTION");
|
||||
shState = SH_DISCONNECTED;
|
||||
if (!shclient.connect(conf->host, 80)) {
|
||||
Serial.println("Connection FAILED");
|
||||
return;
|
||||
}
|
||||
shclient.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->d.time) > (3600 * SONDEHUB_TIME_THRESHOLD) ) {
|
||||
Serial.printf("Sonde time %d too far from current UTC time %ld", s->d.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 ( realtype == STYPE_RS41 || realtype == STYPE_RS92 || realtype == 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\": %.5f,"
|
||||
"\"lon\": %.5f,"
|
||||
"\"alt\": %.5f,"
|
||||
"\"frequency\": %.3f,"
|
||||
"\"vel_h\": %.5f,"
|
||||
"\"vel_v\": %.5f,"
|
||||
"\"heading\": %.5f,"
|
||||
"\"rssi\": %.1f,"
|
||||
"\"frame\": %d,"
|
||||
"\"type\": \"%s\",",
|
||||
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[realtype], s->d.ser,
|
||||
ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec,
|
||||
(float)s->d.lat, (float)s->d.lon, (float)s->d.alt, (float)s->freq, (float)s->d.hs, (float)s->d.vs,
|
||||
(float)s->d.dir, -((float)s->rssi / 2), s->d.vframe, sondeTypeStrSH[realtype]
|
||||
);
|
||||
w += strlen(w);
|
||||
|
||||
// Only send sats if not M20
|
||||
if (realtype != STYPE_M20) {
|
||||
sprintf(w, "\"sats\": %d,", (int)s->d.sats);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
/* if there is a subtype (DFM only) */
|
||||
if ( TYPE_IS_DFM(s->type) && s->d.subtype > 0 ) {
|
||||
if ( (s->d.subtype & 0xF) != DFM_UNK) {
|
||||
const char *t = dfmSubtypeLong[s->d.subtype & 0xF];
|
||||
sprintf(w, "\"subtype\": \"%s\",", t);
|
||||
}
|
||||
else {
|
||||
sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype >> 4); // 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 if provided
|
||||
if (!isnan(s->d.temperature)) {
|
||||
sprintf(w, "\"temp\": %.1f,", s->d.temperature);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send humidity if provided
|
||||
if (!isnan(s->d.relativeHumidity)) {
|
||||
sprintf(w, "\"humidity\": %.1f,", s->d.relativeHumidity);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send pressure if provided
|
||||
if (!isnan(s->d.pressure)) {
|
||||
sprintf(w, "\"pressure\": %.2f,", s->d.pressure);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send burst timer if RS41 and fresh within the last 51s
|
||||
if ((realtype == STYPE_RS41) && (s->d.crefKT > 0) && (s->d.vframe - s->d.crefKT < 51)) {
|
||||
sprintf(w, "\"burst_timer\": %d,", (int)s->d.countKT);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send battery if provided
|
||||
if (s->d.batteryVoltage > 0) {
|
||||
sprintf(w, "\"batt\": %.2f,", s->d.batteryVoltage);
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// Only send antenna if provided
|
||||
if (strlen(conf->antenna) != 0) {
|
||||
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) {
|
||||
sprintf(w, "\"uploader_position\": [%.6f,%.6f,%d]", gpsPos.lat, gpsPos.lon, gpsPos.alt);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
}
|
||||
w += strlen(w);
|
||||
}
|
||||
// Otherweise, in FIXED mode we send the fixed position from config (if specified)
|
||||
else if (chase == SH_LOC_FIXED) {
|
||||
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]");
|
||||
}
|
||||
w += strlen(w);
|
||||
} else {
|
||||
sprintf(w, "\"uploader_position\": [null,null,null]");
|
||||
w += strlen(w);
|
||||
}
|
||||
|
||||
// otherwise (in SH_LOC_NONE mode) we dont include any position info
|
||||
sprintf(w, "}");
|
||||
|
||||
if (shState != SH_CONN_APPENDING) {
|
||||
sondehub_send_header(s, &timeinfo);
|
||||
sondehub_send_next(s, rs_msg, strlen(rs_msg), 1);
|
||||
shState = SH_CONN_APPENDING;
|
||||
shStart = now;
|
||||
} else {
|
||||
sondehub_send_next(s, rs_msg, strlen(rs_msg), 0);
|
||||
}
|
||||
if (now - shStart > SONDEHUB_MAXAGE) { // after MAXAGE seconds
|
||||
sondehub_send_last();
|
||||
shState = SH_CONN_WAITACK;
|
||||
shStart = 0;
|
||||
}
|
||||
//client->println(rs_msg);
|
||||
//Serial.println(rs_msg);
|
||||
//String response = client->readString();
|
||||
//Serial.println(response);
|
||||
}
|
||||
|
||||
void ConnSondehub::sondehub_finish_data() {
|
||||
// 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();
|
||||
shState = SH_CONN_WAITACK;
|
||||
shStart = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
||||
static const char *MONTHS[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Noc", "Dec"};
|
||||
|
||||
void ConnSondehub::sondehub_send_header(SondeInfo * s, struct tm * now) {
|
||||
struct st_sondehub *conf = &sonde.config.sondehub;
|
||||
Serial.print("PUT /sondes/telemetry HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
Serial.println(conf->host);
|
||||
Serial.print("accept: text/plain\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
|
||||
shclient.print("PUT /sondes/telemetry HTTP/1.1\r\n"
|
||||
"Host: ");
|
||||
shclient.println(conf->host);
|
||||
shclient.print("accept: text/plain\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Transfer-Encoding: chunked\r\n");
|
||||
if (now) {
|
||||
Serial.printf("Date: %s, %02d %s %04d %02d:%02d:%02d GMT\r\n",
|
||||
DAYS[now->tm_wday], now->tm_mday, MONTHS[now->tm_mon], now->tm_year + 1900,
|
||||
now->tm_hour, now->tm_min, now->tm_sec);
|
||||
shclient.printf("Date: %s, %02d %s %04d %02d:%02d:%02d GMT\r\n",
|
||||
DAYS[now->tm_wday], now->tm_mday, MONTHS[now->tm_mon], now->tm_year + 1900,
|
||||
now->tm_hour, now->tm_min, now->tm_sec);
|
||||
}
|
||||
shclient.print("User-agent: ");
|
||||
shclient.print(version_name);
|
||||
shclient.print("/");
|
||||
shclient.println(version_id);
|
||||
shclient.println(""); // another cr lf as indication of end of header
|
||||
}
|
||||
void ConnSondehub::sondehub_send_next(SondeInfo * s, char *chunk, int chunklen, int first) {
|
||||
// send next chunk of JSON request
|
||||
shclient.printf("%x\r\n", chunklen + 1);
|
||||
shclient.write(first ? "[" : ",", 1);
|
||||
shclient.write(chunk, chunklen);
|
||||
shclient.print("\r\n");
|
||||
|
||||
Serial.printf("%x\r\n", chunklen + 1);
|
||||
Serial.write((const uint8_t *)(first ? "[" : ","), 1);
|
||||
Serial.write((const uint8_t *)chunk, chunklen);
|
||||
Serial.print("\r\n");
|
||||
}
|
||||
void ConnSondehub::sondehub_send_last() {
|
||||
// last chunk. just the closing "]" of the json request
|
||||
shclient.printf("1\r\n]\r\n0\r\n\r\n");
|
||||
Serial.printf("1\r\n]\r\n0\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ConnSondehub connSondehub;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#include "../features.h"
|
||||
#if FEATURE_SONDEHUB
|
||||
|
||||
#ifndef conn_sondehub_h
|
||||
#define conn_sondehub_h
|
||||
|
||||
#include "conn.h"
|
||||
|
||||
|
||||
class ConnSondehub : public Conn
|
||||
{
|
||||
public:
|
||||
/* Called once on startup */
|
||||
void init();
|
||||
|
||||
/* Called whenever the network becomes available */
|
||||
void netsetup();
|
||||
|
||||
/* Called approx 1x / second (maybe only if good data is available) */
|
||||
void updateSonde( SondeInfo *si );
|
||||
|
||||
/* Called approx 1x / second* */
|
||||
void updateStation( PosInfo *pi );
|
||||
|
||||
String getStatus();
|
||||
|
||||
private:
|
||||
void sondehub_station_update();
|
||||
void sondehub_reply_handler();
|
||||
void sondehub_send_fimport();
|
||||
void sondehub_send_data(SondeInfo * s);
|
||||
void sondehub_finish_data();
|
||||
void sondehub_send_header(SondeInfo * s, struct tm * now);
|
||||
void sondehub_send_next(SondeInfo * s, char *chunk, int chunklen, int first);
|
||||
void sondehub_send_last();
|
||||
};
|
||||
|
||||
|
||||
extern ConnSondehub connSondehub;
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -34,5 +34,7 @@ public:
|
|||
/* Called approx 1x / second* */
|
||||
virtual void updateStation( PosInfo *pi );
|
||||
|
||||
/* Called to retrieve status (used for Info in about tab) */
|
||||
virtual String getStatus();
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const char *version_name = "rdzTTGOsonde";
|
||||
const char *version_id = "devel20240110";
|
||||
const char *version_id = "devel20240116";
|
||||
const int SPIFFS_MAJOR=2;
|
||||
const int SPIFFS_MINOR=17;
|
||||
|
|
|
@ -28,6 +28,7 @@ platform = https://github.com/platformio/platform-espressif32.git#v6.3.0
|
|||
board = ttgo-lora32-v1
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_deps =
|
||||
${extra.lib_deps_builtin}
|
||||
${extra.lib_deps_external}
|
||||
|
|
Ładowanie…
Reference in New Issue