From b2bcc7024d3295d145f3f440d7553b68192ebf61 Mon Sep 17 00:00:00 2001 From: no name Date: Tue, 5 Sep 2023 20:52:24 +0200 Subject: [PATCH] ESP8266 & ESP32; optimized rf24 rx for HM-300 --- src/CHANGES.md | 6 +++-- src/app.cpp | 29 ++++++++++++++++++-- src/config/config.h | 10 ++++++- src/defines.h | 1 - src/hm/hmPayload.h | 5 ++-- src/hm/hmRadio.h | 14 ++-------- src/hm/hmSystem.h | 38 ++++++++++++++++++++++++-- src/hm/miPayload.h | 2 +- src/platformio.ini | 20 +++++++++++--- src/plugins/SML_OBIS_Parser.cpp | 48 ++++++++++++++++++++++++++++----- src/plugins/SML_OBIS_Parser.h | 2 +- 11 files changed, 142 insertions(+), 33 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index ff7fd956..52048f8c 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -6,13 +6,15 @@ Changelog for ahoy-all-in-one compared to 0.6.9 of the main project - show max solar ac/dc power - improved radio retransmit (complete retransmit if nothing was received, but only when inverter ought to be active) for the hm series) - Heuristic for choosing the best send channel (of 5 possible) helps reducing retransmits +- optimized receiving handler for HM Inverters with 1 DC input (reduced frequency hopping and reduced answer timeout) - shortcut radio traces a little bit DRAWBACKS: -- MQTT Source is commented out (except 1 var which is used for other purpose as well) -- only up to 2 Inverters are supported (was: 10) +- ESP8266: MQTT Source is commented out (except 1 var which is used for other purpose as well) +- ESP8266: only up to 2 Inverters are supported (was: 10), for ESP32: 16 - RX/TX of UART0 is used for serial interface to IR sensor. Of course you cannot operate a display that uses RX/TX pins of UART0, simultaneously. And unplug serial connection bevor updating via USB (see also below) - Due to a non-matching licence model of the charting lib certain parts of visualization.html are commented out. See comments there. +- reduced platforms: only 8266 and ESP32 S3 (opendtufusionv1) currently But: Currently there is enough heap available for stable operation on a ESP8266 plattform (WEMOS D1 R1). So adjust the number of inverters and enable MQTT to your needs and see if the AHOY-DTU is still stable in operation with your hw plattform. To update firmware via USB, unplug serial connection to IR sensor first. Surprisingly during normal operation it seems that one can use a fully connected USB cable (for power supply). But I'm not sure if this works for all hardware plattforms. diff --git a/src/app.cpp b/src/app.cpp index c1519bfc..daaad790 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -22,7 +22,11 @@ void app::setup() { Electricity meter sends SML telegrams via IR interface (9600,8,n,1) without being asked (typical behaviour). An IR sensor is connected to the UART0 of AHOY DTU. Connected pins: GND-GND, 3V3-VCC, RX-RX, TX-TX. */ +#ifdef ESP32 + Serial.begin(9600, SERIAL_8N1, RX, -1); +#else Serial.begin(9600, SERIAL_8N1, SERIAL_RX_ONLY); +#endif #else Serial.begin(115200); #endif @@ -129,8 +133,8 @@ void app::loopStandard(void) { DBGPRINT(F(" | ")); mSys.Radio.dumpBuf(p->packet, p->len); #else - DPRINTLN(DBG_INFO, "RX (Ch " + String (p->ch) + "), Delay " + String (p->delay) + - " us, " + String (p->len) + " Bytes"); + DPRINTLN(DBG_INFO, "RX (Ch " + String (p->ch) + "), " + + String (p->len) + " Bytes"); #endif } @@ -512,9 +516,29 @@ void app::check_hist_file (File file) //----------------------------------------------------------------------------- void app::show_history (String path) { +#ifdef ESP32 + File dir = LittleFS.open (path); + File file; +#else Dir dir = LittleFS.openDir (path); +#endif DPRINTLN (DBG_INFO, "Enter Dir: " + path); +#ifdef ESP32 + if (dir) { + while ((file = dir.openNextFile())) { + if (file.isDirectory()) { + show_history ((char *)file.name()); + close (file); + } else { + DPRINTLN (DBG_INFO, "file " + String((char *)file.name()) + + ", Size: " + String (file.size())); + check_hist_file (file); // closes file + } + } + dir.close(); + } +#else while (dir.next()) { if (dir.isDirectory ()) { show_history (path + "/" + dir.fileName()); @@ -524,5 +548,6 @@ void app::show_history (String path) check_hist_file (dir.openFile ("r")); } } +#endif DPRINTLN (DBG_INFO, "Leave Dir: " + path); } diff --git a/src/config/config.h b/src/config/config.h index 1db31520..2159d540 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -72,7 +72,11 @@ #define PACKET_BUFFER_SIZE 30 // number of configurable inverters (increase if you need, and if there is enough heap available for stable operation) +#if defined(ESP32) +#define MAX_NUM_INVERTERS 16 +#else #define MAX_NUM_INVERTERS 2 +#endif // default serial interval #define SERIAL_INTERVAL 5 @@ -114,8 +118,12 @@ #define AHOY_CHARTDATA_WITH_GRID_HDR "Time, AC Power, Grid Power" #define AHOY_CHARTDATA_HDR "Time, AC Power" - +#if defined(ESP32) +#define AHOY_MQTT_SUPPORT +#else +// save RAM // #define AHOY_MQTT_SUPPORT +#endif // default mqtt interval #define MQTT_INTERVAL 90 diff --git a/src/defines.h b/src/defines.h index 14373a4b..42a6a4de 100644 --- a/src/defines.h +++ b/src/defines.h @@ -19,7 +19,6 @@ typedef struct { uint8_t ch; uint8_t len; - long delay; // micros since preceeding tx_fail irq and this rx_ready irq uint8_t packet[MAX_RF_PAYLOAD_SIZE]; } packet_t; diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index f94fdecf..7b6ae95c 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -248,13 +248,14 @@ class HmPayload { if (!mPayload[iv->id].complete) { bool crcPass, pyldComplete; - uint8 Fragments; + uint8_t Fragments; crcPass = build(iv->id, &pyldComplete, &Fragments); // evaluate quality of send channel with rcv params - if (retransmit) { + if (retransmit && mPayload[iv->id].requested && (mPayload[iv->id].retransmits < mMaxRetrans)) { iv->evalTxChanQuality (crcPass, mPayload[iv->id].retransmits, Fragments, mPayload[iv->id].lastFragments); + DPRINT_IVID(DBG_INFO, iv->id); DPRINT (DBG_INFO, "Quality: "); iv->dumpTxChanQuality(); DBGPRINTLN(""); diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index f14e8684..c791960b 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -202,10 +202,6 @@ class HmRadio { } void handleIntr(void) { - if (mRfIrqIndex < MAX_PAYLOAD_ENTRIES) { - // it is allowed to call micros() inside irq handler to read the time (but check for unexpected restart reasons) - mRfIrqTime[mRfIrqIndex++] = micros(); // remember Time of tx_fail and rx_readies - } mIrqRcvd = true; } @@ -345,9 +341,6 @@ class HmRadio { packet_t p; p.ch = mRxChannels[mRxChIdx]; p.len = len; - if (mRfIrqIndex) { - p.delay = mRfIrqTime[mRfIrqIndex - 1] - mRfIrqTime[0]; - } mNrf24.read(p.packet, len); if (p.packet[0] != 0x00) { mBufCtrl.push(p); @@ -416,7 +409,6 @@ class HmRadio { mNrf24.stopListening(); mNrf24.setChannel(rf24ChLst[mTxChIdx]); mNrf24.openWritingPipe(reinterpret_cast(&invId)); - mRfIrqIndex = 0; mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response if(isRetransmit) @@ -427,8 +419,6 @@ class HmRadio { volatile bool mIrqRcvd; uint64_t DTU_RADIO_ID; - volatile uint8_t mRfIrqIndex; - volatile long mRfIrqTime[1 + MAX_PAYLOAD_ENTRIES]; SPIClass* mSpi; RF24 mNrf24; @@ -437,8 +427,8 @@ class HmRadio { uint8_t mRxChIdx; // cur index in mRxChannels uint8_t *mRxChannels; // rx channel to be used; depends on inverter and previous tx channel uint8_t mMaxRxChannels; // actual size of mRxChannels; depends on inverter and previous tx channel - uint32 mRxAnswerTmo; // max wait time in millis for answers of inverter - uint32 mRxChanTmo; // max wait time in micros for a rx channel + uint32_t mRxAnswerTmo; // max wait time in millis for answers of inverter + uint32_t mRxChanTmo; // max wait time in micros for a rx channel }; #endif /*__RADIO_H__*/ diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index a92c3eef..551d4c7c 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -25,7 +25,7 @@ class HmSystem { Radio.setup(); } - void setup(uint32 *timestamp, uint8_t ampPwr, uint8_t irqPin, uint8_t cePin, uint8_t csPin, uint8_t sclkPin, uint8_t mosiPin, uint8_t misoPin) { + void setup(uint32_t *timestamp, uint8_t ampPwr, uint8_t irqPin, uint8_t cePin, uint8_t csPin, uint8_t sclkPin, uint8_t mosiPin, uint8_t misoPin) { mTimestamp = timestamp; mNumInv = 0; Radio.setup(ampPwr, irqPin, cePin, csPin, sclkPin, mosiPin, misoPin); @@ -147,15 +147,48 @@ class HmSystem { } if ((time_today = *mTimestamp)) { +#ifdef ESP32 + File ac_power_dir; + File file; +#else Dir ac_power_dir; +#endif char cur_file_name[sizeof (AC_FORMAT_FILE_NAME)]; time_today = gTimezone.toLocal (time_today); snprintf (cur_file_name, sizeof (cur_file_name), AC_FORMAT_FILE_NAME, day(time_today), month(time_today), year(time_today)); - ac_power_dir = LittleFS.openDir (AC_POWER_PATH); + /* design: no dataserver, cleanup old history */ +#ifdef ESP32 + if ((ac_power_dir = LittleFS.open (AC_POWER_PATH))) { + while ((file = ac_power_dir.openNextFile())) { + const char *fullName = file.name(); + char *name; + + if ((name = strrchr (fullName, '/')) && name[1]) { + name++; + } else { + name = (char *)fullName; + } + if (strcmp (name, cur_file_name)) { + char *path = strdup (fullName); + file.close (); + if (path) { + DPRINTLN (DBG_INFO, "Remove file " + String (name) + + ", Size: " + String (ac_power_dir.size())); + LittleFS.remove (path); + free (path); + } + } else { + file.close(); + } + } + ac_power_dir.close(); + } +#else + ac_power_dir = LittleFS.openDir (AC_POWER_PATH); while (ac_power_dir.next()) { if (ac_power_dir.fileName() != cur_file_name) { DPRINTLN (DBG_INFO, "Remove file " + ac_power_dir.fileName() + @@ -163,6 +196,7 @@ class HmSystem { LittleFS.remove (AC_POWER_PATH "/" + ac_power_dir.fileName()); } } +#endif } else { DPRINTLN (DBG_WARN, "cleanup_history, no time yet"); } diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 2ba5467b..371e3076 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -435,7 +435,7 @@ const byteAssign_t InfoAssignment[] = { crcPass = build(iv->id, &pyldComplete); // evaluate quality of send channel with rcv params - if (retransmit) { + if (retransmit && mPayload[iv->id].requested && (mPayload[iv->id].retransmits < mMaxRetrans)) { iv->evalTxChanQuality (crcPass, mPayload[iv->id].retransmits, mPayload[iv->id].fragments, mPayload[iv->id].lastFragments); DPRINT (DBG_INFO, "Quality: "); diff --git a/src/platformio.ini b/src/platformio.ini index a9756a5e..c011a448 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -13,9 +13,6 @@ src_dir = . include_dir = . [env] -board = d1_mini -board_build.f_cpu = 80000000L -platform=https://github.com/platformio/platform-espressif8266.git@^4.2.0 framework = arduino board_build.filesystem = littlefs upload_speed = 921600 @@ -34,5 +31,22 @@ lib_deps = zinggjm/GxEPD2 @ ^1.5.0 [env:esp8266-release] +platform = espressif8266 +board = esp12e +board_build.f_cpu = 80000000L build_flags = -D RELEASE monitor_filters = esp8266_exception_decoder + +[env:opendtufusionv1-release] +platform = espressif32@>=6.1.0 +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +upload_speed = 115200 +debug_tool = esp-builtin +debug_speed = 12000 +build_flags = -D RELEASE -std=gnu++14 +build_unflags = -std=gnu++11 +monitor_filters = + ;default ; Remove typical terminal control codes from input + time ; Add timestamp with milliseconds for each new line + ;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory diff --git a/src/plugins/SML_OBIS_Parser.cpp b/src/plugins/SML_OBIS_Parser.cpp index 75c37913..7041b762 100644 --- a/src/plugins/SML_OBIS_Parser.cpp +++ b/src/plugins/SML_OBIS_Parser.cpp @@ -1,7 +1,9 @@ #include #include #include +#ifndef ESP32 #include +#endif #include "../utils/dbg.h" #include "../utils/scheduler.h" #include "../config/settings.h" @@ -86,9 +88,9 @@ static uint16_t cur_sml_list_layer; static unsigned char sml_list_layer_entries [SML_MAX_LIST_LAYER]; static unsigned char sml_serial_buf[SML_MAX_SERIAL_BUF]; static unsigned char *cur_serial_buf = sml_serial_buf; -static uint16 sml_serial_len = 0; -static uint16 sml_skip_len = 0; -static uint32 sml_message = SML_MSG_NONE; +static uint16_t sml_serial_len = 0; +static uint16_t sml_skip_len = 0; +static uint32_t sml_message = SML_MSG_NONE; static obis_state_t obis_state = OBIS_ST_NONE; static int obis_power_all_scale, obis_power_all_value; /* design: max 16 bit fuer aktuelle Powerwerte */ @@ -306,14 +308,47 @@ void sml_cleanup_history () obis_cur_pac_index = 0; obis_pac_sum = 0; if ((time_today = *obis_timestamp)) { +#ifdef ESP32 + File grid_power_dir; + File file; +#else Dir grid_power_dir; +#endif char cur_file_name[sizeof (SML_OBIS_FORMAT_FILE_NAME)]; time_today = gTimezone.toLocal (time_today); snprintf (cur_file_name, sizeof (cur_file_name), SML_OBIS_FORMAT_FILE_NAME, day(time_today), month (time_today), year (time_today)); - grid_power_dir = LittleFS.openDir (SML_OBIS_GRID_POWER_PATH); /* design: no dataserver, cleanup old history */ +#ifdef ESP32 + grid_power_dir = LittleFS.open (SML_OBIS_GRID_POWER_PATH); + if (grid_power_dir) { + while ((file = grid_power_dir.openNextFile())) { + const char *fullName = file.name(); + char *name; + + if ((name = strrchr (fullName, '/')) && name[1]) { + name++; + } else { + name = (char *)fullName; + } + if (strcmp (name, cur_file_name)) { + char *path = strdup (fullName); + file.close (); + if (path) { + DPRINTLN (DBG_INFO, "Remove file " + String(name) + + ", Size: " + String (grid_power_dir.size())); + LittleFS.remove (path); + free (path); + } + } else { + file.close(); + } + } + grid_power_dir.close(); + } +#else + grid_power_dir = LittleFS.openDir (SML_OBIS_GRID_POWER_PATH); while (grid_power_dir.next()) { if (grid_power_dir.fileName() != cur_file_name) { @@ -322,6 +357,7 @@ void sml_cleanup_history () LittleFS.remove (SML_OBIS_GRID_POWER_PATH "/" + grid_power_dir.fileName()); } } +#endif } else { DPRINTLN (DBG_WARN, "sml_cleanup_history, no time yet"); } @@ -627,7 +663,7 @@ bool sml_get_list_entries (uint16_t layer) unsigned char entry_len; // Acc. to Spec there might be len_info > 2. But does this happen in real life? // Also: an info_len > 2 could be due to corrupt data. So better break. - uint16 len_info = (*cur_serial_buf & SML_EXT_LENGTH) ? 2 : 1; + uint16_t len_info = (*cur_serial_buf & SML_EXT_LENGTH) ? 2 : 1; if (sml_serial_len < len_info) { if (cur_serial_buf > sml_serial_buf) { @@ -795,7 +831,7 @@ bool sml_get_list_entries (uint16_t layer) } //----------------------------------------------------------------------------- -uint16_t sml_parse_stream (uint16 len) +uint16_t sml_parse_stream (uint16_t len) { bool parse_continue; uint16_t serial_read; diff --git a/src/plugins/SML_OBIS_Parser.h b/src/plugins/SML_OBIS_Parser.h index cb255f75..c7321dbc 100644 --- a/src/plugins/SML_OBIS_Parser.h +++ b/src/plugins/SML_OBIS_Parser.h @@ -13,7 +13,7 @@ void sml_close_hist (File file); int sml_find_hist_power (File file, uint16_t index); int16_t sml_get_obis_pac (); int16_t sml_get_obis_pac_average (); -uint16_t sml_parse_stream (uint16 len); +uint16_t sml_parse_stream (uint16_t len); void sml_set_trace_obis (bool trace_flag); void sml_loop();