STMicroelectronics / STM32CubeWB

Full Firmware Package for the STM32WB series: HAL+LL drivers, CMSIS, BSP, MW, plus a set of Projects (examples and demos) running on all boards provided by ST (Nucleo, Evaluation and Discovery Kits).
https://www.st.com/en/embedded-software/stm32cubewb.html
Other
227 stars 138 forks source link

SRP still not working for me #86

Closed agalliazzo closed 7 months ago

agalliazzo commented 10 months ago

Describe the set-up

Actual network configuration

Describe the bug SRP client still not working also if #78 is marked as resolved. In particular, I can register the first time, then I try to save NVM data (and it works, I check if saved data is in the FLASH using STMCubeProgrammer and the data match) but at the next power-on SRP fail to register due to loosing the key as suggested in this old thread

How To Reproduce

  1. Started a new project from scratch from Thread_SED_Coap_FreeRTOS example
  2. Added features like APP_NVM_Init, Save, Write and Read from the Thread_NVM as suggested in #74
  3. Added function to get the GUA address
  4. Added a function to get the SRP server socket (address + port)
  5. Added a function to register the client to the server
  6. Modify the var OT_NvmRamBuffer to be placed at RAM2 as described on line 44 of Thread_SED_Coap_FreeRTOS\Core\Inc\hw_conf.h (tested also with the original configuration present in the Thread_NVM sample)

