add gpio handling; add downlink handling

This commit is contained in:
xlemmingx 2025-05-28 11:22:54 +02:00
parent 607a089510
commit b683a8a880
15 changed files with 251 additions and 120 deletions

10
.vscode/settings.json vendored
View File

@ -7,6 +7,12 @@
"log.h": "c",
"mlx90614.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,39 +1,52 @@
#include <zephyr/kernel.h>
#include <zephyr/lorawan/lorawan.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 <gpio.h>
/*
TODO:
- DEV NONCE in non volatile memory and increase
- format packets
- send: check if still in network
- downlink callback
#define SETTINGS_KEY "LORAWAN_DEV_NONCE"
LOG_MODULE_REGISTER(lorawan_class_a);
// Build payload byte array
uint8_t uplinkPayload[6];
uplinkPayload[0] = uint8_t (int(temp_room_mlx));
uplinkPayload[1] = uint8_t (int((temp_room_mlx-int(temp_room_mlx)*100))); // See notes for high/lowByte functions
uplinkPayload[2] = uint8_t (int(temp_floor));
uplinkPayload[3] = uint8_t (int((temp_floor-int(temp_floor)*100))); // See notes for high/lowByte functions
uplinkPayload[4] = uint8_t (int(humidity));
uplinkPayload[5] = uint8_t (relaisstate); // See notes for high/lowByte functions
*/
extern bool relais_state;
const static struct device *lora_dev;
static struct lorawan_join_config join_cfg;
static uint8_t app_key[] = LORAWAN_APP_KEY;
const struct device *lora_dev;
struct lorawan_join_config join_cfg;
uint8_t dev_eui[] = LORAWAN_DEV_EUI;
uint8_t join_eui[] = LORAWAN_JOIN_EUI;
uint8_t app_key[] = LORAWAN_APP_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)
{
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)
{
LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
}
void init_lorawan() {
int ret;
/* 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')
{
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 = {
.port = LW_RECV_PORT_ANY,
.cb = dl_callback};
struct lorawan_downlink_cb downlink_cb = {
.port = LW_RECV_PORT_ANY,
.cb = dl_callback};
void init_lorawan()
{
int err;
lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
if (!device_is_ready(lora_dev))
@ -42,10 +55,10 @@ void init_lorawan() {
return 0;
}
ret = lorawan_start();
if (ret < 0)
err = lorawan_start();
if (err < 0)
{
LOG_ERR("lorawan_start failed: %d", ret);
LOG_ERR("lorawan_start failed: %d", err);
return 0;
}
else
@ -54,70 +67,66 @@ void init_lorawan() {
}
lorawan_register_downlink_callback(&downlink_cb);
lorawan_register_dr_changed_callback(lorwan_datarate_changed);
join_cfg.mode = LORAWAN_ACT_OTAA;
join_cfg.dev_eui = dev_eui;
join_cfg.otaa.join_eui = join_eui;
join_cfg.otaa.app_key = app_key;
join_cfg.otaa.nwk_key = app_key;
join_cfg.otaa.dev_nonce = 14u; // TODO
join_cfg.otaa.dev_nonce = sys_rand16_get();
}
void join_network_otaa() {
int8_t status = -1;
while (lorawan_join(&join_cfg) < 0) {
void join_network_otaa()
{
int8_t ret = -1;
while (lorawan_join(&join_cfg) < 0)
{
LOG_ERR("lorawan_join_network failed: %d, retrying..", ret);
// use another dev nonce
join_cfg.otaa.dev_nonce = sys_rand16_get();
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...");
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)
{
LOG_ERR("lorawan_send failed: %d. Continuing...", ret);
k_sleep(DELAY);
k_sleep(RETRY_DELAY);
continue;
}
if (ret < 0)
else if (ret < 0)
{
LOG_ERR("lorawan_send failed: %d", ret);
k_sleep(DELAY);
continue;
k_sleep(RETRY_DELAY);
break;
}
else
{
LOG_INF("Data sent!");
break;
}
k_sleep(DELAY);
k_sleep(RETRY_DELAY);
}
}
static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
const uint8_t *hex_data)
void create_data_package(char *buffer, float temp_room_mlx, float temp_floor, float humidity, uint8_t relais_state)
{
LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm, Time %d", port,
flags & LORAWAN_DATA_PENDING, rssi, snr, !!(flags & LORAWAN_TIME_UPDATED));
if (hex_data)
{
LOG_HEXDUMP_INF(hex_data, len, "Payload: ");
}
}
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);
static void lorwan_datarate_changed(enum lorawan_datarate dr)
{
uint8_t unused, max_size;
lorawan_get_payload_sizes(&unused, &max_size);
LOG_INF("New Datarate: DR_%d, Max Payload %d", dr, max_size);
// Packen der Daten in das Byte-Array
buffer[0] = (temp >> 8) & 0xFF;
buffer[1] = temp & 0xFF;
buffer[2] = (floor_temp >> 8) & 0xFF;
buffer[3] = floor_temp & 0xFF;
buffer[4] = (hum >> 8) & 0xFF;
buffer[5] = hum & 0xFF;
buffer[6] = relais_state;
}

View File

@ -1,23 +1,20 @@
#include <zephyr/kernel.h>
#include <zephyr/lorawan/lorawan.h>
#define LORAWAN_PORT 2
/* Customize based on network configuration */
#define LORAWAN_DEV_EUI { 0x2D, 0xBF, 0xF6, 0xC2, 0xA1, 0x20,\
0x01, 0x4A }
#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
0x00, 0x00 }
/* #define LORAWAN_APP_KEY { 0x71, 0x5A, 0x39, 0xB2, 0x86, 0xC3,\
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 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_SECONDS(20)
static void dl_callback(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
const uint8_t *hex_data);
void init_lorawan();
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/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/lorawan/lorawan.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/sensor/sht4x.h>
#include <zephyr/drivers/gpio.h>
#include <lora/lorawan.h>
#include <sensors/mlx90614.h>
#include <sensors/sht4x.h>
#include <gpio.h>
LOG_MODULE_REGISTER(g2h_heat_main);
#define DELAY K_MSEC(10000)
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
LOG_MODULE_REGISTER(lorawan_class_a);
volatile bool relais_state = false;
/* MAIN */
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/drivers/i2c.h>
#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
static struct device *i2c_dev;
static uint16_t i2c_addr = 0x00;
LOG_MODULE_REGISTER(mlx90614);
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_addr = DT_PROP(DT_NODELABEL(mlx90614), reg);
if (!i2c_dev) {
LOG_ERR("I2C device not found!");
@ -17,27 +20,27 @@ void init_mlx90614() {
} else {
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) {
for (int i = 0; i < MLX90614_RETRY_COUNT; i++) {
if (i2c_write_read(i2c_dev, i2c_addr, &target_addr, sizeof(target_addr), &read_data, sizeof(read_data) == 0)) {
void request_sensor_data_mlx90614(uint8_t target_addr) {
uint8_t err;
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);
return;
} else {
}
else
{
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) {
int16_t temperature = (read_data[1] << 8) | read_data[0]; // Temperaturwert aus den empfangenen Daten
return temperature * 0.02 - 273.15; // Umrechnung in Celsius
float get_temp_from_raw_data_mlx90614() {
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
}

View File

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

View File

@ -6,4 +6,4 @@ void init_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 "st_morpho_connector.dtsi"
#include <zephyr/dt-bindings/input/input-event-codes.h>
//#include "g2h_lorawan_heat_control.dtsi"
/ {
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_SENSOR=y
CONFIG_GPIO=y
CONFIG_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_ZMS=y
CONFIG_I2C=y
CONFIG_SHT4X=y
CONFIG_LORA=y
CONFIG_LORAWAN=y
CONFIG_LORAMAC_REGION_EU868=y
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_ENTROPY_GENERATOR=y