Is anyone here interested in Train Signals and how they work?

I just know I'm going to regret asking... but what is FF.
I know what FNR stands for, I've called it that many times, but what is FF?

FireFox.

The hint is the top right corner of the second screen. Sorry: the other right (wink)

Activities Firefox Web Browser

See new thread about sub-flows. Using Chrome I am having problems with sub-flows using it too.

Chrome has probelms too

1 Like

is anyone here interested in train signals and how they work

YES! & What a great project!!

Years ago I did a LoRa RF & Node-Red POC for monitoring fixed block control equipment for UK rail network. It looks like Sydney in the process of moving from fixed block to moving block signaling.

Great explanation fixed block and moving block signaling
View from 50 sec to 3 min

// Some grossly simplified bits so from my old notes.

Fixed block systems use an electrical track circuit to detect when train is present or not. Original systems used an AC signal to a relay when a train was on the section of track it would short (shunt) and relay would change state to indicate when a block was occupied/not. When continuous tracks were introduced tuned frequencies replaced simple AC signal.

Communications-Based Train Control (CBTC) new systems use Radio (2.4gHz not WiFi) to communicate between central control system track-side equipment and train. Track-side transponders give exact location of a train when passes over it.

For those Brits interested one reason for late completion of Cross Rail is the (CBTC) system is split between Invensys & Siemens for track-side/Central control and Bombardier (on-train controls) looks like systems integration is done from scratch on each rail system! Due to late construction, testing has been delayed and Bugs!! found in both systems along with RF issues at Heathrow.

Lawrence

1 Like

Got a bit bored this afternoon so I knocked-up this demo flow. It has a finite state machine (FSM) for each of the 6 signals and an animated slider to mimic a train journey. To keep things simple there is only one buffer block (caution block) - you can obviously modify that by introducing additional blocks and signals.

BOD means Block Occupancy Detector.

Hope you find it useful.



The Node-RED flow is below as together with the screenshots it exceeded the 32,000 character limit.

Here's the NR-flow.

