Teaching transient and persistent data storage

Hi

I am developing teaching resources for kids 9 - 13.
I am using node-red because it is visual / low-code.

I want to create a flow that teaches transient vs persistent data storage.
I have a momentary button attached to a Raspberry Pi.

I want to use nodes not javascript functions since they are not yet ready for that in the learning programme. I also want to use visible, obvious blocks, rather than the underlying persistence (i.e. changing settings.js).

I have used contrib-counter to increment a value in the msg object based on presses of the button.
I call this the transient count.
I can illustrate that deploy or a node-red restart or a Raspberry Pi reboot, or a loss of power results in a reset of the value.

What I need now is the button press to continue with this transient count but also:

  1. Access a file containing the running total
  2. Increment by 1
  3. Update the file
  4. Display the running total in debug

That way, the learners can press the button, seeing the transient count starting at 1, and the running total at a higher number, restart node-red and see the impact on both numbers.

I am still learning node-red myself, and I just can't get my head around this.
However, I want to struggle to the solution, as that's the best way for me to learn.
So, can someone give me the tiniest nudge in the right direction?

Thanks
Brendon

In the node palette under Storage you will see a few file nodes. You can use them for storage. If you read their respective descriptions you will get an idea of how they function.

Using the counter you already have.

Use the file nodes mentioned above.

Stick a debug node on the end.

And I should add that you will probably need a Change and/or Switch node in there as well to alter the msg properties as they move down the flow, so have a good read up on those too.

For the button you will first need to ensure the Dashboard has been installed. Once you have done that stick a ui button in the front of the above flow and it's done. :grin:

Thanks for the nudges!

Node red has persistent and non persistent context storage

so you could do similar with that, and show loss of data when node-red reboots.

Here is a simple flow that increments count and display count under debug node.

