Hier ist crumb_byte.md im schlanken Crumbforest-Format – copy-paste-sicher, ohne Sonderzeichen.
(Enthält Definition, Topics, Edge-Code, Node-RED-Snippet, ACL, Tests, CO2, Troubleshooting.)


Crumb Byte (CB) – der zaehlbare Funke

Ziel
Ein CB ist ein sicher geloggter Lern-Funke: Kind handelt -> Ereignis wird mit Zeitstempel, Consent und Bestaetigung gespeichert.
CB macht Lernen messbar, ohne persoenliche Daten.

Status: v1
Scope: ESP-Wald (VLAN50), lokaler Broker, Node-RED/DB optional
Abhaengigkeiten: MQTT 3.1.1+, Uhrzeit (Broker/Node-RED), Netz erreichbar


1. Definition

Ein Crumb Byte (CB) ist 1 abgeschlossener Mikro-Zyklus:

  1. Ereignis am Rand (Button/Sensor/Frage)
  2. Payload mit ts (ms), device_id, consent=true
  3. Broker nimmt an
  4. Server bestaetigt Persistenz (ACK)

Kein PII. Nur pseudonyme device_id.


2. Broker-Parameter (Beispiel)

  • Broker IP: 192.168.50.10
  • Port: 1883
  • User (ESP): esp1 (nur write auf incr, read auf ack)
  • User (Dashboard/Node-RED): dash (read alles, write ack/agg)

3. MQTT Topics

crumb/cb/incr         # Edge -> Broker  (einzelner Funke)
crumb/cb/ack          # Server -> Edge  (Persistenz bestaetigt)
crumb/cb/agg/min      # Server -> Dashboard (Rollup pro Minute)
crumb/cb/alert        # Server -> Dashboard (z.B. Flood-Guard)

Payload incr (JSON):

{"id":"esp-hex-01","scene":"A","consent":true,"ts":1720000000}

Payload ack (JSON):

{"ok":true,"id":"esp-hex-01","ts":1720000050}

4. Edge (ESP / MicroPython) – Minimaler Sender

# file: main.py
import ujson as json, utime
from umqtt.simple import MQTTClient

BROKER = "192.168.50.10"
PORT   = 1883
ID     = "esp-hex-01"

c = MQTTClient(ID, BROKER, PORT, keepalive=60)
c.connect()

def cb_funke(scene="A"):
    msg = {
        "id": ID,
        "scene": scene,
        "consent": True,
        "ts": utime.ticks_ms()
    }
    c.publish(b"crumb/cb/incr", json.dumps(msg))

# Beispiel: einmal senden
# cb_funke("hex")

5. Node-RED Mini-Flow (60s Aggregation, Flood-Guard, ACK)

Knoten

  • mqtt-in: topic crumb/cb/incr
  • function: Logik (unten)
  • mqtt-out: crumb/cb/agg/min
  • mqtt-out: crumb/cb/alert
  • mqtt-out: crumb/cb/ack
  • storage: SQLite/Influx (optional)

Function Node Code (3 Ausgaenge):

// Outputs:
// 1 -> crumb/cb/agg/min
// 2 -> crumb/cb/alert
// 3 -> crumb/cb/ack

const now = Date.now();
const W = 60000; // 1 min window

let start = context.get('windowStart') || now;
let cnt   = context.get('cbCount') || 0;

if (now - start >= W) {
  node.send([{payload:{t:start, count:cnt}}, null, null]);
  start = now; cnt = 0;
}

const p = (typeof msg.payload === "string") ? JSON.parse(msg.payload) : (msg.payload||{});
const id = p.id || "unknown";

// Flood guard: max 10 CB per second per device
const bucket = Math.floor(now/1000);
const key = `flood:${id}:${bucket}`;
let secCnt = context.get(key) || 0;
if (secCnt > 10) {
  node.send([null, {payload:{reason:"flood", id, ts:now}}, null]);
  return null;
}
context.set(key, secCnt+1);

// Count + ack
cnt += 1;
context.set('windowStart', start);
context.set('cbCount', cnt);

node.send([null, null, {payload:{ok:true, id, ts:now}}]);
return null;

6. Mosquitto Minimal-Config (Ausschnitt)

/etc/mosquitto/mosquitto.conf

persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
include_dir /etc/mosquitto/conf.d

/etc/mosquitto/acl

user esp1
topic write crumb/cb/incr
topic read  crumb/cb/ack

user dash
topic read  crumb/cb/#
topic write crumb/cb/ack
topic write crumb/cb/agg/min
topic write crumb/cb/alert

Test lokal

mosquitto_sub -h 127.0.0.1 -t 'crumb/#' -u dash -P '***' -v
mosquitto_pub -h 127.0.0.1 -t 'crumb/cb/incr' -m '{"id":"t1","consent":true,"ts":1}' -u esp1 -P '***'

7. Visualisierung (kindtauglich)

  • Sparkline: Balken je Minute (CB/min)
  • Funke-Ring (LED): 1 Puls pro CB, sanft Rot bei Alert, dann Reset
  • Integrity-Badge: green >=95%, yellow 80-95%, red <80%

8. CO2 pro CB (ehrlich, grob)

Formel je Zeitraum:

CO2_per_CB = (Wh_total * Emissionsfaktor_gCO2_pro_Wh) / CB_gesamt

Wh_total = Summe Leistung (Broker + AP + Edge) * Zeit.
Ziel: CO2/CB runter, Integrity rauf.


9. Governance / Schutz

  • Whitelist device_id, keine PII in Payloads
  • Flood guard soft (cooldown 10 s)
  • Consent Flag muss true sein, sonst verwerfen
  • Tgl. Report: CB Summe, Integrity %, Median Latenz, Alerts

10. Testplan (schnell)

  1. Broker offen: nc -zvw3 192.168.50.10 1883
  2. Sub: mosquitto_sub -h 192.168.50.10 -t 'crumb/#' -u dash -P '***' -v
  3. Edge sendet cb_funke("hex")
  4. Erwartet: crumb/cb/incr und crumb/cb/ack erscheinen innerhalb < 500 ms
  5. Nach 60 s: crumb/cb/agg/min mit count

11. Troubleshooting (kurz)

  • OSError 113/116 am ESP: Broker-IP/Port/Firewall checken, Ping vom ESP zur Broker-IP, 2.4 GHz RSSI > -70 dBm
  • Broker startet nicht: doppelte persistence_location entfernen, Logs ansehen
  • Keine Acks: Node-RED-Flow aktiv? Topic-Namen exakt? JSON gueltig?
  • Fluten: crumb/cb/alert pruefen, Sensor entprellen

12. Glossar

  • CB: Crumb Byte, 1 gezaehlter Funke mit Bestaetigung
  • Integrity %: Anteil CB mit ts+consent+persistent OK
  • Flood Guard: Schutz gegen ungewollte Event-Stuerme

Warum das wichtig ist
CB zaehlt nicht Klicks, sondern vertrauenswuerdige Lernmomente.
Klein, pruefbar, freundlich – genau das, was ein Wald braucht.