Arduino Projects

Arduino Speedometer with Neo 6M GPS Module & OLED Display

GPS Speedometer on OLED Display using Arduino

Overview: Arduino GPS Speedometer

This article provides a step-by-step guide on creating a DIY Arduino-based Analog Gauge speedometer using a Neo 6M GPS module. This project aims to measure the speed of a moving object and display it in real time on a 0.96-inch I2C OLED display. The speedometer will showcase the speed in both classic analog gauge style and digital digits. The displayed speed will be measured in miles per hour (MPH).

GPS speedometers are more accurate than traditional speedometers because they can continually track the vehicle’s location and calculate its speed. Traditional speedometers rely on a mechanical cable or sensor to measure the speed of the wheels, which can be affected by factors such as tire wear and road conditions. GPS speedometers, on the other hand, use the Global Positioning System (GPS) to track the vehicle’s location and calculate its speed. GPS is a satellite-based navigation system that provides accurate location information.

Arduino Digital Speedometer with Neo 6M GPS Module & OLED Display

You can also check our previous articles on How to Interface NEO 6M GPS V2 Module with Arduino.


Components Required

Followings are the list of the components that are required for making an “Arduino Speedometer with Neo 6M GPS Module & OLED Display“. You can easily purchase them from the link provided:

S.NCOMPONENTS NAMEQUANTITYPURCHASE LINKS
1Arduino Nano1 Amazon | AliExpress
2Neo 6M GPS module1 Amazon | AliExpress
3SSD1306 0.96″ OLED display1 Amazon | AliExpress
418650 Battery Holder1 Amazon | AliExpress
53.7V 18650 Li-Ion Battery1 Amazon | AliExpress
6TP4056 Module1 Amazon | AliExpress
7Jumper wires10 Amazon | AliExpress
8Breadboard1 Amazon | AliExpress
Components required for Arduino GPS Speedo Meter on OLED Display

*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!


NEO 6M GPS Module

For the Speedometer project will be using the NEO-6M GPS V2 module. The NEO-6M is a popular GPS receiver that has a built-in ceramic antenna, it offers useful satellite search capabilities. With the ability to track up to 22 satellites, it provides accurate location coordinates of your position in the world.

The onboard signal LED indicator will help to monitor the GPS network status. It has a small battery for data backup. With the help of this small battery, GPS module can save data on it’s memory when the main power is shut down.

NEO 6M GPS Module Components

The NEO-6M GPS module uses the powerful NEO-6M GPS chip from u-blox. This chip allows us to track up to 22 satellites on 50 channels. It has a very special sensitivity level which is -161 dBm, meaning it can receive even weak signals effectively.

Specifications of NEO6M GPS Module

  • GPS Chip: u-blox NEO-6M
  • Satellite Tracking: Up to 22 satellites
  • Channels: 50 channels for positioning
  • Sensitivity: -161 dBm
  • Positioning Engine: u-blox 6
  • TTFF: Under 1 second
  • Baud Rate: Supports 4800-230400 bps (default: 9600 bps)
  • Antenna: Built-in ceramic antenna
  • Data Backup: Data backup battery
  • Compatibility: Arduino, Raspberry Pi, and other microcontrollers
  • Operating Voltage: 3.3V-5V
  • Power Consumption: Low power consumption
  • Communication: Serial communication (UART)
  • Output: NMEA protocol
  • Applications: Navigation, tracking, robotics, geocaching, and more.

Pinout of NEO6M GPS Module

Pinout for the NEO-6M GPS Module:

NEO 6M GPS Module Pinouts
  • VCC: Input voltage pin
  • RX: Receive pin for UART communication
  • TX: Transmit pin for UART communication
  • GND: Ground pin

Arduino GPS Speedometer Circuit Diagram

Now let us see the circuit diagram of the Arduino Speedometer with Neo 6M GPS Module & OLED Display. The connection diagram is simple and easy to assemble as shown in the image below.

