kopia lustrzana https://github.com/dl9rdz/rdz_ttgo_sonde
commit
c1d1887d96
|
@ -1,7 +1,7 @@
|
|||
language: c
|
||||
env:
|
||||
global:
|
||||
- ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools
|
||||
- ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools
|
||||
- MKSPIFFS=/home/travis/.arduino15/packages/esp32/tools/mkspiffs/0.2.3/mkspiffs
|
||||
before_install:
|
||||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16"
|
||||
|
|
|
@ -27,9 +27,9 @@ int e;
|
|||
|
||||
enum MainState { ST_DECODER, ST_SPECTRUM, ST_WIFISCAN, ST_UPDATE, ST_TOUCHCALIB };
|
||||
static MainState mainState = ST_WIFISCAN; // ST_WIFISCAN;
|
||||
const char *mainStateStr[5] = {"DECODER", "SPECTRUM", "WIFISCAN", "UPDATE", "TOUCHCALIB" };
|
||||
|
||||
AsyncWebServer server(80);
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
AXP20X_Class axp;
|
||||
#define PMU_IRQ 35
|
||||
|
@ -108,6 +108,7 @@ int readLine(Stream &stream, char *buffer, int maxlen) {
|
|||
return n;
|
||||
}
|
||||
|
||||
|
||||
// Replaces placeholder with LED state value
|
||||
String processor(const String& var) {
|
||||
Serial.println(var);
|
||||
|
@ -214,40 +215,57 @@ void setupChannelList() {
|
|||
file.close();
|
||||
}
|
||||
|
||||
const char *HTMLHEAD = "<html><head> <meta charset=\"UTF-8\"> <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">";
|
||||
void HTMLBODY(char *ptr, const char *which) {
|
||||
strcat(ptr, "<body><form class=\"wrapper\" action=\"");
|
||||
strcat(ptr, which);
|
||||
strcat(ptr, "\" method=\"post\"><div class=\"content\">");
|
||||
}
|
||||
void HTMLBODYEND(char *ptr) {
|
||||
strcat(ptr, "</div></form></body></html>");
|
||||
}
|
||||
void HTMLSAVEBUTTON(char *ptr) {
|
||||
strcat(ptr, "</div><div class=\"footer\"><input type=\"submit\" class=\"save\" value=\"Save changes\"/>");
|
||||
}
|
||||
|
||||
const char *createQRGForm() {
|
||||
char *ptr = message;
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">");
|
||||
strcat(ptr, "<script type=\"text/javascript\">"
|
||||
"let stypes=new Map();"
|
||||
"stypes.set('4', 'RS41');"
|
||||
"stypes.set('R', 'RS92');"
|
||||
"stypes.set('9', 'DFM9 (old)');"
|
||||
"stypes.set('6', 'DFM6 (old)');"
|
||||
"stypes.set('D', 'DFM');"
|
||||
"stypes.set('M', 'M10');"
|
||||
"stypes.set('2', 'M20');"
|
||||
"function prep() {"
|
||||
" var stlist=document.querySelectorAll(\"input.stype\");"
|
||||
" for(txt of stlist){"
|
||||
" var val=txt.getAttribute('value'); var nam=txt.getAttribute('name'); "
|
||||
" var sel=document.createElement('select');"
|
||||
" sel.setAttribute('name',nam);"
|
||||
" for(stype of stypes) { "
|
||||
" var opt=document.createElement('option');"
|
||||
" opt.value=stype[0];"
|
||||
" opt.innerHTML=stype[1];"
|
||||
" if(stype[0]==val) { opt.setAttribute('selected','selected'); }"
|
||||
" sel.appendChild(opt);"
|
||||
" } txt.replaceWith(sel); } } "
|
||||
" window.onload = prep; "
|
||||
"</script>");
|
||||
|
||||
strcat(ptr, "</head><body><form action=\"qrg.html\" method=\"post\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
|
||||
strcpy(ptr, HTMLHEAD);
|
||||
strcat(ptr, "<script src=\"rdz.js\"/> <script> window.onload = prep; </script></head>");
|
||||
/*
|
||||
strcat(ptr, "<script type=\"text/javascript\">"
|
||||
"let stypes=new Map();"
|
||||
"stypes.set('4', 'RS41');"
|
||||
"stypes.set('R', 'RS92');"
|
||||
"stypes.set('9', 'DFM9 (old)');"
|
||||
"stypes.set('6', 'DFM6 (old)');"
|
||||
"stypes.set('D', 'DFM');"
|
||||
"stypes.set('M', 'M10');"
|
||||
"stypes.set('2', 'M20');"
|
||||
"function prep() {"
|
||||
" var stlist=document.querySelectorAll(\"input.stype\");"
|
||||
" for(txt of stlist){"
|
||||
" var val=txt.getAttribute('value'); var nam=txt.getAttribute('name'); "
|
||||
" var sel=document.createElement('select');"
|
||||
" sel.setAttribute('name',nam);"
|
||||
" for(stype of stypes) { "
|
||||
" var opt=document.createElement('option');"
|
||||
" opt.value=stype[0];"
|
||||
" opt.innerHTML=stype[1];"
|
||||
" if(stype[0]==val) { opt.setAttribute('selected','selected'); }"
|
||||
" sel.appendChild(opt);"
|
||||
" } txt.replaceWith(sel); } } "
|
||||
" window.onload = prep; "
|
||||
"</script>");
|
||||
*/
|
||||
HTMLBODY(ptr, "qrg.html");
|
||||
//strcat(ptr, "<body><form class=\"wrapper\" action=\"qrg.html\" method=\"post\"><div class=\"content\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
|
||||
strcat(ptr, "<table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
|
||||
for (int i = 0; i < sonde.config.maxsonde; i++) {
|
||||
//String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type);
|
||||
String site = sonde.sondeList[i].launchsite;
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>%d</td><td><input name=\"A%d\" type=\"checkbox\" %s/></td>"
|
||||
"<td><input name=\"F%d\" type=\"text\" value=\"%3.3f\"></td>"
|
||||
"<td><input name=\"F%d\" type=\"text\" width=12 value=\"%3.3f\"></td>"
|
||||
"<td><input name=\"S%d\" type=\"text\" value=\"%s\"></td>"
|
||||
//"<td><select name=\"T%d\">%s</select></td>",
|
||||
"<td><input class='stype' name='T%d' value='%c'>",
|
||||
|
@ -256,9 +274,12 @@ const char *createQRGForm() {
|
|||
i + 1, i >= sonde.nSonde ? 400.000 : sonde.sondeList[i].freq,
|
||||
i + 1, i >= sonde.nSonde ? " " : sonde.sondeList[i].launchsite,
|
||||
i + 1, i >= sonde.nSonde ? 2 : sondeTypeChar[sonde.sondeList[i].type] );
|
||||
//i + 1, s.c_str());
|
||||
//i + 1, s.c_str());
|
||||
}
|
||||
strcat(ptr, "</table><input type=\"submit\" value=\"Update\"/></form></body></html>");
|
||||
strcat(ptr, "</table>");
|
||||
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
|
||||
HTMLSAVEBUTTON(ptr);
|
||||
HTMLBODYEND(ptr);
|
||||
Serial.printf("QRG form: size=%d bytes\n", strlen(message));
|
||||
return message;
|
||||
}
|
||||
|
@ -353,7 +374,9 @@ void setupWifiList() {
|
|||
const char *createWIFIForm() {
|
||||
char *ptr = message;
|
||||
char tmp[4];
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"wifi.html\" method=\"post\"><table><tr><th>Nr</th><th>SSID</th><th>Password</th></tr>");
|
||||
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
|
||||
HTMLBODY(ptr, "wifi.html");
|
||||
strcat(ptr, "<table><tr><th>Nr</th><th>SSID</th><th>Password</th></tr>");
|
||||
for (int i = 0; i < MAX_WIFI; i++) {
|
||||
sprintf(tmp, "%d", i);
|
||||
sprintf(ptr + strlen(ptr), "<tr><td>%s</td><td><input name=\"S%d\" type=\"text\" value=\"%s\"/></td>"
|
||||
|
@ -362,7 +385,10 @@ const char *createWIFIForm() {
|
|||
i + 1, i < nNetworks ? networks[i].id.c_str() : "",
|
||||
i + 1, i < nNetworks ? networks[i].pw.c_str() : "");
|
||||
}
|
||||
strcat(ptr, "</table><input type=\"submit\" value=\"Update\"></input></form></body></html>");
|
||||
strcat(ptr, "</table>");
|
||||
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
|
||||
HTMLSAVEBUTTON(ptr);
|
||||
HTMLBODYEND(ptr);
|
||||
Serial.printf("WIFI form: size=%d bytes\n", strlen(message));
|
||||
return message;
|
||||
}
|
||||
|
@ -430,13 +456,14 @@ void addSondeStatus(char *ptr, int i)
|
|||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://radiosondy.info/sonde_archive.php?sondenumber=%s\">radiosondy.info</a> - ", s->id);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a> - ", s->lat, s->lon);
|
||||
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.com/maps/search/?api=1&query=%.6f,%.6f\">Google</a></td></tr>", s->lat, s->lon);
|
||||
|
||||
|
||||
strcat(ptr, "</table><p/>\n");
|
||||
}
|
||||
|
||||
const char *createStatusForm() {
|
||||
char *ptr = message;
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"><meta http-equiv=\"refresh\" content=\"5\"></head><body>");
|
||||
strcpy(ptr, HTMLHEAD);
|
||||
strcat(ptr, "<meta http-equiv=\"refresh\" content=\"5\"></head><body>");
|
||||
|
||||
for (int i = 0; i < sonde.nSonde; i++) {
|
||||
int snum = (i + sonde.currentSonde) % sonde.nSonde;
|
||||
|
@ -498,6 +525,7 @@ struct st_configitems config_list[] = {
|
|||
{"dfm.rxbw", "DFM RX bandwidth", 0, &sonde.config.dfm.rxbw},
|
||||
{"m10m20.agcbw", "M10/M20 AGC bandwidth", 0, &sonde.config.m10m20.agcbw},
|
||||
{"m10m20.rxbw", "M10/M20 RX bandwidth", 0, &sonde.config.m10m20.rxbw},
|
||||
{"ephftp", "FTP for eph (RS92)", 39, &sonde.config.ephftp},
|
||||
{"", "Data feed configuration", -5, NULL},
|
||||
/* APRS settings */
|
||||
{"call", "Call", 8, sonde.config.call},
|
||||
|
@ -517,7 +545,7 @@ struct st_configitems config_list[] = {
|
|||
{"tcp.port", "APRS TCP Port", 0, &sonde.config.tcpfeed.port},
|
||||
{"tcp.idformat", "DFM ID Format", -2, &sonde.config.tcpfeed.idformat},
|
||||
{"tcp.highrate", "Rate limit", 0, &sonde.config.tcpfeed.highrate},
|
||||
|
||||
|
||||
/* MQTT */
|
||||
{"mqtt.active", "MQTT Active (needs reboot)", 0, &sonde.config.mqtt.active},
|
||||
{"mqtt.id", "MQTT client ID", 63, &sonde.config.mqtt.id},
|
||||
|
@ -537,6 +565,7 @@ struct st_configitems config_list[] = {
|
|||
{"tft_rs", "TFT RS", 0, &sonde.config.tft_rs},
|
||||
{"tft_cs", "TFT CS", 0, &sonde.config.tft_cs},
|
||||
{"tft_orient", "TFT orientation (0/1/2/3), OLED flip: 3", 0, &sonde.config.tft_orient},
|
||||
{"tft_modeflip", "TFT modeflip (usually 0)", 0, &sonde.config.tft_modeflip},
|
||||
{"button_pin", "Button input port", -4, &sonde.config.button_pin},
|
||||
{"button2_pin", "Button 2 input port", -4, &sonde.config.button2_pin},
|
||||
{"button2_axp", "Use AXP192 PWR as Button 2", 0, &sonde.config.button2_axp},
|
||||
|
@ -606,7 +635,9 @@ void addConfigInt8List(char *ptr, int idx, const char *label, int8_t *list) {
|
|||
|
||||
const char *createConfigForm() {
|
||||
char *ptr = message;
|
||||
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>");
|
||||
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
|
||||
HTMLBODY(ptr, "config.html");
|
||||
strcat(ptr, "<table><tr><th>Option</th><th>Value</th></tr>");
|
||||
for (int i = 0; i < N_CONFIG; i++) {
|
||||
switch (config_list[i].type) {
|
||||
case -5: // Heading
|
||||
|
@ -635,7 +666,10 @@ const char *createConfigForm() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
strcat(ptr, "</table><input type=\"submit\" value=\"Update\"></input></form></body></html>");
|
||||
strcat(ptr, "</table>");
|
||||
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
|
||||
HTMLSAVEBUTTON(ptr);
|
||||
HTMLBODYEND(ptr);
|
||||
Serial.printf("Config form: size=%d bytes\n", strlen(message));
|
||||
return message;
|
||||
}
|
||||
|
@ -694,21 +728,25 @@ const char *handleConfigPost(AsyncWebServerRequest *request) {
|
|||
|
||||
const char *ctrlid[] = {"rx", "scan", "spec", "wifi", "rx2", "scan2", "spec2", "wifi2"};
|
||||
|
||||
const char *ctrllabel[] = {"Receiver (short keypress)", "Scanner (double keypress)", "Spectrum (medium keypress)", "WiFi (long keypress)",
|
||||
"Button 2 (short keypress)", "Button 2 (double keypress)", "Button 2 (medium keypress)", "Button 2 (long keypress)"
|
||||
const char *ctrllabel[] = {"Receiver/next freq. (short keypress)", "Scanner (double keypress)", "Spectrum (medium keypress)", "WiFi (long keypress)",
|
||||
"Button 2/next screen (short keypress)", "Button 2 (double keypress)", "Button 2 (medium keypress)", "Button 2 (long keypress)"
|
||||
};
|
||||
|
||||
const char *createControlForm() {
|
||||
char *ptr = message;
|
||||
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"control.html\" method=\"post\">");
|
||||
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
|
||||
HTMLBODY(ptr, "control.html");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
strcat(ptr, "<input type=\"submit\" name=\"");
|
||||
strcat(ptr, "<input class=\"ctlbtn\" type=\"submit\" name=\"");
|
||||
strcat(ptr, ctrlid[i]);
|
||||
strcat(ptr, "\" value=\"");
|
||||
strcat(ptr, ctrllabel[i]);
|
||||
strcat(ptr, "\"></input><br>");
|
||||
strcat(ptr, "\"></input>");
|
||||
if (i == 3) {
|
||||
strcat(ptr, "<p></p>");
|
||||
}
|
||||
}
|
||||
strcat(ptr, "</form></body></html>");
|
||||
HTMLBODYEND(ptr);
|
||||
Serial.printf("Control form: size=%d bytes\n", strlen(message));
|
||||
return message;
|
||||
}
|
||||
|
@ -756,6 +794,55 @@ const char *handleControlPost(AsyncWebServerRequest *request) {
|
|||
return "";
|
||||
}
|
||||
|
||||
int streamEditForm(int &state, File &file, String filename, char *buffer, size_t maxlen, size_t index) {
|
||||
Serial.printf("streamEdit: state=%d max:%d idx:%d\n", state, maxlen, index);
|
||||
int i = 0;
|
||||
switch (state) {
|
||||
case 0: // header
|
||||
{
|
||||
// we optimistically assume that on first invocation, maxlen is large enough to handle the header.....
|
||||
strncpy(buffer, "<html><head><title>Editor</title></head><body><p>Edit: ", maxlen);
|
||||
i = strlen(buffer);
|
||||
strncpy(buffer + i, filename.c_str(), maxlen - i);
|
||||
i += strlen(buffer + i);
|
||||
strncpy(buffer + i, "</p><form action=\"edit.html?file=", maxlen - i);
|
||||
i += strlen(buffer + i);
|
||||
strncpy(buffer + i, filename.c_str(), maxlen - i);
|
||||
i += strlen(buffer + i);
|
||||
strncpy(buffer + i, "\" method=\"post\" enctype=\"multipart/form-data\"><textarea name=\"text\" cols=\"80\" rows=\"40\">", maxlen - i);
|
||||
i += strlen(buffer + i);
|
||||
if (i >= maxlen) {
|
||||
strncpy(buffer, "Out of memory", maxlen);
|
||||
state = 3;
|
||||
return strlen(buffer);
|
||||
}
|
||||
state++;
|
||||
Serial.printf("Wrote %d bytes. Header finished", i);
|
||||
return i;
|
||||
break;
|
||||
}
|
||||
case 1: // file content
|
||||
while (file.available()) {
|
||||
int cnt = readLine(file, buffer + i, maxlen - i - 1);
|
||||
i += cnt;
|
||||
buffer[i++] = '\n';
|
||||
buffer[i] = 0;
|
||||
if (i + 256 > maxlen) break; // max line length in file 256 chars
|
||||
}
|
||||
if (i > 0) return i;
|
||||
file.close();
|
||||
state++; // intentional fall-through
|
||||
case 2: // footer
|
||||
Serial.println("Appending footer\n");
|
||||
strncpy(buffer, "</textarea><input type=\"submit\" value=\"Save\"></input></form></body></html>", maxlen);
|
||||
state++;
|
||||
return strlen(buffer);
|
||||
case 3: // end
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// bad idea. prone to buffer overflow. use at your own risk...
|
||||
const char *createEditForm(String filename) {
|
||||
Serial.println("Creating edit form");
|
||||
|
@ -868,6 +955,48 @@ const char *handleUpdatePost(AsyncWebServerRequest *request) {
|
|||
return "";
|
||||
}
|
||||
|
||||
const char *createKMLLive(const char *myIP) {
|
||||
char *ptr = message;
|
||||
|
||||
strcpy(ptr, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\"><NetworkLink><name>loads dynamic.kml</name><Link><href>http://");
|
||||
strcat(ptr, myIP);
|
||||
strcat(ptr, "/dynamic.kml</href><refreshMode>onInterval</refreshMode><refreshInterval>10</refreshInterval></Link></NetworkLink></kml>");
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void addSondeStatusKML(char *ptr, int i)
|
||||
{
|
||||
SondeInfo *s = &sonde.sondeList[i];
|
||||
|
||||
if (!s->validID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(ptr + strlen(ptr), "<Placemark id=\"%s\"><name>%s</name><Point><coordinates>%.6f,%.6f,%.0f</coordinates></Point><description>%3.3f MHz, Type: %s, h=%.0fm</description></Placemark>",
|
||||
s->id, s->id,
|
||||
s->lon, s->lat, s->alt,
|
||||
s->freq, sondeTypeStr[s->type], s->alt);
|
||||
}
|
||||
|
||||
const char *createKMLDynamic() {
|
||||
char *ptr = message;
|
||||
|
||||
strcpy(ptr, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\"><Document>");
|
||||
|
||||
for (int i = 0; i < sonde.nSonde; i++) {
|
||||
int snum = (i + sonde.currentSonde) % sonde.nSonde;
|
||||
if (sonde.sondeList[snum].active) {
|
||||
addSondeStatusKML(ptr, snum);
|
||||
}
|
||||
}
|
||||
|
||||
strcat(ptr, "</Document></kml>");
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
const char *sendGPX(AsyncWebServerRequest * request) {
|
||||
Serial.println("\n\n\n********GPX request\n\n");
|
||||
|
@ -894,6 +1023,9 @@ const char *sendGPX(AsyncWebServerRequest * request) {
|
|||
return message;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// no longer supported
|
||||
// tcp socket / json for android app is used now
|
||||
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
Serial.println("Websocket client connection received");
|
||||
|
@ -902,6 +1034,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
|
|||
Serial.println("Client disconnected");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
const char* PARAM_MESSAGE = "message";
|
||||
|
@ -965,14 +1098,27 @@ void SetupAsyncServer() {
|
|||
});
|
||||
|
||||
server.on("/edit.html", HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
|
||||
// new version:
|
||||
// Open file
|
||||
// store file object in request->_tempObject
|
||||
//request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
|
||||
const String filename = request->getParam(0)->value();
|
||||
File file = SPIFFS.open("/" + filename, "r");
|
||||
int state = 0;
|
||||
request->send("text/html", 0, [state, file, filename](uint8_t *buffer, size_t maxLen, size_t index) mutable -> size_t {
|
||||
Serial.printf("******* send callback: %d %d %d\n", state, maxLen, index);
|
||||
return streamEditForm(state, file, filename, (char *)buffer, maxLen, index);
|
||||
});
|
||||
});
|
||||
server.on("/edit.html", HTTP_POST, [](AsyncWebServerRequest * request) {
|
||||
const char *ret = handleEditPost(request);
|
||||
if (ret == NULL)
|
||||
request->send(200, "text/html", "<html><head>ERROR</head><body><p>Something went wrong. Uploaded file is empty.</p></body></hhtml>");
|
||||
else
|
||||
request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
|
||||
else {
|
||||
String f = request->getParam(0)->value();
|
||||
request->redirect("/edit.html?file=" + f);
|
||||
//request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
[](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
|
@ -982,7 +1128,7 @@ void SetupAsyncServer() {
|
|||
// Route to load style.css file
|
||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/style.css", "text/css");
|
||||
response->addHeader("Cache-Control","max-age=86400");
|
||||
response->addHeader("Cache-Control", "max-age=86400");
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
|
@ -991,6 +1137,14 @@ void SetupAsyncServer() {
|
|||
request->send(SPIFFS, "/index.html", String(), false, processor);
|
||||
});
|
||||
|
||||
server.on("/live.kml", HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
request->send(200, "application/vnd.google-earth.kml+xml", createKMLLive(sonde.ipaddr.c_str()));
|
||||
});
|
||||
|
||||
server.on("/dynamic.kml", HTTP_GET, [](AsyncWebServerRequest * request) {
|
||||
request->send(200, "application/vnd.google-earth.kml+xml", createKMLDynamic());
|
||||
});
|
||||
|
||||
server.onNotFound([](AsyncWebServerRequest * request) {
|
||||
if (request->method() == HTTP_OPTIONS) {
|
||||
request->send(200);
|
||||
|
@ -1006,10 +1160,6 @@ void SetupAsyncServer() {
|
|||
}
|
||||
});
|
||||
|
||||
// Set up web socket
|
||||
ws.onEvent(onWsEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
// Start server
|
||||
server.begin();
|
||||
}
|
||||
|
@ -1020,18 +1170,10 @@ int fetchWifiIndex(const char *id) {
|
|||
Serial.printf("Match for %s at %d\n", id, i);
|
||||
return i;
|
||||
}
|
||||
Serial.printf("No match: '%s' vs '%s'\n", id, networks[i].id.c_str());
|
||||
//Serial.printf("No match: '%s' vs '%s'\n", id, networks[i].id.c_str());
|
||||
const char *cfgid = networks[i].id.c_str();
|
||||
int len = strlen(cfgid);
|
||||
if (strlen(id) > len) len = strlen(id);
|
||||
Serial.print("SSID: ");
|
||||
for (int i = 0; i < len; i++) {
|
||||
Serial.printf("%02x ", id[i]);
|
||||
} Serial.println("");
|
||||
Serial.print("Conf: ");
|
||||
for (int i = 0; i < len; i++) {
|
||||
Serial.printf("%02x ", cfgid[i]);
|
||||
} Serial.println("");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -1137,10 +1279,40 @@ void unkHandler(T nmea) {
|
|||
if (*s == ',') return; /// no new course data
|
||||
int lastCourse = nmea.parseFloat(s, 0, NULL);
|
||||
Serial.printf("Course update: %d\n", lastCourse);
|
||||
} else if (strcmp(nmea.getMessageID(), "GST") == 0) {
|
||||
// get horizontal accuracy for android app on devices without gps
|
||||
// GPGST,time,rms,-,-,-,stdlat,stdlon,stdalt,cs
|
||||
const char *s = nmea.getSentence();
|
||||
while (*s && *s != ',') s++; // #0: GST
|
||||
if (*s == ',') s++; else return;
|
||||
while (*s && *s != ',') s++; // #1: time: skip
|
||||
if (*s == ',') s++; else return;
|
||||
while (*s && *s != ',') s++; // #1: rms: skip
|
||||
if (*s == ',') s++; else return;
|
||||
while (*s && *s != ',') s++; // #1: (-): skip
|
||||
if (*s == ',') s++; else return;
|
||||
while (*s && *s != ',') s++; // #1: (-): skip
|
||||
if (*s == ',') s++; else return;
|
||||
while (*s && *s != ',') s++; // #1: (-): skip
|
||||
if (*s == ',') s++; else return;
|
||||
// stdlat
|
||||
int stdlat = nmea.parseFloat(s, 1, NULL);
|
||||
while (*s && *s != ',') s++;
|
||||
if (*s == ',') s++; else return;
|
||||
// stdlong
|
||||
int stdlon = nmea.parseFloat(s, 1, NULL);
|
||||
// calculate position error as 1-signma horizontal RMS
|
||||
// I guess that is equivalent to Androids getAccurac()?
|
||||
int poserr = 0;
|
||||
if (stdlat < 10000 && stdlon < 10000) { // larger errors: no GPS fix, avoid overflow in *
|
||||
poserr = (int)(sqrt(0.5 * (stdlat * stdlat + stdlon * stdlon)));
|
||||
}
|
||||
//Serial.printf("\nHorizontal accuracy: %d, %d => %.1fm\n", stdlat, stdlon, 0.1*poserr);
|
||||
gpsPos.accuracy = poserr;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_GPS 1
|
||||
//#define DEBUG_GPS
|
||||
static bool gpsCourseOld;
|
||||
static int lastCourse;
|
||||
void gpsTask(void *parameter) {
|
||||
|
@ -1152,22 +1324,22 @@ void gpsTask(void *parameter) {
|
|||
//Serial.print(c);
|
||||
if (nmea.process(c)) {
|
||||
gpsPos.valid = nmea.isValid();
|
||||
if(gpsPos.valid) {
|
||||
gpsPos.lon = nmea.getLongitude()*0.000001;
|
||||
gpsPos.lat = nmea.getLatitude()*0.000001;
|
||||
long alt = 0;
|
||||
nmea.getAltitude(alt);
|
||||
gpsPos.alt=(int)(alt/1000);
|
||||
gpsPos.course = (int)(nmea.getCourse()/1000);
|
||||
gpsCourseOld = false;
|
||||
if(gpsPos.course==0) {
|
||||
// either north or not new
|
||||
if(lastCourse!=0) // use old value...
|
||||
{
|
||||
gpsCourseOld = true;
|
||||
gpsPos.course = lastCourse;
|
||||
}
|
||||
}
|
||||
if (gpsPos.valid) {
|
||||
gpsPos.lon = nmea.getLongitude() * 0.000001;
|
||||
gpsPos.lat = nmea.getLatitude() * 0.000001;
|
||||
long alt = 0;
|
||||
nmea.getAltitude(alt);
|
||||
gpsPos.alt = (int)(alt / 1000);
|
||||
gpsPos.course = (int)(nmea.getCourse() / 1000);
|
||||
gpsCourseOld = false;
|
||||
if (gpsPos.course == 0) {
|
||||
// either north or not new
|
||||
if (lastCourse != 0) // use old value...
|
||||
{
|
||||
gpsCourseOld = true;
|
||||
gpsPos.course = lastCourse;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_GPS
|
||||
uint8_t hdop = nmea.getHDOP();
|
||||
|
@ -1181,25 +1353,41 @@ void gpsTask(void *parameter) {
|
|||
|
||||
#define UBX_SYNCH_1 0xB5
|
||||
#define UBX_SYNCH_2 0x62
|
||||
uint8_t ubx_set9k6[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F};
|
||||
uint8_t ubx_factorydef[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0x17, 0x8A };
|
||||
uint8_t ubx_set9k6[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F};
|
||||
uint8_t ubx_factorydef[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0x17, 0x8A };
|
||||
uint8_t ubx_hardreset[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x04, 4, 0, 0xff, 0xff, 0, 0, 0x0C, 0x5D };
|
||||
// GPGST: Class 0xF0 Id 0x07
|
||||
uint8_t ubx_enable_gpgst[] = {UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x01, 3, 0, 0xF0, 0x07, 2, 0x03, 0x1F};
|
||||
|
||||
void initGPS() {
|
||||
if (sonde.config.gps_rxd < 0) return; // GPS disabled
|
||||
if (sonde.config.gps_txd >= 0) { // TX enable, thus try setting baud to 9600 and do a factory reset
|
||||
Serial.println("Trying to reset GPS...");
|
||||
Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_factorydef, sizeof(ubx_factorydef));
|
||||
delay(1000);
|
||||
File testfile = SPIFFS.open("/GPSRESET", FILE_READ);
|
||||
if (testfile && !testfile.isDirectory()) {
|
||||
testfile.close();
|
||||
Serial.println("GPS factory reset...");
|
||||
Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
|
||||
delay(100);
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
Serial2.write(ubx_factorydef, sizeof(ubx_factorydef));
|
||||
delay(100);
|
||||
Serial2.write(ubx_hardreset, sizeof(ubx_hardreset));
|
||||
delay(5000);
|
||||
SPIFFS.remove("/GPSRESET");
|
||||
} else if (testfile) {
|
||||
Serial.println("GPS reset file: not found/isdir");
|
||||
testfile.close();
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
}
|
||||
// Enable GPGST messages
|
||||
Serial2.write(ubx_enable_gpgst, sizeof(ubx_enable_gpgst));
|
||||
} else {
|
||||
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
|
||||
}
|
||||
|
@ -1210,6 +1398,12 @@ void initGPS() {
|
|||
NULL); /* task handle*/
|
||||
}
|
||||
|
||||
const char *getStateStr(int what) {
|
||||
if(what<0 || what>=(sizeof(mainStateStr)/sizeof(const char *)))
|
||||
return "--";
|
||||
else
|
||||
return mainStateStr[what];
|
||||
}
|
||||
|
||||
void sx1278Task(void *parameter) {
|
||||
/* new strategy:
|
||||
|
@ -1225,13 +1419,12 @@ void sx1278Task(void *parameter) {
|
|||
while (1) {
|
||||
if (rxtask.activate >= 128) {
|
||||
// activating sx1278 background task...
|
||||
Serial.printf("rx task: activate=%d mainstate=%d\n", rxtask.activate, rxtask.mainState);
|
||||
Serial.printf("RXtask: start DECODER for sonde %d (was %s)\n", rxtask.activate&0x7f, getStateStr(rxtask.mainState));
|
||||
rxtask.mainState = ST_DECODER;
|
||||
rxtask.currentSonde = rxtask.activate & 0x7F;
|
||||
Serial.println("rx task: calling sonde.setup()");
|
||||
sonde.setup();
|
||||
} else if (rxtask.activate != -1) {
|
||||
Serial.printf("rx task: activate=%d mainstate=%d\n", rxtask.activate, rxtask.mainState);
|
||||
Serial.printf("RXtask: start %s (was %s)\n", getStateStr(rxtask.activate), getStateStr(rxtask.mainState));
|
||||
rxtask.mainState = rxtask.activate;
|
||||
}
|
||||
rxtask.activate = -1;
|
||||
|
@ -1479,7 +1672,7 @@ extern esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_
|
|||
|
||||
void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name)
|
||||
{
|
||||
printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",function_name, requested_size, caps);
|
||||
printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n", function_name, requested_size, caps);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1540,7 +1733,7 @@ void setup()
|
|||
|
||||
// NOT TTGO v1 (fingerprint 64) or Heltec v1/v2 board (fingerprint 4)
|
||||
// and NOT TTGO Lora32 v2.1_1.6 (fingerprint 31/63)
|
||||
if ( ( (sonde.fingerprint&(64+31)) != 31) && ((sonde.fingerprint & 16) == 16) ) {
|
||||
if ( ( (sonde.fingerprint & (64 + 31)) != 31) && ((sonde.fingerprint & 16) == 16) ) {
|
||||
// FOr T-Beam 1.0
|
||||
for (int i = 0; i < 10; i++) { // try multiple times
|
||||
Wire.begin(21, 22);
|
||||
|
@ -1564,12 +1757,12 @@ void setup()
|
|||
axp.adc1Enable(AXP202_VBUS_VOL_ADC1, 1);
|
||||
axp.adc1Enable(AXP202_VBUS_CUR_ADC1, 1);
|
||||
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
|
||||
if(sonde.config.button2_axp) {
|
||||
if (sonde.config.button2_axp) {
|
||||
pinMode(PMU_IRQ, INPUT_PULLUP);
|
||||
attachInterrupt(PMU_IRQ, [] {
|
||||
pmu_irq = true;
|
||||
}, FALLING);
|
||||
//axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1);
|
||||
//axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1);
|
||||
axp.enableIRQ( AXP202_PEK_LONGPRESS_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1 );
|
||||
axp.clearIRQ();
|
||||
}
|
||||
|
@ -1625,7 +1818,7 @@ void setup()
|
|||
sonde.clearDisplay();
|
||||
|
||||
setupWifiList();
|
||||
Serial.printf("before disp.initFromFile... layouts is %p", disp.layouts);
|
||||
Serial.printf("before disp.initFromFile... layouts is %p\n", disp.layouts);
|
||||
|
||||
disp.initFromFile(sonde.config.screenfile);
|
||||
Serial.printf("disp.initFromFile... layouts is %p", disp.layouts);
|
||||
|
@ -1779,39 +1972,47 @@ static const char *action2text(uint8_t action) {
|
|||
#define RDZ_DATA_LEN 128
|
||||
|
||||
void parseGpsJson(char *data) {
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
// very simple json parser: look for ", then key, then ", then :, then number, then , or } or \0
|
||||
for(int i=0; i<RDZ_DATA_LEN; i++) {
|
||||
if(key==NULL) {
|
||||
if(data[i]!='"') continue;
|
||||
key=data+i+1;
|
||||
i+=2;
|
||||
continue;
|
||||
}
|
||||
if(value==NULL) {
|
||||
if(data[i]!=':') continue;
|
||||
value = data+i+1;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if(data[i]==',' || data[i]=='}' || data[i]==0) {
|
||||
// get value
|
||||
double val = strtod(value,NULL);
|
||||
// get data
|
||||
if(strncmp(key,"lat",3)==0) { gpsPos.lat = val; }
|
||||
else if(strncmp(key,"lon",3)==0) { gpsPos.lon = val; }
|
||||
else if(strncmp(key,"alt",3)==0) { gpsPos.alt = (int)val; }
|
||||
else if(strncmp(key,"course",6)==0) { gpsPos.course = (int)val; }
|
||||
gpsPos.valid = true;
|
||||
|
||||
// next item:
|
||||
if(data[i]!=',') break;
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
}
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
// very simple json parser: look for ", then key, then ", then :, then number, then , or } or \0
|
||||
for (int i = 0; i < RDZ_DATA_LEN; i++) {
|
||||
if (key == NULL) {
|
||||
if (data[i] != '"') continue;
|
||||
key = data + i + 1;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
Serial.printf("Parse result: lat=%f, lon=%f, alt=%d, valid=%d\n", gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.valid);
|
||||
if (value == NULL) {
|
||||
if (data[i] != ':') continue;
|
||||
value = data + i + 1;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (data[i] == ',' || data[i] == '}' || data[i] == 0) {
|
||||
// get value
|
||||
double val = strtod(value, NULL);
|
||||
// get data
|
||||
if (strncmp(key, "lat", 3) == 0) {
|
||||
gpsPos.lat = val;
|
||||
}
|
||||
else if (strncmp(key, "lon", 3) == 0) {
|
||||
gpsPos.lon = val;
|
||||
}
|
||||
else if (strncmp(key, "alt", 3) == 0) {
|
||||
gpsPos.alt = (int)val;
|
||||
}
|
||||
else if (strncmp(key, "course", 6) == 0) {
|
||||
gpsPos.course = (int)val;
|
||||
}
|
||||
gpsPos.valid = true;
|
||||
|
||||
// next item:
|
||||
if (data[i] != ',') break;
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
}
|
||||
}
|
||||
Serial.printf("Parse result: lat=%f, lon=%f, alt=%d, valid=%d\n", gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.valid);
|
||||
}
|
||||
|
||||
static char rdzData[RDZ_DATA_LEN];
|
||||
|
@ -1826,9 +2027,9 @@ void loopDecoder() {
|
|||
// TODO: update displayed sonde?
|
||||
|
||||
if (action != ACT_NONE) {
|
||||
Serial.printf("Loop: triggering action %s (%d)\n", action2text(action), action);
|
||||
action = sonde.updateState(action);
|
||||
Serial.printf("Loop: action is %d, sonde index is %d\n", action, sonde.currentSonde);
|
||||
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);
|
||||
action = newact;
|
||||
if (action != 255) {
|
||||
if (action == ACT_DISPLAY_SPECTRUM) {
|
||||
enterMode(ST_SPECTRUM);
|
||||
|
@ -1838,10 +2039,7 @@ void loopDecoder() {
|
|||
enterMode(ST_WIFISCAN);
|
||||
return;
|
||||
}
|
||||
// no... we are already in DECODER mode, so no need to do anything!?
|
||||
//else if (action == ACT_NEXTSONDE) enterMode(ST_DECODER); // update rx background task
|
||||
}
|
||||
Serial.printf("current main is %d, current rxtask is %d\n", sonde.currentSonde, rxtask.currentSonde);
|
||||
}
|
||||
|
||||
if (!tncclient.connected()) {
|
||||
|
@ -1858,33 +2056,24 @@ void loopDecoder() {
|
|||
}
|
||||
Serial.println("");
|
||||
}
|
||||
#if 0
|
||||
if (!rdzclient.connected()) {
|
||||
rdzclient = rdzserver.available();
|
||||
if(rdzclient.connected()) {
|
||||
Serial.println("RDZ JSON socket: new connection");
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (rdzserver.hasClient()) {
|
||||
Serial.println("TCP JSON socket: new connection");
|
||||
if(rdzclient) rdzclient.stop();
|
||||
if (rdzclient) rdzclient.stop();
|
||||
rdzclient = rdzserver.available();
|
||||
}
|
||||
#endif
|
||||
if(rdzclient.available()) {
|
||||
if (rdzclient.available()) {
|
||||
Serial.print("RDZ JSON socket: received ");
|
||||
while(rdzclient.available()) {
|
||||
while (rdzclient.available()) {
|
||||
char c = (char)rdzclient.read();
|
||||
Serial.print(c);
|
||||
if(c=='\n'||c=='}'||rdzDataPos>=RDZ_DATA_LEN) {
|
||||
if (c == '\n' || c == '}' || rdzDataPos >= RDZ_DATA_LEN) {
|
||||
// parse GPS position from phone
|
||||
rdzData[rdzDataPos] = c;
|
||||
if(rdzDataPos>2) parseGpsJson(rdzData);
|
||||
rdzDataPos = 0;
|
||||
rdzData[rdzDataPos] = c;
|
||||
if (rdzDataPos > 2) parseGpsJson(rdzData);
|
||||
rdzDataPos = 0;
|
||||
}
|
||||
else {
|
||||
rdzData[rdzDataPos++] = c;
|
||||
rdzData[rdzDataPos++] = c;
|
||||
}
|
||||
}
|
||||
Serial.println("");
|
||||
|
@ -1914,82 +2103,101 @@ void loopDecoder() {
|
|||
tncclient.write(raw, rawlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// send to MQTT if enabled
|
||||
if (connected && mqttEnabled) {
|
||||
Serial.println("Sending sonde info via MQTT");
|
||||
mqttclient.publishPacket(s);
|
||||
}
|
||||
|
||||
// also send to web socket
|
||||
//TODO
|
||||
}
|
||||
// always send data, even if not valid....
|
||||
if (rdzclient.connected()) {
|
||||
Serial.println("Sending position via TCP as rdzJSON");
|
||||
char raw[1024];
|
||||
const char *typestr = s->typestr;
|
||||
if(*typestr==0) typestr = sondeTypeStr[s->type];
|
||||
int len = snprintf(raw, 1024, "{"
|
||||
"\"res\": %d,"
|
||||
"\"type\": \"%s\","
|
||||
"\"active\": %d,"
|
||||
"\"freq\": %.2f,"
|
||||
"\"id\": \"%s\","
|
||||
"\"ser\": \"%s\","
|
||||
"\"validId\": %d,"
|
||||
"\"launchsite\": \"%s\","
|
||||
"\"lat\": %.5f,"
|
||||
"\"lon\": %.5f,"
|
||||
"\"alt\": %.1f,"
|
||||
"\"vs\": %.1f,"
|
||||
"\"hs\": %.1f,"
|
||||
"\"dir\": %.1f,"
|
||||
"\"sats\": %d,"
|
||||
"\"validPos\": %d,"
|
||||
"\"time\": %d,"
|
||||
"\"sec\": %d,"
|
||||
"\"frame\": %d,"
|
||||
"\"validTime\": %d,"
|
||||
"\"rssi\": %d,"
|
||||
"\"afc\": %d,"
|
||||
"\"launchKT\": %d,"
|
||||
"\"burstKT\": %d,"
|
||||
"\"countKT\": %d,"
|
||||
"\"crefKT\": %d"
|
||||
"}\n",
|
||||
res&0xff,
|
||||
typestr,
|
||||
(int)s->active,
|
||||
s->freq,
|
||||
s->id,
|
||||
s->ser,
|
||||
(int)s->validID,
|
||||
s->launchsite,
|
||||
s->lat,
|
||||
s->lon,
|
||||
s->alt,
|
||||
s->vs,
|
||||
s->hs,
|
||||
s->dir,
|
||||
s->sats,
|
||||
s->validPos,
|
||||
s->time,
|
||||
s->sec,
|
||||
s->frame,
|
||||
(int)s->validTime,
|
||||
s->rssi,
|
||||
s->afc,
|
||||
s->launchKT,
|
||||
s->burstKT,
|
||||
s->countKT,
|
||||
s->crefKT
|
||||
);
|
||||
|
||||
rdzclient.write(raw, len>1024?1024:len);
|
||||
Serial.println("Sending position via TCP as rdzJSON");
|
||||
char raw[1024];
|
||||
char gps[128];
|
||||
const char *typestr = s->typestr;
|
||||
if (*typestr == 0) typestr = sondeTypeStr[s->type];
|
||||
// TODO: only if GPS is valid...
|
||||
if (gpsPos.valid) {
|
||||
snprintf(gps, 128, ", \"gpslat\": %f"
|
||||
"\"gpslon\": %f,"
|
||||
"\"gpsalt\": %d,"
|
||||
"\"gpsacc\": %d,"
|
||||
"\"gpsdir\": %d",
|
||||
gpsPos.lat, gpsPos.lon, gpsPos.alt, gpsPos.accuracy, gpsPos.course);
|
||||
} else {
|
||||
*gps = 0;
|
||||
}
|
||||
//
|
||||
int len = snprintf(raw, 1024, "{"
|
||||
"\"res\": %d,"
|
||||
"\"type\": \"%s\","
|
||||
"\"active\": %d,"
|
||||
"\"freq\": %.2f,"
|
||||
"\"id\": \"%s\","
|
||||
"\"ser\": \"%s\","
|
||||
"\"validId\": %d,"
|
||||
"\"launchsite\": \"%s\","
|
||||
"\"lat\": %.5f,"
|
||||
"\"lon\": %.5f,"
|
||||
"\"alt\": %.1f,"
|
||||
"\"vs\": %.1f,"
|
||||
"\"hs\": %.1f,"
|
||||
"\"dir\": %.1f,"
|
||||
"\"sats\": %d,"
|
||||
"\"validPos\": %d,"
|
||||
"\"time\": %d,"
|
||||
"\"sec\": %d,"
|
||||
"\"frame\": %d,"
|
||||
"\"validTime\": %d,"
|
||||
"\"rssi\": %d,"
|
||||
"\"afc\": %d,"
|
||||
"\"launchKT\": %d,"
|
||||
"\"burstKT\": %d,"
|
||||
"\"countKT\": %d,"
|
||||
"\"crefKT\": %d"
|
||||
"%s"
|
||||
"}\n",
|
||||
res & 0xff,
|
||||
typestr,
|
||||
(int)s->active,
|
||||
s->freq,
|
||||
s->id,
|
||||
s->ser,
|
||||
(int)s->validID,
|
||||
s->launchsite,
|
||||
s->lat,
|
||||
s->lon,
|
||||
s->alt,
|
||||
s->vs,
|
||||
s->hs,
|
||||
s->dir,
|
||||
s->sats,
|
||||
s->validPos,
|
||||
s->time,
|
||||
s->sec,
|
||||
s->frame,
|
||||
(int)s->validTime,
|
||||
s->rssi,
|
||||
s->afc,
|
||||
s->launchKT,
|
||||
s->burstKT,
|
||||
s->countKT,
|
||||
s->crefKT,
|
||||
gps
|
||||
);
|
||||
//Serial.println("Writing rdzclient...");
|
||||
if (len > 1024) len = 1024;
|
||||
int wlen = rdzclient.write(raw, len);
|
||||
if (wlen != len) {
|
||||
Serial.println("Writing rdzClient not OK, closing connection");
|
||||
rdzclient.stop();
|
||||
rdzclient = NULL;
|
||||
}
|
||||
//Serial.println("Writing rdzclient OK");
|
||||
}
|
||||
Serial.print("updateDisplay started... ");
|
||||
Serial.print("MAIN: updateDisplay started\n");
|
||||
if (forceReloadScreenConfig) {
|
||||
disp.initFromFile(sonde.config.screenfile);
|
||||
sonde.clearDisplay();
|
||||
|
@ -1997,7 +2205,7 @@ void loopDecoder() {
|
|||
}
|
||||
int t = millis();
|
||||
sonde.updateDisplay();
|
||||
Serial.printf("updateDisplay done (after %d ms)\n", (int)(millis() - t));
|
||||
Serial.printf("MAIN: updateDisplay done (after %d ms)\n", (int)(millis() - t));
|
||||
}
|
||||
|
||||
void setCurrentDisplay(int value) {
|
||||
|
@ -2085,7 +2293,7 @@ void enableNetwork(bool enable) {
|
|||
tncserver.begin();
|
||||
rdzserver.begin();
|
||||
}
|
||||
|
||||
|
||||
if (sonde.config.mqtt.active && strlen(sonde.config.mqtt.host) > 0) {
|
||||
mqttEnabled = true;
|
||||
mqttclient.init(sonde.config.mqtt.host, sonde.config.mqtt.port, sonde.config.mqtt.id, sonde.config.mqtt.username, sonde.config.mqtt.password, sonde.config.mqtt.prefix);
|
||||
|
@ -2374,19 +2582,12 @@ void loopWifiScan() {
|
|||
int index = -1;
|
||||
int n = WiFi.scanNetworks();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Serial.print("Network name: ");
|
||||
String ssid = WiFi.SSID(i);
|
||||
Serial.println(ssid);
|
||||
disp.rdis->drawString(0, dispys * (1 + line), ssid.c_str());
|
||||
line = (line + 1) % (disph / dispys);
|
||||
Serial.print("Signal strength: ");
|
||||
Serial.println(WiFi.RSSI(i));
|
||||
Serial.print("MAC address: ");
|
||||
Serial.println(WiFi.BSSIDstr(i));
|
||||
Serial.print("Encryption type: ");
|
||||
String mac = WiFi.BSSIDstr(i);
|
||||
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
|
||||
Serial.println(encryptionTypeDescription);
|
||||
Serial.println("-----------------------");
|
||||
Serial.printf("Network %s: RSSI %d, MAC %s, enc: %s\n", ssid.c_str(), WiFi.RSSI(i), mac.c_str(), encryptionTypeDescription.c_str());
|
||||
int curidx = fetchWifiIndex(ssid.c_str());
|
||||
if (curidx >= 0 && index == -1) {
|
||||
index = curidx;
|
||||
|
@ -2624,8 +2825,8 @@ void execOTA() {
|
|||
|
||||
|
||||
void loop() {
|
||||
Serial.printf("\nRunning main loop in state %d [currentDisp:%d, lastDiso:%d]. free heap: %d;\n",
|
||||
mainState, currentDisplay, lastDisplay, ESP.getFreeHeap());
|
||||
Serial.printf("\nMAIN: Running loop in state %d [currentDisp:%d, lastDisp:%d]. free heap: %d, unused stack: %d\n",
|
||||
mainState, currentDisplay, lastDisplay, ESP.getFreeHeap(), uxTaskGetStackHighWaterMark(0));
|
||||
switch (mainState) {
|
||||
case ST_DECODER:
|
||||
#ifndef DISABLE_MAINRX
|
||||
|
@ -2662,5 +2863,4 @@ void loop() {
|
|||
lastMqttUptime = now;
|
||||
}
|
||||
|
||||
Serial.printf("Unused stack: %d\n", uxTaskGetStackHighWaterMark(0));
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
+
|
|
@ -25,8 +25,9 @@
|
|||
#tft_rs=2
|
||||
#tft_cs=0
|
||||
tft_orient=1
|
||||
#tft_modeflip=0
|
||||
#gps_rxd=-1
|
||||
gps_txd=-1
|
||||
#gps_txd=-1
|
||||
# Show AFC value (for RS41 and M10/M20, maybe also DFM, but not useful for RS92)
|
||||
showafc=1
|
||||
# Frequency correction, in Hz
|
||||
|
@ -78,6 +79,14 @@ rs92.alt2d=480
|
|||
dfm.agcbw=20800
|
||||
dfm.rxbw=10400
|
||||
#-------------------------------#
|
||||
# ftp server for RINEX data (for RS92)
|
||||
# YYYY/DDD/brdcDDD0.YYn.gz is appended
|
||||
# s1: igs.bkg.bund.de/IGS/BRDC/
|
||||
# s2: www.ngs.noaa.gov/cors/rinex/
|
||||
#-------------------------------#
|
||||
ephftp=www.ngs.noaa.gov/cors/rinex/
|
||||
#ephftp=igs.bkg.bund.de/IGS/BRDC/
|
||||
#-------------------------------#
|
||||
# axudp for sending to aprsmap
|
||||
#-------------------------------#
|
||||
# local use only, do not feed to public services
|
||||
|
@ -104,7 +113,7 @@ tcp.idformat=0
|
|||
# data not sanitized / quality checked, outliers not filtered out
|
||||
mqtt.active=0
|
||||
mqtt.id=rdz_sonde_server
|
||||
mqtt.ip=
|
||||
mqtt.host=
|
||||
mqtt.port=1883
|
||||
mqtt.username=
|
||||
mqtt.password=
|
||||
|
|
|
@ -1,64 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>RDZSonde Server</title>
|
||||
<title>rdzTTGOsonde Server</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>RDZSonde Server</h2>
|
||||
<!--
|
||||
<p>GPIO state: <strong> %STATE%</strong></p>
|
||||
<p><a href="/on"><button class="button">ON</button></a></p>
|
||||
<p><a href="/off"><button class="button button2">OFF</button></a></p>
|
||||
-->
|
||||
<div class="wrapper"><div class="header">
|
||||
<h1>RDZSonde Server</h1>
|
||||
|
||||
<div class="tab">
|
||||
<button class="tablinks" onclick="selTab(event,'QRG')" id="defaultTab">QRG</button>
|
||||
<button class="tablinks" onclick="selTab(event,'WiFi')">WiFi</button>
|
||||
<button class="tablinks" onclick="selTab(event,'Data')">Data</button>
|
||||
<!-- <button class="tablinks" onclick="selTab(event,'SondeMap')">SondeMap</button> -->
|
||||
<button class="tablinks" onclick="selTab(event,'Config')">Config</button>
|
||||
<button class="tablinks" onclick="selTab(event,'Control')">Control</button>
|
||||
<button class="tablinks" onclick="selTab(event,'About')">About</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="QRG" class="tabcontent" data-src="qrg.html">
|
||||
<h3> QRG - Setup</h3>
|
||||
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
<iframe class="tci" src="" ></iframe>
|
||||
</div>
|
||||
|
||||
<div id="WiFi" class="tabcontent" data-src="wifi.html">
|
||||
<h3> WiFi - Settings</h3>
|
||||
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
<iframe class="tci" src="" ></iframe>
|
||||
</div>
|
||||
|
||||
<div id="Data" class="tabcontent" data-src="status.html">
|
||||
<h3>Data</h3>
|
||||
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
<iframe class="tci" src="" ></iframe>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div id="SondeMap" class="tabcontent" data-src="https://wetterson.de/karte/">
|
||||
<iframe src="" style="border:none;" width="98%%" height="98%%"></iframe>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div id="Config" class="tabcontent" data-src="config.html">
|
||||
<h3>Configuration</h3>
|
||||
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
<iframe class="tci" src="" ></iframe>
|
||||
</div>
|
||||
|
||||
<div id="Control" class="tabcontent" data-src="control.html">
|
||||
<h3>Control</h3>
|
||||
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
|
||||
<iframe class="tci" src="" ></iframe>
|
||||
</div>
|
||||
|
||||
<div id="About" class="tabcontent">
|
||||
<h3>About</h3>
|
||||
<div class="tci">
|
||||
%VERSION_NAME%<br>
|
||||
Copyright © 2019-2020 by Hansi Reiser, DL9RDZ<br>
|
||||
Copyright © 2019-2021 by Hansi Reiser, DL9RDZ<br>
|
||||
(version %VERSION_ID%)<br><br>
|
||||
with contributions by Vigor and Xavier (M20 support), <a href="https://www.dl2mf.de/" target="_blank">Meinhard Guenther, DL2MF</a>,
|
||||
<a href="https://github.com/bazjo">Johannes</a>, <a href="http://www.p1337.synology.me/dokuwiki/doku.php?id=public:wettersonden">Robert Stefanowicz</a>,
|
||||
|
@ -68,14 +54,16 @@
|
|||
<br>
|
||||
Autodetect info: %AUTODETECT_INFO%<br>
|
||||
<br>
|
||||
This program is free software; you can redistribute it and/or<br>
|
||||
modify it under the terms of the GNU General Public License as<br>
|
||||
published by the Free Software Foundation; either version 2 of<br>
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.<br>
|
||||
see <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
|
||||
for details
|
||||
See <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
|
||||
for details.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
function selTab(evt, id) {
|
||||
var i, tabcontent, tablinks;
|
||||
|
@ -93,7 +81,7 @@ function selTab(evt, id) {
|
|||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
var act = document.getElementById(id);
|
||||
act.style.display = "block";
|
||||
act.style.display = "flex";
|
||||
evt.currentTarget.className += " active";
|
||||
|
||||
var link = act.dataset.src;
|
||||
|
@ -104,6 +92,7 @@ function selTab(evt, id) {
|
|||
}
|
||||
document.getElementById("defaultTab").click();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
let stypes=new Map();
|
||||
stypes.set('4', 'RS41');
|
||||
stypes.set('R', 'RS92');
|
||||
stypes.set('9', 'DFM9 (old)');
|
||||
stypes.set('6', 'DFM6 (old)');
|
||||
stypes.set('D', 'DFM');
|
||||
stypes.set('M', 'M10');
|
||||
stypes.set('2', 'M20');
|
||||
|
||||
/* Used by qrg.html in RX_FSK.ino */
|
||||
function prep() {
|
||||
var stlist=document.querySelectorAll("input.stype");
|
||||
for(txt of stlist){
|
||||
var val=txt.getAttribute('value'); var nam=txt.getAttribute('name');
|
||||
var sel=document.createElement('select');
|
||||
sel.setAttribute('name',nam);
|
||||
for(stype of stypes) {
|
||||
var opt=document.createElement('option');
|
||||
opt.value=stype[0];
|
||||
opt.innerHTML=stype[1];
|
||||
if(stype[0]==val) { opt.setAttribute('selected','selected'); }
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
txt.replaceWith(sel);
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = prep;
|
|
@ -4,6 +4,18 @@ body, html {
|
|||
font-family: Arial;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
.tci {
|
||||
flex-grow: 1; border: none; margin: 0; padding: 0;
|
||||
}
|
||||
.footer {
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
|
@ -31,7 +43,8 @@ td#sfreq {
|
|||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
padding: 10px 10px;
|
||||
width: 16vw;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
|
@ -42,13 +55,21 @@ td#sfreq {
|
|||
.tab button.active {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
.tabcontent {
|
||||
display: none;
|
||||
flex: 1;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
html {
|
||||
|
@ -59,12 +80,12 @@ html {
|
|||
}
|
||||
h1{
|
||||
color: #0F3376;
|
||||
padding: 2vh;
|
||||
font-size: 24px
|
||||
}
|
||||
p{
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.button {
|
||||
.canberemoved_button {
|
||||
display: inline-block;
|
||||
background-color: #008CBA;
|
||||
border: none;
|
||||
|
@ -79,3 +100,31 @@ p{
|
|||
.button2 {
|
||||
background-color: #f44336;
|
||||
}
|
||||
.save {
|
||||
background-color: #0F3376;
|
||||
border: black;
|
||||
border-width: 1;
|
||||
color: white;
|
||||
padding: 8px 30px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
margin: 0
|
||||
}
|
||||
.ctlbtn {
|
||||
background-color: #ccc;
|
||||
border: black;
|
||||
border-width: 1;
|
||||
color: black;
|
||||
padding: 4px 30px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin: 2;
|
||||
font-size: 4vh;
|
||||
}
|
||||
.update {
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const char *version_name = "rdzTTGOsonde";
|
||||
const char *version_id = "devel20210126";
|
||||
const char *version_id = "devel20210315";
|
||||
const int SPIFFS_MAJOR=2;
|
||||
const int SPIFFS_MINOR=8;
|
||||
const int SPIFFS_MINOR=10;
|
||||
|
|
|
@ -412,7 +412,7 @@ void DFM::decodeDAT(uint8_t *dat)
|
|||
case 2:
|
||||
{
|
||||
float lat, vh;
|
||||
lat = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]);
|
||||
lat = (int32_t)(((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]));
|
||||
vh = ((uint16_t)dat[4]<<8) + dat[5];
|
||||
Serial.print("GPS-lat: "); Serial.print(lat*0.0000001);
|
||||
Serial.print(", hor-V: "); Serial.print(vh*0.01);
|
||||
|
@ -424,7 +424,7 @@ void DFM::decodeDAT(uint8_t *dat)
|
|||
case 3:
|
||||
{
|
||||
float lon, dir;
|
||||
lon = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + (uint32_t)dat[3];
|
||||
lon = (int32_t)(((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + (uint32_t)dat[3]);
|
||||
dir = ((uint16_t)dat[4]<<8) + dat[5];
|
||||
Serial.print("GPS-lon: "); Serial.print(lon*0.0000001);
|
||||
Serial.print(", dir: "); Serial.print(dir*0.01);
|
||||
|
|
|
@ -339,6 +339,7 @@ static int ngfx = sizeof(gfl)/sizeof(GFXfont *);
|
|||
void ILI9225Display::begin() {
|
||||
tft = new MY_ILI9225(sonde.config.oled_rst, sonde.config.tft_rs, sonde.config.tft_cs,
|
||||
sonde.config.oled_sda, sonde.config.oled_scl, TFT_LED, TFT_BRIGHTNESS);
|
||||
tft->setModeFlip(sonde.config.tft_modeflip);
|
||||
tft->begin(spiDisp);
|
||||
tft->setOrientation(sonde.config.tft_orient);
|
||||
}
|
||||
|
@ -665,7 +666,7 @@ int Display::allocDispInfo(int entries, DispInfo *d, char *label)
|
|||
d->timeouts = (int16_t *)mem;
|
||||
|
||||
d->label = label;
|
||||
Serial.printf("allocated %d bytes (%d entries) for %p (addr=%p)\n", totalsize, entries, d, d->de);
|
||||
Serial.printf("%s: alloc %d bytes (%d entries) for %p (addr=%p)\n", label, totalsize, entries, d, d->de);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -726,7 +727,7 @@ void Display::parseDispElement(char *text, DispEntry *de)
|
|||
case 'f':
|
||||
de->func = disp.drawFreq;
|
||||
de->extra = strdup(text+1);
|
||||
Serial.printf("parsing 'f' entry: extra is '%s'\n", de->extra);
|
||||
//Serial.printf("parsing 'f' entry: extra is '%s'\n", de->extra);
|
||||
break;
|
||||
case 'n':
|
||||
// IP address / small always uses tiny font on TFT for backward compatibility
|
||||
|
@ -780,8 +781,8 @@ void Display::parseDispElement(char *text, DispEntry *de)
|
|||
de->extra = (char *)circinfo;
|
||||
} else {
|
||||
de->extra = strdup(text+1);
|
||||
//Serial.printf("parsing 'g' entry: extra is '%s'\n", de->extra);
|
||||
}
|
||||
Serial.printf("parsing 'g' entry: extra is '%s'\n", de->extra);
|
||||
break;
|
||||
case 'r':
|
||||
de->func = disp.drawRSSI; break;
|
||||
|
@ -839,7 +840,7 @@ int Display::countEntries(File f) {
|
|||
break;
|
||||
}
|
||||
f.seek(pos, SeekSet);
|
||||
Serial.printf("Counted %d entries\n", n);
|
||||
//Serial.printf("Counted %d entries\n", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -848,7 +849,7 @@ void Display::initFromFile(int index) {
|
|||
if(index>0) {
|
||||
char file[20];
|
||||
snprintf(file, 20, "/screens%d.txt", index);
|
||||
Serial.printf("Trying %i (%s)\n", index, file);
|
||||
Serial.printf("Reading %s\n", file);
|
||||
d = SPIFFS.open(file, "r");
|
||||
if(!d || d.available()==0 ) { Serial.printf("%s not found, using /screens.txt\n", file); }
|
||||
}
|
||||
|
@ -888,6 +889,7 @@ void Display::initFromFile(int index) {
|
|||
case -1: // wait for start of screen (@)
|
||||
{
|
||||
if(*s != '@') {
|
||||
if(*s==0 || *s==10 || *s==13) continue;
|
||||
Serial.printf("Illegal start of screen: %s\n", s);
|
||||
continue;
|
||||
}
|
||||
|
@ -896,7 +898,6 @@ void Display::initFromFile(int index) {
|
|||
DebugPrintf(DEBUG_SPARSER,"Reading entry with %d elements\n", entrysize);
|
||||
idx++;
|
||||
int res = allocDispInfo(entrysize, &newlayouts[idx], label);
|
||||
Serial.printf("allocDispInfo: idx %d: label is %p - %s\n",idx,newlayouts[idx].label, newlayouts[idx].label);
|
||||
if(res<0) {
|
||||
Serial.println("Error allocating memory for disp info");
|
||||
continue;
|
||||
|
@ -917,7 +918,7 @@ void Display::initFromFile(int index) {
|
|||
if(newlayouts[idx].timeouts[1]>0) newlayouts[idx].timeouts[1]*=1000;
|
||||
if(newlayouts[idx].timeouts[2]>0) newlayouts[idx].timeouts[2]*=1000;
|
||||
//sscanf(s+6, "%hd,%hd,%hd", newlayouts[idx].timeouts, newlayouts[idx].timeouts+1, newlayouts[idx].timeouts+2);
|
||||
Serial.printf("timer values: %d, %d, %d\n", newlayouts[idx].timeouts[0], newlayouts[idx].timeouts[1], newlayouts[idx].timeouts[2]);
|
||||
//Serial.printf("timer values: %d, %d, %d\n", newlayouts[idx].timeouts[0], newlayouts[idx].timeouts[1], newlayouts[idx].timeouts[2]);
|
||||
} else if(strncmp(s, "key1action=",11)==0) { // key 1 actions
|
||||
char c1,c2,c3,c4;
|
||||
sscanf(s+11, "%c,%c,%c,%c", &c1, &c2, &c3, &c4);
|
||||
|
@ -932,7 +933,7 @@ void Display::initFromFile(int index) {
|
|||
newlayouts[idx].actions[6] = ACTION(c2);
|
||||
newlayouts[idx].actions[7] = ACTION(c3);
|
||||
newlayouts[idx].actions[8] = ACTION(c4);
|
||||
Serial.printf("parsing key2action: %c %c %c %c\n", c1, c2, c3, c4);
|
||||
//Serial.printf("parsing key2action: %c %c %c %c\n", c1, c2, c3, c4);
|
||||
} else if(strncmp(s, "timeaction=",11)==0) { // timer actions
|
||||
char c1,c2,c3;
|
||||
sscanf(s+11, "%c,%c,%c", &c1, &c2, &c3);
|
||||
|
@ -970,9 +971,11 @@ void Display::initFromFile(int index) {
|
|||
what++;
|
||||
newlayouts[idx].de[what].func = NULL;
|
||||
} else {
|
||||
#if 0
|
||||
for(int i=0; i<12; i++) {
|
||||
Serial.printf("action %d: %d\n", i, (int)newlayouts[idx].actions[i]);
|
||||
}
|
||||
#endif
|
||||
what=-1;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -15,6 +15,7 @@ struct GpsPos {
|
|||
double lon;
|
||||
int alt;
|
||||
int course;
|
||||
int accuracy;
|
||||
int valid;
|
||||
};
|
||||
extern struct GpsPos gpsPos;
|
||||
|
|
|
@ -41,6 +41,8 @@ int M10M20::setup(float frequency)
|
|||
Serial.print("M10/M20: setting RX frequency to ");
|
||||
Serial.println(frequency);
|
||||
int res = sx1278.setFrequency(frequency);
|
||||
// Test: maybe fix issue after spectrum display?
|
||||
sx1278.writeRegister(REG_PLL_HOP, 0);
|
||||
|
||||
if(sx1278.setAFCBandwidth(sonde.config.m10m20.agcbw)!=0) {
|
||||
M10M20_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.m10m20.agcbw));
|
||||
|
@ -108,7 +110,7 @@ int M10M20::setup(float frequency)
|
|||
}
|
||||
|
||||
///// Enable auto-AFC, auto-AGC, RX Trigger by preamble
|
||||
///if(sx1278.setRxConf(0x1E)!=0) {
|
||||
//if(sx1278.setRxConf(0x1E)!=0) {
|
||||
// Disable auto-AFC, auto-AGC, RX Trigger by preamble
|
||||
if(sx1278.setRxConf(0x00)!=0) {
|
||||
M10M20_DBG(Serial.println("Setting RX Config FAILED"));
|
||||
|
@ -135,7 +137,6 @@ int M10M20::setup(float frequency)
|
|||
|
||||
// enable RX
|
||||
sx1278.setPayloadLength(0); // infinite for now...
|
||||
//sx1278.setPayloadLength(292);
|
||||
sx1278.setRxConf(0x20);
|
||||
uint16_t afc = sx1278.getRawAFC();
|
||||
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
|
||||
|
@ -368,7 +369,7 @@ void M10M20::processM10data(uint8_t dt)
|
|||
int rssi=sx1278.getRSSI();
|
||||
int fei=sx1278.getFEI();
|
||||
int afc=sx1278.getAFC();
|
||||
Serial.print("Test: RSSI="); Serial.print(rssi);
|
||||
Serial.print("SYNC!!! Test: RSSI="); Serial.print(rssi);
|
||||
Serial.print(" FEI="); Serial.print(fei);
|
||||
Serial.print(" AFC="); Serial.println(afc);
|
||||
sonde.si()->rssi = rssi;
|
||||
|
|
|
@ -17,6 +17,137 @@
|
|||
static byte data[800];
|
||||
static int dpos = 0;
|
||||
|
||||
#if 0
|
||||
// subframe is never used?
|
||||
static byte subframe[51*16]; // 816 subframe bytes
|
||||
// this is moved to sondeInfo->extra
|
||||
static bool subframeReceived[51] = { false }; // do we have data for row
|
||||
static bool subframeComplete = false; // is the subframe complete
|
||||
// this is needed only locally, use a local variable on stack for that
|
||||
static bool validExternalTemperature = false; // have received all the calibration frames for the external temperature
|
||||
static bool validHumidity = false; // have received all the calibration frames for the humidity
|
||||
static bool validRAExternalTemperature = false; // have received all the calibration frames for the external temperature
|
||||
static bool validRAHumidity = false; // have received all the calibration frames for the humidity
|
||||
#endif
|
||||
|
||||
// whole 51 row frame as C structure
|
||||
// taken from https://github.com/einergehtnochrein/ra-firmware
|
||||
struct subframeBuffer {
|
||||
uint64_t valid; // bitmask for subframe valid; lsb=frame 0, etc.
|
||||
union {
|
||||
byte rawData[51*16];
|
||||
struct __attribute__((__packed__)) {
|
||||
uint16_t crc16; /* CRC16 CCITT Checksum over range 0x002...0x31F */
|
||||
uint16_t frequency; /* 0x002: TX is on 400 MHz + (frequency / 64) * 10 kHz */
|
||||
uint8_t startupTxPower; /* 0x004: TX power level at startup (1...7) */
|
||||
uint8_t reserved005;
|
||||
uint8_t reserved006;
|
||||
uint16_t reserved007; /* 0x007: ?? (some bitfield) [0],[1],[2],[3]. Init value = 0xE */
|
||||
uint16_t reserved009; /* 0x009: ? */
|
||||
uint8_t reserved00B;
|
||||
uint8_t reserved00C;
|
||||
uint8_t serial[8]; /* 0x00D: Sonde ID, 8 char, not terminated */
|
||||
uint16_t firmwareVersion; /* 0x015: 10000*major + 100*minor + patch*/
|
||||
uint16_t reserved017;
|
||||
uint16_t minHeight4Flight; /* 0x019: Height (meter above ground) where flight mode begins */
|
||||
uint8_t lowBatteryThreshold100mV; /* 0x01B: (Default=18) Shutdown if battery voltage below this
|
||||
threshold for some time (10s ?)
|
||||
*/
|
||||
uint8_t nfcDetectorThreshold; /* 0x01C: NFC detector threshold [25mV] (Default: 0x05 = 125mV) */
|
||||
uint8_t reserved01D; /* 0x01D: ?? (Init value = 0xB4) */
|
||||
uint8_t reserved01E; /* 0x01E: ?? (Init value = 0x3C) */
|
||||
uint16_t reserved01F;
|
||||
int8_t refTemperatureThreshold; /* 0x021: Reference temperature threshold [°C] */
|
||||
uint8_t reserved022;
|
||||
uint16_t reserved023;
|
||||
uint16_t reserved025;
|
||||
int16_t flightKillFrames; /* 0x027: Number of frames in flight until kill (-1 = disabled) */
|
||||
uint16_t reserved029; /* 0x029: ? (Init value = 0) */
|
||||
uint8_t burstKill; /* 0x02B: Burst kill (0=disabled, 1=enabled) */
|
||||
uint8_t reserved02C;
|
||||
uint8_t reserved02D;
|
||||
uint16_t reserved02E;
|
||||
uint16_t reserved030;
|
||||
uint8_t reserved032;
|
||||
uint16_t reserved033;
|
||||
uint16_t reserved035;
|
||||
uint16_t reserved037;
|
||||
uint16_t reserved039; /* 0x039: */
|
||||
uint8_t reserved03B; /* 0x03B: */
|
||||
uint8_t reserved03C; /* 0x03C: */
|
||||
float refResistorLow; /* 0x03D: Reference resistor low (750 Ohms) */
|
||||
float refResistorHigh; /* 0x041: Reference resistor high (1100 Ohms) */
|
||||
float refCapLow; /* 0x045: Reference capacitance low (0) */
|
||||
float refCapHigh; /* 0x049: Reference capacitance high (47 pF) */
|
||||
float taylorT[3]; /* 0x04D: Tayor coefficients for main temperature calculation */
|
||||
float calT; /* 0x059: Calibration factor for main sensor */
|
||||
float polyT[6]; /* 0x05D: */
|
||||
float calibU[2]; /* 0x075: Calibration coefficients for humidity sensor */
|
||||
|
||||
float matrixU[7][6]; /* 0x07D: Matrix for humidity sensor RH calculation */
|
||||
float taylorTU[3]; /* 0x125: Coefficients for U sensor temperature calculation */
|
||||
float calTU; /* 0x131: Calibration factor for U temperature sensor */
|
||||
float polyTrh[6]; /* 0x135: */
|
||||
|
||||
uint8_t reserved14D; /* 0x14D: */
|
||||
uint32_t reserved14E; /* 0x14E: */
|
||||
|
||||
float f152;
|
||||
uint8_t u156;
|
||||
float f157; /* 0x157: ?? (Initialized by same value as calibU) */
|
||||
uint8_t reserved15B[0x160-0x15B];
|
||||
float f160[35];
|
||||
uint8_t startIWDG; /* 0x1EC: If ==0 or ==2: Watchdog IWDG will not be started */
|
||||
uint8_t parameterSetupDone; /* 0x1ED: Set (!=0) if parameter setup was done */
|
||||
uint8_t reserved1EE;
|
||||
uint8_t reserved1EF;
|
||||
float f1F0[8];
|
||||
float pressureLaunchSite[2]; /* 0x210: Pressure [hPa] at launch site */
|
||||
struct {
|
||||
char variant[10]; /* 0x218: Sonde variant (e.g. "RS41-SG") */
|
||||
uint8_t mainboard[10]; /* 0x222: Name of mainboard (e.g. "RSM412") */
|
||||
} names;
|
||||
struct {
|
||||
uint8_t mainboard[9]; /* 0x22C: Serial number of mainboard (e.g. "L1123553") */
|
||||
uint8_t text235[12]; /* 0x235: "0000000000" */
|
||||
uint16_t reserved241; /* 0x241: */
|
||||
uint8_t pressureSensor[8]; /* 0x243: Serial number of pressure sensor (e.g. "N1310487") */
|
||||
uint16_t reserved24B; /* 0x24B: */
|
||||
} serials;
|
||||
uint16_t reserved24D; /* 0x24D: */
|
||||
uint8_t reserved24F;
|
||||
uint8_t reserved250;
|
||||
uint16_t reserved251; /* 0x251: (Init value = 0x21A = 538) */
|
||||
uint8_t xdataUartBaud; /* 0x253: 1=9k6, 2=19k2, 3=38k4, 4=57k6, 5=115k2 */
|
||||
uint8_t reserved254;
|
||||
float cpuTempSensorVoltageAt25deg; /* 0x255: CPU temperature sensor voltage at 25°C */
|
||||
uint8_t reserved259;
|
||||
uint8_t reserved25A[0x25E -0x25A];
|
||||
float matrixP[18]; /* 0x25E: Coefficients for pressure sensor polynomial */
|
||||
float f2A6[17];
|
||||
uint8_t reserved2EA[0x2FA-0x2EA];
|
||||
uint16_t halfword2FA[9];
|
||||
float reserved30C;
|
||||
float reserved310; /* 0x310: */
|
||||
uint8_t reserved314; /* 0x314: */
|
||||
uint8_t reserved315; /* 0x315: */
|
||||
int16_t burstKillFrames; /* 0x316: Number of active frames after burst kill */
|
||||
uint8_t reserved318[0x320-0x318];
|
||||
|
||||
/* This is fragment 50. It only uses 14 valid bytes! */
|
||||
int16_t killCountdown; /* 0x320: Counts frames remaining until kill (-1 = inactive) */
|
||||
uint8_t reserved322[6];
|
||||
int8_t intTemperatureCpu; /* 0x328: Temperature [°C] of CPU */
|
||||
int8_t intTemperatureRadio; /* 0x329: Temperature [°C] of radio chip */
|
||||
int8_t reserved32A; /* 0x32A: */
|
||||
uint8_t reserved32B; /* 0x32B: */
|
||||
uint8_t reserved32C; /* 0x32C: ? (the sum of two slow 8-bit counters) */
|
||||
uint8_t reserved32D; /* 0x32D: ? (the sum of two slow 8-bit counters) */
|
||||
} value;
|
||||
};
|
||||
};
|
||||
// moved global variable "calibration" to sondeInfo->extra
|
||||
|
||||
static uint16_t CRCTAB[256];
|
||||
|
||||
#define X2C_DIVR(a, b) ((b) != 0.0f ? (a)/(b) : (a))
|
||||
|
@ -135,8 +266,10 @@ 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;
|
||||
|
||||
|
@ -251,6 +384,11 @@ static int32_t getint32(const byte frame[], uint32_t frame_len,
|
|||
return (int32_t)n;
|
||||
} /* end getint32() */
|
||||
|
||||
static uint32_t getint24(const byte frame[], uint32_t frame_len, uint32_t p) { // 24bit unsigned int
|
||||
uint32_t val24 = 0;
|
||||
val24 = frame[p] | (frame[p+1]<<8) | (frame[p+2]<<16);
|
||||
return val24;
|
||||
}
|
||||
|
||||
static uint32_t getcard16(const byte frame[], uint32_t frame_len,
|
||||
uint32_t p)
|
||||
|
@ -357,7 +495,7 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
|
|||
sonde.si()->dir = dir;
|
||||
Serial.print(" ");
|
||||
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve));
|
||||
Serial.print(sonde.si()->hs);
|
||||
Serial.print(sonde.si()->hs*3.6);
|
||||
Serial.print("km/h ");
|
||||
Serial.print(dir);
|
||||
Serial.print("deg ");
|
||||
|
@ -379,7 +517,131 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
|
|||
sonde.si()->validPos = 0x7f;
|
||||
} /* end posrs41() */
|
||||
|
||||
void ProcessSubframe( byte *subframeBytes, int subframeNumber ) {
|
||||
// the total subframe consists of 51 rows, each row 16 bytes
|
||||
// based on https://github.com/bazjo/RS41_Decoding/tree/master/RS41-SGP#Subframe
|
||||
struct subframeBuffer *s = (struct subframeBuffer *)sonde.si()->extra;
|
||||
// Allocate on demand
|
||||
if(!s) {
|
||||
s = (struct subframeBuffer *)malloc( sizeof(struct subframeBuffer) );
|
||||
if(!s) { Serial.println("ProcessSubframe: out of memory"); return; }
|
||||
sonde.si()->extra = s;
|
||||
}
|
||||
memcpy( s->rawData+16*subframeNumber, subframeBytes, 16);
|
||||
s->valid |= (1ULL << subframeNumber);
|
||||
// subframeReceived[subframeNumber] = true; // mark this row of the total subframe as complete
|
||||
|
||||
#if 0
|
||||
Serial.printf("subframe number: 0x%02X\n", subframeNumber );
|
||||
Serial.print("subframe values: ");
|
||||
for ( int i = 0; i < 16; i++ ) {
|
||||
Serial.printf( "%02X ", subframeBytes[i] );
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
Serial.println("Full subframe");
|
||||
for ( int j = 0; j<51; j++ ) {
|
||||
Serial.printf("%03X ", j*16);
|
||||
for ( int i = 0; i < 16; i++ ) {
|
||||
Serial.printf( "%02X ", s.rawData[j*16+i] );
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Find the water vapor saturation pressure for a given temperature.
|
||||
* Uses the Hyland and Wexler equation with coefficients for T < 0°C.
|
||||
*/
|
||||
// taken from https://github.com/einergehtnochrein/ra-firmware
|
||||
static float _RS41_waterVaporSaturationPressure (float Tcelsius)
|
||||
{
|
||||
/* Convert to Kelvin */
|
||||
float T = Tcelsius + 273.15f;
|
||||
|
||||
/* Apply some correction magic */
|
||||
T = 0
|
||||
- 0.4931358f
|
||||
+ (1.0f + 4.61e-3f) * T
|
||||
- 1.3746454e-5f * T * T
|
||||
+ 1.2743214e-8f * T * T * T
|
||||
;
|
||||
|
||||
/* Plug into H+W equation */
|
||||
float p = expf(-5800.2206f / T
|
||||
+ 1.3914993f
|
||||
+ 6.5459673f * logf(T)
|
||||
- 4.8640239e-2f * T
|
||||
+ 4.1764768e-5f * T * T
|
||||
- 1.4452093e-8f * T * T * T
|
||||
);
|
||||
|
||||
/* Scale result to hPa */
|
||||
return p / 100.0f;
|
||||
}
|
||||
|
||||
// taken from https://github.com/einergehtnochrein/ra-firmware
|
||||
float GetRATemp( uint32_t measuredCurrent, uint32_t refMin, uint32_t refMax, float calT, float taylorT[3], float polyT[6] ) {
|
||||
struct subframeBuffer *calibration = (struct subframeBuffer *)sonde.si()->extra;
|
||||
/* Reference values for temperature are two known resistors.
|
||||
* From that we can derive the resistance of the sensor.
|
||||
*/
|
||||
float current = ( float(measuredCurrent) - float(refMin) ) / float(refMax - refMin);
|
||||
float res = calibration->value.refResistorLow
|
||||
+ (calibration->value.refResistorHigh - calibration->value.refResistorLow) * current;
|
||||
float x = res * calT;
|
||||
|
||||
float Tuncal = 0
|
||||
+ taylorT[0]
|
||||
+ taylorT[1] * x
|
||||
+ taylorT[2] * x * x;
|
||||
|
||||
/* Apply calibration polynomial */
|
||||
float temperature =
|
||||
Tuncal + polyT[0]
|
||||
+ polyT[1] * Tuncal
|
||||
+ polyT[2] * Tuncal * Tuncal
|
||||
+ polyT[3] * Tuncal * Tuncal * Tuncal
|
||||
+ polyT[4] * Tuncal * Tuncal * Tuncal * Tuncal
|
||||
+ polyT[5] * Tuncal * Tuncal * Tuncal * Tuncal * Tuncal;
|
||||
|
||||
return temperature;
|
||||
}
|
||||
|
||||
// taken from https://github.com/einergehtnochrein/ra-firmware
|
||||
float GetRAHumidity( uint32_t humCurrent, uint32_t humMin, uint32_t humMax, float sensorTemp, float externalTemp ) {
|
||||
struct subframeBuffer *calibration = (struct subframeBuffer *)sonde.si()->extra;
|
||||
float current = float( humCurrent - humMin) / float( humMax - humMin );
|
||||
/* Compute absolute capacitance from the known references */
|
||||
float C = calibration->value.refCapLow
|
||||
+ (calibration->value.refCapHigh - calibration->value.refCapLow) * current;
|
||||
/* Apply calibration */
|
||||
float Cp = ( C / calibration->value.calibU[0] - 1.0f) * calibration->value.calibU[1];
|
||||
|
||||
int j, k;
|
||||
float sum = 0;
|
||||
float xj = 1.0f;
|
||||
for (j = 0; j < 7; j++) {
|
||||
float yk = 1.0f;
|
||||
for (k = 0; k < 6; k++) {
|
||||
sum += xj * yk * calibration->value.matrixU[j][k];
|
||||
yk *= ( sensorTemp - 20.0f) / 180.0f;
|
||||
}
|
||||
xj *= Cp;
|
||||
}
|
||||
|
||||
/* Since there is always a small difference between the temperature readings for
|
||||
* the atmospheric (main) tempoerature sensor and the temperature sensor inside
|
||||
* the humidity sensor device, transform the humidity value to the atmospheric conditions
|
||||
* with its different water vapor saturation pressure.
|
||||
*/
|
||||
float RH = sum
|
||||
* _RS41_waterVaporSaturationPressure(sensorTemp)
|
||||
/ _RS41_waterVaporSaturationPressure(externalTemp);
|
||||
|
||||
return RH;
|
||||
}
|
||||
|
||||
// returns: 0: ok, -1: rs or crc error
|
||||
int RS41::decode41(byte *data, int maxlen)
|
||||
|
@ -457,6 +719,15 @@ int RS41::decode41(byte *data, int maxlen)
|
|||
sonde.si()->countKT = cntdown;
|
||||
sonde.si()->crefKT = fnr;
|
||||
}
|
||||
#if 0
|
||||
// process this subframe
|
||||
int subframeOffset = 24; // 24 = 0x18, start of subframe data
|
||||
byte receivedBytes[16];
|
||||
memcpy( receivedBytes, data+p+subframeOffset, 16);
|
||||
ProcessSubframe( receivedBytes, calnr );
|
||||
#endif
|
||||
ProcessSubframe( data+p+24, calnr );
|
||||
|
||||
}
|
||||
// TODO: some more data
|
||||
break;
|
||||
|
@ -476,6 +747,61 @@ int RS41::decode41(byte *data, int maxlen)
|
|||
case '{': // pos
|
||||
posrs41(data+p, len, 0);
|
||||
break;
|
||||
case 'z': // 0x7a is character z - 7A-MEAS temperature and humidity frame
|
||||
{
|
||||
uint32_t tempMeasMain = getint24(data, 560, p+0);
|
||||
uint32_t tempMeasRef1 = getint24(data, 560, p+3);
|
||||
uint32_t tempMeasRef2 = getint24(data, 560, p+6);
|
||||
uint32_t humidityMain = getint24(data, 560, p+9);
|
||||
uint32_t humidityRef1 = getint24(data, 560, p+12);
|
||||
uint32_t humidityRef2 = getint24(data, 560, p+15);
|
||||
uint32_t tempHumiMain = getint24(data, 560, p+18);
|
||||
uint32_t tempHumiRef1 = getint24(data, 560, p+21);
|
||||
uint32_t tempHumiRef2 = getint24(data, 560, p+24);
|
||||
uint32_t pressureMain = getint24(data, 560, p+27);
|
||||
uint32_t pressureRef1 = getint24(data, 560, p+30);
|
||||
uint32_t pressureRef2 = getint24(data, 560, p+33);
|
||||
#if 0
|
||||
Serial.printf( "External temp: tempMeasMain = %ld, tempMeasRef1 = %ld, tempMeasRef2 = %ld\n", tempMeasMain, tempMeasRef1, tempMeasRef2 );
|
||||
Serial.printf( "Rel Humidity: humidityMain = %ld, humidityRef1 = %ld, humidityRef2 = %ld\n", humidityMain, humidityRef1, humidityRef2 );
|
||||
Serial.printf( "Humid sensor: tempHumiMain = %ld, tempHumiRef1 = %ld, tempHumiRef2 = %ld\n", tempHumiMain, tempHumiRef1, tempHumiRef2 );
|
||||
Serial.printf( "Pressure sens: pressureMain = %ld, pressureRef1 = %ld, pressureRef2 = %ld\n", pressureMain, pressureRef1, pressureRef2 );
|
||||
#endif
|
||||
struct subframeBuffer *calibration = (struct subframeBuffer *)sonde.si()->extra;
|
||||
#if 0
|
||||
// for a valid temperature, require rLow rHigh (frames 3 4) TaylorT (frames 4 5), polyT (frames 5 6 7),
|
||||
validExternalTemperature = subframeReceived[3] && subframeReceived[4] && subframeReceived[5]
|
||||
&& subframeReceived[6] && subframeReceived[7];
|
||||
|
||||
// for a valid RA humidity, valid temperature, calibU (frame 7) matrixU (frame 7-12), taylorTU (frame 12 13),
|
||||
// calTU (frame 13) polyTrh (frame 13 14)
|
||||
validHumidity = validExternalTemperature && subframeReceived[0x08] && subframeReceived[0x09] && subframeReceived[0x0A]
|
||||
&& subframeReceived[0x0B] && subframeReceived[0x0C] && subframeReceived[0x0D] && subframeReceived[0x0E]
|
||||
&& subframeReceived[0x0F] && subframeReceived[0x10] && subframeReceived[0x11] && subframeReceived[0x12];
|
||||
#else
|
||||
// check for bits 3, 4, 5, 6, and 7 set
|
||||
bool validExternalTemperature = calibration!=NULL && (calibration->valid & 0xF8) == 0xF8;
|
||||
bool validHumidity = calibration!=NULL && (calibration->valid & 0x7FF8) == 0x7FF8;
|
||||
#endif
|
||||
|
||||
if ( validExternalTemperature ) {
|
||||
sonde.si()->temperature = GetRATemp( tempMeasMain, tempMeasRef1, tempMeasRef2,
|
||||
calibration->value.calT, calibration->value.taylorT, calibration->value.polyT );
|
||||
Serial.printf("External temperature = %f\n", sonde.si()->temperature );
|
||||
}
|
||||
|
||||
if ( validHumidity && validExternalTemperature ) {
|
||||
sonde.si()->tempRHSensor = GetRATemp( tempHumiMain, tempHumiRef1, tempHumiRef2,
|
||||
calibration->value.calTU, calibration->value.taylorTU, calibration->value.polyTrh );
|
||||
Serial.printf("Humidity Sensor temperature = %f\n", sonde.si()->tempRHSensor );
|
||||
sonde.si()->relativeHumidity = GetRAHumidity( humidityMain, humidityRef1, humidityRef2, sonde.si()->tempRHSensor, sonde.si()->temperature );
|
||||
Serial.printf("Relative humidity = %f\n", sonde.si()->relativeHumidity );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}}
|
||||
|
@ -524,7 +850,7 @@ 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(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; }
|
||||
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]; }
|
||||
|
|
|
@ -87,6 +87,7 @@ void Sonde::defaultConfig() {
|
|||
config.button2_axp = 0;
|
||||
config.norx_timeout = 20;
|
||||
config.screenfile = 1;
|
||||
config.tft_modeflip = 0;
|
||||
if(initlevels[16]==0) {
|
||||
config.oled_sda = 4;
|
||||
config.oled_scl = 15;
|
||||
|
@ -114,6 +115,7 @@ void Sonde::defaultConfig() {
|
|||
//config.button2_pin = 255;
|
||||
config.button2_axp = 1;
|
||||
config.gps_rxd = 34;
|
||||
config.gps_txd = 12;
|
||||
// Check for I2C-Display@21,22
|
||||
#define SSD1306_ADDRESS 0x3c
|
||||
Wire.begin(21, 22);
|
||||
|
@ -204,6 +206,7 @@ void Sonde::defaultConfig() {
|
|||
config.tcpfeed.highrate = 10;
|
||||
config.tcpfeed.idformat = ID_DFMDXL;
|
||||
config.kisstnc.active = 0;
|
||||
strcpy(config.ephftp,"igs.bkg.bund.de/IGS/BRDC/");
|
||||
|
||||
config.mqtt.active = 0;
|
||||
strcpy(config.mqtt.id, "rdz_sonde_server");
|
||||
|
@ -256,6 +259,8 @@ void Sonde::setConfig(const char *cfg) {
|
|||
config.tft_cs = atoi(val);
|
||||
} else if(strcmp(cfg,"tft_orient")==0) {
|
||||
config.tft_orient = atoi(val);
|
||||
} else if(strcmp(cfg,"tft_modeflip")==0) {
|
||||
config.tft_modeflip = atoi(val);
|
||||
} else if(strcmp(cfg,"gps_rxd")==0) {
|
||||
config.gps_rxd = atoi(val);
|
||||
} else if(strcmp(cfg,"gps_txd")==0) {
|
||||
|
@ -312,6 +317,8 @@ void Sonde::setConfig(const char *cfg) {
|
|||
config.dfm.rxbw = atoi(val);
|
||||
} else if(strcmp(cfg,"rs92.alt2d")==0) {
|
||||
config.rs92.alt2d= atoi(val);
|
||||
} else if(strcmp(cfg,"ephftp")==0) {
|
||||
strncpy(config.ephftp, val, 40);
|
||||
} else if(strcmp(cfg,"kisstnc.active")==0) {
|
||||
config.kisstnc.active = atoi(val);
|
||||
} else if(strcmp(cfg,"kisstnc.idformat")==0) {
|
||||
|
@ -431,17 +438,17 @@ void Sonde::setup() {
|
|||
Serial.print("Invalid rxtask.currentSonde: ");
|
||||
Serial.println(rxtask.currentSonde);
|
||||
rxtask.currentSonde = 0;
|
||||
for(int i=0; i<config.maxsonde - 1; i++) {
|
||||
if(!sondeList[rxtask.currentSonde].active) {
|
||||
rxtask.currentSonde++;
|
||||
if(rxtask.currentSonde>=nSonde) rxtask.currentSonde=0;
|
||||
}
|
||||
}
|
||||
sonde.currentSonde = rxtask.currentSonde;
|
||||
}
|
||||
for(int i=0; i<config.maxsonde - 1; i++) {
|
||||
if(!sondeList[rxtask.currentSonde].active) {
|
||||
rxtask.currentSonde++;
|
||||
if(rxtask.currentSonde>=nSonde) rxtask.currentSonde=0;
|
||||
}
|
||||
}
|
||||
sonde.currentSonde = rxtask.currentSonde;
|
||||
}
|
||||
|
||||
// update receiver config
|
||||
Serial.print("\nSonde::setup() on sonde index ");
|
||||
Serial.print("Sonde.setup() on sonde index ");
|
||||
Serial.println(rxtask.currentSonde);
|
||||
switch(sondeList[rxtask.currentSonde].type) {
|
||||
case STYPE_RS41:
|
||||
|
@ -461,9 +468,10 @@ void Sonde::setup() {
|
|||
break;
|
||||
}
|
||||
// debug
|
||||
float afcbw = sx1278.getAFCBandwidth();
|
||||
float rxbw = sx1278.getRxBandwidth();
|
||||
Serial.printf("AFC BW: %f RX BW: %f\n", afcbw, rxbw);
|
||||
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);
|
||||
|
||||
// reset rxtimer / norxtimer state
|
||||
sonde.sondeList[sonde.currentSonde].lastState = -1;
|
||||
|
@ -501,7 +509,7 @@ void Sonde::receive() {
|
|||
}
|
||||
} else { // RX not ok
|
||||
if(res==RX_ERROR) flashLed(100);
|
||||
Serial.printf("RX result %d, laststate was %d\n", res, si->lastState);
|
||||
Serial.printf("RX result %d (%s), laststate was %d\n", res, (res<=3)?RXstr[res]:"?", si->lastState);
|
||||
if(si->lastState != 0) {
|
||||
si->norxStart = millis();
|
||||
si->lastState = 0;
|
||||
|
@ -517,7 +525,7 @@ void Sonde::receive() {
|
|||
int event = getKeyPressEvent();
|
||||
if (!event) event = timeoutEvent(si);
|
||||
int action = (event==EVT_NONE) ? ACT_NONE : disp.layout->actions[event];
|
||||
if(action!=ACT_NONE) { Serial.printf("event %x: action is %x\n", event, action); }
|
||||
//if(action!=ACT_NONE) { Serial.printf("event %x: action is %x\n", event, action); }
|
||||
// If action is to move to a different sonde index, we do update things here, set activate
|
||||
// to force the sx1278 task to call sonde.setup(), and pass information about sonde to
|
||||
// main loop (display update...)
|
||||
|
@ -539,7 +547,7 @@ void Sonde::receive() {
|
|||
}
|
||||
}
|
||||
res = (action<<8) | (res&0xff);
|
||||
Serial.printf("receive(): Result is %04x (action %d, res %d)\n", res, action, 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;
|
||||
}
|
||||
|
@ -595,22 +603,22 @@ rxloop:
|
|||
|
||||
uint8_t Sonde::timeoutEvent(SondeInfo *si) {
|
||||
uint32_t now = millis();
|
||||
#if 1
|
||||
#if 0
|
||||
Serial.printf("Timeout check: %d - %d vs %d; %d - %d vs %d; %d - %d vs %d; lastState: %d\n",
|
||||
now, si->viewStart, disp.layout->timeouts[0],
|
||||
now, si->rxStart, disp.layout->timeouts[1],
|
||||
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("View timer expired");
|
||||
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("RX timer expired");
|
||||
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("NORX timer expired");
|
||||
Serial.println("Sonde.timeoutEvent: NORX");
|
||||
return EVT_NORXTO;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -99,6 +99,11 @@ typedef struct st_sondeinfo {
|
|||
// shut down timers, currently only for RS41; -1=disabled
|
||||
int16_t launchKT, burstKT, countKT;
|
||||
uint16_t crefKT; // frame number in which countKT was last sent
|
||||
// sonde specific extra data, NULL if unused or not yet initialized, currently used for RS41 subframe data (calibration)
|
||||
void *extra;
|
||||
float temperature = -300.0; // platinum resistor temperature
|
||||
float tempRHSensor = -300.0; // temperature of relative humidity sensor
|
||||
float relativeHumidity = -1.0; // relative humidity
|
||||
} SondeInfo;
|
||||
// rxStat: 3=undef[empty] 1=timeout[.] 2=errro[E] 0=ok[|] 4=no valid position[°]
|
||||
|
||||
|
@ -186,6 +191,7 @@ typedef struct st_rdzconfig {
|
|||
int tft_rs; // TFT RS pin
|
||||
int tft_cs; // TFT CS pin
|
||||
int tft_orient; // TFT orientation (default: 1)
|
||||
int tft_modeflip; // Hack for Joerg's strange display
|
||||
int gps_rxd; // GPS module RXD pin. We expect 9600 baud NMEA data.
|
||||
int gps_txd; // GPS module TXD pin
|
||||
// software configuration
|
||||
|
@ -209,6 +215,7 @@ typedef struct st_rdzconfig {
|
|||
struct st_rs92config rs92;
|
||||
struct st_dfmconfig dfm;
|
||||
struct st_m10m20config m10m20;
|
||||
char ephftp[40];
|
||||
// data feed configuration
|
||||
// for now, one feed for each type is enough, but might get extended to more?
|
||||
char call[10]; // APRS callsign
|
||||
|
|
|
@ -198,6 +198,7 @@ TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_
|
|||
hwSPI = false;
|
||||
writeFunctionLevel = 0;
|
||||
gfxFont = NULL;
|
||||
_modeFlip = 0;
|
||||
}
|
||||
|
||||
// Constructor when using software SPI. All output pins are configurable. Adds backlight brightness 0-255
|
||||
|
@ -212,6 +213,7 @@ TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t sdi, int8_
|
|||
hwSPI = false;
|
||||
writeFunctionLevel = 0;
|
||||
gfxFont = NULL;
|
||||
_modeFlip = 0;
|
||||
}
|
||||
|
||||
// Constructor when using hardware SPI. Faster, but must use SPI pins
|
||||
|
@ -226,6 +228,7 @@ TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led) {
|
|||
hwSPI = true;
|
||||
writeFunctionLevel = 0;
|
||||
gfxFont = NULL;
|
||||
_modeFlip = 0;
|
||||
}
|
||||
|
||||
// Constructor when using hardware SPI. Faster, but must use SPI pins
|
||||
|
@ -241,6 +244,7 @@ TFT22_ILI9225::TFT22_ILI9225(int8_t rst, int8_t rs, int8_t cs, int8_t led, uint8
|
|||
hwSPI = true;
|
||||
writeFunctionLevel = 0;
|
||||
gfxFont = NULL;
|
||||
_modeFlip = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,10 +346,12 @@ void TFT22_ILI9225::begin()
|
|||
endWrite();
|
||||
delay(50);
|
||||
|
||||
#define OCTLFLIP(m) ((m&0xff)<<8)
|
||||
#define EMODEFLIP(m) ((m>>8)<<3)
|
||||
startWrite();
|
||||
_writeRegister(ILI9225_DRIVER_OUTPUT_CTRL, 0x011C); // set the display line number and display direction
|
||||
_writeRegister(ILI9225_DRIVER_OUTPUT_CTRL, OCTLFLIP(_modeFlip) ^ 0x011C); // set the display line number and display direction
|
||||
_writeRegister(ILI9225_LCD_AC_DRIVING_CTRL, 0x0100); // set 1 line inversion
|
||||
_writeRegister(ILI9225_ENTRY_MODE, 0x1038); // set GRAM write direction and BGR=1.
|
||||
_writeRegister(ILI9225_ENTRY_MODE, EMODEFLIP(_modeFlip) ^ 0x1038); // set GRAM write direction and BGR=1.
|
||||
_writeRegister(ILI9225_DISP_CTRL1, 0x0000); // Display off
|
||||
_writeRegister(ILI9225_BLANK_PERIOD_CTRL1, 0x0808); // set the back porch and front porch
|
||||
_writeRegister(ILI9225_FRAME_CYCLE_CTRL, 0x1100); // set the clocks number per line
|
||||
|
@ -482,7 +488,7 @@ void TFT22_ILI9225::_setWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y
|
|||
startWrite();
|
||||
// autoincrement mode
|
||||
if ( _orientation > 0 ) mode = modeTab[_orientation-1][mode];
|
||||
_writeRegister(ILI9225_ENTRY_MODE, 0x1000 | ( mode<<3) );
|
||||
_writeRegister(ILI9225_ENTRY_MODE, EMODEFLIP(_modeFlip) ^ (0x1000 | ( mode<<3)) );
|
||||
_writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR1,x1);
|
||||
_writeRegister(ILI9225_HORIZONTAL_WINDOW_ADDR2,x0);
|
||||
|
||||
|
@ -582,6 +588,9 @@ void TFT22_ILI9225::setDisplay(boolean flag) {
|
|||
}
|
||||
}
|
||||
|
||||
void TFT22_ILI9225::setModeFlip(uint16_t m) {
|
||||
_modeFlip = m;
|
||||
}
|
||||
|
||||
void TFT22_ILI9225::setOrientation(uint8_t orientation) {
|
||||
|
||||
|
|
|
@ -393,6 +393,8 @@ class TFT22_ILI9225 {
|
|||
|
||||
void getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa);
|
||||
|
||||
void setModeFlip(uint16_t m);
|
||||
|
||||
private:
|
||||
|
||||
void _spiWrite(uint8_t v);
|
||||
|
@ -437,6 +439,7 @@ class TFT22_ILI9225 {
|
|||
#endif
|
||||
|
||||
uint8_t _orientation, _brightness;
|
||||
uint16_t _modeFlip;
|
||||
|
||||
// correspondig modes if orientation changed:
|
||||
const autoIncMode_t modeTab [3][8] = {
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
#include <inttypes.h>
|
||||
#include <WiFi.h>
|
||||
#include "Display.h"
|
||||
|
||||
#include "Sonde.h"
|
||||
|
||||
extern WiFiClient client;
|
||||
|
||||
static const char *ftpserver = "www.ngs.noaa.gov";
|
||||
//static const char *ftpserver = "www.ngs.noaa.gov";
|
||||
char outbuf[128];
|
||||
|
||||
uint8_t getreply() {
|
||||
|
@ -72,31 +72,32 @@ void geteph() {
|
|||
Serial.printf("now: %s, existing: %s => updating\n", nowstr, tsstr);
|
||||
}
|
||||
status.close();
|
||||
disp.rdis->clear();
|
||||
disp.rdis->setFont(FONT_SMALL);
|
||||
disp.rdis->drawString(0, 0, "FTP ngs.noaa.gov");
|
||||
// fetch rinex from server
|
||||
File fh = SPIFFS.open("/brdc.gz","w");
|
||||
if(!fh) {
|
||||
Serial.println("cannot open file\n");
|
||||
return;
|
||||
}
|
||||
char buf[252];
|
||||
snprintf(buf, 128, "/cors/rinex/%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000);
|
||||
char host[252];
|
||||
strcpy(host, sonde.config.ephftp);
|
||||
char *buf = strchr(host, '/');
|
||||
if(!buf) { Serial.println("Invalid FTP host config"); return; }
|
||||
*buf = 0;
|
||||
buf++;
|
||||
uint8_t dispw, disph, dispxs, dispys;
|
||||
disp.rdis->getDispSize(&disph, &dispw, &dispxs, &dispys);
|
||||
disp.rdis->clear();
|
||||
disp.rdis->setFont(FONT_SMALL);
|
||||
disp.rdis->drawString(0, 0, host);
|
||||
// fetch rinex from server
|
||||
char *ptr = buf + strlen(buf);
|
||||
snprintf(ptr, 128, "%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000);
|
||||
Serial.println("running geteph\n");
|
||||
disp.rdis->drawString(0, 1, buf+21);
|
||||
disp.rdis->drawString(0, 1*dispys, ptr+9);
|
||||
|
||||
if(!client.connect(ftpserver, 21)) {
|
||||
Serial.println("FTP connection to www.ngs.noaa.gov failed");
|
||||
if(!client.connect(host, 21)) {
|
||||
Serial.printf("FTP connection to %s failed\n", host);
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
while(!client.available()) delay(1);
|
||||
while(client.available()) {
|
||||
String s = client.readStringUntil('\n');
|
||||
Serial.println(s);
|
||||
}
|
||||
#endif
|
||||
if(getreply()>='4') { Serial.println("connected failed"); return; }
|
||||
client.print("USER anonymous\r\n");
|
||||
if(getreply()>='4') { Serial.println("USER failed"); return; }
|
||||
|
@ -121,8 +122,8 @@ void geteph() {
|
|||
}
|
||||
uint16_t port = (array_pasv[4]<<8) | (array_pasv[5]&0xff);
|
||||
WiFiClient dclient;
|
||||
Serial.printf("connecting to %s:%d\n", ftpserver,port);
|
||||
dclient.connect(ftpserver, port);
|
||||
Serial.printf("connecting to %s:%d\n", host, port);
|
||||
dclient.connect(host, port);
|
||||
if(!dclient) {
|
||||
Serial.println("data connection failed");
|
||||
return;
|
||||
|
@ -149,9 +150,9 @@ void geteph() {
|
|||
fh.close();
|
||||
snprintf(buf, 16, "Fetched %d B ",len);
|
||||
buf[16]=0;
|
||||
disp.rdis->drawString(0,2,buf);
|
||||
disp.rdis->drawString(0,2*dispys,buf);
|
||||
|
||||
disp.rdis->drawString(0,4,"Decompressing...");
|
||||
disp.rdis->drawString(0,4*dispys,"Decompressing...");
|
||||
// decompression
|
||||
tinfl_decompressor *decomp = (tinfl_decompressor *)malloc(sizeof(tinfl_decompressor));
|
||||
tinfl_init(decomp);
|
||||
|
@ -219,7 +220,7 @@ void geteph() {
|
|||
status.close();
|
||||
snprintf(buf, 16, "Done: %d B ",total);
|
||||
buf[16]=0;
|
||||
disp.rdis->drawString(0,5,buf);
|
||||
disp.rdis->drawString(0,5*dispys,buf);
|
||||
delay(1000);
|
||||
|
||||
free(obuf);
|
||||
|
|
Ładowanie…
Reference in New Issue