IRI® FILLER PRO · DOCUMENTS
‹ back · zurück · 返回

IRI Filler Pro — HAL Interface Specification v0.6

Confidential — under NDA · YAVU · rev. 0.6, 2026-08-18 Audience: manufacturer electronics/firmware engineers Scope: everything the manufacturer implements in firmware. Nothing more.


1. The two-layer model (contract)

Layer Who Contents Change process
/core YAVU only licence client, state machine, dose limit (60 µl), shot counting/billing, offline unlock codes, OTA logic, display screen content closed — manufacturer never edits
/hal Manufacturer pin mapping, motor/servo/actuator drivers, sensors, power/charging, display panel driver pull request against the YAVU repository; main is protected, YAVU reviews & merges

The manufacturer implements the C++ interfaces in src/hal/hal.h (v0.6) for the chosen controller. The reference target is ESP32-S3 (hal_esp32.cpp stub provided). The core never touches registers, pins or buses — if a hardware detail changes, only /hal changes.

Deliverable for the quotation: implementation of the six interfaces below + board bring-up. No server, no platform, no application logic, no screen design — these exist already (see repository: API contract, acceptance test plan, UI screens, boot assets).

Changes in v0.6 (2026-08-18): - IFirmwareUpdate is new (§2.11) — the field-update sink. Core downloads, verifies (SHA-256) and only then flashes; only in the dock. Return nullptr if the board cannot update in the field. - IDisplayUi::showCodeEntry() added with a no-op default (§2.10): the offline unlock code is now typed on the four panel keys; nothing else to implement. - Watchdog / brownout policy (§2.12) documented; main.cpp arms the task watchdog. - Wi-Fi onboarding requirement stated in §6 — your choice, must be repeatable by the user.

Changes in v0.5 (2026-08-10, later the same day): - IUserInput is new (§2.10) — the four front-panel keys. Core now drives dose, end-treatment and fault acknowledgement from them, so the device is operable without a display module. Return nullptr if your device has no local panel. - IVoiceCoilDriver::busy() is new and mandatory (§2.9). Core no longer bills a voice-coil shot until the driver reports completion and plungerMm() confirms the plunger actually moved. "No error" is not proof of delivery. - The licence transport now requires a pinned root CA (§6). It fails closed.

Changes in v0.4 (2026-08-10): added IPersist (§2.7) and halBegin() (§2.8); BiphasicProfile timing changed from milliseconds to microseconds; the reference hal_esp32.cpp now provides working stubs for every factory (including halVoiceCoil(), which returns nullptr on spring-drive builds).

2. Interfaces to implement (from hal.h v0.6)

2.1 ICockingMotor — gearmotor + pinion on ring gear (middle sleeve)

2.2 IDoseServo — rotates the existing front dose sleeve

2.3 IReleaseActuator — fires nothing by itself

2.4 ITrigger — TOP button on the grip

2.5 IPowerSystem — battery + rear dock charging

2.6 IDisplayUi — panel driver only

2.7 IPersist — non-volatile key/value store (NEW in v0.4)

2.8 halBegin() — one-time platform init (NEW in v0.4)

2.9 IVoiceCoilDriver — only on voice-coil variants

2.10 IUserInput — front-panel keys (NEW in v0.5)

2.11 IFirmwareUpdate — field update sink (NEW in v0.6)

2.12 Watchdog and brownout (policy, no interface — NEW in v0.6)

6. The licence transport is pinned and fails closed (NEW in v0.5)

Every shot is billed over this channel. Without certificate validation a studio's own access point could impersonate the register, answer "all fine, grace 8760 h" to every heartbeat and swallow the shot uploads.

Core therefore requires a pinned root CA, provisioned into NVS as ca_pem, and refuses to connect at all without it — it never downgrades to an unverified link. A device without a provisioned CA simply behaves as if it were offline: the 72 h grace carries the treatment and then it locks.

Provisioning — two ways, your choice. (a) Cable-free pairing (default): you write NOTHING per device. api_base and the root-CA bundle are compiled into the image (-DIRI_API_BASE, -DIRI_ROOT_CA_PEM — the bundle may hold two roots, current and successor). A fresh device joins Wi-Fi, calls POST /enroll, shows a 6-digit code on the display (IDisplayUi::showPairing), and receives its token and unlock secret once the operator pairs it in the register. Firmware stores both in NVS. (b) Factory token: POST /admin/devices/provision in the register returns device_token + unlock_secret_hex once; write dev_token (string) and unlock_key (32 raw bytes) into NVS namespace iri on the line. Either way, NVS values override the compiled defaults.

