 /******************************************************************************
 * @file    main.c
 * @author  Insight SiP
 * @version V2.4.1
 * @date    15-jan-2020
 * @brief   Sensor board project main file.
 *
 * @attention
 *	THIS SOFTWARE IS PROVIDED BY INSIGHT SIP "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 INSIGHT SIP 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.
 *
 *****************************************************************************/
 
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "nordic_common.h"
#include "fds.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
#include "nrf_delay.h"
#include "nrf_drv_clock.h"
#include "nrf_drv_gpiote.h"
#include "nrf_ble_gatt.h"
#include "nrf_gpio.h"
#include "app_timer.h"
#include "app_error.h"
#include "app_scheduler.h"
#include "app_button.h"
#include "ble_advertising.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_err.h"
#include "ble_srv_common.h"
#include "ble_advdata.h"
#include "ble_conn_params.h"
#include "ble_conn_state.h"
#include "peer_manager.h"

#include "ble_loramngt.h"
#include "ble_dis.h"
#include "twi_manager.h"
#include "drv_humidity.h"
#include "drv_pressure.h"
#include "drv_light.h"
#include "drv_motion.h"

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

#include "board.h"
#include "user_config.h"
#include "m_loramngt_flash.h"
#include "radio.h"

#define FW_REVISION "2.4.2"
#define HW_REVISION     "C" 

#define SCHED_MAX_EVENT_DATA_SIZE   MAX(APP_TIMER_SCHED_EVENT_DATA_SIZE, NRF_SDH_BLE_EVT_BUF_SIZE)      /**< Maximum size of scheduler events. */

#if defined(ISP4520_JP)
#define RF_FREQUENCY                    923000000 // Hz
#define DEVICE_NAME                     "ISP4580-JP"
#elif defined(ISP4520_EU)
#define RF_FREQUENCY                    868000000 // Hz    
#define DEVICE_NAME                     "ISP4580-EU"
#elif defined(ISP4520_US)
#define DEVICE_NAME                     "ISP4580-US"
#define RF_FREQUENCY                    915000000 // Hz                   
#else
    #error "Please define a version of an ISP4520."
#endif

#define LORA_CODINGRATE             1         // [1: 4/5,
                                              //  2: 4/6,
                                              //  3: 4/7,
                                              //  4: 4/8]
#define LORA_PREAMBLE_LENGTH        8         // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT         0         // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON  false
#define LORA_IQ_INVERSION_ON        false

// Foward declaration
static void send_lora_frame (void);

// Static vars
NRF_BLE_GATT_DEF(m_gatt);                                                               /**< GATT module instance. */
BLE_ADVERTISING_DEF(m_advertising);                                                     /**< Advertising module instance. */
BLE_LORAMNGT_DEF(m_loramngt);                                                           /**< LoRa management Service instance. */

static RadioEvents_t LoRaEvents;                                                        /**< LoRa events */
static const nrf_drv_twi_t m_twi_sensors = NRF_DRV_TWI_INSTANCE(1);                     /**< I2C instance. */
static const ble_loramngt_config_t  m_lora_default_config = LORAMNGT_CONFIG_DEFAULT; 	/**< Default lora configuration. */
static ble_loramngt_config_t *m_p_lora_config;                                          /**< Lora configuration pointer. */

APP_TIMER_DEF(pwr_led_app_timer_id);        ///< Power LED timer instance.
APP_TIMER_DEF(ble_led_app_timer_id);        ///< BLE LED timer instance.
APP_TIMER_DEF(lora_led_app_timer_id);       ///< LORA LED timer instance.
APP_TIMER_DEF(lora_tx_timer_id);            ///< LoRa tranfer timer instance.

static bool pwr_led_state = false;          ///< Power LED ON/OFF state.
static bool ble_led_state = false;          ///< BLE LED ON/OFF state.
static uint8_t m_lora_frame_counter = 0;


/**@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 Pressure sensor event handler.
 */
static void drv_pressure_evt_handler (drv_pressure_evt_t const *p_event)
{
    //Do nothing
}

/**@brief Function for initializing the pressure sensor
 */
