您尚未登录。

楼主 # 2023-12-23 21:23:40

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

ble scan 扫描不到所有设备

http://tvaira.free.fr/flower-power/ble-scan.c

#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <sys/types.h>
#include <sys/socket.h>

// $ gcc ble-scan.c -lbluetooth -o ble-scan

#define EIR_FLAGS                   0x01  /* flags */
#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT              0x08  /* shortened local name */
#define EIR_NAME_COMPLETE           0x09  /* complete local name */
#define EIR_TX_POWER                0x0A  /* transmit power level */
#define EIR_DEVICE_ID               0x10  /* device ID */
#define EIR_MANUFACTURE_SPECIFIC    0xFF

void process_data(uint8_t *data, size_t data_len, le_advertising_info *info);
int connecter(int sock, char dest[18]);
void deconnecter(int sock, uint16_t handle);

int main (int argc, char **argv)
{
    int sock, retval;
    int i, len;
    unsigned char buf[HCI_MAX_FRAME_SIZE];
    char btAddress[18];
    uint16_t handle;
    struct sockaddr_hci addr;
    struct hci_filter filter;
    int encore = 1;
    
    sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    if (-1 == sock)
    {
        perror("socket"); return 1;
    }
    
    hci_filter_clear(&filter);
    hci_filter_all_ptypes(&filter);
    hci_filter_all_events(&filter);
    
    retval = setsockopt(sock, SOL_HCI, HCI_FILTER, &filter, sizeof(filter)); 
    if (-1 == retval)
    {
        perror("setsockopt"); return 1;
    }
    
    memset(&addr, 0, sizeof(addr));
    addr.hci_family = AF_BLUETOOTH;
    addr.hci_dev = 0;
    retval = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (-1 == retval)
    {
        perror("bind"); return 1;
    }
    
	uint8_t scan_type = 0x00; /* Passive */
    uint16_t interval = htobs(0x0010);
	uint16_t window = htobs(0x0010);
    uint8_t own_type = 0x00;
	uint8_t filter_policy = 0x00; /* 1 -> Whitelist */	

    retval = hci_le_set_scan_parameters(sock, scan_type, interval, window, own_type, filter_policy, 1000);
    //retval = hci_le_set_scan_parameters(sock, 0, 0x10, 0x10, 0, 0, 1000);
    if (retval < 0)
    {
        perror("hci_le_set_scan_parameters"); //return 1;
    }
    
    retval = hci_le_set_scan_enable(sock, 1 /* 1 - turn on, 0 - turn off */, 0 /* 0-filtering disabled, 1-filter out duplicates */, 1000  /* timeout */);
    if (retval < 0)
    {
        perror("hci_le_set_scan_enable"); //return 1;
    }    
    
    do 
    {
        memset (buf, 0, sizeof(buf));
        retval = recv (sock, buf, sizeof(buf), 0);
        if (-1 == retval)
        {
            perror("recv"); return 1;
        }
        printf ("# ");
        for(i=0;i<retval;i++)
            printf ("0x%02X ", buf[i]);
            //printf ("%c ", buf[i]);
        printf ("(%d)\n\n", retval);
        /*printf ("# 0x%02X 0x%02X 0x%02X 0x%02X (%d)\n",
                    buf[0], buf[1],
                    buf[2], buf[3], retval);*/
        switch (buf[1]) 
        {
            case EVT_CMD_STATUS: // 0x0F
                if (buf[3]) 
                {
                    printf ("Erreur !\n");
                    encore = 0;
                } 
                else 
                {
                    printf ("Commande en cours\n");
                }
                break;
            case EVT_INQUIRY_RESULT: // 0x02
            printf ("Périphérique trouvé:\n");
                printf ("  * Adresse : %02x:%02x:%02x:%02x:%02x:%02x\n",
                    buf[9], buf[8],
                    buf[7], buf[6],
                    buf[5], buf[4]);
                printf ("  * Classe  : 0x%02x%02x%02x\n\n",
                    buf[15], buf[14], buf[13]);
            break;
         case EVT_EXTENDED_INQUIRY_RESULT: // 0x2F
                printf ("Périphérique trouvé:\n");
                printf ("  * Adresse : %02x:%02x:%02x:%02x:%02x:%02x\n",
                    buf[9], buf[8],
                    buf[7], buf[6],
                    buf[5], buf[4]);
                printf ("  * Classe  : 0x%02x%02x%02x\n",
                    buf[14], buf[13], buf[12]);
                printf ("  * RSSI    : %d\n\n", // Received Signal Strength Indication
                    buf[17]);
                break;
            case EVT_INQUIRY_COMPLETE: // 0x01
                encore = 0;
                break;
            case EVT_LE_META_EVENT: // 0x3E
                len = retval;
                evt_le_meta_event *meta = (void *)(buf + (1 + HCI_EVENT_HDR_SIZE));

                len -= (1 + HCI_EVENT_HDR_SIZE);
                
                if (meta->subevent == EVT_LE_ADVERTISING_REPORT)
                {
                    printf("EVT_LE_ADVERTISING_REPORT (0x%02X)\n", meta->subevent);                    
                
                    le_advertising_info *info = (le_advertising_info *) (meta->data + 1);                    
                    int8_t rssi;
                
                    ba2str(&info->bdaddr, btAddress);
                    printf("* %s (%s) [ ", btAddress, (info->bdaddr_type == LE_PUBLIC_ADDRESS) ? "public" : "random");
                    for (i = 0; i < info->length; i++) 
                    {
                        printf("0x%02X ", info->data[i]);
                    }

                    rssi = *(info->data + info->length);
                    printf("] rssi = %d dBm\n", rssi);
               
                    if(info->length != 0)
                    {
                        int current_index = 0;
                        int data_error = 0;
                
                        while(!data_error && current_index < info->length)
                        {
                            size_t data_len = info->data[current_index];
                    
                            if(data_len + 1 > info->length)
                            {
                                printf("EIR data length is longer than EIR packet length. %d + 1 > %d", (int)data_len, info->length);
                                data_error = 1;
                            }
                            else
                            {
                                process_data(info->data + current_index + 1, data_len, info);
                                current_index += data_len + 1;
                            }
                        }
                    }
                    else
                        printf("info->length == 0 !\n");                        
                }
                else
                    printf("EVT_LE = 0x%02X\n", meta->subevent);
                
                //handle = connecter(sock, btAddress);
                
                //deconnecter(sock, handle);
                
                encore = 0;    
                break;
            default:
                break;
        }
    } 
    while (encore);

    retval = hci_le_set_scan_enable(sock, 0 /* 1 - turn on, 0 - turn off */, 0 /* 0-filtering disabled, 1-filter out duplicates */, 1000  /* timeout */);
    if (retval < 0)
    {
        perror("hci_le_set_scan_enable"); //return 1;
    }
    close (sock);
    
    return 0;
}

void process_data(uint8_t *data, size_t data_len, le_advertising_info *info)
{
    printf("process_data: %d octets\n", (int)data_len);
    if(data[0] == EIR_NAME_SHORT || data[0] == EIR_NAME_COMPLETE)
    {
        size_t name_len = data_len - 1;
        char *name = malloc(name_len + 1);
        memset(name, 0, name_len + 1);
        memcpy(name, &data[2], name_len);
        
        char addr[18];
        ba2str(&info->bdaddr, addr);
        
        printf("addr=%s name=%s\n", addr, name);
        
        free(name);
    }
    else if(data[0] == EIR_FLAGS)
    {
        printf("-> Flag type: len=%d\n", (int)data_len);
        int i;
        for(i=1; i<data_len; i++)
        {
          printf("\tFlag data: 0x%02X\n", data[i]); // 0x06 -> 0000 0110
        }
        /*
         bit 0 LE Limited Discoverable Mode
         bit 1 LE General Discoverable Mode
         bit 2 BR/EDR Supported
         bit 3 Simultaneous LE and BR/EDR to Same Device Capable (controller)
         bit 4 Simultaneous LE and BR/EDR to Same Device Capable (Host)
        */
    }
    else if(data[0] == EIR_MANUFACTURE_SPECIFIC)
    {
        printf("-> Manufacture specific type: len=%d\n", (int)data_len);
        
        // https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers
        // TODO int company_id = data[current_index + 2] 
        
        int i;
        for(i=1; i<data_len; i++)
        {
          printf("\tData: 0x%02X\n", data[i]);
        }
    }
    else if(data[0] == EIR_UUID128_SOME)
    {
        printf("-> UUID 128 type: len=%d\n", (int)data_len);
        printf("\t");
        int i;
        for(i=data_len-1; i>0; i--)
        {
          printf("%02x", data[i]);
        }
        printf("\n");
    }
    else
    {
        printf("-> Unknown type: type=0x%02X\n", data[0]);        
    }
}

int connecter(int sock, char dest[18])
{    
    struct hci_dev_info di;
    uint16_t handle;
    char addr[18];
    bdaddr_t bdaddr;    
    uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
	uint16_t min_interval, supervision_timeout, window;
	uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
    int retval;
    char name[248];
    
    if (hci_devinfo(0, &di) < 0) 
    {
        perror("hci_devinfo");    
    }    
    ba2str(&di.bdaddr, addr);
    printf("Device  : %s [%s]\n", di.name, addr);        
    
    str2ba(dest, &bdaddr);
    
    interval = htobs(0x0004);
	window = htobs(0x0004);
    //initiator_filter = 0x01; /* Use white list */
    //peer_bdaddr_type = LE_RANDOM_ADDRESS;
	own_bdaddr_type = 0x00;
	min_interval = htobs(0x000F);
	max_interval = htobs(0x000F);
	latency = htobs(0x0000);
	supervision_timeout = htobs(0x0C80);
	min_ce_length = htobs(0x0001);
	max_ce_length = htobs(0x0001);
	retval = hci_le_create_conn(sock, interval, window, initiator_filter,
			peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
			max_interval, latency, supervision_timeout,
			min_ce_length, max_ce_length, &handle, 25000);
    if (retval < 0) 
    {
        perror("hci_le_create_conn");
        // TODO close(sock);
        return -1;
    }    
    printf("Handle : %d\n", handle);    
    
    /*if (hci_read_remote_name(sock, &bdaddr, sizeof(name), name, 25000) == 0)
		printf("Name : %s\n", name);*/
    
    //sleep(1);
    
    return handle;
}

void deconnecter(int sock, uint16_t handle)
{
    hci_disconnect(sock, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
}

只能搜索到一个:

# chmod +x /usr/bin/test4 && /usr/bin/test4
# 0x04 0x3E 0x22 0x02 0x01 0x00 0x01 0x4B 0x9D 0xE5 0x78 0x50 0xED 0x16 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x45 0x44 0x35 0x30 0x37 0x38 0x45 0x35 0x39 0x44 0x34 0x42 0xA5 (37)

EVT_LE_ADVERTISING_REPORT (0x02)
* ED:50:78:E5:9D:4B (random) [ 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x45 0x44 0x35 0x30 0x37 0x38 0x45 0x35 0x39 0x44 0x34 0x42 ] rssi = -91 dBm
process_data: 2 octets
-> Flag type: len=2
        Flag data: 0x06
process_data: 18 octets
addr=ED:50:78:E5:9D:4B name=YCOMED5078E59D4B▒
#
#
#
# chmod +x /usr/bin/test4 && /usr/bin/test4
# 0x04 0x3E 0x22 0x02 0x01 0x00 0x01 0xF1 0x49 0xDF 0xB0 0x2C 0xC7 0x16 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x43 0x37 0x32 0x43 0x42 0x30 0x44 0x46 0x34 0x39 0x46 0x31 0xAA (37)

EVT_LE_ADVERTISING_REPORT (0x02)
* C7:2C:B0:DF:49:F1 (random) [ 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x43 0x37 0x32 0x43 0x42 0x30 0x44 0x46 0x34 0x39 0x46 0x31 ] rssi = -86 dBm
process_data: 2 octets
-> Flag type: len=2
        Flag data: 0x06
process_data: 18 octets
addr=C7:2C:B0:DF:49:F1 name=YCOMC72CB0DF49F1▒
#
#
#
#
#
# chmod +x /usr/bin/test4 && /usr/bin/test4
# 0x04 0x3E 0x22 0x02 0x01 0x00 0x01 0x8E 0xD0 0xD0 0x82 0xE5 0xCC 0x16 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x43 0x43 0x45 0x35 0x38 0x32 0x44 0x30 0x44 0x30 0x38 0x45 0xB8 (37)

EVT_LE_ADVERTISING_REPORT (0x02)
* CC:E5:82:D0:D0:8E (random) [ 0x02 0x01 0x06 0x12 0x09 0x44 0x59 0x43 0x4F 0x4D 0x43 0x43 0x45 0x35 0x38 0x32 0x44 0x30 0x44 0x30 0x38 0x45 ] rssi = -72 dBm
process_data: 2 octets
-> Flag type: len=2
        Flag data: 0x06
process_data: 18 octets
addr=CC:E5:82:D0:D0:8E name=YCOMCCE582D0D08E▒

离线

楼主 #1 2023-12-23 21:26:07

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

595f3c34c741f3851fc7892c646cc28.png

离线

楼主 #2 2023-12-23 22:40:29

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

弄了个极度经典版的 hcitool.c :

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2000-2001  Qualcomm Incorporated
 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <signal.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"


#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

/* Unofficial value, might still change */
#define LE_LINK		0x80

#define FLAGS_AD_TYPE 0x01
#define FLAGS_LIMITED_MODE_BIT 0x01
#define FLAGS_GENERAL_MODE_BIT 0x02

#define EIR_FLAGS                   0x01  /* flags */
#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT              0x08  /* shortened local name */
#define EIR_NAME_COMPLETE           0x09  /* complete local name */
#define EIR_TX_POWER                0x0A  /* transmit power level */
#define EIR_DEVICE_ID               0x10  /* device ID */

#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)

static volatile int signal_received = 0;

static void usage(void);




static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
			char ***argv, const char *usage)
{
	*argc -= optind;
	/* too many arguments, but when "max_num_arg < min_num_arg" then no
		 limiting (prefer "max_num_arg=-1" to gen infinity)
	*/
	if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
		fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
				*argv[0], max_num_arg);
		printf("%s", usage);
		exit(1);
	}

	/* print usage */
	if (*argc < min_num_arg) {
		fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
				*argv[0], min_num_arg);
		printf("%s", usage);
		exit(0);
	}

	*argv += optind;
}

static char *type2str(uint8_t type)
{
	switch (type) {
	case SCO_LINK:
		return "SCO";
	case ACL_LINK:
		return "ACL";
	case ESCO_LINK:
		return "eSCO";
	case LE_LINK:
		return "LE";
	default:
		return "Unknown";
	}
}


static void hex_dump(char *pref, int width, unsigned char *buf, int len)
{
	register int i,n;

	for (i = 0, n = 1; i < len; i++, n++) {
		if (n == 1)
			printf("%s", pref);
		printf("%2.2X ", buf[i]);
		if (n == width) {
			printf("\n");
			n = 0;
		}
	}
	if (i && n!=1)
		printf("\n");
}


static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
{
	size_t offset;

	if (!flags || !data)
		return -EINVAL;

	offset = 0;
	while (offset < size) {
		uint8_t len = data[offset];
		uint8_t type;

		/* Check if it is the end of the significant part */
		if (len == 0)
			break;

		if (len + offset > size)
			break;

		type = data[offset + 1];

		if (type == FLAGS_AD_TYPE) {
			*flags = data[offset + 2];
			return 0;
		}

		offset += 1 + len;
	}

	return -ENOENT;
}

static int check_report_filter(uint8_t procedure, le_advertising_info *info)
{
	uint8_t flags;

	/* If no discovery procedure is set, all reports are treat as valid */
	if (procedure == 0)
		return 1;

	/* Read flags AD type value from the advertising report if it exists */
	if (read_flags(&flags, info->data, info->length))
		return 0;

	switch (procedure) {
	case 'l': /* Limited Discovery Procedure */
		if (flags & FLAGS_LIMITED_MODE_BIT)
			return 1;
		break;
	case 'g': /* General Discovery Procedure */
		if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
			return 1;
		break;
	default:
		fprintf(stderr, "Unknown discovery procedure\n");
	}

	return 0;
}

static void sigint_handler(int sig)
{
	signal_received = sig;
}

static void eir_parse_name(uint8_t *eir, size_t eir_len,
						char *buf, size_t buf_len)
{
	size_t offset;

	offset = 0;
	while (offset < eir_len) {
		uint8_t field_len = eir[0];
		size_t name_len;

		/* Check for the end of EIR */
		if (field_len == 0)
			break;

		if (offset + field_len > eir_len)
			goto failed;

		switch (eir[1]) {
		case EIR_NAME_SHORT:
		case EIR_NAME_COMPLETE:
			name_len = field_len - 1;
			if (name_len > buf_len)
				goto failed;

			memcpy(buf, &eir[2], name_len);
			return;
		}

		offset += field_len + 1;
		eir += field_len + 1;
	}

failed:
	snprintf(buf, buf_len, "(unknown)");
}

static int print_advertising_devices(int dd, uint8_t filter_type)
{
	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
	struct hci_filter nf, of;
	struct sigaction sa;
	socklen_t olen;
	int len;

	olen = sizeof(of);
	if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
		printf("Could not get socket options\n");
		return -1;
	}

	hci_filter_clear(&nf);
	hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
	hci_filter_set_event(EVT_LE_META_EVENT, &nf);

	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
		printf("Could not set socket options\n");
		return -1;
	}

	memset(&sa, 0, sizeof(sa));
	sa.sa_flags = SA_NOCLDSTOP;
	sa.sa_handler = sigint_handler;
	sigaction(SIGINT, &sa, NULL);

	while (1) {
		evt_le_meta_event *meta;
		le_advertising_info *info;
		char addr[18];

		while ((len = read(dd, buf, sizeof(buf))) < 0) {
			if (errno == EINTR && signal_received == SIGINT) {
				len = 0;
				goto done;
			}

			if (errno == EAGAIN || errno == EINTR)
				continue;
			goto done;
		}

		ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
		len -= (1 + HCI_EVENT_HDR_SIZE);

		meta = (void *) ptr;

		if (meta->subevent != 0x02)
			goto done;

		/* Ignoring multiple reports */
		info = (le_advertising_info *) (meta->data + 1);
		if (check_report_filter(filter_type, info)) {
			char name[30];

			memset(name, 0, sizeof(name));

			ba2str(&info->bdaddr, addr);
			eir_parse_name(info->data, info->length,
							name, sizeof(name) - 1);

			printf("%s %s\n", addr, name);
		}
	}

done:
	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));

	if (len < 0)
		return -1;

	return 0;
}

static struct option lescan_options[] = {
	{ "help",	0, 0, 'h' },
	{ "static",	0, 0, 's' },
	{ "privacy",	0, 0, 'p' },
	{ "passive",	0, 0, 'P' },
	{ "whitelist",	0, 0, 'w' }, /* Deprecated. Kept for compatibility. */
	{ "acceptlist",	0, 0, 'a' },
	{ "discovery",	1, 0, 'd' },
	{ "duplicates",	0, 0, 'D' },
	{ 0, 0, 0, 0 }
};

static const char *lescan_help =
	"Usage:\n"
	"\tlescan [--privacy] enable privacy\n"
	"\tlescan [--passive] set scan type passive (default active)\n"
	"\tlescan [--acceptlist] scan for address in the accept list only\n"
	"\tlescan [--discovery=g|l] enable general or limited discovery"
		"procedure\n"
	"\tlescan [--duplicates] don't filter duplicates\n";

static void cmd_lescan(int dev_id, int argc, char **argv)
{
	int err, opt, dd;
	uint8_t own_type = LE_PUBLIC_ADDRESS;
	uint8_t scan_type = 0x01;
	uint8_t filter_type = 0;
	uint8_t filter_policy = 0x00;
	uint16_t interval = htobs(0x0010);
	uint16_t window = htobs(0x0010);
	uint8_t filter_dup = 0x01;

	for_each_opt(opt, lescan_options, NULL) {
		switch (opt) {
		case 's':
			own_type = LE_RANDOM_ADDRESS;
			break;
		case 'p':
			own_type = LE_RANDOM_ADDRESS;
			break;
		case 'P':
			scan_type = 0x00; /* Passive */
			break;
		case 'w': /* Deprecated. Kept for compatibility. */
		case 'a':
			filter_policy = 0x01; /* Accept list */
			break;
		case 'd':
			filter_type = optarg[0];
			if (filter_type != 'g' && filter_type != 'l') {
				fprintf(stderr, "Unknown discovery procedure\n");
				exit(1);
			}

			interval = htobs(0x0012);
			window = htobs(0x0012);
			break;
		case 'D':
			filter_dup = 0x00;
			break;
		default:
			printf("%s", lescan_help);
			return;
		}
	}
	helper_arg(0, 1, &argc, &argv, lescan_help);

	if (dev_id < 0)
		dev_id = hci_get_route(NULL);

	dd = hci_open_dev(dev_id);
	if (dd < 0) {
		perror("Could not open device");
		exit(1);
	}

	err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
						own_type, filter_policy, 10000);
	if (err < 0) {
		perror("Set scan parameters failed");
		exit(1);
	}

	err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
	if (err < 0) {
		perror("Enable scan failed");
		exit(1);
	}

	printf("LE Scan ...\n");

	err = print_advertising_devices(dd, filter_type);
	if (err < 0) {
		perror("Could not receive advertising events");
		exit(1);
	}

	printf("-----------------------------------------------------\n");
	err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
	if (err < 0) {
		perror("Disable scan failed");
		exit(1);
	}

	hci_close_dev(dd);
}



static struct {
	char *cmd;
	void (*func)(int dev_id, int argc, char **argv);
	char *doc;
} command[] = {
	{ "lescan",   cmd_lescan,  "Start LE scan"                        },
	{ NULL, NULL, 0 }
};

static void usage(void)
{
	int i;

	printf("hcitool - HCI Tool ver %s\n", VERSION);
	printf("Usage:\n"
		"\thcitool [options] <command> [command parameters]\n");
	printf("Options:\n"
		"\t--help\tDisplay help\n"
		"\t-i dev\tHCI device\n");
	printf("Commands:\n");
	for (i = 0; command[i].cmd; i++)
		printf("\t%-4s\t%s\n", command[i].cmd,
		command[i].doc);
	printf("\n"
		"For more information on the usage of each command use:\n"
		"\thcitool <command> --help\n" );
}

static struct option main_options[] = {
	{ "help",	0, 0, 'h' },
	{ "device",	1, 0, 'i' },
	{ 0, 0, 0, 0 }
};

int main(int argc, char *argv[])
{
	int opt, i, dev_id = -1;
	bdaddr_t ba;

	while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
		switch (opt) {
		case 'i':
			dev_id = hci_devid(optarg);
			if (dev_id < 0) {
				perror("Invalid device");
				exit(1);
			}
			break;

		case 'h':
		default:
			usage();
			exit(0);
		}
	}

	argc -= optind;
	argv += optind;
	optind = 0;

	if (argc < 1) {
		usage();
		exit(0);
	}

	if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
		perror("Device is not available");
		exit(1);
	}

	for (i = 0; command[i].cmd; i++) {
		if (strncmp(command[i].cmd,
				argv[0], strlen(command[i].cmd)))
			continue;

		command[i].func(dev_id, argc, argv);
		break;
	}

	if (command[i].cmd == 0) {
		fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
		exit(1);
	}

	return 0;
}

离线

楼主 #3 2023-12-23 23:00:11

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2000-2001  Qualcomm Incorporated
 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
 *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <signal.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"


#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

/* Unofficial value, might still change */
#define LE_LINK		0x80

#define FLAGS_AD_TYPE 0x01
#define FLAGS_LIMITED_MODE_BIT 0x01
#define FLAGS_GENERAL_MODE_BIT 0x02

#define EIR_FLAGS                   0x01  /* flags */
#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT              0x08  /* shortened local name */
#define EIR_NAME_COMPLETE           0x09  /* complete local name */
#define EIR_TX_POWER                0x0A  /* transmit power level */
#define EIR_DEVICE_ID               0x10  /* device ID */

#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)

static volatile int signal_received = 0;

static void usage(void);




static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
			char ***argv, const char *usage)
{
	*argc -= optind;
	/* too many arguments, but when "max_num_arg < min_num_arg" then no
		 limiting (prefer "max_num_arg=-1" to gen infinity)
	*/
	if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
		fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
				*argv[0], max_num_arg);
		printf("%s", usage);
		exit(1);
	}

	/* print usage */
	if (*argc < min_num_arg) {
		fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
				*argv[0], min_num_arg);
		printf("%s", usage);
		exit(0);
	}

	*argv += optind;
}

static char *type2str(uint8_t type)
{
	switch (type) {
	case SCO_LINK:
		return "SCO";
	case ACL_LINK:
		return "ACL";
	case ESCO_LINK:
		return "eSCO";
	case LE_LINK:
		return "LE";
	default:
		return "Unknown";
	}
}


static void hex_dump(char *pref, int width, unsigned char *buf, int len)
{
	register int i,n;

	for (i = 0, n = 1; i < len; i++, n++) {
		if (n == 1)
			printf("%s", pref);
		printf("%2.2X ", buf[i]);
		if (n == width) {
			printf("\n");
			n = 0;
		}
	}
	if (i && n!=1)
		printf("\n");
}


static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
{
	size_t offset;

	if (!flags || !data)
		return -EINVAL;

	offset = 0;
	while (offset < size) {
		uint8_t len = data[offset];
		uint8_t type;

		/* Check if it is the end of the significant part */
		if (len == 0)
			break;

		if (len + offset > size)
			break;

		type = data[offset + 1];

		if (type == FLAGS_AD_TYPE) {
			*flags = data[offset + 2];
			return 0;
		}

		offset += 1 + len;
	}

	return -ENOENT;
}

static int check_report_filter(uint8_t procedure, le_advertising_info *info)
{
	uint8_t flags;

	/* If no discovery procedure is set, all reports are treat as valid */
	if (procedure == 0)
		return 1;

	/* Read flags AD type value from the advertising report if it exists */
	if (read_flags(&flags, info->data, info->length))
		return 0;

	switch (procedure) {
	case 'l': /* Limited Discovery Procedure */
		if (flags & FLAGS_LIMITED_MODE_BIT)
			return 1;
		break;
	case 'g': /* General Discovery Procedure */
		if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
			return 1;
		break;
	default:
		fprintf(stderr, "Unknown discovery procedure\n");
	}

	return 0;
}

static void sigint_handler(int sig)
{
	signal_received = sig;
}

static void eir_parse_name(uint8_t *eir, size_t eir_len,
						char *buf, size_t buf_len)
{
	size_t offset;

	offset = 0;
	while (offset < eir_len) {
		uint8_t field_len = eir[0];
		size_t name_len;

		/* Check for the end of EIR */
		if (field_len == 0)
			break;

		if (offset + field_len > eir_len)
			goto failed;

		switch (eir[1]) {
		case EIR_NAME_SHORT:
		case EIR_NAME_COMPLETE:
			name_len = field_len - 1;
			if (name_len > buf_len)
				goto failed;

			memcpy(buf, &eir[2], name_len);
			return;
		}

		offset += field_len + 1;
		eir += field_len + 1;
	}

failed:
	snprintf(buf, buf_len, "(unknown)");
}

static int print_advertising_devices(int dd, uint8_t filter_type)
{
	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
	struct hci_filter nf, of;
	struct sigaction sa;
	socklen_t olen;
	int len;

	olen = sizeof(of);
	if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
		printf("Could not get socket options\n");
		return -1;
	}

	hci_filter_clear(&nf); 	
	hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
	hci_filter_set_event(EVT_LE_META_EVENT, &nf);

	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
		printf("Could not set socket options\n");
		return -1;
	}

	memset(&sa, 0, sizeof(sa));
	sa.sa_flags = SA_NOCLDSTOP;
	sa.sa_handler = sigint_handler;
	sigaction(SIGINT, &sa, NULL);

	while (1) {
		evt_le_meta_event *meta;
		le_advertising_info *info;
		char addr[18];

		while ((len = read(dd, buf, sizeof(buf))) < 0) {
			if (errno == EINTR && signal_received == SIGINT) {
				len = 0;
				goto done;
			}

			if (errno == EAGAIN || errno == EINTR)
				continue;
			goto done;
		}

		ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
		len -= (1 + HCI_EVENT_HDR_SIZE);

		meta = (void *) ptr;

		if (meta->subevent != 0x02)
			goto done;

		/* Ignoring multiple reports */
		info = (le_advertising_info *) (meta->data + 1);
		if (check_report_filter(filter_type, info)) {
			char name[30];

			memset(name, 0, sizeof(name));

			ba2str(&info->bdaddr, addr);
			eir_parse_name(info->data, info->length,
							name, sizeof(name) - 1);

			printf("%s %s\n", addr, name);
		}
	}

