Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Nordic IPv6 ok but lwIP routing problem ERR_RTE

Hi,
I tested a simple client-server UDP application, using as a template the UDP examples from the SDK. The UDP server is running in a nRF52 board and the client is a Python code (extracted from the Nordic infocenter) running in a Windows PC.
For my testing I have a local network with private address as shown in the picture:

This configuration was extracted from a tutorial that I found in this link:
visualgdb.com/.../

When I tested with the nordic ipv6 stack the application behaves without any problems: I can perform a ping from the Windows PC to the nRF52 board, and the UDP client (python) running in the PC Windows communicates perfectly with the nRF52 board.

But, when I use the lwIP stack:
1.- I can ping from the Windows PC to the nRF52 board without problems.
2.- When I send a packet from the PC client, the nRF52 board recive the packet, but when replaying with "udp_sendto" it returns ERR_RTE (-4) (Routing problem.)
3.- When I send the same packet, but this time with the Client running in the Raspberry Pi (using the same python code), the UDP communication work perfectly.

The problem seems to be related to a routing/subnet network address. The Windows PC is in a diferent network (fd00:b::) than the nRF52 board (fd00:a::), so the lwIP stack return the "routing problem" error. When I run the client in the Raspberry Pi it must be using the "bt0" (fd00:a::1) interface to comunicate to the nRF52 board, that is in the same network, so it work ok.

My question is, why the nordic ipv6 works ok and the lwIP have this issue? How can I address this issue in order to work with lwIP?

My setup:
- Raspberry Pi 3 B+, with Ubuntu MATE (18.04.2 LTS (Bionic Beaver) kernel 4.15.0-1032-raspi2)
- Custom Board with nRF52840
- SDK v16.0.0.
- Softdevice s140 v7.0.1
- Windows 10 (with Python 2.7.15)
- Visual Studio Code / gcc

Thank in advance,
Best Regards,

José Forno

main code:

/**
 * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
 *
 * 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 Nordic
 *    Semiconductor ASA 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 Nordic Semiconductor ASA 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, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "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 NORDIC SEMICONDUCTOR ASA 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
 * @defgroup nrf_adc_example main.c
 * @{
 * @ingroup nrf_adc_example
 * @brief ADC Example Application main file.
 *
 * This file contains the source code for a sample application using ADC.
 *
 * @image html example_board_setup_a.jpg "Use board setup A for this example."
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include <math.h>

// New includes from bsp-led-cli test code to this one (saadc test code)
#include "bsp.h"
#include "nrf_drv_clock.h"
#include "app_timer.h"
#include "app_specific.h"

// New includes for FDS
// #include "fds.h"
#include "app_fds.h"
#include "app_cli.h"
#include "app_saadc.h"

// New includes for softdevice
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"

// New include for BLE Advertising and connection
#include "nrf_ble_qwr.h"
#include "ble_advdata.h"
#include "ble_conn_params.h"
#include "nrf_ble_gatt.h"

// New include for BLE with services
#include "ble_lbs.h"
#include "ble_ldrs.h"

// New include for ICMP
#include "ipv6_api.h"
#include "icmp6_api.h"
#include "udp_api.h"
#include "iot_timer.h"
#include "ipv6_medium.h"
#include "iot_timer.h"
#include "app_scheduler.h"

// New include for IwIP
#include "lwip/init.h"
#include "lwip/inet6.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
#include "lwip/netif.h"
/*lint -save -e607 Suppress warning 607 "Parameter p of macro found within string" */
#include "lwip/udp.h"
/*lint -restore */
#include "lwip/timers.h"
#include "nrf_platform_port.h"


/*
***************************************************************************
                    MAIN PREPROCESOR DIRECTIVES
***************************************************************************
*/
#define BLE_LIBRARY_ENABLE          0
#define IOT_LIBRARY_ENABLE          1

#define APP_STATE_FATAL             1000
#define MAX_AUTOMATIC_GC_ATTEMPTS   3


/*
***************************************************************************
                    SCHEDULER PREPROCESOR DIRECTIVES
***************************************************************************
*/
#define SCHED_QUEUE_SIZE              16                                        /**< Maximum number of events in the scheduler queue. */
#define SCHED_MAX_EVENT_DATA_SIZE     192                                       /**< Maximum size of scheduler events. */


/*
***************************************************************************
                    APP TIMER LIBRARY PREPROCESOR DIRECTIVES
***************************************************************************
*/
// Timer for task interval
#define TIMER_TASK_INTERVAL_MS     10000
APP_TIMER_DEF(m_timer_task_interval);

/*
***************************************************************************
                    BSP LIBRARY PREPROCESOR DIRECTIVES
***************************************************************************
*/
#define BSP_LIBRARY_ENABLE  0
#define BUTTON_LED          BSP_BOARD_LED_2
// APP BUTTON Bebounce
#define BUTTON_DETECTION_DELAY          APP_TIMER_TICKS(50)                     /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */

/*
***************************************************************************
                    SOFTDEVICE PREPROCESOR DIRECTIVES
***************************************************************************
*/
#if BLE_LIBRARY_ENABLE

#define APP_BLE_CONN_CFG_TAG            1

// Directives for register a BLE observer
#define APP_BLE_OBSERVER_PRIO           3                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */

// Directives for GAP parameters, For gap_params_init()
#define DEVICE_NAME                     "NodeChincol"                          /**< Name of device. Will be included in the advertising data. */
#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.5 seconds). */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (1 second). */
#define SLAVE_LATENCY                   0                                       /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory time-out (4 seconds). */