Wi-Fi onboarding (your choice, please state it in the quotation): the device joins the studio's own Wi-Fi, so the studio has to hand over its credentials once — and again whenever it changes network (a training device moves from our premises to the studio). Two usual options: BLE onboarding from a phone (the ESP32-S3 has BLE on the same chip, no extra part), or a one-time captive portal (the device opens its own access point, the user types the password in a browser). Core reads stored credentials; the onboarding flow itself is yours. It must be repeatable by the end user, not a factory-only step.

3. Sequencing (owned by core — for reference)

Menu → start treatment → startCock() → [cocked] → treatment cockpit
  → trigger pressed → core checks (licence · cocked · dose · not inDock)
  → releaseActuator.pulse() → shot → auto startCock() → …
End treatment → startDecock() → [home] → trigger disarmed → menu

Radio: pre-certified Wi-Fi module (CE-RED modular approval) — see Drive Concept v1.3 §8. Wi-Fi/TLS stack and licence traffic are core; HAL only guarantees the module is wired per its reference design.

3b. Verifying your HAL against core, before any hardware exists

The repository ships a simulated HAL and a host test suite. They run on a plain PC in about a second, with no ESP32 and no toolchain:

./tools/host_test.sh      # runs the full device logic against a simulated HAL (204 checks)
./tools/syntax_check.sh   # compiles the ESP32-specific sources with lightweight shims

test/host/hal_sim.cpp is a complete worked example of every interface in this document — use it as the reference for what core expects from each call.

4. Acceptance (payment-relevant)

Each HAL function maps to measurable tests in docs/ACCEPTANCE_TEST_PLAN.md (sections A–E): re-cock times fast/quiet, decock-on-end, dose positioning accuracy, trigger debounce, dock-shot-inhibit, noise ≤ 45 dB(A) @ 30 cm (quiet mode), battery/charging telemetry. Milestone payments are gated on these tests.

4b. Safety features that must be quoted AND tested (introduced in v0.4, item 3 revised 10.08.2026)

These three are requirements, not options. Each needs a line in the acceptance test plan before the corresponding milestone can be signed off:

  1. Non-backdrivable holding element on the cocking drive (worm stage or ratchet pawl). The spring must stay safely cocked with power removed, and be releasable under motor control on the next power-up. ICockingMotor says nothing about holding today — quote it explicitly. Test: cock, cut power, verify no backdrive; power up, verify the controlled de-cock runs before the device becomes ready.
  2. Mechanical dry-fire block — the device must not be able to fire with no chamber fitted, or with an empty one. Firmware has no way to know the fill level. Test: attempt to fire with no chamber, and after the chamber's fifth 60 µl shot.
  3. Chamber presence detection at the chamber seat — a plain switch or contact telling the device that a chamber is fitted at all. It feeds the dry-fire block in item 2. Test: attempt to fire with no chamber fitted; fit each chamber and verify normal operation.

Changed 10.08.2026 — please read, this REDUCES your scope. Earlier revisions asked for electrical chamber keying (coding resistor or Hall key) so the device could tell the two chambers apart by itself. That requirement is withdrawn. Both chambers hold 0.3 ml and use the same screw thread, and the chamber is a proven, tooled part that we are not changing to carry a coding element. Do not design a coding element into the chamber. Instead the operator states which chamber is fitted (core::Chamber, already implemented — the device asks once after every power-up and will not fire until it is answered), and the firmware cross-checks that answer against the measured plunger travel of every shot. No new HAL interface is needed; the HAL contract stays at v0.5.

Two things we do ask of you here: - Leave room at the chamber seat for two spring contacts or a small magnet pocket, without fitting them. It costs nothing in the design and keeps the option open if practice shows the manual selection is not reliable enough. - Measure the nozzle bore of the 5 ml refill chamber when you receive the reference unit. The 0.3 ml chamber is measured at 0.18 mm (see measured data); the refill chamber is believed to be narrower but has never been measured, and firmware currently carries a placeholder for it. Background: docs/CHAMBER_SELECTION.md.

5. Open technical points

  1. Motor final sizing after torque measurement on the manual device (≥ 2× margin, ring-gear ratio 3:1–5:1 adjustable).
  2. Release actuator choice: mini solenoid vs. micro servo — evaluate on the rig.
  3. Display controller: ESP32-S3 (MJPEG) vs. ESP32-P4/SoC (720p H.264).
  4. Dock contact system: pogo pins vs. inductive.
  5. Trigger position on the ergonomic mock-up (top of grip, thumb/index).

Source: firmware repo docs/HAL_SPEC.md · confidential · NDA