From 3e56f632c125a80f88d4fe997dbeee0459d5e9ec Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 11:35:54 +0300 Subject: [PATCH 01/50] Initial commit Signed-off-by: JeffMboya --- .gitmodules | 3 + embed-proplet/CMakeLists.txt | 17 + embed-proplet/modules/wamr/CMakeLists.txt | 44 +++ embed-proplet/modules/wamr/wasm-micro-runtime | 1 + embed-proplet/prj.conf | 44 +++ embed-proplet/src/CMakeLists.txt | 6 + embed-proplet/src/client.c | 311 ++++++++++++++++++ embed-proplet/src/client.h | 35 ++ embed-proplet/src/main.c | 39 +++ 9 files changed, 500 insertions(+) create mode 100644 .gitmodules create mode 100644 embed-proplet/CMakeLists.txt create mode 100644 embed-proplet/modules/wamr/CMakeLists.txt create mode 160000 embed-proplet/modules/wamr/wasm-micro-runtime create mode 100644 embed-proplet/prj.conf create mode 100644 embed-proplet/src/CMakeLists.txt create mode 100644 embed-proplet/src/client.c create mode 100644 embed-proplet/src/client.h create mode 100644 embed-proplet/src/main.c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..58b9d8a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "embed-proplet/modules/wamr/wasm-micro-runtime"] + path = embed-proplet/modules/wamr/wasm-micro-runtime + url = https://github.com/bytecodealliance/wasm-micro-runtime.git diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt new file mode 100644 index 0000000..b02d8f2 --- /dev/null +++ b/embed-proplet/CMakeLists.txt @@ -0,0 +1,17 @@ +# my_proplet/CMakeLists.txt + +cmake_minimum_required(VERSION 3.20.0) + +# Standard Zephyr boilerplate +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(my_proplet) + +# Include our application source folder +add_subdirectory(src) + +# Include WAMR as a subdirectory (the submodule) +add_subdirectory(modules/wamr) + +# Link WAMR libraries to 'app' target +link_wamr_to_zephyr(app) + diff --git a/embed-proplet/modules/wamr/CMakeLists.txt b/embed-proplet/modules/wamr/CMakeLists.txt new file mode 100644 index 0000000..40431f4 --- /dev/null +++ b/embed-proplet/modules/wamr/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.20) + +# +# Force WAMR to treat this build as Zephyr + Xtensa +# (Adjust target name as needed: "XTENSA", "esp32", etc.) +# +set(WAMR_BUILD_PLATFORM "zephyr" CACHE STRING "" FORCE) +set(WAMR_BUILD_TARGET "XTENSA" CACHE STRING "" FORCE) + +# +# Minimal build settings +# +set(WAMR_BUILD_INTERP 1 CACHE BOOL "Enable WAMR interpreter" FORCE) +set(WAMR_BUILD_AOT 0 CACHE BOOL "Disable AOT" FORCE) +set(WAMR_BUILD_JIT 0 CACHE BOOL "Disable JIT" FORCE) +set(WAMR_BUILD_FAST_INTERP 1 CACHE BOOL "Enable fast interpreter" FORCE) +set(WAMR_BUILD_LIBC_BUILTIN 1 CACHE BOOL "Enable builtin libc" FORCE) +set(WAMR_BUILD_LIBC_WASI 1 CACHE BOOL "Enable WASI libc" FORCE) +# Disable pthread library in WAMR +set(WAMR_BUILD_LIB_PTHREAD 0 CACHE BOOL "" FORCE) + + +# +# Optional features you can uncomment if desired: +# +#set(WAMR_BUILD_MULTI_MODULE 1 CACHE BOOL "Enable multiple modules" FORCE) +#set(WAMR_BUILD_LIB_PTHREAD 1 CACHE BOOL "Enable pthread library" FORCE) +#set(WAMR_BUILD_LIB_WASI_THREADS 1 CACHE BOOL "Enable WASI threads library" FORCE) + +# +# Add the official WAMR subdirectory +# +add_subdirectory(wasm-micro-runtime + ${CMAKE_CURRENT_BINARY_DIR}/wamr_build +) + +# +# Provide a function for linking WAMR to the Zephyr 'app' target. +# WAMR typically builds a static library named 'vmlib', +# output from 'iwasm_static' with OUTPUT_NAME vmlib. +# +function(link_wamr_to_zephyr TARGET) + target_link_libraries(${TARGET} PRIVATE vmlib) +endfunction() diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime new file mode 160000 index 0000000..902f7d2 --- /dev/null +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -0,0 +1 @@ +Subproject commit 902f7d26310b2b2832dddb3a3ca64141fcfc86f7 diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf new file mode 100644 index 0000000..747e26d --- /dev/null +++ b/embed-proplet/prj.conf @@ -0,0 +1,44 @@ +# +# --- Networking --- +# +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_SOCKETS=y + +# +# --- Enable ESP32 Wi-Fi driver --- +# +#CONFIG_WIFI=y +#CONFIG_WIFI_ESP32=y + +# Auto-connect as a station +#CONFIG_ESP32_WIFI_STA_AUTO=y + +# Reconnect if the station is disconnected +#CONFIG_ESP32_WIFI_STA_RECONNECT=y + +# Wi-Fi SSID and password +#CONFIG_ESP32_WIFI_STA_SSID="Octavifi" +#CONFIG_ESP32_WIFI_STA_PASSWORD="Unic0rn_2030" + +# +# --- MQTT --- +# +CONFIG_MQTT_LIB=y +CONFIG_MQTT_LIB_TLS=n +CONFIG_MQTT_KEEPALIVE=60 + +# +# --- Logging --- +# +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=3 + +# +# --- Main Stack & Thread Priorities --- +# +CONFIG_MAIN_STACK_SIZE=8192 + +# Increase if needed for WAMR or other usage +CONFIG_HEAP_MEM_POOL_SIZE=32768 diff --git a/embed-proplet/src/CMakeLists.txt b/embed-proplet/src/CMakeLists.txt new file mode 100644 index 0000000..811ddbb --- /dev/null +++ b/embed-proplet/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# src/CMakeLists.txt + +target_sources(app PRIVATE + main.c + client.c +) diff --git a/embed-proplet/src/client.c b/embed-proplet/src/client.c new file mode 100644 index 0000000..66c794b --- /dev/null +++ b/embed-proplet/src/client.c @@ -0,0 +1,311 @@ +/* + * client.c + */ + +#include "client.h" + +/* If you need the Wi-Fi header, you can include it here, e.g.: + * #include + */ + +/* Logging: create a separate log module for this client. */ +LOG_MODULE_REGISTER(my_proplet_client, LOG_LEVEL_DBG); + +/* Broker settings */ +#define MQTT_BROKER_HOSTNAME "test.mosquitto.org" +#define MQTT_BROKER_PORT 1883 +#define MQTT_CLIENT_ID "esp32s3_proplet" +#define MQTT_TOPIC_WASM_BINARY "my_proplet/wasm_binary" +#define MQTT_TOPIC_EXEC_RESULT "my_proplet/execution_result" + +/* Buffers for MQTT messages */ +static uint8_t rx_buffer[2048]; +static uint8_t tx_buffer[2048]; + +/* MQTT client object & broker storage */ +static struct mqtt_client client; +static struct sockaddr_storage broker_storage; + +/* WAMR config */ +#define WASM_MAX_APP_MEMORY (64 * 1024) +#define WASM_STACK_SIZE (8 * 1024) +#define WASM_HEAP_SIZE (8 * 1024) + +/* Forward declarations of any static (internal) functions */ +static void mqtt_evt_handler(struct mqtt_client *const c, + const struct mqtt_evt *evt); + +static int mqtt_subscribe_to_topic(void); +static int publish_execution_result(const char *result_str); +static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size); + +/* ------------------------------------------------------------------------- + * 1. Prepare the broker + * ------------------------------------------------------------------------- */ +int prepare_broker(void) +{ + struct sockaddr_in *broker4 = (struct sockaddr_in *)&broker_storage; + + broker4->sin_family = AF_INET; + broker4->sin_port = htons(MQTT_BROKER_PORT); + + if (net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker4->sin_addr)) { + LOG_ERR("Failed to parse broker hostname/IP: %s", MQTT_BROKER_HOSTNAME); + return -EINVAL; + } + + return 0; +} + +/* ------------------------------------------------------------------------- + * 2. Initialize the MQTT client structure + * ------------------------------------------------------------------------- */ +void init_mqtt_client(void) +{ + mqtt_client_init(&client); + + client.broker = &broker_storage; + client.evt_cb = mqtt_evt_handler; + + client.client_id.utf8 = (uint8_t *)MQTT_CLIENT_ID; + client.client_id.size = strlen(MQTT_CLIENT_ID); + + /* MQTT version 3.1.1 */ + client.protocol_version = MQTT_VERSION_3_1_1; + + /* Assign buffers */ + client.rx_buf = rx_buffer; + client.rx_buf_size = sizeof(rx_buffer); + client.tx_buf = tx_buffer; + client.tx_buf_size = sizeof(tx_buffer); + + /* Clean session */ + client.clean_session = 1; + + /* Non-secure transport in this example (port 1883) */ + client.transport.type = MQTT_TRANSPORT_NON_SECURE; +} + +/* ------------------------------------------------------------------------- + * 3. Connect to the broker + * ------------------------------------------------------------------------- */ +int mqtt_connect_to_broker(void) +{ + int ret = mqtt_connect(&client); + if (ret) { + LOG_ERR("mqtt_connect failed: %d", ret); + return ret; + } + LOG_INF("MQTT client connected"); + return 0; +} + +/* ------------------------------------------------------------------------- + * 4. The main MQTT processing loop + * Typically called from your main() in a while(1) or timed loop. + * ------------------------------------------------------------------------- */ +void mqtt_process_events(void) +{ + /* Let the MQTT client handle input and keepalive pings. */ + mqtt_input(&client); + mqtt_live(&client); +} + +/* ------------------------------------------------------------------------- + * Subscribe to the WASM binary topic + * ------------------------------------------------------------------------- */ +static int mqtt_subscribe_to_topic(void) +{ + struct mqtt_topic subscribe_topic = { + .topic = { + .utf8 = (uint8_t *)MQTT_TOPIC_WASM_BINARY, + .size = strlen(MQTT_TOPIC_WASM_BINARY) + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE + }; + + struct mqtt_subscription_list subscription_list = { + .list = &subscribe_topic, + .list_count = 1, + .message_id = 1234 + }; + + int ret = mqtt_subscribe(&client, &subscription_list); + if (ret) { + LOG_ERR("Failed to subscribe, err %d", ret); + return ret; + } + + LOG_INF("Subscribed to topic: %s", MQTT_TOPIC_WASM_BINARY); + return 0; +} + +/* ------------------------------------------------------------------------- + * Publish the execution result back + * ------------------------------------------------------------------------- */ +static int publish_execution_result(const char *result_str) +{ + struct mqtt_publish_param param; + struct mqtt_topic topic = { + .topic = { + .utf8 = (uint8_t *)MQTT_TOPIC_EXEC_RESULT, + .size = strlen(MQTT_TOPIC_EXEC_RESULT), + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE + }; + + param.message.topic = topic; + param.message.payload.data = (uint8_t *)result_str; + param.message.payload.len = strlen(result_str); + param.message_id = 5678; + param.dup_flag = 0; + param.retain_flag = 0; + + return mqtt_publish(&client, ¶m); +} + +/* ------------------------------------------------------------------------- + * Execute the WASM buffer: load, instantiate, call "add" function, publish + * ------------------------------------------------------------------------- */ +static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) +{ + static bool wamr_initialized = false; + if (!wamr_initialized) { + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_Pool; + + /* Provide a global heap for WAMR. */ + static uint8_t global_heap_buf[WASM_MAX_APP_MEMORY]; + init_args.mem_alloc_pool.heap_buf = global_heap_buf; + init_args.mem_alloc_pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Failed to initialize WAMR runtime"); + return -1; + } + + wamr_initialized = true; + } + + /* Load the module */ + WASMModuleCommon *wasm_module = wasm_runtime_load((uint8_t*)wasm_buf, + (uint32_t)wasm_size, + NULL, 0); + if (!wasm_module) { + LOG_ERR("Failed to load WASM module"); + return -1; + } + + /* Instantiate */ + WASMModuleInstanceCommon *wasm_module_inst = + wasm_runtime_instantiate(wasm_module, + WASM_STACK_SIZE, + WASM_HEAP_SIZE, + NULL, 0); + if (!wasm_module_inst) { + LOG_ERR("Failed to instantiate WASM module"); + wasm_runtime_unload(wasm_module); + return -1; + } + + /* Find "add" function */ + const char *func_name = "add"; + wasm_function_inst_t func = + wasm_runtime_lookup_function(wasm_module_inst, func_name, NULL); + if (!func) { + LOG_ERR("Exported function '%s' not found in wasm", func_name); + goto clean; + } + + /* Prepare arguments: (i32, i32)->i32. We'll call add(3, 4). */ + uint32_t argv[2]; + argv[0] = 3; + argv[1] = 4; + + /* Call the function */ + if (!wasm_runtime_call_wasm(wasm_module_inst, func, 2, argv)) { + LOG_ERR("Failed to call '%s'", func_name); + goto clean; + } + + /* The result is now in argv[0] */ + int32_t result = (int32_t)argv[0]; + LOG_INF("WASM function '%s'(3,4) -> %d", func_name, result); + + /* Publish result */ + char result_str[64]; + snprintf(result_str, sizeof(result_str), + "Result from '%s'(3,4): %d", func_name, result); + publish_execution_result(result_str); + +clean: + wasm_runtime_deinstantiate(wasm_module_inst); + wasm_runtime_unload(wasm_module); + return 0; +} + +/* ------------------------------------------------------------------------- + * MQTT event callback (handles connect ack, publish, etc.) + * ------------------------------------------------------------------------- */ +static void mqtt_evt_handler(struct mqtt_client *const c, + const struct mqtt_evt *evt) +{ + switch (evt->type) { + case MQTT_EVT_CONNACK: + LOG_INF("MQTT_EVT_CONNACK"); + mqtt_subscribe_to_topic(); + break; + + case MQTT_EVT_DISCONNECT: + LOG_INF("MQTT_EVT_DISCONNECT"); + break; + + case MQTT_EVT_PUBLISH: { + LOG_INF("MQTT_EVT_PUBLISH"); + + const struct mqtt_publish_param *p = &evt->param.publish; + + /* Validate topic */ + LOG_INF("Topic: %.*s, payload len %d", + p->message.topic.topic.size, + (char *)p->message.topic.topic.utf8, + p->message.payload.len); + + if (p->message.payload.len > sizeof(rx_buffer)) { + LOG_ERR("Received payload is too large!"); + break; + } + + /* Read the payload into rx_buffer */ + int ret = mqtt_read_publish_payload(c, rx_buffer, p->message.payload.len); + if (ret < 0) { + LOG_ERR("mqtt_read_publish_payload error: %d", ret); + break; + } + + /* Execute as WASM module */ + execute_wasm_buffer(rx_buffer, p->message.payload.len); + + /* Acknowledge QOS1 */ + struct mqtt_puback_param puback = { + .message_id = p->message_id + }; + mqtt_publish_qos1_ack(c, &puback); + break; + } + + case MQTT_EVT_PUBACK: + LOG_INF("MQTT_EVT_PUBACK id=%u result=%d", + evt->param.puback.message_id, + evt->result); + break; + + case MQTT_EVT_SUBACK: + LOG_INF("MQTT_EVT_SUBACK"); + break; + + default: + break; + } +} diff --git a/embed-proplet/src/client.h b/embed-proplet/src/client.h new file mode 100644 index 0000000..9209c5c --- /dev/null +++ b/embed-proplet/src/client.h @@ -0,0 +1,35 @@ +#ifndef CLIENT_H +#define CLIENT_H + +#include +#include +#include +#include +#include +#include +#include + +/* If you use WAMR in client.c, include WAMR headers here too */ +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Prepare the MQTT broker (resolve address) */ +int prepare_broker(void); + +/* Initialize the MQTT client structure */ +void init_mqtt_client(void); + +/* Connect to the MQTT broker */ +int mqtt_connect_to_broker(void); + +/* Handle MQTT input & keep-alive in a loop, typically called from main. */ +void mqtt_process_events(void); + +#ifdef __cplusplus +} +#endif + +#endif /* CLIENT_H */ diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c new file mode 100644 index 0000000..3181d59 --- /dev/null +++ b/embed-proplet/src/main.c @@ -0,0 +1,39 @@ +/* + * main.c + */ + +#include +#include +#include "client.h" + +/* We'll use a separate log module for main if desired. */ +LOG_MODULE_REGISTER(my_proplet_main, LOG_LEVEL_DBG); + +void main(void) +{ + LOG_INF("Proplet starting on ESP32-S3 with WAMR + MQTT..."); + + /* 1) Prepare the broker (resolve host/ip) */ + if (prepare_broker() != 0) { + LOG_ERR("Failed to prepare broker"); + return; + } + + /* 2) Initialize MQTT client struct */ + init_mqtt_client(); + + /* 3) Connect to broker */ + if (mqtt_connect_to_broker() != 0) { + LOG_ERR("Failed to connect to the MQTT broker"); + return; + } + + /* 4) Application main loop */ + while (1) { + /* Let the client handle MQTT input and keep-alive */ + mqtt_process_events(); + + /* Sleep some ms to avoid busy loops */ + k_sleep(K_MSEC(50)); + } +} From ae36a48655e2e10e4367a9386a4bf048cac54da8 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 12:04:16 +0300 Subject: [PATCH 02/50] Update submodule wasm-micro-runtime to latest commit --- embed-proplet/modules/wamr/wasm-micro-runtime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime index 902f7d2..45f07b8 160000 --- a/embed-proplet/modules/wamr/wasm-micro-runtime +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 902f7d26310b2b2832dddb3a3ca64141fcfc86f7 +Subproject commit 45f07b8bed348518d50b8c6d9a0f01518950b943 From eaa22f48553ed729aabb51434411797ed4591a0e Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 12:46:21 +0300 Subject: [PATCH 03/50] Force shared library off if building for Zephyr --- embed-proplet/modules/wamr/wasm-micro-runtime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime index 45f07b8..929879c 160000 --- a/embed-proplet/modules/wamr/wasm-micro-runtime +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 45f07b8bed348518d50b8c6d9a0f01518950b943 +Subproject commit 929879cc80169112ba1e009b093b71502a514e38 From 3a304effdd9a75f5173b4163b863c5e0fb30a221 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 14:26:32 +0300 Subject: [PATCH 04/50] Force shared library off if building for Zephyr --- embed-proplet/src/CMakeLists.txt | 2 -- embed-proplet/src/main.c | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/embed-proplet/src/CMakeLists.txt b/embed-proplet/src/CMakeLists.txt index 811ddbb..1e2ad38 100644 --- a/embed-proplet/src/CMakeLists.txt +++ b/embed-proplet/src/CMakeLists.txt @@ -1,5 +1,3 @@ -# src/CMakeLists.txt - target_sources(app PRIVATE main.c client.c diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 3181d59..0a11235 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,39 +1,28 @@ -/* - * main.c - */ - #include #include #include "client.h" -/* We'll use a separate log module for main if desired. */ LOG_MODULE_REGISTER(my_proplet_main, LOG_LEVEL_DBG); void main(void) { LOG_INF("Proplet starting on ESP32-S3 with WAMR + MQTT..."); - /* 1) Prepare the broker (resolve host/ip) */ if (prepare_broker() != 0) { LOG_ERR("Failed to prepare broker"); return; } - /* 2) Initialize MQTT client struct */ init_mqtt_client(); - /* 3) Connect to broker */ if (mqtt_connect_to_broker() != 0) { LOG_ERR("Failed to connect to the MQTT broker"); return; } - /* 4) Application main loop */ while (1) { - /* Let the client handle MQTT input and keep-alive */ mqtt_process_events(); - /* Sleep some ms to avoid busy loops */ k_sleep(K_MSEC(50)); } } From 85d5bd24afa93069114c18c8966a5b95c080f1c7 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 15:07:01 +0300 Subject: [PATCH 05/50] Fix autoconf.h not found --- embed-proplet/CMakeLists.txt | 6 --- embed-proplet/modules/wamr/CMakeLists.txt | 31 ++++++++++--- embed-proplet/modules/wamr/wasm-micro-runtime | 2 +- embed-proplet/prj.conf | 22 --------- embed-proplet/src/client.c | 45 ------------------- embed-proplet/src/client.h | 7 +-- 6 files changed, 27 insertions(+), 86 deletions(-) diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index b02d8f2..4c0840d 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -1,17 +1,11 @@ -# my_proplet/CMakeLists.txt - cmake_minimum_required(VERSION 3.20.0) -# Standard Zephyr boilerplate find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(my_proplet) -# Include our application source folder add_subdirectory(src) -# Include WAMR as a subdirectory (the submodule) add_subdirectory(modules/wamr) -# Link WAMR libraries to 'app' target link_wamr_to_zephyr(app) diff --git a/embed-proplet/modules/wamr/CMakeLists.txt b/embed-proplet/modules/wamr/CMakeLists.txt index 40431f4..5e14fbd 100644 --- a/embed-proplet/modules/wamr/CMakeLists.txt +++ b/embed-proplet/modules/wamr/CMakeLists.txt @@ -1,8 +1,12 @@ +# +# modules/wamr/CMakeLists.txt +# + cmake_minimum_required(VERSION 3.20) # # Force WAMR to treat this build as Zephyr + Xtensa -# (Adjust target name as needed: "XTENSA", "esp32", etc.) +# (Adjust target name if needed: "XTENSA", "esp32", etc.) # set(WAMR_BUILD_PLATFORM "zephyr" CACHE STRING "" FORCE) set(WAMR_BUILD_TARGET "XTENSA" CACHE STRING "" FORCE) @@ -16,16 +20,15 @@ set(WAMR_BUILD_JIT 0 CACHE BOOL "Disable JIT" FORCE) set(WAMR_BUILD_FAST_INTERP 1 CACHE BOOL "Enable fast interpreter" FORCE) set(WAMR_BUILD_LIBC_BUILTIN 1 CACHE BOOL "Enable builtin libc" FORCE) set(WAMR_BUILD_LIBC_WASI 1 CACHE BOOL "Enable WASI libc" FORCE) + # Disable pthread library in WAMR set(WAMR_BUILD_LIB_PTHREAD 0 CACHE BOOL "" FORCE) - # # Optional features you can uncomment if desired: # -#set(WAMR_BUILD_MULTI_MODULE 1 CACHE BOOL "Enable multiple modules" FORCE) -#set(WAMR_BUILD_LIB_PTHREAD 1 CACHE BOOL "Enable pthread library" FORCE) -#set(WAMR_BUILD_LIB_WASI_THREADS 1 CACHE BOOL "Enable WASI threads library" FORCE) +# set(WAMR_BUILD_MULTI_MODULE 1 CACHE BOOL "Enable multiple modules" FORCE) +# set(WAMR_BUILD_LIB_WASI_THREADS 1 CACHE BOOL "Enable WASI threads library" FORCE) # # Add the official WAMR subdirectory @@ -34,9 +37,25 @@ add_subdirectory(wasm-micro-runtime ${CMAKE_CURRENT_BINARY_DIR}/wamr_build ) +# +# If iwasm_static is created by the WAMR build, force it to include Zephyr's paths +# +if(TARGET iwasm_static) + message(STATUS "WAMR: Forcing Zephyr includes for iwasm_static") + + # Typically, Zephyr sets $ENV{ZEPHYR_BASE} or uses a CMake variable. + # We'll assume $ENV{ZEPHYR_BASE} is defined. Adjust if needed. + target_include_directories(iwasm_static PRIVATE + $ENV{ZEPHYR_BASE}/include + # If you need more subfolders, uncomment or add them here: + # $ENV{ZEPHYR_BASE}/include/zephyr + # $ENV{ZEPHYR_BASE}/subsys + ) +endif() + # # Provide a function for linking WAMR to the Zephyr 'app' target. -# WAMR typically builds a static library named 'vmlib', +# WAMR typically builds a static library named 'vmlib', # output from 'iwasm_static' with OUTPUT_NAME vmlib. # function(link_wamr_to_zephyr TARGET) diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime index 929879c..2688374 160000 --- a/embed-proplet/modules/wamr/wasm-micro-runtime +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 929879cc80169112ba1e009b093b71502a514e38 +Subproject commit 2688374f2b0db7aacbab511170050adfb666e0b9 diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 747e26d..5ab417f 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,44 +1,22 @@ -# -# --- Networking --- -# CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=n CONFIG_NET_SOCKETS=y -# -# --- Enable ESP32 Wi-Fi driver --- -# #CONFIG_WIFI=y #CONFIG_WIFI_ESP32=y - -# Auto-connect as a station #CONFIG_ESP32_WIFI_STA_AUTO=y - -# Reconnect if the station is disconnected #CONFIG_ESP32_WIFI_STA_RECONNECT=y - -# Wi-Fi SSID and password #CONFIG_ESP32_WIFI_STA_SSID="Octavifi" #CONFIG_ESP32_WIFI_STA_PASSWORD="Unic0rn_2030" -# -# --- MQTT --- -# CONFIG_MQTT_LIB=y CONFIG_MQTT_LIB_TLS=n CONFIG_MQTT_KEEPALIVE=60 -# -# --- Logging --- -# CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 -# -# --- Main Stack & Thread Priorities --- -# CONFIG_MAIN_STACK_SIZE=8192 -# Increase if needed for WAMR or other usage CONFIG_HEAP_MEM_POOL_SIZE=32768 diff --git a/embed-proplet/src/client.c b/embed-proplet/src/client.c index 66c794b..978ad65 100644 --- a/embed-proplet/src/client.c +++ b/embed-proplet/src/client.c @@ -18,11 +18,9 @@ LOG_MODULE_REGISTER(my_proplet_client, LOG_LEVEL_DBG); #define MQTT_TOPIC_WASM_BINARY "my_proplet/wasm_binary" #define MQTT_TOPIC_EXEC_RESULT "my_proplet/execution_result" -/* Buffers for MQTT messages */ static uint8_t rx_buffer[2048]; static uint8_t tx_buffer[2048]; -/* MQTT client object & broker storage */ static struct mqtt_client client; static struct sockaddr_storage broker_storage; @@ -31,7 +29,6 @@ static struct sockaddr_storage broker_storage; #define WASM_STACK_SIZE (8 * 1024) #define WASM_HEAP_SIZE (8 * 1024) -/* Forward declarations of any static (internal) functions */ static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt); @@ -39,9 +36,6 @@ static int mqtt_subscribe_to_topic(void); static int publish_execution_result(const char *result_str); static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size); -/* ------------------------------------------------------------------------- - * 1. Prepare the broker - * ------------------------------------------------------------------------- */ int prepare_broker(void) { struct sockaddr_in *broker4 = (struct sockaddr_in *)&broker_storage; @@ -57,9 +51,6 @@ int prepare_broker(void) return 0; } -/* ------------------------------------------------------------------------- - * 2. Initialize the MQTT client structure - * ------------------------------------------------------------------------- */ void init_mqtt_client(void) { mqtt_client_init(&client); @@ -70,25 +61,18 @@ void init_mqtt_client(void) client.client_id.utf8 = (uint8_t *)MQTT_CLIENT_ID; client.client_id.size = strlen(MQTT_CLIENT_ID); - /* MQTT version 3.1.1 */ client.protocol_version = MQTT_VERSION_3_1_1; - /* Assign buffers */ client.rx_buf = rx_buffer; client.rx_buf_size = sizeof(rx_buffer); client.tx_buf = tx_buffer; client.tx_buf_size = sizeof(tx_buffer); - /* Clean session */ client.clean_session = 1; - /* Non-secure transport in this example (port 1883) */ client.transport.type = MQTT_TRANSPORT_NON_SECURE; } -/* ------------------------------------------------------------------------- - * 3. Connect to the broker - * ------------------------------------------------------------------------- */ int mqtt_connect_to_broker(void) { int ret = mqtt_connect(&client); @@ -100,20 +84,12 @@ int mqtt_connect_to_broker(void) return 0; } -/* ------------------------------------------------------------------------- - * 4. The main MQTT processing loop - * Typically called from your main() in a while(1) or timed loop. - * ------------------------------------------------------------------------- */ void mqtt_process_events(void) { - /* Let the MQTT client handle input and keepalive pings. */ mqtt_input(&client); mqtt_live(&client); } -/* ------------------------------------------------------------------------- - * Subscribe to the WASM binary topic - * ------------------------------------------------------------------------- */ static int mqtt_subscribe_to_topic(void) { struct mqtt_topic subscribe_topic = { @@ -140,9 +116,6 @@ static int mqtt_subscribe_to_topic(void) return 0; } -/* ------------------------------------------------------------------------- - * Publish the execution result back - * ------------------------------------------------------------------------- */ static int publish_execution_result(const char *result_str) { struct mqtt_publish_param param; @@ -164,9 +137,6 @@ static int publish_execution_result(const char *result_str) return mqtt_publish(&client, ¶m); } -/* ------------------------------------------------------------------------- - * Execute the WASM buffer: load, instantiate, call "add" function, publish - * ------------------------------------------------------------------------- */ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) { static bool wamr_initialized = false; @@ -175,7 +145,6 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) memset(&init_args, 0, sizeof(RuntimeInitArgs)); init_args.mem_alloc_type = Alloc_With_Pool; - /* Provide a global heap for WAMR. */ static uint8_t global_heap_buf[WASM_MAX_APP_MEMORY]; init_args.mem_alloc_pool.heap_buf = global_heap_buf; init_args.mem_alloc_pool.heap_size = sizeof(global_heap_buf); @@ -188,7 +157,6 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) wamr_initialized = true; } - /* Load the module */ WASMModuleCommon *wasm_module = wasm_runtime_load((uint8_t*)wasm_buf, (uint32_t)wasm_size, NULL, 0); @@ -197,7 +165,6 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) return -1; } - /* Instantiate */ WASMModuleInstanceCommon *wasm_module_inst = wasm_runtime_instantiate(wasm_module, WASM_STACK_SIZE, @@ -209,7 +176,6 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) return -1; } - /* Find "add" function */ const char *func_name = "add"; wasm_function_inst_t func = wasm_runtime_lookup_function(wasm_module_inst, func_name, NULL); @@ -218,22 +184,18 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) goto clean; } - /* Prepare arguments: (i32, i32)->i32. We'll call add(3, 4). */ uint32_t argv[2]; argv[0] = 3; argv[1] = 4; - /* Call the function */ if (!wasm_runtime_call_wasm(wasm_module_inst, func, 2, argv)) { LOG_ERR("Failed to call '%s'", func_name); goto clean; } - /* The result is now in argv[0] */ int32_t result = (int32_t)argv[0]; LOG_INF("WASM function '%s'(3,4) -> %d", func_name, result); - /* Publish result */ char result_str[64]; snprintf(result_str, sizeof(result_str), "Result from '%s'(3,4): %d", func_name, result); @@ -245,9 +207,6 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) return 0; } -/* ------------------------------------------------------------------------- - * MQTT event callback (handles connect ack, publish, etc.) - * ------------------------------------------------------------------------- */ static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt) { @@ -266,7 +225,6 @@ static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_publish_param *p = &evt->param.publish; - /* Validate topic */ LOG_INF("Topic: %.*s, payload len %d", p->message.topic.topic.size, (char *)p->message.topic.topic.utf8, @@ -277,17 +235,14 @@ static void mqtt_evt_handler(struct mqtt_client *const c, break; } - /* Read the payload into rx_buffer */ int ret = mqtt_read_publish_payload(c, rx_buffer, p->message.payload.len); if (ret < 0) { LOG_ERR("mqtt_read_publish_payload error: %d", ret); break; } - /* Execute as WASM module */ execute_wasm_buffer(rx_buffer, p->message.payload.len); - /* Acknowledge QOS1 */ struct mqtt_puback_param puback = { .message_id = p->message_id }; diff --git a/embed-proplet/src/client.h b/embed-proplet/src/client.h index 9209c5c..407e6d3 100644 --- a/embed-proplet/src/client.h +++ b/embed-proplet/src/client.h @@ -9,27 +9,22 @@ #include #include -/* If you use WAMR in client.c, include WAMR headers here too */ #include "wasm_export.h" #ifdef __cplusplus extern "C" { #endif -/* Prepare the MQTT broker (resolve address) */ int prepare_broker(void); -/* Initialize the MQTT client structure */ void init_mqtt_client(void); -/* Connect to the MQTT broker */ int mqtt_connect_to_broker(void); -/* Handle MQTT input & keep-alive in a loop, typically called from main. */ void mqtt_process_events(void); #ifdef __cplusplus } #endif -#endif /* CLIENT_H */ +#endif \ No newline at end of file From 4f9b7b673fc081b7fd27a6900f3560256768452f Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 9 Jan 2025 16:39:51 +0300 Subject: [PATCH 06/50] Refactor mqtt client --- embed-proplet/src/client.c | 378 ++++++++++++++++++++----------------- embed-proplet/src/client.h | 26 +-- embed-proplet/src/main.c | 45 ++++- 3 files changed, 247 insertions(+), 202 deletions(-) diff --git a/embed-proplet/src/client.c b/embed-proplet/src/client.c index 978ad65..1a7c7f6 100644 --- a/embed-proplet/src/client.c +++ b/embed-proplet/src/client.c @@ -1,57 +1,58 @@ -/* - * client.c - */ +#include +#include +#include +#include +#include +#include +#include -#include "client.h" +LOG_MODULE_REGISTER(proplet_client, LOG_LEVEL_DBG); -/* If you need the Wi-Fi header, you can include it here, e.g.: - * #include - */ +#define MQTT_BROKER_HOSTNAME "test.mosquitto.org" +#define MQTT_BROKER_PORT 1883 +#define MQTT_CLIENT_ID "esp32s3_proplet" -/* Logging: create a separate log module for this client. */ -LOG_MODULE_REGISTER(my_proplet_client, LOG_LEVEL_DBG); - -/* Broker settings */ -#define MQTT_BROKER_HOSTNAME "test.mosquitto.org" -#define MQTT_BROKER_PORT 1883 -#define MQTT_CLIENT_ID "esp32s3_proplet" -#define MQTT_TOPIC_WASM_BINARY "my_proplet/wasm_binary" -#define MQTT_TOPIC_EXEC_RESULT "my_proplet/execution_result" +#define CONTROL_TOPIC_TEMPLATE "channels/%s/messages/control/manager" +#define REGISTRY_TOPIC_TEMPLATE "channels/%s/messages/registry/proplet" static uint8_t rx_buffer[2048]; static uint8_t tx_buffer[2048]; -static struct mqtt_client client; -static struct sockaddr_storage broker_storage; - -/* WAMR config */ #define WASM_MAX_APP_MEMORY (64 * 1024) #define WASM_STACK_SIZE (8 * 1024) #define WASM_HEAP_SIZE (8 * 1024) -static void mqtt_evt_handler(struct mqtt_client *const c, - const struct mqtt_evt *evt); - -static int mqtt_subscribe_to_topic(void); -static int publish_execution_result(const char *result_str); -static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size); - -int prepare_broker(void) -{ - struct sockaddr_in *broker4 = (struct sockaddr_in *)&broker_storage; - - broker4->sin_family = AF_INET; - broker4->sin_port = htons(MQTT_BROKER_PORT); - - if (net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker4->sin_addr)) { - LOG_ERR("Failed to parse broker hostname/IP: %s", MQTT_BROKER_HOSTNAME); - return -EINVAL; - } - - return 0; -} - -void init_mqtt_client(void) +static struct { + char channel_id[64]; + char thing_id[64]; + bool connected; + uint8_t wasm_chunks[10][4096]; + size_t chunk_sizes[10]; + int total_chunks; + int received_chunks; + bool fetching_binary; + char current_app[64]; +} app_state; + +struct json_rpc_request { + char method[16]; + char params[128]; + int id; +}; + +static const struct json_obj_descr json_rpc_descr[] = { + JSON_OBJ_DESCR_PRIM(struct json_rpc_request, method, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct json_rpc_request, params, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct json_rpc_request, id, JSON_TOK_NUMBER), +}; + +static void start_wasm_app(const char *params); +static void stop_wasm_app(const char *params); +static void handle_json_rpc_message(const char *payload); +static void assemble_and_execute_wasm(void); +static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt); + +void init_mqtt_client(const char *channel_id, const char *thing_id) { mqtt_client_init(&client); @@ -71,74 +72,111 @@ void init_mqtt_client(void) client.clean_session = 1; client.transport.type = MQTT_TRANSPORT_NON_SECURE; + + strncpy(app_state.channel_id, channel_id, sizeof(app_state.channel_id) - 1); + strncpy(app_state.thing_id, thing_id, sizeof(app_state.thing_id) - 1); + app_state.connected = false; + app_state.total_chunks = 0; + app_state.received_chunks = 0; + app_state.fetching_binary = false; + memset(app_state.current_app, 0, sizeof(app_state.current_app)); } -int mqtt_connect_to_broker(void) +static void handle_json_rpc_message(const char *payload) { - int ret = mqtt_connect(&client); - if (ret) { - LOG_ERR("mqtt_connect failed: %d", ret); - return ret; + struct json_rpc_request rpc = {0}; + + if (json_obj_parse(payload, strlen(payload), json_rpc_descr, + ARRAY_SIZE(json_rpc_descr), &rpc) < 0) { + LOG_ERR("Failed to parse JSON-RPC message"); + return; } - LOG_INF("MQTT client connected"); - return 0; -} -void mqtt_process_events(void) -{ - mqtt_input(&client); - mqtt_live(&client); + LOG_INF("Received JSON-RPC method: %s, id: %d", rpc.method, rpc.id); + + if (strcmp(rpc.method, "start") == 0) { + start_wasm_app(rpc.params); + } else if (strcmp(rpc.method, "stop") == 0) { + stop_wasm_app(rpc.params); + } else { + LOG_WRN("Unsupported JSON-RPC method: %s", rpc.method); + } } -static int mqtt_subscribe_to_topic(void) +static void start_wasm_app(const char *params) { - struct mqtt_topic subscribe_topic = { - .topic = { - .utf8 = (uint8_t *)MQTT_TOPIC_WASM_BINARY, - .size = strlen(MQTT_TOPIC_WASM_BINARY) - }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE - }; + char app_name[64] = {0}; + + sscanf(params, "[\"%63[^\"]", app_name); + + LOG_INF("Starting WASM app: %s", app_name); - struct mqtt_subscription_list subscription_list = { - .list = &subscribe_topic, - .list_count = 1, - .message_id = 1234 + if (app_state.fetching_binary) { + LOG_ERR("Already fetching another app, cannot fetch %s", app_name); + return; + } + + char topic[128]; + snprintf(topic, sizeof(topic), REGISTRY_TOPIC_TEMPLATE, app_state.channel_id); + + char payload[128]; + snprintf(payload, sizeof(payload), "{\"app_name\":\"%s\"}", app_name); + + struct mqtt_publish_param param = { + .message.topic = { + .topic = { .utf8 = topic, .size = strlen(topic) }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE + }, + .message.payload = { .data = payload, .len = strlen(payload) }, + .dup_flag = 0, + .retain_flag = 0, + .message_id = 1 }; - int ret = mqtt_subscribe(&client, &subscription_list); - if (ret) { - LOG_ERR("Failed to subscribe, err %d", ret); - return ret; + int ret = mqtt_publish(&client, ¶m); + if (ret != 0) { + LOG_ERR("Failed to send registry request: %d", ret); + return; } - LOG_INF("Subscribed to topic: %s", MQTT_TOPIC_WASM_BINARY); - return 0; + app_state.fetching_binary = true; + strncpy(app_state.current_app, app_name, sizeof(app_state.current_app) - 1); + + LOG_INF("Requested WASM binary for app: %s", app_name); } -static int publish_execution_result(const char *result_str) +static void stop_wasm_app(const char *params) { - struct mqtt_publish_param param; - struct mqtt_topic topic = { - .topic = { - .utf8 = (uint8_t *)MQTT_TOPIC_EXEC_RESULT, - .size = strlen(MQTT_TOPIC_EXEC_RESULT), - }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE - }; + char app_name[64] = {0}; - param.message.topic = topic; - param.message.payload.data = (uint8_t *)result_str; - param.message.payload.len = strlen(result_str); - param.message_id = 5678; - param.dup_flag = 0; - param.retain_flag = 0; + sscanf(params, "[\"%63[^\"]", app_name); - return mqtt_publish(&client, ¶m); + LOG_INF("Stopping WASM app: %s", app_name); + + LOG_INF("WASM app %s stopped", app_name); } -static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) +static void assemble_and_execute_wasm(void) { + size_t total_size = 0; + for (int i = 0; i < app_state.received_chunks; i++) { + total_size += app_state.chunk_sizes[i]; + } + + uint8_t *binary = k_malloc(total_size); + if (!binary) { + LOG_ERR("Failed to allocate memory for WASM binary"); + return; + } + + size_t offset = 0; + for (int i = 0; i < app_state.received_chunks; i++) { + memcpy(binary + offset, app_state.wasm_chunks[i], app_state.chunk_sizes[i]); + offset += app_state.chunk_sizes[i]; + } + + LOG_INF("Executing WASM binary for app: %s", app_state.current_app); + static bool wamr_initialized = false; if (!wamr_initialized) { RuntimeInitArgs init_args; @@ -151,116 +189,118 @@ static int execute_wasm_buffer(const uint8_t *wasm_buf, size_t wasm_size) if (!wasm_runtime_full_init(&init_args)) { LOG_ERR("Failed to initialize WAMR runtime"); - return -1; + k_free(binary); + return; } - wamr_initialized = true; } - WASMModuleCommon *wasm_module = wasm_runtime_load((uint8_t*)wasm_buf, - (uint32_t)wasm_size, - NULL, 0); - if (!wasm_module) { + WASMModuleCommon *module = wasm_runtime_load(binary, total_size, NULL, 0); + if (!module) { LOG_ERR("Failed to load WASM module"); - return -1; + k_free(binary); + return; } - WASMModuleInstanceCommon *wasm_module_inst = - wasm_runtime_instantiate(wasm_module, - WASM_STACK_SIZE, - WASM_HEAP_SIZE, - NULL, 0); - if (!wasm_module_inst) { + WASMModuleInstanceCommon *instance = wasm_runtime_instantiate(module, WASM_STACK_SIZE, WASM_HEAP_SIZE, NULL, 0); + if (!instance) { LOG_ERR("Failed to instantiate WASM module"); - wasm_runtime_unload(wasm_module); - return -1; + wasm_runtime_unload(module); + k_free(binary); + return; } - const char *func_name = "add"; - wasm_function_inst_t func = - wasm_runtime_lookup_function(wasm_module_inst, func_name, NULL); + const char *func_name = "run"; + wasm_function_inst_t func = wasm_runtime_lookup_function(instance, func_name, NULL); if (!func) { - LOG_ERR("Exported function '%s' not found in wasm", func_name); - goto clean; + LOG_ERR("Function '%s' not found", func_name); + goto cleanup; } - uint32_t argv[2]; - argv[0] = 3; - argv[1] = 4; - - if (!wasm_runtime_call_wasm(wasm_module_inst, func, 2, argv)) { + uint32_t argv[2] = {3, 4}; + if (!wasm_runtime_call_wasm(instance, func, 2, argv)) { LOG_ERR("Failed to call '%s'", func_name); - goto clean; + goto cleanup; } - int32_t result = (int32_t)argv[0]; - LOG_INF("WASM function '%s'(3,4) -> %d", func_name, result); + LOG_INF("Function '%s' executed successfully, result: %d", func_name, argv[0]); - char result_str[64]; - snprintf(result_str, sizeof(result_str), - "Result from '%s'(3,4): %d", func_name, result); - publish_execution_result(result_str); +cleanup: + wasm_runtime_deinstantiate(instance); + wasm_runtime_unload(module); + k_free(binary); -clean: - wasm_runtime_deinstantiate(wasm_module_inst); - wasm_runtime_unload(wasm_module); - return 0; + app_state.fetching_binary = false; + app_state.total_chunks = 0; + app_state.received_chunks = 0; + memset(app_state.current_app, 0, sizeof(app_state.current_app)); } -static void mqtt_evt_handler(struct mqtt_client *const c, - const struct mqtt_evt *evt) +static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt) { switch (evt->type) { case MQTT_EVT_CONNACK: - LOG_INF("MQTT_EVT_CONNACK"); - mqtt_subscribe_to_topic(); - break; - - case MQTT_EVT_DISCONNECT: - LOG_INF("MQTT_EVT_DISCONNECT"); + LOG_INF("MQTT connected"); + app_state.connected = true; break; - case MQTT_EVT_PUBLISH: { - LOG_INF("MQTT_EVT_PUBLISH"); - - const struct mqtt_publish_param *p = &evt->param.publish; - - LOG_INF("Topic: %.*s, payload len %d", - p->message.topic.topic.size, - (char *)p->message.topic.topic.utf8, - p->message.payload.len); - - if (p->message.payload.len > sizeof(rx_buffer)) { - LOG_ERR("Received payload is too large!"); - break; - } - - int ret = mqtt_read_publish_payload(c, rx_buffer, p->message.payload.len); - if (ret < 0) { - LOG_ERR("mqtt_read_publish_payload error: %d", ret); - break; + + case MQTT_EVT_PUBLISH: + if (evt->param.publish.message.payload.len < sizeof(rx_buffer)) { + int ret = mqtt_read_publish_payload(c, rx_buffer, evt->param.publish.message.payload.len); + if (ret < 0) { + LOG_ERR("Failed to read payload: %d", ret); + return; + } + rx_buffer[evt->param.publish.message.payload.len] = '\0'; + + if (strcmp(evt->param.publish.message.topic.topic.utf8, + "channels//messages/control/manager") == 0) { + handle_json_rpc_message(rx_buffer); + } else if (strcmp(evt->param.publish.message.topic.topic.utf8, + "channels//messages/registry/proplet") == 0) { + struct chunk_payload { + int chunk_idx; + int total_chunks; + uint8_t data[4096]; + }; + + struct json_obj_descr chunk_payload_descr[] = { + JSON_OBJ_DESCR_PRIM(struct chunk_payload, chunk_idx, JSON_TOK_NUMBER), + JSON_OBJ_DESCR_PRIM(struct chunk_payload, total_chunks, JSON_TOK_NUMBER), + JSON_OBJ_DESCR_PRIM(struct chunk_payload, data, JSON_TOK_STRING), + }; + + struct chunk_payload chunk = {0}; + if (json_obj_parse(rx_buffer, strlen(rx_buffer), chunk_payload_descr, + ARRAY_SIZE(chunk_payload_descr), &chunk) < 0) { + LOG_ERR("Failed to parse chunk payload"); + return; + } + + memcpy(app_state.wasm_chunks[chunk.chunk_idx], chunk.data, sizeof(chunk.data)); + app_state.chunk_sizes[chunk.chunk_idx] = strlen(chunk.data); + app_state.total_chunks = chunk.total_chunks; + app_state.received_chunks++; + + LOG_INF("Received chunk %d/%d", chunk.chunk_idx + 1, chunk.total_chunks); + + if (app_state.received_chunks == app_state.total_chunks) { + assemble_and_execute_wasm(); + } + } + } else { + LOG_ERR("Received payload too large!"); } - - execute_wasm_buffer(rx_buffer, p->message.payload.len); - - struct mqtt_puback_param puback = { - .message_id = p->message_id - }; - mqtt_publish_qos1_ack(c, &puback); - break; - } - - case MQTT_EVT_PUBACK: - LOG_INF("MQTT_EVT_PUBACK id=%u result=%d", - evt->param.puback.message_id, - evt->result); break; - case MQTT_EVT_SUBACK: - LOG_INF("MQTT_EVT_SUBACK"); + case MQTT_EVT_DISCONNECT: + LOG_INF("MQTT disconnected"); + app_state.connected = false; break; default: + LOG_WRN("Unhandled MQTT event: %d", evt->type); break; } } diff --git a/embed-proplet/src/client.h b/embed-proplet/src/client.h index 407e6d3..a18f334 100644 --- a/embed-proplet/src/client.h +++ b/embed-proplet/src/client.h @@ -1,30 +1,10 @@ #ifndef CLIENT_H #define CLIENT_H -#include #include -#include -#include -#include -#include -#include -#include "wasm_export.h" +void init_mqtt_client(const char *channel_id, const char *thing_id); -#ifdef __cplusplus -extern "C" { -#endif +void mqtt_event_handler(struct mqtt_client *const client, const struct mqtt_evt *evt); -int prepare_broker(void); - -void init_mqtt_client(void); - -int mqtt_connect_to_broker(void); - -void mqtt_process_events(void); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file +#endif diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 0a11235..96e863c 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,28 +1,53 @@ #include +#include #include #include "client.h" -LOG_MODULE_REGISTER(my_proplet_main, LOG_LEVEL_DBG); +LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); + +#define CHANNEL_ID "my_channel_id" +#define THING_ID "my_thing_id" + +static struct mqtt_client mqtt_client; +static struct sockaddr_storage broker_storage; void main(void) { - LOG_INF("Proplet starting on ESP32-S3 with WAMR + MQTT..."); + LOG_INF("Starting Proplet MQTT Client"); + + init_mqtt_client(CHANNEL_ID, THING_ID); - if (prepare_broker() != 0) { - LOG_ERR("Failed to prepare broker"); + struct sockaddr_in *broker = (struct sockaddr_in *)&broker_storage; + broker->sin_family = AF_INET; + broker->sin_port = htons(MQTT_BROKER_PORT); + if (net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr) < 0) { + LOG_ERR("Failed to configure broker address"); return; } - init_mqtt_client(); + mqtt_client.broker = &broker_storage; + mqtt_client.evt_cb = mqtt_event_handler; - if (mqtt_connect_to_broker() != 0) { - LOG_ERR("Failed to connect to the MQTT broker"); + if (mqtt_connect(&mqtt_client) != 0) { + LOG_ERR("Failed to connect to MQTT broker"); return; } - while (1) { - mqtt_process_events(); + LOG_INF("Connected to MQTT broker. Entering event loop."); - k_sleep(K_MSEC(50)); + while (true) { + if (mqtt_input(&mqtt_client) < 0) { + LOG_ERR("MQTT input processing failed"); + break; + } + if (mqtt_live(&mqtt_client) < 0) { + LOG_ERR("MQTT keepalive failed"); + break; + } + + k_sleep(K_MSEC(100)); } + + mqtt_disconnect(&mqtt_client); + LOG_INF("Disconnected from MQTT broker."); } From 28ef9b823711e6cffb3df23f7a8d6a27ee904ddc Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Fri, 10 Jan 2025 00:25:33 +0300 Subject: [PATCH 07/50] Fix dependencies --- embed-proplet/CMakeLists.txt | 16 +- embed-proplet/prj.conf | 37 ++-- embed-proplet/src/CMakeLists.txt | 4 - embed-proplet/src/client.c | 306 ------------------------------- embed-proplet/src/client.h | 10 - embed-proplet/src/main.c | 179 ++++++++++++++---- embed-proplet/src/wasm_handler.c | 68 +++++++ embed-proplet/src/wasm_handler.h | 22 +++ 8 files changed, 272 insertions(+), 370 deletions(-) delete mode 100644 embed-proplet/src/CMakeLists.txt delete mode 100644 embed-proplet/src/client.c delete mode 100644 embed-proplet/src/client.h create mode 100644 embed-proplet/src/wasm_handler.c create mode 100644 embed-proplet/src/wasm_handler.h diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index 4c0840d..4b045fa 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -1,11 +1,19 @@ cmake_minimum_required(VERSION 3.20.0) +set(BOARD esp32s3_devkitc) + find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(my_proplet) +project(proplet_mqtt_client) + +include_directories(${ZEPHYR_BASE}/include/zephyr) + +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/modules/wamr/wasm-micro-runtime/product-mini/platforms/zephyr) -add_subdirectory(src) +# Add WAMR include paths +target_include_directories(app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/modules/wamr/wasm-micro-runtime/core/iwasm/include +) -add_subdirectory(modules/wamr) -link_wamr_to_zephyr(app) +target_sources(app PRIVATE src/main.c src/wasm_handler.c) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 5ab417f..ca9bc2e 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,22 +1,33 @@ -CONFIG_NETWORKING=y +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_HEAP_MEM_POOL_SIZE=16384 + +CONFIG_THREAD_RUNTIME_STATS=y + +CONFIG_WIFI=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Nakuja" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="12345678" + CONFIG_NET_IPV4=y CONFIG_NET_IPV6=n +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +CONFIG_NET_DHCPV4=y +CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y -#CONFIG_WIFI=y -#CONFIG_WIFI_ESP32=y -#CONFIG_ESP32_WIFI_STA_AUTO=y -#CONFIG_ESP32_WIFI_STA_RECONNECT=y -#CONFIG_ESP32_WIFI_STA_SSID="Octavifi" -#CONFIG_ESP32_WIFI_STA_PASSWORD="Unic0rn_2030" +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_LOG_DEFAULT_LEVEL=3 + +CONFIG_JSON_LIBRARY=y + +CONFIG_FILE_SYSTEM=y CONFIG_MQTT_LIB=y +CONFIG_MQTT_LIB_CUSTOM_TRANSPORT=n CONFIG_MQTT_LIB_TLS=n +CONFIG_MQTT_LIB_WEBSOCKET=n CONFIG_MQTT_KEEPALIVE=60 -CONFIG_LOG=y -CONFIG_LOG_DEFAULT_LEVEL=3 - -CONFIG_MAIN_STACK_SIZE=8192 - -CONFIG_HEAP_MEM_POOL_SIZE=32768 +CONFIG_ENTROPY_GENERATOR=y diff --git a/embed-proplet/src/CMakeLists.txt b/embed-proplet/src/CMakeLists.txt deleted file mode 100644 index 1e2ad38..0000000 --- a/embed-proplet/src/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -target_sources(app PRIVATE - main.c - client.c -) diff --git a/embed-proplet/src/client.c b/embed-proplet/src/client.c deleted file mode 100644 index 1a7c7f6..0000000 --- a/embed-proplet/src/client.c +++ /dev/null @@ -1,306 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(proplet_client, LOG_LEVEL_DBG); - -#define MQTT_BROKER_HOSTNAME "test.mosquitto.org" -#define MQTT_BROKER_PORT 1883 -#define MQTT_CLIENT_ID "esp32s3_proplet" - -#define CONTROL_TOPIC_TEMPLATE "channels/%s/messages/control/manager" -#define REGISTRY_TOPIC_TEMPLATE "channels/%s/messages/registry/proplet" - -static uint8_t rx_buffer[2048]; -static uint8_t tx_buffer[2048]; - -#define WASM_MAX_APP_MEMORY (64 * 1024) -#define WASM_STACK_SIZE (8 * 1024) -#define WASM_HEAP_SIZE (8 * 1024) - -static struct { - char channel_id[64]; - char thing_id[64]; - bool connected; - uint8_t wasm_chunks[10][4096]; - size_t chunk_sizes[10]; - int total_chunks; - int received_chunks; - bool fetching_binary; - char current_app[64]; -} app_state; - -struct json_rpc_request { - char method[16]; - char params[128]; - int id; -}; - -static const struct json_obj_descr json_rpc_descr[] = { - JSON_OBJ_DESCR_PRIM(struct json_rpc_request, method, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct json_rpc_request, params, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct json_rpc_request, id, JSON_TOK_NUMBER), -}; - -static void start_wasm_app(const char *params); -static void stop_wasm_app(const char *params); -static void handle_json_rpc_message(const char *payload); -static void assemble_and_execute_wasm(void); -static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt); - -void init_mqtt_client(const char *channel_id, const char *thing_id) -{ - mqtt_client_init(&client); - - client.broker = &broker_storage; - client.evt_cb = mqtt_evt_handler; - - client.client_id.utf8 = (uint8_t *)MQTT_CLIENT_ID; - client.client_id.size = strlen(MQTT_CLIENT_ID); - - client.protocol_version = MQTT_VERSION_3_1_1; - - client.rx_buf = rx_buffer; - client.rx_buf_size = sizeof(rx_buffer); - client.tx_buf = tx_buffer; - client.tx_buf_size = sizeof(tx_buffer); - - client.clean_session = 1; - - client.transport.type = MQTT_TRANSPORT_NON_SECURE; - - strncpy(app_state.channel_id, channel_id, sizeof(app_state.channel_id) - 1); - strncpy(app_state.thing_id, thing_id, sizeof(app_state.thing_id) - 1); - app_state.connected = false; - app_state.total_chunks = 0; - app_state.received_chunks = 0; - app_state.fetching_binary = false; - memset(app_state.current_app, 0, sizeof(app_state.current_app)); -} - -static void handle_json_rpc_message(const char *payload) -{ - struct json_rpc_request rpc = {0}; - - if (json_obj_parse(payload, strlen(payload), json_rpc_descr, - ARRAY_SIZE(json_rpc_descr), &rpc) < 0) { - LOG_ERR("Failed to parse JSON-RPC message"); - return; - } - - LOG_INF("Received JSON-RPC method: %s, id: %d", rpc.method, rpc.id); - - if (strcmp(rpc.method, "start") == 0) { - start_wasm_app(rpc.params); - } else if (strcmp(rpc.method, "stop") == 0) { - stop_wasm_app(rpc.params); - } else { - LOG_WRN("Unsupported JSON-RPC method: %s", rpc.method); - } -} - -static void start_wasm_app(const char *params) -{ - char app_name[64] = {0}; - - sscanf(params, "[\"%63[^\"]", app_name); - - LOG_INF("Starting WASM app: %s", app_name); - - if (app_state.fetching_binary) { - LOG_ERR("Already fetching another app, cannot fetch %s", app_name); - return; - } - - char topic[128]; - snprintf(topic, sizeof(topic), REGISTRY_TOPIC_TEMPLATE, app_state.channel_id); - - char payload[128]; - snprintf(payload, sizeof(payload), "{\"app_name\":\"%s\"}", app_name); - - struct mqtt_publish_param param = { - .message.topic = { - .topic = { .utf8 = topic, .size = strlen(topic) }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE - }, - .message.payload = { .data = payload, .len = strlen(payload) }, - .dup_flag = 0, - .retain_flag = 0, - .message_id = 1 - }; - - int ret = mqtt_publish(&client, ¶m); - if (ret != 0) { - LOG_ERR("Failed to send registry request: %d", ret); - return; - } - - app_state.fetching_binary = true; - strncpy(app_state.current_app, app_name, sizeof(app_state.current_app) - 1); - - LOG_INF("Requested WASM binary for app: %s", app_name); -} - -static void stop_wasm_app(const char *params) -{ - char app_name[64] = {0}; - - sscanf(params, "[\"%63[^\"]", app_name); - - LOG_INF("Stopping WASM app: %s", app_name); - - LOG_INF("WASM app %s stopped", app_name); -} - -static void assemble_and_execute_wasm(void) -{ - size_t total_size = 0; - for (int i = 0; i < app_state.received_chunks; i++) { - total_size += app_state.chunk_sizes[i]; - } - - uint8_t *binary = k_malloc(total_size); - if (!binary) { - LOG_ERR("Failed to allocate memory for WASM binary"); - return; - } - - size_t offset = 0; - for (int i = 0; i < app_state.received_chunks; i++) { - memcpy(binary + offset, app_state.wasm_chunks[i], app_state.chunk_sizes[i]); - offset += app_state.chunk_sizes[i]; - } - - LOG_INF("Executing WASM binary for app: %s", app_state.current_app); - - static bool wamr_initialized = false; - if (!wamr_initialized) { - RuntimeInitArgs init_args; - memset(&init_args, 0, sizeof(RuntimeInitArgs)); - init_args.mem_alloc_type = Alloc_With_Pool; - - static uint8_t global_heap_buf[WASM_MAX_APP_MEMORY]; - init_args.mem_alloc_pool.heap_buf = global_heap_buf; - init_args.mem_alloc_pool.heap_size = sizeof(global_heap_buf); - - if (!wasm_runtime_full_init(&init_args)) { - LOG_ERR("Failed to initialize WAMR runtime"); - k_free(binary); - return; - } - wamr_initialized = true; - } - - WASMModuleCommon *module = wasm_runtime_load(binary, total_size, NULL, 0); - if (!module) { - LOG_ERR("Failed to load WASM module"); - k_free(binary); - return; - } - - WASMModuleInstanceCommon *instance = wasm_runtime_instantiate(module, WASM_STACK_SIZE, WASM_HEAP_SIZE, NULL, 0); - if (!instance) { - LOG_ERR("Failed to instantiate WASM module"); - wasm_runtime_unload(module); - k_free(binary); - return; - } - - const char *func_name = "run"; - wasm_function_inst_t func = wasm_runtime_lookup_function(instance, func_name, NULL); - if (!func) { - LOG_ERR("Function '%s' not found", func_name); - goto cleanup; - } - - uint32_t argv[2] = {3, 4}; - if (!wasm_runtime_call_wasm(instance, func, 2, argv)) { - LOG_ERR("Failed to call '%s'", func_name); - goto cleanup; - } - - LOG_INF("Function '%s' executed successfully, result: %d", func_name, argv[0]); - -cleanup: - wasm_runtime_deinstantiate(instance); - wasm_runtime_unload(module); - k_free(binary); - - app_state.fetching_binary = false; - app_state.total_chunks = 0; - app_state.received_chunks = 0; - memset(app_state.current_app, 0, sizeof(app_state.current_app)); -} - -static void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt) -{ - switch (evt->type) { - case MQTT_EVT_CONNACK: - LOG_INF("MQTT connected"); - app_state.connected = true; - break; - - - case MQTT_EVT_PUBLISH: - if (evt->param.publish.message.payload.len < sizeof(rx_buffer)) { - int ret = mqtt_read_publish_payload(c, rx_buffer, evt->param.publish.message.payload.len); - if (ret < 0) { - LOG_ERR("Failed to read payload: %d", ret); - return; - } - rx_buffer[evt->param.publish.message.payload.len] = '\0'; - - if (strcmp(evt->param.publish.message.topic.topic.utf8, - "channels//messages/control/manager") == 0) { - handle_json_rpc_message(rx_buffer); - } else if (strcmp(evt->param.publish.message.topic.topic.utf8, - "channels//messages/registry/proplet") == 0) { - struct chunk_payload { - int chunk_idx; - int total_chunks; - uint8_t data[4096]; - }; - - struct json_obj_descr chunk_payload_descr[] = { - JSON_OBJ_DESCR_PRIM(struct chunk_payload, chunk_idx, JSON_TOK_NUMBER), - JSON_OBJ_DESCR_PRIM(struct chunk_payload, total_chunks, JSON_TOK_NUMBER), - JSON_OBJ_DESCR_PRIM(struct chunk_payload, data, JSON_TOK_STRING), - }; - - struct chunk_payload chunk = {0}; - if (json_obj_parse(rx_buffer, strlen(rx_buffer), chunk_payload_descr, - ARRAY_SIZE(chunk_payload_descr), &chunk) < 0) { - LOG_ERR("Failed to parse chunk payload"); - return; - } - - memcpy(app_state.wasm_chunks[chunk.chunk_idx], chunk.data, sizeof(chunk.data)); - app_state.chunk_sizes[chunk.chunk_idx] = strlen(chunk.data); - app_state.total_chunks = chunk.total_chunks; - app_state.received_chunks++; - - LOG_INF("Received chunk %d/%d", chunk.chunk_idx + 1, chunk.total_chunks); - - if (app_state.received_chunks == app_state.total_chunks) { - assemble_and_execute_wasm(); - } - } - } else { - LOG_ERR("Received payload too large!"); - } - break; - - case MQTT_EVT_DISCONNECT: - LOG_INF("MQTT disconnected"); - app_state.connected = false; - break; - - default: - LOG_WRN("Unhandled MQTT event: %d", evt->type); - break; - } -} diff --git a/embed-proplet/src/client.h b/embed-proplet/src/client.h deleted file mode 100644 index a18f334..0000000 --- a/embed-proplet/src/client.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CLIENT_H -#define CLIENT_H - -#include - -void init_mqtt_client(const char *channel_id, const char *thing_id); - -void mqtt_event_handler(struct mqtt_client *const client, const struct mqtt_evt *evt); - -#endif diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 96e863c..81ba8e1 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,53 +1,166 @@ #include -#include -#include -#include "client.h" +#include +#include +#include +#include +#include +#include +#include "wasm_handler.h" -LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); +#define MQTT_BROKER_HOSTNAME "broker.supermq.example" +#define MQTT_BROKER_PORT 1883 +#define PROPLET_ID "proplet_01" +#define CHANNEL_ID "channel_01" +#define TOPIC_REQUEST "/channels/" CHANNEL_ID "/messages/registry/request" +#define TOPIC_RESPONSE "/channels/" CHANNEL_ID "/messages/registry/response" -#define CHANNEL_ID "my_channel_id" -#define THING_ID "my_thing_id" +static struct mqtt_client client; +static struct sockaddr_storage broker_addr; +static uint8_t rx_buffer[512]; +static uint8_t tx_buffer[512]; +static int configure_broker(void) +{ + struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; + + broker->sin_family = AF_INET; + broker->sin_port = htons(MQTT_BROKER_PORT); -static struct mqtt_client mqtt_client; -static struct sockaddr_storage broker_storage; + int ret = inet_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); + if (ret != 1) { + printk("Failed to configure broker address.\n"); + return -EINVAL; + } -void main(void) + return 0; +} + +static void request_wasm_file(const char *app_name) { - LOG_INF("Starting Proplet MQTT Client"); + char request_payload[128]; + snprintf(request_payload, sizeof(request_payload), "{\"app_name\":\"%s\"}", app_name); - init_mqtt_client(CHANNEL_ID, THING_ID); + struct mqtt_publish_param pub_param = { + .message_id = sys_rand32_get(), + .message = { + .topic = { + .topic = { + .utf8 = TOPIC_REQUEST, + .size = strlen(TOPIC_REQUEST) + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE + }, + .payload = { + .data = request_payload, + .len = strlen(request_payload) + } + } + }; - struct sockaddr_in *broker = (struct sockaddr_in *)&broker_storage; - broker->sin_family = AF_INET; - broker->sin_port = htons(MQTT_BROKER_PORT); - if (net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr) < 0) { - LOG_ERR("Failed to configure broker address"); + int ret = mqtt_publish(&client, &pub_param); + if (ret) { + printk("Failed to request Wasm file: %d\n", ret); + } else { + printk("Requested Wasm file: %s\n", app_name); + } +} + +static void mqtt_event_handler(struct mqtt_client *c, const struct mqtt_evt *evt) +{ + switch (evt->type) { + case MQTT_EVT_CONNACK: + printk("MQTT connected to broker.\n"); + break; + + case MQTT_EVT_PUBLISH: { + const struct mqtt_publish_param *p = &evt->param.publish; + + if (strncmp(p->message.topic.topic.utf8, TOPIC_RESPONSE, p->message.topic.topic.size) == 0) { + handle_wasm_chunk(p->message.payload.data, p->message.payload.len); + } + break; + } + case MQTT_EVT_DISCONNECT: + printk("MQTT disconnected.\n"); + break; + + case MQTT_EVT_PUBACK: + printk("MQTT publish acknowledged.\n"); + break; + + default: + printk("Unhandled MQTT event: %d\n", evt->type); + break; + } +} + +static int mqtt_client_setup(void) +{ + mqtt_client_init(&client); + + client.broker = &broker_addr; + client.evt_cb = mqtt_event_handler; + client.client_id.utf8 = PROPLET_ID; + client.client_id.size = strlen(PROPLET_ID); + client.protocol_version = MQTT_VERSION_3_1_1; + + client.rx_buf = rx_buffer; + client.rx_buf_size = sizeof(rx_buffer); + client.tx_buf = tx_buffer; + client.tx_buf_size = sizeof(tx_buffer); + + return mqtt_connect(&client); +} + +static int wifi_connect(void) +{ + struct net_if *iface = net_if_get_default(); + if (!iface) { + printk("No default network interface found.\n"); + return -ENODEV; + } + + printk("Connecting to Wi-Fi...\n"); + int ret = net_wifi_connect(iface); + if (ret) { + printk("Wi-Fi connection failed: %d\n", ret); + return ret; + } + + k_sleep(K_SECONDS(5)); + + return 0; +} + +void main(void) +{ + printk("Starting Proplet MQTT client on ESP32-S3...\n"); + + int ret = wifi_connect(); + if (ret) { + printk("Failed to connect to Wi-Fi.\n"); return; } + printk("Wi-Fi connected.\n"); - mqtt_client.broker = &broker_storage; - mqtt_client.evt_cb = mqtt_event_handler; + ret = configure_broker(); + if (ret) { + printk("Failed to configure MQTT broker.\n"); + return; + } - if (mqtt_connect(&mqtt_client) != 0) { - LOG_ERR("Failed to connect to MQTT broker"); + ret = mqtt_client_setup(); + if (ret) { + printk("Failed to set up MQTT client: %d\n", ret); return; } - LOG_INF("Connected to MQTT broker. Entering event loop."); + printk("MQTT client setup complete.\n"); - while (true) { - if (mqtt_input(&mqtt_client) < 0) { - LOG_ERR("MQTT input processing failed"); - break; - } - if (mqtt_live(&mqtt_client) < 0) { - LOG_ERR("MQTT keepalive failed"); - break; - } + request_wasm_file("example_app"); + while (1) { + mqtt_input(&client); + mqtt_live(&client); k_sleep(K_MSEC(100)); } - - mqtt_disconnect(&mqtt_client); - LOG_INF("Disconnected from MQTT broker."); } diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c new file mode 100644 index 0000000..680a4fe --- /dev/null +++ b/embed-proplet/src/wasm_handler.c @@ -0,0 +1,68 @@ +#include "wasm_handler.h" +#include +#include + +LOG_MODULE_REGISTER(wasm_handler); + +static uint8_t wasm_file_buffer[8192]; +static size_t wasm_file_offset = 0; + +void handle_wasm_chunk(const uint8_t *data, size_t len) +{ + if (wasm_file_offset + len > sizeof(wasm_file_buffer)) { + LOG_ERR("Wasm file too large to fit in buffer."); + wasm_file_offset = 0; + return; + } + + memcpy(&wasm_file_buffer[wasm_file_offset], data, len); + wasm_file_offset += len; + + if (data[len - 1] == '\0') { + LOG_INF("All Wasm chunks received. Total size: %zu bytes. Executing Wasm file...", wasm_file_offset); + execute_wasm_from_memory(wasm_file_buffer, wasm_file_offset); + wasm_file_offset = 0; + } +} + +void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size) +{ + RuntimeInitArgs init_args = { .mem_alloc_type = Alloc_With_System_Allocator }; + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Failed to initialize WAMR runtime."); + return; + } + + char error_buf[128]; + wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, error_buf, sizeof(error_buf)); + if (!module) { + LOG_ERR("Failed to load Wasm module: %s", error_buf); + wasm_runtime_destroy(); + return; + } + + wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, 1024, 1024, error_buf, sizeof(error_buf)); + if (!module_inst) { + LOG_ERR("Failed to instantiate Wasm module: %s", error_buf); + wasm_runtime_unload(module); + wasm_runtime_destroy(); + return; + } + + wasm_function_inst_t func = wasm_runtime_lookup_function(module_inst, "main"); + if (func) { + LOG_INF("Executing Wasm application..."); + if (!wasm_runtime_call_wasm(module_inst, func, 0, NULL)) { + LOG_ERR("Error invoking Wasm function."); + } else { + LOG_INF("Wasm application executed successfully."); + } + } else { + LOG_ERR("Function 'main' not found in Wasm module."); + } + + + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); + wasm_runtime_destroy(); +} diff --git a/embed-proplet/src/wasm_handler.h b/embed-proplet/src/wasm_handler.h new file mode 100644 index 0000000..878f694 --- /dev/null +++ b/embed-proplet/src/wasm_handler.h @@ -0,0 +1,22 @@ +#ifndef WASM_HANDLER_H +#define WASM_HANDLER_H + +#include + +/** + * Handle a chunk of incoming Wasm file data. + * + * @param data Pointer to the data chunk. + * @param len Length of the data chunk. + */ +void handle_wasm_chunk(const uint8_t *data, size_t len); + +/** + * Execute a Wasm application from the provided memory buffer. + * + * @param wasm_data Pointer to the Wasm file data in memory. + * @param wasm_size Size of the Wasm file data in bytes. + */ +void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size); + +#endif /* WASM_HANDLER_H */ From 4186be7c08ed3c2d7f789a14e0bc373db60bdb9a Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Fri, 10 Jan 2025 12:01:20 +0300 Subject: [PATCH 08/50] Add WAMR support for Zephyr build --- embed-proplet/CMakeLists.txt | 45 ++++++++++++++++--- embed-proplet/modules/wamr/wasm-micro-runtime | 2 +- embed-proplet/prj.conf | 8 +++- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index 4b045fa..1ea4b82 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -5,15 +5,50 @@ set(BOARD esp32s3_devkitc) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(proplet_mqtt_client) -include_directories(${ZEPHYR_BASE}/include/zephyr) +set(WAMR_BUILD_PLATFORM "zephyr") +set(WAMR_BUILD_TARGET "XTENSA") + +if (NOT DEFINED WAMR_BUILD_INTERP) + set(WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + set(WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +# Disable libc wasi support by default. In the future, +# it can be enabled if libc wasi file/socket/lock support is ready on Zephyr platform +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + set(WAMR_BUILD_LIBC_WASI 0) +endif () + +set(WAMR_BUILD_GLOBAL_HEAP_POOL 1) +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + set(WAMR_BUILD_GLOBAL_HEAP_SIZE 40960) +endif () -list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/modules/wamr/wasm-micro-runtime/product-mini/platforms/zephyr) +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/modules/wamr/wasm-micro-runtime) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) -# Add WAMR include paths -target_include_directories(app PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/modules/wamr/wasm-micro-runtime/core/iwasm/include +list(APPEND ZEPHYR_EXTRA_MODULES ${WAMR_ROOT_DIR}/product-mini/platforms/zephyr) + +include_directories(${ZEPHYR_BASE}/include/zephyr) + +target_include_directories(app PRIVATE + ${WAMR_ROOT_DIR}/core/iwasm/include ) +# Embed WAMR as a Zephyr library +zephyr_library_named(wamr_lib) + +zephyr_library_sources(${WAMR_RUNTIME_LIB_SOURCE}) + +zephyr_library_app_memory(wamr_partition) target_sources(app PRIVATE src/main.c src/wasm_handler.c) +target_link_libraries(app PRIVATE wamr_lib) diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime index 2688374..8e40670 160000 --- a/embed-proplet/modules/wamr/wasm-micro-runtime +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 2688374f2b0db7aacbab511170050adfb666e0b9 +Subproject commit 8e40670d53470d886aa97b299f1d043a8f3820ba diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index ca9bc2e..1c62c5c 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,8 +1,9 @@ +CONFIG_USERSPACE=y +CONFIG_STACK_SENTINEL=y + CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=16384 -CONFIG_THREAD_RUNTIME_STATS=y - CONFIG_WIFI=y CONFIG_WIFI_CREDENTIALS_STATIC=y CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Nakuja" @@ -16,9 +17,12 @@ CONFIG_NET_DHCPV4=y CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y +CONFIG_PRINTK=y CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=8096 CONFIG_LOG_MODE_IMMEDIATE=y CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_THREAD_RUNTIME_STATS=y CONFIG_JSON_LIBRARY=y From e47cd343e40523861b16cc0baa17120ddb0aefec Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Fri, 10 Jan 2025 12:39:48 +0300 Subject: [PATCH 09/50] Handle CONFIG_COMMON_LIBC_ABORT conflicts for Zephyr --- embed-proplet/modules/wamr/wasm-micro-runtime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embed-proplet/modules/wamr/wasm-micro-runtime b/embed-proplet/modules/wamr/wasm-micro-runtime index 8e40670..a18f36a 160000 --- a/embed-proplet/modules/wamr/wasm-micro-runtime +++ b/embed-proplet/modules/wamr/wasm-micro-runtime @@ -1 +1 @@ -Subproject commit 8e40670d53470d886aa97b299f1d043a8f3820ba +Subproject commit a18f36a3e0cbc36ed8628843d69125f3741e5d22 From 80bf5fe7fe21a842675f67b4111c0a29590db5b3 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Fri, 10 Jan 2025 13:42:20 +0300 Subject: [PATCH 10/50] Fix Wi-Fi struct and function usage errors --- embed-proplet/prj.conf | 6 ++++++ embed-proplet/src/main.c | 24 ++++++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 1c62c5c..17a493c 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -5,9 +5,15 @@ CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=16384 CONFIG_WIFI=y +CONFIG_WIFI_ESP32=y CONFIG_WIFI_CREDENTIALS_STATIC=y CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Nakuja" CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="12345678" +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + CONFIG_NET_IPV4=y CONFIG_NET_IPV6=n diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 81ba8e1..d1b62e7 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -2,10 +2,14 @@ #include #include #include -#include #include #include #include "wasm_handler.h" +#include +#include +#include +#include +#include #define MQTT_BROKER_HOSTNAME "broker.supermq.example" #define MQTT_BROKER_PORT 1883 @@ -25,8 +29,8 @@ static int configure_broker(void) broker->sin_family = AF_INET; broker->sin_port = htons(MQTT_BROKER_PORT); - int ret = inet_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); - if (ret != 1) { + int ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); + if (ret != 0) { printk("Failed to configure broker address.\n"); return -EINVAL; } @@ -119,15 +123,23 @@ static int wifi_connect(void) return -ENODEV; } + struct wifi_connect_req_params params = { + .ssid = CONFIG_WIFI_CREDENTIALS_STATIC_SSID, + .ssid_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_SSID), + .psk = CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD, + .psk_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD), + .channel = WIFI_CHANNEL_ANY, + .security = WIFI_SECURITY_TYPE_PSK, + }; + printk("Connecting to Wi-Fi...\n"); - int ret = net_wifi_connect(iface); + int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, ¶ms, sizeof(params)); if (ret) { printk("Wi-Fi connection failed: %d\n", ret); return ret; } - k_sleep(K_SECONDS(5)); - + printk("Wi-Fi connected successfully.\n"); return 0; } From 6132ca0dd3ecf0910fa437f35617a65618054c79 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Fri, 10 Jan 2025 16:01:53 +0300 Subject: [PATCH 11/50] Fix null network interface --- .../boards/esp32s3_devkitc_procpu.conf | 5 +++ .../boards/esp32s3_devkitc_procpu.overlay | 10 ++++++ embed-proplet/prj.conf | 31 ++++++++++++++++++- embed-proplet/src/main.c | 10 +++--- 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 embed-proplet/boards/esp32s3_devkitc_procpu.conf create mode 100644 embed-proplet/boards/esp32s3_devkitc_procpu.overlay diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf new file mode 100644 index 0000000..9df750f --- /dev/null +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -0,0 +1,5 @@ +CONFIG_WIFI_ESP32=y +CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y +CONFIG_ESP32_WIFI_AP_STA_MODE=y +CONFIG_WIFI_NM=y +CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 \ No newline at end of file diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.overlay b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay new file mode 100644 index 0000000..a53651c --- /dev/null +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay @@ -0,0 +1,10 @@ +&wifi { + status = "okay"; +}; + +/ { + wifi_ap: wifi_ap { + compatible = "espressif,esp32-wifi"; + status = "okay"; + }; +}; diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 17a493c..caf64ff 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -5,7 +5,6 @@ CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=16384 CONFIG_WIFI=y -CONFIG_WIFI_ESP32=y CONFIG_WIFI_CREDENTIALS_STATIC=y CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Nakuja" CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="12345678" @@ -41,3 +40,33 @@ CONFIG_MQTT_LIB_WEBSOCKET=n CONFIG_MQTT_KEEPALIVE=60 CONFIG_ENTROPY_GENERATOR=y + +# Wi-Fi Configuration +# CONFIG_WIFI=y + +# Network Configuration +CONFIG_NET_CONFIG_AUTO_INIT=n +CONFIG_NET_CONNECTION_MANAGER=y +# CONFIG_NET_DHCPV4=y +CONFIG_NET_DHCPV4_SERVER=y +CONFIG_NET_IF_MAX_IPV4_COUNT=2 +CONFIG_NET_IF_MAX_IPV6_COUNT=2 +# CONFIG_NET_IPV4=y +CONFIG_NET_L2_ETHERNET=y +# CONFIG_NET_L2_WIFI_MGMT=y +# CONFIG_NET_MGMT=y +# CONFIG_NET_MGMT_EVENT=y +# CONFIG_NET_MGMT_EVENT_INFO=y +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=10 +CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 +CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +# CONFIG_NETWORKING=y + +# LOG Configuration +CONFIG_NET_LOG=y +CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y +CONFIG_NET_DHCPV4=y diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index d1b62e7..7aebab5 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -10,6 +10,7 @@ #include #include #include +#include #define MQTT_BROKER_HOSTNAME "broker.supermq.example" #define MQTT_BROKER_PORT 1883 @@ -117,9 +118,9 @@ static int mqtt_client_setup(void) static int wifi_connect(void) { - struct net_if *iface = net_if_get_default(); - if (!iface) { - printk("No default network interface found.\n"); + struct net_if *sta_iface = net_if_get_wifi_sta(); + if (!sta_iface) { + printk("No STA interface found.\n"); return -ENODEV; } @@ -133,7 +134,7 @@ static int wifi_connect(void) }; printk("Connecting to Wi-Fi...\n"); - int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, ¶ms, sizeof(params)); + int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, ¶ms, sizeof(params)); if (ret) { printk("Wi-Fi connection failed: %d\n", ret); return ret; @@ -143,6 +144,7 @@ static int wifi_connect(void) return 0; } + void main(void) { printk("Starting Proplet MQTT client on ESP32-S3...\n"); From f24996432685f39c76c6a055ca1d90d407c31e1a Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Sun, 12 Jan 2025 13:47:20 +0300 Subject: [PATCH 12/50] Fix Wi-Fi not in station mode --- .../boards/esp32s3_devkitc_procpu.conf | 11 ++++++----- embed-proplet/prj.conf | 19 +++++-------------- embed-proplet/src/main.c | 7 ++++++- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf index 9df750f..5f3007a 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.conf +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -1,5 +1,6 @@ -CONFIG_WIFI_ESP32=y -CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y -CONFIG_ESP32_WIFI_AP_STA_MODE=y -CONFIG_WIFI_NM=y -CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 \ No newline at end of file +# CONFIG_WIFI_ESP32=y +# CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y +# CONFIG_ESP32_WIFI_AP_STA_MODE=y +# CONFIG_WIFI_NM=y +# CONFIG_WIFI=y +# CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 \ No newline at end of file diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index caf64ff..82bda37 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -6,8 +6,8 @@ CONFIG_HEAP_MEM_POOL_SIZE=16384 CONFIG_WIFI=y CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Nakuja" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="12345678" +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Octavifi" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="UniC0rn_2030" CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y @@ -41,22 +41,12 @@ CONFIG_MQTT_KEEPALIVE=60 CONFIG_ENTROPY_GENERATOR=y -# Wi-Fi Configuration -# CONFIG_WIFI=y - -# Network Configuration CONFIG_NET_CONFIG_AUTO_INIT=n CONFIG_NET_CONNECTION_MANAGER=y -# CONFIG_NET_DHCPV4=y CONFIG_NET_DHCPV4_SERVER=y CONFIG_NET_IF_MAX_IPV4_COUNT=2 CONFIG_NET_IF_MAX_IPV6_COUNT=2 -# CONFIG_NET_IPV4=y CONFIG_NET_L2_ETHERNET=y -# CONFIG_NET_L2_WIFI_MGMT=y -# CONFIG_NET_MGMT=y -# CONFIG_NET_MGMT_EVENT=y -# CONFIG_NET_MGMT_EVENT_INFO=y CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=10 CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 CONFIG_NET_PKT_RX_COUNT=16 @@ -64,9 +54,10 @@ CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 CONFIG_NET_TCP=y CONFIG_NET_UDP=y -# CONFIG_NETWORKING=y -# LOG Configuration CONFIG_NET_LOG=y CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y CONFIG_NET_DHCPV4=y + +CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y + diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 7aebab5..de00d28 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -118,12 +118,17 @@ static int mqtt_client_setup(void) static int wifi_connect(void) { - struct net_if *sta_iface = net_if_get_wifi_sta(); + struct net_if *sta_iface = net_if_get_wifi_sta(); // Get the station interface if (!sta_iface) { printk("No STA interface found.\n"); return -ENODEV; } + printk("STA interface found: %p\n", sta_iface); + + net_if_up(sta_iface); // Bring up the STA interface + printk("STA interface brought up.\n"); + struct wifi_connect_req_params params = { .ssid = CONFIG_WIFI_CREDENTIALS_STATIC_SSID, .ssid_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_SSID), From 1cad3359eddab8829fb7ebf7014a493d6753938a Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Sun, 12 Jan 2025 13:55:10 +0300 Subject: [PATCH 13/50] Add logging config documentation --- embed-proplet/boards/esp32s3_devkitc_procpu.conf | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf index 5f3007a..775f041 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.conf +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -1,6 +1,7 @@ -# CONFIG_WIFI_ESP32=y -# CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y -# CONFIG_ESP32_WIFI_AP_STA_MODE=y -# CONFIG_WIFI_NM=y -# CONFIG_WIFI=y -# CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 \ No newline at end of file +# This file can be useful for board-specific overrides or testing alternative configurations without affecting the main project-wide settings. + +# - CONFIG_LOG: Enables logging support in the system. +# - CONFIG_LOG_BUFFER_SIZE: Configures the size of the logging buffer. +# - CONFIG_LOG_MODE_IMMEDIATE: Ensures logs are immediately output without buffering. +# - CONFIG_LOG_DEFAULT_LEVEL: Sets the default logging verbosity level. +# - CONFIG_THREAD_RUNTIME_STATS: Enables tracking and reporting of thread runtime statistics. From 04cd2f7604dea4c13ce6ef601e06d08a01656a2f Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Sun, 12 Jan 2025 15:16:12 +0300 Subject: [PATCH 14/50] Update proj.cof --- embed-proplet/prj.conf | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 82bda37..10bb6c1 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -4,10 +4,9 @@ CONFIG_STACK_SENTINEL=y CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=16384 -CONFIG_WIFI=y CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Octavifi" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="UniC0rn_2030" +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="vive tu vida" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="IVYMARION3" CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y @@ -61,3 +60,9 @@ CONFIG_NET_DHCPV4=y CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y +CONFIG_WIFI_ESP32=y +CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y +CONFIG_ESP32_WIFI_AP_STA_MODE=n +CONFIG_WIFI_NM=y +CONFIG_WIFI=y +CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=1 \ No newline at end of file From 44340cd0572a1d5e4d069d446428e74e429a2002 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Sun, 12 Jan 2025 16:33:12 +0300 Subject: [PATCH 15/50] Update proj.cof --- embed-proplet/prj.conf | 8 ++++++-- embed-proplet/src/main.c | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 10bb6c1..2443f8a 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -25,7 +25,7 @@ CONFIG_PRINTK=y CONFIG_LOG=y CONFIG_LOG_BUFFER_SIZE=8096 CONFIG_LOG_MODE_IMMEDIATE=y -CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_DEFAULT_LEVEL=4 CONFIG_THREAD_RUNTIME_STATS=y CONFIG_JSON_LIBRARY=y @@ -42,6 +42,7 @@ CONFIG_ENTROPY_GENERATOR=y CONFIG_NET_CONFIG_AUTO_INIT=n CONFIG_NET_CONNECTION_MANAGER=y +CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=4096 CONFIG_NET_DHCPV4_SERVER=y CONFIG_NET_IF_MAX_IPV4_COUNT=2 CONFIG_NET_IF_MAX_IPV6_COUNT=2 @@ -65,4 +66,7 @@ CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y CONFIG_ESP32_WIFI_AP_STA_MODE=n CONFIG_WIFI_NM=y CONFIG_WIFI=y -CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=1 \ No newline at end of file +CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=1 +CONFIG_ESP32_WIFI_STA_RECONNECT=y +CONFIG_WIFI_LOG_LEVEL_DBG=y +CONFIG_ESP32_WIFI_DEBUG_PRINT=y \ No newline at end of file diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index de00d28..792beef 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -134,7 +134,8 @@ static int wifi_connect(void) .ssid_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_SSID), .psk = CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD, .psk_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD), - .channel = WIFI_CHANNEL_ANY, + .channel = 6, + .band = WIFI_FREQ_BAND_2_4_GHZ, .security = WIFI_SECURITY_TYPE_PSK, }; From 16bf8cf74431144447416ca523982bbc6f800f49 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Sun, 12 Jan 2025 16:55:20 +0300 Subject: [PATCH 16/50] Enable thread analyzer --- embed-proplet/prj.conf | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 2443f8a..bad29f0 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -69,4 +69,14 @@ CONFIG_WIFI=y CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=1 CONFIG_ESP32_WIFI_STA_RECONNECT=y CONFIG_WIFI_LOG_LEVEL_DBG=y -CONFIG_ESP32_WIFI_DEBUG_PRINT=y \ No newline at end of file +CONFIG_ESP32_WIFI_DEBUG_PRINT=y + +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_AUTO=y +CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 +CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y +CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y +CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y + +CONFIG_ISR_STACK_SIZE=4096 +CONFIG_MAIN_STACK_SIZE=6144 \ No newline at end of file From 734f062b6c4a358b2ab356dc4e32e54ceca59268 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 09:40:21 +0300 Subject: [PATCH 17/50] Fix STA mode --- .../boards/esp32s3_devkitc_procpu.conf | 15 +- .../boards/esp32s3_devkitc_procpu.overlay | 9 +- embed-proplet/prj.conf | 93 ++--- embed-proplet/src/main.c | 322 +++++++++--------- 4 files changed, 197 insertions(+), 242 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf index 775f041..8ec74a8 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.conf +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -1,7 +1,10 @@ -# This file can be useful for board-specific overrides or testing alternative configurations without affecting the main project-wide settings. +# Wi-Fi Configuration +CONFIG_WIFI=y +CONFIG_WIFI_ESP32=y +CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y +CONFIG_ESP32_WIFI_AP_STA_MODE=n +CONFIG_WIFI_NM=y +CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 +CONFIG_WIFI_LOG_LEVEL_DBG=y +CONFIG_ESP32_WIFI_DEBUG_PRINT=y -# - CONFIG_LOG: Enables logging support in the system. -# - CONFIG_LOG_BUFFER_SIZE: Configures the size of the logging buffer. -# - CONFIG_LOG_MODE_IMMEDIATE: Ensures logs are immediately output without buffering. -# - CONFIG_LOG_DEFAULT_LEVEL: Sets the default logging verbosity level. -# - CONFIG_THREAD_RUNTIME_STATS: Enables tracking and reporting of thread runtime statistics. diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.overlay b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay index a53651c..cdbd0d4 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.overlay +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay @@ -1,10 +1,3 @@ &wifi { status = "okay"; -}; - -/ { - wifi_ap: wifi_ap { - compatible = "espressif,esp32-wifi"; - status = "okay"; - }; -}; +}; \ No newline at end of file diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index bad29f0..1726911 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,52 +1,17 @@ -CONFIG_USERSPACE=y -CONFIG_STACK_SENTINEL=y - -CONFIG_MAIN_STACK_SIZE=4096 -CONFIG_HEAP_MEM_POOL_SIZE=16384 - -CONFIG_WIFI_CREDENTIALS_STATIC=y -CONFIG_WIFI_CREDENTIALS_STATIC_SSID="vive tu vida" -CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="IVYMARION3" -CONFIG_NET_L2_WIFI_MGMT=y -CONFIG_NET_MGMT=y -CONFIG_NET_MGMT_EVENT=y -CONFIG_NET_MGMT_EVENT_INFO=y - - -CONFIG_NET_IPV4=y -CONFIG_NET_IPV6=n -CONFIG_NET_TCP=y -CONFIG_NET_UDP=y -CONFIG_NET_DHCPV4=y -CONFIG_NETWORKING=y -CONFIG_NET_SOCKETS=y - -CONFIG_PRINTK=y -CONFIG_LOG=y -CONFIG_LOG_BUFFER_SIZE=8096 -CONFIG_LOG_MODE_IMMEDIATE=y -CONFIG_LOG_DEFAULT_LEVEL=4 -CONFIG_THREAD_RUNTIME_STATS=y - -CONFIG_JSON_LIBRARY=y - -CONFIG_FILE_SYSTEM=y - -CONFIG_MQTT_LIB=y -CONFIG_MQTT_LIB_CUSTOM_TRANSPORT=n -CONFIG_MQTT_LIB_TLS=n -CONFIG_MQTT_LIB_WEBSOCKET=n -CONFIG_MQTT_KEEPALIVE=60 - -CONFIG_ENTROPY_GENERATOR=y - +# Network Configuration CONFIG_NET_CONFIG_AUTO_INIT=n CONFIG_NET_CONNECTION_MANAGER=y -CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=4096 +CONFIG_NET_DHCPV4=y CONFIG_NET_DHCPV4_SERVER=y CONFIG_NET_IF_MAX_IPV4_COUNT=2 CONFIG_NET_IF_MAX_IPV6_COUNT=2 +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=10 CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 CONFIG_NET_PKT_RX_COUNT=16 @@ -54,29 +19,27 @@ CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 CONFIG_NET_TCP=y CONFIG_NET_UDP=y +CONFIG_NETWORKING=y +# LOG Configuration +CONFIG_LOG=y CONFIG_NET_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y -CONFIG_NET_DHCPV4=y - -CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y - -CONFIG_WIFI_ESP32=y -CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y -CONFIG_ESP32_WIFI_AP_STA_MODE=n -CONFIG_WIFI_NM=y -CONFIG_WIFI=y -CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=1 -CONFIG_ESP32_WIFI_STA_RECONNECT=y -CONFIG_WIFI_LOG_LEVEL_DBG=y -CONFIG_ESP32_WIFI_DEBUG_PRINT=y - -CONFIG_THREAD_ANALYZER=y -CONFIG_THREAD_ANALYZER_AUTO=y -CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 -CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y -CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y -CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y -CONFIG_ISR_STACK_SIZE=4096 -CONFIG_MAIN_STACK_SIZE=6144 \ No newline at end of file +#Network Buffer Configuration +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_BUF_RX_COUNT=32 +CONFIG_NET_BUF_TX_COUNT=32 +CONFIG_NET_BUF_DATA_SIZE=512 +CONFIG_NET_RX_STACK_SIZE=4096 +CONFIG_NET_TX_STACK_SIZE=4096 +CONFIG_NET_PKT_RX_COUNT=64 +CONFIG_NET_PKT_TX_COUNT=64 + +CONFIG_EARLY_CONSOLE=y +CONFIG_INIT_STACKS=y +CONFIG_NET_STATISTICS=y +CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n +CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 \ No newline at end of file diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 792beef..023ac96 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,186 +1,182 @@ #include -#include -#include -#include -#include -#include -#include "wasm_handler.h" -#include -#include -#include -#include +#include #include #include -#define MQTT_BROKER_HOSTNAME "broker.supermq.example" -#define MQTT_BROKER_PORT 1883 -#define PROPLET_ID "proplet_01" -#define CHANNEL_ID "channel_01" -#define TOPIC_REQUEST "/channels/" CHANNEL_ID "/messages/registry/request" -#define TOPIC_RESPONSE "/channels/" CHANNEL_ID "/messages/registry/response" - -static struct mqtt_client client; -static struct sockaddr_storage broker_addr; -static uint8_t rx_buffer[512]; -static uint8_t tx_buffer[512]; -static int configure_broker(void) -{ - struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; +LOG_MODULE_REGISTER(MAIN); - broker->sin_family = AF_INET; - broker->sin_port = htons(MQTT_BROKER_PORT); +#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" - int ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); - if (ret != 0) { - printk("Failed to configure broker address.\n"); - return -EINVAL; - } +#define NET_EVENT_WIFI_MASK \ + (NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | \ + NET_EVENT_WIFI_AP_ENABLE_RESULT | NET_EVENT_WIFI_AP_DISABLE_RESULT | \ + NET_EVENT_WIFI_AP_STA_CONNECTED | NET_EVENT_WIFI_AP_STA_DISCONNECTED) - return 0; -} +/* AP Mode Configuration */ +#define WIFI_AP_SSID "ESP32S3-AP" +#define WIFI_AP_PSK "" +#define WIFI_AP_IP_ADDRESS "192.168.4.1" +#define WIFI_AP_NETMASK "255.255.255.0" -static void request_wasm_file(const char *app_name) -{ - char request_payload[128]; - snprintf(request_payload, sizeof(request_payload), "{\"app_name\":\"%s\"}", app_name); - - struct mqtt_publish_param pub_param = { - .message_id = sys_rand32_get(), - .message = { - .topic = { - .topic = { - .utf8 = TOPIC_REQUEST, - .size = strlen(TOPIC_REQUEST) - }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE - }, - .payload = { - .data = request_payload, - .len = strlen(request_payload) - } - } - }; - - int ret = mqtt_publish(&client, &pub_param); - if (ret) { - printk("Failed to request Wasm file: %d\n", ret); - } else { - printk("Requested Wasm file: %s\n", app_name); - } -} +/* STA Mode Configuration */ +#define WIFI_SSID "Octavifi" +#define WIFI_PSK "Unic0rn_2030" + +static struct net_if *ap_iface; +static struct net_if *sta_iface; -static void mqtt_event_handler(struct mqtt_client *c, const struct mqtt_evt *evt) +static struct wifi_connect_req_params ap_config; +static struct wifi_connect_req_params sta_config; + +static struct net_mgmt_event_callback cb; + +static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, + struct net_if *iface) { - switch (evt->type) { - case MQTT_EVT_CONNACK: - printk("MQTT connected to broker.\n"); - break; - - case MQTT_EVT_PUBLISH: { - const struct mqtt_publish_param *p = &evt->param.publish; - - if (strncmp(p->message.topic.topic.utf8, TOPIC_RESPONSE, p->message.topic.topic.size) == 0) { - handle_wasm_chunk(p->message.payload.data, p->message.payload.len); - } - break; - } - case MQTT_EVT_DISCONNECT: - printk("MQTT disconnected.\n"); - break; - - case MQTT_EVT_PUBACK: - printk("MQTT publish acknowledged.\n"); - break; - - default: - printk("Unhandled MQTT event: %d\n", evt->type); - break; - } + switch (mgmt_event) { + case NET_EVENT_WIFI_CONNECT_RESULT: { + LOG_INF("Connected to %s", WIFI_SSID); + break; + } + case NET_EVENT_WIFI_DISCONNECT_RESULT: { + LOG_INF("Disconnected from %s", WIFI_SSID); + break; + } + case NET_EVENT_WIFI_AP_ENABLE_RESULT: { + LOG_INF("AP Mode is enabled. Waiting for station to connect"); + break; + } + case NET_EVENT_WIFI_AP_DISABLE_RESULT: { + LOG_INF("AP Mode is disabled."); + break; + } + case NET_EVENT_WIFI_AP_STA_CONNECTED: { + struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; + + LOG_INF("station: " MACSTR " joined ", sta_info->mac[0], sta_info->mac[1], + sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]); + break; + } + case NET_EVENT_WIFI_AP_STA_DISCONNECTED: { + struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; + + LOG_INF("station: " MACSTR " leave ", sta_info->mac[0], sta_info->mac[1], + sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]); + break; + } + default: + break; + } } -static int mqtt_client_setup(void) +static void enable_dhcpv4_server(void) { - mqtt_client_init(&client); + static struct in_addr addr; + static struct in_addr netmaskAddr; + + if (net_addr_pton(AF_INET, WIFI_AP_IP_ADDRESS, &addr)) { + LOG_ERR("Invalid address: %s", WIFI_AP_IP_ADDRESS); + return; + } + + if (net_addr_pton(AF_INET, WIFI_AP_NETMASK, &netmaskAddr)) { + LOG_ERR("Invalid netmask: %s", WIFI_AP_NETMASK); + return; + } + + net_if_ipv4_set_gw(ap_iface, &addr); - client.broker = &broker_addr; - client.evt_cb = mqtt_event_handler; - client.client_id.utf8 = PROPLET_ID; - client.client_id.size = strlen(PROPLET_ID); - client.protocol_version = MQTT_VERSION_3_1_1; + if (net_if_ipv4_addr_add(ap_iface, &addr, NET_ADDR_MANUAL, 0) == NULL) { + LOG_ERR("unable to set IP address for AP interface"); + } - client.rx_buf = rx_buffer; - client.rx_buf_size = sizeof(rx_buffer); - client.tx_buf = tx_buffer; - client.tx_buf_size = sizeof(tx_buffer); + if (!net_if_ipv4_set_netmask_by_addr(ap_iface, &addr, &netmaskAddr)) { + LOG_ERR("Unable to set netmask for AP interface: %s", WIFI_AP_NETMASK); + } - return mqtt_connect(&client); + addr.s4_addr[3] += 10; + if (net_dhcpv4_server_start(ap_iface, &addr) != 0) { + LOG_ERR("DHCP server is not started for desired IP"); + return; + } + + LOG_INF("DHCPv4 server started...\n"); } -static int wifi_connect(void) +static int enable_ap_mode(void) { - struct net_if *sta_iface = net_if_get_wifi_sta(); // Get the station interface - if (!sta_iface) { - printk("No STA interface found.\n"); - return -ENODEV; - } - - printk("STA interface found: %p\n", sta_iface); - - net_if_up(sta_iface); // Bring up the STA interface - printk("STA interface brought up.\n"); - - struct wifi_connect_req_params params = { - .ssid = CONFIG_WIFI_CREDENTIALS_STATIC_SSID, - .ssid_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_SSID), - .psk = CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD, - .psk_length = strlen(CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD), - .channel = 6, - .band = WIFI_FREQ_BAND_2_4_GHZ, - .security = WIFI_SECURITY_TYPE_PSK, - }; - - printk("Connecting to Wi-Fi...\n"); - int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, ¶ms, sizeof(params)); - if (ret) { - printk("Wi-Fi connection failed: %d\n", ret); - return ret; - } - - printk("Wi-Fi connected successfully.\n"); - return 0; + if (!ap_iface) { + LOG_INF("AP: is not initialized"); + return -EIO; + } + + LOG_INF("Turning on AP Mode"); + ap_config.ssid = (const uint8_t *)WIFI_AP_SSID; + ap_config.ssid_length = strlen(WIFI_AP_SSID); + ap_config.psk = (const uint8_t *)WIFI_AP_PSK; + ap_config.psk_length = strlen(WIFI_AP_PSK); + ap_config.channel = WIFI_CHANNEL_ANY; + ap_config.band = WIFI_FREQ_BAND_2_4_GHZ; + + if (strlen(WIFI_AP_PSK) == 0) { + ap_config.security = WIFI_SECURITY_TYPE_NONE; + } else { + + ap_config.security = WIFI_SECURITY_TYPE_PSK; + } + + enable_dhcpv4_server(); + + int ret = net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, &ap_config, + sizeof(struct wifi_connect_req_params)); + if (ret) { + LOG_ERR("NET_REQUEST_WIFI_AP_ENABLE failed, err: %d", ret); + } + + return ret; } - -void main(void) +static int connect_to_wifi(void) { - printk("Starting Proplet MQTT client on ESP32-S3...\n"); - - int ret = wifi_connect(); - if (ret) { - printk("Failed to connect to Wi-Fi.\n"); - return; - } - printk("Wi-Fi connected.\n"); - - ret = configure_broker(); - if (ret) { - printk("Failed to configure MQTT broker.\n"); - return; - } - - ret = mqtt_client_setup(); - if (ret) { - printk("Failed to set up MQTT client: %d\n", ret); - return; - } - - printk("MQTT client setup complete.\n"); - - request_wasm_file("example_app"); - - while (1) { - mqtt_input(&client); - mqtt_live(&client); - k_sleep(K_MSEC(100)); - } + if (!sta_iface) { + LOG_INF("STA: interface no initialized"); + return -EIO; + } + + sta_config.ssid = (const uint8_t *)WIFI_SSID; + sta_config.ssid_length = strlen(WIFI_SSID); + sta_config.psk = (const uint8_t *)WIFI_PSK; + sta_config.psk_length = strlen(WIFI_PSK); + sta_config.security = WIFI_SECURITY_TYPE_PSK; + sta_config.channel = WIFI_CHANNEL_ANY; + sta_config.band = WIFI_FREQ_BAND_2_4_GHZ; + + LOG_INF("Connecting to SSID: %s\n", sta_config.ssid); + + int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, &sta_config, + sizeof(struct wifi_connect_req_params)); + if (ret) { + LOG_ERR("Unable to Connect to (%s)", WIFI_SSID); + } + + return ret; } + +int main(void) +{ + k_sleep(K_SECONDS(5)); + + net_mgmt_init_event_callback(&cb, wifi_event_handler, NET_EVENT_WIFI_MASK); + net_mgmt_add_event_callback(&cb); + + /* Get AP interface in AP-STA mode. */ + ap_iface = net_if_get_wifi_sap(); + + /* Get STA interface in AP-STA mode. */ + sta_iface = net_if_get_wifi_sta(); + + // enable_ap_mode(); + connect_to_wifi(); + + return 0; +} \ No newline at end of file From c3464e67a354ecd49072b5beae9c4b1df9c477da Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 11:28:52 +0300 Subject: [PATCH 18/50] Increase stack sizes --- embed-proplet/prj.conf | 77 ++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 1726911..e8551c8 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,45 +1,72 @@ -# Network Configuration +# General Networking +CONFIG_NETWORKING=y CONFIG_NET_CONFIG_AUTO_INIT=n -CONFIG_NET_CONNECTION_MANAGER=y -CONFIG_NET_DHCPV4=y -CONFIG_NET_DHCPV4_SERVER=y -CONFIG_NET_IF_MAX_IPV4_COUNT=2 -CONFIG_NET_IF_MAX_IPV6_COUNT=2 -CONFIG_NET_IPV4=y -CONFIG_NET_IPV6=n -CONFIG_NET_L2_ETHERNET=y -CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_MGMT_EVENT_INFO=y CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=10 CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096 -CONFIG_NET_PKT_RX_COUNT=16 -CONFIG_NET_PKT_TX_COUNT=16 -CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 +CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 + +# Protocols +CONFIG_NET_DHCPV4=y +CONFIG_NET_DHCPV4_SERVER=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n CONFIG_NET_TCP=y CONFIG_NET_UDP=y -CONFIG_NETWORKING=y -# LOG Configuration -CONFIG_LOG=y -CONFIG_NET_LOG=y -CONFIG_LOG_DEFAULT_LEVEL=3 -CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y +# Interface Configuration +CONFIG_NET_IF_MAX_IPV4_COUNT=2 +CONFIG_NET_IF_MAX_IPV6_COUNT=2 +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_L2_WIFI_MGMT=y -#Network Buffer Configuration -CONFIG_NET_MAX_CONTEXTS=10 +# Buffer and Packet Management CONFIG_NET_BUF_RX_COUNT=32 CONFIG_NET_BUF_TX_COUNT=32 CONFIG_NET_BUF_DATA_SIZE=512 -CONFIG_NET_RX_STACK_SIZE=4096 -CONFIG_NET_TX_STACK_SIZE=4096 CONFIG_NET_PKT_RX_COUNT=64 CONFIG_NET_PKT_TX_COUNT=64 +CONFIG_NET_RX_STACK_SIZE=8192 +CONFIG_NET_TX_STACK_SIZE=4096 +CONFIG_NET_MAX_CONTEXTS=10 +# Logging +CONFIG_LOG=y +CONFIG_NET_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y +CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=8192 + +# Debugging and Early Boot CONFIG_EARLY_CONSOLE=y CONFIG_INIT_STACKS=y +CONFIG_STACK_SENTINEL=y + +# Statistics and Diagnostics CONFIG_NET_STATISTICS=y CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n -CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 -CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 \ No newline at end of file + +# Thread Analyzer +CONFIG_THREAD_NAME=y +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_AUTO=y +CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 +CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y +CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y + +# Wi-Fi Stack Sizes +CONFIG_WIFI_ESP_AT_RX_STACK_SIZE=10240 +CONFIG_WIFI_ESP_AT_WORKQ_STACK_SIZE=8192 +CONFIG_WIFI_NM_WPA_SUPPLICANT_THREAD_STACK_SIZE=8192 +CONFIG_WIFI_NM_WPA_SUPPLICANT_WQ_STACK_SIZE=8192 + +# Miscellaneous +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 +CONFIG_IDLE_STACK_SIZE=2048 +CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 +CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 \ No newline at end of file From 939a2819a12870c9de3ebfb42cf7584db1b070b1 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 13:32:35 +0300 Subject: [PATCH 19/50] modularize network; add mqtt client --- embed-proplet/CMakeLists.txt | 2 +- embed-proplet/prj.conf | 13 +- embed-proplet/src/main.c | 221 +++++++------------------------ embed-proplet/src/mqtt_client.c | 195 +++++++++++++++++++++++++++ embed-proplet/src/mqtt_client.h | 18 +++ embed-proplet/src/wifi_manager.c | 127 ++++++++++++++++++ embed-proplet/src/wifi_manager.h | 12 ++ 7 files changed, 405 insertions(+), 183 deletions(-) create mode 100644 embed-proplet/src/mqtt_client.c create mode 100644 embed-proplet/src/mqtt_client.h create mode 100644 embed-proplet/src/wifi_manager.c create mode 100644 embed-proplet/src/wifi_manager.h diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index 1ea4b82..46d0032 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -49,6 +49,6 @@ zephyr_library_sources(${WAMR_RUNTIME_LIB_SOURCE}) zephyr_library_app_memory(wamr_partition) -target_sources(app PRIVATE src/main.c src/wasm_handler.c) +target_sources(app PRIVATE src/main.c src/mqtt_client.c src/wifi_manager.c src/wasm_handler.c) target_link_libraries(app PRIVATE wamr_lib) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index e8551c8..b6b087f 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -18,7 +18,8 @@ CONFIG_NET_UDP=y # Interface Configuration CONFIG_NET_IF_MAX_IPV4_COUNT=2 -CONFIG_NET_IF_MAX_IPV6_COUNT=2 +# required if NET_IPV6 is set +# CONFIG_NET_IF_MAX_IPV6_COUNT=2 CONFIG_NET_L2_ETHERNET=y CONFIG_NET_L2_WIFI_MGMT=y @@ -32,6 +33,10 @@ CONFIG_NET_RX_STACK_SIZE=8192 CONFIG_NET_TX_STACK_SIZE=4096 CONFIG_NET_MAX_CONTEXTS=10 +# MQTT Client +CONFIG_MQTT_LIB=y +CONFIG_MQTT_KEEPALIVE=10 + # Logging CONFIG_LOG=y CONFIG_NET_LOG=y @@ -56,12 +61,6 @@ CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y -# Wi-Fi Stack Sizes -CONFIG_WIFI_ESP_AT_RX_STACK_SIZE=10240 -CONFIG_WIFI_ESP_AT_WORKQ_STACK_SIZE=8192 -CONFIG_WIFI_NM_WPA_SUPPLICANT_THREAD_STACK_SIZE=8192 -CONFIG_WIFI_NM_WPA_SUPPLICANT_WQ_STACK_SIZE=8192 - # Miscellaneous CONFIG_MAIN_STACK_SIZE=8192 CONFIG_NET_PKT_RX_COUNT=16 diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 023ac96..c7ff290 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,182 +1,53 @@ #include #include -#include -#include +#include "wifi_manager.h" +// #include "mqtt_client.h" -LOG_MODULE_REGISTER(MAIN); +LOG_MODULE_REGISTER(main); -#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" +#define WIFI_SSID "YourSSID" +#define WIFI_PSK "YourPassword" +// #define PROPLET_ID "proplet-esp32s3" +// #define CHANNEL_ID "default_channel" -#define NET_EVENT_WIFI_MASK \ - (NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | \ - NET_EVENT_WIFI_AP_ENABLE_RESULT | NET_EVENT_WIFI_AP_DISABLE_RESULT | \ - NET_EVENT_WIFI_AP_STA_CONNECTED | NET_EVENT_WIFI_AP_STA_DISCONNECTED) - -/* AP Mode Configuration */ -#define WIFI_AP_SSID "ESP32S3-AP" -#define WIFI_AP_PSK "" -#define WIFI_AP_IP_ADDRESS "192.168.4.1" -#define WIFI_AP_NETMASK "255.255.255.0" - -/* STA Mode Configuration */ -#define WIFI_SSID "Octavifi" -#define WIFI_PSK "Unic0rn_2030" - -static struct net_if *ap_iface; -static struct net_if *sta_iface; - -static struct wifi_connect_req_params ap_config; -static struct wifi_connect_req_params sta_config; - -static struct net_mgmt_event_callback cb; - -static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, - struct net_if *iface) +void main(void) { - switch (mgmt_event) { - case NET_EVENT_WIFI_CONNECT_RESULT: { - LOG_INF("Connected to %s", WIFI_SSID); - break; - } - case NET_EVENT_WIFI_DISCONNECT_RESULT: { - LOG_INF("Disconnected from %s", WIFI_SSID); - break; - } - case NET_EVENT_WIFI_AP_ENABLE_RESULT: { - LOG_INF("AP Mode is enabled. Waiting for station to connect"); - break; - } - case NET_EVENT_WIFI_AP_DISABLE_RESULT: { - LOG_INF("AP Mode is disabled."); - break; - } - case NET_EVENT_WIFI_AP_STA_CONNECTED: { - struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; - - LOG_INF("station: " MACSTR " joined ", sta_info->mac[0], sta_info->mac[1], - sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]); - break; - } - case NET_EVENT_WIFI_AP_STA_DISCONNECTED: { - struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; - - LOG_INF("station: " MACSTR " leave ", sta_info->mac[0], sta_info->mac[1], - sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]); - break; - } - default: - break; - } + LOG_INF("Proplet starting..."); + + /* Initialize Wi-Fi */ + wifi_manager_init(); + + /* Connect to Wi-Fi */ + int ret = wifi_manager_connect(WIFI_SSID, WIFI_PSK); + if (ret != 0) { + LOG_ERR("Failed to connect to Wi-Fi, ret=%d", ret); + return; + } + + // /* Initialize and connect MQTT client */ + // ret = mqtt_client_init_and_connect(); + // if (ret != 0) { + // LOG_ERR("Failed to initialize MQTT client, exiting"); + // return; + // } + + // /* Announce discovery */ + // ret = mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID); + // if (ret != 0) { + // LOG_ERR("Failed to publish discovery announcement, exiting"); + // return; + // } + + // /* Subscribe to topics */ + // ret = mqtt_client_subscribe(CHANNEL_ID); + // if (ret != 0) { + // LOG_ERR("Failed to subscribe to topics, exiting"); + // return; + // } + + // /* Main loop for processing MQTT events */ + // while (1) { + // mqtt_client_process(); /* Process MQTT events */ + // k_sleep(K_SECONDS(5)); /* Sleep for a while */ + // } } - -static void enable_dhcpv4_server(void) -{ - static struct in_addr addr; - static struct in_addr netmaskAddr; - - if (net_addr_pton(AF_INET, WIFI_AP_IP_ADDRESS, &addr)) { - LOG_ERR("Invalid address: %s", WIFI_AP_IP_ADDRESS); - return; - } - - if (net_addr_pton(AF_INET, WIFI_AP_NETMASK, &netmaskAddr)) { - LOG_ERR("Invalid netmask: %s", WIFI_AP_NETMASK); - return; - } - - net_if_ipv4_set_gw(ap_iface, &addr); - - if (net_if_ipv4_addr_add(ap_iface, &addr, NET_ADDR_MANUAL, 0) == NULL) { - LOG_ERR("unable to set IP address for AP interface"); - } - - if (!net_if_ipv4_set_netmask_by_addr(ap_iface, &addr, &netmaskAddr)) { - LOG_ERR("Unable to set netmask for AP interface: %s", WIFI_AP_NETMASK); - } - - addr.s4_addr[3] += 10; - if (net_dhcpv4_server_start(ap_iface, &addr) != 0) { - LOG_ERR("DHCP server is not started for desired IP"); - return; - } - - LOG_INF("DHCPv4 server started...\n"); -} - -static int enable_ap_mode(void) -{ - if (!ap_iface) { - LOG_INF("AP: is not initialized"); - return -EIO; - } - - LOG_INF("Turning on AP Mode"); - ap_config.ssid = (const uint8_t *)WIFI_AP_SSID; - ap_config.ssid_length = strlen(WIFI_AP_SSID); - ap_config.psk = (const uint8_t *)WIFI_AP_PSK; - ap_config.psk_length = strlen(WIFI_AP_PSK); - ap_config.channel = WIFI_CHANNEL_ANY; - ap_config.band = WIFI_FREQ_BAND_2_4_GHZ; - - if (strlen(WIFI_AP_PSK) == 0) { - ap_config.security = WIFI_SECURITY_TYPE_NONE; - } else { - - ap_config.security = WIFI_SECURITY_TYPE_PSK; - } - - enable_dhcpv4_server(); - - int ret = net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, &ap_config, - sizeof(struct wifi_connect_req_params)); - if (ret) { - LOG_ERR("NET_REQUEST_WIFI_AP_ENABLE failed, err: %d", ret); - } - - return ret; -} - -static int connect_to_wifi(void) -{ - if (!sta_iface) { - LOG_INF("STA: interface no initialized"); - return -EIO; - } - - sta_config.ssid = (const uint8_t *)WIFI_SSID; - sta_config.ssid_length = strlen(WIFI_SSID); - sta_config.psk = (const uint8_t *)WIFI_PSK; - sta_config.psk_length = strlen(WIFI_PSK); - sta_config.security = WIFI_SECURITY_TYPE_PSK; - sta_config.channel = WIFI_CHANNEL_ANY; - sta_config.band = WIFI_FREQ_BAND_2_4_GHZ; - - LOG_INF("Connecting to SSID: %s\n", sta_config.ssid); - - int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, &sta_config, - sizeof(struct wifi_connect_req_params)); - if (ret) { - LOG_ERR("Unable to Connect to (%s)", WIFI_SSID); - } - - return ret; -} - -int main(void) -{ - k_sleep(K_SECONDS(5)); - - net_mgmt_init_event_callback(&cb, wifi_event_handler, NET_EVENT_WIFI_MASK); - net_mgmt_add_event_callback(&cb); - - /* Get AP interface in AP-STA mode. */ - ap_iface = net_if_get_wifi_sap(); - - /* Get STA interface in AP-STA mode. */ - sta_iface = net_if_get_wifi_sta(); - - // enable_ap_mode(); - connect_to_wifi(); - - return 0; -} \ No newline at end of file diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c new file mode 100644 index 0000000..25a9954 --- /dev/null +++ b/embed-proplet/src/mqtt_client.c @@ -0,0 +1,195 @@ +// #include "mqtt_client.h" +// #include +// #include +// #include + +// LOG_MODULE_REGISTER(mqtt_client); + +// #define RX_BUFFER_SIZE 256 +// #define TX_BUFFER_SIZE 256 + +// #define MQTT_BROKER_HOSTNAME "192.168.1.100" /* Replace with your broker's IP */ +// #define MQTT_BROKER_PORT 1883 + +// #define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" +// #define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" +// #define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" + +// #define CLIENT_ID "proplet-esp32s3" + +// /* Buffers for MQTT client */ +// static uint8_t rx_buffer[RX_BUFFER_SIZE]; +// static uint8_t tx_buffer[TX_BUFFER_SIZE]; + +// /* MQTT client context */ +// static struct mqtt_client client_ctx; +// static struct sockaddr_storage broker_addr; + +// /* Flags to indicate connection status */ +// static bool mqtt_connected = false; + +// static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) +// { +// switch (evt->type) { +// case MQTT_EVT_CONNACK: +// if (evt->result == 0) { +// mqtt_connected = true; +// LOG_INF("Connected to MQTT broker"); +// } else { +// LOG_ERR("Connection failed, result: %d", evt->result); +// } +// break; + +// case MQTT_EVT_DISCONNECT: +// mqtt_connected = false; +// LOG_INF("Disconnected from MQTT broker"); +// break; + +// case MQTT_EVT_PUBLISH: { +// const struct mqtt_publish_param *publish = &evt->param.publish; +// LOG_INF("Message received on topic: %s", publish->message.topic.topic.utf8); + +// /* Handle messages */ +// if (strstr(publish->message.topic.topic.utf8, "start")) { +// LOG_INF("Start command received"); +// /* Handle start command */ +// } else if (strstr(publish->message.topic.topic.utf8, "stop")) { +// LOG_INF("Stop command received"); +// /* Handle stop command */ +// } +// break; +// } + +// case MQTT_EVT_SUBACK: +// LOG_INF("Subscribed to topic(s)"); +// break; + +// case MQTT_EVT_PUBACK: +// LOG_INF("Message published successfully"); +// break; + +// default: +// break; +// } +// } + +// int mqtt_client_init_and_connect(void) +// { +// int ret; + +// /* Resolve broker address */ +// struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; +// broker->sin_family = AF_INET; +// broker->sin_port = htons(MQTT_BROKER_PORT); +// ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); +// if (ret != 0) { +// LOG_ERR("Failed to resolve broker address, ret=%d", ret); +// return ret; +// } + +// /* Initialize MQTT client */ +// mqtt_client_init(&client_ctx); +// client_ctx.broker = &broker_addr; +// client_ctx.evt_cb = mqtt_event_handler; +// client_ctx.client_id.utf8 = CLIENT_ID; +// client_ctx.client_id.size = strlen(CLIENT_ID); +// client_ctx.protocol_version = MQTT_VERSION_3_1_1; +// client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; + +// /* Assign buffers */ +// client_ctx.rx_buf = rx_buffer; +// client_ctx.rx_buf_size = sizeof(rx_buffer); +// client_ctx.tx_buf = tx_buffer; +// client_ctx.tx_buf_size = sizeof(tx_buffer); + +// /* Connect to broker */ +// ret = mqtt_connect(&client_ctx); +// if (ret != 0) { +// LOG_ERR("MQTT connect failed, ret=%d", ret); +// return ret; +// } + +// LOG_INF("MQTT client initialized and connected"); +// return 0; +// } + +// int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id) +// { +// char topic[128]; +// snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); + +// char payload[128]; +// snprintf(payload, sizeof(payload), +// "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); + +// struct mqtt_publish_param param = { +// .message = { +// .topic = { +// .topic = topic, +// .topic_len = strlen(topic), +// }, +// .payload = { +// .data = payload, +// .len = strlen(payload), +// }, +// }, +// .message_id = 0, +// .dup_flag = 0, +// .retain_flag = 0, +// .qos = MQTT_QOS_1_AT_LEAST_ONCE, +// }; + +// int ret = mqtt_publish(&client_ctx, ¶m); +// if (ret != 0) { +// LOG_ERR("Failed to publish discovery announcement, ret=%d", ret); +// } + +// return ret; +// } + +// int mqtt_client_subscribe(const char *channel_id) +// { +// char start_topic[128]; +// snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); + +// char stop_topic[128]; +// snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + +// struct mqtt_topic topics[] = { +// { +// .topic = { +// .topic = start_topic, +// .topic_len = strlen(start_topic), +// }, +// .qos = MQTT_QOS_1_AT_LEAST_ONCE, +// }, +// { +// .topic = { +// .topic = stop_topic, +// .topic_len = strlen(stop_topic), +// }, +// .qos = MQTT_QOS_1_AT_LEAST_ONCE, +// }, +// }; + +// struct mqtt_subscription_list sub_list = { +// .list = topics, +// .list_count = ARRAY_SIZE(topics), +// .message_id = 1, +// }; + +// int ret = mqtt_subscribe(&client_ctx, &sub_list); +// if (ret != 0) { +// LOG_ERR("Failed to subscribe to topics, ret=%d", ret); +// } + +// return ret; +// } + +// void mqtt_client_process(void) +// { +// if (mqtt_connected) { +// mqtt_input(&client_ctx); +// mqtt_live(&client_ctx); +// } +// } diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h new file mode 100644 index 0000000..cff0bcb --- /dev/null +++ b/embed-proplet/src/mqtt_client.h @@ -0,0 +1,18 @@ +// #ifndef MQTT_CLIENT_H +// #define MQTT_CLIENT_H + +// #include + +// /* Initialize and connect the MQTT client */ +// int mqtt_client_init_and_connect(void); + +// /* Process MQTT events */ +// void mqtt_client_process(void); + +// /* Publish discovery announcement */ +// int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); + +// /* Subscribe to required topics */ +// int mqtt_client_subscribe(const char *channel_id); + +// #endif /* MQTT_CLIENT_H */ diff --git a/embed-proplet/src/wifi_manager.c b/embed-proplet/src/wifi_manager.c new file mode 100644 index 0000000..8b6fc71 --- /dev/null +++ b/embed-proplet/src/wifi_manager.c @@ -0,0 +1,127 @@ +#include "wifi_manager.h" +#include +#include + +LOG_MODULE_REGISTER(wifi_manager); + +static struct net_if *ap_iface; +static struct net_if *sta_iface; +static struct net_mgmt_event_callback cb; + +#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" +#define MAC2STR(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + +static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) +{ + switch (mgmt_event) { + case NET_EVENT_WIFI_CONNECT_RESULT: + LOG_INF("Connected to Wi-Fi"); + break; + case NET_EVENT_WIFI_DISCONNECT_RESULT: + LOG_INF("Disconnected from Wi-Fi"); + break; + case NET_EVENT_WIFI_AP_ENABLE_RESULT: + LOG_INF("AP Mode enabled"); + break; + case NET_EVENT_WIFI_AP_DISABLE_RESULT: + LOG_INF("AP Mode disabled"); + break; + case NET_EVENT_WIFI_AP_STA_CONNECTED: { + struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; + LOG_INF("Station " MACSTR " connected", MAC2STR(sta_info->mac)); + break; + } + case NET_EVENT_WIFI_AP_STA_DISCONNECTED: { + struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info; + LOG_INF("Station " MACSTR " disconnected", MAC2STR(sta_info->mac)); + break; + } + default: + break; + } +} + +static void enable_dhcpv4_server(const char *ip_address, const char *netmask) +{ + struct in_addr addr, netmask_addr; + + if (net_addr_pton(AF_INET, ip_address, &addr) != 0) { + LOG_ERR("Invalid IP address: %s", ip_address); + return; + } + + if (net_addr_pton(AF_INET, netmask, &netmask_addr) != 0) { + LOG_ERR("Invalid netmask: %s", netmask); + return; + } + + net_if_ipv4_set_gw(ap_iface, &addr); + net_if_ipv4_addr_add(ap_iface, &addr, NET_ADDR_MANUAL, 0); + net_if_ipv4_set_netmask(ap_iface, &netmask_addr); + + addr.s4_addr[3] += 10; /* Adjust DHCP pool starting address */ + if (net_dhcpv4_server_start(ap_iface, &addr) != 0) { + LOG_ERR("Failed to start DHCPv4 server"); + } + + LOG_INF("DHCPv4 server started"); +} + +void wifi_manager_init(void) +{ + net_mgmt_init_event_callback(&cb, wifi_event_handler, + NET_EVENT_WIFI_CONNECT_RESULT | + NET_EVENT_WIFI_DISCONNECT_RESULT | + NET_EVENT_WIFI_AP_ENABLE_RESULT | + NET_EVENT_WIFI_AP_DISABLE_RESULT | + NET_EVENT_WIFI_AP_STA_CONNECTED | + NET_EVENT_WIFI_AP_STA_DISCONNECTED); + net_mgmt_add_event_callback(&cb); + + ap_iface = net_if_get_wifi_sap(); + sta_iface = net_if_get_wifi_sta(); +} + +int wifi_manager_connect(const char *ssid, const char *psk) +{ + if (!sta_iface) { + LOG_ERR("STA interface not initialized"); + return -EIO; + } + + struct wifi_connect_req_params params = { + .ssid = ssid, + .ssid_length = strlen(ssid), + .psk = psk, + .psk_length = strlen(psk), + .security = WIFI_SECURITY_TYPE_PSK, + .channel = WIFI_CHANNEL_ANY, + .band = WIFI_FREQ_BAND_2_4_GHZ, + }; + + LOG_INF("Connecting to SSID: %s", ssid); + return net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, ¶ms, sizeof(params)); +} + +int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask) +{ + if (!ap_iface) { + LOG_ERR("AP interface not initialized"); + return -EIO; + } + + struct wifi_connect_req_params params = { + .ssid = ssid, + .ssid_length = strlen(ssid), + .psk = psk, + .psk_length = strlen(psk), + .security = (strlen(psk) > 0) ? WIFI_SECURITY_TYPE_PSK : WIFI_SECURITY_TYPE_NONE, + .channel = WIFI_CHANNEL_ANY, + .band = WIFI_FREQ_BAND_2_4_GHZ, + }; + + enable_dhcpv4_server(ip_address, netmask); + + LOG_INF("Enabling AP mode with SSID: %s", ssid); + return net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, ¶ms, sizeof(params)); +} diff --git a/embed-proplet/src/wifi_manager.h b/embed-proplet/src/wifi_manager.h new file mode 100644 index 0000000..0e8fdb3 --- /dev/null +++ b/embed-proplet/src/wifi_manager.h @@ -0,0 +1,12 @@ +#ifndef WIFI_MANAGER_H +#define WIFI_MANAGER_H + +#include + +void wifi_manager_init(void); + +int wifi_manager_connect(const char *ssid, const char *psk); + +int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask); + +#endif From 802445d923d67cc9d267e7c9671294c976e03b1d Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 15:32:43 +0300 Subject: [PATCH 20/50] Fix undefined error in mqtt implemetation --- embed-proplet/prj.conf | 10 +- embed-proplet/src/main.c | 62 ++--- embed-proplet/src/mqtt_client.c | 392 ++++++++++++++++---------------- embed-proplet/src/mqtt_client.h | 24 +- 4 files changed, 245 insertions(+), 243 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index b6b087f..b51ec4e 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -54,12 +54,12 @@ CONFIG_NET_STATISTICS=y CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # Thread Analyzer -CONFIG_THREAD_NAME=y -CONFIG_THREAD_ANALYZER=y -CONFIG_THREAD_ANALYZER_AUTO=y +CONFIG_THREAD_NAME=n +CONFIG_THREAD_ANALYZER=n +CONFIG_THREAD_ANALYZER_AUTO=n CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 -CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y -CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y +CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=n +CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=n # Miscellaneous CONFIG_MAIN_STACK_SIZE=8192 diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index c7ff290..fc424cd 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -1,14 +1,14 @@ #include #include #include "wifi_manager.h" -// #include "mqtt_client.h" +#include "mqtt_client.h" LOG_MODULE_REGISTER(main); -#define WIFI_SSID "YourSSID" -#define WIFI_PSK "YourPassword" -// #define PROPLET_ID "proplet-esp32s3" -// #define CHANNEL_ID "default_channel" +#define WIFI_SSID "Octavifi" +#define WIFI_PSK "Unic0rn_2030" +#define PROPLET_ID "proplet-esp32s3" +#define CHANNEL_ID "default_channel" void main(void) { @@ -24,30 +24,30 @@ void main(void) return; } - // /* Initialize and connect MQTT client */ - // ret = mqtt_client_init_and_connect(); - // if (ret != 0) { - // LOG_ERR("Failed to initialize MQTT client, exiting"); - // return; - // } - - // /* Announce discovery */ - // ret = mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID); - // if (ret != 0) { - // LOG_ERR("Failed to publish discovery announcement, exiting"); - // return; - // } - - // /* Subscribe to topics */ - // ret = mqtt_client_subscribe(CHANNEL_ID); - // if (ret != 0) { - // LOG_ERR("Failed to subscribe to topics, exiting"); - // return; - // } - - // /* Main loop for processing MQTT events */ - // while (1) { - // mqtt_client_process(); /* Process MQTT events */ - // k_sleep(K_SECONDS(5)); /* Sleep for a while */ - // } + /* Initialize and connect MQTT client */ + ret = mqtt_client_init_and_connect(); + if (ret != 0) { + LOG_ERR("Failed to initialize MQTT client, exiting"); + return; + } + + /* Announce discovery */ + ret = mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID); + if (ret != 0) { + LOG_ERR("Failed to publish discovery announcement, exiting"); + return; + } + + /* Subscribe to topics */ + ret = mqtt_client_subscribe(CHANNEL_ID); + if (ret != 0) { + LOG_ERR("Failed to subscribe to topics, exiting"); + return; + } + + /* Main loop for processing MQTT events */ + while (1) { + mqtt_client_process(); /* Process MQTT events */ + k_sleep(K_SECONDS(5)); /* Sleep for a while */ + } } diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 25a9954..e098263 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -1,195 +1,197 @@ -// #include "mqtt_client.h" -// #include -// #include -// #include - -// LOG_MODULE_REGISTER(mqtt_client); - -// #define RX_BUFFER_SIZE 256 -// #define TX_BUFFER_SIZE 256 - -// #define MQTT_BROKER_HOSTNAME "192.168.1.100" /* Replace with your broker's IP */ -// #define MQTT_BROKER_PORT 1883 - -// #define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" -// #define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" -// #define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" - -// #define CLIENT_ID "proplet-esp32s3" - -// /* Buffers for MQTT client */ -// static uint8_t rx_buffer[RX_BUFFER_SIZE]; -// static uint8_t tx_buffer[TX_BUFFER_SIZE]; - -// /* MQTT client context */ -// static struct mqtt_client client_ctx; -// static struct sockaddr_storage broker_addr; - -// /* Flags to indicate connection status */ -// static bool mqtt_connected = false; - -// static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) -// { -// switch (evt->type) { -// case MQTT_EVT_CONNACK: -// if (evt->result == 0) { -// mqtt_connected = true; -// LOG_INF("Connected to MQTT broker"); -// } else { -// LOG_ERR("Connection failed, result: %d", evt->result); -// } -// break; - -// case MQTT_EVT_DISCONNECT: -// mqtt_connected = false; -// LOG_INF("Disconnected from MQTT broker"); -// break; - -// case MQTT_EVT_PUBLISH: { -// const struct mqtt_publish_param *publish = &evt->param.publish; -// LOG_INF("Message received on topic: %s", publish->message.topic.topic.utf8); - -// /* Handle messages */ -// if (strstr(publish->message.topic.topic.utf8, "start")) { -// LOG_INF("Start command received"); -// /* Handle start command */ -// } else if (strstr(publish->message.topic.topic.utf8, "stop")) { -// LOG_INF("Stop command received"); -// /* Handle stop command */ -// } -// break; -// } - -// case MQTT_EVT_SUBACK: -// LOG_INF("Subscribed to topic(s)"); -// break; - -// case MQTT_EVT_PUBACK: -// LOG_INF("Message published successfully"); -// break; - -// default: -// break; -// } -// } - -// int mqtt_client_init_and_connect(void) -// { -// int ret; - -// /* Resolve broker address */ -// struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; -// broker->sin_family = AF_INET; -// broker->sin_port = htons(MQTT_BROKER_PORT); -// ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); -// if (ret != 0) { -// LOG_ERR("Failed to resolve broker address, ret=%d", ret); -// return ret; -// } - -// /* Initialize MQTT client */ -// mqtt_client_init(&client_ctx); -// client_ctx.broker = &broker_addr; -// client_ctx.evt_cb = mqtt_event_handler; -// client_ctx.client_id.utf8 = CLIENT_ID; -// client_ctx.client_id.size = strlen(CLIENT_ID); -// client_ctx.protocol_version = MQTT_VERSION_3_1_1; -// client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; - -// /* Assign buffers */ -// client_ctx.rx_buf = rx_buffer; -// client_ctx.rx_buf_size = sizeof(rx_buffer); -// client_ctx.tx_buf = tx_buffer; -// client_ctx.tx_buf_size = sizeof(tx_buffer); - -// /* Connect to broker */ -// ret = mqtt_connect(&client_ctx); -// if (ret != 0) { -// LOG_ERR("MQTT connect failed, ret=%d", ret); -// return ret; -// } - -// LOG_INF("MQTT client initialized and connected"); -// return 0; -// } - -// int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id) -// { -// char topic[128]; -// snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); - -// char payload[128]; -// snprintf(payload, sizeof(payload), -// "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); - -// struct mqtt_publish_param param = { -// .message = { -// .topic = { -// .topic = topic, -// .topic_len = strlen(topic), -// }, -// .payload = { -// .data = payload, -// .len = strlen(payload), -// }, -// }, -// .message_id = 0, -// .dup_flag = 0, -// .retain_flag = 0, -// .qos = MQTT_QOS_1_AT_LEAST_ONCE, -// }; - -// int ret = mqtt_publish(&client_ctx, ¶m); -// if (ret != 0) { -// LOG_ERR("Failed to publish discovery announcement, ret=%d", ret); -// } - -// return ret; -// } - -// int mqtt_client_subscribe(const char *channel_id) -// { -// char start_topic[128]; -// snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); - -// char stop_topic[128]; -// snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); - -// struct mqtt_topic topics[] = { -// { -// .topic = { -// .topic = start_topic, -// .topic_len = strlen(start_topic), -// }, -// .qos = MQTT_QOS_1_AT_LEAST_ONCE, -// }, -// { -// .topic = { -// .topic = stop_topic, -// .topic_len = strlen(stop_topic), -// }, -// .qos = MQTT_QOS_1_AT_LEAST_ONCE, -// }, -// }; - -// struct mqtt_subscription_list sub_list = { -// .list = topics, -// .list_count = ARRAY_SIZE(topics), -// .message_id = 1, -// }; - -// int ret = mqtt_subscribe(&client_ctx, &sub_list); -// if (ret != 0) { -// LOG_ERR("Failed to subscribe to topics, ret=%d", ret); -// } - -// return ret; -// } - -// void mqtt_client_process(void) -// { -// if (mqtt_connected) { -// mqtt_input(&client_ctx); -// mqtt_live(&client_ctx); -// } -// } +#include "mqtt_client.h" +#include +#include +#include + +LOG_MODULE_REGISTER(mqtt_client); + +#define RX_BUFFER_SIZE 256 +#define TX_BUFFER_SIZE 256 + +#define MQTT_BROKER_HOSTNAME "192.168.1.100" /* Replace with your broker's IP */ +#define MQTT_BROKER_PORT 1883 + +#define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" +#define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" +#define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" + +#define CLIENT_ID "proplet-esp32s3" + +/* Buffers for MQTT client */ +static uint8_t rx_buffer[RX_BUFFER_SIZE]; +static uint8_t tx_buffer[TX_BUFFER_SIZE]; + +/* MQTT client context */ +static struct mqtt_client client_ctx; +static struct sockaddr_storage broker_addr; + +/* Flags to indicate connection status */ +static bool mqtt_connected = false; + +static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) +{ + switch (evt->type) { + case MQTT_EVT_CONNACK: + if (evt->result == 0) { + mqtt_connected = true; + LOG_INF("Connected to MQTT broker"); + } else { + LOG_ERR("Connection failed, result: %d", evt->result); + } + break; + + case MQTT_EVT_DISCONNECT: + mqtt_connected = false; + LOG_INF("Disconnected from MQTT broker"); + break; + + case MQTT_EVT_PUBLISH: { + const struct mqtt_publish_param *publish = &evt->param.publish; + LOG_INF("Message received on topic: %s", publish->message.topic.topic.utf8); + + /* Handle messages */ + if (strstr(publish->message.topic.topic.utf8, "start")) { + LOG_INF("Start command received"); + /* Handle start command */ + } else if (strstr(publish->message.topic.topic.utf8, "stop")) { + LOG_INF("Stop command received"); + /* Handle stop command */ + } + break; + } + + case MQTT_EVT_SUBACK: + LOG_INF("Subscribed to topic(s)"); + break; + + case MQTT_EVT_PUBACK: + LOG_INF("Message published successfully"); + break; + + default: + break; + } +} + +int mqtt_client_init_and_connect(void) +{ + int ret; + + /* Resolve broker address */ + struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; + broker->sin_family = AF_INET; + broker->sin_port = htons(MQTT_BROKER_PORT); + ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); + if (ret != 0) { + LOG_ERR("Failed to resolve broker address, ret=%d", ret); + return ret; + } + + /* Initialize MQTT client */ + mqtt_client_init(&client_ctx); + client_ctx.broker = &broker_addr; + client_ctx.evt_cb = mqtt_event_handler; + client_ctx.client_id.utf8 = CLIENT_ID; + client_ctx.client_id.size = strlen(CLIENT_ID); + client_ctx.protocol_version = MQTT_VERSION_3_1_1; + client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; + + /* Assign buffers */ + client_ctx.rx_buf = rx_buffer; + client_ctx.rx_buf_size = sizeof(rx_buffer); + client_ctx.tx_buf = tx_buffer; + client_ctx.tx_buf_size = sizeof(tx_buffer); + + /* Connect to broker */ + ret = mqtt_connect(&client_ctx); + if (ret != 0) { + LOG_ERR("MQTT connect failed, ret=%d", ret); + return ret; + } + + LOG_INF("MQTT client initialized and connected"); + return 0; +} + +int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id) +{ + char topic[128]; + snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); + + char payload[128]; + snprintf(payload, sizeof(payload), + "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); + + struct mqtt_publish_param param = { + .message = { + .topic = { + .topic = { + .utf8 = (uint8_t *)topic, // Correctly assign UTF-8 pointer + .size = strlen(topic), // Length of the topic string + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS is part of mqtt_topic + }, + .payload = { + .data = (uint8_t *)payload, // Correctly assign payload data + .len = strlen(payload), // Length of the payload string + }, + }, + .message_id = 0, + .dup_flag = 0, + .retain_flag = 0, + }; + + int ret = mqtt_publish(&client_ctx, ¶m); + if (ret != 0) { + LOG_ERR("Failed to publish discovery announcement, ret=%d", ret); + } + + return ret; +} + +int mqtt_client_subscribe(const char *channel_id) +{ + char start_topic[128]; + snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); + + char stop_topic[128]; + snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + + struct mqtt_topic topics[] = { + { + .topic = { + .utf8 = (uint8_t *)start_topic, // Correctly assign UTF-8 pointer + .size = strlen(start_topic), // Length of the topic string + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS level + }, + { + .topic = { + .utf8 = (uint8_t *)stop_topic, // Correctly assign UTF-8 pointer + .size = strlen(stop_topic), // Length of the topic string + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS level + }, + }; + + struct mqtt_subscription_list sub_list = { + .list = topics, + .list_count = ARRAY_SIZE(topics), + .message_id = 1, + }; + + int ret = mqtt_subscribe(&client_ctx, &sub_list); + if (ret != 0) { + LOG_ERR("Failed to subscribe to topics, ret=%d", ret); + } + + return ret; +} + +void mqtt_client_process(void) +{ + if (mqtt_connected) { + mqtt_input(&client_ctx); + mqtt_live(&client_ctx); + } +} diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index cff0bcb..f92ca95 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -1,18 +1,18 @@ -// #ifndef MQTT_CLIENT_H -// #define MQTT_CLIENT_H +#ifndef MQTT_CLIENT_H +#define MQTT_CLIENT_H -// #include +#include -// /* Initialize and connect the MQTT client */ -// int mqtt_client_init_and_connect(void); +/* Initialize and connect the MQTT client */ +int mqtt_client_init_and_connect(void); -// /* Process MQTT events */ -// void mqtt_client_process(void); +/* Process MQTT events */ +void mqtt_client_process(void); -// /* Publish discovery announcement */ -// int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); +/* Publish discovery announcement */ +int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); -// /* Subscribe to required topics */ -// int mqtt_client_subscribe(const char *channel_id); +/* Subscribe to required topics */ +int mqtt_client_subscribe(const char *channel_id); -// #endif /* MQTT_CLIENT_H */ +#endif /* MQTT_CLIENT_H */ From 397ccc3838c6e2808627e587b515c0fad3d14a83 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 16:29:06 +0300 Subject: [PATCH 21/50] move mqtt initialization after network connection --- .../boards/esp32s3_devkitc_procpu.conf | 9 ++-- embed-proplet/prj.conf | 19 +++++--- embed-proplet/src/main.c | 44 ++++++++++--------- embed-proplet/src/wifi_manager.c | 17 +++++++ embed-proplet/src/wifi_manager.h | 3 +- 5 files changed, 57 insertions(+), 35 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf index 8ec74a8..13ad317 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.conf +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -1,10 +1,9 @@ -# Wi-Fi Configuration -CONFIG_WIFI=y +# ESP32 Wi-Fi Configuration CONFIG_WIFI_ESP32=y +CONFIG_ESP32_WIFI_STA_RECONNECT=y CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y CONFIG_ESP32_WIFI_AP_STA_MODE=n -CONFIG_WIFI_NM=y -CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 -CONFIG_WIFI_LOG_LEVEL_DBG=y CONFIG_ESP32_WIFI_DEBUG_PRINT=y +CONFIG_ESP32_WIFI_STA_RECONNECT=y + diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index b51ec4e..f395245 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -1,7 +1,9 @@ # General Networking +CONFIG_WIFI=y +CONFIG_WIFI_NM=y +CONFIG_NET_MGMT=y CONFIG_NETWORKING=y CONFIG_NET_CONFIG_AUTO_INIT=n -CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_MGMT_EVENT_INFO=y CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=10 @@ -22,6 +24,7 @@ CONFIG_NET_IF_MAX_IPV4_COUNT=2 # CONFIG_NET_IF_MAX_IPV6_COUNT=2 CONFIG_NET_L2_ETHERNET=y CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 # Buffer and Packet Management CONFIG_NET_BUF_RX_COUNT=32 @@ -41,6 +44,7 @@ CONFIG_MQTT_KEEPALIVE=10 CONFIG_LOG=y CONFIG_NET_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_WIFI_LOG_LEVEL_DBG=y CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=8192 @@ -54,12 +58,13 @@ CONFIG_NET_STATISTICS=y CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # Thread Analyzer -CONFIG_THREAD_NAME=n -CONFIG_THREAD_ANALYZER=n -CONFIG_THREAD_ANALYZER_AUTO=n -CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 -CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=n -CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=n +# Uncomment the following lines when debugging thread-related issues, such as stack overflows, high CPU usage by specific threads, or deadlocks. +# CONFIG_THREAD_NAME=y +# CONFIG_THREAD_ANALYZER=y +# CONFIG_THREAD_ANALYZER_AUTO=y +# CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 +# CONFIG_THREAD_ANALYZER_LOG_LEVEL_DBG=y +# CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y # Miscellaneous CONFIG_MAIN_STACK_SIZE=8192 diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index fc424cd..3d7fa0e 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -12,42 +12,44 @@ LOG_MODULE_REGISTER(main); void main(void) { - LOG_INF("Proplet starting..."); + LOG_INF("Starting Proplet..."); - /* Initialize Wi-Fi */ + /* Initialize Wi-Fi and connect */ wifi_manager_init(); - - /* Connect to Wi-Fi */ - int ret = wifi_manager_connect(WIFI_SSID, WIFI_PSK); - if (ret != 0) { - LOG_ERR("Failed to connect to Wi-Fi, ret=%d", ret); + if (wifi_manager_connect(WIFI_SSID, WIFI_PSK) != 0) { + LOG_ERR("Wi-Fi connection failed"); return; } - /* Initialize and connect MQTT client */ - ret = mqtt_client_init_and_connect(); - if (ret != 0) { - LOG_ERR("Failed to initialize MQTT client, exiting"); + /* Wait for Wi-Fi connection */ + while (!wifi_manager_is_connected()) { + k_sleep(K_MSEC(500)); + } + LOG_INF("Wi-Fi connected"); + + /* Initialize MQTT client */ + if (mqtt_client_init_and_connect() != 0) { + LOG_ERR("MQTT client initialization failed"); return; } - /* Announce discovery */ - ret = mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID); - if (ret != 0) { - LOG_ERR("Failed to publish discovery announcement, exiting"); + /* Publish discovery announcement */ + if (mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID) != 0) { + LOG_ERR("Discovery announcement failed"); return; } /* Subscribe to topics */ - ret = mqtt_client_subscribe(CHANNEL_ID); - if (ret != 0) { - LOG_ERR("Failed to subscribe to topics, exiting"); + if (mqtt_client_subscribe(CHANNEL_ID) != 0) { + LOG_ERR("Topic subscription failed"); return; } - /* Main loop for processing MQTT events */ + LOG_INF("Proplet ready"); + + /* Main loop for MQTT processing */ while (1) { - mqtt_client_process(); /* Process MQTT events */ - k_sleep(K_SECONDS(5)); /* Sleep for a while */ + mqtt_client_process(); + k_sleep(K_SECONDS(5)); } } diff --git a/embed-proplet/src/wifi_manager.c b/embed-proplet/src/wifi_manager.c index 8b6fc71..f1709ff 100644 --- a/embed-proplet/src/wifi_manager.c +++ b/embed-proplet/src/wifi_manager.c @@ -125,3 +125,20 @@ int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_add LOG_INF("Enabling AP mode with SSID: %s", ssid); return net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, ¶ms, sizeof(params)); } + +bool wifi_manager_is_connected(void) +{ + struct wifi_iface_status status; + + if (!sta_iface) { + LOG_ERR("STA interface not initialized"); + return false; + } + + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, sta_iface, &status, sizeof(status)) != 0) { + LOG_ERR("Failed to get Wi-Fi interface status"); + return false; + } + + return status.state == WIFI_STATE_COMPLETED; +} diff --git a/embed-proplet/src/wifi_manager.h b/embed-proplet/src/wifi_manager.h index 0e8fdb3..abf16b4 100644 --- a/embed-proplet/src/wifi_manager.h +++ b/embed-proplet/src/wifi_manager.h @@ -4,9 +4,8 @@ #include void wifi_manager_init(void); - int wifi_manager_connect(const char *ssid, const char *psk); - int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask); +bool wifi_manager_is_connected(void); #endif From fdbfcb8fcf97e9d660386934d9a0140a453bf656 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 16:42:26 +0300 Subject: [PATCH 22/50] synchronize the Wi-Fi connection status between the wifi_manager.c and main.c --- embed-proplet/src/main.c | 11 ++++++----- embed-proplet/src/wifi_manager.c | 23 ++++++++--------------- embed-proplet/src/wifi_manager.h | 3 ++- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 3d7fa0e..4f831f0 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -14,7 +14,7 @@ void main(void) { LOG_INF("Starting Proplet..."); - /* Initialize Wi-Fi and connect */ + /* Initialize Wi-Fi */ wifi_manager_init(); if (wifi_manager_connect(WIFI_SSID, WIFI_PSK) != 0) { LOG_ERR("Wi-Fi connection failed"); @@ -22,10 +22,11 @@ void main(void) } /* Wait for Wi-Fi connection */ - while (!wifi_manager_is_connected()) { - k_sleep(K_MSEC(500)); + if (!wifi_manager_wait_for_connection(K_SECONDS(10))) { + LOG_ERR("Wi-Fi connection timed out"); + return; } - LOG_INF("Wi-Fi connected"); + LOG_INF("Wi-Fi connected, proceeding with MQTT initialization"); /* Initialize MQTT client */ if (mqtt_client_init_and_connect() != 0) { @@ -49,7 +50,7 @@ void main(void) /* Main loop for MQTT processing */ while (1) { - mqtt_client_process(); + mqtt_client_process(); k_sleep(K_SECONDS(5)); } } diff --git a/embed-proplet/src/wifi_manager.c b/embed-proplet/src/wifi_manager.c index f1709ff..9739328 100644 --- a/embed-proplet/src/wifi_manager.c +++ b/embed-proplet/src/wifi_manager.c @@ -1,12 +1,14 @@ #include "wifi_manager.h" #include #include +#include LOG_MODULE_REGISTER(wifi_manager); static struct net_if *ap_iface; static struct net_if *sta_iface; static struct net_mgmt_event_callback cb; +static struct k_sem wifi_connected_sem; // Semaphore for Wi-Fi connection #define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" #define MAC2STR(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] @@ -16,6 +18,7 @@ static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt switch (mgmt_event) { case NET_EVENT_WIFI_CONNECT_RESULT: LOG_INF("Connected to Wi-Fi"); + k_sem_give(&wifi_connected_sem); // Signal Wi-Fi connection break; case NET_EVENT_WIFI_DISCONNECT_RESULT: LOG_INF("Disconnected from Wi-Fi"); @@ -69,6 +72,8 @@ static void enable_dhcpv4_server(const char *ip_address, const char *netmask) void wifi_manager_init(void) { + k_sem_init(&wifi_connected_sem, 0, 1); // Initialize semaphore with 0 count + net_mgmt_init_event_callback(&cb, wifi_event_handler, NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | @@ -126,19 +131,7 @@ int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_add return net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, ¶ms, sizeof(params)); } -bool wifi_manager_is_connected(void) +bool wifi_manager_wait_for_connection(k_timeout_t timeout) { - struct wifi_iface_status status; - - if (!sta_iface) { - LOG_ERR("STA interface not initialized"); - return false; - } - - if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, sta_iface, &status, sizeof(status)) != 0) { - LOG_ERR("Failed to get Wi-Fi interface status"); - return false; - } - - return status.state == WIFI_STATE_COMPLETED; -} + return k_sem_take(&wifi_connected_sem, timeout) == 0; +} \ No newline at end of file diff --git a/embed-proplet/src/wifi_manager.h b/embed-proplet/src/wifi_manager.h index abf16b4..b229a4b 100644 --- a/embed-proplet/src/wifi_manager.h +++ b/embed-proplet/src/wifi_manager.h @@ -2,10 +2,11 @@ #define WIFI_MANAGER_H #include +#include void wifi_manager_init(void); int wifi_manager_connect(const char *ssid, const char *psk); int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask); -bool wifi_manager_is_connected(void); +bool wifi_manager_wait_for_connection(k_timeout_t timeout); // Wait for Wi-Fi connection #endif From 5539203bd6777de735d7b95eafa6c8005b816cdd Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 17:12:00 +0300 Subject: [PATCH 23/50] Add mqtt config --- embed-proplet/boards/esp32s3_devkitc_procpu.conf | 1 - embed-proplet/prj.conf | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.conf b/embed-proplet/boards/esp32s3_devkitc_procpu.conf index 13ad317..dcd947b 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.conf +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.conf @@ -4,6 +4,5 @@ CONFIG_ESP32_WIFI_STA_RECONNECT=y CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y CONFIG_ESP32_WIFI_AP_STA_MODE=n CONFIG_ESP32_WIFI_DEBUG_PRINT=y -CONFIG_ESP32_WIFI_STA_RECONNECT=y diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index f395245..1d123f7 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -38,13 +38,20 @@ CONFIG_NET_MAX_CONTEXTS=10 # MQTT Client CONFIG_MQTT_LIB=y +# Uncomment to enable TLS support for socket MQTT Library. +# CONFIG_MQTT_LIB_TLS=y CONFIG_MQTT_KEEPALIVE=10 +CONFIG_MQTT_CLEAN_SESSION=n +CONFIG_MQTT_LIB_WEBSOCKET=n +# Uncomment to enable ALPN protocol for socket MQTT Library. +# CONFIG_MQTT_LIB_TLS_USE_ALPN=y # Logging CONFIG_LOG=y CONFIG_NET_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_WIFI_LOG_LEVEL_DBG=y +CONFIG_MQTT_LOG_LEVEL_DBG=y CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=8192 @@ -73,4 +80,4 @@ CONFIG_NET_PKT_TX_COUNT=16 CONFIG_IDLE_STACK_SIZE=2048 CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 -CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 \ No newline at end of file +CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 From d372b43bdbd2562e8ebc8d1afec0ad613b7eab92 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 17:46:57 +0300 Subject: [PATCH 24/50] Fix mqtt client initialization error --- .../boards/esp32s3_devkitc_procpu.overlay | 2 +- embed-proplet/src/mqtt_client.c | 26 +++++++++---------- embed-proplet/src/wifi_manager.c | 8 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/embed-proplet/boards/esp32s3_devkitc_procpu.overlay b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay index cdbd0d4..d342be1 100644 --- a/embed-proplet/boards/esp32s3_devkitc_procpu.overlay +++ b/embed-proplet/boards/esp32s3_devkitc_procpu.overlay @@ -1,3 +1,3 @@ &wifi { status = "okay"; -}; \ No newline at end of file +}; diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index e098263..b5cccba 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -8,7 +8,7 @@ LOG_MODULE_REGISTER(mqtt_client); #define RX_BUFFER_SIZE 256 #define TX_BUFFER_SIZE 256 -#define MQTT_BROKER_HOSTNAME "192.168.1.100" /* Replace with your broker's IP */ +#define MQTT_BROKER_HOSTNAME "192.168.88.179" /* Replace with your broker's IP */ #define MQTT_BROKER_PORT 1883 #define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" @@ -25,7 +25,7 @@ static uint8_t tx_buffer[TX_BUFFER_SIZE]; static struct mqtt_client client_ctx; static struct sockaddr_storage broker_addr; -/* Flags to indicate connection status */ +/* Flag to indicate connection status */ static bool mqtt_connected = false; static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) @@ -126,14 +126,14 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i .message = { .topic = { .topic = { - .utf8 = (uint8_t *)topic, // Correctly assign UTF-8 pointer - .size = strlen(topic), // Length of the topic string + .utf8 = (uint8_t *)topic, + .size = strlen(topic), }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS is part of mqtt_topic + .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, .payload = { - .data = (uint8_t *)payload, // Correctly assign payload data - .len = strlen(payload), // Length of the payload string + .data = (uint8_t *)payload, + .len = strlen(payload), }, }, .message_id = 0, @@ -160,17 +160,17 @@ int mqtt_client_subscribe(const char *channel_id) struct mqtt_topic topics[] = { { .topic = { - .utf8 = (uint8_t *)start_topic, // Correctly assign UTF-8 pointer - .size = strlen(start_topic), // Length of the topic string + .utf8 = (uint8_t *)start_topic, + .size = strlen(start_topic), }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS level + .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, { .topic = { - .utf8 = (uint8_t *)stop_topic, // Correctly assign UTF-8 pointer - .size = strlen(stop_topic), // Length of the topic string + .utf8 = (uint8_t *)stop_topic, + .size = strlen(stop_topic), }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE, // QoS level + .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, }; diff --git a/embed-proplet/src/wifi_manager.c b/embed-proplet/src/wifi_manager.c index 9739328..4d1a359 100644 --- a/embed-proplet/src/wifi_manager.c +++ b/embed-proplet/src/wifi_manager.c @@ -8,7 +8,7 @@ LOG_MODULE_REGISTER(wifi_manager); static struct net_if *ap_iface; static struct net_if *sta_iface; static struct net_mgmt_event_callback cb; -static struct k_sem wifi_connected_sem; // Semaphore for Wi-Fi connection +static struct k_sem wifi_connected_sem; #define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" #define MAC2STR(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] @@ -18,7 +18,7 @@ static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt switch (mgmt_event) { case NET_EVENT_WIFI_CONNECT_RESULT: LOG_INF("Connected to Wi-Fi"); - k_sem_give(&wifi_connected_sem); // Signal Wi-Fi connection + k_sem_give(&wifi_connected_sem); break; case NET_EVENT_WIFI_DISCONNECT_RESULT: LOG_INF("Disconnected from Wi-Fi"); @@ -72,7 +72,7 @@ static void enable_dhcpv4_server(const char *ip_address, const char *netmask) void wifi_manager_init(void) { - k_sem_init(&wifi_connected_sem, 0, 1); // Initialize semaphore with 0 count + k_sem_init(&wifi_connected_sem, 0, 1); net_mgmt_init_event_callback(&cb, wifi_event_handler, NET_EVENT_WIFI_CONNECT_RESULT | @@ -134,4 +134,4 @@ int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_add bool wifi_manager_wait_for_connection(k_timeout_t timeout) { return k_sem_take(&wifi_connected_sem, timeout) == 0; -} \ No newline at end of file +} From 706dcc1913530bbe8cc5d23cd1dd64db3b1bba84 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 18:17:06 +0300 Subject: [PATCH 25/50] Rename test channel and propled ID --- embed-proplet/src/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 4f831f0..3d4fb72 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -7,8 +7,8 @@ LOG_MODULE_REGISTER(main); #define WIFI_SSID "Octavifi" #define WIFI_PSK "Unic0rn_2030" -#define PROPLET_ID "proplet-esp32s3" -#define CHANNEL_ID "default_channel" +#define PROPLET_ID "proplet1" +#define CHANNEL_ID "channel1" void main(void) { @@ -22,7 +22,7 @@ void main(void) } /* Wait for Wi-Fi connection */ - if (!wifi_manager_wait_for_connection(K_SECONDS(10))) { + if (!wifi_manager_wait_for_connection(K_SECONDS(60))) { LOG_ERR("Wi-Fi connection timed out"); return; } From a29e64e409fff113777e06ef61a09ab1be02aa7d Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Mon, 13 Jan 2025 21:42:21 +0300 Subject: [PATCH 26/50] Connect to local mqtt broker --- embed-proplet/src/main.c | 7 ++++ embed-proplet/src/mqtt_client.c | 73 ++++++++++++++++++++++++++------- embed-proplet/src/mqtt_client.h | 12 ++---- 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 3d4fb72..d5838db 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -34,6 +34,13 @@ void main(void) return; } + /* Wait for MQTT connection to be established */ + LOG_INF("Waiting for MQTT connection..."); + while (!mqtt_connected) { + k_sleep(K_MSEC(100)); + } + LOG_INF("MQTT connected successfully."); + /* Publish discovery announcement */ if (mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID) != 0) { LOG_ERR("Discovery announcement failed"); diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index b5cccba..0c130f8 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -26,19 +26,45 @@ static struct mqtt_client client_ctx; static struct sockaddr_storage broker_addr; /* Flag to indicate connection status */ -static bool mqtt_connected = false; +bool mqtt_connected = false; static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) { switch (evt->type) { - case MQTT_EVT_CONNACK: - if (evt->result == 0) { + case MQTT_EVT_CONNACK: { + const struct mqtt_connack_param *connack = &evt->param.connack; + + if (evt->result == 0 && connack->return_code == MQTT_CONNECTION_ACCEPTED) { mqtt_connected = true; - LOG_INF("Connected to MQTT broker"); + LOG_INF("MQTT connection accepted by broker"); } else { - LOG_ERR("Connection failed, result: %d", evt->result); + mqtt_connected = false; + + LOG_ERR("MQTT connection failed. Result: %d, Return Code: %d", evt->result, connack->return_code); + + switch (connack->return_code) { + case MQTT_UNACCEPTABLE_PROTOCOL_VERSION: + LOG_ERR("Error: MQTT_UNACCEPTABLE_PROTOCOL_VERSION - The server does not support the MQTT protocol version requested."); + break; + case MQTT_IDENTIFIER_REJECTED: + LOG_ERR("Error: MQTT_IDENTIFIER_REJECTED - The client identifier is not allowed by the server."); + break; + case MQTT_SERVER_UNAVAILABLE: + LOG_ERR("Error: MQTT_SERVER_UNAVAILABLE - The MQTT service is unavailable."); + break; + case MQTT_BAD_USER_NAME_OR_PASSWORD: + LOG_ERR("Error: MQTT_BAD_USER_NAME_OR_PASSWORD - Username or password is malformed."); + break; + case MQTT_NOT_AUTHORIZED: + LOG_ERR("Error: MQTT_NOT_AUTHORIZED - The client is not authorized to connect."); + break; + default: + LOG_ERR("Error: Unknown connection return code (%d)", connack->return_code); + break; + } } break; + } case MQTT_EVT_DISCONNECT: mqtt_connected = false; @@ -48,15 +74,6 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt case MQTT_EVT_PUBLISH: { const struct mqtt_publish_param *publish = &evt->param.publish; LOG_INF("Message received on topic: %s", publish->message.topic.topic.utf8); - - /* Handle messages */ - if (strstr(publish->message.topic.topic.utf8, "start")) { - LOG_INF("Start command received"); - /* Handle start command */ - } else if (strstr(publish->message.topic.topic.utf8, "stop")) { - LOG_INF("Stop command received"); - /* Handle stop command */ - } break; } @@ -69,6 +86,7 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt break; default: + LOG_WRN("Unhandled MQTT event: %d", evt->type); break; } } @@ -141,11 +159,36 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i .retain_flag = 0, }; + if (!mqtt_connected) { + LOG_ERR("MQTT client is not connected. Discovery announcement aborted."); + return -ENOTCONN; + } + int ret = mqtt_publish(&client_ctx, ¶m); if (ret != 0) { - LOG_ERR("Failed to publish discovery announcement, ret=%d", ret); + LOG_ERR("Failed to publish discovery announcement. Error code: %d", ret); + + switch (ret) { + case -ENOTCONN: + LOG_ERR("Error: MQTT client is not connected to the broker."); + break; + case -EIO: + LOG_ERR("Error: I/O error occurred while publishing."); + break; + case -EINVAL: + LOG_ERR("Error: Invalid parameter provided to publish."); + break; + case -ENOMEM: + LOG_ERR("Error: Insufficient memory to process the publish request."); + break; + default: + LOG_ERR("Error: Unknown publishing error."); + break; + } + return ret; } + LOG_INF("Discovery announcement published successfully to topic: %s", topic); return ret; } diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index f92ca95..3e2bef6 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -2,17 +2,13 @@ #define MQTT_CLIENT_H #include +#include -/* Initialize and connect the MQTT client */ -int mqtt_client_init_and_connect(void); - -/* Process MQTT events */ -void mqtt_client_process(void); +extern bool mqtt_connected; -/* Publish discovery announcement */ +int mqtt_client_init_and_connect(void); int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); - -/* Subscribe to required topics */ int mqtt_client_subscribe(const char *channel_id); +void mqtt_client_process(void); #endif /* MQTT_CLIENT_H */ From b9230f5dbadcdc372bba48dbedd7e4be5687791c Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 14 Jan 2025 11:40:01 +0300 Subject: [PATCH 27/50] Increase CONFIG_MQTT_KEEPALIVE --- embed-proplet/prj.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 1d123f7..5edf0ed 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -40,7 +40,7 @@ CONFIG_NET_MAX_CONTEXTS=10 CONFIG_MQTT_LIB=y # Uncomment to enable TLS support for socket MQTT Library. # CONFIG_MQTT_LIB_TLS=y -CONFIG_MQTT_KEEPALIVE=10 +CONFIG_MQTT_KEEPALIVE=30 CONFIG_MQTT_CLEAN_SESSION=n CONFIG_MQTT_LIB_WEBSOCKET=n # Uncomment to enable ALPN protocol for socket MQTT Library. From a716f37b7081603f14688698646a526b98fb8b16 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 14 Jan 2025 18:18:39 +0300 Subject: [PATCH 28/50] Add polling for sockets --- embed-proplet/prj.conf | 14 ++- embed-proplet/src/main.c | 15 +-- embed-proplet/src/mqtt_client.c | 158 ++++++++++++++++++-------------- embed-proplet/src/mqtt_client.h | 29 +++++- 4 files changed, 134 insertions(+), 82 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 5edf0ed..cf7cad7 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -27,17 +27,20 @@ CONFIG_NET_L2_WIFI_MGMT=y CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 # Buffer and Packet Management -CONFIG_NET_BUF_RX_COUNT=32 -CONFIG_NET_BUF_TX_COUNT=32 +CONFIG_NET_BUF_RX_COUNT=128 +CONFIG_NET_BUF_TX_COUNT=128 CONFIG_NET_BUF_DATA_SIZE=512 -CONFIG_NET_PKT_RX_COUNT=64 -CONFIG_NET_PKT_TX_COUNT=64 +CONFIG_NET_PKT_RX_COUNT=128 +CONFIG_NET_PKT_TX_COUNT=128 CONFIG_NET_RX_STACK_SIZE=8192 CONFIG_NET_TX_STACK_SIZE=4096 CONFIG_NET_MAX_CONTEXTS=10 # MQTT Client CONFIG_MQTT_LIB=y +# Enable Sockets (used by MQTT lib) +CONFIG_NET_SOCKETS=y + # Uncomment to enable TLS support for socket MQTT Library. # CONFIG_MQTT_LIB_TLS=y CONFIG_MQTT_KEEPALIVE=30 @@ -46,6 +49,9 @@ CONFIG_MQTT_LIB_WEBSOCKET=n # Uncomment to enable ALPN protocol for socket MQTT Library. # CONFIG_MQTT_LIB_TLS_USE_ALPN=y +# Enable JSON +CONFIG_JSON_LIBRARY=y + # Logging CONFIG_LOG=y CONFIG_NET_LOG=y diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index d5838db..3f30d06 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -28,17 +28,12 @@ void main(void) } LOG_INF("Wi-Fi connected, proceeding with MQTT initialization"); - /* Initialize MQTT client */ - if (mqtt_client_init_and_connect() != 0) { - LOG_ERR("MQTT client initialization failed"); - return; + /* Initialize and connect MQTT client */ + while (mqtt_client_init_and_connect() != 0) { + LOG_ERR("MQTT client initialization failed. Retrying..."); + k_sleep(K_SECONDS(5)); } - /* Wait for MQTT connection to be established */ - LOG_INF("Waiting for MQTT connection..."); - while (!mqtt_connected) { - k_sleep(K_MSEC(100)); - } LOG_INF("MQTT connected successfully."); /* Publish discovery announcement */ @@ -58,6 +53,6 @@ void main(void) /* Main loop for MQTT processing */ while (1) { mqtt_client_process(); - k_sleep(K_SECONDS(5)); + k_sleep(K_MSEC(100)); // Adjusted for better responsiveness } } diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 0c130f8..a858e73 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -25,60 +25,73 @@ static uint8_t tx_buffer[TX_BUFFER_SIZE]; static struct mqtt_client client_ctx; static struct sockaddr_storage broker_addr; +/* Socket descriptor */ +static struct zsock_pollfd fds[1]; +static int nfds; + /* Flag to indicate connection status */ bool mqtt_connected = false; +/* Prepare the file descriptor for polling */ +static void prepare_fds(struct mqtt_client *client) +{ + if (client->transport.type == MQTT_TRANSPORT_NON_SECURE) { + fds[0].fd = client->transport.tcp.sock; + } +#if defined(CONFIG_MQTT_LIB_TLS) + else if (client->transport.type == MQTT_TRANSPORT_SECURE) { + fds[0].fd = client->transport.tls.sock; + } +#endif + fds[0].events = ZSOCK_POLLIN; + nfds = 1; +} + +static void clear_fds(void) +{ + nfds = 0; +} + +static int poll_mqtt_socket(struct mqtt_client *client, int timeout) +{ + prepare_fds(client); + + if (nfds <= 0) { + return -EINVAL; + } + + int rc = zsock_poll(fds, nfds, timeout); + if (rc < 0) { + LOG_ERR("Socket poll error [%d]", rc); + } + + return rc; +} + static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt *evt) { switch (evt->type) { - case MQTT_EVT_CONNACK: { - const struct mqtt_connack_param *connack = &evt->param.connack; - - if (evt->result == 0 && connack->return_code == MQTT_CONNECTION_ACCEPTED) { + case MQTT_EVT_CONNACK: + if (evt->result != 0) { + LOG_ERR("MQTT connection failed [%d]", evt->result); + } else { mqtt_connected = true; LOG_INF("MQTT connection accepted by broker"); - } else { - mqtt_connected = false; - - LOG_ERR("MQTT connection failed. Result: %d, Return Code: %d", evt->result, connack->return_code); - - switch (connack->return_code) { - case MQTT_UNACCEPTABLE_PROTOCOL_VERSION: - LOG_ERR("Error: MQTT_UNACCEPTABLE_PROTOCOL_VERSION - The server does not support the MQTT protocol version requested."); - break; - case MQTT_IDENTIFIER_REJECTED: - LOG_ERR("Error: MQTT_IDENTIFIER_REJECTED - The client identifier is not allowed by the server."); - break; - case MQTT_SERVER_UNAVAILABLE: - LOG_ERR("Error: MQTT_SERVER_UNAVAILABLE - The MQTT service is unavailable."); - break; - case MQTT_BAD_USER_NAME_OR_PASSWORD: - LOG_ERR("Error: MQTT_BAD_USER_NAME_OR_PASSWORD - Username or password is malformed."); - break; - case MQTT_NOT_AUTHORIZED: - LOG_ERR("Error: MQTT_NOT_AUTHORIZED - The client is not authorized to connect."); - break; - default: - LOG_ERR("Error: Unknown connection return code (%d)", connack->return_code); - break; - } } break; - } case MQTT_EVT_DISCONNECT: mqtt_connected = false; + clear_fds(); LOG_INF("Disconnected from MQTT broker"); break; - case MQTT_EVT_PUBLISH: { - const struct mqtt_publish_param *publish = &evt->param.publish; - LOG_INF("Message received on topic: %s", publish->message.topic.topic.utf8); + case MQTT_EVT_PUBLISH: + LOG_INF("Message received on topic"); break; - } case MQTT_EVT_SUBACK: - LOG_INF("Subscribed to topic(s)"); + LOG_INF("Subscribed successfully"); break; case MQTT_EVT_PUBACK: @@ -86,7 +99,7 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt break; default: - LOG_WRN("Unhandled MQTT event: %d", evt->type); + LOG_WRN("Unhandled MQTT event [%d]", evt->type); break; } } @@ -120,37 +133,63 @@ int mqtt_client_init_and_connect(void) client_ctx.tx_buf = tx_buffer; client_ctx.tx_buf_size = sizeof(tx_buffer); - /* Connect to broker */ - ret = mqtt_connect(&client_ctx); - if (ret != 0) { - LOG_ERR("MQTT connect failed, ret=%d", ret); - return ret; + while (!mqtt_connected) { + /* Attempt to connect */ + ret = mqtt_connect(&client_ctx); + if (ret != 0) { + LOG_ERR("MQTT connect failed [%d]. Retrying...", ret); + k_sleep(K_SECONDS(5)); + continue; + } + + /* Poll the socket for a response */ + LOG_INF("Waiting for CONNACK..."); + ret = poll_mqtt_socket(&client_ctx, 5000); // 5-second timeout + if (ret < 0) { + LOG_ERR("Socket poll failed, ret=%d", ret); + mqtt_abort(&client_ctx); + continue; + } else if (ret == 0) { + LOG_ERR("Poll timed out waiting for CONNACK. Retrying..."); + mqtt_abort(&client_ctx); + continue; + } + + /* Process the incoming data */ + mqtt_input(&client_ctx); + + /* If not connected, abort and retry */ + if (!mqtt_connected) { + LOG_ERR("MQTT connection not established. Retrying..."); + mqtt_abort(&client_ctx); + } } - LOG_INF("MQTT client initialized and connected"); + LOG_INF("MQTT client connected successfully"); return 0; } int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id) { char topic[128]; - snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); - char payload[128]; + + snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); snprintf(payload, sizeof(payload), - "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); + "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", + proplet_id, channel_id); struct mqtt_publish_param param = { .message = { .topic = { .topic = { - .utf8 = (uint8_t *)topic, + .utf8 = topic, .size = strlen(topic), }, .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, .payload = { - .data = (uint8_t *)payload, + .data = payload, .len = strlen(payload), }, }, @@ -167,29 +206,11 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i int ret = mqtt_publish(&client_ctx, ¶m); if (ret != 0) { LOG_ERR("Failed to publish discovery announcement. Error code: %d", ret); - - switch (ret) { - case -ENOTCONN: - LOG_ERR("Error: MQTT client is not connected to the broker."); - break; - case -EIO: - LOG_ERR("Error: I/O error occurred while publishing."); - break; - case -EINVAL: - LOG_ERR("Error: Invalid parameter provided to publish."); - break; - case -ENOMEM: - LOG_ERR("Error: Insufficient memory to process the publish request."); - break; - default: - LOG_ERR("Error: Unknown publishing error."); - break; - } return ret; } LOG_INF("Discovery announcement published successfully to topic: %s", topic); - return ret; + return 0; } int mqtt_client_subscribe(const char *channel_id) @@ -234,7 +255,10 @@ int mqtt_client_subscribe(const char *channel_id) void mqtt_client_process(void) { if (mqtt_connected) { - mqtt_input(&client_ctx); + int ret = poll_mqtt_socket(&client_ctx, mqtt_keepalive_time_left(&client_ctx)); + if (ret > 0) { + mqtt_input(&client_ctx); + } mqtt_live(&client_ctx); } } diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index 3e2bef6..1a552b9 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -4,11 +4,38 @@ #include #include -extern bool mqtt_connected; +/* Extern variables */ +extern bool mqtt_connected; // Indicates MQTT connection status +/* Function Prototypes */ + +/** + * @brief Initialize the MQTT client and establish a connection to the broker. + * + * @return 0 on success, or a negative error code on failure. + */ int mqtt_client_init_and_connect(void); + +/** + * @brief Publish a discovery announcement message to a specific topic. + * + * @param proplet_id The unique ID of the proplet. + * @param channel_id The ID of the channel for the announcement. + * @return 0 on success, or a negative error code on failure. + */ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); + +/** + * @brief Subscribe to topics for a specific channel. + * + * @param channel_id The ID of the channel to subscribe to. + * @return 0 on success, or a negative error code on failure. + */ int mqtt_client_subscribe(const char *channel_id); + +/** + * @brief Process incoming MQTT messages and maintain the connection. + */ void mqtt_client_process(void); #endif /* MQTT_CLIENT_H */ From 489a2f0e429b175ec9f53c9044e594d0cfe6cdbe Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 14 Jan 2025 19:26:05 +0300 Subject: [PATCH 29/50] Fix failed Proplet discovery and keep alive publishing --- embed-proplet/src/mqtt_client.c | 41 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index a858e73..9b45969 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -2,6 +2,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(mqtt_client); @@ -29,10 +30,8 @@ static struct sockaddr_storage broker_addr; static struct zsock_pollfd fds[1]; static int nfds; -/* Flag to indicate connection status */ bool mqtt_connected = false; -/* Prepare the file descriptor for polling */ static void prepare_fds(struct mqtt_client *client) { if (client->transport.type == MQTT_TRANSPORT_NON_SECURE) { @@ -108,7 +107,6 @@ int mqtt_client_init_and_connect(void) { int ret; - /* Resolve broker address */ struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; broker->sin_family = AF_INET; broker->sin_port = htons(MQTT_BROKER_PORT); @@ -117,8 +115,7 @@ int mqtt_client_init_and_connect(void) LOG_ERR("Failed to resolve broker address, ret=%d", ret); return ret; } - - /* Initialize MQTT client */ + mqtt_client_init(&client_ctx); client_ctx.broker = &broker_addr; client_ctx.evt_cb = mqtt_event_handler; @@ -126,15 +123,12 @@ int mqtt_client_init_and_connect(void) client_ctx.client_id.size = strlen(CLIENT_ID); client_ctx.protocol_version = MQTT_VERSION_3_1_1; client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; - - /* Assign buffers */ client_ctx.rx_buf = rx_buffer; client_ctx.rx_buf_size = sizeof(rx_buffer); client_ctx.tx_buf = tx_buffer; client_ctx.tx_buf_size = sizeof(tx_buffer); while (!mqtt_connected) { - /* Attempt to connect */ ret = mqtt_connect(&client_ctx); if (ret != 0) { LOG_ERR("MQTT connect failed [%d]. Retrying...", ret); @@ -143,8 +137,7 @@ int mqtt_client_init_and_connect(void) } /* Poll the socket for a response */ - LOG_INF("Waiting for CONNACK..."); - ret = poll_mqtt_socket(&client_ctx, 5000); // 5-second timeout + ret = poll_mqtt_socket(&client_ctx, 5000); if (ret < 0) { LOG_ERR("Socket poll failed, ret=%d", ret); mqtt_abort(&client_ctx); @@ -155,10 +148,8 @@ int mqtt_client_init_and_connect(void) continue; } - /* Process the incoming data */ mqtt_input(&client_ctx); - /* If not connected, abort and retry */ if (!mqtt_connected) { LOG_ERR("MQTT connection not established. Retrying..."); mqtt_abort(&client_ctx); @@ -179,30 +170,38 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); + LOG_DBG("Topic: %s (Length: %zu)", topic, strlen(topic)); + LOG_DBG("Payload: %s (Length: %zu)", payload, strlen(payload)); + + if (strlen(topic) >= sizeof(topic) || strlen(payload) >= sizeof(payload)) { + LOG_ERR("Topic or payload size exceeds the maximum allowable size."); + return -EINVAL; + } + + if (!mqtt_connected) { + LOG_ERR("MQTT client is not connected. Discovery announcement aborted."); + return -ENOTCONN; + } + struct mqtt_publish_param param = { .message = { .topic = { .topic = { - .utf8 = topic, + .utf8 = (uint8_t *)topic, .size = strlen(topic), }, .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, .payload = { - .data = payload, + .data = (uint8_t *)payload, .len = strlen(payload), }, }, - .message_id = 0, + .message_id = sys_rand32_get() & 0xFFFF, .dup_flag = 0, - .retain_flag = 0, + .retain_flag = 0 }; - if (!mqtt_connected) { - LOG_ERR("MQTT client is not connected. Discovery announcement aborted."); - return -ENOTCONN; - } - int ret = mqtt_publish(&client_ctx, ¶m); if (ret != 0) { LOG_ERR("Failed to publish discovery announcement. Error code: %d", ret); From 1755517976e843258a7534ed3d1597e4b3cb0c5e Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 10:05:51 +0300 Subject: [PATCH 30/50] Handle missing mqtt events --- embed-proplet/src/mqtt_client.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 9b45969..695c95a 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -94,7 +94,27 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt break; case MQTT_EVT_PUBACK: - LOG_INF("Message published successfully"); + LOG_INF("QoS 1 Message published successfully"); + break; + + case MQTT_EVT_PUBREC: + LOG_INF("QoS 2 publish received"); + break; + + case MQTT_EVT_PUBREL: + LOG_INF("QoS 2 publish released"); + break; + + case MQTT_EVT_PUBCOMP: + LOG_INF("QoS 2 publish complete"); + break; + + case MQTT_EVT_UNSUBACK: + LOG_INF("Unsubscribed successfully"); + break; + + case MQTT_EVT_PINGRESP: + LOG_INF("Ping response received from broker"); break; default: From f5475e07570be29762f994f2da4af55aae0d5bd3 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 12:57:24 +0300 Subject: [PATCH 31/50] Add missing sub/pub methods --- embed-proplet/prj.conf | 2 + embed-proplet/src/main.c | 24 +-- embed-proplet/src/mqtt_client.c | 310 ++++++++++++++++++++++++++++++-- embed-proplet/src/mqtt_client.h | 70 +++++++- 4 files changed, 378 insertions(+), 28 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index cf7cad7..acdd078 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -80,6 +80,8 @@ CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # CONFIG_THREAD_ANALYZER_ISR_STACK_USAGE=y # Miscellaneous +CONFIG_BASE64=y +CONFIG_FILE_SYSTEM=y CONFIG_MAIN_STACK_SIZE=8192 CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 3f30d06..dfdc6be 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -10,49 +10,53 @@ LOG_MODULE_REGISTER(main); #define PROPLET_ID "proplet1" #define CHANNEL_ID "channel1" +const char *channel_id = CHANNEL_ID; + void main(void) { LOG_INF("Starting Proplet..."); - /* Initialize Wi-Fi */ wifi_manager_init(); if (wifi_manager_connect(WIFI_SSID, WIFI_PSK) != 0) { LOG_ERR("Wi-Fi connection failed"); return; } - /* Wait for Wi-Fi connection */ if (!wifi_manager_wait_for_connection(K_SECONDS(60))) { LOG_ERR("Wi-Fi connection timed out"); return; } + LOG_INF("Wi-Fi connected, proceeding with MQTT initialization"); - /* Initialize and connect MQTT client */ - while (mqtt_client_init_and_connect() != 0) { + while (mqtt_client_connect() != 0) { LOG_ERR("MQTT client initialization failed. Retrying..."); k_sleep(K_SECONDS(5)); } LOG_INF("MQTT connected successfully."); - /* Publish discovery announcement */ - if (mqtt_client_discovery_announce(PROPLET_ID, CHANNEL_ID) != 0) { + if (publish_discovery(PROPLET_ID, CHANNEL_ID) != 0) { LOG_ERR("Discovery announcement failed"); return; } - /* Subscribe to topics */ - if (mqtt_client_subscribe(CHANNEL_ID) != 0) { + if (subscribe(CHANNEL_ID) != 0) { LOG_ERR("Topic subscription failed"); return; } LOG_INF("Proplet ready"); - /* Main loop for MQTT processing */ while (1) { mqtt_client_process(); - k_sleep(K_MSEC(100)); // Adjusted for better responsiveness + + if (mqtt_connected) { + publish_alive_message(CHANNEL_ID); + } else { + LOG_WRN("MQTT client is not connected"); + } + + k_sleep(K_SECONDS(30)); } } diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 695c95a..36b4dad 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -1,8 +1,12 @@ +#include #include "mqtt_client.h" +#include +#include #include #include #include #include +#include LOG_MODULE_REGISTER(mqtt_client); @@ -12,9 +16,14 @@ LOG_MODULE_REGISTER(mqtt_client); #define MQTT_BROKER_HOSTNAME "192.168.88.179" /* Replace with your broker's IP */ #define MQTT_BROKER_PORT 1883 -#define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" -#define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" -#define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" +#define REGISTRY_ACK_TOPIC_TEMPLATE "channels/%s/messages/control/manager/registry" +#define ALIVE_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/alive" +#define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" +#define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" +#define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" +#define REGISTRY_RESPONSE_TOPIC "channels/%s/messages/registry/server" +#define FETCH_REQUEST_TOPIC_TEMPLATE "channels/%s/messages/registry/proplet" +#define RESULTS_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/results" #define CLIENT_ID "proplet-esp32s3" @@ -31,6 +40,48 @@ static struct zsock_pollfd fds[1]; static int nfds; bool mqtt_connected = false; +struct start_command { + char task_id[50]; + char function_name[50]; + char wasm_file[100]; +}; + +struct stop_command { + char task_id[50]; +}; + +struct registry_response { + char app_name[50]; + int chunk_idx; + int total_chunks; + char data[256]; +}; + +struct chunk_tracker { + int total_chunks; + int received_chunks; + bool *chunk_received; + FILE *file; +}; + +static const struct json_obj_descr start_command_descr[] = { + JSON_OBJ_DESCR_PRIM(struct start_command, task_id, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct start_command, function_name, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct start_command, wasm_file, JSON_TOK_STRING), +}; + +static const struct json_obj_descr stop_command_descr[] = { + JSON_OBJ_DESCR_PRIM(struct stop_command, task_id, JSON_TOK_STRING), +}; + +static const struct json_obj_descr registry_response_descr[] = { + JSON_OBJ_DESCR_PRIM(struct registry_response, app_name, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct registry_response, chunk_idx, JSON_TOK_NUMBER), + JSON_OBJ_DESCR_PRIM(struct registry_response, total_chunks, JSON_TOK_NUMBER), + JSON_OBJ_DESCR_PRIM(struct registry_response, data, JSON_TOK_STRING), +}; + +static struct chunk_tracker *app_chunk_tracker = NULL; static void prepare_fds(struct mqtt_client *client) { @@ -86,8 +137,44 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt break; case MQTT_EVT_PUBLISH: - LOG_INF("Message received on topic"); - break; + { + const struct mqtt_publish_param *pub = &evt->param.publish; + char payload[PAYLOAD_BUFFER_SIZE]; + char start_topic[128]; + char stop_topic[128]; + char registry_response_topic[128]; + int ret; + + extern const char *channel_id; + + snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); + snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); + + LOG_INF("Message received on topic: %s", pub->message.topic.topic.utf8); + + ret = mqtt_read_publish_payload(&client_ctx, payload, MIN(pub->message.payload.len, PAYLOAD_BUFFER_SIZE - 1)); + if (ret < 0) { + LOG_ERR("Failed to read payload [%d]", ret); + return; + } + + // Null-terminate the payload + payload[ret] = '\0'; + + LOG_INF("Payload: %s", payload); + + if (strncmp(pub->message.topic.topic.utf8, start_topic, pub->message.topic.topic.size) == 0) { + handle_start_command(payload); + } else if (strncmp(pub->message.topic.topic.utf8, stop_topic, pub->message.topic.topic.size) == 0) { + handle_stop_command(payload); + } else if (strncmp(pub->message.topic.topic.utf8, registry_response_topic, pub->message.topic.topic.size) == 0) { + handle_registry_response(payload); + } else { + LOG_WRN("Unknown topic"); + } + break; + } case MQTT_EVT_SUBACK: LOG_INF("Subscribed successfully"); @@ -123,7 +210,63 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt } } -int mqtt_client_init_and_connect(void) +int publish(const char *channel_id, const char *topic_template, const char *payload) +{ + char topic[128]; + + snprintf(topic, sizeof(topic), topic_template, channel_id); + + if (!mqtt_connected) { + LOG_ERR("MQTT client is not connected. Cannot publish to topic: %s", topic); + return -ENOTCONN; + } + + struct mqtt_publish_param param = { + .message = { + .topic = { + .topic = { + .utf8 = topic, + .size = strlen(topic), + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE, + }, + .payload = { + .data = (uint8_t *)payload, + .len = strlen(payload), + }, + }, + .message_id = sys_rand32_get() & 0xFFFF, + .dup_flag = 0, + .retain_flag = 0 + }; + + int ret = mqtt_publish(&client_ctx, ¶m); + if (ret != 0) { + LOG_ERR("Failed to publish to topic: %s. Error: %d", topic, ret); + return ret; + } + + LOG_INF("Published to topic: %s. Payload: %s", topic, payload); + return 0; +} + +void publish_alive_message(const char *channel_id) +{ + char payload[128]; + snprintf(payload, sizeof(payload), + "{\"status\":\"alive\",\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", + CLIENT_ID, channel_id); + publish(channel_id, ALIVE_TOPIC_TEMPLATE, payload); +} + +void publish_registry_request(const char *channel_id, const char *app_name) +{ + char payload[128]; + snprintf(payload, sizeof(payload), "{\"app_name\":\"%s\"}", app_name); + publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, payload); +} + +int mqtt_client_connect(void) { int ret; @@ -180,7 +323,7 @@ int mqtt_client_init_and_connect(void) return 0; } -int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id) +int publish_discovery(const char *proplet_id, const char *channel_id) { char topic[128]; char payload[128]; @@ -232,29 +375,38 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i return 0; } -int mqtt_client_subscribe(const char *channel_id) +int subscribe(const char *channel_id) { char start_topic[128]; - snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); - char stop_topic[128]; + char registry_response_topic[128]; + + snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); struct mqtt_topic topics[] = { { .topic = { - .utf8 = (uint8_t *)start_topic, + .utf8 = start_topic, .size = strlen(start_topic), }, .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, { .topic = { - .utf8 = (uint8_t *)stop_topic, + .utf8 = stop_topic, .size = strlen(stop_topic), }, .qos = MQTT_QOS_1_AT_LEAST_ONCE, }, + { + .topic = { + .utf8 = registry_response_topic, + .size = strlen(registry_response_topic), + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE, + }, }; struct mqtt_subscription_list sub_list = { @@ -266,11 +418,145 @@ int mqtt_client_subscribe(const char *channel_id) int ret = mqtt_subscribe(&client_ctx, &sub_list); if (ret != 0) { LOG_ERR("Failed to subscribe to topics, ret=%d", ret); + } else { + LOG_INF("Subscribed to topics successfully"); } return ret; } + +void handle_start_command(const char *payload) { + struct start_command cmd; + int ret; + + ret = json_obj_parse((char *)payload, strlen(payload), start_command_descr, ARRAY_SIZE(start_command_descr), &cmd); + + if (ret < 0) { + LOG_ERR("Failed to parse start command payload, error: %d", ret); + return; + } + + LOG_INF("Starting task:"); + LOG_INF("Task ID: %s", cmd.task_id); + LOG_INF("Function: %s", cmd.function_name); + LOG_INF("Wasm File: %s", cmd.wasm_file); + + // TODO: Use WAMR runtime to start the task + // Example: + // wamr_start_app(cmd.wasm_file, cmd.function_name); +} + +void handle_stop_command(const char *payload) { + struct stop_command cmd; + int ret; + + ret = json_obj_parse(payload, strlen(payload), stop_command_descr, ARRAY_SIZE(stop_command_descr), &cmd); + if (ret < 0) { + LOG_ERR("Failed to parse stop command payload, error: %d", ret); + return; + } + + LOG_INF("Stopping task:"); + LOG_INF("Task ID: %s", cmd.task_id); + + // TODO: Use WAMR runtime to stop the task + // Example: + // wamr_stop_app(cmd.task_id); +} + +void request_registry_file(const char *channel_id, const char *app_name) +{ + char registry_payload[128]; + + snprintf(registry_payload, sizeof(registry_payload), + "{\"app_name\":\"%s\"}", + app_name); + + if (publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, registry_payload) != 0) { + LOG_ERR("Failed to request registry file"); + } else { + LOG_INF("Requested registry file for app: %s", app_name); + } +} + +void publish_results(const char *channel_id, const char *task_id, const char *results) +{ + char results_payload[256]; + + snprintf(results_payload, sizeof(results_payload), + "{\"task_id\":\"%s\",\"results\":\"%s\"}", + task_id, results); + + if (publish(channel_id, RESULTS_TOPIC_TEMPLATE, results_payload) != 0) { + LOG_ERR("Failed to publish results"); + } else { + LOG_INF("Published results for task: %s", task_id); + } +} + +void handle_registry_response(const char *payload) { + struct registry_response resp; + int ret; + + ret = json_obj_parse((char *)payload, strlen(payload), registry_response_descr, ARRAY_SIZE(registry_response_descr), &resp); + if (ret < 0) { + LOG_ERR("Failed to parse registry response, error: %d", ret); + return; + } + + LOG_INF("Registry response received:"); + LOG_INF("App Name: %s", resp.app_name); + LOG_INF("Chunk Index: %d", resp.chunk_idx); + LOG_INF("Total Chunks: %d", resp.total_chunks); + + if (app_chunk_tracker == NULL) { + app_chunk_tracker = malloc(sizeof(struct chunk_tracker)); + app_chunk_tracker->total_chunks = resp.total_chunks; + app_chunk_tracker->received_chunks = 0; + app_chunk_tracker->chunk_received = calloc(resp.total_chunks, sizeof(bool)); + char filename[100]; + snprintf(filename, sizeof(filename), "%s.wasm", resp.app_name); + app_chunk_tracker->file = fopen(filename, "wb"); + if (!app_chunk_tracker->file) { + LOG_ERR("Failed to open file for writing chunks"); + free(app_chunk_tracker->chunk_received); + free(app_chunk_tracker); + return; + } + } + + uint8_t binary_data[256]; + size_t binary_data_len = sizeof(binary_data); + + ret = base64_decode(binary_data, sizeof(binary_data), resp.data, strlen(resp.data), &binary_data_len); + if (ret != 0) { + LOG_ERR("Failed to decode Base64 data for chunk %d", resp.chunk_idx); + return; + } + + fseek(app_chunk_tracker->file, resp.chunk_idx * binary_data_len, SEEK_SET); + fwrite(binary_data, 1, binary_data_len, app_chunk_tracker->file); + + if (!app_chunk_tracker->chunk_received[resp.chunk_idx]) { + app_chunk_tracker->chunk_received[resp.chunk_idx] = true; + app_chunk_tracker->received_chunks++; + } + + LOG_INF("Chunk %d/%d received for app: %s", app_chunk_tracker->received_chunks, app_chunk_tracker->total_chunks, resp.app_name); + + if (app_chunk_tracker->received_chunks == app_chunk_tracker->total_chunks) { + LOG_INF("All chunks received for app: %s. Assembling binary file.", resp.app_name); + fclose(app_chunk_tracker->file); + + free(app_chunk_tracker->chunk_received); + free(app_chunk_tracker); + app_chunk_tracker = NULL; + + LOG_INF("WASM binary assembled successfully for app: %s", resp.app_name); + } +} + void mqtt_client_process(void) { if (mqtt_connected) { diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index 1a552b9..5bfb727 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -4,17 +4,16 @@ #include #include -/* Extern variables */ -extern bool mqtt_connected; // Indicates MQTT connection status +#define PAYLOAD_BUFFER_SIZE 256 -/* Function Prototypes */ +extern bool mqtt_connected; /** * @brief Initialize the MQTT client and establish a connection to the broker. * * @return 0 on success, or a negative error code on failure. */ -int mqtt_client_init_and_connect(void); +int mqtt_client_connect(void); /** * @brief Publish a discovery announcement message to a specific topic. @@ -23,7 +22,7 @@ int mqtt_client_init_and_connect(void); * @param channel_id The ID of the channel for the announcement. * @return 0 on success, or a negative error code on failure. */ -int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_id); +int publish_discovery(const char *proplet_id, const char *channel_id); /** * @brief Subscribe to topics for a specific channel. @@ -31,11 +30,70 @@ int mqtt_client_discovery_announce(const char *proplet_id, const char *channel_i * @param channel_id The ID of the channel to subscribe to. * @return 0 on success, or a negative error code on failure. */ -int mqtt_client_subscribe(const char *channel_id); +int subscribe(const char *channel_id); + +/** + * @brief Publish a message to a specific MQTT topic. + * + * @param channel_id The ID of the channel for dynamic topic generation. + * @param topic_template The template of the topic. + * @param payload The payload to be published. + * @return 0 on success, or a negative error code on failure. + */ +int publish(const char *channel_id, const char *topic_template, const char *payload); + +/** + * @brief Publish a periodic "alive" message to notify the manager of liveliness. + * + * @param channel_id The ID of the channel for dynamic topic generation. + */ +void publish_alive_message(const char *channel_id); + +/** + * @brief Publish a request to fetch a file from the registry. + * + * @param channel_id The ID of the channel for dynamic topic generation. + * @param app_name The name of the application to fetch. + */ +void publish_registry_request(const char *channel_id, const char *app_name); + +/** + * @brief Publish the results of a completed task. + * + * @param channel_id The ID of the channel for dynamic topic generation. + * @param task_id The ID of the task whose results are being published. + * @param results The results of the task. + */ +void publish_results(const char *channel_id, const char *task_id, const char *results); /** * @brief Process incoming MQTT messages and maintain the connection. */ void mqtt_client_process(void); +/** + * @brief Handle the start command received via MQTT. + * + * @param payload The JSON payload for the start command. + */ +void handle_start_command(const char *payload); + +/** + * @brief Handle the stop command received via MQTT. + * + * @param payload The JSON payload for the stop command. + */ +void handle_stop_command(const char *payload); + +/** + * @brief Handle registry responses for WASM binary chunks. + * + * This function processes incoming chunks of WASM binaries sent by the registry proxy. + * It logs details of each chunk, tracks progress, and assembles the chunks into a complete + * binary file when all chunks are received. + * + * @param payload The JSON payload containing the chunk details. + */ +void handle_registry_response(const char *payload); + #endif /* MQTT_CLIENT_H */ From 689f000f557976c9dcd73f6b52aa3462e8abc1e3 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 13:49:27 +0300 Subject: [PATCH 32/50] Fix errors due to added missing sub/pub methods --- embed-proplet/prj.conf | 2 +- embed-proplet/src/mqtt_client.c | 37 ++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index acdd078..c19a7b0 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -81,7 +81,6 @@ CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # Miscellaneous CONFIG_BASE64=y -CONFIG_FILE_SYSTEM=y CONFIG_MAIN_STACK_SIZE=8192 CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 @@ -89,3 +88,4 @@ CONFIG_IDLE_STACK_SIZE=2048 CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 +CONFIG_HEAP_MEM_POOL_SIZE=131072 diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 36b4dad..6f09213 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include LOG_MODULE_REGISTER(mqtt_client); @@ -61,7 +63,7 @@ struct chunk_tracker { int total_chunks; int received_chunks; bool *chunk_received; - FILE *file; + uint8_t *file_data; }; static const struct json_obj_descr start_command_descr[] = { @@ -512,31 +514,40 @@ void handle_registry_response(const char *payload) { if (app_chunk_tracker == NULL) { app_chunk_tracker = malloc(sizeof(struct chunk_tracker)); + if (!app_chunk_tracker) { + LOG_ERR("Failed to allocate memory for chunk tracker"); + return; + } + app_chunk_tracker->total_chunks = resp.total_chunks; app_chunk_tracker->received_chunks = 0; + app_chunk_tracker->chunk_received = calloc(resp.total_chunks, sizeof(bool)); - char filename[100]; - snprintf(filename, sizeof(filename), "%s.wasm", resp.app_name); - app_chunk_tracker->file = fopen(filename, "wb"); - if (!app_chunk_tracker->file) { - LOG_ERR("Failed to open file for writing chunks"); + app_chunk_tracker->file_data = malloc(resp.total_chunks * 256); // Assuming 256 bytes per chunk + if (!app_chunk_tracker->chunk_received || !app_chunk_tracker->file_data) { + LOG_ERR("Failed to allocate memory for chunk data"); free(app_chunk_tracker->chunk_received); free(app_chunk_tracker); + app_chunk_tracker = NULL; return; } } + if (app_chunk_tracker->total_chunks != resp.total_chunks) { + LOG_ERR("Inconsistent total chunks value: %d != %d", app_chunk_tracker->total_chunks, resp.total_chunks); + return; + } + uint8_t binary_data[256]; size_t binary_data_len = sizeof(binary_data); - ret = base64_decode(binary_data, sizeof(binary_data), resp.data, strlen(resp.data), &binary_data_len); - if (ret != 0) { + ret = base64_decode(binary_data, sizeof(binary_data), &binary_data_len, (const uint8_t *)resp.data, strlen(resp.data)); + if (ret < 0) { LOG_ERR("Failed to decode Base64 data for chunk %d", resp.chunk_idx); return; } - fseek(app_chunk_tracker->file, resp.chunk_idx * binary_data_len, SEEK_SET); - fwrite(binary_data, 1, binary_data_len, app_chunk_tracker->file); + memcpy(&app_chunk_tracker->file_data[resp.chunk_idx * binary_data_len], binary_data, binary_data_len); if (!app_chunk_tracker->chunk_received[resp.chunk_idx]) { app_chunk_tracker->chunk_received[resp.chunk_idx] = true; @@ -546,10 +557,12 @@ void handle_registry_response(const char *payload) { LOG_INF("Chunk %d/%d received for app: %s", app_chunk_tracker->received_chunks, app_chunk_tracker->total_chunks, resp.app_name); if (app_chunk_tracker->received_chunks == app_chunk_tracker->total_chunks) { - LOG_INF("All chunks received for app: %s. Assembling binary file.", resp.app_name); - fclose(app_chunk_tracker->file); + LOG_INF("All chunks received for app: %s. Binary is ready in memory.", resp.app_name); + + // Process the complete binary in `app_chunk_tracker->file_data` free(app_chunk_tracker->chunk_received); + free(app_chunk_tracker->file_data); free(app_chunk_tracker); app_chunk_tracker = NULL; From 2a429d7a89b279f6c4609e6592b6644e048b3884 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 14:03:04 +0300 Subject: [PATCH 33/50] Fix dram0_0_seg overflow --- embed-proplet/prj.conf | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index c19a7b0..ce43dc7 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -27,13 +27,13 @@ CONFIG_NET_L2_WIFI_MGMT=y CONFIG_WIFI_NM_MAX_MANAGED_INTERFACES=2 # Buffer and Packet Management -CONFIG_NET_BUF_RX_COUNT=128 -CONFIG_NET_BUF_TX_COUNT=128 -CONFIG_NET_BUF_DATA_SIZE=512 -CONFIG_NET_PKT_RX_COUNT=128 -CONFIG_NET_PKT_TX_COUNT=128 -CONFIG_NET_RX_STACK_SIZE=8192 -CONFIG_NET_TX_STACK_SIZE=4096 +CONFIG_NET_BUF_RX_COUNT=64 +CONFIG_NET_BUF_TX_COUNT=64 +CONFIG_NET_BUF_DATA_SIZE=256 +CONFIG_NET_PKT_RX_COUNT=32 +CONFIG_NET_PKT_TX_COUNT=32 +CONFIG_NET_RX_STACK_SIZE=2048 +CONFIG_NET_TX_STACK_SIZE=2048 CONFIG_NET_MAX_CONTEXTS=10 # MQTT Client @@ -82,10 +82,8 @@ CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # Miscellaneous CONFIG_BASE64=y CONFIG_MAIN_STACK_SIZE=8192 -CONFIG_NET_PKT_RX_COUNT=16 -CONFIG_NET_PKT_TX_COUNT=16 CONFIG_IDLE_STACK_SIZE=2048 CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 -CONFIG_HEAP_MEM_POOL_SIZE=131072 +CONFIG_HEAP_MEM_POOL_SIZE=65536 From 81d110f1904017722762a7e5d04d8b4229c6f7ba Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 15:10:19 +0300 Subject: [PATCH 34/50] Add LWT --- embed-proplet/src/main.c | 7 +++++- embed-proplet/src/mqtt_client.c | 39 ++++++++++++++++++++++++++++----- embed-proplet/src/mqtt_client.h | 20 ++++++++++++----- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index dfdc6be..a1807b0 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -16,6 +16,7 @@ void main(void) { LOG_INF("Starting Proplet..."); + // Initialize Wi-Fi wifi_manager_init(); if (wifi_manager_connect(WIFI_SSID, WIFI_PSK) != 0) { LOG_ERR("Wi-Fi connection failed"); @@ -29,18 +30,21 @@ void main(void) LOG_INF("Wi-Fi connected, proceeding with MQTT initialization"); - while (mqtt_client_connect() != 0) { + // Initialize MQTT client + while (mqtt_client_connect(PROPLET_ID, CHANNEL_ID) != 0) { LOG_ERR("MQTT client initialization failed. Retrying..."); k_sleep(K_SECONDS(5)); } LOG_INF("MQTT connected successfully."); + // Publish discovery message if (publish_discovery(PROPLET_ID, CHANNEL_ID) != 0) { LOG_ERR("Discovery announcement failed"); return; } + // Subscribe to topics if (subscribe(CHANNEL_ID) != 0) { LOG_ERR("Topic subscription failed"); return; @@ -48,6 +52,7 @@ void main(void) LOG_INF("Proplet ready"); + // Main loop while (1) { mqtt_client_process(); diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 6f09213..acb33e2 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -27,6 +27,10 @@ LOG_MODULE_REGISTER(mqtt_client); #define FETCH_REQUEST_TOPIC_TEMPLATE "channels/%s/messages/registry/proplet" #define RESULTS_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/results" +#define WILL_MESSAGE_TEMPLATE "{\"status\":\"offline\",\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}" +#define WILL_QOS MQTT_QOS_1_AT_LEAST_ONCE +#define WILL_RETAIN 1 + #define CLIENT_ID "proplet-esp32s3" /* Buffers for MQTT client */ @@ -268,7 +272,7 @@ void publish_registry_request(const char *channel_id, const char *app_name) publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, payload); } -int mqtt_client_connect(void) +int mqtt_client_connect(const char *proplet_id, const char *channel_id) { int ret; @@ -280,18 +284,43 @@ int mqtt_client_connect(void) LOG_ERR("Failed to resolve broker address, ret=%d", ret); return ret; } - + mqtt_client_init(&client_ctx); + + char will_topic_str[128]; + snprintf(will_topic_str, sizeof(will_topic_str), ALIVE_TOPIC_TEMPLATE, channel_id); + + char will_message_str[256]; + snprintf(will_message_str, sizeof(will_message_str), WILL_MESSAGE_TEMPLATE, proplet_id, channel_id); + + struct mqtt_utf8 will_message = { + .utf8 = (const uint8_t *)will_message_str, + .size = strlen(will_message_str), + }; + + struct mqtt_topic will_topic = { + .topic = { + .utf8 = (const uint8_t *)will_topic_str, + .size = strlen(will_topic_str), + }, + .qos = WILL_QOS, + }; + client_ctx.broker = &broker_addr; client_ctx.evt_cb = mqtt_event_handler; client_ctx.client_id.utf8 = CLIENT_ID; client_ctx.client_id.size = strlen(CLIENT_ID); client_ctx.protocol_version = MQTT_VERSION_3_1_1; client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; + client_ctx.rx_buf = rx_buffer; - client_ctx.rx_buf_size = sizeof(rx_buffer); + client_ctx.rx_buf_size = RX_BUFFER_SIZE; client_ctx.tx_buf = tx_buffer; - client_ctx.tx_buf_size = sizeof(tx_buffer); + client_ctx.tx_buf_size = TX_BUFFER_SIZE; + + client_ctx.will_topic = &will_topic; + client_ctx.will_message = &will_message; + client_ctx.will_retain = WILL_RETAIN; while (!mqtt_connected) { ret = mqtt_connect(&client_ctx); @@ -453,7 +482,7 @@ void handle_stop_command(const char *payload) { struct stop_command cmd; int ret; - ret = json_obj_parse(payload, strlen(payload), stop_command_descr, ARRAY_SIZE(stop_command_descr), &cmd); + ret = json_obj_parse((char *)payload, strlen(payload), stop_command_descr, ARRAY_SIZE(stop_command_descr), &cmd); if (ret < 0) { LOG_ERR("Failed to parse stop command payload, error: %d", ret); return; diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index 5bfb727..b7181d3 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -13,16 +13,17 @@ extern bool mqtt_connected; * * @return 0 on success, or a negative error code on failure. */ -int mqtt_client_connect(void); +int mqtt_client_connect(const char *proplet_id, const char *channel_id); /** - * @brief Publish a discovery announcement message to a specific topic. + * @brief Initialize and connect the MQTT client to the broker. * - * @param proplet_id The unique ID of the proplet. - * @param channel_id The ID of the channel for the announcement. + * @param proplet_id Unique ID of the proplet for the Last Will message. + * @param channel_id Channel ID for the Last Will topic. * @return 0 on success, or a negative error code on failure. */ -int publish_discovery(const char *proplet_id, const char *channel_id); +int mqtt_client_connect(const char *proplet_id, const char *channel_id); + /** * @brief Subscribe to topics for a specific channel. @@ -57,6 +58,15 @@ void publish_alive_message(const char *channel_id); */ void publish_registry_request(const char *channel_id, const char *app_name); +/** + * @brief Publish a discovery message when the Proplet comes online for the first time. + * + * @param proplet_id The unique ID of the proplet. + * @param channel_id The ID of the channel for the announcement. + * @return 0 on success, or a negative error code on failure. + */ +int publish_discovery(const char *proplet_id, const char *channel_id); + /** * @brief Publish the results of a completed task. * From 6843f425940077e5c5edc193452698149fb9564c Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 16:11:10 +0300 Subject: [PATCH 35/50] Remove duplicate logging --- embed-proplet/src/main.c | 37 +++++--------------------------- embed-proplet/src/mqtt_client.c | 24 ++++++++++++--------- embed-proplet/src/wifi_manager.c | 22 +++++++++++++------ embed-proplet/src/wifi_manager.h | 24 +++++++++++++++++++-- 4 files changed, 56 insertions(+), 51 deletions(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index a1807b0..9a252c3 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -16,43 +16,16 @@ void main(void) { LOG_INF("Starting Proplet..."); - // Initialize Wi-Fi - wifi_manager_init(); - if (wifi_manager_connect(WIFI_SSID, WIFI_PSK) != 0) { - LOG_ERR("Wi-Fi connection failed"); - return; - } - - if (!wifi_manager_wait_for_connection(K_SECONDS(60))) { - LOG_ERR("Wi-Fi connection timed out"); - return; - } - - LOG_INF("Wi-Fi connected, proceeding with MQTT initialization"); + wifi_manager_init(); - // Initialize MQTT client - while (mqtt_client_connect(PROPLET_ID, CHANNEL_ID) != 0) { - LOG_ERR("MQTT client initialization failed. Retrying..."); - k_sleep(K_SECONDS(5)); - } - - LOG_INF("MQTT connected successfully."); + wifi_manager_connect(WIFI_SSID, WIFI_PSK); - // Publish discovery message - if (publish_discovery(PROPLET_ID, CHANNEL_ID) != 0) { - LOG_ERR("Discovery announcement failed"); - return; - } + mqtt_client_connect(PROPLET_ID, CHANNEL_ID); - // Subscribe to topics - if (subscribe(CHANNEL_ID) != 0) { - LOG_ERR("Topic subscription failed"); - return; - } + publish_discovery(PROPLET_ID, CHANNEL_ID); - LOG_INF("Proplet ready"); + subscribe(CHANNEL_ID); - // Main loop while (1) { mqtt_client_process(); diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index acb33e2..ba76985 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -323,9 +323,11 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) client_ctx.will_retain = WILL_RETAIN; while (!mqtt_connected) { + LOG_INF("Attempting to connect to the MQTT broker..."); + ret = mqtt_connect(&client_ctx); if (ret != 0) { - LOG_ERR("MQTT connect failed [%d]. Retrying...", ret); + LOG_ERR("MQTT connect failed [%d]. Retrying in 5 seconds...", ret); k_sleep(K_SECONDS(5)); continue; } @@ -333,12 +335,14 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) /* Poll the socket for a response */ ret = poll_mqtt_socket(&client_ctx, 5000); if (ret < 0) { - LOG_ERR("Socket poll failed, ret=%d", ret); + LOG_ERR("Socket poll failed, ret=%d. Retrying in 5 seconds...", ret); mqtt_abort(&client_ctx); + k_sleep(K_SECONDS(5)); continue; } else if (ret == 0) { - LOG_ERR("Poll timed out waiting for CONNACK. Retrying..."); + LOG_ERR("Poll timed out waiting for CONNACK. Retrying in 5 seconds..."); mqtt_abort(&client_ctx); + k_sleep(K_SECONDS(5)); continue; } @@ -364,11 +368,8 @@ int publish_discovery(const char *proplet_id, const char *channel_id) "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", proplet_id, channel_id); - LOG_DBG("Topic: %s (Length: %zu)", topic, strlen(topic)); - LOG_DBG("Payload: %s (Length: %zu)", payload, strlen(payload)); - if (strlen(topic) >= sizeof(topic) || strlen(payload) >= sizeof(payload)) { - LOG_ERR("Topic or payload size exceeds the maximum allowable size."); + LOG_ERR("Discovery topic or payload size exceeds maximum allowable size."); return -EINVAL; } @@ -377,6 +378,8 @@ int publish_discovery(const char *proplet_id, const char *channel_id) return -ENOTCONN; } + LOG_INF("Publishing discovery announcement for Proplet ID: %s, Channel ID: %s", proplet_id, channel_id); + struct mqtt_publish_param param = { .message = { .topic = { @@ -446,17 +449,18 @@ int subscribe(const char *channel_id) .message_id = 1, }; + LOG_INF("Subscribing to topics for channel ID: %s", channel_id); + int ret = mqtt_subscribe(&client_ctx, &sub_list); if (ret != 0) { - LOG_ERR("Failed to subscribe to topics, ret=%d", ret); + LOG_ERR("Failed to subscribe to topics for channel ID: %s. Error code: %d", channel_id, ret); } else { - LOG_INF("Subscribed to topics successfully"); + LOG_INF("Successfully subscribed to topics for channel ID: %s", channel_id); } return ret; } - void handle_start_command(const char *payload) { struct start_command cmd; int ret; diff --git a/embed-proplet/src/wifi_manager.c b/embed-proplet/src/wifi_manager.c index 4d1a359..5dadc6c 100644 --- a/embed-proplet/src/wifi_manager.c +++ b/embed-proplet/src/wifi_manager.c @@ -40,6 +40,7 @@ static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt break; } default: + LOG_DBG("Unhandled Wi-Fi event: %u", mgmt_event); break; } } @@ -104,8 +105,20 @@ int wifi_manager_connect(const char *ssid, const char *psk) .band = WIFI_FREQ_BAND_2_4_GHZ, }; - LOG_INF("Connecting to SSID: %s", ssid); - return net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, ¶ms, sizeof(params)); + while (1) { + LOG_INF("Attempting to connect to Wi-Fi..."); + + int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, ¶ms, sizeof(params)); + if (ret == 0) { + LOG_DBG("Connection request sent, waiting for confirmation..."); + k_sem_take(&wifi_connected_sem, K_FOREVER); + LOG_INF("Successfully connected to Wi-Fi"); + return 0; + } + + LOG_ERR("Connection attempt failed (error: %d). Retrying in 5 seconds...", ret); + k_sleep(K_SECONDS(5)); + } } int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask) @@ -130,8 +143,3 @@ int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_add LOG_INF("Enabling AP mode with SSID: %s", ssid); return net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, ¶ms, sizeof(params)); } - -bool wifi_manager_wait_for_connection(k_timeout_t timeout) -{ - return k_sem_take(&wifi_connected_sem, timeout) == 0; -} diff --git a/embed-proplet/src/wifi_manager.h b/embed-proplet/src/wifi_manager.h index b229a4b..f6fac64 100644 --- a/embed-proplet/src/wifi_manager.h +++ b/embed-proplet/src/wifi_manager.h @@ -4,9 +4,29 @@ #include #include +/** + * @brief Initialize the Wi-Fi manager and set up event callbacks. + */ void wifi_manager_init(void); + +/** + * @brief Connect to a Wi-Fi network with the given SSID and PSK. + * + * @param ssid The SSID of the Wi-Fi network. + * @param psk The pre-shared key (PSK) for the Wi-Fi network. + * @return 0 on successful connection, or a negative error code on failure. + */ int wifi_manager_connect(const char *ssid, const char *psk); + +/** + * @brief Enable Access Point (AP) mode with the specified settings. + * + * @param ssid The SSID of the AP. + * @param psk The pre-shared key (PSK) for the AP (optional). + * @param ip_address The static IP address for the AP. + * @param netmask The netmask for the AP's network. + * @return 0 on success, or a negative error code on failure. + */ int wifi_manager_enable_ap(const char *ssid, const char *psk, const char *ip_address, const char *netmask); -bool wifi_manager_wait_for_connection(k_timeout_t timeout); // Wait for Wi-Fi connection -#endif +#endif /* WIFI_MANAGER_H */ From 0b3b381bba0d6d840c732c038b5dd110f28fa6fd Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 18:20:35 +0300 Subject: [PATCH 36/50] Remove binary chunk assembly logic --- embed-proplet/prj.conf | 11 +++--- embed-proplet/src/mqtt_client.c | 64 +++++++++------------------------ 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index ce43dc7..69a369c 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -54,12 +54,12 @@ CONFIG_JSON_LIBRARY=y # Logging CONFIG_LOG=y -CONFIG_NET_LOG=y -CONFIG_LOG_DEFAULT_LEVEL=3 -CONFIG_WIFI_LOG_LEVEL_DBG=y CONFIG_MQTT_LOG_LEVEL_DBG=y -CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y -CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=8192 +CONFIG_LOG_DEFAULT_LEVEL=3 +# Uncomment the following lines to enable debug logs for networking stack, Wi-Fi operations, and DHCPv4 server component. +# CONFIG_NET_LOG=n +# CONFIG_WIFI_LOG_LEVEL_DBG=n +# CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=n # Debugging and Early Boot CONFIG_EARLY_CONSOLE=y @@ -83,6 +83,7 @@ CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n CONFIG_BASE64=y CONFIG_MAIN_STACK_SIZE=8192 CONFIG_IDLE_STACK_SIZE=2048 +CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=4096 CONFIG_DYNAMIC_THREAD_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=4096 diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index ba76985..879780e 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -9,6 +9,7 @@ #include #include #include +#include "wasm_handler.h" LOG_MODULE_REGISTER(mqtt_client); @@ -542,65 +543,32 @@ void handle_registry_response(const char *payload) { LOG_INF("Registry response received:"); LOG_INF("App Name: %s", resp.app_name); - LOG_INF("Chunk Index: %d", resp.chunk_idx); - LOG_INF("Total Chunks: %d", resp.total_chunks); - - if (app_chunk_tracker == NULL) { - app_chunk_tracker = malloc(sizeof(struct chunk_tracker)); - if (!app_chunk_tracker) { - LOG_ERR("Failed to allocate memory for chunk tracker"); - return; - } - - app_chunk_tracker->total_chunks = resp.total_chunks; - app_chunk_tracker->received_chunks = 0; - - app_chunk_tracker->chunk_received = calloc(resp.total_chunks, sizeof(bool)); - app_chunk_tracker->file_data = malloc(resp.total_chunks * 256); // Assuming 256 bytes per chunk - if (!app_chunk_tracker->chunk_received || !app_chunk_tracker->file_data) { - LOG_ERR("Failed to allocate memory for chunk data"); - free(app_chunk_tracker->chunk_received); - free(app_chunk_tracker); - app_chunk_tracker = NULL; - return; - } - } - if (app_chunk_tracker->total_chunks != resp.total_chunks) { - LOG_ERR("Inconsistent total chunks value: %d != %d", app_chunk_tracker->total_chunks, resp.total_chunks); + size_t decoded_len = (strlen(resp.data) * 3) / 4; + uint8_t *binary_data = malloc(decoded_len); + if (!binary_data) { + LOG_ERR("Failed to allocate memory for decoded binary"); return; } - uint8_t binary_data[256]; - size_t binary_data_len = sizeof(binary_data); - - ret = base64_decode(binary_data, sizeof(binary_data), &binary_data_len, (const uint8_t *)resp.data, strlen(resp.data)); + size_t binary_data_len = decoded_len; + ret = base64_decode(binary_data, decoded_len, &binary_data_len, (const uint8_t *)resp.data, strlen(resp.data)); if (ret < 0) { - LOG_ERR("Failed to decode Base64 data for chunk %d", resp.chunk_idx); + LOG_ERR("Failed to decode Base64 data, error: %d", ret); + free(binary_data); return; } - memcpy(&app_chunk_tracker->file_data[resp.chunk_idx * binary_data_len], binary_data, binary_data_len); + LOG_INF("Successfully decoded Wasm binary. Executing now..."); - if (!app_chunk_tracker->chunk_received[resp.chunk_idx]) { - app_chunk_tracker->chunk_received[resp.chunk_idx] = true; - app_chunk_tracker->received_chunks++; + ret = execute_wasm_from_memory(binary_data, binary_data_len); + if (ret < 0) { + LOG_ERR("Failed to execute Wasm binary, error: %d", ret); + } else { + LOG_INF("Wasm binary executed successfully"); } - LOG_INF("Chunk %d/%d received for app: %s", app_chunk_tracker->received_chunks, app_chunk_tracker->total_chunks, resp.app_name); - - if (app_chunk_tracker->received_chunks == app_chunk_tracker->total_chunks) { - LOG_INF("All chunks received for app: %s. Binary is ready in memory.", resp.app_name); - - // Process the complete binary in `app_chunk_tracker->file_data` - - free(app_chunk_tracker->chunk_received); - free(app_chunk_tracker->file_data); - free(app_chunk_tracker); - app_chunk_tracker = NULL; - - LOG_INF("WASM binary assembled successfully for app: %s", resp.app_name); - } + free(binary_data); } void mqtt_client_process(void) From f0235b7a4ad0bed69ceabf1bfc62c46e9b327cbb Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Wed, 15 Jan 2025 18:26:05 +0300 Subject: [PATCH 37/50] Remove binary chunk assembly logic --- embed-proplet/src/wasm_handler.c | 22 ---------------------- embed-proplet/src/wasm_handler.h | 8 -------- 2 files changed, 30 deletions(-) diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 680a4fe..9d628d4 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -4,27 +4,6 @@ LOG_MODULE_REGISTER(wasm_handler); -static uint8_t wasm_file_buffer[8192]; -static size_t wasm_file_offset = 0; - -void handle_wasm_chunk(const uint8_t *data, size_t len) -{ - if (wasm_file_offset + len > sizeof(wasm_file_buffer)) { - LOG_ERR("Wasm file too large to fit in buffer."); - wasm_file_offset = 0; - return; - } - - memcpy(&wasm_file_buffer[wasm_file_offset], data, len); - wasm_file_offset += len; - - if (data[len - 1] == '\0') { - LOG_INF("All Wasm chunks received. Total size: %zu bytes. Executing Wasm file...", wasm_file_offset); - execute_wasm_from_memory(wasm_file_buffer, wasm_file_offset); - wasm_file_offset = 0; - } -} - void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size) { RuntimeInitArgs init_args = { .mem_alloc_type = Alloc_With_System_Allocator }; @@ -61,7 +40,6 @@ void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size) LOG_ERR("Function 'main' not found in Wasm module."); } - wasm_runtime_deinstantiate(module_inst); wasm_runtime_unload(module); wasm_runtime_destroy(); diff --git a/embed-proplet/src/wasm_handler.h b/embed-proplet/src/wasm_handler.h index 878f694..21020c4 100644 --- a/embed-proplet/src/wasm_handler.h +++ b/embed-proplet/src/wasm_handler.h @@ -3,14 +3,6 @@ #include -/** - * Handle a chunk of incoming Wasm file data. - * - * @param data Pointer to the data chunk. - * @param len Length of the data chunk. - */ -void handle_wasm_chunk(const uint8_t *data, size_t len); - /** * Execute a Wasm application from the provided memory buffer. * From 3889bd1d6cdc849c7d0ea18ef5a90c549d3a5f3f Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 16 Jan 2025 12:32:27 +0300 Subject: [PATCH 38/50] Fix runtime errors --- embed-proplet/src/mqtt_client.c | 22 +++++++++------------- embed-proplet/src/mqtt_client.h | 2 +- embed-proplet/src/wasm_handler.c | 2 +- embed-proplet/src/wasm_handler.h | 2 +- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 879780e..5cd2f03 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -88,8 +88,6 @@ static const struct json_obj_descr registry_response_descr[] = { JSON_OBJ_DESCR_PRIM(struct registry_response, data, JSON_TOK_STRING), }; -static struct chunk_tracker *app_chunk_tracker = NULL; - static void prepare_fds(struct mqtt_client *client) { if (client->transport.type == MQTT_TRANSPORT_NON_SECURE) { @@ -531,14 +529,14 @@ void publish_results(const char *channel_id, const char *task_id, const char *re } } -void handle_registry_response(const char *payload) { +int handle_registry_response(const char *payload) { struct registry_response resp; int ret; ret = json_obj_parse((char *)payload, strlen(payload), registry_response_descr, ARRAY_SIZE(registry_response_descr), &resp); if (ret < 0) { LOG_ERR("Failed to parse registry response, error: %d", ret); - return; + return -1; } LOG_INF("Registry response received:"); @@ -548,7 +546,7 @@ void handle_registry_response(const char *payload) { uint8_t *binary_data = malloc(decoded_len); if (!binary_data) { LOG_ERR("Failed to allocate memory for decoded binary"); - return; + return -1; } size_t binary_data_len = decoded_len; @@ -556,19 +554,17 @@ void handle_registry_response(const char *payload) { if (ret < 0) { LOG_ERR("Failed to decode Base64 data, error: %d", ret); free(binary_data); - return; + return -1; } LOG_INF("Successfully decoded Wasm binary. Executing now..."); - ret = execute_wasm_from_memory(binary_data, binary_data_len); - if (ret < 0) { - LOG_ERR("Failed to execute Wasm binary, error: %d", ret); - } else { - LOG_INF("Wasm binary executed successfully"); - } - + execute_wasm_module(binary_data, binary_data_len); free(binary_data); + + LOG_INF("Wasm binary executed"); + + return 0; } void mqtt_client_process(void) diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index b7181d3..3a52d82 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -104,6 +104,6 @@ void handle_stop_command(const char *payload); * * @param payload The JSON payload containing the chunk details. */ -void handle_registry_response(const char *payload); +int handle_registry_response(const char *payload); #endif /* MQTT_CLIENT_H */ diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 9d628d4..5854ca6 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -4,7 +4,7 @@ LOG_MODULE_REGISTER(wasm_handler); -void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size) +void execute_wasm_module(const uint8_t *wasm_data, size_t wasm_size) { RuntimeInitArgs init_args = { .mem_alloc_type = Alloc_With_System_Allocator }; if (!wasm_runtime_full_init(&init_args)) { diff --git a/embed-proplet/src/wasm_handler.h b/embed-proplet/src/wasm_handler.h index 21020c4..298de1e 100644 --- a/embed-proplet/src/wasm_handler.h +++ b/embed-proplet/src/wasm_handler.h @@ -9,6 +9,6 @@ * @param wasm_data Pointer to the Wasm file data in memory. * @param wasm_size Size of the Wasm file data in bytes. */ -void execute_wasm_from_memory(const uint8_t *wasm_data, size_t wasm_size); +void execute_wasm_module(const uint8_t *wasm_data, size_t wasm_size); #endif /* WASM_HANDLER_H */ From 896cf1c93e3ea0517367150de8ed68976c633adc Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 16 Jan 2025 12:40:58 +0300 Subject: [PATCH 39/50] Replace function_name with app_name --- embed-proplet/src/mqtt_client.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 5cd2f03..8f27263 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -49,7 +49,7 @@ static int nfds; bool mqtt_connected = false; struct start_command { char task_id[50]; - char function_name[50]; + char app_name[50]; char wasm_file[100]; }; @@ -73,7 +73,7 @@ struct chunk_tracker { static const struct json_obj_descr start_command_descr[] = { JSON_OBJ_DESCR_PRIM(struct start_command, task_id, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct start_command, function_name, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM(struct start_command, app_name, JSON_TOK_STRING), JSON_OBJ_DESCR_PRIM(struct start_command, wasm_file, JSON_TOK_STRING), }; @@ -473,12 +473,12 @@ void handle_start_command(const char *payload) { LOG_INF("Starting task:"); LOG_INF("Task ID: %s", cmd.task_id); - LOG_INF("Function: %s", cmd.function_name); + LOG_INF("Function: %s", cmd.app_name); LOG_INF("Wasm File: %s", cmd.wasm_file); // TODO: Use WAMR runtime to start the task // Example: - // wamr_start_app(cmd.wasm_file, cmd.function_name); + // wamr_start_app(cmd.wasm_file, cmd.app_name); } void handle_stop_command(const char *payload) { From b1698fa6351c7f34c377f69434bb3a486d5d563f Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 16 Jan 2025 18:56:06 +0300 Subject: [PATCH 40/50] Add wasm binary handlers --- embed-proplet/src/mqtt_client.c | 559 ++++++++++++++++--------------- embed-proplet/src/mqtt_client.h | 1 - embed-proplet/src/wasm_handler.c | 190 +++++++++-- embed-proplet/src/wasm_handler.h | 34 +- 4 files changed, 496 insertions(+), 288 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 8f27263..4e3cba2 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "wasm_handler.h" @@ -17,75 +16,98 @@ LOG_MODULE_REGISTER(mqtt_client); #define TX_BUFFER_SIZE 256 #define MQTT_BROKER_HOSTNAME "192.168.88.179" /* Replace with your broker's IP */ -#define MQTT_BROKER_PORT 1883 - -#define REGISTRY_ACK_TOPIC_TEMPLATE "channels/%s/messages/control/manager/registry" -#define ALIVE_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/alive" -#define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" -#define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" -#define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" -#define REGISTRY_RESPONSE_TOPIC "channels/%s/messages/registry/server" +#define MQTT_BROKER_PORT 1883 + +#define REGISTRY_ACK_TOPIC_TEMPLATE "channels/%s/messages/control/manager/registry" +#define ALIVE_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/alive" +#define DISCOVERY_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/create" +#define START_TOPIC_TEMPLATE "channels/%s/messages/control/manager/start" +#define STOP_TOPIC_TEMPLATE "channels/%s/messages/control/manager/stop" +#define REGISTRY_RESPONSE_TOPIC "channels/%s/messages/registry/server" #define FETCH_REQUEST_TOPIC_TEMPLATE "channels/%s/messages/registry/proplet" -#define RESULTS_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/results" +#define RESULTS_TOPIC_TEMPLATE "channels/%s/messages/control/proplet/results" #define WILL_MESSAGE_TEMPLATE "{\"status\":\"offline\",\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}" -#define WILL_QOS MQTT_QOS_1_AT_LEAST_ONCE -#define WILL_RETAIN 1 +#define WILL_QOS MQTT_QOS_1_AT_LEAST_ONCE +#define WILL_RETAIN 1 #define CLIENT_ID "proplet-esp32s3" -/* Buffers for MQTT client */ +#define MAX_ID_LEN 64 +#define MAX_NAME_LEN 64 +#define MAX_STATE_LEN 16 +#define MAX_URL_LEN 256 +#define MAX_TIMESTAMP_LEN 32 +#define MAX_BASE64_LEN 256 +#define MAX_INPUTS 16 +#define MAX_RESULTS 16 + +/* + * Keep the most recent "start" Task here, so if + * we fetch the WASM from the registry, we can call + * the WASM with the same inputs. + * + * If you support multiple tasks in parallel, you'll need + * a more robust approach than a single global. + */ +static struct task g_current_task; + static uint8_t rx_buffer[RX_BUFFER_SIZE]; static uint8_t tx_buffer[TX_BUFFER_SIZE]; -/* MQTT client context */ static struct mqtt_client client_ctx; static struct sockaddr_storage broker_addr; -/* Socket descriptor */ static struct zsock_pollfd fds[1]; static int nfds; bool mqtt_connected = false; -struct start_command { - char task_id[50]; - char app_name[50]; - char wasm_file[100]; -}; -struct stop_command { - char task_id[50]; +struct task { + char id[MAX_ID_LEN]; + char name[MAX_NAME_LEN]; + char state[MAX_STATE_LEN]; + char image_url[MAX_URL_LEN]; + + char file[MAX_BASE64_LEN]; + size_t file_len; + + uint64_t inputs[MAX_INPUTS]; + size_t inputs_count; + uint64_t results[MAX_RESULTS]; + size_t results_count; + + char start_time[MAX_TIMESTAMP_LEN]; + char finish_time[MAX_TIMESTAMP_LEN]; + char created_at[MAX_TIMESTAMP_LEN]; + char updated_at[MAX_TIMESTAMP_LEN]; }; struct registry_response { - char app_name[50]; - int chunk_idx; - int total_chunks; - char data[256]; + char app_name[64]; + char data[MAX_BASE64_LEN]; }; -struct chunk_tracker { - int total_chunks; - int received_chunks; - bool *chunk_received; - uint8_t *file_data; -}; +static const struct json_obj_descr task_descr[] = { + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "id", id, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "name", name, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "state", state, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "image_url", image_url, JSON_TOK_STRING), -static const struct json_obj_descr start_command_descr[] = { - JSON_OBJ_DESCR_PRIM(struct start_command, task_id, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct start_command, app_name, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct start_command, wasm_file, JSON_TOK_STRING), -}; + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "file", file, JSON_TOK_STRING), + + JSON_OBJ_DESCR_ARRAY_NAMED(struct task, "inputs", inputs, MAX_INPUTS, inputs_count, JSON_TOK_NUMBER), + JSON_OBJ_DESCR_ARRAY_NAMED(struct task, "results", results, MAX_RESULTS, results_count, JSON_TOK_NUMBER), -static const struct json_obj_descr stop_command_descr[] = { - JSON_OBJ_DESCR_PRIM(struct stop_command, task_id, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "start_time", start_time, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "finish_time", finish_time, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "created_at", created_at, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct task, "updated_at", updated_at, JSON_TOK_STRING), }; static const struct json_obj_descr registry_response_descr[] = { - JSON_OBJ_DESCR_PRIM(struct registry_response, app_name, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM(struct registry_response, chunk_idx, JSON_TOK_NUMBER), - JSON_OBJ_DESCR_PRIM(struct registry_response, total_chunks, JSON_TOK_NUMBER), - JSON_OBJ_DESCR_PRIM(struct registry_response, data, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct registry_response, "app_name", app_name, JSON_TOK_STRING), + JSON_OBJ_DESCR_PRIM_NAMED(struct registry_response, "data", data, JSON_TOK_STRING), }; static void prepare_fds(struct mqtt_client *client) @@ -110,7 +132,6 @@ static void clear_fds(void) static int poll_mqtt_socket(struct mqtt_client *client, int timeout) { prepare_fds(client); - if (nfds <= 0) { return -EINVAL; } @@ -119,7 +140,6 @@ static int poll_mqtt_socket(struct mqtt_client *client, int timeout) if (rc < 0) { LOG_ERR("Socket poll error [%d]", rc); } - return rc; } @@ -141,45 +161,49 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt LOG_INF("Disconnected from MQTT broker"); break; - case MQTT_EVT_PUBLISH: - { - const struct mqtt_publish_param *pub = &evt->param.publish; - char payload[PAYLOAD_BUFFER_SIZE]; - char start_topic[128]; - char stop_topic[128]; - char registry_response_topic[128]; - int ret; - - extern const char *channel_id; - - snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); - snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); - snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); - - LOG_INF("Message received on topic: %s", pub->message.topic.topic.utf8); - - ret = mqtt_read_publish_payload(&client_ctx, payload, MIN(pub->message.payload.len, PAYLOAD_BUFFER_SIZE - 1)); - if (ret < 0) { - LOG_ERR("Failed to read payload [%d]", ret); - return; - } - - // Null-terminate the payload - payload[ret] = '\0'; - - LOG_INF("Payload: %s", payload); - - if (strncmp(pub->message.topic.topic.utf8, start_topic, pub->message.topic.topic.size) == 0) { - handle_start_command(payload); - } else if (strncmp(pub->message.topic.topic.utf8, stop_topic, pub->message.topic.topic.size) == 0) { - handle_stop_command(payload); - } else if (strncmp(pub->message.topic.topic.utf8, registry_response_topic, pub->message.topic.topic.size) == 0) { - handle_registry_response(payload); - } else { - LOG_WRN("Unknown topic"); - } - break; + case MQTT_EVT_PUBLISH: { + const struct mqtt_publish_param *pub = &evt->param.publish; + char payload[PAYLOAD_BUFFER_SIZE]; + int ret; + + extern const char *channel_id; + + char start_topic[128]; + char stop_topic[128]; + char registry_response_topic[128]; + + snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); + snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); + + LOG_INF("Message received on topic: %s", pub->message.topic.topic.utf8); + + ret = mqtt_read_publish_payload( + &client_ctx, + payload, + MIN(pub->message.payload.len, PAYLOAD_BUFFER_SIZE - 1) + ); + if (ret < 0) { + LOG_ERR("Failed to read payload [%d]", ret); + return; } + payload[ret] = '\0'; /* Null-terminate */ + LOG_INF("Payload: %s", payload); + + if (strncmp(pub->message.topic.topic.utf8, start_topic, pub->message.topic.topic.size) == 0) { + handle_start_command(payload); + } + else if (strncmp(pub->message.topic.topic.utf8, stop_topic, pub->message.topic.topic.size) == 0) { + handle_stop_command(payload); + } + else if (strncmp(pub->message.topic.topic.utf8, registry_response_topic, pub->message.topic.topic.size) == 0) { + handle_registry_response(payload); + } + else { + LOG_WRN("Unknown topic"); + } + break; + } case MQTT_EVT_SUBACK: LOG_INF("Subscribed successfully"); @@ -192,19 +216,15 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt case MQTT_EVT_PUBREC: LOG_INF("QoS 2 publish received"); break; - case MQTT_EVT_PUBREL: LOG_INF("QoS 2 publish released"); break; - case MQTT_EVT_PUBCOMP: LOG_INF("QoS 2 publish complete"); break; - case MQTT_EVT_UNSUBACK: LOG_INF("Unsubscribed successfully"); break; - case MQTT_EVT_PINGRESP: LOG_INF("Ping response received from broker"); break; @@ -215,35 +235,36 @@ static void mqtt_event_handler(struct mqtt_client *client, const struct mqtt_evt } } -int publish(const char *channel_id, const char *topic_template, const char *payload) +static void prepare_publish_param(struct mqtt_publish_param *param, + const char *topic_str, + const char *payload) { - char topic[128]; + memset(param, 0, sizeof(*param)); - snprintf(topic, sizeof(topic), topic_template, channel_id); + param->message.topic.topic.utf8 = topic_str; + param->message.topic.topic.size = strlen(topic_str); + param->message.topic.qos = MQTT_QOS_1_AT_LEAST_ONCE; + + param->message.payload.data = (uint8_t *)payload; + param->message.payload.len = strlen(payload); + param->message_id = sys_rand32_get() & 0xFFFF; + param->dup_flag = 0; + param->retain_flag= 0; +} + +int publish(const char *channel_id, const char *topic_template, const char *payload) +{ if (!mqtt_connected) { - LOG_ERR("MQTT client is not connected. Cannot publish to topic: %s", topic); + LOG_ERR("MQTT client is not connected. Cannot publish."); return -ENOTCONN; } - struct mqtt_publish_param param = { - .message = { - .topic = { - .topic = { - .utf8 = topic, - .size = strlen(topic), - }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE, - }, - .payload = { - .data = (uint8_t *)payload, - .len = strlen(payload), - }, - }, - .message_id = sys_rand32_get() & 0xFFFF, - .dup_flag = 0, - .retain_flag = 0 - }; + char topic[128]; + snprintf(topic, sizeof(topic), topic_template, channel_id); + + struct mqtt_publish_param param; + prepare_publish_param(¶m, topic, payload); int ret = mqtt_publish(&client_ctx, ¶m); if (ret != 0) { @@ -255,29 +276,14 @@ int publish(const char *channel_id, const char *topic_template, const char *payl return 0; } -void publish_alive_message(const char *channel_id) -{ - char payload[128]; - snprintf(payload, sizeof(payload), - "{\"status\":\"alive\",\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", - CLIENT_ID, channel_id); - publish(channel_id, ALIVE_TOPIC_TEMPLATE, payload); -} - -void publish_registry_request(const char *channel_id, const char *app_name) -{ - char payload[128]; - snprintf(payload, sizeof(payload), "{\"app_name\":\"%s\"}", app_name); - publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, payload); -} - int mqtt_client_connect(const char *proplet_id, const char *channel_id) { int ret; - struct sockaddr_in *broker = (struct sockaddr_in *)&broker_addr; + broker->sin_family = AF_INET; - broker->sin_port = htons(MQTT_BROKER_PORT); + broker->sin_port = htons(MQTT_BROKER_PORT); + ret = net_addr_pton(AF_INET, MQTT_BROKER_HOSTNAME, &broker->sin_addr); if (ret != 0) { LOG_ERR("Failed to resolve broker address, ret=%d", ret); @@ -290,7 +296,8 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) snprintf(will_topic_str, sizeof(will_topic_str), ALIVE_TOPIC_TEMPLATE, channel_id); char will_message_str[256]; - snprintf(will_message_str, sizeof(will_message_str), WILL_MESSAGE_TEMPLATE, proplet_id, channel_id); + snprintf(will_message_str, sizeof(will_message_str), + WILL_MESSAGE_TEMPLATE, proplet_id, channel_id); struct mqtt_utf8 will_message = { .utf8 = (const uint8_t *)will_message_str, @@ -305,21 +312,21 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) .qos = WILL_QOS, }; - client_ctx.broker = &broker_addr; - client_ctx.evt_cb = mqtt_event_handler; - client_ctx.client_id.utf8 = CLIENT_ID; - client_ctx.client_id.size = strlen(CLIENT_ID); - client_ctx.protocol_version = MQTT_VERSION_3_1_1; - client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; + client_ctx.broker = &broker_addr; + client_ctx.evt_cb = mqtt_event_handler; + client_ctx.client_id.utf8 = CLIENT_ID; + client_ctx.client_id.size = strlen(CLIENT_ID); + client_ctx.protocol_version= MQTT_VERSION_3_1_1; + client_ctx.transport.type = MQTT_TRANSPORT_NON_SECURE; - client_ctx.rx_buf = rx_buffer; - client_ctx.rx_buf_size = RX_BUFFER_SIZE; - client_ctx.tx_buf = tx_buffer; - client_ctx.tx_buf_size = TX_BUFFER_SIZE; + client_ctx.rx_buf = rx_buffer; + client_ctx.rx_buf_size = RX_BUFFER_SIZE; + client_ctx.tx_buf = tx_buffer; + client_ctx.tx_buf_size = TX_BUFFER_SIZE; - client_ctx.will_topic = &will_topic; - client_ctx.will_message = &will_message; - client_ctx.will_retain = WILL_RETAIN; + client_ctx.will_topic = &will_topic; + client_ctx.will_message = &will_message; + client_ctx.will_retain = WILL_RETAIN; while (!mqtt_connected) { LOG_INF("Attempting to connect to the MQTT broker..."); @@ -338,7 +345,8 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) mqtt_abort(&client_ctx); k_sleep(K_SECONDS(5)); continue; - } else if (ret == 0) { + } + else if (ret == 0) { LOG_ERR("Poll timed out waiting for CONNACK. Retrying in 5 seconds..."); mqtt_abort(&client_ctx); k_sleep(K_SECONDS(5)); @@ -357,66 +365,15 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id) return 0; } -int publish_discovery(const char *proplet_id, const char *channel_id) -{ - char topic[128]; - char payload[128]; - - snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); - snprintf(payload, sizeof(payload), - "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", - proplet_id, channel_id); - - if (strlen(topic) >= sizeof(topic) || strlen(payload) >= sizeof(payload)) { - LOG_ERR("Discovery topic or payload size exceeds maximum allowable size."); - return -EINVAL; - } - - if (!mqtt_connected) { - LOG_ERR("MQTT client is not connected. Discovery announcement aborted."); - return -ENOTCONN; - } - - LOG_INF("Publishing discovery announcement for Proplet ID: %s, Channel ID: %s", proplet_id, channel_id); - - struct mqtt_publish_param param = { - .message = { - .topic = { - .topic = { - .utf8 = (uint8_t *)topic, - .size = strlen(topic), - }, - .qos = MQTT_QOS_1_AT_LEAST_ONCE, - }, - .payload = { - .data = (uint8_t *)payload, - .len = strlen(payload), - }, - }, - .message_id = sys_rand32_get() & 0xFFFF, - .dup_flag = 0, - .retain_flag = 0 - }; - - int ret = mqtt_publish(&client_ctx, ¶m); - if (ret != 0) { - LOG_ERR("Failed to publish discovery announcement. Error code: %d", ret); - return ret; - } - - LOG_INF("Discovery announcement published successfully to topic: %s", topic); - return 0; -} - int subscribe(const char *channel_id) { char start_topic[128]; char stop_topic[128]; char registry_response_topic[128]; - snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); - snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); - snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); + snprintf(start_topic, sizeof(start_topic), START_TOPIC_TEMPLATE, channel_id); + snprintf(stop_topic, sizeof(stop_topic), STOP_TOPIC_TEMPLATE, channel_id); + snprintf(registry_response_topic, sizeof(registry_response_topic), REGISTRY_RESPONSE_TOPIC, channel_id); struct mqtt_topic topics[] = { { @@ -443,7 +400,7 @@ int subscribe(const char *channel_id) }; struct mqtt_subscription_list sub_list = { - .list = topics, + .list = topics, .list_count = ARRAY_SIZE(topics), .message_id = 1, }; @@ -452,7 +409,7 @@ int subscribe(const char *channel_id) int ret = mqtt_subscribe(&client_ctx, &sub_list); if (ret != 0) { - LOG_ERR("Failed to subscribe to topics for channel ID: %s. Error code: %d", channel_id, ret); + LOG_ERR("Failed to subscribe to topics for channel ID: %s. Error: %d", channel_id, ret); } else { LOG_INF("Successfully subscribed to topics for channel ID: %s", channel_id); } @@ -460,113 +417,193 @@ int subscribe(const char *channel_id) return ret; } -void handle_start_command(const char *payload) { - struct start_command cmd; - int ret; - - ret = json_obj_parse((char *)payload, strlen(payload), start_command_descr, ARRAY_SIZE(start_command_descr), &cmd); +void handle_start_command(const char *payload) +{ + struct task t; + memset(&t, 0, sizeof(t)); + int ret = json_obj_parse(payload, strlen(payload), + task_descr, + ARRAY_SIZE(task_descr), + &t); if (ret < 0) { - LOG_ERR("Failed to parse start command payload, error: %d", ret); + LOG_ERR("Failed to parse START task payload, error: %d", ret); return; } - LOG_INF("Starting task:"); - LOG_INF("Task ID: %s", cmd.task_id); - LOG_INF("Function: %s", cmd.app_name); - LOG_INF("Wasm File: %s", cmd.wasm_file); + LOG_INF("Starting task: ID=%s, Name=%s, State=%s", t.id, t.name, t.state); + LOG_INF("image_url=%s, file-len(b64)=%zu", t.image_url, strlen(t.file)); + LOG_INF("inputs_count=%zu", t.inputs_count); - // TODO: Use WAMR runtime to start the task - // Example: - // wamr_start_app(cmd.wasm_file, cmd.app_name); -} + memcpy(&g_current_task, &t, sizeof(struct task)); -void handle_stop_command(const char *payload) { - struct stop_command cmd; - int ret; + if (strlen(t.file) > 0) { + static uint8_t wasm_binary[MAX_BASE64_LEN]; + size_t wasm_decoded_len = 0; - ret = json_obj_parse((char *)payload, strlen(payload), stop_command_descr, ARRAY_SIZE(stop_command_descr), &cmd); - if (ret < 0) { - LOG_ERR("Failed to parse stop command payload, error: %d", ret); - return; + ret = base64_decode(wasm_binary, sizeof(wasm_binary), + &wasm_decoded_len, + (const uint8_t *)t.file, + strlen(t.file)); + if (ret < 0) { + LOG_ERR("Failed to decode base64 WASM (task.file). Err=%d", ret); + return; + } + g_current_task.file_len = wasm_decoded_len; + LOG_INF("Decoded WASM size: %zu", g_current_task.file_len); + + execute_wasm_module(g_current_task.id, + wasm_binary, + g_current_task.file_len, + g_current_task.inputs, + g_current_task.inputs_count); } - LOG_INF("Stopping task:"); - LOG_INF("Task ID: %s", cmd.task_id); - - // TODO: Use WAMR runtime to stop the task - // Example: - // wamr_stop_app(cmd.task_id); -} - -void request_registry_file(const char *channel_id, const char *app_name) -{ - char registry_payload[128]; - - snprintf(registry_payload, sizeof(registry_payload), - "{\"app_name\":\"%s\"}", - app_name); - - if (publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, registry_payload) != 0) { - LOG_ERR("Failed to request registry file"); - } else { - LOG_INF("Requested registry file for app: %s", app_name); + else if (strlen(t.image_url) > 0) { + LOG_INF("Requesting WASM from registry: %s", t.image_url); + extern const char *channel_id; + publish_registry_request(channel_id, t.image_url); + } + else { + LOG_WRN("No file or image_url specified; cannot start WASM task!"); } } -void publish_results(const char *channel_id, const char *task_id, const char *results) +void handle_stop_command(const char *payload) { - char results_payload[256]; - - snprintf(results_payload, sizeof(results_payload), - "{\"task_id\":\"%s\",\"results\":\"%s\"}", - task_id, results); + struct task t; + memset(&t, 0, sizeof(t)); - if (publish(channel_id, RESULTS_TOPIC_TEMPLATE, results_payload) != 0) { - LOG_ERR("Failed to publish results"); - } else { - LOG_INF("Published results for task: %s", task_id); + int ret = json_obj_parse(payload, strlen(payload), + task_descr, + ARRAY_SIZE(task_descr), + &t); + if (ret < 0) { + LOG_ERR("Failed to parse STOP task payload, error: %d", ret); + return; } + + LOG_INF("Stopping task: ID=%s, Name=%s, State=%s", t.id, t.name, t.state); + stop_wasm_app(t.id); } -int handle_registry_response(const char *payload) { +/** + * We receive a single chunk "data" field with the full base64 WASM. + */ +int handle_registry_response(const char *payload) +{ struct registry_response resp; - int ret; + memset(&resp, 0, sizeof(resp)); - ret = json_obj_parse((char *)payload, strlen(payload), registry_response_descr, ARRAY_SIZE(registry_response_descr), &resp); + int ret = json_obj_parse(payload, strlen(payload), + registry_response_descr, + ARRAY_SIZE(registry_response_descr), + &resp); if (ret < 0) { LOG_ERR("Failed to parse registry response, error: %d", ret); return -1; } - LOG_INF("Registry response received:"); - LOG_INF("App Name: %s", resp.app_name); + LOG_INF("Single-chunk registry response for app: %s", resp.app_name); - size_t decoded_len = (strlen(resp.data) * 3) / 4; + size_t encoded_len = strlen(resp.data); + size_t decoded_len = (encoded_len * 3) / 4; uint8_t *binary_data = malloc(decoded_len); if (!binary_data) { LOG_ERR("Failed to allocate memory for decoded binary"); return -1; } - size_t binary_data_len = decoded_len; - ret = base64_decode(binary_data, decoded_len, &binary_data_len, (const uint8_t *)resp.data, strlen(resp.data)); + size_t actual_decoded_len = decoded_len; + ret = base64_decode(binary_data, + decoded_len, + &actual_decoded_len, + (const uint8_t *)resp.data, + encoded_len); if (ret < 0) { - LOG_ERR("Failed to decode Base64 data, error: %d", ret); + LOG_ERR("Failed to decode base64 single-chunk, err=%d", ret); free(binary_data); return -1; } - LOG_INF("Successfully decoded Wasm binary. Executing now..."); + LOG_INF("Decoded single-chunk WASM size: %zu. Executing now...", actual_decoded_len); + + execute_wasm_module(g_current_task.id, + binary_data, + actual_decoded_len, + g_current_task.inputs, + g_current_task.inputs_count); - execute_wasm_module(binary_data, binary_data_len); free(binary_data); - LOG_INF("Wasm binary executed"); + LOG_INF("WASM binary executed from single-chunk registry response."); + return 0; +} +void publish_alive_message(const char *channel_id) +{ + char payload[128]; + snprintf(payload, sizeof(payload), + "{\"status\":\"alive\",\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", + CLIENT_ID, channel_id); + publish(channel_id, ALIVE_TOPIC_TEMPLATE, payload); +} + +int publish_discovery(const char *proplet_id, const char *channel_id) +{ + char topic[128]; + char payload[128]; + + snprintf(topic, sizeof(topic), DISCOVERY_TOPIC_TEMPLATE, channel_id); + snprintf(payload, sizeof(payload), + "{\"proplet_id\":\"%s\",\"mg_channel_id\":\"%s\"}", + proplet_id, channel_id); + + if (!mqtt_connected) { + LOG_ERR("MQTT client is not connected. Discovery aborted."); + return -ENOTCONN; + } + + struct mqtt_publish_param param; + prepare_publish_param(¶m, topic, payload); + + int ret = mqtt_publish(&client_ctx, ¶m); + if (ret != 0) { + LOG_ERR("Failed to publish discovery. Error: %d", ret); + return ret; + } + + LOG_INF("Discovery published successfully to topic: %s", topic); return 0; } +void publish_registry_request(const char *channel_id, const char *app_name) +{ + char payload[128]; + snprintf(payload, sizeof(payload), "{\"app_name\":\"%s\"}", app_name); + + if (publish(channel_id, FETCH_REQUEST_TOPIC_TEMPLATE, payload) != 0) { + LOG_ERR("Failed to request registry file"); + } else { + LOG_INF("Requested registry file for: %s", app_name); + } +} + +void publish_results(const char *channel_id, const char *task_id, const char *results) +{ + char results_payload[256]; + + snprintf(results_payload, sizeof(results_payload), + "{\"task_id\":\"%s\",\"results\":\"%s\"}", + task_id, results); + + if (publish(channel_id, RESULTS_TOPIC_TEMPLATE, results_payload) != 0) { + LOG_ERR("Failed to publish results"); + } else { + LOG_INF("Published results for task: %s", task_id); + } +} + void mqtt_client_process(void) { if (mqtt_connected) { diff --git a/embed-proplet/src/mqtt_client.h b/embed-proplet/src/mqtt_client.h index 3a52d82..375836e 100644 --- a/embed-proplet/src/mqtt_client.h +++ b/embed-proplet/src/mqtt_client.h @@ -24,7 +24,6 @@ int mqtt_client_connect(const char *proplet_id, const char *channel_id); */ int mqtt_client_connect(const char *proplet_id, const char *channel_id); - /** * @brief Subscribe to topics for a specific channel. * diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 5854ca6..8a35599 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -1,46 +1,194 @@ #include "wasm_handler.h" #include #include +#include +#include LOG_MODULE_REGISTER(wasm_handler); -void execute_wasm_module(const uint8_t *wasm_data, size_t wasm_size) +#define MAX_WASM_APPS 10 /* How many different Wasm tasks we can track simultaneously */ +#define MAX_ID_LEN 64 +#define MAX_INPUTS 16 /* Max 32-bit arguments we’ll pass to WASM main */ + +/* A record of a "running" (loaded & instantiated) Wasm module. */ +typedef struct { + bool in_use; + char id[MAX_ID_LEN]; + wasm_module_t module; + wasm_module_inst_t module_inst; +} wasm_app_t; + +/* We'll maintain a small global array of possible Wasm apps. */ +static wasm_app_t g_wasm_apps[MAX_WASM_APPS]; + +/* Keep track of whether we've called wasm_runtime_full_init() yet. */ +static bool g_wamr_initialized = false; + +/* Forward declarations for some helper functions. */ +static void maybe_init_wamr_runtime(void); +static int find_free_slot(void); +static int find_app_by_id(const char *task_id); + +/*-------------------------------------------------------------------------*/ +/* Public API: execute_wasm_module(...) */ +/*-------------------------------------------------------------------------*/ +void execute_wasm_module(const char *task_id, + const uint8_t *wasm_data, + size_t wasm_size, + const uint64_t *inputs, + size_t inputs_count) { - RuntimeInitArgs init_args = { .mem_alloc_type = Alloc_With_System_Allocator }; - if (!wasm_runtime_full_init(&init_args)) { - LOG_ERR("Failed to initialize WAMR runtime."); + /* Make sure the WAMR runtime is initialized once. */ + maybe_init_wamr_runtime(); + if (!g_wamr_initialized) { + LOG_ERR("WAMR runtime not available, cannot execute WASM"); return; } + /* If a Wasm app with this ID is already running, stop it first. */ + int existing_idx = find_app_by_id(task_id); + if (existing_idx >= 0) { + LOG_WRN("WASM app with ID %s is already running. Stopping it first...", task_id); + stop_wasm_app(task_id); + } + + /* Find a free slot in the global array. */ + int slot = find_free_slot(); + if (slot < 0) { + LOG_ERR("No free slot to store new WASM app instance (increase MAX_WASM_APPS)."); + return; + } + + /* Load the module from memory. */ char error_buf[128]; - wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, error_buf, sizeof(error_buf)); + wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, + error_buf, sizeof(error_buf)); if (!module) { - LOG_ERR("Failed to load Wasm module: %s", error_buf); - wasm_runtime_destroy(); + LOG_ERR("Failed to load WASM module: %s", error_buf); return; } - wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, 1024, 1024, error_buf, sizeof(error_buf)); + /* Instantiate the module. Increase stack/heap if needed. */ + wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, + 4 * 1024, /* stack size */ + 4 * 1024, /* heap size */ + error_buf, + sizeof(error_buf)); if (!module_inst) { - LOG_ERR("Failed to instantiate Wasm module: %s", error_buf); + LOG_ERR("Failed to instantiate WASM module: %s", error_buf); wasm_runtime_unload(module); - wasm_runtime_destroy(); return; } + /* Store references in the global array, so we can stop it later. */ + g_wasm_apps[slot].in_use = true; + strncpy(g_wasm_apps[slot].id, task_id, MAX_ID_LEN - 1); + g_wasm_apps[slot].id[MAX_ID_LEN - 1] = '\0'; + g_wasm_apps[slot].module = module; + g_wasm_apps[slot].module_inst = module_inst; + + /* + * Optionally call "main" right away. If your Wasm module is meant to run + * a single function and then exit, this is enough. If it's a long-running + * module (e.g., with timers or state), you'd skip the immediate call. + */ wasm_function_inst_t func = wasm_runtime_lookup_function(module_inst, "main"); - if (func) { - LOG_INF("Executing Wasm application..."); - if (!wasm_runtime_call_wasm(module_inst, func, 0, NULL)) { - LOG_ERR("Error invoking Wasm function."); - } else { - LOG_INF("Wasm application executed successfully."); - } + if (!func) { + LOG_WRN("Function 'main' not found in WASM module. No entry point to call."); + return; + } + + LOG_INF("Executing 'main' in WASM module with ID=%s", task_id); + + /* Convert 64-bit inputs to 32-bit if your "main" expects 32-bit args. */ + uint32_t arg_buf[MAX_INPUTS]; + memset(arg_buf, 0, sizeof(arg_buf)); + size_t n_args = (inputs_count > MAX_INPUTS) ? MAX_INPUTS : inputs_count; + for (size_t i = 0; i < n_args; i++) { + arg_buf[i] = (uint32_t)(inputs[i] & 0xFFFFFFFFu); + } + + if (!wasm_runtime_call_wasm(module_inst, func, n_args, arg_buf)) { + LOG_ERR("Error invoking WASM function 'main'"); } else { - LOG_ERR("Function 'main' not found in Wasm module."); + LOG_INF("WASM 'main' executed successfully."); + } + + /* + * NOTE: We do NOT call wasm_runtime_deinstantiate() or wasm_runtime_unload() + * here, so the module remains loaded. That’s what allows stop_wasm_app() + * to do meaningful cleanup later. + */ +} + +/*-------------------------------------------------------------------------*/ +/* Public API: stop_wasm_app(...) */ +/*-------------------------------------------------------------------------*/ +void stop_wasm_app(const char *task_id) +{ + int idx = find_app_by_id(task_id); + if (idx < 0) { + LOG_WRN("No running WASM app found with ID=%s", task_id); + return; } - wasm_runtime_deinstantiate(module_inst); - wasm_runtime_unload(module); - wasm_runtime_destroy(); + wasm_app_t *app = &g_wasm_apps[idx]; + LOG_INF("Stopping WASM app with ID=%s", app->id); + + /* Properly deinstantiate and unload the module. */ + wasm_runtime_deinstantiate(app->module_inst); + wasm_runtime_unload(app->module); + + /* Clear our record for re-use. */ + app->in_use = false; + memset(app->id, 0, sizeof(app->id)); + + LOG_INF("WASM app [%s] has been stopped and unloaded.", task_id); +} + +/*-------------------------------------------------------------------------*/ +/* Internal Helpers */ +/*-------------------------------------------------------------------------*/ + +/** One-time WAMR runtime initialization. */ +static void maybe_init_wamr_runtime(void) +{ + if (g_wamr_initialized) { + return; /* Already inited */ + } + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(init_args)); + init_args.mem_alloc_type = Alloc_With_System_Allocator; + + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Failed to initialize WAMR runtime."); + return; + } + + g_wamr_initialized = true; + LOG_INF("WAMR runtime initialized successfully."); +} + +/** Finds the first free slot in the global g_wasm_apps array, or -1 if full. */ +static int find_free_slot(void) +{ + for (int i = 0; i < MAX_WASM_APPS; i++) { + if (!g_wasm_apps[i].in_use) { + return i; + } + } + return -1; +} + +/** Looks up a loaded module by task ID. Returns array index or -1 if not found. */ +static int find_app_by_id(const char *task_id) +{ + for (int i = 0; i < MAX_WASM_APPS; i++) { + if (g_wasm_apps[i].in_use && + (strcmp(g_wasm_apps[i].id, task_id) == 0)) { + return i; + } + } + return -1; } diff --git a/embed-proplet/src/wasm_handler.h b/embed-proplet/src/wasm_handler.h index 298de1e..b453dee 100644 --- a/embed-proplet/src/wasm_handler.h +++ b/embed-proplet/src/wasm_handler.h @@ -1,14 +1,38 @@ #ifndef WASM_HANDLER_H #define WASM_HANDLER_H -#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Loads and instantiates a Wasm module, then invokes its "main" function (if present). + * The module remains loaded in memory so that it can be stopped later by ID. + * + * @param task_id Unique identifier (string) for this Wasm "task." + * @param wasm_data Pointer to the Wasm file data in memory. + * @param wasm_size Size of the Wasm file data in bytes. + * @param inputs Array of 64-bit inputs that the Wasm main function might consume. + * @param inputs_count Number of elements in the 'inputs' array. + */ +void execute_wasm_module(const char *task_id, + const uint8_t *wasm_data, + size_t wasm_size, + const uint64_t *inputs, + size_t inputs_count); /** - * Execute a Wasm application from the provided memory buffer. + * Stops the Wasm module with the given task_id by deinstantiating and unloading it from memory. * - * @param wasm_data Pointer to the Wasm file data in memory. - * @param wasm_size Size of the Wasm file data in bytes. + * @param task_id The unique string ID assigned to the Wasm module at start time. */ -void execute_wasm_module(const uint8_t *wasm_data, size_t wasm_size); +void stop_wasm_app(const char *task_id); + +#ifdef __cplusplus +} +#endif #endif /* WASM_HANDLER_H */ From 8231e9e32256517c9ce6b53948293897aebfcf61 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Thu, 16 Jan 2025 20:12:31 +0300 Subject: [PATCH 41/50] increase MQTT processing frequency --- embed-proplet/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embed-proplet/src/main.c b/embed-proplet/src/main.c index 9a252c3..0fd1755 100644 --- a/embed-proplet/src/main.c +++ b/embed-proplet/src/main.c @@ -35,6 +35,6 @@ void main(void) LOG_WRN("MQTT client is not connected"); } - k_sleep(K_SECONDS(30)); + k_sleep(K_SECONDS(1)); } } From 9a3fc5117efec1e341421a6ddf320982d93c922a Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 10:27:41 +0300 Subject: [PATCH 42/50] Remove extra fields --- embed-proplet/src/mqtt_client.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 4e3cba2..6ba3231 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(mqtt_client); #define RX_BUFFER_SIZE 256 #define TX_BUFFER_SIZE 256 -#define MQTT_BROKER_HOSTNAME "192.168.88.179" /* Replace with your broker's IP */ +#define MQTT_BROKER_HOSTNAME "172.20.10.4" /* Replace with your broker's IP */ #define MQTT_BROKER_PORT 1883 #define REGISTRY_ACK_TOPIC_TEMPLATE "channels/%s/messages/control/manager/registry" @@ -74,13 +74,6 @@ struct task { uint64_t inputs[MAX_INPUTS]; size_t inputs_count; - uint64_t results[MAX_RESULTS]; - size_t results_count; - - char start_time[MAX_TIMESTAMP_LEN]; - char finish_time[MAX_TIMESTAMP_LEN]; - char created_at[MAX_TIMESTAMP_LEN]; - char updated_at[MAX_TIMESTAMP_LEN]; }; struct registry_response { @@ -97,12 +90,6 @@ static const struct json_obj_descr task_descr[] = { JSON_OBJ_DESCR_PRIM_NAMED(struct task, "file", file, JSON_TOK_STRING), JSON_OBJ_DESCR_ARRAY_NAMED(struct task, "inputs", inputs, MAX_INPUTS, inputs_count, JSON_TOK_NUMBER), - JSON_OBJ_DESCR_ARRAY_NAMED(struct task, "results", results, MAX_RESULTS, results_count, JSON_TOK_NUMBER), - - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "start_time", start_time, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "finish_time", finish_time, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "created_at", created_at, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "updated_at", updated_at, JSON_TOK_STRING), }; static const struct json_obj_descr registry_response_descr[] = { From 363b921ffa5be3d8c19060bd3b751cbc6f94b099 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 10:38:56 +0300 Subject: [PATCH 43/50] Fix comments --- embed-proplet/CMakeLists.txt | 2 +- embed-proplet/prj.conf | 17 +++++++++-------- embed-proplet/src/mqtt_client.c | 11 +++++++++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index 46d0032..7872e40 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -49,6 +49,6 @@ zephyr_library_sources(${WAMR_RUNTIME_LIB_SOURCE}) zephyr_library_app_memory(wamr_partition) -target_sources(app PRIVATE src/main.c src/mqtt_client.c src/wifi_manager.c src/wasm_handler.c) +target_sources(app PRIVATE src/main.c src/mqtt_client.c src/wasm_handler.c src/wifi_manager.c) target_link_libraries(app PRIVATE wamr_lib) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 69a369c..24f3c05 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -14,13 +14,14 @@ CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 CONFIG_NET_DHCPV4=y CONFIG_NET_DHCPV4_SERVER=y CONFIG_NET_IPV4=y -CONFIG_NET_IPV6=n +# Uncomment to enable IPv6 +# CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_UDP=y # Interface Configuration CONFIG_NET_IF_MAX_IPV4_COUNT=2 -# required if NET_IPV6 is set +# required only if NET_IPV6 is set # CONFIG_NET_IF_MAX_IPV6_COUNT=2 CONFIG_NET_L2_ETHERNET=y CONFIG_NET_L2_WIFI_MGMT=y @@ -49,17 +50,17 @@ CONFIG_MQTT_LIB_WEBSOCKET=n # Uncomment to enable ALPN protocol for socket MQTT Library. # CONFIG_MQTT_LIB_TLS_USE_ALPN=y -# Enable JSON +# JSON Support CONFIG_JSON_LIBRARY=y # Logging CONFIG_LOG=y -CONFIG_MQTT_LOG_LEVEL_DBG=y CONFIG_LOG_DEFAULT_LEVEL=3 -# Uncomment the following lines to enable debug logs for networking stack, Wi-Fi operations, and DHCPv4 server component. -# CONFIG_NET_LOG=n -# CONFIG_WIFI_LOG_LEVEL_DBG=n -# CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=n +# Uncomment the following lines to enable debug logs for mqtt, networking stack, Wi-Fi operations, and DHCPv4 server component. +# CONFIG_MQTT_LOG_LEVEL_DBG=y +# CONFIG_NET_LOG=y +# CONFIG_WIFI_LOG_LEVEL_DBG=y +# CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL_DBG=y # Debugging and Early Boot CONFIG_EARLY_CONSOLE=y diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index 6ba3231..aec09e8 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -15,7 +15,7 @@ LOG_MODULE_REGISTER(mqtt_client); #define RX_BUFFER_SIZE 256 #define TX_BUFFER_SIZE 256 -#define MQTT_BROKER_HOSTNAME "172.20.10.4" /* Replace with your broker's IP */ +#define MQTT_BROKER_HOSTNAME "192.168.88.179" /* Replace with your broker's IP */ #define MQTT_BROKER_PORT 1883 #define REGISTRY_ACK_TOPIC_TEMPLATE "channels/%s/messages/control/manager/registry" @@ -38,7 +38,7 @@ LOG_MODULE_REGISTER(mqtt_client); #define MAX_STATE_LEN 16 #define MAX_URL_LEN 256 #define MAX_TIMESTAMP_LEN 32 -#define MAX_BASE64_LEN 256 +#define MAX_BASE64_LEN 512 #define MAX_INPUTS 16 #define MAX_RESULTS 16 @@ -74,6 +74,13 @@ struct task { uint64_t inputs[MAX_INPUTS]; size_t inputs_count; + uint64_t results[MAX_RESULTS]; + size_t results_count; + + char start_time[MAX_TIMESTAMP_LEN]; + char finish_time[MAX_TIMESTAMP_LEN]; + char created_at[MAX_TIMESTAMP_LEN]; + char updated_at[MAX_TIMESTAMP_LEN]; }; struct registry_response { From 62579b965bbe1a836b2b4bdac3c4a42f0c01e3f6 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 12:22:15 +0300 Subject: [PATCH 44/50] encode decoded json for validation --- embed-proplet/src/mqtt_client.c | 47 ++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index aec09e8..a1a51d4 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -425,26 +425,52 @@ void handle_start_command(const char *payload) return; } - LOG_INF("Starting task: ID=%s, Name=%s, State=%s", t.id, t.name, t.state); - LOG_INF("image_url=%s, file-len(b64)=%zu", t.image_url, strlen(t.file)); - LOG_INF("inputs_count=%zu", t.inputs_count); + char encoded_json[1024]; // Adjust size based on expected payload length + ret = json_obj_encode_buf(task_descr, ARRAY_SIZE(task_descr), &t, encoded_json, sizeof(encoded_json)); + if (ret < 0) { + LOG_ERR("Failed to encode struct to JSON, error: %d", ret); + } else { + LOG_INF("Encoded JSON: %s", encoded_json); + } - memcpy(&g_current_task, &t, sizeof(struct task)); + if (strlen(t.id) == 0 || strlen(t.name) == 0 || strlen(t.state) == 0) { + LOG_ERR("Parsed task contains invalid or missing mandatory fields."); + return; + } + + t.id[MAX_ID_LEN - 1] = '\0'; + t.name[MAX_NAME_LEN - 1] = '\0'; + t.state[MAX_STATE_LEN - 1] = '\0'; + + LOG_INF("Starting task: ID=%.63s, Name=%.63s, State=%.15s", t.id, t.name, t.state); if (strlen(t.file) > 0) { + size_t required_size = 0; + + // Calculate required buffer size + ret = base64_decode(NULL, 0, &required_size, (const uint8_t *)t.file, strlen(t.file)); + if (ret < 0) { + LOG_ERR("Failed to calculate buffer size for base64 decode, err=%d", ret); + return; + } + + if (required_size > MAX_BASE64_LEN) { + LOG_ERR("Decoded size exceeds buffer capacity"); + return; + } + static uint8_t wasm_binary[MAX_BASE64_LEN]; size_t wasm_decoded_len = 0; - ret = base64_decode(wasm_binary, sizeof(wasm_binary), - &wasm_decoded_len, - (const uint8_t *)t.file, - strlen(t.file)); + // Perform actual decoding + ret = base64_decode(wasm_binary, sizeof(wasm_binary), &wasm_decoded_len, + (const uint8_t *)t.file, strlen(t.file)); if (ret < 0) { LOG_ERR("Failed to decode base64 WASM (task.file). Err=%d", ret); return; } + g_current_task.file_len = wasm_decoded_len; - LOG_INF("Decoded WASM size: %zu", g_current_task.file_len); execute_wasm_module(g_current_task.id, wasm_binary, @@ -452,7 +478,6 @@ void handle_start_command(const char *payload) g_current_task.inputs, g_current_task.inputs_count); } - else if (strlen(t.image_url) > 0) { LOG_INF("Requesting WASM from registry: %s", t.image_url); extern const char *channel_id; @@ -461,6 +486,8 @@ void handle_start_command(const char *payload) else { LOG_WRN("No file or image_url specified; cannot start WASM task!"); } + + memcpy(&g_current_task, &t, sizeof(t)); } void handle_stop_command(const char *payload) From 679e0645bc0d77372aba79a501e8d9f2fc4e0e33 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 13:31:11 +0300 Subject: [PATCH 45/50] switch from zephyr JSON to cJSON --- embed-proplet/CMakeLists.txt | 2 +- embed-proplet/prj.conf | 10 +- embed-proplet/src/cJSON.c | 3164 +++++++++++++++++++++++++++++++ embed-proplet/src/cJSON.h | 306 +++ embed-proplet/src/mqtt_client.c | 207 +- 5 files changed, 3596 insertions(+), 93 deletions(-) create mode 100644 embed-proplet/src/cJSON.c create mode 100644 embed-proplet/src/cJSON.h diff --git a/embed-proplet/CMakeLists.txt b/embed-proplet/CMakeLists.txt index 7872e40..e69524f 100644 --- a/embed-proplet/CMakeLists.txt +++ b/embed-proplet/CMakeLists.txt @@ -49,6 +49,6 @@ zephyr_library_sources(${WAMR_RUNTIME_LIB_SOURCE}) zephyr_library_app_memory(wamr_partition) -target_sources(app PRIVATE src/main.c src/mqtt_client.c src/wasm_handler.c src/wifi_manager.c) +target_sources(app PRIVATE src/cJSON.c src/main.c src/mqtt_client.c src/wasm_handler.c src/wifi_manager.c) target_link_libraries(app PRIVATE wamr_lib) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 24f3c05..6b29ab5 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -50,13 +50,15 @@ CONFIG_MQTT_LIB_WEBSOCKET=n # Uncomment to enable ALPN protocol for socket MQTT Library. # CONFIG_MQTT_LIB_TLS_USE_ALPN=y -# JSON Support -CONFIG_JSON_LIBRARY=y +# JSON Parsing +# CONFIG_JSON_LIBRARY=y +# Note: Zephyr's JSON library has been replaced by cJSON for JSON parsing and encoding. +# Uncomment the line above only if reverting to the Zephyr JSON library is necessary. # Logging CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 -# Uncomment the following lines to enable debug logs for mqtt, networking stack, Wi-Fi operations, and DHCPv4 server component. +# Uncomment to enable debug logs for mqtt, networking stack, Wi-Fi operations, and DHCPv4 server component. # CONFIG_MQTT_LOG_LEVEL_DBG=y # CONFIG_NET_LOG=y # CONFIG_WIFI_LOG_LEVEL_DBG=y @@ -72,7 +74,7 @@ CONFIG_NET_STATISTICS=y CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n # Thread Analyzer -# Uncomment the following lines when debugging thread-related issues, such as stack overflows, high CPU usage by specific threads, or deadlocks. +# Uncomment to debug thread-related issues. # CONFIG_THREAD_NAME=y # CONFIG_THREAD_ANALYZER=y # CONFIG_THREAD_ANALYZER_AUTO=y diff --git a/embed-proplet/src/cJSON.c b/embed-proplet/src/cJSON.c new file mode 100644 index 0000000..d7c7236 --- /dev/null +++ b/embed-proplet/src/cJSON.c @@ -0,0 +1,3164 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + item->string = NULL; + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + size_t v1_len; + size_t v2_len; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { + return NULL; + } + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) + { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { + return NULL; + } + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + output = NULL; + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + if (printed != NULL) + { + hooks->deallocate(printed); + printed = NULL; + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + p.buffer = NULL; + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0 || newitem == NULL) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + return cJSON_Duplicate_rec(item, 0, recurse ); +} + +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + if(depth >= CJSON_CIRCULAR_LIMIT) { + goto fail; + } + newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); + object = NULL; +} diff --git a/embed-proplet/src/cJSON.h b/embed-proplet/src/cJSON.h new file mode 100644 index 0000000..37520bb --- /dev/null +++ b/embed-proplet/src/cJSON.h @@ -0,0 +1,306 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 18 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/embed-proplet/src/mqtt_client.c b/embed-proplet/src/mqtt_client.c index a1a51d4..24b5e52 100644 --- a/embed-proplet/src/mqtt_client.c +++ b/embed-proplet/src/mqtt_client.c @@ -1,4 +1,5 @@ -#include +// #include +#include "cJSON.h" #include "mqtt_client.h" #include #include @@ -88,22 +89,6 @@ struct registry_response { char data[MAX_BASE64_LEN]; }; -static const struct json_obj_descr task_descr[] = { - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "id", id, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "name", name, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "state", state, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "image_url", image_url, JSON_TOK_STRING), - - JSON_OBJ_DESCR_PRIM_NAMED(struct task, "file", file, JSON_TOK_STRING), - - JSON_OBJ_DESCR_ARRAY_NAMED(struct task, "inputs", inputs, MAX_INPUTS, inputs_count, JSON_TOK_NUMBER), -}; - -static const struct json_obj_descr registry_response_descr[] = { - JSON_OBJ_DESCR_PRIM_NAMED(struct registry_response, "app_name", app_name, JSON_TOK_STRING), - JSON_OBJ_DESCR_PRIM_NAMED(struct registry_response, "data", data, JSON_TOK_STRING), -}; - static void prepare_fds(struct mqtt_client *client) { if (client->transport.type == MQTT_TRANSPORT_NON_SECURE) { @@ -411,139 +396,184 @@ int subscribe(const char *channel_id) return ret; } -void handle_start_command(const char *payload) -{ - struct task t; - memset(&t, 0, sizeof(t)); - - int ret = json_obj_parse(payload, strlen(payload), - task_descr, - ARRAY_SIZE(task_descr), - &t); - if (ret < 0) { - LOG_ERR("Failed to parse START task payload, error: %d", ret); +void handle_start_command(const char *payload) { + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + LOG_ERR("JSON parsing error before: %s", error_ptr); + } return; } - char encoded_json[1024]; // Adjust size based on expected payload length - ret = json_obj_encode_buf(task_descr, ARRAY_SIZE(task_descr), &t, encoded_json, sizeof(encoded_json)); - if (ret < 0) { - LOG_ERR("Failed to encode struct to JSON, error: %d", ret); - } else { - LOG_INF("Encoded JSON: %s", encoded_json); - } + struct task t = {0}; + + cJSON *id = cJSON_GetObjectItemCaseSensitive(json, "id"); + cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); + cJSON *state = cJSON_GetObjectItemCaseSensitive(json, "state"); + cJSON *image_url = cJSON_GetObjectItemCaseSensitive(json, "image_url"); + cJSON *file = cJSON_GetObjectItemCaseSensitive(json, "file"); + cJSON *inputs = cJSON_GetObjectItemCaseSensitive(json, "inputs"); - if (strlen(t.id) == 0 || strlen(t.name) == 0 || strlen(t.state) == 0) { - LOG_ERR("Parsed task contains invalid or missing mandatory fields."); + if (!cJSON_IsString(id) || !cJSON_IsString(name) || !cJSON_IsString(state)) { + LOG_ERR("Invalid or missing mandatory fields in JSON payload"); + cJSON_Delete(json); return; } - t.id[MAX_ID_LEN - 1] = '\0'; - t.name[MAX_NAME_LEN - 1] = '\0'; - t.state[MAX_STATE_LEN - 1] = '\0'; + strncpy(t.id, id->valuestring, MAX_ID_LEN - 1); + strncpy(t.name, name->valuestring, MAX_NAME_LEN - 1); + strncpy(t.state, state->valuestring, MAX_STATE_LEN - 1); - LOG_INF("Starting task: ID=%.63s, Name=%.63s, State=%.15s", t.id, t.name, t.state); + if (cJSON_IsString(image_url)) { + strncpy(t.image_url, image_url->valuestring, MAX_URL_LEN - 1); + } - if (strlen(t.file) > 0) { - size_t required_size = 0; + if (cJSON_IsString(file)) { + strncpy(t.file, file->valuestring, MAX_BASE64_LEN - 1); + } - // Calculate required buffer size - ret = base64_decode(NULL, 0, &required_size, (const uint8_t *)t.file, strlen(t.file)); - if (ret < 0) { - LOG_ERR("Failed to calculate buffer size for base64 decode, err=%d", ret); - return; + if (cJSON_IsArray(inputs)) { + int input_count = cJSON_GetArraySize(inputs); + t.inputs_count = (input_count > MAX_INPUTS) ? MAX_INPUTS : input_count; + for (size_t i = 0; i < t.inputs_count; ++i) { + cJSON *input = cJSON_GetArrayItem(inputs, i); + if (cJSON_IsNumber(input)) { + t.inputs[i] = (uint64_t)input->valuedouble; + } } + } - if (required_size > MAX_BASE64_LEN) { - LOG_ERR("Decoded size exceeds buffer capacity"); - return; - } + LOG_INF("Starting task: ID=%s, Name=%s, State=%s", t.id, t.name, t.state); + LOG_INF("image_url=%s, file-len(b64)=%zu", t.image_url, strlen(t.file)); + LOG_INF("inputs_count=%zu", t.inputs_count); - static uint8_t wasm_binary[MAX_BASE64_LEN]; + if (strlen(t.file) > 0) { size_t wasm_decoded_len = 0; - - // Perform actual decoding - ret = base64_decode(wasm_binary, sizeof(wasm_binary), &wasm_decoded_len, - (const uint8_t *)t.file, strlen(t.file)); + static uint8_t wasm_binary[MAX_BASE64_LEN]; + int ret = base64_decode(wasm_binary, sizeof(wasm_binary), &wasm_decoded_len, + (const uint8_t *)t.file, strlen(t.file)); if (ret < 0) { LOG_ERR("Failed to decode base64 WASM (task.file). Err=%d", ret); + cJSON_Delete(json); return; } g_current_task.file_len = wasm_decoded_len; - execute_wasm_module(g_current_task.id, wasm_binary, g_current_task.file_len, g_current_task.inputs, g_current_task.inputs_count); - } - else if (strlen(t.image_url) > 0) { + } else if (strlen(t.image_url) > 0) { LOG_INF("Requesting WASM from registry: %s", t.image_url); extern const char *channel_id; publish_registry_request(channel_id, t.image_url); - } - else { + } else { LOG_WRN("No file or image_url specified; cannot start WASM task!"); } memcpy(&g_current_task, &t, sizeof(t)); + + cJSON_Delete(json); } -void handle_stop_command(const char *payload) -{ +void handle_stop_command(const char *payload) { + cJSON *json = cJSON_Parse(payload); + if (!json) { + LOG_ERR("Failed to parse JSON payload"); + return; + } + struct task t; memset(&t, 0, sizeof(t)); - int ret = json_obj_parse(payload, strlen(payload), - task_descr, - ARRAY_SIZE(task_descr), - &t); - if (ret < 0) { - LOG_ERR("Failed to parse STOP task payload, error: %d", ret); + cJSON *id = cJSON_GetObjectItemCaseSensitive(json, "id"); + cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); + cJSON *state = cJSON_GetObjectItemCaseSensitive(json, "state"); + + if (cJSON_IsString(id) && id->valuestring) { + strncpy(t.id, id->valuestring, sizeof(t.id) - 1); + } else { + LOG_ERR("Invalid or missing 'id' field in stop command"); + cJSON_Delete(json); + return; + } + + if (cJSON_IsString(name) && name->valuestring) { + strncpy(t.name, name->valuestring, sizeof(t.name) - 1); + } else { + LOG_ERR("Invalid or missing 'name' field in stop command"); + cJSON_Delete(json); + return; + } + + if (cJSON_IsString(state) && state->valuestring) { + strncpy(t.state, state->valuestring, sizeof(t.state) - 1); + } else { + LOG_ERR("Invalid or missing 'state' field in stop command"); + cJSON_Delete(json); return; } LOG_INF("Stopping task: ID=%s, Name=%s, State=%s", t.id, t.name, t.state); + stop_wasm_app(t.id); + + cJSON_Delete(json); } -/** - * We receive a single chunk "data" field with the full base64 WASM. - */ -int handle_registry_response(const char *payload) -{ +// Handles a single chunk "data" field with the full base64 WASM. +int handle_registry_response(const char *payload) { + cJSON *json = cJSON_Parse(payload); + if (!json) { + LOG_ERR("Failed to parse JSON payload"); + return -1; + } + struct registry_response resp; memset(&resp, 0, sizeof(resp)); - int ret = json_obj_parse(payload, strlen(payload), - registry_response_descr, - ARRAY_SIZE(registry_response_descr), - &resp); - if (ret < 0) { - LOG_ERR("Failed to parse registry response, error: %d", ret); + cJSON *app_name = cJSON_GetObjectItemCaseSensitive(json, "app_name"); + cJSON *data = cJSON_GetObjectItemCaseSensitive(json, "data"); + + if (cJSON_IsString(app_name) && app_name->valuestring) { + strncpy(resp.app_name, app_name->valuestring, sizeof(resp.app_name) - 1); + } else { + LOG_ERR("Invalid or missing 'app_name' field in registry response"); + cJSON_Delete(json); + return -1; + } + + if (cJSON_IsString(data) && data->valuestring) { + strncpy(resp.data, data->valuestring, sizeof(resp.data) - 1); + } else { + LOG_ERR("Invalid or missing 'data' field in registry response"); + cJSON_Delete(json); return -1; } LOG_INF("Single-chunk registry response for app: %s", resp.app_name); size_t encoded_len = strlen(resp.data); - size_t decoded_len = (encoded_len * 3) / 4; + size_t decoded_len = (encoded_len * 3) / 4; // Maximum possible decoded size uint8_t *binary_data = malloc(decoded_len); if (!binary_data) { LOG_ERR("Failed to allocate memory for decoded binary"); + cJSON_Delete(json); return -1; } size_t actual_decoded_len = decoded_len; - ret = base64_decode(binary_data, - decoded_len, - &actual_decoded_len, - (const uint8_t *)resp.data, - encoded_len); + int ret = base64_decode(binary_data, + decoded_len, + &actual_decoded_len, + (const uint8_t *)resp.data, + encoded_len); if (ret < 0) { - LOG_ERR("Failed to decode base64 single-chunk, err=%d", ret); + LOG_ERR("Failed to decode Base64 data, err=%d", ret); free(binary_data); + cJSON_Delete(json); return -1; } @@ -556,6 +586,7 @@ int handle_registry_response(const char *payload) g_current_task.inputs_count); free(binary_data); + cJSON_Delete(json); LOG_INF("WASM binary executed from single-chunk registry response."); return 0; From 87a28f46a8b89634a6364fbca632775963b30be8 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 14:47:06 +0300 Subject: [PATCH 46/50] Increase logger buffer size --- embed-proplet/prj.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embed-proplet/prj.conf b/embed-proplet/prj.conf index 6b29ab5..0ed0e66 100644 --- a/embed-proplet/prj.conf +++ b/embed-proplet/prj.conf @@ -58,6 +58,8 @@ CONFIG_MQTT_LIB_WEBSOCKET=n # Logging CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_LOG_MODE_DEFERRED=n # Uncomment to enable debug logs for mqtt, networking stack, Wi-Fi operations, and DHCPv4 server component. # CONFIG_MQTT_LOG_LEVEL_DBG=y # CONFIG_NET_LOG=y From bdf402d76ff9c180fe136c39e254e1bfb551ae58 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 14:50:26 +0300 Subject: [PATCH 47/50] Remove extra comments --- embed-proplet/src/wasm_handler.c | 38 ++++---------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 8a35599..ecd8710 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -6,11 +6,10 @@ LOG_MODULE_REGISTER(wasm_handler); -#define MAX_WASM_APPS 10 /* How many different Wasm tasks we can track simultaneously */ +#define MAX_WASM_APPS 10 #define MAX_ID_LEN 64 -#define MAX_INPUTS 16 /* Max 32-bit arguments we’ll pass to WASM main */ +#define MAX_INPUTS 16 -/* A record of a "running" (loaded & instantiated) Wasm module. */ typedef struct { bool in_use; char id[MAX_ID_LEN]; @@ -18,48 +17,39 @@ typedef struct { wasm_module_inst_t module_inst; } wasm_app_t; -/* We'll maintain a small global array of possible Wasm apps. */ static wasm_app_t g_wasm_apps[MAX_WASM_APPS]; -/* Keep track of whether we've called wasm_runtime_full_init() yet. */ static bool g_wamr_initialized = false; -/* Forward declarations for some helper functions. */ static void maybe_init_wamr_runtime(void); static int find_free_slot(void); static int find_app_by_id(const char *task_id); -/*-------------------------------------------------------------------------*/ -/* Public API: execute_wasm_module(...) */ -/*-------------------------------------------------------------------------*/ + void execute_wasm_module(const char *task_id, const uint8_t *wasm_data, size_t wasm_size, const uint64_t *inputs, size_t inputs_count) { - /* Make sure the WAMR runtime is initialized once. */ maybe_init_wamr_runtime(); if (!g_wamr_initialized) { LOG_ERR("WAMR runtime not available, cannot execute WASM"); return; } - /* If a Wasm app with this ID is already running, stop it first. */ int existing_idx = find_app_by_id(task_id); if (existing_idx >= 0) { LOG_WRN("WASM app with ID %s is already running. Stopping it first...", task_id); stop_wasm_app(task_id); } - /* Find a free slot in the global array. */ int slot = find_free_slot(); if (slot < 0) { LOG_ERR("No free slot to store new WASM app instance (increase MAX_WASM_APPS)."); return; } - /* Load the module from memory. */ char error_buf[128]; wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, error_buf, sizeof(error_buf)); @@ -68,7 +58,6 @@ void execute_wasm_module(const char *task_id, return; } - /* Instantiate the module. Increase stack/heap if needed. */ wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, 4 * 1024, /* stack size */ 4 * 1024, /* heap size */ @@ -80,7 +69,6 @@ void execute_wasm_module(const char *task_id, return; } - /* Store references in the global array, so we can stop it later. */ g_wasm_apps[slot].in_use = true; strncpy(g_wasm_apps[slot].id, task_id, MAX_ID_LEN - 1); g_wasm_apps[slot].id[MAX_ID_LEN - 1] = '\0'; @@ -100,7 +88,6 @@ void execute_wasm_module(const char *task_id, LOG_INF("Executing 'main' in WASM module with ID=%s", task_id); - /* Convert 64-bit inputs to 32-bit if your "main" expects 32-bit args. */ uint32_t arg_buf[MAX_INPUTS]; memset(arg_buf, 0, sizeof(arg_buf)); size_t n_args = (inputs_count > MAX_INPUTS) ? MAX_INPUTS : inputs_count; @@ -113,17 +100,8 @@ void execute_wasm_module(const char *task_id, } else { LOG_INF("WASM 'main' executed successfully."); } - - /* - * NOTE: We do NOT call wasm_runtime_deinstantiate() or wasm_runtime_unload() - * here, so the module remains loaded. That’s what allows stop_wasm_app() - * to do meaningful cleanup later. - */ } -/*-------------------------------------------------------------------------*/ -/* Public API: stop_wasm_app(...) */ -/*-------------------------------------------------------------------------*/ void stop_wasm_app(const char *task_id) { int idx = find_app_by_id(task_id); @@ -135,26 +113,20 @@ void stop_wasm_app(const char *task_id) wasm_app_t *app = &g_wasm_apps[idx]; LOG_INF("Stopping WASM app with ID=%s", app->id); - /* Properly deinstantiate and unload the module. */ wasm_runtime_deinstantiate(app->module_inst); wasm_runtime_unload(app->module); - /* Clear our record for re-use. */ app->in_use = false; memset(app->id, 0, sizeof(app->id)); LOG_INF("WASM app [%s] has been stopped and unloaded.", task_id); } -/*-------------------------------------------------------------------------*/ -/* Internal Helpers */ -/*-------------------------------------------------------------------------*/ -/** One-time WAMR runtime initialization. */ static void maybe_init_wamr_runtime(void) { if (g_wamr_initialized) { - return; /* Already inited */ + return; } RuntimeInitArgs init_args; @@ -170,7 +142,6 @@ static void maybe_init_wamr_runtime(void) LOG_INF("WAMR runtime initialized successfully."); } -/** Finds the first free slot in the global g_wasm_apps array, or -1 if full. */ static int find_free_slot(void) { for (int i = 0; i < MAX_WASM_APPS; i++) { @@ -181,7 +152,6 @@ static int find_free_slot(void) return -1; } -/** Looks up a loaded module by task ID. Returns array index or -1 if not found. */ static int find_app_by_id(const char *task_id) { for (int i = 0; i < MAX_WASM_APPS; i++) { From d9020b79108a20d335e7a068daec2e0aa40cb615 Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 15:30:54 +0300 Subject: [PATCH 48/50] Add execution env --- embed-proplet/src/wasm_handler.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index ecd8710..229fa46 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -25,7 +25,6 @@ static void maybe_init_wamr_runtime(void); static int find_free_slot(void); static int find_app_by_id(const char *task_id); - void execute_wasm_module(const char *task_id, const uint8_t *wasm_data, size_t wasm_size, @@ -59,8 +58,8 @@ void execute_wasm_module(const char *task_id, } wasm_module_inst_t module_inst = wasm_runtime_instantiate(module, - 4 * 1024, /* stack size */ - 4 * 1024, /* heap size */ + 16 * 1024, /* stack size */ + 16 * 1024, /* heap size */ error_buf, sizeof(error_buf)); if (!module_inst) { @@ -75,11 +74,6 @@ void execute_wasm_module(const char *task_id, g_wasm_apps[slot].module = module; g_wasm_apps[slot].module_inst = module_inst; - /* - * Optionally call "main" right away. If your Wasm module is meant to run - * a single function and then exit, this is enough. If it's a long-running - * module (e.g., with timers or state), you'd skip the immediate call. - */ wasm_function_inst_t func = wasm_runtime_lookup_function(module_inst, "main"); if (!func) { LOG_WRN("Function 'main' not found in WASM module. No entry point to call."); @@ -95,11 +89,20 @@ void execute_wasm_module(const char *task_id, arg_buf[i] = (uint32_t)(inputs[i] & 0xFFFFFFFFu); } - if (!wasm_runtime_call_wasm(module_inst, func, n_args, arg_buf)) { + wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, 16 * 1024); + if (!exec_env) { + LOG_ERR("Failed to create execution environment for WASM module."); + stop_wasm_app(task_id); + return; + } + + if (!wasm_runtime_call_wasm(exec_env, func, n_args, arg_buf)) { LOG_ERR("Error invoking WASM function 'main'"); } else { LOG_INF("WASM 'main' executed successfully."); } + + wasm_runtime_destroy_exec_env(exec_env); } void stop_wasm_app(const char *task_id) From 4aaeca47856f0663a567b65a71125a8990a04a3c Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 16:12:05 +0300 Subject: [PATCH 49/50] Get results from exec env --- embed-proplet/src/wasm_handler.c | 44 ++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 229fa46..3738d35 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -50,8 +50,7 @@ void execute_wasm_module(const char *task_id, } char error_buf[128]; - wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, - error_buf, sizeof(error_buf)); + wasm_module_t module = wasm_runtime_load(wasm_data, wasm_size, error_buf, sizeof(error_buf)); if (!module) { LOG_ERR("Failed to load WASM module: %s", error_buf); return; @@ -77,32 +76,56 @@ void execute_wasm_module(const char *task_id, wasm_function_inst_t func = wasm_runtime_lookup_function(module_inst, "main"); if (!func) { LOG_WRN("Function 'main' not found in WASM module. No entry point to call."); + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); + return; + } + + uint32_t result_count = wasm_func_get_result_count(func, module_inst); + if (result_count == 0) { + LOG_ERR("Function has no return value."); + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); return; } - LOG_INF("Executing 'main' in WASM module with ID=%s", task_id); + wasm_valkind_t result_types[result_count]; + wasm_func_get_result_types(func, module_inst, result_types); - uint32_t arg_buf[MAX_INPUTS]; - memset(arg_buf, 0, sizeof(arg_buf)); + wasm_val_t results[result_count]; + for (uint32_t i = 0; i < result_count; i++) { + results[i].kind = result_types[i]; + } + + wasm_val_t args[MAX_INPUTS]; size_t n_args = (inputs_count > MAX_INPUTS) ? MAX_INPUTS : inputs_count; for (size_t i = 0; i < n_args; i++) { - arg_buf[i] = (uint32_t)(inputs[i] & 0xFFFFFFFFu); + args[i].kind = WASM_I32; + args[i].of.i32 = (uint32_t)(inputs[i] & 0xFFFFFFFFu); } wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, 16 * 1024); if (!exec_env) { LOG_ERR("Failed to create execution environment for WASM module."); - stop_wasm_app(task_id); + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); return; } - if (!wasm_runtime_call_wasm(exec_env, func, n_args, arg_buf)) { - LOG_ERR("Error invoking WASM function 'main'"); + if (!wasm_runtime_call_wasm_a(exec_env, func, result_count, results, n_args, args)) { + const char *exception = wasm_runtime_get_exception(module_inst); + LOG_ERR("Error invoking WASM function: %s", exception ? exception : "Unknown error"); } else { - LOG_INF("WASM 'main' executed successfully."); + for (uint32_t i = 0; i < result_count; i++) { + if (results[i].kind == WASM_I32) { + LOG_INF("Result[%u]: %d", i, results[i].of.i32); + } + } } wasm_runtime_destroy_exec_env(exec_env); + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); } void stop_wasm_app(const char *task_id) @@ -125,7 +148,6 @@ void stop_wasm_app(const char *task_id) LOG_INF("WASM app [%s] has been stopped and unloaded.", task_id); } - static void maybe_init_wamr_runtime(void) { if (g_wamr_initialized) { From 36076387751183a8a4e0f71fa3cacdb2432d640a Mon Sep 17 00:00:00 2001 From: JeffMboya Date: Tue, 21 Jan 2025 16:46:53 +0300 Subject: [PATCH 50/50] Publish results back to manager --- embed-proplet/src/wasm_handler.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/embed-proplet/src/wasm_handler.c b/embed-proplet/src/wasm_handler.c index 3738d35..a57cb4b 100644 --- a/embed-proplet/src/wasm_handler.c +++ b/embed-proplet/src/wasm_handler.c @@ -1,4 +1,5 @@ #include "wasm_handler.h" +#include "mqtt_client.h" #include #include #include @@ -9,6 +10,7 @@ LOG_MODULE_REGISTER(wasm_handler); #define MAX_WASM_APPS 10 #define MAX_ID_LEN 64 #define MAX_INPUTS 16 +#define MAX_RESULTS 16 typedef struct { bool in_use; @@ -116,11 +118,25 @@ void execute_wasm_module(const char *task_id, const char *exception = wasm_runtime_get_exception(module_inst); LOG_ERR("Error invoking WASM function: %s", exception ? exception : "Unknown error"); } else { + char result_payload[256] = {0}; + char results_string[MAX_RESULTS * 16] = {0}; + for (uint32_t i = 0; i < result_count; i++) { if (results[i].kind == WASM_I32) { - LOG_INF("Result[%u]: %d", i, results[i].of.i32); + char temp[16]; + snprintf(temp, sizeof(temp), "%d", results[i].of.i32); + strncat(results_string, temp, sizeof(results_string) - strlen(results_string) - 1); + if (i < result_count - 1) { + strncat(results_string, ",", sizeof(results_string) - strlen(results_string) - 1); + } } } + + extern const char *channel_id; + snprintf(result_payload, sizeof(result_payload), + "{\"task_id\":\"%s\",\"results\":[%s]}", task_id, results_string); + publish_results(channel_id, task_id, result_payload); + LOG_INF("WASM execution results published to MQTT topic"); } wasm_runtime_destroy_exec_env(exec_env);