HEART Embedded Systems logoHEART

HEART Signal Tables#

Reading tracks2 Intermediate
Intermediate: all of it, if your product serves many signals (Modbus, CAN, firmware variables). Which track am I?

Every signal in a product, declared once, checked by a person

Author: Milind K. Patil, Syncaissa Systems Inc. Status: Working draft 1, November 2026. To become part of Part 8 (Product Fit) of the HEART Standards Family. Version: 1.0 (draft) Schema: schemas/1.0/signal-table.schema.json · Example: examples/signal-table.json Track: Intermediate: all of it, if your product serves many signals (Modbus, CAN, firmware variables).


In one minute#

A product is full of signals: temperatures, speeds, currents, switch positions, counters. A HEART that knows them can notice problems early, explain them, and give an AI model the facts it needs.

A signal table (signals.json) lists every signal the HEART may read, with its unit, range, privacy and whether it may ever be changed. Three rules make it safe:

  1. Declared or invisible. A signal not in the table is never served or sent anywhere.
  2. Careful by default. Nothing is writable unless a person says so. A safety signal is never writable.
  3. A person checks it. A HEART MUST NOT load a table that has not been reviewed.

Nobody has to write the table by hand. The generator drafts it from files engineers already have: a Modbus register map, a CAN database, or the firmware's own variables. A person then checks the draft and seals it.


1. What a signal is#

FieldMeaningExample
nameThe system ID, then a lower-case signal namecom.example.cool-air.room_temp
title, descriptionFor people, and for AI models"Room temperature"
typenumber, integer, boolean, string or enumnumber
unitThe UCUM code, the standard list of unitsCel (degrees Celsius)
rangeThe possible values. Outside them, the sensor is probably broken.{"min": -20, "max": 60}
sourceWhere it comes from: a bus address, or a pin or variable on the chipModbus input register 0 (table: input, register: 0), scale 0.1
sampleSec, historySecHow often to read it, and how much history to keep5, 3600
privacypublic, internal, private or never-share (Part 5)internal
writableMay HEART ever change it? Default: no.false
physicalDoes changing it move, heat, power or switch something?true for a setpoint
safetyIs it part of a safety function? If so: read-only, always.true for a pressure trip
deriveSummaries to keep, from the shared statistic list: value (latest), min, max, mean, stddev, trend (units per minute, least squares), overshoot (% of the setpoint step), count (events in the window)["mean", "trend"]
alertsWhen to raise a fault code by itselfout of range, sensors.out-of-range

A table MAY also carry $comment, free text for people such as a licence line. HEART ignores it.

Where signals can come from#

protocolFieldsExample
modbus-tcp, modbus-rtutable (holding, input, coil, discrete), register (the 0-based protocol address within table, as sent on the wire; old reference numbers such as 30001 or 40021 MUST NOT be used), scale, offsetAn air conditioner's control board
canframeId, startBit, bits, byteOrder, signed, scale, offsetA vehicle, a chiller, a battery
on-chipsymbol (a firmware variable) or channel (an ADC channel or pin)A microcontroller reading its own sensors
Any adapter of API Standard 14.3Following that adapter's request formatBACnet, OPC UA, I²C

2. The rules#

  1. Declared or invisible. A HEART MUST NOT read, serve or send a signal that is not in its table.
  2. Reviewed before use. A table is usable only when it has a reviewed entry (who, and when) and no signal is still marked draft.
  3. Writable only on purpose. writable defaults to false. Writing a signal passes the approval gate like any action (Part 6), and writing a physical signal counts as a physical action.
  4. Safety signals are read-only. A signal marked safety MUST NOT be writable, and the schema rejects one that is. HEART watches safety signals; it never controls them (Part 6, Section 9).
  5. Watching never disturbs control. A HEART reads signals at low priority, from copies or shadow registers where possible, never from inside a control loop.
  6. Personal signals are private. A signal about a person, such as occupancy, location, voice or camera, MUST be private or never-share.
  7. Budgets are fixed. Sampling and history have fixed limits, so watching never fills the device's memory or wears out its flash.

3. From what you have to a sealed table#

register map (CSV) ─┐
CAN database (.dbc) ├──► draft ──► a person checks ──► seal ──► use
firmware header (.h)┘    (all signals marked draft,           (reviewed: who, when)
                          nothing writable)

The generator, heart-templates/tools/heart_signals.py, has four steps:

StepCommandWhat it does
Draftheart_signals.py draft --system com.acme.chiller --modbus regs.csv --dbc bus.dbc --header signals.h > signals.jsonTurns every register, CAN signal and annotated variable into a draft signal. Converts units to UCUM.
Checkheart_signals.py check signals.jsonLists errors, and every decision still to make
Sealheart_signals.py seal signals.json --by "A. Engineer"Marks the table reviewed. Refused while anything is a draft or wrong.
Exportheart_signals.py export signals.json --out custom/Writes property definitions that heartd loads (Modbus signals, today)
Export as Cheart_signals.py export signals.json --out main/heart_signals.hWrites every signal as a C table for a device, so the chip needs no JSON parser. Used by the esp32-full template.

What the generator does on its own, so the reviewer doesn't have to start from nothing:

  • Every signal is a draft, and nothing is writable, even if the register map says it can be written. The reviewer is told, and decides.
  • Names that suggest a person (occupancy, location, voice) are made private.
  • Names that suggest a safety function (trip, interlock, emergency stop) are marked safety, so they can never be made writable.
  • Names that suggest an actuator (motor, valve, setpoint) get a note: "if made writable, also set physical".
  • A missing range gets a note, because without one a broken sensor cannot be noticed.

Annotating firmware variables#

The generator reads simple C declarations, with optional hints after // heart:

volatile float room_temp_c;       // heart: unit=Cel range=-40..85 title="Room temperature"
volatile uint16_t fan_rpm;        // heart: unit=rpm range=0..3000
volatile float fan_speed_target;  // heart: unit=% range=0..100 writable

writable here is only a suggestion to the reviewer. It never makes a signal writable by itself.


4. How a signal table fits the rest of HEART#

HEART partHow signals appear
Part 3 (Information Model)Each signal is a property: readable at /heart/v1/properties/{name}, with its unit, quality and time
Part 3, fault codesalerts raise standard codes such as sensors.out-of-range and sensors.stuck
Part 2 (Autonomy Levels)Signals give the evidence for "why" at A2, and the trends a Playbook watches at A4
Part 5 (Privacy)Each signal's privacy decides who may read it, and whether it may ever reach an AI model
Part 6 (Human Control)Writing a signal is an action, and physical writes need a person's proof of presence
TemplatesThe Full starter templates (first: esp32-full) use the sealed table, exported as C, and serve each signal as a property

5. Open items#

ItemWhy it matters
physical on property definitionsThe custom definition schema does not yet list physical for properties, and the reference implementation does not yet gate a physical property write as a physical action. Both must be fixed before writable physical signals are used.
Export for CAN and on-chip sourcesExport writes Modbus definitions today. CAN definitions need the CAN adapter's request format to be published.
More source formatsOPC UA node sets, BACnet object lists, EtherCAT ESI files and ELF symbol tables would cover most other products.
Derived signals on small chipsmean, trend and stddev need fixed-memory formulas for microcontrollers.