-
-
Notifications
You must be signed in to change notification settings - Fork 45
Draft demonstration of RFCOMM support #100
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
| hub.light.on(Color.RED) | ||
| conn = None | ||
| try: | ||
| conn = await rfcomm_connect(TARGET_BLUETOOTH_ADDRESS) |
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.
If we are going to use it like this, we should make the connection object a context manager, so we can simply use a with statement instead of try/finally.
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.
Although I do prefer this functional style of rfcomm_connect() creating a connection object in general, it doesn't match our established pattern of a more object-oriented approach in Pybricks.
I.e. we would have rfcomm = Rfcomm() in the setup phase of the program (address could be here or later). And this object would have connect(), wait_for_connection() and disconnect() (or close()) methods.
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.
R.e. context manager: I agree. I will note it as a TODO before we mark the PR as mergable.
R.e. the functional vs. object style: Initially I was going to submit this as a purely experimental API. But since it seems like this might be something we can actually launch, I will convert this to an object style API as you suggest. It might actually make the context manager a little less complicated, since you can do
with RFCOMMSocket() as sock:
await sock.connect(...)As opposed to
async with rfcomm_connect(...) as sock:
...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 wasn't saying that we have to do it the object oriented way. But we should consider how this will fit into the block programming language as well.
with RFCOMMSocket() as sock:
This is actually not ideal when used in a loop because it will create a new object on each loop. Maybe I was too quick to suggest using a context manager.
Our usual pattern is to declare all objects globally at the beginning of the program (setup stack in the block language).
Another thing that has come up in the past is a need to set the actual channel being used. E.g. when Windows connects to a Bluetooth device with serial service, it automatically sets up 2 RFCOMM channels, one for incoming and one for outgoing, so we have to put in the right channel number for that.
So my first thought would be to do something like:
hub = EV3Brick()
comm0 = RFCOMMChannel(0)I'm a little tempted to include the address in the constructor as well, but there are different rules depending on if you are connecting or listening.
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.
This is actually not ideal when used in a loop because it will create a new object on each loop. Maybe I was too quick to suggest using a context manager.
We should expect the need to reconnect to be very rare. I think it's probably okay.
E.g. when Windows connects to a Bluetooth device with serial service, it automatically sets up 2 RFCOMM channels, one for incoming and one for outgoing, so we have to put in the right channel number for that.
I didn't notice that behavior when I was messing with it. I had been under the impression that you can send and receive with the same rfcomm channel. AFAICT when you open a socket with python in Windows you only get one rfcomm channel created on the server. I did also create "ping-pong" test scripts on Windows and both when acting as a server and client, it sends and receives messages on the same rfcomm channel (from btstack's perspective).
I actually have removed myself from Windows lately but I would be interested to hear more about this.
comm0 = RFCOMMChannel(0)
Currently, the code does not allow the user to select which channel to address. This is a problem that we'll want to fix in a later update.
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.
Understood. FWIW, I was able to talk to Windows with this. One drawback of the current code is that it will randomly select among the available channels windows advertises over SDP (picking 1 if possible). It would not be a bad thing to give the users an option of picking a specific channel.
Personally I would advise our users to steer clear of outgoing com ports on Windows. Just use Python with socket(AF_BLUETOOTH) instead, IMO.
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.
Personally I would advise our users to steer clear of outgoing com ports on Windows. Just use Python with socket(AF_BLUETOOTH) instead, IMO.
Sure. Windows is still going to create the COM ports though, which ties up a couple of the RFCOMM channels whether you use the COM ports or not. So if we can make it just work without an explicit channel and avoid the ones Windows uses, all the better. It would be nice if could still talk to NXT/EV3 running official LEGO firmware too though, which might require specifying a specific channel.
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.
Sounds good. I'm down to add specifying a channel explicitly. And likewise on the server.
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 implemented the RFCOMMSocket object per your original suggestion. This seems to be the python/micropython way. I implemented context manager, but its usage is optional. Still haven't done the manual channel selection stuff -- let's finish the review on the main body of the code first and once everyone is satisfied with what is already here I can tack that on.
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.
Thanks for the updates! I haven't been able to follow all updates this weekend.
Could we make some mock examples of end user code with the various approaches in pybricks/support#2274?
This new API is what we intend to be using going forward.
Use context manager in remote_control.
Use new UARTDevice-inspired API.
No description provided.