modbus.cpp
1 #include "modbus.h" 2 3 #include <Arduino.h> 4 #include <ModbusRTUMaster.h> 5 6 namespace { 7 8 constexpr uint8_t MAX_CHANNELS = config::rs485::CHANNEL_COUNT; 9 ModbusRTUMaster modbus0(Serial1, config::rs485::BUS_0.de_re_gpio); 10 ModbusRTUMaster modbus1(Serial2, config::rs485::BUS_1.de_re_gpio); 11 12 struct ChannelContext { 13 hardware::rs485::Channel channel; 14 ModbusRTUMaster *master; 15 bool ready; 16 }; 17 18 ChannelContext channels[MAX_CHANNELS] = { 19 {hardware::rs485::Channel::Bus0, &modbus0, false}, 20 {hardware::rs485::Channel::Bus1, &modbus1, false}, 21 }; 22 23 bool initialized = false; 24 25 size_t channel_index(hardware::rs485::Channel channel) { 26 return static_cast<size_t>(channel); 27 } 28 29 ModbusError translate_error(uint8_t error) { 30 switch (error) { 31 case MODBUS_RTU_MASTER_SUCCESS: return ModbusError::Success; 32 case MODBUS_RTU_MASTER_INVALID_ID: return ModbusError::InvalidId; 33 case MODBUS_RTU_MASTER_INVALID_BUFFER: return ModbusError::InvalidBuffer; 34 case MODBUS_RTU_MASTER_INVALID_QUANTITY: return ModbusError::InvalidQuantity; 35 case MODBUS_RTU_MASTER_RESPONSE_TIMEOUT: return ModbusError::ResponseTimeout; 36 case MODBUS_RTU_MASTER_FRAME_ERROR: return ModbusError::FrameError; 37 case MODBUS_RTU_MASTER_CRC_ERROR: return ModbusError::CrcError; 38 case MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR: return ModbusError::UnknownCommunicationError; 39 case MODBUS_RTU_MASTER_UNEXPECTED_ID: return ModbusError::UnexpectedId; 40 case MODBUS_RTU_MASTER_EXCEPTION_RESPONSE: return ModbusError::ExceptionResponse; 41 case MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE: return ModbusError::UnexpectedFunctionCode; 42 case MODBUS_RTU_MASTER_UNEXPECTED_LENGTH: return ModbusError::UnexpectedLength; 43 case MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT: return ModbusError::UnexpectedByteCount; 44 case MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS: return ModbusError::UnexpectedAddress; 45 case MODBUS_RTU_MASTER_UNEXPECTED_VALUE: return ModbusError::UnexpectedValue; 46 case MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY: return ModbusError::UnexpectedQuantity; 47 default: return ModbusError::UnknownCommunicationError; 48 } 49 } 50 51 } 52 53 bool networking::modbus::initialize() { 54 if (initialized) return true; 55 if (!hardware::rs485::initialize()) return false; 56 57 for (size_t index = 0; index < MAX_CHANNELS; index++) { 58 hardware::rs485::BusDescriptor descriptor = { 59 .channel = channels[index].channel, 60 .serial = nullptr, 61 .de_re_gpio = -1, 62 .baud_rate = 0, 63 .ready = false, 64 }; 65 if (!hardware::rs485::accessDescriptor(&descriptor) || !descriptor.ready || !descriptor.serial) { 66 channels[index].ready = false; 67 continue; 68 } 69 70 channels[index].master->begin(descriptor.baud_rate, SERIAL_8N1); 71 channels[index].master->setTimeout(config::wind::SENSOR_DELAY_MS * 5); 72 channels[index].ready = true; 73 } 74 75 initialized = true; 76 return true; 77 } 78 79 bool networking::modbus::readHoldingRegisters(ReadHoldingRegistersCommand *command) { 80 if (!command || !command->output_words || command->register_count == 0) { 81 if (command) command->error = ModbusError::InvalidBuffer; 82 return false; 83 } 84 if (!networking::modbus::initialize()) { 85 command->error = ModbusError::NotInitialized; 86 return false; 87 } 88 89 size_t index = channel_index(command->channel); 90 if (index >= MAX_CHANNELS || !channels[index].ready) { 91 command->error = ModbusError::InvalidChannel; 92 return false; 93 } 94 95 ModbusRTUMasterError error = channels[index].master->readHoldingRegisters( 96 command->slave_id, command->start_register, command->output_words, 97 command->register_count); 98 command->error = translate_error(error); 99 return command->error == ModbusError::Success; 100 } 101 102 bool networking::modbus::scan(ModbusScanCommand *command) { 103 if (!command || !command->results || command->max_results == 0) return false; 104 if (!networking::modbus::initialize()) return false; 105 106 command->result_count = 0; 107 for (uint8_t slave_id = command->first_slave_id; 108 slave_id <= command->last_slave_id && command->result_count < command->max_results; 109 slave_id++) { 110 uint16_t output_word = 0; 111 ReadHoldingRegistersCommand read_command = { 112 .channel = command->channel, 113 .slave_id = slave_id, 114 .start_register = 0, 115 .register_count = 1, 116 .output_words = &output_word, 117 .error = ModbusError::NotInitialized, 118 }; 119 120 bool responsive = networking::modbus::readHoldingRegisters(&read_command) || 121 read_command.error == ModbusError::ExceptionResponse; 122 if (responsive) { 123 command->results[command->result_count++] = { 124 .channel = command->channel, 125 .slave_id = slave_id, 126 .responsive = true, 127 .error = read_command.error, 128 }; 129 } 130 delay(10); 131 if (slave_id == UINT8_MAX) break; 132 } 133 134 return true; 135 }