keyboard control

Hi,

has anybody managed to control a Picoborg Reverse with, for example, arrow keys instead of the sliders in the GUI? I would like to do this. Can anybody advise? I'm a python noob so I don't know where to start.

piborg's picture

Do you mean using the arrow keys to drive a robot, such as:
  • Move forward
  • Move backward
  • Turn to the left
  • Turn to the right
If so it can be done fairly easily, in fact we can modify our old RemoteKeyBorgC script to control a PicoBorg Reverse instead of sending values over the network.

We did the following to change this script to drive a PicoBorg Reverse as described above:
  1. Remove all of the networking code, in particular any lines referencing socket or sender
  2. Remove all of the message code, in particular any lines referencing command or driveCommands
  3. Removed regularUpdate since it is no longer needed
  4. Added the standard setup code for PicoBorg Reverse
  5. Added some settings for the battery voltage and motor voltage
  6. Made the code inside the movement if, elif, else block set the motor speeds
  7. Call PBR.MotorsOff() when the script is finished

This was the resulting script, which we have called pbrKeyboard.py (attached to the bottom of the post):
#!/usr/bin/env python
# coding: Latin-1
 
# Load library functions we want
import time
import pygame
import os
import PicoBorgRev
 
# Settings for the PicoBorg Reverse keyboard movement
interval = 0.01                         # Time between keyboard updates in seconds, smaller responds faster but uses more processor time

# Power settings
voltageIn = 12.0                        # Total battery voltage to the PicoBorg Reverse
voltageOut = 6.0                        # Maximum motor voltage

# Setup the power limits
if voltageOut > voltageIn:
    maxPower = 1.0
else:
    maxPower = voltageOut / float(voltageIn)

# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
#PBR.i2cAddress = 0x44                  # Uncomment and change the value if you have changed the board address
PBR.Init()
if not PBR.foundChip:
    boards = PicoBorgRev.ScanForPicoBorgReverse()
    if len(boards) == 0:
        print 'No PicoBorg Reverse found, check you are attached :)'
    else:
        print 'No PicoBorg Reverse at address %02X, but we did find boards:' % (PBR.i2cAddress)
        for board in boards:
            print '    %02X (%d)' % (board, board)
        print 'If you need to change the I²C address change the setup line so it is correct, e.g.'
        print 'PBR.i2cAddress = 0x%02X' % (boards[0])
    sys.exit()
#PBR.SetEpoIgnore(True)                 # Uncomment to disable EPO latch, needed if you do not have a switch / jumper
# Ensure the communications failsafe has been enabled!
PBR.ResetEpo()

# Setup pygame and key states
global hadEvent
global moveUp
global moveDown
global moveLeft
global moveRighte
global moveQuit
hadEvent = True
moveUp = False
moveDown = False
moveLeft = False
moveRight = False
moveQuit = False
pygame.init()
screen = pygame.display.set_mode([300,300])
pygame.display.set_caption("RemoteKeyBorg - Press [ESC] to quit")
 
# Function to handle pygame events
def PygameHandler(events):
    # Variables accessible outside this function
    global hadEvent
    global moveUp
    global moveDown
    global moveLeft
    global moveRight
    global moveQuit
    # Handle each event individually
    for event in events:
        if event.type == pygame.QUIT:
            # User exit
            hadEvent = True
            moveQuit = True
        elif event.type == pygame.KEYDOWN:
            # A key has been pressed, see if it is one we want
            hadEvent = True
            if event.key == pygame.K_UP:
                moveUp = True
            elif event.key == pygame.K_DOWN:
                moveDown = True
            elif event.key == pygame.K_LEFT:
                moveLeft = True
            elif event.key == pygame.K_RIGHT:
                moveRight = True
            elif event.key == pygame.K_ESCAPE:
                moveQuit = True
        elif event.type == pygame.KEYUP:
            # A key has been released, see if it is one we want
            hadEvent = True
            if event.key == pygame.K_UP:
                moveUp = False
            elif event.key == pygame.K_DOWN:
                moveDown = False
            elif event.key == pygame.K_LEFT:
                moveLeft = False
            elif event.key == pygame.K_RIGHT:
                moveRight = False
            elif event.key == pygame.K_ESCAPE:
                moveQuit = False
 