done:
	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));

	if (len < 0)
		return -1;

	return 0;
}


static void cmd_lescan(int dev_id, int argc, char **argv)
{
	int err, opt, dd;
	uint8_t own_type = LE_PUBLIC_ADDRESS;
	uint8_t scan_type = 0x01;
	uint8_t filter_type = 0;
	uint8_t filter_policy = 0x00;
	uint16_t interval = htobs(0x0010);
	uint16_t window = htobs(0x0010);
	uint8_t filter_dup = 0x01;

	if (dev_id < 0)
		dev_id = hci_get_route(NULL);

	dd = hci_open_dev(dev_id);
	if (dd < 0) {
		perror("Could not open device");
		exit(1);
	}

	err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
						own_type, filter_policy, 10000);
	if (err < 0) {
		perror("Set scan parameters failed");
		exit(1);
	}

	err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
	if (err < 0) {
		perror("Enable scan failed");
		exit(1);
	}

	printf("LE Scan ...\n");

	err = print_advertising_devices(dd, filter_type);
	if (err < 0) {
		perror("Could not receive advertising events");
		exit(1);
	}

	printf("-----------------------------------------------------\n");
	err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
	if (err < 0) {
		perror("Disable scan failed");
		exit(1);
	}

	hci_close_dev(dd);
}



static struct {
	char *cmd;
	void (*func)(int dev_id, int argc, char **argv);
	char *doc;
} command[] = {
	{ "lescan",   cmd_lescan,  "Start LE scan"                        },
	{ NULL, NULL, 0 }
};

static void usage(void)
{
	int i;

	printf("hcitool - HCI Tool ver %s\n", VERSION);
	printf("Usage:\n"
		"\thcitool [options] <command> [command parameters]\n");
	printf("Options:\n"
		"\t--help\tDisplay help\n"
		"\t-i dev\tHCI device\n");
	printf("Commands:\n");
	for (i = 0; command[i].cmd; i++)
		printf("\t%-4s\t%s\n", command[i].cmd,
		command[i].doc);
	printf("\n"
		"For more information on the usage of each command use:\n"
		"\thcitool <command> --help\n" );
}

static struct option main_options[] = {
	{ "help",	0, 0, 'h' },
	{ "device",	1, 0, 'i' },
	{ 0, 0, 0, 0 }
};

int main(int argc, char *argv[])
{
	int opt, i, dev_id = -1;
	bdaddr_t ba;

	while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
		switch (opt) {
		case 'i':
			dev_id = hci_devid(optarg);
			if (dev_id < 0) {
				perror("Invalid device");
				exit(1);
			}
			break;

		case 'h':
		default:
			usage();
			exit(0);
		}
	}

	argc -= optind;
	argv += optind;
	optind = 0;

	if (argc < 1) {
		usage();
		exit(0);
	}

	if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
		perror("Device is not available");
		exit(1);
	}

	for (i = 0; command[i].cmd; i++) {
		if (strncmp(command[i].cmd,
				argv[0], strlen(command[i].cmd)))
			continue;

		command[i].func(dev_id, argc, argv);
		break;
	}

	if (command[i].cmd == 0) {
		fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
		exit(1);
	}

	return 0;
}

继续精简

离线

楼主 #4 2023-12-23 23:02:14

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

/opt/buildroot/buildroot-2023/output/host/bin/aarch64-linux-gnu-gcc -DVERSION=\"AAAA\" -o hcitoolhcitool.c -I./output/build/bluez5_utils-5.68/ -lbluetooth

离线

楼主 #5 2023-12-24 19:43:41

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <sys/types.h>
#include <sys/socket.h>

// $ gcc ble-scan.c -lbluetooth -o ble-scan

#define EIR_FLAGS                   0x01  /* flags */
#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT              0x08  /* shortened local name */
#define EIR_NAME_COMPLETE           0x09  /* complete local name */
#define EIR_TX_POWER                0x0A  /* transmit power level */
#define EIR_DEVICE_ID               0x10  /* device ID */
#define EIR_MANUFACTURE_SPECIFIC    0xFF

void process_data(uint8_t *data, size_t data_len, le_advertising_info *info);
int connecter(int sock, char dest[18]);
void deconnecter(int sock, uint16_t handle);

