Compare commits

..

3 Commits

Author SHA1 Message Date
xlemmingx
55fbfaf1d5 set dev eui from MCU-ID (UID) of STM32WL 2025-06-05 09:30:42 +02:00
xlemmingx
bee5012e04 fix compiler warning 2025-05-28 11:23:42 +02:00
xlemmingx
b683a8a880 add gpio handling; add downlink handling 2025-05-28 11:22:54 +02:00
15 changed files with 268 additions and 120 deletions

10
.vscode/settings.json vendored
View File

@ -7,6 +7,12 @@
"log.h": "c", "log.h": "c",
"mlx90614.h": "c", "mlx90614.h": "c",
"sensor.h": "c", "sensor.h": "c",
"sht4x.h": "c" "sht4x.h": "c",
} "array": "c",
"string_view": "c",
"initializer_list": "c",
"stm32-pinctrl-common.h": "c",
"printk.h": "c"
},
"cmake.sourceDirectory": "C:/Users/xlemmingx/Documents/PlatformIO/Projects/embedded_raumsenor_lorawan/zephyr"
} }

50
src/gpio.c Normal file
View File

@ -0,0 +1,50 @@
#include <gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(gpio_relais);
#if !DT_NODE_EXISTS(DT_NODELABEL(load_switch))
#error "Overlay for power output node not properly defined."
#endif
static const struct gpio_dt_spec load_switch =
GPIO_DT_SPEC_GET_OR(DT_NODELABEL(load_switch), gpios, {0});
void init_gpio()
{
int err;
if (!gpio_is_ready_dt(&load_switch))
{
LOG_ERR("The load switch pin GPIO port is not ready.\n");
return 0;
}
LOG_INF("Initializing pin with inactive level.\n");
err = gpio_pin_configure_dt(&load_switch, GPIO_OUTPUT_INACTIVE);
if (err != 0)
{
LOG_ERR("Configuring GPIO pin failed: %d\n", err);
return 0;
}
/* Make sure relais is switched off */
set_relais_state(false);
}
void set_relais_state(bool state)
{
int err = gpio_pin_set_dt(&load_switch, state);
if (err != 0)
{
LOG_ERR("Setting GPIO pin level failed: %d\n", err);
}
}
uint8_t get_relais_state()
{
return (uint8_t)gpio_pin_get_dt(&load_switch);
}

7
src/gpio.h Normal file
View File

@ -0,0 +1,7 @@
#include <zephyr/kernel.h>
void init_gpio();
void set_relais_state(bool state);
uint8_t get_relais_state();

View File

