# Guide 11: A HEART-Embedded dog tracking collar

**A case study.** A maker of GPS tracking collars for dogs wants its collars to be HEART-embedded systems: each collar should know when it is unwell, say so in one standard language, tell the owner, and never leak anything it should not. For a tracker, that last part is the hard one: **the collar's main data is where the dog is**, and so where its owner lives, walks and is right now.

This guide goes step by step from day 1 to a collar that can claim **HEART Basic, Autonomy Level A1**, shows how an owner registers it and watches its health on HEART Hub (on `heartcortex.ai` or on their own server), and shows which of the owner's changes may run at once and which must wait for a person.

A working demo comes with it: [`dogCollarSimulator/`](../dogCollarSimulator/README.md), one Python file that simulates a dog's day, the collar and its HEART, and reports to HEART Hub.

> **Status.** A design case study with a simulator. No real collar, dog or owner was involved. In the simulator **everything is simulated**: the dog, its walks and wanders, the GNSS, the uplink, the battery and the sensors. Every position is in metres from a made-up home origin, never a real place. Each step says what is simulated and what a real product would need.

---

## Before you start: what HEART will and will not do

A tracking collar's job is to **find the dog**: take a position, check it against the owner's safe zone, and send it to the owner's app. That is the product's main job, and it stays in the product's firmware and app. HEART **watches the collar** and tells a person when the collar cannot do that job well.

| Stays in the collar's firmware and the maker's app | Goes to HEART |
|---|---|
| Taking GNSS fixes, the position itself, the track | Knowing *whether* the collar has a fix, never *where* it is |
| The safe zone, and telling the owner when the dog leaves it | Watching the battery, GNSS, uplink, temperature and accelerometer |
| Sending positions to the owner's app | Raising alerts with standard fault codes |
| The LED and buzzer drivers, and their welfare limits | Telling the owner, even with no internet |
| Power management, sleep, the modem | Answering "how is the collar?" in one standard format |
| Anything that keeps the dog safe | Passing the owner's few allowed changes through the gate |

The rule of the standard is that HEART stays out of the safety chain and real-time control (Part 6). For a collar, "safety" includes the dog's welfare: a buzzer at a dog's ear, or a light at its neck, acts on a living animal, so a change to either is **physical** (Step 6).

**The single most important design decision**: the dog leaving its safe zone is **a fact about the dog, not a fault of the collar.** It is a private event for the owner, raised by the product, and it is never a HEART alert and never sent to HEART Hub. The pacemaker case study made the same split for atrial fibrillation (Guide 4, Step 3).

---

## Day 1: Choose the hardware, and decide where HEART runs

| Part | Choice | Why |
|---|---|---|
| Microcontroller and modem | A low-power cellular module with GNSS built in and an ARM Cortex-M application core (LTE-M or NB-IoT); or a LoRa radio for farms and rural areas | One chip for positioning and the uplink, sleeping between fixes |
| GNSS | In the module, with an external patch antenna on the top of the collar | It needs open sky: under thick trees or indoors it gets no fix |
| Home base | A small mains-powered box at home, Bluetooth to the collar, Wi-Fi to the internet | At home the collar needs no GNSS and no cellular: it saves most of its battery |
| Accelerometer | A 3-axis MEMS sensor | Resting, walking, running; and whether the collar is on a dog at all |
| Temperature | A sensor next to the battery | A lithium cell must not get too hot, and a hot collar may mean a hot dog |
| Battery | A rechargeable lithium-polymer cell, about 900 mAh | Weeks at home, days away from home, about a day in lost mode |
| LED and buzzer | A small LED and a piezo buzzer | To see the dog in the dark, and to call it or find the collar. Both act on the dog |
| Case | Sealed, with a charging contact | A dog swims and rolls in mud |

**Where HEART runs.** A collar has a small battery that must last days, and spends most of its time asleep. So HEART is split in two, like the collar itself:

```
collar (microcontroller) ──(Bluetooth: home base; LTE-M or LoRa: away)──► home base / maker's server ──► owner's app
  noticing: Minimum Tending            HEART API and HEART Hub reporting run here            and HEART Hub
```

| Part | What it does | HEART's place |
|---|---|---|
| **Collar** | Fixes, safe zone, uplink, LED, buzzer | **Noticing**, with no network: each check in Step 5 runs on the collar, between fixes, and the fault codes ride inside the collar's existing uplink messages. A tiny core fits: the bare-metal C template `heart-templates/templates/c-baremetal` (HEART Line, about 1 KB of code, no heap). Today it raises only `system.main-function-lost`, so you add one line per fault code |
| **Home base, or the maker's server** | Relays the collar's messages | **The HEART API and HEART Hub reporting**: the bridge `heart-templates/templates/bridge` turns HEART Line into HEART Basic over HTTP; or the Python template `heart-templates/templates/python` |
| **The owner's phone app** | Shows the map, the safe zone, lost mode | Reads the HEART API with the owner key; approves physical changes with a passkey |

The ESP32 Full starter (`heart-templates/templates/esp32-full`) has the most HEART features, but it keeps Wi-Fi and an HTTPS server on all the time and has no low-power mode (Guide 3 found the same gap). It suits the home base, not the collar. For the gated LED and buzzer with a passkey (HEART Full), the Rust Core `heartd` (`heart-core-rs`) has the gate, presence checks and Actions; it would run on the maker's server.

The simulator runs all of this in one process on a computer.

---

## Step 1: Give the collar an identity

The collar's HEART serves a Card at `/.well-known/heart-card.json`:

```json
{ "system": { "id": "com.example.pets.tracking-collar", "name": "Example tracking collar", "version": "1.0.0" },
  "level": "basic", "bindings": ["http-server"], "pulse": { "intervalSec": 30 },
  "modules": ["properties"],
  "shares": { "pulse": "public", "arrhythmia": "public", "property": "private", "rhythm": "private", "action": "internal" },
  "autonomy": { "level": 1 }, "template": "heart/embeddedstandard/c-baremetal/v1.2611.2" }
```

The system ID names the product, never the dog or the owner. The device ID used by HEART Hub (Step 8) is random, and nothing links it to a person except the owner's own registration. The Card shows the collar's capabilities (Step 6) only to the owner: in public it lists none.

The simulator's Card uses the system ID `org.heartaispec.demo.dog-collar` and the template `heart/embeddedstandard/dog-collar-simulator/v1.2611.2`.

---

## Step 2: Say what "doing its main job" means

The main job of a tracking collar is: **the owner can be told where the dog is.** It fails when the collar cannot locate the dog *and* cannot send, away from home, or when the battery is about to die:

```python
def main_job_problem(self):
    """CHANGE for a product: None while the collar does its main job (it can tell the owner where the dog is)."""
    if self.battery <= 3.0:
        return "The battery is nearly empty: the collar will switch off and stop reporting where the dog is."
    if self.no_fix() and self.link_down:
        return "No GNSS fix and no uplink, away from home: the owner cannot be told where the dog is."
    return None
```

When it fails, HEART raises `system.main-function-lost` with that reason. A condition must hold for 5 seconds before it is raised, because one odd reading is not a fault.

Do not report "the dog is far away" or "the dog is out of the safe zone" as a fault. A dog on a long walk is not a broken collar.

---

## Step 3: List what the collar knows, and how private each item is

Decide every value's privacy level **before** writing code. Location is the most private data a consumer device can hold: a few days of positions show where the owner lives, when they walk, when the house is empty, and where the dog is right now, for anyone who wants to take it.

