2022-10-06 22:17:04 +01:00
/*
xnrg_29_modbus . ino - Generic Modbus energy meter support for Tasmota
Copyright ( C ) 2022 Theo Arends
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_MODBUS_ENERGY
/*********************************************************************************************\
2022-10-08 15:14:11 +01:00
* Generic Modbus energy meter
2022-10-06 22:17:04 +01:00
*
2022-12-23 15:56:18 +00:00
* - Supports single three phase device or three single phase devices of same model on bus .
* - Uses a rule file called modbus allowing for easy configuration of modbus energy monitor device ( s ) .
2022-10-06 22:17:04 +01:00
*
2022-10-08 15:14:11 +01:00
* Value pair description :
* { " Name " : " SDM230 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A}
* Modbus config parameters :
2022-12-23 15:56:18 +00:00
* Name - Name of energy monitoring device ( s )
2022-10-11 17:39:48 +01:00
* Baud - Baudrate of device modbus interface - optional . default is 9600
2022-10-08 15:14:11 +01:00
* Config - Serial config parameters like 8 N1 - 8 databits , No parity , 1 stop bit
2022-12-24 14:59:29 +00:00
* Poll - Time between modbus requests - optional . default is 200 milliseconds
2022-12-23 15:56:18 +00:00
* Address - Modbus device address entered as decimal ( 1 ) or hexadecimal ( 0x01 ) or up to three addresses ( [ 1 , 2 , 3 ] ) - optional . default = 1
* Function - Modbus function code to access registers - optional . default = 4
2022-10-08 15:14:11 +01:00
* Tasmota default embedded register names :
2022-10-11 17:39:48 +01:00
* Voltage - Voltage register entered as decimal or hexadecimal for one phase ( 0x0000 ) or up to three phases ( [ 0x0000 , 0x0002 , 0x0004 ] ) or
* Additional defined parameters
* Value pair description :
2022-12-23 10:39:13 +00:00
* { " R " : 0 , " T " : 0 , " F " : 0 }
2022-10-11 17:39:48 +01:00
* R - Modbus register entered as decimal or hexadecimal for one phase ( 0x0160 ) or up to three phases ( [ 0x0160 , 0x0162 , 0x0164 ] )
* T - Datatype - optional . default is 0 - float :
* 0 - float
* 1 = 2 - byte signed
* 2 = 4 - byte signed
* 3 = 2 - byte unsigned
* 4 = 4 - byte unsigned
2022-12-21 17:11:35 +00:00
* 5 = not used
* 6 = 4 - byte signed with swapped words
* 7 = not used
* 8 = 4 - byte unsigned with swapped words
2022-12-23 10:39:13 +00:00
* F - Register factor positive for multiplication or negative for division - optional . default is 0 - no action
* - 4 - divide by 10000
* - 3 - divide by 1000
* - 2 - divide by 100
* - 1 - divide by 10
* 0 - no action
* 1 - multiply by 10
* 2 - multiply by 100
* 3 - multiply by 1000
* 4 - multiply by 10000
* M - [ LEGACY - replaced by " F " ] Divide register by 1 to 10000 - optional . default = 0 ( no action )
2022-10-11 17:39:48 +01:00
* Current - Current register entered as decimal or hexadecimal for one phase ( 0x0006 ) or up to three phases ( [ 0x0006 , 0x0008 , 0x000A ] ) or
* See additional defines like voltage .
* Power - Active power register entered as decimal or hexadecimal for one phase ( 0x000C ) or up to three phases ( [ 0x000C , 0x000E , 0x0010 ] ) or
* See additional defines like voltage .
* ApparentPower - Apparent power register entered as decimal or hexadecimal for one phase ( 0x000C ) or up to three phases ( [ 0x000C , 0x000E , 0x0010 ] ) or
* See additional defines like voltage .
* ReactivePower - Reactive power register entered as decimal or hexadecimal for one phase ( 0x0018 ) or up to three phases ( [ 0x0018 , 0x001A , 0x001C ] ) or
* See additional defines like voltage .
* Factor - Power factor register entered as decimal or hexadecimal for one phase ( 0x001E ) or up to three phases ( [ 0x001E , 0x0020 , 0x0022 ] ) or
* See additional defines like voltage .
* Frequency - Frequency register entered as decimal or hexadecimal for one phase ( 0x0046 ) or up to three phases ( [ 0x0046 , 0x0048 , 0x004A ] ) or
* See additional defines like voltage .
* Total - Total active energy register entered as decimal or hexadecimal for one phase ( 0x0156 ) or up to three phases ( [ 0x015A , 0x015C , 0x015E ] ) or
* See additional defines like voltage .
* ExportActive - Export active energy register entered as decimal or hexadecimal for one phase ( 0x0160 ) or up to three phases ( [ 0x0160 , 0x0162 , 0x0164 ] ) or
* See additional defines like voltage .
2022-10-08 15:14:11 +01:00
* Optional user defined registers :
* User - Additional user defined registers
2022-10-08 16:17:15 +01:00
* Value pair description :
2022-12-23 10:39:13 +00:00
* " User " : { " R " : 0x0024 , " T " : 0 , " F " : 0 , " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " : 2 }
2022-10-08 16:17:15 +01:00
* R - Modbus register entered as decimal or hexadecimal for one phase ( 0x0160 ) or up to three phases ( [ 0x0160 , 0x0162 , 0x0164 ] )
2022-10-11 17:39:48 +01:00
* T - Datatype - optional . default is 0 - float :
* 0 - float
* 1 = 2 - byte signed
* 2 = 4 - byte signed
* 3 = 2 - byte unsigned
* 4 = 4 - byte unsigned
2022-12-21 17:11:35 +00:00
* 5 = not used
* 6 = 4 - byte signed with swapped words
* 7 = not used
* 8 = 4 - byte unsigned with swapped words
2022-12-23 10:39:13 +00:00
* F - Register factor positive for multiplication or negative for division - optional . default is 0 - no action
* - 4 - divide by 10000
* - 3 - divide by 1000
* - 2 - divide by 100
* - 1 - divide by 10
* 0 - no action
* 1 - multiply by 10
* 2 - multiply by 100
* 3 - multiply by 1000
* 4 - multiply by 10000
* M - [ LEGACY - replaced by " F " ] Divide register by 1 to 10000 - optional . default = 0 ( no action )
2022-12-28 16:45:13 +00:00
* J - JSON register name ( preferrably without spaces like " PhaseAngle " ) - mandatory . It needs to be different from the Tasmota default embedded register names
* G - GUI register name - optional . If not defined the register will not be shown in the GUI
* U - GUI unit name - optional . default is none
2022-10-11 17:39:48 +01:00
* D - Number of decimals for floating point presentation ( 0 to 20 ) or a code correspondig to Tasmota resolution command settings :
2022-10-08 16:17:15 +01:00
* 21 - VoltRes ( V )
* 22 - AmpRes ( A )
* 23 - WattRes ( W , VA , VAr )
* 24 - EnergyRes ( kWh , kVAh , kVArh )
* 25 - FreqRes ( Hz )
* 26 - TempRes ( C , F )
* 27 - HumRes ( % )
* 28 - PressRes ( hPa , mmHg )
* 29 - WeightRes ( Kg )
2022-10-07 09:46:25 +01:00
*
2022-10-08 15:14:11 +01:00
* Example using default Energy registers :
* rule3 on file # modbus do { " Name " : " SDM230 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A} endon
* rule3 on file # modbus do { " Name " : " SDM230 with hex registers " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0x0000, " Current " :0x0006, " Power " :0x000C, " ApparentPower " :0x0012, " ReactivePower " :0x0018, " Factor " :0x001E, " Frequency " :0x0046, " Total " :0x0156, " ExportActive " :0x004A} endon
2022-10-09 17:38:30 +01:00
* rule3 on file # modbus do { " Name " : " DDSU666 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0x2000, " Current " :0x2002, " Power " :0x2004, " ReactivePower " :0x2006, " Factor " :0x200A, " Frequency " :0x200E, " Total " :0x4000, " ExportActive " :0x400A} endon
2022-12-23 13:26:25 +00:00
* rule3 on file # modbus do { " Name " : " PZEM014 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :{ " R " :0, " T " :3, " F " :-1}, " Current " :{ " R " :1, " T " :8, " F " :-3}, " Power " :{ " R " :3, " T " :8, " F " :-1}, " Factor " :{ " R " :8, " T " :3, " F " :-2}, " Frequency " :{ " R " :7, " T " :3, " F " :-1}, " Total " :{ " R " :5, " T " :8, " F " :-3}} endon
2022-12-23 15:56:18 +00:00
* rule3 on file # modbus do { " Name " : " 3 x PZEM014 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :[1,2,3], " Function " :4, " Voltage " :{ " R " :0, " T " :3, " F " :-1}, " Current " :{ " R " :1, " T " :8, " F " :-3}, " Power " :{ " R " :3, " T " :8, " F " :-1}, " Factor " :{ " R " :8, " T " :3, " F " :-2}, " Frequency " :{ " R " :7, " T " :3, " F " :-1}, " Total " :{ " R " :5, " T " :8, " F " :-3}} endon
2022-12-23 13:26:25 +00:00
* rule3 on file # modbus do { " Name " : " Solax X3MIC " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :{ " R " :0x0404, " T " :3, " F " :-1}, " Power " :{ " R " :0x040e, " T " :3, " F " :0}, " Total " :{ " R " :0x0423, " T " :8, " F " :-3}} endon
2022-10-08 15:14:11 +01:00
*
* Example using default Energy registers and some user defined registers :
2022-10-09 17:38:30 +01:00
* rule3 on file # modbus do { " Name " : " SDM72 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :0x01, " Function " :0x04, " Power " :0x0034, " Total " :0x0156, " ExportActive " :0x004A, " User " :[{ " R " :0x0502, " J " : " ImportActive " , " G " : " Import Active " , " U " : " kWh " , " D " :24},{ " R " :0x0502, " J " : " ExportPower " , " G " : " Export Power " , " U " : " W " , " D " :23},{ " R " :0x0500, " J " : " ImportPower " , " G " : " Import Power " , " U " : " W " , " D " :23}]} endon
* rule3 on file # modbus do { " Name " : " SDM120 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A, " User " :[{ " R " :0x0048, " J " : " ImportActive " , " G " : " Import Active " , " U " : " kWh " , " D " :24},{ " R " :0x004E, " J " : " ExportReactive " , " G " : " Export Reactive " , " U " : " kVArh " , " D " :24},{ " R " :0x004C, " J " : " ImportReactive " , " G " : " Import Reactive " , " U " : " kVArh " , " D " :24},{ " R " :0x0024, " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " :2}]} endon
2022-10-08 15:14:11 +01:00
* rule3 on file # modbus do { " Name " : " SDM230 with two user registers " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A, " User " :[{ " R " :0x004E, " J " : " ExportReactive " , " G " : " Export Reactive " , " U " : " kVArh " , " D " :3},{ " R " :0x0024, " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " :2}]} endon
2022-10-09 17:38:30 +01:00
* rule3 on file # modbus do { " Name " : " SDM630 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :[0,2,4], " Current " :[6,8,10], " Power " :[12,14,16], " ApparentPower " :[18,20,22], " ReactivePower " :[24,26,28], " Factor " :[30,32,34], " Frequency " :70, " Total " :342, " ExportActive " :[352,354,356], " User " :{ " R " :[346,348,350], " J " : " ImportActive " , " G " : " Import Active " , " U " : " kWh " , " D " :24}} endon
2022-10-08 15:14:11 +01:00
*
* Note :
2022-10-09 17:38:30 +01:00
* - To enter long rules using the serial console and solve error " Serial buffer overrun " you might need to enlarge the serial input buffer with command serialbuffer 800
2022-10-08 15:14:11 +01:00
* - Changes to rule file are only executed on restart
*
* Restrictions :
2022-12-23 13:26:25 +00:00
* - Supports Modbus single and double integer registers in addition to floating point registers
2022-10-23 17:21:12 +01:00
* - Max number of user defined registers is defined by one rule buffer ( 511 characters uncompressed , around 800 characters compressed )
2022-10-08 15:14:11 +01:00
*
* To do :
* - Support all three rule slots
* - Support other modbus register like integers
2022-10-06 22:17:04 +01:00
*
* Test set :
2022-12-24 14:59:29 +00:00
* rule3 on file # modbus do { " Name " : " GROWATT " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :11, " Function " :4, " Voltage " :{ " R " :[4110,4114,4118], " T " :3, " F " :-1}, " Current " :{ " R " :[4111,4115,4119], " T " :3, " F " :-1}, " Power " :{ " R " :[4112,4116,4120], " T " :8, " F " :-1}, " Frequency " :{ " R " :4109, " T " :3, " F " :-2}, " Total " :{ " R " :4124, " T " :8, " F " :-1}, " User " :[{ " R " :[4099,4103], " J " : " VoltagePV " , " G " : " Voltage PV " , " U " : " V " , " D " :21, " T " :3, " F " :-1},{ " R " :[4100,4104], " J " : " CurrentPV " , " G " : " Current PV " , " U " : " A " , " D " :22, " T " :3, " F " :-1},{ " R " :[4101,4105], " J " : " PowerPV " , " G " : " Power PV " , " U " : " W " , " D " :23, " T " :8, " F " :-1}]} endon
* rule3 on file # modbus do { " Name " : " 2 x PZEM014 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :[1,1], " Function " :4, " Voltage " :{ " R " :0, " T " :3, " F " :-1}, " Current " :{ " R " :1, " T " :8, " F " :-3}, " Power " :{ " R " :3, " T " :8, " F " :-1}, " Factor " :{ " R " :8, " T " :3, " F " :-2}, " Frequency " :{ " R " :7, " T " :3, " F " :-1}, " Total " :{ " R " :5, " T " :8, " F " :-3}} endon
2022-10-08 15:14:11 +01:00
* rule3 on file # modbus do { " Name " : " SDM230 test1 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :[0,0,0], " Current " :[6,6,6], " Power " :[12,12,12], " ApparentPower " :[18,18,18], " ReactivePower " :[24,24,24], " Factor " :[30,30,30], " Frequency " :[70,70,70], " Total " :[342,342,342]} endon
* rule3 on file # modbus do { " Name " : " SDM230 test2 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :[0,0,0], " Current " :[6,6,6], " Power " :[12,12,12], " ApparentPower " :[18,18,18], " ReactivePower " :[24,24,24], " Factor " :[30,30,30], " Frequency " :70, " Total " :[342,342,342]} endon
* rule3 on file # modbus do { " Name " : " SDM230 test3 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :[6,6,6], " Power " :[12,12,12], " ApparentPower " :[18,18,18], " ReactivePower " :[24,24,24], " Factor " :[30,30,30], " Frequency " :70, " Total " :[342,342,342]} endon
2022-10-08 16:17:15 +01:00
* rule3 on file # modbus do { " Name " : " SDM230 test4 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A, " User " :[{ " R " :0x004E, " J " : " ExportReactive " , " G " : " Export Reactive " , " U " : " kVArh " , " D " :24},{ " R " :0x0024, " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " :2}]} endon
2022-10-08 15:14:11 +01:00
* rule3 on file # modbus do { " Name " : " SDM230 test5 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :[0,0,0], " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A, " User " :[{ " R " :[0x004E,0x004E,0x004E], " J " : " ExportReactive " , " G " : " Export Reactive " , " U " : " kVArh " , " D " :3},{ " R " :0x0024, " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " :2}]} endon
2022-10-09 17:38:30 +01:00
* rule3 on file # modbus do { " Name " : " SDM120 test1 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :0, " Current " :6, " Power " :12, " ApparentPower " :18, " ReactivePower " :24, " Factor " :30, " Frequency " :70, " Total " :342, " ExportActive " :0x004A, " User " :[{ " R " :0x0048, " J " : " ImportActive " , " G " : " Import Active " , " U " : " kWh " , " D " :24},{ " R " :0x004E, " J " : " ExportReactive " , " G " : " Export Reactive " , " U " : " kVArh " , " D " :24},{ " R " :0x004C, " J " : " ImportReactive " , " G " : " Import Reactive " , " U " : " kVArh " , " D " :24},{ " R " :0x0024, " J " : " PhaseAngle " , " G " : " Phase Angle " , " U " : " Deg " , " D " :2}]} endon
2022-12-24 14:59:29 +00:00
* rule3 on file # modbus do { " Name " : " PZEM014 test1 " , " Baud " : 9600 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :{ " R " :0, " T " :3, " F " :-1}, " Current " :{ " R " :1, " T " :8, " F " :-3}, " Power " :{ " R " :3, " T " :8, " F " :-1}, " Factor " :{ " R " :8, " T " :3, " F " :-2}, " Frequency " :{ " R " :7, " T " :3, " F " :-1}, " Total " :{ " R " :5, " T " :8, " F " :-3}, " User " :{ " R " :0, " J " : " VoltageTest " , " G " : " Voltage test " , " U " : " V " , " D " :21, " T " :3, " F " :-1}} endon
2022-10-11 10:10:47 +01:00
*
* rule3 on file # modbus do { " Name " : " SDM230 test6 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :{ " R " :0, " T " :0, " M " :1}, " Current " :{ " R " :6, " T " :0, " M " :1}, " Power " :{ " R " :12, " T " :0, " M " :1}, " Frequency " :70, " Total " :342} endon
2022-12-23 10:39:13 +00:00
* rule3 on file # modbus do { " Name " : " SDM230 test6 " , " Baud " : 2400 , " Config " : 8 N1 " , " Address " :1, " Function " :4, " Voltage " :{ " R " :0, " T " :0, " F " :0}, " Current " :{ " R " :6, " T " :0, " F " :0}, " Power " :{ " R " :12, " T " :0, " F " :0}, " Frequency " :70, " Total " :342, " User " :{ " R " :0x0048, " T " :0, " F " :-1, " J " : " ImportActive " , " G " : " Import Active " , " U " : " kWh " , " D " :24}} endon
2022-10-06 22:17:04 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-11 10:10:47 +01:00
# define XNRG_29 29
2022-10-06 22:17:04 +01:00
2022-10-11 10:10:47 +01:00
# define ENERGY_MODBUS_SPEED 9600 // Default Modbus baudrate
# define ENERGY_MODBUS_CONFIG TS_SERIAL_8N1 // Default Modbus serial configuration
# define ENERGY_MODBUS_ADDR 1 // Default Modbus device_address
# define ENERGY_MODBUS_FUNC 0x04 // Default Modbus function code
2022-12-23 15:56:18 +00:00
# define ENERGY_MODBUS_MAX_DEVICES ENERGY_MAX_PHASES // Support up to three single phase devices as three phases
2022-10-09 17:38:30 +01:00
2022-10-11 10:10:47 +01:00
# define ENERGY_MODBUS_DATATYPE 0 // Default Modbus datatype is 4-byte float
# define ENERGY_MODBUS_DECIMALS 0 // Default user decimal resolution
2022-10-07 09:46:25 +01:00
2022-10-11 17:39:48 +01:00
# define ENERGY_MODBUS_TICKER // Enable for ESP8266 when using softwareserial solving most modbus serial retries
2022-12-24 14:59:29 +00:00
# define ENERGY_MODBUS_TICKER_POLL 200 // Modbus poll time in ms between read register requests
2022-10-11 17:39:48 +01:00
2022-10-11 10:15:25 +01:00
//#define ENERGY_MODBUS_DEBUG
2022-10-11 10:10:47 +01:00
//#define ENERGY_MODBUS_DEBUG_SHOW
2022-10-06 22:17:04 +01:00
2022-12-23 13:26:25 +00:00
const uint16_t nrg_mbs_reg_not_used = 0xFFFF ; // Odd number 65535 is unused register
2022-10-11 10:10:47 +01:00
2022-12-23 15:56:18 +00:00
// Even data type is single (2-byte) register, Odd data type is double (4-byte) registers
2022-12-23 13:26:25 +00:00
enum EnergyModbusDataType { NRG_DT_FLOAT , // 0 = 4-byte float
NRG_DT_S16 , // 1 = 2-byte signed
NRG_DT_S32 , // 2 = 4-byte signed
NRG_DT_U16 , // 3 = 2-byte unsigned
NRG_DT_U32 , // 4 = 4-byte unsigned
2022-12-23 15:56:18 +00:00
NRG_DT_x16_nu1 , // 5 = 2-byte
2022-12-23 13:26:25 +00:00
NRG_DT_S32_SW , // 6 = 4-byte signed with swapped words
2022-12-23 15:56:18 +00:00
NRG_DT_x16_nu2 , // 7 = 2-byte
2022-12-23 13:26:25 +00:00
NRG_DT_U32_SW , // 8 = 4-byte unsigned with swapped words
2022-10-11 10:10:47 +01:00
NRG_DT_MAX } ;
2022-12-24 14:59:29 +00:00
enum EnergyModbusResolutions { NRG_RES_VOLTAGE = 21 , // 21 = V
NRG_RES_CURRENT , // 22 = A
NRG_RES_POWER , // 23 = W, VA, VAr
NRG_RES_ENERGY , // 24 = kWh, kVAh, kVArh
NRG_RES_FREQUENCY , // 25 = Hz
NRG_RES_TEMPERATURE , // 26 = C, F
NRG_RES_HUMIDITY , // 27 = %
NRG_RES_PRESSURE , // 28 = hPa, mmHg
NRG_RES_WEIGHT } ; // 29 = Kg
2022-10-08 16:17:15 +01:00
2022-10-06 22:17:04 +01:00
enum EnergyModbusRegisters { NRG_MBS_VOLTAGE ,
NRG_MBS_CURRENT ,
NRG_MBS_ACTIVE_POWER ,
NRG_MBS_APPARENT_POWER ,
NRG_MBS_REACTIVE_POWER ,
NRG_MBS_POWER_FACTOR ,
NRG_MBS_FREQUENCY ,
2022-10-08 15:14:11 +01:00
NRG_MBS_TOTAL_ENERGY ,
2022-10-06 22:17:04 +01:00
NRG_MBS_EXPORT_ACTIVE_ENERGY ,
NRG_MBS_MAX_REGS } ;
2022-10-07 09:46:25 +01:00
const char kEnergyModbusValues [ ] PROGMEM = D_JSON_VOLTAGE " | " // Voltage
D_JSON_CURRENT " | " // Current
D_JSON_POWERUSAGE " | " // Power
D_JSON_APPARENT_POWERUSAGE " | " // ApparentPower
D_JSON_REACTIVE_POWERUSAGE " | " // ReactivePower
D_JSON_POWERFACTOR " | " // Factor
D_JSON_FREQUENCY " | " // Frequency
2022-10-08 15:14:11 +01:00
D_JSON_TOTAL " | " // Total
2022-10-07 09:46:25 +01:00
D_JSON_EXPORT_ACTIVE " | " // ExportActive
;
2022-10-06 22:17:04 +01:00
# include <TasmotaModbus.h>
TasmotaModbus * EnergyModbus ;
2022-10-11 17:39:48 +01:00
# ifdef ENERGY_MODBUS_TICKER
2022-10-09 17:38:30 +01:00
# include <Ticker.h>
Ticker ticker_energy_modbus ;
2022-10-11 17:39:48 +01:00
# endif // ENERGY_MODBUS_TICKER
2022-10-11 10:10:47 +01:00
struct NRGMBSPARAM {
uint32_t serial_bps ;
uint32_t serial_config ;
2022-12-24 14:59:29 +00:00
uint16_t ticker_poll ;
2022-12-23 15:56:18 +00:00
uint8_t device_address [ ENERGY_MODBUS_MAX_DEVICES ] ;
uint8_t devices ;
2022-10-11 10:10:47 +01:00
uint8_t function ;
uint8_t total_regs ;
uint8_t user_adds ;
uint8_t state ;
uint8_t retry ;
2022-12-28 16:06:54 +00:00
int8_t phase ;
2022-10-11 10:10:47 +01:00
bool mutex ;
} NrgMbsParam ;
typedef struct NRGMBSREGISTER {
uint16_t address [ ENERGY_MAX_PHASES ] ;
2022-12-23 10:39:13 +00:00
int16_t factor ;
2022-10-11 10:10:47 +01:00
uint32_t datatype ;
} NrgMbsRegister_t ;
2022-10-11 17:39:48 +01:00
NrgMbsRegister_t * NrgMbsReg = nullptr ;
2022-10-11 10:10:47 +01:00
typedef struct NRGMBSUSER {
float data [ ENERGY_MAX_PHASES ] ;
char * json_name ;
char * gui_name ;
char * gui_unit ;
uint32_t resolution ;
} NrgMbsUser_t ;
2022-10-11 17:39:48 +01:00
NrgMbsUser_t * NrgMbsUser = nullptr ;
2022-10-11 10:10:47 +01:00
/*********************************************************************************************/
2022-10-06 22:17:04 +01:00
void EnergyModbusLoop ( void ) {
2022-10-11 17:39:48 +01:00
# ifdef ENERGY_MODBUS_TICKER
if ( NrgMbsParam . mutex | | TasmotaGlobal . ota_state_flag ) { return ; }
# else
2022-10-11 10:10:47 +01:00
if ( NrgMbsParam . mutex ) { return ; }
2022-10-11 17:39:48 +01:00
# endif // ENERGY_MODBUS_TICKER
2022-10-11 10:10:47 +01:00
NrgMbsParam . mutex = 1 ;
2022-10-09 17:38:30 +01:00
2022-10-11 10:10:47 +01:00
uint32_t register_count ;
2022-10-08 15:14:11 +01:00
2022-10-06 22:17:04 +01:00
bool data_ready = EnergyModbus - > ReceiveReady ( ) ;
if ( data_ready ) {
2022-10-11 10:10:47 +01:00
uint8_t buffer [ 15 ] ; // At least 5 + (2 * 2) = 9
2022-12-23 15:56:18 +00:00
// Even data type is single register, Odd data type is double registers
2022-10-11 10:10:47 +01:00
register_count = 2 - ( NrgMbsReg [ NrgMbsParam . state ] . datatype & 1 ) ;
uint32_t error = EnergyModbus - > ReceiveBuffer ( buffer , register_count ) ;
2022-10-06 22:17:04 +01:00
if ( error ) {
/* Return codes from TasmotaModbus.h:
* 0 = No error
* 1 = Illegal Function ,
* 2 = Illegal Data Address ,
* 3 = Illegal Data Value ,
* 4 = Slave Error
* 5 = Acknowledge but not finished ( no error )
* 6 = Slave Busy
* 7 = Not enough minimal data received
* 8 = Memory Parity error
* 9 = Crc error
* 10 = Gateway Path Unavailable
* 11 = Gateway Target device failed to respond
* 12 = Wrong number of registers
* 13 = Register data not specified
* 14 = To many registers
*/
2022-12-30 15:56:36 +00:00
# ifdef ENERGY_MODBUS_DEBUG
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " NRG: Modbus register %d, phase %d, rcvd %*_H " ) ,
NrgMbsParam . state , NrgMbsParam . phase , EnergyModbus - > ReceiveCount ( ) , buffer ) ;
# endif
2022-10-06 22:17:04 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Modbus error %d " ) , error ) ;
} else {
2022-12-22 16:54:54 +00:00
/* Modbus protocol format:
* SA = Device Address
* FC = Function Code
* BC = Byte count
* Fh = First or High word MSB
* Fl = First or High word LSB
* Sh = Second or Low word MSB
* Sl = Second or Low word LSB
* Cl = CRC lsb
* Ch = CRC msb
*/
2022-10-11 10:10:47 +01:00
Energy . data_valid [ NrgMbsParam . phase ] = 0 ;
2022-10-06 22:17:04 +01:00
float value ;
2022-10-11 10:10:47 +01:00
switch ( NrgMbsReg [ NrgMbsParam . state ] . datatype ) {
2022-12-22 16:54:54 +00:00
case NRG_DT_FLOAT : { // 0
// 0 1 2 3 4 5 6 7 8
// SA FC BC Fh Fl Sh Sl Cl Ch
// 01 04 04 43 66 33 34 1B 38 = 230.2 Volt
2022-10-11 10:10:47 +01:00
( ( uint8_t * ) & value ) [ 3 ] = buffer [ 3 ] ; // Get float values
( ( uint8_t * ) & value ) [ 2 ] = buffer [ 4 ] ;
( ( uint8_t * ) & value ) [ 1 ] = buffer [ 5 ] ;
( ( uint8_t * ) & value ) [ 0 ] = buffer [ 6 ] ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_S16 : { // 1
// 0 1 2 3 4 5 6
// SA FC BC Fh Fl Cl Ch
2022-10-11 10:10:47 +01:00
int16_t value_buff = ( ( int16_t ) buffer [ 3 ] ) < < 8 | buffer [ 4 ] ;
value = ( float ) value_buff ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_U16 : { // 3
// 0 1 2 3 4 5 6
// SA FC BC Fh Fl Cl Ch
2022-10-11 10:10:47 +01:00
uint16_t value_buff = ( ( uint16_t ) buffer [ 3 ] ) < < 8 | buffer [ 4 ] ;
value = ( float ) value_buff ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_S32 : { // 2
// 0 1 2 3 4 5 6 7 8
// SA FC BC Fh Fl Sh Sl Cl Ch
2022-10-11 10:10:47 +01:00
int32_t value_buff = ( ( int32_t ) buffer [ 3 ] ) < < 24 | ( ( uint32_t ) buffer [ 4 ] ) < < 16 | ( ( uint32_t ) buffer [ 5 ] ) < < 8 | buffer [ 6 ] ;
value = ( float ) value_buff ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_S32_SW : { // 6
// 0 1 2 3 4 5 6 7 8
// SA FC BC Sh Sl Fh Fl Cl Ch
2022-12-21 17:11:35 +00:00
int32_t value_buff = ( ( int32_t ) buffer [ 5 ] ) < < 24 | ( ( uint32_t ) buffer [ 6 ] ) < < 16 | ( ( uint32_t ) buffer [ 3 ] ) < < 8 | buffer [ 4 ] ;
value = ( float ) value_buff ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_U32 : { // 4
// 0 1 2 3 4 5 6 7 8
// SA FC BC Fh Fl Sh Sl Cl Ch
2022-10-11 10:10:47 +01:00
uint32_t value_buff = ( ( uint32_t ) buffer [ 3 ] ) < < 24 | ( ( uint32_t ) buffer [ 4 ] ) < < 16 | ( ( uint32_t ) buffer [ 5 ] ) < < 8 | buffer [ 6 ] ;
value = ( float ) value_buff ;
break ;
}
2022-12-22 16:54:54 +00:00
case NRG_DT_U32_SW : { // 8
// 0 1 2 3 4 5 6 7 8
// SA FC BC Sh Sl Fh Fl Cl Ch
// 01 04 04 EB EC 00 0E 8E 51 = 977.9000 (Solax protocol X1&X3)
2022-12-21 17:11:35 +00:00
uint32_t value_buff = ( ( uint32_t ) buffer [ 5 ] ) < < 24 | ( ( uint32_t ) buffer [ 6 ] ) < < 16 | ( ( uint32_t ) buffer [ 3 ] ) < < 8 | buffer [ 4 ] ;
value = ( float ) value_buff ;
break ;
}
2022-10-11 10:10:47 +01:00
}
2022-12-23 10:39:13 +00:00
uint32_t factor = 1 ;
// 1 = 10, 2 = 100, 3 = 1000, 4 = 10000
uint32_t scaler = abs ( NrgMbsReg [ NrgMbsParam . state ] . factor ) ;
while ( scaler ) {
factor * = 10 ;
scaler - - ;
}
if ( NrgMbsReg [ NrgMbsParam . state ] . factor < 0 ) {
value / = factor ;
2022-12-22 16:54:54 +00:00
} else {
2022-12-23 10:39:13 +00:00
value * = factor ;
2022-12-22 16:54:54 +00:00
}
2022-10-06 22:17:04 +01:00
2022-12-30 15:56:36 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " NRG: Modbus register %d, phase %d, rcvd %*_H, T %d, F %d, value %4_f " ) ,
NrgMbsParam . state , NrgMbsParam . phase , EnergyModbus - > ReceiveCount ( ) , buffer ,
NrgMbsReg [ NrgMbsParam . state ] . datatype , NrgMbsReg [ NrgMbsParam . state ] . factor , & value ) ;
2022-10-11 10:10:47 +01:00
switch ( NrgMbsParam . state ) {
2022-10-06 22:17:04 +01:00
case NRG_MBS_VOLTAGE :
2022-10-11 10:10:47 +01:00
Energy . voltage [ NrgMbsParam . phase ] = value ; // 230.2 V
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_CURRENT :
2022-10-11 10:10:47 +01:00
Energy . current [ NrgMbsParam . phase ] = value ; // 1.260 A
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_ACTIVE_POWER :
2022-10-11 10:10:47 +01:00
Energy . active_power [ NrgMbsParam . phase ] = value ; // -196.3 W
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_APPARENT_POWER :
2022-10-11 10:10:47 +01:00
Energy . apparent_power [ NrgMbsParam . phase ] = value ; // 223.4 VA
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_REACTIVE_POWER :
2022-10-11 10:10:47 +01:00
Energy . reactive_power [ NrgMbsParam . phase ] = value ; // 92.2
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_POWER_FACTOR :
2022-10-11 10:10:47 +01:00
Energy . power_factor [ NrgMbsParam . phase ] = value ; // -0.91
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_FREQUENCY :
2022-10-11 10:10:47 +01:00
Energy . frequency [ NrgMbsParam . phase ] = value ; // 50.0 Hz
2022-10-06 22:17:04 +01:00
break ;
2022-10-08 15:14:11 +01:00
case NRG_MBS_TOTAL_ENERGY :
2022-10-11 10:10:47 +01:00
Energy . import_active [ NrgMbsParam . phase ] = value ; // 6.216 kWh => used in EnergyUpdateTotal()
2022-10-06 22:17:04 +01:00
break ;
case NRG_MBS_EXPORT_ACTIVE_ENERGY :
2022-10-11 10:10:47 +01:00
Energy . export_active [ NrgMbsParam . phase ] = value ; // 478.492 kWh
2022-10-06 22:17:04 +01:00
break ;
2022-10-08 15:14:11 +01:00
default :
2022-10-11 10:10:47 +01:00
if ( NrgMbsUser ) {
NrgMbsUser [ NrgMbsParam . state - NRG_MBS_MAX_REGS ] . data [ NrgMbsParam . phase ] = value ;
2022-10-08 15:14:11 +01:00
}
2022-10-06 22:17:04 +01:00
}
}
} // end data ready
2022-10-11 10:10:47 +01:00
if ( 0 = = NrgMbsParam . retry | | data_ready ) {
NrgMbsParam . retry = 1 ;
2022-12-28 16:06:54 +00:00
uint32_t address = 0 ;
uint32_t phase = 0 ;
do {
NrgMbsParam . phase + + ;
if ( NrgMbsParam . phase > = Energy . phase_count ) {
NrgMbsParam . phase = 0 ;
NrgMbsParam . state + + ;
if ( NrgMbsParam . state > = NrgMbsParam . total_regs ) {
NrgMbsParam . state = 0 ;
NrgMbsParam . phase = 0 ;
EnergyUpdateTotal ( ) ; // update every cycle after all registers have been read
}
}
delay ( 0 ) ;
if ( NrgMbsParam . devices = = 1 ) {
phase = NrgMbsParam . phase ;
} else {
address = NrgMbsParam . phase ;
}
} while ( NrgMbsReg [ NrgMbsParam . state ] . address [ phase ] = = nrg_mbs_reg_not_used ) ;
2022-12-23 15:56:18 +00:00
// Even data type is single register, Odd data type is double registers
2022-10-11 10:10:47 +01:00
register_count = 2 - ( NrgMbsReg [ NrgMbsParam . state ] . datatype & 1 ) ;
2022-12-28 16:06:54 +00:00
# ifdef ENERGY_MODBUS_DEBUG
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " NRG: Modbus send Device %d, Function %d, Register %04X (%d/%d), Size %d " ) ,
NrgMbsParam . device_address [ address ] , NrgMbsParam . function ,
NrgMbsReg [ NrgMbsParam . state ] . address [ phase ] , NrgMbsParam . state , phase ,
register_count ) ;
# endif
2022-12-23 15:56:18 +00:00
EnergyModbus - > Send ( NrgMbsParam . device_address [ address ] , NrgMbsParam . function , NrgMbsReg [ NrgMbsParam . state ] . address [ phase ] , register_count ) ;
2022-10-06 22:17:04 +01:00
} else {
2022-10-11 10:10:47 +01:00
NrgMbsParam . retry - - ;
2022-10-09 17:38:30 +01:00
# ifdef ENERGY_MODBUS_DEBUG
2022-12-23 15:56:18 +00:00
if ( NrgMbsParam . devices > 1 ) {
2022-12-28 16:06:54 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Modbus retry device %d state %d " ) , NrgMbsParam . device_address [ NrgMbsParam . phase ] , NrgMbsParam . state ) ;
2022-12-23 15:56:18 +00:00
} else {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Modbus retry state %d phase %d " ) , NrgMbsParam . state , NrgMbsParam . phase ) ;
}
2022-10-09 17:38:30 +01:00
# endif
2022-10-06 22:17:04 +01:00
}
2022-10-09 17:38:30 +01:00
delay ( 0 ) ;
2022-10-11 10:10:47 +01:00
NrgMbsParam . mutex = 0 ;
2022-10-06 22:17:04 +01:00
}
2022-10-08 15:14:11 +01:00
# ifdef USE_RULES
2022-12-24 14:59:29 +00:00
uint32_t EnergyModbusReadRegisterInfo ( JsonParserObject add_value , uint32_t reg_index ) {
// {"R":0,"T":0,"F":0}
// {"R":[0,2,4],"T":0,"F":0}
// {"R":[0,2,4],"T":0,"M":10} - [LEGACY]
2022-10-08 15:14:11 +01:00
uint32_t phase = 0 ;
2022-12-24 14:59:29 +00:00
JsonParserToken val ;
val = add_value [ PSTR ( " R " ) ] ; // Register address
2022-10-08 15:14:11 +01:00
if ( val . isArray ( ) ) {
2022-12-24 14:59:29 +00:00
// [0,2,4]
2022-10-08 15:14:11 +01:00
JsonParserArray address_arr = val . getArray ( ) ;
for ( auto value : address_arr ) {
2022-10-11 10:10:47 +01:00
NrgMbsReg [ reg_index ] . address [ phase ] = value . getUInt ( ) ;
2022-10-08 15:14:11 +01:00
phase + + ;
2022-10-09 17:38:30 +01:00
if ( phase > = ENERGY_MAX_PHASES ) { break ; }
2022-10-08 15:14:11 +01:00
}
} else if ( val ) {
2022-12-24 14:59:29 +00:00
// 0
2022-10-11 10:10:47 +01:00
NrgMbsReg [ reg_index ] . address [ 0 ] = val . getUInt ( ) ;
2022-10-08 15:14:11 +01:00
phase + + ;
}
2022-12-24 14:59:29 +00:00
val = add_value [ PSTR ( " T " ) ] ; // Register data type
2022-10-11 10:10:47 +01:00
if ( val ) {
2022-12-24 14:59:29 +00:00
// 0
2022-10-11 10:10:47 +01:00
NrgMbsReg [ reg_index ] . datatype = val . getUInt ( ) ;
}
2022-12-24 14:59:29 +00:00
val = add_value [ PSTR ( " F " ) ] ; // Register factor
2022-10-11 10:10:47 +01:00
if ( val ) {
2022-12-24 14:59:29 +00:00
// 1 or -2
2022-12-23 10:39:13 +00:00
NrgMbsReg [ reg_index ] . factor = val . getInt ( ) ;
}
2022-12-24 14:59:29 +00:00
val = add_value [ PSTR ( " M " ) ] ; // [LEGACY] Register divider
2022-12-23 10:39:13 +00:00
if ( val ) {
2022-12-24 14:59:29 +00:00
// 1
2022-12-23 10:39:13 +00:00
int32_t divider = val . getUInt ( ) ;
int factor = 0 ;
while ( divider > 1 ) {
divider / = 10 ;
factor - - ;
}
NrgMbsReg [ reg_index ] . factor = factor ;
2022-10-11 10:10:47 +01:00
}
2022-12-24 14:59:29 +00:00
return phase ;
}
bool EnergyModbusReadUserRegisters ( JsonParserObject user_add_value , uint32_t add_index ) {
// {"R":0x004E,"T":0,"F":0,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0}
// {"R":[0,2,4],"T":0,"F":0,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0}
uint32_t reg_index = NRG_MBS_MAX_REGS + add_index ;
// {"R":0,"T":0,"F":0}
// {"R":[0,2,4],"T":0,"F":0}
// {"R":[0,2,4],"T":0,"M":10} - [LEGACY]
uint32_t phase = EnergyModbusReadRegisterInfo ( user_add_value , reg_index ) ;
if ( ! phase ) {
return false ; // No register entered so skip
}
if ( phase > Energy . phase_count ) {
Energy . phase_count = phase ;
NrgMbsParam . devices = 1 ; // Only one device allowed with multiple phases
}
JsonParserToken val ;
2022-10-08 15:14:11 +01:00
val = user_add_value [ PSTR ( " J " ) ] ; // JSON value name
if ( val ) {
2022-10-11 10:10:47 +01:00
NrgMbsUser [ add_index ] . json_name = SetStr ( val . getStr ( ) ) ;
2022-12-28 16:45:13 +00:00
char json_name [ 32 ] ;
if ( GetCommandCode ( json_name , sizeof ( json_name ) , NrgMbsUser [ add_index ] . json_name , kEnergyModbusValues ) > - 1 ) {
return false ; // Duplicate JSON name
}
2022-10-08 15:14:11 +01:00
} else {
2022-12-28 16:45:13 +00:00
return false ; // No mandatory JSON name
2022-10-08 15:14:11 +01:00
}
val = user_add_value [ PSTR ( " G " ) ] ; // GUI value name
2022-12-28 16:45:13 +00:00
NrgMbsUser [ add_index ] . gui_name = ( val ) ? SetStr ( val . getStr ( ) ) : EmptyStr ;
2022-10-08 15:14:11 +01:00
val = user_add_value [ PSTR ( " U " ) ] ; // GUI value Unit
2022-12-28 16:45:13 +00:00
NrgMbsUser [ add_index ] . gui_unit = ( val ) ? SetStr ( val . getStr ( ) ) : EmptyStr ;
2022-10-11 10:10:47 +01:00
NrgMbsUser [ add_index ] . resolution = ENERGY_MODBUS_DECIMALS ;
2022-10-08 15:14:11 +01:00
val = user_add_value [ PSTR ( " D " ) ] ; // Decimal resolution
if ( val ) {
2022-10-11 10:10:47 +01:00
NrgMbsUser [ add_index ] . resolution = val . getUInt ( ) ;
2022-10-08 15:14:11 +01:00
}
# ifdef ENERGY_MODBUS_DEBUG
2022-12-24 14:59:29 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Idx %d (%s), R [%04X,%04X,%04X], T %d, F %d, J '%s', G '%s', U '%s', D %d " ) ,
2022-12-28 16:06:54 +00:00
reg_index , NrgMbsUser [ add_index ] . json_name ,
2022-10-11 10:10:47 +01:00
NrgMbsReg [ reg_index ] . address [ 0 ] ,
NrgMbsReg [ reg_index ] . address [ 1 ] ,
NrgMbsReg [ reg_index ] . address [ 2 ] ,
NrgMbsReg [ reg_index ] . datatype ,
2022-12-23 10:39:13 +00:00
NrgMbsReg [ reg_index ] . factor ,
2022-10-11 10:10:47 +01:00
NrgMbsUser [ add_index ] . json_name ,
NrgMbsUser [ add_index ] . gui_name ,
NrgMbsUser [ add_index ] . gui_unit ,
NrgMbsUser [ add_index ] . resolution ) ;
2022-10-08 15:14:11 +01:00
# endif
return true ;
}
# endif // USE_RULES
2022-10-06 22:17:04 +01:00
bool EnergyModbusReadRegisters ( void ) {
# ifdef USE_RULES
String modbus = RuleLoadFile ( " MODBUS " ) ;
2022-10-07 09:46:25 +01:00
if ( ! modbus . length ( ) ) { return false ; } // File not found
2022-10-06 22:17:04 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: File '%s'"), modbus.c_str());
const char * json = modbus . c_str ( ) ;
uint32_t len = strlen ( json ) + 1 ;
2022-10-07 09:46:25 +01:00
if ( len < 7 ) { return false ; } // Invalid JSON
2022-10-06 22:17:04 +01:00
char json_buffer [ len ] ;
memcpy ( json_buffer , json , len ) ; // Keep original safe
JsonParser parser ( json_buffer ) ;
JsonParserObject root = parser . getRootObject ( ) ;
2022-10-07 09:46:25 +01:00
if ( ! root ) { return false ; } // Invalid JSON
2022-10-06 22:17:04 +01:00
2022-10-11 10:10:47 +01:00
// Init defaults
2022-12-23 15:56:18 +00:00
Energy . phase_count = 1 ;
2022-10-11 10:10:47 +01:00
NrgMbsParam . serial_bps = ENERGY_MODBUS_SPEED ;
NrgMbsParam . serial_config = ENERGY_MODBUS_CONFIG ;
2022-12-24 14:59:29 +00:00
NrgMbsParam . ticker_poll = ENERGY_MODBUS_TICKER_POLL ;
2022-12-23 15:56:18 +00:00
NrgMbsParam . device_address [ 0 ] = ENERGY_MODBUS_ADDR ;
NrgMbsParam . devices = 1 ;
2022-10-11 10:10:47 +01:00
NrgMbsParam . function = ENERGY_MODBUS_FUNC ;
NrgMbsParam . user_adds = 0 ;
2022-10-06 22:17:04 +01:00
2022-12-24 14:59:29 +00:00
// Detect buffer allocation
2022-10-11 10:10:47 +01:00
JsonParserToken val ;
val = root [ PSTR ( " User " ) ] ;
if ( val ) {
if ( val . isArray ( ) ) {
2022-12-24 14:59:29 +00:00
// [{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3},{"R":0x0024,"J":"PhaseAngle","G":"Phase Angle","U":"Deg","D":2}]
2022-10-11 10:10:47 +01:00
NrgMbsParam . user_adds = val . size ( ) ;
} else {
2022-12-24 14:59:29 +00:00
// {"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3}
2022-10-11 10:10:47 +01:00
NrgMbsParam . user_adds = 1 ;
}
}
NrgMbsParam . total_regs = NRG_MBS_MAX_REGS + NrgMbsParam . user_adds ;
NrgMbsReg = ( NrgMbsRegister_t * ) calloc ( NrgMbsParam . total_regs , sizeof ( NrgMbsRegister_t ) ) ;
if ( NrgMbsReg = = nullptr ) { return false ; } // Unable to allocate variables on heap
2022-12-24 14:59:29 +00:00
2022-10-06 22:17:04 +01:00
// Init defaults
2022-10-11 10:10:47 +01:00
for ( uint32_t i = 0 ; i < NrgMbsParam . total_regs ; i + + ) {
NrgMbsReg [ i ] . datatype = ENERGY_MODBUS_DATATYPE ;
2022-10-06 22:17:04 +01:00
for ( uint32_t j = 0 ; j < ENERGY_MAX_PHASES ; j + + ) {
2022-10-11 10:10:47 +01:00
NrgMbsReg [ i ] . address [ j ] = nrg_mbs_reg_not_used ;
}
}
if ( NrgMbsParam . user_adds ) {
NrgMbsUser = ( NrgMbsUser_t * ) calloc ( NrgMbsParam . user_adds + 1 , sizeof ( NrgMbsUser_t ) ) ;
if ( NrgMbsUser = = nullptr ) {
NrgMbsParam . user_adds = 0 ;
NrgMbsParam . total_regs = NRG_MBS_MAX_REGS ;
} else {
// Init defaults
for ( uint32_t i = 0 ; i < NrgMbsParam . user_adds ; i + + ) {
NrgMbsUser [ i ] . resolution = ENERGY_MODBUS_DECIMALS ;
for ( uint32_t j = 0 ; j < ENERGY_MAX_PHASES ; j + + ) {
NrgMbsUser [ i ] . data [ j ] = NAN ;
}
}
2022-10-06 22:17:04 +01:00
}
}
2022-12-24 14:59:29 +00:00
// Get global parameters
2022-10-07 09:46:25 +01:00
val = root [ PSTR ( " Baud " ) ] ;
2022-10-06 22:17:04 +01:00
if ( val ) {
2022-12-24 14:59:29 +00:00
NrgMbsParam . serial_bps = val . getInt ( ) ; // 2400
2022-10-06 22:17:04 +01:00
}
2022-10-07 09:46:25 +01:00
val = root [ PSTR ( " Config " ) ] ;
2022-10-06 22:17:04 +01:00
if ( val ) {
const char * serial_config = val . getStr ( ) ; // 8N1
2022-10-11 10:10:47 +01:00
NrgMbsParam . serial_config = ConvertSerialConfig ( ParseSerialConfig ( serial_config ) ) ;
2022-10-06 22:17:04 +01:00
}
2022-12-24 14:59:29 +00:00
val = root [ PSTR ( " Poll " ) ] ;
if ( val ) {
NrgMbsParam . ticker_poll = val . getUInt ( ) ; // 200
if ( NrgMbsParam . ticker_poll < 100 ) { // Below 100 ms makes no sense as the comms usually is 9600bps
NrgMbsParam . ticker_poll = ENERGY_MODBUS_TICKER_POLL ;
}
}
2022-10-07 09:46:25 +01:00
val = root [ PSTR ( " Address " ) ] ;
2022-10-06 22:17:04 +01:00
if ( val ) {
2022-12-23 15:56:18 +00:00
NrgMbsParam . devices = 0 ;
if ( val . isArray ( ) ) {
2022-12-24 14:59:29 +00:00
// [1,2,3]
2022-12-23 15:56:18 +00:00
JsonParserArray arr = val . getArray ( ) ;
for ( auto value : arr ) {
NrgMbsParam . device_address [ NrgMbsParam . devices ] = value . getUInt ( ) ; // 1
NrgMbsParam . devices + + ;
if ( NrgMbsParam . devices > = ENERGY_MODBUS_MAX_DEVICES ) { break ; }
}
} else if ( val ) {
2022-12-24 14:59:29 +00:00
// 1
NrgMbsParam . device_address [ 0 ] = val . getUInt ( ) ; // 1
2022-12-23 15:56:18 +00:00
NrgMbsParam . devices + + ;
}
2022-10-06 22:17:04 +01:00
}
2022-10-07 09:46:25 +01:00
val = root [ PSTR ( " Function " ) ] ;
2022-10-06 22:17:04 +01:00
if ( val ) {
2022-12-24 14:59:29 +00:00
NrgMbsParam . function = val . getUInt ( ) ; // 4
2022-10-06 22:17:04 +01:00
}
2022-12-24 14:59:29 +00:00
// Get default energy registers
2022-10-06 22:17:04 +01:00
char register_name [ 32 ] ;
Energy . voltage_available = false ; // Disable voltage is measured
Energy . current_available = false ; // Disable current is measured
for ( uint32_t names = 0 ; names < NRG_MBS_MAX_REGS ; names + + ) {
val = root [ GetTextIndexed ( register_name , sizeof ( register_name ) , names , kEnergyModbusValues ) ] ;
2022-10-08 15:14:11 +01:00
if ( val ) {
// "Voltage":0
2022-12-24 14:59:29 +00:00
// "Voltage":[0,2,4]
2022-12-23 10:39:13 +00:00
// "Voltage":{"R":0,"T":0,"F":0}
2022-12-24 14:59:29 +00:00
// "Voltage":{"R":[0,2,4],"T":0,"F":0}
2022-10-08 15:14:11 +01:00
uint32_t phase = 0 ;
2022-10-11 10:10:47 +01:00
if ( val . isObject ( ) ) {
2022-12-24 14:59:29 +00:00
// {"R":0,"T":0,"F":0}
// {"R":[0,2,4],"T":0,"F":0}
// {"R":[0,2,4],"T":0,"M":10} - [LEGACY]
phase = EnergyModbusReadRegisterInfo ( val . getObject ( ) , names ) ;
2022-10-11 10:10:47 +01:00
} else if ( val . isArray ( ) ) {
2022-12-24 14:59:29 +00:00
// [0,2,4]
2022-10-08 15:14:11 +01:00
JsonParserArray arr = val . getArray ( ) ;
for ( auto value : arr ) {
2022-10-11 10:10:47 +01:00
NrgMbsReg [ names ] . address [ phase ] = value . getUInt ( ) ;
2022-10-08 15:14:11 +01:00
phase + + ;
2022-10-09 17:38:30 +01:00
if ( phase > = ENERGY_MAX_PHASES ) { break ; }
2022-10-08 15:14:11 +01:00
}
} else if ( val ) {
2022-12-24 14:59:29 +00:00
// 0
2022-10-11 10:10:47 +01:00
NrgMbsReg [ names ] . address [ 0 ] = val . getUInt ( ) ;
2022-10-06 22:17:04 +01:00
phase + + ;
}
2022-10-06 22:26:54 +01:00
if ( phase > Energy . phase_count ) {
Energy . phase_count = phase ;
2022-12-24 14:59:29 +00:00
NrgMbsParam . devices = 1 ; // Only one device allowed with multiple phases
2022-10-06 22:26:54 +01:00
}
2022-12-24 14:59:29 +00:00
2022-10-06 22:17:04 +01:00
switch ( names ) {
case NRG_MBS_VOLTAGE :
Energy . voltage_available = true ; // Enable if voltage is measured
if ( 1 = = phase ) {
Energy . voltage_common = true ; // Use common voltage
}
break ;
case NRG_MBS_CURRENT :
Energy . current_available = true ; // Enable if current is measured
break ;
case NRG_MBS_FREQUENCY :
if ( 1 = = phase ) {
Energy . frequency_common = true ; // Use common frequency
}
break ;
2022-10-08 15:14:11 +01:00
case NRG_MBS_TOTAL_ENERGY :
2022-10-07 09:46:25 +01:00
Settings - > flag3 . hardware_energy_total = 1 ; // SetOption72 - Enable hardware energy total counter as reference (#6561)
break ;
2022-10-06 22:17:04 +01:00
}
2022-10-08 15:14:11 +01:00
# ifdef ENERGY_MODBUS_DEBUG
2022-12-24 14:59:29 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Idx %d (%s), R [%04X,%04X,%04X], T %d, F %d " ) ,
names , register_name ,
2022-10-11 10:10:47 +01:00
NrgMbsReg [ names ] . address [ 0 ] ,
NrgMbsReg [ names ] . address [ 1 ] ,
NrgMbsReg [ names ] . address [ 2 ] ,
NrgMbsReg [ names ] . datatype ,
2022-12-23 10:39:13 +00:00
NrgMbsReg [ names ] . factor ) ;
2022-10-08 15:14:11 +01:00
# endif
}
}
2022-12-24 14:59:29 +00:00
// Get user defined registers
// "User":{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0}
// "User":[{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0},{"R":0x0024,"J":"PhaseAngle","G":"Phase Angle","U":"Deg","D":2,"T":0,"F":0}]
2022-10-08 15:14:11 +01:00
val = root [ PSTR ( " User " ) ] ;
if ( val ) {
if ( val . isArray ( ) ) {
2022-12-24 14:59:29 +00:00
// [{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0},{"R":0x0024,"J":"PhaseAngle","G":"Phase Angle","U":"Deg","D":2,"T":0,"F":0}]
2022-10-11 10:10:47 +01:00
JsonParserArray user_adds_arr = val . getArray ( ) ;
uint32_t add_index = 0 ;
for ( auto user_add_values : user_adds_arr ) {
if ( ! user_add_values . isObject ( ) ) { break ; }
if ( EnergyModbusReadUserRegisters ( user_add_values . getObject ( ) , add_index ) ) {
add_index + + ;
} else {
AddLog ( LOG_LEVEL_INFO , PSTR ( " NRG: Dropped JSON user input %d " ) , add_index + 1 ) ;
NrgMbsParam . user_adds - - ;
2022-10-08 15:14:11 +01:00
}
}
2022-10-11 10:10:47 +01:00
} else if ( val ) {
2022-12-24 14:59:29 +00:00
// {"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3,"T":0,"F":0}
2022-10-11 10:10:47 +01:00
if ( val . isObject ( ) ) {
if ( ! EnergyModbusReadUserRegisters ( val . getObject ( ) , 0 ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " NRG: Dropped JSON user input " ) ) ;
NrgMbsParam . user_adds - - ;
2022-10-08 15:14:11 +01:00
}
}
2022-10-11 10:10:47 +01:00
}
NrgMbsParam . total_regs = NRG_MBS_MAX_REGS + NrgMbsParam . user_adds ;
}
2022-12-24 14:59:29 +00:00
// Fix variable boundaries
2022-10-11 10:10:47 +01:00
for ( uint32_t i = 0 ; i < NrgMbsParam . total_regs ; i + + ) {
if ( NrgMbsReg [ i ] . datatype > = NRG_DT_MAX ) {
NrgMbsReg [ i ] . datatype = ENERGY_MODBUS_DATATYPE ;
}
2022-10-06 22:17:04 +01:00
}
2022-12-23 15:56:18 +00:00
if ( NrgMbsParam . devices > 1 ) {
2022-12-24 14:59:29 +00:00
// Multiple devices have no common values
2022-12-23 15:56:18 +00:00
Energy . phase_count = NrgMbsParam . devices ;
Energy . voltage_common = false ; // Use no common voltage
Energy . frequency_common = false ; // Use no common frequency
Settings - > flag5 . energy_phase = 1 ; // SetOption129 - (Energy) Show phase information
}
2022-10-08 15:14:11 +01:00
2022-10-07 09:46:25 +01:00
# ifdef ENERGY_MODBUS_DEBUG
2022-12-24 14:59:29 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Devices %d, RAM usage %d + %d + %d " ) ,
NrgMbsParam . devices ,
sizeof ( NrgMbsParam ) ,
NrgMbsParam . total_regs * sizeof ( NrgMbsRegister_t ) ,
NrgMbsParam . user_adds * sizeof ( NrgMbsUser_t ) ) ;
2022-10-07 09:46:25 +01:00
# endif
2022-10-06 22:17:04 +01:00
2022-10-11 10:10:47 +01:00
// NrgMbsParam.state = 0; // Set by calloc()
2022-12-28 16:06:54 +00:00
NrgMbsParam . phase = - 1 ;
2022-10-06 22:17:04 +01:00
return true ;
# endif // USE_RULES
return false ;
}
bool EnergyModbusRegisters ( void ) {
if ( EnergyModbusReadRegisters ( ) ) {
return true ;
}
2022-12-24 14:59:29 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " NRG: No valid modbus rule data " ) ) ;
2022-10-06 22:17:04 +01:00
return false ;
}
void EnergyModbusSnsInit ( void ) {
if ( EnergyModbusRegisters ( ) ) {
2022-12-03 11:33:42 +00:00
EnergyModbus = new TasmotaModbus ( Pin ( GPIO_NRG_MBS_RX ) , Pin ( GPIO_NRG_MBS_TX ) , Pin ( GPIO_NRG_MBS_TX_ENA ) ) ;
2022-10-11 10:10:47 +01:00
uint8_t result = EnergyModbus - > Begin ( NrgMbsParam . serial_bps , NrgMbsParam . serial_config ) ;
2022-10-06 22:17:04 +01:00
if ( result ) {
if ( 2 = = result ) { ClaimSerial ( ) ; }
2022-10-11 17:39:48 +01:00
# ifdef ENERGY_MODBUS_TICKER
2022-12-24 14:59:29 +00:00
ticker_energy_modbus . attach_ms ( NrgMbsParam . ticker_poll , EnergyModbusLoop ) ;
2022-10-11 17:39:48 +01:00
# endif // ENERGY_MODBUS_TICKER
2022-10-06 22:17:04 +01:00
return ;
}
}
TasmotaGlobal . energy_driver = ENERGY_NONE ;
}
void EnergyModbusDrvInit ( void ) {
if ( PinUsed ( GPIO_NRG_MBS_RX ) & & PinUsed ( GPIO_NRG_MBS_TX ) ) {
TasmotaGlobal . energy_driver = XNRG_29 ;
}
}
2022-10-08 15:14:11 +01:00
/*********************************************************************************************\
* Additional presentation
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void EnergyModbusReset ( void ) {
2022-10-11 10:10:47 +01:00
for ( uint32_t i = 0 ; i < NrgMbsParam . user_adds ; i + + ) {
2022-10-08 15:14:11 +01:00
for ( uint32_t j = 0 ; j < ENERGY_MAX_PHASES ; j + + ) {
2022-10-11 10:10:47 +01:00
if ( NrgMbsReg [ NRG_MBS_MAX_REGS + i ] . address [ 0 ] ! = nrg_mbs_reg_not_used ) {
NrgMbsUser [ i ] . data [ j ] = 0 ;
2022-10-08 15:14:11 +01:00
}
}
}
}
2022-10-08 16:17:15 +01:00
uint32_t EnergyModbusResolution ( uint32_t resolution ) {
if ( resolution > = NRG_RES_VOLTAGE ) {
switch ( resolution ) {
case NRG_RES_VOLTAGE :
return Settings - > flag2 . voltage_resolution ;
case NRG_RES_CURRENT :
return Settings - > flag2 . current_resolution ;
case NRG_RES_POWER :
return Settings - > flag2 . wattage_resolution ;
case NRG_RES_ENERGY :
return Settings - > flag2 . energy_resolution ;
case NRG_RES_FREQUENCY :
return Settings - > flag2 . frequency_resolution ;
case NRG_RES_TEMPERATURE :
return Settings - > flag2 . temperature_resolution ;
case NRG_RES_HUMIDITY :
return Settings - > flag2 . humidity_resolution ;
case NRG_RES_PRESSURE :
return Settings - > flag2 . pressure_resolution ;
case NRG_RES_WEIGHT :
return Settings - > flag2 . weight_resolution ;
}
}
return resolution ;
}
2022-10-08 15:14:11 +01:00
void EnergyModbusShow ( bool json ) {
2022-10-11 10:10:47 +01:00
char value_chr [ GUISZ ] ;
float values [ ENERGY_MAX_PHASES ] ;
for ( uint32_t i = 0 ; i < NrgMbsParam . user_adds ; i + + ) {
uint32_t reg_index = NRG_MBS_MAX_REGS + i ;
2022-10-08 16:17:15 +01:00
# ifdef ENERGY_MODBUS_DEBUG_SHOW
2022-10-08 15:14:11 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: Idx %d, R [%04X,%04X,%04X], J '%s', G '%s', U '%s', D %d, V [%3_f,%3_f,%3_f] " ) ,
i ,
2022-10-11 10:10:47 +01:00
NrgMbsReg [ reg_index ] . address [ 0 ] ,
NrgMbsReg [ reg_index ] . address [ 1 ] ,
NrgMbsReg [ reg_index ] . address [ 2 ] ,
NrgMbsUser [ i ] . json_name ,
NrgMbsUser [ i ] . gui_name ,
NrgMbsUser [ i ] . gui_unit ,
NrgMbsUser [ i ] . resolution ,
& NrgMbsUser [ i ] . data [ 0 ] ,
& NrgMbsUser [ i ] . data [ 1 ] ,
& NrgMbsUser [ i ] . data [ 2 ] ) ;
2022-10-08 15:14:11 +01:00
# endif
2022-10-08 16:17:15 +01:00
2022-10-11 10:10:47 +01:00
if ( ( NrgMbsReg [ reg_index ] . address [ 0 ] ! = nrg_mbs_reg_not_used ) & & ! isnan ( NrgMbsUser [ i ] . data [ 0 ] ) ) {
2022-10-08 15:14:11 +01:00
for ( uint32_t j = 0 ; j < ENERGY_MAX_PHASES ; j + + ) {
2022-10-11 10:10:47 +01:00
values [ j ] = NrgMbsUser [ i ] . data [ j ] ;
2022-10-08 15:14:11 +01:00
}
2022-10-11 10:10:47 +01:00
uint32_t resolution = EnergyModbusResolution ( NrgMbsUser [ i ] . resolution ) ;
2022-11-17 14:14:28 +00:00
uint32_t single = ( ! isnan ( NrgMbsUser [ i ] . data [ 1 ] ) & & ! isnan ( NrgMbsUser [ i ] . data [ 2 ] ) ) ? 0 : 1 ;
2022-10-08 16:17:15 +01:00
# ifdef ENERGY_MODBUS_DEBUG_SHOW
2022-10-11 10:10:47 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NRG: resolution %d -> %d " ) , NrgMbsUser [ i ] . resolution , resolution ) ;
2022-10-08 16:17:15 +01:00
# endif
2022-10-08 15:14:11 +01:00
if ( json ) {
2022-11-17 14:14:28 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" :%s " ) , NrgMbsUser [ i ] . json_name , EnergyFormat ( value_chr , values , resolution , single ) ) ;
2022-10-08 15:14:11 +01:00
# ifdef USE_WEBSERVER
} else {
2022-12-28 16:45:13 +00:00
if ( strlen ( NrgMbsUser [ i ] . gui_name ) ) { // Skip empty GUI names
WSContentSend_PD ( PSTR ( " {s}%s{m}%s %s{e} " ) ,
NrgMbsUser [ i ] . gui_name ,
WebEnergyFormat ( value_chr , values , resolution , single ) ,
NrgMbsUser [ i ] . gui_unit ) ;
}
2022-10-08 15:14:11 +01:00
# endif // USE_WEBSERVER
}
}
}
}
2022-10-06 22:17:04 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xnrg29 ( uint32_t function ) {
2022-10-06 22:17:04 +01:00
bool result = false ;
switch ( function ) {
2022-10-11 17:39:48 +01:00
# ifndef ENERGY_MODBUS_TICKER
// case FUNC_EVERY_200_MSECOND: // Energy ticker interrupt
case FUNC_EVERY_250_MSECOND : // Tasmota dispatcher
2022-10-06 22:17:04 +01:00
EnergyModbusLoop ( ) ;
break ;
2022-10-11 17:39:48 +01:00
# endif // No ENERGY_MODBUS_TICKER
2022-10-08 15:14:11 +01:00
case FUNC_JSON_APPEND :
EnergyModbusShow ( 1 ) ;
break ;
# ifdef USE_WEBSERVER
# ifdef USE_ENERGY_COLUMN_GUI
case FUNC_WEB_COL_SENSOR :
# else // not USE_ENERGY_COLUMN_GUI
case FUNC_WEB_SENSOR :
# endif // USE_ENERGY_COLUMN_GUI
EnergyModbusShow ( 0 ) ;
break ;
# endif // USE_WEBSERVER
2022-10-06 22:17:04 +01:00
case FUNC_ENERGY_RESET :
2022-10-08 15:14:11 +01:00
EnergyModbusReset ( ) ;
2022-10-06 22:17:04 +01:00
break ;
case FUNC_INIT :
EnergyModbusSnsInit ( ) ;
break ;
case FUNC_PRE_INIT :
EnergyModbusDrvInit ( ) ;
break ;
}
return result ;
}
# endif // USE_MODBUS_ENERGY
# endif // USE_ENERGY_SENSOR