downlink changes

This commit is contained in:
xlemmingx 2025-11-05 16:37:08 +01:00
parent 2969221ab2
commit 143520822c
10 changed files with 202 additions and 78 deletions

View File

@ -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

Binary file not shown.

View File

@ -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: ❌

52
README_DEV.MD Normal file
View File

@ -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.

37
chirpstack3_encoder.js Normal file
View File

@ -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]

View File

@ -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);
}
}

View File

@ -20,7 +20,6 @@
0x0F, 0x0F}
#define RETRY_DELAY K_MSEC(1000)
#define SEND_INTERVALL K_MINUTES(10)
void init_lorawan();

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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);
}