Giter VIP home page Giter VIP logo

modbus-mqtt's Introduction

ModbusMQTT

This repository is a workspace for developing ModbusMQTT and the crates developed in the course of the project.

View the appropriate READMEs in each package directory for details about each.

Docker Crates.io docs.rs license

ModbusMQTT is a bridge between Modbus devices and MQTT. It aims to allow the operator to generically expose any compatible Modbus device as though its API were MQTT.

Crates.io docs.rs Crates.io

This is a barebones API client for reading and writing settings for Sungrow solar and hybrid inverters equipped with a WiNet-S communications module. Its only known use is tokio_modbus-winets (see below).

Crates.io docs.rs Crates.io

This wraps sungrow-winets client in the appropriate traits from tokio-modbus to allow accessing the Modbus registers which the Sungrow WiNet-S dongle exposes. The reason for this is simply that Sungrow's TCP Modbus support is buggy and inconsistent (though improving).

modbus-mqtt's People

Contributors

bjeanes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

xsolar-co

modbus-mqtt's Issues

Support setting values for mutable registers

For now, Modbus-MQTT only supports input and holding registers, so this only applies to holding registers.

If a register value is being published to modbus-mqtt/$DEVICE/$REGISTER topic then it should listen to modbus-mqtt/$DEVICE/$REGISTER/set for a new value, parse/interpret it in a reasonable way vis-a-vis scale, offset, type, etc.

Unsure how errors should work here, but probably can just set something in the .../status topic.

When setting a register value, it should read-back the value and publish it on its topic straight away instead of waiting for next tick.

Problems on MQTT disconnect

Scenario

I'm using modbus-mqtt in a Docker container to periodically publish values of a power meter.

Problem

I recently restarted the MQTT broker. modbus-mqtt stopped working from this moment, showing the following log entry:

2023-06-03T06:20:15.796005Z ERROR modbus_mqtt::server: MQTT connection error cause=I/O: Connection reset by peer (os error 104)

After restarting the container and reconfiguring modbus-mqtt, everything continued to work.

I'm experiencing 2 issues:

  1. modbus-mqtt does not reconnect to the broker.
  2. The configuration is lost after reconnection.

Suggestion 1: Reconnect to the MQTT broker

I tried to solve the first issue by implmenting a health check:

services:
  modbus-mqtt:
    image: "bjeanes/modbus-mqtt:edge-alpine"
    restart: unless-stopped
    command: "mqtt://broker"
    healthcheck:
      test: netstat -t | grep ':1883\s\+ESTABLISHED' -q || kill 1
      timeout: 5s
      start_period: 5s

This works but feels a bit cumbersome to me. A disadvantage is that the configuration is lost after restart. I'd prefer to configure modbus-mqtt to reconnect to the broker, keeping the configuration.

Suggestion 2: Add configuration on start

The second issue could be solved by allowing a configuration file as command line argument. I'm currently using a second docker container that publishes the configuration after modbus-mqtt has booted:

services:
  modbus-mqtt:
  [...]

  modbus-mqtt-init:
    environment:
      MQTT_BROKER: broker
      MQTT_TOPIC: meter
    volumes:
      - "./modbus-mqtt.json:/etc/modbus-mqtt.json:ro"
    image: modbus-mqtt-init
    container_name: modbus-mqtt-init
    build:
      dockerfile_inline: |
        FROM alpine
        RUN apk add --no-cache mosquitto-clients
        CMD mosquitto_pub -h "$${MQTT_BROKER}" -t "modbus-mqtt/$${MQTT_TOPIC}/connect" -s </etc/modbus-mqtt.json
    depends_on:
      - modbus-mqtt

This works for a normal service start, but unfortunately doesn't work on MQTT disconnect.


❤ Thanks for this great project which perfectly suits my needs! ❤

Conflicting implementations of trait `Debug` for type `RunningStateBits`

Hi there,

I'm getting an error when trying to compile:

λ cargo run
   Compiling sungrow-winets v0.1.0