Steps in code

  1. Initialize the connection choosing between hard-coded configuration or NVM data based on the presence of the panid value (if 0xffff memory is empty)
  2. Wait to state notification change to Child or Router (it's an MTD, so it will be a child)
  3. Once device is a child wait 10s to ensure everything is properly setted up (this is only a safety delay, networkdata should already be available, but waiting is not a problem)
  4. Get the device GUA
  5. Get the SRP server socket
  6. Register a service with the GUA in the SRP server socket.
  7. After about 1s from the first sucesfull SRP registration, with a FreeRTOS timer, a function call NVM_Save(). At this point data is stored starting from address 0x8000000 + 500K (I edited the linker script to reserve this flash area for NVM storage, this was missing on the Thread_NVM example)
  8. Power off the board and wait 5s
  9. Power on the board again, SRP registration is refused by the server

My code: OHCS_Protocol.h

#ifndef OHCS_PROTOCOL_H
#define OHCS_PROTOCOL_H

#include "FreeRTOS.h"
#include "timers.h"
#include "srp_client.h"
#include "openthread_api_wb.h"

#define TMR_ON_CONNECT_ID   1

#define OT_IN NULL
#define get_default_ot_isntance() NULL

#define CONTAINER_OF(ptr, type, field) ((type *)(((char *)(ptr)) - offsetof(type, field)))

typedef union eui64{
    uint8_t bytes[8];
    uint32_t int16[4];
    uint32_t int32[2];
} eui64_t;

typedef struct {
    void (*cb_SaveNVMData)();
    TimerHandle_t state_change_timer;
    TimerHandle_t save_data_timer;
    otIp6Address OMR;
    otSockAddr SRP_server_address;
    otSrpClientService service;
    eui64_t eui64;

    char * hostname;

} oh_coap_server_context_t;

void OHCS_Initialize(oh_coap_server_context_t * ctx);
void OHCS_OnThreadStateUpdate(oh_coap_server_context_t *ctx, uint32_t NotifFlags);

#endif

OHCS_Protocol.c

/*
 * OHCS_Protocol.c
 *
 *  Created on: Jan 29, 2024
 *      Author: agall
 */

#include "OHCS_Protocol.h"
#include "openthread_api_wb.h"

#ifdef STM32WB
#include "stm32wbxx_nucleo.h"

#endif

static void _OHCS_StateChangeTimerExpired(TimerHandle_t handler);
static otError _OHCS_GetOMR(oh_coap_server_context_t *ctx);
static otError _OHCS_RegisterServices(oh_coap_server_context_t *srv_ctx);
static otError _OHCS_SearchSRPServer(oh_coap_server_context_t *srv_ctx) ;
static void _OHCS_SaveTimerExpired(TimerHandle_t handler);

void OHCS_Initialize(oh_coap_server_context_t *ctx) {
    ctx->state_change_timer = xTimerCreate("TimerStateChange", 10000 / portTICK_PERIOD_MS, 0, ctx,
            _OHCS_StateChangeTimerExpired);
    ctx->save_data_timer = xTimerCreate("TimerSave", 5000 / portTICK_PERIOD_MS, 0, ctx,
            _OHCS_SaveTimerExpired);

    ctx->cb_SaveNVMData = NULL;
    ctx->hostname = "TT3";
}

void OHCS_OnThreadStateUpdate(oh_coap_server_context_t *ctx, uint32_t NotifFlags) {
    if ((NotifFlags & (uint32_t) OT_CHANGED_THREAD_ROLE) == (uint32_t) OT_CHANGED_THREAD_ROLE) {
        switch (otThreadGetDeviceRole(NULL)) {
        default:
        case OT_DEVICE_ROLE_DISABLED:
        case OT_DEVICE_ROLE_DETACHED:
            break;
        case OT_DEVICE_ROLE_CHILD:
        case OT_DEVICE_ROLE_ROUTER:
        case OT_DEVICE_ROLE_LEADER:
            xTimerStart(ctx->state_change_timer, 5000 / portTICK_PERIOD_MS);
            break;
        }
    }
}

static otError _OHCS_GetOMR(oh_coap_server_context_t *ctx) {
    const otNetifAddress *netif_ips = otIp6GetUnicastAddresses(OT_IN);
    otNetifAddress *p = netif_ips;

    while (p) {
        if (p == NULL)
            return NULL;
        if (!p->mRloc && p->mValid && p->mAddressOrigin == 1 && p->mPreferred) {
            memcpy(&ctx->OMR, &p->mAddress, sizeof(otIp6Address));
            return OT_ERROR_NONE;
        }
        p = p->mNext;
    }
    return OT_ERROR_NOT_FOUND;
}

static otError _OHCS_SearchSRPServer(oh_coap_server_context_t *srv_ctx) {
    otError err;
    otServiceConfig conf;
    otNetworkDataIterator it = 0;
    while ((err = otNetDataGetNextService(NULL, &it, &conf) != OT_ERROR_NOT_FOUND)) {
        if (conf.mServiceData[0] == 0x5d) {
            memcpy(&srv_ctx->SRP_server_address.mAddress.mFields.m8, &conf.mServerConfig.mServerData, 16);
            srv_ctx->SRP_server_address.mPort = conf.mServerConfig.mServerData[16] << 8
                    | conf.mServerConfig.mServerData[17];
            return OT_ERROR_NONE;
        }
    }
    return OT_ERROR_NOT_FOUND;
}

void _OHCS_cb_srpRegistration(otError                    aError,
                                    const otSrpClientHostInfo *aHostInfo,
                                    const otSrpClientService  *aServices,
                                    const otSrpClientService  *aRemovedServices,
                                    void                      *aContext){
    oh_coap_server_context_t *ctx = (oh_coap_server_context_t *) aContext;
    if(aError == OT_ERROR_NONE)
        xTimerStart(ctx->save_data_timer, 1000 / portTICK_PERIOD_MS);
    else{
        BSP_LED_On(LED_RED);
        otSrpClientStop(NULL);
    }
}

static otError _OHCS_RegisterServices(oh_coap_server_context_t *srv_ctx){
    otError err;
    srv_ctx->service.mName = "_coap._udp.";
    srv_ctx->service.mInstanceName = srv_ctx->hostname;
    srv_ctx->service.mPort = OT_DEFAULT_COAP_PORT;
    srv_ctx->service.mPriority = 1;
    srv_ctx->service.mWeight = 1;
    srv_ctx->service.mSubTypeLabels = NULL;
    srv_ctx->service.mTxtEntries = NULL;
    srv_ctx->service.mNumTxtEntries = 0;
    srv_ctx->service.mLease = 600;

    //otSrpClientSetLeaseInterval(OT_IN, 600);

    if(otSrpClientIsRunning(OT_IN)){
        otSrpClientStop(NULL);
    }

    otSrpClientSetCallback(NULL, _OHCS_cb_srpRegistration, srv_ctx);
    err = otSrpClientSetHostName(OT_IN, srv_ctx->hostname);
    if (err != OT_ERROR_NONE) {
        return err;
    }

    err = otSrpClientAddService(OT_IN, &srv_ctx->service);
    if (err != OT_ERROR_NONE) {
        return err;
    }

    err = otSrpClientSetHostAddresses(OT_IN, &srv_ctx->OMR, 1);
    if (err != OT_ERROR_NONE) {
        return err;
    }

    err = otSrpClientStart(NULL, &srv_ctx->SRP_server_address);
    if (err != OT_ERROR_NONE) {
        return err;
    }

    return OT_ERROR_NONE;
}
static void _OHCS_StateChangeTimerExpired(TimerHandle_t handler) {
    oh_coap_server_context_t *ctx = (oh_coap_server_context_t *) pvTimerGetTimerID(handler);
    _OHCS_GetOMR(ctx);
    _OHCS_SearchSRPServer(ctx);
    _OHCS_RegisterServices(ctx);
    BSP_LED_On(LED_BLUE);
}

static void _OHCS_SaveTimerExpired(TimerHandle_t handler){
    oh_coap_server_context_t *ctx = (oh_coap_server_context_t *) pvTimerGetTimerID(handler);
    if(ctx->cb_SaveNVMData){
        ctx->cb_SaveNVMData();
        BSP_LED_On(LED_GREEN);
    }
}

app_thread.c

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name          : App/app_thread.c
 * Description        : Thread Application.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2019-2021 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "app_common.h"
#include "utilities_common.h"
#include "app_entry.h"
#include "dbg_trace.h"
#include "app_thread.h"
#include "stm32wbxx_core_interface_def.h"
#include "openthread_api_wb.h"
#include "shci.h"
#include "stm_logging.h"
#include "app_conf.h"
#include "stm32_lpm.h"
#include "cmsis_os.h"
#if (CFG_USB_INTERFACE_ENABLE != 0)
#include "vcp.h"
#include "vcp_conf.h"
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

/* Private includes -----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ee.h"
#include "ee_cfg.h"
#include "OHCS_Protocol.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private defines -----------------------------------------------------------*/
#define C_SIZE_CMD_STRING       256U
#define C_PANID                 0x1234U
#define C_CHANNEL_NB            26U
#define THREAD_LINK_POLL_PERIOD_MS      (5000)                        /**< 5s */

osMutexId_t MtxOtCmdId;

/* FreeRtos stacks attributes */
const osThreadAttr_t ThreadMsgM0ToM4Process_attr = { .name = CFG_THREAD_MSG_M0_TO_M4_PROCESS_NAME, .attr_bits =
        CFG_THREAD_MSG_M0_TO_M4_PROCESS_ATTR_BITS, .cb_mem = CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_MEM, .cb_size =
        CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_SIZE, .stack_mem = CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_MEM, .priority =
        CFG_THREAD_MSG_M0_TO_M4_PROCESS_PRIORITY, .stack_size = CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_SIZE };

const osThreadAttr_t ThreadCliProcess_attr = { .name = CFG_THREAD_CLI_PROCESS_NAME, .attr_bits =
        CFG_THREAD_CLI_PROCESS_ATTR_BITS, .cb_mem = CFG_THREAD_CLI_PROCESS_CB_MEM, .cb_size =
        CFG_THREAD_CLI_PROCESS_CB_SIZE, .stack_mem = CFG_THREAD_CLI_PROCESS_STACK_MEM, .priority =
        CFG_THREAD_CLI_PROCESS_PRIORITY, .stack_size = CFG_THREAD_CLI_PROCESS_STACK_SIZE };

typedef enum {
    ot_TL_CmdBusy, ot_TL_CmdAvailable
} ot_TL_CmdStatus_t;

/* USER CODE BEGIN PD */
#define C_RESSOURCE                     "light"

#define COAP_SEND_TIMEOUT               (1*1000*1000/CFG_TS_TICK_VAL) /**< 1s */

/* FreeRtos stacks attributes */
const osThreadAttr_t ThreadSendCoapMsgProcess_attr = { .name = CFG_THREAD_SEND_COAP_MSG_PROCESS_NAME, .attr_bits =
        CFG_THREAD_SEND_COAP_MSG_PROCESS_ATTR_BITS, .cb_mem = CFG_THREAD_SEND_COAP_MSG_PROCESS_CB_MEM, .cb_size =
        CFG_THREAD_SEND_COAP_MSG_PROCESS_CB_SIZE, .stack_mem = CFG_THREAD_SEND_COAP_MSG_PROCESS_STACK_MEM, .priority =
        CFG_THREAD_SEND_COAP_MSG_PROCESS_PRIORITY, .stack_size = CFG_THREAD_SEND_COAP_MSG_PROCESS_STACk_SIZE };
/* USER CODE END PD */

/* Private macros ------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private function prototypes -----------------------------------------------*/
static void APP_THREAD_CheckWirelessFirmwareInfo(void);
static void APP_THREAD_DeviceConfig(void);
static void APP_THREAD_StateNotif(uint32_t NotifFlags, void *pContext);
static void APP_THREAD_TraceError(const char *pMess, uint32_t ErrCode);
#if (CFG_FULL_LOW_POWER == 0)
static void Send_CLI_To_M0(void);
#endif /* (CFG_FULL_LOW_POWER == 0) */
static void Send_CLI_Ack_For_OT(void);
static void HostTxCb(void);
static void Wait_Getting_Ack_From_M0(void);
static void Receive_Ack_From_M0(void);
static void Receive_Notification_From_M0(void);
static void ot_StatusNot(ot_TL_CmdStatus_t status);
#if (CFG_HW_LPUART1_ENABLED == 1)
extern void MX_LPUART1_UART_Init(void);
#endif
#if (CFG_HW_USART1_ENABLED == 1)
extern void MX_USART1_UART_Init(void);
#endif
#if (CFG_USB_INTERFACE_ENABLE != 0)
static uint32_t ProcessCmdString(uint8_t* buf , uint32_t len);
#else
#if (CFG_FULL_LOW_POWER == 0)
static void RxCpltCallback(void);
#endif /* (CFG_FULL_LOW_POWER == 0) */
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

/* FreeRTos wrapper functions */
static void APP_THREAD_FreeRTOSProcessMsgM0ToM4Task(void *argument);
#if (CFG_FULL_LOW_POWER == 0)
static void APP_THREAD_FreeRTOSSendCLIToM0Task(void *argument);
#endif /* (CFG_FULL_LOW_POWER == 0) */

/* USER CODE BEGIN PFP */
static void APP_THREAD_SendCoapMsg(void);
static void APP_THREAD_SendCoapMulticastRequest(uint8_t command);
static void APP_THREAD_CoapRequestHandler(void *pContext, otMessage *pMessage, const otMessageInfo *pMessageInfo);

static void APP_THREAD_FreeRTOSSendCoapMsgTask(void *argument);
static void APP_THREAD_CoapTimingElapsed(void);
static void APP_THREAD_SendCoapMsg(void);
static void APP_THREAD_StartCoapTimer(void);
/* USER CODE END PFP */

/* Private variables -----------------------------------------------*/
#if (CFG_USB_INTERFACE_ENABLE != 0)
static uint8_t TmpString[C_SIZE_CMD_STRING];
static uint8_t VcpRxBuffer[sizeof(TL_CmdSerial_t)];        /* Received Data over USB are stored in this buffer */
static uint8_t VcpTxBuffer[sizeof(TL_EvtPacket_t) + 254U]; /* Transmit buffer over USB */
#else
#if (CFG_FULL_LOW_POWER == 0)
static uint8_t aRxBuffer[C_SIZE_CMD_STRING];
#endif /* (CFG_FULL_LOW_POWER == 0) */
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

#if (CFG_FULL_LOW_POWER == 0)
static uint8_t CommandString[C_SIZE_CMD_STRING];
#endif /* (CFG_FULL_LOW_POWER == 0) */
static __IO uint16_t indexReceiveChar = 0;
static __IO uint16_t CptReceiveCmdFromUser = 0;

static TL_CmdPacket_t *p_thread_otcmdbuffer;
static TL_EvtPacket_t *p_thread_notif_M0_to_M4;
static __IO uint32_t CptReceiveMsgFromM0 = 0;
static volatile int FlagReceiveAckFromM0 = 0;

PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_TH_Config_t ThreadConfigBuffer;
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ThreadOtCmdBuffer;
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ThreadNotifRspEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE
        + 255U];
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ThreadCliCmdBuffer;
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ThreadCliNotBuffer;
extern uint8_t g_ot_notification_allowed;

