MODBUS - Write boolean to Word

Hi,

I'd like to write throgh a modbus connection a boolean into the corresponding bit contained in a WORD in my PLC, but I can't write the single bit to the FC Holding register. I use node-red-contrib-modbus.

When I read the single bit, I read the whole WORD and split it into 16 bits, with node-red-contrib-bit. I looked for a similar node to assemble 16 bits into a single word, but I couldn't find it.

Anybody can help?

Thanks in advance

Hello Francesco,

Can you send us some more information on your flow (maybe paste a sample of your flow)
Im interested also in Modbus and have the node-red-contrib-modbus set of nodes installed.

For my simulation i used a Modbus Flex Write for a single value write

Flow example :

[{"id":"132dec09.2c9ce4","type":"modbus-flex-write","z":"a311c276.8fe21","name":"","showStatusActivities":false,"showErrors":false,"server":"28c906aa.79aae2","emptyMsgOnFail":false,"keepMsgProperties":false,"x":650,"y":1660,"wires":[["3be8578.b5476a8"],[]]},{"id":"4826e5f9.3b98ec","type":"inject","z":"a311c276.8fe21","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":230,"y":1620,"wires":[["68d6e00d.3536e8"]]},{"id":"3be8578.b5476a8","type":"debug","z":"a311c276.8fe21","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":910,"y":1660,"wires":[]},{"id":"68d6e00d.3536e8","type":"function","z":"a311c276.8fe21","name":"","func":"msg.payload = { \n    value: msg.payload, \n    'fc': 5, \n    'unitid': 1, \n    'address': 0 , \n    'quantity': 1 \n} \nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":430,"y":1660,"wires":[["132dec09.2c9ce4"]]},{"id":"88aa0724.eeafe8","type":"inject","z":"a311c276.8fe21","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":230,"y":1700,"wires":[["68d6e00d.3536e8"]]},{"id":"28c906aa.79aae2","type":"modbus-client","z":"","name":"TCP1","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.1.66","tcpPort":"502","tcpType":"TPC-RTU-BUFFERED","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":1,"commandDelay":200,"clientTimeout":1000,"reconnectOnTimeout":true,"reconnectTimeout":2000,"parallelUnitIdsAllowed":true}]

ps. in the function you can define at what address to write

Hi UnborN,
Yes, of course, down here the flow example with the code:

