mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
Merge branch 'development' into ina3221
This commit is contained in:
commit
5f6d0f8310
@ -6,11 +6,15 @@ All notable changes to this project will be documented in this file.
|
||||
## [12.1.1.4]
|
||||
### Added
|
||||
- Support for Shelly Plus 2PM using template ``{"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,32,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3458,0,0,0,0,0,9472,0,4736,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}``
|
||||
- Zigbee Alexa/Hue emulation, support multiple switches on separate endpoints
|
||||
- Zigbee Alexa/Hue emulation, support multiple switches on separate endpoints (#16718)
|
||||
- Support for QMC5883L magnetic induction sensor by Helge Scheunemann (#16714)
|
||||
- LVGL/HASPmota add tiny "pixel perfect" fonts for small screens (#16758)
|
||||
- HASPmota support for TTF fonts (#16759)
|
||||
- Support for Modbus Energy Monitoring devices using a rule file. See ``xnrg_29_modbus.ino`` for more information
|
||||
|
||||
### Changed
|
||||
- ESP32 LVGL library from v8.3.0 to v8.3.2
|
||||
- Increase serial console fixed input buffer size from 520 to 800
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -124,6 +124,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- Zigbee device plugin mechanism with commands ``ZbLoad``, ``ZbUnload`` and ``ZbLoadDump`` [#16252](https://github.com/arendst/Tasmota/issues/16252)
|
||||
- Zigbee basic support for Green Power [#16407](https://github.com/arendst/Tasmota/issues/16407)
|
||||
- Zigbee friendly names per endpoint
|
||||
- Zigbee Alexa/Hue emulation, support multiple switches on separate endpoints [#16718](https://github.com/arendst/Tasmota/issues/16718)
|
||||
- Flowrate meter flow amount/duration, show values in table format [#16385](https://github.com/arendst/Tasmota/issues/16385)
|
||||
- Support of optional file calib.dat on ADE7953 based energy monitors like Shelly EM [#16486](https://github.com/arendst/Tasmota/issues/16486)
|
||||
- Support for Ethernet in ESP32 safeboot firmware [#16388](https://github.com/arendst/Tasmota/issues/16388)
|
||||
@ -131,14 +132,17 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- ESP32-S2 and ESP32-S3 touch button support
|
||||
- Berry has persistent MQTT subscriptions: auto-subscribe at (re)connection
|
||||
- Berry automated solidification of code
|
||||
- LVGL/HASPmota add tiny "pixel perfect" fonts for small screens [#16758](https://github.com/arendst/Tasmota/issues/16758)
|
||||
- HASPmota support for TTF fonts [#16759](https://github.com/arendst/Tasmota/issues/16759)
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
### Changed
|
||||
- ESP32 NimBLE library from v1.3.6 to v1.4.0
|
||||
- IRremoteESP8266 library from v2.8.2 to v2.8.4
|
||||
- Tasmota Core32 from 2.0.4.1 to 2.0.5
|
||||
- TasmotaModbus library from v3.5.0 to v3.6.0 [#16351](https://github.com/arendst/Tasmota/issues/16351)
|
||||
- ESP32 NimBLE library from v1.3.6 to v1.4.0
|
||||
- ESP32 LVGL library from v8.3.0 to v8.3.2
|
||||
- ESP32 Tasmota Core32 from 2.0.4.1 to 2.0.5
|
||||
- Button debouncing V3 by adopting switch debounce code [#16339](https://github.com/arendst/Tasmota/issues/16339)
|
||||
- Thermostat max allowed temperature from 100 to 200C [#16363](https://github.com/arendst/Tasmota/issues/16363)
|
||||
- Using command ``SerialBuffer`` raise max allowed buffer size to 2048 characters [#16374](https://github.com/arendst/Tasmota/issues/16374)
|
||||
|
@ -37,6 +37,7 @@ static int m_dump(bvm *vm)
|
||||
time_insert(vm, "min", t->tm_min);
|
||||
time_insert(vm, "sec", t->tm_sec);
|
||||
time_insert(vm, "weekday", t->tm_wday);
|
||||
time_insert(vm, "epoch", ts);
|
||||
be_pop(vm, 1);
|
||||
be_return(vm);
|
||||
}
|
||||
|
@ -1719,7 +1719,7 @@ be_local_closure(lvh_obj_get_pad_left, /* name */
|
||||
********************************************************************/
|
||||
be_local_closure(lvh_obj_set_text_font, /* name */
|
||||
be_nested_proto(
|
||||
12, /* nstack */
|
||||
16, /* nstack */
|
||||
2, /* argc */
|
||||
2, /* varg */
|
||||
0, /* has upvals */
|
||||
@ -1727,28 +1727,32 @@ be_local_closure(lvh_obj_set_text_font, /* name */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[17]) { /* constants */
|
||||
( &(const bvalue[21]) { /* constants */
|
||||
/* K0 */ be_nested_str_weak(int),
|
||||
/* K1 */ be_nested_str_weak(lv),
|
||||
/* K2 */ be_nested_str_weak(font_embedded),
|
||||
/* K3 */ be_nested_str_weak(robotocondensed),
|
||||
/* K4 */ be_nested_str_weak(montserrat),
|
||||
/* K5 */ be_nested_str_weak(string),
|
||||
/* K6 */ be_nested_str_weak(split),
|
||||
/* K7 */ be_nested_str_weak(_X3A),
|
||||
/* K8 */ be_const_int(1),
|
||||
/* K9 */ be_nested_str_weak(_X2D),
|
||||
/* K10 */ be_const_int(0),
|
||||
/* K11 */ be_nested_str_weak(load_font),
|
||||
/* K6 */ be_nested_str_weak(re),
|
||||
/* K7 */ be_nested_str_weak(split),
|
||||
/* K8 */ be_nested_str_weak(_X3A),
|
||||
/* K9 */ be_const_int(1),
|
||||
/* K10 */ be_nested_str_weak(_X2D),
|
||||
/* K11 */ be_const_int(0),
|
||||
/* K12 */ be_const_int(2),
|
||||
/* K13 */ be_nested_str_weak(concat),
|
||||
/* K14 */ be_nested_str_weak(_lv_obj),
|
||||
/* K15 */ be_nested_str_weak(set_style_text_font),
|
||||
/* K16 */ be_nested_str_weak(HSP_X3A_X20Unsupported_X20font_X3A),
|
||||
/* K14 */ be_nested_str_weak(match),
|
||||
/* K15 */ be_nested_str_weak(_X2E_X2A_X5C_X2Ettf_X24),
|
||||
/* K16 */ be_nested_str_weak(load_freetype_font),
|
||||
/* K17 */ be_nested_str_weak(load_font),
|
||||
/* K18 */ be_nested_str_weak(_lv_obj),
|
||||
/* K19 */ be_nested_str_weak(set_style_text_font),
|
||||
/* K20 */ be_nested_str_weak(HSP_X3A_X20Unsupported_X20font_X3A),
|
||||
}),
|
||||
be_str_weak(set_text_font),
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[114]) { /* code */
|
||||
( &(const binstruction[143]) { /* code */
|
||||
0x4C080000, // 0000 LDNIL R2
|
||||
0x600C0004, // 0001 GETGBL R3 G4
|
||||
0x5C100200, // 0002 MOVE R4 R1
|
||||
@ -1782,87 +1786,116 @@ be_local_closure(lvh_obj_set_text_font, /* name */
|
||||
0xB0080000, // 001E RAISE 2 R0 R0
|
||||
0x70020000, // 001F JMP #0021
|
||||
0xB0080000, // 0020 RAISE 2 R0 R0
|
||||
0x70020041, // 0021 JMP #0064
|
||||
0x7002005E, // 0021 JMP #0081
|
||||
0x600C0004, // 0022 GETGBL R3 G4
|
||||
0x5C100200, // 0023 MOVE R4 R1
|
||||
0x7C0C0200, // 0024 CALL R3 1
|
||||
0x1C0C0705, // 0025 EQ R3 R3 K5
|
||||
0x780E003C, // 0026 JMPF R3 #0064
|
||||
0x780E0059, // 0026 JMPF R3 #0081
|
||||
0xA40E0A00, // 0027 IMPORT R3 K5
|
||||
0x8C100706, // 0028 GETMET R4 R3 K6
|
||||
0x5C180200, // 0029 MOVE R6 R1
|
||||
0x581C0007, // 002A LDCONST R7 K7
|
||||
0xA4120C00, // 0028 IMPORT R4 K6
|
||||
0x8C140707, // 0029 GETMET R5 R3 K7
|
||||
0x5C1C0200, // 002A MOVE R7 R1
|
||||
0x58200008, // 002B LDCONST R8 K8
|
||||
0x7C100800, // 002C CALL R4 4
|
||||
0x8C140706, // 002D GETMET R5 R3 K6
|
||||
0x5C1C0200, // 002E MOVE R7 R1
|
||||
0x58200009, // 002F LDCONST R8 K9
|
||||
0x7C140600, // 0030 CALL R5 3
|
||||
0x6018000C, // 0031 GETGBL R6 G12
|
||||
0x5C1C0800, // 0032 MOVE R7 R4
|
||||
0x7C180200, // 0033 CALL R6 1
|
||||
0x24180D08, // 0034 GT R6 R6 K8
|
||||
0x781A000A, // 0035 JMPF R6 #0041
|
||||
0x6018000C, // 0036 GETGBL R6 G12
|
||||
0x941C090A, // 0037 GETIDX R7 R4 K10
|
||||
0x7C180200, // 0038 CALL R6 1
|
||||
0x1C180D08, // 0039 EQ R6 R6 K8
|
||||
0x781A0005, // 003A JMPF R6 #0041
|
||||
0xB81A0200, // 003B GETNGBL R6 K1
|
||||
0x8C180D0B, // 003C GETMET R6 R6 K11
|
||||
0x5C200200, // 003D MOVE R8 R1
|
||||
0x7C180400, // 003E CALL R6 2
|
||||
0x5C080C00, // 003F MOVE R2 R6
|
||||
0x70020022, // 0040 JMP #0064
|
||||
0x6018000C, // 0041 GETGBL R6 G12
|
||||
0x5C1C0A00, // 0042 MOVE R7 R5
|
||||
0x7C180200, // 0043 CALL R6 1
|
||||
0x28180D0C, // 0044 GE R6 R6 K12
|
||||
0x781A001D, // 0045 JMPF R6 #0064
|
||||
0x60180009, // 0046 GETGBL R6 G9
|
||||
0x541DFFFE, // 0047 LDINT R7 -1
|
||||
0x941C0A07, // 0048 GETIDX R7 R5 R7
|
||||
0x7C180200, // 0049 CALL R6 1
|
||||
0x541DFFFD, // 004A LDINT R7 -2
|
||||
0x401E1407, // 004B CONNECT R7 K10 R7
|
||||
0x941C0A07, // 004C GETIDX R7 R5 R7
|
||||
0x8C1C0F0D, // 004D GETMET R7 R7 K13
|
||||
0x58240009, // 004E LDCONST R9 K9
|
||||
0x7C1C0400, // 004F CALL R7 2
|
||||
0x24200D0A, // 0050 GT R8 R6 K10
|
||||
0x78220011, // 0051 JMPF R8 #0064
|
||||
0x6020000C, // 0052 GETGBL R8 G12
|
||||
0x5C240E00, // 0053 MOVE R9 R7
|
||||
0x7C200200, // 0054 CALL R8 1
|
||||
0x2420110A, // 0055 GT R8 R8 K10
|
||||
0x7822000C, // 0056 JMPF R8 #0064
|
||||
0xA8020007, // 0057 EXBLK 0 #0060
|
||||
0xB8220200, // 0058 GETNGBL R8 K1
|
||||
0x8C201102, // 0059 GETMET R8 R8 K2
|
||||
0x5C280E00, // 005A MOVE R10 R7
|
||||
0x5C2C0C00, // 005B MOVE R11 R6
|
||||
0x7C200600, // 005C CALL R8 3
|
||||
0x5C081000, // 005D MOVE R2 R8
|
||||
0xA8040001, // 005E EXBLK 1 1
|
||||
0x70020003, // 005F JMP #0064
|
||||
0xAC200000, // 0060 CATCH R8 0 0
|
||||
0x70020000, // 0061 JMP #0063
|
||||
0x70020000, // 0062 JMP #0064
|
||||
0xB0080000, // 0063 RAISE 2 R0 R0
|
||||
0x4C0C0000, // 0064 LDNIL R3
|
||||
0x200C0403, // 0065 NE R3 R2 R3
|
||||
0x780E0005, // 0066 JMPF R3 #006D
|
||||
0x880C010E, // 0067 GETMBR R3 R0 K14
|
||||
0x8C0C070F, // 0068 GETMET R3 R3 K15
|
||||
0x5C140400, // 0069 MOVE R5 R2
|
||||
0x5818000A, // 006A LDCONST R6 K10
|
||||
0x7C0C0600, // 006B CALL R3 3
|
||||
0x70020003, // 006C JMP #0071
|
||||
0x600C0001, // 006D GETGBL R3 G1
|
||||
0x58100010, // 006E LDCONST R4 K16
|
||||
0x5C140200, // 006F MOVE R5 R1
|
||||
0x7C0C0400, // 0070 CALL R3 2
|
||||
0x80000000, // 0071 RET 0
|
||||
0x58240009, // 002C LDCONST R9 K9
|
||||
0x7C140800, // 002D CALL R5 4
|
||||
0x8C180707, // 002E GETMET R6 R3 K7
|
||||
0x5C200200, // 002F MOVE R8 R1
|
||||
0x5824000A, // 0030 LDCONST R9 K10
|
||||
0x7C180600, // 0031 CALL R6 3
|
||||
0x5C1C0200, // 0032 MOVE R7 R1
|
||||
0x5820000B, // 0033 LDCONST R8 K11
|
||||
0x50240000, // 0034 LDBOOL R9 0 0
|
||||
0x6028000C, // 0035 GETGBL R10 G12
|
||||
0x5C2C0A00, // 0036 MOVE R11 R5
|
||||
0x7C280200, // 0037 CALL R10 1
|
||||
0x24281509, // 0038 GT R10 R10 K9
|
||||
0x782A0003, // 0039 JMPF R10 #003E
|
||||
0x6028000C, // 003A GETGBL R10 G12
|
||||
0x942C0B0B, // 003B GETIDX R11 R5 K11
|
||||
0x7C280200, // 003C CALL R10 1
|
||||
0x742A0000, // 003D JMPT R10 #003F
|
||||
0x50280001, // 003E LDBOOL R10 0 1
|
||||
0x50280200, // 003F LDBOOL R10 1 0
|
||||
0x602C000C, // 0040 GETGBL R11 G12
|
||||
0x5C300C00, // 0041 MOVE R12 R6
|
||||
0x7C2C0200, // 0042 CALL R11 1
|
||||
0x282C170C, // 0043 GE R11 R11 K12
|
||||
0x782E000B, // 0044 JMPF R11 #0051
|
||||
0x602C0009, // 0045 GETGBL R11 G9
|
||||
0x5431FFFE, // 0046 LDINT R12 -1
|
||||
0x94300C0C, // 0047 GETIDX R12 R6 R12
|
||||
0x7C2C0200, // 0048 CALL R11 1
|
||||
0x5C201600, // 0049 MOVE R8 R11
|
||||
0x542DFFFD, // 004A LDINT R11 -2
|
||||
0x402E160B, // 004B CONNECT R11 K11 R11
|
||||
0x942C0C0B, // 004C GETIDX R11 R6 R11
|
||||
0x8C2C170D, // 004D GETMET R11 R11 K13
|
||||
0x5834000A, // 004E LDCONST R13 K10
|
||||
0x7C2C0400, // 004F CALL R11 2
|
||||
0x5C1C1600, // 0050 MOVE R7 R11
|
||||
0x8C2C090E, // 0051 GETMET R11 R4 K14
|
||||
0x5834000F, // 0052 LDCONST R13 K15
|
||||
0x5C380E00, // 0053 MOVE R14 R7
|
||||
0x7C2C0600, // 0054 CALL R11 3
|
||||
0x782E0006, // 0055 JMPF R11 #005D
|
||||
0x8C2C0707, // 0056 GETMET R11 R3 K7
|
||||
0x5C340E00, // 0057 MOVE R13 R7
|
||||
0x58380008, // 0058 LDCONST R14 K8
|
||||
0x7C2C0600, // 0059 CALL R11 3
|
||||
0x5431FFFE, // 005A LDINT R12 -1
|
||||
0x941C160C, // 005B GETIDX R7 R11 R12
|
||||
0x50240200, // 005C LDBOOL R9 1 0
|
||||
0x78260007, // 005D JMPF R9 #0066
|
||||
0xB82E0200, // 005E GETNGBL R11 K1
|
||||
0x8C2C1710, // 005F GETMET R11 R11 K16
|
||||
0x5C340E00, // 0060 MOVE R13 R7
|
||||
0x5C381000, // 0061 MOVE R14 R8
|
||||
0x583C000B, // 0062 LDCONST R15 K11
|
||||
0x7C2C0800, // 0063 CALL R11 4
|
||||
0x5C081600, // 0064 MOVE R2 R11
|
||||
0x7002001A, // 0065 JMP #0081
|
||||
0x782A0005, // 0066 JMPF R10 #006D
|
||||
0xB82E0200, // 0067 GETNGBL R11 K1
|
||||
0x8C2C1711, // 0068 GETMET R11 R11 K17
|
||||
0x5C340200, // 0069 MOVE R13 R1
|
||||
0x7C2C0400, // 006A CALL R11 2
|
||||
0x5C081600, // 006B MOVE R2 R11
|
||||
0x70020013, // 006C JMP #0081
|
||||
0x242C110B, // 006D GT R11 R8 K11
|
||||
0x782E0011, // 006E JMPF R11 #0081
|
||||
0x602C000C, // 006F GETGBL R11 G12
|
||||
0x5C300E00, // 0070 MOVE R12 R7
|
||||
0x7C2C0200, // 0071 CALL R11 1
|
||||
0x242C170B, // 0072 GT R11 R11 K11
|
||||
0x782E000C, // 0073 JMPF R11 #0081
|
||||
0xA8020007, // 0074 EXBLK 0 #007D
|
||||
0xB82E0200, // 0075 GETNGBL R11 K1
|
||||
0x8C2C1702, // 0076 GETMET R11 R11 K2
|
||||
0x5C340E00, // 0077 MOVE R13 R7
|
||||
0x5C381000, // 0078 MOVE R14 R8
|
||||
0x7C2C0600, // 0079 CALL R11 3
|
||||
0x5C081600, // 007A MOVE R2 R11
|
||||
0xA8040001, // 007B EXBLK 1 1
|
||||
0x70020003, // 007C JMP #0081
|
||||
0xAC2C0000, // 007D CATCH R11 0 0
|
||||
0x70020000, // 007E JMP #0080
|
||||
0x70020000, // 007F JMP #0081
|
||||
0xB0080000, // 0080 RAISE 2 R0 R0
|
||||
0x4C0C0000, // 0081 LDNIL R3
|
||||
0x200C0403, // 0082 NE R3 R2 R3
|
||||
0x780E0005, // 0083 JMPF R3 #008A
|
||||
0x880C0112, // 0084 GETMBR R3 R0 K18
|
||||
0x8C0C0713, // 0085 GETMET R3 R3 K19
|
||||
0x5C140400, // 0086 MOVE R5 R2
|
||||
0x5818000B, // 0087 LDCONST R6 K11
|
||||
0x7C0C0600, // 0088 CALL R3 3
|
||||
0x70020003, // 0089 JMP #008E
|
||||
0x600C0001, // 008A GETGBL R3 G1
|
||||
0x58100014, // 008B LDCONST R4 K20
|
||||
0x5C140200, // 008C MOVE R5 R1
|
||||
0x7C0C0400, // 008D CALL R3 2
|
||||
0x80000000, // 008E RET 0
|
||||
})
|
||||
)
|
||||
);
|
||||
|
@ -535,20 +535,35 @@ class lvh_obj
|
||||
end
|
||||
elif type(t) == 'string'
|
||||
import string
|
||||
import re
|
||||
# look for 'A:name.font' style font file name
|
||||
var drive_split = string.split(t, ':', 1)
|
||||
var fn_split = string.split(t, '-')
|
||||
if size(drive_split) > 1 && size(drive_split[0]) == 1
|
||||
|
||||
var name = t
|
||||
var sz = 0
|
||||
var is_ttf = false
|
||||
var is_binary = (size(drive_split) > 1 && size(drive_split[0]))
|
||||
|
||||
if size(fn_split) >= 2
|
||||
sz = int(fn_split[-1])
|
||||
name = fn_split[0..-2].concat('-') # rebuild the font name
|
||||
end
|
||||
if re.match(".*\\.ttf$", name)
|
||||
# ttf font
|
||||
name = string.split(name, ':')[-1] # remove A: if any
|
||||
is_ttf = true
|
||||
end
|
||||
|
||||
if is_ttf
|
||||
font = lv.load_freetype_font(name, sz, 0)
|
||||
elif is_binary
|
||||
# font is from disk
|
||||
font = lv.load_font(t)
|
||||
elif size(fn_split) >= 2 # it does contain '-'
|
||||
var sz = int(fn_split[-1])
|
||||
var name = fn_split[0..-2].concat('-') # rebuild the font name
|
||||
if sz > 0 && size(name) > 0 # looks good, let's have a try
|
||||
try
|
||||
font = lv.font_embedded(name, sz)
|
||||
except ..
|
||||
end
|
||||
elif sz > 0 && size(name) > 0 # looks good, let's have a try
|
||||
try
|
||||
font = lv.font_embedded(name, sz)
|
||||
except ..
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -196,8 +196,9 @@ const uint8_t OTA_ATTEMPTS = 10; // Number of times to try fetching t
|
||||
const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware
|
||||
#endif // ESP8266
|
||||
|
||||
const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in Tasmota serial command buffer
|
||||
const uint16_t MIN_INPUT_BUFFER_SIZE = 256; // Max number of characters in Tasmota serial command buffer
|
||||
//const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in Tasmota serial command buffer
|
||||
const uint16_t INPUT_BUFFER_SIZE = 800; // Max number of characters in Tasmota serial command buffer
|
||||
const uint16_t MIN_INPUT_BUFFER_SIZE = 256; // Max number of characters in Tasmota serial command buffer
|
||||
const uint16_t MAX_INPUT_BUFFER_SIZE = 2048; // Max number of characters in Arduino serial command buffer
|
||||
const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32)
|
||||
const uint16_t CMDSZ = 24; // Max number of characters in command
|
||||
|
@ -230,6 +230,7 @@ struct mi_sensor_t{
|
||||
uint8_t lastCnt; //device generated counter of the packet
|
||||
uint8_t shallSendMQTT;
|
||||
uint8_t MAC[6];
|
||||
uint16_t PID;
|
||||
uint8_t *key;
|
||||
uint32_t lastTimeSeen;
|
||||
union {
|
||||
@ -335,6 +336,7 @@ const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|Key|Cfg|Option";
|
||||
|
||||
void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, &CmndMi32Cfg, &CmndMi32Option };
|
||||
|
||||
#define UNKNOWN_MI 0
|
||||
#define FLORA 1
|
||||
#define MJ_HT_V1 2
|
||||
#define LYWSD02 3
|
||||
@ -351,8 +353,9 @@ void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, &CmndMi32Cfg, &Cmnd
|
||||
#define SJWS01L 14
|
||||
#define PVVX 15
|
||||
#define YLKG08 16
|
||||
#define YLAI003 17
|
||||
|
||||
#define MI32_TYPES 16 //count this manually
|
||||
#define MI32_TYPES 17 //count this manually
|
||||
|
||||
const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
|
||||
0x01aa, // MJ_HT_V1
|
||||
@ -369,29 +372,11 @@ const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
|
||||
0x098b, // MCCGQ02
|
||||
0x0863, // SJWS01L
|
||||
0x944a, // PVVX -> this is a fake ID
|
||||
0x03b6 // YLKG08 and YLKG07 - version w/wo mains
|
||||
0x03b6, // YLKG08 and YLKG07 - version w/wo mains
|
||||
0x07bf, // YLAI003
|
||||
};
|
||||
|
||||
const char kMI32DeviceType1[] PROGMEM = "Flora";
|
||||
const char kMI32DeviceType2[] PROGMEM = "MJ_HT_V1";
|
||||
const char kMI32DeviceType3[] PROGMEM = "LYWSD02";
|
||||
const char kMI32DeviceType4[] PROGMEM = "LYWSD03";
|
||||
const char kMI32DeviceType5[] PROGMEM = "CGG1";
|
||||
const char kMI32DeviceType6[] PROGMEM = "CGD1";
|
||||
const char kMI32DeviceType7[] PROGMEM = "NLIGHT";
|
||||
const char kMI32DeviceType8[] PROGMEM = "MJYD2S";
|
||||
const char kMI32DeviceType9[] PROGMEM = "YLYK01"; //old name yeerc
|
||||
const char kMI32DeviceType10[] PROGMEM ="MHOC401";
|
||||
const char kMI32DeviceType11[] PROGMEM ="MHOC303";
|
||||
const char kMI32DeviceType12[] PROGMEM ="ATC";
|
||||
const char kMI32DeviceType13[] PROGMEM ="MCCGQ02";
|
||||
const char kMI32DeviceType14[] PROGMEM ="SJWS01L";
|
||||
const char kMI32DeviceType15[] PROGMEM ="PVVX";
|
||||
const char kMI32DeviceType16[] PROGMEM ="YLKG08";
|
||||
const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,
|
||||
kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,
|
||||
kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,
|
||||
kMI32DeviceType13,kMI32DeviceType14,kMI32DeviceType15,kMI32DeviceType16};
|
||||
const char kMI32DeviceType[] PROGMEM = {"Flora|MJ_HT_V1|LYWSD02|LYWSD03|CGG1|CGD1|NLIGHT|MJYD2S|YLYK01|MHOC401|MHOC303|ATC|MCCGQ02|SJWS01L|PVVX|YLKG08|YLAI003"};
|
||||
|
||||
const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|did disconnect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out";
|
||||
|
||||
|
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/Berkelium1541.ttf
Executable file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/Berkelium1541.ttf
Executable file
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/Berkelium64.ttf
Executable file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/Berkelium64.ttf
Executable file
Binary file not shown.
20
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/FreeLicense.txt
Executable file
20
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium (GEOS)/FreeLicense.txt
Executable file
@ -0,0 +1,20 @@
|
||||
KREATIVE SOFTWARE RELAY FONTS FREE USE LICENSE
|
||||
version 1.2f
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or entity (the "User") obtaining a copy of the included font files (the "Software") produced by Kreative Software, to utilize, display, embed, or redistribute the Software, subject to the following conditions:
|
||||
|
||||
1. The User may not sell copies of the Software for a fee.
|
||||
|
||||
1a. The User may give away copies of the Software free of charge provided this license and any documentation is included verbatim and credit is given to Kreative Korporation or Kreative Software.
|
||||
|
||||
2. The User may not modify, reverse-engineer, or create any derivative works of the Software.
|
||||
|
||||
3. Any Software carrying the following font names or variations thereof is not covered by this license and may not be used under the terms of this license: Jewel Hill, Miss Diode n Friends, This is Beckie's font!
|
||||
|
||||
3a. Any Software carrying a font name ending with the string "Pro CE" is not covered by this license and may not be used under the terms of this license.
|
||||
|
||||
4. This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
5. Kreative Software reserves the right to change this license at any time without notice.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE SOFTWARE OR FROM OTHER DEALINGS IN THE SOFTWARE.
|
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium64.lvfont
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Berkelium64.lvfont
Normal file
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Daniel Linssen/m3x6.ttf
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Daniel Linssen/m3x6.ttf
Normal file
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Daniel Linssen/m5x7.ttf
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Daniel Linssen/m5x7.ttf
Normal file
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
KREATIVE SOFTWARE RELAY FONTS FREE USE LICENSE
|
||||
version 1.2f
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or entity (the "User") obtaining a copy of the included font files (the "Software") produced by Kreative Software, to utilize, display, embed, or redistribute the Software, subject to the following conditions:
|
||||
|
||||
1. The User may not sell copies of the Software for a fee.
|
||||
|
||||
1a. The User may give away copies of the Software free of charge provided this license and any documentation is included verbatim and credit is given to Kreative Korporation or Kreative Software.
|
||||
|
||||
2. The User may not modify, reverse-engineer, or create any derivative works of the Software.
|
||||
|
||||
3. Any Software carrying the following font names or variations thereof is not covered by this license and may not be used under the terms of this license: Jewel Hill, Miss Diode n Friends, This is Beckie's font!
|
||||
|
||||
3a. Any Software carrying a font name ending with the string "Pro CE" is not covered by this license and may not be used under the terms of this license.
|
||||
|
||||
4. This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
5. Kreative Software reserves the right to change this license at any time without notice.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE SOFTWARE OR FROM OTHER DEALINGS IN THE SOFTWARE.
|
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/PrintChar21.lvfont
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/PrintChar21.lvfont
Normal file
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
KREATIVE SOFTWARE RELAY FONTS FREE USE LICENSE
|
||||
version 1.2f
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or entity (the "User") obtaining a copy of the included font files (the "Software") produced by Kreative Software, to utilize, display, embed, or redistribute the Software, subject to the following conditions:
|
||||
|
||||
1. The User may not sell copies of the Software for a fee.
|
||||
|
||||
1a. The User may give away copies of the Software free of charge provided this license and any documentation is included verbatim and credit is given to Kreative Korporation or Kreative Software.
|
||||
|
||||
2. The User may not modify, reverse-engineer, or create any derivative works of the Software.
|
||||
|
||||
3. Any Software carrying the following font names or variations thereof is not covered by this license and may not be used under the terms of this license: Jewel Hill, Miss Diode n Friends, This is Beckie's font!
|
||||
|
||||
3a. Any Software carrying a font name ending with the string "Pro CE" is not covered by this license and may not be used under the terms of this license.
|
||||
|
||||
4. This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
5. Kreative Software reserves the right to change this license at any time without notice.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE SOFTWARE OR FROM OTHER DEALINGS IN THE SOFTWARE.
|
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Shaston320.lvfont
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/Shaston320.lvfont
Normal file
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/m3x6.lvfont
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/m3x6.lvfont
Normal file
Binary file not shown.
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/m5x7.lvfont
Normal file
BIN
tasmota/lvgl_berry/fonts/pixel_perfect/m5x7.lvfont
Normal file
Binary file not shown.
15
tasmota/lvgl_berry/fonts/pixel_perfect/pages.jsonl
Normal file
15
tasmota/lvgl_berry/fonts/pixel_perfect/pages.jsonl
Normal file
@ -0,0 +1,15 @@
|
||||
{"page":0,"comment":"---------- Upper stat line ----------"}
|
||||
{"id":0,"text_color":"#FFFFFF"}
|
||||
{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
|
||||
|
||||
{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
|
||||
{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
|
||||
|
||||
{"page":1,"comment":"---------- Page 1 ----------"}
|
||||
{"id":5,"obj":"label","x":2,"y":30,"w":316,"text":"Berkalium 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:Berkelium64.lvfont"}
|
||||
{"id":6,"obj":"label","x":2,"y":55,"w":316,"text":"PrintChar21 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:PrintChar21.lvfont"}
|
||||
{"id":7,"obj":"label","x":2,"y":80,"w":316,"text":"Shaston320 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:Shaston320.lvfont"}
|
||||
{"id":8,"obj":"label","x":2,"y":105,"w":316,"text":"m5x7 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:m5x7.lvfont"}
|
||||
{"id":9,"obj":"label","x":2,"y":130,"w":316,"text":"m3x6 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:m3x6.lvfont"}
|
||||
|
||||
{"id":99,"obj":"label","x":2,"y":170,"w":316,"text":"unscii-8 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"unscii-8"}
|
@ -837,7 +837,9 @@ void ResponseAppendFeatures(void)
|
||||
#if defined(USE_I2C) && defined(USE_QMC5883L)
|
||||
feature9 |= 0x00000008; // xsns_33_qmc5882l.ino
|
||||
#endif
|
||||
// feature9 |= 0x00000010;
|
||||
#if defined(USE_ENERGY_SENSOR) && defined(USE_MODBUS_ENERGY)
|
||||
feature9 |= 0x00000010; // xnrg_29_modbus.ino
|
||||
#endif
|
||||
// feature9 |= 0x00000020;
|
||||
// feature9 |= 0x00000040;
|
||||
// feature9 |= 0x00000080;
|
||||
|
@ -342,18 +342,43 @@ public:
|
||||
manufacturer = (const char*)_manuf;
|
||||
}
|
||||
|
||||
// check if a matches b, return true if so
|
||||
//
|
||||
// Special behavior:
|
||||
// - return true if `a` is empty or null
|
||||
// - return false if `b` is null
|
||||
// - matches start of `a` if `a` ends with `'*'`
|
||||
// - exact match otherwise
|
||||
static bool matchStar(const char *_a, const char *_b) {
|
||||
if (_a == nullptr || *_a == '\0') { return true; }
|
||||
if (_b == nullptr) { return false; }
|
||||
|
||||
const char *a = _a;
|
||||
const char *b = _b;
|
||||
while (1) {
|
||||
if (a[0] == '*' && a[1] == '\0') { // pattern ends with '*'
|
||||
return true; // matches worked until now, accept match
|
||||
}
|
||||
if (*a != *b) {
|
||||
return false;
|
||||
}
|
||||
if (*a == '\0') {
|
||||
return true;
|
||||
}
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
bool match(const char *match_model, const char *match_manuf) const {
|
||||
bool match = true;
|
||||
if (model.length() > 0) {
|
||||
if (!model.equals(match_model)) {
|
||||
match = false;
|
||||
}
|
||||
if (!matchStar(model.c_str(), match_model)) {
|
||||
match = false;
|
||||
}
|
||||
if (manufacturer.length() > 0) {
|
||||
if (!manufacturer.equals(match_manuf)) {
|
||||
if (!matchStar(manufacturer.c_str(), match_manuf)) {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">match device(%s, %s) compared to (%s, %s) = %i", match_model, match_manuf, model.c_str(), manufacturer.c_str(), match);
|
||||
return match;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ extern "C" {
|
||||
********************************************************************/
|
||||
|
||||
extern uint32_t MI32numberOfDevices();
|
||||
extern const char * MI32getDeviceName(uint32_t slot);
|
||||
extern char * MI32getDeviceName(uint32_t slot);
|
||||
extern void MI32setBatteryForSlot(uint32_t slot, uint8_t value);
|
||||
extern void MI32setHumidityForSlot(uint32_t slot, float value);
|
||||
extern void MI32setTemperatureForSlot(uint32_t slot, float value);
|
||||
|
@ -514,15 +514,14 @@ extern "C" {
|
||||
// write BMP header
|
||||
static const uint8_t bmp_sign[] = { 0x42, 0x4d }; // BM = Windows
|
||||
f.write(bmp_sign, sizeof(bmp_sign));
|
||||
size_t bmp_size = bmp_width * bmp_height * LV_COLOR_DEPTH / 8 + 0x44;
|
||||
size_t bmp_size = bmp_width * bmp_height * LV_COLOR_DEPTH / 8 + 0x42;
|
||||
f.write((uint8_t*)&bmp_size, sizeof(bmp_size));
|
||||
uint32_t zero = 0;
|
||||
f.write((uint8_t*) &zero, sizeof(zero)); // reserved 4-bytes
|
||||
uint32_t bmp_offset_to_pixels = 0x44; // TODO
|
||||
uint32_t bmp_offset_to_pixels = 0x42; // TODO
|
||||
f.write((uint8_t*) &bmp_offset_to_pixels, sizeof(bmp_offset_to_pixels));
|
||||
|
||||
// DIB Header BITMAPINFOHEADER
|
||||
size_t bmp_dib_header_size = 52; // BITMAPV2INFOHEADER
|
||||
size_t bmp_dib_header_size = 0x28;
|
||||
f.write((uint8_t*) &bmp_dib_header_size, sizeof(bmp_dib_header_size));
|
||||
|
||||
f.write((uint8_t*) &bmp_width, sizeof(bmp_width));
|
||||
@ -530,13 +529,15 @@ extern "C" {
|
||||
|
||||
// rest of header
|
||||
// BITMAPV2INFOHEADER = 52 bytes header, 40 bytes sub-header
|
||||
static const uint8_t bmp_dib_header[] = {
|
||||
static const uint8_t bmp_dib_header1[] = {
|
||||
0x01, 0x00, // planes
|
||||
16, 0x00, // bits per pixel = 16
|
||||
0x03, 0x00, 0x00, 0x00, // compression = BI_BITFIELDS uncrompressed
|
||||
0x00, 0x00, 0x00, 0x00, // Image size, 0 is valid for BI_RGB (uncompressed) TODO
|
||||
0x00, 0x00, 0x00, 0x00, // X pixels per meter
|
||||
0x00, 0x00, 0x00, 0x00, // Y pixels per meter
|
||||
};
|
||||
|
||||
static const uint8_t bmp_dib_header2[] = {
|
||||
0xC4, 0xE0, 0x00, 0x00, // X pixels per meter
|
||||
0xC4, 0xE0, 0x00, 0x00, // Y pixels per meter
|
||||
0x00, 0x00, 0x00, 0x00, // Colors in table
|
||||
0x00, 0x00, 0x00, 0x00, // Important color count
|
||||
|
||||
@ -544,10 +545,10 @@ extern "C" {
|
||||
0x00, 0xF8, 0x00, 0x00, // Red channel mask
|
||||
0xE0, 0x07, 0x00, 0x00, // Green channel mask
|
||||
0x1F, 0x00, 0x00, 0x00, // Blue channel mask
|
||||
|
||||
0x00, 0x00, // Padding to align on 4 bytes boundary
|
||||
};
|
||||
f.write(bmp_dib_header, sizeof(bmp_dib_header));
|
||||
f.write(bmp_dib_header1, sizeof(bmp_dib_header1));
|
||||
f.write((uint8_t*)&bmp_size, sizeof(bmp_size));
|
||||
f.write(bmp_dib_header2, sizeof(bmp_dib_header2));
|
||||
// now we can write the pixels array
|
||||
|
||||
// redraw screen
|
||||
|
@ -265,6 +265,7 @@ extern "C" {
|
||||
be_map_insert_int(vm, "min", t->tm_min);
|
||||
be_map_insert_int(vm, "sec", t->tm_sec);
|
||||
be_map_insert_int(vm, "weekday", t->tm_wday);
|
||||
be_map_insert_int(vm, "epoch", mktime(t));
|
||||
if (unparsed) be_map_insert_str(vm, "unparsed", unparsed);
|
||||
be_pop(vm, 1);
|
||||
}
|
||||
|
@ -20,32 +20,104 @@
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_MODBUS_ENERGY
|
||||
/*********************************************************************************************\
|
||||
* Generic Modbus energy meter - experimental (but works on my SDM230)
|
||||
* Generic Modbus energy meter
|
||||
*
|
||||
* Using a rule file called modbus allows to easy configure modbus energy monitor devices.
|
||||
* Using a rule file called modbus allows to easy configure modbus energy monitor devices up to three phases.
|
||||
*
|
||||
* Works:
|
||||
* rule3 on file#modbus do {"Name":"SDM230","Baud":2400,"Config":8N1","Address":1,"Function":4,"Voltage":0,"Current":6,"Power":12,"ApparentPower":18,"ReactivePower":24,"Factor":30,"Frequency":70,"ImportActive":342,"ExportActive":0x004A} endon
|
||||
* rule3 on file#modbus do {"Name":"SDM230","Baud":2400,"Config":8N1","Address":1,"Function":4,"Voltage":0x0000,"Current":0x0006,"Power":0x000C,"ApparentPower":0x0012,"ReactivePower":0x0018,"Factor":0x001E,"Frequency":0x0046,"ImportActive":0x0156,"ExportActive":0x004A} endon
|
||||
* Value pair description:
|
||||
* {"Name":"SDM230","Baud":2400,"Config":8N1","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:
|
||||
* Name - Name of energy monitoring device
|
||||
* Baud - Baudrate of device modbus interface
|
||||
* Config - Serial config parameters like 8N1 - 8 databits, No parity, 1 stop bit
|
||||
* Address - Modbus device address entered as decimal (1) or hexadecimal (0x01))
|
||||
* Function - Modbus function code to access two registers
|
||||
* Tasmota default embedded register names:
|
||||
* Voltage - Voltage register entered as decimal or hexadecimal for one phase (0x0000) or up to three phases ([0x0000,0x0002,0x0004])
|
||||
* Current - Current register entered as decimal or hexadecimal for one phase (0x0006) or up to three phases ([0x0006,0x0008,0x000A])
|
||||
* Power - Active power register entered as decimal or hexadecimal for one phase (0x000C) or up to three phases ([0x000C,0x000E,0x0010])
|
||||
* ApparentPower - Apparent power register entered as decimal or hexadecimal for one phase (0x000C) or up to three phases ([0x000C,0x000E,0x0010])
|
||||
* ReactivePower - Reactive power register entered as decimal or hexadecimal for one phase (0x0018) or up to three phases ([0x0018,0x001A,0x001C])
|
||||
* Factor - Power factor register entered as decimal or hexadecimal for one phase (0x001E) or up to three phases ([0x001E,0x0020,0x0022])
|
||||
* Frequency - Frequency register entered as decimal or hexadecimal for one phase (0x0046) or up to three phases ([0x0046,0x0048,0x004A])
|
||||
* Total - Total active energy register entered as decimal or hexadecimal for one phase (0x0156) or up to three phases ([0x015A,0x015C,0x015E])
|
||||
* ExportActive - Export active energy register entered as decimal or hexadecimal for one phase (0x0160) or up to three phases ([0x0160,0x0162,0x0164])
|
||||
* Optional user defined registers:
|
||||
* User - Additional user defined registers
|
||||
* Value pair description:
|
||||
* "User":{"R":0x0024,"J":"PhaseAngle","G":"Phase Angle","U":"Deg","D":2}
|
||||
* R - Modbus register entered as decimal or hexadecimal for one phase (0x0160) or up to three phases ([0x0160,0x0162,0x0164])
|
||||
* J - JSON register name (preferrably without spaces like "PhaseAngle")
|
||||
* G - GUI register name
|
||||
* U - GUI unit name
|
||||
* D - Number of decimals for floating point presentation or a code correspondig to Tasmota resolution command settings:
|
||||
* 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)
|
||||
*
|
||||
* Example using default Energy registers:
|
||||
* rule3 on file#modbus do {"Name":"SDM230","Baud":2400,"Config":8N1","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":8N1","Address":1,"Function":4,"Voltage":0x0000,"Current":0x0006,"Power":0x000C,"ApparentPower":0x0012,"ReactivePower":0x0018,"Factor":0x001E,"Frequency":0x0046,"Total":0x0156,"ExportActive":0x004A} endon
|
||||
* rule3 on file#modbus do {"Name":"DDSU666","Baud":9600,"Config":8N1","Address":1,"Function":4,"Voltage":0x2000,"Current":0x2002,"Power":0x2004,"ReactivePower":0x2006,"Factor":0x200A,"Frequency":0x200E,"Total":0x4000,"ExportActive":0x400A} endon
|
||||
*
|
||||
* Example using default Energy registers and some user defined registers:
|
||||
* rule3 on file#modbus do {"Name":"SDM72","Baud":9600,"Config":8N1","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":8N1","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
|
||||
* rule3 on file#modbus do {"Name":"SDM230 with two user registers","Baud":2400,"Config":8N1","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
|
||||
* rule3 on file#modbus do {"Name":"SDM630","Baud":9600,"Config":8N1","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
|
||||
*
|
||||
* Note:
|
||||
* - 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
|
||||
* - Changes to rule file are only executed on restart
|
||||
*
|
||||
* Restrictions:
|
||||
* - Supports Modbus floating point registers
|
||||
* - Max number of uer defined registers is defined by one rule buffer (511 characters uncompressed, around 800 characters compressed)
|
||||
*
|
||||
* To do:
|
||||
* - Support all three rule slots
|
||||
* - Support other modbus register like integers
|
||||
*
|
||||
* Test set:
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test1","Baud":2400,"Config":8N1","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],"ImportActive":[342,342,342]} endon
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test2","Baud":2400,"Config":8N1","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,"ImportActive":[342,342,342]} endon
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test3","Baud":2400,"Config":8N1","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,"ImportActive":[342,342,342]} endon
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test1","Baud":2400,"Config":8N1","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":8N1","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":8N1","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
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test4","Baud":2400,"Config":8N1","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
|
||||
* rule3 on file#modbus do {"Name":"SDM230 test5","Baud":2400,"Config":8N1","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
|
||||
* rule3 on file#modbus do {"Name":"SDM120 test1","Baud":2400,"Config":8N1","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
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XNRG_29 29
|
||||
|
||||
#define ENERGY_MODBUS_SPEED 9600 // default Modbus baudrate
|
||||
#define ENERGY_MODBUS_CONFIG TS_SERIAL_8N1
|
||||
#define ENERGY_MODBUS_ADDR 1 // default Modbus device_address
|
||||
#define ENERGY_MODBUS_FUNC 0x04 // default Modbus function code
|
||||
#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
|
||||
|
||||
#define ENERGY_MODBUS_UNITS "" // Default user GUI unit
|
||||
#define ENERGY_MODBUS_DECIMALS 0 // Default user decimal resolution
|
||||
|
||||
//#define ENERGY_MODBUS_DEBUG
|
||||
//#define ENERGY_MODBUS_DEBUG_SHOW
|
||||
|
||||
const uint16_t nrg_mbs_reg_not_used = 1; // Odd number 1 is unused register
|
||||
|
||||
enum EnergyModbusResolutions { NRG_RES_VOLTAGE = 21, // V
|
||||
NRG_RES_CURRENT, // A
|
||||
NRG_RES_POWER, // W, VA, VAr
|
||||
NRG_RES_ENERGY, // kWh, kVAh, kVArh
|
||||
NRG_RES_FREQUENCY, // Hz
|
||||
NRG_RES_TEMPERATURE, // C, F
|
||||
NRG_RES_HUMIDITY, // %
|
||||
NRG_RES_PRESSURE, // hPa, mmHg
|
||||
NRG_RES_WEIGHT }; // Kg
|
||||
|
||||
enum EnergyModbusRegisters { NRG_MBS_VOLTAGE,
|
||||
NRG_MBS_CURRENT,
|
||||
NRG_MBS_ACTIVE_POWER,
|
||||
@ -53,7 +125,7 @@ enum EnergyModbusRegisters { NRG_MBS_VOLTAGE,
|
||||
NRG_MBS_REACTIVE_POWER,
|
||||
NRG_MBS_POWER_FACTOR,
|
||||
NRG_MBS_FREQUENCY,
|
||||
NRG_MBS_IMPORT_ACTIVE_ENERGY,
|
||||
NRG_MBS_TOTAL_ENERGY,
|
||||
NRG_MBS_EXPORT_ACTIVE_ENERGY,
|
||||
NRG_MBS_MAX_REGS };
|
||||
|
||||
@ -64,12 +136,14 @@ const char kEnergyModbusValues[] PROGMEM = D_JSON_VOLTAGE "|" // Vo
|
||||
D_JSON_REACTIVE_POWERUSAGE "|" // ReactivePower
|
||||
D_JSON_POWERFACTOR "|" // Factor
|
||||
D_JSON_FREQUENCY "|" // Frequency
|
||||
D_JSON_IMPORT_ACTIVE "|" // ImportActive
|
||||
D_JSON_TOTAL "|" // Total
|
||||
D_JSON_EXPORT_ACTIVE "|" // ExportActive
|
||||
;
|
||||
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *EnergyModbus;
|
||||
#include <Ticker.h>
|
||||
Ticker ticker_energy_modbus;
|
||||
|
||||
struct NRGMODBUS {
|
||||
uint32_t serial_bps;
|
||||
@ -77,19 +151,37 @@ struct NRGMODBUS {
|
||||
uint16_t register_address[NRG_MBS_MAX_REGS][ENERGY_MAX_PHASES];
|
||||
uint8_t device_address;
|
||||
uint8_t function;
|
||||
uint8_t user_adds;
|
||||
uint8_t phase;
|
||||
uint8_t state;
|
||||
uint8_t retry;
|
||||
bool mutex;
|
||||
} *NrgModbus = nullptr;
|
||||
|
||||
typedef struct NRGMODBUSUSER {
|
||||
float register_data[ENERGY_MAX_PHASES];
|
||||
uint16_t register_address[ENERGY_MAX_PHASES];
|
||||
uint8_t resolution;
|
||||
String json_name;
|
||||
String gui_name;
|
||||
String gui_unit;
|
||||
} NrgModbusUser_t;
|
||||
NrgModbusUser_t* NrgModbusUser = nullptr;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void EnergyModbusLoop(void) {
|
||||
if (NrgModbus->mutex) { return; }
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: EnergyModbusLoop() entry"));
|
||||
|
||||
NrgModbus->mutex = 1;
|
||||
|
||||
uint16_t register_address;
|
||||
bool data_ready = EnergyModbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[14]; // At least 5 + (2 * 2) = 9
|
||||
|
||||
uint8_t buffer[9]; // At least 5 + (2 * 2) = 9
|
||||
uint32_t error = EnergyModbus->ReceiveBuffer(buffer, 2);
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NRG: Modbus register %d, phase %d, rcvd %*_H"),
|
||||
@ -148,38 +240,118 @@ void EnergyModbusLoop(void) {
|
||||
case NRG_MBS_FREQUENCY:
|
||||
Energy.frequency[NrgModbus->phase] = value; // 50.0 Hz
|
||||
break;
|
||||
case NRG_MBS_IMPORT_ACTIVE_ENERGY:
|
||||
case NRG_MBS_TOTAL_ENERGY:
|
||||
Energy.import_active[NrgModbus->phase] = value; // 6.216 kWh => used in EnergyUpdateTotal()
|
||||
break;
|
||||
case NRG_MBS_EXPORT_ACTIVE_ENERGY:
|
||||
Energy.export_active[NrgModbus->phase] = value; // 478.492 kWh
|
||||
break;
|
||||
default:
|
||||
if (NrgModbusUser) {
|
||||
NrgModbusUser[NrgModbus->state - NRG_MBS_MAX_REGS].register_data[NrgModbus->phase] = value;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
NrgModbus->phase++;
|
||||
if (NrgModbus->phase == Energy.phase_count) {
|
||||
if (NrgModbus->phase >= Energy.phase_count) {
|
||||
NrgModbus->phase = 0;
|
||||
NrgModbus->state++;
|
||||
if (NrgModbus->state == NRG_MBS_MAX_REGS) {
|
||||
if (NrgModbus->state >= NRG_MBS_MAX_REGS + NrgModbus->user_adds) {
|
||||
NrgModbus->state = 0;
|
||||
NrgModbus->phase = 0;
|
||||
EnergyUpdateTotal(); // update every cycle after all registers have been read
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (NrgModbus->register_address[NrgModbus->state][NrgModbus->phase] == nrg_mbs_reg_not_used);
|
||||
delay(0);
|
||||
register_address = (NrgModbus->state < NRG_MBS_MAX_REGS) ? NrgModbus->register_address[NrgModbus->state][NrgModbus->phase] :
|
||||
NrgModbusUser[NrgModbus->state - NRG_MBS_MAX_REGS].register_address[NrgModbus->phase];
|
||||
} while (register_address == nrg_mbs_reg_not_used);
|
||||
}
|
||||
} // end data ready
|
||||
|
||||
if (0 == NrgModbus->retry || data_ready) {
|
||||
NrgModbus->retry = 5;
|
||||
EnergyModbus->Send(NrgModbus->device_address, NrgModbus->function, NrgModbus->register_address[NrgModbus->state][NrgModbus->phase], 2);
|
||||
NrgModbus->retry = 1;
|
||||
register_address = (NrgModbus->state < NRG_MBS_MAX_REGS) ? NrgModbus->register_address[NrgModbus->state][NrgModbus->phase] :
|
||||
NrgModbusUser[NrgModbus->state - NRG_MBS_MAX_REGS].register_address[NrgModbus->phase];
|
||||
EnergyModbus->Send(NrgModbus->device_address, NrgModbus->function, register_address, 2);
|
||||
} else {
|
||||
NrgModbus->retry--;
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Modbus state %d retry %d"), NrgModbus->state, NrgModbus->retry);
|
||||
#endif
|
||||
|
||||
}
|
||||
delay(0);
|
||||
NrgModbus->mutex = 0;
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: EnergyModbusLoop() exit"));
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_RULES
|
||||
bool EnergyModbusReadUserRegisters(JsonParserObject user_add_value, uint32_t add_index) {
|
||||
// {"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3}
|
||||
JsonParserToken val;
|
||||
val = user_add_value[PSTR("R")]; // Register address
|
||||
uint32_t phase = 0;
|
||||
if (val.isArray()) {
|
||||
JsonParserArray address_arr = val.getArray();
|
||||
for (auto value : address_arr) {
|
||||
NrgModbusUser[add_index].register_address[phase] = value.getUInt();
|
||||
phase++;
|
||||
if (phase >= ENERGY_MAX_PHASES) { break; }
|
||||
}
|
||||
} else if (val) {
|
||||
NrgModbusUser[add_index].register_address[0] = val.getUInt();
|
||||
phase++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (phase > Energy.phase_count) {
|
||||
Energy.phase_count = phase;
|
||||
}
|
||||
val = user_add_value[PSTR("J")]; // JSON value name
|
||||
if (val) {
|
||||
NrgModbusUser[add_index].json_name = val.getStr();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
val = user_add_value[PSTR("G")]; // GUI value name
|
||||
if (val) {
|
||||
NrgModbusUser[add_index].gui_name = val.getStr();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
NrgModbusUser[add_index].gui_unit = ENERGY_MODBUS_UNITS;
|
||||
val = user_add_value[PSTR("U")]; // GUI value Unit
|
||||
if (val) {
|
||||
NrgModbusUser[add_index].gui_unit = val.getStr();
|
||||
}
|
||||
NrgModbusUser[add_index].resolution = ENERGY_MODBUS_DECIMALS;
|
||||
val = user_add_value[PSTR("D")]; // Decimal resolution
|
||||
if (val) {
|
||||
NrgModbusUser[add_index].resolution = val.getUInt();
|
||||
}
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Idx %d, R [%04X,%04X,%04X], J '%s', G '%s', U '%s', D %d"),
|
||||
add_index,
|
||||
NrgModbusUser[add_index].register_address[0],
|
||||
NrgModbusUser[add_index].register_address[1],
|
||||
NrgModbusUser[add_index].register_address[2],
|
||||
NrgModbusUser[add_index].json_name.c_str(),
|
||||
NrgModbusUser[add_index].gui_name.c_str(),
|
||||
NrgModbusUser[add_index].gui_unit.c_str(),
|
||||
NrgModbusUser[add_index].resolution);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // USE_RULES
|
||||
|
||||
bool EnergyModbusReadRegisters(void) {
|
||||
#ifdef USE_RULES
|
||||
String modbus = RuleLoadFile("MODBUS");
|
||||
@ -196,8 +368,8 @@ bool EnergyModbusReadRegisters(void) {
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (!root) { return false; } // Invalid JSON
|
||||
|
||||
NrgModbus = (NRGMODBUS *)calloc(sizeof(struct NRGMODBUS), 1);
|
||||
if (NrgModbus == nullptr) { return false; } // Unable to allocate variabvles on heap
|
||||
NrgModbus = (NRGMODBUS *)calloc(1, sizeof(struct NRGMODBUS));
|
||||
if (NrgModbus == nullptr) { return false; } // Unable to allocate variables on heap
|
||||
|
||||
// Init defaults
|
||||
NrgModbus->serial_bps = ENERGY_MODBUS_SPEED;
|
||||
@ -222,32 +394,33 @@ bool EnergyModbusReadRegisters(void) {
|
||||
}
|
||||
val = root[PSTR("Address")];
|
||||
if (val) {
|
||||
NrgModbus->device_address = val.getInt(); // 1
|
||||
NrgModbus->device_address = val.getUInt(); // 1
|
||||
}
|
||||
val = root[PSTR("Function")];
|
||||
if (val) {
|
||||
NrgModbus->function = val.getInt(); // 4
|
||||
NrgModbus->function = val.getUInt(); // 4
|
||||
}
|
||||
|
||||
char register_name[32];
|
||||
uint32_t phase;
|
||||
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++) {
|
||||
phase = 0;
|
||||
val = root[GetTextIndexed(register_name, sizeof(register_name), names, kEnergyModbusValues)];
|
||||
if (val.isArray()) {
|
||||
JsonParserArray arr = val.getArray();
|
||||
for (auto value : arr) {
|
||||
NrgModbus->register_address[names][phase] = value.getUInt();
|
||||
if (val) {
|
||||
// "Voltage":0
|
||||
// "Voltage":[0,0,0]
|
||||
uint32_t phase = 0;
|
||||
if (val.isArray()) {
|
||||
JsonParserArray arr = val.getArray();
|
||||
for (auto value : arr) {
|
||||
NrgModbus->register_address[names][phase] = value.getUInt();
|
||||
phase++;
|
||||
if (phase >= ENERGY_MAX_PHASES) { break; }
|
||||
}
|
||||
} else if (val) {
|
||||
NrgModbus->register_address[names][0] = val.getUInt();
|
||||
phase++;
|
||||
if (phase == ENERGY_MAX_PHASES) { break; }
|
||||
}
|
||||
} else if (val) {
|
||||
NrgModbus->register_address[names][phase] = val.getUInt();
|
||||
phase++;
|
||||
}
|
||||
if (phase) {
|
||||
if (phase > Energy.phase_count) {
|
||||
Energy.phase_count = phase;
|
||||
}
|
||||
@ -266,14 +439,68 @@ bool EnergyModbusReadRegisters(void) {
|
||||
Energy.frequency_common = true; // Use common frequency
|
||||
}
|
||||
break;
|
||||
case NRG_MBS_IMPORT_ACTIVE_ENERGY:
|
||||
case NRG_MBS_TOTAL_ENERGY:
|
||||
Settings->flag3.hardware_energy_total = 1; // SetOption72 - Enable hardware energy total counter as reference (#6561)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Idx %d, R [%04X,%04X,%04X]"),
|
||||
names,
|
||||
NrgModbus->register_address[names][0],
|
||||
NrgModbus->register_address[names][1],
|
||||
NrgModbus->register_address[names][2]);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
NrgModbus->user_adds = 0;
|
||||
// "User":{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3}
|
||||
// "User":[{"R":0x004E,"J":"ExportReactive","G":"Export Reactive","U":"kVArh","D":3},{"R":0x0024,"J":"PhaseAngle","G":"Phase Angle","U":"Deg","D":2}]
|
||||
val = root[PSTR("User")];
|
||||
if (val) {
|
||||
NrgModbus->user_adds = 1;
|
||||
if (val.isArray()) {
|
||||
NrgModbus->user_adds = val.size();
|
||||
}
|
||||
NrgModbusUser = (NrgModbusUser_t*)calloc(NrgModbus->user_adds, sizeof(NrgModbusUser_t));
|
||||
if (NrgModbusUser) {
|
||||
// Init defaults
|
||||
for (uint32_t i = 0; i < NrgModbus->user_adds; i++) {
|
||||
for (uint32_t j = 0; j < ENERGY_MAX_PHASES; j++) {
|
||||
NrgModbusUser[i].register_address[j] = nrg_mbs_reg_not_used;
|
||||
NrgModbusUser[i].register_data[j] = NAN;
|
||||
}
|
||||
}
|
||||
if (val.isArray()) {
|
||||
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);
|
||||
NrgModbus->user_adds--;
|
||||
}
|
||||
}
|
||||
} else if (val) {
|
||||
if (val.isObject()) {
|
||||
if (!EnergyModbusReadUserRegisters(val.getObject(), 0)) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("NRG: Dropped JSON user input"));
|
||||
NrgModbus->user_adds--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unable to allocate variables on heap
|
||||
NrgModbus->user_adds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Registers %*_H"), sizeof(NrgModbus->register_address), NrgModbus->register_address);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: RAM usage %d + %d"), sizeof(struct NRGMODBUS), NrgModbus->user_adds * sizeof(NrgModbusUser_t));
|
||||
#endif
|
||||
|
||||
// NrgModbus->state = 0; // Set by calloc()
|
||||
@ -298,6 +525,7 @@ void EnergyModbusSnsInit(void) {
|
||||
uint8_t result = EnergyModbus->Begin(NrgModbus->serial_bps, NrgModbus->serial_config);
|
||||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
ticker_energy_modbus.attach_ms(150, EnergyModbusLoop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -310,6 +538,90 @@ void EnergyModbusDrvInit(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Additional presentation
|
||||
\*********************************************************************************************/
|
||||
|
||||
void EnergyModbusReset(void) {
|
||||
for (uint32_t i = 0; i < NrgModbus->user_adds; i++) {
|
||||
for (uint32_t j = 0; j < ENERGY_MAX_PHASES; j++) {
|
||||
if (NrgModbusUser[i].register_address[0] != nrg_mbs_reg_not_used) {
|
||||
NrgModbusUser[i].register_data[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void EnergyModbusShow(bool json) {
|
||||
char value_chr[TOPSZ];
|
||||
for (uint32_t i = 0; i < NrgModbus->user_adds; i++) {
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG_SHOW
|
||||
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,
|
||||
NrgModbusUser[i].register_address[0],
|
||||
NrgModbusUser[i].register_address[1],
|
||||
NrgModbusUser[i].register_address[2],
|
||||
NrgModbusUser[i].json_name.c_str(),
|
||||
NrgModbusUser[i].gui_name.c_str(),
|
||||
NrgModbusUser[i].gui_unit.c_str(),
|
||||
NrgModbusUser[i].resolution,
|
||||
&NrgModbusUser[i].register_data[0],
|
||||
&NrgModbusUser[i].register_data[1],
|
||||
&NrgModbusUser[i].register_data[2]);
|
||||
#endif
|
||||
|
||||
if ((NrgModbusUser[i].register_address[0] != nrg_mbs_reg_not_used) && !isnan(NrgModbusUser[i].register_data[0])) {
|
||||
float values[ENERGY_MAX_PHASES];
|
||||
for (uint32_t j = 0; j < ENERGY_MAX_PHASES; j++) {
|
||||
values[j] = NrgModbusUser[i].register_data[j];
|
||||
}
|
||||
uint32_t resolution = EnergyModbusResolution(NrgModbusUser[i].resolution);
|
||||
|
||||
#ifdef ENERGY_MODBUS_DEBUG_SHOW
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: resolution %d -> %d"), NrgModbusUser[i].resolution, resolution);
|
||||
#endif
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"%s\":%s"), NrgModbusUser[i].json_name.c_str(), EnergyFormat(value_chr, values, resolution));
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(PSTR("{s}%s{m}%s %s{e}"),
|
||||
NrgModbusUser[i].gui_name.c_str(),
|
||||
WebEnergyFormat(value_chr, values, resolution),
|
||||
NrgModbusUser[i].gui_unit.c_str());
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
@ -318,12 +630,26 @@ bool Xnrg29(uint8_t function) {
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
// case FUNC_EVERY_250_MSECOND:
|
||||
case FUNC_EVERY_200_MSECOND:
|
||||
/*
|
||||
case FUNC_EVERY_200_MSECOND: // Energy ticker interrupt
|
||||
// case FUNC_EVERY_250_MSECOND: // Tasmota dispatcher
|
||||
EnergyModbusLoop();
|
||||
break;
|
||||
*/
|
||||
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
|
||||
case FUNC_ENERGY_RESET:
|
||||
// EnergyModbusReset();
|
||||
EnergyModbusReset();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
EnergyModbusSnsInit();
|
||||
|
@ -22,6 +22,8 @@
|
||||
--------------------------------------------------------------------------------------------
|
||||
Version yyyymmdd Action Description
|
||||
--------------------------------------------------------------------------------------------
|
||||
0.9.5.6 20221006 changed - remove old HASS code, allow adding unknown sensors, prepare YLAI003
|
||||
-------
|
||||
0.9.5.5 20220326 changed - refactored connection task for asynchronous op, add response option,
|
||||
fixed MI32Key command
|
||||
-------
|
||||
@ -91,7 +93,7 @@ static BLEScan* MI32Scan;
|
||||
|
||||
class MI32SensorCallback : public NimBLEClientCallbacks {
|
||||
void onConnect(NimBLEClient* pclient) {
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.conCtx->slot].type)-1]);
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("connected %s"), MI32getDeviceName(MI32.conCtx->slot));
|
||||
MI32.infoMsg = MI32_DID_CONNECT;
|
||||
MI32.mode.willConnect = 0;
|
||||
MI32.mode.connected = 1;
|
||||
@ -251,14 +253,16 @@ void MI32_ReverseMAC(uint8_t _mac[]){
|
||||
|
||||
void MI32AddKey(mi_bindKey_t keyMAC){
|
||||
bool unknownMAC = true;
|
||||
uint32_t _slot = 0;
|
||||
for(auto &_sensor : MIBLEsensors){
|
||||
if(memcmp(keyMAC.MAC,_sensor.MAC,sizeof(keyMAC.MAC))==0){
|
||||
_sensor.key = new uint8_t[16];
|
||||
memcpy(_sensor.key,keyMAC.key,16);
|
||||
unknownMAC=false;
|
||||
_sensor.status.hasWrongKey = 0;
|
||||
AddLog(LOG_LEVEL_INFO,PSTR("add key to %s"),kMI32DeviceType[_sensor.type-1]);
|
||||
AddLog(LOG_LEVEL_INFO,PSTR("add key to %s"),MI32getDeviceName(_slot));
|
||||
}
|
||||
_slot++;
|
||||
}
|
||||
if(unknownMAC){
|
||||
AddLog(LOG_LEVEL_ERROR,PSTR("M32: unknown MAC"));
|
||||
@ -291,7 +295,7 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
|
||||
}
|
||||
|
||||
uint32_t _version = (uint32_t)_beacon->frame.version;
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: encrypted msg from %s with version:%u"),kMI32DeviceType[MIBLEsensors[_slot].type-1],_version);
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: encrypted msg from %s with version:%u"),MI32getDeviceName(_slot),_version);
|
||||
|
||||
if(_version == 5){
|
||||
if(_beacon->frame.includesMAC){
|
||||
@ -361,12 +365,13 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3
|
||||
/**
|
||||
* @brief Return the slot number of a known sensor or return create new sensor slot
|
||||
*
|
||||
* @param _MAC BLE address of the sensor
|
||||
* @param _MAC BLE address of the sensor
|
||||
* @param _type Type number of the sensor
|
||||
* @return uint32_t Known or new slot in the sensors-vector
|
||||
*/
|
||||
uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter){
|
||||
DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type);
|
||||
uint16_t _pid = _type; // save for unknown types
|
||||
bool _success = false;
|
||||
for (uint32_t i=0;i<MI32_TYPES;i++){ // i < sizeof(kMI32DeviceID) gives compiler warning
|
||||
if(_type == kMI32DeviceID[i]){
|
||||
@ -374,11 +379,8 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
||||
_type = i+1;
|
||||
_success = true;
|
||||
}
|
||||
else {
|
||||
DEBUG_SENSOR_LOG(PSTR("%s: ID-type is not: %x"),D_CMND_MI32,kMI32DeviceID[i]);
|
||||
}
|
||||
}
|
||||
if(!_success) return 0xff;
|
||||
if(!_success) _type = UNKNOWN_MI;
|
||||
|
||||
DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_MI32, MIBLEsensors.size());
|
||||
for(uint32_t i=0; i<MIBLEsensors.size(); i++){
|
||||
@ -401,6 +403,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
||||
DEBUG_SENSOR_LOG(PSTR("%s: found new sensor"),D_CMND_MI32);
|
||||
mi_sensor_t _newSensor;
|
||||
memcpy(_newSensor.MAC,_MAC, sizeof(_MAC));
|
||||
_newSensor.PID = _pid;
|
||||
_newSensor.type = _type;
|
||||
_newSensor.eventType.raw = 0;
|
||||
_newSensor.feature.raw = 0;
|
||||
@ -414,6 +417,9 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
||||
_newSensor.key = nullptr;
|
||||
switch (_type)
|
||||
{
|
||||
case UNKNOWN_MI:
|
||||
_newSensor.hum_history = (uint8_t*) calloc(24,1);
|
||||
break;
|
||||
case FLORA:
|
||||
_newSensor.moisture =0xff;
|
||||
_newSensor.fertility =0xffff;
|
||||
@ -450,7 +456,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
||||
_newSensor.feature.bat=1;
|
||||
_newSensor.NMT=0;
|
||||
break;
|
||||
case YLYK01: case YLKG08:
|
||||
case YLYK01: case YLKG08: case YLAI003:
|
||||
_newSensor.feature.Btn = 1;
|
||||
_newSensor.Btn = 99;
|
||||
if(_type == YLKG08){
|
||||
@ -496,7 +502,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
||||
break;
|
||||
}
|
||||
MIBLEsensors.push_back(_newSensor);
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: new %s at slot: %u"),kMI32DeviceType[_type-1],MIBLEsensors.size()-1);
|
||||
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: new %s at slot: %u"),MI32getDeviceName(MIBLEsensors.size()-1),MIBLEsensors.size()-1);
|
||||
MI32.mode.shallShowStatusInfo = 1;
|
||||
return MIBLEsensors.size()-1;
|
||||
};
|
||||
@ -796,9 +802,15 @@ extern "C" {
|
||||
return MIBLEsensors[slot].MAC;
|
||||
}
|
||||
|
||||
const char * MI32getDeviceName(uint32_t slot){
|
||||
if(slot>MIBLEsensors.size()-1) return "";
|
||||
return kMI32DeviceType[MIBLEsensors[slot].type-1];
|
||||
char * MI32getDeviceName(uint32_t slot){
|
||||
static char _name[12];
|
||||
if( MIBLEsensors[slot].type == UNKNOWN_MI){
|
||||
snprintf_P(_name,8,PSTR("MI_%04X"),MIBLEsensors[slot].PID);
|
||||
}
|
||||
else{
|
||||
GetTextIndexed(_name, sizeof(_name), MIBLEsensors[slot].type-1, kMI32DeviceType);
|
||||
}
|
||||
return _name;
|
||||
}
|
||||
|
||||
} //extern "C"
|
||||
@ -1337,10 +1349,11 @@ if(decryptRet!=0){
|
||||
return;
|
||||
}
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_payload.type);
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), MI32getDeviceName(_slot),_slot,_payload.type);
|
||||
MIBLEsensors[_slot].lastTime = millis();
|
||||
switch(_payload.type){
|
||||
case 0x01:
|
||||
MIBLEsensors[_slot].feature.Btn = 1;
|
||||
if(_payload.Btn.type == 4){ //dimmer knob rotation
|
||||
MIBLEsensors[_slot].eventType.knob = 1;
|
||||
if(_payload.Btn.num == 0){
|
||||
@ -1385,6 +1398,7 @@ if(decryptRet!=0){
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn );
|
||||
break;
|
||||
case 0x04:
|
||||
MIBLEsensors[_slot].feature.temp = 1;
|
||||
_tempFloat=(float)(_payload.temp)/10.0f;
|
||||
if(_tempFloat<60){
|
||||
MIBLEsensors[_slot].temp=_tempFloat;
|
||||
@ -1400,6 +1414,7 @@ if(decryptRet!=0){
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _payload.temp );
|
||||
break;
|
||||
case 0x06:
|
||||
MIBLEsensors[_slot].feature.hum = 1;
|
||||
_tempFloat=(float)(_payload.hum)/10.0f;
|
||||
if(_tempFloat<101){
|
||||
MIBLEsensors[_slot].hum=_tempFloat;
|
||||
@ -1415,6 +1430,7 @@ if(decryptRet!=0){
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _payload.hum);
|
||||
break;
|
||||
case 0x07:
|
||||
MIBLEsensors[_slot].feature.lux = 1;
|
||||
MIBLEsensors[_slot].lux=_payload.lux & 0x00ffffff;
|
||||
if(MIBLEsensors[_slot].type==MJYD2S){
|
||||
MIBLEsensors[_slot].eventType.noMotion = 1;
|
||||
@ -1429,18 +1445,21 @@ if(decryptRet!=0){
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _payload.lux & 0x00ffffff);
|
||||
break;
|
||||
case 0x08:
|
||||
MIBLEsensors[_slot].feature.moist = 1;
|
||||
MIBLEsensors[_slot].moisture=_payload.moist;
|
||||
MIBLEsensors[_slot].eventType.moist = 1;
|
||||
DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated"));
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _payload.moist);
|
||||
break;
|
||||
case 0x09:
|
||||
MIBLEsensors[_slot].fertility=_payload.fert;
|
||||
MIBLEsensors[_slot].eventType.fert = 1;
|
||||
DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated"));
|
||||
MIBLEsensors[_slot].feature.fert = 1;
|
||||
MIBLEsensors[_slot].fertility=_payload.fert;
|
||||
MIBLEsensors[_slot].eventType.fert = 1;
|
||||
DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated"));
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _payload.fert);
|
||||
break;
|
||||
case 0x0a:
|
||||
MIBLEsensors[_slot].feature.bat = 1;
|
||||
if(MI32.option.ignoreBogusBattery){
|
||||
if(MIBLEsensors[_slot].type==LYWSD03MMC || MIBLEsensors[_slot].type==MHOC401){
|
||||
break;
|
||||
@ -1473,6 +1492,8 @@ if(decryptRet!=0){
|
||||
|
||||
case 0x0f:
|
||||
if (_payload.ten!=0) break;
|
||||
MIBLEsensors[_slot].feature.motion = 1;
|
||||
MIBLEsensors[_slot].feature.NMT = 1; //only driver based
|
||||
MIBLEsensors[_slot].eventType.motion = 1;
|
||||
MIBLEsensors[_slot].events++;
|
||||
MIBLEsensors[_slot].lux = _payload.lux & 0x00ffffff;
|
||||
@ -1489,6 +1510,7 @@ if(decryptRet!=0){
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("motion: primary"),MIBLEsensors[_slot].lux );
|
||||
break;
|
||||
case 0x14:
|
||||
MIBLEsensors[_slot].feature.leak = 1;
|
||||
MIBLEsensors[_slot].leak = _payload.leak;
|
||||
MIBLEsensors[_slot].eventType.leak = 1;
|
||||
if(_payload.leak>0) MI32.mode.shallTriggerTele = 1;
|
||||
@ -1497,12 +1519,14 @@ if(decryptRet!=0){
|
||||
#endif //USE_MI_HOMEKIT
|
||||
break;
|
||||
case 0x17:
|
||||
MIBLEsensors[_slot].feature.NMT = 1;
|
||||
MIBLEsensors[_slot].NMT = _payload.NMT;
|
||||
MIBLEsensors[_slot].eventType.NMT = 1;
|
||||
MI32.mode.shallTriggerTele = 1;
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _payload.NMT);
|
||||
break;
|
||||
case 0x19:
|
||||
MIBLEsensors[_slot].feature.door = 1;
|
||||
MIBLEsensors[_slot].door = _payload.door;
|
||||
MIBLEsensors[_slot].eventType.door = 1;
|
||||
MIBLEsensors[_slot].events++;
|
||||
@ -1544,7 +1568,7 @@ void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI)
|
||||
_slot = MIBLEgetSensorSlot(_packet->MAC, 0x944a, _packet->P.frameCnt); // ... and again
|
||||
}
|
||||
if(_slot==0xff) return;
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), MI32getDeviceName(_slot),_slot);
|
||||
|
||||
MIBLEsensors[_slot].RSSI=RSSI;
|
||||
MIBLEsensors[_slot].lastTime = millis();
|
||||
@ -1581,7 +1605,7 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI
|
||||
memcpy(_addr,addr,6);
|
||||
uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576, 0); // This must be hard-coded, no object-id in Cleargrass-packet, we have no packet counter too
|
||||
if(_slot==0xff) return;
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
|
||||
// AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), MI32getDeviceName(_slot),_slot);
|
||||
MIBLEsensors[_slot].RSSI=RSSI;
|
||||
MIBLEsensors[_slot].lastTime = millis();
|
||||
cg_packet_t _packet;
|
||||
@ -1918,9 +1942,9 @@ void MI32sendWidget(uint32_t slot){
|
||||
snprintf_P(_bat,24,PSTR("🔋%u%%"), _sensor.bat);
|
||||
if(!_sensor.feature.bat) _bat[0] = 0;
|
||||
if (_sensor.bat == 0) _bat[9] = 0;
|
||||
WSContentSend_P(HTTP_MI32_WIDGET,slot+1,_opacity,_MAC,_sensor.RSSI,_bat,_key,kMI32DeviceType[_sensor.type-1]);
|
||||
WSContentSend_P(HTTP_MI32_WIDGET,slot+1,_opacity,_MAC,_sensor.RSSI,_bat,_key,MI32getDeviceName(slot));
|
||||
|
||||
if(_sensor.feature.tempHum){
|
||||
if(_sensor.feature.temp && _sensor.feature.hum){
|
||||
if(!isnan(_sensor.temp)){
|
||||
char _polyline[176];
|
||||
MI32createPolyline(_polyline,_sensor.temp_history);
|
||||
@ -2058,15 +2082,6 @@ void MI32ShowContinuation(bool *commaflg) {
|
||||
void MI32Show(bool json)
|
||||
{
|
||||
if (json) {
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
bool _noSummarySave = MI32.option.noSummary;
|
||||
bool _minimalSummarySave = MI32.option.minimalSummary;
|
||||
if(hass_mode==2){
|
||||
MI32.option.noSummary = false;
|
||||
MI32.option.minimalSummary = false;
|
||||
}
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
|
||||
if(!MI32.mode.triggeredTele){
|
||||
if(MI32.option.noSummary) return; // no message at TELEPERIOD
|
||||
}
|
||||
@ -2077,18 +2092,14 @@ void MI32Show(bool json)
|
||||
|
||||
bool commaflg = false;
|
||||
ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"),
|
||||
kMI32DeviceType[MIBLEsensors[i].type-1],
|
||||
MI32getDeviceName(i),
|
||||
MIBLEsensors[i].MAC[3], MIBLEsensors[i].MAC[4], MIBLEsensors[i].MAC[5]);
|
||||
|
||||
if((!MI32.mode.triggeredTele && !MI32.option.minimalSummary)||MI32.mode.triggeredTele){
|
||||
bool tempHumSended = false;
|
||||
if(MIBLEsensors[i].feature.tempHum){
|
||||
if(MIBLEsensors[i].eventType.tempHum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){
|
||||
if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode!=-1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum);
|
||||
tempHumSended = true;
|
||||
@ -2097,11 +2108,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
if(MIBLEsensors[i].feature.temp && !tempHumSended){
|
||||
if(MIBLEsensors[i].eventType.temp || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) {
|
||||
if (!isnan(MIBLEsensors[i].temp)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode!=-1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if (!isnan(MIBLEsensors[i].temp)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%*_f"),
|
||||
Settings->flag2.temperature_resolution, &MIBLEsensors[i].temp);
|
||||
@ -2110,11 +2117,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
if(MIBLEsensors[i].feature.hum && !tempHumSended){
|
||||
if(MIBLEsensors[i].eventType.hum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) {
|
||||
if (!isnan(MIBLEsensors[i].hum)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode!=-1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if (!isnan(MIBLEsensors[i].hum)) {
|
||||
char hum[FLOATSZ];
|
||||
dtostrfd(MIBLEsensors[i].hum, Settings->flag2.humidity_resolution, hum);
|
||||
MI32ShowContinuation(&commaflg);
|
||||
@ -2124,17 +2127,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
if (MIBLEsensors[i].feature.lux){
|
||||
if(MIBLEsensors[i].eventType.lux || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
if ((hass_mode != -1) && (MIBLEsensors[i].lux == 0x0ffffff)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"" D_JSON_ILLUMINANCE "\":null"));
|
||||
} else
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
if ((MIBLEsensors[i].lux != 0x0ffffff)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
|| (hass_mode != -1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) { // this is the error code -> no lux
|
||||
if ((MIBLEsensors[i].lux != 0x0ffffff)) { // this is the error code -> no lux
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux);
|
||||
}
|
||||
@ -2142,17 +2135,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
if (MIBLEsensors[i].feature.moist){
|
||||
if(MIBLEsensors[i].eventType.moist || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
if ((hass_mode != -1) && (MIBLEsensors[i].moisture == 0xff)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"" D_JSON_MOISTURE "\":null"));
|
||||
} else
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
if ((MIBLEsensors[i].moisture != 0xff)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
|| (hass_mode != -1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if ((MIBLEsensors[i].moisture != 0xff)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture);
|
||||
}
|
||||
@ -2160,38 +2143,20 @@ void MI32Show(bool json)
|
||||
}
|
||||
if (MIBLEsensors[i].feature.fert){
|
||||
if(MIBLEsensors[i].eventType.fert || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
if ((hass_mode != -1) && (MIBLEsensors[i].fertility == 0xffff)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Fertility\":null"));
|
||||
} else
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
if ((MIBLEsensors[i].fertility != 0xffff)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
|| (hass_mode != -1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if ((MIBLEsensors[i].fertility != 0xffff)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Fertility\":%u"), MIBLEsensors[i].fertility);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MIBLEsensors[i].feature.Btn){
|
||||
if(MIBLEsensors[i].eventType.Btn
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode==2)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
){
|
||||
if(MIBLEsensors[i].eventType.Btn){
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Button%u\":%u"),MIBLEsensors[i].Btn,MIBLEsensors[i].BtnType + 1); //internal type is Xiaomi/Homekit 0,1,2 -> Tasmota 1,2,3
|
||||
}
|
||||
}
|
||||
if (MIBLEsensors[i].feature.knob){
|
||||
if(MIBLEsensors[i].eventType.knob
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode==2)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
){
|
||||
if(MIBLEsensors[i].eventType.knob){
|
||||
MI32ShowContinuation(&commaflg);
|
||||
char _pressed[3] = {'_','P',0};
|
||||
if (MIBLEsensors[i].pressed == 0){
|
||||
@ -2199,11 +2164,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
ResponseAppend_P(PSTR("\"Dimmer%s\":%d"),_pressed, MIBLEsensors[i].dimmer);
|
||||
}
|
||||
if(MIBLEsensors[i].eventType.longpress
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
||(hass_mode==2)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
){
|
||||
if(MIBLEsensors[i].eventType.longpress){
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Hold\":%d"), MIBLEsensors[i].longpress);
|
||||
}
|
||||
@ -2250,17 +2211,7 @@ void MI32Show(bool json)
|
||||
}
|
||||
if (MIBLEsensors[i].feature.bat){
|
||||
if(MIBLEsensors[i].eventType.bat || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
if ((hass_mode != -1) && (MIBLEsensors[i].bat == 0x00)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Battery\":null"));
|
||||
} else
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
if ((MIBLEsensors[i].bat != 0x00)
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
|| (hass_mode != -1)
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
) {
|
||||
if ((MIBLEsensors[i].bat != 0x00)) {
|
||||
MI32ShowContinuation(&commaflg);
|
||||
ResponseAppend_P(PSTR("\"Battery\":%u"), MIBLEsensors[i].bat);
|
||||
}
|
||||
@ -2279,12 +2230,6 @@ void MI32Show(bool json)
|
||||
}
|
||||
}
|
||||
MI32.mode.triggeredTele = 0;
|
||||
#ifdef USE_HOME_ASSISTANT
|
||||
if(hass_mode==2){
|
||||
MI32.option.noSummary = _noSummarySave;
|
||||
MI32.option.minimalSummary = _minimalSummarySave;
|
||||
}
|
||||
#endif //USE_HOME_ASSISTANT
|
||||
#ifdef USE_MI_EXT_GUI
|
||||
Mi32invalidateOldHistory();
|
||||
#ifdef USE_MI_ESP32_ENERGY
|
||||
@ -2307,22 +2252,23 @@ void MI32Show(bool json)
|
||||
WSContentSend_PD(HTTP_MI32_HL);
|
||||
char _MAC[18];
|
||||
ToHex_P(MIBLEsensors[i].MAC,6,_MAC,18,':');
|
||||
WSContentSend_PD(HTTP_MI32_MAC, kMI32DeviceType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, _MAC);
|
||||
WSContentSend_PD(HTTP_RSSI, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].RSSI);
|
||||
const char * _sensorName = MI32getDeviceName(i);
|
||||
WSContentSend_PD(HTTP_MI32_MAC, _sensorName, D_MAC_ADDRESS, _MAC);
|
||||
WSContentSend_PD(HTTP_RSSI, _sensorName, MIBLEsensors[i].RSSI);
|
||||
if (MIBLEsensors[i].type==FLORA) {
|
||||
if (!isnan(MIBLEsensors[i].temp)) {
|
||||
WSContentSend_Temp(kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp);
|
||||
WSContentSend_Temp(_sensorName, MIBLEsensors[i].temp);
|
||||
}
|
||||
if (MIBLEsensors[i].moisture!=0xff) {
|
||||
WSContentSend_PD(HTTP_SNS_MOISTURE, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture);
|
||||
WSContentSend_PD(HTTP_SNS_MOISTURE, _sensorName, MIBLEsensors[i].moisture);
|
||||
}
|
||||
if (MIBLEsensors[i].fertility!=0xffff) {
|
||||
WSContentSend_PD(HTTP_MI32_FLORA_DATA, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].fertility);
|
||||
WSContentSend_PD(HTTP_MI32_FLORA_DATA, _sensorName, MIBLEsensors[i].fertility);
|
||||
}
|
||||
}
|
||||
if (MIBLEsensors[i].type>FLORA) { // everything "above" Flora
|
||||
if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) {
|
||||
WSContentSend_THD(kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum);
|
||||
WSContentSend_THD(_sensorName, MIBLEsensors[i].temp, MIBLEsensors[i].hum);
|
||||
}
|
||||
}
|
||||
if (MIBLEsensors[i].feature.needsKey) {
|
||||
@ -2334,20 +2280,20 @@ void MI32Show(bool json)
|
||||
}
|
||||
}
|
||||
if (MIBLEsensors[i].type==NLIGHT || MIBLEsensors[i].type==MJYD2S) {
|
||||
WSContentSend_PD(HTTP_EVENTS, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].events);
|
||||
if(MIBLEsensors[i].NMT>0) WSContentSend_PD(HTTP_NMT, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].NMT);
|
||||
WSContentSend_PD(HTTP_EVENTS, _sensorName, MIBLEsensors[i].events);
|
||||
if(MIBLEsensors[i].NMT>0) WSContentSend_PD(HTTP_NMT, _sensorName, MIBLEsensors[i].NMT);
|
||||
}
|
||||
if(MIBLEsensors[i].door != 255 && MIBLEsensors[i].type==MCCGQ02){
|
||||
WSContentSend_PD(HTTP_DOOR, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].door);
|
||||
WSContentSend_PD(HTTP_DOOR, _sensorName, MIBLEsensors[i].door);
|
||||
}
|
||||
if (MIBLEsensors[i].lux!=0x00ffffff) { // this is the error code -> no valid value
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux);
|
||||
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, _sensorName, MIBLEsensors[i].lux);
|
||||
}
|
||||
if(MIBLEsensors[i].bat!=0x00){
|
||||
WSContentSend_PD(HTTP_BATTERY, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat);
|
||||
WSContentSend_PD(HTTP_BATTERY, _sensorName, MIBLEsensors[i].bat);
|
||||
}
|
||||
if (MIBLEsensors[i].type==YLYK01){
|
||||
WSContentSend_PD(HTTP_LASTBUTTON, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].Btn);
|
||||
WSContentSend_PD(HTTP_LASTBUTTON, _sensorName, MIBLEsensors[i].Btn);
|
||||
}
|
||||
}
|
||||
#endif //USE_MI_EXT_GUI
|
||||
|
@ -18,7 +18,7 @@ static int MI32_accessory_identify(hap_acc_t *ha);
|
||||
static void MI32_bridge_thread_entry(void *p);
|
||||
|
||||
extern uint32_t MI32numberOfDevices();
|
||||
extern const char *MI32getDeviceName(uint32_t slot);
|
||||
extern char *MI32getDeviceName(uint32_t slot);
|
||||
extern uint32_t MI32getDeviceType(uint32_t slot);
|
||||
extern void MI32saveHAPhandles(uint32_t slot, uint32_t type, void* handle);
|
||||
extern void MI32passHapEvent(uint32_t event);
|
||||
@ -32,6 +32,7 @@ static bool MIBridgeWasNeverConnected = true;
|
||||
|
||||
#define CONFIG_EXAMPLE_SETUP_ID "MI32"
|
||||
|
||||
#define UNKNOWN_MI 0
|
||||
#define FLORA 1
|
||||
#define MJ_HT_V1 2
|
||||
#define LYWSD02 3
|
||||
@ -48,6 +49,7 @@ static bool MIBridgeWasNeverConnected = true;
|
||||
#define SJWS01L 14
|
||||
#define PVVX 15
|
||||
#define YLKG08 16
|
||||
#define YLAI003 17
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Homekit
|
||||
@ -139,7 +141,8 @@ static void MI32_bridge_thread_entry(void *p)
|
||||
/* Create and add the Accessory to the Bridge object*/
|
||||
uint32_t _numDevices = MI32numberOfDevices();
|
||||
for (uint32_t i = 0; i < _numDevices; i++) {
|
||||
char *accessory_name = (char*)MI32getDeviceName(i);
|
||||
if(MI32getDeviceType(i) == UNKNOWN_MI) continue;
|
||||
char *accessory_name = MI32getDeviceName(i);
|
||||
char _serialNum[4] = {0};
|
||||
snprintf(_serialNum,sizeof(_serialNum),"%u", i);
|
||||
|
||||
|
4
tasmota/zigbee/TS0001_switch.zb
Normal file
4
tasmota/zigbee/TS0001_switch.zb
Normal file
@ -0,0 +1,4 @@
|
||||
#Z2Tv1
|
||||
# TuYa generic switch https://www.zigbee2mqtt.io/devices/TS0001_switch_module.html
|
||||
:TS0001,_TZ3000_*
|
||||
0006/8002%30,StartUpOnOff
|
@ -287,7 +287,7 @@ a_features = [[
|
||||
"USE_BP5758D","USE_HYT","USE_SM2335","USE_DISPLAY_TM1621_SONOFF"
|
||||
],[
|
||||
"USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","USE_QMC5883L",
|
||||
"","","","",
|
||||
"USE_MODBUS_ENERGY","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
|
Loading…
x
Reference in New Issue
Block a user