Spring 2016 A-TeChToP ATBTLC1000 Testing

By: Rose Leidenfrost (Electronics Engineer)

Table of Contents

Purpose

The purpose of this experimentation was to identify example applications for the ATBTLC1000 and determine which parts might be useful for software program subroutines. The example code used is also provided.

Send Seizure Alert

BT1

The Alert notification application is used to send ANS alerts over the BLE connection from the android phone via the Atmel smart connect app to the ATBTLC1000. This example program application is enabled to send missed call and new sms alerts to the remote device. The ANS example code will provide a starting foundation on which we will build upon to develop the seizure alert subroutine for the Seizure Watch. Useful sections of the program code are given below.

/**

* \file

*

* \brief Alert Notification Profile

*

* Copyright (c) 2015 Atmel Corporation. All rights reserved.

The function app_client_adv below is responsible for initializing the advertisements for Bluetooth connectivity based on the UUID. It then begins sending the advertisement so that it may be paired with the android Bluetooth device. The debug terminal is updated throughout with the status of the advertisement set up.

void anp_client_adv(void)

{

uint8_t idx = 0;

uint16_t adv_service_uuid;

adv_service_uuid = ANP_SERVICE_UUID ;

uint8_t adv_data[ANP_ADV_DATA_NAME_LEN + ANP_ADV_DATA_UUID_LEN  + 2*2];

// Prepare ADV Data

adv_data[idx++] = ANP_ADV_DATA_NAME_LEN + ADV_TYPE_LEN;

adv_data[idx++] = ANP_ADV_DATA_NAME_TYPE;

memcpy(&adv_data[idx], ANP_ADV_DATA_NAME_DATA, ANP_ADV_DATA_NAME_LEN);

idx += ANP_ADV_DATA_NAME_LEN;

adv_data[idx++] = ANP_ADV_DATA_UUID_LEN  + ADV_TYPE_LEN;

adv_data[idx++] = ANP_ADV_DATA_UUID_TYPE;

memcpy(&adv_data[idx], &adv_service_uuid, ANP_ADV_DATA_UUID_LEN );

idx += ANP_ADV_DATA_UUID_LEN;

if (at_ble_adv_data_set(adv_data, idx, scan_rsp_data, SCAN_RESP_LEN)

!= AT_BLE_SUCCESS) {

DBG_LOG(“adv set data not successful”);

}

if(at_ble_adv_start(AT_BLE_ADV_TYPE_UNDIRECTED, AT_BLE_ADV_GEN_DISCOVERABLE,

NULL, AT_BLE_ADV_FP_ANY,APP_ANP_FAST_ADV, APP_ANP_ADV_TIMEOUT,

0) != AT_BLE_SUCCESS) {

DBG_LOG(“Failed to start advertisement”);

} else {

DBG_LOG(“Device is in Advertising Mode”);

}

}

The function anp_info_service_discover() discovers which of the Alert services will be utilized in this context. The function searches to discover the generic attribute profile (GATT) which describes how the two devices communicate.

at_ble_status_t anp_info_service_discover(at_ble_connected_t *conn_params)

{

if (conn_params->conn_status != AT_BLE_SUCCESS) {

return conn_params->conn_status;

}

if(at_ble_primary_service_discover_all(conn_params->handle,

GATT_DISCOVERY_STARTING_HANDLE,

GATT_DISCOVERY_ENDING_HANDLE)

== AT_BLE_SUCCESS) {

DBG_LOG_DEV(“GATT Discovery request started “);

return AT_BLE_SUCCESS;

} else {

DBG_LOG(“GATT Discovery request failed”);

}

return AT_BLE_FAILURE;

}

The function anp_client_characteristic_found_handler() occurs in the code once the BLE connection has been established and confirmed. Within this function the type of alert notification is identified and defined by a specific UUID. Again, status messages are updated to the debug terminal.

void anp_client_characteristic_found_handler(at_ble_characteristic_found_t *characteristic_found)

{

uint16_t charac_16_uuid;

DBG_LOG_DEV(“The characteristic type is %d”,characteristic_found->char_uuid.type);

charac_16_uuid = (uint16_t)((characteristic_found->char_uuid.uuid[0]) | \

(characteristic_found->char_uuid.uuid[1] << 8));

switch(charac_16_uuid)

{

/* Supported New Alert Category */

