Hi very excited about the KNOT but looking at the Docs/Application examples it appears that modbus functions as an RTU to IP client/server.
So it looks like we can’t route modbus to/from MQTT/HTTPs REST ?
Thanks
Lawrence
Hi very excited about the KNOT but looking at the Docs/Application examples it appears that modbus functions as an RTU to IP client/server.
So it looks like we can’t route modbus to/from MQTT/HTTPs REST ?
Thanks
Lawrence
Can someone confirm the features around Modbus ?
It would be great to be able to listen a RTU/Modbus and publish to MQTT.
modbus works partially. read holding register only. there is no way to get a response in high-low. answers in integer only. export data to file only.
I’m thinking on switching from generic ModBus RTU->TCP gateways to the new KNOT. RouterOS can be a great tool to protect plain ModBus/TCP traffic over public channels.
Even a more compact solution when combined into one device with the RTU to TCP gateway like the KNOT.
As of today Mikrotik’s new Atlassian Confluance based documetation doesn’t list ModBus commands under IoT just yet.
So if I understand correctly: when I want to read lets say a kWh value from an electricity meter that’s float datatype and stored in at least two 16bit registers,
I get the output as two separate 16bit registers (on the RouterOS terminal at least). The only thing I can set is the count of registers to read and data endianness is fixed.
To get over this I might have to do some background magic on the data processing side of things to combine those into one value in the correct order.
So you say there’s no option to set register type like with mbpoll (mbpoll … -t 3:float)?
Is this the case with doing a remote client query over IP too when the KNOT is a ModBus/TCP server ?
Is this mechanism going to improve/evolve in the future?
I haven’t even find a way to read to registers. I got poll out
Any help pls?
Currently, MODBUS in RouterOS can display only the response/reply to function code 3 (Modbus) request (that is if Modbus “slave” device that is connected to KNOT supports “function code 3”):
https://help.mikrotik.com/docs/pages/viewpage.action?pageId=61046813#heading-ReadingMODBUSdatausingRouterOS
So as long as it can be “printed” in CLI, it can be scripted and posted via MQTT.
I understand this limitation if you are scripting within RouterOS. However, can RouterOS pass function code 4 requests/responses using the RS485-TCP bridge?
Currently I’m unable to read FC4 over TCP, but I can do FC 3. When I use other non-mikrotik gateways, I can read/pass FC 4 messages, hence my question
For the anyone looking into using the KNOT with MODBUS
Known Bug > RouterOS v6, and versions including v7.2.3 (current stable version at the time of this post) where the parity setting of > even > or > odd > continues to behave as parity=none. If your MODBUS network uses parity=> even > or > odd> , then connectivity won’t work as expected. > This issue is resolved as of v7.3beta33
Limitation > the versions of RouterOS available at the time of this post only supports Function Code 3 querying from within RouterOS. If you need other function codes you need to enable the MODBUS RS485-TCP bridge and use external software
Hope this helps.
@asiobob thanks for the quickstart, may I ask a little more detail to get point 4. working?
There are few examples in other threads. For example: http://forum.mikrotik.com/t/mqtt-script-simple-data/162494/3
Thank you it’s what I was looking for, just a few questions
what is the meaning of " as-value once " , it’s in the official docs but got no description:
https://help.mikrotik.com/docs/pages/viewpage.action?pageId=61046813
Anyway all of this should be put in a script and then system>scheduled, so the minimum time between each poll is 1 second?
is it supported only read-holding-registers or exist also a write-holding-registers?
Thanks
There is scripting “tips and tricks”, that discuss “once”:
https://wiki.mikrotik.com/wiki/Manual:Scripting_Tips_and_Tricks#Get_values_from_looped_interactive_commands_like_“monitor”
Re “as-value”, If you try from the CLI, you’ll notice that with “once” BUT NO “as-value”:
/iot modbus read-holding-registers slave-id=0x03 num-regs=0x1 reg-addr=0x0 once
… This “prints” (outputs) to the console the values.
If you remove the “once” from above command, you’ll note the it just keeps running outputting value forever. But if you need a value to continue a script, you don’t want it to keep outputting values – the “once” say “get the first value and move on to next command”.
If you add “as-value” to same command it will print nothing – that because it’s “return the value” to a variable. So the Mikrotik example tries to show both the script usage and CLI in one command (e.g. store the output to variable via “as-value”, but another command :put to output to console:
{:local output [/iot modbus read-holding-registers slave-id=0x03 num-regs=0x1 reg-addr=0x0 as-value once];:put [($output->"values")]}
So without the “once” your schedule script would run forever.
That’s the granularity of the schedule, so 1 sec.
BUT, coming back to the “once”… The read-holdiing-registers command also supports a “do=” code block which how you can continuously read the registers. Essentially instead of “once”, you can use do={ # code that uses $values } - that make the command like a “infinite while loop” with the do={} part executed per the defined interval. You can make do= “not infinite” by using a timeout=1s, and if you called MQTT inside the do loop, it remote as often as values were returned. But this approach is tricky in scheduler, since it’s managing the interval already. The do={} method is useful sometimes. But you want something continuously reading reliably to report to something like MQTT, the schedule at 1 seconds and using “once as-value” is pretty simple.
Docs say KNOT only support function 3, which is the read registers.
Just a note about point 3) official docs it report in packet structure: “Function Parameters - usually specifies register address, data length and data to be written in case the “write” function is used.”
So I was wondering if there was some optional hidden parameters, but it’s not the case.
@Amm0 many thanks for your explanations, these should be added to official docs!
https://mikrotik.com/download/changelogs/testing-release-tree
What’s new in 7.10beta5 (2023-May-09 13:38):
*) iot - added option to send Modbus function code commands directly from RouterOS (CLI only);
Meaning, that you can now script the KNOT to send function code commands to the directly connected MODBUS device, store replies as variables and send them further via MQTT or /tool fetch
We are waiting for function 04 read input register
As per the guide:
https://help.mikrotik.com/docs/pages/viewpage.action?pageId=61046813#heading-ReadingMODBUSdatausingRouterOS
the new “transceive” function allows you to send “function=x” command. In the example, we demonstrate:
/iot modbus transceive address=1 function=3 data=20000001
But change “function=4” and that will send “read input register”.
This is what I do to get modes to mqtt
# wcs, copyright © 2010. all rights reserved
# script version : 1.0
:local errors 0
#
:global gostatusXscript
:while ($gostatusXscript = "no" ) do={
:delay 5s;
}
:set gostatusXscript "no"
#
:do {
:global modbusregisters {
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=154 as-value once]";"0";"154";"grid_voltage"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=192 as-value once]";"0";"192";"grid_frequency"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=167 as-value once]";"0";"167";"grid_power"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=183 as-value once]";"0";"183";"battery_voltage"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=184 as-value once]";"0";"184";"battery_state_of_charge"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=190 as-value once]";"0";"190";"battery_power"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=175 as-value once]";"0";"175";"inverter_power"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=164 as-value once]";"0";"164";"inverter_current"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=186 as-value once]";"0";"186";"mppt_1"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=187 as-value once]";"0";"187";"mppt_2"};
{":set tmp [/iot modbus read-holding-registers slave-id=0x01 num-regs=0x1 reg-addr=172 as-value once]";"0";"172";"172?"};
}
:global tmp
#
:log info "[ starting - modbus mqtt ]"
#
:foreach register in=$modbusregisters do={
:log info ($register->3)
#
:log info (" register:")
:log info (" " . ($register->2))
#
:log info (" command:")
:log info (" " . ($register->0))
:execute ($register->0)
:delay 2.5s
#
:set ($register->1) [($tmp->"values")]
:log info (" reading:")
:log info (" " . ($register->1))
#
:log info ""
#
/iot mqtt publish broker="x x x x " topic=("feeds/thing-x/process-power-x/outgoing/sunsynk/" . $register->2) message=($register->1) qos=2
}
#
:log info "[ ending - modbus mqtt ]"
#
} on-error={
:set gostatusXscript "yes"
:log error "script failure [ modbus mqtt ]"
}
#
:log info "script completed with $errors errors"
:set gostatusXscript "yes"
:log info " "
#