A small temperature alarm in Toit 2.0
A temperature alarm needs a little memory. Switch it on at 30°C, keep it on while the reading drifts down, and switch it off at 27°C. That gap stops a sequence like 30, 29, 30, 29 from repeatedly changing the alarm state.
I used that small task to try Toit 2.0. The release is a useful excuse to look beyond blinking an LED: the same language also runs host scripts, so the decision logic can be exercised before connecting a sensor. [1][2]
Start with the state you want
There are two states, on and off. A reading at or above 30 sets the state to on. A reading at or below 27 sets it to off. Everything between those limits leaves it alone. This is hysteresis: the current reading does not completely determine the output; the previous state matters too.
The initial state is part of the design. In this example it is off, so starting at 29°C leaves the alarm off. Arriving at the same temperature after a 30°C reading leaves it on. Neither case is an accident.
!Synthetic temperature trace with an alarm turning on at 30 degrees, holding through 29, and turning off at 27.
Original plot from the executed synthetic sequence. Sample positions are ordered readings, not elapsed seconds.
Run the whole program
Get the archive that matches your platform from the official v2.0.0 release, extract it, and put its bin directory on your PATH. Check the selected runtime:
toit --version
Mine returned v2.0.0. Save this as sensor.toit:
main:
readings := [28, 30, 29, 30, 29, 27, 29, 31]
alarm := false
readings.do: |temperature|
if temperature >= 30:
alarm = true
else if temperature <= 27:
alarm = false
print "$temperature C: $alarm"
Then analyze and execute it:
toit analyze sensor.toit
toit run sensor.toit
The analyzer completed successfully. The program printed:
28 C: false
30 C: true
29 C: true
30 C: true
29 C: true
27 C: false
29 C: false
31 C: true
The syntax takes a moment to read if you normally use Python. := introduces a local variable; = changes it. readings.do: calls a block for each value, and |temperature| names the block's parameter. The string interpolates both variables. [3]
The missing assignment does the work
There is deliberately no final else. At 28 or 29, the program does not assign to alarm, which preserves the previous state. Replacing that block with alarm = temperature >= 30 would throw the memory away.
For this exact sequence, including the initial off state, the single-threshold rule changes state five times. The hysteresis rule changes it three times. Those are properties of this synthetic input, not measurements of a physical sensor or proof that these thresholds suit a device.
I also ran separate inputs for an empty list, each exact threshold, a starting value inside the gap, a descending sequence, and repeated boundary readings. They behaved as specified. A real device still needs decisions about missing readings, units, startup behavior and what should happen if its process restarts.
Toit 2.0's announcement includes a peripheral API change that accepts pin numbers directly. That is relevant when taking the next step onto supported hardware, but this program uses no GPIO, board or sensor. [1] It is a small, completed host-side example you can change without wiring anything first.
References
1. Florian Loitsch, Toit v2.0.0 and the official release.
2. Toit contributors, Learn Toit.
3. Toit contributors, language source and standard library.
Asset credits
Original chart made from the synthetic program output; no third-party image assets.
AI disclosure
AI was used to draft the article.
Toit v2.0.0 has been released
I'm one of the maintainers of Toit, an open-source programming language and framework for microcontrollers, particularly the ESP32.
After more than 4 years of development and ~200 alpha releases, we have finally released Toit v2.0.0.
Toit is a high-level object-oriented programming language with a garbage collector and other goodies. In many ways, it resembles Python, but unlike Python it was designed from the ground up for efficient execution in resource-constrained environments.
Some of Toit's features:
- Fast development cycle: with Jaguar you can update applications over WiFi without needing to reflash the entire firmware.
- Containers: individual programs run in their own containers, improving stability through memory isolation, and allowing applications to be updated independently.
- Hardware support: we support most ESP32 peripherals, including GPIO, I2C, SPI, UART, WiFi, BLE, ESP-NOW, PWM, and more. If you are missing something let me know.
- Fully open-source: the language, runtime, libraries and tooling are all developed in the open.
- Growing platform support: we support almost every ESP32 variant, and recently added our first ARM based chip, the EC618.
- Concurrency: containers run independently in parallel, and tasks inside containers use cooperative threading.
If you are curious, I recommend starting with our tutorials.
I will be happy to answer questions here, but you are also welcome to join us on Discord.
50 points · 7 comments
Mentions
It's a good point. LLMs have not been trained on Toit code as much as they have on Python or JS. No question about that. Without the Toit skills (toitlang/ai-instructions) LLMs used to be really bad. (They might be better now, but I never bothered to remove the skill files). From that point of view, Toit isn't a good choice. On the other hand, Toit provides containers and a fast refresh-cycle. Both are godsends for AI agents: the containers make it so that the agent doesn't accidentally bring down the whole system when there is a bug, and the refresh cycle makes iterating on programs much faster. (Something I find agents to be really good at). Fwiw, there isn't any clever metaprogramming in Toit.
A small temperature alarm in Toit 2.0 A temperature alarm needs a little memory. Switch it on at 30°C, keep it on while the reading drifts down, and switch it off at 27°C. That gap stops a sequence like 30, 29, 30, 29 from repeatedly changing the alarm state. I used that small task to try Toit 2.0. The release is a useful excuse to look beyond blinking an LED: the same language also runs host scripts, so the decision logic can be exercised before connecting a sensor. [1][2] Start with the state you want There are two states, on and off. A reading at or above 30 sets the state to on. A reading at or below 27 sets it to off. Everything between those limits leaves it alone. This is hysteresis: the current reading does not completely determine the output; the previous state matters too. The initial state is part of the design. In this example it is off, so starting at 29°C leaves the alarm off. Arriving at the same temperature after a 30°C reading leaves it on. Neither case is an accident. !Synthetic temperature trace with an alarm turning on at 30 degrees, holding through 29, and turning off at 27. Original plot from the executed synthetic sequence. Sample positions are ordered readings, not elapsed seconds. Run the whole program Get the archive that matches your platform from the official v2.0.0 release, extract it, and put its bin directory on your PATH. Check the selected runtime: toit --version Mine returned v2.0.0. Save this as sensor.toit: main: readings := [28, 30, 29, 30, 29, 27, 29, 31] alarm := false readings.do: |temperature| if temperature >= 30: alarm = true else if temperature <= 27: alarm = false print "$temperature C: $alarm" Then analyze and execute it: toit analyze sensor.toit toit run sensor.toit The analyzer completed successfully. The program printed: 28 C: false 30 C: true 29 C: true 30 C: true 29 C: true 27 C: false 29 C: false 31 C: true The syntax takes a moment to read if you normally use Python. := introduces a local variable; = changes it. readings.do: calls a block for each value, and |temperature| names the block's parameter. The string interpolates both variables. [3] The missing assignment does the work There is deliberately no final else. At 28 or 29, the program does not assign to alarm, which preserves the previous state. Replacing that block with alarm = temperature >= 30 would throw the memory away. For this exact sequence, including the initial off state, the single-threshold rule changes state five times. The hysteresis rule changes it three times. Those are properties of this synthetic input, not measurements of a physical sensor or proof that these thresholds suit a device. I also ran separate inputs for an empty list, each exact threshold, a starting value inside the gap, a descending sequence, and repeated boundary readings. They behaved as specified. A real device still needs decisions about missing readings, units, startup behavior and what should happen if its process restarts. Toit 2.0's announcement includes a peripheral API change that accepts pin numbers directly. That is relevant when taking the next step onto supported hardware, but this program uses no GPIO, board or sensor. [1] It is a small, completed host-side example you can change without wiring anything first. References 1. Florian Loitsch, Toit v2.0.0 and the official release. 2. Toit contributors, Learn Toit. 3. Toit contributors, language source and standard library. Asset credits Original chart made from the synthetic program output; no third-party image assets. AI disclosure AI was used to draft the article.
Less time waiting for firmware builds. More time experimenting with hardware. GitHub: toitlang/toit
Toit v2.0.0 has been released I'm one of the maintainers of Toit, an open-source programming language and framework for microcontrollers, particularly the ESP32. After more than 4 years of development and ~200 alpha releases, we have finally released Toit v2.0.0. Toit is a high-level object-oriented programming language with a garbage collector and other goodies. In many ways, it resembles Python, but unlike Python it was designed from the ground up for efficient execution in resource-constrained environments. Some of Toit's features: - Fast development cycle: with Jaguar you can update applications over WiFi without needing to reflash the entire firmware. - Containers: individual programs run in their own containers, improving stability through memory isolation, and allowing applications to be updated independently. - Hardware support: we support most ESP32 peripherals, including GPIO, I2C, SPI, UART, WiFi, BLE, ESP-NOW, PWM, and more. If you are missing something let me know. - Fully open-source: the language, runtime, libraries and tooling are all developed in the open. - Growing platform support: we support almost every ESP32 variant, and recently added our first ARM based chip, the EC618. - Concurrency: containers run independently in parallel, and tasks inside containers use cooperative threading. If you are curious, I recommend starting with our tutorials. I will be happy to answer questions here, but you are also welcome to join us on Discord.