Skip to content

Commit 4a5f77d

Browse files
committed
pbio/drv/bluetooth: Send notifications by priority.
As we add more events, we shouldn't be sending them at equal priority but in order. This way, heavy user printing takes priority over things such as telemetry. Also generalize the public pbdrv_bluetooth_send_event_notification function so it works beyond just the current user app data. We will use this to add telemetry. Finally, fix the event code for app data, which was accidentally changed to stdout in previous debugging sessions.
1 parent 927a585 commit 4a5f77d

File tree

4 files changed

+78
-55
lines changed

4 files changed

+78
-55
lines changed

lib/pbio/drv/bluetooth/bluetooth.c

Lines changed: 68 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ void pbdrv_bluetooth_set_receive_handler(pbdrv_bluetooth_receive_handler_t handl
3737
// Functions related to sending value notifications (stdout, status, app data).
3838
//
3939

40+
/**
41+
* Each event has a buffer of maximum packet size. They are prioritized by
42+
* ascending event number, so status gets sent first, then stdout, etc.
43+
*/
44+
static uint32_t pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_NUM_EVENTS];
45+
static uint8_t pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_NUM_EVENTS][PBDRV_BLUETOOTH_MAX_CHAR_SIZE];
46+
4047
/**
4148
* Buffer scheduled status.
4249
*/
@@ -59,7 +66,6 @@ void pbdrv_bluetooth_schedule_status_update(const uint8_t *status_msg) {
5966
* Buffer for scheduled stdout.
6067
*/
6168
static lwrb_t stdout_ring_buf;
62-
static bool stdout_send_busy;
6369

6470
void pbdrv_bluetooth_init(void) {
6571
// enough for two packets, one currently being sent and one to be ready
@@ -103,40 +109,34 @@ bool pbdrv_bluetooth_tx_is_idle(void) {
103109
return true;
104110
}
105111

106-
return !stdout_send_busy && lwrb_get_full(&stdout_ring_buf) == 0;
112+
return lwrb_get_full(&stdout_ring_buf) == 0 && !pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_WRITE_STDOUT];
107113
}
108114

109-
/**
110-
* Buffer for generic awaitable notification.
111-
*/
112-
static uint8_t user_notification_send_buf[PBDRV_BLUETOOTH_MAX_CHAR_SIZE];
113-
static size_t user_notification_size;
114-
115115
pbio_error_t pbdrv_bluetooth_send_event_notification(pbio_os_state_t *state, pbio_pybricks_event_t event_type, const uint8_t *data, size_t size) {
116116
PBIO_OS_ASYNC_BEGIN(state);
117117

118118
if (!pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_PYBRICKS)) {
119119
return PBIO_ERROR_INVALID_OP;
120120
}
121121

