/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. * * The information contained herein is property of Nordic Semiconductor ASA. * Terms and conditions of usage are described in detail in NORDIC * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. * * Licensees are granted free, non-transferable use of the information. NO * WARRANTY of ANY KIND is provided. This heading must NOT be removed from * the file. * */ /* clang-format off */ /** @file * * @defgroup iot_sdk_tcp_server main.c * @{ * @ingroup iot_sdk_app_lwip * @brief This file contains the source code for LwIP TCP Server sample application. * */ #include #include #include "boards.h" #include "app_timer_appsh.h" #include "app_scheduler.h" #include "app_button.h" #include "nordic_common.h" #include "softdevice_handler_appsh.h" #include "ble_advdata.h" #include "ble_srv_common.h" #include "ble_ipsp.h" #include "ble_6lowpan.h" #include "mem_manager.h" #include "app_trace.h" /* * arm-none-eabi-gcc has BYTE_ORDER already defined, so in order to avoid * warnings in lwip, we have to undefine it * * TODO: Check if in the future versions of nRF5 SDK that changes. * Current version of nRF51 SDK: 0.8.0 * nRF5 SDK: 0.9.0 */ #undef BYTE_ORDER #include "lwip/init.h" #include "lwip/inet6.h" #include "lwip/ip6.h" #include "lwip/ip6_addr.h" #include "lwip/netif.h" /*lint -save -e607 */ #include "lwip/tcp.h" /*lint -restore */ #include "lwip/timers.h" #include "nrf_platform_port.h" #include "app_util_platform.h" #define DEVICE_NAME "LwIPTCPServer" /**< Device name used in BLE undirected advertisement. */ #define APP_TIMER_PRESCALER NRF51_DRIVER_TIMER_PRESCALER /**< Value of the RTC1 PRESCALER register. */ #define APP_TIMER_MAX_TIMERS 1 /**< Maximum number of simultaneously created timers. */ #define APP_TIMER_OP_QUEUE_SIZE 1 #define LWIP_SYS_TIMER_INTERVAL APP_TIMER_TICKS(250, APP_TIMER_PRESCALER) /**< Interval for timer used as trigger to send. */ #define SCHED_MAX_EVENT_DATA_SIZE 128 /**< Maximum size of scheduler events. */ #define SCHED_QUEUE_SIZE 12 /**< Maximum number of events in the scheduler queue. */ #define ADVERTISING_LED BSP_LED_0_MASK /**< Is on when device is advertising. */ #define CONNECTED_LED BSP_LED_1_MASK /**< Is on when device is connected. */ #define TCP_CONNECTED_LED BSP_LED_2_MASK /**< Is on when device is connected. */ #define DISPLAY_LED_0 BSP_LED_0_MASK /**< LED used for displaying mod 4 of data payload received on UDP port. */ #define DISPLAY_LED_1 BSP_LED_1_MASK /**< LED used for displaying mod 4 of data payload received on UDP port. */ #define DISPLAY_LED_2 BSP_LED_2_MASK /**< LED used for displaying mod 4 of data payload received on UDP port. */ #define DISPLAY_LED_3 BSP_LED_3_MASK /**< LED used for displaying mod 4 of data payload received on UDP port. */ #define ALL_APP_LED (BSP_LED_0_MASK | BSP_LED_1_MASK | \ BSP_LED_2_MASK | BSP_LED_3_MASK) /**< Define used for simultaneous operation of all application LEDs. */ #define APP_ADV_TIMEOUT 0 /**< Time for which the device must be advertising in non-connectable mode (in seconds). 0 disables timeout. */ #define APP_ADV_ADV_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS) /**< The advertising interval. This value can vary between 100ms to 10.24s). */ #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ #define APPL_LOG app_trace_log /**< Macro for logging application messages on UART, in case ENABLE_DEBUG_LOG_SUPPORT is not defined, no logging occurs. */ #define APPL_DUMP app_trace_dump /**< Macro for dumping application data on UART, in case ENABLE_DEBUG_LOG_SUPPORT is not defined, no logging occurs. */ #define TCP_SERVER_PORT 9000 /**< TCP server listen port number. */ #define TCP_DATA_SIZE 8 /**< UDP Data size sent on remote. */ typedef enum { TCP_STATE_IDLE, TCP_STATE_REQUEST_CONNECTION, TCP_STATE_CONNECTED, TCP_STATE_DATA_TX_IN_PROGRESS, TCP_STATE_DISCONNECTED }tcp_state_t; eui64_t eui64_local_iid; /**< Local EUI64 value that is used as the IID for*/ static ble_gap_adv_params_t m_adv_params; /**< Parameters to be passed to the stack when starting advertising. */ static app_timer_id_t m_sys_timer_id; /**< System Timer used to service LwIP timers periodically. */ static struct tcp_pcb * mp_tcp_port; /**< TCP Port to listen on. */ static tcp_state_t m_tcp_state; /**< TCP State information. */ /**@brief Function for error handling, which is called when an error has occurred. * * @warning This handler is an example only and does not fit a final product. You need to analyse * how your product is supposed to react in case of error. * * @param[in] error_code Error code supplied to the handler. * @param[in] line_num Line number where the handler is called. * @param[in] p_file_name Pointer to the file name. */ void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name) { //Halt the application and notify of error using the LEDs. APPL_LOG("[** ASSERT **]: Error 0x%08lX, Line %ld, File %s\r\n",error_code, line_num, p_file_name); LEDS_ON(ALL_APP_LED); for(;;) { } // @note: In case on assert, it is desired to only recover and reset, uncomment the line below. //NVIC_SystemReset(); } /**@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 analyse * 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 the LEDs initialization. * * @details Initializes all LEDs used by this application. */ static void leds_init(void) { // Configure application LED pins. LEDS_CONFIGURE(ALL_APP_LED); // Turn off all LED on initialization. LEDS_OFF(ALL_APP_LED); } /**@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) { uint32_t err_code; ble_advdata_t advdata; uint8_t flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; ble_gap_conn_sec_mode_t sec_mode; ble_gap_addr_t my_addr; 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); err_code = sd_ble_gap_address_get(&my_addr); APP_ERROR_CHECK(err_code); my_addr.addr[5] = 0x00; my_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; err_code = sd_ble_gap_address_set(&my_addr); APP_ERROR_CHECK(err_code); IPV6_EUI64_CREATE_FROM_EUI48(eui64_local_iid.identifier, my_addr.addr, my_addr.addr_type); ble_uuid_t adv_uuids[] = { {BLE_UUID_IPSP_SERVICE, BLE_UUID_TYPE_BLE} }; //Build and set advertising data. memset(&advdata, 0, sizeof(advdata)); advdata.name_type = BLE_ADVDATA_FULL_NAME; advdata.flags = flags; advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]); advdata.uuids_complete.p_uuids = adv_uuids; err_code = ble_advdata_set(&advdata, NULL); APP_ERROR_CHECK(err_code); //Initialize advertising parameters (used when starting advertising). memset(&m_adv_params, 0, sizeof(m_adv_params)); m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND; m_adv_params.p_peer_addr = NULL; // Undirected advertisement. m_adv_params.fp = BLE_GAP_ADV_FP_ANY; m_adv_params.interval = APP_ADV_ADV_INTERVAL; m_adv_params.timeout = APP_ADV_TIMEOUT; } /**@brief Function for starting advertising. */ static void advertising_start(void) { uint32_t err_code; err_code = sd_ble_gap_adv_start(&m_adv_params); APP_ERROR_CHECK(err_code); LEDS_ON(ADVERTISING_LED); } /**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: APPL_LOG ("[APPL]: Connected.\r\n"); break; case BLE_GAP_EVT_DISCONNECTED: APPL_LOG ("[APPL]: Disconnected.\r\n"); advertising_start(); break; default: break; } } /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler. * * @details This function is called from the BLE Stack event interrupt handler after a BLE stack * event has been received. * * @param[in] p_ble_evt Bluetooth stack event. */ static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { ble_ipsp_evt_handler(p_ble_evt); on_ble_evt(p_ble_evt); } /**@brief Function for initializing the BLE stack. * * @details Initializes the SoftDevice and the BLE event interrupt. */ static void ble_stack_init(void) { uint32_t err_code; // Initialize the SoftDevice handler module. SOFTDEVICE_HANDLER_APPSH_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, true); // Register with the SoftDevice handler module for BLE events. err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch); APP_ERROR_CHECK(err_code); } /**@brief Function for the Event Scheduler initialization. */ static void scheduler_init(void) { APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); } /**@brief Function to TCP port. * * @details Function to close the TCP port and reset any information on the port. */ static void tcp_port_close(struct tcp_pcb * p_pcb) { m_tcp_state = TCP_STATE_REQUEST_CONNECTION; //Reset all information set on and/or callback registered for the port. tcp_arg(p_pcb, NULL); tcp_sent(p_pcb, NULL); tcp_recv(p_pcb, NULL); tcp_err(p_pcb, NULL); tcp_poll(p_pcb, NULL, 0); UNUSED_VARIABLE(tcp_close(p_pcb)); LEDS_OFF((DISPLAY_LED_0 | DISPLAY_LED_1 | DISPLAY_LED_2 | DISPLAY_LED_3)); LEDS_ON(CONNECTED_LED); } /**@brief TCP Port Write complete callback. * * @details Calbback registered to be notified of the write complete event on the TCP port. * In case write complete is notified with 'zero' length, port is closed. * * @param[in] p_arg Receive argument set on the port. * @param[in] p_pcb PCB identifier of the port. * @param[in] len Length of data written successfully. * * @retval ERR_OK. */ static err_t tcp_write_complete(void * p_arg, struct tcp_pcb * p_pcb, u16_t len) { UNUSED_PARAMETER(p_arg); UNUSED_PARAMETER(p_pcb); if (len != 0) { //Write complete, reset the state to connected from transmit pending. m_tcp_state = TCP_STATE_CONNECTED; } else { //Something is not right on the port, close the port. tcp_port_close(mp_tcp_port); } return ERR_OK; } /**@brief Send test data on the port. * * @details Sends TCP data in Request of size 8 in format described in description above. * * @param[in] p_pcb PCB identifier of the port. */ static void tcp_send_data(struct tcp_pcb * p_pcb, uint32_t sequence_number) { err_t err = ERR_OK; if (m_tcp_state != TCP_STATE_DATA_TX_IN_PROGRESS) { //Register callback to get notification of data reception is complete. tcp_sent(p_pcb, tcp_write_complete); uint8_t tcp_data[TCP_DATA_SIZE]; tcp_data[0] = (uint8_t )((sequence_number >> 24) & 0x000000FF); tcp_data[1] = (uint8_t )((sequence_number >> 16) & 0x000000FF); tcp_data[2] = (uint8_t )((sequence_number >> 8) & 0x000000FF); tcp_data[3] = (uint8_t )(sequence_number & 0x000000FF); tcp_data[4] = 'P'; tcp_data[5] = 'o'; tcp_data[6] = 'n'; tcp_data[7] = 'g'; //Enqueue data for transmission. err = tcp_write(p_pcb, tcp_data, TCP_DATA_SIZE, 1); if (err != ERR_OK) { APPL_LOG ("[APPL]: Failed to send TCP packet, reason %d\r\n", err); } else { m_tcp_state = TCP_STATE_DATA_TX_IN_PROGRESS; } } else { //Wait for tx to be complete. } } /**@brief Callback registered for receiving data on the TCP Port. * * @param[in] p_arg Receive argument set on the TCP port. * @param[in] p_pcb TCP PCB on which data is received. * @param[in] p_buffer Buffer with received data. * @param[in] err Event result indicating error associated with the receive, * if any, else ERR_OK. */ err_t tcp_recv_data_handler(void * p_arg, struct tcp_pcb * p_pcb, struct pbuf * p_buffer, err_t err) { APPL_LOG ("[APPL]: >> TCP Data.\r\n"); //Check event result before proceeding. if (err == ERR_OK) { uint8_t *p_data = p_buffer->payload; if (p_buffer->len == TCP_DATA_SIZE) { uint32_t sequence_number = 0; sequence_number = ((p_data[0] << 24) & 0xFF000000); sequence_number |= ((p_data[1] << 16) & 0x00FF0000); sequence_number |= ((p_data[2] << 8) & 0x0000FF00); sequence_number |= (p_data[3] & 0x000000FF); LEDS_OFF(ALL_APP_LED); if (sequence_number & 0x00000001) { LEDS_ON(DISPLAY_LED_0); } if (sequence_number & 0x00000002) { LEDS_ON(DISPLAY_LED_1); } if (sequence_number & 0x00000004) { LEDS_ON(DISPLAY_LED_2); } if (sequence_number & 0x00000008) { LEDS_ON(DISPLAY_LED_3); } //Send Response tcp_send_data(p_pcb, sequence_number); } else { APPL_LOG ("[APPL]: TCP data received in incorrect format.\r\n"); } // All is good with the data received, process it. tcp_recved(p_pcb, p_buffer->tot_len); UNUSED_VARIABLE(pbuf_free(p_buffer)); } else { //Free the buffer in case its not NULL. if (p_buffer != NULL) { UNUSED_VARIABLE(pbuf_free(p_buffer)); } //Something is not right with the port, close the port. tcp_port_close(mp_tcp_port); } return ERR_OK; } /**@brief Function for initializing IP stack. * * @details Initialize the IP Stack and its driver. */ static void ip_stack_init(void) { uint32_t err_code = nrf51_sdk_mem_init(); APP_ERROR_CHECK(err_code); //Initialize LwIP stack. lwip_init(); //Initialize LwIP stack driver. err_code = nrf51_driver_init(); APP_ERROR_CHECK(err_code); } /**@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(void * p_context) { UNUSED_VARIABLE(p_context); sys_check_timeouts(); } /**@brief Function for the Timer initialization. * * @details Initializes the timer module. This creates and starts application timers. */ static void timers_init(void) { uint32_t err_code; // Initialize timer module. APP_TIMER_APPSH_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true); // Create timers. err_code = app_timer_create(&m_sys_timer_id, APP_TIMER_MODE_REPEATED, system_timer_callback); APP_ERROR_CHECK(err_code); } /**@brief Function to handle interface up event. */ void nrf51_driver_interface_up(void) { uint32_t err_code; APPL_LOG ("[APPL]: IPv6 interface up.\r\n"); sys_check_timeouts(); m_tcp_state = TCP_STATE_REQUEST_CONNECTION; err_code = app_timer_start(m_sys_timer_id, LWIP_SYS_TIMER_INTERVAL, NULL); APP_ERROR_CHECK(err_code); LEDS_OFF(ADVERTISING_LED); LEDS_ON(CONNECTED_LED); } /**@brief Function to handle interface down event. */ void nrf51_driver_interface_down(void) { uint32_t err_code; APPL_LOG ("[APPL]: IPv6 interface down.\r\n"); err_code = app_timer_stop(m_sys_timer_id); APP_ERROR_CHECK(err_code); LEDS_OFF((DISPLAY_LED_0 | DISPLAY_LED_1 | DISPLAY_LED_2 | DISPLAY_LED_3)); LEDS_ON(ADVERTISING_LED); m_tcp_state = TCP_STATE_DISCONNECTED; } void bleconfig_init(void) { //Initialize. app_trace_init(); leds_init(); timers_init(); ble_stack_init(); advertising_init(); ip_stack_init (); scheduler_init(); APPL_LOG ("\r\n"); APPL_LOG ("[APPL]: Init done.\r\n"); //Start execution. advertising_start(); } void bleconfig_poll(void) { //Execute event schedule. app_sched_execute(); }