File syn_modbus_tcp.c¶
File List > proto > syn_modbus_tcp.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_MODBUS) || SYN_USE_MODBUS
#include "../util/syn_assert.h"
#include "../util/syn_pack.h"
#include "syn_modbus_tcp.h"
#include <string.h>
void syn_mbap_encode_header(const SYN_MBAP_Header *hdr, uint8_t *buf)
{
SYN_ASSERT(hdr != NULL);
SYN_ASSERT(buf != NULL);
if (hdr == NULL || buf == NULL)
return;
size_t pos = 0;
syn_pack_u16(buf, &pos, hdr->transaction_id);
syn_pack_u16(buf, &pos, hdr->protocol_id);
syn_pack_u16(buf, &pos, hdr->length);
syn_pack_u8(buf, &pos, hdr->unit_id);
}
bool syn_mbap_decode_header(const uint8_t *buf, SYN_MBAP_Header *hdr)
{
SYN_ASSERT(buf != NULL);
SYN_ASSERT(hdr != NULL);
size_t pos = 0;
hdr->transaction_id = syn_unpack_u16(buf, &pos);
hdr->protocol_id = syn_unpack_u16(buf, &pos);
hdr->length = syn_unpack_u16(buf, &pos);
hdr->unit_id = syn_unpack_u8(buf, &pos);
return (hdr->protocol_id == 0x0000);
}
static uint16_t get_response_pdu_len(const uint8_t *buf)
{
uint8_t fc = buf[1];
if (fc & 0x80) {
return 2; /* Function Code + Exception Code */
}
switch (fc) {
case SYN_MB_FC_READ_COILS:
case SYN_MB_FC_READ_DISCRETE_INPUTS:
case SYN_MB_FC_READ_HOLDING:
case SYN_MB_FC_READ_INPUT:
case SYN_MB_FC_REPORT_SERVER_ID:
return (uint16_t)(2 + buf[2]);
case SYN_MB_FC_WRITE_SINGLE_COIL:
case SYN_MB_FC_WRITE_SINGLE:
case SYN_MB_FC_WRITE_MULTIPLE_COILS:
case SYN_MB_FC_WRITE_MULTIPLE:
return 5;
case SYN_MB_FC_READ_EXCEPTION_STATUS:
return 2;
case SYN_MB_FC_MASK_WRITE_REGISTER:
return 7;
default:
return (uint16_t)(2 + buf[2]);
}
}
bool syn_modbus_tcp_process_slave(SYN_Modbus *mb, const uint8_t *req_adu, uint16_t req_len,
uint8_t *resp_adu, uint16_t resp_max, uint16_t *resp_len)
{
SYN_ASSERT(mb != NULL);
SYN_ASSERT(req_adu != NULL);
SYN_ASSERT(resp_adu != NULL);
SYN_ASSERT(resp_len != NULL);
*resp_len = 0;
if (req_len < SYN_MBAP_HEADER_LEN + 1) {
return false; /* ADU too short */
}
SYN_MBAP_Header req_hdr;
if (!syn_mbap_decode_header(req_adu, &req_hdr)) {
return false; /* Not Modbus TCP protocol */
}
uint16_t expected_payload_len = req_hdr.length;
if (expected_payload_len < 2 || req_len < SYN_MBAP_HEADER_LEN - 1 + expected_payload_len) {
return false; /* Length mismatch */
}
uint16_t pdu_len = expected_payload_len - 1; /* Unit ID is 1 byte */
if (pdu_len > mb->buf_size - 3) {
return false;
}
/* Unit ID check: Accept if matching slave address or unit_id == 0 or 255 */
if (req_hdr.unit_id != mb->cfg.slave_addr && req_hdr.unit_id != 0 && req_hdr.unit_id != 0xFF) {
return false;
}
/* Copy Unit ID and PDU into slave buffer, then append CRC for syn_modbus_process */
mb->buf[0] = req_hdr.unit_id;
memcpy(&mb->buf[1], &req_adu[SYN_MBAP_HEADER_LEN], pdu_len);
uint16_t crc = syn_crc16_modbus(mb->buf, 1 + pdu_len);
mb->buf[1 + pdu_len] = (uint8_t)(crc & 0xFF);
mb->buf[2 + pdu_len] = (uint8_t)((crc >> 8) & 0xFF);
mb->rx_len = 3 + pdu_len;
/* Process PDU using slave dispatcher */
bool has_response = syn_modbus_process(mb);
/* Broadcast or exception without response */
if (!has_response || mb->buf[0] == 0) {
return false;
}
uint16_t resp_pdu_len = get_response_pdu_len(mb->buf);
uint16_t total_resp_adu_len = SYN_MBAP_HEADER_LEN + resp_pdu_len;
if (total_resp_adu_len > resp_max) {
return false;
}
SYN_MBAP_Header resp_hdr;
resp_hdr.transaction_id = req_hdr.transaction_id;
resp_hdr.protocol_id = 0x0000;
resp_hdr.length = (uint16_t)(1 + resp_pdu_len); /* Unit ID (1) + PDU */
resp_hdr.unit_id = req_hdr.unit_id;
syn_mbap_encode_header(&resp_hdr, resp_adu);
memcpy(&resp_adu[SYN_MBAP_HEADER_LEN], &mb->buf[1], resp_pdu_len);
*resp_len = total_resp_adu_len;
return true;
}
SYN_Status syn_modbus_tcp_build_client_adu(uint16_t transaction_id, uint8_t unit_id,
const uint8_t *pdu, uint16_t pdu_len, uint8_t *req_adu,
uint16_t req_max, uint16_t *req_len)
{
SYN_ASSERT(pdu != NULL);
SYN_ASSERT(req_adu != NULL);
SYN_ASSERT(req_len != NULL);
if (pdu_len == 0 || pdu_len > 253)
return SYN_INVALID_PARAM;
uint16_t total_len = SYN_MBAP_HEADER_LEN + pdu_len;
if (total_len > req_max)
return SYN_INVALID_PARAM;
SYN_MBAP_Header hdr;
hdr.transaction_id = transaction_id;
hdr.protocol_id = 0x0000;
hdr.length = (uint16_t)(1 + pdu_len);
hdr.unit_id = unit_id;
syn_mbap_encode_header(&hdr, req_adu);
memcpy(&req_adu[SYN_MBAP_HEADER_LEN], pdu, pdu_len);
*req_len = total_len;
return SYN_OK;
}
#endif /* SYN_USE_MODBUS */