A sensor demonstration often starts with numbers scrolling in a terminal. Then someone asks for a screen, a saved log or both. Before adding those features to a user-test prototype, check how the program will share its time: taking measurements, holding them in memory and using them are separate jobs. Extra work can leave readings waiting to be processed.
We ran a small temperature example on our bench to make those jobs visible. A timer requests work every three seconds, a function reads the RP2040's internal temperature sensor, and the main loop prints values from a queue. There is no dashboard in this experiment. It is a way to understand the data path before building one, and it raised two details worth checking before reuse: what the printed time means, and which queued value the program takes next.
Follow one reading from the timer to the terminal
The program targeted MicroPython 1.23.0. It creates ADC(4), which selects the RP2040's internal temperature channel, and starts a periodic timer with a 3000-millisecond period. The MicroPython RP2 quick reference for that version identifies the internal sensor channel and the timer interface. This is a reading from inside the microcontroller, not a calibrated room-temperature measurement.
The timer callback does almost nothing itself. It asks MicroPython to run another function later:
def timer_callback(timer):
micropython.schedule(collect_temperature, 0)
That keeps the timer callback small. The scheduled function performs the reading and floating-point conversion, then puts the result in a temperature queue. Scheduling means the work runs when the runtime can execute it; it does not mean the ADC sample was taken at the instant the timer expired. The MicroPython 1.23 documentation source for micropython.schedule also documents a finite pending-callback queue, which raises an error when full.
The main loop checks the temperature queue once per second. If a value is waiting, it removes one and prints it. Under this light workload, the consumer gets more chances to take a value than the timer gets to request one. A slow display update, a file write or another task changes that balance, and it has to be checked in the combined program.
Decide whether you want history or the newest value
The queue is created with deque([], 10, 1): a maximum length of ten items, with overflow checking enabled. A queue is simply a waiting line for data. New measurements go onto one end with append, and the main loop takes them from the other end with popleft.
That arrangement is first-in, first-out: the oldest waiting measurement is used next, so values keep the order in which they were added. One of our code comments called the result the "latest" temperature, but that is only true when no older value is waiting ahead of it. If the main loop falls behind, a freshly printed number may describe an earlier moment.
The MicroPython 1.23 collections documentation explains the overflow option. With the flag enabled, adding to a full queue raises IndexError instead of quietly discarding from the other end. Our program had no recovery policy for that condition. The pending-callback queue and the ten-item temperature queue are also different queues, so handling one limit does not handle the other.
For a recording task, keeping readings in order may be exactly what you want. For a live status display, showing the newest valid value may matter more than replaying every intermediate one. Decide that with the person using the prototype. Then specify how discarded readings, delays and full buffers should be reported, rather than assuming one queue policy fits both uses.
Read the timing output correctly
The console printed Time interval lines rising through roughly 3001, 6000, 9000, 12000 and 15000 milliseconds. The code calculates each value from a fixed start_time, not from the previous measurement. These are cumulative elapsed times. Their differences show the roughly three-second pattern; the printed values are not progressively longer sampling intervals.
We have separate runs: one shows values around 22 to 24 degrees C, another shows 26.58 degrees C. None includes a reference thermometer, an enclosure test or a controlled temperature change. The numbers cannot establish ambient accuracy or explain why the temperature differed.
The conversion uses an assumed 3.3 V scale and the internal-sensor formula. Printing two decimal places makes values easy to compare; it does not mean hundredth-of-a-degree accuracy. For the question in this note, the useful evidence is that scheduled work produces values and the main loop consumes them. Precision and thermal behavior need their own experiment.
Check the combined prototype before the user test
Keeping up with incoming data is a recurring problem. In a May 2023 MicroPython discussion, a developer asked how to collect ADC data at a fixed rate while processing it separately. That example used a much faster rate on another platform, so its performance discussion does not transfer directly. The question is the same: can the processing keep up, and what happens when it cannot?
For your next test, record two timestamps per value:
- one taken next to the actual reading;
- one taken when the display or logger uses the value.
Their difference is the data's age at that point. Take the first timestamp at the reading itself, rather than assuming the timer's requested time is when acquisition happened. For a sensor that does its own conversion internally, check what its driver means by a fresh reading too.
Then run the real combination of tasks: the screen, logging and any communications the demonstration needs. Look at the longest observed age and whether the queue grows. Deliberately slowing the consumer will reveal the chosen overflow behavior. We have not run that overload test yet; it is the next step. Also define how the interface should look when no new valid reading arrives.
The useful outcome is a clear promise to the user: what the number represents, how recent it is, and how the prototype shows a missing update.