Modbus - Writing uint8 to a device

Hi,

I have just installed Itho Daalderop HRU400 ventilation unit and I am trying to connect it to via Modbus.
So far I can read all the values, but since most of the registers are unsigned 8 bit integer I have problems writing them (and therefore changing the fan air volume).

Among the other register configuration I am now testing writing the following registers used to change the fan level:

    Bridge poller Ventilation7 [ start=2000, length=2, refresh=600, type="holding" ] {
        Thing data     Werkelijk_ventilatieniveau     "Werkelijk ventilatieniveau "     [ readStart="2000.0", readValueType="uint8",     writeStart="2000.0", writeType="holding",     writeValueType="bit"] 
        Thing data     42001_inschakelen_uitschakelen     "42001 inschakelen/uitschakelen"     [ readStart="2001.0", readValueType="uint8",     writeStart="2001.0", writeType="holding",     writeValueType="bit"]
    }

Reading goes well, but when I try to write I get invalid function:

2022-02-20 13:44:08.408 [WARN ] [rt.modbus.internal.ModbusManagerImpl] - Try 1 out of 3 failed when executing request (ModbusWriteRegisterRequestBlueprint [slaveId=1, reference=2000, registers=ModbusRegisterArray(0001), maxTries=3, getFunctionCode()=WRITE_SINGLE_REGISTER]). Will try again soon. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 1 [operation ID 923ee963-7293-43b4-9414-ca6fd3c3665b]

2022-02-20 13:44:08.548 [WARN ] [rt.modbus.internal.ModbusManagerImpl] - Try 2 out of 3 failed when executing request (ModbusWriteRegisterRequestBlueprint [slaveId=1, reference=2000, registers=ModbusRegisterArray(0001), maxTries=3, getFunctionCode()=WRITE_SINGLE_REGISTER]). Will try again soon. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 1 [operation ID 923ee963-7293-43b4-9414-ca6fd3c3665b]

2022-02-20 13:44:08.692 [ERROR] [rt.modbus.internal.ModbusManagerImpl] - Last try 3 failed when executing request (ModbusWriteRegisterRequestBlueprint [slaveId=1, reference=2000, registers=ModbusRegisterArray(0001), maxTries=3, getFunctionCode()=WRITE_SINGLE_REGISTER]). Aborting. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 1 [operation ID 923ee963-7293-43b4-9414-ca6fd3c3665b]

2022-02-20 13:44:08.695 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'modbus:data:Ventilation:Ventilation7:Werkelijk_ventilatieniveau' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (ModbusSlaveErrorResponseExceptionImpl) with write. Request: ModbusWriteRegisterRequestBlueprint [slaveId=1, reference=2000, registers=ModbusRegisterArray(0001), maxTries=3, getFunctionCode()=WRITE_SINGLE_REGISTER]. Description: ModbusSlaveErrorResponseException(error=1). Message: Slave responded with error=1 (ILLEGAL_FUNCTION)

2022-02-20 13:44:09.344 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'modbus:data:Ventilation:Ventilation7:Werkelijk_ventilatieniveau' changed from OFFLINE (COMMUNICATION_ERROR): Error (ModbusSlaveErrorResponseExceptionImpl) with write. Request: ModbusWriteRegisterRequestBlueprint [slaveId=1, reference=2000, registers=ModbusRegisterArray(0001), maxTries=3, getFunctionCode()=WRITE_SINGLE_REGISTER]. Description: ModbusSlaveErrorResponseException(error=1). Message: Slave responded with error=1 (ILLEGAL_FUNCTION) to ONLINE

When I use external modbus simulator I can write without problems:

Any ideas?
How can I enable the modbus logging in the console? I want to see what the binding is sending to the device, similar to above screenshot.

I tried this and get below result

openhab> log:set DEBUG org.openhab.binding.modbus
Error executing command: Unable to set level for logger

And last question: My understanding of the Modbus binding is to create a thing for each polling block, is that right?

Thanks

No one knows if I can see the Modbus message logging via OH log?
I tried to simulate the Modbus server on my laptop to see what kind of messages the OH Modbus binding is sending, but I don’t have succes so far. (Will try again in the weekend)

Hello maybe take a look at my post and the response from rossko57 because from what i can see your problem is also writing a holding bit

https://community.openhab.org/t/schneider-smartlink-modbus/132064/6?u=stamate_viorel

also looking at the manual it uses the same standard as schneider this is copy from the unit manual

All registers are holding registers. This
are specified in the tables Modbus RS-485: Table 1:
Holding registers on page 31 and Modbus RS-485: Table 2:
Reading-Writing registers on page 32 .
When register 40001 to 40003 (configuration) are
changed the unit will reply to the master with the old one
configuration. Only when the device is rebooted
(by removing the power and putting it back on, or by
using register 40500) the unit will switch to
the new configuration.
Register writing must be done with function code 16.
Reading registers should be done with function code 3.
When transmitting or receiving the Modbus registers,
of the register numbers as stated in the tables always 40001
subtracted (using the original method of
Schneider Electric). Modbus register 40014 thus becomes register 13
(40014-40001=13).
used abbreviations
Bit Description Min value Max value
0x00 Unsigned 8 bit 0 255
0x10 Unsigned 16 bit 0 65535
0x91 Signed 16 bit/10 -3276.8 3276.7
Bridge poller Ventilation7 [ start=2000, length=4, refresh=600, type="holding" ] {
        
        Thing data     42001_inschakelen_uitschakelen     "42001 inschakelen/uitschakelen"     [ readStart="2000", readValueType="uint8", readTransform="JS:dimread255.js",     writeStart="2000", writeType="holding",     writeValueType="uint8", writeTransform="JS:dimwrite255.js"]
    }

transform/dimwrite255.js

// variable "input" contains command string passed by openHAB
(function(inputData) {
    // here set the 100% equivalent register value
    var MAX_SCALE = 25;
    var out = 1
    if (inputData == 'ON') {
          // set max
         out = MAX_SCALE
    } else if (inputData == 'OFF') {
         out = 0
    } else {
         // scale from percent
         out = Math.round( parseFloat(inputData, 10) * MAX_SCALE / 10 )
    }
    return out
})(input)

transform/dimread255.js

// Wrap everything in a function (no global variable pollution)
// variable "input" contains data string passed by binding
(function(inputData) {
    // here set the 100% equivalent register value
    var MAX_SCALE = 25;
    // convert to percent
    return Math.round( parseFloat(inputData, 10) * 10 / MAX_SCALE );
})(input)

test this

It’s always good to have a working example before trying to configure openHAB :smiley:
There you use FC16 (hex 10) as the write instruction - “write multiple registers”

But openhab is using

so the consequence is that the target device responds with

It’s telling you what is wrong at this point.

Use the writeMultipleEvenWithSingleRegisterOrCoil option in your data Things

1 Like

Hi

Yes, I have seen that I need to use FC16 in the documentation after my first post and tried to test the writeMultipleEvenWithSingleRegisterOrCoil already. I don’t get the Modbus error anymore, but also nothing is written.
I even don’ see any modbus related logs in openhab, so I am not sure if something is really going out to the device.
That is why I wanted to simulate it and see if OH is sending something.

Thanks, I will

Now i saw that you need to subtract in order to have the correct register so you need register 2000

OK, I connected Modbus simulator directly to the rPi and I was able to see that there was problem with my item names that started with numbers and nothing was coming into the comms monitor of the simulator. So I changed the items and the thing to start with the letter and I tried the above configuration

I made test rule just writing 1 to both registers:

 i42001_inschakelen_uitschakelen.sendCommand(1)
 Werkelijk_ventilatieniveau.sendCommand(1)

