slowtec / tokio-modbus

A tokio-based modbus library
Apache License 2.0
378 stars 117 forks source link

Modbus RTU - Serial Direction Pin #229

Open ViliamVadocz opened 6 months ago

ViliamVadocz commented 6 months ago

Is it possible to configure a serial direction pin when using Modbus RTU?

I am communicating between a Raspberry Pi 4 and a CLM8 Intelligent Junction Box (load cell amplifier). I am sending using UART which gets converted to RS485 with MAX485. I think I need to set a specific pin high/low based on whether I am transmitting or receiving. Would it be possible to do this within this library?

I am able to access the pins using rppal, but I would think you would juse use OutputPin from embedded_hal in your interface.

Related issues in another library: https://github.com/emelianov/modbus-esp8266/issues/62

ViliamVadocz commented 6 months ago

For anyone who finds themselves in a similar situation: I used rmodbus instead which is a bit more verbose, but lets me change pin voltage between writing and receiving over UART.

fredrik-jansson-se commented 4 months ago

@ViliamVadocz, I'd also be interested in this. Could you by any chance share some (pseudo) code on how you used rmodbus to accomplish this?

ViliamVadocz commented 4 months ago

@fredrik-jansson-se

Pseudocode:

let mut direction_pin = gpio.get(DIRECTION_PIN)?.into_output();
let mut modbus_request = ModbusRequest::new(SLAVE_ADDRESS, ModbusProto::Rtu);

let mut request_buffer = Vec::new();
let mut response_buffer = Vec::new();
let mut uart_buffer = [0u8; 128];

// example: get registers
modbus_request.generate_get_holdings(address, num_registers, &mut request_buffer)?;

direction_pin.set_high();
uart.write(&request)?;
// sleep (~ 1ms) to let device see that the pin is high and read data
direction_pin.set_low();
// sleep (~ 10ms) to let the device process request and send response

let read = uart.read(&mut uart_buffer)?;
modbus_request.parse_u16(&uart_buffer[..read], &mut response_buffer)?;
// use data in `response_buffer`

Of course, be sure to .clear() the Vecs and .fill(0) the UART buffer before or after use.

fredrik-jansson-se commented 4 months ago

Fantastic, thank you!!

amreo commented 2 months ago

@fredrik-jansson-se

Pseudocode:

let mut direction_pin = gpio.get(DIRECTION_PIN)?.into_output();
let mut modbus_request = ModbusRequest::new(SLAVE_ADDRESS, ModbusProto::Rtu);

let mut request_buffer = Vec::new();
let mut response_buffer = Vec::new();
let mut uart_buffer = [0u8; 128];

// example: get registers
modbus_request.generate_get_holdings(address, num_registers, &mut request_buffer)?;

direction_pin.set_high();
uart.write(&request)?;
// sleep (~ 1ms) to let device see that the pin is high and read data
direction_pin.set_low();
// sleep (~ 10ms) to let the device process request and send response

let read = uart.read(&mut uart_buffer)?;
modbus_request.parse_u16(&uart_buffer[..read], &mut response_buffer)?;
// use data in `response_buffer`

Of course, be sure to .clear() the Vecs and .fill(0) the UART buffer before or after use.

What is uart in this pseudocode?

fredrik-jansson-se commented 2 months ago

@amreo, full disclouse, I ended up hacking this demo in Python. But I assume the uart instance is e.g. an instance of SerialPort from https://crates.io/crates/serial.

Does that help?