MQTT garage door opener with Node-RED

Thanks Julian,

appears to be very correct and off-topic. :smile:

All i was looking for is some flow export that help me progress further with adding sensors,kill-switches, photocells etc to a heavy gate and slide motorised.

Maybe but then you probably don't want random hackers opening your garage door! :scream:

2 Likes

Again thanks Julian

i can picture hackers in a van parked outside my premises. i'm going to power-off the grid to the house...ROFL

Actually, you might have missed an important point. Most hackers are not (at least initially) targeting you are a person/place. They are running large-scale botnets looking for weaknesses or looking for "interesting" things to play with (depending on the type). Initial stages are automated. Access may be sold on via marketplaces to others.

As we've previously seen though already and reported on this forum, it only takes a single interested hacker with time on their hands to exploit something. Weak VPN's are of interest, IoT devices certainly so.

Yes, the individual risk for any one person/location is very low unless you are a person of interest. But it isn't zero and the more people who do something, the higher the overall risk. The longer you leave a potential weakness, the worse it gets and the more you forget about it until it becomes a real problem.

Best to avoid in my view.

Anyway, enough from me, if I haven't convinced you - perhaps more importantly others reading this thread - to take care, I don't need to labour the points any further.

Julian,

i'm convinced already before you jumped on. :smile: To share i'm liking the idea to drop the VPN or do required hardening on the home network. that's actually my full time job for the last 15 years. Doing pentration testing and stuff is fun and helpful.

here i'm more of a baby with node-red flows almost zero programming skills, was hoping to get some cookie-cutter flow export if someone's already using so that i can get that deployed for my 2 gates and i tweak them further later on after motorized.

Attaching one visual studio code that i tried to put on nodemcu using platformIO but couldn't. i comparatively find NodeRed more pleasant, if someone can make a NR-flow with similar logic would be awesome. not sure how to get help on this

gate-main-code.txt (6.2 KB)

1 Like

I think your application probably lends itself to using a Finite State Machine (FSM) as you go from one state to another depending on events/conditions. I'm busy until the weekend, but as soon as I have a spare moment I'll rough-out some ideas for you. In the meantime, here's a link to a FSM I created for a Block Signalling scheme 'simulation' for the forum member... @Trying_to_learn.

Block Signalling scheme (simulation)

The FSMs are written in Javascript and are located in the 'function' blocks. In your application you would only need one FSM.

Note:
@cflurin created 'node-contrib-dsm' which removes some of the complexity of writing a FSM.

Dave,

Is it something that can help me?

Kind of went above my head....lol

See: GitHub - cflurin/node-red-contrib-dsm: A dynamic state machine node based on the Finite state machine.

Here's a tutorial I wrote for my IoT students which may give you an insight into FSMs.

FSM tutorial

The above example is for a UK-based traffic light and just goes from one state to another state in a circular sequence. In your application you would go from one state to another depending on the button that was pressed (e.g. keyfob) and the states/events of the system (e.g. limit switches, emergency stop, etc..)

I'll sketch out a state diagram for you when I have some spare time.

Sorry to harp back to the safety issue again, but it is imperative that the safety interlocks should be a completely separate system, so you need both a failure of the node red control system and also a failure of the interlocks to kill someone. If you were already planning that then sorry for bringing it up again.

3 Likes

I got a bit bored this evening and had a go at writing a Node-RED flow to simulate your gates.

Most of the Node-RED flow is concerned with running the simulation. I've annotated the flow so you can see where the finite state machine (FSM) is located which does all the clever stuff.

There's quite a bit of code to interlock the push buttons so the user can't do 'silly things'.

Last of all... I think the simulation looks best when you use the Dark theme.

Please ask if you have any questions about the code. I dare say the code can be simplified.

