From f3b6cd1d7391d60498ebd5a84e000591ae37aa29 Mon Sep 17 00:00:00 2001 From: Dmytro Shestakov Date: Wed, 2 Nov 2022 18:38:53 +0200 Subject: [PATCH 1/5] Add TM1637 driver written in Berry It allows to use this type of display in addition to any standard Tasmota display simultaneously --- tasmota/berry/drivers/tm1637.be | 190 ++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 tasmota/berry/drivers/tm1637.be diff --git a/tasmota/berry/drivers/tm1637.be b/tasmota/berry/drivers/tm1637.be new file mode 100644 index 000000000..9e7b572f0 --- /dev/null +++ b/tasmota/berry/drivers/tm1637.be @@ -0,0 +1,190 @@ +#- +Simplified Tasmota TM1637 driver written in Berry +Might be helpful in the case of using multiple displays +Supports only the 4 digit basic display + +DIO_PIN and CLK_PIN are your esp32 pin numbers +How to use + +> load('tm1637') +> tm = Tm1637(DIO_PIN, CLK_PIN) +> tm.set_on(4) +> tm.print('0123') +> tm.print(456) + +Add custom commands to the native Tasmota console: +> tm_add_custom_commands(DIO_PIN, CLK_PIN) + +And then: +TmBrightness 2 +TmPrint 0123 +TmPrint -5.67 + +Note: adding these commands to autoexec.be should be performed via creating an additional .be file with the content: + tm_add_custom_commands(DIO_PIN, CLK_PIN) + +and then loading it in autoexec.be via load('tm1637') and load('script_name') +The direct addition may not work +-# + +class Tm1637 + static var CMD_CTRL = 0x80 + static var CMD_DISP_ON = 0x08 + static var CMD_DATA = 0x40 + static var CMD_ADDR = 0xC0 + + static var SYMB_DOT = 0x80 + + static var DIGIT_MAP = { + '0': 0x3F, + '1': 0x06, + '2': 0x5B, + '3': 0x4F, + '4': 0x66, + '5': 0x6D, + '6': 0x7D, + '7': 0x07, + '8': 0x7F, + '9': 0x6F, + '-': 0x40, + ' ': 0x00 + } + + var PIN_DIO + var PIN_CLK + + def init(dio, clk) + self.PIN_DIO = dio + self.PIN_CLK = clk + gpio.pin_mode(self.PIN_DIO, gpio.OUTPUT) + gpio.pin_mode(self.PIN_CLK, gpio.OUTPUT) + gpio.digital_write(self.PIN_DIO, 1) + gpio.digital_write(self.PIN_CLK, 1) + end + + def start() + gpio.digital_write(self.PIN_DIO, 1) + gpio.digital_write(self.PIN_CLK, 1) + gpio.digital_write(self.PIN_DIO, 0) + end + + def stop() + gpio.digital_write(self.PIN_CLK, 0) + gpio.digital_write(self.PIN_DIO, 0) + gpio.digital_write(self.PIN_CLK, 1) + gpio.digital_write(self.PIN_DIO, 1) + end + + def ack() + gpio.digital_write(self.PIN_CLK, 0) + gpio.pin_mode(self.PIN_DIO, gpio.INPUT_PULLUP) + var ack_state = gpio.digital_read(self.PIN_DIO) == 0 + gpio.digital_write(self.PIN_CLK, 1) + gpio.digital_write(self.PIN_CLK, 0) + gpio.pin_mode(self.PIN_DIO, gpio.OUTPUT) + return ack_state + end + + def write_bit(bitval) + gpio.digital_write(self.PIN_CLK, 0) + gpio.digital_write(self.PIN_DIO, bitval) + gpio.digital_write(self.PIN_CLK, 1) + end + + def write_byte(byteval) + for pos: 0..7 + self.write_bit((byteval >> pos) & 0x01) + end + end + + def send_command(command) + self.start() + self.write_byte(command) + var ack_state = self.ack() + self.stop() + return ack_state + end + + def send_data(data) + var ack_state = true + self.start() + for i : 0..size(data)-1 + self.write_byte(data[i]) + ack_state = self.ack() && ack_state + end + self.stop() + return ack_state + end + + # 0-8 range, 0 to 'OFF' + def set_on(brightness) + if brightness == nil || brightness > 8 + brightness = 8 + elif brightness < 0 + brightness = 0 + end + var cmd = self.CMD_CTRL + if brightness + cmd |= self.CMD_DISP_ON + brightness -= 1 + end + return self.send_command(cmd | brightness) + end + + def print(num) + import string + + num = str(num) + var max_str_len = 4 + + do + var dot_pos = string.find(num, '.') + if dot_pos >= 0 && dot_pos < 5 + max_str_len = 5 + end + end + if size(num) > max_str_len + num = string.split(num, max_str_len)[0] + end + num = string.format('%4s', num) + var payload = bytes(-5) + payload[0] = self.CMD_ADDR + var int_offset = 1 + for i : 0..size(num)-1 + if num[i] == '.' + payload[i] |= self.SYMB_DOT + int_offset = 0 + else + payload[i + int_offset] = self.DIGIT_MAP[num[i]] + end + end + var ack_state = self.send_command(self.CMD_DATA) && self.send_data(payload) + if !ack_state + log('TM1637 - no ACK, please check connections') + end + return ack_state + end + + def clear() + self.print(' ') + end + + # Won't be called on the system restart + def deinit() + self.set_on(0) + end +end + +def tm_add_custom_commands(dio, clk) + var tm = Tm1637(dio, clk) + tm.clear() + tm.set_on(4) + tasmota.add_cmd('tmprint', def(cmd, idx, payload) + tm.print(payload) ? tasmota.resp_cmnd_done() : tasmota.resp_cmnd_failed() + end) + # 0-8 range, 0 to 'OFF' + tasmota.add_cmd('tmbrightness', def(cmd, idx, payload) + tm.set_on(int(payload)) ? tasmota.resp_cmnd_done() : tasmota.resp_cmnd_failed() + end) + log("Tasmota custom commands registered: TmPrint, TmBrightness") +end From e910f3071c6c03df41cb51eb7804ece1b7d4586c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 6 Nov 2022 10:52:17 +0100 Subject: [PATCH 2/5] Berry add ``dyn`` class --- CHANGELOG.md | 1 + lib/libesp32/berry/default/be_modtab.c | 2 + lib/libesp32/berry_tasmota/src/be_dyn_class.c | 4 + .../berry_tasmota/src/embedded/dyn.be | 27 +++ .../src/solidify/solidified_dyn.h | 157 ++++++++++++++++++ 5 files changed, 191 insertions(+) create mode 100644 lib/libesp32/berry_tasmota/src/be_dyn_class.c create mode 100644 lib/libesp32/berry_tasmota/src/embedded/dyn.be create mode 100644 lib/libesp32/berry_tasmota/src/solidify/solidified_dyn.h diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c140e23..52a1b30b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - ESP32 Support for DMX ArtNet Led matrix animations (#16984) - Command ``SetOption47 1..255`` to delay power on relay state in seconds reducing power surge. ``SO47 1`` delays until network connected. ``SO47 2`` delays until mqtt connected - ESP32 DMX ArtNet optimization to avoid any object allocation and avoid garbage collector pauses +- Berry add ``dyn`` class ### Breaking Changed diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c index 1cc28efee..5b018304e 100644 --- a/lib/libesp32/berry/default/be_modtab.c +++ b/lib/libesp32/berry/default/be_modtab.c @@ -178,6 +178,7 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = { NULL /* do not remove */ }; +be_extern_native_class(dyn); be_extern_native_class(tasmota); be_extern_native_class(Trigger); be_extern_native_class(Driver); @@ -228,6 +229,7 @@ be_extern_native_class(int64); BERRY_LOCAL bclass_array be_class_table = { #ifdef TASMOTA /* first list are direct classes */ + &be_native_class(dyn), &be_native_class(tasmota), &be_native_class(Trigger), &be_native_class(Driver), diff --git a/lib/libesp32/berry_tasmota/src/be_dyn_class.c b/lib/libesp32/berry_tasmota/src/be_dyn_class.c new file mode 100644 index 000000000..b41ac1b29 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/be_dyn_class.c @@ -0,0 +1,4 @@ +/******************************************************************** + * Tasmota dyn class + *******************************************************************/ +#include "solidify/solidified_dyn.h" diff --git a/lib/libesp32/berry_tasmota/src/embedded/dyn.be b/lib/libesp32/berry_tasmota/src/embedded/dyn.be new file mode 100644 index 000000000..5266304ba --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/embedded/dyn.be @@ -0,0 +1,27 @@ +################################################################################# +# dyn class +# +# Allows to use a map with members +# see https://github.com/berry-lang/berry/wiki/Chapter-8 +################################################################################# +#@ solidify:dyn +class dyn + var _attr + def init() + self._attr = {} + end + def setmember(name, value) + self._attr[name] = value + end + def member(name) + if self._attr.contains(name) + return self._attr[name] + else + import undefined + return undefined + end + end + def tostring() + return self._attr.tostring() + end +end diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_dyn.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_dyn.h new file mode 100644 index 000000000..ccb1c98e5 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_dyn.h @@ -0,0 +1,157 @@ +/* Solidification of dyn.h */ +/********************************************************************\ +* Generated code, don't edit * +\********************************************************************/ +#include "be_constobj.h" + +/******************************************************************** +** Solidified function: tostring +********************************************************************/ +be_local_closure(dyn_tostring, /* name */ + be_nested_proto( + 3, /* nstack */ + 1, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 2]) { /* constants */ + /* K0 */ be_nested_str(_attr), + /* K1 */ be_nested_str(tostring), + }), + &be_const_str_tostring, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x88040100, // 0000 GETMBR R1 R0 K0 + 0x8C040301, // 0001 GETMET R1 R1 K1 + 0x7C040200, // 0002 CALL R1 1 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: member +********************************************************************/ +be_local_closure(dyn_member, /* name */ + be_nested_proto( + 5, /* nstack */ + 2, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 3]) { /* constants */ + /* K0 */ be_nested_str(_attr), + /* K1 */ be_nested_str(contains), + /* K2 */ be_nested_str(undefined), + }), + &be_const_str_member, + &be_const_str_solidified, + ( &(const binstruction[12]) { /* code */ + 0x88080100, // 0000 GETMBR R2 R0 K0 + 0x8C080501, // 0001 GETMET R2 R2 K1 + 0x5C100200, // 0002 MOVE R4 R1 + 0x7C080400, // 0003 CALL R2 2 + 0x780A0003, // 0004 JMPF R2 #0009 + 0x88080100, // 0005 GETMBR R2 R0 K0 + 0x94080401, // 0006 GETIDX R2 R2 R1 + 0x80040400, // 0007 RET 1 R2 + 0x70020001, // 0008 JMP #000B + 0xA40A0400, // 0009 IMPORT R2 K2 + 0x80040400, // 000A RET 1 R2 + 0x80000000, // 000B RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: setmember +********************************************************************/ +be_local_closure(dyn_setmember, /* name */ + be_nested_proto( + 4, /* nstack */ + 3, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 1]) { /* constants */ + /* K0 */ be_nested_str(_attr), + }), + &be_const_str_setmember, + &be_const_str_solidified, + ( &(const binstruction[ 3]) { /* code */ + 0x880C0100, // 0000 GETMBR R3 R0 K0 + 0x980C0202, // 0001 SETIDX R3 R1 R2 + 0x80000000, // 0002 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: init +********************************************************************/ +be_local_closure(dyn_init, /* name */ + be_nested_proto( + 2, /* nstack */ + 1, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 1]) { /* constants */ + /* K0 */ be_nested_str(_attr), + }), + &be_const_str_init, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x60040013, // 0000 GETGBL R1 G19 + 0x7C040000, // 0001 CALL R1 0 + 0x90020001, // 0002 SETMBR R0 K0 R1 + 0x80000000, // 0003 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified class: dyn +********************************************************************/ +be_local_class(dyn, + 1, + NULL, + be_nested_map(5, + ( (struct bmapnode*) &(const bmapnode[]) { + { be_const_key(tostring, 2), be_const_closure(dyn_tostring_closure) }, + { be_const_key(member, 3), be_const_closure(dyn_member_closure) }, + { be_const_key(init, 4), be_const_closure(dyn_init_closure) }, + { be_const_key(setmember, -1), be_const_closure(dyn_setmember_closure) }, + { be_const_key(_attr, -1), be_const_var(0) }, + })), + (bstring*) &be_const_str_dyn +); +/*******************************************************************/ + +void be_load_dyn_class(bvm *vm) { + be_pushntvclass(vm, &be_class_dyn); + be_setglobal(vm, "dyn"); + be_pop(vm, 1); +} +/********************************************************************/ +/* End of solidification */ From 31516f2d34649c0216640b205f7f67d11b2b0618 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sun, 6 Nov 2022 10:12:08 +0100 Subject: [PATCH 3/5] Add ModbusBridge malloc error notes --- .../xdrv_63_modbus_bridge.ino | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 479ea560b..43bea32bf 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -187,6 +187,10 @@ uint32_t swap_endian32(uint32_t num) ((num<<24)&0xff000000); // byte 0 to byte 3 } +void ModbusBridgeAllocError(const char* s) +{ + AddLog(LOG_LEVEL_ERROR, PSTR("MBS: could not allocate %s buffer"), s); +} /********************************************************************************************/ // @@ -248,6 +252,11 @@ void ModbusBridgeHandle(void) uint8_t *buffer; if (modbusBridge.byteCount == 0) modbusBridge.byteCount = modbusBridge.dataCount * 2; buffer = (uint8_t *)malloc(9 + modbusBridge.byteCount); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2) + if (nullptr == buffer) + { + ModbusBridgeAllocError(PSTR("read")); + return; + } memset(buffer, 0, 9 + modbusBridge.byteCount); uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, 0, modbusBridge.byteCount); @@ -562,9 +571,9 @@ void ModbusBridgeInit(void) #ifdef USE_MODBUS_BRIDGE_TCP // If TCP bridge is enabled allocate a TCP receive buffer modbusBridgeTCP.tcp_buf = (uint8_t *)malloc(MODBUS_BRIDGE_TCP_BUF_SIZE); - if (!modbusBridgeTCP.tcp_buf) + if (nullptr == modbusBridgeTCP.tcp_buf) { - AddLog(LOG_LEVEL_ERROR, PSTR("MBS: MBRTCP could not allocate buffer")); + ModbusBridgeAllocError(PSTR("TCP")); return; } #endif @@ -674,6 +683,11 @@ void ModbusTCPHandle(void) modbusBridge.dataCount = 1; writeData = (uint16_t *)malloc((byteCount / 2)+1); + if (nullptr == writeData) + { + ModbusBridgeAllocError(PSTR("write")); + return; + } if ((mbfunctioncode == 15) || (mbfunctioncode == 16)) count = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[10]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[11])); else count = 1; @@ -860,6 +874,11 @@ void CmndModbusBridgeSend(void) else { writeData = (uint16_t *)malloc(modbusBridge.dataCount); + if (nullptr == writeData) + { + ModbusBridgeAllocError(PSTR("write")); + return; + } for (uint8_t jsonDataArrayPointer = 0; jsonDataArrayPointer < writeDataSize; jsonDataArrayPointer++) { From f76bed338be31a2e92f5ba290dbc47820ff6347e Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sun, 6 Nov 2022 10:23:05 +0100 Subject: [PATCH 4/5] Localize ModbusBridge global func/var names --- .../xdrv_63_modbus_bridge.ino | 106 ++++++++---------- 1 file changed, 49 insertions(+), 57 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 43bea32bf..0e39e97a6 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -35,10 +35,10 @@ * * -- Write multiple coils -- * ModbusSend {"deviceaddress": 1, "functioncode": 15, "startaddress": 1, "type":"bit", "count":4, "values":[1,0,1,1]} - * - * Info for modbusBridgeTCPServer: + * + * Info for modbusBridgeTCPServer: * https://ipc2u.com/articles/knowledge-base/detailed-description-of-the-modbus-tcp-protocol-with-command-examples/ - * + * * Info for modbus serial communications: * https://ozeki.hu/p_5879-mobdbus-function-code-4-read-input-registers.html * https://www.modbustools.com/modbus.html @@ -102,7 +102,7 @@ ModbusBridgeTCP modbusBridgeTCP; #endif #include -TasmotaModbus *tasmotaModbus = nullptr; +TasmotaModbus *modbusBridgeModbus = nullptr; enum class ModbusBridgeError { @@ -172,21 +172,13 @@ ModbusBridge modbusBridge; /********************************************************************************************/ // -// Helper functions for data conversion between little and big endian +// Helper functions // -uint16_t swap_endian16(uint16_t num) +uint16_t ModbusBridgeSwapEndian16(uint16_t num) { return (num>>8) | (num<<8); } -uint32_t swap_endian32(uint32_t num) -{ - return ((num>>24)&0xff) | // move byte 3 to byte 0 - ((num<<8)&0xff0000) | // move byte 1 to byte 2 - ((num>>8)&0xff00) | // move byte 2 to byte 1 - ((num<<24)&0xff000000); // byte 0 to byte 3 -} - void ModbusBridgeAllocError(const char* s) { AddLog(LOG_LEVEL_ERROR, PSTR("MBS: could not allocate %s buffer"), s); @@ -203,7 +195,7 @@ bool ModbusBridgeBegin(void) if (Settings->modbus_sconfig > TS_SERIAL_8O2) Settings->modbus_sconfig = TS_SERIAL_8N1; - int result = tasmotaModbus->Begin(Settings->modbus_sbaudrate * 300, ConvertSerialConfig(Settings->modbus_sconfig)); // Reinitialize modbus port with new baud rate + int result = modbusBridgeModbus->Begin(Settings->modbus_sbaudrate * 300, ConvertSerialConfig(Settings->modbus_sconfig)); // Reinitialize modbus port with new baud rate if (result) { if (2 == result) @@ -215,7 +207,7 @@ bool ModbusBridgeBegin(void) return result; } -void SetModbusBridgeConfig(uint32_t serial_config) +void ModbusBridgeSetConfig(uint32_t serial_config) { if (serial_config > TS_SERIAL_8O2) { @@ -228,7 +220,7 @@ void SetModbusBridgeConfig(uint32_t serial_config) } } -void SetModbusBridgeBaudrate(uint32_t baudrate) +void ModbusBridgeSetBaudrate(uint32_t baudrate) { if ((baudrate >= 300) && (baudrate <= 115200)) { @@ -246,7 +238,7 @@ void SetModbusBridgeBaudrate(uint32_t baudrate) // void ModbusBridgeHandle(void) { - bool data_ready = tasmotaModbus->ReceiveReady(); + bool data_ready = modbusBridgeModbus->ReceiveReady(); if (data_ready) { uint8_t *buffer; @@ -258,7 +250,7 @@ void ModbusBridgeHandle(void) return; } memset(buffer, 0, 9 + modbusBridge.byteCount); - uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, 0, modbusBridge.byteCount); + uint32_t error = modbusBridgeModbus->ReceiveBuffer(buffer, 0, modbusBridge.byteCount); #ifdef USE_MODBUS_BRIDGE_TCP for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) @@ -283,7 +275,7 @@ void ModbusBridgeHandle(void) nrOfBytes += 1; client.write(header, 9); } - else if (buffer[1] <= 2) + else if (buffer[1] <= 2) { header[4] = modbusBridge.byteCount >> 8; header[5] = modbusBridge.byteCount + 3; @@ -293,7 +285,7 @@ void ModbusBridgeHandle(void) client.write(buffer + 3, modbusBridge.byteCount); // Don't send CRC nrOfBytes += modbusBridge.byteCount; } - else if (buffer[1] <= 4) + else if (buffer[1] <= 4) { header[4] = modbusBridge.byteCount >> 8; header[5] = modbusBridge.byteCount + 3; @@ -367,10 +359,10 @@ void ModbusBridgeHandle(void) if (modbusBridge.type == ModbusBridgeType::mb_raw) { Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{\"RAW\":[")); - for (uint8_t i = 0; i < tasmotaModbus->ReceiveCount(); i++) + for (uint8_t i = 0; i < modbusBridgeModbus->ReceiveCount(); i++) { ResponseAppend_P(PSTR("%d"), buffer[i]); - if (i < tasmotaModbus->ReceiveCount() - 1) + if (i < modbusBridgeModbus->ReceiveCount() - 1) ResponseAppend_P(PSTR(",")); } ResponseAppend_P(PSTR("]}")); @@ -380,10 +372,10 @@ void ModbusBridgeHandle(void) else if (modbusBridge.type == ModbusBridgeType::mb_hex) { Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{\"HEX\":[")); - for (uint8_t i = 0; i < tasmotaModbus->ReceiveCount(); i++) + for (uint8_t i = 0; i < modbusBridgeModbus->ReceiveCount(); i++) { ResponseAppend_P(PSTR("0x%02X"), buffer[i]); - if (i < tasmotaModbus->ReceiveCount() - 1) + if (i < modbusBridgeModbus->ReceiveCount() - 1) ResponseAppend_P(PSTR(",")); } ResponseAppend_P(PSTR("]}")); @@ -405,7 +397,7 @@ void ModbusBridgeHandle(void) ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_START_ADDRESS "\":%d,"), (buffer[2] << 8) + buffer[3]); dataOffset = 4; } - ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_LENGTH "\":%d,"), tasmotaModbus->ReceiveCount()); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_LENGTH "\":%d,"), modbusBridgeModbus->ReceiveCount()); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_COUNT "\":%d,"), modbusBridge.count); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_VALUES "\":[")); @@ -539,7 +531,7 @@ void ModbusBridgeHandle(void) ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_FUNCTION_CODE "\":%d,"), buffer[1]); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_START_ADDRESS "\":%d,"), (buffer[2] << 8) + buffer[3]); - ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_LENGTH "\":%d,"), tasmotaModbus->ReceiveCount()); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_LENGTH "\":%d,"), modbusBridgeModbus->ReceiveCount()); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_COUNT "\":%d"), (buffer[4] << 8) + buffer[5]); ResponseAppend_P(PSTR("}")); ResponseJsonEnd(); @@ -566,7 +558,7 @@ void ModbusBridgeInit(void) { if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) { - tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); + modbusBridgeModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); ModbusBridgeBegin(); #ifdef USE_MODBUS_BRIDGE_TCP // If TCP bridge is enabled allocate a TCP receive buffer @@ -591,7 +583,7 @@ void ModbusTCPHandle(void) bool busy; // did we transfer some data? int32_t buf_len; - if (!tasmotaModbus) + if (!modbusBridgeModbus) return; // check for a new client connection @@ -665,8 +657,8 @@ void ModbusTCPHandle(void) if (mbfunctioncode <= 2) { count = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[10]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[11])); - modbusBridge.byteCount = ((count - 1) >> 3) + 1; - modbusBridge.dataCount = ((count - 1) >> 4) + 1; + modbusBridge.byteCount = ((count - 1) >> 3) + 1; + modbusBridge.dataCount = ((count - 1) >> 4) + 1; } else if (mbfunctioncode <= 4) { @@ -676,7 +668,7 @@ void ModbusTCPHandle(void) } else { - // For functioncode 15 & 16 ignore bytecount, tasmotaModbus does calculate this + // For functioncode 15 & 16 ignore bytecount, modbusBridgeModbus does calculate this uint8_t dataStartByte = mbfunctioncode <= 6 ? 10 : 13; uint16_t byteCount = (buf_len - dataStartByte); modbusBridge.byteCount = 2; @@ -688,10 +680,10 @@ void ModbusTCPHandle(void) ModbusBridgeAllocError(PSTR("write")); return; } - + if ((mbfunctioncode == 15) || (mbfunctioncode == 16)) count = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[10]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[11])); else count = 1; - + for (uint16_t dataPointer = 0; dataPointer < byteCount; dataPointer++) { if (dataPointer % 2 == 0) @@ -699,7 +691,7 @@ void ModbusTCPHandle(void) writeData[dataPointer / 2] = (uint16_t)(((uint16_t)modbusBridgeTCP.tcp_buf[dataStartByte + dataPointer]) << 8); } else - { + { writeData[dataPointer / 2] |= ((uint16_t)modbusBridgeTCP.tcp_buf[dataStartByte + dataPointer]); } } @@ -708,7 +700,7 @@ void ModbusTCPHandle(void) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: MBRTCP to Modbus TransactionId:%d, deviceAddress:%d, functionCode:%d, startAddress:%d, count:%d, recvCount:%d, recvBytes:%d"), modbusBridgeTCP.tcp_transaction_id, mbdeviceaddress, mbfunctioncode, mbstartaddress, count, modbusBridge.dataCount, modbusBridge.byteCount); - tasmotaModbus->Send(mbdeviceaddress, mbfunctioncode, mbstartaddress, count, writeData); + modbusBridgeModbus->Send(mbdeviceaddress, mbfunctioncode, mbstartaddress, count, writeData); free(writeData); } @@ -908,7 +900,7 @@ void CmndModbusBridgeSend(void) writeData[jsonDataArrayPointer / 2] = (int8_t)jsonDataArray[jsonDataArrayPointer / 2].getInt(0) << 8; if (modbusBridge.dataCount != writeDataSize / 2) errorcode = ModbusBridgeError::wrongcount; break; - + case ModbusBridgeType::mb_hex: case ModbusBridgeType::mb_raw: case ModbusBridgeType::mb_uint8: @@ -918,31 +910,31 @@ void CmndModbusBridgeSend(void) writeData[jsonDataArrayPointer / 2] = (uint8_t)jsonDataArray[jsonDataArrayPointer].getUInt(0) << 8; if (modbusBridge.dataCount != writeDataSize / 2) errorcode = ModbusBridgeError::wrongcount; break; - + case ModbusBridgeType::mb_int16: - writeData[jsonDataArrayPointer] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getInt(0)) + writeData[jsonDataArrayPointer] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getInt(0)) : (int16_t)jsonDataArray[jsonDataArrayPointer].getInt(0); break; - + case ModbusBridgeType::mb_uint16: - writeData[jsonDataArrayPointer] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getUInt(0)) + writeData[jsonDataArrayPointer] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getUInt(0)) : (int16_t)jsonDataArray[jsonDataArrayPointer].getUInt(0); break; - + case ModbusBridgeType::mb_int32: - writeData[(jsonDataArrayPointer * 2)] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getInt(0)) + writeData[(jsonDataArrayPointer * 2)] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getInt(0)) : (int16_t)(jsonDataArray[jsonDataArrayPointer].getInt(0) >> 16); - writeData[(jsonDataArrayPointer * 2) + 1] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getInt(0) >> 16) + writeData[(jsonDataArrayPointer * 2) + 1] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getInt(0) >> 16) : (uint16_t)(jsonDataArray[jsonDataArrayPointer].getInt(0)); break; - + case ModbusBridgeType::mb_uint32: - writeData[(jsonDataArrayPointer * 2)] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getUInt(0)) + writeData[(jsonDataArrayPointer * 2)] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getUInt(0)) : (uint16_t)(jsonDataArray[jsonDataArrayPointer].getUInt(0) >> 16); - writeData[(jsonDataArrayPointer * 2) + 1] = bitMode ? swap_endian16(jsonDataArray[jsonDataArrayPointer].getUInt(0) >> 16) + writeData[(jsonDataArrayPointer * 2) + 1] = bitMode ? ModbusBridgeSwapEndian16(jsonDataArray[jsonDataArrayPointer].getUInt(0) >> 16) : (uint16_t)(jsonDataArray[jsonDataArrayPointer].getUInt(0)); break; - + case ModbusBridgeType::mb_float: // TODO default: @@ -971,20 +963,20 @@ void CmndModbusBridgeSend(void) if ((modbusBridge.functionCode == ModbusBridgeFunctionCode::mb_writeSingleCoil) || (modbusBridge.functionCode == ModbusBridgeFunctionCode::mb_writeSingleRegister)) modbusBridge.dataCount = 1; - uint8_t error = tasmotaModbus->Send(modbusBridge.deviceAddress, (uint8_t)modbusBridge.functionCode, modbusBridge.startAddress, modbusBridge.dataCount, writeData); + uint8_t error = modbusBridgeModbus->Send(modbusBridge.deviceAddress, (uint8_t)modbusBridge.functionCode, modbusBridge.startAddress, modbusBridge.dataCount, writeData); free(writeData); - + if (error) { AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Driver send error %u"), error); return; - } + } ResponseCmndDone(); } void CmndModbusBridgeSetBaudrate(void) { - SetModbusBridgeBaudrate(XdrvMailbox.payload); + ModbusBridgeSetBaudrate(XdrvMailbox.payload); ResponseCmndNumber(Settings->modbus_sbaudrate * 300); } @@ -1000,7 +992,7 @@ void CmndModbusBridgeSetConfig(void) { // Use 0..23 as serial config option if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { - SetModbusBridgeConfig(XdrvMailbox.payload); + ModbusBridgeSetConfig(XdrvMailbox.payload); } } else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) @@ -1008,7 +1000,7 @@ void CmndModbusBridgeSetConfig(void) int8_t serial_config = ParseSerialConfig(XdrvMailbox.data); if (serial_config >= 0) { - SetModbusBridgeConfig(serial_config); + ModbusBridgeSetConfig(serial_config); } } } @@ -1023,7 +1015,7 @@ void CmndModbusBridgeSetConfig(void) void CmndModbusTCPStart(void) { - if (!tasmotaModbus) + if (!modbusBridgeModbus) { return; } @@ -1076,7 +1068,7 @@ void CmndModbusTCPConnect(void) { int32_t tcp_port = XdrvMailbox.payload; - if (!tasmotaModbus) + if (!modbusBridgeModbus) { return; } @@ -1135,7 +1127,7 @@ bool Xdrv63(uint8_t function) { ModbusBridgeInit(); } - else if (tasmotaModbus) + else if (modbusBridgeModbus) { switch (function) { From 050f2e7e61f9544e4996735e6b006a34dc20f2d1 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sun, 6 Nov 2022 12:32:02 +0100 Subject: [PATCH 5/5] Fix ModbusBridge buffer overflow (#16979) --- CHANGELOG.md | 1 + tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a1b30b0..5a3da0ee0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Deduplicate code and fix %timer n% rule regression from v12.2.0 (#16914) - Serial initialization for baudrate and config (#16970) +- ModbusBridge buffer overflow (#16979) ### Removed - Define ``USE_PN532_DATA_RAW`` from NFC reader (#16939) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 0e39e97a6..31b1c7a19 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -258,7 +258,7 @@ void ModbusBridgeHandle(void) WiFiClient &client = modbusBridgeTCP.client_tcp[i]; if (client) { - uint8_t header[8]; + uint8_t header[9]; uint8_t nrOfBytes = 8; header[0] = modbusBridgeTCP.tcp_transaction_id >> 8; header[1] = modbusBridgeTCP.tcp_transaction_id;