static uint32_t pressure_sensor_init (const nrf_drv_twi_t *p_twi_instance)
{
    drv_pressure_init_t init_params;

    static const nrf_drv_twi_config_t twi_config =
    {
        .scl                = PIN_TWI_SCL,
        .sda                = PIN_TWI_SDA,
        .frequency          = NRF_TWI_FREQ_400K,
        .interrupt_priority = APP_IRQ_PRIORITY_LOW,
        .clear_bus_init     = true
    };

    init_params.pin_int                 = PIN_PRESS_INTDRDY;
    init_params.p_twi_instance          = p_twi_instance;
    init_params.p_twi_cfg               = &twi_config;
    init_params.evt_handler             = drv_pressure_evt_handler;

    return drv_pressure_init(&init_params);
}

/**@brief Function for initializing the humidity/temperature sensor
 */
static uint32_t humidity_sensor_init (const nrf_drv_twi_t * p_twi_instance)
{
    static const nrf_drv_twi_config_t twi_config =
    {
        .scl                = PIN_TWI_SCL,
        .sda                = PIN_TWI_SDA,
        .frequency          = NRF_TWI_FREQ_400K,
        .interrupt_priority = APP_IRQ_PRIORITY_LOW
    };

    drv_humidity_init_t    init_params =
    {
        .p_twi_instance      = p_twi_instance,
        .p_twi_cfg           = &twi_config,
        .evt_handler         = NULL
    };

    return drv_humidity_init(&init_params);
}

/**@brief Light sensor event handler.
 */
static void drv_light_evt_handler (drv_light_evt_t const *p_event)
{
    //Do nothing
}

/**@brief Function for initializing the light sensor
 */
static uint32_t light_sensor_init (const nrf_drv_twi_t *p_twi_instance)
{
    drv_light_init_t init_params;

    static const nrf_drv_twi_config_t twi_config =
    {
        .scl                = PIN_TWI_SCL,
        .sda                = PIN_TWI_SDA,
        .frequency          = NRF_TWI_FREQ_400K,
        .interrupt_priority = APP_IRQ_PRIORITY_LOW
    };

    init_params.p_twi_instance = p_twi_instance;
    init_params.p_twi_cfg      = &twi_config;
    init_params.evt_handler    = drv_light_evt_handler;
    init_params.pin_int        = DRV_LIGHT_PIN_NOT_USED;

    return drv_light_init(&init_params);
}

/**@brief Motion sensor event handler.
 */
static void drv_motion_evt_handler (drv_motion_evt_t const * p_evt, void * p_data, uint32_t size)
{
    //Do nothing
}

/**@brief Function for initializing the motion sensor
 */
static uint32_t motion_sensor_init (const nrf_drv_twi_t *p_twi_instance)
{
    drv_motion_init_t init_params;

    static const nrf_drv_twi_config_t twi_config =
    {
        .scl                = PIN_TWI_SCL,
        .sda                = PIN_TWI_SDA,
        .frequency          = NRF_TWI_FREQ_400K,
        .interrupt_priority = APP_IRQ_PRIORITY_LOW
    };

    init_params.p_twi_instance  = p_twi_instance;
    init_params.p_twi_cfg       = &twi_config;
    init_params.evt_handler     = &drv_motion_evt_handler;
    init_params.acc_pin_int     = DRV_MOTION_PIN_NOT_USED;
    init_params.mag_pin_int     = DRV_MOTION_PIN_NOT_USED;
	
    return drv_motion_init(&init_params);
}


/**@brief Function for handling a lora led timer timeout event.
 */
static void lora_led_periodic_handler (void * unused)
{
    nrf_gpio_cfg_default(PIN_GREEN_LED);
}

/**@brief Function for handling a ble led timer timeout event.
 */
static void ble_led_periodic_handler (void * unused)
{
    uint32_t next_interval;
	
    if (ble_led_state)
    {
        ble_led_state = false;
        next_interval = BLE_LED_OFF_INTERVAL_MS;
        nrf_gpio_cfg_default(PIN_YELLOW_LED);
    }
    else
    {
        ble_led_state = true;
        next_interval = BLE_LED_ON_INTERVAL_MS;
        nrf_gpio_pin_clear(PIN_YELLOW_LED);
        nrf_gpio_cfg_output(PIN_YELLOW_LED);
    }
	
    app_timer_start(ble_led_app_timer_id, APP_TIMER_TICKS(next_interval), NULL);
}

/**@brief Function for handling a power led timer timeout event.
 */
