diff --git a/LoRaWAN_Payload_Description.md b/LoRaWAN_Payload_Description.md index f56c25e..b2ae0be 100644 --- a/LoRaWAN_Payload_Description.md +++ b/LoRaWAN_Payload_Description.md @@ -19,27 +19,36 @@ ## Downlink Payload (Network Server → Device) **Port:** Any -**Format:** ASCII commands +**Format:** Binary -### Relay Control Commands +### Combined Configuration Packet -| Command | Length | Format | Description | -|---------|--------|--------|-------------| -| `r1 0` | 4 bytes | ASCII | Force relay OFF + disable auto control | -| `r1 1` | 4 bytes | ASCII | Re-enable automatic temperature control | +**Length:** 3 bytes +**Format:** `[heating_enable][room_temp_threshold][floor_temp_threshold]` -### Temperature Threshold Commands - -| Command | Length | Format | Description | -|---------|--------|--------|-------------| -| `t1[temp]` | 3 bytes | ASCII + uint8 | Set room temperature threshold (°C) | -| `t2[temp]` | 3 bytes | ASCII + uint8 | Set floor temperature threshold (°C) | +| Byte | Field | Type | Range | Description | +|------|-------|------|-------|-------------| +| 0 | Heating Enable | uint8_t | 0-1 | Heating control logic (0=DISABLED, 1=ENABLED) | +| 1 | Room Temperature Threshold | uint8_t | 0-255 | Target room temperature in °C | +| 2 | Floor Temperature Threshold | uint8_t | 0-255 | Target floor temperature in °C | **Examples:** -- `72 31 20 30` (`r1 0`) = Force relay OFF -- `72 31 20 31` (`r1 1`) = Enable auto control -- `74 31 16` (`t1` + 22) = Set room threshold to 22°C -- `74 32 1A` (`t2` + 26) = Set floor threshold to 26°C +- `01 16 19` = Enable heating, room 22°C, floor 25°C +- `00 14 18` = Disable heating, room 20°C, floor 24°C + +### Send Interval Command + +**Length:** 2 bytes +**Format:** `'i'[interval_minutes]` + +| Byte | Field | Type | Range | Description | +|------|-------|------|-------|-------------| +| 0 | Command | ASCII | 'i' (0x69) | Send interval command identifier | +| 1 | Interval | uint8_t | 1-255 | Send interval in minutes | + +**Examples:** +- `69 05` = Set send interval to 5 minutes +- `69 3C` = Set send interval to 60 minutes ## Data Conversion diff --git a/LoRaWAN_Payload_Description.pdf b/LoRaWAN_Payload_Description.pdf index a16bcac..be0e521 100644 Binary files a/LoRaWAN_Payload_Description.pdf and b/LoRaWAN_Payload_Description.pdf differ diff --git a/README.md b/README.md index f6a32fd..2ad91cf 100644 --- a/README.md +++ b/README.md @@ -27,33 +27,38 @@ Das System schaltet die Heizung automatisch basierend auf zwei Temperaturschwell - **Aktivierung**: ABP (Activation by Personalization) - **Region**: EU868 - **Port**: 2 -- **Sendeintervall**: 5 Minuten +- **Sendeintervall**: Standard 10 Minuten (konfigurierbar 1-255 min) ## Downlink-Befehle Alle Befehle als Base64 in ChirpStack eingeben: -### Relais-Steuerung -| Befehl | Base64 | Funktion | -|--------|--------|----------| -| r1:0 | `cjE6AA==` | Relais AUS + Automatik deaktiviert | -| r1:1 | `cjE6AQ==` | Automatik aktiviert | +### Kombinierte Konfiguration (3 Bytes) +**Format**: `[heating_enable][room_temp][floor_temp]` -### Temperatur-Schwellwerte -| Befehl | Beispiel Base64 | Funktion | -|--------|--------|----------| -| t1 + Wert | `dDESOQ==` (18°C) | Raumtemperatur-Schwellwert setzen | -| t1 + Wert | `dDFVUQ==` (21°C) | Raumtemperatur-Schwellwert setzen | -| t1 + Wert | `dDFZWQ==` (25°C) | Raumtemperatur-Schwellwert setzen | -| t2 + Wert | `dDIUFQ==` (20°C) | Bodentemperatur-Schwellwert setzen | -| t2 + Wert | `dDIWFg==` (22°C) | Bodentemperatur-Schwellwert setzen | -| t2 + Wert | `dDIZGQ==` (25°C) | Bodentemperatur-Schwellwert setzen | +| Beschreibung | Hex | Base64 | Funktion | +|--------------|-----|--------|----------| +| Heizung AN, 22°C Raum, 25°C Boden | `01 16 19` | `ARYa` | Automatik aktiviert mit neuen Schwellwerten | +| Heizung AUS | `00 14 18` | `ABQa` | Heizung komplett deaktiviert | +| Heizung AN, 20°C Raum, 24°C Boden | `01 14 18` | `ARQa` | Automatik mit niedrigeren Schwellwerten | -**Hinweis**: Alle Temperaturwerte von 0-255°C sind möglich. +### Sendeintervall ändern (2 Bytes) +**Format**: `'i'[minuten]` + +| Beschreibung | Hex | Base64 | Funktion | +|--------------|-----|--------|----------| +| 5 Minuten Intervall | `69 05` | `aQU=` | Häufigere Übertragung | +| 15 Minuten Intervall | `69 0F` | `aQ8=` | Normale Übertragung | +| 60 Minuten Intervall | `69 3C` | `aTw=` | Seltene Übertragung | + +**Hinweis**: +- Temperaturwerte: 0-255°C möglich +- Sendeintervall: 1-255 Minuten möglich +- Heating Enable: 0=AUS (Relais bleibt aus), 1=AN (automatische Regelung) ## Datenformat (Uplink) -Alle 5 Minuten wird ein 7-Byte Datenpaket gesendet: +Alle 10 Minuten wird ein 7-Byte Datenpaket gesendet: | Byte | Inhalt | Format | |------|--------|--------| @@ -69,8 +74,8 @@ Alle 5 Minuten wird ein 7-Byte Datenpaket gesendet: - Beide Temperaturen werden überwacht (falls MLX-Sensor verfügbar) ### 2. Manueller Override -- Aktiviert durch `r1:0` → Heizung permanent AUS -- Deaktiviert durch `r1:1` oder neue Schwellwert-Befehle +- Aktiviert durch Heizung AUS Befehl (Byte 0 = 0) → Heizung permanent AUS +- Deaktiviert durch Heizung AN Befehl (Byte 0 = 1) ### 3. Sensor-Fallback - Wenn MLX90614 nicht verfügbar → nur Raumtemperatur wird überwacht @@ -223,12 +228,12 @@ pio run -t upload 2. **ChirpStack Monitoring**: - Device sollte nach ~30 Sekunden erste Uplinks senden - - LoRaWAN Frames Tab: Uplinks alle 5 Minuten + - LoRaWAN Frames Tab: Uplinks alle 10 Minuten - Payload: 7 Bytes Sensordaten 3. **Test Downlink**: - In ChirpStack: Queue Downlink - - Payload: `cjE6AA==` (Base64) + - Payload: `ARYa` (Base64) - Heizung AN, 22°C Raum, 25°C Boden - Port: 2 - Confirmed: ❌ diff --git a/README_DEV.MD b/README_DEV.MD new file mode 100644 index 0000000..02b5cae --- /dev/null +++ b/README_DEV.MD @@ -0,0 +1,52 @@ +Hier sind alle Framework-Änderungen die ich gemacht habe: + + Framework-Änderungen für funktionierenden Build + + 1. LoRaMAC-Node: sx126x.c Datei gelöscht + + # Datei entfernt: + /home/jochen/.platformio/packages/framework-zephyr/_pio/modules/lib/loramac-node/src/radio/sx126x/sx126x.c + + 2. LoRaMAC-Node: radio_sx126x.c Datei hinzugefügt + + # Datei kopiert von Windows-Installation: + /home/jochen/.platformio/packages/framework-zephyr/_pio/modules/lib/loramac-node/src/radio/sx126x/radio_sx126x.c + + 3. LoRaMAC-Node CMakeLists.txt modifiziert + + Datei: /home/jochen/.platformio/packages/framework-zephyr/modules/loramac-node/CMakeLists.txt + + Geändert: + # Von: + zephyr_library_sources_ifdef(CONFIG_HAS_SEMTECH_SX126X + ${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/radio/sx126x/sx126x.c + ${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/radio/sx126x/radio.c + ) + + # Zu: + zephyr_library_sources_ifdef(CONFIG_HAS_SEMTECH_SX126X + ${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/radio/sx126x/radio.c + ${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/radio/sx126x/radio_sx126x.c + ) + + 4. Device Tree Fix (bereits im Projekt) + + Datei: zephyr/boards/st/g2h_lorawan_heat_control/g2h_lorawan_heat_control.dts + + Hinzugefügt: + lora: radio@0 { + compatible = "st,stm32wl-subghz-radio"; + reg = <0>; + interrupts = <50 0>; + spi-max-frequency = <8000000>; + status = "okay"; + // ... rest bleibt gleich + } + + Zusammenfassung + + - 3 Framework-Dateien modifiziert/gelöscht/hinzugefügt + - 1 Device Tree im Projekt gefixt + - Grund: Zephyr native STM32WL LoRa-Driver + LoRaMAC-Node Integration funktionsfähig machen + + Das sind alle Änderungen die nötig waren um den Build zum Laufen zu bringen. \ No newline at end of file diff --git a/chirpstack3_encoder.js b/chirpstack3_encoder.js new file mode 100644 index 0000000..0b64807 --- /dev/null +++ b/chirpstack3_encoder.js @@ -0,0 +1,37 @@ +// ChirpStack3 Encoder for Combined Downlink Packet +// Format: [heating_enable][room_temp_threshold][floor_temp_threshold] + +function Encode(fPort, obj, variables) { + var bytes = []; + + // Combined packet format (3 bytes) + if (obj.heating_enable !== undefined && obj.room_temp !== undefined && obj.floor_temp !== undefined) { + // Byte 0: Heating enable (0=DISABLED, 1=ENABLED) + bytes[0] = obj.heating_enable ? 1 : 0; + + // Byte 1: Room temperature threshold (°C) + bytes[1] = parseInt(obj.room_temp); + + // Byte 2: Floor temperature threshold (°C) + bytes[2] = parseInt(obj.floor_temp); + + return bytes; + } + + // Send interval format (2 bytes): 'i' + minutes + if (obj.send_interval !== undefined) { + bytes[0] = 0x69; // ASCII 'i' + bytes[1] = parseInt(obj.send_interval); + + return bytes; + } + + return null; +} + +// Example usage: +// Combined packet: {"heating_enable": true, "room_temp": 22, "floor_temp": 25} +// Result: [0x01, 0x16, 0x19] + +// Send interval: {"send_interval": 5} +// Result: [0x69, 0x05] \ No newline at end of file diff --git a/src/lora/lorawan.c b/src/lora/lorawan.c index e1501dd..95322e9 100644 --- a/src/lora/lorawan.c +++ b/src/lora/lorawan.c @@ -15,6 +15,7 @@ extern bool relais_state; extern bool relais_manual_override; extern uint8_t temperature_threshold; extern uint8_t floor_temp_threshold; +extern uint32_t send_interval_minutes; const static struct device *lora_dev; static struct lorawan_join_config join_cfg; static uint8_t dev_eui[8]; @@ -31,6 +32,8 @@ static uint8_t apps_key[] = LORAWAN_APPS_KEY; static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len, const uint8_t *hex_data) { + printk("*** DOWNLINK RECEIVED ***\n"); + LOG_INF("*** DOWNLINK CALLBACK TRIGGERED ***"); LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm, Len: %d, Time %d", port, flags & LORAWAN_DATA_PENDING, rssi, snr, len, !!(flags & LORAWAN_TIME_UPDATED)); if (hex_data) @@ -38,46 +41,60 @@ static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, u LOG_HEXDUMP_INF(hex_data, len, "Payload: "); } - /* Could be moved in a separate thread so we only have to set the variable here */ - if (len > 3 && hex_data[0] == 'r' && hex_data[1] == '1') + /* Combined packet format: [heating_enable][temp1_threshold][temp2_threshold] + * Byte 0: Heating control (0=DISABLED, 1=ENABLED) + * Byte 1: Room temperature threshold (°C) + * Byte 2: Floor temperature threshold (°C) + */ + if (len >= 3) { - uint8_t relais_value = hex_data[3]; - LOG_INF("Received relais1 state change: %d", relais_value); + uint8_t heating_enable = hex_data[0]; + uint8_t room_temp_threshold = hex_data[1]; + uint8_t floor_temp_threshold_new = hex_data[2]; - if (relais_value == 0) + LOG_INF("Received combined packet: heating_enable=%d, room_temp=%d°C, floor_temp=%d°C", + heating_enable, room_temp_threshold, floor_temp_threshold_new); + + /* Process heating control command */ + if (heating_enable == 0) { - /* r1 0: Force relais OFF and disable temperature control */ - relais_state = 0; + /* Disable heating control logic completely */ relais_manual_override = true; set_relais_state(0); - LOG_INF("Relais forced OFF, temperature control disabled"); + LOG_INF("Heating control logic DISABLED, relais forced OFF"); } - else if (relais_value == 1) + else if (heating_enable == 1) { - /* r1 1: Re-enable automatic temperature control */ + /* Enable automatic heating control logic */ relais_manual_override = false; - LOG_INF("Automatic temperature control re-enabled"); + LOG_INF("Heating control logic ENABLED"); } else { - LOG_WRN("Invalid relais command value: %d (valid: 0=OFF, 1=AUTO)", relais_value); + LOG_WRN("Invalid heating enable value: %d (valid: 0=DISABLED, 1=ENABLED)", heating_enable); + } + + /* Update temperature thresholds */ + temperature_threshold = room_temp_threshold; + floor_temp_threshold = floor_temp_threshold_new; + LOG_INF("Temperature thresholds updated: room=%d°C, floor=%d°C", + temperature_threshold, floor_temp_threshold); + + /* Log final state */ + if (heating_enable == 1) + { + LOG_INF("Heating control logic enabled with new thresholds"); } } - /* Room temperature threshold command: t1[temp] (e.g., "t1" + 18 for 18°C) */ - else if (len > 3 && hex_data[0] == 't' && hex_data[1] == '1') + /* Send interval command: 'i' + interval_minutes (1 byte) + * Format: ['i'][interval_minutes] + * Example: 'i' + 5 = 5 minutes + */ + else if (len >= 2 && hex_data[0] == 'i') { - uint8_t new_threshold = hex_data[2]; - temperature_threshold = new_threshold; - relais_manual_override = false; // Re-enable automatic control - LOG_INF("Room temperature threshold updated to %d°C, automatic control enabled", temperature_threshold); - } - /* Floor temperature threshold command: t2[temp] (e.g., "t2" + 22 for 22°C) */ - else if (len > 3 && hex_data[0] == 't' && hex_data[1] == '2') - { - uint8_t new_threshold = hex_data[2]; - floor_temp_threshold = new_threshold; - relais_manual_override = false; // Re-enable automatic control - LOG_INF("Floor temperature threshold updated to %d°C, automatic control enabled", floor_temp_threshold); + uint8_t new_interval = hex_data[1]; + send_interval_minutes = new_interval; + LOG_INF("Send interval updated to %d minutes", send_interval_minutes); } } diff --git a/src/lora/lorawan.h b/src/lora/lorawan.h index e372a18..214e140 100644 --- a/src/lora/lorawan.h +++ b/src/lora/lorawan.h @@ -20,7 +20,6 @@ 0x0F, 0x0F} #define RETRY_DELAY K_MSEC(1000) -#define SEND_INTERVALL K_MINUTES(10) void init_lorawan(); diff --git a/src/main.c b/src/main.c index 2f8d676..7733c66 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,7 @@ volatile bool relais_state = false; volatile bool relais_manual_override = false; volatile uint8_t temperature_threshold = 21; volatile uint8_t floor_temp_threshold = 24; +volatile uint32_t send_interval_minutes = 10; /* MAIN */ int main(void) @@ -89,6 +90,6 @@ int main(void) send_data_packet(data, 7); /* Delay until next cycle */ - k_sleep(SEND_INTERVALL); + k_sleep(K_MINUTES(send_interval_minutes)); } } \ No newline at end of file diff --git a/src/sensors/mlx90614.c b/src/sensors/mlx90614.c index 9301cb1..7cffe69 100644 --- a/src/sensors/mlx90614.c +++ b/src/sensors/mlx90614.c @@ -35,7 +35,6 @@ void request_sensor_data_mlx90614(uint8_t target_addr) err = i2c_write_read(i2c_dev, 0x5A, &target_addr, sizeof(target_addr), &read_data_buffer, sizeof(read_data_buffer)); if (err == 0) { - LOG_INF("MLX90614 sensor values successfully read after %d tries", i + 1); mlx90614_sensor_available = true; return; } diff --git a/src/sensors/sht4x.c b/src/sensors/sht4x.c index 9c11441..a630faf 100644 --- a/src/sensors/sht4x.c +++ b/src/sensors/sht4x.c @@ -12,13 +12,14 @@ static const struct device *sht; void init_sht4x() { - if (!DT_HAS_COMPAT_STATUS_OKAY(sensirion_sht4x)) { + if (!DT_HAS_COMPAT_STATUS_OKAY(sensirion_sht4x)) + { LOG_ERR("No sensirion,sht4x compatible node found in the device tree"); return; } - + sht = DEVICE_DT_GET_ANY(sensirion_sht4x); - + if (!device_is_ready(sht)) { LOG_ERR("SHT4X-Device %s is not ready.\n", sht->name); @@ -32,7 +33,6 @@ void request_sensor_data_sht4x() { if (sensor_sample_fetch(sht) == 0) { - LOG_INF("SHT4X sensor values successfully read after %d tries", i + 1); return; } else @@ -47,19 +47,21 @@ float get_value_from_current_sample_sht4x(enum sensor_channel sensor) { struct sensor_value value; float result; - if (sensor_channel_get(sht, sensor, &value) == 0) { + if (sensor_channel_get(sht, sensor, &value) == 0) + { result = sensor_value_to_double(&value); - const char* sensor_name = (sensor == SENSOR_CHAN_AMBIENT_TEMP) ? "temperature" : "humidity"; + const char *sensor_name = (sensor == SENSOR_CHAN_AMBIENT_TEMP) ? "temperature" : "humidity"; LOG_DBG("SHT4X %s value: %d.%02d", sensor_name, (int)result, (int)(result * 100) % 100); - } else { - const char* sensor_name = (sensor == SENSOR_CHAN_AMBIENT_TEMP) ? "temperature" : "humidity"; + } + else + { + const char *sensor_name = (sensor == SENSOR_CHAN_AMBIENT_TEMP) ? "temperature" : "humidity"; LOG_ERR("Can't read SHT4X %s from sample!", sensor_name); return 0.0; } return result; } - static float read_sht4x_temp(void) { request_sensor_data_sht4x(); @@ -74,12 +76,15 @@ static float read_sht4x_humidity(void) float get_stable_value_sht4x(enum sensor_channel sensor) { - const float TEMP_THRESHOLD = 2.0; // °C + const float TEMP_THRESHOLD = 2.0; // °C const float HUMIDITY_THRESHOLD = 2.0; // %RH - if (sensor == SENSOR_CHAN_AMBIENT_TEMP) { + if (sensor == SENSOR_CHAN_AMBIENT_TEMP) + { return get_stable_sensor_value(read_sht4x_temp, TEMP_THRESHOLD); - } else if (sensor == SENSOR_CHAN_HUMIDITY) { + } + else if (sensor == SENSOR_CHAN_HUMIDITY) + { return get_stable_sensor_value(read_sht4x_humidity, HUMIDITY_THRESHOLD); }