// For advertising_init()
#define APP_ADV_INTERVAL                64                                      /**< The advertising interval (in units of 0.625 ms; this value corresponds to 40 ms). */
#define APP_ADV_DURATION                BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED   /**< The advertising time-out (in units of seconds). When set to 0, we will never time out. */

// For conn_params_init()
#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(20000)                  /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (15 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(5000)                   /**< Time between each call to sd_ble_gap_conn_param_update after the first call (5 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */

// For advertising_start()
#define ADVERTISING_LED                 BSP_BOARD_LED_0                         /**< Is on when device is advertising. */
#define CONNECTED_LED                   BSP_BOARD_LED_1                         /**< Is on when device has connected. */
// For led_write_handler()
#define SERVICE_LED                     BSP_BOARD_LED_2
#define SERVICE_BUTTON                  BSP_BUTTON_0                            /**< Button that will trigger the notification event with the LED Button Service */

// GATT module instance
NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
// Queue write module instance
NRF_BLE_QWR_DEF(m_qwr);
// LDR Service instances
BLE_LDRS_DEF(m_ldrs);
BLE_LBS_DEF(m_lbs);

// For softdevice assert
#define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#endif //#if BLE_LIBRARY_ENABLE

/*
***************************************************************************
                    IOT PREPROCESOR DIRECTIVES
***************************************************************************
*/
#if IOT_LIBRARY_ENABLE

// *** First Step (from ICMP example)

// For Softdevice assert
#define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

// For LOGs
#define APPL_LOG    NRF_LOG_INFO
#define APPL_DUMP   NRF_LOG_RAW_HEXDUMP_INFO
#define APPL_ADDR   IPV6_ADDRESS_LOG

// *** Second Step (from UDP server example)
#define UDP_PORT                      9000

// Timer for IoT (IwIP)
APP_TIMER_DEF(m_iot_timer_tick_src_id);

#define LWIP_SYS_TICK_MS            100             /**< Interval for timer used as trigger to send. */

#endif //IOT_LIBRARY_ENABLE


/*
***************************************************************************
                    MAIN APPLICATION GLOBALS
***************************************************************************
*/
volatile bool m_wake_up = false;        // UTILIZAR MUTEX!!
static uint16_t m_main_state = 0;

// *********************** BLE Globals ************************************
#if BLE_LIBRARY_ENABLE

static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;                        /**< Handle of the current connection. */
static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;                   /**< Advertising handle used to identify an advertising set. */
static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX];                    /**< Buffer for storing an encoded advertising set. */
static uint8_t m_enc_scan_response_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];         /**< Buffer for storing an encoded scan data. */

volatile bool m_ble_connected = false;                                          // Connection Flag

/**@brief Struct that contains pointers to the encoded advertising data. */
static ble_gap_adv_data_t m_adv_data =
{
    .adv_data =
    {
        .p_data = m_enc_advdata,
        .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    },
    .scan_rsp_data =
    {
        .p_data = m_enc_scan_response_data,
        .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX

    }
};

#endif //#if BLE_LIBRARY_ENABLE

// *********************** IoT Globals ************************************
#if IOT_LIBRARY_ENABLE

// *** First Step (From ICMP example)
eui64_t                       eui64_local_iid;
//static iot_interface_t      * mp_interface = NULL;    // NOT USED WITH IWIP
static ipv6_medium_instance_t m_ipv6_medium;

// *** Second Step (From UDP Server example)
//static udp6_socket_t            m_udp_socket; // NOT USED WITH IWIP
volatile nrf_saadc_value_t      m_adcValue_UDP;


// For IwIP Stack
static struct udp_pcb           *mp_udp_port;       /**< UDP Port to listen on. */

#endif //IOT_LIBRARY_ENABLE


/*
*********************************************************************
                    SCHEDULER LIBRARY MODULE
*********************************************************************
*/

/**@brief Function for the Event Scheduler initialization.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}


/*
*********************************************************************
                    BSP LIBRARY MODULE
*********************************************************************
*/

#if BSP_LIBRARY_ENABLE
/**@brief Function for handling bsp events.
 */
void bsp_evt_handler(bsp_event_t evt)
{
    ret_code_t err_code;
    bsp_event_t x;
    
    switch (evt)
    {
        case BSP_EVENT_KEY_0:
            //bsp_board_led_invert(BUTTON_LED);
            NRF_LOG_INFO("Send button state change.");
            
            err_code = ble_lbs_on_button_change(m_conn_handle, &m_lbs, button_action);
            if (err_code != NRF_SUCCESS &&
                err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
                err_code != NRF_ERROR_INVALID_STATE &&
                err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            return; // no implementation needed
    }
    
    NRF_LOG_INFO("Evento %i", evt);
}

/**@brief Function for initializing bsp module.
 */
void bsp_configuration()
{
    uint32_t err_code;

    err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_evt_handler);
    APP_ERROR_CHECK(err_code);
}
#else


#if BLE_LIBRARY_ENABLE
/*
*********************************************************************
             LED AND BUTTON LIBRARY MODULE (FOR BLE EXAMPLE)
*********************************************************************
*/
/**@brief Function for the LEDs initialization.
 *
 * @details Initializes all LEDs used by the application.
 */
static void leds_init(void)
{
    bsp_board_init(BSP_INIT_LEDS);
}

/**@brief Function for handling events from the button handler module.
 *
 * @param[in] pin_no        The pin that the event applies to.
 * @param[in] button_action The button action (press/release).
 */
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    ret_code_t err_code;

    switch (pin_no)
    {
        case SERVICE_BUTTON:
            NRF_LOG_INFO("Send button state change.");
            err_code = ble_lbs_on_button_change(m_conn_handle, &m_lbs, button_action);
            if (err_code != NRF_SUCCESS &&
                err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
                err_code != NRF_ERROR_INVALID_STATE &&
                err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            APP_ERROR_HANDLER(pin_no);
            break;
    }
}