case SUPPORT_NEW_ALERT_CHAR_UUID :

{

anp_handle.supp_new_char_handle = characteristic_found->value_handle;

DBG_LOG(“Supported new alert category characteristic discovered”);

DBG_LOG_DEV(“Supported New Alert Category characteristics “

“%04X”,anp_handle.supp_new_char_handle);

DBG_LOG_DEV(“Characteristic Info ConnHandle 0x%02x : “

“Char handle 0x%02x : Value handle : 0x%02x :”

” Properties : 0x%02x”,

characteristic_found->conn_handle,

characteristic_found->char_handle,

characteristic_found->value_handle,

characteristic_found->properties);

DBG_LOG_DEV(“UUID : 0x%02x%02x”,characteristic_found->char_uuid.uuid[1],

characteristic_found->char_uuid.uuid[0]);

}

break;

/* New Alert Category */

case NEW_ALERT_CHAR_UUID:

{

anp_handle.new_alert_char_handle = characteristic_found->value_handle;

DBG_LOG(“New alert category characteristic discovered”);

DBG_LOG_DEV(“New Alert characteristics %04X”,anp_handle.new_alert_char_handle);

DBG_LOG_DEV(“Characteristic Info ConnHandle 0x%02x : “

“Char handle 0x%02x : Value handle : 0x%02x”

” : Properties : 0x%02x”,

characteristic_found->conn_handle,

characteristic_found->char_handle,

characteristic_found->value_handle,

characteristic_found->properties);

DBG_LOG_DEV(“UUID : 0x%02x%02x”,

characteristic_found->char_uuid.uuid[1],

characteristic_found->char_uuid.uuid[0]);

}

break;

/* Supported Unread Alert Category */

case SUPPORT_UNREAD_ALERT_CHAR_UUID:

{

anp_handle.supp_unread_char_handle = characteristic_found->value_handle;

DBG_LOG(“Supported unread alert characteristic discovered”);

DBG_LOG_DEV(“Supported Unread Alert Category characteristics “

“%04X”,anp_handle.supp_unread_char_handle);

DBG_LOG_DEV(“Characteristic Info ConnHandle 0x%02x : Char handle 0x%02x”

” : Value handle : 0x%02x : Properties : 0x%02x”,

characteristic_found->conn_handle,

characteristic_found->char_handle,

characteristic_found->value_handle,

characteristic_found->properties);

DBG_LOG_DEV(“UUID : 0x%02x%02x”,characteristic_found->char_uuid.uuid[1],

characteristic_found->char_uuid.uuid[0]);

}

break;

/* Unread Alert Status*/

case UNREAD_ALERT_STATUS_CHAR_UUID:

{

anp_handle.unread_alert_char_handle = characteristic_found->value_handle;

DBG_LOG(“Unread alert status characteristic discovered”);

DBG_LOG_DEV(“Unread Alert status characteristics “

“%04X”,anp_handle.unread_alert_char_handle);

DBG_LOG_DEV(“Characteristic Info ConnHandle 0x%02x :”

” Char handle 0x%02x : Value handle : 0x%02x : Properties : 0x%02x”,

characteristic_found->conn_handle,

characteristic_found->char_handle,

characteristic_found->value_handle,

characteristic_found->properties);

DBG_LOG_DEV(“UUID : 0x%02x%02x”,characteristic_found->char_uuid.uuid[1],

characteristic_found->char_uuid.uuid[0]);

}

break;

/* Alert Notification Control Point */

case ALERT_NOTI_CONTROL_CHAR_UUID:

{

anp_handle.alert_np_char_handle = characteristic_found->value_handle;

DBG_LOG(“Alert Notification control characteristic discovered”);

DBG_LOG_DEV(“Alert Notification Control Point”

” characteristics %04X”,anp_handle.alert_np_char_handle);

DBG_LOG_DEV(“Characteristic Info ConnHandle 0x%02x : Char handle 0x%02x :”

” Value handle : 0x%02x : Properties : 0x%02x”,

characteristic_found->conn_handle,

characteristic_found->char_handle,

characteristic_found->value_handle,

characteristic_found->properties);

DBG_LOG_DEV(“UUID : 0x%02x%02x”,

characteristic_found->char_uuid.uuid[1],

characteristic_found->char_uuid.uuid[0]);

}

break;

}

anp_handle.desc_discovery= AT_BLE_SUCCESS;

}

Accepting Sensor Data

BT2

The Health thermometer profile example application utilizes the Xplained PRO on- board temperature sensor to take measurements of the ambient room temperature and send them over the BLE connection to the android phone. The Atmel Smart Connect app has a health temperature profile setting where it displays the changing temperature on a thermometer. The user has the ability to change the temperature measurement location by pressing sw0, the measurement will cycle through a list of pre-defined measurement locations which will also be displayed on the app interface. This example code will serve as a reference on how to accept sensor input to the BTLC1000 as it applies to our Seizure Watch. Useful sections of the program code are given below.

The function htp_temperature_send() reads the temperature from the Xplained PRO board and checks if the button has been pressed to update the measurement location. It also converts the measurement to Fahrenheit and outputs both the temperature in Celsius and Fahrenheit to the debug terminal.

static void htp_temperature_send(htp_app_t *htp_temp)

{

at_ble_prf_date_time_t timestamp;

#if SAMD21 || SAML21

float temperature;

/* Read Temperature Value from IO1 Xplained Pro */

temperature = at30tse_read_temperature();

#endif

 

#if SAMG55

double temperature;

/* Read Temperature Value from IO1 Xplained Pro */

at30tse_read_temperature(&temperature);

#endif

 

if(button_pressed)

{

update_temperature_type_location();

button_pressed = false;

}

 

if (htp_temp->flags & HTPT_FLAG_FAHRENHEIT)

{

temperature = (((temperature * 9.0)/5.0) + 32.0);

}

timestamp.day = 1;

timestamp.hour = 9;

timestamp.min = 2;

timestamp.month = 8;

timestamp.sec = 36;

timestamp.year = 15;

if(at_ble_htpt_temp_send(convert_ieee754_ieee11073_float((float)temperature),

&timestamp,

htp_temp->flags,

htp_temp->temperature_type,

STABLE_TEMPERATURE_VAL

) == AT_BLE_SUCCESS)

{

if (htp_temp->flags & HTPT_FLAG_FAHRENHEIT)

{

DBG_LOG(“Temperature: %d Fahrenheit”, (uint16_t)temperature);

}

else

{

DBG_LOG(“Temperature: %d Deg Celsius”, (uint16_t)temperature);

}

}

}

The function htp_init() initializes the health temperature database to its default settings and initializes the advertisement data.

static void htp_init(void)

{

/* Initialize the htp_data to default value */

htp_init_defaults(&htp_data);

/* Register the Initialized value into htp profile */

if(at_ble_htpt_create_db(

htp_data.optional,

htp_data.temperature_type,

htp_data.min_measurement_intv,

htp_data.max_meaurement_intv,

htp_data.measurement_interval,

htp_data.security_lvl,

&htpt_conn_handle) != AT_BLE_SUCCESS)

{

/* Failed to create HTP data base */

DBG_LOG(“HTP Data Base creation failed”);

while(1);

}

htpt_set_advertisement_data();

}

The function at30tse_read_temperature() reads the data from the 16-bit temperature register and converts the values to temperature based on the resolution of the ADC.

double at30tse_read_temperature()

{

/* Read the 16-bit temperature register. */

uint16_t data = at30tse_read_register(AT30TSE_TEMPERATURE_REG,

AT30TSE_NON_VOLATILE_REG,

AT30TSE_TEMPERATURE_REG_SIZE);

 

double temperature = 0;

int8_t sign = 1;

 

/*Check if negative and clear sign bit. */

if (data & (1 << 15)){

sign *= -1;

data &= ~(1 << 15);

}

 

/* Convert to temperature  */

switch (resolution){

case AT30TSE_CONFIG_RES_9_bit:

data = (data >> 7);

temperature = data * sign * 0.5;

break;

case AT30TSE_CONFIG_RES_10_bit:

data = (data >> 6);

temperature = data * sign * 0.25;

break;

case AT30TSE_CONFIG_RES_11_bit:

data = (data >> 5);

temperature = data * sign * 0.125;

break;

case AT30TSE_CONFIG_RES_12_bit:

data = (data >> 4);

temperature = data * sign * 0.0625;

break;

default:

break;

}

return temperature;

}

