Compare commits

...

2 Commits

Author SHA1 Message Date
dbee6b8287 Skalierungskorrekturen 2026-07-14 13:36:38 +02:00
71892d4d5c Div. Anpassungen 2026-07-14 13:34:37 +02:00
17 changed files with 463 additions and 153 deletions

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"nrf-connect.applications": [
"${workspaceFolder}/firmware"
],
"nrf-connect.boardRoots": [
"${workspaceFolder}/firmware"
]
}

View File

@@ -0,0 +1,2 @@
config BOARD_POOLTEMP
select SOC_NRF52840_QIAA

View File

@@ -0,0 +1,2 @@
config HW_STACK_PROTECTION
default ARCH_HAS_STACK_PROTECTION

View File

@@ -0,0 +1,2 @@
config PARTITION_MANAGER
default n

View File

@@ -0,0 +1 @@
&pinctrl {};

View File

@@ -0,0 +1,106 @@
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include "PoolTemp-pinctrl.dtsi"
/ {
model = "Pool Thermometer";
compatible = "ItenEngineering,PoolTemp";
chosen {
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,code-partition = &slot0_partition;
zephyr,ieee802154 = &ieee802154;
};
zephyr,user {
io-channels = <&adc 0>;
};
aliases {
pooltemp = &ds18b20_sensor;
};
sensor_power: regulator-sensor-power {
compatible = "regulator-fixed";
regulator-name = "sensor-power";
enable-gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>;
startup-delay-us = <2000>;
regulator-boot-on;
};
w1_0: w1-zephyr-gpio {
compatible = "zephyr,w1-gpio";
gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
ds18b20_sensor: ds18b20 {
compatible = "maxim,ds18b20";
family-code = <0x28>;
resolution = <12>;
};
};
};
&ieee802154 {
status = "okay";
};
&reg1 {
regulator-initial-mode = <NRF5X_REG_MODE_DCDC>;
};
&gpio0 {
status = "okay";
};
&gpiote {
status = "okay";
};
/* Battery voltage measurement via the internal VDD channel.
* E73 modules are typically powered from VDD (not VDDH).
*/
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x0 DT_SIZE_K(48)>;
};
slot0_partition: partition@c000 {
label = "image-0";
reg = <0xc000 DT_SIZE_K(472)>;
};
slot1_partition: partition@82000 {
label = "image-1";
reg = <0x82000 DT_SIZE_K(472)>;
};
storage_partition: partition@f8000 {
label = "storage";
reg = <0xf8000 DT_SIZE_K(32)>;
};
};
};
&adc {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)>;
zephyr,resolution = <12>;
zephyr,input-positive = <NRF_SAADC_VDD>;
};
};

View File

@@ -0,0 +1,10 @@
identifier: PoolTemp/nrf52840
name: Pool Thermometer
vendor: ItenEngineering
type: mcu
arch: arm
ram: 256
flash: 472
toolchain:
- zephyr
supported: []

View File

@@ -0,0 +1,3 @@
CONFIG_ARM_MPU=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y

View File

@@ -0,0 +1,9 @@
set(OPENOCD_NRF5_SUBFAMILY "nrf52")
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=1000")
board_runner_args(pyocd "--target=nrf52840" "--frequency=1000000")
include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)

View File

@@ -0,0 +1,6 @@
board:
name: PoolTemp
full_name: PoolTemp
vendor: ItenEngineering
socs:
- name: nrf52840

View File

@@ -0,0 +1,2 @@
# Suppress "unique_unit_address_if_enabled" to handle some overlaps
list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")

View File

@@ -2,6 +2,30 @@
zephyr,user { zephyr,user {
io-channels = <&adc 0>; io-channels = <&adc 0>;
}; };
aliases {
pooltemp = &ds18b20_sensor;
};
sensor_power: regulator-sensor-power {
compatible = "regulator-fixed";
regulator-name = "sensor-power";
enable-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; // Check your supply pin here.
startup-delay-us = <2000>;
regulator-boot-on;
};
w1_0: w1-zephyr-gpio {
compatible = "zephyr,w1-gpio";
gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; // Check your data pin here.
ds18b20_sensor: ds18b20 {
compatible = "maxim,ds18b20";
family-code = <0x28>;
resolution = <12>;
// Removed: supply-gpios = <&sensor_power>;
};
};
}; };
&adc { &adc {
@@ -12,9 +36,9 @@
channel@0 { channel@0 {
reg = <0>; reg = <0>;
zephyr,gain = "ADC_GAIN_1_6"; zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL"; // Intern 0.6V zephyr,reference = "ADC_REF_INTERNAL"; // Internal 0.6 V
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_VDD>; // Hier greift er intern VDD ab! zephyr,input-positive = <NRF_SAADC_VDD>; // This reads VDD internally.
zephyr,resolution = <12>; zephyr,resolution = <12>;
}; };
}; };

23
firmware/debug.Kconfig Normal file
View File