@ -1,52 +1,81 @@
#include <zephyr/kernel.h>
#include <zephyr/lorawan/lorawan.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zephyr/random/random.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <lora/lorawan.h> #include <lora/lorawan.h>
#include <gpio.h>
/* #define SETTINGS_KEY "LORAWAN_DEV_NONCE"
TODO:
- DEV NONCE in non volatile memory and increase
- format packets
- send: check if still in network
- downlink callback
LOG_MODULE_REGISTER(lorawan_class_a);
// Build payload byte array extern bool relais_state;
uint8_t uplinkPayload[6]; const static struct device *lora_dev;
uplinkPayload[0] = uint8_t (int(temp_room_mlx)); static struct lorawan_join_config join_cfg;
uplinkPayload[1] = uint8_t (int((temp_room_mlx-int(temp_room_mlx)*100))); // See notes for high/lowByte functions static uint8_t dev_eui[8];
uplinkPayload[2] = uint8_t (int(temp_floor)); static uint8_t app_key[] = LORAWAN_APP_KEY;
uplinkPayload[3] = uint8_t (int((temp_floor-int(temp_floor)*100))); // See notes for high/lowByte functions static uint8_t join_eui[] = LORAWAN_APP_EUI;
uplinkPayload[4] = uint8_t (int(humidity));
uplinkPayload[5] = uint8_t (relaisstate); // See notes for high/lowByte functions
*/
const struct device *lora_dev; static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
struct lorawan_join_config join_cfg; const uint8_t *hex_data)
uint8_t dev_eui[] = LORAWAN_DEV_EUI; {
uint8_t join_eui[] = LORAWAN_JOIN_EUI; LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm, Len: %d, Time %d", port,
uint8_t app_key[] = LORAWAN_APP_KEY; flags & LORAWAN_DATA_PENDING, rssi, snr, len, !!(flags & LORAWAN_TIME_UPDATED));
if (hex_data)
{
LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
}
void init_lorawan() { /* Could be moved in a separate thread so we only have to set the variable here */
int ret; if (len > 3 && hex_data[0] == 'r' && hex_data[1] == '1')
{
uint8_t relais_value = hex_data[3];
LOG_INF("Received relais1 state change: %d", relais_value);
if (get_relais_state() != relais_value)
{
set_relais_state(relais_value);
LOG_INF("Set relais 1 value");
}
relais_state = relais_value;
}
}
struct lorawan_downlink_cb downlink_cb = { struct lorawan_downlink_cb downlink_cb = {
.port = LW_RECV_PORT_ANY, .port = LW_RECV_PORT_ANY,
.cb = dl_callback}; .cb = dl_callback};
void init_lorawan()
{
/* set dev eui from MCU-ID (UID) of STM32WL */
uint32_t uid_l = HAL_GetUIDw0();
uint32_t uid_h = HAL_GetUIDw1();
dev_eui[0] = (uid_h >> 24) & 0xFF;
dev_eui[1] = (uid_h >> 16) & 0xFF;
dev_eui[2] = (uid_h >> 8) & 0xFF;
dev_eui[3] = (uid_h) & 0xFF;
dev_eui[4] = (uid_l >> 24) & 0xFF;
dev_eui[5] = (uid_l >> 16) & 0xFF;
dev_eui[6] = (uid_l >> 8) & 0xFF;
dev_eui[7] = (uid_l) & 0xFF;
int err;
lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0)); lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
if (!device_is_ready(lora_dev)) if (!device_is_ready(lora_dev))
{ {
LOG_ERR("%s: device not ready.", lora_dev->name); LOG_ERR("%s: device not ready.", lora_dev->name);
return 0; return;
} }
ret = lorawan_start(); err = lorawan_start();
if (ret < 0) if (err < 0)
{ {
LOG_ERR("lorawan_start failed: %d", ret); LOG_ERR("lorawan_start failed: %d", err);
return 0; return;
} }
else else
{ {
@ -54,70 +83,65 @@ void init_lorawan() {
} }
lorawan_register_downlink_callback(&downlink_cb); lorawan_register_downlink_callback(&downlink_cb);
lorawan_register_dr_changed_callback(lorwan_datarate_changed);
join_cfg.mode = LORAWAN_ACT_OTAA; join_cfg.mode = LORAWAN_ACT_OTAA;
join_cfg.dev_eui = dev_eui; join_cfg.dev_eui = dev_eui;
join_cfg.otaa.join_eui = join_eui; join_cfg.otaa.join_eui = join_eui;
join_cfg.otaa.app_key = app_key; join_cfg.otaa.app_key = app_key;
join_cfg.otaa.nwk_key = app_key;
join_cfg.otaa.dev_nonce = 14u; // TODO
} }
void join_network_otaa() { void join_network_otaa()
int8_t status = -1; {
while (lorawan_join(&join_cfg) < 0) { int8_t ret = -1;
while (lorawan_join(&join_cfg) < 0)
{
LOG_ERR("lorawan_join_network failed: %d, retrying..", ret); LOG_ERR("lorawan_join_network failed: %d, retrying..", ret);
k_sleep(K_MSEC(5000)); k_sleep(K_MSEC(5000));
} }
LOG_INF("Joining network over OTAA"); LOG_INF("Succesfully joined network over OTAA");
} }
void send_data_packet(char *data) { void send_data_packet(char *data, uint8_t data_len)
{
LOG_INF("Sending data..."); LOG_INF("Sending data...");
while (1) while (1)
{ {
int8_t ret = lorawan_send(LORAWAN_PORT, data, sizeof(data), LORAWAN_MSG_CONFIRMED); int8_t ret = lorawan_send(LORAWAN_PORT, (uint8_t *)data, data_len, LORAWAN_MSG_UNCONFIRMED);
if (ret == -EAGAIN) if (ret == -EAGAIN)
{ {
LOG_ERR("lorawan_send failed: %d. Continuing...", ret); LOG_ERR("lorawan_send failed: %d. Continuing...", ret);
k_sleep(DELAY); k_sleep(RETRY_DELAY);
continue; continue;
} }
else if (ret < 0)
if (ret < 0)
{ {
LOG_ERR("lorawan_send failed: %d", ret); LOG_ERR("lorawan_send failed: %d", ret);
k_sleep(DELAY); k_sleep(RETRY_DELAY);
continue; break;
} }
else else
{ {
LOG_INF("Data sent!"); LOG_INF("Data sent!");
break;
} }
k_sleep(DELAY); k_sleep(RETRY_DELAY);
} }
} }
void create_data_package(char *buffer, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state)
static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
const uint8_t *hex_data)
{ {
LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm, Time %d", port, int16_t temp = (int16_t)(temp_room_mlx * 100);
flags & LORAWAN_DATA_PENDING, rssi, snr, !!(flags & LORAWAN_TIME_UPDATED)); int16_t floor_temp = (int16_t)(temp_floor * 100);
if (hex_data) int16_t hum = (int16_t)(humidity * 100);
{
LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
}
}
static void lorwan_datarate_changed(enum lorawan_datarate dr) // Packen der Daten in das Byte-Array
{ buffer[0] = (temp >> 8) & 0xFF;
uint8_t unused, max_size; buffer[1] = temp & 0xFF;
buffer[2] = (floor_temp >> 8) & 0xFF;
lorawan_get_payload_sizes(&unused, &max_size); buffer[3] = floor_temp & 0xFF;
LOG_INF("New Datarate: DR_%d, Max Payload %d", dr, max_size); buffer[4] = (hum >> 8) & 0xFF;
buffer[5] = hum & 0xFF;
buffer[6] = relais_state;
} }