| Value | Source | Privacy | Why |
|---|---|---|---|
| Healthy or not; open fault codes | Derived by HEART | `public` | The only things HEART Hub ever receives (Step 8) |
| Battery level and status (ok, low, empty) | Fuel gauge | `public` | Harmless, and useful to anyone the owner shares the page with |
| Battery hours left in the current mode | Fuel gauge and mode | `internal` | For the owner |
| Collar temperature | Sensor | `internal` | The collar's health |
| GNSS state (off, searching, fix), satellites, HDOP | GNSS receiver | `internal` | Whether it *can* locate the dog, not where |
| Uplink (home base, LTE-M, down), signal strength, reports waiting | Modem | `internal` | |
| Report interval | Firmware | `internal` | |
| Accelerometer movement | Accelerometer | `internal` | Sensor health; one number, not a history |
| LED mode | Firmware | `internal` | |
| Lost mode on or off | Firmware | `private` | It says the dog is lost right now |
| Activity: resting, walking, running | Accelerometer | `private` | The dog's day, and so the owner's |
| Position, its source and age; distance from home; in the safe zone; home base in range | GNSS, home base | `private` | Where the dog is. "Home base in range" alone tells whether the dog is at home |
| Safe-zone events, heat warnings, the track | Firmware | `private` | A history of where the dog has been: the most private of all |
| The safe zone's centre (the home) | The owner's app | `private`, kept in the app or encrypted on the collar | It is the owner's address |
| Owner's name, address, phone; the dog's name | The maker's account system | **not in the HEART** | The HEART does not need them, so it never has them. The dog's name belongs on an engraved tag |

Note what is **not** an alert: the dog leaving the safe zone, the dog lying still, the dog being hot. Each is a fact about the dog. The collar's firmware records it as a `private` Rhythm event (`zone.left`, `zone.returned`, `heat.risk`) for the owner's app. Only the collar's own problems become alerts.

---

## Step 4: The two kinds of message, side by side

| | A private event about the dog | An alert about the collar |
|---|---|---|
| Example | `zone.left`: "The dog has left the safe zone alone, 93 m from home" | `sensors.no-reading`: "GNSS has had no fix for a long time" |
| Raised by | The product's firmware (its main job) | HEART (noticing) |
| Message | `heart.rhythm`, category `domain`, `heartprivacy: private` | `heart.arrhythmia` with a Part 3 fault code |
| Carries | The distance and position, whether the owner was with the dog | Only the code, severity, status and times, in public |
| Goes to | The owner's app, with the owner key | The owner, and in public form to HEART Hub |
| Changes the Pulse status | No | Yes: degraded or failing |

The hot car shows why both are needed. The collar's firmware sends the owner a **private** `heat.risk` warning as soon as the collar is above 40 Cel and the dog is away from home without its owner: "Is it shut in a car? Check on the dog now." Above 45 Cel, HEART raises the **collar's** fault `performance.too-hot`, because a lithium cell must not get that hot. HEART Hub sees `performance.too-hot` and nothing else: not where the car is, not that a dog is in it.

---

## Step 5: The faults it recognises

Every alert carries a fault code from Part 3, Section 5.4, so the collar, the owner's app and any monitor site read it the same way. Each code below was checked against that list.

| What happens | Fault code | Severity | Status of the collar |
|---|---|---|---|
| No fix and no uplink away from home, or the battery at 3 % | `system.main-function-lost` | critical | failing |
| Completely still (not even breathing) for a long time, and cooler than on a dog: it may have slipped off | `system.main-function-degraded` | warning | degraded |
| Battery below 15 % | `power.battery-low` | warning | degraded |
| Collar above 45 Cel | `performance.too-hot` | error | degraded |
| The accelerometer gives the very same reading again and again while GNSS shows the collar moving | `sensors.stuck` | warning | degraded |
| Away from home: 3 failed fixes in a row and 150 s with no position | `sensors.no-reading` | warning | degraded |
| Neither LTE-M nor the home base answers | `connectivity.link-lost` | warning | degraded |
| Silent for three Pulse intervals | `system.flatline` | | raised by HEART Hub |