static void pwr_led_periodic_handler (void * unused)
{
    uint32_t next_interval;

    if (pwr_led_state)
    {
        pwr_led_state = false;
        next_interval = PWR_LED_OFF_INTERVAL_MS;
        nrf_gpio_cfg_default(PIN_RED_LED);
    }
    else
    {
        pwr_led_state = true;
        next_interval = PWR_LED_ON_INTERVAL_MS;
        nrf_gpio_pin_clear(PIN_RED_LED);
        nrf_gpio_cfg_output(PIN_RED_LED);
    }
	
    app_timer_start(pwr_led_app_timer_id, APP_TIMER_TICKS(next_interval), NULL);
}

/**@brief Function for handling a LoRa tx timer timeout event.
 */
static void tx_lora_periodic_handler (void * unused)
{
    send_lora_frame();


    // BLibk green led
    nrf_gpio_pin_clear(PIN_GREEN_LED);
    nrf_gpio_cfg_output(PIN_GREEN_LED);

    nrf_delay_ms(100);

    nrf_gpio_cfg_default(PIN_GREEN_LED);
}

/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static uint32_t timers_init (void)
{
    ret_code_t err_code;
	
    APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);

    // Initialize timer module.
    err_code = app_timer_init();
    VERIFY_SUCCESS(err_code);
	
    // Initialize timers
    err_code = app_timer_create(&pwr_led_app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, pwr_led_periodic_handler);
    VERIFY_SUCCESS(err_code);
	
    err_code = app_timer_create(&ble_led_app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, ble_led_periodic_handler);
    VERIFY_SUCCESS(err_code);
	
    err_code = app_timer_create(&lora_led_app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, lora_led_periodic_handler);
    VERIFY_SUCCESS(err_code);
	
    err_code = app_timer_create(&lora_tx_timer_id, APP_TIMER_MODE_REPEATED, tx_lora_periodic_handler);
    VERIFY_SUCCESS(err_code);
	
    return NRF_SUCCESS;
}

/** @brief button event handler.
 */
static void app_button_handler (uint8_t pin_no, uint8_t button_action)
{
    uint32_t err_code;

    // Go to system-off (sd_power_system_off() will not return; wakeup will cause a reset). When debugging, this function may return and code execution will continue.
    err_code = sd_power_system_off(); 
    NRF_LOG_WARNING("sd_power_system_off() returned. -Probably due to debugger being used. Instructions will still run.\r\n");
    NRF_LOG_FLUSH();
    APP_ERROR_CHECK(err_code);
}

static app_button_cfg_t btn_cfg;
/**@brief Function for the button initialization.
 */
static uint32_t button_init (void)
{
    ret_code_t err_code;

    btn_cfg.pin_no = PIN_USER_BUTTON;
    btn_cfg.button_handler = app_button_handler;
    btn_cfg.active_state = APP_BUTTON_ACTIVE_LOW;
    btn_cfg.pull_cfg = NRF_GPIO_PIN_NOPULL;
	
    err_code = app_button_init(&btn_cfg, 1, APP_TIMER_TICKS(1000));
    VERIFY_SUCCESS(err_code);
	
    err_code = app_button_enable();
    VERIFY_SUCCESS(err_code);
	
    return NRF_SUCCESS;
}

/**@brief Function to be executed on Radio Tx Done event
 */
void lora_tx_done_evt_handler (void)
{
    Radio.Sleep();
}

/**@brief Function to be executed on Radio Rx Done event
 */
void lora_rx_done_evt_handler (uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
    Radio.Sleep();

     NRF_LOG_DEBUG("lora_rx_done_evt_handler");
}

/**@brief Function to be executed on Radio Tx Timeout event
 */
void lora_tx_timeout_evt_handler (void)
{
    Radio.Sleep();

    NRF_LOG_DEBUG("lora_tx_timeout_evt_handler");
}

/**@brief Function to be executed on Radio Rx Timeout event
 */
void lora_rx_timeout_evt_handler (void)
{
    Radio.Sleep();

    NRF_LOG_DEBUG("lora_rx_timeout_evt_handler");
}

/**@brief Function to be executed on Radio Rx Error event
 */
void lora_rx_error_evt_handler (void)
{
    Radio.Sleep();

    NRF_LOG_DEBUG("lora_rx_error_evt_handler");
}

/**@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 = NRF_SUCCESS;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_DISCONNECTED:
        {
            NRF_LOG_INFO("Disconnected.");
#if defined(BTN_LED_PRESENT)
            app_timer_stop(ble_led_app_timer_id);
            nrf_gpio_cfg_default(PIN_YELLOW_LED);
#endif
        } break;

        case BLE_GAP_EVT_CONNECTED:
        {
            NRF_LOG_INFO("Connected.");
#if defined(BTN_LED_PRESENT)
            ble_led_periodic_handler(NULL);
#endif
        } 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;

        case BLE_EVT_USER_MEM_REQUEST:
            err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
            ble_gatts_evt_rw_authorize_request_t  req;
            ble_gatts_rw_authorize_reply_params_t auth_reply;

            req = p_ble_evt->evt.gatts_evt.params.authorize_request;

            if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
            {
                if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                {
                    if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                    }
                    else
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                    }
                    auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle, &auth_reply);
                    APP_ERROR_CHECK(err_code);
                }
            }
        } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

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

/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
static uint32_t ble_stack_init (void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    VERIFY_SUCCESS(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    VERIFY_SUCCESS(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    VERIFY_SUCCESS(err_code);

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

/**@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 uint32_t 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));
    VERIFY_SUCCESS(err_code);

    memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    gap_conn_params.min_conn_interval = MSEC_TO_UNITS(MIN_CONN_INTERVAL_MS, UNIT_1_25_MS);
    gap_conn_params.max_conn_interval = MSEC_TO_UNITS(MAX_CONN_INTERVAL_MS, UNIT_1_25_MS);;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = MSEC_TO_UNITS(CONN_SUP_TIMEOUT_MS, UNIT_10_MS);

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    VERIFY_SUCCESS(err_code);

    err_code = sd_ble_gap_tx_power_set(TX_POWER_LEVEL);
    VERIFY_SUCCESS(err_code);
	
    err_code = sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
    VERIFY_SUCCESS(err_code);
	 
    return NRF_SUCCESS;
}

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

/**@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 uint32_t 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             = true;
    cp_init.evt_handler                    = NULL;
    cp_init.error_handler                  = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    VERIFY_SUCCESS(err_code);
	
    return NRF_SUCCESS;
}

/**@brief Function for handling advertising events.
 *
 * @param[in] ble_adv_evt  Advertising event.
 */
static void adv_evt_handler (ble_adv_evt_t ble_adv_evt)
{
    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_DEBUG("Fast advertising.");
            break; // BLE_ADV_EVT_FAST

        case BLE_ADV_EVT_IDLE:
            NRF_LOG_DEBUG("Idle advertising.");
            break; // BLE_ADV_EVT_IDLE

        default:
            break;
    }
}

/**@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 uint32_t advertising_init(void)
{
    ret_code_t err_code;
    ble_advertising_init_t init;
    ble_uuid_t adv_uuid =  {BLE_UUID_LORAMNGT_SERVICE, m_loramngt.uuid_type};

    memset(&init, 0, sizeof(init));
    init.advdata.name_type               	= BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance      	= false;
    init.advdata.flags                   	= BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    init.srdata.uuids_complete.p_uuids  	= &adv_uuid;
    init.srdata.uuids_complete.uuid_cnt 	= 1;
    init.config.ble_adv_fast_enabled  		= true;
    init.config.ble_adv_fast_interval 		= APP_ADV_INTERVAL;
    init.config.ble_adv_fast_timeout  		= APP_ADV_TIMEOUT_IN_SECONDS;
    init.evt_handler = adv_evt_handler;

    err_code = ble_advertising_init(&m_advertising, &init);
    VERIFY_SUCCESS(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
	
    return NRF_SUCCESS;
}



/**@brief Function for applying the configuration.
 *
 */
