A button can mean start, advance, acknowledge or cancel. If those meanings are unclear, a correctly wired prototype can still feel broken to the person trying it. For a team testing an interactive object, defining that behavior is often more useful than adding another sensor.
A children's museum game discussion on the Arduino forum makes this concrete: a panel with many buttons needs rules about order and participation before anyone chooses the electronics. This note covers the small logic pieces needed to express such rules. We ran them as BIPES block programs, which generate MicroPython, on a Raspberry Pi Pico. They are building blocks, not a finished game controller.
Turn a sentence into a condition
An if block asks whether a condition is true at the moment execution reaches it. A comparison might ask whether a reading exceeds a threshold. An and block requires both conditions; an or block accepts either. Not reverses the result, which is particularly useful when a switch reads low while pressed.
Read a proposed rule aloud before building it: "respond when the button is pressed and the game is ready." That sentence contains two independent conditions. "Respond when either sensor triggers" describes a different rule. The program should preserve that distinction, and so should the written specification someone else will test against.
Comparisons also have edges. Strictly greater than and greater than or equal to behave differently at the boundary value, so decide which one the rule means before the threshold is tuned.
A few other blocks are easy to misuse:
- A true or false constant is useful while checking a branch. An if with true always runs its action; an if with false never does. That lets you test the action separately from the input.
- Null, generated as
Nonein Python, represents no value. A missing sensor reading should not silently become a legitimate zero. Decide explicitly how missing data affects the interaction. - The conditional expression chooses between two values and can sit inside another expression. It does not replace a branch that runs a sequence of actions.
Remember what happened
A variable keeps a value between steps of the running program. On our bench, assigning 10 to a variable and printing it printed 10. A second check started a variable at 7, changed it by -1 and printed 6. These small results make assignment and updating visible before anything physical is attached.
Two details in the generated code are worth knowing. The editor initializes variables to None before the program sets them, which is setup rather than a stored result. The "change by" block also generates a helper that checks for an integer, which matters if you later reuse it with other types of values.
For an interaction, a variable might instead remember ready, playing or finished. Decide when each state begins, which inputs it accepts and how it ends. Initialize it deliberately when the program starts. A value held in RAM does not survive a power cycle, so a prototype that restarts mid-session will come back in whatever state the startup code chooses.
A held button is still one human action
An if statement inside a loop can run many times while a person holds a button. Increasing a score on every pass would count loop iterations, not presses. To count presses, the program needs to recognize the transition from released to pressed, and it needs a rule for contact bounce.
The same applies to feedback. If the program is busy playing a light or tone, decide whether a press during that time is ignored, queued or acted on. Each choice is reasonable for some product; leaving it undefined is what makes a prototype feel unreliable.
Write the expected behavior before the user test
Before inviting someone to try the prototype, walk through these cases and write the expected response for each:
- an ordinary press;
- a long hold;
- a second press while feedback is still running;
- a press in the wrong state, such as before the game is ready;
- a restart or power cycle in the middle of a session.
That list is a small interaction specification another person can test, rather than an assumption hidden inside blocks. When the behavior later moves to firmware on a custom board, the same list becomes the acceptance check for the new code.