From 5d3b0caf93076bfd275e3a0da4477878d2aa9b69 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 12 Apr 2026 22:26:16 +0100 Subject: [PATCH] first commit --- app.overlay | 42 ++++ prj.conf | 163 +++++++++++++ src/config.h | 67 ++++++ src/main.c | 609 +++++++++++++++++++++++++++++++++++++++++++++++ src/test_certs.h | 145 +++++++++++ 5 files changed, 1026 insertions(+) create mode 100644 app.overlay create mode 100644 prj.conf create mode 100644 src/config.h create mode 100644 src/main.c create mode 100644 src/test_certs.h diff --git a/app.overlay b/app.overlay new file mode 100644 index 0000000..86eca7d --- /dev/null +++ b/app.overlay @@ -0,0 +1,42 @@ +/ { + chosen { + zephyr,console = &cdc_acm_uart0; + zephyr,shell-uart = &cdc_acm_uart0; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserve last 64KB for settings storage */ + storage_partition: partition@3f0000 { + label = "storage"; + reg = <0x003f0000 0x00010000>; + }; + }; +}; + +/ { + aliases { + sw0 = &door_sensor; + }; + + gpio_keys { + compatible = "gpio-keys"; + + door_sensor: door_sensor { + gpios = <&gpio0 22 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Door Reed Switch"; + }; + }; +}; \ No newline at end of file diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..ef6fce8 --- /dev/null +++ b/prj.conf @@ -0,0 +1,163 @@ +CONFIG_NETWORKING=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_TCP=y +CONFIG_NET_LOG=y + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 + +CONFIG_PRINTK=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_POSIX_API=y + +# Enable IPv6 support +CONFIG_NET_IPV6=n +# Enable IPv4 support +CONFIG_NET_IPV4=y + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.178.109" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.168.178.40" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_BUF_DATA_SIZE=256 + +CONFIG_NET_SHELL=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y + +# Enable settings subsystem for persistent storage +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y + +# Enable WiFi credentials storage +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_BACKEND_SETTINGS=y + +################### +CONFIG_EARLY_CONSOLE=y + +CONFIG_NETWORKING=y +CONFIG_TEST_RANDOM_GENERATOR=y + +CONFIG_MAIN_STACK_SIZE=5200 +CONFIG_SHELL_STACK_SIZE=5200 +CONFIG_NET_TX_STACK_SIZE=2048 +CONFIG_NET_RX_STACK_SIZE=2048 + +CONFIG_NET_PKT_RX_COUNT=10 +CONFIG_NET_PKT_TX_COUNT=10 +CONFIG_NET_BUF_RX_COUNT=20 +CONFIG_NET_BUF_TX_COUNT=20 +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_DHCPV4=y + +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n + +CONFIG_NET_TCP=y + +CONFIG_NET_LOG=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_SHELL=y + +CONFIG_NET_STATISTICS=y +CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n + +CONFIG_WIFI=y +CONFIG_WIFI_LOG_LEVEL_ERR=y +CONFIG_NET_L2_WIFI_SHELL=y +# printing of scan results puts pressure on queues in new locking +# design in net_mgmt. So, use a higher timeout for a crowded +# environment. +CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 + +# Enable USB CDC ACM for console +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_CDC_ACM=y +CONFIG_UART_LINE_CTRL=y + +# Use USB for console instead of UART +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y +CONFIG_SHELL_BACKEND_SERIAL_CHECK_DTR=y +#CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM_0" + +#CONFIG_WIFI_INFINEON_AIROC=y +CONFIG_NET_L2_WIFI_MGMT=y + + +# Enable settings subsystem for persistent storage +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y +CONFIG_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y + +# Enable WiFi credentials storage +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_BACKEND_SETTINGS=y + +# Optional: Auto-connect on boot +CONFIG_NET_CONFIG_AUTO_INIT=y +CONFIG_NET_CONFIG_NEED_IPV4=y + +# MPU settings for flash access +CONFIG_MPU_ALLOW_FLASH_WRITE=y + +# Auto-connect to saved WiFi on boot +#CONFIG_NET_CONNECTION_MANAGER=y +#CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=1024 +#CONFIG_NET_MGMT_EVENT_STACK_SIZE=1024 + +# WEBSOCKETS +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y + +# Networking HTTP POST +#CONFIG_NET_SOCKETS=y +#CONFIG_NET_SOCKETS_POSIX_NAMES=y +CONFIG_NET_TCP=y +#CONFIG_NET_HTTP_CLIENT=y +CONFIG_DNS_RESOLVER=y +CONFIG_NET_DHCPV4=y +CONFIG_HTTP_CLIENT=y + +# Shell (if you want to keep the shell) +#CONFIG_SHELL=y +#CONFIG_SHELL_BACKEND_SERIAL=y + +# USB CDC +#CONFIG_USB_DEVICE_STACK=y +#CONFIG_USB_CDC_ACM=y +#CONFIG_UART_LINE_CTRL=y +#CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y + +# buttons +CONFIG_GPIO=y +CONFIG_USB_DEVICE_PRODUCT="Pico Zephyr Device" +CONFIG_USB_DEVICE_PID=0x0001 +CONFIG_USB_DEVICE_VID=0x2FE3 +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_SHELL=y +CONFIG_SHELL_BACKEND_SERIAL=y + diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..026fb47 --- /dev/null +++ b/src/config.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#ifdef CONFIG_NET_CONFIG_SETTINGS +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV6_ADDR +#define SERVER_ADDR CONFIG_NET_CONFIG_PEER_IPV6_ADDR +#else +#define ZEPHYR_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR +#define SERVER_ADDR CONFIG_NET_CONFIG_PEER_IPV4_ADDR +#endif +#else +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR "2001:db8::1" +#define SERVER_ADDR "2001:db8::2" +#else +#define ZEPHYR_ADDR "192.168.178.109" +#define SERVER_ADDR "192.168.1.40" +#endif +#endif + +#if defined(CONFIG_SOCKS) +#define SOCKS5_PROXY_ADDR SERVER_ADDR +#define SOCKS5_PROXY_PORT 1080 +#endif + +#ifdef CONFIG_MQTT_LIB_TLS +#ifdef CONFIG_MQTT_LIB_WEBSOCKET +#define SERVER_PORT 9001 +#else +#define SERVER_PORT 8883 +#endif /* CONFIG_MQTT_LIB_WEBSOCKET */ +#else +#ifdef CONFIG_MQTT_LIB_WEBSOCKET +#define SERVER_PORT 9001 +#else +#define SERVER_PORT 1883 +#endif /* CONFIG_MQTT_LIB_WEBSOCKET */ +#endif + +#define APP_CONNECT_TIMEOUT_MS 2000 +#define APP_SLEEP_MSECS 500 + +#define APP_CONNECT_TRIES 10 + +#define APP_MQTT_BUFFER_SIZE 128 + +#define MQTT_CLIENTID "zephyr_publisher" + +/* Set the following to 1 to enable the Bluemix topic format */ +#define APP_BLUEMIX_TOPIC 0 + +/* These are the parameters for the Bluemix topic format */ +#if APP_BLUEMIX_TOPIC +#define BLUEMIX_DEVTYPE "sensor" +#define BLUEMIX_DEVID "carbon" +#define BLUEMIX_EVENT "status" +#define BLUEMIX_FORMAT "json" +#endif + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4013435 --- /dev/null +++ b/src/main.c @@ -0,0 +1,609 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(door_sensor, LOG_LEVEL_INF); + +/* ========== Configuration ========== */ +#define MQTT_BROKER_ADDR "78.141.235.43" +#define MQTT_BROKER_PORT 1883 +#define MQTT_CLIENT_ID "pico2w_sensor" +#define MQTT_TOPIC_STATUS "sensors/motion" +#define MQTT_TOPIC_ALERT "alert" + +// Power-saving configuration +#define STATUS_INTERVAL_MS (600000) // Send status every 600 seconds +#define WIFI_TIMEOUT_MS (10000) // WiFi connection timeout +#define MQTT_TIMEOUT_MS (5000) // MQTT connection timeout +#define CONNECTION_KEEP_ALIVE_MS (500) // REDUCED: Keep connection for batching +#define MAX_RETRY_ATTEMPTS 3 + +// Debounce configuration +#define DEBOUNCE_DELAY_MS (50) // Debounce time +#define EVENT_QUEUE_SIZE 10 // Queue for events + +/* ========== Door Sensor GPIO ========== */ +#define SW0_NODE DT_ALIAS(sw0) +#if !DT_NODE_HAS_STATUS_OKAY(SW0_NODE) +#error "Unsupported board: sw0 devicetree alias is not defined" +#endif + +static const struct gpio_dt_spec door_sensor = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0}); +static struct gpio_callback door_cb_data; + +/* ========== WiFi Credentials ========== */ +struct wifi_credentials_personal creds = { + .header = { + .ssid = "fuck off", + .ssid_len = 8, + .type = WIFI_SECURITY_TYPE_PSK, + }, + .password = "09701067356094858066", + .password_len = 20, +}; + +/* ========== MQTT Buffers and Context ========== */ +static uint8_t rx_buffer[256]; +static uint8_t tx_buffer[256]; +static struct mqtt_client client; +static struct sockaddr_storage broker; +static struct pollfd fds; + +/* ========== Event Queue Structure ========== */ +struct door_event { + bool door_open; + int64_t timestamp; +}; + +static struct { + struct door_event events[EVENT_QUEUE_SIZE]; + size_t head; + size_t tail; + size_t count; + struct k_mutex mutex; +} event_queue; + +/* ========== State Management ========== */ +static struct { + bool wifi_connected; + bool mqtt_connected; + bool door_open; + int64_t last_door_change; + int64_t last_status_time; + char device_serial[32]; +} state = {0}; + +/* ========== Debounce Work ========== */ +static struct k_work_delayable debounce_work; + +/* ========== Event Queue Functions ========== */ +static void event_queue_init(void) +{ + k_mutex_init(&event_queue.mutex); + event_queue.head = 0; + event_queue.tail = 0; + event_queue.count = 0; +} + +static bool event_queue_push(bool door_open, int64_t timestamp) +{ + k_mutex_lock(&event_queue.mutex, K_FOREVER); + + if (event_queue.count >= EVENT_QUEUE_SIZE) { + LOG_WRN("Event queue full, dropping oldest event"); + event_queue.tail = (event_queue.tail + 1) % EVENT_QUEUE_SIZE; + event_queue.count--; + } + + event_queue.events[event_queue.head].door_open = door_open; + event_queue.events[event_queue.head].timestamp = timestamp; + event_queue.head = (event_queue.head + 1) % EVENT_QUEUE_SIZE; + event_queue.count++; + + k_mutex_unlock(&event_queue.mutex); + return true; +} + +static bool event_queue_pop(struct door_event *event) +{ + k_mutex_lock(&event_queue.mutex, K_FOREVER); + + if (event_queue.count == 0) { + k_mutex_unlock(&event_queue.mutex); + return false; + } + + *event = event_queue.events[event_queue.tail]; + event_queue.tail = (event_queue.tail + 1) % EVENT_QUEUE_SIZE; + event_queue.count--; + + k_mutex_unlock(&event_queue.mutex); + return true; +} + +static size_t event_queue_size(void) +{ + size_t count; + k_mutex_lock(&event_queue.mutex, K_FOREVER); + count = event_queue.count; + k_mutex_unlock(&event_queue.mutex); + return count; +} + +/* ========== WiFi Event Callback ========== */ +static struct net_mgmt_event_callback wifi_mgmt_cb; + +static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, + uint64_t mgmt_event, struct net_if *iface) +{ + switch (mgmt_event) { + case NET_EVENT_WIFI_CONNECT_RESULT: + LOG_INF("WiFi connected"); + state.wifi_connected = true; + break; + case NET_EVENT_WIFI_DISCONNECT_RESULT: + LOG_INF("WiFi disconnected"); + state.wifi_connected = false; + state.mqtt_connected = false; + break; + default: + break; + } +} + +/* ========== Device Serial/ID ========== */ +static void get_device_serial(char *serial_buf, size_t len) +{ + uint32_t id = sys_rand32_get(); + snprintf(serial_buf, len, "PICO2W_%08X", id); +} + +/* ========== MQTT Event Handler ========== */ +void mqtt_evt_handler(struct mqtt_client *const cli, const struct mqtt_evt *evt) +{ + switch (evt->type) { + case MQTT_EVT_CONNACK: + if (evt->result == 0) { + state.mqtt_connected = true; + LOG_INF("MQTT connected!"); + } else { + LOG_ERR("MQTT connection failed: %d", evt->result); + state.mqtt_connected = false; + } + break; + + case MQTT_EVT_DISCONNECT: + LOG_DBG("MQTT disconnected"); + state.mqtt_connected = false; + break; + + case MQTT_EVT_PUBACK: + LOG_DBG("Message published (ID: %u)", evt->param.puback.message_id); + break; + + default: + break; + } +} + +/* ========== Debounce Work Handler ========== */ +static void debounce_work_fn(struct k_work *work) +{ + int current_state = gpio_pin_get_dt(&door_sensor); + bool door_is_open = (current_state == 1); + + // Only queue if state actually changed + if (door_is_open != state.door_open) { + state.door_open = door_is_open; + state.last_door_change = k_uptime_get(); + + event_queue_push(door_is_open, state.last_door_change); + LOG_INF("Door %s (debounced) - queued for sending", + door_is_open ? "OPENED" : "CLOSED"); + } +} + +/* ========== Door Sensor Interrupt ========== */ +void door_state_changed_handler(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + // Reschedule debounce - resets timer on each interrupt + k_work_reschedule(&debounce_work, K_MSEC(DEBOUNCE_DELAY_MS)); +} + +/* ========== WiFi Connection Management ========== */ +static int wifi_connect(void) +{ + int ret; + struct net_if *iface; + + if (state.wifi_connected) { + LOG_DBG("WiFi already connected"); + return 0; + } + + LOG_INF("Connecting to WiFi..."); + + net_mgmt_init_event_callback(&wifi_mgmt_cb, wifi_mgmt_event_handler, + NET_EVENT_WIFI_CONNECT_RESULT | + NET_EVENT_WIFI_DISCONNECT_RESULT); + net_mgmt_add_event_callback(&wifi_mgmt_cb); + + ret = wifi_credentials_set_personal_struct(&creds); + if (ret < 0) { + LOG_ERR("Failed to store WiFi credentials: %d", ret); + return ret; + } + + iface = net_if_get_default(); + if (!iface) { + LOG_ERR("No network interface found"); + return -ENODEV; + } + + ret = net_mgmt(NET_REQUEST_WIFI_CONNECT_STORED, iface, NULL, 0); + if (ret < 0 && ret != -EALREADY) { + LOG_ERR("WiFi connection request failed: %d", ret); + return ret; + } + + int64_t timeout = k_uptime_get() + WIFI_TIMEOUT_MS; + while (!state.wifi_connected && k_uptime_get() < timeout) { + k_sleep(K_MSEC(100)); + } + + if (!state.wifi_connected) { + LOG_ERR("WiFi connection timeout"); + return -ETIMEDOUT; + } + + k_sleep(K_MSEC(1000)); + LOG_INF("WiFi connected successfully"); + + return 0; +} + +static void wifi_disconnect(void) +{ + struct net_if *iface; + + if (!state.wifi_connected) { + return; + } + + LOG_INF("Disconnecting WiFi..."); + + iface = net_if_get_default(); + if (iface) { + net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0); + } + + state.wifi_connected = false; + state.mqtt_connected = false; +} + +/* ========== MQTT Connection Management ========== */ +static void mqtt_setup(void) +{ + struct sockaddr_in *broker4 = (struct sockaddr_in *)&broker; + + mqtt_client_init(&client); + + broker4->sin_family = AF_INET; + broker4->sin_port = htons(MQTT_BROKER_PORT); + zsock_inet_pton(AF_INET, MQTT_BROKER_ADDR, &broker4->sin_addr); + + client.broker = &broker; + client.evt_cb = mqtt_evt_handler; + client.client_id.utf8 = (uint8_t *)MQTT_CLIENT_ID; + client.client_id.size = strlen(MQTT_CLIENT_ID); + client.password = NULL; + client.user_name = NULL; + client.protocol_version = MQTT_VERSION_3_1_1; + + client.rx_buf = rx_buffer; + client.rx_buf_size = sizeof(rx_buffer); + client.tx_buf = tx_buffer; + client.tx_buf_size = sizeof(tx_buffer); + + client.transport.type = MQTT_TRANSPORT_NON_SECURE; +} + +static int mqtt_connect_broker(void) +{ + int rc; + + if (state.mqtt_connected) { + LOG_DBG("MQTT already connected"); + return 0; + } + + LOG_INF("Connecting to MQTT broker..."); + + mqtt_setup(); + + rc = mqtt_connect(&client); + if (rc != 0) { + LOG_ERR("MQTT connect failed: %d", rc); + return rc; + } + + fds.fd = client.transport.tcp.sock; + fds.events = POLLIN; + + int64_t timeout = k_uptime_get() + MQTT_TIMEOUT_MS; + while (!state.mqtt_connected && k_uptime_get() < timeout) { + if (poll(&fds, 1, 100) > 0) { + mqtt_input(&client); + } + mqtt_live(&client); + } + + if (!state.mqtt_connected) { + LOG_ERR("MQTT connection timeout"); + mqtt_abort(&client); + return -ETIMEDOUT; + } + + LOG_INF("MQTT connected successfully"); + return 0; +} + +static void mqtt_disconnect_broker(void) +{ + if (!state.mqtt_connected) { + return; + } + + LOG_DBG("Disconnecting from MQTT broker..."); + mqtt_disconnect(&client, NULL); + state.mqtt_connected = false; +} + +/* ========== MQTT Publish ========== */ +static int mqtt_publish_to_topic(const char *topic, const char *message) +{ + struct mqtt_publish_param param; + + if (!state.mqtt_connected) { + LOG_ERR("Cannot publish - MQTT not connected"); + return -ENOTCONN; + } + + param.message.topic.qos = MQTT_QOS_0_AT_MOST_ONCE; + param.message.topic.topic.utf8 = (uint8_t *)topic; + param.message.topic.topic.size = strlen(topic); + param.message.payload.data = (uint8_t *)message; + param.message.payload.len = strlen(message); + param.message_id = sys_rand32_get(); + param.dup_flag = 0; + param.retain_flag = 0; + + int rc = mqtt_publish(&client, ¶m); + + if (rc == 0) { + for (int i = 0; i < 5; i++) { + if (poll(&fds, 1, 100) > 0) { + mqtt_input(&client); + } + mqtt_live(&client); + } + } + + return rc; +} + +/* ========== Connection Handler with Retry ========== */ +#define MAX_WIFI_RETRIES 3 +#define MAX_MQTT_RETRIES 3 + +static int ensure_connection(void) +{ + int wifi_attempts = 0; + int mqtt_attempts = 0; + int ret; + + while (wifi_attempts < MAX_WIFI_RETRIES) { + // (Re)connect WiFi + ret = wifi_connect(); + if (ret != 0) { + LOG_WRN("WiFi connection failed (attempt %d/%d)", + wifi_attempts + 1, MAX_WIFI_RETRIES); + wifi_disconnect(); + wifi_attempts++; + k_sleep(K_SECONDS(3)); + continue; // Try WiFi again + } + // WiFi connected; now try MQTT up to 3 times + mqtt_attempts = 0; + while (mqtt_attempts < MAX_MQTT_RETRIES) { + ret = mqtt_connect_broker(); + if (ret == 0) { + // Success! + return 0; + } + LOG_WRN("MQTT connection failed (attempt %d/%d)", + mqtt_attempts + 1, MAX_MQTT_RETRIES); + mqtt_disconnect_broker(); + mqtt_attempts++; + k_sleep(K_SECONDS(2)); + } + // MQTT could not connect after MAX_MQTT_RETRIES - restart WiFi + LOG_ERR("MQTT repeatedly failed after WiFi was attached; restarting WiFi..."); + wifi_disconnect(); + wifi_attempts++; + k_sleep(K_SECONDS(4)); + } + LOG_ERR("Failed to establish WiFi/MQTT connection after all retries"); + return -EFAULT; +} + +/* ========== Process ALL Queued Events ========== */ +static int process_all_events(void) +{ + int ret; + char message[128]; + struct door_event event; + int events_sent = 0; + + // Ensure we're connected first + ret = ensure_connection(); + if (ret != 0) { + return ret; + } + + // Send all queued events + while (event_queue_pop(&event)) { + snprintf(message, sizeof(message), + "{\"serial\":\"%s\",\"event\":\"door_%s\",\"timestamp\":%lld}", + state.device_serial, + event.door_open ? "opened" : "closed", + event.timestamp); + + ret = mqtt_publish_to_topic(MQTT_TOPIC_ALERT, message); + if (ret == 0) { + LOG_INF("Event sent: door %s", event.door_open ? "opened" : "closed"); + events_sent++; + } else { + LOG_ERR("Failed to send event: %d", ret); + // Re-queue failed event + event_queue_push(event.door_open, event.timestamp); + break; + } + + // Small delay between messages + k_sleep(K_MSEC(100)); + } + + // Keep connection alive briefly in case more events arrive + k_sleep(K_MSEC(CONNECTION_KEEP_ALIVE_MS)); + + // Disconnect to save power + mqtt_disconnect_broker(); + wifi_disconnect(); + + LOG_INF("Sent %d event(s), %d remaining in queue", + events_sent, event_queue_size()); + + return 0; +} + +/* ========== Door Sensor Initialization ========== */ +static int init_door_sensor(void) +{ + int ret; + + if (!gpio_is_ready_dt(&door_sensor)) { + LOG_ERR("Door sensor device %s is not ready", door_sensor.port->name); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&door_sensor, GPIO_INPUT); + if (ret != 0) { + LOG_ERR("Failed to configure door sensor pin: %d", ret); + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(&door_sensor, GPIO_INT_EDGE_BOTH); + if (ret != 0) { + LOG_ERR("Failed to configure interrupt: %d", ret); + return ret; + } + + gpio_init_callback(&door_cb_data, door_state_changed_handler, BIT(door_sensor.pin)); + gpio_add_callback(door_sensor.port, &door_cb_data); + + // Initialize debounce work + k_work_init_delayable(&debounce_work, debounce_work_fn); + + // Read initial state + state.door_open = (gpio_pin_get_dt(&door_sensor) == 1); + + LOG_INF("Door sensor initialized (initial state: %s)", + state.door_open ? "OPEN" : "CLOSED"); + + return 0; +} + +/* ========== Main Application ========== */ +int main(void) +{ + int ret; + char message[128]; + + LOG_INF("Power-optimized door sensor with debouncing starting..."); + + // Initialize event queue + event_queue_init(); + + // Get device serial/ID + get_device_serial(state.device_serial, sizeof(state.device_serial)); + LOG_INF("Device ID: %s", state.device_serial); + + // Initialize door sensor + ret = init_door_sensor(); + if (ret < 0) { + LOG_ERR("Door sensor initialization failed: %d", ret); + return ret; + } + + state.last_status_time = k_uptime_get(); + + LOG_INF("System ready - entering low-power monitoring mode"); + + while (1) { + int64_t now = k_uptime_get(); + + /* Process ALL queued door events */ + if (event_queue_size() > 0) { + LOG_INF("Processing %d queued event(s)...", event_queue_size()); + ret = process_all_events(); + if (ret != 0) { + LOG_ERR("Failed to process events: %d", ret); + } + state.last_status_time = now; // Reset status timer after events + } + + /* Handle periodic status update */ + if ((now - state.last_status_time) >= STATUS_INTERVAL_MS) { + LOG_INF("Sending periodic status update..."); + + ret = ensure_connection(); + if (ret == 0) { + int uptime_sec = (int)(now / 1000); + snprintf(message, sizeof(message), + "{\"serial\":\"%s\",\"uptime\":%d,\"door\":\"%s\"}", + state.device_serial, + uptime_sec, + state.door_open ? "open" : "closed"); + + ret = mqtt_publish_to_topic(MQTT_TOPIC_STATUS, message); + k_sleep(K_MSEC(500)); + mqtt_disconnect_broker(); + wifi_disconnect(); + + if (ret == 0) { + LOG_INF("Status update sent"); + } + } + + state.last_status_time = now; + } + + k_sleep(K_MSEC(100)); + } + + return 0; +} diff --git a/src/test_certs.h b/src/test_certs.h new file mode 100644 index 0000000..aa9980e --- /dev/null +++ b/src/test_certs.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __TEST_CERTS_H__ +#define __TEST_CERTS_H__ + +#if defined(CONFIG_NET_SOCKETS_OFFLOAD) +/* By default only certificates in DER format are supported. If you want to use + * certificate in PEM format, you can enable support for it in Kconfig. + */ + +#if defined(CONFIG_TLS_CREDENTIAL_FILENAMES) +static const unsigned char ca_certificate[] = "ca_cert.der"; +#else +static const unsigned char ca_certificate[] = { +#include "ca_cert.der.inc" +}; +#endif + +#else +#include + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/* This byte array can be generated by + * "cat ca.crt | sed -e '1d;$d' | base64 -d |xxd -i" + */ +static const unsigned char ca_certificate[] = { + 0x30, 0x82, 0x02, 0xfb, 0x30, 0x82, 0x01, 0xe3, + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xee, 0x10, 0x1f, 0xc1, 0xf2, 0x30, 0xe9, 0x11, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x37, 0x30, 0x36, 0x32, 0x36, 0x31, + 0x30, 0x35, 0x36, 0x31, 0x30, 0x5a, 0x17, 0x0d, + 0x34, 0x34, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, + 0x35, 0x36, 0x31, 0x30, 0x5a, 0x30, 0x14, 0x31, + 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, + 0x6f, 0x73, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xc6, 0x00, 0x7b, 0x0d, + 0xd1, 0x17, 0x43, 0x6b, 0xa9, 0xa7, 0x79, 0x9c, + 0x0f, 0x8d, 0x77, 0x91, 0xd2, 0xf7, 0x47, 0x35, + 0xb3, 0x17, 0xe2, 0xdd, 0xed, 0x6d, 0x01, 0xf9, + 0xb1, 0x92, 0xc9, 0x48, 0x80, 0xe0, 0x1f, 0xcf, + 0xb7, 0xa4, 0x5f, 0xf0, 0x36, 0xea, 0xbf, 0xe1, + 0x33, 0xf8, 0xa9, 0xc5, 0xe6, 0xd4, 0x19, 0x8b, + 0x82, 0x25, 0xd9, 0x19, 0x74, 0x70, 0x79, 0xec, + 0xc6, 0x68, 0xc9, 0xef, 0xce, 0x1a, 0xa9, 0xf0, + 0xb7, 0x01, 0x35, 0x91, 0xff, 0xd3, 0x75, 0x6e, + 0x02, 0xba, 0x06, 0x9a, 0x2a, 0xac, 0xcf, 0x22, + 0xbf, 0x2b, 0x1f, 0xc1, 0x72, 0x38, 0x22, 0x35, + 0xea, 0xda, 0x6f, 0xdd, 0x67, 0xa2, 0x2b, 0x19, + 0x38, 0x19, 0x0e, 0x44, 0xd1, 0x71, 0x38, 0xb4, + 0x6d, 0x26, 0x85, 0xd6, 0xc6, 0xbe, 0xc1, 0x6f, + 0x3c, 0xee, 0xaf, 0x94, 0x3c, 0x05, 0x56, 0x4e, + 0xad, 0x53, 0x81, 0x8b, 0xd4, 0x23, 0x31, 0x69, + 0x72, 0x27, 0x93, 0xb4, 0x3a, 0xac, 0x23, 0xe8, + 0x10, 0xae, 0xf5, 0x9f, 0x0b, 0xa6, 0x6e, 0xd3, + 0x73, 0xca, 0x18, 0x11, 0xca, 0xbe, 0x71, 0x00, + 0x56, 0x29, 0x34, 0x54, 0xcc, 0xda, 0x29, 0x5b, + 0x26, 0x29, 0x99, 0x4d, 0x5f, 0xa1, 0xa6, 0xb9, + 0xcb, 0x2b, 0xb2, 0x0f, 0x10, 0x00, 0x04, 0xa9, + 0x11, 0x2c, 0x48, 0xb1, 0x99, 0xa5, 0xca, 0x7c, + 0x67, 0xa5, 0xbe, 0x14, 0x20, 0x12, 0xb7, 0x3b, + 0x7a, 0x4f, 0xdc, 0xc7, 0xd5, 0x2d, 0x04, 0x66, + 0xbb, 0xf5, 0x0c, 0xcd, 0xf1, 0x32, 0x39, 0xd7, + 0x51, 0x9b, 0xba, 0xdb, 0xf1, 0xa7, 0xfe, 0x2d, + 0x9a, 0xe6, 0x9c, 0x6b, 0x54, 0xda, 0xf1, 0xdd, + 0x48, 0xf9, 0xd7, 0xf0, 0x35, 0x7c, 0x8e, 0x24, + 0x7e, 0x44, 0x2f, 0xf3, 0xbf, 0x39, 0x0e, 0x96, + 0xab, 0xe1, 0x45, 0x03, 0x8b, 0x54, 0xdc, 0xe1, + 0xb6, 0x11, 0x81, 0x21, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xa4, 0xef, 0x6d, 0xdc, 0x9b, 0x23, 0xc5, 0x3a, + 0xdd, 0x34, 0xd9, 0x01, 0x1c, 0x68, 0x03, 0x53, + 0xae, 0x92, 0xc2, 0xc9, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xa4, 0xef, 0x6d, 0xdc, 0x9b, 0x23, 0xc5, + 0x3a, 0xdd, 0x34, 0xd9, 0x01, 0x1c, 0x68, 0x03, + 0x53, 0xae, 0x92, 0xc2, 0xc9, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x67, + 0x65, 0xbf, 0x93, 0x89, 0xde, 0x4f, 0x71, 0xff, + 0x1c, 0x93, 0x68, 0xa0, 0x64, 0x09, 0x5e, 0x95, + 0x94, 0xf5, 0xd5, 0xf4, 0x6b, 0x20, 0x32, 0xd8, + 0x04, 0x80, 0xac, 0xf8, 0x52, 0x36, 0x7a, 0x38, + 0x83, 0xae, 0xab, 0x29, 0x22, 0x42, 0x71, 0x7e, + 0xea, 0xe5, 0x4f, 0x71, 0xac, 0x44, 0x3f, 0x9e, + 0x5e, 0x49, 0x22, 0x05, 0xee, 0xa6, 0x7b, 0xab, + 0x56, 0x2e, 0xb3, 0x9a, 0x35, 0x1a, 0x88, 0xc3, + 0x54, 0x9b, 0xfd, 0xac, 0x65, 0x54, 0xaf, 0x21, + 0xa7, 0xe0, 0xdd, 0x62, 0x29, 0x8c, 0xae, 0x26, + 0x0b, 0x84, 0x1f, 0x69, 0x78, 0x84, 0xc6, 0x7e, + 0xcf, 0xc8, 0xf5, 0x92, 0x8c, 0x05, 0xa8, 0x13, + 0x38, 0xcd, 0x0b, 0x98, 0x53, 0xfb, 0xdd, 0x8d, + 0x51, 0x90, 0xa8, 0x51, 0xfa, 0x52, 0xbe, 0x28, + 0xd4, 0x71, 0x50, 0x73, 0x1f, 0xb0, 0xb6, 0x0e, + 0x45, 0xb1, 0x47, 0x41, 0x06, 0xd9, 0x1d, 0x7a, + 0x34, 0xe7, 0x80, 0x2e, 0x0c, 0x02, 0x50, 0x97, + 0xde, 0xa8, 0x7a, 0x84, 0x2c, 0x1d, 0xf4, 0x51, + 0x56, 0xa5, 0x52, 0xb5, 0x04, 0x2e, 0xcb, 0xdd, + 0x8b, 0x2e, 0x16, 0xc6, 0xde, 0xc8, 0xe9, 0x8d, + 0xee, 0x5e, 0xb6, 0xa0, 0xe0, 0x2b, 0x85, 0x2a, + 0x89, 0x7b, 0xba, 0x68, 0x80, 0x2b, 0xfb, 0x6e, + 0x2e, 0x80, 0xe7, 0x7a, 0x97, 0x09, 0xb5, 0x2f, + 0x20, 0x8e, 0xed, 0xbc, 0x98, 0x6f, 0x95, 0xd5, + 0x5b, 0x3d, 0x26, 0x19, 0x26, 0x14, 0x39, 0x82, + 0xa8, 0xa8, 0x42, 0x46, 0xab, 0x59, 0x93, 0x47, + 0x83, 0xf7, 0x79, 0xbf, 0x73, 0xb5, 0x5d, 0x5d, + 0x78, 0xfe, 0x62, 0xac, 0xed, 0xb7, 0x1e, 0x4a, + 0xad, 0xc3, 0x99, 0x39, 0x7d, 0x3e, 0x30, 0x21, + 0x26, 0x1d, 0x66, 0xdb, 0x0d, 0xf3, 0xba, 0x87, + 0x46, 0xf0, 0x04, 0xfc, 0xc3, 0xbe, 0x84, 0x85, + 0x3c, 0x01, 0xef, 0xe0, 0x68, 0x65, 0xee, +}; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +/* Avoid leading zero in psk because there's a potential issue of mosquitto + * that leading zero of psk will be skipped and it leads to TLS handshake + * failure + */ +const unsigned char client_psk[] = { + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f +}; + +const char client_psk_id[] = "Client_identity"; +#endif + +#endif /* CONFIG_NET_SOCKETS_OFFLOAD */ + +#endif