add energy tracking: 15-byte payload, power consumption accumulation
- Firmware: energy.c/h tracks relay ON-time and uptime via k_uptime_get, load_watts (default 400W) persistent via Zephyr Settings API - Payload extended from 7 to 15 bytes (backwards compatible): relay ON-time (uint24), uptime (uint24), load in watts (uint16) - New 'w' downlink for load configuration - Fix downlink handler: 'i'/'w' commands now checked before combined packet - Server: g2h_rs_energy_tracking table for lifetime power_consumption_wh, reboot detection via uptime vs real elapsed time - Decoder/encoder and docs updated
This commit is contained in:
parent
aefdc7b05c
commit
0287921b38
@ -3,61 +3,83 @@
|
||||
## Uplink Payload (Device → Network Server)
|
||||
|
||||
**Port:** 2
|
||||
**Length:** 7 bytes
|
||||
**Length:** 7 Bytes (ohne Energietracking) oder 15 Bytes (mit Energietracking)
|
||||
**Format:** Binary
|
||||
|
||||
| Byte | Field | Type | Unit | Description |
|
||||
|------|-------|------|------|-------------|
|
||||
| 0-1 | Room Temperature | int16_t | 0.01°C | SHT4x sensor reading (big-endian) |
|
||||
| 2-3 | Floor Temperature | int16_t | 0.01°C | MLX90614 IR sensor reading (big-endian) * |
|
||||
| 4-5 | Humidity | int16_t | 0.01% | SHT4x sensor reading (big-endian) |
|
||||
| 6 | Relay State | uint8_t | - | Current relay status (0=OFF, 1=ON) |
|
||||
|-------|------------------|---------|----------|-------------|
|
||||
| 0-1 | Room Temperature | int16 | 0.01°C | SHT4x sensor (big-endian) |
|
||||
| 2-3 | Floor Temperature| int16 | 0.01°C | MLX90614 IR sensor (big-endian) * |
|
||||
| 4-5 | Humidity | int16 | 0.01% | SHT4x sensor (big-endian) |
|
||||
| 6 | Relay State | uint8 | - | 0=OFF, 1=ON |
|
||||
| **7-9** | **Relay ON-Time** | **uint24** | **Sekunden** | **Kumulative Einschaltdauer seit Boot** |
|
||||
| **10-12** | **Uptime** | **uint24** | **Sekunden** | **Gerätelaufzeit seit Boot** |
|
||||
| **13-14** | **Load** | **uint16** | **Watt** | **Konfigurierte Heizlast** |
|
||||
|
||||
**Example Uplink:**
|
||||
- `08 34 0A 28 15 E0 01` = 21.00°C room, 26.00°C floor, 56.00% humidity, relay ON
|
||||
**Hinweis zur Abwärtskompatibilität:** Geräte ohne Energietracking senden 7 Bytes. Der Decoder erkennt die Version anhand von `bytes.length`.
|
||||
|
||||
**Beispiel (15 Bytes):**
|
||||
- `08 34 0A 28 15 E0 01 00 00 F0 00 01 2C 01 90`
|
||||
= 21.00°C Raum, 26.00°C Boden, 56.00% Feuchte, Relay ON, 240s ON-Zeit, 300s Uptime, 400W Last
|
||||
|
||||
## Downlink Payload (Network Server → Device)
|
||||
|
||||
**Port:** Any
|
||||
**Format:** Binary
|
||||
|
||||
### Combined Configuration Packet
|
||||
### Kombiniertes Konfigurationspaket
|
||||
|
||||
**Length:** 3 bytes
|
||||
**Length:** 3 Bytes
|
||||
**Format:** `[heating_enable][room_temp_threshold][floor_temp_threshold]`
|
||||
|
||||
| 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 |
|
||||
|------|--------------------------|-------|-------|-------------|
|
||||
| 0 | Heating Enable | uint8 | 0-1 | 0=DISABLED, 1=ENABLED |
|
||||
| 1 | Room Temperature Threshold | uint8 | 0-255 | Raumtemperatur-Schwellwert in °C |
|
||||
| 2 | Floor Temperature Threshold | uint8 | 0-255 | Bodentemperatur-Schwellwert in °C |
|
||||
|
||||
**Examples:**
|
||||
- `01 16 19` = Enable heating, room 22°C, floor 25°C
|
||||
- `00 14 18` = Disable heating, room 20°C, floor 24°C
|
||||
**Beispiele:**
|
||||
- `01 16 19` = Heizung AN, Raum 22°C, Boden 25°C
|
||||
- `00 14 18` = Heizung AUS
|
||||
|
||||
### Send Interval Command
|
||||
### Sendeintervall
|
||||
|
||||
**Length:** 2 bytes
|
||||
**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 |
|
||||
|------|----------|-------|-------|-------------|
|
||||
| 0 | Command | ASCII | 0x69 | Befehlskennung 'i' |
|
||||
| 1 | Interval | uint8 | 1-255 | Sendeintervall in Minuten |
|
||||
|
||||
**Examples:**
|
||||
- `69 05` = Set send interval to 5 minutes
|
||||
- `69 0A` = Set send interval to 10 minutes
|
||||
- `69 0F` = Set send interval to 15 minutes
|
||||
- `69 3C` = Set send interval to 60 minutes
|
||||
**Beispiele:**
|
||||
- `69 05` = 5 Minuten
|
||||
- `69 0A` = 10 Minuten
|
||||
- `69 3C` = 60 Minuten
|
||||
|
||||
## Data Conversion
|
||||
### Last-Konfiguration
|
||||
|
||||
- **Temperature:** `value = (int16_t)(temperature * 100)`
|
||||
- **Humidity:** `value = (int16_t)(humidity * 100)`
|
||||
- **Decoding:** `actual_value = received_value / 100.0`
|
||||
**Length:** 3 Bytes
|
||||
**Format:** `'w'[watts_high][watts_low]`
|
||||
|
||||
## Special Cases
|
||||
| Byte | Field | Type | Range | Description |
|
||||
|------|-------------|-------|--------|-------------|
|
||||
| 0 | Command | ASCII | 0x77 | Befehlskennung 'w' |
|
||||
| 1-2 | Load Watts | uint16 | 1-65535 | Heizlast in Watt (big-endian), persistent gespeichert |
|
||||
|
||||
**\* Floor Temperature Sensor:** If the MLX90614 sensor is not available or not responding, the floor temperature will be transmitted as `-273.15°C` (hex: `95 5D`).
|
||||
**Beispiele:**
|
||||
- `77 01 90` = 400W (0x0190)
|
||||
- `77 03 E8` = 1000W (0x03E8)
|
||||
|
||||
## Datenkonvertierung
|
||||
|
||||
- **Temperatur:** `raw = (int16)(temperature * 100)` → `actual = raw / 100.0`
|
||||
- **Luftfeuchtigkeit:** `raw = (int16)(humidity * 100)` → `actual = raw / 100.0`
|
||||
- **ON-Zeit / Uptime:** uint24 big-endian, Sekunden seit Boot (wird bei Reboot zurückgesetzt)
|
||||
- **Last:** uint16 big-endian, Watt
|
||||
|
||||
## Sonderfälle
|
||||
|
||||
**\* Bodentemperatursensor:** Wenn der MLX90614 nicht verfügbar ist, wird `-273.15°C` (hex: `95 5D`) übertragen.
|
||||
|
||||
**Reboot-Erkennung (Portal):** Wenn `relay_on_time` im aktuellen Paket kleiner ist als im vorherigen, wurde das Gerät neu gestartet.
|
||||
|
||||
16
README.md
16
README.md
@ -42,6 +42,15 @@ Alle Befehle als Base64 in ChirpStack eingeben:
|
||||
| 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 |
|
||||
|
||||
### Heizlast konfigurieren (3 Bytes)
|
||||
**Format**: `'w'[watts_high][watts_low]`
|
||||
|
||||
| Beschreibung | Hex | Base64 | Funktion |
|
||||
|--------------|-----|--------|----------|
|
||||
| 400W (Standard) | `77 01 90` | `dwGQ` | Typische Fußbodenheizung |
|
||||
| 1000W | `77 03 E8` | `dwPo` | Stärkere Heizung |
|
||||
| 2000W | `77 07 D0` | `dw/Q` | Hochleistungsheizung |
|
||||
|
||||
### Sendeintervall ändern (2 Bytes)
|
||||
**Format**: `'i'[minuten]`
|
||||
|
||||
@ -58,14 +67,17 @@ Alle Befehle als Base64 in ChirpStack eingeben:
|
||||
|
||||
## Datenformat (Uplink)
|
||||
|
||||
Alle 10 Minuten wird ein 7-Byte Datenpaket gesendet:
|
||||
Alle 10 Minuten wird ein 15-Byte Datenpaket gesendet (abwärtskompatibel zu 7-Byte):
|
||||
|
||||
| Byte | Inhalt | Format |
|
||||
|------|--------|--------|
|
||||
|-------|-------------------|--------|
|
||||
| 0-1 | Raumtemperatur | int16 × 100 (z.B. 2134 = 21.34°C) |
|
||||
| 2-3 | Bodentemperatur | int16 × 100 |
|
||||
| 4-5 | Luftfeuchtigkeit | int16 × 100 (z.B. 4520 = 45.20%) |
|
||||
| 6 | Relais-Status | 0 = AUS, 1 = AN |
|
||||
| 7-9 | Relay ON-Zeit | uint24, Sekunden seit Boot |
|
||||
| 10-12 | Uptime | uint24, Sekunden seit Boot |
|
||||
| 13-14 | Heizlast | uint16, Watt |
|
||||
|
||||
## Betriebsmodi
|
||||
|
||||
|
||||
33
chirpstack3_decoder.js
Normal file
33
chirpstack3_decoder.js
Normal file
@ -0,0 +1,33 @@
|
||||
// Decode decodes an array of bytes into an object.
|
||||
// - fPort contains the LoRaWAN fPort number
|
||||
// - bytes is an array of bytes, e.g. [225, 230, 255, 0]
|
||||
// - variables contains the device variables e.g. {"calibration": "3.5"}
|
||||
// The function must return an object, e.g. {"temperature": 22.5}
|
||||
function Decode(fPort, bytes, variables) {
|
||||
|
||||
// Hilfsfunktion: 16-Bit signed Big Endian lesen
|
||||
function readInt16BE(b0, b1) {
|
||||
var val = (b0 << 8) | b1;
|
||||
if (val & 0x8000) val = val - 0x10000;
|
||||
return val;
|
||||
}
|
||||
|
||||
if (bytes.length < 7) {
|
||||
return { error: "invalid payload length" };
|
||||
}
|
||||
|
||||
var d = {};
|
||||
|
||||
d.heating = readInt16BE(bytes[0], bytes[1]) / 100.0;
|
||||
d.heatingFloor = readInt16BE(bytes[2], bytes[3]) / 100.0;
|
||||
d.airHumidity = readInt16BE(bytes[4], bytes[5]) / 100.0;
|
||||
d.switch = bytes[6];
|
||||
|
||||
if (bytes.length >= 15) {
|
||||
d.relayOnTime = (bytes[7] << 16) | (bytes[8] << 8) | bytes[9];
|
||||
d.uptime = (bytes[10] << 16) | (bytes[11] << 8) | bytes[12];
|
||||
d.loadWatts = (bytes[13] << 8) | bytes[14];
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
@ -26,6 +26,16 @@ function Encode(fPort, obj, variables) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Load configuration format (3 bytes): 'w' + watts (2 bytes big-endian)
|
||||
if (obj.load_watts !== undefined) {
|
||||
var w = parseInt(obj.load_watts);
|
||||
bytes[0] = 0x77; // ASCII 'w'
|
||||
bytes[1] = (w >> 8) & 0xFF;
|
||||
bytes[2] = w & 0xFF;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -35,3 +45,6 @@ function Encode(fPort, obj, variables) {
|
||||
|
||||
// Send interval: {"send_interval": 5}
|
||||
// Result: [0x69, 0x05]
|
||||
|
||||
// Load configuration: {"load_watts": 400}
|
||||
// Result: [0x77, 0x01, 0x90]
|
||||
530
debug_G2H-RS.php
Normal file
530
debug_G2H-RS.php
Normal file
@ -0,0 +1,530 @@
|
||||
<?php
|
||||
include("./connect_iot.php");
|
||||
|
||||
$methode = $_SERVER['REQUEST_METHOD'];
|
||||
$debug = true;
|
||||
$file = 'log_g2h_rs_'.date("Y-m-d").'.txt';
|
||||
$seqNumber = getseqno();
|
||||
|
||||
$inputstream = file_get_contents('php://input');
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($file, $inputstream, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
$daten = json_decode($inputstream, true);
|
||||
|
||||
switch ($methode) {
|
||||
case 'GET':
|
||||
$response = array(
|
||||
'status' => 0,
|
||||
'status_message' => 'Methode GET nicht erlaubt.'
|
||||
);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$daten = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($file, "\n\nProcessing POST request\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
// Skip downlink confirmations - only process uplink messages with sensor data
|
||||
if(!isset($daten['objectJSON']) && !isset($daten['object'])) {
|
||||
if($debug) {
|
||||
file_put_contents($file, "Skipping - no sensor data (likely downlink confirmation)\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
$response = array(
|
||||
'status' => 200,
|
||||
'status_message' => 'downlink confirmation processed'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize variables
|
||||
$heating = '';
|
||||
$heatingFloor = '';
|
||||
$airHumidity = '';
|
||||
$switch = '';
|
||||
|
||||
// Extract data from decoded payload
|
||||
$mesg = array();
|
||||
if(isset($daten['object'])) {
|
||||
$mesg = $daten['object'];
|
||||
} elseif(isset($daten['objectJSON'])) {
|
||||
$mesg = json_decode($daten['objectJSON'], true);
|
||||
}
|
||||
|
||||
if(!empty($mesg)) {
|
||||
if(isset($mesg['heating'])) {
|
||||
$heating = $mesg['heating'];
|
||||
}
|
||||
|
||||
if(isset($mesg['heatingFloor'])) {
|
||||
$heatingFloor = $mesg['heatingFloor'];
|
||||
}
|
||||
|
||||
if(isset($mesg['airHumidity'])) {
|
||||
$airHumidity = $mesg['airHumidity'];
|
||||
}
|
||||
|
||||
if(isset($mesg['switch'])) {
|
||||
$switch = $mesg['switch'];
|
||||
}
|
||||
}
|
||||
|
||||
// Energy tracking fields (nur bei 15-Byte Payload vorhanden)
|
||||
$relayOnTime = isset($mesg['relayOnTime']) ? (int)$mesg['relayOnTime'] : null;
|
||||
$uptime = isset($mesg['uptime']) ? (int)$mesg['uptime'] : null;
|
||||
$loadWatts = isset($mesg['loadWatts']) ? (int)$mesg['loadWatts'] : null;
|
||||
|
||||
// Verbrauchsberechnung via DB-Tabelle g2h_rs_energy_tracking
|
||||
$powerConsumptionWh = null;
|
||||
if($relayOnTime !== null && $uptime !== null && $loadWatts !== null) {
|
||||
$dbiot2 = new iotdbObj();
|
||||
$conn2 = $dbiot2->getConnstring_iot();
|
||||
$devId = mysqli_real_escape_string($conn2, $daten['deviceName']);
|
||||
$row = mysqli_fetch_assoc(mysqli_query($conn2,
|
||||
"SELECT relay_on_time, power_consumption_wh, UNIX_TIMESTAMP(updated_at) AS last_ts
|
||||
FROM g2h_rs_energy_tracking WHERE device_id = '$devId'"));
|
||||
|
||||
$prevOnTime = $row ? (int)$row['relay_on_time'] : 0;
|
||||
$prevWh = $row ? (float)$row['power_consumption_wh'] : 0.0;
|
||||
$elapsed = $row ? (time() - (int)$row['last_ts']) : 0;
|
||||
|
||||
// Reboot-Erkennung: Uptime kleiner als vergangene Realzeit (60s Toleranz)
|
||||
$reboot = ($row && $uptime < $elapsed - 60);
|
||||
if($reboot) {
|
||||
$delta = $relayOnTime; // Counter läuft seit Neustart von 0
|
||||
} else {
|
||||
$delta = max(0, $relayOnTime - $prevOnTime);
|
||||
}
|
||||
$powerConsumptionWh = round($prevWh + ($delta * $loadWatts) / 3600.0, 4);
|
||||
|
||||
mysqli_query($conn2,
|
||||
"INSERT INTO g2h_rs_energy_tracking
|
||||
(device_id, relay_on_time, power_consumption_wh)
|
||||
VALUES ('$devId', $relayOnTime, $powerConsumptionWh)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
relay_on_time = $relayOnTime,
|
||||
power_consumption_wh = $powerConsumptionWh,
|
||||
updated_at = CURRENT_TIMESTAMP");
|
||||
}
|
||||
|
||||
// Extract time information
|
||||
$time = 'TIME:';
|
||||
if(isset($daten['rxInfo']['0']['time'])) {
|
||||
$time .= date("U", strtotime($daten['rxInfo']['0']['time']));
|
||||
} elseif(isset($daten['rxInfo']['time'])) {
|
||||
$time .= date("U", strtotime($daten['rxInfo']['time']));
|
||||
}
|
||||
|
||||
// Extract location information
|
||||
$computedLocation = array();
|
||||
if(isset($daten['rxInfo']['0']['location'])) {
|
||||
$location = $daten['rxInfo']['0']['location'];
|
||||
if(isset($location['latitude']) && isset($location['longitude'])) {
|
||||
$computedLocation['lat'] = (float)$location['latitude'];
|
||||
$computedLocation['lng'] = (float)$location['longitude'];
|
||||
$computedLocation['radius'] = 20;
|
||||
$computedLocation['source'] = 2;
|
||||
$computedLocation['status'] = 1;
|
||||
}
|
||||
} elseif(isset($daten['rxInfo']['location'])) {
|
||||
$location = $daten['rxInfo']['location'];
|
||||
if(isset($location['latitude']) && isset($location['longitude'])) {
|
||||
$computedLocation['lat'] = (float)$location['latitude'];
|
||||
$computedLocation['lng'] = (float)$location['longitude'];
|
||||
$computedLocation['radius'] = 2000;
|
||||
$computedLocation['source'] = 2;
|
||||
$computedLocation['status'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'status' => 400,
|
||||
'status_message' => 'success'
|
||||
);
|
||||
|
||||
// Build JSON payload for IoT portal
|
||||
$json = array();
|
||||
$json['id'] = $daten['deviceName'];
|
||||
|
||||
if(isset($daten['importtime'])) {
|
||||
$json['time'] = $daten['importtime'];
|
||||
} else {
|
||||
$json['time'] = time();
|
||||
}
|
||||
|
||||
$json['computedLocation'] = $computedLocation;
|
||||
$json['data'] = "G2H-RS-HTGS-LoRA";
|
||||
|
||||
// Map G2H-RS sensor data to IoT portal format
|
||||
if($heating !== '') {
|
||||
$json['heating'] = $heating;
|
||||
}
|
||||
|
||||
if($heatingFloor !== '') {
|
||||
$json['heatingFloor'] = $heatingFloor;
|
||||
}
|
||||
|
||||
if($airHumidity !== '') {
|
||||
$json['airHumidity'] = $airHumidity;
|
||||
}
|
||||
|
||||
if($switch !== '') {
|
||||
$json['switch'] = $switch;
|
||||
}
|
||||
|
||||
if($relayOnTime !== null) $json['relayOnTime'] = $relayOnTime;
|
||||
if($uptime !== null) $json['uptime'] = $uptime;
|
||||
if($loadWatts !== null) $json['loadWatts'] = $loadWatts;
|
||||
if($powerConsumptionWh !== null) $json['power_consumption'] = $powerConsumptionWh;
|
||||
|
||||
// Handle sequence number - use fCnt from ChirpStack if available
|
||||
$seqNumber_dev = 1; // Default
|
||||
|
||||
if(isset($daten['fCnt']) && $daten['fCnt'] != '') {
|
||||
// Use LoRaWAN frame counter as sequence number
|
||||
$seqNumber_dev = (int)$daten['fCnt'];
|
||||
} elseif(isset($mesg['seqno'])) {
|
||||
// Use sequence number from decoded payload if available
|
||||
$seqNumber_dev = (int)$mesg['seqno'];
|
||||
} else {
|
||||
// Fallback to internal counter
|
||||
if(isset($seqNumber[$daten['deviceName']])) {
|
||||
$seqNumber_dev = $seqNumber[$daten['deviceName']];
|
||||
$seqNumber_dev++;
|
||||
} else {
|
||||
$seqNumber_dev = isset($seqNumber['1']) ? $seqNumber['1'] : 1;
|
||||
$seqNumber_dev++;
|
||||
}
|
||||
$seqNumber[$daten['deviceName']] = $seqNumber_dev;
|
||||
}
|
||||
|
||||
$json['seqNumber'] = (int)$seqNumber_dev;
|
||||
$json['lqi'] = "Good";
|
||||
$json['decoderClass'] = "G2HRoomSensor";
|
||||
$json['countrycode'] = "276";
|
||||
|
||||
// Skip sending if sequence number is 1 (initialization)
|
||||
if($json['seqNumber'] == '1') {
|
||||
if($debug) {
|
||||
file_put_contents($file, "\n\nSkipping send - seqNumber is 1\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($file, json_encode($json), FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
} else {
|
||||
// Send to IoT portal
|
||||
$json_encoded = json_encode($json);
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($file, "\n\nSending to IoT portal:\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($file, $json_encoded, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
$url = 'https://iot.satspeed.com/api/public/message';
|
||||
$headers = array(
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json'
|
||||
);
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $json_encoded);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
curl_close($curl);
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($file, "\n\nResponse code: " . $code . "\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($file, "Response: " . $response . "\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
// AUTO-DOWNLINK: Send settings-based downlink after successful uplink processing
|
||||
if($code == 200) {
|
||||
sendAutomaticDownlink($daten['deviceName'], $daten['devEUI'], $file, $debug);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
$response = array(
|
||||
'status' => 0,
|
||||
'status_message' => 'Methode PUT nicht erlaubt.'
|
||||
);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
$response = array(
|
||||
'status' => 0,
|
||||
'status_message' => 'Methode DELETE nicht erlaubt.'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(["ok" => true]);
|
||||
|
||||
|
||||
// Database operations - store message data
|
||||
sleep(1);
|
||||
$dbiot = new iotdbObj();
|
||||
$iotconnection = $dbiot->getConnstring_iot();
|
||||
|
||||
// Update last device message table
|
||||
if(isset($daten['deviceName'])) {
|
||||
$sql2 = "SELECT * FROM device_messages WHERE device_id = '".$daten['deviceName']."' ORDER BY id DESC LIMIT 1";
|
||||
$result2 = mysqli_query($iotconnection, $sql2);
|
||||
$row = mysqli_fetch_assoc($result2);
|
||||
|
||||
if($row && isset($row['id'])) {
|
||||
$sql3 = "UPDATE last_device_message SET
|
||||
id = '".$row['id']."',
|
||||
time = '".$row['time']."',
|
||||
data = '".$row['data']."',
|
||||
seqNumber = '".$row['seqNumber']."',
|
||||
created_at = '".$row['created_at']."',
|
||||
updated_at = '".$row['updated_at']."',
|
||||
atlas_location = '".$row['atlas_location']."',
|
||||
data_formatted = '".$row['data_formatted']."',
|
||||
lqi = '".$row['lqi']."',
|
||||
decoderClass = '".$row['decoderClass']."'
|
||||
WHERE device_id = '".$row['device_id']."'";
|
||||
mysqli_query($iotconnection, $sql3);
|
||||
}
|
||||
}
|
||||
|
||||
// === AUTO-DOWNLINK FUNCTIONS ===
|
||||
|
||||
function sendAutomaticDownlink($deviceName, $devEUI, $logFile, $debug) {
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "\n\n=== AUTO-DOWNLINK ===\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "Device: $deviceName, DevEUI: $devEUI\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
// Convert Base64 DevEUI to Hex if needed
|
||||
$hexDevEUI = $devEUI;
|
||||
if(!ctype_xdigit($devEUI)) {
|
||||
// DevEUI is Base64, decode to binary then to hex
|
||||
$binaryDevEUI = base64_decode($devEUI);
|
||||
$hexDevEUI = bin2hex($binaryDevEUI);
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "Converted DevEUI: $devEUI -> $hexDevEUI\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
// Get current settings from database
|
||||
$settings = getG2HSettings($deviceName, $debug, $logFile);
|
||||
|
||||
if(empty($settings)) {
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "No settings found for device - skipping downlink\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect all settings for combined packet
|
||||
$combinedSettings = [
|
||||
'heating_enable' => true, // Default: heating enabled
|
||||
'room_temp' => 20, // Default room temperature
|
||||
'floor_temp' => 22 // Default floor temperature
|
||||
];
|
||||
|
||||
foreach($settings as $setting) {
|
||||
switch($setting['type']) {
|
||||
case 'r1': // Relais control
|
||||
$combinedSettings['heating_enable'] = ($setting['value'] == '1');
|
||||
break;
|
||||
case 't1': // Room temperature
|
||||
$combinedSettings['room_temp'] = intval($setting['value']);
|
||||
break;
|
||||
case 't2': // Floor temperature
|
||||
$combinedSettings['floor_temp'] = intval($setting['value']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send combined downlink packet
|
||||
$jsonPayload = json_encode($combinedSettings);
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "Preparing combined downlink packet\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "Combined settings: " . $jsonPayload . "\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "heating_enable: " . ($combinedSettings['heating_enable'] ? 'true' : 'false') . "\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "room_temp: " . $combinedSettings['room_temp'] . "\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "floor_temp: " . $combinedSettings['floor_temp'] . "\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
sendChirpStackJsonDownlink($hexDevEUI, $jsonPayload, $logFile, $debug);
|
||||
}
|
||||
|
||||
function getG2HSettings($deviceName, $debug, $logFile) {
|
||||
$dbiot = new iotdbObj();
|
||||
$iotconnection = $dbiot->getConnstring_iot();
|
||||
|
||||
// Query for G2H-RS settings based on DB analysis (without settings_area for flexibility):
|
||||
// - Relais: name='active' AND settings_type=32
|
||||
// - Raumtemperatur: name='fixValue' AND settings_type=32
|
||||
// - Bodentemperatur: name='fixValue' AND settings_type=31
|
||||
$sql = "SELECT sf.value, sf.settings_type, sf.name, s.subject_id
|
||||
FROM settings_fields sf, settings s
|
||||
WHERE s.subject_id = '$deviceName'
|
||||
AND s.user_type = 'user'
|
||||
AND s.id = sf.settings
|
||||
AND (
|
||||
(sf.name = 'active' AND sf.settings_type = 32) OR
|
||||
(sf.name = 'fixValue' AND sf.settings_type = 32) OR
|
||||
(sf.name = 'fixValue' AND sf.settings_type = 31)
|
||||
)";
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "SQL: $sql\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
$result = mysqli_query($iotconnection, $sql);
|
||||
$settings = array();
|
||||
|
||||
while($row = mysqli_fetch_assoc($result)) {
|
||||
$setting = array();
|
||||
|
||||
if($row['name'] == 'active' && $row['settings_type'] == '32') {
|
||||
$setting['type'] = 'r1'; // Relais control
|
||||
$setting['value'] = ($row['value'] == 'true' || $row['value'] == '1') ? '1' : '0';
|
||||
}
|
||||
elseif($row['name'] == 'fixValue' && $row['settings_type'] == '32') {
|
||||
$setting['type'] = 't1'; // Room temperature
|
||||
$setting['value'] = $row['value'];
|
||||
}
|
||||
elseif($row['name'] == 'fixValue' && $row['settings_type'] == '31') {
|
||||
$setting['type'] = 't2'; // Floor temperature
|
||||
$setting['value'] = $row['value'];
|
||||
}
|
||||
|
||||
if(!empty($setting)) {
|
||||
$settings[] = $setting;
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "Found setting: {$setting['type']} = {$setting['value']}\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
function clearChirpStackDeviceQueue($devEUI, $logFile = null, $debug = false) {
|
||||
$CS_URL = 'http://172.17.13.42:8080';
|
||||
$TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcGlfa2V5X2lkIjoiMDcxM2MyNzUtMWIyNy00YzQ4LTlkYjQtNjc3MDg1NTgzN2M2IiwiYXVkIjoiYXMiLCJpc3MiOiJhcyIsIm5iZiI6MTc1NTk1OTgxOSwic3ViIjoiYXBpX2tleSJ9.sFDgV0Sk5LvXD1KAq50pXP2dmzf6qVtwJP8hPkriHpY';
|
||||
|
||||
$url = "{$CS_URL}/api/devices/{$devEUI}/queue";
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => "DELETE",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Grpc-Metadata-Authorization: Bearer {$TOKEN}",
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
curl_close($curl);
|
||||
|
||||
if($debug && $logFile) {
|
||||
file_put_contents($logFile, "Queue cleared for device {$devEUI}, HTTP {$httpCode}: {$response}\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
return $httpCode === 200;
|
||||
}
|
||||
|
||||
function sendChirpStackJsonDownlink($devEUI, $jsonPayload, $logFile, $debug) {
|
||||
$CS_URL = 'http://172.17.13.42:8080';
|
||||
$TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcGlfa2V5X2lkIjoiMDcxM2MyNzUtMWIyNy00YzQ4LTlkYjQtNjc3MDg1NTgzN2M2IiwiYXVkIjoiYXMiLCJpc3MiOiJhcyIsIm5iZiI6MTc1NTk1OTgxOSwic3ViIjoiYXBpX2tleSJ9.sFDgV0Sk5LvXD1KAq50pXP2dmzf6qVtwJP8hPkriHpY';
|
||||
|
||||
// Clear the queue first to prevent queue overflows
|
||||
// Wait 5 seconds to ensure old packets had opportunity to be sent during downlink windows
|
||||
sleep(5);
|
||||
clearChirpStackDeviceQueue($devEUI, $logFile, $debug);
|
||||
|
||||
// ChirpStack v3 format: we need to encode manually and send as base64 data
|
||||
$jsonObject = json_decode($jsonPayload, true);
|
||||
|
||||
// Manual encoding like the ChirpStack encoder does
|
||||
$bytes = [];
|
||||
$bytes[0] = $jsonObject['heating_enable'] ? 1 : 0; // Byte 0: heating enable
|
||||
$bytes[1] = intval($jsonObject['room_temp']); // Byte 1: room temp
|
||||
$bytes[2] = intval($jsonObject['floor_temp']); // Byte 2: floor temp
|
||||
|
||||
$binaryPayload = pack('C*', ...$bytes);
|
||||
$base64Payload = base64_encode($binaryPayload);
|
||||
|
||||
$queueItem = [
|
||||
'deviceQueueItem' => [
|
||||
'confirmed' => false,
|
||||
'fPort' => 2,
|
||||
'data' => $base64Payload, // Base64 data for ChirpStack v3
|
||||
],
|
||||
];
|
||||
|
||||
$url = "{$CS_URL}/api/devices/{$devEUI}/queue";
|
||||
$headers = [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json',
|
||||
'Grpc-Metadata-Authorization: Bearer ' . $TOKEN,
|
||||
];
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "Sending JSON downlink: $jsonPayload\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_POSTFIELDS => json_encode($queueItem, JSON_UNESCAPED_SLASHES),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if($debug) {
|
||||
file_put_contents($logFile, "Binary Downlink HTTP $httpCode: $response\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "Encoded bytes: " . bin2hex($binaryPayload) . " (Base64: $base64Payload)\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($logFile, "Full API Payload: " . json_encode($queueItem, JSON_UNESCAPED_SLASHES) . "\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save sequence number changes
|
||||
setarray($seqNumber);
|
||||
|
||||
function getseqno()
|
||||
{
|
||||
$testarray = unserialize(file_get_contents('/var/www/iot-dev/web/seqno.txt'));
|
||||
return ($testarray);
|
||||
}
|
||||
|
||||
function setarray($seqno)
|
||||
{
|
||||
file_put_contents('/var/www/iot-dev/web/seqno.txt', serialize($seqno));
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
62
src/energy.c
Normal file
62
src/energy.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
|
||||
#include <energy.h>
|
||||
|
||||
static int64_t relay_on_timestamp = 0;
|
||||
static uint32_t relay_on_time_seconds = 0;
|
||||
static bool relay_currently_on = false;
|
||||
static uint16_t load_watts = 400;
|
||||
|
||||
static int energy_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
if (strcmp(key, "load_watts") == 0) {
|
||||
read_cb(cb_arg, &load_watts, sizeof(load_watts));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(energy, "energy", NULL, energy_set, NULL, NULL);
|
||||
|
||||
void energy_init(void)
|
||||
{
|
||||
settings_load_subtree("energy");
|
||||
}
|
||||
|
||||
void energy_update_relay_state(bool new_state)
|
||||
{
|
||||
int64_t now = k_uptime_get();
|
||||
|
||||
if (relay_currently_on && !new_state) {
|
||||
relay_on_time_seconds += (uint32_t)((now - relay_on_timestamp) / 1000);
|
||||
}
|
||||
if (new_state && !relay_currently_on) {
|
||||
relay_on_timestamp = now;
|
||||
}
|
||||
relay_currently_on = new_state;
|
||||
}
|
||||
|
||||
uint32_t energy_get_on_time_seconds(void)
|
||||
{
|
||||
if (relay_currently_on) {
|
||||
int64_t now = k_uptime_get();
|
||||
return relay_on_time_seconds + (uint32_t)((now - relay_on_timestamp) / 1000);
|
||||
}
|
||||
return relay_on_time_seconds;
|
||||
}
|
||||
|
||||
uint32_t energy_get_uptime_seconds(void)
|
||||
{
|
||||
return (uint32_t)(k_uptime_get() / 1000);
|
||||
}
|
||||
|
||||
uint16_t energy_get_load_watts(void)
|
||||
{
|
||||
return load_watts;
|
||||
}
|
||||
|
||||
void energy_set_load_watts(uint16_t watts)
|
||||
{
|
||||
load_watts = watts;
|
||||
settings_save_one("energy/load_watts", &load_watts, sizeof(load_watts));
|
||||
}
|
||||
11
src/energy.h
Normal file
11
src/energy.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void energy_init(void);
|
||||
void energy_update_relay_state(bool state);
|
||||
uint32_t energy_get_on_time_seconds(void);
|
||||
uint32_t energy_get_uptime_seconds(void);
|
||||
uint16_t energy_get_load_watts(void);
|
||||
void energy_set_load_watts(uint16_t watts);
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <lora/lorawan.h>
|
||||
#include <gpio.h>
|
||||
#include <energy.h>
|
||||
|
||||
#define SETTINGS_KEY "LORAWAN_DEV_NONCE"
|
||||
|
||||
@ -41,12 +42,32 @@ static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, u
|
||||
LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
|
||||
}
|
||||
|
||||
/* Send interval command: 'i' + interval_minutes (1 byte)
|
||||
* Format: ['i'][interval_minutes]
|
||||
* Example: 'i' + 5 = 5 minutes
|
||||
*/
|
||||
if (len >= 2 && hex_data[0] == 'i')
|
||||
{
|
||||
uint8_t new_interval = hex_data[1];
|
||||
send_interval_minutes = new_interval;
|
||||
LOG_INF("Send interval updated to %d minutes", send_interval_minutes);
|
||||
}
|
||||
/* Load configuration: 'w' + watts (2 bytes big-endian)
|
||||
* Format: ['w'][watts_high][watts_low]
|
||||
* Example: 0x77 0x01 0x90 = 400W
|
||||
*/
|
||||
else if (len >= 3 && hex_data[0] == 'w')
|
||||
{
|
||||
uint16_t new_load = ((uint16_t)hex_data[1] << 8) | hex_data[2];
|
||||
energy_set_load_watts(new_load);
|
||||
LOG_INF("Load updated to %d W", new_load);
|
||||
}
|
||||
/* 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)
|
||||
else if (len >= 3)
|
||||
{
|
||||
uint8_t heating_enable = hex_data[0];
|
||||
uint8_t room_temp_threshold = hex_data[1];
|
||||
@ -86,16 +107,6 @@ static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, u
|
||||
LOG_INF("Heating control logic enabled with new thresholds");
|
||||
}
|
||||
}
|
||||
/* 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_interval = hex_data[1];
|
||||
send_interval_minutes = new_interval;
|
||||
LOG_INF("Send interval updated to %d minutes", send_interval_minutes);
|
||||
}
|
||||
}
|
||||
|
||||
struct lorawan_downlink_cb downlink_cb = {
|
||||
@ -342,13 +353,12 @@ void send_data_packet(char *data, uint8_t data_len)
|
||||
}
|
||||
}
|
||||
|
||||
void create_data_package(char *buffer, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state)
|
||||
void create_data_package(char *buffer, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state, uint32_t on_time, uint32_t uptime, uint16_t load_watts)
|
||||
{
|
||||
int16_t temp = (int16_t)(temp_room_mlx * 100);
|
||||
int16_t floor_temp = (int16_t)(temp_floor * 100);
|
||||
int16_t hum = (int16_t)(humidity * 100);
|
||||
|
||||
// Packen der Daten in das Byte-Array
|
||||
buffer[0] = (temp >> 8) & 0xFF;
|
||||
buffer[1] = temp & 0xFF;
|
||||
buffer[2] = (floor_temp >> 8) & 0xFF;
|
||||
@ -356,4 +366,18 @@ void create_data_package(char *buffer, float temp_room_mlx, float temp_floor, fl
|
||||
buffer[4] = (hum >> 8) & 0xFF;
|
||||
buffer[5] = hum & 0xFF;
|
||||
buffer[6] = relais_state;
|
||||
|
||||
/* Relay ON-time (uint24, seconds since boot) */
|
||||
buffer[7] = (on_time >> 16) & 0xFF;
|
||||
buffer[8] = (on_time >> 8) & 0xFF;
|
||||
buffer[9] = on_time & 0xFF;
|
||||
|
||||
/* Uptime (uint24, seconds since boot) */
|
||||
buffer[10] = (uptime >> 16) & 0xFF;
|
||||
buffer[11] = (uptime >> 8) & 0xFF;
|
||||
buffer[12] = uptime & 0xFF;
|
||||
|
||||
/* Load (uint16, Watt) */
|
||||
buffer[13] = (load_watts >> 8) & 0xFF;
|
||||
buffer[14] = load_watts & 0xFF;
|
||||
}
|
||||
@ -29,4 +29,4 @@ void join_network_abp();
|
||||
|
||||
void send_data_packet(char *data, uint8_t data_len);
|
||||
|
||||
void create_data_package(char *package, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state);
|
||||
void create_data_package(char *package, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state, uint32_t on_time, uint32_t uptime, uint16_t load_watts);
|
||||
|
||||
18
src/main.c
18
src/main.c
@ -11,6 +11,7 @@
|
||||
#include <sensors/mlx90614.h>
|
||||
#include <sensors/sht4x.h>
|
||||
#include <gpio.h>
|
||||
#include <energy.h>
|
||||
|
||||
LOG_MODULE_REGISTER(g2h_heat_main);
|
||||
|
||||
@ -31,6 +32,7 @@ int main(void)
|
||||
init_mlx90614();
|
||||
init_sht4x();
|
||||
init_lorawan();
|
||||
energy_init();
|
||||
// join_network_otaa();
|
||||
join_network_abp();
|
||||
|
||||
@ -86,22 +88,26 @@ int main(void)
|
||||
// Heat OFF if ANY temperature reaches its threshold
|
||||
if (temp_below_threshold && floor_below_threshold)
|
||||
{
|
||||
relais_state = 1; // Heat on - both temps below thresholds
|
||||
relais_state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
relais_state = 0; // Heat off - at least one threshold reached
|
||||
relais_state = 0;
|
||||
}
|
||||
energy_update_relay_state(relais_state);
|
||||
set_relais_state(relais_state);
|
||||
}
|
||||
printk("relais: %d (heating_enabled: %d, temp_th: %d°C, floor_th: %d°C)\n",
|
||||
relais_state, heating_logic_enabled, temperature_threshold, floor_temp_threshold);
|
||||
|
||||
/* Pack and send values */
|
||||
char data[7];
|
||||
create_data_package(data, temp, floor_temp, humidity, relais_state);
|
||||
LOG_HEXDUMP_DBG(data, 7, "data");
|
||||
send_data_packet(data, 7);
|
||||
char data[15];
|
||||
create_data_package(data, temp, floor_temp, humidity, relais_state,
|
||||
energy_get_on_time_seconds(),
|
||||
energy_get_uptime_seconds(),
|
||||
energy_get_load_watts());
|
||||
LOG_HEXDUMP_DBG(data, 15, "data");
|
||||
send_data_packet(data, 15);
|
||||
|
||||
/* Delay until next cycle */
|
||||
k_sleep(K_MINUTES(send_interval_minutes));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user