Skip to content

Instantly share code, notes, and snippets.

@CelliesProjects
Last active November 7, 2020 14:16
Show Gist options
  • Save CelliesProjects/565102c0fb2f4b9eeb64b5a5cc1624c9 to your computer and use it in GitHub Desktop.
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 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));
}
}
}
}
@CelliesProjects
Copy link
Author

For a esp32 board

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment