mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
Added V9240 energy metering chip driver (#23127)
* Add V9240 driver * Addendum to previous commit * Add driver code similar to the prototype * they are talking to each other * Added implementation of calibration commands * continued work of calibration * Maybe it works. * Post-merger control * Change driver number 34 to 25 * Correction of other comments * Removed duplicate code * Adjusting the calibration procedure according to the behavior stated here. To the extent possible. https://tasmota.github.io/docs/Power-Monitoring-Calibration/#calibration-procedure * Removed added trailing whitespaces * Fixing several small issues.
This commit is contained in:
parent
08fae4bcd6
commit
758ba17dde
@ -333,6 +333,8 @@ const be_const_member_t lv_gpio_constants[] = {
|
||||
{ "TUYA_TX", (int32_t) GPIO_TUYA_TX },
|
||||
{ "TX2X_TXD_BLACK", (int32_t) GPIO_TX2X_TXD_BLACK },
|
||||
{ "TXD", (int32_t) GPIO_TXD },
|
||||
{ "V9240_RX", (int32_t) GPIO_V9240_RX },
|
||||
{ "V9240_TX", (int32_t) GPIO_V9240_TX },
|
||||
{ "VINDRIKTNING_RX", (int32_t) GPIO_VINDRIKTNING_RX },
|
||||
{ "VL53LXX_XSHUT1", (int32_t) GPIO_VL53LXX_XSHUT1 },
|
||||
{ "WE517_RX", (int32_t) GPIO_WE517_RX },
|
||||
|
@ -230,6 +230,7 @@ enum UserSelectablePins {
|
||||
GPIO_TM1640CLK, GPIO_TM1640DIN, // TM1640 (16 x seven-segment LED controler)
|
||||
GPIO_TWAI_TX, GPIO_TWAI_RX, GPIO_TWAI_BO, GPIO_TWAI_CLK, // ESP32 TWAI serial interface
|
||||
GPIO_C8_CO2_5K_TX, GPIO_C8_CO2_5K_RX, // C8-CO2-5K CO2 Sensor
|
||||
GPIO_V9240_TX, GPIO_V9240_RX, // V9240 serial interface
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
// Error as warning to rethink GPIO usage with max 2045
|
||||
@ -506,7 +507,8 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_I2C_SER_TX "|" D_SENSOR_I2C_SER_RX "|"
|
||||
D_SENSOR_TM1640_CLK "|" D_SENSOR_TM1640_DIN "|"
|
||||
D_SENSOR_TWAI_TX "|" D_SENSOR_TWAI_RX "|" D_SENSOR_TWAI_BO "|" D_SENSOR_TWAI_CLK "|"
|
||||
D_SENSOR_C8_CO2_5K_TX "|" D_SENSOR_C8_CO2_5K_RX
|
||||
D_SENSOR_C8_CO2_5K_TX "|" D_SENSOR_C8_CO2_5K_RX "|"
|
||||
D_SENSOR_V9240_TX "|" D_SENSOR_V9240_RX
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
@ -1007,6 +1009,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
AGPIO(GPIO_BL6523_TX), // BL6523 based Watt meter Serial interface
|
||||
AGPIO(GPIO_BL6523_RX), // BL6523 based Watt meter Serial interface
|
||||
#endif
|
||||
#ifdef USE_V9240
|
||||
AGPIO(GPIO_V9240_RX), // Serial V9240 interface
|
||||
AGPIO(GPIO_V9240_TX), // Serial V9240 interface
|
||||
#endif
|
||||
#endif // USE_ENERGY_SENSOR
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_AF_AF_H_
|
||||
|
@ -1314,4 +1314,10 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
|
||||
#endif // _LANGUAGE_BG_BG_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_CA_AD_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_CS_CZ_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Aufladen"
|
||||
#define D_CAPACITY "Kapazität"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_DE_DE_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_EL_GR_H_
|
||||
|
@ -1315,4 +1315,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_EN_GB_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Cargando"
|
||||
#define D_CAPACITY "Capacidad"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_ES_ES_H_
|
||||
|
@ -1315,4 +1315,8 @@
|
||||
#define D_CHARGING "En charge"
|
||||
#define D_CAPACITY "Capacité"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_FR_FR_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_FY_NL_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_HE_HE_H_
|
||||
|
@ -1321,4 +1321,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_HU_HU_H_
|
||||
|
@ -1315,4 +1315,8 @@
|
||||
#define D_CHARGING "In carica"
|
||||
#define D_CAPACITY "Capacità"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_IT_IT_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_KO_KO_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Kraunasi"
|
||||
#define D_CAPACITY "Talpa"
|
||||
|
||||
// xnrg_34_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_LT_LT_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_NL_NL_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Ładowanie"
|
||||
#define D_CAPACITY "Pojemność"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_PL_PL_D_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_PT_BR_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_PT_PT_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_RO_RO_H_
|
||||
|
@ -1315,4 +1315,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_RU_RU_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_SK_SK_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_SV_SE_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_TR_TR_H_
|
||||
|
@ -1314,4 +1314,9 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_UK_UA_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_VI_VN_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_CN_H_
|
||||
|
@ -1314,4 +1314,8 @@
|
||||
#define D_CHARGING "Charging"
|
||||
#define D_CAPACITY "Capacity"
|
||||
|
||||
// xnrg_25_v9240.ino
|
||||
#define D_SENSOR_V9240_TX "V9240 TX"
|
||||
#define D_SENSOR_V9240_RX "V9240 RX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_TW_H_
|
||||
|
@ -950,6 +950,7 @@
|
||||
// #define IEM3000_IEM3155 // Compatibility fix for Iem3155 (changes Power and Energy total readout)
|
||||
//#define USE_WE517 // Add support for Orno WE517-Modbus energy monitor (+1k code)
|
||||
//#define USE_MODBUS_ENERGY // Add support for generic modbus energy monitor using a user file in rule space (+5k)
|
||||
//#define USE_V9240 // Add support for v9240
|
||||
|
||||
// -- Low level interface devices -----------------
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code)
|
||||
|
@ -943,7 +943,9 @@ constexpr uint32_t feature[] = {
|
||||
#ifdef USE_WIZMOTE
|
||||
0x00002000 | // xdrv_77_wizmote.ino
|
||||
#endif
|
||||
// 0x00004000 | //
|
||||
#if defined(USE_ENERGY_SENSOR) && defined(USE_V9240)
|
||||
0x00004000 | // xnrg_25_v9240.ino
|
||||
#endif
|
||||
// 0x00008000 | //
|
||||
// 0x00010000 | //
|
||||
// 0x00020000 | //
|
||||
|
@ -924,6 +924,7 @@ uint32_t EnergyGetCalibration(uint32_t cal_type, uint32_t chan = 0) {
|
||||
case ENERGY_POWER_CALIBRATION: return Settings->energy_power_calibration;
|
||||
case ENERGY_VOLTAGE_CALIBRATION: return Settings->energy_voltage_calibration;
|
||||
case ENERGY_CURRENT_CALIBRATION: return Settings->energy_current_calibration;
|
||||
|
||||
}
|
||||
}
|
||||
return Settings->energy_frequency_calibration;
|
||||
@ -1300,7 +1301,6 @@ void EnergyShow(bool json) {
|
||||
else if (0 == Energy->current[i]) {
|
||||
reactive_power[i] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
721
tasmota/tasmota_xnrg_energy/xnrg_25_v9240.ino
Normal file
721
tasmota/tasmota_xnrg_energy/xnrg_25_v9240.ino
Normal file
@ -0,0 +1,721 @@
|
||||
/*
|
||||
xnrg_25_v9240.ino - v9240 energy sensor support for Tasmota
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_V9240
|
||||
|
||||
#define XNRG_25 25
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define V9240_SERIAL_BAUDRATE 19200
|
||||
|
||||
|
||||
namespace Address
|
||||
{
|
||||
static const uint16_t SysCtrl = 0x0180;
|
||||
static const uint16_t AnaCtrl0 = 0x0182;
|
||||
static const uint16_t AnaCtrl1 = 0x0183;
|
||||
|
||||
|
||||
static const uint16_t PAC = 0x00F6;
|
||||
static const uint16_t PHC = 0x00F7;
|
||||
static const uint16_t PADCC = 0x00F8;
|
||||
static const uint16_t QAC = 0x00F9;
|
||||
static const uint16_t QADCC = 0x00FB;
|
||||
static const uint16_t IAC = 0x00FD;
|
||||
static const uint16_t IADCC = 0x00FE;
|
||||
static const uint16_t UC = 0x00FF;
|
||||
static const uint16_t IAADCC = 0x0104;
|
||||
static const uint16_t UADCC = 0x0106;
|
||||
static const uint16_t BPFPARA = 0x0107;
|
||||
static const uint16_t UDCC = 0x0108;
|
||||
static const uint16_t CKSUM = 0x0109;
|
||||
|
||||
static const uint16_t RW_START = PAC;
|
||||
|
||||
static const uint16_t SysStsClr = 0x019D;
|
||||
static const uint16_t SFTRST = 0x01BF;
|
||||
|
||||
static const uint16_t SysSts = 0x00CA;
|
||||
static const uint16_t FREQINST = 0x00CB;
|
||||
static const uint16_t PAINST = 0x00CC;
|
||||
static const uint16_t QINST = 0x00CD;
|
||||
static const uint16_t IAINST = 0x00CE;
|
||||
static const uint16_t UINST = 0x00CF;
|
||||
static const uint16_t PAAVG = 0x00D0;
|
||||
static const uint16_t QAVG = 0x00D1;
|
||||
static const uint16_t FREQAVG = 0x00D2;
|
||||
static const uint16_t IAAVG = 0x00D3;
|
||||
static const uint16_t UAVG = 0x00D4;
|
||||
static const uint16_t UDCINST = 0x00D9;
|
||||
static const uint16_t IADCINST = 0x00DA;
|
||||
static const uint16_t ZXDATREG = 0x00DC;
|
||||
static const uint16_t ZXDAT = 0x00DD;
|
||||
static const uint16_t PHDAT = 0x00DE;
|
||||
|
||||
static const uint16_t T8BAUD = 0x00E0;
|
||||
|
||||
static const uint16_t RO_START = SysSts;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// register structures
|
||||
|
||||
union SysSts // 0x0CA
|
||||
{
|
||||
int32_t reg;
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
uint32_t ref:1; // 0
|
||||
uint32_t phsdone:1; // 1
|
||||
uint32_t chkerr:1; // 2
|
||||
uint32_t rstsrc:3; // 3:5
|
||||
uint32_t pdn_r:1; // 6
|
||||
uint32_t pdn:1; // 7
|
||||
uint32_t bisterr:1; // 8
|
||||
uint32_t phsdone_r:1; // 9
|
||||
uint32_t Resered1:1; // 10
|
||||
uint32_t usign:1; // 11
|
||||
};
|
||||
} ;
|
||||
|
||||
union SysCtrl // 0x0180
|
||||
{
|
||||
int32_t reg;
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
uint32_t Reserved0:1; // 0
|
||||
uint32_t pgau:1; // 1
|
||||
uint32_t bphpf:1; // 2
|
||||
uint32_t iesul:1; // 3
|
||||
uint32_t iepdn:1; // 4
|
||||
uint32_t iehse:1; // 5
|
||||
uint32_t rcx12:1; // 6
|
||||
uint32_t rctrim:5; // 7:11
|
||||
uint32_t shorti:1; // 12
|
||||
uint32_t shortu:1; // 13
|
||||
uint32_t restl:2; // 14:15
|
||||
uint32_t rest:3; // 16:18
|
||||
uint32_t meaclksel:1; // 19
|
||||
uint32_t adcclksel:2; // 20:21
|
||||
uint32_t gai:2; // 22:23
|
||||
uint32_t Reserved1:2; // 24:25
|
||||
uint32_t gu:1; // 26
|
||||
uint32_t adciapdn:1; // 27
|
||||
uint32_t Reserved2:1; // 28
|
||||
uint32_t adcupdn:1; // 29
|
||||
uint32_t Reserved3:2; // 30:31
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
union AnaCtrl0 // 0x0182
|
||||
{
|
||||
int32_t reg;
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
uint32_t Reserved1:8; // 0:7
|
||||
uint32_t IT:2; // 8:9
|
||||
uint32_t Reserved2:22; // 10:31
|
||||
};
|
||||
};
|
||||
|
||||
union AnaCtrl1 // 0x0183
|
||||
{
|
||||
int32_t reg;
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
uint32_t Reserved1:28; // 0:27
|
||||
uint32_t CSEL:2; // 28:29
|
||||
uint32_t Reserved2:2; // 30:31
|
||||
};
|
||||
};
|
||||
|
||||
union SysStsClr // 0x019D
|
||||
{
|
||||
int32_t reg;
|
||||
struct __attribute__ ((packed))
|
||||
{
|
||||
uint32_t Reserved1:6; // 0:5
|
||||
uint32_t pdn_clr:1; // 6
|
||||
uint32_t Reserved2:2; // 7:8
|
||||
uint32_t phsdone_clr:1;// 9
|
||||
uint32_t Reserved3:22; // 10:31
|
||||
};
|
||||
};
|
||||
|
||||
// communication structures
|
||||
union packet
|
||||
{
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t header;
|
||||
uint16_t ctrl:4;
|
||||
uint16_t addr_h:4;
|
||||
uint16_t addr_l:8;
|
||||
uint32_t data;
|
||||
uint8_t check;
|
||||
} ;
|
||||
char buff[8];
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
class V9240
|
||||
{
|
||||
private:
|
||||
static constexpr size_t rw_len = 23;
|
||||
static constexpr size_t ro_len = 22;
|
||||
static constexpr size_t buffer_size = 128; // max response + max query
|
||||
|
||||
static constexpr int32_t header = 0x7d;
|
||||
static constexpr int32_t ctrl_read = 0x1;
|
||||
static constexpr int32_t ctrl_write = 0x2;
|
||||
|
||||
explicit V9240();
|
||||
~V9240();
|
||||
|
||||
public:
|
||||
enum parameter{
|
||||
Voltage = 0,
|
||||
Amperage,
|
||||
Frequency,
|
||||
Power,
|
||||
Reactive
|
||||
};
|
||||
|
||||
static V9240& instance();
|
||||
|
||||
bool open(char const*);
|
||||
void close();
|
||||
void start();
|
||||
bool reset();
|
||||
inline void v9240_loop() {read_data_from_port();};
|
||||
|
||||
|
||||
bool process_command();
|
||||
|
||||
float value(const parameter p) const;
|
||||
inline float operator [] (const parameter p) const {return value(p);};
|
||||
private:
|
||||
bool read(int32_t *ptr,uint16_t address, size_t n);
|
||||
bool write(int16_t address, int32_t data);
|
||||
void set_checksum();
|
||||
inline char calc_check(char *buff,size_t len);
|
||||
|
||||
private :
|
||||
void read_data_from_port();
|
||||
void send_next();
|
||||
|
||||
private:
|
||||
TasmotaSerial *port;
|
||||
|
||||
size_t step = 0;
|
||||
|
||||
size_t next_read_len = 0;
|
||||
char serial_buff[buffer_size];
|
||||
int32_t *ptr_read = nullptr;
|
||||
uint32_t start_pause_timer = 0;
|
||||
uint32_t timeout = 0 ;
|
||||
|
||||
// chip memory
|
||||
int32_t rw_mem[rw_len];
|
||||
int32_t ro_mem[ro_len];
|
||||
|
||||
// RW Control & Calibration registers
|
||||
volatile SysCtrl &SYSCTRL; // rw_mem[0];
|
||||
volatile AnaCtrl0 &ANACTRL0;
|
||||
volatile AnaCtrl1 &ANACTRL1;
|
||||
|
||||
volatile int32_t &UADCC;
|
||||
volatile int32_t &IAADCC ;
|
||||
volatile int32_t &PAC ; // = 1;
|
||||
volatile int32_t &PADCC; // = 0;
|
||||
volatile int32_t &PHC; // = 0;
|
||||
volatile int32_t &QAC; // = 1;
|
||||
volatile int32_t &QADCC; // = 0;
|
||||
volatile int32_t &IAC; // = 0;
|
||||
volatile int32_t &IADCC; // = 0;
|
||||
volatile int32_t &UC; // = -1103500000;
|
||||
volatile int32_t &UDCC; // = 1196289;
|
||||
volatile int32_t &BPFPARA; // = 0x806764B6;
|
||||
volatile int32_t &CKSUM; // = 0;
|
||||
|
||||
// RO Meterin control register
|
||||
volatile const SysSts &SYSSTS;
|
||||
volatile const int32_t &FREQINST;
|
||||
volatile const int32_t &PAINST;
|
||||
volatile const int32_t &QINST;
|
||||
volatile const int32_t &IAINST;
|
||||
volatile const int32_t &UINST;
|
||||
volatile const int32_t &PAAVG;
|
||||
volatile const int32_t &QAVG;
|
||||
volatile const int32_t &FREQAVG;
|
||||
volatile const int32_t &IAAVG;
|
||||
volatile const int32_t &UAVG;
|
||||
volatile const int32_t &UDCINST;
|
||||
volatile const int32_t &IADCINST;
|
||||
volatile const int32_t &ZXDATREG;
|
||||
volatile const int32_t &ZXDAT;
|
||||
volatile const int32_t &PHDAT;
|
||||
volatile const int32_t &T8BAUD;
|
||||
|
||||
};
|
||||
|
||||
V9240& V9240::instance()
|
||||
{
|
||||
static V9240 chip;
|
||||
return chip;
|
||||
}
|
||||
|
||||
|
||||
V9240::V9240() : port(nullptr)
|
||||
// mapping registers to arrays
|
||||
, SYSCTRL (*reinterpret_cast<SysCtrl*>(rw_mem + 0))
|
||||
, ANACTRL0(*reinterpret_cast<AnaCtrl0*>(rw_mem + 1))
|
||||
, ANACTRL1(*reinterpret_cast<AnaCtrl1*>(rw_mem + 2))
|
||||
, UADCC (*(rw_mem+3+ (Address::UADCC - Address::RW_START )))
|
||||
, IAADCC (*(rw_mem+3+ (Address::IAADCC - Address::RW_START )))
|
||||
, PAC (*(rw_mem+3+ (Address::PAC - Address::RW_START )))
|
||||
, PADCC (*(rw_mem+3+ (Address::PADCC - Address::RW_START )))
|
||||
, PHC (*(rw_mem+3+ (Address::PHC - Address::RW_START )))
|
||||
, QAC (*(rw_mem+3+ (Address::QAC - Address::RW_START )))
|
||||
, QADCC (*(rw_mem+3+ (Address::QADCC - Address::RW_START )))
|
||||
, IAC (*(rw_mem+3+ (Address::IAC - Address::RW_START )))
|
||||
, IADCC (*(rw_mem+3+ (Address::IADCC - Address::RW_START )))
|
||||
, UC (*(rw_mem+3+ (Address::UC - Address::RW_START )))
|
||||
, UDCC (*(rw_mem+3+ (Address::UDCC - Address::RW_START )))
|
||||
, BPFPARA(*(rw_mem+3+ (Address::BPFPARA - Address::RW_START )))
|
||||
, CKSUM (*(rw_mem+3+ (Address::CKSUM - Address::RW_START )))
|
||||
|
||||
, SYSSTS (*reinterpret_cast<SysSts*>(ro_mem + (Address::SysSts - Address::RO_START)))
|
||||
, FREQINST(*(ro_mem + (Address::FREQINST - Address::RO_START )))
|
||||
, PAINST (*(ro_mem + (Address::PAINST - Address::RO_START )))
|
||||
, QINST (*(ro_mem + (Address::QINST - Address::RO_START )))
|
||||
, IAINST (*(ro_mem + (Address::IAINST - Address::RO_START )))
|
||||
, UINST (*(ro_mem + (Address::UINST - Address::RO_START )))
|
||||
, PAAVG (*(ro_mem + (Address::PAAVG - Address::RO_START )))
|
||||
, QAVG (*(ro_mem + (Address::QAVG - Address::RO_START )))
|
||||
, FREQAVG (*(ro_mem + (Address::FREQAVG - Address::RO_START )))
|
||||
, IAAVG (*(ro_mem + (Address::IAAVG - Address::RO_START )))
|
||||
, UAVG (*(ro_mem + (Address::UAVG - Address::RO_START )))
|
||||
, UDCINST (*(ro_mem + (Address::UDCINST - Address::RO_START )))
|
||||
, IADCINST(*(ro_mem + (Address::IADCINST - Address::RO_START )))
|
||||
, ZXDATREG(*(ro_mem + (Address::ZXDATREG - Address::RO_START )))
|
||||
, ZXDAT (*(ro_mem + (Address::ZXDAT - Address::RO_START )))
|
||||
, PHDAT (*(ro_mem + (Address::PHDAT - Address::RO_START )))
|
||||
, T8BAUD (*(ro_mem + (Address::T8BAUD - Address::RO_START - 1 )))
|
||||
{
|
||||
memset(rw_mem,0,sizeof (rw_mem));
|
||||
memset(ro_mem,0,sizeof (ro_mem));
|
||||
|
||||
// its calibration coeficients for my chip
|
||||
|
||||
PAC = EnergyGetCalibration(ENERGY_POWER_CALIBRATION, 0);
|
||||
PADCC = 0;
|
||||
PHC = EnergyGetCalibration(ENERGY_FREQUENCY_CALIBRATION, 0);
|
||||
QAC = -1;
|
||||
QADCC = 0;
|
||||
IAC = EnergyGetCalibration(ENERGY_CURRENT_CALIBRATION, 0);
|
||||
IADCC = 75969; // 16758789;
|
||||
|
||||
UC = EnergyGetCalibration(ENERGY_VOLTAGE_CALIBRATION);
|
||||
UDCC = 3596949; // 1196289;
|
||||
|
||||
BPFPARA = 0x806764B6;
|
||||
|
||||
}
|
||||
|
||||
V9240::~V9240()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void V9240::start()
|
||||
{
|
||||
if(port != nullptr)
|
||||
{
|
||||
step = 0;
|
||||
port->flush();
|
||||
read(ro_mem,Address::SysSts,1);
|
||||
timeout = millis();
|
||||
}
|
||||
}
|
||||
|
||||
float V9240::value(const V9240::parameter p) const
|
||||
{
|
||||
float v = 0;
|
||||
switch (p) {
|
||||
case Voltage:
|
||||
v = float(UAVG)*1e-6;
|
||||
break;
|
||||
case Amperage:
|
||||
v = float(IAAVG)*1e-6;
|
||||
break;
|
||||
case Frequency:
|
||||
v = 0.00390625*V9240_SERIAL_BAUDRATE*T8BAUD/float(FREQAVG);
|
||||
break;
|
||||
case Power:
|
||||
v = float(PAAVG)*1e-3;
|
||||
break;
|
||||
case Reactive:
|
||||
v = float(QAVG)*1e-3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
void V9240::set_checksum()
|
||||
{
|
||||
CKSUM = UINT32_MAX - std::accumulate(rw_mem,rw_mem+rw_len-1 ,0);
|
||||
write(Address::CKSUM,CKSUM);
|
||||
}
|
||||
|
||||
char V9240::calc_check(char *buff, size_t len)
|
||||
{
|
||||
return ~std::accumulate(buff,buff+len,0)+0x33;
|
||||
}
|
||||
|
||||
|
||||
void V9240::send_next()
|
||||
{
|
||||
port->flush();
|
||||
|
||||
switch (step++) {
|
||||
case 0:
|
||||
read(rw_mem,Address::SysCtrl,1);
|
||||
break;
|
||||
case 1:
|
||||
write(Address::AnaCtrl0,ANACTRL0.reg);
|
||||
break;
|
||||
case 2:
|
||||
// Configure CSEL (
|
||||
ANACTRL1.CSEL = 1;
|
||||
write(Address::AnaCtrl1,ANACTRL1.reg);
|
||||
break;
|
||||
case 3:
|
||||
// Disable internal PGA
|
||||
SYSCTRL.gu = 1;
|
||||
SYSCTRL.gai = 3;
|
||||
// Enable ADC
|
||||
SYSCTRL.adcupdn = 1;
|
||||
SYSCTRL.adciapdn = 1;
|
||||
write(Address::SysCtrl,SYSCTRL.reg);
|
||||
break;
|
||||
case 4:
|
||||
// DC calibratio. Paragraph 7.4 page 33
|
||||
SYSCTRL.shortu = 1 ;
|
||||
SYSCTRL.shorti = 1 ;
|
||||
write(Address::SysCtrl,SYSCTRL.reg);
|
||||
break;
|
||||
case 5:
|
||||
read(ro_mem, Address::RO_START,ro_len);
|
||||
break;
|
||||
case 6:
|
||||
UADCC = UDCINST;
|
||||
IAADCC = IADCINST;
|
||||
|
||||
SYSCTRL.shortu = 0 ;
|
||||
SYSCTRL.shorti = 0 ;
|
||||
write(Address::SysCtrl,SYSCTRL.reg);
|
||||
break;
|
||||
case 7:
|
||||
// Write calibration parameter
|
||||
write(Address::UADCC ,UADCC);
|
||||
break;
|
||||
case 8:
|
||||
write(Address::IAADCC ,IAADCC);
|
||||
break;
|
||||
case 9:
|
||||
write(Address::PAC ,PAC);
|
||||
break;
|
||||
case 10:
|
||||
write(Address::PADCC ,PADCC);
|
||||
break;
|
||||
case 11:
|
||||
write(Address::QAC ,QAC);
|
||||
break;
|
||||
case 12:
|
||||
write(Address::QADCC ,QADCC);
|
||||
break;
|
||||
case 13:
|
||||
write(Address::PHC ,PHC);
|
||||
break;
|
||||
case 14:
|
||||
write(Address::IAC ,IAC);
|
||||
break;
|
||||
case 15:
|
||||
write(Address::IADCC ,IADCC);
|
||||
break;
|
||||
case 16:
|
||||
write(Address::UC ,UC);
|
||||
break;
|
||||
case 17:
|
||||
write(Address::UDCC ,UDCC);
|
||||
break;
|
||||
case 18:
|
||||
write(Address::BPFPARA ,BPFPARA);
|
||||
break;
|
||||
case 19:
|
||||
set_checksum();
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_DEBUG "*** V9240 started"));
|
||||
break;
|
||||
case 20:
|
||||
read(&ro_mem[ro_len-1],Address::T8BAUD,1);
|
||||
break;
|
||||
default:
|
||||
read(ro_mem,Address::RO_START,ro_len-1);
|
||||
step = 20;
|
||||
}
|
||||
|
||||
timeout = millis();
|
||||
}
|
||||
|
||||
// next methods is platfrm-specific (serial port & Qt signals)
|
||||
void V9240::read_data_from_port()
|
||||
{
|
||||
if(next_read_len != 0 )
|
||||
{
|
||||
auto bytes = port->available();
|
||||
|
||||
if(bytes >= next_read_len)
|
||||
{
|
||||
port->read(serial_buff,next_read_len);
|
||||
|
||||
char checksum = calc_check(serial_buff+sizeof(packet),next_read_len-sizeof(packet)-1);
|
||||
if(checksum == serial_buff[next_read_len-1])
|
||||
{
|
||||
packet &recv = *reinterpret_cast<packet*>( serial_buff + sizeof (packet) );
|
||||
if(recv.ctrl == ctrl_read)
|
||||
{
|
||||
int32_t *data = (int32_t*)(serial_buff + sizeof (packet) + 3);
|
||||
memcpy(ptr_read,data,sizeof (int32_t) * (recv.addr_l==0 ? 1 : recv.addr_l));
|
||||
}
|
||||
}
|
||||
else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_DEBUG "*** V9240 checksum error in: %d calc: %d")
|
||||
,uint32_t(serial_buff[next_read_len-1]),uint32_t(checksum));
|
||||
}
|
||||
next_read_len = 0;
|
||||
ptr_read = nullptr;
|
||||
|
||||
start_pause_timer = millis();
|
||||
timeout = 0;
|
||||
}
|
||||
else if(timeout > 0 && millis() - timeout > 1000)
|
||||
{
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_DEBUG "*** V9240 timeout"));
|
||||
start_pause_timer = 0;
|
||||
start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(start_pause_timer != 0 && millis()-start_pause_timer > 10)
|
||||
{
|
||||
send_next();
|
||||
start_pause_timer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool V9240::open(char const *portName)
|
||||
{
|
||||
if(port != nullptr)
|
||||
return false;
|
||||
|
||||
port = new TasmotaSerial(Pin(GPIO_V9240_RX),Pin(GPIO_V9240_TX),1,0,256,false);
|
||||
if (port->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
port->begin(V9240_SERIAL_BAUDRATE,SERIAL_8O1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void V9240::close()
|
||||
{
|
||||
if(port != nullptr)
|
||||
{
|
||||
port->end();
|
||||
delete port;
|
||||
port = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool V9240::reset()
|
||||
{
|
||||
if(port != nullptr)
|
||||
{
|
||||
port->begin(100,SERIAL_8O1);
|
||||
port->write(uint8_t(0));
|
||||
char zero;
|
||||
port->read(&zero,1);
|
||||
port->begin(V9240_SERIAL_BAUDRATE,SERIAL_8O1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool V9240::read(int32_t *ptr, uint16_t address, size_t n)
|
||||
{
|
||||
packet p;
|
||||
p.header = header;
|
||||
p.ctrl = ctrl_read;
|
||||
p.addr_h = (address&0x0F00)>>8;
|
||||
p.addr_l = address&0x00FF;
|
||||
p.data = n;
|
||||
p.check = calc_check(p.buff,sizeof(p)-1);
|
||||
|
||||
next_read_len = sizeof (p) + 4 + sizeof (int32_t)*n;
|
||||
ptr_read = ptr;
|
||||
|
||||
port->write(p.buff,sizeof (p));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V9240::write(int16_t address, int32_t data)
|
||||
{
|
||||
packet p;
|
||||
p.header = header;
|
||||
p.ctrl = ctrl_write;
|
||||
p.addr_h = (address&0x0F00)>>8;
|
||||
p.addr_l = address&0x00FF;
|
||||
p.data = data;
|
||||
p.check = calc_check(p.buff,sizeof(p)-1);
|
||||
|
||||
next_read_len = sizeof (p) + 4;
|
||||
|
||||
port->write(p.buff,sizeof (p));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V9240::process_command()
|
||||
{
|
||||
float value = atof(XdrvMailbox.data);
|
||||
XdrvMailbox.payload = 0;
|
||||
|
||||
auto calc_cor = [] (float new_value,float old_value,int32_t k) -> int32_t
|
||||
{ return int32_t( (float(INT_MAX) *( new_value - old_value ) + float(k) * new_value ) / old_value ); };
|
||||
|
||||
switch (Energy->command_code) {
|
||||
case CMND_POWERCAL:
|
||||
// PADCC = calc_cor (value,(*this)[Power],P);
|
||||
break;
|
||||
case CMND_VOLTAGECAL:
|
||||
// UDCC = float(INT_MAX)*value;
|
||||
break;
|
||||
case CMND_CURRENTCAL:
|
||||
// IADCC = float(INT_MAX)*value;
|
||||
break;
|
||||
case CMND_FREQUENCYCAL:
|
||||
PHC = value ;
|
||||
EnergySetCalibration(ENERGY_FREQUENCY_CALIBRATION, PHC, 0);
|
||||
break;
|
||||
case CMND_POWERSET:
|
||||
PAC = calc_cor (value,(*this)[Power],PAC);
|
||||
EnergySetCalibration(ENERGY_POWER_CALIBRATION, PAC, 0);
|
||||
break;
|
||||
case CMND_VOLTAGESET:
|
||||
UC = calc_cor (value,(*this)[Voltage],UC);
|
||||
EnergySetCalibration(ENERGY_VOLTAGE_CALIBRATION, UC, 0);
|
||||
break;
|
||||
case CMND_CURRENTSET:
|
||||
IAC = calc_cor (value,(*this)[Amperage],IAC);
|
||||
EnergySetCalibration(ENERGY_CURRENT_CALIBRATION, IAC, 0);
|
||||
break;
|
||||
case CMND_FREQUENCYSET:
|
||||
break;
|
||||
case CMND_MODULEADDRESS:
|
||||
break;
|
||||
case CMND_ENERGYCONFIG:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
step = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Xnrg34Init()
|
||||
{
|
||||
Energy->phase_count = 1;
|
||||
Energy->voltage_common = true; // Phase voltage = false, Common voltage = true
|
||||
Energy->frequency_common = true; // Phase frequency = false, Common frequency = true
|
||||
Energy->type_dc = false; // AC = false, DC = true;
|
||||
Energy->use_overtemp = true; // Use global temperature for overtemp detection
|
||||
V9240::instance().start();
|
||||
}
|
||||
|
||||
void Xnrg34Second()
|
||||
{
|
||||
static int32_t last_power = 0;
|
||||
|
||||
if (Energy->power_on) {
|
||||
Energy->data_valid[0] = 0;
|
||||
Energy->frequency[0] = V9240::instance()[V9240::Frequency];
|
||||
Energy->voltage[0] = V9240::instance()[V9240::Voltage];
|
||||
Energy->current[0] = V9240::instance()[V9240::Amperage];
|
||||
Energy->active_power[0] = V9240::instance()[V9240::Power];
|
||||
// Energy->reactive_power[0] = V9240::instance()[V9240::Reactive];
|
||||
|
||||
Energy->kWhtoday_delta[0] += (Energy->active_power[0]+last_power) * 500.0 / 36.0; // trapezoid integration
|
||||
last_power = Energy->active_power[0];
|
||||
EnergyUpdateTotal();
|
||||
}
|
||||
}
|
||||
|
||||
void Xnrg34PreInit()
|
||||
{
|
||||
if (PinUsed(GPIO_V9240_RX) && PinUsed(GPIO_V9240_TX))
|
||||
{
|
||||
TasmotaGlobal.energy_driver = XNRG_25;
|
||||
V9240::instance().open("");
|
||||
V9240::instance().reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Xnrg25(uint32_t function) {
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_LOOP:
|
||||
V9240::instance().v9240_loop();
|
||||
break;
|
||||
case FUNC_ENERGY_EVERY_SECOND:
|
||||
Xnrg34Second();
|
||||
break;
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = V9240::instance().process_command();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
Xnrg34Init();
|
||||
break;
|
||||
case FUNC_PRE_INIT:
|
||||
Xnrg34PreInit();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_ENERGY_V9240
|
||||
#endif // USE_ENERGY_SENSOR
|
Loading…
x
Reference in New Issue
Block a user