View File

@ -1,23 +1,22 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/lorawan/lorawan.h> #include <zephyr/lorawan/lorawan.h>
#define LORAWAN_PORT 2 #define LORAWAN_PORT 2
/* Customize based on network configuration */ /* Customize based on network configuration */
#define LORAWAN_DEV_EUI { 0x2D, 0xBF, 0xF6, 0xC2, 0xA1, 0x20,\ #define LORAWAN_APP_KEY {0x71, 0x5A, 0x39, 0xB2, 0x86, 0xC3, \
0x01, 0x4A } 0x37, 0xA3, 0xC4, 0xF0, 0x78, 0xF9, \
#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ 0x0F, 0x33, 0x07, 0x7D}
0x00, 0x00 } #define LORAWAN_APP_EUI {0x1F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, \
/* #define LORAWAN_APP_KEY { 0x71, 0x5A, 0x39, 0xB2, 0x86, 0xC3,\ 0x0F, 0x0F}
0x37, 0xA3, 0xC4, 0xF0, 0x78, 0xF9,\
0x0F, 0x33, 0x07, 0x7D } */
#define LORAWAN_APP_KEY { 0x71, 0x5A, 0x39, 0xB2, 0x86, 0xC3,\
0x37, 0xA3, 0xC4, 0xF0, 0x78, 0xF9,\
0x0F, 0x33, 0x07, 0x7D }
#define RETRY_DELAY K_MSEC(1000)
#define SEND_INTERVALL K_MINUTES(5)
static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len, void init_lorawan();
const uint8_t *hex_data);
static void lorwan_datarate_changed(enum lorawan_datarate dr); void join_network_otaa();
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);

