Skip to content

jollyjinx/modbus-2-mqtt-bridge

Repository files navigation

modbus2mqtt - Bidirectional Modbus 2 MQTT Bridge

modbus2mqtt allows you to have any modbus capable device (Ethernet/USB/Serial) being available and manipulatable in MQTT.

It comes with json definition files for:

  • B+G E-Tech SD100-00B - Energy meter (b+ge-tech.sd100-00b.json)
  • DaheimLader Wallbox - Daheimladen Wallbox (daheimladen.json)
  • GoodWe ET 15-30 - Solar inverter (goodwe-et15-30.json)
  • Hanmatek HM310T - Laboratory power supply (hanmatek.hm310t.json)
  • Lambda Eureka series - Heatpumps (EU8L, EU13L, EU15L) (lambda.json)
  • Lambda Solartherm - Heat pump with solar thermal integration (lambda.solartherm.json)
  • Phoenix Contact - Electric vehicle charge controller (phoenix.evcharger.json)
  • SMA Sunny Boy - Solar inverter (sma.sunnyboy.json / sma.sunnyboy.all.json)
  • SMA Sunny Boy Storage - Solar inverter with battery storage (sma.sunnystore.json / sma.sunnystore.all.json)

Note: .all.json versions contain extended register sets. For SMA devices, sma2mqtt is recommended for better support.

You can easily add json definition files for your own devices. All device definitions are available in the DeviceDefinitions/ directory.

Docker Container Use

Docker images are available for both AMD64 (x86_64) and ARM64 (aarch64) architectures. These multi-architecture images are compatible with a wide range of devices, including x86 servers, Raspberry Pi, Apple Silicon Macs, and other ARM-based computers. The image can be used directly with the following command:

	docker run --name modbus2mqtt \
		ghcr.io/jollyjinx/modbus-2-mqtt-bridge:latest modbus2mqtt \
		--modbus-server lambda \
		--mqtt-servername=mqtt.local \
		--topic lambda \
		--device-description-file lambda.json

This runs modbus2mqtt for a lambda heatpump and output the values to the mqtt server topic /lambda. If there is no mqtt server specified the server named mqtt is used.

This will look like the following on MQTT Explorer or in node-red:

MQTT Explorer ScreenshotMQTT Explorer Screenshot

You can create your own docker container by using the following command:

    docker build . --file modbus2mqtt.product.dockerfile --tag modbus2mqtt
    docker run --name modbus2mqtt modbus2mqtt --modbus-server lambda --device-description-file lambda.json --topic lambda

JSON Definition Files

It's easy to setup your own modbus2mqtt definition file. A json definition file looks like this:

{
  {
    "address": 40631,                   // modbus address
    "modbustype": "holding",            // holding,coil,
    "modbusaccess": "readwrite",        // read/write/readwrite
    "valuetype": "string",              // string, ipv4address, macaddress, uint8, int8, uint16,...
    "length": 24,                       // strings need a length
    "interval": 1000,                   // interval being updated in seconds (decimal value like 0.2 possible)
                                        // interval of 0 means it will be requested only at start and then 
                                        // using mqtt retain to retain it.
    "mqtt": "visible",                  // if shown in mqtt visible/invisible/retained
    "topic": "settings/name",           // topic to post values
    "title": "Name"                     // description for gui (like node-red)
  },
  {
    "address": 30581,
    "modbustype": "holding",
    "modbusaccess": "read",
    "valuetype": "uint32",  
    "factor": 0.001,                    // factor to multiply by
    "unit": "kWh",                      // unit for gui
    "mqtt": "visible",
    "interval": 1000,
    "topic": "counter/totalusage",
    "title": "Total Yield"
  },
  {
    "address": 1,
    "modbustype": "holding",
    "modbusaccess": "read",
    "valuetype": "uint16",
    "mqtt": "visible",
    "interval": 5,
    "map": {                            // you can add mappings for values
        "0": "OFF",                     // so value will be "AUTOMATIC" if the 
        "1": "AUTOMATIK",               // raw value is 1
        "2": "MANUAL",                  // original values can then be accessed with  
        "3": "ERROR"                    // "rawValue" key in the output
    },
    "topic": "ambient/operatingstate",
    "title": "Ambient Operating State"
  },
  {
    "address": 2,
    "modbustype": "holding",
    "modbusaccess": "read",
    "valuetype": "uint16",
    "mqtt": "visible",
    "interval": 5,
    "bits": {                            // you can bit mappings for values
        "0": { "name" : "running" },     // a single bit will be mapped to a boolean
        "1-3": { "name" : "mode" },      // multiple values will be mapped to the integer number with those bits shifted to the right
        "4-15": { "name" : "reserved" }  
    },
    "topic": "test/state",
    "title": "Test State"
  }
}

Remark: Be aware that json does not support comments like in this example. After creating your own json definition, you can use it with the commandline option --device-description-file yourfilename

