From a5c342b26fd99409ca577532057d3eaf88c8539c Mon Sep 17 00:00:00 2001 From: James Date: Mon, 29 Apr 2024 09:19:39 +0100 Subject: [PATCH] Try ETH websockets. Wont connect :/ --- ESPBMS.ino | 104 ++++++++++++++++++++++++++++++++++++--------- WebSocketManager.h | 83 ++++++++++++++++++++++++++++++++++++ bin/2graphite.py | 57 ------------------------- ca_cert.h | 23 ++++++++++ victron.ino | 14 +++--- 5 files changed, 197 insertions(+), 84 deletions(-) create mode 100644 WebSocketManager.h delete mode 100644 bin/2graphite.py create mode 100644 ca_cert.h diff --git a/ESPBMS.ino b/ESPBMS.ino index 2660332..1112970 100644 --- a/ESPBMS.ino +++ b/ESPBMS.ino @@ -1,9 +1,20 @@ #include "BLEDevice.h" +#include +#include "WebSocketManager.h" + +WebSocketManager wsManager("ws://chodbox.home.arpa", 8765, "/"); static BLEUUID serviceUUID("0000ff00-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms service static BLEUUID charUUID_rx("0000ff01-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms rx id static BLEUUID charUUID_tx("0000ff02-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms tx id +#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN // ETH_CLOCK_GPIO17_OUT +#define ETH_POWER_PIN 16 +#define ETH_TYPE ETH_PHY_LAN8720 +#define ETH_ADDR 1 +#define ETH_MDC_PIN 23 +#define ETH_MDIO_PIN 18 + typedef struct { byte start; @@ -12,15 +23,19 @@ typedef struct byte dataLen; } bmsPacketHeaderStruct; -void setup() { - Serial.begin(115200); - BLEDevice::init(""); // Initialize BLE device -} - char currentName[128]; bool gotBasicInfo; bool gotCellInfo; +static bool eth_ready = false; +void setup() { + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + Serial.begin(115200); + WiFi.onEvent(WiFiEvent); + ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE); + BLEDevice::init(""); + wsManager.begin(); +} void loop() { Serial.printf("\r\n\r\n===============================\r\n\r\n"); @@ -33,6 +48,16 @@ void loop() { Serial.println("Devices found: " + String(foundDevices.getCount())); + while(!eth_ready){ + Serial.println("Wait for eth..."); + delay(250); + } + + while(!wsManager.isConnected()){ + Serial.println("Wait for socket..."); + delay(250); + } + for (int i = 0; i < foundDevices.getCount(); i++) { delay(1000); Serial.printf("\r\n\r\n===============================\r\n\r\n"); @@ -304,8 +329,8 @@ bool processBasicInfo(byte *data, unsigned int dataLen){ int32_t Watts = Volts * Amps / 1000000; // W - //Serial.printf("Remaining Capacity: %4.2fAhr\n", ((float)(data[4] * 256 + data[5]))/100); - //Serial.printf("Nominal Capacity: %4.2fAhr\n", ((float)(data[6] * 256 + data[7]))/100); + //Serial.printf("Remaining Capacity: %4.2fAh\n", ((float)(data[4] * 256 + data[5]))/100); + //Serial.printf("Nominal Capacity: %4.2fAh\n", ((float)(data[6] * 256 + data[7]))/100); uint32_t CapacityRemainAh = ((uint16_t)two_ints_into16(data[4], data[5])) * 10; uint8_t CapacityRemainPercent = ((uint8_t)data[19]); @@ -317,14 +342,14 @@ bool processBasicInfo(byte *data, unsigned int dataLen){ uint16_t BalanceCodeHigh = (two_ints_into16(data[14], data[15])); uint8_t MosfetStatus = ((byte)data[20]); - Serial.printf(">>>RC.%s.Voltage %f\r\n",currentName, (float)Volts / 1000); - Serial.printf(">>>RC.%s.Amps %f\r\n",currentName, (float)Amps / 1000); - Serial.printf(">>>RC.%s.Watts %f\r\n",currentName, (float)Watts); - Serial.printf(">>>RC.%s.Capacity_Remain_Ah %f\r\n",currentName, (float)CapacityRemainAh / 1000); - Serial.printf(">>>RC.%s.Capacity_Remain_Wh %f\r\n",currentName, ((float)(CapacityRemainAh) / 1000) * ((float)(Volts) / 1000)); - Serial.printf(">>>RC.%s.Capacity_Remain_Percent %d\r\n",currentName, CapacityRemainPercent); - Serial.printf(">>>RC.%s.Temp1 %f\r\n",currentName, (float)Temp1 / 10); - Serial.printf(">>>RC.%s.Temp2 %f\r\n",currentName, (float)Temp2 / 10); + wsManager.sendText("test.RC.%s.Voltage %f",currentName, (float)Volts / 1000); + wsManager.sendText("test.RC.%s.Amps %f",currentName, (float)Amps / 1000); + wsManager.sendText("test.RC.%s.Watts %f",currentName, (float)Watts); + wsManager.sendText("test.RC.%s.Capacity_Remain_Ah %f",currentName, (float)CapacityRemainAh / 1000); + wsManager.sendText("test.RC.%s.Capacity_Remain_Wh %f",currentName, ((float)(CapacityRemainAh) / 1000) * ((float)(Volts) / 1000)); + wsManager.sendText("test.RC.%s.Capacity_Remain_Percent %d",currentName, CapacityRemainPercent); + wsManager.sendText("test.RC.%s.Temp1 %f",currentName, (float)Temp1 / 10); + wsManager.sendText("test.RC.%s.Temp2 %f",currentName, (float)Temp2 / 10); /* Serial.printf("%s Balance Code Low: 0x%x\r\n",currentName, BalanceCodeLow); Serial.printf("%s Balance Code High: 0x%x\r\n",currentName, BalanceCodeHigh); @@ -358,13 +383,13 @@ bool processCellInfo(byte *data, unsigned int dataLen) _cellMin = CellVolt; } - Serial.printf(">>>RC.%s.Cell.%d.Voltage %f\r\n",currentName, i+1,(float)CellVolt/1000); + wsManager.sendText("test.RC.%s.Cell.%d.Voltage %f",currentName, i+1,(float)CellVolt/1000); } - Serial.printf(">>>RC.%s.Max_Cell_Voltage %f\r\n",currentName, (float)_cellMax / 1000); - Serial.printf(">>>RC.%s.Min_Cell_Voltage %f\r\n",currentName, (float)_cellMin / 1000); - Serial.printf(">>>RC.%s.Difference_Cell_Voltage %f\r\n",currentName, (float)(_cellMax - _cellMin) / 1000); - Serial.printf(">>>RC.%s.Average_Cell_Voltage %f\r\n",currentName, (float)(_cellSum / NumOfCells) / 1000); + wsManager.sendText("test.RC.%s.Max_Cell_Voltage %f",currentName, (float)_cellMax / 1000); + wsManager.sendText("test.RC.%s.Min_Cell_Voltage %f",currentName, (float)_cellMin / 1000); + wsManager.sendText("test.RC.%s.Difference_Cell_Voltage %f",currentName, (float)(_cellMax - _cellMin) / 1000); + wsManager.sendText("test.RC.%s.Average_Cell_Voltage %f",currentName, (float)(_cellSum / NumOfCells) / 1000); gotCellInfo=true; @@ -412,3 +437,42 @@ int16_t two_ints_into16(int highbyte, int lowbyte) // turns two bytes into a sin result = (result | lowbyte); //OR operation, merge the two return result; } + + +void WiFiEvent(WiFiEvent_t event) { + Serial.print("E:"); + Serial.println(event); + switch (event) { + case ARDUINO_EVENT_ETH_START: + Serial.println("ETH Started"); + //set eth hostname here + ETH.setHostname("esp32-ethernet"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + Serial.println("ETH Connected"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + Serial.print("ETH MAC: "); + Serial.print(ETH.macAddress()); + Serial.print(", IPv4: "); + Serial.print(ETH.localIP()); + if (ETH.fullDuplex()) { + Serial.print(", FULL_DUPLEX"); + } + Serial.print(", "); + Serial.print(ETH.linkSpeed()); + Serial.println("Mbps"); + eth_ready = true; + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + Serial.println("ETH Disconnected"); + eth_ready = false; + break; + case ARDUINO_EVENT_ETH_STOP: + Serial.println("ETH Stopped"); + eth_ready = false; + break; + default: + break; + } +} diff --git a/WebSocketManager.h b/WebSocketManager.h new file mode 100644 index 0000000..a6bde96 --- /dev/null +++ b/WebSocketManager.h @@ -0,0 +1,83 @@ +#ifndef WEBSOCKETMANAGER_H +#define WEBSOCKETMANAGER_H + +#include +#include +#include +#include "ca_cert.h" + +class WebSocketManager { +public: + WebSocketManager(const char* server, uint16_t port, const char* url) { + serverUrl = server; + serverPort = port; + serverPath = url; + } + + void begin() { + xTaskCreate(&WebSocketManager::webSocketTask, "WebSocketTask", 8192, nullptr, 1, nullptr); + } + + template + void sendText(const char* format, Args... args) { + if (webSocket.isConnected() && isConnected) { + char buffer[512]; // Define a buffer to hold the constructed message + snprintf(buffer, sizeof(buffer), format, args...); // Use snprintf to format the string + webSocket.sendTXT(buffer); + } + } + + static bool isConnected() { + return connected; + } + +private: + static void webSocketTask(void *param) { + WiFiClient client; + //client.setCACert(ca_cert); + //webSocket.beginSSL(serverUrl.c_str(), serverPort, serverPath.c_str(), ca_cert); + webSocket.begin(serverUrl.c_str(), serverPort, serverPath.c_str()); + webSocket.onEvent(webSocketEvent); + webSocket.setReconnectInterval(1000); + + for (;;) { + webSocket.loop(); + vTaskDelay(1 / portTICK_PERIOD_MS); + } + } + + static void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + connected = false; + Serial.println("[WebSocket] Disconnected"); + break; + case WStype_CONNECTED: + connected = true; + Serial.println("[WebSocket] Connected"); + break; + case WStype_TEXT: + Serial.print("[WebSocket] Received: "); + Serial.println((char*)payload); + break; + case WStype_BIN: + Serial.println("[WebSocket] Received binary data"); + break; + } + } + + static WebSocketsClient webSocket; + static String serverUrl; + static uint16_t serverPort; + static String serverPath; + static bool connected; +}; + +// Define static members +WebSocketsClient WebSocketManager::webSocket; +String WebSocketManager::serverUrl = ""; +uint16_t WebSocketManager::serverPort = 0; +String WebSocketManager::serverPath = ""; +bool WebSocketManager::connected = false; + +#endif // WEBSOCKETMANAGER_H diff --git a/bin/2graphite.py b/bin/2graphite.py deleted file mode 100644 index 7561cf7..0000000 --- a/bin/2graphite.py +++ /dev/null @@ -1,57 +0,0 @@ -import asyncio -import aiohttp -import sys -import serial_asyncio # Ensure this package is installed - -def parse_to_graphite(data): - results = [] - - lines = data.strip().split('\n') - for line in lines: - if line.startswith('>>>'): - metric_path = line[3:].strip() # Remove the ">>>" prefix and any leading/trailing whitespace - results.append(metric_path) - return results - -async def send_to_graphite(data, session, api_url): - for message in data: - #print(f"Sending data to API: {message}") # Debug message for sending data - try: - # Sending raw metric path directly as plain text - headers = {'Content-Type': 'text/plain'} - async with session.post(api_url, data=message, headers=headers) as response: - if response.status != 200: - print(f"Failed to send data: {response.status}", await response.text()) - except Exception as e: - print(f"Error sending data: {e}") - -async def handle_serial(reader, api_url): - session = aiohttp.ClientSession() - try: - while True: - line = await reader.readline() - if not line: - break - line = line.decode('utf-8') - print(line.strip()) # echo output for received line - graphite_data = parse_to_graphite(line) - if graphite_data: - await send_to_graphite(graphite_data, session, api_url) - finally: - await session.close() - -async def main(): - if len(sys.argv) < 3: - print("Usage: python script.py ") - sys.exit(1) - - serial_device = sys.argv[1] - api_url = sys.argv[2] - baud_rate = 115200 # You can modify this as needed - - # Creating the connection to the serial port - reader, _ = await serial_asyncio.open_serial_connection(url=serial_device, baudrate=baud_rate) - await handle_serial(reader, api_url) - -if __name__ == '__main__': - asyncio.run(main()) diff --git a/ca_cert.h b/ca_cert.h new file mode 100644 index 0000000..c77eccc --- /dev/null +++ b/ca_cert.h @@ -0,0 +1,23 @@ +#ifndef CA_CERT_H +#define CA_CERT_H +const char* ca_cert = \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIDHTCCAgWgAwIBAgIUWLAb5lCXs4G6QxCaV78EtsRQgkEwDQYJKoZIhvcNAQEL\n" \ + "BQAwHTEbMBkGA1UEAwwSYXBpLmVjb21vdHVzLmNvLnVrMCAXDTIyMTAxOTE1Mjgx\n" \ + "N1oYDzIxMjIwOTI1MTUyODE3WjAdMRswGQYDVQQDDBJhcGkuZWNvbW90dXMuY28u\n" \ + "dWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwXfttIwX1y1tSeaiZ\n" \ + "0LtnQ3q9xosglFsXyoLcctJmOf+zgdHbNNxMH8CRbm4Z3ZQ4ghBoL/1RHaERl5aA\n" \ + "U7oVxr4MPHn6fWsYrLlaXLIcmL6ZS91woTKLejhf6D991sH2Jt0xVDhqerimnF4p\n" \ + "Ut1U6rY6Lw7aUAUUldChhzRUAkAcMHApWwzxElAM+KFFleLq63AESkT21xYOO+WG\n" \ + "2hLTQB+hDcBvN9IQ4Ud1V7AQ/MDKzVvJsn/z+KnslbH246l1w6haJk230UbPizau\n" \ + "fAWl63O2/xIxPJzWBXJeUuvi+lTqCf+ZVPBU6chpyL4xX+I7vCTBoKmpaI+qAF9F\n" \ + "2C4HAgMBAAGjUzBRMB0GA1UdDgQWBBTtxYrL2Cg9PVp4wx6MllB/cKXXYjAfBgNV\n" \ + "HSMEGDAWgBTtxYrL2Cg9PVp4wx6MllB/cKXXYjAPBgNVHRMBAf8EBTADAQH/MA0G\n" \ + "CSqGSIb3DQEBCwUAA4IBAQBAai8ewCT3Q2CgBMxvDLKQx7YRBNlv1gbUtYq88rvK\n" \ + "iz6yzGmbPP1Ax5LCv0oRtRdnrz0h2F80tBibS2mJ2tqsLd3277yMN81mHB0qVIrR\n" \ + "tq9aTzjGHUXgXmcezEgkTLTfISebvCB8jdR7cjvFUaTUKH3MLR3jNAAqU6WLVY6Q\n" \ + "wCYLKRhTU+aYkDeObOu2fsoph8FwR9gB9D4K0/W78UTiOQxLFJmCqubooNtGLrph\n" \ + "dz1hmIkYSKH3pdhE3kZwNilYVjyfxq3UFkh2/2J0Fz7vB7eaJE6PptcPJ2KgxTMO\n" \ + "i7QEQ+jNru8B20F4DrbvEa0IY5wv9mywugBsXg5rcfjs\n" \ + "-----END CERTIFICATE-----\n"; +#endif diff --git a/victron.ino b/victron.ino index ab856f5..c477990 100644 --- a/victron.ino +++ b/victron.ino @@ -185,12 +185,12 @@ void decodeVictron(BLEAdvertisedDevice advertisedDevice) { return; } - Serial.printf(">>>RC.MPPT.1.Battery_Volts %f\r\n",batteryVoltage); - Serial.printf(">>>RC.MPPT.1.Battery_Amps %f\r\n",batteryCurrent); - Serial.printf(">>>RC.MPPT.1.Battery_Watts %f\r\n",batteryVoltage*batteryCurrent); - Serial.printf(">>>RC.MPPT.1.Solar_Watts %f\r\n",inputPower); - Serial.printf(">>>RC.MPPT.1.Output_Current %f\r\n",outputCurrent); - Serial.printf(">>>RC.MPPT.1.Yield %f\r\n",todayYield); - Serial.printf(">>>RC.MPPT.1.State %d\r\n",deviceState); + wsManager.sendText("test.RC.MPPT.1.Battery_Volts %f",batteryVoltage); + wsManager.sendText("test.RC.MPPT.1.Battery_Amps %f",batteryCurrent); + wsManager.sendText("test.RC.MPPT.1.Battery_Watts %f",batteryVoltage*batteryCurrent); + wsManager.sendText("test.RC.MPPT.1.Solar_Watts %f",inputPower); + wsManager.sendText("test.RC.MPPT.1.Output_Current %f",outputCurrent); + wsManager.sendText("test.RC.MPPT.1.Yield %f",todayYield); + wsManager.sendText("test.RC.MPPT.1.State %d",deviceState); } }