View File

@ -2,28 +2,54 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
#include <zephyr/lorawan/lorawan.h> #include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/sensor/sht4x.h> #include <zephyr/drivers/sensor/sht4x.h>
#include <zephyr/drivers/gpio.h>
#include <lora/lorawan.h> #include <lora/lorawan.h>
#include <sensors/mlx90614.h> #include <sensors/mlx90614.h>
#include <sensors/sht4x.h> #include <sensors/sht4x.h>
#include <gpio.h>
LOG_MODULE_REGISTER(g2h_heat_main);
volatile bool relais_state = false;
#define DELAY K_MSEC(10000)
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
LOG_MODULE_REGISTER(lorawan_class_a);
/* MAIN */ /* MAIN */
int main(void) int main(void)
{ {
/* Initialization */
init_gpio();
init_mlx90614();
init_sht4x();
init_lorawan();
join_network_otaa();
float humidity, temp, floor_temp;
while (true)
{
/* Get values from SHT4X */
request_sensor_data_sht4x();
humidity = get_value_from_current_sample_sht4x(SENSOR_CHAN_HUMIDITY);
printk("hum: %.2f%%\n", humidity);
/* Get values from MLX90614 */
request_sensor_data_mlx90614(MLX90614_INTERNAL_TEMP_ADDR);
temp = get_temp_from_raw_data_mlx90614();
printk("temp: %.2f °C\n", temp);
request_sensor_data_mlx90614(MLX90614_IR_TEMP_ADDR);
floor_temp = get_temp_from_raw_data_mlx90614();
printk("floor_temp: %.2f °C\n", floor_temp);
/* 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);
/* Delay until next cycle */
k_sleep(SEND_INTERVALL);
}
} }

View File

@ -3,13 +3,16 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h> #include <zephyr/drivers/i2c.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
static struct device *i2c_dev; LOG_MODULE_REGISTER(mlx90614);
static uint16_t i2c_addr = 0x00;
void init_mlx90614() { static const struct device *i2c_dev;
static const uint16_t i2c_addr = 0x5A;
static uint8_t read_data_buffer[3];
int init_mlx90614() {
i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c1)); i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c1));
i2c_addr = DT_PROP(DT_NODELABEL(mlx90614), reg);
if (!i2c_dev) { if (!i2c_dev) {
LOG_ERR("I2C device not found!"); LOG_ERR("I2C device not found!");
@ -17,27 +20,27 @@ void init_mlx90614() {
} else { } else {
LOG_INF("I2C device initialized."); LOG_INF("I2C device initialized.");
} }
if (i2c_addr == 0x00) {
LOG_ERR("MLX90614 I2C address fetch from device tree failed!");
} else {
LOG_INF("MLX90614 I2C address found.");
}
} }
void request_sensor_data_mlx90614(uint16_t target_addr, const uint8_t *read_data) { void request_sensor_data_mlx90614(uint8_t target_addr) {
for (int i = 0; i < MLX90614_RETRY_COUNT; i++) { uint8_t err;
if (i2c_write_read(i2c_dev, i2c_addr, &target_addr, sizeof(target_addr), &read_data, sizeof(read_data) == 0)) { for (int i = 0; i < MLX90614_RETRY_COUNT; i++)
{
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); LOG_INF("MLX90614 sensor values successfully read after %d tries", i+1);
return; return;
} else { }
else
{
k_sleep(K_MSEC(100)); k_sleep(K_MSEC(100));
} }
} }
LOG_ERR("MLX90614 reading sensor values failed %d times!", MLX90614_RETRY_COUNT); LOG_ERR("MLX90614 reading sensor values failed %d times with error %d!", MLX90614_RETRY_COUNT, err);
} }
float get_temp_from_raw_data_mlx90614(const uint8_t *read_data) { float get_temp_from_raw_data_mlx90614() {
int16_t temperature = (read_data[1] << 8) | read_data[0]; // Temperaturwert aus den empfangenen Daten int16_t temperature = (read_data_buffer[1] << 8) | read_data_buffer[0]; // Temperaturwert aus den empfangenen Daten
return temperature * 0.02 - 273.15; // Umrechnung in Celsius return temperature * 0.02 - 273.15; // Umrechnung in Celsius
} }

