12 Monate Crumbforest-Wachstum, sortiert nach Waldlogik. Struktur: - 454 Dokumente in 7 organischen Ebenen - Lichtung → Waldrand → Waldmitte → Wurzeln → Krone → Unterholz → Samen - INDEX.md für jede Ebene (eigene Stimme) - WALDKARTE.md (Master-Navigation) - crumbcodex.md (Das Versprechen zwischen Krümel & Bits) - lichtung/willkommen.md (Nullfeld-Einstieg für Kinder) Philosophie: - Waldlogik statt Ordnerlogik - Tiefe, Licht, Wurzeln - wie ein echter Wald - Schutz für Krümel, Tiefe für Gräber, Poesie für Atmende Repos verbunden: - OZM-Keks-Handbuch-v1 (Das Wissen) - Crumb-Core-v.1 (Das Herz) - 194.164.194.191 - crumbmissions (Das Spiel) Wuuuhuuuuu! 🦉✨
244 lines
5.6 KiB
Markdown
244 lines
5.6 KiB
Markdown
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.
|