Docs

API Reference

Use this as the practical runtime reference: what to call, when to call it, and which surfaces are stable versus optional.

Why this page matters

This page explains how API Reference fits into the wider ZeroKernel execution model, what problem it is meant to solve, and what trade-off you are actually accepting when you use it in production firmware. The goal is not to treat API Reference as an isolated API call, but to understand where it sits inside bounded scheduling, queue discipline, fault visibility, and profile selection.

Read this topic as an operational contract. Start from the smallest working path, wire it into a lean profile first, and only expand into richer routing, diagnostics, or transport state after you can prove that the timing outcome is still worth the extra flash and RAM. That mindset is what keeps ZeroKernel useful on small boards instead of turning it into another bloated abstraction.

The safest pattern is always the same: define the runtime boundary, keep the hot path short, measure the effect with compare scripts, and only then scale complexity. The examples below are not filler; they show the smallest repeatable patterns you can lift into real firmware when you need clean integration instead of ad-hoc loops.

Three practical patterns

Full validation sequence

Use this when you need a credible regression pass before publishing numbers or changing docs.

Shell
    bash scripts/run_desktop_tests.sh
bash scripts/run_desktop_benchmark.sh --enforce-performance
bash scripts/run_resource_matrix.sh --enforce-budget
  
Hardware compare pass

Run a focused hardware compare instead of guessing whether a change helped or hurt.

Shell
    bash scripts/run_esp32_modules_compare.sh /dev/ttyUSB1
bash scripts/run_esp32_real_project_demo.sh /dev/ttyUSB1
  
Lean build guard

Lock the build into the intended profile before treating a benchmark or compare as authoritative.

Text
    -DZEROKERNEL_PROFILE_LEAN_NET
-DZEROKERNEL_ENABLE_DIAGNOSTICS=0
-DZEROKERNEL_ENABLE_LEGACY_LABEL_API=0
  

What to verify while you use it

  • Validate timing before you validate aesthetics. A cleaner API is not a win if fast misses rise.
  • Prefer the smallest profile that still matches the workload, then add optional modules only when the measured payoff is obvious.
  • Keep callbacks and transport steps bounded so watchdog, panic flow, and queue limits remain meaningful.

Common mistakes that make results misleading

  • Do not copy a demo pattern into production firmware without measuring it on the real board and real build profile you plan to ship.
  • Do not read success counters without reading queue depth, timing, and workload label next to them.
  • Do not enable heavier diagnostics and compatibility flags in a lean target just because the defaults looked convenient.

Recommended working sequence

Start from the smallest valid path

Boot the runtime, register the minimum useful task set, and prove that the baseline timing is clean before adding optional layers.

Add one layer, then measure it

Introduce routing, diagnostics, or transport one layer at a time so the cost and payoff remain obvious.

Publish only repeatable results

Update docs, charts, or public claims only after the same workload survives the same validation path more than once.

How to read this reference

This page is the operational reference for the ZeroKernel surface that teams touch most often. It is not trying to be a raw symbol dump. The goal is to help you understand which calls belong to setup, which calls belong to the runtime loop, which calls shape tasks, and which calls exist only to observe or escalate runtime behavior. That split matters because embedded firmware becomes much easier to reason about when those boundaries stay clean.

In practice, most firmware only relies on a small subset of the total public surface. A typical node will initialize the runtime, register tasks, route data, and read timing or state counters. A more advanced node may also expose signals, panic hooks, capabilities, or network modules. The purpose of this page is to give you the mental map for those surfaces before you drill into the dedicated pages that document each one in more depth.

If you are new to the runtime, read this page top to bottom once, then keep it open as a lookup table while you build. If you are already integrating ZeroKernel into a product, use it as a checklist: lifecycle first, task control second, routing third, diagnostics last. That order keeps the system understandable and reduces the chance of mixing one-time setup calls with hot-path runtime work.

Lifecycle and boot sequence

CallWhen to call itWhy it exists
ZeroKernel.begin(clockSource)Exactly once during setup or boot.Binds the runtime to a board clock and resets the scheduler state.
ZeroKernel.tick()Continuously from the main loop.Advances the cooperative scheduler and drains bounded work.
ZeroKernel.identity()Any time after begin().Exposes name, vendor, tagline, and runtime version metadata.
ZeroKernel.abiVersion()When validating compatibility.Lets modules and firmware agree on a stable ABI contract.
ZeroKernel.runtimeVersion()At boot banners and telemetry snapshots.Lets the firmware report the real running version.

The most common lifecycle mistake is treating begin() like a cosmetic call. It is not. If you start the runtime without the correct board clock, any claim about cadence, jitter, or compare results becomes weaker. The runtime can only be as stable as the clock contract you feed into it. That is why the examples in this documentation always pass an explicit clock source instead of relying on hidden defaults.

C++
    static unsigned long boardMillis() {
  return millis();
}

void setup() {
  ZeroKernel.begin(boardMillis);
}

void loop() {
  ZeroKernel.tick();
}
  

Task control and task shaping

CallPrimary jobTypical reason
addTask(...)Register a periodic cooperative task.Turn a manual loop fragment into a measurable runtime unit.
setTaskPriority(...)Adjust tie-break order for due tasks.Make critical work win when multiple tasks are due together.
setTaskHeartbeatTimeout(...)Define heartbeat expectations.Detect silent tasks that stop reporting liveness.
suspendTask(...)Pause a task without deleting it.Disable best-effort work in degraded or maintenance modes.
resumeTask(...)Resume a suspended task.Bring non-critical work back after recovery.
heartbeatTask(...)Signal manual liveness.Keep a long-running cooperative task visible to the watchdog.

