I built another clock recently. This time I decided to go with an orrery that uses a laser pointer to track the location of the sun in three dimensions. The idea is that if the clock knows three things (time, location, and north) it can figure out where the sun should be and point a laser at it.
During the day it points in the direction of the sun and at night (since the sun is below the horizon) it points at the appropriate point on the floor.
Hardware
I decided to mount a laser pointer on a servo and mount that servo on another servo at a right angle allowing me to point the laser at any X and Y coordinate. I was surprised to find out that 360 degree servos are pretty hard to find. After much searching I managed to find a suitable 360 degree servo (the GWS S125 1 turn).
I also had to decide how to set the clock. I figured I’d need to be able to:
- Adjust the servo endpoints (so that they each rotated exactly one full revolution and 0 degrees pointed at north and horizontal respectively).
- Set the time.
- Set the longitude and latitude.
- Invert the X and Y rotational directions (if I decided to mount the clock upside down on the ceiling, for instance).
- For debugging, be able to point each of the servos in an arbitrary direction.
Obviously the number of functions I wanted to support would be too much to implement with a set of dedicated buttons. For input I considered:
- Using dedicated buttons (this would require too many buttons)
- Using an LCD and menu system (complicated and inconvenient to use)
- Use USB (USB is too complicated to implement from scratch and I didn’t have a PIC that supports it on hand).
- Use RS-232 (would require some additional hardware to support).
I chose RS-232 (aka serial) as being the best compromise between ease of use and ease of development. Using this approach I could allow the clock to self document (by supplying help, and process commands such as set longitude and set year).
For a microcontroller I chose the PIC 18F877 because I had a couple on hand and they feature USARTs (for serial communication) several different timers (for handling internal timing), and a pulse width modulation module (for controlling the servos).
Software
The first thing to figure out was how to determine where the sun is. The algorithm is relatively straighforward, but requires floating point math and trigonometric functions. Although my C compiler supports floating point math, I quickly learned that I’d run out of memory before I implemented even half of the computation algorithm. I was forced to write my own fixed precision math and trig functions.
Angles
Using degrees would be awkward so I decided to break a full circle into 10 bits or 1024 “hexians”. (Yeah, I know somebody has probably already done this and given it a different name, but that’s the name I chose, so deal with it.)
Fractions
My C compiler allows 32 bit signed and unsigned numbers so for fixed precision I decided to reserve the least significant twenty bits for fractional portions of numbers. So for example, 90 degrees is 256 hexians. With my fixed precision this would work out to 256 * 2 ^ 20 = 268435456. This leaves two bits left over –one for a sign and one extra to allow me to express numbers from -2048 to +2047 with twenty bits of precision.
Multiplying
When multiplying I had to be careful to pre-scale my multiplicands before multiplying them or I’d get an overflow (20 bits of precision * 20 bits of precision = 40 bits of precision). Typically before multiplying any two of these numbers together I’d right-shift each of them ten bits to yield a result that didn’t cause an overflow (20 bits >> 10 * 20 bits >> 10 = 20 bits of precision). Overflow is still possible if the integer portions of the numbers were large, but for most cases where I was doing something like multiplying the result of trig functions together (where results would be less than 1) this worked fine.
Division
Division is another story. Let’s say I want to divide 3.7 by 2.5. Using my fixed precision system this would be expressed as 3879731 / 2621440. If I let my C compiler divided these two integers I’d get a result of 1 (because integers don’t have fractions). The answer I actually want is 1551892 (1551892 / 2 ^ 20 is approximately 1.48).
If I tried to pre-scale either of the numbers I’d either risk an overflow or have to throw away precision. Unfortunately I could think of no easy way to overcome this limitation and I wound up implementing my own division routine using some clever bit twiddling.
Trigonometry
Next I had to implement my trigonometric functions. This actually wasn’t too bad. A sine wave is symmetrical and only 90 degrees (256 hexians) is required and the remaining 270 degrees (768 hexians) can be gleaned by reflecting the values. Cosine is just sine offset by 90 degrees so it could be determined by using the sine function. Tangents are computed by dividing sin by cos.
Similarly arcsine can be computed by doing a binary search on the sine table and the other inverse functions can be computed in terms of arcsine.
Servo Control
Servos are controlled by using pulse width modulation (PWM). About every 20ms the servo expects a pulse that lasts between 1ms and 2ms indicating how far it should turn (with 1.5ms being about centered). Since I supported 1024 hexians for a full circle I would need to precisely time a pulse that lasted between 1ms and 2ms in 1024 increments (approximately 1 microsecond increments). Running at 10MHz that works out to only 2.5 instructions per increment!
Originally I was planning to use the PIC 16F877’s built-in PWM module to control the servos but I soon realized that it wouldn’t work because it supports a “resolution” of only 10 bits (1024). The ratio of duty cycle to period made it impossible. I tinkered with the idea of turning the PWM module on and off so that I could set the period to 1ms and then use a 10 bit duty cycle, but this seemed unnecessarily complicated.
Finally I decided to use the 16F877’s 16 bit timer. I could turn on the signal, set the timer, and when the interrupt fired, turn off the signal and get pretty precise timing. Controlling two servos posed a problem however –what if they both have approximately the same duty cycle? By the time I’ve turned off the first servo’s signal and figured out when I should turn off the second servo’s signal it would be too late.
The solution eventually occurred to me (and after I figured it out I realized that other people use the same technique –I just didn’t realize what they were describing): the servos don’t have to be controlled simulataneously. If I offset the two servo’s periods I would only ever have to deal with one at a time (in other words, at time 0 turn on servo X, at (say) 1.5ms turn off X, at 10ms turn on servo Y, at (say) 1.3ms turn off servo Y, at 20ms turn on servo X, …).
Interface
I eventually settled on the following commands (this is copy-and-pasted directly from the help that the clock provides).
These are the supported commands (all ended with CTRL-M): <blank> (Help) Returns help showing these commands. STA (Status) Show status. xx (Set UTC year since 2000). Ex: "03" means March. Mxx (Set UTC month). 01-12 Dxx (Set UTC day). 01-31. Hxx (Set UTC hour). 00 is midnight, 23 is 11PM. mxx (Set UTC minute). 00-59. Sxx (Set UTC second). 00-59. LONsnnndd (Set longitude ex: -12236 is 122.36W) LATsnnndd (Set latitude ex: +04762 is 47.62N) DEBUG/CONFIG: (Any of these commands causes debug mode to start until ESC is pressed xnnnn (Point X at Hexian 0-1023) ynnnn (Point Y at Hexian) SXZ (Set X Zero mode: + increases - decreases) SXF (Set X 1023 mode: +/-) SYZ (Set Y Zero mode: +/-) SYF (Set Y 1023 mode: +/-) RST (Restore default servo endpoints) INX Invert X (Clockwise vs CCW) INY Invert Y
It took me about a month to write and test all the code.
Construction
As usual, I began by building out the prototype on a breadboard. I dealt with what I consider typical pains (figuring out that the “receive” pin on the connector is actually the “transmit” pin on the PIC due to DCE/DTE confusion, dealing with brownouts when the servos moved and sucked a whole bunch of power out of the circuit, etc.)

After about a week of playing with the circuit I has satisfied that everything was working correctly and I was ready to build a permanent circuit. I layed out the schematic and components using Eagle and photo etched a printed circuit board. Lessons learned: use thick traces with lots of spacing, use large pin pads, it’s better to overexpose the copper board than underexpose it.
I made a few mistakes on my etching, but I was able to get the circuit working through a combination of cutting invalid traces and “fixing” broken traces using wire.

Conclusion
The circuit is now assembled and running in my living room. Since I live in the northern hemisphere I put the clock on the north side of my dining room. This allows the laser to shine into the kitchen, the dining room, the living room, and my neighbor’s living room at various times of the day/year.

The servos do not have enough resolution to actually handle 1024 increments. What typically happens is the angle sent to them needs to move a certain amount from wherever the servo is currently pointing before it moves. I’d estimate that I have about 1 degree of accuracy.
The algorithm I implemented for computing where the sun is, similarly has about 1 degree of accuracy.
Overall, the laser does point in the right direction and is accurate enough to stand up to visual scrutiny (but not accurate enough to, say, aim a radio telescope).

If I Did It Again
- I’d use a more accurate movement mechanism (probably stepper motors with rotational sensors).
- I’d lock up my dogs so they wouldn’t be so annoying whenever the laser pointer zips around the room while I’m debugging.
Enclosure
One thing I haven’t been able to decide on is how to enclose the circuit. Right now it’s just a circuit board and a couple of taped-together servos sitting on a shelf. I toyed with the idea of building a latex “pointing finger” around the laser or something like that, but I’m not sure I could get it to work due to the odd dimensions of the servos and laser. If anyone has an good idea for a finishing touch, I’m open to suggestions.



#1 - November 18, 2008 at 08:37 pm
Holy crap, you’re smart!
And nice job including your stack of 2’s in the photo
#2 - November 19, 2008 at 12:15 am
The stach of 2’s is awesome :))
#3 - November 19, 2008 at 05:06 am
Will you be building a second one for Daylight Saving Time?
#4 - November 19, 2008 at 09:10 am
It’s all about the Jeffersons.
#5 - November 19, 2008 at 09:10 am
The clock runs on UTC time so I don’t have to deal with time zones and Daylight Saving Time.
#6 - November 24, 2008 at 01:46 pm
How about a latex penis around the laser that would only go from 6 to midnight?
#7 - November 24, 2008 at 03:14 pm
I figured someone would suggest a robot dildo sooner or later.
I’m not sure it would work with the rest of my home decor. Plus the laser points in three dimensions.
#8 - March 11, 2010 at 08:12 pm
Cloth! Go Muppet style! And I would put the board in a heavy box so I could mount a compass and your Muppet laser on top.