static uint32_t lora_config_apply(ble_loramngt_config_t *p_config)
{
     ret_code_t err_code;
    int8_t power;
    uint32_t bw;
    uint32_t sf;
	
    // stop running timer
    app_timer_stop(lora_tx_timer_id);

    power = 14 - 2*p_config->tx_power;

#if defined(ISP4520_EU) || defined(ISP4520_JP)
    switch(p_config->data_rate)
    {
        case 0:
            bw = 0;
            sf = 12;
            break;
        case 1:
            bw = 0;
            sf = 11;
            break;
        case 2:
            bw = 0;
            sf = 10;
            break;
        case 3:
            bw = 0;
            sf = 9;
            break;
        case 4:
            bw = 0;
            sf = 8;
            break;
        case 5:
            bw = 0;
            sf = 7;
            break;
        case 6:
            bw = 1;
            sf = 7;
            break;
        default:
            bw = 0;
            sf = 12;
            break;
    }
#elif defined(ISP4520_US)
    switch(p_config->data_rate)
    {
        case 0:
            bw = 0;
            sf = 10;
            break;
        case 1:
            bw = 0;
            sf = 9;
            break;
        case 2:
            bw = 0;
            sf = 8;
            break;
        case 3:
            bw = 0;
            sf = 7;
            break;
        default:
            bw = 0;
            sf = 10;
            break;
    }
#endif

    Radio.SetTxConfig(  MODEM_LORA, power, bw, sf,
                        7, LORA_CODINGRATE,
                        LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
                        true, 0, 0, LORA_IQ_INVERSION_ON, 3000);

    Radio.SetRxConfig(  MODEM_LORA, bw, sf,
                        LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
                        LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
                        0, true, 0, 0, LORA_IQ_INVERSION_ON, true);

    Radio.Sleep();
	
    *m_p_lora_config = *p_config;
	
    // start timer
    app_timer_start(lora_tx_timer_id, APP_TIMER_TICKS(m_p_lora_config->tx_request_interval * 1000), NULL);

    tx_lora_periodic_handler(NULL);
	
    return NRF_SUCCESS;
}

/**@brief Function for handling event from the loramngt service.
 *
 * @details This function will process the data received from the loramngt BLE Service.
 *
 * @param[in] p_loramngt   	loramngt service structure.
 * @param[in] evt_type 		loramngt service event type.
 * @param[in] p_data   		Event data.
 * @param[in] length   		Length of the data.
 */
static void ble_loramngt_evt_handler( 	ble_loramngt_t        	*p_loramngt,
                                        ble_loramngt_evt_type_t evt_type,
                                        uint8_t                 *p_data,
                                        uint16_t                length)
{
    switch (evt_type)
    {
        case BLE_LORAMNGT_EVT_CONFIG_RECEIVED:
        {
            NRF_LOG_INFO("BLE_LORAMNGT_EVT_CONFIG_RECEIVED: %d", length);
            APP_ERROR_CHECK_BOOL(length == sizeof(ble_loramngt_config_t));

            (void)m_loramngt_flash_config_store((ble_loramngt_config_t *)p_data);
            (void)lora_config_apply((ble_loramngt_config_t *)p_data);
            break;
        }

        default:
            break;
    }		
}

/**@brief Function for initializing the loramngt Service.
 *
 * @details This callback function will be called from the ble handling module to initialize the loramngt service.
 *
 * @retval NRF_SUCCESS If initialization was successful.
 */