Circuit Diagram of Arduino Speedometer with Neo 6M GPS Module & OLED Display
  • 1. Connect the VCC pin of the GPS module to the 3.3V pin of the Arduino.
  • 2. Connect the GND pin of the GPS module to the GND pin of the Arduino.
  • 3. Connect the RX pin of the GPS module to digital pin 3 on the Arduino.
  • 4. Connect the TX pin of the GPS module to digital pin 4 on the Arduino.
  • 5. Connect the VCC and GND pins of the I2C OLED display to the Arduino’s 5V and GND pins, and connect the SDA and SCL pins to the Arduino’s A4 and A5 pins, respectively.
Circuit Assembly of Arduino GPS Speedo meter

I have used TP4056 charging module for charging 18650 battery. The whole system is powered with a single 3.7V Li-ion battery. as shown in the above circuit diagram.


NextPCB for PCB Manufacturing Services

In electronics projects, the quality of printed circuit boards (PCBs) plays an important role in the performance and reliability of electronic devices. For my recent project, I was fortunate to have the support and assistance of NextPCB, a leading PCB manufacturer renowned for its wonderful services and commitment to customer satisfaction.

NextPCB offers an extensive range of high-quality, reliable PCBs at competitive prices. Their single-layer PCBs start at just $1.9, while multilayer PCBs start at $6.9. You can also enjoy the free PCB Assembly for 5 boards in your first order. This makes NextPCB an ideal choice for both hobbyists and professionals.

If you are looking for a reliable and cost-effective PCB manufacturer, NextPCB is an excellent choice. With their commitment to quality, fast turnaround times, and value-added services, NextPCB is the ideal partner for your PCB manufacturing needs.


Arduino Source code for GPS Speedometer on OLED

To make program your own Arduino based GPS Speedometer you need to install the following required libraries on Arduino IDE.

Download the TinyGPS++ library and add it to the Arduino library folder.
Download the U8glib library and add it to the Arduino library folder.

There are two different variant of Arduino based GPS Speedometer using Neo 6M GPS Module and 0.96 inch OLED Display.

Simple GPS Speedometer with Analog Gauge & Digital Numbers on OLED

Arduino OLED Speedometer Code.

#include "U8glib.h"

// if you are using SPI version of the display (with 7 pins), comment the first line and uncomment the second line
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI
//U8GLIB_SSD1306_128X64 u8g(13, 11, 8, 9, 10); // SPI connection  - SCL = 13, SDA = 11, RES = 10, DC = 9, CS = 8

#include <SoftwareSerial.h>
#define rxPin 4
#define txPin 3 
SoftwareSerial neogps(rxPin,txPin);
 
#include <TinyGPS++.h> //1.0.3
TinyGPSPlus gps;
double lat;
double lng;
int num_sat, speed;
String heading;