static osThreadId_t OsTaskMsgM0ToM4Id; /* Task managing the M0 to M4 messaging        */
#if (CFG_FULL_LOW_POWER == 0)
static osThreadId_t OsTaskCliId;            /* Task used to manage CLI command             */
#endif /* (CFG_FULL_LOW_POWER == 0) */

/* USER CODE BEGIN PV */
static otCoapResource OT_Ressource = { C_RESSOURCE, APP_THREAD_CoapRequestHandler, "myCtx", NULL };
static otMessageInfo OT_MessageInfo = { 0 };
static uint8_t OT_Command = 0;
static uint8_t OT_ReceivedCommand = 0;
static otMessage *pOT_Message = NULL;
static osThreadId_t OsTaskSendCoapMsgId; /* Task managing the COAP transfer             */
static uint8_t sedCoapTimerID;

/* Debug */
static uint32_t DebugRxCoapCpt = 0;
static uint32_t DebugTxCoapCpt = 0;

////////////////////////////////////////////////////////////////////////
/// MY CODE
////////////////////////////////////////////////////////////////////////

oh_coap_server_context_t srv_ctx;

PLACE_IN_SECTION("MB_MEM2") uint32_t OT_NvmRamBuffer[CFG_THREAD_NVM_RAM_SIZE_IN_BYTES / 4];

static void APP_THREAD_NVM_Init(void);
static bool APP_THREAD_NVM_Read(void);
static bool APP_THREAD_NVM_Write(void);
static void APP_THREAD_NvmSave(void);

/* USER CODE END PV */

/* Functions Definition ------------------------------------------------------*/

void APP_THREAD_Init(void) {
    /* USER CODE BEGIN APP_THREAD_INIT_1 */
    /* Do not allow stop mode before Thread is initialized */
    UTIL_LPM_SetStopMode(1 << CFG_LPM_APP_THREAD, UTIL_LPM_DISABLE);

    APP_THREAD_NVM_Init();
    /* USER CODE END APP_THREAD_INIT_1 */

    SHCI_CmdStatus_t ThreadInitStatus;

    /* Check the compatibility with the Coprocessor Wireless Firmware loaded */
    APP_THREAD_CheckWirelessFirmwareInfo();

#if (CFG_USB_INTERFACE_ENABLE != 0)
  VCP_Init(&VcpTxBuffer[0], &VcpRxBuffer[0]);
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

    /* Register cmdbuffer */
    APP_THREAD_RegisterCmdBuffer(&ThreadOtCmdBuffer);

    /**
     * Do not allow standby in the application
     */
    UTIL_LPM_SetOffMode(1 << CFG_LPM_APP_THREAD, UTIL_LPM_DISABLE);

    /* Init config buffer and call TL_THREAD_Init */
    APP_THREAD_TL_THREAD_INIT();

    /* Configure UART for sending CLI command from M4 */
    APP_THREAD_Init_UART_CLI();

    /* Send Thread start system cmd to M0 */
    ThreadInitStatus = SHCI_C2_THREAD_Init();

    /* Prevent unused argument(s) compilation warning */
    UNUSED(ThreadInitStatus);

    /* USER CODE BEGIN APP_THREAD_INIT_TIMER */
    OHCS_Initialize(&srv_ctx);
    srv_ctx.cb_SaveNVMData = APP_THREAD_NvmSave;
    /**
     * Create timer to handle COAP request sending
     */
    HW_TS_Create(CFG_TIM_PROC_ID_ISR, &sedCoapTimerID, hw_ts_Repeated, APP_THREAD_CoapTimingElapsed);
    /* Allow the 800_15_4 IP to enter in low power mode */
    SHCI_C2_RADIO_AllowLowPower(THREAD_IP, TRUE);

    /* USER CODE END APP_THREAD_INIT_TIMER */

    /* Create the different FreeRTOS tasks requested to run this Thread application*/
    OsTaskMsgM0ToM4Id = osThreadNew(APP_THREAD_FreeRTOSProcessMsgM0ToM4Task, NULL, &ThreadMsgM0ToM4Process_attr);

    /* USER CODE BEGIN APP_THREAD_INIT_FREERTOS */
    OsTaskSendCoapMsgId = osThreadNew(APP_THREAD_FreeRTOSSendCoapMsgTask, NULL, &ThreadSendCoapMsgProcess_attr);
    /* USER CODE END APP_THREAD_INIT_FREERTOS */

    /* Configure the Thread device at start */
    APP_THREAD_DeviceConfig();

    /* USER CODE BEGIN APP_THREAD_INIT_2 */
    /* Allow stop mode after Thread initialization*/
    UTIL_LPM_SetStopMode(1 << CFG_LPM_APP_THREAD, UTIL_LPM_ENABLE);
    /* USER CODE END APP_THREAD_INIT_2 */
}

/**
 * @brief  Trace the error or the warning reported.
 * @param  ErrId :
 * @param  ErrCode
 * @retval None
 */
