EcsControlWatch
The third-round evaluation program: the LED world under coordinated blink periods, four partitions, and the print-clip observation and adaptation loop.
Overview
EcsControlWatch is the third-round evaluation program for the Entity Component System (ECS) architecture for control systems, following the second round's EcsControlStore. Where the second round built the mechanisms – manifests, the derived schedule, checked construction, the split store – this round built partitioning and observations: the program is divided into four partitions, each with its own store, and it carries a complete observation-and-adaptation loop that watches one of its own facilities and adjusts a System's pace in response.
This document describes the program itself – its partitions, its world, its Systems, and what it does on the bench. It is deliberately short: the implementation document explains every construct the program is built from, with this program's code as its listings, and the Observations set covers the observation loop from concepts through verification. The authoritative form of every access fact below is the manifests in the code.
Scope
New at program level this round:
-
four partitions, each with its own definition and store modules and its own creation procedure – the main program module is reduced to calling them in order;
-
transports between partitions: a sampling port carries blink periods from the coordination partition into the blink partition, and the monitor partition's Systems deposit into the print partition's buffers through a published deposit surface;
-
the observation loop, live: clip events tallied at the source, distilled into a trouble level, answered by a supervisor that commands the drain's pace – calibrated from a declared specification, verified on target by a sixteen-run suite;
-
the definition disciplines: one token per store variable, named array types in system signatures, every fact authored – stated and held in the implementation document.
Still at the concept level: several schedules and the asynchronous seam (this program runs one schedule on one core), and recovery – the motivating horizon, with observations as its detection substrate.
What the Program Does
Four LEDs under closed-loop control, a heartbeat report to a terminal – and the program watching its own print facility.
Two leader LEDs blink, each at its own period, free-phasing on per-leader tickers. Two follower LEDs each copy their leader's set-point, read as the previous tick's value – a deterministic one-tick delay. SenseSystem reads the actual pin levels back every tick; ActuateSystem writes a pin only on mismatch between set-point and measurement. Every five seconds, PeriodSystem – the "mission control" – publishes the next step of a four-step period table, and both leaders change their blink rate; the table wraps, so the cadence cycles every twenty seconds.
The print side is where the round's work lives. HeartbeatSystem writes a fixed text batch into a print ring buffer every 600 ms; DrainSystem feeds the buffered text to a UART at a commanded pace. The buffer is deliberately sized so that the shipped load slightly overfills it: writes that do not fit are clipped, the clips are tallied at the source, PrintClipSystem distils the tally into a trouble level, and AdaptDwellSystem reads the level through a sampling port and commands the drain faster or slower through a command port. The loop runs perpetually: fill, clip, trip, faster drain, discharge, clear, slower drain – one cycle about every eleven seconds, visible on both the program terminal and the console.
TickMonitorSystem watches the kernel for missed ticks and deposits a report line only when a slip occurred – in normal operation it stays silent.
Hardware
-
Pico2 board; four LEDs with their resistors –
LED0/LED2are the leaders,LED1/LED3the followers, as assigned inRfromLEDbinding. -
Drain UART:
UART1, TX/RX on pins 4/5, 38400 baud, 8N1 – the "plant" terminal showing the heartbeat text. -
The console UART stays reserved for the run-time error output, the development-build construction report, and the verification instrumentation lines.
The Partitions
| Partition | Bench role | Systems |
|---|---|---|
PrintPartition |
the plant and its observation loop | Drain, PrintClip, AdaptDwell |
BlinkPartition |
the demonstration application | Blink, Couple, Actuate, Sense |
CoordPartition |
mission control | Period |
MonPartition |
monitoring and test instrumentation | TickMonitor, Heartbeat |
Each partition owns its complete module family – wiring (W), values (V), stores (CS, CL), tokens (T), Systems, and the partition module that creates its world and registers its Systems. Tbase allocates each partition's token range; the store carries fifteen tokens:
| Partition | Token | Backing | Writer | Readers |
|---|---|---|---|---|
| Blink | ledMeasuredLeader |
leader array | Sense |
Actuate |
| Blink | ledMeasuredFollower |
follower array | Sense |
Actuate |
| Blink | ledSetpointLeader |
leader array | Blink |
Couple (previous value), Actuate |
| Blink | ledSetpointFollower |
follower array | Couple |
Actuate |
| Blink | blinkTicker |
leader array | Blink (owned) |
– |
pbBuf |
buffer state + storage | Heartbeat, TickMonitor (deposit) |
Drain (collector) |
|
pbClipsTally |
clip tally per buffer | Heartbeat, TickMonitor (deposit) |
PrintClip (collector) |
|
pbClipsAcct |
distiller account | PrintClip (owned) |
– | |
pbClipsObsPort |
sampling port | PrintClip |
AdaptDwell |
|
drainCmdPort |
command port | AdaptDwell |
Drain (previous value) |
|
drainRun |
run state | Drain (owned) |
– | |
adaptRun |
run state | AdaptDwell (owned) |
– | |
| Coord | periodPort |
sampling port | Period |
Blink |
| Coord | periodSeq |
sequence step | Period (owned) |
– |
| Mon | heartbeatStatus |
test-player state | Heartbeat (owned) |
– |
Three tokens cross partition lines, all transport-mediated: periodPort carries the blink periods from Coord to Blink, and pbBuf with pbClipsTally form the print partition's published deposit surface (Tprint.I.print), which the two monitor Systems name wholesale as their deposit class. The clip tally is written by the depositors themselves – clipping is recorded at the source, inside the buffer's write operation.
The Systems
Ten Systems, one module each:
| System | Manifest (compact) | Runs |
|---|---|---|
Blink |
C: periodPort; O: blinkTicker; P: ledSetpointLeader | every tick, per-leader tickers |
Couple |
Cp: ledSetpointLeader; P: ledSetpointFollower | every tick |
Actuate |
C: ledMeasuredLeader, ledMeasuredFollower, ledSetpointLeader, ledSetpointFollower | every tick |
Sense |
P: ledMeasuredLeader, ledMeasuredFollower | every tick |
Period |
O: periodSeq; P: periodPort | every 100 ticks (5 s) |
Drain |
Cp: drainCmdPort; O: drainRun; Cx: pbBuf | every tick, fires at the commanded period, grid-locked |
PrintClip |
O: pbClipsAcct; P: pbClipsObsPort; Cx: pbClipsTally | every 12 ticks (600 ms) |
AdaptDwell |
C: pbClipsObsPort; O: adaptRun; P: drainCmdPort | every 12 ticks |
TickMonitor |
Px: pbBuf, pbClipsTally | every tick, deposits only on slip |
Heartbeat |
O: heartbeatStatus; Px: pbBuf, pbClipsTally | every 12 ticks |
Two chains flow through the stores. The LED chain, fed by "mission control":
PeriodSystem ---- periodPort ----> BlinkSystem (sampling port, Coord -> Blink)
BlinkSystem ---- ledSetpointLeader ------+--> CoupleSystem ---+
(previous value) | | ledSetpointFollower
+--------------------+--> ActuateSystem
SenseSystem ---- ledMeasured (leader + follower) -------------+
And the print facility with its observation loop closed around it:
HeartbeatSystem ---+-- pbBuf ---------> DrainSystem --> UART
| (deposited / collected)
TickMonitorSystem -+-- pbClipsTally --> PrintClipSystem
(tallied at the source) |
| pbClipsObsPort (sampling port)
v
DrainSystem <---- drainCmdPort <---- AdaptDwellSystem
(command port, previous value)
The Systems are registered partition by partition, in an arbitrary order; the schedule is derived from the manifests at start-up:
derived scheduling order:
0 Couple
1 Sense
2 Period
3 Blink
4 Actuate
5 TickMonitor
6 Heartbeat
7 Drain
8 PrintClip
9 AdaptDwell
The partitions interleave freely – the order follows the dataflow, not the module structure. The full development-build report – manifests, dependencies, checks – is shown and read in the implementation document.
Wiring and Values
The deployment choices, all in the definition modules:
-
the LED pins and the drain UART's unit, pins, and handles:
R; -
the follower coupling:
CLblink.L.ledFollows, wired straight – follower 0 follows leader 0, follower 1 leader 1; -
one print buffer participation (the terminal), paired with its text writer; buffer capacity 64 characters (
Vprint.BufSize) – deliberately below the wiring's 128 so that the shipped load clips; -
the drain period repertoire: 600 / 300 / 150 ms as command values 0 / 1 / 2, held locked in
CLprint.L.drainCfg; -
the blink period table: four steps, leader 0 from 300 to 1200 ms, leader 1 from 450 to 1800 ms, in
Vcoord, locked inCLcoord; -
the tick period: 50 ms; all System periods as tick counts, converted once, in
P.
The observation calibration is declared in Vprint: accept 1 clip per 8 operations, trigger at 1 per 6, a burst of 6 clipping operations reaches the trip threshold, clear at 34% of it, cap at 200%. The working thresholds are derived from this declaration at creation, by Observations.Calibrate – the calibration document owns the whole scheme.
The heartbeat's load is defined in CLmon as test-case tables – the same tables the verification suite ran; the program as published selects the shipped configuration (sequence 8: 12 write operations, 40 characters per beat), in MonPartition.
World Creation
The main program module's body is the whole sequence: install the kernel, create the four partition worlds, Kernel.Begin, register the four partitions' Systems, then Plan – report – Commit – Run. Each partition creates its world in the four phases build / acquire / value / propagate; concretely:
-
Print builds the UART device, the print buffer with its tally, and the text writer; values the run states, accounts, and ports; and derives the calibration into
CL; -
Blink configures the LED pins, acquires the actual pin levels into the measured Components, zeroes set-points and tickers, and propagates the set-points back out to the pins;
-
Coord values the period port from the table's first step;
-
Mon selects the test sequence and values the heartbeat's player state.
The full creation story – and the main program module, listed complete – is in the implementation document.
What You Observe
The four LEDs: the leaders blink at their two rates, the followers reproduce them one tick behind – and every five seconds the cadence steps, cycling through the four-step table.
On the plant terminal, one heartbeat line per 600 ms: hb, the tick count, the load's name, and a tail of short dash runs. In the clipping phase of the cycle the tail visibly truncates – the designed boundary, readable by eye.
On the console, after the calibration head and the construction report: the verification record – one line per drain firing (command, period) and one line per distiller pass (operations, events, level, last event tick). The shipped configuration cycles steadily: the level climbs past the trip threshold, the supervisor steps the drain from 600 to 300 ms, the buffer discharges, the level decays below the clear threshold, the drain steps back – one full cycle every 228 ticks, about 11.4 seconds. The dwell policy visible in that record – one command step, held until fresh evidence accumulates – and the decode of both terminals are the verification document's subject; its traces are from this program, byte for byte.
Development and Deployment Build
The program as published is the development build: main calls PlanView.Report between Kernel.Plan and Kernel.Commit, importing the development modules PlanView and Tnames. The calibration head and the drain and distiller console lines are deliberate verification instrumentation – their line lengths are tuned to stay within the console UART's FIFO, so they never distort the timing they document. A deployed build drops the report call, the development imports, and the instrumentation prints; Plan and Commit appear identically in both builds, and a rejected plan is then a hard fault with its own error code.
The lib Directory
The example directory holds the program; the example's own lib directory alongside holds everything the program needs beyond the installed framework – the ECS kernel and its development-build modules, the service modules in their handle-based form, and the round's new candidate material: Observations (the distiller and calibration machinery), ObservationPorts and SamplingPort (the transport discipline), RingBuffer, and the CommandPorts authoring template. Together the two directories are self-contained: building the example writes nothing outside its tree, and the installed lib/v3.2 is untouched. What sits in the example's lib remains candidate material for lib/v4.0 – API and conceptual changes, not additions to v3.2; its delineation is upcoming evaluation work.
Repository
lib/v3.2 – <repo>/examples/v3.2/rpi/pico2/EcsControlWatch/EcsControlWatch-v21, with the example's lib directory alongside. PrintPartition/adapt-variants/ holds the three alternative supervisor policies from the verification suite; the shipped policy is selected by a single aliased import in PrintPartition.
See Also
- ECS for Control: Concepts
- ECS for Control: Implementation
- ECS for Control: Observations – Concepts
- ECS for Control: Observations – Calibration
- ECS for Control: Observations – Implementation
- ECS for Control: Observations – Verification
- EcsControlStore
Last updated: 1 September 2026