Initial commit: Crumbforest Architecture Refinement v1 (Clean)
This commit is contained in:
243
docs/crumbforest/crumb_byte.md
Normal file
243
docs/crumbforest/crumb_byte.md
Normal file
@@ -0,0 +1,243 @@
|
||||
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.
|
||||
Reference in New Issue
Block a user