void APP_THREAD_Error(uint32_t ErrId, uint32_t ErrCode) {
    /* USER CODE BEGIN APP_THREAD_Error_1 */

    /* USER CODE END APP_THREAD_Error_1 */
    switch (ErrId) {
    case ERR_REC_MULTI_MSG_FROM_M0:
        APP_THREAD_TraceError("ERROR : ERR_REC_MULTI_MSG_FROM_M0 ", ErrCode);
        break;
    case ERR_THREAD_SET_STATE_CB:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_SET_STATE_CB ", ErrCode);
        break;
    case ERR_THREAD_SET_CHANNEL:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_SET_CHANNEL ", ErrCode);
        break;
    case ERR_THREAD_SET_PANID:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_SET_PANID ", ErrCode);
        break;
    case ERR_THREAD_IPV6_ENABLE:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_IPV6_ENABLE ", ErrCode);
        break;
    case ERR_THREAD_START:
        APP_THREAD_TraceError("ERROR: ERR_THREAD_START ", ErrCode);
        break;
    case ERR_THREAD_ERASE_PERSISTENT_INFO:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_ERASE_PERSISTENT_INFO ", ErrCode);
        break;
    case ERR_THREAD_SET_NETWORK_KEY:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_SET_NETWORK_KEY ", ErrCode);
        break;
    case ERR_THREAD_CHECK_WIRELESS:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_CHECK_WIRELESS ", ErrCode);
        break;
        /* USER CODE BEGIN APP_THREAD_Error_2 */
    case ERR_THREAD_COAP_START:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_START ", ErrCode);
        break;
    case ERR_THREAD_COAP_ADD_RESSOURCE:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_ADD_RESSOURCE ", ErrCode);
        break;
    case ERR_THREAD_MESSAGE_READ:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_MESSAGE_READ ", ErrCode);
        break;
    case ERR_THREAD_COAP_SEND_RESPONSE:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_SEND_RESPONSE ", ErrCode);
        break;
    case ERR_THREAD_COAP_APPEND:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_APPEND ", ErrCode);
        break;
    case ERR_THREAD_COAP_SEND_REQUEST:
        APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_SEND_REQUEST ", ErrCode);
        break;
    case ERR_TIMER_INIT:
        APP_THREAD_TraceError("ERROR : ERR_TIMER_INIT ", ErrCode);
        break;
    case ERR_TIMER_START:
        APP_THREAD_TraceError("ERROR : ERR_TIMER_START ", ErrCode);
        break;
        /* USER CODE END APP_THREAD_Error_2 */
    default:
        APP_THREAD_TraceError("ERROR Unknown ", 0);
        break;
    }
}

/*************************************************************
 *
 * LOCAL FUNCTIONS
 *
 *************************************************************/

/**
 * @brief Thread initialization.
 * @param  None
 * @retval None
 */
static void APP_THREAD_DeviceConfig(void) {

    otError error;
    otNetworkKey networkKey = { { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
            0xEE, 0xFF } };

    otLinkModeConfig OT_LinkMode = { 0 };
    /* Set the sleepy end device mode */
    OT_LinkMode.mRxOnWhenIdle = 0;
    OT_LinkMode.mDeviceType = 0;
    OT_LinkMode.mNetworkData = 1U;
    error = otSetStateChangedCallback(NULL, APP_THREAD_StateNotif, NULL);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_SET_STATE_CB, error);
    }

    otPanId pan_id = otLinkGetPanId(NULL);
    if(pan_id == 0xffff){
        // Flash not populated
        otInstanceInitSingle();
        error = otLinkSetChannel(NULL, C_CHANNEL_NB);
        if (error != OT_ERROR_NONE) {
            APP_THREAD_Error(ERR_THREAD_SET_CHANNEL, error);
        }
        error = otLinkSetPanId(NULL, C_PANID);
        if (error != OT_ERROR_NONE) {
            APP_THREAD_Error(ERR_THREAD_SET_PANID, error);
        }
        error = otThreadSetNetworkKey(NULL, &networkKey);
        if (error != OT_ERROR_NONE) {
            APP_THREAD_Error(ERR_THREAD_SET_NETWORK_KEY, error);
        }
        error = otLinkSetPollPeriod(NULL, THREAD_LINK_POLL_PERIOD_MS);

        /*if (error != OT_ERROR_NONE)
         {
         APP_THREAD_Error(ERR_THREAD_TO_BE_DEFINED,error); // l'erreur pour la pollperiod n'existe pas
         }*/
        error = otThreadSetLinkMode(NULL, OT_LinkMode);
        if (error != OT_ERROR_NONE) {
            APP_THREAD_Error(ERR_THREAD_LINK_MODE, error);
        }

    }

    error = otIp6SetEnabled(NULL, true);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_IPV6_ENABLE, error);
    }
    error = otThreadSetEnabled(NULL, true);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_START, error);
    }

    error = otCoapStart(NULL, OT_DEFAULT_COAP_PORT);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_COAP_START, error);
    }

#if 0
    error = otInstanceErasePersistentInfo(NULL);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_ERASE_PERSISTENT_INFO, error);
    }
    otInstanceFinalize(NULL);
    otInstanceInitSingle();

    /* USER CODE BEGIN DEVICECONFIG */
    /* Start the COAP server */
    error = otCoapStart(NULL, OT_DEFAULT_COAP_PORT);
    if (error != OT_ERROR_NONE) {
        APP_THREAD_Error(ERR_THREAD_COAP_START, error);
    }
    /* Add COAP resources */
    otCoapAddResource(NULL, &OT_Ressource);

    /* USER CODE END DEVICECONFIG */
#endif
}

/**
 * @brief Thread notification when the state changes.
 * @param  aFlags  : Define the item that has been modified
 *         aContext: Context
 *
 * @retval None
 */
static void APP_THREAD_StateNotif(uint32_t NotifFlags, void *pContext) {
    /* Prevent unused argument(s) compilation warning */
    UNUSED(pContext);

    /* USER CODE BEGIN APP_THREAD_STATENOTIF */
    OHCS_OnThreadStateUpdate(&srv_ctx, NotifFlags);
    /* USER CODE END APP_THREAD_STATENOTIF */

    if ((NotifFlags & (uint32_t) OT_CHANGED_THREAD_ROLE) == (uint32_t) OT_CHANGED_THREAD_ROLE) {
        switch (otThreadGetDeviceRole(NULL)) {
        case OT_DEVICE_ROLE_DISABLED:
            /* USER CODE BEGIN OT_DEVICE_ROLE_DISABLED */
            /* USER CODE END OT_DEVICE_ROLE_DISABLED */
            break;
        case OT_DEVICE_ROLE_DETACHED:
            /* USER CODE BEGIN OT_DEVICE_ROLE_DETACHED */
            /* USER CODE END OT_DEVICE_ROLE_DETACHED */
            break;
        case OT_DEVICE_ROLE_CHILD:
            /* USER CODE BEGIN OT_DEVICE_ROLE_CHILD */
            /* Set the mode sleepy end device */
            /* Start the timer */
            //APP_THREAD_StartCoapTimer();
            /* USER CODE END OT_DEVICE_ROLE_CHILD */
            break;
        case OT_DEVICE_ROLE_ROUTER:
            /* USER CODE BEGIN OT_DEVICE_ROLE_ROUTER */
            /* Set the mode sleepy end device */
            /* Start the timer */
            //APP_THREAD_StartCoapTimer();
            /* USER CODE END OT_DEVICE_ROLE_ROUTER */
            break;
        case OT_DEVICE_ROLE_LEADER:
            /* USER CODE BEGIN OT_DEVICE_ROLE_LEADER */
            /* USER CODE END OT_DEVICE_ROLE_LEADER */
            break;
        default:
            /* USER CODE BEGIN DEFAULT */
            /* USER CODE END DEFAULT */
            break;
        }
        OHCS_OnThreadStateUpdate(&srv_ctx, NotifFlags);
    }
}

/**
 * @brief  Warn the user that an error has occurred.In this case,
 *         the LEDs on the Board will start blinking.
 *
 * @param  pMess  : Message associated to the error.
 * @param  ErrCode: Error code associated to the module (OpenThread or other module if any)
 * @retval None
 */
static void APP_THREAD_TraceError(const char *pMess, uint32_t ErrCode) {
    /* USER CODE BEGIN TRACE_ERROR */
    APP_DBG("**** Fatal error = %s (Err = %d)", pMess, ErrCode);
    while (1U == 1U) {
        /* Note : Can be replace by HAL_Delay with timer to toggle LEDs */
        BSP_LED_On(LED1);
        BSP_LED_On(LED2);
        BSP_LED_On(LED3);
    }
    /* USER CODE END TRACE_ERROR */
}

/**
 * @brief Check if the Coprocessor Wireless Firmware loaded supports Thread
 *        and display associated information
 * @param  None
 * @retval None
 */
