Skip to content

Commit c973cfd

Browse files
iabdalkaderdpgeorge
authored andcommitted
rp2: Add support for bluetooth module using NimBLE.
1 parent 8064c3b commit c973cfd

File tree

7 files changed

+402
-0
lines changed

7 files changed

+402
-0
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,32 @@ set(PICO_SDK_COMPONENTS
159159
tinyusb_device
160160
)
161161

162+
if(MICROPY_PY_BLUETOOTH)
163+
list(APPEND MICROPY_SOURCE_PORT mpbthciport.c)
164+
target_compile_definitions(${MICROPY_TARGET} PRIVATE
165+
MICROPY_PY_BLUETOOTH=1
166+
MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1
167+
MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING=1
168+
MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS=1
169+
)
170+
endif()
171+
172+
if(MICROPY_BLUETOOTH_NIMBLE)
173+
list(APPEND MICROPY_SOURCE_PORT mpnimbleport.c)
174+
target_compile_definitions(${MICROPY_TARGET} PRIVATE
175+
MICROPY_BLUETOOTH_NIMBLE=1
176+
MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY=0
177+
)
178+
target_compile_options(${MICROPY_TARGET} PRIVATE
179+
# TODO: This flag is currently needed to make nimble build.
180+
-Wno-unused-but-set-variable
181+
)
182+
include(${MICROPY_DIR}/extmod/nimble/nimble.cmake)
183+
target_link_libraries(${MICROPY_TARGET} micropy_extmod_nimble)
184+
get_target_property(NIMBLE_INCLUDE micropy_extmod_nimble INTERFACE_INCLUDE_DIRECTORIES)
185+
list(APPEND MICROPY_INC_CORE ${NIMBLE_INCLUDE})
186+
endif()
187+
162188
# Define mpy-cross flags and frozen manifest
163189
set(MICROPY_CROSS_FLAGS -march=armv7m)
164190
if (NOT MICROPY_FROZEN_MANIFEST)

ports/rp2/main.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
#include "py/mperrno.h"
3333
#include "py/mphal.h"
3434
#include "py/stackctrl.h"
35+
#include "extmod/modbluetooth.h"
3536
#include "shared/readline/readline.h"
3637
#include "shared/runtime/gchelper.h"
3738
#include "shared/runtime/pyexec.h"
3839
#include "tusb.h"
3940
#include "uart.h"
4041
#include "modmachine.h"
4142
#include "modrp2.h"
43+
#include "mpbthciport.h"
4244
#include "genhdr/mpversion.h"
4345

