Musings on State-Time

Musings on State-Time

Introduction

I’ve been thinking about the majority of systems I’ve had the opportunity to build, and it seems many—if not all—were anchored by time:

I think it’s also fair to say that many of the bugs I’ve hit weren’t logic errors in the traditional sense—although we’ve all had our share of those.

They were timing issues—and more specifically, how that time was organized. Something happening too soon or too late. A control loop where sampling rates weren’t consistent with loop bandwidth. Too many CPU cycles to fit the event timing constraints.

And one of the defining characteristics—and maybe the greatest challenge in debugging—is the apparent randomness of it all.

Systems that work most of the time… until they don’t.

If there’s an Achilles’ heel in embedded systems, this has to be near the top of the list.

In physics, spacetime describes the relationship between space and time as a single, unified concept.

In embedded systems, we’re dealing with something similar—just in a different domain.

Not spacetime… but state-time.

State doesn’t really exist independently. It evolves through time. And most of the systems we build are really just different ways of managing that relationship.

Events Are a Function of Time

We often describe systems as event-driven—“asynchronous.” But even asynchronous systems are detected and acted on over time, usually derived from some master clock:

At the most fundamental level, a processor samples input signals to detect a change of state.

Asynchronous events themselves are a function of time, synchronized by a common watch.

We think we’re responding to an event—but we’re really sampling time and interpreting it. The system isn’t just processing logic. It’s coordinating time.

Schedulers

I’ve always had a fascination with operating systems.

Schedulers, by name and by definition, are managers of resources—the big one being time.

Over the span of a career—from MINIX to µC/OS to FreeRTOS to Contiki—the goal has always been the same: the management of time in embedded system architectures, and the deterministic handling of processes and events.

The abstractions have evolved—but the underlying problem hasn’t. We’re still trying to structure behavior in systems where time is the dominant constraint.

The Problem with How We Express Time

Despite this, most of our programming models don’t express time clearly. Instead, we build layers of abstraction:

The laws of entropy prevail, and systems begin to fragment. We’re no longer describing behavior in a natural way.

We’re managing state variables, timing conditions, and transitions that become increasingly difficult to follow. And the system, while functional, becomes harder to reason about.

We’re no longer describing behavior—we’re managing fragments of time and state.

The Sequential Model

This led me to explore an alternative way of structuring embedded systems. What if instead of managing time indirectly, we expressed it directly through execution?

What if code flowed naturally with time—and followed the natural train of thought? Instead of jumping between states, maintaining explicit state variables, and scattering timing logic, we could write:

wait → act → wait → act → delay → act

This is the foundation of a sequential programming model using protothreads. In this model, execution flow becomes the state.

The result is code that is more readable, more maintainable, and closer to the way we think about system behavior.

Cooperative Scheduling

To support this model, I’ve been developing a lightweight cooperative scheduler (pico-os), built around the idea that not all systems require preemption.

Around that same time, I came across Adam Dunkels’ work—particularly protothreads in the context of resource-constrained systems. Different application space—but very similar underlying ideas.

In many embedded systems, interrupts already provide preemptive urgency, tasks naturally yield while waiting, and timing constraints are predictable.

A cooperative model allows simpler reasoning, reduced overhead, and clearer system flow. In many cases, it avoids the complexity and resource cost that come with preemptive systems.

Preemption solves a real problem—but often one that many systems don’t actually have. All that glitters isn’t always gold.

Where This Model Breaks

To be as complete and fair as possible—no model eliminates complexity. It shifts where that complexity lives.

A colleague and I have often talked about what we call the “Conservation of Complexity.” Systems that appear simple push complexity somewhere else—usually out of sight. The inverse is true as well.

Sequential models and cooperative scheduling work well in many cases—but they’re not universal.

This is where systems begin to fail—not in isolation, but in interaction.

The Missing Piece

Even with improvements in scheduling and programming models, something remains unresolved. We still struggle with reusability and expandability: driver design, hardware abstraction layers, portability across platforms, reuse of modules, and contracts between components.

In developing pico-os, I made progress in encapsulation—but not enough. If I had to point to one place where I fell short, it’s here. The interface between modules and hardware platforms remained too tightly coupled.

How do we construct a framework that promotes code sharing and drives evolution—rather than continual reinvention?

Closing Thoughts

Embedded systems are often described in terms of logic, events, and control. But underneath all of it lies a more fundamental truth: embedded systems are about managing time.

The way we structure that time—how we express it, abstract it, and reason about it—directly impacts system reliability, maintainability, and our ability to debug.

Much of my work has been in the phase where systems “should work, but don’t.” More often than not, those failures trace back to timing, interaction, and the boundaries between components.

Simplifying how we express time doesn’t eliminate these problems—but it makes them visible, understandable, and ultimately solvable.

Originally published on LinkedIn as “Musings on State-Time”.

Need help structuring embedded firmware around state, time, and real-world behavior?
Mesa Technologies provides senior embedded systems consulting for firmware architecture, board bring-up, debugging, project recovery, and risk reduction.

Schedule a technical call