/**@brief Function for initializing the button handler module.
 */
static void buttons_init(void)
{
    ret_code_t err_code;

    //The array must be static because a pointer to it will be saved in the button handler module.
    static app_button_cfg_t buttons[] =
    {
        {SERVICE_BUTTON, false, BUTTON_PULL, button_event_handler}
    };

    err_code = app_button_init(buttons, ARRAY_SIZE(buttons),
                               BUTTON_DETECTION_DELAY);
    APP_ERROR_CHECK(err_code);
}

#endif //#if BLE_LIBRARY_ENABLE

#endif //#if BSP_LIBRARY_ENABLE

/*
*********************************************************************
                            CLOCK DRIVER
*********************************************************************
*/
/**@brief Function for initializing HFCLK and LFCLK clock both with
 * external Xtal.
 */
void clock_init(int32_t *tryHF, int32_t *tryLF)
{
    ret_code_t ret;

    // INIT CLOCK DRIVER AND SET XTAL IN BOTH HFCLK AND LFCLK
    ret = nrf_drv_clock_init();
    APP_ERROR_CHECK(ret);
    nrf_drv_clock_hfclk_request(NULL);  // Use the HFXO (exteral xtal)
    // Wait untel HFCLK is running on external xtal
    *tryHF = 0;
    while(!nrf_drv_clock_hfclk_is_running())
    {
        nrf_delay_ms(100);
        if((*tryHF)++ > 20) break;  // If 2 sec waiting then exit and give up, but the request is still on, so it has to happend some time
    }

#ifndef SOFTDEVICE_PRESENT
    nrf_drv_clock_lfclk_request(NULL);  // Use the LFXO (external xtal)
    // Wait until LFCLK is running
    *tryLF = 0;
    while(!nrf_drv_clock_lfclk_is_running())
    {
        nrf_delay_ms(100);
        if((*tryLF)++ > 20) break;  // If 2 sec waiting then exit and give up, but the request is still on, so it has to happend some time
    }
#endif
}

/*
*********************************************************************
                            APP TIMER LIBRARY
*********************************************************************
*/

/*
* Brief: App Timer handler for main sleep interval
*/
void app_timer_task_interval_handler(void *p_context)
{
    m_wake_up = true;
}

/*
*********************************************************************
                            SOFTDEVICE
*********************************************************************
*/

#if BLE_LIBRARY_ENABLE

/**@brief Function for assert macro callback.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num    Line number of the failing ASSERT call.
 * @param[in] p_file_name File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Function for the GAP initialization.
 *
 * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
 *          device including the device name, appearance, and the preferred connection parameters.
 */
static void gap_params_init(void)
{
    ret_code_t              err_code;
    ble_gap_conn_params_t   gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *)DEVICE_NAME,
                                          strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);

    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the GATT module.
 */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the Advertising functionality.
 *
 * @details Encodes the required advertising data and passes it to the stack.
 *          Also builds a structure to be passed to the stack when starting advertising.
 */
static void advertising_init(void)
{
    ret_code_t    err_code;
    ble_advdata_t advdata;
    ble_advdata_t srdata;

    //ble_uuid_t adv_uuids[] = {{LDR_SERVICE_UUID, BLE_UUID_TYPE_VENDOR_BEGIN}}; /**< Universally unique service identifiers. */
    ble_uuid_t adv_uuids[] = {{LBS_UUID_SERVICE, m_lbs.uuid_type}};

    // Build and set advertising data.
    memset(&advdata, 0, sizeof(advdata));
    advdata.name_type          = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = true;
    advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    memset(&srdata, 0, sizeof(srdata));
    srdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
    srdata.uuids_complete.p_uuids  = adv_uuids;

    err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);

    err_code = ble_advdata_encode(&srdata, m_adv_data.scan_rsp_data.p_data, &m_adv_data.scan_rsp_data.len);
    APP_ERROR_CHECK(err_code);

    ble_gap_adv_params_t adv_params;

    // Set advertising parameters.
    memset(&adv_params, 0, sizeof(adv_params));

    adv_params.primary_phy     = BLE_GAP_PHY_1MBPS;
    adv_params.duration        = APP_ADV_DURATION;
    adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
    adv_params.p_peer_addr     = NULL;
    adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    adv_params.interval        = APP_ADV_INTERVAL;

    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &adv_params);
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for handling Queued Write Module errors.
 *
 * @details A pointer to this function will be passed to each service which may need to inform the
 *          application about an error.
 *
 * @param[in]   nrf_error   Error code containing information about what went wrong.
 */
static void nrf_qwr_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}

/**@brief Function for handling write events to the LED characteristic.
 *
 * @param[in] p_lbs     Instance of LED Button Service to which the write applies.
 * @param[in] led_state Written/desired state of the LED.
 */
static void led_write_handler(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t led_state)
{
    if (led_state)
    {
        bsp_board_led_on(SERVICE_LED);
        NRF_LOG_INFO("Received LED ON!");
    }
    else
    {
        bsp_board_led_off(SERVICE_LED);
        NRF_LOG_INFO("Received LED OFF!");
    }
}


