There is a tank of water, and something needs to know how full it is. A controller reads that number and decides whether to open a valve, so it has to be current, and it has to be obviously wrong when it is wrong rather than quietly wrong.
This course follows one device that answers that question: an ESP32-S3 running bare-metal Rust, no operating system, no allocator, two sensors on two different buses, about two thousand lines. It has been in place for a few months, which means it is also a tour of the things that went wrong.
The hardware
An ESP32-S3 DevKitC on an eletechsup DNS3485 carrier, fed 24 V.
- Level — a submerged pressure transmitter speaking Modbus-RTU. The carrier wires the devkit's serial pins and a driver-enable line into an RS485 transceiver, and brings the differential pair out on the A+/B− screw terminals. That is where the sensor connects.
- Temperature — a DS18B20 on a single pin, powered from another pin held high for the lifetime of the program. It draws about 1.5 mA, well inside what a pin will source.
Two sensors, two buses, two completely different failure modes. That is the whole course.
The seam that makes any of this testable
Framing, conversion and timing live in a no_std crate with no HAL types and no I/O at all. The same source compiles for the device and for cargo test on a laptop. Everything that touches a peripheral sits above it.
This is not a tidiness argument. It is the difference between a bug you can reproduce on your desk and one you can only watch happen in a plant room.
#![cfg_attr(not(test), no_std)]
pub mod level; // raw register -> percent, litres, mm
pub mod rtu; // Modbus framing: CRC, request, response
pub mod timing; // when the RS485 driver may be releasedIf you come from C#
The nearest habit is putting your domain logic in a class library with no HttpClient and no DbContext in it. Same instinct, sharper consequence: here the wrong dependency does not slow your tests down, it stops the crate compiling for the target at all.
Check yourself
Not graded — just to see whether it landed.
1.What does keeping the logic in a no_std core actually buy you?
2.The temperature probe is powered from a GPIO held high rather than a supply rail. Why is that acceptable?