#include "em_common.h" #include "sl_bluetooth.h" #include "gatt_db.h" #include "app.h" #include "sl_emlib_gpio_init_LED_config.h" #include "sl_emlib_gpio_init_BTN_config.h" #include "sl_iostream_usart_vcom_config.h" #include "gpiointerrupt.h" #include "em_gpio.h" #include "stdio.h" #include "em_usart.h" #include "sl_sleeptimer.h" #define DISCONNECTED 0 #define SCANNING 1 #define FIND_SERVICE 2 #define FIND_CHAR 3 #define ENABLE_NOTIF 4 #define DATA_MODE 5 #define DISCONNECTING 6 #define TIMER_SIGNAL 100 // SPP service UUID: 4880c12c-fdcb-4077-8920-a450d7f9b907 const uint8_t serviceUUID[16] = {0x07,0xb9,0xf9,0xd7,0x50,0xa4,0x20,0x89,0x77,0x40,0xcb,0xfd,0x2c,0xc1,0x80,0x48}; // SPP data UUID: fec26ec4-6d71-4442-9f81-55bc21d658d6 const uint8_t charUUID[16] = {0xd6,0x58,0xd6,0x21,0xbc,0x55,0x81,0x9f,0x42,0x44,0x71,0x6d,0xc4,0x6e,0xc2,0xfe}; /* maximum number of iterations when polling UART RX data before sending data over BLE connection * set value to 0 to disable optimization -> minimum latency but may decrease throughput */ #define UART_POLL_TIMEOUT 5000 static void reset_variables(void); static void send_spp_data(void); static int process_scan_response(struct sl_bt_evt_scanner_scan_report_s *pResp); static void Button_Event(const uint8_t pin); static void Timer_Callback(sl_sleeptimer_timer_handle_t *handle, void *data); //static void button_poll(void); static uint8_t conn_handle = 0xFF; static uint8_t advertising_set_handle = 0xff; static uint8_t main_state; static uint32_t service_handle; static uint16_t char_handle; static sl_sleeptimer_timer_handle_t timerHandle; /* Default maximum packet size is 20 bytes. This is adjusted after connection is opened based * on the connection parameters */ static uint8_t max_packet_size = 20; static uint8_t min_packet_size = 20; // Target minimum bytes for one packet static sl_status_t result; SL_WEAK void app_init(void) { GPIO_ExtIntConfig(SL_EMLIB_GPIO_INIT_BTN_PORT, SL_EMLIB_GPIO_INIT_BTN_PIN, SL_EMLIB_GPIO_INIT_BTN_PIN, false, true, true); GPIOINT_CallbackRegister(SL_EMLIB_GPIO_INIT_BTN_PIN, Button_Event); // register callback function } SL_WEAK void app_process_action(void) { sl_bt_msg_t evt; sl_bt_run(); uint32_t event_len = sl_bt_event_pending_len(); // For preventing from data loss, the event will be kept in the stack's queue // if application cannot process it at the moment. if ((event_len == 0) || (!sl_bt_can_process_event(event_len))) { if (main_state == DATA_MODE) { send_spp_data(); } return; } // Pop (non-blocking) a Bluetooth stack event from event queue. sl_status_t status = sl_bt_pop_event(&evt); if (status != SL_STATUS_OK) { return; } sl_bt_process_event(&evt); } void sl_bt_on_event(sl_bt_msg_t *evt) { switch (SL_BT_MSG_ID(evt->header)) { case sl_bt_evt_system_boot_id: reset_variables(); sl_bt_advertiser_create_set(&advertising_set_handle); sl_bt_gatt_set_max_mtu(247, NULL); sl_bt_scanner_start (sl_bt_gap_1m_phy, sl_bt_scanner_discover_generic); main_state = SCANNING; break; case sl_bt_evt_scanner_scan_report_id: // some BT device is discovered if (process_scan_response(&(evt->data.evt_scanner_scan_report)) > 0) { // required service found sl_bt_scanner_stop (); // match found -> stop discovery and try to connect result = sl_bt_connection_open(evt->data.evt_scanner_scan_report.address, evt->data.evt_scanner_scan_report.address_type, sl_bt_gap_1m_phy, &conn_handle); if (result != SL_STATUS_OK) { printf("Connect failed with code 0x%4.4x\n", (int)result); } } break; case sl_bt_evt_connection_opened_id: printf("Connected.\n"); GPIO_PinOutSet(SL_EMLIB_GPIO_INIT_LED_PORT, SL_EMLIB_GPIO_INIT_LED_PIN); // turn LED on sl_bt_gatt_discover_primary_services_by_uuid(conn_handle, 16, serviceUUID); // start service discovery main_state = FIND_SERVICE; break; case sl_bt_evt_connection_closed_id: printf("DISCONNECTED!\n"); GPIO_PinOutClear(SL_EMLIB_GPIO_INIT_LED_PORT, SL_EMLIB_GPIO_INIT_LED_PIN); // turn LED off main_state = DISCONNECTED; reset_variables(); sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1); // Enable sleeping after disconnect // Create one-shot soft timer that will restart discovery after 1 second delay sl_sleeptimer_start_timer_ms(&timerHandle, 1000, Timer_Callback, NULL, 0, 0); break; case sl_bt_evt_connection_parameters_id: printf("Conn.parameters: interval %u units, txsize %u\r\n", evt->data.evt_connection_parameters.interval, evt->data.evt_connection_parameters.txsize); break; case sl_bt_evt_gatt_mtu_exchanged_id: /* Calculate maximum data per one notification / write-without-response, this depends on the MTU. * up to ATT_MTU-3 bytes can be sent at once */ max_packet_size = evt->data.evt_gatt_mtu_exchanged.mtu - 3; min_packet_size = max_packet_size; // Try to send maximum length packets whenever possible printf("MTU exchanged: %d\n", evt->data.evt_gatt_mtu_exchanged.mtu); break; case sl_bt_evt_gatt_service_id: // some service is discovered if (evt->data.evt_gatt_service.uuid.len == 16) { if (memcmp(serviceUUID, evt->data.evt_gatt_service.uuid.data, 16) == 0) { printf("Service discovered.\n"); service_handle = evt->data.evt_gatt_service.service; } } break; case sl_bt_evt_gatt_characteristic_id: // some characteristic is discovered if (evt->data.evt_gatt_characteristic.uuid.len == 16) { if (memcmp(charUUID, evt->data.evt_gatt_characteristic.uuid.data, 16) == 0) { printf("Characteristic discovered.\n"); char_handle = evt->data.evt_gatt_characteristic.characteristic; } } break; case sl_bt_evt_gatt_procedure_completed_id: // current GATT command is completed switch (main_state) { case FIND_SERVICE: if (service_handle > 0) { // service found, next search for characteristics sl_bt_gatt_discover_characteristics(conn_handle, service_handle); main_state = FIND_CHAR; } else { // no service found -> disconnect printf("SPP service not found.\n"); sl_bt_connection_close(conn_handle); } break; case FIND_CHAR: if (char_handle > 0) { // characteristic found, turn on notifications sl_bt_gatt_set_characteristic_notification(conn_handle, char_handle, gatt_notification); main_state = ENABLE_NOTIF; } else { // no characteristic found -> disconnect printf("SPP characteristic not found.\n"); sl_bt_connection_close(conn_handle); } break; case ENABLE_NOTIF: main_state = DATA_MODE; printf("SPP Mode ON\r\n"); sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1); // Disable sleeping when SPP mode active break; default: break; } break; case sl_bt_evt_gatt_characteristic_value_id: // data is received if (evt->data.evt_gatt_characteristic_value.characteristic == char_handle) { for (int i=0; idata.evt_gatt_characteristic_value.value.len; i++) { USART_Tx(SL_IOSTREAM_USART_VCOM_PERIPHERAL, evt->data.evt_gatt_characteristic_value.value.data[i]); } } break; case sl_bt_evt_system_external_signal_id: // button event if (evt->data.evt_system_external_signal.extsignals == SL_EMLIB_GPIO_INIT_BTN_PIN) { if (conn_handle != 0xFF) { sl_bt_connection_close(conn_handle); main_state = DISCONNECTING; } } if (evt->data.evt_system_external_signal.extsignals == TIMER_SIGNAL) { // restart discovery using the default 1M PHY sl_bt_scanner_start (sl_bt_gap_1m_phy, sl_bt_scanner_discover_generic); main_state = SCANNING; } break; default: break; } } static void reset_variables() { conn_handle = 0xFF; main_state = DISCONNECTED; service_handle = 0; char_handle = 0; max_packet_size = 20; } static int process_scan_response(struct sl_bt_evt_scanner_scan_report_s *pResp) { // Decoding advertising packets is done here. The list of AD types can be found // at: https://www.bluetooth.com/specifications/assigned-numbers/Generic-Access-Profile int i = 0; int ad_match_found = 0; int ad_len; int ad_type; char name[32]; while (i < (pResp->data.len - 1)) { ad_len = pResp->data.data[i]; ad_type = pResp->data.data[i + 1]; if (ad_type == 0x08 || ad_type == 0x09) { // Type 0x08 = Shortened Local Name // Type 0x09 = Complete Local Name memcpy(name, &(pResp->data.data[i + 2]), ad_len - 1); name[ad_len - 1] = 0; printf("%s\n", name); } // 4880c12c-fdcb-4077-8920-a450d7f9b907 if (ad_type == 0x06 || ad_type == 0x07) { // Type 0x06 = Incomplete List of 128-bit Service Class UUIDs // Type 0x07 = Complete List of 128-bit Service Class UUIDs if (memcmp(serviceUUID, &(pResp->data.data[i + 2]), 16) == 0) { printf("Found SPP device.\n"); ad_match_found = 1; } } // Jump to next AD record i = i + ad_len + 1; } return (ad_match_found); } static void send_spp_data(void) { uint8_t len = 0; uint8_t data[256]; uint16_t result; int c; int timeout = 0; // Read up to _max_packet_size characters from local buffer while (len < max_packet_size) { c = getchar(); if (c >= 0) { data[len++] = (uint8_t) c; } else if (len == 0) { /* If the first ReadChar() fails then return immediately */ return; } else { /* Speed optimization: if there are some bytes to be sent but the length is still * below the preferred minimum packet size, then wait for additional bytes * until timeout. Target is to put as many bytes as possible into each air packet */ // Conditions for exiting the while loop and proceed to send data: if (timeout++ > UART_POLL_TIMEOUT) { break; } else if (len >= min_packet_size) { break; } } } if (len > 0) { // Stack may return "out-of-memory" error if the local buffer is full -> in that case, just keep trying until the command succeeds do { result = sl_bt_gatt_write_characteristic_value_without_response(conn_handle, char_handle, len, data, NULL); } while (result != SL_STATUS_OK); } } static void Button_Event(const uint8_t pin) { if (pin == SL_EMLIB_GPIO_INIT_BTN_PIN) { sl_bt_external_signal(SL_EMLIB_GPIO_INIT_BTN_PIN); } } static void Timer_Callback(sl_sleeptimer_timer_handle_t *handle, void *data) { (void) handle; (void) data; sl_bt_external_signal(TIMER_SIGNAL); }