ESP8266-based WiFi serial modem emulator ROM
0
fork

Configure Feed

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

AT$UPDATE: Allow passing a URL to update from, support non-https

Also add a 'serve_ota' make target that creates an OTA text file and
serves it. This should speed up development and allow others to
update their devices from repos other than mine.

+127 -59
+19 -1
GNUmakefile
··· 16 16 ARDUINO_ROOT?= /usr/local/share/arduino 17 17 ARDUINO_LIBS?= ${ESP_ROOT}/libraries 18 18 19 - UPLOAD_PORT?= /dev/cuaU1 19 + UPLOAD_PORT?= /dev/cuaU0 20 + 21 + SERVER_IP?= `route -qn get 0.0.0.0 | grep 'address:' | sed 's/.*: //'` 22 + SERVER_PORT?= 8000 20 23 21 24 include /usr/local/share/makeEspArduino/makeEspArduino.mk 25 + 26 + serve_ota: all 27 + @[ -d release ] || mkdir release 28 + @grep _VERSION wifippp.h | sed -e 's/"$$//' -e 's/.*"//' > release/ota.txt 29 + @cp -f $(MAIN_EXE) release/update.bin 30 + @stat -f "%z" release/update.bin >> release/ota.txt 31 + @md5 -q release/update.bin >> release/ota.txt 32 + @echo http://$(SERVER_IP):$(SERVER_PORT)/update.bin >> release/ota.txt 33 + @echo "" 34 + @echo "Issue update command:" 35 + @echo "" 36 + @echo -n "AT$$" 37 + @echo "UPDATE! http://${SERVER_IP}:${SERVER_PORT}/ota.txt" 38 + @echo "" 39 + cd release && python3 -m http.server $(SERVER_PORT)
+85 -46
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 "wifippp.h" 20 21 21 - #define OTA_VERSION_URL "https://raw.githubusercontent.com/jcs/WiFiPPP/main/release/version.txt" 22 + static const char OTA_VERSION_URL[] PROGMEM = 23 + "https://raw.githubusercontent.com/jcs/WiFiPPP/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) { 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 { 51 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 + } 62 78 63 - if (!client.connect(host, 443)) { 64 - outputf("ERROR OTA failed connecting to %s:443\r\n", host); 79 + if (!(tls ? client_tls : client).connect(host, port)) { 80 + outputf("ERROR OTA failed connecting to http%s://%s:%d\r\n", 81 + tls ? "s" : "", host, port); 65 82 free(path); 66 83 free(host); 67 84 return false; 68 - } 85 + } 69 86 70 - client.printf("GET %s HTTP/1.0\r\n", path); 71 - client.printf("Host: %s\r\n", host); 72 - client.printf("User-Agent: WiFiPPP %s\r\n", WIFIPPP_VERSION); 73 - client.printf("Connection: close\r\n"); 74 - client.printf("\r\n"); 87 + (tls ? client_tls : client).printf("GET %s HTTP/1.0\r\n", path); 88 + (tls ? client_tls : client).printf("Host: %s\r\n", host); 89 + (tls ? client_tls : client).printf("User-Agent: WiFiPPP %s\r\n", WIFIPPP_VERSION); 90 + (tls ? client_tls : client).printf("Connection: close\r\n"); 91 + (tls ? client_tls : client).printf("\r\n"); 75 92 76 93 free(path); 77 94 free(host); 78 95 79 96 /* read headers */ 80 97 lines = 0; 81 - while (client.connected()) { 82 - String line = client.readStringUntil('\n'); 98 + while ((tls ? client_tls : client).connected() || 99 + (tls ? client_tls : client).available()) { 100 + String line = (tls ? client_tls : client).readStringUntil('\n'); 83 101 84 102 if (lines == 0) 85 103 sscanf(line.c_str(), "HTTP/1.%d %d%n", &httpver, 86 104 &status, &chars); 87 105 else if (sscanf(line.c_str(), "Content-Length: %d%n", 88 - &tlength, &chars) == 1 && chars > 0) { 106 + &tlength, &chars) == 1 && chars > 0) 89 107 clength = tlength; 108 + else if (line == "\r") { 109 + break; 90 110 } 91 111 92 - if (line == "\r") 93 - break; 94 - 95 112 lines++; 96 113 } 97 114 98 115 if (status != 200) { 99 - outputf("ERROR OTA fetch of %s failed with HTTP status %s\r\n", 116 + outputf("ERROR OTA fetch of %s failed with HTTP status %d\r\n", 100 117 url, status); 101 118 goto drain; 102 119 } ··· 110 127 return true; 111 128 112 129 drain: 113 - while (client.available()) 114 - client.read(); 115 - client.stop(); 130 + while ((tls ? client_tls : client).available()) 131 + (tls ? client_tls : client).read(); 132 + (tls ? client_tls : client).stop(); 116 133 return false; 117 134 } 118 135 119 136 void 120 - update_process(bool do_update, bool force) 137 + update_process(char *url, bool do_update, bool force) 121 138 { 122 - String url, md5, version; 123 - int bytesize = 0, lines = 0; 139 + String rom_url, md5, version; 140 + int bytesize = 0, lines = 0, len; 141 + char *furl = NULL; 142 + 143 + outputf("\n"); 144 + 145 + if (url == NULL) { 146 + furl = url = (char *)malloc(len = (strlen_P(OTA_VERSION_URL) + 147 + 1)); 148 + if (url == NULL) { 149 + output("ERROR malloc failed\r\n"); 150 + return; 151 + } 152 + memcpy_P(url, OTA_VERSION_URL, len); 153 + } 124 154 125 - if (!update_https_get_body(OTA_VERSION_URL, 0)) 155 + if (!update_https_get_body(url, 0)) { 156 + if (furl) 157 + free(furl); 126 158 return; 159 + } 160 + if (furl) 161 + free(furl); 127 162 128 163 lines = 0; 129 - while (client.available()) { 130 - String line = client.readStringUntil('\n'); 164 + while ((tls ? client_tls : client).available()) { 165 + String line = (tls ? client_tls : client).readStringUntil('\n'); 131 166 132 167 switch (lines) { 133 168 case 0: ··· 140 175 md5 = line; 141 176 break; 142 177 case 3: 143 - url = line; 178 + rom_url = line; 144 179 break; 145 180 default: 146 181 #if DEBUG ··· 153 188 lines++; 154 189 } 155 190 156 - client.stop(); 191 + (tls ? client_tls : client).stop(); 157 192 158 193 if (version == WIFIPPP_VERSION && !force) { 159 194 outputf("ERROR OTA server reports version %s, no update " ··· 167 202 168 203 /* doing an update, parse the url */ 169 204 170 - if (!update_https_get_body((char *)url.c_str(), bytesize)) 205 + if (!update_https_get_body((char *)rom_url.c_str(), bytesize)) 171 206 return; 172 207 173 208 outputf("Updating to version %s (%d bytes) from %s\r\n", 174 - version.c_str(), bytesize, (char *)url.c_str()); 209 + version.c_str(), bytesize, (char *)rom_url.c_str()); 175 210 176 - Update.begin(bytesize, U_FLASH, pRedLED); 211 + Update.begin(bytesize, U_FLASH, -1); 177 212 178 213 Update.setMD5(md5.c_str()); 179 214 180 215 Update.onProgress([](unsigned int progress, unsigned int total) { 181 - outputf("\rFlash update progress: % 6u of % 6u", progress, 216 + /* 217 + * Just force serial output here rather than using outputf, 218 + * don't let it block us. 219 + */ 220 + Serial.printf("\rFlash update progress: % 6d of % 6d", progress, 182 221 total); 183 222 }); 184 223 185 - if ((int)Update.writeStream(client) != bytesize) { 224 + if ((int)Update.writeStream((tls ? client_tls : client)) != bytesize) { 186 225 if (Update.getError() == UPDATE_ERROR_BOOTSTRAP) 187 226 outputf("ERROR update must be done from fresh " 188 227 "reset, not from uploaded code\r\n"); ··· 190 229 outputf("ERROR failed writing download bytes: %d\r\n", 191 230 Update.getError()); 192 231 193 - while (client.available()) 194 - client.read(); 232 + while ((tls ? client_tls : client).available()) 233 + (tls ? client_tls : client).read(); 195 234 196 - client.stop(); 235 + (tls ? client_tls : client).stop(); 197 236 return; 198 237 } 199 238 ··· 203 242 return; 204 243 } 205 244 206 - client.stop(); 245 + (tls ? client_tls : client).stop(); 207 246 outputf("\r\nOK update completed, restarting\r\n"); 208 247 209 248 delay(500);
+1 -1
wifippp.h
··· 125 125 int telnet_write(String s); 126 126 127 127 /* update.cpp */ 128 - void update_process(bool, bool); 128 + void update_process(char *, bool, bool); 129 129 130 130 /* util.cpp */ 131 131 void syslog_setup(void);
+22 -11
wifippp.ino
··· 211 211 char cmd_char; 212 212 uint8_t cmd_num = 0; 213 213 bool did_nl = false; 214 + bool did_response = false; 214 215 215 216 lcmd = olcmd = (char *)malloc(len + 1); 216 217 if (lcmd == NULL) { ··· 238 239 cmd += 2; 239 240 lcmd += 2; 240 241 len -= 2; 241 - 242 - /* whether we printed a newline in our response */ 243 - did_nl = false; 244 242 245 243 parse_cmd: 246 244 if (lcmd[0] == '\0') ··· 763 761 /* AT$TTY?: show telnet TTYPE setting */ 764 762 outputf("\n%s\r\n", settings->telnet_tterm); 765 763 did_nl = true; 766 - } else if (strcmp(lcmd, "update?") == 0) { 764 + } else if (strncmp(lcmd, "update?", 7) == 0) { 767 765 /* AT$UPDATE?: show whether an OTA update is available */ 768 - update_process(false, false); 769 - } else if (strcmp(lcmd, "update!") == 0) { 766 + char *url = NULL; 767 + if (strncmp(lcmd, "update? http", 12) == 0) 768 + url = lcmd + 8; 769 + update_process(url, false, false); 770 + did_response = true; 771 + } else if (strncmp(lcmd, "update!", 7) == 0) { 770 772 /* AT$UPDATE!: force an OTA update */ 771 - update_process(true, true); 772 - } else if (strcmp(lcmd, "update") == 0) { 773 + char *url = NULL; 774 + if (strncmp(lcmd, "update! http", 12) == 0) 775 + url = lcmd + 8; 776 + update_process(url, true, true); 777 + did_response = true; 778 + } else if (strcmp(lcmd, "update") == 0 || 779 + strncmp(lcmd, "update http", 11) == 0) { 773 780 /* AT$UPDATE: do an OTA update */ 774 - update_process(true, false); 781 + char *url = NULL; 782 + if (strncmp(lcmd, "update http", 11) == 0) 783 + url = lcmd + 7; 784 + update_process(url, true, false); 785 + did_response = true; 775 786 } else 776 787 goto error; 777 788 ··· 867 878 if (olcmd) 868 879 free(olcmd); 869 880 870 - if (state == STATE_AT && !settings->quiet) { 881 + if (!did_response && state == STATE_AT && !settings->quiet) { 871 882 if (settings->verbal) 872 883 outputf("%sOK\r\n", did_nl ? "" : "\n"); 873 884 else ··· 880 891 if (olcmd) 881 892 free(olcmd); 882 893 883 - if (!settings->quiet) { 894 + if (!did_response && !settings->quiet) { 884 895 if (settings->verbal) { 885 896 output("\nERROR"); 886 897 if (errstr != NULL)