/* SPDX-License-Identifier: Apache-2.0 */ /* Settlement node on Zephyr: reads "A " or "B " lines on the console (the charger link or the sensor task), runs the keystone loop, and writes one OER/2 receipt per line. Energy fields come from a meter driver; this skeleton uses a cycle counter for compute and constants for the rest until the rail meter is wired in. */ #include #include #include #include #include #include #include "settle.h" static settle_ctx_t ctx; static char out[4096]; static uint32_t cycles_now(void){ return k_cycle_get_32(); } int main(void){ uint8_t seed[32]; const struct device *ent = DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)); if (device_is_ready(ent)) entropy_get_entropy(ent, seed, sizeof seed); else memset(seed, 0x5a, sizeof seed); /* In production the seed lives in the signing element (OpenTitan or ATECC608) and never in RAM. */ settle_init(&ctx, seed, 300.0, 86400.0); console_getline_init(); printk("settle node ready\n"); int64_t last_ms = k_uptime_get(); const double standby_w = 1.8e-3, shared_w = 0.0; while (1) { char *line = console_getline(); if (!line || strlen(line) < 3) continue; char cls = line[0]; uint32_t id = (uint32_t)strtoul(line + 2, NULL, 10); int64_t now_ms = k_uptime_get(); uint32_t c0 = cycles_now(); settle_meter_t m; m.movement_j = cls == 'A' ? 2.3e-6 : 0; /* wired charger link: replace with the meter's reading */ m.idle_alloc_j = standby_w * (now_ms - last_ms) / 1000.0; m.shared_alloc_j = shared_w * (now_ms - last_ms) / 1000.0; m.upstream_j = 0; m.method = "calibrated_model"; m.weakest_how = "projected"; m.weakest_input = "standby_w"; m.compute_j = 0; m.latency_s = 0; settle_result_t res; int rc = settle_decide(&ctx, cls, id, now_ms / 1000.0, &m, out, sizeof out, &res); uint32_t cyc = cycles_now() - c0; (void)cyc; /* the next receipt's compute_j = cyc * ctx.cycle_j; a two-pass emit would put it on this one */ if (rc == 0) printk("%s\n", out); else printk("{\"error\":\"decide\",\"rc\":%d}\n", rc); last_ms = now_ms; } return 0; }