@@ -0,0 +1,23 @@
# Logging
CONFIG_LOG=y
CONFIG_PM=n
CONFIG_NET_LOG=y
# Networking and OpenThread shells
CONFIG_SHELL=y
CONFIG_NET_SHELL=y
CONFIG_OPENTHREAD_SHELL=y
# SEGGER RTT Backend (Logging & Shell)
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_INIT_MODE_ALWAYS=y
CONFIG_SHELL_BACKEND_RTT=y
CONFIG_RTT_CONSOLE=n
CONFIG_LOG_BACKEND_RTT=n
CONFIG_SHELL_BACKEND_RTT_BUFFER=0
CONFIG_SHELL_LOG_BACKEND=y
# UART Backends deaktivieren
CONFIG_LOG_BACKEND_UART=n
CONFIG_SHELL_BACKEND_SERIAL=n
CONFIG_UART_CONSOLE=n

View File

@@ -19,18 +19,14 @@ CONFIG_OPENTHREAD_FTD=n
CONFIG_OPENTHREAD_MTD=y CONFIG_OPENTHREAD_MTD=y
CONFIG_OPENTHREAD_MTD_SED=y CONFIG_OPENTHREAD_MTD_SED=y
# Logging
CONFIG_LOG=y
CONFIG_NET_LOG=y
# Networking and OpenThread shells
CONFIG_SHELL=y
CONFIG_NET_SHELL=y
CONFIG_OPENTHREAD_SHELL=y
# HW info for Identifier # HW info for Identifier
CONFIG_HWINFO=y CONFIG_HWINFO=y
# Interner Temperatursensor # Temperature Sensor and VDD measurement
CONFIG_GPIO=y
CONFIG_SENSOR=y CONFIG_SENSOR=y
CONFIG_W1=y
CONFIG_DS18B20=y
CONFIG_ADC=y CONFIG_ADC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED=y

View File