/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    ret_code_t          err_code;
    nrf_ble_qwr_init_t  qwr_init = {0};
    ble_lbs_init_t      init     = {0};

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

    // Initialize LBS.
    init.led_write_handler = led_write_handler;

    err_code = ble_lbs_init(&m_lbs, &init);
    APP_ERROR_CHECK(err_code);

    // Initializa LDRS
    err_code = ble_ldrs_init(&m_ldrs);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling the Connection Parameters Module.
 *
 * @details This function will be called for all events in the Connection Parameters Module that
 *          are passed to the application.
 *
 * @note All this function does is to disconnect. This could have been done by simply
 *       setting the disconnect_on_fail config parameter, but instead we use the event
 *       handler mechanism to demonstrate its use.
 *
 * @param[in] p_evt  Event received from the Connection Parameters Module.
 */
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    ret_code_t err_code;

    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for handling a Connection Parameters error.
 *
 * @param[in] nrf_error  Error code containing information about what went wrong.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for initializing the Connection Parameters module.
 */
static void conn_params_init(void)
{
    ret_code_t             err_code;
    ble_conn_params_init_t cp_init;

    memset(&cp_init, 0, sizeof(cp_init));

    cp_init.p_conn_params                  = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.evt_handler                    = on_conn_params_evt;
    cp_init.error_handler                  = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for starting advertising.
 */
static void advertising_start(void)
{
    ret_code_t           err_code;

    err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
    APP_ERROR_CHECK(err_code);

    bsp_board_led_on(ADVERTISING_LED);
}

/**@brief Function for starting/stopping advertising used by CLI commands
 */
bool advertising_operation(bool op_start)
{
    ret_code_t           err_code;
    bool resp = true;

    if(op_start)
    {
        err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
        if(err_code == NRF_ERROR_INVALID_STATE) resp = false;
        else
        {
            APP_ERROR_CHECK(err_code);
            bsp_board_led_on(ADVERTISING_LED);
        }
    }
    else
    {
        err_code = sd_ble_gap_adv_stop(m_adv_handle);
        if(err_code == NRF_ERROR_INVALID_STATE) resp = false;
        else 
        {
            APP_ERROR_CHECK(err_code);
            bsp_board_led_off(ADVERTISING_LED);
        }
    }
    
    return resp;
}

/**@brief Function for disconnecting used by CLI commands
 */
bool connection_operation(void)
{
    ret_code_t           err_code;
    bool resp = true;

    if(m_conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        // Not yet connected
        resp = false;
    }
    else
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
        if(err_code == NRF_ERROR_INVALID_STATE) resp = false;
        else 
        {
            APP_ERROR_CHECK(err_code);
        }
    }
    
    return resp;
}

/**@brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 * @param[in]   p_context   Unused.
 */
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            m_ble_connected = true;
            NRF_LOG_INFO("Connected");
            bsp_board_led_on(CONNECTED_LED);
            bsp_board_led_off(ADVERTISING_LED);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
            APP_ERROR_CHECK(err_code);
            err_code = app_button_enable();
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            m_ble_connected = false;
            NRF_LOG_INFO("Disconnected");
            bsp_board_led_off(CONNECTED_LED);
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
            err_code = app_button_disable();
            APP_ERROR_CHECK(err_code);
            advertising_start();
            break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            // Pairing not supported
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
                                                   BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
                                                   NULL,
                                                   NULL);
            APP_ERROR_CHECK(err_code);
            break;
        
        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            // No system attributes have been stored.
            err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT Client Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_TIMEOUT:
            // Disconnect on GATT Server timeout event.
            NRF_LOG_DEBUG("GATT Server Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;

        default:
            // No implementation needed.
            break;
    }
}

