First time using buttons

I did one project in the past with node red and that worked after struggling a lot. Now im doing a new one but i've lost the minimal knowledge i learned in the first project.

Here is my project.
I have 5 buttons.
1 4xrelay board.
1 pi zero w

ive installed the pi and installed node red succesfully.
Button 1 needs to switch relay1 on for 40 ms and then turn off again. This one is to boot the pc.
Relay 2,3 and 4 are switching power to 3 monitors.
Button 2 needs to turn all monitors off, if any of them are on. If none of them are on, turn all monitors on.
button 3 -if relay 2 is on, turn it off. If its off, turn it on.
button 4 -if relay 3 is on, turn it off. If its off, turn it on.
button 5 -if relay 4 is on, turn it off. If its off, turn it on.

(bonus, i also like the option (function to turn off or on) on button 1 to trigger the function of button 2 as well. not sure if its practical.

Now, ive added a trigger node to put a 400ms delay on relay1 and then turn off. This, does not worp properly because it keeps triggering the action without inject is triggered. so, I may do it incorrectly. I dont know why it keeps triggering.

the rest of the buttons i cant work at all. I can make a ON injection and OFF injection to switch the relays on and off. But making a single button action that turns it on when its off and off when its on, I cant figure out. Let alone the more complex button 2.
Ive asked chatgpt to help me out with some code to put into a function but I keep getting the same error on all de code she is proposing:
05/06/2023, 21:48:57node: Relay3msg : string[18]

"Invalid input: NaN"

the code I put in is this:

// Input: msg.payload (always 1)
// Output: msg.payload (command to toggle relay 2)

var relay2State = context.get("relay2");
if (relay2State === undefined || isNaN(relay2State)) {
    relay2State = 0;  // Set initial state to off if not defined or NaN
}

if (relay2State === 1) {
    relay2State = 0;  // Turn off relay 2
} else {
    relay2State = 1;  // Turn on relay 2
}

context.set("relay2", relay2State);

msg.payload = { relay2: relay2State };
return msg;

I hope someone can point me in the right direction.
p.s. i havent wired up the relay or the buttons because i want to be safe. I am also not sure if I need to add resistors to the buttons or not.

Let's solve them one at a time. This should be the easiest

First add a debug node showing what is going into the Trigger and another showing what is coming out of the trigger. See whether there are extra inputs going in or extra outputs coming out. If that is not behaving correctly then select the button and trigger nodes (and any other nodes directly involved in this problem) and Export them and paste them here. In case you do not know how to post a flow, in order to make code readable and usable it is necessary to surround your code with three backticks (also known as a left quote or backquote ```)

``` 
   code goes here 
```

See this post for more details - How to share code or flow json

1 Like

It's only natural for someone who comes to Node-red with a bit of programming knowledge to leap into the function node. After all, it's the only bit that looks like a program.
However, this is generally not a very good idea.
It's like buying a comprehensive toolbox, throwing away all the tools except the hammer and using it to make your own.

Yes, a trigger node is the solution here. This flow might look something like this:


What does your version look like?
If you have GPIO-in nodes, these will reflect the actual pin status. Have you chosen appropriate pins? Have you configured pull-up/down resistor options?
Definitely don't copy my pin selection, it's random.

There is a discussion about that at https://discourse.nodered.org/t/toggle-gpio-input/60643/7

Do not wire up physical buttons or relays until you are sure. There are countless tutorials on raspberrypi.com and it's associated sites, youtube and elsewhere.

So you asked an infinite number of monkeys to write you a program and it didn't go well. How surprising is that? :smile:

2 Likes

flows.json (10.7 KB)
O i forgot to mention I did have a debug. Still image, But what I saw yesterday happen was that the "button 1gp" switches to 0 and 1 and back without any interaction multiple times. There is no button or any wire connected to any pins yet, so no physical trigger possible.

[
    {
        "id": "09c77f24e7bccf15",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "d4cdc552e12c5621",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Button 1",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "Repeat",
                "v": "none",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "flow",
        "x": 200,
        "y": 140,
        "wires": [
            [
                "c91e285e20d5a874"
            ]
        ]
    },
    {
        "id": "f40fe163d7499fb8",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Button 2",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "Repeat",
                "v": "none",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 220,
        "y": 320,
        "wires": [
            [
                "cd7c353cd80d7aeb"
            ]
        ]
    },
    {
        "id": "cd7c353cd80d7aeb",
        "type": "function",
        "z": "09c77f24e7bccf15",
        "name": "function 2",
        "func": "// Input: msg.payload (button state)\n// Output: msg.payload (command to control relays)\n\nvar relayState2 = flow.get(\"relayState2\") || false;\nvar relayState3 = flow.get(\"relayState3\") || false;\nvar relayState4 = flow.get(\"relayState4\") || false;\n\n// Check if any of the relays are off\nif (!relayState2 || !relayState3 || !relayState4) {\n    // Turn on all relays\n    relayState2 = true;\n    relayState3 = true;\n    relayState4 = true;\n} else {\n    // Turn off all relays\n    relayState2 = false;\n    relayState3 = false;\n    relayState4 = false;\n}\n\nflow.set(\"relayState2\", relayState2);\nflow.set(\"relayState3\", relayState3);\nflow.set(\"relayState4\", relayState4);\n\n// Prepare the output command\nvar command = {\n    relay2: relayState2 ? 1 : 0,\n    relay3: relayState3 ? 1 : 0,\n    relay4: relayState4 ? 1 : 0\n};\n\nmsg.payload = command;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 420,
        "y": 320,
        "wires": [
            [
                "7223ffd76740e175"
            ]
        ]
    },
    {
        "id": "f53cb388b02ccf28",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Button 3",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "Repeat",
                "v": "none",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 160,
        "y": 500,
        "wires": [
            [
                "921a33090ad9837c"
            ]
        ]
    },
    {
        "id": "f66f04e359b9c550",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Button 4",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "Repeat",
                "v": "none",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 200,
        "y": 620,
        "wires": [
            []
        ]
    },
    {
        "id": "ed26f34718414f5e",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Button 5",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "Repeat",
                "v": "none",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 200,
        "y": 660,
        "wires": [
            []
        ]
    },
    {
        "id": "a82449df7ba0d205",
        "type": "rpi-gpio out",
        "z": "09c77f24e7bccf15",
        "name": "Relay1",
        "pin": "22",
        "set": false,
        "level": "0",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 690,
        "y": 140,
        "wires": []
    },
    {
        "id": "642fcfa9eb369aad",
        "type": "rpi-gpio out",
        "z": "09c77f24e7bccf15",
        "name": "Relay2",
        "pin": "16",
        "set": "",
        "level": "0",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 690,
        "y": 320,
        "wires": []
    },
    {
        "id": "dccae454835bdfd4",
        "type": "rpi-gpio out",
        "z": "09c77f24e7bccf15",
        "name": "Relay3",
        "pin": "18",
        "set": "",
        "level": "0",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 690,
        "y": 380,
        "wires": []
    },
    {
        "id": "47f57b74dbb0cf48",
        "type": "rpi-gpio out",
        "z": "09c77f24e7bccf15",
        "name": "Relay4",
        "pin": "22",
        "set": "",
        "level": "0",
        "freq": "",
        "out": "out",
        "bcm": true,
        "x": 690,
        "y": 440,
        "wires": []
    },
    {
        "id": "525384a05af3164c",
        "type": "rpi-gpio in",
        "z": "09c77f24e7bccf15",
        "name": "Button 1gp",
        "pin": "15",
        "intype": "tri",
        "debounce": "25",
        "read": false,
        "bcm": true,
        "x": 200,
        "y": 80,
        "wires": [
            [
                "c91e285e20d5a874"
            ]
        ]
    },
    {
        "id": "c91e285e20d5a874",
        "type": "trigger",
        "z": "09c77f24e7bccf15",
        "name": "400ms",
        "op1": "1",
        "op2": "0",
        "op1type": "str",
        "op2type": "str",
        "duration": "400",
        "extend": false,
        "overrideDelay": false,
        "units": "ms",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 410,
        "y": 80,
        "wires": [
            [
                "a82449df7ba0d205",
                "37e6a4cea7d28de6"
            ]
        ]
    },
    {
        "id": "37e6a4cea7d28de6",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 80,
        "wires": []
    },
    {
        "id": "7223ffd76740e175",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 500,
        "wires": []
    },
    {
        "id": "2cddb2d96640f760",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 3",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 580,
        "wires": []
    },
    {
        "id": "84f502513097d5a4",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 4",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 620,
        "wires": []
    },
    {
        "id": "767331b147c24f38",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 5",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 660,
        "wires": []
    },
    {
        "id": "3cb117841d1bdba7",
        "type": "debug",
        "z": "09c77f24e7bccf15",
        "name": "debug 6",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 540,
        "wires": []
    },
    {
        "id": "770c0858dd2addc1",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "On",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 250,
        "y": 380,
        "wires": [
            [
                "642fcfa9eb369aad"
            ]
        ]
    },
    {
        "id": "ceca0a062856929e",
        "type": "inject",
        "z": "09c77f24e7bccf15",
        "name": "Off",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "0",
        "payloadType": "num",
        "x": 230,
        "y": 420,
        "wires": [
            [
                "642fcfa9eb369aad"
            ]
        ]
    },
    {
        "id": "921a33090ad9837c",
        "type": "function",
        "z": "09c77f24e7bccf15",
        "name": "function 3",
        "func": "// Input: msg.payload (always 1)\n// Output: msg.payload (command to toggle relay 2)\n\nvar relay2State = context.get(\"relay2\");\nif (relay2State === undefined || isNaN(relay2State)) {\n    relay2State = 0;  // Set initial state to off if not defined or NaN\n}\n\nif (relay2State === 1) {\n    relay2State = 0;  // Turn off relay 2\n} else {\n    relay2State = 1;  // Turn on relay 2\n}\n\ncontext.set(\"relay2\", relay2State);\n\nmsg.payload = { relay2: relay2State };\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 420,
        "y": 480,
        "wires": [
            [
                "dccae454835bdfd4",
                "3cb117841d1bdba7"
            ]
        ]
    }
]

"Edit:
I spoke too soon, the self switching of the gpio in is still happening."

well it seemed I was on the right track, your layout was the same as mine. :grinning: however the gpio in for the button is switching itself randomly.

Ill check out the link.
One of the reason I did not wire it was because I found different pin layouts. Plus the whole bcm vs gpio always confuses me, a bit.

I know chatgpt is not flawless but I thought it might be useful because of my own limited coding knowledge. So, for me, the only tool in the shed when it comes to coding. I hoped this challenge would be simple enough for chatgpt to handle.

You might find this useful as a reference.

Here's a link to a worksheet I created for my IoT students. It shows how to create various types of buttons.

Having fun with buttons

1 Like

Are we talking a dashboard button or a physical button on a gpio pin?

If a physical button are you getting several signals close together when you press the button or truly random timing?

If close together when you press/release it then increase the denounce time in the input node.
If random then show us how you have connected the button to the input.

Thanks for the documentation. I tried the funtion node B but it has a problem with line 12 that I cant find. I checked my code next to the documents and its seems to be the same. It has a red squere on the node, and it does not output.

// @ts-nocheck
if (msg.payload == "change") {
    var state = flow.get("button_2") || "off";
    if (state == "off") {
        state = "on";
        node.status({fill:"green",shape:"dot",text:"Button_2 is ON"});
        msg.payload = 1; // set thois to waht you want to pass on

    }
    else if (state == "on") {
        state = "off";
        node.status({fill:"red",shape;"dot",text:"Button_2 is OFF"});
        msg.payload = 0; // Set this to what you want to pass on
    }
    flow.set("button_2",state);
    return msg;
}

else if (msg.payload == "reset") {
    flow.set("button_2","off");
    node.status({fill:"red",shape:"dot",text:"Button_2 is OFF"});
    msg.payload = 0; // Set this to waht you want to pass on 
    return msg;
}

did I do something wrong?

since nothing is wired up its a gpio in node, next to a injection node. The gpio node is the one that is switching randomly by itself. No physical button attached yet. The zero under the button 1gp keept switching between 1 and 0.

The colon after shape is a semi colon.

1 Like


this happens without touching a thing.

should i try another pin?

thank you, its damn hard to see sometimes. Now its working.

Unless you control it, the value at a GPIO pin may well change randomly. Even before you connect a physical button you need to control the pin.
The GPIO nodes have various options for controlling the pin:
Input or output (seperate node for each)
Pull-up vs pull-down resistor stabilises the output to 1 or 0 - until something happens to change it.
Debounce time - at the millisecond/nanometre scale, physical button contacts can actually bounce. This setting filters out such noise.

Note that connecting the wrong thing to a GPIO pin can very easily fry your Pi.

Me too. Take a look at pinout.xyz for a good view and explanation of individual pins.
The only pin numbers you need to use in Node-red are the physical numbers - odd on one side, even on the other and the GPIO number. You should probably aim to use pins which that site shows with a green circle.

Okey, after the correction I am now happy to say that button 3,4 and 5 are working as intended. Thank you.

Only thing left it the more complex button 2 and the buggy button 1.

but how do I prevent it from doing this? should I just use a different pin, this one has RxD behind it, maybe thats the reason?

Firstly, you have not got the debug node connected directly to the gpio node.

If it still does it, then if you have not connected a signal to it, it does not know whether it should be high or low. I am not at my pc, but I think that you can tell the pi to apply an internal pull up to the pin, so do that.

If you read to the end of the Tutorial there is a link to the Node-RED flow (json file) which would have saved you typing in all the code. I need to make this link easier to find for people (but not my students).

For future reference, here's a direct link to the flow.
http://resources-area.co.uk/node-red-flows/having_fun_with_buttons.json

I'll restate what I said yesterday but with some gaps and emphasis in the hope that you can spot the clues.

Basically you need to tell the Pi whether the pin should normally be high or low.
It does that by applying an internal resistor.

It can't hurt.

Ill try and find a resistor and make a small board so I can route my wires though them.

There seems to be some language difficulty between us.

I can't help you any further, sorry.