HEART Embedded Systems logoHEART

Guide 11: A HEART-Embedded dog tracking collar#

Reading tracks2 Intermediate
Intermediate: private location, and a person's changes that run at once or wait for proof of presence. Which track am I?

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/, 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 appGoes to HEART
Taking GNSS fixes, the position itself, the trackKnowing whether the collar has a fix, never where it is
The safe zone, and telling the owner when the dog leaves itWatching the battery, GNSS, uplink, temperature and accelerometer
Sending positions to the owner's appRaising alerts with standard fault codes
The LED and buzzer drivers, and their welfare limitsTelling the owner, even with no internet
Power management, sleep, the modemAnswering "how is the collar?" in one standard format
Anything that keeps the dog safePassing 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#

PartChoiceWhy
Microcontroller and modemA 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 areasOne chip for positioning and the uplink, sleeping between fixes
GNSSIn the module, with an external patch antenna on the top of the collarIt needs open sky: under thick trees or indoors it gets no fix
Home baseA small mains-powered box at home, Bluetooth to the collar, Wi-Fi to the internetAt home the collar needs no GNSS and no cellular: it saves most of its battery
AccelerometerA 3-axis MEMS sensorResting, walking, running; and whether the collar is on a dog at all
TemperatureA sensor next to the batteryA lithium cell must not get too hot, and a hot collar may mean a hot dog
BatteryA rechargeable lithium-polymer cell, about 900 mAhWeeks at home, days away from home, about a day in lost mode
LED and buzzerA small LED and a piezo buzzerTo see the dog in the dark, and to call it or find the collar. Both act on the dog
CaseSealed, with a charging contactA 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
PartWhat it doesHEART's place
CollarFixes, safe zone, uplink, LED, buzzerNoticing, 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 serverRelays the collar's messagesThe 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 appShows the map, the safe zone, lost modeReads 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:

{ "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:

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.

ValueSourcePrivacyWhy
Healthy or not; open fault codesDerived by HEARTpublicThe only things HEART Hub ever receives (Step 8)
Battery level and status (ok, low, empty)Fuel gaugepublicHarmless, and useful to anyone the owner shares the page with
Battery hours left in the current modeFuel gauge and modeinternalFor the owner
Collar temperatureSensorinternalThe collar's health
GNSS state (off, searching, fix), satellites, HDOPGNSS receiverinternalWhether it can locate the dog, not where
Uplink (home base, LTE-M, down), signal strength, reports waitingModeminternal
Report intervalFirmwareinternal
Accelerometer movementAccelerometerinternalSensor health; one number, not a history
LED modeFirmwareinternal
Lost mode on or offFirmwareprivateIt says the dog is lost right now
Activity: resting, walking, runningAccelerometerprivateThe dog's day, and so the owner's
Position, its source and age; distance from home; in the safe zone; home base in rangeGNSS, home baseprivateWhere the dog is. "Home base in range" alone tells whether the dog is at home
Safe-zone events, heat warnings, the trackFirmwareprivateA history of where the dog has been: the most private of all
The safe zone's centre (the home)The owner's appprivate, kept in the app or encrypted on the collarIt is the owner's address
Owner's name, address, phone; the dog's nameThe maker's account systemnot in the HEARTThe 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 dogAn alert about the collar
Examplezone.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 byThe product's firmware (its main job)HEART (noticing)
Messageheart.rhythm, category domain, heartprivacy: privateheart.arrhythmia with a Part 3 fault code
CarriesThe distance and position, whether the owner was with the dogOnly the code, severity, status and times, in public
Goes toThe owner's app, with the owner keyThe owner, and in public form to HEART Hub
Changes the Pulse statusNoYes: 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 happensFault codeSeverityStatus of the collar
No fix and no uplink away from home, or the battery at 3 %system.main-function-lostcriticalfailing
Completely still (not even breathing) for a long time, and cooler than on a dog: it may have slipped offsystem.main-function-degradedwarningdegraded
Battery below 15 %power.battery-lowwarningdegraded
Collar above 45 Celperformance.too-hoterrordegraded
The accelerometer gives the very same reading again and again while GNSS shows the collar movingsensors.stuckwarningdegraded
Away from home: 3 failed fixes in a row and 150 s with no positionsensors.no-readingwarningdegraded
Neither LTE-M nor the home base answersconnectivity.link-lostwarningdegraded
Silent for three Pulse intervalssystem.flatlineraised 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:

ChangePhysical?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 minutesNo: it changes reporting, nothing the dog feelsAt 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 neckWaits for the owner's proof of presence
Buzzer at a volume for some seconds (collar.buzzer)Yes: sound at a dog's earWaits for the owner's proof of presence, and the collar's welfare guard applies
Anything else: the safe zone, GNSS, the uplink, a propertyRefused: 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 runs the same site on one computer with Node, its data in one file:

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#

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.

ScenarioWhat happensWhat you see
lowbatteryAn old cell at 15.6 %, losing charge by itselfpower.battery-low; then the owner charges it
wanderThe gate is open: the dog wanders off alone, and the owner fetches itNo alert. A private zone.left (alone, 93 m from home) and zone.returned, for the owner's app only
linkNo uplink for 4 minutes, during the wanderconnectivity.link-lost; the private events wait on the collar; the page shows nothing new until the link is back
offThe buckle opens: the collar lies on the grasssystem.main-function-degraded, "may have slipped off the dog"
hotcarThe dog is left in a parked car in the sunA private heat.risk warning, then the collar's own performance.too-hot
stuckOn a walk, the accelerometer freezessensors.stuck: the same reading again and again while GNSS shows movement
nofixThe walk goes into thick woodssensors.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.

What is simulatedWhat a real collar would need
The dog, its owner and its dayA dog
Positions in metres from a made-up originLatitude and longitude, kept private and encrypted at rest
GNSS fixes and their quality, on a timerThe receiver's own fix, satellites and HDOP
The uplink as up or downThe modem's real registration and the home base's Bluetooth
The battery as average currents per modeA fuel gauge
The safe-zone check in the same processThe collar's firmware and the maker's app
touch button.press as proof of presenceA passkey on the owner's phone (webauthn), verified for the exact Action digest
HEART noticing, API and reporting in one processNoticing 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.
LevelFits 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.