RemoteKeyBorg2 - Control your PicoBorg car with LedBorg lights remotely with the keyboard
So you have a PicoBorg based robot and an LedBorg you want to control at the same time, then we need an upgrade to RemoteKeyBorg!
RemoteKeyBorg2 is a pair of scripts that takes RemoteKeyBorg and adds functionality for sequencing an LedBorg at the same time.
The example sequences for RemoteKeyBorg2C use the keyboard keys
If you need a way of connecting multiple expansion board to your Raspberry Pi (for this and many other things), check out TriBorg.
To get a PicoBorg robot base ready see PiCy for a full set of instructions, or RemoteKeyBorg for the motor connections only.
Now we need the new scripts.
RemoteKeyBorg2 is a script that comes in two parts:
and you can download the RemoteKeyBorg2C script file as text here.
Save the text files on your Raspberry Pis as RemoteKeyBorg2S.py and RemoteKeyBorg2C.py respectively.
Make the scripts executable using
and run on the Raspberry Pi with the PicoBorg using
and run on the commanding Raspberry Pi using
RemoteKeyBorg2 is a pair of scripts that takes RemoteKeyBorg and adds functionality for sequencing an LedBorg at the same time.
The example sequences for RemoteKeyBorg2C use the keyboard keys
1
, 2
, 3
, and 0
to set the sequence the LedBorg will run.If you need a way of connecting multiple expansion board to your Raspberry Pi (for this and many other things), check out TriBorg.
To get a PicoBorg robot base ready see PiCy for a full set of instructions, or RemoteKeyBorg for the motor connections only.
Now we need the new scripts.
RemoteKeyBorg2 is a script that comes in two parts:
- RemoteKeyBorg2S.py (Server)
This script runs on the Raspberry Pi with the PicoBorg + LedBorg and sets the PicoBorg drives when told to
For remote control we suggest this Raspberry Pi is connected to the network using WiFi, so it is free of cables :) - RemoteKeyBorg2C.py (Client)
This script runs on the Raspberry Pi which is controlling the robot, it loads a blank window which responds to up, left and right
This script can be run on any computer connected to the same network, running Python and pygame, but it needs a GUI available (no telnets I am afraid...)
If testing this may even be run on the same Raspberry Pi, but that would defeat the point :)
broadcastIP
in RemoteKeyBorg2C.py, line 10
IP address to send to (Raspberry Pi with the PicoBorg), may be a single machine (e.g. 192.168.1.5) or a broadcast (e.g. 192.168.1.255) where '255' is used to indicate that number is everybodybroadcastPort
in RemoteKeyBorg2C.py, line 11
Number used to identify who gets network messages, if two copies of RemoteKeyBorg2 are used in the same network this should be changed to identify which copy is whichleftDrive
in RemoteKeyBorg2C.py, line 12
Drive number for the left wheel, change this if your wiring does not match the example diagramrightDrive
in RemoteKeyBorg2C.py, line 13
Drive number for the right wheel, change this if your wiring does not match the example diagraminterval
in RemoteKeyBorg2C.py, line 14
Delay between checking for keyboard updates, smaller numbers respond faster but will use more processor timeregularUpdate
in RemoteKeyBorg2C.py, line 15
Set to True the script will send a command at every interval, set to False it will only send a command when a key changesledSeq0
in RemoteKeyBorg2C.py, line 23
Sets the LedBorg sequence (speed and colours) for the0
key, default example is offledSeq1
in RemoteKeyBorg2C.py, line 24
Sets the LedBorg sequence (speed and colours) for the1
key, default example is a police siren red/blue patternledSeq2
in RemoteKeyBorg2C.py, line 25
Sets the LedBorg sequence (speed and colours) for the2
key, default example is a cycle of the colour spectrumledSeq3
in RemoteKeyBorg2C.py, line 26
Sets the LedBorg sequence (speed and colours) for the3
key, default example is a slow changing red pulsecolours
in RemoteKeyBorg2S.py, line 30
The start-up LedBorg sequence, colour codes only, no delay time, default is no pattern / offcoloursDelay
in RemoteKeyBorg2S.py, line 31
The start-up LedBorg sequence speed, default is to change every 300 msportListen
in RemoteKeyBorg2S.py, line 48
If you change the port your RemoteKeyBorg2C.py is using, change this to match
and you can download the RemoteKeyBorg2C script file as text here.
Save the text files on your Raspberry Pis as RemoteKeyBorg2S.py and RemoteKeyBorg2C.py respectively.
Make the scripts executable using
chmod +x RemoteKeyBorg2*.py
and run on the Raspberry Pi with the PicoBorg using
sudo ./RemoteKeyBorg2S.py
and run on the commanding Raspberry Pi using
./RemoteKeyBorg2C.py
RemoteKeyBorg2S
#!/usr/bin/env python # coding: Latin-1 # Load library functions we want import SocketServer import time import threading import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) # Set which GPIO pins the drive outputs are connected to DRIVE_1 = 4 DRIVE_2 = 18 DRIVE_3 = 8 DRIVE_4 = 7 # Set all of the drive pins as output pins GPIO.setup(DRIVE_1, GPIO.OUT) GPIO.setup(DRIVE_2, GPIO.OUT) GPIO.setup(DRIVE_3, GPIO.OUT) GPIO.setup(DRIVE_4, GPIO.OUT) # Map of drives to pins lDrives = [DRIVE_1, DRIVE_2, DRIVE_3, DRIVE_4] # Setup the default LedBorg pattern global colours global coloursDelay global coloursUpdated colours = [] # Default running sequence coloursDelay = 0.3 # Time between updates in seconds for the LED coloursUpdated = False # When True the sequence will be re-started # Make a function to set the LedBorg colour def SetColour(colour): LedBorg=open('/dev/ledborg','w') LedBorg.write(colour) LedBorg.close() # Function to set all drives off def MotorOff(): GPIO.output(DRIVE_1, GPIO.LOW) GPIO.output(DRIVE_2, GPIO.LOW) GPIO.output(DRIVE_3, GPIO.LOW) GPIO.output(DRIVE_4, GPIO.LOW) # Settings for the RemoteKeyBorg server portListen = 9038 # What messages to listen for (LEDB on an LCD) # Class for running the LedBorg class LedBorgSequencer(threading.Thread): # The code which will be run when the thread is started def run(self): global isRunning global colours global coloursDelay global coloursUpdated # Keep running while the system has not been terminated while isRunning: if len(colours) == 0: # No colours (not cycling), wait until the sequence is updated while not coloursUpdated: time.sleep(coloursDelay) if not isRunning: # We have been terminated, skip to the outer loop break coloursUpdated = False else: # Loop each colour in turn for colour in colours: SetColour(colour) time.sleep(coloursDelay) if coloursUpdated: # We have received a new colour sequence coloursUpdated = False if len(colours) == 0: # The new sequence is empty, turn the LedBorg off SetColour('000') break if not isRunning: # We have been terminated, skip to the outer loop break # Class used to handle UDP messages class PicoBorgHandler(SocketServer.BaseRequestHandler): # Function called when a new message has been received def handle(self): global isRunning global colours global coloursDelay global coloursUpdated request, socket = self.request # Read who spoke to us and what they said request = request.upper() # Convert command to upper case driveCommands = request.split(',') # Separate the command into individual drives if len(driveCommands) == 1: # Special commands specialCommands = request.split(':') # Separate the special command into parameters if specialCommands[0] == 'ALLOFF': # Turn all drives off MotorOff() print 'All drives off' elif specialCommands[0] == 'EXIT': # Exit the program isRunning = False elif specialCommands[0] == 'LEDBORG': # LedBorg command sequence if len(specialCommands) > 1: # First parameter is the sequence rate try: coloursDelay = float(specialCommands[1]) except: print 'LedBorg sequence speed "%s" is not a valid number!' % (specialCommands[1]) # Subsequent parameters make up the colour list colours = specialCommands[2:] if len(colours) > 0: print 'LedBorg sequence: ', for colour in colours: print colour + ' ', print 'at %f intervals' % (coloursDelay) else: print 'LedBorg sequence off, check at %f intervals' % (coloursDelay) # Flag that we have a new colours list coloursUpdated = True else: print 'LedBorg sequence expects at least a speed to be defined, e.g. "LEDBORG:0.3"' else: # Unknown command print 'Special command "%s" not recognised' % (request) elif len(driveCommands) == len(lDrives): # For each drive we check the command for driveNo in range(len(driveCommands)): command = driveCommands[driveNo] if command == 'ON': # Set drive on GPIO.output(lDrives[driveNo], GPIO.HIGH) elif command == 'OFF': # Set drive off GPIO.output(lDrives[driveNo], GPIO.LOW) elif command == 'X': # No command for this drive pass else: # Unknown command print 'Drive %d command "%s" not recognised!' % (driveNo, command) else: # Did not get the right number of drive commands print 'Command "%s" did not have %d parts!' % (request, len(lDrives)) try: global isRunning # Start by turning all drives off MotorOff() raw_input('You can now turn on the power, press ENTER to continue') # Setup the UDP listener remoteKeyBorgServer = SocketServer.UDPServer(('', portListen), PicoBorgHandler) isRunning = True # Start the LedBorg sequence handler loop ledBorgSequencer = LedBorgSequencer() ledBorgSequencer.start() # Loop until terminated remotely while isRunning: remoteKeyBorgServer.handle_request() # Turn off the drives and release the GPIO pins print 'Finished' MotorOff() # Wait for the LedBorg sequence handler loop to terminate ledBorgSequencer.join() if len(colours) > 0: SetColour('000') raw_input('Turn the power off now, press ENTER to continue') GPIO.cleanup() except KeyboardInterrupt: # CTRL+C exit, turn off the drives and release the GPIO pins isRunning = False print 'Terminated' MotorOff() # Wait for the LedBorg sequence handler loop to terminate ledBorgSequencer.join() if len(colours) > 0: SetColour('000') raw_input('Turn the power off now, press ENTER to continue') GPIO.cleanup()
RemoteKeyBorg2C
#!/usr/bin/env python # coding: Latin-1 # Load library functions we want import socket import time import pygame # Settings for the RemoteKeyBorg client broadcastIP = '192.168.0.255' # IP address to send to, 255 in one or more positions is a broadcast / wild-card broadcastPort = 9038 # What message number to send with (LEDB on an LCD) leftDrive = 1 # Drive number for left motor rightDrive = 4 # Drive number for right motor interval = 0.1 # Time between keyboard updates in seconds, smaller responds faster but uses more processor time regularUpdate = True # If True we send a command at a regular interval, if False we only send commands when keys are pressed or released # Setup the connection for sending on sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Create the socket sender.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcasting (sending to many IPs based on wild-cards) sender.bind(('0.0.0.0', 0)) # Set the IP and port number to use locally, IP 0.0.0.0 means all connections and port 0 means assign a number for us (do not care) # LedBorg sequences, first entry is delay between steps in seconds ledSeq0 = ['0.5'] # Off / no sequence ledSeq1 = ['0.1', '100', '200', '100', '001', '002', '001'] ledSeq2 = ['0.3', '200', '210', '220', '120', '020', '021', '022', '012', '002', '102', '202', '201'] ledSeq3 = ['0.5', '100', '200', '211', '200'] # Setup pygame and key states global hadEvent global moveUp global moveDown global moveLeft global moveRight global moveQuit global newLedSequence global ledSequence hadEvent = True moveUp = False moveDown = False moveLeft = False moveRight = False moveQuit = False newLedSequence = False ledSequence = ledSeq0 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 global newLedSequence global ledSequence # 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.key == pygame.K_0: ledSequence = ledSeq0 newLedSequence = True elif event.key == pygame.K_1: ledSequence = ledSeq1 newLedSequence = True elif event.key == pygame.K_2: ledSequence = ledSeq2 newLedSequence = True elif event.key == pygame.K_3: ledSequence = ledSeq3 newLedSequence = 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 newLedSequence: newLedSequence = False # Send a command to change the LedBorg sequence command = 'LEDBORG:' for ledStep in ledSequence: command += ledStep + ':' command = command[:-1] # Strip the trailing colon sender.sendto(command, (broadcastIP, broadcastPort)) if hadEvent or regularUpdate: # Keys have changed, generate the command list based on keys hadEvent = False driveCommands = ['X', 'X', 'X', 'X'] # Default to do not change if moveQuit: break elif moveLeft: driveCommands[leftDrive - 1] = 'OFF' driveCommands[rightDrive - 1] = 'ON' elif moveRight: driveCommands[leftDrive - 1] = 'ON' driveCommands[rightDrive - 1] = 'OFF' elif moveUp: driveCommands[leftDrive - 1] = 'ON' driveCommands[rightDrive - 1] = 'ON' else: # None of our expected keys, stop driveCommands[leftDrive - 1] = 'OFF' driveCommands[rightDrive - 1] = 'OFF' # Send the drive commands command = '' for driveCommand in driveCommands: command += driveCommand + ',' command = command[:-1] # Strip the trailing comma sender.sendto(command, (broadcastIP, broadcastPort)) # Wait for the interval period time.sleep(interval) # Inform the server to stop sender.sendto('ALLOFF', (broadcastIP, broadcastPort)) except KeyboardInterrupt: # CTRL+C exit, inform the server to stop sender.sendto('ALLOFF', (broadcastIP, broadcastPort))