const unsigned char bitmap_center_fill [] PROGMEM = {
	0x00, 0x3c, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x00
};
// 'center_outline', 8x8px
const unsigned char bitmap_center_outline [] PROGMEM = {
	0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00
};
// 'gauge_bg', 72x64px
const unsigned char bitmap_gauge_bg [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x20, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 
	0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xff, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 
	0x3c, 0x00, 0x3c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0xc0, 0x00, 0x03, 0x84, 0x00, 0x00, 0x00, 
	0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x00, 0x02, 0x18, 0x00, 
	0x00, 0x00, 0x04, 0x20, 0x40, 0x00, 0x02, 0x04, 0x20, 0x00, 0x00, 0x04, 0xc0, 0x20, 0x00, 0x04, 
	0x03, 0x20, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x84, 0x00, 0xef, 0x00, 0xcf, 0x00, 0x21, 0x00, 0x00, 0x48, 
	0x01, 0x09, 0x01, 0x29, 0x00, 0x12, 0x00, 0x00, 0x10, 0x01, 0xc9, 0x00, 0xc9, 0x00, 0x08, 0x00, 
	0x00, 0x20, 0x01, 0x29, 0x01, 0x29, 0x00, 0x04, 0x00, 0x00, 0x40, 0x01, 0x29, 0x01, 0x29, 0x00, 
	0x02, 0x00, 0x04, 0x40, 0x00, 0xcf, 0x00, 0xcf, 0x00, 0x02, 0x20, 0x02, 0x80, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x01, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x20, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x22, 
	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44, 0x14, 0x01, 0x2f, 0x00, 0x00, 0x02, 0xf7, 0x80, 
	0x28, 0x04, 0x01, 0x29, 0x00, 0x00, 0x02, 0x94, 0x80, 0x20, 0x04, 0x01, 0xe9, 0x00, 0x00, 0x02, 
	0x94, 0x80, 0x20, 0x08, 0x00, 0x29, 0x00, 0x00, 0x02, 0x94, 0x80, 0x10, 0x08, 0x00, 0x29, 0x00, 
	0x00, 0x02, 0x94, 0x80, 0x10, 0xc8, 0x00, 0x2f, 0x00, 0x00, 0x02, 0xf7, 0x80, 0x13, 0x08, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 
	0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xd0, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x39, 0xe0, 
	0x00, 0x00, 0x00, 0x17, 0x3c, 0x08, 0x10, 0x05, 0x20, 0x00, 0x00, 0x00, 0x10, 0xa4, 0x08, 0x10, 
	0x09, 0x20, 0x00, 0x00, 0x00, 0x11, 0x24, 0x08, 0x17, 0x91, 0x20, 0x00, 0x00, 0x00, 0x12, 0x25, 
	0xe8, 0xd0, 0x21, 0x20, 0x00, 0x00, 0x00, 0x14, 0x24, 0x0b, 0x10, 0x3d, 0xe0, 0x00, 0x00, 0x00, 
	0x17, 0xbc, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 
	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x20, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x29, 0x78, 0x00, 0x40, 0x02, 0x00, 0x12, 0x00, 0x00, 
	0x29, 0x48, 0x00, 0x40, 0x09, 0x00, 0x12, 0x00, 0x00, 0x2f, 0x48, 0x00, 0x90, 0x11, 0x00, 0x12, 
	0x00, 0x00, 0x21, 0x48, 0x00, 0x88, 0x00, 0x80, 0x12, 0x00, 0x00, 0x21, 0x48, 0x01, 0x00, 0x00, 
	0x40, 0x1e, 0x00, 0x00, 0x21, 0x78, 0x02, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x02, 0x02, 
	0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x88, 0x80, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x04, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
};
// 'mph_label', 16x6px
const unsigned char bitmap_mph_label [] PROGMEM = {
	0x8b, 0xd2, 0xda, 0x52, 0xab, 0xde, 0x8a, 0x12, 0x8a, 0x12, 0x8a, 0x12
};