int main (int argc, char **argv)
{
    int sock, retval;
    int i, len;
    unsigned char buf[HCI_MAX_FRAME_SIZE];
    char btAddress[18];
    uint16_t handle;
    struct sockaddr_hci addr;
    struct hci_filter filter;
    int encore = 1;

    sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    if (-1 == sock)
    {
        perror("socket"); return 1;
    }

    hci_filter_clear(&filter);
    hci_filter_all_ptypes(&filter);
    hci_filter_all_events(&filter);

    retval = setsockopt(sock, SOL_HCI, HCI_FILTER, &filter, sizeof(filter));
    if (-1 == retval)
    {
        perror("setsockopt"); return 1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.hci_family = AF_BLUETOOTH;
    addr.hci_dev = 0;
    retval = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (-1 == retval)
    {
        perror("bind"); return 1;
    }

        uint8_t scan_type = 0x00; /* Passive */
    uint16_t interval = htobs(0x0010);
        uint16_t window = htobs(0x0010);
    uint8_t own_type = 0x00;
        uint8_t filter_policy = 0x00; /* 1 -> Whitelist */

    retval = hci_le_set_scan_parameters(sock, scan_type, interval, window, own_type, filter_policy, 1000);
    //retval = hci_le_set_scan_parameters(sock, 0, 0x10, 0x10, 0, 0, 1000);
    if (retval < 0)
    {
        perror("hci_le_set_scan_parameters"); //return 1;
    }

    retval = hci_le_set_scan_enable(sock, 1 /* 1 - turn on, 0 - turn off */, 0 /* 0-filtering disabled, 1-filter out duplicates */, 1000  /* timeout */);
    if (retval < 0)
    {
        perror("hci_le_set_scan_enable"); //return 1;
    }

    do
    {
        memset (buf, 0, sizeof(buf));
        retval = recv (sock, buf, sizeof(buf), 0);
        if (-1 == retval)
        {
            perror("recv"); return 1;
        }
#if 0
        printf ("# ");
        for(i=0;i<retval;i++)
            printf ("0x%02X ", buf[i]);
            //printf ("%c ", buf[i]);
        printf ("(%d)\n\n", retval);
        /*printf ("# 0x%02X 0x%02X 0x%02X 0x%02X (%d)\n",
                    buf[0], buf[1],
                    buf[2], buf[3], retval);*/
#endif
        switch (buf[1])
        {
#if 0
            case EVT_CMD_STATUS: // 0x0F
                if (buf[3])
                {
                    printf ("Erreur !\n");
                    //encore = 0;
                }
                else
                {
                    printf ("Commande en cours\n");
                }
                break;
            case EVT_INQUIRY_RESULT: // 0x02
            printf ("Périphérique trouvé:\n");
                printf ("  * Adresse : %02x:%02x:%02x:%02x:%02x:%02x\n",
                    buf[9], buf[8],
                    buf[7], buf[6],
                    buf[5], buf[4]);
                printf ("  * Classe  : 0x%02x%02x%02x\n\n",
                    buf[15], buf[14], buf[13]);
            break;
         case EVT_EXTENDED_INQUIRY_RESULT: // 0x2F
                printf ("Périphérique trouvé:\n");
                printf ("  * Adresse : %02x:%02x:%02x:%02x:%02x:%02x\n",
                    buf[9], buf[8],
                    buf[7], buf[6],
                    buf[5], buf[4]);
                printf ("  * Classe  : 0x%02x%02x%02x\n",
                    buf[14], buf[13], buf[12]);
                printf ("  * RSSI    : %d\n\n", // Received Signal Strength Indication
                    buf[17]);
                break;
            case EVT_INQUIRY_COMPLETE: // 0x01
                //encore = 0;
                break;
#endif
            case EVT_LE_META_EVENT: // 0x3E
                len = retval;
                evt_le_meta_event *meta = (void *)(buf + (1 + HCI_EVENT_HDR_SIZE));

                len -= (1 + HCI_EVENT_HDR_SIZE);

                if (meta->subevent == EVT_LE_ADVERTISING_REPORT)
                {
//                    printf("EVT_LE_ADVERTISING_REPORT (0x%02X)\n", meta->subevent);

                    le_advertising_info *info = (le_advertising_info *) (meta->data + 1);
                    int8_t rssi;

                    ba2str(&info->bdaddr, btAddress);
#if 0
                    printf("* %s (%s) [ ", btAddress, (info->bdaddr_type == LE_PUBLIC_ADDRESS) ? "public" : "random");
                    for (i = 0; i < info->length; i++)
                    {
                        printf("0x%02X ", info->data[i]);
                    }

                    rssi = *(info->data + info->length);
                    printf("] rssi = %d dBm\n", rssi);
#endif
                    if(info->length != 0)
                    {
                        int current_index = 0;
                        int data_error = 0;

                        while(!data_error && current_index < info->length)
                        {
                            size_t data_len = info->data[current_index];

                            if(data_len + 1 > info->length)
                            {
                                printf("EIR data length is longer than EIR packet length. %d + 1 > %d", (int)data_len, info->length);
                                data_error = 1;
                            }
                            else
                            {
                                process_data(info->data + current_index + 1, data_len, info);
                                current_index += data_len + 1;
                            }
                        }
                    }
                    else
                        printf("info->length == 0 !\n");
                }
                else
                    printf("EVT_LE = 0x%02X\n", meta->subevent);

                //handle = connecter(sock, btAddress);

                //deconnecter(sock, handle);

                //encore = 0;
                break;
            default:
                break;
        }
    }
    while (encore);

    retval = hci_le_set_scan_enable(sock, 0 /* 1 - turn on, 0 - turn off */, 0 /* 0-filtering disabled, 1-filter out duplicates */, 1000  /* timeout */);
    if (retval < 0)
    {
        perror("hci_le_set_scan_enable"); //return 1;
    }
    close (sock);

    return 0;
}

void process_data(uint8_t *data, size_t data_len, le_advertising_info *info)
{
//    printf("process_data: %d octets\n", (int)data_len);
    if(data[0] == EIR_NAME_SHORT || data[0] == EIR_NAME_COMPLETE)
    {
        size_t name_len = data_len - 1;
        char *name = malloc(name_len + 1);
        memset(name, 0, name_len + 1);
        memcpy(name, &data[2], name_len);

        char addr[18];
        ba2str(&info->bdaddr, addr);

        printf("addr=%s name=%s\n", addr, name);

        free(name);
    }
    else if(data[0] == EIR_FLAGS)
    {
#if 0
        printf("-> Flag type: len=%d\n", (int)data_len);
        int i;
        for(i=1; i<data_len; i++)
        {
          printf("\tFlag data: 0x%02X\n", data[i]); // 0x06 -> 0000 0110
        }
        /*
         bit 0 LE Limited Discoverable Mode
         bit 1 LE General Discoverable Mode
         bit 2 BR/EDR Supported
         bit 3 Simultaneous LE and BR/EDR to Same Device Capable (controller)
         bit 4 Simultaneous LE and BR/EDR to Same Device Capable (Host)
        */
#endif
    }
    else if(data[0] == EIR_MANUFACTURE_SPECIFIC)
    {
#if 0
        printf("-> Manufacture specific type: len=%d\n", (int)data_len);

        // https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers
        // TODO int company_id = data[current_index + 2]

        int i;
        for(i=1; i<data_len; i++)
        {
          printf("\tData: 0x%02X\n", data[i]);
        }
#endif
    }
    else if(data[0] == EIR_UUID128_SOME)
    {
        printf("-> UUID 128 type: len=%d\n", (int)data_len);
        printf("\t");
        int i;
        for(i=data_len-1; i>0; i--)
        {
          printf("%02x", data[i]);
        }
        printf("\n");
    }
    else
    {
        printf("-> Unknown type: type=0x%02X\n", data[0]);
    }
}

int connecter(int sock, char dest[18])
{
    struct hci_dev_info di;
    uint16_t handle;
    char addr[18];
    bdaddr_t bdaddr;
    uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
        uint16_t min_interval, supervision_timeout, window;
        uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
    int retval;
    char name[248];

    if (hci_devinfo(0, &di) < 0)
    {
        perror("hci_devinfo");
    }
    ba2str(&di.bdaddr, addr);
    printf("Device  : %s [%s]\n", di.name, addr);

    str2ba(dest, &bdaddr);

    interval = htobs(0x0004);
        window = htobs(0x0004);
    //initiator_filter = 0x01; /* Use white list */
    //peer_bdaddr_type = LE_RANDOM_ADDRESS;
        own_bdaddr_type = 0x00;
        min_interval = htobs(0x000F);
        max_interval = htobs(0x000F);
        latency = htobs(0x0000);
        supervision_timeout = htobs(0x0C80);
        min_ce_length = htobs(0x0001);
        max_ce_length = htobs(0x0001);
        retval = hci_le_create_conn(sock, interval, window, initiator_filter,
                        peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
                        max_interval, latency, supervision_timeout,
                        min_ce_length, max_ce_length, &handle, 25000);
    if (retval < 0)
    {
        perror("hci_le_create_conn");
        // TODO close(sock);
        return -1;
    }
    printf("Handle : %d\n", handle);

    /*if (hci_read_remote_name(sock, &bdaddr, sizeof(name), name, 25000) == 0)
                printf("Name : %s\n", name);*/

    //sleep(1);

    return handle;
}

void deconnecter(int sock, uint16_t handle)
{
    hci_disconnect(sock, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
}

精简了一下,可以一直搜索附近的BLE设备

离线

楼主 #6 2023-12-31 16:12:39

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

蓝牙的uuid16和uuid128的意义:

蓝牙的UUID (Universally Unique Identifier) 是用于唯一标识蓝牙设备、服务和特性的值。UUID 可以使用不同的长度进行表示,其中包括 UUID16 和 UUID128。

① UUID16:UUID16 是一个 16 位的短格式 UUID。它由 4 个十六进制数字表示,例如 0x180D。UUID16 范围从 0x0000 到 0xFFFF,共有 65536 个可能的值。UUID16 常用于标识通用的蓝牙服务,如心率监测(Heart Rate Monitor)、电池服务(Battery Service)等。由于 UUID16 长度较短,因此存在较大的可能性发生冲突。

② UUID128:UUID128 是一个 128 位的长格式 UUID。它由 32 个十六进制数字表示,例如 00002a37-0000-1000-8000-00805f9b34fb。UUID128 的长度更长,因此具有更高的唯一性。UUID128 常用于自定义的蓝牙服务和特性,以确保全球范围内的唯一性。

需要注意的是,UUID16 和 UUID128 之间是可以相互转换的。UUID16 可以通过添加预定义的基础 UUID(Base UUID)或 Bluetooth SIG 定义的预定义服务 UUID 来扩展为 UUID128。UUID128 可以通过截断前面的字节来缩短为 UUID16。

在蓝牙设备之间进行通信时,UUID 用于唯一标识服务和特性。通过使用不同长度的 UUID,可以满足不同场景下的需求,并确保唯一性和互操作性。

离线

楼主 #7 2023-12-31 16:13:24

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

蓝牙开发常用UUID表

Sample Services
0000180d-0000-1000-8000-00805f9b34fb    Heart Rate Service    
0000180a-0000-1000-8000-00805f9b34fb    Device Information Service    
Sample Characteristics. 
00002a37-0000-1000-8000-00805f9b34fb    Heart Rate Measurement    
00002a29-0000-1000-8000-00805f9b34fb    Manufacturer Name String    
    
GATT Services 
00001800-0000-1000-8000-00805f9b34fb    GenericAccess    
00001801-0000-1000-8000-00805f9b34fb    GenericAttribute    
 
GATT Declarations 
00002800-0000-1000-8000-00805f9b34fb    Primary Service    
00002801-0000-1000-8000-00805f9b34fb    Secondary Service    
00002802-0000-1000-8000-00805f9b34fb    Include    
00002803-0000-1000-8000-00805f9b34fb    Characteristic    
         
GATT Descriptors 
00002900-0000-1000-8000-00805f9b34fb    Characteristic Extended Properties    
00002901-0000-1000-8000-00805f9b34fb    Characteristic User Description    
00002902-0000-1000-8000-00805f9b34fb    Client Characteristic Configuration    
00002903-0000-1000-8000-00805f9b34fb    Server Characteristic Configuration    
00002904-0000-1000-8000-00805f9b34fb    Characteristic Presentation Format    
00002905-0000-1000-8000-00805f9b34fb    Characteristic Aggregate Format    
00002906-0000-1000-8000-00805f9b34fb    Valid Range    
00002907-0000-1000-8000-00805f9b34fb    External Report Reference Descriptor    
00002908-0000-1000-8000-00805f9b34fb    Report Reference Descriptor    
 
GATT Characteristics 
00002a00-0000-1000-8000-00805f9b34fb    Device Name    
00002a01-0000-1000-8000-00805f9b34fb    Appearance    
00002a02-0000-1000-8000-00805f9b34fb    Peripheral Privacy Flag    
00002a03-0000-1000-8000-00805f9b34fb    Reconnection Address    
00002a04-0000-1000-8000-00805f9b34fb    PPCP    
00002a05-0000-1000-8000-00805f9b34fb    Service Changed    
         
GATT Service UUIDs 
00001802-0000-1000-8000-00805f9b34fb    Immediate Alert    
00001803-0000-1000-8000-00805f9b34fb    Link Loss    
00001804-0000-1000-8000-00805f9b34fb    Tx Power    
00001805-0000-1000-8000-00805f9b34fb    Current Time Service    
00001806-0000-1000-8000-00805f9b34fb    Reference Time Update Service    
00001807-0000-1000-8000-00805f9b34fb    Next DST Change Service    
00001808-0000-1000-8000-00805f9b34fb    Glucose    
00001809-0000-1000-8000-00805f9b34fb    Health Thermometer    
0000180a-0000-1000-8000-00805f9b34fb    Device Information    
0000180b-0000-1000-8000-00805f9b34fb    Network Availability    
0000180d-0000-1000-8000-00805f9b34fb    Heart Rate    
0000180e-0000-1000-8000-00805f9b34fb    Phone Alert Status Service    
0000180f-0000-1000-8000-00805f9b34fb    Battery Service    
00001810-0000-1000-8000-00805f9b34fb    Blood Pressure    
00001811-0000-1000-8000-00805f9b34fb    Alert Notification Service    
00001812-0000-1000-8000-00805f9b34fb    Human Interface Device    
00001813-0000-1000-8000-00805f9b34fb    Scan Parameters    
00001814-0000-1000-8000-00805f9b34fb    Running Speed and Cadence    
00001816-0000-1000-8000-00805f9b34fb    Cycling Speed and Cadence    
00001818-0000-1000-8000-00805f9b34fb    Cycling Power    
00001819-0000-1000-8000-00805f9b34fb    Location and Navigation    
     
GATT Characteristic UUIDs 
00002a06-0000-1000-8000-00805f9b34fb    Alert Level    
00002a07-0000-1000-8000-00805f9b34fb    Tx Power Level    
00002a08-0000-1000-8000-00805f9b34fb    Date Time    
00002a09-0000-1000-8000-00805f9b34fb    Day of Week    
00002a0a-0000-1000-8000-00805f9b34fb    Day Date Time    
00002a0c-0000-1000-8000-00805f9b34fb    Exact Time 256    
00002a0d-0000-1000-8000-00805f9b34fb    DST Offset    
00002a0e-0000-1000-8000-00805f9b34fb    Time Zone    
00002a0f-0000-1000-8000-00805f9b34fb    Local Time Information    
00002a11-0000-1000-8000-00805f9b34fb    Time with DST    
00002a12-0000-1000-8000-00805f9b34fb    Time Accuracy    
00002a13-0000-1000-8000-00805f9b34fb    Time Source    
00002a14-0000-1000-8000-00805f9b34fb    Reference Time Information    
00002a16-0000-1000-8000-00805f9b34fb    Time Update Control Point    
00002a17-0000-1000-8000-00805f9b34fb    Time Update State    
00002a18-0000-1000-8000-00805f9b34fb    Glucose Measurement    
00002a19-0000-1000-8000-00805f9b34fb    Battery Level    
00002a1c-0000-1000-8000-00805f9b34fb    Temperature Measurement    
00002a1d-0000-1000-8000-00805f9b34fb    Temperature Type    
00002a1e-0000-1000-8000-00805f9b34fb    Intermediate Temperature    
00002a21-0000-1000-8000-00805f9b34fb    Measurement Interval    
00002a22-0000-1000-8000-00805f9b34fb    Boot Keyboard Input Report    
00002a23-0000-1000-8000-00805f9b34fb    System ID    
00002a24-0000-1000-8000-00805f9b34fb    Model Number String    
00002a25-0000-1000-8000-00805f9b34fb    Serial Number String    
00002a26-0000-1000-8000-00805f9b34fb    Firmware Revision String    
00002a27-0000-1000-8000-00805f9b34fb    Hardware Revision String    
00002a28-0000-1000-8000-00805f9b34fb    Software Revision String    
00002a29-0000-1000-8000-00805f9b34fb    Manufacturer Name String    
00002a2a-0000-1000-8000-00805f9b34fb    IEEE 11073-20601 Regulatory Certification Data List    
00002a2b-0000-1000-8000-00805f9b34fb    Current Time    
00002a31-0000-1000-8000-00805f9b34fb    Scan Refresh    
00002a32-0000-1000-8000-00805f9b34fb    Boot Keyboard Output Report    
00002a33-0000-1000-8000-00805f9b34fb    Boot Mouse Input Report    
00002a34-0000-1000-8000-00805f9b34fb    Glucose Measurement Context    
00002a35-0000-1000-8000-00805f9b34fb    Blood Pressure Measurement    
00002a36-0000-1000-8000-00805f9b34fb    Intermediate Cuff Pressure    
00002a37-0000-1000-8000-00805f9b34fb    Heart Rate Measurement    
00002a38-0000-1000-8000-00805f9b34fb    Body Sensor Location    
00002a39-0000-1000-8000-00805f9b34fb    Heart Rate Control Point    
00002a3e-0000-1000-8000-00805f9b34fb    Network Availability    
00002a3f-0000-1000-8000-00805f9b34fb    Alert Status    
00002a40-0000-1000-8000-00805f9b34fb    Ringer Control Point    
00002a41-0000-1000-8000-00805f9b34fb    Ringer Setting    
00002a42-0000-1000-8000-00805f9b34fb    Alert Category ID Bit Mask    
00002a43-0000-1000-8000-00805f9b34fb    Alert Category ID    
00002a44-0000-1000-8000-00805f9b34fb    Alert Notification Control Point    
00002a45-0000-1000-8000-00805f9b34fb    Unread Alert Status    
00002a46-0000-1000-8000-00805f9b34fb    New Alert    
00002a47-0000-1000-8000-00805f9b34fb    Supported New Alert Category    
00002a48-0000-1000-8000-00805f9b34fb    Supported Unread Alert Category    
00002a49-0000-1000-8000-00805f9b34fb    Blood Pressure Feature    
00002a4a-0000-1000-8000-00805f9b34fb    HID Information    
00002a4b-0000-1000-8000-00805f9b34fb    Report Map    
00002a4c-0000-1000-8000-00805f9b34fb    HID Control Point    
00002a4d-0000-1000-8000-00805f9b34fb    Report    
00002a4e-0000-1000-8000-00805f9b34fb    Protocol Mode    
00002a4f-0000-1000-8000-00805f9b34fb    Scan Interval Window    
00002a50-0000-1000-8000-00805f9b34fb    PnP ID    
00002a51-0000-1000-8000-00805f9b34fb    Glucose Feature    
00002a52-0000-1000-8000-00805f9b34fb    Record Access Control Point    
00002a53-0000-1000-8000-00805f9b34fb    RSC Measurement    
00002a54-0000-1000-8000-00805f9b34fb    RSC Feature    
00002a55-0000-1000-8000-00805f9b34fb    SC Control Point    
00002a5b-0000-1000-8000-00805f9b34fb    CSC Measurement    
00002a5c-0000-1000-8000-00805f9b34fb    CSC Feature    
00002a5d-0000-1000-8000-00805f9b34fb    Sensor Location    
00002a63-0000-1000-8000-00805f9b34fb    Cycling Power Measurement    
00002a64-0000-1000-8000-00805f9b34fb    Cycling Power Vector    
00002a65-0000-1000-8000-00805f9b34fb    Cycling Power Feature    
00002a66-0000-1000-8000-00805f9b34fb    Cycling Power Control Point    
00002a67-0000-1000-8000-00805f9b34fb    Location and Speed    
00002a68-0000-1000-8000-00805f9b34fb    Navigation    
00002a69-0000-1000-8000-00805f9b34fb    Position Quality    
00002a6a-0000-1000-8000-00805f9b34fb    LN Feature    
00002a6b-0000-1000-8000-00805f9b34fb    LN Control Point"

比如电池电量特征的uuid16 就是 2a19,uuid128就是 00002a19-0000-1000-8000-00805f9b34fb

离线

楼主 #8 2023-12-31 16:24:50

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

aa5d2983b3575b17a9970eaadf690ec.png

2157b9a1c52fe94432aa4f93f26a1d4.png

先输入P执行连接,

运行 btstack 的测试程序:ble_central_test -u /dev/ttyS1

离线

楼主 #9 2023-12-31 16:31:32

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

SM: IO_CAPABILITY_NO_INPUT_NO_OUTPUT, MITM protection 0
SM: key range [7..16], OOB data: None
Privacy 0
Device name: BTstack

c/C - connectable off                                                 |  j/J - Read (Long) Characteristic Value by handle
d/D - bondable off/on                                                 |  k/K - Read Characteristic Value by UUID16/UUID128
---                                                                   |  l/L - Read (Long) Characteristic Descriptor by handle
1   - enable privacy using random non-resolvable private address      |  N   - Read Multiple Characteristic Values
2   - clear Peripheral Privacy Flag on PTS                            |  O   - Write without Response
3   - set Peripheral Privacy Flag on PTS                              |  q/Q - Write (Long) Characteristic Value
9   - create HCI Classic connection to addr 23:11:23:00:76:A1         |  r   - Characteristic Reliable Write
s/S - passive/active scanning                                         |  R   - Signed Write
a   - enable Advertisements                                           |  u/U - Write (Long) Characteristic Descriptor
b   - start bonding                                                   |  T   - Read Generic Profile Attributes by Type
n   - query GAP Device Name                                           |  E   - Prepare Write
o   - set GAP Reconnection Address                                    |  v   - Execute Write
t   - terminate connection, stop connecting                           |  V   - Cancel Write
p   - auto connect to PTS                                             |  ---
P   - direct connect to PTS                                           |  4   - IO_CAPABILITY_DISPLAY_ONLY
w   - signed write on characteristic with UUID b00d                   |  5   - IO_CAPABILITY_DISPLAY_YES_NO
W   - signed write on attribute with handle 0x00b1 and value 0x12     |  6   - IO_CAPABILITY_NO_INPUT_NO_OUTPUT
z   - Update L2CAP Connection Parameters                              |  7   - IO_CAPABILITY_KEYBOARD_ONLY
---                                                                   |  8   - IO_CAPABILITY_KEYBOARD_DISPLAY
e   - Discover all Primary Services                                   |  m/M - MITM protection off
f/F - Discover Primary Service by UUID16/UUID128                      |  x/X - encryption key range [7..16]/[16..16]
g   - Discover all characteristics by UUID16                          |  y/Y - OOB data off/on/toggle A/B
G   - Discover all characteristics in range                           |  ---
h   - Discover Characteristic Descriptors                             |  Ctrl-c - exit
i   - Find all included services

Direct Connection Establishment to type 0, addr 23:11:23:00:76:A1
Connection complete, handle 0x0012
Primary Service with UUID 180f, start group handle 0x0001, end group handle 0x0004
Primary Service with UUID 1804, start group handle 0x0005, end group handle 0x0007
Primary Service with UUID 1803, start group handle 0x0008, end group handle 0x000b
Primary Service with UUID 1802, start group handle 0x000c, end group handle 0x000f
Primary Service with UUID ffe0, start group handle 0x0010, end group handle 0x0026
Primary Service with UUID 5833FF01-9B8B-5191-6142-22A4536EF123, start group handle 0x0027, end group handle 0x002c
Primary Service Discovery complete
Please enter service UUID16: 180f
Discover Primary Services with UUID16 180f
Primary Service with UUID 180f, start group handle 0x0001, end group handle 0x0004
Characteristic found at 0x0002 with value handle 0x0003, uuid 2a19
Please enter service UUID16: 1804
Discover Primary Services with UUID16 1804
Primary Service with UUID 1804, start group handle 0x0005, end group handle 0x0007
Characteristic found at 0x0006 with value handle 0x0007, uuid 2a07
Please enter service UUID16: 1803
Discover Primary Services with UUID16 1803
Primary Service with UUID 1803, start group handle 0x0008, end group handle 0x000b
Characteristic found at 0x0009 with value handle 0x000a, uuid 2a06
Please enter service UUID16: 1802
Discover Primary Services with UUID16 1802
Primary Service with UUID 1802, start group handle 0x000c, end group handle 0x000f
Characteristic found at 0x000d with value handle 0x000e, uuid 2a06

离线

楼主 #10 2023-12-31 17:05:47

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

查询 2a19 对应的 handle:
5402139652e8c398ca228180b3ae664.png

先输入P连接BLE设备,输入k,2a19,5获取电量数据:
7e9f3ad2e7170d0f76df975e8a2164e.png

离线

楼主 #11 2023-12-31 17:11:20

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

83d6a61370e00592a979097ccfd315b.png

离线

楼主 #12 2023-12-31 18:03:33

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

发送端(/usr/bin/ble_central_test -u /dev/ttyS1):
02a39799963e02ae55e5498d92b0da3.png

接收端(/usr/bin/nordic_spp_le_counter -u /dev/ttyS1):
c433fbc0fd484550484392cc57aafbf.png

离线

楼主 #14 2023-12-31 22:49:13

memory
会员
注册时间: 2021-08-11
已发帖子: 244
积分: 218

Re: ble scan 扫描不到所有设备

55ee14733d431beede7c3c2a03ba147.png


btstack终于可以同时连两个BLE设备了

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "btstack_config.h"

#include "ad_parser.h"
#include "ble/att_db.h"
#include "ble/att_server.h"
#include "ble/le_device_db.h"
#include "ble/sm.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "gap.h"
#include "hci.h"
#include "hci_dump.h"
#include "l2cap.h"
#include "btstack_stdin.h"

// test profile
#include "ble_central_test.h"
#include "ble/gatt_client.h"

// Non standard IXIT
#define PTS_USES_RECONNECTION_ADDRESS_FOR_ITSELF
#define PTS_UUID128_REPRESENTATION

extern void le_device_db_local_csrk_get(int index, sm_key_t csrk);

typedef enum {
    CENTRAL_IDLE,
    CENTRAL_W4_NAME_QUERY_COMPLETE,
    CENTRAL_W4_NAME_VALUE,
    CENTRAL_W4_RECONNECTION_ADDRESS_QUERY_COMPLETE,
    CENTRAL_W4_PERIPHERAL_PRIVACY_FLAG_QUERY_COMPLETE,
    CENTRAL_W4_SIGNED_WRITE_QUERY_COMPLETE,
    CENTRAL_W4_PRIMARY_SERVICES,
    CENTRAL_ENTER_SERVICE_UUID_4_DISCOVER_CHARACTERISTICS,
    CENTRAL_ENTER_START_HANDLE_4_DISCOVER_CHARACTERISTICS,
    CENTRAL_ENTER_END_HANDLE_4_DISCOVER_CHARACTERISTICS,
    CENTRAL_W4_CHARACTERISTICS,
    CENTRAL_W4_DISCOVER_CHARACTERISTIC_DESCRIPTORS,
    CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_HANDLE,
    CENTRAL_ENTER_HANDLE_4_READ_CHARACTERISTIC_VALUE_BY_UUID,
    CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_UUID,
    CENTRAL_ENTER_OFFSET_4_READ_LONG_CHARACTERISTIC_VALUE_BY_HANDLE,
    CENTRAL_W4_READ_LONG_CHARACTERISTIC_VALUE_BY_HANDLE,
    CENTRAL_W4_READ_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE,
    CENTRAL_ENTER_OFFSET_4_READ_LONG_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE,
    CENTRAL_W4_READ_LONG_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE,
    CENTRAL_W4_READ_MULTIPLE_CHARACTERISTIC_VALUES,
    CENTRAL_W4_WRITE_WITHOUT_RESPONSE,
    CENTRAL_W4_WRITE_CHARACTERICISTIC_VALUE,
    CENTRAL_ENTER_HANDLE_4_WRITE_LONG_CHARACTERISTIC_VALUE,
    CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_VALUE,
    CENTRAL_W4_RELIABLE_WRITE,
    CENTRAL_W4_WRITE_CHARACTERISTIC_DESCRIPTOR,
    CENTRAL_ENTER_HANDLE_4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR,
    CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR,
    CENTRAL_W4_SIGNED_WRITE,

    CENTRAL_W4_ENTER_HANDLE_4_PREPARE_WRITE,
    CENTRAL_W4_ENTER_OFFSET_4_PREPARE_WRITE,

    CENTRAL_GPA_ENTER_UUID,
    CENTRAL_GPA_ENTER_START_HANDLE,
    CENTRAL_GPA_ENTER_END_HANDLE,
    CENTRAL_GPA_W4_RESPONSE,
    CENTRAL_GPA_W4_RESPONSE2,
    CENTRAL_GPA_W4_RESPONSE3,
    CENTRAL_GPA_W4_RESPONSE4,
} central_state_t;

typedef struct advertising_report {
    uint8_t   type;
    uint8_t   event_type;
    uint8_t   address_type;
    bd_addr_t address;
    uint8_t   rssi;
    uint8_t   length;
    uint8_t * data;
} advertising_report_t;

static const uint8_t gpa_format_type_len[] = {
    /* 0x00 */
    1,1,1,1,1,
    /* 0x05 */
    2,2,
    /* 0x07 */
    3,4,6,8,16,
    /* 0x0c */
    1,2,2,3,4,6,8,16,
    /* 0x14 */
    4,8,2,4,4
};


static int gap_privacy = 0;
static int gap_bondable = 1;
static char gap_device_name[20];
static int gap_connectable = 0;

static char * sm_io_capabilities = NULL;
static int sm_mitm_protection = 0;
static int sm_have_oob_data = 0;
static uint8_t * sm_oob_data_A = (uint8_t *) "0123456789012345"; // = { 0x30...0x39, 0x30..0x35}
static uint8_t * sm_oob_data_B = (uint8_t *) "3333333333333333"; // = { 0x30...0x39, 0x30..0x35}
static int sm_min_key_size = 7;
static uint8_t pts_privacy_flag;

static int ui_passkey = 0;
static int ui_digits_for_passkey = 0;
static int ui_uint16 = 0;
static int ui_uint16_request = 0;
static int ui_uint16_pos = 0;
static int ui_uuid16 = 0;
static int ui_uuid128_request = 0;
static int ui_uuid128_pos     = 0;
static uint8_t ui_uuid128[16];
static int      ui_handles_count;
static int      ui_handles_index;
static uint16_t ui_handles[10];
static uint16_t ui_attribute_handle;
static int      ui_attribute_offset;
static int      ui_value_request = 0;
static uint8_t  ui_value_data[50];
static int      ui_value_pos = 0;
static uint16_t ui_start_handle;
static uint16_t ui_end_handle;
static uint8_t ui_presentation_format[7];
static uint16_t ui_aggregate_handle;
static uint16_t handle = 0;

//static bd_addr_t public_pts_address = {0x00, 0x1B, 0xDC, 0x07, 0x32, 0xef};
static bd_addr_t public_pts_address = {0x23, 0x11, 0x23, 0x00, 0x76, 0xa1}; //iTag
static bd_addr_t public_pts_address2 = {0x0B, 0x3B, 0x22, 0xAC, 0x88, 0x20}; //A133模拟的le_counter0B:3B:22:AC:88:20
//static bd_addr_t public_pts_address = {0xe2, 0x9a, 0xd0, 0x68, 0x6d, 0x66};
//static bd_addr_t public_pts_address = {0xF7, 0xAA, 0x0D, 0xE2, 0x71, 0x16};

static int       public_pts_address_type = 0;
static int       public_pts_address_type2 = 0;

static bd_addr_t current_pts_address;
static int       current_pts_address_type;
static int       reconnection_address_set = 0;
static bd_addr_t our_private_address;

static uint16_t pts_signed_write_characteristic_uuid = 0xb00d;
static uint16_t pts_signed_write_characteristic_handle = 0x00b1;
static uint8_t signed_write_value[] = { 0x12 };

static central_state_t central_state = CENTRAL_IDLE;
static gatt_client_characteristic_t gap_name_characteristic;
static gatt_client_characteristic_t gap_reconnection_address_characteristic;
static gatt_client_characteristic_t gap_peripheral_privacy_flag_characteristic;
static gatt_client_characteristic_t signed_write_characteristic;
static gatt_client_service_t        service;

static btstack_packet_callback_registration_t hci_event_callback_registration;

static void show_usage(void);
///

static void printUUID(uint8_t * uuid128, uint16_t uuid16){
    if (uuid16){
        printf("%04x",uuid16);
    } else {
        printf("%s", uuid128_to_str(uuid128));
    }
}

static const char * att_errors[] = {
    "OK",
    "Invalid Handle",
    "Read Not Permitted",
    "Write Not Permitted",
    "Invalid PDU",
    "Insufficient Authentication",
    "Request No Supported",
    "Invalid Offset",
    "Insufficient Authorization",
    "Prepare Queue Full",
    "Attribute Not Found",
    "Attribute Not Long",
    "Insufficient Encryption Size",
    "Invalid Attribute Value Length",
    "Unlikely Error",
    "Insufficient Encryption",
    "Unsupported Group Type",
    "Insufficient Resource"
};
static const char * att_error_reserved = "Reserved";
static const char * att_error_application = "Application Error";
static const char * att_error_common_error = "Common Profile and Service Error Codes";
static const char * att_error_timeout = "Timeout";

static const char * att_error_string_for_code(uint8_t code){
    if (code >= 0xe0) return att_error_common_error;
    if (code >= 0xa0) return att_error_reserved;
    if (code >= 0x80) return att_error_application;
    if (code == 0x7f) return att_error_timeout;
    if (code >= 0x12) return att_error_reserved;
    return att_errors[ code];
}

const char * ad_event_types[] = {
    "Connectable undirected advertising",
    "Connectable directed advertising",
    "Scannable undirected advertising",
    "Non connectable undirected advertising",
    "Scan Response"
};

static void handle_advertising_event(uint8_t * packet, int size){
    // filter PTS
    bd_addr_t addr;
    gap_event_advertising_report_get_address(packet, addr);
    uint8_t addr_type = gap_event_advertising_report_get_address_type(packet);
    // always request address resolution
    sm_address_resolution_lookup(addr_type, addr);

    // ignore advertisement from devices other than pts
    // if (memcmp(addr, current_pts_address, 6)) return;
    uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet);
    printf("Advertisement: %s - %s, ", bd_addr_to_str(addr), ad_event_types[adv_event_type]);
    int adv_size = gap_event_advertising_report_get_data_length(packet);
    const uint8_t * adv_data = gap_event_advertising_report_get_data(packet);

    // check flags
    ad_context_t context;
    for (ad_iterator_init(&context, adv_size, (uint8_t *)adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){
        uint8_t data_type = ad_iterator_get_data_type(&context);
        // uint8_t size      = ad_iterator_get_data_len(&context);
        const uint8_t * data = ad_iterator_get_data(&context);
        switch (data_type){
            case 1: // AD_FLAGS
                if (*data & 1) printf("LE Limited Discoverable Mode, ");
                if (*data & 2) printf("LE General Discoverable Mode, ");
                break;
            default:
                break;
        }
    }

    // dump data
    printf("Data: ");
    printf_hexdump(adv_data, adv_size);
}

static uint8_t gap_adv_type(void){
    // if (gap_scannable) return 0x02;
    // if (gap_directed_connectable) return 0x01;
    if (!gap_connectable) return 0x03;
    return 0x00;
}

static void update_advertisment_params(void){
    uint8_t adv_type = gap_adv_type();
    printf("GAP: Connectable = %u -> advertising_type %u (%s)\n", gap_connectable, adv_type, ad_event_types[adv_type]);
    bd_addr_t null_addr;
    memset(null_addr, 0, 6);
    uint16_t adv_int_min = 0x800;
    uint16_t adv_int_max = 0x800;
    switch (adv_type){
        case 0:
        case 2:
        case 3:
            gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
            break;
        case 1:
        case 4:
            gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, public_pts_address_type, public_pts_address, 0x07, 0x00);
            break;
        }
}

static void gap_run(void){
    if (!hci_can_send_command_packet_now()) return;
}

static void app_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    uint16_t aHandle;

    switch (packet_type) {

        case HCI_EVENT_PACKET:
            switch (packet[0]) {

                case BTSTACK_EVENT_STATE:
                    // bt stack activated, get started
                    if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
                        printf("Central test ready\n");
                        show_usage();
                        gap_run();
                    }
                    break;

                case HCI_EVENT_LE_META:
                    switch (hci_event_le_meta_get_subevent_code(packet)) {
                        case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
                            handle = little_endian_read_16(packet, 4);
                            printf("Connection complete, handle 0x%04x\n", handle);
                            break;

                        default:
                            break;
                    }
                    break;

                case HCI_EVENT_DISCONNECTION_COMPLETE:
                    aHandle = little_endian_read_16(packet, 3);
                    printf("Disconnected from handle 0x%04x\n", aHandle);
                    break;

                case GAP_EVENT_ADVERTISING_REPORT:
                    handle_advertising_event(packet, size);
                    break;

                default:
                    break;
            }
    }
    gap_run();
}

