pybricksdev icon indicating copy to clipboard operation
pybricksdev copied to clipboard

Pybricksdev changes for Remote Control based on Pybrickdev-Demo

Open johnscary-ev3 opened this issue 3 years ago • 7 comments

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())

johnscary-ev3 avatar Jun 21 '22 04:06 johnscary-ev3

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'

johnscary-ev3 avatar Jun 21 '22 05:06 johnscary-ev3

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())

dlech avatar Jun 21 '22 16:06 dlech

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

johnscary-ev3 avatar Jun 21 '22 18:06 johnscary-ev3

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

dlech avatar Jun 21 '22 19:06 dlech

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.

dlech avatar Jun 21 '22 19:06 dlech

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!

johnscary-ev3 avatar Jun 21 '22 21:06 johnscary-ev3

It is about 2600 lines of Python code.

:open_mouth:

That's pretty big.

dlech avatar Jun 21 '22 21:06 dlech