Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ota #3

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/painlessMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include <ArduinoJson.h>
#include <functional>
#include <memory>
#ifdef ESP32
#include <Update.h>
#endif

using namespace std;
#include "espInterface.h"
#include "painlessTCP.h"
Expand All @@ -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
};
Expand Down Expand Up @@ -164,6 +168,9 @@ class painlessMesh {

void tcpServerInit();

// in painlessMeshOTA.cpp
void handleOTA(std::shared_ptr<MeshConnection> conn, JsonObject& root);

// callbacks
// in painlessMeshConnection.cpp
static int espWifiEventCb(void * ctx, system_event_t *event);
Expand Down
3 changes: 3 additions & 0 deletions src/painlessMeshConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
203 changes: 203 additions & 0 deletions src/painlessMeshOTA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <memory>
#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<MeshConnection> conn, JsonObject& root) {

otaPacketType t_ota = (otaPacketType)(int)(root["msg"]["type"]);

String msg;

if(t_ota == OTA_INIT)
{
String otaMD5 = root["msg"]["md5"].as<String>();
#ifdef ESP32
uint32_t maxSketchSpace = 0x140000;
#else
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);
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);
}
}
}