From 0bfc93d3ea19015109aa0901dab4b4da96a83693 Mon Sep 17 00:00:00 2001 From: Duke NUCem Date: Mon, 11 Oct 2021 17:05:54 +0200 Subject: [PATCH] WIP write multiple registers --- fw/Core/Inc/modbus.h | 1 + fw/Core/Src/main.c | 11 +++-- fw/Core/Src/modbus.c | 28 ++++++++++++- .../write_multiple_holding_registers_test.py | 41 +++++++++++++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) create mode 100755 tests/write_multiple_holding_registers_test.py diff --git a/fw/Core/Inc/modbus.h b/fw/Core/Inc/modbus.h index cd77c61..3eafbe4 100644 --- a/fw/Core/Inc/modbus.h +++ b/fw/Core/Inc/modbus.h @@ -107,6 +107,7 @@ typedef enum { 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, diff --git a/fw/Core/Src/main.c b/fw/Core/Src/main.c index 005a154..f2c5493 100644 --- a/fw/Core/Src/main.c +++ b/fw/Core/Src/main.c @@ -291,7 +291,11 @@ int main(void) /* TODO: Process data and light a desired color of LED */ /* TODO: Add hystheresis */ - if (sensor_config.led_on) { + /* Reset the TIM21 Elapsed Period Flag */ + tim21_elapsed_period = 0; + } + /* TEST END */ + if (sensor_config.led_on) { if (CO2 <= sensor_config.led_co2_alert_limit1) { /* CO2 is OK -> GREEN */ LL_GPIO_SetOutputPin(LED_R_GPIO_Port, LED_R_Pin); @@ -315,11 +319,6 @@ int main(void) LL_GPIO_SetOutputPin(LED_G_GPIO_Port, LED_G_Pin); LL_GPIO_SetOutputPin(LED_B_GPIO_Port, LED_B_Pin); } - - /* Reset the TIM21 Elapsed Period Flag */ - tim21_elapsed_period = 0; - } - /* TEST END */ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ diff --git a/fw/Core/Src/modbus.c b/fw/Core/Src/modbus.c index c48c762..ef2eeba 100644 --- a/fw/Core/Src/modbus.c +++ b/fw/Core/Src/modbus.c @@ -58,6 +58,7 @@ int8_t modbus_copy_reply_to_buffer(uint8_t *buffer, uint8_t *msg_len, modbus_tra uint16_t crc16; uint8_t byte_count; + // TODO use relative indices (increments) instead of absolute buffer[0] = modbus_slave_address; buffer[1] = transaction->function_code; *msg_len = 5; @@ -86,6 +87,13 @@ int8_t modbus_copy_reply_to_buffer(uint8_t *buffer, uint8_t *msg_len, modbus_tra buffer[5] = (uint8_t) transaction->holding_registers[0]; *msg_len = 8; break; + case MODBUS_WRITE_MULTIPLE_REGISTERS: + buffer[2] = (uint8_t) (transaction->register_address >> 8); + buffer[3] = (uint8_t) transaction->register_address; + buffer[4] = (uint8_t) (transaction->register_count >> 8); + buffer[5] = (uint8_t) transaction->register_count; + *msg_len = 8; + } } crc16 = modbus_CRC16(buffer, *msg_len - 2); /* last two bytes is the checksum itself */ @@ -109,11 +117,18 @@ int8_t modbus_slave_set_address(uint8_t address) 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; int8_t callback_result; uint8_t buffer_pos = 0; + uint8_t byte_count; if (len < MODBUS_MINIMAL_FRAME_LEN) { /* frame too short; return error */ @@ -188,11 +203,22 @@ int8_t modbus_slave_process_msg(const uint8_t *buffer, int len) return MODBUS_ERROR; } transaction.register_address = (buffer[buffer_pos++] << 8) | buffer[buffer_pos++]; + // TODO check length! if (flags & MODBUS_FLAG_WRITE) { if (flags & MODBUS_FLAG_SINGLE) { transaction.holding_registers[0] = (buffer[buffer_pos++] << 8) | buffer[buffer_pos++]; } else { - // write multiple register - TODO + /* Write multiple registers */ + transaction.register_count = (buffer[buffer_pos++] << 8) | buffer[buffer_pos++]; + 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.exception_code = MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY; + } else { + for (uint8_t i = 0; i < transaction.register_count; i++) { + transaction.holding_registers[i] = (buffer[buffer_pos++] << 8) | buffer[buffer_pos++]; + } + } } } else { transaction.register_count = (buffer[buffer_pos++] << 8) | buffer[buffer_pos++]; diff --git a/tests/write_multiple_holding_registers_test.py b/tests/write_multiple_holding_registers_test.py new file mode 100755 index 0000000..add79fd --- /dev/null +++ b/tests/write_multiple_holding_registers_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +from time import sleep +from sys import exit +import minimalmodbus +import serial + +slave_address = 254 +instrument = minimalmodbus.Instrument('/dev/ttyUSB0', slave_address, close_port_after_each_call=True) # port name, slave address (in decimal) + +instrument.serial.baudrate = 19200 +instrument.serial.bytesize = 8 +instrument.serial.parity = serial.PARITY_EVEN +instrument.serial.stopbits = 1 +instrument.serial.timeout = 0.05 # seconds +instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode +instrument.clear_buffers_before_each_transaction = True + +# holding register numbers +LED_on_register = 0 +LED_brightness_register = 1 +LED_smooth_register = 2 +CO2_alert_limit_1_register = 3 +CO2_alert_limit_2_register = 4 +SCD4x_temperature_offset_register = 5 +MODBUS_address_register = 6 +baudrate_register = 7 +# values +LED_on = 1 +LED_brightness = 100 +LED_smooth = 0 +CO2_alert_limit_1 = 1000 +CO2_alert_limit_2 = 2000 +SCD4x_temperature_offset = 0 +MODBUS_address = 254 +baudrate = 0 +registers = [LED_on, LED_brightness, LED_smooth, CO2_alert_limit_1, CO2_alert_limit_2, SCD4x_temperature_offset, MODBUS_address, baudrate] +# write to holding registers +print('---- Writing to multiple holding registers ----') +instrument.write_registers(LED_on_register, registers) +print('---- DONE ----') +exit(0)