[{"id":"d0383a44.bb6f98","type":"inject","z":"b9712f38.cc3d58","name":"Clock Pulse Generator","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"1","payloadType":"num","x":190,"y":260,"wires":[["7ba8c160.c38be"]]},{"id":"ff4aca1c.1649a8","type":"ui_slider","z":"b9712f38.cc3d58","name":"Left-hand gate","label":"","tooltip":"","group":"173d5c59.768484","order":2,"width":"6","height":"1","passthru":true,"outs":"all","topic":"","min":0,"max":"100","step":"10","x":780,"y":280,"wires":[["1892f88e.72326f","f795e3b4.d9a9c"]]},{"id":"1892f88e.72326f","type":"function","z":"b9712f38.cc3d58","name":"Simulate CLOSED limit switch","func":"if (msg.payload >= 90) {\n    msg.background = \"red\";\n    flow.set(\"closed_limit_switch\",\"activated\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"closed_limit_switch\",\"inactive\");\n    return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1030,"y":260,"wires":[["a1111341.3f4538"]]},{"id":"22d44bc2.91d3f4","type":"inject","z":"b9712f38.cc3d58","name":"Set initial conditions","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":180,"y":80,"wires":[["2a70b209.4e23c6"]]},{"id":"2a70b209.4e23c6","type":"function","z":"b9712f38.cc3d58","name":"Set initail conditions","func":"flow.set(\"position\",0);\n\nflow.set(\"gate_fsm_state_register\",\"stopped\");\n\nflow.set(\"button_pressed\",\"stop\");\n\n// flow.set(\"simulation_status\",\"stopped\");\n\nmsg.payload = 100;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":420,"y":80,"wires":[["ff4aca1c.1649a8","72893f84.1fafa8","3c5014dc.9fe7a4"]]},{"id":"d20a6897.b9b85","type":"ui_template","z":"b9712f38.cc3d58","group":"173d5c59.768484","name":"Fix the width of the dashboard to 800px","order":1,"width":0,"height":0,"format":"<style>\n.masonry-container {\n    width: 800px;\n}\n</style>\n<style>\nbutton{\n    text-transform: none !important;\n}\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":880,"y":40,"wires":[[]]},{"id":"c723b57f.ba441","type":"ui_button","z":"b9712f38.cc3d58","name":"Open limit switch","group":"b830026.09b9e8","order":3,"width":"3","height":"1","passthru":false,"label":"Open Limit Switch","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"open_limit_switch","payloadType":"str","topic":"","x":1290,"y":300,"wires":[[]]},{"id":"a1111341.3f4538","type":"ui_button","z":"b9712f38.cc3d58","name":"Closed limit switch","group":"b830026.09b9e8","order":1,"width":"3","height":"1","passthru":false,"label":"Closed Limit Switch","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"closed_limit_switch","payloadType":"str","topic":"","x":1290,"y":260,"wires":[[]]},{"id":"f795e3b4.d9a9c","type":"function","z":"b9712f38.cc3d58","name":"Simulate OPENED limit switch","func":"if (msg.payload <= 20) {\n    msg.background = \"red\";\n    flow.set(\"opened_limit_switch\",\"activated\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"opened_limit_switch\",\"inactive\");\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1030,"y":300,"wires":[["c723b57f.ba441"]]},{"id":"7ba8c160.c38be","type":"function","z":"b9712f38.cc3d58","name":"gate_fsm","func":"var fsm_state = flow.get(\"gate_fsm_state_register\") || \"stopped\";\n\nvar closed_limit_switch = flow.get(\"closed_limit_switch\") || \"activated\";\nvar opened_limit_switch = flow.get(\"opened_limit_switch\") || \"activated\";\n\nvar button_pressed = flow.get(\"button_pressed\") || \"stopped\";\n\nswitch (fsm_state)\n   {\n        case \"stopped\":\n        if ( (button_pressed == \"open\") && (opened_limit_switch == \"inactive\") ) {\n            fsm_state = \"opening\";\n            node.status({text:\"State register = Opening\"});\n            node.send( {payload:\"opening\"} );\n        }\n        else if ( (button_pressed == \"close\") && (closed_limit_switch == \"inactive\") ) {\n            fsm_state = \"closing\";\n            node.status({text:\"State register = Closing\"});\n            node.send( {payload:\"closing\"} );\n        }\n        break;\n       \n        case \"opening\":\n        if  ( (button_pressed == \"stop\") || (opened_limit_switch == \"activated\") ) {\n            fsm_state = \"stopped\";\n            node.status({text:\"State register = Stopped\"});\n            node.send( [{payload:\"stopped\"}] );\n            flow.set(\"button_pressed\",\"stop\");\n        }\n        else {  \n            fsm_state = \"opening\";\n            node.status({text:\"State register = Opening\"});\n            node.send( {payload:\"opening\"} );\n        }\n        break;\n       \n        case \"closing\":\n        if  ( (button_pressed == \"stop\") || (closed_limit_switch == \"activated\") ) {\n            fsm_state = \"stopped\";\n            node.status({text:\"State register = stopped\"});\n            node.send( [{payload:\"stopped\"}] );\n            flow.set(\"button_pressed\",\"stop\");\n        }\n        else {\n            fsm_state = \"closing\";\n            node.status({text:\"State register = Closing\"});\n            node.send( {payload:\"closing\"} );\n        }\n        break;\n   }\n\nflow.set(\"gate_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":240,"y":320,"wires":[["658e87aa.a6e538","42c4a527.320d1c","3c5014dc.9fe7a4"]]},{"id":"72893f84.1fafa8","type":"ui_slider","z":"b9712f38.cc3d58","name":"Right-hand gate","label":"","tooltip":"","group":"173d5c59.768484","order":3,"width":"6","height":"1","passthru":true,"outs":"all","topic":"","min":"100","max":"0","step":"10","x":780,"y":360,"wires":[[]]},{"id":"e85840f.82c754","type":"ui_button","z":"b9712f38.cc3d58","name":"Motor-left state","group":"b830026.09b9e8","order":2,"width":"3","height":"1","passthru":false,"label":"{{msg.topic}}","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"","payloadType":"str","topic":"","x":780,"y":440,"wires":[[]]},{"id":"a84bfea6.58983","type":"ui_button","z":"b9712f38.cc3d58","name":"Motor-right state","group":"b830026.09b9e8","order":2,"width":"3","height":"1","passthru":false,"label":"{{msg.topic}}","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"","payloadType":"str","topic":"","x":790,"y":480,"wires":[[]]},{"id":"d80acb77.2220c8","type":"ui_button","z":"b9712f38.cc3d58","name":"Open","group":"876a6d6.be7489","order":2,"width":"3","height":"1","passthru":false,"label":"Open","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"btn_open","payloadType":"str","topic":"","x":170,"y":640,"wires":[["47128f2a.e3958"]]},{"id":"de7e2620.b2f658","type":"ui_button","z":"b9712f38.cc3d58","name":"Close","group":"876a6d6.be7489","order":3,"width":"3","height":"1","passthru":false,"label":"Close","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"btn_close","payloadType":"str","topic":"","x":170,"y":700,"wires":[["5cef6460.dcfb2c"]]},{"id":"8bf888fb.f8e3b8","type":"ui_button","z":"b9712f38.cc3d58","name":"STOP","group":"876a6d6.be7489","order":5,"width":"3","height":"1","passthru":false,"label":"STOP","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"btn_stop","payloadType":"str","topic":"","x":170,"y":760,"wires":[["51dc7dba.5978e4"]]},{"id":"47128f2a.e3958","type":"function","z":"b9712f38.cc3d58","name":"","func":"var opened_limit_switch = flow.get(\"opened_limit_switch\") || \"activated\";\n\nif (opened_limit_switch == \"inactive\") {\n    var button_pressed = flow.get(\"button_pressed\") || \"stop\";\n    if (button_pressed == \"close\") {\n        flow.set(\"button_pressed\", \"stop\");\n    }\n    else {\n        flow.set(\"button_pressed\",\"open\");\n    }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":640,"wires":[["3c5014dc.9fe7a4"]]},{"id":"5cef6460.dcfb2c","type":"function","z":"b9712f38.cc3d58","name":"","func":"var closed_limit_switch = flow.get(\"closed_limit_switch\") || \"activated\";\n\nif (closed_limit_switch == \"inactive\") {\n    var button_pressed = flow.get(\"button_pressed\") || \"stop\";\n    if (button_pressed == \"open\") {\n        flow.set(\"button_pressed\", \"stop\");\n    }\n    else {\n        flow.set(\"button_pressed\",\"close\");\n    }\n}\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":700,"wires":[["3c5014dc.9fe7a4"]]},{"id":"51dc7dba.5978e4","type":"function","z":"b9712f38.cc3d58","name":"","func":"flow.set(\"button_pressed\",\"stop\");\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":760,"wires":[["3c5014dc.9fe7a4"]]},{"id":"658e87aa.a6e538","type":"function","z":"b9712f38.cc3d58","name":"Decode FSM outputs for gate movement","func":"var position = flow.get(\"position\") || 100;\n\nif (msg.payload == \"opening\") { \n    // Increment gate position\n    if (position >= 10) {\n        position = position - 10;\n        flow.set(\"position\",position);\n        \n        node.status({text:\"Position =\" + position});\n        \n        msg.payload = position;\n        return msg;\n    }\n}\nelse if (msg.payload == \"closing\") {\n    // Decrement gate position\n    if (position <= 90) {\n        position = position + 10;\n        flow.set(\"position\",position);\n        \n        node.status({text:\"Position =\" + position});\n        \n        msg.payload = position;\n        return msg;\n    }\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":500,"y":320,"wires":[["ff4aca1c.1649a8","72893f84.1fafa8"]]},{"id":"3c5014dc.9fe7a4","type":"function","z":"b9712f38.cc3d58","name":"","func":"var button_pressed = flow.get(\"button_pressed\") || \"stop\";\n\nif (button_pressed == \"open\") {\n    node.send( [{background:\"red\"},{background:\"#097479\"},{background:\"#097479\"}] );\n}\nelse if (button_pressed == \"close\") {\n    node.send( [{background: \"#097479\"},{background:\"red\"},{background: \"#097479\"}] );\n}\nelse if (button_pressed == \"stop\") {\n    node.send( [{background: \"#097479\"},{background: \"#097479\"},{background:\"red\"}] );\n}\n","outputs":3,"noerr":0,"initialize":"","finalize":"","x":590,"y":700,"wires":[["d80acb77.2220c8"],["de7e2620.b2f658"],["8bf888fb.f8e3b8"]]},{"id":"42c4a527.320d1c","type":"function","z":"b9712f38.cc3d58","name":"Decode FSM outputs for the motors","func":"if (msg.payload == \"stopped\")  { \n    msg.topic = \"Motor is stopped\";\n    msg.background = \"#097479\";\n    return msg;\n\n}\nelse {\n    msg.topic =\"Motor is running\";\n    msg.background = \"red\";\n    return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":510,"y":460,"wires":[["e85840f.82c754","a84bfea6.58983"]]},{"id":"927029c7.a456e8","type":"comment","z":"b9712f38.cc3d58","name":"I think the dashboard looks best in Dark mode","info":"","x":890,"y":100,"wires":[]},{"id":"c7205543.b79dd","type":"comment","z":"b9712f38.cc3d58","name":"Readme for... FSM details","info":"This is the Finite State machine.\n\nIt has three states... \"stopped\", \"opening\" and \"closing\".\n\nIt can move between the states depending on the limit switches and/or the push buttons. ","x":290,"y":380,"wires":[]},{"id":"8bbc7156.ee8e18","type":"comment","z":"b9712f38.cc3d58","name":"Direct movement is prohibited","info":"Direct movement is prohibited\n\nThe code in the three function blocks prevents direct movement\nfrom Open to Close or Close to Open as this could damage the motors.\n\nIf the user did try to do this, the system goes into the STOPPED mode. \n\nThe function blocks also prevent a user from trying to...\n   Open  the gates if they are already OPEN\n   Close the gates if they are already CLOSED\n\n","x":340,"y":600,"wires":[]},{"id":"173d5c59.768484","type":"ui_group","z":"","name":"Simulated movement of a pair of gates","tab":"58343703.f0abb8","order":1,"disp":true,"width":"12","collapse":false},{"id":"b830026.09b9e8","type":"ui_group","z":"","name":"Gate sensors and state of the motor","tab":"58343703.f0abb8","order":2,"disp":true,"width":"12","collapse":false},{"id":"876a6d6.be7489","type":"ui_group","z":"","name":"Simulation control buttons","tab":"58343703.f0abb8","order":3,"disp":true,"width":"12","collapse":false},{"id":"58343703.f0abb8","type":"ui_tab","z":"","name":"Gate simulation","icon":"dashboard","order":41,"disabled":false,"hidden":false}]
3 Likes

Colin,

i appreciate underlining the safety and best-approach to deal with this. before giving it a final go, i broke down this safety related tasks into secondary phase planning.

a big thank you for telling me all should i expect

Dave,

i'm super excited to see this advanced flow. trying to understand as much as i can.

i have started feeling there could be misunderstanding, obviously my fault. Actually, i have 2 separate entrances/gates on both sides of my property. East & South side, both gates slide but manually. sometimes i need to park on east side and sometimes on south side. program/hardware/rails/power supplies, motors, nodemcu(s) are almost replica. Track/Gate sizes do vary though.
Note - one motor only per gate.

in the above flow, i think it's like 2 pieces of the gate coming from opposite directions to a point they get fixed. i never thought this could be possible but again wouldn't have been simpler like it's now.

Anyway, i like the UI and every button with actual concept.
closed limit switch
open limit switch
motor is running
open
close
stop

this is ultimate approach.

When you said you had two gates to open/close I thought they slid together and apart.
That's what comes of "assuming" things.

My flow will work just as well with just a single gate and one motor (it's just the animation won't match your particular situation - I might get one of my students to change it).

Hope the simulation works for you as it took a fair time to create the twin gate animation.
Looking back, it would have been easier doing a simulation for a single gate - but who cares!!

2 Likes

my sincere apologies for the big misunderstanding.

question - in this flow is there mqtt in/out nodes possibility.

If you mean the control buttons, then Yes.

But if you want to control the system from your mobile phone, then the 'telegram' node is what you'll need.

It might help if you drew a rough sketch of the geographical positions and distances of key items as people could suggest options for you.

Dave,

i am referring to use the browser on the LAN (internal home network)
attaching what it looks like

remote_app|281x500

finally understood how to use Telegram for achieving remote trigger stuff. i get it now

1 Like

i'm fascinated by this flow you created. Truly appreciated.

Did I miss it, but do you have a sensor(s) to confirm the physical motion of the gate? For my garage door, I used a infrared sensor to could the number of wheels in the rail passed the sensor. In my case I could then determine the degree of open or close of the garage door with just the one sensor. I also have a completely open and completely closed sensors, just validate same. Of course simple, magnetic switches or distance sensors work well for this of course, distance sensor can tell me where the given end-wheel stopped as applicable. I then added a progress bar to my control panel to mimic the physical position of the door.

But I did not stop there, given we had a few break ins in the area, where thieves would trip the door release with a long pole, I added a camera, so every time my garage door opens or closes, the camera is on, in real time, data copied to the web of course.

The one thing I did not do, was add actuators to move lock pins already in the door assembly. This could with a lost of power to the house, force me to release the pins manually... something in an emergency I just did not want to have to remember to do. But I have friends that have done this via various methods.

2 Likes