try:
    print 'Press [ESC] to quit'
    # Loop indefinitely
    while True:
        # Get the currently pressed keys on the keyboard
        PygameHandler(pygame.event.get())
        if hadEvent:
            # Keys have changed, change the motor outputs
            hadEvent = False
            if moveQuit:
                break
            elif moveLeft:
                PBR.SetMotor1(-maxPower)
                PBR.SetMotor2(+maxPower)
            elif moveRight:
                PBR.SetMotor1(+maxPower)
                PBR.SetMotor2(-maxPower)
            elif moveUp:
                PBR.SetMotor1(+maxPower)
                PBR.SetMotor2(+maxPower)
            elif moveDown:
                PBR.SetMotor1(-maxPower)
                PBR.SetMotor2(-maxPower)
            else:
                # None of our expected keys, stop
                PBR.MotorsOff()
        # Wait for the interval period
        time.sleep(interval)
    # Inform the server to stop
    PBR.MotorsOff()
except KeyboardInterrupt:
    # CTRL+C exit, inform the server to stop
    PBR.MotorsOff()
Attachments: 

Thanks so much. Really helpful. BTW, not a criticism, there's a little typo on line 47 'moveRighte' should be 'moveRight'. Thanks again for being awesome!

Hi, thank you for the above code. I have successfully configured all the hardware and software to produce the desired result using a bluetooth keyboard. I would like to control my motors over the network - from a remote computer. Would it be possible to re-instate the networking code into the above pbrKeyboard.py?

I am interested in what this guy did https://github.com/shaunuk/picar

He used socket.io and javascript to collect accelerometer data from a smartphone via a web browser and used this to drive his rc car. Would such a thing be possible with PicoborgReverse?

piborg's picture

It would be possible to put the network functionality back in, using the network to send commands from another computer.

Alternatively you could modify the picar code you linked to work with PicoBorg Reverse instead.

Cody Erekson has made a javascript library for controlling PicoBorg Reverse:
https://github.com/cerekson/picoborgrev
You should be able to modify the picar/app.js script to call this pbr object instead of the piblaster object it currently uses.

Loading Picoborg Reverse on bus 1, address 44
Traceback (most recent call last):
   File "pbrKeyboard.py", line 26, in 
	PBR.Init()
   File "/home/pi/PicoBorgRev.py", line 234, in Init
	self.bus = smbus.SMBus(self.busNumber)
IOError: [Erno 2] No such file or directory
piborg's picture

I think this is because the scripts are trying to use the wrong I2C bus for your Raspberry Pi.
We tend to see this problem with older model Raspberry Pis running recent copies of Raspbian.

By default the scripts try looking for the PicoBorg Reverse on i2c-1, then if they cannot find it they try i2c-0.

Try changing line 194 in PicoBorgRev.py to be:

    busNumber               = 0

This should make the script try i2c-0 before i2c-1.
Since all of the examples use PicoBorgRev.py it should fix all of them.

Thanks

I found the solution here:

https://www.piborg.org/picoborgrev/troubleshoot/forum

I had to go really far down the posts to the bit with " bash ... curl ...https://...txt"
and then enable i2c via raspi-config
I left line 194 as busNumber = 1

all sorted.

I use a wireless keyboard and this is working fine when I run it from a terminal window. I would like to run it on reboot via crontab -e as for the Sequence program. It does not work. My keyboard is activated by the first keypress, ie it does not have an on/off switch. So that I can press a key to start the keyboard I have tried putting in a 20 second time delay both at the beginning of the code after the time library call. I have also tried putting the 20 second delay after the library calls and just before setup pygame and keyboard states. Neither work, any help much appreciated.

piborg's picture

I think the problem you are having is that the script is not in the foreground, so the keyboard inputs are going elsewhere.

You could try adding this line just after the set_caption line and it might work:
pygame.event.set_grab(True)
This is not really recommended as it will prevent other things seeing the keyboard.
In this case though it may be the best way to ensure the input goes to this dialog instead of somwhere else.
Bascially this gets the script to 'steal' the keyboard input for itself, so it should not loose the keyboard when running.

I would keep the delay in before pygame is setup, it will give the desktop a chance to load before the script tries to create its dialog.

Thanks for the suggestions. It is still not working. I will try to set up an ap to control out of wifi coverage but it is not easy on the pi. Until then I have moved the files to the top level and type them in blind which does of course work except for finger trouble.

Subscribe to Comments for "keyboard control"