4446
#include "pico/stdlib.h"
@@ -107,6 +109,10 @@ int main(int argc, char **argv) {
107109
machine_pin_init();
108110
rp2_pio_init();
109111

112+
#if MICROPY_PY_BLUETOOTH
113+
mp_bluetooth_hci_init();
114+
#endif
115+
110116
// Execute _boot.py to set up the filesystem.
111117
pyexec_frozen_module("_boot.py");
112118

@@ -137,6 +143,9 @@ int main(int argc, char **argv) {
137143
soft_reset_exit:
138144
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
139145
rp2_pio_deinit();
146+
#if MICROPY_PY_BLUETOOTH
147+
mp_bluetooth_deinit();
148+
#endif
140149
machine_pin_deinit();
141150
#if MICROPY_PY_THREAD
142151
mp_thread_deinit();

ports/rp2/mpbthciport.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Ibrahim Abdelkader <[email protected]>
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/runtime.h"
28+
#include "py/stream.h"
29+
#include "py/mphal.h"
30+
#include "extmod/modbluetooth.h"
31+
#include "extmod/mpbthci.h"
32+
#include "modmachine.h"
33+
#include "mpbthciport.h"
34+
#include "pico/stdlib.h"
35+
36+
#if MICROPY_PY_BLUETOOTH
37+
38+
#define debug_printf(...) // mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__)
39+
#define error_printf(...) mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__)
40+
41+
// Poll timer ID.
42+
static alarm_id_t poll_timer_id = 0;
43+
44+
uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
45+
46+
// Prevent double-enqueuing of the scheduled task.
47+
STATIC volatile bool events_task_is_scheduled;
48+
49+
void mp_bluetooth_hci_init(void) {
50+
events_task_is_scheduled = false;
51+
}
52+
53+
STATIC void mp_bluetooth_hci_start_polling(void) {
54+
events_task_is_scheduled = false;
55+
mp_bluetooth_hci_poll_now();
56+
}
57+
58+
static int64_t mp_bluetooth_hci_timer_callback(alarm_id_t id, void *user_data) {
59+
poll_timer_id = 0;
60+
mp_bluetooth_hci_poll_now();
61+
return 0;
62+
}
63+
64+
void mp_bluetooth_hci_poll_in_ms(uint32_t ms) {
65+
poll_timer_id = add_alarm_in_ms(ms, mp_bluetooth_hci_timer_callback, NULL, true);
66+
}
67+
68+
// For synchronous mode, we run all BLE stack code inside a scheduled task.
69+
// This task is scheduled periodically via a timer, or immediately after UART RX IRQ.
70+
STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) {
71+
(void)none_in;
72+
events_task_is_scheduled = false;
73+
// This will process all buffered HCI UART data, and run any callouts or events.
74+
mp_bluetooth_hci_poll();
75+
return mp_const_none;
76+
}
77+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_scheduled_task);
78+
79+
// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to
80+
// request that processing happens ASAP in the scheduler.
81+
void mp_bluetooth_hci_poll_now(void) {
82+
if (!events_task_is_scheduled) {
83+
events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none);
84+
if (!events_task_is_scheduled) {
85+
// The schedule queue is full, set callback to try again soon.
86+
mp_bluetooth_hci_poll_in_ms(5);
87+
}
88+
}
89+
}
90+
91+
mp_obj_t mp_bthci_uart;
92+
93+
int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
94+
debug_printf("mp_bluetooth_hci_uart_init\n");
95+
96+
mp_obj_t args[] = {
97+
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_BLE_UART_ID),
98+
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_BLE_UART_BAUDRATE),
99+
MP_OBJ_NEW_QSTR(MP_QSTR_flow), MP_OBJ_NEW_SMALL_INT((1 | 2)),
100+
MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_OBJ_NEW_SMALL_INT(1000),
101+
};
102+
103+
mp_bthci_uart = machine_uart_type.make_new((mp_obj_t)&machine_uart_type, 2, 2, args);
104+
MP_STATE_PORT(mp_bthci_uart) = mp_bthci_uart;
105+
106+
// Start the HCI polling to process any initial events/packets.
107+
mp_bluetooth_hci_start_polling();
108+
return 0;
109+
}
110+
111+
int mp_bluetooth_hci_uart_deinit(void) {
112+
debug_printf("mp_bluetooth_hci_uart_deinit\n");
113+
114+
// If a poll callback is set cancel it now.
115+
if (poll_timer_id > 0) {
116+
cancel_alarm(poll_timer_id);
117+
}
118+
poll_timer_id = 0;
119+
return 0;
120+
}
121+
122+
int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) {
123+
debug_printf("mp_bluetooth_hci_uart_set_baudrate(%lu)\n", baudrate);
124+
return 0;
125+
}
126+
127+
int mp_bluetooth_hci_uart_any(void) {
128+
int errcode = 0;
129+
const mp_stream_p_t *proto = (mp_stream_p_t *)machine_uart_type.protocol;
130+
131+
mp_uint_t ret = proto->ioctl(mp_bthci_uart, MP_STREAM_POLL, MP_STREAM_POLL_RD, &errcode);
132+
if (errcode != 0) {
133+
error_printf("Uart ioctl failed to poll UART %d\n", errcode);
134+
return -1;
135+
}
136+
return ret & MP_STREAM_POLL_RD;
137+
}
138+
139+
int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) {
140+
debug_printf("mp_bluetooth_hci_uart_write\n");
141+
142+
int errcode = 0;
143+
const mp_stream_p_t *proto = (mp_stream_p_t *)machine_uart_type.protocol;
144+
145+
mp_bluetooth_hci_controller_wakeup();
146+
147+
if (proto->write(mp_bthci_uart, (void *)buf, len, &errcode) < 0) {
148+
error_printf("mp_bluetooth_hci_uart_write: failed to write to UART %d\n", errcode);
149+
}
150+
return 0;
151+
}
152+
153+
// This function expects the controller to be in the wake state via a previous call
154+
// to mp_bluetooth_hci_controller_woken.
155+
int mp_bluetooth_hci_uart_readchar(void) {
156+
debug_printf("mp_bluetooth_hci_uart_readchar\n");
157+
if (mp_bluetooth_hci_uart_any()) {
158+
int errcode = 0;
159+
uint8_t buf = 0;
160+
const mp_stream_p_t *proto = (mp_stream_p_t *)machine_uart_type.protocol;
161+
if (proto->read(mp_bthci_uart, (void *)&buf, 1, &errcode) < 0) {
162+
error_printf("mp_bluetooth_hci_uart_readchar: failed to read UART %d\n", errcode);
163+
return -1;
164+
}
165+
return buf;
166+
} else {
167+
debug_printf("mp_bluetooth_hci_uart_readchar: not ready\n");
168+
return -1;
169+
}
170+
}
171+
172+
// Default (weak) implementation of the HCI controller interface.
173+
// A driver (e.g. cywbt43.c) can override these for controller-specific
174+
// functionality (i.e. power management).
175+
MP_WEAK int mp_bluetooth_hci_controller_init(void) {
176+
debug_printf("mp_bluetooth_hci_controller_init (default)\n");
177+
return 0;
178+
}
179+
180+
MP_WEAK int mp_bluetooth_hci_controller_deinit(void) {
181+
debug_printf("mp_bluetooth_hci_controller_deinit (default)\n");
182+
return 0;
183+
}
184+
185+
MP_WEAK int mp_bluetooth_hci_controller_sleep_maybe(void) {
186+
debug_printf("mp_bluetooth_hci_controller_sleep_maybe (default)\n");
187+
return 0;
188+
}
189+
190+
MP_WEAK bool mp_bluetooth_hci_controller_woken(void) {
191+
debug_printf("mp_bluetooth_hci_controller_woken (default)\n");
192+
return true;
193+
}
194+
195+
MP_WEAK int mp_bluetooth_hci_controller_wakeup(void) {
196+
debug_printf("mp_bluetooth_hci_controller_wakeup (default)\n");
197+
return 0;
198+
}
199+
200+
#endif // MICROPY_PY_BLUETOOTH

