mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-27 19:17:48 +08:00
c04e3f80b9
PUBLISHED_FROM=6af3ed56802d2619f673c36059370440a0c06397
570 lines
18 KiB
C
570 lines
18 KiB
C
/* 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
#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"
|
|
#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();
|
|
}
|
|
|