After this I was able to see the data coming to the simulator, however it was incorrect, for the first item I could see value 00 01, but for the second it was 00 11:

When I changed the write thing config to uint16, I got the message that I have seen successfully writing as in my first message:

 Bridge poller Ventilation7 [ start=2000, length=2, refresh=1000000, type="holding" ] {
        Thing data     Werkelijk_ventilatieniveau     "Werkelijk ventilatieniveau "     [ readStart="2000.0", readValueType="uint8",     writeStart="2000", writeType="holding",     writeValueType="uint16", writeMultipleEvenWithSingleRegisterOrCoil=true] 
        Thing data     t42001_inschakelen_uitschakelen     "42001 inschakelen/uitschakelen"     [ readStart="2001.0", readValueType="uint8",     writeStart="2001", writeType="holding",     writeValueType="uint16", writeMultipleEvenWithSingleRegisterOrCoil=true]
    }

Getting late here, so tomorrow I am going to try with the vent unit

Yes, this is not permitted (but poorly validated)

Bear in mind writing individual bits to a 16-bit register is not possible over Modbus.
We simulate that by setting/unsetting the target bit in a 16-bit image, and writing the whole 16-bit image.
What should we do about the non-targeted bits? There is no rule to say “set them all”, or “unset them all”.
The binding does its best to keep a cached image of the whole register and only do what you asked it to do - set/unset the target bit, and leave the other bits as they are.

That seems to be working as designed:
write bit 0, get 0000 0001
next
write bit 1, get 0000 0011
because we’ve maintained the ‘old’ bit 0.

If you must clear all the other bits, then do so - by writing a full 16-bit value to the complete register in the ordinary holding register way i.e. write “1” or “2” to int16 address 2000

Having had a look at the device manual, I think you are tying yourself in knots using exotic binding features.
So far as I can see, you only need to read/write holding registers as int16, just expect values <255 for the registers designated “8-bit”.

1 Like

I don’t understand how this is working. The binding manual says that I need to configure uint8 as bit when writing, and that was what I was doing. I was sending integer value 1 to register 2000 (or 2000.0 meaning the lower byte of the register) but the value written was 3.

Yes, you’re right. Thanks for the support

This does not make sense as “uint8” and “bit” are both value types used on read/write. You cannot have both active at the same time.

Can you point out the section in documentation so we can remove the incorrect statement?

My fault, there is no statement that the uint8 should be configured as bit in the documentation.
Only that the register has to be written in from x.y (where y is 0 or 1, pointing to the higher or lower byte). link
However if the write thing is configured as uint8 with register x.y, there is error shown in the thing that the register should be bit and the thing is set to offline (see screenshots below):

Thing modbus:data:Ventilation:Ventilation777:Werkelijk_ventilatieniveau invalid writeType, writeValueType or parent. Since writeStart=X.Y, one should set writeType=holding, writeValueType=bit and have the thing as child of poller

I cannot now open the docs link (on mobile). But yes, reading hi/lo byte of register is supported.

However, writing to individual bytes of a register is not implemented (although it could be, to function exactly like you have explained above). Error message is pointing out the illegal combination of parameters - with X.Y write address format, only bit write is supported.

Would you have any advice how to make the error message more clear?

I wanted to write uint8, not individual bits. I didn’t need that functionality for OH yet (but I have used it for my work, although it is almost 10yrs since I worked with Modbus last time).

Regarding the message, maybe advise that uint8 could be written as uint16 would be better than the current message.
If one wants to write to the higher byte then that might be done by doing some binary math

Yes, I know. It is not implemented. There were clear demand for the individual bits, but not so much for bytes and thus only bits were implemented.

I am not sure it is a good advice to refer to uint16, it is quite different in operation really as the high byte will be always zero ( with small positive integers that fit 1 byte). I am happy that in your case you are really after writing the full register value, and the current functionality is enough.

Yes, workarounds exist using rules and keeping the register cache there.