// 'digit_0', 16x28px
const unsigned char bitmap_digit_0 [] PROGMEM = {
	0x0f, 0xff, 0x1f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 
	0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 
	0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xf8
};
// 'digit_1', 16x28px
const unsigned char bitmap_digit_1 [] PROGMEM = {
	0x01, 0xf8, 0x03, 0xf8, 0x07, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 
	0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 
	0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 
	0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8
};
// 'digit_2', 16x28px
const unsigned char bitmap_digit_2 [] PROGMEM = {
	0x0f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 
	0xfc, 0x3f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0xff, 0x01, 0xfe, 0x03, 0xfc, 0x0f, 0xf8, 0x1f, 0xf0, 
	0x3f, 0xe0, 0x7f, 0xc0, 0xff, 0x00, 0xfe, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// 'digit_3', 16x28px
const unsigned char bitmap_digit_3 [] PROGMEM = {
	0x0f, 0xfe, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xf8, 0x3f, 
	0xf8, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xfe, 0x00, 0xfc, 0x00, 0xf8, 0x00, 0xff, 0x00, 0xff, 
	0x00, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xf8
};
// 'digit_4', 16x28px
const unsigned char bitmap_digit_4 [] PROGMEM = {
	0x00, 0x70, 0x00, 0x78, 0x00, 0xfc, 0x00, 0xfe, 0x01, 0xff, 0x03, 0xff, 0x03, 0xff, 0x07, 0xff, 
	0x07, 0xff, 0x0f, 0xff, 0x0f, 0xbf, 0x1f, 0xbf, 0x1f, 0x3f, 0x3f, 0x3f, 0x3e, 0x3f, 0x7e, 0x3f, 
	0x7c, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 0x7f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x1f, 0xff, 0x00, 0x3f, 
	0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f
};
// 'digit_5', 16x28px
const unsigned char bitmap_digit_5 [] PROGMEM = {
	0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfc, 0x00, 0xfc, 0x00, 
	0xfc, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0xff, 0xf0, 0xff, 0xf8, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 
	0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x7f, 0xff, 0xff, 
	0xff, 0xff, 0x7f, 0xff, 0x3f, 0xff, 0x1f, 0xff
};
// 'digit_6', 16x28px
const unsigned char bitmap_digit_6 [] PROGMEM = {
	0x0f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 
	0xfc, 0x3f, 0xfc, 0x00, 0xfc, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x7f, 0xff, 0xff, 
	0xff, 0xfe, 0xff, 0xfc, 0xff, 0xf8, 0xff, 0xf0
};
// 'digit_7', 16x28px
const unsigned char bitmap_digit_7 [] PROGMEM = {
	0xff, 0xf0, 0xff, 0xf8, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3e, 0xf8, 0x7e, 
	0xf8, 0x7e, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x03, 0xf0, 
	0x03, 0xf0, 0x07, 0xf0, 0x07, 0xe0, 0x07, 0xe0, 0x0f, 0xc0, 0x0f, 0xc0, 0x0f, 0xc0, 0x1f, 0x80, 
	0x1f, 0x80, 0x3f, 0x80, 0x3f, 0x00, 0x3f, 0x00
};
// 'digit_8', 16x28px
const unsigned char bitmap_digit_8 [] PROGMEM = {
	0x0f, 0xfe, 0x1f, 0xfe, 0x3f, 0xfe, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfc, 0x3e, 0xfc, 0x3e, 
	0xfc, 0x3e, 0xfc, 0x3e, 0xfc, 0x3e, 0xfc, 0x3e, 0xff, 0xfc, 0xff, 0xf8, 0x1f, 0xff, 0x7f, 0xff, 
	0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xf8
};
// 'digit_9', 16x28px
const unsigned char bitmap_digit_9 [] PROGMEM = {
	0x07, 0xff, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xfc, 0x3f, 
	0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xf8
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 800)
const int bitmap_allArray_LEN = 10;
const unsigned char* bitmap_allArray[10] = {
	bitmap_digit_0,
	bitmap_digit_1,
	bitmap_digit_2,
	bitmap_digit_3,
	bitmap_digit_4,
	bitmap_digit_5,
	bitmap_digit_6,
	bitmap_digit_7,
	bitmap_digit_8,
	bitmap_digit_9
};

char speed_string[10]; // speed number value converted to c-style string (array of characters)
int speed_string_length; // length of the speed_string
int speed_string_start_pos; // start x position for the big numbers - calculated based on the number of digits

int needle_angle_deg = 45; // angle of the needle in degrees, based on potentiometer value

int needle_start_x; // needle start point, x position
int needle_start_y; // needle start point, y position
int needle_end_x; // needle end point, x position
int needle_end_y; // needle end point, y position
int needle_center_x = 36; // needle center position, x position
int needle_center_y = 36; // needle center position, y position
int needle_radius_big = 30; // lenght of needle
int needle_radius_small = 10; // lenght of "tail" of the needle
int needle_offset_x; // second line offset x
int needle_offset_y; // second line offset y

void setup() {
  Serial.begin(9600);
  neogps.begin(9600);
  u8g.setFont(u8g_font_tpssb); // set u8g font, although this is not used anywhere
  u8g.setColorIndex(1); // set drawing color to white

}

void Read_GPS(){
  //------------------------------------------------------------------
  boolean newData = false;
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (neogps.available())
    {
      if (gps.encode(neogps.read()))
      {
        newData = true;
        break;
      }
    }
  }
  //------------------------------------------------------------------
  //If newData is true
  if(newData == true){
    newData = false;
    Get_GPS();
  }
  else { 
    //no data
  }
}

