Skip to content

Commit cb7bfec

Browse files
committed
pybricks/bluetooth: Add scan for EV3.
The ev3 can now scan for discoverable Bluetooth devices.
1 parent a17c1d2 commit cb7bfec

File tree

10 files changed

+369
-0
lines changed

10 files changed

+369
-0
lines changed

bricks/_common/qstrdefs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Q(pybricks.ev3devices)
1515
Q(pybricks.experimental)
1616
#endif
1717

18+
#if PYBRICKS_PY_COMMON_BTC
19+
Q(pybricks.experimental.btc)
20+
#endif
21+
1822
#if MICROPY_PY_BUILTINS_FLOAT // backwards compatibility with Pybricks V3.2, maps to pybricks.tools
1923
Q(pybricks.geometry)
2024
#endif

bricks/_common/sources.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ PYBRICKS_PYBRICKS_SRC_C = $(addprefix pybricks/,\
3333
ev3devices/pb_type_ev3devices_infraredsensor.c \
3434
ev3devices/pb_type_ev3devices_touchsensor.c \
3535
ev3devices/pb_type_ev3devices_ultrasonicsensor.c \
36+
experimental/pb_module_btc.c \
3637
experimental/pb_module_experimental.c \
3738
hubs/pb_module_hubs.c \
3839
hubs/pb_type_buildhat.c \
@@ -117,6 +118,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\
117118
drv/bluetooth/bluetooth.c \
118119
drv/bluetooth/bluetooth_btstack_stm32_hal.c \
119120
drv/bluetooth/bluetooth_btstack.c \
121+
drv/bluetooth/bluetooth_btstack_classic.c \
120122
drv/bluetooth/bluetooth_btstack_ev3.c \
121123
drv/bluetooth/bluetooth_simulation.c \
122124
drv/bluetooth/bluetooth_stm32_bluenrg.c \

bricks/ev3/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// Pybricks modules
1717
#define PYBRICKS_PY_COMMON (1)
1818
#define PYBRICKS_PY_COMMON_BLE (0)
19+
#define PYBRICKS_PY_COMMON_BTC (1)
1920
#define PYBRICKS_PY_COMMON_CHARGER (0)
2021
#define PYBRICKS_PY_COMMON_COLOR_LIGHT (1)
2122
#define PYBRICKS_PY_COMMON_CONTROL (1)

lib/pbio/drv/bluetooth/bluetooth_btstack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "bluetooth.h"
2626
#include "bluetooth_btstack.h"
27+
#include "bluetooth_btstack_classic.h"
2728

2829
#if PBDRV_CONFIG_BLUETOOTH_BTSTACK_LE
2930
#include "genhdr/pybricks_service.h"
@@ -1180,6 +1181,8 @@ void pbdrv_bluetooth_init_hci(void) {
11801181
nordic_spp_service_server_init(nordic_spp_packet_handler);
11811182
#endif // PBDRV_CONFIG_BLUETOOTH_BTSTACK_LE
11821183

1184+
pbdrv_bluetooth_classic_init();
1185+
11831186
bluetooth_thread_err = PBIO_ERROR_AGAIN;
11841187
bluetooth_thread_state = 0;
11851188
pbio_os_process_start(&pbdrv_bluetooth_hci_process, pbdrv_bluetooth_hci_process_thread, NULL);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2025 The Pybricks Authors
3+
4+
// This file defines the functions required to implement Pybricks' Bluetooth
5+
// classic functionality using BTStack.
6+
7+
#include <pbdrv/config.h>
8+
9+
#if PBDRV_CONFIG_BLUETOOTH_BTSTACK_CLASSIC
10+
11+
#include <pbdrv/bluetooth.h>
12+
13+
#include <inttypes.h>
14+
#include <stdint.h>
15+
#include <stdio.h>
16+
17+
#include <btstack.h>
18+
19+
#include <pbdrv/bluetooth.h>
20+
21+
static int32_t pending_inquiry_response_count;
22+
static int32_t pending_inquiry_response_limit;
23+
static void *pending_inquiry_result_handler_context;
24+
static pbdrv_bluetooth_inquiry_result_handler_t pending_inquiry_result_handler;
25+
26+
static void handle_hci_event_packet(uint8_t *packet, uint16_t size);
27+
28+
void pbdrv_bluetooth_classic_handle_packet(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
29+
switch (packet_type) {
30+
case HCI_EVENT_PACKET:
31+
handle_hci_event_packet(packet, size);
32+
}
33+
}
34+
35+
static void handle_hci_event_packet(uint8_t *packet, uint16_t size) {
36+
switch (hci_event_packet_get_type(packet)) {
37+
case GAP_EVENT_INQUIRY_RESULT: {
38+
if (!pending_inquiry_result_handler) {
39+
return;
40+
}
41+
pbdrv_bluetooth_inquiry_result_t result;
42+
gap_event_inquiry_result_get_bd_addr(packet, result.bdaddr);
43+
if (gap_event_inquiry_result_get_rssi_available(packet)) {
44+
result.rssi = gap_event_inquiry_result_get_rssi(packet);
45+
}
46+
if (gap_event_inquiry_result_get_name_available(packet)) {
47+
const uint8_t *name = gap_event_inquiry_result_get_name(packet);
48+
const size_t name_len = gap_event_inquiry_result_get_name_len(packet);
49+
snprintf(result.name, sizeof(result.name), "%.*s", (int)name_len, name);
50+
}
51+
result.class_of_device = gap_event_inquiry_result_get_class_of_device(packet);
52+
pending_inquiry_result_handler(pending_inquiry_result_handler_context, &result);
53+
if (pending_inquiry_response_limit > 0) {
54+
pending_inquiry_response_count++;
55+
if (pending_inquiry_response_count >= pending_inquiry_response_limit) {
56+
gap_inquiry_stop();
57+
}
58+
}
59+
break;
60+
}
61+
case GAP_EVENT_INQUIRY_COMPLETE: {
62+
if (pending_inquiry_result_handler) {
63+
pending_inquiry_result_handler = NULL;
64+
pending_inquiry_result_handler_context = NULL;
65+
pbio_os_request_poll();
66+
}
67+
break;
68+
}
69+
default:
70+
break;
71+
}
72+
}
73+
74+
void pbdrv_bluetooth_classic_init() {
75+
static btstack_packet_callback_registration_t hci_event_handler_registration;
76+
hci_event_handler_registration.callback = pbdrv_bluetooth_classic_handle_packet;
77+
hci_add_event_handler(&hci_event_handler_registration);
78+
}
79+
80+
pbio_error_t pbdrv_bluetooth_inquiry_scan(
81+
pbio_os_state_t *state,
82+
int32_t max_responses,
83+
int32_t timeout,
84+
void *context,
85+
pbdrv_bluetooth_inquiry_result_handler_t result_handler) {
86+
PBIO_OS_ASYNC_BEGIN(state);
87+
if (pending_inquiry_result_handler) {
88+
return PBIO_ERROR_BUSY;
89+
}
90+
PBIO_OS_AWAIT_UNTIL(state, hci_get_state() == HCI_STATE_WORKING);
91+
pending_inquiry_response_count = 0;
92+
pending_inquiry_response_limit = max_responses;
93+
pending_inquiry_result_handler = result_handler;
94+
pending_inquiry_result_handler_context = context;
95+
gap_inquiry_start(timeout);
96+
97+
PBIO_OS_AWAIT_UNTIL(state, !pending_inquiry_result_handler);
98+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
99+
}
100+
101+
#endif // PBDRV_CONFIG_BLUETOOTH_BTSTACK_CLASSIC
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef PBDRV_BLUETOOTH_BLUETOOTH_BTSTACK_CLASSIC_H
2+
#define PBDRV_BLUETOOTH_BLUETOOTH_BTSTACK_CLASSIC_H
3+
#include <pbdrvconfig.h>
4+
5+
#if PBDRV_CONFIG_BLUETOOTH_CLASSIC
6+
7+
#include <stdint.h>
8+
9+
void pbdrv_bluetooth_classic_init();
10+
11+
#else
12+
13+
static inline void pbdrv_bluetooth_classic_init() {}
14+
15+
#endif // PBDRV_CONFIG_BLUETOOTH_CLASSIC
16+
17+
#endif // PBDRV_BLUETOOTH_BLUETOOTH_BTSTACK_CLASSIC_H

lib/pbio/include/pbdrv/bluetooth.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,48 @@ static inline pbio_error_t pbdrv_bluetooth_close_user_tasks(pbio_os_state_t *sta
612612

613613
#endif // PBDRV_CONFIG_BLUETOOTH
614614

615+
#if PBDRV_CONFIG_BLUETOOTH_CLASSIC
616+
617+
// A single result from an inquiry scan.
618+
typedef struct {
619+
uint8_t bdaddr[6];
620+
int8_t rssi;
621+
char name[249];
622+
uint32_t class_of_device;
623+
} pbdrv_bluetooth_inquiry_result_t;
624+
625+
// Callback for handling inquiry results.
626+
typedef void (*pbdrv_bluetooth_inquiry_result_handler_t)(void *context, const pbdrv_bluetooth_inquiry_result_t *result);
627+
628+
/**
629+
* Runs a bluetooth inquiry scan. Only one such scan can be active at a time.
630+
*
631+
* @param [in] state Protothread state.
632+
* @param [in] max_responses Maximum number of responses to report. Use -1 for unlimited.
633+
* @param [in] timeout Timeout in units of 1.28 seconds. Values less than one will be coerced to one.
634+
* @param [in] context Context pointer to be passed to the result handler.
635+
* @param [in] result_handler Callback that will be called for each inquiry result.
636+
*/
637+
pbio_error_t pbdrv_bluetooth_inquiry_scan(
638+
pbio_os_state_t *state,
639+
int32_t max_responses,
640+
int32_t timeout,
641+
void *context,
642+
pbdrv_bluetooth_inquiry_result_handler_t result_handler);
643+
644+
#else // PBDRV_CONFIG_BLUETOOTH_CLASSIC
645+
646+
static inline pbio_error_t pbdrv_bluetooth_inquiry_scan(
647+
pbio_os_state_t *state,
648+
int32_t max_responses,
649+
int32_t timeout,
650+
void *context,
651+
void *result_handler) {
652+
return PBIO_ERROR_NOT_SUPPORTED;
653+
}
654+
655+
#endif // PBDRV_CONFIG_BLUETOOTH_CLASSIC
656+
615657
#endif // _PBDRV_BLUETOOTH_H_
616658

617659
/** @} */

lib/pbio/platform/ev3/pbdrvconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#define PBDRV_CONFIG_I2C_EV3 (1)
4747

4848
#define PBDRV_CONFIG_BLUETOOTH (1)
49+
#define PBDRV_CONFIG_BLUETOOTH_CLASSIC (1)
4950
#define PBDRV_CONFIG_BLUETOOTH_MAX_MTU_SIZE 515
5051
#define PBDRV_CONFIG_BLUETOOTH_BTSTACK (1)
5152
#define PBDRV_CONFIG_BLUETOOTH_BTSTACK_CLASSIC (1)

0 commit comments

Comments
 (0)