122-
if (size + 1 > PBIO_ARRAY_SIZE(user_notification_send_buf)) {
122+
if (size + 1 > PBIO_ARRAY_SIZE(pbdrv_bluetooth_noti_buf[0]) || event_type >= PBIO_PYBRICKS_EVENT_NUM_EVENTS) {
123123
return PBIO_ERROR_INVALID_ARG;
124124
}
125125

126-
// Existing user notification ongoing.
127-
if (user_notification_size) {
126+
// Existing notification waiting to be sent first.
127+
if (pbdrv_bluetooth_noti_size[event_type]) {
128128
return PBIO_ERROR_BUSY;
129129
}
130130

131131
// Copy to local buffer and set size so main thread knows to handle it.
132-
user_notification_size = size + 1;
133-
user_notification_send_buf[0] = event_type;
134-
memcpy(&user_notification_send_buf[1], data, size);
132+
pbdrv_bluetooth_noti_size[event_type] = size + 1;
133+
pbdrv_bluetooth_noti_buf[event_type][0] = event_type;
134+
memcpy(&pbdrv_bluetooth_noti_buf[event_type][1], data, size);
135135
pbio_os_request_poll();
136136

137137
// Await until main process has finished sending user data. If it
138138
// disconnected while sending, this completes as well.
139-
PBIO_OS_AWAIT_WHILE(state, user_notification_size);
139+
PBIO_OS_AWAIT_WHILE(state, pbdrv_bluetooth_noti_size[event_type]);
140140

141141
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
142142
}
@@ -408,6 +408,53 @@ pbio_error_t pbdrv_bluetooth_await_advertise_or_scan_command(pbio_os_state_t *st
408408
return advertising_or_scan_func ? advertising_or_scan_err : PBIO_SUCCESS;
409409
}
410410

411+
/**
412+
* Updates send buffers by draining relevant data buffers and find which event
413+
* has the highest priority to send.
414+
*/
415+
static bool update_and_get_event_buffer(uint8_t **buf, uint32_t **len) {
416+
417+
static pbio_os_timer_t status_timer;
418+
419+
// Prepare status.
420+
if (status_data_pending || pbio_os_timer_is_expired(&status_timer)) {
421+
// When a status is pending, drain it here while we write it out,
422+
// so a new status can be set in the mean time without losing it.
423+
memcpy(pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_STATUS_REPORT], status_data, PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE);
424+
pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_STATUS_REPORT] = PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE;
425+
status_data_pending = false;
426+
pbio_os_timer_set(&status_timer, PBDRV_BLUETOOTH_STATUS_UPDATE_INTERVAL);
427+
}
428+
429+
// Prepare stdout, drain into chunk of maximum send size.
430+
if (lwrb_get_full(&stdout_ring_buf) != 0) {
431+
// Message always starts with event byte.
432+
if (!pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_WRITE_STDOUT]) {
433+
pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_WRITE_STDOUT][0] = PBIO_PYBRICKS_EVENT_WRITE_STDOUT;
434+
pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_WRITE_STDOUT] = 1;
435+
}
436+
// Drain ring buffer to send buffer as much as we can.
437+
uint32_t stdout_free = sizeof(pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_WRITE_STDOUT]) - pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_WRITE_STDOUT];
438+
if (stdout_free) {
439+
uint8_t *dest = &pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_WRITE_STDOUT][sizeof(pbdrv_bluetooth_noti_buf[PBIO_PYBRICKS_EVENT_WRITE_STDOUT]) - stdout_free];
440+
pbdrv_bluetooth_noti_size[PBIO_PYBRICKS_EVENT_WRITE_STDOUT] += lwrb_read(&stdout_ring_buf, dest, stdout_free);
441+
}
442+
}
443+
444+
// Other events are awaited as-is and don't allow setting new data until
445+
// they have been transmitted, so don't need further processing/draining.
446+
447+
// Return highest priority pending event, ready for sending.s
448+
for (uint32_t i = 0; i < PBIO_PYBRICKS_EVENT_NUM_EVENTS; i++) {
449+
if (pbdrv_bluetooth_noti_size[i]) {
450+
*len = &pbdrv_bluetooth_noti_size[i];
451+
*buf = pbdrv_bluetooth_noti_buf[i];
452+
return true;
453+
}
454+
}
455+
return false;
456+
}
457+
411458
static bool shutting_down;
412459

