MQTT Modbus to Thingsboard

Hello,

Is it possible to modify the transmission format in MQTT Modbus Gateway on a RUT956? For example, a script that reads data from MQTT like:

{ "Device": "RUT", "Relay": 1 }

or:

"RUT": { "Relay": 1 }

or:

{ "RUT_Relay": 1 }

…and then the script would translate it depending on what’s set in the Modbus client or into something like:

{
  "cookie": 65432,
  "type": 0,
  "host": "192.168.1.1",
  "port": 502,
  "timeout": 1,
  "server_id": 1,
  "function": 6,
  "register_number": 203,
  "value": 0
}

I have no idea where to start or any example to go by. I’m trying to fully integrate it into ThingsBoard and it seems very complicated, even though it’s an extremely good device from many points of view. But I’m forced to give up on it because I can’t manage to write Modbus registers over MQTT in a simpler, scalable way. It seems bad idea to me to use a Raspberry Pi just to act as a gateway for ThingsBoard.

Thanks for help!
Have a nice day,

Hello,

Could you please clarify your intended workflow a bit further, especially regarding why you need to read data from MQTT? Is the goal to retrieve the current relay state via MQTT messages and then act upon it?

If so, this could actually be handled locally using a simple script. For example, you can query the relay state via CLI with:

ubus call ioman.relay.relay0 status

and then, based on the returned value, publish an MQTT message using mosquitto_pub in your preferred JSON structure.

Additionally, here are some relevant topics that might be helpful or bring insights:

If this is not the intended setup you’re looking for, could you clarify it a bit more? Thank you.

Best regards,

I expressed myself wrong.
Let’s talk about the MQTT and modbus protocols:
So I can receive data in thingsboard very well but sending it is very complicated from the point of view of the thingsboard app (we are talking about Thingsboard CE). I add Teltonika as a “Device” not as a “gateway” (I need docker for this). But I would like to be able to customize the message that arrives at teltonika (RX) just as I can customize the message that leaves teltonika (TX) via Data to server.
For example, I can build the following message (the interface gives you the possibility to modify the message structure) with data from the modbus protocol from a device called RUT.

 RUT= { "Uptime": 306675, "RSS": -59, "serial": "cfg59765456", "data": "etc", "RelayStatus":1}

I want to be able to do the same thing on the message that comes on mqtt. That is, when I want to set an output in the modubus protocol. For example:

RUT= {"setRelay1": 0}

at this moment I have to send through the Modbus Gateway like this:

RUT = {
"cookie": 65432,
"type": 0,
"host": "192.168.1.1",
"port": 502,
"timeout": 1,
"server_id": 1,
"function": 6,
"register_number": 203,
"value": 0
}

Long story short I want to send on MQTT
RUT = { "Relay": 0}
and the router to actuate the relay

I hope I understand better now

Thank you very much
Have a nice day

Hello,

Apologies for the delay in getting back to you.

I’ve forwarded this specific inquiry to our development team to check whether this type of MQTT Modbus relay control setup with customizable incoming message formats, as part of your ThingsBoard integration, would be possible to achieve on the RUT956.

I’ll get back to you as soon as there’s any feedback or suggestions from our R&D side regarding this.

Thank you very much for your patience and understanding.

Best regards,

Hello,

I’ve checked this with our development team, and I’d like to share some insights. Currently, the Modbus MQTT Gateway service supports only two predefined data formats, and unfortunately, it isn’t natively adjustable through the WebUI in the way you described for customizing RX messages.

However, there’s a workaround solution developed by our programmer available by implementing a custom Lua service acting as a bridge between ThingsBoard and the device’s Modbus MQTT gateway. This Lua script essentially creates an additional MQTT client that listens for RPC requests from ThingsBoard, converts them into a format compatible with the Modbus MQTT Gateway, and forwards the response back to ThingsBoard.

Below you’ll find the full implementation steps for setting this up:


:pushpin: Installation & Setup

:one: Install required Lua modules:

Download and upload the following .ipk packages to your device using SCP, FTP, or any preferred method:

  • luamqtt.ipk
  • luabitop_1.0.2-1_mipsel_24kc.ipk
    (Note: luabitop modules needs to be compiled (unlike luamqtt is pure lua, thus it should work on any arch) I compiled for TRB2M, but any other arch can be compiled using SDK if you have other devices. Simply select luabitop lib in make menuconfig and take package from bin/ folder from SDK root.)

:two: Install the packages:

Run:

opkg install /tmp/luabitop_1.0.2-1_<arch>.ipk
opkg install /tmp/luamqtt.ipk

:three: Create and configure the Lua bridge script

touch /root/mqtt-modbus-gateway-adapter.lua
vim /root/mqtt-modbus-gateway-adapter.lua

Paste the following Lua code into the file (press i in vim to insert):


