Engine

The Bobulation Engine

The Bobulation Engine is the real-time decisioning layer that fuses inputs, resolves conflicts, prioritizes signals, and routes outputs to the right actuators — in under 8 milliseconds.

Hardware is commoditized. Software is not. Anyone can solder three radios to a dual-core SoC; what makes the Wireless Bobulator a product rather than a dev board is the Bobulation Engine — the real-time decisioning layer that fuses inputs, resolves conflicts, prioritizes signals, and routes outputs to exactly the right actuators, deterministically, in under 8 milliseconds.1 This page is the engine, opened up.

The rule engine

Behaviour is defined in a small declarative YAML DSL. Rules are when/then blocks: a set of conditions over the normalized signal space, and a set of actions to fire when they all hold. You push rules to a single device or to the whole fleet over the REST API; they hot-reload without a reboot.

rule "gym-entrance-soft-open":
  priority: 40
  when:
    - sensor.door.reed == CLOSED
    - time_of_day() in "06:00-09:00"
  then:
    - light.zone.2.fade_to(30%, over=5s)
    - audio.zone.2.play("morning-warmup.m3u")
    - hvac.zone.2.setpoint = 68
    - log.info("auto-warmup-triggered", room="main-floor")

Conditions can reference any normalized channel, a handful of built-in functions (time_of_day(), zone(), user(), rolling_avg()), and the output of the optional ML model. Actions address outputs by a stable domain.zone.id path, so a rule written against light.zone.2 keeps working when you swap the underlying fixture.

Conflict resolution

In any real deployment, two rules eventually fire at the same instant and demand opposite things — one says dim the lights, another says full brightness, motion detected. A naive system picks whichever evaluated last. The Bobulator refuses to be non-deterministic about it. Conflicts are resolved by a fixed three-key sort:

  1. Explicit priority — the integer priority: on each rule wins first.
  2. Signal freshness — if priorities tie, the rule driven by the most recently updated input wins. A 200 ms-old motion event beats a 5-second-old schedule.
  3. Specificity — if still tied, the rule with more matched conditions (the more specific one) wins.

Every resolution is written to the audit log with both contenders and the key that decided it. There is no hidden tiebreak and no race.

On-device ML (optional)

For decisions that don’t reduce to clean rules — is this vibration pattern the bearing starting to fail? — the engine can run a quantized TFLite model directly on the Cortex-M7. Models are small by design (typically under 256 KB), inference is sub-millisecond, and the output is just another input the rule layer can reference: when: ml.anomaly_score > 0.8. Nothing leaves the device; the model runs at the edge, offline, every cycle.

Context-aware routing

The same physical event should do different things depending on where, when, and who. The routing table keys on zone, time-of-day, and user identity, so one rule set adapts to context instead of multiplying into dozens of near-duplicates.

Context keyExampleEffect
Zonezone() == "court-3"route to court-3 actuators only
Time-of-daytime_of_day() in "22:00-06:00"quiet-hours output profile
User identityuser().role == "operator"unlock manual override panel

Audit log format

Every decision the engine makes is appended to a structured, tamper-evident log — newline-delimited JSON, one record per decision, with a rolling hash chain so a deleted line is detectable.

{"ts":"2026-06-29T07:14:02.318Z","rule":"gym-entrance-soft-open",
 "trigger":"sensor.door.reed","conflict_with":null,
 "actions":4,"latency_ms":2.1,"hash":"a91f…"}

The log streams to the dashboard live and syncs to the cloud when a hybrid topology is configured. For regulated environments it is the difference between we think the interlock fired and here is the exact 2.1 ms in which it fired and why.

Worked example: a multi-sensor conflict

A factory cell has three active rules. At 07:14:02.318, a worker crosses a light curtain at the same moment a scheduled production ramp begins:

  • R1 safety-interlock (priority: 100): light curtain broken → halt conveyor.
  • R2 production-ramp (priority: 50): schedule hit 07:14 → conveyor to full speed.
  • R3 energy-saver (priority: 10): no motion 5 min → idle conveyor.

R1 and R2 both fire on the same cycle and contradict each other. The engine sorts by explicit priority: R1 wins, 100 > 50, conveyor halts in 2.1 ms. R3 never even contends — its motion precondition is false. The audit log records R1 as the winner, R2 as conflict_with, and the priority key as the reason. Deterministic, logged, and fast enough to matter.

See how the engine sits in the broader five-layer architecture, or read the full specifications.

copied!