Convert a 16 bit integer to 16 discrete outputs

Hi Everyone, I would like to create a function that can take a 16 bit integer, break out each bit and assign the value to the appropriate output. I'm using a loop that will run 16 times. It will check the value of each bit in the integer to see if it's a zero or one. I then need to assign that result to one of the functions outputs. My return includes all 16 outputs, but it looks like I can't use an index number within the loop to point at the msg(x).

  1. var CurrentBit = 1
  2. var IntIn = 4161 // An input integer
  3. var MessageNumber = 1 // An index pointing at an output for the function
  4. for (i = 1; i <= 32768; i = i * 2){ // A loop that will run 16 times checking the value of each bit.
  5. if ((IntIn & i) > 0) {
    
  6.     CurrentBit = 1
    
  7. } else {
    
  8.     CurrentBit = 0
    
  9. }
    
  10. msg[MessageNumber].payload = CurrentBit //Point at a msg object and give the payload the current bit value.
    
  11. MessageNumber ++ // increment by 1
  12. }
  13. return [msg1, msg2, msg3, msg4, msg5, msg6, msg7,msg8, msg9, msg10, msg11, msg12, msg13, msg14,msg15,msg16];

Sorry about the readability of the code. Not sure what happened there,

Welcome to the forum. Putting the results in an array may be easier.

just looking at the code, I see one reasons that it won't work at all (before getting to whether the results are what you want)

