Modbus openHAB2 binding available for alpha testing

I’m so sorry – I should have checked the code before commenting. I was sure I implemented it otherwise.

You are right, currently both read and write are with relative address.

You have good point regarding entity numbers, perhaps we need to require the user to understand the minimum basics of modbus addressing.


I have logging on debug and there’s a lot of lines regarding transformations.

To me it seems wasted processing time.

Yeah there is some additional work going on currently, something I might optimize later once the thing structure and other things “stabilize”. Due to the nature of channels in openhab2, it might be hard though. I should probably have logic not to process the channel transformation on read if the channel is not linked in neither read and readwrite things.

For example is it possible to combine connection and poller definitions as one thing?

I have been thinking this as well but it does not really reduce the number of things considerably. I think typically people have only single connection?

And according to thing configuration it would create channels?

I have not found other way except to provide “all” channels (dimmer, number, switch, contact, etc.), such that user can link it to the item of matching type. From modbus configuration alone it is impossible to say which types are necessary?

Naturally we could decide that only Number items are supported (-> only one channel) but this would mean that user needs to introduce additional rules and proxy items to convert the number to Contact, for example. I have been trying to avoid additional rules & proxy items for the typical use cases.

Alternative idea for the thing structure

I have alternative proposal that perhaps could solve the “too many things” issue.

First a bit of history: currently the thing structure is quite one-to-one mapping with openhab1 binding.

For example, the extended item configuration string format in the old binding maps to single readwrite, with read and write things as children. The read things are < definitions in the old binding, Analogously, write things match to > definitions.

Since we want to support multiple < (read) or > (write) definitions (to support e.g. the Rollershutter example above) it was natural to have multiple things in the new binding as well. Having separate things has the benefit of not introducing a new “mini-language” for configuration strings like in openHAB1. Initially for me the < and > looked a bit cryptic initially but I decided to go with those in the old binding as other bindings used similar syntax as well (http, mqtt).

Instead of thinking how it was with the old binding, I have been trying to think the common (?) use cases with the modbus binding, and use that information as basis of designing the binding from scratch.

Known use cases

  1. Same read/write address, same value type (e.g. reading/writing integer to coil/holding register) – the most common use case for sure
  2. “Set coil to true on any command”, writing to different address than reading, write transformed differently than read. example in old binding
  3. read & write different addresses. In new binding must point to same endpoint, though.
  4. Rollershutter example, mix-match of other use cases
  5. converting different commands to different register values (ON=256, OFF=512)
  6. writing an openHAB command to multiple registers (e.g. turning many devices off)
  7. inverting values on read/write

(anything else?)