View File

@ -6,6 +6,6 @@
int init_mlx90614(); int init_mlx90614();
int request_sensor_data_mlx90614(uint16_t target_addr, const uint8_t *read_data); void request_sensor_data_mlx90614(uint8_t target_addr);
float get_temp_from_raw_data_mlx90614(const uint8_t *read_data); float get_temp_from_raw_data_mlx90614();

View File

@ -5,7 +5,9 @@
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/sensor/sht4x.h> #include <zephyr/drivers/sensor/sht4x.h>
static struct device *sht; LOG_MODULE_REGISTER(sht4x);
static const struct device *sht;
void init_sht4x() void init_sht4x()
{ {
@ -46,10 +48,10 @@ float get_value_from_current_sample_sht4x(enum sensor_channel sensor)
float result; float result;
if (sensor_channel_get(sht, sensor, &value) == 0) { if (sensor_channel_get(sht, sensor, &value) == 0) {
result = sensor_value_to_double(&value); result = sensor_value_to_double(&value);
LOG_INF("Value '%s' successfully read [%f]", sensor, result); printf("Value '%s' successfully read [%f]", sensor, result);
} else { } else {
LOG_ERR("Can't read sensor value '%s' from sample!", sensor); LOG_ERR("Can't read sensor value '%s' from sample!", sensor);
return 0.0; return 0.0;
} }
return value; return result;
} }

View File

@ -6,4 +6,4 @@ void init_sht4x();
void request_sensor_data_sht4x(); void request_sensor_data_sht4x();
float get_value_from_raw_data_sht4x(enum sensor_channel sensor); float get_value_from_current_sample_sht4x(enum sensor_channel sensor);

View File

@ -10,6 +10,7 @@
#include "arduino_r3_connector.dtsi" #include "arduino_r3_connector.dtsi"
#include "st_morpho_connector.dtsi" #include "st_morpho_connector.dtsi"
#include <zephyr/dt-bindings/input/input-event-codes.h> #include <zephyr/dt-bindings/input/input-event-codes.h>
//#include "g2h_lorawan_heat_control.dtsi"
/ { / {
model = "G2H LoraWAN Heat control based on STM32WL55JC-NUCLEO"; model = "G2H LoraWAN Heat control based on STM32WL55JC-NUCLEO";

View File

@ -0,0 +1,8 @@
#include <zephyr/dt-bindings/pinctrl/stm32-pinctrl.h>
#include <st/wl/stm32wl55jcix-pinctrl.dtsi>
&pinctrl {
gpio_pc0: gpio_pc0 {
pinmux = <STM32_PINMUX(STM32_PORTC, 0, STM32_GPIO)>;
};
};

View File

@ -0,0 +1,10 @@
description: GPIO pin to switch a power output on or off
compatible: "power-switch"
properties:
gpios:
type: phandle-array
required: true
description: |
The GPIO connected to the gate driver for the MOSFET.

View File

@ -0,0 +1,6 @@
/ {
load_switch: load_switch {
compatible = "power-switch";
gpios = <&gpioc 0 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -1,12 +1,18 @@
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_SENSOR=y CONFIG_SENSOR=y
CONFIG_GPIO=y
CONFIG_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_ZMS=y
CONFIG_I2C=y CONFIG_I2C=y
CONFIG_SHT4X=y CONFIG_SHT4X=y
CONFIG_LORA=y CONFIG_LORA=y
CONFIG_LORAWAN=y CONFIG_LORAWAN=y
CONFIG_LORAMAC_REGION_EU868=y CONFIG_LORAMAC_REGION_EU868=y
CONFIG_MAIN_STACK_SIZE=2048 CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_ENTROPY_GENERATOR=y