2026-05-07 09:51:45 +02:00
<!DOCTYPE html>
< html lang = "de" >
< head >
< meta charset = "UTF-8" >
< title > Fensterpiepser ESP32 – Architekturübersicht< / title >
< style >
: root {
--bg : #0f1117 ;
--panel : #1a1d27 ;
--border : #2e3248 ;
--accent : #4f8ef7 ;
--green : #3ecf8e ;
--orange : #f7964f ;
--red : #f75f5f ;
--purple : #b57bee ;
--yellow : #f7d44f ;
--text : #d4d8f0 ;
--muted : #6b7194 ;
--mono : 'Consolas' , 'Courier New' , monospace ;
}
* { box-sizing : border-box ; margin : 0 ; padding : 0 ; }
body { background : var ( - - bg ) ; color : var ( - - text ) ; font-family : var ( - - mono ) ; font-size : 13 px ; padding : 24 px ; }
h1 { font-size : 20 px ; color : var ( - - accent ) ; margin-bottom : 4 px ; }
h2 { font-size : 14 px ; color : var ( - - orange ) ; margin : 28 px 0 10 px ; text-transform : uppercase ; letter-spacing : 1 px ; }
. subtitle { color : var ( - - muted ) ; font-size : 11 px ; margin-bottom : 24 px ; }
svg text { font-family : var ( - - mono ) ; }
table { border-collapse : collapse ; width : 100 % ; margin-bottom : 8 px ; }
th { background : #22263a ; color : var ( - - orange ) ; font-weight : normal ; text-align : left ; padding : 5 px 10 px ; border : 1 px solid var ( - - border ) ; font-size : 11 px ; text-transform : uppercase ; letter-spacing : .5 px ; }
td { padding : 5 px 10 px ; border : 1 px solid var ( - - border ) ; vertical-align : top ; }
tr : nth-child ( even ) td { background : #161924 ; }
. tag { display : inline-block ; padding : 1 px 6 px ; border-radius : 3 px ; font-size : 10 px ; margin : 1 px ; }
. t-isr { background : #3a1a1a ; color : var ( - - red ) ; border : 1 px solid var ( - - red ) ; }
. t-task { background : #1a2a3a ; color : var ( - - accent ) ; border : 1 px solid var ( - - accent ) ; }
. t-loop { background : #1a3a2a ; color : var ( - - green ) ; border : 1 px solid var ( - - green ) ; }
. t-hw { background : #2a2a1a ; color : var ( - - yellow ) ; border : 1 px solid var ( - - yellow ) ; }
. t-net { background : #2a1a3a ; color : var ( - - purple ) ; border : 1 px solid var ( - - purple ) ; }
. col-core0 { color : var ( - - accent ) ; }
. col-core1 { color : var ( - - green ) ; }
. col-isr { color : var ( - - red ) ; }
. col-hw { color : var ( - - yellow ) ; }
. col-net { color : var ( - - purple ) ; }
. grid2 { display : grid ; grid-template-columns : 1 fr 1 fr ; gap : 20 px ; }
. grid3 { display : grid ; grid-template-columns : 1 fr 1 fr 1 fr ; gap : 20 px ; }
section { margin-bottom : 32 px ; }
< / style >
< / head >
< body >
< h1 > Fensterpiepser ESP32 — Architekturübersicht< / h1 >
< div class = "subtitle" > NodeMCU-32S · Arduino/ESP-IDF · v2.0.8 · Varianten: EG / DG< / div >
<!-- ═══════════════════════════════════════════════════════════
1. DUAL - CORE / TASK ÜBERSICHT
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 1 · Tasks & Interrupts – Gesamtübersicht< / h2 >
< svg viewBox = "0 0 960 460" width = "100%" style = "max-width:960px;display:block;" >
< defs >
< marker id = "arr" markerWidth = "8" markerHeight = "6" refX = "8" refY = "3" orient = "auto" >
< polygon points = "0 0,8 3,0 6" fill = "#4f8ef7" / >
< / marker >
< marker id = "arr-g" markerWidth = "8" markerHeight = "6" refX = "8" refY = "3" orient = "auto" >
< polygon points = "0 0,8 3,0 6" fill = "#3ecf8e" / >
< / marker >
< marker id = "arr-o" markerWidth = "8" markerHeight = "6" refX = "8" refY = "3" orient = "auto" >
< polygon points = "0 0,8 3,0 6" fill = "#f7964f" / >
< / marker >
< marker id = "arr-r" markerWidth = "8" markerHeight = "6" refX = "8" refY = "3" orient = "auto" >
< polygon points = "0 0,8 3,0 6" fill = "#f75f5f" / >
< / marker >
< marker id = "arr-p" markerWidth = "8" markerHeight = "6" refX = "8" refY = "3" orient = "auto" >
< polygon points = "0 0,8 3,0 6" fill = "#b57bee" / >
< / marker >
< / defs >
<!-- ESP32 chip outline -->
< rect x = "10" y = "10" width = "940" height = "440" rx = "10" fill = "#12151f" stroke = "#2e3248" stroke-width = "1.5" / >
< text x = "480" y = "32" text-anchor = "middle" fill = "#2e3248" font-size = "13" font-weight = "bold" > ESP32 NodeMCU-32S · 240 MHz · Xtensa LX6 Dual-Core< / text >
<!-- ── Core 0 ── -->
< rect x = "30" y = "44" width = "420" height = "390" rx = "8" fill = "#14192a" stroke = "#4f8ef7" stroke-width = "1.2" / >
< text x = "240" y = "64" text-anchor = "middle" fill = "#4f8ef7" font-size = "12" font-weight = "bold" > Core 0< / text >
<!-- beepTask -->
< rect x = "50" y = "76" width = "380" height = "90" rx = "6" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "70" y = "96" fill = "#4f8ef7" font-size = "12" font-weight = "bold" > beepTask< / text >
< text x = "380" y = "96" text-anchor = "end" fill = "#6b7194" font-size = "10" > Prio 5 · Core 0 · Stack 2 KB< / text >
2026-05-07 12:20:12 +02:00
< text x = "70" y = "114" fill = "#d4d8f0" font-size = "10" > · Wartet auf Task Notification (gegeben alle 10 ms von timerIsr)< / text >
2026-05-07 09:51:45 +02:00
< text x = "70" y = "129" fill = "#d4d8f0" font-size = "10" > · Short-Beep-Timing (30 ms Puls)< / text >
< text x = "70" y = "144" fill = "#d4d8f0" font-size = "10" > · PiepPattern::TaskCyclic() → PiepMode → toneRequest → GPIO18< / text >
<!-- rfDecodeTask -->
< rect x = "50" y = "178" width = "380" height = "80" rx = "6" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "70" y = "198" fill = "#4f8ef7" font-size = "12" font-weight = "bold" > rfDecodeTask< / text >
< text x = "380" y = "198" text-anchor = "end" fill = "#6b7194" font-size = "10" > Prio 4 · Core 0 · Stack 3 KB< / text >
< text x = "70" y = "216" fill = "#d4d8f0" font-size = "10" > · Wartet auf rfEventQueue (RfEvent_t: dtMicros, pinValue)< / text >
< text x = "70" y = "231" fill = "#d4d8f0" font-size = "10" > · receiverSw / Aiggend / Oval / Kerui / Thermo .Isr()< / text >
< text x = "70" y = "246" fill = "#d4d8f0" font-size = "10" > · Setzt lastReceivedKeyNum / bNewData für loop< / text >
<!-- WiFi stack -->
< rect x = "50" y = "270" width = "380" height = "60" rx = "6" fill = "#1a1a30" stroke = "#2e3248" stroke-width = "1" stroke-dasharray = "4,3" / >
< text x = "70" y = "292" fill = "#6b7194" font-size = "12" font-weight = "bold" > WiFi / TCP-IP Stack< / text >
< text x = "380" y = "292" text-anchor = "end" fill = "#6b7194" font-size = "10" > ESP-IDF intern< / text >
< text x = "70" y = "310" fill = "#6b7194" font-size = "10" > · Autonome Tasks (pro_cpu_0) · PubSubClient nutzt WiFiClient< / text >
< text x = "70" y = "323" fill = "#6b7194" font-size = "10" > · SSID: Elektrosmog · Broker: flokke.de:1883< / text >
<!-- NTP / Hostname -->
< rect x = "50" y = "342" width = "380" height = "74" rx = "6" fill = "#1a1a2a" stroke = "#2e3248" stroke-width = "1" stroke-dasharray = "4,3" / >
< text x = "70" y = "362" fill = "#6b7194" font-size = "11" > NTP: ptbtime3.ptb.de (GMT+1 / DST+1)< / text >
< text x = "70" y = "378" fill = "#6b7194" font-size = "11" > Hostname: FENSTER_PIEPSER_NODEMCU_32_S_EG< / text >
< text x = "70" y = "394" fill = "#6b7194" font-size = "11" > OTA: WebServer Port 80 (/update)< / text >
< text x = "70" y = "409" fill = "#6b7194" font-size = "11" > Reset nach 24 h (wenn alle Fenster zu)< / text >
<!-- ── Core 1 ── -->
< rect x = "510" y = "44" width = "420" height = "390" rx = "8" fill = "#14211a" stroke = "#3ecf8e" stroke-width = "1.2" / >
< text x = "720" y = "64" text-anchor = "middle" fill = "#3ecf8e" font-size = "12" font-weight = "bold" > Core 1< / text >
<!-- timerIsr -->
< rect x = "530" y = "76" width = "380" height = "75" rx = "6" fill = "#2a1a1a" stroke = "#f75f5f" stroke-width = "1" / >
< text x = "550" y = "96" fill = "#f75f5f" font-size = "12" font-weight = "bold" > timerIsr< / text >
< text x = "900" y = "96" text-anchor = "end" fill = "#6b7194" font-size = "10" > hw_timer 0 · 1 MHz · Alarm 10 000 µs< / text >
< text x = "550" y = "114" fill = "#d4d8f0" font-size = "10" > · ++count; if count==10 → tDecis++ (volatile)< / text >
2026-05-07 12:20:22 +02:00
< text x = "550" y = "129" fill = "#d4d8f0" font-size = "10" > · vTaskNotifyGiveFromISR(beepTaskHandle) + portYIELD_FROM_ISR< / text >
2026-05-07 09:51:45 +02:00
< text x = "550" y = "144" fill = "#6b7194" font-size = "10" > ← gesamte Beep-Logik jetzt in beepTask< / text >
<!-- pinLevelChangeIsr -->
< rect x = "530" y = "163" width = "380" height = "75" rx = "6" fill = "#2a1a1a" stroke = "#f75f5f" stroke-width = "1" / >
< text x = "550" y = "183" fill = "#f75f5f" font-size = "12" font-weight = "bold" > pinLevelChangeIsr< / text >
< text x = "900" y = "183" text-anchor = "end" fill = "#6b7194" font-size = "10" > GPIO 26 · CHANGE · INPUT_PULLUP< / text >
< text x = "550" y = "201" fill = "#d4d8f0" font-size = "10" > · dtMicros = micros() − lastMics · pinVal = digitalRead(26)< / text >
< text x = "550" y = "216" fill = "#d4d8f0" font-size = "10" > · Filter: loopCountSincePause < 2 || dtMicros < 100 µs → skip< / text >
< text x = "550" y = "231" fill = "#d4d8f0" font-size = "10" > · xQueueSendFromISR(rfEventQueue, & ev) + portYIELD_FROM_ISR< / text >
<!-- loopTask -->
< rect x = "530" y = "250" width = "380" height = "174" rx = "6" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "550" y = "270" fill = "#3ecf8e" font-size = "12" font-weight = "bold" > loopTask (Arduino loop)< / text >
< text x = "900" y = "270" text-anchor = "end" fill = "#6b7194" font-size = "10" > Prio 1 · Core 1 · vTaskDelay 5 ms< / text >
< text x = "550" y = "289" fill = "#d4d8f0" font-size = "10" > 1 handleKeyReceived() – Empfangene Keys → Fenster-State → MQTT< / text >
< text x = "550" y = "304" fill = "#d4d8f0" font-size = "10" > 2 handleDispUpdate() – SPI-Display (1 s / 10 s Interval)< / text >
< text x = "550" y = "319" fill = "#d4d8f0" font-size = "10" > 3 handleWifiConnection() – Reconnect nach 30 s, Restart on Timeout< / text >
< text x = "550" y = "334" fill = "#d4d8f0" font-size = "10" > 4 myMqttClient.onLoop() – MQTT keepalive, RSSI alle 600 s< / text >
< text x = "550" y = "349" fill = "#d4d8f0" font-size = "10" > 5 UpdateHandler() – HTTP OTA (WebServer Port 80)< / text >
< text x = "550" y = "364" fill = "#d4d8f0" font-size = "10" > 6 updatePiepPattern() – Pause-Modus nach 6 min Dauerbeeep< / text >
< text x = "550" y = "379" fill = "#d4d8f0" font-size = "10" > 7 xcpLoop() – XCP-Events 10 / 100 / 500 / 1000 ms< / text >
< text x = "550" y = "394" fill = "#d4d8f0" font-size = "10" > 8 loopCountSincePause++ – RF-Debounce Zähler< / text >
< text x = "550" y = "409" fill = "#d4d8f0" font-size = "10" > 9 vTaskDelay(5 ms) – gibt Core 1 für ISRs frei< / text >
<!-- ── FreeRTOS Primitives ── -->
2026-05-07 12:20:33 +02:00
<!-- Task Notification arrow: timerIsr → beepTask -->
2026-05-07 09:51:45 +02:00
< line x1 = "530" y1 = "108" x2 = "432" y2 = "108" stroke = "#4f8ef7" stroke-width = "1.5" marker-end = "url(#arr)" stroke-dasharray = "5,3" / >
2026-05-07 12:20:33 +02:00
< rect x = "436" y = "98" width = "90" height = "18" rx = "3" fill = "#0f1117" / >
< text x = "481" y = "111" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > Task Notification< / text >
2026-05-07 09:51:45 +02:00
<!-- rfEventQueue arrow: pinLevelChangeIsr → rfDecodeTask -->
< line x1 = "530" y1 = "210" x2 = "432" y2 = "218" stroke = "#4f8ef7" stroke-width = "1.5" marker-end = "url(#arr)" stroke-dasharray = "5,3" / >
< rect x = "438" y = "207" width = "82" height = "18" rx = "3" fill = "#0f1117" / >
< text x = "479" y = "220" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > rfEventQueue[32]< / text >
<!-- receiver state arrow: rfDecodeTask → loop (dashed green) -->
< line x1 = "430" y1 = "242" x2 = "530" y2 = "310" stroke = "#3ecf8e" stroke-width = "1" marker-end = "url(#arr-g)" stroke-dasharray = "4,3" / >
< text x = "471" y = "284" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" transform = "rotate(-27,471,284)" > keyNum / dir< / text >
< / svg >
< / section >
<!-- ═══════════════════════════════════════════════════════════
2. FreeRTOS PRIMITIVE
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 2 · FreeRTOS Synchronisationsobjekte< / h2 >
< table >
< tr > < th > Objekt< / th > < th > Typ< / th > < th > Größe< / th > < th > Produzent (ISR)< / th > < th > Konsument (Task)< / th > < th > Zweck< / th > < / tr >
< tr >
< td > < span class = "tag t-task" > beepSemaphore< / span > < / td >
< td > Binary Semaphore< / td > < td > – < / td >
< td > < span class = "tag t-isr" > timerIsr< / span > alle 10 ms< / td >
< td > < span class = "tag t-task" > beepTask< / span > Core 0, Prio 5< / td >
< td > Takt für Beep-State-Machine; ersetzt direkten ISR-Aufruf von PiepPattern::TaskCyclic()< / td >
< / tr >
< tr >
< td > < span class = "tag t-task" > rfEventQueue< / span > < / td >
< td > Queue – RfEvent_t< / td > < td > 32 Einträge × 5 B< / td >
< td > < span class = "tag t-isr" > pinLevelChangeIsr< / span > bei jeder Flanke< / td >
< td > < span class = "tag t-task" > rfDecodeTask< / span > Core 0, Prio 4< / td >
< td > Überträgt {dtMicros, pinValue}; alle 5 Receiver-State-Machines laufen jetzt im Task-Kontext< / td >
< / tr >
< / table >
< / section >
<!-- ═══════════════════════════════════════════════════════════
3. SIGNAL - FLÜSSE
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 3 · Signalflüsse< / h2 >
< svg viewBox = "0 0 960 300" width = "100%" style = "max-width:960px;display:block;" >
< defs >
< marker id = "a1" markerWidth = "7" markerHeight = "5" refX = "7" refY = "2.5" orient = "auto" > < polygon points = "0 0,7 2.5,0 5" fill = "#f7964f" / > < / marker >
< marker id = "a2" markerWidth = "7" markerHeight = "5" refX = "7" refY = "2.5" orient = "auto" > < polygon points = "0 0,7 2.5,0 5" fill = "#3ecf8e" / > < / marker >
< marker id = "a3" markerWidth = "7" markerHeight = "5" refX = "7" refY = "2.5" orient = "auto" > < polygon points = "0 0,7 2.5,0 5" fill = "#b57bee" / > < / marker >
< / defs >
<!-- ── RF - Empfangspfad ── -->
< text x = "20" y = "28" fill = "#f7964f" font-size = "11" font-weight = "bold" > RF-Empfangspfad (433 MHz OOK)< / text >
<!-- boxes -->
< rect x = "20" y = "38" width = "100" height = "40" rx = "5" fill = "#2a1a0a" stroke = "#f7964f" stroke-width = "1" / >
< text x = "70" y = "54" text-anchor = "middle" fill = "#f7964f" font-size = "10" > 433 MHz< / text >
< text x = "70" y = "68" text-anchor = "middle" fill = "#f7964f" font-size = "10" > Empfänger< / text >
< rect x = "150" y = "38" width = "110" height = "40" rx = "5" fill = "#2a1a1a" stroke = "#f75f5f" stroke-width = "1" / >
< text x = "205" y = "54" text-anchor = "middle" fill = "#f75f5f" font-size = "10" > pinLevelChange< / text >
< text x = "205" y = "68" text-anchor = "middle" fill = "#f75f5f" font-size = "10" > Isr (Core 1)< / text >
< rect x = "290" y = "38" width = "100" height = "40" rx = "5" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "340" y = "54" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > rfEventQueue< / text >
< text x = "340" y = "68" text-anchor = "middle" fill = "#6b7194" font-size = "10" > 32 × RfEvent_t< / text >
< rect x = "420" y = "38" width = "100" height = "40" rx = "5" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "470" y = "54" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > rfDecodeTask< / text >
< text x = "470" y = "68" text-anchor = "middle" fill = "#6b7194" font-size = "10" > Core 0 Prio 4< / text >
< rect x = "550" y = "20" width = "115" height = "18" rx = "3" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "607" y = "33" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > ReceiverSmartWares< / text >
< rect x = "550" y = "42" width = "115" height = "18" rx = "3" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "607" y = "55" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > ReceiverAiggend< / text >
< rect x = "550" y = "64" width = "115" height = "18" rx = "3" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "607" y = "77" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > ReceiverOval< / text >
< rect x = "550" y = "86" width = "115" height = "18" rx = "3" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "607" y = "99" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > ReceiverKerui< / text >
< rect x = "550" y = "108" width = "115" height = "18" rx = "3" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "607" y = "121" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > ReceiverFunkThermo< / text >
< rect x = "695" y = "38" width = "100" height = "40" rx = "5" fill = "#1a2a1a" stroke = "#3ecf8e" stroke-width = "1" / >
< text x = "745" y = "54" text-anchor = "middle" fill = "#3ecf8e" font-size = "10" > loopTask< / text >
< text x = "745" y = "68" text-anchor = "middle" fill = "#6b7194" font-size = "10" > handleKey< / text >
< rect x = "825" y = "38" width = "110" height = "40" rx = "5" fill = "#2a1a3a" stroke = "#b57bee" stroke-width = "1" / >
< text x = "880" y = "54" text-anchor = "middle" fill = "#b57bee" font-size = "10" > MQTT publish< / text >
< text x = "880" y = "68" text-anchor = "middle" fill = "#6b7194" font-size = "10" > flokke.de:1883< / text >
<!-- arrows RF path -->
< line x1 = "120" y1 = "58" x2 = "148" y2 = "58" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "260" y1 = "58" x2 = "288" y2 = "58" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "390" y1 = "58" x2 = "418" y2 = "58" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "520" y1 = "50" x2 = "548" y2 = "30" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "520" y1 = "55" x2 = "548" y2 = "51" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "520" y1 = "58" x2 = "548" y2 = "73" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "520" y1 = "62" x2 = "548" y2 = "95" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "520" y1 = "66" x2 = "548" y2 = "117" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "665" y1 = "58" x2 = "693" y2 = "58" stroke = "#3ecf8e" stroke-width = "1.5" marker-end = "url(#a2)" / >
< line x1 = "795" y1 = "58" x2 = "823" y2 = "58" stroke = "#b57bee" stroke-width = "1.5" marker-end = "url(#a3)" / >
<!-- ── Beep - Pfad ── -->
< text x = "20" y = "165" fill = "#f7964f" font-size = "11" font-weight = "bold" > Beep-Pfad (Buzzer-Ansteuerung)< / text >
< rect x = "20" y = "175" width = "100" height = "40" rx = "5" fill = "#2a1a1a" stroke = "#f75f5f" stroke-width = "1" / >
< text x = "70" y = "191" text-anchor = "middle" fill = "#f75f5f" font-size = "10" > timerIsr< / text >
< text x = "70" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > 10 ms · Core 1< / text >
< rect x = "150" y = "175" width = "100" height = "40" rx = "5" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "200" y = "191" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > beepSemaphore< / text >
< text x = "200" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > binary< / text >
< rect x = "280" y = "175" width = "100" height = "40" rx = "5" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "330" y = "191" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > beepTask< / text >
< text x = "330" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > Core 0 Prio 5< / text >
< rect x = "410" y = "175" width = "100" height = "40" rx = "5" fill = "#1a2240" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "460" y = "191" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > PiepPattern< / text >
< text x = "460" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > TaskCyclic()< / text >
< rect x = "540" y = "157" width = "90" height = "18" rx = "3" fill = "#1a2040" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "585" y = "170" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > piepFast × 2< / text >
< rect x = "540" y = "179" width = "90" height = "18" rx = "3" fill = "#1a2040" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "585" y = "192" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > piepSlow × 1< / text >
< rect x = "540" y = "201" width = "90" height = "18" rx = "3" fill = "#1a2040" stroke = "#4f8ef7" stroke-width = "1" / >
< text x = "585" y = "214" text-anchor = "middle" fill = "#4f8ef7" font-size = "10" > piepPause × 120< / text >
< rect x = "660" y = "175" width = "100" height = "40" rx = "5" fill = "#2a2a1a" stroke = "#f7d44f" stroke-width = "1" / >
< text x = "710" y = "191" text-anchor = "middle" fill = "#f7d44f" font-size = "10" > toneRequest< / text >
< text x = "710" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > bitmask< / text >
< rect x = "790" y = "175" width = "100" height = "40" rx = "5" fill = "#2a2a1a" stroke = "#f7d44f" stroke-width = "1" / >
< text x = "840" y = "191" text-anchor = "middle" fill = "#f7d44f" font-size = "10" > GPIO 18< / text >
< text x = "840" y = "205" text-anchor = "middle" fill = "#6b7194" font-size = "10" > PIEP_VCC< / text >
< rect x = "910" y = "175" width = "35" height = "40" rx = "5" fill = "#1a1a00" stroke = "#f7d44f" stroke-width = "1" / >
< text x = "928" y = "199" text-anchor = "middle" fill = "#f7d44f" font-size = "20" > 🔔< / text >
<!-- arrows beep path -->
< line x1 = "120" y1 = "195" x2 = "148" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "250" y1 = "195" x2 = "278" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "380" y1 = "195" x2 = "408" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "510" y1 = "188" x2 = "538" y2 = "167" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "510" y1 = "193" x2 = "538" y2 = "188" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "510" y1 = "198" x2 = "538" y2 = "210" stroke = "#f7964f" stroke-width = "1" marker-end = "url(#a1)" / >
< line x1 = "630" y1 = "195" x2 = "658" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "760" y1 = "195" x2 = "788" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
< line x1 = "890" y1 = "195" x2 = "908" y2 = "195" stroke = "#f7964f" stroke-width = "1.5" marker-end = "url(#a1)" / >
<!-- PWM note -->
< text x = "840" y = "240" text-anchor = "middle" fill = "#6b7194" font-size = "9" > GPIO33 PIEP_PWM< / text >
< text x = "840" y = "252" text-anchor = "middle" fill = "#6b7194" font-size = "9" > 2 kHz · LEDC Ch12< / text >
< text x = "840" y = "264" text-anchor = "middle" fill = "#6b7194" font-size = "9" > duty 127/255 (fix)< / text >
<!-- bRequestShortBeep note -->
< text x = "310" y = "240" text-anchor = "middle" fill = "#6b7194" font-size = "9" > bRequestShortBeep (volatile)< / text >
< text x = "310" y = "252" text-anchor = "middle" fill = "#6b7194" font-size = "9" > gesetzt von loopTask< / text >
< text x = "310" y = "264" text-anchor = "middle" fill = "#6b7194" font-size = "9" > → 30 ms Bestätigungs-Piep< / text >
< / svg >
< / section >
<!-- ═══════════════════════════════════════════════════════════
4. RECEIVER - ÜBERSICHT
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 4 · 433-MHz-Receiver< / h2 >
< table >
< tr > < th > Objekt< / th > < th > Protokoll< / th > < th > Key-Bytes< / th > < th > Anzahl Keys< / th > < th > Key-Index-Offset< / th > < th > Basisklasse< / th > < / tr >
< tr > < td > receiverSw< / td > < td > SmartWares Manchester OOK< / td > < td > 8 B< / td > < td > 22< / td > < td > 0< / td > < td > Receiver433< / td > < / tr >
< tr > < td > receiverAiggend< / td > < td > 3-Byte-Key PWM< / td > < td > 3 B + 1 Richtungs-B< / td > < td > konfigurierbar< / td > < td > +30< / td > < td > Receiver3ByteKey< / td > < / tr >
< tr > < td > receiverOval< / td > < td > 3-Byte-Key PWM< / td > < td > 3 B + 1 Richtungs-B< / td > < td > konfigurierbar< / td > < td > +40< / td > < td > Receiver3ByteKey< / td > < / tr >
< tr > < td > receiverKerui< / td > < td > 3-Byte-Key PWM< / td > < td > 3 B + 1 Richtungs-B< / td > < td > konfigurierbar< / td > < td > +50< / td > < td > Receiver3ByteKey< / td > < / tr >
< tr > < td > receiverThermo< / td > < td > Funk-Thermometer PWM< / td > < td > 4 B (36 bit, 4 bit ignoriert)< / td > < td > – < / td > < td > – < / td > < td > Receiver433< / td > < / tr >
< / table >
< div style = "color:var(--muted);font-size:11px;margin-top:6px;" >
Alle .Isr()-Aufrufe erfolgen in < span class = "tag t-task" > rfDecodeTask< / span > (Core 0) via rfEventQueue.
Debounce: loopCountSincePause < 2 → ISR verwirft Flanke nach Key-Empfang.
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
5. FENSTER - OBJEKTE
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 5 · Fenster-Objekte< / h2 >
< div class = "grid2" >
< div >
< div style = "color:var(--accent);font-size:11px;margin-bottom:6px;" > Variante EG (VERSION_DG_ONLY=0, alleFenster[4])< / div >
< table >
< tr > < th > Name< / th > < th > Sensor-Typ< / th > < th > Key-Nr (mit Offset)< / th > < th > Wait-Zeit-Quelle< / th > < / tr >
< tr > < td > fensterWz< / td > < td > Kerui< / td > < td > 2 + 50 = 52< / td > < td > waitTimeDs (Temperatur)< / td > < / tr >
< tr > < td > fensterBadUnten< / td > < td > Aiggend< / td > < td > 3 + 30 = 33< / td > < td > waitTimeDs< / td > < / tr >
< tr > < td > fensterKueche< / td > < td > Oval< / td > < td > 2 + 40 = 42< / td > < td > waitTimeDs< / td > < / tr >
< tr > < td > fensterToniNeu< / td > < td > Oval< / td > < td > 1 + 40 = 41< / td > < td > waitTimeDs< / td > < / tr >
< / table >
< / div >
< div >
< div style = "color:var(--accent);font-size:11px;margin-bottom:6px;" > Variante DG (VERSION_DG_ONLY=1, alleFenster[4])< / div >
< table >
< tr > < th > Name< / th > < th > Sensor-Typ< / th > < th > Key-Nr (mit Offset)< / th > < th > Wait-Zeit-Quelle< / th > < / tr >
< tr > < td > fensterBadOben< / td > < td > Aiggend< / td > < td > 1 + 30 = 31< / td > < td > waitTimeDs< / td > < / tr >
< tr > < td > fensterFlo< / td > < td > Aiggend< / td > < td > 2 + 30 = 32< / td > < td > waitTimeDs< / td > < / tr >
< tr > < td > fensterHanna< / td > < td > Oval< / td > < td > 5 + 40 = 45< / td > < td > waitTimeDs< / td > < / tr >
< tr > < td > fensterFloBuero< / td > < td > Oval< / td > < td > 4 + 40 = 44< / td > < td > waitTimeDs< / td > < / tr >
< / table >
< / div >
< / div >
< div style = "margin-top:10px;" >
< table >
< tr > < th colspan = "4" > Sonder-Objekte (immer instanziiert)< / th > < / tr >
< tr > < th > Name< / th > < th > Sensor-Typ< / th > < th > Key-Nr< / th > < th > Besonderheit< / th > < / tr >
< tr > < td > fensterGefrier< / td > < td > SmartWares< / td > < td > 10< / td > < td > Feste Wait-Zeit: 30 s (hardcodiert in ShallPiep())< / td > < / tr >
< / table >
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
6. BEEP - MUSTER
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 6 · Beep-Muster (PiepPattern)< / h2 >
< table >
< tr > < th > Modus< / th > < th > onTime< / th > < th > offTime< / th > < th > requestCount< / th > < th > Reihenfolge< / th > < / tr >
< tr > < td > piepFast< / td > < td > 20 ms< / td > < td > 80 ms< / td > < td > 2 Mal< / td > < td > 1.< / td > < / tr >
< tr > < td > piepSlow< / td > < td > 500 ms< / td > < td > 100 ms< / td > < td > 1 Mal< / td > < td > 2.< / td > < / tr >
< tr > < td > piepPauseLong< / td > < td > 0 ms< / td > < td > 1000 ms< / td > < td > 120 × = 60 s< / td > < td > 3. (Standardpause)< / td > < / tr >
< tr > < td > piepPauseShort< / td > < td > 0 ms< / td > < td > 1000 ms< / td > < td > 20 × = 10 s< / td > < td > 3. (nach 6 min Dauerbeeep)< / td > < / tr >
< / table >
< div style = "color:var(--muted);font-size:11px;margin-top:6px;" >
Pattern läuft zyklisch: fast → slow → pause(Long/Short) → fast → …
Start/Stop über piepPattern→startRequest / stopRequest (volatile).
Stopp bei Nacht (bIsNightTime) oder alle Fenster zu.
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
7. WAITTIME (Temperaturabhängige Alarmverzögerung)
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 7 · Wartezeit-Berechnung (waitTimeDs)< / h2 >
< table >
< tr > < th > Außentemperatur< / th > < th > Wartezeit< / th > < th > Erklärung< / th > < / tr >
< tr > < td > < − 5 °C< / td > < td > 5 min< / td > < td > Kurz – kalte Luft, schnelles Lüften< / td > < / tr >
< tr > < td > − 5 … 19,5 °C< / td > < td > 5 … 30 min (linear)< / td > < td > Lineare Interpolation: 5 + (T+5)× 25/24,5< / td > < / tr >
< tr > < td > 19,5 … 25 °C< / td > < td > ~2 Wochen< / td > < td > Kein Alarm – Sommer, Lüften sinnvoll< / td > < / tr >
< tr > < td > > 25 °C< / td > < td > 30 min< / td > < td > Heiß – kurze Alarmverzögerung< / td > < / tr >
< tr > < td > kein Sensor-Wert< / td > < td > 10 min< / td > < td > Default bis erste Thermo-Meldung< / td > < / tr >
< / table >
< div style = "color:var(--muted);font-size:11px;margin-top:6px;" >
Quelle: receiverThermo (Funk-Thermometer, Aussensensor) · Update bei jedem neuen Thermo-Paket (~50 s Intervall).
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
8. GPIO - BELEGUNG
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 8 · GPIO-Belegung< / h2 >
< div class = "grid2" >
< table >
< tr > < th > GPIO< / th > < th > Richtung< / th > < th > Funktion< / th > < / tr >
< tr > < td > 26< / td > < td > INPUT_PULLUP< / td > < td > DIN_FUNK – 433-MHz-Datensignal → pinLevelChangeIsr< / td > < / tr >
< tr > < td > 12< / td > < td > OUTPUT HIGH< / td > < td > DOUT_FUNK_VCC – Versorgung Empfängermodul< / td > < / tr >
< tr > < td > 32< / td > < td > OUTPUT LOW< / td > < td > DOUT_FUNK_GND – GND Empfängermodul< / td > < / tr >
< tr > < td > 18< / td > < td > OUTPUT< / td > < td > DOUT_PIEP_VCC – Buzzer-Versorgung (geschaltet via toneRequest)< / td > < / tr >
< tr > < td > 33< / td > < td > LEDC PWM< / td > < td > DOUT_PIEP_PWM – Buzzer-PWM 2 kHz, Ch 12, duty 127< / td > < / tr >
< tr > < td > 16< / td > < td > OUTPUT HIGH< / td > < td > DOUT_TEST_TIMER – Debug-Pin< / td > < / tr >
< / table >
< table >
< tr > < th > GPIO< / th > < th > Funktion< / th > < th > Interface< / th > < / tr >
< tr > < td > 14< / td > < td > TFT_PIN_CLK (SCK)< / td > < td rowspan = "5" style = "vertical-align:middle" > SPI – Adafruit ST7735< br > (VERSION_BIG_DISPLAY)< / td > < / tr >
< tr > < td > 13< / td > < td > TFT_PIN_MOSI (SDA)< / td > < / tr >
< tr > < td > 22< / td > < td > TFT_PIN_RST (RES)< / td > < / tr >
< tr > < td > 21< / td > < td > TFT_PIN_DC (RS)< / td > < / tr >
< tr > < td > 15< / td > < td > TFT_PIN_CS< / td > < / tr >
< / table >
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
9. MQTT - TOPICS
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 9 · MQTT-Topics (Broker: flokke.de:1883)< / h2 >
< table >
< tr > < th > Topic< / th > < th > Retain< / th > < th > Wann< / th > < th > Payload (Beispiel)< / th > < / tr >
< tr > < td > fenster/system/EG< / td > < td > nein< / td > < td > Connect, Reset< / td > < td > {"e":"hello","client":"FensterpiepserEG_v2.0.8"}< / td > < / tr >
< tr > < td > fenster/status/EG< / td > < td > ja< / td > < td > Fenster bewegt / Beep gestartet< / td > < td > {"t":1700000000,"e":"move","n":4,"f":["Wohnz",…],"o":[0,1,…],"b":[0,0,…]}< / td > < / tr >
< tr > < td > fenster/temprh/EG< / td > < td > ja< / td > < td > Neues Thermo-Paket (~50 s)< / td > < td > {"T":7,"rh":65,"t":1700000000,"raw":3422534}< / td > < / tr >
< tr > < td > fenster/rssi/EG< / td > < td > nein< / td > < td > Alle 600 s< / td > < td > {"rssi":-62}< / td > < / tr >
< / table >
< div style = "color:var(--muted);font-size:11px;margin-top:6px;" >
LWT (Will) auf fenster/temprh/EG: {"T":0,"rh":0,"t":0} · User: lightcontrol · Keepalive: 600 s
< / div >
< / section >
<!-- ═══════════════════════════════════════════════════════════
10. VOLATILE / SHARED STATE
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 10 · Geteilte Variablen & volatile-Annotierungen< / h2 >
< table >
< tr > < th > Variable< / th > < th > Typ< / th > < th > volatile< / th > < th > Schreiber< / th > < th > Leser< / th > < th > Anmerkung< / th > < / tr >
< tr > < td > tDecis< / td > < td > uint32_t< / td > < td > ✓< / td > < td > timerIsr (Core 1)< / td > < td > loopTask, beepTask, Fenster::ShallPiep< / td > < td > Monoton steigend, 32-bit-Reads atomar auf LX6< / td > < / tr >
< tr > < td > bRequestShortBeep< / td > < td > bool< / td > < td > ✓< / td > < td > loopTask (Core 1)< / td > < td > beepTask (Core 0)< / td > < td > Cross-Core; single-byte write atomar< / td > < / tr >
< tr > < td > loopCountSincePause< / td > < td > uint32_t< / td > < td > ✓< / td > < td > loopTask (Core 1)< / td > < td > pinLevelChangeIsr (Core 1)< / td > < td > RF-Debounce nach Key-Empfang< / td > < / tr >
< tr > < td > PiepPattern::startRequest< br > PiepPattern::stopRequest< / td > < td > uint8_t< / td > < td > ✓< / td > < td > loopTask via Start()/Stop()< / td > < td > beepTask via TaskCyclic()< / td > < td > Cross-Core; Flags, nicht Zähler< / td > < / tr >
< tr > < td > toneRequest< / td > < td > uint8_t< / td > < td > – < / td > < td > beepTask (exklusiv)< / td > < td > beepTask< / td > < td > Kein Cross-Core-Zugriff mehr nach Refactoring< / td > < / tr >
< / table >
< / section >
<!-- ═══════════════════════════════════════════════════════════
11. XCP
═══════════════════════════════════════════════════════════ -->
< section >
< h2 > 11 · XCP-Protokoll< / h2 >
< table >
< tr > < th > Event< / th > < th > Intervall< / th > < th > Aufruf< / th > < / tr >
< tr > < td > XcpEvent(0)< / td > < td > 10 ms< / td > < td > xcpLoop() in loopTask (millis-basiert)< / td > < / tr >
< tr > < td > XcpEvent(1)< / td > < td > 100 ms< / td > < td > xcpLoop()< / td > < / tr >
< tr > < td > XcpEvent(2)< / td > < td > 500 ms< / td > < td > xcpLoop()< / td > < / tr >
< tr > < td > XcpEvent(3)< / td > < td > 1000 ms< / td > < td > xcpLoop()< / td > < / tr >
< / table >
< div style = "color:var(--muted);font-size:11px;margin-top:6px;" >
XcpPoll() + XcpSendLoop() werden ebenfalls in xcpLoop() aufgerufen.
Timing ist millis()-basiert → toleriert den 5-ms-vTaskDelay des loopTask.
< / div >
< / section >
< / body >
< / html >