Time for another AverageBot #PiWars devblog! It’s been over two weeks since my last update and the competition is just one month away.
If you want to read my full devblog series, select the ‘Pi Wars‘ option in the menu.
Over the last couple of weeks I’ve mostly been concerning myself with code, having pretty much ignored it up to this point. I’ve also been playing with ball-pushing options for the Skittles challenge, program menus, displays, switches, new attachments and lots more.
So, let’s show you my progress…
I put off any real effort on the code for a while, as I simply prefer building things than I do coding them. I’d much rather get my tools out and hit some stuff than mess around fixing those pesky syntax errors!
I’ll share my complete code later this month once it’s complete, but I thought it would be better to show you some snippets and things I learnt this week:
PyGame not Initialising
You may have read in my last devblog that I am now using PyGame and a 2.4Ghz remote to control my robot.
One issue I kept coming across was PyGame not initialising properly. My robot script would start, then it would get to this line and get stuck with no errors:
I had to press Ctrl+C on my USB connected keyboard to force it to initialise and get past this line, which wouldn’t work at all in the competition environment.
After a bit of digging and some help from Twitter folks, it turned out I was causing this behaviour myself by not closing PyGame down properly when I was exiting my script.
I didn’t have a try/except block in my code (more on that below), so my script would exit without running pygame.quit(), and then something remains in the background and clashes the next time you try to run the PyGame initialisation.
I fixed that by using a…
Try: Except: Block
I hadn’t used these blocks much before, mostly due to a lack of understanding. I’d seen it used here and there, but I have always done things a different (worse) way. This post from RasPi.TV explains it very well:
The try/except block puts your main code in once section (the try: section), and your exit/cleanup code in another section (the except: section). All this does is say “run my code in the try section, and if anything goes wrong, run the code in the except section and close the script”.
Simple right? I can’t believe I’ve ignored this very useful option all this time. I’m converted.
I even found that using ‘finally:’ was all I really needed, where you replace ‘except’ for ‘finally’. This option kills your program on any exception or error, and is a bit more of a simple blanket approach.
What does all of this mean to AverageBot? Well, it means I won’t have the PyGame initialisation issue any more, as no matter how I kill the program, it will always run the ‘finally:’ block – which includes pygame.quit().
Menu display code
I added a display board to my robot this week, a 4tronix IP display board.
This will be used as a program menu, allowing me to select the program to run by using switches on the robot itself. I looked at other displays but they either used SPI (which is taken by the motor controller) or were just a bit too big for my small robot.
This cheap little number is intended to be an IP address display board add-on for the range of 4tronix robot controller boards, but you can of course play with the code to get it to display whatever you want.
This proved very challenging, and I’ve only really got it doing the bare minimum due to my lack of education of binary. The board uses an MCP23017 which I’m familiar with, but when it comes to segment displays I really struggle – especially as I don’t have a wiring diagram for the board.
You can see the original IP display code here. I didn’t find it very easy to break down and figure out, but coding isn’t my strong point. I gave up on any kind of scrolling letters or fancy business, and simply came up with a way of showing one number to represent the program to run.
So how do we do that?
To get this display to work (the MCP23017) we use smbus. First things first, import it:
Then we need to set up smbus. I use a ‘1’ here for the newer version of Pi, if you’re using a revision 1 Pi, use a ‘0’ instead:
bus = smbus.SMBus(1)
Next I set up some variables to make life easier later on. These are the segment numbers we want to display. I borrowed these from a forum post I stumbled across:
N0 = 0x3F N1 = 0x06 N2 = 0x5B N3 = 0x4F N4 = 0x66 N5 = 0x6D N6 = 0x7D N7 = 0x07 N8 = 0x7F N9 = 0x6F dash = 0x40 underscore = 0x08
Next you have to set up the MCP23017 used on the display board. Again, just copying the 4tronix code here:
addr = 0x20 # I2C address of MCP23017 bus.write_byte_data(addr, 0x00, 0x00) # Set all of bank 0 to outputs bus.write_byte_data(addr, 0x01, 0x00) # Set all of bank 1 to outputs
After all of that confusing stuff, to display a number on the first (left) segment, we just use this:
bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N1)
Now I have no idea how I ended up using a ‘7’ at the end of the first line above, it happened by accident and I didn’t touch it again after it let me put a digit in the first segment. Ah the pythonic perils of the Average Man!
One day I’ll master binary and segment displays, but at the moment that feels a long way off. At least I have a menu system working though, which looks a bit like this:
count = 0 try: def menu(): global count print "Menu start" while 1: print "while cycle--" time.sleep(0.2) if GPIO.input(switch1): count = count +1 if count == 1: print "count is 1" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N1) if count == 2: print "count is 2" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N2) if count == 3: print "count is 3" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N3) if count == 4: print "count is 4" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N4) if count == 5: print "count is 5" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N5) if count == 6: print "count is 6" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N6) if count == 7: print "count is 7" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N7) if count == 8: count = 1 print "...returning count to 1" bus.write_byte_data(addr, 0x13, 7) bus.write_byte_data(addr, 0x12, N1) if GPIO.input(switch2): time.sleep(0.5) if count == 1: bus.write_byte_data(addr, 0x13, 0xff) generalcontrol() if count == 2: bus.write_byte_data(addr, 0x13, 0xff) proximity() if count == 3: bus.write_byte_data(addr, 0x13, 0xff) threepoint() if count == 4: bus.write_byte_data(addr, 0x13, 0xff) linefollow() if count == 5: bus.write_byte_data(addr, 0x13, 0xff) skittles() if count == 6: bus.write_byte_data(addr, 0x13, 0xff) endscript() if count == 7: bus.write_byte_data(addr, 0x13, 0xff) haltcommand()
That’s pretty much it for the code this week, apart from some servo code a bit further down the page.
Starting my script at boot
I had to get my script starting on boot rather than logging in via SSH, as I don’t want to have to log in to my robot on the day. I need AverageBot to be easy, simple and robust.
There are different ways of achieving this such as using rc.local, but the first method I learnt was using the ‘cron’ scheduler. You know how it is, you always stick with what you know.
One problem I find with cron is it sometimes doesn’t work when the script you want to run is in a directory, or relies on other files in a directory. I found a great Instructables post that explained how to overcome this with a launcher script, and even produce a log file for any cron errors.
That’s the auto start sorted for my script, I just need to test that PyGame and all the different functions still work when using this method. More on that next week.
I still haven’t coded these…I know I know!
However, the latest EduKit 3 (a new £17 robotics kit) has a worksheet for line sensors so I may use this as a starter for 10. Watch this space.
Pi Noon wire holder
I finally got round to making a wire holder for the Pi Noon challenge. Competitors haven’t been told anything about this challenge other than that your robot must be able to hold a thin wire at one end of the robot.
I looked at a few options including clamps, screws and cycle brake cable tighteners, but eventually settled for a nice simple solution that holds the wire between two pieces of acrylic, tightened up by screws either side:
This is simply a mix of some L-brackets from eBay, and a couple of laser cut pieces of acrylic that I got made at MakersCafe along with other parts being made.
I got most of my robot parts re-made following a few improvements that I needed to make. These were simple but useful things such as removal of unwanted holes, expanded wire holes, a cut out to let the SR-04 sensor sit a bit better and a few other things:
Three Point Turn
I’ve decided I’m going to ‘wing it’ on this challenge, mostly because I’m running out of time, but also because I’d like to see how far a guy can get with a very basic approach.
I’m simply going to measure out the track on my floor at home, and try to code the timing just right to make it go forward/left/reverse/right at the right times. I might add a bit of code to use the SR-04 sensor, but only if I feel I have the time.
It definitely won’t win, but it could be a chuckle if I beat someone using sensors, encoders and other magic!
The “BIG PUSHA”!
Some of you will recall an attachment I made a while back called ‘Average-Claw’. It was a minimum attempt to compete in the skittles challenge where you need to push a ball towards some skittles and knock them over.
I decided it wasn’t enough to just shunt the ball towards the pins, so I started looking at servos as a way of adding some speed to the ball push.
First I had to get a servo talking to the Pi. The wiring is very simple, just three wires that route to a GPIO, GND and 5V – no resistors or anything else. I’m using a separate 4x AA power supply to avoid any drain on my Pi battery.
After it was wired up I initially tried the example scripts that came with my motor controller, which use something called Servoblaster. These scripts didn’t work for me straight away, and despite offers of help from Gareth at 4tronix, I just got fed up and started looking at other options.
After a short YouTube’ing session I came across a good video that had a nice easy servo example. Whilst I didn’t understand everything he talked about (Duty cycles?), I gave the code a go and it worked first time. A quick play with the parameters and I had it doing what I needed.
Here’s the code I’m currently using to drive 2 servos opposite eachother. They’re a bit twitchy but do the job:
p = GPIO.PWM(23,50) q = GPIO.PWM(22,50) p.start(7.5) q.start(7.5) p.ChangeDutyCycle(2.5) #180 q.ChangeDutyCycle(12.5) #0 try: while True: time.sleep(0.02) if GPIO.input(switch1): print "Firing!" time.sleep(1) p.ChangeDutyCycle(12.5) #180 q.ChangeDutyCycle(2.5) #0 if GPIO.input(switch2): print "Resetting" time.sleep(1) p.ChangeDutyCycle(2.5) #180 q.ChangeDutyCycle(12.5) #0
With the servos working it was time to look at how to fix these to my robot with some kind of pushing paddle. I decided that making another attachment would be a good shout, however this would be an almighty add-on considering the height of the ball, battery pack and all the wires. Challenge accepted!
Long story short, here’s what I have so far. It’s work in progress, and still needs wiring up:
All that’s left to do is add some kind of paddle to connect to the servos. I did originally cut one from acrylic, but stupid Average Man here got the dimensions round the wrong way. I still don’t really know how effective this will be at pushing a ball, but 10 points for effort right?
More on this next week.
With ‘Average-Claw’ now retired, I wanted to make a dedicated proximity alert challenge attachment to match my other attachments. I simply added this to the laser job I was having made. It’s a bit excessive and unnecessary, but then again, so is making a Raspberry Pi robot!
Terminal block switch cover
Another change/upgrade was my terminal block cover. I originally made my own cover for the terminal block to replace the cheap clear one that came with it, however I needed to add a momentary switch for my display menu and saw the terminal block as a good place for it to live.
All I’ve done here is extended a section of the cover, added a switch hole (and the little hole to keep the switch straight) and popped it on top. I had to keep my original cover in place to make it high enough to give the switch enough clearance.
The switch is a SPDT momentary variation. I soldered a GPIO to each end of the switch, and then wired a 3.3V line to the middle shared connection.
SPDT stands for ‘Single Pole Double Throw’, which just means each end of the switch has a single terminal (single pole), and the switch can go in two different directions (double throw). When I click it in one direction, it connects that GPIO to the shared 3.3V line (a GPIO input).
Using pull-down resistors instead of physical ones (thanks to RasPi.TV for the excellent article on this), I just wire the other ends of these to my Pi (no resistors needed) and flicking the switch each way triggers a different GPIO pin.
Perfect…apart from the fact that this particular switch appears to have a poor connection on one side!
With less than 10 days to go before code entries need to be submitted for judging, I suppose I best continue to work at combining and refining my code. I’m not sure my line follower code will be ready in time, but hey ho.
Sorting out the servo paddle will be the next task, getting something cut and ordering some square wood dowel to attach it to the servo horns.
Then there’s the three point turn – I’ll need to test my rough and ready approach to that one very soon.
Hmm…maybe I’m not as prepared as I thought I was?!
Until next time…