@@ -7,16 +7,22 @@
#include <zephyr/drivers/adc.h> #include <zephyr/drivers/adc.h>
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/hwinfo.h> #include <zephyr/drivers/hwinfo.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h> #include <zephyr/settings/settings.h>
#if defined(CONFIG_SHELL)
#include <zephyr/shell/shell.h>
#endif
#include <openthread/thread.h> #include <openthread/thread.h>
#include <openthread/platform/radio.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
// #define FAST_TIMES // use seconds instead of minutes and hours for testing // #define FAST_TIMES
// --- Konfiguration ---
#define TARGET_IP "fd00:10:0:10:be24:11ff:fe12:6196" #define TARGET_IP "fd00:10:0:10:be24:11ff:fe12:6196"
#define TARGET_PORT 6969 #define TARGET_PORT 6969
#define LISTEN_PORT 6969 #define LISTEN_PORT 6969
@@ -25,6 +31,7 @@
#define MAX_SEND_INTERVAL_M 20 #define MAX_SEND_INTERVAL_M 20
#define BATTERY_MEASURE_INTERVAL_H 24 #define BATTERY_MEASURE_INTERVAL_H 24
#define UDP_RX_POLL_TIMEOUT_MS 100 #define UDP_RX_POLL_TIMEOUT_MS 100
#define IEEE802154_MAX_TX_POWER_DBM 8
#define S_TO_MS(s) ((uint64_t)(s) * 1000ULL) #define S_TO_MS(s) ((uint64_t)(s) * 1000ULL)
#define M_TO_MS(m) ((uint64_t)(m) * 60ULL * 1000ULL) #define M_TO_MS(m) ((uint64_t)(m) * 60ULL * 1000ULL)
@@ -36,10 +43,9 @@ enum payload_type
{ {
PAYLOAD_TYPE_TEMP = 0x00, PAYLOAD_TYPE_TEMP = 0x00,
PAYLOAD_TYPE_BATTERY = 0x01, PAYLOAD_TYPE_BATTERY = 0x01,
PAYLOAD_TYPE_CONFIG = 0x10 PAYLOAD_TYPE_CONFIG = 0x10,
}; };
// --- Payload Definition ---
struct header struct header
{ {
uint8_t proto_version; uint8_t proto_version;
@@ -49,6 +55,7 @@ struct header
} __attribute__((packed)); } __attribute__((packed));
int32_t temp_mcelsius; int32_t temp_mcelsius;
int32_t filtered_temperature_mc = 0xFFFF;
int16_t adc_buffer; int16_t adc_buffer;
uint16_t measure_interval_m = MEASURE_INTERVAL_M; uint16_t measure_interval_m = MEASURE_INTERVAL_M;
@@ -56,19 +63,78 @@ uint16_t send_delta_mc = SEND_DELTA_MC;
uint16_t max_send_interval_m = MAX_SEND_INTERVAL_M; uint16_t max_send_interval_m = MAX_SEND_INTERVAL_M;
uint16_t battery_measure_interval_h = BATTERY_MEASURE_INTERVAL_H; uint16_t battery_measure_interval_h = BATTERY_MEASURE_INTERVAL_H;
int sock; int sock = -1;
struct sockaddr_in6 dest_addr; struct sockaddr_in6 dest_addr;
struct header header = {.proto_version = 1, .device_id = {0}, .seq_num = 0}; struct header header = {.proto_version = 1, .device_id = {0}, .seq_num = 0};
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user)); static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
static const struct device *const sensor_power_regulator = DEVICE_DT_GET(DT_NODELABEL(sensor_power));
static const struct device *const temp_dev = DEVICE_DT_GET(DT_ALIAS(pooltemp));
struct adc_sequence sequence = { struct adc_sequence sequence = {
.buffer = &adc_buffer, .buffer = &adc_buffer,
.buffer_size = sizeof(adc_buffer), .buffer_size = sizeof(adc_buffer),
.calibrate = true, .calibrate = true,
}; };
static int pool_settings_set(const char *name, size_t len, static int sensor_power_init(void)
settings_read_cb read_cb, void *cb_arg) {
if (!device_is_ready(sensor_power_regulator))
{
LOG_ERR("Sensor power regulator is not ready");
return -ENODEV;
}
int rc = regulator_enable(sensor_power_regulator);
if (rc < 0)
{
LOG_ERR("Failed to enable sensor power regulator (%d)", rc);
return rc;
}
k_msleep(10);
return 0;
}
static int sensor_power_off(void)
{
int rc = regulator_disable(sensor_power_regulator);
if (rc < 0)
{
LOG_WRN("Failed to disable sensor power regulator (%d)", rc);
}
return rc;
}
static int radio_set_max_tx_power(void)
{
otInstance *instance;
otError err;
openthread_mutex_lock();
instance = openthread_get_default_instance();
if (instance == NULL)
{
openthread_mutex_unlock();
LOG_ERR("OpenThread instance not available");
return -ENODEV;
}
err = otPlatRadioSetTransmitPower(instance, IEEE802154_MAX_TX_POWER_DBM);
openthread_mutex_unlock();
if (err != OT_ERROR_NONE)
{
LOG_ERR("Failed to set IEEE 802.15.4 TX power to %d dBm (%d)", IEEE802154_MAX_TX_POWER_DBM, err);
return -EIO;
}
LOG_INF("IEEE 802.15.4 TX power set to %d dBm", IEEE802154_MAX_TX_POWER_DBM);
return 0;
}
static int pool_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
{ {
const char *next; const char *next;
size_t name_len = settings_name_next(name, &next); size_t name_len = settings_name_next(name, &next);
@@ -86,43 +152,46 @@ static int pool_settings_set(const char *name, size_t len,
rc = read_cb(cb_arg, &measure_interval_m, sizeof(measure_interval_m)); rc = read_cb(cb_arg, &measure_interval_m, sizeof(measure_interval_m));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen von meas_int: %d", rc); LOG_ERR("Failed to read meas_int: %d", rc);
return rc; return rc;
} }
LOG_DBG("Gelesen: meas_int=%u", measure_interval_m); LOG_DBG("Read meas_int=%u", measure_interval_m);
return 0; return 0;
} }
if (strncmp(name, "delta_mc", name_len) == 0) if (strncmp(name, "delta_mc", name_len) == 0)
{ {
rc = read_cb(cb_arg, &send_delta_mc, sizeof(send_delta_mc)); rc = read_cb(cb_arg, &send_delta_mc, sizeof(send_delta_mc));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen von delta_mc: %d", rc); LOG_ERR("Failed to read delta_mc: %d", rc);
return rc; return rc;
} }
LOG_DBG("Gelesen: delta_mc=%u", send_delta_mc); LOG_DBG("Read delta_mc=%u", send_delta_mc);
return 0; return 0;
} }
if (strncmp(name, "max_int", name_len) == 0) if (strncmp(name, "max_int", name_len) == 0)
{ {
rc = read_cb(cb_arg, &max_send_interval_m, sizeof(max_send_interval_m)); rc = read_cb(cb_arg, &max_send_interval_m, sizeof(max_send_interval_m));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen von max_int: %d", rc); LOG_ERR("Failed to read max_int: %d", rc);
return rc; return rc;
} }
LOG_DBG("Gelesen: max_int=%u", max_send_interval_m); LOG_DBG("Read max_int=%u", max_send_interval_m);
return 0; return 0;
} }
if (strncmp(name, "batt_h", name_len) == 0) if (strncmp(name, "batt_h", name_len) == 0)
{ {
rc = read_cb(cb_arg, &battery_measure_interval_h, sizeof(battery_measure_interval_h)); rc = read_cb(cb_arg, &battery_measure_interval_h, sizeof(battery_measure_interval_h));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen von batt_h: %d", rc); LOG_ERR("Failed to read batt_h: %d", rc);
return rc; return rc;
} }
LOG_DBG("Gelesen: batt_h=%u", battery_measure_interval_h); LOG_DBG("Read batt_h=%u", battery_measure_interval_h);
return 0; return 0;
} }
@@ -131,7 +200,8 @@ static int pool_settings_set(const char *name, size_t len,
struct settings_handler pool_conf_handler = { struct settings_handler pool_conf_handler = {
.name = "pool", .name = "pool",
.h_set = pool_settings_set}; .h_set = pool_settings_set,
};
static bool thread_is_attached(void) static bool thread_is_attached(void)
{ {
@@ -149,37 +219,33 @@ static bool thread_is_attached(void)
role = otThreadGetDeviceRole(instance); role = otThreadGetDeviceRole(instance);
openthread_mutex_unlock(); openthread_mutex_unlock();
return role == OT_DEVICE_ROLE_CHILD || return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
role == OT_DEVICE_ROLE_ROUTER ||
role == OT_DEVICE_ROLE_LEADER;
} }
int adc_init(void) int adc_init(void)
{ {
int rc; int rc;
// ADC-Sequenz konfigurieren
if (!adc_is_ready_dt(&adc_channel)) if (!adc_is_ready_dt(&adc_channel))
{ {
printk("Fehler: ADC-Device ist nicht bereit.\n"); printk("Error: ADC device is not ready.\n");
return -ENODEV; return -ENODEV;
} }
// Kanal initialisieren
rc = adc_channel_setup_dt(&adc_channel); rc = adc_channel_setup_dt(&adc_channel);
if (rc < 0) if (rc < 0)
{ {
printk("Fehler beim Setup des ADC-Kanals (%d)\n", rc); printk("Failed to set up ADC channel (%d)\n", rc);
return rc; return rc;
} }
// Sequence mit den DT-Specs füllen
rc = adc_sequence_init_dt(&adc_channel, &sequence); rc = adc_sequence_init_dt(&adc_channel, &sequence);
if (rc < 0) if (rc < 0)
{ {
printk("Fehler beim Initialisieren der Sequenz (%d)\n", rc); printk("Failed to initialize ADC sequence (%d)\n", rc);
return rc; return rc;
} }
return 0; return 0;
} }
@@ -195,17 +261,15 @@ int send_frame(uint8_t type, uint8_t *data, size_t data_len)
sys_put_be16(header.seq_num, &buffer[10]); sys_put_be16(header.seq_num, &buffer[10]);
memcpy(&buffer[12], data, data_len); memcpy(&buffer[12], data, data_len);
rc = zsock_sendto(sock, buffer, sizeof(header) + data_len, 0, rc = zsock_sendto(sock, buffer, sizeof(header) + data_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
(struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Senden fehlgeschlagen: %d", rc); LOG_ERR("Send failed: %d", rc);
} }
else else
{ {
LOG_INF("Paket gesendet | Typ: 0x%02x | Seq: %u", header.type, header.seq_num); LOG_INF("Packet sent | type: 0x%02x | seq: %u", header.type, header.seq_num);
LOG_HEXDUMP_DBG(buffer, sizeof(header) + data_len, "Gesendete Daten:"); LOG_HEXDUMP_DBG(buffer, sizeof(header) + data_len, "Sent data:");
} }
header.seq_num++; header.seq_num++;
@@ -215,6 +279,7 @@ int send_frame(uint8_t type, uint8_t *data, size_t data_len)
int send_config(void) int send_config(void)
{ {
uint8_t send_buf[8]; uint8_t send_buf[8];
sys_put_be16(measure_interval_m, &send_buf[0]); sys_put_be16(measure_interval_m, &send_buf[0]);
sys_put_be16(send_delta_mc, &send_buf[2]); sys_put_be16(send_delta_mc, &send_buf[2]);
sys_put_be16(max_send_interval_m, &send_buf[4]); sys_put_be16(max_send_interval_m, &send_buf[4]);
@@ -228,105 +293,132 @@ int recv_udp(void)
uint8_t recv_buf[32]; uint8_t recv_buf[32];
struct sockaddr_in6 from_addr; struct sockaddr_in6 from_addr;
socklen_t from_addr_len = sizeof(from_addr); socklen_t from_addr_len = sizeof(from_addr);
// Poll-Struktur vorbereiten
struct zsock_pollfd fds[1]; struct zsock_pollfd fds[1];
fds[0].fd = sock; fds[0].fd = sock;
fds[0].events = ZSOCK_POLLIN; fds[0].events = ZSOCK_POLLIN;
LOG_DBG("Warte kurz auf eingehende Konfigurationsdaten..."); LOG_DBG("Waiting briefly for incoming config data...");
// poll blockiert maximal UDP_RX_POLL_TIMEOUT_MS
int ret = zsock_poll(fds, 1, UDP_RX_POLL_TIMEOUT_MS); int ret = zsock_poll(fds, 1, UDP_RX_POLL_TIMEOUT_MS);
if (ret > 0) if (ret > 0)
{ {
// Daten liegen bereit! recvfrom blockiert JETZT nicht mehr
if (fds[0].revents & ZSOCK_POLLIN) if (fds[0].revents & ZSOCK_POLLIN)
{ {
int len = zsock_recvfrom(sock, recv_buf, sizeof(recv_buf), 0, int len = zsock_recvfrom(sock, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&from_addr, &from_addr_len);
(struct sockaddr *)&from_addr, &from_addr_len);
if (len < 0) if (len < 0)
{ {
LOG_ERR("recvfrom fehlgeschlagen: %d (errno: %d)", len, errno); LOG_ERR("recvfrom failed: %d (errno: %d)", len, errno);
return len; return len;
} }
if (len >= 12 && if (len >= 12 && recv_buf[0] == header.proto_version && recv_buf[1] == PAYLOAD_TYPE_CONFIG)
recv_buf[0] == header.proto_version &&
recv_buf[1] == PAYLOAD_TYPE_CONFIG)
{ {
/* Kurzes Config-Format ohne UUID:
* [ver(1), type(1), seq(2), meas(2), delta(2), max(2), batt(2)]
*/
uint16_t rx_seq = sys_get_be16(&recv_buf[2]); uint16_t rx_seq = sys_get_be16(&recv_buf[2]);
uint16_t new_meas_int = sys_get_be16(&recv_buf[4]); uint16_t new_meas_int = sys_get_be16(&recv_buf[4]);
uint16_t new_delta_mc = sys_get_be16(&recv_buf[6]); uint16_t new_delta_mc = sys_get_be16(&recv_buf[6]);
uint16_t new_max_int = sys_get_be16(&recv_buf[8]); uint16_t new_max_int = sys_get_be16(&recv_buf[8]);
uint16_t new_batt_int = sys_get_be16(&recv_buf[10]); uint16_t new_batt_int = sys_get_be16(&recv_buf[10]);
LOG_HEXDUMP_DBG(recv_buf, len, "Empfangene Config-Daten:"); LOG_HEXDUMP_DBG(recv_buf, len, "Received config data:");
LOG_INF("Neue Config erhalten! Seq: %u", rx_seq); LOG_INF("New config received! seq: %u", rx_seq);
LOG_INF(" -> Messintervall: %u min", new_meas_int); LOG_INF(" -> measurement interval: %u min", new_meas_int);
LOG_INF(" -> Temp Delta-T: %u m°C", new_delta_mc); LOG_INF(" -> temperature delta: %u mC", new_delta_mc);
LOG_INF(" -> Max Intervall: %u min", new_max_int); LOG_INF(" -> max interval: %u min", new_max_int);
LOG_INF(" -> Batt Intervall:%u h", new_batt_int); LOG_INF(" -> battery interval: %u h", new_batt_int);
if (new_max_int < new_meas_int) if (new_max_int < new_meas_int)
{ {
LOG_WRN("Empfangenes Max-Intervall (%u) ist kleiner als Messintervall (%u). Ignoriere Max-Intervall.", LOG_WRN("Received max interval (%u) is smaller than measurement interval (%u). Ignoring max interval.", new_max_int, new_meas_int);
new_max_int, new_meas_int); new_max_int = new_meas_int;
new_max_int = new_meas_int; // Behalte alten Wert bei
} }
if (new_meas_int >= 0 && new_meas_int <= 60 && new_meas_int != measure_interval_m) if (new_meas_int <= 60 && new_meas_int != measure_interval_m)
{ {
measure_interval_m = new_meas_int; measure_interval_m = new_meas_int;
settings_save_one("pool/meas_int", &measure_interval_m, sizeof(measure_interval_m)); settings_save_one("pool/meas_int", &measure_interval_m, sizeof(measure_interval_m));
LOG_INF("Messintervall auf %u min gesetzt und im Flash gespeichert.", measure_interval_m); LOG_INF("Measurement interval set to %u min and saved to flash.", measure_interval_m);
} }
if (new_delta_mc > 0 && new_delta_mc <= 2000 && new_delta_mc != send_delta_mc) if (new_delta_mc > 0 && new_delta_mc <= 2000 && new_delta_mc != send_delta_mc)
{ {
send_delta_mc = new_delta_mc; send_delta_mc = new_delta_mc;
settings_save_one("pool/delta_mc", &send_delta_mc, sizeof(send_delta_mc)); settings_save_one("pool/delta_mc", &send_delta_mc, sizeof(send_delta_mc));
LOG_INF("Temp Delta auf %u m°C gesetzt und im Flash gespeichert.", send_delta_mc); LOG_INF("Temperature delta set to %u mC and saved to flash.", send_delta_mc);
} }
if (new_max_int >= 5 && new_max_int <= 120 && new_max_int != max_send_interval_m) if (new_max_int >= 5 && new_max_int <= 120 && new_max_int != max_send_interval_m)
{ {
max_send_interval_m = new_max_int; max_send_interval_m = new_max_int;
settings_save_one("pool/max_int", &max_send_interval_m, sizeof(max_send_interval_m)); settings_save_one("pool/max_int", &max_send_interval_m, sizeof(max_send_interval_m));
LOG_INF("Max Intervall auf %u min gesetzt und im Flash gespeichert.", max_send_interval_m); LOG_INF("Max interval set to %u min and saved to flash.", max_send_interval_m);
} }
if (new_batt_int >= 2 && new_batt_int <= 72 && new_batt_int != battery_measure_interval_h) if (new_batt_int >= 2 && new_batt_int <= 72 && new_batt_int != battery_measure_interval_h)
{ {
battery_measure_interval_h = new_batt_int; battery_measure_interval_h = new_batt_int;
settings_save_one("pool/batt_h", &battery_measure_interval_h, sizeof(battery_measure_interval_h)); settings_save_one("pool/batt_h", &battery_measure_interval_h, sizeof(battery_measure_interval_h));
LOG_INF("Batterie Intervall auf %u h gesetzt und im Flash gespeichert.", battery_measure_interval_h); LOG_INF("Battery interval set to %u h and saved to flash.", battery_measure_interval_h);
} }
LOG_INF("Settings im Flash gespeichert.");
send_config(); // Sende die neue Config zurück, um zu bestätigen LOG_INF("Settings saved to flash.");
send_config();
} }
else else
{ {
LOG_DBG("UDP Paket ignoriert (len=%d, type=0x%02x)", len, LOG_DBG("UDP packet ignored (len=%d, type=0x%02x)", len, len > 1 ? recv_buf[1] : 0xff);
len > 1 ? recv_buf[1] : 0xff);
} }
} }
} }
else if (ret == 0) else if (ret == 0)
{ {
// Das ist der Timeout nach 1 Sekunde LOG_DBG("No config data pending from the gateway (timeout).");
LOG_DBG("Keine Config-Daten vom Gateway ausstehend (Timeout).");
} }
else else
{ {
LOG_ERR("Fehler beim Pollen des Sockets: %d (errno: %d)", ret, errno); LOG_ERR("Error polling socket: %d (errno: %d)", ret, errno);
} }
return 0; return 0;
} }
#if defined(CONFIG_SHELL)
static int cmd_udp_receive_pending(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (sock < 0)
{
shell_error(sh, "UDP socket is not initialized yet.");
return -ENOTCONN;
}
int rc = send_frame(0xFF, NULL, 0);
if (rc < 0)
{
shell_error(sh, "Failed to send UDP ping frame (%d)", rc);
return rc;
}
k_msleep(300);
rc = recv_udp();
if (rc < 0)
{
shell_error(sh, "UDP receive failed (%d)", rc);
return rc;
}
shell_print(sh, "UDP ping sent, waited 300 ms, receive poll done.");
return 0;
}
SHELL_CMD_REGISTER(udp_recv, NULL,
"Poll pending UDP config frames once",
cmd_udp_receive_pending);
#endif
int main(void) int main(void)
{ {
static int rc; static int rc;
@@ -337,65 +429,70 @@ int main(void)
static uint64_t last_temp_send_time = 0; static uint64_t last_temp_send_time = 0;
static int32_t last_temp_send_mc = 0; static int32_t last_temp_send_mc = 0;
static uint64_t last_battery_send_time = 0; static uint64_t last_battery_send_time = 0;
uint32_t temperature_mc;
const struct device *const temp_dev = DEVICE_DT_GET_ANY(nordic_nrf_temp); rc = sensor_power_init();
if (rc < 0)
if (temp_dev == NULL)
{ {
LOG_ERR("Fehler: Kein nRF-Temperatursensor gefunden."); return rc;
return -ENODEV;
} }
sensor_power_off();
if (!device_is_ready(temp_dev)) if (!device_is_ready(temp_dev))
{ {
LOG_ERR("Fehler: Temperatursensor-Device ist nicht bereit."); LOG_ERR("Error: Temperature sensor device is not ready.");
return -ENODEV; return -ENODEV;
} }
LOG_INF("Temperatursensor (%s) erfolgreich initialisiert.", temp_dev->name); LOG_INF("Temperature sensor (%s) successfully initialized.", temp_dev->name);
rc = adc_init(); rc = adc_init();
if (rc < 0) if (rc < 0)
{ {
printk("Fehler beim Initialisieren des ADC (%d)\n", rc); printk("Failed to initialize ADC (%d)\n", rc);
return rc; return rc;
} }
rc = settings_subsys_init(); rc = settings_subsys_init();
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Settings Subsystem Init fehlgeschlagen: %d", rc); LOG_ERR("Settings subsystem init failed: %d", rc);
} }
else else
{ {
rc = settings_register(&pool_conf_handler); rc = settings_register(&pool_conf_handler);
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Settings Register fehlgeschlagen: %d", rc); LOG_ERR("Settings register failed: %d", rc);
} }
// Lädt alle gespeicherten Werte aus dem NVS-Flash und triggert den Handler
rc = settings_load(); rc = settings_load();
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Settings Load fehlgeschlagen: %d", rc); LOG_ERR("Settings load failed: %d", rc);
} }
else else
{ {
LOG_INF("Settings erfolgreich aus dem Flash geladen."); LOG_INF("Settings successfully loaded from flash.");
} }
} }
LOG_INF("Starte UDP Sensor Node..."); LOG_INF("Starting UDP sensor node...");
k_sleep(K_SECONDS(1)); k_sleep(K_SECONDS(1));
LOG_INF("Warte auf OpenThread Attach..."); rc = radio_set_max_tx_power();
if (rc < 0)
{
return rc;
}
LOG_INF("Waiting for OpenThread attach...");
for (int i = 0; i < 15; i++) for (int i = 0; i < 15; i++)
{ {
if (thread_is_attached()) if (thread_is_attached())
{ {
was_connected = true; was_connected = true;
LOG_INF("OpenThread verbunden. UDP wird gestartet."); LOG_INF("OpenThread connected. UDP will start.");
break; break;
} }
@@ -404,13 +501,13 @@ int main(void)
if (!was_connected) if (!was_connected)
{ {
LOG_WRN("Noch nicht attached, starte im Pause-Modus und warte auf Re-Attach."); LOG_WRN("Not attached yet; starting in pause mode and waiting for re-attach.");
} }
sock = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); sock = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) if (sock < 0)
{ {
LOG_ERR("Fehler beim Erstellen des Sockets: %d", sock); LOG_ERR("Failed to create socket: %d", sock);
return -1; return -1;
} }
@@ -420,7 +517,7 @@ int main(void)
rc = zsock_inet_pton(AF_INET6, TARGET_IP, &dest_addr.sin6_addr); rc = zsock_inet_pton(AF_INET6, TARGET_IP, &dest_addr.sin6_addr);
if (rc != 1) if (rc != 1)
{ {
LOG_ERR("Ungueltige Ziel-IP: %s", TARGET_IP); LOG_ERR("Invalid destination IP: %s", TARGET_IP);
zsock_close(sock); zsock_close(sock);
return -EINVAL; return -EINVAL;
} }
@@ -428,33 +525,32 @@ int main(void)
struct sockaddr_in6 local_addr; struct sockaddr_in6 local_addr;
memset(&local_addr, 0, sizeof(local_addr)); memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin6_family = AF_INET6; local_addr.sin6_family = AF_INET6;
local_addr.sin6_port = htons(LISTEN_PORT); // Höre genau auf Port 6969! local_addr.sin6_port = htons(LISTEN_PORT);
local_addr.sin6_addr = in6addr_any; // Akzeptiere Pakete auf allen lokalen IPs local_addr.sin6_addr = in6addr_any;
rc = zsock_bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr)); rc = zsock_bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr));
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Binden des Sockets (bind): %d", errno); LOG_ERR("Failed to bind socket: %d", errno);
zsock_close(sock); zsock_close(sock);
return -1; return -1;
} }
LOG_INF("Socket erfolgreich an lokalen Port %d gebunden.", LISTEN_PORT);
LOG_INF("Ziel: [%s]:%d", TARGET_IP, TARGET_PORT); LOG_INF("Socket successfully bound to local port %d.", LISTEN_PORT);
LOG_INF("Target: [%s]:%d", TARGET_IP, TARGET_PORT);
hwinfo_get_device_id(header.device_id, sizeof(header.device_id)); hwinfo_get_device_id(header.device_id, sizeof(header.device_id));
LOG_INF("Device ID: %02x%02x%02x%02x%02x%02x%02x%02x", LOG_INF("Device ID: %02x%02x%02x%02x%02x%02x%02x%02x",
header.device_id[0], header.device_id[1], header.device_id[2], header.device_id[3], header.device_id[0], header.device_id[1], header.device_id[2], header.device_id[3],
header.device_id[4], header.device_id[5], header.device_id[6], header.device_id[7]); header.device_id[4], header.device_id[5], header.device_id[6], header.device_id[7]);
#ifdef FAST_TIMES
#ifdef FAST_TIMES
last_temp_send_time = -S_TO_MS(measure_interval_m); last_temp_send_time = -S_TO_MS(measure_interval_m);
last_battery_send_time = -S_TO_MS(battery_measure_interval_h); last_battery_send_time = -S_TO_MS(battery_measure_interval_h);
#else #else
last_temp_send_time = -M_TO_MS(measure_interval_m); last_temp_send_time = -M_TO_MS(measure_interval_m);
last_battery_send_time = -H_TO_MS(battery_measure_interval_h); last_battery_send_time = -H_TO_MS(battery_measure_interval_h);
#endif #endif
send_config(); send_config();
@@ -467,12 +563,12 @@ int main(void)
{ {
if (connected) if (connected)
{ {
LOG_INF("OpenThread wieder verbunden, sende weiter."); LOG_INF("OpenThread reconnected, continuing to send.");
send_config(); send_config();
} }
else else
{ {
LOG_WRN("OpenThread getrennt, Senden pausiert."); LOG_WRN("OpenThread disconnected, sending paused.");
} }
was_connected = connected; was_connected = connected;
@@ -484,46 +580,68 @@ int main(void)
continue; continue;
} }
// Temparaturpaket (Simulation mit die temp anstatt DS18B20) rc = sensor_power_init();
if (rc < 0)
{
k_sleep(K_SECONDS(2));
continue;
}
rc = sensor_sample_fetch(temp_dev); rc = sensor_sample_fetch(temp_dev);
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Abrufen der Temperaturdaten (%d)", rc); LOG_ERR("Error fetching temperature data (%d)", rc);
sensor_power_off();
} }
else else
{ {
rc = sensor_channel_get(temp_dev, SENSOR_CHAN_DIE_TEMP, &temp_val); rc = sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, &temp_val);
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen des Temperaturkanals (%d)", rc); LOG_ERR("Error reading temperature channel (%d)", rc);
sensor_power_off();
k_msleep(2000); k_msleep(2000);
continue; continue;
} }
temp_mcelsius = (uint32_t)(temp_val.val1 * 1000 + temp_val.val2 / 1000); // Umrechnung in Milligrad Celsius temp_mcelsius = temp_val.val1 * 1000 + temp_val.val2 / 1000;
temperature_mc = (uint32_t)temp_mcelsius;
LOG_DBG("Die-Temperatur: %u.%03u °C", temp_mcelsius / 1000, temp_mcelsius % 1000); if (filtered_temperature_mc == 0xFFFF)
{
filtered_temperature_mc = temperature_mc;
}
else
{
filtered_temperature_mc = (temperature_mc + (filtered_temperature_mc * 3)) / 4;
}
LOG_DBG("Temperature: %u.%03u C (filtered: %u.%03u C)",
temperature_mc / 1000, temperature_mc % 1000,
filtered_temperature_mc / 1000, filtered_temperature_mc % 1000);
#ifdef FAST_TIMES #ifdef FAST_TIMES
if (abs(temp_mcelsius - last_temp_send_mc) >= send_delta_mc || current_time - last_temp_send_time >= S_TO_MS(max_send_interval_m)) if (abs((int32_t)filtered_temperature_mc - last_temp_send_mc) >= send_delta_mc ||
{ current_time - last_temp_send_time >= S_TO_MS(max_send_interval_m))
#else #else
if (abs(temp_mcelsius - last_temp_send_mc) >= send_delta_mc || current_time - last_temp_send_time >= M_TO_MS(max_send_interval_m)) if (abs((int32_t)filtered_temperature_mc - last_temp_send_mc) >= send_delta_mc ||
{ current_time - last_temp_send_time >= M_TO_MS(max_send_interval_m))
#endif #endif
sys_put_be32(temp_mcelsius, &send_buf[0]); {
rc = send_frame(PAYLOAD_TYPE_TEMP, send_buf, sizeof(temp_mcelsius)); sys_put_be32(filtered_temperature_mc, &send_buf[0]);
rc = send_frame(PAYLOAD_TYPE_TEMP, send_buf, sizeof(filtered_temperature_mc));
if (rc >= 0) if (rc >= 0)
{ {
recv_udp(); recv_udp();
last_temp_send_mc = temp_mcelsius; last_temp_send_mc = (int32_t)filtered_temperature_mc;
last_temp_send_time = current_time; last_temp_send_time = current_time;
} }
} }
sensor_power_off();
} }
// Batteriepaket
#ifdef FAST_TIMES #ifdef FAST_TIMES
if (current_time - last_battery_send_time >= S_TO_MS(battery_measure_interval_h)) if (current_time - last_battery_send_time >= S_TO_MS(battery_measure_interval_h))
#else #else
@@ -535,24 +653,22 @@ int main(void)
rc = adc_read(adc_channel.dev, &sequence); rc = adc_read(adc_channel.dev, &sequence);
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler beim Lesen des ADC-Kanals (%d)", rc); LOG_ERR("Error reading ADC channel (%d)", rc);
break; break;
} }
else
{
battery_mv = adc_buffer; battery_mv = adc_buffer;
// Umrechnung des ADC-Werts in Millivolt (abhängig von der Referenzspannung und dem ADC-Auflösungsbereich)
rc = adc_raw_to_millivolts_dt(&adc_channel, &battery_mv); rc = adc_raw_to_millivolts_dt(&adc_channel, &battery_mv);
if (rc < 0) if (rc < 0)
{ {
LOG_ERR("Fehler bei der mV-Konvertierung (%d)\n", rc); LOG_ERR("Error in mV conversion (%d)", rc);
} }
else else
{ {
LOG_DBG("VDD Spannung: %d mV ", battery_mv); LOG_DBG("VDD voltage: %d mV", battery_mv);
} }
uint16_t battery_mv_u16 = (uint16_t)battery_mv; // Konvertierung in uint16_t, falls nötig uint16_t battery_mv_u16 = (uint16_t)battery_mv;
sys_put_be16(battery_mv_u16, &send_buf[0]); sys_put_be16(battery_mv_u16, &send_buf[0]);
rc = send_frame(PAYLOAD_TYPE_BATTERY, send_buf, sizeof(battery_mv_u16)); rc = send_frame(PAYLOAD_TYPE_BATTERY, send_buf, sizeof(battery_mv_u16));
@@ -561,7 +677,7 @@ int main(void)
last_battery_send_time = current_time; last_battery_send_time = current_time;
} }
} }
}
#ifdef FAST_TIMES #ifdef FAST_TIMES
k_sleep(K_SECONDS(measure_interval_m)); k_sleep(K_SECONDS(measure_interval_m));
#else #else