A task in ZeroKernel is still your code. The runtime does not make it safe automatically. The payoff is that once the task is declared, the runtime can account for it, supervise it, compare it, and expose its behavior in diagnostics. That is why task registration is more valuable than just replacing one callback style with another. It turns informal firmware flow into something the runtime can actually observe.

The practical rule is simple: keep the task body short and non-blocking, then use the task metadata to tell the runtime how to treat it. Priority, heartbeat, and execution contract data are what make the task visible to the rest of the runtime. If you skip that layer, you still get scheduling, but you lose most of the value that makes ZeroKernel feel like a runtime instead of a callback helper.

C++
    void sampleTask() {
  readSensor();
}

void setup() {
  ZeroKernel.begin(boardMillis);
  ZeroKernel.addTask("sample", sampleTask, 10);
  ZeroKernel.setTaskPriority("sample", ZeroKernel::kPriorityHigh);
  ZeroKernel.setTaskHeartbeatTimeout("sample", 50);
}
  
C++
    void maintenanceTask() {
  flushLogs();
}

void onStateChange(ZeroKernel::KernelState state) {
  if (state == ZeroKernel::kStateSafeMode) {
    ZeroKernel.suspendTask("maintenance");
  }
}
  

Routing, commands, and signals

CallPreferred useWhy it matters
makeTopicKey(...)Create stable key-first routes.Moves repeated dispatch off the string path.
publishFast(...)Publish immediate event data.Cheap path for internal state or fast signals.
publishDeferredFast(...)Publish bounded deferred work.Safer path when inline callback execution would be risky.
enqueueCommandFast(...)Queue command work for later draining.Serializes control work in a bounded cooperative queue.
setSignalHandler(...)Subscribe to runtime signal output.Centralizes drops, misses, and escalation events.

For new firmware, key-first routing is the normal path. String-based routing still exists, but it exists as a bridge, not as the strategic path. The more frequently a route executes, the more valuable it is to remove repeated label comparisons from that route. That is especially true when the same topic is used by telemetry, network pumps, or command-driven workflows.

C++
    const auto telemetryKey = ZeroKernel.makeTopicKey("telemetry.sample");

void onTelemetry(long value) {
  latestSample = value;
}

void setup() {
  ZeroKernel.subscribeFast(telemetryKey, onTelemetry);
}

void sampleTask() {
  ZeroKernel.publishDeferredFast(telemetryKey, readSensor());
}
  
C++
    const auto flushKey = ZeroKernel.makeTopicKey("transport.flush");

void triggerFlush() {
  ZeroKernel.enqueueCommandFast(flushKey, 0);
}
  

State, diagnostics, and publishable proof

CallWhat you readWhen teams use it
getStats()Aggregate counters and totals.Dashboards, periodic status reports, compare scripts.
getTimingReport()Tick and task timing summary.Benchmark output and regression gates.
state()Current kernel state.Recovery logic and operational telemetry.
onStateChange(...)State transitions.Alerting when the runtime enters degraded or panic modes.
triggerPanic(...)Explicit panic escalation.Hard-fail cases where hiding the fault is worse.
dumpStats(...)Formatted diagnostics output.Serial-based bring-up and field service sessions.

The diagnostics surface is what turns engineering claims into something testable. If a team says the runtime is deterministic, the real evidence lives in timing reports. If a team says the runtime is resilient, the real evidence lives in state transitions, recoveries, and bounded escalation. That is why this part of the API is not optional from a product perspective, even when some of the heavier diagnostics can be stripped in lean builds.

C++
    void reportTask() {
  auto stats = ZeroKernel.getStats();
  auto timing = ZeroKernel.getTimingReport();

  Serial.print("runs=");
  Serial.print(stats.taskRuns);
  Serial.print(" fast_miss=");
  Serial.print(timing.fastMissCount);
  Serial.print(" state=");
  Serial.println(ZeroKernel.state());
}
  

Three common reference patterns

Most projects repeatedly fall into the same small set of API usage patterns. A minimal node only needs lifecycle plus a couple of periodic tasks. A routed node adds topic keys and deferred publication. A supervised node adds signal hooks, heartbeat rules, and state transitions. Once you can recognize those patterns, the API becomes easier to navigate because you stop seeing it as a long list of functions and start seeing it as a set of operating modes.

C++
    // Pattern 1: minimal cooperative loop
ZeroKernel.begin(boardMillis);
ZeroKernel.addTask("blink", blinkTask, 250);

void loop() {
  ZeroKernel.tick();
}
  
C++
    // Pattern 2: routed node
const auto key = ZeroKernel.makeTopicKey("sensor.sample");
ZeroKernel.subscribeFast(key, onSample);
ZeroKernel.addTask("sample", sampleTask, 20);
  
C++
    // Pattern 3: supervised node
ZeroKernel.setTaskHeartbeatTimeout("sample", 50);
ZeroKernel.onStateChange(onKernelStateChanged);
ZeroKernel.setSignalHandler(onKernelSignal);
  

API reference FAQ

Is this the complete symbol reference?

No. This is the operational reference for the calls most teams use first and most often. The public header is broader, but this page is organized around real usage, not symbol order.

Should I prefer the fast API over legacy string calls?

Yes. Key-first routing is the preferred path when you care about size, speed, and predictable overhead.

Should I call every API from every task?

No. Keep lifecycle in setup, keep scheduling in the main loop, keep routing in cooperative task logic, and keep diagnostics in dedicated reporting paths. Mixing everything everywhere makes firmware harder to reason about.

What is the safest first subset to learn?

Start with begin, addTask, tick, makeTopicKey, publishDeferredFast, and getTimingReport. That subset already covers most common runtime patterns.