Building a Digital Speedometer



I'm not a granny, and I don't drive

like one either, but a couple of weeks ago I was driving my fiancee’s car and noticed that people were just flying by me on the freeway. The speedometer said I was driving a little over the speed limit. By chance I passed one of those speed trap trailers that the police put on the side of the road. To my surprise there was a 10 mph differential between the speed indicated on the trap and the speed indicated on my speedometer! I instantly wondered how often my fiancee was honked at. So I did what any good enginerd/boyfriend would do; I built her a new one! In this tutorial I’ll show you how I used 4 different SparkFun products to build a pretty cool GPS powered digital speedometer.

I started with the Package Tracker (because I had a spare one on my desk), but you can use any GPS device you around that has a UART available. The code in this tutorial was written for the LPC2148 ARM7, which is on the Package Tracker, but it would be pretty simple to modify it to work on any platform. Along with the Package Tracker I needed a GPS module; I chose the EM408 because I'm familiar with it. I also needed a microSD card so that I could use the USB ARM Bootloader to load my code onto the board.

For the display I decided to use the monochrome Graphic LCD 128x64. I could have used a smaller text based LCD but I wanted to make the speedometer replicate the analog speedometer typically seen in your dashboard. To make interfacing with the LCD easier I decided to use the Serial Graphic LCD Backpack that Pete made for the Graphic LCD Huge 160x128. The only other product I ended up needing was the NCP1400 5V DC-DC Step-Up Converter Breakout Board so I could power the LCD.

Hardware Setup

There are four different products we have to connect to get this thing ready for programming: the Package Tracker, the Graphic LCD, the Serial Graphic Backpack, and the 5V Step-Up board. As should be obvious, the GPS module plugs directly into the Package Tracker and the microSD card also just gets inserted into the microSD socket on the board. Getting the LCD Backpack onto the LCD is as easy as soldering a female header onto one of the boards, and a male header onto the other, and plugging them in.

After reading the LCD datasheet I found that I had to power the LCD with 5 volts. Unfortunately the Package Tracker is a 3.3V system, and is powered by a 3.7V LiPo battery. Luckily SparkFun has a 5V DC-DC converter breakout board! Now we just have to figure out how to get power from the Package Tracker to the 5V step-up, and how to get the 5 volts from the step-up board to the LCD. The Package Tracker has a nice 6 pin header on one side that has VCC and GND. Maybe I can get power from this? Better check the schematic first though. Turns out we can’t use this VCC for the 5V step up board; the VCC on the header is from the 3.3V regulator on the Package Tracker. While 3.3V is enough for the step-up board we can only source 150 mA from this regulator. Considering the regulator has to power the LPC2148 plus all of the peripheral components on the Package Tracker I don’t want to risk adding another pretty big load to it by asking it to power the LCD and the Serial Backpack.

Since we can't use the VCC on the header we'll just have to take power directly from the battery to the 5V step-up board. I put the step-up board on a breadboard so that it was easier to work with, but you could probably figure out a way to get this working without it. Once we've got power to the breakout board, we'll just connect the 5V output to the Serial Backpack and we're good to go!

Writing the Code

The code for this project can be split

up into two general parts: getting the speed data from the GPS module, and sending the data to the LCD. The first thing we have to do, though, is draw the speedometer. The speedometer is really just a circle with a bunch of numbers placed inside of it. Pete did a great job on the Serial Graphic Backpack firmware and included a circle command, as well as text commands. All I had to do was figure out how big I wanted the circle to be and where to place it, along with where I wanted to place all of the text. Once I knew these things I just had to send the serial commands to the LCD Backpack. The serial commands for the LCD Backpack can be found here in the Serial Graphic Backpack datasheet.

To be honest I found the size and location of the circle and numbers by using trial and error (...a lot of error, let me tell you that!). You can see all of the numbers in the drawSpeedometerOutline function of the Speedometer.c file and the Speedometer.h file, but I can quickly tell you that I used a circle with a radius of 34 pixels centered at the X,Y coordinates 34,26. Does it seem weird to you that the center of the circle is at a height of 26 pixels, but the radius of the circle is 34? It should seem as if the circle would run off the bottom of the LCD. And it does! But this was precisely the look I was going for.

Now that I have the outline of my speedometer I just need a ?needle? to indicate the current speed. I thought the easiest way to do this would be to use the LINE command on the serial backpack, however I would still need to know the coordinates for the line that corresponded to each tick on the speedometer. Unfortunately this called for a little bit of trigonometry! I suppose I could have stuck with my trial and error method, but I wanted to finish sometime this century; so I busted out the calculator (well, actually I busted out Excel but you get the picture). Take a look at this figure and the equations to see how I was able to get the line coordinates.

