Added device types and vest mini app
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 22s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 22s
This commit is contained in:
@@ -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).
|
||||
|
||||

|
||||
|
||||
@@ -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<br>**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. <br>**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.<br>**Charakteristik**: Besitzt eine sehr agressive **AGC (Automatic Gain Controll)**<br>**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.<br>**Nutzen:** Das Signal bleibt viel konstanter als bei bei einem TSOP.|
|
||||
|
||||
|
||||
*Stand: 04.01.2026*
|
||||
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<br>600 µs"] space1["Space 1<br>1200 µs"]:2 mark2["Mark<br>600 µs"] space2["Space 0<br>600 µs"] mark3["Mark<br>600 µs"] space3["Space 1<br>1200 µs"]:2
|
||||
bit1["<b>1</b>"]:3 bit2["<b>0</b>"]:2 bit3["<b>1</b>"]: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) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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)
|
||||
* `8 bit` Protokoll-ID (Magic Byte zur Unterscheidung von Fernbedienungen)
|
||||
* `16 bit` Shooter ID
|
||||
* `8 bit` Info (4 bit Team, 4 bit Damage Class)
|
||||
@@ -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) {
|
||||
|
||||
13
firmware/apps/vest/CMakeLists.txt
Normal file
13
firmware/apps/vest/CMakeLists.txt
Normal file
@@ -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)
|
||||
41
firmware/apps/vest/prj.conf
Normal file
41
firmware/apps/vest/prj.conf
Normal file
@@ -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
|
||||
46
firmware/apps/vest/src/main.c
Normal file
46
firmware/apps/vest/src/main.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <lasertag_utils.h>
|
||||
#include <thread_mgmt.h>
|
||||
#include <ble_mgmt.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
#include <openthread/thread.h>
|
||||
#include <openthread/coap.h>
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -3,4 +3,4 @@
|
||||
add_subdirectory(ble_mgmt)
|
||||
add_subdirectory(thread_mgmt)
|
||||
add_subdirectory(lasertag_utils)
|
||||
add_subdirectory(ir_send)
|
||||
add_subdirectory(ir)
|
||||
@@ -2,4 +2,4 @@
|
||||
rsource "lasertag_utils/Kconfig"
|
||||
rsource "thread_mgmt/Kconfig"
|
||||
rsource "ble_mgmt/Kconfig"
|
||||
rsource "ir_send/Kconfig"
|
||||
rsource "ir/Kconfig"
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
2
firmware/libs/ir/CMakeLists.txt
Normal file
2
firmware/libs/ir/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(send)
|
||||
add_subdirectory(recv)
|
||||
73
firmware/libs/ir/Kconfig
Normal file
73
firmware/libs/ir/Kconfig
Normal file
@@ -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
|
||||
5
firmware/libs/ir/recv/CMakeLists.txt
Normal file
5
firmware/libs/ir/recv/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
if(CONFIG_IR_RECV)
|
||||
zephyr_library()
|
||||
zephyr_sources(src/ir_recv.c)
|
||||
zephyr_include_directories(include)
|
||||
endif()
|
||||
6
firmware/libs/ir/recv/Kconfig
Normal file
6
firmware/libs/ir/recv/Kconfig
Normal file
@@ -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.*
|
||||
14
firmware/libs/ir/recv/include/ir_recv.h
Normal file
14
firmware/libs/ir/recv/include/ir_recv.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef IR_RECV_H
|
||||
#define IR_RECV_H
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
12
firmware/libs/ir/recv/src/ir_recv.c
Normal file
12
firmware/libs/ir/recv/src/ir_recv.c
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
6
firmware/libs/ir/send/Kconfig
Normal file
6
firmware/libs/ir/send/Kconfig
Normal file
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user