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

Transparent serial-TCP bridge and flash-programming of attached AVR uControllers #1314

Open
wants to merge 8 commits into
base: v2
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
6 changes: 6 additions & 0 deletions code/devices/086_wifi_pico_12.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"board": 86,
"device": "WIFI_PICO_12",
"ledGPIO0": 0,
"ledLogic0": 1,
}
11 changes: 11 additions & 0 deletions code/espurna/config/dependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
#define TERMINAL_SUPPORT 0
#undef DEBUG_SERIAL_SUPPORT
#define DEBUG_SERIAL_SUPPORT 0
#undef SERIAL_BRIDGE_SUPPORT
#define SERIAL_BRIDGE_SUPPORT 0
#endif

#if SERIAL_BRIDGE_SUPPORT
//#undef TERMINAL_SUPPORT
//#define TERMINAL_SUPPORT 0
//#undef DEBUG_SERIAL_SUPPORT
//#define DEBUG_SERIAL_SUPPORT 0
#undef UART_MQTT_SUPPORT
#define UART_MQTT_SUPPORT 0
#endif

#if DOMOTICZ_SUPPORT
Expand Down
3 changes: 3 additions & 0 deletions code/espurna/config/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum devices {
DEVICE_IWOOLE_LED_TABLE_LAMP,
DEVICE_EXS_WIFI_RELAY_V50,
DEVICE_BESTEK_MRJ1011,
DEVICE_WIFI_PICO_12,

DEVICE_LAST

Expand Down Expand Up @@ -160,7 +161,9 @@ enum devices {
defined(TONBUX_MOSQUITO_KILLER) || \
defined(TONBUX_POWERSTRIP02) || \
defined(TONBUX_XSSSA06) || \
defined(WEMOS_D1_MINI) || \
defined(WEMOS_D1_MINI_RELAYSHIELD) || \
defined(WIFI_PICO_12) || \
defined(WION_50055) || \
defined(WORKCHOICE_ECOPLUG) || \
defined(XENON_SM_PW702U) || \
Expand Down
7 changes: 7 additions & 0 deletions code/espurna/config/devices/086_wifi_pico_12.json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#define device_config_len 64
const uint8_t device_config[] PROGMEM = {
0x7b,0x22,0x62,0x6f,0x61,0x72,0x64,0x22,0x3a,0x38,0x36,0x2c,0x22,0x64,0x65,0x76,0x69,0x63,0x65,0x22,
0x3a,0x22,0x57,0x49,0x46,0x49,0x5f,0x50,0x49,0x43,0x4f,0x5f,0x31,0x32,0x22,0x2c,0x22,0x6c,0x65,0x64,
0x47,0x50,0x49,0x4f,0x30,0x22,0x3a,0x30,0x2c,0x22,0x6c,0x65,0x64,0x4c,0x6f,0x67,0x69,0x63,0x30,0x22,
0x3a,0x31,0x2c,0x7d
};
32 changes: 32 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,38 @@

#define UART_MQTT_BUFFER_SIZE 100 // UART buffer size

// -----------------------------------------------------------------------------
// SERIAL BRIDGE (UART <-> TCP)
// -----------------------------------------------------------------------------

#ifndef SERIAL_BRIDGE_SUPPORT
#define SERIAL_BRIDGE_SUPPORT 0 // No support by default
#endif

#ifndef SERIAL_BRIDGE_PORT
#define SERIAL_BRIDGE_PORT Serial // Hardware serial port (no real choice)
#endif

#ifndef SBR_PORT
#define SBR_PORT 2323 // Serial bridge TCP properties
#endif

#ifndef SBR_BAUD
#define SBR_BAUD 115200 // Serial speed for normal bridging
#endif

#ifndef SBR_RXBUF
#define SBR_RXBUF 2048 // Serial port RX buffer size in bytes
#endif

#ifndef SBR_AVRBAUD
#define SBR_AVRBAUD 57600 // Serial speed for programming an AVR
#endif

#ifndef SBR_AVRRESET
#define SBR_AVRRESET 12 // Pin to use to reset attached AVR
#endif

// -----------------------------------------------------------------------------
// MQTT
// -----------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions code/espurna/config/progmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ PROGMEM const char espurna_modules[] =
#if SENSOR_SUPPORT
"SENSOR "
#endif
#if SERBRIDGE_SUPPORT
"SERBRIDGE "
#endif
#if SPIFFS_SUPPORT
"SPIFFS "
#endif
Expand Down
12 changes: 11 additions & 1 deletion code/espurna/config/webui.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
#define WEBUI_IMAGE_SENSOR 2
#define WEBUI_IMAGE_RFBRIDGE 4
#define WEBUI_IMAGE_RFM69 8
#define WEBUI_IMAGE_FULL 15
#define WEBUI_IMAGE_SERBRIDGE 16
#define WEBUI_IMAGE_FULL 31

#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#ifdef WEBUI_IMAGE
Expand Down Expand Up @@ -45,6 +46,15 @@
#endif
#endif

#if SERIAL_BRIDGE_SUPPORT == 1
#ifndef WEBUI_IMAGE
#define WEBUI_IMAGE WEBUI_IMAGE_SERBRIDGE
#else
#undef WEBUI_IMAGE
#define WEBUI_IMAGE WEBUI_IMAGE_FULL
#endif
#endif

#ifndef WEBUI_IMAGE
#define WEBUI_IMAGE WEBUI_IMAGE_SMALL
#endif
Expand Down
9 changes: 4 additions & 5 deletions code/espurna/debug.ino
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,10 @@ void debugSetup() {

if (_dbg_serial_enabled) {

unsigned char port = getSetting("dbgPort", 0).toInt();
if (0 == port) {
_dbg_port = Serial;
} else {
_dbg_port = Serial1;
unsigned char port = getSetting("dbgPort", 255).toInt();
switch (port) {
case 0: _dbg_port = Serial; break;
case 1: _dbg_port = Serial1; break;
}

unsigned long speed = getSetting("dbgSpeed", DEBUG_SERIAL_SPEED).toInt();
Expand Down
9 changes: 9 additions & 0 deletions code/espurna/device.ino
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ void _deviceLoad() {
setSetting("rlyGPIO", 0, 5);
setSetting("rlyType", 0, RELAY_TYPE_NORMAL);

#elif defined(WIFI_PICO_12)

setSetting("board", DEVICE_WIFI_PICO_12);
setSetting("device", "WIFI_PICO_12");
setSetting("fw", ESPURNA_BASIC);

setSetting("ledGPIO", 0, 0);
setSetting("ledLogic", 0, GPIO_LOGIC_INVERSE);

#elif defined(ITEAD_SONOFF_BASIC)

setSetting("board", DEVICE_ITEAD_SONOFF_BASIC);
Expand Down
3 changes: 3 additions & 0 deletions code/espurna/espurna.ino
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ void setup() {
#if UART_MQTT_SUPPORT
uartmqttSetup();
#endif
#if SERIAL_BRIDGE_SUPPORT
serialBridgeSetup();
#endif

// 3rd party code hook
#if USE_EXTRA
Expand Down
65 changes: 65 additions & 0 deletions code/espurna/serialbridge.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*

SERIAL_BRIDGE MODULE

Copyright (C) 2018 by Throsten von Eicken

Module key prefix: sbr

*/

#if SERIAL_BRIDGE_SUPPORT

#include "SerialBridge.h"

SerialBridge _sbr;

// Settings
int _sbrPort, _sbrBaud, _sbrRxBuf;
int _sbrAvrBaud;

#if WEB_SUPPORT

void _sbrWebSocketOnSend(JsonObject& root) {
//DEBUG_MSG_P(PSTR("[SERIAL BRIDGE] WebSocket OnSend\n"));
root["sbrVisible"] = 1;
root["sbrPort"] = getSetting("sbrPort", SBR_PORT);
root["sbrBaud"] = getSetting("sbrBaud", SBR_BAUD);
root["sbrRxBuf"] = getSetting("sbrRxBuf", SBR_RXBUF);
root["sbrAvrBaud"] = getSetting("sbrAvrBaud", SBR_AVRBAUD);
root["sbrAvrReset"] = getSetting("sbrAvrReset", SBR_AVRRESET);
}

void _sbrWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
//if (strcmp(action, "rfblearn") == 0) _rfLearn(data["id"], data["status"]);
//if (strcmp(action, "rfbforget") == 0) _rfForget(data["id"], data["status"]);
//if (strcmp(action, "rfbsend") == 0) _rfStore(data["id"], data["status"], data["data"].as<long>());
}

#endif

bool _sbrKeyCheck(const char * key) {
return (strncmp(key, "sbr", 3) == 0);
}

void _sbrLoop() {
_sbr.loop();
}

void serialBridgeSetup() {
_sbr.debug(&debugSend_P);
_sbr.begin(
getSetting("sbrPort", SBR_PORT).toInt(),
getSetting("sbrBaud", SBR_BAUD).toInt(),
getSetting("sbrRxBuf", SBR_RXBUF).toInt());

#if WEB_SUPPORT
wsOnSendRegister(_sbrWebSocketOnSend);
//wsOnActionRegister(_sbrWebSocketOnAction);
#endif

settingsRegisterKeyCheck(_sbrKeyCheck);
espurnaRegisterLoop(_sbrLoop);
}

#endif
118 changes: 118 additions & 0 deletions code/espurna/web.ino
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Module key prefix: web

#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AVRFlash.h>
#include <Hash.h>
#include <FS.h>
#include <AsyncJson.h>
Expand All @@ -29,6 +30,8 @@ Module key prefix: web
#include "static/index.rfbridge.html.gz.h"
#elif WEBUI_IMAGE == WEBUI_IMAGE_RFM69
#include "static/index.rfm69.html.gz.h"
#elif WEBUI_IMAGE == WEBUI_IMAGE_SERBRIDGE
#include "static/index.sbr.html.gz.h"
#elif WEBUI_IMAGE == WEBUI_IMAGE_FULL
#include "static/index.all.html.gz.h"
#endif
Expand All @@ -49,6 +52,8 @@ bool _webConfigSuccess = false;

std::vector<web_request_callback_f> _web_request_callbacks;

AVRFlash *_avrflash = 0;

// -----------------------------------------------------------------------------
// HOOKS
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -267,9 +272,11 @@ int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
#endif

void _onUpgrade(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[UPGRADE] Auth\n"));

webLog(request);
if (!webAuthenticate(request)) {
DEBUG_MSG_P(PSTR("[UPGRADE] Auth failed\n"));
return request->requestAuthentication(getHostname().c_str());
}

Expand Down Expand Up @@ -326,6 +333,116 @@ void _onUpgradeData(AsyncWebServerRequest *request, String filename, size_t inde
}
}

extern SerialBridge _sbr;

void _AVRflashRespond(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[AVRFLASH] _AVRflashRespond req=%x cli=%x\n"), (uint32_t)request, (uint32_t)(request->client()));
char buffer[128];
int code = 200;
if (!_avrflash->hasError()) {
sprintf_P(buffer, PSTR("Flashing successful!"));
DEBUG_MSG_P(PSTR("[AVRFLASH] Success\n"));
//DEBUG_MSG_P(PSTR("[AVRFLASH] Success: %u bytes\n"), index + len);
} else {
DEBUG_MSG_P(PSTR("[AVRFLASH] Error: %s\n"), _avrflash->getError());
sprintf_P(buffer, PSTR("Flashing error: %s"), _avrflash->getError());
code = 400;
}

AsyncWebServerResponse *response = request->beginResponse(code, "text/plain", buffer);
response->addHeader("Connection", "close");
response->addHeader("X-XSS-Protection", "1; mode=block");
response->addHeader("X-Content-Type-Options", "nosniff");
response->addHeader("X-Frame-Options", "deny");
DEBUG_MSG_P(PSTR("sending\n"));
request->send(response);
DEBUG_MSG_P(PSTR("[AVRFLASH] Response sent\n"));

if (_avrflash) delete _avrflash;
_avrflash = 0;
_sbr.enable();
}

// _onAVRflash is called to finish off the flashing of an attached AVR microprocessor and to return
// an HTTP response.
void _onAVRflash(AsyncWebServerRequest *request) {
webLog(request);
DEBUG_MSG_P(PSTR("[AVRFLASH] _onAVRflash client=%x\n"), (uint32_t)(request->client()));
if (!webAuthenticate(request)) {
DEBUG_MSG_P(PSTR("[AVRFLASH] requesting auth\n"));
return request->requestAuthentication(getHostname().c_str());
}

DEBUG_MSG_P(PSTR("[AVRFLASH] Finishing req=%x cli=%x\n"), (uint32_t)request,
(uint32_t)(request->client()));
if (_avrflash) _avrflash->finish(_AVRflashRespond, request);
}

// need to instatiate template member functions that we use... yuck, I hate c++
template void AVRFlash::finish<AsyncWebServerRequest*>(
void(*)(AsyncWebServerRequest*), AsyncWebServerRequest*);
template uint32_t HexRecord::write<AsyncWebServerRequest*>(
uint8_t *data, size_t len, void (*stop)(AsyncWebServerRequest*), void
(*resume)(AsyncWebServerRequest*), AsyncWebServerRequest* cbArg);

void _AVRflashStop(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[AVRFLASH] Stop TCP\n"));
request->client()->ackLater();
}

void _AVRflashResume(AsyncWebServerRequest *request) {
DEBUG_MSG_P(PSTR("[AVRFLASH] Resume TCP\n"));
request->client()->ack(1000000);
}

void _AVRflashDisconnect() {
DEBUG_MSG_P(PSTR("[AVRFLASH] Disconnect\n"));
if (!_avrflash) return;
_avrflash->abort();
delete _avrflash;
_avrflash = 0;
_sbr.enable();
}

// _onAVRflashData is called with data to flash an attached AVR microprocessor.
void _onAVRflashData(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
//DEBUG_MSG_P(PSTR("[AVRFLASH] _onAVRflashData i=%d len=%d final=%d\n"), index, len, final);
if (index == 0) {
// Index is zero if this is the very first callback for a request.
if (_avrflash) {
DEBUG_MSG_P(PSTR("[AVRFLASH] Ooops, multiple concurrent flashing operations...\n"));
}
_sbr.disable();
request->onDisconnect(&_AVRflashDisconnect);
request->client()->setRxTimeout(30);
_avrflash = new AVRFlash(Serial,
getSetting("sbrAvrReset", SBR_AVRRESET).toInt(),
getSetting("sbrAvrBaud", SBR_AVRBAUD).toInt());
_avrflash->debug(debugSend_P);
_avrflash->sync();
DEBUG_MSG_P(PSTR("[AVRFLASH] %s @%dbaud reset on pin %d\n"), filename.c_str(), 57600,
12);
}

if (_avrflash->hasError()) return;

if (_avrflash->write(data, len, _AVRflashStop, _AVRflashResume, request) != len) {
DEBUG_MSG_P(PSTR("[AVRFLASH] Error #%u\n"), _avrflash->getError());
return;
}
DEBUG_MSG_P(PSTR("[AVRFLASH] Progress: %u bytes\r"), index + len);

#if 0
// final is true if this is the last callback for a request.
if (!final) return;
if (_avrflash->finish()){
DEBUG_MSG_P(PSTR("[AVRFLASH] Success: %u bytes\n"), index + len);
} else {
DEBUG_MSG_P(PSTR("[AVRFLASH] Error #%u\n"), _avrflash->getError());
}
#endif
}

void _webWebSocketOnSend(JsonObject& root) {
root["webPort"] = webPort();
}
Expand Down Expand Up @@ -413,6 +530,7 @@ void webSetup() {
_server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigData);
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData);
_server->on("/discover", HTTP_GET, _onDiscover);
_server->on("/flashavr", HTTP_POST, _onAVRflash, _onAVRflashData);

// Serve static files
#if SPIFFS_SUPPORT
Expand Down
Loading