kopia lustrzana https://github.com/dl9rdz/rdz_ttgo_sonde
v0.3: merging some changes (axupd support, partial/readonly config web page)
rodzic
69cb203a4f
commit
791a6bdb75
|
@ -9,8 +9,6 @@
|
|||
#include <Sonde.h>
|
||||
#include <Scanner.h>
|
||||
#include <aprs.h>
|
||||
//#include <RS41.h>
|
||||
//#include <DFM.h>
|
||||
|
||||
#define LORA_LED 9
|
||||
|
||||
|
@ -28,8 +26,11 @@ int e;
|
|||
|
||||
AsyncWebServer server(80);
|
||||
|
||||
const char * udpAddress = "192.168.179.21";
|
||||
const int udpPort = 9002;
|
||||
#define LOCALUDPPORT 9002
|
||||
|
||||
// moved to sonde.config
|
||||
//const char * udpAddress = "192.168.42.20";
|
||||
//const int udpPort = 9002;
|
||||
|
||||
boolean connected = false;
|
||||
WiFiUDP udp;
|
||||
|
@ -268,11 +269,85 @@ const char *createStatusForm() {
|
|||
return message;
|
||||
}
|
||||
|
||||
///////////////////// Config form
|
||||
|
||||
struct st_configitems {
|
||||
const char *label;
|
||||
int type; // 0: numeric; i>0 string of length i; -1: separator; -2: type selector
|
||||
void *data;
|
||||
};
|
||||
#define N_CONFIG 16
|
||||
struct st_configitems config_list[N_CONFIG] = {
|
||||
{"Call", 8, sonde.config.call},
|
||||
{"Passcode", 8, sonde.config.passcode},
|
||||
{"---", -1, NULL},
|
||||
{"AXUDP active", -3, &sonde.config.udpfeed.active},
|
||||
{"AXUDP Host", 63, sonde.config.udpfeed.host},
|
||||
{"AXUDP Port", 0, &sonde.config.udpfeed.port},
|
||||
{"DFM ID Format", -2, &sonde.config.udpfeed.idformat},
|
||||
{"Rate limit", 0, &sonde.config.udpfeed.highrate},
|
||||
{"---", -1, NULL},
|
||||
{"APRS TCP active", -3, &sonde.config.tcpfeed.active},
|
||||
{"ARPS TCP Host", 63, sonde.config.tcpfeed.host},
|
||||
{"APRS TCP Port", 0, &sonde.config.tcpfeed.port},
|
||||
{"DFM ID Format", -2, &sonde.config.tcpfeed.idformat},
|
||||
{"Rate limit", 0, &sonde.config.tcpfeed.highrate},
|
||||
{"---", -1, NULL},
|
||||
{"Spectrum noise floor", 0, &sonde.config.noisefloor}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
void addConfigNumEntry(char *ptr, int idx, const char *label, int *value) {
|
||||
sprintf(ptr+strlen(ptr), "<tr><td>%s</td><td><input name=\"CFG%d\" type=\"text\" value=\"%d\"/></td></tr>\n",
|
||||
label, idx, *value);
|
||||
}
|
||||
void addConfigTypeEntry(char *ptr, int idx, const char *label, int *value) {
|
||||
// TODO
|
||||
}
|
||||
void addConfigOnOffEntry(char *ptr, int idx, const char *label, int *value) {
|
||||
// TODO
|
||||
}
|
||||
void addConfigSeparatorEntry(char *ptr) {
|
||||
strcat(ptr, "<tr><td colspan=\"2\" class=\"divider\"><hr /></td></tr>\n");
|
||||
}
|
||||
|
||||
const char *createConfigForm() {
|
||||
char *ptr = message;
|
||||
char tmp[4];
|
||||
strcpy(ptr,"<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"config.html\" method=\"post\"><table><tr><th>Option</th><th>Value</th></tr>");
|
||||
for(int i=0; i<N_CONFIG; i++) {
|
||||
switch(config_list[i].type) {
|
||||
case -3: // in/offt
|
||||
addConfigOnOffEntry(ptr, i, config_list[i].label, (int *)config_list[i].data);
|
||||
break;
|
||||
case -2: // DFM format
|
||||
addConfigTypeEntry(ptr, i, config_list[i].label, (int *)config_list[i].data);
|
||||
break;
|
||||
case -1:
|
||||
addConfigSeparatorEntry(ptr);
|
||||
break;
|
||||
case 0:
|
||||
addConfigNumEntry(ptr, i, config_list[i].label, (int *)config_list[i].data);
|
||||
break;
|
||||
default:
|
||||
addConfigStringEntry(ptr, i, config_list[i].label, config_list[i].type, (char *)config_list[i].data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
strcat(ptr,"</table><input type=\"submit\" value=\"Update not yet implemented\"></input></form></body></html>");
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char* PARAM_MESSAGE = "message";
|
||||
void SetupAsyncServer() {
|
||||
// Route for root / web page
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/plain", "Hello, world");
|
||||
request->send(SPIFFS, "/index.html", String(), false, processor);
|
||||
});
|
||||
|
||||
server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
|
@ -299,6 +374,10 @@ void SetupAsyncServer() {
|
|||
request->send(200, "text/html", createWIFIForm());
|
||||
});
|
||||
|
||||
server.on("/config.html", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/html", createConfigForm());
|
||||
});
|
||||
|
||||
server.on("/status.html", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/html", createStatusForm());
|
||||
});
|
||||
|
@ -308,29 +387,11 @@ void SetupAsyncServer() {
|
|||
request->send(SPIFFS, "/style.css", "text/css");
|
||||
});
|
||||
|
||||
// Route to set GPIO to HIGH
|
||||
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
digitalWrite(ledPin, HIGH);
|
||||
request->send(SPIFFS, "/index.html", String(), false, processor);
|
||||
});
|
||||
|
||||
// Route to set GPIO to HIGH
|
||||
server.on("/test.php", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
//digitalWrite(ledPin, HIGH);
|
||||
request->send(SPIFFS, "/index.html", String(), false, processor);
|
||||
});
|
||||
|
||||
// Route to set GPIO to LOW
|
||||
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
digitalWrite(ledPin, LOW);
|
||||
request->send(SPIFFS, "/index.html", String(), false, processor);
|
||||
});
|
||||
|
||||
// Send a POST request to <IP>/post with a form field message set to <message>
|
||||
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
handleQRGPost(request);
|
||||
request->send(200, "text/plain", "Hello, POST done");
|
||||
});
|
||||
|
||||
// Start server
|
||||
server.begin();
|
||||
|
@ -422,7 +483,9 @@ void setup()
|
|||
Serial.println(f);
|
||||
#endif
|
||||
|
||||
sx1278.setLNAGain(0); //-48);
|
||||
//sx1278.setLNAGain(-48);
|
||||
sx1278.setLNAGain(0);
|
||||
|
||||
int gain = sx1278.getLNAGain();
|
||||
Serial.print("RX LNA Gain is ");
|
||||
Serial.println(gain);
|
||||
|
@ -489,9 +552,9 @@ void loopDecoder() {
|
|||
SondeInfo *s = sonde.si();
|
||||
char raw[201];
|
||||
const char *str = aprs_senddata(s->lat, s->lon, s->hei, s->hs, s->dir, s->vs, sondeTypeStr[s->type], s->id, "TE0ST", "EO");
|
||||
int rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
|
||||
int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
|
||||
Serial.print("Sending: "); Serial.println(raw);
|
||||
udp.beginPacket(udpAddress,udpPort);
|
||||
udp.beginPacket(sonde.config.udpfeed.host,sonde.config.udpfeed.port);
|
||||
udp.write((const uint8_t *)raw,rawlen);
|
||||
udp.endPacket();
|
||||
}
|
||||
|
@ -572,7 +635,7 @@ void WiFiEvent(WiFiEvent_t event){
|
|||
Serial.println(WiFi.localIP());
|
||||
//initializes the UDP state
|
||||
//This initializes the transfer buffer
|
||||
udp.begin(WiFi.localIP(),udpPort);
|
||||
udp.begin(WiFi.localIP(),LOCALUDPPORT);
|
||||
connected = true;
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
|
@ -582,6 +645,7 @@ void WiFiEvent(WiFiEvent_t event){
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static char* _scan[2]={"/","\\"};
|
||||
void loopWifiScan() {
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
@ -623,12 +687,14 @@ void loopWifiScan() {
|
|||
Serial.print(".");
|
||||
u8x8.drawString(15,7,_scan[cnt&1]);
|
||||
cnt++;
|
||||
#if 0
|
||||
if(cnt==4) {
|
||||
WiFi.disconnect(true); // retry, for my buggy FritzBox
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
WiFi.begin(id, pw);
|
||||
}
|
||||
if(cnt==10) {
|
||||
#endif
|
||||
if(cnt==15) {
|
||||
WiFi.disconnect(true);
|
||||
delay(1000);
|
||||
WiFi.softAP(networks[0].id.c_str(),networks[0].pw.c_str());
|
||||
|
@ -666,11 +732,7 @@ void loop() {
|
|||
case ST_SPECTRUM: loopSpectrum(); break;
|
||||
case ST_WIFISCAN: loopWifiScan(); break;
|
||||
}
|
||||
#if 0
|
||||
//wifiloop(NULL);
|
||||
//e = dfm.receiveFrame();
|
||||
//e = rs41.receiveFrame();
|
||||
delay(1000);
|
||||
#if 1
|
||||
int rssi = sx1278.getRSSI();
|
||||
Serial.print(" RSSI: ");
|
||||
Serial.print(rssi);
|
||||
|
@ -678,6 +740,5 @@ void loop() {
|
|||
int gain = sx1278.getLNAGain();
|
||||
Serial.print(" LNA Gain: "),
|
||||
Serial.println(gain);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<button class="tablinks" onclick="selTab(event,'QRG')" id="defaultTab">QRG</button>
|
||||
<button class="tablinks" onclick="selTab(event,'WIFI')">WLAN</button>
|
||||
<button class="tablinks" onclick="selTab(event,'Data')">Data</button>
|
||||
<button class="tablinks" onclick="selTab(event,'Config')">Config</button>
|
||||
<button class="tablinks" onclick="selTab(event,'About')">About</button>
|
||||
</div>
|
||||
|
||||
|
@ -36,6 +37,11 @@
|
|||
<iframe src="status.html" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="Config" class="tabcontent">
|
||||
<h3>Config</h3>
|
||||
<iframe src="config.html" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="About" class="tabcontent">
|
||||
<h3>About</h3>
|
||||
RDZSonde
|
||||
|
|
|
@ -357,7 +357,7 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
|
|||
|
||||
|
||||
|
||||
void RS41::decode41(byte *data, int MAXLEN)
|
||||
void RS41::decode41(byte *data, int maxlen)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
|
@ -369,10 +369,10 @@ void RS41::decode41(byte *data, int MAXLEN)
|
|||
Serial.print(corr);
|
||||
Serial.println();
|
||||
int p = 57; // 8 byte header, 48 byte RS
|
||||
while(p<MAXLEN) { /* why 555? */
|
||||
while(p<maxlen) { /* why 555? */
|
||||
uint8_t typ = data[p++];
|
||||
uint32_t len = data[p++]+2UL;
|
||||
if(p+len>MAXLEN) break;
|
||||
if(p+len>maxlen) break;
|
||||
|
||||
#if 1
|
||||
// DEBUG OUTPUT
|
||||
|
@ -459,20 +459,20 @@ static uint8_t scramble[64] = {150U,131U,62U,81U,177U,73U,8U,152U,50U,5U,89U,
|
|||
|
||||
static byte data[800];
|
||||
|
||||
#define MAXLEN (320)
|
||||
#define RS41MAXLEN (320)
|
||||
int RS41::receiveFrame() {
|
||||
//sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
|
||||
sx1278.setPayloadLength(MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
|
||||
sx1278.setPayloadLength(RS41MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
|
||||
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
int e = sx1278.receivePacketTimeout(1000, data+8);
|
||||
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1
|
||||
|
||||
for(int i=0; i<MAXLEN; i++) { data[i] = reverse(data[i]); }
|
||||
for(int i=0; i<RS41MAXLEN; i++) { data[i] = reverse(data[i]); }
|
||||
//printRaw(data, MAXLEN);
|
||||
for(int i=0; i<MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
|
||||
for(int i=0; i<RS41MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
|
||||
//printRaw(data, MAXLEN);
|
||||
decode41(data, MAXLEN);
|
||||
decode41(data, RS41MAXLEN);
|
||||
return RX_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,23 @@ static uint8_t empty_tile1[8]={0x00, 0xF0, 0x88, 0x48, 0x28, 0xF0, 0x00, 0x00};
|
|||
static uint8_t empty_tile2[8]={0x00, 0x11, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00};
|
||||
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
|
||||
|
||||
Sonde::Sonde() {
|
||||
config.noisefloor = -130;
|
||||
strcpy(config.call,"NOCALL");
|
||||
strcpy(config.passcode, "---");
|
||||
config.udpfeed.active = 1;
|
||||
config.udpfeed.type = 0;
|
||||
strcpy(config.udpfeed.host, "192.168.42.20");
|
||||
config.udpfeed.port = 9002;
|
||||
config.udpfeed.highrate = 1;
|
||||
config.udpfeed.idformat = ID_DFMGRAW;
|
||||
config.tcpfeed.active = 0;
|
||||
config.tcpfeed.type = 1;
|
||||
strcpy(config.tcpfeed.host, "radiosondy.info");
|
||||
config.tcpfeed.port = 12345;
|
||||
config.tcpfeed.highrate = 10;
|
||||
config.tcpfeed.idformat = ID_DFMDXL;
|
||||
}
|
||||
|
||||
void Sonde::setIP(const char *ip, bool AP) {
|
||||
memset(myIP_tiles, 0, 11*8);
|
||||
|
@ -153,11 +170,11 @@ void Sonde::updateDisplayPos2() {
|
|||
u8x8.drawString(10,4," ");
|
||||
return;
|
||||
}
|
||||
snprintf(buf, 16, si()->hei>999?"%5.0fm":"%3.1fm", si()->hei);
|
||||
snprintf(buf, 16, si()->hei>999?" %5.0fm":" %3.1fm", si()->hei);
|
||||
u8x8.drawString((10+6-strlen(buf)),2,buf);
|
||||
snprintf(buf, 16, si()->hs>99?"%3.0f":"%2.1f", si()->hs);
|
||||
snprintf(buf, 16, si()->hs>99?" %3.0f":" %2.1f", si()->hs);
|
||||
u8x8.drawString((10+4-strlen(buf)),3,buf);
|
||||
snprintf(buf, 16, "%+2.1f", si()->vs);
|
||||
snprintf(buf, 16, " %+2.1f", si()->vs);
|
||||
u8x8.drawString((10+4-strlen(buf)),4,buf);
|
||||
u8x8.drawTile(14,3,2,kmh_tiles);
|
||||
u8x8.drawTile(14,4,2,ms_tiles);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#ifndef Sonde_h
|
||||
#define Sonde_H
|
||||
|
||||
#include "aprs.h"
|
||||
|
||||
// RX_TIMEOUT: no header detected
|
||||
// RX_ERROR: header detected, but data not decoded (crc error, etc.)
|
||||
// RX_OK: header and data ok
|
||||
|
@ -10,6 +12,15 @@ enum RxResult { RX_OK, RX_TIMEOUT, RX_ERROR };
|
|||
enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41 };
|
||||
extern const char *sondeTypeStr[5];
|
||||
|
||||
typedef struct st_rdzconfig {
|
||||
int noisefloor; // for spectrum display
|
||||
char call[9];
|
||||
char passcode[9];
|
||||
// for now, one feed for each type is enough, but might get extended to more?
|
||||
struct st_feedinfo udpfeed; // target for AXUDP messages
|
||||
struct st_feedinfo tcpfeed; // target for APRS-IS TCP connections
|
||||
} RDZConfig;
|
||||
|
||||
typedef struct st_sondeinfo {
|
||||
// receiver configuration
|
||||
bool active;
|
||||
|
@ -39,10 +50,13 @@ class Sonde
|
|||
{
|
||||
private:
|
||||
public:
|
||||
RDZConfig config;
|
||||
int currentSonde = 0;
|
||||
int nSonde;
|
||||
SondeInfo sondeList[MAXSONDE+1];
|
||||
|
||||
Sonde();
|
||||
|
||||
void clearSonde();
|
||||
void addSonde(float frequency, SondeType type, int active);
|
||||
void nextConfig();
|
||||
|
|
|
@ -45,7 +45,7 @@ void aprsstr_append(char *b, const char *data)
|
|||
{
|
||||
int blen=strlen(b);
|
||||
int len=strlen(data);
|
||||
if(blen+len>MAXLEN) len=MAXLEN-blen;
|
||||
if(blen+len>APRS_MAXLEN) len=APRS_MAXLEN-blen;
|
||||
strncat(b, data, len);
|
||||
}
|
||||
|
||||
|
@ -233,26 +233,26 @@ char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, fl
|
|||
int i = strlen(b);
|
||||
int lati = abs((int)lat);
|
||||
int latm = (fabs(lat)-lati)*6000;
|
||||
snprintf(b+i, MAXLEN-i, "%02d%02d.%02d%c%c", lati, latm/100, latm%100, lat<0?'S':'N', sym[0]);
|
||||
snprintf(b+i, APRS_MAXLEN-i, "%02d%02d.%02d%c%c", lati, latm/100, latm%100, lat<0?'S':'N', sym[0]);
|
||||
i = strlen(b);
|
||||
int loni = abs((int)lon);
|
||||
int lonm = (fabs(lon)-loni)*6000;
|
||||
snprintf(b+i, MAXLEN-i, "%03d%02d.%02d%c%c", loni, lonm/100, lonm%100, lon<0?'W':'E', sym[1]);
|
||||
snprintf(b+i, APRS_MAXLEN-i, "%03d%02d.%02d%c%c", loni, lonm/100, lonm%100, lon<0?'W':'E', sym[1]);
|
||||
#if 1
|
||||
if(speed>0.5) {
|
||||
i=strlen(b);
|
||||
snprintf(b+i, MAXLEN-i, "%03d/%03d", realcard(dir+1.5), realcard(speed*1.0/KNOTS+0.5));
|
||||
snprintf(b+i, APRS_MAXLEN-i, "%03d/%03d", realcard(dir+1.5), realcard(speed*1.0/KNOTS+0.5));
|
||||
}
|
||||
#endif
|
||||
if(hei>0.5) {
|
||||
i=strlen(b);
|
||||
snprintf(b+i, MAXLEN-i, "/A=%06d", realcard(hei*FEET+0.5));
|
||||
snprintf(b+i, APRS_MAXLEN-i, "/A=%06d", realcard(hei*FEET+0.5));
|
||||
}
|
||||
#if 0
|
||||
int dao=1;
|
||||
if(dao) {
|
||||
i=strlen(b);
|
||||
snprintf(b+i, MAXLEN-i, "!w%c%c!", 33+dao91(lat), 33+dao91(lon));
|
||||
snprintf(b+i, APRS_MAXLEN-i, "!w%c%c!", 33+dao91(lat), 33+dao91(lon));
|
||||
}
|
||||
#endif
|
||||
const char *comm="&test";
|
||||
|
@ -273,11 +273,11 @@ int main(int argc, char *argv[])
|
|||
float lat=48, lon=10;
|
||||
while(1) {
|
||||
const char *str = aprs_senddata(lat, lon, 543, 5, 180, 1.5, "RS41", "TE0ST", "TE1ST", "EO");
|
||||
int rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
|
||||
int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
|
||||
sendudp(fd, raw, rawlen);
|
||||
|
||||
str = "OE3XKC>APMI06,qAR,OE3XLR:;ER-341109*111111z4803.61NE01532.39E0145.650MHz R15k OE3XPA";
|
||||
rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
|
||||
rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN);
|
||||
sendudp(fd, &si, raw, rawlen);
|
||||
lat += 0.002; lon += 0.01;
|
||||
sleep(5);
|
||||
|
|
|
@ -2,17 +2,21 @@
|
|||
#ifndef _aprs_h
|
||||
#define _aprs_h
|
||||
|
||||
enum IDTYPE { ID_DFMDXL, ID_DFMREAL, ID_DFMAUTO };
|
||||
enum IDTYPE { ID_DFMDXL, ID_DFMGRAW, ID_DFMAUTO };
|
||||
|
||||
typedef struct st_feedinfo {
|
||||
bool active;
|
||||
int type; // 0:UDP(axudp), 1:TCP(aprs.fi)
|
||||
char *host;
|
||||
char host[64];
|
||||
int port;
|
||||
int rate;
|
||||
int lowrate;
|
||||
int highrate;
|
||||
int lowlimit;
|
||||
int idformat; // 0: dxl 1: real 2: auto
|
||||
}
|
||||
#define MAXLEN 201
|
||||
};
|
||||
|
||||
|
||||
#define APRS_MAXLEN 201
|
||||
void aprs_gencrctab(void);
|
||||
int aprsstr_mon2raw(const char *mon, char raw[], int raw_len);
|
||||
char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym);
|
||||
|
|
Ładowanie…
Reference in New Issue