Hi,
I'm facing some problems trying to implement IoT library. I'm working in a code that uses FDS, SAADC, USBD-CDC-ACM among other things. I was able successfully to implement a BLE application with a simple custom service.
My next step is to implement an IoT node. I took the ICMP example as a reference, I disabled the previous BLE code, and added the relevant ICMP's example code to my own code.
I was able to compile with no errors/warnings, but at this point I facing two problems:
1.- Power management/driver problem: The code asserts in the power driver "nrf_drv_power.c" line 357, when checking a global flag (m_initialized). It happens when the function "ipv6_medium_init" is called from my code (I suppose that this function use internally the power driver). The ICMP example's sdk_config file has the power management and driver disabled, so I disabled both in my sdk_config file and it worked. But the USB use the power driver (it doesn't compile without it), so I was forced to disable the USB. Is this the solution or there's a better way? Anyway I want to have the USB (I'm using CLI through USB) working and maybe the power management/driver enabled too. Is this possible?
2.- FDS handler: Later in the code, I register and init the FDS without problems. Then it read a file and update it, the function fds_record_update returns OK. Then I pull a flag to know if the event was generated and it hangs there forever and ever. It means (as far as I concern) that the event is never generated (but at the other hand the init event was generated at the beginning). What could it be? For the record: I tested removing the code regarding to the IoT library and the FDS worked OK (using the softdevice as backend).
The main code and the fds code is atteched, as well the sdk_config.h
I'm Using:
- nRF5 SDK v16.0.0
- Softdevice s140 v7.0.1
- Custom Board with nRF52840
- Visual Studio Code with gcc
Thank you in advance!
/** * 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" /* *************************************************************************** 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 // 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 #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 eui64_t eui64_local_iid; static iot_interface_t * mp_interface = NULL; static ipv6_medium_instance_t m_ipv6_medium; #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 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 ICMP6 module event handler. * * @details Callback registered with the ICMP6 module to receive asynchronous events from * the module, if the ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION constant is not zero * or the ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION constant is not zero. */ uint32_t icmp6_handler(iot_interface_t * p_interface, ipv6_header_t * p_ip_header, icmp6_header_t * p_icmp_header, uint32_t process_result, iot_pbuffer_t * p_rx_packet) { APPL_LOG("Got ICMP6 Application Handler Event on interface %p", p_interface); APP_ERROR_CHECK(process_result); APPL_LOG("\tSrc IPv6 Address: "); APPL_ADDR(p_ip_header->srcaddr); APPL_LOG("\tDst IPv6 Address: "); APPL_ADDR(p_ip_header->destaddr); APPL_LOG("\tProcess result = 0x%08lX\r\n", process_result); switch (p_icmp_header->type) { case ICMP6_TYPE_DESTINATION_UNREACHABLE: APPL_LOG("ICMP6 Message Type = Destination Unreachable Error"); break; case ICMP6_TYPE_PACKET_TOO_LONG: APPL_LOG("ICMP6 Message Type = Packet Too Long Error"); break; case ICMP6_TYPE_TIME_EXCEED: APPL_LOG("ICMP6 Message Type = Time Exceed Error"); break; case ICMP6_TYPE_PARAMETER_PROBLEM: APPL_LOG("ICMP6 Message Type = Parameter Problem Error"); break; case ICMP6_TYPE_ECHO_REQUEST: APPL_LOG("ICMP6 Message Type = Echo Request"); break; case ICMP6_TYPE_ECHO_REPLY: APPL_LOG("ICMP6 Message Type = Echo Reply"); /*if (m_led_feedback_enabled == true) { // Start blinking LEDs to indicate that Echo Reply has been received. led_blinking_start(); }*/ break; case ICMP6_TYPE_ROUTER_SOLICITATION: APPL_LOG("ICMP6 Message Type = Router Solicitation"); break; case ICMP6_TYPE_ROUTER_ADVERTISEMENT: APPL_LOG("ICMP6 Message Type = Router Advertisement"); /*if (m_led_feedback_enabled == true) { // Start blinking LEDs to indicate that Echo Reply has been received. led_blinking_start(); }*/ break; case ICMP6_TYPE_NEIGHBOR_SOLICITATION: APPL_LOG("ICMP6 Message Type = Neighbor Solicitation"); break; case ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: APPL_LOG("ICMP6 Message Type = Neighbor Advertisement"); /*if (m_led_feedback_enabled == true) { // Start blinking LEDS to indicate that Echo Reply has been received. led_blinking_start(); }*/ break; default: break; } return NRF_SUCCESS; } /**@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 IP Stack interface events handler. */ void ip_app_handler(iot_interface_t * p_interface, ipv6_event_t * p_event) { APPL_LOG("Got IP Application Handler Event on interface %p", p_interface); switch (p_event->event_id) { case IPV6_EVT_INTERFACE_ADD: #ifdef COMMISSIONING_ENABLED commissioning_joining_mode_timer_ctrl(JOINING_MODE_TIMER_STOP_RESET); #endif // COMMISSIONING_ENABLED APPL_LOG("New interface added!"); mp_interface = p_interface; //m_disp_state = LEDS_IPV6_IF_UP; break; case IPV6_EVT_INTERFACE_DELETE: #ifdef COMMISSIONING_ENABLED commissioning_joining_mode_timer_ctrl(JOINING_MODE_TIMER_START); #endif // COMMISSIONING_ENABLED APPL_LOG("Interface removed!"); mp_interface = NULL; //m_led_feedback_enabled = false; //m_disp_state = LEDS_IPV6_IF_DOWN; break; case IPV6_EVT_INTERFACE_RX_DATA: APPL_LOG("Got unsupported protocol data!"); break; default: //Unknown event. Should not happen. break; } } /**@brief Function for initializing IP stack. * * @details Initialize the IP Stack. */ static void ip_stack_init(void) { uint32_t err_code; ipv6_init_t init_param; err_code = ipv6_medium_eui64_get(m_ipv6_medium.ipv6_medium_instance_id, &eui64_local_iid); APP_ERROR_CHECK(err_code); init_param.p_eui64 = &eui64_local_iid; init_param.event_handler = ip_app_handler; err_code = ipv6_init(&init_param); APP_ERROR_CHECK(err_code); err_code = icmp6_receive_register(icmp6_handler); 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; 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-p1 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 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(); 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("Initializing fds..."); m_main_state = 1; 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)) { 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"); } } */ } } /** @} */
#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include "nrf.h" #include "app_error.h" #include "nrf_log.h" #include "fds.h" #include "nrf_atfifo.h" #include "app_fds.h" /* *************************************************************************** FDS RELATED PREPROCESOR DIRECTIVES *************************************************************************** */ /* BOOT INIT File Id and Record Key */ #define BOOTINIT_FILE (0x8010) #define BOOTINIT_REC_KEY (0x7010) /* CONFIG File Id and Record Key */ #define CONFIG_FILE (0x8011) #define CONFIG_REC_KEY (0x7011) /* SAADC File Id and Record Key */ #define SAADC_FILE (0x8012) #define SAADC_REC_KEY (0x7012) // Values of the application configuration #define APP_ID 1 #define APP_VERSION (0x01A0) // Version 1.0 #define APP_RECORD_VERSION (0x01A0) // Version 1.0 #define APP_NODE_NUMBER 0 #define APP_NODE_NAME "Nameless" /* *************************************************************************** FDS RELATED GLOBAL VARIABLES *************************************************************************** */ /* *********************** BOOT INIT FILE ******************************* */ // Constant varibale containig the boot init file static app_bootinit_data_t m_fds_bootinit_data = { .boot_count = 0, .last_error_code = 0, }; // Constant varibale containig the record static fds_record_t const m_fds_bootinit_record = { .file_id = BOOTINIT_FILE, .key = BOOTINIT_REC_KEY, .data.p_data = &m_fds_bootinit_data, /* The length of a record is always expressed in 4-byte units (words). */ .data.length_words = (sizeof(m_fds_bootinit_data) + 3) / sizeof(uint32_t), }; /* *********************** APP CONFIG FILE ****************************** */ // Constant varibale containig the aplication configuration static app_configuration_data_t m_fds_app_cfg_data = { .app_id = APP_ID, .app_version = APP_VERSION, .record_version = APP_RECORD_VERSION, .node_number = APP_NODE_NUMBER, .node_name = APP_NODE_NAME, }; // Constant varibale containig the record static fds_record_t const m_fds_app_cfg_record = { .file_id = CONFIG_FILE, .key = CONFIG_REC_KEY, .data.p_data = &m_fds_app_cfg_data, /* The length of a record is always expressed in 4-byte units (words). */ .data.length_words = (sizeof(m_fds_app_cfg_data) + 3) / sizeof(uint32_t), }; // Temp data to store config data that can be modified by user static uint32_t tmp_node_number; static char tmp_node_name[sizeof(m_fds_app_cfg_data.node_name)] = {0}; static bool tmp_node_number_change = false; static bool tmp_node_name_change = false; /* Global variable for detecting FDS event and its result */ volatile static bool m_fds_event; volatile static ret_code_t m_fds_event_code; volatile static fds_evt_id_t m_fds_event_id; /* ********************************************************************* FDS LIBRARY ********************************************************************* */ static void fds_evt_handler(fds_evt_t const * p_evt) { m_fds_event = true; m_fds_event_code = p_evt->result; m_fds_event_id = p_evt->id; if (p_evt->result == NRF_SUCCESS) { NRF_LOG_INFO("[FDS] Event: %i received (NRF_SUCCESS)", p_evt->id); } else { NRF_LOG_INFO("[FDS] Event: %i received code error (%u)", p_evt->id, p_evt->result); } switch (p_evt->id) { case FDS_EVT_INIT: if (p_evt->result != NRF_SUCCESS) { NRF_LOG_INFO("[FDS](I) Init ERROR (code: %u)", p_evt->result); } break; case FDS_EVT_WRITE: if (p_evt->result == NRF_SUCCESS) { NRF_LOG_INFO("[FDS](W) Record ID:\t0x%04x", p_evt->write.record_id); NRF_LOG_INFO("[FDS](W) File ID:\t0x%04x", p_evt->write.file_id); NRF_LOG_INFO("[FDS](W) Record key:\t0x%04x", p_evt->write.record_key); NRF_LOG_INFO("[FDS](W) Is Record Updated: \t%c", (p_evt->write.is_record_updated == true) ? 'Y' : 'N'); } else { NRF_LOG_INFO("[FDS](W) Write ERROR (code: %u)", p_evt->result); } break; case FDS_EVT_UPDATE: if (p_evt->result == NRF_SUCCESS) { NRF_LOG_INFO("[FDS](U) Record ID:\t0x%04x", p_evt->write.record_id); NRF_LOG_INFO("[FDS](U) File ID:\t0x%04x", p_evt->write.file_id); NRF_LOG_INFO("[FDS](U) Record key:\t0x%04x", p_evt->write.record_key); NRF_LOG_INFO("[FDS](U) Is Record Updated:\t%c", (p_evt->write.is_record_updated == true) ? 'Y' : 'N'); } else { NRF_LOG_INFO("[FDS](U) Update ERROR (code: %u)", p_evt->result); } break; case FDS_EVT_GC: // Garbage collection done if (p_evt->result == NRF_SUCCESS) { NRF_LOG_INFO("[FDS](G) Garbage Collection done (NRF_SUCCESS)"); } else { NRF_LOG_INFO("[FDS](G) Garbage Collection ERROR (code: %u)", p_evt->result); } break; /* case FDS_EVT_DEL_RECORD: { if (p_evt->result == NRF_SUCCESS) { NRF_LOG_INFO("Record ID:\t0x%04x", p_evt->del.record_id); NRF_LOG_INFO("File ID:\t0x%04x", p_evt->del.file_id); NRF_LOG_INFO("Record key:\t0x%04x", p_evt->del.record_key); } m_delete_all.pending = false; } break; */ default: break; } } /* * Brief: Load boot-init file. * - If file does not exist then exit with error * - If file exist then load it, and put data into *pconfig structure * Return: error code from FDS library (NRF_SUCCESS if it was a success) */ ret_code_t app_fds_bootinit_load(app_bootinit_data_t *pconfig) { ret_code_t rc; fds_record_desc_t desc = {0}; fds_find_token_t tok = {0}; rc = fds_record_find(BOOTINIT_FILE, BOOTINIT_REC_KEY, &desc, &tok); if (rc == NRF_SUCCESS) { fds_flash_record_t frec = {0}; rc = fds_record_open(&desc, &frec); if (rc == NRF_SUCCESS) { /* Copy the configuration from flash into pconfig. */ memcpy(pconfig, frec.p_data, sizeof(app_bootinit_data_t)); rc = fds_record_close(&desc); } } return rc; } /* * Brief: Load configuration file. * - If file does not exist then exit with error * - If file exist then load it, and put data into *pconfig structure * Return: error code from FDS library (NRF_SUCCESS if it was a success) */ ret_code_t app_fds_config_load(app_configuration_data_t *pconfig) { ret_code_t rc; fds_record_desc_t desc = {0}; fds_find_token_t tok = {0}; rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); if (rc == NRF_SUCCESS) { fds_flash_record_t frec = {0}; rc = fds_record_open(&desc, &frec); if (rc == NRF_SUCCESS) { /* Copy the configuration from flash into pconfig. */ memcpy(pconfig, frec.p_data, sizeof(app_configuration_data_t)); rc = fds_record_close(&desc); } } return rc; } /* * Brief: Update configuration file. * - If file does not exist then return with the error code * - If file exist then it perform an update * Return: error code from FDS library (NRF_SUCCESS if it was a success) */ ret_code_t app_fds_config_update(void) { ret_code_t rc; fds_record_desc_t desc = {0}; fds_find_token_t tok = {0}; rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); if (rc == NRF_SUCCESS) { /* Write the updated record to flash. */ rc = fds_record_update(&desc, &m_fds_app_cfg_record); if(rc == NRF_SUCCESS) { while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { NRF_LOG_INFO("AU Record ID:\t0x%04x", desc.record_id); NRF_LOG_INFO("AU GC Count:\t%u", desc.gc_run_count); NRF_LOG_INFO("AU Record Open:\t%c", desc.record_is_open ? 'Y' : 'N'); return NRF_SUCCESS; } else { APP_ERROR_CHECK(m_fds_event_code); } } } return rc; } /* * Brief: Load, create or update boot-init file. * - If file does not exist then create new one with values in zero * - If file exist then load it, increase the boot count * and update it (the porpuse of boot count is to track the number of times * the device was restared) * - If there is no space avaliable the GC (garbage collector) is triggered. * Return: error code defined by me. */ app_fds_res_t app_fds_bootinit_start(void) { ret_code_t rc; fds_stat_t stat = {0}; rc = fds_stat(&stat); APP_ERROR_CHECK(rc); NRF_LOG_INFO("Found %d valid records.", stat.valid_records); NRF_LOG_INFO("Found %d dirty records (ready to be garbage collected).", stat.dirty_records); fds_record_desc_t desc = {0}; fds_find_token_t tok = {0}; rc = fds_record_find(BOOTINIT_FILE, BOOTINIT_REC_KEY, &desc, &tok); if (rc == NRF_SUCCESS) { /* A config file is in flash. Let's update it. */ fds_flash_record_t bootinit = {0}; /* Open the record and read its contents. */ rc = fds_record_open(&desc, &bootinit); APP_ERROR_CHECK(rc); /* Copy the configuration from flash into m_app_cfg. */ memcpy(&m_fds_bootinit_data, bootinit.p_data, sizeof(app_bootinit_data_t)); NRF_LOG_INFO("Boot-Init file found, updating boot count to %d.", m_fds_bootinit_data.boot_count); /* Update boot count. */ m_fds_bootinit_data.boot_count++; /* Close the record when done reading. */ rc = fds_record_close(&desc); APP_ERROR_CHECK(rc); /* Write the updated record to flash. */ rc = fds_record_update(&desc, &m_fds_bootinit_record); if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH)) { NRF_LOG_INFO("No space in flash, starting garbage collector..."); rc = fds_gc(); if(rc == NRF_SUCCESS) // TODO: Verify if event ID equals to GC { // Wait for fds library response while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { return APP_FDS_GC_IN_PROGRESS; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } else { if(rc == NRF_SUCCESS) // TODO: Verify if event ID equals to UPDATE { while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { NRF_LOG_INFO("BOOT Record ID:\t0x%04x", desc.record_id); NRF_LOG_INFO("BOOT GC Count:\t%u", desc.gc_run_count); NRF_LOG_INFO("BOOT Record open:\t%c", desc.record_is_open ? 'Y' : 'N'); return APP_FDS_OK; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } } else { /* System config not found; write a new one. */ NRF_LOG_INFO("Boot-Init file not found, writing default file..."); rc = fds_record_write(&desc, &m_fds_bootinit_record); if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH)) { NRF_LOG_INFO("No space in flash, starting garbage collector..."); rc = fds_gc(); if(rc == NRF_SUCCESS) // TODO: Verify if event ID equals to GC { // Wait for fds library response while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { return APP_FDS_GC_IN_PROGRESS; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } else { if(rc == NRF_SUCCESS) { // Wait for fds library response while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { NRF_LOG_INFO("BOOT Record ID:\t0x%04x", desc.record_id); NRF_LOG_INFO("BOOT GC Count:\t%u", desc.gc_run_count); NRF_LOG_INFO("BOOT Record key:\t%c", desc.record_is_open ? 'Y' : 'N'); return APP_FDS_OK; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } } return APP_FDS_FATAL_ERROR; } /* * Brief: Load, create or update configuration file. * - If file does not exist then create new one wiht initial values * - If file exist then load it, increase the boot count * and update it (the porpuse of boot count is to track the number of times * the device was restared) * - If there is no space avaliable the GC (garbage collector) is triggered. * Return: error code defined by me. */ app_fds_res_t app_fds_config_start(void) { ret_code_t rc; fds_record_desc_t desc = {0}; fds_find_token_t tok = {0}; rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); if (rc == NRF_SUCCESS) { /* A config file is in flash. */ fds_flash_record_t config = {0}; /* Open the record and read its contents. */ rc = fds_record_open(&desc, &config); APP_ERROR_CHECK(rc); /* Copy the configuration from flash into m_app_cfg. */ memcpy(&m_fds_app_cfg_data, config.p_data, sizeof(app_configuration_data_t)); NRF_LOG_INFO("Config file found and loaded OK"); /* Close the record when done reading. */ rc = fds_record_close(&desc); APP_ERROR_CHECK(rc); return APP_FDS_OK; } else { /* System config not found; write a new one. */ NRF_LOG_INFO("Config file not found, writing default file..."); rc = fds_record_write(&desc, &m_fds_app_cfg_record); if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH)) { NRF_LOG_INFO("No space in flash, starting garbage collector..."); rc = fds_gc(); if(rc == NRF_SUCCESS) // TODO: Verify if event ID equals to GC { // Wait for fds library response while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { return APP_FDS_GC_IN_PROGRESS; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } else { if(rc == NRF_SUCCESS) { // Wait for fds library response while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { NRF_LOG_INFO("APP CFG Record ID:\t0x%04x", desc.record_id); NRF_LOG_INFO("APP CFG GC Count:\t%u", desc.gc_run_count); NRF_LOG_INFO("APP CFG Record key:\t%c", desc.record_is_open ? 'Y' : 'N'); return APP_FDS_OK; } else { APP_ERROR_CHECK(m_fds_event_code); } } else APP_ERROR_CHECK(rc); } } return APP_FDS_FATAL_ERROR; } /* * Brief: Register the callback function and Init the FDS library * It waits until the init event is generated. * Return: error code defined by me. */ app_fds_res_t app_fds_register_init(void) { ret_code_t ret_code; m_fds_event = false; /* Register first to receive an event when initialization is complete. */ (void) fds_register(fds_evt_handler); // Init fds ret_code = fds_init(); APP_ERROR_CHECK(ret_code); // Wait for init event while(!m_fds_event); m_fds_event = false; if(m_fds_event_code == NRF_SUCCESS) { return APP_FDS_OK; } else { APP_ERROR_CHECK(m_fds_event_code); } return APP_FDS_FATAL_ERROR; } /* * Brief: Save node number in a temp variable for later storage * using app_fds_commit_cfg_data_RAM function */ void app_fds_set_node_number_in_RAM(uint32_t node_number) { tmp_node_number = node_number; tmp_node_number_change = true; } /* * Brief: Save node name in a temp variable for later storage * using app_fds_commit_cfg_data_RAM function */ bool app_fds_set_node_name_in_RAM(char *node_name) { bool result = false; if (strlen(node_name) < sizeof(m_fds_app_cfg_data.node_name)) { strcpy(tmp_node_name, node_name); tmp_node_name_change = true; result = true; } return result; } /* * Brief: Save temp data to the actual RAM structure */ void app_fds_commit_cfg_data_RAM(void) { if(tmp_node_number_change) { m_fds_app_cfg_data.node_number = tmp_node_number; tmp_node_number_change = false; } if(tmp_node_name_change) { strcpy(m_fds_app_cfg_data.node_name, tmp_node_name); tmp_node_name_change = false; } } /* * Brief: get the max character size of node name */ uint32_t app_fds_get_node_name_size(void) { return((uint32_t)sizeof(m_fds_app_cfg_data.node_name) - 1); }