Three choices to notice:

- **"Not moving" is careful.** A sleeping dog is still, and that is not a fault. A collar on a dog always shows breathing and the dog's warmth; a collar lying on the grass shows only sensor noise and cools to the air. Only both together raise an alert, and its explanation says "HEART cannot be sure: check that the collar is on the dog". On a hot day the check cannot tell at all. The code is `system.main-function-degraded`, not `-lost`, because the collar still works; it may just be tracking a patch of lawn. The simulator waits 3 minutes so the demo is short; a real collar would wait about 30.
- **No fix is normal indoors.** At home the home base gives the position and GNSS is off, so `sensors.no-reading` is raised only away from home.
- **Water.** A collar gets wet, and a leak would be worth noticing, but Part 3 has no fault code that fits water getting in (`security.tamper-detected` means someone opened it, which is not the same). So this design does not raise one. A maker who can detect it would use a product code under its own domain, such as `com.example.pets.water-ingress`, described in the Card's `events` (Part 3, Section 5.3).

The thresholds here are illustrations. A real product takes them from its own cell, antenna and enclosure.

---

## Step 6: Commands, approvals and the welfare guard

The owner can change three things, with the **owner key**, through the local API. Each is a capability, listed in the Card for the owner only:

| Change | Physical? | How it runs |
|---|---|---|
| **Lost mode** on or off (`collar.lost_mode`): a GNSS fix and a position report every 30 s instead of every 10 minutes | **No**: it changes reporting, nothing the dog feels | At once with the owner key. Its Action shows what it costs: in the simulator, from weeks to about a day on a full battery |
| **LED** off, steady or blink (`collar.led`) | **Yes**: light at a living animal's neck | Waits for the owner's proof of presence |
| **Buzzer** at a volume for some seconds (`collar.buzzer`) | **Yes**: sound at a dog's ear | Waits for the owner's proof of presence, and the collar's welfare guard applies |
| Anything else: the safe zone, GNSS, the uplink, a property | | Refused: `403 capability-not-allowed`. Set in the maker's app, not through HEART |

**Proof of presence for a collar.** Nobody presses a button on a dog's neck, so the proof is the owner confirming the change on their phone with a **passkey** (Part 6, Section 3: `webauthn` over the exact Action digest, with user verification). A request with only the key is not enough: an approval without proof is refused with `403 presence-required`, so a stolen key, or an AI agent holding one, can never sound the buzzer at the dog. Under Part 6 a person who starts a physical Action sends the proof with the request itself; the app would do that in one step.

**In the simulator** there is no passkey. A physical request waits, and the proof is a file: `touch button.press` in the simulator's working directory stands for the owner confirming on the phone. The simulator records that honestly as `presenceMethod: device-button`, and refuses any `presence` it is sent over the network (`403 presence-invalid`), because it cannot verify a passkey.

**The welfare guard stays in the collar**, not in HEART: the buzzer is never louder than 50 %, never longer than 5 s, never sooner than 60 s after the last time. A request outside it is refused with `409 guard-failed`, when it is made and again after the owner confirms. The hardware's own range (0 to 100 %, 1 to 30 s) gives `422 out-of-range`.

**HEART never uses the LED or the buzzer by itself**, not even to escalate. And there is no Stop button: the standard requires Stop from Autonomy Level A3 up, and this HEART takes no automatic action.

---

## Step 7: Tell a person

Autonomy Level A1 needs an escalation route. For a collar it is the **owner's app**: a notification with the fault and its explanation ("3 GNSS fixes failed in a row, 185 s without a position. Thick trees or a building block the sky; the collar keeps trying"). Change `escalate()` to send it. With no internet, the collar's messages wait on the collar and go when the uplink is back; the home base can also show a light.

HEART Hub (Step 8) adds one more: an email when the collar goes silent.

---

