At some point a block project needs a small operation that is easier to express in code. Perhaps a driver exposes a method with no matching block, or a value needs a format the palette does not offer. You can add that operation without abandoning the visible structure that made the prototype approachable.
This is also a handoff problem. Another person needs to know which project produced a result and what happened when something failed. The BIPES project connects visual programming with generated MicroPython. We used its tools and Python blocks on a Raspberry Pi Pico to see how records, errors and custom code behave in small, inspectable steps.
Begin with a record, not a dashboard claim
A project-information block stores an author, an IoT ID and a short description. In the generated program these fields become comments. They help identify which program is running; they do not authenticate a device or set up a remote service.
The IoT block sends a labeled marker through the terminal: a BIPES-DATA line with a channel and a value. Our sample used channel 1 and value 119, with no physical unit. That is a convention the browser tool reads, not a complete telemetry protocol. A local-storage example paired a timestamp with a random number. These show how data can be formatted; they are not measured environmental data and not proof of durable storage.
For a real trial, record for every logged value:
- the signal name and unit;
- the board and the program version;
- the time basis, and whether timestamps come from the device or the computer.
Export the results you need to keep and check that they reopen correctly. A browser feature called localStorage should not be the only copy of an important test record; clearing browser data removes it.
Let errors remain visible
A deliberate test divided 10 by zero. The except branch printed a message saying the divisor cannot be zero. The example is useful because the program reports a failure instead of displaying a plausible answer.
A broad exception handler can also hide an unrelated wiring, library or programming error. In a sensor program, keep the error information and mark the reading unavailable. Do not substitute zero just to keep the screen moving: zero may be a valid measurement with a very different meaning.
try:
value = sensor.read()
except OSError as e:
value = None
print("sensor read failed:", e)
Here sensor stands for whatever driver object your project uses. The point is that a failed read produces None and a logged reason, and the display code then has to decide how to show "no reading."
Separate an action from a value
BIPES has two ways to insert Python. "Run Python code" inserts a statement, such as a print. "Execute Python custom with output" inserts an expression whose result can feed another block. Assigning the expression 3 + 5 to a variable and printing it gave 8, a clear example of the second form.
Keep custom code small enough to explain next to the blocks around it. Save the generated program with the editable project, and note the libraries it needs.
Check that the board supports what you wrote
A snippet that works in desktop Python may call methods missing from the MicroPython build on the board. In one of our tests, the editor offered a title-case string operation that raised an AttributeError on the device, while uppercase and lowercase worked. Run each custom line on the target board, not only in a desktop interpreter, and record the firmware version you tested.
What to hand over
When a project becomes hard for a second person to run, the problem is rarely the blocks. It is the missing context. Hand over the editable project, the generated code, the list of libraries and versions, the firmware version, the wiring, and a short log showing what a correct run looks like, including how failures appear. With that, someone else can reproduce the result, and later port it to production firmware without guessing which behavior was intended.