[{"id":"ead82657.2ee248","type":"tab","label":"MODBUS_TEST","disabled":false,"info":""},{"id":"70da052c.09c75c","type":"debug","z":"ead82657.2ee248","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":592,"y":540,"wires":[]},{"id":"28fac143.cf7476","type":"modbus-read","z":"ead82657.2ee248","name":"Modbus Read MD0","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"2","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"341b16ee.e3dffa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":180,"y":540,"wires":[["6c7bfdfe.87a534"],[]]},{"id":"e0f41600.53ce68","type":"function","z":"ead82657.2ee248","name":"Read UINT","func":"// Create new Buffer based on array bytes\nconst buf = Buffer.from(msg.payload.buffer);\n// Represent these bytes as 32-bit unsigned int\nconst value = buf.readUInt16BE();\n// save the value\n\nmsg.payload = value;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":412,"y":432,"wires":[["f1b3cab3.1b88b"]]},{"id":"5aeebb15.80f56c","type":"modbus-read","z":"ead82657.2ee248","name":"Modbus Read MW3","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"3","quantity":"2","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"341b16ee.e3dffa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":180,"y":432,"wires":[[],["e0f41600.53ce68"]]},{"id":"f1b3cab3.1b88b","type":"debug","z":"ead82657.2ee248","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":592,"y":450,"wires":[]},{"id":"13ecf8fc.313597","type":"comment","z":"ead82657.2ee248","name":"UINT READ","info":"","x":268,"y":378,"wires":[]},{"id":"a0e74ae9.d3b928","type":"comment","z":"ead82657.2ee248","name":"REAL READ","info":"","x":268,"y":486,"wires":[]},{"id":"6c7bfdfe.87a534","type":"function","z":"ead82657.2ee248","name":"Read REAL","func":"//msg.payload = [0, 16833];\nmsg.payload = [msg.payload[0], msg.payload[1]]\nlet pay = msg.payload;\n\nconst buf = Buffer.allocUnsafe(4);\nbuf.writeUInt16BE(pay[0],2);\nbuf.writeUInt16BE(pay[1],0);\n\nmsg.payload = buf.readFloatBE(0);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":412,"y":540,"wires":[["70da052c.09c75c"]]},{"id":"43703f7.3db08c","type":"debug","z":"ead82657.2ee248","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":700,"y":162,"wires":[]},{"id":"c6ba30e0.caf548","type":"comment","z":"ead82657.2ee248","name":"BOOL READ","info":"","x":268,"y":72,"wires":[]},{"id":"47ce73c1.02fbb4","type":"comment","z":"ead82657.2ee248","name":"BOOL WRITE","info":"","x":916,"y":54,"wires":[]},{"id":"5888d77e.059e","type":"bit-sixteen","z":"ead82657.2ee248","name":"","x":492,"y":180,"wires":[["43703f7.3db08c","6bd05902.79641"],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]},{"id":"6bd05902.79641","type":"ui_switch","z":"ead82657.2ee248","name":"VALORE A PLC","label":"Valore a PLC","tooltip":"","group":"82eddb90.b6485","order":0,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":692,"y":108,"wires":[[]]},{"id":"72139cff.fc6254","type":"modbus-read","z":"ead82657.2ee248","name":"ModbusReadMX0.0","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"1","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"341b16ee.e3dffa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":180,"y":180,"wires":[["5888d77e.059e"],[]]},{"id":"123dcf08.ba3881","type":"ui_switch","z":"ead82657.2ee248","name":"FORZA VAL","label":"FORZA VALORE","tooltip":"","group":"82eddb90.b6485","order":1,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"","style":"","onvalue":"1","onvalueType":"num","onicon":"","oncolor":"","offvalue":"0","offvalueType":"str","officon":"","offcolor":"","x":916,"y":108,"wires":[["28e5f81c.46ab98"]]},{"id":"28e5f81c.46ab98","type":"modbus-write","z":"ead82657.2ee248","name":"ModbusWrite BOOL","showStatusActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"1","server":"341b16ee.e3dffa","emptyMsgOnFail":false,"keepMsgProperties":false,"x":946,"y":234,"wires":[["5fbc069e.9c397"],[]]},{"id":"405107a0.5c678","type":"modbus-write","z":"ead82657.2ee248","name":"ModbusWrite INT","showStatusActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"1","quantity":"1","server":"341b16ee.e3dffa","emptyMsgOnFail":false,"keepMsgProperties":false,"x":918,"y":504,"wires":[["6f11292a.9b006"],[]]},{"id":"80311350.e85188","type":"comment","z":"ead82657.2ee248","name":"INT WRITE","info":"","x":906,"y":396,"wires":[]},{"id":"6f11292a.9b006","type":"debug","z":"ead82657.2ee248","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1150,"y":504,"wires":[]},{"id":"619b8931.38a47","type":"ui_text_input","z":"ead82657.2ee248","name":"","label":"","tooltip":"","group":"82eddb90.b6485","order":6,"width":4,"height":1,"passthru":true,"mode":"number","delay":"1000","topic":"","x":898,"y":450,"wires":[["405107a0.5c678"]]},{"id":"5fbc069e.9c397","type":"debug","z":"ead82657.2ee248","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1168,"y":234,"wires":[]},{"id":"341b16ee.e3dffa","type":"modbus-client","z":"","name":"m251@192.168.1.14","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.1.14","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"1","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true},{"id":"82eddb90.b6485","type":"ui_group","z":"","name":"SWITCH","tab":"a1cfbe2c.6f9948","order":1,"disp":true,"width":"6","collapse":false},{"id":"a1cfbe2c.6f9948","type":"ui_tab","z":"","name":"MODBUS","icon":"dashboard","disabled":false,"hidden":false}]

Actually like now I can't test your example, this monday I surely will!

1 Like

Hello .. thanks for sharing the flow ..

One thing i noticed is in your FORZA VAL ui_switch you have the Output set :
ON number and the OFF to String.
image

I think they should be both set to numbers for modbus-write to accept them.

1 Like

Hi,

yes, I fixed that. Now I can write the value, but it overwrite the whole Word.
The problem is my PLC doesn't support the FC5 function (the coils) because it hasn't the hardware memory, so I can only write the whole word. If I want set the 4th bit to 1, the only way I found to write it is pass a decimal value that's equal to a word that has the 4th bit set to 1.

The problem is in this way I erase the states of the other bits.
I found this code (to be paste in a funciton node?) on another topic:

//Get bit
function getBit(number, bitPosition) {
  return (number & (1 << bitPosition)) === 0 ? 0 : 1;
}

//Set Bit
function setBit(number, bitPosition) {
  return number | (1 << bitPosition);
}

//Clear Bit
function clearBit(number, bitPosition) {
  const mask = ~(1 << bitPosition);
  return number & mask;
}

//Update Bit
function updateBit(number, bitPosition, bitValue) {
  const bitValueNormalized = bitValue ? 1 : 0;
  const clearMask = ~(1 << bitPosition);
  return (number & clearMask) | (bitValueNormalized << bitPosition);
}

//You could use the code from these to update a WORD value and set / reset bits...

var plcValue = msg.payload;

//Set Bit 12
plcValue = plcValue | (1 << 12);

//reset Bit 3
const mask = ~(1 << 3);
plcValue = plcValue & mask;

msg.payload = plcValue; //(return this for next node to send it back to the PLC

return msg;

And I think is the right way to achive what I wan: something like everytime I want to set a bit in a Word, check the other bits and write the word in the corrisponding decimal value to set to desidered bit only without alter the others.

The problem is I can't figure out how exactly combine the code above to the function nodes and the modbus writing ones...

So the only way is using FC15 to write to multiple coils?
I don't know how useful those binary functions can be and I dont have any experience with those
but i was thinking the following stages for a possible solution.

  1. Do an initial FC1 read with a trigger node and save the array status result in Node-red flow Context.
    You can read about the use of Context here. This array should be an array of boolean [ true, true, false, false, true ] each representing the current status of the coils.
  2. When its time to write to the coils, update with a function node the node-red context array, prepare the modbus msg and send it.

Flow Example :

[{"id":"7ed14e5.f2503b","type":"modbus-flex-write","z":"ddb94f08.dac8a8","name":"","showStatusActivities":false,"showErrors":false,"server":"28c906aa.79aae2","emptyMsgOnFail":false,"keepMsgProperties":false,"x":830,"y":420,"wires":[["30459f25.0f9838"],[]]},{"id":"a590b4c1.0f80e","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":310,"y":320,"wires":[["ebde8026.c14da8"]]},{"id":"30459f25.0f9838","type":"debug","z":"ddb94f08.dac8a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1070,"y":400,"wires":[]},{"id":"ebde8026.c14da8","type":"function","z":"ddb94f08.dac8a8","name":"FC15","func":"// get current status array for example [ true, true, false, false, true ]\nlet deviceStatus = flow.get(\"deviceStatus\");  \n\n// what coil to update ..  must be sent in msg.coil as a number \nlet coil = msg.coil;\n\n// update x coil status in array based on payload true,false\ndeviceStatus[coil] = msg.payload;\n// update status for context\nflow.set(\"deviceStatus\", deviceStatus);     \n\n// prepare msg for Modbus flex write\nmsg.payload =  {\n    value: deviceStatus , \n    'fc': 15, \n    'unitid': 1, \n    'address': 0 , \n    'quantity': 5 \n};  \n\n // send msg to Modbus flex write\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":610,"y":420,"wires":[["7ed14e5.f2503b"]]},{"id":"ef4575de.037b28","type":"comment","z":"ddb94f08.dac8a8","name":"Update Context and Prepare Write","info":"","x":760,"y":320,"wires":[]},{"id":"3237f347.ed54b4","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"3","topic":"","payload":"trigger","payloadType":"str","x":190,"y":140,"wires":[["4b202f6f.f4938"]]},{"id":"eb0f70eb.9b4c4","type":"debug","z":"ddb94f08.dac8a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":140,"wires":[]},{"id":"4b202f6f.f4938","type":"function","z":"ddb94f08.dac8a8","name":"FC01","func":"msg.payload = { \n    value: msg.payload, \n    'fc': 1, \n    'unitid': 1, \n    'address': 0 , \n    'quantity': 5 \n} \nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":370,"y":140,"wires":[["1d12e1bf.a40bbe"]]},{"id":"1d12e1bf.a40bbe","type":"modbus-flex-getter","z":"ddb94f08.dac8a8","name":"","showStatusActivities":false,"showErrors":false,"logIOActivities":false,"server":"28c906aa.79aae2","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":620,"y":140,"wires":[["e471c7d9.aab7c"],[]]},{"id":"e471c7d9.aab7c","type":"function","z":"ddb94f08.dac8a8","name":"","func":"// take only the first 5 because flex getter returns 8\nlet deviceStatus = msg.payload.slice(0,5)  \n\n// set Node-red context in memory\nflow.set('deviceStatus', deviceStatus)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":840,"y":140,"wires":[["eb0f70eb.9b4c4"]]},{"id":"5818ac8a.bccd24","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":310,"y":380,"wires":[["ebde8026.c14da8"]]},{"id":"5eac710f.091238","type":"comment","z":"ddb94f08.dac8a8","name":"Initial Read","info":"","x":340,"y":80,"wires":[]},{"id":"25f887fd.782f48","type":"comment","z":"ddb94f08.dac8a8","name":"Set Context","info":"","x":850,"y":80,"wires":[]},{"id":"a55a5106.4402b","type":"comment","z":"ddb94f08.dac8a8","name":"0","info":"","x":150,"y":360,"wires":[]},{"id":"d2c7c90d.c7ad5","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":310,"y":460,"wires":[["ebde8026.c14da8"]]},{"id":"b8d89b7e.10ab6","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":310,"y":520,"wires":[["ebde8026.c14da8"]]},{"id":"70365c01.0a213c","type":"comment","z":"ddb94f08.dac8a8","name":"1","info":"","x":150,"y":500,"wires":[]},{"id":"28c906aa.79aae2","type":"modbus-client","z":"","name":"TCP1","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.1.66","tcpPort":"502","tcpType":"TPC-RTU-BUFFERED","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":1,"commandDelay":200,"clientTimeout":1000,"reconnectOnTimeout":true,"reconnectTimeout":2000,"parallelUnitIdsAllowed":true}]

ps. modifications needed to represent your devices and number of outputs etc

Hi There,
Actually it hasn't coil memory at all, it uses different memory function to handle boolean values. I can only read and write Holding Registers, and so the problem occurs.
Anyway, I appreciate a lot yuor example: I managed to use the code above to handle the entire register reading it and update it in order to set or reset the single desired bit, but I had to understand in nodered how to pass information through nodes.
I'm using the payload's topic property, but when I use switch or buttons to pass the 0,1 values it doesn't work like expected.

Later I'll post my actual flow!

Thanks anyway.

Hi,
As promised here the flow:

[{"id":"24c3208f.07b0f8","type":"tab","label":"TEST 1","disabled":false,"info":""},{"id":"3b0ba67b.d282aa","type":"comment","z":"24c3208f.07b0f8","d":true,"name":"NON TOCCARE","info":"","x":134,"y":54,"wires":[]},{"id":"c4ea4cd3.589e6","type":"modbus-read","z":"24c3208f.07b0f8","d":true,"name":"ModbusReadMX0.0","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"1","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"341b16ee.e3dffa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":126,"y":126,"wires":[["2a4c3fa7.cec83"],[]]},{"id":"2a4c3fa7.cec83","type":"function","z":"24c3208f.07b0f8","d":true,"name":"","func":"//Get bit\nfunction getBit(number, bitPosition) {\n  return (number & (1 << bitPosition)) === 0 ? 0 : 1;\n}\n\n//Set Bit\nfunction setBit(number, bitPosition) {\n  return number | (1 << bitPosition);\n}\n\n//Clear Bit\nfunction clearBit(number, bitPosition) {\n  const mask = ~(1 << bitPosition);\n  return number & mask;\n}\n\n//Update Bit\nfunction updateBit(number, bitPosition, bitValue) {\n  const bitValueNormalized = bitValue ? 1 : 0;\n  const clearMask = ~(1 << bitPosition);\n  return (number & clearMask) | (bitValueNormalized << bitPosition);\n}\n\n//You could use the code from these to update a WORD value and set / reset bits...\n\nvar plcValue = msg.payload;\n\n//Set Bit 12\nplcValue = plcValue | (1 << 12);\n\n//reset Bit 3\nconst mask = ~(1 << 3);\nplcValue = plcValue & mask;\n\nmsg.payload = plcValue; //(return this for next node to send it back to the PLC\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":348,"y":126,"wires":[["ddbb45c0.098cf8"]]},{"id":"ddbb45c0.098cf8","type":"debug","z":"24c3208f.07b0f8","d":true,"name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":126,"wires":[]},{"id":"ab331823.bbe138","type":"modbus-read","z":"24c3208f.07b0f8","name":"ModbusReadMX0.0","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"1","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"341b16ee.e3dffa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":162,"y":396,"wires":[["b1ef07b9.1a414"],[]]},{"id":"39fa5629.27d82a","type":"debug","z":"24c3208f.07b0f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1096,"y":396,"wires":[]},{"id":"51581f15.ad7af","type":"comment","z":"24c3208f.07b0f8","name":"TEST","info":"","x":176,"y":234,"wires":[]},{"id":"43246e79.252d2","type":"modbus-write","z":"24c3208f.07b0f8","name":"","showStatusActivities":false,"showErrors":false,"unitid":"","dataType":"HoldingRegister","adr":"0","quantity":"1","server":"341b16ee.e3dffa","emptyMsgOnFail":false,"keepMsgProperties":false,"x":890,"y":396,"wires":[["39fa5629.27d82a"],[]]},{"id":"d235ab78.64466","type":"function","z":"24c3208f.07b0f8","name":"ModbusWrite BOOL","func":"//Get bit\nfunction getBit(number, bitPosition) {\n  return (number & (1 << bitPosition)) === 0 ? 0 : 1;\n}\n\n//Set Bit\nfunction setBit(number, bitPosition) {\n  return number | (1 << bitPosition);\n}\n\n//Clear Bit\nfunction ResetBit(number, bitPosition) {\n  const mask = ~(1 << bitPosition);\n  return number & mask;\n}\n\n//Update Bit\nfunction updateBit(number, bitPosition, bitValue) {\n  const bitValueNormalized = bitValue ? 1 : 0;\n  const clearMask = ~(1 << bitPosition);\n  return (number & clearMask) | (bitValueNormalized << bitPosition);\n}\n\n//You could use the code from these to update a WORD value and set / reset bits...\n\nvar plcValue = {payload : (msg.topic === \"read\")};\nvar nBitSet = {payload : (msg.topic === \"nBitSet\")};\nvar nBitReset = {payload : (msg.topic === \"nBitReset\")};\nvar BitSet = {payload : (msg.topic === \"BitSet\")};\nvar BitReset = {payload : (msg.topic === \"BitReset\")};\n\n//Set Bit\nplcValue = setBit(plcValue,nBitSet)\nplcValue = ResetBit(plcValue,nBitReset)\n\n//reset Bit \n//plcValue = clearBit(plcValue,)\n\nif (BitSet || BitReset) {\nmsg.payload = plcValue;\nreturn msg;\n}//(return this for next node to send it back to the PLC\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":622,"y":396,"wires":[["43246e79.252d2","cc4776e5.82b14"]]},{"id":"d672abb8.25bfe8","type":"ui_numeric","z":"24c3208f.07b0f8","name":"nBitSet","label":"Setta il Bit","tooltip":"","group":"55555671.2d14","order":0,"width":0,"height":0,"wrap":true,"passthru":true,"topic":"nBitSet","format":"{{value}}","min":0,"max":"15","step":"1","x":402,"y":270,"wires":[["d235ab78.64466","8f9449aa.d5ed78"]]},{"id":"be7ad185.b3dc38","type":"ui_numeric","z":"24c3208f.07b0f8","name":"nBitReset","label":"Resetta il Bit","tooltip":"","group":"55555671.2d14","order":0,"width":0,"height":0,"wrap":true,"passthru":true,"topic":"nBitReset","format":"{{value}}","min":0,"max":"15","step":1,"x":402,"y":306,"wires":[["d235ab78.64466","5174aa49.7fd86c"]]},{"id":"f9616625.8e8ae","type":"ui_button","z":"24c3208f.07b0f8","name":"BitSet","group":"55555671.2d14","order":2,"width":0,"height":0,"passthru":false,"label":"Alza il Bit","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"BitSet","x":410,"y":468,"wires":[["d235ab78.64466"]]},{"id":"ef158df8.ebe1d","type":"ui_button","z":"24c3208f.07b0f8","name":"BitReset","group":"55555671.2d14","order":2,"width":0,"height":0,"passthru":false,"label":"Abbassa il Bit","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"BitReset","x":402,"y":504,"wires":[["d235ab78.64466"]]},{"id":"b1ef07b9.1a414","type":"function","z":"24c3208f.07b0f8","name":"","func":"msg.topic = \"read\"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":366,"y":396,"wires":[["d235ab78.64466"]]},{"id":"5174aa49.7fd86c","type":"debug","z":"24c3208f.07b0f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":646,"y":306,"wires":[]},{"id":"8f9449aa.d5ed78","type":"debug","z":"24c3208f.07b0f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":646,"y":270,"wires":[]},{"id":"cc4776e5.82b14","type":"debug","z":"24c3208f.07b0f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":808,"y":486,"wires":[]},{"id":"341b16ee.e3dffa","type":"modbus-client","z":"","name":"m251@192.168.1.14","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.1.14","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"1","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true},{"id":"55555671.2d14","type":"ui_group","name":"Group 1","tab":"fcf634.204631d","order":1,"disp":true,"width":6},{"id":"fcf634.204631d","type":"ui_tab","z":"","name":"TEST1","icon":"dashboard","order":3,"disabled":false,"hidden":false}]

As now it works if i put numbers manually in the functions, but when I try to send values from the buttons or the number input, it doesn't work (the detalis are in the flow).

Also, it continuosly writing because it is linked to the reading node, and I have to pass the entire word, as now I can't send the word just once

I'm new to node-red, and as I said maybe I'm not already able to understand properly how to pass information throgh the varius nodes.
Maybe you can help!

Thanks again

Aha! .. only after your last posts did it 'register' with me that you were sending decimals to holding registers to control the DO.
I made some modifications to the flow to reflect that .. i dont have time now to expand but there are some comments between the function code.

Try the following :

[{"id":"7ed14e5.f2503b","type":"modbus-flex-write","z":"ddb94f08.dac8a8","name":"","showStatusActivities":false,"showErrors":false,"server":"28c906aa.79aae2","emptyMsgOnFail":false,"keepMsgProperties":false,"x":830,"y":420,"wires":[["30459f25.0f9838"],[]]},{"id":"a590b4c1.0f80e","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"str","x":310,"y":320,"wires":[["ebde8026.c14da8"]]},{"id":"30459f25.0f9838","type":"debug","z":"ddb94f08.dac8a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1070,"y":400,"wires":[]},{"id":"ebde8026.c14da8","type":"function","z":"ddb94f08.dac8a8","name":"FC6","func":"// get current status array\nlet deviceStatus = flow.get(\"deviceStatus\");  \n\n// what coil to update ..  must be sent in msg.coil as a number \nlet coil = msg.coil;\n\n// make string to array\ndeviceStatus = deviceStatus.split('');\n\n// update x coil status in array based on payload 0 or 1\ndeviceStatus[coil] = msg.payload;\n// make it to a string AGAIN !!!\ndeviceStatus = deviceStatus.join('');\n\n// update status for Context so node-red will know the status\nflow.set(\"deviceStatus\", deviceStatus);     \n\n\n// make the string to a decimal\nlet dec = parseInt(deviceStatus, 2)\n\n\n// prepare msg for Modbus flex write\nmsg.payload =  {\n    value: dec, \n    'fc': 6, \n    'unitid': 1, \n    'address': 0,\n    'quantity': 1 \n};  \n\n // send msg to Modbus flex write\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":610,"y":420,"wires":[["7ed14e5.f2503b"]]},{"id":"ef4575de.037b28","type":"comment","z":"ddb94f08.dac8a8","name":"Update Context and Prepare Write","info":"","x":760,"y":320,"wires":[]},{"id":"3237f347.ed54b4","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"3","topic":"","payload":"trigger","payloadType":"str","x":190,"y":140,"wires":[["4b202f6f.f4938"]]},{"id":"eb0f70eb.9b4c4","type":"debug","z":"ddb94f08.dac8a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1070,"y":140,"wires":[]},{"id":"4b202f6f.f4938","type":"function","z":"ddb94f08.dac8a8","name":"FC03","func":"msg.payload = { \n  //  value: msg.payload, \n    'fc': 3, \n    'unitid': 1, \n    'address': 0,\n    'quantity': 1 \n} \nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":370,"y":140,"wires":[["1d12e1bf.a40bbe"]]},{"id":"1d12e1bf.a40bbe","type":"modbus-flex-getter","z":"ddb94f08.dac8a8","name":"","showStatusActivities":false,"showErrors":false,"logIOActivities":false,"server":"28c906aa.79aae2","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":620,"y":140,"wires":[["e471c7d9.aab7c"],[]]},{"id":"e471c7d9.aab7c","type":"function","z":"ddb94f08.dac8a8","name":"","func":"\n// convert to binary string and add leading zeros\nlet deviceStatus = msg.payload[0].toString(2).padStart(16, '0');\n\n// set Node-red context in memory so node-red can access the status\nflow.set('deviceStatus', deviceStatus)\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":840,"y":140,"wires":[["eb0f70eb.9b4c4"]]},{"id":"5818ac8a.bccd24","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"str","x":310,"y":380,"wires":[["ebde8026.c14da8"]]},{"id":"5eac710f.091238","type":"comment","z":"ddb94f08.dac8a8","name":"Initial Read","info":"","x":340,"y":80,"wires":[]},{"id":"25f887fd.782f48","type":"comment","z":"ddb94f08.dac8a8","name":"Set Context","info":"","x":850,"y":80,"wires":[]},{"id":"a55a5106.4402b","type":"comment","z":"ddb94f08.dac8a8","name":"0","info":"","x":150,"y":360,"wires":[]},{"id":"d2c7c90d.c7ad5","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"str","x":310,"y":460,"wires":[["ebde8026.c14da8"]]},{"id":"b8d89b7e.10ab6","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"},{"p":"coil","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"str","x":310,"y":520,"wires":[["ebde8026.c14da8"]]},{"id":"70365c01.0a213c","type":"comment","z":"ddb94f08.dac8a8","name":"1","info":"","x":150,"y":500,"wires":[]},{"id":"64793635.7d5c4","type":"inject","z":"ddb94f08.dac8a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":720,"wires":[["5ab93fcf.bff26"]]},{"id":"5ab93fcf.bff26","type":"function","z":"ddb94f08.dac8a8","name":"","func":"//get current status from memory\nlet deviceStatus = flow.get(\"deviceStatus\")  \n\nlet dec = parseInt(deviceStatus, 2);\n\nmsg.deviceStatus = deviceStatus;\nmsg.dec = dec;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":460,"y":720,"wires":[["de14584b.b0e298"]]},{"id":"de14584b.b0e298","type":"debug","z":"ddb94f08.dac8a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":720,"wires":[]},{"id":"c011668.3824818","type":"comment","z":"ddb94f08.dac8a8","name":"test read - binary to decimal","info":"","x":480,"y":660,"wires":[]},{"id":"28c906aa.79aae2","type":"modbus-client","z":"","name":"TCP1","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.1.66","tcpPort":"502","tcpType":"TPC-RTU-BUFFERED","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"1","commandDelay":"200","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true}]

ps. the order of the binary may be wrong, in that case it may need to be reversed.

If you are new to Node-red there is a very good video playlist that goes throught the manipulation of messages here

2 Likes

Wow, thanks UnborN, it did the job!

And thanks for the suggested link to cover at least the basics.

P.s. Yes, the bits are reversed, but no problem.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.