static void ble_stack_init(void)
{
    ret_code_t rc;
    uint32_t   ram_start;

    /* Enable the SoftDevice. */
    rc = nrf_sdh_enable_request();
    APP_ERROR_CHECK(rc);

    rc = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(rc);

    rc = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(rc);

    NRF_LOG_INFO("BLE Stack Init OK. RAM Start at: %u (%04X)", ram_start, ram_start);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

#else

/*
**@brief DUMMY Function used by CLI commands (to avoid error 'cause no BLE code used)
 */
bool connection_operation(void)
{
    return true;
}

/**@brief DUMMY Function used by CLI commands (to avoid error 'cause no BLE code used)
 */
bool advertising_operation(bool op_start)
{
    return true;
}

#endif //#if BLE_LIBRARY_ENABLE

/*
*********************************************************************
                            IoT LIBRARY
*********************************************************************
*/

#if IOT_LIBRARY_ENABLE


/**@brief Callback registered with UDP module to handle incoming data on the port.
 *
 * @details Callback registered with UDP module to handle incoming data on the port.
 *
 * @param[in] p_arg          Any arguments registered by application on the port.
 * @param[in] p_pcb          Identifies the UDP port on which data is received.
 * @param[in] p_buffer       Buffer containing data and its length.
 * @param[in] p_remote_addr  Address of the remote side from which data was received.
 * @param[in] remote_port    Remote port number used when sending the data to the application port.
 */
void udp_recv_data_handler(void              * p_arg,
                           struct udp_pcb    * p_pcb,
                           struct pbuf       * p_buffer,
                           const ip6_addr_t  * p_remote_addr,
                           u16_t               remote_port)
{
    //uint8_t *p_tmp = p_buffer->payload;

    APPL_LOG (">> UDP Data Rx on Port 0x%08X", remote_port);
    APPL_LOG (">> UDP packet sequence number: %ld",
             uint32_decode((uint8_t *) p_buffer->payload));

    struct pbuf * p_send_buf;
    uint8_t     * p_udp_data;

    // Allocate packet buffer.
    p_send_buf = pbuf_alloc (PBUF_TRANSPORT, p_buffer->len, PBUF_RAM);
    ASSERT(p_send_buf != NULL);
    p_send_buf->len = p_buffer->len;
    p_udp_data = p_send_buf->payload;

    // Copy the request to the response
    memcpy(p_send_buf->payload, p_buffer->payload, p_buffer->len);

    // Insert ADC value after sequence number, only if there's enough space (4 + 2)
    if(p_buffer->len > 6)
    {
        uint16_encode(m_adcValue_UDP, p_udp_data + 4);
        APPL_LOG("Sended UDP packet ADC value: %d",
             uint16_decode(&p_udp_data[4]));
    }

    // Send back the received packet payload without any modification with ADC inserted
    err_t err = udp_sendto(mp_udp_port, p_send_buf, p_remote_addr, remote_port);
    if(err != ERR_OK)
    {
        APPL_LOG("ERROR in udp send: %d", err)
    }

    APP_ERROR_CHECK(err);

    UNUSED_VARIABLE(pbuf_free(p_send_buf));

    //APPL_DUMP(p_send_buf->payload,p_send_buf->len);
    APPL_LOG ("<< UDP Data TX");

    //APPL_LOG ("<< UDP Data Rx on Port 0x%08X", remote_port);
}

/**@brief UDP Port Set-Up.
 *
 * @details Sets up UDP Port to listen on.
 */
static void udp_port_setup(void)
{
    ip6_addr_t any_addr;
    ip6_addr_set_any(&any_addr);

    mp_udp_port = udp_new();

    if (mp_udp_port != NULL)
    {
        err_t err = udp_bind(mp_udp_port, &any_addr, UDP_PORT);
        APP_ERROR_CHECK(err);

        udp_recv(mp_udp_port,udp_recv_data_handler, NULL);
    }
    else
    {
        ASSERT(0);
    }
}

/**@brief Timer callback used for periodic servicing of LwIP protocol timers.
 *
 * @details Timer callback used for periodic servicing of LwIP protocol timers.
 *
 * @param[in]   p_context   Pointer used for passing context. No context used in this application.
 */
static void system_timer_callback(iot_timer_time_in_ms_t wall_clock_value)
{
    UNUSED_VARIABLE(wall_clock_value);
    sys_check_timeouts();
}


/**@brief Function for updating the wall clock of the IoT Timer module.
 */
static void iot_timer_tick_callback(void * p_context)
{
    UNUSED_VARIABLE(p_context);

    uint32_t err_code = iot_timer_update();
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the IoT Timer. */
static void iot_timer_init(void)
{
    uint32_t err_code;

    static const iot_timer_client_t list_of_clients[] =
    {
        {system_timer_callback,   LWIP_SYS_TICK_MS},
#ifdef COMMISSIONING_ENABLED
        {blink_timeout_handler,   LED_BLINK_INTERVAL_MS},
        {commissioning_time_tick, SEC_TO_MILLISEC(COMMISSIONING_TICK_INTERVAL_SEC)}
#endif // COMMISSIONING_ENABLED
    };

    // The list of IoT Timer clients is declared as a constant.
    static const iot_timer_clients_list_t iot_timer_clients =
    {
        (sizeof(list_of_clients) / sizeof(iot_timer_client_t)),
        &(list_of_clients[0]),
    };

    // Passing the list of clients to the IoT Timer module.
    err_code = iot_timer_client_list_set(&iot_timer_clients);
    APP_ERROR_CHECK(err_code);

    // Starting the app timer instance that is the tick source for the IoT Timer.
    err_code = app_timer_start(m_iot_timer_tick_src_id,
                               APP_TIMER_TICKS(IOT_TIMER_RESOLUTION_IN_MS),
                               NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function to handle interface up event. */
void nrf_driver_interface_up(iot_interface_t const * p_interface)
{
    UNUSED_PARAMETER(p_interface);

#ifdef COMMISSIONING_ENABLED
    commissioning_joining_mode_timer_ctrl(JOINING_MODE_TIMER_STOP_RESET);
#endif // COMMISSIONING_ENABLED

    APPL_LOG ("IPv6 interface up.");

    sys_check_timeouts();

    //LEDS_OFF(LED_ONE);
    //LEDS_ON(LED_TWO);
}


/**@brief Function to handle interface down event. */
void nrf_driver_interface_down(iot_interface_t const * p_interface)
{
    UNUSED_PARAMETER(p_interface);

#ifdef COMMISSIONING_ENABLED
    commissioning_joining_mode_timer_ctrl(JOINING_MODE_TIMER_START);
#endif // COMMISSIONING_ENABLED

    APPL_LOG ("IPv6 interface down.");

    //LEDS_ON(LED_ONE);
    //LEDS_OFF(LED_TWO);
}


/*
******************************************
**** First Step (From ICMP exaplmple) ****
******************************************
*/

/**@brief Callback function for asserts in the SoftDevice.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in]   line_num   Line number of the failing ASSERT call.
 * @param[in]   file_name  File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Function for starting connectable mode.
 */
static void connectable_mode_enter(void)
{
    uint32_t err_code = ipv6_medium_connectable_mode_enter(m_ipv6_medium.ipv6_medium_instance_id);
    APP_ERROR_CHECK(err_code);

    APPL_LOG("Physical layer in connectable mode.");
    //m_disp_state = LEDS_CONNECTABLE_MODE;
}



/**@brief Function for initializing IP stack.
 *
 * @details Initialize the IP Stack and its driver.
 */
static void ip_stack_init(void)
{
    uint32_t err_code;
    err_code = ipv6_medium_eui64_get(m_ipv6_medium.ipv6_medium_instance_id,
                                     &eui64_local_iid);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_mem_init();
    APP_ERROR_CHECK(err_code);

    //Initialize LwIP stack.
    lwip_init();

    //Initialize LwIP stack driver.
    err_code = nrf_driver_init();
    APP_ERROR_CHECK(err_code);
}


static void on_ipv6_medium_evt(ipv6_medium_evt_t * p_ipv6_medium_evt)
{
    switch (p_ipv6_medium_evt->ipv6_medium_evt_id)
    {
        case IPV6_MEDIUM_EVT_CONN_UP:
        {
            APPL_LOG("Physical layer connected.");
            //m_disp_state = LEDS_IPV6_IF_DOWN;
            break;
        }
        case IPV6_MEDIUM_EVT_CONN_DOWN:
        {
            APPL_LOG("Physical layer disconnected.");
            connectable_mode_enter();
            break;
        }
        default:
        {
            break;
        }
    }
}

static void on_ipv6_medium_error(ipv6_medium_error_t * p_ipv6_medium_error)
{
    // Do something.
}

#endif //IOT_LIBRARY_ENABLE


/**
 * @brief Function for main application entry.
 */
int main(void)
{
    ret_code_t ret_code;
    uint32_t err_code;
    app_fds_res_t app_resp;

    nrf_saadc_value_t adcValue;
    app_saadc_settings_t *pAdcSet;

    //m_calib_doit = false;
    //m_calib_request = false;

    int32_t tmp1, tmp2;

    m_adcValue_UDP = 0;

    scheduler_init();

    // Init Clock
    clock_init(&tmp1, &tmp2);

    // Init LOG and configure only if CLI isn't over UART and LOG is over UART
    cli_log_default_backends();
    NRF_LOG_INFO("NodeChincol-p3 very first try v%s.", APP_SPECIFIC_VERSION);

    // Init Power Management Library
    //ret_code = nrf_pwr_mgmt_init();
    //APP_ERROR_CHECK(ret_code);

    // Init app timer (for bsp and timer for taking CPU to sleep)
    ret_code = app_timer_init();
    APP_ERROR_CHECK(ret_code);

    // Create timer_joe
    ret_code = app_timer_create(&m_timer_task_interval, APP_TIMER_MODE_SINGLE_SHOT, app_timer_task_interval_handler);
    APP_ERROR_CHECK(ret_code);

#if IOT_LIBRARY_ENABLE

    // Create a sys timer.
    ret_code = app_timer_create(&m_iot_timer_tick_src_id,
                                APP_TIMER_MODE_REPEATED,
                                iot_timer_tick_callback);
    APP_ERROR_CHECK(ret_code);

    iot_timer_init();

    static ipv6_medium_init_params_t ipv6_medium_init_params;
    memset(&ipv6_medium_init_params, 0x00, sizeof(ipv6_medium_init_params));
    ipv6_medium_init_params.ipv6_medium_evt_handler    = on_ipv6_medium_evt;
    ipv6_medium_init_params.ipv6_medium_error_handler  = on_ipv6_medium_error;
#ifdef COMMISSIONING_ENABLED
    ipv6_medium_init_params.commissioning_id_mode_cb   = commissioning_id_mode_cb;
    ipv6_medium_init_params.commissioning_power_off_cb = commissioning_power_off_cb;
#endif // COMMISSIONING_ENABLED

    err_code = ipv6_medium_init(&ipv6_medium_init_params,
                                IPV6_MEDIUM_ID_BLE,
                                &m_ipv6_medium);
    APP_ERROR_CHECK(err_code);

    eui48_t ipv6_medium_eui48;
    err_code = ipv6_medium_eui48_get(m_ipv6_medium.ipv6_medium_instance_id,
                                     &ipv6_medium_eui48);

    ipv6_medium_eui48.identifier[EUI_48_SIZE - 1] = 0x00;

    err_code = ipv6_medium_eui48_set(m_ipv6_medium.ipv6_medium_instance_id,
                                     &ipv6_medium_eui48);
    APP_ERROR_CHECK(err_code);

    // Initialize IP Stack.
    ip_stack_init();
    udp_port_setup();
    APPL_LOG("Stack IP Init.");

    // Start execution.
    connectable_mode_enter();

#endif //IOT_LIBRARY_ENABLE

    // USBD Init
    usbd_init();
    // Init CLI library
    cli_init();

#if BLE_LIBRARY_ENABLE
    // BLE Stack Init
    ble_stack_init();
#endif


#if BSP_LIBRARY_ENABLE
    // Config BSP
    bsp_configuration();
#else
#if BLE_LIBRARY_ENABLE
    leds_init();
    buttons_init();
#endif
#endif

    // Init and config SDAAC
    app_saadc_init();
#if ENABLE_SAADC_OVER_PPI
    app_saadc_sampling_event_init();
    app_saadc_sampling_event_enable();
#endif

    // Run CLI
    cli_start();

#if BLE_LIBRARY_ENABLE
    // Init BLE stuff and start adv
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
    advertising_start();
#endif

    //NRF_LOG_FLUSH();

    while (1)
    {
        /* Execute event schedule */
        app_sched_execute();

        while (NRF_LOG_PROCESS() == true);

        cli_process();
        
        switch(m_main_state)
        {
            case 0:
                // Init Message
                pAdcSet = app_saadc_get_config();
                NRF_LOG_INFO("SAADC settings:");
                NRF_LOG_INFO("-Resolution: %i", pAdcSet->resolution);
                NRF_LOG_INFO("-Mode: %i", pAdcSet->mode);
                NRF_LOG_INFO("-Gain: " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(pAdcSet->gain));
                NRF_LOG_INFO("-Reference: " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(pAdcSet->reference));
                NRF_LOG_INFO("-Oversamplig: %i x", pAdcSet->oversamplig);
                NRF_LOG_INFO("-Burst: %c", pAdcSet->burst);
                NRF_LOG_INFO("-BSP Enable: %c", BSP_LIBRARY_ENABLE ? 'Y' : 'N');
                NRF_LOG_INFO("-EUI48: %02X:%02X:%02X:%02X:%02X:%02X", 
                                                    ipv6_medium_eui48.identifier[5],
                                                    ipv6_medium_eui48.identifier[4],
                                                    ipv6_medium_eui48.identifier[3],
                                                    ipv6_medium_eui48.identifier[2],
                                                    ipv6_medium_eui48.identifier[1],
                                                    ipv6_medium_eui48.identifier[0]);
                NRF_LOG_INFO("Initializing fds...");
                m_main_state = 4;
                break;
            case 1:
                // Register and Init FDS
                if(app_fds_register_init() == APP_FDS_OK)
                {
                    NRF_LOG_INFO("FDS initiated");
                    tmp1 = 0;
                    m_main_state = 2;
                }
                else
                {
                    NRF_LOG_INFO("FDS Error init");
                    m_main_state = APP_STATE_FATAL; // go to defaul state
                }
                break;
            case 2:
                // Load/write/update config file.
                app_resp = app_fds_bootinit_start();
                if(app_resp == APP_FDS_OK)
                {
                    NRF_LOG_INFO("Boot-Init File operation OK");
                    tmp1 = 0;
                    m_main_state = 3;
                }
                else if(app_resp == APP_FDS_GC_IN_PROGRESS)
                {
                    if(tmp1++ > MAX_AUTOMATIC_GC_ATTEMPTS) m_main_state = 2; // Retry
                    else m_main_state = APP_STATE_FATAL; // go to defaul state
                }
                break;
            case 3:
                // Load/write/update config file.
                app_resp = app_fds_config_start();
                if(app_resp == APP_FDS_OK)
                {
                    NRF_LOG_INFO("Config File operation OK");
                    m_main_state = 4;
                }
                else if(app_resp == APP_FDS_GC_IN_PROGRESS)
                {
                    if(tmp1++ > MAX_AUTOMATIC_GC_ATTEMPTS) m_main_state = 3; // Retry
                    else m_main_state = APP_STATE_FATAL; // go to defaul state
                }
                break;
            case 4:
                // Trigger sampling and wait to sample to be done
                if (app_saadc_process(&adcValue))
                {
                    m_adcValue_UDP = adcValue;
                    NRF_LOG_INFO("-Sample: %d", adcValue);
                    //float v = app_saadc_SampleToVolts(adcValue);
                    //NRF_LOG_INFO("-Volts: " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(v));
            #if BLE_LIBRARY_ENABLE
                    // Send ADC value over BLE
                    NRF_LOG_INFO("Send ADC Value over BLE.");
                    ret_code = ble_ldrs_on_adc_change(m_conn_handle, &m_ldrs, (int16_t) adcValue);
                    if (ret_code != NRF_SUCCESS &&
                        ret_code != BLE_ERROR_INVALID_CONN_HANDLE &&
                        ret_code != NRF_ERROR_INVALID_STATE &&
                        ret_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                    {
                        APP_ERROR_CHECK(ret_code);
                    }
            #endif //#if BLE_LIBRARY_ENABLE
                    // Start the APP TIMER for wake-up
                    ret_code = app_timer_start(m_timer_task_interval, APP_TIMER_TICKS(TIMER_TASK_INTERVAL_MS), NULL);
                    APP_ERROR_CHECK(ret_code);
                    //NRF_LOG_INFO("Wake-up Timer Start");
                    m_main_state = 5;
                }
                else
                {
                    // Go to sleep while waiting for sample
                    //nrf_pwr_mgmt_run();
                    err_code = sd_app_evt_wait();
                    APP_ERROR_CHECK(err_code);
                }
                break;
            case 5:
                // Go to sleep and wait to wake up
                //NRF_LOG_INFO("Go to sleep");
                //nrf_pwr_mgmt_run();
                err_code = sd_app_evt_wait();
                APP_ERROR_CHECK(err_code);
                if (m_wake_up)
                {
                    m_wake_up = false;    // <- VERY BAD PRACTICE, USE MUTEX
                    //ret_code = app_timer_stop(m_timer_task_interval);
                    //APP_ERROR_CHECK(ret_code);
                    //NRF_LOG_INFO("Timer wake-up");
                    m_main_state = 4;
                }
                else
                {
                #if defined(CLI_OVER_UART) && (CLI_OVER_UART == 0)
                #if defined(CLI_OVER_USB_CDC_ACM) && (CLI_OVER_USB_CDC_ACM == 0)
                    NRF_LOG_INFO("Who knows wake-up");
                #endif
                #endif
                }
                break;
            default:
                NRF_LOG_INFO("Error: Default State");
                NRF_LOG_FLUSH();
                while(1);
                break;

        }

        /*
        //nrf_pwr_mgmt_run();
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        cli_process();
        if(m_calib_doit)
        {
            NRF_LOG_INFO("Starting calibration:");
            while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);
            m_calib_doit = false;
            m_calib_request = false;
            NRF_LOG_INFO("Ending calibration");
        }*/

        /*
        if (NRF_LOG_PROCESS() == false)
        {
            cli_process();
            nrf_pwr_mgmt_run();
            if(m_calib_doit)
            {
                NRF_LOG_INFO("Starting calibration:");
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);
                m_calib_doit = false;
                m_calib_request = false;
                NRF_LOG_INFO("Ending calibration");
            }
        }
        */

    }
}


/** @} */

Python client code:

import socket
import struct
import random

SERVER_ADDR = 'FD00:B::WWWW:XXXX:YYYY:ZZZZ'
# CLIENT_ADDR = 'FD00:B::2'
UDP_PORT    = 9000

def clientble_create():
    sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
    # client_addr = (CLIENT_ADDR, UDP_PORT)
    # sock.bind(client_addr)
    return sock

def clientble_send(sock, sequence_number):
    data = struct.pack("I", sequence_number)
    data += ''.join(chr(val) for val in [random.randint(0,0xFF) for r in xrange(16)])
    sock.sendto(data, (SERVER_ADDR, UDP_PORT))
    print("\nSending UDP ipv6 Packet:")
    # print("Client PC: " + CLIENT_ADDR + " -> Device BLE: " + SERVER_ADDR)
    print("Tx Sequence Number: " + str(sequence_number))
    recv_data = sock.recvfrom(32)
    rx_sequence_number = struct.unpack("I", recv_data[0][:4])[0]
    tmp_array = bytearray(recv_data[0][4:8])
    tmp_array[2] = 0
    tmp_array[3] = 0
    rx_adc_value = struct.unpack("I", tmp_array)[0]
    print 'Rx Sequence Number: ' + str(rx_sequence_number)
    if sequence_number != rx_sequence_number:
        print 'ERROR: Sequence number mismatch!!!!'
    print 'Rx ADC Value: ' + str(rx_adc_value)

def client_terminate(sock):
    sock.close()
    del sock
    

if __name__ == '__main__':
    print 'Client IPv6 UDP Test to BLE Device Server'
    fsm = 0
    sequence_number = 0
    while True :
        in_cmd = raw_input("\nPress s to send packet or q to quit: ")
        if in_cmd == 's':
            sequence_number += 1
            if fsm == 0:
                fsm = 1
                sock = clientble_create()
                clientble_send(sock, sequence_number)
            else:
                clientble_send(sock, sequence_number)
        elif in_cmd == 'q':
            if fsm > 0:
                client_terminate(sock)
            break            
        else:
            print 'Nothing to do'
    print 'quitting'

Parents
  • Hello,

    I checked with our SDK team (I am not very familiar with the IoT part of the SDK, as this is actually deprecated). They said that the RTE error is a quite common error. It may mean that the address resolution process is not yet complete, and hence the lwIP stack doesn't permit to send packets on any global address yet. One thing you can try is to just wait a bit more before you send the packet, or reply to it using "udp_sendto".

    Is it possible to capture a wire shark log from the communication? Perhaps this can shed some light on the issue.

    BR,

    Edvin

  • Hi Edvin,

    Thanks for your answer.
    I made three captures, one using the firmware with the nordic ipv6 stack (that works ok, "ws_cap_nordic_ipv6.pcapng") and two with the lwIP stack ("ws_cap_lwIP_ipv6.pcapng") in the nRF52 board.
    Wireshark was running in the Raspberry Pi using the "bt0" interface for capture (I don't have the ble dongle to perform a ble capture).
    I attached the three files:
    - In the "ws_cap_nordic_ipv6.pcapng" file you can see the two UDP packets, the request from the client (PC windows) and response from the server (nRF52 board). The packets looks ok.
    - In the "ws_cap_lwIP_ipv6.pcapng" file only the UDP packet from the client is captured. The server doen't not respond because the "-4" error. Only one UDP request is present in the log.
    - In the "ws_cap_lwIP_ipv6_2.pcapng" file you can see 6 UDP packets from the client and none of them was repliyed (because the same "-4" error). Also, I wait several minutes after doing the test to be shure that the radvd performed the prefix distribution.

    By the way, I didn't know that the IoT part of the SDK was deprecated. My next step is to implement a MQTT client publisher, so , Should I try to implement it with another or external library?

    Best regards
    José Forno

    ws_cap_lwIP_ipv6.pcapngws_cap_lwIP_ipv6_2.pcapngws_cap_nordic_ipv6.pcapng

Reply
  • Hi Edvin,

    Thanks for your answer.
    I made three captures, one using the firmware with the nordic ipv6 stack (that works ok, "ws_cap_nordic_ipv6.pcapng") and two with the lwIP stack ("ws_cap_lwIP_ipv6.pcapng") in the nRF52 board.
    Wireshark was running in the Raspberry Pi using the "bt0" interface for capture (I don't have the ble dongle to perform a ble capture).
    I attached the three files:
    - In the "ws_cap_nordic_ipv6.pcapng" file you can see the two UDP packets, the request from the client (PC windows) and response from the server (nRF52 board). The packets looks ok.
    - In the "ws_cap_lwIP_ipv6.pcapng" file only the UDP packet from the client is captured. The server doen't not respond because the "-4" error. Only one UDP request is present in the log.
    - In the "ws_cap_lwIP_ipv6_2.pcapng" file you can see 6 UDP packets from the client and none of them was repliyed (because the same "-4" error). Also, I wait several minutes after doing the test to be shure that the radvd performed the prefix distribution.

    By the way, I didn't know that the IoT part of the SDK was deprecated. My next step is to implement a MQTT client publisher, so , Should I try to implement it with another or external library?

    Best regards
    José Forno

    ws_cap_lwIP_ipv6.pcapngws_cap_lwIP_ipv6_2.pcapngws_cap_nordic_ipv6.pcapng

Children
  • Hello,

    Sorry for the late reply.

     

    Jose Forno said:
    By the way, I didn't know that the IoT part of the SDK was deprecated. My next step is to implement a MQTT client publisher, so , Should I try to implement it with another or external library?

     It was never a thing that got much traction, and hence it was never completed, because of lack of interrest from customers.

    From your ipv6_2 sniffer trace, it looks like the Raspberry tries to ping the device before it has joined the network properly. Is it something special you use in the Raspberry pi image?

    You can see that it receives the ping before the nRF has received any information that it is part of a network, or has been assigned an IP address.

Related