From 80c772887826474815142aefda76a40b3e7fcbfe Mon Sep 17 00:00:00 2001 From: justinhrpr-cmi Date: Wed, 7 Oct 2026 13:56:47 -0400 Subject: [PATCH 1/5] demo code for mqtt on team 3 wifi --- MXChip/AZ3166/app/CMakeLists.txt | 1 + MXChip/AZ3166/app/mqtt/cloud_config.h | 14 ++- MXChip/AZ3166/app/mqtt/main.c | 30 ++--- MXChip/AZ3166/app/mqtt/mqtt_client.c | 77 +++++++------ MXChip/AZ3166/app/mqtt/powertrain_display.c | 115 ++++++++++++++++++++ MXChip/AZ3166/app/mqtt/powertrain_display.h | 15 +++ MXChip/AZ3166/app/mqtt/telemetry.c | 7 +- MXChip/AZ3166/app/mqtt/telemetry.h | 4 +- MXChip/AZ3166/app/starter/cloud_config.h | 4 +- reflash.sh | 49 +++++++++ 10 files changed, 258 insertions(+), 58 deletions(-) create mode 100644 MXChip/AZ3166/app/mqtt/powertrain_display.c create mode 100644 MXChip/AZ3166/app/mqtt/powertrain_display.h create mode 100755 reflash.sh diff --git a/MXChip/AZ3166/app/CMakeLists.txt b/MXChip/AZ3166/app/CMakeLists.txt index 79a0fa4e..8e542b6f 100644 --- a/MXChip/AZ3166/app/CMakeLists.txt +++ b/MXChip/AZ3166/app/CMakeLists.txt @@ -60,6 +60,7 @@ elseif(APP_CONFIG STREQUAL "mqtt") ${COMMON_DIR}/screen.c ${CONFIG_DIR}/telemetry.c ${CONFIG_DIR}/mqtt_client.c + ${CONFIG_DIR}/powertrain_display.c ${CONFIG_DIR}/main.c ${CONFIG_DIR}/cloud_config.h ) diff --git a/MXChip/AZ3166/app/mqtt/cloud_config.h b/MXChip/AZ3166/app/mqtt/cloud_config.h index 5e24cdcf..378c2dd1 100644 --- a/MXChip/AZ3166/app/mqtt/cloud_config.h +++ b/MXChip/AZ3166/app/mqtt/cloud_config.h @@ -30,23 +30,25 @@ typedef enum // WiFi connection config // ---------------------------------------------------------------------------- #define HOSTNAME "eclipse-threadx" //Change to unique hostname. -#define WIFI_SSID "" -#define WIFI_PASSWORD "" +#define WIFI_SSID "Hackathon-Team-03" +#define WIFI_PASSWORD "SDVTeam-123456" #define WIFI_MODE WPA2_PSK_AES // ---------------------------------------------------------------------------- // MQTT Config // ---------------------------------------------------------------------------- #define MQTT_CLIENT_NAME "ThreadXAZ3166" //Change to unique name. -// Use test.mosquitto.org in a pinch. -#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(5, 196, 78, 28)) -#define MQTT_SUBSCRIBE_TOPIC MQTT_CLIENT_NAME "/incoming" +// imx95-a.local: use its IPv4 address directly; no username or password required. +#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(192, 168, 103, 191)) +#define MQTT_BROKER_PORT 1883 +#define MQTT_SUBSCRIBE_TOPIC "vehicle/powertrain/mode" #define MQTT_PUBLISH_TOPIC MQTT_CLIENT_NAME "/telemetry" // ---------------------------------------------------------------------------- // MQTT Support infrastructure // ---------------------------------------------------------------------------- -extern TX_QUEUE mqtt_queue; +// The OLED and telemetry sensors share the same I2C bus. +extern TX_MUTEX mqtt_i2c_mutex; extern TX_EVENT_FLAGS_GROUP mqtt_app_flag; #define MQTT_RECEIVE_EVENT 1 #define MQTT_MESSAGE_READY 2 diff --git a/MXChip/AZ3166/app/mqtt/main.c b/MXChip/AZ3166/app/mqtt/main.c index 65afdc62..3edae45a 100644 --- a/MXChip/AZ3166/app/mqtt/main.c +++ b/MXChip/AZ3166/app/mqtt/main.c @@ -30,32 +30,32 @@ #define ECLIPSETX_THREAD_STACK_SIZE 4096 #define ECLIPSETX_THREAD_PRIORITY 4 -#define BYTE_POOL_SIZE 1024 -#define QUEUE_SIZE 16 - - -TX_QUEUE mqtt_queue; +TX_MUTEX mqtt_i2c_mutex; TX_EVENT_FLAGS_GROUP mqtt_app_flag; -TX_BYTE_POOL byte_pool; TX_THREAD telemetry_thread; TX_THREAD mqtt_thread; ULONG telemetry_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; ULONG mqtt_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; -TX_EVENT_FLAGS_GROUP mqtt_app_flag; void tx_application_define(void* first_unused_memory) { systick_interval_set(TX_TIMER_TICKS_PER_SECOND); - CHAR *pointer; - - /* Allocate the message queue. */ - tx_byte_allocate(&byte_pool, (VOID **)&pointer, QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); - - /* Create the message queue shared by the telemetry and MQTT threads */ - tx_queue_create(&mqtt_queue, "Shared queue", TX_1_ULONG, pointer, QUEUE_SIZE*sizeof(ULONG)); + /* Both threads need these objects before they can start. */ + UINT status = tx_event_flags_create(&mqtt_app_flag, "MQTT event"); + if (status != TX_SUCCESS) + { + printf("ERROR: MQTT event creation failed (0x%02x)\r\n", status); + return; + } + status = tx_mutex_create(&mqtt_i2c_mutex, "MQTT sensor/display I2C", TX_INHERIT); + if (status != TX_SUCCESS) + { + printf("ERROR: I2C mutex creation failed (0x%02x)\r\n", status); + return; + } // Create Telemetry thread - UINT status = tx_thread_create(&telemetry_thread, + status = tx_thread_create(&telemetry_thread, "Eclipse ThreadX telemetry Thread", telemetry_thread_entry, 0, diff --git a/MXChip/AZ3166/app/mqtt/mqtt_client.c b/MXChip/AZ3166/app/mqtt/mqtt_client.c index 9428007b..0a875bc5 100644 --- a/MXChip/AZ3166/app/mqtt/mqtt_client.c +++ b/MXChip/AZ3166/app/mqtt/mqtt_client.c @@ -13,6 +13,7 @@ #include "cloud_config.h" #include "mqtt_client.h" #include "nx_api.h" +#include "powertrain_display.h" #include "telemetry.h" #include "wwd_networking.h" @@ -26,8 +27,8 @@ static ULONG mqtt_client_stack[MQTT_CLIENT_STACK_SIZE / sizeof(ULONG)]; /* Declare buffers to hold message and topic. */ -static UCHAR message_buffer[NXD_MQTT_MAX_MESSAGE_LENGTH]; -static UCHAR topic_buffer[NXD_MQTT_MAX_TOPIC_NAME_LENGTH]; +static UCHAR message_buffer[NXD_MQTT_MAX_MESSAGE_LENGTH + 1]; +static UCHAR topic_buffer[NXD_MQTT_MAX_TOPIC_NAME_LENGTH + 1]; /* Declare the MQTT client control block. */ @@ -45,7 +46,7 @@ static void send_message(){ UINT status; /* Publish a message with QoS Level 1. */ - char buffer[160] = {0}; + char buffer[TELEMETRY_BUFFER_SIZE] = {0}; get_current_telemetry_string(buffer); //printf("%s", buffer); @@ -63,17 +64,32 @@ static void send_message(){ static void receive_message(){ UINT status; UINT topic_length, message_length; - ULONG message_sent = 0; - - status = nxd_mqtt_client_message_get(&mqtt_client, topic_buffer, sizeof(topic_buffer), &topic_length, - message_buffer, sizeof(message_buffer), &message_length); - printf("Received message and status: %d \r\n", status); - if (status == NXD_MQTT_SUCCESS){ - topic_buffer[topic_length] = 0; - message_buffer[message_length] = 0; - message_sent = message_buffer[0]; - status = tx_queue_send(&mqtt_queue, &message_sent, TX_WAIT_FOREVER); + + /* Notifications may cover several messages; drain them all. Leave room + * for terminators when receiving a maximum-length topic or payload. */ + while (1){ + status = nxd_mqtt_client_message_get(&mqtt_client, topic_buffer, sizeof(topic_buffer) - 1, &topic_length, + message_buffer, sizeof(message_buffer) - 1, &message_length); + if (status == NXD_MQTT_NO_MESSAGE){ + return; + } + if (status != NXD_MQTT_SUCCESS){ + printf("MQTT receive failed with code: %d\r\n", status); + return; + } + topic_buffer[topic_length] = '\0'; + message_buffer[message_length] = '\0'; printf("Topic: %s, Message: %s\r\n", topic_buffer, message_buffer); + + if (topic_length == STRLEN(MQTT_SUBSCRIBE_TOPIC) && + memcmp(topic_buffer, MQTT_SUBSCRIBE_TOPIC, topic_length) == 0){ + tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); + bool displayed = powertrain_display_update((const char *)message_buffer, message_length); + tx_mutex_put(&mqtt_i2c_mutex); + if (!displayed){ + printf("Ignoring powertrain message without a valid Mode string\r\n"); + } + } } } @@ -85,7 +101,6 @@ static VOID client_notify_func(NXD_MQTT_CLIENT *client_ptr, UINT number_of_messa return; } -static ULONG error_count; static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ UINT status; NXD_ADDRESS server_ip; @@ -99,7 +114,7 @@ static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ if (status){ printf("Error in creating MQTT client: 0x%02x\n", status); - error_count++; + return; } printf(" MQTT client created\r\n"); @@ -107,20 +122,24 @@ static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ /* Register the disconnect notification function. */ nxd_mqtt_client_disconnect_notify_set(&mqtt_client, client_disconnect_func); - /* Create an event flag for this demo. */ - status = tx_event_flags_create(&mqtt_app_flag, "MQTT event"); - if (status) - error_count++; + /* Install the callback before subscribing: retained messages can arrive + * as soon as the broker receives the subscription. */ + status = nxd_mqtt_client_receive_notify_set(&mqtt_client, client_notify_func); + if (status != NXD_MQTT_SUCCESS){ + printf("MQTT receive notify setup failed with code: %d\r\n", status); + return; + } server_ip.nxd_ip_version = 4; server_ip.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; - /* Start the connection to the server. */ + /* Connect over plain MQTT without setting a username or password. */ - status = nxd_mqtt_client_connect(&mqtt_client, &server_ip, NXD_MQTT_PORT, + status = nxd_mqtt_client_connect(&mqtt_client, &server_ip, MQTT_BROKER_PORT, MQTT_KEEP_ALIVE_TIMER, 0, NX_WAIT_FOREVER); if (status != NXD_MQTT_SUCCESS){ printf("MQTT connect failed with code: %d\r\n", status); + return; } else{ printf("MQTT Client connected.\r\n"); @@ -130,20 +149,12 @@ static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ status = nxd_mqtt_client_subscribe(&mqtt_client, MQTT_SUBSCRIBE_TOPIC, STRLEN(MQTT_SUBSCRIBE_TOPIC), QOS0); if (status != NXD_MQTT_SUCCESS){ printf("MQTT subscribe failed with code: %d\r\n", status); + return; } else{ printf("Subscribed to topic %s.\r\n", MQTT_SUBSCRIBE_TOPIC); } - /* Set the receive notify function. */ - status = nxd_mqtt_client_receive_notify_set(&mqtt_client, client_notify_func); - if (status != NXD_MQTT_SUCCESS){ - printf("MQTT receive notify setup failed with code: %d\r\n", status); - } - else{ - printf("MQTT Receive notify function set.\r\n"); - } - /* Now wait for the broker to publish the message. */ printf("Waiting for messages\r\n"); @@ -153,7 +164,7 @@ static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ receive_message(); } - else if (events & MQTT_MESSAGE_READY){ + if (events & MQTT_MESSAGE_READY){ send_message(); } } @@ -172,6 +183,10 @@ void mqtt_thread_entry(ULONG parameter){ printf("Starting Eclipse ThreadX MQTT thread\r\n\r\n"); + tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); + powertrain_display_waiting(); + tx_mutex_put(&mqtt_i2c_mutex); + // Initialize the network if ((status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE))){ printf("ERROR: Failed to initialize the network (0x%08x)\r\n", status); diff --git a/MXChip/AZ3166/app/mqtt/powertrain_display.c b/MXChip/AZ3166/app/mqtt/powertrain_display.c new file mode 100644 index 00000000..2cb495f8 --- /dev/null +++ b/MXChip/AZ3166/app/mqtt/powertrain_display.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: MIT */ +#include "powertrain_display.h" + +#include "jsmn.h" +#include "ssd1306.h" + +#include +#include + +static void draw_heading(void) +{ + ssd1306_Fill(Black); + ssd1306_SetCursor(2, 0); + ssd1306_WriteString("Powertrain mode", Font_7x10, White); +} + +void powertrain_display_waiting(void) +{ + draw_heading(); + ssd1306_SetCursor(2, 26); + ssd1306_WriteString("Waiting for mode", Font_7x10, White); + ssd1306_UpdateScreen(); +} + +static void draw_mode(const char *mode, size_t length) +{ + /* Short names, including "Performance", fit on one large-text line. + * Longer names wrap in the smaller font, with ellipsis if necessary. */ + FontDef font = length <= (SSD1306_WIDTH - 4) / Font_11x18.FontWidth + ? Font_11x18 : Font_7x10; + size_t columns = (SSD1306_WIDTH - 4) / font.FontWidth; + size_t capacity = columns * ((SSD1306_HEIGHT - 22) / font.FontHeight); + size_t visible = length < capacity ? length : capacity; + + draw_heading(); + for (size_t i = 0; i < visible; ++i) + { + if (i % columns == 0) + { + ssd1306_SetCursor(2, 22 + (i / columns) * font.FontHeight); + } + unsigned char ch = (unsigned char)mode[i]; + if (length > capacity && i >= capacity - 3) + { + ch = '.'; + } + /* The OLED font contains printable ASCII only. */ + ssd1306_WriteChar(ch >= 32 && ch <= 126 ? (char)ch : '?', font, White); + } + ssd1306_UpdateScreen(); +} + +bool powertrain_display_update(const char *payload, size_t payload_length) +{ + jsmn_parser parser; + jsmntok_t tokens[16]; + + if (payload == NULL || payload_length == 0) + { + return false; + } + + jsmn_init(&parser); + int count = jsmn_parse(&parser, payload, payload_length, tokens, + sizeof(tokens) / sizeof(tokens[0])); + if (count < 3 || tokens[0].type != JSMN_OBJECT || + tokens[count - 1].end > tokens[0].end) + { + return false; + } + + /* Walk top-level key/value pairs, skipping nested metadata. */ + for (int i = 1; i + 1 < count;) + { + const jsmntok_t *key = &tokens[i]; + const jsmntok_t *value = &tokens[i + 1]; + if (key->type != JSMN_STRING || key->size != 1) + { + return false; + } + if (key->end - key->start == 4 && + memcmp(payload + key->start, "Mode", 4) == 0) + { + if (value->type != JSMN_STRING) + { + return false; + } + const char *mode = payload + value->start; + size_t length = (size_t)(value->end - value->start); + while (length > 0 && isspace((unsigned char)*mode)) + { + ++mode; + --length; + } + while (length > 0 && isspace((unsigned char)mode[length - 1])) + { + --length; + } + if (length == 0) + { + return false; + } + draw_mode(mode, length); + return true; + } + + int value_end = value->end; + i += 2; + while (i < count && tokens[i].start < value_end) + { + ++i; + } + } + return false; +} diff --git a/MXChip/AZ3166/app/mqtt/powertrain_display.h b/MXChip/AZ3166/app/mqtt/powertrain_display.h new file mode 100644 index 00000000..500fa8f9 --- /dev/null +++ b/MXChip/AZ3166/app/mqtt/powertrain_display.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef POWERTRAIN_DISPLAY_H +#define POWERTRAIN_DISPLAY_H + +#include +#include + +/* The caller holds mqtt_i2c_mutex while updating the display. */ +void powertrain_display_waiting(void); + +/* Display the top-level "Mode" string in a powertrain MQTT JSON payload. + * Invalid messages leave the last displayed mode intact. */ +bool powertrain_display_update(const char *payload, size_t payload_length); + +#endif diff --git a/MXChip/AZ3166/app/mqtt/telemetry.c b/MXChip/AZ3166/app/mqtt/telemetry.c index 707925a0..8bf0c4db 100644 --- a/MXChip/AZ3166/app/mqtt/telemetry.c +++ b/MXChip/AZ3166/app/mqtt/telemetry.c @@ -26,7 +26,6 @@ static sensor_data current_sensor_data; // Telemetry output static const int TELEMETRY_ROWS = 5; static const int TELEMETRY_ROW_SIZE = 40; -const int TELEMETRY_BUFFER_SIZE = 256; /* Function to compare two float arrays * Returns true if arrays are equal within the given tolerance, otherwise false. @@ -108,7 +107,8 @@ void telemetry_thread_entry(ULONG parameter) while(1){ - // Acquire fresh data. + // Serialize sensor reads with OLED transfers on the same I2C bus. + tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); lps22hb_t lps22hb_data = lps22hb_data_read(); new_sensor_data.temperature_degC = lps22hb_data.temperature_degC; new_sensor_data.pressure_hPa = lps22hb_data.pressure_hPa; @@ -122,14 +122,15 @@ void telemetry_thread_entry(ULONG parameter) memcpy(new_sensor_data.magnetic_mG, lis2mdl_data.magnetic_mG, sizeof(lis2mdl_data.magnetic_mG)); + tx_mutex_put(&mqtt_i2c_mutex); if (data_changed(¤t_sensor_data, &new_sensor_data)){ #ifdef LOG_TELEMETRY printf("Telemetry changed.\r\n"); print_sensor_data(new_sensor_data); #endif - tx_event_flags_set(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR); current_sensor_data = new_sensor_data; + tx_event_flags_set(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR); } #ifdef LOG_TELEMETRY else{ diff --git a/MXChip/AZ3166/app/mqtt/telemetry.h b/MXChip/AZ3166/app/mqtt/telemetry.h index 9dd795a4..fef22fef 100644 --- a/MXChip/AZ3166/app/mqtt/telemetry.h +++ b/MXChip/AZ3166/app/mqtt/telemetry.h @@ -19,6 +19,8 @@ #ifndef _TELEMETRY_H #define _TELEMETRY_H +#define TELEMETRY_BUFFER_SIZE 256 + // Log telemetry if needed //#define LOG_TELEMETRY @@ -34,4 +36,4 @@ typedef struct{ void telemetry_thread_entry(ULONG parameter); void get_current_telemetry_string(char* output); -#endif // _TELEMETRY_H \ No newline at end of file +#endif // _TELEMETRY_H diff --git a/MXChip/AZ3166/app/starter/cloud_config.h b/MXChip/AZ3166/app/starter/cloud_config.h index 9c4acf9e..b070a926 100644 --- a/MXChip/AZ3166/app/starter/cloud_config.h +++ b/MXChip/AZ3166/app/starter/cloud_config.h @@ -28,8 +28,8 @@ typedef enum // WiFi connection config // ---------------------------------------------------------------------------- #define HOSTNAME "eclipse-threadx" //Change to unique hostname. -#define WIFI_SSID "" -#define WIFI_PASSWORD "" +#define WIFI_SSID "SDVTeam1" +#define WIFI_PASSWORD "SDVTeam1-123456" #define WIFI_MODE WPA2_PSK_AES // ---------------------------------------------------------------------------- diff --git a/reflash.sh b/reflash.sh new file mode 100755 index 00000000..6cc1563b --- /dev/null +++ b/reflash.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +LABEL="AZ3166" +MOUNT_POINT="/mnt/AZ3166" +DEPLOY_SCRIPT="./MXChip/AZ3166/scripts/deploy.sh" + +echo "[INFO] Looking for $LABEL..." + +DEVICE=$(lsblk -rno PATH,LABEL | awk -v label="$LABEL" '$2 == label {print $1; exit}') + +if [ -z "$DEVICE" ]; then + echo "[ERROR] AZ3166 device not found." + echo "[ERROR] Make sure the board is attached to WSL with usbipd." + exit 1 +fi + +echo "[INFO] Found AZ3166 at $DEVICE" + +# Unmount if currently mounted +if findmnt -rn "$MOUNT_POINT" >/dev/null 2>&1; then + echo "[INFO] Unmounting $MOUNT_POINT..." + sudo umount "$MOUNT_POINT" +fi + +# Repair/check the virtual FAT filesystem +echo "[INFO] Checking filesystem..." +sudo fsck.vfat -a "$DEVICE" + +# Ensure mount point exists +sudo mkdir -p "$MOUNT_POINT" + +# Mount read/write +echo "[INFO] Mounting $DEVICE at $MOUNT_POINT..." +sudo mount -o rw "$DEVICE" "$MOUNT_POINT" + +# Verify that it really mounted read/write +if ! findmnt -rn -o OPTIONS "$MOUNT_POINT" | grep -qw "rw"; then + echo "[ERROR] AZ3166 did not mount read/write." + exit 1 +fi + +echo "[INFO] AZ3166 mounted read/write." + +# Deploy +echo "[INFO] Flashing AZ3166..." +sudo "$DEPLOY_SCRIPT" "$MOUNT_POINT" + +echo "[OK] AZ3166 reflash complete." From 6d613ede97f1792c7a95dceceb47865578f3db2d Mon Sep 17 00:00:00 2001 From: justinhrpr-cmi Date: Wed, 7 Oct 2026 19:37:19 -0400 Subject: [PATCH 2/5] initial changes for uProtocol example --- MXChip/AZ3166/app/mqtt/cloud_config.h | 8 +- MXChip/AZ3166/app/mqtt/main.c | 8 +- MXChip/AZ3166/app/mqtt/mqtt_client.c | 344 +++++++++++++--------- MXChip/AZ3166/app/mqtt/mqtt_client.h | 25 +- MXChip/AZ3166/app/mqtt/uprotocol_config.h | 33 +++ 5 files changed, 251 insertions(+), 167 deletions(-) create mode 100644 MXChip/AZ3166/app/mqtt/uprotocol_config.h diff --git a/MXChip/AZ3166/app/mqtt/cloud_config.h b/MXChip/AZ3166/app/mqtt/cloud_config.h index 378c2dd1..6b9b4dab 100644 --- a/MXChip/AZ3166/app/mqtt/cloud_config.h +++ b/MXChip/AZ3166/app/mqtt/cloud_config.h @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -12,6 +13,7 @@ * Microsoft - Initial version * Frédéric Desbiens - 2024 version. */ +// Portions of this file were generated with AI assistance. #ifndef _CLOUD_CONFIG_H #define _CLOUD_CONFIG_H @@ -30,8 +32,8 @@ typedef enum // WiFi connection config // ---------------------------------------------------------------------------- #define HOSTNAME "eclipse-threadx" //Change to unique hostname. -#define WIFI_SSID "Hackathon-Team-03" -#define WIFI_PASSWORD "SDVTeam-123456" +#define WIFI_SSID "SDV-imx95-A" +#define WIFI_PASSWORD "eclipsesdv" #define WIFI_MODE WPA2_PSK_AES // ---------------------------------------------------------------------------- @@ -39,7 +41,7 @@ typedef enum // ---------------------------------------------------------------------------- #define MQTT_CLIENT_NAME "ThreadXAZ3166" //Change to unique name. // imx95-a.local: use its IPv4 address directly; no username or password required. -#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(192, 168, 103, 191)) +#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(192, 168, 60, 1)) #define MQTT_BROKER_PORT 1883 #define MQTT_SUBSCRIBE_TOPIC "vehicle/powertrain/mode" #define MQTT_PUBLISH_TOPIC MQTT_CLIENT_NAME "/telemetry" diff --git a/MXChip/AZ3166/app/mqtt/main.c b/MXChip/AZ3166/app/mqtt/main.c index 3edae45a..3d367b86 100644 --- a/MXChip/AZ3166/app/mqtt/main.c +++ b/MXChip/AZ3166/app/mqtt/main.c @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -13,7 +14,7 @@ * Frédéric Desbiens - 2024 version. * Frédéric Desbiens - Added telemetry and MQTT. */ - +// Portions of this file were generated with AI assistance. #include #include "tx_api.h" @@ -30,12 +31,13 @@ #define ECLIPSETX_THREAD_STACK_SIZE 4096 #define ECLIPSETX_THREAD_PRIORITY 4 +#define MQTT_APP_THREAD_STACK_SIZE 8192 TX_MUTEX mqtt_i2c_mutex; TX_EVENT_FLAGS_GROUP mqtt_app_flag; TX_THREAD telemetry_thread; TX_THREAD mqtt_thread; ULONG telemetry_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; -ULONG mqtt_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; +ULONG mqtt_thread_stack[MQTT_APP_THREAD_STACK_SIZE / sizeof(ULONG)]; void tx_application_define(void* first_unused_memory) { @@ -77,7 +79,7 @@ void tx_application_define(void* first_unused_memory) mqtt_thread_entry, 0, mqtt_thread_stack, - ECLIPSETX_THREAD_STACK_SIZE, + MQTT_APP_THREAD_STACK_SIZE, ECLIPSETX_THREAD_PRIORITY, ECLIPSETX_THREAD_PRIORITY, TX_NO_TIME_SLICE, diff --git a/MXChip/AZ3166/app/mqtt/mqtt_client.c b/MXChip/AZ3166/app/mqtt/mqtt_client.c index 0a875bc5..52c938dc 100644 --- a/MXChip/AZ3166/app/mqtt/mqtt_client.c +++ b/MXChip/AZ3166/app/mqtt/mqtt_client.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2025 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -10,189 +11,250 @@ * Contributors: * Frédéric Desbiens - Initial version. */ +// Portions of this file were generated with AI assistance. +/* MISRA C:2012/2023 Rule 15.5 deviation: early returns keep startup cleanup + * adjacent to failure checks. Rule 17.7 deviation: cleanup calls on valid + * ThreadX/HAL objects deliberately discard their return status. */ #include "cloud_config.h" #include "mqtt_client.h" -#include "nx_api.h" +#include "nxd_uprotocol.h" #include "powertrain_display.h" +#include "sntp_client.h" +#include "stm32f4xx_hal.h" #include "telemetry.h" +#include "uprotocol_config.h" #include "wwd_networking.h" -#include +#include #include -// Helper function. -#define STRLEN(p) (sizeof(p) - 1) +#define STRLEN(s) (sizeof(s) - 1u) +#define UP_WAIT (5u * TX_TIMER_TICKS_PER_SECOND) +#define UP_POLL ((TX_TIMER_TICKS_PER_SECOND + 9u) / 10u) -/* Declare the MQTT thread stack space. */ +/* Large control blocks and buffers stay off the application thread stack. */ static ULONG mqtt_client_stack[MQTT_CLIENT_STACK_SIZE / sizeof(ULONG)]; - -/* Declare buffers to hold message and topic. */ -static UCHAR message_buffer[NXD_MQTT_MAX_MESSAGE_LENGTH + 1]; -static UCHAR topic_buffer[NXD_MQTT_MAX_TOPIC_NAME_LENGTH + 1]; - - -/* Declare the MQTT client control block. */ static NXD_MQTT_CLIENT mqtt_client; - - -/* Declare the disconnect notify function. */ -static VOID client_disconnect_func(NXD_MQTT_CLIENT *client_ptr) +static NXD_UPROTOCOL_CLIENT transport; +static NXD_UPROTOCOL_CONFIG configuration; +static NXD_UPROTOCOL_MESSAGE outgoing; +static NXD_UPROTOCOL_URI telemetry_uri; +static NXD_UPROTOCOL_URI powertrain_uri; +static UCHAR receive_buffer[UPROTOCOL_RECEIVE_SIZE]; +static CHAR telemetry_buffer[TELEMETRY_BUFFER_SIZE]; +static RNG_HandleTypeDef uuid_rng; + +/* The network startup has synchronized this clock before transport creation. */ +static uint64_t epoch_milliseconds(VOID *context) { - NX_PARAMETER_NOT_USED(client_ptr); - printf("client disconnected from broker.\r\n"); + NX_PARAMETER_NOT_USED(context); + return sntp_time_get_ms(); } -static void send_message(){ - UINT status; - - /* Publish a message with QoS Level 1. */ - char buffer[TELEMETRY_BUFFER_SIZE] = {0}; - get_current_telemetry_string(buffer); - //printf("%s", buffer); +/* Generate fresh UUID entropy; never substitute predictable bytes on failure. + * This handle is used only by the MQTT application thread. */ +static UINT uuid_random(UCHAR bytes[10]) +{ + UINT i; + uint32_t word = 0; + for (i = 0; i < 10u; i++) + { + if ((i % 4u) == 0u && HAL_RNG_GenerateRandomNumber(&uuid_rng, &word) != HAL_OK) + { + return NXD_UPROTOCOL_INTERNAL; + } + bytes[i] = (UCHAR)(word >> (8u * (i % 4u))); + } + return NXD_UPROTOCOL_OK; +} - status = nxd_mqtt_client_publish(&mqtt_client, MQTT_PUBLISH_TOPIC, STRLEN(MQTT_PUBLISH_TOPIC), - (CHAR *)buffer, strlen(buffer), 0, QOS1, NX_WAIT_FOREVER); +/* Listener payload is a bounded borrowed byte span, not a C string. */ +static VOID powertrain_received(const NXD_UPROTOCOL_MESSAGE *message, VOID *context) +{ + bool displayed; + NX_PARAMETER_NOT_USED(context); + if (message->type != NXD_UPROTOCOL_PUBLISH || + message->payload_format != NXD_UPROTOCOL_PAYLOAD_JSON) + { + printf("Ignoring powertrain message without JSON publish attributes\r\n"); + return; + } + if (tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER) != TX_SUCCESS) + { + return; + } + displayed = powertrain_display_update((const char *)message->payload, message->payload_length); + tx_mutex_put(&mqtt_i2c_mutex); + if (!displayed) + { + printf("Ignoring powertrain JSON without a valid Mode string\r\n"); + } +} - if (status != NXD_MQTT_SUCCESS){ - printf("Publish failed with code: %d\r\n", status); +/* JSON content stays unchanged; uProtocol supplies identity, UUID and TTL. */ +static UINT send_telemetry(VOID) +{ + UCHAR entropy[10]; + UINT status = uuid_random(entropy); + if (status != NXD_UPROTOCOL_OK) + { + return status; } - else{ - printf("Published message.\r\n"); + memset(&outgoing, 0, sizeof(outgoing)); + memset(telemetry_buffer, 0, sizeof(telemetry_buffer)); + get_current_telemetry_string(telemetry_buffer); + status = nxd_uprotocol_uuid_create(&outgoing.id, epoch_milliseconds(NX_NULL), entropy); + if (status != NXD_UPROTOCOL_OK) + { + return status; } + outgoing.type = NXD_UPROTOCOL_PUBLISH; + outgoing.source = telemetry_uri; + outgoing.priority = NXD_UPROTOCOL_CS1; + outgoing.present = NXD_UPROTOCOL_HAS_TTL; + outgoing.ttl = UPROTOCOL_TELEMETRY_TTL_MS; + outgoing.payload_format = NXD_UPROTOCOL_PAYLOAD_JSON; + outgoing.payload = (const UCHAR *)telemetry_buffer; + outgoing.payload_length = (UINT)strlen(telemetry_buffer); + return nxd_uprotocol_send(&transport, &outgoing, UP_WAIT); } -static void receive_message(){ +/* Polling services both incoming publications and the addon's reconnect timer. + * Each pass drains a bounded batch so incoming traffic cannot starve telemetry. */ +static VOID transport_loop(VOID) +{ + UINT registered = 0; + UINT pending = 0; + UINT previous_status = NXD_UPROTOCOL_OK; UINT status; - UINT topic_length, message_length; - - /* Notifications may cover several messages; drain them all. Leave room - * for terminators when receiving a maximum-length topic or payload. */ - while (1){ - status = nxd_mqtt_client_message_get(&mqtt_client, topic_buffer, sizeof(topic_buffer) - 1, &topic_length, - message_buffer, sizeof(message_buffer) - 1, &message_length); - if (status == NXD_MQTT_NO_MESSAGE){ - return; + UINT i; + ULONG events; + for (;;) + { + events = 0; + tx_event_flags_get(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR_CLEAR, &events, UP_POLL); + if (events & MQTT_MESSAGE_READY) + { + pending = 1; + } + for (i = 0; i < 4u; i++) + { + status = nxd_uprotocol_process(&transport, receive_buffer, sizeof(receive_buffer), UP_WAIT); + if (status == NXD_UPROTOCOL_NOT_FOUND) + { + status = NXD_UPROTOCOL_OK; + break; + } + if (status != NXD_UPROTOCOL_OK) + { + break; + } } - if (status != NXD_MQTT_SUCCESS){ - printf("MQTT receive failed with code: %d\r\n", status); - return; + if (status != NXD_UPROTOCOL_OK && status != previous_status) + { + printf("uProtocol process: UCode=%u MQTT=0x%x\r\n", status, transport.last_mqtt_status); } - topic_buffer[topic_length] = '\0'; - message_buffer[message_length] = '\0'; - printf("Topic: %s, Message: %s\r\n", topic_buffer, message_buffer); - - if (topic_length == STRLEN(MQTT_SUBSCRIBE_TOPIC) && - memcmp(topic_buffer, MQTT_SUBSCRIBE_TOPIC, topic_length) == 0){ - tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); - bool displayed = powertrain_display_update((const char *)message_buffer, message_length); - tx_mutex_put(&mqtt_i2c_mutex); - if (!displayed){ - printf("Ignoring powertrain message without a valid Mode string\r\n"); + previous_status = status; + if (!transport.connected) + { + continue; + } + if (!registered) + { + status = nxd_uprotocol_register_listener(&transport, &powertrain_uri, NX_NULL, + powertrain_received, NX_NULL, UP_WAIT); + if (status != NXD_UPROTOCOL_OK) + { + printf("uProtocol listener registration failed: %u\r\n", status); + tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND); + continue; } + registered = 1; + printf("Listening for %s\r\n", UPROTOCOL_POWERTRAIN_URI); + } + if (pending) + { + /* A failed send may have reached the broker; publish fresh sensor + * data with a fresh UUID on the next telemetry event. */ + pending = 0; + status = send_telemetry(); + printf("uProtocol telemetry: UCode=%u\r\n", status); } } } -static VOID client_notify_func(NXD_MQTT_CLIENT *client_ptr, UINT number_of_messages) +/* Attach the transport to an otherwise unused MQTT client. The addon owns its + * acknowledgement/disconnect callbacks; do not install raw MQTT callbacks. */ +static VOID mqtt_thread_work(NX_IP *ip, NX_PACKET_POOL *pool) { - NX_PARAMETER_NOT_USED(client_ptr); - NX_PARAMETER_NOT_USED(number_of_messages); - tx_event_flags_set(&mqtt_app_flag, MQTT_RECEIVE_EVENT, TX_OR); - return; -} - -static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ UINT status; - NXD_ADDRESS server_ip; - ULONG events; - - printf("Creating MQTT client\r\n"); - /* Create MQTT client instance. */ - status = nxd_mqtt_client_create(&mqtt_client, MQTT_CLIENT_NAME, MQTT_CLIENT_NAME, STRLEN(MQTT_CLIENT_NAME), - ip_ptr, pool_ptr, (VOID *)mqtt_client_stack, sizeof(mqtt_client_stack), - MQTT_THREAD_PRIORTY, NX_NULL, 0); - - if (status){ - printf("Error in creating MQTT client: 0x%02x\n", status); + memset(&configuration, 0, sizeof(configuration)); + if (nxd_uprotocol_uri_parse(UPROTOCOL_LOCAL_URI, &configuration.local_uri) || + nxd_uprotocol_uri_parse(UPROTOCOL_TELEMETRY_URI, &telemetry_uri) || + nxd_uprotocol_uri_parse(UPROTOCOL_POWERTRAIN_URI, &powertrain_uri)) + { + printf("Invalid uProtocol URI configuration\r\n"); return; } - - printf(" MQTT client created\r\n"); - - /* Register the disconnect notification function. */ - nxd_mqtt_client_disconnect_notify_set(&mqtt_client, client_disconnect_func); - - /* Install the callback before subscribing: retained messages can arrive - * as soon as the broker receives the subscription. */ - status = nxd_mqtt_client_receive_notify_set(&mqtt_client, client_notify_func); - if (status != NXD_MQTT_SUCCESS){ - printf("MQTT receive notify setup failed with code: %d\r\n", status); + configuration.mode = NXD_UPROTOCOL_E2E; + configuration.server.nxd_ip_version = NX_IP_VERSION_V4; + configuration.server.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; + configuration.port = MQTT_BROKER_PORT; + configuration.keepalive = MQTT_KEEP_ALIVE_TIMER; + configuration.clean_start = 1; + configuration.session_expiry = 0; + configuration.maximum_packet_size = UPROTOCOL_MAXIMUM_PACKET_SIZE; + configuration.clock = epoch_milliseconds; + + __HAL_RCC_RNG_CLK_ENABLE(); + uuid_rng.Instance = RNG; + if (HAL_RNG_Init(&uuid_rng) != HAL_OK) + { + printf("UUID RNG initialization failed\r\n"); return; } - - server_ip.nxd_ip_version = 4; - server_ip.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; - - /* Connect over plain MQTT without setting a username or password. */ - - status = nxd_mqtt_client_connect(&mqtt_client, &server_ip, MQTT_BROKER_PORT, - MQTT_KEEP_ALIVE_TIMER, 0, NX_WAIT_FOREVER); - if (status != NXD_MQTT_SUCCESS){ - printf("MQTT connect failed with code: %d\r\n", status); - return; - } - else{ - printf("MQTT Client connected.\r\n"); - } - - /* Subscribe to the topic with QoS level 0. */ - status = nxd_mqtt_client_subscribe(&mqtt_client, MQTT_SUBSCRIBE_TOPIC, STRLEN(MQTT_SUBSCRIBE_TOPIC), QOS0); - if (status != NXD_MQTT_SUCCESS){ - printf("MQTT subscribe failed with code: %d\r\n", status); - return; - } - else{ - printf("Subscribed to topic %s.\r\n", MQTT_SUBSCRIBE_TOPIC); + status = nxd_mqtt_client_create(&mqtt_client, MQTT_CLIENT_NAME, MQTT_CLIENT_NAME, + STRLEN(MQTT_CLIENT_NAME), ip, pool, + mqtt_client_stack, sizeof(mqtt_client_stack), + MQTT_THREAD_PRIORTY, NX_NULL, 0); + if (status != NX_SUCCESS) + { + printf("MQTT create failed: 0x%x\r\n", status); + HAL_RNG_DeInit(&uuid_rng); + return; } - - /* Now wait for the broker to publish the message. */ - printf("Waiting for messages\r\n"); - - while (1){ - tx_event_flags_get(&mqtt_app_flag, MQTT_ALL_EVENTS, TX_OR_CLEAR, &events, TX_WAIT_FOREVER); - if (events & MQTT_RECEIVE_EVENT){ - receive_message(); - - } - if (events & MQTT_MESSAGE_READY){ - send_message(); - } + status = nxd_uprotocol_create(&transport, &mqtt_client, &configuration); + if (status != NXD_UPROTOCOL_OK) + { + printf("uProtocol create failed: %u\r\n", status); + nxd_mqtt_client_delete(&mqtt_client); + HAL_RNG_DeInit(&uuid_rng); + return; } - - /* Cleanup. Release resources. */ - nxd_mqtt_client_unsubscribe(&mqtt_client, MQTT_SUBSCRIBE_TOPIC, STRLEN(MQTT_SUBSCRIBE_TOPIC)); - nxd_mqtt_client_disconnect(&mqtt_client); - nxd_mqtt_client_delete(&mqtt_client); - - return; + status = nxd_uprotocol_connect(&transport, UP_WAIT); + printf("uProtocol MQTT 5 connect: UCode=%u; telemetry=%s\r\n", status, UPROTOCOL_TELEMETRY_URI); + /* A failed initial connection is retried by process with backoff. */ + transport_loop(); } -void mqtt_thread_entry(ULONG parameter){ - +/* Reuse the demo's Wi-Fi, DHCP, DNS and SNTP startup before using uProtocol. */ +void mqtt_thread_entry(ULONG parameter) +{ UINT status; - - printf("Starting Eclipse ThreadX MQTT thread\r\n\r\n"); - + NX_PARAMETER_NOT_USED(parameter); + printf("Starting Eclipse ThreadX uProtocol MQTT 5 demo\r\n"); tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); powertrain_display_waiting(); tx_mutex_put(&mqtt_i2c_mutex); - - // Initialize the network - if ((status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE))){ - printf("ERROR: Failed to initialize the network (0x%08x)\r\n", status); + status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE); + if (status == NX_SUCCESS) + { + status = wwd_network_connect(); + } + if (status != NX_SUCCESS || sntp_time_get_ms() == 0) + { + printf("Network/time initialization failed: 0x%x\r\n", status); + return; } - - wwd_network_connect(); - mqtt_thread_work(&nx_ip, nx_pool); } diff --git a/MXChip/AZ3166/app/mqtt/mqtt_client.h b/MXChip/AZ3166/app/mqtt/mqtt_client.h index 7c0378b5..af054a71 100644 --- a/MXChip/AZ3166/app/mqtt/mqtt_client.h +++ b/MXChip/AZ3166/app/mqtt/mqtt_client.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2025 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -10,31 +11,15 @@ * Contributors: * Frédéric Desbiens - Initial version. */ - -#include "nxd_mqtt_client.h" - +// Portions of this file were generated with AI assistance. #ifndef MQTT_CLIENT_H #define MQTT_CLIENT_H -/* Declare event flag, which is used in this demo. */ -#endif -#undef NXD_MQTT_MAX_TOPIC_NAME_LENGTH -#undef NXD_MQTT_MAX_MESSAGE_LENGTH -#define NXD_MQTT_MAX_TOPIC_NAME_LENGTH 70 -#define NXD_MQTT_MAX_MESSAGE_LENGTH 170 +#include "tx_api.h" #define MQTT_CLIENT_STACK_SIZE 5120 - - -void mqtt_thread_entry(ULONG thread_input); - -/* Define the symbol for signaling a received message. */ - -/* Define the priority of the MQTT internal thread. */ #define MQTT_THREAD_PRIORTY 2 - -/* Define the MQTT keep alive timer for 5 minutes */ #define MQTT_KEEP_ALIVE_TIMER 300 +void mqtt_thread_entry(ULONG thread_input); -#define QOS0 0 -#define QOS1 1 +#endif diff --git a/MXChip/AZ3166/app/mqtt/uprotocol_config.h b/MXChip/AZ3166/app/mqtt/uprotocol_config.h new file mode 100644 index 00000000..9e4bb52c --- /dev/null +++ b/MXChip/AZ3166/app/mqtt/uprotocol_config.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0). + * Human review and verification are required before submission. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef UPROTOCOL_CONFIG_H +#define UPROTOCOL_CONFIG_H + +/* Demo identities; coordinate these with the powertrain publisher. + * Telemetry entity/version/authority must match the local identity. */ +#ifndef UPROTOCOL_LOCAL_URI +#define UPROTOCOL_LOCAL_URI "up://az3166/AB/1/0" +#endif +#ifndef UPROTOCOL_TELEMETRY_URI +#define UPROTOCOL_TELEMETRY_URI "up://az3166/AB/1/8001" +#endif +#ifndef UPROTOCOL_POWERTRAIN_URI +#define UPROTOCOL_POWERTRAIN_URI "up://powertrain/BC/1/8001" +#endif +#define UPROTOCOL_TELEMETRY_TTL_MS 10000u +#define UPROTOCOL_RECEIVE_SIZE 512u +#define UPROTOCOL_MAXIMUM_PACKET_SIZE 2048u + +#endif From 805aabf821adfe15171b88acf64af45237c44448 Mon Sep 17 00:00:00 2001 From: justinhrpr-cmi Date: Wed, 7 Oct 2026 21:09:10 -0400 Subject: [PATCH 3/5] restore original mqtt example --- MXChip/AZ3166/app/mqtt/cloud_config.h | 16 +- MXChip/AZ3166/app/mqtt/main.c | 38 ++- MXChip/AZ3166/app/mqtt/mqtt_client.c | 347 ++++++++++---------------- MXChip/AZ3166/app/mqtt/mqtt_client.h | 25 +- MXChip/AZ3166/app/mqtt/telemetry.c | 7 +- MXChip/AZ3166/app/mqtt/telemetry.h | 4 +- 6 files changed, 183 insertions(+), 254 deletions(-) diff --git a/MXChip/AZ3166/app/mqtt/cloud_config.h b/MXChip/AZ3166/app/mqtt/cloud_config.h index 6b9b4dab..5e24cdcf 100644 --- a/MXChip/AZ3166/app/mqtt/cloud_config.h +++ b/MXChip/AZ3166/app/mqtt/cloud_config.h @@ -1,7 +1,6 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation - * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -13,7 +12,6 @@ * Microsoft - Initial version * Frédéric Desbiens - 2024 version. */ -// Portions of this file were generated with AI assistance. #ifndef _CLOUD_CONFIG_H #define _CLOUD_CONFIG_H @@ -32,25 +30,23 @@ typedef enum // WiFi connection config // ---------------------------------------------------------------------------- #define HOSTNAME "eclipse-threadx" //Change to unique hostname. -#define WIFI_SSID "SDV-imx95-A" -#define WIFI_PASSWORD "eclipsesdv" +#define WIFI_SSID "" +#define WIFI_PASSWORD "" #define WIFI_MODE WPA2_PSK_AES // ---------------------------------------------------------------------------- // MQTT Config // ---------------------------------------------------------------------------- #define MQTT_CLIENT_NAME "ThreadXAZ3166" //Change to unique name. -// imx95-a.local: use its IPv4 address directly; no username or password required. -#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(192, 168, 60, 1)) -#define MQTT_BROKER_PORT 1883 -#define MQTT_SUBSCRIBE_TOPIC "vehicle/powertrain/mode" +// Use test.mosquitto.org in a pinch. +#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(5, 196, 78, 28)) +#define MQTT_SUBSCRIBE_TOPIC MQTT_CLIENT_NAME "/incoming" #define MQTT_PUBLISH_TOPIC MQTT_CLIENT_NAME "/telemetry" // ---------------------------------------------------------------------------- // MQTT Support infrastructure // ---------------------------------------------------------------------------- -// The OLED and telemetry sensors share the same I2C bus. -extern TX_MUTEX mqtt_i2c_mutex; +extern TX_QUEUE mqtt_queue; extern TX_EVENT_FLAGS_GROUP mqtt_app_flag; #define MQTT_RECEIVE_EVENT 1 #define MQTT_MESSAGE_READY 2 diff --git a/MXChip/AZ3166/app/mqtt/main.c b/MXChip/AZ3166/app/mqtt/main.c index 3d367b86..65afdc62 100644 --- a/MXChip/AZ3166/app/mqtt/main.c +++ b/MXChip/AZ3166/app/mqtt/main.c @@ -1,7 +1,6 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation - * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -14,7 +13,7 @@ * Frédéric Desbiens - 2024 version. * Frédéric Desbiens - Added telemetry and MQTT. */ -// Portions of this file were generated with AI assistance. + #include #include "tx_api.h" @@ -31,33 +30,32 @@ #define ECLIPSETX_THREAD_STACK_SIZE 4096 #define ECLIPSETX_THREAD_PRIORITY 4 -#define MQTT_APP_THREAD_STACK_SIZE 8192 -TX_MUTEX mqtt_i2c_mutex; +#define BYTE_POOL_SIZE 1024 +#define QUEUE_SIZE 16 + + +TX_QUEUE mqtt_queue; TX_EVENT_FLAGS_GROUP mqtt_app_flag; +TX_BYTE_POOL byte_pool; TX_THREAD telemetry_thread; TX_THREAD mqtt_thread; ULONG telemetry_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; -ULONG mqtt_thread_stack[MQTT_APP_THREAD_STACK_SIZE / sizeof(ULONG)]; +ULONG mqtt_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; +TX_EVENT_FLAGS_GROUP mqtt_app_flag; void tx_application_define(void* first_unused_memory) { systick_interval_set(TX_TIMER_TICKS_PER_SECOND); - /* Both threads need these objects before they can start. */ - UINT status = tx_event_flags_create(&mqtt_app_flag, "MQTT event"); - if (status != TX_SUCCESS) - { - printf("ERROR: MQTT event creation failed (0x%02x)\r\n", status); - return; - } - status = tx_mutex_create(&mqtt_i2c_mutex, "MQTT sensor/display I2C", TX_INHERIT); - if (status != TX_SUCCESS) - { - printf("ERROR: I2C mutex creation failed (0x%02x)\r\n", status); - return; - } + CHAR *pointer; + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool, (VOID **)&pointer, QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by the telemetry and MQTT threads */ + tx_queue_create(&mqtt_queue, "Shared queue", TX_1_ULONG, pointer, QUEUE_SIZE*sizeof(ULONG)); // Create Telemetry thread - status = tx_thread_create(&telemetry_thread, + UINT status = tx_thread_create(&telemetry_thread, "Eclipse ThreadX telemetry Thread", telemetry_thread_entry, 0, @@ -79,7 +77,7 @@ void tx_application_define(void* first_unused_memory) mqtt_thread_entry, 0, mqtt_thread_stack, - MQTT_APP_THREAD_STACK_SIZE, + ECLIPSETX_THREAD_STACK_SIZE, ECLIPSETX_THREAD_PRIORITY, ECLIPSETX_THREAD_PRIORITY, TX_NO_TIME_SLICE, diff --git a/MXChip/AZ3166/app/mqtt/mqtt_client.c b/MXChip/AZ3166/app/mqtt/mqtt_client.c index 52c938dc..9428007b 100644 --- a/MXChip/AZ3166/app/mqtt/mqtt_client.c +++ b/MXChip/AZ3166/app/mqtt/mqtt_client.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2025 Eclipse Foundation - * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -11,250 +10,174 @@ * Contributors: * Frédéric Desbiens - Initial version. */ -// Portions of this file were generated with AI assistance. -/* MISRA C:2012/2023 Rule 15.5 deviation: early returns keep startup cleanup - * adjacent to failure checks. Rule 17.7 deviation: cleanup calls on valid - * ThreadX/HAL objects deliberately discard their return status. */ #include "cloud_config.h" #include "mqtt_client.h" -#include "nxd_uprotocol.h" -#include "powertrain_display.h" -#include "sntp_client.h" -#include "stm32f4xx_hal.h" +#include "nx_api.h" #include "telemetry.h" -#include "uprotocol_config.h" #include "wwd_networking.h" -#include +#include #include -#define STRLEN(s) (sizeof(s) - 1u) -#define UP_WAIT (5u * TX_TIMER_TICKS_PER_SECOND) -#define UP_POLL ((TX_TIMER_TICKS_PER_SECOND + 9u) / 10u) +// Helper function. +#define STRLEN(p) (sizeof(p) - 1) -/* Large control blocks and buffers stay off the application thread stack. */ +/* Declare the MQTT thread stack space. */ static ULONG mqtt_client_stack[MQTT_CLIENT_STACK_SIZE / sizeof(ULONG)]; + +/* Declare buffers to hold message and topic. */ +static UCHAR message_buffer[NXD_MQTT_MAX_MESSAGE_LENGTH]; +static UCHAR topic_buffer[NXD_MQTT_MAX_TOPIC_NAME_LENGTH]; + + +/* Declare the MQTT client control block. */ static NXD_MQTT_CLIENT mqtt_client; -static NXD_UPROTOCOL_CLIENT transport; -static NXD_UPROTOCOL_CONFIG configuration; -static NXD_UPROTOCOL_MESSAGE outgoing; -static NXD_UPROTOCOL_URI telemetry_uri; -static NXD_UPROTOCOL_URI powertrain_uri; -static UCHAR receive_buffer[UPROTOCOL_RECEIVE_SIZE]; -static CHAR telemetry_buffer[TELEMETRY_BUFFER_SIZE]; -static RNG_HandleTypeDef uuid_rng; - -/* The network startup has synchronized this clock before transport creation. */ -static uint64_t epoch_milliseconds(VOID *context) -{ - NX_PARAMETER_NOT_USED(context); - return sntp_time_get_ms(); -} -/* Generate fresh UUID entropy; never substitute predictable bytes on failure. - * This handle is used only by the MQTT application thread. */ -static UINT uuid_random(UCHAR bytes[10]) + +/* Declare the disconnect notify function. */ +static VOID client_disconnect_func(NXD_MQTT_CLIENT *client_ptr) { - UINT i; - uint32_t word = 0; - for (i = 0; i < 10u; i++) - { - if ((i % 4u) == 0u && HAL_RNG_GenerateRandomNumber(&uuid_rng, &word) != HAL_OK) - { - return NXD_UPROTOCOL_INTERNAL; - } - bytes[i] = (UCHAR)(word >> (8u * (i % 4u))); - } - return NXD_UPROTOCOL_OK; + NX_PARAMETER_NOT_USED(client_ptr); + printf("client disconnected from broker.\r\n"); } -/* Listener payload is a bounded borrowed byte span, not a C string. */ -static VOID powertrain_received(const NXD_UPROTOCOL_MESSAGE *message, VOID *context) -{ - bool displayed; - NX_PARAMETER_NOT_USED(context); - if (message->type != NXD_UPROTOCOL_PUBLISH || - message->payload_format != NXD_UPROTOCOL_PAYLOAD_JSON) - { - printf("Ignoring powertrain message without JSON publish attributes\r\n"); - return; - } - if (tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER) != TX_SUCCESS) - { - return; +static void send_message(){ + UINT status; + + /* Publish a message with QoS Level 1. */ + char buffer[160] = {0}; + get_current_telemetry_string(buffer); + //printf("%s", buffer); + + status = nxd_mqtt_client_publish(&mqtt_client, MQTT_PUBLISH_TOPIC, STRLEN(MQTT_PUBLISH_TOPIC), + (CHAR *)buffer, strlen(buffer), 0, QOS1, NX_WAIT_FOREVER); + + if (status != NXD_MQTT_SUCCESS){ + printf("Publish failed with code: %d\r\n", status); } - displayed = powertrain_display_update((const char *)message->payload, message->payload_length); - tx_mutex_put(&mqtt_i2c_mutex); - if (!displayed) - { - printf("Ignoring powertrain JSON without a valid Mode string\r\n"); + else{ + printf("Published message.\r\n"); } } -/* JSON content stays unchanged; uProtocol supplies identity, UUID and TTL. */ -static UINT send_telemetry(VOID) -{ - UCHAR entropy[10]; - UINT status = uuid_random(entropy); - if (status != NXD_UPROTOCOL_OK) - { - return status; - } - memset(&outgoing, 0, sizeof(outgoing)); - memset(telemetry_buffer, 0, sizeof(telemetry_buffer)); - get_current_telemetry_string(telemetry_buffer); - status = nxd_uprotocol_uuid_create(&outgoing.id, epoch_milliseconds(NX_NULL), entropy); - if (status != NXD_UPROTOCOL_OK) - { - return status; +static void receive_message(){ + UINT status; + UINT topic_length, message_length; + ULONG message_sent = 0; + + status = nxd_mqtt_client_message_get(&mqtt_client, topic_buffer, sizeof(topic_buffer), &topic_length, + message_buffer, sizeof(message_buffer), &message_length); + printf("Received message and status: %d \r\n", status); + if (status == NXD_MQTT_SUCCESS){ + topic_buffer[topic_length] = 0; + message_buffer[message_length] = 0; + message_sent = message_buffer[0]; + status = tx_queue_send(&mqtt_queue, &message_sent, TX_WAIT_FOREVER); + printf("Topic: %s, Message: %s\r\n", topic_buffer, message_buffer); } - outgoing.type = NXD_UPROTOCOL_PUBLISH; - outgoing.source = telemetry_uri; - outgoing.priority = NXD_UPROTOCOL_CS1; - outgoing.present = NXD_UPROTOCOL_HAS_TTL; - outgoing.ttl = UPROTOCOL_TELEMETRY_TTL_MS; - outgoing.payload_format = NXD_UPROTOCOL_PAYLOAD_JSON; - outgoing.payload = (const UCHAR *)telemetry_buffer; - outgoing.payload_length = (UINT)strlen(telemetry_buffer); - return nxd_uprotocol_send(&transport, &outgoing, UP_WAIT); } -/* Polling services both incoming publications and the addon's reconnect timer. - * Each pass drains a bounded batch so incoming traffic cannot starve telemetry. */ -static VOID transport_loop(VOID) +static VOID client_notify_func(NXD_MQTT_CLIENT *client_ptr, UINT number_of_messages) { - UINT registered = 0; - UINT pending = 0; - UINT previous_status = NXD_UPROTOCOL_OK; + NX_PARAMETER_NOT_USED(client_ptr); + NX_PARAMETER_NOT_USED(number_of_messages); + tx_event_flags_set(&mqtt_app_flag, MQTT_RECEIVE_EVENT, TX_OR); + return; +} + +static ULONG error_count; +static void mqtt_thread_work(NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr){ UINT status; - UINT i; + NXD_ADDRESS server_ip; ULONG events; - for (;;) - { - events = 0; - tx_event_flags_get(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR_CLEAR, &events, UP_POLL); - if (events & MQTT_MESSAGE_READY) - { - pending = 1; - } - for (i = 0; i < 4u; i++) - { - status = nxd_uprotocol_process(&transport, receive_buffer, sizeof(receive_buffer), UP_WAIT); - if (status == NXD_UPROTOCOL_NOT_FOUND) - { - status = NXD_UPROTOCOL_OK; - break; - } - if (status != NXD_UPROTOCOL_OK) - { - break; - } - } - if (status != NXD_UPROTOCOL_OK && status != previous_status) - { - printf("uProtocol process: UCode=%u MQTT=0x%x\r\n", status, transport.last_mqtt_status); - } - previous_status = status; - if (!transport.connected) - { - continue; - } - if (!registered) - { - status = nxd_uprotocol_register_listener(&transport, &powertrain_uri, NX_NULL, - powertrain_received, NX_NULL, UP_WAIT); - if (status != NXD_UPROTOCOL_OK) - { - printf("uProtocol listener registration failed: %u\r\n", status); - tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND); - continue; - } - registered = 1; - printf("Listening for %s\r\n", UPROTOCOL_POWERTRAIN_URI); - } - if (pending) - { - /* A failed send may have reached the broker; publish fresh sensor - * data with a fresh UUID on the next telemetry event. */ - pending = 0; - status = send_telemetry(); - printf("uProtocol telemetry: UCode=%u\r\n", status); - } + + printf("Creating MQTT client\r\n"); + /* Create MQTT client instance. */ + status = nxd_mqtt_client_create(&mqtt_client, MQTT_CLIENT_NAME, MQTT_CLIENT_NAME, STRLEN(MQTT_CLIENT_NAME), + ip_ptr, pool_ptr, (VOID *)mqtt_client_stack, sizeof(mqtt_client_stack), + MQTT_THREAD_PRIORTY, NX_NULL, 0); + + if (status){ + printf("Error in creating MQTT client: 0x%02x\n", status); + error_count++; } -} -/* Attach the transport to an otherwise unused MQTT client. The addon owns its - * acknowledgement/disconnect callbacks; do not install raw MQTT callbacks. */ -static VOID mqtt_thread_work(NX_IP *ip, NX_PACKET_POOL *pool) -{ - UINT status; - memset(&configuration, 0, sizeof(configuration)); - if (nxd_uprotocol_uri_parse(UPROTOCOL_LOCAL_URI, &configuration.local_uri) || - nxd_uprotocol_uri_parse(UPROTOCOL_TELEMETRY_URI, &telemetry_uri) || - nxd_uprotocol_uri_parse(UPROTOCOL_POWERTRAIN_URI, &powertrain_uri)) - { - printf("Invalid uProtocol URI configuration\r\n"); - return; + printf(" MQTT client created\r\n"); + + /* Register the disconnect notification function. */ + nxd_mqtt_client_disconnect_notify_set(&mqtt_client, client_disconnect_func); + + /* Create an event flag for this demo. */ + status = tx_event_flags_create(&mqtt_app_flag, "MQTT event"); + if (status) + error_count++; + + server_ip.nxd_ip_version = 4; + server_ip.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; + + /* Start the connection to the server. */ + + status = nxd_mqtt_client_connect(&mqtt_client, &server_ip, NXD_MQTT_PORT, + MQTT_KEEP_ALIVE_TIMER, 0, NX_WAIT_FOREVER); + if (status != NXD_MQTT_SUCCESS){ + printf("MQTT connect failed with code: %d\r\n", status); } - configuration.mode = NXD_UPROTOCOL_E2E; - configuration.server.nxd_ip_version = NX_IP_VERSION_V4; - configuration.server.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; - configuration.port = MQTT_BROKER_PORT; - configuration.keepalive = MQTT_KEEP_ALIVE_TIMER; - configuration.clean_start = 1; - configuration.session_expiry = 0; - configuration.maximum_packet_size = UPROTOCOL_MAXIMUM_PACKET_SIZE; - configuration.clock = epoch_milliseconds; - - __HAL_RCC_RNG_CLK_ENABLE(); - uuid_rng.Instance = RNG; - if (HAL_RNG_Init(&uuid_rng) != HAL_OK) - { - printf("UUID RNG initialization failed\r\n"); - return; + else{ + printf("MQTT Client connected.\r\n"); } - status = nxd_mqtt_client_create(&mqtt_client, MQTT_CLIENT_NAME, MQTT_CLIENT_NAME, - STRLEN(MQTT_CLIENT_NAME), ip, pool, - mqtt_client_stack, sizeof(mqtt_client_stack), - MQTT_THREAD_PRIORTY, NX_NULL, 0); - if (status != NX_SUCCESS) - { - printf("MQTT create failed: 0x%x\r\n", status); - HAL_RNG_DeInit(&uuid_rng); - return; + + /* Subscribe to the topic with QoS level 0. */ + status = nxd_mqtt_client_subscribe(&mqtt_client, MQTT_SUBSCRIBE_TOPIC, STRLEN(MQTT_SUBSCRIBE_TOPIC), QOS0); + if (status != NXD_MQTT_SUCCESS){ + printf("MQTT subscribe failed with code: %d\r\n", status); + } + else{ + printf("Subscribed to topic %s.\r\n", MQTT_SUBSCRIBE_TOPIC); + } + + /* Set the receive notify function. */ + status = nxd_mqtt_client_receive_notify_set(&mqtt_client, client_notify_func); + if (status != NXD_MQTT_SUCCESS){ + printf("MQTT receive notify setup failed with code: %d\r\n", status); + } + else{ + printf("MQTT Receive notify function set.\r\n"); } - status = nxd_uprotocol_create(&transport, &mqtt_client, &configuration); - if (status != NXD_UPROTOCOL_OK) - { - printf("uProtocol create failed: %u\r\n", status); - nxd_mqtt_client_delete(&mqtt_client); - HAL_RNG_DeInit(&uuid_rng); - return; + + /* Now wait for the broker to publish the message. */ + printf("Waiting for messages\r\n"); + + while (1){ + tx_event_flags_get(&mqtt_app_flag, MQTT_ALL_EVENTS, TX_OR_CLEAR, &events, TX_WAIT_FOREVER); + if (events & MQTT_RECEIVE_EVENT){ + receive_message(); + + } + else if (events & MQTT_MESSAGE_READY){ + send_message(); + } } - status = nxd_uprotocol_connect(&transport, UP_WAIT); - printf("uProtocol MQTT 5 connect: UCode=%u; telemetry=%s\r\n", status, UPROTOCOL_TELEMETRY_URI); - /* A failed initial connection is retried by process with backoff. */ - transport_loop(); + + /* Cleanup. Release resources. */ + nxd_mqtt_client_unsubscribe(&mqtt_client, MQTT_SUBSCRIBE_TOPIC, STRLEN(MQTT_SUBSCRIBE_TOPIC)); + nxd_mqtt_client_disconnect(&mqtt_client); + nxd_mqtt_client_delete(&mqtt_client); + + return; } -/* Reuse the demo's Wi-Fi, DHCP, DNS and SNTP startup before using uProtocol. */ -void mqtt_thread_entry(ULONG parameter) -{ +void mqtt_thread_entry(ULONG parameter){ + UINT status; - NX_PARAMETER_NOT_USED(parameter); - printf("Starting Eclipse ThreadX uProtocol MQTT 5 demo\r\n"); - tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); - powertrain_display_waiting(); - tx_mutex_put(&mqtt_i2c_mutex); - status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE); - if (status == NX_SUCCESS) - { - status = wwd_network_connect(); - } - if (status != NX_SUCCESS || sntp_time_get_ms() == 0) - { - printf("Network/time initialization failed: 0x%x\r\n", status); - return; + + printf("Starting Eclipse ThreadX MQTT thread\r\n\r\n"); + + // Initialize the network + if ((status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE))){ + printf("ERROR: Failed to initialize the network (0x%08x)\r\n", status); } + + wwd_network_connect(); + mqtt_thread_work(&nx_ip, nx_pool); } diff --git a/MXChip/AZ3166/app/mqtt/mqtt_client.h b/MXChip/AZ3166/app/mqtt/mqtt_client.h index af054a71..7c0378b5 100644 --- a/MXChip/AZ3166/app/mqtt/mqtt_client.h +++ b/MXChip/AZ3166/app/mqtt/mqtt_client.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2025 Eclipse Foundation - * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -11,15 +10,31 @@ * Contributors: * Frédéric Desbiens - Initial version. */ -// Portions of this file were generated with AI assistance. + +#include "nxd_mqtt_client.h" + #ifndef MQTT_CLIENT_H #define MQTT_CLIENT_H +/* Declare event flag, which is used in this demo. */ +#endif -#include "tx_api.h" +#undef NXD_MQTT_MAX_TOPIC_NAME_LENGTH +#undef NXD_MQTT_MAX_MESSAGE_LENGTH +#define NXD_MQTT_MAX_TOPIC_NAME_LENGTH 70 +#define NXD_MQTT_MAX_MESSAGE_LENGTH 170 #define MQTT_CLIENT_STACK_SIZE 5120 + + +void mqtt_thread_entry(ULONG thread_input); + +/* Define the symbol for signaling a received message. */ + +/* Define the priority of the MQTT internal thread. */ #define MQTT_THREAD_PRIORTY 2 + +/* Define the MQTT keep alive timer for 5 minutes */ #define MQTT_KEEP_ALIVE_TIMER 300 -void mqtt_thread_entry(ULONG thread_input); -#endif +#define QOS0 0 +#define QOS1 1 diff --git a/MXChip/AZ3166/app/mqtt/telemetry.c b/MXChip/AZ3166/app/mqtt/telemetry.c index 8bf0c4db..707925a0 100644 --- a/MXChip/AZ3166/app/mqtt/telemetry.c +++ b/MXChip/AZ3166/app/mqtt/telemetry.c @@ -26,6 +26,7 @@ static sensor_data current_sensor_data; // Telemetry output static const int TELEMETRY_ROWS = 5; static const int TELEMETRY_ROW_SIZE = 40; +const int TELEMETRY_BUFFER_SIZE = 256; /* Function to compare two float arrays * Returns true if arrays are equal within the given tolerance, otherwise false. @@ -107,8 +108,7 @@ void telemetry_thread_entry(ULONG parameter) while(1){ - // Serialize sensor reads with OLED transfers on the same I2C bus. - tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); + // Acquire fresh data. lps22hb_t lps22hb_data = lps22hb_data_read(); new_sensor_data.temperature_degC = lps22hb_data.temperature_degC; new_sensor_data.pressure_hPa = lps22hb_data.pressure_hPa; @@ -122,15 +122,14 @@ void telemetry_thread_entry(ULONG parameter) memcpy(new_sensor_data.magnetic_mG, lis2mdl_data.magnetic_mG, sizeof(lis2mdl_data.magnetic_mG)); - tx_mutex_put(&mqtt_i2c_mutex); if (data_changed(¤t_sensor_data, &new_sensor_data)){ #ifdef LOG_TELEMETRY printf("Telemetry changed.\r\n"); print_sensor_data(new_sensor_data); #endif - current_sensor_data = new_sensor_data; tx_event_flags_set(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR); + current_sensor_data = new_sensor_data; } #ifdef LOG_TELEMETRY else{ diff --git a/MXChip/AZ3166/app/mqtt/telemetry.h b/MXChip/AZ3166/app/mqtt/telemetry.h index fef22fef..9dd795a4 100644 --- a/MXChip/AZ3166/app/mqtt/telemetry.h +++ b/MXChip/AZ3166/app/mqtt/telemetry.h @@ -19,8 +19,6 @@ #ifndef _TELEMETRY_H #define _TELEMETRY_H -#define TELEMETRY_BUFFER_SIZE 256 - // Log telemetry if needed //#define LOG_TELEMETRY @@ -36,4 +34,4 @@ typedef struct{ void telemetry_thread_entry(ULONG parameter); void get_current_telemetry_string(char* output); -#endif // _TELEMETRY_H +#endif // _TELEMETRY_H \ No newline at end of file From 62c90f5f57967f89b9c21fb5b7c46c2d000c711d Mon Sep 17 00:00:00 2001 From: justinhrpr-cmi Date: Wed, 7 Oct 2026 21:11:57 -0400 Subject: [PATCH 4/5] new example using uProtocol --- .gitmodules | 2 +- MXChip/AZ3166/README.md | 5 +- MXChip/AZ3166/app/CMakeLists.txt | 13 +- .../app/uprotocol-mqtt/README-UPROTOCOL.md | 63 +++++ .../AZ3166/app/uprotocol-mqtt/UPROTOCOL.adoc | 107 ++++++++ .../AZ3166/app/uprotocol-mqtt/cloud_config.h | 57 ++++ MXChip/AZ3166/app/uprotocol-mqtt/main.c | 98 +++++++ .../AZ3166/app/uprotocol-mqtt/mqtt_client.c | 259 ++++++++++++++++++ .../AZ3166/app/uprotocol-mqtt/mqtt_client.h | 24 ++ .../powertrain_display.c | 10 +- .../powertrain_display.h | 10 +- MXChip/AZ3166/app/uprotocol-mqtt/telemetry.c | 147 ++++++++++ MXChip/AZ3166/app/uprotocol-mqtt/telemetry.h | 36 +++ .../app/uprotocol-mqtt/threadx_pnp_info.h | 36 +++ .../uprotocol_config.h | 6 +- MXChip/AZ3166/deps/lib/netxduo | 2 +- .../scripts/publish_uprotocol_powertrain.py | 68 +++++ reflash.sh | 49 ---- 18 files changed, 933 insertions(+), 59 deletions(-) create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/README-UPROTOCOL.md create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/UPROTOCOL.adoc create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/cloud_config.h create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/main.c create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.c create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.h rename MXChip/AZ3166/app/{mqtt => uprotocol-mqtt}/powertrain_display.c (88%) rename MXChip/AZ3166/app/{mqtt => uprotocol-mqtt}/powertrain_display.h (51%) create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/telemetry.c create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/telemetry.h create mode 100644 MXChip/AZ3166/app/uprotocol-mqtt/threadx_pnp_info.h rename MXChip/AZ3166/app/{mqtt => uprotocol-mqtt}/uprotocol_config.h (87%) create mode 100644 MXChip/AZ3166/scripts/publish_uprotocol_powertrain.py delete mode 100755 reflash.sh diff --git a/.gitmodules b/.gitmodules index 51d2e5a9..60cf1860 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/eclipse-threadx/threadx.git [submodule "MXChip/AZ3166/deps/lib/netxduo"] path = MXChip/AZ3166/deps/lib/netxduo - url = https://github.com/eclipse-threadx/netxduo.git + url = git@github.com:Eclipse-SDV-Hackathon-Chapter-Four/netxduo.git [submodule "libs/threadx"] path = libs/threadx url = https://github.com/eclipse-threadx/threadx.git diff --git a/MXChip/AZ3166/README.md b/MXChip/AZ3166/README.md index ea79dd30..309d3dfc 100644 --- a/MXChip/AZ3166/README.md +++ b/MXChip/AZ3166/README.md @@ -112,7 +112,7 @@ winget install --id=Kitware.CMake -e To compile the application, use the provided scripts in the `MXChip/AZ3166/scripts` folder. ### Windows (PowerShell) -You can build the application using `build.ps1`. It accepts a `-Config` parameter to select the application version (`starter`, `arcade`, `telemetry`, or `mqtt`). +You can build the application using `build.ps1`. It accepts a `-Config` parameter to select the application version (`starter`, `arcade`, `telemetry`, `mqtt`, or `uprotocol-mqtt`). ```powershell .\scripts\build.ps1 -Config starter ``` @@ -176,7 +176,8 @@ The application supports multiple configurations that you can select at build ti - `starter`: Initiates the board and WiFi connectivity. - `telemetry`: Adds code to read the on-board sensors and print the output. -- `mqtt`: Adds code to publish the telemetry over MQTT. Also creates a second thread subscribing to an MQTT topic; the received messages will be printed. (Note: This configuration builds upon `telemetry`). +- `mqtt`: The original upstream MQTT telemetry demo. +- `uprotocol-mqtt`: Publishes telemetry and receives powertrain events using uProtocol over MQTT 5. See [README-UPROTOCOL.md](MXChip/AZ3166/app/uprotocol-mqtt/README-UPROTOCOL.md) for setup and deployment. - `arcade`: A collection of arcade games. Thanks to Sébastien Heurtematte for this contribution! diff --git a/MXChip/AZ3166/app/CMakeLists.txt b/MXChip/AZ3166/app/CMakeLists.txt index 8e542b6f..7f815f7c 100644 --- a/MXChip/AZ3166/app/CMakeLists.txt +++ b/MXChip/AZ3166/app/CMakeLists.txt @@ -55,6 +55,16 @@ elseif(APP_CONFIG STREQUAL "telemetry") ) elseif(APP_CONFIG STREQUAL "mqtt") # MQTT depends on Telemetry + list(APPEND SOURCES + ${COMMON_DIR}/board_init.c + ${COMMON_DIR}/screen.c + ${CONFIG_DIR}/telemetry.c + ${CONFIG_DIR}/mqtt_client.c + ${CONFIG_DIR}/main.c + ${CONFIG_DIR}/cloud_config.h + ) +elseif(APP_CONFIG STREQUAL "uprotocol-mqtt") + # uProtocol over MQTT 5, with the powertrain display demo. list(APPEND SOURCES ${COMMON_DIR}/board_init.c ${COMMON_DIR}/screen.c @@ -63,6 +73,7 @@ elseif(APP_CONFIG STREQUAL "mqtt") ${CONFIG_DIR}/powertrain_display.c ${CONFIG_DIR}/main.c ${CONFIG_DIR}/cloud_config.h + ${CONFIG_DIR}/uprotocol_config.h ) else() # starter list(APPEND SOURCES @@ -93,7 +104,7 @@ target_include_directories(${PROJECT_NAME} ${CONFIG_DIR} ) -if(APP_CONFIG STREQUAL "mqtt") +if(APP_CONFIG STREQUAL "mqtt" OR APP_CONFIG STREQUAL "uprotocol-mqtt") # Also include telemetry directory for MQTT target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/telemetry) endif() diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/README-UPROTOCOL.md b/MXChip/AZ3166/app/uprotocol-mqtt/README-UPROTOCOL.md new file mode 100644 index 00000000..e2813999 --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/README-UPROTOCOL.md @@ -0,0 +1,63 @@ +# Build and deploy the AZ3166 MQTT demos + +SampleX has two independent AZ3166 applications: `app/mqtt` is the original +upstream MQTT 3.1.1 demo, and `app/uprotocol-mqtt` publishes and receives +uProtocol 1.6 messages over MQTT 5. The latter uses the AZ3166 NetX Duo +submodule pinned to fork commit `0edae5f` and the uProtocol specification at +up-spec commit `3130bc5bfe661f60509b9d5e1d03995d608a18b2`. + +Run these commands from the SampleX repository root. Initialize the board +submodules before building: + +```sh +git submodule update --init MXChip/AZ3166/deps/lib/threadx MXChip/AZ3166/deps/lib/netxduo +``` + +## Build + +Use the existing build script. Its `rebuild` argument clears the shared +`MXChip/AZ3166/build` directory, which is necessary when switching app +configurations. + +```sh +# Original upstream MQTT demo +MXChip/AZ3166/scripts/build.sh mqtt rebuild + +# uProtocol MQTT 5 demo (run this command when you want to deploy uProtocol) +MXChip/AZ3166/scripts/build.sh uprotocol-mqtt rebuild +``` + +Both commands produce `MXChip/AZ3166/build/app/mxchip_threadx.bin`. +Subsequent builds of the same configuration can omit `rebuild`. +The original MQTT app has its upstream defaults in `app/mqtt/cloud_config.h`. +The uProtocol app's Wi-Fi and broker settings are in +`app/uprotocol-mqtt/cloud_config.h`; its demo identities are in +`app/uprotocol-mqtt/uprotocol_config.h`. + +The imported WICED archive uses fixed offsets in NetX's `NX_IP` structure. +Keep `NX_ENABLE_EXTENDED_NOTIFY_SUPPORT` disabled in `lib/netxduo/nx_user.h`; +`app/common/wwd_networking.c` checks those offsets at compile time. + +## Deploy + +Attach the AZ3166 to WSL with `usbipd` and mount its drive at `/mnt/AZ3166`. +The existing deployment script copies the binary from the standard build +folder: + +```sh +sudo MXChip/AZ3166/scripts/deploy.sh /mnt/AZ3166 +``` + +Check its output for a copy error: the script can print success even when +`cp` fails. Wait for DAPLink to remount its drive before resetting the board. +`FAIL.TXT` on the remounted drive indicates a programming failure. +If the board disappears from WSL after a USB reset, attach it again with +`usbipd` before the next deployment. + +For serial output, open `sudo screen /dev/ttyACM0 115200` and reset the +board. Exit `screen` with `Ctrl-A`, `K`, then `Y`. + +The original MQTT demo uses its configured raw MQTT topics. The uProtocol +app publishes telemetry to `az3166/AB/0/1/8001` and receives powertrain +events on `powertrain/BC/0/1/8001`, with attributes in MQTT 5 properties. +See [UPROTOCOL.adoc](UPROTOCOL.adoc) for its peer contract and host tests. diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/UPROTOCOL.adoc b/MXChip/AZ3166/app/uprotocol-mqtt/UPROTOCOL.adoc new file mode 100644 index 00000000..1e826dfb --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/UPROTOCOL.adoc @@ -0,0 +1,107 @@ += AZ3166 uProtocol MQTT demo + +The `uprotocol-mqtt` application uses the NetX Duo uProtocol addon at up-spec commit +`3130bc5bfe661f60509b9d5e1d03995d608a18b2`, over MQTT 5 with E2E topic mapping. +The implementation was prepared with Codex (GPT-6); human review is required before submission. + +== Build and deploy + +The AZ3166 NetX Duo submodule is pinned to the uProtocol fork commit `0edae5f`. +Initialize it with `git submodule update --init MXChip/AZ3166/deps/lib/netxduo`. +From the SampleX repository root: + +[source,sh] +---- +MXChip/AZ3166/scripts/build.sh uprotocol-mqtt rebuild +---- + +Deploy with `sudo MXChip/AZ3166/scripts/deploy.sh /mnt/AZ3166` after mounting +the board. See `README-UPROTOCOL.md` for both app configurations and deployment. +Use a clean build when switching from an older NetX Duo checkout because the MQTT client control block changed. + +== Configuration and peer contract + +Wi-Fi and broker address/port are in `app/uprotocol-mqtt/cloud_config.h`. +The demo uses plain TCP and the existing no-authentication broker configuration. +It requires a reachable MQTT 5 broker and successful SNTP synchronization. + +uProtocol settings are in `app/uprotocol-mqtt/uprotocol_config.h`: + +[cols="2,3,3"] +|=== +|Purpose |uProtocol URI |MQTT topic +|Local identity |`up://az3166/AB/1/0` |Not a publication +|Telemetry source |`up://az3166/AB/1/8001` |`az3166/AB/0/1/8001` +|Powertrain mode source |`up://powertrain/BC/1/8001` |`powertrain/BC/0/1/8001` +|=== + +These are demo identifiers, not assigned production service IDs. +Coordinate changes with the peer and keep the telemetry source's authority/entity/version equal to the local identity. +The old raw MQTT topic macros are no longer used by this demo. +Telemetry JSON and the display's JSON `Mode` field are unchanged, with uProtocol payload format JSON (3). +Raw JSON on the old `vehicle/powertrain/mode` topic will not update the display. +The publisher must include uProtocol attributes as MQTT 5 properties. + +To publish a test event using installed Python and `mosquitto_pub`: + +[source,sh] +---- +python3 MXChip/AZ3166/scripts/publish_uprotocol_powertrain.py \ + --host BROKER_IP --mode Sport +---- + +`--dry-run` prints the command without connecting. `--authority`, `--entity`, +`--version` and `--resource` allow the helper to follow changed demo identities; +numeric URI fields are hexadecimal. +The helper generates a fresh UUIDv7, includes the uProtocol MQTT properties and publishes non-retained JSON with QoS 1 and a 10-second TTL. +It does not introduce a firmware dependency. + +== Runtime + +Network startup initializes Wi-Fi, DHCP, DNS and SNTP before the transport starts. +The SNTP adapter preserves the fractional timestamp and advances Unix milliseconds +using ThreadX ticks, including tick-counter rollover while called regularly. +Startup stops on time-sync failure instead of sending invalid wall-clock timestamps. +The STM32 HAL RNG supplies fresh UUID entropy; RNG errors prevent publication. + +The MQTT thread waits at most 100 ms between processing passes when idle. +Each pass drains up to four messages and services reconnect deadlines. +The addon restores the registered listener after session loss. +Telemetry events coalesce while disconnected, so reconnect publishes current sensor data. +A failed publication is reported; the next telemetry event creates a fresh message. +The display callback keeps the original I2C mutex and parses bounded JSON payloads. + +Large transport/message buffers are static. The application MQTT stack is 8 KiB; +the internal MQTT stack remains 5 KiB. The maximum MQTT packet is 2048 bytes and +the application receive buffer is 512 bytes. Oversized input is discarded with +an explicit error by the addon. Confirm stack high-water marks and packet-pool +capacity on the physical board under expected traffic. + +The code contains no additional worker thread and uses no new firmware library dependency. +Cross-compilation is not evidence of board execution; validate Wi-Fi/SNTP/RNG, +telemetry reception, display updates, stale/malformed message rejection and broker +restart/reconnection on the board before presenting this as hardware-verified. + +== Validation + +The MQTT firmware has cross-compiled with Arm GNU 13.3.1. The linker reported +the existing RWX load-segment warning (the original demo ELF has the same +segment permissions), plus serial LTO notices. The uProtocol build has reached +DHCP and DNS on the physical board; end-to-end MQTT 5/uProtocol traffic still +needs confirmation. + +Host tests check SNTP fractional conversion, frequent clock reads, 32-bit tick +rollover and resynchronization, plus the test publisher's URI/UUID/JSON/MQTT properties. +On Linux with GCC multilib, CMake, Ninja and Python available: + +[source,sh] +---- +cmake -S MXChip/AZ3166/tests/uprotocol -B MXChip/AZ3166/build-uprotocol/host-tests -G Ninja +cmake --build MXChip/AZ3166/build-uprotocol/host-tests +ctest --test-dir MXChip/AZ3166/build-uprotocol/host-tests --output-on-failure +---- + +The clock test compiles the actual SNTP source, replacing only time, interrupt +control and SNTP timestamp access; it does not simulate networking or the board RNG. +The publisher properties were also accepted by the production uProtocol decoder +in a separate host check. diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/cloud_config.h b/MXChip/AZ3166/app/uprotocol-mqtt/cloud_config.h new file mode 100644 index 00000000..9be6b688 --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/cloud_config.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: Portions of this file were generated with Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. Human review is required. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef _CLOUD_CONFIG_H +#define _CLOUD_CONFIG_H + +#include "nx_api.h" + +typedef enum +{ + None = 0, + WEP = 1, + WPA_PSK_TKIP = 2, + WPA2_PSK_AES = 3 +} WiFi_Mode; + +// ---------------------------------------------------------------------------- +// WiFi connection config +// ---------------------------------------------------------------------------- +#define HOSTNAME "eclipse-threadx" //Change to unique hostname. +#define WIFI_SSID "SDV-imx95-A" +#define WIFI_PASSWORD "eclipsesdv" +#define WIFI_MODE WPA2_PSK_AES + +// ---------------------------------------------------------------------------- +// MQTT Config +// ---------------------------------------------------------------------------- +#define MQTT_CLIENT_NAME "ThreadXAZ3166" //Change to unique name. +// imx95-a.local: use its IPv4 address directly; no username or password required. +#define MQTT_LOCAL_BROKER_IP (IP_ADDRESS(192, 168, 60, 1)) +#define MQTT_BROKER_PORT 1883 +#define MQTT_SUBSCRIBE_TOPIC "vehicle/powertrain/mode" +#define MQTT_PUBLISH_TOPIC MQTT_CLIENT_NAME "/telemetry" + +// ---------------------------------------------------------------------------- +// MQTT Support infrastructure +// ---------------------------------------------------------------------------- +// The OLED and telemetry sensors share the same I2C bus. +extern TX_MUTEX mqtt_i2c_mutex; +extern TX_EVENT_FLAGS_GROUP mqtt_app_flag; +#define MQTT_RECEIVE_EVENT 1 +#define MQTT_MESSAGE_READY 2 +#define MQTT_ALL_EVENTS 3 + + +#endif // _CLOUD_CONFIG_H diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/main.c b/MXChip/AZ3166/app/uprotocol-mqtt/main.c new file mode 100644 index 00000000..a63c7db4 --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/main.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: Portions of this file were generated with Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. Human review is required. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ +#include + +#include "tx_api.h" + +#include "board_init.h" +#include "cmsis_utils.h" +#include "mqtt_client.h" +#include "screen.h" +#include "sntp_client.h" +#include "telemetry.h" +#include "wwd_networking.h" + +#include "cloud_config.h" + +#define ECLIPSETX_THREAD_STACK_SIZE 4096 +#define ECLIPSETX_THREAD_PRIORITY 4 +#define MQTT_APP_THREAD_STACK_SIZE 8192 +TX_MUTEX mqtt_i2c_mutex; +TX_EVENT_FLAGS_GROUP mqtt_app_flag; +TX_THREAD telemetry_thread; +TX_THREAD mqtt_thread; +ULONG telemetry_thread_stack[ECLIPSETX_THREAD_STACK_SIZE / sizeof(ULONG)]; +ULONG mqtt_thread_stack[MQTT_APP_THREAD_STACK_SIZE / sizeof(ULONG)]; + +void tx_application_define(void* first_unused_memory) +{ + systick_interval_set(TX_TIMER_TICKS_PER_SECOND); + /* Both threads need these objects before they can start. */ + UINT status = tx_event_flags_create(&mqtt_app_flag, "MQTT event"); + if (status != TX_SUCCESS) + { + printf("ERROR: MQTT event creation failed (0x%02x)\r\n", status); + return; + } + status = tx_mutex_create(&mqtt_i2c_mutex, "MQTT sensor/display I2C", TX_INHERIT); + if (status != TX_SUCCESS) + { + printf("ERROR: I2C mutex creation failed (0x%02x)\r\n", status); + return; + } + + // Create Telemetry thread + status = tx_thread_create(&telemetry_thread, + "Eclipse ThreadX telemetry Thread", + telemetry_thread_entry, + 0, + telemetry_thread_stack, + ECLIPSETX_THREAD_STACK_SIZE, + ECLIPSETX_THREAD_PRIORITY, + ECLIPSETX_THREAD_PRIORITY, + TX_NO_TIME_SLICE, + TX_AUTO_START); + + if (status != TX_SUCCESS) + { + printf("ERROR: Eclipse ThreadX telemetry thread creation failed\r\n"); + } + + // Create MQTT thread + status = tx_thread_create(&mqtt_thread, + "Eclipse ThreadX MQTT Thread", + mqtt_thread_entry, + 0, + mqtt_thread_stack, + MQTT_APP_THREAD_STACK_SIZE, + ECLIPSETX_THREAD_PRIORITY, + ECLIPSETX_THREAD_PRIORITY, + TX_NO_TIME_SLICE, + TX_AUTO_START); + + if (status != TX_SUCCESS) + { + printf("ERROR: Eclipse ThreadX MQTT thread creation failed\r\n"); + }} + +int main(void) +{ + // Initialize the board + board_init(); + + // Enter the ThreadX kernel + tx_kernel_enter(); + + return 0; +} diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.c b/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.c new file mode 100644 index 00000000..85977cd2 --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.c @@ -0,0 +1,259 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: Portions of this file were generated with Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. Human review is required. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ +/* MISRA C:2012/2023 Rule 15.5 deviation: early returns keep startup cleanup + * adjacent to failure checks. Rule 17.7 deviation: cleanup calls on valid + * ThreadX/HAL objects deliberately discard their return status. */ +#include "cloud_config.h" +#include "mqtt_client.h" +#include "nxd_uprotocol.h" +#include "powertrain_display.h" +#include "sntp_client.h" +#include "stm32f4xx_hal.h" +#include "telemetry.h" +#include "uprotocol_config.h" +#include "wwd_networking.h" + +#include +#include + +#define STRLEN(s) (sizeof(s) - 1u) +#define UP_WAIT (5u * TX_TIMER_TICKS_PER_SECOND) +#define UP_POLL ((TX_TIMER_TICKS_PER_SECOND + 9u) / 10u) + +/* Large control blocks and buffers stay off the application thread stack. */ +static ULONG mqtt_client_stack[MQTT_CLIENT_STACK_SIZE / sizeof(ULONG)]; +static NXD_MQTT_CLIENT mqtt_client; +static NXD_UPROTOCOL_CLIENT transport; +static NXD_UPROTOCOL_CONFIG configuration; +static NXD_UPROTOCOL_MESSAGE outgoing; +static NXD_UPROTOCOL_URI telemetry_uri; +static NXD_UPROTOCOL_URI powertrain_uri; +static UCHAR receive_buffer[UPROTOCOL_RECEIVE_SIZE]; +static CHAR telemetry_buffer[TELEMETRY_BUFFER_SIZE]; +static RNG_HandleTypeDef uuid_rng; + +/* The network startup has synchronized this clock before transport creation. */ +static uint64_t epoch_milliseconds(VOID *context) +{ + NX_PARAMETER_NOT_USED(context); + return sntp_time_get_ms(); +} + +/* Generate fresh UUID entropy; never substitute predictable bytes on failure. + * This handle is used only by the MQTT application thread. */ +static UINT uuid_random(UCHAR bytes[10]) +{ + UINT i; + uint32_t word = 0; + for (i = 0; i < 10u; i++) + { + if ((i % 4u) == 0u && HAL_RNG_GenerateRandomNumber(&uuid_rng, &word) != HAL_OK) + { + return NXD_UPROTOCOL_INTERNAL; + } + bytes[i] = (UCHAR)(word >> (8u * (i % 4u))); + } + return NXD_UPROTOCOL_OK; +} + +/* Listener payload is a bounded borrowed byte span, not a C string. */ +static VOID powertrain_received(const NXD_UPROTOCOL_MESSAGE *message, VOID *context) +{ + bool displayed; + NX_PARAMETER_NOT_USED(context); + if (message->type != NXD_UPROTOCOL_PUBLISH || + message->payload_format != NXD_UPROTOCOL_PAYLOAD_JSON) + { + printf("Ignoring powertrain message without JSON publish attributes\r\n"); + return; + } + if (tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER) != TX_SUCCESS) + { + return; + } + displayed = powertrain_display_update((const char *)message->payload, message->payload_length); + tx_mutex_put(&mqtt_i2c_mutex); + if (!displayed) + { + printf("Ignoring powertrain JSON without a valid Mode string\r\n"); + } +} + +/* JSON content stays unchanged; uProtocol supplies identity, UUID and TTL. */ +static UINT send_telemetry(VOID) +{ + UCHAR entropy[10]; + UINT status = uuid_random(entropy); + if (status != NXD_UPROTOCOL_OK) + { + return status; + } + memset(&outgoing, 0, sizeof(outgoing)); + memset(telemetry_buffer, 0, sizeof(telemetry_buffer)); + get_current_telemetry_string(telemetry_buffer); + status = nxd_uprotocol_uuid_create(&outgoing.id, epoch_milliseconds(NX_NULL), entropy); + if (status != NXD_UPROTOCOL_OK) + { + return status; + } + outgoing.type = NXD_UPROTOCOL_PUBLISH; + outgoing.source = telemetry_uri; + outgoing.priority = NXD_UPROTOCOL_CS1; + outgoing.present = NXD_UPROTOCOL_HAS_TTL; + outgoing.ttl = UPROTOCOL_TELEMETRY_TTL_MS; + outgoing.payload_format = NXD_UPROTOCOL_PAYLOAD_JSON; + outgoing.payload = (const UCHAR *)telemetry_buffer; + outgoing.payload_length = (UINT)strlen(telemetry_buffer); + return nxd_uprotocol_send(&transport, &outgoing, UP_WAIT); +} + +/* Polling services both incoming publications and the addon's reconnect timer. + * Each pass drains a bounded batch so incoming traffic cannot starve telemetry. */ +static VOID transport_loop(VOID) +{ + UINT registered = 0; + UINT pending = 0; + UINT previous_status = NXD_UPROTOCOL_OK; + UINT status; + UINT i; + ULONG events; + for (;;) + { + events = 0; + tx_event_flags_get(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR_CLEAR, &events, UP_POLL); + if (events & MQTT_MESSAGE_READY) + { + pending = 1; + } + for (i = 0; i < 4u; i++) + { + status = nxd_uprotocol_process(&transport, receive_buffer, sizeof(receive_buffer), UP_WAIT); + if (status == NXD_UPROTOCOL_NOT_FOUND) + { + status = NXD_UPROTOCOL_OK; + break; + } + if (status != NXD_UPROTOCOL_OK) + { + break; + } + } + if (status != NXD_UPROTOCOL_OK && status != previous_status) + { + printf("uProtocol process: UCode=%u MQTT=0x%x\r\n", status, transport.last_mqtt_status); + } + previous_status = status; + if (!transport.connected) + { + continue; + } + if (!registered) + { + status = nxd_uprotocol_register_listener(&transport, &powertrain_uri, NX_NULL, + powertrain_received, NX_NULL, UP_WAIT); + if (status != NXD_UPROTOCOL_OK) + { + printf("uProtocol listener registration failed: %u\r\n", status); + tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND); + continue; + } + registered = 1; + printf("Listening for %s\r\n", UPROTOCOL_POWERTRAIN_URI); + } + if (pending) + { + /* A failed send may have reached the broker; publish fresh sensor + * data with a fresh UUID on the next telemetry event. */ + pending = 0; + status = send_telemetry(); + printf("uProtocol telemetry: UCode=%u\r\n", status); + } + } +} + +/* Attach the transport to an otherwise unused MQTT client. The addon owns its + * acknowledgement/disconnect callbacks; do not install raw MQTT callbacks. */ +static VOID mqtt_thread_work(NX_IP *ip, NX_PACKET_POOL *pool) +{ + UINT status; + memset(&configuration, 0, sizeof(configuration)); + if (nxd_uprotocol_uri_parse(UPROTOCOL_LOCAL_URI, &configuration.local_uri) || + nxd_uprotocol_uri_parse(UPROTOCOL_TELEMETRY_URI, &telemetry_uri) || + nxd_uprotocol_uri_parse(UPROTOCOL_POWERTRAIN_URI, &powertrain_uri)) + { + printf("Invalid uProtocol URI configuration\r\n"); + return; + } + configuration.mode = NXD_UPROTOCOL_E2E; + configuration.server.nxd_ip_version = NX_IP_VERSION_V4; + configuration.server.nxd_ip_address.v4 = MQTT_LOCAL_BROKER_IP; + configuration.port = MQTT_BROKER_PORT; + configuration.keepalive = MQTT_KEEP_ALIVE_TIMER; + configuration.clean_start = 1; + configuration.session_expiry = 0; + configuration.maximum_packet_size = UPROTOCOL_MAXIMUM_PACKET_SIZE; + configuration.clock = epoch_milliseconds; + + __HAL_RCC_RNG_CLK_ENABLE(); + uuid_rng.Instance = RNG; + if (HAL_RNG_Init(&uuid_rng) != HAL_OK) + { + printf("UUID RNG initialization failed\r\n"); + return; + } + status = nxd_mqtt_client_create(&mqtt_client, MQTT_CLIENT_NAME, MQTT_CLIENT_NAME, + STRLEN(MQTT_CLIENT_NAME), ip, pool, + mqtt_client_stack, sizeof(mqtt_client_stack), + MQTT_THREAD_PRIORTY, NX_NULL, 0); + if (status != NX_SUCCESS) + { + printf("MQTT create failed: 0x%x\r\n", status); + HAL_RNG_DeInit(&uuid_rng); + return; + } + status = nxd_uprotocol_create(&transport, &mqtt_client, &configuration); + if (status != NXD_UPROTOCOL_OK) + { + printf("uProtocol create failed: %u\r\n", status); + nxd_mqtt_client_delete(&mqtt_client); + HAL_RNG_DeInit(&uuid_rng); + return; + } + status = nxd_uprotocol_connect(&transport, UP_WAIT); + printf("uProtocol MQTT 5 connect: UCode=%u; telemetry=%s\r\n", status, UPROTOCOL_TELEMETRY_URI); + /* A failed initial connection is retried by process with backoff. */ + transport_loop(); +} + +/* Reuse the demo's Wi-Fi, DHCP, DNS and SNTP startup before using uProtocol. */ +void mqtt_thread_entry(ULONG parameter) +{ + UINT status; + NX_PARAMETER_NOT_USED(parameter); + printf("Starting Eclipse ThreadX uProtocol MQTT 5 demo\r\n"); + tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); + powertrain_display_waiting(); + tx_mutex_put(&mqtt_i2c_mutex); + status = wwd_network_init(WIFI_SSID, WIFI_PASSWORD, WIFI_MODE); + if (status == NX_SUCCESS) + { + status = wwd_network_connect(); + } + if (status != NX_SUCCESS || sntp_time_get_ms() == 0) + { + printf("Network/time initialization failed: 0x%x\r\n", status); + return; + } + mqtt_thread_work(&nx_ip, nx_pool); +} diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.h b/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.h new file mode 100644 index 00000000..9dbc09ce --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/mqtt_client.h @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: Portions of this file were generated with Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. Human review is required. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ +#ifndef MQTT_CLIENT_H +#define MQTT_CLIENT_H + +#include "tx_api.h" + +#define MQTT_CLIENT_STACK_SIZE 5120 +#define MQTT_THREAD_PRIORTY 2 +#define MQTT_KEEP_ALIVE_TIMER 300 +void mqtt_thread_entry(ULONG thread_input); + +#endif diff --git a/MXChip/AZ3166/app/mqtt/powertrain_display.c b/MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.c similarity index 88% rename from MXChip/AZ3166/app/mqtt/powertrain_display.c rename to MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.c index 2cb495f8..8cdf6162 100644 --- a/MXChip/AZ3166/app/mqtt/powertrain_display.c +++ b/MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.c @@ -1,4 +1,12 @@ -/* SPDX-License-Identifier: MIT */ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ #include "powertrain_display.h" #include "jsmn.h" diff --git a/MXChip/AZ3166/app/mqtt/powertrain_display.h b/MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.h similarity index 51% rename from MXChip/AZ3166/app/mqtt/powertrain_display.h rename to MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.h index 500fa8f9..8b2dbfce 100644 --- a/MXChip/AZ3166/app/mqtt/powertrain_display.h +++ b/MXChip/AZ3166/app/uprotocol-mqtt/powertrain_display.h @@ -1,4 +1,12 @@ -/* SPDX-License-Identifier: MIT */ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ #ifndef POWERTRAIN_DISPLAY_H #define POWERTRAIN_DISPLAY_H diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.c b/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.c new file mode 100644 index 00000000..3c654bdb --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.c @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "cloud_config.h" +#include "nanoprintf.h" +#include "sensor.h" +#include "telemetry.h" +#include + +// Refresh interval +static const int32_t telemetry_interval = 5; + +// Current data +static sensor_data current_sensor_data; + +// Telemetry output +static const int TELEMETRY_ROWS = 5; +static const int TELEMETRY_ROW_SIZE = 40; + +/* Function to compare two float arrays + * Returns true if arrays are equal within the given tolerance, otherwise false. + */ +static UINT compare_float_arrays(const float* arr1, const float* arr2, size_t size, float epsilon) { + if (arr1 == NULL || arr2 == NULL) { + return 0; + } + + for (size_t i = 0; i < size; ++i) { + if (fabsf((float)(arr1[i] - arr2[i])) > epsilon) { + return 0; // Found a difference outside the tolerance + } + } + return 1; // No significant differences found +} + +/** + * Check if the data set changed between readings. + * + * Proper epsilon values for physical sensor readings depend on the sensor's accuracy. + * We picked a margin of error that makes sense for the use case. + */ +static UINT data_changed(sensor_data const * const current_data, sensor_data const * const new_data){ + return !(current_data->pressure_hPa == new_data->pressure_hPa && + current_data->temperature_degC == new_data->temperature_degC && + current_data->humidity_perc == new_data->humidity_perc && + compare_float_arrays(current_data->acceleration_mg, new_data->acceleration_mg, 3, 0.1f) && + compare_float_arrays(current_data->magnetic_mG, new_data->magnetic_mG, 3, 1.0f)); // Could also be 5.0f +} + +static void get_sensor_data_buffer(sensor_data data, char* output){ + char buf[TELEMETRY_ROWS][TELEMETRY_ROW_SIZE]; + npf_snprintf(buf[0], TELEMETRY_ROW_SIZE, "Pressure: %.2f\r\n", (double)data.pressure_hPa); + npf_snprintf(buf[1], TELEMETRY_ROW_SIZE, "Temperature: %.2f\r\n", (double)data.temperature_degC); + npf_snprintf(buf[2], TELEMETRY_ROW_SIZE, "Humidity: %.2f\r\n", (double)data.humidity_perc); + npf_snprintf(buf[3], TELEMETRY_ROW_SIZE, "Acceleration: %.2f, %.2f, %.2f\r\n", + (double)data.acceleration_mg[0], + (double)data.acceleration_mg[1], + (double)data.acceleration_mg[2]); + npf_snprintf(buf[4], TELEMETRY_ROW_SIZE, "Magnetic: %.2f, %.2f, %.2f\r\n", + (double)data.magnetic_mG[0], + (double)data.magnetic_mG[1], + (double)data.magnetic_mG[2]); + + // Initialize the new array with an empty string. + output[0] = '\0'; + + // Concatenate the strings from the 2D array. + for (int i = 0; i < TELEMETRY_ROWS; i++) { + strcat(output, buf[i]); + } +} + +/** + * Only used if LOG_TELEMETRY is defined. + * + * Uncomment the definition in telemetry.h if needed. + */ +#ifdef LOG_TELEMETRY +static void print_sensor_data(sensor_data data){ + char data_string[TELEMETRY_BUFFER_SIZE]; + get_sensor_data_buffer(data, data_string); + printf("=====\r\n"); + printf("%s", data_string); + printf("=====\r\n\r\n"); +} +#endif + +/** + * Entry point for the telemetry thread. + */ +void telemetry_thread_entry(ULONG parameter) +{ + //UINT status; + sensor_data new_sensor_data; + + printf("Starting telemetry thread\r\n\r\n"); + + while(1){ + + // Serialize sensor reads with OLED transfers on the same I2C bus. + tx_mutex_get(&mqtt_i2c_mutex, TX_WAIT_FOREVER); + lps22hb_t lps22hb_data = lps22hb_data_read(); + new_sensor_data.temperature_degC = lps22hb_data.temperature_degC; + new_sensor_data.pressure_hPa = lps22hb_data.pressure_hPa; + hts221_data_t hts221_data = hts221_data_read(); + new_sensor_data.humidity_perc = hts221_data.humidity_perc; + lsm6dsl_data_t lsm6dsl_data = lsm6dsl_data_read(); + memcpy(new_sensor_data.acceleration_mg, + lsm6dsl_data.acceleration_mg, + sizeof(lsm6dsl_data.acceleration_mg)); + lis2mdl_data_t lis2mdl_data = lis2mdl_data_read(); + memcpy(new_sensor_data.magnetic_mG, + lis2mdl_data.magnetic_mG, + sizeof(lis2mdl_data.magnetic_mG)); + tx_mutex_put(&mqtt_i2c_mutex); + + if (data_changed(¤t_sensor_data, &new_sensor_data)){ + #ifdef LOG_TELEMETRY + printf("Telemetry changed.\r\n"); + print_sensor_data(new_sensor_data); + #endif + current_sensor_data = new_sensor_data; + tx_event_flags_set(&mqtt_app_flag, MQTT_MESSAGE_READY, TX_OR); + } + #ifdef LOG_TELEMETRY + else{ + printf("Telemetry did not change.\r\n"); + } + #endif + + tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND * telemetry_interval); + } +} + +/** + * Returns current telemetry as a string. + */ +void get_current_telemetry_string(char* output){ + get_sensor_data_buffer(current_sensor_data, output); +} diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.h b/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.h new file mode 100644 index 00000000..81b3cd61 --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/telemetry.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ +#include "sensor.h" +#include "tx_api.h" +#include +#include +#include + +#ifndef _TELEMETRY_H +#define _TELEMETRY_H + +#define TELEMETRY_BUFFER_SIZE 256 + +// Log telemetry if needed +//#define LOG_TELEMETRY + +// Sensor data +typedef struct{ + float pressure_hPa; + float temperature_degC; + float humidity_perc; + float acceleration_mg[3]; + float magnetic_mG[3]; +} sensor_data; + +void telemetry_thread_entry(ULONG parameter); +void get_current_telemetry_string(char* output); + +#endif // _TELEMETRY_H diff --git a/MXChip/AZ3166/app/uprotocol-mqtt/threadx_pnp_info.h b/MXChip/AZ3166/app/uprotocol-mqtt/threadx_pnp_info.h new file mode 100644 index 00000000..dc6acaba --- /dev/null +++ b/MXChip/AZ3166/app/uprotocol-mqtt/threadx_pnp_info.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef _THREADX_PNP_INFO_H +#define _THREADX_PNP_INFO_H + +#define DEVICE_INFO_COMPONENT_NAME "deviceInformation" + +// Device Info property names +#define DEVICE_INFO_MANUFACTURER_PROPERTY_NAME "manufacturer" +#define DEVICE_INFO_MODEL_PROPERTY_NAME "model" +#define DEVICE_INFO_SW_VERSION_PROPERTY_NAME "swVersion" +#define DEVICE_INFO_OS_NAME_PROPERTY_NAME "osName" +#define DEVICE_INFO_PROCESSOR_ARCHITECTURE_PROPERTY_NAME "processorArchitecture" +#define DEVICE_INFO_PROCESSOR_MANUFACTURER_PROPERTY_NAME "processorManufacturer" +#define DEVICE_INFO_TOTAL_STORAGE_PROPERTY_NAME "totalStorage" +#define DEVICE_INFO_TOTAL_MEMORY_PROPERTY_NAME "totalMemory" + +// Device Info property values +#define DEVICE_INFO_MANUFACTURER_PROPERTY_VALUE "MXCHIP" +#define DEVICE_INFO_MODEL_PROPERTY_VALUE "AZ3166" +#define DEVICE_INFO_SW_VERSION_PROPERTY_VALUE "6.1.4" +#define DEVICE_INFO_OS_NAME_PROPERTY_VALUE "Eclipse ThreadX" +#define DEVICE_INFO_PROCESSOR_ARCHITECTURE_PROPERTY_VALUE "Arm Cortex M4" +#define DEVICE_INFO_PROCESSOR_MANUFACTURER_PROPERTY_VALUE "STMicroelectronics" +#define DEVICE_INFO_TOTAL_STORAGE_PROPERTY_VALUE 1024 +#define DEVICE_INFO_TOTAL_MEMORY_PROPERTY_VALUE 128 + +#endif diff --git a/MXChip/AZ3166/app/mqtt/uprotocol_config.h b/MXChip/AZ3166/app/uprotocol-mqtt/uprotocol_config.h similarity index 87% rename from MXChip/AZ3166/app/mqtt/uprotocol_config.h rename to MXChip/AZ3166/app/uprotocol-mqtt/uprotocol_config.h index 9e4bb52c..70f3f4b1 100644 --- a/MXChip/AZ3166/app/mqtt/uprotocol_config.h +++ b/MXChip/AZ3166/app/uprotocol-mqtt/uprotocol_config.h @@ -5,9 +5,9 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * - * AI Disclosure: This file was largely AI-generated by Codex (GPT-6). - * The AI-generated portions may be considered public domain (CC0-1.0). - * Human review and verification are required before submission. + * AI Disclosure: Portions of this file were generated with Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. Human review is required. * * SPDX-License-Identifier: MIT and CC0-1.0 **************************************************************************/ diff --git a/MXChip/AZ3166/deps/lib/netxduo b/MXChip/AZ3166/deps/lib/netxduo index 6c8e9d1c..0edae5f8 160000 --- a/MXChip/AZ3166/deps/lib/netxduo +++ b/MXChip/AZ3166/deps/lib/netxduo @@ -1 +1 @@ -Subproject commit 6c8e9d1c95d71bd4b313e1cc37d8f8841543b248 +Subproject commit 0edae5f800ce6adbc6a1f0366a1621a8bc66a7a3 diff --git a/MXChip/AZ3166/scripts/publish_uprotocol_powertrain.py b/MXChip/AZ3166/scripts/publish_uprotocol_powertrain.py new file mode 100644 index 00000000..ecc9e704 --- /dev/null +++ b/MXChip/AZ3166/scripts/publish_uprotocol_powertrain.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 Eclipse ThreadX contributors +# This program and the accompanying materials are made available under +# the terms of the MIT License: https://opensource.org/licenses/MIT. +# AI Disclosure: This file was largely AI-generated by Codex (GPT-6). +# The AI-generated portions may be considered public domain (CC0-1.0). +# Human review and verification are required before submission. +# SPDX-License-Identifier: MIT and CC0-1.0 +"""Publish a uProtocol powertrain event using the existing mosquitto_pub tool.""" +import argparse +import os +import shlex +import subprocess +import time +import uuid +import json + + +def publish_command(host, port, mode, authority="powertrain", entity=0xBC, version=1, resource=0x8001): + """Encode the pinned MQTT mapping; JSON remains the application payload.""" + if not authority or any(c not in "abcdefghijklmnopqrstuvwxyz0123456789-._~" for c in authority): + raise ValueError("authority must be a lowercase URI name") + if not 0 <= entity <= 0xFFFFFFFF or (entity & 0xFFFF) == 0xFFFF or entity >> 16 == 0xFFFF: + raise ValueError("entity must be a concrete 32-bit uEntity ID") + if not 0 <= version < 255 or not 0x8000 <= resource < 0xFFFF: + raise ValueError("version/resource must identify a concrete publish resource") + milliseconds = time.time_ns() // 1_000_000 + identifier = bytearray(milliseconds.to_bytes(6, "big") + os.urandom(10)) + identifier[6] = (identifier[6] & 0x0F) | 0x70 + identifier[8] = (identifier[8] & 0x3F) | 0x80 + source = f"up://{authority}/{entity:X}/{version:X}/{resource:X}" + topic = f"{authority}/{entity & 0xFFFF:X}/{entity >> 16:X}/{version:X}/{resource:X}" + command = ["mosquitto_pub", "-V", "mqttv5", "-h", host, "-p", str(port), + "-q", "1", "-t", topic, "-m", json.dumps({"Mode": mode})] + for key, value in [("uP", "1"), ("1", str(uuid.UUID(bytes=bytes(identifier)))), + ("2", "up-pub.v1"), ("3", source), ("5", "CS1")]: + command.extend(["-D", "PUBLISH", "user-property", key, value]) + command.extend(["-D", "PUBLISH", "content-type", "3", + "-D", "PUBLISH", "message-expiry-interval", "10"]) + return command + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--host", required=True) + parser.add_argument("--port", type=int, default=1883) + parser.add_argument("--mode", required=True, help="Value displayed as the JSON Mode string") + parser.add_argument("--authority", default="powertrain") + parser.add_argument("--entity", type=lambda value: int(value, 16), default=0xBC) + parser.add_argument("--version", type=lambda value: int(value, 16), default=1) + parser.add_argument("--resource", type=lambda value: int(value, 16), default=0x8001) + parser.add_argument("--dry-run", action="store_true", help="Print the command without publishing") + args = parser.parse_args() + if not 1 <= args.port <= 65535: + parser.error("port must be between 1 and 65535") + try: + command = publish_command(args.host, args.port, args.mode, args.authority, + args.entity, args.version, args.resource) + except ValueError as error: + parser.error(str(error)) + if args.dry_run: + print(shlex.join(command)) + return 0 + return subprocess.run(command, check=False).returncode + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/reflash.sh b/reflash.sh deleted file mode 100755 index 6cc1563b..00000000 --- a/reflash.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -set -e - -LABEL="AZ3166" -MOUNT_POINT="/mnt/AZ3166" -DEPLOY_SCRIPT="./MXChip/AZ3166/scripts/deploy.sh" - -echo "[INFO] Looking for $LABEL..." - -DEVICE=$(lsblk -rno PATH,LABEL | awk -v label="$LABEL" '$2 == label {print $1; exit}') - -if [ -z "$DEVICE" ]; then - echo "[ERROR] AZ3166 device not found." - echo "[ERROR] Make sure the board is attached to WSL with usbipd." - exit 1 -fi - -echo "[INFO] Found AZ3166 at $DEVICE" - -# Unmount if currently mounted -if findmnt -rn "$MOUNT_POINT" >/dev/null 2>&1; then - echo "[INFO] Unmounting $MOUNT_POINT..." - sudo umount "$MOUNT_POINT" -fi - -# Repair/check the virtual FAT filesystem -echo "[INFO] Checking filesystem..." -sudo fsck.vfat -a "$DEVICE" - -# Ensure mount point exists -sudo mkdir -p "$MOUNT_POINT" - -# Mount read/write -echo "[INFO] Mounting $DEVICE at $MOUNT_POINT..." -sudo mount -o rw "$DEVICE" "$MOUNT_POINT" - -# Verify that it really mounted read/write -if ! findmnt -rn -o OPTIONS "$MOUNT_POINT" | grep -qw "rw"; then - echo "[ERROR] AZ3166 did not mount read/write." - exit 1 -fi - -echo "[INFO] AZ3166 mounted read/write." - -# Deploy -echo "[INFO] Flashing AZ3166..." -sudo "$DEPLOY_SCRIPT" "$MOUNT_POINT" - -echo "[OK] AZ3166 reflash complete." From 4caf274abdc5e15b1aa1b4d2d59cc10717d65e5e Mon Sep 17 00:00:00 2001 From: justinhrpr-cmi Date: Thu, 8 Oct 2026 02:09:05 -0400 Subject: [PATCH 5/5] add changes to common and lib that were needed for on board example --- MXChip/AZ3166/app/common/sntp_client.c | 57 +++++- MXChip/AZ3166/app/common/sntp_client.h | 6 +- MXChip/AZ3166/app/common/wwd_networking.c | 184 ++++++++++++++++-- MXChip/AZ3166/lib/CMakeLists.txt | 5 + MXChip/AZ3166/lib/netxduo/nx_user.h | 4 +- MXChip/AZ3166/lib/stm32cubef4/CMakeLists.txt | 5 +- .../stm32cubef4/config/stm32f4xx_hal_conf.h | 7 +- 7 files changed, 237 insertions(+), 31 deletions(-) diff --git a/MXChip/AZ3166/app/common/sntp_client.c b/MXChip/AZ3166/app/common/sntp_client.c index 698349d2..52b728bc 100644 --- a/MXChip/AZ3166/app/common/sntp_client.c +++ b/MXChip/AZ3166/app/common/sntp_client.c @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -12,7 +13,7 @@ * Microsoft - Initial version * Frédéric Desbiens - 2024 version. */ - +// Portions of this file were generated with AI assistance. #include "sntp_client.h" #include @@ -47,6 +48,9 @@ static TX_EVENT_FLAGS_GROUP sntp_flags; // Variables to keep track of time static ULONG sntp_last_time = 0; static ULONG tx_last_ticks = 0; +static uint64_t sntp_epoch_ms; +static uint64_t sntp_elapsed_ticks; +static ULONG sntp_ms_last_tick; static VOID time_update_callback(NX_SNTP_TIME_MESSAGE* time_update_ptr, NX_SNTP_TIME* local_time) { @@ -54,28 +58,35 @@ static VOID time_update_callback(NX_SNTP_TIME_MESSAGE* time_update_ptr, NX_SNTP_ tx_event_flags_set(&sntp_flags, SNTP_UPDATE_EVENT, TX_OR); } -static void set_sntp_time() +static UINT set_sntp_time() { UINT status; ULONG seconds; - ULONG milliseconds; + ULONG fraction; + TX_INTERRUPT_SAVE_AREA CHAR time_buffer[64]; - status = nx_sntp_client_get_local_time(&sntp_client, &seconds, &milliseconds, NX_NULL); + status = nx_sntp_client_get_local_time(&sntp_client, &seconds, &fraction, NX_NULL); if (status != NX_SUCCESS) { printf("ERROR: Internal error with getting local time (0x%08x)\n", status); - return; + return status; } // Stash the Unix and ThreadX times sntp_last_time = seconds - UNIX_TO_NTP_EPOCH_SECS; tx_last_ticks = tx_time_get(); + TX_DISABLE + sntp_epoch_ms = (uint64_t)sntp_last_time * 1000u + (((uint64_t)fraction * 1000u) >> 32); + sntp_elapsed_ticks = 0; + sntp_ms_last_tick = tx_last_ticks; + TX_RESTORE nx_sntp_client_utility_display_date_time(&sntp_client, time_buffer, sizeof(time_buffer)); printf("\tSNTP time update: %s\r\n", time_buffer); printf("SUCCESS: SNTP initialized\r\n"); + return NX_SUCCESS; } static UINT sntp_client_run() @@ -83,7 +94,7 @@ static UINT sntp_client_run() UINT status; NXD_ADDRESS sntp_address; - if (sntp_server_count > (sizeof(SNTP_SERVER) / sizeof(&SNTP_SERVER))) + if (sntp_server_count >= (sizeof(SNTP_SERVER) / sizeof(SNTP_SERVER[0]))) { // We rotated through all the servers, fail out. return NX_SNTP_SERVER_NOT_AVAILABLE; @@ -133,6 +144,22 @@ ULONG sntp_time_get() return sntp_time; } +/* Read/advance the millisecond clock atomically on this single-core board. + * Frequent calls retain sub-millisecond ticks instead of rounding each delta. */ +uint64_t sntp_time_get_ms(void) +{ + ULONG current; + uint64_t result; + TX_INTERRUPT_SAVE_AREA + TX_DISABLE + current = tx_time_get(); + sntp_elapsed_ticks += (ULONG)(current - sntp_ms_last_tick); + sntp_ms_last_tick = current; + result = sntp_epoch_ms ? sntp_epoch_ms + sntp_elapsed_ticks * 1000u / TX_TIMER_TICKS_PER_SECOND : 0; + TX_RESTORE + return result; +} + UINT sntp_time(ULONG* unix_time) { *unix_time = sntp_time_get(); @@ -185,7 +212,15 @@ UINT sntp_sync() while (NX_TRUE) { // Run the client - sntp_client_run(); + status = sntp_client_run(); + if (status != NX_SUCCESS) + { + if (sntp_server_count >= sizeof(SNTP_SERVER) / sizeof(SNTP_SERVER[0])) + { + break; + } + continue; + } // Wait for new events events = 0; @@ -197,11 +232,15 @@ UINT sntp_sync() nx_sntp_client_receiving_updates(&sntp_client, &server_status); // New time, update our local time and we are done - set_sntp_time(); - status = NX_SUCCESS; + status = set_sntp_time(); break; } + if (sntp_server_count >= sizeof(SNTP_SERVER) / sizeof(SNTP_SERVER[0])) + { + status = NX_SNTP_SERVER_NOT_AVAILABLE; + break; + } // Otherwise we rotate around to the next server } diff --git a/MXChip/AZ3166/app/common/sntp_client.h b/MXChip/AZ3166/app/common/sntp_client.h index 820572eb..19e97f52 100644 --- a/MXChip/AZ3166/app/common/sntp_client.h +++ b/MXChip/AZ3166/app/common/sntp_client.h @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -12,13 +13,16 @@ * Microsoft - Initial version * Frédéric Desbiens - 2024 version. */ - +// Portions of this file were generated with AI assistance. #ifndef _SNTP_CLIENT_H #define _SNTP_CLIENT_H #include +#include ULONG sntp_time_get(); +/* Unix milliseconds, or zero until a successful sync. Call at least once per tick wrap. */ +uint64_t sntp_time_get_ms(void); UINT sntp_time(ULONG* unix_time); UINT sntp_init(); diff --git a/MXChip/AZ3166/app/common/wwd_networking.c b/MXChip/AZ3166/app/common/wwd_networking.c index 834695e3..427c9dec 100644 --- a/MXChip/AZ3166/app/common/wwd_networking.c +++ b/MXChip/AZ3166/app/common/wwd_networking.c @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -13,8 +14,12 @@ * Frédéric Desbiens - 2024 version. */ +// Portions of this file were generated with AI assistance. + #include "wwd_networking.h" +#include + #include "nx_api.h" #include "nx_secure_tls_api.h" #include "nxd_dhcp_client.h" @@ -24,6 +29,14 @@ #include "sntp_client.h" +/* The imported WICED archive accesses these NX_IP fields at fixed offsets. + * Reject NetX configurations that would silently corrupt its IP instance. */ +typedef char wiced_netx_ip_layout_check[ + (offsetof(NX_IP, nx_ip_interface[0].nx_interface_address_mapping_needed) == 0x72d && + offsetof(NX_IP, nx_ip_interface[0].nx_interface_physical_address_msw) == 0x734 && + offsetof(NX_IP, nx_ip_interface[0].nx_interface_physical_address_lsw) == 0x738 && + offsetof(NX_IP, nx_ip_interface[0].nx_interface_ip_mtu_size) == 0x74c) ? 1 : -1]; + #define NETX_IP_STACK_SIZE 2048 #define NETX_TX_PACKET_COUNT 16 #define NETX_RX_PACKET_COUNT 12 @@ -50,6 +63,7 @@ static CHAR* netx_password; static wiced_security_t netx_mode; static NX_DHCP nx_dhcp_client; +static UINT dhcp_started = NX_FALSE; NX_IP nx_ip; NX_PACKET_POOL nx_pool[2]; // 0=TX, 1=RX. @@ -68,7 +82,8 @@ static void print_address(CHAR* preable, ULONG address) /* Join Network. */ static UINT wifi_init() { - wiced_mac_t mac; + wiced_mac_t mac = {{0}}; + wwd_result_t mac_status; printf("\r\nInitializing WiFi\r\n"); @@ -92,7 +107,8 @@ static UINT wifi_init() return NX_NOT_SUCCESSFUL; } - wwd_wifi_get_mac_address(&mac, WWD_STA_INTERFACE); + mac_status = wwd_wifi_get_mac_address(&mac, WWD_STA_INTERFACE); + printf("\tMAC read status: %ld\r\n", (long)mac_status); printf("\tMAC address: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac.octet[0], mac.octet[1], @@ -106,33 +122,88 @@ static UINT wifi_init() return NX_SUCCESS; } +/* Keep diagnostics bounded: this callback runs in the DHCP thread. */ +static VOID dhcp_state_changed(NX_DHCP* client, UCHAR state) +{ + const char* name; + NX_PARAMETER_NOT_USED(client); + + switch (state) + { + case NX_DHCP_STATE_NOT_STARTED: name = "NOT_STARTED"; break; + case NX_DHCP_STATE_BOOT: name = "BOOT"; break; + case NX_DHCP_STATE_INIT: name = "INIT"; break; + case NX_DHCP_STATE_SELECTING: name = "SELECTING (waiting for offer)"; break; + case NX_DHCP_STATE_REQUESTING: name = "REQUESTING (waiting for ACK)"; break; + case NX_DHCP_STATE_BOUND: name = "BOUND"; break; + case NX_DHCP_STATE_RENEWING: name = "RENEWING"; break; + case NX_DHCP_STATE_REBINDING: name = "REBINDING"; break; + case NX_DHCP_STATE_FORCERENEW: name = "FORCERENEW"; break; + case NX_DHCP_STATE_ADDRESS_PROBING: name = "ADDRESS_PROBING"; break; + default: name = "UNKNOWN"; break; + } + printf("DHCP state: %u %s\r\n", (unsigned int)state, name); +} + static UINT dhcp_connect(void) { - UINT status; - ULONG actual_status; + UINT status = NX_NOT_SUCCESSFUL; + ULONG actual_status = 0; ULONG ip_address; ULONG network_mask; ULONG gateway_address; + ULONG timeout; + UCHAR state; + UINT valid; + UINT interval; + ULONG discoveries; + ULONG offers; + ULONG errors; + + printf("\r\nInitializing DHCP (diagnostics enabled)\r\n"); + + /* Take a snapshot after Wi-Fi joins; earlier state changes may be missed on the console. */ + for (interval = 0; interval < 6; ++interval) + { + tx_mutex_get(&nx_dhcp_client.nx_dhcp_mutex, TX_WAIT_FOREVER); + valid = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_record_valid; + state = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_state; + timeout = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_timeout; + discoveries = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_discoveries_sent; + offers = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_offers_received; + errors = nx_dhcp_client.nx_dhcp_interface_record[0].nx_dhcp_internal_errors; + tx_mutex_put(&nx_dhcp_client.nx_dhcp_mutex); + printf("DHCP at %u s: state=%u valid=%u next retry ticks=%lu WiFi ready=%u\r\n", + (unsigned int)(interval * 5), (unsigned int)state, (unsigned int)valid, + timeout, (unsigned int)(wwd_wifi_is_ready_to_transceive(WWD_STA_INTERFACE) == WWD_SUCCESS)); + printf("DHCP traffic: discover=%lu offer=%lu errors=%lu ip_tx=%lu ip_rx=%lu link=%u\r\n", + discoveries, offers, errors, nx_ip.nx_ip_total_packets_sent, + nx_ip.nx_ip_total_packets_received, (unsigned int)nx_ip.nx_ip_driver_link_up); + printf("DHCP send path: udp_tx=%lu dropped=%lu mtu=%lu pool_free=%lu\r\n", + nx_ip.nx_ip_udp_packets_sent, nx_ip.nx_ip_send_packets_dropped, + nx_ip.nx_ip_interface[0].nx_interface_ip_mtu_size, + nx_dhcp_client.nx_dhcp_packet_pool_ptr->nx_packet_pool_available); + + status = nx_ip_status_check(&nx_ip, NX_IP_ADDRESS_RESOLVED, &actual_status, + 5 * TX_TIMER_TICKS_PER_SECOND); + if (status == NX_SUCCESS) + { + break; + } + } - printf("\r\nInitializing DHCP\r\n"); - - // Wait until address is solved. - if ((status = nx_ip_status_check(&nx_ip, NX_IP_ADDRESS_RESOLVED, &actual_status, DHCP_WAIT_TIME_TICKS))) + if (status != NX_SUCCESS) { - // DHCP Failed... no IP address! - printf("ERROR: Can't resolve DHCP address (0x%08x\r\n", status); + printf("ERROR: Can't resolve DHCP address after 30 seconds (0x%08x, status=0x%08lx)\r\n", + status, actual_status); return status; } - // Get IP address and gateway address nx_ip_address_get(&nx_ip, &ip_address, &network_mask); nx_ip_gateway_address_get(&nx_ip, &gateway_address); - - // Output IP address and gateway address print_address("IP address", ip_address); print_address("Mask", network_mask); print_address("Gateway", gateway_address); - printf("SUCCESS: DHCP initialized\r\n"); return NX_SUCCESS; @@ -288,14 +359,14 @@ UINT wwd_network_init(CHAR* ssid, CHAR* password, WiFi_Mode mode) printf("ERROR: nx_dhcp_create (0x%08x)\r\n", status); } - // Start the DHCP Client. - else if ((status = nx_dhcp_start(&nx_dhcp_client))) + // Register diagnostics before DHCP starts so early transitions are visible. + else if ((status = nx_dhcp_state_change_notify(&nx_dhcp_client, dhcp_state_changed))) { nx_dhcp_delete(&nx_dhcp_client); nx_ip_delete(&nx_ip); nx_packet_pool_delete(&nx_pool[0]); nx_packet_pool_delete(&nx_pool[1]); - printf("ERROR: nx_dhcp_start (0x%08x)\r\n", status); + printf("ERROR: nx_dhcp_state_change_notify (0x%08x)\r\n", status); } // Create DNS @@ -379,6 +450,82 @@ UINT wwd_network_connect() printf("SUCCESS: WiFi connected\r\n"); } + { + wiced_mac_t associated_mac = {{0}}; + wwd_result_t mac_status = wwd_wifi_get_mac_address(&associated_mac, WWD_STA_INTERFACE); + printf("\tMAC after join (status=%ld): %02X:%02X:%02X:%02X:%02X:%02X\r\n", + (long)mac_status, + associated_mac.octet[0], associated_mac.octet[1], associated_mac.octet[2], + associated_mac.octet[3], associated_mac.octet[4], associated_mac.octet[5]); + if (mac_status != WWD_SUCCESS || + !(associated_mac.octet[0] | associated_mac.octet[1] | + associated_mac.octet[2] | associated_mac.octet[3] | + associated_mac.octet[4] | associated_mac.octet[5])) + { + printf("ERROR: WICED did not provide a valid MAC address\r\n"); + return NX_NOT_SUCCESSFUL; + } + + printf("\tNetX driver init: MAC=%04lX:%08lX MTU=%lu mapping=%u\r\n", + nx_ip.nx_ip_interface[0].nx_interface_physical_address_msw, + nx_ip.nx_ip_interface[0].nx_interface_physical_address_lsw, + nx_ip.nx_ip_interface[0].nx_interface_ip_mtu_size, + (unsigned int)nx_ip.nx_ip_interface[0].nx_interface_address_mapping_needed); + + ULONG msw = ((ULONG)associated_mac.octet[0] << 8) | associated_mac.octet[1]; + ULONG lsw = ((ULONG)associated_mac.octet[2] << 24) | + ((ULONG)associated_mac.octet[3] << 16) | + ((ULONG)associated_mac.octet[4] << 8) | associated_mac.octet[5]; + /* The imported WICED driver leaves NetX's interface MAC unset. The radio + * already has its real MAC, so update only NetX's interface record. */ + status = nx_ip_interface_physical_address_set(&nx_ip, 0, msw, lsw, NX_FALSE); + if (status != NX_SUCCESS) + { + printf("ERROR: nx_ip_interface_physical_address_set (0x%08x)\r\n", status); + return status; + } + msw = nx_ip.nx_ip_interface[0].nx_interface_physical_address_msw; + lsw = nx_ip.nx_ip_interface[0].nx_interface_physical_address_lsw; + printf("\tNetX MAC after join: %02lX:%02lX:%02lX:%02lX:%02lX:%02lX\r\n", + (msw >> 8) & 0xff, msw & 0xff, (lsw >> 24) & 0xff, + (lsw >> 16) & 0xff, (lsw >> 8) & 0xff, lsw & 0xff); + } + + /* The WICED link reports no MTU; NetX drops all outgoing IPv4 packets in that state. */ + if (nx_ip.nx_ip_interface[0].nx_interface_ip_mtu_size == 0) + { + status = nx_ip_interface_mtu_set(&nx_ip, 0, WICED_PAYLOAD_MTU); + if (status != NX_SUCCESS) + { + printf("ERROR: nx_ip_interface_mtu_set (0x%08x)\r\n", status); + return status; + } + printf("WICED IPv4 MTU set to %u\r\n", (unsigned int)WICED_PAYLOAD_MTU); + } + + /* This Wi-Fi link carries Ethernet frames. Without address mapping, + * NetX sends unicast IP packets without first resolving the peer by ARP. */ + status = nx_ip_interface_address_mapping_configure(&nx_ip, 0, NX_TRUE); + if (status != NX_SUCCESS) + { + printf("ERROR: nx_ip_interface_address_mapping_configure (0x%08x)\r\n", status); + return status; + } + printf("NetX Ethernet address mapping enabled: %u\r\n", + (unsigned int)nx_ip.nx_ip_interface[0].nx_interface_address_mapping_needed); + + // Start DHCP only after Wi-Fi association, so the first discovery can reach the network. + if (!dhcp_started) + { + status = nx_dhcp_start(&nx_dhcp_client); + if (status != NX_SUCCESS) + { + printf("ERROR: nx_dhcp_start (0x%08x)\r\n", status); + return status; + } + dhcp_started = NX_TRUE; + } + // Fetch IP details if ((status = dhcp_connect())) { @@ -395,6 +542,9 @@ UINT wwd_network_connect() else if ((status = sntp_sync())) { printf("ERROR: Failed to sync SNTP time (0x%08x)\r\n", status); + printf("ARP traffic: requests sent=%lu received=%lu responses sent=%lu received=%lu\r\n", + nx_ip.nx_ip_arp_requests_sent, nx_ip.nx_ip_arp_requests_received, + nx_ip.nx_ip_arp_responses_sent, nx_ip.nx_ip_arp_responses_received); } return status; diff --git a/MXChip/AZ3166/lib/CMakeLists.txt b/MXChip/AZ3166/lib/CMakeLists.txt index ac81a798..3d5b9666 100644 --- a/MXChip/AZ3166/lib/CMakeLists.txt +++ b/MXChip/AZ3166/lib/CMakeLists.txt @@ -1,5 +1,6 @@ # Copyright (c) Microsoft # Copyright (c) 2024 Eclipse Foundation +# Copyright (c) 2026 Eclipse ThreadX contributors # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at @@ -10,6 +11,7 @@ # Contributors: # Microsoft - Initial version # Frédéric Desbiens - 2024 version. +# Portions of this file were generated with AI assistance. # Define ThreadX user configuration set(TX_USER_FILE "${CMAKE_CURRENT_LIST_DIR}/threadx/tx_user.h" CACHE STRING "Enable TX user configuration") @@ -20,6 +22,9 @@ set(NXD_ENABLE_FILE_SERVERS OFF CACHE BOOL "Disable fileX dependency by netxduo" # Core libraries add_subdirectory(${SHARED_LIB_DIR}/threadx threadx) +if(APP_CONFIG STREQUAL "uprotocol-mqtt" AND NOT EXISTS "${SHARED_LIB_DIR}/netxduo/addons/uprotocol/nxd_uprotocol.h") + message(FATAL_ERROR "The uProtocol MQTT demo requires the pinned NetX Duo submodule. Run git submodule update --init MXChip/AZ3166/deps/lib/netxduo") +endif() add_subdirectory(${SHARED_LIB_DIR}/netxduo netxduo) add_subdirectory(${SHARED_LIB_DIR}/jsmn jsmn) add_subdirectory(${SHARED_LIB_DIR}/nanoprintf nanoprintf) diff --git a/MXChip/AZ3166/lib/netxduo/nx_user.h b/MXChip/AZ3166/lib/netxduo/nx_user.h index 48a54de1..3d112e5d 100644 --- a/MXChip/AZ3166/lib/netxduo/nx_user.h +++ b/MXChip/AZ3166/lib/netxduo/nx_user.h @@ -1,6 +1,7 @@ /* * Copyright (c) Microsoft * Copyright (c) 2024 Eclipse Foundation + * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at @@ -12,12 +13,13 @@ * Microsoft - Initial version * Frédéric Desbiens - 2024 version. */ +// Portions of this file were generated with AI assistance. #ifndef NX_USER_H #define NX_USER_H #define NX_SECURE_ENABLE -#define NX_ENABLE_EXTENDED_NOTIFY_SUPPORT +/* Keep NX_IP layout compatible with the prebuilt WICED NetX driver. */ #define NX_ENABLE_IP_PACKET_FILTER #define NX_DISABLE_IPV6 #define NX_DNS_CLIENT_USER_CREATE_PACKET_POOL diff --git a/MXChip/AZ3166/lib/stm32cubef4/CMakeLists.txt b/MXChip/AZ3166/lib/stm32cubef4/CMakeLists.txt index b0128dab..634f5eb0 100644 --- a/MXChip/AZ3166/lib/stm32cubef4/CMakeLists.txt +++ b/MXChip/AZ3166/lib/stm32cubef4/CMakeLists.txt @@ -1,8 +1,11 @@ +# Copyright (c) 2026 Eclipse ThreadX contributors +# SPDX-License-Identifier: MIT +# Portions of this file were generated with AI assistance. set(STM32_FAMILY "F4") set(STM32Cube_DIR "./") find_package(CMSIS REQUIRED) -find_package(STM32HAL REQUIRED COMPONENTS dma flash gpio tim timebase_tim uart usart i2c) +find_package(STM32HAL REQUIRED COMPONENTS dma flash gpio tim timebase_tim uart usart i2c rng) set(TARGET stm32cubef4) diff --git a/MXChip/AZ3166/lib/stm32cubef4/config/stm32f4xx_hal_conf.h b/MXChip/AZ3166/lib/stm32cubef4/config/stm32f4xx_hal_conf.h index 5125d8e4..7ed824e9 100644 --- a/MXChip/AZ3166/lib/stm32cubef4/config/stm32f4xx_hal_conf.h +++ b/MXChip/AZ3166/lib/stm32cubef4/config/stm32f4xx_hal_conf.h @@ -10,6 +10,7 @@ * *

© Copyright (c) 2017 STMicroelectronics. * All rights reserved.

+ * Copyright (c) 2026 Eclipse ThreadX contributors * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the @@ -17,7 +18,9 @@ * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** - */ + */ +// Portions of this file were generated with AI assistance. + /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F4xx_HAL_CONF_H @@ -64,7 +67,7 @@ #define HAL_PWR_MODULE_ENABLED /* #define HAL_QSPI_MODULE_ENABLED */ #define HAL_RCC_MODULE_ENABLED -/* #define HAL_RNG_MODULE_ENABLED */ +#define HAL_RNG_MODULE_ENABLED /* #define HAL_RTC_MODULE_ENABLED */ /* #define HAL_SAI_MODULE_ENABLED */ /* #define HAL_SD_MODULE_ENABLED */