diff --git a/tasmota/berry/include/be_gpio_defines.h b/tasmota/berry/include/be_gpio_defines.h index fc868e6d5..3c833acc1 100644 --- a/tasmota/berry/include/be_gpio_defines.h +++ b/tasmota/berry/include/be_gpio_defines.h @@ -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 }, diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 6e49061b6..f6b68cf3b 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -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 /*-------------------------------------------------------------------------------------------*\ diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 98a31b0e3..aeb690224 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.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_AF_AF_H_ diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 51b4f4ac3..54e4fda15 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.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_ diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h index 6e9f6f999..373ee312f 100644 --- a/tasmota/language/ca_AD.h +++ b/tasmota/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_CA_AD_H_ diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index ddfdbbf6e..7e4a1d6a2 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.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_ diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index c1ea4dd30..d48df566b 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.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_ diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 5eeecb716..72f00e61c 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.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_ diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 43fc302aa..f9de343bc 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.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_ diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 4c243c834..a17b1f628 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.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_ diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index b89b5476d..403304737 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.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_ diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index b3df21bbb..0c362f0ba 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/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_FY_NL_H_ diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 06923f13c..ab67db617 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.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_ diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 69962aad5..87cb26693 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.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_ diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 849b43f37..3a9db30cc 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.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_ diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 96daa238c..fda914fd7 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.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_ diff --git a/tasmota/language/lt_LT.h b/tasmota/language/lt_LT.h index d4f6c3560..d03792699 100644 --- a/tasmota/language/lt_LT.h +++ b/tasmota/language/lt_LT.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_ diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index d7b9bc0f0..cffeb5fc9 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_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_NL_NL_H_ diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index b0e87e711..801924e5b 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.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_ diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 8fdb5522a..cf26085df 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/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_BR_H_ diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 456a63ba3..7b7d90d5a 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/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_PT_PT_H_ diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 9a8d8847e..9898d3128 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.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_ diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 43cfba06a..fc935ace2 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.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_ diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 57998b8d9..c6e37b675 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/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_SK_SK_H_ diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 338447aea..1314ca049 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/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_SV_SE_H_ diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 60ca0cbb0..228e6da08 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.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_ diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 3ec1df400..c9e9a267f 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.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_ diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 97297a248..9079f40e3 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/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_VI_VN_H_ diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 065864175..520e90d0d 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/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_CN_H_ diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 4bda42c0f..a3f615d2f 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.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_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 587b7a571..307ac8e52 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.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) diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index f08d13dea..8dc6a5e4e 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -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 | // diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino index aae18311e..a669363c3 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino @@ -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; } - } } } diff --git a/tasmota/tasmota_xnrg_energy/xnrg_25_v9240.ino b/tasmota/tasmota_xnrg_energy/xnrg_25_v9240.ino new file mode 100644 index 000000000..ac6445aa0 --- /dev/null +++ b/tasmota/tasmota_xnrg_energy/xnrg_25_v9240.ino @@ -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 +#include + +#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(rw_mem + 0)) + , ANACTRL0(*reinterpret_cast(rw_mem + 1)) + , ANACTRL1(*reinterpret_cast(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(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( 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