static void use_public_pts_address(void){
    memcpy(current_pts_address, public_pts_address, 6);
    current_pts_address_type = public_pts_address_type;
}

static void extract_service(gatt_client_service_t * aService, uint8_t * data){
    aService->start_group_handle = little_endian_read_16(data, 0);
    aService->end_group_handle   = little_endian_read_16(data, 2);
    aService->uuid16 = 0;
    reverse_128(&data[4], aService->uuid128);
    if (uuid_has_bluetooth_prefix(aService->uuid128)){
        aService->uuid16 = big_endian_read_32(aService->uuid128, 0);
    }
}

static void extract_characteristic(gatt_client_characteristic_t * characteristic, uint8_t * packet){
    characteristic->start_handle = little_endian_read_16(packet, 4);
    characteristic->value_handle = little_endian_read_16(packet, 6);
    characteristic->end_handle =   little_endian_read_16(packet, 8);
    characteristic->properties =   little_endian_read_16(packet, 10);
    characteristic->uuid16 = 0;
    reverse_128(&packet[12], characteristic->uuid128);
    if (uuid_has_bluetooth_prefix(characteristic->uuid128)){
        characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
    }
}

static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){

    if (packet_type != HCI_EVENT_PACKET) return;

    uint8_t address_type;
    bd_addr_t flipped_address;
    gatt_client_characteristic_t characteristic;
    uint8_t *           value;
    uint16_t            value_handle;
    uint16_t            value_length;
    uint16_t            value_offset;
    uint8_t             status;

    switch(packet[0]){
        case GATT_EVENT_SERVICE_QUERY_RESULT:
            switch (central_state){
                case CENTRAL_W4_PRIMARY_SERVICES:
                case CENTRAL_ENTER_SERVICE_UUID_4_DISCOVER_CHARACTERISTICS:
                    extract_service(&service, &packet[4]);
                    printf("Primary Service with UUID ");
                    printUUID(service.uuid128, service.uuid16);
                    printf(", start group handle 0x%04x, end group handle 0x%04x\n", service.start_group_handle, service.end_group_handle);
                    break;
                    extract_service(&service, &packet[4]);
                    printf("Primary Service with UUID ");
                    printUUID(service.uuid128, service.uuid16);
                    printf(", start group handle 0x%04x, end group handle 0x%04x\n", service.start_group_handle, service.end_group_handle);
                    break;
                default:
                    break;
                }
            break;
        case GATT_EVENT_INCLUDED_SERVICE_QUERY_RESULT:
            value_handle = little_endian_read_16(packet, 4);
            extract_service(&service, &packet[6]);
            printf("Included Service at 0x%04x: ", value_handle);
            printf("start group handle 0x%04x, end group handle 0x%04x with UUID ", service.start_group_handle, service.end_group_handle);
            printUUID(service.uuid128, service.uuid16);
            printf("\n");
            break;
        case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
            extract_characteristic(&characteristic, packet);
            switch (central_state) {
                case CENTRAL_W4_NAME_QUERY_COMPLETE:
                    gap_name_characteristic = characteristic;
                    printf("GAP Name Characteristic found, value handle: 0x04%x\n", gap_name_characteristic.value_handle);
                    break;
                case CENTRAL_W4_RECONNECTION_ADDRESS_QUERY_COMPLETE:
                    gap_reconnection_address_characteristic = characteristic;
                    printf("GAP Reconnection Address Characteristic found, value handle: 0x04%x\n", gap_reconnection_address_characteristic.value_handle);
                    break;
                case CENTRAL_W4_PERIPHERAL_PRIVACY_FLAG_QUERY_COMPLETE:
                    gap_peripheral_privacy_flag_characteristic = characteristic;
                    printf("GAP Peripheral Privacy Flag Characteristic found, value handle: 0x04%x\n", gap_peripheral_privacy_flag_characteristic.value_handle);
                    break;
                case CENTRAL_W4_SIGNED_WRITE_QUERY_COMPLETE:
                    signed_write_characteristic = characteristic;
                    printf("Characteristic for Signed Write found, value handle: 0x%04x\n", signed_write_characteristic.value_handle);
                    break;
                case CENTRAL_W4_CHARACTERISTICS:
                    printf("Characteristic found at 0x%04x with value handle 0x%04x, uuid ", characteristic.start_handle, characteristic.value_handle);
                    if (characteristic.uuid16){
                        printf("%04x\n", characteristic.uuid16);
                    } else {
                        printf_hexdump(characteristic.uuid128, 16);
                    }
                    break;
                case CENTRAL_GPA_W4_RESPONSE2:
                    switch (ui_uuid16){
                        case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
                        case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
                            ui_attribute_handle = characteristic.value_handle;
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            break;
        case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT: {
            uint16_t descriptor_handle = little_endian_read_16(packet, 4);
            uint8_t uuid128[16];
            reverse_128(&packet[6], uuid128);
            if (uuid_has_bluetooth_prefix(uuid128)){
                printf("Characteristic descriptor at 0x%04x with UUID %04x\n", descriptor_handle, big_endian_read_32(uuid128, 0));
            } else {
                printf("Characteristic descriptor at 0x%04x with UUID %s\n", descriptor_handle, uuid128_to_str(uuid128));
            }
            break;
        }
        case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
            value_handle = little_endian_read_16(packet, 4);
            value_length = little_endian_read_16(packet, 6);
            value = &packet[8];
            switch (central_state){
                case CENTRAL_W4_NAME_VALUE:
                    value[value_length] = 0;
                    printf("GAP Service: Device Name: %s\n", value);
                    break;
                case CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_HANDLE:
                case CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_UUID:
                case CENTRAL_W4_READ_MULTIPLE_CHARACTERISTIC_VALUES:
                    printf("Value: ");
                    printf_hexdump(value, value_length);
                    break;
                case CENTRAL_GPA_W4_RESPONSE:
                    switch (ui_uuid16){
                        case GATT_PRIMARY_SERVICE_UUID:
                            printf ("Attribute handle 0x%04x, primary service 0x%04x\n", value_handle, little_endian_read_16(value,0));
                            break;
                        case GATT_SECONDARY_SERVICE_UUID:
                            printf ("Attribute handle 0x%04x, secondary service 0x%04x\n", value_handle, little_endian_read_16(value,0));
                            break;
                        case GATT_INCLUDE_SERVICE_UUID:
                            printf ("Attribute handle 0x%04x, included service attribute handle 0x%04x, end group handle 0x%04x, uuid %04x\n",
                             value_handle, little_endian_read_16(value,0), little_endian_read_16(value,2), little_endian_read_16(value,4));
                            break;
                        case GATT_CHARACTERISTICS_UUID:
                            printf ("Attribute handle 0x%04x, properties 0x%02x, value handle 0x%04x, uuid ",
                             value_handle, value[0], little_endian_read_16(value,1));
                            if (value_length < 19){
                                printf("%04x\n", little_endian_read_16(value, 3));
                            } else {
                                uint8_t uuid128[16];
                                reverse_128(&value[3], uuid128);
                                printf("%s\n", uuid128_to_str(uuid128));
                            }
                            break;
                        case GATT_CHARACTERISTIC_EXTENDED_PROPERTIES:
                            printf ("Attribute handle 0x%04x, gatt characteristic properties 0x%04x\n", value_handle, little_endian_read_16(value,0));
                            break;
                        case GATT_CHARACTERISTIC_USER_DESCRIPTION:
                            // go the value, but PTS 6.3 requires another request
                            printf("Read by type request received, store attribute handle for read request\n");
                            ui_attribute_handle = value_handle;
                            break;
                        case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION:
                            printf ("Attribute handle 0x%04x, gatt client characteristic configuration 0x%04x\n", value_handle, little_endian_read_16(value,0));
                            break;
                        case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
                            ui_handles_count = value_length >> 1;
                            printf ("Attribute handle 0x%04x, gatt characteristic aggregate format. Handles: ", value_handle);
                            for (ui_handles_index = 0; ui_handles_index < ui_handles_count ; ui_handles_index++){
                                ui_handles[ui_handles_index] = little_endian_read_16(value, (ui_handles_index << 1));
                                printf("0x%04x, ", ui_handles[ui_handles_index]);
                            }
                            printf("\n");
                            ui_handles_index = 0;
                            ui_aggregate_handle = value_handle;
                            break;
                        case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
                            printf("Presentation format: ");
                            printf_hexdump(value, value_length);
                            memcpy(ui_presentation_format, value, 7);
                            break;
                        default:
                            printf("Value: ");
                            printf_hexdump(value, value_length);
                            break;
                    }
                    break;
                case CENTRAL_GPA_W4_RESPONSE3:
                    switch (ui_uuid16){
                        case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
                            printf("Value: ");
                            printf_hexdump(value, value_length);
                            printf("Format 0x%02x, Exponent 0x%02x, Unit 0x%04x\n",
                                ui_presentation_format[0], ui_presentation_format[1], little_endian_read_16(ui_presentation_format, 2));
                            break;
                        case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
                            printf("Aggregated value: ");
                            printf_hexdump(value, value_length);
                            memcpy(ui_value_data, value, value_length);
                            ui_value_pos = 0;
                            central_state = CENTRAL_GPA_W4_RESPONSE4;
                        default:
                            break;
                    }
                    break;
               default:
                    break;
            }
            break;
        case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
            value = &packet[10];
            value_offset = little_endian_read_16(packet, 6);
            value_length = little_endian_read_16(packet, 8);
            central_state = CENTRAL_IDLE;
            printf("Value (offset %02u): ", value_offset);
            printf_hexdump(value, value_length);
            break;
        case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
            value_handle = little_endian_read_16(packet, 4);
            value_length = little_endian_read_16(packet, 6);
            value = &packet[8];
            switch (central_state){
                case CENTRAL_GPA_W4_RESPONSE2:
                    switch (ui_uuid16){
                        case GATT_CHARACTERISTIC_USER_DESCRIPTION:
                            value[value_length] = 0;
                            printf ("Attribute handle 0x%04x, characteristic user descriptor: %s\n", value_handle, value);
                            break;
                        default:
                            break;
                    }
                    break;
                case CENTRAL_GPA_W4_RESPONSE4:
                    // only characteristic aggregate format
                    printf("Value: ");
                    printf_hexdump(&ui_value_data[ui_value_pos], gpa_format_type_len[value[0]]);
                    ui_value_pos +=  gpa_format_type_len[value[0]];
                    printf("Format 0x%02x, Exponent 0x%02x, Unit 0x%04x\n",
                        value[0], value[1], little_endian_read_16(value, 2));
                    break;
                default:
                    printf("Value: ");
                    printf_hexdump(value, value_length);
                    break;
            }
            break;
        case GATT_EVENT_LONG_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
            value = &packet[10];
            value_offset = little_endian_read_16(packet, 6);
            value_length = little_endian_read_16(packet, 8);
            printf("Value (offset %02u): ", value_offset);
            printf_hexdump(value, value_length);
            break;

        case GATT_EVENT_QUERY_COMPLETE:
            status = packet[4];
            if (status){
                central_state = CENTRAL_IDLE;
                printf("GATT_EVENT_QUERY_COMPLETE: %s 0x%02x\n",
                att_error_string_for_code(status),  status);
                break;
            }
            switch (central_state){
                case CENTRAL_W4_NAME_QUERY_COMPLETE:
                    central_state = CENTRAL_W4_NAME_VALUE;
                    gatt_client_read_value_of_characteristic(handle_gatt_client_event, handle, &gap_name_characteristic);
                    break;
                case CENTRAL_W4_RECONNECTION_ADDRESS_QUERY_COMPLETE:
                    central_state = CENTRAL_IDLE;
                    gap_le_get_own_address(&address_type, our_private_address);
                    printf("Our private address: %s\n", bd_addr_to_str(our_private_address));
                    reverse_bd_addr(our_private_address, flipped_address);
                    gatt_client_write_value_of_characteristic(handle_gatt_client_event, handle, gap_reconnection_address_characteristic.value_handle, 6, flipped_address);
                    reconnection_address_set = 1;
#ifdef PTS_USES_RECONNECTION_ADDRESS_FOR_ITSELF
                    memcpy(current_pts_address, our_private_address, 6);
                    current_pts_address_type = 1;
#endif
                    break;
                case CENTRAL_W4_PERIPHERAL_PRIVACY_FLAG_QUERY_COMPLETE:
                    central_state = CENTRAL_IDLE;
                    switch (pts_privacy_flag){
                        case 0:
                            use_public_pts_address();
                            printf("Peripheral Privacy Flag set to FALSE, connecting to public PTS address again\n");
                            gatt_client_write_value_of_characteristic(handle_gatt_client_event, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag);
                            break;
                        case 1:
                            printf("Peripheral Privacy Flag set to TRUE\n");
                            gatt_client_write_value_of_characteristic(handle_gatt_client_event, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag);
                            break;
                        default:
                            break;
                        }
                    break;
                case CENTRAL_W4_SIGNED_WRITE_QUERY_COMPLETE:
                    printf("Signed write on Characteristic with UUID 0x%04x\n", pts_signed_write_characteristic_uuid);
                    gatt_client_signed_write_without_response(handle_gatt_client_event, handle, signed_write_characteristic.value_handle, sizeof(signed_write_value), signed_write_value);
                    break;
                case CENTRAL_W4_PRIMARY_SERVICES:
                    printf("Primary Service Discovery complete\n");
                    central_state = CENTRAL_IDLE;
                    break;
                case CENTRAL_ENTER_SERVICE_UUID_4_DISCOVER_CHARACTERISTICS:
                    gatt_client_discover_characteristics_for_service(handle_gatt_client_event, handle, &service);
                    central_state =  CENTRAL_W4_CHARACTERISTICS;
                    break;
                case CENTRAL_GPA_W4_RESPONSE:
                    switch (ui_uuid16){
                        case GATT_CHARACTERISTIC_USER_DESCRIPTION:
                            central_state = CENTRAL_GPA_W4_RESPONSE2;
                            printf("Sending Read Characteristic Descriptor at 0x%04x\n", ui_attribute_handle);
                            gatt_client_read_characteristic_descriptor_using_descriptor_handle(handle_gatt_client_event, handle, ui_attribute_handle);
                            break;
                        case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
                        case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
                            {
                                printf("Searching Characteristic Declaration\n");
                                central_state = CENTRAL_GPA_W4_RESPONSE2;
                                gatt_client_service_t aService;
                                aService.start_group_handle = ui_start_handle;
                                aService.end_group_handle   = ui_end_handle;
                                gatt_client_discover_characteristics_for_service(handle_gatt_client_event, handle, &aService);
                                break;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case CENTRAL_GPA_W4_RESPONSE2:
                    switch(ui_uuid16){
                        case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
                        case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
                            printf("Reading characteristic value at 0x%04x\n", ui_attribute_handle);
                            central_state = CENTRAL_GPA_W4_RESPONSE3;
                            gatt_client_read_value_of_characteristic_using_value_handle(handle_gatt_client_event, handle, ui_attribute_handle);
                            break;
                        default:
                            break;
                    }
                    break;
                case CENTRAL_GPA_W4_RESPONSE4:
                    // so far, only GATT_CHARACTERISTIC_AGGREGATE_FORMAT
                    if (ui_handles_index < ui_handles_count) {
                        printf("Reading Characteristic Presentation Format at 0x%04x\n", ui_handles[ui_handles_index]);
                        gatt_client_read_characteristic_descriptor_using_descriptor_handle(handle_gatt_client_event, handle, ui_handles[ui_handles_index]);
                        ui_handles_index++;
                        break;
                    }
                    if (ui_handles_index == ui_handles_count ) {
                        // PTS rqequires to read the characteristic aggregate descriptor again (no idea why)
                        gatt_client_read_value_of_characteristic_using_value_handle(handle_gatt_client_event, handle, ui_aggregate_handle);
                        ui_handles_index++;
                    }
                    break;
                default:
                    central_state = CENTRAL_IDLE;
                    break;
            }
            break;
        case GATT_EVENT_NOTIFICATION:
            value_handle = little_endian_read_16(packet, 4);
            value_length = little_endian_read_16(packet, 6);
            value = &packet[8];
            printf("Notification handle 0x%04x, value: ", value_handle);
            printf_hexdump(value, value_length);
            break;
        case GATT_EVENT_INDICATION:
            value_handle = little_endian_read_16(packet, 4);
            value_length = little_endian_read_16(packet, 6);
            value = &packet[8];
            printf("Indication handle 0x%04x, value: ", value_handle);
            printf_hexdump(value, value_length);
            break;
        default:
            break;
    }
}

uint16_t value_handle = 1;
uint16_t attribute_size = 1;
int scanning_active = 0;

int num_rows = 0;
int num_lines = 0;
const char * rows[100];
const char * lines[100];
const char * empty_string = "";
const int width = 70;

static void reset_screen(void){
    // free memory
    int i = 0;
    for (i=0;i<num_rows;i++) {
        free((void*)rows[i]);
        rows[i] = NULL;
    }
    num_rows = 0;
    for (i=0;i<num_lines;i++) {
        free((void*)lines[i]);
        lines[i] = NULL;
    }
    num_lines = 0;
}

static void print_line(const char * format, ...){
    va_list argptr;
    va_start(argptr, format);
    char * line = malloc(80);
    vsnprintf(line, 80, format, argptr);
    va_end(argptr);
    lines[num_lines] = line;
    num_lines++;
}

static void printf_row(const char * format, ...){
    va_list argptr;
    va_start(argptr, format);
    char * row = malloc(80);
    vsnprintf(row, 80, format, argptr);
    va_end(argptr);
    rows[num_rows] = row;
    num_rows++;
}

static void print_screen(void){

    // clear screen
    printf("\e[1;1H\e[2J");

    // full lines on top
    int i;
    for (i=0;i<num_lines;i++){
        printf("%s\n", lines[i]);
    }
    printf("\n");

    // two columns
    int second_half = (num_rows + 1) / 2;
    for (i=0;i<second_half;i++){
        int pos = strlen(rows[i]);
        printf("%s", rows[i]);
        while (pos < width){
            printf(" ");
            pos++;
        }
        if (i + second_half < num_rows){
            printf("|  %s", rows[i+second_half]);
        }
        printf("\n");
    }
    printf("\n");
}

static void show_usage(void){
    uint8_t iut_address_type;
    bd_addr_t      iut_address;
    gap_le_get_own_address(&iut_address_type, iut_address);

    reset_screen();

    print_line("--- CLI for LE Central ---");
    print_line("PTS: addr type %u, addr %s", current_pts_address_type, bd_addr_to_str(current_pts_address));
    print_line("IUT: addr type %u, addr %s", iut_address_type, bd_addr_to_str(iut_address));
    print_line("--------------------------");
    print_line("GAP: connectable %u, bondable %u", gap_connectable, gap_bondable);
    print_line("SM: %s, MITM protection %u", sm_io_capabilities, sm_mitm_protection);
    print_line("SM: key range [%u..16], OOB data: %s", sm_min_key_size,
        sm_have_oob_data ? (sm_have_oob_data == 1 ? (const char*) sm_oob_data_A : (const char*) sm_oob_data_B) : "None");
    print_line("Privacy %u", gap_privacy);
    print_line("Device name: %s", gap_device_name);

    printf_row("c/C - connectable off");
    printf_row("d/D - bondable off/on");
    printf_row("---");
    printf_row("1   - enable privacy using random non-resolvable private address");
    printf_row("2   - clear Peripheral Privacy Flag on PTS");
    printf_row("3   - set Peripheral Privacy Flag on PTS");
    printf_row("9   - create HCI Classic connection to addr %s", bd_addr_to_str(public_pts_address));
    printf_row("s/S - passive/active scanning");
    printf_row("a   - enable Advertisements");
    printf_row("b   - start bonding");
    printf_row("n   - query GAP Device Name");
    printf_row("o   - set GAP Reconnection Address");
    printf_row("t   - terminate connection, stop connecting");
    printf_row("p   - auto connect to PTS");
    printf_row("P   - direct connect to PTS");
    printf_row("w   - signed write on characteristic with UUID %04x", pts_signed_write_characteristic_uuid);
    printf_row("W   - signed write on attribute with handle 0x%04x and value 0x12", pts_signed_write_characteristic_handle);
    printf_row("z   - Update L2CAP Connection Parameters");
    printf_row("---");
    printf_row("e   - Discover all Primary Services");
    printf_row("f/F - Discover Primary Service by UUID16/UUID128");
    printf_row("g   - Discover all characteristics by UUID16");
    printf_row("G   - Discover all characteristics in range");
    printf_row("h   - Discover Characteristic Descriptors");
    printf_row("i   - Find all included services");
    printf_row("j/J - Read (Long) Characteristic Value by handle");
    printf_row("k/K - Read Characteristic Value by UUID16/UUID128");
    printf_row("l/L - Read (Long) Characteristic Descriptor by handle");
    printf_row("N   - Read Multiple Characteristic Values");
    printf_row("O   - Write without Response");
    printf_row("q/Q - Write (Long) Characteristic Value");
    printf_row("r   - Characteristic Reliable Write");
    printf_row("R   - Signed Write");
    printf_row("u/U - Write (Long) Characteristic Descriptor");
    printf_row("T   - Read Generic Profile Attributes by Type");
    printf_row("E   - Prepare Write");
    printf_row("v   - Execute Write");
    printf_row("V   - Cancel Write");
    printf_row("---");
    printf_row("4   - IO_CAPABILITY_DISPLAY_ONLY");
    printf_row("5   - IO_CAPABILITY_DISPLAY_YES_NO");
    printf_row("6   - IO_CAPABILITY_NO_INPUT_NO_OUTPUT");
    printf_row("7   - IO_CAPABILITY_KEYBOARD_ONLY");
    printf_row("8   - IO_CAPABILITY_KEYBOARD_DISPLAY");
    printf_row("m/M - MITM protection off");
    printf_row("x/X - encryption key range [7..16]/[16..16]");
    printf_row("y/Y - OOB data off/on/toggle A/B");
    printf_row("---");
    printf_row("Ctrl-c - exit");

    print_screen();
}



static int hexForChar(char c){
    if (c >= '0' && c <= '9'){
        return c - '0';
    }
    if (c >= 'a' && c <= 'f'){
        return c - 'a' + 10;
    }
    if (c >= 'A' && c <= 'F'){
        return c - 'A' + 10;
    }
    return -1;
}

static void ui_request_uint16(const char * message){
    printf("%s", message);
    fflush(stdout);
    ui_uint16_request = 1;
    ui_uint16 = 0;
    ui_uint16_pos = 0;
}


static void ui_request_data(const char * message){
    printf("%s", message);
    fflush(stdout);
    ui_value_request = 1;
    ui_value_pos = 0;
    memset(ui_value_data, 0, sizeof(ui_value_data));
}

static int ui_process_digits_for_passkey(char buffer){
    if (buffer < '0' || buffer > '9') {
        return 0;
    }
    printf("%c", buffer);
    fflush(stdout);
    ui_passkey = ui_passkey * 10 + buffer - '0';
    ui_digits_for_passkey--;
    if (ui_digits_for_passkey == 0){
        printf("\nSending Passkey %u (0x%x)\n", ui_passkey, ui_passkey);
        sm_passkey_input(handle, ui_passkey);
    }
    return 0;
}

static int ui_process_uint16_request(char buffer){
    if (buffer == 0x7f || buffer == 0x08) {
        if (ui_uint16_pos){
            printf("\b \b");
            fflush(stdout);
            ui_uint16 >>= 4;
            ui_uint16_pos--;
        }
        return 0;
    }
    if (buffer == '\n' || buffer == '\r'){
        ui_uint16_request = 0;
        printf("\n");
        switch (central_state){
            case CENTRAL_W4_PRIMARY_SERVICES:
                printf("Discover Primary Services with UUID16 %04x\n", ui_uint16);
                gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, handle, ui_uint16);
                return 0;
            case CENTRAL_ENTER_SERVICE_UUID_4_DISCOVER_CHARACTERISTICS:
                printf("Discover Primary Services with UUID16 %04x\n", ui_uint16);
                gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, handle, ui_uint16);
                return 0;
            case CENTRAL_ENTER_START_HANDLE_4_DISCOVER_CHARACTERISTICS:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter end handle: ");
                central_state = CENTRAL_ENTER_END_HANDLE_4_DISCOVER_CHARACTERISTICS;
                return 0;
            case CENTRAL_ENTER_END_HANDLE_4_DISCOVER_CHARACTERISTICS: {
                printf("Discover Characteristics from 0x%04x to 0x%04x\n", ui_attribute_handle, ui_uint16);
                central_state = CENTRAL_W4_CHARACTERISTICS;
                gatt_client_service_t aService;
                aService.start_group_handle = ui_attribute_handle;
                aService.end_group_handle   = ui_uint16;
                gatt_client_discover_characteristics_for_service(handle_gatt_client_event, handle, &aService);
                return 0;
            }
            case CENTRAL_W4_CHARACTERISTICS:
                printf("Discover Characteristics with UUID16 %04x\n", ui_uint16);
                gatt_client_discover_characteristics_for_handle_range_by_uuid16(handle_gatt_client_event, handle, 0x0001, 0xffff, ui_uint16);
                return 0;
            case CENTRAL_W4_DISCOVER_CHARACTERISTIC_DESCRIPTORS: {
                gatt_client_characteristic_t characteristic;
                characteristic.value_handle = ui_uint16 - 1;
                characteristic.end_handle = ui_uint16;
                gatt_client_discover_characteristic_descriptors(handle_gatt_client_event, handle, &characteristic);
                break;
            }
            case CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_HANDLE:
                printf("Read Characteristic Value with handle 0x%04x\n", ui_uint16);
                gatt_client_read_value_of_characteristic_using_value_handle(handle_gatt_client_event, handle, ui_uint16);
                return 0;
            case CENTRAL_ENTER_OFFSET_4_READ_LONG_CHARACTERISTIC_VALUE_BY_HANDLE:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter long value offset: ");
                central_state = CENTRAL_W4_READ_LONG_CHARACTERISTIC_VALUE_BY_HANDLE;
                return 0;
            case CENTRAL_W4_READ_LONG_CHARACTERISTIC_VALUE_BY_HANDLE:
                printf("Read Long Characteristic Value with handle 0x%04x, offset 0x%04x\n", ui_attribute_handle, ui_uint16);
                gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(handle_gatt_client_event, handle, ui_attribute_handle, ui_uint16);
                return 0;
            case CENTRAL_W4_READ_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE:
                printf("Read Characteristic Descriptor with handle 0x%04x\n", ui_uint16);
                gatt_client_read_characteristic_descriptor_using_descriptor_handle(handle_gatt_client_event, handle, ui_uint16);
                return 0;
            case CENTRAL_ENTER_OFFSET_4_READ_LONG_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter long characteristic offset: ");
                central_state = CENTRAL_W4_READ_LONG_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE;
                return 0;
            case CENTRAL_W4_READ_LONG_CHARACTERISTIC_DESCRIPTOR_BY_HANDLE:
                printf("Read Long Characteristic Descriptor with handle 0x%04x, offset 0x%04x\n", ui_attribute_handle, ui_uint16);
                gatt_client_read_long_characteristic_descriptor_using_descriptor_handle_with_offset(handle_gatt_client_event, handle, ui_attribute_handle, ui_uint16);
                return 0;
            case CENTRAL_ENTER_HANDLE_4_READ_CHARACTERISTIC_VALUE_BY_UUID:
                ui_uuid16 = ui_uint16;
                ui_request_uint16("Please enter start handle: ");
                central_state = CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_UUID;
                return 0;
            case CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_UUID:
                printf("Read Characteristic Value with UUID16 0x%04x\n", ui_uint16);
                gatt_client_read_value_of_characteristics_by_uuid16(handle_gatt_client_event, handle, ui_uint16, 0xffff, ui_uuid16);
                return 0;
            case CENTRAL_W4_READ_MULTIPLE_CHARACTERISTIC_VALUES:
                if (ui_uint16){
                    ui_handles[ui_handles_count++] = ui_uint16;
                    ui_request_uint16("Please enter handle: ");
                } else {
                    int i;
                    printf("Read multiple values, handles: ");
                    for (i=0;i<ui_handles_count;i++){
                        printf("0x%04x, ", ui_handles[i]);
                    }
                    printf("\n");
                    gatt_client_read_multiple_characteristic_values(handle_gatt_client_event, handle, ui_handles_count, ui_handles);
                }
                return 0;

            case CENTRAL_ENTER_HANDLE_4_WRITE_LONG_CHARACTERISTIC_VALUE:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter offset: ");
                central_state = CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_VALUE;
                return 0;
            case CENTRAL_ENTER_HANDLE_4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter offset: ");
                central_state = CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR;
                return 0;
            case CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_VALUE:
            case CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR:
                ui_attribute_offset = ui_uint16;
                ui_request_data("Please enter data: ");
                return 0;
            case CENTRAL_W4_WRITE_WITHOUT_RESPONSE:
            case CENTRAL_W4_WRITE_CHARACTERICISTIC_VALUE:
            case CENTRAL_W4_RELIABLE_WRITE:
            case CENTRAL_W4_WRITE_CHARACTERISTIC_DESCRIPTOR:
            case CENTRAL_W4_SIGNED_WRITE:
                ui_attribute_handle = ui_uint16;
                ui_request_data("Please enter data: ");
                return 0;
            case CENTRAL_W4_ENTER_OFFSET_4_PREPARE_WRITE:
                ui_attribute_offset = ui_uint16;
                ui_request_data("Please enter data: ");
                return 0;
            case CENTRAL_W4_ENTER_HANDLE_4_PREPARE_WRITE:
                ui_attribute_handle = ui_uint16;
                ui_request_uint16("Please enter offset: ");
                central_state = CENTRAL_W4_ENTER_OFFSET_4_PREPARE_WRITE;
                return 0;
            case CENTRAL_GPA_ENTER_START_HANDLE:
                ui_start_handle = ui_uint16;
                central_state = CENTRAL_GPA_ENTER_END_HANDLE;
                ui_request_uint16("Please enter end handle: ");
                return 0;
            case CENTRAL_GPA_ENTER_END_HANDLE:
                ui_end_handle = ui_uint16;
                central_state = CENTRAL_GPA_W4_RESPONSE;
                ui_request_uint16("Please enter uuid: ");
                return 0;
            case CENTRAL_GPA_W4_RESPONSE:
                ui_uuid16 = ui_uint16;
                printf("Read by type: range 0x%04x-0x%04x, uuid %04x\n", ui_start_handle, ui_end_handle, ui_uuid16);
                gatt_client_read_value_of_characteristics_by_uuid16(handle_gatt_client_event, handle, ui_start_handle, ui_end_handle, ui_uuid16);
                return 0;
            default:
                return 0;
        }
    }
    int hex = hexForChar(buffer);
    if (hex < 0){
        return 0;
    }
    printf("%c", buffer);
    fflush(stdout);
    ui_uint16 = ui_uint16 << 4 | hex;
    ui_uint16_pos++;
    return 0;
}

static int uuid128_pos_starts_with_dash(int pos){
    switch(pos){
        case 8:
        case 12:
        case 16:
        case 20:
#ifdef PTS_UUID128_REPRESENTATION
        case 4:
        case 24:
#endif
            return 1;
        default:
            return 0;
    }
}

static int ui_process_uuid128_request(char buffer){
    if (buffer == '-') return 0;    // skip -

    if (buffer == 0x7f || buffer == 0x08) {
        if (ui_uuid128_pos){
            if (uuid128_pos_starts_with_dash(ui_uuid128_pos)){
                printf("\b \b");
                fflush(stdout);
            }
            printf("\b \b");
            fflush(stdout);
            ui_uuid128_pos--;
        }
        return 0;
    }

    int hex = hexForChar(buffer);
    if (hex < 0){
        return 0;
    }
    printf("%c", buffer);
    fflush(stdout);
    if (ui_uuid128_pos & 1){
        ui_uuid128[ui_uuid128_pos >> 1] = (ui_uuid128[ui_uuid128_pos >> 1] & 0xf0) | hex;
    } else {
        ui_uuid128[ui_uuid128_pos >> 1] = hex << 4;
    }
    ui_uuid128_pos++;
    if (ui_uuid128_pos == 32){
        ui_uuid128_request = 0;
        printf("\n");
        switch (central_state){
            case CENTRAL_W4_PRIMARY_SERVICES:
                printf("Discover Primary Services with UUID128 %s\n", uuid128_to_str(ui_uuid128));
                gatt_client_discover_primary_services_by_uuid128(handle_gatt_client_event, handle, ui_uuid128);
                return 0;
            case CENTRAL_W4_READ_CHARACTERISTIC_VALUE_BY_UUID:
                printf("Read Characteristic Value with UUID128 %s\n", uuid128_to_str(ui_uuid128));
                gatt_client_read_value_of_characteristics_by_uuid128(handle_gatt_client_event, handle, 0x0001, 0xffff, ui_uuid128);
                return 0;
            default:
                return 0;
        }
    }
    if (uuid128_pos_starts_with_dash(ui_uuid128_pos)){
        printf("-");
        fflush(stdout);
    }
    return 0;
}

static void ui_announce_write(const char * method){
    printf("Request: %s handle 0x%04x data: ", method, ui_uint16);
    printf_hexdump(ui_value_data, ui_value_pos >> 1);
    printf("\n");
}

static int ui_process_data_request(char buffer){
    if (buffer == 0x7f || buffer == 0x08) {
        if (ui_value_pos){
            if ((ui_value_pos & 1) == 0){
                printf("\b");
            }
            printf("\b \b");
            fflush(stdout);
            ui_value_pos--;
        }
        return 0;
    }
    if (buffer == '\n' || buffer == '\r'){
        ui_value_request = 0;
        printf("\n");
        uint16_t value_len = ui_value_pos >> 1;
        switch (central_state){
            case CENTRAL_W4_WRITE_WITHOUT_RESPONSE:
                ui_announce_write("Write without response");
                gatt_client_write_value_of_characteristic_without_response(handle, ui_attribute_handle, value_len, ui_value_data);
                break;
            case CENTRAL_W4_WRITE_CHARACTERICISTIC_VALUE:
                ui_announce_write("Write Characteristic Value");
                gatt_client_write_value_of_characteristic(handle_gatt_client_event, handle, ui_attribute_handle, value_len, ui_value_data);
                break;
            case CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_VALUE:
                ui_announce_write("Write Long Characteristic Value");
                gatt_client_write_long_value_of_characteristic_with_offset(handle_gatt_client_event, handle, ui_attribute_handle, ui_attribute_offset, value_len, ui_value_data);
                break;
            case CENTRAL_W4_RELIABLE_WRITE:
                ui_announce_write("Reliabe Write");
                gatt_client_reliable_write_long_value_of_characteristic(handle_gatt_client_event, handle, ui_attribute_handle, value_len, ui_value_data);
                break;
            case CENTRAL_W4_WRITE_CHARACTERISTIC_DESCRIPTOR:
                ui_announce_write("Write Characteristic Descriptor");
                gatt_client_write_characteristic_descriptor_using_descriptor_handle(handle_gatt_client_event, handle, ui_attribute_handle, value_len, ui_value_data);
                break;
            case CENTRAL_W4_WRITE_LONG_CHARACTERISTIC_DESCRIPTOR:
                ui_announce_write("Write Long Characteristic Descriptor");
                gatt_client_write_long_characteristic_descriptor_using_descriptor_handle_with_offset(handle_gatt_client_event, handle, ui_attribute_handle, ui_attribute_offset, value_len, ui_value_data);
                break;
            case CENTRAL_W4_SIGNED_WRITE:
                ui_announce_write("Signed Write");
                gatt_client_signed_write_without_response(handle_gatt_client_event, handle, ui_attribute_handle, value_len, ui_value_data);
                break;
            case CENTRAL_W4_ENTER_OFFSET_4_PREPARE_WRITE:
                ui_announce_write("Preprare Write");
                gatt_client_prepare_write(handle_gatt_client_event, handle, ui_attribute_handle, ui_attribute_offset, value_len, ui_value_data);
                break;
            default:
                break;
        }
        return 0;
    }

    // ignore spaces
    if (buffer == ' ') return 0;

    int hex = hexForChar(buffer);
    if (hex < 0){
        return 0;
    }

    printf("%c", buffer);

    if (ui_value_pos & 1){
        ui_value_data[ui_value_pos >> 1] = (ui_value_data[ui_value_pos >> 1] & 0xf0) | hex;
        printf(" ");
    } else {
        ui_value_data[ui_value_pos >> 1] = hex << 4;
    }
    ui_value_pos++;

    fflush(stdout);
    return 0;
}

static void ui_process_command(char buffer){
    //int res;
    switch (buffer){
        case '1':
            printf("Enabling non-resolvable private address\n");
            gap_random_address_set_mode(GAP_RANDOM_ADDRESS_NON_RESOLVABLE);
            gap_privacy = 1;
            update_advertisment_params();
            show_usage();
            break;

        case 'a':
            handle = 0x12;
            printf("handle = 0x%x\n", handle);
            break;
        case 'b':
            handle = 0x13;
            printf("handle = 0x%x\n", handle);
            break;
        case 'P':
            memcpy(current_pts_address, public_pts_address, 6);
            current_pts_address_type = public_pts_address_type;
            gap_connect(current_pts_address, current_pts_address_type);
            printf("Direct Connection Establishment to type %u, addr %s\n", current_pts_address_type, bd_addr_to_str(current_pts_address));
            break;

        case 'A':
            memcpy(current_pts_address, public_pts_address2, 6);
            current_pts_address_type = public_pts_address_type2;
            gap_connect(current_pts_address, current_pts_address_type);
            printf("Direct Connection Establishment to type %u, addr %s\n", current_pts_address_type, bd_addr_to_str(current_pts_address));
            break;

        case 's':
            if (scanning_active){
                gap_stop_scan();
                scanning_active = 0;
                break;
            }
            printf("Start passive scanning\n");
            gap_set_scan_parameters(0, 48, 48);
            gap_start_scan();
            scanning_active = 1;
            break;
        case 'S':
            if (scanning_active){
                printf("Stop scanning\n");
                gap_stop_scan();
                scanning_active = 0;
                break;
            }
            printf("Start active scanning\n");
            gap_set_scan_parameters(1, 48, 48);
            gap_start_scan();
            scanning_active = 1;
            break;
        case 't':
            printf("Terminating connection\n");
            hci_send_cmd(&hci_disconnect, handle, 0x13);
            gap_auto_connection_stop_all();
            gap_connect_cancel();
            break;

        case 'O':
            central_state = CENTRAL_W4_WRITE_WITHOUT_RESPONSE;
            ui_request_uint16("Please enter handle: ");
            break;
        case 'q':
            central_state = CENTRAL_W4_WRITE_CHARACTERICISTIC_VALUE;
            ui_request_uint16("Please enter handle: ");
            break;
        case 'Q':
            central_state = CENTRAL_ENTER_HANDLE_4_WRITE_LONG_CHARACTERISTIC_VALUE;
            ui_request_uint16("Please enter handle: ");
            break;

        default:
            show_usage();
            break;
    }
}

static void stdin_process(char c){
    if (ui_digits_for_passkey){
        ui_process_digits_for_passkey(c);
        return;
    }

    if (ui_uint16_request){
        ui_process_uint16_request(c);
        return;
    }

    if (ui_uuid128_request){
        ui_process_uuid128_request(c);
        return;
    }

    if (ui_value_request){
        ui_process_data_request(c);
        return;
    }

    ui_process_command(c);

    return;
}

// 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 con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){

    printf("READ Callback, handle %04x, offset %u, buffer size %u\n", handle, offset, buffer_size);
    uint16_t  att_value_len;

    uint16_t uuid16 = att_uuid_for_handle(handle);
    switch (uuid16){
        case 0x2a00:
            att_value_len = strlen(gap_device_name);
            if (buffer) {
                memcpy(buffer, gap_device_name, att_value_len);
            }
            return att_value_len;
        default:
            break;
    }
    return 0;
}

int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){

    printf("BTstack LE Peripheral starting up...\n");

    memset(rows, 0, sizeof(char *) * 100);
    memset(lines, 0, sizeof(char *) * 100);

    strcpy(gap_device_name, "BTstack");

    // register for HCI Events
    hci_event_callback_registration.callback = &app_packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);

    // set up l2cap_le
    l2cap_init();

    // setup GATT Client
    gatt_client_init();

    // Setup ATT/GATT Server
    att_server_init(profile_data, att_read_callback, NULL);
    att_server_register_packet_handler(app_packet_handler);

    // Setup LE Device DB
    le_device_db_init();

    uint8_t pts_irk[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
    le_device_db_add(public_pts_address_type, public_pts_address, pts_irk);
    le_device_db_add(public_pts_address_type2, public_pts_address2, pts_irk);

    // set adv params
    update_advertisment_params();

    memcpy(current_pts_address, public_pts_address, 6);
    current_pts_address_type = public_pts_address_type;

    // classic discoverable / connectable
    gap_connectable_control(0);
    gap_discoverable_control(1);

    // allow foor terminal input
    btstack_stdin_setup(stdin_process);

    // turn on!
    hci_power_control(HCI_POWER_ON);

    return 0;
}

输入 P 连接第一个设备,输入a切换到第一个设备对应的handle
输入O
输入e回车
输入1回车
第一个设备哔哔哔响

输入 A 连接第二个设备,输入b切换到第二个设备对应的handle
输入q
输入6回车
输入16进制数据
第二个设备收到串口数据

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn