Skip to content

Commit 137b88b

Browse files
committed
Tidy up ble pointer example
1 parent 2461c4c commit 137b88b

3 files changed

Lines changed: 104 additions & 81 deletions

File tree

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
11
# Add mpu6050 libary
22
add_library(mpu6050_i2c_lib INTERFACE)
33
target_sources(mpu6050_i2c_lib INTERFACE
4-
${CMAKE_CURRENT_LIST_DIR}/mpu6050_i2c_lib.c
5-
)
4+
${CMAKE_CURRENT_LIST_DIR}/mpu6050_i2c_lib.c
5+
)
66
target_include_directories(mpu6050_i2c_lib INTERFACE
7-
${CMAKE_CURRENT_LIST_DIR}
8-
)
7+
${CMAKE_CURRENT_LIST_DIR}
8+
)
99
target_link_libraries(mpu6050_i2c_lib INTERFACE
1010
pico_stdlib
1111
hardware_i2c
12-
)
12+
)
1313

14-
# Add executable. Default name is the project name, version 0.1
1514
add_executable(ble_pointer
1615
ble_pointer.c
17-
)
18-
19-
# Add the standard library to the build
16+
)
2017
target_link_libraries(ble_pointer
2118
pico_stdlib
2219
pico_btstack_ble
2320
pico_btstack_cyw43
2421
pico_cyw43_arch_none
2522
mpu6050_i2c_lib
26-
)
27-
28-
# Add the standard include files to the build
23+
)
2924
target_include_directories(ble_pointer PRIVATE
3025
${CMAKE_CURRENT_LIST_DIR}
3126
${CMAKE_CURRENT_LIST_DIR}/..
32-
)
33-
27+
)
28+
target_compile_definitions(ble_pointer PRIVATE
29+
#WANT_HCI_DUMP=1
30+
)
3431
pico_btstack_make_gatt_header(ble_pointer PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ble_pointer.gatt")
35-
3632
pico_add_extra_outputs(ble_pointer)
3733

bluetooth/ble_pointer/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
This example is based on BTstack's 'hog_mouse_demo' and demonstrates a Bluetooth HID mouse. Cursor position is controlled by mpu6050 angle measurements, allowing you to point at the screen and move the mouse cursor.
44

5-
To use this example connect a mpu6050 (which can be found at https://thepihut.com/products/6-dof-sensor-mpu6050) to the pico, with SDA connected to pin 4 and SCL connected to pin 5. Also, connect 2 buttons to pins 15 and 16 for left and right click.
5+
To use this example connect a mpu6050 (which can be found at https://thepihut.com/products/6-dof-sensor-mpu6050) to the pico, with SDA connected to pin 4 and SCL connected to pin 5. Also, connect 2 buttons to pins 15 and 16 and ground for left and right click.
66

77
Once powered, you should be able to pair your computer with 'HID Mouse' and use the pointer.

bluetooth/ble_pointer/ble_pointer.c

Lines changed: 92 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@
3535
*
3636
*/
3737

38-
//#define BTSTACK_FILE__ "hog_mouse_demo.c"
39-
40-
// *****************************************************************************
41-
/* EXAMPLE_START(hog_mouse_demo): HID Mouse LE
42-
*/
43-
// *****************************************************************************
44-
4538
#include <stdint.h>
4639
#include <stdio.h>
4740
#include <stdlib.h>
@@ -67,6 +60,18 @@
6760
#include <mpu6050_i2c_lib.h>
6861
#include <math.h>
6962

63+
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
64+
#error Example requires a board with I2C pins
65+
#endif
66+
67+
#ifndef NDEBUG
68+
#define DEBUG_LOG(...) printf(__VA_ARGS__)
69+
#else
70+
#define DEBUG_LOG(...)
71+
#endif
72+
#define INFO_LOG printf
73+
#define ERROR_LOG printf
74+
7075
#define ALPHA 0.05 // for complimentary filter, big alpha gives faster reponse but more noise
7176

7277
// FS values are 0, 1, 2, or 3
@@ -78,21 +83,44 @@
7883
// how many readings taken to find gyro offsets
7984
#define OFFSET_NUM 10000
8085

81-
//pins for buttons
86+
// pins for buttons
87+
#ifndef LEFT_BUTTON_GPIO_NUM
8288
#define LEFT_BUTTON_GPIO_NUM 15
89+
#endif
90+
91+
#ifndef RIGHT_BUTTON_GPIO_NUM
8392
#define RIGHT_BUTTON_GPIO_NUM 16
93+
#endif
94+
95+
#ifndef MOUSE_PERIOD_MS
96+
#define MOUSE_PERIOD_MS 15
97+
#endif
98+
99+
#ifndef SCREEN_WIDTH
100+
#define SCREEN_WIDTH 1920
101+
#endif
102+
103+
#ifndef SCREEN_HEIGHT
104+
#define SCREEN_HEIGHT 1080
105+
#endif
84106

85-
float roll;
86-
float pitch;
87-
float yaw;
107+
static float roll;
108+
static float pitch;
109+
static float yaw;
88110

89-
float roll_offset;
90-
float pitch_offset;
91-
float yaw_offset;
111+
static float roll_offset;
112+
static float pitch_offset;
113+
static float yaw_offset;
92114

93115
// start in top left corner
94-
int abs_x = 0;
95-
int abs_y = 0;
116+
static int abs_x = 0;
117+
static int abs_y = 0;
118+
119+
static int dx;
120+
static int dy;
121+
static uint8_t buttons;
122+
static btstack_timer_source_t mousing_timer;
123+
static int mouse_active = 0;
96124

97125
// from USB HID Specification 1.1, Appendix B.2
98126
const uint8_t hid_descriptor_mouse_boot_mode[] = {
@@ -160,7 +188,7 @@ static void hog_mouse_setup(void){
160188

161189
// initialize CYW43 driver architecture (will enable BT if/because CYW43_ENABLE_BLUETOOTH == 1)
162190
if (cyw43_arch_init()) {
163-
printf("failed to initialise cyw43_arch\n");
191+
ERROR_LOG("failed to initialise cyw43_arch\n");
164192
}
165193

166194
// setup l2cap
@@ -222,21 +250,11 @@ static void send_report(uint8_t buttons, int8_t dx, int8_t dy){
222250
}
223251
}
224252

225-
static int dx;
226-
static int dy;
227-
static uint8_t buttons;
228-
229-
#define MOUSE_PERIOD_MS 15
230-
#define SCREEN_WIDTH 1920
231-
#define SCREEN_HEIGHT 1080
232-
233-
static btstack_timer_source_t mousing_timer;
234-
static int mousing_active = 0;
235-
236253
static void mousing_timer_handler(btstack_timer_source_t * ts){
237254

238255
if (con_handle == HCI_CON_HANDLE_INVALID) {
239-
mousing_active = 0;
256+
INFO_LOG("Disabling mouse\n");
257+
mouse_active = 0;
240258
return;
241259
}
242260

@@ -261,38 +279,49 @@ static void mousing_timer_handler(btstack_timer_source_t * ts){
261279
abs_x += dx;
262280
abs_y += dy;
263281

264-
// trigger send
282+
// trigger send. We can call this from here because we are in a btstack callback
265283
hids_device_request_can_send_now_event(con_handle);
266284

267285
// set next timer
268286
btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS);
269287
btstack_run_loop_add_timer(ts);
270288
}
271289

290+
static void button_notification_fn(__unused void * arg) {
291+
hids_device_request_can_send_now_event(con_handle);
292+
}
293+
static btstack_context_callback_registration_t button_notification = { .callback = button_notification_fn };
294+
272295
// IRQ handler for mouse buttons
273296
void button_irq_handler(uint gpio, uint32_t events) {
274297
if (gpio == LEFT_BUTTON_GPIO_NUM) {
275-
printf("left click!\n");
298+
INFO_LOG("Left click!\n");
276299
if (!gpio_get(gpio)) {
277300
buttons = 1;
278301
} else {
279302
buttons = 0;
280303
}
281304
} else if (gpio == RIGHT_BUTTON_GPIO_NUM) {
282-
printf("right click!\n");
305+
INFO_LOG("Right click!\n");
283306
if (!gpio_get(gpio)) {
284307
buttons = 2;
285308
} else {
286309
buttons = 0;
287310
}
288311
}
312+
// We want to call hids_device_request_can_send_now_event,
313+
// but we are in an IRQ and so it's dangerous to call BTStack directly from here.
314+
// So we ask BTstack to callback a function from the "main thread"
315+
// We could also use an async (when pending) worker using async_when_pending_worker_t,
316+
// async_context_add_when_pending_worker and async_context_set_work_pending
317+
btstack_run_loop_execute_on_main_thread(&button_notification);
289318
}
290319

291320
static void hid_embedded_start_mousing(void){
292-
if (mousing_active) return;
293-
mousing_active = 1;
321+
if (mouse_active) return;
322+
mouse_active = 1;
294323

295-
printf("Start mousing..\n");
324+
INFO_LOG("Enabling mouse\n");
296325

297326
// set one-shot timer
298327
mousing_timer.process = &mousing_timer_handler;
@@ -310,30 +339,30 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
310339
switch (hci_event_packet_get_type(packet)) {
311340
case HCI_EVENT_DISCONNECTION_COMPLETE:
312341
con_handle = HCI_CON_HANDLE_INVALID;
313-
printf("Disconnected\n");
342+
INFO_LOG("Disconnected\n");
314343
break;
315344
case SM_EVENT_JUST_WORKS_REQUEST:
316-
printf("Just Works requested\n");
345+
INFO_LOG("Just Works requested\n");
317346
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
318347
break;
319348
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
320-
printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
349+
INFO_LOG("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
321350
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
322351
break;
323352
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
324-
printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
353+
INFO_LOG("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
325354
break;
326355
case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
327-
printf("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet));
356+
DEBUG_LOG("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet));
328357
break;
329358
case HCI_EVENT_META_GAP:
330359
switch (hci_event_gap_meta_get_subevent_code(packet)) {
331360
case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
332361
// print connection parameters (without using float operations)
333362
conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet);
334-
printf("LE Connection Complete:\n");
335-
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
336-
printf("- Connection Latency: %u\n", gap_subevent_le_connection_complete_get_conn_latency(packet));
363+
DEBUG_LOG("LE Connection Complete:\n");
364+
DEBUG_LOG("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
365+
DEBUG_LOG("- Connection Latency: %u\n", gap_subevent_le_connection_complete_get_conn_latency(packet));
337366
break;
338367
default:
339368
break;
@@ -344,9 +373,9 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
344373
case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
345374
// print connection parameters (without using float operations)
346375
conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
347-
printf("LE Connection Update:\n");
348-
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
349-
printf("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet));
376+
DEBUG_LOG("LE Connection Update:\n");
377+
DEBUG_LOG("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
378+
DEBUG_LOG("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet));
350379
break;
351380
default:
352381
break;
@@ -356,28 +385,20 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
356385
switch (hci_event_hids_meta_get_subevent_code(packet)){
357386
case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
358387
con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
359-
printf("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet));
360-
#ifndef HAVE_BTSTACK_STDIN
388+
DEBUG_LOG("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet));
361389
hid_embedded_start_mousing();
362-
#endif
363-
// request connection param update via L2CAP following Apple Bluetooth Design Guidelines
364-
// gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s
365-
366-
// directly update connection params via HCI following Apple Bluetooth Design Guidelines
367-
// gap_update_connection_parameters(con_handle, 12, 12, 4, 100); // 60-75 ms, 4, 1s
368-
369390
break;
370391
case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE:
371392
con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet);
372-
printf("Boot Keyboard Characteristic Subscribed %u\n", hids_subevent_boot_keyboard_input_report_enable_get_enable(packet));
393+
DEBUG_LOG("Boot Keyboard Characteristic Subscribed %u\n", hids_subevent_boot_keyboard_input_report_enable_get_enable(packet));
373394
break;
374395
case HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE:
375396
con_handle = hids_subevent_boot_mouse_input_report_enable_get_con_handle(packet);
376-
printf("Boot Mouse Characteristic Subscribed %u\n", hids_subevent_boot_mouse_input_report_enable_get_enable(packet));
397+
DEBUG_LOG("Boot Mouse Characteristic Subscribed %u\n", hids_subevent_boot_mouse_input_report_enable_get_enable(packet));
377398
break;
378399
case HIDS_SUBEVENT_PROTOCOL_MODE:
379400
protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
380-
printf("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot");
401+
DEBUG_LOG("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot");
381402
break;
382403
case HIDS_SUBEVENT_CAN_SEND_NOW:
383404
send_report(buttons, dx, dy);
@@ -395,12 +416,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
395416
int main(void) {
396417
stdio_init_all();
397418

398-
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
399-
#warning Example requires a board with I2C pins
400-
puts("Default I2C pins were not defined");
401-
return 1;
402-
#endif
403-
404419
// Make the I2C pins available to picotool
405420
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
406421

@@ -440,8 +455,20 @@ int main(void) {
440455
// turn on!
441456
hci_power_control(HCI_POWER_ON);
442457

443-
while (true) {
458+
// btstack_run_loop_execute is only required when using the 'polling' method (e.g. using pico_cyw43_arch_poll library).
459+
// This example uses the 'threadsafe background` method, where BT work is handled in a low priority IRQ, so it
460+
// is fine to call bt_stack_run_loop_execute() but equally you can continue executing user code.
461+
462+
#if 1 // this is only necessary when using polling (which we aren't, but we're showing it is still safe to call in this case)
463+
btstack_run_loop_execute();
464+
#else
465+
// this core is free to do it's own stuff except when using 'polling' method (in which case you should use
466+
// btstacK_run_loop_ methods to add work to the run loop.
467+
468+
// this is a forever loop in place of where user code would go.
469+
while(true) {
444470
sleep_ms(1000);
445471
}
472+
#endif
446473
return 0;
447474
}

0 commit comments

Comments
 (0)