## Step 8: Register and watch it on HEART Hub

HEART Hub lets an owner see the collar's health from anywhere, on a private page.

1. **First start.** The collar makes a random device ID, claim code and key, keeps them in secure storage, and the claim code is printed with a QR code on a card in the box.
2. **Sign in.** The owner opens `https://heartcortex.ai/login` and enters an email address. A 6-digit code arrives; there are no passwords.
3. **Register.** At `https://heartcortex.ai/register?id=<device ID>` (the QR code opens it), the owner enters the claim code. Holding the collar's box is the proof of ownership.
4. **Watch.** The page, `https://heartcortex.ai/device/<device ID>`, shows the collar's status and its open and recent alerts, private to the owner until they share it.
5. **Silence.** If no report arrives for three Pulse intervals (a flat battery, a collar at the bottom of a lake), the page shows a Flatline and the owner gets one email.

Only the public Pulse and public alerts are sent, and the site keeps only the status, timing and fault codes. **No position, safe-zone event, activity or reading can reach it**: they are not in the public messages at all. Sharing the page with a dog sitter shares "the collar is healthy, the battery is low", never where the dog is.

**Your own server.** An owner, a kennel or the maker may rather keep even that on their own machine. HEART Hub is open code, and any site that follows the HEART Hub profile works with every HEART device. [`ownMonitorServer/README.md`](../ownMonitorServer/README.md) runs the same site on one computer with Node, its data in one file:

```sh
node ownMonitorServer/server.mjs
python3 dogCollarSimulator/dogCollarSimulator.py --monitor-url http://127.0.0.1:8787 --scenario all --speed 5
```

For collars on the internet, put HTTPS in front of it (its README shows Caddy): devices refuse a monitor site without HTTPS, except on the same machine.

---

## Step 9: Try it with the simulator

```sh
python3 dogCollarSimulator/dogCollarSimulator.py --scenario all --clock 09:00
```

It prints a device ID and claim code; register them as in Step 8 and watch the page while it stages each event in turn. **Everything is simulated**: a dog's day (sleep at night, rest and play in the garden, walks with the owner, now and then a wander alone), its position as a random walk in and around a 60 m safe zone round a made-up home origin, its activity from a simulated accelerometer, the collar's temperature, GNSS fix quality, the home base and LTE-M uplink, and a battery that drains faster with GNSS, the uplink and lost mode.

| Scenario | What happens | What you see |
|---|---|---|
| `lowbattery` | An old cell at 15.6 %, losing charge by itself | `power.battery-low`; then the owner charges it |
| `wander` | The gate is open: the dog wanders off alone, and the owner fetches it | **No alert.** A private `zone.left` (alone, 93 m from home) and `zone.returned`, for the owner's app only |
| `link` | No uplink for 4 minutes, during the wander | `connectivity.link-lost`; the private events wait on the collar; the page shows nothing new until the link is back |
| `off` | The buckle opens: the collar lies on the grass | `system.main-function-degraded`, "may have slipped off the dog" |
| `hotcar` | The dog is left in a parked car in the sun | A private `heat.risk` warning, then the collar's own `performance.too-hot` |
| `stuck` | On a walk, the accelerometer freezes | `sensors.stuck`: the same reading again and again while GNSS shows movement |
| `nofix` | The walk goes into thick woods | `sensors.no-reading` after 3 failed fixes and 150 s without a position |

Turn on lost mode with the owner key and watch the battery estimate fall; ask for the buzzer and see it wait, be refused over the network, and run after `touch button.press`; ask for a loud one and see the guard refuse it. The local API, every option and the test are in the simulator's [README](../dogCollarSimulator/README.md).