you're putting your CurrentBit in msg[#] (i.e, array position # in array msg), but then returning [msg1, msg2, ...], an array populated with variables named msg1 etc., which isn't the same thing (and those variables aren't declared). I'm not sure if putting an array index into msg itself is ok, or if you should use a different variable, but either way, you need to return just the array (return resultarray), or put square braces around the indices in your current code (return [msg[1], msg[2],...])

edit = removed stupid math error on my part; i=i*2 is fine

my original suggestion, before I caught my own error, was to use i++ instead of i=i*2 and loop zero to 15, then use i for both the math and as the output array index. That may not fix the problem, but removes one variable and streamlines the code. (also just noticed that you're starting your MessageNumber index from 1, it should be zero)

Another solution could be the toString() method of JavaScript number objects. It takes a radix, so Number(4161).toString(2) would give you 1000001000001 as string which can be processed further.

To take this even further, your function node could look like

const outputs = 16;

let n = msg.payload;
// n is supposed to be a numeric value, add checks here if necessary

return n.toString(2) // to binary
    .slice(-outputs) // keep 16 lowest bits if number is too great
    .padStart(outputs, '0') // fill missing highest bits with 0
    .split('') // create array of single chars
    .reverse() // reverse order: lowest bit -> output 1
    .map((el, i) => { // create message objects
        return { 
            payload: parseInt(el),
            topic: 'bit_' + i
        };
    });

That creates an array with the appropriate message for each output. The lowest bit will be the first (top-most) output.

I know that it is not the most efficient solution. I rather see it as a demonstration of JavaScript's capabilities. :nerd_face:

2 Likes

There is a very similar question in this post. There is an alternative solution there.

Thanks everyone for the great ideas. I should tell you what I'm trying to accomplish overall. I have a Modbus read bringing in data in the form of an integer. This integer actually represents the value of 16 status items in an Allen Bradley AC drive.

Modbus Register 8448
0 1 = Ready, 0 = Not Ready
1 1 = Active (Running), 0 = Not Active
2 1 = Cmd Forward, 0 = Cmd Reverse
3 1 = Rotating Forward, 0 = Rotating Reverse
4 1 = Accelerating, 0 = Not Accelerating
5 1 = Decelerating, 0 = Not Decelerating
6 Not Used
7 1 = Faulted, 0 = Not Faulted
8 1 = At Reference, 0 = Not At Reference
9 1 = Main Freq Controlled by Active Comm
10 1 = Operation Cmd Controlled by Active Comm
11 1 = Parameters have been locked
12 Digital Input 1 Status (DigIn TermBlk 05)
13 Digital Input 2 Status (DigIn TermBlk 06)
14 Digital Input 3 Status (DigIn TermBlk 07)
15 Digital Input 4 Status (DigIn TermBlk 08)

I would like to have each of those status bits drive there own dashboard LED (node-red-contrib-ui-led) Originally I thought the LED would accept only true or false, but now I see there are other possible inputs. I'm beginning to wonder if I can send the integer number to the LED at process it there.

1 Like

Thanks Andrei. This is very interesting.

1 Like

See also this existing node - https://flows.nodered.org/node/node-red-contrib-bitunloader - that will create various combinations of bits for you.

1 Like

Hi @cdoctor.

Some time ago I just finished a project with an arduino nano that communicates with the PCF8575 port expander via an i2c bus.
A detailed explanation is at GPIOs two way port expander via USB.

In short, the device analyzes the status of the ports on the PCF8575 and sends them via USB in the form of a series of numbers (integer), with the addition of letters at the beginning.
The letter indicates whether it is a first or second group of ports at a specific i2C address that is referred to PCF8575.

So, the message is: A00100001.

This means that at the first i2c address (A), the status of ports (ports) is 00100001.
Ports are numbered from right to left.

I created a node for this analysis.
Node is a node-red-contrib-PCF8575-extractor.
You need to install it manually.

Node analyzes whether the first character is (A, B, C, D, E, F, G or H), whether the 2nd to 9th characters are numbers 0 or 1, and that the message length is 9 characters.

The analyzed value goes to 8 outputs with associated value.

Try installing and testing if it might be useful to you.
You have an example in Menu-> Import-> Example -> ...

Feel free to edit the node according to your needs.

Greeting!

2 Likes

I'm happy to say, I have the solution. I'm embarrassed to say that it occurred to me while sitting on the toilet! :blush: I created an array of 16 messages. Each message built within the loop. Then return message array.

//Create a static topic array
var Topics = ["Ready","Running","Cmd Forward","Rotating Forward","Accelerating","Decelerating","Not Used","Faulted","At Reference","Main Freq Controlled by Active Comm","Operation Cmd Controlled by Active Comm","Parameters locked","Digital Input 1","Digital Input 2","Digital Input 3","Digital Input 4"]
// An input integer
var IntIn = msg.payload;
//State of the current bit being analysed
var CurrentBit;
//prepare an array to hold 16 messages
var OutputBit = new Array(16);
//an independant counter to index the array of messages
var c = 0;
// A loop that will run 16 times checking the value of each bit.i will increment 1, 2, 4, 8, 16 and so on. Set CurrentBit to a bool depending on the & outcome
for (i = 1; i <= 32768; i = i * 2){ if ((IntIn & i) > 0) {
CurrentBit = true;
} else {
CurrentBit = false;
}
//Build each of the 16 messages in the message array
OutputBit[c] = {topic: Topics[c], payload:(CurrentBit)};
//Increment c by 1
c ++ // increment by 1
}
//Return the entire message array. The function must have 16 outputs.
return OutputBit;

You might like to consider this solution

const Topics = ["Ready","Running","Cmd Forward","Rotating Forward",
  "Accelerating","Decelerating","Not Used","Faulted",
  "At Reference","Main Freq Controlled by Active Comm","Operation Cmd Controlled by Active Comm","Parameters locked",
  "Digital Input 1","Digital Input 2","Digital Input 3","Digital Input 4"
  ]
// build array of 16 messages, function must have 16 outputs
messages = Topics.map( getMsg )
return messages

function getMsg(topic, index) {
    return {topic: topic, payload: (msg.payload >> index) & 1}
}
3 Likes

Nice elegant solution, @Colin! I completely forget about the bit-shift operator. I shouldn't write posts and have supper at the same time. :see_no_evil:

It's not only you, I am getting epiphanies there, too. :rofl:

Wow! There's no "consider" about it! I am definitely using your solution. Brilliant! I'm new to Node-Red and javascript. You have helped me clean up my code and provided a valuable lesson. Thanks! :smiley:

I added a "color" property to the message. The color is based on the outcome of (msg.payload >> index) & 1. Is it possible to do the if else within the return line of the function decleration?

const Topics = ["Ready","Running","Cmd Forward","Rotating Forward",
"Accelerating","Decelerating","Not Used","Faulted",
"At Reference","Main Freq Controlled by Active Comm","Operation Cmd Controlled by Active Comm","Parameters locked",
"Digital Input 1","Digital Input 2","Digital Input 3","Digital Input 4"
]
// build array of 16 messages, function must have 16 outputs
messages = Topics.map( getMsg )
return messages

function getMsg(topic, index) {
var setcolor;
if ((msg.payload >> index) & 1) {
setcolor = "green";
} else {
setcolor = "white";
}
return {topic: topic, payload: (msg.payload >> index) & 1, color: setcolor};
}

careful use of the ternary operator should get you there...
have a read up on this

1 Like

It's best to post code in the same way that flows are posted so the forum doesn't mess with special characters, that is between lines each containing three backticks.
You could do something like this

function getMsg(topic, index) {
    const state = (msg.payload >> index) & 1
    return {topic: topic, payload: state, color: (state ? "green" : "white")};
}

See the Ternary operator in https://www.w3schools.com/js/js_comparisons.asp

1 Like

Thanks again!

This post looks better! :wink:

const Topics = ["Ready","Running","Cmd Forward","Rotating Forward",
  "Accelerating","Decelerating","Not Used","Faulted",
  "At Reference","Main Freq Controlled by Active Comm","Operation Cmd Controlled by Active Comm","Parameters locked",
  "Digital Input 1","Digital Input 2","Digital Input 3","Digital Input 4"
  ]
// build array of 16 messages, function must have 16 outputs
messages = Topics.map( getMsg )
return messages

function getMsg(topic, index) {
    const state = (msg.payload >> index) & 1
    return {topic: topic, payload: state, color: (state ? "green" : "white")};
}
1 Like