void Get_GPS(){
  num_sat = gps.satellites.value();
 
  if (gps.location.isValid() == 1) {
    speed = gps.speed.kmph();
    Serial.print("Speed: ");Serial.println(speed);
    lat = gps.location.lat();
    Serial.print("lat: ");Serial.println(lat);
    lng = gps.location.lng();
    Serial.print("lng: ");Serial.println(lng);
    heading = gps.cardinal(gps.course.value());
    Serial.print("heading: ");Serial.println(heading);
  }
}

void loop() {
  Read_GPS();

  //if(speed>140)
  //speed=0;

  itoa (speed, speed_string, 10); // convert speed integer to c-style string speed_string, decimal format
  speed_string_length = strlen(speed_string); // get speed_string length
  speed_string_start_pos = 99 - speed_string_length * 8; // start x position of the big numbers

  needle_angle_deg = map(speed, 0, 140, 45, 270+45); // calculate the angle in degrees based on the speed value, between 45-315

  needle_start_x = needle_radius_big * -sin(radians(needle_angle_deg)) + needle_center_x; // calculate needle start x position
  needle_start_y = needle_radius_big *  cos(radians(needle_angle_deg)) + needle_center_y; // calculate needle start y position

  needle_end_x = needle_radius_small * -sin(radians(needle_angle_deg + 180)) + needle_center_x; // calculate needle end x position
  needle_end_y = needle_radius_small *  cos(radians(needle_angle_deg + 180)) + needle_center_y; // calculate needle end y position

  // calculate offset for the second line for the needle, based on the needle slope
  if ((needle_angle_deg > 45 && needle_angle_deg < 135) || (needle_angle_deg > 225 && needle_angle_deg < 315)) {
    // needle is more horizontal, offset the second line by y
    needle_offset_x = 0;
    needle_offset_y = 1;    
  } else {
    // needle is more vertical, offset the second line by x
    needle_offset_x = 1;
    needle_offset_y = 0;    
  }

  u8g.firstPage(); // u8g drawing 
  do {

     //u8g.drawStr(90, 20, speed_string); // draw speed_string, not needed anymore

     for (int i = 0; i < speed_string_length; i++) { // loop for every speed_string character
       // draw the big digit
       // subtract value 45 from the character value, since the ASCII value of digit "0" is 48
       u8g.drawBitmapP(speed_string_start_pos + 18 * i, 16, 16/8, 28, bitmap_allArray[speed_string[i] - 48]); 
     }

     u8g.drawBitmapP(  0,  0, 72/8, 64, bitmap_gauge_bg); // draw gauge background image

     u8g.drawLine(needle_start_x, needle_start_y, needle_end_x, needle_end_y); // draw first line for the needle
     u8g.drawLine(needle_start_x+needle_offset_x, needle_start_y+needle_offset_y, needle_end_x+needle_offset_x, needle_end_y+needle_offset_y); // draw second line for the needle

     u8g.setColorIndex(0); // black color
     u8g.drawBitmapP( 32, 33,  8/8,  8, bitmap_center_fill); // draw needle center cover
     u8g.setColorIndex(1); // white color
     u8g.drawBitmapP( 32, 33,  8/8,  8, bitmap_center_outline); // draw needle center piece
     
     u8g.drawBitmapP( 93, 46, 16/8,  6, bitmap_mph_label); // draw MPH label
    

  } while ( u8g.nextPage() );
//--------------------------------
}


GPS Speedometer with Gauge & NTP Clock on OLED

Arduino OLED Speedometer Code – Odometer with Analog Gauge and NTP Clock, Location coordinates, and Number of satellites connected.

//---------------------------------------------------------------------------
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);
//---------------------------------------------------------------------------
const int timezone_hours = 5;
const int timezone_minutes = 0;
//---------------------------------------------------------------------------
#include <SoftwareSerial.h>
#define rxPin 4
#define txPin 3 
SoftwareSerial neogps(rxPin,txPin);

#include <TinyGPS++.h> //1.0.3
TinyGPSPlus gps;
//---------------------------------------------------------------------------
int x_max    = 128;       //OLED display width, in pixels
int y_max    = 62;        //OLED display width, in pixels
int x_center = x_max/2;
int y_center = y_max/2+10;
int arc      = y_max/2;
int angle    = 0;
//---------------------------------------------------------------------------
int needle_pos = 0;
u8g_uint_t xx  = 0;
//---------------------------------------------------------------------------
//satellite logo
#define sat_logo_width 20
#define sat_logo_height 20