Send Telemetry Data

BT3

The custom serial chat application allows a user to send data between the BTLC1000 and a host platform with the Atmel Smart Connect app. This is an application example implemented over GATT or the generic attribute profile which defines the way that the two BLE devices communicate with each other.

The user is able to input a message in the debug terminal and have it displayed on the phone and vice versa. This example code will serve as a reference on how to communicate over BLE from the BTLC1000 to the Arxterra control panel as it applies to our Seizure Watch. Useful sections of the program code are given below.

/**

* \file

*

* \brief Custom Serial Chat Application declarations

*

* Copyright (c) 2015 Atmel Corporation. All rights reserved.

*

The function csc_app_recv_buf is the function used to receive data from the Smart Connect CSC app.

static void csc_app_recv_buf(uint8_t *recv_data, uint8_t recv_len)

{

uint16_t ind = 0;

if (recv_len){

for (ind = 0; ind < recv_len; ind++){

sio2host_putchar(recv_data[ind]);

}

DBG_LOG(“\r\n”);

}

}

Within the CSC app solution there is a file named sio2host.c that handles brief serial I/O functionalities from the host device. Below I have included two of the useful functions which transmit and receive the chat data.

uint8_t sio2host_tx(uint8_t *data, uint8_t length)

{

#if SAMD || SAMR21 || SAML21

status_code_genare_t status;

#else

status_code_t status;

#endif /*SAMD || SAMR21 || SAML21 */

 

do {

#if SAMD || SAMR21 || SAML21

status

= usart_serial_write_packet(&host_uart_module,

(const uint8_t *)data, length);

#else

status = usart_serial_write_packet(USART_HOST,

(const uint8_t *)data,

length);

#endif

} while (status != STATUS_OK);

return length;

}

 

uint8_t sio2host_rx(uint8_t *data, uint8_t max_length)

{

uint8_t data_received = 0;

if(serial_rx_buf_tail >= serial_rx_buf_head)

{

serial_rx_count = serial_rx_buf_tail serial_rx_buf_head;

}

else

{

serial_rx_count = serial_rx_buf_tail + (SERIAL_RX_BUF_SIZE_HOST serial_rx_buf_head);

}

if (0 == serial_rx_count) {

return 0;

}

 

if (SERIAL_RX_BUF_SIZE_HOST <= serial_rx_count) {

/*

* Bytes between head and tail are overwritten by new data.

* The oldest data in buffer is the one to which the tail is

* pointing. So reading operation should start from the tail.

*/

serial_rx_buf_head = serial_rx_buf_tail;

 

/*

* This is a buffer overflow case. But still only the number of

* bytes equivalent to

* full buffer size are useful.

*/

serial_rx_count = SERIAL_RX_BUF_SIZE_HOST;

 

/* Bytes received is more than or equal to buffer. */

if (SERIAL_RX_BUF_SIZE_HOST <= max_length) {

/*

* Requested receive length (max_length) is more than

* the

* max size of receive buffer, but at max the full

* buffer can be read.

*/

max_length = SERIAL_RX_BUF_SIZE_HOST;

}

} else {

/* Bytes received is less than receive buffer maximum length. */

if (max_length > serial_rx_count) {

/*

* Requested receive length (max_length) is more than

* the data

* present in receive buffer. Hence only the number of

* bytes

* present in receive buffer are read.

*/

max_length = serial_rx_count;

}

}

 

data_received = max_length;

while (max_length > 0) {

/* Start to copy from head. */

*data = serial_rx_buf[serial_rx_buf_head];

data++;

max_length–;

if ((SERIAL_RX_BUF_SIZE_HOST 1) == serial_rx_buf_head) {

serial_rx_buf_head = 0;

}

else

{

serial_rx_buf_head++;

}

}

return data_received;

}

 

Reference:

Atmel (2012) Atmel Software Framework [Online] Available: http://asf.atmel.no/docs/latest/search.html?device=saml21