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):** ```json {"id":"esp-hex-01","scene":"A","consent":true,"ts":1720000000} ``` **Payload ack (JSON):** ```json {"ok":true,"id":"esp-hex-01","ts":1720000050} ``` --- ## 4. Edge (ESP / MicroPython) – Minimaler Sender ```python # 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):** ```js // 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.