static void APP_THREAD_CheckWirelessFirmwareInfo(void) {
    WirelessFwInfo_t wireless_info_instance;
    WirelessFwInfo_t *p_wireless_info = &wireless_info_instance;

    if (SHCI_GetWirelessFwInfo(p_wireless_info) != SHCI_Success) {
        APP_THREAD_Error((uint32_t) ERR_THREAD_CHECK_WIRELESS, (uint32_t) ERR_INTERFACE_FATAL);
    } else {
        APP_DBG("**********************************************************");
        APP_DBG("WIRELESS COPROCESSOR FW:");
        /* Print version */
        APP_DBG("VERSION ID = %d.%d.%d", p_wireless_info->VersionMajor, p_wireless_info->VersionMinor,
                p_wireless_info->VersionSub);

        switch (p_wireless_info->StackType) {
        case INFO_STACK_TYPE_THREAD_FTD:
            APP_DBG("FW Type : Thread FTD")
            ;
            break;
        case INFO_STACK_TYPE_THREAD_MTD:
            APP_DBG("FW Type : Thread MTD")
            ;
            break;
        case INFO_STACK_TYPE_BLE_THREAD_FTD_STATIC:
            APP_DBG("FW Type : Static Concurrent Mode BLE/Thread")
            ;
            break;
        default:
            /* No Thread device supported ! */
            APP_THREAD_Error((uint32_t) ERR_THREAD_CHECK_WIRELESS, (uint32_t) ERR_INTERFACE_FATAL);
            break;
        }
        APP_DBG("**********************************************************");
    }
}

/*************************************************************
 *
 * FREERTOS WRAPPER FUNCTIONS
 *
 *************************************************************/
static void APP_THREAD_FreeRTOSProcessMsgM0ToM4Task(void *argument) {
    UNUSED(argument);
    for (;;) {
        /* USER CODE BEGIN APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_1 */

        /* USER END END APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_1 */
        osThreadFlagsWait(1, osFlagsWaitAll, osWaitForever);
        APP_THREAD_ProcessMsgM0ToM4();
        /* USER CODE BEGIN APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_2 */

        /* USER END END APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_2 */
    }
}

#if (CFG_FULL_LOW_POWER == 0)
static void APP_THREAD_FreeRTOSSendCLIToM0Task(void *argument)
{
  UNUSED(argument);
  for(;;)
  {
    /* USER CODE BEGIN APP_THREAD_FREERTOS_SEND_CLI_TO_M0_1 */

    /* USER END END APP_THREAD_FREERTOS_SEND_CLI_TO_M0_1 */
    osThreadFlagsWait(1,osFlagsWaitAll,osWaitForever);
    Send_CLI_To_M0();
    /* USER CODE BEGIN APP_THREAD_FREERTOS_SEND_CLI_TO_M0_2 */

    /* USER END END APP_THREAD_FREERTOS_SEND_CLI_TO_M0_2 */
  }
}
#endif /* (CFG_FULL_LOW_POWER == 0) */

/* USER CODE BEGIN FREERTOS_WRAPPER_FUNCTIONS */
/*static void APP_THREAD_FreeRTOSSetModeTask(void *argument)
 {
 UNUSED(argument);
 for(;;)
 {
 osThreadFlagsWait(1,osFlagsWaitAll,osWaitForever);
 APP_THREAD_SetSleepyEndDeviceMode();
 }
 }*/

static void APP_THREAD_FreeRTOSSendCoapMsgTask(void *argument) {
    UNUSED(argument);
    for (;;) {
        osThreadFlagsWait(4, osFlagsWaitAll, osWaitForever);
        APP_THREAD_SendCoapMsg();
    }
}
/* USER CODE END FREERTOS_WRAPPER_FUNCTIONS */

/* USER CODE BEGIN FD_LOCAL_FUNCTIONS */
/**
 * @brief Send a CoAP request with defined parameters.
 *
 * @param[in]  aCoapRessource   A pointer to a otCoapResource.
 * @param[in]  aCoapType        otCoapType.
 * @param[in]  aCoapCode        otCoapCode.
 * @param[in]  aStringAddress   A pointer to a NULL-terminated string representing the address. Example: "FF03::1" for Multicast.
 * @param[in]  aPeerAddress     A pointer to otIp6Address Peer Address.
 * @param[in]  aPayload         A pointer to payload.
 * @param[in]  aHandler         A pointer to CoAP response handler.
 * @param[in]  aContext         A pointer to application specific context.
 *
 * @retval none.
 */
static void APP_THREAD_CoapSendRequest(otCoapResource *aCoapRessource, otCoapType aCoapType, otCoapCode aCoapCode,
        const char *aStringAddress, const otIp6Address *aPeerAddress, uint8_t *aPayload, uint16_t Size,
        otCoapResponseHandler aHandler, void *aContext) {
    otError error = OT_ERROR_NONE;

    do {
        pOT_Message = otCoapNewMessage(NULL, NULL);
        if (pOT_Message == NULL) {
            APP_THREAD_Error(ERR_THREAD_COAP_NEW_MSG, error);
            break;
        }

        otCoapMessageInit(pOT_Message, aCoapType, aCoapCode);
        otCoapMessageAppendUriPathOptions(pOT_Message, aCoapRessource->mUriPath);
        otCoapMessageSetPayloadMarker(pOT_Message);

        if ((aPayload != NULL) && (Size > 0)) {
            error = otMessageAppend(pOT_Message, aPayload, Size);
            if (error != OT_ERROR_NONE) {
                APP_THREAD_Error(ERR_THREAD_COAP_APPEND, error);
                break;
            }
        } else {
            APP_DBG("APP_THREAD_CoapSendRequest: No payload passed");
        }

        memset(&OT_MessageInfo, 0, sizeof(OT_MessageInfo));
        OT_MessageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;

        if ((aPeerAddress == NULL) && (aStringAddress != NULL)) {
            APP_DBG("Use String Address : %s ", aStringAddress);
            otIp6AddressFromString(aStringAddress, &OT_MessageInfo.mPeerAddr);
        } else if (aPeerAddress != NULL) {
            APP_DBG("Use Peer Address");
            memcpy(&OT_MessageInfo.mPeerAddr, aPeerAddress, sizeof(OT_MessageInfo.mPeerAddr));
        } else {
            APP_DBG("ERROR: Address string and Peer Address not defined");
            APP_THREAD_Error(ERR_THREAD_COAP_ADDRESS_NOT_DEFINED, 0);
        }

        if (aCoapType == OT_COAP_TYPE_NON_CONFIRMABLE) {
            APP_DBG("aCoapType == OT_COAP_TYPE_NON_CONFIRMABLE");
            error = otCoapSendRequest(NULL, pOT_Message, &OT_MessageInfo,
            NULL,
            NULL);
        }
        if (aCoapType == OT_COAP_TYPE_CONFIRMABLE) {
            APP_DBG("aCoapType == OT_COAP_TYPE_CONFIRMABLE");
            error = otCoapSendRequest(NULL, pOT_Message, &OT_MessageInfo, aHandler, aContext);
        }
    } while (false);
    if (error != OT_ERROR_NONE && pOT_Message != NULL) {
        otMessageFree(pOT_Message);
        APP_THREAD_Error(ERR_THREAD_COAP_SEND_REQUEST, error);
    }
}

/**
 * @brief This function is used to set the sleepyEndDevice mode
 *        and configure its pool period.
 *
 * @param None
 * @retval None
 */
static void APP_THREAD_StartCoapTimer(void) {

    /* Start the timer */
    HW_TS_Start(sedCoapTimerID, (uint32_t) COAP_SEND_TIMEOUT);

}

