mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 10:46:31 +00:00
Added F&F LE-01MR support
This commit is contained in:
parent
4a432fc2cd
commit
dc2d7cc681
@ -634,6 +634,8 @@
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
#define D_SENSOR_LE01MR_RX "LE-01MR Rx"
|
||||
#define D_SENSOR_LE01MR_TX "LE-01MR Tx"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -498,7 +498,9 @@
|
||||
//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code)
|
||||
#define SOLAXX1_SPEED 9600 // Solax X1 Modbus RS485 serial speed (default: 9600 baud)
|
||||
#define SOLAXX1_PV2 // Solax X1 using second PV
|
||||
|
||||
//#define USE_LE01MR // Add support for F&F LE-01MR modbus energy meter
|
||||
//#define LE01MR_SPEED 2400 // LE-01MR modbus baudrate (2400 default)
|
||||
//#define LE01MR_ADDR 0x01 // LE-01MR modbus address (0x01 default)
|
||||
// -- Low level interface devices -----------------
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code)
|
||||
|
||||
|
@ -501,8 +501,9 @@ void GetFeatures(void)
|
||||
#ifdef USE_HOTPLUG
|
||||
feature5 |= 0x00800000;
|
||||
#endif
|
||||
|
||||
// feature5 |= 0x01000000;
|
||||
#ifdef USE_LE01MR
|
||||
feature5 |= 0x01000000;
|
||||
#endif
|
||||
// feature5 |= 0x02000000;
|
||||
// feature5 |= 0x04000000;
|
||||
// feature5 |= 0x08000000;
|
||||
|
@ -194,6 +194,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code)
|
||||
//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code)
|
||||
//#define USE_LE01MR // Add support for F&F LE-01MR modbus energy meter
|
||||
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI
|
||||
@ -281,6 +282,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
|
||||
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter
|
||||
|
||||
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
|
||||
#define USE_DISPLAY // Add I2C Display Support (+2k code)
|
||||
@ -362,6 +364,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
|
||||
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter
|
||||
|
||||
//#undef USE_DS18x20 // Disable support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code)
|
||||
|
||||
@ -481,6 +484,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
|
||||
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter
|
||||
|
||||
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
@ -592,6 +596,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
|
||||
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter
|
||||
|
||||
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
|
@ -216,6 +216,8 @@ enum UserSelectablePins {
|
||||
GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface
|
||||
GPIO_GPS_RX, // GPS serial interface
|
||||
GPIO_GPS_TX, // GPS serial interface
|
||||
GPIO_LE01MR_RX, // F&F LE-01MR energy meter
|
||||
GPIO_LE01MR_TX, // F&F LE-01MR energy meter
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
// Programmer selectable GPIO functionality
|
||||
@ -296,7 +298,8 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|"
|
||||
D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|"
|
||||
D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|"
|
||||
D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX
|
||||
D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|"
|
||||
D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
@ -676,6 +679,10 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
||||
GPIO_SOLAXX1_TX, // Solax Inverter tx pin
|
||||
GPIO_SOLAXX1_RX, // Solax Inverter rx pin
|
||||
#endif // USE_SOLAX_X1
|
||||
#ifdef USE_LE01MR
|
||||
GPIO_LE01MR_RX, // F7F LE-01MR energy meter rx pin
|
||||
GPIO_LE01MR_TX, // F7F LE-01MR energy meter tx pin
|
||||
#endif // IFDEF:USE_LE01MR
|
||||
#endif // USE_ENERGY_SENSOR
|
||||
|
||||
// Serial
|
||||
|
290
tasmota/xnrg_13_fif_le01mr.ino
Normal file
290
tasmota/xnrg_13_fif_le01mr.ino
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
xnrg_13_fif_le01mr.ino - F&F LE-01MR energy meter with Modbus interface - support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Przemyslaw Wistuba
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_LE01MR
|
||||
/*********************************************************************************************\
|
||||
* F&F LE-01MR - This is a single phase energy meter with rs485 modbus interface
|
||||
* (and bidirectional energy counting - enabled by RS485).
|
||||
* It measure: Active energy imported AE+ [kWh] , Reactive energy imported RE+ [kvarh],
|
||||
* Voltage V [V], Current I [A], Frequency F [Hz], power factor (aka "cos-phi"),
|
||||
* Active power P [kW], Reactive power Q [kvar], Apparent power S [kVA],
|
||||
* *Active energy exported AE- [kWh] (when meter is switched to bi-directional counting then
|
||||
* reactive energy imported register contains value of Active energy exported).
|
||||
*
|
||||
* Meter descriptions at manufacturer page (english version have some description errors):
|
||||
* EN: https://www.fif.com.pl/en/usage-electric-power-meters/517-electricity-consumption-meter-le-01mr.html
|
||||
* PL: https://www.fif.com.pl/pl/liczniki-zuzycia-energii-elektrycznej/517-licznik-zuzycia-energii-le-01mr.html
|
||||
*
|
||||
* Register descriptions (not all, only those that are being read):
|
||||
*
|
||||
* /----------------------------------- Register address
|
||||
* | /-------------------------- Registers count
|
||||
* | | /---------------------- Datatype and size
|
||||
* | | | /----------------- Resolution (or multiplier)
|
||||
* | | | | /---------- Unit
|
||||
* | | | | | /---- Description
|
||||
* 0x0130 1 U16 0.01 Hz Frequency
|
||||
* 0x0131 1 U16 0.01 V Voltage
|
||||
* 0x0139 2 U32 0.001 A Current
|
||||
* 0x0140 2 U32 0.001 kW Active power
|
||||
* 0x0148 2 U32 0.001 kvar Reactive power
|
||||
* 0x0150 2 U32 0.001 kVA Apparent power
|
||||
* 0x0158 1 S16 0.001 - Power factor
|
||||
* 0xA000 2 U32 0.01 kWh Active energy imported
|
||||
* 0xA01E 2 U32 0.01 kvarh Reactive energy imported
|
||||
*
|
||||
* Datatype: S = signed int, U = unsigend int,
|
||||
* U32 - the first (lower) register contains high word,
|
||||
* second register contains lower word of 32bit dword:
|
||||
* value_32bit = (register+0)<<16 | (register+1);
|
||||
* /or/ val32bit = (reg+0)*65536 + (reg+1);
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XNRG_13 13
|
||||
|
||||
// can be user defined in my_user_config.h
|
||||
#ifndef LE01MR_SPEED
|
||||
#define LE01MR_SPEED 2400 // default LE01MR Modbus speed
|
||||
#endif
|
||||
// can be user defined in my_user_config.h
|
||||
#ifndef LE01MR_ADDR
|
||||
#define LE01MR_ADDR 1 // default LE01MR Modbus address
|
||||
#endif
|
||||
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *FifLEModbus;
|
||||
|
||||
const uint8_t le01mr_table_sz = 9;
|
||||
|
||||
const uint16_t le01mr_register_addresses[] {
|
||||
// IDX (reg count/datatype) [unit]
|
||||
0x0130, // 00 . LE01MR_FREQUENCY (1/U16) [Hz]
|
||||
0x0131, // 01 . LE01MR_VOLTAGE (1/U16) [V]
|
||||
0x0158, // 02 . LE01MR_POWER_FACTOR (1/S16)
|
||||
0x0139, // 03 . LE01MR_CURRENT (2/U32) [A]
|
||||
0x0140, // 04 . LE01MR_ACTIVE_POWER (2/U32) [kW]
|
||||
0x0148, // 05 . LE01MR_REACTIVE_POWER (2/U32) [kvar]
|
||||
0x0150, // 06 . LE01MR_APPARENT_POWER (2/U32) [kVA]
|
||||
0xA000, // 07 . LE01MR_TOTAL_ACTIVE_ENERGY (2/U32) [kWh]
|
||||
0xA01E // 08 . LE01MR_TOTAL_REACTIVE_ENERGY (2/U32) [kvarh]
|
||||
};
|
||||
|
||||
struct LE01MR {
|
||||
float total_active = 0;
|
||||
float total_reactive = 0;
|
||||
uint8_t read_state = 0;
|
||||
uint8_t send_retry = 0;
|
||||
uint8_t start_address_count = le01mr_table_sz;
|
||||
} Le01mr;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void FifLEEvery250ms(void)
|
||||
{
|
||||
bool data_ready = FifLEModbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[14]; // At least 9
|
||||
uint8_t reg_count = 2;
|
||||
if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) {
|
||||
reg_count=1;
|
||||
}
|
||||
|
||||
uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count);
|
||||
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount());
|
||||
|
||||
if (error) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error);
|
||||
} else {
|
||||
Energy.data_valid[0] = 0;
|
||||
|
||||
// SA=Slave Address, FC=Function Code, BC=Byte Count, B3..B0=Data byte, Ch Cl = crc16 checksum
|
||||
// U32 registers:
|
||||
// 00 01 02 03 04 05 06 07 08
|
||||
// SA FC BC B3 B2 B1 B0 Cl Ch
|
||||
// 01 03 04 00 00 00 72 7A 16 = REG[B3..B2=0x0139,B1..B0=0x013A] 114 = 0.114 A
|
||||
// 01 03 04 00 00 00 B0 FB 87 = REG[B3..B2=0xA01E,B1..B0=0xA01F] 176 = 1.76 kvarh
|
||||
// U16/S16 registers:
|
||||
// 00 01 02 03 04 05 06
|
||||
// SA FC BC B1 B0 Cl Ch
|
||||
// 01 03 02 5B 02 02 B5 = REG[B1..B0=0x0131] 23298 = 232.98 V
|
||||
// 01 03 02 03 E8 B8 FA = REG[B1..B0=0x0158] 1000 = 1.000 (power factor)
|
||||
// there are 3 data types used:
|
||||
// U16 - uint16_t
|
||||
// U32 - uint32_t
|
||||
// S16 - int16_t
|
||||
// everything drop into uint32 value, but depending on register ther will be 2 or 4 bytes
|
||||
uint32_t value_buff = 0;
|
||||
// for register table items 0..2 use 2 bytes (U16)
|
||||
if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { //
|
||||
value_buff = ((uint32_t)buffer[3])<<8 | buffer[4];
|
||||
//((uint8_t*)&value_buff)[1] = buffer[3];
|
||||
//((uint8_t*)&value_buff)[0] = buffer[4];
|
||||
} else {
|
||||
value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6];
|
||||
}
|
||||
|
||||
switch(Le01mr.read_state) {
|
||||
case 0:
|
||||
Energy.frequency[0] = value_buff * 0.01f; // 5000 => 50.00
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Energy.voltage[0] = value_buff * 0.01f; // 23298 => 232.98 V
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; // 1000 => 1.000
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Energy.current[0] = value_buff * 0.001f; // 114 => 0.114 A
|
||||
break;
|
||||
|
||||
case 4:
|
||||
Energy.active_power[0] = value_buff * 0.001f;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
Energy.reactive_power[0] = value_buff * 0.001f;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
Energy.apparent_power[0] = value_buff * 0.001f;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
Le01mr.total_active = value_buff * 0.01f;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
Le01mr.total_reactive = value_buff * 0.01f; // 176 => 1.76
|
||||
break;
|
||||
}
|
||||
|
||||
Le01mr.read_state++;
|
||||
if (Le01mr.read_state == Le01mr.start_address_count) {
|
||||
Le01mr.read_state = 0;
|
||||
|
||||
EnergyUpdateTotal(Le01mr.total_active, true);
|
||||
}
|
||||
}
|
||||
} // end data ready
|
||||
|
||||
if (0 == Le01mr.send_retry || data_ready) {
|
||||
uint8_t reg_count = 2;
|
||||
|
||||
Le01mr.send_retry = 5;
|
||||
// some registers are 1reg in size
|
||||
if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) reg_count=1;
|
||||
// send request
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus req reg %X, count %d"), le01mr_register_addresses[Le01mr.read_state], reg_count);
|
||||
FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count);
|
||||
} else {
|
||||
Le01mr.send_retry--;
|
||||
}
|
||||
}
|
||||
|
||||
void FifLESnsInit(void)
|
||||
{
|
||||
FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_RX]);
|
||||
uint8_t result = FifLEModbus->Begin(LE01MR_SPEED);
|
||||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void FifLEDrvInit(void)
|
||||
{
|
||||
if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_RX] < 99)) {
|
||||
energy_flg = XNRG_13;
|
||||
}
|
||||
}
|
||||
|
||||
void FifLEReset(void)
|
||||
{
|
||||
Le01mr.total_active = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_ENERGY_LE01MR[] PROGMEM =
|
||||
"{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}";
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
void FifLEShow(bool json)
|
||||
{
|
||||
char total_reactive_chr[FLOATSZ];
|
||||
dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr);
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_REACTIVE "\":%s"),
|
||||
total_reactive_chr);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_ENERGY_LE01MR, total_reactive_chr);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define SLOW_DOWN_X250MS 6
|
||||
uint8_t slowdown = SLOW_DOWN_X250MS;
|
||||
|
||||
bool Xnrg13(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
if (uptime > 4) {
|
||||
if (--slowdown == 0) {
|
||||
FifLEEvery250ms();
|
||||
slowdown=SLOW_DOWN_X250MS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
FifLEShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
FifLEShow(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
case FUNC_ENERGY_RESET:
|
||||
FifLEReset();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
FifLESnsInit();
|
||||
break;
|
||||
case FUNC_PRE_INIT:
|
||||
FifLEDrvInit();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_LE01MR
|
||||
#endif // USE_ENERGY_SENSOR
|
Loading…
x
Reference in New Issue
Block a user