error[E0119]: conflicting implementations of trait `Debug` for type `RunningStateBits`
   --> /Users/dominik/.cargo/registry/src/github.com-1ecc6299db9ec823/sungrow-winets-0.1.0/src/lib.rs:255:1
    |
255 | #[bitmask_enum::bitmask(u16)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `RunningStateBits`
256 | #[derive(Debug)]
    |          ----- first implementation here
    |
    = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0119`.
error: could not compile `sungrow-winets` due to previous error

Some system infos:

λ rustc -V
rustc 1.69.0 (84c898d65 2023-04-16)
λ arch
arm64
λ sw_vers
ProductName:		macOS
ProductVersion:		13.4
BuildVersion:		22F66

My Cargo.toml

[package]
name = "solar"
version = "0.1.0"
edition = "2021"

[dependencies]
sungrow-winets = "0.1.0"

And my main.rs

fn main() {
	println!("hello world");
}

Support for multiple units per connection

First of all: thanks for this project. It's really useful for me!

I successfully use modbus-mqtt in a project to read a single energy meter via TCP. This works fine.

I'm now evaluating whether modbus-mqtt can be used for a different project where multiple energy meters have to be read via RTU.

Current setup

I managed to read a single energy meter via RTU using the following configuration:

{
  "proto": "rtu",
  "tty": "/dev/ttyUSB0",
  "baud_rate": 19200,

  "unit": 30,
  "registers": [
    { "name": "firmware", "address":   1, "register_type": "holding", "scale": -1, "interval": "100y" },
    { "name": "energy",   "address":  28, "register_type": "holding", "type": "u32", "scale": -2, "interval": "30s" },
    { "name": "power",    "address":  38, "register_type": "holding", "scale": -2, "interval": "30s" }
  ]
}

This works fine, too. Now I'd like to add a second energy meter that is connected to the same bus. The second meter has the same model as the first one, only the unit (slave id) differs. My project includes 15 meters in total.

When I read the documentation right, modbus-mqtt supports a 1:1 relation between connection and unit. Is that correct? If so, the serial connection would have to be declared for each energy meter. I'm concerned about problems when requests for multiple units are written to the serial device without coordination.

Suggestion: Multiple units per connection

Would it be possible to allow a 1:n relation between connection and units? For example:

{
  "proto": "rtu",
  "tty": "/dev/ttyUSB0",
  "baud_rate": 19200,

  "units": [
    {
      "unit": 30,
      "registers": [
        { "name": "firmware", "address":   1, "register_type": "holding", "scale": -1, "interval": "100y" },
        { "name": "energy",   "address":  28, "register_type": "holding", "type": "u32", "scale": -2, "interval": "30s" },
        { "name": "power",    "address":  38, "register_type": "holding", "scale": -2, "interval": "30s" }
      ]
    }
    {
      "unit": 31,
      "registers": [
        { "name": "firmware", "address":   1, "register_type": "holding", "scale": -1, "interval": "100y" },
        { "name": "energy",   "address":  28, "register_type": "holding", "type": "u32", "scale": -2, "interval": "30s" },
        { "name": "power",    "address":  38, "register_type": "holding", "scale": -2, "interval": "30s" }
      ]
    }
  ]
}

With such a feature I'd be able to implement my project with modbus-mqtt. Without it, I'll probably need to look for an alternative, which I'd like to avoid.

Optional feature: Unit templates

When multiple counters of the same model are connected, their declaration would introduce redundancy. This could be avoided by a template mechanism, e.g.:

{
  "proto": "rtu",
  "tty": "/dev/ttyUSB0",
  "baud_rate": 19200,

  "unit_templates": {
    "foo": {
      "address_offset": -1,
      "register_type": "holding",
      "registers": [
        { "name": "firmware",  "address":   1, "scale": -1, "interval": "100y" }
      ]
    }
  },

  "units": [
    { "unit": 30, "template": "foo" },
    { "unit": 31, "template": "foo" }
  ]
}

Such a the template mechanism would be nice to have, but is not required to complete my project.

Unfortunately, I have no experience in Rust so I can't supply a pull request. When there's a chance to implement this feature, I'm ready to help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.