/**
 * @brief Process associated to the sending of a COAP request.
 * @param  None
 * @retval None
 */
static void APP_THREAD_SendCoapMsg(void) {
    APP_THREAD_SendCoapMulticastRequest(1);
}

/**
 * @brief Handler called when the server receives a COAP request.
 * @param pContext : Context
 * @param pMessage : Message
 * @param pMessageInfo : Message information
 * @retval None
 */
static void APP_THREAD_CoapRequestHandler(void *pContext, otMessage *pMessage, const otMessageInfo *pMessageInfo) {
    do {
        if (otCoapMessageGetType(pMessage) != OT_COAP_TYPE_NON_CONFIRMABLE) {
            break;
        }

        if (otCoapMessageGetCode(pMessage) != OT_COAP_CODE_PUT) {
            break;
        }

        if (otMessageRead(pMessage, otMessageGetOffset(pMessage), &OT_ReceivedCommand, 1U) != 1U) {
            APP_THREAD_Error(ERR_THREAD_MESSAGE_READ, 0);
        }

        if (OT_ReceivedCommand == 1U) {
            BSP_LED_Toggle(LED1);
            APP_DBG("**** Recept COAP nb **** %d ", DebugRxCoapCpt++);
        }

    } while (false);
}

/**
 * @brief Send a COAP multicast request to all the devices which are connected
 *        on the Thread network
 * @param command : Command associated to the COAP request.
 * @retval None
 */
static void APP_THREAD_SendCoapMulticastRequest(uint8_t command) {
    otError error = OT_ERROR_NONE;

    OT_Command = command;

    /* Forbid the 800_15_4 IP to enter in low power mode.
     *
     * Reason for that...
     * As soon as the M0 is receiving an ot cmd, it wake up the IP 802_15_4,
     * send the command and put back the IP 802_15_4 in sleep mode when possible.
     * If the application send too much ot_cmd commands sequentially, the IP 802_15_4
     * will wake up and sleep at a non reasonable speed. It is why it is advised
     * to control when the IP 802_15_4 radio is allowed to enter in low power.
     */
    SHCI_C2_RADIO_AllowLowPower(THREAD_IP, FALSE);

    /* Send the COAP request */
    APP_THREAD_CoapSendRequest(&OT_Ressource, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_PUT,
    MULICAST_FTD_MED,
    NULL, &OT_Command, sizeof(OT_Command),
    NULL,
    NULL);

    APP_DBG("*** Send COAP nb **** %d", DebugTxCoapCpt++);

    if (error != OT_ERROR_NONE && pOT_Message != NULL) {
        otMessageFree(pOT_Message);
        APP_THREAD_Error(ERR_THREAD_COAP_SEND_REQUEST, error);
    }

    /* Allow the 800_15_4 IP to enter in low power mode */
    SHCI_C2_RADIO_AllowLowPower(THREAD_IP, TRUE);
}

static void APP_THREAD_CoapTimingElapsed(void) {
    /* DEBUG */
    /*
     APP_DBG("TIMER COAP EXPIRED");
     BSP_LED_Toggle(LED1);
     */
    {

        osThreadFlagsSet(OsTaskSendCoapMsgId, 4);
    }
}

/* USER CODE END FD_LOCAL_FUNCTIONS */

/*************************************************************
 *
 * WRAP FUNCTIONS
 *
 *************************************************************/

void APP_THREAD_RegisterCmdBuffer(TL_CmdPacket_t *p_buffer) {
    p_thread_otcmdbuffer = p_buffer;
}

Thread_OT_Cmd_Request_t* THREAD_Get_OTCmdPayloadBuffer(void) {
    return (Thread_OT_Cmd_Request_t*) p_thread_otcmdbuffer->cmdserial.cmd.payload;
}

Thread_OT_Cmd_Request_t* THREAD_Get_OTCmdRspPayloadBuffer(void) {
    return (Thread_OT_Cmd_Request_t*) ((TL_EvtPacket_t*) p_thread_otcmdbuffer)->evtserial.evt.payload;
}

Thread_OT_Cmd_Request_t* THREAD_Get_NotificationPayloadBuffer(void) {
    return (Thread_OT_Cmd_Request_t*) (p_thread_notif_M0_to_M4)->evtserial.evt.payload;
}

static void Ot_Cmd_Transfer_Common(void) {
    ot_StatusNot(ot_TL_CmdBusy);

    /* OpenThread OT command cmdcode range 0x280 .. 0x3DF = 352 */
    p_thread_otcmdbuffer->cmdserial.cmd.cmdcode = 0x280U;
    /* Size = otCmdBuffer->Size (Number of OT cmd arguments : 1 arg = 32bits so multiply by 4 to get size in bytes)
     * + ID (4 bytes) + Size (4 bytes) */
    uint32_t l_size = ((Thread_OT_Cmd_Request_t*) (p_thread_otcmdbuffer->cmdserial.cmd.payload))->Size * 4U + 8U;
    p_thread_otcmdbuffer->cmdserial.cmd.plen = l_size;

    TL_OT_SendCmd();

    /* Wait completion of cmd */
    Wait_Getting_Ack_From_M0();

    ot_StatusNot(ot_TL_CmdAvailable);
}

/**
 * @brief  This function is used to transfer the Ot commands from the
 *         M4 to the M0.
 *
 * @param   None
 * @return  None
 */
void Ot_Cmd_Transfer(void) {
    Ot_Cmd_Transfer_Common();
}

/**
 * @brief  This function is used to transfer the Ot commands from the
 *         M4 to the M0 with Notification M0 to M4 allowed.
 *
 * @param   None
 * @return  None
 */
void Ot_Cmd_TransferWithNotif(void) {
    /* Flag to specify to UTIL_SEQ_EvtIdle that M0 to M4 notifications are allowed */
    g_ot_notification_allowed = 1U;

    Ot_Cmd_Transfer_Common();
}

/**
 * @brief  This function is called when acknowledge from OT command is received from the M0+.
 *
 * @param   Otbuffer : a pointer to TL_EvtPacket_t
 * @return  None
 */
void TL_OT_CmdEvtReceived(TL_EvtPacket_t *Otbuffer) {
    /* Prevent unused argument(s) compilation warning */
    UNUSED(Otbuffer);

    Receive_Ack_From_M0();

    /* Does not allow OpenThread M0 to M4 notification */
    g_ot_notification_allowed = 0U;
}

/**
 * @brief  This function is called when notification from M0+ is received.
 *
 * @param   Notbuffer : a pointer to TL_EvtPacket_t
 * @return  None
 */
void TL_THREAD_NotReceived(TL_EvtPacket_t *Notbuffer) {
    p_thread_notif_M0_to_M4 = Notbuffer;

    Receive_Notification_From_M0();
}

/**
 * @brief  This function is called before sending any ot command to the M0
 *         core. The purpose of this function is to be able to check if
 *         there are no notifications coming from the M0 core which are
 *         pending before sending a new ot command.
 * @param  None
 * @retval None
 */
void Pre_OtCmdProcessing(void) {

}

/**
 * @brief  This function waits for getting an acknowledgment from the M0.
 *
 * @param  None
 * @retval None
 */
static void Wait_Getting_Ack_From_M0(void) {
    while (FlagReceiveAckFromM0 == 0) {
    }
    FlagReceiveAckFromM0 = 0;
}

/**
 * @brief  Receive an acknowledgment from the M0+ core.
 *         Each command send by the M4 to the M0 are acknowledged.
 *         This function is called under interrupt.
 * @param  None
 * @retval None
 */
static void Receive_Ack_From_M0(void) {
    FlagReceiveAckFromM0 = 1;
}

