1
0
Fork 0
 
 
Go to file
Bo Jeanes 2d8612348f Updating to MQTT with parsed state working
However, definitely getting some TCP issues to Modbus client, likely due
to unsafe polling of a task or something. If the connection establishes,
it seems rock solid, but sometimes it will get a "could not fill buffer"
error early on and then it just doesn't work.

It seems to come in waves. I think I'll need to handle it specifically
and restart the connection, if I can't identify the root cause.
2022-09-09 16:12:58 +10:00
.github/workflows Add GH action for tests/clippy 2022-09-04 13:10:39 +10:00
modbus-mqtt Updating to MQTT with parsed state working 2022-09-09 16:12:58 +10:00
sungrow-winets Updating to MQTT with parsed state working 2022-09-09 16:12:58 +10:00
tokio_modbus-winets winets transport doesn't need tcp or rtu features 2022-09-04 13:10:39 +10:00
.gitignore Initial commit based on rumqttc example 2022-07-15 17:11:38 +10:00
Cargo.lock wip 2022-09-08 17:52:49 +10:00
Cargo.toml Implement basic WiNet-S modbus driver 2022-08-30 16:28:33 +10:00
README.md Update README 2022-09-09 12:34:03 +10:00

README.md

ModbusMQTT

A bridge between Modbus devices and MQTT.

It is early days, but the plan is:

  • Support custom Modbus transports (Sungrow WiNet-S has been implemented)
    • Modbus RTU has not been tested because I don't have a serial Modbus device, but in principle it should work. Please let me know
  • Support reading input registers
  • Support reading holding registers
  • Support setting holding registers
  • Support optional auto-configuration of Home Assistant entities, including using MQTT Number et al for holding registers, to allow setting the value.
  • TLS MQTT connections
  • ws:// and ws:// MQTT connections

NOTE: For the time being, this does not support MQTTv5.

Installing

For now, use cargo install (Rust toolchain required). Soon, I will have release binaries attached to GitHub releases. In the future, there will also be Docker images made available for convenience.

Running

Start the binary, passing in the URL to your MQTT server, including any credentials:

$ modbus-mqtt mqtt://$MQTT_HOST[:$MQTT_PORT]/[$CUSTOM_MODBUS_TOPIC]

The supported protocols are currently just tcp:///mqtt://, but with intent to support: mqtts://, ssl:///tls://, ws://, and wss://.

The default topic which ModbusMQTT monitors and to which it publishes is modbus-mqtt. You can vary that by changing the path portion of the MQTT URL.

Further, you can change other MQTT options by using query params, such as setting a custom client_id:

"mqtt://1.2.3.4/?client_id=$CUSTOM_CLIENT_ID"

For a full list of supported options, check the MQTT client library's source code.

Connecting to Modbus devices

To connect to a Modbus device, you need to post the connection details to MQTT under a topic of $prefix/$connection_id/connect. It is intended that such messages are marked as retained so that ModbusMQTT reconnects to your devices when it restarts.

For instance, a simple config might be:

// PUBLISH modbus-mqtt/solar-inverter/connect
{
  "host": "10.10.10.219",
  "proto": "tcp",
}

If the connection is successful, you will see the following message like the following sent to the MQTT server:

// modbus-mqtt/solar-inverter/state
"connected"

Full connection examples

All fields accepted (optional fields show defaults)

{
  // Common fields
  "address_offset": 0, // optional
  "unit": 1,           // optional, aliased to "slave"

  // TCP:
  "proto": "tcp",
  "host": "1.2.3.4",
  "port": 502, // optional

  // RTU / Serial:
  "proto": "rtu",
  "tty": "/dev/ttyACM0",
  "data_bits": "Eight",   // optional (TODO: accept numeric and lowercase)
                          //   valid: Five, Six, Seven, Eight
  "stop_bits": "One",     // optional (TODO: accept numeric and lowercase)
                          //   valid: One, Two
  "flow_control": "None", // optional (TODO: accept lowercase)
                          //   valid: None, Software, Hardware
  "parity": "None",       // optional (TODO: accept lowercase)
                          //   valid: None, Odd, Even

  // Sungrow WiNet-S dongle
  "proto": "winet-s",
  "host": "1.2.3.4",
}

Monitoring registers

Post to $MODBUS_MQTT_TOPIC/$CONNECTION_ID/$TYPE/$ADDRESS where $TYPE is one of input or holding with the following payload (optional fields show defaults):

{
  "name": null,        // OPTIONAL - gives the register a name which is used in the register MQTT topics (must be a valid topic component)

  "interval": "1m",    // OPTIONAL - how often to update the registers value to MQTT
                       //   e.g.: 3s (every 3 seconds)
                       //         2m (every 2 minutes)
                       //         1h (every 1 hour)

  "swap_bytes": false, // OPTIONAL
  "swap_words": false, // OPTIONAL

  "type": "s16",       // OPTIONAL
                       //   valid: s8, s16, s32, s64 (signed)
                       //          u8, u16, u32, u64 (unsigned)
                       //          f32, f64          (floating point)

  "scale": 0,          // OPTIONAL - number in register will be multiplied by 10^(scale)
                       //   e.g.: to turn kW into W, you would provide scale=3
                       //         to turn W into kW, you would provide scale=-3

  "offset": 0,         // OPTIONAL - will be added to the final result (AFTER scaling)


  // Additionally, "type" can be set to "array":
  "type": "array",
  "of": "u16"          // The default array element is u16, but you can change it with the `of` field
}

Further, the type field can additionally be set to "array", in which case, a count field must be provided. The array elements default to "s16" but can be overriden in the "of" field.

NOTE: this is likely to change such that there is always a count field (with default of 1) and if provided to be greater than 1, it will be interpreted to be an array of elements of the type specified.

There is some code to accept "string" type (with a required length field) but this is experimental and untested.

Register shorthand

When issuing the connect payload, you can optionally include input and/or holding fields as arrays containing the above register schema, as long as an address field is added. When present, these payloads will be replayed to the MQTT server as if the user had specified each register separately, as above.

This is a recommended way to specify connections, but the registers are broken out separately so that they can be dynamically added to too.

Development

TODO: set up something like https://hub.docker.com/r/oitc/modbus-server to test with

Similar projects