We know one set of coordinates for the needle will be the center of the circle; the length of the needle is up to you. I chose my needle to be 20 pixels long because it seemed like the needle could go all the way around the speedometer without running over the numbers inside the circle. Knowing these two things would allow me to find the second set of X,Y coordinates for each MPH on the speedometer. The first thing I did was relate the degrees of the circle to a speed. I want the speedometer to display up to 100 mph (even though there's no way that car will EVER hit that speed). So if I estimated that if I used the entire circle for speed I would have 133 mph. With this number I found that (1 MPH) is equal to (2.7 Degrees) on the circle. Now I can use basic trigonometric functions to find all of my coordinates. To find the X coordinate I use the equation:

X Length = sin(MPH * 2.7) * 20

And to find the Y coordinate I use the equation:

Y Length = cos(MPH * 2.7) * 20

I would need to perform this equation for each MPH unit on the speedometer to get all of the X,Y coordinates; but a simple spreadsheet can perform this for me in a flash. You can see the spreadsheet I used in this file. After running the calculation I just copied all of the coordinates into an array in my code.

Whew! That's definitely enough math for one day. After running some tests with my new coordinates I had some good news and some bad news. The good news is that the equations worked and the speedometer needle seemed to be right on track. The bad news was that the LINE command wasn't going to be fast enough to use in the system. This wasn't too much of a hassle, I took the code for the LCD backpack and added a new command called SPEED; it basically just takes an input speed and draws a line.

So now we have a speedometer drawing, and we can send a speed to it. All we have left is to get our speed! We'll get our speed from the GPS module on the LPC2148. If you've never used a GPS module before, don't be intimidated. It's very easy. In fact the only thing we do in this application is specify which NMEA message we want to receive, and then listen to the messages on the UART. You can find more about NMEA messages in this document. I chose to listen to the RMC message because it includes ground speed in the output. How simple is that! Every time I receive an RMC message from the GPS module, I just extract the speed from the message. The ground speed is reported in meters per second, so I just convert the units to miles per hour by multiplying the value by 1.151. It's not a perfect conversion but it will do.

Once I have the speed from the GPS module, I just need to post it to the LCD by sending my SPEED command. However, my GPS module only updates once every second. This means that my speed value can jump around. I don't want the needle on my speedometer jumping around! I want the needle to increment from it's current position to it's new position one mile per hour at a time. This is pretty easy to do by using two different variables for speed: one of the variables keeps track of the GPS speed, and the other variable acts like a ?chaser? that is always trying to catch the GPS speed. So, the GPS speed might go from 0 to 5 MPH in one reading; but the ?chaser? value has to increment it's speed by going from 0 to 1, 1 to 2 and so on. Then instead of sending the GPS speed straight to the LCD, we send the ?chaser? speed to the LCD so we get a more realistic visual on the LCD.

Where To Go From Here

The digital speedometer works great, but what can you do to it to make it better? I’ve created a list of improvements that could be made to our project. I’d love to see what you come up with!
  • Adding a Compass (Difficulty: Beginner)-One of the RMC message fields from the GPS module is Heading. Use this value to add a compass to the speedometer
  • Add Temperature (Difficulty: Intermediate) - The package tracker also has an SCP1000 and an SHT15 on board. Each of these sensors also has a temperature sensor on it. Add in sensor communication and display the temperature on the speedometer.
  • Improve the Speed Accuracy Part 1 (Difficulty: Advanced)-Currently the speed for the speedometer is only updated at 1 Hz because that’s how often we receive updates from the GPS module. Use an accelerometer (on board the Package Tracker) to determine intermediate speeds by integrating the acceleration values.
  • Improve the Speed Accuracy Part 2 (Difficulty: Advanced)-The problem with using speed from the GPS module is that GPS actually provides the Speed Over Ground value, not the actual speed. So if you’re going up a steep incline or decline the vehicle speed won’t be accurately reflected in the ground speed. You can compensate for this in one of two ways:
    • Use static measurements from an accelerometer to determine the tilt of the vehicle, and modify the speed accordingly. The problem with this method is that the orientation of the speedometer must be fixed to get accurate results
    • Enable GGA messages in the GPS module. GGA messages include the current altitude of the device. Use the altitude differental between points to calculate the slope of the vehicle path and adjust the speed accordingly.
I hope you enjoyed this tutorial! If you have any problems, or see any mistakes be sure to let us know by leaving a comment!

Comments 16 comments

  • It seems that the LCD used in this project is only sold without the backpack, and the backpack is only sold pre-attached to the “huge” graphic LCD. Can we get them sold together or at least the backpack sold by itself? Pretty please?

  • Thanks for the tutorial!
    Being the laziest person on the planet, I would have just used any off-the-shelf GPS unit – they all have a speedometer function. (I know… what’s the fun in that?)

    • JSAlexander?(or?anybody?with?this?knowledge),?can?you?please?explain?how?to?get

      • explain how to get speed data from a GPS receiver into a PIC microcontroller.

  • You know you didn’t have to cone op with a data table to use in the Arduino code right? Arduino is capable of doing really efficient on-board trigonometry using the math.h library. you did know this right? or at least found out after doing this project??!

  • I like this project, I’ve been thinking of doing something like this for a motorcycle because the original speedometer is getting a bit clunky.
    But there’s one thing I would do different and that would be to use the factory speedometer, and output your speed to that.
    This of course requires another project, but one which I have been looking into. If this car is like most, it will have an air-core movement driving the speedometer needle. To make it easier to figure out, I would cut all the wiring to this movement and wire-in an all new circuit using something like a CS4192 air-core driver chip ( This takes a serial input and calculates the correct polarity and voltage to send to the two coils in the meter movement.
    What I have built so far is based on the CS8190, which uses an analog input to drive an air-core movement over 300 degrees. This is being used with an LC-1 air/fuel ratio controller. So I have a gauge that shows the A/F ratio using a nice 6 inch needle which used to be a tachometer from a Ford. It just happened to work out that a 5 volt signal put the needle almost exactly at the 270 degree position, so it ‘looks’ right. Then I printed a new gauge face on acid-free matte photo paper. I had to make a traditional light to show the gauge face at night because the factory approach illuminates the face from behind, but the needle is still lit-up the same way as originally.
    What I want to do is use the digital output on the LS-1, to get better accuracy. So I’m trying to learn how to use digital signals. Not there yet…

  • This is great! I am going to use this code for my senior design. I do have some questions though.
    Did you actually modify the serial LCD backpack firmware? I noticed that you said you made a SPEED command, because the LINE command is too slow. Does that mean I will run into problems if I use this LINE command? I don’t understand how it is too slow anyways….
    Also, am I correct in saying that you put the LCD speedometer coordinates in the firmware code. Can I get by not messing with the firmware code at all and still making this speedometer?

  • I’ve tried the above method and used trigonometry and formula above, but my needle is all over the place.
    I’ve even tried just giving it values manually, but the needle start somehwere and jumps here and tehre every time a new value is entered!
    What am I doing wrong?

  • Hi,
    Thanks for sharing your project.
    I thought about using the car's
    onboard computer to obtain speedometer
    information but I think the GPS
    would definitely make it independent
    of any car electronics.
    I think one can modify this project
    to be an automotive noise maker for
    electric and hybrid cars which are
    currently so quiet you can easily get
    run over by them.

  • Good idea Starvin. Unfortunately,I have a Buick that I bought new 20 years ago and parts are getting hard to find. I’m on my third VSS, which was bought salvaged, and it’s intermittent as is the display itself.
    A GPS speedo would be a relatively quick fix and something interesting to design and build as well. Something I could scratch off of my list of things to worry about :-)

  • i love the out of the box thinking but would have suggested a signal from the Vehicle speed sensor (located on the transmission) I don’t know what the honda voltages are but my dodge is 5V. So then you could check it against the GPS sig figure out what the difference was then make corrections to the signal from the VSS (which was reading slow on the stock speedo)
    I am a car nut and am learning a lot more about my complicated car every day. I hope to contribute what I know about cars in exchange for some EE learning!

  • Hi, my name is Artiom and I am an Electrical Engineering student at University of Central Florida. I came across this tutorial when I was looking for ideas in implementing a GPS module in our project for Senior Design. I found your tutorial to be very helpful and I would like to have your permission to implement parts of your code in my project.
    Please contact me at
    Thank You,

  • Hey there, I stumbled across this post and wanted to let you know that it was the speed sensor and/ or the lines to the ECU on the car that caused the speed problem…I own two EG civics and both had similar problems. The electronic speed sensors from later model EK model civics can be used to interface to different gauge clusters too. if you still have the car it might be worth hacking on it a little bit more.

  • Cory, I am wanting to build something like this but can measure the speed of short distance movements, in the range of a few feet that move 0.3 to 2.0 m/s. Is this possible do you know? If it is and you’d like to make a few extra $ I’d love the help.

  • LOL. You?ve created a digital representation of an analog gauge. Thanks for posting this!

  • Great tutorial!
    Another way to get a more accurate velocity measurement would be to put the GPS in to binary mode so you can get your X, Y, Z position in ECEF (earth centered earth fixed) format. Then d/dt (sqrt(X2 + Y2 + Z2)) gives you your actual speed in m/s. Even if you drive off a cliff!