local HOSTNAME = "192.168.1.10"
local CLOUD_HOSTNAME = "eu.thingsboard.cloud"
local REQUEST_TOPIC = "v1/devices/me/rpc/request/+"
local REQUEST_TOPIC_CONVERTED = "request"
local RESPONSE_TOPIC = "response"
local RESPONSE_TOPIC_CONVERTED = "v1/devices/me/rpc/response/"
local TOKEN = "NyqmjbQdOGHsLPwHIn6V"

-- load mqtt and other modules
local mqtt = require("mqtt")
local json = require("luci.jsonc")
local util = require("vuci.util")

-- create mqtt client
-- this client will connect to Thingsboard cloud platform
local client = mqtt.client{
	uri = CLOUD_HOSTNAME,
	username = TOKEN,
	clean = true,
}

-- Thingsboard cloud platform trial account accepts only one client connections
-- so I decided to create another client that will connect to local broker
-- where MQTT Modbus Gateway is connected
-- should be done with one broker only with different topics
local device = mqtt.client{
	uri = HOSTNAME,
	clean = true,
}

print("created MQTT client", client)

client:on{
	connect = function(connack)
		if connack.rc ~= 0 then
			print("connection to broker failed:", connack:reason_string(), connack)
			return
		end
		print("connected:", connack) -- successful connection

		-- subscribe to test topic and publish message after it
		assert(client:subscribe{ topic=REQUEST_TOPIC, qos=1, callback=function(suback)
			print("subscribed:", suback)
		end})
	end,

	message = function(msg)
		assert(client:acknowledge(msg))
		print("received:", msg)
		request_id = string.sub(msg.topic,
			string.len(RESPONSE_TOPIC_CONVERTED),
			string.len(msg.topic))
		print("request_id:", request_id)

		local payload = json.parse(msg.payload)
		util.dumptable(payload)
		if payload.method == "setValue" then
			local response_payload = payload.params
			print("response_payload:", response_payload)
			assert(device:publish{
				topic = REQUEST_TOPIC_CONVERTED,
				payload = response_payload,
				qos = 1
			})
		end
	end,

	error = function(err)
		print("MQTT device client error:", err)
	end,
}

device:on{
	connect = function(connack)
		if connack.rc ~= 0 then
			print("connection to broker failed:", connack:reason_string(), connack)
			return
		end
		print("connected to device:", connack) -- successful connection

		assert(device:subscribe{ topic=RESPONSE_TOPIC, qos=1, callback=function(suback)
			print("subscribed:", suback)
		end})
	end,

	message = function(msg)
		assert(device:acknowledge(msg))
		print("received from device:", msg)
		response_topic = RESPONSE_TOPIC_CONVERTED .. request_id
		print("response_topic:", response_topic)

		assert(client:publish{
			topic = response_topic,
			payload = msg.payload,
			qos = 1
		})
	end,

	error = function(err)
		print("MQTT device client error:", err)
	end,
}

mqtt.run_ioloop(client, device)

Customize the MQTT broker addresses, topics, and authentication tokens as per your ThingsBoard and Teltonika device configuration.


:four: Create an init service script:

Create a new init script:

touch /etc/init.d/mqtt-modbus-gateway-adapter
vim /etc/init.d/mqtt-modbus-gateway-adapter
chmod 755 /etc/init.d/mqtt-modbus-gateway-adapter

Paste the following content:

#!/bin/sh /etc/rc.common

USE_PROCD=1
START=99
STOP=01

start_service() {
  procd_open_instance
  procd_set_param command /usr/bin/lua /root/mqtt-modbus-gateway-adapter.lua
  procd_set_param respawn ${respawn_threshold:-0} ${respawn_timeout:-6} ${respawn_retry:-0}
  procd_close_instance
}

:five: Enable and start the service:

service mqtt-modbus-gateway-adapter enable
service mqtt-modbus-gateway-adapter start

Now the MQTT Modbus gateway should be reachable from a cloud. Additionally, you can create a separate service to directly handle Modbus commands using Lua libraries like lua-libmodbus or via ubus Modbus methods, if needed.


I hope this solution will help you achieve the integration flow you were aiming for. Should you have any further questions or need assistance, feel free to reach out.

Best regards,

Hi,
to extend this example, I would like to call modbus directly via separate service.
When I create a simple demo-rtu Lua script with lua-libmodbus like

local dev = mb.new_rtu(“/dev/rs485”, 9600, “none”, 8, 1)
dev:set_debug()
ok, err = dev:connect()

if not ok then error("Couldn’t connect: " .. err) end

I get error

Opening /dev/rs485 at 9600 bauds (N, 8, 1)
lua: /usr/local/bin/s.lua:200: Couldn’t connect: No such file or directory`

Any clue what’s wrong, why I can’t connect to rs485 ?
I have disabled modbus service to be sure that device is not blocked.

Regards,
Michal

This topic was automatically closed after 60 days. New replies are no longer allowed.