Keyboard ⌨️ Control

Try Your Hand at Driving RVR+/RVR with Your Keyboard

One of the best ways to get started with your RVR+/RVR is by learning to drive using your keyboard! Gamers rejoice, as your beloved ASDW keys become your "keys" to letting your RVR+/RVR know where it is you want it to go! 🏎️ 

In this tutorial, you'll use a Raspberry Pi mounted on your RVR+/RVR, as well as your keyboard (to avoid getting tangled up in wires galore ➰, we would recommend that you SSH into your Raspberry Pi, though you could do this with a keyboard wired into your Raspberry Pi) to control your RVR+/RVR using an asynchronous python script 🕹️.

If you haven't already connected your Raspberry Pi to your RVR+/RVR, go ahead and check out our helpful docs on how to get up and running before attempting to follow along with this example (doing so will get most of the hardware setup completed already, as well 😉).

This intermediate tutorial should take about 20 minutes to complete (if you've already set up your Raspberry Pi and connected it to the RVR+/RVR).

Hardware Setup

In addition to the materials necessary to connect the Raspberry Pi to the RVR+/RVR, we'll need:

Software Setup

If you've gone through our Raspberry Pi Set Up Docs, you will have already downloaded/cloned the needed code for this example, in addition to installed the dependencies necessary for your Raspberry Pi to communicate with your RVR+/RVR. Because the Raspberry Pi examples we have set up rely on files located throughout the file structure (not just the file we walk through below), we recommend that you use the version of the file included in those you downloaded/cloned from our GitHub, rather than trying to write the file from scratch as we walk through it together below (with that said, feel free to play with/add to the version you downloaded; we always love to see how creative our users are 🥰).

You can find the file for this example at 👩‍💻/projects/keyboard_control/drive_with_wasd_keys.py, in the file structure you downloaded; we'll repeat this later as we talk about how to actually run the code, but figured you might like to have the file in front of you as you follow along here...

Let's get started with our imports. Most of these will be similar across all of our examples. One of the elements that is unique about this example is the KeyboardHelperimport. Keyboard Helper is a helper file 🙋‍ that we created to simplify the code in this file that references keys on your keyboard, making communicating with the RVR+/RVR via the keyboard a bit cleaner and easier. You can find the full KeyboardHelper code here.

Line 3 of the code below is what allows us to pull from elsewhere in the file structure. It references files that are two layers above (../../), as well as those within folders at that level, and appends the absolute path of the directory with the name after "from" in the imports on lines 7-9:

import sys

import os

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')))


import asyncio


from projects.keyboard_control import KeyboardHelper

from sphero_sdk import SerialAsyncDal

from sphero_sdk import SpheroRvrAsync



Next, we'll initialize our global variables, including a specific asynchronous loop 🔄 and defining rvr 🤖 so that we can use it to call commands off of, later on:

key_helper = KeyboardHelper()

break_loop = False

red = [0xFF, 0x0, 0x0]

blue = [0x0, 0x0, 0xFF]

driving_keys = [119, 97, 115, 100, 113, 32]

speed = 64


loop = asyncio.get_event_loop()

rvr = SpheroRvrAsync(

    dal=SerialAsyncDal(

        loop

    )

)



As we dive into the actions, we'll start by setting some colors 🎨 Our first function will set all of the LEDs:

async def set_all_leds(rgb_triples):

    await rvr.set_all_leds_with_32_bit_mask(

        0x3FFFFFFF,

        rgb_triples

    )

    await asyncio.sleep(.01)



Second, we'll set some functions to to flip the LEDs back and forth between blue and red:

def construct_blue():

    global red

    global blue

    colors = []

    for x in range(10):

        if (x % 2) == 0:

            colors.extend(red)

        else:

            colors.extend(blue)

    return colors



def construct_red():

    global red

    global blue

    colors = []

    for x in range(10):

        if (x % 2 - 1) == 0:

            colors.extend(red)

        else:

            colors.extend(blue)

    return colors



We'll also create a function to cause the lights to flash red and blue 🚔, using our set_all_leds method:

async def strobe_lights():

    lights_red = True

    while True:

        lights_red = not lights_red

        if lights_red:

            rgb_red = construct_red()

            await set_all_leds(rgb_red)

        else:

            rgb_blue = construct_blue()

            await set_all_leds(rgb_blue)

        await asyncio.sleep(0.25)




Because the RVR+/RVR has treads on each side, we can set the mode and speed of each, using raw_motors. The following two functions control starting and stopping this function; you'll notice when we stop, we set all values to 0:

async def run_raw_motors(left_mode, left_speed, right_mode, right_speed):

    await rvr.raw_motors(left_mode, left_speed, right_mode, right_speed)



async def stop_raw_motors():

    await rvr.raw_motors(0, 0, 0, 0)



We get to drive now!! The following function allows you to assign keys (in this case, W ☝️, A 👈, S 👇 and D 👉) to control the direction that the RVR+/RVR heads in, by attaching it to the run_raw_motors method we defined above, associating a different heading with each key. The speed was set in the global variables we initiated in the beginning and can be reassigned there. The following listens for one of the assigned keys and reacts with the corresponding behavior:

async def drive():

    global loop

    global break_loop

    global speed


    while key_helper.key_code not in driving_keys:

        await asyncio.sleep(0.05)


    print('Drive with key code: ', str(key_helper.key_code))


    if key_helper.key_code == 119# W

        await run_raw_motors(1, speed, 1, speed)

    elif key_helper.key_code == 97# A

        await run_raw_motors(2, speed, 1, speed)

    elif key_helper.key_code == 115# S

        await run_raw_motors(2, speed, 2, speed)

    elif key_helper.key_code == 100# D

        await run_raw_motors(1, speed, 2, speed)

    elif key_helper.key_code == 113:

        break_loop = True

    elif key_helper.key_code == 32# SPACE

        await stop_raw_motors()


    key_helper.key_code = -1



The following three functions are more "functional" than "fun" 🤓, but are still very important! They allow us to wake the RVR+/RVR up when we are ready to start interacting with it (since we can ask the RVR+/RVR for information about itself when it is in soft sleep, but we need to wake the RVR+/RVR it before making any mechanical requests), maintain our ability to interact with the bot and flash its lights, and know when to continue and when to stop:

async def main():

    await rvr.wake()

    while True:

        await drive()



def run_loop():

    global loop

    loop.run_until_complete(

        asyncio.gather(

            main(),

            strobe_lights()

        )

    )



if __name__ == '__main__':

    loop.run_in_executor(None, key_helper.get_key_continuous)

    try:

        run_loop()

    except KeyboardInterrupt:

        print('Exiting...')

    finally:

        key_helper.end_get_key_continuous()

        exit(1)


The full code can be found on GitHub.

Running the Code!

To run the code, you simply need to go to your terminal, cd into the correct directory (/keyboard_control) and type:

python3 drive_with_wasd_keys.py


but...

If you've taken our advice and decided to use SSH to access your fancy, wireless RVR+/RVR 💁‍, you'll just need to take a few extra steps to create the connection between your RVR+/RVR and the machine you are SSHing into it with before running the above command.

If you haven't recently grabbed your IP address, you'll need to do that with a monitor/mouse/keyboard attached to your Raspberry Pi by jumping into the terminal on your Raspberry Pi and typing:

hostname -I


Write down the IP address it spits out and let's unplug the mouse/keyboard/monitor from your Raspberry Pi and jump over to the machine you'll be using to control your RVR+/RVR!


On your remote machine (which must be on the same Wifi network you set your Raspberry Pi to connect to), you'll also want to jump into the terminal, where you'll use the IP address you just grabbed and type:

ssh pi@ipAddressYouGrabbed


If it is successful in finding your Raspberry Pi using its IP address (ie if you are on the correct network and typed the correct IP address (and your Raspberry Pi is powered on and has been for at least 15 seconds)), you'll be prompted to type in the password to your Raspberry Pi. As you are doing so, you won't get an indication that the terminal is registering your keystrokes, but that is just to make you feel extra safe, so dangerous hackers can't see how long your password is and steal it 🦹.

If all went to plan with that, you should get a success message and be ready to cd into your /keyboard_control folder in your terminal and run:

python3 drive_with_wasd_keys.py


Once this is running (ie the command above didn't result in an error), you can start using your W ☝️, A 👈, S 👇 and D 👉 keys to control your RVR+/RVR and Spacebar 🛑 to stop it, all from the terminal!

Once you are done driving, you'll want to make sure you properly shut down your Raspberry Pi. You can do this by pressing Ctrl + C and then any other key to kill the program and then turning off your Raspberry Pi by running:

sudo shutdown now


Now What?

Try to improve your Keyboard Controlled RVR+/RVR! Here are some ideas to get you started: