Initially, I decided that a stepper motor would be the best choice for the project. I needed something that could rotate through a full 360 degrees in a constant direction (clockwise) and that I could control the speed and position of the spindle. Stepper motors are controlled by sending pulses of varying duration to the motor in order to control the angle of rotation. Check out the wikipedia page for more info about how they work.
The essential part of controling a stepper is the timing of the signal. Knowing this, I was unsure about whether or not the RPi would be suitable for this task as given that it's running the whole linux operating system it isn't really a real-time system, so timings might not be too accurate. Either way, I tried attaching the stepper motor's driver board to the GPIO pins on the RPi and pulsing them in the same way the example Arduino sketch supplied with the motor did. Everything seemed to work perfectly, so I sat back and had myself an enjoyable Club Mate at the London Hackspace, where I did pretty much all of the work on the project after moving to the capital.
I was starting then to work out how to get the pointer to move to the correct part of the clock - however I wanted to be sure that the motor wasn't drifting, so I set it off to do 100 rotations, came back, and, ah. Turns out it was drifting, and by quite a lot (about 20-30 degrees). So no good. I assumed this must be the timing problem, so tried again using just an Arduino, but no luck! I was using a very cheap stepper motor, but with no dedicated driver circuit to handle accel/decelleration, missed steps and the like. One of the ICs alone would've cost more than the motor, and good quality NEMA motors themselves also cost about £15, and are too large for what I wanted. I also didn't like how the whole system was 'open-loop', with no feadback on the position of the motor shaft. Basically, what I needed was a servo. A 360 degree servo? Why yes, it turns out such things exist! Except it wasn't quite what I was expecting.
I ordered this 'Full Rotation Servo' without properly reading the datasheet or understanding what it does. It's not actually a servo in the traditional sense as far as I can tell, the only thing that you can control is the speed of rotation. I spent a while thinking about how to resolve this issue and decided that the best way forward would be to somehow add feedback to the motor. I had thought about using some kind of rotary encoder but these can be very expensive and would've required a fair bit of mechanics. I happened to have a few micro-switches with rollers on the arms and using one of the 'heads' supplied with the servo I put together this hack:
On the left is a ULN2003 control IC. This consists of several 'Darlington Pairs' and allow the Arduino to switch a higher current than it would be able to do so on its own. In the centre, an Arduino Duemillnova and at the right the stepper motor with a microswitch epoxy-ed on to the side. Every time one of the arms from the wheel passes over roller on the arm of the microswitch - it pulls an input on the arduino low. The Arduino counts these pulses and can hopefully work out how far the shaft has turned. I had to be careful to 'debounce' the signal coming in from the switch. 'Bouncing' happens when the mechanical contacts on a switch almost literally bounce off of one another, creating a noisy signal - shown on the right of the image below, instead of a nice clean one like that on the left:
If the Arduino simply counts all of the pulses it receives, it'll count all of the short ones in between the switch being pressed by the arm and then it springing back up! Despite having come across this countless times before it caught me out again and it took me a few minutes of wondering what was going on and worrying that I'd irreversably glued a broken switch to the side of the motor to figure out what'd happened. I'll post the Arduino Sketch I used to solve this on the code page [TODO - Link]. I've since discovered the 'bounce' library that may solve this problem better than my quick and dirty code, so I'd recommend checking that out.
(By the way - I know that all this is possible with a Raspberry Pi on it's own, but as I was really pressed for time I found it quicker and easier to prototype the software and electronics with the Arduino I already had)
Whilst this solution was all very well for rotating the arm around 60 ( = 360 / 6) degrees, it's no good for being able to work out where the hand is. This could be solved by always remembering the last place it was moved to and then counting 60 degree rotations from then to move it to a new place, but, like the stepper motor, one click out and it's never going to be back in the right place (by itself), and that's assuming the counting method always works. I decided that the quickest, cheapest and simplest way to solve this was simply to glue another microswitch on the side of the first one and then extend one of the arms out so it only hit it once per revolution. Then, whenever a pulse is received from the second microswitch the Arduino knows it's got round to the start - a fixed point of reference. The first iteration whilst I was testing it extended this arm with several bits of old credit card glued onto the arm. Needless to say, this was totally useless for anything more than testing (it fell off after 10 mins) - but it was
great ok for prototyping. Here's a video of it in action:
The final version of this mechanism does away with the naff bit of plastic and has a nice aluminium disk mounted flush on the hub of the wheel and a M6 bolt head in line with the second microswitch. Stupidly I didn't get a photo of it - I could've sworn I had! Sorry :(
As you can see - there's a Raspberry Pi with an Adafruit PiPlate (supplied by the excellent Phenoptix) that I've used to break out the GPIO pins on the Pi. Seeing as I had developed all the code for driving the motor for the Arudino, the Raspberry Pi needs a way to send the data it retrieves from the internet to the Arduino. There are many ways of doing this and I chose probably the most ridiculous and quick and dirty one that's probably never been seen before. A couple of things I didn't want to do:
- Use the Arduino board with the RPi over USB - Seemed expensive, slightly wasteful for the tiny amount of data I needed to transfer and would've used up a lot of my power budget (more about that in the next section). Easily the simplest way though in terms of software.
- I2C / SPI - I'm not even sure if both the Arduino (or at least the Atmega328) supports these without heaps of coding, and it looks like getting it to work reliably on the Pi is a bit of a work in progress without again lots of software/config problems. (I'm probably wrong about both of these but I didn't get a chance to look into this. Please correct me in the comments if you're knowledgeable - it'd be great to find out) Also the Rpi runs at 3v3 not 5v like the Arduino - which would mean some kind of level shifting or at the very least a resistor network would be needed to get them to play nice (i.e. Not destroy each other)
- TTY from the RPi's serial port to Arduino - The only reason I didn't do this was because all the development I was doing with the Pi was over it's TTY port that I was logging in to with a USB -> RS232 at 3v3 lead. I don't recommend this (it's so much better just to SSH into it) as all the formatting (especially on something like VIM) just gets completely messed up. I don't know why I stuck at it like this.
Given that the only data I was going to be sending would be a number between 1 and 6 and probably at the ver most every half an hour - I made my own protocol. It's illustrated well in the video with the LEDs on the PiPlate - one line rises high and then an amount of pulses made on the other line. The Arduino counts these pulses and then starts the motor - waiting until the second microswitch is hit (12 o'clock) and then stops after counting this many pulses on the first switch input. This does mean that the hand always passes through 12 o'clock whenever it moves (so going from 1 to 11 means rotating almost 2 full rotations) - but I thought this an acceptable cost to almost guarantee that the hand will always be in the intended place.
In case you didn't catch the video, here's that last bit illustrated:
This works because the Arduino is capable of registering 3.3v signals as 'logic high' despite being run at 5v, although we can't send signals back to the Pi. This is something I'd like to fix when it's time to 'upgrade' the clock as it'd be very useful to know if the hand gets stuck or something. (I believe the Atmega328 will happily work at 3.3v - so might be simpler just to give it its own power supply).
Next step - Code...