static uint32_t services_init (void)
{
    uint32_t err_code;
    ble_loramngt_init_t loramngt_init;
    ble_dis_init_t          dis_init;

    // Load configuration from flash.   
    err_code = m_loramngt_flash_init(&m_lora_default_config, &m_p_lora_config);
    VERIFY_SUCCESS(err_code);
	
    // Verify configuration retreived from flash. 
    //TODO
//    err_code = config_verify(m_p_lora_config);
//    VERIFY_SUCCESS(err_code);

   // Initialize services
    memset(&loramngt_init, 0, sizeof(ble_loramngt_init_t));
    loramngt_init.p_init_config 	= m_p_lora_config;
    loramngt_init.evt_handler           = ble_loramngt_evt_handler;
    err_code = ble_loramngt_init(&m_loramngt, &loramngt_init);
    VERIFY_SUCCESS(err_code);

    memset (&dis_init, 0, sizeof(ble_dis_init_t));
    ble_srv_ascii_to_utf8 (&dis_init.fw_rev_str, (char*)FW_REVISION);
    ble_srv_ascii_to_utf8 (&dis_init.hw_rev_str, (char*)HW_REVISION);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN (&dis_init.dis_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS (&dis_init.dis_attr_md.write_perm);
    err_code = ble_dis_init(&dis_init);
    VERIFY_SUCCESS(err_code);

    return NRF_SUCCESS;
}

static void send_lora_frame (void)
{
    uint32_t err_code;
    uint8_t buffer[30];
    uint8_t buffsize;
    float temperature_f32, humidity_f32, pressure_f32;
    double light_f32;
    drv_lsm9ds1_acc_data_t raw_acc;
    drv_lsm9ds1_mag_data_t raw_mag;
    drv_lsm9ds1_gyro_data_t raw_gyro;
    float acc_f32[3], mag_f32[3], gyro_f32[3];
	
    uint16_t pressure;
    int16_t temperature;
    uint16_t humidity;
    uint16_t light;
    int16_t acc[3];
    int16_t mag[3];
    int16_t gyro[3];
	
#if defined(SENSORS_PRESENT)
    // Enable all sensors
    drv_humidity_enable();
    drv_pressure_enable();
    drv_light_enable();
    drv_motion_enable(DRV_MOTION_FEATURE_MASK_RAW);
	
    nrf_delay_ms(500);
	
    // Temperature
    err_code = drv_temperature_and_humidity_get(&temperature_f32, &humidity_f32);
    if (err_code != NRF_SUCCESS)
    {
        temperature_f32 = 0;
        humidity_f32 = 0;
    }
	
    // Pressure
    drv_pressure_sample();
    nrf_delay_ms(5);
    err_code = drv_pressure_get(&pressure_f32);
    if (err_code != NRF_SUCCESS)
    {
        pressure_f32 = 0;
    }
	
    // Light
    err_code = drv_light_get(&light_f32);
    if (err_code != NRF_SUCCESS)
    {
        light_f32 = 0;
    }
	
    // Motion
    err_code = drv_motion_raw_acc_get(&raw_acc);
    if (err_code != NRF_SUCCESS)
    {
        raw_acc.x = 0;
        raw_acc.y = 0;
        raw_acc.z = 0;
    }
    err_code = drv_motion_raw_mag_get(&raw_mag);
    if (err_code != NRF_SUCCESS)
    {
        raw_mag.x = 0;
        raw_mag.y = 0;
        raw_mag.z = 0;
    }
    err_code = drv_motion_raw_gyro_get(&raw_gyro);
    if (err_code != NRF_SUCCESS)
    {
        raw_gyro.x = 0;
        raw_gyro.y = 0;
        raw_gyro.z = 0;
    }
    acc_f32[0] 	= raw_acc.x * ACC_SCALE_2G;
    acc_f32[1] 	= raw_acc.y * ACC_SCALE_2G;
    acc_f32[2] 	= raw_acc.z * ACC_SCALE_2G;
    mag_f32[0] 	= raw_mag.x * GYRO_SCALE_2000DPS;
    mag_f32[1] 	= raw_mag.y * GYRO_SCALE_2000DPS;
    mag_f32[2] 	= raw_mag.z * GYRO_SCALE_2000DPS;
    gyro_f32[0] = raw_gyro.x * MAG_SCALE_4Gs_IN_UT;
    gyro_f32[1] = raw_gyro.y * MAG_SCALE_4Gs_IN_UT;
    gyro_f32[2] = raw_gyro.z * MAG_SCALE_4Gs_IN_UT;
	
    drv_humidity_disable();
    drv_pressure_disable();
    drv_light_disable();
    drv_motion_disable(DRV_MOTION_FEATURE_MASK_RAW);
	
#else
    temperature_f32             = DUMMY_TEMPERATURE;
    humidity_f32 		= DUMMY_HUMIDITY;
    pressure_f32 		= DUMMY_PRESSURE;
    light_f32 			= DUMMY_LIGHT;
    acc_f32[0] 			= DUMMY_ACC_X;
    acc_f32[1] 			= DUMMY_ACC_Y;
    acc_f32[2] 			= DUMMY_ACC_Z;
    mag_f32[0] 			= DUMMY_MAG_X;
    mag_f32[1] 			= DUMMY_MAG_Y;
    mag_f32[2] 			= DUMMY_MAG_Z;
    gyro_f32[0] 		= DUMMY_GYRO_X;
    gyro_f32[1] 		= DUMMY_GYRO_Y;
    gyro_f32[2] 		= DUMMY_GYRO_Z;	
#endif
	
    pressure    = (uint16_t)(pressure_f32 * 10);        /* in hPa * 10 	*/
    temperature = (int16_t)(temperature_f32 * 100);     /* in °C * 100 	*/
    humidity    = (uint16_t)(humidity_f32 * 100);       /* in % * 100  	*/
    light       = (uint16_t)(light_f32 * 100);          /* in lux * 100 */	
    acc[0] 	= (int16_t)(acc_f32[0] * 100);          /* in g * 100   */
    acc[1] 	= (int16_t)(acc_f32[1] * 100);          /* in g * 100   */	
    acc[2] 	= (int16_t)(acc_f32[2] * 100);          /* in g * 100   */
    mag[0] 	= (int16_t)(mag_f32[0] * 100);          /* in uT * 100  */
    mag[1] 	= (int16_t)(mag_f32[1] * 100);          /* in uT * 100  */
    mag[2] 	= (int16_t)(mag_f32[2] * 100);          /* in uT * 100  */
    gyro[0]     = (int16_t)(gyro_f32[0] * 100);         /* in dps * 100 */
    gyro[1]     = (int16_t)(gyro_f32[1] * 100);         /* in dps * 100 */
    gyro[2]     = (int16_t)(gyro_f32[2] * 100);         /* in dps * 100 */
	
    NRF_LOG_INFO("press = %d hPa",          pressure);
    NRF_LOG_INFO("temp  = %d °C",           temperature);
    NRF_LOG_INFO("humi  = %d %",            humidity);
    NRF_LOG_INFO("light = %d lux",          light);
    NRF_LOG_INFO("acc   = %d %d %d g",      acc[0], acc[1], acc[2]);
    NRF_LOG_INFO("mag   = %d %d %d uT",     mag[0], mag[1], mag[2]);
    NRF_LOG_INFO("rot   = %d %d %d dps",    gyro[0], gyro[1], gyro[2]);
	
    /*
    Lora Packet structure:
	
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
     ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------
          |           |           |           |           |           |           |           |           |           |           |           |           |
      PRESSURE   TEMPERATURE   HUMIDITY     LIGHT       ACC X       ACC Y       ACC Z       MAG X       MAG Y       MAG Z       GYRO X      GYRO Y      GYRO Z  
        uint16      int16       uint16      uint16      int16       int16       int16       int16       int16       int16        int16       int16       int16
    */
    uint32_t i = 0;
    buffer[i++] = m_lora_frame_counter++;
    buffer[i++] = (pressure >> 8) & 0xFF;
    buffer[i++] = pressure & 0xFF;
    buffer[i++] = (temperature >> 8) & 0xFF;
    buffer[i++] = temperature & 0xFF;
    buffer[i++] = (humidity >> 8) & 0xFF;
    buffer[i++] = humidity & 0xFF;
    buffer[i++] = (light >> 8) & 0xFF;
    buffer[i++] = light & 0xFF;
    buffer[i++] = (acc[0] >> 8) & 0xFF;
    buffer[i++] = acc[0] & 0xFF;
    buffer[i++] = (acc[1] >> 8) & 0xFF;
    buffer[i++] = acc[1] & 0xFF;
    buffer[i++] = (acc[2] >> 8) & 0xFF;
    buffer[i++] = acc[2] & 0xFF;
    buffer[i++] = (mag[0] >> 8) & 0xFF;
    buffer[i++] = mag[0] & 0xFF;
    buffer[i++] = (mag[1] >> 8) & 0xFF;
    buffer[i++] = mag[1] & 0xFF;
    buffer[i++] = (mag[2] >> 8) & 0xFF;
    buffer[i++] = mag[2] & 0xFF;
    buffer[i++] = (gyro[0] >> 8) & 0xFF;
    buffer[i++] = gyro[0] & 0xFF;
    buffer[i++] = (gyro[1] >> 8) & 0xFF;
    buffer[i++] = gyro[1] & 0xFF;
    buffer[i++] = (gyro[2] >> 8) & 0xFF;
    buffer[i++] = gyro[2] & 0xFF;
    buffsize = i;
  
    Radio.Send(buffer, buffsize);
}

/**@brief Function for handling Peer Manager events.
 *
 * @param[in] p_evt  Peer Manager event.
 */
static void pm_evt_handler (pm_evt_t const * p_evt)
{
    switch (p_evt->evt_id)
    {
        case PM_EVT_PEERS_DELETE_SUCCEEDED:
            ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            break;

        default:
            break;
    }
}

/**@brief Function for the Peer Manager initialization.
 */
#define SEC_PARAM_BOND                      1                                       /**< Perform bonding. */
#define SEC_PARAM_MITM                      0                                       /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC                      0                                       /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS                  0                                       /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES           BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
#define SEC_PARAM_OOB                       0                                       /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE              7                                       /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE              16                                      /**< Maximum encryption key size. */
static uint32_t peer_manager_init(void)
{
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    VERIFY_SUCCESS(err_code);

    // Security parameters to be used for all security procedures.
    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.lesc           = SEC_PARAM_LESC;
    sec_param.keypress       = SEC_PARAM_KEYPRESS;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    VERIFY_SUCCESS(err_code);

    err_code = pm_register(pm_evt_handler);
    VERIFY_SUCCESS(err_code);

    return NRF_SUCCESS;
}

/**@brief Function for putting the chip into sleep mode.
 *
 * @note This function will not return.
 */
static void sleep_mode_enter (void)
{
    ret_code_t err_code;

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    err_code = sd_power_system_off();
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the nrf log module.
 */
static void log_init (void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}
#define FPU_EXCEPTION_MASK 0x0000009F 
/**@brief Function for the Power manager.
 */
static void power_manage (void)
{
    /* Clear exceptions and PendingIRQ from the FPU unit */
    __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK));      
    (void) __get_FPSCR();
    NVIC_ClearPendingIRQ(FPU_IRQn);
	
    ret_code_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for application main entry.
 */
int main (void)
{
    uint32_t err_code;
	
    // Initialize logs.
    log_init();
    NRF_LOG_INFO("BLE LORA sensor board started.");
	
    // Initialize clocks
    nrf_drv_clock_init();
    nrf_drv_clock_lfclk_request(NULL);
	
    // Initialize Scheduler and timer
    err_code = timers_init();
    APP_ERROR_CHECK(err_code);
	
#if defined(BTN_LED_PRESENT)
    pwr_led_state = false;
    ble_led_state = false;
    pwr_led_periodic_handler(NULL);
	
    // Initialize button
    err_code = button_init();
    APP_ERROR_CHECK(err_code);
#endif
	
#if defined(SENSORS_PRESENT)
    nrf_delay_ms(1000);
	
    /**@brief Initialize the TWI manager. */
    err_code = twi_manager_init(APP_IRQ_PRIORITY_THREAD);
    APP_ERROR_CHECK(err_code);
	
    // Initialize sensors
    err_code = pressure_sensor_init(&m_twi_sensors);
    APP_ERROR_CHECK(err_code);

    err_code = humidity_sensor_init(&m_twi_sensors);
    APP_ERROR_CHECK(err_code);
	
    err_code = light_sensor_init(&m_twi_sensors);
    APP_ERROR_CHECK(err_code);
	
    err_code = motion_sensor_init(&m_twi_sensors);
    APP_ERROR_CHECK(err_code);
#endif
	
    // Initialize BLE
    err_code = ble_stack_init();
    APP_ERROR_CHECK(err_code);
    err_code = gap_params_init();
    APP_ERROR_CHECK(err_code);

    err_code = conn_params_init();
    APP_ERROR_CHECK(err_code);
	
    err_code = services_init();
    APP_ERROR_CHECK(err_code);

    err_code = peer_manager_init();
    APP_ERROR_CHECK(err_code);
	
    err_code = advertising_init();
    APP_ERROR_CHECK(err_code);
    // Initialize LoRa chip.
    err_code = lora_hardware_init();
    APP_ERROR_CHECK(err_code);

    // Initialize LoRa
    LoRaEvents.TxDone       = lora_tx_done_evt_handler;
    LoRaEvents.RxDone       = lora_rx_done_evt_handler;
    LoRaEvents.TxTimeout    = lora_tx_timeout_evt_handler;
    LoRaEvents.RxTimeout    = lora_rx_timeout_evt_handler;
    LoRaEvents.RxError      = lora_rx_error_evt_handler;
    Radio.Init(&LoRaEvents);
    Radio.SetChannel(RF_FREQUENCY);

    lora_config_apply(m_p_lora_config);

    // Start BLE
    ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
	
    // Enter main loop.
    for (;;)
    {
        Radio.IrqProcess();
        app_sched_execute();
        if (NRF_LOG_PROCESS() == false)
        {
            power_manage();
        }
    }
}

