diff --git a/example/Makefile.inc b/example/Makefile.inc index 532985c23..23428e099 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -112,6 +112,7 @@ EXAMPLES = \ spp_and_le_streamer \ spp_counter \ spp_streamer \ + spp_streamer_client \ gap_le_advertisements \ hfp_ag_demo \ hfp_hf_demo \ @@ -205,10 +206,10 @@ spp_and_le_counter: spp_and_le_counter.h ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ spp_and_le_streamer: spp_and_le_streamer.h ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} spp_and_le_streamer.c ${CC} $(filter-out spp_and_le_streamer.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ -spp_counter_ssp: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} spp_counter_ssp.c +spp_streamer: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} spp_streamer.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -spp_streamer: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} spp_streamer.c +spp_streamer_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} spp_streamer_client.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ gap_dedicated_bonding: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} gap_dedicated_bonding.c diff --git a/example/spp_and_le_streamer.gatt b/example/spp_and_le_streamer.gatt new file mode 100644 index 000000000..b801b417a --- /dev/null +++ b/example/spp_and_le_streamer.gatt @@ -0,0 +1,14 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "LE Streamer" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_SERVICE_CHANGED, READ, + +// Test Service +PRIMARY_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB +// Test Characteristic, only notify +CHARACTERISTIC, 0000FF11-0000-1000-8000-00805F9B34FB, NOTIFY, +// Test Characterisitic, only write_without_response +CHARACTERISTIC, 0000FF12-0000-1000-8000-00805F9B34FB, WRITE_WITHOUT_RESPONSE | DYNAMIC, + + diff --git a/example/spp_streamer_client.c b/example/spp_streamer_client.c new file mode 100644 index 000000000..dca111633 --- /dev/null +++ b/example/spp_streamer_client.c @@ -0,0 +1,265 @@ +/* + * 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_streamer_client): Client for SPP Streamer + * + * @text The SPP Streamer Clients connects to SPP Streamer and tracks the + * amount of received data + */ +// ***************************************************************************** + +#include +#include +#include +#include +#include + +#include "btstack.h" + +#define RFCOMM_SERVER_CHANNEL 1 + +#define TEST_COD 0x1234 + +typedef enum { + // SPP + W4_PEER_COD, + W4_SCAN_COMPLETE, + W4_SDP_RESULT, + W4_SDP_COMPLETE, + W4_RFCOMM_CHANNEL, + SENDING, + DONE +} state_t; + +static btstack_packet_callback_registration_t hci_event_callback_registration; + +static bd_addr_t peer_addr; +static state_t state;; + +// SPP +static uint16_t rfcomm_mtu; +static uint16_t rfcomm_cid = 0; +// static uint32_t data_to_send = DATA_VOLUME; + + +/** + * 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 */ + +/* + * @section Packet Handler + * + * @text The packet handler of the combined example is just the combination of the individual packet handlers. + */ + +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + + bd_addr_t event_addr; + uint8_t rfcomm_channel_nr; + uint32_t class_of_device; + + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + + case HCI_EVENT_PIN_CODE_REQUEST: + // inform about pin code request + printf("Pin code request - using '0000'\n"); + hci_event_pin_code_request_get_bd_addr(packet, event_addr); + gap_pin_code_response(event_addr, "0000"); + break; + + case HCI_EVENT_USER_CONFIRMATION_REQUEST: + // inform about user confirmation request + printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8)); + printf("SSP User Confirmation Auto accept\n"); + 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); + test_reset(); + // disable page and inquiry scan + gap_connectable_control(0); + } + break; + + case RFCOMM_EVENT_CHANNEL_CLOSED: + printf("RFCOMM channel closed\n"); + rfcomm_cid = 0; // re-enable page scan + gap_connectable_control(1); + break; + + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + start_scan(); + break; + + case GAP_EVENT_INQUIRY_RESULT: + class_of_device = gap_event_inquiry_result_get_class_of_device(packet); + gap_event_inquiry_result_get_bd_addr(packet, event_addr); + if (class_of_device == TEST_COD){ + memcpy(peer_addr, event_addr, 6); + printf("Peer found: %s\n", bd_addr_to_str(peer_addr)); + stop_scan(); + + printf("Start to connect\n"); + state = W4_RFCOMM_CHANNEL; + rfcomm_create_channel(packet_handler, peer_addr, RFCOMM_SERVER_CHANNEL, NULL); + } else { + printf("Device found: %s with COD: 0x%06x\n", bd_addr_to_str(event_addr), (int) class_of_device); + } + break; + + case HCI_EVENT_INQUIRY_COMPLETE: + printf("Inquiry complete\n"); + printf("Peer not found, starting scan again\n"); + start_scan(); + break; + + default: + break; + } + break; + + case RFCOMM_DATA_PACKET: + test_track_transferred(size); +#if 0 + printf("RCV: '"); + for (i=0;i