Bringing WiFi to the Cidco Mailstation
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

esp8266: Add syslog support from WiFiPPP, add custom OTA updates

+208 -53
+3
.gitmodules
··· 1 + [submodule "esp8266/lib/Syslog"] 2 + path = esp8266/lib/Syslog 3 + url = https://github.com/arcao/Syslog.git
+26
esp8266/GNUmakefile
··· 9 9 BUILD_ROOT = $(CURDIR)/obj 10 10 EXCLUDE_DIRS = $(BUILD_ROOT) 11 11 12 + LIBS= Syslog 13 + 12 14 UNAME_S := $(shell uname -s) 13 15 ifeq ($(UNAME_S),OpenBSD) 14 16 ESP_ROOT = /usr/local/share/arduino/hardware/espressif/esp8266 ··· 20 22 else 21 23 include ../../makeEspArduino/makeEspArduino.mk 22 24 endif 25 + 26 + ifeq ($(UNAME_S),OpenBSD) 27 + SERVER_IP?= `route -qn get 0.0.0.0 | grep 'address:' | sed 's/.*: //'` 28 + endif 29 + ifeq ($(UNAME_S),Darwin) 30 + _IFACE= $(shell route -qn get 0.0.0.0 | grep interface: | sed 's/.*: //') 31 + SERVER_IP?= $(shell ipconfig getifaddr ${_IFACE}) 32 + endif 33 + SERVER_PORT?= 8000 34 + 35 + serve_ota: all 36 + @[ -d ../release ] || mkdir ../release 37 + @grep _VERSION wifistation.h | sed -e 's/"$$//' -e 's/.*"//' > ../release/ota.txt 38 + @cp -f $(MAIN_EXE) ../release/update.bin 39 + @stat -f "%z" ../release/update.bin >> ../release/ota.txt 40 + @md5 -q ../release/update.bin >> ../release/ota.txt 41 + @echo http://$(SERVER_IP):$(SERVER_PORT)/update.bin >> ../release/ota.txt 42 + @echo "" 43 + @echo "Issue update command:" 44 + @echo "" 45 + @/bin/echo -n "AT$$" 46 + @echo "UPDATE! http://${SERVER_IP}:${SERVER_PORT}/ota.txt" 47 + @echo "" 48 + cd ../release && python3 -m http.server --bind $(SERVER_IP) $(SERVER_PORT)
+116 -46
esp8266/update.cpp
··· 15 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 16 */ 17 17 18 + #include <WiFiClient.h> 18 19 #include <WiFiClientSecure.h> 19 20 #include "wifistation.h" 20 21 21 - #define OTA_VERSION_URL "https://raw.githubusercontent.com/jcs/WiFiStation/main/release/version.txt" 22 + static const char OTA_VERSION_URL[] PROGMEM = 23 + "https://raw.githubusercontent.com/jcs/WiFiStation/main/release/version.txt"; 22 24 23 - WiFiClientSecure client; 25 + static WiFiClient client; 26 + static WiFiClientSecure client_tls; 27 + static bool tls = false; 24 28 25 29 bool 26 30 update_https_get_body(const char *url, long expected_length) 27 31 { 28 - char *host, *path; 29 - int status, httpver, chars, lines, tlength, clength = -1; 32 + char *host, *path, *colon; 33 + int status = 0, port, httpver, chars, lines, tlength, clength = -1; 30 34 31 35 if (WiFi.status() != WL_CONNECTED) { 32 36 output("ERROR WiFi is not connected\r\n"); ··· 46 50 return false; 47 51 } 48 52 49 - if (sscanf(url, "https://%[^/]%s%n", host, path, &chars) != 2 || 50 - chars == 0) { 51 - outputf("ERROR failed parsing URL %s\r\n", url); 53 + if (sscanf(url, "http://%[^/]%s%n", host, path, &chars) == 2 && 54 + chars != 0) { 55 + tls = false; 56 + } else if (sscanf(url, "https://%[^/]%s%n", host, path, &chars) == 2 57 + && chars != 0) { 58 + tls = true; 59 + /* 60 + * This would be nice to not have to do, but keeping up with 61 + * GitHub's TLS cert fingerprints will be tedious, and we have 62 + * no cert chain. 63 + */ 64 + client_tls.setInsecure(); 65 + } else { 66 + outputf("ERROR failed parsing URL \"%s\"\r\n", url); 52 67 free(path); 53 68 free(host); 54 69 return false; 55 70 } 56 71 57 - /* 58 - * This would be nice to not have to do, but keeping up with GitHub's 59 - * TLS cert fingerprints will be tedious, and we have no cert chain. 60 - */ 61 - client.setInsecure(); 72 + if ((colon = strchr(host, ':'))) { 73 + colon[0] = '\0'; 74 + port = atoi(colon + 1); 75 + } else { 76 + port = (tls ? 443 : 80); 77 + } 78 + 79 + #ifdef UPDATE_TRACE 80 + syslog.logf(LOG_DEBUG, "%s: host \"%s\" path \"%s\" port %d tls %d", 81 + __func__, host, path, port, tls ? 1 : 0); 82 + #endif 62 83 63 - if (!client.connect(host, 443)) { 64 - outputf("ERROR OTA failed connecting to %s:443\r\n", host); 84 + if (!(tls ? client_tls : client).connect(host, port)) { 85 + outputf("ERROR OTA failed connecting to http%s://%s:%d\r\n", 86 + tls ? "s" : "", host, port); 65 87 free(path); 66 88 free(host); 67 89 return false; 68 - } 90 + } 69 91 70 - client.printf("GET %s HTTP/1.0\r\n", path); 71 - client.printf("Host: %s\r\n", host); 72 - client.printf("User-Agent: wifistation %s\r\n", WIFISTATION_VERSION); 73 - client.printf("Connection: close\r\n"); 74 - client.printf("\r\n"); 92 + (tls ? client_tls : client).printf("GET %s HTTP/1.0\r\n", path); 93 + (tls ? client_tls : client).printf("Host: %s\r\n", host); 94 + (tls ? client_tls : client).printf("User-Agent: WiFiStation %s\r\n", 95 + WIFISTATION_VERSION); 96 + (tls ? client_tls : client).printf("Connection: close\r\n"); 97 + (tls ? client_tls : client).printf("\r\n"); 75 98 76 99 free(path); 77 100 free(host); 78 101 79 102 /* read headers */ 80 103 lines = 0; 81 - while (client.connected() || client.available()) { 82 - String line = client.readStringUntil('\n'); 104 + while ((tls ? client_tls : client).connected() || 105 + (tls ? client_tls : client).available()) { 106 + String line = (tls ? client_tls : client).readStringUntil('\n'); 107 + 108 + #ifdef UPDATE_TRACE 109 + syslog.logf(LOG_DEBUG, "%s: read header \"%s\"", __func__, 110 + line.c_str()); 111 + #endif 83 112 84 113 if (lines == 0) 85 114 sscanf(line.c_str(), "HTTP/1.%d %d%n", &httpver, 86 115 &status, &chars); 87 116 else if (sscanf(line.c_str(), "Content-Length: %d%n", 88 - &tlength, &chars) == 1 && chars > 0) { 117 + &tlength, &chars) == 1 && chars > 0) 89 118 clength = tlength; 90 - } else if (line == "\r") 119 + else if (line == "\r") { 91 120 break; 121 + } 92 122 93 123 lines++; 94 124 } 95 125 126 + #ifdef UPDATE_TRACE 127 + syslog.logf(LOG_DEBUG, "%s: read status %d, content-length %d vs " 128 + "expected %ld", __func__, status, clength, expected_length); 129 + #endif 130 + 96 131 if (status != 200) { 97 132 outputf("ERROR OTA fetch of %s failed with HTTP status %d\r\n", 98 133 url, status); ··· 108 143 return true; 109 144 110 145 drain: 111 - while (client.available()) 112 - client.read(); 113 - client.stop(); 146 + #ifdef UPDATE_TRACE 147 + syslog.logf(LOG_DEBUG, "%s: draining remaining body", __func__); 148 + #endif 149 + while ((tls ? client_tls : client).available()) 150 + (tls ? client_tls : client).read(); 151 + (tls ? client_tls : client).stop(); 114 152 return false; 115 153 } 116 154 117 155 void 118 - update_process(bool do_update, bool force) 156 + update_process(char *url, bool do_update, bool force) 119 157 { 120 - String url, md5, version; 121 - int bytesize = 0, lines = 0; 158 + String rom_url, md5, version; 159 + int bytesize = 0, lines = 0, len; 160 + char *furl = NULL; 161 + 162 + if (url == NULL) { 163 + furl = url = (char *)malloc(len = (strlen_P(OTA_VERSION_URL) + 164 + 1)); 165 + if (url == NULL) { 166 + output("ERROR malloc failed\r\n"); 167 + return; 168 + } 169 + memcpy_P(url, OTA_VERSION_URL, len); 170 + } 122 171 123 - if (!update_https_get_body(OTA_VERSION_URL, 0)) 172 + #ifdef UPDATE_TRACE 173 + syslog.logf(LOG_DEBUG, "processing update from \"%s\"", url); 174 + #endif 175 + 176 + if (!update_https_get_body(url, 0)) { 177 + if (furl) 178 + free(furl); 124 179 return; 180 + } 181 + if (furl) 182 + free(furl); 125 183 126 184 lines = 0; 127 - while (client.available()) { 128 - String line = client.readStringUntil('\n'); 185 + while ((tls ? client_tls : client).available()) { 186 + String line = (tls ? client_tls : client).readStringUntil('\n'); 187 + 188 + #ifdef UPDATE_TRACE 189 + syslog.logf(LOG_DEBUG, "%s: read body[%d] \"%s\"", __func__, 190 + lines, line.c_str()); 191 + #endif 129 192 130 193 switch (lines) { 131 194 case 0: ··· 138 201 md5 = line; 139 202 break; 140 203 case 3: 141 - url = line; 204 + rom_url = line; 142 205 break; 143 206 default: 144 207 #if DEBUG ··· 151 214 lines++; 152 215 } 153 216 154 - client.stop(); 217 + (tls ? client_tls : client).stop(); 155 218 156 219 if (version == WIFISTATION_VERSION && !force) { 157 220 outputf("ERROR OTA server reports version %s, no update " ··· 163 226 return; 164 227 } 165 228 166 - /* doing an update, parse the url */ 167 - 168 - if (!update_https_get_body((char *)url.c_str(), bytesize)) 229 + /* doing an update, parse the url read */ 230 + #ifdef UPDATE_TRACE 231 + syslog.logf(LOG_DEBUG, "%s: doing update with ROM url \"%s\" size %d", 232 + __func__, rom_url.c_str(), bytesize); 233 + #endif 234 + if (!update_https_get_body((char *)rom_url.c_str(), bytesize)) 169 235 return; 170 236 171 237 outputf("Updating to version %s (%d bytes) from %s\r\n", 172 - version.c_str(), bytesize, (char *)url.c_str()); 238 + version.c_str(), bytesize, (char *)rom_url.c_str()); 173 239 174 - Update.begin(bytesize, U_FLASH, pRedLED); 240 + Update.begin(bytesize, U_FLASH, -1); 175 241 176 242 Update.setMD5(md5.c_str()); 177 243 178 244 Update.onProgress([](unsigned int progress, unsigned int total) { 179 - outputf("\rFlash update progress: % 6u of % 6u", progress, 245 + /* 246 + * Just force serial output here rather than using outputf, 247 + * don't let it block us. 248 + */ 249 + Serial.printf("\rFlash update progress: % 6d of % 6d", progress, 180 250 total); 181 251 }); 182 252 183 - if ((int)Update.writeStream(client) != bytesize) { 253 + if ((int)Update.writeStream((tls ? client_tls : client)) != bytesize) { 184 254 if (Update.getError() == UPDATE_ERROR_BOOTSTRAP) 185 255 outputf("ERROR update must be done from fresh " 186 256 "reset, not from uploaded code\r\n"); ··· 188 258 outputf("ERROR failed writing download bytes: %d\r\n", 189 259 Update.getError()); 190 260 191 - while (client.available()) 192 - client.read(); 261 + while ((tls ? client_tls : client).available()) 262 + (tls ? client_tls : client).read(); 193 263 194 - client.stop(); 264 + (tls ? client_tls : client).stop(); 195 265 return; 196 266 } 197 267 ··· 201 271 return; 202 272 } 203 273 204 - client.stop(); 274 + (tls ? client_tls : client).stop(); 205 275 outputf("\r\nOK update completed, restarting\r\n"); 206 276 207 277 delay(500);
+20
esp8266/util.cpp
··· 19 19 20 20 struct eeprom_data *settings; 21 21 22 + WiFiUDP syslogUDPClient; 23 + Syslog syslog(syslogUDPClient, SYSLOG_PROTO_BSD); 24 + 22 25 bool serial_alive = true; 23 26 bool mailstation_alive = false; 24 27 ··· 74 77 EEPROM.commit(); 75 78 } 76 79 80 + syslog_setup(); 81 + 77 82 Serial.begin(settings->baud); 78 83 delay(1000); 79 84 ··· 93 98 } 94 99 95 100 void 101 + syslog_setup(void) 102 + { 103 + if (settings->syslog_server[0]) 104 + syslog.server(settings->syslog_server, 514); 105 + else 106 + syslog.server(NULL, 514); 107 + 108 + syslog.appName("WiFiStation"); 109 + } 110 + 111 + void 96 112 led_setup(void) 97 113 { 98 114 /* setup LEDs */ ··· 169 185 output(const char *str) 170 186 { 171 187 size_t len = strlen(str); 188 + 189 + #ifdef OUTPUT_TRACE 190 + syslog.logf(LOG_DEBUG, "output: \"%s\"", str); 191 + #endif 172 192 173 193 for (size_t i = 0; i < len; i++) 174 194 output(str[i]);
+6 -1
esp8266/wifistation.h
··· 21 21 #include <Arduino.h> 22 22 #include <EEPROM.h> 23 23 #include <ESP8266WiFi.h> 24 + #include <Syslog.h> 25 + #include <WiFiUdp.h> 24 26 25 27 #define WIFISTATION_VERSION "0.7" 26 28 ··· 41 43 #define BOOKMARK_SIZE 64 42 44 #define NUM_BOOKMARKS 3 43 45 char bookmarks[NUM_BOOKMARKS][BOOKMARK_SIZE]; 46 + char syslog_server[64]; 44 47 }; 45 48 46 49 extern struct eeprom_data *settings; 50 + extern Syslog syslog; 47 51 48 52 #define MAX_UPLOAD_SIZE (16 * 1024) 49 53 ··· 57 61 extern bool mailstation_alive; 58 62 59 63 /* util.cpp */ 64 + void syslog_setup(void); 60 65 void led_setup(void); 61 66 void led_reset(void); 62 67 void error_flash(void); ··· 100 105 void http_process(void); 101 106 102 107 /* update.cpp */ 103 - void update_process(bool do_update, bool force); 108 + void update_process(char *, bool, bool); 104 109 105 110 #endif
+37 -6
esp8266/wifistation.ino
··· 182 182 return; 183 183 } 184 184 185 + #ifdef AT_TRACE 186 + syslog.logf(LOG_DEBUG, "%s: parsing \"%s\"", __func__, cmd); 187 + #endif 188 + 185 189 for (size_t i = 0; i < len; i++) 186 190 lcmd[i] = tolower(cmd[i]); 187 191 lcmd[len] = '\0'; ··· 324 328 outputf("ATDS bookmark %d: %s\r\n", 325 329 i + 1, settings->bookmarks[i]); 326 330 } 331 + outputf("Syslog server: %s\r\n", 332 + settings->syslog_server); 333 + 327 334 output("OK\r\n"); 328 335 break; 329 336 case '1': { ··· 557 564 } else if (strcmp(lcmd, "at$ssid?") == 0) { 558 565 /* AT$SSID?: print wifi ssid */ 559 566 outputf("%s\r\nOK\r\n", settings->wifi_ssid); 567 + } else if (strncmp(lcmd, "at$syslog=", 10) == 0) { 568 + /* AT$SYSLOG=...: set syslog server */ 569 + memset(settings->syslog_server, 0, 570 + sizeof(settings->syslog_server)); 571 + strncpy(settings->syslog_server, cmd + 10, 572 + sizeof(settings->syslog_server)); 573 + syslog_setup(); 574 + syslog.logf(LOG_INFO, "syslog server changed to %s", 575 + settings->syslog_server); 576 + output("OK\r\n"); 577 + } else if (strcmp(lcmd, "at$syslog?") == 0) { 578 + /* AT$SYSLOG?: print syslog server */ 579 + outputf("%s\r\n", settings->syslog_server); 580 + output("OK\r\n"); 560 581 } else if (strncmp(lcmd, "at$tts=", 7) == 0) { 561 582 /* AT$TTS=: set telnet NAWS */ 562 583 int w, h, chars; ··· 592 613 } else if (strcmp(lcmd, "at$tty?") == 0) { 593 614 /* AT$TTY?: show telnet TTYPE setting */ 594 615 outputf("%s\r\nOK\r\n", settings->telnet_tterm); 595 - } else if (strcmp(lcmd, "at$update?") == 0) { 616 + } else if (strncmp(lcmd, "at$update?", 10) == 0) { 596 617 /* AT$UPDATE?: show whether an OTA update is available */ 597 - update_process(false, false); 598 - } else if (strcmp(lcmd, "at$update!") == 0) { 618 + char *url = NULL; 619 + if (strncmp(lcmd, "at$update? http", 15) == 0) 620 + url = lcmd + 11; 621 + update_process(url, false, false); 622 + } else if (strncmp(lcmd, "at$update!", 10) == 0) { 599 623 /* AT$UPDATE!: force an OTA update */ 600 - update_process(true, true); 601 - } else if (strcmp(lcmd, "at$update") == 0) { 624 + char *url = NULL; 625 + if (strncmp(lcmd, "at$update! http", 15) == 0) 626 + url = lcmd + 11; 627 + update_process(url, true, true); 628 + } else if (strcmp(lcmd, "at$update") == 0 || 629 + strncmp(lcmd, "at$update http", 14) == 0) { 602 630 /* AT$UPDATE: do an OTA update */ 603 - update_process(true, false); 631 + char *url = NULL; 632 + if (strncmp(lcmd, "at$update http", 14) == 0) 633 + url = lcmd + 10; 634 + update_process(url, true, false); 604 635 } else if (strncmp(lcmd, "at$upload", 9) == 0) { 605 636 /* AT$UPLOAD: mailstation program loader */ 606 637 unsigned int bytes = 0;