As you were already thinking, my proposal is to have single “readwrite” thing (EDIT: this is now called data thing in the latest version. Also check the docs for descriptions of parameters), with the following configuration parameters

  • readAddress Can be empty for write-only.
  • readTransform
  • readValueType
  • writeType
  • writeAddress (absolute address, with writeType=holding, writeAddress=0 would refer to holding register #40001 for example). Keep empty for read-only.
  • writeTransform
  • writeValueType
  • writeMultipleEvenWithSingleRegister

Essentially you would have the current thing configurations from read and write things, with the exception of missing trigger. Transformation would take the role of trigger.

I realized that the need to have multiple read (< in openhab1) or write (> in openhab2) can be worked around by using transformations. You can branch the logic based on incoming command, same as with trigger parameter in openHAB1. This would not solve the issue of writing to different register/coil based on command (e.g. roller shutter example). I have proposal below to solve that using “general purpose write”.

This is how the above use cases would be solved

  1. readAddress equals writeAddress, readValueType equals writeValueType
  2. transformation returning always 1
  3. just configure read index and write index
  4. more advanced case – would be solved by “mini binding” or “general purpose write” approaches
  5. solvable e.g. using JS transformation and switch-case
  6. more advanced case – would be solved by “general purpose write” approaches
  7. solvable by JS transformation

general purpose write For complex scenarios we could allow complex output from transformation.
We could represent the raw modbus writes using the JSON syntax:
[ {... write instruction ...}, {... write instruction ...}, ... ]

where each {... write instruction ...} is a JSON object describing a modbus write request.

For example, if the transformation returns the following JSON

[
   {"functionCode":5, "index":0, "value":1}, 
   {"functionCode":6, "index":0, "value":256},
   {"functionCode":16, "index":1, "value":[512, 256]} 
]

EDIT: the JSON keys turned slightly different in the final implementation: functionCode, address, and value.

the binding would execute following modbus write requests

  1. set coil 0 to on, using FC5 (write single coil)
  2. set holding register 0 to 256, using FC6 (write single holding register)
  3. set holding register 1 and 2 to 16-bit values 512, 256, respectively, using FC16 (write multiple holding registers), in a single modbus request.

One can also suppress all writes using empty json list [].

We SHOULD support simple transformations as well (outputting just value), and leave the JSON syntax for advanced cases only (assuming the advanced cases are much more rare).

mini bindings Provide special use-cases as mini bindings

For example, we could introduce rollershutter binding (built on top modbus binding) which would introduce rollershutter-readwrite thing with the following configuration

  • up_down_write_index : in what (coil/holding) index to write UP and DOWN commands
  • up_down_type : coil or holding. What FC is used to write UP/DOWN
  • up_value: value to write with UP, e.g. 1
  • down_value: value to write with DOWN, e.g. -1
  • move_stop_write_index : in what (coil/holding) index to write MOVE and STOP commands
  • move_stop_write_type : coil or holding. What FC is used to write MOVE/STOP
  • move_value: value to write with UP, e.g. 1
  • stop_value: value to write with DOWN, e.g. 0
  • position_index : from what index position is read

There are other ways to encode rollershutter using modbus. These different versions can be incorporated in the binding as the need arises.

Note that the kind of rollershutter mini binding could be implemented also with “general purpose write” described above.

more examples

“Simple case” (use case 0)

Bridge modbus:tcp:endpointTCP [ host="192.168.1.100", port=502, id=2 ] {
    Bridge poller coils [ start=2, length=4, refresh=5000, type="coil" ] {
        Bridge readwrite DO2 { 
            Thing read readTCP [ start=0, valueType="bit" ]
            Thing write writeTCP [ start=0, valueType="bit" ]
        }
    	Bridge readwrite DO3 { 
            Thing read readTCP [ start=1, valueType="bit" ]
            Thing write writeTCP [ start=1, valueType="bit" ]
        }
    }
	Bridge poller holding [ start=0, length=5, refresh=5000, type="holding" ] {
        Bridge readwrite modbusMakuuhuone4 { 
            Thing read mh4 [ start=0, transform="default", trigger="*", valueType="uint16" ]
        	}
        Bridge readwrite modbusPukuhuone {
        	Thing read ph [ start=2, transform="default", trigger="*", valueType="uint16" ]
        	}
        Bridge readwrite modbusMakuuhuone1 {
        	Thing read mh1 [ start=4, transform="default", trigger="*", valueType="uint16" ]
        
        }
    }
}

would transform to roughly something like below

Bridge modbus:tcp:endpointTCP [ host="192.168.1.100", port=502, id=2 ] {
    Bridge poller coils [ start=2, length=4, refresh=5000, type="coil" ] {
        Bridge readwrite DO2 [ readAddress=2, readValueType="bit", readType="coil", writeAddress=2, writeValueType="bit", writeType="coil" ]
        Bridge readwrite DO3 [ readAddress=3, readValueType="bit", readType="coil", writeAddress=3, writeValueType="bit", writeType="coil" ]
    }
	Bridge poller holding [ start=0, length=5, refresh=5000, type="holding" ] {
        Bridge readwrite modbusMakuuhuone4 [ readAddress=0, readValueType="uint16" ]
        Bridge readwrite modbusPukuhuone [ readAddress=2, readValueType="uint16" ]
        Bridge readwrite modbusMakuuhuone1 [ readAddress=4, readValueType="uint16" ]
    }
}

writing an openHAB command to multiple registers (use case 5)

In openhab1 binding

Switch Light "Roller1" (ALL) 
{modbus=">[slave2:0:trigger=UP,transformation=256],>[slave2:0:trigger=STOP,transformation=512],>[slave2:0:trigger=DOWN,transformation=512],>[slave2:1:trigger=UP,transformation=512],>[slave2:1:trigger=STOP,transformation=512],>[slave2:1:trigger=DOWN,transformation=256]"}

(plus some modbus.cfg)

With the new proposed config:

Bridge modbus:tcp:endpointTCP [ host="192.168.1.100", port=502, id=2 ] {
    Bridge poller coils [ start=0, length=4, refresh=0, type="holding" ] { // refresh=0 -> no polling
        Bridge readwrite Roller1 [ writeTransform="JS(myroller.js)", writeValueType="uint", writeType="holding" ]
    }
}

with the following transformation myroller.js (assuming we are writing holding registers)

// Wrap everything in a function
(function(cmd) {
	var cmdToValue = {"UP":256, "STOP":512, "DOWN":512, "UP":512, "STOP":512, "DOWN":256};
	var cmdToAddress = {"UP":0, "STOP":0, "DOWN":0, "UP":1, "STOP":1, "DOWN":1};

	var value = cmdToValue[cmd];
	var address = cmdToAddress[cmd];
	if(value === undefined || address === undefined) {
		// unknown command, do not write anything
		return "[]";
	} else {
		return [
			"[",
	   			"{\"functionCode\":6,  \"index\":" + address.toString() + ", \"value\":" + value +  "}",
			"]",
		].join("\n")
	}
})(input)
// input variable contains data passed by openhab

The transformation transforms UP to

[ {"functionCode":6,  "index":1, "value":512} ]

and DOWN to

[ {"functionCode":6,  "index":1, "value":256} ]

Hopefully this makes sense, it’s always a bit hard to put word to early ideas. I think the above approach would be much more streamlined way of configuring the binding for simple and typical use cases but still allow enough flexibility for some of the “harder” cases.

We could also get rid of the child->parent thing interaction (e.g. read things affect channels of readwrite things), which has felt like a wrong design choice. It seems somehow counterintuitive.

Best
Sami