From 043ed53ee13ced14570d5f0fa6df25763c237036 Mon Sep 17 00:00:00 2001 From: DeeFuse Date: Thu, 22 Mar 2018 15:47:01 +0100 Subject: [PATCH 1/2] OTA working with painlessMeshManager --- src/painlessMesh.h | 9 +- src/painlessMeshConnection.cpp | 3 + src/painlessMeshOTA.cpp | 199 +++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/painlessMeshOTA.cpp diff --git a/src/painlessMesh.h b/src/painlessMesh.h index 8d5c0dc..6af4f5e 100644 --- a/src/painlessMesh.h +++ b/src/painlessMesh.h @@ -10,6 +10,10 @@ #include #include #include +#ifdef ESP32 + #include +#endif + using namespace std; #include "espInterface.h" #include "painlessTCP.h" @@ -34,7 +38,7 @@ enum meshPackageType { TIME_SYNC = 4, NODE_SYNC_REQUEST = 5, NODE_SYNC_REPLY = 6, - CONTROL = 7, //deprecated + OTA = 7, //OTA packet BROADCAST = 8, //application data for everyone SINGLE = 9 //application data for a single node }; @@ -164,6 +168,9 @@ class painlessMesh { void tcpServerInit(); + // in painlessMeshOTA.cpp + void handleOTA(std::shared_ptr conn, JsonObject& root); + // callbacks // in painlessMeshConnection.cpp static int espWifiEventCb(void * ctx, system_event_t *event); diff --git a/src/painlessMeshConnection.cpp b/src/painlessMeshConnection.cpp index cc57990..be2e6a6 100644 --- a/src/painlessMeshConnection.cpp +++ b/src/painlessMeshConnection.cpp @@ -581,9 +581,12 @@ void ICACHE_FLASH_ATTR MeshConnection::handleMessage(String &buffer, uint32_t re case SINGLE: case TIME_DELAY: + case OTA: if ((uint32_t)root["dest"] == staticThis->getNodeId()) { // msg for us! if (t_message == TIME_DELAY) { staticThis->handleTimeDelay(rConn, root, receivedAt); + } else if(t_message == OTA) { + staticThis->handleOTA(rConn, root); } else { if (staticThis->receivedCallback) staticThis->receivedCallback((uint32_t)root["from"], msg); diff --git a/src/painlessMeshOTA.cpp b/src/painlessMeshOTA.cpp new file mode 100644 index 0000000..ad0c4c4 --- /dev/null +++ b/src/painlessMeshOTA.cpp @@ -0,0 +1,199 @@ +#include +#include +#include +#include "painlessMesh.h" + +enum otaPacketType { + OTA_INIT = 0, + OTA_DATA, + OTA_FIN +}; + +extern painlessMesh* staticThis; + +/* Modified from https://github.com/fcgdam/ESP8266-base64 to handle larger data portions */ +const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/* 'Private' declarations */ +inline void a3_to_a4(unsigned char * a4, unsigned char * a3); +inline void a4_to_a3(unsigned char * a3, unsigned char * a4); +inline unsigned char b64_lookup(char c); + +size_t base64_encode(char *output, char *input, size_t inputLen) { + size_t i = 0, j = 0; + size_t encLen = 0; + unsigned char a3[3]; + unsigned char a4[4]; + + while(inputLen--) { + a3[i++] = *(input++); + if(i == 3) { + a3_to_a4(a4, a3); + + for(i = 0; i < 4; i++) { + output[encLen++] = b64_alphabet[a4[i]]; + } + i = 0; + } + } + + if(i) { + for(j = i; j < 3; j++) { + a3[j] = '\0'; + } + + a3_to_a4(a4, a3); + + for(j = 0; j < i + 1; j++) { + output[encLen++] = b64_alphabet[a4[j]]; + } + + while((i++ < 3)) { + output[encLen++] = '='; + } + } + output[encLen] = '\0'; + return encLen; +} + +size_t base64_decode(char *output, char *input, size_t inputLen) { + size_t i = 0, j = 0; + size_t decLen = 0; + unsigned char a3[3]; + unsigned char a4[4]; + + + while (inputLen--) { + if(*input == '=') { + break; + } + + a4[i++] = *(input++); + if (i == 4) { + for (i = 0; i <4; i++) { + a4[i] = b64_lookup(a4[i]); + } + + a4_to_a3(a3,a4); + + for (i = 0; i < 3; i++) { + output[decLen++] = a3[i]; + } + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) { + a4[j] = '\0'; + } + + for (j = 0; j <4; j++) { + a4[j] = b64_lookup(a4[j]); + } + + a4_to_a3(a3,a4); + + for (j = 0; j < i - 1; j++) { + output[decLen++] = a3[j]; + } + } + output[decLen] = '\0'; + return decLen; +} + +size_t base64_enc_len(size_t plainLen) { + size_t n = plainLen; + return (n + 2 - ((n + 2) % 3)) / 3 * 4; +} + +size_t base64_dec_len(char * input, size_t inputLen) { + size_t i = 0; + size_t numEq = 0; + for(i = inputLen - 1; input[i] == '='; i--) { + numEq++; + } + + return ((6 * inputLen) / 8) - numEq; +} + +inline void a3_to_a4(unsigned char * a4, unsigned char * a3) { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = (a3[2] & 0x3f); +} + +inline void a4_to_a3(unsigned char * a3, unsigned char * a4) { + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; +} + +inline unsigned char b64_lookup(char c) { + int i; + for(i = 0; i < 64; i++) { + if(b64_alphabet[i] == c) { + return i; + } + } + return -1; +} + +void ICACHE_FLASH_ATTR painlessMesh::handleOTA(std::shared_ptr conn, JsonObject& root) { + + otaPacketType t_ota = (otaPacketType)(int)(root["msg"]["type"]); + + String msg; + + if(t_ota == OTA_INIT) + { + String otaMD5 = root["msg"]["md5"].as(); +#ifdef ESP32 + uint32_t maxSketchSpace = 0x140000; +#else + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#endif + Serial.printf("Sketch size %d\n", maxSketchSpace); + if(!Update.begin(maxSketchSpace)){ //start with max available size + staticThis->debugMsg(ERROR, "handleOTA(): OTA start failed!\n"); + Update.printError(Serial); + Update.end(); + } else { + Update.setMD5(otaMD5.c_str()); + msg = "OK"; + staticThis->sendMessage(conn, conn->nodeId, _nodeId, OTA, msg, true); + } + } + if(t_ota == OTA_DATA) + { + const char* b64data = root["msg"]["data"]; + size_t b64len = root["msg"]["length"]; + size_t binlength = base64_dec_len((char*)b64data, b64len); + uint8_t *b64Data = (uint8_t*)malloc(binlength); + + base64_decode((char*)b64Data, (char*)b64data, b64len); // Dekodiere Base64 + + if(Update.write(b64Data, binlength) != binlength){ + staticThis->debugMsg(ERROR, "handleOTA(): OTA write failed!\n"); + Update.printError(Serial); + Update.end(); + } else { + msg = "OK"; + staticThis->sendMessage(conn, conn->nodeId, _nodeId, OTA, msg, true); + } + free(b64Data); + } + if(t_ota == OTA_FIN) + { + if(Update.end(true)){ //true to set the size to the current progress + staticThis->debugMsg(APPLICATION, "handleOTA(): OTA Success!\n"); + ESP.restart(); + } else { + staticThis->debugMsg(ERROR, "handleOTA(): OTA failed!\n"); + Update.printError(Serial); + } + } +} \ No newline at end of file From cdf359259a4d9e1f3d6d8b62c0524ffec5f80612 Mon Sep 17 00:00:00 2001 From: DeeFuse Date: Fri, 23 Mar 2018 15:17:58 +0100 Subject: [PATCH 2/2] Add cleanup at upload reattempt --- src/painlessMeshOTA.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/painlessMeshOTA.cpp b/src/painlessMeshOTA.cpp index ad0c4c4..ad1cf4d 100644 --- a/src/painlessMeshOTA.cpp +++ b/src/painlessMeshOTA.cpp @@ -157,6 +157,10 @@ void ICACHE_FLASH_ATTR painlessMesh::handleOTA(std::shared_ptr c uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; #endif Serial.printf("Sketch size %d\n", maxSketchSpace); + if(Update.isRunning()) + { + Update.end(false); + } if(!Update.begin(maxSketchSpace)){ //start with max available size staticThis->debugMsg(ERROR, "handleOTA(): OTA start failed!\n"); Update.printError(Serial);