const unsigned char sat_logo[] = {
  0x00, 0x01, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x06, 0x00, 0x60, 0x30, 0x00,
  0x60, 0x78, 0x00, 0xc0, 0xfc, 0x00, 0x00, 0xfe, 0x01, 0x00, 0xff, 0x01,
  0x80, 0xff, 0x00, 0xc0, 0x7f, 0x06, 0xc0, 0x3f, 0x06, 0x80, 0x1f, 0x0c,
  0x80, 0x4f, 0x06, 0x19, 0xc6, 0x03, 0x1b, 0x80, 0x01, 0x73, 0x00, 0x00,
  0x66, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x70, 0x00, 0x00
};
//---------------------------------------------------------------------------

//Program variables
double lat;
double lng;
//int day, month, year;
String hours, minutes;
int second;

int num_sat, speed;
String heading;


/*******************************************************************************************
 * gauge function
 * dispay gauge and other gps data on oled
*******************************************************************************************/
void gauge(uint8_t angle) {
  //fonts: u8g_font_chikita - u8g_font_04b_03br
  // u8g_font_orgv01 - u8g_font_freedoomr10r
  u8g.setFont(u8g_font_chikita);
  //---------------------------------------------------------------------------
  // draw border of the gauge
  u8g.drawCircle(x_center,y_center,arc+6, U8G_DRAW_UPPER_RIGHT);
  u8g.drawCircle(x_center,y_center,arc+4, U8G_DRAW_UPPER_RIGHT);
  u8g.drawCircle(x_center,y_center,arc+6, U8G_DRAW_UPPER_LEFT);
  u8g.drawCircle(x_center,y_center,arc+4, U8G_DRAW_UPPER_LEFT);
  //---------------------------------------------------------------------------
  // show gauge values
  u8g.drawStr(20,  42, "0");   
  u8g.drawStr(18,  29, "25");
  u8g.drawStr(28,  14, "50");
  u8g.drawStr(60,  14, "100");
  u8g.drawStr(91,  14, "150");
  u8g.drawStr(101, 29, "175");
  u8g.drawStr(105, 42, "200"); 
  //---------------------------------------------------------------------------
  // show gauge label
  u8g.setPrintPos(54,25);
  u8g.print("km/h");
  u8g.setPrintPos(50,32);
  u8g.print("SPEED");
  //---------------------------------------------------------------------------
  // draw the needle
  float x1=sin(2*angle*2*3.14/360);           
  float y1=cos(2*angle*2*3.14/360); 
  u8g.drawLine(x_center, y_center, x_center+arc*x1, y_center-arc*y1);
  u8g.drawDisc(x_center, y_center, 5, U8G_DRAW_UPPER_LEFT);
  u8g.drawDisc(x_center, y_center, 5, U8G_DRAW_UPPER_RIGHT);
  //---------------------------------------------------------------------------
  //TOP LEFT: draw satellite logo and number of satellites
  u8g.drawXBM(0, 0, sat_logo_width, sat_logo_height, sat_logo);
  u8g.setPrintPos(18, 5);
  u8g.print(num_sat, 5);
  //---------------------------------------------------------------------------
  //TOP RIGHT: Display direction
  u8g.setPrintPos(110, 5);
  u8g.print(heading);
  //---------------------------------------------------------------------------
  //Display latitude and longitude
  u8g.setPrintPos(0, 55);
  u8g.print(lat, 4);
  u8g.setPrintPos(0, 62);
  u8g.print(lng, 4);
  //---------------------------------------------------------------------------
  //Display time
  u8g.setFont(u8g_font_freedoomr10r);
  u8g.setPrintPos(90, 65);
  u8g.print(hours);
  
  if(second%2 == 0) 
    {u8g.drawStr(104, 65, ":");}
  else
    {u8g.drawStr(104, 65, " ");}
    
  u8g.setPrintPos(111, 65);
  u8g.print(minutes);
  
  //u8g.drawStr(90, 65, "00:00");
  //---------------------------------------------------------------------------
  // Show Speed and align its position
  u8g.setFont(u8g_font_profont22);
  u8g.setPrintPos(54,60);
  if (speed<10){                              
    u8g.print("0");
  }
  if (speed>99) {                                  
    u8g.setPrintPos(47,60);
  }
  u8g.print(speed);
  //---------------------------------------------------------------------------
}

