A little while back I wrote a post about creating a python script to control a brickpi robot, now that I actually have the brickpi components, motors and cables and have actually tried it out, I’ve made a few improvements to that script to make it work.
Installing Raspbian
Firstly, I had to revisit the raspbian installation. With the brickpi bundle I bought from Dexter Industries, it came with a pre-installed raspbian SD card. I’m not sure what was wrong, but I wasn’t able to boot into it, there were startup errors regarding /etc/init.d. A quick Google didn’t help, so I decided to flash a new SD card with a more recent version of raspbian.
It’s important to note that the stock raspbian image won’t automatically work with the brickpi, as some modifications are required in order to control I/O for the motors. You can make these modifications yourself as there are instructions on the brickpi website, but it’s far easier to just download the pre-modified raspbian from dexter industries.
This was quite simple, on a mac you can erase and format an SD card using Disk Utility, after that, use these commands to create the SD card image:
diskutil list diskutil unmountdisk /dev/disk1 dd if=2013.07.27_BrickPi.img of=/dev/disk1 bs=2m
diskutil list will show you the drives attached, in my case the SD card was disk1 but it may vary for you. It takes around 10 minutes to flash the SD card so go make a brew.
Configuring WiFi
Setting up wifi on the pi is pretty easy, I have a USB wifi adapter, so it was just a case of configuring a wireless interface, which can be done by adding the following to /etc/network/interfaces
sudo nano /etc/network/interfaces add iface wlan0 inet dhcp wpa-ssid "NETWORK_ID_HERE" wpa-psk "NETWORK_PASSWORD_HERE"
Then do a sudo reboot
Expanding the SD card
In order to expand the root partition and make use of the entire SD card, run the following command and follow the menus to expand SD card
sudo raspi-config
Then do a sudo reboot
Setting up the brickpi dependencies
SSH onto the pi and run the following
mkdir brickpi cd brickpi/ git clone https://github.com/DexterInd/BrickPi_Python.git cd BrickPi_Python sudo apt-get install python-setuptools sudo python setup.py install
This will clone the BrickPi python repository (which contains a load of examples), install python-setuptools and install the brickpi module, so you can import Brickpi globally in your python scripts.
Controlling the robot
After testing out the brickpi examples, it was quite clear that I’d need to modify my original python script. I needed 2 threads.
- Thread 1 would continually update the motors every 200ms, otherwise the motors would turn briefly and then remain still.
- Thread 2 would process incoming commands and set the motor speeds.
Other than the threading code, the script is quite easy to follow
from BrickPi import * #import BrickPi.py file to use BrickPi operations import threading import socket import select import Queue from threading import Thread import sys BrickPiSetup() # setup the serial port for communication BrickPi.MotorEnable[PORT_A] = 1 #Enable the Motor A BrickPi.MotorEnable[PORT_D] = 1 #Enable the Motor D BrickPiSetupSensors() #Send the properties of sensors to BrickPi running = True #This thread is used for keeping the motor running while the main thread waits for user input class BrickPiThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): while running: BrickPiUpdateValues() # Ask BrickPi to update values for sensors/motors time.sleep(.2) # sleep for 200 ms brickPiThread = BrickPiThread(1, "BrickPiThread", 1) #Setup and start the thread brickPiThread.setDaemon(True) brickPiThread.start() class ProcessCommandThread(Thread): def __init__(self): super(ProcessCommandThread, self).__init__() self.running = True self.q = Queue.Queue() def add(self, data): self.q.put(data) def stop(self): self.running = False def run(self): q = self.q while self.running: try: # block for 1 second only: value = q.get(block=True, timeout=1) process(value) except Queue.Empty: sys.stdout.write('.') sys.stdout.flush() if not q.empty(): print "Elements left in the queue:" while not q.empty(): print q.get() commandThread = ProcessCommandThread() commandThread.start() def process(value): print "Processing [{v}]".format(v=value) # Left side if value == 'L-FORWARD-Start': print "L-FORWARD-Start" BrickPi.MotorSpeed[PORT_A] = 200 elif value == 'L-FORWARD-Stop': print "L-FORWARD-Stop" BrickPi.MotorSpeed[PORT_A] = 0 elif value == 'L-BACK-Start': print "L-BACK-Start" BrickPi.MotorSpeed[PORT_A] = -200 elif value == 'L-BACK-Stop': print "L-BACK-Stop" BrickPi.MotorSpeed[PORT_A] = 0 # Right side if value == 'R-FORWARD-Start': print "R-FORWARD-Start" BrickPi.MotorSpeed[PORT_D] = 200 elif value == 'R-FORWARD-Stop': print "R-FORWARD-Stop" BrickPi.MotorSpeed[PORT_D] = 0 elif value == 'R-BACK-Start': print "R-BACK-Start" BrickPi.MotorSpeed[PORT_D] = -200 elif value == 'R-BACK-Stop': print "R-BACK-Stop" BrickPi.MotorSpeed[PORT_D] = 0 def main(): s = socket.socket() #host = socket.gethostname() host = "192.168.0.10" port = 3033 s.bind((host, port)) print "Server listening on port {p}...".format(p=port) s.listen(5) # Now wait for client connection. while True: try: client, addr = s.accept() ready = select.select([client, ], [], [], 2) if ready[0]: data = client.recv(4096) commandThread.add(data) except KeyboardInterrupt: print print "Stopping server." break except socket.error, msg: print "Socket error %s" % msg break cleanup() def cleanup(): commandThread.stop() commandThread.join() if __name__ == "__main__": main()
Paste that into server.py then run the following:
sudo python server.py
You should see some output in the terminal as it’ll print out any incoming commands. Then its up to you to connect onto that socket with a client of your choice. I’m using an android app (which I’ve detailed here), but you could use something as simple as the following Java client
import java.io.*; import java.net.Socket; public class Runner { public static void main(String[] args) throws IOException { Socket socket = new Socket("raspberrypi", 3033); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); OutputStreamWriter osw = new OutputStreamWriter(bos, "US-ASCII"); osw.write("L-FORWARD-Stop"); osw.flush(); } }
Resources
Any thoughts / suggestions? Please comment below!