diff --git a/port/esp32/main/btstack_config.h b/port/esp32/main/btstack_config.h index fde32a3f4..e60019a6c 100644 --- a/port/esp32/main/btstack_config.h +++ b/port/esp32/main/btstack_config.h @@ -15,8 +15,8 @@ #define ENABLE_LE_PERIPHERAL #define ENABLE_LE_CENTRAL #define ENABLE_LOG_ERROR -#define ENABLE_LOG_INFO -#define ENABLE_LOG_DEBUG +// #define ENABLE_LOG_INFO +// #define ENABLE_LOG_DEBUG // #define ENABLE_EHCILL // BTstack configuration. buffers, sizes, ... diff --git a/port/esp32/main/le_counter.c b/port/esp32/main/le_counter.c deleted file mode 100644 index 68dda2a09..000000000 --- a/port/esp32/main/le_counter.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2014 BlueKitchen GmbH - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * 4. Any redistribution, use, or modification is done solely for - * personal benefit and not for any commercial purpose or for - * monetary gain. - * - * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS - * RINGWALD 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. - * - * Please inquire about commercial licensing options at - * contact@bluekitchen-gmbh.com - * - */ - -// ***************************************************************************** -/* EXAMPLE_START(le_counter): LE Peripheral - Heartbeat Counter over GATT - * - * @text All newer operating systems provide GATT Client functionality. - * The LE Counter examples demonstrates how to specify a minimal GATT Database - * with a custom GATT Service and a custom Characteristic that sends periodic - * notifications. - */ - // ***************************************************************************** - -#include -#include -#include -#include - -#include "le_counter.h" -#include "btstack.h" -#include "ble/gatt-service/battery_service_server.h" - -#define HEARTBEAT_PERIOD_MS 1000 - -/* @section Main Application Setup - * - * @text Listing MainConfiguration shows main application code. - * It initializes L2CAP, the Security Manager and configures the ATT Server with the pre-compiled - * ATT Database generated from $le_counter.gatt$. - * Additionally, it enables the Battery Service Server with the current battery level. - * Finally, it configures the advertisements - * and the heartbeat handler and boots the Bluetooth stack. - * In this example, the Advertisement contains the Flags attribute and the device name. - * The flag 0x06 indicates: LE General Discoverable Mode and BR/EDR not supported. - */ - -/* LISTING_START(MainConfiguration): Init L2CAP SM ATT Server and start heartbeat timer */ -static int le_notification_enabled; -static btstack_timer_source_t heartbeat; -static btstack_packet_callback_registration_t hci_event_callback_registration; -static hci_con_handle_t con_handle; -static uint8_t battery = 100; - -static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); -static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); -static void heartbeat_handler(struct btstack_timer_source *ts); -static void beat(void); - -const uint8_t adv_data[] = { - // Flags general discoverable, BR/EDR not supported - 0x02, 0x01, 0x06, - // Name - 0x0b, 0x09, 'L', 'E', ' ', 'C', 'o', 'u', 'n', 't', 'e', 'r', -}; -const uint8_t adv_data_len = sizeof(adv_data); - -static void le_counter_setup(void){ - -#if 0 - // register for HCI events - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); - - l2cap_init(); - - // setup le device db - le_device_db_init(); - - // setup SM: Display only - sm_init(); - - // setup ATT server - att_server_init(profile_data, att_read_callback, att_write_callback); - att_server_register_packet_handler(packet_handler); - - // setup battery service - battery_service_server_init(battery); - - // setup advertisements - uint16_t adv_int_min = 0x0030; - uint16_t adv_int_max = 0x0030; - uint8_t adv_type = 0; - bd_addr_t null_addr; - memset(null_addr, 0, 6); - gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); - gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); - gap_advertisements_enable(1); - - // set one-shot timer - heartbeat.process = &heartbeat_handler; - btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - btstack_run_loop_add_timer(&heartbeat); - - // beat once - beat(); -#endif - -} -/* LISTING_END */ - -/* - * @section Heartbeat Handler - * - * @text The heartbeat handler updates the value of the single Characteristic provided in this example, - * and request a ATT_EVENT_CAN_SEND_NOW to send a notification if enabled see Listing heartbeat. - */ - - /* LISTING_START(heartbeat): Hearbeat Handler */ -static int counter = 0; -static char counter_string[30]; -static int counter_string_len; - -static void beat(void){ - counter++; - counter_string_len = sprintf(counter_string, "BTstack counter %04u", counter); - puts(counter_string); -} - -static void heartbeat_handler(struct btstack_timer_source *ts){ - if (le_notification_enabled) { - beat(); - att_server_request_can_send_now_event(con_handle); - } - - // simulate battery drain - battery--; - if (battery < 50) { - battery = 100; - } - battery_service_server_set_battery_value(battery); - - btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - btstack_run_loop_add_timer(ts); -} -/* LISTING_END */ - -/* - * @section Packet Handler - * - * @text The packet handler is used to: - * - stop the counter after a disconnect - * - send a notification when the requested ATT_EVENT_CAN_SEND_NOW is received - */ - -/* LISTING_START(packetHandler): Packet Handler */ -static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(channel); - UNUSED(size); - - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (hci_event_packet_get_type(packet)) { - case HCI_EVENT_DISCONNECTION_COMPLETE: - le_notification_enabled = 0; - break; - case ATT_EVENT_CAN_SEND_NOW: - att_server_notify(con_handle, ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE, (uint8_t*) counter_string, counter_string_len); - break; - } - break; - } -} -/* LISTING_END */ - -/* - * @section ATT Read - * - * @text The ATT Server handles all reads to constant data. For dynamic data like the custom characteristic, the registered - * att_read_callback is called. To handle long characteristics and long reads, the att_read_callback is first called - * with buffer == NULL, to request the total value length. Then it will be called again requesting a chunk of the value. - * See Listing attRead. - */ - -/* LISTING_START(attRead): ATT Read */ - -// ATT Client Read Callback for Dynamic Data -// - if buffer == NULL, don't copy data, just return size of value -// - if buffer != NULL, copy data and return number bytes copied -// @param offset defines start of attribute value -static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ - UNUSED(connection_handle); - - if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE){ - if (buffer){ - memcpy(buffer, &counter_string[offset], buffer_size); - return buffer_size; - } else { - return counter_string_len; - } - } - return 0; -} -/* LISTING_END */ - - -/* - * @section ATT Write - * - * @text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification - * and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used - * in the heartbeat handler to decide if a new value should be sent. See Listing attWrite. - */ - -/* LISTING_START(attWrite): ATT Write */ -static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ - UNUSED(transaction_mode); - UNUSED(offset); - UNUSED(buffer_size); - - if (att_handle != ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE) return 0; - le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; - con_handle = connection_handle; - return 0; -} -/* LISTING_END */ - -int btstack_main(void); -int btstack_main(void) -{ - le_counter_setup(); - - // turn on! - hci_power_control(HCI_POWER_ON); - - return 0; -} -/* EXAMPLE_END */ diff --git a/port/esp32/main/main.c b/port/esp32/main/main.c index 296ca5655..fee00034b 100644 --- a/port/esp32/main/main.c +++ b/port/esp32/main/main.c @@ -220,7 +220,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack static void btstack_setup(void){ - hci_dump_open(NULL, HCI_DUMP_STDOUT); + // hci_dump_open(NULL, HCI_DUMP_STDOUT); /// GET STARTED with BTstack /// btstack_memory_init(); diff --git a/port/esp32/main/spp_and_le_streamer.c b/port/esp32/main/spp_and_le_streamer.c new file mode 100644 index 000000000..795c415f0 --- /dev/null +++ b/port/esp32/main/spp_and_le_streamer.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +// ***************************************************************************** +/* EXAMPLE_START(spp_and_le_counter): Dual mode example + * + * @text The SPP and LE Counter example combines the Bluetooth Classic SPP Counter + * and the Bluetooth LE Counter into a single application. + * + * In this Section, we only point out the differences to the individual examples + * and how how the stack is configured. + */ +// ***************************************************************************** + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "spp_and_le_streamer.h" + +int btstack_main(int argc, const char * argv[]); + +#define RFCOMM_SERVER_CHANNEL 1 +#define HEARTBEAT_PERIOD_MS 1000 + +#define TEST_COD 0x1234 +#define NUM_ROWS 25 +#define NUM_COLS 40 +#define DATA_VOLUME (10 * 1000 * 1000) + +// prototypes +static enum test_mode { + TEST_NONE, + TEST_SPP, + TEST_LE, +} test_mode = TEST_NONE; + +typedef enum { + // SPP + W4_PEER_COD, + W4_SCAN_COMPLETE, + W4_SDP_RESULT, + W4_SDP_COMPLETE, + W4_RFCOMM_CHANNEL, + SENDING, + // LE + TC_W4_SCAN_RESULT, + TC_W4_CONNECT, + TC_W4_SERVICE_RESULT, + TC_W4_CHARACTERISTIC_RESULT, + TC_W4_TEST_DATA, + // Both + DONE +} state_t; + +/* + * @section Advertisements + * + * @text The Flags attribute in the Advertisement Data indicates if a device is in dual-mode or not. + * Flag 0x06 indicates LE General Discoverable, BR/EDR not supported although we're actually using BR/EDR. + * In the past, there have been problems with Anrdoid devices when the flag was not set. + * Setting it should prevent the remote implementation to try to use GATT over LE/EDR, which is not + * implemented by BTstack. So, setting the flag seems like the safer choice (while it's technically incorrect). + */ +/* LISTING_START(advertisements): Advertisement data: Flag 0x06 indicates LE-only device */ +const uint8_t adv_data[] = { + // Flags general discoverable, BR/EDR not supported + 0x02, 0x01, 0x06, + // Name + 0x0c, 0x09, 'L', 'E', ' ', 'S', 't', 'r', 'e', 'a', 'm', 'e', 'r', +}; + +static const uint8_t eir_data[] = { +// iAP2 UUID +0x11, 0x07, 0xff, 0xca, 0xca, 0xde, 0xaf, 0xde, 0xca, 0xde, 0xde, 0xfa, 0xca, 0xde, 0x00, 0x00, 0x00, 0x00, +// Local Name +0x13, 0x09, 'B','T','s','t','a','c','k',' ','M','F','i',' ','D','e','v','i','c','e', +// TX Power Level +0x02, 0x0a, 0x00 +}; + +static btstack_packet_callback_registration_t hci_event_callback_registration; + +uint8_t adv_data_len = sizeof(adv_data); + +static bd_addr_t peer_addr; + +static uint8_t test_data[NUM_ROWS * NUM_COLS]; +static state_t state = W4_SDP_RESULT; + +// SPP +static uint8_t spp_service_buffer[150]; + +static uint16_t spp_test_data_len = sizeof(test_data); +static uint16_t rfcomm_mtu; +static uint16_t rfcomm_cid = 0; +// static uint32_t data_to_send = DATA_VOLUME; + +// LE +static uint16_t att_mtu; +static int counter = 'A'; +static int le_notification_enabled; +static uint16_t le_test_data_len; +static hci_con_handle_t le_connection_handle; + +// addr and type of device with correct name +static bd_addr_t le_streamer_addr; +static bd_addr_type_t le_streamer_addr_type; +static uint8_t le_streamer_service_uuid[16] = { 0x00, 0x00, 0xFF, 0x10, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; +static uint8_t le_streamer_characteristic_uuid[16] = { 0x00, 0x00, 0xFF, 0x11, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; +static gatt_client_service_t le_streamer_service; +static gatt_client_characteristic_t le_streamer_characteristic; +static gatt_client_notification_t notification_registration; + + +/** + * Find remote peer by COD + */ +#define INQUIRY_INTERVAL 5 +static void start_scan(void){ + printf("Starting inquiry scan..\n"); + hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0); + state = W4_PEER_COD; +} +static void stop_scan(void){ + printf("Stopping inquiry scan..\n"); + hci_send_cmd(&hci_inquiry_cancel); + state = W4_SCAN_COMPLETE; +} +/* + * @section Track throughput + * @text We calculate the throughput by setting a start time and measuring the amount of + * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s + * and reset the counter and start time. + */ + +/* LISTING_START(tracking): Tracking throughput */ +#define REPORT_INTERVAL_MS 3000 +static uint32_t test_data_transferred; +static uint32_t test_data_start; + +static void test_reset(void){ + test_data_start = btstack_run_loop_get_time_ms(); + test_data_transferred = 0; +} + +static void test_track_transferred(int bytes_sent){ + test_data_transferred += bytes_sent; + // evaluate + uint32_t now = btstack_run_loop_get_time_ms(); + uint32_t time_passed = now - test_data_start; + if (time_passed < REPORT_INTERVAL_MS) return; + // print speed + int bytes_per_second = test_data_transferred * 1000 / time_passed; + printf("%u bytes -> %u.%03u kB/s\n", (int) test_data_transferred, (int) bytes_per_second / 1000, bytes_per_second % 1000); + + // restart + test_data_start = now; + test_data_transferred = 0; +} +/* LISTING_END(tracking): Tracking throughput */ + + +static void spp_create_test_data(void){ + int x,y; + for (y=0;y 'Z') counter = 'A'; + memset(test_data, counter, sizeof(test_data)); + + // send + att_server_notify(le_connection_handle, ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE, (uint8_t*) test_data, le_test_data_len); + + // track + test_track_transferred(le_test_data_len); + + // request next send event + att_server_request_can_send_now_event(le_connection_handle); +} + +// returns 1 if name is found in advertisement +static int advertisement_report_contains_name(const char * name, uint8_t * advertisement_report){ + // get advertisement from report event + const uint8_t * adv_data = gap_event_advertising_report_get_data(advertisement_report); + uint16_t adv_len = gap_event_advertising_report_get_data_length(advertisement_report); + int name_len = strlen(name); + + // iterate over advertisement data + ad_context_t context; + for (ad_iterator_init(&context, adv_len, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ + uint8_t data_type = ad_iterator_get_data_type(&context); + uint8_t data_size = ad_iterator_get_data_len(&context); + const uint8_t * data = ad_iterator_get_data(&context); + int i; + switch (data_type){ + case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME: + case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME: + // compare common prefix + for (i=0; i sizeof(test_data)){ + le_test_data_len = sizeof(test_data); + } + break; + + case ATT_EVENT_CAN_SEND_NOW: + le_streamer(); + break; + + case GAP_EVENT_ADVERTISING_REPORT: + if (state != TC_W4_SCAN_RESULT) return; + // check name in advertisement + if (!advertisement_report_contains_name("LE Streamer", packet)) return; + // store address and type + gap_event_advertising_report_get_address(packet, le_streamer_addr); + le_streamer_addr_type = gap_event_advertising_report_get_address_type(packet); + // stop scanning, and connect to the device + state = TC_W4_CONNECT; + gap_stop_scan(); + printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(le_streamer_addr)); + gap_connect(le_streamer_addr,le_streamer_addr_type); + break; + + case RFCOMM_EVENT_INCOMING_CONNECTION: + // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) + rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr); + rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet); + rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet); + printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr)); + rfcomm_accept_connection(rfcomm_cid); + break; + + case RFCOMM_EVENT_CHANNEL_OPENED: + // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) + if (rfcomm_event_channel_opened_get_status(packet)) { + printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet)); + } else { + rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet); + rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet); + printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_cid, rfcomm_mtu); + + // initiator also sends data + test_reset(); + if (test_mode == TEST_SPP){ + rfcomm_request_can_send_now_event(rfcomm_cid); + } + } + break; + + case RFCOMM_EVENT_CAN_SEND_NOW: + if (test_mode == TEST_SPP){ + spp_send_packet(); + } + break; + + case RFCOMM_EVENT_CHANNEL_CLOSED: + printf("RFCOMM channel closed\n"); + rfcomm_cid = 0; + break; + + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + break; + + case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE: + num_responses = hci_event_inquiry_result_get_num_responses(packet); + for (i=0; i