/*******************************************************************************************
 * gauge function
 * dispay gauge and other gps data on oled
*******************************************************************************************/
int i = 200;
void setup(void) {
  Serial.begin(9600);

  neogps.begin(9600);
  
  u8g.setFont(u8g_font_chikita);
  u8g.setColorIndex(1);
}

/*******************************************************************************************
 * gauge function
 * dispay gauge and other gps data on oled
*******************************************************************************************/
void loop(void){
  //----------------------------------------------------------
  Read_GPS();
  //----------------------------------------------------------
  needle_pos = map(speed,0,200,0,90); //SET NEEDLE
  // show needle and dial
  xx = needle_pos;                                    
  if (xx<45)
    {xx=xx+135;}
  else
    {xx=xx-45;} 
  //----------------------------------------------------------
  //Display Data on Oled
  {
    u8g.firstPage(); 
    do {             
      gauge(xx);
    }
    while( u8g.nextPage() );
  }
  //----------------------------------------------------------
}




void Read_GPS(){
  //------------------------------------------------------------------
  boolean newData = false;
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (neogps.available())
    {
      if (gps.encode(neogps.read()))
      {
        newData = true;
        break;
      }
    }
  }
  //------------------------------------------------------------------
  //If newData is true
  if(newData == true){
    newData = false;
    Get_GPS();
  }
  else { 
    //no data
  }
}


void Get_GPS(){
  num_sat = gps.satellites.value();

  if (gps.location.isValid() == 1) {
    speed = gps.speed.kmph();
    //Serial.print("Speed: ");Serial.println(gps_speed);
    lat = gps.location.lat();
    //Serial.print("lat: ");Serial.println(lat);
    lng = gps.location.lng();
    //Serial.print("lng: ");Serial.println(lng);
    heading = gps.cardinal(gps.course.value());
    //Serial.print("heading: ");Serial.println(heading);
  }
  
  if (gps.time.isValid()){
    int hour = gps.time.hour();
    int minute = gps.time.minute();
    second = gps.time.second();

    abc(hour, minute);
    
    hours   = String(hour);
    minutes = String(minute);
    hours = (hours.length() == 1) ? "0"+hours : hours;
    minutes = (minutes.length() == 1) ? "0"+minutes : minutes;
  }
}

void abc(int &hour, int &minute){
    Serial.println("UTC =  "+String(hour)+":"+String(minute)+":"+String(second));
    minute = minute + timezone_minutes;
    if(minute >= 60){
      minute = minute - 60;
      hour = hour + 1;
    }
    if(minute < 0){
      minute = minute + 60;
      hour = hour - 1;
    }
    hour = hour + timezone_hours;
    if(hour >= 24){
      hour = hour - 24;
    }
    else if(hour < 0){
      hour = hour + 24;
    }
    if(hour >12){
      hour = hour - 12;
    }
    Serial.println("Time=  "+String(hour)+":"+String(minute)+":"+String(second));
}

Testing of Arduino GPS Speedometer

Once you upload the program code on to your Arduino board. You will need some time to get signals from GPS module. The GPS signals synchronization with number of satellites is directly affected by the environmental conditions as well as the module used inside or outside environment.

Arduino Analog Speedometer with Neo 6M GPS Module & OLED Display

Arduino Speedometer on OLED Display

Once your GPS module receives the enough signal and connects with enough number of satellites, the OLED Display will display the speed in kilometers per hour (KPH) or in Miles per hour (MPH) in analog as well as on digital format.

Arduino OLED Speedo Meter

In a stationary condition or when the device is not in motion the speed displayed on the OLED Display is Zero. The accuracy of the speed readings can be influenced by factors such as signal strength, satellite visibility, and environmental conditions.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button