Last active
November 7, 2020 14:16
-
-
Save CelliesProjects/565102c0fb2f4b9eeb64b5a5cc1624c9 to your computer and use it in GitHub Desktop.
A simple Serial to WebSocket bridge - poc for reading DSMR conforming smart meters.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This is a serial to websocket server for application with a DSMR 5.x. conform 'Dutch Smart Meter'. | |
This sketch reads messages (terminated by a newline) from a smart meter connected on Serial | |
and echos these messages to clients connected on websocket '/ws'. | |
All non utf8 chararacters in the messages are dropped. | |
*/ | |
// Pins: https://github.com/LilyGO/ESP32-MINI-32-V1.3/blob/master/ZZ_Images/image1.jpg | |
#include <AsyncTCP.h> /* https://github.com/me-no-dev/AsyncTCP */ | |
#include <ESPAsyncWebServer.h> /* https://github.com/me-no-dev/ESPAsyncWebServer */ | |
#define TXD2_PIN 22 | |
#define RXD2_PIN 21 | |
const char * WIFI_NETWORK = "xxx"; | |
const char * WIFI_PASSWORD = "xxx"; | |
#define SET_STATIC_IP false /* If SET_STATIC_IP is set to true then STATIC_IP, GATEWAY, SUBNET and PRIMARY_DNS have to be set to some sane values */ | |
const IPAddress STATIC_IP(192, 168, 0, 90); /* This should be outside your router dhcp range! */ | |
const IPAddress GATEWAY(192, 168, 0, 1); /* Set to your gateway ip address */ | |
const IPAddress SUBNET(255, 255, 255, 0); /* Usually 255,255,255,0 but check in your router or pc connected to the same network */ | |
const IPAddress PRIMARY_DNS(192, 168, 0, 30); /* Check in your router */ | |
const IPAddress SECONDARY_DNS( 192, 168, 0, 50 ); /* Check in your router */ | |
AsyncWebServer server(80); | |
AsyncWebSocket ws("/ws"); | |
void setup() { | |
Serial.begin(115200); | |
Serial.printf("\n\nSerial to websocket server\n\nConnecting to %s...\n", WIFI_NETWORK); | |
if (SET_STATIC_IP && !WiFi.config(STATIC_IP, GATEWAY, SUBNET, PRIMARY_DNS, SECONDARY_DNS)) | |
Serial.println("Setting static IP failed"); | |
WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD); | |
while (!WiFi.isConnected()) | |
delay(10); | |
Serial.printf("Connected: %s\n", WiFi.localIP().toString().c_str()); | |
ws.onEvent(onEvent); | |
server.addHandler(&ws); | |
server.onNotFound([](AsyncWebServerRequest * request) { | |
ESP_LOGD(TAG, "404 - Not found: 'http://%s%s'", request->host().c_str(), request->url().c_str()); | |
request->send(404); | |
}); | |
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); | |
server.begin(); | |
//pinMode(RXD2_PIN, INPUT); | |
//Serial2.begin(115200, SERIAL_8N1, RXD2_PIN, TXD2_PIN); | |
//Serial.printf("Listening on Serial2 RXD=%i TXD=%i\n", RXD2_PIN, TXD2_PIN); | |
} | |
// https://www.utf8-chartable.de/ | |
void loop() { | |
static String message{""}; | |
// https://www.utf8-chartable.de/ | |
while (Serial.available()) { | |
const uint8_t incomingByte = Serial.read(); | |
switch (incomingByte) { | |
case '\n': | |
if (ws.count() && !message.equals("")) { | |
ws.textAll(message); | |
ESP_LOGI(TAG, "Message relayed: %s", message.c_str()); | |
} | |
message = ""; | |
break; | |
case 0x20 ... 0x7E : | |
message.concat((char)incomingByte); | |
ESP_LOGD(TAG, "Added code 0x%x '%c' to message", incomingByte, (char)incomingByte); | |
break; | |
case 0xC2 : { | |
const uint8_t secondByte = Serial.read(); | |
switch (secondByte) { | |
case 0xA0 ... 0xBF : { | |
message.concat((char)incomingByte); | |
message.concat((char)secondByte); | |
} | |
break; | |
default: ESP_LOGE(TAG, "Invalid 16-bit utf8 sequence. Dropped 2 bytes."); | |
} | |
} | |
break; | |
case 0xC3 : { | |
const uint8_t secondByte = Serial.read(); | |
switch (secondByte) { | |
case 0x80 ... 0xBF : { | |
message.concat((char)incomingByte); | |
message.concat((char)secondByte); | |
} | |
break; | |
default: ESP_LOGE(TAG, "Invalid 16-bit utf8 sequence. Dropped 2 bytes."); | |
} | |
} | |
break; | |
default : | |
ESP_LOGI(TAG, "Dropped invalid utf8 byte: 0x%x", incomingByte); | |
} | |
} | |
delay(1); | |
} | |
void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { | |
if (type == WS_EVT_CONNECT) { | |
ESP_LOGI(TAG, "ws[%s][%u] connect", server->url(), client->id()); | |
} else if (type == WS_EVT_DISCONNECT) { | |
ESP_LOGI(TAG, "ws[%s][%u] disconnect: %u", server->url(), client->id()); | |
} else if (type == WS_EVT_ERROR) { | |
ESP_LOGE(TAG, "ws[%s][%u] error(%u): %s", server->url(), client->id(), *((uint16_t*)arg), (char*)data); | |
} else if (type == WS_EVT_DATA) { | |
AwsFrameInfo * info = (AwsFrameInfo*)arg; | |
// here all data is contained in a single packet - and since we only connect and listen we do not check for multi-packet or multi-frame messages | |
if (info->final && info->index == 0 && info->len == len) { | |
if (info->opcode == WS_TEXT) { | |
data[len] = 0; | |
ESP_LOGD(TAG, "ws request: %s", reinterpret_cast<char*>(data)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For a esp32 board