arduino-libraries / ArduinoModbus

244 stars 116 forks source link

ModbusRTUClient.holdingRegisterWrite -> Invalid argument #152

Closed o7-machinehum closed 1 month ago

o7-machinehum commented 1 month ago

I create the SensorO2 object and then call check() which works great. But then when I call powerOn(), which calls write16 holdingRegisterWrite is erroring out, as _modbusRtuClient.lastError() is returning: [O2] ModbusRTUClient.write Failed: Invalid argument

I've included my .cpp/.h below. Any help or ideas would be very appreciated. I've also tried without the _modbusRtuClient.setTimeout(3000);

Here is the device: https://www.processsensing.com/docs/userguide/UG-004_OXY-LC-User-Guide.pdf

SENSOR_SST_O2_ADDRESS is set to 0x01

If I uncomment out all the code in write16 (using .beginTransmission, .write etc...) and comment out the code that does holdingRegisterWrite I get [O2] ModbusRTUClient.write Failed: Connection timed out I find it stange the read works fine but the write has issues.

Any help is very much appreciated :)

#include "SensorO2.h"

SensorO2::SensorO2(RS485Class* rs485) : _rs485(rs485) 
{
    init();
};

SensorO2::~SensorO2() {
    _modbusRtuClient.end();
}

void SensorO2::init()
{
    Serial.printf("pre/post delay: %d, %d\n\r", (int)PRE_DELAY_BR, (int)POST_DELAY_BR);
    _rs485->setDelays(
        static_cast<int>(PRE_DELAY_BR), static_cast<int>(POST_DELAY_BR)
    );

    if (_modbusRtuClient.begin(
            *_rs485, board::SENSE_MODULE_MODBUS_BUADRATE, SERIAL_8N1
        )) {
        _available = true;
    }

    _modbusRtuClient.setTimeout(3000);
}

bool SensorO2::check()
{
    uint16_t sn = read16(REGISTER_O2_SN);
    if(sn) {
        Serial.printf("[O2] Connected to Sensor. SN: %d\n\r", sn);
        return true;
    }
    return false;
}

bool SensorO2::available() { return _available; }

uint8_t SensorO2::getRegtype(uint16_t reg) {
    if(reg > 0x7546) {
        return HOLDING_REGISTERS; 
    }
    return INPUT_REGISTERS;
}

const uint16_t SensorO2::read16(uint16_t reg) {
    uint8_t regtype = getRegtype(reg); 

    if (!_modbusRtuClient.requestFrom(
            board::SENSOR_SST_O2_ADDRESS,
            regtype,
            reg,
            1
        )) {
        Serial.printf("[O2] Failed to read registers!: %s\n\r", _modbusRtuClient.lastError());
        return 0;
    }
    return(_modbusRtuClient.read());
}

void SensorO2::write16(int reg, uint16_t val) {
    uint8_t regtype = getRegtype(reg); 

    // if (!_modbusRtuClient.beginTransmission(
    //         board::SENSOR_SST_O2_ADDRESS,
    //         regtype,
    //         reg,
    //         1
    //     )) {
    //     Serial.printf("[O2] modbusRtuClient.beginTransmission Failed: %s\n\r", _modbusRtuClient.lastError());
    //     return;
    // }

    // if(!ModbusRTUClient.write(val)){
    //     Serial.printf("[O2] ModbusRTUClient.write Failed: %s\n\r", _modbusRtuClient.lastError());
    //     return;
    // }

    if(!ModbusRTUClient.holdingRegisterWrite(0x01, 0x9C41, 0x01)){
        Serial.printf("[O2] ModbusRTUClient.write Failed: %s\n\r", _modbusRtuClient.lastError());
        return;
    }

    // if (!ModbusRTUClient.endTransmission()) {
    //     Serial.printf("[O2] ModbusRTUClient.endTransmission: %s\n\r", _modbusRtuClient.lastError());
    //     return;
    // }
}

void SensorO2::powerOn() {
    Serial.print("[O2] Powering On Sensor\n\r");
    write16(SENSOR_ON, 0x01);
    Serial.printf("[O2] Powering On :%d, %d\n\r", read16(SENSOR_ON), read16(0x9C42));
}
#pragma once

#include <Arduino.h>
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>
#include <stdint.h>

#include "Board.h"

// https://www.processsensing.com/docs/userguide/UG-004_OXY-LC-User-Guide.pdf

class SensorO2 final
{
   public:
    SensorO2(RS485Class* rs485);
    ~SensorO2();

    void init();
    void powerOn();
    bool check();
    bool available();

    float readO2();

   private:
    bool _available = false;

    RS485Class* _rs485;

    ModbusRTUClientClass _modbusRtuClient;

    float readFloat(uint16_t registerAddress);

    const uint16_t read16(uint16_t reg);
    void write16(int reg, uint16_t val);
    uint8_t getRegtype(uint16_t reg);

    // Input Registers
    static constexpr uint16_t REGISTER_O2_AVERAGE_FLOAT = 0x7531;  // in %
    static constexpr uint16_t REGISTER_O2_SN = 0x7545;  

    // Holding Registers
    static constexpr int SENSOR_ON = 0x9C41;  

    // Calculate preDelay and postDelay in microseconds as per Modbus RTU
    // Specification
    //
    // MODBUS over serial line specification and implementation guide V1.02
    // Paragraph 2.5.1.1 MODBUS Message RTU Framing
    // https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
    static constexpr float BIT_DURATION{
        1.f / board::SENSE_MODULE_MODBUS_BUADRATE
    };
    static constexpr float WORDLENGTH{9.6f};  // try also with 10.0f

    static constexpr float PRE_DELAY_BR{
         BIT_DURATION * WORDLENGTH * 3.5f * 1e6
    };
    static constexpr float POST_DELAY_BR{
        BIT_DURATION * WORDLENGTH * 3.5f * 1e6
    };
};
o7-machinehum commented 1 month ago

Derpppp it can be seen I was using ModbusRTUClient., rather than the object I created.