Extracting boolean variables with buffer-parser

So I have managed to read a whole buffer from a Mitsubishi PLC with the mcprotocol node, and I'm trying to parse the variables with buffer-parser.

Integers do not seem to be an issue, I just add the proper offset and I get an integer (16 or 32 bit).

Now I find myself having to read two booleans from the same word. I checked this thread where reading nibbles was discussed. However, nibbles are small integers, not booleans.

In my example, I must read two of the bits. I set up a binary mask to read bits 3 and 4, considering that the LSB is bit 0 and the MSB is bit 15. The result I'm getting is 8 in one of the bits and 16 in the other.

Now, I was thinking of using the scaling function to convert them to either 1 or 0 (with the filter, I shouldn't have any other result), but the variable will still be an integer.

Is there any way to specify the output to be of a certain type (boolean, in my case)?

Thanks!

The simplest is to use the bool type

however if you want to use bit masks (or more commonly hex masks) then you will get the answer as a masked result (e.g. a mask of 0000 0000 0001 0000 WILL result in a the number 8) so you will need to RIGHT SHIFT the result 3 places.

Example:

Flow

[{"id":"abde808441497640","type":"inject","z":"6429d72ae185e590","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":1310,"y":80,"wires":[["96d8281ed160c22d"]]},{"id":"96632d5a3b49e49e","type":"debug","z":"6429d72ae185e590","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1820,"y":80,"wires":[]},{"id":"96d8281ed160c22d","type":"buffer-maker","z":"6429d72ae185e590","name":"Dummy PLC Data (fill with 0b10101010)","specification":"spec","specificationType":"ui","items":[{"name":"item1","type":"byte","length":1,"dataType":"num","data":"0b10101010"},{"name":"item2","type":"byte","length":1,"dataType":"num","data":"0b10101010"},{"name":"item3","type":"byte","length":1,"dataType":"num","data":"0b10101010"},{"name":"item4","type":"byte","length":1,"dataType":"num","data":"0b10101010"}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","x":1560,"y":80,"wires":[["96632d5a3b49e49e","710ea0a0bb20302a"]]},{"id":"818748a08ae25cce","type":"buffer-parser","z":"6429d72ae185e590","name":"Easy (using bool)","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"bool","name":"byte2_bit3","offset":2,"length":1,"offsetbit":3,"scale":"1","mask":""},{"type":"bool","name":"byte2_bit4","offset":2,"length":1,"offsetbit":4,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"return","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":1490,"y":160,"wires":[["6b458a2a6fdcd277"]]},{"id":"6b458a2a6fdcd277","type":"debug","z":"6429d72ae185e590","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1820,"y":160,"wires":[]},{"id":"233349320631f9b2","type":"debug","z":"6429d72ae185e590","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1820,"y":240,"wires":[]},{"id":"d06b537d7b7991a7","type":"buffer-parser","z":"6429d72ae185e590","name":"using byte with mask","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"uint8","name":"byte2HexMask_bit3","offset":2,"length":1,"offsetbit":5,"scale":"1","mask":"0x08"},{"type":"uint8","name":"byte2HexMask_bit4","offset":2,"length":1,"offsetbit":5,"scale":"1","mask":"0x10"},{"type":"uint8","name":"byte2HexMaskToBool_bit3","offset":2,"length":1,"offsetbit":5,"scale":"!!","mask":"0x08"},{"type":"uint8","name":"byte2HexMaskToBool_bit4","offset":2,"length":1,"offsetbit":5,"scale":"!!","mask":"0x10"},{"type":"uint8","name":"byte2HexMaskShifted_bit3","offset":2,"length":1,"offsetbit":5,"scale":">> 3","mask":"0x08"},{"type":"uint16be","name":"byte2HexMaskShifted_bit4","offset":2,"length":1,"offsetbit":5,"scale":">> 4","mask":"0x10"},{"type":"uint8","name":"byte2BinMaskRaw_bit3","offset":2,"length":1,"offsetbit":5,"scale":"1","mask":"0b00001000"},{"type":"uint8","name":"byte2BinMaskRaw_bit4","offset":2,"length":1,"offsetbit":5,"scale":"1","mask":"0b00010000"},{"type":"uint8","name":"byte2BinMaskShifted_bit3","offset":2,"length":1,"offsetbit":5,"scale":">> 3","mask":"0b00010000"},{"type":"uint8","name":"byte2BinMaskShifted_bit4","offset":2,"length":1,"offsetbit":5,"scale":">> 4","mask":"0b00010000"}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"return","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":1500,"y":240,"wires":[["233349320631f9b2"]]},{"id":"710ea0a0bb20302a","type":"junction","z":"6429d72ae185e590","x":1330,"y":160,"wires":[["818748a08ae25cce","d06b537d7b7991a7"]]}]
1 Like

