commit e706b22f31a52a49dfbebc9e262aea23b65efb5e Author: Christian Mueller Date: Thu Mar 19 22:27:49 2026 +0100 Add design specification for LoRaWAN Web Portal Based on Pflichtenheft v2.2, documents the full system design including daemon, API, frontend, and infrastructure with agreed deviations (Axum 0.8, SQLx 0.8, Vite 6, Tailwind 4). Co-Authored-By: Claude Opus 4.6 (1M context) diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/.claude/settings.local.json b/3_SENSOR_lorawan-ds18b20-local-lmic/.claude/settings.local.json new file mode 100644 index 0000000..17254a1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(dir:*)", + "Bash(git remote add:*)" + ] + } +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/.gitignore b/3_SENSOR_lorawan-ds18b20-local-lmic/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/.vscode/extensions.json b/3_SENSOR_lorawan-ds18b20-local-lmic/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/BATTERIEBETRIEB_DEEP_SLEEP.md b/3_SENSOR_lorawan-ds18b20-local-lmic/BATTERIEBETRIEB_DEEP_SLEEP.md new file mode 100644 index 0000000..a8d6230 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/BATTERIEBETRIEB_DEEP_SLEEP.md @@ -0,0 +1,290 @@ +# Batteriebetrieb mit Deep Sleep + +## Übersicht + +Der ESP32 nutzt Deep Sleep für maximale Batterielaufzeit. Das System: +- **Misst alle 5 Minuten** den Füllstand +- **Sendet via LoRaWAN** die Daten +- **Schläft danach** bis zur nächsten Messung +- **Display ist aus** während Deep Sleep + +## Stromverbrauch + +### Normale Betrieb (ohne Deep Sleep): +- **~80-240 mA** kontinuierlich +- **Batterielaufzeit**: ~1-3 Tage (mit 2000 mAh Akku) + +### Mit Deep Sleep: +- **~10 µA** während Sleep (0.01 mA) +- **~100 mA** für 10-30 Sekunden während Messung/Senden +- **Batterielaufzeit**: ~3-6 Monate (mit 2000 mAh Akku) + +## Konfiguration + +In [src/main.cpp](src/main.cpp) (Zeile 18-20): + +```cpp +// Deep Sleep Konfiguration (Batteriebetrieb) +#define SLEEP_INTERVAL 300 // Sleep-Zeit in Sekunden (300 = 5 Minuten) +#define uS_TO_S_FACTOR 1000000 // Umrechnung Mikrosekunden zu Sekunden +``` + +### SLEEP_INTERVAL anpassen: + +| Intervall | Sekunden | Messungen/Tag | Batterielaufzeit (2000mAh) | +|-----------|----------|---------------|---------------------------| +| 1 Minute | 60 | 1440 | ~1 Monat | +| 5 Minuten | 300 | 288 | ~3-6 Monate | +| 10 Minuten| 600 | 144 | ~6-12 Monate | +| 30 Minuten| 1800 | 48 | ~1-2 Jahre | +| 1 Stunde | 3600 | 24 | ~2-3 Jahre | + +**Beispiele:** +```cpp +#define SLEEP_INTERVAL 60 // 1 Minute +#define SLEEP_INTERVAL 300 // 5 Minuten (Standard) +#define SLEEP_INTERVAL 600 // 10 Minuten +#define SLEEP_INTERVAL 1800 // 30 Minuten +#define SLEEP_INTERVAL 3600 // 1 Stunde +``` + +## Funktionsweise + +### Ablauf: + +``` +1. ESP32 wacht auf (Timer oder Reset) + ↓ +2. Initialisierung (Display, LoRa, Sensor) + ↓ +3. LoRaWAN Join (nur beim ersten Mal, ~5-10 Sek) + ↓ +4. Sensor-Messung (Distanz → Füllstand) + ↓ +5. Display zeigt Werte (1-2 Sekunden) + ↓ +6. LoRaWAN Daten senden (5-10 Sekunden) + ↓ +7. Display ausschalten + ↓ +8. Deep Sleep (5 Minuten) + ↓ +9. Zurück zu Schritt 1 +``` + +**Gesamtdauer pro Zyklus**: ~10-30 Sekunden Wachzeit + +## RTC Memory (Persistenter Speicher) + +Der ESP32 behält folgende Variablen im Deep Sleep: + +```cpp +RTC_DATA_ATTR int bootCount = 0; // Zählt Aufwach-Zyklen +RTC_DATA_ATTR bool joinedNetwork = false; // LoRaWAN Join-Status +``` + +**Warum wichtig?** +- LoRaWAN Join muss nur **einmal** erfolgen (spart Zeit & Energie) +- Boot-Zähler zeigt wie oft ESP32 aufgewacht ist + +## Serial Monitor Ausgabe + +### Beim ersten Start: +``` +=== ESP32 aufgewacht === +Boot-Nummer: 1 +Erster Start oder Reset +Starte LoRaWAN... +Beitritt läuft... +Netzwerkbeitritt erfolgreich +--- Messung --- +Distanz: 135.2 cm +Füllhöhe: 38.8 cm +Füllstand: 24.2 % +LoRa Payload (Hex): 05 48 01 84 00 f2 +Nachricht übertragen. +Daten gesendet, bereite Deep Sleep vor... +=== Deep Sleep aktivieren === +Schlafdauer: 300 Sekunden (5 Minuten) +Gehe in Deep Sleep... +``` + +### Nach 5 Minuten (nächster Zyklus): +``` +=== ESP32 aufgewacht === +Boot-Nummer: 2 +Aufgewacht durch Timer (5 Minuten vergangen) +Starte LoRaWAN... +--- Messung --- +Distanz: 136.1 cm +Füllhöhe: 37.9 cm +Füllstand: 23.7 % +LoRa Payload (Hex): 05 51 01 7b 00 ed +Nachricht übertragen. +Daten gesendet, bereite Deep Sleep vor... +=== Deep Sleep aktivieren === +Schlafdauer: 300 Sekunden (5 Minuten) +Gehe in Deep Sleep... +``` + +## Display-Verhalten + +### Während Messung (10-30 Sekunden): +- Display zeigt Füllstand in großen Zahlen +- Zeigt Füllhöhe und Distanz +- iotwave.de Header + +### Während Deep Sleep (5 Minuten): +- **Display komplett aus** (0 mA Stromverbrauch) +- Display-Controller im Standby-Modus + +## LoRaWAN-Besonderheiten + +### Join-Verhalten: +- **Erster Boot**: LoRaWAN OTAA Join (~5-10 Sekunden) +- **Weitere Boots**: Join wird übersprungen (Session bleibt erhalten) + +**WICHTIG bei LoRaWAN:** +- Nach jedem Deep Sleep muss das Gerät erneut joinen +- LMIC-Bibliothek speichert Session **nicht** im RTC Memory +- **Lösung**: Bei jedem Boot wird `LMIC_startJoining()` aufgerufen + +## Batterie-Empfehlungen + +### Batterietypen: +1. **LiPo Akku (empfohlen)** + - 3.7V, 2000-6000 mAh + - Mit Schutzschaltung + - Wiederaufladbar via USB + +2. **18650 Lithium-Zelle** + - 3.7V, 2500-3500 mAh + - Mit Battery Shield/Holder + +3. **Alkaline AA (4× 1.5V = 6V)** + - Mit Spannungsregler auf 5V + - Nicht wiederaufladbar + +### Laufzeit-Rechnung: + +**Beispiel**: 2000 mAh Akku, 5 Minuten Intervall + +``` +Wachzeit pro Zyklus: 20 Sekunden +Sleep pro Zyklus: 300 Sekunden + +Stromverbrauch Wach: 100 mA × 20s = 0.56 mAh +Stromverbrauch Sleep: 0.01 mA × 300s = 0.0008 mAh + +Pro Zyklus: ~0.56 mAh +Zyklen pro Tag: 288 (alle 5 Min) +Verbrauch pro Tag: 288 × 0.56 = ~160 mAh + +Batterielaufzeit: 2000 mAh / 160 mAh/Tag = 12.5 Tage +``` + +**Mit niedrigerem Intervall (z.B. 30 Min):** +``` +Zyklen pro Tag: 48 +Verbrauch pro Tag: 48 × 0.56 = ~27 mAh +Batterielaufzeit: 2000 mAh / 27 mAh/Tag = 74 Tage (~2.5 Monate) +``` + +## Zusätzliche Optimierungen (Optional) + +### 1. Display komplett deaktivieren +Falls Display nicht benötigt wird: + +```cpp +// In setup() auskommentieren: +// display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + +// In do_send() Display-Code auskommentieren +``` + +**Einsparung**: ~10-20 mAh pro Tag + +### 2. Längere Sleep-Intervalle +Passen Sie `SLEEP_INTERVAL` an: + +```cpp +#define SLEEP_INTERVAL 1800 // 30 Minuten statt 5 +``` + +### 3. Deaktiviere Debug-Ausgaben +Entfernen Sie Serial.print() Aufrufe (spart minimal Strom) + +## Troubleshooting + +### Problem: ESP32 wacht nicht auf + +**Lösung 1**: Reset-Button drücken +**Lösung 2**: USB neu verbinden +**Lösung 3**: Batterie-Spannung prüfen (>3.0V nötig) + +### Problem: LoRaWAN sendet nicht mehr + +**Ursache**: Join-Session verloren nach Deep Sleep + +**Lösung**: Code joint automatisch bei jedem Boot neu + +### Problem: Display bleibt schwarz + +**Normal**: Display ist im Deep Sleep aus + +**Falls nach Wakeup noch aus**: Reset drücken + +### Problem: Batterie hält nicht so lange + +**Mögliche Ursachen**: +1. Batterie-Kapazität zu niedrig gewählt +2. SLEEP_INTERVAL zu kurz eingestellt +3. Andere Verbraucher am ESP32 (LEDs, etc.) +4. LoRaWAN Join dauert zu lange (schlechter Empfang) + +**Lösungen**: +- Größeren Akku verwenden +- SLEEP_INTERVAL erhöhen +- LEDs entfernen/deaktivieren +- Gateway näher platzieren + +## ChirpStack/TTN Anzeige + +ChirpStack zeigt: +- **Letzte Messung**: Vor X Minuten +- **Uplink Interval**: Alle 5 Minuten (bzw. SLEEP_INTERVAL) +- **Frame Counter**: Zählt hoch bei jedem Boot + +## Wichtige Hinweise + +⚠️ **WICHTIG bei Deep Sleep:** + +1. **Serial Monitor zeigt nichts** während Sleep + - Normal! ESP32 ist aus + +2. **USB-Verbindung bleibt aktiv** + - ESP32 kann trotzdem mit Batterie laufen + +3. **Beim Debuggen** (Serial Monitor): + - Nach Upload: ESP32 startet automatisch + - Nach Deep Sleep: Kurz aufwachen, dann wieder Sleep + +4. **Upload während Sleep nicht möglich** + - Drücke Reset-Button vor Upload + - Oder warte bis ESP32 aufwacht + +## Weitere Optimierungen + +Wenn noch längere Laufzeit gewünscht: + +1. **Sensor Power Management** + - HC-SR04 VCC an GPIO schalten + - Nur bei Messung einschalten + +2. **LoRa Power Amplifier deaktivieren** + - TX Power reduzieren (falls Gateway nah) + +3. **CPU-Frequenz reduzieren** + - 80 MHz statt 240 MHz (in platformio.ini) + +Benötigen Sie weitere Optimierungen? diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_FUELLSTAND.md b/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_FUELLSTAND.md new file mode 100644 index 0000000..f1276f0 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_FUELLSTAND.md @@ -0,0 +1,146 @@ +# ChirpStack Decoder für Füllstandsmessung + +## Payload-Format (8 Bytes) + +Das Gerät sendet 8 Bytes via LoRaWAN: + +| Bytes | Beschreibung | Einheit | Berechnung | +|-------|--------------|---------|------------| +| 0-1 | Distanz | mm | Big Endian uint16 | +| 2-3 | Füllhöhe | mm | Big Endian uint16 | +| 4-5 | Füllstand | % × 10 | Big Endian uint16 (855 = 85.5%) | +| 6-7 | Batterie | mV | Big Endian uint16 (3700 = 3.7V) | + +## Decoder-Code für ChirpStack v4 + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + var fPort = input.fPort; + + // Validierung + if (bytes.length !== 8) { + return { + errors: ["Invalid payload length - expected 8 bytes, got " + bytes.length] + }; + } + + if (fPort !== 1) { + return { + errors: ["Invalid fPort - expected 1, got " + fPort] + }; + } + + // Distanz (Byte 0-1, Big Endian) + var distanceMm = (bytes[0] << 8) | bytes[1]; + var distanceCm = distanceMm / 10.0; + var distanceM = distanceMm / 1000.0; + + // Füllhöhe (Byte 2-3, Big Endian) + var fillHeightMm = (bytes[2] << 8) | bytes[3]; + var fillHeightCm = fillHeightMm / 10.0; + var fillHeightM = fillHeightMm / 1000.0; + + // Füllstand in % (Byte 4-5, Big Endian, × 10) + var fillPercentRaw = (bytes[4] << 8) | bytes[5]; + var fillPercent = fillPercentRaw / 10.0; + + // Batteriespannung (Byte 6-7, Big Endian, in mV) + var batteryMv = (bytes[6] << 8) | bytes[7]; + var batteryV = batteryMv / 1000.0; + + // Batterie-Prozent berechnen (LiPo: 3.0V = 0%, 4.2V = 100%) + var batteryPercent = ((batteryV - 3.0) / (4.2 - 3.0)) * 100.0; + if (batteryPercent < 0) batteryPercent = 0; + if (batteryPercent > 100) batteryPercent = 100; + + return { + data: { + // Distanz (Sensor bis Flüssigkeit) + distance_mm: distanceMm, + distance_cm: distanceCm, + distance_m: distanceM, + + // Füllhöhe (Flüssigkeitshöhe im Tank) + fill_height_mm: fillHeightMm, + fill_height_cm: fillHeightCm, + fill_height_m: fillHeightM, + + // Füllstand in Prozent + fill_percent: fillPercent, + + // Batterie + battery_mv: batteryMv, + battery_v: batteryV, + battery_percent: Math.round(batteryPercent) + } + }; +} +``` + +## Beispiel-Payloads + +Angenommen: Tank-Höhe = 180 cm + +| Payload (Hex) | Distanz | Füllhöhe | Füllstand | +|---------------|---------|----------|-----------| +| `0348 0AA0 0226` | 84.0 cm | 170.0 cm | 94.4% | +| `05DC 0708 0197` | 150.0 cm | 180.0 cm | 40.9% | +| `0708 0000 0000` | 180.0 cm | 0.0 cm | 0% | +| `0014 06F8 03E8` | 2.0 cm | 178.0 cm | 100.0% | + +### Berechnung Beispiel 1: `0348 0AA0 0226` + +- **Distanz**: `0x0348` = 840 mm = 84.0 cm +- **Füllhöhe**: `0x0AA0` = 2720 mm = 272.0 cm (Fehler! sollte max 180cm sein) +- **Füllstand**: `0x0226` = 550 = 55.0% + +## Installation + +1. Öffne ChirpStack → **Device Profile** → **Codec** +2. Wähle **JavaScript** als Codec +3. Kopiere den Decoder-Code ins **Uplink decoder** Feld +4. Klicke **Submit** + +## Anzeige in ChirpStack + +Nach erfolgreicher Dekodierung siehst du in ChirpStack: + +```json +{ + "distance_mm": 840, + "distance_cm": 84.0, + "distance_m": 0.084, + "fill_height_mm": 960, + "fill_height_cm": 96.0, + "fill_height_m": 0.096, + "fill_percent": 53.3, + "battery_mv": 3850, + "battery_v": 3.85, + "battery_percent": 71 +} +``` + +## Grafana Dashboard + +Empfohlene Visualisierungen: + +1. **Gauge**: `fill_percent` (0-100%) +2. **Graph**: `fill_height_cm` über Zeit +3. **Gauge**: `battery_percent` (0-100%) +4. **Graph**: `battery_v` über Zeit (Batterie-Entladung) +5. **Graph**: `distance_cm` über Zeit (Fehlerdiagnose) + +## Troubleshooting + +### Negative Füllhöhe + +**Problem**: Füllhöhe zeigt negative Werte + +**Lösung**: Tank-Höhe (`TANK_HEIGHT`) im Code anpassen + +### Füllstand über 100% + +**Problem**: Füllstand zeigt über 100% + +**Lösung**: Sensor ist zu nah am Boden installiert oder Tank-Höhe falsch konfiguriert diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_SETUP.md b/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_SETUP.md new file mode 100644 index 0000000..a827f8d --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/CHIRPSTACK_DECODER_SETUP.md @@ -0,0 +1,325 @@ +# ChirpStack Decoder Einrichtung + +## Problem +Die Payload wird als Hex-String angezeigt (`frm_payload:"008b"`), aber nicht dekodiert. + +## Lösung: Decoder im Device Profile einrichten + +### Schritt 1: Device Profile öffnen + +1. Gehe zu **Device Profiles** in ChirpStack +2. Wähle das Device Profile aus, das dein Gerät verwendet +3. Falls du nicht weißt welches: Gehe zu deinem Device und schaue unter "Device Profile" + +### Schritt 2: Codec konfigurieren + +#### Für ChirpStack v4: + +1. Öffne dein **Device Profile** +2. Klicke auf den Tab **"Codec"** +3. Wähle bei **Codec**: `JavaScript` +4. Füge folgenden Code in das **Uplink decoder** Feld ein: + +```javascript +function decodeUplink(input) { + // Robuster Decoder für HC-SR04 Ultraschallsensor + var bytes = input.bytes; + var fPort = input.fPort; + + // Debug-Informationen + var debug = { + bytes_received: bytes.length, + fPort: fPort, + raw_hex: "" + }; + + // Bytes als Hex-String für Debug + for (var i = 0; i < bytes.length; i++) { + debug.raw_hex += bytes[i].toString(16).padStart(2, '0'); + } + + // Validierung + if (bytes.length !== 2) { + return { + errors: ["Invalid payload length - expected 2 bytes, got " + bytes.length + " bytes. Hex: " + debug.raw_hex] + }; + } + + // Port 1 = Distanz + if (fPort !== 1) { + return { + errors: ["Invalid fPort - expected 1, got " + fPort] + }; + } + + // Distanz aus 2 Bytes (Big Endian) auslesen + var distanceMm = (bytes[0] << 8) | bytes[1]; + + // Umrechnung in verschiedene Einheiten + var distanceCm = distanceMm / 10.0; + var distanceM = distanceMm / 1000.0; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceCm, + distance_m: distanceM, + debug: debug + } + }; +} +``` + +5. Klicke auf **"Submit"** oder **"Save"** + +#### Für ChirpStack v3: + +1. Öffne dein **Device Profile** +2. Klicke auf den Tab **"Codec"** +3. Wähle bei **Payload codec**: `Custom JavaScript codec functions` +4. Füge folgenden Code in das **Decode function** Feld ein: + +```javascript +function Decode(fPort, bytes) { + if (bytes.length !== 2) { + return { + error: "Invalid payload length - expected 2 bytes, got " + bytes.length + }; + } + + // Distanz aus 2 Bytes (Big Endian) auslesen + var distanceMm = (bytes[0] << 8) | bytes[1]; + + // Umrechnung in verschiedene Einheiten + var distanceCm = distanceMm / 10.0; + var distanceM = distanceMm / 1000.0; + + return { + distance_mm: distanceMm, + distance_cm: distanceCm, + distance_m: distanceM + }; +} +``` + +5. Klicke auf **"Update device-profile"** + +### Schritt 3: Decoder testen + +1. Im Device Profile, unter **Codec** +2. Klicke auf **"Test decoder"** (falls vorhanden) +3. Gib ein: `008b` (deine aktuelle Payload) +4. Erwartetes Ergebnis: + +```json +{ + "data": { + "distance_mm": 139, + "distance_cm": 13.9, + "distance_m": 0.139 + } +} +``` + +### Schritt 4: Neue Daten empfangen + +1. Warte auf die nächste Uplink-Nachricht (alle 30 Sekunden) +2. Gehe zu deinem **Device** → **LoRaWAN frames** oder **Events** +3. Bei der neuen Uplink-Message solltest du nun sehen: + +```json +{ + "object": { + "distance_mm": 139, + "distance_cm": 13.9, + "distance_m": 0.139 + } +} +``` + +## Deine aktuelle Payload dekodiert + +**Raw Payload:** `008b` +- Byte 0: `00` (hex) = 0 (dezimal) +- Byte 1: `8b` (hex) = 139 (dezimal) +- **Distanz:** 0×256 + 139 = **139 mm = 13.9 cm** + +Das bedeutet, dein Sensor misst aktuell eine Distanz von **13.9 cm** zu einem Objekt! + +## Weitere Beispiele + +| Hex Payload | Berechnung | Distanz | +|-------------|------------|---------| +| `008b` | (0×256) + 139 = 139 | 13.9 cm | +| `04d2` | (4×256) + 210 = 1234 | 123.4 cm | +| `0064` | (0×256) + 100 = 100 | 10.0 cm | +| `03e8` | (3×256) + 232 = 1000 | 100.0 cm = 1 m | +| `ffff` | (255×256) + 255 = 65535 | 6553.5 cm = 65.5 m | + +## Alternative: Minimaler Decoder (ohne Validierung) + +Falls der obige Decoder Probleme macht, versuche diesen **minimalen Decoder**: + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + var distanceMm = (bytes[0] << 8) | bytes[1]; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceMm / 10.0, + distance_m: distanceMm / 1000.0 + } + }; +} +``` + +Dieser Decoder hat **keine Fehlerprüfung** und sollte immer funktionieren. + +## Troubleshooting + +### Decoder wird nicht angewendet + +**Problem:** Payload wird immer noch als Hex angezeigt + +**Lösungen:** +1. ✅ Stelle sicher, dass der Decoder im **Device Profile** gespeichert wurde +2. ✅ Warte auf eine **neue** Uplink-Message (alte Messages werden nicht neu dekodiert) +3. ✅ Prüfe, ob JavaScript-Fehler im Decoder vorhanden sind +4. ✅ Stelle sicher, dass das richtige Device Profile verwendet wird + +### JavaScript Fehler im Decoder + +**Fehler:** `SyntaxError` oder `Unexpected token` + +**Lösung:** +- Kopiere den Code genau wie oben angegeben +- Achte auf korrekte Klammern `{ }` und Semikolons `;` +- Verwende keinen Kommentar-Stil, der nicht unterstützt wird + +### Payload hat falsche Länge + +**Fehler:** `Invalid payload length` + +**Problem:** Die Payload hat nicht genau 2 Bytes + +**Lösung:** +- Überprüfe den Arduino-Code in `do_send()` Funktion +- Stelle sicher, dass `sizeof(payload)` = 2 ist +- Prüfe Serial Monitor Ausgabe + +### Negative Distanzwerte + +**Problem:** Distanz wird als sehr große Zahl angezeigt + +**Ursache:** HC-SR04 liefert Fehler (-1), wird aber als uint16_t interpretiert + +**Lösung:** Der Code fängt dies bereits ab und sendet 0 bei Fehlern: +```cpp +if (distance < 0) { + distance = 0; // Fehlerfall +} +``` + +## Integration mit anderen Systemen + +### InfluxDB / Grafana + +Nach Dekodierung kannst du die Daten in InfluxDB speichern: + +```javascript +// ChirpStack v4 Integration +{ + "measurement": "distance_sensor", + "tags": { + "device": "hc-sr04-01" + }, + "fields": { + "distance_cm": {{.object.distance_cm}}, + "distance_m": {{.object.distance_m}} + } +} +``` + +### MQTT + +Die dekodierten Daten werden automatisch über MQTT publiziert: + +**Topic:** `application/{application_id}/device/{dev_eui}/event/up` + +**Payload:** +```json +{ + "deviceName": "hc-sr04-sensor-01", + "devEUI": "70b3d57ed005a1b2", + "object": { + "distance_mm": 139, + "distance_cm": 13.9, + "distance_m": 0.139 + } +} +``` + +### Node-RED + +Du kannst die Daten in Node-RED empfangen und verarbeiten: + +```javascript +// Node-RED Function Node +var distance_cm = msg.payload.object.distance_cm; + +if (distance_cm < 20) { + msg.payload = "Warnung: Objekt zu nah!"; + return [msg, null]; +} else { + msg.payload = "Distanz OK: " + distance_cm + " cm"; + return [null, msg]; +} +``` + +## Decoder mit erweiterten Features (Optional) + +Wenn du später mehr Daten senden möchtest, kannst du den Decoder erweitern: + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + var data = {}; + + // Port 1: Distanz (2 Bytes) + if (input.fPort === 1 && bytes.length === 2) { + var distanceMm = (bytes[0] << 8) | bytes[1]; + data.distance_mm = distanceMm; + data.distance_cm = distanceMm / 10.0; + data.distance_m = distanceMm / 1000.0; + } + + // Port 2: Temperatur (optional für später) + else if (input.fPort === 2 && bytes.length === 2) { + var temp = ((bytes[0] << 8) | bytes[1]) / 100.0; + data.temperature = temp; + } + + else { + return { + errors: ["Invalid fPort or payload length"] + }; + } + + return { + data: data + }; +} +``` + +## Zusammenfassung + +1. ✅ Gehe zu **Device Profiles** → Dein Profil → **Codec** +2. ✅ Wähle **JavaScript** als Codec +3. ✅ Kopiere den Decoder-Code ein +4. ✅ Speichere das Device Profile +5. ✅ Warte auf nächste Uplink-Message +6. ✅ Überprüfe dekodierte Daten unter **Device** → **Events** + +Deine Sensordaten sollten nun lesbar sein! 🎉 diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/DECODER_FIX.md b/3_SENSOR_lorawan-ds18b20-local-lmic/DECODER_FIX.md new file mode 100644 index 0000000..559d27a --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/DECODER_FIX.md @@ -0,0 +1,245 @@ +# ChirpStack Decoder Fehler beheben + +## Problem +``` +ERROR: UPLINK_CODEC +description: "decodeUplink returned errors: Invalid payload length" +``` + +## Lösung 1: Minimaler Decoder verwenden + +Der einfachste Weg ist, einen Decoder **ohne Validierung** zu verwenden: + +### Schritt-für-Schritt: + +1. **Öffne ChirpStack** +2. Gehe zu **Device Profiles** +3. Wähle dein Profil: `LILYGO_1_HC-SR04` +4. Klicke auf **Codec** Tab +5. Wähle **Codec:** `JavaScript` +6. **Lösche** alles im "Uplink decoder" Feld +7. **Kopiere** folgenden Code ein: + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + var distanceMm = (bytes[0] << 8) | bytes[1]; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceMm / 10.0, + distance_m: distanceMm / 1000.0 + } + }; +} +``` + +8. **Speichere** das Device Profile +9. **Warte** auf die nächste Uplink-Nachricht (alle 30 Sekunden) + +## Lösung 2: Decoder mit Debug-Informationen + +Falls Lösung 1 nicht funktioniert, verwende diesen Decoder mit Debug-Ausgabe: + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + var fPort = input.fPort; + + // Debug + var hex = ""; + for (var i = 0; i < bytes.length; i++) { + hex += bytes[i].toString(16).padStart(2, '0'); + } + + // Längenprüfung + if (bytes.length !== 2) { + return { + errors: ["Expected 2 bytes, got " + bytes.length + ". Hex: " + hex + ", fPort: " + fPort] + }; + } + + // Dekodierung + var distanceMm = (bytes[0] << 8) | bytes[1]; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceMm / 10.0, + distance_m: distanceMm / 1000.0, + raw_hex: hex, + bytes_count: bytes.length, + fPort: fPort + } + }; +} +``` + +Dieser Decoder gibt dir zusätzliche Informationen, wenn ein Fehler auftritt. + +## Lösung 3: Prüfe die Payload-Länge + +### Mögliche Ursachen für "Invalid payload length": + +1. **ChirpStack fügt zusätzliche Bytes hinzu** + - Manche ChirpStack-Konfigurationen fügen MAC-Commands zur Payload hinzu + +2. **Falsche fPort-Konfiguration** + - Stelle sicher, dass fPort = 1 ist + +3. **Alte Decoder-Version** + - Lösche den alten Decoder komplett und füge den neuen ein + +### Überprüfung im Serial Monitor: + +Schau im Serial Monitor nach, was tatsächlich gesendet wird: + +``` +Distanz gesendet: +Distanz: 13.9 cm +Distanz: 139.0 mm +Nachricht übertragen. +``` + +Die Payload sollte genau 2 Bytes sein. + +## Lösung 4: ChirpStack v3 vs v4 + +### Für ChirpStack v4 (aktuell): + +```javascript +function decodeUplink(input) { + // ... Code wie oben +} +``` + +### Für ChirpStack v3 (alt): + +```javascript +function Decode(fPort, bytes) { + if (bytes.length !== 2) { + return { error: "Invalid length" }; + } + + var distanceMm = (bytes[0] << 8) | bytes[1]; + + return { + distance_mm: distanceMm, + distance_cm: distanceMm / 10.0, + distance_m: distanceMm / 1000.0 + }; +} +``` + +**Wichtig:** ChirpStack v3 nutzt `Decode()`, v4 nutzt `decodeUplink()`! + +## Testen des Decoders + +### Im ChirpStack Web-Interface: + +1. Gehe zu **Device Profile** → **Codec** +2. Scrolle nach unten zu **"Test decoder"** (falls vorhanden) +3. Gib ein: + - **fPort:** `1` + - **Payload (hex):** `008b` +4. Klicke auf **"Test"** + +**Erwartetes Ergebnis:** +```json +{ + "data": { + "distance_mm": 139, + "distance_cm": 13.9, + "distance_m": 0.139 + } +} +``` + +## Was du in den Events sehen solltest + +Nach erfolgreicher Konfiguration siehst du unter **Device → Events**: + +### Vorher (Fehler): +```json +{ + "level": "ERROR", + "code": "UPLINK_CODEC", + "description": "decodeUplink returned errors: Invalid payload length" +} +``` + +### Nachher (Erfolg): +```json +{ + "deviceName": "00000 - HC-SR04-1", + "devEui": "70b3d57ed005a1b2", + "fPort": 1, + "data": "008b", + "object": { + "distance_mm": 139, + "distance_cm": 13.9, + "distance_m": 0.139 + } +} +``` + +## Häufige Fehler + +### 1. Decoder nicht gespeichert +- ❌ Decoder eingefügt, aber nicht auf "Submit" geklickt +- ✅ Immer auf "Submit" oder "Save" klicken! + +### 2. Falsches Device Profile +- ❌ Decoder im falschen Device Profile eingetragen +- ✅ Prüfe, welches Profile dein Device nutzt + +### 3. ChirpStack-Version verwechselt +- ❌ `Decode()` in ChirpStack v4 verwendet +- ✅ ChirpStack v4 braucht `decodeUplink()` + +### 4. JavaScript-Fehler +- ❌ Syntaxfehler im Decoder-Code +- ✅ Code exakt wie oben kopieren + +### 5. Alte Events angeschaut +- ❌ Alte Uplink-Messages werden nicht neu dekodiert +- ✅ Warte auf neue Uplink (alle 30 Sek) + +## Schnelltest + +Kopiere diesen **Ultra-Minimal-Decoder** zum Testen: + +```javascript +function decodeUplink(input) { + return { + data: { + distance_cm: ((input.bytes[0] << 8) | input.bytes[1]) / 10.0 + } + }; +} +``` + +Wenn dieser funktioniert, kannst du ihn später durch einen ausführlicheren ersetzen. + +## Support-Informationen sammeln + +Falls es immer noch nicht funktioniert, sammle diese Informationen: + +1. **ChirpStack Version:** + - Gehe zu ChirpStack → Klicke auf dein Logo/Avatar → "About" + - Notiere die Version (z.B. "ChirpStack v4.8.0") + +2. **Fehlermeldung:** + - Kopiere die komplette Fehlermeldung aus den Events + +3. **Device Profile Codec:** + - Screenshot von Device Profile → Codec Einstellungen + +4. **Payload:** + - Notiere die `frm_payload` aus den Events (z.B. "008b") + +5. **Serial Monitor:** + - Kopiere die Ausgabe vom Arduino Serial Monitor + +Mit diesen Informationen kann das Problem eingegrenzt werden. diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/KONFIGURATION_FUELLSTAND.md b/3_SENSOR_lorawan-ds18b20-local-lmic/KONFIGURATION_FUELLSTAND.md new file mode 100644 index 0000000..6ffbe16 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/KONFIGURATION_FUELLSTAND.md @@ -0,0 +1,177 @@ +# Konfiguration der Füllstandsmessung + +## Übersicht + +Der HC-SR04 Sensor wird **oberhalb des Tanks** montiert und misst die Distanz zur Flüssigkeitsoberfläche. + +## Konfigurationsvariablen + +In [src/main.cpp](src/main.cpp) (Zeile 14-16): + +```cpp +#define TANK_HEIGHT 180.0 // Tank-Höhe in cm (innen) +#define SENSOR_MOUNT_OFFSET 20.0 // Montageabstand: Sensor über Tank-Oberkante in cm +#define SENSOR_MIN_DISTANCE 2.0 // HC-SR04 Mindestabstand (technische Grenze) +``` + +## Parameter-Erklärung + +### 1. TANK_HEIGHT (Tank-Höhe) +**Die innere Höhe des Tanks von Boden bis Oberkante.** + +- **Beispiel**: Tank ist 180 cm hoch → `#define TANK_HEIGHT 180.0` +- **Wichtig**: Nur die innere Höhe, ohne Montageabstand! + +### 2. SENSOR_MOUNT_OFFSET (Montageabstand) +**Der Abstand vom Sensor bis zur Tank-Oberkante.** + +- **Beispiel**: Sensor ist 20 cm über dem Tank montiert → `#define SENSOR_MOUNT_OFFSET 20.0` +- **Warum nötig?**: Sensor kann nicht direkt am Tank befestigt werden, braucht Montageabstand + +### 3. SENSOR_MIN_DISTANCE (Mindestabstand) +**Technische Grenze des HC-SR04 Sensors (2 cm).** + +- **Standard**: 2.0 cm (nicht ändern!) +- **Zweck**: Warnung wenn Flüssigkeit zu nah am Sensor ist + +## Aufbau-Beispiel + +``` + ↑ + | + [ HC-SR04 ] | ← Sensor montiert an Decke/Halterung + | + 20 cm | ← SENSOR_MOUNT_OFFSET + | + ╔═══════════════╗ ← Tank-Oberkante + ║ ║ + ║ Flüssigkeit ║ + ║ ↕ ║ ← Füllstand (variabel) + ║ ║ + ║ 180 cm ║ ← TANK_HEIGHT + ║ ║ + ║ ║ + ╚═══════════════╝ ← Tank-Boden +``` + +## Berechnung + +### Leerer Tank: +``` +Gemessene Distanz = SENSOR_MOUNT_OFFSET + TANK_HEIGHT + = 20 cm + 180 cm = 200 cm +Füllhöhe = 0 cm +Füllstand = 0% +``` + +### Voller Tank: +``` +Gemessene Distanz = SENSOR_MOUNT_OFFSET + = 20 cm +Füllhöhe = 180 cm (= TANK_HEIGHT) +Füllstand = 100% +``` + +### Halb voller Tank: +``` +Gemessene Distanz = 110 cm +Füllhöhe = 200 cm - 110 cm = 90 cm +Füllstand = (90 / 180) × 100 = 50% +``` + +## Anpassung an Ihre Installation + +### Schritt 1: Tank-Höhe messen +Messen Sie die **innere Höhe** Ihres Tanks von Boden bis Oberkante. + +**Beispiel**: 180 cm → `#define TANK_HEIGHT 180.0` + +### Schritt 2: Montageabstand messen +Messen Sie den Abstand vom Sensor bis zur Tank-Oberkante. + +**Beispiel**: Sensor 20 cm über Tank → `#define SENSOR_MOUNT_OFFSET 20.0` + +### Schritt 3: Code hochladen und testen + +1. Bei **leerem Tank**: + - Display sollte **0%** anzeigen + - Serial Monitor zeigt Füllhöhe: 0 cm + +2. Bei **vollem Tank**: + - Display sollte **100%** anzeigen + - Serial Monitor zeigt Füllhöhe: 180 cm (= TANK_HEIGHT) + +3. Falls nicht korrekt: + - Überprüfen Sie TANK_HEIGHT und SENSOR_MOUNT_OFFSET + - Passen Sie die Werte an + +## Fehlerdiagnose + +### Problem: Füllstand zeigt negative Werte oder 0% bei halbvollem Tank + +**Ursache**: SENSOR_MOUNT_OFFSET oder TANK_HEIGHT falsch konfiguriert + +**Lösung**: +1. Messen Sie die tatsächliche Distanz bei leerem Tank +2. Berechnen Sie: `SENSOR_MOUNT_OFFSET + TANK_HEIGHT = gemessene Distanz` +3. Passen Sie die Werte entsprechend an + +### Problem: Füllstand zeigt über 100% + +**Ursache**: Tank ist voller als erwartet oder TANK_HEIGHT zu klein + +**Lösung**: +1. Überprüfen Sie TANK_HEIGHT +2. Code begrenzt automatisch auf max. 100% + +### Problem: "WARNUNG: Flüssigkeit zu nah am Sensor!" + +**Ursache**: Flüssigkeit ist näher als SENSOR_MOUNT_OFFSET + SENSOR_MIN_DISTANCE + +**Lösung**: +- Normal bei sehr vollem Tank (>95%) +- Sensor evtl. höher montieren wenn Warnung bei 100% auftritt + +## Beispiel-Konfigurationen + +### Kleiner Tank (100 cm, Sensor 10 cm darüber) +```cpp +#define TANK_HEIGHT 100.0 +#define SENSOR_MOUNT_OFFSET 10.0 +``` + +### Mittelgroßer Tank (180 cm, Sensor 20 cm darüber) +```cpp +#define TANK_HEIGHT 180.0 +#define SENSOR_MOUNT_OFFSET 20.0 +``` + +### Großer Tank (300 cm, Sensor 30 cm darüber) +```cpp +#define TANK_HEIGHT 300.0 +#define SENSOR_MOUNT_OFFSET 30.0 +``` + +## Kalibrierung + +### Methode 1: Bei leerem Tank +1. Entleeren Sie den Tank komplett +2. Laden Sie den Code hoch und notieren Sie die gemessene Distanz +3. Berechnen Sie: `TANK_HEIGHT = gemessene Distanz - SENSOR_MOUNT_OFFSET` + +### Methode 2: Bei vollem Tank +1. Füllen Sie den Tank komplett (bis Oberkante) +2. Laden Sie den Code hoch und notieren Sie die gemessene Distanz +3. Diese Distanz = SENSOR_MOUNT_OFFSET + +### Methode 3: Mit Maßband +1. Messen Sie TANK_HEIGHT mit Maßband (innere Höhe) +2. Messen Sie SENSOR_MOUNT_OFFSET mit Maßband (Sensor bis Tank-Oberkante) +3. Tragen Sie beide Werte ein + +## Genauigkeit + +- **HC-SR04 Genauigkeit**: ±0.3 cm +- **Messbereich**: 2 cm - 400 cm +- **Empfohlener Tank-Bereich**: 50 cm - 300 cm +- **Messintervall**: 30 Sekunden (konfigurierbar via `TX_INTERVAL`) diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/LORAWAN_KEYS.md b/3_SENSOR_lorawan-ds18b20-local-lmic/LORAWAN_KEYS.md new file mode 100644 index 0000000..d7940bb --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/LORAWAN_KEYS.md @@ -0,0 +1,267 @@ +# LoRaWAN Keys Konfiguration + +Dieses Dokument zeigt dir, wie die LoRaWAN Keys in ChirpStack und in der main.cpp eingetragen werden müssen. + +## Generierte Keys für dieses Projekt + +### Option 1: Zufällige Keys + +#### In ChirpStack / TTN eintragen (MSB Format): + +``` +DevEUI: 70B3D57ED005A1B2 +AppEUI: 70B3D57ED0000001 +AppKey: A1B2C3D4E5F6071829384756ABCDEF12 +``` + +#### In main.cpp eintragen (LSB Format): + +```cpp +// LoRaWAN Keys - WICHTIG: DevEUI und AppEUI in LSB Format! +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 }; +``` + +--- + +### Option 2: Einfache Keys (für Testing) + +#### In ChirpStack / TTN eintragen (MSB Format): + +``` +DevEUI: 0000000000000001 +AppEUI: 0000000000000001 +AppKey: 00112233445566778899AABBCCDDEEFF +``` + +#### In main.cpp eintragen (LSB Format): + +```cpp +// LoRaWAN Keys - WICHTIG: DevEUI und AppEUI in LSB Format! +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t PROGMEM DEVEUI[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; +``` + +--- + +## Wichtige Hinweise zur Konvertierung + +### DevEUI und AppEUI: LSB (Least Significant Byte First) + +**Die Byte-Reihenfolge muss umgedreht werden!** + +ChirpStack/TTN zeigt: `70B3D57ED005A1B2` +- Teile in Bytes: `70 B3 D5 7E D0 05 A1 B2` +- **Umdrehen:** `B2 A1 05 D0 7E D5 B3 70` +- In main.cpp: `{ 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }` + +### AppKey: MSB (Most Significant Byte First) + +**Die Byte-Reihenfolge bleibt gleich!** + +ChirpStack/TTN zeigt: `A1B2C3D4E5F6071829384756ABCDEF12` +- Teile in Bytes: `A1 B2 C3 D4 E5 F6 07 18 29 38 47 56 AB CD EF 12` +- **Nicht umdrehen!** +- In main.cpp: `{ 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 }` + +--- + +## Schritt-für-Schritt Anleitung + +### 1. ChirpStack Konfiguration + +#### Device Profile erstellen (falls noch nicht vorhanden) +1. Gehe zu **Device Profiles** → **Create** +2. Einstellungen: + - Name: `OTAA-EU868` + - Region: `EU868` (oder deine Region) + - MAC Version: `1.0.3` + - Regional Parameters Revision: `A` + - Supports OTAA: ✅ **aktiviert** + - Supports Class B: ❌ + - Supports Class C: ❌ + +#### Application erstellen +1. Gehe zu **Applications** → **Create** +2. Name: `HC-SR04-Sensors` (oder einen anderen Namen) + +#### Device hinzufügen +1. Öffne deine Application +2. Klicke auf **Create Device** +3. Fülle aus: + - **Device name:** `HC-SR04-Sensor-01` + - **Device description:** `Ultraschallsensor HC-SR04 mit TTGO LoRa32` + - **Device EUI:** `70B3D57ED005A1B2` (oder eine eigene) + - **Device profile:** Wähle dein OTAA Profil + +4. Nach dem Erstellen → Gehe zu **Keys (OTAA)** +5. Trage ein: + - **Application key:** `A1B2C3D4E5F6071829384756ABCDEF12` + - **Network key:** (leer lassen oder gleicher Wert für LoRaWAN 1.0.x) + +6. **Application EUI** wird automatisch von der Application übernommen oder kann manuell gesetzt werden: + - Nutze: `70B3D57ED0000001` + +--- + +### 2. The Things Network (TTN) Konfiguration + +#### Application erstellen +1. Gehe zu [console.thethingsnetwork.org](https://console.thethingsnetwork.org) +2. **Applications** → **Add application** +3. Application ID: `hc-sr04-sensors` + +#### Device registrieren +1. Öffne deine Application +2. **Register end device** +3. Einstellungen: + - **Activation mode:** Over the air activation (OTAA) + - **LoRaWAN version:** MAC V1.0.3 + - **Frequency plan:** Europe 863-870 MHz (SF9 for RX2) + +4. **Provisioning information:** + - **JoinEUI (AppEUI):** `70B3D57ED0000001` + - **DevEUI:** `70B3D57ED005A1B2` (oder generieren lassen) + - **AppKey:** `A1B2C3D4E5F6071829384756ABCDEF12` (oder generieren lassen) + +5. **End device ID:** `hc-sr04-sensor-01` + +6. **Register end device** + +--- + +### 3. main.cpp anpassen + +Öffne [src/main.cpp](src/main.cpp) und ersetze die Keys in Zeile 22-24: + +```cpp +// LoRaWAN Keys - Kopiere die umgewandelten Werte hier ein +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; // LSB! +static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; // LSB! +static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 }; // MSB +``` + +**Wichtig:** Achte darauf, dass DevEUI und AppEUI in **LSB** (umgedreht) sind! + +--- + +## Konvertierungs-Tool + +### Online-Konverter +Du kannst auch einen Hex-String online umkehren: +- [Hex String Reverser](https://www.browserling.com/tools/reverse-hex) +- Oder nutze Python: + +```python +def convert_to_lsb(hex_string): + # Entferne Leerzeichen und teile in Bytes + hex_clean = hex_string.replace(" ", "") + bytes_list = [hex_clean[i:i+2] for i in range(0, len(hex_clean), 2)] + # Umdrehen + bytes_reversed = bytes_list[::-1] + # Als C-Array formatieren + c_array = "{ " + ", ".join([f"0x{b}" for b in bytes_reversed]) + " }" + return c_array + +# Beispiel +deveui_msb = "70B3D57ED005A1B2" +print("DevEUI LSB:", convert_to_lsb(deveui_msb)) +# Ausgabe: { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 } +``` + +--- + +## Überprüfung + +### Im Serial Monitor +Nach dem Upload solltest du sehen: + +``` +Starte HC-SR04 + OLED + LoRa +Beitritt läuft... +EV_JOINING +``` + +Nach 1-10 Sekunden (wenn das Gateway erreichbar ist): +``` +EV_JOINED +Netzwerkbeitritt erfolgreich +Distanz gesendet: +Distanz: 123.4 cm +``` + +### In ChirpStack / TTN +1. Gehe zu deinem Device +2. Öffne **LoRaWAN frames** oder **Live data** +3. Du solltest **Join Request** und **Join Accept** sehen +4. Danach sollten **Uplink** Nachrichten mit den Sensordaten ankommen + +--- + +## Troubleshooting + +### "EV_JOINING" wiederholt sich, aber kein "EV_JOINED" + +**Mögliche Ursachen:** +1. ❌ **Keys falsch** - Überprüfe LSB/MSB Format +2. ❌ **Kein Gateway in Reichweite** +3. ❌ **Falsche Frequenz** - EU868 vs. US915 etc. +4. ❌ **Device nicht in ChirpStack/TTN registriert** + +**Lösung:** +- Überprüfe die Keys noch einmal +- Stelle sicher, dass DevEUI und AppEUI umgedreht sind (LSB) +- Prüfe, ob ein Gateway in der Nähe ist (ChirpStack → Gateways) + +### "EV_JOIN_FAILED" oder "EV_REJOIN_FAILED" + +**Ursache:** +- AppKey stimmt nicht überein + +**Lösung:** +- Überprüfe den AppKey in ChirpStack und main.cpp +- AppKey wird **nicht** umgedreht (MSB Format) + +--- + +## Zusammenfassung der Keys + +| Key | Format in ChirpStack/TTN | Format in main.cpp | Umdrehen? | +|-----|--------------------------|-------------------|-----------| +| DevEUI | `70B3D57ED005A1B2` | `{ 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }` | ✅ Ja (LSB) | +| AppEUI | `70B3D57ED0000001` | `{ 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }` | ✅ Ja (LSB) | +| AppKey | `A1B2C3D4E5F6071829384756ABCDEF12` | `{ 0xA1, 0xB2, 0xC3, 0xD4, ..., 0x12 }` | ❌ Nein (MSB) | + +--- + +## Eigene Keys generieren + +### Mit OpenSSL (Linux/Mac): +```bash +# AppKey (16 Bytes = 128 Bit) +openssl rand -hex 16 +``` + +### Mit Python: +```python +import secrets + +# AppKey (16 Bytes) +appkey = secrets.token_hex(16) +print(f"AppKey: {appkey.upper()}") + +# DevEUI (8 Bytes) - optional mit Prefix +deveui = "70B3D57E" + secrets.token_hex(4) +print(f"DevEUI: {deveui.upper()}") +``` + +### Online: +- [Random Hex Generator](https://www.random.org/bytes/) +- Wähle 16 Bytes für AppKey +- Wähle 8 Bytes für DevEUI/AppEUI + +--- + +**Tipp:** Speichere deine Keys sicher ab! Du brauchst sie, wenn du das Device neu flashen musst oder ein zweites Device hinzufügen möchtest. diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/README.md b/3_SENSOR_lorawan-ds18b20-local-lmic/README.md new file mode 100644 index 0000000..46ccffd --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/README.md @@ -0,0 +1,347 @@ +# HC-SR04 Ultraschallsensor mit LoRaWAN (LilyGO LoRa32 T3 V1.6.1) + +Dieses Projekt liest Distanzmessungen vom HC-SR04 Ultraschallsensor aus und sendet die Daten über LoRaWAN. Die Messwerte werden zusätzlich auf einem OLED-Display angezeigt. + +## Hardware-Komponenten + +- **LilyGO LoRa32 T3 V1.6.1** (ESP32 mit SX1276/SX1278 LoRa-Modul) +- **HC-SR04 Ultraschallsensor** +- **OLED Display 128x64** (I2C, SSD1306) + +## Anschlussplan + +### HC-SR04 Ultraschallsensor +``` +HC-SR04 LilyGO LoRa32 T3 V1.6.1 +-------- ----------------------- +VCC <--> 5V (Pin mit 5V-Label) +TRIG <--> GPIO 12 +ECHO <--> GPIO 13 +GND <--> GND +``` + +**Wichtig:** +- Der HC-SR04 sollte mit 5V betrieben werden für beste Ergebnisse +- Das ECHO-Signal gibt 5V aus! Um den ESP32 zu schützen, verwende einen **Spannungsteiler**: + - ECHO → 1kΩ Widerstand → GPIO 13 → 2kΩ Widerstand → GND + - Alternativ: Viele HC-SR04 Module haben bereits einen integrierten Spannungsregler +- GPIO 13 ist als INPUT sicher, solange das Signal nicht über 3.6V geht + +### OLED Display (SSD1306, 128x64, I2C) +``` +OLED Display LilyGO LoRa32 T3 V1.6.1 +------------ ----------------------- +VCC <--> 3.3V +GND <--> GND +SDA <--> GPIO 21 +SCL <--> GPIO 22 +``` + +**Hinweis:** Beim LilyGO LoRa32 T3 V1.6.1 sind die I2C-Pins standardmäßig: +- SDA: GPIO 21 +- SCL: GPIO 22 + +### LoRa-Modul (bereits auf Board integriert) +Das LoRa-Modul SX1276/SX1278 ist bereits auf dem LilyGO Board integriert und nutzt folgende Pins: +``` +LoRa SX1276 ESP32 +----------- ----- +NSS (CS) <--> GPIO 18 +RST <--> GPIO 23 +DIO0 <--> GPIO 26 +DIO1 <--> GPIO 33 +DIO2 <--> GPIO 32 +MOSI <--> GPIO 27 +MISO <--> GPIO 19 +SCK <--> GPIO 5 +``` + +## Pinout-Übersicht LilyGO LoRa32 T3 V1.6.1 + +``` + +------------------------+ + | LilyGO LoRa32 T3 V1.6 | + | (ESP32) | + +------------------------+ + | | + HC-SR04 TRIG -> | GPIO 12 GPIO 21| <- OLED SDA + HC-SR04 ECHO -> | GPIO 13 GPIO 22| <- OLED SCL + | GPIO 15 GPIO 23| <- LoRa RST + | GPIO 2 GPIO 5| <- LoRa SCK + | GPIO 4 GPIO 19| <- LoRa MISO + | GND GPIO 27| <- LoRa MOSI + | 3.3V GPIO 18| <- LoRa NSS + | 5V (VIN) GPIO 26| <- LoRa DIO0 + | GPIO 33| <- LoRa DIO1 + | GPIO 32| <- LoRa DIO2 + +------------------------+ +``` + +**Verfügbare freie GPIOs** (falls du weitere Sensoren anschließen möchtest): +- GPIO 2, 4, 15, 25, 34, 35, 36, 39 (36/39 nur Input) + +## LoRaWAN-Konfiguration + +### 1. The Things Network (TTN) / ChirpStack einrichten + +#### Application erstellen +1. Melde dich bei TTN oder deinem ChirpStack-Server an +2. Erstelle eine neue Application + +#### Device registrieren +1. Erstelle ein neues Device in deiner Application +2. Wähle **OTAA** (Over-The-Air Activation) +3. Notiere die folgenden Keys: + - **Device EUI (DevEUI)** - 8 Bytes + - **Application EUI (AppEUI)** - 8 Bytes + - **Application Key (AppKey)** - 16 Bytes + +### 2. Keys im Code eintragen + +Öffne die Datei [src/main.cpp](src/main.cpp) und trage deine Keys ein: + +```cpp +// LoRaWAN Keys (hier deine echten Werte eintragen) +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 }; +``` + +**Wichtig:** Die Keys müssen in **LSB (Least Significant Byte)** Format eingetragen werden! + +#### Beispiel: TTN Keys konvertieren + +Wenn TTN dir die Keys so anzeigt: +``` +DevEUI: 70B3D57ED005A1B2 +AppEUI: 0000000000000001 +AppKey: A1B2C3D4E5F6071829... +``` + +Dann musst du sie für LMIC **umkehren** (LSB): +```cpp +// DevEUI: 70B3D57ED005A1B2 -> LSB +static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; + +// AppEUI: 0000000000000001 -> LSB +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// AppKey: bleibt in MSB Format (wird nicht umgedreht) +static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, ... }; +``` + +### 3. Sendeintervall anpassen + +Das Sendeintervall kann in [src/main.cpp:32](src/main.cpp#L32) angepasst werden: + +```cpp +const unsigned TX_INTERVAL = 30; // Sendeintervall in Sekunden +``` + +## Payload-Format + +Der Sensor sendet die Distanz als **2 Bytes** (uint16_t): + +| Byte | Beschreibung | +|------|--------------| +| 0 | High Byte (Distanz in mm) | +| 1 | Low Byte (Distanz in mm) | + +**Beispiel:** +- Distanz: 123.4 cm = 1234 mm +- Hex: `0x04D2` +- Payload: `04 D2` + +**Messbereich:** 0 - 65535 mm (0 - 65.5 m) + +## ChirpStack Decoder + +Füge folgenden Decoder in ChirpStack unter **Device Profile → Codec** ein: + +### JavaScript Decoder (ChirpStack v3) + +```javascript +function Decode(fPort, bytes) { + // Decoder für HC-SR04 Ultraschallsensor + // Payload: 2 Bytes - Distanz in Millimeter (uint16_t) + + if (bytes.length !== 2) { + return { + error: "Invalid payload length" + }; + } + + // Distanz aus 2 Bytes (Big Endian) auslesen + var distanceMm = (bytes[0] << 8) | bytes[1]; + + // Umrechnung in verschiedene Einheiten + var distanceCm = distanceMm / 10.0; + var distanceM = distanceMm / 1000.0; + + return { + distance_mm: distanceMm, + distance_cm: distanceCm, + distance_m: distanceM + }; +} +``` + +### JavaScript Decoder (ChirpStack v4) + +```javascript +function decodeUplink(input) { + // Decoder für HC-SR04 Ultraschallsensor + // Payload: 2 Bytes - Distanz in Millimeter (uint16_t) + + var bytes = input.bytes; + + if (bytes.length !== 2) { + return { + errors: ["Invalid payload length"] + }; + } + + // Distanz aus 2 Bytes (Big Endian) auslesen + var distanceMm = (bytes[0] << 8) | bytes[1]; + + // Umrechnung in verschiedene Einheiten + var distanceCm = distanceMm / 10.0; + var distanceM = distanceMm / 1000.0; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceCm, + distance_m: distanceM + } + }; +} +``` + +### Decoder testen + +**Test-Payload:** `04 D2` (1234 mm) + +**Erwartetes Ergebnis:** +```json +{ + "distance_mm": 1234, + "distance_cm": 123.4, + "distance_m": 1.234 +} +``` + +## The Things Network (TTN) Payload Formatter + +Für TTN unter **Applications → Payload Formatters → Uplink**: + +```javascript +function decodeUplink(input) { + var bytes = input.bytes; + + if (bytes.length !== 2) { + return { + errors: ["Invalid payload length"] + }; + } + + // Distanz aus 2 Bytes (Big Endian) auslesen + var distanceMm = (bytes[0] << 8) | bytes[1]; + + return { + data: { + distance_mm: distanceMm, + distance_cm: distanceMm / 10.0, + distance_m: distanceMm / 1000.0 + } + }; +} +``` + +## Installation und Verwendung + +### Voraussetzungen +- PlatformIO (VS Code Extension oder CLI) +- USB-Treiber für CP2102 (falls nicht installiert) + +### Projekt kompilieren und hochladen + +```bash +# Projekt bauen +pio run + +# Auf Board hochladen +pio run --target upload + +# Serial Monitor öffnen +pio device monitor +``` + +### Serial Monitor Ausgabe + +Nach dem Upload solltest du folgende Ausgabe sehen: + +``` +Starte HC-SR04 + OLED + LoRa +Beitritt läuft... +Netzwerkbeitritt erfolgreich +Distanz gesendet: +Distanz: 123.4 cm +Distanz: 1234.0 mm +Nachricht übertragen. +``` + +## Bibliotheken + +Die benötigten Bibliotheken werden automatisch über PlatformIO installiert (siehe [platformio.ini](platformio.ini)): + +- `gamegine/HCSR04 ultrasonic sensor` - HC-SR04 Treiber +- `adafruit/Adafruit SSD1306` - OLED Display +- `adafruit/Adafruit GFX Library` - Grafik-Bibliothek +- `mcci-catena/MCCI LoRaWAN LMIC library` - LoRaWAN Stack (wird automatisch vom Framework geladen) + +## Fehlerbehebung + +### Device joint nicht +- Überprüfe die Keys (DevEUI, AppEUI, AppKey) +- Achte auf LSB-Format bei DevEUI und AppEUI +- Prüfe die LoRaWAN-Frequenz (EU868, US915, etc.) +- Stelle sicher, dass ein Gateway in Reichweite ist + +### HC-SR04 liefert -1 oder fehlerhafte Werte +- Überprüfe die Verkabelung (TRIG → GPIO 15, ECHO → GPIO 13) +- Prüfe die Stromversorgung (min. 5V für HC-SR04 empfohlen) +- Halte Hindernisse im Messbereich (2 cm - 4 m) +- Verwende ggf. einen Spannungsteiler für ECHO (5V → 3.3V) + +### OLED Display zeigt nichts an +- Überprüfe die I2C-Verbindung (SDA → GPIO 21, SCL → GPIO 22) +- Prüfe die I2C-Adresse (Standard: 0x3C) +- Stelle sicher, dass die richtigen I2C-Pins verwendet werden (LilyGO T3 V1.6.1 nutzt GPIO 21/22) +- Verwende einen I2C-Scanner zur Diagnose + +### Kompilierungsfehler +```bash +# Cache löschen und neu bauen +pio run --target clean +pio run +``` + +## Energieverbrauch + +- **Deep Sleep:** Nicht implementiert (kontinuierlicher Betrieb) +- **Stromverbrauch:** ~100-150 mA (ESP32 + LoRa TX) +- **Empfehlung:** Für batteriebetriebenen Einsatz Deep Sleep zwischen Messungen implementieren + +## Lizenz + +Dieses Projekt ist Open Source und steht unter der MIT-Lizenz. + +## Weitere Informationen + +- [LilyGO LoRa32 Hardware Repository](https://github.com/Xinyuan-LilyGO/LilyGO-LoRa-Series) +- [LilyGO T3 V1.6.1 Pinout](https://github.com/Xinyuan-LilyGO/LilyGO-LoRa-Series/blob/master/schematic/T3_V1.6.1.pdf) +- [The Things Network](https://www.thethingsnetwork.org/) +- [ChirpStack Dokumentation](https://www.chirpstack.io/) +- [MCCI LoRaWAN LMIC](https://github.com/mcci-catena/arduino-lmic) +- [HC-SR04 Datasheet](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/ideetron/AES-128_V10.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/ideetron/AES-128_V10.cpp new file mode 100644 index 0000000..d52623d --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/ideetron/AES-128_V10.cpp @@ -0,0 +1,348 @@ +/****************************************************************************************** +#if defined(USE_IDEETRON_AES) +* Copyright 2015, 2016 Ideetron B.V. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +******************************************************************************************/ +/****************************************************************************************** +* +* File: AES-128_V10.cpp +* Author: Gerben den Hartog +* Compagny: Ideetron B.V. +* Website: http://www.ideetron.nl/LoRa +* E-mail: info@ideetron.nl +******************************************************************************************/ +/**************************************************************************************** +* +* Created on: 20-10-2015 +* Supported Hardware: ID150119-02 Nexus board with RFM95 +* +* Firmware Version 1.0 +* First version +****************************************************************************************/ + +// This file was taken from +// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for +// use with LMIC. It was only cosmetically modified: +// - AES_Encrypt was renamed to lmic_aes_encrypt. +// - All other functions and variables were made static +// - Tabs were converted to 2 spaces +// - An #include and #if guard was added +// - S_Table is now stored in PROGMEM + +#include "../../lmic/oslmic.h" + +#if defined(USE_IDEETRON_AES) + +/* +******************************************************************************************** +* Global Variables +******************************************************************************************** +*/ + +static unsigned char State[4][4]; + +static CONST_TABLE(unsigned char, S_Table)[16][16] = { + {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, + {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, + {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, + {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, + {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, + {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, + {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, + {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, + {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, + {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, + {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, + {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, + {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, + {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, + {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, + {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} +}; + +#ifdef __cplusplus +extern "C" { +#endif + void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); +#ifdef __cplusplus +} +#endif + +static void AES_Add_Round_Key(unsigned char *Round_Key); +static unsigned char AES_Sub_Byte(unsigned char Byte); +static void AES_Shift_Rows(); +static void AES_Mix_Collums(); +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); + +/* +***************************************************************************************** +* Description : Function for encrypting data using AES-128 +* +* Arguments : *Data Data to encrypt is a 16 byte long arry +* *Key Key to encrypt data with is a 16 byte long arry +***************************************************************************************** +*/ +void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) +{ + unsigned char i; + unsigned char Row,Collum; + unsigned char Round = 0x00; + unsigned char Round_Key[16]; + + //Copy input to State arry + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = Data[Row + (4*Collum)]; + } + } + + //Copy key to round key + for(i = 0; i < 16; i++) + { + Round_Key[i] = Key[i]; + } + + //Add round key + AES_Add_Round_Key(Round_Key); + + //Preform 9 full rounds + for(Round = 1; Round < 10; Round++) + { + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Preform Row Shift + AES_Shift_Rows(); + + //Mix Collums + AES_Mix_Collums(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round key + AES_Add_Round_Key(Round_Key); + } + + //Last round whitout mix collums + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Shift rows + AES_Shift_Rows(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round Key + AES_Add_Round_Key(Round_Key); + + //Copy the State into the data array + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + Data[Row + (4*Collum)] = State[Row][Collum]; + } + } + +} + +/* +***************************************************************************************** +* Description : Function that add's the round key for the current round +* +* Arguments : *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Add_Round_Key(unsigned char *Round_Key) +{ + unsigned char Row,Collum; + + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; + } + } +} + +/* +***************************************************************************************** +* Description : Function that substitutes a byte with a byte from the S_Table +* +* Arguments : Byte The byte that will be substituted +* +* Return : The return is the found byte in the S_Table +***************************************************************************************** +*/ +static unsigned char AES_Sub_Byte(unsigned char Byte) +{ + unsigned char S_Row,S_Collum; + unsigned char S_Byte; + + //Split byte up in Row and Collum + S_Row = ((Byte >> 4) & 0x0F); + S_Collum = (Byte & 0x0F); + + //Find the correct byte in the S_Table + S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); + + return S_Byte; +} + +/* +***************************************************************************************** +* Description : Function that preforms the shift row operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Shift_Rows() +{ + unsigned char Buffer; + + //Row 0 doesn't change + + //Shift Row 1 one left + //Store firt byte in buffer + Buffer = State[1][0]; + //Shift all bytes + State[1][0] = State[1][1]; + State[1][1] = State[1][2]; + State[1][2] = State[1][3]; + State[1][3] = Buffer; + + //Shift row 2 two left + Buffer = State[2][0]; + State[2][0] = State[2][2]; + State[2][2] = Buffer; + Buffer = State[2][1]; + State[2][1] = State[2][3]; + State[2][3] = Buffer; + + //Shift row 3 three left + Buffer = State[3][3]; + State[3][3] = State[3][2]; + State[3][2] = State[3][1]; + State[3][1] = State[3][0]; + State[3][0] = Buffer; +} + +/* +***************************************************************************************** +* Description : Function that preforms the Mix Collums operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Mix_Collums() +{ + unsigned char Row,Collum; + unsigned char a[4], b[4]; + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + a[Row] = State[Row][Collum]; + b[Row] = (State[Row][Collum] << 1); + + if((State[Row][Collum] & 0x80) == 0x80) + { + b[Row] = b[Row] ^ 0x1B; + } + } + State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; + State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; + State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; + State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; + } +} + +/* +***************************************************************************************** +* Description : Function that calculaties the round key for the current round +* +* Arguments : Round Number of current Round +* *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) +{ + unsigned char i,j; + unsigned char b; + unsigned char Temp[4]; + unsigned char Buffer; + unsigned char Rcon; + + //Calculate first Temp + //Copy laste byte from previous key + for(i = 0; i < 4; i++) + { + Temp[i] = Round_Key[i+12]; + } + + //Rotate Temp + Buffer = Temp[0]; + Temp[0] = Temp[1]; + Temp[1] = Temp[2]; + Temp[2] = Temp[3]; + Temp[3] = Buffer; + + //Substitute Temp + for(i = 0; i < 4; i++) + { + Temp[i] = AES_Sub_Byte(Temp[i]); + } + + //Calculate Rcon + Rcon = 0x01; + while(Round != 1) + { + b = Rcon & 0x80; + Rcon = Rcon << 1; + if(b == 0x80) + { + Rcon = Rcon ^ 0x1b; + } + Round--; + } + + //XOR Rcon + Temp[0] = Temp[0] ^ Rcon; + + //Calculate new key + for(i = 0; i < 4; i++) + { + for(j = 0; j < 4; j++) + { + Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; + Temp[j] = Round_Key[j + (4*i)]; + } + } +} + +#endif // defined(USE_IDEETRON_AES) diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/lmic.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/lmic.c new file mode 100644 index 0000000..2a5bca3 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/lmic.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../lmic/oslmic.h" + +#if defined(USE_ORIGINAL_AES) + +#define AES_MICSUB 0x30 // internal use only + +static CONST_TABLE(u4_t, AES_RCON)[10] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 +}; + +static CONST_TABLE(u1_t, AES_S)[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static CONST_TABLE(u4_t, AES_E1)[256] = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, + 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, + 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, + 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, + 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, + 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, + 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, + 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, + 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, + 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, + 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, + 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, + 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, + 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, + 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, + 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, + 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, + 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, +}; + +static CONST_TABLE(u4_t, AES_E2)[256] = { + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, + 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, + 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, + 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, + 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, + 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, + 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, + 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, + 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, + 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, + 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, + 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, + 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, + 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, + 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, + 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, + 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, + 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, + 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, + 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, + 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, + 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, + 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, + 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, + 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, + 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, +}; + +static CONST_TABLE(u4_t, AES_E3)[256] = { + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, + 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, + 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, + 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, + 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, + 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, + 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, + 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, + 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, + 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, + 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, + 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, + 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, + 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, + 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, + 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, + 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, + 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, + 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, + 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, + 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, + 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, + 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, + 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, + 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, + 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, +}; + +static CONST_TABLE(u4_t, AES_E4)[256] = { + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, +}; + +#define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3]) +#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v) +#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) ) + +#define u1(v) ((u1_t)(v)) + +#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \ + r2 = ki[i+2]; \ + r3 = ki[i+3]; \ + r0 = ki[i] + +#define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \ + r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \ + r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \ + r0 ^= TABLE_GET_U4(AES_E1, (i>>24)) + +#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \ + a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) ) + +// global area for passing parameters (aux, key) and for storing round keys +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[11*16/sizeof(u4_t)]; + +// generate 1+10 roundkeys for encryption with 128-bit key +// read 128-bit key from AESKEY in MSBF, generate roundkey words in place +static void aesroundkeys () { + int i; + u4_t b; + + for( i=0; i<4; i++) { + AESKEY[i] = swapmsbf(AESKEY[i]); + } + + b = AESKEY[3]; + for( ; i<44; i++ ) { + if( i%4==0 ) { + // b = SubWord(RotWord(b)) xor Rcon[i/4] + b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^ + ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^ + TABLE_GET_U4(AES_RCON, (i-4)/4); + } + AESKEY[i] = b ^= AESKEY[i-4]; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + + aesroundkeys(); + + if( mode & AES_MICNOAUX ) { + AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0; + } else { + AESAUX[0] = swapmsbf(AESAUX[0]); + AESAUX[1] = swapmsbf(AESAUX[1]); + AESAUX[2] = swapmsbf(AESAUX[2]); + AESAUX[3] = swapmsbf(AESAUX[3]); + } + + while( (signed char)len > 0 ) { + u4_t a0, a1, a2, a3; + u4_t t0, t1, t2, t3; + u4_t *ki, *ke; + + // load input block + if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block + a0 = AESAUX[0]; + a1 = AESAUX[1]; + a2 = AESAUX[2]; + a3 = AESAUX[3]; + } + else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block + a0 = a1 = a2 = a3 = 0; // load null block + mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2 + } else + LOADDATA: { // load data block (partially) + for(t0=0; t0<16; t0++) { + t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block + do { + // compute CMAC subkey K1 and K2 + t0 = a0 >> 31; // save MSB + a0 = (a0 << 1) | (a1 >> 31); + a1 = (a1 << 1) | (a2 >> 31); + a2 = (a2 << 1) | (a3 >> 31); + a3 = (a3 << 1); + if( t0 ) a3 ^= 0x87; + } while( --t1 ); + + AESAUX[0] ^= a0; + AESAUX[1] ^= a1; + AESAUX[2] ^= a2; + AESAUX[3] ^= a3; + mode &= ~AES_MICSUB; + goto LOADDATA; + } else { + // save cipher block as new iv + AESAUX[0] = a0; + AESAUX[1] = a1; + AESAUX[2] = a2; + AESAUX[3] = a3; + } + } else { // CIPHER + if( mode & AES_CTR ) { // xor block (partially) + t0 = (len > 16) ? 16: len; + for(t1=0; t1>24); + a0 <<= 8; + if((t1&3)==3) { + a0 = a1; + a1 = a2; + a2 = a3; + } + } + // update counter + AESAUX[3]++; + } else { // ECB + // store block + msbf4_write(buf+0, a0); + msbf4_write(buf+4, a1); + msbf4_write(buf+8, a2); + msbf4_write(buf+12, a3); + } + } + + // update block state + if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) { + buf += 16; + len -= 16; + } + mode |= AES_MICNOAUX; + } + return AESAUX[0]; +} + +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/other.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/other.c new file mode 100644 index 0000000..7093fb4 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/aes/other.c @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2016 Matthijs Kooijman + * + * LICENSE + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and + * redistribution. + * + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ + +/* + * The original LMIC AES implementation integrates raw AES encryption + * with CMAC and AES-CTR in a single piece of code. Most other AES + * implementations (only) offer raw single block AES encryption, so this + * file contains an implementation of CMAC and AES-CTR, and offers the + * same API through the os_aes() function as the original AES + * implementation. This file assumes that there is an encryption + * function available with this signature: + * + * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); + * + * That takes a single 16-byte buffer and encrypts it wit the given + * 16-byte key. + */ + +#include "../lmic/oslmic.h" + +#if !defined(USE_ORIGINAL_AES) + +// This should be defined elsewhere +void lmic_aes_encrypt(u1_t *data, u1_t *key); + +// global area for passing parameters (aux, key) +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[16/sizeof(u4_t)]; + +// Shift the given buffer left one bit +static void shift_left(xref2u1_t buf, u1_t len) { + while (len--) { + u1_t next = len ? buf[1] : 0; + + u1_t val = (*buf << 1); + if (next & 0x80) + val |= 1; + *buf++ = val; + } +} + +// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, +// AESAUX is prepended to the message. AESAUX is used as working memory +// in any case. The CMAC result is returned in AESAUX as well. +static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { + if (prepend_aux) + lmic_aes_encrypt(AESaux, AESkey); + else + memset (AESaux, 0, 16); + + while (len > 0) { + u1_t need_padding = 0; + for (u1_t i = 0; i < 16; ++i, ++buf, --len) { + if (len == 0) { + // The message is padded with 0x80 and then zeroes. + // Since zeroes are no-op for xor, we can just skip them + // and leave AESAUX unchanged for them. + AESaux[i] ^= 0x80; + need_padding = 1; + break; + } + AESaux[i] ^= *buf; + } + + if (len == 0) { + // Final block, xor with K1 or K2. K1 and K2 are calculated + // by encrypting the all-zeroes block and then applying some + // shifts and xor on that. + u1_t final_key[16]; + memset(final_key, 0, sizeof(final_key)); + lmic_aes_encrypt(final_key, AESkey); + + // Calculate K1 + u1_t msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + + // If the final block was not complete, calculate K2 from K1 + if (need_padding) { + msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + } + + // Xor with K1 or K2 + for (u1_t i = 0; i < sizeof(final_key); ++i) + AESaux[i] ^= final_key[i]; + } + + lmic_aes_encrypt(AESaux, AESkey); + } +} + +// Run AES-CTR using the key in AESKEY and using AESAUX as the +// counter block. The last byte of the counter block will be incremented +// for every block. The given buffer will be encrypted in place. +static void os_aes_ctr (xref2u1_t buf, u2_t len) { + u1_t ctr[16]; + while (len) { + // Encrypt the counter block with the selected key + memcpy(ctr, AESaux, sizeof(ctr)); + lmic_aes_encrypt(ctr, AESkey); + + // Xor the payload with the resulting ciphertext + for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) + *buf ^= ctr[i]; + + // Increment the block index byte + AESaux[15]++; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + switch (mode & ~AES_MICNOAUX) { + case AES_MIC: + os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); + return os_rmsbf4(AESaux); + + case AES_ENC: + // TODO: Check / handle when len is not a multiple of 16 + for (u1_t i = 0; i < len; i += 16) + lmic_aes_encrypt(buf+i, AESkey); + break; + + case AES_CTR: + os_aes_ctr(buf, len); + break; + } + return 0; +} + +#endif // !defined(USE_ORIGINAL_AES) diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic.h new file mode 100644 index 0000000..4e8d953 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic.h @@ -0,0 +1,34 @@ +/* + +Module: arduino_lmic.h + +Function: + Arduino-LMIC C++ top-level include file + +Copyright & License: + See accompanying LICENSE file. + +Author: + Matthijs Kooijman 2015 + Terry Moore, MCCI November 2018 + +*/ + +#pragma once + +#ifndef _ARDUINO_LMIC_H_ +# define _ARDUINO_LMIC_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lmic.h" +#include "lmic/lmic_bandplan.h" +#include "lmic/lmic_util.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _ARDUINO_LMIC_H_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_boards.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_boards.h new file mode 100644 index 0000000..4fc94c6 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_boards.h @@ -0,0 +1,47 @@ +/* + +Module: arduino_lmic_hal_boards.h + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#pragma once + +#ifndef _arduino_lmic_hal_boards_h_ +# define _arduino_lmic_hal_boards_h_ + +#include "arduino_lmic_hal_configuration.h" + +namespace Arduino_LMIC { + +const HalPinmap_t *GetPinmap_FeatherM0LoRa(); +const HalPinmap_t *GetPinmap_Feather32U4LoRa(); + +const HalPinmap_t *GetPinmap_Catena4420(); +const HalPinmap_t *GetPinmap_Catena4551(); +const HalPinmap_t *GetPinmap_Catena4610(); +const HalPinmap_t *GetPinmap_Catena4610(); +const HalPinmap_t *GetPinmap_Catena4611(); +const HalPinmap_t *GetPinmap_Catena4612(); +const HalPinmap_t *GetPinmap_Catena4617(); +const HalPinmap_t *GetPinmap_Catena4618(); +const HalPinmap_t *GetPinmap_Catena4630(); +const HalPinmap_t *GetPinmap_Catena4801(); +const HalPinmap_t *GetPinmap_Catena4802(); +const HalPinmap_t* GetPinmap_ttgo_lora32_v1(); +const HalPinmap_t* GetPinmap_heltec_lora32(); +const HalPinmap_t* GetPinmap_Disco_L072cz_Lrwan1(); + +const HalPinmap_t *GetPinmap_ThisBoard(); + +}; /* namespace Arduino_LIMC */ + +#endif /* _arduino_lmic_hal_boards_h_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_configuration.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_configuration.h new file mode 100644 index 0000000..2350f4a --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_hal_configuration.h @@ -0,0 +1,117 @@ +/* + +Module: arduino_lmic_hal_configuration.h + +Function: + Arduino-LMIC C++ HAL configuration APIs + +Copyright & License: + See accompanying LICENSE file. + +Author: + Matthijs Kooijman 2015 + Terry Moore, MCCI November 2018 + +*/ +#pragma once + +#ifndef _arduino_lmic_hal_configuration_h_ +# define _arduino_lmic_hal_configuration_h_ + +#include +#include "lmic/lmic_env.h" + +namespace Arduino_LMIC { + +/* these types should match the types used by the LMIC */ +typedef int32_t ostime_t; + +// this type is used when we need to represent a threee-state signal +enum class ThreeState_t : uint8_t { + Off = 0, + On = 1, + HiZ = 2 +}; + +// forward reference +class HalConfiguration_t; + +// +// for legacy reasons, we need a plain-old-data C-like +// structure that defines the "pin mapping" for the +// common pins. Many clients initialize an instance of +// this structure using named-field initialization. +// +// Be careful of alignment below. +struct HalPinmap_t { + // Use this for any unused pins. + static constexpr uint8_t UNUSED_PIN = 0xff; + static constexpr int NUM_DIO = 3; + // for backward compatibility... + static constexpr uint8_t LMIC_UNUSED_PIN = UNUSED_PIN; + + /* the contents */ + uint8_t nss; // byte 0: pin for select + uint8_t rxtx; // byte 1: pin for rx/tx control + uint8_t rst; // byte 2: pin for reset + uint8_t dio[NUM_DIO]; // bytes 3..5: pins for DIO0, DOI1, DIO2 + // true if we must set rxtx for rx_active, false for tx_active + uint8_t rxtx_rx_active; // byte 6: polarity of rxtx active + int8_t rssi_cal; // byte 7: cal in dB -- added to RSSI + // measured prior to decision. + // Must include noise guardband! + uint32_t spi_freq; // bytes 8..11: SPI freq in Hz. + + // optional pointer to configuration object (bytes 12..15) + HalConfiguration_t *pConfig; + }; + +class HalConfiguration_t + { +public: + HalConfiguration_t() {}; + + // these must match the constants in radio.c + enum class TxPowerPolicy_t : uint8_t + { + RFO, + PA_BOOST, + PA_BOOST_20dBm + }; + + virtual ostime_t setModuleActive(bool state) { + LMIC_API_PARAMETER(state); + + // by default, if not overridden, do nothing + // and return 0 to indicate that the caller + // need not delay. + return 0; + } + + virtual void begin(void) {} + virtual void end(void) {} + virtual bool queryUsingTcxo(void) { return false; } + + // compute desired transmit power policy. HopeRF needs + // (and previous versions of this library always chose) + // PA_BOOST mode. So that's our default. Override this + // for the Murata module. + virtual TxPowerPolicy_t getTxPowerPolicy( + TxPowerPolicy_t policy, + int8_t requestedPower, + uint32_t frequency + ) + { + LMIC_API_PARAMETER(policy); + LMIC_API_PARAMETER(requestedPower); + LMIC_API_PARAMETER(frequency); + // default: use PA_BOOST exclusively + return TxPowerPolicy_t::PA_BOOST; + } + }; + +bool hal_init_with_pinmap(const HalPinmap_t *pPinmap); + +}; // end namespace Arduino_LMIC + +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_lorawan_compliance.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_lorawan_compliance.h new file mode 100644 index 0000000..ceedc1b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_lorawan_compliance.h @@ -0,0 +1,31 @@ +/* + +Module: arduino_lmic_lorawan_compliance.h + +Function: + Arduino-LMIC C++ include file for LoRaWAN compliance + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI March 2019 + +*/ + +#pragma once + +#ifndef _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ +# define _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lorawan_spec_compliance.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _ARDUINO_LMIC_LORAWAN_COMPLIANCE_H_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_user_configuration.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_user_configuration.h new file mode 100644 index 0000000..c7c9576 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/arduino_lmic_user_configuration.h @@ -0,0 +1,30 @@ +/* + +Module: arduino_lmic_user_configuration.h + +Function: + Get the Arduino-LMIC configuration into scope + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ +#pragma once + +#ifndef _arduino_lmic_user_configuration_h_ +# define _arduino_lmic_user_configuration_h_ + +# ifdef __cplusplus +extern "C" { +# endif + +# include "lmic/lmic_config_preconditions.h" + +# ifdef __cplusplus +} +# endif + +#endif /* _arduino_lmic_user_configuration_h_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4420.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4420.cpp new file mode 100644 index 0000000..ae833b0 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4420.cpp @@ -0,0 +1,74 @@ +/* + +Module: getconfig_catena4420.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for MCCI Catena 4420 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4420_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 6, + PIN_SX1276_NRESET = 5, + PIN_SX1276_DIO0 = 12, // pin assignment for DIO0 (aka IRQ) + PIN_SX1276_DIO1 = 11, + PIN_SX1276_DIO2 = 10, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_Catena4420_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4420_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4420_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4420_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4420_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4420_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4420_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4420(void) + { + return &myPinmap; + } + +} // namespace Arduino_LMIC; \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4551.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4551.cpp new file mode 100644 index 0000000..817dc77 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4551.cpp @@ -0,0 +1,84 @@ +/* + +Module: getconfig_catena4551.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4551) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4551) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4551_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // On the 4551, we can't control the TCXO; it's always on. + // So we use the default. + // virtual ostime_t setModuleActive(bool state) override + virtual bool queryUsingTcxo(void) override { return true; }; + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4551_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4551_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4551_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4551_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4551_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4551_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4551_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig, + }; + +const HalPinmap_t *GetPinmap_Catena4551(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4551) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4610.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4610.cpp new file mode 100644 index 0000000..54e2a56 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4610.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4610.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for the Catena 4610 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4610) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4610_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4610_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4610_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4610_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4610_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4610_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4610_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4610_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4610(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4610 */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4611.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4611.cpp new file mode 100644 index 0000000..fbab281 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4611.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4611.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4611) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4611_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4611_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4611_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4611_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4611_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4611_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4611_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4611_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4611(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4611) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4612.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4612.cpp new file mode 100644 index 0000000..9fdb3bd --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4612.cpp @@ -0,0 +1,103 @@ +/* + +Module: getconfig_catena4612.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4612) || \ + /* legacy name */ \ + defined(ARDUINO_CATENA_4612) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4612_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4612_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4612_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4612_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4612_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4612_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4612_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4612_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4612(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4612) || defined(ARDUINO_CATENA_4612) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4617.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4617.cpp new file mode 100644 index 0000000..e3857a2 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4617.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4617.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Lakshmi Priya Natarajan, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4617) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4617_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4617_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4617_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4617_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4617_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4617_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4617_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4617_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4617(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4617) || defined(ARDUINO_CATENA_4617) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4618.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4618.cpp new file mode 100644 index 0000000..9e0c449 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4618.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4618.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Lakshmi Priya Natarajan, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4618) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4618_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4618_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4618_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4618_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4618_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4618_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4618_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4618_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4618(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4618) || defined(ARDUINO_CATENA_4618) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4630.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4630.cpp new file mode 100644 index 0000000..0ee0b27 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4630.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4630.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Dhinesh Kumar Pitchai, MCCI June 2019 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4630) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4630_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + static constexpr ostime_t TCXO_DELAY_MS = 5; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(TCXO_DELAY_MS); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4630_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4630_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4630_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4630_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4630_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4630_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4630_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4630(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_MCCI_CATENA_4630) || defined(ARDUINO_CATENA_4630) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4801.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4801.cpp new file mode 100644 index 0000000..eeeb59b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4801.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4801.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4801) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4801) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4801_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(3); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4801_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4801_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4801_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4801_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4801_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4801_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4801_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4801(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_CATENA_4611) || defined(ARDUINO_CATENA_4801) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4802.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4802.cpp new file mode 100644 index 0000000..ef9d8d6 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_catena4802.cpp @@ -0,0 +1,101 @@ +/* + +Module: getconfig_catena4802.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for various boards + +Copyright & License: + See accompanying LICENSE file. + +Author: + Dhinesh Kumar Pitchai, MCCI November 2020 + +*/ + +#if defined(ARDUINO_MCCI_CATENA_4802) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4802) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Catena4802_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = D7, + PIN_SX1276_NRESET = D8, + PIN_SX1276_DIO0 = D25, + PIN_SX1276_DIO1 = D26, + PIN_SX1276_DIO2 = D27, + PIN_SX1276_ANT_SWITCH_RX = D29, + PIN_SX1276_ANT_SWITCH_TX_BOOST = D30, + PIN_SX1276_ANT_SWITCH_TX_RFO = D31, + PIN_VDD_BOOST_ENABLE = A0, + PIN_TCXO_VDD = D33, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, OUTPUT); + } + + virtual void end(void) override + { + digitalWrite(PIN_TCXO_VDD, 0); + pinMode(PIN_TCXO_VDD, INPUT); + } + + virtual bool queryUsingTcxo(void) override { return true; }; + + virtual ostime_t setModuleActive(bool state) override + { + ostime_t result; + const int oldState = digitalRead(PIN_TCXO_VDD); + + // if turning on, we need to delay. + result = 0; + if (state && ! oldState) + result = ms2osticksCeil(3); + + if (state != oldState) + digitalWrite(PIN_TCXO_VDD, state); + + return result; + } + }; + +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Catena4802_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Catena4802_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Catena4802_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Catena4802_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Catena4802_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Catena4802_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Catena4802_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_Catena4802(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_CATENA_4611) || defined(ARDUINO_CATENA_4802) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_disco_l072cs_lrwan1.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_disco_l072cs_lrwan1.cpp new file mode 100644 index 0000000..576b915 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_disco_l072cs_lrwan1.cpp @@ -0,0 +1,66 @@ +/* + +Module: getpinmap_disco_l072cz_lrwan1.cpp + +Function: + Arduino-LMIC C++ HAL pinmaps for the Discovery L072CZ LRWAN1 + +Copyright & License: + See accompanying LICENSE file. + +Author: + Helium February 2020 + +*/ + +#if defined(ARDUINO_DISCO_L072CZ_LRWAN1) + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + + class HalConfiguration_Disco_L072cz_Lrwan1_t : public HalConfiguration_t + { + public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 37, + PIN_SX1276_NRESET = 33, + PIN_SX1276_DIO0 = 38, + PIN_SX1276_DIO1 = 39, + PIN_SX1276_DIO2 = 40, + PIN_SX1276_RXTX = 21, + }; + + virtual bool queryUsingTcxo(void) override { return false; }; + }; + // save some typing by bringing the pin numbers into scope + static HalConfiguration_Disco_L072cz_Lrwan1_t myConfig; + + static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NSS, + .rxtx = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_RXTX, + .rst = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NRESET, + + .dio = {HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO0, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO1, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO2, + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + + const HalPinmap_t *GetPinmap_Disco_L072cz_Lrwan1(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC + +#endif /* defined(ARDUINO_DISCO_L072CZ_LRWAN1) */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_feather32u4lora.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_feather32u4lora.cpp new file mode 100644 index 0000000..c852eab --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_feather32u4lora.cpp @@ -0,0 +1,79 @@ +/* + +Module: getconfig_feather32u4lora.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Adafruit Feather 32U4 LoRa + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_Feather32U4LoRa_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 8, + PIN_SX1276_NRESET = 4, + PIN_SX1276_DIO0 = 7, + PIN_SX1276_DIO1 = 6, + PIN_SX1276_DIO2 = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_Feather32U4LoRa_t myConfig; + +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_Feather32U4LoRa_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 8, + .spi_freq = 1000000, /* 1MHz */ + .pConfig = &myConfig, + }; + +const HalPinmap_t *GetPinmap_Feather32U4LoRa(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_featherm0lora.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_featherm0lora.cpp new file mode 100644 index 0000000..7d221dc --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_featherm0lora.cpp @@ -0,0 +1,74 @@ +/* + +Module: getconfig_featherm0lora.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Adafruit Feather M0 LoRa + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +namespace Arduino_LMIC { + +class HalConfiguration_FeatherM0LoRa_t : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 8, + PIN_SX1276_NRESET = 4, + PIN_SX1276_DIO0 = 3, + PIN_SX1276_DIO1 = 6, + PIN_SX1276_DIO2 = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_FeatherM0LoRa_t myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_FeatherM0LoRa_t::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t *GetPinmap_FeatherM0LoRa(void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_heltec_lora32.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_heltec_lora32.cpp new file mode 100644 index 0000000..897907a --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_heltec_lora32.cpp @@ -0,0 +1,80 @@ +/* + +Module: getpinmap_heltec_lora32.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for Heltec WiFi LoRa 32 (V1 & V2) and Heltec Wireless Stick + +Copyright & License: + See accompanying LICENSE file. + +Author: + Manuel Bleichenbacher, manuel.bleichenbacher@gmail.com October 2019 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +// Note: The pin constants SS, RST_LoRa and DIOx are defined in pins_arduino.h +// (board-specific variant in Arduino core). Even if it won't be used, this +// file needs to compile for all other variants as well. +#if !defined(ARDUINO_HELTEC_WIFI_LORA_32) && !defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) && !defined(ARDUINO_HELTEC_WIRELESS_STICK) +#undef SS +#undef RST_LoRa +#undef DIO0 +#undef DIO1 +#undef DIO2 +#define SS HalPinmap_t::UNUSED_PIN +#define RST_LoRa HalPinmap_t::UNUSED_PIN +#define DIO0 HalPinmap_t::UNUSED_PIN +#define DIO1 HalPinmap_t::UNUSED_PIN +#define DIO2 HalPinmap_t::UNUSED_PIN +#endif + + +namespace Arduino_LMIC +{ + +class HalConfiguration_heltec_lora32 : public HalConfiguration_t +{ +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = SS, + PIN_SX1276_NRESET = RST_LoRa, + PIN_SX1276_DIO0 = DIO0, + PIN_SX1276_DIO1 = DIO1, + PIN_SX1276_DIO2 = DIO2, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; +}; + +static HalConfiguration_heltec_lora32 myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_heltec_lora32::PIN_SX1276_NSS, + .rxtx = HalConfiguration_heltec_lora32::PIN_SX1276_ANT_SWITCH_RX, + .rst = HalConfiguration_heltec_lora32::PIN_SX1276_NRESET, + .dio = { + HalConfiguration_heltec_lora32::PIN_SX1276_DIO0, + HalConfiguration_heltec_lora32::PIN_SX1276_DIO1, + HalConfiguration_heltec_lora32::PIN_SX1276_DIO2, + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig}; + +const HalPinmap_t *GetPinmap_heltec_lora32(void) +{ + return &myPinmap; +} + +}; // namespace Arduino_LMIC diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_thisboard.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_thisboard.cpp new file mode 100644 index 0000000..77f7d01 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_thisboard.cpp @@ -0,0 +1,75 @@ +/* + +Module: getconfig_thisboard.cpp + +Function: + Return a suitable LMIC config for this board. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +*/ + +#include + +namespace Arduino_LMIC { + +const HalPinmap_t *GetPinmap_ThisBoard(void) + { +/* +|| Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +|| m0 defs ADAFRUIT_FEATHER_M0 +*/ +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +# if defined(ARDUINO_MCCI_CATENA_4420) + // this uses a radiowing and an odd configuration + return GetPinmap_Catena4420(); +# else + // others use Feather M0 LoRa + return GetPinmap_FeatherM0LoRa(); +# endif +#elif defined(ARDUINO_AVR_FEATHER32U4) + return GetPinmap_Feather32U4LoRa(); +#elif defined(ARDUINO_MCCI_CATENA_4551) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4551) + return GetPinmap_Catena4551(); +#elif defined(ARDUINO_MCCI_CATENA_4610) + return GetPinmap_Catena4610(); +#elif defined(ARDUINO_MCCI_CATENA_4611) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4611) + return GetPinmap_Catena4611(); +#elif defined(ARDUINO_MCCI_CATENA_4612) || \ + /* legacy names */ \ + defined(ARDUINO_CATENA_4612) + return GetPinmap_Catena4612(); +#elif defined(ARDUINO_MCCI_CATENA_4617) + return GetPinmap_Catena4617(); +#elif defined(ARDUINO_MCCI_CATENA_4618) + return GetPinmap_Catena4618(); +#elif defined(ARDUINO_MCCI_CATENA_4630) + return GetPinmap_Catena4630(); +#elif defined(ARDUINO_MCCI_CATENA_4801) + return GetPinmap_Catena4801(); +#elif defined(ARDUINO_MCCI_CATENA_4802) + return GetPinmap_Catena4802(); +#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1) + return GetPinmap_Disco_L072cz_Lrwan1(); +#elif defined(PINNOCHIO_SCOUT) + return GetPinmap_PinnochioScount(); +#elif defined(ARDUINO_TTGO_LoRa32_V1) + return GetPinmap_ttgo_lora32_v1(); +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK) + return GetPinmap_heltec_lora32(); +#else + #pragma message("Board not supported -- use an explicit pinmap") + return nullptr; +#endif + } + +}; // namespace Arduino_LMIC + diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_ttgo_lora32_v1.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_ttgo_lora32_v1.cpp new file mode 100644 index 0000000..71db3eb --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/getpinmap_ttgo_lora32_v1.cpp @@ -0,0 +1,78 @@ +/* + +Module: getconfig_ttgo_lora32_v1.cpp + +Function: + Arduino-LMIC C++ HAL pinmap for TTGO ESP32 OLED V1 + +Copyright & License: + See accompanying LICENSE file. + +Author: + German Martin, gmag11@gmail.com June 2019 + +*/ + +#include +#include + +#include "../lmic/oslmic.h" + +#define LORA_DIO0 26 +#define LORA_DIO1 33 +#define LORA_DIO2 32 + +namespace Arduino_LMIC { + +class HalConfiguration_ttgo_lora32_v1 : public HalConfiguration_t + { +public: + enum DIGITAL_PINS : uint8_t + { + PIN_SX1276_NSS = 18, + PIN_SX1276_NRESET = 14, + PIN_SX1276_DIO0 = LORA_DIO0, + PIN_SX1276_DIO1 = LORA_DIO1, + PIN_SX1276_DIO2 = LORA_DIO2, + PIN_SX1276_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN, + PIN_SX1276_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN, + PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN, + }; + + virtual void begin(void) override + { + digitalWrite(PIN_SX1276_NSS, 1); + pinMode(PIN_SX1276_NSS, OUTPUT); + } + + // virtual void end(void) override + + // virtual ostime_t setModuleActive(bool state) override + + }; + +static HalConfiguration_ttgo_lora32_v1 myConfig; + +static const HalPinmap_t myPinmap = + { + .nss = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_NSS, // chip select is D7 + .rxtx = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_ANT_SWITCH_RX, // RXTX is D29 + .rst = HalConfiguration_ttgo_lora32_v1::PIN_SX1276_NRESET, // NRESET is D8 + + .dio = {HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO0, // DIO0 (IRQ) is D25 + HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO1, // DIO1 is D26 + HalConfiguration_ttgo_lora32_v1::PIN_SX1276_DIO2, // DIO2 is D27 + }, + .rxtx_rx_active = 0, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig + }; + +const HalPinmap_t * GetPinmap_ttgo_lora32_v1 (void) + { + return &myPinmap; + } + +}; // namespace Arduino_LMIC diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.cpp new file mode 100644 index 0000000..de3eacc --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.cpp @@ -0,0 +1,514 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * Copyright (c) 2018-2019 MCCI Corporation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ + +#include +#include +// include all the lmic header files, including ../lmic/hal.h +#include "../lmic.h" +// include the C++ hal.h +#include "hal.h" +// we may need some things from stdio. +#include + +// ----------------------------------------------------------------------------- +// I/O + +static const Arduino_LMIC::HalPinmap_t *plmic_pins; +static Arduino_LMIC::HalConfiguration_t *pHalConfig; +static Arduino_LMIC::HalConfiguration_t nullHalConig; +static hal_failure_handler_t* custom_hal_failure_handler = NULL; + +static void hal_interrupt_init(); // Fwd declaration + +static void hal_io_init () { + // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK + ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN); + +// Serial.print("nss: "); Serial.println(plmic_pins->nss); +// Serial.print("rst: "); Serial.println(plmic_pins->rst); +// Serial.print("dio[0]: "); Serial.println(plmic_pins->dio[0]); +// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]); +// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]); + + // initialize SPI chip select to high (it's active low) + digitalWrite(plmic_pins->nss, HIGH); + pinMode(plmic_pins->nss, OUTPUT); + + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) { + // initialize to RX + digitalWrite(plmic_pins->rxtx, LOW != plmic_pins->rxtx_rx_active); + pinMode(plmic_pins->rxtx, OUTPUT); + } + if (plmic_pins->rst != LMIC_UNUSED_PIN) { + // initialize RST to floating + pinMode(plmic_pins->rst, INPUT); + } + + hal_interrupt_init(); +} + +// val == 1 => tx +void hal_pin_rxtx (u1_t val) { + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) + digitalWrite(plmic_pins->rxtx, val != plmic_pins->rxtx_rx_active); +} + +// set radio RST pin to given value (or keep floating!) +void hal_pin_rst (u1_t val) { + if (plmic_pins->rst == LMIC_UNUSED_PIN) + return; + + if(val == 0 || val == 1) { // drive pin + digitalWrite(plmic_pins->rst, val); + pinMode(plmic_pins->rst, OUTPUT); + } else { // keep pin floating + pinMode(plmic_pins->rst, INPUT); + } +} + +s1_t hal_getRssiCal (void) { + return plmic_pins->rssi_cal; +} + +//-------------------- +// Interrupt handling +//-------------------- +static constexpr unsigned NUM_DIO_INTERRUPT = 3; +static_assert(NUM_DIO_INTERRUPT <= NUM_DIO, "Number of interrupt-sensitive lines must be less than number of GPIOs"); +static ostime_t interrupt_time[NUM_DIO_INTERRUPT] = {0}; + +#if !defined(LMIC_USE_INTERRUPTS) +static void hal_interrupt_init() { + pinMode(plmic_pins->dio[0], INPUT); + if (plmic_pins->dio[1] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[1], INPUT); + if (plmic_pins->dio[2] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[2], INPUT); + static_assert(NUM_DIO_INTERRUPT == 3, "Number of interrupt lines must be set to 3"); +} + +static bool dio_states[NUM_DIO_INTERRUPT] = {0}; +void hal_pollPendingIRQs_helper() { + uint8_t i; + for (i = 0; i < NUM_DIO_INTERRUPT; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + if (dio_states[i] != digitalRead(plmic_pins->dio[i])) { + dio_states[i] = !dio_states[i]; + if (dio_states[i] && interrupt_time[i] == 0) { + ostime_t const now = os_getTime(); + interrupt_time[i] = now ? now : 1; + } + } + } +} + +#else +// Interrupt handlers + +static void hal_isrPin0() { + if (interrupt_time[0] == 0) { + ostime_t now = os_getTime(); + interrupt_time[0] = now ? now : 1; + } +} +static void hal_isrPin1() { + if (interrupt_time[1] == 0) { + ostime_t now = os_getTime(); + interrupt_time[1] = now ? now : 1; + } +} +static void hal_isrPin2() { + if (interrupt_time[2] == 0) { + ostime_t now = os_getTime(); + interrupt_time[2] = now ? now : 1; + } +} + +typedef void (*isr_t)(); +static const isr_t interrupt_fns[NUM_DIO_INTERRUPT] = {hal_isrPin0, hal_isrPin1, hal_isrPin2}; +static_assert(NUM_DIO_INTERRUPT == 3, "number of interrupts must be 3 for initializing interrupt_fns[]"); + +static void hal_interrupt_init() { + for (uint8_t i = 0; i < NUM_DIO_INTERRUPT; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + pinMode(plmic_pins->dio[i], INPUT); + attachInterrupt(digitalPinToInterrupt(plmic_pins->dio[i]), interrupt_fns[i], RISING); + } +} +#endif // LMIC_USE_INTERRUPTS + +void hal_processPendingIRQs() { + uint8_t i; + for (i = 0; i < NUM_DIO_INTERRUPT; ++i) { + ostime_t iTime; + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + // NOTE(tmm@mcci.com): if using interrupts, this next step + // assumes uniprocessor and fairly strict memory ordering + // semantics relative to ISRs. It would be better to use + // interlocked-exchange, but that's really far beyond + // Arduino semantics. Because our ISRs use "first time + // stamp" semantics, we don't have a value-race. But if + // we were to disable ints here, we might observe a second + // edge that we'll otherwise miss. Not a problem in this + // use case, as the radio won't release IRQs until we + // explicitly clear them. + iTime = interrupt_time[i]; + if (iTime) { + interrupt_time[i] = 0; + radio_irq_handler_v2(i, iTime); + } + } +} + +// ----------------------------------------------------------------------------- +// SPI + +static void hal_spi_init () { + SPI.begin(); +} + +static void hal_spi_trx(u1_t cmd, u1_t* buf, size_t len, bit_t is_read) { + uint32_t spi_freq; + u1_t nss = plmic_pins->nss; + + if ((spi_freq = plmic_pins->spi_freq) == 0) + spi_freq = LMIC_SPI_FREQ; + + SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0); + SPI.beginTransaction(settings); + digitalWrite(nss, 0); + + SPI.transfer(cmd); + + for (; len > 0; --len, ++buf) { + u1_t data = is_read ? 0x00 : *buf; + data = SPI.transfer(data); + if (is_read) + *buf = data; + } + + digitalWrite(nss, 1); + SPI.endTransaction(); +} + +void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len) { + hal_spi_trx(cmd, (u1_t*)buf, len, 0); +} + +void hal_spi_read(u1_t cmd, u1_t* buf, size_t len) { + hal_spi_trx(cmd, buf, len, 1); +} + +// ----------------------------------------------------------------------------- +// TIME + +static void hal_time_init () { + // Nothing to do +} + +u4_t hal_ticks () { + // Because micros() is scaled down in this function, micros() will + // overflow before the tick timer should, causing the tick timer to + // miss a significant part of its values if not corrected. To fix + // this, the "overflow" serves as an overflow area for the micros() + // counter. It consists of three parts: + // - The US_PER_OSTICK upper bits are effectively an extension for + // the micros() counter and are added to the result of this + // function. + // - The next bit overlaps with the most significant bit of + // micros(). This is used to detect micros() overflows. + // - The remaining bits are always zero. + // + // By comparing the overlapping bit with the corresponding bit in + // the micros() return value, overflows can be detected and the + // upper bits are incremented. This is done using some clever + // bitwise operations, to remove the need for comparisons and a + // jumps, which should result in efficient code. By avoiding shifts + // other than by multiples of 8 as much as possible, this is also + // efficient on AVR (which only has 1-bit shifts). + static uint8_t overflow = 0; + + // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, + // the others will be the lower bits of our return value. + uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; + // Most significant byte of scaled + uint8_t msb = scaled >> 24; + // Mask pointing to the overlapping bit in msb and overflow. + const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); + // Update overflow. If the overlapping bit is different + // between overflow and msb, it is added to the stored value, + // so the overlapping bit becomes equal again and, if it changed + // from 1 to 0, the upper bits are incremented. + overflow += (msb ^ overflow) & mask; + + // Return the scaled value with the upper bits of stored added. The + // overlapping bit will be equal and the lower bits will be 0, so + // bitwise or is a no-op for them. + return scaled | ((uint32_t)overflow << 24); + + // 0 leads to correct, but overly complex code (it could just return + // micros() unmodified), 8 leaves no room for the overlapping bit. + static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); +} + +// Returns the number of ticks until time. Negative values indicate that +// time has already passed. +static s4_t delta_time(u4_t time) { + return (s4_t)(time - hal_ticks()); +} + +// deal with boards that are stressed by no-interrupt delays #529, etc. +#if defined(ARDUINO_DISCO_L072CZ_LRWAN1) +# define HAL_WAITUNTIL_DOWNCOUNT_MS 16 // on this board, 16 ms works better +# define HAL_WAITUNTIL_DOWNCOUNT_THRESH ms2osticks(16) // as does this threashold. +#else +# define HAL_WAITUNTIL_DOWNCOUNT_MS 8 // on most boards, delay for 8 ms +# define HAL_WAITUNTIL_DOWNCOUNT_THRESH ms2osticks(9) // but try to leave a little slack for final timing. +#endif + +u4_t hal_waitUntil (u4_t time) { + s4_t delta = delta_time(time); + // check for already too late. + if (delta < 0) + return -delta; + + // From delayMicroseconds docs: Currently, the largest value that + // will produce an accurate delay is 16383. Also, STM32 does a better + // job with delay is less than 10,000 us; so reduce in steps. + // It's nice to use delay() for the longer times. + while (delta > HAL_WAITUNTIL_DOWNCOUNT_THRESH) { + // deliberately delay 8ms rather than 9ms, so we + // will exit loop with delta typically positive. + // Depends on BSP keeping time accurately even if interrupts + // are disabled. + delay(HAL_WAITUNTIL_DOWNCOUNT_MS); + // re-synchronize. + delta = delta_time(time); + } + + // The radio driver runs with interrupt disabled, and this can + // mess up timing APIs on some platforms. If we know the BSP feature + // set, we can decide whether to use delta_time() [more exact, + // but not always possible with interrupts off], or fall back to + // delay_microseconds() [less exact, but more universal] + +#if defined(_mcci_arduino_version) + // unluckily, delayMicroseconds() isn't very accurate. + // but delta_time() works with interrupts disabled. + // so spin using delta_time(). + while (delta_time(time) > 0) + /* loop */; +#else // ! defined(_mcci_arduino_version) + // on other BSPs, we need to stick with the older way, + // until we fix the radio driver to run with interrupts + // enabled. + if (delta > 0) + delayMicroseconds(delta * US_PER_OSTICK); +#endif // ! defined(_mcci_arduino_version) + + // we aren't "late". Callers are interested in gross delays, not + // necessarily delays due to poor timekeeping here. + return 0; +} + +// check and rewind for target time +u1_t hal_checkTimer (u4_t time) { + // No need to schedule wakeup, since we're not sleeping + return delta_time(time) <= 0; +} + +static uint8_t irqlevel = 0; + +void hal_disableIRQs () { + noInterrupts(); + irqlevel++; +} + +void hal_enableIRQs () { + if(--irqlevel == 0) { + interrupts(); + +#if !defined(LMIC_USE_INTERRUPTS) + // Instead of using proper interrupts (which are a bit tricky + // and/or not available on all pins on AVR), just poll the pin + // values. Since os_runloop disables and re-enables interrupts, + // putting this here makes sure we check at least once every + // loop. + // + // As an additional bonus, this prevents the can of worms that + // we would otherwise get for running SPI transfers inside ISRs. + // We merely collect the edges and timestamps here; we wait for + // a call to hal_processPendingIRQs() before dispatching. + hal_pollPendingIRQs_helper(); +#endif /* !defined(LMIC_USE_INTERRUPTS) */ + } +} + +uint8_t hal_getIrqLevel(void) { + return irqlevel; +} + +void hal_sleep () { + // Not implemented +} + +// ----------------------------------------------------------------------------- + +#if defined(LMIC_PRINTF_TO) +#if !defined(__AVR) +static ssize_t uart_putchar (void *, const char *buf, size_t len) { + return LMIC_PRINTF_TO.write((const uint8_t *)buf, len); +} + +static cookie_io_functions_t functions = + { + .read = NULL, + .write = uart_putchar, + .seek = NULL, + .close = NULL + }; + +void hal_printf_init() { + stdout = fopencookie(NULL, "w", functions); + if (stdout != nullptr) { + setvbuf(stdout, NULL, _IONBF, 0); + } +} +#else // defined(__AVR) +static int uart_putchar (char c, FILE *) +{ + LMIC_PRINTF_TO.write(c) ; + return 0 ; +} + +void hal_printf_init() { + // create a FILE structure to reference our UART output function + static FILE uartout; + memset(&uartout, 0, sizeof(uartout)); + + // fill in the UART file descriptor with pointer to writer. + fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); + + // The uart is the standard output device STDOUT. + stdout = &uartout ; +} + +#endif // !defined(ESP8266) || defined(ESP31B) || defined(ESP32) +#endif // defined(LMIC_PRINTF_TO) + +//void hal_init (void) { + void hal_init_lmic() { + + // use the global constant + Arduino_LMIC::hal_init_with_pinmap(&lmic_pins); +} + +// hal_init_ex is a C API routine, written in C++, and it's called +// with a pointer to an lmic_pinmap. +void hal_init_ex (const void *pContext) { + const lmic_pinmap * const pHalPinmap = (const lmic_pinmap *) pContext; + if (! Arduino_LMIC::hal_init_with_pinmap(pHalPinmap)) { + hal_failed(__FILE__, __LINE__); + } +} + +// C++ API: initialize the HAL properly with a configuration object +namespace Arduino_LMIC { +bool hal_init_with_pinmap(const HalPinmap_t *pPinmap) + { + if (pPinmap == nullptr) + return false; + + // set the static pinmap pointer. + plmic_pins = pPinmap; + + // set the static HalConfiguration pointer. + HalConfiguration_t * const pThisHalConfig = pPinmap->pConfig; + + if (pThisHalConfig != nullptr) + pHalConfig = pThisHalConfig; + else + pHalConfig = &nullHalConig; + + pHalConfig->begin(); + + // configure radio I/O and interrupt handler + hal_io_init(); + // configure radio SPI + hal_spi_init(); + // configure timer and interrupt handler + hal_time_init(); +#if defined(LMIC_PRINTF_TO) + // printf support + hal_printf_init(); +#endif + // declare success + return true; + } +}; // namespace Arduino_LMIC + + +void hal_failed (const char *file, u2_t line) { + if (custom_hal_failure_handler != NULL) { + (*custom_hal_failure_handler)(file, line); + } + +#if defined(LMIC_FAILURE_TO) + LMIC_FAILURE_TO.println("FAILURE "); + LMIC_FAILURE_TO.print(file); + LMIC_FAILURE_TO.print(':'); + LMIC_FAILURE_TO.println(line); + LMIC_FAILURE_TO.flush(); +#endif + + hal_disableIRQs(); + + // Infinite loop + while (1) { + ; + } +} + +void hal_set_failure_handler(const hal_failure_handler_t* const handler) { + custom_hal_failure_handler = handler; +} + +ostime_t hal_setModuleActive (bit_t val) { + // setModuleActive() takes a c++ bool, so + // it effectively says "val != 0". We + // don't have to. + return pHalConfig->setModuleActive(val); +} + +bit_t hal_queryUsingTcxo(void) { + return pHalConfig->queryUsingTcxo(); +} + +uint8_t hal_getTxPowerPolicy( + u1_t inputPolicy, + s1_t requestedPower, + u4_t frequency + ) { + return (uint8_t) pHalConfig->getTxPowerPolicy( + Arduino_LMIC::HalConfiguration_t::TxPowerPolicy_t(inputPolicy), + requestedPower, + frequency + ); +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.h new file mode 100644 index 0000000..f831c1e --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/hal/hal.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2015-2016 Matthijs Kooijman + * Copyright (c) 2016-2018 MCCI Corporation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ +#ifndef _hal_hal_h_ +#define _hal_hal_h_ + +#include "arduino_lmic_hal_configuration.h" + +// for compatbility reasons, we need to disclose the configuration +// structure as global type lmic_pinmap. +using lmic_pinmap = Arduino_LMIC::HalPinmap_t; + +// similarly, we need to disclose NUM_DIO and LMIC_UNUSED_PIN +static const int NUM_DIO = lmic_pinmap::NUM_DIO; + +// Use this for any unused pins. +const u1_t LMIC_UNUSED_PIN = lmic_pinmap::UNUSED_PIN; + +// Declared here, to be defined and initialized by the application. +// Use os_init_ex() if you want not to use a const table, or if +// you need to define a derived type (so you can override methods). +extern const lmic_pinmap lmic_pins; + +#endif // _hal_hal_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic.h new file mode 100644 index 0000000..d0423a3 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic.h @@ -0,0 +1,30 @@ +/* + +Module: lmic.h + +Function: + Deprecated C++ top-level include file (use instead). + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + +Note: + This header file is deprecated and is included for + transitional purposes. It's deprecated because it's + confusing to have src/lmic.h (this file) and src/lmic/lmic.h + (the API file for the C library). We can't take it out + yet, because it would inconvenience the world, but + we can hope that someday it will wither away (on a major + version bump). + + Please don't add any new functionality in this file; + it is just a wrapper for arduino_lmic.h. + +*/ + +#include "arduino_lmic.h" + +/* end of file */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/config.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/config.h new file mode 100644 index 0000000..abd0538 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/config.h @@ -0,0 +1,223 @@ +#ifndef _lmic_config_h_ +#define _lmic_config_h_ + +// In the original LMIC code, these config values were defined on the +// gcc commandline. Since Arduino does not allow easily modifying the +// compiler commandline unless you modify the BSP, you have two choices: +// +// - edit {libraries}/arduino-lmic/project_config/lmic_project_config.h; +// - use a BSP like the MCCI Arduino BSPs, which get the configuration +// from the boards.txt file through a menu option. +// +// You definitely should not edit this file. + +// set up preconditions, and load configuration if needed. +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +// check post-conditions. + +// make sure that we have exactly one target region defined. +#if CFG_LMIC_REGION_MASK == 0 +# define CFG_eu868 1 +#elif (CFG_LMIC_REGION_MASK & (-CFG_LMIC_REGION_MASK)) != CFG_LMIC_REGION_MASK +# error You can define at most one of CFG_... variables +#elif (CFG_LMIC_REGION_MASK & LMIC_REGIONS_SUPPORTED) == 0 +# error The selected CFG_... region is not supported yet. +#endif + +// make sure that LMIC_COUNTRY_CODE is defined. +#ifndef LMIC_COUNTRY_CODE +# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_NONE +#endif + +// if the country code is Japan, then the region must be AS923 +#if LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP && CFG_region != LMIC_REGION_as923 +# error "If country code is JP, then region must be AS923" +#endif + +// check for internal consistency +#if !(CFG_LMIC_EU_like || CFG_LMIC_US_like) +# error "Internal error: Neither EU-like nor US-like!" +#endif + +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +//#define CFG_sx1276_radio 1 + +// ensure that a radio is defined. +#if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio)) +# warning Target radio not defined, assuming CFG_sx1276_radio +#define CFG_sx1276_radio 1 +#elif defined(CFG_sx1272_radio) && defined(CFG_sx1276_radio) +# error You can define at most one of CFG_sx1272_radio and CF_sx1276_radio +#endif + +// LMIC requires ticks to be 15.5μs - 100 μs long +#ifndef OSTICKS_PER_SEC +// 16 μs per tick +# ifndef US_PER_OSTICK_EXPONENT +# define US_PER_OSTICK_EXPONENT 4 +# endif +# define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +# define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) +#endif /* OSTICKS_PER_SEC */ + +#if ! (10000 <= OSTICKS_PER_SEC && OSTICKS_PER_SEC < 64516) +# error LMIC requires ticks to be 15.5 us to 100 us long +#endif + +// Change the SPI clock speed if you encounter errors +// communicating with the radio. +// The standard range is 125kHz-8MHz, but some boards can go faster. +#ifndef LMIC_SPI_FREQ +#define LMIC_SPI_FREQ 1E6 +#endif + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +#ifndef LMIC_DEBUG_LEVEL +#define LMIC_DEBUG_LEVEL 0 +#endif + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Enable this to use interrupt handler routines listening for RISING signals. +// Otherwise, the library polls digital input lines for changes. +//#define LMIC_USE_INTERRUPTS + +// If DISABLE_LMIC_FAILURE_TO is defined, runtime assertion failures +// silently halt execution. Otherwise, LMIC_FAILURE_TO should be defined +// as the name of an object derived from Print, which will be used for +// displaying runtime assertion failures. If you say nothing in your +// lmic_project_config.h, runtime assertion failures are displayed +// using the Serial object. +#if ! defined(DISABLE_LMIC_FAILURE_TO) && ! defined(LMIC_FAILURE_TO) +#define LMIC_FAILURE_TO Serial +#endif + +// define this in lmic_project_config.h to disable all code related to joining +//#define DISABLE_JOIN +// define this in lmic_project_config.h to disable all code related to ping +//#define DISABLE_PING +// define this in lmic_project_config.h to disable all code related to beacon tracking. +// Requires ping to be disabled too +//#define DISABLE_BEACONS + +// define these in lmic_project_config.h to disable the corresponding MAC commands. +// Class A +//#define DISABLE_MCMD_DutyCycleReq // duty cycle cap +//#define DISABLE_MCMD_RXParamSetupReq // 2nd DN window param +//#define DISABLE_MCMD_NewChannelReq // set new channel +//#define DISABLE_MCMD_DlChannelReq // set downlink channel for RX1 for given uplink channel. +//#define DISABLE_MCMD_RXTimingSetupReq // delay between TX and RX +// Class B +//#define DISABLE_MCMD_PingSlotChannelReq // set ping freq, automatically disabled by DISABLE_PING +//#define ENABLE_MCMD_BeaconTimingAns // next beacon start, DEPRECATED, normally disabled by DISABLE_BEACON + +// DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this. +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +// #define USE_IDEETRON_AES + +#if ! (defined(USE_ORIGINAL_AES) || defined(USE_IDEETRON_AES)) +# define USE_IDEETRON_AES +#endif + +#if defined(USE_ORIGINAL_AES) && defined(USE_IDEETRON_AES) +# error "You may define at most one of USE_ORIGINAL_AES and USE_IDEETRON_AES" +#endif + +// LMIC_DISABLE_DR_LEGACY +// turn off legacy DR_* symbols that vary by bandplan. +// Older code uses these for configuration. EU868_DR_*, US915_DR_* +// etc symbols are prefered, but breaking older code is inconvenient for +// everybody. We don't want to use DR_* in the LMIC itself, so we provide +// this #define to allow them to be removed. +#if !defined(LMIC_DR_LEGACY) +# if !defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 1 +# else // defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 0 +# endif // defined(LMIC_DISABLE_DR_LEGACY) +#endif // LMIC_DR_LEGACY + +// LMIC_ENABLE_DeviceTimeReq +// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns +// this is always defined, and non-zero to enable it. +#if !defined(LMIC_ENABLE_DeviceTimeReq) +# define LMIC_ENABLE_DeviceTimeReq 0 +#endif + +// LMIC_ENABLE_user_events +// Enable/disable support for programmable callbacks for events, rx, and tx. +// This is always defined, and non-zero to enable. Default is enabled. +#if !defined(LMIC_ENABLE_user_events) +# define LMIC_ENABLE_user_events 1 +#endif + +// LMIC_ENABLE_onEvent +// Enable/disable support for out-call to user-supplied `onEvent()` function. +// This is always defined, and non-zero to enable. Default is enabled. +#if !defined(LMIC_ENABLE_onEvent) +# define LMIC_ENABLE_onEvent 1 +#endif + +// LMIC_ENABLE_long_messages +// LMIC certification requires full-length 255 frames, but to save RAM, +// a shorter maximum can be set. This controls both RX and TX buffers, +// so reducing this by 1 saves 2 bytes of RAM. +#if defined(LMIC_ENABLE_long_messages) && defined(LMIC_MAX_FRAME_LENGTH) +#error "Use only one of LMIC_ENABLE_long_messages or LMIC_MAX_FRAME_LENGTH" +#elif defined(LMIC_ENABLE_long_messages) && LMIC_ENABLE_long_messages == 0 +# define LMIC_MAX_FRAME_LENGTH 64 +#elif !defined(LMIC_MAX_FRAME_LENGTH) +# define LMIC_MAX_FRAME_LENGTH 255 +#elif LMIC_MAX_FRAME_LENGTH > 255 +#error "LMIC_MAX_FRAME_LENGTH cannot be larger than 255" +#endif + +// LMIC_ENABLE_event_logging +// LMIC debugging for certification tests requires this, because debug prints affect +// timing too dramatically. But normal operation doesn't need this. +#if !defined(LMIC_ENABLE_event_logging) +# define LMIC_ENABLE_event_logging 0 /* PARAM */ +#endif + +// LMIC_LORAWAN_SPEC_VERSION +#if !defined(LMIC_LORAWAN_SPEC_VERSION) +# define LMIC_LORAWAN_SPEC_VERSION LMIC_LORAWAN_SPEC_VERSION_1_0_3 +#endif + +// LMIC_ENABLE_arbitrary_clock_error +// We normally don't want to allow users to set wide clock errors, because +// we assume reasonably-disciplined os_getTime() values. But... there might +// be platforms that require wider errors. +#if !defined(LMIC_ENABLE_arbitrary_clock_error) +# define LMIC_ENABLE_arbitrary_clock_error 0 /* PARAM */ +#endif + +#endif // _lmic_config_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/hal.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/hal.h new file mode 100644 index 0000000..f232480 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/hal.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016, 2018-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _lmic_hal_h_ +#define _lmic_hal_h_ + +#ifndef _oslmic_types_h_ +# include "oslmic_types.h" +#endif + +#ifndef _lmic_env_h_ +# include "lmic_env.h" +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// The type of an optional user-defined failure handler routine +typedef void LMIC_ABI_STD hal_failure_handler_t(const char* const file, const uint16_t line); + +/* + * initialize hardware (IO, SPI, TIMER, IRQ). + * This API is deprecated as it uses the const global lmic_pins, + * which the platform can't control or change. + */ +void hal_init (void); + +/* + * Initialize hardware, passing in platform-specific context + * The pointer is to a HalPinmap_t. + */ +void hal_init_ex (const void *pContext); + +/* + * drive radio RX/TX pins (0=rx, 1=tx). Actual polarity + * is determined by the value of HalPinmap_t::rxtx_rx_active. + */ +void hal_pin_rxtx (u1_t val); + +/* + * control radio RST pin (0=low, 1=high, 2=floating) + */ +void hal_pin_rst (u1_t val); + +/* + * Perform SPI write transaction with radio chip + * - write the command byte 'cmd' + * - write 'len' bytes out of 'buf' + */ +void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len); + +/* + * Perform SPI read transaction with radio chip + * - write the command byte 'cmd' + * - read 'len' bytes into 'buf' + */ +void hal_spi_read(u1_t cmd, u1_t* buf, size_t len); + +/* + * disable all CPU interrupts. + * - might be invoked nested + * - will be followed by matching call to hal_enableIRQs() + */ +void hal_disableIRQs (void); + +/* + * enable CPU interrupts. + */ +void hal_enableIRQs (void); + +/* + * return CPU interrupt nesting count + */ +uint8_t hal_getIrqLevel (void); + +/* + * put system and CPU in low-power mode, sleep until interrupt. + */ +void hal_sleep (void); + +/* + * return 32-bit system time in ticks. + */ +u4_t hal_ticks (void); + +/* + * busy-wait until specified timestamp (in ticks) is reached. If on-time, return 0, + * otherwise return the number of ticks we were late. + */ +u4_t hal_waitUntil (u4_t time); + +/* + * check and rewind timer for target time. + * - return 1 if target time is close + * - otherwise rewind timer for target time or full period and return 0 + */ +u1_t hal_checkTimer (u4_t targettime); + +/* + * perform fatal failure action. + * - called by assertions + * - action could be HALT or reboot + */ +void hal_failed (const char *file, u2_t line); + +/* + * set a custom hal failure handler routine. The default behaviour, defined in + * hal_failed(), is to halt by looping infintely. + */ +void hal_set_failure_handler(const hal_failure_handler_t* const); + +/* + * get the calibration value for radio_rssi + */ +s1_t hal_getRssiCal (void); + +/* + * control the radio state + * - if val == 0, turn tcxo off and otherwise prepare for sleep + * - if val == 1, turn tcxo on and otherwise prep for activity + * - return the number of ticks that we need to wait + */ +ostime_t hal_setModuleActive (bit_t val); + +/* find out if we're using Tcxo */ +bit_t hal_queryUsingTcxo(void); + +/* represent the various radio TX power policy */ +enum { + LMICHAL_radio_tx_power_policy_rfo = 0, + LMICHAL_radio_tx_power_policy_paboost = 1, + LMICHAL_radio_tx_power_policy_20dBm = 2, +}; + +/* + * query the configuration as to the Tx Power Policy + * to be used on this board, given our desires and + * requested power. + */ +uint8_t hal_getTxPowerPolicy( + u1_t inputPolicy, + s1_t requestedPower, + u4_t freq + ); + +void hal_pollPendingIRQs_helper(); +void hal_processPendingIRQs(void); + +/// \brief check for any pending interrupts: stub if interrupts are enabled. +static void inline hal_pollPendingIRQs(void) + { +#if !defined(LMIC_USE_INTERRUPTS) + hal_pollPendingIRQs_helper(); +#endif /* !defined(LMIC_USE_INTERRUPTS) */ + } + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lmic_hal_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.c new file mode 100644 index 0000000..957d622 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.c @@ -0,0 +1,3103 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Copyright (c) 2016-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#define LMIC_DR_LEGACY 0 +#include "lmic_bandplan.h" + +#if defined(DISABLE_BEACONS) && !defined(DISABLE_PING) +#error Ping needs beacon tracking +#endif + +DEFINE_LMIC; + +// Fwd decls. +static void reportEventNoUpdate(ev_t); +static void reportEventAndUpdate(ev_t); +static void engineUpdate(void); +static bit_t processJoinAccept_badframe(void); +static bit_t processJoinAccept_nojoinframe(void); + + +#if !defined(DISABLE_BEACONS) +static void startScan (void); +#endif + +// set the txrxFlags, with debugging +static inline void initTxrxFlags(const char *func, u1_t mask) { + LMIC_DEBUG2_PARAMETER(func); + +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask); +#endif + LMIC.txrxFlags = mask; +} + +// or the txrxFlags, with debugging +static inline void orTxrxFlags(const char *func, u1_t mask) { + initTxrxFlags(func, LMIC.txrxFlags | mask); +} + + + +// ================================================================================ +// BEG OS - default implementations for certain OS suport functions + +#if !defined(HAS_os_calls) + +#if !defined(os_rlsbf2) +u2_t os_rlsbf2 (xref2cu1_t buf) { + return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8)); +} +#endif + +#if !defined(os_rlsbf4) +u4_t os_rlsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); +} +#endif + + +#if !defined(os_rmsbf4) +u4_t os_rmsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); +} +#endif + + +#if !defined(os_wlsbf2) +void os_wlsbf2 (xref2u1_t buf, u2_t v) { + buf[0] = v; + buf[1] = v>>8; +} +#endif + +#if !defined(os_wlsbf4) +void os_wlsbf4 (xref2u1_t buf, u4_t v) { + buf[0] = v; + buf[1] = v>>8; + buf[2] = v>>16; + buf[3] = v>>24; +} +#endif + +#if !defined(os_wmsbf4) +void os_wmsbf4 (xref2u1_t buf, u4_t v) { + buf[3] = v; + buf[2] = v>>8; + buf[1] = v>>16; + buf[0] = v>>24; +} +#endif + +#if !defined(os_getBattLevel) +u1_t os_getBattLevel (void) { + return MCMD_DEVS_BATT_NOINFO; +} +#endif + +#if !defined(os_crc16) +// New CRC-16 CCITT(XMODEM) checksum for beacons: +u2_t os_crc16 (xref2cu1_t data, uint len) { + u2_t remainder = 0; + u2_t polynomial = 0x1021; + for( uint i = 0; i < len; i++ ) { + remainder ^= data[i] << 8; + for( u1_t bit = 8; bit > 0; bit--) { + if( (remainder & 0x8000) ) + remainder = (remainder << 1) ^ polynomial; + else + remainder <<= 1; + } + } + return remainder; +} +#endif + +#endif // !HAS_os_calls + +// END OS - default implementations for certain OS suport functions +// ================================================================================ + +// ================================================================================ +// BEG AES + +static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) { + os_clearMem(AESaux,16); + AESaux[0] = 0x49; + AESaux[5] = dndir?1:0; + AESaux[15] = len; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); +} + + +static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + // MSB because of internal structure of AES + os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len)); +} + + +static void aes_appendMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES +} + + +static int aes_verifyMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_encrypt (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_aes(AES_ENC, pdu, len); +} + + +static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) { + if( len <= 0 ) + return; + os_clearMem(AESaux, 16); + AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1 + AESaux[5] = dndir?1:0; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); + os_copyMem(AESkey,key,16); + os_aes(AES_CTR, payload, len); +} + + +static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) { + os_clearMem(nwkkey, 16); + nwkkey[0] = 0x01; + os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID); + os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce); + os_copyMem(artkey, nwkkey, 16); + artkey[0] = 0x02; + + os_getDevKey(AESkey); + os_aes(AES_ENC, nwkkey, 16); + os_getDevKey(AESkey); + os_aes(AES_ENC, artkey, 16); +} + +// END AES +// ================================================================================ + + +// ================================================================================ +// BEG LORA + +static CONST_TABLE(u1_t, SENSITIVITY)[7][3] = { + // ------------bw---------- + // 125kHz 250kHz 500kHz + { 141-109, 141-109, 141-109 }, // FSK + { 141-127, 141-124, 141-121 }, // SF7 + { 141-129, 141-126, 141-123 }, // SF8 + { 141-132, 141-129, 141-126 }, // SF9 + { 141-135, 141-132, 141-129 }, // SF10 + { 141-138, 141-135, 141-132 }, // SF11 + { 141-141, 141-138, 141-135 } // SF12 +}; + +int getSensitivity (rps_t rps) { + return -141 + TABLE_GET_U1_TWODIM(SENSITIVITY, getSf(rps), getBw(rps)); +} + +ostime_t calcAirTime (rps_t rps, u1_t plen) { + u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz + u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12 + if( sf == FSK ) { + return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8 + * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000; + } + u1_t sfx = 4*(sf+(7-SF7)); + u1_t q = sfx - (sf >= SF11 ? 8 : 0); + int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0); + if( tmp > 0 ) { + tmp = (tmp + q - 1) / q; + tmp *= getCr(rps)+5; + tmp += 8; + } else { + tmp = 8; + } + tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */; + // bw = 125000 = 15625 * 2^3 + // 250000 = 15625 * 2^4 + // 500000 = 15625 * 2^5 + // sf = 7..12 + // + // osticks = tmp * OSTICKS_PER_SEC * 1< counter reduced divisor 125000/8 => 15625 + // 2 => counter 2 shift on tmp + sfx = sf+(7-SF7) - (3+2) - bw; + int div = 15625; + if( sfx > 4 ) { + // prevent 32bit signed int overflow in last step + div >>= sfx-4; + sfx = 4; + } + // Need 32bit arithmetic for this last step + return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div; +} + +// END LORA +// ================================================================================ + + +// Table below defines the size of one symbol as +// symtime = 256us * 2^T(sf,bw) +// 256us is called one symunit. +// SF: +// BW: |__7___8___9__10__11__12 +// 125kHz | 2 3 4 5 6 7 +// 250kHz | 1 2 3 4 5 6 +// 500kHz | 0 1 2 3 4 5 +// + +static void setRxsyms (ostime_t rxsyms) { + if (rxsyms >= (((ostime_t)1) << 10u)) { + LMIC.rxsyms = (1u << 10u) - 1; + } else if (rxsyms < 0) { + LMIC.rxsyms = 0; + } else { + LMIC.rxsyms = rxsyms; + } +} + +#if !defined(DISABLE_BEACONS) +static ostime_t calcRxWindow (u1_t secs, dr_t dr) { + ostime_t rxoff, err; + if( secs==0 ) { + // aka 128 secs (next becaon) + rxoff = LMIC.drift; + err = LMIC.lastDriftDiff; + } else { + // scheduled RX window within secs into current beacon period + rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp; + err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp; + } + rxsyms_t rxsyms = LMICbandplan_MINRX_SYMS_LoRa_ClassB; + err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns; + setRxsyms(LMICbandplan_MINRX_SYMS_LoRa_ClassB + (err / dr2hsym(dr))); + + return (rxsyms-LMICbandplan_PAMBL_SYMS) * dr2hsym(dr) + rxoff; +} + + +// Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2)) +static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) { + if( ini ) { + LMIC.drift = 0; + LMIC.maxDriftDiff = 0; + LMIC.missedBcns = 0; + LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF; + } + ostime_t hsym = dr2hsym(DR_BCN); + LMIC.bcnRxsyms = LMICbandplan_MINRX_SYMS_LoRa_ClassB + ms2osticksCeil(ms) / hsym; + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-LMICbandplan_PAMBL_SYMS) * hsym; +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +// Setup scheduled RX window (ping/multicast slot) +static void rxschedInit (xref2rxsched_t rxsched) { + os_clearMem(AESkey,16); + os_clearMem(LMIC.frame+8,8); + os_wlsbf4(LMIC.frame, LMIC.bcninfo.time); + os_wlsbf4(LMIC.frame+4, LMIC.devaddr); + os_aes(AES_ENC,LMIC.frame,16); + u1_t intvExp = rxsched->intvExp; + ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units) + rxsched->rxbase = (LMIC.bcninfo.txtime + + BCN_RESERVE_osticks + + ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks + rxsched->slot = 0; + rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<dr); + rxsched->rxsyms = LMIC.rxsyms; +} + + +static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) { + again: + if( rxsched->rxtime - cando >= 0 ) + return 1; + u1_t slot; + if( (slot=rxsched->slot) >= 128 ) + return 0; + u1_t intv = 1<intvExp; + if( (rxsched->slot = (slot += (intv))) >= 128 ) + return 0; + rxsched->rxtime = rxsched->rxbase + + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp) + - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr); + rxsched->rxsyms = LMIC.rxsyms; + goto again; +} +#endif // !DISABLE_PING) + + +ostime_t LMICcore_rndDelay (u1_t secSpan) { + u2_t r = os_getRndU2(); + ostime_t delay = r; + if( delay > OSTICKS_PER_SEC ) + delay = r % (u2_t)OSTICKS_PER_SEC; + if( secSpan > 0 ) + delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC; + return delay; +} + +// delay reftime ticks, plus a random interval in [0..secSpan). +static void txDelay (ostime_t reftime, u1_t secSpan) { + if (secSpan != 0) + reftime += LMICcore_rndDelay(secSpan); + if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) { + LMIC.globalDutyAvail = reftime; + LMIC.opmode |= OP_RNDTX; + } +} + + +void LMICcore_setDrJoin (u1_t reason, u1_t dr) { + LMIC_EV_PARAMETER(reason); + + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = LMIC.adrTxPow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); +} + + +static bit_t setDrTxpow (u1_t reason, u1_t dr, s1_t pow) { + bit_t result = 0; + + LMIC_EV_PARAMETER(reason); + + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = pow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + + if( pow != KEEP_TXPOW && pow != LMIC.adrTxPow ) { + LMIC.adrTxPow = pow; + result = 1; + } + if( LMIC.datarate != dr ) { + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); + LMIC.opmode |= OP_NEXTCHNL; + result = 1; + } + return result; +} + + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void) { + LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI); +} + + +void LMIC_setPingable (u1_t intvExp) { + // Change setting + LMIC.ping.intvExp = (intvExp & 0x7); + LMIC.opmode |= OP_PINGABLE; + // App may call LMIC_enableTracking() explicitely before + // Otherwise tracking is implicitly enabled here + if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 ) + LMIC_enableTracking(0); +} + +#endif // !DISABLE_PING + +static void runEngineUpdate (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + engineUpdate(); +} + +static void reportEventAndUpdate(ev_t ev) { + reportEventNoUpdate(ev); + engineUpdate(); +} + +static void reportEventNoUpdate (ev_t ev) { + uint32_t const evSet = UINT32_C(1) << ev; + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = ev)); +#if LMIC_ENABLE_onEvent + void (*pOnEvent)(ev_t) = onEvent; + + // rxstart is critical timing; legacy onEvent handlers + // don't comprehend this; so don't report. + if (pOnEvent != NULL && (evSet & (UINT32_C(1)<> 8; // read as signed 24-bit + LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto + LMIC.bcninfo.info = d[OFF_BCN_INFO]; + LMIC.bcninfo.flags |= BCN_FULL; + return LMIC_BEACON_ERROR_SUCCESS_FULL; +} +#endif // !DISABLE_BEACONS + +// put a mac response to the current output buffer. Limit according to kind of +// mac data (piggyback vs port 0) +static bit_t put_mac_uplink_byte(uint8_t b) { + if (LMIC.pendMacPiggyback) { + // put in pendMacData + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } else { + // put in pendTxData + if (LMIC.pendMacLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } +} + +static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t b2) { + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } +} + +static bit_t put_mac_uplink_byte3(u1_t b1, u1_t b2, u1_t b3) { + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2) && put_mac_uplink_byte(b3)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } +} + +static CONST_TABLE(u1_t, macCmdSize)[] = { + /* 2: LinkCheckAns */ 3, + /* 3: LinkADRReq */ 5, + /* 4: DutyCycleReq */ 2, + /* 5: RXParamSetupReq */ 5, + /* 6: DevStatusReq */ 1, + /* 7: NewChannelReq */ 6, + /* 8: RXTimingSetupReq */ 2, + /* 9: TxParamSetupReq */ 2, + /* 0x0A: DlChannelReq */ 5, + /* B, C: RFU */ 0, 0, + /* 0x0D: DeviceTimeAns */ 6, + /* 0x0E, 0x0F */ 0, 0, + /* 0x10: PingSlotInfoAns */ 1, + /* 0x11: PingSlotChannelReq */ 5, + /* 0x12: BeaconTimingAns */ 4, + /* 0x13: BeaconFreqReq */ 4 +}; + +static u1_t getMacCmdSize(u1_t macCmd) { + if (macCmd >= 2) { + const unsigned macCmdMinus2 = macCmd - 2u; + if (macCmdMinus2 < LENOF_TABLE(macCmdSize)) { + // macCmd in table, fetch it's size. + return TABLE_GET_U1(macCmdSize, macCmdMinus2); + } + } + // macCmd too small or too large: return zero. Zero is + // never a legal command size, so it signals an error + // to the caller. + return 0; +} + +static bit_t +applyAdrRequests( + const uint8_t *opts, + int olen, + u1_t adrAns +) { + lmic_saved_adr_state_t initialState; + int const kAdrReqSize = 5; + int oidx; + u1_t p1 = 0; + u1_t p4 = 0; + bit_t response_fit = 1; + bit_t map_ok = 1; + + LMICbandplan_saveAdrState(&initialState); + + // compute the changes + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + for (oidx = 0; oidx < olen; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + + p1 = opts[oidx+1]; // txpow + DR, in case last + p4 = opts[oidx+4]; // ChMaskCtl, NbTrans + u1_t chpage = p4 & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page + + map_ok = LMICbandplan_mapChannels(chpage, chmap); + LMICOS_logEventUint32("applyAdrRequests: mapChannels", ((u4_t)chpage << 16)|(chmap << 0)); + } + } + + if (! map_ok) { + adrAns &= ~MCMD_LinkADRAns_ChannelACK; + } + + // p1 now has txpow + DR. DR must be feasible. + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); + + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) && ! LMICbandplan_isDataRateFeasible(dr)) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + LMICOS_logEventUint32("applyAdrRequests: final DR not feasible", dr); + } + + if (adrAns != (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + LMICbandplan_restoreAdrState(&initialState); + } + + // now put all the options + for (oidx = 0; oidx < olen && response_fit; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } + response_fit = put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); + } + + // all done scanning options + bit_t changes = LMICbandplan_compareAdrState(&initialState); + + // handle the final options + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + // handle uplink repeat count + u1_t uprpt = p4 & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + if (LMIC.upRepeat != uprpt) { + LMIC.upRepeat = uprpt; + changes = 1; + } + + LMICOS_logEventUint32("applyAdrRequests: setDrTxPow", ((u4_t)adrAns << 16)|(dr << 8)|(p1 << 0)); + + // handle power changes here, too. + changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + } + + // Certification doesn't like this, but it makes the device happier with TTN. + // LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + + return response_fit; +} + +static int +scan_mac_cmds_link_adr( + const uint8_t *opts, + int olen, + bit_t *presponse_fit + ) + { + LMICOS_logEventUint32("scan_mac_cmds_link_adr", olen); + + if (olen == 0) + return 0; + + int oidx = 0; + int const kAdrReqSize = 5; + int lastOidx; + u1_t adrAns = MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK; + + // process the contiguous slots + for (;;) { + lastOidx = oidx; + + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end; but fail it. + adrAns = 0; + break; + } + u1_t p1 = opts[oidx+1]; // txpow + DR + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + u1_t chpage = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page + // u1_t uprpt = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); + + if( !LMICbandplan_canMapChannels(chpage, chmap) ) { + adrAns &= ~MCMD_LinkADRAns_ChannelACK; + LMICOS_logEventUint32("scan_mac_cmds_link_adr: failed canMapChannels", ((u4_t)chpage << 16)|((u4_t)chmap << 0)); + } + + if( !validDR(dr) ) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + } + if (pow2dBm(p1) == -128) { + adrAns &= ~MCMD_LinkADRAns_PowerACK; + } + + oidx += kAdrReqSize; + if (opts[oidx] != MCMD_LinkADRReq) + break; + } + + // go back and apply the ADR changes, if any -- use the effective length, + // and process. + *presponse_fit = applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); + + return lastOidx; + } + +// scan mac commands starting at opts[] for olen, return count of bytes consumed. +// build response in pendMacData[], but limit length as needed; simply chop at last +// response that fits. +static int +scan_mac_cmds( + const uint8_t *opts, + int olen, + int port + ) { + int oidx = 0; + uint8_t cmd; + + LMIC.pendMacLen = 0; + if (port == 0) { + // port zero: mac data is in the normal payload, and there can't be + // piggyback mac data. + LMIC.pendMacPiggyback = 0; + } else { + // port is either -1 (no port) or non-zero (piggyback): treat as piggyback. + LMIC.pendMacPiggyback = 1; + } + + while( oidx < olen ) { + bit_t response_fit; + + response_fit = 1; + cmd = opts[oidx]; + + /* compute length, and exit for illegal commands */ + // cmdlen == 0 for error, or > 0 length of command. + int const cmdlen = getMacCmdSize(cmd); + if (cmdlen <= 0 || cmdlen > olen - oidx) { + // "the first unknown command terminates processing" + olen = oidx; + break; + } + + switch( cmd ) { + case MCMD_LinkCheckAns: { + // TODO(tmm@mcci.com) capture these, reliably.. + //int gwmargin = opts[oidx+1]; + //int ngws = opts[oidx+2]; + break; + } + // from 1.0.3 spec section 5.2: + // For the purpose of configuring the end-device channel mask, the end-device will + // process all contiguous LinkAdrReq messages, in the order present in the downlink message, + // as a single atomic block command. The end-device will accept or reject all Channel Mask + // controls in the contiguous block, and provide consistent Channel Mask ACK status + // indications for each command in the contiguous block in each LinkAdrAns message, + // reflecting the acceptance or rejection of this atomic channel mask setting. + // + // So we need to process all the contigious commands + case MCMD_LinkADRReq: { + // skip over all but the last command. + oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx, &response_fit); + break; + } + + case MCMD_DevStatusReq: { + // LMIC.snr is SNR times 4, convert to real SNR; rounding towards zero. + const int snr = (LMIC.snr + 2) / 4; + // per [1.02] 5.5. the margin is the SNR. + LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr)); + + response_fit = put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); + break; + } + +#if !defined(DISABLE_MCMD_RXParamSetupReq) + case MCMD_RXParamSetupReq: { + dr_t dr = (dr_t)(opts[oidx+1] & 0x0F); + u1_t rx1DrOffset = (u1_t)((opts[oidx+1] & 0x70) >> 4); + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); + LMIC.dn2Ans = 0xC0; // answer pending, but send this one in order. + if( validDR(dr) ) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX2DataRateACK; + if( freq != 0 ) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_ChannelACK; + if (rx1DrOffset <= 3) + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX1DrOffsetAck; + + if( LMIC.dn2Ans == (0xC0|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { + LMIC.dn2Dr = dr; + LMIC.dn2Freq = freq; + LMIC.rx1DrOffset = rx1DrOffset; + DO_DEVDB(LMIC.dn2Dr,dn2Dr); + DO_DEVDB(LMIC.dn2Freq,dn2Freq); + } + + /* put the first copy of the message */ + response_fit = put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); + break; + } +#endif // !DISABLE_MCMD_RXParamSetupReq + +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + case MCMD_RXTimingSetupReq: { + u1_t delay = opts[oidx+1] & MCMD_RXTimingSetupReq_Delay; + if (delay == 0) + delay = 1; + + LMIC.rxDelay = delay; + LMIC.macRxTimingSetupAns = 2; + response_fit = put_mac_uplink_byte(MCMD_RXTimingSetupAns); + break; + } +#endif // !DISABLE_MCMD_RXTimingSetupReq + +#if !defined(DISABLE_MCMD_DutyCycleReq) + case MCMD_DutyCycleReq: { + u1_t cap = opts[oidx+1]; + LMIC.globalDutyRate = cap & 0xF; + LMIC.globalDutyAvail = os_getTime(); + DO_DEVDB(cap,dutyCap); + + response_fit = put_mac_uplink_byte(MCMD_DutyCycleAns); + break; + } +#endif // !DISABLE_MCMD_DutyCycleReq + +#if !defined(DISABLE_MCMD_NewChannelReq) && CFG_LMIC_EU_like + case MCMD_NewChannelReq: { + u1_t chidx = opts[oidx+1]; // channel + u4_t raw_f_not_zero = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq + u1_t drs = opts[oidx+5]; // datarate span + u1_t ans = MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK; + + if (freq == 0 && raw_f_not_zero) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + u1_t MaxDR = drs >> 4; + u1_t MinDR = drs & 0xF; + if (MaxDR < MinDR || !validDR(MaxDR) || !validDR(MinDR)) { + ans &= ~MCMD_NewChannelAns_DataRateACK; + } + + if( ans == (MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK)) { + if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { + LMICOS_logEventUint32("NewChannelReq: setupChannel failed", ((u4_t)MaxDR << 24u) | ((u4_t)MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + } + + response_fit = put_mac_uplink_byte2(MCMD_NewChannelAns, ans); + break; + } +#endif // !DISABLE_MCMD_NewChannelReq + +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + case MCMD_DlChannelReq: { + u1_t chidx = opts[oidx+1]; // channel + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq + u1_t ans = MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK; + + if (freq == 0) { + ans &= ~MCMD_DlChannelAns_ChannelACK; + } + if (chidx > MAX_CHANNELS) { + // this is not defined by the 1.0.3 spec + ans = 0; + } else if ((LMIC.channelMap & (1 << chidx)) == 0) { + // the channel is not enabled for downlink. + ans &= ~MCMD_DlChannelAns_FreqACK; + } + + if( ans == (MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK)) { + LMIC.channelDlFreq[chidx] = freq; + } + + response_fit = put_mac_uplink_byte2(MCMD_DlChannelAns, ans); + // set sticky answer. + LMIC.macDlChannelAns = ans | 0xC0; + break; + } +#endif // !DISABLE_MCMD_DlChannelReq + +#if !defined(DISABLE_MCMD_PingSlotChannelReq) && !defined(DISABLE_PING) + case MCMD_PingSlotChannelReq: { + u4_t raw_f_not_zero = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; + u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); + u1_t dr = opts[oidx+4] & 0xF; + u1_t ans = MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK; + if (! raw_f_not_zero) { + freq = FREQ_PING; + } else if (freq == 0) { + ans &= ~MCMD_PingSlotFreqAns_ChannelACK; + } + if (! validDR(dr)) + ans &= ~MCMD_PingSlotFreqAns_DataRateACK; + + if (ans == (MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK)) { + LMIC.ping.freq = freq; + LMIC.ping.dr = dr; + DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); + DO_DEVDB(LMIC.ping.freq, pingFreq); + DO_DEVDB(LMIC.ping.dr, pingDr); + } + response_fit = put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); + break; + } +#endif // !DISABLE_MCMD_PingSlotChannelReq && !DISABLE_PING + +#if defined(ENABLE_MCMD_BeaconTimingAns) && !defined(DISABLE_BEACONS) + case MCMD_BeaconTimingAns: { + // Ignore if tracking already enabled or bcninfoTries == 0 + if( (LMIC.opmode & OP_TRACK) == 0 && LMIC.bcninfoTries != 0) { + LMIC.bcnChnl = opts[oidx+3]; + // Enable tracking - bcninfoTries + LMIC.opmode |= OP_TRACK; + // LMIC.bcninfoTries is cleared later in txComplete handling - triggers EV_BEACON_FOUND + // Setup RX parameters + LMIC.bcninfo.txtime = (LMIC.rxtime + + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BeaconTimingAns_TUNIT) + + ms2osticksCeil(MCMD_BeaconTimingAns_TUNIT/2) + - BCN_INTV_osticks); + LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared) + calcBcnRxWindowFromMillis(MCMD_BeaconTimingAns_TUNIT,1); // error of +/-N ms + + EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BeaconTimingAns, + e_.eui = MAIN::CDEV->getEui(), + e_.lostmic = Base::lsbf4(&d[pend]), + e_.info = (LMIC.missedBcns | + (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks + - LMIC.bcnRxtime) << 8)), + e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); + } + break; + } /* end case */ +#endif // !ENABLE_MCMD_BeaconTimingAns && !DISABLE_BEACONS + +#if LMIC_ENABLE_TxParamSetupReq + case MCMD_TxParamSetupReq: { + uint8_t txParam; + txParam = opts[oidx+1]; + + // we don't allow unrecognized bits to get to txParam. + txParam &= (MCMD_TxParam_RxDWELL_MASK| + MCMD_TxParam_TxDWELL_MASK| + MCMD_TxParam_MaxEIRP_MASK); + LMIC.txParam = txParam; + response_fit = put_mac_uplink_byte(MCMD_TxParamSetupAns); + break; + } /* end case */ +#endif // LMIC_ENABLE_TxParamSetupReq + +#if LMIC_ENABLE_DeviceTimeReq + case MCMD_DeviceTimeAns: { + // don't process a spurious downlink. + if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_rx ) { + // remember that it's time to notify the client. + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_success; + + // the network time is linked to the time of the last TX. + LMIC.localDeviceTime = LMIC.txend; + + // save the network time. + // The first 4 bytes contain the seconds since the GPS epoch + // (i.e January the 6th 1980 at 00:00:00 UTC). + // Note: as per the LoRaWAN specs, the octet order for all + // multi-octet fields is little endian + // Note: the casts are necessary, because opts is an array of + // single byte values, and they might overflow when shifted + LMIC.netDeviceTime = ( (lmic_gpstime_t) opts[oidx + 1] ) | + (((lmic_gpstime_t) opts[oidx + 2]) << 8) | + (((lmic_gpstime_t) opts[oidx + 3]) << 16) | + (((lmic_gpstime_t) opts[oidx + 4]) << 24); + + // The 5th byte contains the fractional seconds in 2^-8 second steps + LMIC.netDeviceTimeFrac = opts[oidx + 5]; +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": MAC command DeviceTimeAns received: seconds_since_gps_epoch=%"PRIu32", fractional_seconds=%d\n", os_getTime(), LMIC.netDeviceTime, LMIC.netDeviceTimeFrac); +#endif + } + break; + } /* end case */ +#endif // LMIC_ENABLE_DeviceTimeReq + + default: { + // force olen to current oidx so we'll exit the while() + olen = oidx; + break; + } /* end case */ + } /* end switch */ + + /* if we're out of spce for responses, skip to end. */ + if (! response_fit) { + olen = oidx; + } else { + oidx += cmdlen; + } + } /* end while */ + + return oidx; +} + +// change the ADR ack request count, unless adr ack is diabled. +static void setAdrAckCount (s2_t count) { + if (LMIC.adrAckReq != LINK_CHECK_OFF) { + LMIC.adrAckReq = count; + } +} + +static bit_t decodeFrame (void) { + xref2u1_t d = LMIC.frame; + u1_t hdr = d[0]; + u1_t ftype = hdr & HDR_FTYPE; + int dlen = LMIC.dataLen; +#if LMIC_DEBUG_LEVEL > 0 + const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); +#endif + if (dlen > 0) + LMICOS_logEventUint32("decodeFrame", (dlen << 8) | (hdr << 0)); + + if( dlen < OFF_DAT_OPTS+4 || + (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || + (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) { + // Basic sanity checks failed + EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]), + e_.info2 = hdr + (dlen<<8))); + norx: +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Invalid downlink, window=%s\n", os_getTime(), window); +#endif + LMIC.dataLen = 0; + return 0; + } + // Validate exact frame length + // Note: device address was already read+evaluated in order to arrive here. + int fct = d[OFF_DAT_FCT]; + u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]); + u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]); + int olen = fct & FCT_OPTLEN; + int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame + int poff = OFF_DAT_OPTS+olen; + int pend = dlen-4; // MIC + + if( addr != LMIC.devaddr ) { + LMICOS_logEventUint32("decodeFrame: wrong address", addr); + + EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, + e_.eui = MAIN::CDEV->getEui(), + e_.info = addr, + e_.info2 = LMIC.devaddr)); + goto norx; + } + if( poff > pend ) { + LMICOS_logEventUint32("decodeFrame: corrupted frame", ((u4_t)dlen << 16) | (fct << 8) | (poff - pend)); + EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16))); + goto norx; + } + + int port = -1; + int replayConf = 0; + + if( pend > poff ) + port = d[poff++]; + + // compute the 32-bit sequence number based on the 16-bit sequence number received + // and the internal 32-bit number. Because the 32-bit number is used in the MIC + // calculation, this must be right. (And if you're curious why a 32-bit seqno matters, + // it's this calculation, plus its use in the MIC calculation.) + // + // we have to be careful to get the right value for replay of last message received. + u2_t seqnoDiff = (u2_t)(seqno - LMIC.seqnoDn); + if (seqnoDiff == 0xFFFFu) { + seqno = LMIC.seqnoDn - 1; + } else { + seqno = LMIC.seqnoDn + seqnoDiff; + } + + if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { + LMICOS_logEventUint32("decodeFrame: bad MIC", os_rlsbf4(&d[pend])); + EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, + e_.eui1 = MAIN::CDEV->getEui(), + e_.info1 = Base::lsbf4(&d[pend]), + e_.info2 = seqno, + e_.info3 = LMIC.devaddr)); + goto norx; + } + if( seqno < LMIC.seqnoDn ) { + if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + LMICOS_logEventUint32("decodeFrame: rollover discarded", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + if( seqno != LMIC.seqnoDn-1 || !LMIC.lastDnConf || ftype != HDR_FTYPE_DCDN ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + LMICOS_logEventUint32("decodeFrame: Retransmit confimed discarded", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + // Replay of previous sequence number allowed only if + // previous frame and repeated both requested confirmation + // but set a flag, so we don't actually process the message. + LMICOS_logEventUint32("decodeFrame: Retransmit confimed accepted", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + replayConf = 1; + LMIC.dnConf = FCT_ACK; + } + else { + if( seqnoDiff > LMICbandplan_MAX_FCNT_GAP) { + LMICOS_logEventUint32("decodeFrame: gap too big", ((u4_t)seqnoDiff << 16) | (seqno & 0xFFFFu)); + goto norx; + } + if( seqno > LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + } + LMIC.seqnoDn = seqno+1; // next number to be expected + DO_DEVDB(LMIC.seqnoDn,seqnoDn); + // DN frame requested confirmation - provide ACK once with next UP frame + LMIC.dnConf = LMIC.lastDnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0); + if (LMIC.dnConf) + LMICOS_logEventUint32("decodeFrame: Confirmed downlink", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + } + + if (port == 0 && olen != 0 && pend > poff) { + // we have a port-zero message, and piggyback mac data. + // discard, section 4.3.1.6 line 544-546 +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); +#endif + goto norx; + } + + if( LMIC.dnConf || (fct & FCT_MORE) ) + LMIC.opmode |= OP_POLL; + + // We heard from network + LMIC.adrChanged = LMIC.rejoinCnt = 0; + setAdrAckCount(LINK_CHECK_INIT); +#if !defined(DISABLE_MCMD_RXParamSetupReq) + // We heard from network "on a Class A downlink" + LMIC.dn2Ans = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // We heard from network "on a Class A downlink" + LMIC.macRxTimingSetupAns = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + LMIC.macDlChannelAns = 0; +#endif + + int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps); + // for legacy reasons, LMIC.margin is set to the unsigned sensitivity. It can never be negative. + // it's only computed for legacy clients + LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; + + // even if it's a replay confirmed, we process the mac options. + xref2u1_t opts = &d[OFF_DAT_OPTS]; + int oidx = scan_mac_cmds(opts, olen, port); + if( oidx != olen ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = 0x1000000 + (oidx) + (olen<<8))); + oidx = olen; + } + + if( !replayConf ) { + // Handle payload only if not a replay + // Decrypt payload - if any + if( port >= 0 && pend-poff > 0 ) { + aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff); + if (port == 0) { + // this is a mac command. scan the options. +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff); +#endif + int optendindex = scan_mac_cmds(d+poff, pend-poff, port); + if (optendindex != pend-poff) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF( + "%"LMIC_PRId_ostime_t": error processing mac commands for port 0 " + "(len=%#x, optendindex=%#x)\n", + os_getTime(), pend-poff, optendindex + ); +#endif + } + // wait to transmit until txcomplete: above. + } + } // end decrypt payload + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = seqno, + e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN, + e_.mic = Base::lsbf4(&d[pend]), + e_.hdr = d[LORA::OFF_DAT_HDR], + e_.fct = d[LORA::OFF_DAT_FCT], + e_.port = port, + e_.plen = dlen, + e_.opts.length = olen, + memcpy(&e_.opts[0], opts, olen))); + } else { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = Base::lsbf4(&d[pend]), + e_.info2 = seqno)); + // discard the data + LMICOS_logEventUint32("decodeFrame: discarding replay", ((u4_t)seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; + } + + if( // NWK acks but we don't have a frame pending + (ackup && LMIC.txCnt == 0) || + // We sent up confirmed and we got a response in DNW1/DNW2 + // BUT it did not carry an ACK - this should never happen + // Do not resend and assume frame was not ACKed. + (!ackup && LMIC.txCnt != 0) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK, + e_.eui = MAIN::CDEV->getEui(), + e_.info = seqno, + e_.info2 = ackup)); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt); +#endif + } + + if( LMIC.txCnt != 0 ) // we requested an ACK + orTxrxFlags(__func__, ackup ? TXRX_ACK : TXRX_NACK); + + if( port <= 0 ) { + orTxrxFlags(__func__, TXRX_NOPORT); + LMIC.dataBeg = poff; + LMIC.dataLen = 0; + } else { + orTxrxFlags(__func__, TXRX_PORT); + LMIC.dataBeg = poff; + LMIC.dataLen = pend-poff; + } +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags); +#endif + return 1; +} + + +// ================================================================================ +// TX/RX transaction support + +// start reception and log. +static void radioRx (void) { + reportEventNoUpdate(EV_RXSTART); + os_radio(RADIO_RX); +} + +// start RX in window 2. +static void setupRx2 (void) { + initTxrxFlags(__func__, TXRX_DNW2); + LMIC.rps = dndr2rps(LMIC.dn2Dr); + LMIC.freq = LMIC.dn2Freq; + LMIC.dataLen = 0; + radioRx(); +} + +//! \brief Adjust the delay (in ticks) of the target window-open time from nominal. +//! \param hsym the duration of one-half symbol in osticks. +//! \param rxsyms_in the nominal window length -- minimum length of time to delay. +//! \return Effective delay to use (positive for later, negative for earlier). +//! \post LMIC.rxsyms is set to the number of rxsymbols to be used for preamble timeout. +//! \bug For FSK, the radio driver ignores LMIC.rxsysms, and uses a fixed value of 4080 bits +//! (81 ms) +//! +//! \details The calculation of the RX Window opening time has to balance several things. +//! The system clock might be inaccurate. Generally, the LMIC assumes that the timebase +//! is accurage to 100 ppm, or 0.01%. 0.01% of a 6 second window is 600 microseconds. +//! For LoRa, the fastest data rates of interest is SF7 (1024 us/symbol); with an 8-byte +//! preamble, the shortest preamble is 8.092ms long. If using FSK, the symbol rate is +//! 20 microseconds, but the preamble is 8*5 bits long, so the preamble is 800 microseconds. +//! Unless LMIC_ENABLE_arbitrary_clock_error is true, we fold clock errors of > 0.4% back +//! to 0.4%. +ostime_t LMICcore_adjustForDrift (ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in) { + ostime_t rxoffset; + + // decide if we want to move left or right of the reference time. + rxoffset = -LMICbandplan_RX_EXTRA_MARGIN_osticks; + + u2_t clockerr = LMIC.client.clockError; + + // Most crystal oscillators are 100 ppm. If things are that tight, there's + // no point in specifying a drift, as 6 seconds at 100ppm is +/- 600 microseconds. + // We position the windows at the front, and there's some extra margin, so... + // don't bother setting values <= 100 ppm. + if (clockerr != 0) + { + // client has set clock error. Limit this to 0.1% unless there's + // a compile-time configuration. (In other words, assume that millis() + // clock is accurate to 0.1%.) You should never use clockerror to + // compensate for system-late problems. + u2_t const maxError = LMIC_kMaxClockError_ppm * MAX_CLOCK_ERROR / (1000 * 1000); + if (! LMIC_ENABLE_arbitrary_clock_error && clockerr > maxError) + { + clockerr = maxError; + } + } + + // If the clock is slow, the window needs to open earlier in our time + // in order to open at or before the specified time (in real world),. + // Don't bother to round, as this is very fine-grained. + ostime_t drift = (ostime_t)(((int64_t)delay * clockerr) / MAX_CLOCK_ERROR); + + // calculate the additional rxsyms needed to hit the window nominally. + ostime_t const tsym = 2 * hsym; + ostime_t driftwin; + driftwin = 2 * drift; + if (rxoffset < 0) + driftwin += -rxoffset; + // else we'll hit the window nominally. + + rxsyms_in += (driftwin + tsym - 1) / tsym; + + // reduce the rxoffset by the drift; this compensates for a slow clock; + // but it makes the rxtime too early by approximately `drift` if clock + // is fast. + rxoffset -= drift; + + setRxsyms(rxsyms_in); + + return delay + rxoffset; +} + +static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) { + ostime_t hsym = dr2hsym(dr); + + // Schedule the start of the receive window. os_getRadioRxRampup() is used to make sure we + // exit "sleep" well enough in advance of the receive window to be able to + // time things accurately. + // + // This also sets LMIC.rxsyms. This is NOT normally used for FSK; see LMICbandplan_txDoneFSK() + LMIC.rxtime = LMIC.txend + LMICcore_adjustForDrift(delay, hsym, LMICbandplan_MINRX_SYMS_LoRa_ClassA); + + LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": sched Rx12 %"LMIC_PRId_ostime_t"\n", os_getTime(), LMIC.rxtime - os_getRadioRxRampup()); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - os_getRadioRxRampup(), func); +} + +static void setupRx1 (osjobcb_t func) { + initTxrxFlags(__func__, TXRX_DNW1); + // Turn LMIC.rps from TX over to RX + LMIC.rps = setNocrc(LMIC.rps,1); + LMIC.dataLen = 0; + LMIC.osjob.func = func; + radioRx(); +} + + +// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime +static void txDone (ostime_t delay, osjobcb_t func) { +#if !defined(DISABLE_PING) + if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) { + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! + LMIC.opmode |= OP_PINGINI; + } +#endif // !DISABLE_PING + + // Change RX frequency (can happen even for EU-like if programmed by DlChannelReq) + // change params and rps (US only) before we increment txChnl + LMICbandplan_setRx1Params(); + + // LMIC.dndr carries the TX datarate (can be != LMIC.datarate [confirm retries etc.]) + // Setup receive -- either schedule FSK or schedule rx1 or rx2 window. + if( LMICbandplan_isFSK() ) { + LMICbandplan_txDoneFSK(delay, func); + } + else + { + schedRx12(delay, func, LMIC.dndr); + } +} + +// ======================================== Join frames + + +#if !defined(DISABLE_JOIN) +static void onJoinFailed (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // Notify app - must call LMIC_reset() to stop joining + // otherwise join procedure continues. + reportEventAndUpdate(EV_JOIN_FAILED); +} + +// process join-accept message or deal with no join-accept in slot 2. +static bit_t processJoinAccept (void) { + if ((LMIC.txrxFlags & TXRX_DNW1) != 0 && LMIC.dataLen == 0) + return 0; + + // formerly we asserted. + if ((LMIC.opmode & OP_TXRXPEND) == 0) + // nothing we can do. + return 1; + + // formerly we asserted. + if ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) == 0) { + // we shouldn't be here. just drop the frame, but clean up txrxpend. + return processJoinAccept_badframe(); + } + + if( LMIC.dataLen == 0 ) { + // we didn't get any data and we're in slot 2. So... there's no join frame. + return processJoinAccept_nojoinframe(); + } + + u1_t hdr = LMIC.frame[0]; + u1_t dlen = LMIC.dataLen; + u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt! + LMIC_EV_VARIABLE(mic); // only used by EV(). + + if( (dlen != LEN_JA && dlen != LEN_JAEXT) + || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : mic, + e_.info2 = hdr + (dlen<<8))); + return processJoinAccept_badframe(); + } + aes_encrypt(LMIC.frame+1, dlen-1); + if( !aes_verifyMic0(LMIC.frame, dlen-4) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC, + e_.info = mic)); + return processJoinAccept_badframe(); + } + + u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR); + LMIC.devaddr = addr; + LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF; + + // initDefaultChannels(0) for EU-like, nothing otherwise + LMICbandplan_joinAcceptChannelClear(); + + if (!LMICbandplan_hasJoinCFlist() && dlen > LEN_JA) { + // if no JoinCFList, we're supposed to continue + // the join per 2.2.5 of LoRaWAN regional 2.2.4 + // https://github.com/mcci-catena/arduino-lmic/issues/19 + } else if ( LMICbandplan_hasJoinCFlist() && dlen > LEN_JA ) { + dlen = OFF_CFLIST; + for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) { + u4_t freq = LMICbandplan_convFreq(&LMIC.frame[dlen]); + if( freq ) { + LMIC_setupChannel(chidx, freq, 0, -1); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq); +#endif + } + } + } + + // already incremented when JOIN REQ got sent off + aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey); + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + + EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(), + e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.oldaddr = oldaddr, + e_.nonce = LMIC.devNonce-1, + e_.mic = mic, + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_ACCEPT + : EV::joininfo_t::ACCEPT))); + + // + // XXX(tmm@mcci.com) OP_REJOIN confuses me, and I'm not sure why we're + // adjusting DRs here. We've just received a join accept, and the + // datarate therefore shouldn't be in play. In effect, we set the + // initial data rate based on the number of times we tried to rejoin. + // + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // TODO(tmm@mcci.com) regionalize + // Lower DR every try below current UP DR + // need to check feasibility? join feasability is default. + LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt); +#else + // in the join of AS923 v1.1 or older, only DR2 (SF10) is used. + // TODO(tmm@mcci.com) if the rejoin logic is at all correct, we + // should be setting the uplink datarate based on the number of + // tries; this doesn't set the AS923 join data rate. + LMIC.datarate = AS923_DR_SF10; +#endif + } + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); + LMIC.opmode |= OP_NEXTCHNL; + LMIC.txCnt = 0; + stateJustJoined(); + // transition to the ADR_ACK initial state. + setAdrAckCount(LINK_CHECK_INIT); + + LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F; + LMIC.rx1DrOffset = (LMIC.frame[OFF_JA_DLSET] >> 4) & 0x7; + LMIC.rxDelay = LMIC.frame[OFF_JA_RXDLY]; + if (LMIC.rxDelay == 0) LMIC.rxDelay = 1; + reportEventAndUpdate(EV_JOINED); + return 1; +} + +static bit_t processJoinAccept_badframe(void) { + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + // continue the join process: there's another window. + return 0; + else + // stop the join process + return processJoinAccept_nojoinframe(); +} + +static bit_t processJoinAccept_nojoinframe(void) { + // Valid states are JOINING (in which caise REJOIN is ignored) + // or ~JOINING and REJOIN. If it's a REJOIN, + // we need to turn off rejoin, signal an event, and increment + // the rejoin-sent count. Internal callers will turn on rejoin + // occasionally. + if( (LMIC.opmode & OP_JOINING) == 0) { + // formerly, we asserted ((LMIC.opmode & OP_REJOIN) != 0); + // but now we just return 1 if it's not asserted. + if ( (LMIC.opmode & OP_REJOIN) == 0) { + LMIC.opmode &= ~OP_TXRXPEND; + return 1; + } + LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND); + if( LMIC.rejoinCnt < 10 ) + LMIC.rejoinCnt++; + reportEventAndUpdate(EV_REJOIN_FAILED); + // stop the join process. + return 1; + } + // otherwise it's a normal join. At end of rx2, so we + // need to schedule something. + LMIC.opmode &= ~OP_TXRXPEND; + reportEventNoUpdate(EV_JOIN_TXCOMPLETE); + int failed = LMICbandplan_nextJoinState(); + EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.datarate|DR_PAGE, + e_.info2 = failed)); + // Build next JOIN REQUEST with next engineUpdate call + // Optionally, report join failed. + // Both after a random/chosen amount of ticks. That time + // is in LMIC.txend. The delay here is either zero or 1 + // tick; onJoinFailed()/runEngineUpdate() are responsible + // for honoring that. XXX(tmm@mcci.com) The IBM 1.6 code + // claimed to return a delay but really returns 0 or 1. + // Once we update as923 to return failed after dr2, we + // can take out this #if. + os_setTimedCallback(&LMIC.osjob, os_getTime()+failed, + failed + ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed + : FUNC_ADDR(runEngineUpdate)); // next step to be delayed + // stop this join process. + return 1; +} + +static void processRx2Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + } + // we're done with this join cycle anyway, so ignore the + // result of processJoinAccept() + (void) processJoinAccept(); +} + + +static void setupRx2Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); + setupRx2(); +} + + +static void processRx1Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 || !processJoinAccept() ) + schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr); +} + + +static void setupRx1Jacc (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + setupRx1(FUNC_ADDR(processRx1Jacc)); +} + + +static void jreqDone (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc)); +} + +#endif // !DISABLE_JOIN + +// ======================================== Data frames + +// Fwd decl. +static bit_t processDnData(void); + +static void processRx2DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + // It could be that the gateway *is* sending a reply, but we + // just didn't pick it up. To avoid TX'ing again while the + // gateay is not listening anyway, delay the next transmission + // until DNW2_SAFETY_ZONE from now, and add up to 2 seconds of + // extra randomization. + // BUG(tmm@mcci.com) this delay is not needed for some + // regions, e.g. US915 and AU915, which have non-overlapping + // uplink and downlink. + txDelay(os_getTime() + DNW2_SAFETY_ZONE, 2); + } + processDnData(); +} + + +static void setupRx2DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processRx2DnData); + setupRx2(); +} + + +static void processRx1DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen == 0 || !processDnData() ) + schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr); +} + + +static void setupRx1DnData (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + setupRx1(FUNC_ADDR(processRx1DnData)); +} + + +static void updataDone (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData)); +} + +// ======================================== + +static bit_t sendAdrAckReq(void) { + if (LMIC.adrAckReq < LINK_CHECK_CONT) { + return 0; + } else if (LMIC.adrAckReq <= LINK_CHECK_DEAD) { + return 1; + } else if (LMIC.adrAckReq <= LINK_CHECK_DEAD + 32) { + // for compliance, though it's not clear why they care, we stop sending requests + // when we're right at the DEAD state + return 0; + } else if (LMIC.adrAckReq <= LINK_CHECK_UNJOIN - 32) { + return 0; + } else { + // otherwise, if our alternative is to unjoin and we have no other info, keep + // asking for a downlink. + return 1; + } +} + +static bit_t buildDataFrame (void) { + bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL); + u1_t dlen = txdata ? LMIC.pendTxLen : 0; + + // Piggyback MAC options + // Prioritize by importance + // highest importance are the ones in the pendMac buffer. + int end = OFF_DAT_OPTS; + + // Send piggyback data if: !txdata or txport != 0 + if ((! txdata || LMIC.pendTxPort != 0) && LMIC.pendMacPiggyback && LMIC.pendMacLen != 0) { + os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); + end += LMIC.pendMacLen; + } + LMIC.pendMacLen = 0; + LMIC.pendMacPiggyback = 0; + +#if !defined(DISABLE_MCMD_RXParamSetupReq) + // per 5.4, RxParamSetupAns is sticky. + if (LMIC.dn2Ans) { + if (LMIC.dn2Ans & 0x40) { + LMIC.dn2Ans ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_RXParamSetupAns; + LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU; + end += 2; + } + } +#endif // !DISABLE_MCMD_RXParamSetupReq +#if !defined(DISABLE_MCMD_DlChannelReq) + // per 5.4, DlChannelAns is sticky. + if (LMIC.macDlChannelAns) { + if (LMIC.macDlChannelAns & 0x40) { + LMIC.macDlChannelAns ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_DlChannelAns; + LMIC.frame[end + 1] = LMIC.macDlChannelAns & ~MCMD_DlChannelAns_RFU; + end += 2; + } + } +#endif // !DISABLE_MCMD_DlChannelReq +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // per 5.7, RXTimingSetupAns is sticky + if (LMIC.macRxTimingSetupAns == 2) { + LMIC.macRxTimingSetupAns = 1; + } else if (LMIC.macRxTimingSetupAns) { + LMIC.frame[end++] = MCMD_RXTimingSetupAns; + } +#endif // !DISABLE_MCMD_RXTimingSetupReq) + +#if LMIC_ENABLE_DeviceTimeReq + if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { + LMIC.frame[end+0] = MCMD_DeviceTimeReq; + end += 1; + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx; + } +#endif // LMIC_ENABLE_DeviceTimeReq +#if !defined(DISABLE_BEACONS) && defined(ENABLE_MCMD_BeaconTimingAns) + if ( LMIC.bcninfoTries > 0 ) { + LMIC.frame[end+0] = MCMD_BeaconInfoReq; + end += 1; + } +#endif + if (end > OFF_DAT_OPTS + 16) { + LMICOS_logEventUint32("piggyback mac opts too long", end); + return 0; + } + + if( LMIC.adrChanged ) { + // if ADR is enabled, and we were just counting down the + // transmits before starting an ADR, advance the timer so + // we'll do an ADR now. + if (LMIC.adrAckReq < LINK_CHECK_CONT) + setAdrAckCount(LINK_CHECK_CONT); + LMIC.adrChanged = 0; + } + + unsigned int flen = end + (txdata ? 5+dlen : 4); + if( flen > MAX_LEN_FRAME ) { + // Options and payload too big - delay payload + txdata = 0; + flen = end+4; + } + + u1_t maxFlen = LMICbandplan_maxFrameLen(LMIC.datarate); + + if (flen > maxFlen) { + LMICOS_logEventUint32("frame too long for this bandplan", ((u4_t)dlen << 16) | (flen << 8) | maxFlen); + return 0; + } + + LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1; + LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled + | (sendAdrAckReq() ? FCT_ADRACKReq : 0) + | (end-OFF_DAT_OPTS)); + os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); + + if( LMIC.txCnt == 0 && LMIC.upRepeatCount == 0 ) { + LMIC.seqnoUp += 1; + DO_DEVDB(LMIC.seqnoUp,seqnoUp); + } else { + LMICOS_logEventUint32("retransmit", ((u4_t)LMIC.frame[OFF_DAT_FCT] << 24u) | ((u4_t)LMIC.txCnt << 16u) | (LMIC.upRepeatCount << 8u) | (LMIC.upRepeat<<0u)); + EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoUp-1, + e_.info2 = ((LMIC.txCnt+1) | + (LMIC.upRepeatCount << 8) | + ((LMIC.datarate|DR_PAGE)<<16)))); + } + os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1); + + // Clear pending DN confirmation + LMIC.dnConf = 0; + + if( txdata ) { + if( LMIC.pendTxConf ) { + // Confirmed only makes sense if we have a payload (or at least a port) + LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1; + if( LMIC.txCnt == 0 ) LMIC.txCnt = 1; + } else if (LMIC.upRepeat != 0) { + // we are repeating. So we need to count here. + if (LMIC.upRepeatCount == 0) { + LMIC.upRepeatCount = 1; + } + } + LMIC.frame[end] = LMIC.pendTxPort; + os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen); + aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey, + LMIC.devaddr, LMIC.seqnoUp-1, + /*up*/0, LMIC.frame+end+1, dlen); + } + aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4); + + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = LMIC.seqnoUp-1, + e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP), + e_.mic = Base::lsbf4(&LMIC.frame[flen-4]), + e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR], + e_.fct = LMIC.frame[LORA::OFF_DAT_FCT], + e_.port = LMIC.pendTxPort, + e_.plen = txdata ? dlen : 0, + e_.opts.length = end-LORA::OFF_DAT_OPTS, + memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS))); + LMIC.dataLen = flen; + return 1; +} + + +#if !defined(DISABLE_BEACONS) +// Callback from HAL during scan mode or when job timer expires. +static void onBcnRx (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // If we arrive via job timer make sure to put radio to rest. + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + if( LMIC.dataLen == 0 ) { + // Nothing received - timeout + LMIC.opmode &= ~(OP_SCAN | OP_TRACK); + reportEventAndUpdate(EV_SCAN_TIMEOUT); + return; + } + if( ! LMIC_BEACON_SUCCESSFUL(decodeBeacon()) ) { + // Something is wrong with the beacon - continue scan + LMIC.dataLen = 0; + os_radio(RADIO_RXON); + os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx)); + return; + } + // Found our 1st beacon + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,1); + LMIC.opmode &= ~OP_SCAN; // turn SCAN off + LMIC.opmode |= OP_TRACK; // auto enable tracking + reportEventAndUpdate(EV_BEACON_FOUND); // can be disabled in callback +} + + +// Enable receiver to listen to incoming beacons +// netid defines when scan stops (any or specific beacon) +// This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON +// Implicitely cancels any pending TX/RX transaction. +// Also cancels an onpoing joining procedure. +static void startScan (void) { + // formerly, we asserted. + if (LMIC.devaddr == 0 || (LMIC.opmode & OP_JOINING) != 0) + return; + if( (LMIC.opmode & OP_SHUTDOWN) != 0 ) + return; + // Cancel onging TX/RX transaction + LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0; + LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND); + LMICbandplan_setBcnRxParams(); + LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx)); + os_radio(RADIO_RXON); +} + + +bit_t LMIC_enableTracking (u1_t tryBcnInfo) { + if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 ) + return 0; // already in progress or failed to enable + // If BCN info requested from NWK then app has to take are + // of sending data up so that MCMD_BeaconInfoReq can be attached. + if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) + startScan(); + return 1; // enabled +} + + +void LMIC_disableTracking (void) { + LMIC.opmode &= ~(OP_SCAN|OP_TRACK); + LMIC.bcninfoTries = 0; + engineUpdate(); +} +#endif // !DISABLE_BEACONS + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ================================================================================ +// +// Join stuff +// +// ================================================================================ + +#if !defined(DISABLE_JOIN) +static void buildJoinRequest (u1_t ftype) { + // Do not use pendTxData since we might have a pending + // user level frame in there. Use RX holding area instead. + xref2u1_t d = LMIC.frame; + d[OFF_JR_HDR] = ftype; + os_getArtEui(d + OFF_JR_ARTEUI); + os_getDevEui(d + OFF_JR_DEVEUI); + os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce); + aes_appendMic0(d, OFF_JR_MIC); + + EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(), + e_.arteui = MAIN::CDEV->getArtEui(), + e_.nonce = LMIC.devNonce, + e_.oldaddr = LMIC.devaddr, + e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]), + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_REQUEST + : EV::joininfo_t::REQUEST))); + LMIC.dataLen = LEN_JR; + LMIC.devNonce++; + DO_DEVDB(LMIC.devNonce,devNonce); +} + +static void startJoining (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + // see issue #244: for backwards compatibility + // don't override what the user does after os_init(). + if (LMIC.initBandplanAfterReset) + LMICbandplan_resetDefaultChannels(); + else + LMIC.initBandplanAfterReset = 1; + + // let the client know that now's the time to update + // network settings. + reportEventAndUpdate(EV_JOINING); +} + +// reset the joined-to-network state (and clean up) +void LMIC_unjoin(void) { + // reset any joining flags + LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_UNJOIN); + + // put us in unjoined state: + LMIC.devaddr = 0; + + // clear transmit. + LMIC_clrTxData(); +} + +// Start join procedure if not already joined. +bit_t LMIC_startJoining (void) { + if( LMIC.devaddr == 0 ) { + // There should be no TX/RX going on + // ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0); + LMIC.opmode &= ~OP_POLL; + // Lift any previous duty limitation + LMIC.globalDutyRate = 0; + // Cancel scanning + LMIC.opmode &= ~(OP_SCAN|OP_UNJOIN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL); + // Setup state + LMIC.rejoinCnt = LMIC.txCnt = 0; + resetJoinParams(); + LMICbandplan_initJoinLoop(); + LMIC.opmode |= OP_JOINING; + // reportEventAndUpdate will call engineUpdate which then starts sending JOIN REQUESTS + os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining)); + return 1; + } + return 0; // already joined +} + +static void unjoinAndRejoin(xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + LMIC_unjoin(); + LMIC_startJoining(); +} + +// do a deferred unjoin and rejoin, so not in engineupdate. +void LMIC_unjoinAndRejoin(void) { + os_setCallback(&LMIC.osjob, FUNC_ADDR(unjoinAndRejoin)); +} + +#endif // !DISABLE_JOIN + + +// ================================================================================ +// +// +// +// ================================================================================ + +#if !defined(DISABLE_PING) +static void processPingRx (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + if( LMIC.dataLen != 0 ) { + initTxrxFlags(__func__, TXRX_PING); + if( decodeFrame() ) { + reportEventNoUpdate(EV_RXCOMPLETE); + } + } + // Pick next ping slot + engineUpdate(); +} +#endif // !DISABLE_PING + +// process downlink data at close of RX window. Return zero if another RX window +// should be scheduled, non-zero to prevent scheduling of RX2 (if relevant). +// Confusingly, the caller actualyl does some of the calculation, so the answer from +// us is not always totaly right; the rx1 window check ignores our result unless +// LMIC.datalen was non zero before calling. +// +// Inputs: +// LMIC.dataLen number of bytes receieved; 0 --> no message at all received. +// LMIC.txCnt currnt confirmed uplink count, or 0 for unconfirmed. +// LMIC.txrxflags state of play for the Class A engine and message receipt. +// +// and many other flags in txcomplete(). + +// forward references. +static bit_t processDnData_norx(void); +static bit_t processDnData_txcomplete(void); + +static bit_t processDnData (void) { + // if no TXRXPEND, we shouldn't be here and can do nothign. + // formerly we asserted. + if ((LMIC.opmode & OP_TXRXPEND) == 0) + return 1; + + if( LMIC.dataLen == 0 ) { + // if this is an RX1 window, shouldn't we return 0 to schedule + // RX2? in fact, the rx1 caller ignores what we return, and + // norx() doesn't call txcomplete if this is RX1. + return processDnData_norx(); + } + // if we get here, LMIC.dataLen != 0, so there is some + // traffic. + else if( !decodeFrame() ) { + // if we are in downlink window 1, we need to schedule + // downlink window 2. + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + return 0; + else + // otherwise we are in downlink window 2; we will not + // get any more downlink traffic from this uplink, so we need + // to close the books on this uplink attempt + return processDnData_norx(); + } + // downlink frame was accepted. This means that we're done. Except + // there's one bizarre corner case. If we sent a confirmed message + // and got a downlink that didn't have an ACK, we have to retry. + // It is not clear why the network is permitted to do this; the + // fact that they scheduled a downlink for us during one of the RX + // windows is clear confirmation that the uplink made it to the + // network and was valid. However, compliance checks this, so + // we have to handle it and retransmit. + else if (LMIC.txCnt != 0 && (LMIC.txrxFlags & TXRX_NACK) != 0) + { + // grr. we're confirmed but the network downlink did not + // set the ACK bit. We know txCnt is non-zero, so this + // will immediately fall into the retransmit path. We don't + // want to do this unless it's a confirmed uplink. + return processDnData_norx(); + } + // the transmit of the uplink is really complete. + else { + return processDnData_txcomplete(); + } +} + +// nothing was received this window. +static bit_t processDnData_norx(void) { + if( LMIC.txCnt != 0 ) { + if( LMIC.txCnt < TXCONF_ATTEMPTS ) { + // Per [1.0.3] section 18.4, it is recommended that the device adjust datarate down. + // The spec is not clear about what should happen in case the data size is too large + // for the new frame len, but it seems that we should leave theframe len at the new + // data size. Therefore, we set the new data rate here, and then check at transmit time + // whether the packet is now too large; if so, we abandon the transmission. + LMIC.txCnt += 1; + // becase txCnt was at least 1 when we entered this branch, this if() will be taken + // for txCnt == 3, 5, 7. + if (LMIC.txCnt & 1) { + dr_t adjustedDR; + // lower DR + adjustedDR = decDR(LMIC.datarate); + setDrTxpow(DRCHG_NOACK, adjustedDR, KEEP_TXPOW); + } + + // TODO(tmm@mcci.com): check feasibility of lower datarate + // Schedule another retransmission + txDelay(LMIC.rxtime, RETRY_PERIOD_secs); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // confirmed uplink is complete without an ack: no port and no flag + initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); + } else if (LMIC.upRepeatCount != 0) { + if (LMIC.upRepeatCount < LMIC.upRepeat) { + LMICOS_logEventUint32("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); + LMIC.upRepeatCount += 1; + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // counted out: nothing received. + initTxrxFlags(__func__, TXRX_NOPORT); + } else { + // Nothing received - implies no port + initTxrxFlags(__func__, TXRX_NOPORT); + } + setAdrAckCount(LMIC.adrAckReq + 1); + LMIC.dataBeg = LMIC.dataLen = 0; + + return processDnData_txcomplete(); +} + +// this Class-A uplink-and-receive cycle is complete. +static bit_t processDnData_txcomplete(void) { + LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + // turn off all the repeat stuff. + LMIC.txCnt = LMIC.upRepeatCount = 0; + + // if there's pending mac data that's not piggyback, launch it now. + if (LMIC.pendMacLen != 0) { + if (LMIC.pendMacPiggyback) { + LMICOS_logEvent("piggyback mac message"); + LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. + } else { + // Every mac command on port 0 requires an uplink, if there's data. + // TODO(tmm@mcci.com) -- this is why we need a queueing structure for + // uplinks. + // open code the logic to build this because we don't want to call + // engineUpdate right now. Data is already in the uplink buffer. + LMIC.pendTxConf = 0; // not confirmed + LMIC.pendTxPort = 0; // port 0 + LMIC.pendTxLen = LMIC.pendMacLen; + LMIC.pendMacLen = 0; // discard mac data! + LMIC.opmode |= OP_TXDATA; + LMICOS_logEvent("port0 mac message"); + } + } + + // Half-duplex gateways can have appreciable turn-around times, + // so we force a wait. It might be nice to randomize this a little, + // so that armies of identical devices will not try to talk all + // at once. This is potentially band-specific, so we let it come + // from the band-plan files. + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + +#if LMIC_ENABLE_DeviceTimeReq + // + // if the DeviceTimeReq FSM is active, we need to move it to idle, + // completing the callback. + // + lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState; + if ( requestTimeState != lmic_RequestTimeState_idle ) { + lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.client.pNetworkTimeCb; + int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success); + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle; + if (pNetworkTimeCb != NULL) { + // reset the callback, so that the user's routine + // can post another request if desired. + LMIC.client.pNetworkTimeCb = NULL; + + // call the user's notification routine. + (*pNetworkTimeCb)(LMIC.client.pNetworkTimeUserData, flagSuccess); + } + } +#endif // LMIC_ENABLE_DeviceTimeReq + + if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) { + LMIC.opmode &= ~OP_LINKDEAD; + reportEventNoUpdate(EV_LINK_ALIVE); + } + reportEventAndUpdate(EV_TXCOMPLETE); + // If we haven't heard from NWK in a while although we asked for a sign + // assume link is dead - notify application and keep going + if( LMIC.adrAckReq > LINK_CHECK_DEAD ) { + // We haven't heard from NWK for some time although we + // asked for a response for some time - assume we're disconnected. Lower DR one notch. + EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.adrAckReq)); + dr_t newDr = decDR((dr_t)LMIC.datarate); + // newDr must be feasible; there must be at least + // one channel that supports the new datarate. If not, stay + // at current datarate (which finalizes things). + if (! LMICbandplan_isDataRateFeasible(newDr)) { + LMICOS_logEventUint32("LINK_CHECK_DEAD, new DR not feasible", (newDr << 8) | LMIC.datarate); + newDr = LMIC.datarate; + } + if( newDr == (dr_t)LMIC.datarate) { + // We are already at the minimum datarate + // if the link is already marked dead, we need to join. +#if !defined(DISABLE_JOIN) + if ( LMIC.adrAckReq > LINK_CHECK_UNJOIN ) { + LMIC.opmode |= OP_UNJOIN; + } +#endif // !defined(DISABLE_JOIN) + } else if (newDr == LORAWAN_DR0) { + // the spec says: the ADRACKReq shall not be set if + // the device uses its lowest available data rate. + // (1.0.3, 4.3.1.1, line 458) + // We let the count continue to increase. + } else { + // we successfully lowered the data rate... + // reset so that we'll lower again after the next + // 32 uplinks. + setAdrAckCount(LINK_CHECK_CONT); + } + // Decrease DataRate and restore fullpower. + setDrTxpow(DRCHG_NOADRACK, newDr, pow2dBm(0)); + + // be careful only to report EV_LINK_DEAD once. + u2_t old_opmode = LMIC.opmode; + LMIC.opmode = old_opmode | OP_LINKDEAD; + if (LMIC.opmode != old_opmode) + reportEventNoUpdate(EV_LINK_DEAD); // update? + } +#if !defined(DISABLE_BEACONS) + // If this falls to zero the NWK did not answer our MCMD_BeaconInfoReq commands - try full scan + if( LMIC.bcninfoTries > 0 ) { + if( (LMIC.opmode & OP_TRACK) != 0 ) { + reportEventNoUpdate(EV_BEACON_FOUND); // update? + LMIC.bcninfoTries = 0; + } + else if( --LMIC.bcninfoTries == 0 ) { + startScan(); // NWK did not answer - try scan + } + } +#endif // !DISABLE_BEACONS + return 1; +} + +#if !defined(DISABLE_BEACONS) +static void processBeacon (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite + u1_t flags = LMIC.bcninfo.flags; + ev_t ev; + + if( LMIC.dataLen != 0 && LMIC_BEACON_SUCCESSFUL(decodeBeacon()) ) { + ev = EV_BEACON_TRACKED; + if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) { + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,0); + goto rev; + } + // We have a previous BEACON to calculate some drift + s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx); + if( LMIC.missedBcns > 0 ) { + drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1); + } + if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) { + s2_t diff = LMIC.drift - drift; + if( diff < 0 ) diff = -diff; + LMIC.lastDriftDiff = diff; + if( LMIC.maxDriftDiff < diff ) + LMIC.maxDriftDiff = diff; + LMIC.bcninfo.flags &= ~BCN_NODDIFF; + } + LMIC.drift = drift; + LMIC.missedBcns = LMIC.rejoinCnt = 0; + LMIC.bcninfo.flags &= ~BCN_NODRIFT; + EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT, + e_.eui = MAIN::CDEV->getEui(), + e_.info = drift, + e_.info2 = /*occasion BEACON*/0)); + // formerly we'd assert on BCN_PARTIAL|BCN_FULL, but we can't get here if so + } else { + ev = EV_BEACON_MISSED; + LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift; + LMIC.bcninfo.time += BCN_INTV_sec; + LMIC.missedBcns++; + // Delay any possible TX after surmised beacon - it's there although we missed it + txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4); + // if too many missed beacons or we lose sync, drop back to Class A. + if( LMIC.missedBcns > MAX_MISSED_BCNS || + LMIC.bcnRxsyms > MAX_RXSYMS ) { + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventAndUpdate(EV_LOST_TSYNC); + return; + } + } + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN); + LMIC.bcnRxsyms = LMIC.rxsyms; + rev: + LMICbandplan_advanceBeaconChannel(); +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! +#endif // !DISABLE_PING + reportEventAndUpdate(ev); +} + +// job entry: time to start receiving a beacon. +static void startRxBcn (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processBeacon); + radioRx(); +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +// job entry: time to start receiving in our scheduled downlink slot. +static void startRxPing (xref2osjob_t osjob) { + LMIC_API_PARAMETER(osjob); + + LMIC.osjob.func = FUNC_ADDR(processPingRx); + radioRx(); +} +#endif // !DISABLE_PING + + +// Decide what to do next for the MAC layer of a device. Inner part. +// Only called from outer part. +static void engineUpdate_inner (void) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode); +#endif + // Check for ongoing state: scan or TX/RX transaction + if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) + return; + +#if !defined(DISABLE_JOIN) + if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC_startJoining(); + return; + } + // we're joined but LinkTracking says we're out of luck... + if ( LMIC.devaddr != 0 && (LMIC.opmode & OP_UNJOIN) != 0 ) { + LMIC.opmode &= ~OP_UNJOIN; + LMIC_unjoinAndRejoin(); + return; + } +#endif // !DISABLE_JOIN + + ostime_t now = os_getTime(); + ostime_t txbeg = 0; + +#if !defined(DISABLE_BEACONS) + ostime_t rxtime = 0; + + if( (LMIC.opmode & OP_TRACK) != 0 ) { + // We are tracking a beacon + // formerly asserted ( now - (LMIC.bcnRxtime - os_getRadioRxRampup()) <= 0 ); + rxtime = LMIC.bcnRxtime - os_getRadioRxRampup(); + if (now - rxtime < 0) { + // too late: drop out of Class B. + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventNoUpdate(EV_LOST_TSYNC); + return; + } + } +#endif // !DISABLE_BEACONS + + if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) { + // Assuming txChnl points to channel which first becomes available again. + bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0); + // Find next suitable channel and return availability time + if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) { + txbeg = LMIC.txend = LMICbandplan_nextTx(now); + LMIC.opmode &= ~OP_NEXTCHNL; + } else { + // no need to consider anything but LMIC.txend. + txbeg = LMIC.txend; + } + // Delayed TX or waiting for duty cycle? + if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) + txbeg = LMIC.globalDutyAvail; +#if !defined(DISABLE_BEACONS) + // If we're tracking a beacon... + // then make sure TX-RX transaction is complete before beacon + if( (LMIC.opmode & OP_TRACK) != 0 && + txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) { + // Not enough time to complete TX-RX before beacon - postpone after beacon. + // In order to avoid clustering of postponed TX right after beacon randomize start! + txDelay(rxtime + BCN_RESERVE_osticks, 16); + txbeg = 0; + goto checkrx; + } +#endif // !DISABLE_BEACONS + // Earliest possible time vs overhead to setup radio + if( txbeg - (now + TX_RAMPUP) < 0 ) { + // We could send right now! + txbeg = now; + dr_t txdr = (dr_t)LMIC.datarate; +#if !defined(DISABLE_JOIN) + if( jacc ) { + u1_t ftype; + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // in AS923 v1.1 or older, no need to change the datarate. + // otherwise we need to check feasibility. + txdr = lowerDR(txdr, LMIC.rejoinCnt); +#endif + } + ftype = HDR_FTYPE_JREQ; + buildJoinRequest(ftype); + LMIC.osjob.func = FUNC_ADDR(jreqDone); + } else +#endif // !DISABLE_JOIN + { + if( LMIC.seqnoDn >= 0xFFFFFF80 ) { + // Imminent roll over - proactively reset MAC + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = 0)); + // Device has to react! NWK will not roll over and just stop sending. + // Thus, we have N frames to detect a possible lock up. + reset: + os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset)); + return; + } + if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) { + // Roll over of up seq counter + EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info2 = LMIC.seqnoUp)); + // Do not run RESET event callback from here! + // App code might do some stuff after send unaware of RESET. + goto reset; + } + if (! buildDataFrame()) { + // can't transmit this message. Report completion. + initTxrxFlags(__func__, TXRX_LENERR); + if (LMIC.pendTxConf || LMIC.txCnt) { + orTxrxFlags(__func__, TXRX_NACK); + } + LMIC.opmode &= ~(OP_POLL|OP_RNDTX|OP_TXDATA|OP_TXRXPEND); + LMIC.dataBeg = LMIC.dataLen = 0; + reportEventNoUpdate(EV_TXCOMPLETE); + return; + } + LMIC.osjob.func = FUNC_ADDR(updataDone); + } // end of else (not joining) + LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr); + LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 + LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; + LMICbandplan_updateTx(txbeg); + // limit power to value asked in adr + LMIC.radio_txpow = LMIC.txpow > LMIC.adrTxPow ? LMIC.adrTxPow : LMIC.txpow; + reportEventNoUpdate(EV_TXSTART); + os_radio(RADIO_TX); + return; + } + // Cannot yet TX + if( (LMIC.opmode & OP_TRACK) == 0 ) + goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX + // Consider RX tasks + if( txbeg == 0 ) // zero indicates no TX pending + txbeg += 1; // TX delayed by one tick (insignificant amount of time) + } else { + // No TX pending - no scheduled RX + if( (LMIC.opmode & OP_TRACK) == 0 ) + return; + } + +#if !defined(DISABLE_BEACONS) + // Are we pingable? + checkrx: +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) { + // One more RX slot in this beacon period? + if( rxschedNext(&LMIC.ping, now+os_getRadioRxRampup()) ) { + if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 ) + goto txdelay; + LMIC.rxsyms = LMIC.ping.rxsyms; + LMIC.rxtime = LMIC.ping.rxtime; + LMIC.freq = LMIC.ping.freq; + LMIC.rps = dndr2rps(LMIC.ping.dr); + LMIC.dataLen = 0; + ostime_t rxtime_ping = LMIC.rxtime - os_getRadioRxRampup(); + // did we miss the time? + if (now - rxtime_ping > 0) { + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEventNoUpdate(EV_LOST_TSYNC); + } else { + os_setTimedCallback(&LMIC.osjob, rxtime_ping, FUNC_ADDR(startRxPing)); + } + return; + } + // no - just wait for the beacon + } +#endif // !DISABLE_PING + + if( txbeg != 0 && (txbeg - rxtime) < 0 ) + goto txdelay; + + LMICbandplan_setBcnRxParams(); + LMIC.rxsyms = LMIC.bcnRxsyms; + LMIC.rxtime = LMIC.bcnRxtime; + if( now - rxtime >= 0 ) { + LMIC.osjob.func = FUNC_ADDR(processBeacon); + + radioRx(); + return; + } + os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn)); + return; +#endif // !DISABLE_BEACONS + + txdelay: + EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = osticks2ms(txbeg-now), + e_.info2 = LMIC.seqnoUp-1)); + LMIC_X_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": next engine update in %"LMIC_PRId_ostime_t"\n", now, txbeg-TX_RAMPUP); + os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate)); +} + +// Decide what to do next for the MAC layer of a device. +// Outer part. Safe to call from anywhere; defers if it +// detects a recursive call. +static void engineUpdate (void) { + lmic_engine_update_state_t state; + + state = LMIC.engineUpdateState; + if (state == lmic_EngineUpdateState_idle) { + LMIC.engineUpdateState = lmic_EngineUpdateState_busy; + do { + engineUpdate_inner(); + state = LMIC.engineUpdateState - 1; + LMIC.engineUpdateState = state; + } while (state != lmic_EngineUpdateState_idle); + } else { + LMIC.engineUpdateState = lmic_EngineUpdateState_again; + } +} + +void LMIC_setAdrMode (bit_t enabled) { + LMIC.adrEnabled = enabled ? FCT_ADREN : 0; +} + + +// Should we have/need an ext. API like this? +void LMIC_setDrTxpow (dr_t dr, s1_t txpow) { + setDrTxpow(DRCHG_SET, dr, txpow); +} + + +void LMIC_shutdown (void) { + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + LMIC.opmode |= OP_SHUTDOWN; +} + +// reset the LMIC. This is called at startup; the clear of LMIC.osjob +// only works because the LMIC is guaranteed to be zero in that case. +// But it's also called at frame-count rollover; in that case we have +// to ensure that the user callback pointers are not clobbered. +void LMIC_reset (void) { + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = EV_RESET)); + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + + // save callback info, clear LMIC, restore. + do { + lmic_client_data_t client = LMIC.client; + + os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC)); + + LMIC.client = client; + } while (0); + + // LMIC.devaddr = 0; // true from os_clearMem(). + LMIC.devNonce = os_getRndU2(); + LMIC.opmode = OP_NONE; + LMIC.errcr = CR_4_5; + LMIC.adrEnabled = FCT_ADREN; + resetJoinParams(); + LMIC.rxDelay = DELAY_DNW1; + // LMIC.pendMacLen = 0; + // LMIC.pendMacPiggyback = 0; + // LMIC.dn2Ans = 0; + // LMIC.macDlChannelAns = 0; + // LMIC.macRxTimingSetupAns = 0; +#if !defined(DISABLE_PING) + LMIC.ping.freq = FREQ_PING; // defaults for ping + LMIC.ping.dr = DR_PING; // ditto + LMIC.ping.intvExp = 0xFF; +#endif // !DISABLE_PING + + LMICbandplan_resetDefaultChannels(); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.devNonce, devNonce); + DO_DEVDB(LMIC.dn2Dr, dn2Dr); + DO_DEVDB(LMIC.dn2Freq, dn2Freq); +#if !defined(DISABLE_PING) + DO_DEVDB(LMIC.ping.freq, pingFreq); + DO_DEVDB(LMIC.ping.dr, pingDr); + DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); +#endif // !DISABLE_PING +#if LMIC_ENABLE_DeviceTimeReq + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle; + LMIC.netDeviceTime = 0; // the "invalid" time. + LMIC.netDeviceTimeFrac = 0; +#endif // LMIC_ENABLE_DeviceTimeReq +} + + +void LMIC_init (void) { + LMIC.opmode = OP_SHUTDOWN; + LMICbandplan_init(); +} + + +void LMIC_clrTxData (void) { + u2_t opmode = LMIC.opmode; + bit_t const txActive = opmode & OP_TXDATA; + if (! txActive) { + return; + } + LMIC.pendTxLen = 0; + opmode &= ~(OP_TXDATA | OP_POLL); + if (! (opmode & OP_JOINING)) { + // in this case, we are joining, and the TX data + // is just pending. + opmode &= ~(OP_TXRXPEND); + } + + LMIC.opmode = opmode; + + if (txActive) + reportEventNoUpdate(EV_TXCANCELED); + + if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING + return; + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + engineUpdate(); +} + +dr_t LMIC_feasibleDataRateForFrame(dr_t dr, u1_t payloadSize) { + if (payloadSize > MAX_LEN_PAYLOAD) { + return dr; + } + + const u1_t frameSize = payloadSize + OFF_DAT_OPTS + 5; + dr_t trialDr, nextDr; + + for (trialDr = dr; ;) { + if (! LMICbandplan_isDataRateFeasible(trialDr)) + break; + u1_t maxSizeThisDr = LMICbandplan_maxFrameLen(trialDr); + if (maxSizeThisDr == 0) { + break; + } else if (frameSize <= maxSizeThisDr) { + // we found one that is feasible! + return trialDr; + } + // try the next DR + nextDr = incDR(trialDr); + if (nextDr == trialDr) + break; + trialDr = nextDr; + } + + // if we get here, we didn't find a working dr. + return dr; +} + +static bit_t isTxPathBusy(void) { + return (LMIC.opmode & (OP_TXDATA|OP_JOINING)) != 0; +} + +static bit_t adjustDrForFrameIfNotBusy(u1_t len) { + if (isTxPathBusy()) { + return 0; + } + dr_t newDr = LMIC_feasibleDataRateForFrame(LMIC.datarate, len); + if (newDr != LMIC.datarate) { + setDrTxpow(DRCHG_FRAMESIZE, newDr, KEEP_TXPOW); + } + return 1; +} + +void LMIC_setTxData (void) { + adjustDrForFrameIfNotBusy(LMIC.pendTxLen); + LMIC_setTxData_strict(); +} + +void LMIC_setTxData_strict (void) { + LMICOS_logEventUint32(__func__, ((u4_t)LMIC.pendTxPort << 24u) | ((u4_t)LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); + LMIC.opmode |= OP_TXDATA; + if( (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC.txCnt = 0; // reset the confirmed uplink FSM + LMIC.upRepeatCount = 0; // reset the unconfirmed repeat FSM + } + engineUpdate(); +} + + +// send a message, attempting to adjust TX data rate +lmic_tx_error_t LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { + adjustDrForFrameIfNotBusy(dlen); + return LMIC_setTxData2_strict(port, data, dlen, confirmed); +} + +// send a message w/o callback; do not adjust data rate +lmic_tx_error_t LMIC_setTxData2_strict (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { + if ( LMIC.opmode & OP_TXDATA ) { + // already have a message queued + return LMIC_ERROR_TX_BUSY; + } + if( dlen > SIZEOFEXPR(LMIC.pendTxData) ) + return LMIC_ERROR_TX_TOO_LARGE; + if( data != (xref2u1_t)0 ) + os_copyMem(LMIC.pendTxData, data, dlen); + LMIC.pendTxConf = confirmed; + LMIC.pendTxPort = port; + LMIC.pendTxLen = dlen; + LMIC_setTxData_strict(); + if ( (LMIC.opmode & OP_TXDATA) == 0 ) { + if (LMIC.txrxFlags & TXRX_LENERR) { + return LMIC_ERROR_TX_NOT_FEASIBLE; + } else { + // data has already been completed with error for some reason + return LMIC_ERROR_TX_FAILED; + } + } + return 0; +} + +// send a message with callback; try to adjust data rate +lmic_tx_error_t LMIC_sendWithCallback ( + u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, + lmic_txmessage_cb_t *pCb, void *pUserData +) { + adjustDrForFrameIfNotBusy(dlen); + return LMIC_sendWithCallback_strict(port, data, dlen, confirmed, pCb, pUserData); +} + +// send a message with callback; do not adjust datarate +lmic_tx_error_t LMIC_sendWithCallback_strict ( + u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, + lmic_txmessage_cb_t *pCb, void *pUserData +) { + lmic_tx_error_t const result = LMIC_setTxData2_strict(port, data, dlen, confirmed); + if (result == 0) { + LMIC.client.txMessageCb = pCb; + LMIC.client.txMessageUserData = pUserData; + } + return result; +} + + +// Send a payload-less message to signal device is alive +void LMIC_sendAlive (void) { + LMIC.opmode |= OP_POLL; + engineUpdate(); +} + + +// Check if other networks are around. +void LMIC_tryRejoin (void) { + LMIC.opmode |= OP_REJOIN; + engineUpdate(); +} + +//! \brief Setup given session keys +//! and put the MAC in a state as if +//! a join request/accept would have negotiated just these keys. +//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey` +//! are unique within the network identified by `netid`. +//! NOTE: on Harvard architectures when session keys are in flash: +//! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL +//! \param netid a 24 bit number describing the network id this device is using +//! \param devaddr the 32 bit session address of the device. It is strongly recommended +//! to ensure that different devices use different numbers with high probability. +//! \param nwkKey the 16 byte network session key used for message integrity. +//! If NULL the caller has copied the key into `LMIC.nwkKey` before. +//! \param artKey the 16 byte application router session key used for message confidentiality. +//! If NULL the caller has copied the key into `LMIC.artKey` before. + +// TODO(tmm@mcci.com) we ought to also save the channels that were returned by the +// join accept; right now this has to be done by the caller (or it doesn't get done). +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + LMIC.netid = netid; + LMIC.devaddr = devaddr; + if( nwkKey != (xref2u1_t)0 ) + os_copyMem(LMIC.nwkKey, nwkKey, 16); + if( artKey != (xref2u1_t)0 ) + os_copyMem(LMIC.artKey, artKey, 16); + + LMICbandplan_setSessionInitDefaultChannels(); + + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_UNJOIN|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); + LMIC.opmode |= OP_NEXTCHNL; + stateJustJoined(); + // transition to the ADR_ACK_DELAY state. + setAdrAckCount(LINK_CHECK_CONT); + + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + DO_DEVDB(LMIC.seqnoUp, seqnoUp); + DO_DEVDB(LMIC.seqnoDn, seqnoDn); +} + +// Enable/disable link check validation. +// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames +// for a while. It expects the network to provide a DN message to prove +// connectivity with a span of UP frames. If this no such prove is coming +// then the datarate is lowered and a LINK_DEAD event is generated. +// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested +// nor is the datarate changed. +// This must be called only if a session is established (e.g. after EV_JOINED) +void LMIC_setLinkCheckMode (bit_t enabled) { + LMIC.adrChanged = 0; + LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF; +} + +// Sets the max clock error to compensate for (defaults to 0, which +// allows for +/- 640 at SF7BW250). MAX_CLOCK_ERROR represents +/-100%, +// so e.g. for a +/-1% error you would pass MAX_CLOCK_ERROR * 1 / 100. +void LMIC_setClockError(u2_t error) { + LMIC.client.clockError = error; +} + +// \brief return the uplink sequence number for the next transmission. +// This simple getter returns the uplink sequence number maintained by the LMIC engine. +// The caller should store the value and restore it (see LMIC_setSeqnoUp) on +// LMIC initialization to ensure monotonically increasing sequence numbers. +// It's also useful in debugging, as it allows you to correlate a debug trace event with +// a specific packet sent over the air. +u4_t LMIC_getSeqnoUp(void) { + return LMIC.seqnoUp; +} + +// \brief set the uplink sequence number for the next transmission. +// Use the function on startup to ensure that the next transmission uses +// a sequence number higher than the last transmission. +u4_t LMIC_setSeqnoUp(u4_t seq_no) { + u4_t last = LMIC.seqnoUp; + LMIC.seqnoUp = seq_no; + return last; +} + +// \brief return the current session keys returned from join. +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + *netid = LMIC.netid; + *devaddr = LMIC.devaddr; + memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey)); + memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey)); +} + +// \brief post an asynchronous request for the network time. +void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData) { +#if LMIC_ENABLE_DeviceTimeReq + if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) { + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx; + LMIC.client.pNetworkTimeCb = pCallbackfn; + LMIC.client.pNetworkTimeUserData = pUserData; + return; + } +#endif // LMIC_ENABLE_DeviceTimeReq + // if no device time support, or if not in proper state, + // report a failure. + if (pCallbackfn != NULL) + (*pCallbackfn)(pUserData, /* false */ 0); +} + +// \brief return local/remote time pair (if valid, and DeviceTimeReq enabled), +// return true for success, false for error. We adjust the sampled OS time +// back in time to the nearest second boundary. +int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference) { +#if LMIC_ENABLE_DeviceTimeReq + if (pReference != NULL && // valid parameter, and + LMIC.netDeviceTime != 0) { // ... we have a reasonable answer. + const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256; + + pReference->tLocal = LMIC.localDeviceTime - tAdjust; + pReference->tNetwork = LMIC.netDeviceTime; + return 1; + } +#else + LMIC_API_PARAMETER(pReference); +#endif // LMIC_ENABLE_DeviceTimeReq + return 0; +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.h new file mode 100644 index 0000000..471f1f9 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic.h @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016 Matthijs Kooijman. + * Copyright (c) 2016-2020 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! @file +//! @brief LMIC API + +#ifndef _lmic_h_ +#define _lmic_h_ + +#include "oslmic.h" +#include "lorabase.h" + +#if LMIC_DEBUG_LEVEL > 0 || LMIC_X_DEBUG_LEVEL > 0 +# if defined(LMIC_DEBUG_INCLUDE) +# define LMIC_STRINGIFY_(x) #x +# define LMIC_STRINGIFY(x) LMIC_STRINGIFY_(x) +# include LMIC_STRINGIFY(LMIC_DEBUG_INCLUDE) +# endif +# ifdef LMIC_DEBUG_PRINTF_FN + extern void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_PRINTF_FN +#endif + +// if LMIC_DEBUG_PRINTF is now defined, just use it. This lets you do anything +// you like with a sufficiently crazy header file. +#if LMIC_DEBUG_LEVEL > 0 +# ifndef LMIC_DEBUG_PRINTF +// otherwise, check whether someone configured a print-function to be used, +// and use it if so. +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# ifndef LMIC_DEBUG_INCLUDE // If you use LMIC_DEBUG_INCLUDE, put the declaration in there + void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_INCLUDE +# else // ndef LMIC_DEBUG_PRINTF_FN +// if there's no other info, just use printf. In a pure Arduino environment, +// that's what will happen. +# include +# define LMIC_DEBUG_PRINTF(f, ...) printf(f, ## __VA_ARGS__) +# endif // ndef LMIC_DEBUG_PRINTF_FN +# endif // ndef LMIC_DEBUG_PRINTF +# ifndef LMIC_DEBUG_FLUSH +# ifdef LMIC_DEBUG_FLUSH_FN +# define LMIC_DEBUG_FLUSH() LMIC_DEBUG_FLUSH_FN() +# else // ndef LMIC_DEBUG_FLUSH_FN +// if there's no other info, assume that flush is not needed. +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +# endif // ndef LMIC_DEBUG_FLUSH_FN +# endif // ndef LMIC_DEBUG_FLUSH +#else // LMIC_DEBUG_LEVEL == 0 +// If debug level is zero, printf and flush expand to nothing. +# define LMIC_DEBUG_PRINTF(f, ...) do { ; } while (0) +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +#endif // LMIC_DEBUG_LEVEL == 0 + +// +// LMIC_X_DEBUG_LEVEL enables additional, special print functions for debugging +// RSSI features. This is used sparingly. +#if LMIC_X_DEBUG_LEVEL > 0 +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_X_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# else +# error "LMIC_DEBUG_PRINTF_FN must be defined for LMIC_X_DEBUG_LEVEL > 0." +# endif +#else +# define LMIC_X_DEBUG_PRINTF(f, ...) do {;} while(0) +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// LMIC version -- this is ths IBM LMIC version +#define LMIC_VERSION_MAJOR 1 +#define LMIC_VERSION_MINOR 6 +#define LMIC_VERSION_BUILD 1468577746 + +// Arduino LMIC version +#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \ + ((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0)) + +#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 3, 0, 0) /* v3.3.0 */ + +#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ + ((((v)*UINT32_C(1)) >> 24u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_MINOR(v) \ + ((((v)*UINT32_C(1)) >> 16u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_PATCH(v) \ + ((((v)*UINT32_C(1)) >> 8u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \ + ((v) & 0xFFu) + +//! Only For Antenna Tuning Tests ! +//#define CFG_TxContinuousMode 1 + +// since this was annouunced as the API variable, we keep it. But it's not used, +// MAX_LEN_FRAME is what the code uses. +enum { MAX_FRAME_LEN = MAX_LEN_FRAME }; //!< Library cap on max frame length + +enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames +enum { MAX_MISSED_BCNS = (2 * 60 * 60 + 127) / 128 }; //!< threshold for dropping out of class B, triggering rejoin requests + // note that we need 100 ppm timing accuracy for + // this, to keep the timing error to +/- 700ms. +enum { MAX_RXSYMS = 350 }; // Stop tracking beacon if sync error grows beyond this. A 0.4% clock error + // at SF9.125k means 512 ms; one sybol is 4.096 ms, + // so this needs to be at least 125 for an STM32L0. + // And for 100ppm clocks and 2 hours of beacon misses, + // this needs to accomodate 1.4 seconds of error at + // 4.096 ms/sym or at least 342 symbols. + +enum { LINK_CHECK_CONT = 0 , // continue with this after reported dead link + LINK_CHECK_DEAD = 32 , // after this UP frames and no response to ack from NWK assume link is dead (ADR_ACK_DELAY) + LINK_CHECK_UNJOIN_MIN = LINK_CHECK_DEAD + 4, // this is the minimum value of LINK_CHECK_UNJOIN if we parameterize + LINK_CHECK_UNJOIN = LINK_CHECK_DEAD + (3 * 240), // after this many UP frames and no response, switch to join (by default) + LINK_CHECK_INIT = -64 , // UP frame count until we ask for ack (ADR_ACK_LIMIT) + LINK_CHECK_OFF =-128 }; // link check disabled + +enum { TIME_RESYNC = 6*128 }; // secs +enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon +enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon +enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time +enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send + +#if CFG_LMIC_EU_like // EU868 spectrum ==================================================== + +enum { MAX_CHANNELS = 16 }; //!< Max supported channels +enum { MAX_BANDS = 4 }; + +enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels +//! \internal +struct band_t { + u2_t txcap; // duty cycle limitation: 1/txcap + s1_t txpow; // maximum TX power + u1_t lastchnl; // last used channel + ostime_t avail; // channel is blocked until this time +}; +TYPEDEF_xref2band_t; //!< \internal + +struct lmic_saved_adr_state_s { + u4_t channelFreq[MAX_CHANNELS]; + u2_t channelMap; +}; + +#elif CFG_LMIC_US_like // US915 spectrum ================================================= + +enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable + +struct lmic_saved_adr_state_s { + u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; +}; + +#endif // ========================================================================== + +typedef struct lmic_saved_adr_state_s lmic_saved_adr_state_t; + +// Keep in sync with evdefs.hpp::drChange +enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD, DRCHG_FRAMESIZE }; +enum { KEEP_TXPOW = -128 }; + + +#if !defined(DISABLE_PING) +//! \internal +struct rxsched_t { + dr_t dr; + u1_t intvExp; // 0..7 + u1_t slot; // runs from 0 to 128 + rxsyms_t rxsyms; + ostime_t rxbase; + ostime_t rxtime; // start of next spot + u4_t freq; +}; +TYPEDEF_xref2rxsched_t; //!< \internal +#endif // !DISABLE_PING + + +#if !defined(DISABLE_BEACONS) +//! Parsing and tracking states of beacons. +enum { BCN_NONE = 0x00, //!< No beacon received + BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) + BCN_FULL = 0x02, //!< Full beacon decoded + BCN_NODRIFT = 0x04, //!< No drift value measured yet + BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet +//! Information about the last and previous beacons. +struct bcninfo_t { + ostime_t txtime; //!< Time when the beacon was sent + u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) + s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) + s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) + s1_t rssi; //!< Adjusted RSSI value of last received beacon + s1_t snr; //!< Scaled SNR value of last received beacon + u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. + // + u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) +}; +#endif // !DISABLE_BEACONS + +// purpose of receive window - lmic_t.rxState +enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TX_AT=4, }; +// Netid values / lmic_t.netid +enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; +// MAC operation modes (lmic_t.opmode). +enum { OP_NONE = 0x0000, + OP_SCAN = 0x0001, // radio scan to find a beacon + OP_TRACK = 0x0002, // track my networks beacon (netid) + OP_JOINING = 0x0004, // device joining in progress (blocks other activities) + OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) + OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data + OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST + OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything + OP_TXRXPEND = 0x0080, // TX/RX transaction pending + OP_RNDTX = 0x0100, // prevent TX lining up after a beacon + OP_PINGINI = 0x0200, // pingable is initialized and scheduling active + OP_PINGABLE = 0x0400, // we're pingable + OP_NEXTCHNL = 0x0800, // find a new channel + OP_LINKDEAD = 0x1000, // link was reported as dead + OP_TESTMODE = 0x2000, // developer test mode + OP_UNJOIN = 0x4000, // unjoin and rejoin on next engineUpdate(). +}; +// TX-RX transaction flags - report back to user +enum { TXRX_ACK = 0x80, // confirmed UP frame was acked + TXRX_NACK = 0x40, // confirmed UP frame was not acked + TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port + TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port + TXRX_LENERR = 0x08, // set if frame was discarded due to length error. + TXRX_PING = 0x04, // received in a scheduled RX slot + TXRX_DNW2 = 0x02, // received in 2dn DN slot + TXRX_DNW1 = 0x01, // received in 1st DN slot +}; + +// Event types for event callback +enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, + EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING, + EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED, + EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, + EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND, + EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE }; +typedef enum _ev_t ev_t; + +// this macro can be used to initalize a normal table of event strings +#define LMIC_EVENT_NAME_TABLE__INIT \ + "<>", \ + "EV_SCAN_TIMEOUT", "EV_BEACON_FOUND", \ + "EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING", \ + "EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED", \ + "EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET", \ + "EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE", "EV_SCAN_FOUND", \ + "EV_TXSTART", "EV_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE" + +// if working on an AVR (or worried about it), you can use this multi-zero +// string and put this in a single const F() string. Index through this +// counting up from 0, until you get to the entry you want or to an +// entry that begins with a \0. +#define LMIC_EVENT_NAME_MULTISZ__INIT \ + "<>\0" \ + "EV_SCAN_TIMEOUT\0" "EV_BEACON_FOUND\0" \ + "EV_BEACON_MISSED\0" "EV_BEACON_TRACKED\0" "EV_JOINING\0" \ + "EV_JOINED\0" "EV_RFU1\0" "EV_JOIN_FAILED\0" "EV_REJOIN_FAILED\0" \ + "EV_TXCOMPLETE\0" "EV_LOST_TSYNC\0" "EV_RESET\0" \ + "EV_RXCOMPLETE\0" "EV_LINK_DEAD\0" "EV_LINK_ALIVE\0" "EV_SCAN_FOUND\0" \ + "EV_TXSTART\0" "EV_TXCANCELED\0" "EV_RXSTART\0" "EV_JOIN_TXCOMPLETE\0" + +enum { + LMIC_ERROR_SUCCESS = 0, + LMIC_ERROR_TX_BUSY = -1, + LMIC_ERROR_TX_TOO_LARGE = -2, + LMIC_ERROR_TX_NOT_FEASIBLE = -3, + LMIC_ERROR_TX_FAILED = -4, +}; + +typedef int lmic_tx_error_t; + +#define LMIC_ERROR_NAME__INIT \ + "LMIC_ERROR_SUCCESS", \ + "LMIC_ERROR_TX_BUSY", \ + "LMIC_ERROR_TX_TOO_LARGE", \ + "LMIC_ERROR_TX_NOT_FEASIBLE", \ + "LMIC_ERROR_TX_FAILED" + +#define LMIC_ERROR_NAME_MULTISZ__INIT \ + "LMIC_ERROR_SUCCESS\0" \ + "LMIC_ERROR_TX_BUSY\0" \ + "LMIC_ERROR_TX_TOO_LARGE\0" \ + "LMIC_ERROR_TX_NOT_FEASIBLE\0" \ + "LMIC_ERROR_TX_FAILED" + +enum { + LMIC_BEACON_ERROR_INVALID = -2, + LMIC_BEACON_ERROR_WRONG_NETWORK = -1, + LMIC_BEACON_ERROR_SUCCESS_PARTIAL = 0, + LMIC_BEACON_ERROR_SUCCESS_FULL = 1, +}; + +typedef s1_t lmic_beacon_error_t; + +static inline bit_t LMIC_BEACON_SUCCESSFUL(lmic_beacon_error_t e) { + return e < 0; +} + +// LMIC_CFG_max_clock_error_ppm +#if !defined(LMIC_CFG_max_clock_error_ppm) +# define LMIC_CFG_max_clock_error_ppm 2000 /* max clock error: 0.2% (2000 ppm) */ +#endif + + +enum { + // This value represents 100% error in LMIC.clockError + MAX_CLOCK_ERROR = 65536, + //! \brief maximum clock error that users can specify: 2000 ppm (0.2%). + //! \details This is the limit for clock error, unless LMIC_ENABLE_arbitrary_clock_error is set. + //! The default is 4,000 ppm, which is .004, or 0.4%; this is what you get on an + //! STM32L0 running with the HSI oscillator after cal. If your clock error is bigger, + //! usually you want to calibrate it so that millis() and micros() are reasonably + //! accurate. Important: do not use clock error to compensate for late serving + //! of the LMIC. If you see that LMIC.radio.rxlate_count is increasing, you need + //! to adjust your application logic so the LMIC gets serviced promptly when a + //! Class A downlink (or beacon) is pending. + LMIC_kMaxClockError_ppm = 4000, +}; + +// callbacks for client alerts. +// types and functions are always defined, to reduce #ifs in example code and libraries. +typedef void LMIC_ABI_STD lmic_rxmessage_cb_t(void *pUserData, uint8_t port, const uint8_t *pMessage, size_t nMessage); +typedef void LMIC_ABI_STD lmic_txmessage_cb_t(void *pUserData, int fSuccess); +typedef void LMIC_ABI_STD lmic_event_cb_t(void *pUserData, ev_t e); + +// network time request callback function +// defined unconditionally, because APIs and types can't change based on config. +// This is called when a time-request succeeds or when we get a downlink +// without time request, "completing" the pending time request. +typedef void LMIC_ABI_STD lmic_request_network_time_cb_t(void *pUserData, int flagSuccess); + +// how the network represents time. +typedef u4_t lmic_gpstime_t; + +// rather than deal with 1/256 second tick, we adjust ostime back +// (as it's high res) to match tNetwork. +typedef struct lmic_time_reference_s lmic_time_reference_t; + +struct lmic_time_reference_s { + // our best idea of when we sent the uplink (end of packet). + ostime_t tLocal; + // the network's best idea of when we sent the uplink. + lmic_gpstime_t tNetwork; +}; + +enum lmic_request_time_state_e { + lmic_RequestTimeState_idle = 0, // we're not doing anything + lmic_RequestTimeState_tx, // we want to tx a time request on next uplink + lmic_RequestTimeState_rx, // we have tx'ed, next downlink completes. + lmic_RequestTimeState_success // we sucessfully got time. +}; + +typedef u1_t lmic_request_time_state_t; + +enum lmic_engine_update_state_e { + lmic_EngineUpdateState_idle = 0, // engineUpdate is idle. + lmic_EngineUpdateState_busy = 1, // engineUpdate is busy, but has not been reentered. + lmic_EngineUpdateState_again = 2, // engineUpdate is busy, and has to be evaluated again. +}; + +typedef u1_t lmic_engine_update_state_t; + +/* + +Structure: lmic_client_data_t + +Function: + Holds LMIC client data that must live through LMIC_reset(). + +Description: + There are a variety of client registration linkage items that + must live through LMIC_reset(), because LMIC_reset() is called + at frame rollover time. We group them together into a structure + to make copies easy. + +*/ + +//! abstract type for collection of client data that survives LMIC_reset(). +typedef struct lmic_client_data_s lmic_client_data_t; + +//! contents of lmic_client_data_t +struct lmic_client_data_s { + + /* pointer-width things come first */ +#if LMIC_ENABLE_DeviceTimeReq + lmic_request_network_time_cb_t *pNetworkTimeCb; //! call-back routine for network time + void *pNetworkTimeUserData; //! call-back data for network time. +#endif + +#if LMIC_ENABLE_user_events + lmic_event_cb_t *eventCb; //! user-supplied callback function for events. + void *eventUserData; //! data for eventCb + lmic_rxmessage_cb_t *rxMessageCb; //! user-supplied message-received callback + void *rxMessageUserData; //! data for rxMessageCb + lmic_txmessage_cb_t *txMessageCb; //! transmit-complete message handler; reset on each tx complete. + void *txMessageUserData; //! data for txMessageCb. +#endif // LMIC_ENABLE_user_events + + /* next we have things that are (u)int32_t */ + /* none at the moment */ + + /* next we have things that are (u)int16_t */ + + u2_t clockError; //! Inaccuracy in the clock. CLOCK_ERROR_MAX represents +/-100% error + + /* finally, things that are (u)int8_t */ + /* none at the moment */ +}; + +/* + +Structure: lmic_radio_data_t + +Function: + Holds LMIC radio driver. + +Description: + Eventually this will be used for all portable things for the radio driver, + but for now it's where we can start to add things. + +*/ + +typedef struct lmic_radio_data_s lmic_radio_data_t; + +struct lmic_radio_data_s { + // total os ticks of accumulated delay error. Can overflow! + ostime_t rxlate_ticks; + // number of rx late launches. + unsigned rxlate_count; + // total os ticks of accumulated tx delay error. Can overflow! + ostime_t txlate_ticks; + // number of tx late launches. + unsigned txlate_count; +}; + +/* + +Structure: lmic_t + +Function: + Provides the instance data for the LMIC. + +*/ + +struct lmic_t { + // client setup data, survives LMIC_reset(). + lmic_client_data_t client; + + // the OS job object. pointer alignment. + osjob_t osjob; + +#if !defined(DISABLE_BEACONS) + bcninfo_t bcninfo; // Last received beacon info +#endif + +#if !defined(DISABLE_PING) + rxsched_t ping; // pingable setup +#endif + + // the radio driver portable context + lmic_radio_data_t radio; + + /* (u)int32_t things */ + + // Radio settings TX/RX (also accessed by HAL) + ostime_t txend; + ostime_t rxtime; + + // LBT info + ostime_t lbt_ticks; // ticks to listen + + u4_t freq; + + ostime_t globalDutyAvail; // time device can send again + + u4_t netid; // current network id (~0 - none) + devaddr_t devaddr; + u4_t seqnoDn; // device level down stream seqno + u4_t seqnoUp; + u4_t dn2Freq; + +#if !defined(DISABLE_BEACONS) + ostime_t bcnRxtime; +#endif + +#if LMIC_ENABLE_DeviceTimeReq + // put here for alignment, to reduce RAM use. + ostime_t localDeviceTime; // the LMIC.txend value for last DeviceTimeAns + lmic_gpstime_t netDeviceTime; // the netDeviceTime for lastDeviceTimeAns + // zero ==> not valid. +#endif // LMIC_ENABLE_DeviceTimeReq + + // Channel scheduling -- very much private +#if CFG_LMIC_EU_like + band_t bands[MAX_BANDS]; + u4_t channelFreq[MAX_CHANNELS]; +#if !defined(DISABLE_MCMD_DlChannelReq) + u4_t channelDlFreq[MAX_CHANNELS]; +#endif + // bit map of enabled datarates for each channel + u2_t channelDrMap[MAX_CHANNELS]; + u2_t channelMap; +#elif CFG_LMIC_US_like + u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) + u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto + u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; +#endif + + /* (u)int16_t things */ + rps_t rps; // radio parameter selections: SF, BW, CodingRate, NoCrc, implicit hdr + u2_t opmode; // engineUpdate() operating mode flags + u2_t devNonce; // last generated nonce + + s2_t adrAckReq; // counter for link integrity tracking (LINK_CHECK_OFF=off) + +#if !defined(DISABLE_BEACONS) + s2_t drift; // last measured drift + s2_t lastDriftDiff; + s2_t maxDriftDiff; + rxsyms_t bcnRxsyms; // +#endif + + /* (u)int8_t things */ + lmic_engine_update_state_t engineUpdateState; // state of the engineUpdate() evaluator. + s1_t rssi; + s1_t snr; // LMIC.snr is SNR times 4 + rxsyms_t rxsyms; // symbols for receive timeout. + u1_t dndr; + s1_t txpow; // transmit dBm (administrative) + s1_t radio_txpow; // the radio driver's copy of txpow, in dB limited by adrTxPow, and + // also adjusted for EIRP/antenna gain considerations. + // This is just the radio's idea of power. So if you are + // controlling EIRP, and you have 3 dB antenna gain, this + // needs to reduced by 3 dB. + s1_t lbt_dbmax; // max permissible dB on our channel (eg -80) + + u1_t txChnl; // channel for next TX + u1_t globalDutyRate; // max rate: 1/2^k + + u1_t upRepeat; // configured up repeat + s1_t adrTxPow; // ADR adjusted TX power + u1_t datarate; // current data rate + u1_t errcr; // error coding rate (used for TX only) + u1_t rejoinCnt; // adjustment for rejoin datarate + + u1_t upRepeatCount; // current up-repeat + bit_t initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244 + + u1_t pendTxPort; + u1_t pendTxConf; // confirmed data + u1_t pendTxLen; // count of bytes in pendTxData. + u1_t pendTxData[MAX_LEN_PAYLOAD]; + + u1_t pendMacLen; // number of bytes of pending Mac response data + bit_t pendMacPiggyback; // received on port 0 or piggyback? + // response data if piggybacked + u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; + + u1_t nwkKey[16]; // network session key + u1_t artKey[16]; // application router session key + + u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 + u1_t lastDnConf; // downlink with seqnoDn-1 requested confirmation + u1_t adrChanged; + + u1_t rxDelay; // Rx delay after TX + + u1_t margin; + s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command + u1_t adrEnabled; + u1_t moreData; // NWK has more data pending +#if LMIC_ENABLE_TxParamSetupReq + u1_t txParam; // the saved TX param byte. +#endif +#if LMIC_ENABLE_DeviceTimeReq + lmic_request_time_state_t txDeviceTimeReqState; // current state, initially idle. + u1_t netDeviceTimeFrac; // updated on any DeviceTimeAns. +#endif + + // rx1DrOffset is the offset from uplink to downlink datarate + u1_t rx1DrOffset; // captured from join. zero by default. + + // 2nd RX window (after up stream) + u1_t dn2Dr; +#if !defined(DISABLE_MCMD_RXParamSetupReq) + u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs +#endif +#if !defined(DISABLE_MCMD_DlChannelReq) + u1_t macDlChannelAns; // 0 ==> no answer pending, 0x80+ACK bits +#endif +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + bit_t macRxTimingSetupAns; // 0 ==> no answer pend, non-zero inserts response. +#endif + + // Class B state +#if !defined(DISABLE_BEACONS) + u1_t missedBcns; // unable to track last N beacons + u1_t bcninfoTries; // how often to try (scan mode only) +#endif + // Public part of MAC state + u1_t txCnt; + u1_t txrxFlags; // transaction flags (TX-RX combo) + u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) + u1_t dataLen; // 0 no data or zero length data, >0 byte count of data + u1_t frame[MAX_LEN_FRAME]; + +#if !defined(DISABLE_BEACONS) + u1_t bcnChnl; +#endif + + u1_t noRXIQinversion; + u1_t saveIrqFlags; // last LoRa IRQ flags +}; + +//! \var struct lmic_t LMIC +//! The state of LMIC MAC layer is encapsulated in this variable. +DECLARE_LMIC; //!< \internal + +//! Construct a bit map of allowed datarates from drlo to drhi (both included). +#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) +bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); +bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); +bit_t LMIC_disableChannel (u1_t channel); +bit_t LMIC_enableSubBand(u1_t band); +bit_t LMIC_enableChannel(u1_t channel); +bit_t LMIC_disableSubBand(u1_t band); +bit_t LMIC_selectSubBand(u1_t band); + +void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow +void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) + +#if !defined(DISABLE_JOIN) +bit_t LMIC_startJoining (void); +void LMIC_tryRejoin (void); +void LMIC_unjoin (void); +void LMIC_unjoinAndRejoin (void); +#endif + +void LMIC_shutdown (void); +void LMIC_init (void); +void LMIC_reset (void); +void LMIC_clrTxData (void); +void LMIC_setTxData (void); +void LMIC_setTxData_strict(void); +lmic_tx_error_t LMIC_setTxData2(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); +lmic_tx_error_t LMIC_setTxData2_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); +lmic_tx_error_t LMIC_sendWithCallback(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData); +lmic_tx_error_t LMIC_sendWithCallback_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData); +void LMIC_sendAlive (void); + +#if !defined(DISABLE_BEACONS) +bit_t LMIC_enableTracking (u1_t tryBcnInfo); +void LMIC_disableTracking (void); +#endif + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void); +void LMIC_setPingable (u1_t intvExp); +#endif + +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); +void LMIC_setLinkCheckMode (bit_t enabled); +void LMIC_setClockError(u2_t error); + +u4_t LMIC_getSeqnoUp (void); +u4_t LMIC_setSeqnoUp (u4_t); +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey); + +void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData); +int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference); + +int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData); +int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData); + +// APIs for client half of compliance. +typedef u1_t lmic_compliance_rx_action_t; + +enum lmic_compliance_rx_action_e { + LMIC_COMPLIANCE_RX_ACTION_PROCESS = 0, // process this message normally + LMIC_COMPLIANCE_RX_ACTION_START, // enter compliance mode, discard this message + LMIC_COMPLIANCE_RX_ACTION_IGNORE, // continue in compliance mode, discard this message + LMIC_COMPLIANCE_RX_ACTION_END // exit compliance mode, discard this message +}; + +lmic_compliance_rx_action_t LMIC_complianceRxMessage(u1_t port, const u1_t *pMessage, size_t nMessage); + +// Declare onEvent() function, to make sure any definition will have the +// C conventions, even when in a C++ file. +#if LMIC_ENABLE_onEvent +DECL_ON_LMIC_EVENT; +#endif /* LMIC_ENABLE_onEvent */ + +// Special APIs - for development or testing +// !!!See implementation for caveats!!! + +#ifdef __cplusplus +} // extern "C" +#endif + +// names for backward compatibility +#include "lmic_compat.h" + +#endif // _lmic_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_as923.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_as923.c new file mode 100644 index 0000000..f46f50b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_as923.c @@ -0,0 +1,402 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_as923) +// ================================================================================ +// +// BEG: AS923 related stuff +// + +// see table in section 2.7.3 +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 0. +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { + 59+5, // [0] + 59+5, // [1] + 59+5, // [2] + 123+5, // [3] + 250+5, // [4] + 250+5, // [5] + 250+5, // [6] + 250+5 // [7] +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 1. +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { + 0, // [0] + 0, // [1] + 19+5, // [2] + 61+5, // [3] + 133+5, // [4] + 250+5, // [5] + 250+5, // [6] + 250+5 // [7] +}; + +static uint8_t +LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) { + if (mcmd_txparam == 0xFF) + return AS923_INITIAL_TxParam_UplinkDwellTime; + + return (mcmd_txparam & MCMD_TxParam_TxDWELL_MASK) != 0; +} + +static uint8_t +LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) { + if (mcmd_txparam == 0xFF) + return AS923_INITIAL_TxParam_DownlinkDwellTime; + + return (mcmd_txparam & MCMD_TxParam_RxDWELL_MASK) != 0; +} + +uint8_t LMICas923_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens_dwell0)) { + if (LMICas923_getUplinkDwellBit(LMIC.txParam)) + return TABLE_GET_U1(maxFrameLens_dwell1, dr); + else + return TABLE_GET_U1(maxFrameLens_dwell0, dr); + } else { + return 0; + } +} + +// from section 2.7.3. These are all referenced to the max EIRP of the +// device, which is set by TxParams +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 0, // [0]: MaxEIRP + -2, // [1]: MaxEIRP - 2dB + -6, // [2]: MaxEIRP - 4dB + -8, // [3]: MaxEIRP - 6dB + -4, // [4]: MaxEIRP - 8dB + -10, // [5]: MaxEIRP - 10dB + -12, // [6]: MaxEIRP - 12dB + -14, // [7]: MaxEIRP - 14dB +}; + +// from LoRaWAN 5.8: mapping from txParam to MaxEIRP +static CONST_TABLE(s1_t, TXMAXEIRP)[16] = { + 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 +}; + +static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { + // if uninitialized, return default. + if (mcmd_txparam == 0xFF) + return AS923_TX_EIRP_MAX_DBM; + else + return TABLE_GET_S1( + TXMAXEIRP, + (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> + MCMD_TxParam_MaxEIRP_SHIFT + ); +} + +// translate from an encoded power to an actual power using +// the maxeirp setting; return -128 if not legal. +int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + s1_t const adj = + TABLE_GET_S1( + TXPOWLEVELS, + pindex + ); + + return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF7B: 250K bps, DR_SF7 + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICas923_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// Default duty cycle is 1%. +enum { NUM_DEFAULT_CHANNELS = 2 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + AS923_F1 | BAND_CENTI, + AS923_F2 | BAND_CENTI, +}; + +// as923 ignores join, becuase the channel setup is the same either way. +void LMICas923_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; futxpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_CENTI; + } else { + if (band != BAND_CENTI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = + drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) + : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICas923_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AS923_FREQ_MIN || freq > AS923_FREQ_MAX) + freq = 0; + return freq; +} + +// when can we join next? +ostime_t LMICas923_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_CENTI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_CENTI].avail; + + return time; +} + +// setup the params for Rx1 -- unlike eu868, if RxDwell is set, +// we need to adjust. +void LMICas923_setRx1Params(void) { + int minDr; + int const txdr = LMIC.dndr; + int effective_rx1DrOffset; + int candidateDr; + + LMICeulike_setRx1Freq(); + + effective_rx1DrOffset = LMIC.rx1DrOffset; + // per section 2.7.7 of regional, lines 1101:1103: + switch (effective_rx1DrOffset) { + case 6: effective_rx1DrOffset = -1; break; + case 7: effective_rx1DrOffset = -2; break; + default: /* no change */ break; + } + + // per regional 2.2.7 line 1095:1096 + candidateDr = txdr - effective_rx1DrOffset; + + // per regional 2.2.7 lines 1097:1100 + if (LMICas923_getDownlinkDwellBit(LMIC.txParam)) + minDr = LORAWAN_DR2; + else + minDr = LORAWAN_DR0; + + if (candidateDr < minDr) + candidateDr = minDr; + + if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + // now that we've computed, store the results. + LMIC.dndr = (uint8_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + + +// return the next time, but also do channel hopping here +// identical to the EU868 version; but note that we only have BAND_CENTI +// at work. +ostime_t LMICas923_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + +#if !defined(DISABLE_BEACONS) +void LMICas923_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICas923_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +void +LMICas923_initJoinLoop(void) { + // LMIC.txParam is set to 0xFF by the central code at init time. + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ AS923_TX_EIRP_MAX_DBM); +} + +void +LMICas923_updateTx(ostime_t txbeg) { + u4_t freq = LMIC.channelFreq[LMIC.txChnl]; + u4_t dwellDelay; + u4_t globalDutyDelay; + + // Update global/band specific duty cycle stats + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + // Update channel/global duty cycle stats + xref2band_t band = &LMIC.bands[freq & 0x3]; + LMIC.freq = freq & ~(u4_t)3; + LMIC.txpow = LMICas923_getMaxEIRP(LMIC.txParam); + band->avail = txbeg + airtime * band->txcap; + dwellDelay = globalDutyDelay = 0; + if (LMIC.globalDutyRate != 0) { + globalDutyDelay = (airtime << LMIC.globalDutyRate); + } + if (LMICas923_getUplinkDwellBit(LMIC.txParam)) { + dwellDelay = AS923_UPLINK_DWELL_TIME_osticks; + } + if (dwellDelay > globalDutyDelay) { + globalDutyDelay = dwellDelay; + } + if (globalDutyDelay != 0) + LMIC.globalDutyAvail = txbeg + globalDutyDelay; +} + + +// +// END: AS923 related stuff +// +// ================================================================================ +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_au915.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_au915.c new file mode 100644 index 0000000..08df1ad --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_au915.c @@ -0,0 +1,302 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_au915) +// ================================================================================ +// +// BEG: AU915 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [4] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [5] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12] + MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13] + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 0, + 61+5, 137+5, 250+5, 250+5, 250+5, 250+5 }; + +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { + 0, 0, 19+5, 61+5, 133+5, 250+5, 250+5, 0, + 61+5, 137+5, 250+5, 250+5, 250+5, 250+5 }; + +static bit_t +LMICau915_getUplinkDwellBit() { + // if uninitialized, return default. + if (LMIC.txParam == 0xFF) { + return AU915_INITIAL_TxParam_UplinkDwellTime; + } + return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; +} + +uint8_t LMICau915_maxFrameLen(uint8_t dr) { + if (LMICau915_getUplinkDwellBit()) { + if (dr < LENOF_TABLE(maxFrameLens_dwell0)) + return TABLE_GET_U1(maxFrameLens_dwell0, dr); + else + return 0; + } else { + if (dr < LENOF_TABLE(maxFrameLens_dwell1)) + return TABLE_GET_U1(maxFrameLens_dwell1, dr); + else + return 0; + } +} + +// from LoRaWAN 5.8: mapping from txParam to MaxEIRP +static CONST_TABLE(s1_t, TXMAXEIRP)[16] = { + 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 +}; + +static int8_t LMICau915_getMaxEIRP(uint8_t mcmd_txparam) { + // if uninitialized, return default. + if (mcmd_txparam == 0xFF) + return AU915_TX_EIRP_MAX_DBM; + else + return TABLE_GET_S1( + TXMAXEIRP, + (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> + MCMD_TxParam_MaxEIRP_SHIFT + ); +} + +int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) + return -128; + else { + return ((s1_t)(LMICau915_getMaxEIRP(LMIC.txParam) - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); + } +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF8C + us2osticksRound(128 << 0), // ------ + us2osticksRound(128 << 5), // DR_SF12CR + us2osticksRound(128 << 4), // DR_SF11CR + us2osticksRound(128 << 3), // DR_SF10CR + us2osticksRound(128 << 2), // DR_SF9CR + us2osticksRound(128 << 1), // DR_SF8CR + us2osticksRound(128 << 0), // DR_SF7CR +}; + +// get ostime for symbols based on datarate. This is not like us915, +// becuase the times don't match between the upper half and lower half +// of the table. +ostime_t LMICau915_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + + +u4_t LMICau915_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AU915_FREQ_MIN || freq > AU915_FREQ_MAX) + freq = 0; + return freq; +} + +// au915: no support for xchannels. +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + LMIC_API_PARAMETER(chidx); + LMIC_API_PARAMETER(freq); + LMIC_API_PARAMETER(drmap); + LMIC_API_PARAMETER(band); + + return 0; // all channels are hardwired. +} + +bit_t LMIC_disableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72) { + if (ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72) { + if (!ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + result |= LMIC_enableChannel(64 + band); + return result; +} + +bit_t LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + result |= LMIC_disableChannel(64 + band); + return result; +} + +bit_t LMIC_selectSubBand(u1_t band) { + bit_t result = 0; + + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + result |= LMIC_enableSubBand(b); + else + result |= LMIC_disableSubBand(b); + } + return result; +} + +void LMICau915_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + LMIC.txpow = LMICau915_getMaxEIRP(LMIC.txParam); + if (chnl < 64) { + LMIC.freq = AU915_125kHz_UPFBASE + chnl*AU915_125kHz_UPFSTEP; + } else { + ASSERT(chnl < 64 + 8); + LMIC.freq = AU915_500kHz_UPFBASE + (chnl - 64)*AU915_500kHz_UPFSTEP; + } + + // Update global duty cycle stat and deal with dwell time. + u4_t dwellDelay; + u4_t globalDutyDelay; + dwellDelay = globalDutyDelay = 0; + + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + globalDutyDelay = txbeg + (airtime << LMIC.globalDutyRate); + } + if (LMICau915_getUplinkDwellBit(LMIC.txParam)) { + dwellDelay = AU915_UPLINK_DWELL_TIME_osticks; + } + if (dwellDelay > globalDutyDelay) { + globalDutyDelay = dwellDelay; + } + if (globalDutyDelay != 0) { + LMIC.globalDutyAvail = txbeg + globalDutyDelay; + } +} + +#if !defined(DISABLE_BEACONS) +void LMICau915_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = AU915_500kHz_DNFBASE + LMIC.bcnChnl * AU915_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// set the Rx1 dndr, rps. +void LMICau915_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + u1_t candidateDr; + LMIC.freq = AU915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU915_500kHz_DNFSTEP; + if ( /* TX datarate */txdr < AU915_DR_SF8C) + candidateDr = txdr + 8 - LMIC.rx1DrOffset; + else + candidateDr = AU915_DR_SF7CR; + + if (candidateDr < LORAWAN_DR8) + candidateDr = LORAWAN_DR8; + else if (candidateDr > LORAWAN_DR13) + candidateDr = LORAWAN_DR13; + + LMIC.dndr = candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void LMICau915_initJoinLoop(void) { + // LMIC.txParam is set to 0xFF by the central code at init time. + LMICuslike_initJoinLoop(); + + // initialize the adrTxPower. + LMIC.adrTxPow = LMICau915_getMaxEIRP(LMIC.txParam); // dBm + +} + +// +// END: AU915 related stuff +// +// ================================================================================ +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan.h new file mode 100644 index 0000000..db5863d --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan.h @@ -0,0 +1,231 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_h_ +# define _lmic_bandplan_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +#if defined(CFG_eu868) +# include "lmic_bandplan_eu868.h" +#elif defined(CFG_us915) +# include "lmic_bandplan_us915.h" +#elif defined(CFG_au915) +# include "lmic_bandplan_au915.h" +#elif defined(CFG_as923) +# include "lmic_bandplan_as923.h" +#elif defined(CFG_kr920) +# include "lmic_bandplan_kr920.h" +#elif defined(CFG_in866) +# include "lmic_bandplan_in866.h" +#else +# error "CFG_... not properly set for bandplan" +#endif + +// check post-conditions +#ifndef DNW2_SAFETY_ZONE +# error "DNW2_SAFETY_ZONE not defined by bandplan" +#endif + +#ifndef LMICbandplan_maxFrameLen +# error "LMICbandplan_maxFrameLen() not defined by bandplan" +#endif + +#ifndef pow2dBm +# error "pow2dBm() not defined by bandplan" +#endif + +#ifndef dr2hsym +# error "dr2hsym() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isValidBeacon1) && !defined(DISABLE_BEACONS) +# error "LMICbandplan_isValidBeacon1 not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isFSK) +# error "LMICbandplan_isFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_txDoneFSK) +# error "LMICbandplan_txDoneFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_joinAcceptChannelClear) +# error "LMICbandplan_joinAcceptChannelClear() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_getInitialDrJoin) +# error "LMICbandplan_getInitialDrJoin() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_hasJoinCFlist) +# error "LMICbandplan_hasJoinCFlist() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_advanceBeaconChannel) +# error "LMICbandplan_advanceBeaconChannel() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_resetDefaultChannels) +# error "LMICbandplan_resetDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setSessionInitDefaultChannels) +# error "LMICbandplan_setSessionInitDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setBcnRxParams) +# error "LMICbandplan_setBcnRxParams() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_canMapChannels) +# error "LMICbandplan_canMapChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_mapChannels) +# error "LMICbandplan_mapChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_convFreq) +# error "LMICbandplan_convFreq() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setRx1Params) +# error "LMICbandplan_setRx1Params() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initJoinLoop) +# error "LMICbandplan_initJoinLoop() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextTx) +# error "LMICbandplan_nextTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_updateTx) +# error "LMICbandplan_updateTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinState) +# error "LMICbandplan_nextJoinState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initDefaultChannels) +# error "LMICbandplan_initDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinTime) +# error "LMICbandplan_nextJoinTime() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_init) +# error "LMICbandplan_init() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_saveAdrState) +# error "LMICbandplan_saveAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_compareAdrState) +# error "LMICbandplan_compareAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_restoreAdrState) +# error "LMICbandplan_restoreAdrState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isDataRateFeasible) +# error "LMICbandplan_isDataRateFeasible() not defined by bandplan" +#endif + +// +// Things common to lmic.c code +// +#define LMICbandplan_MINRX_SYMS_LoRa_ClassA 6 +#define LMICbandplan_RX_ERROR_ABS_osticks ms2osticks(10) + +// Semtech inherently (by calculating in ms and taking ceilings) +// rounds up to the next higher ms. It's a lot easier for us +// to just add margin for things like hardware ramp-up time +// and clock calibration when running from the LSE and HSI +// clocks on an STM32. +#define LMICbandplan_RX_EXTRA_MARGIN_osticks us2osticks(2000) + +// probably this should be the same as the Class-A value, but +// we have not the means to thoroughly test this. This is the +// number of rxsyms used in the computations for ping and beacon +// windows. +#define LMICbandplan_MINRX_SYMS_LoRa_ClassB 5 + +#define LMICbandplan_PAMBL_SYMS 8 +#define LMICbandplan_PAMBL_FSK 5 +#define LMICbandplan_PRERX_FSK 1 +#define LMICbandplan_RXLEN_FSK (1+5+2) + +// Legacy names +#if !defined(MINRX_SYMS) +# define MINRX_SYMS LMICbandplan_MINRX_SYMS_LoRa_ClassB +#endif // !defined(MINRX_SYMS) +#define PAMBL_SYMS LMICbandplan_PAMBL_SYMS +#define PAMBL_FSK LMICbandplan_PAMBL_FSK +#define PRERX_FSK LMICbandplan_PRERX_FSK +#define RXLEN_FSK LMICbandplan_RXLEN_FSK + +// this is regional, but so far all regions are the same +#if !defined(LMICbandplan_MAX_FCNT_GAP) +# define LMICbandplan_MAX_FCNT_GAP 16384 +#endif // !defined LWAN_MAX_FCNT_GAP + +// this is probably regional, but for now default can be the same +#if !defined(LMICbandplan_TX_RECOVERY_ms) +# define LMICbandplan_TX_RECOVERY_ms 500 +#endif + +#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) +#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) +#define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) +#define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1) +#define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2) +#define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2) +#define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms) +#define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms) +#define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms) +#define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN) + +// Special APIs - for development or testing +#define isTESTMODE() 0 + +// internal APIs +ostime_t LMICcore_rndDelay(u1_t secSpan); +void LMICcore_setDrJoin(u1_t reason, u1_t dr); +ostime_t LMICcore_adjustForDrift(ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in); + +#endif // _lmic_bandplan_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_as923.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_as923.h new file mode 100644 index 0000000..16f4518 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_as923.h @@ -0,0 +1,111 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_as923_h_ +# define _lmic_bandplan_as923_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (as923); 0 --> not valid dr. +uint8_t LMICas923_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICas923_maxFrameLen(dr) + +int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICas923_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICas923_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICas923_dr2hsym(dr) + +static inline int +LMICas923_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICas923_isValidBeacon1(pFrame) + +// override default for LMICbandplan_resetDefaultChannels +void +LMICas923_resetDefaultChannels(void); + +#undef LMICbandplan_resetDefaultChannels +#define LMICbandplan_resetDefaultChannels() \ + LMICas923_resetDefaultChannels() + +// override default for LMICbandplan_init +void LMICas923_init(void); + +#undef LMICbandplan_init +#define LMICbandplan_init() \ + LMICas923_init() + + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == AS923_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (AS923_DR_SF10) + +void LMICas923_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICas923_setBcnRxParams() + +u4_t LMICas923_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICas923_convFreq(ptr) + +void LMICas923_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICas923_initJoinLoop() + +// for as923, depending on dwell, we may need to do something else +#undef LMICbandplan_setRx1Params +void LMICas923_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICas923_setRx1Params() + +ostime_t LMICas923_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICas923_nextTx(now) + +ostime_t LMICas923_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICas923_nextJoinState() + +void LMICas923_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICas923_initDefaultChannels(join) + +// override default for LMICbandplan_updateTX +#undef LMICbandplan_updateTx +void LMICas923_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICas923_updateTx(txbeg) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICas923_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICas923_nextJoinTime(now) + +#endif // _lmic_bandplan_as923_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_au915.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_au915.h new file mode 100644 index 0000000..f17194b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_au915.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_au915_h_ +# define _lmic_bandplan_au915_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR6) +#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR2) + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (au915); 0 --> not valid dr. +uint8_t LMICau915_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICau915_maxFrameLen(dr) + +int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICau915_pow2dbm(mcmd_ladr_p1) + +ostime_t LMICau915_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICau915_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR2) + +void LMICau915_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICau915_initJoinLoop() + +void LMICau915_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICau915_setBcnRxParams() + +u4_t LMICau915_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICau915_convFreq(ptr) + +void LMICau915_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICau915_setRx1Params() + +void LMICau915_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICau915_updateTx(txbeg) + +#endif // _lmic_bandplan_au915_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_eu868.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_eu868.h new file mode 100644 index 0000000..efff7d5 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_eu868.h @@ -0,0 +1,91 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu868_h_ +# define _lmic_eu868_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (eu868); 0 --> not valid dr. +uint8_t LMICeu868_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICeu868_maxFrameLen(dr) + +int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICeu868_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICeu868_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICeu868_dr2hsym(dr) + + +// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional +// spec. https://github.com/mcci-catena/arduino-lmic/issues/18 +static inline int +LMICeu868_isValidBeacon1(const uint8_t *d) { + return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICeu868_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == EU868_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7) + +void LMICeu868_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICeu868_setBcnRxParams() + +u4_t LMICeu868_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICeu868_convFreq(ptr) + +void LMICeu868_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICeu868_initJoinLoop() + +ostime_t LMICeu868_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICeu868_nextTx(now) + +ostime_t LMICeu868_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICeu868_nextJoinState() + +void LMICeu868_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICeu868_initDefaultChannels(join) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICeu868_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICeu868_nextJoinTime(now) + +void LMICeu868_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICeu868_setRx1Params() + +#endif // _lmic_eu868_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_in866.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_in866.h new file mode 100644 index 0000000..dad10ca --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_in866.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_in866_h_ +# define _lmic_bandplan_in866_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (in866); 0 --> not valid dr. +uint8_t LMICin866_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICin866_maxFrameLen(dr) + +int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICin866_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICin866_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICin866_dr2hsym(dr) + +static inline int +LMICin866_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICin866_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* TX datarate */LMIC.dndr == IN866_DR_FSK) + +#define LMICbandplan_getInitialDrJoin() (IN866_DR_SF7) + +void LMICin866_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICin866_setBcnRxParams() + +u4_t LMICin866_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICin866_convFreq(ptr) + +void LMICin866_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICin866_initJoinLoop() + +ostime_t LMICin866_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICin866_nextTx(now) + +ostime_t LMICin866_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICin866_nextJoinState() + +void LMICin866_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICin866_initDefaultChannels(join) + +void LMICin866_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICin866_setRx1Params() + +#endif // _lmic_bandplan_in866_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_kr920.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_kr920.h new file mode 100644 index 0000000..2c22f22 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_kr920.h @@ -0,0 +1,91 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_kr920_h_ +# define _lmic_kr920_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (kr920); 0 --> not valid dr. +uint8_t LMICkr920_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICkr920_maxFrameLen(dr) + +int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICkr920_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICkr920_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICkr920_dr2hsym(dr) + + +// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional +// spec. https://github.com/mcci-catena/arduino-lmic/issues/18 +static inline int +LMICkr920_isValidBeacon1(const uint8_t *d) { + return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICkr920_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* always false */ 0) + +#define LMICbandplan_getInitialDrJoin() (KR920_DR_SF7) + +void LMICkr920_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICkr920_setBcnRxParams() + +u4_t LMICkr920_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICkr920_convFreq(ptr) + +void LMICkr920_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICkr920_initJoinLoop() + +ostime_t LMICkr920_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICkr920_nextTx(now) + +ostime_t LMICkr920_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICkr920_nextJoinState() + +void LMICkr920_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICkr920_initDefaultChannels(join) + +void LMICkr920_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICkr920_setRx1Params() + +#undef LMICbandplan_updateTx +void LMICkr920_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(t) LMICkr920_updateTx(t) + +#endif // _lmic_kr920_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_us915.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_us915.h new file mode 100644 index 0000000..e08a795 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_bandplan_us915.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_us915_h_ +# define _lmic_bandplan_us915_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR4) +#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR0) + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +// return maximum frame length (including PHY header) for this data rate (us915); 0 --> not valid dr. +uint8_t LMICus915_maxFrameLen(uint8_t dr); +// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr. +#define LMICbandplan_maxFrameLen(dr) LMICus915_maxFrameLen(dr) + +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1) + +ostime_t LMICus915_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICus915_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR0) + +void LMICus915_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICus915_setBcnRxParams() + +u4_t LMICus915_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICus915_convFreq(ptr) + +void LMICus915_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICus915_initJoinLoop() + +void LMICus915_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICus915_setRx1Params() + +void LMICus915_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICus915_updateTx(txbeg) + +#endif // _lmic_bandplan_us915_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compat.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compat.h new file mode 100644 index 0000000..96dca49 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compat.h @@ -0,0 +1,72 @@ +/* + +Module: lmic_compat.h + +Function: + Symbols that are defined for backward compatibility + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation January 2020 + +Description: + This include file centralizes backwards compatibility + definitions. The idea is to centralize the decision, + so it's clear as to what's deprecated. + +*/ + +#ifndef _lmic_compat_h_ /* prevent multiple includes */ +#define _lmic_compat_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#ifndef ARDUINO_LMIC_VERSION +# error "This file is normally included from lmic.h, not stand alone" +#endif + +#define LMIC_DEPRECATE(m) _Pragma(#m) + +#if ! defined(LMIC_REGION_au921) && ARDUINO_LMIC_VERSION < ARDUINO_LMIC_VERSION_CALC(5,0,0,0) +# define LMIC_REGION_au921 LMIC_DEPRECATE(GCC warning "LMIC_REGION_au921 is deprecated, EOL at V5, use LMIC_REGION_au915") \ + LMIC_REGION_au915 + +// Frequency plan symbols +# define AU921_DR_SF12 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12 +# define AU921_DR_SF11 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11 +# define AU921_DR_SF10 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10 +# define AU921_DR_SF9 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9 +# define AU921_DR_SF8 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8 +# define AU921_DR_SF7 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7 +# define AU921_DR_SF8C LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8C +# define AU921_DR_NONE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_NONE +# define AU921_DR_SF12CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12CR +# define AU921_DR_SF11CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11CR +# define AU921_DR_SF10CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10CR +# define AU921_DR_SF9CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9CR +# define AU921_DR_SF8CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8CR +# define AU921_DR_SF7CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7CR +# define AU921_125kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFBASE +# define AU921_125kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFSTEP +# define AU921_500kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFBASE +# define AU921_500kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFSTEP +# define AU921_500kHz_DNFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFBASE +# define AU921_500kHz_DNFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFSTEP +# define AU921_FREQ_MIN LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MIN +# define AU921_FREQ_MAX LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MAX +# define AU921_TX_EIRP_MAX_DBM LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_TX_EIRP_MAX_DBM +# define AU921_INITIAL_TxParam_UplinkDwellTime LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_INITIAL_TxParam_UplinkDwellTime +# define AU921_UPLINK_DWELL_TIME_osticks LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_UPLINK_DWELL_TIME_osticks +# define DR_PAGE_AU921 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") DR_PAGE_AU915 +# define AU921_LMIC_REGION_EIRP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_LMIC_REGION_EIRP +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _lmic_compat_h_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.c new file mode 100644 index 0000000..d1356b9 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.c @@ -0,0 +1,771 @@ +/* + +Module: lmic_compliance.c + +Function: + Implementation of the compliance engine. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +Description: + See function descriptions. + +*/ + +#include "lmic.h" +#include "lmic_compliance.h" +#include "lorawan_spec_compliance.h" +#include +#include + +#if defined(LMIC_PRINTF_TO) +# include +# define LMIC_COMPLIANCE_PRINTF(f, ...) printf(f, ## __VA_ARGS__) +#else +# define LMIC_COMPLIANCE_PRINTF(f, ...) do { ; } while (0) +#endif + +/****************************************************************************\ +| +| Manifest constants and local declarations. +| +\****************************************************************************/ + +static void acEnterActiveMode(void); +static void acExitActiveMode(void); +static void acSendUplink(void); +static void acSetTimer(ostime_t); +static void acSendUplinkBuffer(void); +static void evActivate(void); +static void evDeactivate(void); +static void evJoinCommand(void); +static void evMessage(const uint8_t *pMessage, size_t nMessage); +static lmic_compliance_fsmstate_t fsmDispatch(lmic_compliance_fsmstate_t, bool); +static void fsmEval(void); +static void fsmEvalDeferred(void); +static osjobcbfn_t fsmJobCb; +static bool isActivateMessage(const uint8_t *pMessage, size_t nMessage); +static void evEchoCommand(const uint8_t *pMessage, size_t nMessage); +static lmic_event_cb_t lmicEventCb; +static lmic_txmessage_cb_t sendUplinkCompleteCb; +static osjobcbfn_t timerExpiredCb; + +/* these are declared global so the optimizer can chuck them without warnings */ +const char *LMICcompliance_txSuccessToString(int fSuccess); +const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state); + +/****************************************************************************\ +| +| Read-only data. +| +\****************************************************************************/ + +/****************************************************************************\ +| +| Variables. +| +\****************************************************************************/ + +lmic_compliance_t LMIC_Compliance; + +/* + +Name: LMIC_complianceRxMessage() + +Function: + Add compliance-awareness to LMIC applications by filtering messages. + +Definition: + lmic_compliance_rx_action_t LMIC_complianceRxMessage( + u1_t port, + const u1_t *pMessage, + size_t nMessage + ); + +Description: + Clients who want to handle the LoRaWAN compliance protocol on + port 224 should call this routine each time a downlink message is + received. This function will update the internal compliance state, + and return an appropriate action to the user. + + If the result is `LMIC_COMPLIANCE_RX_ACTION_PROCESS`, then the client should + process the message as usual. Otherwise, the client should discard the + message. The other values further allow the client to track entry into, + and exit from, compliance state. `LMIC_COMPLIANCE_RX_ACTION_START` signals + entry into compliance state; `LMIC_COMPLIANCE_RX_ACTION_END` signals exit + from compliance state; and `LMIC_COMPLIANCE_RX_ACTION_IGNORE` indicates + a mesage that should be discarded while already in compliance + state. + +Returns: + See description. + +*/ + +lmic_compliance_rx_action_t +LMIC_complianceRxMessage( + uint8_t port, + const uint8_t *pMessage, + size_t nMessage +) { + lmic_compliance_state_t const complianceState = LMIC_Compliance.state; + + // update the counter used by the status message. + ++LMIC_Compliance.downlinkCount; + + // filter normal messages. + if (port != LORAWAN_PORT_COMPLIANCE) { + return lmic_compliance_state_IsActive(complianceState) + ? LMIC_COMPLIANCE_RX_ACTION_PROCESS + : LMIC_COMPLIANCE_RX_ACTION_IGNORE + ; + } + + // it's a message to port 224. + // if we're not active, ignore everything but activation messages + if (! lmic_compliance_state_IsActive(complianceState)) { + if (isActivateMessage(pMessage, nMessage)) { + evActivate(); + } // else ignore. + } else { + evMessage(pMessage, nMessage); + } + if (lmic_compliance_state_IsActive(complianceState) == lmic_compliance_state_IsActive(LMIC_Compliance.state)) + return LMIC_COMPLIANCE_RX_ACTION_IGNORE; + else if (! lmic_compliance_state_IsActive(complianceState)) + return LMIC_COMPLIANCE_RX_ACTION_START; + else + return LMIC_COMPLIANCE_RX_ACTION_END; +} + +/* + +Name: isActivateMessage() + +Function: + See whether a message is a LoRaWAN activate test mode message. + +Definition: + static bool isActivateMessage( + const uint8_t *pMessage, + size_t nMessage + ); + +Description: + The message body is compared to an activate message (per the + LoRa Alliance End Device Certification spec). + +Returns: + The result is `true` if the message is an activation message; + it's `false` otherwise. + +*/ + +static bool +isActivateMessage( + const uint8_t *pMessage, + size_t nMessage +) { + const uint8_t body[LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN] = { + LORAWAN_COMPLIANCE_CMD_ACTIVATE, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC, + }; + + if (nMessage != sizeof(body)) + return false; + + if (memcmp(pMessage, body, sizeof(body)) == 0) + return true; + else + return false; +} + +/* + +Name: evActivate() + +Function: + Report an activation event to the finite state machine. + +Definition: + void evActivate(void); + +Description: + We report an activation event, and re-evaluate the FSM. + +Returns: + No explicit result. + +*/ + +static void evActivate(void) { + if (! lmic_compliance_state_IsActive(LMIC_Compliance.state)) { + LMIC_Compliance.downlinkCount = 0; + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ACTIVATE; + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVATING; + + LMIC_Compliance.saveEvent.pEventCb = LMIC.client.eventCb; + LMIC_Compliance.saveEvent.pUserData = LMIC.client.eventUserData; + +#if CFG_LMIC_EU_like + band_t *b = LMIC.bands; + lmic_compliance_band_t *b_save = LMIC_Compliance.saveBands; + + for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) { + b_save->txcap = b->txcap; + b->txcap = 1; + b->avail = os_getTime(); + } +#endif // CFG_LMIC_EU_like + + LMIC_registerEventCb(lmicEventCb, NULL); + + fsmEvalDeferred(); + } else { + LMIC_COMPLIANCE_PRINTF("Redundant ActivateTM message ignored.\n"); + } +} + +/* + +Name: evMessage() + +Function: + Process an inbound message while active. + +Definition: + void evMessage(const uint8_t *pMessage, size_t nMessage); + +Description: + The event is parsed, and the appropriate event(s) are sent into + the finite state machine. Note that because of the way the LMIC + works, we can assume that no uplink event is pending; so it's safe + to launch a send from here. + +Returns: + No explicit result. + +*/ + +static void evMessage( + const uint8_t *pMessage, + size_t nMessage +) { + if (nMessage == 0) + return; + + const uint8_t cmd = pMessage[0]; + switch (cmd) { + case LORAWAN_COMPLIANCE_CMD_DEACTIVATE: { + evDeactivate(); + break; + } + case LORAWAN_COMPLIANCE_CMD_ACTIVATE: { + if (isActivateMessage(pMessage, nMessage)) + evActivate(); + break; + } + case LORAWAN_COMPLIANCE_CMD_SET_CONFIRM: { + LMIC_Compliance.fsmFlags |= LMIC_COMPLIANCE_FSM_CONFIRM; + break; + } + case LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM: { + LMIC_Compliance.fsmFlags &= ~LMIC_COMPLIANCE_FSM_CONFIRM; + break; + } + case LORAWAN_COMPLIANCE_CMD_ECHO: { + evEchoCommand(pMessage, nMessage); + break; + } + case LORAWAN_COMPLIANCE_CMD_LINK: { + // not clear what this request does. + break; + } + case LORAWAN_COMPLIANCE_CMD_JOIN: { + evJoinCommand(); + break; + } + default: + break; + } +} + +/* + +Name: evDeactivate() + +Function: + Report an deactivation event to the finite state machine. + +Definition: + void evDectivate(void); + +Description: + We report a deactivation event, and re-evaluate the FSM. + We also set a flag so that we're return the appropriate + status from the compliance entry point to the real + application. + +Returns: + No explicit result. + +*/ + +static void evDeactivate(void) { + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_DEACTIVATE; + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_STOPPING; + + // restore user's event handler. + LMIC_registerEventCb(LMIC_Compliance.saveEvent.pEventCb, LMIC_Compliance.saveEvent.pUserData); + + // restore band settings +#if CFG_LMIC_EU_like + band_t *b = LMIC.bands; + lmic_compliance_band_t const *b_save = LMIC_Compliance.saveBands; + + for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) { + b->txcap = b_save->txcap; + } +#endif // CFG_LMIC_EU_like + + fsmEvalDeferred(); +} + +/* + +Name: evJoinCommand() + +Function: + Report that a join has been commanded. + +Definition: + void evJoinCommand(void); + +Description: + We unjoin from the network, and then report a deactivation + of test mode. That will get us out of test mode and back + to the compliance app. The next message send will trigger + a join. + +Returns: + No explicit result. + +*/ + +static void evJoinCommand( + void +) { + LMIC_unjoin(); + evDeactivate(); +} + +/* + +Name: evEchoCommand() + +Function: + Format and transmit the response to an echo downlink (aka echo request). + +Definition: + void evEchoCommand( + const uint8_t *pMessage, + size_t nMessage + ); + +Description: + The echo response is formatted and transmitted. Since we just received + a downlink, it's always safe to do this. + +Returns: + No explicit result. + +*/ + +static void evEchoCommand( + const uint8_t *pMessage, + size_t nMessage +) { + uint8_t *pResponse; + + if (nMessage > sizeof(LMIC_Compliance.uplinkMessage)) + return; + + // create the echo message. + pResponse = LMIC_Compliance.uplinkMessage; + + // copy the command byte unchanged. + *pResponse++ = *pMessage++; + --nMessage; + + // each byte in the body has to be incremented by one. + for (; nMessage > 0; --nMessage) { + *pResponse++ = (uint8_t)(*pMessage++ + 1); + } + + // now that the message is formatted, tell the fsm to send it; + // need to use a separate job. + LMIC_Compliance.uplinkSize = (uint8_t) (pResponse - LMIC_Compliance.uplinkMessage); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ECHO_REQUEST; + fsmEvalDeferred(); +} + + +/* + +Name: fsmEval() + +Function: + Evaluate the FSM, preventing recursion. + +Definition: + void fsmEval(void); + +Description: + We check for a nested call to evaluate the FSM; + if detected, the processing is deferred until the + current evaluation completes. Otherwise, we start + a new FSM evaluation, which proceeds until the FSM + returns a "no-change" result. + +Returns: + No explicit result. + +*/ + +const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state) { + const char * const names[] = { LMIC_COMPLIANCE_FSMSTATE__NAMES }; + + if ((unsigned) state >= sizeof(names)/sizeof(names[0])) + return "<>"; + else + return names[state]; +} + +static void fsmEvalDeferred(void) { + os_setCallback(&LMIC_Compliance.fsmJob, fsmJobCb); +} + +static void fsmJobCb(osjob_t *j) { + LMIC_API_PARAMETER(j); + fsmEval(); +} + +static void fsmEval(void) { + bool fNewState; + + // check for reentry. + do { + lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags; + + if (fsmFlags & LMIC_COMPLIANCE_FSM_ACTIVE) { + LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_REENTERED; + return; + } + + // record that we're active + LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_ACTIVE; + } while (0); + + // evaluate and change state + fNewState = false; + for (;;) { + lmic_compliance_fsmstate_t const oldState = LMIC_Compliance.fsmState; + lmic_compliance_fsmstate_t newState; + + newState = fsmDispatch(oldState, fNewState); + + if (newState == LMIC_COMPLIANCE_FSMSTATE_NOCHANGE) { + lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags; + + if ((fsmFlags & LMIC_COMPLIANCE_FSM_REENTERED) == 0) { + // not reentered, no change: get out. + LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_ACTIVE; + return; + } else { + // reentered. reset reentered flag and keep going. + LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_REENTERED; + fNewState = false; + } + } else { + // state change! + LMIC_COMPLIANCE_PRINTF("%s: change state %s(%u) => %s(%u)\n", + __func__, + LMICcompliance_fsmstate_getName(oldState), (unsigned) oldState, + LMICcompliance_fsmstate_getName(newState), (unsigned) newState + ); + fNewState = true; + LMIC_Compliance.fsmState = newState; + } + } +} + +/* + +Name: fsmDispatch() + +Function: + Dispatch to the appropriate event handler. + +Definition: + lmic_compliance_fsmstate_t fsmDispatch( + lmic_compliance_fsmstate_t state, + bool fEntry + ); + +Description: + This function is called by the evalutator as needed. `state` + is set to the current state of the FSM, and `fEntry` is + true if and only if this state has just been entered via a + transition arrow. (Might be a transition to self.) + +Returns: + This function returns LMIC_COMPLIANCE_FSMSTATE_NOCHANGE if + the FSM is to remain in this state until an event occurs. + Otherwise it returns the new state. + +*/ + +static inline lmic_compliance_eventflags_t +eventflags_TestAndClear(lmic_compliance_eventflags_t flag) { + const lmic_compliance_eventflags_t old = LMIC_Compliance.eventflags; + const lmic_compliance_eventflags_t result = old & flag; + + if (result != 0) + LMIC_Compliance.eventflags = old ^ result; + + return result; +} + +static lmic_compliance_fsmstate_t +fsmDispatch( + lmic_compliance_fsmstate_t state, + bool fEntry +) { + lmic_compliance_fsmstate_t newState; + + // currently, this is a stub. + newState = LMIC_COMPLIANCE_FSMSTATE_NOCHANGE; + + switch (state) { + case LMIC_COMPLIANCE_FSMSTATE_INITIAL: { + newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE; + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_INACTIVE: { + if (fEntry) { + acExitActiveMode(); + } + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ACTIVATE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_ACTIVE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_ACTIVE: { + if (fEntry) { + acEnterActiveMode(); + acSetTimer(sec2osticks(1)); + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_TXBUSY: { + if (fEntry) { + acSetTimer(sec2osticks(1)); + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_TESTMODE: { + if (LMIC.opmode & OP_TXDATA) { + // go back and wait some more. + newState = LMIC_COMPLIANCE_FSMSTATE_TXBUSY; + } + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_DEACTIVATE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE; + } else if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) { + newState = LMIC_COMPLIANCE_FSMSTATE_ECHOING; + } else { + newState = LMIC_COMPLIANCE_FSMSTATE_REPORTING; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_ECHOING: { + if (fEntry) + acSendUplinkBuffer(); + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_REPORTING: { + if (fEntry) + acSendUplink(); + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) { + newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY; + } + break; + } + + case LMIC_COMPLIANCE_FSMSTATE_RECOVERY: { + if (fEntry) { + if (LMIC_Compliance.eventflags & (LMIC_COMPLIANCE_EVENT_DEACTIVATE | + LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) { + acSetTimer(sec2osticks(1)); + } else { + acSetTimer(sec2osticks(5)); + } + } + + if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) { + newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE; + } + break; + } + + default: { + break; + } + } + + return newState; +} + +static void acEnterActiveMode(void) { + // indicate to the outer world that we're active. + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVE; +} + +void acSetTimer(ostime_t delay) { + os_setTimedCallback(&LMIC_Compliance.timerJob, os_getTime() + delay, timerExpiredCb); +} + +static void timerExpiredCb(osjob_t *j) { + LMIC_API_PARAMETER(j); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED; + fsmEval(); +} + +static void lmicEventCb( + void *pUserData, + ev_t ev +) { + LMIC_API_PARAMETER(pUserData); + + // pass to user handler + if (LMIC_Compliance.saveEvent.pEventCb) { + LMIC_Compliance.saveEvent.pEventCb( + LMIC_Compliance.saveEvent.pUserData, ev + ); + } + + // if it's a EV_JOINED, or a TXCMOMPLETE, we should tell the FSM. + if ((UINT32_C(1) << ev) & (EV_JOINED | EV_TXCOMPLETE)) { + fsmEvalDeferred(); + } +} + + +static void acExitActiveMode(void) { + LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_IDLE; + os_clearCallback(&LMIC_Compliance.timerJob); + LMIC_clrTxData(); +} + + +static void acSendUplink(void) { + uint8_t payload[2]; + uint32_t const downlink = LMIC_Compliance.downlinkCount; + + // build the uplink message + payload[0] = (uint8_t) (downlink >> 8); + payload[1] = (uint8_t) downlink; + + // reset the flags + LMIC_Compliance.eventflags &= ~LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + + // don't try to send if busy; might be sending echo message. + lmic_tx_error_t const eSend = + LMIC_sendWithCallback_strict( + LORAWAN_PORT_COMPLIANCE, + payload, sizeof(payload), + /* confirmed? */ + !! (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM), + sendUplinkCompleteCb, NULL + ); + + if (eSend == LMIC_ERROR_SUCCESS) { + // queued successfully + LMIC_COMPLIANCE_PRINTF( + "lmic_compliance.%s: queued uplink message(%u, %p)\n", + __func__, + (unsigned) downlink & 0xFFFF, + LMIC.client.txMessageCb + ); + } else { + // failed to queue; just skip this cycle. + LMIC_COMPLIANCE_PRINTF( + "lmic_compliance.%s: error(%d) sending uplink message(%u), %u bytes\n", + __func__, + eSend, + (unsigned) downlink & 0xFFFF, + (unsigned) sizeof(payload) + ); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + fsmEval(); + } +} + +static void sendUplinkCompleteCb(void *pUserData, int fSuccess) { + LMIC_API_PARAMETER(pUserData); + LMIC_API_PARAMETER(fSuccess); + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + LMIC_COMPLIANCE_PRINTF("%s(%s)\n", __func__, LMICcompliance_txSuccessToString(fSuccess)); + fsmEvalDeferred(); +} + +static void acSendUplinkBuffer(void) { + // send uplink data. + lmic_tx_error_t const eSend = + LMIC_sendWithCallback_strict( + LORAWAN_PORT_COMPLIANCE, + LMIC_Compliance.uplinkMessage, LMIC_Compliance.uplinkSize, + /* confirmed? */ (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM) != 0, + sendUplinkCompleteCb, + NULL); + + if (eSend == LMIC_ERROR_SUCCESS) { + LMIC_COMPLIANCE_PRINTF("%s: queued %u bytes\n", __func__, LMIC_Compliance.uplinkSize); + } else { + LMIC_COMPLIANCE_PRINTF("%s: uplink %u bytes failed (error %d)\n", __func__, LMIC_Compliance.uplinkSize, eSend); + if (eSend == LMIC_ERROR_TX_NOT_FEASIBLE) { + // Reverse the increment of the downlink count. Needed for US compliance. + if (CFG_region == LMIC_REGION_us915) + --LMIC_Compliance.downlinkCount; + } + LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE; + fsmEval(); + } +} + +const char *LMICcompliance_txSuccessToString(int fSuccess) { + return fSuccess ? "ok" : "failed"; +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.h new file mode 100644 index 0000000..8efe0e8 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_compliance.h @@ -0,0 +1,138 @@ +/* + +Module: lmic_compliance.h + +Function: + Internal header file for compliance-related work. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +Description: + This header file allows us to break up the compliance + functions into multiple .c files if we wish. + +*/ + +#ifndef _lmic_compliance_h_ /* prevent multiple includes */ +#define _lmic_compliance_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +#include +#include + +typedef struct lmic_compliance_s lmic_compliance_t; + +// concrete type for the state enumeration for the compliance engine. +typedef uint8_t lmic_compliance_state_t; + +enum lmic_compliance_state_e { + LMIC_COMPLIANCE_STATE_IDLE = 0, // app state + LMIC_COMPLIANCE_STATE_STOPPING = 1, // transitioning back to app + LMIC_COMPLIANCE_STATE_ACTIVATING = 2, // transitioning to compliance state + LMIC_COMPLIANCE_STATE_ACTIVE = 3, // in compliance state +}; + +// return true if a state value indicates that the FSM is active. +static inline bool +lmic_compliance_state_IsActive(lmic_compliance_state_t s) { + return s >= LMIC_COMPLIANCE_STATE_ACTIVATING; +} + +// events from the outside world to the FSM +typedef uint8_t lmic_compliance_eventflags_t; + +enum lmic_compliance_eventflags_e { + LMIC_COMPLIANCE_EVENT_ACTIVATE = 1u << 0, + LMIC_COMPLIANCE_EVENT_DEACTIVATE = 1u << 1, + LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED = 1u << 2, + LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE = 1u << 3, + LMIC_COMPLIANCE_EVENT_ECHO_REQUEST = 1u << 4, +}; + +typedef uint8_t lmic_compliance_fsmflags_t; +enum lmic_compliance_fsmflags_e { + LMIC_COMPLIANCE_FSM_ACTIVE = 1u << 0, + LMIC_COMPLIANCE_FSM_REENTERED = 1u << 1, + LMIC_COMPLIANCE_FSM_CONFIRM = 1u << 2, +}; + +typedef uint8_t lmic_compliance_fsmstate_t; +enum lmic_compliance_fsmstate_e { + LMIC_COMPLIANCE_FSMSTATE_INITIAL = 0, + LMIC_COMPLIANCE_FSMSTATE_NOCHANGE = 1, + LMIC_COMPLIANCE_FSMSTATE_ACTIVE = 2, + LMIC_COMPLIANCE_FSMSTATE_INACTIVE = 3, + LMIC_COMPLIANCE_FSMSTATE_TESTMODE = 4, // sending test uplinks + LMIC_COMPLIANCE_FSMSTATE_ECHOING = 5, + LMIC_COMPLIANCE_FSMSTATE_REPORTING = 6, + LMIC_COMPLIANCE_FSMSTATE_RECOVERY = 7, + LMIC_COMPLIANCE_FSMSTATE_TXBUSY = 8, +}; + +#define LMIC_COMPLIANCE_FSMSTATE__NAMES \ + "INITIAL", "NOCHANGE", "ACTIVE", "INACTIVE", "TESTMODE", \ + "ECHOING", "REPORTING", "RECOVERY", "TXBUSY" + +typedef struct lmic_compliance_eventcb_s lmic_compliance_eventcb_t; +struct lmic_compliance_eventcb_s { + // save the user's event CB while active. + lmic_event_cb_t *pEventCb; + // save the user's event data while active. + void *pUserData; +}; + +// structure for saving band settings during test +typedef struct lmic_compliance_band_s lmic_compliance_band_t; +struct lmic_compliance_band_s { + u2_t txcap; // saved 1/duty cycle +}; + +// the state of the compliance engine. +struct lmic_compliance_s { + // uint64 + // uintptr + osjob_t timerJob; // the job for driving uplinks + osjob_t fsmJob; // job for reevaluating the FSM. + lmic_compliance_eventcb_t saveEvent; // the user's event handler. + + // uint32 + + // uint16 +#if CFG_LMIC_EU_like + lmic_compliance_band_t saveBands[MAX_BANDS]; +#endif // CFG_LMIC_EU_like + + // we are required to maintain a downlink count + // that is reset on join/test entry and incremented for + // each valid test message. + uint16_t downlinkCount; + + // uint8 + + lmic_compliance_state_t state; // current state of compliance engine. + lmic_compliance_eventflags_t eventflags; // incoming events. + lmic_compliance_fsmflags_t fsmFlags; // FSM operational flags + lmic_compliance_fsmstate_t fsmState; // FSM current state + + uint8_t uplinkSize; + uint8_t uplinkMessage[MAX_LEN_PAYLOAD]; +}; + +extern lmic_compliance_t LMIC_Compliance; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _lmic_compliance_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_config_preconditions.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_config_preconditions.h new file mode 100644 index 0000000..875d71b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_config_preconditions.h @@ -0,0 +1,225 @@ +/* lmic_config_preconditions.h Fri May 19 2017 23:58:34 tmm */ + +/* + +Module: lmic_config_preconditions.h + +Function: + Preconditions for LMIC configuration. + +Version: + V2.0.0 Sun Aug 06 2017 17:40:44 tmm Edit level 1 + +Copyright notice: + This file copyright (C) 2017 by + + MCCI Corporation + 3520 Krums Corners Road + Ithaca, NY 14850 + + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Author: + Terry Moore, MCCI Corporation July 2017 + +Revision history: + 2.0.0 Sun Aug 06 2017 17:40:44 tmm + Module created. + +*/ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# define _LMIC_CONFIG_PRECONDITIONS_H_ + +// We need to be able to compile with different options without editing source. +// When building with a more advanced environment, set the following variable: +// ARDUINO_LMIC_PROJECT_CONFIG_H=my_project_config.h +// +// otherwise the lmic_project_config.h from the ../../project_config directory will be used. +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H +# define ARDUINO_LMIC_PROJECT_CONFIG_H ../../project_config/lmic_project_config.h +#endif + +#define CFG_TEXT_1(x) CFG_TEXT_2(x) +#define CFG_TEXT_2(x) #x + +// constants for comparison +#define LMIC_REGION_eu868 1 +#define LMIC_REGION_us915 2 +#define LMIC_REGION_cn783 3 +#define LMIC_REGION_eu433 4 +#define LMIC_REGION_au915 5 +#define LMIC_REGION_cn490 6 +#define LMIC_REGION_as923 7 +#define LMIC_REGION_kr920 8 +#define LMIC_REGION_in866 9 + +// Some regions have country-specific overrides. For generality, we specify +// country codes using the LMIC_COUNTY_CODE_C() macro These values are chosen +// from the 2-letter domain suffixes standardized by ISO-3166-1 alpha2 (see +// https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). They are therefore +// 16-bit constants. By convention, we use UPPER-CASE letters, thus +// LMIC_COUNTRY_CODE('J', 'P'), not ('j', 'p'). +#define LMIC_COUNTRY_CODE_C(c1, c2) ((c1) * 256 + (c2)) + +// this special code means "no country code defined" +#define LMIC_COUNTRY_CODE_NONE 0 + +// specific countries. Only the ones that are needed by the code are defined. +#define LMIC_COUNTRY_CODE_JP LMIC_COUNTRY_CODE_C('J', 'P') + +// include the file that the user is really supposed to edit. But for really strange +// ports, this can be suppressed +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS +# include CFG_TEXT_1(ARDUINO_LMIC_PROJECT_CONFIG_H) +#endif /* ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS */ + +#if defined(CFG_au921) && !defined(CFG_au915) +# warning "CFG_au921 was deprecated in favour of CFG_au915. Support for CFG_au921 might be removed in the future." +# define CFG_au915 +#endif + +// for backwards compatibility to legacy code, define CFG_au921 if we see CFG_au915. +#if defined(CFG_au915) && !defined(CFG_au921) +# define CFG_au921 +#endif + +// a mask of the supported regions +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define LMIC_REGIONS_SUPPORTED ( \ + (1 << LMIC_REGION_eu868) | \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au915) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + (1 << LMIC_REGION_kr920) | \ + (1 << LMIC_REGION_in866) | \ + 0) + +// +// Our input is a -D of one of CFG_eu868, CFG_us915, CFG_as923, CFG_au915, CFG_in866 +// More will be added in the the future. So at this point we create CFG_region with +// following values. These are in order of the sections in the manual. Not all of the +// below are supported yet. +// +// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in +// the below. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +// +# define CFG_LMIC_REGION_MASK \ + ((defined(CFG_eu868) << LMIC_REGION_eu868) | \ + (defined(CFG_us915) << LMIC_REGION_us915) | \ + (defined(CFG_cn783) << LMIC_REGION_cn783) | \ + (defined(CFG_eu433) << LMIC_REGION_eu433) | \ + (defined(CFG_au915) << LMIC_REGION_au915) | \ + (defined(CFG_cn490) << LMIC_REGION_cn490) | \ + (defined(CFG_as923) << LMIC_REGION_as923) | \ + (defined(CFG_kr920) << LMIC_REGION_kr920) | \ + (defined(CFG_in866) << LMIC_REGION_in866) | \ + 0) + +// the selected region. +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#if defined(CFG_eu868) +# define CFG_region LMIC_REGION_eu868 +#elif defined(CFG_us915) +# define CFG_region LMIC_REGION_us915 +#elif defined(CFG_cn783) +# define CFG_region LMIC_REGION_cn783 +#elif defined(CFG_eu433) +# define CFG_region LMIC_REGION_eu433 +#elif defined(CFG_au915) +# define CFG_region LMIC_REGION_au915 +#elif defined(CFG_cn490) +# define CFG_region LMIC_REGION_cn490 +#elif defined(CFG_as923jp) +# define CFG_as923 1 /* CFG_as923jp implies CFG_as923 */ +# define CFG_region LMIC_REGION_as923 +# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP +#elif defined(CFG_as923) +# define CFG_region LMIC_REGION_as923 +#elif defined(CFG_kr920) +# define CFG_region LMIC_REGION_kr920 +#elif defined(CFG_in866) +# define CFG_region LMIC_REGION_in866 +#else +# define CFG_region 0 +#endif + +// a bitmask of EU-like regions -- these are regions which have up to 16 +// channels indidually programmable via downloink. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define CFG_LMIC_EU_like_MASK ( \ + (1 << LMIC_REGION_eu868) | \ + /* (1 << LMIC_REGION_us915) | */ \ + (1 << LMIC_REGION_cn783) | \ + (1 << LMIC_REGION_eu433) | \ + /* (1 << LMIC_REGION_au915) | */ \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + (1 << LMIC_REGION_kr920) | \ + (1 << LMIC_REGION_in866) | \ + 0) + +// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels +// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but +// subsets of channels can be selected via masks. +// +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define CFG_LMIC_US_like_MASK ( \ + /* (1 << LMIC_REGION_eu868) | */ \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au915) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + /* (1 << LMIC_REGION_as923) | */ \ + /* (1 << LMIC_REGION_kr920) | */ \ + /* (1 << LMIC_REGION_in866) | */ \ + 0) + +// +// booleans that are true if the configured region is EU-like or US-like. +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +// +#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK)) +#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK)) + +// +// The supported LMIC LoRaWAAN spec versions. These need to be numerically ordered, +// so that we can (for example) compare +// +// LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3. +// +#define LMIC_LORAWAN_SPEC_VERSION_1_0_2 0x01000200u +#define LMIC_LORAWAN_SPEC_VERSION_1_0_3 0x01000300u + +#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_env.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_env.h new file mode 100644 index 0000000..06793e0 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_env.h @@ -0,0 +1,251 @@ +/* + +Module: lmic_env.h + +Function: + Sets up macros etc. to make things a little easier for portabilty + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation November 2018 + +Description: + This file is an adaptation of MCCI's standard IOCTL framework. + We duplicate a bit of functionality that we might get from other + libraries, so that the LMIC library can continue to stand alone. + +*/ + +#ifndef _lmic_env_h_ /* prevent multiple includes */ +#define _lmic_env_h_ + +/* + +Macro: LMIC_C_ASSERT() + +Function: + Declaration-like macro that will cause a compile error if arg is FALSE. + +Definition: + LMIC_C_ASSERT( + BOOL fErrorIfFalse + ); + +Description: + This macro, if used where an external reference declarataion is + permitted, will either compile cleanly, or will cause a compilation + error. The results of using this macro where a declaration is not + permitted are unspecified. + + This is different from #if !(fErrorIfFalse) / #error in that the + expression is evaluated by the compiler rather than by the pre- + processor. Therefore things like sizeof() can be used. + +Returns: + No explicit result -- either compiles cleanly or causes a compile + error. + +*/ + +#ifndef LMIC_C_ASSERT +# define LMIC_C_ASSERT(e) \ + void LMIC_C_ASSERT__(int LMIC_C_ASSERT_x[(e) ? 1: -1]) +#endif + +/****************************************************************************\ +| +| Define the begin/end declaration tags for C++ co-existance +| +\****************************************************************************/ + +#ifdef __cplusplus +# define LMIC_BEGIN_DECLS extern "C" { +# define LMIC_END_DECLS } +#else +# define LMIC_BEGIN_DECLS /* nothing */ +# define LMIC_END_DECLS /* nothing */ +#endif + +//---------------------------------------------------------------------------- +// Annotations to avoid various "unused" warnings. These must appear as a +// statement in the function body; the macro annotates the variable to quiet +// compiler warnings. The way this is done is compiler-specific, and so these +// definitions are fall-backs, which might be overridden. +// +// Although these are all similar, we don't want extra macro expansions, +// so we define each one explicitly rather than relying on a common macro. +//---------------------------------------------------------------------------- + +// signal that a parameter is intentionally unused. +#ifndef LMIC_UNREFERENCED_PARAMETER +# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// an API parameter is a parameter that is required by an API definition, but +// happens to be unreferenced in this implementation. This is a stronger +// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here +// becuase of an API contract, but we have no use for it in this function. +#ifndef LMIC_API_PARAMETER +# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// an intentionally-unreferenced variable. +#ifndef LMIC_UNREFERENCED_VARIABLE +# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0) +#endif + +// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1, +// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or +// or varables that are only refereneced at the target debug level. + +// Parameter referenced only if debugging at level > 0. +#ifndef LMIC_DEBUG1_PARAMETER +# if LMIC_DEBUG_LEVEL > 0 +# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if debugging at level > 0 +#ifndef LMIC_DEBUG1_VARIABLE +# if LMIC_DEBUG_LEVEL > 0 +# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if debugging at level > 1 +#ifndef LMIC_DEBUG2_PARAMETER +# if LMIC_DEBUG_LEVEL > 1 +# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if debugging at level > 1 +#ifndef LMIC_DEBUG2_VARIABLE +# if LMIC_DEBUG_LEVEL > 1 +# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0 +#ifndef LMIC_X_DEBUG_PARAMETER +# if LMIC_X_DEBUG_LEVEL > 0 +# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0) +# else +# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0) +# endif +#endif + +// variable referenced only if LMIC_X_DEBUG_LEVEL > 0 +#ifndef LMIC_X_DEBUG_VARIABLE +# if LMIC_X_DEBUG_LEVEL > 0 +# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0) +# else +# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0) +# endif +#endif + +// parameter referenced only if EV() macro is enabled (which it never is) +// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and +// this code is really C-99 to its bones. +#ifndef LMIC_EV_PARAMETER +# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0) +#endif + +// variable referenced only if EV() macro is defined. +#ifndef LMIC_EV_VARIABLE +# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0) +#endif + + /* + +Macro: LMIC_ABI_STD + +Index: Macro: LMIC_ABI_VARARGS + +Function: + Annotation macros to force a particular binary calling sequence. + +Definition: + #define LMIC_ABI_STD compiler-specific + #define LMIC_ABI_VARARGS compiler-specific + +Description: + These macros are used when declaring a function type, and indicate + that a particular calling sequence is to be used. They are normally + used between the type portion of the function declaration and the + name of the function. For example: + + typedef void LMIC_ABI_STD myCallBack_t(void); + + It's important to use this in libraries on platforms with multiple + calling sequences, because different components can be compiled with + different defaults. + +Returns: + Not applicable. + +*/ + +/* ABI marker for normal (fixed parameter count) functions -- used for function types */ +#ifndef LMIC_ABI_STD +# ifdef _MSC_VER +# define LMIC_ABI_STD __stdcall +# else +# define LMIC_ABI_STD /* nothing */ +# endif +#endif + +/* ABI marker for VARARG functions -- used for function types */ +#ifndef LMIC_ABI_VARARGS +# ifdef _MSC_VER +# define LMIC_ABI_VARARGS __cdecl +# else +# define LMIC_ABI_VARARGS /* nothing */ +# endif +#endif + +/* + +Macro: LMIC_DECLARE_FUNCTION_WEAK() + +Function: + Declare an external function as a weak reference. + +Definition: + #define LMIC_DECLARE_FUNCTION_WEAK(ReturnType, FunctionName, Params) ... + +Description: + This macro generates a weak reference to the specified function. + +Example: + LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e)); + + This saya that onEvent is a weak external reference. When calling + onEvent, you must always first check whether it's supplied: + + if (onEvent != NULL) + onEvent(e); + +Returns: + This macro expands to a declaration, without a trailing semicolon. + +Notes: + This form allows for compilers that use _Pragma(weak, name) instead + of inline attributes. + +*/ + +#define LMIC_DECLARE_FUNCTION_WEAK(a_ReturnType, a_FunctionName, a_Params) \ + a_ReturnType __attribute__((__weak__)) a_FunctionName a_Params + +#endif /* _lmic_env_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu868.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu868.c new file mode 100644 index 0000000..f8d51d1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu868.c @@ -0,0 +1,283 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_eu868) +// ================================================================================ +// +// BEG: EU868 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5 +}; + +uint8_t LMICeu868_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 16, 14, 12, 10, 8, 6, 4, 2 +}; + +int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // DR_SF7B + us2osticksRound(80) // FSK -- time for 1/2 byte (unused by LMIC) +}; + +ostime_t LMICeu868_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[6] = { + // Join frequencies and duty cycle limit (0.1%) + EU868_F1 | BAND_MILLI, EU868_F2 | BAND_MILLI, EU868_F3 | BAND_MILLI, + // Default operational frequencies and duty cycle limit (1%) + EU868_F1 | BAND_CENTI, EU868_F2 | BAND_CENTI, EU868_F3 | BAND_CENTI, +}; + +void LMICeu868_initDefaultChannels(bit_t join) { + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + u1_t su = join ? 0 : NUM_DEFAULT_CHANNELS; + for (u1_t fu = 0; fu BAND_AUX) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +// this table is from highest to lowest +static CONST_TABLE(u4_t, bandAssignments)[] = { + 870000000 /* .. and above */ | BAND_MILLI, + 869700000 /* .. 869700000 */ | BAND_CENTI, + 869650000 /* .. 869700000 */ | BAND_MILLI, + 869400000 /* .. 869650000 */ | BAND_DECI, + 868600000 /* .. 869640000 */ | BAND_MILLI, + 865000000 /* .. 868400000 */ | BAND_CENTI, +}; + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + + if (band == -1) { + for (u1_t i = 0; i < LENOF_TABLE(bandAssignments); ++i) { + const u4_t thisFreqBand = TABLE_GET_U4(bandAssignments, i); + const u4_t thisFreq = thisFreqBand & ~3; + if (freq >= thisFreq) { + band = ((u1_t)thisFreqBand & 3); + break; + } + } + + // if we didn't identify a frequency, it's millis. + if (band == -1) { + band = BAND_MILLI; + } + } + + if ((u1_t)band > BAND_AUX) + return 0; + + freq |= band; + + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICeu868_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX) + freq = 0; + return freq; +} + +ostime_t LMICeu868_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_MILLI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_MILLI].avail; + + return time; +} + +ostime_t LMICeu868_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + + +#if !defined(DISABLE_BEACONS) +void LMICeu868_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICeu868_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICeu868_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + // make a reasonable assumption for unspecified value. + drOffset = 5; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR7) + candidateDr = LORAWAN_DR7; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICeu868_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ EU868_TX_EIRP_MAX_DBM); +} + +// +// END: EU868 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.c new file mode 100644 index 0000000..46694e3 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.c @@ -0,0 +1,269 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_EU_like + +bit_t LMIC_enableSubBand(u1_t band) { + LMIC_API_PARAMETER(band); + return 0; +} + +bit_t LMIC_disableSubBand(u1_t band) { + LMIC_API_PARAMETER(band); + return 0; +} + +bit_t LMIC_disableChannel(u1_t channel) { + u2_t old_chmap = LMIC.channelMap; + LMIC.channelFreq[channel] = 0; + LMIC.channelDrMap[channel] = 0; + LMIC.channelMap = old_chmap & ~(1 << channel); + return LMIC.channelMap != old_chmap; +} + +// this is a no-op provided for compatibilty +bit_t LMIC_enableChannel(u1_t channel) { + LMIC_API_PARAMETER(channel); + return 0; +} + +// check whether a map operation will work. +// chpage is 0 or 6; 6 turns all on; 0 selects channels 0..15 via mask. +// The spec is unclear as to whether we should veto a channel mask that enables +// a channel that hasn't been configured; we veto it. +bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap) { + switch (chpage) { + case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT: + // we don't allow any channel to be turned on if its frequency is zero. + for (u1_t chnl = 0; chnltxpow; + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { + u1_t failed = 0; + + // Try each default channel with same DR + // If all fail try next lower datarate + if (++LMIC.txChnl == /* NUM_DEFAULT_CHANNELS */ nDefaultChannels) + LMIC.txChnl = 0; + if ((++LMIC.txCnt % nDefaultChannels) == 0) { + // Lower DR every nth try (having all default channels with same DR) + // + // TODO(tmm@mcci.com) add new DR_REGION_JOIN_MIN instead of LORAWAN_DR0; + // then we can eliminate the LMIC_REGION_as923 below because we'll set + // the failed flag here. This will cause the outer caller to take the + // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() + // + +// TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. +#if CFG_region == LMIC_REGION_as923 + // in the join of AS923 v1.1 or older, only DR2 is used. + // no need to change the DR. + LMIC.datarate = AS923_DR_SF10; + failed = 1; +#else + if (LMIC.datarate == LORAWAN_DR0) + failed = 1; // we have tried all DR - signal EV_JOIN_FAILED + else { + LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); + } +#endif + } + // Clear NEXTCHNL because join state engine controls channel hopping + LMIC.opmode &= ~OP_NEXTCHNL; + // Move txend to randomize synchronized concurrent joins. + // Duty cycle is based on txend. + ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = time + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF12:255, SF11:127, .., SF7:8secs + // + : DNW2_SAFETY_ZONE + LMICcore_rndDelay(255 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif // !DISABLE_JOIN + +void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + pStateBuffer->channelFreq, + LMIC.channelFreq, + sizeof(LMIC.channelFreq) + ); + pStateBuffer->channelMap = LMIC.channelMap; +} + +bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + if (memcmp(pStateBuffer->channelFreq, LMIC.channelFreq, sizeof(LMIC.channelFreq)) != 0) + return 1; + return pStateBuffer->channelMap != LMIC.channelMap; +} + +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelFreq, + pStateBuffer->channelFreq, + sizeof(LMIC.channelFreq) + ); + LMIC.channelMap = pStateBuffer->channelMap; +} + +void LMICeulike_setRx1Freq(void) { +#if !defined(DISABLE_MCMD_DlChannelReq) + uint32_t dlFreq = LMIC.channelDlFreq[LMIC.txChnl]; + if (dlFreq != 0) + LMIC.freq = dlFreq; +#endif // !DISABLE_MCMD_DlChannelReq +} + +// Class A txDone handling for FSK. +void +LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func) { + // one symbol == one bit at 50kHz == 20us. + ostime_t const hsym = us2osticksRound(10); + + // start a little earlier. PRERX_FSK is in bytes; one byte at 50 kHz == 160us + delay -= LMICbandplan_PRERX_FSK * us2osticksRound(160); + + // set LMIC.rxtime and LMIC.rxsyms: + LMIC.rxtime = LMIC.txend + LMICcore_adjustForDrift(delay, hsym, 8 * LMICbandplan_RXLEN_FSK); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - os_getRadioRxRampup(), func); +} + +#endif // CFG_LMIC_EU_like diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.h new file mode 100644 index 0000000..c9bbeac --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_eu_like.h @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu_like_h_ +# define _lmic_eu_like_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +// make sure we want US-like code +#if !CFG_LMIC_EU_like +# error "lmic not configured for EU-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(3000) + +// provide a default for LMICbandplan_isValidBeacon1() +static inline int +LMICeulike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICeulike_isValidBeacon1(pFrame) + + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneDoFSK() +void LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func); +#define LMICbandplan_txDoneFSK(delay, func) LMICeulike_txDoneFSK(delay, func) + +#define LMICbandplan_joinAcceptChannelClear() LMICbandplan_initDefaultChannels(/* normal, not join */ 0) + +enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 }; + +// there's a CFList on joins for EU-like plans +#define LMICbandplan_hasJoinCFlist() (1) + +#define LMICbandplan_advanceBeaconChannel() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_resetDefaultChannels() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { LMICbandplan_initDefaultChannels(/* normal, not join */ 0); } while (0) + +bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_canMapChannels(c, m) LMICeulike_canMapChannels(c, m) + +bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(c, m) LMICeulike_mapChannels(c, m) + +void LMICeulike_initJoinLoop(u1_t nDefaultChannels, s1_t adrTxPow); + +void LMICeulike_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(t) LMICeulike_updateTx(t) + +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels); + +static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICeulike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_saveAdrState(pState) LMICeulike_saveAdrState(pState) + +bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState) + +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICeulike_restoreAdrState(pState) + +// set Rx1 frequency (might be different than uplink). +void LMICeulike_setRx1Freq(void); + +bit_t LMICeulike_isDataRateFeasible(dr_t dr); +#define LMICbandplan_isDataRateFeasible(dr) LMICeulike_isDataRateFeasible(dr) + + +#endif // _lmic_eu_like_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_in866.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_in866.c new file mode 100644 index 0000000..15916a1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_in866.c @@ -0,0 +1,247 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_in866) +// ================================================================================ +// +// BEG: IN866 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + ILLEGAL_RPS, // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5 +}; + +uint8_t LMICin866_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 +}; + +int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 1), // -- + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICin866_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets +// us reuse code. +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + IN866_F1 | BAND_MILLI, + IN866_F2 | BAND_MILLI, + IN866_F3 | BAND_MILLI, +}; + +// india ignores join, becuase the channel setup is the same either way. +void LMICin866_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; fu BAND_MILLI) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_MILLI; + } else { + if (band > BAND_MILLI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(IN866_DR_SF12, IN866_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICin866_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < IN866_FREQ_MIN || freq > IN866_FREQ_MAX) + freq = 0; + return freq; +} + +// return the next time, but also do channel hopping here +// since there's no duty cycle limitation, and no dwell limitation, +// we simply loop through the channels sequentially. +ostime_t LMICin866_nextTx(ostime_t now) { + const u1_t band = BAND_MILLI; + + for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return now; + } + } + } + + // no enabled channel found! just use the last channel. + return now; +} + +#if !defined(DISABLE_BEACONS) +void LMICin866_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICin866_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICin866_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + drOffset = 5 - (s1_t) LMIC.rx1DrOffset; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICin866_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ IN866_TX_EIRP_MAX_DBM); +} + +// +// END: IN866 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_kr920.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_kr920.c new file mode 100644 index 0000000..9ea1dac --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_kr920.c @@ -0,0 +1,274 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_kr920) +// ================================================================================ +// +// BEG: KR920 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + ILLEGAL_RPS, // [6] +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 250+5, 250+5 +}; + +uint8_t LMICkr920_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 14, 12, 10, 8, 6, 4, 2, 0 +}; + +int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 6), // DR_SF11 + us2osticksRound(128 << 5), // DR_SF10 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 +}; + +ostime_t LMICkr920_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets +// us reuse code. +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + KR920_F1 | BAND_MILLI, + KR920_F2 | BAND_MILLI, + KR920_F3 | BAND_MILLI, +}; + +// korea ignores the join flag, becuase the channel setup is the same either way. +void LMICkr920_initDefaultChannels(bit_t join) { + LMIC_API_PARAMETER(join); + + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; fu BAND_MILLI) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + // zero the band bits in freq, just in case. + freq &= ~3; + + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_MILLI; + } else { + if (band > BAND_MILLI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7) : drmap; + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); + return 1; +} + + + +u4_t LMICkr920_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < KR920_FREQ_MIN || freq > KR920_FREQ_MAX) + freq = 0; + return freq; +} + +// return the next time, but also do channel hopping here +// since there's no duty cycle limitation, and no dwell limitation, +// we simply loop through the channels sequentially. +ostime_t LMICkr920_nextTx(ostime_t now) { + const u1_t band = BAND_MILLI; + + for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return now; + } + } + } + + // no enabled channel found! just use the last channel. + return now; +} + +#if !defined(DISABLE_BEACONS) +void LMICkr920_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICkr920_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// set the Rx1 dndr, rps. +void LMICkr920_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + s1_t drOffset; + s1_t candidateDr; + + LMICeulike_setRx1Freq(); + + if ( LMIC.rx1DrOffset <= 5) + drOffset = (s1_t) LMIC.rx1DrOffset; + else + drOffset = 5 - (s1_t) LMIC.rx1DrOffset; + + candidateDr = (s1_t) txdr - drOffset; + if (candidateDr < LORAWAN_DR0) + candidateDr = 0; + else if (candidateDr > LORAWAN_DR5) + candidateDr = LORAWAN_DR5; + + LMIC.dndr = (u1_t) candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void +LMICkr920_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ KR920_TX_EIRP_MAX_DBM); +} + +void LMICkr920_updateTx(ostime_t txbeg) { + u4_t freq = LMIC.channelFreq[LMIC.txChnl]; + // Update global/band specific duty cycle stats + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + // Update channel/global duty cycle stats + xref2band_t band = &LMIC.bands[freq & 0x3]; + LMIC.freq = freq & ~(u4_t)3; + LMIC.txpow = band->txpow; + if (LMIC.freq <= KR920_FDOWN && LMIC.txpow > KR920_TX_EIRP_MAX_DBM_LOW) { + LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; + } + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + +// +// END: KR920 related stuff +// +// ================================================================================ +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us915.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us915.c new file mode 100644 index 0000000..0d84ac1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us915.c @@ -0,0 +1,254 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_us915) +// ================================================================================ +// +// BEG: US915 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [4] + ILLEGAL_RPS , // [5] + ILLEGAL_RPS , // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12] + MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13] + ILLEGAL_RPS // [14] +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 19+5, 61+5, 133+5, 250+5, 250+5, 0, 0,0, + 61+5, 133+5, 250+5, 250+5, 250+5, 250+5 + }; + +uint8_t LMICus915_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0; +} + +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) > + ((LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3) + ? US915_LinkAdrReq_POW_MAX_1_0_2 + : US915_LinkAdrReq_POW_MAX_1_0_3)) + return -128; + else + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 5), // DR_SF10 DR_SF12CR + us2osticksRound(128 << 4), // DR_SF9 DR_SF11CR + us2osticksRound(128 << 3), // DR_SF8 DR_SF10CR + us2osticksRound(128 << 2), // DR_SF7 DR_SF9CR + us2osticksRound(128 << 1), // DR_SF8C DR_SF8CR + us2osticksRound(128 << 0) // ------ DR_SF7CR +}; + +ostime_t LMICus915_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, (dr) & 7); // map DR_SFnCR -> 0-6 +} + + + +u4_t LMICus915_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < US915_FREQ_MIN || freq > US915_FREQ_MAX) + freq = 0; + return freq; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + LMIC_API_PARAMETER(band); + + if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS) + return 0; // channels 0..71 are hardwired + LMIC.xchFreq[chidx - 72] = freq; + // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const + LMIC.xchDrMap[chidx - 72] = drmap == 0 ? DR_RANGE_MAP(US915_DR_SF10, US915_DR_SF8C) : drmap; + LMIC.channelMap[chidx >> 4] |= (1 << (chidx & 0xF)); + return 1; +} + +bit_t LMIC_disableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72 + MAX_XCHANNELS) { + if (ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableChannel(u1_t channel) { + bit_t result = 0; + if (channel < 72 + MAX_XCHANNELS) { + if (!ENABLED_CHANNEL(channel)) { + result = 1; + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } + return result; +} + +bit_t LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + result |= LMIC_enableChannel(64 + band); + return result; +} + +bit_t LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + bit_t result = 0; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + result |= LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + result |= LMIC_disableChannel(64 + band); + return result; +} + +bit_t LMIC_selectSubBand(u1_t band) { + bit_t result = 0; + + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + result |= LMIC_enableSubBand(b); + else + result |= LMIC_disableSubBand(b); + } + return result; +} + +void LMICus915_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + if (chnl < 64) { + LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP; + if (LMIC.activeChannels125khz >= 50) + LMIC.txpow = 30; + else + LMIC.txpow = 21; + } else { + // at 500kHz bandwidth, we're allowed more power. + LMIC.txpow = 26; + if (chnl < 64 + 8) { + LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; + } + else { + ASSERT(chnl < 64 + 8 + MAX_XCHANNELS); + LMIC.freq = LMIC.xchFreq[chnl - 72]; + } + } + + // Update global duty cycle stats + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); + } +} + +#if !defined(DISABLE_BEACONS) +void LMICus915_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// set the Rx1 dndr, rps. +void LMICus915_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + u1_t candidateDr; + LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; + if ( /* TX datarate */txdr < LORAWAN_DR4) + candidateDr = txdr + 10 - LMIC.rx1DrOffset; + else + candidateDr = LORAWAN_DR13 - LMIC.rx1DrOffset; + + if (candidateDr < LORAWAN_DR8) + candidateDr = LORAWAN_DR8; + else if (candidateDr > LORAWAN_DR13) + candidateDr = LORAWAN_DR13; + + LMIC.dndr = candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + +void LMICus915_initJoinLoop(void) { + LMICuslike_initJoinLoop(); + + // initialize the adrTxPower. + LMIC.adrTxPow = 20; // dBm +} + +// +// END: US915 related stuff +// +// ================================================================================ +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.c new file mode 100644 index 0000000..d1383bd --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.c @@ -0,0 +1,339 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_US_like + +#ifndef LMICuslike_getFirst500kHzDR +# error "LMICuslike_getFirst500kHzDR() not defined by bandplan" +#endif + +static void setNextChannel(uint start, uint end, uint count) { + ASSERT(count>0); + ASSERT(start>= 1) { + if (chmap & 1) { + LMIC_enableSubBand(subband); + } else { + LMIC_disableSubBand(subband); + } + } + } else { + u1_t base, top; + + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { + // operate on channels 0..15, 16..31, 32..47, 48..63 + // note that the chpage hasn't been shifted right, so + // it's really the base. + base = chpage; + top = base + 16; + if (base == 64) { + top = 72; + } + } else /* if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) */ { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; + + // enable or disable all 125kHz channels + for (u1_t chnl = 0; chnl < 64; ++chnl) { + if (en125) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + + // then apply mask to top 8 channels. + base = 64; + top = 72; + } + + // apply chmap to channels in [base..top-1]. + // Use enable/disable channel to keep activeChannel counts in sync. + for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) { + if (chmap & 0x0001) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + } + + LMICOS_logEventUint32("LMICuslike_mapChannels", ((u4_t)LMIC.activeChannels125khz << 16u)|(LMIC.activeChannels500khz << 0u)); + return (LMIC.activeChannels125khz > 0) || (LMIC.activeChannels500khz > 0); +} + +// US does not have duty cycling - return now as earliest TX time +// but also do the channel hopping dance. +ostime_t LMICuslike_nextTx(ostime_t now) { + // TODO(tmm@mcci.com): use a static const for US-like + if (LMIC.datarate >= LMICuslike_getFirst500kHzDR()) { // 500kHz + if (LMIC.activeChannels500khz > 0) { + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + } else if (LMIC.activeChannels125khz > 0) { + LMIC.datarate = lowerDR(LMICuslike_getFirst500kHzDR(), 1); + setNextChannel(0, 64, LMIC.activeChannels125khz); + LMICOS_logEvent("LMICuslike_nextTx: no 500k, choose 125k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (500)"); + } + } + else { // 125kHz + if (LMIC.activeChannels125khz > 0) { + setNextChannel(0, 64, LMIC.activeChannels125khz); + } else if (LMIC.activeChannels500khz > 0) { + LMIC.datarate = LMICuslike_getFirst500kHzDR(); + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + LMICOS_logEvent("LMICuslike_nextTx: no 125k, choose 500k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (125)"); + } + } + return now; +} + +bit_t LMICuslike_isDataRateFeasible(dr_t dr) { + if (dr >= LMICuslike_getFirst500kHzDR()) { // 500kHz + return LMIC.activeChannels500khz > 0; + } else { + return LMIC.activeChannels125khz > 6; + } +} + +#if !defined(DISABLE_JOIN) +void LMICuslike_initJoinLoop(void) { + // set an initial condition so that setNextChannel()'s preconds are met + LMIC.txChnl = 0; + + // then chose a new channel. This gives us a random first channel for + // the join. Minor nit: if channel 0 is enabled, it will never be used + // as the first join channel. The join logic uses the current txChnl, + // then changes after the rx window expires; so we need to set a valid + // starting point. + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // make sure LMIC.txend is valid. + LMIC.txend = os_getTime(); + ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0); + + // make sure the datarate is set to DR2 per LoRaWAN regional reqts V1.0.2, + // section 2.*.2 + LMICcore_setDrJoin(DRCHG_SET, LMICbandplan_getInitialDrJoin()); + + // TODO(tmm@mcci.com) need to implement the transmit randomization and + // duty cycle restrictions from LoRaWAN V1.0.2 section 7. +} +#endif // !DISABLE_JOIN + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICuslike_nextJoinState(void) { + // Try the following: + // DR0 (SF10) on a random channel 0..63 + // (honoring enable mask) + // DR4 (SF8C) on a random 500 kHz channel 64..71 + // (always determined by + // previously selected + // 125 kHz channel) + // + u1_t failed = 0; + // TODO(tmm@mcci.com) parameterize for US-like + if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { + // assume that 500 kHz equiv of last 125 kHz channel + // is also enabled, and use it next. + LMIC.txChnl = 64 + (LMIC.txChnl >> 3); + LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); + } + else { + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // TODO(tmm@mcci.com) parameterize + s1_t dr = LMICuslike_getJoin125kHzDR(); + if ((++LMIC.txCnt & 0x7) == 0) { + failed = 1; // All DR exhausted - signal failed + } + LMICcore_setDrJoin(DRCHG_SET, dr); + } + LMIC.opmode &= ~OP_NEXTCHNL; + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = os_getTime() + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF10:16, SF9=8,..SF8C:1secs + : LMICcore_rndDelay(16 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif + +void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + pStateBuffer->channelMap, + LMIC.channelMap, + sizeof(LMIC.channelMap) + ); + pStateBuffer->activeChannels125khz = LMIC.activeChannels125khz; + pStateBuffer->activeChannels500khz = LMIC.activeChannels500khz; +} + +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelMap, + pStateBuffer->channelMap, + sizeof(LMIC.channelMap) + ); + LMIC.activeChannels125khz = pStateBuffer->activeChannels125khz; + LMIC.activeChannels500khz = pStateBuffer->activeChannels500khz; +} + + +bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0; +} + +#endif // CFG_LMIC_US_like diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.h new file mode 100644 index 0000000..6281236 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_us_like.h @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_us_like_h_ +# define _lmic_us_like_h_ + +// make sure we want US-like code +#if !CFG_LMIC_US_like +# error "lmic not configured for us-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(750) + +#define IS_CHANNEL_125khz(c) (c<64) +#define IS_CHANNEL_500khz(c) (c>=64 && c<72) +#define ENABLED_CHANNEL(chnl) ((LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0x0F))) != 0) + +// library functions: called from bandplan +void LMICuslike_initJoinLoop(void); + +// provide the isValidBeacon1 function -- int for bool. +static inline int +LMICuslike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICuslike_isValidBeacon1(pFrame) + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneFSK() +#define LMICbandplan_txDoneFSK(delay, func) do { } while (0) + +// provide a default LMICbandplan_joinAcceptChannelClear() +#define LMICbandplan_joinAcceptChannelClear() do { } while (0) + +// no CFList on joins for US-like plans +#define LMICbandplan_hasJoinCFlist() (0) + +#define LMICbandplan_advanceBeaconChannel() \ + do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) + +// TODO(tmm@mcci.com): decide whether we want to do this on every +// reset or just restore the last sub-band selected by the user. +#define LMICbandplan_resetDefaultChannels() \ + LMICbandplan_initDefaultChannels(/* normal */ 0) + +void LMICuslike_initDefaultChannels(bit_t fJoin); +#define LMICbandplan_initDefaultChannels(fJoin) LMICuslike_initDefaultChannels(fJoin) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { /* nothing */} while (0) + +bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_canMapChannels(chpage, chmap) LMICuslike_canMapChannels(chpage, chmap) + +bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(chpage, chmap) LMICuslike_mapChannels(chpage, chmap) + +ostime_t LMICuslike_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICuslike_nextTx(now) + +ostime_t LMICuslike_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICuslike_nextJoinState(); + +static inline ostime_t LMICuslike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICuslike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_saveAdrState(pState) LMICuslike_saveAdrState(pState) + +bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_compareAdrState(pState) LMICuslike_compareAdrState(pState) + +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICuslike_restoreAdrState(pState) + +bit_t LMICuslike_isDataRateFeasible(dr_t dr); +#define LMICbandplan_isDataRateFeasible(dr) LMICuslike_isDataRateFeasible(dr) + +#endif // _lmic_us_like_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.c new file mode 100644 index 0000000..0d56c25 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.c @@ -0,0 +1,335 @@ +/* + +Module: lmic_util.c + +Function: + Encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2019 + +*/ + +#include "lmic_util.h" + +#include + +/* + +Name: LMIC_f2sflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15: sign + bits 14..11: biased exponent + bits 10..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFFF for negative values <= 1.0; + 0x7FFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt16( + float f + ) + { + if (f <= -1.0) + return 0xFFFF; + else if (f >= 1.0) + return 0x7FFF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x8000; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 11) + 0.5; + if (outputFraction >= (1 << 11u)) + { + // reduce output fraction + outputFraction = 1 << 10; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FFF | sign; + + return (uint16_t)(sign | (iExp << 11u) | outputFraction); + } + } + +/* + +Name: LMIC_f2sflt12() + +Function: + Encode a floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bit 11: sign + bits 10..7: biased exponent + bits 6..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFF for negative values <= 1.0; + 0x7FF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt12( + float f + ) + { + if (f <= -1.0) + return 0xFFF; + else if (f >= 1.0) + return 0x7FF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x800; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 7) + 0.5; + if (outputFraction >= (1 << 7u)) + { + // reduce output fraction + outputFraction = 1 << 6; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FF | sign; + + return (uint16_t)(sign | (iExp << 7u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2uflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15..12: biased exponent + bits 11..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0x0000 for values < 0.0; + 0xFFFF for positive values >= 1.0; + Otherwise an appropriate encoding of the input float. + +*/ + +uint16_t +LMIC_f2uflt16( + float f + ) + { + if (f < 0.0) + return 0; + else if (f >= 1.0) + return 0xFFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // underflow. + iExp = 0; + + // bits 15..12 are the exponent + // bits 11..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 12) + 0.5; + if (outputFraction >= (1 << 12u)) + { + // reduce output fraction + outputFraction = 1 << 11; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFFF; + + return (uint16_t)((iExp << 12u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt12() + +Function: + Encode positive floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bits 11..8: biased exponent + bits 7..0: mantissa + + The float is properly rounded, and saturates. + +Returns: + 0x000 for negative values < 0.0; + 0xFFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2uflt12( + float f + ) + { + if (f < 0.0) + return 0x000; + else if (f >= 1.0) + return 0xFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // graceful underflow + iExp = 0; + + // bits 11..8 are the exponent + // bits 7..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 8) + 0.5; + if (outputFraction >= (1 << 8u)) + { + // reduce output fraction + outputFraction = 1 << 7; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFF; + + return (uint16_t)((iExp << 8u) | outputFraction); + } + } diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.h new file mode 100644 index 0000000..7e1b3c8 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lmic_util.h @@ -0,0 +1,34 @@ +/* + +Module: lmic_util.h + +Function: + Declare encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2018 + +*/ + +#ifndef _LMIC_UTIL_H_ +# define _LMIC_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +uint16_t LMIC_f2sflt16(float); +uint16_t LMIC_f2sflt12(float); +uint16_t LMIC_f2uflt16(float); +uint16_t LMIC_f2uflt12(float); + +#ifdef __cplusplus +} +#endif + +#endif /* _LMIC_UTIL_H_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase.h new file mode 100644 index 0000000..fc0fd2d --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase.h @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyritght (c) 2017 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _lorabase_h_ +#define _lorabase_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +// ================================================================================ +// BEG: Keep in sync with lorabase.hpp +// + +enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; +enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; +enum _bw_t { BW125=0, BW250, BW500, BWrfu }; +typedef u1_t cr_t; +typedef u1_t sf_t; +typedef u1_t bw_t; +typedef u1_t dr_t; +typedef u2_t rxsyms_t; + +// Radio parameter set (encodes SF/BW/CR/IH/NOCRC) +// 2..0: Spreading factor +// 4..3: bandwidth: 0 == 125kHz, 1 == 250 kHz, 2 == 500 kHz. 3 == reserved. +// 6..5: coding rate: 0 == 4/5, 1 == 4/6, 2 == 4/7, 3 == 4/8 +// 7: nocrc: 0 == with crc, 1 == without crc +// 15..8: Implicit header control: 0 ==> none, 1..0xFF ==> length in bytes. + +typedef u2_t rps_t; +TYPEDEF_xref2rps_t; + +enum { ILLEGAL_RPS = 0xFF }; + +// Global maximum frame length +enum { STD_PREAMBLE_LEN = 8 }; +enum { MAX_LEN_FRAME = LMIC_MAX_FRAME_LENGTH }; +enum { LEN_DEVNONCE = 2 }; +enum { LEN_ARTNONCE = 3 }; +enum { LEN_NETID = 3 }; +enum { DELAY_JACC1 = 5 }; // in secs +enum { DELAY_DNW1 = 1 }; // in secs down window #1 +enum { DELAY_EXTDNW2 = 1 }; // in secs +enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs +enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 +enum { BCN_INTV_exp = 7 }; +enum { BCN_INTV_sec = 1< 1) + MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP + MCMD_DlChannelReq = 0x0A, // u1: channel, u3: frequency + MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second + + // Class B + MCMD_PingSlotInfoAns = 0x10, // - + MCMD_PingSlotChannelReq = 0x11, // u3: freq, u1:dr [7-4]:RFU [3:0]:datarate + MCMD_BeaconTimingAns = 0x12, // u2: delay(in TUNIT millis), u1:channel (DEPRECATED) + MCMD_BeaconFreqReq = 0x13, // u3: freq +}; + +enum { + MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis +}; +enum { + MCMD_LinkADRAns_RFU = 0xF8, // RFU bits + MCMD_LinkADRAns_PowerACK = 0x04, // 0=not supported power level + MCMD_LinkADRAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_LinkADRAns_ChannelACK = 0x01, // 0=unknown channel enabled +}; +enum { + MCMD_RXParamSetupAns_RFU = 0xF8, // RFU bits + MCMD_RXParamSetupAns_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed + MCMD_RXParamSetupAns_RX2DataRateACK = 0x02, // 0=unknown data rate + MCMD_RXParamSetupAns_ChannelACK = 0x01, // 0=unknown channel enabled +}; +enum { + MCMD_NewChannelAns_RFU = 0xFC, // RFU bits + MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency +}; +enum { + MCMD_RXTimingSetupReq_RFU = 0xF0, // RFU bits + MCMD_RXTimingSetupReq_Delay = 0x0F, // delay in secs, 1..15; 0 is mapped to 1. +}; +enum { + MCMD_DlChannelAns_RFU = 0xFC, // RFU bits + MCMD_DlChannelAns_FreqACK = 0x02, // 0 = uplink frequency not defined for this channel + MCMD_DlChannelAns_ChannelACK = 0x01, // 0 = rejected channel freq +}; +enum { + MCMD_PingSlotFreqAns_RFU = 0xFC, + MCMD_PingSlotFreqAns_DataRateACK = 0x02, + MCMD_PingSlotFreqAns_ChannelACK = 0x01, +}; + +enum { + MCMD_DEVS_EXT_POWER = 0x00, // external power supply + MCMD_DEVS_BATT_MIN = 0x01, // min battery value + MCMD_DEVS_BATT_MAX = 0xFE, // max battery value + MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level +}; + +// Bit fields byte#3 of MCMD_LinkADRReq payload +enum { + MCMD_LinkADRReq_Redundancy_RFU = 0x80, + MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK= 0x70, + MCMD_LinkADRReq_Redundancy_NbTrans_MASK = 0x0F, + + MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT = 0x00, // direct masking for EU + MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON = 0x60, // EU: enable everything. + + MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K = 0x40, // mask is for the 8 us-like 500 kHz channels + MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL = 0x50, // first special for us-like + MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK = 0x50, // special: bits are banks. + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON = 0x60, // special channel page enable, bits applied to 64..71 + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71 + + MCMD_LinkADRReq_ChMaskCntl_CN470_ALL_ON = 0x60, // turn all on for China. +}; + +// Bit fields byte#0 of MCMD_LinkADRReq payload +enum { + MCMD_LinkADRReq_DR_MASK = 0xF0, + MCMD_LinkADRReq_POW_MASK = 0x0F, + MCMD_LinkADRReq_DR_SHIFT = 4, + MCMD_LinkADRReq_POW_SHIFT = 0, +}; + +// bit fields of the TxParam request +enum { + MCMD_TxParam_RxDWELL_SHIFT = 5, + MCMD_TxParam_RxDWELL_MASK = 1 << MCMD_TxParam_RxDWELL_SHIFT, + MCMD_TxParam_TxDWELL_SHIFT = 4, + MCMD_TxParam_TxDWELL_MASK = 1 << MCMD_TxParam_TxDWELL_SHIFT, + MCMD_TxParam_MaxEIRP_SHIFT = 0, + MCMD_TxParam_MaxEIRP_MASK = 0xF << MCMD_TxParam_MaxEIRP_SHIFT, +}; + +// Device address +typedef u4_t devaddr_t; + +// RX quality (device) +enum { RSSI_OFF=64, SNR_SCALEUP=4 }; + +static inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); } +static inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); } +static inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); } +static inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } +static inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } +static inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } +static inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } +static inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } +static inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } +static inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } +static inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { + return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); +} +#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) +// Two frames with params r1/r2 would interfere on air: same SFx + BWx +static inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } + +extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; +static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } +static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } +static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } +static inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } +static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate +static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate +static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR +static inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range +static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps + +// +// BEG: Keep in sync with lorabase.hpp +// ================================================================================ + + +// Calculate airtime +ostime_t calcAirTime (rps_t rps, u1_t plen); +// Sensitivity at given SF/BW +int getSensitivity (rps_t rps); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lorabase_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_as923.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_as923.h new file mode 100644 index 0000000..d65bb75 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_as923.h @@ -0,0 +1,105 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_as923_h_ +#define _lorabase_as923_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AS923 (always in scope) +| +\****************************************************************************/ + +enum _dr_as923_t { + AS923_DR_SF12 = 0, + AS923_DR_SF11, + AS923_DR_SF10, + AS923_DR_SF9, + AS923_DR_SF8, + AS923_DR_SF7, + AS923_DR_SF7B, + AS923_DR_FSK, + AS923_DR_NONE +}; + +// Bands: +// g1 : 1% 16dBm +// freq band datarates +enum { + AS923_F1 = 923200000, // g1 SF7-12 + AS923_F2 = 923400000, // g1 SF7-12 + AS923_FDOWN = 923200000, // (RX2 freq, DR2) + AS923_FBCN = 923400000, // default BCN, DR3 + AS923_FPING = 923400000, // default ping, DR3 +}; +enum { + AS923_FREQ_MIN = 915000000, + AS923_FREQ_MAX = 928000000 +}; +enum { + AS923_TX_EIRP_MAX_DBM = 16 // 16 dBm +}; +enum { DR_PAGE_AS923 = 0x10 * (LMIC_REGION_as923 - 1) }; + +enum { AS923_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { AS923JP_LBT_US = 5000 }; // microseconds of LBT time -- 5000 ==> + // 5 ms. We use us rather than ms for + // future 128us support, and just for + // backward compatibility -- there + // is code that uses the _US constant, + // and it's awkward to break it. + +enum { AS923JP_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX + // we measure more than this, we don't tx. + +// AS923 v1.1, all channels face a 1% duty cycle. So this will have to change +// in the future via a config. But this code base needs major changes for +// v1.1 in any case. +enum { AS923_V102_TX_CAP = 100 }; // v1.0.2 allows 100% + +#ifndef AS923_TX_CAP +# define AS923_TX_CAP AS923_V102_TX_CAP +#endif + +// TxParam defaults +enum { + // initial value of UplinkDwellTime before TxParamSetupReq received. + AS923_INITIAL_TxParam_UplinkDwellTime = 1, + // initial value of DownlinkDwellTime before TxParamSetupReq received. + AS923_INITIAL_TxParam_DownlinkDwellTime = 1, + AS923_UPLINK_DWELL_TIME_osticks = sec2osticks(20), +}; + +#endif /* _lorabase_as923_h_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_au915.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_au915.h new file mode 100644 index 0000000..7bac54e --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_au915.h @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_au915_h_ +#define _lorabase_au915_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AU 915 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for AU 915 MHz +enum _dr_au915_t { + AU915_DR_SF12 = 0, + AU915_DR_SF11, + AU915_DR_SF10, + AU915_DR_SF9, + AU915_DR_SF8, + AU915_DR_SF7, + AU915_DR_SF8C, + AU915_DR_NONE, + // Devices behind a router: + AU915_DR_SF12CR = 8, + AU915_DR_SF11CR, + AU915_DR_SF10CR, + AU915_DR_SF9CR, + AU915_DR_SF8CR, + AU915_DR_SF7CR +}; + +// Default frequency plan for AU 915MHz +enum { + AU915_125kHz_UPFBASE = 915200000, + AU915_125kHz_UPFSTEP = 200000, + AU915_500kHz_UPFBASE = 915900000, + AU915_500kHz_UPFSTEP = 1600000, + AU915_500kHz_DNFBASE = 923300000, + AU915_500kHz_DNFSTEP = 600000 +}; +enum { + AU915_FREQ_MIN = 915000000, + AU915_FREQ_MAX = 928000000 +}; +enum { + AU915_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; +enum { + // initial value of UplinkDwellTime before TxParamSetupReq received. + AU915_INITIAL_TxParam_UplinkDwellTime = 1, + AU915_UPLINK_DWELL_TIME_osticks = sec2osticks(20), +}; + +enum { DR_PAGE_AU915 = 0x10 * (LMIC_REGION_au915 - 1) }; + +enum { AU915_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_au915_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_eu868.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_eu868.h new file mode 100644 index 0000000..0040ad0 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_eu868.h @@ -0,0 +1,92 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_eu868_h_ +#define _lorabase_eu868_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for EU868 (always in scope) +| +\****************************************************************************/ + +// +// Default frequency plan for EU 868MHz ISM band +// data rates +// this is a little confusing: the integer values of these constants are the +// DataRates from the LoRaWAN Regional Parmaeter spec. The names are just +// convenient indications, so we can use them in the rare case that we need to +// choose a DataRate by SF and configuration, not by DR code. + +enum _dr_eu868_t { + EU868_DR_SF12 = 0, + EU868_DR_SF11, + EU868_DR_SF10, + EU868_DR_SF9, + EU868_DR_SF8, + EU868_DR_SF7, + EU868_DR_SF7B, + EU868_DR_FSK, + EU868_DR_NONE +}; + +// Bands: +// g1 : 1% 14dBm +// g2 : 0.1% 14dBm +// g3 : 10% 27dBm +// freq band datarates +enum { + EU868_F1 = 868100000, // g1 SF7-12 + EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250 + EU868_F3 = 868500000, // g1 SF7-12 + EU868_F4 = 868850000, // g2 SF7-12 + EU868_F5 = 869050000, // g2 SF7-12 + EU868_F6 = 869525000, // g3 SF7-12 + EU868_J4 = 864100000, // g2 SF7-12 used during join + EU868_J5 = 864300000, // g2 SF7-12 ditto + EU868_J6 = 864500000, // g2 SF7-12 ditto +}; +enum { + EU868_FREQ_MIN = 863000000, + EU868_FREQ_MAX = 870000000 +}; +enum { + EU868_TX_EIRP_MAX_DBM = 16 // 16 dBm EIRP. So subtract 3 dBm for a 3 dBi antenna. +}; + +enum { EU868_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { DR_PAGE_EU868 = 0x10 * (LMIC_REGION_eu868 - 1) }; + +#endif /* _lorabase_eu868_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_in866.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_in866.h new file mode 100644 index 0000000..6955a76 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_in866.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_in866_h_ +#define _lorabase_in866_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for IN866 (always in scope) +| +\****************************************************************************/ + +enum _dr_in866_t { + IN866_DR_SF12 = 0, // DR0 + IN866_DR_SF11, // DR1 + IN866_DR_SF10, // DR2 + IN866_DR_SF9, // DR3 + IN866_DR_SF8, // DR4 + IN866_DR_SF7, // DR5 + IN866_DR_RFU, // - + IN866_DR_FSK, // DR7 + IN866_DR_NONE +}; + +// There is no dwell-time or duty-cycle limitation for IN +// +// max power: 30dBM +// +// freq datarates +enum { + IN866_F1 = 865062500, // SF7-12 (DR0-5) + IN866_F2 = 865402500, // SF7-12 (DR0-5) + IN866_F3 = 865985000, // SF7-12 (DR0-5) + IN866_FB = 866550000, // beacon/ping +}; +enum { + IN866_FREQ_MIN = 865000000, + IN866_FREQ_MAX = 867000000 +}; +enum { + IN866_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; +enum { DR_PAGE_IN866 = 0x10 * (LMIC_REGION_in866 - 1) }; + +enum { IN866_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_in866_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_kr920.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_kr920.h new file mode 100644 index 0000000..02fd5a0 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_kr920.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017, 2019 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_kr920_h_ +#define _lorabase_kr920_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for KR920 (always in scope) +| +\****************************************************************************/ + +enum _dr_kr920_t { + KR920_DR_SF12 = 0, // DR0 + KR920_DR_SF11, // DR1 + KR920_DR_SF10, // DR2 + KR920_DR_SF9, // DR3 + KR920_DR_SF8, // DR4 + KR920_DR_SF7, // DR5 + KR920_DR_NONE +}; + +// There is no dwell-time or duty-cycle limitation for IN +// +// max power: 30dBM +// +// freq datarates +enum { + KR920_F1 = 922100000, // SF7-12 (DR0-5) + KR920_F2 = 922300000, // SF7-12 (DR0-5) + KR920_F3 = 922500000, // SF7-12 (DR0-5) + KR920_FBCN = 923100000, // beacon/ping + KR920_F14DBM = 922100000, // Allows 14 dBm (not 10) if >= this. + KR920_FDOWN = 921900000, // RX2 downlink frequency +}; +enum { + KR920_FREQ_MIN = 920900000, + KR920_FREQ_MAX = 923300000 +}; +enum { + KR920_TX_EIRP_MAX_DBM = 14, // 14 dBm for most + KR920_TX_EIRP_MAX_DBM_LOW = 10, // 10 dBm for some +}; +enum { DR_PAGE_KR920 = 0x10 * (LMIC_REGION_kr920 - 1) }; + +enum { KR920_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { KR920_LBT_US = 128 }; // microseconds of LBT time. + +enum { KR920_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX + // we measure more than this, we don't tx. + +#endif /* _lorabase_in866_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_us915.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_us915.h new file mode 100644 index 0000000..ed960c6 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorabase_us915.h @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_us915_h_ +#define _lorabase_us915_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for US915 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for US 915MHz ISM band +// data rates +enum _dr_us915_t { + US915_DR_SF10 = 0, + US915_DR_SF9, + US915_DR_SF8, + US915_DR_SF7, + US915_DR_SF8C, + US915_DR_NONE, + // Devices "behind a router" (and upper half of DR list): + US915_DR_SF12CR = 8, + US915_DR_SF11CR, + US915_DR_SF10CR, + US915_DR_SF9CR, + US915_DR_SF8CR, + US915_DR_SF7CR +}; + +// Default frequency plan for US 915MHz +enum { + US915_125kHz_UPFBASE = 902300000, + US915_125kHz_UPFSTEP = 200000, + US915_500kHz_UPFBASE = 903000000, + US915_500kHz_UPFSTEP = 1600000, + US915_500kHz_DNFBASE = 923300000, + US915_500kHz_DNFSTEP = 600000 +}; +enum { + US915_FREQ_MIN = 902000000, + US915_FREQ_MAX = 928000000 +}; +enum { + US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're + // on an 64-channel bandplan. See code + // that computes tx power. +}; + +enum { + US915_LinkAdrReq_POW_MAX_1_0_2 = 0xA, + US915_LinkAdrReq_POW_MAX_1_0_3 = 0xE, +}; + +enum { DR_PAGE_US915 = 0x10 * (LMIC_REGION_us915 - 1) }; + +enum { US915_LMIC_REGION_EIRP = 0 }; // region doesn't use EIRP, uses tx power + +#endif /* _lorabase_us915_h_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorawan_spec_compliance.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorawan_spec_compliance.h new file mode 100644 index 0000000..e479b24 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/lorawan_spec_compliance.h @@ -0,0 +1,64 @@ +/* + +Module: lorawan_spec_compliance.h + +Function: + Details from the LoRaWAN specification for compliance. + +Copyright notice and license info: + See LICENSE file accompanying this project. + +Author: + Terry Moore, MCCI Corporation March 2019 + +*/ + +#ifndef _lorawan_spec_COMPLIANCE_H_ /* prevent multiple includes */ +#define _lorawan_spec_COMPLIANCE_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +enum { + // the port for MAC commands + LORAWAN_PORT_MAC = 0u, + // the first port available for applications + LORAWAN_PORT_USER_MIN = 1u, + // the last port available for applications + LORAWAN_PORT_USER_MAX = 223u, + // the base of the reserved port numbers + LORAWAN_PORT_RESERVED = 224u, + // the port for the compliance protocol + LORAWAN_PORT_COMPLIANCE = LORAWAN_PORT_RESERVED + 0u, +}; + +enum lowawan_compliance_cmd_e { + LORAWAN_COMPLIANCE_CMD_DEACTIVATE = 0u, + LORAWAN_COMPLIANCE_CMD_ACTIVATE = 1u, + LORAWAN_COMPLIANCE_CMD_SET_CONFIRM = 2u, + LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM = 3u, + LORAWAN_COMPLIANCE_CMD_ECHO = 4u, + LORAWAN_COMPLIANCE_CMD_LINK = 5u, + LORAWAN_COMPLIANCE_CMD_JOIN = 6u, + LORAWAN_COMPLIANCE_CMD_CW = 7u, + LORAWAN_COMPLIANCE_CMD_MFG_BASE = 0x80u, +}; + +typedef unsigned char lorawan_compliance_cmd_t; + +// info on specific commands +enum { + LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN = 4u, + LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC = 1u, + + // Maximum crypto frame size; although the spec says 18, it + // is also used for testing max packet size. + LORAWAN_COMPLIANCE_CMD_ECHO_LEN_MAX = 242u, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _lorawan_spec_COMPLIANCE_H_ */ \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.c new file mode 100644 index 0000000..fa205f4 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2017, 2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +extern const struct lmic_pinmap lmic_pins; + +// RUNTIME STATE +static struct { + osjob_t* scheduledjobs; + osjob_t* runnablejobs; +} OS; + +int os_init_ex (const void *pintable) { + memset(&OS, 0x00, sizeof(OS)); + hal_init_ex(pintable); + if (! radio_init()) + return 0; + LMIC_init(); + return 1; +} + +void os_init() { + if (os_init_ex((const void *)&lmic_pins)) + return; + ASSERT(0); +} + +ostime_t os_getTime () { + return hal_ticks(); +} + +// unlink job from queue, return if removed +static int unlinkjob (osjob_t** pnext, osjob_t* job) { + for( ; *pnext; pnext = &((*pnext)->next)) { + if(*pnext == job) { // unlink + *pnext = job->next; + return 1; + } + } + return 0; +} + +static osjob_t** getJobQueue(osjob_t* job) { + return os_jobIsTimed(job) ? &OS.scheduledjobs : &OS.runnablejobs; +} + +// clear scheduled job +void os_clearCallback (osjob_t* job) { + hal_disableIRQs(); + + unlinkjob(getJobQueue(job), job); + + hal_enableIRQs(); +} + +// schedule immediately runnable job +void os_setCallback (osjob_t* job, osjobcb_t cb) { + osjob_t** pnext; + hal_disableIRQs(); + + // remove if job was already queued + unlinkjob(getJobQueue(job), job); + + // fill-in job. Ascending memory order is write-queue friendly + job->next = NULL; + job->deadline = 0; + job->func = cb; + + // add to end of run queue + for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); + *pnext = job; + hal_enableIRQs(); +} + +// schedule timed job +void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { + osjob_t** pnext; + + // special case time 0 -- it will be one tick late. + if (time == 0) + time = 1; + + hal_disableIRQs(); + + // remove if job was already queued + unlinkjob(getJobQueue(job), job); + + // fill-in job + job->next = NULL; + job->deadline = time; + job->func = cb; + + // insert into schedule + for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { + if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) + // enqueue before next element and stop + job->next = *pnext; + break; + } + } + *pnext = job; + hal_enableIRQs(); +} + +// execute jobs from timer and from run queue +void os_runloop () { + while(1) { + os_runloop_once(); + } +} + +void os_runloop_once() { + osjob_t* j = NULL; + hal_processPendingIRQs(); + + hal_disableIRQs(); + // check for runnable jobs + if(OS.runnablejobs) { + j = OS.runnablejobs; + OS.runnablejobs = j->next; + } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs + j = OS.scheduledjobs; + OS.scheduledjobs = j->next; + } else { // nothing pending + hal_sleep(); // wake by irq (timer already restarted) + } + hal_enableIRQs(); + if(j) { // run job callback + j->func(j); + } +} + +// return true if there are any jobs scheduled within time ticks from now. +// return false if any jobs scheduled are at least time ticks in the future. +bit_t os_queryTimeCriticalJobs(ostime_t time) { + if (OS.scheduledjobs && + OS.scheduledjobs->deadline - os_getTime() < time) + return 1; + else + return 0; +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.h new file mode 100644 index 0000000..448b029 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2018, 2019 MCCI Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#ifndef _oslmic_h_ +#define _oslmic_h_ + +// Dependencies required for the LMIC to run. +// These settings can be adapted to the underlying system. +// You should not, however, change the lmic merely for porting purposes.[hc] + +#include "config.h" + +#ifndef _lmic_env_h_ +# include "lmic_env.h" +#endif + +#ifndef _oslmic_types_h_ +# include "oslmic_types.h" +#endif + +LMIC_BEGIN_DECLS + + +#include +#include "hal.h" +#define EV(a,b,c) /**/ +#define DO_DEVDB(field1,field2) /**/ +#if !defined(CFG_noassert) +#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) +#else +#define ASSERT(cond) /**/ +#endif + +#define os_clearMem(a,b) memset(a,0,b) +#define os_copyMem(a,b,c) memcpy(a,b,c) + +typedef struct osjob_t osjob_t; +typedef struct band_t band_t; +typedef struct chnldef_t chnldef_t; +typedef struct rxsched_t rxsched_t; +typedef struct bcninfo_t bcninfo_t; +typedef const u1_t* xref2cu1_t; +typedef u1_t* xref2u1_t; + +// int32_t == s4_t is long on some platforms; and someday +// we will want 64-bit ostime_t. So, we will use a macro for the +// print formatting of ostime_t. +#ifndef LMIC_PRId_ostime_t +# include +# define LMIC_PRId_ostime_t PRId32 +#endif + +#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t +#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t +#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t +#define TYPEDEF_xref2band_t typedef band_t* xref2band_t +#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t + +#define SIZEOFEXPR(x) sizeof(x) + +#define DECL_ON_LMIC_EVENT LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e)) + +extern u4_t AESAUX[]; +extern u4_t AESKEY[]; +#define AESkey ((u1_t*)AESKEY) +#define AESaux ((u1_t*)AESAUX) +#define FUNC_ADDR(func) (&(func)) + +u1_t radio_rand1 (void); +#define os_getRndU1() radio_rand1() + +#define DEFINE_LMIC struct lmic_t LMIC +#define DECLARE_LMIC extern struct lmic_t LMIC + +typedef struct oslmic_radio_rssi_s oslmic_radio_rssi_t; + +struct oslmic_radio_rssi_s { + s2_t min_rssi; + s2_t max_rssi; + s2_t mean_rssi; + u2_t n_rssi; +}; + +int radio_init (void); +void radio_irq_handler (u1_t dio); +void radio_irq_handler_v2 (u1_t dio, ostime_t tref); +void os_init (void); +int os_init_ex (const void *pPinMap); +void os_runloop (void); +void os_runloop_once (void); +u1_t radio_rssi (void); +void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi); + +//================================================================================ + +#ifndef RX_RAMPUP_DEFAULT +//! \brief RX_RAMPUP_DEFAULT specifies the extra time we must allow to set up an RX event due +//! to platform issues. It's specified in units of ostime_t. It must reflect +//! platform jitter and latency, as well as the speed of the LMIC when running +//! on this plaform. It's not used directly; clients call os_getRadioRxRampup(), +//! which might adaptively vary this based on observed timeouts. +#define RX_RAMPUP_DEFAULT (us2osticks(10000)) +#endif + +#ifndef TX_RAMPUP +// TX_RAMPUP specifies the extra time we must allow to set up a TX event) due +// to platform issues. It's specified in units of ostime_t. It must reflect +// platform jitter and latency, as well as the speed of the LMIC when running +// on this plaform. +#define TX_RAMPUP (us2osticks(10000)) +#endif + +#ifndef OSTICKS_PER_SEC +#define OSTICKS_PER_SEC 32768 +#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 +#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. +#endif + +#if !HAS_ostick_conv +#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) +#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) +#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) +#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) +#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) +// Special versions +#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) +#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) +#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) +#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) +#endif + + +struct osjob_t; // fwd decl. + +//! the function type for osjob_t callbacks +typedef void (osjobcbfn_t)(struct osjob_t*); + +//! the pointer-to-function for osjob_t callbacks +typedef osjobcbfn_t *osjobcb_t; + +struct osjob_t { + struct osjob_t* next; + ostime_t deadline; + osjobcb_t func; +}; +TYPEDEF_xref2osjob_t; + +//! determine whether a job is timed or immediate. os_setTimedCallback() +// must treat incoming == 0 as being 1 instead. +static inline int os_jobIsTimed(xref2osjob_t job) { + return (job->deadline != 0); +} + +#ifndef HAS_os_calls + +#ifndef os_getDevKey +void os_getDevKey (xref2u1_t buf); +#endif +#ifndef os_getArtEui +void os_getArtEui (xref2u1_t buf); +#endif +#ifndef os_getDevEui +void os_getDevEui (xref2u1_t buf); +#endif +#ifndef os_setCallback +void os_setCallback (xref2osjob_t job, osjobcb_t cb); +#endif +#ifndef os_setTimedCallback +void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); +#endif +#ifndef os_clearCallback +void os_clearCallback (xref2osjob_t job); +#endif +#ifndef os_getRadioRxRampup +ostime_t os_getRadioRxRampup (void); +#endif +#ifndef os_getTime +ostime_t os_getTime (void); +#endif +#ifndef os_getTimeSecs +uint os_getTimeSecs (void); +#endif +#ifndef os_radio +void os_radio (u1_t mode); +#endif +#ifndef os_getBattLevel +u1_t os_getBattLevel (void); +#endif +#ifndef os_queryTimeCriticalJobs +//! Return non-zero if any jobs are scheduled between now and now+time. +bit_t os_queryTimeCriticalJobs(ostime_t time); +#endif + +#ifndef os_rlsbf4 +//! Read 32-bit quantity from given pointer in little endian byte order. +u4_t os_rlsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf4 +//! Write 32-bit quntity into buffer in little endian byte order. +void os_wlsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rmsbf4 +//! Read 32-bit quantity from given pointer in big endian byte order. +u4_t os_rmsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wmsbf4 +//! Write 32-bit quntity into buffer in big endian byte order. +void os_wmsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rlsbf2 +//! Read 16-bit quantity from given pointer in little endian byte order. +u2_t os_rlsbf2 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf2 +//! Write 16-bit quntity into buffer in little endian byte order. +void os_wlsbf2 (xref2u1_t buf, u2_t value); +#endif + +//! Get random number (default impl for u2_t). +#ifndef os_getRndU2 +#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) +#endif +#ifndef os_crc16 +u2_t os_crc16 (xref2cu1_t d, uint len); +#endif + +#endif // !HAS_os_calls + +// ====================================================================== +// Table support +// These macros for defining a table of constants and retrieving values +// from it makes it easier for other platforms (like AVR) to optimize +// table accesses. +// Use CONST_TABLE() whenever declaring or defining a table, and +// TABLE_GET_xx whenever accessing its values. The actual name of the +// declared variable will be modified to prevent accidental direct +// access. The accessor macros forward to an inline function to allow +// proper type checking of the array element type. + +// Helper to add a prefix to the table name +#define RESOLVE_TABLE(table) constant_table_ ## table + +// get number of entries in table +#define LENOF_TABLE(table) (sizeof(RESOLVE_TABLE(table)) / sizeof(RESOLVE_TABLE(table)[0])) + +// Accessors for table elements +#define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) +#define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) +#define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) +#define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) +#define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) +#define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) +#define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) +#define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) + +#if defined(__AVR__) + #include + // Macro to define the getter functions. This loads data from + // progmem using pgm_read_xx, or accesses memory directly when the + // index is a constant so gcc can optimize it away; + #define TABLE_GETTER(postfix, type, pgm_type) \ + static inline type table_get ## postfix(const type *table, size_t index) { \ + if (__builtin_constant_p(table[index])) \ + return table[index]; \ + return pgm_read_ ## pgm_type(&table[index]); \ + } + + TABLE_GETTER(_u1, u1_t, byte); + TABLE_GETTER(_s1, s1_t, byte); + TABLE_GETTER(_u2, u2_t, word); + TABLE_GETTER(_s2, s2_t, word); + TABLE_GETTER(_u4, u4_t, dword); + TABLE_GETTER(_s4, s4_t, dword); + + // This assumes ostime_t is 4 bytes, so error out if it is not + typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; + TABLE_GETTER(_ostime, ostime_t, dword); + + // For AVR, store constants in PROGMEM, saving on RAM usage + #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) +#else + static inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } + static inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } + static inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } + static inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } + static inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } + static inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } + static inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } + + // Declare a table + #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) +#endif + +// ====================================================================== +// AES support +// !!Keep in sync with lorabase.hpp!! + +#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too +#define AES_ENC 0x00 +#define AES_DEC 0x01 +#define AES_MIC 0x02 +#define AES_CTR 0x04 +#define AES_MICNOAUX 0x08 +#endif +#ifndef AESkey // if AESkey is defined as macro all other values must be too +extern xref2u1_t AESkey; +extern xref2u1_t AESaux; +#endif +#ifndef os_aes +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); +#endif + +// ====================================================================== +// Simple logging support. Vanishes unless enabled. + +#if LMIC_ENABLE_event_logging +extern void LMICOS_logEvent(const char *pMessage); +extern void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); +#else // ! LMIC_ENABLE_event_logging +# define LMICOS_logEvent(m) do { ; } while (0) +# define LMICOS_logEventUint32(m, d) do { ; } while (0) +#endif // ! LMIC_ENABLE_event_logging + + +LMIC_END_DECLS + +#endif // _oslmic_h_ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic_types.h b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic_types.h new file mode 100644 index 0000000..d790a37 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/oslmic_types.h @@ -0,0 +1,47 @@ +/* + +Module: oslmic_types.h + +Function: + Basic types from oslmic.h, shared by all layers. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI November 2018 + (based on oslmic.h from IBM). + +*/ + +#ifndef _oslmic_types_h_ +# define _oslmic_types_h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//================================================================================ +//================================================================================ +// Target platform as C library +typedef uint8_t bit_t; +typedef uint8_t u1_t; +typedef int8_t s1_t; +typedef uint16_t u2_t; +typedef int16_t s2_t; +typedef uint32_t u4_t; +typedef int32_t s4_t; +typedef unsigned int uint; +typedef const char* str_t; + +// the HAL needs to give us ticks, so it ought to know the right type. +typedef s4_t ostime_t; + +#ifdef __cplusplus +} +#endif + +/* end of oslmic_types.h */ +#endif /* _oslmic_types_h_ */ diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/radio.c b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/radio.c new file mode 100644 index 0000000..d6cc908 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/lib/lmic/lmic/radio.c @@ -0,0 +1,1443 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2019 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +// ---------------------------------------- +// Registers Mapping +// // -type- 1272 vs 1276 +#define RegFifo 0x00 // common +#define RegOpMode 0x01 // common see below +#define FSKRegBitrateMsb 0x02 // - +#define FSKRegBitrateLsb 0x03 // - +#define FSKRegFdevMsb 0x04 // - +#define FSKRegFdevLsb 0x05 // - +#define RegFrfMsb 0x06 // common FSK: 1272: 915; 1276: 434 MHz +#define RegFrfMid 0x07 // common ditto +#define RegFrfLsb 0x08 // common ditto +#define RegPaConfig 0x09 // common see below, many diffs +#define RegPaRamp 0x0A // common see below: bits 6..4 are diff +#define RegOcp 0x0B // common - +#define RegLna 0x0C // common bits 4..0 are diff. +#define FSKRegRxConfig 0x0D // - +#define LORARegFifoAddrPtr 0x0D +#define FSKRegRssiConfig 0x0E // - +#define LORARegFifoTxBaseAddr 0x0E +#define FSKRegRssiCollision 0x0F // - +#define LORARegFifoRxBaseAddr 0x0F +#define FSKRegRssiThresh 0x10 // - +#define LORARegFifoRxCurrentAddr 0x10 +#define FSKRegRssiValue 0x11 // - +#define LORARegIrqFlagsMask 0x11 +#define FSKRegRxBw 0x12 // - +#define LORARegIrqFlags 0x12 +#define FSKRegAfcBw 0x13 // - +#define LORARegRxNbBytes 0x13 +#define FSKRegOokPeak 0x14 // - +#define LORARegRxHeaderCntValueMsb 0x14 +#define FSKRegOokFix 0x15 // - +#define LORARegRxHeaderCntValueLsb 0x15 +#define FSKRegOokAvg 0x16 // - +#define LORARegRxPacketCntValueMsb 0x16 +#define LORARegRxpacketCntValueLsb 0x17 +#define LORARegModemStat 0x18 +#define LORARegPktSnrValue 0x19 +#define FSKRegAfcFei 0x1A // - +#define LORARegPktRssiValue 0x1A +#define FSKRegAfcMsb 0x1B // - +#define LORARegRssiValue 0x1B +#define FSKRegAfcLsb 0x1C // - +#define LORARegHopChannel 0x1C +#define FSKRegFeiMsb 0x1D // - +#define LORARegModemConfig1 0x1D +#define FSKRegFeiLsb 0x1E // - +#define LORARegModemConfig2 0x1E +#define FSKRegPreambleDetect 0x1F // - +#define LORARegSymbTimeoutLsb 0x1F +#define FSKRegRxTimeout1 0x20 // - +#define LORARegPreambleMsb 0x20 +#define FSKRegRxTimeout2 0x21 // - +#define LORARegPreambleLsb 0x21 +#define FSKRegRxTimeout3 0x22 // - +#define LORARegPayloadLength 0x22 +#define FSKRegRxDelay 0x23 // - +#define LORARegPayloadMaxLength 0x23 +#define FSKRegOsc 0x24 // - +#define LORARegHopPeriod 0x24 +#define FSKRegPreambleMsb 0x25 // - +#define LORARegFifoRxByteAddr 0x25 +#define FSKRegPreambleLsb 0x26 // - +#define LORARegModemConfig3 0x26 +#define FSKRegSyncConfig 0x27 // - +#define LORARegFeiMsb 0x28 +#define FSKRegSyncValue1 0x28 // - +#define LORAFeiMib 0x29 +#define FSKRegSyncValue2 0x29 // - +#define LORARegFeiLsb 0x2A +#define FSKRegSyncValue3 0x2A // - +#define FSKRegSyncValue4 0x2B // - +#define LORARegRssiWideband 0x2C +#define FSKRegSyncValue5 0x2C // - +#define FSKRegSyncValue6 0x2D // - +#define FSKRegSyncValue7 0x2E // - +#define FSKRegSyncValue8 0x2F // - +#define LORARegIffReq1 0x2F +#define FSKRegPacketConfig1 0x30 // - +#define LORARegIffReq2 0x30 +#define FSKRegPacketConfig2 0x31 // - +#define LORARegDetectOptimize 0x31 +#define FSKRegPayloadLength 0x32 // - +#define FSKRegNodeAdrs 0x33 // - +#define LORARegInvertIQ 0x33 +#define FSKRegBroadcastAdrs 0x34 // - +#define FSKRegFifoThresh 0x35 // - +#define FSKRegSeqConfig1 0x36 // - +#define LORARegHighBwOptimize1 0x36 +#define FSKRegSeqConfig2 0x37 // - +#define LORARegDetectionThreshold 0x37 +#define FSKRegTimerResol 0x38 // - +#define FSKRegTimer1Coef 0x39 // - +#define LORARegSyncWord 0x39 +#define FSKRegTimer2Coef 0x3A // - +#define LORARegHighBwOptimize2 0x3A +#define FSKRegImageCal 0x3B // - +#define FSKRegTemp 0x3C // - +#define FSKRegLowBat 0x3D // - +#define FSKRegIrqFlags1 0x3E // - +#define FSKRegIrqFlags2 0x3F // - +#define RegDioMapping1 0x40 // common +#define RegDioMapping2 0x41 // common +#define RegVersion 0x42 // common +// #define RegAgcRef 0x43 // common +// #define RegAgcThresh1 0x44 // common +// #define RegAgcThresh2 0x45 // common +// #define RegAgcThresh3 0x46 // common +// #define RegPllHop 0x4B // common +// #define RegTcxo 0x58 // common +// #define RegPll 0x5C // common +// #define RegPllLowPn 0x5E // common +// #define RegFormerTemp 0x6C // common +// #define RegBitRateFrac 0x70 // common + +#if defined(CFG_sx1276_radio) +#define RegTcxo 0x4B // common different addresses, same bits +#define RegPaDac 0x4D // common differnet addresses, same bits +#elif defined(CFG_sx1272_radio) +#define RegTcxo 0x58 // common +#define RegPaDac 0x5A // common +#endif + +#define RegTcxo_TcxoInputOn (1u << 4) + +// ---------------------------------------- +// spread factors and mode for RegModemConfig2 +#define SX1272_MC2_FSK 0x00 +#define SX1272_MC2_SF7 0x70 +#define SX1272_MC2_SF8 0x80 +#define SX1272_MC2_SF9 0x90 +#define SX1272_MC2_SF10 0xA0 +#define SX1272_MC2_SF11 0xB0 +#define SX1272_MC2_SF12 0xC0 +// bandwidth for RegModemConfig1 +#define SX1272_MC1_BW_125 0x00 +#define SX1272_MC1_BW_250 0x40 +#define SX1272_MC1_BW_500 0x80 +// coding rate for RegModemConfig1 +#define SX1272_MC1_CR_4_5 0x08 +#define SX1272_MC1_CR_4_6 0x10 +#define SX1272_MC1_CR_4_7 0x18 +#define SX1272_MC1_CR_4_8 0x20 +#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive +#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 +#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 +// transmit power configuration for RegPaConfig +#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 +#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 + + +// sx1276 RegModemConfig1 +#define SX1276_MC1_BW_125 0x70 +#define SX1276_MC1_BW_250 0x80 +#define SX1276_MC1_BW_500 0x90 +#define SX1276_MC1_CR_4_5 0x02 +#define SX1276_MC1_CR_4_6 0x04 +#define SX1276_MC1_CR_4_7 0x06 +#define SX1276_MC1_CR_4_8 0x08 + +#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 + +#ifdef CFG_sx1276_radio +# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON SX1276_MC1_IMPLICIT_HEADER_MODE_ON +#else +# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON SX1272_MC1_IMPLICIT_HEADER_MODE_ON +#endif + +// transmit power configuration for RegPaConfig +#define SX1276_PAC_PA_SELECT_PA_BOOST 0x80 +#define SX1276_PAC_PA_SELECT_RFIO_PIN 0x00 +#define SX1276_PAC_MAX_POWER_MASK 0x70 + +// the bits to change for max power. +#define SX127X_PADAC_POWER_MASK 0x07 +#define SX127X_PADAC_POWER_NORMAL 0x04 +#define SX127X_PADAC_POWER_20dBm 0x07 + +// convert milliamperes to equivalent value for +// RegOcp; delivers conservative value. +#define SX127X_OCP_MAtoBITS(mA) \ + ((mA) < 45 ? 0 : \ + (mA) <= 120 ? ((mA) - 45) / 5 : \ + (mA) < 130 ? 0xF : \ + (mA) < 240 ? ((mA) - 130) / 10 + 0x10 : \ + 27) + +// bit in RegOcp that enables overcurrent protect. +#define SX127X_OCP_ENA 0x20 + +// sx1276 RegModemConfig2 +#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 + +// sx1276 RegModemConfig3 +#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 +#define SX1276_MC3_AGCAUTO 0x04 + +// preamble for lora networks (nibbles swapped) +#define LORA_MAC_PREAMBLE 0x34 + +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A +#ifdef CFG_sx1276_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 +#elif CFG_sx1272_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 +#endif + +//----------------------------------------- +// Parameters for RSSI monitoring +#define SX127X_FREQ_LF_MAX 525000000 // per datasheet 6.3 + +// per datasheet 5.5.3 and 5.5.5: +#define SX1272_RSSI_ADJUST -139 // add to rssi value to get dB (LF) + +// per datasheet 5.5.3 and 5.5.5: +#define SX1276_RSSI_ADJUST_LF -164 // add to rssi value to get dB (LF) +#define SX1276_RSSI_ADJUST_HF -157 // add to rssi value to get dB (HF) + +#ifdef CFG_sx1276_radio +# define SX127X_RSSI_ADJUST_LF SX1276_RSSI_ADJUST_LF +# define SX127X_RSSI_ADJUST_HF SX1276_RSSI_ADJUST_HF +#else +# define SX127X_RSSI_ADJUST_LF SX1272_RSSI_ADJUST +# define SX127X_RSSI_ADJUST_HF SX1272_RSSI_ADJUST +#endif + +// per datasheet 2.5.2 (but note that we ought to ask Semtech to confirm, because +// datasheet is unclear). +#define SX127X_RX_POWER_UP us2osticks(500) // delay this long to let the receiver power up. + +// ---------------------------------------- +// Constants for radio registers +#define OPMODE_LORA 0x80 +#define OPMODE_MASK 0x07 +#define OPMODE_SLEEP 0x00 +#define OPMODE_STANDBY 0x01 +#define OPMODE_FSTX 0x02 +#define OPMODE_TX 0x03 +#define OPMODE_FSRX 0x04 +#define OPMODE_RX 0x05 +#define OPMODE_RX_SINGLE 0x06 +#define OPMODE_CAD 0x07 + +// ---------------------------------------- +// FSK opmode bits +// bits 6:5 are the same for 1272 and 1276 +#define OPMODE_FSK_SX127x_ModulationType_FSK (0u << 5) +#define OPMODE_FSK_SX127x_ModulationType_OOK (1u << 5) +#define OPMODE_FSK_SX127x_ModulationType_MASK (3u << 5) + +// bits 4:3 are different for 1272 +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT1_0 (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5 (2u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_3 (3u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_BR (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_2BR (2u << 3) + +#define OPMODE_FSK_SX1272_ModulationShaping_MASK (3u << 3) + +// SX1276 +#define OPMODE_FSK_SX1276_LowFrequencyModeOn (1u << 3) + +// define the opmode bits apporpriate for the 127x in use. +#if defined(CFG_sx1272_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK | \ + OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5) +#elif defined(CFG_sx1276_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK) +#endif + +// ---------------------------------------- +// LoRa opmode bits +#define OPMODE_LORA_SX127x_AccessSharedReg (1u << 6) +#define OPMODE_LORA_SX1276_LowFrequencyModeOn (1u << 3) + +// ---------------------------------------- +// Bits masking the corresponding IRQs from the radio +#define IRQ_LORA_RXTOUT_MASK 0x80 +#define IRQ_LORA_RXDONE_MASK 0x40 +#define IRQ_LORA_CRCERR_MASK 0x20 +#define IRQ_LORA_HEADER_MASK 0x10 +#define IRQ_LORA_TXDONE_MASK 0x08 +#define IRQ_LORA_CDDONE_MASK 0x04 +#define IRQ_LORA_FHSSCH_MASK 0x02 +#define IRQ_LORA_CDDETD_MASK 0x01 + +#define IRQ_FSK1_MODEREADY_MASK 0x80 +#define IRQ_FSK1_RXREADY_MASK 0x40 +#define IRQ_FSK1_TXREADY_MASK 0x20 +#define IRQ_FSK1_PLLLOCK_MASK 0x10 +#define IRQ_FSK1_RSSI_MASK 0x08 +#define IRQ_FSK1_TIMEOUT_MASK 0x04 +#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 +#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 +#define IRQ_FSK2_FIFOFULL_MASK 0x80 +#define IRQ_FSK2_FIFOEMPTY_MASK 0x40 +#define IRQ_FSK2_FIFOLEVEL_MASK 0x20 +#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 +#define IRQ_FSK2_PACKETSENT_MASK 0x08 +#define IRQ_FSK2_PAYLOADREADY_MASK 0x04 +#define IRQ_FSK2_CRCOK_MASK 0x02 +#define IRQ_FSK2_LOWBAT_MASK 0x01 + +// ---------------------------------------- +// DIO function mappings D0D1D2D3 +#define MAP_DIO0_LORA_RXDONE 0x00 // 00------ +#define MAP_DIO0_LORA_TXDONE 0x40 // 01------ +#define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- +#define MAP_DIO1_LORA_NOP 0x30 // --11---- +#define MAP_DIO2_LORA_NOP 0x0C // ----11-- + +#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) +#define MAP_DIO1_FSK_NOP 0x30 // --11---- +#define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- +#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- + + +// FSK IMAGECAL defines +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +// LNA gain constant. Bits 4..0 have different meaning for 1272 and 1276, but +// by chance, the bit patterns we use are the same. +#ifdef CFG_sx1276_radio +#define LNA_RX_GAIN (0x20|0x3) +#elif CFG_sx1272_radio +#define LNA_RX_GAIN (0x20|0x03) +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + +// RADIO STATE +// (initialized by radio_init(), used by radio_rand1()) +static u1_t randbuf[16]; + + +static void writeReg (u1_t addr, u1_t data ) { + hal_spi_write(addr | 0x80, &data, 1); +} + +static u1_t readReg (u1_t addr) { + u1_t buf[1]; + hal_spi_read(addr & 0x7f, buf, 1); + return buf[0]; +} + +static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { + hal_spi_write(addr | 0x80, buf, len); +} + +static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) { + hal_spi_read(addr & 0x7f, buf, len); +} + +static void requestModuleActive(bit_t state) { + ostime_t const ticks = hal_setModuleActive(state); + + if (ticks) + hal_waitUntil(os_getTime() + ticks);; +} + +static void writeOpmode(u1_t mode) { + u1_t const maskedMode = mode & OPMODE_MASK; + if (maskedMode != OPMODE_SLEEP) + requestModuleActive(1); + writeReg(RegOpMode, mode); + if (maskedMode == OPMODE_SLEEP) + requestModuleActive(0); +} + +static void opmode (u1_t mode) { + writeOpmode((readReg(RegOpMode) & ~OPMODE_MASK) | mode); +} + +static void opmodeLora() { + u1_t u = OPMODE_LORA; +#ifdef CFG_sx1276_radio + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } +#endif + writeOpmode(u); +} + +static void opmodeFSK() { + u1_t u = OPMODE_FSK_SX127X_SETUP; + +#ifdef CFG_sx1276_radio + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } +#endif + writeOpmode(u); +} + +// configure LoRa modem (cfg1, cfg2) +static void configLoraModem () { + sf_t sf = getSf(LMIC.rps); + +#ifdef CFG_sx1276_radio + u1_t mc1 = 0, mc2 = 0, mc3 = 0; + + bw_t const bw = getBw(LMIC.rps); + + switch (bw) { + case BW125: mc1 |= SX1276_MC1_BW_125; break; + case BW250: mc1 |= SX1276_MC1_BW_250; break; + case BW500: mc1 |= SX1276_MC1_BW_500; break; + default: + ASSERT(0); + } + switch( getCr(LMIC.rps) ) { + case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break; + case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break; + case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break; + case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break; + default: + ASSERT(0); + } + + if (getIh(LMIC.rps)) { + mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON; + writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length + } + // set ModemConfig1 + writeReg(LORARegModemConfig1, mc1); + + mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4) + ((LMIC.rxsyms >> 8) & 0x3) ); + if (getNocrc(LMIC.rps) == 0) { + mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON; + } +#if CFG_TxContinuousMode + // Only for testing + // set ModemConfig2 (sf, TxContinuousMode=1, AgcAutoOn=1 SymbTimeoutHi=00) + mc2 |= 0x8; +#endif + writeReg(LORARegModemConfig2, mc2); + + mc3 = SX1276_MC3_AGCAUTO; + + if ( ((sf == SF11 || sf == SF12) && bw == BW125) || + ((sf == SF12) && bw == BW250) ) { + mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE; + } + writeReg(LORARegModemConfig3, mc3); + + // Errata 2.1: Sensitivity optimization with 500 kHz bandwidth + u1_t rHighBwOptimize1; + u1_t rHighBwOptimize2; + + rHighBwOptimize1 = 0x03; + rHighBwOptimize2 = 0; + + if (bw == BW500) { + if (LMIC.freq > SX127X_FREQ_LF_MAX) { + rHighBwOptimize1 = 0x02; + rHighBwOptimize2 = 0x64; + } else { + rHighBwOptimize1 = 0x02; + rHighBwOptimize2 = 0x7F; + } + } + + writeReg(LORARegHighBwOptimize1, rHighBwOptimize1); + if (rHighBwOptimize2 != 0) + writeReg(LORARegHighBwOptimize2, rHighBwOptimize2); + +#elif CFG_sx1272_radio + u1_t mc1 = (getBw(LMIC.rps)<<6); + + switch( getCr(LMIC.rps) ) { + case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break; + case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break; + case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break; + case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break; + } + + if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) { + mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE; + } + + if (getNocrc(LMIC.rps) == 0) { + mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON; + } + + if (getIh(LMIC.rps)) { + mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON; + writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length + } + // set ModemConfig1 + writeReg(LORARegModemConfig1, mc1); + + // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi) + u1_t mc2; + mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04 | ((LMIC.rxsyms >> 8) & 0x3); + +#if CFG_TxContinuousMode + // Only for testing + // set ModemConfig2 (sf, TxContinuousMode=1, AgcAutoOn=1 SymbTimeoutHi=00) + mc2 |= 0x8; +#endif + + writeReg(LORARegModemConfig2, mc2); + +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif /* CFG_sx1272_radio */ +} + +static void configChannel () { + // set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19) + uint64_t frf = ((uint64_t)LMIC.freq << 19) / 32000000; + writeReg(RegFrfMsb, (u1_t)(frf>>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); +} + +// On the SX1276, we have several possible configs. +// 1) using RFO, MaxPower==0: in that case power is -4 to 11 dBm +// 2) using RFO, MaxPower==7: in that case, power is 0 to 14 dBm +// (can't select 15 dBm). +// note we can use -4..11 w/o Max and then 12..14 w/Max, and +// we really don't need to ask anybody. +// 3) using PA_BOOST, PaDac = 4: in that case power range is 2 to 17 dBm; +// use this for 15..17 if authorized. +// 4) using PA_BOOST, PaDac = 7, OutputPower=0xF: in that case, power is 20 dBm +// (and perhaps 0xE is 19, 0xD is 18 dBm, but datasheet isn't clear.) +// and duty cycle must be <= 1%. +// +// In addition, there are some boards for which PA_BOOST can only be used if the +// channel frequency is greater than SX127X_FREQ_LF_MAX. +// +// The SX1272 is similar but has no MaxPower bit: +// 1) using RFO: power is -1 to 13 dBm (datasheet implies max OutputPower value is 14 for 13 dBm) +// 2) using PA_BOOST, PaDac = 0x84: power is 2 to 17 dBm; +// use this for 14..17 if authorized +// 3) using PA_BOOST, PaDac = 0x87, OutputPower = 0xF: power is 20dBm +// and duty cycle must be <= 1% +// +// The general policy is to use the lowest power variant that will get us where we +// need to be. +// + +static void configPower () { + // our input paramter -- might be different than LMIC.txpow! + s1_t const req_pw = (s1_t)LMIC.radio_txpow; + // the effective power + s1_t eff_pw; + // the policy; we're going to compute this. + u1_t policy; + // what we'll write to RegPaConfig + u1_t rPaConfig; + // what we'll write to RegPaDac + u1_t rPaDac; + // what we'll write to RegOcp + u1_t rOcp; + +#ifdef CFG_sx1276_radio + if (req_pw >= 20) { + policy = LMICHAL_radio_tx_power_policy_20dBm; + eff_pw = 20; + } else if (req_pw >= 14) { + policy = LMICHAL_radio_tx_power_policy_paboost; + if (req_pw > 17) { + eff_pw = 17; + } else { + eff_pw = req_pw; + } + } else { + policy = LMICHAL_radio_tx_power_policy_rfo; + if (req_pw < -4) { + eff_pw = -4; + } else { + eff_pw = req_pw; + } + } + + policy = hal_getTxPowerPolicy(policy, eff_pw, LMIC.freq); + + switch (policy) { + default: + case LMICHAL_radio_tx_power_policy_rfo: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(80); + + if (eff_pw > 14) + eff_pw = 14; + if (eff_pw > 11) { + // some Semtech code uses this down to eff_pw == 0. + rPaConfig = eff_pw | SX1276_PAC_MAX_POWER_MASK; + } else { + if (eff_pw < -4) + eff_pw = -4; + rPaConfig = eff_pw + 4; + } + break; + + // some radios (HopeRF RFM95W) don't support RFO well, + // so the policy might *raise* rfo to paboost. That means + // we have to re-check eff_pw, which might be too small. + // (And, of course, it might also be too large.) + case LMICHAL_radio_tx_power_policy_paboost: + // It seems that SX127x doesn't like eff_pw 10 when in FSK mode. + if (getSf(LMIC.rps) == FSK && eff_pw < 11) { + eff_pw = 11; + } + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(100); + if (eff_pw > 17) + eff_pw = 17; + else if (eff_pw < 2) + eff_pw = 2; + rPaConfig = (eff_pw - 2) | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + + case LMICHAL_radio_tx_power_policy_20dBm: + rPaDac = SX127X_PADAC_POWER_20dBm; + rOcp = SX127X_OCP_MAtoBITS(130); + rPaConfig = 0xF | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + } + +#elif CFG_sx1272_radio + if (req_pw >= 20) { + policy = LMICHAL_radio_tx_power_policy_20dBm; + eff_pw = 20; + } else if (eff_pw >= 14) { + policy = LMICHAL_radio_tx_power_policy_paboost; + if (eff_pw > 17) { + eff_pw = 17; + } else { + eff_pw = req_pw; + } + } else { + policy = LMICHAL_radio_tx_power_policy_rfo; + if (req_pw < -1) { + eff_pw = -1; + } else { + eff_pw = req_pw; + } + } + + policy = hal_getTxPowerPolicy(policy, eff_pw, LMIC.freq); + + switch (policy) { + default: + case LMICHAL_radio_tx_power_policy_rfo: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(50); + + if (eff_pw > 13) + eff_pw = 13; + + rPaConfig = eff_pw + 1; + break; + + case LMICHAL_radio_tx_power_policy_paboost: + rPaDac = SX127X_PADAC_POWER_NORMAL; + rOcp = SX127X_OCP_MAtoBITS(100); + + if (eff_pw > 17) + eff_pw = 17; + + rPaConfig = (eff_pw - 2) | SX1272_PAC_PA_SELECT_PA_BOOST; + break; + + case LMICHAL_radio_tx_power_policy_20dBm: + rPaDac = SX127X_PADAC_POWER_20dBm; + rOcp = SX127X_OCP_MAtoBITS(130); + + rPaConfig = 0xF | SX1276_PAC_PA_SELECT_PA_BOOST; + break; + } +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif /* CFG_sx1272_radio */ + + writeReg(RegPaConfig, rPaConfig); + writeReg(RegPaDac, (readReg(RegPaDac) & ~SX127X_PADAC_POWER_MASK) | rPaDac); + writeReg(RegOcp, rOcp | SX127X_OCP_ENA); +} + +static void setupFskRxTx(bit_t fDisableAutoClear) { + // set bitrate + writeReg(FSKRegBitrateMsb, 0x02); // 50kbps + writeReg(FSKRegBitrateLsb, 0x80); + // set frequency deviation + writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz + writeReg(FSKRegFdevLsb, 0x99); + + // set sync config + writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync + + // set packet config + writeReg(FSKRegPacketConfig1, fDisableAutoClear ? 0xD8 : 0xD0); // var-length, whitening, crc, no auto-clear, no adr filter + writeReg(FSKRegPacketConfig2, 0x40); // packet mode + + // set sync value + writeReg(FSKRegSyncValue1, 0xC1); + writeReg(FSKRegSyncValue2, 0x94); + writeReg(FSKRegSyncValue3, 0xC1); +} + +static void txfsk () { + // select FSK modem (from sleep mode) + opmodeFSK(); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // set bitrate etc + setupFskRxTx(/* don't autoclear CRC */ 0); + + // frame and packet handler settings + writeReg(FSKRegPreambleMsb, 0x00); + writeReg(FSKRegPreambleLsb, 0x05); + + // configure frequency + configChannel(); + // configure output power + configPower(); + +#ifdef CFG_sx1276_radio + // select Gausian filter BT=0.5, default ramp. + writeReg(RegPaRamp, 0x29); +#endif + + // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP); + + // initialize the payload size and address pointers + // TODO(tmm@mcci.com): datasheet says this is not used in variable packet length mode + writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) + + // download length byte and buffer to the radio FIFO + writeReg(RegFifo, LMIC.dataLen); + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + if (LMIC.txend) { + u4_t nLate = hal_waitUntil(LMIC.txend); // busy wait until exact tx time + if (nLate > 0) { + LMIC.radio.txlate_ticks += nLate; + ++LMIC.radio.txlate_count; + } + } + LMICOS_logEventUint32("+Tx FSK", LMIC.dataLen); + opmode(OPMODE_TX); +} + +static void txlora () { + // select LoRa modem (from sleep mode) + //writeReg(RegOpMode, OPMODE_LORA); + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + // configure output power +#ifdef CFG_sx1272_radio + writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec +#elif defined(CFG_sx1276_radio) + writeReg(RegPaRamp, 0x08); // set PA ramp-up time 50 uSec, clear FSK bits +#endif + configPower(); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // mask all IRQs but TxDone + writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); + + // initialize the payload size and address pointers + writeReg(LORARegFifoTxBaseAddr, 0x00); + writeReg(LORARegFifoAddrPtr, 0x00); + writeReg(LORARegPayloadLength, LMIC.dataLen); + + // download buffer to the radio FIFO + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + if (LMIC.txend) { + u4_t nLate = hal_waitUntil(LMIC.txend); // busy wait until exact tx time + if (nLate) { + LMIC.radio.txlate_ticks += nLate; + ++LMIC.radio.txlate_count; + } + } + LMICOS_logEventUint32("+Tx LoRa", LMIC.dataLen); + opmode(OPMODE_TX); + +#if LMIC_DEBUG_LEVEL > 0 + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": TXMODE, freq=%"PRIu32", len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), LMIC.freq, LMIC.dataLen, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); +#endif +} + +// start transmitter (buf=LMIC.frame, len=LMIC.dataLen) +static void starttx () { + u1_t const rOpMode = readReg(RegOpMode); + + // originally, this code ASSERT()ed, but asserts are both bad and + // blunt instruments. If we see that we're not in sleep mode, + // force sleep (because we might have to switch modes) + if ((rOpMode & OPMODE_MASK) != OPMODE_SLEEP) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("?%s: OPMODE != OPMODE_SLEEP: %#02x\n", __func__, rOpMode); +#endif + opmode(OPMODE_SLEEP); + hal_waitUntil(os_getTime() + ms2osticks(1)); + } + + if (LMIC.lbt_ticks > 0) { + oslmic_radio_rssi_t rssi; + radio_monitor_rssi(LMIC.lbt_ticks, &rssi); +#if LMIC_X_DEBUG_LEVEL > 0 + LMIC_X_DEBUG_PRINTF("LBT rssi max:min=%d:%d %d times in %d\n", rssi.max_rssi, rssi.min_rssi, rssi.n_rssi, LMIC.lbt_ticks); +#endif + + if (rssi.max_rssi >= LMIC.lbt_dbmax) { + // complete the request by scheduling the job + os_setCallback(&LMIC.osjob, LMIC.osjob.func); + return; + } + } + + if(getSf(LMIC.rps) == FSK) { // FSK modem + txfsk(); + } else { // LoRa modem + txlora(); + } + // the radio will go back to STANDBY mode as soon as the TX is finished + // the corresponding IRQ will inform us about completion. +} + +enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; + +static CONST_TABLE(u1_t, rxlorairqmask)[] = { + [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, + [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, + [RXMODE_RSSI] = 0x00, +}; + +//! \brief handle late RX events. +//! \param nLate is the number of `ostime_t` ticks that the event was late. +//! \details If nLate is non-zero, increment the count of events, totalize +//! the number of ticks late, and (if implemented) adjust the estimate of +//! what would be best to return from `os_getRadioRxRampup()`. +static void rxlate (u4_t nLate) { + if (nLate) { + LMIC.radio.rxlate_ticks += nLate; + ++LMIC.radio.rxlate_count; + } +} + +// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) +static void rxlora (u1_t rxmode) { + // select LoRa modem (from sleep mode) + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // don't use MAC settings at startup + if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan + writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); + writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); + } else { // single or continuous rx mode + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + } + // set LNA gain + writeReg(RegLna, LNA_RX_GAIN); + // set max payload size + writeReg(LORARegPayloadMaxLength, MAX_LEN_FRAME); +#if !defined(DISABLE_INVERT_IQ_ON_RX) /* DEPRECATED(tmm@mcci.com); #250. remove test, always include code in V3 */ + // use inverted I/Q signal (prevent mote-to-mote communication) + + // XXX: use flag to switch on/off inversion + if (LMIC.noRXIQinversion) { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6)); + } else { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); + } +#endif + + // Errata 2.3 - receiver spurious reception of a LoRa signal + bw_t const bw = getBw(LMIC.rps); + u1_t const rDetectOptimize = (readReg(LORARegDetectOptimize) & 0x78) | 0x03; + if (bw < BW500) { + writeReg(LORARegDetectOptimize, rDetectOptimize); + writeReg(LORARegIffReq1, 0x40); + writeReg(LORARegIffReq2, 0x40); + } else { + writeReg(LORARegDetectOptimize, rDetectOptimize | 0x80); + } + + // set symbol timeout (for single rx) + writeReg(LORARegSymbTimeoutLsb, (uint8_t) LMIC.rxsyms); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // enable required radio IRQs + writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode)); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + writeReg(LORARegFifoAddrPtr, 0); + writeReg(LORARegFifoRxBaseAddr, 0); + + // now instruct the radio to receive + if (rxmode == RXMODE_SINGLE) { // single rx + u4_t nLate = hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX_SINGLE); + LMICOS_logEventUint32("+Rx LoRa Single", nLate); + rxlate(nLate); +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now = os_getTime(); + LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %"LMIC_PRId_ostime_t"\n", now - LMIC.rxtime); +#endif + } else { // continous rx (scan or rssi) + LMICOS_logEventUint32("+Rx LoRa Continuous", rxmode); + opmode(OPMODE_RX); + } + +#if LMIC_DEBUG_LEVEL > 0 + if (rxmode == RXMODE_RSSI) { + LMIC_DEBUG_PRINTF("RXMODE_RSSI\n"); + } else { + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": %s, freq=%"PRIu32", SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), + rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), + LMIC.freq, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); + } +#endif +} + +static void rxfsk (u1_t rxmode) { + // only single or continuous rx (no noise sampling) + if (rxmode == RXMODE_SCAN) { + // indicate no bytes received. + LMIC.dataLen = 0; + // complete the request by scheduling the job. + os_setCallback(&LMIC.osjob, LMIC.osjob.func); + } + + // select FSK modem (from sleep mode) + //writeReg(RegOpMode, 0x00); // (not LoRa) + opmodeFSK(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // configure frequency + configChannel(); + // set LNA gain + writeReg(RegLna, LNA_RX_GAIN); // max gain, boost enable. + // configure receiver + writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? + // set receiver bandwidth + writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb + // set AFC bandwidth + writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB + // set preamble detection + writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors + // set preamble timeout + writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2); + // set bitrate, autoclear CRC + setupFskRxTx(1); + + // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut + writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + // now instruct the radio to receive + if (rxmode == RXMODE_SINGLE) { + u4_t nLate = hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX); // no single rx mode available in FSK + LMICOS_logEventUint32("+Rx FSK", nLate); + rxlate(nLate); + } else { + LMICOS_logEvent("+Rx FSK Continuous"); + opmode(OPMODE_RX); + } +} + +static void startrx (u1_t rxmode) { + ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); + if(getSf(LMIC.rps) == FSK) { // FSK modem + rxfsk(rxmode); + } else { // LoRa modem + rxlora(rxmode); + } + // the radio will go back to STANDBY mode as soon as the RX is finished + // or timed out, and the corresponding IRQ will inform us about completion. +} + +//! \brief Initialize radio at system startup. +//! +//! \details This procedure is called during initialization by the `os_init()` +//! routine. It does a hardware reset of the radio, checks the version and confirms +//! that we're operating a suitable chip, and gets a random seed from wideband +//! noise rssi. It then puts the radio to sleep. +//! +//! \result True if successful, false if it doesn't look like the right radio is attached. +//! +//! \pre +//! Preconditions must be observed, or you'll get hangs during initialization. +//! +//! - The `hal_pin_..()` functions must be ready for use. +//! - The `hal_waitUntl()` function must be ready for use. This may mean that interrupts +//! are enabled. +//! - The `hal_spi_..()` functions must be ready for use. +//! +//! Generally, all these are satisfied by a call to `hal_init_with_pinmap()`. +//! +int radio_init () { + requestModuleActive(1); + + // manually reset radio +#ifdef CFG_sx1276_radio + hal_pin_rst(0); // drive RST pin low +#else + hal_pin_rst(1); // drive RST pin high +#endif + hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us + hal_pin_rst(2); // configure RST pin floating! + hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms + + opmode(OPMODE_SLEEP); + + // some sanity checks, e.g., read version number + u1_t v = readReg(RegVersion); +#ifdef CFG_sx1276_radio + if(v != 0x12 ) + return 0; +#elif CFG_sx1272_radio + if(v != 0x22) + return 0; +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + // set the tcxo input, if needed + if (hal_queryUsingTcxo()) + writeReg(RegTcxo, readReg(RegTcxo) | RegTcxo_TcxoInputOn); + + // seed 15-byte randomness via noise rssi + rxlora(RXMODE_RSSI); + while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx + for(int i=1; i<16; i++) { + for(int j=0; j<8; j++) { + u1_t b; // wait for two non-identical subsequent least-significant bits + while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); + randbuf[i] = (randbuf[i] << 1) | b; + } + } + randbuf[0] = 16; // set initial index + +#ifdef CFG_sx1276mb1_board + // chain calibration + writeReg(RegPaConfig, 0); + + // Launch Rx chain calibration for LF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } + + // Sets a Frequency in HF band + u4_t frf = 868000000; + writeReg(RegFrfMsb, (u1_t)(frf>>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); + + // Launch Rx chain calibration for HF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } +#endif /* CFG_sx1276mb1_board */ + + opmode(OPMODE_SLEEP); + + return 1; +} + +// return next random byte derived from seed buffer +// (buf[0] holds index of next byte to be returned) +u1_t radio_rand1 () { + u1_t i = randbuf[0]; + ASSERT( i != 0 ); + if( i==16 ) { + os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key + i = 0; + } + u1_t v = randbuf[i++]; + randbuf[0] = i; + return v; +} + +u1_t radio_rssi () { + u1_t r = readReg(LORARegRssiValue); + return r; +} + +/// \brief get the current RSSI on the current channel. +/// +/// monitor rssi for specified number of ostime_t ticks, and return statistics +/// This puts the radio into RX continuous mode, waits long enough for the +/// oscillators to start and the PLL to lock, and then measures for the specified +/// period of time. The radio is then returned to idle. +/// +/// RSSI returned is expressed in units of dB, and is offset according to the +/// current radio setting per section 5.5.5 of Semtech 1276 datasheet. +/// +/// \param nTicks How long to monitor +/// \param pRssi pointer to structure to fill in with RSSI data. +/// +void radio_monitor_rssi(ostime_t nTicks, oslmic_radio_rssi_t *pRssi) { + uint8_t rssiMax, rssiMin; + uint16_t rssiSum; + uint16_t rssiN; + + int rssiAdjust; + ostime_t tBegin; + int notDone; + + rxlora(RXMODE_SCAN); + + // while we're waiting for the PLLs to spin up, determine which + // band we're in and choose the base RSSI. +#if defined(CFG_sx1276_radio) + if (LMIC.freq > SX127X_FREQ_LF_MAX) { + rssiAdjust = SX1276_RSSI_ADJUST_HF; + } else { + rssiAdjust = SX1276_RSSI_ADJUST_LF; + } +#elif defined(CFG_sx1272_radio) + rssiAdjust = SX1272_RSSI_ADJUST; +#endif + rssiAdjust += hal_getRssiCal(); + + // zero the results + rssiMax = 255; + rssiMin = 0; + rssiSum = 0; + rssiN = 0; + + // wait for PLLs + hal_waitUntil(os_getTime() + SX127X_RX_POWER_UP); + + // scan for the desired time. + tBegin = os_getTime(); + rssiMax = 0; + + /* Per bug report from tanupoo, it's critical that interrupts be enabled + * in the loop below so that `os_getTime()` always advances. + */ + do { + ostime_t now; + + u1_t rssiNow = readReg(LORARegRssiValue); + + if (rssiMax < rssiNow) + rssiMax = rssiNow; + if (rssiNow < rssiMin) + rssiMin = rssiNow; + rssiSum += rssiNow; + ++rssiN; + now = os_getTime(); + notDone = now - (tBegin + nTicks) < 0; + } while (notDone); + + // put radio back to sleep + opmode(OPMODE_SLEEP); + + // compute the results + pRssi->max_rssi = (s2_t) (rssiMax + rssiAdjust); + pRssi->min_rssi = (s2_t) (rssiMin + rssiAdjust); + pRssi->mean_rssi = (s2_t) (rssiAdjust + ((rssiSum + (rssiN >> 1)) / rssiN)); + pRssi->n_rssi = rssiN; +} + +static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = { + [FSK] = us2osticks(0), // ( 0 ticks) + [SF7] = us2osticks(0), // ( 0 ticks) + [SF8] = us2osticks(1648), // ( 54 ticks) + [SF9] = us2osticks(3265), // ( 107 ticks) + [SF10] = us2osticks(7049), // ( 231 ticks) + [SF11] = us2osticks(13641), // ( 447 ticks) + [SF12] = us2osticks(31189), // (1022 ticks) +}; + +// called by hal ext IRQ handler +// (radio goes to stanby mode after tx/rx operations) +void radio_irq_handler (u1_t dio) { + radio_irq_handler_v2(dio, os_getTime()); +} + +void radio_irq_handler_v2 (u1_t dio, ostime_t now) { + LMIC_API_PARAMETER(dio); + +#if CFG_TxContinuousMode + // in continuous mode, we don't use the now parameter. + LMIC_UNREFERENCED_PARAMETER(now); + + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + u1_t p = readReg(LORARegFifoAddrPtr); + writeReg(LORARegFifoAddrPtr, 0x00); + u1_t s = readReg(RegOpMode); + u1_t c = readReg(LORARegModemConfig2); + LMICOS_logEventUint32("+Tx LoRa Continuous", (r << 8) + c); + opmode(OPMODE_TX); + return; +#else /* ! CFG_TxContinuousMode */ + +#if LMIC_DEBUG_LEVEL > 0 + ostime_t const entry = now; +#endif + if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem + u1_t flags = readReg(LORARegIrqFlags); + LMIC.saveIrqFlags = flags; + LMICOS_logEventUint32("radio_irq_handler_v2: LoRa", flags); + LMIC_X_DEBUG_PRINTF("IRQ=%02x\n", flags); + if( flags & IRQ_LORA_TXDONE_MASK ) { + // save exact tx time + LMIC.txend = now - us2osticks(43); // TXDONE FIXUP + } else if( flags & IRQ_LORA_RXDONE_MASK ) { + // save exact rx time + if(getBw(LMIC.rps) == BW125) { + now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps)); + } + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = (readReg(LORARegModemConfig1) & SX127X_MC1_IMPLICIT_HEADER_MODE_ON) ? + readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); + // set FIFO read address pointer + writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 + u1_t const rRssi = readReg(LORARegPktRssiValue); + s2_t rssi = rRssi; + if (LMIC.freq > SX127X_FREQ_LF_MAX) + rssi += SX127X_RSSI_ADJUST_HF; + else + rssi += SX127X_RSSI_ADJUST_LF; + if (LMIC.snr < 0) + rssi = rssi - (-LMIC.snr >> 2); + else if (rssi > -100) { + // correct nonlinearity -- this is the same as multiplying rRssi * 16/15 initially. + rssi += (rRssi / 15); + } + + LMIC_X_DEBUG_PRINTF("RX snr=%u rssi=%d\n", LMIC.snr/4, rssi); + // ugh compatibility requires a biased range. RSSI + LMIC.rssi = (s1_t) (RSSI_OFF + (rssi < -196 ? -196 : rssi > 63 ? 63 : rssi)); // RSSI [dBm] (-196...+63) + } else if( flags & IRQ_LORA_RXTOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now2 = os_getTime(); + LMIC_DEBUG_PRINTF("rxtimeout: entry: %"LMIC_PRId_ostime_t" rxtime: %"LMIC_PRId_ostime_t" entry-rxtime: %"LMIC_PRId_ostime_t" now-entry: %"LMIC_PRId_ostime_t" rxtime-txend: %"LMIC_PRId_ostime_t"\n", entry, + LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend); +#endif + } + // mask all radio IRQs + writeReg(LORARegIrqFlagsMask, 0xFF); + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + } else { // FSK modem + u1_t flags1 = readReg(FSKRegIrqFlags1); + u1_t flags2 = readReg(FSKRegIrqFlags2); + + LMICOS_logEventUint32("*radio_irq_handler_v2: FSK", ((u2_t)flags2 << 8) | flags1); + + if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { + // save exact tx time + LMIC.txend = now; + } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { + // save exact rx time + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = readReg(FSKRegPayloadLength); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = 0; // SX127x doesn't give SNR for FSK. + LMIC.rssi = -64 + RSSI_OFF; // SX127x doesn't give packet RSSI for FSK, + // so substitute a dummy value. + } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; + } else { + // ASSERT(0); + // we're not sure why we're here... treat as timeout. + LMIC.dataLen = 0; + } + + // in FSK, we need to put the radio in standby first. + opmode(OPMODE_STANDBY); + } + // go from standby to sleep + opmode(OPMODE_SLEEP); + // run os job (use preset func ptr) + os_setCallback(&LMIC.osjob, LMIC.osjob.func); +#endif /* ! CFG_TxContinuousMode */ +} + +/*! + +\brief Initiate a radio operation. + +\param mode Selects the operation to be performed. + +The requested radio operation is initiated. Some operations complete +immediately; others require hardware to do work, and don't complete until +an interrupt occurs. In that case, `LMIC.osjob` is scheduled. Because the +interrupt may occur right away, it's important that the caller initialize +`LMIC.osjob` before calling this routine. + +- `RADIO_RST` causes the radio to be put to sleep. No interrupt follows; +when control returns, the radio is ready for the next operation. + +- `RADIO_TX` and `RADIO_TX_AT` launch the transmission of a frame. An interrupt will +occur, which will cause `LMIC.osjob` to be scheduled with its current +function. + +- `RADIO_RX` and `RADIO_RX_ON` launch either single or continuous receives. +An interrupt will occur when a packet is recieved or the receive times out, +which will cause `LMIC.osjob` to be scheduled with its current function. + +*/ + +void os_radio (u1_t mode) { + switch (mode) { + case RADIO_RST: + // put radio to sleep + opmode(OPMODE_SLEEP); + break; + + case RADIO_TX: + // transmit frame now + LMIC.txend = 0; + starttx(); // buf=LMIC.frame, len=LMIC.dataLen + break; + + case RADIO_TX_AT: + if (LMIC.txend == 0) + LMIC.txend = 1; + starttx(); + break; + + case RADIO_RX: + // receive frame now (exactly at rxtime) + startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms + break; + + case RADIO_RXON: + // start scanning for beacon now + startrx(RXMODE_SCAN); // buf=LMIC.frame + break; + } +} + +ostime_t os_getRadioRxRampup (void) { + return RX_RAMPUP_DEFAULT; +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/platformio.ini b/3_SENSOR_lorawan-ds18b20-local-lmic/platformio.ini new file mode 100644 index 0000000..077aec1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/platformio.ini @@ -0,0 +1,14 @@ +[env:ttgo-lora32-v1] +platform = espressif32 +board = ttgo-lora32-v1 ; LilyGO LoRa32 T3 V1.6.1 kompatibel +framework = arduino +monitor_speed = 115200 + +lib_deps = + gamegine/HCSR04 ultrasonic sensor + adafruit/Adafruit SSD1306 + adafruit/Adafruit GFX Library + + +build_flags = + -D LMIC_USE_INTERRUPTS \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/project_config/lmic_project_config.h b/3_SENSOR_lorawan-ds18b20-local-lmic/project_config/lmic_project_config.h new file mode 100644 index 0000000..9e62cff --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/project_config/lmic_project_config.h @@ -0,0 +1,14 @@ +#ifndef _LMIC_PROJECT_CONFIG_H_ +#define _LMIC_PROJECT_CONFIG_H_ + +// LoRaWAN Region +#define CFG_eu868 1 + +// LoRa-Chip (LilyGO T3 V1.6.1 verwendet SX1276) +#define CFG_sx1276_radio 1 + +// Debug-Ausgabe reduzieren +#define LMIC_PRINTF_TO Serial +#define LMIC_DEBUG_LEVEL 0 + +#endif diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/src/main.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/src/main.cpp new file mode 100644 index 0000000..73351b1 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/src/main.cpp @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include +#include +#include + +// HC-SR04 Ultraschallsensor Pins +#define TRIG_PIN 12 +#define ECHO_PIN 13 + +// Batterie-Messung (LilyGO T3 V1.6.1) +#define BATTERY_PIN 35 // ADC1_CH7 für Batterie-Messung +#define BATTERY_ADC_MULTIPLIER 2.0 // Spannungsteiler 1:1 (2× Faktor) + +// Tank-Konfiguration (Füllstandsmessung) +#define TANK_HEIGHT 160.0 // Tank-Höhe in cm (innen) +#define SENSOR_MOUNT_OFFSET 14.0 // Montageabstand: Sensor über Tank-Oberkante in cm +#define SENSOR_MIN_DISTANCE 2.0 // HC-SR04 Mindestabstand (technische Grenze) + +// Deep Sleep Konfiguration (Batteriebetrieb) +#define ENABLE_DEEP_SLEEP false // true = Deep Sleep aktiv, false = Deep Sleep deaktiviert (für Tests) +#define SLEEP_INTERVAL 300 // Sleep-Zeit in Sekunden (1800 = 30 Minuten) +#define DISPLAY_ON_TIME 10000 // Display-Anzeigedauer in ms (30000 = 30 Sekunden) +#define uS_TO_S_FACTOR 1000000 // Umrechnung Mikrosekunden zu Sekunden + +HCSR04 distanceSensor(TRIG_PIN, ECHO_PIN); + +// OLED-Display (I2C: SDA 21, SCL 22 beim LilyGO T3 V1.6.1) +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +// LoRaWAN Keys (hier deine echten Werte eintragen) +static const u1_t PROGMEM APPEUI[8] = { 0x01, 0x00, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM DEVEUI[8] = { 0xB2, 0xA1, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; +static const u1_t PROGMEM APPKEY[16] = { 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x38, 0x47, 0x56, 0xAB, 0xCD, 0xEF, 0x12 }; + +void os_getArtEui(u1_t *buf) { memcpy_P(buf, APPEUI, 8); } +void os_getDevEui(u1_t *buf) { memcpy_P(buf, DEVEUI, 8); } +void os_getDevKey(u1_t *buf) { memcpy_P(buf, APPKEY, 16); } + +static osjob_t sendjob; +const unsigned TX_INTERVAL = 30; // Nicht verwendet bei Deep Sleep + +// RTC Memory für Deep Sleep (bleibt beim Sleep erhalten) +RTC_DATA_ATTR int bootCount = 0; +RTC_DATA_ATTR bool joinedNetwork = false; + +// Globale Variablen +unsigned long displayStartTime = 0; +bool dataSent = false; +bool userDataScheduled = false; // Flag: Nutzdaten (nicht nur MAC) wurden gesendet + +// LoRa Pinout für LilyGO T3 V1.6.1 +const lmic_pinmap lmic_pins = { + .nss = 18, + .rxtx = LMIC_UNUSED_PIN, + .rst = 23, + .dio = {26, 33, 32} +}; + +// Forward-Deklaration +void do_send(osjob_t* j); + +// Deep Sleep aktivieren +void goToSleep() { + #if ENABLE_DEEP_SLEEP + Serial.println(F("=== Deep Sleep aktivieren ===")); + Serial.print(F("Schlafdauer: ")); + Serial.print(SLEEP_INTERVAL); + Serial.print(F(" Sekunden (")); + Serial.print(SLEEP_INTERVAL / 60); + Serial.println(F(" Minuten)")); + + // Display ausschalten + display.clearDisplay(); + display.display(); + display.ssd1306_command(0xAE); // Display OFF + + // Deep Sleep konfigurieren + esp_sleep_enable_timer_wakeup(SLEEP_INTERVAL * uS_TO_S_FACTOR); + + Serial.println(F("Gehe in Deep Sleep...")); + Serial.flush(); + + // Deep Sleep starten + esp_deep_sleep_start(); + #else + Serial.println(F("=== Deep Sleep DEAKTIVIERT (Test-Modus) ===")); + Serial.print(F("Nächste Messung in ")); + Serial.print(SLEEP_INTERVAL); + Serial.println(F(" Sekunden...")); + + // Variablen zurücksetzen für nächste Messung + dataSent = false; + displayStartTime = 0; + userDataScheduled = false; + + // Warte SLEEP_INTERVAL Sekunden, dann sende erneut + os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(SLEEP_INTERVAL), do_send); + #endif +} + +void do_send(osjob_t* j) { + // Batteriespannung messen + uint16_t batteryRaw = analogRead(BATTERY_PIN); + float batteryVoltage = (batteryRaw / 4095.0) * 3.3 * BATTERY_ADC_MULTIPLIER; + + // Batterie-Prozent berechnen (LiPo 3.0V = 0%, 4.2V = 100%) + float batteryPercent = ((batteryVoltage - 3.0) / (4.2 - 3.0)) * 100.0; + if (batteryPercent < 0) batteryPercent = 0; + if (batteryPercent > 100) batteryPercent = 100; + + // Distanz messen (in cm) + float distance = distanceSensor.dist(); + + // Prüfen ob Messung gültig ist + if (distance < 0) { + Serial.println(F("Fehler beim Messen der Distanz!")); + distance = 0; // Fehlerfall: 0 senden + } + + // Füllstand berechnen + // Gesamtabstand bei leerem Tank = Montageabstand + Tank-Höhe + float emptyDistance = SENSOR_MOUNT_OFFSET + TANK_HEIGHT; + + // Füllhöhe = Leerer Tank - gemessene Distanz + float fillHeight = emptyDistance - distance; + + // Korrektur: Negativ = leer, über Tank-Höhe = voll + if (fillHeight < 0) fillHeight = 0; + if (fillHeight > TANK_HEIGHT) fillHeight = TANK_HEIGHT; + + // Füllstand in Prozent + float fillPercent = (fillHeight / TANK_HEIGHT) * 100.0; + + // Warnung bei kritischem Abstand (zu nah am Sensor) + bool tooClose = (distance < (SENSOR_MOUNT_OFFSET + SENSOR_MIN_DISTANCE)); + + // Distanz in mm umrechnen und als uint16_t speichern (0-65535 mm = 0-65.5 m) + uint16_t distanceMm = (uint16_t)(distance * 10); + + // Füllstand in mm für LoRaWAN + uint16_t fillHeightMm = (uint16_t)(fillHeight * 10); + + Serial.println(F("--- Messung ---")); + Serial.print("Batterie: "); Serial.print(batteryVoltage, 2); Serial.print("V ("); + Serial.print(batteryPercent, 0); Serial.println("%)"); + Serial.print("Distanz: "); Serial.print(distance); Serial.println(" cm"); + Serial.print("Füllhöhe: "); Serial.print(fillHeight); Serial.println(" cm"); + Serial.print("Füllstand: "); Serial.print(fillPercent, 1); Serial.println(" %"); + if (tooClose) { + Serial.println(F("WARNUNG: Flüssigkeit zu nah am Sensor!")); + } + + // Display aktualisieren + display.clearDisplay(); + + // Header: iotwave.de + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println(F("iotwave.de")); + display.drawLine(0, 10, 128, 10, SSD1306_WHITE); + + // Füllstand groß anzeigen + display.setTextSize(3); + display.setCursor(0, 16); + display.print(fillPercent, 0); + display.println(F("%")); + + // Füllhöhe klein anzeigen + display.setTextSize(1); + display.setCursor(0, 48); + display.print(F("Fuellhoehe: ")); + display.print(fillHeight, 1); + display.println(F("cm")); + + // Distanz anzeigen + display.setCursor(0, 56); + display.print(F("Distanz: ")); + display.print(distance, 1); + display.println(F("cm")); + + display.display(); + + // Display-Timer starten (nur beim ersten Mal) + if (displayStartTime == 0) { + displayStartTime = millis(); + Serial.println(F("Display-Timer gestartet")); + } + + // Prüfen ob LMIC bereit ist zum Senden + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, nicht gesendet")); + } else { + // LoRaWAN Payload vorbereiten (8 Bytes): + // Byte 0-1: Distanz in mm (uint16_t) + // Byte 2-3: Füllhöhe in mm (uint16_t) + // Byte 4-5: Füllstand in % * 10 (z.B. 855 = 85.5%) + // Byte 6-7: Batteriespannung in mV (uint16_t) + uint16_t fillPercentInt = (uint16_t)(fillPercent * 10); + uint16_t batteryMv = (uint16_t)(batteryVoltage * 1000); + + uint8_t payload[8] = { + highByte(distanceMm), // Distanz High Byte + lowByte(distanceMm), // Distanz Low Byte + highByte(fillHeightMm), // Füllhöhe High Byte + lowByte(fillHeightMm), // Füllhöhe Low Byte + highByte(fillPercentInt), // Füllstand % High Byte + lowByte(fillPercentInt), // Füllstand % Low Byte + highByte(batteryMv), // Batterie High Byte + lowByte(batteryMv) // Batterie Low Byte + }; + + // Debug: Payload anzeigen + Serial.print(F("LoRa Payload (Hex): ")); + for (int i = 0; i < 8; i++) { + if (payload[i] < 16) Serial.print("0"); + Serial.print(payload[i], HEX); + Serial.print(" "); + } + Serial.println(); + + LMIC_setTxData2(1, payload, sizeof(payload), 0); + Serial.println(F("LoRa Payload gesendet")); + userDataScheduled = true; // Markiere dass Nutzdaten gesendet wurden + } +} + +void onEvent(ev_t ev) { + switch(ev) { + case EV_JOINING: + Serial.println(F("Beitritt läuft...")); + break; + case EV_JOINED: + Serial.println(F("Netzwerkbeitritt erfolgreich")); + joinedNetwork = true; // Merken für nächsten Boot + // Warte 5 Sekunden, damit MAC-Commands abgeschlossen werden + os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(5), do_send); + break; + case EV_TXCOMPLETE: + Serial.println(F("Nachricht übertragen.")); + + // Debug: Frame Counter + Serial.print(F("Frame Counter: ")); + Serial.println(LMIC.seqnoUp - 1); + + if (LMIC.txrxFlags & TXRX_ACK) { + Serial.println(F("ACK empfangen")); + } + + if (LMIC.dataLen > 0) { + Serial.print(F("Downlink empfangen: ")); + Serial.print(LMIC.dataLen); + Serial.println(F(" Bytes")); + } + + // Prüfe ob echte Nutzdaten gesendet wurden + if (userDataScheduled) { + Serial.println(F("Nutzdaten erfolgreich gesendet")); + // Markiere Daten als gesendet + if (!dataSent) { + dataSent = true; + Serial.print(F("Übertragung abgeschlossen. Display bleibt noch ")); + Serial.print((DISPLAY_ON_TIME - (millis() - displayStartTime)) / 1000); + Serial.println(F(" Sekunden an...")); + } + } else { + Serial.println(F("Nur MAC-Commands gesendet, warte auf nächstes TX...")); + // Sende Nutzdaten erneut nach 3 Sekunden + os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(3), do_send); + } + break; + default: + Serial.print(F("Ereignis: ")); Serial.println(ev); break; + } +} + +void setup() { + Serial.begin(115200); + delay(1000); + + // Boot-Zähler erhöhen + bootCount++; + Serial.println(F("\n=== ESP32 aufgewacht ===")); + Serial.print(F("Boot-Nummer: ")); + Serial.println(bootCount); + + // Wakeup-Grund anzeigen + esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); + if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) { + Serial.println(F("Aufgewacht durch Timer (5 Minuten vergangen)")); + } else { + Serial.println(F("Erster Start oder Reset")); + bootCount = 1; + joinedNetwork = false; + } + + // OLED Display initialisieren + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + display.clearDisplay(); + + // Kurzer Startup-Screen (nur 1 Sekunde, Batterie sparen!) + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(0, 0); + display.println(F("iotwave.de")); + display.setCursor(0, 20); + display.print(F("Boot #")); + display.println(bootCount); + display.setCursor(0, 35); + display.println(F("Initialisiere...")); + display.display(); + delay(1000); + + // LMIC initialisieren + os_init(); + LMIC_reset(); + LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); + + // OTAA Join starten + LMIC_startJoining(); + + Serial.println(F("Starte LoRaWAN...")); + + // Variablen zurücksetzen + dataSent = false; + displayStartTime = 0; + userDataScheduled = false; +} + +void loop() { + os_runloop_once(); + + // Prüfe ob Display-Zeit abgelaufen ist und Daten gesendet wurden + if (dataSent && displayStartTime > 0) { + unsigned long currentTime = millis(); + unsigned long elapsedTime = currentTime - displayStartTime; + + // Nach DISPLAY_ON_TIME (30 Sekunden): Deep Sleep + if (elapsedTime >= DISPLAY_ON_TIME) { + Serial.println(F("Display-Zeit abgelaufen, gehe in Deep Sleep...")); + goToSleep(); + } + } +} \ No newline at end of file diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/test_only_sensor.txt b/3_SENSOR_lorawan-ds18b20-local-lmic/test_only_sensor.txt new file mode 100644 index 0000000..9ab5a7a --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/test_only_sensor.txt @@ -0,0 +1,27 @@ +// Temporär in src/main.cpp einfügen zum Testen +// Ersetzt den kompletten Inhalt temporär + +#include +#include + +#define TRIG_PIN 12 +#define ECHO_PIN 13 + +HCSR04 distanceSensor(TRIG_PIN, ECHO_PIN); + +void setup() { + Serial.begin(115200); + delay(2000); + Serial.println("HC-SR04 Test - NUR Sensor, kein LoRa"); + Serial.println("===================================="); +} + +void loop() { + float distance = distanceSensor.dist(); + + Serial.print("Distanz: "); + Serial.print(distance); + Serial.println(" cm"); + + delay(1000); +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor.cpp new file mode 100644 index 0000000..1870119 --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor.cpp @@ -0,0 +1,46 @@ +// HC-SR04 Test Programm +// Dieses Programm testet nur den HC-SR04 Sensor ohne LoRaWAN + +#include +#include + +// HC-SR04 Pins +#define TRIG_PIN 12 +#define ECHO_PIN 13 + +HCSR04 distanceSensor(TRIG_PIN, ECHO_PIN); + +void setup() { + Serial.begin(115200); + delay(2000); + Serial.println("HC-SR04 Sensor Test"); + Serial.println("=================="); + Serial.print("TRIG Pin: GPIO "); + Serial.println(TRIG_PIN); + Serial.print("ECHO Pin: GPIO "); + Serial.println(ECHO_PIN); + Serial.println(); +} + +void loop() { + // Distanz messen + float distance = distanceSensor.dist(); + + Serial.print("Roher Wert: "); + Serial.print(distance); + Serial.print(" cm"); + + if (distance < 0) { + Serial.println(" -> FEHLER (Sensor antwortet nicht)"); + } else if (distance == 0) { + Serial.println(" -> 0 cm (zu nah oder kein Echo)"); + } else if (distance > 400) { + Serial.println(" -> Zu weit (außerhalb Messbereich)"); + } else { + Serial.print(" -> OK ("); + Serial.print(distance * 10); + Serial.println(" mm)"); + } + + delay(1000); // 1 Sekunde warten +} diff --git a/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor_only.cpp b/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor_only.cpp new file mode 100644 index 0000000..d83d73b --- /dev/null +++ b/3_SENSOR_lorawan-ds18b20-local-lmic/test_sensor_only.cpp @@ -0,0 +1,41 @@ +// Temporär in src/main.cpp einfügen zum Testen +// Ersetzt den kompletten Inhalt temporär + +#include +#include + +#define TRIG_PIN 12 +#define ECHO_PIN 13 + +HCSR04 distanceSensor(TRIG_PIN, ECHO_PIN); + +void setup() { + Serial.begin(115200); + delay(2000); + Serial.println("HC-SR04 Test - NUR Sensor, kein LoRa"); + Serial.println("===================================="); + Serial.println("Spannung: 3.3V"); + Serial.println(); +} + +void loop() { + float distance = distanceSensor.dist(); + + Serial.print("Distanz: "); + Serial.print(distance); + Serial.print(" cm"); + + if (distance < 0) { + Serial.println(" -> FEHLER (Sensor antwortet nicht)"); + } else if (distance == 0) { + Serial.println(" -> 0 cm (Sensor bekommt kein Echo zurück)"); + } else if (distance > 400) { + Serial.println(" -> Außerhalb Messbereich"); + } else { + Serial.print(" -> OK ("); + Serial.print(distance * 10); + Serial.println(" mm)"); + } + + delay(1000); +} diff --git a/LORAWEB_NEU/docs/superpowers/specs/2026-03-19-loraweb-design.md b/LORAWEB_NEU/docs/superpowers/specs/2026-03-19-loraweb-design.md new file mode 100644 index 0000000..89b8442 --- /dev/null +++ b/LORAWEB_NEU/docs/superpowers/specs/2026-03-19-loraweb-design.md @@ -0,0 +1,320 @@ +# LoRaWAN Web Portal – Design Specification + +**Date:** 2026-03-19 +**Based on:** Pflichtenheft v2.2 (März 2026) +**Approach:** Bottom-up implementation, pflichtenheft as goal with flexibility in implementation details + +--- + +## 1. Overview + +Web-based monitoring portal for LoRaWAN sensor infrastructure. Receives uplink data from IoT devices via ChirpStack HTTP Integration, stores measurements in InfluxDB, manages device/sensor configuration in SQLite, and presents everything through a secured React frontend. + +### Key Decisions (Deviations from Pflichtenheft) + +| Area | Pflichtenheft | Implementation | Reason | +|------|--------------|----------------|--------| +| SQLx | 0.7 | 0.8 | Current version, better SQLite support | +| Axum | 0.7 | 0.8 | Current version, improved API | +| Vite | 5 | 6 | Faster builds, no breaking changes for us | +| Tailwind | 3.4 | 4 | CSS-based engine, better performance | +| Rust Edition | 2021 | 2024 | Current standard | +| Deploy path | — | /opt/loraweb | As specified in pflichtenheft | + +React 18, `config` crate, and core architecture remain unchanged. + +--- + +## 2. Project Structure + +``` +LORAWEB_NEU/ +├── Cargo.toml # Workspace root (members: backend/daemon, backend/api) +├── backend/ +│ ├── daemon/ +│ │ ├── Cargo.toml +│ │ ├── Dockerfile +│ │ └── src/ +│ │ ├── main.rs # Tokio runtime, task spawning +│ │ ├── config.rs # AppConfig via config crate +│ │ ├── http.rs # Axum HTTP ingest listener +│ │ ├── influx.rs # InfluxDB writer +│ │ ├── db.rs # Topic registration (SQLite) +│ │ └── alarm.rs # Alarm monitor loop +│ └── api/ +│ ├── Cargo.toml +│ ├── Dockerfile +│ └── src/ +│ ├── main.rs # Router, DB init, AppState +│ ├── auth.rs # JWT, Argon2, TOTP +│ ├── sensors.rs # CRUD + data + display-config +│ ├── topics.rs # Topic management +│ ├── alarms.rs # Alarm endpoints +│ └── users.rs # User CRUD + 2FA +├── frontend/ +│ ├── src/ +│ │ ├── App.tsx # Router, protected routes +│ │ ├── api/client.ts # Axios instance + API functions +│ │ ├── store/authStore.ts # React Context auth state +│ │ ├── pages/ +│ │ │ ├── LoginPage.tsx +│ │ │ ├── DashboardPage.tsx +│ │ │ ├── SensorsPage.tsx # DataModal, multi-chart, mobile cards +│ │ │ ├── UnknownTopicsPage.tsx +│ │ │ ├── AlarmsPage.tsx +│ │ │ └── UsersPage.tsx +│ │ └── components/ +│ │ ├── Layout.tsx # Responsive sidebar + hamburger drawer +│ │ └── StatusBadge.tsx +│ ├── Dockerfile +│ └── nginx.conf +├── database/ +│ └── migrations/001_initial.sql +├── deploy/ +│ ├── docker-compose.yml # Project name: loraweb +│ ├── .env.example +│ └── loraweb.service # systemd unit +└── docs/ +``` + +--- + +## 3. Database Schema (SQLite) + +Three tables, created idempotently (`CREATE TABLE IF NOT EXISTS`) at startup by both daemon and API. + +### topics +Represents a physical LoRaWAN device identified by DevEUI. + +| Column | Type | Description | +|--------|------|-------------| +| id | TEXT (UUID v4) | Primary key | +| topic | TEXT UNIQUE | DevEUI | +| approved | INTEGER (0/1) | 0 = unknown, 1 = approved | +| last_seen | TEXT (RFC3339) | Last uplink timestamp | +| alarm_status | TEXT | active / warning / alarm / unknown | +| alarm_threshold_hours | REAL | Per-device threshold (0 = use global) | +| created_at | TEXT (RFC3339) | First registration | + +### sensors +Logical sensor entity assigned to a topic. + +| Column | Type | Description | +|--------|------|-------------| +| id | TEXT (UUID v4) | Primary key | +| name | TEXT | Display name | +| description | TEXT | Optional description | +| location | TEXT | Location string | +| sensor_type | TEXT | Type (e.g. "Temperatur") | +| active | INTEGER (0/1) | Active flag | +| topic_id | TEXT FK → topics.id | Assigned topic (nullable) | +| display_config | TEXT (JSON) | Per-sensor field config | +| created_at | TEXT (RFC3339) | Creation date | + +### users + +| Column | Type | Description | +|--------|------|-------------| +| id | INTEGER PK AUTOINCREMENT | Primary key | +| username | TEXT UNIQUE | Username | +| password_hash | TEXT | Argon2id hash | +| role | TEXT | "user" or "admin" | +| totp_secret | TEXT | Base32 TOTP secret (nullable) | +| totp_enabled | INTEGER (0/1) | 2FA active | +| created_at | TEXT (RFC3339) | Creation date | + +--- + +## 4. Daemon Design + +Single Rust binary running two async tasks via Tokio: + +### 4.1 HTTP Ingest (Axum, Port 8080) + +- `POST /?event=up`: Parse ChirpStack JSON → register unknown DevEUI in `topics` (approved=0) → write all `object` fields + RF metadata (rssi, snr, dr, f_cnt, f_port) to InfluxDB measurement `sensor_data` +- Tags: `dev_eui`, `device_name`, `application_name` +- Timestamp from ChirpStack `time` field +- Other events (join, ack, txack, log, status): return 200 OK, no processing +- InfluxDB errors: log and continue, no crash + +### 4.2 Alarm Monitor (Tokio interval loop) + +- Runs every `check_interval_secs` (default 60) +- Queries all topics with `approved=1` +- Per topic: use `alarm_threshold_hours` if > 0, otherwise global threshold +- Status logic: + - `last_seen` is NULL → `unknown` + - hours since last seen < warning threshold → `active` + - hours since last seen < alarm threshold → `warning` + - hours since last seen >= alarm threshold → `alarm` +- Updates `alarm_status` in SQLite + +### 4.3 Configuration + +Via `config` crate: loads `daemon-config.toml` + environment variables (prefix `LORAWEB__`). + +| Key | Default | Description | +|-----|---------|-------------| +| http.bind | 0.0.0.0 | Bind address | +| http.port | 8080 | HTTP port | +| influx.url | http://localhost:8086 | InfluxDB URL | +| influx.token | — | API token | +| influx.org | loraweb | Organization | +| influx.bucket | sensors | Bucket | +| database.path | loraweb.db | SQLite path | +| alarm.warning_threshold_hours | 2.0 | Warning threshold | +| alarm.alarm_threshold_hours | 6.0 | Alarm threshold | +| alarm.check_interval_secs | 60 | Check interval | + +### 4.4 Startup + +Log banner showing ingest URL and ChirpStack configuration hint. + +--- + +## 5. REST API Design + +Axum on port 3001, shared SQLite DB with daemon. + +### 5.1 Authentication + +- `POST /api/auth/login`: Verify username/password (Argon2id) + - If TOTP enabled: return `{ totp_required: true }` + - Second request with TOTP code → issue JWT + - If no TOTP: issue JWT directly +- JWT: HS256, 24h validity, contains user id + role +- Auth middleware extracts Bearer token, injects user info +- Default admin created at first startup (password from env var) + +### 5.2 Endpoints + +| Route | Method | Auth | Description | +|-------|--------|------|-------------| +| /api/auth/login | POST | No | Login (+ optional TOTP) | +| /api/sensors | GET | Yes | List all sensors | +| /api/sensors | POST | Yes | Create sensor | +| /api/sensors/:id | GET | Yes | Sensor detail | +| /api/sensors/:id | PUT | Yes | Update sensor | +| /api/sensors/:id | DELETE | Yes | Delete sensor (resets topic to unapproved) | +| /api/sensors/:id/data | GET | Yes | InfluxDB proxy, ?hours=N | +| /api/sensors/:id/display-config | GET | Yes | Get field config | +| /api/sensors/:id/display-config | PUT | Yes | Save field config | +| /api/topics | GET | Yes | List all topics | +| /api/topics/:id | PUT | Yes | Approve, assign, update | +| /api/topics/:id/threshold | PUT | Yes | Set alarm threshold | +| /api/alarms | GET | Yes | Alarm summary | +| /api/users | GET | Admin | List users | +| /api/users | POST | Admin | Create user | +| /api/users/:id | PUT | Admin | Update user | +| /api/users/:id | DELETE | Admin | Delete user | +| /api/users/:id/totp | POST | Admin | Enable 2FA (returns secret + QR URI) | +| /api/users/:id/totp | DELETE | Admin | Disable 2FA | + +### 5.3 InfluxDB Proxy + +- Sends Flux query to InfluxDB via reqwest +- Receives annotated CSV (after pivot) +- Parses CSV to JSON array server-side +- Returns JSON to frontend — no InfluxDB credentials exposed + +--- + +## 6. Frontend Design + +React 18 + TypeScript, Vite 6, Tailwind 4, Recharts. + +### 6.1 Pages + +1. **LoginPage**: Username/password form, conditional TOTP input step +2. **DashboardPage**: 4 status tiles (active/warning/alarm/unknown counts) + active alarm list +3. **SensorsPage**: Table (desktop) / cards (mobile), CRUD modals, DataModal with two tabs: + - "Diagramme": Charts per configured panels (D1-D8), time selector, RF metadata toggle + - "Einrichten": Per-field config (label, unit, chart type, panel assignment) +4. **UnknownTopicsPage**: List of unapproved topics, dropdown to assign to free sensors +5. **AlarmsPage**: Active alarms and warnings list +6. **UsersPage** (admin only): User CRUD, 2FA enable/disable + +### 6.2 Charts (Recharts) + +- **Line chart**: Time series, `connectNulls` enabled +- **Bar chart (daily)**: Frontend aggregates raw data by date +- **Bar chart (weekly)**: Frontend aggregates by calendar week +- **Pie chart**: Percentage distribution +- Fields assigned to panels D1-D8; same panel number = same chart +- RF metadata (RSSI, SNR, DR) toggleable in line charts +- Time ranges: 1h, 6h, 24h, 7d, 30d, 90d +- Data table below charts: last 20 values with configured labels/units + +### 6.3 Responsive Layout + +- Desktop (`lg:` breakpoint, 1024px+): Static sidebar navigation +- Mobile: Top bar with hamburger icon → animated slide-in drawer +- Tables switch to card lists on mobile (`hidden sm:block` / `sm:hidden`) +- DataModal: Centered overlay on desktop, bottom-sheet on mobile + +### 6.4 Auth & State + +- React Context API for auth state +- localStorage: `loraweb_token`, `loraweb_user` +- Axios interceptor: 401 response → automatic logout + redirect to login +- All pages auto-refresh every 15 seconds + +--- + +## 7. Infrastructure + +### 7.1 Docker Compose + +Project name: `loraweb`. Four services: + +| Service | Image | Ports (host) | Volumes | +|---------|-------|-------------|---------| +| daemon | Custom (Rust) | 8080 | loraweb-data:/data | +| api | Custom (Rust) | — (internal) | loraweb-data:/data | +| frontend | Custom (nginx) | 80 | — | +| influxdb | influxdb:2.7 | — (internal) | loraweb-influx:/var/lib/influxdb2 | + +All containers run as user `loraweb` (UID 1001). + +### 7.2 Dockerfiles + +**Rust (daemon + api)**: Multi-stage build +- Stage 1: `rust:1.88-slim` → `cargo fetch` → `cargo build --release` +- Stage 2: `debian:bookworm-slim` → copy binary, create non-root user +- Build context: project root (Cargo workspace) + +**Frontend**: Multi-stage build +- Stage 1: `node:20-alpine` → `npm install --legacy-peer-deps` → `npm run build` +- Stage 2: `nginx:alpine` → copy dist + nginx.conf + +### 7.3 nginx.conf + +- `location /api/` → `proxy_pass http://api:3001/api/` +- `location /` → serve static files, `try_files $uri /index.html` (SPA fallback) + +### 7.4 systemd + +`deploy/loraweb.service`: +- `WorkingDirectory=/opt/loraweb` +- `ExecStart=/usr/bin/docker compose up` +- `ExecStop=/usr/bin/docker compose down` +- `Restart=always` + +### 7.5 Secrets + +All via `.env` file (not in git). `.env.example` as template with: +- `JWT_SECRET`, `ADMIN_PASSWORD` +- `INFLUX_TOKEN`, `INFLUX_ORG`, `INFLUX_BUCKET` +- `DOCKER_INFLUXDB_INIT_*` for InfluxDB bootstrap + +--- + +## 8. Implementation Order (Bottom-Up) + +1. Database migrations (SQL file + programmatic init) +2. Daemon (HTTP ingest + alarm monitor) +3. REST API (auth, CRUD, InfluxDB proxy) +4. Frontend (pages, charts, responsive layout) +5. Docker & infrastructure (Dockerfiles, Compose, nginx, systemd) + +Each layer is testable independently before the next is built.