I like knowing how fast my car is going, but the stock analog speedometer only goes up to 140kph. Plus, I suspect its accuracy isn’t all that great, it doesn’t register speeds below 15kph, and there’s no way to calibrate it. I looked at digital speedometers online, but most of them were backlit LCD displays (almost illegible in the sunlight atop my dashboard, and an eyestrain even when you can read them). The ones that weren’t were just too expensive for what should be a rather simple piece of electronics. I wanted a three digit 7-segment LED display, like my buddy Takahashi has in his Lancer EVO 6.
I looked at frequency counter chips, figuring I could use BCD decoder and a trio of 7 segment driver chips to display everything, but they were outdated and the amount of external timing circuitry I’d need was a bit ridiculous. I looked around at the local electronics supply store, and stumbled across some cheap programmable microchips. At first I thought it would be overkill, but considering a simple microcontroller is only $2 or $3 and reduces the number of parts significantly, it actually turns out to be the best option. Plus, you can add extra features like in-software calibration, 1/4 mile “drag strip” times, storing top speed, display in kph or mph, and whatever else you can think of. The drawback is the initial investment of a PIC programmer so that you can use your computer to burn your programs to the chip. You can now get an official PICKit 2 programmer for about $35 plus shipping, and it can be used on a variety of microchips all the way up through DSP microchips. If you’re into hobbyist electronics, there’s really no reason you shouldn’t get one. Cheap, easy to program, and powerful enough for most stuff.
The cheapest microcontroller I could find was the PIC16F648A, which is an 8 bit microcontroller with 16 input/output pins and 3 hardware timers, more than enough for this project. It has 256 bytes of RAM, 256 bytes of EEPROM permanent storage, and programming storage space for about four thousand instructions, and best of all, it doesn’t require any external oscillator hardware. PIC microcontrollers have a very minimal instruction set, meaning that any operation more complex than addition or subtraction requires a lot of instructions, but since this chip defaults to 1 million instructions per second that’s not usually a problem, assuming you have a compiler capable of generating those sequences. As well, almost every input and output is reprogrammable to different hardware functions such as voltage comparison, interrupt triggering, edge counting, timer capture, or even serial communication. It’s really quite a powerful little package, especially if you compare it to what was available just 5 years ago.
Your basic 7 segment LED has 10 pins. Two of those are the same, connected to either the anode or the cathode of all the LEDs, and the other 8 are the other end of each of the 8 LEDs. Since 24 LEDs is too many to control with 16 pins, I multiplexed them; that is, the 8 individual pins are all connected through resistors to 8 of the microcontroller pins, and I use 3 other pins to select which of the three displays to light up (via transistors since the PIC pins can’t source that much current). If you switch between the displays fast enough, the human eye can’t even tell they’re being switched on and off.
Obviously the whole thing runs off 5 volts, so to step down the 12-14 volts from a car battery you need a voltage regulator (I used an LM7805 because it was what I had on hand). A couple capacitors also help, as cars tend to have lots of voltage transients. If I wanted to be a bit safer I could have also used a zener diode for overvoltage protection, but the power supply area of the circuit board was already a bit tight for space and the LM7805 and associated components are cheap enough to replace if something bad happens.
Here’s a quick parts list of what I used:
1 – PIC16F648A microcontroller
1 – 18 pin socket (not needed if you’re just breadboarding it)
3 – 7-segment displays, common cathode
8 – LED resistors (I used 120 ohm, but you should design with the max LED current in mind.)
3 – NPN transistors, anything should work
3 – 1k resistors (to use with the base of each transistor)
1 – LM7805 5 volt voltage regulator
2 – 22uF 50V capacitors (maybe overkill, but if you have noisy wiring you will need to adjust this)
1 – button (for software control)
2 – 10nF capacitor (button debounce and speedo input filtering)
1 – 100k resistor (button pullup)
a board to put it all on, and cables to string it together. You should be able to get all this stuff for under $20.
If you’re hooking this directly to a reed switch with no external driving circuitry (like a stock ECU), you will have to add another pullup resistor.
The easiest way would just be to hook up all the 8 LED leads to the same 8 bit port (PORTA or PORTB), but because we’re using some ports of A and some of B to do other things, we can’t be so lazy. So we’ll spread those out and use a software bitmask to determine which ports to light up. Thankfully, it doesn’t really matter that much what order they go in, because you can define it in software. The speedometer input has to be on a specific pin if we want to automatically log the timer when we get a pulse. And one of the pins cannot be used as an output, so we’ll set that one to be our button input (and when we’re putting our board together we’ll use it as a reset pin). Aside from that, we can stick the three selector bits wherever we want them, and still have the two serial port pins left for CAN interfacing if we want it, and another for supply voltage sensing (so we can determine whether the headlights are on or off and dim the display accordingly). The parts for these last two options aren’t included in the list above, because I haven’t added them yet.
Once the hardware is all set up, you need some software to run it, and that’s the tough part. The first order of business is reading the chip manuals thoroughly, and then finding yourself a C compiler, because programming in assembly is time consuming and rife with pitfalls. I started with cc5x but the free version only does up to 16 bit math, and it has a slew of other problems (“Can’t generate code” is the chief among them, requiring you to manually simplify your code and manually define intermediate variables for calculations). But I tried out SourceBoost and the free version has far fewer limitations. The code should first set up all the interrupts and config registers, define the BCD and seven segment driver code, set up the timers, and tell the main loop to show something useful. It’s best to stay simple; the first version of my code simply did a lamp test (all on, all off) and then displayed a rising sequence of numbers (which was actually just a spare timer I wasn’t using). This makes sure your lamps all work and your wiring is correct. Then you can start adding in code to test that the pulses are being received and triggering properly without bounce, probably using a counter.
It seems one of the LED 7-segment displays I bought has a bad lamp (D, the one on the bottom). Of course the test lamp feature caught this easily, but only after I soldered everything to a board. Note to self: in the future, use sockets instead of soldering 7 segment displays directly to a board.
It turned out the lamp wasn’t bad at all. It was just a short from overzealous soldering. Unfortunately I didn’t catch this until AFTER I had ripped out the display element, trashing it in the process. Oh well, there goes 200 yen. But the good news is that it works now, and I’ve worked out the software bugs I’ve been able to find.
I finally wired everything into the car. My wiring was fine, but I had lots of noise in the input signal, causing a small interrupt storm that sent the microcontroller into fits. A small capacitor (10nF) between the signal and ground wires at the speedometer fixed that problem. Driving around, the speedometer was just fine below 10kph, but above that there was a lot of jitter in the time between pulses. I’m tweaking the algorithm to average more and more pulses at higher speeds, but since memory is limited this is an interesting problem.
I’ve had a chance to run the speedometer in my car for several months, and it’s given me a chance to test it out. It works pretty well, is a lot more visible than my stock speedometer, and turns a few heads to boot. (“You built that YOURSELF??”)
Since several people have asked me to release the source, I will do so. The source is released under GPLv3. You are free to take the code and use it, modify it, and improve it. If you fix any bugs let me know so I can fix them on my speedometer too! Also if you build your own DIY speedometer and put it in a car or bike or whatever, I’d love to see it. Please ask me before using it in a commercial product; I’ll probably say OK but I would like to see the end result! The source does have known bugs which I haven’t gotten around to fixing; these are listed at the top of the source file in the comments. The biggest one is that quarter mile calibration still doesn’t work properly; with snow and ice on the ground I wasn’t too motivated to fix them. Now that spring has arrived, I can do so.
As the GPL works, if you redistribute assembled code including hardware with the code installed, you must distribute the source along with it. You got it for free, so you can’t complain.
The test modes included in my source code all let you test your wiring easily; simply set MODE_DEFAULT to MODE_TEST in the code, recompile and flash, and the tests will run when you apply power. The first test should display a constantly increasing number which wraps at 255. This makes sure your lamps are wired correctly and set up in software correctly. Press the button to go to the second test. The second test shows how long the button has been held down; a count of 30 is about one second. Use this to make sure your button works properly and isn’t bouncing. After about 15 seconds the pulse counter mode is activated. This will show how many speedometer input pulses have been received. This is handy to verify that noise isn’t triggering as pulses, and the pulses are registering properly. If all three of these tests work, then your wiring is correct and it should be possible to calibrate the speedometer (drive 50 and enter calibration mode) and display your speed.
The schematic I’d posted before was using the wrong parts in the wrong configuration. I’m not sure how I screwed this up, but it’s fixed now. Thanks, Velson.
Since I’ve moved back from Japan and have changed cars, I don’t have a working testbed anymore. Feel free to submit patches to the code or modifications to the schematic, and of course if something doesn’t look right please let me know so I can fix it.