413460
/**
@@ -424,7 +471,6 @@ pbio_error_t pbdrv_bluetooth_process_thread(pbio_os_state_t *state, void *contex
424471

425472
static pbio_os_state_t sub;
426473
static pbio_os_timer_t timer;
427-
static pbio_os_timer_t status_timer;
428474
pbio_error_t err;
429475

430476
// Shorthand notation accessible throughout.
@@ -449,8 +495,6 @@ pbio_error_t pbdrv_bluetooth_process_thread(pbio_os_state_t *state, void *contex
449495

450496
DEBUG_PRINT("Bluetooth is now on and initialized.\n");
451497

452-
pbio_os_timer_set(&status_timer, PBDRV_BLUETOOTH_STATUS_UPDATE_INTERVAL);
453-
454498
// Service scheduled tasks as long as Bluetooth is enabled.
455499
while (!shutting_down) {
456500

@@ -459,41 +503,12 @@ pbio_error_t pbdrv_bluetooth_process_thread(pbio_os_state_t *state, void *contex
459503
// allows short stdout messages to be queued rather than sent separately.
460504
PBIO_OS_AWAIT_MS(state, &timer, 1);
461505

462-
// Handle pending status update, if any.
463-
if (can_send && (status_data_pending || pbio_os_timer_is_expired(&status_timer))) {
464-
465-
// When a status is pending, cache it here while we write it out,
466-
// so a new status can be set in the mean time without losing it.
467-
static uint8_t status_buf[PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE];
468-
memcpy(status_buf, status_data, PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE);
469-
status_data_pending = false;
470-
471-
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_send_pybricks_value_notification(&sub, status_buf, sizeof(status_buf)));
472-
473-
pbio_os_timer_set(&status_timer, status_timer.duration);
474-
}
475-
476-
// Handle pending stdout, if any.
477-
if (can_send && lwrb_get_full(&stdout_ring_buf) != 0) {
478-
stdout_send_busy = true;
479-
480-
static uint8_t stdout_buf[PBDRV_BLUETOOTH_MAX_CHAR_SIZE] = { PBIO_PYBRICKS_EVENT_WRITE_STDOUT };
481-
static uint16_t stdout_len;
482-
483-
stdout_len = lwrb_read(&stdout_ring_buf, &stdout_buf[1], PBDRV_BLUETOOTH_MAX_CHAR_SIZE - 1) + 1;
484-
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_send_pybricks_value_notification(&sub, stdout_buf, stdout_len));
485-
486-
stdout_send_busy = false;
487-
}
488-
489-
// Handle pending user value notification, if any.
490-
if (can_send && user_notification_size) {
491-
492-
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_send_pybricks_value_notification(&sub,
493-
user_notification_send_buf,
494-
user_notification_size));
495-
496-
user_notification_size = 0;
506+
// Send one event notification (status, stdout, ...)
507+
static uint32_t *noti_size;
508+
static uint8_t *noti_buf;
509+
if (can_send && update_and_get_event_buffer(&noti_buf, &noti_size)) {
510+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_send_pybricks_value_notification(&sub, noti_buf, *noti_size));
511+
*noti_size = 0;
497512
}
498513

499514
// Handle pending advertising/scan enable/disable task, if any.

lib/pbio/include/pbio/protocol.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@ typedef enum {
261261
* @since Pybricks Profile v1.4.0
262262
*/
263263
PBIO_PYBRICKS_EVENT_WRITE_APP_DATA = 2,
264+
/**
265+
* The total number of events that can be queued and sent.
266+
*/
267+
PBIO_PYBRICKS_EVENT_NUM_EVENTS,
264268
} pbio_pybricks_event_t;
265269

266270
/**

pybricks/tools/pb_type_app_data.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ static mp_obj_t pb_type_app_data_write_bytes(mp_obj_t self_in, mp_obj_t data_in)
7575
size_t size;
7676
const uint8_t *data = (const uint8_t *)mp_obj_str_get_data(data_in, &size);
7777
pbio_os_state_t state = 0;
78-
pbio_error_t err = pbsys_host_send_event(&state, PBIO_PYBRICKS_EVENT_WRITE_STDOUT, data, size);
78+
pbio_error_t err = pbsys_host_send_event(&state, PBIO_PYBRICKS_EVENT_WRITE_APP_DATA, data, size);
7979

8080
// Expect yield after the initial call.
8181
if (err == PBIO_SUCCESS) {

tests/virtualhub/basics/hello.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
from pybricks.tools import wait
1+
from pybricks.tools import wait, AppData
2+
3+
app = AppData("<bb")
4+
5+
app.write_bytes(b"BOO")
26

37
print("\nHello")
48
wait(1000)

0 commit comments

Comments
 (0)