/**
 * @brief  Receive a notification from the M0+ through the IPCC.
 *         This function is called under interrupt.
 * @param  None
 * @retval None
 */
static void Receive_Notification_From_M0(void) {
    CptReceiveMsgFromM0++;
    osThreadFlagsSet(OsTaskMsgM0ToM4Id, 1);
}

static void ot_StatusNot(ot_TL_CmdStatus_t status) {
    switch (status) {
    case ot_TL_CmdBusy:
        osMutexAcquire(MtxOtCmdId, osWaitForever);
        break;

    case ot_TL_CmdAvailable:
        osMutexRelease(MtxOtCmdId);
        break;

    default:
        break;
    }
    return;
}

#if (CFG_USB_INTERFACE_ENABLE != 0)
#else
#if (CFG_FULL_LOW_POWER == 0)
static void RxCpltCallback(void)
{
  /* Filling buffer and wait for '\r' char */
  if (indexReceiveChar < C_SIZE_CMD_STRING)
  {
    CommandString[indexReceiveChar++] = aRxBuffer[0];
    if (aRxBuffer[0] == '\r')
    {
      CptReceiveCmdFromUser = 1U;

      /* UART task scheduling*/
      osThreadFlagsSet(OsTaskCliId,1);
    }
  }

  /* Once a character has been sent, put back the device in reception mode */
  HW_UART_Receive_IT(CFG_CLI_UART, aRxBuffer, 1U, RxCpltCallback);
}
#endif /* (CFG_FULL_LOW_POWER == 0) */
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

#if (CFG_USB_INTERFACE_ENABLE != 0)
/**
 * @brief Process the command strings.
 *        As soon as a complete command string has been received, the task
 *        in charge of sending the command to the M0 is scheduled
 * @param  None
 * @retval None
 */
static uint32_t  ProcessCmdString( uint8_t* buf , uint32_t len )
{
  uint32_t i,j,tmp_start;
  tmp_start = 0;
  uint32_t res = 0;

  i= 0;
  while ((buf[i] != '\r') && (i < len))
  {
    i++;
  }

  if (i != len)
  {
    memcpy(CommandString, buf,(i+1));
    indexReceiveChar = i + 1U; /* Length of the buffer containing the command string */
    osThreadFlagsSet(OsTaskCliId,1)
    tmp_start = i;
    for (j = 0; j < (len - tmp_start - 1U) ; j++)
    {
      buf[j] = buf[tmp_start + j + 1U];
    }
    res = len - tmp_start - 1U;
  }
  else
  {
    res = len;
  }
  return res; /* Remaining characters in the temporary buffer */
}
#endif/* (CFG_USB_INTERFACE_ENABLE != 0) */

#if (CFG_FULL_LOW_POWER == 0)
/**
 * @brief Process sends receive CLI command to M0.
 * @param  None
 * @retval None
 */
static void Send_CLI_To_M0(void)
{
  memset(ThreadCliCmdBuffer.cmdserial.cmd.payload, 0x0U, 255U);
  memcpy(ThreadCliCmdBuffer.cmdserial.cmd.payload, CommandString, indexReceiveChar);
  ThreadCliCmdBuffer.cmdserial.cmd.plen = indexReceiveChar;
  ThreadCliCmdBuffer.cmdserial.cmd.cmdcode = 0x0;

  /* Clear receive buffer, character counter and command complete */
  CptReceiveCmdFromUser = 0;
  indexReceiveChar = 0;
  memset(CommandString, 0, C_SIZE_CMD_STRING);

  TL_CLI_SendCmd();
}
#endif /* (CFG_FULL_LOW_POWER == 0) */

/**
 * @brief Send notification for CLI TL Channel.
 * @param  None
 * @retval None
 */
static void Send_CLI_Ack_For_OT(void) {

    /* Notify M0 that characters have been sent to UART */
    TL_THREAD_CliSendAck();
}

/**
 * @brief Perform initialization of CLI UART interface.
 * @param  None
 * @retval None
 */
void APP_THREAD_Init_UART_CLI(void) {
#if (CFG_FULL_LOW_POWER == 0)
  OsTaskCliId = osThreadNew(APP_THREAD_FreeRTOSSendCLIToM0Task, NULL,&ThreadCliProcess_attr);
#endif /* (CFG_FULL_LOW_POWER == 0) */

#if (CFG_USB_INTERFACE_ENABLE != 0)
#else
#if (CFG_FULL_LOW_POWER == 0)
  MX_LPUART1_UART_Init();
  HW_UART_Receive_IT(CFG_CLI_UART, aRxBuffer, 1, RxCpltCallback);
#endif /* (CFG_FULL_LOW_POWER == 0) */
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */
}

/**
 * @brief Perform initialization of TL for THREAD.
 * @param  None
 * @retval None
 */
void APP_THREAD_TL_THREAD_INIT(void) {
    ThreadConfigBuffer.p_ThreadOtCmdRspBuffer = (uint8_t*) &ThreadOtCmdBuffer;
    ThreadConfigBuffer.p_ThreadNotAckBuffer = (uint8_t*) ThreadNotifRspEvtBuffer;
    ThreadConfigBuffer.p_ThreadCliRspBuffer = (uint8_t*) &ThreadCliCmdBuffer;
    ThreadConfigBuffer.p_ThreadCliNotBuffer = (uint8_t*) &ThreadCliNotBuffer;

    TL_THREAD_Init(&ThreadConfigBuffer);
}

/**
 * @brief  This function is called when notification on CLI TL Channel from M0+ is received.
 *
 * @param   Notbuffer : a pointer to TL_EvtPacket_t
 * @return  None
 */
void TL_THREAD_CliNotReceived(TL_EvtPacket_t *Notbuffer) {
    TL_CmdPacket_t *l_CliBuffer = (TL_CmdPacket_t*) Notbuffer;
    uint8_t l_size = l_CliBuffer->cmdserial.cmd.plen;

    /* WORKAROUND: if string to output is "> " then respond directly to M0 and do not output it */
    if (strcmp((const char*) l_CliBuffer->cmdserial.cmd.payload, "> ") != 0) {
        /* Write to CLI UART */
#if (CFG_USB_INTERFACE_ENABLE != 0)
    VCP_SendData( l_CliBuffer->cmdserial.cmd.payload, l_size, HostTxCb);
#else
        HW_UART_Transmit_IT(CFG_CLI_UART, l_CliBuffer->cmdserial.cmd.payload, l_size, HostTxCb);
#endif /*USAGE_OF_VCP */
    } else {
        Send_CLI_Ack_For_OT();
    }
}

/**
 * @brief  End of transfer callback for CLI UART sending.
 *
 * @param   Notbuffer : a pointer to TL_EvtPacket_t
 * @return  None
 */
void HostTxCb(void) {
    Send_CLI_Ack_For_OT();
}

/**
 * @brief Process the messages coming from the M0.
 * @param  None
 * @retval None
 */
void APP_THREAD_ProcessMsgM0ToM4(void) {
    if (CptReceiveMsgFromM0 != 0) {
        /* If CptReceiveMsgFromM0 is > 1. it means that we did not serve all the events from the radio */
        if (CptReceiveMsgFromM0 > 1U) {
            APP_THREAD_Error(ERR_REC_MULTI_MSG_FROM_M0, 0);
        } else {
            OpenThread_CallBack_Processing();
        }
        /* Reset counter */
        CptReceiveMsgFromM0 = 0;
    }
}

#if (CFG_USB_INTERFACE_ENABLE != 0)
/**
 * @brief  This function is called when thereare some data coming
 *         from the Hyperterminal via the USB port
 *         Data received over USB OUT endpoint are sent over CDC interface
 *         through this function.
 * @param  Buf: Buffer of data received
 * @param  Len: Number of data received (in bytes)
 * @retval Number of characters remaining in the buffer and not yet processed
 */
void VCP_DataReceived(uint8_t* Buf , uint32_t *Len)
{
  uint32_t i,flag_continue_checking = TRUE;
  uint32_t char_remaining = 0;
  static uint32_t len_total = 0;

  /* Copy the characters in the temporary buffer */
  for (i = 0; i < *Len; i++)
  {
    TmpString[len_total++] = Buf[i];
  }

  /* Process the buffer commands one by one     */
  /* A command is limited by a \r caracaters    */
  while (flag_continue_checking == TRUE)
  {
    char_remaining = ProcessCmdString(TmpString,len_total);
    /* If char_remaining is equal to len_total, it means that the command string is not yet
     * completed.
     * If char_remaining is equal to 0, it means that the command string has
     * been entirely processed.
     */
    if ((char_remaining == 0) || (char_remaining == len_total))
    {
      flag_continue_checking = FALSE;
    }
    len_total = char_remaining;
  }
}
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */

/* USER CODE BEGIN FD_WRAP_FUNCTIONS */
/**
 * @brief  Init the NVM
 * @param  None
 * @retval None
 */
static void APP_THREAD_NVM_Init(void) {
    int eeprom_init_status;

    /* Take semaphore to make sure there will be no modification of the NVM Ram buffer during Flash operation */
    while (LL_HSEM_1StepLock( HSEM, CFG_HW_THREAD_NVM_SRAM_SEMID))
        ;

    APP_DBG("Flash starting address = %x", HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS);
    eeprom_init_status = EE_Init(0, HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS);

    if (eeprom_init_status != EE_OK) {
        /* format NVM since init failed */
        eeprom_init_status = EE_Init(1, HW_FLASH_ADDRESS + CFG_NVM_BASE_ADDRESS);
    }
    APP_DBG("EE_init status = %d", eeprom_init_status);

    /* Retrieve data from NVM to RAM buffer */
    APP_DBG("Retrieving persistent data from FLASH");
    if (APP_THREAD_NVM_Read() == false) {
        APP_DBG("APP_ZIGBEE_NVM_Read failed!");
    }

    /* Release semaphore to allow operation of NVM Ram buffer on CPU2 side */
    LL_HSEM_ReleaseLock( HSEM, CFG_HW_THREAD_NVM_SRAM_SEMID, 0);
}

/**
 *@brief  Read the persistent data from NVM
 * @param  None
 * @retval true if success , false if failed
 */
static bool APP_THREAD_NVM_Read(void) {
    bool status = true;
//  int ee_status = 0;
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGSERR | FLASH_FLAG_WRPERR | FLASH_FLAG_OPTVERR);

    /**
     * In the current application, the NVMA_Read() is used only to dump the NVM to the SRAM
     * at startup.
     *
     * In case there are some use cases where the application needs to read only 1 data,
     * a test versus the Size needs to be implemented
     * The algorithm to use depends on the way the application stores the data in NVM but most of the time,
     * EE_Read() should be used when Size = 1 and EE_Dump() otherwise
     */
    EE_Dump(0, 0, OT_NvmRamBuffer, CFG_EE_BANK0_MAX_NB);

//  for (uint16_t index = 0; index < CFG_THREAD_NVM_RAM_SIZE; index++)
//  {
//    ee_status = EE_Read(0, index, &OT_NvmRamBuffer[index]);
//    if (ee_status != EE_OK)
//    {
//      APP_DBG("Read not found leaving");
//      status = false;
//      break;
//    }
//  }

    HAL_FLASH_Lock();
    return status;
}

/**
 * @brief  Write the persistent data in NVM
 * @param  None
 * @retval None
 */
static bool APP_THREAD_NVM_Write(void) {
    int ee_status = 0;
    int index = 0;

    /* Take semaphore to make sure there will be no modification of the NVM Ram buffer during Flash operation */
    while (LL_HSEM_1StepLock( HSEM, CFG_HW_THREAD_NVM_SRAM_SEMID))
        ;

    for (index = 0; index < CFG_EE_BANK0_MAX_NB; index++) {
        ee_status = EE_Write(0, (uint16_t) index, OT_NvmRamBuffer[index]);
        if (ee_status != EE_OK) {
            if (ee_status == EE_CLEAN_NEEDED) /* Shall not be there if CFG_EE_AUTO_CLEAN = 1*/
            {
                APP_DBG("CLEAN NEEDED, CLEANING");
                EE_Clean(0, 0);
            } else {
                /* Failed to write , an Erase shall be done */
                APP_DBG("APP_THREAD_NVM_Write failed @ %d status %d", index, ee_status);
                break;
            }
        }
    }

    /* Release semaphore to allow operation of NVM Ram buffer on CPU2 side */
    LL_HSEM_ReleaseLock( HSEM, CFG_HW_THREAD_NVM_SRAM_SEMID, 0);

    if (ee_status != EE_OK) {
        APP_DBG("WRITE STOPPED, need a FLASH ERASE");
        return false;
    }

    return true;
}

static void APP_THREAD_NvmSave(void) {
    APP_DBG("--- APP_THREAD_NvmSave START ---");
    if (APP_THREAD_NVM_Write() == true) {
        APP_DBG("APP_THREAD_NVM_Write SUCCESS!");
    } else {
        APP_DBG("APP_THREAD_NVM_Write FAIL!");
    }
    APP_DBG("--- APP_THREAD_NvmSave END ---");
}
/* USER CODE END FD_WRAP_FUNCTIONS */

app_entry.c - Relevant part only

static void APPE_SysEvtReadyProcessing(void)
{
  /* Traces channel initialization */
  TL_TRACES_Init();
  /* Configuration to CPU2 */
  SHCI_C2_CONFIG_Cmd_Param_t config_param = {0};
  /* Set the address that will be used by OT stack for NVM data management */
  config_param.ThreadNvmRamAddress = (uint32_t)OT_NvmRamBuffer;
  (void)SHCI_C2_Config(&config_param);

  APP_THREAD_Init();
  UTIL_LPM_SetStopMode(1 << CFG_LPM_APP, UTIL_LPM_ENABLE);
  UTIL_LPM_SetOffMode(1U << CFG_LPM_APP, UTIL_LPM_ENABLE);
  return;
}
RJMSTM commented 9 months ago

ST Internal Reference: 172491

RJMSTM commented 9 months ago

Hello @agalliazzo,

Excuse this delayed reply. Our development teams are already having a look to what you reported SRP is normally working fine but there are two important comments when looking at the code:

Replace this: PLACE_IN_SECTION("MB_MEM2") uint32_t OT_NvmRamBuffer[CFG_THREAD_NVM_RAM_SIZE_IN_BYTES / 4];

by this: uint32_t OT_NvmRamBuffer[CFG_THREAD_NVM_RAM_SIZE_IN_BYTES / 4];

If you save just data in NVM just after being connected to the leader which is SRP server, but before SRP registration, it will fail on reboot and I get error 28 (OT_ERROR_RESPONSE_TIMEOUT). This is due to the key not being the good one, as it wasn't saved in NVM.

If you save after SRP registration, it works fine every time. you can sometimes get the error 29 (OT_ERROR_DUPLICATED) as SRP registration already exist on server's side but it immediately gives a retry knowing that and is successful.

which error you gets?

With Regards, Rania

RJMSTM commented 8 months ago

Hello,

Any feedback please!!

RJMSTM commented 7 months ago

Hello @agalliazzo,

Please allow me to close this issue as no activity. You may reopen it at any time if you have any details to share with us in order to help you to solve the issue. Thank you for your comprehension.

With regards