For reference - from the docs

  • Final values can be masked (e.g. a MASK of 0x7FFF could be used to remove the MSB or 0b1000000000000001 to keep only MSB and LSB)
    • Binary and Octal masks only available in V3.1 onwards
  • Final values can be have a Scale value or a simple Scale Equation (New in V3.1) applied...
    • e.g. Entering a Scale value of 0.01 would turn 9710 into 97.1
    • e.g. Entering a Scale value of 10 would turn 4.2 into 42
    • e.g. Entering a Scale Equation of >> 4 would bit shift the value 0x0070 to 0x0007
    • e.g. Entering a Scale Equation of + 42 would add an offset of 42 to the final value (New in V3.1)
    • Supported Scaling Equations are...
      • << e.g. <<2 would left shift the parsed value 2 places
      • >> e.g. >>2 would right shift the parsed value 2 places
      • >>> e.g. >>>2 would zero-fill right shift the parsed value 2 places (returns a 32bit unsigned value)
      • + e.g. +10 would add 10 to the parsed value
      • - e.g. -10 would deduct 10 from the parsed value
      • / e.g. /10 would divide the parsed value by 10
      • * e.g. *10 would multiply the parsed value by 10
      • ** e.g. **2 would raise the parsed value to the power of 2
      • ^ e.g. ^0xf0 would XOR the parsed value with 0xf0
      • == e.g. ==10 would result in true if the parsed value was equal to 10
      • != e.g. !=10 would result in false if the parsed value was equal to 10
      • !! e.g. !! would result in true if the parsed value was 1 (same as !!1 == true)
      • > e.g. >10 would result in true if the parsed value was greater than 10
      • < e.g. <10 would result in true if the parsed value was less than 10
    • NOTE: the scale/equation is applied AFTER the mask

In the end, and since the bits I need are in the same word, I used filtering, int scaling with boolean conversion.

If not, I could have probably used the ==8 or ==16 to get a boolean.

Thanks for all the help, it's been extremely useful :slight_smile:

That is not really relevant. It would be pretty much the same amount of work under the hood to use the bool type, even with the same offset. In fact, it is less work to use the bool type since it does not have to perform the scaling equation!

The double bang !! scaling equation does not have parameters.

Since ANDing a VALUE & MASK returns the masked value (e.g. 0xFFFF & 0x0010 === 0x0010 === 8), using !! (without the number) means "invert invert value" i.e. Since the result of VALUE & MASK is 8 (the number 8 is truthy) therefore ! true is false, !! true is true BUT you should NOT be adding the value to the double bang in the equation - the equation is applied to the result of the masked value:

the scale/equation is applied AFTER the mask

in other words, !!0.125 is wrong.


My advice in order of recommendation:

  1. Use the bool type as described and demoed in my previous post.
    OR
  2. Use a mask with a scale equation of ==8 (or whatever the result of VALUE & MASK is)
    OR
  3. Use a mask with a scale equation of !!
    OR
  4. Use a mask with a scale equation of >0

That was solved, option (2) seems to be the easiest.

Now my battle is with the vendor, since they spread the data I need all over the PLC memory, and I'm not sure if all the memory addresses they use in their program are properly labelled, or, on the other side, they forgot to label certain memory positions that are used, and overwriting them will break stuff.

Basically, they had variables to read between positions D228 and D16800 or something like that. Trying to read more than 3600 positions will give a timeout/quality problem as a return message.

I don't know how the MCProtocol works internally, but would it be possible to include only the needed variables in the buffer in a single read, similar to what the S7 module does? I know the node doesn't allow it at the moment, but is it limited by hardware, or it could be done but it's not coded?

Thanks!

MC protocol does have multi read function but I can't remember if I implemented it in src. What does the help say again?

All I can suggest for now is make as many grouped reads as possible.

For example, say you need d10, d124, d125, d9000, d9007, d9110, d20000 d20002 d20055 then read them in 3 lots of

D0 [126]
D9000 [111]
D20000 [56]

Then use buffer parser to pluck out the elements of interest.

Just remember, it takes a handful of milliseconds more to read 100 items than it does to read 1 item. So if you end up doing 100 reads instead 1 larger read, you would end out unnecessarily flooding the network and it would take approx 99times longer. And you data would be completely inconsistent by around 50 PLC scans from the first to the last item (unless you have a collection buffer in the PLC)

MC Read

Read data from an MITSUBISHI PLCs using MC Protocol

Inputs

ConnectionDF1 Connection
The PLC connection
Addressnumber | string | payload
PLC Memory Address

INFO: To open / close the MC Protocol connection, send boolean true in msg.connect or msg.disconnect. Alternatively, send string connect or disconnect in msg.topic to the any mc read / mc write node.

Outputs

payloadarray | object
the values read from the PLC.
To view additional details of the PLC read result, use a debug node to inspect the data

Details

If an error occurs the output of this node may still be triggered. In this case, the payload will be null but the mcWriteDetails will contain additional information about the problem.

Address format [DS] DEV [DT] DN [.BIT] [,CNT] [:OPTS]...

  1. [1] DS - {string} Digit specifier (e.g. K4) [optional]
  2. [2] DEV - {string} Device (Y|X|D|F|W|B|R|etc)
  3. [3] DT - {string} Data type (REAL|FLOAT|STR|WORD|DWORD|DINT|UINT) [optional]
  4. [4] DN - {int} Device number
  5. [5] BIT - {int} Bit number [optional]
  6. [6] CNT - {int} Count of items [optional]
  7. [7] OPTS - {JSON} Options for specifying network and station e.g. {N:2,S:3} would attempt to route the message to station 3 on network 2 [optional]

Notes...
DS and DT should not be used together.
if CNT is omitted, then 1 is assumed.
DN may be DEC or HEX depending on the notation of the Device e.g. YA, X1E, WBA4, B3D

data returned will match the address specified e.g...

Address Example Data Type Notes
K4Y0 int −32,768 to 32,767
DSTR0,10 string Up to 10 chars long
DUINT0,10 UINT[10] 10 WD array containing values 0 to 65535

As far as I can see, it does not mention it.

I tried passing some variable addresses separated by commas, and also an array of addresses. In the first case it says the address is not correct, on the second it returns only the value for the first address in the array.

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