[{"id":"2f381f10.8ed8c","type":"inject","z":"57675e72.26a0d8","name":"initialise count","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":190,"y":2860,"wires":[["11604342.d265a5"]]},{"id":"11604342.d265a5","type":"change","z":"57675e72.26a0d8","name":"","rules":[{"t":"set","p":"count","pt":"flow","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":2860,"wires":[[]]},{"id":"7bac466a.ecce5","type":"inject","z":"57675e72.26a0d8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":180,"y":2920,"wires":[[]]},{"id":"98511ca.63d616","type":"debug","z":"57675e72.26a0d8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"$flowContext(\"count\")","statusType":"jsonata","x":570,"y":2920,"wires":[]},{"id":"22e60732.309208","type":"inject","z":"57675e72.26a0d8","name":"initialise count","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":2280,"wires":[["9dcc5ac4.5593e8"]]},{"id":"9c825e71.d614e","type":"inject","z":"57675e72.26a0d8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":100,"y":2340,"wires":[["5d154888.ffdf9"]]},{"id":"5d154888.ffdf9","type":"change","z":"57675e72.26a0d8","name":"","rules":[{"t":"set","p":"count","pt":"flow","to":"$flowContext(\"count\") + 1","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":2340,"wires":[["819c37d1.394e8"]]},{"id":"9dcc5ac4.5593e8","type":"change","z":"57675e72.26a0d8","name":"","rules":[{"t":"set","p":"count","pt":"flow","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":2280,"wires":[[]]},{"id":"819c37d1.394e8","type":"debug","z":"57675e72.26a0d8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"$flowContext(\"count\")","statusType":"jsonata","x":490,"y":2340,"wires":[]}]

count has to be initialised and can be reset with The initialise inject to.

1 Like

Perhaps not the easiest thing to comprehend for kids, at least initially.

Never under estimate children. They do not need to uderstand how context storage works fully, they just need to see where it is stored and if not stored that it is non persistant, this can be seen in the info window under context data.

Yes, my own kids have taught me never to underestimate children!

The context of this exploration is a series of workshops.

So, even though the kids are likely to be able to cope, I have to keep in mind what has been covered in previous workshops, and what we can get through in the time available for the current workshop.

E1cid, the sample flow you posted above didn't work for me, and had 2 unconnected nodes. Was that correct?

Thanks

Naturally. Equally, I wouldn't teach Quantum Theory to them either, when they should be learning Newtonian Physics first.

Your example is a stretch. As we are talk storage and persistance, whether context or file makes little difference as long as kids can visualise it.

Leaving that behind.
@brendonhatcher, I have up dated the flow. sorry the change node must of not been selected when i exported.Try it again.

Not from where I sit. The concept of a file has been a fundamental building block in IT since the beginning of time. Context as a concept in NR is quite esoteric.

Anyhow, this one is for discussion over a pint :wink:

... in linux, everything is a file...

Ouch. Not everything in the world lives in linux :smirk:

To use the momentary button on the Pi, (as opposed to the dashboard ui-button) you will also need to use the rpi-gpio-in node set to the GPIO pin the momentary button is set to.

I would do this in a series of steps/lessons.

  1. use an inject node to kick off the the flow and the output going to a debug node
  2. add two ui-text nodes to show the two variables on the dashboard
  3. add a ui-button node to tigger the flow
  4. finally add the `rpi-gpio-in' node to trigger the flow

This way you go from the simple to more complex letting the kids see the construction thru to the final result.

Thanks for all the input. Here is what I have created:

[{"id":"433c7ad4.7a3344","type":"tab","label":"Basic persistent storage","disabled":false,"info":""},{"id":"24d6fb90.6b1914","type":"counter","z":"433c7ad4.7a3344","name":"Increment by 1","init":"0","step":"1","lower":"","upper":"","mode":"increment","outputs":"1","x":240,"y":200,"wires":[["71cad3d3.e2d45c"]]},{"id":"71cad3d3.e2d45c","type":"debug","z":"433c7ad4.7a3344","name":"Session count","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"count","targetType":"msg","statusVal":"","statusType":"auto","x":420,"y":200,"wires":[]},{"id":"d21bd054.b285c","type":"debug","z":"433c7ad4.7a3344","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":430,"y":560,"wires":[]},{"id":"664900bb.59e42","type":"increment","z":"433c7ad4.7a3344","name":"Increment by 1","target":"payload","x":240,"y":440,"wires":[["7d14b3a2.bcbe0c"]]},{"id":"60f2313.e1e3ed","type":"file in","z":"433c7ad4.7a3344","name":"Read lifetime count","filename":"/home/pi/.node-red/running.dat","format":"lines","chunk":false,"sendError":false,"encoding":"none","x":250,"y":320,"wires":[["d6f80283.3f749"]]},{"id":"d6f80283.3f749","type":"change","z":"433c7ad4.7a3344","name":"Change to a number","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":320,"wires":[["664900bb.59e42"]]},{"id":"7d14b3a2.bcbe0c","type":"file","z":"433c7ad4.7a3344","name":"New running total","filename":"/home/pi/.node-red/running.dat","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"none","x":250,"y":560,"wires":[["d21bd054.b285c"]]},{"id":"37714909.370256","type":"rpi-gpio in","z":"433c7ad4.7a3344","name":"Button","pin":"19","intype":"up","debounce":"25","read":false,"x":70,"y":80,"wires":[["24d6fb90.6b1914","60f2313.e1e3ed"]]},{"id":"77984d85.fe4e94","type":"comment","z":"433c7ad4.7a3344","name":"Press the momentary button","info":"","x":140,"y":40,"wires":[]},{"id":"58e88192.cd973","type":"comment","z":"433c7ad4.7a3344","name":"Increase the session count by 1","info":"","x":290,"y":160,"wires":[]},{"id":"f80622c1.a3bcf","type":"comment","z":"433c7ad4.7a3344","name":"Fetch the lifetime count from the file and set it to a number data type","info":"","x":400,"y":280,"wires":[]},{"id":"ab1c263c.1304f8","type":"comment","z":"433c7ad4.7a3344","name":"Increase the lifetime count by 1","info":"","x":290,"y":400,"wires":[]},{"id":"b59c8d0a.39ca7","type":"comment","z":"433c7ad4.7a3344","name":"Save the new lifetime count to the file and output it","info":"","x":350,"y":520,"wires":[]}]

Brendon

I would change the file location to /home/pi/running.data

Just checking, you'd change it because .dat is a known file extension that may have other associations, and/or .data clearly states what it is?

Sorry seems like auto correct strikes again. I was trying to say is to put the file in the pi’s home folder, not in the .node-red folder

Ah. I was so focused on the filename change I didn't see the path change.

So, what is the reason for changing the file location?

Regards
Brendon

  1. The .node-red folder is a hidden folder so you won't normally see it. By putting it in the Pi's home folder, you or the kids could actually see the file and examine it in an editor.
  2. I think it is best to get into a practice of keeping data files, databases, etc out of the .node-red folder. For instance, when I want to use an sqlite database, I create a subfolder under my user ('pi') called 'databases' amd then the path is /home/pi/databases/database_name_I_choose
1 Like

Got it! Learning so much!