Pybricksdev changes for Remote Control based on Pybrickdev-Demo
Hi guys
I am using pybricksdev to do remote control of RI robot. I developed this from pybricksdev-demo a while back. It was working fine but then I updated the pybrickdev library and seems like alot of changes happened and does not work anymore. Mostly related to how we open the hub and check status now. Looks like PybricksHub does not exist now and hub.program_running also and maybe some other hub methods. Below is code that was working but now does not.
Can you give me some comments, examples, or other docs that might help me get upgraded to new lib?
from pybricksdev.connections import PybricksHub
from pybricksdev.ble import find_device
from asyncio import gather, sleep, run
import msvcrt
async def main():
print('main: Start')
hub = PybricksHub()
# You can search for the address like this:
address = await find_device()
await hub.connect(address)
await gather(
hub.run('robot_Blast.py', print_output=True),
forwarder(hub)
)
# Disconnect from the hub
await hub.disconnect()
print('main: Stop')
async def forwarder(hub):
print("forwarder: Start")
# Give the hubs some time to start
while not hub.program_running :
#print('forwarder: Waiting for Hub to Run')
await sleep(2)
print('forwarder: Hub Running')
# Keyboard command input loop
while hub.program_running :
# Non blocking keyboard Input
if msvcrt.kbhit():
# get char with or without echo
command = msvcrt.getch()
# clear any extra characters
while msvcrt.kbhit():
msvcrt.getch()
else:
command= ''
# Try to send to the receiving hub
if len(command) > 0:
try:
await hub.write(bytes([ord(command)]))
except:
pass
# wait some
await sleep(.1)
# Hub has stopped
print('forwarder: Hub Not Running')
#start it up
run(main())
I found that PybricksHub moved so this import works now but other calls still give errors as below. Thanks.
from pybricksdev.connections.pybricks import PybricksHub
File "c:/Users/johns/pybricksdev-demo/RemoteControl_Blast.py", line 27, in forwarder
while not hub.program_running :
AttributeError: 'PybricksHub' object has no attribute 'program_running'
How about something like this?
import asyncio
import platform
import sys
try:
import msvcrt
except ModuleNotFoundError:
pass
try:
import termios
import tty
except ModuleNotFoundError:
pass
from pybricksdev.connections.pybricks import PybricksHub, StatusFlag
from pybricksdev.ble import find_device
def read_key():
if platform.system() == 'Windows':
return msvcrt.getch()
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
async def main():
print('main: Start')
hub = PybricksHub()
device = await find_device()
await hub.connect(device)
try:
forwarder_task = asyncio.create_task(forwarder(hub))
try:
await hub.run('robot_Blast.py')
finally:
forwarder_task.cancel()
finally:
# Disconnect from the hub
await hub.disconnect()
print('main: Stop')
async def forwarder(hub: PybricksHub):
print("forwarder: Start")
queue = asyncio.Queue()
# wait for user program on the hub to start
with hub.status_observable.subscribe(lambda s: queue.put_nowait(s)):
while True:
status = await queue.get()
if status & StatusFlag.USER_PROGRAM_RUNNING:
break
print('forwarder: Hub Running')
loop = asyncio.get_running_loop()
# Keyboard command input loop
while True:
command = await loop.run_in_executor(None, read_key)
# Try to send to the receiving hub
try:
await hub.write(bytes([ord(command)]))
except:
pass
#start it up
asyncio.run(main())
Thanks so much for the help. I can see from your example and reading the latest pybricksdev code that you have reworked how we get hub status now using the status queue to get status updates. Seems like a reasonable way to get different status codes.
On your example code, I tried to run it. I don't get any errors, but it seems like it downloads and starts my robot code ok but then immediately drops to the hub.disconnect and stops. The robot side program keeps running fine but with no connection to PC side. I tried adding a wait to the hub.run call but it made no difference as wait=True is default anyway.
Any ideas on this?
PS C:\Users\johns\pybricksdev-demo> & c:/Users/johns/pybricksdev-demo/.venv/Scripts/python.exe c:/Users/johns/pybricksdev-demo/RemoteControl_example_June_2022
main: Start
forwarder: Start
100%|███████████████████████████████████████████████████████████████| 36.1k/36.1k [00:45<00:00, 790B/s]
main: Stop
I have tested my code on both Windows and Linux with the the following substitute for robot_Blast.py:
import usys
from pybricks import version
print('hub program started')
print(version)
while True:
print('received', usys.stdin.read(1))
And it produces the following output when keys are pressed:
main: Start
forwarder: Start
100%|██████████████████████████████████████████████████████████████████████████████████████████| 144/144 [00:00<00:00, 716B/s]
hub program started
forwarder: Hub Running
('technichub', '3.2.0b1', 'v3.2.0b1-28-g8c1d7f77 on 2022-06-14')
received a
received d
received f
received
received H
It looks like there is a possible problem with the way waiting for a user program running works in pybricksdev. I just published v1.0.0-alpha.27 with an increased timeout that may help if this is the issue.
I tried your short robot side program and it worked fine as you mentioned.
Then saw your post about update v1.0.0-alpha.27 so I updated to that and tried your PC side program with my larger robot side program, and it works fine now. So that timeout must have been the issue. My robot program can take almost 60 seconds to download and start up. It is about 2600 lines of Python code. Not sure how that compares to what others may have with respect to download times, but the new timeout seems to cover it.
Thanks again for all the help on this!
It is about 2600 lines of Python code.
:open_mouth:
That's pretty big.