[{"id":"e6af7661.98a67","type":"inject","z":"7dd0692d.3c26f","name":"Clock Pulse","topic":"","payload":"1","payloadType":"num","repeat":"1","crontab":"","once":true,"onceDelay":"1","x":130,"y":200,"wires":[["f96a8d90.ddf9a","5598a61a.4b0218","82a2afd3.5536f8","322e947e.fe86cc","c3235ddb.872fb","eb6dd745.74aac8","3830eb14.bfe52c"]]},{"id":"bca42931.22a958","type":"ui_slider","z":"7dd0692d.3c26f","name":"","label":"Train","tooltip":"","group":"44ba51bf.ff26b","order":2,"width":"14","height":"1","passthru":true,"outs":"all","topic":"","min":0,"max":"500","step":"10","x":550,"y":200,"wires":[["46e70695.7297c8","379335cb.710c12","40b82f33.63d028","6fe4ada3.bdc7a4","147c6ec9.b34cb9","7c8dc833.268ac"]]},{"id":"3830eb14.bfe52c","type":"function","z":"7dd0692d.3c26f","name":"Check if \"running\"","func":"var status = flow.get(\"status\") || \"stopped\";\n\nif (status == \"running\") {\n    var distance=flow.get(\"distance\") || 0;\n    msg.payload = distance;\n\n    if (distance < 500) {\n        distance = distance + 10;\n        flow.set(\"distance\",distance);\n        return msg;\n    }\n    else {\n        flow.set(\"status\",\"stopped\");\n    }\n}","outputs":1,"noerr":0,"x":350,"y":200,"wires":[["bca42931.22a958"]]},{"id":"46e70695.7297c8","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-1","func":"if ((msg.payload > 20) && (msg.payload < 110)) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_1\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_1\",\"off\");\n    return msg;\n}","outputs":1,"noerr":0,"x":740,"y":100,"wires":[["30ed5c4b.be225c"]]},{"id":"c5a4b482.79a1f","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"24d134c4.309bfc","order":0,"width":"2","height":"2","passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"0","payloadType":"num","topic":"","x":130,"y":80,"wires":[["4801205a.c0f248"]]},{"id":"51bedd23.d1c9d4","type":"inject","z":"7dd0692d.3c26f","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":170,"y":120,"wires":[["4801205a.c0f248"]]},{"id":"4801205a.c0f248","type":"function","z":"7dd0692d.3c26f","name":"Set initail conditions","func":"flow.set(\"block1_fsm_state_register\",\"red\");\nflow.set(\"block2_fsm_state_register\",\"red\");\nflow.set(\"block3_fsm_state_register\",\"red\");\nflow.set(\"block4_fsm_state_register\",\"red\");\nflow.set(\"block5_fsm_state_register\",\"red\");\nflow.set(\"block6_fsm_state_register\",\"red\");\n\nflow.set(\"distance\",0);\nflow.set(\"status\",\"stopped\");\n\nmsg.payload = 0;\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":80,"wires":[["bca42931.22a958"]]},{"id":"1fc6e207.120f26","type":"ui_text","z":"7dd0692d.3c26f","group":"689d4556.e51cb4","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":520,"wires":[]},{"id":"461cfa31.a538d4","type":"ui_text","z":"7dd0692d.3c26f","group":"689d4556.e51cb4","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":480,"wires":[]},{"id":"72632ee.682d2d","type":"ui_text","z":"7dd0692d.3c26f","group":"689d4556.e51cb4","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":440,"wires":[]},{"id":"6480f9c6.cf1818","type":"ui_text","z":"7dd0692d.3c26f","group":"adb35923.fef558","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":760,"wires":[]},{"id":"d8a3d820.0bfbd","type":"ui_text","z":"7dd0692d.3c26f","group":"75a1505d.cf3df8","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":440,"wires":[]},{"id":"981c54c5.36a2e8","type":"ui_text","z":"7dd0692d.3c26f","group":"d303798b.f2b6b","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":600,"wires":[]},{"id":"ef698d0b.f03e68","type":"ui_text","z":"7dd0692d.3c26f","group":"2767fc53.8a4954","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":760,"wires":[]},{"id":"db873d6b.6ac3b","type":"ui_text","z":"7dd0692d.3c26f","group":"35fb7b16.134684","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":640,"wires":[]},{"id":"b7f2633e.8644a","type":"ui_text","z":"7dd0692d.3c26f","group":"75a1505d.cf3df8","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":480,"wires":[]},{"id":"94d277d7.9fa9c8","type":"ui_text","z":"7dd0692d.3c26f","group":"d303798b.f2b6b","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":640,"wires":[]},{"id":"f29c3bb9.88efb","type":"ui_text","z":"7dd0692d.3c26f","group":"2767fc53.8a4954","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":800,"wires":[]},{"id":"c14d64c.f085198","type":"ui_text","z":"7dd0692d.3c26f","group":"adb35923.fef558","order":2,"width":"2","height":"1","name":"Yellow","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":800,"wires":[]},{"id":"4141a06c.d85c","type":"ui_text","z":"7dd0692d.3c26f","group":"35fb7b16.134684","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":680,"wires":[]},{"id":"8cb6a9db.b2bdc8","type":"ui_text","z":"7dd0692d.3c26f","group":"adb35923.fef558","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":840,"wires":[]},{"id":"35f7087.cd7fe78","type":"ui_text","z":"7dd0692d.3c26f","group":"75a1505d.cf3df8","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":520,"wires":[]},{"id":"d0991cd0.5d77e8","type":"ui_text","z":"7dd0692d.3c26f","group":"d303798b.f2b6b","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":680,"wires":[]},{"id":"a3c35e53.6cd858","type":"ui_text","z":"7dd0692d.3c26f","group":"2767fc53.8a4954","order":1,"width":"2","height":"1","name":"Red","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":1130,"y":840,"wires":[]},{"id":"2b2fe5b3.2e0ca2","type":"ui_template","z":"7dd0692d.3c26f","group":"44ba51bf.ff26b","name":"Fix the width of the dashboard to 1000px","order":2,"width":0,"height":0,"format":"<style>\n.masonry-container {\n    width: 1000px;\n}\n</style>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":820,"y":40,"wires":[[]]},{"id":"30ed5c4b.be225c","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"689d4556.e51cb4","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-1","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_1","payloadType":"str","topic":"","x":930,"y":100,"wires":[[]]},{"id":"1699869c.7115d1","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"35fb7b16.134684","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-2","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_2","payloadType":"str","topic":"","x":930,"y":140,"wires":[[]]},{"id":"a2ca93c8.97cf78","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"adb35923.fef558","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-3","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_1","payloadType":"str","topic":"","x":930,"y":180,"wires":[[]]},{"id":"f3641b7e.57cae","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"2767fc53.8a4954","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-6","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_6","payloadType":"str","topic":"","x":930,"y":320,"wires":[[]]},{"id":"453d94d2.44cfc4","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"75a1505d.cf3df8","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-4","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_4","payloadType":"str","topic":"","x":930,"y":240,"wires":[[]]},{"id":"9a4c50fd.8035f8","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"d303798b.f2b6b","order":4,"width":"2","height":"1","passthru":false,"label":"BOD Blk-5","tooltip":"","color":"","bgcolor":"{{msg.background}}","icon":"","payload":"bod_blk_5","payloadType":"str","topic":"","x":930,"y":280,"wires":[[]]},{"id":"d0e0d730.7d789","type":"ui_button","z":"7dd0692d.3c26f","name":"","group":"24d134c4.309bfc","order":0,"width":"2","height":"2","passthru":false,"label":"Start-Stop","tooltip":"","color":"","bgcolor":"","icon":"","payload":"0","payloadType":"num","topic":"","x":150,"y":40,"wires":[["850483db.dc1d8"]]},{"id":"850483db.dc1d8","type":"function","z":"7dd0692d.3c26f","name":"Toggle start-stop button","func":"var status = flow.get(\"status\") || \"stopped\";\n\nif (status == \"stopped\") {\n    status = \"running\";\n}\nelse {\n    status =\"stopped\";\n}\n\nflow.set(\"status\",status);\n","outputs":0,"noerr":0,"x":430,"y":40,"wires":[]},{"id":"379335cb.710c12","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-2","func":"if ((msg.payload > 100) && (msg.payload < 190)) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_2\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_2\",\"off\");\n    return msg;\n}\n","outputs":1,"noerr":0,"x":740,"y":140,"wires":[["1699869c.7115d1"]]},{"id":"f96a8d90.ddf9a","type":"function","z":"7dd0692d.3c26f","name":"block_1_fsm","func":"var fsm_state = flow.get(\"block1_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_1 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( {payload:\"yellow\"} );\n        }\n        else if ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"yellow\":\n        if ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"on\") ){\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"green\":\n        if ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"off\") ) {   \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if  ( (bod_blk_1 == \"off\") && (bod_blk_2 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if (bod_blk_1 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block1_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"x":150,"y":480,"wires":[["49640562.765e3c"]]},{"id":"49640562.765e3c","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":330,"y":480,"wires":[["72632ee.682d2d"],["461cfa31.a538d4"],["1fc6e207.120f26"]]},{"id":"40b82f33.63d028","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-3","func":"if ((msg.payload > 180) && (msg.payload < 290)) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_3\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_3\",\"off\");\n    return msg;\n}\n","outputs":1,"noerr":0,"x":740,"y":180,"wires":[["a2ca93c8.97cf78"]]},{"id":"6fe4ada3.bdc7a4","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-4","func":"if ((msg.payload > 280) && (msg.payload < 360)) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_4\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_4\",\"off\");\n    return msg;\n}\n","outputs":1,"noerr":0,"x":740,"y":240,"wires":[["453d94d2.44cfc4"]]},{"id":"147c6ec9.b34cb9","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-5","func":"if ((msg.payload > 350) && (msg.payload < 440)) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_5\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_5\",\"off\");\n    return msg;\n}\n","outputs":1,"noerr":0,"x":740,"y":280,"wires":[["9a4c50fd.8035f8"]]},{"id":"7c8dc833.268ac","type":"function","z":"7dd0692d.3c26f","name":"Check for Blk-6","func":"if (msg.payload > 430) {\n    msg.background = \"red\";\n    flow.set(\"bod_blk_6\",\"on\");\n    return msg;\n}\nelse {\n    msg.background = \"#097479\";\n    flow.set(\"bod_blk_6\",\"off\");\n    return msg;\n}\n","outputs":1,"noerr":0,"x":740,"y":320,"wires":[["f3641b7e.57cae"]]},{"id":"abb8d8db.ab8208","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":330,"y":640,"wires":[["996e0cb.8134bf"],["db873d6b.6ac3b"],["4141a06c.d85c"]]},{"id":"5598a61a.4b0218","type":"function","z":"7dd0692d.3c26f","name":"block_2_fsm","func":"var fsm_state = flow.get(\"block2_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_2 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( {payload:\"yellow\"} );\n        }\n        else if ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"yellow\":\n        if ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"on\") ){\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"green\":\n        if ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"off\") ) {   \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if  ( (bod_blk_2 == \"off\") && (bod_blk_3 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if (bod_blk_2 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block2_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"x":150,"y":640,"wires":[["abb8d8db.ab8208"]]},{"id":"996e0cb.8134bf","type":"ui_text","z":"7dd0692d.3c26f","group":"35fb7b16.134684","order":3,"width":"2","height":"1","name":"Green","label":"","format":"<font color={{msg.payload}} ><i class=\"fa fa-circle\" style=\"font-size:24px;\"></i></font>","layout":"col-center","x":490,"y":600,"wires":[]},{"id":"5ec436fa.73c06","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":330,"y":800,"wires":[["6480f9c6.cf1818"],["c14d64c.f085198"],["8cb6a9db.b2bdc8"]]},{"id":"82a2afd3.5536f8","type":"function","z":"7dd0692d.3c26f","name":"block_3_fsm","func":"var fsm_state = flow.get(\"block3_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_3 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( {payload:\"yellow\"} );\n        }\n        else if ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"yellow\":\n        if ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"on\") ){\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"green\":\n        if ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"off\") ) {   \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if  ( (bod_blk_3 == \"off\") && (bod_blk_4 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if (bod_blk_3 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block3_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"x":150,"y":800,"wires":[["5ec436fa.73c06"]]},{"id":"67c15f9.dfd772","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":970,"y":480,"wires":[["d8a3d820.0bfbd"],["b7f2633e.8644a"],["35f7087.cd7fe78"]]},{"id":"322e947e.fe86cc","type":"function","z":"7dd0692d.3c26f","name":"block_4_fsm","func":"var fsm_state = flow.get(\"block4_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_4 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( {payload:\"yellow\"} );\n        }\n        else if ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"yellow\":\n        if ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"on\") ){\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"green\":\n        if ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"off\") ) {   \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if  ( (bod_blk_4 == \"off\") && (bod_blk_5 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if (bod_blk_4 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block4_fsm_state_register\", fsm_state);\n","outputs":1,"noerr":0,"x":790,"y":480,"wires":[["67c15f9.dfd772"]]},{"id":"e2c24607.959458","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":970,"y":640,"wires":[["981c54c5.36a2e8"],["94d277d7.9fa9c8"],["d0991cd0.5d77e8"]]},{"id":"c3235ddb.872fb","type":"function","z":"7dd0692d.3c26f","name":"block_5_fsm","func":"var fsm_state = flow.get(\"block5_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_5 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( {payload:\"yellow\"} );\n        }\n        else if ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"yellow\":\n        if ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"on\") ){\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"off\") ) {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        break;\n       \n        case \"green\":\n        if ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"off\") ) { \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if  ( (bod_blk_5 == \"off\") && (bod_blk_6 == \"on\") ) {\n            fsm_state = \"yellow\";\n            node.status({text:\"State register = yellow\"});\n            node.send( [{payload:\"yellow\"}] );\n        }\n        else if (bod_blk_5 == \"on\") {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block5_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"x":790,"y":640,"wires":[["e2c24607.959458"]]},{"id":"ff29f5fb.afd138","type":"function","z":"7dd0692d.3c26f","name":"LED decoder","func":"if (msg.payload == \"red\") {\n    node.send( [{payload:\"grey\"},{payload: \"grey\"},{payload: \"red\"}] );\n}\nelse if (msg.payload == \"yellow\") {\n    node.send( [{payload:\"grey\"},{payload: \"yellow\"},{payload: \"grey\"}] );\n}\nelse if (msg.payload == \"green\") {\n    node.send( [{payload:\"green\"},{payload: \"grey\"},{payload: \"grey\"}] );\n}","outputs":3,"noerr":0,"x":970,"y":800,"wires":[["ef698d0b.f03e68"],["f29c3bb9.88efb"],["a3c35e53.6cd858"]]},{"id":"eb6dd745.74aac8","type":"function","z":"7dd0692d.3c26f","name":"block_6_fsm","func":"var fsm_state = flow.get(\"block6_fsm_state_register\") || \"red\";\n\nvar bod_blk_1 = flow.get(\"bod_blk_1\") || 1;\nvar bod_blk_2 = flow.get(\"bod_blk_2\") || 1;\nvar bod_blk_3 = flow.get(\"bod_blk_3\") || 1;\nvar bod_blk_4 = flow.get(\"bod_blk_4\") || 1;\nvar bod_blk_5 = flow.get(\"bod_blk_5\") || 1;\nvar bod_blk_6 = flow.get(\"bod_blk_6\") || 1;\n\nswitch (fsm_state)\n   {\n        case \"red\":\n        if (bod_blk_6 == \"on\") \n        {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( {payload:\"red\"} );\n        }\n        else if (bod_blk_6 == \"off\") \n        {\n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( {payload:\"green\"} );\n        }\n        break;\n       \n        case \"green\":\n        if (bod_blk_6 == \"off\")\n        {   \n            fsm_state = \"green\";\n            node.status({text:\"State register = green\"});\n            node.send( [{payload:\"green\"}] );\n        }\n        else if (bod_blk_6 == \"on\")\n        {\n            fsm_state = \"red\";\n            node.status({text:\"State register = red\"});\n            node.send( [{payload:\"red\"}] );\n        }\n        break;\n   }\n\nflow.set(\"block6_fsm_state_register\", fsm_state);\n\n","outputs":1,"noerr":0,"x":790,"y":800,"wires":[["ff29f5fb.afd138"]]},{"id":"44ba51bf.ff26b","type":"ui_group","z":"","name":"Simulated train journey","tab":"73fa038.3bc0ffc","order":8,"disp":true,"width":"14","collapse":false},{"id":"24d134c4.309bfc","type":"ui_group","z":"","name":"Sim Ctrl","tab":"73fa038.3bc0ffc","order":1,"disp":true,"width":"2","collapse":false},{"id":"689d4556.e51cb4","type":"ui_group","z":"","name":"Block 1","tab":"73fa038.3bc0ffc","order":2,"disp":true,"width":"2","collapse":false},{"id":"adb35923.fef558","type":"ui_group","z":"","name":"Block 3","tab":"73fa038.3bc0ffc","order":4,"disp":true,"width":"2","collapse":false},{"id":"75a1505d.cf3df8","type":"ui_group","z":"","name":"Block 4","tab":"73fa038.3bc0ffc","order":5,"disp":true,"width":"2","collapse":false},{"id":"d303798b.f2b6b","type":"ui_group","z":"","name":"Block 5","tab":"73fa038.3bc0ffc","order":6,"disp":true,"width":"2","collapse":false},{"id":"2767fc53.8a4954","type":"ui_group","z":"","name":"Block 6","tab":"73fa038.3bc0ffc","order":7,"disp":true,"width":"2","collapse":false},{"id":"35fb7b16.134684","type":"ui_group","z":"","name":"Block 2","tab":"73fa038.3bc0ffc","order":3,"disp":true,"width":"2","collapse":false},{"id":"73fa038.3bc0ffc","type":"ui_tab","z":"","name":"Block Signalling","icon":"dashboard","order":41,"disabled":false,"hidden":false}]

Nice.
(I've only just loaded it.)

You mentioned in the first post the fsm.

(I have it installed but never got into it)

Where in the flow it is?

I think you might be thinking of the node-red node called 'node-red-contrib-dsm'.

My flow doesn't use any of those nodes, I've coded the finite state machines using JavaScript in the six function nodes called 'block_X_fsm' (where X ranges from 1 to 6).

Hope you find the flow useful.

My original idea was to have a stationary train sitting on one of the blocks so when the moving train caught up with it, it would stop. I just ran out of time this afternoon - sorry.

Ah! Sorry. That explains it.

DSM is way (WAY!) above my skill set. Thus I have never tried to use them or write one.

On my dahsboard the slider doesn't lie under the LEDs, but I get the idea.

I'll have to look at how it works. But a I said, I am not much on the black arts of JS or DSM.

In the mean time I have (yet again) re-written the sub-flow and have found/seen some weird things happening with the GUI side of things using both FireFox and Chrome and CPU load.

Adding to the weirdness, exiting the browser resets the CPU load. Strange as it is NR which is creating the load. I would have thought that I would need to stop/start (or restart) Node-Red.

Anyway, again: Thanks.

You could try altering the pixel value in the ui-template node (maybe reduce it to 1200 or even 1000) and see if that improves the layout. I'm using FF Nightly version 73.0a1 (2020-01-04) and Node-RED v1.0.2

Nice thread. Please keep us updated on how things progress. :slight_smile:

A thought... Would it make sense to look at how hardware block detectors work, then mimic them in software? For example, I haven't played with model trains in years (busy with family, work, etc), but years ago I saw a pretty simple schematic for a block detector, and (if my memory serves me), it had the following inputs and outputs:

Inputs

  • Input from detector for current block
  • Input from previous block detector.
  • Input from next block detector.

Outputs

  • Output to LEDs (red, yellow and green).
  • Output to previous block detector.
  • Output to next block detector.

The idea was that you basically daisy chain all the block detectors together. Each block detector needed to know the state of the previous and next block detectors in order to help decide which LEDs were on for this block. This made the whole system very scale-able as you just add another block detector when you decided to break up a section of track into two blocks.

I just looked for the schematic online and, of course, I couldn't find it, but I believe the circuit was pretty simple.

@dynamicdave - I'm a bit confused by your flow, as I don't see how a given block detector know what's going on in the previous or next block. What am I missing here?

-Jon

Hi @JonS

Originally the flow was a mess. Then I got into the sub flow idea and started to modularise the workings.

There have been a few versions of that.

I have a working model which handles a train going along the track with all being ok.

However, when I want to close a section of track and have the train stop there and then release the train: it all falls apart.

I can get the train to stop and you can see that nicely.

But releasing it........ Whole other problem.

Also: a couple of blocks up from where the train is, I change the track - like set a point - the signals overwrite the ones set at the one where the train is.

I am just not quite getting the logic/flow/sequence/how to do that.

I need a bigger piece of paper to get that working. (from JAWS: I think we need a bigger boat)

I agree that knowing how the real ones work would help. Alas I don't and am so speculating on what is known.

Hi @Trying_to_learn.

Gotcha. I'll be keeping an eye on this thread, as I have an interest in what you're working on. Every Halloween (weather permitting) I setup an HO "Haunted Train" layout for the trick-or-treaters to gawk at. Right now it's a simple oval with a single train, but I'm hoping that this year I can expand it and have an oval with a siding on it. I'd have two trains -- a normal looking train (with people, animals, etc), and a "haunted" train (with skeleton people, skeleton animals, etc). The siding will be hidden in a mountain, and what would happen is first the normal train comes out the end of a tunnel and makes a complete loop. Then it goes into the entrances of the tunnel where (out of sight of the viewers) that train stops on a siding and the haunted train starts up. The haunted train comes out the end of the tunnel and makes a loop. The haunted train would loop and enter the tunnel, park on its siding, then the normal train would come out. This alternating-trains would continue indefinitely.

I am going to see if I can do this logic using Node-RED and "Arduino Railway Control" (https://arduinorailwaycontrol.com/). I'd also set it up to control some exterior lights such as an overhead black light and an overhead spot light -- the black light would be on while the haunted train is making its run, and the non-black light would be on for the normal train.

Ideally, I want to also add signals to the layout, and hopefully control them via Node-RED (like everything else will be controlled).

Keep up the good work! :slight_smile:

-Jon

PS. Love the Jaws reference. And when you mentioned Jaws, suddenly the following popped into my head (sung to the "Baby Shark" tune):

Node-RED flow, doo doo doo doo doo doo
Node-RED flow, doo doo doo doo doo doo
Node-RED flow, doo doo doo doo doo doo
Node-RED flow!

Ok, I either need more coffee, or I've had too much today... :wink:

1 Like

:slight_smile: @JonS

Not to dismiss your thoughts, but..... On the model train:

Ok, I get the idea. Sound like fun. Though there are many other systems (I think it is called DCC as one example) to control trains, here's a thought on how you could do what you want easily.

What I get so far:
1 - there is a loop. Be it somewhat extended.
2 - there is a "turn out" / "side track" which is hidden in the mountain where you can switch trains.

Ok, my idea:
The track has three parts.
1 - Siding #1
2 - Siding #2
3 - The remainder of the track.

Depending on how the points work nowadays there are a couple of ways to do this. *originally* - when I played with model trains - the points did **not** isolate the dead part of the track. Only the part of the track which was connected was *live*. If you have the latter, even easier. (See later)

Needed:
1 - For worst case scenario you need 2 relays. (2 pole Change over)
2 - Two small magnets to stick under each engine/loco'.
3 - Two reed switches to put under the track in each siding.
4 - Electronically controlled points. (4 in total)

Idea:
The points are set for siding #1 to be the one in use.
Power is already on the main part of the track.
Push the start button and relay #1 activates and powers Train #1 in *siding #1.
Train #1 pulls out and does its stuff.
The reed switch counts now many times around the loop it goes.
When you want to switch trains, push button #2
When train #1 passes over the switch, it is detected.
Power is turned off to that siding. Train #1 stops.
The points are changed to siding #2.
Power is then put onto siding #2 and Train #2 powers up.
Train #2 does it's thing.
When you want to change back to train #1, the process is basically repeated but swapping train #1 and #2 - of course.

I can draw up a basic schematic if you want.

Sure NR could be used to control it. But that's my take on the how to do it.
Though I think even an Arduino could handle that scenario.

Now, to the points:
If they isolate the dead part of the track, that will kind of help, as it will help with track isolation. All the three parts are going to be powered from the same supply anyway.
Though, now thinking about it, those kind of points may not really help. I think the relays would be better.

Hi Jon,
Although my flow might look complicated, it is in fact a couple of flows replicated six times.

As the train moves along on its journey, the numeric value of the UI-slider is decoded by the six function blocks (BOD) and used to set/clear six flow variables (BOD_blk_1 to BOD_blk_6) and also illuminate the appropriate UI-button.

Once every second the inject node fires. This is like a clock-pulse in a real hardware system.

The pulse triggers all six finite state machines which run through some JavaScript that basically uses the current state of each signal (e.g. is it red, yellow or green ?) and also checks the flow-variable for the current block (BOD_blk_X) and the flow-variable for the next block (BOD_blk_X+1).

Note:
To keep things simple in this demonstration flow the train only moves from left to right, so it only needs to check the current and the next block.

Depending on the occupancy of the train-track-blocks each finite state machine can move from one state to another. Have a look at the JavaScript in one of the FSM blocks and you'll see what I mean.

All the state machines, apart from block_6_fsm, use the same algorithm.
Block_6 is special in this simple mock-up as there isn't a block ahead of it.

Hope this explanation helps you to understand the flow a bit more. Please ask if you need more details.

My original idea was to have a stationary train sitting on one of the blocks so when the moving train caught up with it, it would stop. I just ran out of time when I coded the flow a week or so ago.