ports/rp2/mpbthciport.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
#ifndef MICROPY_INCLUDED_RP2_MPBTHCIPORT_H
27+
#define MICROPY_INCLUDED_RP2_MPBTHCIPORT_H
28+
29+
// Initialise the HCI subsystem (should be called once, early on).
30+
void mp_bluetooth_hci_init(void);
31+
32+
// Poll the HCI now, or after a certain timeout.
33+
void mp_bluetooth_hci_poll_now(void);
34+
void mp_bluetooth_hci_poll_in_ms(uint32_t ms);
35+
36+
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
37+
// Request new data from the uart and pass to the stack, and run pending events/callouts.
38+
// This is a low-level function and should not be called directly, use
39+
// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead.
40+
void mp_bluetooth_hci_poll(void);
41+
42+
#endif // MICROPY_INCLUDED_RP2_MPBTHCIPORT_H

ports/rp2/mpconfigport.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ extern const struct _mp_obj_module_t mp_module_rp2;
171171
extern const struct _mp_obj_module_t mp_module_uos;
172172
extern const struct _mp_obj_module_t mp_module_utime;
173173

174+
#if MICROPY_PY_BLUETOOTH
175+
#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH struct _machine_uart_obj_t *mp_bthci_uart;
176+
#else
177+
#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH
178+
#endif
179+
180+
#if MICROPY_BLUETOOTH_NIMBLE
181+
struct _mp_bluetooth_nimble_root_pointers_t;
182+
struct _mp_bluetooth_nimble_malloc_t;
183+
#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE struct _mp_bluetooth_nimble_malloc_t *bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers;
184+
#else
185+
#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE
186+
#endif
187+
174188
#define MICROPY_PORT_BUILTIN_MODULES \
175189
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \
176190
{ MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \
@@ -190,6 +204,8 @@ extern const struct _mp_obj_module_t mp_module_utime;
190204
void *rp2_uart_rx_buffer[2]; \
191205
void *rp2_uart_tx_buffer[2]; \
192206
MICROPY_BOARD_ROOT_POINTERS \
207+
MICROPY_PORT_ROOT_POINTER_BLUETOOTH \
208+
MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE \
193209

194210
#define MP_STATE_PORT MP_STATE_VM
195211

0 commit comments

Comments
 (0)