From 27a4ea0f702daf179e8d928a0433ca6836c55941 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:05:16 +0200 Subject: [PATCH] Add Support for BL0906 up to 6 channel energy monitor as used in Athom EM2/EM6 (#22167) --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/include/tasmota_template.h | 11 +- .../tasmota_xnrg_energy/xnrg_24_bl0906.ino | 523 ++++++++++++++++++ 4 files changed, 532 insertions(+), 4 deletions(-) create mode 100644 tasmota/tasmota_xnrg_energy/xnrg_24_bl0906.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index b726578e6..d6b54a6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - ESP8266 support for I2C CLK on GPIO16 (#22199) - ESP8266 support for one-wire M1601 temperature sensor on DS18x20 GPIO (#21376) +- Support for BL0906 up to 6 channel energy monitor as used in Athom EM2/EM6 (#22167) ### Breaking Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 62f9a8fa3..44433d5eb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -127,6 +127,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - ESP8266 support for I2C CLK on GPIO16 [#22199](https://github.com/arendst/Tasmota/issues/22199) - Support for I2C M5Unit (Mini)Scales using HX711 driver - Support for RX8010 RTC as used in IOTTIMER [#21376](https://github.com/arendst/Tasmota/issues/21376) +- Support for BL0906 up to 6 channel energy monitor as used in Athom EM2/EM6 [#22167](https://github.com/arendst/Tasmota/issues/22167) - Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage - Energy command ``CurrentSet 60,230`` to calibrate both Power and Current with known resistive load of 60W at 230V using calibrated Voltage - MQTT warning if trying to connect without TLS on a port that normally uses TLS [#22175](https://github.com/arendst/Tasmota/issues/22175) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index f6b9c846b..0bcb2adfd 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -519,6 +519,7 @@ const char kSensorNamesFixed[] PROGMEM = #define MAX_BP1658CJ_DAT 16 #define MAX_DINGTIAN_SHIFT 4 #define MAX_MAGIC_SWITCH_MODES 2 +#define MAX_BL0906_RX 6 // Model number of phases, 2 (EM2), 6 (EM6) #define MAX_BL0942_RX 8 // Baudrates 1/5 (4800), 2/6 (9600), 3/7 (19200), 4/8 (38400), Support Positive values only 1..4, Support also negative values 5..8 #define MAX_CSE7761 2 // Model 1/2 (DUALR3), 2/2 (POWCT) @@ -946,14 +947,16 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SOLAXX1_TX), // Solax Inverter tx pin AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin AGPIO(GPIO_SOLAXX1_RTS), // Solax Inverter RTS pin -#endif // USE_SOLAX_X1 +#endif // USE_SOLAX_X1 #ifdef USE_LE01MR AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin -#endif // USE_LE01MR +#endif // USE_LE01MR +#ifdef ESP32 #ifdef USE_BL0906 - AGPIO(GPIO_BL0906_RX), // BL0906 Serial interface (Athom EM6) -#endif // USE_BL0906 + AGPIO(GPIO_BL0906_RX) + MAX_BL0906_RX, // BL0906 Serial interface (Athom EM6) +#endif // USE_BL0906 +#endif // ESP32 #if defined(USE_BL0940) || defined(USE_BL09XX) AGPIO(GPIO_BL0939_RX), // BL0939 Serial interface (Dual R3 v2) AGPIO(GPIO_BL0940_RX), // BL0940 Serial interface diff --git a/tasmota/tasmota_xnrg_energy/xnrg_24_bl0906.ino b/tasmota/tasmota_xnrg_energy/xnrg_24_bl0906.ino new file mode 100644 index 000000000..bf2222e99 --- /dev/null +++ b/tasmota/tasmota_xnrg_energy/xnrg_24_bl0906.ino @@ -0,0 +1,523 @@ +/* + xnrg_24_bl0906.ino - BL0906 energy sensor support for Tasmota + + SPDX-FileCopyrightText: 2024 Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef ESP32 +#ifdef USE_ENERGY_SENSOR +#ifdef USE_BL0906 +/*********************************************************************************************\ + * Support the following Shangai Belling energy sensors: + * + * BL0906 - Energy (as in Athom 6CH Energy Meter EM6) + * Based on athom-tech https://github.com/athom-tech/esp32-configs/tree/main/components/bl0906 + * See https://github.com/arendst/Tasmota/discussions/22167 + * + * {"NAME":"Athom EM2","GPIO":[0,0,0,0,0,0,0,3200,11329,1,544,0,0,0,0,0,0,0,1,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Athom EM6","GPIO":[0,0,0,0,0,0,0,3200,11333,1,544,0,0,0,0,0,0,0,1,0,0,0],"FLAG":0,"BASE":1} + * + * Optional commands: + * EnergyCols ` - Change default 4 column GUI display to columns + * VoltRes 1 - Change none to 1 decimal display + * WattRes 2 - Change none to 2 decimals display + * SetOption129 1 - Display energy for each phase instead of single sum + * SetOption150 1 - Display no common voltage/frequency + \*********************************************************************************************/ + +#define XNRG_24 24 + +#ifndef BL0906_UPDATE +#define BL0906_UPDATE 5 // Update every 5 seconds +#endif +//#define DEBUG_BL0906 + +// Total power conversion +static const float BL0906_WATT = 16 * 1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / + (40.41259 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000); +// Total Energy conversion +static const float BL0906_CF = 16 * 4194304 * 0.032768 * 16 / + (3600000 * 16 * + (40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / + (1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000)))); +// Frequency conversion +static const float BL0906_FREF = 10000000; +// Temperature conversion +static const float BL0906_TREF = 12.5 / 59 - 40; // Celsius +// Current conversion +static const float BL0906_IREF = 1.097 / (12875 * 1 * (5.1 + 5.1) * 1000 / 2000); +// Voltage conversion +static const float BL0906_UREF = 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / (13162 * 1 * 100 * 1000); +// Power conversion +static const float BL0906_PREF = 1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / + (40.41259 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000); +// Energy conversion +static const float BL0906_EREF = 4194304 * 0.032768 * 16 / + (3600000 * 16 * + (40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / + (1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000)))); +// Current coefficient +static const float BL0906_KI = 12875 * 1 * (5.1 + 5.1) * 1000 / 2000 / 1.097; +// Power coefficient +static const float BL0906_KP = 40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / 1.097 / 1.097 / + (20000 + 20000 + 20000 + 20000 + 20000); + +// Register address +// Voltage +static const uint8_t BL0906_V_RMS = 0x16; + +// Total power +static const uint8_t BL0906_WATT_SUM = 0X2C; + +// Current1~6 +static const uint8_t BL0906_I_1_RMS = 0x0D; // current_1 +static const uint8_t BL0906_I_2_RMS = 0x0E; +static const uint8_t BL0906_I_3_RMS = 0x0F; +static const uint8_t BL0906_I_4_RMS = 0x10; +static const uint8_t BL0906_I_5_RMS = 0x13; +static const uint8_t BL0906_I_6_RMS = 0x14; // current_6 + +// Power1~6 +static const uint8_t BL0906_WATT_1 = 0X23; // power_1 +static const uint8_t BL0906_WATT_2 = 0X24; +static const uint8_t BL0906_WATT_3 = 0X25; +static const uint8_t BL0906_WATT_4 = 0X26; +static const uint8_t BL0906_WATT_5 = 0X29; +static const uint8_t BL0906_WATT_6 = 0X2A; // power_6 + +// Active pulse count, unsigned +static const uint8_t BL0906_CF_1_CNT = 0X30; // Channel_1 +static const uint8_t BL0906_CF_2_CNT = 0X31; +static const uint8_t BL0906_CF_3_CNT = 0X32; +static const uint8_t BL0906_CF_4_CNT = 0X33; +static const uint8_t BL0906_CF_5_CNT = 0X36; +static const uint8_t BL0906_CF_6_CNT = 0X37; // Channel_6 + +// Total active pulse count, unsigned +static const uint8_t BL0906_CF_SUM_CNT = 0X39; + +// Voltage frequency cycle +static const uint8_t BL0906_FREQUENCY = 0X4E; + +// Internal temperature +static const uint8_t BL0906_TEMPERATURE = 0X5E; + +// Calibration register +// RMS gain adjustment register +static const uint8_t BL0906_RMSGN_1 = 0x6D; // Channel_1 +static const uint8_t BL0906_RMSGN_2 = 0x6E; +static const uint8_t BL0906_RMSGN_3 = 0x6F; +static const uint8_t BL0906_RMSGN_4 = 0x70; +static const uint8_t BL0906_RMSGN_5 = 0x73; +static const uint8_t BL0906_RMSGN_6 = 0x74; // Channel_6 + +// RMS offset correction register +static const uint8_t BL0906_RMSOS_1 = 0x78; // Channel_1 +static const uint8_t BL0906_RMSOS_2 = 0x79; +static const uint8_t BL0906_RMSOS_3 = 0x7A; +static const uint8_t BL0906_RMSOS_4 = 0x7B; +static const uint8_t BL0906_RMSOS_5 = 0x7E; +static const uint8_t BL0906_RMSOS_6 = 0x7F; // Channel_6 + +// Active power gain adjustment register +static const uint8_t BL0906_WATTGN_1 = 0xB7; // Channel_1 +static const uint8_t BL0906_WATTGN_2 = 0xB8; +static const uint8_t BL0906_WATTGN_3 = 0xB9; +static const uint8_t BL0906_WATTGN_4 = 0xBA; +static const uint8_t BL0906_WATTGN_5 = 0xBD; +static const uint8_t BL0906_WATTGN_6 = 0xBE; // Channel_6 + +// Commands +static const uint8_t BL0906_READ_COMMAND = 0x35; +static const uint8_t BL0906_WRITE_COMMAND = 0xCA; + +// User write protection setting register, +// You must first write 0x5555 to the write protection setting register before writing to other registers. +static const uint8_t BL0906_USR_WRPROT = 0x9E; +// Enable User Operation Write +static const uint8_t BL0906_WRPROT_WRITABLE[6] = {BL0906_WRITE_COMMAND, BL0906_USR_WRPROT, 0x55, 0x55, 0x00, 0xB7}; +// Disable User Operation Write +static const uint8_t BL0906_WRPROT_ONLYREAD[6] = {BL0906_WRITE_COMMAND, BL0906_USR_WRPROT, 0x00, 0x00, 0x00, 0x61}; + +// Reset Register +static const uint8_t BL0906_SOFT_RESET = 0x9F; +// Reset to default +static const uint8_t BL0906_INIT[6] = {BL0906_WRITE_COMMAND, BL0906_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x52}; + +typedef struct Bl0906DataPacket { + uint8_t l{0}; + uint8_t m{0}; + uint8_t h{0}; + uint8_t checksum; + uint8_t address; +} Bl0906DataPacket; + +typedef struct Bl0906ube24_t { + uint8_t l{0}; + uint8_t m{0}; + uint8_t h{0}; +} Bl0906ube24_t; + +typedef struct Bl0906sbe24_t { + uint8_t l{0}; + uint8_t m{0}; + int8_t h{0}; +} Bl0906sbe24_t; + +#include +TasmotaSerial *Bl0906Serial = nullptr; + +struct BL0906 { + float temperature; + uint16_t baudrate; + uint8_t current_channel = 0; + uint8_t model = 0; + uint8_t rx_pin; +} Bl0906; + +/********************************************************************************************/ + +bool Bl0906_check_read_timeout(size_t len) { + if (Bl0906Serial->available() >= int(len)) { + return true; + } + uint32_t start_time = millis(); + while (Bl0906Serial->available() < int(len)) { + if (millis() - start_time > 100) { +#ifdef DEBUG_BL0906 + AddLog(LOG_LEVEL_DEBUG, PSTR("BL6: Timeout at %u/%u"), Bl0906Serial->available(), len); +#endif // DEBUG_BL0906 + return false; + } + yield(); + } + return true; +} + +bool Bl0906ReadArray(uint8_t *data, size_t len) { + if (!Bl0906_check_read_timeout(len)) { + return false; + } + Bl0906Serial->read(data, len); + return true; +} + +/********************************************************************************************/ + +uint32_t Bl0906_to_uint32_t(Bl0906ube24_t input); +uint32_t Bl0906_to_uint32_t(Bl0906ube24_t input) { + return input.h << 16 | input.m << 8 | input.l; +} + +int32_t Bl0906_to_int32_t(Bl0906sbe24_t input); +int32_t Bl0906_to_int32_t(Bl0906sbe24_t input) { + return input.h << 16 | input.m << 8 | input.l; +} + +/********************************************************************************************/ + +// The SUM byte is (Addr+Data_L+Data_M+Data_H)&0xFF negated; +uint8_t Bl0906Checksum(const uint8_t address, const Bl0906DataPacket *data); // Pre-declare to fix compile error on Bl0906DataPacket +uint8_t Bl0906Checksum(const uint8_t address, const Bl0906DataPacket *data) { + return (address + data->l + data->m + data->h) ^ 0xFF; +} + +// RMS offset correction +void Bl0906BiasCorrection(uint8_t address, float measurements, float correction) { + Bl0906DataPacket data; + float ki = 12875 * 1 * (5.1 + 5.1) * 1000 / 2000 / 1.097; // Current coefficient + float i_rms0 = measurements * ki; + float i_rms = correction * ki; + int32_t value = (i_rms * i_rms - i_rms0 * i_rms0) / 256; + data.l = value << 24 >> 24; + data.m = value << 16 >> 24; + if (value < 0) { + data.h = (value << 8 >> 24) | 0b10000000; + } + data.address = Bl0906Checksum(address, &data); +#ifdef DEBUG_BL0906 + AddLog(LOG_LEVEL_DEBUG, PSTR("BL6: BiasCorrection %02X %02X %02X %02X %02X %02X"), BL0906_WRITE_COMMAND, address, data.l, data.m, data.h, data.address); +#endif // DEBUG_BL0906 + Bl0906Serial->write(BL0906_WRITE_COMMAND); + Bl0906Serial->write(address); + Bl0906Serial->write(data.l); + Bl0906Serial->write(data.m); + Bl0906Serial->write(data.h); + Bl0906Serial->write(data.address); +} +/* +// Gain adjustment +void Bl0906GainCorrection(const uint8_t address, const float measurements, const float Correction, const float coefficient) { + Bl0906DataPacket data; + float I_RMS0 = measurements * coefficient; + float I_RMS = Correction * coefficient; + float rms_gn = int((I_RMS / I_RMS0 - 1) * 65536); + int16_t value; + if (rms_gn <= -32767) { + value = -32767; + } else { + value = int(rms_gn); + } + data.h = 0xFF; + data.m = value >> 8; + data.l = value << 8 >> 8; + data.address = Bl0906Checksum(address, &data); +#ifdef DEBUG_BL0906 + AddLog(LOG_LEVEL_DEBUG, PSTR("BL6: GainCorrection %02X %02X %02X %02X %02X %02X"), BL0906_WRITE_COMMAND, address, data.l, data.m, data.h, data.address); +#endif // DEBUG_BL0906 + Bl0906Serial->write(BL0906_WRITE_COMMAND); + Bl0906Serial->write(address); + Bl0906Serial->write(data.l); + Bl0906Serial->write(data.m); + Bl0906Serial->write(data.h); + Bl0906Serial->write(data.address); +} +*/ +void Bl0906Setup(void) { + while (Bl0906Serial->available()) { + Bl0906Serial->flush(); + } + Bl0906Serial->write(BL0906_WRPROT_WRITABLE, sizeof(BL0906_WRPROT_WRITABLE)); + // Calibration (1: register address; 2: value before calibration; 3: value after calibration) + Bl0906BiasCorrection(BL0906_RMSOS_1, 0.01600, 0); // Calibration current_1 + Bl0906BiasCorrection(BL0906_RMSOS_2, 0.01500, 0); + Bl0906BiasCorrection(BL0906_RMSOS_3, 0.01400, 0); + Bl0906BiasCorrection(BL0906_RMSOS_4, 0.01300, 0); + Bl0906BiasCorrection(BL0906_RMSOS_5, 0.01200, 0); + Bl0906BiasCorrection(BL0906_RMSOS_6, 0.01200, 0); // Calibration current_6 +/* + Bl0906GainCorrection(BL0906_RMSGN_1, 2.15000, 2.148, BL0906_KI); //RMS gain adjustment current_1 + Bl0906GainCorrection(BL0906_RMSGN_2, 2.15100, 2.148, BL0906_KI); + Bl0906GainCorrection(BL0906_RMSGN_3, 2.15200, 2.148, BL0906_KI); + Bl0906GainCorrection(BL0906_RMSGN_4, 2.14500, 2.148, BL0906_KI); + Bl0906GainCorrection(BL0906_RMSGN_5, 2.14600, 2.148, BL0906_KI); + Bl0906GainCorrection(BL0906_RMSGN_6, 2.14600, 2.148, BL0906_KI); //RMS gain adjustment current_6 + + Bl0906GainCorrection(BL0906_WATTGN_1, 15.13427, 14.5, BL0906_KP); //Active power gain adjustment power_1 + Bl0906GainCorrection(BL0906_WATTGN_2, 15.23937, 14.5, BL0906_KP); + Bl0906GainCorrection(BL0906_WATTGN_3, 15.44956, 14.5, BL0906_KP); + Bl0906GainCorrection(BL0906_WATTGN_4, 16.57646, 14.5, BL0906_KP); + Bl0906GainCorrection(BL0906_WATTGN_5, 15.27440, 14.5, BL0906_KP); + Bl0906GainCorrection(BL0906_WATTGN_6, 31.75744, 14.5, BL0906_KP); //Active power gain adjustment power_6 +*/ + Bl0906Serial->write(BL0906_WRPROT_ONLYREAD, sizeof(BL0906_WRPROT_ONLYREAD)); +} + +// Reset energy +void Bl0906ResetEnergy(void) { + Bl0906Serial->write(BL0906_INIT, sizeof(BL0906_INIT)); + delay(1); + Bl0906Serial->flush(); // Flush send buffer +} + +// Read data +void Bl0906ReadData(const uint8_t address, const float reference, float *sensor) { + if (sensor == nullptr) { + return; + } + + Bl0906Serial->write(BL0906_READ_COMMAND); + Bl0906Serial->write(address); + Bl0906DataPacket buffer; + if (Bl0906ReadArray((uint8_t *) &buffer, sizeof(buffer) - 1)) { + if (Bl0906Checksum(address, &buffer) == buffer.checksum) { + + Bl0906ube24_t data_u24; + Bl0906sbe24_t data_s24; + bool signed_result = reference == BL0906_TREF || reference == BL0906_WATT || reference == BL0906_PREF; + if (signed_result) { + data_s24.l = buffer.l; + data_s24.m = buffer.m; + data_s24.h = buffer.h; + } else { + data_u24.l = buffer.l; + data_u24.m = buffer.m; + data_u24.h = buffer.h; + } + + float value = 0; + // Power + if (reference == BL0906_PREF) { + value = (float) Bl0906_to_int32_t(data_s24) * reference; + } + // Total power + if (reference == BL0906_WATT) { + value = (float) Bl0906_to_int32_t(data_s24) * reference; + } + // Voltage, current, power, total power + if (reference == BL0906_UREF || reference == BL0906_IREF || reference == BL0906_EREF || reference == BL0906_CF) { + value = (float) Bl0906_to_uint32_t(data_u24) * reference; + } + // Frequency + if (reference == BL0906_FREF) { + value = reference / (float) Bl0906_to_uint32_t(data_u24); + } + // Chip temperature + if (reference == BL0906_TREF) { + value = (float) Bl0906_to_int32_t(data_s24); + value = (value - 64) * 12.5 / 59 - 40; // Celsius +// value = (value - 64) * reference; + value = ConvertTemp(value); + } + *sensor = value; + } else { + AddLog(LOG_LEVEL_DEBUG, PSTR("BL6: CRC error")); + while (Bl0906Serial->read() >= 0); // Flush receive buffer + } + } +} + +/********************************************************************************************/ + +void Bl0906Loop(void) { + if (UINT8_MAX == Bl0906.current_channel) { + return; + } + + while (Bl0906Serial->available()) + Bl0906Serial->flush(); + + if (0 == Bl0906.current_channel) { + // Temperature + Bl0906ReadData(BL0906_TEMPERATURE, BL0906_TREF, &Bl0906.temperature); + } else if (1 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_1_RMS, BL0906_IREF, &Energy->current[0]); + Bl0906ReadData(BL0906_WATT_1, BL0906_PREF, &Energy->active_power[0]); + Bl0906ReadData(BL0906_CF_1_CNT, BL0906_EREF, &Energy->import_active[0]); + } else if (2 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_2_RMS, BL0906_IREF, &Energy->current[1]); + Bl0906ReadData(BL0906_WATT_2, BL0906_PREF, &Energy->active_power[1]); + Bl0906ReadData(BL0906_CF_2_CNT, BL0906_EREF, &Energy->import_active[1]); + } else if (3 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_3_RMS, BL0906_IREF, &Energy->current[2]); + Bl0906ReadData(BL0906_WATT_3, BL0906_PREF, &Energy->active_power[2]); + Bl0906ReadData(BL0906_CF_3_CNT, BL0906_EREF, &Energy->import_active[2]); + } else if (4 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_4_RMS, BL0906_IREF, &Energy->current[3]); + Bl0906ReadData(BL0906_WATT_4, BL0906_PREF, &Energy->active_power[3]); + Bl0906ReadData(BL0906_CF_4_CNT, BL0906_EREF, &Energy->import_active[3]); + } else if (5 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_5_RMS, BL0906_IREF, &Energy->current[4]); + Bl0906ReadData(BL0906_WATT_5, BL0906_PREF, &Energy->active_power[4]); + Bl0906ReadData(BL0906_CF_5_CNT, BL0906_EREF, &Energy->import_active[4]); + } else if (6 == Bl0906.current_channel) { + Bl0906ReadData(BL0906_I_6_RMS, BL0906_IREF, &Energy->current[5]); + Bl0906ReadData(BL0906_WATT_6, BL0906_PREF, &Energy->active_power[5]); + Bl0906ReadData(BL0906_CF_6_CNT, BL0906_EREF, &Energy->import_active[5]); + } else if (8 == Bl0906.current_channel) { + // Frequency + Bl0906ReadData(BL0906_FREQUENCY, BL0906_FREF, &Energy->frequency[0]); + // Voltage + Bl0906ReadData(BL0906_V_RMS, BL0906_UREF, &Energy->voltage[0]); + } else if (9 == Bl0906.current_channel) { + // Total power +// Bl0906ReadData(BL0906_WATT_SUM, BL0906_WATT, this->total_power_sensor_); + // Total Energy +// Bl0906ReadData(BL0906_CF_SUM_CNT, BL0906_CF, &Energy->total[0]); + + EnergyUpdateTotal(); + } else { + Bl0906.current_channel = UINT8_MAX - 1; // Stop + } + if (Bl0906.current_channel == Energy->phase_count) { + Bl0906.current_channel = 7; // Skip next phases and go to frequency and voltage + } + + Bl0906.current_channel++; + +/* + while (Bl0906Serial->available()) { + Bl0906Serial->read(); + } +*/ + while (Bl0906Serial->read() >= 0); +} + +void Bl0906EverySecond(void) { + if (!(TasmotaGlobal.uptime % BL0906_UPDATE)) { // Every BL0906_UPDATE seconds an update + if (UINT8_MAX == Bl0906.current_channel) { + Bl0906.current_channel = 0; + } + } +} + +void Bl0906Show(bool json) { + if (json) { + ResponseAppend_P(JSON_SNS_F_TEMP, "BL0909", Settings->flag2.temperature_resolution, &Bl0906.temperature); + if (0 == TasmotaGlobal.tele_period) { +#ifdef USE_DOMOTICZ + DomoticzFloatSensor(DZ_TEMP, Bl0906.temperature); +#endif // USE_DOMOTICZ +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, Bl0906.temperature); +#endif // USE_KNX + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_Temp("", Bl0906.temperature); +#endif // USE_WEBSERVER + } +} + +void Bl0906Init(void) { + // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions + Bl0906Serial = new TasmotaSerial(Bl0906.rx_pin, Pin(GPIO_TXD), 1); + if (Bl0906Serial->begin(Bl0906.baudrate)) { + AddLog(LOG_LEVEL_DEBUG, PSTR("BL6: Serial UART%d"), Bl0906Serial->getUart()); + Bl0906Setup(); +// Bl0906ResetEnergy(); + } +} + +void Bl0906PreInit(void) { + if (PinUsed(GPIO_TXD) && PinUsed(GPIO_BL0906_RX, GPIO_ANY)) { + Bl0906.rx_pin = Pin(GPIO_BL0906_RX, GPIO_ANY); + uint32_t option = GetPin(Bl0906.rx_pin) - AGPIO(GPIO_BL0906_RX); // 0 .. 5 + Bl0906.baudrate = 19200; + + Energy->voltage_common = true; // Use common voltage + Energy->frequency_common = true; // Use common frequency + Energy->use_overtemp = true; // Use global temperature for overtemp detection + Energy->phase_count = option +1; // Handle 1 to 6 channels as phases + + TasmotaGlobal.energy_driver = XNRG_24; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg24(uint32_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (Bl0906Serial) { Bl0906Loop(); } + break; + case FUNC_EVERY_SECOND: + Bl0906EverySecond(); + break; + case FUNC_JSON_APPEND: + Bl0906Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Bl0906Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_INIT: + Bl0906Init(); + break; + case FUNC_PRE_INIT: + Bl0906PreInit(); + break; + } + return result; +} + +#endif // USE_BL0906 +#endif // USE_ENERGY_SENSOR +#endif // ESP32