View File

@@ -144,9 +144,9 @@ class Controller:
node["config_shadow"]["max_interval"] = max_interval node["config_shadow"]["max_interval"] = max_interval
self.mqtt_client.publish_sensor_value(uuid, "meas_interval", meas_interval) self.mqtt_client.publish_sensor_value(uuid, "meas_interval", meas_interval)
self.mqtt_client.publish_sensor_value(uuid, "temp_delta", temp_delta / 100.0) self.mqtt_client.publish_sensor_value(uuid, "temp_delta", temp_delta / 1000.0)
self.mqtt_client.publish_sensor_value(uuid, "max_interval", max_interval) self.mqtt_client.publish_sensor_value(uuid, "max_interval", max_interval)
self.mqtt_client.publish_sensor_value(uuid, "batt_interval", batt_interval / 60.0) self.mqtt_client.publish_sensor_value(uuid, "batt_interval", batt_interval)
else: else:
self.logger.info(f"[{uuid}] Wert geparkt ({msg_type}, warte auf MQTT Sync)...") self.logger.info(f"[{uuid}] Wert geparkt ({msg_type}, warte auf MQTT Sync)...")

View File

@@ -115,7 +115,7 @@ class MQTTDiscovery:
"name": "Temperatur Delta-T [°C]", "name": "Temperatur Delta-T [°C]",
"state_topic": f"{self.sensor_topic}/{uuid}/temp_delta/state", "state_topic": f"{self.sensor_topic}/{uuid}/temp_delta/state",
"command_topic": f"{self.sensor_topic}/{uuid}/temp_delta/set", "command_topic": f"{self.sensor_topic}/{uuid}/temp_delta/set",
"min": 0.1, "max": 2.0, "step": 0.05, "min": 0.1, "max": 2.0, "step": 0.1,
"unit_of_measurement": "°C", "unit_of_measurement": "°C",
"unique_id": f"{uuid}_temp_delta", "unique_id": f"{uuid}_temp_delta",
"icon": "mdi:thermometer", "icon": "mdi:thermometer",