diff --git a/doc/docs/konzept/hardware.md b/doc/docs/konzept/hardware.md index 608f4e1..286601b 100644 --- a/doc/docs/konzept/hardware.md +++ b/doc/docs/konzept/hardware.md @@ -63,27 +63,27 @@ Kompakte passive Power-Up-Geräte mit Druckschalter. ### 2.1 Akkusystem -**Standard:** 2S LiPo (7.4 V nominal, 8.4 V voll geladen, 6.0 V Entladungsschutz). +**Standard:** 2S LiPo (7.4 V nominal, 8.4 V voll geladen, 6.0 V Entladungsschutz). **Alternative:** 1S nur für Tests oder Low-Power-Prototypen – ungeeignet für hohe IR-LED-Ströme (siehe Abschnitt 4.1). **Schutz & Laden:** - **Zellenschutz-IC:** HY2120-CB + FS8205A (Dual-FET) – schützt vor Über-/Unterspannung, Überstrom, Kurzschluss. - **Lade-IC:** IP2326 (2S Balancing, USB-C) – ermöglicht einfaches Laden ohne externe Balancer. -- **Fuel Gauge:** Spannungsteiler-basierte ADC-Messung (R1=100k, R2=47k) → Software-Mapping auf Ladestand (6.0 V = 0 %, 8.4 V = 100 %). +- **Fuel Gauge:** Spannungsteiler-basierte ADC-Messung (R1=100k, R2=47k) → Software-Mapping auf Ladestand (6.0 V = 0 %, 8.4 V = 100 %). !!! info "Warum 2S?" - 2S-Systeme bieten ausreichend Headroom für IR-LED-Konstantstromquellen (>4.8 V nötig bei 3A) und stabile Versorgung auch bei hoher Last. 1S-Zellen brechen unter 1A+ schnell auf 3.4–3.6 V ein. + 2S-Systeme bieten ausreichend Headroom für IR-LED-Konstantstromquellen (>4.8 V nötig bei 3A) und stabile Versorgung auch bei hoher Last. 1S-Zellen brechen unter 1A+ schnell auf 3.4–3.6 V ein. ### 2.2 Spannungsebenen & Wandler -**Primär-Rail (Batterie, 6.0–8.4 V):** +**Primär-Rail (Batterie, 6.0–8.4 V):** Direkt gespeist: IR-LED-Treiber, Muzzle-Flash-LED, Solenoid 6V (Open Frame, taktiles Feedback Rückstoss). -**Sekundär-Rail (5.0 V, ~1.5 A):** +**Sekundär-Rail (5.0 V, ~1.5 A):** Buck-Converter (z.B. MP2315, TPS62130) für Audio-IC (MAX98357A), adressierbare LEDs (WS2812B) und nachgeschalteten LDO. Hohe Schaltfrequenz gewünscht (geringe Induktivität, kompakte Bauform). -**Tertiär-Rail (3.3 V, ~30 mA):** +**Tertiär-Rail (3.3 V, ~30 mA):** LDO (z.B. MCP1826, AMS1117-3.3) aus 5V Buck-Ausgang für nRF52840 und QSPI Flash. Geringer Dropout (5V → 3.3V = 1.7V) reduziert Wärmeentwicklung; Low-Noise-Design minimiert Störungen auf der RF-Schaltung. **IR-Empfänger (5V mit Level-Shift):** @@ -100,23 +100,23 @@ Die Weste hat durch LEDs und Audio den höchsten Verbrauch; hier die Peak-Absch | Komponente | Zustand | Strom | Leistung | | :--- | :--- | ---: | ---: | -| Audio (MAX98357A) | Volllast (3W @ 90% η) | ~670 mA | 3.35 W | -| WS2812B LEDs (5×) | 100 % Weiß | 300 mA | 1.50 W | -| IR-Empfänger (5×) | Dauerbetrieb @ 5V | ~50 mA | 0.25 W | -| LDO 3.3V (Durchleitung) | 30 mA @ 3.3V | ~30 mA | 0.15 W | -| **Gesamt 5V (Peak)** | | **~1.05 A** | **5.25 W** | +| Audio (MAX98357A) | Volllast (3W @ 90% η) | ~670 mA | 3.35 W | +| WS2812B LEDs (5×) | 100 % Weiß | 300 mA | 1.50 W | +| IR-Empfänger (5×) | Dauerbetrieb @ 5V | ~50 mA | 0.25 W | +| LDO 3.3V (Durchleitung) | 30 mA @ 3.3V | ~30 mA | 0.15 W | +| **Gesamt 5V (Peak)** | | **~1.05 A** | **5.25 W** | **3.3V-Rail (LDO aus 5V Buck):** | Komponente | Zustand | Strom | Leistung | | :--- | :--- | ---: | ---: | -| nRF52840 | BLE+Thread aktiv | ~15 mA | 0.05 W | -| QSPI Flash | Read/Write Burst | ~15 mA | 0.05 W | -| **Gesamt 3.3V (Peak)** | | **~30 mA** | **0.10 W** | +| nRF52840 | BLE+Thread aktiv | ~15 mA | 0.05 W | +| QSPI Flash | Read/Write Burst | ~15 mA | 0.05 W | +| **Gesamt 3.3V (Peak)** | | **~30 mA** | **0.10 W** | **Auslegung:** -- **Buck-Regler:** 1.5 A Nennstrom (30 % Reserve), hohe Schaltfrequenz (>1 MHz) für kompakte Drossel/Kondensatoren. -- **LDO:** 100 mA Nennstrom ausreichend; Verlustleistung bei 30 mA: $P_{loss} = (5V - 3.3V) \cdot 30mA = 51 mW$ (unkritisch). Low-Noise-Design (< 50 µVrms) für sauberen RF-Betrieb. +- **Buck-Regler:** 1.5 A Nennstrom (30 % Reserve), hohe Schaltfrequenz (>1 MHz) für kompakte Drossel/Kondensatoren. +- **LDO:** 100 mA Nennstrom ausreichend; Verlustleistung bei 30 mA: $P_{loss} = (5V - 3.3V) \cdot 30mA = 51 mW$ (unkritisch). Low-Noise-Design (< 50 µVrms) für sauberen RF-Betrieb. ### 2.4 Blockschaltbild Energieversorgung @@ -169,7 +169,7 @@ Diese Tabelle gibt einen Überblick über die groben Komponenten pro Einheit. De #### Funktionsprinzip -Hybride PNP/NPN-Topologie für präzisen, modulierten IR-Puls (38 kHz). Die Stromquelle stellt sicher, dass bei wechselnder Batteriespannung der LED-Strom konstant bleibt (→ reproduzierbare Reichweite). +Hybride PNP/NPN-Topologie für präzisen, modulierten IR-Puls (38 kHz). Die Stromquelle stellt sicher, dass bei wechselnder Batteriespannung der LED-Strom konstant bleibt (→ reproduzierbare Reichweite). ![LED DRIVER](../img/concept_hardware_led_driver.svg) @@ -183,10 +183,10 @@ $$R_{set} = \frac{0,65\,\text{V}}{I_{\text{LED}}}$$ | $I_{\text{LED}}$ | $R_{set}$ | Einsatz | | :--- | :--- | :--- | -| 0,5 A | 1,30 Ω | Standard/Nahkampf | -| 1,0 A | 0,65 Ω | Hohe Reichweite (SFH 4550) | -| 2,0 A | 0,33 Ω | Pulsbetrieb (extreme Leistung) | -| 3,0 A | 0,22 Ω | Scharfschütze (Oslon Black) | +| 0,5 A | 1,30 Ω | Standard/Nahkampf | +| 1,0 A | 0,65 Ω | Hohe Reichweite (SFH 4550) | +| 2,0 A | 0,33 Ω | Pulsbetrieb (extreme Leistung) | +| 3,0 A | 0,22 Ω | Scharfschütze (Oslon Black) | **Thermik:** Bei 38-kHz-Modulation (Duty-Cycle ~30 %) ist $P_{\text{avg}} = R_{set} \cdot I^2_{\text{LED}} \cdot DC$ → deutlich unter Peak. $R_{set}$ muss aber Spitzenstrom verkraften → impulsfeste Typen (Metallschicht, Drahtwiderstand). @@ -198,22 +198,22 @@ $$V_{\text{CC,min}} = V_{f(\text{LED})} + 0,65\,\text{V} + 1,0\,\text{V}_{\text{ | $I_{\text{LED}}$ | $V_f$ (typ.) | $V_{\text{CC,min}}$ | Akku | | :--- | :--- | :--- | :--- | -| 0,5 A | 2,0 V | 3,65 V | 1S (nur voll geladen) | -| 1,0 A | 2,4 V | 4,05 V | 2S empfohlen | -| 2,0 A | 2,8 V | 4,45 V | 2S erforderlich | -| 3,0 A | 3,2 V | 4,85 V | 2S erforderlich | +| 0,5 A | 2,0 V | 3,65 V | 1S (nur voll geladen) | +| 1,0 A | 2,4 V | 4,05 V | 2S empfohlen | +| 2,0 A | 2,8 V | 4,45 V | 2S erforderlich | +| 3,0 A | 3,2 V | 4,85 V | 2S erforderlich | !!! warning "1S ungeeignet für >1A" - 1S-Akkus brechen unter Last auf 3,4–3,6 V ein → Regelung versagt, Reichweite bricht ein. 2S liefert auch bei Teilentladung (7,0 V) genug Headroom. + 1S-Akkus brechen unter Last auf 3,4–3,6 V ein → Regelung versagt, Reichweite bricht ein. 2S liefert auch bei Teilentladung (7,0 V) genug Headroom. ### 4.2 Adressierbare LEDs (WS2812B) **Anforderung:** 5V-Versorgung, aber Daten-Pegel kompatibel mit nRF52840 (3.3V Logic). -**Level-Shift:** SN74AHCT1G125 (3.3V → 5V, Single-Gate); schnell genug für WS2812-Timing (800 kHz). -**Serienwiderstand:** ~330 Ω nach dem Shifter → dämpft Reflexionen auf der Data-Leitung, verhindert Überschwinger. +**Level-Shift:** SN74AHCT1G125 (3.3V → 5V, Single-Gate); schnell genug für WS2812-Timing (800 kHz). +**Serienwiderstand:** ~330 Ω nach dem Shifter → dämpft Reflexionen auf der Data-Leitung, verhindert Überschwinger. -**Layout:** Data-Leitung kurz halten; bei mehreren LEDs in Serie: Bypass-Kondensator (100 nF + 10 µF) pro 3–5 LEDs. +**Layout:** Data-Leitung kurz halten; bei mehreren LEDs in Serie: Bypass-Kondensator (100 nF + 10 µF) pro 3–5 LEDs. ### 4.3 Audio-Verstärker (MAX98357A) @@ -222,39 +222,39 @@ $$V_{\text{CC,min}} = V_{f(\text{LED})} + 0,65\,\text{V} + 1,0\,\text{V}_{\text{ **Architektur:** I2S Class-D Verstärker – DAC + Endstufe integriert, filterlose Topologie (wenige Bauteile). * **Schnittstelle:** I2S (digital); Audio-Stream per EasyDMA vom nRF52840 → CPU bleibt frei für Game Logic. -* **Leistung:** ~3.2 W @ 4Ω – laut genug für Outdoor-Einsatz. +* **Leistung:** ~3.2 W @ 4Ω – laut genug für Outdoor-Einsatz. * **Effizienz:** ~90 % → Akku-schonend; geringer Ruhestrom im Idle. -* **Layout:** Kurze, symmetrische Leitungen zu Speaker-Terminals; separate Ground-Plane; Entkopplung (10 µF + 100 nF) nahe VDD-Pin. +* **Layout:** Kurze, symmetrische Leitungen zu Speaker-Terminals; separate Ground-Plane; Entkopplung (10 µF + 100 nF) nahe VDD-Pin. ### 4.4 Flash-Speicher (QSPI) **Aufgabe:** Audio-Files (Schuss-FX, Ansagen) und Spiel-Logs (optional Treffer-Historie). -* **Technik:** QSPI-NOR-Flash (z.B. W25Q128JV, GD25Q16C); 1.8 V oder 3.3 V; XIP-fähig (Execute-in-Place für Code möglich). -* **Kapazität:** 8–16 MB; reicht für ~3 min @ 22 kHz oder ~1.5 min @ 44 kHz (16 bit mono). Empfehlung: 22 kHz – höhere Sample-Rate bringt bei Outdoor-Speaker kaum Mehrwert. +* **Technik:** QSPI-NOR-Flash (z.B. W25Q128JV, GD25Q16C); 1.8 V oder 3.3 V; XIP-fähig (Execute-in-Place für Code möglich). +* **Kapazität:** 8–16 MB; reicht für ~3 min @ 22 kHz oder ~1.5 min @ 44 kHz (16 bit mono). Empfehlung: 22 kHz – höhere Sample-Rate bringt bei Outdoor-Speaker kaum Mehrwert. * **Interface:** QSPI (4-Bit parallel); nRF52840 unterstützt DMA-basierten Zugriff → schnelle Reads ohne CPU-Last. -* **Layout:** Flash nahe am MCU (< 5 cm Leitungslänge); Differenzen in Trace-Längen < 1 mm; saubere Ground-Plane; JEDEC-ID beim Boot prüfen. +* **Layout:** Flash nahe am MCU (< 5 cm Leitungslänge); Differenzen in Trace-Längen < 1 mm; saubere Ground-Plane; JEDEC-ID beim Boot prüfen. ### 4.5 Akku-Überwachung (Fuel Gauge) -**Prinzip:** Spannungsteiler + ADC für 2S-Akkus (0–8.4 V) → Software-basierte Ladezustandsschätzung (kein dediziertes Fuel-Gauge-IC nötig). +**Prinzip:** Spannungsteiler + ADC für 2S-Akkus (0–8.4 V) → Software-basierte Ladezustandsschätzung (kein dediziertes Fuel-Gauge-IC nötig). **Schaltungskomponenten:** | Bauteil | Wert | Funktion | | :--- | :--- | :--- | -| $R_1$ | 100 kΩ | Spannungsteiler – oberer Zweig | -| $R_2$ | 47 kΩ | Spannungsteiler – unterer Zweig (→ ADC) | -| $C_1$ | 100 nF | Tiefpass-Glättung am ADC-Eingang | +| $R_1$ | 100 kΩ | Spannungsteiler – oberer Zweig | +| $R_2$ | 47 kΩ | Spannungsteiler – unterer Zweig (→ ADC) | +| $C_1$ | 100 nF | Tiefpass-Glättung am ADC-Eingang | **Softwarelogik:** -1. **ADC-Konvertierung:** 12-bit ADC liest $V_{\text{div}}$ (max. 3.3 V bei VRef = 3.3 V). +1. **ADC-Konvertierung:** 12-bit ADC liest $V_{\text{div}}$ (max. 3.3 V bei VRef = 3.3 V). 2. **Rückrechnung:** $V_{\text{bat}} = V_{\text{adc}} \cdot \frac{R_1 + R_2}{R_2} = V_{\text{adc}} \cdot 3.13$ 3. **Mapping:** Lookup-Table oder linear interpoliert: - - 8.4 V → 100 % (voll geladen) - - 7.4 V → ~50 % (nominal) - - 6.0 V → 0 % (Schutzschaltung aktiv) + - 8.4 V → 100 % (voll geladen) + - 7.4 V → ~50 % (nominal) + - 6.0 V → 0 % (Schutzschaltung aktiv) **Kalibrierung:** Einmalig bei Produktion: Spannung an bekanntem Referenzpunkt messen, Offset/Gain in NVS speichern. @@ -267,23 +267,23 @@ Konsolidierte Liste der Schlüsselkomponenten mit konkreten Part-Vorschlägen. D | Kategorie | Bauteil/Funktion | Vorschlag | Alternativen | Anmerkung | | :--- | :--- | :--- | :--- | :--- | | **MCU** | Mikrocontroller | nRF52840 | — | Zephyr-Support, BLE+Thread | -| **Energie** | 2S-Akku | Li-Po 7.4V, 1000–2000 mAh | — | Kapazität je nach Gerät | +| **Energie** | 2S-Akku | Li-Po 7.4V, 1000–2000 mAh | — | Kapazität je nach Gerät | | | Zellenschutz | HY2120-CB + FS8205A | DW01A + 8205A | OV/UV/OC-Protection | | | Lade-IC | IP2326 (2S Balancing) | TP4056 (nur 1S) | USB-C, Balancing integriert | -| | Buck 5V | MP2315, TPS62130 | — | 1.5 A, >1 MHz Schaltfrequenz | +| | Buck 5V | MP2315, TPS62130 | — | 1.5 A, >1 MHz Schaltfrequenz | | | LDO 3.3V | MCP1826, AMS1117-3.3 | XC6206P332MR | Low-Noise für RF, < 0.5V Dropout | -| **IR** | IR-LED | SFH 4550, Oslon Black | TSAL6400 | 940 nm, >50 m Reichweite | -| | IR-Empfänger | TSOP4838, TSOP38438 | VS1838B | 38 kHz Demodulator, 5V Supply | +| **IR** | IR-LED | SFH 4550, Oslon Black | TSAL6400 | 940 nm, >50 m Reichweite | +| | IR-Empfänger | TSOP4838, TSOP38438 | VS1838B | 38 kHz Demodulator, 5V Supply | | | LED-Treiber | PNP/NPN diskret | IRL530 (Logic-FET) | Konstantstrom, PWM-fähig | | | Level-Shifter IR | AO4300A (N-Ch MOSFET) | BSS138, 2N7002 | 5V → 3.3V, invertierend | -| **LED** | Adressierbare | WS2812B (5050) | SK6812, APA102 | 5V, ~60 mA/LED @ weiß | +| **LED** | Adressierbare | WS2812B (5050) | SK6812, APA102 | 5V, ~60 mA/LED @ weiß | | | Level-Shift | SN74AHCT1G125 | 74HCT245 (8-Kanal) | 3.3V → 5V, single-gate | | **Audio** | Class-D Amp | MAX98357A | PAM8302, TPA2005D1 | I2S, 3.2W @ 4Ω | | | Speaker | 4Ω, 3–5W | 8Ω (lower SPL) | Outdoor-tauglich | -| **Speicher** | QSPI Flash | W25Q128JV (16 MB) | GD25Q16C (2 MB) | NOR-Flash, 3.3V | +| **Speicher** | QSPI Flash | W25Q128JV (16 MB) | GD25Q16C (2 MB) | NOR-Flash, 3.3V | | **Feedback** | Solenoid | 6V Open Frame | — | Rückstoss direkt ab Batterie | | | Muzzle LED | Weiß/Gelb, 1W+ | Cree XP-E2 | Sichtbar bei Tag | -| **Passiv** | $R_{\text{set}}$ (IR) | 0.22–1.3 Ω, 3W | Metallschicht, Draht | Impulsfest | +| **Passiv** | $R_{\text{set}}$ (IR) | 0.22–1.3 Ω, 3W | Metallschicht, Draht | Impulsfest | | | Spannungsteiler | 100k + 47k, 1% | 0.1% für Präzision | Fuel Gauge | | **Mechanik** | Stecker | JST-XH (2.54mm) | Molex PicoBlade | Verriegelnd, 3–5 Pole | | | Taster | Omron B3F, Alps SKQG | Cherry MX (größer) | Trigger, Reload | @@ -292,7 +292,26 @@ Konsolidierte Liste der Schlüsselkomponenten mit konkreten Part-Vorschlägen. D - **IR-LED:** Oslon Black für extreme Reichweite (3A-Betrieb), SFH 4550 für Standard (1–2A). - **Audio:** MAX98357A ist quasi-Standard; Alternativen (PAM8302) haben höheren THD, aber OK für SFX. -- **Flash:** 16 MB erlauben ~6 min Audio @ 22 kHz – gut für zukünftige Erweiterungen (z.B. mehrsprachige Ansagen). +- **Flash:** 16 MB erlauben ~6 min Audio @ 22 kHz – gut für zukünftige Erweiterungen (z.B. mehrsprachige Ansagen). - **Stecker:** JST-XH ist weit verbreitet und günstig; Molex PicoBlade kompakter, aber teurer. +### 5.1 IR-LEDs + +| Typ | Leistung |Bemerkungen | +|-----|----------|------------| +| **SFH 4725S** | 3W | Standardmodell für 940nm
**Vorteile:** Sehr bewährt, gute Effizienz | +| **SFH 4726S** | 3W | Ähnlich wie die 4725S, aber oft mit einer leicht anderen internen Linsencharakteristik (breiterer Abstrahlwinkel ohne externe Optik). | +| **SFH 4727AS** | 5W | Das 940-nm-Gegenstück zu deiner 4715AS.
**Vorteil:** Für deine 3-A-Pulse im Outdoor-Modus die stabilste Wahl. Sie verträgt die hohen Pulsströme thermisch am besten.| +|**SFH 4725AS**| 3W | Eine neuere "A"-Revision mit verbesserter Wärmeableitung.| + +Es wird empfohlen, entsprechende "STAR"-Aluplatinen zu verwenden, um die Wärmeableitung zu garantieren. + +### IR-Empfänger + +| Typ | Bemerkungen | +|-----|------------| +| **TSOP34456 / TSOP38456** | Der Standard für 56 kHz.
**Charakteristik**: Besitzt eine sehr agressive **AGC (Automatic Gain Controll)**
**Problem:** Bei extrem starken Signalen im Nahbereich kann die AGC "zumachen" und die Hüllkurve verzerren. | +| **TSSP4056 / TSSP77056** | **Vorteil:** Er hat eine **feste Verstärkung (Fixed Gain)**. Er regelt also nicht ab, wenn das Signal stark wird.
**Nutzen:** Das Signal bleibt viel konstanter als bei bei einem TSOP.| + + *Stand: 04.01.2026* \ No newline at end of file diff --git a/doc/docs/konzept/software.md b/doc/docs/konzept/software.md index 95ae0fd..d23a30d 100644 --- a/doc/docs/konzept/software.md +++ b/doc/docs/konzept/software.md @@ -116,9 +116,9 @@ sequenceDiagram 7. *(Optional)* Weste B sendet UDP-Paket an Leader für Live-Scoreboard (Best Effort). !!! info "Warum kein MilesTag2?" - MilesTag2 wurde als Basis erwogen, ist aber mit ~40 ms Frame-Zeit und starren 8-Bit-IDs zu langsam und unflexibel. Unser Custom-Protokoll bietet: + MilesTag2 wurde als Basis erwogen, ist aber mit ~40 ms Frame-Zeit und starren 8-Bit-IDs zu langsam und unflexibel. Unser Custom-Protokoll bietet: - - **Kürzere Frames:** ~36 ms vs. ~40 ms (weniger anfällig für Zittern/Bewegung) + - **Kürzere Frames:** ~36 ms vs. ~40 ms (weniger anfällig für Zittern/Bewegung) - **Flexible Type-Codes:** Hit/Heal/PowerUp/Admin in einem Format - **CRC8-Prüfung:** >99.5% Fehlerrate-Erkennung bei Sonnenlicht - **Variable Daten:** 13-Bit-Payload anpassbar pro Type @@ -219,7 +219,7 @@ Die Logik für die Modi wird primär auf den Westen implementiert (Regelwerk), g * **Medipacks (Objekte):** Aktiv durch Tastendruck; senden erst nach Button-Press ein Heal-IR mit breiter Streuung. * **Wirkung:** * Heal-IR ist als eigene Damage-Class kodiert (negativer Schaden → Heilung). - * Reichweite absichtlich klein (< 2–3 m) und stark gestreut, damit Heilen ein Positionierungs-Feature bleibt. + * Reichweite absichtlich klein (< 2–3 m) und stark gestreut, damit Heilen ein Positionierungs-Feature bleibt. * Treffer-Logik auf der Weste interpretiert diese Pakete als Heilung (+HP, begrenzt durch `health_max`). * **Balancing:** * Heal pro Tick konfigurierbar; zusätzlich wählbar: max. Anzahl Heilungen, Mindest-Pause zwischen Heilungen oder beides. @@ -238,7 +238,7 @@ Die Logik für die Modi wird primär auf den Westen implementiert (Regelwerk), g * `foe`: HP-Delta für andere Teams (-10 Schaden) * `rssi`: Mindest-RSSI (dBm) für Wirksamkeit (z.B. -70 → nur nahe dran) * `warn`: Anzahl Aussendungen als Warnung, bevor der Effekt scharf wird -* **Instant-Death Beispiel:** `team: 0, friend: -128, foe: -128, warn: 0, rssi: -60` → Jeder Empfänger mit RSSI besser als -60 dBm fällt sofort auf 0 HP. +* **Instant-Death Beispiel:** `team: 0, friend: -128, foe: -128, warn: 0, rssi: -60` → Jeder Empfänger mit RSSI besser als -60 dBm fällt sofort auf 0 HP. --- @@ -309,7 +309,7 @@ struct zone_effect_packet { uint8_t team_id; // Besitzer der Zone (0=neutral) int8_t friend_delta; // HP-Delta für eigenes Team (z.B. +20 Heal) int8_t foe_delta; // HP-Delta für andere Teams (z.B. -10 Schaden) - int8_t rssi_thresh_dbm; // Mindest-RSSI für Wirksamkeit (z.B. -70 dBm) + int8_t rssi_thresh_dbm; // Mindest-RSSI für Wirksamkeit (z.B. -70 dBm) uint8_t warn_count; // Anzahl Warn-Pakete vor scharfem Effekt } __packed; @@ -389,7 +389,7 @@ Payloads von Power-Up-Stationen und Buzzer-Boxen (Protokoll-ID `0xBB`): | Von | Nach | Auslöser | Aktion | Bedingung | | :--- | :--- | :--- | :--- | :--- | | Idle | Lobby | CoAP `GAME_STATE_LOBBY` | LED idle-Animation, warten auf Start | Multicast vom Leader | -| Lobby | Countdown | CoAP `GAME_START_COUNTDOWN` | Audio-Countdown 10→1 sec, Countdown-Timer init | Payload: 10 sek | +| Lobby | Countdown | CoAP `GAME_START_COUNTDOWN` | Audio-Countdown 10→1 sec, Countdown-Timer init | Payload: 10 sek | | Countdown | Running | Countdown = 0 | Health reset, Treffer-Sensor aktivieren, Waffe unlock | Timer lokal abgelaufen | | Running | Dead | Health <= 0 | LED rot/aus, Dead-Sound, CoAP CMD_DISABLE an Waffe | Nach IR-Hit-Verarbeitung | | Dead | Running | Respawn-Timer = 0 | Health reset, CoAP CMD_ENABLE an Waffe, Sensor on | Optional; Config-abhängig | diff --git a/doc/docs/planung.md b/doc/docs/planung.md index 674544f..0f1d8e2 100644 --- a/doc/docs/planung.md +++ b/doc/docs/planung.md @@ -39,7 +39,7 @@ Diese Roadmap führt vom nRF52840DK bis zum fertigen Produkt. - [ ] Zephyr Setup: Installation des nRF Connect SDK (NCS) und VS Code. - [ ] Custom Board Definition: Board-File anlegen, das die Pins des nRF52840DK auf die geplanten Funktionen mappt (PWM für IR, GPIO für Buttons). - [ ] Thread Mesh: Minimalen OpenThread-Stack aufsetzen. Ein DK als Leader (FTD), einer als Child. UDP/CoAP-Ping bei Knopfdruck. -- [ ] IR-Engine: Custom IR-Protokoll (pulse-distance, 38 kHz) mit nrfx_pwm + PPI implementieren. Signal mit Oszilloskop/Logic Analyzer verifizieren. Sicherstellen, dass Funk die IR-Engine nicht stört. **Hinweis:** MilesTag2 wurde verworfen zugunsten eines eigenen, kürzeren Protokolls mit CRC8 (siehe [Spezifikationen](specifications/ir_protocol.md)). +- [ ] IR-Engine: Custom IR-Protokoll (pulse-distance, 38 kHz) mit nrfx_pwm + PPI implementieren. Signal mit Oszilloskop/Logic Analyzer verifizieren. Sicherstellen, dass Funk die IR-Engine nicht stört. **Hinweis:** MilesTag2 wurde verworfen zugunsten eines eigenen, kürzeren Protokolls mit CRC8 (siehe [Spezifikationen](specifications/ir_protocol.md)). #### Phase 2: Der "Prototyp" (Integration) **Ziel:** Einbindung von Audio und Solenoid. diff --git a/doc/docs/specifications/ir_protocol.md b/doc/docs/specifications/ir_protocol.md index 0377956..a0db7b7 100644 --- a/doc/docs/specifications/ir_protocol.md +++ b/doc/docs/specifications/ir_protocol.md @@ -2,78 +2,63 @@ ## Übersicht -Das Infrarot-Kommunikationsprotokoll basiert auf Pulse-Distance-Codierung mit 38 kHz Träger und ist ähnlich Sony SIRC, aber optimiert für die Anforderungen des Lasertag-Systems. Das Protokoll bietet robuste Übertragung mit CRC-Fehlerprüfung und kurzen Frame-Zeiten (~36 ms). +Das Infrarot-Kommunikationsprotokoll basiert auf Pulse-Distance-Codierung mit 38 kHz Träger und ist ähnlich Sony SIRC, aber optimiert für die Anforderungen des Lasertag-Systems. Das Protokoll bietet robuste Übertragung mit CRC-Fehlerprüfung und kurzen Frame-Zeiten (~36 ms). ## Physikalische Schicht | Parameter | Wert | Anmerkung | |-----------|------|----------| -| Trägerfrequenz | 38 kHz | Standard für TSOP48xx Empfänger | +| Trägerfrequenz | 38 kHz | Standard für TSOP48xx Empfänger | | Tastgrad (Duty Cycle) | 50 % | Konfigurierbar (25–75 %) | | Modulation | PWM mit Pulse-Distance-Codierung | Hardware-basiert via nRF52 PWM-Peripheral | -| Empfänger | TSOP4838 (kompatibel) | Active-Low Ausgang, 38 kHz Bandpass | +| Empfänger | TSOP4838 (kompatibel) | Active-Low Ausgang, 38 kHz Bandpass | ## Timing-Spezifikation | Symbol | Dauer | Toleranz | Beschreibung | |--------|-------|----------|-------------| -| **Start Burst** | 2400 µs | ±200 µs | Frame-Synchronisations-Impuls | -| **Mark** | 600 µs | ±100 µs | Träger AN (konstant für alle Bits) | -| **Space 0** | 600 µs | ±100 µs | Träger AUS für logisch 0 | -| **Space 1** | 1200 µs | ±150 µs | Träger AUS für logisch 1 | +| **Start Burst** | 4 × Basistakt (Standard 2400 µs) | ±200 µs | Träger AN, Frame-Synchronisations-Impuls | +| **Gap** | 1 × Basistakt (Standard 600 µs) | ±100 µs | Träger AUS nach Start (optional, konfigurierbar) | +| **Mark** | 1 × Basistakt (Standard 600 µs) | ±100 µs | Träger AN (konstant für alle Bits) | +| **Space 0** | 1 × Basistakt (Standard 600 µs) | ±100 µs | Träger AUS für logisch 0 | +| **Space 1** | 2 × Basistakt (Standard 1200 µs) | ±150 µs | Träger AUS für logisch 1 | + +**Basistakt:** `CONFIG_IR_PROTO_BASE_US` (Standard 600 µs). Alle Zeiten ergeben sich daraus per Multiplikatoren (`IR_PROTO_*_MULT`). ### Bit-Codierung ``` -Bit 0: [Mark 600µs] + [Space 600µs] = 1.2 ms -Bit 1: [Mark 600µs] + [Space 1.2ms] = 1.8 ms +Bit 0: [Mark 600µs] + [Space 600µs] = 1.2 ms +Bit 1: [Mark 600µs] + [Space 1.2ms] = 1.8 ms ``` -### Beispiel-Wellenform (3 Bits: `101`) +### Beispiel-Wellenform (3 Bits: `101`) **Träger-Timing für Bits 1-0-1:** | Segment | Dauer | State | Bit-Wert | |---------|-------|-------|---------| -| Mark | 600 µs | AN | | -| Space 1 | 1200 µs | AUS | **1** (1.8 ms total) | -| Mark | 600 µs | AN | | -| Space 0| 600 µs | AUS | **0** (1.2 ms total) | -| Mark | 600 µs | AN | | -| Space 1| 1200 µs | AUS | **1** (1.8 ms total) | - -```mermaid -block-beta - columns 8 - mark1["Mark
600 µs"] space1["Space 1
1200 µs"]:2 mark2["Mark
600 µs"] space2["Space 0
600 µs"] mark3["Mark
600 µs"] space3["Space 1
1200 µs"]:2 - bit1["1"]:3 bit2["0"]:2 bit3["1"]:3 - - style space1 fill:none - style space2 fill:none - style space3 fill:none -``` +| Mark | 600 µs | AN | | +| Space 1 | 1200 µs | AUS | **1** (1.8 ms total) | +| Mark | 600 µs | AN | | +| Space 0 | 600 µs | AUS | **0** (1.2 ms total) | +| Mark | 600 µs | AN | | +| Space 1 | 1200 µs | AUS | **1** (1.8 ms total) | ## Frame-Format -Alle Frames bestehen aus 24 Bits, übertragen MSB-first: +Alle Frames bestehen aus 24 Bits, übertragen MSB-first: -| Feld | Start Burst | Type | Data | CRC8 | -|------|-------------|------|------|------| -| **Dauer** | 2400 µs | 3 Bits | 13 Bits | 8 Bits | -| **Funktion** | Synchronisation | Frame-Typ | Payload | Fehlerprüfung | -| **Summe** | – | – | – | **24 Bits** | - -```mermaid -packet-beta title Frame -+3: "Typ" -+13: "Payload" -+8: "Fehlerprüfung" -``` +| Feld | Start Burst + Gap | Type | Data | CRC8 | +|------|-------------------|------|------|------| +| **Dauer** | (Start: 4× Basis) + (Gap: 1× Basis) | 3 Bits | 13 Bits | 8 Bits | +| **Funktion** | Synchronisation | Frame-Typ | Payload | Fehlerprüfung | +| **Summe** | – | – | – | **24 Bits** | -**Gesamte Frame-Zeit:** ~36 ms (Start + 24 × 1.5 ms durchschnittliche Bit-Zeit) +**Gesamte Frame-Zeit:** ~39 ms bei Standardwerten (Start 2400 µs + Gap 600 µs + 24 × 1.5 ms Bit-Durchschnitt). Mit angepasstem Basistakt/Multi skaliert alles linear. -### Type-Feld (3 Bits) +### Type-Feld (3 Bits) | Wert | Typ | Beschreibung | |------|-----|-------------| @@ -83,7 +68,7 @@ title Frame | `011` | Admin | System-Steuerbefehle | | `100`–`111` | Reserviert | Zukünftige Nutzung | -### Data-Feld (13 Bits) – Type-abhängig +### Data-Feld (13 Bits) – Type-abhängig #### Hit-Frame (`000`) @@ -112,12 +97,12 @@ title Frame |-----------|------|-------------|-------------| | 0–12 | Command Data | 0–8191 | Implementierungsdefinierte Steuerbefehle | -### CRC-Feld (8 Bits) +### CRC-Feld (8 Bits) - **Algorithmus:** CRC-8-CCITT - **Polynom:** 0x07 (x⁸ + x² + x + 1) - **Initialwert:** 0x00 -- **Eingabe:** Type (3 Bits) + Data (13 Bits) = 16 Bits +- **Eingabe:** Type (3 Bits) + Data (13 Bits) = 16 Bits - **Zweck:** Fehlererkennung bei Bitfehlern durch Umgebungslicht oder Interferenzen **Erwartete Fehlererkennungsrate:** >99.5 % für Einfach- oder Doppelbitfehler @@ -131,7 +116,7 @@ Type: 000 (Hit) Data: 00101010 01010 (ShooterID=42, Damage=10) CRC8: [berechnet aus obigen Daten] -Kompletter Frame (24 Bits): +Kompletter Frame (24 Bits): 000 00101010 01010 CCCCCCCC │ │ │ └─ CRC8 │ │ └─ Damage (10) @@ -141,10 +126,10 @@ Kompletter Frame (24 Bits): **Übertragungsabfolge:** -1. Start Burst: 2400 µs Träger AN -2. Bit 0 (Type): 600 µs Mark + 600 µs Space -3. Bit 1 (Type): 600 µs Mark + 600 µs Space -4. Bit 2 (Type): 600 µs Mark + 600 µs Space +1. Start Burst: 2400 µs Träger AN +2. Bit 0 (Type): 600 µs Mark + 600 µs Space +3. Bit 1 (Type): 600 µs Mark + 600 µs Space +4. Bit 2 (Type): 600 µs Mark + 600 µs Space 5. ... (21 weitere Bits) 6. Ende: Träger AUS @@ -158,9 +143,9 @@ Kompletter Frame (24 Bits): ### Software-State-Machine -1. **IDLE:** Auf Start Burst warten (2000–2800 µs) +1. **IDLE:** Auf Start Burst warten (2000–2800 µs) 2. **SYNC:** Start Burst erkannt, Vorbereitung zur Bit-Empfang -3. **DATA:** Space nach jedem Mark messen, 24 Bits dekodieren +3. **DATA:** Space nach jedem Mark messen, 24 Bits dekodieren 4. **VALIDATE:** CRC prüfen, Frame bei Gültigkeit verarbeiten ### Timing-Toleranzen @@ -173,12 +158,14 @@ Kompletter Frame (24 Bits): Die Protokoll-Timing kann via Kconfig für verschiedene Umgebungen angepasst werden: -- `CONFIG_IR_SEND_CARRIER_HZ`: Trägerfrequenz (30–45 kHz) +- `CONFIG_IR_SEND_CARRIER_HZ`: Trägerfrequenz (30–45 kHz) - `CONFIG_IR_SEND_DUTY_CYCLE_PERCENT`: PWM Tastgrad (25–75 %) -- `CONFIG_IR_SEND_MARK_US`: Mark-Dauer (300–1000 µs) -- `CONFIG_IR_SEND_SPACE0_US`: Space für Bit 0 (300–1000 µs) -- `CONFIG_IR_SEND_SPACE1_US`: Space für Bit 1 (800–2000 µs) -- `CONFIG_IR_SEND_START_BURST_US`: Start Burst (1500–4000 µs) +- `CONFIG_IR_PROTO_BASE_US`: Basistakt (300–1000 µs) +- `CONFIG_IR_PROTO_START_MULT`: Startburst-Faktor (2–8) +- `CONFIG_IR_PROTO_GAP_MULT`: Gap-Faktor (0–4; 0 = kein Gap) +- `CONFIG_IR_PROTO_MARK_MULT`: Mark-Faktor (1–2) +- `CONFIG_IR_PROTO_SPACE0_MULT`: Space0-Faktor (1–3) +- `CONFIG_IR_PROTO_SPACE1_MULT`: Space1-Faktor (1–4) Die Standardwerte folgen Sony SIRC Timing-Konventionen für bewährte Zuverlässigkeit. @@ -186,12 +173,12 @@ Die Standardwerte folgen Sony SIRC Timing-Konventionen für bewährte Zuverläss | Metrik | Wert | |--------|------| -| Frame-Zeit | ~36 ms | -| Datenrate | ~670 bit/s | +| Frame-Zeit | ~39 ms | +| Datenrate | ~410 bit/s | | Max. Spieler-IDs | 256 | -| Reichweite (Außen) | ~50–100 m (abhängig von Sender-Leistung und Umgebungslicht) | +| Reichweite (Außen) | ~50–100 m (abhängig von Sender-Leistung und Umgebungslicht) | | Fehler-Erkennung | >99.5 % via CRC-8 | -| Störfestigkeit | Hoch (Hardware-Bandpass 38 kHz) | +| Störfestigkeit | Hoch (Hardware-Bandpass 38 kHz) | --- diff --git a/doc/software.md b/doc/software.md index 39daa19..9f272da 100644 --- a/doc/software.md +++ b/doc/software.md @@ -189,6 +189,6 @@ struct hit_report_packet { Das IR-Signal nutzt eine 38kHz Trägerfrequenz (NEC-ähnlich). Payload (32-bit): -* `8 bit` Protokoll-ID (Magic Byte zur Unterscheidung von Fernbedienungen) -* `16 bit` Shooter ID -* `8 bit` Info (4 bit Team, 4 bit Damage Class) \ No newline at end of file +* `8 bit` Protokoll-ID (Magic Byte zur Unterscheidung von Fernbedienungen) +* `16 bit` Shooter ID +* `8 bit` Info (4 bit Team, 4 bit Damage Class) \ No newline at end of file diff --git a/firmware/apps/leader/src/main.c b/firmware/apps/leader/src/main.c index eb2a851..6ed8136 100644 --- a/firmware/apps/leader/src/main.c +++ b/firmware/apps/leader/src/main.c @@ -12,7 +12,7 @@ int main(void) lasertag_utils_init(); /* Initialize and start BLE management for provisioning */ - int rc = ble_mgmt_init(); + int rc = ble_mgmt_init(LT_TYPE_LEADER); if (rc) { LOG_ERR("BLE initialization failed (err %d)", rc); return rc; @@ -20,6 +20,14 @@ int main(void) LOG_INF("BLE Management initialized successfully."); } + /* Start BLE advertising */ + rc = ble_mgmt_adv_start(); + if (rc) { + LOG_ERR("BLE advertising start failed (err %d)", rc); + } else { + LOG_INF("BLE advertising started."); + } + /* Initialize and start OpenThread stack */ rc = thread_mgmt_init(); if (rc) { diff --git a/firmware/apps/vest/CMakeLists.txt b/firmware/apps/vest/CMakeLists.txt new file mode 100644 index 0000000..f1cd5c8 --- /dev/null +++ b/firmware/apps/vest/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +# Tell Zephyr to look into our libs folder for extra modules +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../../libs) + +# Set board root to find custom board overlays in firmware/boards +set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lasertag_vest) + +# Define application source files +target_sources(app PRIVATE src/main.c) \ No newline at end of file diff --git a/firmware/apps/vest/prj.conf b/firmware/apps/vest/prj.conf new file mode 100644 index 0000000..5803d3c --- /dev/null +++ b/firmware/apps/vest/prj.conf @@ -0,0 +1,41 @@ +# Console and Logging +CONFIG_LOG=y + +# Shell and Built-in Commands +CONFIG_SHELL=y +CONFIG_KERNEL_SHELL=y +CONFIG_DEVICE_SHELL=y +CONFIG_REBOOT=y + +# --- STACK SIZE UPDATES (Fixes the Hard Fault) --- +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_BT_RX_STACK_SIZE=2048 + +# Storage and Settings (NVS) +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_SETTINGS=y + +# Network and OpenThread +CONFIG_NETWORKING=y +CONFIG_NET_L2_OPENTHREAD=y +CONFIG_OPENTHREAD=y +CONFIG_OPENTHREAD_FTD=y +CONFIG_OPENTHREAD_SHELL=y + +# --- CoAP & UDP Features --- +CONFIG_OPENTHREAD_COAP=y +CONFIG_OPENTHREAD_MANUAL_START=y + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="Lasertag-Device" +CONFIG_BT_DEVICE_NAME_DYNAMIC=y + +# Enable Lasertag Shared Modules +CONFIG_LASERTAG_UTILS=y +CONFIG_THREAD_MGMT=y +CONFIG_BLE_MGMT=y \ No newline at end of file diff --git a/firmware/apps/vest/src/main.c b/firmware/apps/vest/src/main.c new file mode 100644 index 0000000..1533ccb --- /dev/null +++ b/firmware/apps/vest/src/main.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(vest_app, CONFIG_LOG_DEFAULT_LEVEL); + +int main(void) +{ + /* Initialize shared project logic and NVS */ + lasertag_utils_init(); + + /* Initialize and start BLE management for provisioning */ + int rc = ble_mgmt_init(LT_TYPE_VEST); + if (rc) { + LOG_ERR("BLE initialization failed (err %d)", rc); + return rc; + } else { + LOG_INF("BLE Management initialized successfully."); + } + + /* Start BLE advertising */ + rc = ble_mgmt_adv_start(); + if (rc) { + LOG_ERR("BLE advertising start failed (err %d)", rc); + } else { + LOG_INF("BLE advertising started."); + } + + /* Initialize and start OpenThread stack */ + rc = thread_mgmt_init(); + if (rc) { + LOG_ERR("Thread initialization failed (err %d)", rc); + } else { + LOG_INF("Leader Application successfully started with Thread Mesh."); + return rc; + } + + while (1) { + /* Main loop - handle high-level game logic here */ + k_sleep(K_MSEC(1000)); + } + + return 0; +} \ No newline at end of file diff --git a/firmware/apps/weapon/CMakeLists.txt b/firmware/apps/weapon/CMakeLists.txt index 9009ce9..be8dfe4 100644 --- a/firmware/apps/weapon/CMakeLists.txt +++ b/firmware/apps/weapon/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -# Zephyr mitteilen, dass unsere Libs Teil des Projekts sind +# Tell Zephyr that our libs are part of the project list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../../libs) # Set board root to find custom board overlays in firmware/boards diff --git a/firmware/apps/weapon/main.c b/firmware/apps/weapon/main.c index bec315c..2f3a4b2 100644 --- a/firmware/apps/weapon/main.c +++ b/firmware/apps/weapon/main.c @@ -4,12 +4,12 @@ #include #include -/* Unsere neue Library */ +/* Our new library */ #include "game_logic.h" LOG_MODULE_REGISTER(weapon_app, LOG_LEVEL_INF); -/* Spiel-Kontext */ +/* Game context */ static struct game_ctx game; /* Forward Declarations */ @@ -19,15 +19,15 @@ static void on_button_changed(uint32_t button_state, uint32_t has_changed); static void on_game_state_change(enum game_state new_state) { - LOG_INF("APP: Spielstatus geändert -> %d", new_state); + LOG_INF("APP: Game state changed -> %d", new_state); switch (new_state) { case GAME_STATE_RUNNING: - dk_set_led_on(DK_LED1); // LED an wenn Spiel läuft + dk_set_led_on(DK_LED1); // LED on when game is running break; case GAME_STATE_FINISHED: dk_set_led_off(DK_LED1); - // Blinken oder ähnliches + // Blink or similar break; default: dk_set_led_off(DK_LED1); @@ -37,46 +37,46 @@ static void on_game_state_change(enum game_state new_state) static void on_hit_received(uint16_t shooter_id) { - LOG_WARN("APP: AUA! Getroffen von Spieler %d. Leben: %d", shooter_id, game.health); + LOG_WARN("APP: OUCH! Hit by player %d. Health: %d", shooter_id, game.health); // Visuelles Feedback: LED 2 blinkt kurz dk_set_led_on(DK_LED2); k_msleep(200); dk_set_led_off(DK_LED2); - // TODO: Hier später CoAP Nachricht an Leader senden! + // TODO: Send CoAP message to leader later! // send_hit_report_to_leader(...); } static void on_shot_fired(void) { - LOG_INF("APP: PENG! Schuss abgefeuert."); - // TODO: Hier IR-Protokoll senden (NEC/RC5) + LOG_INF("APP: BANG! Shot fired."); + // TODO: Send IR protocol here (NEC/RC5) } /* --- Hardware Callbacks --- */ static void on_button_changed(uint32_t button_state, uint32_t has_changed) { - // Button 1: Schießen + // Button 1: Shoot if ((has_changed & DK_BTN1_MSK) && (button_state & DK_BTN1_MSK)) { if (game.current_state == GAME_STATE_RUNNING) { on_shot_fired(); } else { - LOG_INF("Schuss blockiert - Spiel läuft nicht."); + LOG_INF("Shot blocked - game not running."); } } - // Button 2: Treffer simulieren (Self-Hit Test) + // Button 2: Simulate hit (Self-Hit Test) if ((has_changed & DK_BTN2_MSK) && (button_state & DK_BTN2_MSK)) { - LOG_INF("Simuliere Treffer durch Spieler 99..."); + LOG_INF("Simulating hit by player 99..."); struct game_hit_packet hit_packet; - // Wir tun so, als hätte der IR-Sensor Spieler 99 erkannt + // Pretend the IR sensor detected player 99 if (game_logic_register_hit(99, &hit_packet)) { - // Wenn Treffer gültig war (Spiel läuft, wir leben noch), haben wir jetzt ein Paket - // das wir via Thread versenden könnten. - LOG_INF("Treffer registriert! Damage: %d", hit_packet.damage); + // If hit was valid (game running, we're still alive), we now have a packet + // that we could send via Thread. + LOG_INF("Hit registered! Damage: %d", hit_packet.damage); } } } @@ -89,18 +89,18 @@ void main(void) int err = dk_buttons_init(on_button_changed); if (err) { - LOG_ERR("Buttons konnten nicht initialisiert werden (err %d)", err); + LOG_ERR("Buttons could not be initialized (err %d)", err); } // Game Logic Setup game.on_state_change = on_game_state_change; game.on_hit_received = on_hit_received; - // Initialisiere als Spieler mit ID aus Kconfig (oder NVS später) + // Initialize as player with ID from Kconfig (or NVS later) game_logic_init(&game, CONFIG_LASERTAG_PLAYER_ID_DEFAULT); - // Zum Testen setzen wir den Status manuell auf RUNNING, - // bis wir das Start-Signal vom Leader via Thread empfangen. + // For testing, we manually set the status to RUNNING, + // until we receive the start signal from the leader via Thread. struct game_state_packet fake_start = {.state = GAME_STATE_RUNNING}; game_logic_handle_state_update(&fake_start); diff --git a/firmware/apps/weapon/prj.conf b/firmware/apps/weapon/prj.conf index 13d39a9..dbd0372 100644 --- a/firmware/apps/weapon/prj.conf +++ b/firmware/apps/weapon/prj.conf @@ -15,6 +15,6 @@ CONFIG_LASERTAG_GAME_LOGIC=y CONFIG_LASERTAG_ROLE_PLAYER=y CONFIG_LASERTAG_PLAYER_ID_DEFAULT=2 -# Optional: Shell für Debugging +# Optional: Shell for debugging CONFIG_SHELL=y CONFIG_OPENTHREAD_SHELL=y \ No newline at end of file diff --git a/firmware/libs/CMakeLists.txt b/firmware/libs/CMakeLists.txt index 6fe1643..c88206b 100644 --- a/firmware/libs/CMakeLists.txt +++ b/firmware/libs/CMakeLists.txt @@ -3,4 +3,4 @@ add_subdirectory(ble_mgmt) add_subdirectory(thread_mgmt) add_subdirectory(lasertag_utils) -add_subdirectory(ir_send) \ No newline at end of file +add_subdirectory(ir) \ No newline at end of file diff --git a/firmware/libs/Kconfig b/firmware/libs/Kconfig index 8748f87..2051f98 100644 --- a/firmware/libs/Kconfig +++ b/firmware/libs/Kconfig @@ -2,4 +2,4 @@ rsource "lasertag_utils/Kconfig" rsource "thread_mgmt/Kconfig" rsource "ble_mgmt/Kconfig" -rsource "ir_send/Kconfig" \ No newline at end of file +rsource "ir/Kconfig" \ No newline at end of file diff --git a/firmware/libs/ble_mgmt/include/ble_mgmt.h b/firmware/libs/ble_mgmt/include/ble_mgmt.h index 759e6f4..5ec9197 100644 --- a/firmware/libs/ble_mgmt/include/ble_mgmt.h +++ b/firmware/libs/ble_mgmt/include/ble_mgmt.h @@ -3,14 +3,25 @@ /** * @file ble_mgmt.h - * @brief Bluetooth Low Energy management for provisioning. + * @brief Bluetooth Low Energy management for provisioning and game communication. + * This module handles Bluetooth initialization, advertising, and stopping advertising. */ +/** + * @brief Device types for LaserTag devices. + */ +#define LT_TYPE_LEADER 0x01 +#define LT_TYPE_WEAPON 0x02 +#define LT_TYPE_VEST 0x03 +#define LT_TYPE_BEACON 0x04 + /** * @brief Initialize Bluetooth and prepare services. + * + * @param device_type The type of the device (e.g., leader, weapon, vest, beacon). * @return 0 on success. */ -int ble_mgmt_init(void); +int ble_mgmt_init(uint8_t device_type); /** * @brief Start Bluetooth advertising so the web app can find the device. diff --git a/firmware/libs/ble_mgmt/src/ble_mgmt.c b/firmware/libs/ble_mgmt/src/ble_mgmt.c index d2e3388..320b9fb 100644 --- a/firmware/libs/ble_mgmt/src/ble_mgmt.c +++ b/firmware/libs/ble_mgmt/src/ble_mgmt.c @@ -13,62 +13,83 @@ LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL); /** - * Basis UUID: 03afe2cf-6c64-4a22-9289-c3ae820cXXXX - * Alias-Stellen: Byte 12 & 13 + * Base UUID: 03afe2cf-6c64-4a22-9289-c3ae820cXXXX + * Alias positions: Byte 12 & 13 */ #define LT_UUID_BASE_VAL \ - BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c0000) + BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c0000) /* ========================================================================== SERVICE 1: PROVISIONING (0x10XX) ========================================================================== */ -#define BT_UUID_LT_PROV_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000)) +#define BT_UUID_LT_PROV_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000)) -#define BT_UUID_LT_PROV_NAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1001)) -#define BT_UUID_LT_PROV_PANID_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1002)) -#define BT_UUID_LT_PROV_CHAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1003)) -#define BT_UUID_LT_PROV_EXTPAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1004)) -#define BT_UUID_LT_PROV_NETKEY_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1005)) +#define BT_UUID_LT_PROV_NAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1001)) +#define BT_UUID_LT_PROV_PANID_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1002)) +#define BT_UUID_LT_PROV_CHAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1003)) +#define BT_UUID_LT_PROV_EXTPAN_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1004)) +#define BT_UUID_LT_PROV_NETKEY_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1005)) #define BT_UUID_LT_PROV_NETNAME_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1006)) -#define BT_UUID_LT_PROV_NODES_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1007)) +#define BT_UUID_LT_PROV_NODES_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1007)) +#define BT_UUID_LT_PROV_TYPE_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1008)) /* ========================================================================== SERVICE 2: GAME (0x20XX) ========================================================================== */ -#define BT_UUID_LT_GAME_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2000)) +#define BT_UUID_LT_GAME_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2000)) -#define BT_UUID_LT_GAME_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2001)) +#define BT_UUID_LT_GAME_CONFIG_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2001)) #define BT_UUID_LT_GAME_COMMAND_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2002)) #define BT_UUID_LT_GAME_LOG_DATA_CHAR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c2003)) + +/* --- Global Variables --- */ +static uint8_t device_role = 0; // Store device type for provisioning + /* --- GATT Callbacks --- */ static ssize_t read_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) + void *buf, uint16_t len, uint16_t offset) { const char *val_ptr = NULL; size_t val_len = 0; - if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) { + if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_TYPE_CHAR) == 0) + { + val_ptr = (char *)&device_role; + val_len = sizeof(device_role); + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) + { val_ptr = lasertag_get_device_name(); val_len = strlen(val_ptr); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0) + { static uint16_t pan_id; pan_id = lasertag_get_thread_pan_id(); val_ptr = (char *)&pan_id; val_len = sizeof(pan_id); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0) + { static uint8_t chan; chan = lasertag_get_thread_channel(); val_ptr = (char *)&chan; val_len = sizeof(chan); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0) + { val_ptr = (char *)lasertag_get_thread_ext_pan_id(); val_len = 8; - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0) + { val_ptr = (char *)lasertag_get_thread_network_key(); val_len = 16; - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0) + { val_ptr = lasertag_get_thread_network_name(); val_len = strlen(val_ptr); } @@ -77,110 +98,138 @@ static ssize_t read_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr } static ssize_t write_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, uint16_t offset, uint8_t flags) + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { int rc = 0; - if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) { + if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) + { rc = lasertag_set_device_name(buf, len); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0) { - if (len != 2) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); - rc = lasertag_set_thread_pan_id(*(uint16_t*)buf); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0) { - if (len != 1) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); - rc = lasertag_set_thread_channel(*(uint8_t*)buf); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0) { - if (len != 8) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_PANID_CHAR) == 0) + { + if (len != 2) + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + rc = lasertag_set_thread_pan_id(*(uint16_t *)buf); + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_CHAN_CHAR) == 0) + { + if (len != 1) + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + rc = lasertag_set_thread_channel(*(uint8_t *)buf); + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_EXTPAN_CHAR) == 0) + { + if (len != 8) + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); rc = lasertag_set_thread_ext_pan_id(buf); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0) { - if (len != 16) return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETKEY_CHAR) == 0) + { + if (len != 16) + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); rc = lasertag_set_thread_network_key(buf); - } else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0) { + } + else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NETNAME_CHAR) == 0) + { rc = lasertag_set_thread_network_name(buf, len); } - if (rc) return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + if (rc) + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); return len; } static ssize_t read_discovered_nodes(struct bt_conn *conn, const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) + void *buf, uint16_t len, uint16_t offset) { const char *list = thread_mgmt_get_discovered_list(); return bt_gatt_attr_read(conn, attr, buf, len, offset, list, strlen(list)); } static ssize_t write_discover_cmd(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, uint16_t offset, uint8_t flags) + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { - /* Wenn irgendwas geschrieben wird, triggere Discovery im Thread Mesh */ + /* If anything is written, trigger discovery in Thread Mesh */ thread_mgmt_discover_nodes(); return len; } /* Service Definition */ BT_GATT_SERVICE_DEFINE(provisioning_svc, - BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE), - - /* Gerätename */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NAME_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), - - /* Thread PAN ID */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_PANID_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), + BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE), - /* Thread Kanal */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CHAN_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), + /* Device name */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NAME_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), - /* Extended PAN ID */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_EXTPAN_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), + /* Device Type (Read-only) */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_TYPE_CHAR, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + read_lasertag_val, NULL, NULL), + /* Thread PAN ID */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_PANID_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), - /* Netzwerk Key */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETKEY_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), + /* Thread Channel */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CHAN_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), - /* Thread Netzwerk Name */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETNAME_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_lasertag_val, write_lasertag_val, NULL), + /* Extended PAN ID */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_EXTPAN_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), - /* Knoten-Liste / Discovery Trigger */ - BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NODES_CHAR, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_discovered_nodes, write_discover_cmd, NULL), -); + /* Network Key */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETKEY_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), + /* Thread Network Name */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETNAME_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_lasertag_val, write_lasertag_val, NULL), + + /* Node List / Discovery Trigger */ + BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NODES_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_discovered_nodes, write_discover_cmd, NULL), ); + +static uint8_t mfg_data[] = { 0xff, 0xff, 0x00 }; // Last byte for device role static const struct bt_data ad[] = { - BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), - BT_DATA_BYTES(BT_DATA_UUID128_ALL, - 0x00, 0xbc, 0x0c, 0x82, 0xae, 0xc3, 0x89, 0x92, - 0x22, 0x4a, 0x64, 0x6c, 0xcf, 0xe2, 0xaf, 0x03), + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, + 0x00, 0x10, 0x0c, 0x82, 0xae, 0xc3, 0x89, 0x92, + 0x22, 0x4a, 0x64, 0x6c, 0xcf, 0xe2, 0xaf, 0x03), + BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)), }; -int ble_mgmt_init(void) +int ble_mgmt_init(uint8_t device_type) { + device_role = device_type; + int err = bt_enable(NULL); - if (err) return err; - LOG_INF("Bluetooth initialisiert"); + if (err) + return err; + LOG_INF("Bluetooth initialized"); return 0; } int ble_mgmt_adv_start(void) { + const char set_device_role = device_role; + mfg_data[2] = set_device_role; // Update device role in advertising data + const char *name = lasertag_get_device_name(); bt_set_name(name); @@ -196,8 +245,9 @@ int ble_mgmt_adv_start(void) }; int err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), dynamic_sd, ARRAY_SIZE(dynamic_sd)); - if (!err) { - LOG_INF("Advertising gestartet als: %s", name); + if (!err) + { + LOG_INF("Advertising started as: %s, type: %d", name, device_role); } return err; } @@ -205,8 +255,9 @@ int ble_mgmt_adv_start(void) int ble_mgmt_adv_stop(void) { int err = bt_le_adv_stop(); - if (!err) { - LOG_INF("Advertising gestoppt"); + if (!err) + { + LOG_INF("Advertising stopped"); } return err; } \ No newline at end of file diff --git a/firmware/libs/ir/CMakeLists.txt b/firmware/libs/ir/CMakeLists.txt new file mode 100644 index 0000000..09f6d7a --- /dev/null +++ b/firmware/libs/ir/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(send) +add_subdirectory(recv) diff --git a/firmware/libs/ir/Kconfig b/firmware/libs/ir/Kconfig new file mode 100644 index 0000000..4940631 --- /dev/null +++ b/firmware/libs/ir/Kconfig @@ -0,0 +1,73 @@ +menu "IR protocol" + +config IR_PROTO + bool "IR protocol core" + help + Enable shared IR protocol settings used by send/receive libraries. + +if IR_PROTO + +menu "IR protocol configuration" + +config IR_SEND_CARRIER_HZ + int "IR carrier frequency (Hz)" + default 38000 + range 30000 45000 + help + Carrier frequency for PWM generation. Standard value is 38 kHz for TSOP48xx receivers. + +config IR_SEND_DUTY_CYCLE_PERCENT + int "Carrier duty cycle (%)" + default 50 + range 25 75 + help + PWM duty cycle percentage. Some receivers prefer 33%, others 50%. + Default 50% works with most TSOP-series receivers. + +config IR_PROTO_BASE_US + int "IR base period (microseconds)" + default 600 + range 300 1000 + help + Base timing used for start/mark/space. Default 600 µs (similar to Sony SIRC). + +config IR_PROTO_START_MULT + int "Start burst factor" + default 4 + range 2 8 + help + Start burst duration = base × factor. Default 4× for robust sync. + +config IR_PROTO_GAP_MULT + int "Start gap factor" + default 1 + range 0 4 + help + Gap after start burst (carrier off) = base × factor. 0 disables the gap. + +config IR_PROTO_MARK_MULT + int "Mark factor" + default 1 + range 1 2 + help + Mark duration = base × factor. Default 1×. + +config IR_PROTO_SPACE0_MULT + int "Space0 factor (bit 0)" + default 1 + range 1 3 + help + Space for bit 0 = base × factor. Default 1×. + +config IR_PROTO_SPACE1_MULT + int "Space1 factor (bit 1)" + default 2 + range 1 4 + help + Space for bit 1 = base × factor. Default 2× (double base time). + +endmenu + +endif + +endmenu diff --git a/firmware/libs/ir/recv/CMakeLists.txt b/firmware/libs/ir/recv/CMakeLists.txt new file mode 100644 index 0000000..8ac1824 --- /dev/null +++ b/firmware/libs/ir/recv/CMakeLists.txt @@ -0,0 +1,5 @@ +if(CONFIG_IR_RECV) + zephyr_library() + zephyr_sources(src/ir_recv.c) + zephyr_include_directories(include) +endif() diff --git a/firmware/libs/ir/recv/Kconfig b/firmware/libs/ir/recv/Kconfig new file mode 100644 index 0000000..19e6d3a --- /dev/null +++ b/firmware/libs/ir/recv/Kconfig @@ -0,0 +1,6 @@ +config IR_RECV + bool "IR Receive Library" + select IR_PROTO + help + Enable IR receive library for the laser tag system. + Placeholder implementation; timing parameters come from IR_PROTO.* diff --git a/firmware/libs/ir/recv/include/ir_recv.h b/firmware/libs/ir/recv/include/ir_recv.h new file mode 100644 index 0000000..38e70b6 --- /dev/null +++ b/firmware/libs/ir/recv/include/ir_recv.h @@ -0,0 +1,14 @@ +#ifndef IR_RECV_H +#define IR_RECV_H + +#include + +/** + * @brief Initialize IR receive pipeline (stub). + * + * Intended to configure GPIO/interrupts/ppi for future implementation. + * @return 0 on success, negative errno otherwise. + */ +int ir_recv_init(void); + +#endif /* IR_RECV_H */ diff --git a/firmware/libs/ir/recv/src/ir_recv.c b/firmware/libs/ir/recv/src/ir_recv.c new file mode 100644 index 0000000..0abfae9 --- /dev/null +++ b/firmware/libs/ir/recv/src/ir_recv.c @@ -0,0 +1,12 @@ +#include +#include + +#include "ir_recv.h" + +LOG_MODULE_REGISTER(ir_recv, LOG_LEVEL_INF); + +int ir_recv_init(void) +{ + LOG_INF("IR receive stub initialized (no implementation yet)"); + return 0; +} diff --git a/firmware/libs/ir_send/CMakeLists.txt b/firmware/libs/ir/send/CMakeLists.txt similarity index 100% rename from firmware/libs/ir_send/CMakeLists.txt rename to firmware/libs/ir/send/CMakeLists.txt diff --git a/firmware/libs/ir/send/Kconfig b/firmware/libs/ir/send/Kconfig new file mode 100644 index 0000000..aadfc00 --- /dev/null +++ b/firmware/libs/ir/send/Kconfig @@ -0,0 +1,6 @@ +config IR_SEND + bool "IR Send Library" + select IR_PROTO + help + Enable IR transmission library for laser tag system. + Provides PWM-based IR carrier generation with pulse-distance coding. diff --git a/firmware/libs/ir_send/include/ir_send.h b/firmware/libs/ir/send/include/ir_send.h similarity index 100% rename from firmware/libs/ir_send/include/ir_send.h rename to firmware/libs/ir/send/include/ir_send.h diff --git a/firmware/libs/ir_send/src/ir_send.c b/firmware/libs/ir/send/src/ir_send.c similarity index 100% rename from firmware/libs/ir_send/src/ir_send.c rename to firmware/libs/ir/send/src/ir_send.c diff --git a/firmware/libs/ir_send/Kconfig b/firmware/libs/ir_send/Kconfig index 4165bd4..c14339e 100644 --- a/firmware/libs/ir_send/Kconfig +++ b/firmware/libs/ir_send/Kconfig @@ -24,37 +24,47 @@ config IR_SEND_DUTY_CYCLE_PERCENT PWM duty cycle percentage. Some receivers prefer 33%, others 50%. Default 50% works with most TSOP-series receivers. -config IR_SEND_MARK_US - int "Mark duration (microseconds)" +config IR_PROTO_BASE_US + int "IR Basistakt (Mikrosekunden)" default 600 range 300 1000 help - Duration of carrier burst (mark) for each bit. Standard is 600 µs - following Sony SIRC timing. + Gemeinsamer Basistakt für Mark/Space/Start. Standard 600 µs (Sony SIRC ähnlich). -config IR_SEND_SPACE0_US - int "Space duration for bit 0 (microseconds)" - default 600 - range 300 1000 +config IR_PROTO_START_MULT + int "Startburst Faktor" + default 4 + range 2 8 help - Carrier-off duration (space) after mark for logical 0. - Default 600 µs creates 1.2 ms total bit time. + Startburst-Dauer = Basistakt × Faktor. Standard 4× zur sicheren Synchronisation. -config IR_SEND_SPACE1_US - int "Space duration for bit 1 (microseconds)" - default 1200 - range 800 2000 +config IR_PROTO_GAP_MULT + int "Start-Gap Faktor" + default 1 + range 0 4 help - Carrier-off duration (space) after mark for logical 1. - Default 1200 µs creates 1.8 ms total bit time. + Gap nach Startburst (Träger AUS) = Basistakt × Faktor. 0 deaktiviert Gap. -config IR_SEND_START_BURST_US - int "Start burst duration (microseconds)" - default 2400 - range 1500 4000 +config IR_PROTO_MARK_MULT + int "Mark Faktor" + default 1 + range 1 2 help - Initial synchronization burst at frame start. Receivers detect - this to sync on incoming frames. Default 2400 µs = 4× mark time. + Mark-Dauer = Basistakt × Faktor. Standard 1×. + +config IR_PROTO_SPACE0_MULT + int "Space0 Faktor (Bit 0)" + default 1 + range 1 3 + help + Space für Bit 0 = Basistakt × Faktor. Standard 1×. + +config IR_PROTO_SPACE1_MULT + int "Space1 Faktor (Bit 1)" + default 2 + range 1 4 + help + Space für Bit 1 = Basistakt × Faktor. Standard 2× (doppelte Basiszeit). endmenu diff --git a/firmware/libs/lasertag_utils/include/lasertag_utils.h b/firmware/libs/lasertag_utils/include/lasertag_utils.h index 88ce37e..789eae3 100644 --- a/firmware/libs/lasertag_utils/include/lasertag_utils.h +++ b/firmware/libs/lasertag_utils/include/lasertag_utils.h @@ -49,4 +49,48 @@ const uint8_t* lasertag_get_thread_ext_pan_id(void); */ const uint8_t* lasertag_get_thread_network_key(void); +/** + * @brief Set the device name. + * @param name Pointer to the name string. + * @param len Length of the name. + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_device_name(const char *name, size_t len); + +/** + * @brief Set the Thread PAN ID. + * @param pan_id 16-bit PAN ID. + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_thread_pan_id(uint16_t pan_id); + +/** + * @brief Set the Thread Network Name. + * @param name Pointer to the network name string. + * @param len Length of the name. + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_thread_network_name(const char *name, size_t len); + +/** + * @brief Set the Thread Channel. + * @param channel 8-bit channel (usually 11-26). + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_thread_channel(uint8_t channel); + +/** + * @brief Set the Thread Extended PAN ID. + * @param ext_id Pointer to the 8-byte extended PAN ID. + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_thread_ext_pan_id(const uint8_t *ext_id); + +/** + * @brief Set the Thread Network Key. + * @param key Pointer to the 16-byte network key. + * @return 0 on success, negative error code otherwise. + */ +int lasertag_set_thread_network_key(const uint8_t *key); + #endif /* LASERTAG_UTILS_H */ \ No newline at end of file diff --git a/firmware/libs/lasertag_utils/src/lasertag_utils.c b/firmware/libs/lasertag_utils/src/lasertag_utils.c index e81bf97..1a0c1ed 100644 --- a/firmware/libs/lasertag_utils/src/lasertag_utils.c +++ b/firmware/libs/lasertag_utils/src/lasertag_utils.c @@ -155,9 +155,9 @@ static int cmd_thread_set_chan(const struct shell *sh, size_t argc, char **argv) } SHELL_STATIC_SUBCMD_SET_CREATE(sub_thread, - SHELL_CMD_ARG(panid, NULL, "PAN ID setzen", cmd_thread_set_panid, 2, 0), - SHELL_CMD_ARG(chan, NULL, "Kanal setzen", cmd_thread_set_chan, 2, 0), - SHELL_CMD(ping, NULL, "Multicast Ping senden", cmd_thread_ping), + SHELL_CMD_ARG(panid, NULL, "Set PAN ID", cmd_thread_set_panid, 2, 0), + SHELL_CMD_ARG(chan, NULL, "Set channel", cmd_thread_set_chan, 2, 0), + SHELL_CMD(ping, NULL, "Send multicast ping", cmd_thread_ping), SHELL_SUBCMD_SET_END ); @@ -171,8 +171,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_ble, ); SHELL_STATIC_SUBCMD_SET_CREATE(sub_lasertag, - SHELL_CMD_ARG(name, NULL, "Name setzen", cmd_name_set, 2, 0), - SHELL_CMD(thread, &sub_thread, "Thread Konfiguration", NULL), + SHELL_CMD_ARG(name, NULL, "Set name", cmd_name_set, 2, 0), + SHELL_CMD(thread, &sub_thread, "Thread configuration", NULL), SHELL_CMD(ble, &sub_ble, "BLE Management", NULL), SHELL_CMD(reboot, NULL, "Reboot", cmd_reboot), SHELL_SUBCMD_SET_END diff --git a/firmware/libs/thread_mgmt/include/thread_mgmt.h b/firmware/libs/thread_mgmt/include/thread_mgmt.h index b4fb0cd..ac1b4ab 100644 --- a/firmware/libs/thread_mgmt/include/thread_mgmt.h +++ b/firmware/libs/thread_mgmt/include/thread_mgmt.h @@ -4,22 +4,22 @@ #include /** - * @brief Initialisiert den OpenThread-Stack, UDP und CoAP. + * @brief Initializes the OpenThread stack, UDP and CoAP. */ int thread_mgmt_init(void); /** - * @brief Sendet eine UDP-Nachricht. + * @brief Sends a UDP message. */ int thread_mgmt_send_udp(const char *addr_str, uint8_t *payload, uint16_t len); /** - * @brief Startet die Gerätesuche via CoAP Multicast. + * @brief Starts device discovery via CoAP Multicast. */ int thread_mgmt_discover_nodes(void); /** - * @brief Gibt die Liste der entdeckten Knotennamen zurück (kommagetrennt). + * @brief Returns the list of discovered node names (comma-separated). */ const char* thread_mgmt_get_discovered_list(void); diff --git a/firmware/libs/thread_mgmt/src/thread_mgmt.c b/firmware/libs/thread_mgmt/src/thread_mgmt.c index c0e667f..66bbcaa 100644 --- a/firmware/libs/thread_mgmt/src/thread_mgmt.c +++ b/firmware/libs/thread_mgmt/src/thread_mgmt.c @@ -33,7 +33,7 @@ static void coap_id_handler(void *context, otMessage *message, const otMessageIn return; } - LOG_INF("CoAP GET /id empfangen"); + LOG_INF("CoAP GET /id received"); response = otCoapNewMessage(instance, NULL); if (response == NULL) return; @@ -79,9 +79,9 @@ static void coap_discover_res_handler(void *context, otMessage *message, const o char addr_str[OT_IP6_ADDRESS_STRING_SIZE]; otIp6AddressToString(&message_info->mPeerAddr, addr_str, sizeof(addr_str)); - LOG_INF("Node entdeckt: %s (%s)", name_buf, addr_str); + LOG_INF("Node discovered: %s (%s)", name_buf, addr_str); - /* Zur Liste hinzufügen (einfaches CSV Format für BLE) */ + /* Add to list (simple CSV format for BLE) */ if (node_count < MAX_DISCOVERED_NODES && !strstr(discovered_nodes_list, name_buf)) { if (node_count > 0) strcat(discovered_nodes_list, ","); strcat(discovered_nodes_list, name_buf); @@ -96,7 +96,7 @@ int thread_mgmt_discover_nodes(void) otMessageInfo message_info; otError error = OT_ERROR_NONE; - /* Liste zurücksetzen */ + /* Reset list */ discovered_nodes_list[0] = '\0'; node_count = 0; @@ -116,7 +116,7 @@ int thread_mgmt_discover_nodes(void) return -EIO; } - LOG_INF("Discovery gestartet..."); + LOG_INF("Discovery started..."); return 0; } @@ -164,7 +164,7 @@ int thread_mgmt_send_udp(const char *addr_str, uint8_t *payload, uint16_t len) otMessageAppend(message, payload, len); otUdpSend(instance, &s_udp_socket, message, &message_info); - LOG_INF("UDP gesendet an %s", addr_str); + LOG_INF("UDP sent to %s", addr_str); return 0; } @@ -211,6 +211,6 @@ int thread_mgmt_init(void) s_id_resource.mContext = instance; otCoapAddResource(instance, &s_id_resource); - LOG_INF("Thread MGMT: Initialisiert, UDP %d & CoAP %d offen.", UDP_PORT, OT_DEFAULT_COAP_PORT); + LOG_INF("Thread MGMT: Initialized, UDP %d & CoAP %d open.", UDP_PORT, OT_DEFAULT_COAP_PORT); return 0; } \ No newline at end of file