Two more blinks
If you've followed the steps in Getting started you'll now have a board with a blinking LED. This tutorial starts from there and shows you two other methods to get the same effect. In the process you'll directly access the hardware through MicroPython and see how ThingStudio nodes and flows can keep track of past events.
The Blink an LED flow contained three nodes, a timer → function → gpio out. We'll just be changing the function node in this tutorial.
The first variant is Read the pin, write it back: the blink's state lives on the board, in the pin, and the
function node runs your own MicroPython to reach it.
The second variant is Keep the state in the node: the state lives inside the function node itself, in
context.
You can build this flow up yourself or start from the finished flow,
two-more-blinks.flow.json,
in the tutorials folder, with File → Open…. It carries the first of the two
blinks: change 15 in machine.Pin(15) to your LED's GPIO number.
If a step misbehaves on the way, start at Debugging & troubleshooting.
Read the pin, write it back
Each message so far decides the LED by counting. Let the pin say where it is instead. Replace the function's code
with this, using your LED's GPIO number in place of PIN:
import machine
msg['payload'] = 0 if machine.Pin(PIN).value() else 1
return msg
Deploy. The LED blinks as before, but the reason has changed: each message now reads the pin and writes back the opposite of what it found. The state, what the LED is actually showing, lives on the board between messages, so the flow can't contradict it.
machine.Pin is MicroPython's own pin API. Constructed with a number and no other settings, it takes the pin as it
is and .value() reads the level the gpio out node has written. The write itself goes out of the function's
output, through to gpio out.
The pin fields on gpio nodes are checked against your board at compile time, but code inside a function isn't. If
PIN isn't a pin number your board has, the compile check passes and the error arrives when the flow runs, as a
[NODE_ERROR] in the console. See Reading a node error; Pins… next to
the Board menu lists your board's pins.
Keep the state in the node
An important point to understand
A flow is stateless: each event is handled on its own, without regard to what came before. In general this is a good thing:
- An error shows up with the message that caused it, not in some state built over earlier operations.
- Recovery is doing nothing: the next message starts clean. A crashed stateful component needs its data restored to an acceptable earlier version, with nothing to say which that is.
When a flow does need to remember, ThingStudio's answer is a context — state held between messages, either
private to one node (context) or shared across the whole flow (flow). This blink's LED level belongs to one
function node, so its private store is the one to use. All three stores, context and flow among them, are
in Context, flow and settings; settings even survives a
reboot.
The pin held the state on the board. The other place is inside the function node itself:
on = not context.get('on', False)
context.set('on', on)
msg['payload'] = on
return msg
Deploy again: the same blink. Nothing outside the node holds the state now. context.get('on', False) reads this
function node's own memory; context.set('on', on) writes it; the False is what .get answers when nothing is
stored yet, which is why the first message turns the LED on.
For state kept without code, the change node stores a flow variable, the same store a
function's flow.get()/flow.set() reads. A change node can store a flow variable but can't flip it, so a toggle
needs a function node.
Next
The next tutorial, the next job, and the node pages behind the code:
- Web Blink — the next tutorial: the same LED, driven by an
http innode from anywhere on your WiFi. - A sensor display — the next job: read a sensor and show it on a screen.
- gpio out — what else the node that drives the LED can do.
- function —
context,flowandsettings, and several outputs from one function node.