Skip to content

Instantly share code, notes, and snippets.

@davidgyoung
Created March 3, 2021 19:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidgyoung/0a18028b4338ff6cb201fba274502662 to your computer and use it in GitHub Desktop.
Save davidgyoung/0a18028b4338ff6cb201fba274502662 to your computer and use it in GitHub Desktop.
C program to scan beacons on a raspberry pi
//
// cc scanner.c -lbluetooth -o scanner
//
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <time.h>
struct hci_request ble_hci_request(uint16_t ocf, int clen, void * status, void * cparam)
{
struct hci_request rq;
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_LE_CTL;
rq.ocf = ocf;
rq.cparam = cparam;
rq.clen = clen;
rq.rparam = status;
rq.rlen = 1;
return rq;
}
int main()
{
int ret, status;
// Get HCI device.
int device = hci_open_dev(1);
if ( device < 0 ) {
device = hci_open_dev(0);
if (device >= 0) {
printf("Using hci0\n");
}
}
else {
printf("Using hci1\n");
}
if ( device < 0 ) {
perror("Failed to open HCI device.");
return 0;
}
// Set BLE scan parameters.
le_set_scan_parameters_cp scan_params_cp;
memset(&scan_params_cp, 0, sizeof(scan_params_cp));
scan_params_cp.type = 0x00;
scan_params_cp.interval = htobs(0x0010);
scan_params_cp.window = htobs(0x0010);
scan_params_cp.own_bdaddr_type = 0x00; // Public Device Address (default).
scan_params_cp.filter = 0x00; // Accept all.
struct hci_request scan_params_rq = ble_hci_request(OCF_LE_SET_SCAN_PARAMETERS, LE_SET_SCAN_PARAMETERS_CP_SIZE, &status, &scan_params_cp);
ret = hci_send_req(device, &scan_params_rq, 1000);
if ( ret < 0 ) {
hci_close_dev(device);
perror("Failed to set scan parameters data.");
return 0;
}
// Set BLE events report mask.
le_set_event_mask_cp event_mask_cp;
memset(&event_mask_cp, 0, sizeof(le_set_event_mask_cp));
int i = 0;
for ( i = 0 ; i < 8 ; i++ ) event_mask_cp.mask[i] = 0xFF;
struct hci_request set_mask_rq = ble_hci_request(OCF_LE_SET_EVENT_MASK, LE_SET_EVENT_MASK_CP_SIZE, &status, &event_mask_cp);
ret = hci_send_req(device, &set_mask_rq, 1000);
if ( ret < 0 ) {
hci_close_dev(device);
perror("Failed to set event mask.");
return 0;
}
// Enable scanning.
le_set_scan_enable_cp scan_cp;
memset(&scan_cp, 0, sizeof(scan_cp));
scan_cp.enable = 0x01; // Enable flag.
scan_cp.filter_dup = 0x00; // Filtering disabled.
struct hci_request enable_adv_rq = ble_hci_request(OCF_LE_SET_SCAN_ENABLE, LE_SET_SCAN_ENABLE_CP_SIZE, &status, &scan_cp);
ret = hci_send_req(device, &enable_adv_rq, 1000);
if ( ret < 0 ) {
hci_close_dev(device);
perror("Failed to enable scan.");
return 0;
}
// Get Results.
struct hci_filter nf;
hci_filter_clear(&nf);
hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
hci_filter_set_event(EVT_LE_META_EVENT, &nf);
if ( setsockopt(device, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0 ) {
hci_close_dev(device);
perror("Could not set socket options\n");
return 0;
}
uint8_t buf[HCI_MAX_EVENT_SIZE];
evt_le_meta_event * meta_event;
le_advertising_info * info;
int len;
int count = 0;
unsigned now = (unsigned)time(NULL);
unsigned last_detection_time = now;
// Keep scanning until we see nothing for 10 secs or we have seen lots of advertisements. Then exit.
// We exit in this case because the scan may have failed or stopped. Higher level code can restart
while ( last_detection_time - now < 10 && count < 1000 ) {
len = read(device, buf, sizeof(buf));
if ( len >= HCI_EVENT_HDR_SIZE ) {
count++;
last_detection_time = (unsigned)time(NULL);
meta_event = (evt_le_meta_event*)(buf+HCI_EVENT_HDR_SIZE+1);
if ( meta_event->subevent == EVT_LE_ADVERTISING_REPORT ) {
uint8_t reports_count = meta_event->data[0];
void * offset = meta_event->data + 1;
while ( reports_count-- ) {
info = (le_advertising_info *)offset;
char addr[18];
ba2str(&(info->bdaddr), addr);
printf("%s %d", addr, (int8_t)info->data[info->length]);
for (int i = 0; i < info->length; i++) {
printf(" %02X", (unsigned char)info->data[i]);
}
printf("\n");
offset = info->data + info->length + 2;
}
}
}
now = (unsigned)time(NULL);
}
// Disable scanning.
memset(&scan_cp, 0, sizeof(scan_cp));
scan_cp.enable = 0x00; // Disable flag.
struct hci_request disable_adv_rq = ble_hci_request(OCF_LE_SET_SCAN_ENABLE, LE_SET_SCAN_ENABLE_CP_SIZE, &status, &scan_cp);
ret = hci_send_req(device, &disable_adv_rq, 1000);
if ( ret < 0 ) {
hci_close_dev(device);
perror("Failed to disable scan.");
return 0;
}
hci_close_dev(device);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment