Deleted old modbus files

This commit is contained in:
Jan Mrna 2022-06-17 09:22:32 +02:00
parent 256303335a
commit f790d3ddde
2 changed files with 0 additions and 662 deletions

View File

@ -1,231 +0,0 @@
/*
* modbus.h
*
* Created on: Jul 18, 2021
* Author: user
*
* Modbus slave RTU library (does NOT support ASCII and TCP)
*
* Useful links:
* https://www.picotech.com/library/oscilloscopes/modbus-serial-protocol-decoding
* https://ipc2u.com/articles/knowledge-base/modbus-rtu-made-simple-with-detailed-descriptions-and-examples/
* https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
* https://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
*
* Note that byte order is big endian.
*
* USAGE:
*
* 1) Implement functions modbus_callback_function() and modbus_uart_transmit_function()
* - modbus_uart_transmit_function() sends data via UART
* - modbus_callback_function() does the real work: read sensors, set outputs...
* note that when filling buffers (e.g. input_registers[]) user must
* ensure that all data is big-endian
* These functions are implementation-specific.
* 2) Set device address (variable modbus_device_address); you can do this either
* - setting modbus_device_address directly (modbus.h needs to be included, duh)
* - using modbus_set_device_address(uint8_t address) function
* Or you can leave address as-is (MODBUS_DEFAULT_SLAVE_ADDRESS) and set it via
* Modbus during runtime
* 3) Call modbus_process_msg() after message reception; you need to observe Modbus RTU timing:
* - pauses between chars in frame are less or equal to 1.5 char
* - pauses between frames are at least 3.5 chars (of silence)
* For more information see section 2.5.1.1 (MODBUS Message RTU Framing)
* in "MODBUS over Serial Line: Specification and Implementation Guide"
*
*/
#ifndef SRC_MODBUS_H_
#define SRC_MODBUS_H_
#include "stdint.h"
#include "stddef.h"
#include "string.h"
/*
* Defines & macros
*/
#define MODBUS_BROADCAST_ADDR 0
#define MODBUS_DEFAULT_SLAVE_ADDRESS 247 /* 255 may be used for bridge device */
/* minimal frame length is 4 bytes: 1 B slave address, 1 B function code, 2 B CRC */
#define MODBUS_MINIMAL_FRAME_LEN 4
#define MODBUS_MINIMAL_READWRITE_LEN 4
#define MODBUS_MINIMAL_WRITE_MULTIPLE_LEN 5
#define MODBUS_READ_DEVICE_ID_REQUEST_LEN 4
#define MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN 4
#define MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET 3
#define MODBUS_MAX_RTU_FRAME_SIZE 256
#define MODBUS_BUFFER_SIZE MODBUS_MAX_RTU_FRAME_SIZE /* alias */
#define MODBUS_ERROR_FLAG 0x80
#define MODBUS_MAX_REGISTERS 125
/* read device id constants */
#define MODBUS_MEI 0x0E
#define MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG 0x80
#define MODBUS_MORE_FOLLOWS 0xFF
#define MODBUS_NO_MORE_FOLLOWS 0x00
#define MODBUS_BASIC_OBJECT_COUNT 3
#define MODBUS_REGULAR_OBJECT_COUNT 7
/*
* Return values
*/
#define MODBUS_OK 0
#define MODBUS_ERROR -1 // generic error
#define MODBUS_ERROR_CRC -2 // checksum failed
#define MODBUS_ERROR_FRAME_INVALID -3 // invalid frame format / length
#define MODBUS_ERROR_OUT_OF_BOUNDS -4 // requested register is out of bounds
#define MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED -5 // function not implemented in callback
#define MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED -6 // register not implemented in callback
#define MODBUS_ERROR_DEVICE_ID_NOT_IMPLEMENTED -7
/*
* Data types
*/
/* Public functions codes (Modbus Application protocol specification, section 5.1) */
typedef enum {
/* single bit access functions */
MODBUS_READ_COILS = 1,
MODBUS_READ_DO = 1, // alias
MODBUS_READ_DISCRETE_INPUTS = 2,
MODBUS_READ_DI = 2, // alias
MODBUS_WRITE_SINGLE_COIL = 5,
MODBUS_WRITE_SINGLE_DO = 5, // alias
MODBUS_WRITE_MULTIPLE_COILS = 15,
MODBUS_WRITE_MULTIPLE_DO = 15, // alias
/* 16-bit access functions */
MODBUS_READ_HOLDING_REGISTERS = 3,
MODBUS_READ_AO = 3, // alias
MODBUS_READ_INPUT_REGISTERS = 4,
MODBUS_READ_AI = 4, // alias
MODBUS_WRITE_SINGLE_REGISTER = 6,
MODBUS_WRITE_SINGLE_AO = 6, // alias
MODBUS_WRITE_MULTIPLE_REGISTERS = 16,
MODBUS_WRITE_MULTIPLE_AO = 16, // alias
MODBUS_MASK_WRITE_REGISTER = 22,
MODBUS_READ_WRITE_MULTIPLE_REGISTERS = 23,
MODBUS_READ_FIFO_QUEUE = 24,
/* file record access */
MODBUS_READ_FILE_RECORD = 20,
MODBUS_WRITE_FILE_RECORD = 21,
/* diagnostics */
MODBUS_READ_EXCEPTION_STATUS = 7,
MODBUS_DIAGNOSTIC = 8, /* sub codes: 00-18,20 */
MODBUS_GET_COM_EVENT_COUNTER = 11,
MODBUS_GET_COM_EVENT_LOG = 12,
MODBUS_REPORT_SLAVE_ID = 17,
MODBUS_READ_DEVICE_IDENTIFICATION = 43, /* sub codes: 14 */
} modbus_function_code_t;
typedef enum {
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 1,
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2,
MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY = 2,
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3,
MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE = 4,
MODBUS_EXCEPTION_ACKNOWLEDGE = 5,
MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY = 6,
MODBUS_EXCEPTION_MEMORY_PARITY_ERROR = 8,
MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 10,
MODBUS_EXCEPTION_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 11,
MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE = 3
} modbus_exception_code_t;
typedef struct {
modbus_function_code_t function_code : 8;
uint16_t register_address; // e.g. first register of A0: 0
uint16_t register_number; // e.g. first register of A0: 40001
uint8_t register_count; // number of registers to be read/written
modbus_exception_code_t exception;
uint8_t broadcast; // 1 if broadcast, 0 otherwise
union {
uint8_t buffer8b[MODBUS_MAX_RTU_FRAME_SIZE];
uint16_t buffer16b[MODBUS_MAX_RTU_FRAME_SIZE/2];
uint16_t input_registers[MODBUS_MAX_REGISTERS];
uint16_t holding_registers[MODBUS_MAX_REGISTERS];
int16_t input_registers_signed[MODBUS_MAX_REGISTERS];
int16_t holding_registers_signed[MODBUS_MAX_REGISTERS];
};
/* process device id */
uint8_t read_device_id_code;
uint8_t object_id;
} modbus_transaction_t;
typedef enum {
MODBUS_DO_START_NUMBER = 1, // Discrete output coils
MODBUS_DO_END_NUMBER = 9999,
MODBUS_DI_START_NUMBER = 10001, // Discrete input contacts
MODBUS_DI_END_NUMBER = 19999,
MODBUS_AI_START_NUMBER = 30001, // Analog input registers
MODBUS_AI_END_NUMBER = 39999,
MODBUS_AO_START_NUMBER = 40001, // Analog output (holding registers)
MODBUS_AO_END_NUMBER = 49999
} modbus_register_number_t;
typedef enum {
MODBUS_CONFORMITY_BASIC = 1,
MODBUS_CONFORMITY_REGULAR = 2,
MODBUS_CONFORMITY_EXTENDED = 3,
MODBUS_INDIVIDUAL_ACCESS = 4 /* not actually part of conformity, but I'll keep it here anyway */
} modbus_conformity_level_t;
/* Device ID datatypes */
#define MODBUS_DEVICE_ID_OBJECT_NUM 7
typedef struct {
union {
struct {
/* Basic category (mandatory part) */
char *VendorName;
char *ProductCode;
char *MajorMinorRevision;
/* Regular category (optional part) */
char *VendorUrl;
char *ProductName;
char *ModelName;
char *UserApplicationName;
/* Extended category (optional part) */
// Nothing here yet!
} object_name;
char *object_id[MODBUS_DEVICE_ID_OBJECT_NUM];
};
uint8_t conformity_level;
} modbus_device_id_t;
/*
* Global variables
*/
/* device address: declared in modbus.c */
extern uint8_t modbus_slave_address;
/* shared modbus buffer; defined in modbus.c; may be used elsewhere in code */
extern uint8_t modbus_buffer[];
/* modbus device id struct */
extern modbus_device_id_t *modbus_device_id;
/*
* Function prototypes
*/
/* process message: should be called in when modbus message was received (e.g. in main.c)
* modbus_process_msg() may call following functions:
* - modbus_callback_function() if data readout is requested
* - modbus_uart_transmit_function() if response is required
* Both functions have to be implemented by user.
*/
int8_t modbus_slave_process_msg(const uint8_t *buffer, int len);
int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id);
int8_t modbus_slave_set_address(uint8_t address);
/* modbus callback function type - should be implemented by user (e.g. in main.c) */
int8_t modbus_slave_callback(modbus_transaction_t *transaction);
/* UART transmit function type - should be implemented by user (e.g. in main.c) */
int8_t modbus_transmit_function(uint8_t *buffer, uint16_t data_len);
#endif /* SRC_MODBUS_H_ */

View File

@ -1,431 +0,0 @@
/*
* modbus.c
*
* Created on: Jul 18, 2021
* Author: user
*/
#include "modbus.h"
/*
* Global variables
*/
/* Modbus TX buffer; can be also used for RX in memory constrained systems (e.g. in main.c);
* NOTE if shared buffer is used for TX/RX, care must be taken to prevent writing into buffer
* during execution of modbus_process_message() */
uint8_t modbus_buffer[MODBUS_MAX_RTU_FRAME_SIZE];
/* MODBUS device address */
uint8_t modbus_slave_address = MODBUS_DEFAULT_SLAVE_ADDRESS;
/* Device ID struct */
modbus_device_id_t *modbus_device_id = NULL;
/*
* CRC16 functions
* see https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
* section 6.2.2
*/
/* CRC16 (without memory mapped values)
* taken from https://ctlsys.com/support/how_to_compute_the_modbus_rtu_message_crc/ */
uint16_t modbus_CRC16(const uint8_t *buf, int len)
{
uint16_t crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (uint16_t)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
} else { // Else LSB is not set
crc >>= 1; // Just shift right
}
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
/*
* Private functions
*/
static uint8_t modbus_fill_device_id_objects(uint8_t *buffer, modbus_transaction_t *transaction)
{
/* we assume buffer is 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN = 252 bytes long */
/* find out how many objects we copy to buffer */
int len;
uint8_t object_index = transaction->object_id;
uint8_t object_count;
uint8_t more_follows = MODBUS_NO_MORE_FOLLOWS;
uint8_t next_object_id;
uint8_t last_object;
const uint8_t max_len = 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN;
/* last object index */
if (transaction->read_device_id_code == MODBUS_CONFORMITY_BASIC) {
last_object = MODBUS_BASIC_OBJECT_COUNT;
} else if (transaction->read_device_id_code == MODBUS_CONFORMITY_REGULAR) {
last_object = MODBUS_REGULAR_OBJECT_COUNT;
/* extended not implemented */
// } else if (transaction->read_device_id_code == MODBUS_CONFORMITY_EXTENDED){
// last_object = MODBUS_EXTENDED_OBJECT_COUNT;
} else {
/* fallback: regular */
last_object = MODBUS_REGULAR_OBJECT_COUNT;
}
last_object--; // we need index
/* copy as many objects as possible */
do {
/* copy object */
int object_len = strlen(modbus_device_id->object_id[object_index]);
if (len + object_len + 2 > max_len) {
more_follows = MODBUS_MORE_FOLLOWS;
next_object_id = object_index;
break;
}
/* offset is for "more follows", "next object id", "object count" */
buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_index;
buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_len;
/* note that string copied to buffer is not null-terminated */
strncpy((char*)(buffer + len), (char*)modbus_device_id->object_id[object_index++], object_len);
len += object_len;
object_count++;
} while (object_index < last_object);
buffer[0] = more_follows;
buffer[1] = next_object_id;
buffer[2] = object_count;
return MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len;
}
/* here we assume buffer has minimal size of MODBUS_MAX_RTU_FRAME_SIZE;
* this function is private, so hopefully it's going to be ok */
static int8_t modbus_transaction_to_buffer(uint8_t *buffer, uint8_t *msg_len, modbus_transaction_t *transaction)
{
uint16_t crc16;
uint8_t byte_count;
uint8_t buffer_pos = 0;
// TODO use relative indices (increments) instead of absolute
buffer[buffer_pos++] = modbus_slave_address;
buffer[buffer_pos++] = transaction->function_code;
*msg_len = 5;
if (transaction->function_code & MODBUS_ERROR_FLAG) {
/* sending error reply */
buffer[buffer_pos++] = transaction->exception;
} else {
switch (transaction->function_code) {
case MODBUS_READ_HOLDING_REGISTERS:
case MODBUS_READ_INPUT_REGISTERS:
byte_count = transaction->register_count * 2;
buffer[buffer_pos++] = byte_count;
*msg_len = byte_count + 5;
for (int i = 0; i < transaction->register_count; i++) {
// TODO endianness handling
/* buffer16b is alias for both holding and input register buffers */
buffer[buffer_pos++] = transaction->buffer16b[i] >> 8;
buffer[buffer_pos++] = transaction->buffer16b[i] & 0xff;
}
break;
case MODBUS_WRITE_SINGLE_REGISTER:
buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8);
buffer[buffer_pos++] = (uint8_t) transaction->register_address;
buffer[buffer_pos++] = (uint8_t) (transaction->holding_registers[0] >> 8);
buffer[buffer_pos++] = (uint8_t) transaction->holding_registers[0];
*msg_len = 8; /* includes 2 bytes for CRC */
break;
case MODBUS_WRITE_MULTIPLE_REGISTERS:
buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8);
buffer[buffer_pos++] = (uint8_t) transaction->register_address;
buffer[buffer_pos++] = (uint8_t) (transaction->register_count >> 8);
buffer[buffer_pos++] = (uint8_t) transaction->register_count;
*msg_len = 8; /* includes 2 bytes for CRC */
break;
case MODBUS_READ_DEVICE_IDENTIFICATION:
/* MEI type */
buffer[buffer_pos++] = MODBUS_MEI;
/* read device id */
buffer[buffer_pos++] = transaction->read_device_id_code;
/* conformity level */
buffer[buffer_pos++] = modbus_device_id->conformity_level;
/* fill buffer with as many objects as possible */
*msg_len = modbus_fill_device_id_objects(buffer+buffer_pos, transaction);
*msg_len += 7; /* includes 2 bytes for CRC */
break;
}
}
crc16 = modbus_CRC16(buffer, buffer_pos); /* last two bytes is the checksum itself */
buffer[buffer_pos++] = crc16 & 0xff;
buffer[buffer_pos++] = crc16 >> 8;
return MODBUS_OK;
}
static int8_t modbus_process_device_id_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction)
{
uint8_t MEI_type;
uint8_t read_device_id_code;
uint8_t object_id;
uint8_t buffer_pos = 0;
if (transaction->broadcast == 1) {
/* Read device ID broadcast - invalid; ignore (master will get timeout) */
return MODBUS_ERROR;
}
if (modbus_device_id == NULL) {
/* modbus_device_id not initialized; user should use modbus_slave_init_device_id() first */
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE;
return MODBUS_OK;
}
if (len < MODBUS_READ_DEVICE_ID_REQUEST_LEN) {
/* frame too short, ignore */
return MODBUS_ERROR;
}
/* next byte should be MEI = 0x0E */
MEI_type = buffer[buffer_pos++];
if (MEI_type != MODBUS_MEI) {
/* invalid MEI, ignore. I have no idea what MEI does, but it should always be 0x0E */
return MODBUS_ERROR;
}
/* next byte is read device id code */
read_device_id_code = buffer[buffer_pos++];
/* read device id code can only have values 1,2,3,4 */
if (read_device_id_code < 1 || read_device_id_code > 4) {
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE;
return MODBUS_OK;
}
transaction->read_device_id_code = read_device_id_code;
/* next byte is object id */
object_id = buffer[buffer_pos++];
transaction->object_id = object_id;
if (object_id > MODBUS_DEVICE_ID_OBJECT_NUM) {
/* illegal object ID */
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
return MODBUS_OK;
}
/* Message processed */
return MODBUS_OK;
}
/* returns ERROR only when no response to master is needed */
static int8_t modbus_process_read_write_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction)
{
uint8_t byte_count;
int8_t callback_result;
uint8_t buffer_pos = 0;
/* set starting register number */
switch (transaction->function_code) {
/* coils */
case MODBUS_READ_DO:
case MODBUS_WRITE_SINGLE_DO:
case MODBUS_WRITE_MULTIPLE_DO:
transaction->register_number = MODBUS_DO_START_NUMBER;
break;
/* discrete inputs */
case MODBUS_READ_DI:
transaction->register_number = MODBUS_DI_START_NUMBER;
break;
/* input registers */
case MODBUS_READ_AI:
transaction->register_number = MODBUS_AI_START_NUMBER;
break;
/* holding registers */
case MODBUS_READ_AO:
case MODBUS_WRITE_SINGLE_AO:
case MODBUS_WRITE_MULTIPLE_AO:
case MODBUS_READ_WRITE_MULTIPLE_REGISTERS:
transaction->register_number = MODBUS_AO_START_NUMBER;
break;
}
#define MODBUS_FLAG_WRITE 0x01
#define MODBUS_FLAG_SINGLE 0x02
uint8_t flags = 0x00;
/* process message */
switch (transaction->function_code) {
case MODBUS_WRITE_SINGLE_COIL:
case MODBUS_WRITE_SINGLE_REGISTER: /* holding register */
flags |= MODBUS_FLAG_SINGLE;
case MODBUS_WRITE_MULTIPLE_COILS:
case MODBUS_WRITE_MULTIPLE_REGISTERS:
flags |= MODBUS_FLAG_WRITE;
case MODBUS_READ_DISCRETE_INPUTS:
case MODBUS_READ_COILS:
case MODBUS_READ_INPUT_REGISTERS:
case MODBUS_READ_HOLDING_REGISTERS:
if (len < MODBUS_MINIMAL_READWRITE_LEN) {
/* buffer too short to contain everything we need */
return MODBUS_ERROR;
}
transaction->register_address = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1];
buffer += 2;
// TODO check length!
if (flags & MODBUS_FLAG_WRITE) {
if (flags & MODBUS_FLAG_SINGLE) {
transaction->holding_registers[0] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1];
buffer_pos += 2;
} else {
/* Write multiple registers */
transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1];
buffer_pos += 2;
if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN) {
return MODBUS_ERROR;
}
byte_count = buffer[buffer_pos++];
if (transaction->register_count > 123 || 2*transaction->register_count != byte_count) {
/* Max number of register is defined by Modbus_Application_Protocol_V1_1b, section 6.12 */
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY;
} else {
if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN + byte_count) {
return MODBUS_ERROR;
}
for (uint8_t i = 0; i < transaction->register_count; i++) {
transaction->holding_registers[i] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1];
buffer_pos += 2;
}
}
}
} else {
transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1];
buffer_pos += 2;
if (
transaction->register_count < 1 ||
transaction->register_count > MODBUS_MAX_REGISTERS
) {
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE;
}
}
// add offset to register number
transaction->register_number += transaction->register_address;
break;
default:
/* function code not known / not implemented, reply with
* ExceptionCode 1 */
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
break;
}
/* data in modbus_buffer have been processed and buffer can be re-used for TX */
/* handle reply */
if (transaction->exception != 0) {
/* indicate error */
transaction->function_code |= MODBUS_ERROR_FLAG;
} else {
callback_result = modbus_slave_callback(transaction);
/* error handling */
if (callback_result != MODBUS_OK) {
transaction->function_code |= MODBUS_ERROR_FLAG;
if (callback_result == MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED) {
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
} else if (callback_result == MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED) {
transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
}
}
}
return MODBUS_OK;
}
/*
* Public function definitions
*/
int8_t modbus_slave_set_address(uint8_t address)
{
if (address == 0) {
/* address 0 is broadcast address */
return MODBUS_ERROR;
}
modbus_slave_address = address;
return MODBUS_OK;
}
int8_t modbus_slave_process_msg(const uint8_t *buffer, int len)
{
/*
* TODO list:
*
* 1) check that errors and exceptions are handled according to Modbus_Application_Protocol_V1_1b.pdf
* 2) buffer overflow prevention: for each function code, check that buffer is long enough
*/
/* transaction holds message context and content:
* it wraps all necessary buffers and variables */
modbus_transaction_t transaction;
uint8_t buffer_pos = 0;
if (len < MODBUS_MINIMAL_FRAME_LEN) {
/* frame too short; return error (no reply needed) */
return MODBUS_ERROR_FRAME_INVALID;
}
/* check CRC first */
uint16_t crc_received = (buffer[len - 1] << 8) | buffer[len - 2];
uint16_t crc_calculated = modbus_CRC16(buffer, len - 2);
if (crc_received != crc_calculated) {
/* CRC mismatch, return error (no reply needed) */
return MODBUS_ERROR_CRC;
}
/* check if address matches ours */
uint8_t address = buffer[buffer_pos++];
transaction.broadcast = (address == MODBUS_BROADCAST_ADDR);
if (address != modbus_slave_address && transaction.broadcast != 1) {
/* Message is not for us (no reply needed) */
return MODBUS_OK;
}
/* get function code */
transaction.function_code = buffer[buffer_pos++];
transaction.exception = 0;
uint8_t request_processing_result;
if (transaction.function_code == MODBUS_READ_DEVICE_IDENTIFICATION) {
/* Read device ID request is quite complicated, therefore it has its own processing function */
request_processing_result = modbus_process_device_id_request(buffer + buffer_pos, len - buffer_pos, &transaction);
} else {
/* process other requests: input register read, holding register read/write */
request_processing_result = modbus_process_read_write_request(buffer + buffer_pos, len - buffer_pos, &transaction);
}
uint8_t msg_len;
/* reply only if request was processed successfully and message was not broadcast */
if (request_processing_result == MODBUS_OK && transaction.broadcast == 0) {
modbus_transaction_to_buffer(modbus_buffer, &msg_len, &transaction);
/* send reply */
modbus_transmit_function(modbus_buffer, msg_len);
}
return MODBUS_OK;
}
int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id)
{
if (device_id == NULL) {
return MODBUS_ERROR;
}
/* at least basic category objects have to be implemented */
if ( device_id->object_name.VendorName == NULL ||
device_id->object_name.ProductCode == NULL ||
device_id->object_name.MajorMinorRevision == NULL
) {
return MODBUS_ERROR;
}
/* set conformity level: currently only "basic" and "regular" is implemented */
if ( device_id->object_id[3] != NULL &&
device_id->object_id[4] != NULL &&
device_id->object_id[5] != NULL
) {
/* strings are present in regular category (optional) */
device_id->conformity_level = MODBUS_CONFORMITY_REGULAR;
} else {
device_id->conformity_level = MODBUS_CONFORMITY_BASIC;
}
/* we support both stream and individual access to objects */
device_id->conformity_level |= MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG;
modbus_device_id = device_id;
return MODBUS_OK;
}