-
-
Notifications
You must be signed in to change notification settings - Fork 80
[FYI] Further ev3 bluetooth-related changes #405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Keeping pull request under around 500 lines changed or so definitely makes them easier to review. 😄
You can thank Laurens for making it easy to use. So yes, use it, that is what it is there for.
Can you give an example of what you mean? We don't really have an RTOS, just cooperative multitasking using coroutines. Usually, the mechanism is that we receive an interrupt letting us know that data is ready. In the interrupt handler, we call a process poll function to let a "process" (just a coroutine) know that something change and that it should run to actually do something with the data that was received.
Could have fooled me. 😁 |
btstack may send us buffers longer than 256 bytes to pass to the uart. It should be relatively low-cost to increase the length of the read and write buffers to accomodate this.
This commit creates a UART block implementation for btstack on the EV3. This will allow us to communicate HCI commands from the btstack library to the EV3's bluetooth module. The commit also contains necessary changes to makefiles to allow compiling this module.
This adds the CTS and RTS pin definitions for UART2. It also configures the "slow clock" input for the bluetooth module. Configures the BTnShutdn gpio. Enables the ECAP functions in the PSC.
Adds the definitions for the platform data necessary to hook btstack up to the ev3's bluetooth module.
ba10bc8 to
f0565c5
Compare
So, for example, I was originally just posting this as an FYI, with the intention to eventually split it into 3 different requests. So what I'm wondering is whether this pull request already represents the right granularity, or if I should indeed go ahead and split it up. It sounds like you're saying this is already about the right granularity and we can just go forward with this. Anyway, going forward, if I send stuff that's hard for y'all to review please don't hesitate to correct me.
I'm referring to this code. What it does is it adds a hook that the btstack run loop will poll to check if the DMA for the previous write is complete. In this pull request, I instead do what you're describing e.g. in btstack_uart_block_ev3_receive_block, bypassing the data_source_handler mechanism in the btstack runloop. (And the interrupt to hear if the DMA is complete is handled by the pbio uart implementation, in the form of completing the child async op in
Very kind of you to say. |
|
To be clear I know how to set up the pinmux for a given pin if I know which
bank it’s in. But I don’t know which pins are 51,78 and 87. For example,
is 51==3[3] (16*3+3)?
…On Sat, Oct 25, 2025 at 11:43 AM David Lechner ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In lib/pbio/platform/ev3/platform.c
<#405 (comment)>
:
> const pbdrv_gpio_t bluetooth_uart_rx = PBDRV_GPIO_EV3_PIN(4, 19, 16, 1, 3);
const pbdrv_gpio_t bluetooth_uart_tx = PBDRV_GPIO_EV3_PIN(4, 23, 20, 1, 2);
+ const pbdrv_gpio_t bluetooth_uart_cts = PBDRV_GPIO_EV3_PIN(0, 31, 28, 0, 8);
To find the magic numbers to put into the PBDRV_GPIO_EV3_PIN() macro, you
have to find e.g. GP0[5] in the pinmux tables in the technical reference
manual.
image.png (view on web)
<https://github.com/user-attachments/assets/23d63233-e66d-4bcc-9833-015b2e33b891>
This would translate to PBDRV_GPIO_EV3_PIN(1, 11, 8, 0, 5). 1, 11, 8,
comes from PINMUX1_11_8 and 0, 5 comes from GP0[5].
You can also avoid having to look in the TRM by searching for GPIO0_5 in
lib/tiam1808/tiam1808/hw/hw_syscfg0_AM1808.h and you will find
SYSCFG_PINMUX1_PINMUX1_11_8_GPIO0_5 which has the same info.
but according to that device configuration, it's gpio 5 from the
perspective of ev3dev
No, these are two different pins. BTSLOWCLK is not used as a GPIO, but
rather as the ECAP2_PWM2 pulse width modulation output (each pad on the CPU
has a multiplexer to pick one of several pin functions). GP0[5] is BTCLKDIS
which is a different signal. If BTCLKDIS is configured as a GPIO and pulled
low, it will essentially turn off the Bluetooth clock even if the PWM
output is still on.
Technically, we could just not do anything with the BTCLKDIS and CTS_PIC
pins. The gpio-hog in Linux is there to make sure no one can change them
from userspace. But they are set to input and all pins are already high
impedance by default. But we aren't too limited on flash memory on EV3 so
it wouldn't hurt to set them in code anyway just as a matter of
documentation.
—
Reply to this email directly, view it on GitHub
<#405 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGDGTDDPQAN33UEYWNOOQT3ZPAFFAVCNFSM6AAAAACJ5GFRHOVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZTGOBQGE4TKMBUHA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Oh, right. Yes, just do floor division by 16 to get the bank and the remainder is the pin. You can also verify on the schematic, e.g. PIC_EN is EMA_D11/GP3_3 on the schematic which matches bt-pic-en-hog with gpios = <51 ...> in the devicetree. |
If it works, then it sounds like a nice simplification. 👍 |
The new pins are copied from the old ev3dev sources. These pins are responsible for enabling or disabling a separate PIC microcontroller that is not needed for usual bluetooth communication.
The PB_LIB_BTSTACK library has to be compiled with different sources if it is being used to drive a classic module vs. a dual or LE module. Per PR feedback, adjust the PB_LIB_BTSTACK variable to signal which state we are in, rather than using separate PB_LIB_BTSTACK and PB_LIB_BTSTACK_CLASSIC variables.
|
In terms of verification, all I've done is upload this to my EV3 and ensure it can run "hello, world", which it can. And all the other bricks still build. In my next pull request I will install a classic-only run loop for btstack and ensure that we can do a basic hello-world operation with the bluetooth module. Probably y'all will also want me to fix up the history a bit once the review is done. Let me know if that's desired. |
I just returned from a short trip so probably I haven't followed this closely enough yet. We're very happy with contributions like this, so thanks for working on it!
Making submissions for feedback is fine, and some standalone changes like the UART size argument can be merged if they are tested independently. We may be a bit slower to merge driver code for something that might not get used, so here a demonstration would be great. |
Totally understandable. I feel I’m pretty close to having a working demo, so I will hold off on sending anything else until things are demonstrably working. |
This is awesome. If you'd asked me last year, I would never have imagined that the very first EV3 release might have anything Bluetooth related at all 😄 By the way, if you want some quick hooks from Python into C without learning all the API details just yet, you can modify experimental_hello_world as needed. It is used like this: from pybricks.experimental import hello_world
result = hello_world(foo=5, bar="hi")
print("the square of foo is", result)To add more functions, copy that function below and adjust as needed. Then add it to the table here. |
Hah. Yeah, well, I'm to the point where if the code were all working correctly I would be able to get some basic local address info across the HCI interface all the way up to the Python layer. But, Pybricks is not starting up, so the code isn't all working. So, it's time to do some printf debugging. We'll see where that gets us. Turns out I don't have a USB uart thingy, so I whipped one up with an ESP32 I had lying around. I'll try to hook it up and see what's being printed tonight. |
|
Status report:
What seems to be the problem is that UART2 is reporting buffers containing zero in response to Fortunately, the manual for the CC2560 has a detailed startup sequence and I guess I can use it to figure out if the thing is turned on properly. I'm going to try that tomorrow. Any helpful insights for further debugging would be welcome. My janky UART->PC setup: Logfile: |
|
To start simple, is there some kind of basic command we can send/receive to verify that UART is working at that the Bluetooth chip is up and running? Like some HCI command to read a vendor string. That might help test the basics before hooking it all up to btstack. For additional debugging, we could expose this UART to the user code (just like the UARTs on S1 through S4 can be used) so you can poke at it from Python. Let me know if I should set this up.
If it works, it works! But if you want to buy this one, we would gladly refund the expense. |
Yep, that's ultimately what I'm trying to get set up as my hello world demo for this pull request or the next one. The simplest HCI command is read_local_version_information, I think, and I have that command wired all the way through to Python. However, we don't even get that far. In the log, the problem is more fundamental. We are sending the initial HCI power-on packet, and we can't get back a single valid packet from the bluetooth module. I'm assuming that either I've configured some pin wrong, the UART is configured wrong, or the startup sequence is busted somehow. If you look at the cc2560 manual, section 5.7.1.2, it seems there's a very specific order things need to happen. My next goal is to make sure things are happening in this order and ensure that the RTS wire is being pulled low before continuing with startup.
Hah, thank you, but there's no need now that I have this thing working. |
|
Okay, actually, no worries. I've found the bts files that @dlech originally uploaded to the linux firmware git repo. I have also found the convert_bts_init_scripts.py file in the btstack library. I think I can bump these two things together until something good comes out. |
It's useful to have a vprintf version of the uart_debug functions. This will be used in a later change to assist with logging messages originating in BTStack. Also, there was a bug where the error from writing to the UART was not captured. This fixes that bug and logs the error on the next attempt to use the port.
Cleared the final bug to get the HCI interface up and running. The baudrate was too high! |
During debugging, found a bunch of errors with the EV3 UART block impl. - `block_received` and `block_sent` are written before open, therefore, we have to set them in init, not in open. - Read thread passed wrong state to pbdrv_uart_read. - Safer shutdown sequence.
f7fde9e to
fac4394
Compare
The EV3 was released with two different revisions of the CC2560 bluetooth module. Therefore we cannot rely on a statically compiled bluetooth init script. We include both init scripts in the firmware to be selected dynamically at module startup time.
The classic runloop is separated from the LE one because little code will be shared between them. For now, it's just a skeleton.
fac4394 to
98c13bf
Compare
|
I'm kicking myself for not remembering this sooner, but once upon a time we tried running Pybricks on ev3rt, with this library. We ended up not using it for various reasons but it is still a useful inspiration for some driver configurations. Notably, it uses BTstack. Some pointers include:
A note of caution with this code base: Just because something is there, doesn't mean it is used/included/built. There are two different versions of That said, it did work. So another insightful approach could be to build it and see what the UART is sending and reporting, to verify your own work. Also, please see this article. There are different EV3 brick versions containing different Bluetooth chips. @dlech probably remembers this better, but I thought the EV3 brick hardware version was still the same, so maybe we can determine the variant based on what the chip is saying. EDIT: I hadn't seen your latest replies when I wrote this. Looks like you have found some resources already. Nice work! |
|
Possibly worth spinning off to a separate discussion... But there are some chips This happened with the Pebble smartwatch:
|
Yep, this is implemented in the code as it stands. Have a gander at bluetooth_btstack_classic.cc around where we test if
That's very helpful! I will read the rfcomm and spp code especially. Now that we have communication with the chip up getting things working should be much simpler, but it's always nice to have a well let trail. |
FWIW, my 2560 could not load the firmware for the 2560a. At least, BTStack choked on it. Maybe there was a way to bypass the btstack objection, but I didn't look further, since I only really care about EV3<->EV3 communication at the moment. |
To clarify, EV3 variants with 2560 and 2560a both exist and would eventually need to be tested and supported. We can start with the one you have, and we can dust off our collection to find and test the other one. (It is the 2564 hack I was curious about, but this can be saved for another time. If at all, it would only work on the 2560a, as per the reference above. It is probably confusing to have different end-user experiences anyway, so we will probably not do this even if it worked.) |
|
The two version of the chip in EV3 are CC2560 and CC2560A. Only the later is Bluetooth 4.0. And the "firmware" files are really only patch files, not full firmware - that is why the chips have a "ROM version". I have only seen BLE enabled firmware for CC2560B and CC2560C, so I still have some doubts that it could work on CC2560A because of that. Reference: https://git.ti.com/cgit/ti-bt/service-packs/about/ |
|
Status update: Demo code: from pybricks.hubs import EV3Brick
from pybricks.tools import run_task
print("Hello, World!")
brick = EV3Brick()
print("EV3Brick fields: ", dir(brick))
print("Brick.btc fields: ", dir(brick.btc))
print("Scan function: ", dir(brick.btc.scan))
async def scan():
print("Start bluetooth scan...")
devices = await brick.btc.scan()
print("Scan complete, devices found: ")
for device in devices:
print(device)
run_task(scan())This is just my iphone in discoverable mode. I don't really know what's up with the CoD here -- it seems wrong. And the name field should be populated unless I'm missing something. More work and debugging to do, but we are making progress. Question: at what feature-point should this pull request be wrapped and polished? I was thinking that of implementing |
If at any point there is an error in the UART layer, we can't continue using the connection. For now, just return the error. In the future, we need to reset the bluetooth module if this happens.
The classic driver now has a single packet handler that deals with all events from the module. We also implement a scan API. As of this commit, there still appear to be some bugs with the scan correctly picking up the name of the scanned devices.
The bluetooth classic object currently only supports scanning for devices. A future commit will add RFCOMM connection support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made 2 inline comments of some things I think we should do first. (And of course, we need to rebase this to fix the merge conflicts.)
Then I would be OK with merging this with just one basic function to read e.g. the version info from the chip so we can see that it is working.
Figuring out what we will want the actual Python API to be will take some thought, but ideally it would be (mostly) compatible with https://pybricks.com/ev3-micropython/messaging.html. That can be a discussion for another day.
And we will need to figure out a user interface for the EV3 so that we can do things like turn Bluetooth on and off. (That is being discussed elsewhere.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file name should not have x in it. It won't work with CC2560A. We will need a 2nd init script for that one based on TIInit_6.6.15.bts. And we will have to find a way to read info from the chip to pick the right one at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will work with the CC2560A, scroll down to cc2560a_init_script and cc2560a_init_script_size. pbdrv_bluetooth_get_init_script selects the correct init script given the lmp subversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah-ha! I missed that detail.
| typedef struct _hubs_EV3Brick_obj_t { | ||
| mp_obj_base_t base; | ||
| mp_obj_t battery; | ||
| #if PYBRICKS_PY_COMMON_BTC |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not put this in EV3Brick yet until we figure out what the actual API is going to be. Instead, let's just use pybricks.experimental with just functions, no objects yet, for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
Sure. My thinking was that we could implement in C only what is needed to make the RFCOMM connections, and then in Python it would be pretty trivial to implement the mailbox API on top of the lower level RFCOMM API.
Where is elsewhere? I don't necessarily desire to have a say, but I'd like to know what is being discussed so that I can make sure not to design a huge problem into the API I'm working on. |
EV3 UI design/planning: pybricks/support#2360 |
Wherever it makes sense, we often start making it available through Python code. This usually helps us figure out what the best way is to split between user code and system code. So I wouldn't worry about future UIs for now. Code isn't all that rigid as it might seem, and we tend to refactor things over time (we really do tend to get to those But I appreciate that some guidance would help. Will try and brainstorm some suggestions for APIs. |
|
I've been thinking about APIs a bit. I was typing a message here, but we can probably discuss it in pybricks/support#2274 (comment) instead and keep this PR more about the initial proof of concept of RFCOMM. Feedback and ideas welcome! I think the API proposed there could also be the backbone of the old |

This pull request is just a heads up, so that y'all can tell me if I'm barking up the wrong tree. It's my intention to break these commits into separate requests as each parent commit gets approved. (I don't know if this is the style you all prefer to review code -- at work, we are told to make small, self-contained changes, and I intend to proceed in that mode unless told otherwise.)
I have a few questions about what I'm doing here:
I'll be on vacation from tomorrow to Monday, so I probably won't make any further progress until Tuesday next week.