Node-Red call service node

I am trying to set a value in a call service node manually or thru modbus read node but I keep getting errors. Looking for help on how this should be done?

[{"id":"9e1f0234218e75e4","type":"modbus-read","z":"d7d5eabb97697529","name":"","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"","dataType":"HoldingRegister","adr":"416384","quantity":"16","rate":"100","rateUnit":"ms","delayOnStart":false,"startDelayTime":"","server":"d698a02d97233c82","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":350,"y":260,"wires":[["fc3dea3bf17ff067","338fb32198f16c65"],["338fb32198f16c65"]]},{"id":"fc3dea3bf17ff067","type":"api-call-service","z":"d7d5eabb97697529","name":"","server":"e07a00da.95ce7","version":5,"debugenabled":false,"domain":"input_number","service":"set_value","areaId":[],"deviceId":[],"entityId":["input_number.gate_position"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":750,"y":280,"wires":[["2f618767ad4e55b1"]]},{"id":"23d14d54f3f2f4b6","type":"inject","z":"d7d5eabb97697529","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2000","payloadType":"num","x":390,"y":320,"wires":[["fc3dea3bf17ff067"]]},{"id":"2f618767ad4e55b1","type":"debug","z":"d7d5eabb97697529","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1060,"y":260,"wires":[]},{"id":"338fb32198f16c65","type":"debug","z":"d7d5eabb97697529","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":740,"y":380,"wires":[]},{"id":"d698a02d97233c82","type":"modbus-client","name":"Gate PLC","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"192.168.1.25","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":1,"commandDelay":1,"clientTimeout":1000,"reconnectOnTimeout":true,"reconnectTimeout":2000,"parallelUnitIdsAllowed":true,"showWarnings":true,"showLogs":true},{"id":"e07a00da.95ce7","type":"server","name":"Home Assistant","addon":true}]

When you ask for help, it would be helpful if you say what errors you are getting.

Try setting the data to
{“value”:"{{payload}}"}

data

Ok, I did try setting the data in the call service to: {“value”:"{{payload}}"}
but I am still getting this error:
“Call-service error. required key not provided @ data[‘value’]”

I suspect that the issue is that the msg.payload data type does not match up.

You appear to be running the Modbus Read node asking for 16 registers (quantity). I assume that the node will therefore return, in msg.payload, an array of 16 (integer) values. Even when asking for one register this node returns an array in payload.

The specific service call ‘input_number.set_value’ requires a JSON structure in the field entry for ‘data’ as valid JSON {“value”: x } where x is a number (not an array).

So, we need to move one of your array values to x. I have no idea which array element you require, however in JSONata (use the J: option for data field) it would be

{"value": payload[4]}

if you want the 5th item in the array (index from 0).

My personal preference is to use a JSONata expression, but for Mustache templates I believe the equivalent would be

{"value": "{{payload.4}}" }

where the .n suffix indexes the array as required.

NOTE that if you are using an inject node, then if you set payload to 2000 the service node can only work for “payload”, and if you want to use both Modbus node and Inject node, the inject input has to be set to a JSON array to match the (now expected) output of the Modbus Read node. In the inject node, use JSON as the input type, and something like

[1, 2, 3, 4, 500, 6, 7, 8, 9, 10]

in the data field to inject an array into msg.payload - you will have to set whichever index you are using to 2000 as required.

Hope this fixes it for you!

I only set the Modbus read node quantity to 16 registers for testing since I did not have the right address initially but its set to qty 1 now and with your help this now works!!

I also don’t need the inject node now either since the Modbus read node is working.

I do have another question, this Modbus address is actually a double integer in my click plc i am using for my gate control so when I am over 32,767 (or something) the modbus ouput is cut off. Do you happen to know how to get the full double integer number from my plc?

Good to know it works for you now.

Modbus registers are always 16 bits or a two-byte word long. What goes into them and how they are used is entirely up to the manufacturer.

You should, hopefully, find in the documentation for each register address what it holds in terms of U16 or S16 and U32 and S32, amongst other things. These mean Signed or Unsigned integers and 1 register or 2 long.

Where you have 32 bits, the first thing to find out is if they are big-endian or little-endian. This just means if the most significant (high end) word comes first (as we would write it) or last (words reversed).

If you have big-endian U32 at register address 12345, then reading 2 registers at this address gets an array of two items (address 12345 in [0] and 12346 in [1]). The lowest 16 bits can hold from 0 to 65535, so we can always get at the integer (up to this value) just by reading payload[1].
If the number goes over this value, then the next word at payload[0] contains the higher part, and

payload[0]*65536 + payload[1]

should get the two parts combined correctly. If the endian is back to front then just reverse the [0] and [1].

Signed integers are a bit more complicated. These are usually stored in a way that the very top most bit is used to indicate sign, and the entire number is stored as a 2-compliment. You can often tell if you have a negative number because, if reading them as unsigned, the values are very large!

Negative numbers for S32 are going to be 2,147,483,648 or larger, and 4,294,967,295 represents -1, so a formula for unpicking this would be

(
    $v:=payload[0]*65536 + payload[1];
    $v < 2147483648 ? $v : $v-4294967296
)

assuming that I have got my maths correct. This is JSONata, and the () allow for multiple statement lines in a block, and the $v:= is variable binding. The predicate ? true : false acts as a conditional, the result of which is either $v or the negative value we want.

As this is all rather messy (and all to easy to get wrong) I opted to add the node-red-contrib-buffer-parser node to my pallet, and use this to do the hard work. It has all the options for reading an array / buffer based on offset in the array and data type. Here is my simple setting for reading just one S32 value at the start of the array.

I am also not sure why you require the service call & input-number in HA as you could use the home-assistant WebSocket sensor node to create and update an entity from Node-RED.
The Modbus Read node is also OK on its own, but if you need several of them consider using the flex-getter equivalent nodes - they reduce potential conflict in trying to read too much at once.
I also have set the ‘empty message’ on error option and filter out messages with payload.length=0. This is a very simple way of preventing read errors (they do happen) from sending rubbish further down the flow!

Good luck with your project.

1 Like

image
Good evening, folks. I thought I had solved it, but no. I’m facing this issue where this node, which is native to HA, is having this update problem and I can’t see its nodes anymore. I’ve already restarted Node-RED and deleted it, but it always comes back, like a ghost.