2022-07-19 09:37:03 +01:00
/*
2022-07-20 09:41:02 +01:00
xdrv_63_modbus_bridge . ino - modbus bridge support for Tasmota
2022-07-19 09:37:03 +01:00
2022-07-20 09:41:02 +01:00
Copyright ( C ) 2021 Theo Arends and Jeroenst
2022-07-19 09:37:03 +01:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2022-07-25 08:56:35 +01:00
# if defined(USE_MODBUS_BRIDGE)
2022-07-19 09:37:03 +01:00
/*********************************************************************************************\
* Modbus Bridge using Modbus library ( TasmotaModbus )
2022-07-20 10:11:18 +01:00
*
2022-07-25 08:56:35 +01:00
* Can be used trough web / mqtt commands and also via direct TCP connection ( when defined )
2022-08-17 18:55:41 +01:00
*
2022-07-25 08:56:35 +01:00
* When USE_MODBUS_BRIDGE_TCP is also defined , this bridge can also be used as an ModbusTCP
* bridge .
*
2022-08-27 08:11:36 +01:00
* Example Commands :
* - - Read Coils - -
* ModbusSend { " deviceaddress " : 1 , " functioncode " : 1 , " startaddress " : 1 , " type " : " bit " , " count " : 2 }
*
2022-08-17 18:55:41 +01:00
* - - Read Input Register - -
2022-07-25 08:56:35 +01:00
* ModbusSend { " deviceaddress " : 1 , " functioncode " : 3 , " startaddress " : 1 , " type " : " uint16 " , " count " : 2 }
2022-08-27 08:11:36 +01:00
*
2022-08-17 18:55:41 +01:00
* - - Write multiple coils - -
2022-08-27 08:11:36 +01:00
* ModbusSend { " deviceaddress " : 1 , " functioncode " : 15 , " startaddress " : 1 , " type " : " bit " , " count " : 4 , " values " : [ 1 , 0 , 1 , 1 ] }
2022-11-06 09:23:05 +00:00
*
* Info for modbusBridgeTCPServer :
2022-08-27 08:11:36 +01:00
* https : //ipc2u.com/articles/knowledge-base/detailed-description-of-the-modbus-tcp-protocol-with-command-examples/
2022-11-06 09:23:05 +00:00
*
2022-08-27 08:11:36 +01:00
* Info for modbus serial communications :
* https : //ozeki.hu/p_5879-mobdbus-function-code-4-read-input-registers.html
* https : //www.modbustools.com/modbus.html
* https : //ipc2u.com/articles/knowledge-base/modbus-rtu-made-simple-with-detailed-descriptions-and-examples/
2022-07-19 09:37:03 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-07-25 08:56:35 +01:00
# define XDRV_63 63
2022-07-19 09:37:03 +01:00
2022-07-25 08:56:35 +01:00
# define MBR_MAX_VALUE_LENGTH 30
2022-07-25 18:18:30 +01:00
# define MBR_BAUDRATE TM_MODBUS_BAUDRATE
2022-07-25 08:56:35 +01:00
# define MBR_MAX_REGISTERS 64
2022-07-20 09:41:02 +01:00
# define D_CMND_MODBUS_SEND "Send"
# define D_CMND_MODBUS_SETBAUDRATE "Baudrate"
# define D_CMND_MODBUS_SETSERIALCONFIG "SerialConfig"
2022-07-19 09:37:03 +01:00
# define D_JSON_MODBUS_RECEIVED "ModbusReceived"
# define D_JSON_MODBUS_DEVICE_ADDRESS "DeviceAddress"
# define D_JSON_MODBUS_FUNCTION_CODE "FunctionCode"
# define D_JSON_MODBUS_START_ADDRESS "StartAddress"
# define D_JSON_MODBUS_COUNT "Count"
# define D_JSON_MODBUS_ENDIAN "Endian"
# define D_JSON_MODBUS_TYPE "Type" // allready defined
# define D_JSON_MODBUS_VALUES "Values"
# define D_JSON_MODBUS_LENGTH "Length"
2022-07-25 08:56:35 +01:00
# ifndef USE_MODBUS_BRIDGE_TCP
2022-07-20 09:41:02 +01:00
const char kModbusBridgeCommands [ ] PROGMEM = " Modbus| " // Prefix
2022-07-19 09:37:03 +01:00
D_CMND_MODBUS_SEND " | " D_CMND_MODBUS_SETBAUDRATE " | " D_CMND_MODBUS_SETSERIALCONFIG ;
void ( * const ModbusBridgeCommand [ ] ) ( void ) PROGMEM = {
& CmndModbusBridgeSend , & CmndModbusBridgeSetBaudrate , & CmndModbusBridgeSetConfig } ;
2022-07-25 08:56:35 +01:00
# endif
# ifdef USE_MODBUS_BRIDGE_TCP
# define MODBUS_BRIDGE_TCP_CONNECTIONS 1 // number of maximum parallel connections, only 1 supported with modbus
# define MODBUS_BRIDGE_TCP_BUF_SIZE 255 // size of the buffer, above 132 required for efficient XMODEM
# define D_CMND_MODBUS_TCP_START "TCPStart"
# define D_CMND_MODBUS_TCP_CONNECT "TCPConnect"
const char kModbusBridgeCommands [ ] PROGMEM = " Modbus| " // Prefix
D_CMND_MODBUS_TCP_START " | " D_CMND_MODBUS_TCP_CONNECT " | " D_CMND_MODBUS_SEND " | " D_CMND_MODBUS_SETBAUDRATE " | " D_CMND_MODBUS_SETSERIALCONFIG ;
void ( * const ModbusBridgeCommand [ ] ) ( void ) PROGMEM = {
& CmndModbusTCPStart , & CmndModbusTCPConnect ,
& CmndModbusBridgeSend , & CmndModbusBridgeSetBaudrate , & CmndModbusBridgeSetConfig } ;
struct ModbusBridgeTCP
{
WiFiServer * server_tcp = nullptr ;
WiFiClient client_tcp [ MODBUS_BRIDGE_TCP_CONNECTIONS ] ;
uint8_t client_next = 0 ;
uint8_t * tcp_buf = nullptr ; // data transfer buffer
IPAddress ip_filter ;
uint16_t tcp_transaction_id = 0 ;
} ;
ModbusBridgeTCP modbusBridgeTCP ;
# endif
2022-07-19 09:37:03 +01:00
# include <TasmotaModbus.h>
2022-11-06 09:23:05 +00:00
TasmotaModbus * modbusBridgeModbus = nullptr ;
2022-07-19 09:37:03 +01:00
enum class ModbusBridgeError
{
noerror = 0 ,
nodataexpected = 1 ,
wrongdeviceaddress = 2 ,
wrongfunctioncode = 3 ,
wrongstartaddress = 4 ,
wrongtype = 5 ,
2022-08-17 18:55:41 +01:00
wrongdataCount = 6 ,
2022-08-17 18:55:41 +01:00
wrongcount = 7 ,
tomanydata = 8
2022-07-19 09:37:03 +01:00
} ;
enum class ModbusBridgeFunctionCode
{
mb_undefined = 0 ,
2022-08-17 18:55:41 +01:00
mb_readCoilStatus = 1 ,
2022-08-27 08:11:36 +01:00
mb_readInputStatus = 2 ,
2022-08-17 18:55:41 +01:00
mb_readHoldingRegisters = 3 ,
mb_readInputRegisters = 4 ,
2022-07-19 09:37:03 +01:00
mb_writeSingleCoil = 5 ,
2022-08-17 18:55:41 +01:00
mb_writeSingleRegister = 6 ,
mb_writeMultipleCoils = 15 ,
mb_writeMultipleRegisters = 16
2022-07-19 09:37:03 +01:00
} ;
enum class ModbusBridgeType
{
mb_undefined ,
2022-08-17 18:55:41 +01:00
mb_uint8 ,
2022-07-19 09:37:03 +01:00
mb_uint16 ,
mb_uint32 ,
2022-08-17 18:55:41 +01:00
mb_int8 ,
2022-07-19 09:37:03 +01:00
mb_int16 ,
mb_int32 ,
mb_float ,
mb_raw ,
2022-08-17 18:55:41 +01:00
mb_hex ,
2022-07-25 08:56:35 +01:00
mb_bit
2022-07-19 09:37:03 +01:00
} ;
enum class ModbusBridgeEndian
{
mb_undefined ,
mb_msb ,
mb_lsb
} ;
struct ModbusBridge
{
unsigned long polling_window = 0 ;
int in_byte_counter = 0 ;
ModbusBridgeFunctionCode functionCode = ModbusBridgeFunctionCode : : mb_undefined ;
ModbusBridgeType type = ModbusBridgeType : : mb_undefined ;
2022-08-17 18:55:41 +01:00
uint16_t dataCount = 0 ;
2022-08-27 08:11:36 +01:00
uint16_t byteCount = 0 ;
2022-07-20 09:41:02 +01:00
uint16_t startAddress = 0 ;
2022-07-19 09:37:03 +01:00
uint8_t deviceAddress = 0 ;
2022-07-20 09:41:02 +01:00
uint8_t count = 0 ;
bool raw = false ;
2022-07-19 09:37:03 +01:00
} ;
ModbusBridge modbusBridge ;
2022-08-27 08:11:36 +01:00
/********************************************************************************************/
//
2022-11-06 09:23:05 +00:00
// Helper functions
2022-08-27 08:11:36 +01:00
//
2022-11-06 09:23:05 +00:00
uint16_t ModbusBridgeSwapEndian16 ( uint16_t num )
2022-08-27 08:11:36 +01:00
{
return ( num > > 8 ) | ( num < < 8 ) ;
}
2022-11-06 09:12:08 +00:00
void ModbusBridgeAllocError ( const char * s )
{
AddLog ( LOG_LEVEL_ERROR , PSTR ( " MBS: could not allocate %s buffer " ) , s ) ;
}
2022-08-27 08:11:36 +01:00
2022-07-19 09:37:03 +01:00
/********************************************************************************************/
2022-07-25 08:56:35 +01:00
//
// Applies serial configuration to modbus serial port
//
bool ModbusBridgeBegin ( void )
2022-07-19 09:37:03 +01:00
{
2022-08-17 18:55:41 +01:00
if ( ( Settings - > modbus_sbaudrate < 1 ) | | ( Settings - > modbus_sbaudrate > ( 115200 / 300 ) ) )
Settings - > modbus_sbaudrate = ( uint8_t ) ( ( uint32_t ) MBR_BAUDRATE / 300 ) ;
if ( Settings - > modbus_sconfig > TS_SERIAL_8O2 )
Settings - > modbus_sconfig = TS_SERIAL_8N1 ;
2022-07-25 18:18:30 +01:00
2022-11-06 09:23:05 +00:00
int result = modbusBridgeModbus - > Begin ( Settings - > modbus_sbaudrate * 300 , ConvertSerialConfig ( Settings - > modbus_sconfig ) ) ; // Reinitialize modbus port with new baud rate
2022-07-25 08:56:35 +01:00
if ( result )
{
if ( 2 = = result )
{
ClaimSerial ( ) ;
}
2022-07-25 18:18:30 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBR %s ser init at %d baud " ) , ( 2 = = result ? " HW " : " SW " ) , Settings - > modbus_sbaudrate * 300 ) ;
2022-07-25 08:56:35 +01:00
}
return result ;
2022-07-19 09:37:03 +01:00
}
2022-11-06 09:23:05 +00:00
void ModbusBridgeSetConfig ( uint32_t serial_config )
2022-07-19 09:37:03 +01:00
{
if ( serial_config > TS_SERIAL_8O2 )
{
serial_config = TS_SERIAL_8N1 ;
}
2022-07-25 18:18:30 +01:00
if ( serial_config ! = Settings - > modbus_sconfig )
2022-07-19 09:37:03 +01:00
{
2022-07-25 18:18:30 +01:00
Settings - > modbus_sconfig = serial_config ;
2022-07-25 08:56:35 +01:00
ModbusBridgeBegin ( ) ;
2022-07-19 09:37:03 +01:00
}
}
2022-11-06 09:23:05 +00:00
void ModbusBridgeSetBaudrate ( uint32_t baudrate )
2022-07-25 08:56:35 +01:00
{
2022-07-25 18:18:30 +01:00
if ( ( baudrate > = 300 ) & & ( baudrate < = 115200 ) )
2022-07-25 08:56:35 +01:00
{
2022-08-17 18:55:41 +01:00
if ( baudrate / 300 ! = Settings - > modbus_sbaudrate )
2022-07-25 18:18:30 +01:00
{
Settings - > modbus_sbaudrate = baudrate / 300 ;
ModbusBridgeBegin ( ) ;
}
2022-07-25 08:56:35 +01:00
}
}
2022-07-19 09:37:03 +01:00
2022-07-25 08:56:35 +01:00
/********************************************************************************************/
//
// Handles data received from tasmota modbus wrapper and send this to (TCP or) MQTT client
//
2022-07-19 09:37:03 +01:00
void ModbusBridgeHandle ( void )
{
2022-11-06 09:23:05 +00:00
bool data_ready = modbusBridgeModbus - > ReceiveReady ( ) ;
2022-07-19 09:37:03 +01:00
if ( data_ready )
{
uint8_t * buffer ;
2022-08-27 08:11:36 +01:00
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)
2022-11-06 09:12:08 +00:00
if ( nullptr = = buffer )
{
ModbusBridgeAllocError ( PSTR ( " read " ) ) ;
return ;
}
2022-08-27 08:11:36 +01:00
memset ( buffer , 0 , 9 + modbusBridge . byteCount ) ;
2022-11-06 09:23:05 +00:00
uint32_t error = modbusBridgeModbus - > ReceiveBuffer ( buffer , 0 , modbusBridge . byteCount ) ;
2022-07-19 09:37:03 +01:00
2022-07-25 08:56:35 +01:00
# ifdef USE_MODBUS_BRIDGE_TCP
for ( uint32_t i = 0 ; i < nitems ( modbusBridgeTCP . client_tcp ) ; i + + )
{
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
if ( client )
{
2022-11-06 11:32:02 +00:00
uint8_t header [ 9 ] ;
2022-08-27 08:11:36 +01:00
uint8_t nrOfBytes = 8 ;
header [ 0 ] = modbusBridgeTCP . tcp_transaction_id > > 8 ;
header [ 1 ] = modbusBridgeTCP . tcp_transaction_id ;
header [ 2 ] = 0 ;
header [ 3 ] = 0 ;
header [ 6 ] = buffer [ 0 ] ; // Send slave address
header [ 7 ] = buffer [ 1 ] ; // Send function code
2022-08-27 08:11:36 +01:00
if ( error )
{
header [ 4 ] = 0 ; // Message Length Hi-Byte
header [ 5 ] = 3 ; // Message Length Low-Byte
2022-08-27 08:11:36 +01:00
header [ 7 ] = buffer [ 1 ] | 0x80 ; // Send function code
2022-08-27 08:11:36 +01:00
header [ 8 ] = error ;
nrOfBytes + = 1 ;
client . write ( header , 9 ) ;
}
2022-11-06 09:23:05 +00:00
else if ( buffer [ 1 ] < = 2 )
2022-08-27 08:11:36 +01:00
{
2022-08-27 08:11:36 +01:00
header [ 4 ] = modbusBridge . byteCount > > 8 ;
header [ 5 ] = modbusBridge . byteCount + 3 ;
header [ 8 ] = modbusBridge . byteCount ;
2022-08-27 08:11:36 +01:00
client . write ( header , 9 ) ;
nrOfBytes + = 1 ;
2022-08-27 08:11:36 +01:00
client . write ( buffer + 3 , modbusBridge . byteCount ) ; // Don't send CRC
nrOfBytes + = modbusBridge . byteCount ;
2022-08-27 08:11:36 +01:00
}
2022-11-06 09:23:05 +00:00
else if ( buffer [ 1 ] < = 4 )
2022-08-27 08:11:36 +01:00
{
2022-08-27 08:11:36 +01:00
header [ 4 ] = modbusBridge . byteCount > > 8 ;
header [ 5 ] = modbusBridge . byteCount + 3 ;
header [ 8 ] = modbusBridge . byteCount ;
2022-08-27 08:11:36 +01:00
client . write ( header , 9 ) ;
nrOfBytes + = 1 ;
2022-08-27 08:11:36 +01:00
client . write ( buffer + 3 , modbusBridge . byteCount ) ; // Don't send CRC
nrOfBytes + = modbusBridge . byteCount ;
2022-08-27 08:11:36 +01:00
}
else
{
header [ 4 ] = 0 ; // Message Length Hi-Byte
header [ 5 ] = 6 ; // Message Length Low-Byte
client . write ( header , 8 ) ;
client . write ( buffer + 2 , 4 ) ; // Don't send CRC
nrOfBytes + = 4 ;
}
2022-07-25 08:56:35 +01:00
client . flush ( ) ;
2022-08-27 08:11:36 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBRTCP from Modbus deviceAddress %d, writing %d bytes to client " ) , buffer [ 0 ] , nrOfBytes ) ;
2022-07-25 08:56:35 +01:00
}
2022-07-19 09:37:03 +01:00
}
2022-07-25 08:56:35 +01:00
# endif
2022-08-27 08:11:36 +01:00
if ( error )
{
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBR Driver receive error %d " ) , error ) ;
free ( buffer ) ;
return ;
}
2022-08-27 09:40:02 +01:00
modbusBridge . byteCount = 0 ;
2022-07-25 08:56:35 +01:00
ModbusBridgeError errorcode = ModbusBridgeError : : noerror ;
2022-08-27 09:54:41 +01:00
2022-07-25 08:56:35 +01:00
if ( modbusBridge . deviceAddress = = 0 )
{
# ifdef USE_MODBUS_BRIDGE_TCP
// If tcp client connected don't log error and exit this function (do not process)
if ( nitems ( modbusBridgeTCP . client_tcp ) )
{
free ( buffer ) ;
return ;
}
# endif
2022-07-19 09:37:03 +01:00
errorcode = ModbusBridgeError : : nodataexpected ;
2022-07-25 08:56:35 +01:00
}
2022-07-19 09:37:03 +01:00
else if ( modbusBridge . deviceAddress ! = ( uint8_t ) buffer [ 0 ] )
errorcode = ModbusBridgeError : : wrongdeviceaddress ;
else if ( ( uint8_t ) modbusBridge . functionCode ! = ( uint8_t ) buffer [ 1 ] )
errorcode = ModbusBridgeError : : wrongfunctioncode ;
2022-08-17 18:55:41 +01:00
else if ( ( uint8_t ) modbusBridge . functionCode < 5 )
2022-08-17 18:55:41 +01:00
{
2022-08-17 18:55:41 +01:00
if ( ( uint8_t ) modbusBridge . functionCode < 3 )
{
if ( ( uint8_t ) ( ( ( modbusBridge . dataCount - 1 ) > > 3 ) + 1 ) ! = ( uint8_t ) buffer [ 2 ] )
errorcode = ModbusBridgeError : : wrongdataCount ;
}
else
{
2022-08-27 08:11:36 +01:00
if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int8 | | modbusBridge . type = = ModbusBridgeType : : mb_uint8 ) & & ( ( uint8_t ) modbusBridge . dataCount * 2 ! = ( uint8_t ) buffer [ 2 ] ) )
2022-08-17 18:55:41 +01:00
errorcode = ModbusBridgeError : : wrongdataCount ;
2022-08-27 08:11:36 +01:00
else if ( ( modbusBridge . type = = ModbusBridgeType : : mb_bit ) & & ( ( uint8_t ) modbusBridge . dataCount * 2 ! = ( uint8_t ) buffer [ 2 ] ) )
2022-08-17 18:55:41 +01:00
errorcode = ModbusBridgeError : : wrongdataCount ;
2022-08-27 08:11:36 +01:00
else if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int16 | | modbusBridge . type = = ModbusBridgeType : : mb_uint16 ) & & ( ( uint8_t ) modbusBridge . dataCount * 2 ! = ( uint8_t ) buffer [ 2 ] ) )
2022-08-17 18:55:41 +01:00
errorcode = ModbusBridgeError : : wrongdataCount ;
2022-08-27 08:11:36 +01:00
else if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int32 | | modbusBridge . type = = ModbusBridgeType : : mb_uint32 | | modbusBridge . type = = ModbusBridgeType : : mb_float ) & & ( ( uint8_t ) modbusBridge . dataCount * 2 ! = ( uint8_t ) buffer [ 2 ] ) )
2022-08-17 18:55:41 +01:00
errorcode = ModbusBridgeError : : wrongdataCount ;
}
2022-08-17 18:55:41 +01:00
}
2022-08-17 18:55:41 +01:00
if ( errorcode = = ModbusBridgeError : : noerror )
2022-07-19 09:37:03 +01:00
{
if ( modbusBridge . type = = ModbusBridgeType : : mb_raw )
{
2022-08-17 18:55:41 +01:00
Response_P ( PSTR ( " { \" " D_JSON_MODBUS_RECEIVED " \" :{ \" RAW \" :[ " ) ) ;
2022-11-06 09:23:05 +00:00
for ( uint8_t i = 0 ; i < modbusBridgeModbus - > ReceiveCount ( ) ; i + + )
2022-07-19 09:37:03 +01:00
{
ResponseAppend_P ( PSTR ( " %d " ) , buffer [ i ] ) ;
2022-11-06 09:23:05 +00:00
if ( i < modbusBridgeModbus - > ReceiveCount ( ) - 1 )
2022-07-19 09:37:03 +01:00
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " ]} " ) ) ;
ResponseJsonEnd ( ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_MODBUS_RECEIVED ) ) ;
}
2022-08-17 18:55:41 +01:00
else if ( modbusBridge . type = = ModbusBridgeType : : mb_hex )
{
Response_P ( PSTR ( " { \" " D_JSON_MODBUS_RECEIVED " \" :{ \" HEX \" :[ " ) ) ;
2022-11-06 09:23:05 +00:00
for ( uint8_t i = 0 ; i < modbusBridgeModbus - > ReceiveCount ( ) ; i + + )
2022-08-17 18:55:41 +01:00
{
ResponseAppend_P ( PSTR ( " 0x%02X " ) , buffer [ i ] ) ;
2022-11-06 09:23:05 +00:00
if ( i < modbusBridgeModbus - > ReceiveCount ( ) - 1 )
2022-08-17 18:55:41 +01:00
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " ]} " ) ) ;
ResponseJsonEnd ( ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_MODBUS_RECEIVED ) ) ;
}
2022-08-17 18:55:41 +01:00
else if ( ( buffer [ 1 ] > 0 ) & & ( buffer [ 1 ] < 7 ) ) // Read Registers
2022-07-19 09:37:03 +01:00
{
2022-08-17 18:55:41 +01:00
uint8_t dataOffset = 3 ;
2022-07-19 09:37:03 +01:00
Response_P ( PSTR ( " { \" " D_JSON_MODBUS_RECEIVED " \" :{ " ) ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_DEVICE_ADDRESS " \" :%d, " ) , buffer [ 0 ] ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_FUNCTION_CODE " \" :%d, " ) , buffer [ 1 ] ) ;
2022-08-17 18:55:41 +01:00
if ( buffer [ 1 ] < 5 )
{
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_START_ADDRESS " \" :%d, " ) , modbusBridge . startAddress ) ;
}
else
{
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_START_ADDRESS " \" :%d, " ) , ( buffer [ 2 ] < < 8 ) + buffer [ 3 ] ) ;
dataOffset = 4 ;
}
2022-11-06 09:23:05 +00:00
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_LENGTH " \" :%d, " ) , modbusBridgeModbus - > ReceiveCount ( ) ) ;
2022-07-19 09:37:03 +01:00
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_COUNT " \" :%d, " ) , modbusBridge . count ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_VALUES " \" :[ " ) ) ;
2022-08-17 18:55:41 +01:00
uint8_t data_count = modbusBridge . count ;
if ( ( uint8_t ) modbusBridge . functionCode < 3 )
{
if ( modbusBridge . type = = ModbusBridgeType : : mb_int8 | | modbusBridge . type = = ModbusBridgeType : : mb_uint8 )
data_count = ( uint8_t ) ( ( ( modbusBridge . count - 1 ) > > 3 ) + 1 ) ;
else if ( modbusBridge . type = = ModbusBridgeType : : mb_int16 | | modbusBridge . type = = ModbusBridgeType : : mb_uint16 )
data_count = ( uint8_t ) ( ( ( modbusBridge . count - 1 ) > > 4 ) + 1 ) ;
else if ( modbusBridge . type = = ModbusBridgeType : : mb_int32 | | modbusBridge . type = = ModbusBridgeType : : mb_uint32 | | modbusBridge . type = = ModbusBridgeType : : mb_float )
data_count = ( uint8_t ) ( ( ( modbusBridge . count - 1 ) > > 5 ) + 1 ) ;
}
for ( uint8_t count = 0 ; count < data_count ; count + + )
2022-07-19 09:37:03 +01:00
{
char svalue [ MBR_MAX_VALUE_LENGTH + 1 ] = " " ;
if ( modbusBridge . type = = ModbusBridgeType : : mb_float )
{
2022-08-27 08:11:36 +01:00
// Convert next 4 bytes to float
2022-07-19 09:37:03 +01:00
float value = 0 ;
2022-08-17 18:55:41 +01:00
if ( buffer [ 1 ] < 3 )
{
2022-08-27 08:11:36 +01:00
// In bit mode only convert returned bytes
2022-08-17 18:55:41 +01:00
if ( buffer [ 2 ] - ( count * 4 ) )
2022-08-27 08:11:36 +01:00
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + ( count * 4 ) ] ; // Get float values
2022-08-17 18:55:41 +01:00
if ( ( buffer [ 2 ] - ( count * 4 ) ) > > 1 )
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + 1 + ( count * 4 ) ] ;
if ( ( buffer [ 2 ] - ( count * 4 ) - 1 ) > > 1 )
( ( uint8_t * ) & value ) [ 2 ] = buffer [ dataOffset + 2 + ( count * 4 ) ] ;
if ( ( buffer [ 2 ] - ( count * 4 ) ) > > 2 )
( ( uint8_t * ) & value ) [ 3 ] = buffer [ dataOffset + 3 + ( count * 4 ) ] ;
}
else
{
( ( uint8_t * ) & value ) [ 3 ] = buffer [ dataOffset + ( count * 4 ) ] ; // Get float values
( ( uint8_t * ) & value ) [ 2 ] = buffer [ dataOffset + 1 + ( count * 4 ) ] ;
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + 2 + ( count * 4 ) ] ;
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + 3 + ( count * 4 ) ] ;
}
2022-07-19 09:37:03 +01:00
ext_snprintf_P ( svalue , sizeof ( svalue ) , " %*_f " , 10 , & value ) ;
}
2022-08-17 18:55:41 +01:00
else if ( modbusBridge . type = = ModbusBridgeType : : mb_bit )
{
2022-08-27 08:11:36 +01:00
uint8_t bits_left = modbusBridge . count - ( ( count / 8 ) * 8 ) ;
uint8_t value = 0 ;
if ( bits_left < 8 )
{
uint8_t bits_skip = 8 - bits_left ;
value = ( uint8_t ) ( buffer [ dataOffset + ( ( count + bits_skip ) > > 3 ) ] ) ;
}
else
{
value = ( uint8_t ) ( buffer [ dataOffset + ( count > > 3 ) ] ) ;
}
2022-08-17 18:55:42 +01:00
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %d " , ( ( value > > ( count & 7 ) ) & 1 ) ) ;
2022-08-17 18:55:41 +01:00
}
2022-07-19 09:37:03 +01:00
else
{
if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int32 ) | |
( modbusBridge . type = = ModbusBridgeType : : mb_uint32 ) )
{
uint32_t value = 0 ;
2022-08-17 18:55:41 +01:00
if ( buffer [ 1 ] < 3 )
{
if ( buffer [ 2 ] - ( count * 4 ) )
2022-08-27 08:11:36 +01:00
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + ( count * 4 ) ] ; // Get uint values
if ( buffer [ 2 ] - ( ( count * 4 ) - 1 ) )
2022-08-17 18:55:41 +01:00
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + 1 + ( count * 4 ) ] ;
2022-08-27 08:11:36 +01:00
if ( buffer [ 2 ] - ( ( count * 4 ) - 2 ) )
2022-08-17 18:55:41 +01:00
( ( uint8_t * ) & value ) [ 2 ] = buffer [ dataOffset + 2 + ( count * 4 ) ] ;
2022-08-27 08:11:36 +01:00
if ( buffer [ 2 ] - ( ( count * 4 ) - 3 ) )
2022-08-17 18:55:41 +01:00
( ( uint8_t * ) & value ) [ 3 ] = buffer [ dataOffset + 3 + ( count * 4 ) ] ;
}
else
{
2022-08-27 08:11:36 +01:00
( ( uint8_t * ) & value ) [ 3 ] = buffer [ dataOffset + ( count * 4 ) ] ; // Get uint values
2022-08-17 18:55:41 +01:00
( ( uint8_t * ) & value ) [ 2 ] = buffer [ dataOffset + 1 + ( count * 4 ) ] ;
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + 2 + ( count * 4 ) ] ;
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + 3 + ( count * 4 ) ] ;
}
2022-07-19 09:37:03 +01:00
if ( modbusBridge . type = = ModbusBridgeType : : mb_int32 )
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %d " , value ) ;
else
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %u " , value ) ;
}
else if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int16 ) | |
( modbusBridge . type = = ModbusBridgeType : : mb_uint16 ) )
{
uint16_t value = 0 ;
2022-08-17 18:55:41 +01:00
if ( buffer [ 1 ] < 3 )
{
if ( buffer [ 2 ] - ( count * 2 ) )
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + ( count * 2 ) ] ;
2022-08-27 08:11:36 +01:00
if ( buffer [ 2 ] - ( ( count * 2 ) - 1 ) )
2022-08-17 18:55:41 +01:00
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + 1 + ( count * 2 ) ] ;
}
else
{
( ( uint8_t * ) & value ) [ 1 ] = buffer [ dataOffset + ( count * 2 ) ] ;
( ( uint8_t * ) & value ) [ 0 ] = buffer [ dataOffset + 1 + ( count * 2 ) ] ;
}
2022-07-19 09:37:03 +01:00
if ( modbusBridge . type = = ModbusBridgeType : : mb_int16 )
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %d " , value ) ;
else
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %u " , value ) ;
}
2022-08-17 18:55:41 +01:00
else if ( ( modbusBridge . type = = ModbusBridgeType : : mb_int8 ) | |
( modbusBridge . type = = ModbusBridgeType : : mb_uint8 ) )
{
uint8_t value = buffer [ dataOffset + ( count * 1 ) ] ;
if ( modbusBridge . type = = ModbusBridgeType : : mb_int8 )
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %d " , value ) ;
else
snprintf ( svalue , MBR_MAX_VALUE_LENGTH , " %u " , value ) ;
}
2022-07-19 09:37:03 +01:00
}
ResponseAppend_P ( PSTR ( " %s " ) , svalue ) ;
2022-08-17 18:55:41 +01:00
if ( count < data_count - 1 )
2022-07-19 09:37:03 +01:00
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " ]} " ) ) ;
ResponseJsonEnd ( ) ;
if ( errorcode = = ModbusBridgeError : : noerror )
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_MODBUS_RECEIVED ) ) ;
}
2022-08-17 18:55:41 +01:00
else if ( ( buffer [ 1 ] = = 15 ) | | ( buffer [ 1 ] = = 16 ) ) // Write Multiple Registers
2022-08-17 18:55:41 +01:00
{
Response_P ( PSTR ( " { \" " D_JSON_MODBUS_RECEIVED " \" :{ " ) ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_DEVICE_ADDRESS " \" :%d, " ) , buffer [ 0 ] ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_FUNCTION_CODE " \" :%d, " ) , buffer [ 1 ] ) ;
2022-08-17 18:55:41 +01:00
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_START_ADDRESS " \" :%d, " ) , ( buffer [ 2 ] < < 8 ) + buffer [ 3 ] ) ;
2022-11-06 09:23:05 +00:00
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_LENGTH " \" :%d, " ) , modbusBridgeModbus - > ReceiveCount ( ) ) ;
2022-08-17 18:55:41 +01:00
ResponseAppend_P ( PSTR ( " \" " D_JSON_MODBUS_COUNT " \" :%d " ) , ( buffer [ 4 ] < < 8 ) + buffer [ 5 ] ) ;
2022-08-17 18:55:41 +01:00
ResponseAppend_P ( PSTR ( " } " ) ) ;
ResponseJsonEnd ( ) ;
if ( errorcode = = ModbusBridgeError : : noerror )
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_MODBUS_RECEIVED ) ) ;
}
2022-07-25 08:56:35 +01:00
else
errorcode = ModbusBridgeError : : wrongfunctioncode ;
2022-07-19 09:37:03 +01:00
}
if ( errorcode ! = ModbusBridgeError : : noerror )
{
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBR Recv Error %d " ) , ( uint8_t ) errorcode ) ;
}
modbusBridge . deviceAddress = 0 ;
free ( buffer ) ;
}
}
/********************************************************************************************/
2022-07-25 08:56:35 +01:00
//
// Inits the tasmota modbus driver, sets serialport and if TCP enabled allocates a TCP buffer
//
2022-07-19 09:37:03 +01:00
void ModbusBridgeInit ( void )
{
if ( PinUsed ( GPIO_MBR_RX ) & & PinUsed ( GPIO_MBR_TX ) )
{
2022-12-03 11:33:42 +00:00
modbusBridgeModbus = new TasmotaModbus ( Pin ( GPIO_MBR_RX ) , Pin ( GPIO_MBR_TX ) , Pin ( GPIO_MBR_TX_ENA ) ) ;
2022-07-26 08:38:57 +01:00
ModbusBridgeBegin ( ) ;
2022-07-25 08:56:35 +01:00
# 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 ) ;
2022-11-06 09:12:08 +00:00
if ( nullptr = = modbusBridgeTCP . tcp_buf )
2022-07-25 08:56:35 +01:00
{
2022-11-06 09:12:08 +00:00
ModbusBridgeAllocError ( PSTR ( " TCP " ) ) ;
2022-07-25 08:56:35 +01:00
return ;
}
# endif
}
}
# ifdef USE_MODBUS_BRIDGE_TCP
/********************************************************************************************/
//
// Handles data for TCP server and TCP client. Sends requests to Modbus Devices
//
void ModbusTCPHandle ( void )
{
uint8_t c ;
bool busy ; // did we transfer some data?
int32_t buf_len ;
2022-11-06 09:23:05 +00:00
if ( ! modbusBridgeModbus )
2022-07-25 08:56:35 +01:00
return ;
// check for a new client connection
if ( ( modbusBridgeTCP . server_tcp ) & & ( modbusBridgeTCP . server_tcp - > hasClient ( ) ) )
{
WiFiClient new_client = modbusBridgeTCP . server_tcp - > available ( ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP Got connection from %s " ) , new_client . remoteIP ( ) . toString ( ) . c_str ( ) ) ;
// Check for IP filtering if it's enabled.
if ( modbusBridgeTCP . ip_filter )
{
if ( modbusBridgeTCP . ip_filter ! = new_client . remoteIP ( ) )
{
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP Rejected due to filtering " ) ) ;
new_client . stop ( ) ;
}
else
{
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP Allowed through filter " ) ) ;
}
}
// find an empty slot
uint32_t i ;
for ( i = 0 ; i < nitems ( modbusBridgeTCP . client_tcp ) ; i + + )
2022-07-19 09:37:03 +01:00
{
2022-07-25 08:56:35 +01:00
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
if ( ! client )
2022-07-19 09:37:03 +01:00
{
2022-07-25 08:56:35 +01:00
client = new_client ;
break ;
2022-07-19 09:37:03 +01:00
}
2022-07-25 08:56:35 +01:00
}
if ( i > = nitems ( modbusBridgeTCP . client_tcp ) )
{
i = modbusBridgeTCP . client_next + + % nitems ( modbusBridgeTCP . client_tcp ) ;
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
client . stop ( ) ;
client = new_client ;
2022-07-19 09:37:03 +01:00
}
}
2022-07-25 08:56:35 +01:00
do
{
busy = false ; // exit loop if no data was transferred
// handle data received from TCP
for ( uint32_t i = 0 ; i < nitems ( modbusBridgeTCP . client_tcp ) ; i + + )
{
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
buf_len = 0 ;
while ( client & & ( buf_len < MODBUS_BRIDGE_TCP_BUF_SIZE ) & & ( client . available ( ) ) )
{
c = client . read ( ) ;
if ( c > = 0 )
{
modbusBridgeTCP . tcp_buf [ buf_len + + ] = c ;
busy = true ;
}
}
2022-08-27 08:11:36 +01:00
if ( buf_len > = 12 )
2022-07-25 08:56:35 +01:00
{
uint8_t mbdeviceaddress = ( uint8_t ) modbusBridgeTCP . tcp_buf [ 6 ] ;
uint8_t mbfunctioncode = ( uint8_t ) modbusBridgeTCP . tcp_buf [ 7 ] ;
uint16_t mbstartaddress = ( uint16_t ) ( ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 8 ] ) < < 8 ) | ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 9 ] ) ) ;
2022-08-27 08:11:36 +01:00
uint16_t * writeData = NULL ;
2022-08-27 08:11:36 +01:00
uint16_t count = 0 ;
2022-08-27 08:11:36 +01:00
2022-07-25 08:56:35 +01:00
modbusBridgeTCP . tcp_transaction_id = ( uint16_t ) ( ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 0 ] ) < < 8 ) | ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 1 ] ) ) ;
2022-08-27 08:11:36 +01:00
if ( mbfunctioncode < = 2 )
{
2022-08-27 08:11:36 +01:00
count = ( uint16_t ) ( ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 10 ] ) < < 8 ) | ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 11 ] ) ) ;
2022-11-06 09:23:05 +00:00
modbusBridge . byteCount = ( ( count - 1 ) > > 3 ) + 1 ;
modbusBridge . dataCount = ( ( count - 1 ) > > 4 ) + 1 ;
2022-08-27 08:11:36 +01:00
}
else if ( mbfunctioncode < = 4 )
2022-08-27 08:11:36 +01:00
{
2022-08-27 08:11:36 +01:00
count = ( uint16_t ) ( ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 10 ] ) < < 8 ) | ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 11 ] ) ) ;
modbusBridge . byteCount = count * 2 ;
modbusBridge . dataCount = count ;
2022-08-27 08:11:36 +01:00
}
else
{
2022-11-06 09:23:05 +00:00
// For functioncode 15 & 16 ignore bytecount, modbusBridgeModbus does calculate this
2022-08-27 08:11:36 +01:00
uint8_t dataStartByte = mbfunctioncode < = 6 ? 10 : 13 ;
2022-08-27 08:11:36 +01:00
uint16_t byteCount = ( buf_len - dataStartByte ) ;
2022-08-27 08:11:36 +01:00
modbusBridge . byteCount = 2 ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = 1 ;
2022-08-27 08:11:36 +01:00
writeData = ( uint16_t * ) malloc ( ( byteCount / 2 ) + 1 ) ;
2022-11-06 09:12:08 +00:00
if ( nullptr = = writeData )
{
ModbusBridgeAllocError ( PSTR ( " write " ) ) ;
return ;
}
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
if ( ( mbfunctioncode = = 15 ) | | ( mbfunctioncode = = 16 ) ) count = ( uint16_t ) ( ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 10 ] ) < < 8 ) | ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ 11 ] ) ) ;
else count = 1 ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
for ( uint16_t dataPointer = 0 ; dataPointer < byteCount ; dataPointer + + )
2022-08-27 08:11:36 +01:00
{
2022-08-27 08:11:36 +01:00
if ( dataPointer % 2 = = 0 )
{
writeData [ dataPointer / 2 ] = ( uint16_t ) ( ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ dataStartByte + dataPointer ] ) < < 8 ) ;
}
else
2022-11-06 09:23:05 +00:00
{
2022-08-27 08:11:36 +01:00
writeData [ dataPointer / 2 ] | = ( ( uint16_t ) modbusBridgeTCP . tcp_buf [ dataStartByte + dataPointer ] ) ;
}
2022-08-27 08:11:36 +01:00
}
}
2022-07-25 08:56:35 +01:00
2022-08-27 09:40:02 +01:00
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 ) ;
2022-08-27 08:11:36 +01:00
2022-11-06 09:23:05 +00:00
modbusBridgeModbus - > Send ( mbdeviceaddress , mbfunctioncode , mbstartaddress , count , writeData ) ;
2022-08-27 08:11:36 +01:00
free ( writeData ) ;
2022-07-25 08:56:35 +01:00
}
}
yield ( ) ; // avoid WDT if heavy traffic
} while ( busy ) ;
2022-07-19 09:37:03 +01:00
}
2022-07-25 08:56:35 +01:00
# endif
2022-07-19 09:37:03 +01:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CmndModbusBridgeSend ( void )
{
2022-08-17 18:55:41 +01:00
uint16_t * writeData = NULL ;
uint8_t writeDataSize = 0 ;
2022-08-27 08:11:36 +01:00
bool bitMode = false ;
2022-08-17 18:55:41 +01:00
ModbusBridgeError errorcode = ModbusBridgeError : : noerror ;
2022-07-19 09:37:03 +01:00
JsonParser parser ( XdrvMailbox . data ) ;
JsonParserObject root = parser . getRootObject ( ) ;
if ( ! root )
return ;
modbusBridge . deviceAddress = root . getUInt ( PSTR ( D_JSON_MODBUS_DEVICE_ADDRESS ) , 0 ) ;
uint8_t functionCode = root . getUInt ( PSTR ( D_JSON_MODBUS_FUNCTION_CODE ) , 0 ) ;
modbusBridge . startAddress = root . getULong ( PSTR ( D_JSON_MODBUS_START_ADDRESS ) , 0 ) ;
2022-08-17 18:55:41 +01:00
2022-07-19 09:37:03 +01:00
const char * stype = root . getStr ( PSTR ( D_JSON_MODBUS_TYPE ) , " uint8 " ) ;
2022-08-27 08:11:36 +01:00
modbusBridge . count = root . getUInt ( PSTR ( D_JSON_MODBUS_COUNT ) , 1 ) ; // Number of bits or bytes to read / write
if ( ( functionCode = = 1 ) | | ( functionCode = = 2 ) | | ( functionCode = = 15 ) ) bitMode = true ;
2022-07-19 09:37:03 +01:00
if ( modbusBridge . deviceAddress = = 0 )
errorcode = ModbusBridgeError : : wrongdeviceaddress ;
2022-08-17 18:55:41 +01:00
else if ( ( functionCode > ( uint8_t ) ModbusBridgeFunctionCode : : mb_writeSingleRegister ) & &
( functionCode ! = ( uint8_t ) ModbusBridgeFunctionCode : : mb_writeMultipleCoils ) & &
( functionCode ! = ( uint8_t ) ModbusBridgeFunctionCode : : mb_writeMultipleRegisters ) )
errorcode = ModbusBridgeError : : wrongfunctioncode ; // Invalid function code
2022-07-19 09:37:03 +01:00
else
{
modbusBridge . functionCode = static_cast < ModbusBridgeFunctionCode > ( functionCode ) ;
if ( modbusBridge . functionCode = = ModbusBridgeFunctionCode : : mb_undefined )
errorcode = ModbusBridgeError : : wrongfunctioncode ;
}
modbusBridge . type = ModbusBridgeType : : mb_undefined ;
2022-08-17 18:55:41 +01:00
if ( strcmp ( stype , " int8 " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_int8 ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : ( ( modbusBridge . count - 1 ) / 2 ) + 1 ;
if ( bitMode ) modbusBridge . byteCount = ( modbusBridge . count / 8 ) + 1 ;
2022-08-17 18:55:41 +01:00
}
else if ( strcmp ( stype , " int16 " ) = = 0 )
2022-07-19 09:37:03 +01:00
{
modbusBridge . type = ModbusBridgeType : : mb_int16 ;
2022-08-17 18:55:41 +01:00
modbusBridge . dataCount = modbusBridge . count ;
2022-07-19 09:37:03 +01:00
}
else if ( strcmp ( stype , " int32 " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_int32 ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : 2 * modbusBridge . count ;
2022-07-19 09:37:03 +01:00
}
2022-08-17 18:55:41 +01:00
else if ( ( strcmp ( stype , " uint8 " ) = = 0 ) )
{
modbusBridge . type = ModbusBridgeType : : mb_uint8 ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : ( ( modbusBridge . count - 1 ) / 2 ) + 1 ;
2022-08-17 18:55:41 +01:00
}
2022-08-17 18:55:41 +01:00
else if ( ( strcmp ( stype , " uint16 " ) = = 0 ) | | ( strcmp ( stype , " " ) = = 0 ) ) // Default is uint16
2022-07-19 09:37:03 +01:00
{
modbusBridge . type = ModbusBridgeType : : mb_uint16 ;
2022-08-17 18:55:41 +01:00
modbusBridge . dataCount = modbusBridge . count ;
2022-07-19 09:37:03 +01:00
}
else if ( strcmp ( stype , " uint32 " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_uint32 ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : 2 * modbusBridge . count ;
2022-07-19 09:37:03 +01:00
}
else if ( strcmp ( stype , " float " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_float ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? 2 * modbusBridge . count : modbusBridge . count ;
2022-07-19 09:37:03 +01:00
}
else if ( strcmp ( stype , " raw " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_raw ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : ( ( modbusBridge . count - 1 ) / 2 ) + 1 ;
2022-07-19 09:37:03 +01:00
}
2022-08-17 18:55:41 +01:00
else if ( strcmp ( stype , " hex " ) = = 0 )
{
modbusBridge . type = ModbusBridgeType : : mb_hex ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : ( ( modbusBridge . count - 1 ) / 2 ) + 1 ;
2022-08-17 18:55:41 +01:00
}
2022-07-25 08:56:35 +01:00
else if ( strcmp ( stype , " bit " ) = = 0 )
2022-07-19 09:37:03 +01:00
{
2022-07-25 08:56:35 +01:00
modbusBridge . type = ModbusBridgeType : : mb_bit ;
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = bitMode ? modbusBridge . count : ( ( modbusBridge . count - 1 ) / 16 ) + 1 ;
2022-07-19 09:37:03 +01:00
}
else
errorcode = ModbusBridgeError : : wrongtype ;
2022-08-27 08:11:36 +01:00
// If functioncode is 15, the count is not the number of registers but the number
// of bit to write, so calculate the number data bytes to write.
if ( modbusBridge . functionCode = = ModbusBridgeFunctionCode : : mb_writeMultipleCoils )
{
2022-08-27 08:11:36 +01:00
modbusBridge . dataCount = modbusBridge . count ;
2022-08-27 08:11:36 +01:00
}
// Prevent buffer overflow due to usage of to many registers
2022-08-27 09:54:41 +01:00
if ( ( ! bitMode ) & & ( modbusBridge . dataCount > MBR_MAX_REGISTERS ) )
errorcode = ModbusBridgeError : : wrongcount ;
if ( ( bitMode ) & & ( modbusBridge . dataCount > MBR_MAX_REGISTERS * 8 ) )
2022-07-25 08:56:35 +01:00
errorcode = ModbusBridgeError : : wrongcount ;
2022-07-19 09:37:03 +01:00
2022-08-27 08:11:36 +01:00
// Get Json data for writing
2022-08-17 18:55:41 +01:00
JsonParserArray jsonDataArray = root [ PSTR ( D_JSON_MODBUS_VALUES ) ] . getArray ( ) ;
2022-08-27 08:11:36 +01:00
writeDataSize = jsonDataArray . size ( ) ;
// Check if number of supplied data items is valid
switch ( modbusBridge . functionCode )
{
case ModbusBridgeFunctionCode : : mb_writeMultipleCoils :
// In writeMultipleCoil mode the amount of given data bits is less or equal to the count
switch ( modbusBridge . type )
{
case ModbusBridgeType : : mb_bit :
if ( modbusBridge . count > writeDataSize ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
case ModbusBridgeType : : mb_uint8 :
case ModbusBridgeType : : mb_int8 :
case ModbusBridgeType : : mb_raw :
case ModbusBridgeType : : mb_hex :
if ( modbusBridge . count > writeDataSize * 8 ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
case ModbusBridgeType : : mb_uint16 :
case ModbusBridgeType : : mb_int16 :
if ( modbusBridge . count > writeDataSize * 16 ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
case ModbusBridgeType : : mb_uint32 :
case ModbusBridgeType : : mb_int32 :
if ( modbusBridge . count > writeDataSize * 32 ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
}
break ;
case ModbusBridgeFunctionCode : : mb_writeSingleRegister :
case ModbusBridgeFunctionCode : : mb_writeSingleCoil :
if ( modbusBridge . count ! = 1 ) errorcode = ModbusBridgeError : : wrongcount ;
2022-08-27 08:11:36 +01:00
break ;
2022-08-27 08:11:36 +01:00
case ModbusBridgeFunctionCode : : mb_writeMultipleRegisters :
if ( modbusBridge . count ! = writeDataSize ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
}
// If write data is specified in JSON copy it into writeData array
if ( ( errorcode = = ModbusBridgeError : : noerror ) & & ( jsonDataArray . isArray ( ) ) )
2022-08-17 18:55:41 +01:00
{
2022-08-17 18:55:41 +01:00
if ( modbusBridge . dataCount > 40 )
2022-08-17 18:55:41 +01:00
{
2022-08-17 18:55:41 +01:00
errorcode = ModbusBridgeError : : tomanydata ;
2022-08-17 18:55:41 +01:00
}
else
{
2022-08-27 08:11:36 +01:00
writeData = ( uint16_t * ) malloc ( modbusBridge . dataCount ) ;
2022-11-06 09:12:08 +00:00
if ( nullptr = = writeData )
{
ModbusBridgeAllocError ( PSTR ( " write " ) ) ;
return ;
}
2022-08-17 18:55:41 +01:00
2022-08-27 08:11:36 +01:00
for ( uint8_t jsonDataArrayPointer = 0 ; jsonDataArrayPointer < writeDataSize ; jsonDataArrayPointer + + )
2022-08-17 18:55:41 +01:00
{
2022-08-27 08:11:36 +01:00
if ( errorcode ! = ModbusBridgeError : : noerror ) break ;
switch ( modbusBridge . type )
2022-08-17 18:55:41 +01:00
{
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_bit :
{
// Initialize current data/register to 0
if ( jsonDataArrayPointer % 16 = = 0 )
2022-08-17 18:55:41 +01:00
{
2022-08-27 08:11:36 +01:00
writeData [ jsonDataArrayPointer / 15 ] = 0 ;
2022-08-17 18:55:41 +01:00
}
2022-08-27 08:11:36 +01:00
// Swap low and high bytes according to modbus specification
uint16_t bitValue = ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) = = 1 ) ? 1 : 0 ;
uint8_t bitPointer = ( jsonDataArrayPointer % 16 ) + 8 ;
if ( bitPointer > 15 ) bitPointer - = 16 ;
writeData [ jsonDataArrayPointer / 16 ] + = bitValue < < bitPointer ;
}
break ;
case ModbusBridgeType : : mb_int8 :
if ( jsonDataArrayPointer % 2 )
writeData [ jsonDataArrayPointer / 2 ] + = ( int8_t ) jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) ;
else
writeData [ jsonDataArrayPointer / 2 ] = ( int8_t ) jsonDataArray [ jsonDataArrayPointer / 2 ] . getInt ( 0 ) < < 8 ;
if ( modbusBridge . dataCount ! = writeDataSize / 2 ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_hex :
case ModbusBridgeType : : mb_raw :
case ModbusBridgeType : : mb_uint8 :
if ( jsonDataArrayPointer % 2 )
writeData [ jsonDataArrayPointer / 2 ] + = ( uint8_t ) jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) ;
else
writeData [ jsonDataArrayPointer / 2 ] = ( uint8_t ) jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) < < 8 ;
if ( modbusBridge . dataCount ! = writeDataSize / 2 ) errorcode = ModbusBridgeError : : wrongcount ;
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_int16 :
2022-11-06 09:23:05 +00:00
writeData [ jsonDataArrayPointer ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) )
2022-08-27 08:11:36 +01:00
: ( int16_t ) jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) ;
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_uint16 :
2022-11-06 09:23:05 +00:00
writeData [ jsonDataArrayPointer ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) )
2022-08-27 08:11:36 +01:00
: ( int16_t ) jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) ;
2022-08-27 08:11:36 +01:00
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_int32 :
2022-11-06 09:23:05 +00:00
writeData [ ( jsonDataArrayPointer * 2 ) ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) )
2022-08-27 08:11:36 +01:00
: ( int16_t ) ( jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) > > 16 ) ;
2022-11-06 09:23:05 +00:00
writeData [ ( jsonDataArrayPointer * 2 ) + 1 ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) > > 16 )
2022-08-27 08:11:36 +01:00
: ( uint16_t ) ( jsonDataArray [ jsonDataArrayPointer ] . getInt ( 0 ) ) ;
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_uint32 :
2022-11-06 09:23:05 +00:00
writeData [ ( jsonDataArrayPointer * 2 ) ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) )
2022-08-27 08:11:36 +01:00
: ( uint16_t ) ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) > > 16 ) ;
2022-11-06 09:23:05 +00:00
writeData [ ( jsonDataArrayPointer * 2 ) + 1 ] = bitMode ? ModbusBridgeSwapEndian16 ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) > > 16 )
2022-08-27 08:11:36 +01:00
: ( uint16_t ) ( jsonDataArray [ jsonDataArrayPointer ] . getUInt ( 0 ) ) ;
break ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
case ModbusBridgeType : : mb_float :
// TODO
default :
errorcode = ModbusBridgeError : : wrongtype ;
break ;
2022-08-17 18:55:41 +01:00
}
2022-08-17 18:55:41 +01:00
}
}
2022-08-27 08:11:36 +01:00
// Adapt data according to modbus protocol
if ( modbusBridge . functionCode = = ModbusBridgeFunctionCode : : mb_writeSingleCoil )
{
writeData [ 0 ] = writeData [ 0 ] ? 0xFF00 : 0x0000 ; // High Byte
}
2022-08-17 18:55:41 +01:00
}
// Handle errorcode and exit function when an error has occured
2022-07-19 09:37:03 +01:00
if ( errorcode ! = ModbusBridgeError : : noerror )
{
2022-08-17 18:55:41 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBR Send Error %u " ) , ( uint8_t ) errorcode ) ;
2022-08-17 18:55:41 +01:00
free ( writeData ) ;
2022-07-19 09:37:03 +01:00
return ;
}
2022-08-17 18:55:41 +01:00
// If writing a single coil or single register, the register count is always 1. We also prevent writing data out of range
2022-08-17 18:55:41 +01:00
if ( ( modbusBridge . functionCode = = ModbusBridgeFunctionCode : : mb_writeSingleCoil ) | | ( modbusBridge . functionCode = = ModbusBridgeFunctionCode : : mb_writeSingleRegister ) )
2022-08-17 18:55:41 +01:00
modbusBridge . dataCount = 1 ;
2022-08-17 18:55:41 +01:00
2022-11-06 09:23:05 +00:00
uint8_t error = modbusBridgeModbus - > Send ( modbusBridge . deviceAddress , ( uint8_t ) modbusBridge . functionCode , modbusBridge . startAddress , modbusBridge . dataCount , writeData ) ;
2022-08-27 08:11:36 +01:00
free ( writeData ) ;
2022-11-06 09:23:05 +00:00
2022-08-27 08:11:36 +01:00
if ( error )
2022-08-27 08:11:36 +01:00
{
2022-08-27 08:11:36 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " MBS: MBR Driver send error %u " ) , error ) ;
2022-08-27 08:11:36 +01:00
return ;
2022-11-06 09:23:05 +00:00
}
2022-07-19 09:37:03 +01:00
ResponseCmndDone ( ) ;
}
void CmndModbusBridgeSetBaudrate ( void )
{
2022-11-06 09:23:05 +00:00
ModbusBridgeSetBaudrate ( XdrvMailbox . payload ) ;
2022-07-25 18:18:30 +01:00
ResponseCmndNumber ( Settings - > modbus_sbaudrate * 300 ) ;
2022-07-19 09:37:03 +01:00
}
void CmndModbusBridgeSetConfig ( void )
{
// See TasmotaModusConfig for possible options
// ModbusConfig 0..23 where 3 equals 8N1
// ModbusConfig 8N1
if ( XdrvMailbox . data_len > 0 )
{
if ( XdrvMailbox . data_len < 3 )
{ // Use 0..23 as serial config option
if ( ( XdrvMailbox . payload > = TS_SERIAL_5N1 ) & & ( XdrvMailbox . payload < = TS_SERIAL_8O2 ) )
{
2022-11-06 09:23:05 +00:00
ModbusBridgeSetConfig ( XdrvMailbox . payload ) ;
2022-07-19 09:37:03 +01:00
}
}
else if ( ( XdrvMailbox . payload > = 5 ) & & ( XdrvMailbox . payload < = 8 ) )
{
int8_t serial_config = ParseSerialConfig ( XdrvMailbox . data ) ;
if ( serial_config > = 0 )
{
2022-11-06 09:23:05 +00:00
ModbusBridgeSetConfig ( serial_config ) ;
2022-07-19 09:37:03 +01:00
}
}
}
2022-07-25 18:18:30 +01:00
ResponseCmndChar ( GetSerialConfig ( Settings - > modbus_sconfig ) . c_str ( ) ) ;
2022-07-19 09:37:03 +01:00
}
2022-07-25 08:56:35 +01:00
# ifdef USE_MODBUS_BRIDGE_TCP
//
// Command `TCPStart`
// Params: port,<IPv4 allow>
//
void CmndModbusTCPStart ( void )
{
2022-11-06 09:23:05 +00:00
if ( ! modbusBridgeModbus )
2022-07-25 08:56:35 +01:00
{
return ;
}
int32_t tcp_port = XdrvMailbox . payload ;
if ( ArgC ( ) = = 2 )
{
char sub_string [ XdrvMailbox . data_len ] ;
modbusBridgeTCP . ip_filter . fromString ( ArgV ( sub_string , 2 ) ) ;
}
else
{
// Disable whitelist if previously set
modbusBridgeTCP . ip_filter = ( uint32_t ) 0 ;
}
if ( modbusBridgeTCP . server_tcp )
{
AddLog ( LOG_LEVEL_INFO , PSTR ( " MBS: MBRTCP Stopping server " ) ) ;
modbusBridgeTCP . server_tcp - > stop ( ) ;
delete modbusBridgeTCP . server_tcp ;
modbusBridgeTCP . server_tcp = nullptr ;
for ( uint32_t i = 0 ; i < nitems ( modbusBridgeTCP . client_tcp ) ; i + + )
{
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
client . stop ( ) ;
}
}
if ( tcp_port > 0 )
{
AddLog ( LOG_LEVEL_INFO , PSTR ( " MBS: MBRTCP Starting server on port %d " ) , tcp_port ) ;
if ( modbusBridgeTCP . ip_filter )
{
AddLog ( LOG_LEVEL_INFO , PSTR ( " MBS: MBRTCP Filtering %s " ) , modbusBridgeTCP . ip_filter . toString ( ) . c_str ( ) ) ;
}
modbusBridgeTCP . server_tcp = new WiFiServer ( tcp_port ) ;
modbusBridgeTCP . server_tcp - > begin ( ) ; // start TCP server
modbusBridgeTCP . server_tcp - > setNoDelay ( true ) ;
}
ResponseCmndDone ( ) ;
}
//
// Command `Connect`
// Params: port,<IPv4>
//
void CmndModbusTCPConnect ( void )
{
int32_t tcp_port = XdrvMailbox . payload ;
2022-11-06 09:23:05 +00:00
if ( ! modbusBridgeModbus )
2022-07-25 08:56:35 +01:00
{
return ;
}
if ( ArgC ( ) = = 2 )
{
char sub_string [ XdrvMailbox . data_len ] ;
WiFiClient new_client ;
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP Connecting to %s on port %d " ) , ArgV ( sub_string , 2 ) , tcp_port ) ;
if ( new_client . connect ( ArgV ( sub_string , 2 ) , tcp_port ) )
{
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP connected! " ) ) ;
}
else
{
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBRTCP error connecting! " ) ) ;
}
// find an empty slot
uint32_t i ;
for ( i = 0 ; i < nitems ( modbusBridgeTCP . client_tcp ) ; i + + )
{
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
if ( ! client )
{
client = new_client ;
break ;
}
}
if ( i > = nitems ( modbusBridgeTCP . client_tcp ) )
{
i = modbusBridgeTCP . client_next + + % nitems ( modbusBridgeTCP . client_tcp ) ;
WiFiClient & client = modbusBridgeTCP . client_tcp [ i ] ;
client . stop ( ) ;
client = new_client ;
}
}
else
{
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_TCP " MBS: MBR Usage: port,ip_address " ) ) ;
}
ResponseCmndDone ( ) ;
}
# endif
2022-07-19 09:37:03 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xdrv63 ( uint32_t function )
2022-07-19 09:37:03 +01:00
{
bool result = false ;
if ( FUNC_PRE_INIT = = function )
{
ModbusBridgeInit ( ) ;
}
2022-11-06 09:23:05 +00:00
else if ( modbusBridgeModbus )
2022-07-19 09:37:03 +01:00
{
switch ( function )
{
case FUNC_COMMAND :
result = DecodeCommand ( kModbusBridgeCommands , ModbusBridgeCommand ) ;
break ;
2022-07-25 08:56:35 +01:00
case FUNC_LOOP :
ModbusBridgeHandle ( ) ;
# ifdef USE_MODBUS_BRIDGE_TCP
ModbusTCPHandle ( ) ;
# endif
break ;
2022-07-19 09:37:03 +01:00
}
}
return result ;
}
# endif // USE_MODBUS_BRIDGE