Skip to content

Commit 05065ff

Browse files
laurensvalkjuagol
andcommitted
pybricks.messaging: Implement rfcomm_scan skeleton.
This allocates data for the async rfcomm_scan results, and returns a list of dictionaries. Doesn't do any scanning yet. Co-authored-by: James Aguilar <[email protected]>
1 parent b9edf65 commit 05065ff

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

lib/pbio/include/pbdrv/bluetooth.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,14 @@ pbio_error_t pbdrv_bluetooth_await_advertise_or_scan_command(pbio_os_state_t *st
553553
*/
554554
pbio_error_t pbdrv_bluetooth_close_user_tasks(pbio_os_state_t *state, pbio_os_timer_t *timer);
555555

556+
// A single result from an inquiry scan.
557+
typedef struct {
558+
uint8_t bdaddr[6];
559+
int8_t rssi;
560+
char name[249];
561+
uint32_t class_of_device;
562+
} pbdrv_bluetooth_inquiry_result_t;
563+
556564
#else // PBDRV_CONFIG_BLUETOOTH
557565

558566
static inline void pbdrv_bluetooth_init(void) {

pybricks/messaging/pb_module_messaging.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,150 @@
55

66
#if PYBRICKS_PY_MESSAGING
77

8+
#include <stdio.h>
9+
#include <string.h>
10+
811
#include "py/mphal.h"
912
#include "py/obj.h"
1013
#include "py/objstr.h"
1114
#include "py/runtime.h"
1215
#include "py/mperrno.h"
1316

17+
#include <pbdrv/bluetooth.h>
18+
19+
#include <pbio/int_math.h>
1420
#include <pbio/util.h>
1521

1622
#include <pybricks/util_mp/pb_obj_helper.h>
1723
#include <pybricks/util_mp/pb_kwarg_helper.h>
1824

1925
#include <pybricks/util_pb/pb_error.h>
26+
#include <pybricks/tools/pb_type_async.h>
27+
28+
#define DEBUG 0
29+
30+
#if DEBUG
31+
#include <pbio/debug.h>
32+
#define DEBUG_PRINT pbio_debug
33+
#else
34+
#define DEBUG_PRINT(...)
35+
#endif
36+
37+
typedef struct {
38+
mp_obj_base_t base;
39+
uint32_t num_results;
40+
uint32_t num_results_max;
41+
uint32_t num_results_printed;
42+
pbdrv_bluetooth_inquiry_result_t results[];
43+
} pb_messaging_rfcomm_scan_result_obj_t;
44+
45+
static mp_obj_t pb_messaging_rfcomm_scan_close(mp_obj_t self_in) {
46+
pb_messaging_rfcomm_scan_result_obj_t *self = MP_OBJ_TO_PTR(self_in);
47+
DEBUG_PRINT("rfcomm scan data freed\n");
48+
self->num_results_max = 0;
49+
return mp_const_none;
50+
}
51+
MP_DEFINE_CONST_FUN_OBJ_1(pb_messaging_rfcomm_scan_close_obj, pb_messaging_rfcomm_scan_close);
52+
53+
static const mp_rom_map_elem_t pb_messaging_rfcomm_scan_locals_dict_table[] = {
54+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&pb_messaging_rfcomm_scan_close_obj) },
55+
};
56+
static MP_DEFINE_CONST_DICT(pb_messaging_rfcomm_scan_locals_dict, pb_messaging_rfcomm_scan_locals_dict_table);
57+
58+
MP_DEFINE_CONST_OBJ_TYPE(pb_type_messaging_rfcomm_scan, MP_QSTR_rfcomm_scan, MP_TYPE_FLAG_NONE, locals_dict, &pb_messaging_rfcomm_scan_locals_dict);
59+
60+
/**
61+
* Utility for converting a bluetooth address byte buffer to a string.
62+
*
63+
* The result does not persist. Intended for instant consumption.
64+
*
65+
* @param [in] address 6-byte bluetooth address.
66+
* @return Formatted bluetooth address string.
67+
*/
68+
static char *format_bluetooth_address(uint8_t *address) {
69+
static char bdaddr_str[18];
70+
snprintf(bdaddr_str, sizeof(bdaddr_str), "%02X:%02X:%02X:%02X:%02X:%02X",
71+
address[0], address[1], address[2], address[3], address[4], address[5]);
72+
return bdaddr_str;
73+
}
74+
75+
/**
76+
* Wrapper around the actual driver task that prints progress as we go along
77+
* if the verbose option was specified.
78+
*/
79+
static pbio_error_t pb_messaging_rfcomm_scan_thread(pbio_os_state_t *state, mp_obj_t parent_obj) {
80+
81+
pb_messaging_rfcomm_scan_result_obj_t *scanner = MP_OBJ_TO_PTR(parent_obj);
82+
83+
while (scanner->num_results_printed < scanner->num_results) {
84+
pbdrv_bluetooth_inquiry_result_t *result = &scanner->results[scanner->num_results_printed++];
85+
mp_printf(&mp_plat_print, "Detected %s: %s with class %d and RSSI %d.\n",
86+
format_bluetooth_address(result->bdaddr), result->name, result->class_of_device, result->rssi);
87+
}
88+
89+
return PBIO_SUCCESS;
90+
}
91+
92+
/**
93+
* Maps the inquiry results to a list of dictionary, to be returned to the user.
94+
*/
95+
static mp_obj_t pb_messaging_rfcomm_scan_return_map(mp_obj_t parent_obj) {
96+
97+
pb_messaging_rfcomm_scan_result_obj_t *scanner = MP_OBJ_TO_PTR(parent_obj);
98+
99+
mp_obj_t list = mp_obj_new_list(0, NULL);
100+
101+
for (uint32_t i = 0; i < scanner->num_results; i++) {
102+
mp_obj_t dict = mp_obj_new_dict(0);
103+
pbdrv_bluetooth_inquiry_result_t *result = &scanner->results[i];
104+
mp_obj_dict_store(dict, MP_ROM_QSTR(MP_QSTR_address), mp_obj_new_str(format_bluetooth_address(result->bdaddr), 17));
105+
mp_obj_dict_store(dict, MP_ROM_QSTR(MP_QSTR_name), mp_obj_new_str(result->name, strlen(result->name)));
106+
mp_obj_dict_store(dict, MP_ROM_QSTR(MP_QSTR_rssi), mp_obj_new_int(result->rssi));
107+
mp_obj_dict_store(dict, MP_ROM_QSTR(MP_QSTR_class), mp_obj_new_int(result->class_of_device));
108+
mp_obj_list_append(list, dict);
109+
}
110+
return list;
111+
}
112+
113+
// pybricks.messaging.rfcomm_scan
114+
static mp_obj_t pb_messaging_rfcomm_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
115+
PB_PARSE_ARGS_FUNCTION(n_args, pos_args, kw_args,
116+
PB_ARG_DEFAULT_INT(timeout, 10000),
117+
PB_ARG_DEFAULT_INT(num_results, 5),
118+
PB_ARG_DEFAULT_TRUE(verbose)
119+
);
120+
121+
// Allocate the maximum number of expected results.
122+
uint32_t num_results_max = mp_obj_get_int(num_results_in);
123+
if (!num_results_max) {
124+
num_results_max = 1;
125+
}
126+
pb_messaging_rfcomm_scan_result_obj_t *scanner = mp_obj_malloc_var_with_finaliser(
127+
pb_messaging_rfcomm_scan_result_obj_t, pbdrv_bluetooth_inquiry_result_t,
128+
num_results_max, &pb_type_messaging_rfcomm_scan);
129+
130+
// Initialize at zero results.
131+
scanner->num_results = 0;
132+
scanner->num_results_max = mp_obj_get_int(num_results_in);
133+
scanner->num_results_printed = mp_obj_is_true(verbose_in) ? 0 : scanner->num_results_max;
134+
(void)timeout_in;
135+
136+
// Create an awaitable with a reference to our result to keep it from being
137+
// garbage collected.
138+
pb_type_async_t *iter = NULL;
139+
pb_type_async_t config = {
140+
.iter_once = pb_messaging_rfcomm_scan_thread,
141+
.parent_obj = MP_OBJ_FROM_PTR(scanner),
142+
.return_map = pb_messaging_rfcomm_scan_return_map,
143+
};
144+
return pb_type_async_wait_or_await(&config, &iter, false);
145+
}
146+
// See also messaging_globals_table below. This function object is added there to make it importable.
147+
static MP_DEFINE_CONST_FUN_OBJ_KW(pb_messaging_rfcomm_scan_obj, 0, pb_messaging_rfcomm_scan);
20148

21149
static const mp_rom_map_elem_t messaging_globals_table[] = {
22150
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_messaging) },
151+
{ MP_ROM_QSTR(MP_QSTR_rfcomm_scan), MP_ROM_PTR(&pb_messaging_rfcomm_scan_obj) },
23152
};
24153
static MP_DEFINE_CONST_DICT(pb_module_messaging_globals, messaging_globals_table);
25154

0 commit comments

Comments
 (0)