From 66b10feb5238d9b8d9fabbaffaf936ca810c6a6b Mon Sep 17 00:00:00 2001 From: Alexandru Tiniuc Date: Mon, 17 Jun 2024 16:22:18 +0100 Subject: [PATCH] update from latest codegen --- BATTLESHIP/CMakeLists.txt | 44 +- BATTLESHIP/TopLevelCMakeLists.txt | 21 +- BATTLESHIP/build.sh | 7 +- BATTLESHIP/build_info.md | 2 + BATTLESHIP/build_win_bin_ninja.sh | 3 +- BATTLESHIP/build_win_bin_ninja_ota.sh | 47 + BATTLESHIP/knx_eink_battleships.c | 1281 ++++++++++++----- BATTLESHIP/knx_eink_battleships.h | 697 +++++++--- BATTLESHIP/knx_eink_battleships_dev.c | 150 +- BATTLESHIP/knx_eink_battleships_gui.cpp | 1528 +++++++++++++++++++++ BATTLESHIP/knx_eink_battleships_logic.c | 958 ++++++------- BATTLESHIP/knx_eink_battleships_virtual.c | 52 + BATTLESHIP/knx_iot_sleepy_main.c | 197 ++- BATTLESHIP/knx_iot_sleepy_main.h | 64 + BATTLESHIP/knx_iot_sleepy_main_extern.h | 133 +- BATTLESHIP/knx_iot_wakeful_main.c | 96 +- BATTLESHIP/knx_iot_wakeful_main_extern.h | 73 +- BATTLESHIP/readme.md | 6 +- EXAMPLE/CMakeLists.txt | 3 +- EXAMPLE/build_win_bin_ninja.sh | 3 +- EXAMPLE/build_win_bin_ninja_ota.sh | 47 + EXAMPLE/knx_iot_example.c | 311 ++++- EXAMPLE/knx_iot_example.h | 29 +- EXAMPLE/knx_iot_example_dev.c | 80 +- EXAMPLE/knx_iot_example_gui.cpp | 74 +- EXAMPLE/knx_iot_example_logic.c | 16 +- EXAMPLE/knx_iot_sleepy_main.c | 134 +- EXAMPLE/knx_iot_sleepy_main.h | 64 + EXAMPLE/knx_iot_sleepy_main_extern.h | 123 +- EXAMPLE/knx_iot_wakeful_main.c | 40 +- EXAMPLE/knx_iot_wakeful_main_extern.h | 73 +- EXAMPLE/readme.md | 2 +- 32 files changed, 4817 insertions(+), 1541 deletions(-) create mode 100644 BATTLESHIP/build_win_bin_ninja_ota.sh create mode 100644 BATTLESHIP/knx_eink_battleships_gui.cpp create mode 100644 BATTLESHIP/knx_eink_battleships_virtual.c create mode 100644 BATTLESHIP/knx_iot_sleepy_main.h create mode 100644 EXAMPLE/build_win_bin_ninja_ota.sh create mode 100644 EXAMPLE/knx_iot_sleepy_main.h diff --git a/BATTLESHIP/CMakeLists.txt b/BATTLESHIP/CMakeLists.txt index 222ed12..bc04b6f 100644 --- a/BATTLESHIP/CMakeLists.txt +++ b/BATTLESHIP/CMakeLists.txt @@ -42,18 +42,19 @@ set(KNX_IOT_STACK_REF master CACHE STRING "The branch of the KNX IoT Stack to us set(USE_GITLAB OFF CACHE BOOL "use gitlab as source for KNX IoT Stack") if(CASCODA_USE_PRIVATE_SDK) - set(CASCODA_REPO cascoda-sdk-priv) + set(CASCODA_SDK_REPO https://github.com/Cascoda/cascoda-sdk-priv.git CACHE STRING "") else() - set(CASCODA_REPO cascoda-sdk) + set(CASCODA_SDK_REPO https://github.com/Cascoda/cascoda-sdk.git CACHE STRING "") endif() -set(CASCODA_SDK_REPO https://github.com/Cascoda/${CASCODA_REPO}.git) + +set(CASCODA_REPO cascoda-sdk) if(USE_GITLAB) set(KNX_REPO knx-iot-stack-gitlab) - set(KNX_IOT_STACK_REPO https://gitlab.knx.org/shared-projects/knx-iot-point-api-public-stack/) + set(KNX_IOT_STACK_REPO https://gitlab.knx.org/shared-projects/knx-iot-point-api-public-stack/ CACHE STRING "") else() set(KNX_REPO knx-iot-stack) - set(KNX_IOT_STACK_REPO https://github.com/KNX-IOT/KNX-IOT-STACK.git) + set(KNX_IOT_STACK_REPO https://github.com/cascoda/knx-iot-stack.git CACHE STRING "") endif() # KNX IoT Stack @@ -70,6 +71,7 @@ FetchContent_Declare( GIT_TAG ${CASCODA_SDK_REF} ) + if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) set(USE_CONSOLE OFF CACHE BOOL "use console (for output logging)") @@ -86,7 +88,8 @@ if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) FetchContent_MakeAvailable(${KNX_REPO}) add_executable(knx_eink_battleships - ${PROJECT_SOURCE_DIR}/knx_eink_battleships.c ) + ${PROJECT_SOURCE_DIR}/knx_eink_battleships.c + ${PROJECT_SOURCE_DIR}/knx_eink_battleships_virtual.c) target_link_libraries(knx_eink_battleships kisClientServer) if(WIN32) @@ -101,7 +104,8 @@ if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) add_executable(knx_eink_battleships_gui WIN32 ${PROJECT_SOURCE_DIR}/knx_eink_battleships_gui.cpp # ${PROJECT_SOURCE_DIR}/knx_eink_battleships_logic.c - ${PROJECT_SOURCE_DIR}/knx_eink_battleships.c) + ${PROJECT_SOURCE_DIR}/knx_eink_battleships.c + ${PROJECT_SOURCE_DIR}/knx_eink_battleships_virtual.c) target_link_libraries(knx_eink_battleships_gui wx::net wx::core wx::base kisClientServer) # enable flag to compile the console in, so that the printf of the stack are shown. target_compile_definitions(knx_eink_battleships_gui PUBLIC KNX_GUI) @@ -113,9 +117,11 @@ if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) endif() + + else() - set(CASCODA_CHILI2_CONFIG_STRING "DEV_BOARD" CACHE STRING "KNX only supported on Devboard" FORCE) - set(CASCODA_CHILI2_REV "1" CACHE STRING "KNX only supported for Chili2 Rev 1" FORCE) + set(CASCODA_CHILI2_CONFIG_STRING "DEV_BOARD" CACHE STRING "KNX only supported on Devboard") + set(CASCODA_CHILI2_REV "1" CACHE STRING "KNX only supported for Chili2 Rev 1") set(CASCODA_OTA_UPGRADE_ENABLED 0 CACHE BOOL "Enables OTA upgrade") set(USE_DEMO_MODE OFF CACHE BOOL "demo mode (no reset)") option(CASCODA_BUILD_KNX "Enable the KNX-IoT Port" ON) @@ -132,8 +138,6 @@ else() ${PROJECT_SOURCE_DIR}/knx_eink_battleships_logic.c # Embedded specific main loop, which initialises the MCU, communications & then runs the KNX application ${PROJECT_SOURCE_DIR}/knx_iot_wakeful_main.c - # - # ${PROJECT_SOURCE_DIR}/KNX_EINK_BS.c ) target_link_libraries(knx_eink_battleships_ed kisClientServer @@ -153,8 +157,6 @@ else() ${PROJECT_SOURCE_DIR}/knx_eink_battleships_logic.c # Embedded specific main loop, which initialises the MCU, communications & then runs the KNX application ${PROJECT_SOURCE_DIR}/knx_iot_wakeful_main.c - # - # ${PROJECT_SOURCE_DIR}/KNX_EINK_BS.c ) target_link_libraries(knx_eink_battleships_reed kisClientServer @@ -174,8 +176,6 @@ else() ${PROJECT_SOURCE_DIR}/knx_eink_battleships_logic.c # Embedded specific main loop, which initialises the MCU, communications & then runs the KNX application ${PROJECT_SOURCE_DIR}/knx_iot_sleepy_main.c - # - # ${PROJECT_SOURCE_DIR}/KNX_EINK_BS.c ) target_link_libraries(knx_eink_battleships_sleepy kisClientServer @@ -187,6 +187,9 @@ else() eink-driver-waveshare-1-54-half-res ) +# include device cmake files, after all the targets have been created so that they may be altered within + + if(USE_DEMO_MODE) target_compile_definitions(knx_eink_battleships_ed PUBLIC DEMO_MODE) target_compile_definitions(knx_eink_battleships_reed PUBLIC DEMO_MODE) @@ -202,7 +205,7 @@ else() set(STACK_SIZE_KB "5" CACHE STRING "Number of KB to alloc stack") endif() if(NOT DEFINED HEAP_SIZE_BASE_KB) - set(HEAP_SIZE_BASE_KB "40" CACHE STRING "Number of KB to alloc heap") + set(HEAP_SIZE_BASE_KB "20" CACHE STRING "Number of KB to alloc heap") endif() math(EXPR STACK_SIZE "1024 * ${STACK_SIZE_KB}") math(EXPR HEAP_SIZE_ED "1024 * (${HEAP_SIZE_BASE_KB})") @@ -242,12 +245,21 @@ else() if (NOT TARGET copy-ldrom) + if (CASCODA_BM_INTERFACE STREQUAL "USB") add_custom_target(copy-ldrom ALL DEPENDS ldrom_hid) add_custom_command(TARGET copy-ldrom POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid ${PROJECT_BINARY_DIR}/bin COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid.bin ${PROJECT_BINARY_DIR}/bin ) + elseif (CASCODA_BM_INTERFACE STREQUAL "UART") + add_custom_target(copy-ldrom ALL DEPENDS ldrom_uart) + add_custom_command(TARGET copy-ldrom + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_uart ${PROJECT_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_uart.bin ${PROJECT_BINARY_DIR}/bin + ) + endif() endif() if (CASCODA_OTA_UPGRADE_ENABLED EQUAL 1) diff --git a/BATTLESHIP/TopLevelCMakeLists.txt b/BATTLESHIP/TopLevelCMakeLists.txt index fb8a849..bcac5fe 100644 --- a/BATTLESHIP/TopLevelCMakeLists.txt +++ b/BATTLESHIP/TopLevelCMakeLists.txt @@ -3,19 +3,24 @@ project(knx_iot_demos) option(CASCODA_USE_PRIVATE_SDK "" OFF) -if(CASCODA_USE_PRIVATE_SDK) - set(CASCODA_REPO cascoda-sdk-priv) -else() - set(CASCODA_REPO cascoda-sdk) -endif() +set(CASCODA_REPO cascoda-sdk) if(CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) + if (CASCODA_BM_INTERFACE STREQUAL "USB") add_custom_target(copy-ldrom ALL DEPENDS ldrom_hid) add_custom_command(TARGET copy-ldrom - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid ${PROJECT_BINARY_DIR}/bin/ldrom_hid - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid.bin ${PROJECT_BINARY_DIR}/bin/ldrom_hid.bin + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid ${PROJECT_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_hid.bin ${PROJECT_BINARY_DIR}/bin + ) + elseif (CASCODA_BM_INTERFACE STREQUAL "UART") + add_custom_target(copy-ldrom ALL DEPENDS ldrom_uart) + add_custom_command(TARGET copy-ldrom + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_uart ${PROJECT_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/_deps/${CASCODA_REPO}-build/bin/ldrom_uart.bin ${PROJECT_BINARY_DIR}/bin ) + endif() if (CASCODA_OTA_UPGRADE_ENABLED EQUAL 1) add_custom_target(copy-ota-bootloader ALL DEPENDS ota-bootloader) diff --git a/BATTLESHIP/build.sh b/BATTLESHIP/build.sh index bcd2b48..6ac795b 100644 --- a/BATTLESHIP/build.sh +++ b/BATTLESHIP/build.sh @@ -36,6 +36,9 @@ mkdir -p build cd build -cmake .. -DOC_DNS_SD_ENABLED=OFF -DOC_OSCORE_ENABLED=ON -DUSE_CONSOLE=ON +cmake .. -DOC_DNS_SD_ENABLED=ON -DOC_OSCORE_ENABLED=ON -DUSE_CONSOLE=ON # build step -cmake --build . +# default is all targets +#cmake --build . +cmake --build . --target knx_eink_battleships +cmake --build . --target knx_eink_battleships_gui diff --git a/BATTLESHIP/build_info.md b/BATTLESHIP/build_info.md index 22cb3c3..66bbc3d 100644 --- a/BATTLESHIP/build_info.md +++ b/BATTLESHIP/build_info.md @@ -25,12 +25,14 @@ CLI: - knx_eink_battleships Application (CLI) using the following files: - knx_eink_battleships.c + - knx_eink_battleships_virtual.c - knx_eink_battleships.h Windows GUI using WxWidgets: - knx_eink_battleships_gui Application (wxWidgets) using the following files: - knx_eink_battleships.c + - knx_eink_battleships_virtual.c - knx_eink_battleships.h - knx_eink_battleships.cpp diff --git a/BATTLESHIP/build_win_bin_ninja.sh b/BATTLESHIP/build_win_bin_ninja.sh index 8fafa2b..0d9611b 100644 --- a/BATTLESHIP/build_win_bin_ninja.sh +++ b/BATTLESHIP/build_win_bin_ninja.sh @@ -40,7 +40,8 @@ mkdir -p build_win_bin cd build_win_bin # create the make files for Windows/Linux cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE=../arm_gcc_m2351.cmake -DOC_OSCORE_ENABLED=ON \ - -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF + -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF +#-DCASCODA_USE_PRIVATE_SDK=ON cmake . # build it ninja diff --git a/BATTLESHIP/build_win_bin_ninja_ota.sh b/BATTLESHIP/build_win_bin_ninja_ota.sh new file mode 100644 index 0000000..9755012 --- /dev/null +++ b/BATTLESHIP/build_win_bin_ninja_ota.sh @@ -0,0 +1,47 @@ +# Copyright (c) 2022-2023 Cascoda Ltd +# All rights reserved. +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Cascoda Limited. +# integrated circuit in a product or a software update for such product, must +# reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific prior written +# permission. +# +# 4. This software, whether provided in binary or any other form must not be decompiled, +# disassembled, reverse engineered or otherwise modified. +# +# 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. +# +# THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# build script for creating a binary for the dev board +# on windows/linux using the ninja toolchain +echo "using Ninja" + +mkdir -p build_win_bin +cd build_win_bin +# create the make files for Windows/Linux +cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE=../arm_gcc_m2351.cmake -DOC_OSCORE_ENABLED=ON \ + -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF -DCASCODA_OTA_UPGRADE_ENABLED=1 +cmake . +# build it +ninja + diff --git a/BATTLESHIP/knx_eink_battleships.c b/BATTLESHIP/knx_eink_battleships.c index 464be85..6c47459 100644 --- a/BATTLESHIP/knx_eink_battleships.c +++ b/BATTLESHIP/knx_eink_battleships.c @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Ltd + Copyright (c) 2022-2024 Cascoda Ltd -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -116,6 +116,9 @@ static struct timespec ts; #endif #include /* defines FILENAME_MAX */ +#include +#include +#include #define STORE_PREFIX "app" #define MY_NAME "KNX Battleships Demo" /**< The name of the application */ @@ -133,6 +136,10 @@ static CRITICAL_SECTION cs; /**< event loop variable */ #define GetCurrentDir getcwd #endif +#ifdef HARDWARE_INIT + void hardware_init(void); +#endif /* hardware init */ + #define btoa(x) ((x) ? "true" : "false") volatile int quit = 0; /**< stop variable, used by handle_signal */ bool g_reset = false; /**< reset variable, set by commandline arguments */ @@ -141,75 +148,46 @@ char g_serial_number[20] = "00fa10010713"; +volatile DPT_Uint_XY gSendShot; /**< global variable for SendShot */ +volatile DPT_Uint_XY gReceiveShot; /**< global variable for ReceiveShot */ +volatile DPT_Shot_Status gSendShotStatus; /**< global variable for SendShotStatus */ +volatile DPT_Shot_Status gReceiveShotStatus; /**< global variable for ReceiveShotStatus */ +volatile DPT_Start gSendReady; /**< global variable for SendReady */ +volatile DPT_Start gReceiveReady; /**< global variable for ReceiveReady */ +volatile DPT_Param_Bool gStarting_Player; /**< global variable for Starting_Player */ -volatile DPT_Uint_XY g_SendShot; /**< global variable for SendShot */ -volatile DPT_Uint_XY g_ReceiveShot; /**< global variable for ReceiveShot */ -volatile DPT_Shot_Status g_SendShotStatus; /**< global variable for SendShotStatus */ -volatile DPT_Shot_Status g_ReceiveShotStatus; /**< global variable for ReceiveShotStatus */ -volatile DPT_Start g_SendReady; /**< global variable for SendReady */ -volatile DPT_Start g_ReceiveReady; /**< global variable for ReceiveReady */ - -volatile bool g_fault_ReceiveShot; /**< global variable for fault ReceiveShot */ -volatile bool g_fault_ReceiveShotStatus; /**< global variable for fault ReceiveShotStatus */ -volatile bool g_fault_ReceiveReady; /**< global variable for fault ReceiveReady */ - - -volatile DPT_Param_Bool g_Starting_Player1; /**< global variable for Starting_Player */ +volatile bool g_faultReceiveShot; /**< global variable for fault ReceiveShot */ +volatile bool g_faultReceiveShotStatus; /**< global variable for fault ReceiveShotStatus */ +volatile bool g_faultReceiveReady; /**< global variable for fault ReceiveReady */ +volatile DPT_Param_Bool gStarting_Player1; /**< global variable for Starting_Player */ void get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data); void -put_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data); - -typedef enum DatapointType{ - DatapointType_bool, - DatapointType_int, - DatapointType_float, - DatapointType_string, - DatapointType_DPT_Param_Bool, - DatapointType_DPT_Shot_Status, - DatapointType_DPT_Start, - DatapointType_DPT_Uint_XY, - DatapointType_MAX_NUM, -} DatapointType; - -typedef struct datapoint_t { - oc_resource_t resource; - const char *const *metadata; - const char *feedback_url; - DatapointType type; - volatile void *g_var; - volatile void *g_fault; - bool persistent; - int num_elements; -} datapoint_t; - - - - - - - - - - -extern const datapoint_t g_datapoints[]; -const size_t num_datapoints = 6; +put_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data); +const size_t num_datapoints = 6; /** number of data points */ +const size_t num_parameters = 1; /** number of parameters */ + +oc_resource_dummy_t app_resource_end = {NULL, -1}; +oc_resource_data_t runtime_dataSendShot; +oc_resource_data_t runtime_dataReceiveShot; +oc_resource_data_t runtime_dataSendShotStatus; +oc_resource_data_t runtime_dataReceiveShotStatus; +oc_resource_data_t runtime_dataSendReady; +oc_resource_data_t runtime_dataReceiveReady; +oc_resource_data_t runtime_dataStarting_Player; + +#if defined _MSC_VER && !defined __INTEL_COMPILER +_Pragma("warning(disable:4090)") +#elif defined(__GNUC__) +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wdiscarded-array-qualifiers\"") +#endif -extern const datapoint_t g_parameters[]; -const size_t num_parameters = 1; -oc_resource_dummy_t app_resource_end; -oc_resource_data_t runtime_data_SendShot; -oc_resource_data_t runtime_data_ReceiveShot; -oc_resource_data_t runtime_data_SendShotStatus; -oc_resource_data_t runtime_data_ReceiveShotStatus; -oc_resource_data_t runtime_data_SendReady; -oc_resource_data_t runtime_data_ReceiveReady; -oc_resource_data_t runtime_data_Starting_Player; const datapoint_t g_datapoints[6] = { - /*[0] = */{ + /*[0] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[1].resource, /*device*/ 0, @@ -217,7 +195,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_1"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65500.101" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.uint_XY"), - /*interfaces*/ OC_IF_O, + /*interfaces*/ OC_IF_D | OC_IF_O, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[0]}, @@ -229,17 +207,18 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_SendShot + /*runtime_data*/ &runtime_dataSendShot }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Uint_XY, - /*.g_var =*/ (void*)&g_SendShot, + /*.g_var =*/ (void*)&gSendShot, /*.g_fault =*/ NULL, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[1] = */{ + /*[1] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[2].resource, /*device*/ 0, @@ -247,7 +226,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_2"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65501.111" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.uint_XY"), - /*interfaces*/ OC_IF_I, + /*interfaces*/ OC_IF_D | OC_IF_I, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[1]}, @@ -259,17 +238,18 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_ReceiveShot + /*runtime_data*/ &runtime_dataReceiveShot }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Uint_XY, - /*.g_var =*/ (void*)&g_ReceiveShot, - /*.g_fault =*/ &g_fault_ReceiveShot, + /*.g_var =*/ (void*)&gReceiveShot, + /*.g_fault =*/ &g_faultReceiveShot, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[2] = */{ + /*[2] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[3].resource, /*device*/ 0, @@ -277,7 +257,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_3"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65501.102" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.shot_Status"), - /*interfaces*/ OC_IF_O, + /*interfaces*/ OC_IF_D | OC_IF_O, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[2]}, @@ -289,17 +269,18 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_SendShotStatus + /*runtime_data*/ &runtime_dataSendShotStatus }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Shot_Status, - /*.g_var =*/ (void*)&g_SendShotStatus, + /*.g_var =*/ (void*)&gSendShotStatus, /*.g_fault =*/ NULL, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[3] = */{ + /*[3] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[4].resource, /*device*/ 0, @@ -307,7 +288,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_4"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65500.112" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.shot_Status"), - /*interfaces*/ OC_IF_I, + /*interfaces*/ OC_IF_D | OC_IF_I, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[3]}, @@ -319,17 +300,18 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_ReceiveShotStatus + /*runtime_data*/ &runtime_dataReceiveShotStatus }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Shot_Status, - /*.g_var =*/ (void*)&g_ReceiveShotStatus, - /*.g_fault =*/ &g_fault_ReceiveShotStatus, + /*.g_var =*/ (void*)&gReceiveShotStatus, + /*.g_fault =*/ &g_faultReceiveShotStatus, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[4] = */{ + /*[4] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[5].resource, /*device*/ 0, @@ -337,7 +319,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_5"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65500.103" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.start"), - /*interfaces*/ OC_IF_O, + /*interfaces*/ OC_IF_D | OC_IF_O, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[4]}, @@ -349,17 +331,18 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_SendReady + /*runtime_data*/ &runtime_dataSendReady }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Start, - /*.g_var =*/ (void*)&g_SendReady, + /*.g_var =*/ (void*)&gSendReady, /*.g_fault =*/ NULL, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[5] = */{ + /*[5] (saved=0)= */{ /* .resource=*/ { /*next*/(oc_resource_t*)&g_parameters[0].resource, /*device*/ 0, @@ -367,7 +350,7 @@ const datapoint_t g_datapoints[6] = { /*uri*/ oc_string_create_const("/p/o_1_6"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65501.113" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.start"), - /*interfaces*/ OC_IF_I, + /*interfaces*/ OC_IF_D | OC_IF_I, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[5]}, @@ -379,19 +362,20 @@ const datapoint_t g_datapoints[6] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_ReceiveReady + /*runtime_data*/ &runtime_dataReceiveReady }, /*.metadata =*/ NULL, /*.feedback_url =*/NULL, /*.type =*/ DatapointType_DPT_Start, - /*.g_var =*/ (void*)&g_ReceiveReady, - /*.g_fault =*/ &g_fault_ReceiveReady, + /*.g_var =*/ (void*)&gReceiveReady, + /*.g_fault =*/ &g_faultReceiveReady, /*.persistent =*/ false, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, }; const datapoint_t g_parameters[1] = { - /*[7] =*/ { + /* 1 (saved=1)[7] =*/ { /* .resource=*/ { /*next*/(oc_resource_t*)&app_resource_end, /*device*/ 0, @@ -399,9 +383,9 @@ const datapoint_t g_parameters[1] = { /*uri*/ oc_string_create_const("/p/p_1_1"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.65500.201" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.param_Bool"), - /*interfaces*/ OC_IF_P, + /*interfaces*/ OC_IF_D | OC_IF_P, /*content_type*/ APPLICATION_CBOR, - /*properties*/ OC_DISCOVERABLE, + /*properties*/ OC_UNDISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_parameters[0]}, /*put_handler*/ {put_generic, (void*)&g_parameters[0]}, /*post_handler*/ {NULL, NULL}, @@ -411,28 +395,44 @@ const datapoint_t g_parameters[1] = { /*observe_period_seconds*/ 0, /*fb_instance*/ 1, /*is_const*/ true, - /*runtime_data*/ &runtime_data_Starting_Player + /*runtime_data*/ &runtime_dataStarting_Player }, /*.metadata =*/ NULL, /*.feedback_url =*/ NULL, - - - // param can't be booleans: {param_type }} - /*.type =*/ DatapointType_int, - /*.g_var =*/ (void*)&g_Starting_Player1, + /*.type =*/ DatapointType_DPT_Param_Bool, + /*.g_var =*/ (void*)&gStarting_Player1, /*.g_fault =*/ NULL, /*.persistent =*/ true, + /*.default_present =*/ false, /*.num_elements =*/ 0 }, -}; +}; +/* + total saved parameters/datapoints = 1 + total group object entries = + total publisher entries = + total receiver entries = + total auth entries = + total SSN entries = + ---------------------------------------- + + total files = 101 +*/ + -typedef const volatile void* (*app_get_variable_fn)(const char*, void*); -typedef const volatile void* (*app_get_array_fn)(const char*, void*, int); -typedef const volatile void* (*app_get_array_elems_fn)(const char*, void*, int start, int n); +#if defined _MSC_VER && !defined __INTEL_COMPILER +_Pragma("warning(default:4090)") +#elif defined(__GNUC__) +_Pragma("GCC diagnostic pop") +#endif + +typedef const void* (*app_get_variable_fn)(const char*, void*); +typedef const void* (*app_get_array_fn)(const char*, void*, int); +typedef const void* (*app_get_array_elems_fn)(const char*, void*, int start, int n); +typedef void (*app_set_default_value_fn)(const char*); typedef void (*app_set_variable_fn)(const char*, const void*); -typedef void (*app_set_array_fn)(const char*, const void*, int n); -typedef void (*app_set_array_elems_fn)(const char*, const void*, int start, int n); -typedef void (*oc_encode_fn)(const void*); +typedef void (*app_set_array_fn)(const char*, const void*, int n, bool store_persistently); +typedef void (*app_set_array_elems_fn)(const char*, const void*, int start, int n, bool store_persistently); +typedef void (*oc_encode_fn)(const void*, bool is_metadata); typedef void (*oc_encode_array_fn)(const void*, int n); typedef void (*oc_encode_array_fn)(const void*, int n); typedef bool (*oc_parse_fn)(oc_rep_t *rep, void *out); @@ -445,6 +445,7 @@ struct datapoint_type_t { app_get_variable_fn app_get_variable; app_get_array_fn app_get_array; app_get_array_elems_fn app_get_array_elems; + app_set_default_value_fn app_set_default_value; app_set_variable_fn app_set_variable; app_set_array_fn app_set_array; app_set_array_elems_fn app_set_array_elems; @@ -462,10 +463,11 @@ const struct datapoint_type_t g_datapoint_types[DatapointType_MAX_NUM] = { {sizeof(float)}, {sizeof(char*)}, { - sizeof(DPT_Param_Bool), + sizeof(DPT_Param_Bool), // DPT_Param_Bool DPT_Param_Bool (app_get_variable_fn)app_get_DPT_Param_Bool_variable, (app_get_array_fn)app_get_DPT_Param_Bool_array, (app_get_array_elems_fn)app_get_DPT_Param_Bool_array_elems, + (app_set_default_value_fn)app_set_DPT_Param_Bool_default_value, (app_set_variable_fn)app_set_DPT_Param_Bool_variable, (app_set_array_fn)app_set_DPT_Param_Bool_array, (app_set_array_elems_fn)app_set_DPT_Param_Bool_array_elems, @@ -477,10 +479,11 @@ const struct datapoint_type_t g_datapoint_types[DatapointType_MAX_NUM] = { (persistent_load_array_fn)persistent_load_DPT_Param_Bool_array, }, { - sizeof(DPT_Shot_Status), + sizeof(DPT_Shot_Status), // DPT_Shot_Status DPT_Shot_Status (app_get_variable_fn)app_get_DPT_Shot_Status_variable, (app_get_array_fn)app_get_DPT_Shot_Status_array, (app_get_array_elems_fn)app_get_DPT_Shot_Status_array_elems, + (app_set_default_value_fn)app_set_DPT_Shot_Status_default_value, (app_set_variable_fn)app_set_DPT_Shot_Status_variable, (app_set_array_fn)app_set_DPT_Shot_Status_array, (app_set_array_elems_fn)app_set_DPT_Shot_Status_array_elems, @@ -492,10 +495,11 @@ const struct datapoint_type_t g_datapoint_types[DatapointType_MAX_NUM] = { (persistent_load_array_fn)persistent_load_DPT_Shot_Status_array, }, { - sizeof(DPT_Start), + sizeof(DPT_Start), // DPT_Start DPT_Start (app_get_variable_fn)app_get_DPT_Start_variable, (app_get_array_fn)app_get_DPT_Start_array, (app_get_array_elems_fn)app_get_DPT_Start_array_elems, + (app_set_default_value_fn)app_set_DPT_Start_default_value, (app_set_variable_fn)app_set_DPT_Start_variable, (app_set_array_fn)app_set_DPT_Start_array, (app_set_array_elems_fn)app_set_DPT_Start_array_elems, @@ -507,10 +511,11 @@ const struct datapoint_type_t g_datapoint_types[DatapointType_MAX_NUM] = { (persistent_load_array_fn)persistent_load_DPT_Start_array, }, { - sizeof(DPT_Uint_XY), + sizeof(DPT_Uint_XY), // DPT_Uint_XY DPT_Uint_XY (app_get_variable_fn)app_get_DPT_Uint_XY_variable, (app_get_array_fn)app_get_DPT_Uint_XY_array, (app_get_array_elems_fn)app_get_DPT_Uint_XY_array_elems, + (app_set_default_value_fn)app_set_DPT_Uint_XY_default_value, (app_set_variable_fn)app_set_DPT_Uint_XY_variable, (app_set_array_fn)app_set_DPT_Uint_XY_array, (app_set_array_elems_fn)app_set_DPT_Uint_XY_array_elems, @@ -542,7 +547,7 @@ static const char *get_datapoint_dpt(const datapoint_t *dp) { static const char *get_datapoint_dpa(const datapoint_t *dp) { for (int i = 0; i < oc_string_array_size(dp->resource.types); i++) { const char *rt = oc_string_array(dp->resource.types)[i]; - if (strstr("dpa.", rt) != NULL) + if (strstr(rt, "dpa.") != NULL) return rt; } return NULL; @@ -552,7 +557,7 @@ static const char *const *get_datapoint_metadata(const datapoint_t *dp) { return dp->metadata; } -static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps) { +static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps, bool is_metadata) { size_t count = dp->num_elements; const struct datapoint_type_t *dpt = &g_datapoint_types[dp->type]; int n = ps; @@ -574,7 +579,7 @@ static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps) { if (ps > 1) g_datapoint_types[dp->type].oc_encode_array(var, n); else - g_datapoint_types[dp->type].oc_encode(var); + g_datapoint_types[dp->type].oc_encode(var, is_metadata); free(var); return true; @@ -596,33 +601,45 @@ static bool oc_parse_datapoint(const datapoint_t *dp, oc_rep_t *rep, void *out, } } -static void datapoint_set(const datapoint_t *dp, void *in, int start, int n) +void datapoint_set(const datapoint_t *dp, void *in, int start, int n) { if (in == NULL) return; if (start > 0 || n > 1){ if (g_datapoint_types[dp->type].app_set_array) - g_datapoint_types[dp->type].app_set_array_elems(get_datapoint_url(dp), in, start, n); + g_datapoint_types[dp->type].app_set_array_elems(get_datapoint_url(dp), in, start, n, true); } else if (g_datapoint_types[dp->type].app_set_variable) g_datapoint_types[dp->type].app_set_variable(get_datapoint_url(dp), in); } -static const datapoint_t *get_datapoint_by_url(const char *url) { +const datapoint_t *get_datapoint_by_url(const char *url) { //this can likely be optimised in the future for speed //with a binary search or similar -for(int i = 0; i < num_datapoints; i++) { - const datapoint_t *it = &g_datapoints[i]; - const char *_url = get_datapoint_url(it); - if (strcmp(_url, url) == 0) { - return it; + + bool is_param = false; + if (strncmp(url,"/p/p",4) == 0) { + is_param = true; + } + + + if (is_param == false) { + for(int i = 0; i < num_datapoints; i++) { + const datapoint_t *it = &g_datapoints[i]; + const char *_url = get_datapoint_url(it); + // it always start with /p/ + if (strcmp((char *)&_url[3], (char *)&url[3]) == 0) { + return it; + } } } -for(int i = 0; i < num_parameters; i++) { + + for(int i = 0; i < num_parameters; i++) { const datapoint_t *it = &g_parameters[i]; const char *_url = get_datapoint_url(it); - if (strcmp(_url, url) == 0) { + // it always start with /p/p + if (strcmp((char*)&_url[4], (char*)&url[4]) == 0) { return it; } } @@ -637,6 +654,41 @@ int app_get_url_array_size(const char *url) return dp->num_elements; } +bool get_module_url(char* out_url, const char* in_url, int module_index) +{ + int digits_after_underscore = 0; + size_t url_length = strlen(in_url); + size_t keep_len; + char temp[50]; + + // Find out how many more digits we have after the underscore + for (int i = url_length - 1; i >= 0; --i) + { + // We have reached the underscore, we can stop counting + if (*(in_url + i) == '_') + break; + + // We have counted one more digit after the underscore + if (isdigit(*(in_url + i))) + ++digits_after_underscore; + else // We have encountered a non-digit and non-underscore, which is unexpected. + return true; + } + + // We can now calculate the number of characters that we need to keep from the original URL + keep_len = url_length - digits_after_underscore; + + // Copy over those characters into a temp array + strncpy(temp, in_url, keep_len); + temp[keep_len] = '\0'; + + // Add the module number at the end, and set the out_url pointer + sprintf(temp + strlen(temp), "%d", module_index); + strcpy(out_url, temp); + + return false; +} + // Getters/Setters for DPT_Param_Bool bool app_is_DPT_Param_Bool_url(const char* url) @@ -646,14 +698,19 @@ bool app_is_DPT_Param_Bool_url(const char* url) return false; } - - if (dp->type == DatapointType_int) { + if (dp->type == DatapointType_DPT_Param_Bool) { return true; } return false; } -void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* in, int start, int n) +void app_set_DPT_Param_Bool_default_value(const char* url) +{ + (void)url; + // No default value, do nothing... +} + +void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* in, int start, int n, bool store_persistently) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -671,10 +728,10 @@ void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* i } if (dp->g_var) { memcpy(&((DPT_Param_Bool*)dp->g_var)[start], in, sizeof(DPT_Param_Bool)*n); - if (dp->persistent) { + if (dp->persistent && store_persistently) { persistent_store_DPT_Param_Bool_array(get_datapoint_url(dp), (DPT_Param_Bool*)dp->g_var, max_elem); } - }else if (dp->persistent) { + }else if (dp->persistent && store_persistently) { int count = dp->num_elements; count = count?count:1; DPT_Param_Bool *data = calloc(sizeof(DPT_Param_Bool), count); @@ -685,17 +742,17 @@ void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* i return; } -void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int n) +void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int n, bool store_persistently) { - app_set_DPT_Param_Bool_array_elems(url, in, 0, n); + app_set_DPT_Param_Bool_array_elems(url, in, 0, n, store_persistently); } void app_set_DPT_Param_Bool_variable(const char* url, const DPT_Param_Bool* in) { - app_set_DPT_Param_Bool_array(url, in, 1); + app_set_DPT_Param_Bool_array(url, in, 1, true); } -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array_elems(const char *url, DPT_Param_Bool* out, int start, int n) +const DPT_Param_Bool* app_get_DPT_Param_Bool_array_elems(const char *url, DPT_Param_Bool* out, int start, int n) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -731,12 +788,12 @@ const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array_elems(const char *ur return NULL; } -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array(const char *url, DPT_Param_Bool* out, int n) +const DPT_Param_Bool* app_get_DPT_Param_Bool_array(const char *url, DPT_Param_Bool* out, int n) { return app_get_DPT_Param_Bool_array_elems(url, out, 0, n); } -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, DPT_Param_Bool* out) +const DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, DPT_Param_Bool* out) { return app_get_DPT_Param_Bool_array(url, out, 1); } @@ -744,10 +801,17 @@ const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, bool oc_parse_DPT_Param_Bool_single(oc_rep_t *rep, DPT_Param_Bool *out) { oc_array_t arr; - // this really shouldn't be void* - // we need to find a better way. +// ('Enumeration', OrderedDict([('@Id', 'DPST-60012-3_F-1'), ('@Name', 'ValueBool'), ('@Width', '8'), ('EnumValue', [OrderedDict([('@Id', 'DPST-60012-3_F-1-1'), ('@Value', '0'), ('@Text', 'False')]), OrderedDict([('@Id', 'DPST-60012-3_F-1-2'), ('@Value', '1'), ('@Text', 'True')])])])) if (rep->type != OC_REP_INT) return false; - return oc_rep_i_get_int(rep, 1, (void*)out); + int64_t temp = 0; + int64_t *out_temp; + out_temp = &temp; + bool ret = oc_rep_i_get_int(rep, 1, out_temp); + DPT_Param_Bool temp2 = (DPT_Param_Bool)*out_temp; + *out = temp2; + + + return ret; } bool oc_parse_DPT_Param_Bool(oc_rep_t *rep, DPT_Param_Bool *out) @@ -788,17 +852,21 @@ void oc_encode_DPT_Param_Bool_single(CborEncoder *parent, const DPT_Param_Bool * return; } oc_rep_i_set_key(parent, 1); - cbor_encode_int(parent, *in); + cbor_encode_int(parent, (int64_t)*in); } -void oc_encode_DPT_Param_Bool(const DPT_Param_Bool *in) +void oc_encode_DPT_Param_Bool(const DPT_Param_Bool *in, bool is_metadata) { if (in == NULL) { return; } - oc_rep_begin_root_object(); + if (!is_metadata) { // Don't begin & end root object as it's already done during metadata handling + oc_rep_begin_root_object(); + } oc_encode_DPT_Param_Bool_single(oc_rep_object(root), in); - oc_rep_end_root_object(); + if (!is_metadata) { + oc_rep_end_root_object(); + } } void oc_encode_DPT_Param_Bool_array(const DPT_Param_Bool *in, int n) @@ -823,15 +891,21 @@ void persistent_store_DPT_Param_Bool(const char *name, const DPT_Param_Bool *in) void persistent_store_DPT_Param_Bool_array(const char *name, const DPT_Param_Bool *in, int n) { uint8_t *rep_buf; + long ret; const size_t max_size = 8 * n + 2; rep_buf = malloc(max_size); - char store_name[32] = STORE_PREFIX; +#ifdef OPTIMIZE_STORAGE +// use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else + char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); while(pos) { *pos = '_'; pos = strchr(store_name, '/'); } +#endif oc_rep_new(rep_buf, max_size); oc_encode_DPT_Param_Bool_array(in, n); @@ -842,10 +916,14 @@ void persistent_store_DPT_Param_Bool_array(const char *name, const DPT_Param_Boo PRINT_APP("%02X ", rep_buf[i]); } PRINT_APP("\n"); - oc_storage_write(store_name, rep_buf, size); + ret = oc_storage_write(store_name, rep_buf, size); }else{ PRINT_APP("Error encoding DPT_Param_Bool %s for storage\n", name); } + + if (ret <= 0) { + PRINT_APP("oc_storage_write failed with error: %d\n", -ret); + } free(rep_buf); } @@ -859,6 +937,10 @@ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, oc_rep_t *rep = NULL; int max_size = 8 * n + 2; uint8_t *oc_storage_buf; +#ifdef OPTIMIZE_STORAGE + // use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); @@ -866,10 +948,12 @@ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, *pos = '_'; pos = strchr(store_name, '/'); } +#endif long ret; bool error = true; struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; oc_storage_buf = malloc(max_size); + struct oc_memb *prev_rep_obj = oc_rep_get_pool(); oc_rep_set_pool(&rep_objects); ret = oc_storage_read(store_name, oc_storage_buf, max_size); @@ -879,6 +963,7 @@ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, } PRINT_APP("\n"); if (ret <= 0) { + PRINT_APP("oc_storage_read failed with error: %d\n", -ret); goto err; } if (oc_parse_rep(oc_storage_buf, ret, &rep) != CborNoError) { @@ -894,9 +979,66 @@ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, if(error) { oc_storage_erase(name); } + oc_rep_set_pool(prev_rep_obj); return !error; } +int app_sprintf_DPT_Param_Bool(const DPT_Param_Bool *in, char* text, int size) +{ + char item[50]; + memset(text, 0, size); + if (in == NULL) { + return 1; + } + sprintf(item," %d ", *in); + strcat(text, item); + + return 0; +} + +int app_sscanf_DPT_Param_Bool(DPT_Param_Bool *in, char* text) +{ + char temp_string[300]; + const char s[2] = " "; + char *token; + + if (text == NULL) { + return 1; + } + memset(temp_string, 0, 300); + strncpy(temp_string, text, 299); + int retval = 0; + + // strtok is destructing the input string so copy it first + token = strtok(temp_string, s); + // Enumeration + retval = sscanf(token, " %d", in); + if (retval != 1) return 1; + token = strtok(NULL, s); + + return 0; +} + +int app_str_expected_DPT_Param_Bool(int select, char* text) +{ + if (text == NULL) { + return 1; + } + if (select == 1) { + // Enumeration + strcat(text, " %d"); + return 0; + } + + if (select == 2) { + strcat(text, " 0"); + return 0; + } + + return 1; +} + + // Getters/Setters for DPT_Shot_Status @@ -907,14 +1049,19 @@ bool app_is_DPT_Shot_Status_url(const char* url) return false; } - if (dp->type == DatapointType_DPT_Shot_Status) { return true; } return false; } -void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* in, int start, int n) +void app_set_DPT_Shot_Status_default_value(const char* url) +{ + (void)url; + // No default value, do nothing... +} + +void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* in, int start, int n, bool store_persistently) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -932,10 +1079,10 @@ void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* } if (dp->g_var) { memcpy(&((DPT_Shot_Status*)dp->g_var)[start], in, sizeof(DPT_Shot_Status)*n); - if (dp->persistent) { + if (dp->persistent && store_persistently) { persistent_store_DPT_Shot_Status_array(get_datapoint_url(dp), (DPT_Shot_Status*)dp->g_var, max_elem); } - }else if (dp->persistent) { + }else if (dp->persistent && store_persistently) { int count = dp->num_elements; count = count?count:1; DPT_Shot_Status *data = calloc(sizeof(DPT_Shot_Status), count); @@ -946,17 +1093,17 @@ void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* return; } -void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, int n) +void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, int n, bool store_persistently) { - app_set_DPT_Shot_Status_array_elems(url, in, 0, n); + app_set_DPT_Shot_Status_array_elems(url, in, 0, n, store_persistently); } void app_set_DPT_Shot_Status_variable(const char* url, const DPT_Shot_Status* in) { - app_set_DPT_Shot_Status_array(url, in, 1); + app_set_DPT_Shot_Status_array(url, in, 1, true); } -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array_elems(const char *url, DPT_Shot_Status* out, int start, int n) +const DPT_Shot_Status* app_get_DPT_Shot_Status_array_elems(const char *url, DPT_Shot_Status* out, int start, int n) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -992,12 +1139,12 @@ const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array_elems(const char * return NULL; } -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array(const char *url, DPT_Shot_Status* out, int n) +const DPT_Shot_Status* app_get_DPT_Shot_Status_array(const char *url, DPT_Shot_Status* out, int n) { return app_get_DPT_Shot_Status_array_elems(url, out, 0, n); } -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url, DPT_Shot_Status* out) +const DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url, DPT_Shot_Status* out) { return app_get_DPT_Shot_Status_array(url, out, 1); } @@ -1014,11 +1161,12 @@ bool oc_parse_DPT_Shot_Status_single(oc_rep_t *rep, DPT_Shot_Status *out) arr = rep->value.array; if (oc_bool_array_size(arr) < 2) return false; - out->_DPST600041_F1 = oc_bool_array(arr)[0]; - out->_DPST600041_F2 = oc_bool_array(arr)[1]; + out->DPST_60004_1_F_1 = oc_bool_array(arr)[0]; + out->DPST_60004_1_F_2 = oc_bool_array(arr)[1]; rep = rep->next; - if (rep == NULL) return false;if (rep->type != OC_REP_INT) return false; - out->_DPST600041_F3 = rep->value.integer; + if (rep == NULL) return false; + if (rep->type != OC_REP_INT) return false; + out->DPST_60004_1_F_3 = rep->value.integer; return true; } @@ -1062,21 +1210,25 @@ void oc_encode_DPT_Shot_Status_single(CborEncoder *parent, const DPT_Shot_Status oc_rep_i_set_key(parent, 1); oc_rep_begin_array(parent, arr); oc_rep_begin_array(oc_rep_array(arr), bool); - oc_rep_add_boolean(bool, in->_DPST600041_F1); - oc_rep_add_boolean(bool, in->_DPST600041_F2); + oc_rep_add_boolean(bool, in->DPST_60004_1_F_1); + oc_rep_add_boolean(bool, in->DPST_60004_1_F_2); oc_rep_end_array(oc_rep_array(arr), bool); - oc_rep_add_int(arr, in->_DPST600041_F3); + oc_rep_add_int(arr, in->DPST_60004_1_F_3); oc_rep_end_array(parent, arr); } -void oc_encode_DPT_Shot_Status(const DPT_Shot_Status *in) +void oc_encode_DPT_Shot_Status(const DPT_Shot_Status *in, bool is_metadata) { if (in == NULL) { return; } - oc_rep_begin_root_object(); + if (!is_metadata) { // Don't begin & end root object as it's already done during metadata handling + oc_rep_begin_root_object(); + } oc_encode_DPT_Shot_Status_single(oc_rep_object(root), in); - oc_rep_end_root_object(); + if (!is_metadata) { + oc_rep_end_root_object(); + } } void oc_encode_DPT_Shot_Status_array(const DPT_Shot_Status *in, int n) @@ -1101,15 +1253,21 @@ void persistent_store_DPT_Shot_Status(const char *name, const DPT_Shot_Status *i void persistent_store_DPT_Shot_Status_array(const char *name, const DPT_Shot_Status *in, int n) { uint8_t *rep_buf; + long ret; const size_t max_size = 14 * n + 2; rep_buf = malloc(max_size); - char store_name[32] = STORE_PREFIX; +#ifdef OPTIMIZE_STORAGE +// use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else + char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); while(pos) { *pos = '_'; pos = strchr(store_name, '/'); } +#endif oc_rep_new(rep_buf, max_size); oc_encode_DPT_Shot_Status_array(in, n); @@ -1120,10 +1278,14 @@ void persistent_store_DPT_Shot_Status_array(const char *name, const DPT_Shot_Sta PRINT_APP("%02X ", rep_buf[i]); } PRINT_APP("\n"); - oc_storage_write(store_name, rep_buf, size); + ret = oc_storage_write(store_name, rep_buf, size); }else{ PRINT_APP("Error encoding DPT_Shot_Status %s for storage\n", name); } + + if (ret <= 0) { + PRINT_APP("oc_storage_write failed with error: %d\n", -ret); + } free(rep_buf); } @@ -1137,6 +1299,10 @@ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *ou oc_rep_t *rep = NULL; int max_size = 14 * n + 2; uint8_t *oc_storage_buf; +#ifdef OPTIMIZE_STORAGE + // use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); @@ -1144,10 +1310,12 @@ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *ou *pos = '_'; pos = strchr(store_name, '/'); } +#endif long ret; bool error = true; struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; oc_storage_buf = malloc(max_size); + struct oc_memb *prev_rep_obj = oc_rep_get_pool(); oc_rep_set_pool(&rep_objects); ret = oc_storage_read(store_name, oc_storage_buf, max_size); @@ -1157,6 +1325,7 @@ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *ou } PRINT_APP("\n"); if (ret <= 0) { + PRINT_APP("oc_storage_read failed with error: %d\n", -ret); goto err; } if (oc_parse_rep(oc_storage_buf, ret, &rep) != CborNoError) { @@ -1172,9 +1341,90 @@ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *ou if(error) { oc_storage_erase(name); } + oc_rep_set_pool(prev_rep_obj); return !error; } +int app_sprintf_DPT_Shot_Status(const DPT_Shot_Status *in, char* text, int size) +{ + char item[50]; + memset(text, 0, size); + if (in == NULL) { + return 1; + } + sprintf(item," %d ", in->DPST_60004_1_F_1); + strcat(text, item); + sprintf(item," %d ", in->DPST_60004_1_F_2); + strcat(text, item); + sprintf(item," %d ", in->DPST_60004_1_F_3); + strcat(text, item); + + return 0; +} + +int app_sscanf_DPT_Shot_Status(DPT_Shot_Status *in, char* text) +{ + char temp_string[300]; + const char s[2] = " "; + char *token; + + if (text == NULL) { + return 1; + } + memset(temp_string, 0, 300); + strncpy(temp_string, text, 299); + int retval = 0; + + // strtok is destructing the input string so copy it first + token = strtok(temp_string, s); + { + int var; + retval = sscanf(token, " %d", &var); + in->DPST_60004_1_F_1 = (bool)var; + } + if (retval != 1) return 1; + token = strtok(NULL, s); + { + int var; + retval = sscanf(token, " %d", &var); + in->DPST_60004_1_F_2 = (bool)var; + } + if (retval != 1) return 1; + token = strtok(NULL, s); + { + int var; + retval = sscanf(token, " %d", &var); + if (retval != 1) return 1; + in->DPST_60004_1_F_3 = var; + } + token = strtok(NULL, s); + + return 0; +} + +int app_str_expected_DPT_Shot_Status(int select, char* text) +{ + if (text == NULL) { + return 1; + } + if (select == 1) { + strcat(text, " %d"); + strcat(text, " %d"); + strcat(text, " %d"); + return 0; + } + + if (select == 2) { + strcat(text, " 0"); + strcat(text, " 0"); + strcat(text, " 0"); + return 0; + } + + return 1; +} + + // Getters/Setters for DPT_Start @@ -1185,14 +1435,19 @@ bool app_is_DPT_Start_url(const char* url) return false; } - if (dp->type == DatapointType_DPT_Start) { return true; } return false; } -void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int start, int n) +void app_set_DPT_Start_default_value(const char* url) +{ + (void)url; + // No default value, do nothing... +} + +void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int start, int n, bool store_persistently) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -1210,10 +1465,10 @@ void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int sta } if (dp->g_var) { memcpy(&((DPT_Start*)dp->g_var)[start], in, sizeof(DPT_Start)*n); - if (dp->persistent) { + if (dp->persistent && store_persistently) { persistent_store_DPT_Start_array(get_datapoint_url(dp), (DPT_Start*)dp->g_var, max_elem); } - }else if (dp->persistent) { + }else if (dp->persistent && store_persistently) { int count = dp->num_elements; count = count?count:1; DPT_Start *data = calloc(sizeof(DPT_Start), count); @@ -1224,17 +1479,17 @@ void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int sta return; } -void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n) +void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n, bool store_persistently) { - app_set_DPT_Start_array_elems(url, in, 0, n); + app_set_DPT_Start_array_elems(url, in, 0, n, store_persistently); } void app_set_DPT_Start_variable(const char* url, const DPT_Start* in) { - app_set_DPT_Start_array(url, in, 1); + app_set_DPT_Start_array(url, in, 1, true); } -const volatile DPT_Start* app_get_DPT_Start_array_elems(const char *url, DPT_Start* out, int start, int n) +const DPT_Start* app_get_DPT_Start_array_elems(const char *url, DPT_Start* out, int start, int n) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -1270,12 +1525,12 @@ const volatile DPT_Start* app_get_DPT_Start_array_elems(const char *url, DPT_Sta return NULL; } -const volatile DPT_Start* app_get_DPT_Start_array(const char *url, DPT_Start* out, int n) +const DPT_Start* app_get_DPT_Start_array(const char *url, DPT_Start* out, int n) { return app_get_DPT_Start_array_elems(url, out, 0, n); } -const volatile DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* out) +const DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* out) { return app_get_DPT_Start_array(url, out, 1); } @@ -1283,10 +1538,13 @@ const volatile DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* bool oc_parse_DPT_Start_single(oc_rep_t *rep, DPT_Start *out) { oc_array_t arr; - // this really shouldn't be void* - // we need to find a better way. +// ('bool', OrderedDict([('@Id', 'DPST-1-10_F-1'), ('@Cleared', 'Stop'), ('@Set', 'Start')])) + if (rep->type != OC_REP_BOOL) return false; if (rep->type != OC_REP_BOOL) return false; - return oc_rep_i_get_bool(rep, 1, (void*)out); + // todo Fix void + bool ret = oc_rep_i_get_bool(rep, 1, (void*)out); + + return ret; } bool oc_parse_DPT_Start(oc_rep_t *rep, DPT_Start *out) @@ -1327,17 +1585,21 @@ void oc_encode_DPT_Start_single(CborEncoder *parent, const DPT_Start *in) return; } oc_rep_i_set_key(parent, 1); - cbor_encode_boolean(parent, *in); + cbor_encode_boolean(parent, (bool)*in); } -void oc_encode_DPT_Start(const DPT_Start *in) +void oc_encode_DPT_Start(const DPT_Start *in, bool is_metadata) { if (in == NULL) { return; } - oc_rep_begin_root_object(); + if (!is_metadata) { // Don't begin & end root object as it's already done during metadata handling + oc_rep_begin_root_object(); + } oc_encode_DPT_Start_single(oc_rep_object(root), in); - oc_rep_end_root_object(); + if (!is_metadata) { + oc_rep_end_root_object(); + } } void oc_encode_DPT_Start_array(const DPT_Start *in, int n) @@ -1362,15 +1624,21 @@ void persistent_store_DPT_Start(const char *name, const DPT_Start *in) void persistent_store_DPT_Start_array(const char *name, const DPT_Start *in, int n) { uint8_t *rep_buf; + long ret; const size_t max_size = 4 * n + 2; rep_buf = malloc(max_size); - char store_name[32] = STORE_PREFIX; +#ifdef OPTIMIZE_STORAGE +// use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else + char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); while(pos) { *pos = '_'; pos = strchr(store_name, '/'); } +#endif oc_rep_new(rep_buf, max_size); oc_encode_DPT_Start_array(in, n); @@ -1381,10 +1649,14 @@ void persistent_store_DPT_Start_array(const char *name, const DPT_Start *in, int PRINT_APP("%02X ", rep_buf[i]); } PRINT_APP("\n"); - oc_storage_write(store_name, rep_buf, size); + ret = oc_storage_write(store_name, rep_buf, size); }else{ PRINT_APP("Error encoding DPT_Start %s for storage\n", name); } + + if (ret <= 0) { + PRINT_APP("oc_storage_write failed with error: %d\n", -ret); + } free(rep_buf); } @@ -1398,6 +1670,10 @@ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n) oc_rep_t *rep = NULL; int max_size = 4 * n + 2; uint8_t *oc_storage_buf; +#ifdef OPTIMIZE_STORAGE + // use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); @@ -1405,10 +1681,12 @@ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n) *pos = '_'; pos = strchr(store_name, '/'); } +#endif long ret; bool error = true; struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; oc_storage_buf = malloc(max_size); + struct oc_memb *prev_rep_obj = oc_rep_get_pool(); oc_rep_set_pool(&rep_objects); ret = oc_storage_read(store_name, oc_storage_buf, max_size); @@ -1418,6 +1696,7 @@ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n) } PRINT_APP("\n"); if (ret <= 0) { + PRINT_APP("oc_storage_read failed with error: %d\n", -ret); goto err; } if (oc_parse_rep(oc_storage_buf, ret, &rep) != CborNoError) { @@ -1433,9 +1712,69 @@ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n) if(error) { oc_storage_erase(name); } + oc_rep_set_pool(prev_rep_obj); return !error; } +int app_sprintf_DPT_Start(const DPT_Start *in, char* text, int size) +{ + char item[50]; + memset(text, 0, size); + if (in == NULL) { + return 1; + } + sprintf(item," %d ", *in); + strcat(text, item); + + return 0; +} + +int app_sscanf_DPT_Start(DPT_Start *in, char* text) +{ + char temp_string[300]; + const char s[2] = " "; + char *token; + + if (text == NULL) { + return 1; + } + memset(temp_string, 0, 300); + strncpy(temp_string, text, 299); + int retval = 0; + + // strtok is destructing the input string so copy it first + token = strtok(temp_string, s); + // bool + { + int var; + retval = sscanf(token, " %d", &var); + if (retval != 1) return 1; + *in = (bool)var; + } + + return 0; +} + +int app_str_expected_DPT_Start(int select, char* text) +{ + if (text == NULL) { + return 1; + } + if (select == 1) { + // bool + strcat(text, " %d"); + return 0; + } + + if (select == 2) { + strcat(text, " 0"); + return 0; + } + + return 1; +} + + // Getters/Setters for DPT_Uint_XY @@ -1446,14 +1785,19 @@ bool app_is_DPT_Uint_XY_url(const char* url) return false; } - if (dp->type == DatapointType_DPT_Uint_XY) { return true; } return false; } -void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int start, int n) +void app_set_DPT_Uint_XY_default_value(const char* url) +{ + (void)url; + // No default value, do nothing... +} + +void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int start, int n, bool store_persistently) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -1471,10 +1815,10 @@ void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int } if (dp->g_var) { memcpy(&((DPT_Uint_XY*)dp->g_var)[start], in, sizeof(DPT_Uint_XY)*n); - if (dp->persistent) { + if (dp->persistent && store_persistently) { persistent_store_DPT_Uint_XY_array(get_datapoint_url(dp), (DPT_Uint_XY*)dp->g_var, max_elem); } - }else if (dp->persistent) { + }else if (dp->persistent && store_persistently) { int count = dp->num_elements; count = count?count:1; DPT_Uint_XY *data = calloc(sizeof(DPT_Uint_XY), count); @@ -1485,17 +1829,17 @@ void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int return; } -void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n) +void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n, bool store_persistently) { - app_set_DPT_Uint_XY_array_elems(url, in, 0, n); + app_set_DPT_Uint_XY_array_elems(url, in, 0, n, store_persistently); } void app_set_DPT_Uint_XY_variable(const char* url, const DPT_Uint_XY* in) { - app_set_DPT_Uint_XY_array(url, in, 1); + app_set_DPT_Uint_XY_array(url, in, 1, true); } -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array_elems(const char *url, DPT_Uint_XY* out, int start, int n) +const DPT_Uint_XY* app_get_DPT_Uint_XY_array_elems(const char *url, DPT_Uint_XY* out, int start, int n) { const datapoint_t *dp = get_datapoint_by_url(url); if (dp == NULL) { @@ -1531,12 +1875,12 @@ const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array_elems(const char *url, DPT return NULL; } -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array(const char *url, DPT_Uint_XY* out, int n) +const DPT_Uint_XY* app_get_DPT_Uint_XY_array(const char *url, DPT_Uint_XY* out, int n) { return app_get_DPT_Uint_XY_array_elems(url, out, 0, n); } -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_variable(const char *url, DPT_Uint_XY* out) +const DPT_Uint_XY* app_get_DPT_Uint_XY_variable(const char *url, DPT_Uint_XY* out) { return app_get_DPT_Uint_XY_array(url, out, 1); } @@ -1548,8 +1892,9 @@ bool oc_parse_DPT_Uint_XY_single(oc_rep_t *rep, DPT_Uint_XY *out) arr = rep->value.array; if (oc_int_array_size(arr) < 2) return false; - out->_DPST600091_F1 = oc_int_array(arr)[0]; - out->_DPST600091_F2 = oc_int_array(arr)[1]; + out->DPST_60009_1_F_1 = oc_int_array(arr)[0]; + out->DPST_60009_1_F_2 = oc_int_array(arr)[1]; + return true; } bool oc_parse_DPT_Uint_XY(oc_rep_t *rep, DPT_Uint_XY *out) @@ -1591,19 +1936,23 @@ void oc_encode_DPT_Uint_XY_single(CborEncoder *parent, const DPT_Uint_XY *in) } oc_rep_i_set_key(parent, 1); oc_rep_begin_array(parent, arr); - oc_rep_add_int(arr, in->_DPST600091_F1); - oc_rep_add_int(arr, in->_DPST600091_F2); + oc_rep_add_int(arr, in->DPST_60009_1_F_1); + oc_rep_add_int(arr, in->DPST_60009_1_F_2); oc_rep_end_array(parent, arr); } -void oc_encode_DPT_Uint_XY(const DPT_Uint_XY *in) +void oc_encode_DPT_Uint_XY(const DPT_Uint_XY *in, bool is_metadata) { if (in == NULL) { return; } - oc_rep_begin_root_object(); + if (!is_metadata) { // Don't begin & end root object as it's already done during metadata handling + oc_rep_begin_root_object(); + } oc_encode_DPT_Uint_XY_single(oc_rep_object(root), in); - oc_rep_end_root_object(); + if (!is_metadata) { + oc_rep_end_root_object(); + } } void oc_encode_DPT_Uint_XY_array(const DPT_Uint_XY *in, int n) @@ -1628,15 +1977,21 @@ void persistent_store_DPT_Uint_XY(const char *name, const DPT_Uint_XY *in) void persistent_store_DPT_Uint_XY_array(const char *name, const DPT_Uint_XY *in, int n) { uint8_t *rep_buf; + long ret; const size_t max_size = 15 * n + 2; rep_buf = malloc(max_size); - char store_name[32] = STORE_PREFIX; +#ifdef OPTIMIZE_STORAGE +// use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else + char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); while(pos) { *pos = '_'; pos = strchr(store_name, '/'); } +#endif oc_rep_new(rep_buf, max_size); oc_encode_DPT_Uint_XY_array(in, n); @@ -1647,10 +2002,14 @@ void persistent_store_DPT_Uint_XY_array(const char *name, const DPT_Uint_XY *in, PRINT_APP("%02X ", rep_buf[i]); } PRINT_APP("\n"); - oc_storage_write(store_name, rep_buf, size); + ret = oc_storage_write(store_name, rep_buf, size); }else{ PRINT_APP("Error encoding DPT_Uint_XY %s for storage\n", name); } + + if (ret <= 0) { + PRINT_APP("oc_storage_write failed with error: %d\n", -ret); + } free(rep_buf); } @@ -1664,6 +2023,10 @@ bool persistent_load_DPT_Uint_XY_array(const char *name, DPT_Uint_XY *out, int n oc_rep_t *rep = NULL; int max_size = 15 * n + 2; uint8_t *oc_storage_buf; +#ifdef OPTIMIZE_STORAGE + // use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); @@ -1671,10 +2034,12 @@ bool persistent_load_DPT_Uint_XY_array(const char *name, DPT_Uint_XY *out, int n *pos = '_'; pos = strchr(store_name, '/'); } +#endif long ret; bool error = true; struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; oc_storage_buf = malloc(max_size); + struct oc_memb *prev_rep_obj = oc_rep_get_pool(); oc_rep_set_pool(&rep_objects); ret = oc_storage_read(store_name, oc_storage_buf, max_size); @@ -1684,6 +2049,7 @@ bool persistent_load_DPT_Uint_XY_array(const char *name, DPT_Uint_XY *out, int n } PRINT_APP("\n"); if (ret <= 0) { + PRINT_APP("oc_storage_read failed with error: %d\n", -ret); goto err; } if (oc_parse_rep(oc_storage_buf, ret, &rep) != CborNoError) { @@ -1699,8 +2065,78 @@ bool persistent_load_DPT_Uint_XY_array(const char *name, DPT_Uint_XY *out, int n if(error) { oc_storage_erase(name); } + oc_rep_set_pool(prev_rep_obj); return !error; } + +int app_sprintf_DPT_Uint_XY(const DPT_Uint_XY *in, char* text, int size) +{ + char item[50]; + memset(text, 0, size); + if (in == NULL) { + return 1; + } + sprintf(item," %u ", in->DPST_60009_1_F_1); + strcat(text, item); + sprintf(item," %u ", in->DPST_60009_1_F_2); + strcat(text, item); + + return 0; +} + +int app_sscanf_DPT_Uint_XY(DPT_Uint_XY *in, char* text) +{ + char temp_string[300]; + const char s[2] = " "; + char *token; + + if (text == NULL) { + return 1; + } + memset(temp_string, 0, 300); + strncpy(temp_string, text, 299); + int retval = 0; + + // strtok is destructing the input string so copy it first + token = strtok(temp_string, s); + { + unsigned int var; + retval = sscanf(token, " %u", &var); + if (retval != 1) return 1; + in->DPST_60009_1_F_1 = var; + } + token = strtok(NULL, s); + { + unsigned int var; + retval = sscanf(token, " %u", &var); + if (retval != 1) return 1; + in->DPST_60009_1_F_2 = var; + } + token = strtok(NULL, s); + + return 0; +} + +int app_str_expected_DPT_Uint_XY(int select, char* text) +{ + if (text == NULL) { + return 1; + } + if (select == 1) { + strcat(text, " %u"); + strcat(text, " %u"); + return 0; + } + + if (select == 2) { + strcat(text, " 0"); + strcat(text, " 0"); + return 0; + } + + return 1; +} + // BOOLEAN code /** @@ -1761,6 +2197,31 @@ bool app_retrieve_bool_variable(const char* url) return false; } + +/** + * @brief retrieve the global int variable at the url + * + * @param url the url indicating the global variable + * @param value ptr to value (which is going to be set) + * @return the true if variable is set + */ +bool app_retrieve_int_variable(const char* url, int* value) +{ + const datapoint_t *dp = get_datapoint_by_url(url); + if (dp == NULL) { + return false; + } + if (dp->type != DatapointType_int) { + return false; + } + if (dp->g_var) { + value = ((int*)dp->g_var); + return true; + } + return false; +} + + // FAULT code /** @@ -1775,7 +2236,7 @@ void app_set_fault_variable(const char* url, bool value) if (dp == NULL) { return; } - if (dp->resource.interfaces & OC_IF_A == 0) { + if ((dp->resource.interfaces & OC_IF_A) == 0) { return; } if (dp->g_fault) { @@ -1809,6 +2270,11 @@ bool app_retrieve_fault_variable(const char* url) bool app_is_url_parameter(const char* url) { + // all parametes start with /p/pxxx + if (strncmp(url,"/p/p",4) == 0 ) { + return false; + } + for (int i = 0; i < num_parameters; i++) { const datapoint_t *dp = &g_parameters[i]; if (strcmp(get_datapoint_url(dp), url) == 0) { @@ -1894,6 +2360,7 @@ int app_initialize_stack(); void signal_event_loop(void); void register_resources(void); void initialize_variables(); +void reset_variables(); void logic_initialize(); int app_init(void); #ifdef __cplusplus @@ -1949,11 +2416,11 @@ oc_add_s_mode_response_cb(char *url, oc_rep_t *rep, oc_rep_t *rep_value) * */ void app_str_to_upper(char *str){ - while (*str != '\0') - { - *str = toupper(*str); - str++; - } + while (*str != '\0') + { + *str = toupper(*str); + str++; + } } /** @@ -1966,7 +2433,7 @@ void app_str_to_upper(char *str){ * - knx spec version * - hardware version : [0, 4, 0] * - firmware version : [0, 4, 0] - * - hardware type : Windows + * - hardware type : 000000000000 * - device model : KNX Battleships Demo eink * */ @@ -1981,6 +2448,25 @@ app_init(void) oc_device_info_t *device = oc_core_get_device_info(0); +#ifdef LINUX + #define MAX_HOSTNAME 50 + char hostname[50]; + // set the hostname + ret = gethostname(&hostname[0], 50); + if (ret != -1) { + printf("Hostname (linux): %s\n", hostname); + oc_core_set_device_hostname(0, hostname); + } +#endif +#ifdef WIN32 + char hostname_str[50]; + int error = gethostname(hostname_str, 50); + if (error == 0) { + printf("Hostname (win32): %s\n", hostname_str); + oc_core_set_device_hostname(0, hostname_str); + } +#endif + /* set the hardware version 0.4.0 */ oc_core_set_device_hwv(0, 0, 4, 0); @@ -1989,10 +2475,11 @@ app_init(void) /* set the firmware version 0.4.0 */ oc_core_set_device_fwv(0, 0, 4, 0); - + + /* manufacturer id */ + oc_core_set_device_mid(0, 0x00FA); /* set the hardware type*/ - oc_core_set_device_hwt(0, "00696e646f77"); - + oc_core_set_device_hwt(0, "000000000000"); /* set the model */ oc_core_set_device_model(0, "KNX Battleships Demo eink"); @@ -2014,24 +2501,10 @@ app_init(void) app_initialize(); #endif if (oc_is_device_in_runtime(0) == false) { - static struct qr_code_t knx_qr; - static char knx_qr_buf[32+10+12+1]; - char serial_number_uppercase[20]; - strncpy(serial_number_uppercase, oc_string(device->serialnumber), 19); - app_str_to_upper(serial_number_uppercase); - // KNX QR code, example: note: SN should be upper case - // KNX:S:00FA10010400;P:ABY8B77J50YXMUDW3DG4 - char* sn = oc_string(device->serialnumber); - char* pw = app_get_password(); - snprintf(knx_qr_buf, sizeof(knx_qr_buf)-1, "KNX:S:%s;P:%s",serial_number_uppercase, pw); - - knx_qr.str_data = knx_qr_buf; - knx_qr.desc = "KNX QR code"; #ifdef NO_MAIN // only for embedded systems - register_qr_code(&knx_qr); - PRINT_APP("Registered KNX QR code"); -#endif + refresh_screen(true); +#endif /* NO_MAIN */ } return ret; @@ -2055,8 +2528,9 @@ bool request_query_get_int(oc_request_t *request, const char *query, int *out) char *query_str; int query_len = oc_get_query_value(request, query, &query_str); - if(query_len <= 0) + if(query_len <= 0) { return false; + } char buf[16]; strncpy(buf, query_str, query_len); *out = atoi(buf); @@ -2070,13 +2544,18 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da const datapoint_t *dp = user_data; bool error_state = false; /* the error state, the generated code */ - PRINT("-- Begin get_generic (%s) \n", get_datapoint_name(dp)); if (dp == NULL) { - PRINT("Error dp is NULL\n"); - oc_send_response(request, OC_STATUS_INTERNAL_SERVER_ERROR); - return; + dp = get_datapoint_by_url(request->uri_path); + if (dp == NULL) { + PRINT("Error dp is NULL\n"); + oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); + return; + } } + + PRINT("-- Begin get_generic (%s) \n", get_datapoint_name(dp)); + /* MANUFACTORER: SENSOR add here the code to talk to the HW if one implements a sensor. the call to the HW needs to fill in the global variable before it returns to this function here. alternative is to have a callback from the @@ -2085,7 +2564,7 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da /* check if the accept header is CBOR */ if (oc_check_accept_header(request, APPLICATION_CBOR) == false) { - oc_send_response(request, OC_STATUS_BAD_OPTION); + oc_send_response_no_format(request, OC_STATUS_BAD_OPTION); return; } @@ -2098,31 +2577,51 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da if(!request_query_get_int(request, "pn", &pn) || dp->num_elements == 0) pn = 0; if(!request_query_get_int(request, "ps", &ps) || dp->num_elements == 0) ps = 1; if (m_len != -1) { - PRINT(" Query param: %.*s",(int)m_len, m); + PRINT(" Query param: %.*s\n",(int)m_len, m); oc_init_query_iterator(); size_t device_index = request->resource->device; oc_device_info_t *device = oc_core_get_device_info(device_index); if (device != NULL) { + bool m_valid = false; oc_rep_begin_root_object(); while (oc_iterate_query(request, &m_key, &m_key_len, &m, &m_len) != -1) { + // value + if ((strncmp(m, "value", m_len) == 0) | + (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; + if (oc_encode_datapoint(dp, pn, ps, true) == false) { + oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); + goto done; + } + } // unique identifier if ((strncmp(m, "id", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; char mystring[100]; - snprintf(mystring,99,"urn:knx:sn:%s%s",oc_string(device->serialnumber), - oc_string(request->resource->uri)); - oc_rep_i_set_text_string(root, 9, mystring); + snprintf(mystring,99,"knx://sn.%s%s",oc_string(device->serialnumber), + oc_string(request->resource->uri)); + oc_rep_i_set_text_string(root, 0, mystring); + } + // href, i.e. url + if ((strncmp(m, "href", m_len) == 0) | + (strncmp(m, "*", m_len) == 0) ) { + // Valid and mandatory metadata, but "can be omitted in response" + m_valid = true; + oc_rep_set_text_string(root, href, get_datapoint_url(dp)); } // resource types if ((strncmp(m, "rt", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; const char *dpa = get_datapoint_dpa(dp); if (dpa) - oc_rep_set_text_string(root, rt, dpa); + oc_rep_set_text_string(root, rt, &dpa[7]); } // interfaces if ((strncmp(m, "if", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; oc_rep_set_key(oc_rep_object(root), "if"); oc_rep_begin_array(oc_rep_object(root), if); for (int i = 1; i <= (1<resource->dpt)); + m_valid = true; + char *full_dpt = oc_string(request->resource->dpt); + oc_rep_set_text_string(root, dpt, &full_dpt[7]); } // ga if ((strncmp(m, "ga", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; int index = oc_core_find_group_object_table_url(oc_string(request->resource->uri)); if (index > -1) { - oc_group_object_table_t* got_table_entry = oc_core_get_group_object_table_entry(index); - if (got_table_entry) { + oc_group_object_table_t* got_table_entry = oc_core_get_group_object_table_entry(index); + if (got_table_entry) { oc_rep_set_int_array(root, ga, got_table_entry->ga, got_table_entry->ga_len); - } + } } } for (const char *const *md = get_datapoint_metadata(dp); md && *md; md+=2) { if((strncmp(m, md[0], m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { + m_valid = true; oc_rep_set_text_string_no_tag(root, md[0]); oc_rep_set_text_string_no_tag(root, md[1]); } } } /* query iterator */ oc_rep_end_root_object(); + if (m_valid == false) { + oc_send_response_no_format(request, OC_STATUS_BAD_REQUEST); + goto done; + } } else { /* device is NULL */ - oc_send_cbor_response(request, OC_STATUS_BAD_OPTION); + oc_send_response_no_format(request, OC_STATUS_BAD_OPTION); + goto done; } oc_send_cbor_response(request, OC_STATUS_OK); - return; + goto done; } - if (oc_encode_datapoint(dp, pn, ps) == false) { - oc_send_response(request, OC_STATUS_INTERNAL_SERVER_ERROR); + if (oc_encode_datapoint(dp, pn, ps, false) == false) { + oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); goto done; } if (g_err) { @@ -2172,7 +2680,7 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da if (error_state == false) { oc_send_cbor_response(request, OC_STATUS_OK); } else { - oc_send_response(request, OC_STATUS_BAD_OPTION); + oc_send_response_no_format(request, OC_STATUS_BAD_OPTION); } done: PRINT("-- End get_generic (%s)\n", get_datapoint_url(dp)); @@ -2186,15 +2694,18 @@ put_generic(oc_request_t *request, oc_interface_mask_t interfaces, (void)interfaces; const datapoint_t *dp = user_data; bool error_state = true; - - PRINT("-- Begin put_generic (%s):\n", get_datapoint_url(dp)); if (dp == NULL) { - PRINT("Error dp is NULL\n"); - oc_send_response(request, OC_STATUS_INTERNAL_SERVER_ERROR); - return; + dp = get_datapoint_by_url(request->uri_path); + if (dp == NULL) { + PRINT("Error dp is NULL\n"); + oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); + return; + } } + PRINT("-- Begin put_generic (%s):\n", get_datapoint_url(dp)); + oc_rep_t *rep = NULL; int pn, ps; if(!request_query_get_int(request, "pn", &pn) || dp->num_elements == 0) pn = 0; @@ -2210,22 +2721,27 @@ put_generic(oc_request_t *request, oc_interface_mask_t interfaces, error_state = !oc_parse_datapoint(dp, rep, new_value, ps); if (error_state == false){ - oc_send_cbor_response(request, OC_STATUS_CHANGED); - datapoint_set(dp, new_value, pn*ps, ps); - if (dp->feedback_url) { - //check types match - const datapoint_t *feedback = get_datapoint_by_url(dp->feedback_url); - if (feedback->type == dp->type && feedback->num_elements == dp->num_elements) { - datapoint_set(feedback, new_value, pn*ps, ps); - PRINT(" Send status to '%s' with flag: 'w'\n", get_datapoint_url(feedback)); - oc_do_s_mode_with_scope(5, dp->feedback_url, "w"); - } + const oc_resource_t *my_resource = + oc_ri_get_app_resource_by_uri(request->uri_path, strlen(request->uri_path), 0); + if (my_resource != NULL) + oc_notify_observers(my_resource); + + oc_send_response_no_format(request, OC_STATUS_CHANGED); + datapoint_set(dp, new_value, pn*ps, ps); + if (dp->feedback_url) { + //check types match + const datapoint_t *feedback = get_datapoint_by_url(dp->feedback_url); + if (feedback->type == dp->type && feedback->num_elements == dp->num_elements) { + datapoint_set(feedback, new_value, pn*ps, ps); + PRINT(" Send status to '%s' with flag: 'w'\n", get_datapoint_url(feedback)); + oc_do_s_mode_with_scope(5, dp->feedback_url, "w"); } + } - do_put_cb(get_datapoint_url(dp)); + do_put_cb(get_datapoint_url(dp)); } else { /* request data was not recognized, so it was a bad request */ - oc_send_response(request, OC_STATUS_BAD_REQUEST); + oc_send_response_no_format(request, OC_STATUS_BAD_REQUEST); } free(new_value); PRINT("-- End put_generic (%s)\n", get_datapoint_url(dp)); @@ -2260,6 +2776,36 @@ register_resources(void) oc_ri_add_resource_block(&g_datapoints[0].resource); } +#ifdef MQTT_PROXY +void +configure_mqtt_from_parameters() { + strncpy(g_mqttconf_server, gMQTT_hostname0, sizeof(g_mqttconf_server)); + g_mqttconf_port = gMQTT_port_number0; + strncpy(g_mqttconf_username, gMQTT_username0, sizeof(g_mqttconf_username)); + strncpy(g_mqttconf_pwd, gMQTT_password0, sizeof(g_mqttconf_pwd)); + strncpy(g_iid_name, gIID_name0, sizeof(g_iid_name)); + PRINT("MQTT configuration:\n"); + PRINT(" hostname: %s\n", g_mqttconf_server); + PRINT(" port : %d\n", g_mqttconf_port); + PRINT(" username: %s\n", g_mqttconf_username); + PRINT(" password: %s\n", g_mqttconf_pwd); + PRINT(" IID name: %s\n", g_iid_name); +} +#endif + +/** + * @brief callback when a/lsm state changes + * @param device_index the device identifier of the list of devices + * @param current_state current state (i.e. after the change) + * @param data the supplied data. + */ +void +lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data) +{ + (void)device_index; + (void)data; +} + /** * @brief initiate preset for device * current implementation: device reset as command line argument @@ -2271,11 +2817,7 @@ factory_presets_cb(size_t device_index, void *data) { (void)device_index; (void)data; - - if (g_reset) { - PRINT("factory_presets_cb: resetting device\n"); - oc_knx_device_storage_reset(device_index, 2); - } + reset_variables(); } /** @@ -2361,44 +2903,97 @@ initialize_variables(void) long ret; bool err; - PRINT_APP("Initializing persitent data\n"); -for(int i = 0; i < num_datapoints; i++) { + PRINT_APP("Initializing persistent data\n"); + + for(int i = 0; i < num_datapoints; i++) { const datapoint_t *it = &g_datapoints[i]; - if (!it->persistent) continue; + if (!it->persistent && !it->default_present) continue; if (it->g_var == NULL) continue; if (it->num_elements){ - if (g_datapoint_types[it->type].persistent_load_array == NULL){ - PRINT_APP("ERR: persistent load array missing for %d\n", it->type); - } else { - g_datapoint_types[it->type].persistent_load_array(get_datapoint_url(it), (void*)it->g_var, it->num_elements); + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + if (it->persistent) { + if (g_datapoint_types[it->type].persistent_load_array == NULL){ + PRINT_APP("ERR: persistent load array missing for %d\n", it->type); + } else { + g_datapoint_types[it->type].persistent_load_array(get_datapoint_url(it), (void*)it->g_var, it->num_elements); + } } } else { - if (g_datapoint_types[it->type].persistent_load == NULL){ - PRINT_APP("ERR: persistent load array missing for %d\n", it->type); - } else { - g_datapoint_types[it->type].persistent_load(get_datapoint_url(it), (void*)it->g_var); + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + if (it->persistent) { + if (g_datapoint_types[it->type].persistent_load == NULL){ + PRINT_APP("ERR: persistent load array missing for %d\n", it->type); + } else { + g_datapoint_types[it->type].persistent_load(get_datapoint_url(it), (void*)it->g_var); + } } } } -for(int i = 0; i < num_parameters; i++) { + + for(int i = 0; i < num_parameters; i++) { const datapoint_t *it = &g_parameters[i]; - if (!it->persistent) continue; + if (!it->persistent && !it->default_present) continue; if (it->g_var == NULL) continue; if (it->num_elements){ - if (g_datapoint_types[it->type].persistent_load_array == NULL){ - PRINT_APP("ERR: persistent load array missing for %d\n", it->type); - } else { - g_datapoint_types[it->type].persistent_load_array(get_datapoint_url(it), (void*)it->g_var, it->num_elements); + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + if (it->persistent) { + if (g_datapoint_types[it->type].persistent_load_array == NULL){ + PRINT_APP("ERR: persistent load array missing for %d\n", it->type); + } else { + g_datapoint_types[it->type].persistent_load_array(get_datapoint_url(it), (void*)it->g_var, it->num_elements); + } } } else { - if (g_datapoint_types[it->type].persistent_load == NULL){ - PRINT_APP("ERR: persistent load array missing for %d\n", it->type); - } else { - g_datapoint_types[it->type].persistent_load(get_datapoint_url(it), (void*)it->g_var); + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + if (it->persistent) { + if (g_datapoint_types[it->type].persistent_load == NULL){ + PRINT_APP("ERR: persistent load array missing for %d\n", it->type); + } else { + g_datapoint_types[it->type].persistent_load(get_datapoint_url(it), (void*)it->g_var); + } } } } } +/** + * @brief reset variables to default value + * for the resources + * for the parameters + */ +void +reset_variables(void) +{ + /* reset variables to default value */ + PRINT_APP("Resetting to default value\n"); + + for(int i = 0; i < num_datapoints; i++) { + const datapoint_t *it = &g_datapoints[i]; + if (it->persistent) { + oc_storage_erase(get_datapoint_url(it)); + } + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + } + + for(int i = 0; i < num_parameters; i++) { + const datapoint_t *it = &g_parameters[i]; + if (it->persistent) { + oc_storage_erase(get_datapoint_url(it)); + } + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } + } +} int app_set_serial_number(const char* serial_number) { @@ -2434,7 +3029,7 @@ int app_initialize_stack() #else PRINT("\tstorage at 'knx_eink_battleships_creds' \n"); oc_storage_config("./knx_eink_battleships_creds"); -#endif +#endif /* WIN32 */ /* initializes the handlers structure */ @@ -2446,10 +3041,11 @@ int app_initialize_stack() /* set the application callbacks */ oc_set_hostname_cb(hostname_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if defined WIN32 || defined __linux__ oc_set_swu_cb(swu_cb, (void *)fname); -#endif +#endif /* WIN32 || defined __linux__ */ /* start the stack */ init = oc_main_init(&handler); @@ -2459,6 +3055,13 @@ int app_initialize_stack() return init; } + if (g_reset) { + PRINT("factory_presets_cb: resetting device\n"); + oc_knx_device_storage_reset(0, 2); + } + + oc_knx_knx_ignore_smessage_from_self(true); + #ifdef OC_OSCORE PRINT("OSCORE - Enabled\n"); #else @@ -2536,8 +3139,16 @@ print_usage() PRINT("-help : this message\n"); PRINT("reset : does an full reset of the device\n"); PRINT("-s : sets the serial number of the device\n"); +#ifdef MQTT_PROXY + PRINT("MQTT proxy configurations (only used before ETS download):\n"); + PRINT("-host : sets the MQTT broker hostname\n"); + PRINT("-port : sets the MQTT broker port number\n"); + PRINT("-username : sets the MQTT username\n"); + PRINT("-pwd : sets the MQTT password\n"); +#endif exit(0); } + /** * @brief main application. * initializes the global variables @@ -2551,9 +3162,15 @@ main(int argc, char *argv[]) oc_clock_time_t next_event; bool do_send_s_mode = false; + +#ifdef HARDWARE_INIT + hardware_init(); +#endif /* hardware init */ + + #ifdef KNX_GUI - WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWNORMAL); -#endif + WinMain(GetModuleHandle(NULL), NULL, (LPSTR)GetCommandLine(), SW_SHOWNORMAL); +#endif /* KNX_GUI */ #ifdef WIN32 /* windows specific */ @@ -2561,7 +3178,7 @@ main(int argc, char *argv[]) InitializeConditionVariable(&cv); /* install Ctrl-C */ signal(SIGINT, handle_signal); -#endif +#endif /* WIN32 */ #ifdef __linux__ /* Linux specific */ struct sigaction sa; @@ -2570,26 +3187,72 @@ main(int argc, char *argv[]) sa.sa_handler = handle_signal; /* install Ctrl-C */ sigaction(SIGINT, &sa, NULL); -#endif + #define MAX_HOSTNAME 256 + char hostname[MAX_HOSTNAME]; + // set the hostname + ret = gethostname(&hostname[0], MAX_HOSTNAME); + if (ret != -1) { + printf("Hostname: %s\n", hostname); + oc_core_set_device_hostname(0, hostname, MAX_HOSTNAME); + } +#endif /* __linux__ */ for (int i = 0; i < argc; i++) { PRINT_APP("argv[%d] = %s\n", i, argv[i]); - } - if (argc > 1) { - if (strcmp(argv[1], "reset") == 0) { + if (strcmp(argv[i], "-help") == 0) { + print_usage(); + } + if (strcmp(argv[i], "reset") == 0) { PRINT(" internal reset\n"); g_reset = true; } - if (strcmp(argv[1], "-help") == 0) { - print_usage(); - } - } - if (argc > 2) { - if (strcmp(argv[1], "-s") == 0) { + if (strcmp(argv[i], "-s") == 0) { + if (i + 1 < argc) { // serial number - PRINT("serial number %s\n", argv[2]); - app_set_serial_number(argv[2]); - } + PRINT("serial number %s\n", argv[i + 1]); + app_set_serial_number(argv[i + 1]); + } else { + PRINT("ERROR: \"-s\" flag detected, but no serial number provided!\n"); + } + } +#ifdef MQTT_PROXY + if (strcmp(argv[i], "-host") == 0) { + if (i + 1 < argc) { + // hostname for MQTT proxy server + PRINT("MQTT proxy host %s\n", argv[i + 1]); + strncpy(g_mqttconf_server, argv[i + 1], sizeof(g_mqttconf_server)); + } else { + PRINT("ERROR: \"-host\" flag detected, but no hostname provided!\n"); + } + } + if (strcmp(argv[i], "-port") == 0) { + if (i + 1 < argc) { + // port number for MQTT proxy server + PRINT("MQTT proxy port number %s\n", argv[i + 1]); + g_mqttconf_port = atoi(argv[i + 1]); + } else { + PRINT("ERROR: \"-port\" flag detected, but no port number provided!\n"); + } + } + if (strcmp(argv[i], "-username") == 0) { + if (i + 1 < argc) { + // username for MQTT proxy server + PRINT("MQTT proxy username %s\n", argv[i + 1]); + strncpy(g_mqttconf_username, argv[i + 1], sizeof(g_mqttconf_username)); + } else { + PRINT("ERROR: \"-username\" flag detected, but no username provided!\n"); + } + } + if (strcmp(argv[i], "-pwd") == 0) { + if (i + 1 < argc) { + // password for MQTT proxy server + PRINT("MQTT proxy pwd %s\n", argv[i + 1]); + strncpy(g_mqttconf_pwd, argv[i + 1], sizeof(g_mqttconf_pwd)); + } else { + PRINT("ERROR: \"-pwd\" flag detected, but no password provided!\n"); + } + } +#endif } /* do all initialization */ @@ -2609,7 +3272,7 @@ main(int argc, char *argv[]) } } } -#endif +#endif /* WIN32 */ #ifdef __linux__ /* Linux specific loop */ @@ -2625,7 +3288,7 @@ main(int argc, char *argv[]) } pthread_mutex_unlock(&mutex); } -#endif +#endif /* __linux__ */ /* shut down the stack */ oc_main_shutdown(); diff --git a/BATTLESHIP/knx_eink_battleships.h b/BATTLESHIP/knx_eink_battleships.h index 7fb258a..3062302 100644 --- a/BATTLESHIP/knx_eink_battleships.h +++ b/BATTLESHIP/knx_eink_battleships.h @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Ltd + Copyright (c) 2022-2024 Cascoda Ltd -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -61,55 +61,106 @@ extern "C" { #define PACKED //!< Helper macro to create smaller packed structs #endif -#define THIS_DEVICE 0 +#define THIS_DEVICE 0 //!< single device instance, e.g. number 0 // URL defines -#define URL_SENDSHOT "/p/o_1_1" //!< URL define for SendShot -#define URL_RECEIVESHOT "/p/o_1_2" //!< URL define for ReceiveShot -#define URL_SENDSHOTSTATUS "/p/o_1_3" //!< URL define for SendShotStatus -#define URL_RECEIVESHOTSTATUS "/p/o_1_4" //!< URL define for ReceiveShotStatus -#define URL_SENDREADY "/p/o_1_5" //!< URL define for SendReady -#define URL_RECEIVEREADY "/p/o_1_6" //!< URL define for ReceiveReady -#define URL_STARTING_PLAYER "/p/p_1_1" //!< URL define for Starting_Player +#define URL_SENDSHOT "/p/o_1_1" //!< URL "SendShot" desc:"" +#define URL_RECEIVESHOT "/p/o_1_2" //!< URL "ReceiveShot" desc:"" +#define URL_SENDSHOTSTATUS "/p/o_1_3" //!< URL "SendShotStatus" desc:"" +#define URL_RECEIVESHOTSTATUS "/p/o_1_4" //!< URL "ReceiveShotStatus" desc:"" +#define URL_SENDREADY "/p/o_1_5" //!< URL "SendReady" desc:"" +#define URL_RECEIVEREADY "/p/o_1_6" //!< URL "ReceiveReady" desc:"" +#define URL_STARTING_PLAYER "/p/p_1_1" //!< URL "Starting_Player" desc:"" + +typedef enum DatapointType{ + DatapointType_bool, + DatapointType_int, + DatapointType_float, + DatapointType_string, + DatapointType_DPT_Param_Bool, + DatapointType_DPT_Shot_Status, + DatapointType_DPT_Start, + DatapointType_DPT_Uint_XY, + DatapointType_MAX_NUM, +} DatapointType; + +typedef struct datapoint_t { + oc_resource_t resource; + const char *const *metadata; + const char *feedback_url; + DatapointType type; + void *g_var; + volatile void *g_fault; + bool persistent; + bool default_present; + int num_elements; +} datapoint_t; + +/* all data points */ +extern const datapoint_t g_datapoints[]; +extern const size_t num_datapoints; + +/* all parameters */ +extern const datapoint_t g_parameters[]; +extern const size_t num_parameters; +/* ENUM defines, for each module instance */ + + +/** + * @brief Returns the datapoint for the given URL + * + * @param url URL of the datapoint + */ +const datapoint_t *get_datapoint_by_url(const char *url); + +/** + * @brief Returns the url of the module by instance + * + * @param out_url URL of the datapoint given back (e.g. with the corrected post fix) + * @param in_url URL of the datapoint of the module + * @param module_index URL the module index + * @return false if operation successful, true if something went wrong + */ +bool get_module_url(char* out_url, const char* in_url, int module_index); ///@defgroup DPT_Param_Bool ///@ingroup DPT_Param_Bool typedef -enum DPT_Param_Bool_ValueBool { - _DPST600013_F11 = 0, - _ValueBool_False = 0, - DPT_Param_Bool_ValueBool_False = 0, - _DPST600013_F12 = 1, - _ValueBool_True = 1, - DPT_Param_Bool_ValueBool_True = 1, +enum DPT_Param_BoolValueBool { + DPST_60012_3_F_1_1 = 0, + ValueBoolFalse = 0, + DPT_Param_BoolValueBoolFalse = 0, + DPST_60012_3_F_1_2 = 1, + ValueBoolTrue = 1, + DPT_Param_BoolValueBoolTrue = 1, } DPT_Param_Bool; ///@defgroup DPT_Shot_Status -enum DPT_Shot_Status_ShipType { - _DPST600041_F31 = 0, - _ShipType_NoHit = 0, - DPT_Shot_Status_ShipType_NoHit = 0, - _DPST600041_F32 = 1, - _ShipType_Destroyer = 1, - DPT_Shot_Status_ShipType_Destroyer = 1, - _DPST600041_F33 = 2, - _ShipType_Submarine = 2, - DPT_Shot_Status_ShipType_Submarine = 2, - _DPST600041_F34 = 3, - _ShipType_Cruiser = 3, - DPT_Shot_Status_ShipType_Cruiser = 3, - _DPST600041_F35 = 4, - _ShipType_Battleship = 4, - DPT_Shot_Status_ShipType_Battleship = 4, - _DPST600041_F36 = 5, - _ShipType_Carrier = 5, - DPT_Shot_Status_ShipType_Carrier = 5, +enum DPT_Shot_StatusShipType { + DPST_60004_1_F_3_1 = 0, + ShipTypeNo_Hit = 0, + DPT_Shot_StatusShipTypeNo_Hit = 0, + DPST_60004_1_F_3_2 = 1, + ShipTypeDestroyer = 1, + DPT_Shot_StatusShipTypeDestroyer = 1, + DPST_60004_1_F_3_3 = 2, + ShipTypeSubmarine = 2, + DPT_Shot_StatusShipTypeSubmarine = 2, + DPST_60004_1_F_3_4 = 3, + ShipTypeCruiser = 3, + DPT_Shot_StatusShipTypeCruiser = 3, + DPST_60004_1_F_3_5 = 4, + ShipTypeBattleship = 4, + DPT_Shot_StatusShipTypeBattleship = 4, + DPST_60004_1_F_3_6 = 5, + ShipTypeCarrier = 5, + DPT_Shot_StatusShipTypeCarrier = 5, }; /** * @ingroup DPT_Shot_Status - * DPT_Shot_Status struct with member fields + * DPT_Shot_Status struct with member fields for DPT_Shot_Status */ struct DPT_Shot_Status_s { @@ -121,9 +172,9 @@ struct DPT_Shot_Status_s { ///@endcond ///@name Aliases for DPT_Shot_Status DPST-60004-1_F-1 ///@{ - bool _Hit; //!< DPST-60004-1_F-1 member alias Hit for DPT_Shot_Status - bool _DPST600041_F1; //!< DPST-60004-1_F-1 member alias DPST-60004-1_F-1 for DPT_Shot_Status - bool _F1; //!< DPST-60004-1_F-1 member alias F-1 for DPT_Shot_Status + bool Hit; //!< DPST-60004-1_F-1 member alias Hit for DPT_Shot_Status + bool DPST_60004_1_F_1; //!< DPST-60004-1_F-1 member alias DPST-60004-1_F-1 for DPT_Shot_Status + bool F_1; //!< DPST-60004-1_F-1 member alias F-1 for DPT_Shot_Status ///@} ///@cond U_END } PACKED; //!< union to alias DPST-60004-1_F-1 name variations @@ -137,9 +188,9 @@ struct DPT_Shot_Status_s { ///@endcond ///@name Aliases for DPT_Shot_Status DPST-60004-1_F-2 ///@{ - bool _Sunk; //!< DPST-60004-1_F-2 member alias Sunk for DPT_Shot_Status - bool _DPST600041_F2; //!< DPST-60004-1_F-2 member alias DPST-60004-1_F-2 for DPT_Shot_Status - bool _F2; //!< DPST-60004-1_F-2 member alias F-2 for DPT_Shot_Status + bool Sunk; //!< DPST-60004-1_F-2 member alias Sunk for DPT_Shot_Status + bool DPST_60004_1_F_2; //!< DPST-60004-1_F-2 member alias DPST-60004-1_F-2 for DPT_Shot_Status + bool F_2; //!< DPST-60004-1_F-2 member alias F-2 for DPT_Shot_Status ///@} ///@cond U_END } PACKED; //!< union to alias DPST-60004-1_F-2 name variations @@ -153,9 +204,9 @@ struct DPT_Shot_Status_s { ///@endcond ///@name Aliases for DPT_Shot_Status DPST-60004-1_F-3 ///@{ - enum DPT_Shot_Status_ShipType _ShipType:3; //!< DPST-60004-1_F-3 member alias ShipType for DPT_Shot_Status - enum DPT_Shot_Status_ShipType _DPST600041_F3:3; //!< DPST-60004-1_F-3 member alias DPST-60004-1_F-3 for DPT_Shot_Status - enum DPT_Shot_Status_ShipType _F3:3; //!< DPST-60004-1_F-3 member alias F-3 for DPT_Shot_Status + enum DPT_Shot_StatusShipType ShipType; //!< DPST-60004-1_F-3 member alias ShipType for DPT_Shot_Status + enum DPT_Shot_StatusShipType DPST_60004_1_F_3; //!< DPST-60004-1_F-3 member alias DPST-60004-1_F-3 for DPT_Shot_Status + enum DPT_Shot_StatusShipType F_3; //!< DPST-60004-1_F-3 member alias F-3 for DPT_Shot_Status ///@} ///@cond U_END } PACKED; //!< union to alias DPST-60004-1_F-3 name variations @@ -172,7 +223,7 @@ typedef bool DPT_Start; /** * @ingroup DPT_Uint_XY - * DPT_Uint_XY struct with member fields + * DPT_Uint_XY struct with member fields for DPT_Uint_XY */ struct DPT_Uint_XY_s { @@ -184,9 +235,9 @@ struct DPT_Uint_XY_s { ///@endcond ///@name Aliases for DPT_Uint_XY DPST-60009-1_F-1 ///@{ - unsigned int _X:8; //!< DPST-60009-1_F-1 member alias X for DPT_Uint_XY - unsigned int _DPST600091_F1:8; //!< DPST-60009-1_F-1 member alias DPST-60009-1_F-1 for DPT_Uint_XY - unsigned int _F1:8; //!< DPST-60009-1_F-1 member alias F-1 for DPT_Uint_XY + unsigned int X; //!< DPST-60009-1_F-1 member alias X for DPT_Uint_XY + unsigned int DPST_60009_1_F_1; //!< DPST-60009-1_F-1 member alias DPST-60009-1_F-1 for DPT_Uint_XY + unsigned int F_1; //!< DPST-60009-1_F-1 member alias F-1 for DPT_Uint_XY ///@} ///@cond U_END } PACKED; //!< union to alias DPST-60009-1_F-1 name variations @@ -200,9 +251,9 @@ struct DPT_Uint_XY_s { ///@endcond ///@name Aliases for DPT_Uint_XY DPST-60009-1_F-2 ///@{ - unsigned int _Y:8; //!< DPST-60009-1_F-2 member alias Y for DPT_Uint_XY - unsigned int _DPST600091_F2:8; //!< DPST-60009-1_F-2 member alias DPST-60009-1_F-2 for DPT_Uint_XY - unsigned int _F2:8; //!< DPST-60009-1_F-2 member alias F-2 for DPT_Uint_XY + unsigned int Y; //!< DPST-60009-1_F-2 member alias Y for DPT_Uint_XY + unsigned int DPST_60009_1_F_2; //!< DPST-60009-1_F-2 member alias DPST-60009-1_F-2 for DPT_Uint_XY + unsigned int F_2; //!< DPST-60009-1_F-2 member alias F-2 for DPT_Uint_XY ///@} ///@cond U_END } PACKED; //!< union to alias DPST-60009-1_F-2 name variations @@ -231,8 +282,10 @@ typedef struct DPT_Uint_XY_s DPT_Uint_XY; #define BOTTOM_MOST_LINE_Y_COORD EINK_LINE_NO(8) // This is the number of lines that will keep scrolling before looping back, // after the last line has been displayed. -#define CONVENIENCE_PADDING_FOR_SCROLLING 3 +#define CONVENIENCE_PADDING_FOR_SCROLLING 0 +#ifndef INITIAL_SCREEN_TIMEOUT #define INITIAL_SCREEN_TIMEOUT 0 +#endif extern bool g_eink_clean_redraw; @@ -267,14 +320,11 @@ struct EinkScreenHandler SPLASH_SCREEN = 0, MENU_SCREEN = 1, QR_SCREEN = 2, - GOT_SCREEN = 3, - GRT_SCREEN = 4, - GPT_SCREEN = 5, - AUTH_SCREEN = 6, - DEV_SCREEN = 7, - RESET_SCREEN = 8, - ROLE_SCREEN = 9, - TABLES_SCREEN = 10, + DEV_SCREEN = 3, + RESET_SCREEN = 4, + ROLE_SCREEN = 5, + TABLES_SCREEN = 6, + HELP_SCREEN = 7, GAME_INTRO, CONTROLS_1, PLACE_SHIPS, @@ -294,130 +344,34 @@ struct menu_t{ void (*onclick)(void); }; +enum BattDisplaySymbol +{ + BATT_DISPLAY_NONE, + BATT_DISPLAY_ICON, + BATT_DISPLAY_PERCENT, + BATT_DISPLAY_END, +}; + +extern enum BattDisplaySymbol g_batt_display_symbol; + +uint8_t get_batt_percent(void); + /** * @brief Draw the nice screen header and frame. * * @param screen Screen number for the header */ -void screen_header_draw(enum Screen); +void app_header_draw(enum Screen nr); /** * @brief Draw a scrollable menu and highlight * the selected item. */ -void draw_scrollable_menu(const struct menu_t *menu, int numentries); +void draw_scrollable_menu(struct menu_t *menu, int numentries, enum Screen nr); void menu_next(); void menu_prev(); void menu_select(); - -/** - * @name QRcode - * QRcode struct and register/deregister functions - */ -/**@{*/ - -struct qr_code_t { - struct qr_code_t *next; - const char *str_data; - const char *desc; -}; - -/** - * @brief Register a QR code to be displayed on the - * QR code page of the EINK interface. - * @note This function retains the pointer to qrcode - * beyond the end of the function so these MUST be malloc'd or - * global scope/lifetime. - * - * @param qrcode QR code structure to be registered - * - * ~~~{.c} - * void my_init() { - * // this function is called ONCE at startup - * static struct qr_code_t my_qrcode; - * my_qrcode.desc = "My QR code"; - * my_qrcode.str_data = "Data to embed in QR code"; - * register_qr_code(&my_qrcode); - * } - * ~~~ - */ -void register_qr_code(struct qr_code_t *qrcode); -/** - * @brief De-register a QR code from the eink interface - * - * @param qrcode QR code to deregister. - */ -void deregister_qr_code(struct qr_code_t *qrcode); -/** - * @brief Check if a QR code is registered to the eink interface - * - * @param qrcode QR code to check if registered - */ -bool qr_code_is_registered(struct qr_code_t *qrcode); -/** - * @brief Get the number of QR codes registered - * - * @return number of QR codes registered - */ -uint32_t num_qr_codes(); -/**@}*/ - - -/** - * @name Reset - * reset struct and register/deregister functions - */ -/**@{*/ -struct reset_t { - struct reset_t *next; - const char *desc; - void (*do_reset)(void); -}; - -/** - * @brief Register a reset to be displayed on the - * reset page of the EINK interface. - * @note This function retains the pointer to reset - * beyond the end of the function so these MUST be malloc'd or - * global scope/lifetime. - * - * @param reset QR code structure to be registered - * - * ~~~{.c} - * void my_reset_cb() { - * //do some kind of platform specific reset? - * } - * void my_init() { - * // this function is called ONCE at startup - * static struct reset_t my_reset; - * my_reset.desc = "My Reset"; - * my_reset.do_reset = &my_reset_cb; - * register_reset(&my_reset); - * } - * ~~~ - */ -void register_reset(struct reset_t *reset); -/** - * @brief De-register a reset from the eink interface - * - * @param reset reset to deregister. - */ -void deregister_reset(struct reset_t *reset); -/** - * @brief Check if a reset is registered to the eink interface - * - * @param reset reset to check if registered - */ -bool reset_is_registered(struct reset_t *reset); -/** - * @brief Get the number of resets registered - * - * @return number of resets registered - */ -uint32_t num_resets(); -/**@}*/ - -extern bool g_clean_redraw; +void go_CONNECTIVITY_SCREEN(); /** * @brief Set the screen to be loaded and redraw. @@ -458,26 +412,6 @@ inline static void go_MENU_SCREEN() { set_screen(MENU_SCREEN); } * Can be used as an eink screen event handler. */ inline static void go_QR_SCREEN() { set_screen(QR_SCREEN); } -/** - * @brief Load the builtin GOT_SCREEN screen immediately - * Can be used as an eink screen event handler. - */ -inline static void go_GOT_SCREEN() { set_screen(GOT_SCREEN); } -/** - * @brief Load the builtin GRT_SCREEN screen immediately - * Can be used as an eink screen event handler. - */ -inline static void go_GRT_SCREEN() { set_screen(GRT_SCREEN); } -/** - * @brief Load the builtin GPT_SCREEN screen immediately - * Can be used as an eink screen event handler. - */ -inline static void go_GPT_SCREEN() { set_screen(GPT_SCREEN); } -/** - * @brief Load the builtin AUTH_SCREEN screen immediately - * Can be used as an eink screen event handler. - */ -inline static void go_AUTH_SCREEN() { set_screen(AUTH_SCREEN); } /** * @brief Load the builtin DEV_SCREEN screen immediately * Can be used as an eink screen event handler. @@ -497,7 +431,12 @@ inline static void go_ROLE_SCREEN() { set_screen(ROLE_SCREEN); } * @brief Load the builtin TABLES_SCREEN screen immediately * Can be used as an eink screen event handler. */ -inline static void go_TABLES_SCREEN() { set_screen(TABLES_SCREEN); } +inline static void go_TABLES_SCREEN() { set_screen(TABLES_SCREEN); } +/** + * @brief Load the builtin HELP_SCREEN screen immediately + * Can be used as an eink screen event handler. + */ +inline static void go_HELP_SCREEN() { set_screen(HELP_SCREEN); } /** * @brief Load the user defined GAME_INTRO screen immediately * Can be used as an eink screen event handler. @@ -590,7 +529,7 @@ void button_3_Hold_cb(void *ctx); /////////////////////////////////////////////////////////////////////////////// /** - * Callback invoked by the stack when a successfull put is done + * Callback invoked by the stack when a successful put is done * * @param[in] url the url of the put * @@ -614,7 +553,7 @@ typedef struct oc_put_struct_t void app_set_put_cb(oc_put_cb_t cb); /** - * Callback invoked by the stack when a successfull put is done + * Callback invoked by the stack when a successful put is done * * @param[in] url the url of the put * @@ -660,6 +599,7 @@ int app_set_serial_number(const char* serial_number); * @return int 0 if not an array endpoint, else length of array */ int app_get_url_array_size(const char *url); + /** * @name DPT_Param_Bool functions * getters/setters and other functions for DPT_Param_Bool @@ -674,6 +614,18 @@ int app_get_url_array_size(const char *url); */ bool app_is_DPT_Param_Bool_url(const char* url); +/** + * @ingroup DPT_Param_Bool + * @brief Set a DPT_Param_Bool to the default value + * + * @param[in] url the url for the DPT_Param_Bool to set + * + * ~~~{.c} + * app_set_DPT_Param_Bool_default_value("/some/url"); + * ~~~ + */ +void app_set_DPT_Param_Bool_default_value(const char* url); + /** * @ingroup DPT_Param_Bool * @brief Set a DPT_Param_Bool @@ -700,6 +652,7 @@ void app_set_DPT_Param_Bool_variable(const char* url, const DPT_Param_Bool* in); * @param[in] in a pointer to the DPT_Param_Bool to copy * Can be the global variable itself * @param[in] n number of elements in the array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Param_Bool my_arr[5]; @@ -707,7 +660,7 @@ void app_set_DPT_Param_Bool_variable(const char* url, const DPT_Param_Bool* in); * app_set_DPT_Param_Bool_array("/some/url", my_arr, 5); * ~~~ */ -void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int n); +void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int n, bool store_persistently); /** * @ingroup DPT_Param_Bool @@ -720,6 +673,7 @@ void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int * Can be the global variable itself * @param[in] start starting index to write to array * @param[in] n number of elements to write to array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Param_Bool my_var; @@ -728,7 +682,7 @@ void app_set_DPT_Param_Bool_array(const char* url, const DPT_Param_Bool* in, int * app_set_DPT_Param_Bool_array("/some/url", &my_var, 5, 1); * ~~~ */ -void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* in, int start, int n); +void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* in, int start, int n, bool store_persistently); /** * @ingroup DPT_Param_Bool @@ -755,7 +709,7 @@ void app_set_DPT_Param_Bool_array_elems(const char* url, const DPT_Param_Bool* i * // do something with my_var * ~~~ */ -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, DPT_Param_Bool* out); +const DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, DPT_Param_Bool* out); /** * @ingroup DPT_Param_Bool @@ -783,7 +737,67 @@ const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_variable(const char *url, * // do something with my_arr * ~~~ */ -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array(const char *url, DPT_Param_Bool* out, int n); +const DPT_Param_Bool* app_get_DPT_Param_Bool_array(const char *url, DPT_Param_Bool* out, int n); + + +/** + * @ingroup DPT_Param_Bool + * @brief Get a DPT_Param_Bool as string + * + * @param[in] in the data type + * @param[in] text, reserved space to copy the generated text too + * @param[in] size size of the allocated text + * + * ~~~{.c} + * DPT_Param_Bool my_type; + * char my_text[100]; + * if (app_sprintf_DPT_Param_Bool(my_type, my_text, 100) != 0) { + * //Something went wrong + * return; + * } + * // printf(my_text); + * ~~~ + */ +int app_sprintf_DPT_Param_Bool(const DPT_Param_Bool *in, char* text, int size); + + +/** + * @ingroup DPT_Param_Bool + * @brief Get a DPT_Param_Bool from a string + * + * @param[in] in the data type + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Param_Bool my_type; + * char my_text[100]; + * if (app_sscanf_DPT_Param_Bool(&my_type, my_text) != 0) { + * //Something went wrong + * return; + * } + * // do something with my_type + * ~~~ + */ +int app_sscanf_DPT_Param_Bool(DPT_Param_Bool *in, char* text); + +/** + * @ingroup DPT_Param_Bool + * @brief Get an example of DPT_Param_Bool in a string + * + * @param[in] select 1 = format, 2 = example + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Param_Bool my_type; + * char my_text[100]; + * if (app_str_expected_DPT_Param_Bool(1 , my_text) != 0) { + * //Something went wrong + * return; + * } + * printf(my_text); + * ~~~ + */ +int app_str_expected_DPT_Param_Bool(int select, char* text); /** * @ingroup DPT_Param_Bool @@ -813,7 +827,7 @@ const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array(const char *url, DPT * // do something with my_var * ~~~ */ -const volatile DPT_Param_Bool* app_get_DPT_Param_Bool_array_elems(const char *url, DPT_Param_Bool* out, int start, int n); +const DPT_Param_Bool* app_get_DPT_Param_Bool_array_elems(const char *url, DPT_Param_Bool* out, int start, int n); /** * @ingroup DPT_Param_Bool @@ -863,6 +877,7 @@ bool oc_parse_DPT_Param_Bool_array(oc_rep_t *rep, DPT_Param_Bool *out, int n); * @brief Encode a DPT_Param_Bool using oc_rep * * @param[in] in The DPT_Param_Bool to encode + * @param[is_metadata] is_metadata Whether function is called during metadata query handling * * ~~~{.c} * DPT_Param_Bool my_var; @@ -875,7 +890,7 @@ bool oc_parse_DPT_Param_Bool_array(oc_rep_t *rep, DPT_Param_Bool *out, int n); * uint8_t buf = oc_rep_get_encoder_buf(); * ~~~ */ -void oc_encode_DPT_Param_Bool(const DPT_Param_Bool *in); +void oc_encode_DPT_Param_Bool(const DPT_Param_Bool *in, bool is_metadata); /** * @ingroup DPT_Param_Bool @@ -967,6 +982,7 @@ bool persistent_load_DPT_Param_Bool(const char *name, DPT_Param_Bool *out); */ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, int n); /**@}*/ + /** * @name DPT_Shot_Status functions * getters/setters and other functions for DPT_Shot_Status @@ -981,6 +997,18 @@ bool persistent_load_DPT_Param_Bool_array(const char *name, DPT_Param_Bool *out, */ bool app_is_DPT_Shot_Status_url(const char* url); +/** + * @ingroup DPT_Shot_Status + * @brief Set a DPT_Shot_Status to the default value + * + * @param[in] url the url for the DPT_Shot_Status to set + * + * ~~~{.c} + * app_set_DPT_Shot_Status_default_value("/some/url"); + * ~~~ + */ +void app_set_DPT_Shot_Status_default_value(const char* url); + /** * @ingroup DPT_Shot_Status * @brief Set a DPT_Shot_Status @@ -1007,6 +1035,7 @@ void app_set_DPT_Shot_Status_variable(const char* url, const DPT_Shot_Status* in * @param[in] in a pointer to the DPT_Shot_Status to copy * Can be the global variable itself * @param[in] n number of elements in the array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Shot_Status my_arr[5]; @@ -1014,7 +1043,7 @@ void app_set_DPT_Shot_Status_variable(const char* url, const DPT_Shot_Status* in * app_set_DPT_Shot_Status_array("/some/url", my_arr, 5); * ~~~ */ -void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, int n); +void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, int n, bool store_persistently); /** * @ingroup DPT_Shot_Status @@ -1027,6 +1056,7 @@ void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, i * Can be the global variable itself * @param[in] start starting index to write to array * @param[in] n number of elements to write to array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Shot_Status my_var; @@ -1035,7 +1065,7 @@ void app_set_DPT_Shot_Status_array(const char* url, const DPT_Shot_Status* in, i * app_set_DPT_Shot_Status_array("/some/url", &my_var, 5, 1); * ~~~ */ -void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* in, int start, int n); +void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* in, int start, int n, bool store_persistently); /** * @ingroup DPT_Shot_Status @@ -1062,7 +1092,7 @@ void app_set_DPT_Shot_Status_array_elems(const char* url, const DPT_Shot_Status* * // do something with my_var * ~~~ */ -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url, DPT_Shot_Status* out); +const DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url, DPT_Shot_Status* out); /** * @ingroup DPT_Shot_Status @@ -1090,7 +1120,67 @@ const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url * // do something with my_arr * ~~~ */ -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array(const char *url, DPT_Shot_Status* out, int n); +const DPT_Shot_Status* app_get_DPT_Shot_Status_array(const char *url, DPT_Shot_Status* out, int n); + + +/** + * @ingroup DPT_Shot_Status + * @brief Get a DPT_Shot_Status as string + * + * @param[in] in the data type + * @param[in] text, reserved space to copy the generated text too + * @param[in] size size of the allocated text + * + * ~~~{.c} + * DPT_Shot_Status my_type; + * char my_text[100]; + * if (app_sprintf_DPT_Shot_Status(my_type, my_text, 100) != 0) { + * //Something went wrong + * return; + * } + * // printf(my_text); + * ~~~ + */ +int app_sprintf_DPT_Shot_Status(const DPT_Shot_Status *in, char* text, int size); + + +/** + * @ingroup DPT_Shot_Status + * @brief Get a DPT_Shot_Status from a string + * + * @param[in] in the data type + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Shot_Status my_type; + * char my_text[100]; + * if (app_sscanf_DPT_Shot_Status(&my_type, my_text) != 0) { + * //Something went wrong + * return; + * } + * // do something with my_type + * ~~~ + */ +int app_sscanf_DPT_Shot_Status(DPT_Shot_Status *in, char* text); + +/** + * @ingroup DPT_Shot_Status + * @brief Get an example of DPT_Shot_Status in a string + * + * @param[in] select 1 = format, 2 = example + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Shot_Status my_type; + * char my_text[100]; + * if (app_str_expected_DPT_Shot_Status(1 , my_text) != 0) { + * //Something went wrong + * return; + * } + * printf(my_text); + * ~~~ + */ +int app_str_expected_DPT_Shot_Status(int select, char* text); /** * @ingroup DPT_Shot_Status @@ -1120,7 +1210,7 @@ const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array(const char *url, D * // do something with my_var * ~~~ */ -const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_array_elems(const char *url, DPT_Shot_Status* out, int start, int n); +const DPT_Shot_Status* app_get_DPT_Shot_Status_array_elems(const char *url, DPT_Shot_Status* out, int start, int n); /** * @ingroup DPT_Shot_Status @@ -1170,6 +1260,7 @@ bool oc_parse_DPT_Shot_Status_array(oc_rep_t *rep, DPT_Shot_Status *out, int n); * @brief Encode a DPT_Shot_Status using oc_rep * * @param[in] in The DPT_Shot_Status to encode + * @param[is_metadata] is_metadata Whether function is called during metadata query handling * * ~~~{.c} * DPT_Shot_Status my_var; @@ -1182,7 +1273,7 @@ bool oc_parse_DPT_Shot_Status_array(oc_rep_t *rep, DPT_Shot_Status *out, int n); * uint8_t buf = oc_rep_get_encoder_buf(); * ~~~ */ -void oc_encode_DPT_Shot_Status(const DPT_Shot_Status *in); +void oc_encode_DPT_Shot_Status(const DPT_Shot_Status *in, bool is_metadata); /** * @ingroup DPT_Shot_Status @@ -1274,6 +1365,7 @@ bool persistent_load_DPT_Shot_Status(const char *name, DPT_Shot_Status *out); */ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *out, int n); /**@}*/ + /** * @name DPT_Start functions * getters/setters and other functions for DPT_Start @@ -1288,6 +1380,18 @@ bool persistent_load_DPT_Shot_Status_array(const char *name, DPT_Shot_Status *ou */ bool app_is_DPT_Start_url(const char* url); +/** + * @ingroup DPT_Start + * @brief Set a DPT_Start to the default value + * + * @param[in] url the url for the DPT_Start to set + * + * ~~~{.c} + * app_set_DPT_Start_default_value("/some/url"); + * ~~~ + */ +void app_set_DPT_Start_default_value(const char* url); + /** * @ingroup DPT_Start * @brief Set a DPT_Start @@ -1314,6 +1418,7 @@ void app_set_DPT_Start_variable(const char* url, const DPT_Start* in); * @param[in] in a pointer to the DPT_Start to copy * Can be the global variable itself * @param[in] n number of elements in the array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Start my_arr[5]; @@ -1321,7 +1426,7 @@ void app_set_DPT_Start_variable(const char* url, const DPT_Start* in); * app_set_DPT_Start_array("/some/url", my_arr, 5); * ~~~ */ -void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n); +void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n, bool store_persistently); /** * @ingroup DPT_Start @@ -1334,6 +1439,7 @@ void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n); * Can be the global variable itself * @param[in] start starting index to write to array * @param[in] n number of elements to write to array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Start my_var; @@ -1342,7 +1448,7 @@ void app_set_DPT_Start_array(const char* url, const DPT_Start* in, int n); * app_set_DPT_Start_array("/some/url", &my_var, 5, 1); * ~~~ */ -void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int start, int n); +void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int start, int n, bool store_persistently); /** * @ingroup DPT_Start @@ -1369,7 +1475,7 @@ void app_set_DPT_Start_array_elems(const char* url, const DPT_Start* in, int sta * // do something with my_var * ~~~ */ -const volatile DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* out); +const DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* out); /** * @ingroup DPT_Start @@ -1397,7 +1503,67 @@ const volatile DPT_Start* app_get_DPT_Start_variable(const char *url, DPT_Start* * // do something with my_arr * ~~~ */ -const volatile DPT_Start* app_get_DPT_Start_array(const char *url, DPT_Start* out, int n); +const DPT_Start* app_get_DPT_Start_array(const char *url, DPT_Start* out, int n); + + +/** + * @ingroup DPT_Start + * @brief Get a DPT_Start as string + * + * @param[in] in the data type + * @param[in] text, reserved space to copy the generated text too + * @param[in] size size of the allocated text + * + * ~~~{.c} + * DPT_Start my_type; + * char my_text[100]; + * if (app_sprintf_DPT_Start(my_type, my_text, 100) != 0) { + * //Something went wrong + * return; + * } + * // printf(my_text); + * ~~~ + */ +int app_sprintf_DPT_Start(const DPT_Start *in, char* text, int size); + + +/** + * @ingroup DPT_Start + * @brief Get a DPT_Start from a string + * + * @param[in] in the data type + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Start my_type; + * char my_text[100]; + * if (app_sscanf_DPT_Start(&my_type, my_text) != 0) { + * //Something went wrong + * return; + * } + * // do something with my_type + * ~~~ + */ +int app_sscanf_DPT_Start(DPT_Start *in, char* text); + +/** + * @ingroup DPT_Start + * @brief Get an example of DPT_Start in a string + * + * @param[in] select 1 = format, 2 = example + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Start my_type; + * char my_text[100]; + * if (app_str_expected_DPT_Start(1 , my_text) != 0) { + * //Something went wrong + * return; + * } + * printf(my_text); + * ~~~ + */ +int app_str_expected_DPT_Start(int select, char* text); /** * @ingroup DPT_Start @@ -1427,7 +1593,7 @@ const volatile DPT_Start* app_get_DPT_Start_array(const char *url, DPT_Start* ou * // do something with my_var * ~~~ */ -const volatile DPT_Start* app_get_DPT_Start_array_elems(const char *url, DPT_Start* out, int start, int n); +const DPT_Start* app_get_DPT_Start_array_elems(const char *url, DPT_Start* out, int start, int n); /** * @ingroup DPT_Start @@ -1477,6 +1643,7 @@ bool oc_parse_DPT_Start_array(oc_rep_t *rep, DPT_Start *out, int n); * @brief Encode a DPT_Start using oc_rep * * @param[in] in The DPT_Start to encode + * @param[is_metadata] is_metadata Whether function is called during metadata query handling * * ~~~{.c} * DPT_Start my_var; @@ -1489,7 +1656,7 @@ bool oc_parse_DPT_Start_array(oc_rep_t *rep, DPT_Start *out, int n); * uint8_t buf = oc_rep_get_encoder_buf(); * ~~~ */ -void oc_encode_DPT_Start(const DPT_Start *in); +void oc_encode_DPT_Start(const DPT_Start *in, bool is_metadata); /** * @ingroup DPT_Start @@ -1581,6 +1748,7 @@ bool persistent_load_DPT_Start(const char *name, DPT_Start *out); */ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n); /**@}*/ + /** * @name DPT_Uint_XY functions * getters/setters and other functions for DPT_Uint_XY @@ -1595,6 +1763,18 @@ bool persistent_load_DPT_Start_array(const char *name, DPT_Start *out, int n); */ bool app_is_DPT_Uint_XY_url(const char* url); +/** + * @ingroup DPT_Uint_XY + * @brief Set a DPT_Uint_XY to the default value + * + * @param[in] url the url for the DPT_Uint_XY to set + * + * ~~~{.c} + * app_set_DPT_Uint_XY_default_value("/some/url"); + * ~~~ + */ +void app_set_DPT_Uint_XY_default_value(const char* url); + /** * @ingroup DPT_Uint_XY * @brief Set a DPT_Uint_XY @@ -1621,6 +1801,7 @@ void app_set_DPT_Uint_XY_variable(const char* url, const DPT_Uint_XY* in); * @param[in] in a pointer to the DPT_Uint_XY to copy * Can be the global variable itself * @param[in] n number of elements in the array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Uint_XY my_arr[5]; @@ -1628,7 +1809,7 @@ void app_set_DPT_Uint_XY_variable(const char* url, const DPT_Uint_XY* in); * app_set_DPT_Uint_XY_array("/some/url", my_arr, 5); * ~~~ */ -void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n); +void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n, bool store_persistently); /** * @ingroup DPT_Uint_XY @@ -1641,6 +1822,7 @@ void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n); * Can be the global variable itself * @param[in] start starting index to write to array * @param[in] n number of elements to write to array + * @param[in] store_persistently Whether or not the value should also be stored in persistent store. * * ~~~{.c} * DPT_Uint_XY my_var; @@ -1649,7 +1831,7 @@ void app_set_DPT_Uint_XY_array(const char* url, const DPT_Uint_XY* in, int n); * app_set_DPT_Uint_XY_array("/some/url", &my_var, 5, 1); * ~~~ */ -void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int start, int n); +void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int start, int n, bool store_persistently); /** * @ingroup DPT_Uint_XY @@ -1676,7 +1858,7 @@ void app_set_DPT_Uint_XY_array_elems(const char* url, const DPT_Uint_XY* in, int * // do something with my_var * ~~~ */ -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_variable(const char *url, DPT_Uint_XY* out); +const DPT_Uint_XY* app_get_DPT_Uint_XY_variable(const char *url, DPT_Uint_XY* out); /** * @ingroup DPT_Uint_XY @@ -1704,7 +1886,67 @@ const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_variable(const char *url, DPT_Ui * // do something with my_arr * ~~~ */ -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array(const char *url, DPT_Uint_XY* out, int n); +const DPT_Uint_XY* app_get_DPT_Uint_XY_array(const char *url, DPT_Uint_XY* out, int n); + + +/** + * @ingroup DPT_Uint_XY + * @brief Get a DPT_Uint_XY as string + * + * @param[in] in the data type + * @param[in] text, reserved space to copy the generated text too + * @param[in] size size of the allocated text + * + * ~~~{.c} + * DPT_Uint_XY my_type; + * char my_text[100]; + * if (app_sprintf_DPT_Uint_XY(my_type, my_text, 100) != 0) { + * //Something went wrong + * return; + * } + * // printf(my_text); + * ~~~ + */ +int app_sprintf_DPT_Uint_XY(const DPT_Uint_XY *in, char* text, int size); + + +/** + * @ingroup DPT_Uint_XY + * @brief Get a DPT_Uint_XY from a string + * + * @param[in] in the data type + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Uint_XY my_type; + * char my_text[100]; + * if (app_sscanf_DPT_Uint_XY(&my_type, my_text) != 0) { + * //Something went wrong + * return; + * } + * // do something with my_type + * ~~~ + */ +int app_sscanf_DPT_Uint_XY(DPT_Uint_XY *in, char* text); + +/** + * @ingroup DPT_Uint_XY + * @brief Get an example of DPT_Uint_XY in a string + * + * @param[in] select 1 = format, 2 = example + * @param[in] text, the input string + * + * ~~~{.c} + * DPT_Uint_XY my_type; + * char my_text[100]; + * if (app_str_expected_DPT_Uint_XY(1 , my_text) != 0) { + * //Something went wrong + * return; + * } + * printf(my_text); + * ~~~ + */ +int app_str_expected_DPT_Uint_XY(int select, char* text); /** * @ingroup DPT_Uint_XY @@ -1734,7 +1976,7 @@ const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array(const char *url, DPT_Uint_ * // do something with my_var * ~~~ */ -const volatile DPT_Uint_XY* app_get_DPT_Uint_XY_array_elems(const char *url, DPT_Uint_XY* out, int start, int n); +const DPT_Uint_XY* app_get_DPT_Uint_XY_array_elems(const char *url, DPT_Uint_XY* out, int start, int n); /** * @ingroup DPT_Uint_XY @@ -1784,6 +2026,7 @@ bool oc_parse_DPT_Uint_XY_array(oc_rep_t *rep, DPT_Uint_XY *out, int n); * @brief Encode a DPT_Uint_XY using oc_rep * * @param[in] in The DPT_Uint_XY to encode + * @param[is_metadata] is_metadata Whether function is called during metadata query handling * * ~~~{.c} * DPT_Uint_XY my_var; @@ -1796,7 +2039,7 @@ bool oc_parse_DPT_Uint_XY_array(oc_rep_t *rep, DPT_Uint_XY *out, int n); * uint8_t buf = oc_rep_get_encoder_buf(); * ~~~ */ -void oc_encode_DPT_Uint_XY(const DPT_Uint_XY *in); +void oc_encode_DPT_Uint_XY(const DPT_Uint_XY *in, bool is_metadata); /** * @ingroup DPT_Uint_XY @@ -1915,7 +2158,16 @@ void app_set_bool_variable(const char* url, bool value); */ bool app_retrieve_bool_variable(const char *url); - +/** + * @brief Get a int + * + * @param url the url for the int to get + * @param value the value to be used for the return value + * @return bool, true, value is set + */ +bool app_retrieve_int_variable(const char* url, int* value); + + /** * @brief checks if the url represents a parameter * @@ -1988,12 +2240,13 @@ void app_str_to_upper(char *str); * @param url the url of the resource/data point */ void dev_btn_toggle_cb(const char *url); + void onReceivedShotStatus(const char *url); void onReceivedShot(const char *url); void onReceivedReady(const char *url); -void app_initialize(); +void app_initialize(); +void eink_load_screen(enum Screen screen_nr); #ifdef __cplusplus } #endif - diff --git a/BATTLESHIP/knx_eink_battleships_dev.c b/BATTLESHIP/knx_eink_battleships_dev.c index 8c51cba..5b69540 100644 --- a/BATTLESHIP/knx_eink_battleships_dev.c +++ b/BATTLESHIP/knx_eink_battleships_dev.c @@ -60,28 +60,42 @@ #include "oc_api.h" #include "oc_core_res.h" +#include "api/oc_knx_dev.h" #include "api/oc_knx_fp.h" #include "port/oc_clock.h" #include "port/dns-sd.h" +#ifndef EXCLUDE_CASCODA_BAREMETAL + #include "cascoda-bm/cascoda_sensorif.h" #include "cascoda-bm/cascoda_interface.h" #include "cascoda-util/cascoda_tasklet.h" #include "cascoda-util/cascoda_time.h" -#include "devboard_btn.h" -#include "devboard_btn_ext.h" +#include "cascoda-bm/cascoda_wait.h" #include "ca821x_error.h" +#include "sed_poll.h" #include #include +//#include "sif_btn_ext_pi4ioe5v6408.h" + +#include "devboard_btn.h" +#include "devboard_btn_ext.h" + + #ifdef SLEEPY #include "knx_iot_sleepy_main_extern.h" +#include "knx_iot_sleepy_main.h" #else #include "knx_iot_wakeful_main_extern.h" #endif -#include "knx_eink_battleships.h" #include "sif_ssd1681.h" // 1.5 inch display #define SPI_NUM 1 +#endif /* EXCLUDE_CASCODA_BAREMETAL */ +#include "knx_eink_battleships.h" + + + // generic defines #define SCHEDULE_NOW 0 #define S_MODE_INTERVAL 30 @@ -90,13 +104,12 @@ #define THIS_DEVICE 0 #ifdef SLEEPY -// tick timer upon last wake -static uint32_t g_time_of_last_wake; -// tasklet for Thread keep-alive -static ca_tasklet g_sed_poll_tasklet; -extern otInstance *OT_INSTANCE; +#include "sed_poll.h" #endif +// forward declaration. +void dev_put_callback(const char* url); + /** * @brief retrieve the fault state of the url/data point * the caller needs to know if the resource/data point implements a fault situation @@ -111,6 +124,7 @@ bool app_retrieve_fault_variable(const char* url); + // Uncomment the line of code below if you want the test code to execute //#define ACTUATOR_TEST_MODE @@ -166,24 +180,6 @@ void dev_put_callback(const char* url){ * @param url the url that received a GET invocation. */ void dev_get_callback(const char* url){ - if (strcmp(url, URL_RECEIVESHOT) == 0) { - if (strcmp(url, URL_RECEIVESHOT) == 0) - { - onReceivedShot(url); - } - } - if (strcmp(url, URL_RECEIVESHOTSTATUS) == 0) { - if (strcmp(url, URL_RECEIVESHOTSTATUS) == 0) - { - onReceivedShotStatus(url); - } - } - if (strcmp(url, URL_RECEIVEREADY) == 0) { - if (strcmp(url, URL_RECEIVEREADY) == 0) - { - onReceivedReady(url); - } - } } /** * @brief reset the device (knx only) @@ -216,30 +212,23 @@ void programming_mode_embedded(size_t device_index, bool programming_mode) oc_knx_device_set_programming_mode(device_index, programming_mode); if (g_screen_nr == DEV_SCREEN) - refresh_screen(false); + refresh_screen(false); } #ifdef SLEEPY - // Sleepy handler for programming mode button bool hardware_can_sleep() { -#ifdef SLEEPY - return (DVBD_CanSleep() && !oc_knx_device_in_programming_mode(THIS_DEVICE)); -#else - return DVBD_CanSleep(); -#endif +return DVBD_CanSleep(); } // Sleepy Device void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent) { - uint32_t taskletTimeLeft = SED_POLL_PERIOD; + // 20 min wakeup if no tasklet is scheduled (should not happen) + uint32_t taskletTimeLeft = 20 * 60 * 1000; - /* Schedule a data poll if one is not already scheduled */ - if (!TASKLET_IsQueued(&g_sed_poll_tasklet)) - TASKLET_ScheduleDelta(&g_sed_poll_tasklet, SED_POLL_PERIOD, NULL); /* schedule wakeup */ TASKLET_GetTimeToNext(&taskletTimeLeft); @@ -247,23 +236,26 @@ void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent) if (taskletTimeLeft > nextAppEvent) taskletTimeLeft = nextAppEvent; - bool has_min_awake_time_passed = TIME_Cmp(TIME_ReadAbsoluteTime(), g_time_of_last_wake + SED_MIN_AWAKE_TIME) >= 0; - bool sleep_after_joining = has_min_awake_time_passed || (otThreadGetDeviceRole(OT_INSTANCE) != OT_DEVICE_ROLE_DETACHED); + bool sleep_after_joining = otThreadGetDeviceRole(OT_INSTANCE) != OT_DEVICE_ROLE_DETACHED; /* check that it's worth going to sleep */ if (taskletTimeLeft > 100 && sleep_after_joining) { /* and sleep */ - DVBD_DevboardSleep(taskletTimeLeft, pDeviceRef); - g_time_of_last_wake = TIME_ReadAbsoluteTime(); + DVBD_DevboardSleep(taskletTimeLeft, pDeviceRef); } } void hardware_reinitialise(void) { +#ifdef SLEEPY_USE_LED + // hardcoded pin # of sensor board pm mode led + BSP_ModuleSetGPIOPin(36, LED_ON); +#endif } #endif // SLEEPY + /** * @brief do the hardware installation * @@ -274,37 +266,28 @@ void hardware_init() /* set the put callback on the underlaying code */ app_set_put_cb(dev_put_callback); //app_set_get_cb(dev_get_callback); -#ifdef SLEEPY - // Initialize sleepy timeout handler - TASKLET_Init(&g_sed_poll_tasklet, &sed_poll_handler); - g_time_of_last_wake = TIME_ReadAbsoluteTime(); - #ifdef SLEEPY_USE_LED // Debug: blink programming mode indicator on wakeup - DVBD_RegisterLEDOutput(PROGRAMMING_MODE_INDICATOR, JUMPER_POS_1); - DVBD_SetLED(PROGRAMMING_MODE_INDICATOR, LED_ON); -#endif + // hardcoded pin # of sensor board pm mode led + BSP_ModuleRegisterGPIOOutputOD(36, MODULE_PIN_TYPE_LED); + BSP_ModuleSetGPIOPin(36, LED_ON); #endif /* Eink Initialisation */ SENSORIF_SPI_Config(SPI_NUM); SIF_SSD1681_Initialise(); DVBD_RegisterButtonIRQInput(DEV_SWITCH_2, JUMPER_POS_1); DVBD_SetButtonShortPressCallback(DEV_SWITCH_2, &button_1_ShortPress_cb, NULL, BTN_SHORTPRESS_RELEASED); - DVBD_SetButtonLongPressCallback(DEV_SWITCH_2, &button_1_LongPress_cb, NULL, BTN_LONGPRESS_TIME_DEFAULT); + DVBD_SetButtonLongPressCallback(DEV_SWITCH_2, &button_1_LongPress_cb, NULL, BTN_LONGPRESS_TIME_DEFAULT); DVBD_RegisterButtonIRQInput(DEV_SWITCH_3, JUMPER_POS_1); DVBD_SetButtonShortPressCallback(DEV_SWITCH_3, &button_2_ShortPress_cb, NULL, BTN_SHORTPRESS_RELEASED); - DVBD_SetButtonLongPressCallback(DEV_SWITCH_3, &button_2_LongPress_cb, NULL, BTN_LONGPRESS_TIME_DEFAULT); + DVBD_SetButtonLongPressCallback(DEV_SWITCH_3, &button_2_LongPress_cb, NULL, BTN_LONGPRESS_TIME_DEFAULT); DVBD_RegisterButtonIRQInput(DEV_SWITCH_4, JUMPER_POS_1); DVBD_SetButtonShortPressCallback(DEV_SWITCH_4, &button_3_ShortPress_cb, NULL, BTN_SHORTPRESS_RELEASED); DVBD_SetButtonLongPressCallback(DEV_SWITCH_4, &button_3_LongPress_cb, NULL, BTN_LONGPRESS_TIME_DEFAULT); - DVBD_SetButtonHoldCallback(DEV_SWITCH_4, &button_3_Hold_cb, NULL, BTN_HOLD_TIME_DEFAULT); + DVBD_SetButtonHoldCallback(DEV_SWITCH_4, &button_3_Hold_cb, NULL, BTN_HOLD_TIME_DEFAULT); -// gfx_drv_initialise(); - - - @@ -316,44 +299,12 @@ void hardware_init() actuator_test_init(); #endif -if (!otDatasetIsCommissioned(OT_INSTANCE)) - { - PRINT_APP("Registering THREAD qr code"); - static struct qr_code_t ot_qr_code; - static char ot_data_buf[64]; - ot_qr_code.str_data = ot_data_buf; - PlatformGetQRString(ot_data_buf, 63, OT_INSTANCE); - ot_qr_code.desc = "Thread QR code"; - register_qr_code(&ot_qr_code); - } - - void do_knx_reset(){ - const char *url = NULL; - for (int i = 0; url = app_get_parameter_url(i); i++) { - oc_storage_erase(url); - } - oc_reset_device(THIS_DEVICE, 2); - } - - void do_thread_reset(){ - PlatformEraseJoinerCredentials(OT_INSTANCE); - BSP_SystemReset(SYSRESET_APROM); - } - - //register resets - static struct reset_t knx_reset, ot_reset; - knx_reset.desc = "KNX reset"; - knx_reset.do_reset = &do_knx_reset; - ot_reset.desc = "Thread reset"; - ot_reset.do_reset = &do_thread_reset; - register_reset(&knx_reset); - register_reset(&ot_reset); - - - +#ifdef SLEEPY + // default sleepy polling: poll every 10 minutes + SED_InitPolling(1500, 10 * 60 * 1000, 0); +#endif } - /** @@ -363,11 +314,13 @@ if (!otDatasetIsCommissioned(OT_INSTANCE)) */ void hardware_poll() { +#ifndef EXCLUDE_CASCODA_BAREMETAL + + DVBD_PollButtons(); - // Add a delay so we don't poll too fast - // as this affects the brightness of the - // shared LEDs - WAIT_ms(3); + + +#endif /* EXCLUDE_CASCODA_BAREMETAL */ } bool app_is_url_in_use(const char* url) @@ -388,8 +341,6 @@ bool app_is_url_in_use(const char* url) return false; } - - #ifdef ACTUATOR_TEST_MODE /* code for actuator testing */ @@ -422,6 +373,9 @@ void actuator_test_init() } #endif // ACTUATOR_TEST_MODE + +#ifndef EXCLUDE_CASCODA_BAREMETAL //embedded is always built with __attribute__((__weak__)) extern void app_initialize(){} +#endif /* EXCLUDE_CASCODA_BAREMETAL */ diff --git a/BATTLESHIP/knx_eink_battleships_gui.cpp b/BATTLESHIP/knx_eink_battleships_gui.cpp new file mode 100644 index 0000000..4696daf --- /dev/null +++ b/BATTLESHIP/knx_eink_battleships_gui.cpp @@ -0,0 +1,1528 @@ +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Copyright (c) 2022-2023 Cascoda Ltd +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Cascoda Limited. + * integrated circuit in a product or a software update for such product, must + * reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * 4. This software, whether provided in binary or any other form must not be decompiled, + * disassembled, reverse engineered or otherwise modified. + * + * 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. + * + * THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +// 2024-06-17 16:19:21.624451 + +// For compilers that support precompilation, includes "wx/wx.h". +#include +#include +#ifndef WX_PRECOMP + #include +#endif +#include + +#ifdef WIN32 +#include +#endif + +#define NO_MAIN +#include "knx_eink_battleships.h" +#include "api/oc_knx_dev.h" +#include "api/oc_knx_sec.h" +#include "api/oc_knx_fp.h" +#include "port/dns-sd.h" + +enum +{ + RESET = wxID_HIGHEST + 1, // ID for reset button in the menu + RESET_TABLE = RESET + 1, // ID for clear table button in the menu + IA_TEXT = RESET_TABLE + 1, // ID for internal address text + IID_TEXT = IA_TEXT + 1, // ID for installation id text + PM_TEXT = IID_TEXT + 1, // ID for programming mode text + LS_TEXT = PM_TEXT + 1, // ID for load status text + HOSTNAME_TEXT = LS_TEXT + 1, // ID for hostname text + GOT_TABLE_ID = HOSTNAME_TEXT + 1, // ID for the Group object window + PUB_TABLE_ID = GOT_TABLE_ID + 1, // ID for the publisher table window + REC_TABLE_ID = PUB_TABLE_ID + 1, // ID for the recipient table window + PARAMETER_LIST_ID = REC_TABLE_ID + 1, // ID for the parameter window + AT_TABLE_ID = PARAMETER_LIST_ID + 1, // ID for the auth/at window + CHECK_GA_DISPLAY = AT_TABLE_ID + 1 , // ga display check + CHECK_IID_DISPLAY = CHECK_GA_DISPLAY + 1, // iid display check + CHECK_GRPID_DISPLAY = CHECK_IID_DISPLAY + 1, // grpid display check + CHECK_SLEEPY = CHECK_GRPID_DISPLAY + 1 , // sleepy check + CHECK_PM = CHECK_SLEEPY + 1 , // programming mode check in menu bar + DP_IDSENDSHOT = CHECK_PM + 1, // SendShot for /p/o_1_1 + DP_IDRECEIVESHOT = CHECK_PM + 2, // ReceiveShot for /p/o_1_2 + DP_IDSENDSHOTSTATUS = CHECK_PM + 3, // SendShotStatus for /p/o_1_3 + DP_IDRECEIVESHOTSTATUS = CHECK_PM + 4, // ReceiveShotStatus for /p/o_1_4 + DP_IDSENDREADY = CHECK_PM + 5, // SendReady for /p/o_1_5 + DP_IDRECEIVEREADY = CHECK_PM + 6 // ReceiveReady for /p/o_1_6 +}; + +static const wxCmdLineEntryDesc g_cmdLineDesc[] = +{ + { wxCMD_LINE_OPTION, "s", "serialnumber", "serial number", wxCMD_LINE_VAL_STRING }, + { wxCMD_LINE_NONE } +}; + +wxCmdLineParser* g_cmd; + + +//---------------------------------------------------- +//---------------------------------------------------- +//---------------------------------------------------- + +class CustomDialog : public wxDialog +{ +public: + CustomDialog(const wxString& title, const wxString& text); + +private: + void on_close(wxCommandEvent& event); +}; + +void CustomDialog::on_close(wxCommandEvent& event) +{ + this->Destroy(); +} + +CustomDialog::CustomDialog(const wxString& title, const wxString& text) + : wxDialog(NULL, -1, title, wxDefaultPosition, wxSize(550, 300)) +{ + int size_x = 520; + int size_y = 300; + + wxPanel* panel = new wxPanel(this, -1); + + wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL); + + wxTextCtrl* tc = new wxTextCtrl(panel, -1, text, wxPoint(10, 10), + wxSize(size_x, size_y), wxTE_MULTILINE | wxTE_READONLY); + + wxButton* closeButton = new wxButton(this, -1, wxT("Close"), + wxDefaultPosition, wxDefaultSize); + closeButton->Bind(wxEVT_BUTTON, &CustomDialog::on_close, this); + + hbox->Add(closeButton, 1, wxLEFT, 5); + vbox->Add(panel, 1); + vbox->Add(hbox, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, 10); + + SetSizerAndFit(vbox); + Centre(); + ShowModal(); + Destroy(); +} + +//---------------------------------------------------- +//---------------------------------------------------- +//---------------------------------------------------- + +class MyApp : public wxApp +{ +public: + virtual bool OnInit(); +}; + +//---------------------------------------------------- +//---------------------------------------------------- +//---------------------------------------------------- + +class ScrolledWidgetsPane : public wxScrolledWindow +{ +public: + wxFrame* m_parentFrame= NULL; + + ScrolledWidgetsPane(wxWindow* parent, wxWindowID id) : wxScrolledWindow(parent, id) + { + // the sizer will take care of determining the needed scroll size + // (if you don't use sizers you will need to manually set the viewport size) + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + + int x_width = 230; /* width of the widgets */ + int x_height = 25; /* height of the widgets */ + int border_size = 1; /* border size, size between the widgets */ + // complex data type, as string + char text_SENDSHOT[100]; + memset(text_SENDSHOT, 0, 100); + app_str_expected_DPT_Uint_XY(2, text_SENDSHOT); + mSENDSHOT = new wxTextCtrl(this, DP_IDSENDSHOT, text_SENDSHOT, wxPoint(0, 0), wxSize(x_width, x_height), wxTE_PROCESS_ENTER); + char text_tt_SENDSHOT[100]; + memset(text_tt_SENDSHOT, 0, 100); + app_str_expected_DPT_Uint_XY(1, text_tt_SENDSHOT); + mSENDSHOT->SetToolTip(text_tt_SENDSHOT); + mSENDSHOT->Enable(true); + { + + wxTextCtrl* lSENDSHOT_text = new wxTextCtrl(this, -1, _T("SendShot ('/p/o_1_1') if.o "), wxPoint(0, 0), wxSize(x_width, x_height)); + lSENDSHOT_text->SetEditable(false); + lSENDSHOT_text->SetToolTip("SendShot urn:knx:dpt.uint_XY ['urn:knx:dpa.65500.101'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lSENDSHOT_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mSENDSHOT, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + // add callback + mSENDSHOT->Bind(wxEVT_TEXT_ENTER, &ScrolledWidgetsPane::OnTextSENDSHOT, this); + // complex data type, as string + char text_RECEIVESHOT[100]; + memset(text_RECEIVESHOT, 0, 100); + app_str_expected_DPT_Uint_XY(2, text_RECEIVESHOT); + mRECEIVESHOT = new wxTextCtrl(this, DP_IDRECEIVESHOT, text_RECEIVESHOT, wxPoint(0, 0), wxSize(x_width, x_height), wxTE_PROCESS_ENTER); + char text_tt_RECEIVESHOT[100]; + memset(text_tt_RECEIVESHOT, 0, 100); + app_str_expected_DPT_Uint_XY(1, text_tt_RECEIVESHOT); + mRECEIVESHOT->SetToolTip(text_tt_RECEIVESHOT); + mRECEIVESHOT->Enable(true); + { + + wxTextCtrl* lRECEIVESHOT_text = new wxTextCtrl(this, -1, _T("ReceiveShot ('/p/o_1_2') if.i "), wxPoint(0, 0), wxSize(x_width, x_height)); + lRECEIVESHOT_text->SetEditable(false); + lRECEIVESHOT_text->SetToolTip("ReceiveShot urn:knx:dpt.uint_XY ['urn:knx:dpa.65501.111'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lRECEIVESHOT_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mRECEIVESHOT, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + // complex data type, as string + char text_SENDSHOTSTATUS[100]; + memset(text_SENDSHOTSTATUS, 0, 100); + app_str_expected_DPT_Shot_Status(2, text_SENDSHOTSTATUS); + mSENDSHOTSTATUS = new wxTextCtrl(this, DP_IDSENDSHOTSTATUS, text_SENDSHOTSTATUS, wxPoint(0, 0), wxSize(x_width, x_height), wxTE_PROCESS_ENTER); + char text_tt_SENDSHOTSTATUS[100]; + memset(text_tt_SENDSHOTSTATUS, 0, 100); + app_str_expected_DPT_Shot_Status(1, text_tt_SENDSHOTSTATUS); + mSENDSHOTSTATUS->SetToolTip(text_tt_SENDSHOTSTATUS); + mSENDSHOTSTATUS->Enable(true); + { + + wxTextCtrl* lSENDSHOTSTATUS_text = new wxTextCtrl(this, -1, _T("SendShotStatus ('/p/o_1_3') if.o "), wxPoint(0, 0), wxSize(x_width, x_height)); + lSENDSHOTSTATUS_text->SetEditable(false); + lSENDSHOTSTATUS_text->SetToolTip("SendShotStatus urn:knx:dpt.shot_Status ['urn:knx:dpa.65501.102'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lSENDSHOTSTATUS_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mSENDSHOTSTATUS, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + // add callback + mSENDSHOTSTATUS->Bind(wxEVT_TEXT_ENTER, &ScrolledWidgetsPane::OnTextSENDSHOTSTATUS, this); + // complex data type, as string + char text_RECEIVESHOTSTATUS[100]; + memset(text_RECEIVESHOTSTATUS, 0, 100); + app_str_expected_DPT_Shot_Status(2, text_RECEIVESHOTSTATUS); + mRECEIVESHOTSTATUS = new wxTextCtrl(this, DP_IDRECEIVESHOTSTATUS, text_RECEIVESHOTSTATUS, wxPoint(0, 0), wxSize(x_width, x_height), wxTE_PROCESS_ENTER); + char text_tt_RECEIVESHOTSTATUS[100]; + memset(text_tt_RECEIVESHOTSTATUS, 0, 100); + app_str_expected_DPT_Shot_Status(1, text_tt_RECEIVESHOTSTATUS); + mRECEIVESHOTSTATUS->SetToolTip(text_tt_RECEIVESHOTSTATUS); + mRECEIVESHOTSTATUS->Enable(true); + { + + wxTextCtrl* lRECEIVESHOTSTATUS_text = new wxTextCtrl(this, -1, _T("ReceiveShotStatus ('/p/o_1_4') if.i "), wxPoint(0, 0), wxSize(x_width, x_height)); + lRECEIVESHOTSTATUS_text->SetEditable(false); + lRECEIVESHOTSTATUS_text->SetToolTip("ReceiveShotStatus urn:knx:dpt.shot_Status ['urn:knx:dpa.65500.112'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lRECEIVESHOTSTATUS_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mRECEIVESHOTSTATUS, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + //DP_IDSENDREADY bool if.o + // if.o ==> sensor == possible to change value in UI + mSENDREADY = new wxButton(this, DP_IDSENDREADY, _T("SendReady"), wxPoint(0, 0), wxSize(x_width, x_height)); + mSENDREADY->Bind(wxEVT_BUTTON, &ScrolledWidgetsPane::OnPressedSendReady, this); + { + wxTextCtrl* lSENDREADY_text = new wxTextCtrl(this, -1, _T("SendReady ('/p/o_1_5')"), wxPoint(0, 0), wxSize(x_width, x_height)); + lSENDREADY_text->SetEditable(false); + lSENDREADY_text->SetToolTip("SendReady urn:knx:dpt.start ['urn:knx:dpa.65500.103'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lSENDREADY_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mSENDREADY, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + //DP_IDRECEIVEREADY bool if.i + mRECEIVEREADY = new wxCheckBox(this, DP_IDRECEIVEREADY, _T("ReceiveReady "), wxPoint(0, 0), wxSize(x_width, x_height)); + mRECEIVEREADY->Enable(false); + { + wxTextCtrl* lRECEIVEREADY_text = new wxTextCtrl(this, -1, _T("ReceiveReady ('/p/o_1_6') "), wxPoint(0, 0), wxSize(x_width, x_height)); + lRECEIVEREADY_text->SetEditable(false); + lRECEIVEREADY_text->SetToolTip("ReceiveReady urn:knx:dpt.start ['urn:knx:dpa.65501.113'] "); + // add sizer for the group. + wxBoxSizer* vsizer = new wxBoxSizer(wxHORIZONTAL); + vsizer->Add(lRECEIVEREADY_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + vsizer->Add(mRECEIVEREADY, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // add the sizer + sizer->Add(vsizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + } + + this->SetSizer(sizer); + // this part makes the scrollbars show up + this->FitInside(); // ask the sizer about the needed size + this->SetScrollRate(5, 5); + } + + //-------------------------- + //-------------------------- + // complex datatype + void OnTextSENDSHOT(wxCommandEvent& event); + // complex datatype + void OnTextSENDSHOTSTATUS(wxCommandEvent& event); + void OnPressedSendReady(wxCommandEvent& event); + //DP_IDSENDSHOT + //type is complex! + wxTextCtrl* mSENDSHOT; + //DP_IDRECEIVESHOT + //type is complex! + wxTextCtrl* mRECEIVESHOT; + //DP_IDSENDSHOTSTATUS + //type is complex! + wxTextCtrl* mSENDSHOTSTATUS; + //DP_IDRECEIVESHOTSTATUS + //type is complex! + wxTextCtrl* mRECEIVESHOTSTATUS; + //DP_IDSENDREADY bool + wxButton* mSENDREADY; // SendReady if.o + //DP_IDRECEIVEREADY bool + wxCheckBox* mRECEIVEREADY ; // ReceiveReady if.i +}; +void ScrolledWidgetsPane::OnPressedSendReady(wxCommandEvent& event) +{ + char url[] = "/p/o_1_5"; + char my_text[100]; + bool p = (bool)*app_get_DPT_Start_variable(url, NULL); + if (p == true) { + p = false; + } + else { + p = true; + } + app_set_DPT_Start_variable(url, (DPT_Start*)&p); + oc_do_s_mode_with_scope(2, url, "w"); + oc_do_s_mode_with_scope(5, url, "w"); + sprintf(my_text, "SendReady ('%s') pressed: %d", url, (int)p); + this->m_parentFrame->SetStatusText(my_text); +} +// Complex datatype for SendShot +void ScrolledWidgetsPane::OnTextSENDSHOT(wxCommandEvent& event) +{ + char url[] = "/p/o_1_1"; + char my_text[200]; + bool input_ok = true; + DPT_Uint_XY dt_converted; + + sprintf(my_text, "invalid text "); + wxString entered_str = mSENDSHOT->GetValue(); + wxCharBuffer buffer = entered_str.ToUTF8(); + char* text_as_char = buffer.data(); + int error = app_sscanf_DPT_Uint_XY(&dt_converted, text_as_char); + if ( error == 0 ){ + if (input_ok) { + // send out the message + //dt_converted = (DPT_Uint_XY)converted; + app_set_DPT_Uint_XY_variable(url, &dt_converted); + oc_do_s_mode_with_scope(2, url, "w"); + oc_do_s_mode_with_scope(5, url, "w"); + sprintf(my_text, "Sensor SendShot (/p/o_1_1) :: %s ", text_as_char); + } + } else { + char txt[100]; + memset(txt, 0, 100); + app_str_expected_DPT_Uint_XY(1, txt); + strcat(my_text, " expected format: "); + strcat(my_text, txt); + } + this->m_parentFrame->SetStatusText(my_text); +} + + + +// Complex datatype for SendShotStatus +void ScrolledWidgetsPane::OnTextSENDSHOTSTATUS(wxCommandEvent& event) +{ + char url[] = "/p/o_1_3"; + char my_text[200]; + bool input_ok = true; + DPT_Shot_Status dt_converted; + + sprintf(my_text, "invalid text "); + wxString entered_str = mSENDSHOTSTATUS->GetValue(); + wxCharBuffer buffer = entered_str.ToUTF8(); + char* text_as_char = buffer.data(); + int error = app_sscanf_DPT_Shot_Status(&dt_converted, text_as_char); + if ( error == 0 ){ + if (input_ok) { + // send out the message + //dt_converted = (DPT_Shot_Status)converted; + app_set_DPT_Shot_Status_variable(url, &dt_converted); + oc_do_s_mode_with_scope(2, url, "w"); + oc_do_s_mode_with_scope(5, url, "w"); + sprintf(my_text, "Sensor SendShotStatus (/p/o_1_3) :: %s ", text_as_char); + } + } else { + char txt[100]; + memset(txt, 0, 100); + app_str_expected_DPT_Shot_Status(1, txt); + strcat(my_text, " expected format: "); + strcat(my_text, txt); + } + this->m_parentFrame->SetStatusText(my_text); +} + + + + +//---------------------------------------------------- +//---------------------------------------------------- +//---------------------------------------------------- +//---------------------------------------------------- + +class MyFrame : public wxFrame +{ +public: + MyFrame(char* serial_number); +private: + void OnGroupObjectTable(wxCommandEvent& event); + void OnPublisherTable(wxCommandEvent& event); + void OnRecipientTable(wxCommandEvent& event); + void OnParameterList(wxCommandEvent& event); + void OnAuthTable(wxCommandEvent& event); + void OnProgrammingMode(wxCommandEvent& event); + void OnSleepyMode(wxCommandEvent& event); + void OnReset(wxCommandEvent& event); + void OnClearTables(wxCommandEvent& event); + void OnExit(wxCommandEvent& event); + void OnAbout(wxCommandEvent& event); + void OnTimer(wxTimerEvent& event); + + void updateInfoCheckBoxes(); + void updateInfoButtons(); + void updateTextButtons(); + void bool2text(bool on_off, char* text); + void int2text(int value, char* text); + void int2gatext(uint32_t value, char* text, bool as_ets=false); + void int2grpidtext(uint64_t value, char* text, bool as_ets); + void int2scopetext(uint32_t value, char* text); + void double2text(double value, char* text); + + wxMenu* m_menuFile; + wxMenu* m_menuDisplay; + wxMenu* m_menuOptions; + wxTimer m_timer; + ScrolledWidgetsPane* m_scrolledwindow; + + // sleepy information + int m_sleep_counter = 0; + int m_sleep_seconds = 20; + + wxTextCtrl* m_ia_text; // text control for internal address + wxTextCtrl* m_iid_text; // text control for installation id + wxTextCtrl* m_pm_text; // text control for programming mode + wxTextCtrl* m_ls_text; // text control for load state + wxTextCtrl* m_hostname_text; // text control for host name + wxTextCtrl* m_secured_text; // text secure/not secure +}; + +wxIMPLEMENT_APP(MyApp); + +/** + * @brief initialization of the application + * + * @return true + * @return false + */ +bool MyApp::OnInit() +{ + int argc = wxAppConsole::argc; + wxChar** argv = wxAppConsole::argv; + + g_cmd = new wxCmdLineParser(argc, argv); + g_cmd->SetDesc(g_cmdLineDesc); + g_cmd->Parse(true); + + wxString serial_number; + if (g_cmd->Found("s", &serial_number)) { + } + MyFrame* frame = new MyFrame((char*)(serial_number.c_str()).AsChar()); + frame->SetVirtualSize(650, 300); + frame->SetSize(700, 400); + frame->Show(true); + return true; +} + +/** + * @brief Construct a new My Frame:: My Frame object + * + * @param str_serial_number + */ +MyFrame::MyFrame(char* str_serial_number) + : wxFrame(NULL, wxID_ANY, "KNX Battleships Demo") +{ + m_menuFile = new wxMenu; + m_menuFile->Append(GOT_TABLE_ID, "List Group Object Table", "List the Group object table", false); + m_menuFile->Append(PUB_TABLE_ID, "List Publisher Table", "List the Publisher table", false); + m_menuFile->Append(REC_TABLE_ID, "List Recipient Table", "List the Recipient table", false); + m_menuFile->Append(PARAMETER_LIST_ID, "List Parameters", "List the parameters of the device", false); + m_menuFile->Append(AT_TABLE_ID, "List Auth/AT Table", "List the security data of the device", false); + m_menuFile->Append(CHECK_PM, "Programming Mode", "Sets the application in programming mode", true); + m_menuFile->Append(RESET_TABLE, "Reset (7) (Tables)", "Reset 7 (Reset to default without IA).", false); + m_menuFile->Append(RESET, "Reset (2)(ex-factory)", "Reset 2 (Reset to default state)", false); + m_menuFile->AppendSeparator(); + m_menuFile->Append(wxID_EXIT); + // display menu + // display menu + m_menuDisplay = new wxMenu; + m_menuDisplay->Append(CHECK_GA_DISPLAY, "GA 3-level (ETS)", "Displays the group addresses as GA 3-Level or as integer", true); + m_menuDisplay->Check(CHECK_GA_DISPLAY, true); + m_menuDisplay->Append(CHECK_GRPID_DISPLAY, "GRPID as partial ipv6 address (ETS)", "Displays the grpid as integer", true); + m_menuDisplay->Check(CHECK_GRPID_DISPLAY, true); + m_menuDisplay->Append(CHECK_IID_DISPLAY, "IID as partial ipv6 address (ETS)", "Displays the iid as integer", true); + m_menuDisplay->Check(CHECK_IID_DISPLAY, true); + // Option menu + m_menuOptions = new wxMenu; + m_menuOptions->Append(CHECK_SLEEPY, "Act as Sleepy Device", "Sleeps for 20 seconds", true); + m_menuOptions->Check(CHECK_SLEEPY, false); + // help menu + wxMenu *menuHelp = new wxMenu; + menuHelp->Append(wxID_ABOUT); + // full menu bar + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append(m_menuFile, "&File"); + menuBar->Append(m_menuDisplay, "&Display"); + menuBar->Append(m_menuOptions, "&Options"); + menuBar->Append(menuHelp, "&Help"); + SetMenuBar( menuBar ); + CreateStatusBar(); + SetStatusText("Welcome to KNX Battleships Demo!"); + Bind(wxEVT_MENU, &MyFrame::OnReset, this, RESET); + Bind(wxEVT_MENU, &MyFrame::OnClearTables, this, RESET_TABLE); + Bind(wxEVT_MENU, &MyFrame::OnGroupObjectTable, this, GOT_TABLE_ID); + Bind(wxEVT_MENU, &MyFrame::OnPublisherTable, this, PUB_TABLE_ID); + Bind(wxEVT_MENU, &MyFrame::OnRecipientTable, this, REC_TABLE_ID); + Bind(wxEVT_MENU, &MyFrame::OnParameterList, this, PARAMETER_LIST_ID); + Bind(wxEVT_MENU, &MyFrame::OnAuthTable, this, AT_TABLE_ID); + Bind(wxEVT_MENU, &MyFrame::OnProgrammingMode, this, CHECK_PM); + Bind(wxEVT_MENU, &MyFrame::OnSleepyMode, this, CHECK_SLEEPY); + Bind(wxEVT_MENU, &MyFrame::OnReset, this, RESET); + Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT); + Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); + int x_width = 350; /* width of the widgets */ + int x_height = 25; /* height of the widgets */ + int max_instances = 6; + int max_dp_count = 6; + int border_size = 1; + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + + m_scrolledwindow = new ScrolledWidgetsPane(this, wxID_ANY); + sizer->Add(m_scrolledwindow, 1, wxEXPAND); + this->SetSizer(sizer); + m_scrolledwindow->m_parentFrame = this; + + if (strlen(str_serial_number) > 1) { + app_set_serial_number(str_serial_number); + } + // initialize the KNX stack + app_initialize_stack(); + // grid for the info of the device + wxGridSizer* gridsizer = new wxGridSizer( 4, 2, border_size, border_size); + // serial number + char text[500]; + strcpy(text, "Device Serial Number: -sn "); + oc_device_info_t* device = oc_core_get_device_info(0); + strcat(text, oc_string(device->serialnumber)); + wxTextCtrl* Statictext; + Statictext = new wxTextCtrl(this, wxID_ANY, text, wxPoint(0, 0), wxSize(x_width, x_height)); + Statictext->SetEditable(false); + Statictext->SetToolTip("Serial number of this instance, can be changed on the commandline"); + gridsizer->Add(Statictext, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + + /* QR code + KNX:S:serno;P:password + where: + KNX: is a fixed prefix + S: means a KNX serial number follows, serno itself is encoded as + 12 upper-case hexadecimal characters + P: means a password follows, password itself is just + the KNX IoT Point API password; + this works as the allowed password characters do not interfere + with the separator characters colon and semicolon and are in the Alphanumeric range. + */ + char qrtext[500]; + strcpy(qrtext, "QR (ISO): 41S"); + strcat(qrtext, oc_string(device->serialnumber)); + strcat(qrtext, "+3ZPA:"); + strcat(qrtext, app_get_password()); + app_str_to_upper(qrtext); + wxTextCtrl* Statictext2; + Statictext2 = new wxTextCtrl(this, wxID_ANY, qrtext, wxPoint(0, 0), wxSize(x_width, x_height)); + Statictext2->SetEditable(false); + Statictext2->SetToolTip("QR info in ISO format, can be copied paste into ETS"); + gridsizer->Add(Statictext2, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // individual address + sprintf(text, "IA: %d", device->ia); + m_ia_text = new wxTextCtrl(this, IA_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + m_ia_text->SetEditable(false); + m_ia_text->SetToolTip("Individual Address"); + gridsizer->Add(m_ia_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM , FromDIP(border_size)); + // installation id + sprintf(text, "IID: %lld", device->iid); + m_iid_text = new wxTextCtrl(this, IID_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + m_iid_text->SetEditable(false); + m_iid_text->SetToolTip("Installation IDentifier"); + gridsizer->Add(m_iid_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // programming mode + sprintf(text, "Programming Mode: %d", device->pm); + m_pm_text = new wxTextCtrl(this, PM_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + m_pm_text->SetEditable(false); + //m_pm_text->SetToolTip("Programming Mode"); + gridsizer->Add(m_pm_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // installation id + sprintf(text, "LoadState: %s", oc_core_get_lsm_state_as_string(device->lsm_s)); + m_ls_text = new wxTextCtrl(this, LS_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + m_ls_text->SetEditable(false); + //m_ls_text->SetToolTip("LoadState, 'loaded' means in running state"); + gridsizer->Add(m_ls_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + // host name +#ifdef WIN32 + + char hostname_str[50]; + int error = gethostname(hostname_str, 50); + oc_core_set_device_hostname(0, hostname_str); +#endif + sprintf(text, "host name: %s", oc_string(device->hostname)); + m_hostname_text = new wxTextCtrl(this, LS_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + m_hostname_text->SetEditable(false); +#ifdef WIN32 +m_hostname_text->SetToolTip("Hostname of the PC"); +#else + m_hostname_text->SetToolTip("Hostname not supported"); +#endif + gridsizer->Add(m_hostname_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + if (app_is_secure()) { + strcpy(text, app_get_password()); + } + else { + strcpy(text, "unsecured"); + } + // this one should be rich text wxTE_RICH + m_secured_text = new wxTextCtrl(this, LS_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); + gridsizer->Add(m_secured_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); + m_secured_text->SetEditable(false); + m_secured_text->SetToolTip("Password if the device is secure"); + if (app_is_secure() == false) { + m_secured_text->SetStyle(0, 100, (wxTextAttr(*wxRED))); + } + // update the UI + sizer->Add(gridsizer, 0, wxEXPAND | wxALL, FromDIP(border_size)); + this->SetSizer(sizer); + // initialize the UI with values from the stack + this->updateTextButtons(); + this->updateInfoButtons(); + this->updateInfoCheckBoxes(); + // start the timer for UI updates and stack polls + m_timer.Bind(wxEVT_TIMER, &MyFrame::OnTimer, this); + m_timer.Start(1, wxTIMER_CONTINUOUS); // 1 millisecond interval +} + +/** + * @brief exit the application + * + * @param event command triggered by the framework + */ +void MyFrame::OnExit(wxCommandEvent& event) +{ + Close(true); +} + +/** + * @brief checks/unchecks the programming mode + * + * @param event command triggered by the menu button + */ +void MyFrame::OnProgrammingMode(wxCommandEvent& event) +{ + int device_index = 0; + SetStatusText("Changing programming mode"); + + bool my_val = m_menuFile->IsChecked(CHECK_PM); + oc_device_info_t* device = oc_core_get_device_info(0); + device->pm = my_val; + + // update the UI + this->updateTextButtons(); + // update mdns + knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); +} + + +/** + * @brief checks/unchecks the sleepy mode + * + * @param event command triggered by the menu button + */ +void MyFrame::OnSleepyMode(wxCommandEvent& event) +{ + int device_index = 0; + SetStatusText("Changing sleepy mode"); + + bool my_sleepy = m_menuOptions->IsChecked(CHECK_SLEEPY); + oc_device_info_t* device = oc_core_get_device_info(0); + + if (my_sleepy) { + knx_service_sleep_period(20); + } else { + knx_service_sleep_period(0); + } + // update mdns + knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); +} + +/** + * @brief update the text buttons + * - IA + * - Loadstate + * - programming mode + * - IID + * - Hostname + */ +void MyFrame::updateTextButtons() +{ + + char text[500]; + size_t device_index = 0; + bool iid_conversion = m_menuDisplay->IsChecked(CHECK_IID_DISPLAY); + + // get the device data structure + oc_device_info_t* device = oc_core_get_device_info(device_index); + // update the text labels + // ia_0 == AAxxxxxx = AA + // ia_1 == xxAAxxxx = AA + // ia_2 == xxxxAAAA = AAAA + uint32_t ia = device->ia; + uint32_t ia_o = (ia >> 12); + uint32_t ia_1 = (ia >> 8) & 0xF; + uint32_t ia_2 = (ia & 0x000000FF); + sprintf(text, "IA: %d.%d.%d [%d]", ia_o, ia_1, ia_2, device->ia); + m_ia_text->SetLabelText(text); + sprintf(text, "LoadState: %s", oc_core_get_lsm_state_as_string(device->lsm_s)); + m_pm_text->SetLabelText(text); + sprintf(text, "Programming Mode : % d", device->pm); + m_ls_text->SetLabelText(text); + strcpy(text, "IID: "); + this->int2grpidtext(device->iid, text, iid_conversion); + m_iid_text->SetLabelText(text); + sprintf(text, "host name: %s", oc_string(device->hostname)); + m_hostname_text->SetLabelText(text); + // reset the programming mode to what the device has + m_menuFile->Check(CHECK_PM, device->pm); +} + +/** + * @brief clear the tables of the device + * + * @param event command triggered by button in the menu + */ +void MyFrame::OnClearTables(wxCommandEvent& event) +{ + int device_index = 0; + SetStatusText("Clear Tables"); + // reset the device + oc_knx_device_storage_reset(device_index, 7); + // update the UI + this->updateTextButtons(); +} + +/** + * @brief reset the device + * + * @param event command triggered by button in the menu + */ +void MyFrame::OnReset(wxCommandEvent& event) +{ + int device_index = 0; + SetStatusText("Device Reset"); + // reset the device + oc_knx_device_storage_reset(device_index, 2); + // update the UI + this->updateTextButtons(); +} + +/** + * @brief shows the group object table in a window + * + * @param event command triggered by a menu button + */ +void MyFrame::OnGroupObjectTable(wxCommandEvent& event) +{ + int device_index = 0; + char text[1024 * 5]; + char line[200]; + char windowtext[200]; + bool ga_conversion = m_menuDisplay->IsChecked(CHECK_GA_DISPLAY); + + strcpy(text, ""); + oc_device_info_t* device = oc_core_get_device_info(device_index); + if (device == NULL) { + return; + } + int total = oc_core_get_group_object_table_total_size(); + for (int index = 0; index < total; index++) { + oc_group_object_table_t* entry = oc_core_get_group_object_table_entry(index); + + if (entry && entry->ga_len > 0) { + sprintf(line, "Index %d \n", index); + strcat(text, line); + sprintf(line, " id: '%d' ", entry->id); + strcat(text, line); + sprintf(line, " url: '%s' ", oc_string(entry->href)); + strcat(text, line); + sprintf(line, " cflags : '%d' ", (int)entry->cflags); + oc_cflags_as_string(line, entry->cflags); + strcat(text, line); + strcpy(line," ga : ["); + for (int i = 0; i < entry->ga_len; i++) { + this->int2gatext(entry->ga[i], line, ga_conversion); + } + strcat(line," ]\n"); + strcat(text, line); + } + } + strcpy(windowtext, "Group Object Table "); + strcat(windowtext, oc_string(device->serialnumber)); + CustomDialog(windowtext, text); + SetStatusText("List Group Object Table"); +} + +/** + * @brief shows the Publisher table in a window + * + * @param event command triggered by a menu button + */ +void MyFrame::OnPublisherTable(wxCommandEvent& event) +{ + int device_index = 0; + char text[1024 * 5]; + char line[200]; + char windowtext[200]; + bool ga_conversion = m_menuDisplay->IsChecked(CHECK_GA_DISPLAY); + bool grpid_conversion = m_menuDisplay->IsChecked(CHECK_GRPID_DISPLAY); + bool iid_conversion = m_menuDisplay->IsChecked(CHECK_IID_DISPLAY); + + strcpy(text, ""); + oc_device_info_t* device = oc_core_get_device_info(device_index); + if (device == NULL) { + return; + } + + int total = oc_core_get_publisher_table_size(); + for (int index = 0; index < total; index++) { + oc_group_rp_table_t* entry = oc_core_get_publisher_table_entry(index); + + if (entry && entry->id >= 0) { + sprintf(line, "Index %d \n", index); + strcat(text, line); + sprintf(line, " id: '%d' ", entry->id); + strcat(text, line); + if ( entry->ia >= 0) { + sprintf(line, " ia: '%d' ", entry->ia); + strcat(text, line); + } + if ( entry->iid >= 0) { + strcpy(line, " iid: "); + this->int2grpidtext(entry->iid, line, iid_conversion); + strcat(text, line); + } + if ( entry->fid >= 0) { + sprintf(line, " fid: '%lld' ", entry->fid); + strcat(text, line); + } + if ( entry->grpid > 0) { + //sprintf(line, " grpid: '%u' ", entry->grpid); + strcpy(line, " grpid: "); + this->int2grpidtext(entry->grpid, line, grpid_conversion); + strcat(text, line); + } + if (oc_string_len(entry->url) > 0) { + sprintf(line, " url: '%s' ", oc_string(entry->url)); + strcat(text, line); + } + if (oc_string_len(entry->path) > 0) { + sprintf(line, " path: '%s' ", oc_string(entry->path)); + strcat(text, line); + } + if (oc_string_len(entry->at) > 0){ + sprintf(line, " at: '%s' ", oc_string(entry->at)); + strcat(text, line); + } + if ( entry->ga_len > 0) { + strcpy(line," ga : ["); + for (int i = 0; i < entry->ga_len; i++) { + this->int2gatext(entry->ga[i], line, ga_conversion); + } + strcat(line," ]\n"); + strcat(text, line); + } + } + } + strcpy(windowtext, "Publisher Table "); + strcat(windowtext, oc_string(device->serialnumber)); + CustomDialog(windowtext, text); + SetStatusText("List Publisher Table"); +} + +/** + * @brief shows the Recipient table in a window + * + * @param event command triggered by a menu button + */ +void MyFrame::OnRecipientTable(wxCommandEvent& event) +{ + int device_index = 0; + char text[1024 * 5]; + char line[200]; + char windowtext[200]; + bool ga_conversion = m_menuDisplay->IsChecked(CHECK_GA_DISPLAY); + bool grpid_conversion = m_menuDisplay->IsChecked(CHECK_GRPID_DISPLAY); + bool iid_conversion = m_menuDisplay->IsChecked(CHECK_IID_DISPLAY); + + strcpy(text, ""); + oc_device_info_t* device = oc_core_get_device_info(device_index); + if (device == NULL) { + return; + } + + int total = oc_core_get_recipient_table_size(); + for (int index = 0; index < total; index++) { + oc_group_rp_table_t* entry = oc_core_get_recipient_table_entry(index); + + if (entry && entry->id >= 0) { + sprintf(line, "Index %d \n", index); + strcat(text, line); + sprintf(line, " id: '%d' ", entry->id); + strcat(text, line); + if ( entry->ia >= 0) { + sprintf(line, " ia: '%d' ", entry->ia); + strcat(text, line); + } + if ( entry->iid >= 0) { + strcpy(line, " iid: "); + this->int2grpidtext(entry->iid, line, iid_conversion); + strcat(text, line); + } + if ( entry->fid >= 0) { + sprintf(line, " fid: '%lld' ", entry->fid); + strcat(text, line); + } + if ( entry->grpid >= 0) { + strcpy(line, " grpid: "); + this->int2grpidtext(entry->grpid, line, grpid_conversion); + strcat(text, line); + } + if (oc_string_len(entry->url) > 0) { + sprintf(line, " url: '%s' ", oc_string(entry->url)); + strcat(text, line); + } + if (oc_string_len(entry->path) > 0){ + sprintf(line, " path: '%s' ", oc_string(entry->path)); + strcat(text, line); + } + if (oc_string_len(entry->at) > 0){ + sprintf(line, " at: '%s' ", oc_string(entry->at)); + strcat(text, line); + } + if ( entry->ga_len > 0) { + strcpy(line," ga : ["); + for (int i = 0; i < entry->ga_len; i++) { + this->int2gatext(entry->ga[i], line, ga_conversion); + } + strcat(line," ]\n"); + strcat(text, line); + } + sprintf(line, " non: %d mt: %d\n", entry->non, entry->mt); + strcat(text, line); + } + } + strcpy(windowtext, "Recipient Table "); + strcat(windowtext, oc_string(device->serialnumber)); + CustomDialog(windowtext, text); + SetStatusText("List Recipient Table"); +} +/** + * @brief shows a window containing the parameters and current values of the application + * + * @param event command triggered by a menu button + */ +void MyFrame::OnParameterList(wxCommandEvent& event) +{ + int device_index = 0; + char text[1024 + (200*1)]; + char line[200]; + char windowtext[200]; + + strcpy(text, ""); + + oc_device_info_t* device = oc_core_get_device_info(device_index); + if (device == NULL) { + return; + } + strcpy(windowtext, "Parameter List "); + strcat(windowtext, oc_string(device->serialnumber)); + + // start looping at 0. + int index = 0; + const char* url = app_get_parameter_url(index); + if (url == NULL) { + strcat(text, "no parameters in this device"); + } + bool added = false; + + while (url) { + sprintf(line, "\nIndex %02d ", index); + strcat(text, line); + sprintf(line, " url : '%s' ", url); + strcat(text, line); + const char* name = app_get_parameter_name(index); + if (name) { + sprintf(line, " name: '%s' ", app_get_parameter_name(index)); + strcat(text, line); + } + added = false; + if (app_is_DPT_Param_Bool_url(url)) { + DPT_Param_Bool param_value; + memset(¶m_value, 0, sizeof(param_value)); + app_get_DPT_Param_Bool_variable(url, ¶m_value); + added = true; + + // Enumeration + sprintf(line, " value : '%d' ", param_value); + strcat(text, line); + } + if (app_is_DPT_Shot_Status_url(url)) { + DPT_Shot_Status param_value; + memset(¶m_value, 0, sizeof(param_value)); + app_get_DPT_Shot_Status_variable(url, ¶m_value); + added = true; + + + // param_value.DPST_60004_1_F_1 of bool type + sprintf(line, " %s : '%d' ", "DPST-60004-1_F-1", param_value.DPST_60004_1_F_1); + strcat(text, line); + if (!param_value.DPST_60004_1_F_1) { + sprintf(line, " (%s) ", "Miss"); + strcat(text, line); + } + if (param_value.DPST_60004_1_F_1) { + sprintf(line, " (%s) ", "Hit"); + strcat(text, line); + } + + // param_value.DPST_60004_1_F_2 of bool type + sprintf(line, " %s : '%d' ", "DPST-60004-1_F-2", param_value.DPST_60004_1_F_2); + strcat(text, line); + if (!param_value.DPST_60004_1_F_2) { + sprintf(line, " (%s) ", "Floating"); + strcat(text, line); + } + if (param_value.DPST_60004_1_F_2) { + sprintf(line, " (%s) ", "Sunk"); + strcat(text, line); + } + strcat(text, "\n"); + + // param_value.DPST_60004_1_F_3 of Enumeration type + } + if (app_is_DPT_Start_url(url)) { + DPT_Start param_value; + memset(¶m_value, 0, sizeof(param_value)); + app_get_DPT_Start_variable(url, ¶m_value); + added = true; + + // bool + sprintf(line, " value : '%d' ", param_value); + strcat(text, line); + if (!param_value) { + sprintf(line, " (%s) ", "Stop"); + strcat(text, line); + } + if (param_value) { + sprintf(line, " (%s) ", "Start"); + strcat(text, line); + } + } + if (app_is_DPT_Uint_XY_url(url)) { + DPT_Uint_XY param_value; + memset(¶m_value, 0, sizeof(param_value)); + app_get_DPT_Uint_XY_variable(url, ¶m_value); + added = true; + + // param_value.DPST_60009_1_F_1 of unsigned int type + sprintf(line, " %s : '%d' ", "DPST-60009-1_F-1", param_value.DPST_60009_1_F_1); + strcat(text, line); + strcat(text, "\n"); + + // param_value.DPST_60009_1_F_2 of unsigned int type + sprintf(line, " %s : '%d' ", "DPST-60009-1_F-2", param_value.DPST_60009_1_F_2); + strcat(text, line); + } + + if (added == false) { + // type is boolean + sprintf(line, " (%d) ", app_retrieve_bool_variable(url)); + strcat(text, line); + { + // type is int + int value; + if (app_retrieve_int_variable(url, &value)) { + sprintf(line, " (%d) ", value); + strcat(text, line); + } + } + } + + //if (app_is_string_url(url)) { + // sprintf(line, " value : '%s' ", app_retrieve_string_variable(url)); + // strcat(text, line); + //} + index++; + url = app_get_parameter_url(index); + } + + //wxMessageBox(text, windowtext, + // wxOK | wxICON_NONE); + CustomDialog(windowtext, text); + SetStatusText("List Parameters and their current set values"); +} + +/** + * @brief shows the (loaded) auth/at table + * + * @param event command triggered by a menu button + */ +void MyFrame::OnAuthTable(wxCommandEvent& event) +{ + int device_index = 0; + char text[1024 * 10]; + char line[500]; + bool ga_conversion = m_menuDisplay->IsChecked(CHECK_GA_DISPLAY); + char windowtext[200]; + int max_entries = oc_core_get_at_table_size(); + int index = 1; + + oc_device_info_t* device = oc_core_get_device_info(device_index); + if (device == NULL) { + return; + } + + strcpy(text, ""); + for (index = 0; index < max_entries; index++) { + + oc_auth_at_t* my_entry = oc_get_auth_at_entry(device_index, index); + if (my_entry != NULL) { + if (oc_string_len(my_entry->id)) { + sprintf(line, "index : '%d' id = '%s' \n", index, oc_string(my_entry->id)); + strcat(text, line); + sprintf(line, " profile : %d (%s)\n", my_entry->profile, + oc_at_profile_to_string(my_entry->profile)); + strcat(text, line); + if (my_entry->profile == OC_PROFILE_COAP_DTLS) { + if (oc_string_len(my_entry->sub) > 0) { + sprintf(line, " sub : %s\n", oc_string(my_entry->sub)); + strcat(text, line); + } + if (oc_string_len(my_entry->kid) > 0) { + sprintf(line, " kid : %s\n", oc_string(my_entry->kid)); + strcat(text, line); + } + } + if (my_entry->profile == OC_PROFILE_COAP_OSCORE ||my_entry->profile == OC_PROFILE_COAP_PASE) { + if (oc_byte_string_len(my_entry->osc_id) > 0) { + sprintf(line, " osc_id [%d]: ", (int)oc_byte_string_len(my_entry->osc_id)); + strcat(text, line); + char* ms = oc_string(my_entry->osc_id); + int length = (int)oc_byte_string_len(my_entry->osc_id); + for (int i = 0; i < length; i++) { + sprintf(line, "%02x", (unsigned char)ms[i]); + strcat(text, line); + } + sprintf(line, "\n"); + strcat(text, line); + } + if (oc_byte_string_len(my_entry->osc_ms) > 0) { + sprintf(line, " osc_ms [%d]: ",(int)oc_byte_string_len(my_entry->osc_ms)); + strcat(text, line); + int length = (int)oc_byte_string_len(my_entry->osc_ms); + char* ms = oc_string(my_entry->osc_ms); + for (int i = 0; i < length; i++) { + sprintf(line, "%02x", (unsigned char)ms[i]); + strcat(text, line); + } + sprintf(line, "\n"); + strcat(text, line); + } + if (oc_byte_string_len(my_entry->osc_salt) > 0) { + sprintf(line, " osc_salt [%d]: ",(int)oc_byte_string_len(my_entry->osc_salt)); + strcat(text, line); + int length = (int)oc_byte_string_len(my_entry->osc_salt); + char* salt = oc_string(my_entry->osc_salt); + for (int i = 0; i < length; i++) { + sprintf(line, "%02x", (unsigned char)salt[i]); + strcat(text, line); + } + sprintf(line, "\n"); + strcat(text, line); + } + if (oc_byte_string_len(my_entry->osc_contextid) > 0) { + sprintf(line, " osc_contextid (o)[%d]: ", (int)oc_byte_string_len(my_entry->osc_contextid)); + strcat(text, line); + char* ms = oc_string(my_entry->osc_contextid); + int length = (int)oc_byte_string_len(my_entry->osc_contextid); + for (int i = 0; i < length; i++) { + sprintf(line, "%02x", (unsigned char)ms[i]); + strcat(text, line); + } + sprintf(line, "\n"); + strcat(text, line); + } + if (oc_string_len(my_entry->sub) > 0) { + sprintf(line, " sub : %s\n", oc_string(my_entry->sub)); + strcat(text, line); + } + if (my_entry->ga_len > 0) { + sprintf(line, " osc_ga : ["); + strcat(text, line); + for (int i = 0; i < my_entry->ga_len; i++) { + this->int2gatext(my_entry->ga[i], text, ga_conversion); + } + sprintf(line, " ]\n"); + strcat(text, line); + } else { + sprintf(line, " scope : "); + this->int2scopetext(my_entry->scope, line); + strcat(text, line); + strcat(text, "\n"); + } + } + } + } + } + + strcpy(windowtext, "Auth AT Table "); + strcat(windowtext, oc_string(device->serialnumber)); + CustomDialog(windowtext, text); + SetStatusText("List security entries"); +} + +/** + * @brief shows static info about the application + * + * @param event command triggered by a menu button + */ +void MyFrame::OnAbout(wxCommandEvent& event) +{ + char text[500 + (200* 6)]; + strcpy(text, "KNX Battleships Demo\n"); + strcat(text, "\nDevice Serial Number: "); + oc_device_info_t* device = oc_core_get_device_info(0); + strcat(text, oc_string(device->serialnumber)); + strcat(text,"\n"); + strcat(text,"manufacturer : cascoda\n"); + strcat(text,"model : KNX Battleships Demo eink\n"); + strcat(text,"hardware type : 000000000000\n"); + strcat(text,"hardware version : [0, 4, 0]\n"); + strcat(text,"firmware version : [0, 4, 0]\n\n"); + + strcat(text, "data points:\n"); + strcat(text,"url:/p/o_1_1 rt:urn:knx:dpa.65500.101 if:if.o inst:1 name:SendShot\n"); + strcat(text,"url:/p/o_1_2 rt:urn:knx:dpa.65501.111 if:if.i inst:1 name:ReceiveShot\n"); + strcat(text,"url:/p/o_1_3 rt:urn:knx:dpa.65501.102 if:if.o inst:1 name:SendShotStatus\n"); + strcat(text,"url:/p/o_1_4 rt:urn:knx:dpa.65500.112 if:if.i inst:1 name:ReceiveShotStatus\n"); + strcat(text,"url:/p/o_1_5 rt:urn:knx:dpa.65500.103 if:if.o inst:1 name:SendReady\n"); + strcat(text,"url:/p/o_1_6 rt:urn:knx:dpa.65501.113 if:if.i inst:1 name:ReceiveReady\n"); + strcat(text, "\n"); + + strcat(text, "(c) Cascoda Ltd\n"); + strcat(text, "2024-06-17 16:19:21.624451"); + CustomDialog("About", text); +} + +/** + * @brief update the UI on the timer ticks + * updates: + * - check boxes + * - info buttons + * - text buttons + * does a oc_main_poll to give a tick to the stack + * takes into account if the device is sleepy + * e.g. then it only does an poll each 20 seconds + * @param event triggered by a timer + */ +void MyFrame::OnTimer(wxTimerEvent& event) +{ + bool do_poll = true; + bool sleepy = m_menuOptions->IsChecked(CHECK_SLEEPY); + // do whatever you want to do every millisecond here + if (sleepy) + { + do_poll = false; + m_sleep_counter++; + if ((m_sleep_counter / 1000) > m_sleep_seconds) { + // only do a poll each x (20) seconds + do_poll = true; + m_sleep_counter = 0; + } + if (oc_knx_device_in_programming_mode(0)) { + // make sure that the device is reactive in programming mode + // e.g. keep on polling + do_poll = true; + } + } + + if (do_poll) { + oc_clock_time_t next_event; + next_event = oc_main_poll(); + } + this->updateInfoCheckBoxes(); + this->updateInfoButtons(); + this->updateTextButtons(); +} + + +/** + * @brief update the UI e.g. check boxes in the UI + * updates: + * does a oc_main_poll to give a tick to the stack + * + * @param event triggered by a timer + */ +void MyFrame::updateInfoCheckBoxes() +{ + bool p; + p = *app_get_DPT_Start_variable("/p/o_1_6", NULL); // set toggle of ReceiveReady + m_scrolledwindow->mRECEIVEREADY->SetValue(p); + +} + +/** + * @brief convert the boolean to text for display + * + * @param on_off the boolean + * @param text the text to add the boolean as text + */ +void MyFrame::bool2text(bool on_off, char* text) +{ + if (on_off) { + strcat(text, " On"); + } + else { + strcat(text, " Off"); + } +} + +/** + * @brief convert the integer to text for display + * + * @param value the integer + * @param text the text to add info to + */ +void MyFrame::int2text(int value, char* text) +{ + char value_text[50]; + + sprintf(value_text, " %d", value); + strcat(text, value_text); +} + +/** + * @brief convert the group address to text for display + * + * @param value the integer + * @param text the text to add info to + * @param as_ets the text as terminology as used in ets + */ +void MyFrame::int2gatext(uint32_t value, char* text, bool as_ets) +{ + char value_text[50]; + + if (as_ets) { + /* + The so called Group Address structure correlates with its representation style in ETS, + see also the relevant ETS Professional article. + The information about the ETS Group Address representation style itself is NOT included in the Group Address. + '3-level' = main/middle/sub + main = D7+D6+D5+D4+D3 of the first octet (high address) + middle = D2+D1+D0 of the first octet (high address) + sub = the entire second octet (low address) + ranges: main = 0..31, middle = 0..7, sub = 0..255 + */ + uint32_t ga = value; + uint32_t ga_main = (ga >> 11); + uint32_t ga_middle = (ga >> 8) & 0x7; + uint32_t ga_sub = (ga & 0x000000FF); + sprintf(value_text, " %lu/%lu/%lu", ga_main, ga_middle, ga_sub); + strcat(text, value_text); + } else { + sprintf(value_text, " %lu", value); + strcat(text, value_text); + } +} + +/** + * @brief convert the scope to text for display + * + * @param value the scope + * @param text the text to add info too + */ +void MyFrame::int2scopetext(uint32_t value, char* text) +{ + char value_text[150]; + + sprintf(value_text, " [%d]", value); + strcat(text, value_text); + // should be the same as + if (value & (1 << 1)) strcat(text, " if.i"); + if (value & (1 << 2)) strcat(text, " if.o"); + if (value & (1 << 3)) strcat(text, " if.g.s"); + if (value & (1 << 4)) strcat(text, " if.c"); + if (value & (1 << 5)) strcat(text, " if.p"); + if (value & (1 << 6)) strcat(text, " if.d"); + if (value & (1 << 7)) strcat(text, " if.a"); + if (value & (1 << 8)) strcat(text, " if.s"); + if (value & (1 << 9)) strcat(text, " if.ll"); + if (value & (1 << 10)) strcat(text, " if.b"); + if (value & (1 << 11)) strcat(text, " if.sec"); + if (value & (1 << 12)) strcat(text, " if.swu"); + if (value & (1 << 13)) strcat(text, " if.pm"); + if (value & (1 << 14)) strcat(text, " if.m"); +} + +/** + * @brief convert the group id to text for display + * + * @param value the group id + * @param text the text to add info too + * @param as_ets the text as terminology as used in ets + */ +void MyFrame::int2grpidtext(uint64_t value, char* text, bool as_ets) +{ + char value_text[50]; + + if (as_ets) { + /* + create the multicast address from group and scope + FF3_:FD__:____:____:(8-f)___:____ + FF35:30::: + | 5 == scope + | 3 == scope + Multicast prefix: FF35:0030: [4 bytes] + ULA routing prefix: FD11:2222:3333:: [6 bytes + 2 empty bytes] + Group Identifier: 8000 : 0068 [4 bytes ] + */ + // group number to the various bytes + uint8_t byte_1 = (uint8_t)value; + uint8_t byte_2 = (uint8_t)(value >> 8); + uint8_t byte_3 = (uint8_t)(value >> 16); + uint8_t byte_4 = (uint8_t)(value >> 24); + uint8_t byte_5 = (uint8_t)(value >> 32); + + if (byte_5 == 0) { + sprintf(value_text, " %02x%02x:%02x%02x", byte_4, byte_3, byte_2, byte_1); + } + else { + sprintf(value_text, " %02x:%02x%02x:%02x%02x", byte_5, byte_4, byte_3, byte_2, byte_1); + } + + strcat(text, value_text); + } + else { + //sprintf(text, "IID: %lld", device->iid); + sprintf(value_text, " %" PRIu64 "\n", value); + strcat(text, value_text); + } +} + +/** + * @brief convert the double (e.g. float) to text for display + * + * @param value the value + * @param text the text to add info too + */ +void MyFrame::double2text(double value, char* text) +{ + char new_text[200]; + sprintf(new_text," %f", value); + strcat(text, new_text); +} + +/** + * @brief update the buttons + * + */ +void MyFrame::updateInfoButtons() +{ + char text[200]; + bool p; + int p_int; + float f; + double d; + // name=ReceiveShot dpt=urn:knx:dpt.uint_XY if=if.i ctype= + { + const DPT_Uint_XY* d = (const DPT_Uint_XY *)app_get_DPT_Uint_XY_variable(URL_RECEIVESHOT, NULL); + // set text of ReceiveShot + strcpy(text, ""); + app_sprintf_DPT_Uint_XY(d, text, 200); + m_scrolledwindow->mRECEIVESHOT->SetLabel(text); + } + // name=ReceiveShotStatus dpt=urn:knx:dpt.shot_Status if=if.i ctype= + { + const DPT_Shot_Status* d = (const DPT_Shot_Status *)app_get_DPT_Shot_Status_variable(URL_RECEIVESHOTSTATUS, NULL); + // set text of ReceiveShotStatus + strcpy(text, ""); + app_sprintf_DPT_Shot_Status(d, text, 200); + m_scrolledwindow->mRECEIVESHOTSTATUS->SetLabel(text); + } + // name=ReceiveReady dpt=urn:knx:dpt.start if=if.i ctype=bool + p = (bool)*app_get_DPT_Start_variable(URL_RECEIVEREADY, NULL); + // set text of ReceiveReady + strcpy(text, ""); + this->bool2text(p, text); + m_scrolledwindow->mRECEIVEREADY->SetLabel(text); + +} + + + + + diff --git a/BATTLESHIP/knx_eink_battleships_logic.c b/BATTLESHIP/knx_eink_battleships_logic.c index 45844c2..c1d345a 100644 --- a/BATTLESHIP/knx_eink_battleships_logic.c +++ b/BATTLESHIP/knx_eink_battleships_logic.c @@ -37,32 +37,44 @@ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ +#include #include "knx_eink_battleships.h" #include "cascoda-util/cascoda_tasklet.h" #include "oc_core_res.h" #include "api/oc_knx_dev.h" #include "api/oc_knx_sec.h" #include "api/oc_knx_fp.h" +#include "port/dns-sd.h" #include "oc_knx.h" +#include "port/dns-sd.h" +#include "openthread/thread.h" +#include "openthread/ping_sender.h" +#include "platform.h" +#include "manufacturer_storage.h" /////////////////////////////////////////////////////////////////////////////// // EINK code // /////////////////////////////////////////////////////////////////////////////// - +#include "sif_ssd1681.h" // 1.5 inch display #include "gfx_library.h" #include "gfx_driver.h" #include "knx_iot_image_1_54.h" // splash screen -#include "sif_ssd1681.h" - enum Screen g_screen_nr; -uint32_t g_qr; -bool g_eink_clean_redraw; -ca_tasklet screen_Tasklet; +bool g_eink_clean_redraw = true; +uint32_t g_eink_draws_without_refresh = 0; +const uint32_t g_eink_draws_cutoff = 12; +enum BattDisplaySymbol g_batt_display_symbol = BATT_DISPLAY_ICON; +ca_tasklet screen_Tasklet; +#ifdef TRANSLATE_PRESENT +#define T translate +extern char *translate(const char *s); +#else +#define T(x) x +#endif #define SCHEDULE_NOW 0 - #include "cascoda-util/cascoda_tasklet.h" #include "cascoda-util/cascoda_time.h" @@ -237,8 +249,8 @@ static void game_fire_shot() if(g_game.currentPlayerNo != g_game.myPlayerNo) return; DPT_Uint_XY shot_pos; - shot_pos._X = g_game.tx; - shot_pos._Y = g_game.ty; + shot_pos.X = g_game.tx; + shot_pos.Y = g_game.ty; app_set_DPT_Uint_XY_variable(URL_SENDSHOT, &shot_pos); oc_do_s_mode_with_scope(5, URL_SENDSHOT, "w"); } @@ -637,7 +649,7 @@ static bool load_game_intro() */ static void game_start() { - g_clean_redraw = true; + g_eink_clean_redraw = true; set_screen(PLACE_SHIPS); } @@ -663,12 +675,12 @@ static bool load_shot_info() DPT_Shot_Status *status = app_get_DPT_Shot_Status_variable(URL_RECEIVESHOTSTATUS, NULL); display_setCursor(0, 10); - if (!status->_F1 /*hit*/) { + if (!status->F_1 /*hit*/) { display_puts("MISS"); } else { - display_puts(status->_F2?"SUNK":"HIT"); + display_puts(status->F_2?"SUNK":"HIT"); display_setCursor(0, 20); - display_puts(ship_names[status->_F3-1]); + display_puts(ship_names[status->F_3-1]); } display_setCursor(0, 40); display_puts("Press 4"); @@ -806,10 +818,6 @@ void app_initialize(void) game_setup(); } -static void init_globals() -{ - // Empty for now -} /** * @brief Called when ShotStatus url is PUT @@ -831,25 +839,25 @@ void onReceivedShotStatus(const char *url) DPT_Shot_Status *status = app_get_DPT_Shot_Status_variable(URL_RECEIVESHOTSTATUS, NULL); DPT_Uint_XY *shot = app_get_DPT_Uint_XY_variable(URL_SENDSHOT, NULL); struct battleships_board *board = &g_game.boards[player]; - struct battleships_cell *cell = &(board->cells[shot->_X][shot->_Y]); - struct battleships_ship *ship = &g_game.ships[player][status->_F3-1]; + struct battleships_cell *cell = &(board->cells[shot->X][shot->Y]); + struct battleships_ship *ship = &g_game.ships[player][status->F_3-1]; int ship_idx = get_occupy_idx_from_ship(ship); - if (!status->_F1 /*hit*/) { + if (!status->F_1 /*hit*/) { cell->hitstate = HS_MISS; }else{ cell->hitstate = HS_HIT; cell->occupy_index = ship_idx; - if (status->_F2 /*sunk*/) { + if (status->F_2 /*sunk*/) { ship->sunk = true; - ship->length = ship_lens[5-status->_F3]; + ship->length = ship_lens[5-status->F_3]; //locate the ship // it could be horizontal or vertical // we could be at the edge of it // if theres match at x-1, y then horiz int px; int py; - px = shot->_X; - py = shot->_Y; + px = shot->X; + py = shot->Y; // check up/down if ( (py > 0 && board->cells[px][py-1].occupy_index == ship_idx) || (py < 9 && board->cells[px][py+1].occupy_index == ship_idx)) { @@ -884,7 +892,7 @@ void onReceivedShotStatus(const char *url) if (i != 5) { //redraw - g_clean_redraw = false; + g_eink_clean_redraw = false; set_screen(SHOT_INFO); g_game.currentPlayerNo = player; @@ -912,24 +920,24 @@ void onReceivedShot(const char *url) // place shot on our board enum PlayerNo player = swapPlayers(g_game.currentPlayerNo); struct battleships_board *board = &g_game.boards[player]; - struct battleships_cell *cell = &(board->cells[shot->_X][shot->_Y]); + struct battleships_cell *cell = &(board->cells[shot->X][shot->Y]); struct battleships_ship *ship = get_occupy_from_idx(cell->occupy_index); struct battleships_ship *ships = g_game.ships[player]; //it's now our turn! DPT_Shot_Status *status = app_get_DPT_Shot_Status_variable(URL_SENDSHOTSTATUS, NULL); - status->_F1 = false; - status->_F2 = false; - status->_F3 = _ShipType_NoHit; + status->F_1 = false; + status->F_2 = false; + status->F_3 = ShipTypeNo_Hit; if(cell->hitstate == HS_HIT) return; if (ship) { cell->hitstate = HS_HIT; - status->_F1 = true; - status->_F3 = 5-(ship-ships); + status->F_1 = true; + status->F_3 = 5-(ship-ships); int i; for(i = 0; i < ship->length; i++){ uint8_t sx = ship->px + i*((ship->orientation==HORIZONTAL)?1:0); @@ -938,7 +946,7 @@ void onReceivedShot(const char *url) break; } if (i == ship->length){ - status->_F2 = true; /*sunk*/ + status->F_2 = true; /*sunk*/ ship->sunk = true; } }else{ @@ -956,12 +964,12 @@ void onReceivedShot(const char *url) } if (i != 5) { //redraw - g_clean_redraw = false; + g_eink_clean_redraw = false; eink_load_screen(FIRE_SHOT); g_game.currentPlayerNo = player; - g_clean_redraw = false; + g_eink_clean_redraw = false; TASKLET_ScheduleDelta(&screen_Tasklet, 2*1000, NULL); } else { set_screen(GAME_OVER); @@ -1000,6 +1008,8 @@ void onReceivedReady(const char *url) } +uint8_t user_interaction_occurred = 0; + /////////////////////////////////////////////////////////////////////////////// // BUTTON Functions // @@ -1009,13 +1019,11 @@ void onReceivedReady(const char *url) // EINK code // /////////////////////////////////////////////////////////////////////////////// -bool g_clean_redraw = true; - -struct qr_code_t *g_qr_codes = NULL; -struct reset_t *g_resets = NULL; int g_max_y_coord; int g_scroll_position; +extern otInstance *OT_INSTANCE; + oc_auth_at_t *get_auth_at_entry(int idx) { return oc_get_auth_at_entry(0, idx); @@ -1041,7 +1049,7 @@ static uint16_t scroll_adjust_coord(uint16_t num) // automatically adjust the coordinates of the string to display it // at the correct location on the screen based on the current scrolling // position. -static void scroll_display_wrapper(uint16_t x, uint16_t y, char *str) +static void scroll_display_wrapper(uint16_t x, uint16_t y, const char *str) { // Keep track of lowest line that will be displayed if (y > g_max_y_coord) @@ -1075,17 +1083,65 @@ static bool is_scrollable_screen(void) // all calls to scroll_display_wrapper() static void show_scroll_bar(void) { -// Put all the stuff below in a function -#define SCALING_FACTOR 2 -#define BOTTOM_OFFSET 3 //bottom-most y coordinate above the bottom border -#define TOP_OFFSET 13 //top-most y coordinate under the lower portion of the title border - - int lower_y_coord = display_getHeight() - BOTTOM_OFFSET - SCALING_FACTOR * calculate_lines_to_scroll();//- SCALING_FACTOR * calculate_lines_to_scroll(); - display_drawLine(1, TOP_OFFSET + SCALING_FACTOR * g_scroll_position, 1, lower_y_coord + SCALING_FACTOR * g_scroll_position, BLACK); - -#undef SCALING_FACTOR -#undef BOTTOM_OFFSET -#undef TOP_OFFSET +#define SCALING_FACTOR 4 +#define BOTTOM_OF_HEADER 14 +#define TRIANGLE_HEIGHT 2 +#define TRIANGLE_BASE 4 +#define SCROLL_BAR_TOP_OFFSET (BOTTOM_OF_HEADER + TRIANGLE_HEIGHT + 1) //top-most y coordinate under the lower portion of the title border + +#define TOP_TRIANGLE_X1 (width - 5) +#define TOP_TRIANGLE_Y1 (BOTTOM_OF_HEADER + TRIANGLE_HEIGHT) +#define TOP_TRIANGLE_X2 (TOP_TRIANGLE_X1 + TRIANGLE_BASE) +#define TOP_TRIANGLE_Y2 (TOP_TRIANGLE_Y1) +#define TOP_TRIANGLE_X3 (TOP_TRIANGLE_X1 + (TRIANGLE_BASE / 2)) +#define TOP_TRIANGLE_Y3 (BOTTOM_OF_HEADER) + +#define BOTTOM_TRIANGLE_X1 (TOP_TRIANGLE_X1) +#define BOTTOM_TRIANGLE_Y1 (height - TRIANGLE_HEIGHT - 1) +#define BOTTOM_TRIANGLE_X2 (TOP_TRIANGLE_X2) +#define BOTTOM_TRIANGLE_Y2 (BOTTOM_TRIANGLE_Y1) +#define BOTTOM_TRIANGLE_X3 (TOP_TRIANGLE_X3) +#define BOTTOM_TRIANGLE_Y3 (BOTTOM_TRIANGLE_Y1 + TRIANGLE_HEIGHT) + +#define SCROLL_BAR_BOTTOM_OFFSET (BOTTOM_TRIANGLE_Y1 - 2) + + int width = display_getWidth(); + int height = display_getHeight(); + int incremental_offset = SCALING_FACTOR * g_scroll_position; + int upper_y_coord = SCROLL_BAR_TOP_OFFSET + incremental_offset; + int lower_y_coord = SCROLL_BAR_BOTTOM_OFFSET - SCALING_FACTOR * calculate_lines_to_scroll() + incremental_offset; + + // Scroll bar + display_drawLine(TOP_TRIANGLE_X3, upper_y_coord, TOP_TRIANGLE_X3, lower_y_coord, BLACK); + + // Surrounding lines + display_drawLine(TOP_TRIANGLE_X3 - 1, SCROLL_BAR_TOP_OFFSET, TOP_TRIANGLE_X3 - 1, SCROLL_BAR_BOTTOM_OFFSET, BLACK); + display_drawLine(TOP_TRIANGLE_X3 + 1, SCROLL_BAR_TOP_OFFSET, TOP_TRIANGLE_X3 + 1, SCROLL_BAR_BOTTOM_OFFSET, BLACK); + + // Upper triangle + display_drawTriangle(TOP_TRIANGLE_X1, TOP_TRIANGLE_Y1, TOP_TRIANGLE_X2, TOP_TRIANGLE_Y2, TOP_TRIANGLE_X3, TOP_TRIANGLE_Y3, BLACK); + + // Lower triangle + display_drawTriangle(BOTTOM_TRIANGLE_X1, BOTTOM_TRIANGLE_Y1, BOTTOM_TRIANGLE_X2, BOTTOM_TRIANGLE_Y2, BOTTOM_TRIANGLE_X3, BOTTOM_TRIANGLE_Y3, BLACK); + +#undef SCALING_FACTOR +#undef BOTTOM_OF_HEADER +#undef TRIANGLE_HEIGHT +#undef TRIANGLE_BASE +#undef SCROLL_BAR_TOP_OFFSET +#undef TOP_TRIANGLE_X1 +#undef TOP_TRIANGLE_Y1 +#undef TOP_TRIANGLE_X2 +#undef TOP_TRIANGLE_Y2 +#undef TOP_TRIANGLE_X3 +#undef TOP_TRIANGLE_Y3 +#undef BOTTOM_TRIANGLE_X1 +#undef BOTTOM_TRIANGLE_Y1 +#undef BOTTOM_TRIANGLE_X2 +#undef BOTTOM_TRIANGLE_Y2 +#undef BOTTOM_TRIANGLE_X3 +#undef BOTTOM_TRIANGLE_Y3 +#undef SCROLL_BAR_BOTTOM_OFFSET } void scroll_up() @@ -1110,13 +1166,9 @@ void eink_display() { } -static int g_selected_row; - - -void screen_header_draw(enum Screen nr) +static int g_selected_row;static void screen_header_draw(enum Screen nr) { int display_width = display_getWidth(); - int display_height = display_getHeight(); char screen_str[32]; int first_screen = TABLES_SCREEN+1; int num_screens = NUM_SCREENS-first_screen; @@ -1124,12 +1176,8 @@ void screen_header_draw(enum Screen nr) const char *title = screen_get_title(nr); sprintf((char *)&screen_str, "%d", screen_nr); // generic stuff - display_drawRect(0, 0, display_width, display_width, BLACK); - display_drawLine(0, 12, display_width, 12, BLACK); - int width = ((float)display_width/(float)num_screens)*(screen_nr); - display_drawLine(0, 11, width, 11, BLACK); - display_drawLine(0, 10, width, 10, BLACK); - display_drawLine(0, 12, display_width, 12, BLACK); + display_drawLine(2, 12, display_width, 12, BLACK); + if (title){ display_setCursor(3, 2); snprintf(screen_str, 19, "%s", title); @@ -1137,72 +1185,67 @@ void screen_header_draw(enum Screen nr) display_setCursor(35, 2); snprintf(screen_str, 19, "%d/%d", screen_nr, num_screens); } - display_puts(screen_str); + display_puts_max_n(screen_str, 11); } -static const struct menu_t g_system_menu[] = { +static struct menu_t g_system_menu[] = { { "QR Codes", &go_QR_SCREEN, }, { - "Group Objects", - &go_GOT_SCREEN, - }, - { - "Recipients", - &go_GRT_SCREEN, - }, - { - "Publisher", - &go_GPT_SCREEN, - }, - { - "Security", - &go_AUTH_SCREEN, - }, - { - "Device info", + "Device Info", &go_DEV_SCREEN, }, { - "Reset", - &go_RESET_SCREEN, - }, - { - "Role", + "Connection", &go_ROLE_SCREEN, }, { - "Tables", + "KNX Tables", &go_TABLES_SCREEN, + }, + { + "Help", + &go_HELP_SCREEN, }, }; -const int system_menu_entries = 9; -struct menu_t *g_cur_menu; +void app_header_draw(enum Screen nr) +{ + screen_header_draw(nr);} + +const int system_menu_entries = 5; + +const struct menu_t *g_cur_menu; int g_cur_menu_entries; -void draw_scrollable_menu(const struct menu_t* menu, int numentries) +static int g_menu_screen_selected_row = 0; + + +void draw_scrollable_menu(struct menu_t* menu, int numentries, enum Screen nr) { + if (nr == MENU_SCREEN) + g_selected_row = g_menu_screen_selected_row; + g_cur_menu = menu; g_cur_menu_entries = numentries; for (int i = 0; i < numentries; i++) { if (g_selected_row == i) + { display_setTextColor(WHITE, BLACK); + display_drawLine(0, EINK_LINE_NO(i+1-g_scroll_position), 0, EINK_LINE_NO(i+1-g_scroll_position)+6, BLACK); + } else + { display_setTextColor(BLACK, WHITE); - scroll_display_wrapper(3, EINK_LINE_NO(i+1), menu[i].text); + } + + scroll_display_wrapper(1, EINK_LINE_NO(i+1), T(menu[i].text)); } display_setTextColor(WHITE, BLACK); } -bool load_menu_screen() -{ - screen_header_draw(MENU_SCREEN); - draw_scrollable_menu(g_system_menu, system_menu_entries); -} - void menu_scroll() { if ((g_selected_row - 8) >= g_scroll_position) @@ -1211,13 +1254,26 @@ void menu_scroll() g_scroll_position = g_selected_row; } + +bool load_menu_screen() +{ + app_header_draw(MENU_SCREEN); + menu_scroll(); + draw_scrollable_menu(g_system_menu, system_menu_entries, MENU_SCREEN); +} + void menu_next() { g_selected_row++; - if (g_selected_row > (g_cur_menu_entries)) + if (g_selected_row > (g_cur_menu_entries-1)) g_selected_row = 0; menu_scroll(); refresh_screen(false); + // do not refresh while in the menu, refresh will be done sholtly after entering a regular page + g_eink_draws_without_refresh = g_eink_draws_cutoff; + // cache the selected menu row (only for the system menu for now) + if(g_screen_nr == MENU_SCREEN) + g_menu_screen_selected_row = g_selected_row; } void menu_prev() @@ -1227,6 +1283,9 @@ void menu_prev() g_selected_row = (g_cur_menu_entries-1); menu_scroll(); refresh_screen(false); + g_eink_draws_without_refresh = g_eink_draws_cutoff; + if(g_screen_nr == MENU_SCREEN) + g_menu_screen_selected_row = g_selected_row; } void menu_select() @@ -1239,282 +1298,123 @@ void menu_select() bool load_dev_screen() { - screen_header_draw(DEV_SCREEN); + app_header_draw(DEV_SCREEN); oc_device_info_t *device = oc_core_get_device_info(THIS_DEVICE); char screen_str[20]; if (device == NULL) { - display_puts("Not inited"); + display_setCursor(3, EINK_LINE_NO(1)); + display_puts(T("Not initialised")); return true; } - char* hwt = oc_string(device->hwt ); + display_setCursor(3, EINK_LINE_NO(1)); - display_puts("LP4:Back SP4:PM"); + strncpy(screen_str, oc_string(device->serialnumber), 19); + display_puts("S:"); + display_puts(screen_str); + + char* hwt = oc_string(device->hwt ); display_setCursor(2, EINK_LINE_NO(2)); display_puts("IA:"); snprintf(screen_str, 19, "%d", device->ia); display_puts(screen_str); display_setCursor(2, EINK_LINE_NO(3)); - display_puts("PM:"); - if (device->pm) { - display_setTextColor(WHITE, BLACK); - display_puts(" true "); - display_setTextColor(BLACK, WHITE); - } else { - display_puts(" false "); - } - display_setCursor(2, EINK_LINE_NO(4)); display_puts("HW:"); snprintf(screen_str, 19, "%d %d %d", device->hwv.major, device->hwv.minor, device->hwv.patch); display_puts(screen_str); - display_setCursor(2, EINK_LINE_NO(5)); + display_setCursor(2, EINK_LINE_NO(4)); display_puts("SW:"); snprintf(screen_str, 19, "%d %d %d", device->fwv.major, device->fwv.minor, device->fwv.patch); display_puts(screen_str); + display_setCursor(2, EINK_LINE_NO(5)); + display_puts("HWT"); + display_puts(hwt); display_setCursor(2, EINK_LINE_NO(6)); - display_puts("Model:"); + display_puts(T("Model:")); display_puts(oc_string(device->model)); - display_setCursor(2, EINK_LINE_NO(7)); - display_puts("HWT:"); - display_puts(hwt); return true; } -bool load_got_screen() +bool load_tables_screen() { - screen_header_draw(GOT_SCREEN); - oc_group_object_table_t *entry; - uint8_t cur_line = 1; - char c_flags_buffer[6]; - char screen_str[20]; +#ifdef __GNUC__ + _Pragma("GCC diagnostic push"); + _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\""); +#endif + app_header_draw(TABLES_SCREEN); - int max_entries = oc_core_get_group_object_table_total_size(); - for (int i = 0; i < max_entries; ++i) - { - entry = oc_core_get_group_object_table_entry(i); - if (entry->ga_len <= 0) // There is no entry at that index - break; + // If the device has not been initialised, then show "device not initialised" + oc_device_info_t *device = oc_core_get_device_info(THIS_DEVICE); + if (device == NULL) { + display_setCursor(3, EINK_LINE_NO(1)); + display_puts(T("Device not initialised.")); - // Print entry to the screen - /* - Entry %d (i + 1): - - id: - - href: - - cflags: --cd-d - - ga: - . - . - . - */ - snprintf(screen_str, 19, "==> Entry %d ", i + 1); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - snprintf(screen_str, 19, "-id: %d", entry->id); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - snprintf(screen_str, 19, "-href:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - snprintf(screen_str, 19, " %s", oc_string_checked(entry->href)); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - - c_flags_buffer[0] = '\0'; - oc_cflags_as_string(c_flags_buffer, entry->cflags); - snprintf(screen_str, 19, "-cflags: %s", c_flags_buffer); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - - snprintf(screen_str, 19, "-ga:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - for (int j = 0; j < entry->ga_len; ++j) - { - snprintf(screen_str, 19, " %d", entry->ga[j]); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - } + return true; } - return true; -} - -void load_grt_gpt_screen(int(*size_fn)(), void*(*entry_fn)(int)) -{ - oc_group_rp_table_t *entry; - uint8_t cur_line = 1; - char screen_str[20]; - - int max_entries = (*size_fn)(); - for (int i = 0; i < max_entries; ++i) + // If the lsm_state is not LSM_S_LOADED, then show "device not loaded" + if (oc_a_lsm_state(THIS_DEVICE) != LSM_S_LOADED) { - entry = (*entry_fn)(i); - if (entry->ga_len <= 0) // There is no entry at that index - break; + display_setCursor(3, EINK_LINE_NO(1)); + display_puts(T("Device not loaded.")); - // Print entry to the screen - /* - Entry %d (i + 1): - - id: - - iid: - - grpid: - - ga: - . - . - . - */ - snprintf(screen_str, 19, "==> Entry %d", i + 1); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - snprintf(screen_str, 19, "-id: %d", entry->id); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - - // uint64_t decimal number has max 20 numbers + 1 for terminator - char str[21]; - oc_conv_uint64_to_dec_string(str, entry->iid); - snprintf(screen_str, 19, "-iid: %s", str); - - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - snprintf(screen_str, 19, "-grpid: %d", entry->grpid); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - - snprintf(screen_str, 19, "-ga:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - for (int j = 0; j < entry->ga_len; ++j) - { - snprintf(screen_str, 19, " %d", entry->ga[j]); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - } + return true; } -} -bool load_grt_screen() -{ - screen_header_draw(GRT_SCREEN); - load_grt_gpt_screen(&oc_core_get_recipient_table_size, - &oc_core_get_recipient_table_entry); - return true; -} + char iid_str[17]; // 16 + null terminator + char screen_str[30]; // 17 + enough for some extra characters e.g. "iid: " -bool load_gpt_screen() -{ - screen_header_draw(GPT_SCREEN); - load_grt_gpt_screen(&oc_core_get_publisher_table_size, - &oc_core_get_publisher_table_entry); - return true; -} - -bool load_auth_screen() -{ - - screen_header_draw(AUTH_SCREEN); - oc_auth_at_t *entry; - uint8_t cur_line = 1; - char screen_str[20]; - - int max_entries = oc_core_get_at_table_size(); - for (int i = 0; i < max_entries; ++i) - { - entry = get_auth_at_entry(i); - if (oc_string_len(entry->id) == 0) // There is no entry at that index - break; - - // Print entry to the screen - /* - Entry %d (i + 1): - - id - ... - - osc_id - ... - - osc_rid - ... - - aud - ... - - ga: - . - . - typedef struct oc_auth_at_t - { - oc_string_t id; - oc_interface_mask_t scope; - oc_at_profile_t - profile; - oc_string_t aud; - oc_string_t sub; - oc_string_t kid; - oc_string_t osc_version; - oc_string_t osc_ms; - uint8_t osc_hkdf; - uint8_t osc_alg; - oc_string_t osc_salt; - oc_string_t osc_contextid; - oc_string_t osc_id; - oc_string_t osc_rid; - int nbf; - int ga_len; - int64_t *ga; - } oc_auth_at_t; - */ - snprintf(screen_str, 19, "==> Entry %d", i + 1); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), "-id:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), oc_string(entry->id)); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), "-osc_id:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), oc_string(entry->osc_id)); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), "-osc_rid:"); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), oc_string(entry->osc_rid)); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), "-ga:"); - for (int j = 0; j < entry->ga_len; j++) { - int64_t ga = entry->ga[j]; - snprintf(screen_str, 19, " %d", ga); - scroll_display_wrapper(3, EINK_LINE_NO(cur_line++), screen_str); - } - - } - return true; -} + oc_conv_uint64_to_hex_string(iid_str, device->iid); + display_setCursor(3, EINK_LINE_NO(1)); + snprintf(screen_str, 17, "iid:h%s", iid_str); + display_puts(screen_str); -bool load_tables_screen() -{ - screen_header_draw(TABLES_SCREEN); - char screen_str[20]; uint8_t num_entries = 0; - uint8_t cur_line = 1; + uint8_t cur_line = 3; bool check_got_entry(oc_group_object_table_t *ga) { return oc_string_len(ga->href) != 0; } - bool check_grt_gpt_entry(oc_group_rp_table_t *grp) { return grp->ga_len != 0; } + bool check_grt_gpt_entry(oc_group_rp_table_t *grp) { return grp->id > -1; } bool check_sec_entry(oc_auth_at_t *at) { return oc_string_len(at->id) != 0; } struct table_fns_s{ const char *fmt; int (*tbl_size_fn)(void); void *(*tbl_entry_fn)(int); - bool (*checker)(void*) - } table_fns[] = { + bool (*checker)(void*); + }; + struct table_fns_s table_fns[] = { { "Grp Obj Tbl: %d", - &oc_core_get_group_object_table_total_size, - &oc_core_get_group_object_table_entry, - &check_got_entry + (void*)&oc_core_get_group_object_table_total_size, + (void*)&oc_core_get_group_object_table_entry, + (void*)&check_got_entry }, { "Rec Tbl: %d", - &oc_core_get_recipient_table_size, - &oc_core_get_recipient_table_entry, - &check_grt_gpt_entry + (void*)&oc_core_get_recipient_table_size, + (void*)&oc_core_get_recipient_table_entry, + (void*)&check_grt_gpt_entry }, { "Pub Tbl: %d", - &oc_core_get_publisher_table_size, - &oc_core_get_publisher_table_entry, - &check_grt_gpt_entry + (void*)&oc_core_get_publisher_table_size, + (void*)&oc_core_get_publisher_table_entry, + (void*)&check_grt_gpt_entry }, { "Sec Tbl: %d", - &oc_core_get_at_table_size, - &get_auth_at_entry, - &check_sec_entry + (void*)&oc_core_get_at_table_size, + (void*)&get_auth_at_entry, + (void*)&check_sec_entry } }; const int num_tables = (sizeof(table_fns)/sizeof(table_fns[0])); - for (int i = 0; i < 4; i++) { + for (int i = 0; i < num_tables; i++) { struct table_fns_s *fns = &table_fns[i]; display_setCursor(3, EINK_LINE_NO(cur_line++)); num_entries = 0; @@ -1523,72 +1423,112 @@ bool load_tables_screen() if ((*fns->checker)((*fns->tbl_entry_fn)(j))) ++num_entries; } + snprintf(screen_str, 20, fns->fmt, num_entries); display_puts(screen_str); } return true; +#ifdef __GNUC__ +_Pragma("GCC diagnostic pop") +#endif } -bool load_reset_screen() -{ - screen_header_draw(RESET_SCREEN); - struct reset_t *it = g_resets; - int i = 0; - while(it) { - if (g_selected_row == i) - display_setTextColor(WHITE, BLACK); - else - display_setTextColor(BLACK, WHITE); - scroll_display_wrapper(0, EINK_LINE_NO(i+1), it->desc); - i++; - it = it->next; - } - display_setTextColor(WHITE, BLACK); +bool load_help_screen() +{ + app_header_draw(HELP_SCREEN); + + const char *web_str = "cascoda.com/products"; + SIF_SSD1681_overlay_qr_code(web_str, get_framebuffer(), 2, 25, 30); + return true; } -void reset_next() -{ - g_selected_row++; - if (g_selected_row >= num_resets()) - g_selected_row = 0; - menu_scroll(); - refresh_screen(false); -} +static bool should_go_back_to_sleep = false; +static uint16_t replies_received = 0; +static uint16_t requests_sent = 0; -void reset_prev() +static void ping_stat_cb(const otPingSenderStatistics *aStatistics, void *aContext) { - g_selected_row--; - if (g_selected_row < 0) - g_selected_row = num_resets()-1; - menu_scroll(); + (void)aContext; + requests_sent += aStatistics->mSentCount; + replies_received += aStatistics->mReceivedCount; refresh_screen(false); } -void reset_select() -{ - int i = g_selected_row; - struct reset_t *it = g_resets; - while (i && it) { - i--; - it = it->next; - } - if (!it) - return; - (it->do_reset)(); - set_screen(MENU_SCREEN); -} - bool load_role_screen() { - screen_header_draw(ROLE_SCREEN); + char screen_str[21]; + const otExtAddress *addr = otLinkGetExtendedAddress(OT_INSTANCE); + + app_header_draw(ROLE_SCREEN); + + display_setCursor(1, EINK_LINE_NO(4)); + snprintf(screen_str, 20, "%s: %s", T("Role"), T(otThreadDeviceRoleToString(otThreadGetDeviceRole(OT_INSTANCE)))); + display_puts(screen_str); + + if (otThreadGetDeviceRole(OT_INSTANCE) <= OT_DEVICE_ROLE_DETACHED) + goto exit; + + if (addr) + { + display_setCursor(1, EINK_LINE_NO(1)); + snprintf(screen_str, 20, "%02x%02x%02x%02x%02x%02x%02x%02x", + addr->m8[0], + addr->m8[1], + addr->m8[2], + addr->m8[3], + addr->m8[4], + addr->m8[5], + addr->m8[6], + addr->m8[7]); + display_puts(screen_str); + } + + display_setCursor(1, EINK_LINE_NO(2)); + snprintf(screen_str, 20, "0x%04x", otThreadGetRloc16(OT_INSTANCE)); + display_puts(screen_str); + + display_setCursor(1, EINK_LINE_NO(5)); + display_puts(T("Pinging...")); + + display_setCursor(1, EINK_LINE_NO(6)); + snprintf(screen_str, 20, "%s %d", T("Requests:"), requests_sent); + display_puts(screen_str); + + display_setCursor(1, EINK_LINE_NO(7)); + snprintf(screen_str, 20, "%s %d", T("Replies:"), replies_received); + display_puts(screen_str); + + otPingSenderConfig config; + memset(&config, 0, sizeof(config)); + memcpy(&config.mDestination, otThreadGetRealmLocalAllThreadNodesMulticastAddress(OT_INSTANCE), sizeof(otIp6Address)); + config.mReplyCallback = NULL; + config.mStatisticsCallback = ping_stat_cb; + config.mCallbackContext = NULL; + config.mCount = 1; + config.mInterval = 3000; + config.mTimeout = 5000; + config.mAllowZeroHopLimit = false; + + if (!otThreadGetLinkMode(OT_INSTANCE).mRxOnWhenIdle) // Sleepy device + { + should_go_back_to_sleep = true; + otLinkModeConfig linkMode = {0}; + linkMode.mRxOnWhenIdle = 1; + otThreadSetLinkMode(OT_INSTANCE, linkMode); + } + + otPingSenderPing(OT_INSTANCE, &config); + +exit: return true; } bool load_splash_screen() { display_fixed_image(knx_iot_logo); + return false; } @@ -1626,80 +1566,128 @@ uint32_t num_generic(void *list) { return i; } -void register_qr_code(struct qr_code_t *qrcode) { - register_generic(&g_qr_codes, qrcode); - if (g_screen_nr == QR_SCREEN) - refresh_screen(false); -} +bool load_qr_screen() +{ +#define O_NUM_PREFIX "1P" +#define MAKE_STR(x) #x +#define SN_PREFIX "+41S" +#define EUI_PREFIX "+3ZEUI:" +#define NET_PASS_PREFIX ".P:" +#define APP_PASS_PREFIX ".PA:" -void deregister_qr_code(struct qr_code_t *qrcode) { - return deregister_generic(&g_qr_codes, qrcode); - if (g_screen_nr == QR_SCREEN) - refresh_screen(false); -} + char qr_str[100]; -bool qr_code_is_registered(struct qr_code_t *qrcode) { - return generic_is_registered(g_qr_codes, qrcode); -} + char thread_pw_str_arr[33]; + const char *thread_pw_str_ptr; -uint32_t num_qr_codes() { - return num_generic(g_qr_codes); -} + uint8_t eui64_bytes[8]; + char eui64_str[19]; // 8 bytes -> 16 characters + 1 null terminator -void register_reset(struct reset_t *reset) -{ - register_generic(&g_resets, reset); - if (g_screen_nr == RESET_SCREEN) - refresh_screen(false); -} + uint8_t sn_bytes[6]; + char sn_str[13]; // 6 bytes -> 12 characters + 1 null terminator -void deregister_reset(struct reset_t *reset) -{ - deregister_generic(&g_resets, reset); - if (g_screen_nr == RESET_SCREEN) - refresh_screen(false); -} + char knx_pw_str_arr[33]; + const char *knx_pw_str_ptr; -bool reset_is_registered(struct reset_t *reset) -{ - return generic_is_registered(g_resets, reset); -} - -uint32_t num_resets() -{ - return num_generic(g_resets); -} + int error; + // Check if all the information is available in storage + error = knx_get_stored_thread_password(thread_pw_str_arr); + error |= knx_get_stored_eui64(eui64_bytes); + error |= knx_get_stored_serial_number(sn_bytes); + error |= knx_get_stored_password(knx_pw_str_arr); -bool load_qr_screen() -{ - char qr[100]; - int i = 0; - struct qr_code_t *qrcode = g_qr_codes; - while (i < g_qr && qrcode) { - qrcode = qrcode->next; - i++; + if (error) // Some information is missing from storage + { + // Check if the device's KNX stack has been initialised + oc_device_info_t *device = oc_core_get_device_info(THIS_DEVICE); + if (device == NULL) + { + // Device's KNX stack is not initialised, only show Thread QR code + display_setCursor(2, EINK_LINE_NO(0)); + display_puts("Thread"); + PlatformGetQRString(qr_str, 64, OT_INSTANCE); + } + else + { + // Device's KNX stack is initialised, show the joint Thread + QR code + display_setCursor(2, EINK_LINE_NO(0)); + display_puts("Thread + KNX"); + + thread_pw_str_ptr = PlatformGetJoinerCredential(OT_INSTANCE); + + // eui64 + otExtAddress eui64; + otLinkGetFactoryAssignedIeeeEui64(OT_INSTANCE, &eui64); + snprintf(eui64_str, sizeof(eui64_str), "%02X%02X%02X%02X%02X%02X%02X%02X", + eui64.m8[0], + eui64.m8[1], + eui64.m8[2], + eui64.m8[3], + eui64.m8[4], + eui64.m8[5], + eui64.m8[6], + eui64.m8[7]); + + // serial number + snprintf(sn_str, sizeof(sn_str), "%s", oc_string(device->serialnumber)); + uint8_t i = 0; + while (sn_str[i++]) + sn_str[i - 1] = toupper(sn_str[i - 1]); + + // knx password + knx_pw_str_ptr = app_get_password(); + + snprintf(qr_str, 100, O_NUM_PREFIX MAKE_STR() SN_PREFIX "%s" EUI_PREFIX "%s" NET_PASS_PREFIX "%s" APP_PASS_PREFIX "%s", + sn_str, eui64_str, thread_pw_str_ptr, knx_pw_str_ptr); + } } - if (qrcode != NULL){ - strncpy(qr, qrcode->str_data, 99); - SIF_SSD1681_overlay_qr_code(qr, get_framebuffer(), 2, 20, 2); - } else { - strcpy(qr, "No QR codes registered!"); + else // All the information is available in storage + { + display_setCursor(2, EINK_LINE_NO(0)); + display_puts("Thread + KNX"); + + // serial number in upper case + snprintf(sn_str, + sizeof(sn_str), + "%02X%02X%02X%02X%02X%02X", + sn_bytes[0], + sn_bytes[1], + sn_bytes[2], + sn_bytes[3], + sn_bytes[4], + sn_bytes[5]); + + // eui64 in upper case + snprintf(eui64_str, + sizeof(eui64_str), + "%02X%02X%02X%02X%02X%02X%02X%02X", + eui64_bytes[0], + eui64_bytes[1], + eui64_bytes[2], + eui64_bytes[3], + eui64_bytes[4], + eui64_bytes[5], + eui64_bytes[6], + eui64_bytes[7]); + + snprintf(qr_str, 100, O_NUM_PREFIX MAKE_STR() SN_PREFIX "%s" EUI_PREFIX "%s" NET_PASS_PREFIX "%s" APP_PASS_PREFIX "%s", + sn_str, eui64_str, thread_pw_str_arr, knx_pw_str_arr); } - display_setCursor(1, 70); - display_setTextColor(BLACK, WHITE), - display_puts(qr); + + // Display the QR code + SIF_SSD1681_overlay_qr_code(qr_str, get_framebuffer(), 2, 18, EINK_LINE_NO(2)); + + // Fading QR Codes are harder to read + g_eink_clean_redraw = true; return true; -} -static void qr_switch_screen() -{ - // QR code on THREAD - g_qr += 1; - if (g_qr >= num_qr_codes()) { - g_qr = 0; - } - refresh_screen(false); +#undef O_NUM_PREFIX +#undef MAKE_STR +#undef SERIAL_NUM_PREFIX +#undef EUI_PREFIX +#undef NETWORK_PASS_PREFIX +#undef APPLICATION_PASS_PREFIX } // go to the next screen, either up or down @@ -1755,24 +1743,16 @@ static bool load_pm_screen() display_puts(" ON"); else display_puts(" OFF"); - draw_buttons(); - return true; -} - + + draw_buttons(); + -static void toggle_pm() -{ - bool pm = oc_knx_device_in_programming_mode(THIS_DEVICE); - pm = !pm; - oc_knx_device_set_programming_mode(THIS_DEVICE, pm); - oc_device_info_t* device = oc_core_get_device_info(THIS_DEVICE); - knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); - refresh_screen(false); + return true; } void refresh_screen(bool clean_redraw) { - g_clean_redraw |= clean_redraw; + g_eink_clean_redraw = clean_redraw; TASKLET_ScheduleDelta(&screen_Tasklet, SCHEDULE_NOW, NULL); } struct EinkScreenHandler g_screenHandlers[NUM_SCREENS] = @@ -1791,56 +1771,25 @@ struct EinkScreenHandler g_screenHandlers[NUM_SCREENS] = .screen_button_2_ShortPress_cb = &menu_prev, }, [QR_SCREEN] = { .load_screen_cb = &load_qr_screen, - .title = "QR codes", - .screen_button_3_ShortPress_cb = &qr_switch_screen, + .title = "QR Codes", + .screen_button_3_ShortPress_cb = &go_CONNECTIVITY_SCREEN, .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - - }, [GOT_SCREEN] = { - .load_screen_cb = &load_got_screen, - .title = "Group addrs", - .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_1_ShortPress_cb = &scroll_down, - .screen_button_2_ShortPress_cb = &scroll_up, - }, [GRT_SCREEN] = { - .load_screen_cb = &load_grt_screen, - .title = "Recipients", - .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_1_ShortPress_cb = &scroll_down, - .screen_button_2_ShortPress_cb = &scroll_up, - }, [GPT_SCREEN] = { - .load_screen_cb = &load_gpt_screen, - .title = "Publishers", - .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_1_ShortPress_cb = &scroll_down, - .screen_button_2_ShortPress_cb = &scroll_up, - }, [AUTH_SCREEN] = { - .load_screen_cb = &load_auth_screen, - .title = "Security", - .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_1_ShortPress_cb = &scroll_down, - .screen_button_2_ShortPress_cb = &scroll_up, }, [DEV_SCREEN] = { .load_screen_cb = &load_dev_screen, .title = "Device", .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_3_ShortPress_cb = &toggle_pm, - }, [RESET_SCREEN] = { - .load_screen_cb = &load_reset_screen, - .title = "Reset", - .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - .screen_button_3_ShortPress_cb = &reset_select, - .screen_button_1_ShortPress_cb = &reset_next, - .screen_button_2_ShortPress_cb = &reset_prev, }, [ROLE_SCREEN] = { .load_screen_cb = &load_role_screen, - .title = "Role", + .title = "Connection", .screen_button_3_LongPress_cb = &go_MENU_SCREEN, }, [TABLES_SCREEN] = { .load_screen_cb = &load_tables_screen, - .title = "Tables", - + .title = "KNX Tables", + .screen_button_3_LongPress_cb = &go_MENU_SCREEN, + }, [HELP_SCREEN] = { + .load_screen_cb = &load_help_screen, + .title = "Help", .screen_button_3_LongPress_cb = &go_MENU_SCREEN, - }, [GAME_INTRO] = { .load_screen_cb = &load_game_intro, .title = NULL, @@ -1886,7 +1835,7 @@ struct EinkScreenHandler g_screenHandlers[NUM_SCREENS] = const char* screen_get_title(enum Screen screen) { - return g_screenHandlers[screen].title; + return T(g_screenHandlers[screen].title); } void button_1_ShortPress_cb(void *ctx) { @@ -1933,6 +1882,24 @@ void button_3_Hold_cb(void *ctx) void eink_load_screen(enum Screen screen_nr) { + user_interaction_occurred = 1; + + if (screen_nr != ROLE_SCREEN) + { + otPingSenderStop(OT_INSTANCE); + replies_received = 0; + requests_sent = 0; + + // If device was sleeping before the pings, then go back to sleep now. + if (should_go_back_to_sleep) + { + should_go_back_to_sleep = false; + otLinkModeConfig linkMode = {0}; + otThreadSetLinkMode(OT_INSTANCE, linkMode); + } + } + + // Processing for loading the new screen. g_max_y_coord = EINK_LINE_NO(8); if (g_screen_nr < 0 || screen_nr >= NUM_SCREENS) { //soft reset eink screens @@ -1951,12 +1918,22 @@ void eink_load_screen(enum Screen screen_nr) show_scroll_bar(); display_setCursor(0,0); display_setTextSize(1); - if (g_eink_clean_redraw) { - display_render_full(); + if (g_eink_clean_redraw || g_eink_draws_without_refresh > g_eink_draws_cutoff) { + display_render_full(); g_eink_clean_redraw = false; + g_eink_draws_without_refresh = 0; + } else { - display_render_partial(false); + display_render_partial(false); + g_eink_draws_without_refresh++; } + + oc_storage_write("screen_nr", &screen_nr, 1); +} + +void go_CONNECTIVITY_SCREEN() +{ + set_screen(ROLE_SCREEN); } void eink_redraw() @@ -1966,12 +1943,18 @@ void eink_redraw() ca_error eink_initial_screen(void *args) { - static int g_start_counter = 0; // initial check to draw splash screen +#ifdef USE_STARTUP_FLOW + static int g_start_counter = 0; + PRINT_APP("USE_STARTUP_FLOW\n"); +#else + PRINT_APP("Jump at initial screen\n"); + static int g_start_counter = 3; +#endif oc_device_info_t* device = oc_core_get_device_info(THIS_DEVICE); if (g_start_counter == 0) { eink_load_screen(SPLASH_SCREEN); g_start_counter = 1; - TASKLET_ScheduleDelta(&screen_Tasklet, 5*1000, NULL); + TASKLET_ScheduleDelta(&screen_Tasklet, 3*1000, NULL); return CA_ERROR_SUCCESS; } else if (g_start_counter == 1) { g_start_counter = 2; @@ -1987,7 +1970,22 @@ ca_error eink_initial_screen(void *args) go_first_screen(); return CA_ERROR_SUCCESS; } + } else if (g_start_counter == 3) { + g_start_counter = 4; + g_eink_clean_redraw = true; + eink_load_screen(SPLASH_SCREEN); + + g_screen_nr = 0; + oc_storage_read("screen_nr", &g_screen_nr, 1); + + if (g_screen_nr == 0) // Meaning nothing was read from the storage + g_screen_nr = MENU_SCREEN; // Go to the navigation menu + + TASKLET_ScheduleDelta(&screen_Tasklet, 3 * 1000, NULL); + + return CA_ERROR_SUCCESS; } + eink_load_screen(g_screen_nr); return CA_ERROR_SUCCESS; } @@ -1999,8 +1997,18 @@ ca_error eink_initial_screen(void *args) void logic_initialize() { - // screen update, do it ASAP since it is the splash screen TASKLET_Init(&screen_Tasklet, &eink_initial_screen); TASKLET_ScheduleDelta(&screen_Tasklet, INITIAL_SCREEN_TIMEOUT, NULL); } + +void logic_role_changed() +{ +if (g_screen_nr == ROLE_SCREEN || g_screen_nr == DEV_SCREEN) + refresh_screen(true); +} + +bool logic_is_role_screen() +{ + return (g_screen_nr == ROLE_SCREEN); +} diff --git a/BATTLESHIP/knx_eink_battleships_virtual.c b/BATTLESHIP/knx_eink_battleships_virtual.c new file mode 100644 index 0000000..4a51cb5 --- /dev/null +++ b/BATTLESHIP/knx_eink_battleships_virtual.c @@ -0,0 +1,52 @@ +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Copyright (c) 2022 Cascoda Ltd +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Cascoda Limited. + * integrated circuit in a product or a software update for such product, must + * reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * 4. This software, whether provided in binary or any other form must not be decompiled, + * disassembled, reverse engineered or otherwise modified. + * + * 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. + * + * THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +/** + * @file + * + * This file is used to include additional code needed for virtual devices. + */ + +#include "oc_api.h" +#include "oc_core_res.h" +#include "api/oc_knx_dev.h" +#include "api/oc_knx_fp.h" +#include +#include "knx_eink_battleships.h" + + diff --git a/BATTLESHIP/knx_iot_sleepy_main.c b/BATTLESHIP/knx_iot_sleepy_main.c index 85c1ce9..8e1c4d3 100644 --- a/BATTLESHIP/knx_iot_sleepy_main.c +++ b/BATTLESHIP/knx_iot_sleepy_main.c @@ -33,7 +33,7 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include @@ -57,6 +57,7 @@ #include "cascoda-bm/cascoda_wait.h" #include "knx_iot_sleepy_main_extern.h" +#include "knx_iot_sleepy_main.h" #include "api/oc_knx_dev.h" #include "api/oc_knx_fp.h" @@ -91,6 +92,7 @@ void exit(int e) } otInstance *OT_INSTANCE; +extern uint8_t user_interaction_occurred; // Defer publishing of the service until you have the correct IP address // to advertise void knx_srp_add_service(void); @@ -98,6 +100,7 @@ void knx_srp_add_service(void); // To be implemented by the application void register_resources(void); void factory_presets_cb(size_t device_index, void *data); +void lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data); void hostname_cb(size_t device_index, oc_string_t host_name, void *data); int app_set_serial_number(char *serial_number); int app_init(void); @@ -113,6 +116,7 @@ void swu_cb(size_t device_index, oc_separate_response_t *response, size_t binary */ /** + /** * Handle application specific commands. */ @@ -153,10 +157,10 @@ static void ot_state_changed(uint32_t flags, void *context) if (flags & OT_CHANGED_THREAD_ROLE) { otDeviceRole role = otThreadGetDeviceRole(OT_INSTANCE); - PRINT("Role: %s\n", otThreadDeviceRoleToString(role)); + printf("Role: %s\n", otThreadDeviceRoleToString(role)); } // publish the MDNS service on startup - oc_device_info_t* device = oc_core_get_device_info(0); + oc_device_info_t *device = oc_core_get_device_info(0); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); #ifdef USE_SNTP bool must_update_rtc = (SNTP_GetState() == NO_TIME); @@ -166,6 +170,8 @@ static void ot_state_changed(uint32_t flags, void *context) SNTP_Update(); } #endif /* USE_SNTP */ + + logic_role_changed(); } static void signal_event_loop(void) @@ -212,19 +218,69 @@ static void sleep_if_possible(struct ca821x_dev *pDeviceRef, oc_clock_time_t tim return; /* check for hardware (application-specific) */ - if(!hardware_can_sleep()) + if (!hardware_can_sleep()) + return; + + hardware_sleep(pDeviceRef, nextAppEvent); +} + +// Skip Thread check, this function is used to allow sleeping before the device +// is fully commissioned to a Thread network +static void sleep_if_possible_no_thread(struct ca821x_dev *pDeviceRef, uint64_t timeToNextAppEvent) +{ + uint32_t nextAppEvent = (uint32_t)timeToNextAppEvent; + + if (timeToNextAppEvent > 0x7FFFFFFF || !timeToNextAppEvent) + nextAppEvent = 0x7FFFFFFF; + + /* check for hardware (application-specific) */ + if (!hardware_can_sleep()) return; hardware_sleep(pDeviceRef, nextAppEvent); } -// Sleepy Device - handler for polling (keep-alive) if required -ca_error sed_poll_handler(void *aContext) +// Variables used for the resynch mechanism +static int g_resynch_context; +static ca_tasklet g_resynch_tasklet; + +static ca_error child_resynch_handler(void *context) +{ + g_resynch_context = *((int *)context); + ca_error err = otThreadSleepyChildResynchronize(OT_INSTANCE); + TASKLET_ScheduleDelta(&g_resynch_tasklet, g_resynch_context * 1000, &g_resynch_context); + return err; +} + +void main_SetThreadChildTimeout(int seconds) +{ + otThreadSetChildTimeout(OT_INSTANCE, seconds); +} + +void swu_start_update_cb_imp(size_t device_index, uint32_t start_time, void *data) +{ + (void)data; + ca_error status = CA_ERROR_FAIL; + PRINT_APP("swu_start_update_cb_imp(), device: %d, applying new sofware in %d\n", device_index, start_time); +#if CASCODA_OTA_UPGRADE_ENABLED + status = ota_start_upgrade(start_time); + PRINT_APP("swu_start_update_cb_imp(), status = %d\n",status); +#endif +} + +void main_KickOffChildResynchMechanism(int seconds) { + static uint8_t s_first_call = true; + g_resynch_context = seconds; - otLinkSendDataRequest(OT_INSTANCE); + if (s_first_call) + TASKLET_Init(&g_resynch_tasklet, &child_resynch_handler); + else if (TASKLET_IsQueued(&g_resynch_tasklet)) + TASKLET_Cancel(&g_resynch_tasklet); - return CA_ERROR_SUCCESS; + TASKLET_ScheduleDelta(&g_resynch_tasklet, seconds * 1000, &g_resynch_context); + + s_first_call = false; } /** @@ -240,9 +296,12 @@ int main(void) oc_clock_time_t next_event; u8_t StartupStatus; struct ca821x_dev dev; + char thread_pw[33]; + uint8_t thread_eui64[8]; + int error; cascoda_serial_dispatch = ot_serial_dispatch; // Sleepy Device - cascoda_reinitialise = reinitialise_after_wakeup; + cascoda_reinitialise = reinitialise_after_wakeup; otError otErr = OT_ERROR_NONE; ca821x_api_init(&dev); @@ -251,7 +310,19 @@ int main(void) StartupStatus = EVBMEInitialise(CA_TARGET_NAME, &dev); BSP_RTCInitialise(); - PlatformRadioInitWithDev(&dev); + error = knx_get_stored_thread_password(thread_pw); + error |= knx_get_stored_eui64(thread_eui64); + + // if the details aren't present, initialise with values generated + // on first boot + if (error) + { + PlatformRadioInitWithDev(&dev); + } + else + { + PlatformRadioInitWithDevEui64(&dev, thread_eui64); + } // OpenThread Configuration OT_INSTANCE = otInstanceInitSingle(); @@ -262,37 +333,83 @@ int main(void) // Hardware specific setup hardware_init(); + logic_initialize(); #if CASCODA_OTA_UPGRADE_ENABLED /* Initialises handling of OTA Firmware Upgrade */ ota_upgrade_init(); #endif - // A backoff mechanism for joining the network + // A backoff/sleep mechanism for joining the network + enum backoff_and_sleep_timings + { + BACKOFF_MIN = 6 * 1000, // Start at 6 seconds + BACKOFF_MAX = 120 * 1000, // Increase till 2 minutes is reached + BACKOFF_INCREMENT = 6 * 1000, // Increase in intervals of 6 seconds + POST_DISCOVERY_AWAKE_TIME = 2 * 1000, // After sending a discovery request, stay awake for 2 seconds + }; + + u32_t curr_backoff_time = BACKOFF_MIN; u32_t joinCooldownTimer = 0; // Try to join network do { - cascoda_io_handler(&dev); + cascoda_io_handler(&dev); // Allows the device to do IO communications + hardware_poll(); // Allows the device to poll the hardware, e.g. sensor measurements + + if (user_interaction_occurred) + { + user_interaction_occurred = 0; + curr_backoff_time = BACKOFF_MIN; + } + + if (joinCooldownTimer >= POST_DISCOVERY_AWAKE_TIME) + { + uint64_t sleep_time = curr_backoff_time - POST_DISCOVERY_AWAKE_TIME; + sleep_if_possible_no_thread(&dev, sleep_time); + joinCooldownTimer += sleep_time; + } // If the timer has expired, try to join the network - if (joinCooldownTimer == 30) + if (joinCooldownTimer >= curr_backoff_time) { - printf("Trying to join Thread network...\n"); + if (!logic_is_role_screen()) + { + curr_backoff_time += BACKOFF_INCREMENT; + if (curr_backoff_time >= BACKOFF_MAX) + curr_backoff_time = BACKOFF_MAX; + } - // Print the joiner credentials, delaying for up to 1 second - PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); + printf("Trying to join Thread network...\n"); - otErr = PlatformTryJoin(&dev, OT_INSTANCE); - if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) - break; - joinCooldownTimer = 0; + // if the details aren't present, initialise with values generated + // on first boot + if (error) + { + // Print the joiner credentials, delaying for up to 1 second + PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); + + otErr = PlatformTryJoinWithCustomPoll(&dev, OT_INSTANCE, &hardware_poll); + if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) + break; + joinCooldownTimer = 0; + } + else + { + // Print the joiner credentials, delaying for up to 1 second + PlatformPrintJoinerCredentialsWithPskd(&dev, OT_INSTANCE, 0, thread_pw); + + otErr = PlatformTryJoinWithPskdWithCustomPoll(&dev, OT_INSTANCE, thread_pw, &hardware_poll); + if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) + break; + joinCooldownTimer = 0; + } } - joinCooldownTimer += 1; + ++joinCooldownTimer; - WAIT_ms(200); + WAIT_ms(1); } while (1); otThreadSetEnabled(OT_INSTANCE, true); @@ -300,7 +417,7 @@ int main(void) // Sleepy Device - SED initialisation, is this enough? - Need to implement poll handler and scheduling in hardware functions? otLinkModeConfig linkMode = {0}; otThreadSetLinkMode(OT_INSTANCE, linkMode); - //otLinkSetPollPeriod(OT_INSTANCE, SED_POLL_PERIOD); + // otLinkSetPollPeriod(OT_INSTANCE, SED_POLL_PERIOD); DNS_Init(OT_INSTANCE); #ifdef USE_SNTP @@ -325,19 +442,18 @@ int main(void) oc_storage_config("./knx_iot_creds"); - /* configure the serial number */ uint8_t sn[6]; - int error = knx_get_stored_serial_number(sn); + /* configure the serial number. must be done before stack initialization */ + error = knx_get_stored_serial_number(sn); if (error) { PRINT_APP("ERROR: Unique serial number not found! Using default value...\n"); PRINT_APP( "Please create the data file using knx-gen-data and flash it with chilictl in order to fix this issue.\n"); - } - else - { + } else { // turn binary to hexadecimal char serial_number_str[13]; + // serial number in upper case snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X", @@ -349,7 +465,6 @@ int main(void) sn[5]); app_set_serial_number(serial_number_str); } - #ifdef OC_SPAKE char pwd[33]; error = knx_get_stored_password(pwd); @@ -363,7 +478,7 @@ int main(void) { oc_spake_set_password(pwd); } - + uint8_t salt[32], rand[32]; uint32_t it; mbedtls_mpi w0; @@ -389,14 +504,28 @@ int main(void) oc_set_hostname_cb(hostname_cb, NULL); oc_set_reset_cb(reset_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if CASCODA_OTA_UPGRADE_ENABLED oc_set_swu_cb(swu_cb, (void *)"image_name"); + oc_set_swu_startupdate_cb(swu_start_update_cb_imp, (void *)"image_name"); #endif + oc_set_programming_mode_cb(prog_mode_cb, NULL); /* start the stack */ init = oc_main_init(&handler); + // configure the hostname. + // note: this method sets the SN part to uppercase, as that is how + // the serial number is stored in the oc_device_info_t + oc_device_info_t *device = oc_core_get_device_info(0); + char hostname_str[50]; + memset(hostname_str, 0, 49); + strcat(hostname_str, "knx-"); + strcat(hostname_str, oc_string(device->serialnumber)); + strcat(hostname_str, ".local"); + oc_core_set_device_hostname(0, hostname_str); + oc_set_max_app_data_size(1024); oc_set_mtu_size(1232); @@ -406,9 +535,9 @@ int main(void) } // publish the MDNS service on startup - oc_device_info_t* device = oc_core_get_device_info(0); + // knx_service_sleep_period(SED_POLL_PERIOD); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); - + PRINT("KNX IoT device, waiting on incoming connections.\n"); uint64_t iid = oc_core_get_device_iid(THIS_DEVICE); @@ -416,7 +545,7 @@ int main(void) // this information always gets printed. printf("Device iid: "); oc_print_uint64_t(iid, DEC_REPRESENTATION); - printf("\n"); + printf("\n"); printf("group publisher table:\n"); oc_print_reduced_group_publisher_table(); @@ -429,7 +558,7 @@ int main(void) hardware_poll(); otTaskletsProcess(OT_INSTANCE); // Sleepy Device - //oc_main_poll(); + // oc_main_poll(); sleep_if_possible(&dev, oc_main_poll()); } diff --git a/BATTLESHIP/knx_iot_sleepy_main.h b/BATTLESHIP/knx_iot_sleepy_main.h new file mode 100644 index 0000000..2b46c04 --- /dev/null +++ b/BATTLESHIP/knx_iot_sleepy_main.h @@ -0,0 +1,64 @@ +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Copyright (c) 2022-2023 Cascoda Limited +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Cascoda Limited. + * integrated circuit in a product or a software update for such product, must + * reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * 4. This software, whether provided in binary or any other form must not be decompiled, + * disassembled, reverse engineered or otherwise modified. + * + * 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. + * + * THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* calls that are implemented in sleepy main */ + +/** + * @brief sets the sleep time of the device (e.g. child) + * + * @param seconds, time in seconds + */ +void main_SetThreadChildTimeout(int seconds); + +/** + * @brief kicks off the resynch mechanism: sending of MLE Child Update Requests + * periodically to reattach to the parent in case the child was timed out + * + * @param seconds, time in seconds + */ +void main_KickOffChildResynchMechanism(int seconds); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/BATTLESHIP/knx_iot_sleepy_main_extern.h b/BATTLESHIP/knx_iot_sleepy_main_extern.h index 1a7b6b9..7fcb895 100644 --- a/BATTLESHIP/knx_iot_sleepy_main_extern.h +++ b/BATTLESHIP/knx_iot_sleepy_main_extern.h @@ -33,91 +33,90 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cascoda-bm/cascoda_interface.h" #include "cascoda-bm/cascoda_types.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/* for OpenThread: poll period for keep-alive */ -#define SED_POLL_PERIOD 5000 -/* devices will stay awake for at least this long before going back to sleep*/ -#define SED_MIN_AWAKE_TIME 1000 + /** + * @brief define what happens when receiving a post mesage on a URL + * + * @param url the URL of the message as a string + */ + void post_callback(char *url); -/** - * @brief define what happens when receiving a post mesage on a URL - * - * @param url the URL of the message as a string - */ -void post_callback(char *url); + /** + * @brief any hardware specific setup e.g. registering board LEDs and buttons + * + */ + void hardware_init(); -/** - * @brief any hardware specific setup e.g. registering board LEDs and buttons - * - */ -void hardware_init(); + /** + * @brief any hardware independent setup e.g. setting up graphics + * + */ + void logic_initialize(); -/** - * @brief any hardware independent setup e.g. setting up graphics - * - */ -void logic_initialize(); + /** + * @brief any action to take as a result of the role changing, e.g. refreshing screen + * + */ + void logic_role_changed(); -/** - * @brief any hardware specific actions to be continually run e.g. checking buttons for input - * - */ -void hardware_poll(); + /** + * @brief Whether or not the currently displayed screen is the ROLE screen + * + * @return true is on ROLE screen, false otherwise + */ + bool logic_is_role_screen(); -/** - * @brief Application-specific handling of programming mode command received from linker - * - */ -void programming_mode_embedded(size_t device_index, bool programming_mode); + /** + * @brief any hardware specific actions to be continually run e.g. checking buttons for input + * + */ + void hardware_poll(); -/** - * @brief Application-specific handling of reset when received from linker - * - */ -void reset_embedded(size_t device_index, int reset_value, void *data); + /** + * @brief Application-specific handling of programming mode command received from linker + * + */ + void programming_mode_embedded(size_t device_index, bool programming_mode); -/** - * @brief any hardware specific reinitialisation after wakeup from sleep - * - */ - void hardware_reinitialise(); + /** + * @brief Application-specific handling of reset when received from linker + * + */ + void reset_embedded(size_t device_index, int reset_value, void *data); -/** - * @brief hardware specific sleep (power-down) function - * - * @param pDeviceRef - Pointer to initialised ca821x_device_ref struct - * - * @param nextAppEvent - time to next application layer event - * - */ -void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent); + /** + * @brief any hardware specific reinitialisation after wakeup from sleep + * + */ + void hardware_reinitialise(); -/** - * @brief hardware specific check if device can sleep - * - * @return true if device is allowed to sleep, false otherwise - * - */ -bool hardware_can_sleep(); + /** + * @brief hardware specific sleep (power-down) function + * + * @param pDeviceRef - Pointer to initialised ca821x_device_ref struct + * + * @param nextAppEvent - time to next application layer event + * + */ + void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent); -/** - * @brief poll handler for openthread sleepy end device for keep-alive - * - * @param pDeviceRef - Pointer to tasklet context - * - * @return error status - * - */ -ca_error sed_poll_handler(void *aContext); + /** + * @brief hardware specific check if device can sleep + * + * @return true if device is allowed to sleep, false otherwise + * + */ + bool hardware_can_sleep(); #ifdef __cplusplus } diff --git a/BATTLESHIP/knx_iot_wakeful_main.c b/BATTLESHIP/knx_iot_wakeful_main.c index 9bf866a..b29d05e 100644 --- a/BATTLESHIP/knx_iot_wakeful_main.c +++ b/BATTLESHIP/knx_iot_wakeful_main.c @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Limited + Copyright (c) 2022-2024 Cascoda Limited -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -33,7 +33,7 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ #include @@ -100,6 +100,7 @@ void knx_srp_add_service(void); // To be implemented by the application void register_resources(void); void factory_presets_cb(size_t device_index, void *data); +void lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data); void hostname_cb(size_t device_index, oc_string_t host_name, void *data); int app_set_serial_number(char *serial_number); int app_init(void); @@ -167,10 +168,10 @@ static void ot_state_changed(uint32_t flags, void *context) if (flags & OT_CHANGED_THREAD_ROLE) { otDeviceRole role = otThreadGetDeviceRole(OT_INSTANCE); - PRINT("Role: %s\n", otThreadDeviceRoleToString(role)); + printf("Role: %s\n", otThreadDeviceRoleToString(role)); } // publish the MDNS service on startup - oc_device_info_t* device = oc_core_get_device_info(0); + oc_device_info_t *device = oc_core_get_device_info(0); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); #ifdef USE_SNTP @@ -181,6 +182,8 @@ static void ot_state_changed(uint32_t flags, void *context) SNTP_Update(); } #endif /* USE_SNTP */ + + logic_role_changed(); } static void signal_event_loop(void) @@ -202,6 +205,16 @@ static void reset_cb(size_t device_index, int reset_value, void *data) reset_embedded(device_index, reset_value, data); } +void swu_start_update_cb_imp(size_t device_index, uint32_t start_time, void *data) +{ + ca_error status = CA_ERROR_FAIL; + PRINT_APP("swu_start_update_cb_imp(), device: %d, applying new sofware in %d\n", device_index, start_time); +#if CASCODA_OTA_UPGRADE_ENABLED + status = ota_start_upgrade(start_time); + PRINT_APP("swu_start_update_cb_imp(), status = %d\n",status); +#endif +} + /** * main application. * intializes the global variables @@ -215,6 +228,9 @@ int main(void) oc_clock_time_t next_event; u8_t StartupStatus; struct ca821x_dev dev; + char thread_pw[33]; + uint8_t thread_eui64[8]; + int error; cascoda_serial_dispatch = ot_serial_dispatch; otError otErr = OT_ERROR_NONE; @@ -224,7 +240,19 @@ int main(void) StartupStatus = EVBMEInitialise(CA_TARGET_NAME, &dev); BSP_RTCInitialise(); - PlatformRadioInitWithDev(&dev); + error = knx_get_stored_thread_password(thread_pw); + error |= knx_get_stored_eui64(thread_eui64); + + // if the details aren't present, initialise with values generated + // on first boot + if (error) + { + PlatformRadioInitWithDev(&dev); + } + else + { + PlatformRadioInitWithDevEui64(&dev, thread_eui64); + } // OpenThread Configuration OT_INSTANCE = otInstanceInitSingle(); @@ -237,7 +265,6 @@ int main(void) hardware_init(); logic_initialize(); - #if CASCODA_OTA_UPGRADE_ENABLED /* Initialises handling of OTA Firmware Upgrade */ ota_upgrade_init(); @@ -250,19 +277,35 @@ int main(void) do { cascoda_io_handler(&dev); + hardware_poll(); // If the timer has expired, try to join the network if (joinCooldownTimer == 60) { printf("Trying to join Thread network...\n"); - // Print the joiner credentials, delaying for up to 1 second - PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); - - otErr = PlatformTryJoin(&dev, OT_INSTANCE); - if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) - break; - joinCooldownTimer = 0; + // if the details aren't present, initialise with values generated + // on first boot + if (error) + { + // Print the joiner credentials, delaying for up to 1 second + PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); + + otErr = PlatformTryJoinWithCustomPoll(&dev, OT_INSTANCE, &hardware_poll); + if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) + break; + joinCooldownTimer = 0; + } + else + { + // Print the joiner credentials, delaying for up to 1 second + PlatformPrintJoinerCredentialsWithPskd(&dev, OT_INSTANCE, 0, thread_pw); + + otErr = PlatformTryJoinWithPskdWithCustomPoll(&dev, OT_INSTANCE, thread_pw, &hardware_poll); + if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) + break; + joinCooldownTimer = 0; + } } joinCooldownTimer += 1; @@ -295,19 +338,18 @@ int main(void) oc_storage_config("./knx_iot_creds"); - /* configure the serial number */ uint8_t sn[6]; - int error = knx_get_stored_serial_number(sn); + /* configure the serial number. must be done before stack initialization */ + error = knx_get_stored_serial_number(sn); if (error) { PRINT_APP("ERROR: Unique serial number not found! Using default value...\n"); PRINT_APP( "Please create the data file using knx-gen-data and flash it with chilictl in order to fix this issue.\n"); - } - else - { + } else { // turn binary to hexadecimal char serial_number_str[13]; + // serial number in upper case snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X", @@ -319,7 +361,6 @@ int main(void) sn[5]); app_set_serial_number(serial_number_str); } - #ifdef OC_SPAKE char pwd[33]; error = knx_get_stored_password(pwd); @@ -333,7 +374,7 @@ int main(void) { oc_spake_set_password(pwd); } - + uint8_t salt[32], rand[32]; uint32_t it; mbedtls_mpi w0; @@ -359,13 +400,26 @@ int main(void) oc_set_hostname_cb(hostname_cb, NULL); oc_set_reset_cb(reset_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if CASCODA_OTA_UPGRADE_ENABLED oc_set_swu_cb(swu_cb, (void *)"image_name"); + oc_set_swu_startupdate_cb(swu_start_update_cb_imp, (void *)"image_name"); #endif oc_set_programming_mode_cb(prog_mode_cb, NULL); /* start the stack */ init = oc_main_init(&handler); + + // configure the hostname. + // note: this method sets the SN part to uppercase, as that is how + // the serial number is stored in the oc_device_info_t + oc_device_info_t *device = oc_core_get_device_info(0); + char hostname_str[50]; + memset(hostname_str, 0, 49); + strcat(hostname_str, "knx-"); + strcat(hostname_str, oc_string(device->serialnumber)); + strcat(hostname_str, ".local"); + oc_core_set_device_hostname(0, hostname_str); oc_set_max_app_data_size(1024); oc_set_mtu_size(1232); @@ -382,7 +436,7 @@ int main(void) // this information always gets printed. printf("Device iid: "); oc_print_uint64_t(iid, DEC_REPRESENTATION); - printf("\n"); + printf("\n"); printf("group publisher table:\n"); oc_print_reduced_group_publisher_table(); diff --git a/BATTLESHIP/knx_iot_wakeful_main_extern.h b/BATTLESHIP/knx_iot_wakeful_main_extern.h index a8ef4a9..0f9fba0 100644 --- a/BATTLESHIP/knx_iot_wakeful_main_extern.h +++ b/BATTLESHIP/knx_iot_wakeful_main_extern.h @@ -33,52 +33,59 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cascoda-bm/cascoda_interface.h" #include "cascoda-bm/cascoda_types.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/** - * @brief define what happens when receiving a post mesage on a URL - * - * @param url the URL of the message as a string - */ -void post_callback(char *url); + /** + * @brief define what happens when receiving a post mesage on a URL + * + * @param url the URL of the message as a string + */ + void post_callback(char *url); -/** - * @brief any hardware specific setup e.g. registering board LEDs and buttons - * - */ -void hardware_init(); + /** + * @brief any hardware specific setup e.g. registering board LEDs and buttons + * + */ + void hardware_init(); -/** - * @brief any hardware independent setup e.g. setting up graphics - * - */ -void logic_initialize(); + /** + * @brief any hardware independent setup e.g. setting up graphics + * + */ + void logic_initialize(); -/** - * @brief any hardware specific actions to be continually run e.g. checking buttons for input - * - */ -void hardware_poll(); + /** + * @brief any action to take as a result of the role changing, e.g. refreshing screen + * + */ + void logic_role_changed(); -/** - * @brief Application-specific handling of programming mode command received from linker - * - */ -void programming_mode_embedded(size_t device_index, bool programming_mode); + /** + * @brief any hardware specific actions to be continually run e.g. checking buttons for input + * + */ + void hardware_poll(); -/** - * @brief Application-specific handling of reset when received from linker - * - */ -void reset_embedded(size_t device_index, int reset_value, void *data); + /** + * @brief Application-specific handling of programming mode command received from linker + * + */ + void programming_mode_embedded(size_t device_index, bool programming_mode); + + /** + * @brief Application-specific handling of reset when received from linker + * + */ + void reset_embedded(size_t device_index, int reset_value, void *data); #ifdef __cplusplus } diff --git a/BATTLESHIP/readme.md b/BATTLESHIP/readme.md index 2c664da..f911943 100644 --- a/BATTLESHIP/readme.md +++ b/BATTLESHIP/readme.md @@ -45,9 +45,9 @@ The general structure of these programs are: - manufactorer : - model : KNX Battleships Demo eink -- hardware_type : Windows -- hardware version : [0, 1, 3] -- firmware version : [0, 1, 1] +- hardware_type : 000000000000 +- hardware version : [0, 4, 0] +- firmware version : [0, 4, 0] ### Data points diff --git a/EXAMPLE/CMakeLists.txt b/EXAMPLE/CMakeLists.txt index 3040042..f72601c 100644 --- a/EXAMPLE/CMakeLists.txt +++ b/EXAMPLE/CMakeLists.txt @@ -54,7 +54,7 @@ if(USE_GITLAB) set(KNX_IOT_STACK_REPO https://gitlab.knx.org/shared-projects/knx-iot-point-api-public-stack/ CACHE STRING "") else() set(KNX_REPO knx-iot-stack) - set(KNX_IOT_STACK_REPO https://github.com/KNX-IOT/KNX-IOT-STACK.git CACHE STRING "") + set(KNX_IOT_STACK_REPO https://github.com/cascoda/knx-iot-stack.git CACHE STRING "") endif() # KNX IoT Stack @@ -71,6 +71,7 @@ FetchContent_Declare( GIT_TAG ${CASCODA_SDK_REF} ) + if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL M2351) set(USE_CONSOLE OFF CACHE BOOL "use console (for output logging)") diff --git a/EXAMPLE/build_win_bin_ninja.sh b/EXAMPLE/build_win_bin_ninja.sh index 8fafa2b..0d9611b 100644 --- a/EXAMPLE/build_win_bin_ninja.sh +++ b/EXAMPLE/build_win_bin_ninja.sh @@ -40,7 +40,8 @@ mkdir -p build_win_bin cd build_win_bin # create the make files for Windows/Linux cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE=../arm_gcc_m2351.cmake -DOC_OSCORE_ENABLED=ON \ - -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF + -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF +#-DCASCODA_USE_PRIVATE_SDK=ON cmake . # build it ninja diff --git a/EXAMPLE/build_win_bin_ninja_ota.sh b/EXAMPLE/build_win_bin_ninja_ota.sh new file mode 100644 index 0000000..9755012 --- /dev/null +++ b/EXAMPLE/build_win_bin_ninja_ota.sh @@ -0,0 +1,47 @@ +# Copyright (c) 2022-2023 Cascoda Ltd +# All rights reserved. +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Cascoda Limited. +# integrated circuit in a product or a software update for such product, must +# reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific prior written +# permission. +# +# 4. This software, whether provided in binary or any other form must not be decompiled, +# disassembled, reverse engineered or otherwise modified. +# +# 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. +# +# THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# build script for creating a binary for the dev board +# on windows/linux using the ninja toolchain +echo "using Ninja" + +mkdir -p build_win_bin +cd build_win_bin +# create the make files for Windows/Linux +cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE=../arm_gcc_m2351.cmake -DOC_OSCORE_ENABLED=ON \ + -DOC_DNS_SD_ENABLED=ON -DOC_PRINT_ENABLED=OFF -DOC_PRINT_APP_ENABLED=OFF -DCASCODA_OTA_UPGRADE_ENABLED=1 +cmake . +# build it +ninja + diff --git a/EXAMPLE/knx_iot_example.c b/EXAMPLE/knx_iot_example.c index d6d5868..9c10f75 100644 --- a/EXAMPLE/knx_iot_example.c +++ b/EXAMPLE/knx_iot_example.c @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Ltd + Copyright (c) 2022-2024 Cascoda Ltd -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -116,6 +116,9 @@ static struct timespec ts; #endif #include /* defines FILENAME_MAX */ +#include +#include +#include #define STORE_PREFIX "app" #define MY_NAME "KNX Switching example" /**< The name of the application */ @@ -145,7 +148,6 @@ char g_serial_number[20] = "00FA10010710"; - volatile DPT_Switch gLED_1; /**< global variable for LED_1 */ volatile DPT_Switch gPB_1; /**< global variable for PB_1 */ volatile DPT_Switch gInfoOnOff_1; /**< global variable for InfoOnOff_1 */ @@ -153,29 +155,20 @@ volatile DPT_Switch gInfoOnOff_1; /**< global variable for InfoOnOff_1 */ volatile bool g_faultLED_1; /**< global variable for fault LED_1 */ - - void get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data); void -put_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data); - - -const char *const mdLED_1[3] = { +put_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_data);const char *const mdLED_1[3] = { "desc", "On/Off switch 1", NULL -}; -const char *const mdPB_1[3] = { +}; const char *const mdPB_1[3] = { "desc", "On/Off push button 1", NULL -}; -const char *const mdInfoOnOff_1[3] = { +}; const char *const mdInfoOnOff_1[3] = { "desc", "Feedback 1", NULL }; - -const size_t num_datapoints = 3; - +const size_t num_datapoints = 3; /** number of data points */ oc_resource_dummy_t app_resource_end = {NULL, -1}; oc_resource_data_t runtime_dataLED_1; @@ -189,8 +182,9 @@ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdiscarded-array-qualifiers\"") #endif + const datapoint_t g_datapoints[3] = { - /*[0] = */{ + /*[0] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[1].resource, /*device*/ 0, @@ -198,7 +192,7 @@ const datapoint_t g_datapoints[3] = { /*uri*/ oc_string_create_const("/p/o_1_1"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.417.61" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.switch"), - /*interfaces*/ OC_IF_A, + /*interfaces*/ OC_IF_D | OC_IF_A, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[0]}, @@ -221,7 +215,7 @@ const datapoint_t g_datapoints[3] = { /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[1] = */{ + /*[1] (saved=0)= */{ /* .resource=*/ { /*next*/ (oc_resource_t*)&g_datapoints[2].resource, /*device*/ 0, @@ -229,7 +223,7 @@ const datapoint_t g_datapoints[3] = { /*uri*/ oc_string_create_const("/p/o_2_2"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.421.61" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.switch"), - /*interfaces*/ OC_IF_S, + /*interfaces*/ OC_IF_D | OC_IF_S, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[1]}, @@ -252,7 +246,7 @@ const datapoint_t g_datapoints[3] = { /*.default_present =*/ false, /*.num_elements =*/ 0 }, - /*[2] = */{ + /*[2] (saved=0)= */{ /* .resource=*/ { /*next*/(oc_resource_t*)&app_resource_end, /*device*/ 0, @@ -260,7 +254,7 @@ const datapoint_t g_datapoints[3] = { /*uri*/ oc_string_create_const("/p/o_3_3"), /*types*/ oc_string_array_create_const(_ECHO, 1, "urn:knx:dpa.417.51" ), /*dpt*/ oc_string_create_const("urn:knx:dpt.switch"), - /*interfaces*/ OC_IF_S, + /*interfaces*/ OC_IF_D | OC_IF_S, /*content_type*/ APPLICATION_CBOR, /*properties*/ OC_DISCOVERABLE, /*get_handler*/ {get_generic, (void*)&g_datapoints[2]}, @@ -284,7 +278,18 @@ const datapoint_t g_datapoints[3] = { /*.num_elements =*/ 0 }, }; - + +/* + total saved parameters/datapoints = 0 + total group object entries = + total publisher entries = + total receiver entries = + total auth entries = + total SSN entries = + ---------------------------------------- + + total files = 100 +*/ + #if defined _MSC_VER && !defined __INTEL_COMPILER _Pragma("warning(default:4090)") @@ -299,7 +304,7 @@ typedef void (*app_set_default_value_fn)(const char*); typedef void (*app_set_variable_fn)(const char*, const void*); typedef void (*app_set_array_fn)(const char*, const void*, int n, bool store_persistently); typedef void (*app_set_array_elems_fn)(const char*, const void*, int start, int n, bool store_persistently); -typedef void (*oc_encode_fn)(const void*); +typedef void (*oc_encode_fn)(const void*, bool is_metadata); typedef void (*oc_encode_array_fn)(const void*, int n); typedef void (*oc_encode_array_fn)(const void*, int n); typedef bool (*oc_parse_fn)(oc_rep_t *rep, void *out); @@ -330,7 +335,7 @@ const struct datapoint_type_t g_datapoint_types[DatapointType_MAX_NUM] = { {sizeof(float)}, {sizeof(char*)}, { - sizeof(DPT_Switch), + sizeof(DPT_Switch), // DPT_Switch DPT_Switch (app_get_variable_fn)app_get_DPT_Switch_variable, (app_get_array_fn)app_get_DPT_Switch_array, (app_get_array_elems_fn)app_get_DPT_Switch_array_elems, @@ -376,7 +381,7 @@ static const char *const *get_datapoint_metadata(const datapoint_t *dp) { return dp->metadata; } -static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps) { +static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps, bool is_metadata) { size_t count = dp->num_elements; const struct datapoint_type_t *dpt = &g_datapoint_types[dp->type]; int n = ps; @@ -398,7 +403,7 @@ static bool oc_encode_datapoint(const datapoint_t *dp, int pn, int ps) { if (ps > 1) g_datapoint_types[dp->type].oc_encode_array(var, n); else - g_datapoint_types[dp->type].oc_encode(var); + g_datapoint_types[dp->type].oc_encode(var, is_metadata); free(var); return true; @@ -420,7 +425,7 @@ static bool oc_parse_datapoint(const datapoint_t *dp, oc_rep_t *rep, void *out, } } -static void datapoint_set(const datapoint_t *dp, void *in, int start, int n) +void datapoint_set(const datapoint_t *dp, void *in, int start, int n) { if (in == NULL) return; @@ -436,12 +441,21 @@ static void datapoint_set(const datapoint_t *dp, void *in, int start, int n) const datapoint_t *get_datapoint_by_url(const char *url) { //this can likely be optimised in the future for speed //with a binary search or similar + + bool is_param = false; + if (strncmp(url,"/p/p",4) == 0) { + is_param = true; + } + - for(int i = 0; i < num_datapoints; i++) { - const datapoint_t *it = &g_datapoints[i]; - const char *_url = get_datapoint_url(it); - if (strcmp(_url, url) == 0) { - return it; + if (is_param == false) { + for(int i = 0; i < num_datapoints; i++) { + const datapoint_t *it = &g_datapoints[i]; + const char *_url = get_datapoint_url(it); + // it always start with /p/ + if (strcmp((char *)&_url[3], (char *)&url[3]) == 0) { + return it; + } } } @@ -456,6 +470,41 @@ int app_get_url_array_size(const char *url) return dp->num_elements; } +bool get_module_url(char* out_url, const char* in_url, int module_index) +{ + int digits_after_underscore = 0; + size_t url_length = strlen(in_url); + size_t keep_len; + char temp[50]; + + // Find out how many more digits we have after the underscore + for (int i = url_length - 1; i >= 0; --i) + { + // We have reached the underscore, we can stop counting + if (*(in_url + i) == '_') + break; + + // We have counted one more digit after the underscore + if (isdigit(*(in_url + i))) + ++digits_after_underscore; + else // We have encountered a non-digit and non-underscore, which is unexpected. + return true; + } + + // We can now calculate the number of characters that we need to keep from the original URL + keep_len = url_length - digits_after_underscore; + + // Copy over those characters into a temp array + strncpy(temp, in_url, keep_len); + temp[keep_len] = '\0'; + + // Add the module number at the end, and set the out_url pointer + sprintf(temp + strlen(temp), "%d", module_index); + strcpy(out_url, temp); + + return false; +} + // Getters/Setters for DPT_Switch bool app_is_DPT_Switch_url(const char* url) @@ -574,7 +623,6 @@ bool oc_parse_DPT_Switch_single(oc_rep_t *rep, DPT_Switch *out) // todo Fix void bool ret = oc_rep_i_get_bool(rep, 1, (void*)out); - return ret; } @@ -619,14 +667,18 @@ void oc_encode_DPT_Switch_single(CborEncoder *parent, const DPT_Switch *in) cbor_encode_boolean(parent, (bool)*in); } -void oc_encode_DPT_Switch(const DPT_Switch *in) +void oc_encode_DPT_Switch(const DPT_Switch *in, bool is_metadata) { if (in == NULL) { return; } - oc_rep_begin_root_object(); + if (!is_metadata) { // Don't begin & end root object as it's already done during metadata handling + oc_rep_begin_root_object(); + } oc_encode_DPT_Switch_single(oc_rep_object(root), in); - oc_rep_end_root_object(); + if (!is_metadata) { + oc_rep_end_root_object(); + } } void oc_encode_DPT_Switch_array(const DPT_Switch *in, int n) @@ -654,13 +706,18 @@ void persistent_store_DPT_Switch_array(const char *name, const DPT_Switch *in, i long ret; const size_t max_size = 4 * n + 2; rep_buf = malloc(max_size); - char store_name[32] = STORE_PREFIX; +#ifdef OPTIMIZE_STORAGE +// use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else + char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); while(pos) { *pos = '_'; pos = strchr(store_name, '/'); } +#endif oc_rep_new(rep_buf, max_size); oc_encode_DPT_Switch_array(in, n); @@ -676,9 +733,9 @@ void persistent_store_DPT_Switch_array(const char *name, const DPT_Switch *in, i PRINT_APP("Error encoding DPT_Switch %s for storage\n", name); } - if (ret <= 0) + if (ret <= 0) { PRINT_APP("oc_storage_write failed with error: %d\n", -ret); - + } free(rep_buf); } @@ -692,6 +749,10 @@ bool persistent_load_DPT_Switch_array(const char *name, DPT_Switch *out, int n) oc_rep_t *rep = NULL; int max_size = 4 * n + 2; uint8_t *oc_storage_buf; +#ifdef OPTIMIZE_STORAGE + // use input as storage name with "/p/" prefix removed + const char* store_name = (char *)&name[3]; +#else char store_name[32] = STORE_PREFIX; strcat(store_name, name); char *pos = strchr(store_name, '/'); @@ -699,10 +760,12 @@ bool persistent_load_DPT_Switch_array(const char *name, DPT_Switch *out, int n) *pos = '_'; pos = strchr(store_name, '/'); } +#endif long ret; bool error = true; struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 }; oc_storage_buf = malloc(max_size); + struct oc_memb *prev_rep_obj = oc_rep_get_pool(); oc_rep_set_pool(&rep_objects); ret = oc_storage_read(store_name, oc_storage_buf, max_size); @@ -728,6 +791,7 @@ bool persistent_load_DPT_Switch_array(const char *name, DPT_Switch *out, int n) if(error) { oc_storage_erase(name); } + oc_rep_set_pool(prev_rep_obj); return !error; } @@ -750,8 +814,7 @@ int app_sscanf_DPT_Switch(DPT_Switch *in, char* text) const char s[2] = " "; char *token; - if (text == NULL) - { + if (text == NULL) { return 1; } memset(temp_string, 0, 300); @@ -773,19 +836,16 @@ int app_sscanf_DPT_Switch(DPT_Switch *in, char* text) int app_str_expected_DPT_Switch(int select, char* text) { - if (text == NULL) - { + if (text == NULL) { return 1; } - if (select == 1) - { + if (select == 1) { // bool strcat(text, " %d"); return 0; } - if (select == 2) - { + if (select == 2) { strcat(text, " 0"); return 0; } @@ -926,6 +986,11 @@ bool app_retrieve_fault_variable(const char* url) bool app_is_url_parameter(const char* url) { + // all parametes start with /p/pxxx + if (strncmp(url,"/p/p",4) == 0 ) { + return false; + } + return false; } @@ -1058,11 +1123,11 @@ oc_add_s_mode_response_cb(char *url, oc_rep_t *rep, oc_rep_t *rep_value) * */ void app_str_to_upper(char *str){ - while (*str != '\0') - { - *str = toupper(*str); - str++; - } + while (*str != '\0') + { + *str = toupper(*str); + str++; + } } /** @@ -1075,7 +1140,7 @@ void app_str_to_upper(char *str){ * - knx spec version * - hardware version : [0, 4, 0] * - firmware version : [0, 4, 0] - * - hardware type : 000001 + * - hardware type : 000000000000 * - device model : dev board example * */ @@ -1090,6 +1155,25 @@ app_init(void) oc_device_info_t *device = oc_core_get_device_info(0); +#ifdef LINUX + #define MAX_HOSTNAME 50 + char hostname[50]; + // set the hostname + ret = gethostname(&hostname[0], 50); + if (ret != -1) { + printf("Hostname (linux): %s\n", hostname); + oc_core_set_device_hostname(0, hostname); + } +#endif +#ifdef WIN32 + char hostname_str[50]; + int error = gethostname(hostname_str, 50); + if (error == 0) { + printf("Hostname (win32): %s\n", hostname_str); + oc_core_set_device_hostname(0, hostname_str); + } +#endif + /* set the hardware version 0.4.0 */ oc_core_set_device_hwv(0, 0, 4, 0); @@ -1099,12 +1183,10 @@ app_init(void) oc_core_set_device_fwv(0, 0, 4, 0); - /* manufactorer id */ + /* manufacturer id */ oc_core_set_device_mid(0, 0x00FA); - /* set the hardware type*/ - oc_core_set_device_hwt(0, "003030303030"); - + oc_core_set_device_hwt(0, "000000000000"); /* set the model */ oc_core_set_device_model(0, "dev board example"); @@ -1143,8 +1225,9 @@ bool request_query_get_int(oc_request_t *request, const char *query, int *out) char *query_str; int query_len = oc_get_query_value(request, query, &query_str); - if(query_len <= 0) + if(query_len <= 0) { return false; + } char buf[16]; strncpy(buf, query_str, query_len); *out = atoi(buf); @@ -1203,7 +1286,7 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da if ((strncmp(m, "value", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { m_valid = true; - if (oc_encode_datapoint(dp, pn, ps) == false) { + if (oc_encode_datapoint(dp, pn, ps, true) == false) { oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); goto done; } @@ -1228,7 +1311,7 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da if ((strncmp(m, "rt", m_len) == 0) | (strncmp(m, "*", m_len) == 0) ) { m_valid = true; - char *dpa = get_datapoint_dpa(dp); + const char *dpa = get_datapoint_dpa(dp); if (dpa) oc_rep_set_text_string(root, rt, &dpa[7]); } @@ -1255,10 +1338,10 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da m_valid = true; int index = oc_core_find_group_object_table_url(oc_string(request->resource->uri)); if (index > -1) { - oc_group_object_table_t* got_table_entry = oc_core_get_group_object_table_entry(index); - if (got_table_entry) { + oc_group_object_table_t* got_table_entry = oc_core_get_group_object_table_entry(index); + if (got_table_entry) { oc_rep_set_int_array(root, ga, got_table_entry->ga, got_table_entry->ga_len); - } + } } } for (const char *const *md = get_datapoint_metadata(dp); md && *md; md+=2) { @@ -1283,7 +1366,7 @@ get_generic(oc_request_t *request, oc_interface_mask_t interfaces, void *user_da oc_send_cbor_response(request, OC_STATUS_OK); goto done; } - if (oc_encode_datapoint(dp, pn, ps) == false) { + if (oc_encode_datapoint(dp, pn, ps, false) == false) { oc_send_response_no_format(request, OC_STATUS_INTERNAL_SERVER_ERROR); goto done; } @@ -1335,19 +1418,24 @@ put_generic(oc_request_t *request, oc_interface_mask_t interfaces, error_state = !oc_parse_datapoint(dp, rep, new_value, ps); if (error_state == false){ - oc_send_response_no_format(request, OC_STATUS_CHANGED); - datapoint_set(dp, new_value, pn*ps, ps); - if (dp->feedback_url) { - //check types match - const datapoint_t *feedback = get_datapoint_by_url(dp->feedback_url); - if (feedback->type == dp->type && feedback->num_elements == dp->num_elements) { - datapoint_set(feedback, new_value, pn*ps, ps); - PRINT(" Send status to '%s' with flag: 'w'\n", get_datapoint_url(feedback)); - oc_do_s_mode_with_scope(5, dp->feedback_url, "w"); - } + const oc_resource_t *my_resource = + oc_ri_get_app_resource_by_uri(request->uri_path, strlen(request->uri_path), 0); + if (my_resource != NULL) + oc_notify_observers(my_resource); + + oc_send_response_no_format(request, OC_STATUS_CHANGED); + datapoint_set(dp, new_value, pn*ps, ps); + if (dp->feedback_url) { + //check types match + const datapoint_t *feedback = get_datapoint_by_url(dp->feedback_url); + if (feedback->type == dp->type && feedback->num_elements == dp->num_elements) { + datapoint_set(feedback, new_value, pn*ps, ps); + PRINT(" Send status to '%s' with flag: 'w'\n", get_datapoint_url(feedback)); + oc_do_s_mode_with_scope(5, dp->feedback_url, "w"); } + } - do_put_cb(get_datapoint_url(dp)); + do_put_cb(get_datapoint_url(dp)); } else { /* request data was not recognized, so it was a bad request */ oc_send_response_no_format(request, OC_STATUS_BAD_REQUEST); @@ -1385,6 +1473,36 @@ register_resources(void) oc_ri_add_resource_block(&g_datapoints[0].resource); } +#ifdef MQTT_PROXY +void +configure_mqtt_from_parameters() { + strncpy(g_mqttconf_server, gMQTT_hostname0, sizeof(g_mqttconf_server)); + g_mqttconf_port = gMQTT_port_number0; + strncpy(g_mqttconf_username, gMQTT_username0, sizeof(g_mqttconf_username)); + strncpy(g_mqttconf_pwd, gMQTT_password0, sizeof(g_mqttconf_pwd)); + strncpy(g_iid_name, gIID_name0, sizeof(g_iid_name)); + PRINT("MQTT configuration:\n"); + PRINT(" hostname: %s\n", g_mqttconf_server); + PRINT(" port : %d\n", g_mqttconf_port); + PRINT(" username: %s\n", g_mqttconf_username); + PRINT(" password: %s\n", g_mqttconf_pwd); + PRINT(" IID name: %s\n", g_iid_name); +} +#endif + +/** + * @brief callback when a/lsm state changes + * @param device_index the device identifier of the list of devices + * @param current_state current state (i.e. after the change) + * @param data the supplied data. + */ +void +lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data) +{ + (void)device_index; + (void)data; +} + /** * @brief initiate preset for device * current implementation: device reset as command line argument @@ -1530,6 +1648,9 @@ reset_variables(void) if (it->persistent) { oc_storage_erase(get_datapoint_url(it)); } + if (it->default_present){ + g_datapoint_types[it->type].app_set_default_value(get_datapoint_url(it)); + } } } @@ -1580,6 +1701,7 @@ int app_initialize_stack() /* set the application callbacks */ oc_set_hostname_cb(hostname_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if defined WIN32 || defined __linux__ oc_set_swu_cb(swu_cb, (void *)fname); @@ -1678,7 +1800,11 @@ print_usage() PRINT("reset : does an full reset of the device\n"); PRINT("-s : sets the serial number of the device\n"); #ifdef MQTT_PROXY + PRINT("MQTT proxy configurations (only used before ETS download):\n"); PRINT("-host : sets the MQTT broker hostname\n"); + PRINT("-port : sets the MQTT broker port number\n"); + PRINT("-username : sets the MQTT username\n"); + PRINT("-pwd : sets the MQTT password\n"); #endif exit(0); } @@ -1721,6 +1847,14 @@ main(int argc, char *argv[]) sa.sa_handler = handle_signal; /* install Ctrl-C */ sigaction(SIGINT, &sa, NULL); + #define MAX_HOSTNAME 256 + char hostname[MAX_HOSTNAME]; + // set the hostname + ret = gethostname(&hostname[0], MAX_HOSTNAME); + if (ret != -1) { + printf("Hostname: %s\n", hostname); + oc_core_set_device_hostname(0, hostname, MAX_HOSTNAME); + } #endif /* __linux__ */ for (int i = 0; i < argc; i++) { @@ -1751,6 +1885,33 @@ main(int argc, char *argv[]) PRINT("ERROR: \"-host\" flag detected, but no hostname provided!\n"); } } + if (strcmp(argv[i], "-port") == 0) { + if (i + 1 < argc) { + // port number for MQTT proxy server + PRINT("MQTT proxy port number %s\n", argv[i + 1]); + g_mqttconf_port = atoi(argv[i + 1]); + } else { + PRINT("ERROR: \"-port\" flag detected, but no port number provided!\n"); + } + } + if (strcmp(argv[i], "-username") == 0) { + if (i + 1 < argc) { + // username for MQTT proxy server + PRINT("MQTT proxy username %s\n", argv[i + 1]); + strncpy(g_mqttconf_username, argv[i + 1], sizeof(g_mqttconf_username)); + } else { + PRINT("ERROR: \"-username\" flag detected, but no username provided!\n"); + } + } + if (strcmp(argv[i], "-pwd") == 0) { + if (i + 1 < argc) { + // password for MQTT proxy server + PRINT("MQTT proxy pwd %s\n", argv[i + 1]); + strncpy(g_mqttconf_pwd, argv[i + 1], sizeof(g_mqttconf_pwd)); + } else { + PRINT("ERROR: \"-pwd\" flag detected, but no password provided!\n"); + } + } #endif } diff --git a/EXAMPLE/knx_iot_example.h b/EXAMPLE/knx_iot_example.h index 3ae83a8..a38576f 100644 --- a/EXAMPLE/knx_iot_example.h +++ b/EXAMPLE/knx_iot_example.h @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Ltd + Copyright (c) 2022-2024 Cascoda Ltd -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -61,12 +61,12 @@ extern "C" { #define PACKED //!< Helper macro to create smaller packed structs #endif -#define THIS_DEVICE 0 +#define THIS_DEVICE 0 //!< single device instance, e.g. number 0 // URL defines -#define URL_LED_1 "/p/o_1_1" //!< URL define for LED_1 -#define URL_PB_1 "/p/o_2_2" //!< URL define for PB_1 -#define URL_INFOONOFF_1 "/p/o_3_3" //!< URL define for InfoOnOff_1 +#define URL_LED_1 "/p/o_1_1" //!< URL "LED_1" desc:"" +#define URL_PB_1 "/p/o_2_2" //!< URL "PB_1" desc:"" +#define URL_INFOONOFF_1 "/p/o_3_3" //!< URL "InfoOnOff_1" desc:"" typedef enum DatapointType{ DatapointType_bool, @@ -89,17 +89,29 @@ typedef struct datapoint_t { int num_elements; } datapoint_t; +/* all data points */ extern const datapoint_t g_datapoints[]; extern const size_t num_datapoints; - + + /** * @brief Returns the datapoint for the given URL * * @param url URL of the datapoint */ -const datapoint_t *get_datapoint_by_url(const char *url); +const datapoint_t *get_datapoint_by_url(const char *url); + +/** + * @brief Returns the url of the module by instance + * + * @param out_url URL of the datapoint given back (e.g. with the corrected post fix) + * @param in_url URL of the datapoint of the module + * @param module_index URL the module index + * @return false if operation successful, true if something went wrong + */ +bool get_module_url(char* out_url, const char* in_url, int module_index); ///@defgroup DPT_Switch ///@ingroup DPT_Switch @@ -488,6 +500,7 @@ bool oc_parse_DPT_Switch_array(oc_rep_t *rep, DPT_Switch *out, int n); * @brief Encode a DPT_Switch using oc_rep * * @param[in] in The DPT_Switch to encode + * @param[is_metadata] is_metadata Whether function is called during metadata query handling * * ~~~{.c} * DPT_Switch my_var; @@ -500,7 +513,7 @@ bool oc_parse_DPT_Switch_array(oc_rep_t *rep, DPT_Switch *out, int n); * uint8_t buf = oc_rep_get_encoder_buf(); * ~~~ */ -void oc_encode_DPT_Switch(const DPT_Switch *in); +void oc_encode_DPT_Switch(const DPT_Switch *in, bool is_metadata); /** * @ingroup DPT_Switch diff --git a/EXAMPLE/knx_iot_example_dev.c b/EXAMPLE/knx_iot_example_dev.c index 15c66c8..71064fd 100644 --- a/EXAMPLE/knx_iot_example_dev.c +++ b/EXAMPLE/knx_iot_example_dev.c @@ -72,6 +72,7 @@ #include "cascoda-util/cascoda_time.h" #include "cascoda-bm/cascoda_wait.h" #include "ca821x_error.h" +#include "sed_poll.h" #include #include @@ -83,6 +84,7 @@ #ifdef SLEEPY #include "knx_iot_sleepy_main_extern.h" +#include "knx_iot_sleepy_main.h" #else #include "knx_iot_wakeful_main_extern.h" #endif @@ -102,6 +104,9 @@ #ifdef SLEEPY #include "sed_poll.h" #endif + +// forward declaration. +void dev_put_callback(const char* url); // // code for programming mode and reset // @@ -277,6 +282,8 @@ static void reset_hold_cb(void *context) oc_device_info_t* device = oc_core_get_device_info(0); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); + + break; case THREAD_RESET: @@ -391,6 +398,17 @@ static ca_error programming_mode_handler(void *context) TASKLET_ScheduleDelta(&g_programming_mode_handler, PROGRAMMING_MODE_INDICATOR_FLASHING_PERIOD_MS / 2, NULL); } +static ca_error become_sleepy(void *ctx) +{ + otLinkModeConfig linkMode = {0}; + otThreadSetLinkMode(OT_INSTANCE, linkMode); + // Poll after the child tells its parent it is a SED once more + SED_PollSoon(); + return CA_ERROR_SUCCESS; +} + +static ca_tasklet become_sleepy_tasklet; + /** * @brief exit the programming mode * e.g. stop flickering the LED @@ -399,11 +417,6 @@ static ca_error programming_mode_handler(void *context) */ static void exit_programming_mode(size_t device_index) { -#ifdef SLEEPY - SED_SetMinDelay(1500); - SED_SetMaxDelay(SED_POLL_PERIOD); - knx_service_sleep_period(SED_POLL_PERIOD); -#endif oc_knx_device_set_programming_mode(device_index, false); TASKLET_Cancel(&g_programming_mode_handler); @@ -413,6 +426,11 @@ static void exit_programming_mode(size_t device_index) oc_device_info_t* device = oc_core_get_device_info(0); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); +#ifdef SLEEPY + // Devices remain awake for 20 seconds after PM mode is disabled, to ensure programming + // is fast and reliable + TASKLET_ScheduleDelta(&become_sleepy_tasklet, 20 * 1000, NULL); +#endif } /** @@ -425,10 +443,10 @@ static void enter_programming_mode(size_t device_index) { #ifdef SLEEPY // When in programming mode, devices must respond within 2 seconds - SED_SetMinDelay(250); - SED_SetMaxDelay(1500); - knx_service_sleep_period(SED_POLL_PERIOD); - SED_PollNow(); + otLinkModeConfig linkMode = {0}; + linkMode.mRxOnWhenIdle = 1; + otThreadSetLinkMode(OT_INSTANCE, linkMode); + TASKLET_Cancel(&become_sleepy_tasklet); #endif oc_knx_device_set_programming_mode(device_index, true); TASKLET_ScheduleDelta(&g_programming_mode_handler, SCHEDULE_NOW, NULL); @@ -447,6 +465,11 @@ static void prog_mode_short_press_cb(void *context) (void)context; PRINT_APP("=== prog_mode_short_press_cb()\n"); + // if the device is not initialized, the check for PM always returns false + // and the PM indicator cannot be turned off, so we return early + if (otThreadGetDeviceRole(OT_INSTANCE) <= OT_DEVICE_ROLE_DETACHED) + return; + // If in programming mode, exit. Otherwise enter. if (oc_knx_device_in_programming_mode(THIS_DEVICE)) exit_programming_mode(THIS_DEVICE); @@ -586,7 +609,9 @@ return DVBD_CanSleep(); // Sleepy Device void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent) { - uint32_t taskletTimeLeft = SED_POLL_PERIOD; + // 20 min wakeup if no tasklet is scheduled (should not happen) + uint32_t taskletTimeLeft = 20 * 60 * 1000; + /* schedule wakeup */ TASKLET_GetTimeToNext(&taskletTimeLeft); @@ -613,6 +638,7 @@ void hardware_reinitialise(void) } #endif // SLEEPY + /** * @brief do the hardware installation * @@ -623,15 +649,11 @@ void hardware_init() /* set the put callback on the underlaying code */ app_set_put_cb(dev_put_callback); //app_set_get_cb(dev_get_callback); -#ifdef SLEEPY - // Initialize sleepy timeout handler - SED_InitPolling(250, SED_POLL_PERIOD); #ifdef SLEEPY_USE_LED // Debug: blink programming mode indicator on wakeup // hardcoded pin # of sensor board pm mode led BSP_ModuleRegisterGPIOOutputOD(36, MODULE_PIN_TYPE_LED); BSP_ModuleSetGPIOPin(36, LED_ON); -#endif #endif /* Initialize knx-specific development board functionality */ knx_specific_init(); @@ -641,9 +663,6 @@ void hardware_init() DVBD_RegisterLEDOutput(DEV_SWITCH_2, JUMPER_POS_1); - - - @@ -652,7 +671,10 @@ DVBD_RegisterLEDOutput(DEV_SWITCH_2, JUMPER_POS_1); actuator_test_init(); #endif - +#ifdef SLEEPY + // default sleepy polling: poll every 10 minutes + SED_InitPolling(1500, 10 * 60 * 1000, 0); +#endif } void setLED(led_t led, bool value) @@ -671,29 +693,11 @@ void setLED(led_t led, bool value) void hardware_poll() { #ifndef EXCLUDE_CASCODA_BAREMETAL - // Needs to be here for the programming mode button - // Calling this twice in a row should be safe, as there is edge - // detection and it is called in a loop anyways - - - DVBD_PollButtons(); - - -#endif /* EXCLUDE_CASCODA_BAREMETAL */ DVBD_PollButtons(); - - - - - + -#ifndef EXCLUDE_CASCODA_BAREMETAL - // Add a delay so we don't poll too fast - // as this affects the brightness of the - // shared LEDs - WAIT_ms(3); #endif /* EXCLUDE_CASCODA_BAREMETAL */ } @@ -713,7 +717,7 @@ bool app_is_url_in_use(const char* url) } } return false; -} +} #ifdef ACTUATOR_TEST_MODE /* code for actuator testing */ diff --git a/EXAMPLE/knx_iot_example_gui.cpp b/EXAMPLE/knx_iot_example_gui.cpp index 3085f11..4187d3a 100644 --- a/EXAMPLE/knx_iot_example_gui.cpp +++ b/EXAMPLE/knx_iot_example_gui.cpp @@ -36,7 +36,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ -// 2024-02-12 12:32:52.379436 +// 2024-06-17 16:16:29.293447 // For compilers that support precompilation, includes "wx/wx.h". #include @@ -46,6 +46,10 @@ #endif #include +#ifdef WIN32 +#include +#endif + #define NO_MAIN #include "knx_iot_example.h" #include "api/oc_knx_dev.h" @@ -285,7 +289,8 @@ class MyFrame : public wxFrame void updateInfoButtons(); void updateTextButtons(); void bool2text(bool on_off, char* text); - void int2text(int value, char* text, bool as_ets=false); + void int2text(int value, char* text); + void int2gatext(uint32_t value, char* text, bool as_ets=false); void int2grpidtext(uint64_t value, char* text, bool as_ets); void int2scopetext(uint32_t value, char* text); void double2text(double value, char* text); @@ -347,7 +352,6 @@ MyFrame::MyFrame(char* str_serial_number) m_menuFile->Append(GOT_TABLE_ID, "List Group Object Table", "List the Group object table", false); m_menuFile->Append(PUB_TABLE_ID, "List Publisher Table", "List the Publisher table", false); m_menuFile->Append(REC_TABLE_ID, "List Recipient Table", "List the Recipient table", false); - m_menuFile->Append(PARAMETER_LIST_ID, "List Parameters", "List the parameters of the device", false); m_menuFile->Append(AT_TABLE_ID, "List Auth/AT Table", "List the security data of the device", false); m_menuFile->Append(CHECK_PM, "Programming Mode", "Sets the application in programming mode", true); m_menuFile->Append(RESET_TABLE, "Reset (7) (Tables)", "Reset 7 (Reset to default without IA).", false); @@ -393,7 +397,7 @@ MyFrame::MyFrame(char* str_serial_number) Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); int x_width = 350; /* width of the widgets */ int x_height = 25; /* height of the widgets */ - int max_instances = 1; + int max_instances = 1; // might not work with modules int max_dp_count = 3; int border_size = 1; wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); @@ -468,10 +472,20 @@ MyFrame::MyFrame(char* str_serial_number) //m_ls_text->SetToolTip("LoadState, 'loaded' means in running state"); gridsizer->Add(m_ls_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); // host name +#ifdef WIN32 + + char hostname_str[50]; + int error = gethostname(hostname_str, 50); + oc_core_set_device_hostname(0, hostname_str); +#endif sprintf(text, "host name: %s", oc_string(device->hostname)); m_hostname_text = new wxTextCtrl(this, LS_TEXT, text, wxPoint(0, 0), wxSize(x_width, x_height)); m_hostname_text->SetEditable(false); - m_hostname_text->SetToolTip("Hostname not supported on windows"); +#ifdef WIN32 +m_hostname_text->SetToolTip("Hostname of the PC"); +#else + m_hostname_text->SetToolTip("Hostname not supported"); +#endif gridsizer->Add(m_hostname_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(border_size)); if (app_is_secure()) { strcpy(text, app_get_password()); @@ -656,7 +670,7 @@ void MyFrame::OnGroupObjectTable(wxCommandEvent& event) strcat(text, line); strcpy(line," ga : ["); for (int i = 0; i < entry->ga_len; i++) { - this->int2text(entry->ga[i], line, ga_conversion); + this->int2gatext(entry->ga[i], line, ga_conversion); } strcat(line," ]\n"); strcat(text, line); @@ -732,7 +746,7 @@ void MyFrame::OnPublisherTable(wxCommandEvent& event) if ( entry->ga_len > 0) { strcpy(line," ga : ["); for (int i = 0; i < entry->ga_len; i++) { - this->int2text(entry->ga[i], line, ga_conversion); + this->int2gatext(entry->ga[i], line, ga_conversion); } strcat(line," ]\n"); strcat(text, line); @@ -808,11 +822,13 @@ void MyFrame::OnRecipientTable(wxCommandEvent& event) if ( entry->ga_len > 0) { strcpy(line," ga : ["); for (int i = 0; i < entry->ga_len; i++) { - this->int2text(entry->ga[i], line, ga_conversion); + this->int2gatext(entry->ga[i], line, ga_conversion); } strcat(line," ]\n"); strcat(text, line); } + sprintf(line, " non: %d mt: %d\n", entry->non, entry->mt); + strcat(text, line); } } strcpy(windowtext, "Recipient Table "); @@ -973,6 +989,18 @@ void MyFrame::OnAuthTable(wxCommandEvent& event) sprintf(line, "\n"); strcat(text, line); } + if (oc_byte_string_len(my_entry->osc_salt) > 0) { + sprintf(line, " osc_salt [%d]: ",(int)oc_byte_string_len(my_entry->osc_salt)); + strcat(text, line); + int length = (int)oc_byte_string_len(my_entry->osc_salt); + char* salt = oc_string(my_entry->osc_salt); + for (int i = 0; i < length; i++) { + sprintf(line, "%02x", (unsigned char)salt[i]); + strcat(text, line); + } + sprintf(line, "\n"); + strcat(text, line); + } if (oc_byte_string_len(my_entry->osc_contextid) > 0) { sprintf(line, " osc_contextid (o)[%d]: ", (int)oc_byte_string_len(my_entry->osc_contextid)); strcat(text, line); @@ -993,7 +1021,7 @@ void MyFrame::OnAuthTable(wxCommandEvent& event) sprintf(line, " osc_ga : ["); strcat(text, line); for (int i = 0; i < my_entry->ga_len; i++) { - this->int2text(my_entry->ga[i], text, ga_conversion); + this->int2gatext(my_entry->ga[i], text, ga_conversion); } sprintf(line, " ]\n"); strcat(text, line); @@ -1029,7 +1057,7 @@ void MyFrame::OnAbout(wxCommandEvent& event) strcat(text,"\n"); strcat(text,"manufacturer : cascoda\n"); strcat(text,"model : dev board example\n"); - strcat(text,"hardware type : 000001\n"); + strcat(text,"hardware type : 000000000000\n"); strcat(text,"hardware version : [0, 4, 0]\n"); strcat(text,"firmware version : [0, 4, 0]\n\n"); @@ -1040,7 +1068,7 @@ void MyFrame::OnAbout(wxCommandEvent& event) strcat(text, "\n"); strcat(text, "(c) Cascoda Ltd\n"); - strcat(text, "2024-02-12 12:32:52.379436"); + strcat(text, "2024-06-17 16:16:29.293447"); CustomDialog("About", text); } @@ -1120,11 +1148,25 @@ void MyFrame::bool2text(bool on_off, char* text) /** * @brief convert the integer to text for display * - * @param on_off the integer - * @param text the text to add info too + * @param value the integer + * @param text the text to add info to + */ +void MyFrame::int2text(int value, char* text) +{ + char value_text[50]; + + sprintf(value_text, " %d", value); + strcat(text, value_text); +} + +/** + * @brief convert the group address to text for display + * + * @param value the integer + * @param text the text to add info to * @param as_ets the text as terminology as used in ets */ -void MyFrame::int2text(int value, char* text, bool as_ets) +void MyFrame::int2gatext(uint32_t value, char* text, bool as_ets) { char value_text[50]; @@ -1143,10 +1185,10 @@ void MyFrame::int2text(int value, char* text, bool as_ets) uint32_t ga_main = (ga >> 11); uint32_t ga_middle = (ga >> 8) & 0x7; uint32_t ga_sub = (ga & 0x000000FF); - sprintf(value_text, " %d/%d/%d", ga_main, ga_middle, ga_sub); + sprintf(value_text, " %lu/%lu/%lu", ga_main, ga_middle, ga_sub); strcat(text, value_text); } else { - sprintf(value_text, " %d", value); + sprintf(value_text, " %lu", value); strcat(text, value_text); } } diff --git a/EXAMPLE/knx_iot_example_logic.c b/EXAMPLE/knx_iot_example_logic.c index c3a65ae..64a69c6 100644 --- a/EXAMPLE/knx_iot_example_logic.c +++ b/EXAMPLE/knx_iot_example_logic.c @@ -37,6 +37,7 @@ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ +#include #include "knx_iot_example.h" #include "cascoda-util/cascoda_tasklet.h" #include "oc_core_res.h" @@ -47,12 +48,17 @@ #include "oc_knx.h" #include "port/dns-sd.h" #include "openthread/thread.h" +#include "openthread/ping_sender.h" +#include "platform.h" +#include "manufacturer_storage.h" #define SCHEDULE_NOW 0 +uint8_t user_interaction_occurred = 0; + /////////////////////////////////////////////////////////////////////////////// // BUTTON Functions // @@ -67,6 +73,14 @@ void logic_initialize() { - } + +void logic_role_changed() +{ + +} + +bool logic_is_role_screen() +{ +} diff --git a/EXAMPLE/knx_iot_sleepy_main.c b/EXAMPLE/knx_iot_sleepy_main.c index 9cdf392..8e1c4d3 100644 --- a/EXAMPLE/knx_iot_sleepy_main.c +++ b/EXAMPLE/knx_iot_sleepy_main.c @@ -57,6 +57,7 @@ #include "cascoda-bm/cascoda_wait.h" #include "knx_iot_sleepy_main_extern.h" +#include "knx_iot_sleepy_main.h" #include "api/oc_knx_dev.h" #include "api/oc_knx_fp.h" @@ -91,6 +92,7 @@ void exit(int e) } otInstance *OT_INSTANCE; +extern uint8_t user_interaction_occurred; // Defer publishing of the service until you have the correct IP address // to advertise void knx_srp_add_service(void); @@ -98,6 +100,7 @@ void knx_srp_add_service(void); // To be implemented by the application void register_resources(void); void factory_presets_cb(size_t device_index, void *data); +void lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data); void hostname_cb(size_t device_index, oc_string_t host_name, void *data); int app_set_serial_number(char *serial_number); int app_init(void); @@ -113,6 +116,7 @@ void swu_cb(size_t device_index, oc_separate_response_t *response, size_t binary */ /** + /** * Handle application specific commands. */ @@ -166,6 +170,8 @@ static void ot_state_changed(uint32_t flags, void *context) SNTP_Update(); } #endif /* USE_SNTP */ + + logic_role_changed(); } static void signal_event_loop(void) @@ -217,6 +223,66 @@ static void sleep_if_possible(struct ca821x_dev *pDeviceRef, oc_clock_time_t tim hardware_sleep(pDeviceRef, nextAppEvent); } + +// Skip Thread check, this function is used to allow sleeping before the device +// is fully commissioned to a Thread network +static void sleep_if_possible_no_thread(struct ca821x_dev *pDeviceRef, uint64_t timeToNextAppEvent) +{ + uint32_t nextAppEvent = (uint32_t)timeToNextAppEvent; + + if (timeToNextAppEvent > 0x7FFFFFFF || !timeToNextAppEvent) + nextAppEvent = 0x7FFFFFFF; + + /* check for hardware (application-specific) */ + if (!hardware_can_sleep()) + return; + + hardware_sleep(pDeviceRef, nextAppEvent); +} + +// Variables used for the resynch mechanism +static int g_resynch_context; +static ca_tasklet g_resynch_tasklet; + +static ca_error child_resynch_handler(void *context) +{ + g_resynch_context = *((int *)context); + ca_error err = otThreadSleepyChildResynchronize(OT_INSTANCE); + TASKLET_ScheduleDelta(&g_resynch_tasklet, g_resynch_context * 1000, &g_resynch_context); + return err; +} + +void main_SetThreadChildTimeout(int seconds) +{ + otThreadSetChildTimeout(OT_INSTANCE, seconds); +} + +void swu_start_update_cb_imp(size_t device_index, uint32_t start_time, void *data) +{ + (void)data; + ca_error status = CA_ERROR_FAIL; + PRINT_APP("swu_start_update_cb_imp(), device: %d, applying new sofware in %d\n", device_index, start_time); +#if CASCODA_OTA_UPGRADE_ENABLED + status = ota_start_upgrade(start_time); + PRINT_APP("swu_start_update_cb_imp(), status = %d\n",status); +#endif +} + +void main_KickOffChildResynchMechanism(int seconds) +{ + static uint8_t s_first_call = true; + g_resynch_context = seconds; + + if (s_first_call) + TASKLET_Init(&g_resynch_tasklet, &child_resynch_handler); + else if (TASKLET_IsQueued(&g_resynch_tasklet)) + TASKLET_Cancel(&g_resynch_tasklet); + + TASKLET_ScheduleDelta(&g_resynch_tasklet, seconds * 1000, &g_resynch_context); + + s_first_call = false; +} + /** * main application. * intializes the global variables @@ -274,18 +340,47 @@ int main(void) ota_upgrade_init(); #endif - // A backoff mechanism for joining the network + // A backoff/sleep mechanism for joining the network + enum backoff_and_sleep_timings + { + BACKOFF_MIN = 6 * 1000, // Start at 6 seconds + BACKOFF_MAX = 120 * 1000, // Increase till 2 minutes is reached + BACKOFF_INCREMENT = 6 * 1000, // Increase in intervals of 6 seconds + POST_DISCOVERY_AWAKE_TIME = 2 * 1000, // After sending a discovery request, stay awake for 2 seconds + }; + + u32_t curr_backoff_time = BACKOFF_MIN; u32_t joinCooldownTimer = 0; // Try to join network do { - cascoda_io_handler(&dev); - hardware_poll(); + cascoda_io_handler(&dev); // Allows the device to do IO communications + hardware_poll(); // Allows the device to poll the hardware, e.g. sensor measurements + + if (user_interaction_occurred) + { + user_interaction_occurred = 0; + curr_backoff_time = BACKOFF_MIN; + } + + if (joinCooldownTimer >= POST_DISCOVERY_AWAKE_TIME) + { + uint64_t sleep_time = curr_backoff_time - POST_DISCOVERY_AWAKE_TIME; + sleep_if_possible_no_thread(&dev, sleep_time); + joinCooldownTimer += sleep_time; + } // If the timer has expired, try to join the network - if (joinCooldownTimer == 6000) + if (joinCooldownTimer >= curr_backoff_time) { + if (!logic_is_role_screen()) + { + curr_backoff_time += BACKOFF_INCREMENT; + if (curr_backoff_time >= BACKOFF_MAX) + curr_backoff_time = BACKOFF_MAX; + } + printf("Trying to join Thread network...\n"); // if the details aren't present, initialise with values generated @@ -295,7 +390,7 @@ int main(void) // Print the joiner credentials, delaying for up to 1 second PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); - otErr = PlatformTryJoin(&dev, OT_INSTANCE); + otErr = PlatformTryJoinWithCustomPoll(&dev, OT_INSTANCE, &hardware_poll); if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) break; joinCooldownTimer = 0; @@ -305,14 +400,14 @@ int main(void) // Print the joiner credentials, delaying for up to 1 second PlatformPrintJoinerCredentialsWithPskd(&dev, OT_INSTANCE, 0, thread_pw); - otErr = PlatformTryJoinWithPskd(&dev, OT_INSTANCE, thread_pw); + otErr = PlatformTryJoinWithPskdWithCustomPoll(&dev, OT_INSTANCE, thread_pw, &hardware_poll); if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) break; joinCooldownTimer = 0; } } - joinCooldownTimer += 1; + ++joinCooldownTimer; WAIT_ms(1); } while (1); @@ -347,19 +442,18 @@ int main(void) oc_storage_config("./knx_iot_creds"); - /* configure the serial number */ uint8_t sn[6]; + /* configure the serial number. must be done before stack initialization */ error = knx_get_stored_serial_number(sn); if (error) { PRINT_APP("ERROR: Unique serial number not found! Using default value...\n"); PRINT_APP( "Please create the data file using knx-gen-data and flash it with chilictl in order to fix this issue.\n"); - } - else - { + } else { // turn binary to hexadecimal char serial_number_str[13]; + // serial number in upper case snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X", @@ -371,7 +465,6 @@ int main(void) sn[5]); app_set_serial_number(serial_number_str); } - #ifdef OC_SPAKE char pwd[33]; error = knx_get_stored_password(pwd); @@ -411,14 +504,28 @@ int main(void) oc_set_hostname_cb(hostname_cb, NULL); oc_set_reset_cb(reset_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if CASCODA_OTA_UPGRADE_ENABLED oc_set_swu_cb(swu_cb, (void *)"image_name"); + oc_set_swu_startupdate_cb(swu_start_update_cb_imp, (void *)"image_name"); #endif + oc_set_programming_mode_cb(prog_mode_cb, NULL); /* start the stack */ init = oc_main_init(&handler); + // configure the hostname. + // note: this method sets the SN part to uppercase, as that is how + // the serial number is stored in the oc_device_info_t + oc_device_info_t *device = oc_core_get_device_info(0); + char hostname_str[50]; + memset(hostname_str, 0, 49); + strcat(hostname_str, "knx-"); + strcat(hostname_str, oc_string(device->serialnumber)); + strcat(hostname_str, ".local"); + oc_core_set_device_hostname(0, hostname_str); + oc_set_max_app_data_size(1024); oc_set_mtu_size(1232); @@ -428,8 +535,7 @@ int main(void) } // publish the MDNS service on startup - oc_device_info_t *device = oc_core_get_device_info(0); - knx_service_sleep_period(SED_POLL_PERIOD); + // knx_service_sleep_period(SED_POLL_PERIOD); knx_publish_service(oc_string(device->serialnumber), device->iid, device->ia, device->pm); PRINT("KNX IoT device, waiting on incoming connections.\n"); diff --git a/EXAMPLE/knx_iot_sleepy_main.h b/EXAMPLE/knx_iot_sleepy_main.h new file mode 100644 index 0000000..2b46c04 --- /dev/null +++ b/EXAMPLE/knx_iot_sleepy_main.h @@ -0,0 +1,64 @@ +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Copyright (c) 2022-2023 Cascoda Limited +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Cascoda Limited. + * integrated circuit in a product or a software update for such product, must + * reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Cascoda Limited nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * 4. This software, whether provided in binary or any other form must not be decompiled, + * disassembled, reverse engineered or otherwise modified. + * + * 5. This software, in whole or in part, must only be used with a Cascoda Limited circuit. + * + * THIS SOFTWARE IS PROVIDED BY CASCODA LIMITED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CASCODA LIMITED OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* calls that are implemented in sleepy main */ + +/** + * @brief sets the sleep time of the device (e.g. child) + * + * @param seconds, time in seconds + */ +void main_SetThreadChildTimeout(int seconds); + +/** + * @brief kicks off the resynch mechanism: sending of MLE Child Update Requests + * periodically to reattach to the parent in case the child was timed out + * + * @param seconds, time in seconds + */ +void main_KickOffChildResynchMechanism(int seconds); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/EXAMPLE/knx_iot_sleepy_main_extern.h b/EXAMPLE/knx_iot_sleepy_main_extern.h index 5f6a221..7fcb895 100644 --- a/EXAMPLE/knx_iot_sleepy_main_extern.h +++ b/EXAMPLE/knx_iot_sleepy_main_extern.h @@ -33,79 +33,90 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cascoda-bm/cascoda_interface.h" #include "cascoda-bm/cascoda_types.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/* for OpenThread: poll period for keep-alive */ -#define SED_POLL_PERIOD 60000 + /** + * @brief define what happens when receiving a post mesage on a URL + * + * @param url the URL of the message as a string + */ + void post_callback(char *url); -/** - * @brief define what happens when receiving a post mesage on a URL - * - * @param url the URL of the message as a string - */ -void post_callback(char *url); + /** + * @brief any hardware specific setup e.g. registering board LEDs and buttons + * + */ + void hardware_init(); -/** - * @brief any hardware specific setup e.g. registering board LEDs and buttons - * - */ -void hardware_init(); + /** + * @brief any hardware independent setup e.g. setting up graphics + * + */ + void logic_initialize(); -/** - * @brief any hardware independent setup e.g. setting up graphics - * - */ -void logic_initialize(); + /** + * @brief any action to take as a result of the role changing, e.g. refreshing screen + * + */ + void logic_role_changed(); -/** - * @brief any hardware specific actions to be continually run e.g. checking buttons for input - * - */ -void hardware_poll(); + /** + * @brief Whether or not the currently displayed screen is the ROLE screen + * + * @return true is on ROLE screen, false otherwise + */ + bool logic_is_role_screen(); -/** - * @brief Application-specific handling of programming mode command received from linker - * - */ -void programming_mode_embedded(size_t device_index, bool programming_mode); + /** + * @brief any hardware specific actions to be continually run e.g. checking buttons for input + * + */ + void hardware_poll(); -/** - * @brief Application-specific handling of reset when received from linker - * - */ -void reset_embedded(size_t device_index, int reset_value, void *data); + /** + * @brief Application-specific handling of programming mode command received from linker + * + */ + void programming_mode_embedded(size_t device_index, bool programming_mode); -/** - * @brief any hardware specific reinitialisation after wakeup from sleep - * - */ - void hardware_reinitialise(); + /** + * @brief Application-specific handling of reset when received from linker + * + */ + void reset_embedded(size_t device_index, int reset_value, void *data); -/** - * @brief hardware specific sleep (power-down) function - * - * @param pDeviceRef - Pointer to initialised ca821x_device_ref struct - * - * @param nextAppEvent - time to next application layer event - * - */ -void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent); + /** + * @brief any hardware specific reinitialisation after wakeup from sleep + * + */ + void hardware_reinitialise(); -/** - * @brief hardware specific check if device can sleep - * - * @return true if device is allowed to sleep, false otherwise - * - */ -bool hardware_can_sleep(); + /** + * @brief hardware specific sleep (power-down) function + * + * @param pDeviceRef - Pointer to initialised ca821x_device_ref struct + * + * @param nextAppEvent - time to next application layer event + * + */ + void hardware_sleep(struct ca821x_dev *pDeviceRef, uint32_t nextAppEvent); + + /** + * @brief hardware specific check if device can sleep + * + * @return true if device is allowed to sleep, false otherwise + * + */ + bool hardware_can_sleep(); #ifdef __cplusplus } diff --git a/EXAMPLE/knx_iot_wakeful_main.c b/EXAMPLE/knx_iot_wakeful_main.c index 0ed2c78..b29d05e 100644 --- a/EXAMPLE/knx_iot_wakeful_main.c +++ b/EXAMPLE/knx_iot_wakeful_main.c @@ -1,6 +1,6 @@ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - Copyright (c) 2022-2023 Cascoda Limited + Copyright (c) 2022-2024 Cascoda Limited -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= * All rights reserved. * @@ -100,6 +100,7 @@ void knx_srp_add_service(void); // To be implemented by the application void register_resources(void); void factory_presets_cb(size_t device_index, void *data); +void lsm_change_cb(size_t device_index, oc_lsm_state_t current_state, void *data); void hostname_cb(size_t device_index, oc_string_t host_name, void *data); int app_set_serial_number(char *serial_number); int app_init(void); @@ -181,6 +182,8 @@ static void ot_state_changed(uint32_t flags, void *context) SNTP_Update(); } #endif /* USE_SNTP */ + + logic_role_changed(); } static void signal_event_loop(void) @@ -202,6 +205,16 @@ static void reset_cb(size_t device_index, int reset_value, void *data) reset_embedded(device_index, reset_value, data); } +void swu_start_update_cb_imp(size_t device_index, uint32_t start_time, void *data) +{ + ca_error status = CA_ERROR_FAIL; + PRINT_APP("swu_start_update_cb_imp(), device: %d, applying new sofware in %d\n", device_index, start_time); +#if CASCODA_OTA_UPGRADE_ENABLED + status = ota_start_upgrade(start_time); + PRINT_APP("swu_start_update_cb_imp(), status = %d\n",status); +#endif +} + /** * main application. * intializes the global variables @@ -278,7 +291,7 @@ int main(void) // Print the joiner credentials, delaying for up to 1 second PlatformPrintJoinerCredentials(&dev, OT_INSTANCE, 0); - otErr = PlatformTryJoin(&dev, OT_INSTANCE); + otErr = PlatformTryJoinWithCustomPoll(&dev, OT_INSTANCE, &hardware_poll); if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) break; joinCooldownTimer = 0; @@ -288,7 +301,7 @@ int main(void) // Print the joiner credentials, delaying for up to 1 second PlatformPrintJoinerCredentialsWithPskd(&dev, OT_INSTANCE, 0, thread_pw); - otErr = PlatformTryJoinWithPskd(&dev, OT_INSTANCE, thread_pw); + otErr = PlatformTryJoinWithPskdWithCustomPoll(&dev, OT_INSTANCE, thread_pw, &hardware_poll); if (otErr == OT_ERROR_NONE || otErr == OT_ERROR_ALREADY) break; joinCooldownTimer = 0; @@ -325,19 +338,18 @@ int main(void) oc_storage_config("./knx_iot_creds"); - /* configure the serial number */ uint8_t sn[6]; + /* configure the serial number. must be done before stack initialization */ error = knx_get_stored_serial_number(sn); if (error) { PRINT_APP("ERROR: Unique serial number not found! Using default value...\n"); PRINT_APP( "Please create the data file using knx-gen-data and flash it with chilictl in order to fix this issue.\n"); - } - else - { + } else { // turn binary to hexadecimal char serial_number_str[13]; + // serial number in upper case snprintf(serial_number_str, sizeof(serial_number_str), "%02X%02X%02X%02X%02X%02X", @@ -349,7 +361,6 @@ int main(void) sn[5]); app_set_serial_number(serial_number_str); } - #ifdef OC_SPAKE char pwd[33]; error = knx_get_stored_password(pwd); @@ -389,13 +400,26 @@ int main(void) oc_set_hostname_cb(hostname_cb, NULL); oc_set_reset_cb(reset_cb, NULL); oc_set_factory_presets_cb(factory_presets_cb, NULL); + oc_set_lsm_change_cb(lsm_change_cb, NULL); #if CASCODA_OTA_UPGRADE_ENABLED oc_set_swu_cb(swu_cb, (void *)"image_name"); + oc_set_swu_startupdate_cb(swu_start_update_cb_imp, (void *)"image_name"); #endif oc_set_programming_mode_cb(prog_mode_cb, NULL); /* start the stack */ init = oc_main_init(&handler); + + // configure the hostname. + // note: this method sets the SN part to uppercase, as that is how + // the serial number is stored in the oc_device_info_t + oc_device_info_t *device = oc_core_get_device_info(0); + char hostname_str[50]; + memset(hostname_str, 0, 49); + strcat(hostname_str, "knx-"); + strcat(hostname_str, oc_string(device->serialnumber)); + strcat(hostname_str, ".local"); + oc_core_set_device_hostname(0, hostname_str); oc_set_max_app_data_size(1024); oc_set_mtu_size(1232); diff --git a/EXAMPLE/knx_iot_wakeful_main_extern.h b/EXAMPLE/knx_iot_wakeful_main_extern.h index a8ef4a9..0f9fba0 100644 --- a/EXAMPLE/knx_iot_wakeful_main_extern.h +++ b/EXAMPLE/knx_iot_wakeful_main_extern.h @@ -33,52 +33,59 @@ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cascoda-bm/cascoda_interface.h" #include "cascoda-bm/cascoda_types.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/** - * @brief define what happens when receiving a post mesage on a URL - * - * @param url the URL of the message as a string - */ -void post_callback(char *url); + /** + * @brief define what happens when receiving a post mesage on a URL + * + * @param url the URL of the message as a string + */ + void post_callback(char *url); -/** - * @brief any hardware specific setup e.g. registering board LEDs and buttons - * - */ -void hardware_init(); + /** + * @brief any hardware specific setup e.g. registering board LEDs and buttons + * + */ + void hardware_init(); -/** - * @brief any hardware independent setup e.g. setting up graphics - * - */ -void logic_initialize(); + /** + * @brief any hardware independent setup e.g. setting up graphics + * + */ + void logic_initialize(); -/** - * @brief any hardware specific actions to be continually run e.g. checking buttons for input - * - */ -void hardware_poll(); + /** + * @brief any action to take as a result of the role changing, e.g. refreshing screen + * + */ + void logic_role_changed(); -/** - * @brief Application-specific handling of programming mode command received from linker - * - */ -void programming_mode_embedded(size_t device_index, bool programming_mode); + /** + * @brief any hardware specific actions to be continually run e.g. checking buttons for input + * + */ + void hardware_poll(); -/** - * @brief Application-specific handling of reset when received from linker - * - */ -void reset_embedded(size_t device_index, int reset_value, void *data); + /** + * @brief Application-specific handling of programming mode command received from linker + * + */ + void programming_mode_embedded(size_t device_index, bool programming_mode); + + /** + * @brief Application-specific handling of reset when received from linker + * + */ + void reset_embedded(size_t device_index, int reset_value, void *data); #ifdef __cplusplus } diff --git a/EXAMPLE/readme.md b/EXAMPLE/readme.md index ff4dcfd..5deea05 100644 --- a/EXAMPLE/readme.md +++ b/EXAMPLE/readme.md @@ -53,7 +53,7 @@ The general structure of these programs are: - manufactorer : - model : dev board example -- hardware_type : 000001 +- hardware_type : 000000000000 - hardware version : [0, 4, 0] - firmware version : [0, 4, 0]