| What is simulated | What a real collar would need |
|---|---|
| The dog, its owner and its day | A dog |
| Positions in metres from a made-up origin | Latitude and longitude, kept private and encrypted at rest |
| GNSS fixes and their quality, on a timer | The receiver's own fix, satellites and HDOP |
| The uplink as up or down | The modem's real registration and the home base's Bluetooth |
| The battery as average currents per mode | A fuel gauge |
| The safe-zone check in the same process | The collar's firmware and the maker's app |
| `touch button.press` as proof of presence | A passkey on the owner's phone (`webauthn`), verified for the exact Action digest |
| HEART noticing, API and reporting in one process | Noticing on the collar (HEART Line); API and reporting on the home base or server |

---

## Step 10: Test it, rate it and claim only what passed

1. Run the simulator's end-to-end test, `python3 dogCollarSimulator/test_e2e.py`. It checks against a local HEART Hub site that every message is valid against the official schemas; that public views carry no location of any kind; that lost mode runs at once and shows its battery cost; that the LED and buzzer wait for presence and are refused over the network with `403 presence-required`; that the buzzer's guard holds; that the wander is a private event and never an alert; that every staged fault reaches the owner's page with its code; and that no position, coordinate or safe-zone event ever reaches the site.
2. For a real collar, run the **HEART-Rating client** (`heart-rating/client/heart_rating.py`) against the HEART API, and the hands-on tests of Part 9: cover the antenna, take it into a basement, cut the cellular network, drain the battery, leave it on a lawn, heat it (safely, without a dog).
3. Publish a **Declaration** with every result.
4. Claim **HEART Basic, Autonomy Level A1**, and nothing more until the tests for more have passed.

| Level | Fits a tracking collar? |
|---|---|
| **A1** (notices and tells a person) | Yes: this guide |
| **A2** (says why, with a confidence) | Yes, at HEART **Connected**, with Rhythm events behind each cause, on the home base or the maker's server: "no fix for 10 minutes; the last fix had 4 satellites and HDOP 4.2, falling: probably under thick trees, confidence medium" |
| **A3** (suggests a fix and waits for a yes) | Yes, with HEART **Full** (approval and Stop through the standard API): "the battery will not last the walk at this rate: turn lost mode off?". Never for the LED or buzzer without the owner's passkey |
| **A4** (fixes known problems by itself) | Only for non-physical settings, as adopted Playbooks on the owner's allowlist for `rule`, inside an envelope: for example "battery below 10 % and lost mode on: report every 2 minutes instead of 30 s". **Never** the LED or the buzzer, which act on the dog, unless the owner adopted the Playbook as physical, with presence |
| **A5** (works out fixes for new problems) | The `ai` and `evolving` modules, with Trials. For a collar this adds little and risks much: keep the dog's welfare out of it |

HEART conformance says nothing about radio, battery or animal-welfare approval. The collar still needs its own (for example radio and battery safety marks, and the rules on animal welfare and location privacy in each market).

---

## What the case study shows

- **The main data is the most private.** A tracker exists to know where the dog is, and that is exactly what must never leave the owner. HEART needs only *whether* the collar can locate the dog, never *where*.
- **A fact about the dog is not a fault of the collar.** Leaving the safe zone, lying still and being hot are private events for the owner. Only the collar's own problems become alerts, and only their codes reach HEART Hub.
- **One problem can be two messages.** A dog in a hot car is a private warning about the dog and a public fault code about the collar's battery, and neither says where the car is.
- **"Physical" means "acts on a living thing".** Lost mode changes a lot (a battery that lasts a day instead of weeks) but nothing the dog feels, so it runs at once. A buzzer for three seconds is physical, so it waits for the owner's passkey, and the collar's welfare guard refuses it if it is too loud, too long or too soon.
- **HEART can be split where the power is.** Noticing fits on a sleeping microcontroller in about a kilobyte; the API and reporting run on the home base or the maker's server.
- **It found gaps in the templates.** The bare-metal C template raises only `system.main-function-lost` today; the ESP32 Full starter has no low-power mode; and Part 3 has no fault code for water getting into a device.
