mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-02-03 20:54:18 +00:00
example: add spp_streamer_client
This commit is contained in:
parent
402122d413
commit
cee62800b7
@ -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
|
||||
|
14
example/spp_and_le_streamer.gatt
Normal file
14
example/spp_and_le_streamer.gatt
Normal file
@ -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,
|
||||
|
||||
|
265
example/spp_streamer_client.c
Normal file
265
example/spp_streamer_client.c
Normal file
@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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<size;i++){
|
||||
putchar(packet[i]);
|
||||
}
|
||||
printf("'\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @section Main Application Setup
|
||||
*
|
||||
* @text As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
|
||||
*/
|
||||
|
||||
|
||||
/* LISTING_START(MainConfiguration): Init L2CAP RFCOMM SDO SM ATT Server and start heartbeat timer */
|
||||
int btstack_main(int argc, const char * argv[]);
|
||||
int btstack_main(int argc, const char * argv[]){
|
||||
UNUSED(argc);
|
||||
(void)argv;
|
||||
|
||||
// register for HCI events
|
||||
hci_event_callback_registration.callback = &packet_handler;
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
|
||||
l2cap_init();
|
||||
|
||||
rfcomm_init();
|
||||
rfcomm_register_service(packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff);
|
||||
|
||||
// init SDP
|
||||
gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
|
||||
|
||||
// turn on!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* LISTING_END */
|
||||
/* EXAMPLE_END */
|
Loading…
x
Reference in New Issue
Block a user