Bridge goes both ways

modbus2mqtt is a bridge it does not only allow modbus devices show up in mqtt, it also allows writing values to the modbus devices from mqtt. It uses a Request/Response pattern. You send a mqtt request to the mqtt request topic and are given the result of the request in the response topic path.

To set the output voltage of the HM310T to 14.04 Volt you can send the following json

{
  "value": 14.04,
  "date": "2022-10-21T08:43:22Z",
  "topic": "set/voltage",
  "id": "D2129DBF-9F94-46D7-86BC-4A07152FF1D8"
}

to the topic hm310/request/mychange of the MQTT server. The bridge will pickup the request and return the response to the response topic hm310/response/mychange . Be aware that you set the value 14.04 which will be correctly converted to the (u)int16 value of the specific modbus address.

In swift Request/Responses are defined as follows.

struct MQTTRequest:Encodable,Decodable,Hashable,Equatable
{
    let date:Date       // needs to be within request ttl time 
    let id:UUID
    let topic:String
    let value:MQTTCommandValue
}

struct MQTTResponse:Encodable,Decodable
{
    let date:Date
    let id:UUID         // same uuid of request
    let success:Bool
    let error:String?
}

Status

I'm using it 24/7 on my own modbus devices (lambda, B+G E-Tech SD100-00B, phoenix-charger, Hanmatek HM310T).

Starting the application

> modbus2mqtt --topic=sma/sunnystore \
              --modbus-server=sunnyboy.local \
              --mqtt-servername=mqtt.local \
              --device-description-file=sma.sunnystore.json

It supports command line help:

> ./.build/release/modbus2mqtt --help 
USAGE: modbus2mqtt <options>

OPTIONS:
  --log-level <log-level> Set the log level. (default: notice)
  --mqtt-servername <mqtt-servername>
                          MQTT Server hostname (default: mqtt)
  --mqtt-port <mqtt-port> MQTT Server port (default: 1883)
  --mqtt-username <mqtt-username>
                          MQTT Server username
  --mqtt-password <mqtt-password>
                          MQTT Server password
  --emit-interval <emit-interval>
                          Minimum interval to send updates to mqtt Server.
                          (default: 0.1)
  -t, --topic <topic>     MQTT Server topic. (default: modbus/sunnyboy)
  --mqtt-request-ttl <mqtt-request-ttl>
                          Maximum time a mqttRequest can lie in the future/past
                          to be accepted. (default: 10.0)
  --mqtt-auto-retain-time <mqtt-auto-retain-time>
                          If mqttTopic has a refreshtime larger than this value
                          it will be retained. (default: 10.0)
  --modbus-device-path <modbus-device-path>
                          Serial Modbus Device path
  --modbus-serial-speed <modbus-serial-speed>
                          Serial Modbus Speed (default: 9600)
  -m, --modbus-server <modbus-server>
                          Modbus Device Servername. (default:
                          modbus.example.com)
  --modbus-port <modbus-port>
                          Modbus Device Port number. (default: 502)
  --modbus-address <modbus-address>
                          Modbus Device Address. (default: 3)
  --device-description-file <device-description-file>
                          Modbus Device Description file (JSON). (default:
                          sma.sunnyboy.json)
  --device-reset-url <device-reset-url>
                          Device Reset URL (HTTP GET) - called when
                          communication fails repeatedly.
  -h, --help              Show help information.

Device Reset for Unreliable Connections

Some Modbus devices may stop responding due to firmware issues, network problems, or hardware glitches. The --device-reset-url option provides an automatic recovery mechanism.

How it works:

  • modbus2mqtt tracks communication errors with an error counter
  • After 7 consecutive failures, it calls the specified reset URL (HTTP GET request)
  • The URL typically triggers a device reboot (e.g., via a smart power switch, relay, or device management interface)
  • After calling the reset URL, it waits 60 seconds for the device to reboot
  • Communication attempts resume automatically
  • If errors continue beyond 10 failures, the current communication session is restarted after a short delay instead of exiting the application

Example use cases:

# Reset via Tasmota/Sonoff power switch
modbus2mqtt --modbus-server=mydevice.local \
            --device-reset-url="http://switch.local/cm?cmnd=Power%20Toggle"

# Reset via Shelly relay (double toggle for reboot)
modbus2mqtt --modbus-server=mydevice.local \
            --device-reset-url="http://192.168.1.50/relay/0?turn=off"

# Reset via custom REST API
modbus2mqtt --modbus-server=mydevice.local \
            --device-reset-url="http://192.168.1.100/api/reset"

This feature significantly improves reliability for devices with known stability issues.

Feedback welcome

In case you add json definitions for your own devices, create pull requests.

About

Bidirectional Modbus 2 MQTT Bridge with docker container and ready to use json definitions for lambda heatpumps, sma & goodwe inverters, phoenix contact, hm310,...

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors