Guide 6: A HEART-Embedded greenhouse irrigation system#
A case study. A maker of greenhouse irrigation controllers wants its products to be HEART-embedded systems: each controller should know when it is unwell, say so in one standard language, tell the grower, and never leak anything it should not. It also wants something more: to let an AI suggest better watering schedules, without ever letting an AI water the plants on its own. This guide goes step by step from day 1 to a system that can claim HEART Basic, Autonomy Level A1, shows how an owner registers it and watches it on HEART Hub, and demonstrates the update function of the Core Standard (Section 7): the brain proposes, the HEART decides, and a person approves at the greenhouse.
A working demo comes with it: greenhouseSimulator/, one Python file that simulates a greenhouse, its controller, its HEART and a mock brain, and reports to https://heartcortex.ai.
Status. A design case study with a simulator. No real greenhouse, controller or model was involved. The brain in the simulator is a mock (fixed rules, not a model), clearly labelled. Each step marks what is simulated and what a real product would need.
Before you start: what HEART will and will not do#
An irrigation controller's job, opening valves and running the pump on a schedule, is control. Its safety limits, such as never running the pump dry, sit in the firmware and must never depend on anything else. HEART stays out of them.
| Stays in the controller and its firmware | Goes to HEART |
|---|---|
| Running the pump and valves on the schedule | Knowing whether the plants are actually getting water |
| The tank interlock: the pump never runs dry | Watching the tank, pump, probes, valves and Wi-Fi |
| The daily maximum per zone; the pump's thermal cut-out | Raising alerts with standard fault codes, and telling the grower |
| The schedule itself, and the panel that sets it | Asking a brain about problems its rules cannot fix |
| Anything that protects the pump, the plants or people | Trying a proposed schedule change only after a person approved it, watching it, and undoing it if it goes wrong |
This is the general rule of the standard (Part 6: HEART stays out of the safety chain and out of real-time control loops, and never bypasses an interlock). HEART changes one supervisory value, the watering minutes per zone per day, through the same path the controller's own panel uses, and the firmware still checks every value against its own limits.
What is physical here. Watering moves water onto living plants: any change to the schedule is physical. So every change an AI proposes needs a person's approval, every time, whatever the envelope says (Part 6, Section 2.3; Core Standard 7.4, rule 4).
Step 1: Decide where HEART runs#
probes, tank, pump, valves ──► irrigation controller ──(Modbus / local API)──► HEART ──(internet)──► HEART Hub
firmware, schedule, on the controller, and the grower
interlocks or a small box beside it
| Part | What it does | HEART's place |
|---|---|---|
| Controller | Runs pump and valves on the schedule; holds the interlocks | Read; one writable value (the schedule), through its own interface |
| HEART | On the controller if it is an embedded Linux board or an ESP32, otherwise on a small computer beside it | Runs here, with the button for approvals and Stop |
| Brain | A model, anywhere: in the Model Slot, on a Gateway, in the cloud | Sees a Consultation, sends a Proposal. Never a command |
| Grower | Decides | Approves at the greenhouse; receives escalations |
In the simulator, all of this is one process. Controller is the firmware, Heart is the HEART, and MockBrain is the brain.
Step 2: Give the system an identity#
The HEART serves a Card at /.well-known/heart-card.json:
{ "system": { "id": "com.example.irrigation.gh-controller", "name": "Example greenhouse controller", "version": "1.0.0" },
"level": "basic", "bindings": ["http-server"], "pulse": { "intervalSec": 30 },
"modules": ["properties", "control"],
"shares": { "pulse": "public", "arrhythmia": "public", "property": "public", "trial": "internal", "proposal": "internal" },
"autonomy": { "level": 1 }, "template": "heart/embeddedstandard/python/v1.2611.2" }
To the owner (and only the owner) it also lists its capabilities: set_watering_zone1 to set_watering_zone3 (physical: true, risk: write, reversible: true, callers: ["ai"], an interlock that the controller must answer), and flush_lines (callers: ["human"]: only a person at the panel). The simulator uses system ID org.heartaispec.demo.greenhouse and template heart/embeddedstandard/greenhouse-simulator/v1.2611.2.
The system ID names the product, never the grower or the site. The HEART Hub device ID (Step 8) is random.
Step 3: List what the system knows, and how private each item is#
A greenhouse holds little personal data, but some: when people come and go, and who approved what.
| Value | Source | Privacy | Why |
|---|---|---|---|
| Healthy or not; open fault codes | Derived by HEART | public | The only things HEART Hub ever receives (Step 8) |
| Tank status: ok, low, empty | Controller | public | Needed to act; reveals no more than the alert does |
| Tank level, water used today | Controller | internal | Operations |
| Soil moisture per zone | Probes | internal | Operations; also the grower's crop know-how |
| Watering minutes per zone (the schedule) | Controller | internal | The grower's recipe |
| Valve and pump state, pump temperature | Controller | internal | Device health |
| Air temperature, humidity, sunlight | Sensors | internal | Operations |
| Internet link state | HEART | internal | |
| Consultations, Proposals, Trials, Actions, learned Playbooks | HEART | internal | What was tried and why. A Consultation sends features at their own level, never raw samples |
| Door open or closed; door visits | Door sensor | private | Reveals when people are in the greenhouse |
| Who approved a change | HEART | private | A person |
| Grower's name, address, crop contracts | Grower's own systems | not on the controller | The HEART does not need them |
Only public items leave for HEART Hub. What the brain may see is decided by the Valve: here, internal features of the zone's soil, the air and the tank, and never the door.
Step 4: Say what "doing its main job" means#
The main job of an irrigation system is: every zone gets the water its schedule says, and its soil stays in its band.
def check_main_function():
"""CHANGE: return (True, "") if watering works, else (False, "what is wrong, in plain words")."""
if tank_level() < TANK_INTERLOCK:
return False, "The tank is empty: the pump is locked out, nothing can be watered."
return True, ""
Less than that is degraded: the tank below 20 % (watering will stop soon), or a zone's soil below 25 % (plants will wilt). A condition must hold for 5 seconds before it is raised, because one odd reading is not a fault.
Step 5: The faults it recognises#
Every alert carries a fault code from Part 3, Section 5.4. Each code below was checked against that list.
| What happens | Fault code | Severity | Status |
|---|---|---|---|
| Tank below 5 %: the firmware locks the pump out | system.main-function-lost | critical | failing |
| Tank below 20 %, or a zone's soil below 25 % | system.main-function-degraded | warning | degraded |
| A zone's 5-minute mean above its 35 to 45 % band | performance.overshoot | warning | degraded |
| A soil probe's reading unchanged for 90 s, through watering | sensors.stuck | error | degraded |
| A zone watered twice and its soil not wetter (valve stuck or line blocked) | actuators.not-responding | error | degraded |
| Pump above 70 °C (the firmware cuts out at 80 °C) | performance.too-hot | warning | degraded |
| The greenhouse Wi-Fi is down | connectivity.link-lost | warning | degraded |
| A Trial's undo could not run (Core Standard 7.5) | actuators.not-responding (component trial) | critical | failing |
| Silent for three Pulse intervals | system.flatline | raised by HEART Hub |
Two details matter. A stuck probe also makes a valve look stuck (the reading does not rise), so the valve check is skipped while the probe is stuck. And the valve check compares the rise after watering with where the soil was heading anyway, so a hot afternoon is not mistaken for a stuck valve.
The thresholds here are illustrations. A real product takes them from its crops and its controller's specification.
Step 6: Commands, approvals and Stop#
This is where the greenhouse differs from a HEART that only watches. It has one kind of change, the watering minutes of a zone, and it may come only one way.
No direct changes. Every PUT, POST, PATCH or DELETE on properties is refused with 403 capability-not-allowed. The grower sets the schedule on the controller's panel as before.
The update function: the brain proposes, the HEART decides (Core Standard 7.1 to 7.6).
- Detect, reflex first. Zone 1's soil stays above its band (
performance.overshoot). No adopted Playbook covers it, so the HEART consults its brain, about one problem at a time. - Consultation. The HEART summarises, on the device: statistics (value, min, max, mean, trend) of zone 1's soil, the air and the tank over 10 minutes, each with its privacy level; the one candidate,
set_watering_zone1, with its current value (read by the HEART), declared range (10 to 60), the owner's envelope (15 to 45, steps of at most 3, at most 2 Trials a day, 600 s apart), its guard, andphysical: true. Never raw samples, never the door. - Proposal. The brain answers: water 20 % less, 30 → 24 min/day, in two steps (27, wait 120 s, then 24);
expect: the 4-minute mean ends up ≤ 45 % and ≥ 35 % within 6 minutes of the last step;undoIf: the reading falls below 30 % within 6 minutes. It sends no current value: it cannot. - Check. The HEART checks the Proposal in the order of Core Standard 7.3 (shape, mode, target, callers, not destructive, range, input schema, can read the current value, no Trial live on the target), and also the owner's envelope. It refuses the first failure with its problem code. It reads the value to restore (30) from the controller itself.
- One approval for the whole plan. The Proposal becomes an Action with initiator
ai, riskdestructive(a physical change started by an AI counts as destructive) and a digest over its arguments, every step and the value the undo will restore. The controller shows a short code from the digest; the grower checks it and presses the button on the controller. One press approves the plan and its undo. An approval sent over the network with the key alone is refused with403 presence-required, so neither the key nor an AI agent holding it can water the plants. - Try. Step 1 (27), then after
waitSec, step 2 (24). Each step passes the guard (the controller answers), stays inside the envelope, and goes through the firmware's own check. Later steps and the undo are Actions whoseapprovedBynames the first. - Verify. From the first step the HEART watches
undoIf. The Trial is kept only when everyexpecthas held and the longestundoIfwatch has passed. IfundoIffires, anexpectdoes not hold in time, a step fails, or Stop is pressed, the HEART restores 30 on its own. If even the undo cannot run, the Trial endsundo-failed, a critical alert opens, and automatic action stops. - Remember. A kept Trial becomes a learned Playbook with
status: proposed: "when the mean is above 45, set 24". It never runs until a person adopts it, with proof of presence andadoptedPhysical: true, and then for at most a year before review.
| Trial state | Next |
|---|---|
awaiting-approval | running (approved), refused (denied, expired, guard), stopped (Stop) |
running | kept, undoing, refused (first step failed) |
undoing | undone, stopped (Stop caused it), undo-failed |
kept, undone, refused, stopped, undo-failed | Final: the Outcome |
Stop. Holding the button on the controller (in the simulator, touch button.hold), or POST /heart/v1/tending/stop with the owner key, stops all automatic action at once: waiting Actions are cancelled, a running Trial is undone and ends stopped, and every new Proposal gets 409 autonomy-stopped. It needs no approval and no internet. Resuming gives authority back, so it waits for a press at the controller.
In the simulator. The brain is MockBrain: fixed rules dressed as a model. A remote brain can also deliver a Proposal with POST /heart/v1/tending/proposals and a brain key (scope heart.propose only, never the owner's key). Adopting a learned Playbook is not part of the demo (501). The simulator's envelope check, maxPerDay and cooldownSec apply to every Trial, even those a person approves: stricter than the standard asks, as a HEART may always be. A real product needs a real button (or a passkey) as its proof of presence, a real model or Gateway behind the Proposal, and Trials stored so that a restart undoes a running one (Core Standard 9).
Step 7: Tell a person#
Autonomy Level A1 needs an escalation route that works with no internet:
- On the controller: a light or a buzzer, and the waiting change's code on its display.
- To the grower: the app, an SMS, or the farm's own dashboard.
Change escalate() to use them. HEART Hub (Step 8) adds email when the greenhouse goes silent.
Step 8: Register and watch it on HEART Hub#
- First start. The controller makes a random device ID, claim code and key, keeps them, and shows the claim code only on its label or display.
- Sign in. The grower opens
https://heartcortex.ai/loginand enters an email address. A 6-digit code arrives; there are no passwords. - Register. At
https://heartcortex.ai/register?id=<device ID>(a QR code on the label can open it), they enter the claim code. - Watch. The device's page,
https://heartcortex.ai/device/<device ID>, shows its status and open alerts, private until shared. Their devices are athttps://heartcortex.ai/devices. - Silence. If no report arrives for three Pulse intervals, the page shows a Flatline and the grower gets one email.
Only the public Pulse and public alerts are sent, and the site keeps only the status, timing and fault codes: never a soil reading, the schedule, a Proposal or a door visit.
Approving from afar (HEART Hub 7.1) is off by default. With it on, a waiting change can also be approved on the device page with an emailed code; the controller acts only on a decision signed by the site key it pinned, for the exact digest, at most 300 s old. Because watering is physical, the owner must also turn on remoteSitePhysical on the controller itself. It is weaker than a press at the greenhouse: it proves the grower could read their inbox, not that anyone looked at the plants. The simulator has both settings, as --monitor-approvals and --remote-site-physical, with the checks copied from the Python template.
Step 9: Try it with the simulator#
python3 greenhouseSimulator/greenhouseSimulator.py --scenario all
Register it as in Step 8, then watch. Zones 1 and 2 start too wet. When the HEART asks, touch button.press in the folder it runs in: zone 1's Trial is kept and becomes a proposed Playbook. Then zone 2's Trial starts after your next press, and the staged hot spell dries the soil below 30 %: the Trial is undone, 30 min/day restored without anyone touching it. Meanwhile it stages a stuck valve, a lost Wi-Fi link, a stuck probe, an overheating pump and an emptying tank, each fixed a few minutes later. Details and every option are in greenhouseSimulator/README.md.
What is simulated: the whole greenhouse, the controller, its limits, the button, and the brain. A greenhouse day lasts 24 minutes. Durations in Trials count simulated seconds.
Step 10: Test it, rate it and claim only what passed#
- Run
python3 greenhouseSimulator/test_e2e.py: it checks every message against the schemas, the Trial paths (kept, undone, stopped), that nothing ran before approval, that steps stayed in the envelope, and that only public data reached the monitor site. - On a real product, run the HEART-Rating client (
heart-rating/client/heart_rating.py) and the hands-on tests of Part 9: empty the tank, block a valve, unplug a probe, heat the pump, cut the Wi-Fi, and press Stop during a Trial. - Publish a Declaration with every result, and claim HEART Basic, Autonomy Level A1.
The Trial loop in this case study is a demonstration of the rule, not a claim of a higher level. What each level would need for this product (Part 2, Section 8):
| Level | For a greenhouse irrigation system |
|---|---|
| A1 (notices and tells a person) | Yes: Steps 4, 5 and 7 |
| A2 (says why, with a confidence) | HEART Connected: "zone 3 was watered twice and did not get wetter; the soil probe still changes, so the valve is likely stuck; confidence medium" |
| A3 (suggests or makes a fix, a person approves) | HEART Full, with the control module: the Trial loop of Step 6, with Stop. Every watering change still waits for a person, because it is physical |
| A4 (acts on its own within adopted rules) | Adopted Playbooks plus an allowlist entry for rule. A learned Playbook from a kept Trial, adopted by the grower with adoptedPhysical: true and a review date, could then run as a reflex, every run still a Trial with its own undo |
| A5 (AI acts within its envelope) | The ai and evolving modules. Even at A5, a brain's physical change needs a person: the way to make a watering fix automatic stays an adopted Playbook, not the AI |
HEART conformance says nothing about electrical, pressure-equipment or water-supply rules. The controller still needs its own (for example backflow prevention for mains-fed tanks, and the electrical safety rules for pumps in wet places).
What the case study shows#
- A HEART can change things without ever being in charge. The controller keeps its interlocks, the firmware checks every value, and HEART changes one supervisory number only in a Trial a person approved.
- The brain proposes; the HEART decides. The brain sees features, not raw data; sends a Proposal, not a command; and supplies no current values. The HEART checks it, reads what to restore itself, and owns the undo.
- One approval, for the whole plan. The digest covers every step and the undo, so the person approves exactly what will happen, including how it will be taken back.
- A Trial is honest about failure. The same Proposal is kept on a normal day and undone in a hot spell. Both outcomes are evidence, and only the kept one becomes a proposed Playbook.
- Physical means a person, at every level. Envelopes narrow automatic changes; they never replace the person for a change that waters plants.