/ firmware / src / networking / modbus.cpp
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  }