Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 10:34 01 Aug 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : GPS Speedometer Pico style

Author Message
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 01:15pm 10 Nov 2021
Copy link to clipboard 
Print this post

I have to thank Tom thwill for his massive help with the code for this project
Also Geoff and Peter for the Pico port

I wanted to make a small GPS speedometer for the dash of my car because I'm sure most of you know the speedometers are not calibrated
They can be up to 10% in error of the speed, reading lower than what you're actually doing.

I chose the Picomite because of it's small size, I had one here, Simplicity and it could do everything I wanted
I used an 0.95" 96x64 SPI Full Colour SSD1331 OLED LCD Display Module but the second board I have coming from China will also be compatable with
the 0.96" ST7735S 80x160 SPI Colour IPS TFT LCD Display Module  
I'll update the code to work with the new display once the new board has arrived from China
For the GPS I chose the Matek Neo M9N-5883 GNSS + Compass Module from here https://www.unmannedtechshop.co.uk/product/matek-neo-m9n-5883-gnss-compass-module/
I chose that module because it's reliable with 10hz update speed and works with GPS, GALILEO, BEIDOU, and GLOSNASS satellites all at the same time
this gives it greater accuracy.

It was important to me that the unit be battery powered and need NO physical connection to the car and be small enough to sit
on the dash in front of the steering wheel and be inobrusive.

The unit is powered in my instance by a 6800mAh Lifepo4 battery pack, which has a Solar Charger attached and a 6v Solar panel
The solar panel charges the battery even with a fully cloud covered sky in the UK and as the unit has enough battery power
to power it for 90 hours with the cpu at full speed and the unit running continously then it should run forever (or many years)

The code below wakes the display once the speed is at or above 3mps/kph
I chose this value as sometimes when stopped the gps shows tiny speeds and I wanted to make certain the car was actually moving.

Once the speed is 0 a timer starts
If it has been 0 for greater than 1 minute then the display turns off, the cpu sleeps for 5 seconds, wakes for 200ms to check for
movement, it there is any it checks the speed, if it's greater than 3 then the display comes on and the unit works as normal.
If it's below 3 then the unit goes back to sleep to save battery power.

I could save more power by slowing the CPU down, but I haven't done this yet.

There was a lot of problems once the code was working where the display would not update in real time, it was really lagging
and on investigation it was missing GPS(Valid) signals from the Pico once it woke up from sleep.

The way this was fixed was the following
Increase the baud rate to the Pico to 460800 and use the GPS config tool on the PC to change the sentences being received.
I turned off every sentence other than the $GNRMC sentence which we actually need.

This gave it 100% reliability
but the update of the display was still not in real time
I then used the same tool and changed the GPS update rate from 1hz to 10hz
This cured all the problems, the display shows the slightest change in speed in real time and looks beautiful I think

The display shows the battery voltage on start up and if the voltage drops below 3.2V then the K and M indicators are
replaced by a B for battery.
Once it has a valid satellite lock there is a green dot on the top right corner of the display indicating a valid GPS lock
There is also an M and K indicator at the bottom right to indicate if the speed is MPH or KPH

I have a switch wired to the Pico this lets you select MPH or KPH
I used this because I sometimes drive to Ireland which uses KPH, if you only use one and not the other then the switch can be omitted

The new board that is coming doesn't have the SD card you can see in the pictures below because it isn't needed

I added a connector for a U1V10F5 battery to 5V buck convertor
An U1V11F5 will also fit if the enable pin is missed, the other pins are identical
They both boost the voltage to  5v output from any input from 0.5V

The board has 3.7 Lipo4 battery only written on it but I put that on the board before I decided to use the new power up module

This means the unit can be run on any battery from 0.5v up
as long as the voltage isn't over 5V



I think the program has enough comments on it to be self explanatory but if it doesn't just ask.

Again many thanks to Tom for his invaluable help without which this code would not be possible

Once i get the new display that is wider I'll update the code to show the changes and if I ever get the built in compass working
then I'll get that incorporated into the display as well


Has anyone in the UK got a 3D printer that they'd be willing to make a case for this?
I'll be willing to pay for it

Option Base 0
Option Default None
Option Explicit On

Cls

Const VERSION$ = "0.25"

' **** Constants controlling hardware configuration and diagnostics ***********

' For testing purposes the code can run on the Colour Maximite 2.
Const IS_CMM2% = Left$(Mm.Device$, 17) = "Colour Maximite 2"
If Not IS_CMM2% And Mm.Device$ <> "PicoMite" Then Error "Unknown device"

If IS_CMM2% Then
 Option Console Serial
 Const WIDTH% = 96
 Const HEIGHT% = 64
Else
 Const WIDTH% = Mm.HRes
 Const HEIGHT% = Mm.VRes
EndIf

' Set these to 1 to interface with real hardware or 0 to use the keyboard alternative:
'  [A]     - accelerate
'  [Z]     - decelerate
'  [<]     - decrease battery voltage
'  [>]     - increase battery voltage
'  [Space] - toggle MPH/KPH
'  [X]     - toggle GPS valid/invalid
Const REAL_GPS% = 1
Const REAL_BATTERY% = 1
Const REAL_MPH_TOGGLE% = 1

' 1 to display diagnostics on the OLED, 0 for normal operation.
Const DIAGNOSTICS_ON% = 0

' If this is > 0 then generate debug output on this interval in milliseconds.
Const DEBUG_INTERVAL% = 6000 ' 6 seconds.

' *****************************************************************************

' If the speed is < 3 (MPH or KPH depending on toggle) for this duration then
' we transition from the DRIVING state to the STOPPING state.
' 10 seconds when REAL_GPS% = 0, otherwise 1 minute
Const DELAY% = Choice(REAL_GPS%, 60, 10) * 1000

' These are the states that the device can be in:
Const STATE_STARTING%   = 1
Const STATE_DRIVING%    = 2
Const STATE_STOPPING%   = 3
Const STATE_STOPPED%    = 4
Const STATE_WAKING%     = 5
Const STATE_RESTARTING% = 6

Const BATTERY_MIN! = 3.2     ' If the battery voltage falls below this the low
                            ' battery warning will be shown.

' State variables:
Dim gps_valid%               ' 1 for valid, 0 for invalid.
Dim mph_flag%                ' 1 for MPH, 0 for KPH.
Dim speed!                   ' Current speed in MPH or KPH as above.
Dim battery!                 ' Battery voltage.
Dim state% = STATE_STARTING% ' Current state, one of the STATE_XXXX% constants.

' When using the keyboard rather than the real hardware these are the variables
' that the keyboard manipulates.
Dim test_gps_valid% = 1      ' Default to GPS signal valid.
Dim test_mph_flag%  = 1      ' Default to MPH.
Dim test_speed!     = 0      ' Note this is the speed in knots.
Dim test_battery!   = 5      ' Default to 5V.

If REAL_GPS% Then
 SetPin GP0, GP1, COM1
 Open "COM1:460800" As Gps  ' Sets GPS pins
EndIf

If REAL_BATTERY% Then SetPin GP26, AIN  ' This pin is always 1/2 battery voltage

If REAL_MPH_TOGGLE% Then SetPin GP14, DIN, PULLUP  ' MPH/KPH Select - 3.3V = MPH, 0V = KPH

' Enable keyboard interrupt unless we are using entirely real hardware.
If Not REAL_GPS% Or Not REAL_BATTERY% Or Not REAL_MPH_TOGGLE% Then
 On Key on_key
EndIf

' Generate debug output on the given interval.
If DEBUG_INTERVAL% > 0 Then
 Print "PicoMite GPS Speedo v" VERSION$
 print_debug_ex("POWER ON")
 SetTick DEBUG_INTERVAL%, print_debug
EndIf

Timer = 0

clear_display()

Do
 read_inputs()
 update_state()
 update_display()

 If state% = STATE_STOPPED% And DIAGNOSTICS_ON% = 0 Then
   cpu_sleep(5) ' Sleep CPU for 5 seconds to save battery.
 EndIf

 ' Lewis you might consider adding an ELSE clause to the above, might make
 ' display marginally smoother and means you aren't constantly polling the GPS:
 ' Else
 '   Pause 100
 ' EndIf
Loop

End

Sub clear_display()
 If IS_CMM2% Then
   ' On the CMM2 we draw a box to simulate the OLED size.
   Box 0, 0, WIDTH%, HEIGHT%, 1, Rgb(Gray), Rgb(Black)
 Else
   Cls
 EndIf
End Sub

Sub read_inputs()
 ' Check if the GPS has a valid lock:
 gps_valid% = Choice(REAL_GPS%, Gps(Valid), test_gps_valid%)

 ' Read the M/K select pin:
 mph_flag% = Choice(REAL_MPH_TOGGLE%, Pin(GP14), test_mph_flag%)

 ' Read speed and adjust according to M/K select pin
 ' - note this may not actually be correct if gps_valid% = 0.
 speed! = Choice(REAL_GPS%, Gps(Speed), test_speed!)
 speed! = speed! * Choice(mph_flag%, 1.15077945, 1.852)

 ' Read battery voltage:
 battery! = Choice(REAL_BATTERY%, 2 * Pin(GP26), test_battery!)
End Sub

' Decides what the unit is doing.
Sub update_state()
 Local old_state% = state%

 Select Case state%
   Case STATE_STARTING%
     If Timer > 1000 Then state% = STATE_DRIVING%

   Case STATE_DRIVING%
     If speed! < 3 Then
       Timer = 0
       state% = STATE_STOPPING%
     EndIf

   Case STATE_STOPPING%
     If speed! >= 3 Then
       state% = STATE_DRIVING%
     ElseIf Timer > DELAY% Then
       state% = STATE_STOPPED%
     EndIf

   Case STATE_STOPPED%
     If speed! >= 3 Then
       Timer = 0
       state% = STATE_RESTARTING%
     Else
       Timer = 0
       state% = STATE_WAKING%
     EndIf

   Case STATE_WAKING%
     If speed! >= 3 Then
       Timer = 0        
       state% = STATE_RESTARTING%
     Else If Timer > 200 Then
       state% = STATE_STOPPED%
     EndIf

   Case STATE_RESTARTING%
     If Timer > 2000 Then state% = STATE_DRIVING%

   Case Else
     Error "Unknown state"
     
 End Select

 ' Always log state changes.
 If state% <> old_state% Then print_debug()
End Sub

Sub update_display()
 If DIAGNOSTICS_ON% Then
   update_diagnostics_display()
   Exit Sub
 EndIf

 ' Static variables are like Dim'd (global) variables except they are local to the
 ' Sub or Function, they are only initialised the first time they are encountered.
 Static old_state% = -1
 Static old_dvalue$ = ""
 Static old_dfont% = -1
 Static old_dcolour% = -1
 Static old_dindicator% = -1
 Static old_dunits$= ""

 ' If we are still STOPPED/WAKING then don't update the display.
 If stopped_or_waking%(state%) And stopped_or_waking%(old_state%) Then Exit Sub

 Local dvalue$ = display_value$()
 Local dfont% = display_font%()
 Local dcolour% = display_colour%()
 Local dindicator% = display_indicator%()
 Local dunits$ = display_units$()

 ' If nothing has changed then don't update the display
 ' - a single line containing 5 AND statements is too long to work with, so do it this way.
 Local changed%
 Inc changed%, old_dvalue$ <> dvalue$
 Inc changed%, old_dfont% <> dfont%
 Inc changed%, old_dcolour% <> dcolour%
 Inc changed%, old_dindicator% <> dindicator%
 Inc changed%, old_dunits$ <> dunits$
 If Not changed% Then Exit Sub

 clear_display()

 If Not stopped_or_waking%(state%) Then
   Text WIDTH%/2, HEIGHT%/2, dvalue$, CM, dfont%, 1, dcolour%
   Circle 89, 5, 4, 1, 1, dindicator%, dindicator%
   Text 85, 62, dunits$, LB, 4, 1, Rgb(White)

   ' Uncomment to show version on STARTING/RESTARTING displays.
   If state% = STATE_STARTING% Or state% = STATE_RESTARTING% Then
     Text 2, 58, "v" + VERSION$, LB, 7
   EndIf
 EndIf

 old_state% = state%
 old_dvalue$ = dvalue$
 old_dfont% = dfont%
 old_dcolour% = dcolour%
 old_dindicator% = dindicator%
 old_dunits$= dunits$
End Sub

Function stopped_or_waking%(st%)
 stopped_or_waking% = (st% = STATE_STOPPED% Or st% = STATE_WAKING%)
End Function

Function display_indicator%()
 display_indicator% = Choice(gps_valid%, Rgb(Green), Rgb(Black))
End Function

Function display_value$()
 If state% = STATE_STARTING% Or state% = STATE_RESTARTING% Then
   display_value$ = Str$(battery!, 1, 1) + "V"
 ElseIf Not gps_valid% Then
   display_value$ = "--"
 Else
   display_value$ = Str$(speed!, 0, 0)
 EndIf
End Function

Function display_font%()
 display_font% = Choice(state% = STATE_STARTING% Or state% = STATE_RESTARTING%, 3, 6)
End Function

' Returns colour to be used for speed display depending on current speed/state.
Function display_colour%()
 If state% = STATE_STARTING% Or state% = STATE_RESTARTING% Then
   display_colour% = Rgb(White)
 ElseIf stopped_or_waking%(state%) Then
   ' We should never actually get to this point as we don't show the display
   ' at all under these conditions.
   display_colour% = Rgb(Black)
 ElseIf state% = STATE_STOPPING% Or Not gps_valid% Then
   display_colour% = Rgb(0, 128, 0)
 ElseIf speed! > Choice(mph_flag%, 71, 121) Then ' RED if Speed is 71Mph/121Kph or more
   display_colour% = Rgb(Red)
 ElseIf speed! > Choice(mph_flag%, 61, 101) Then ' ORANGE if Speed is 61Mph/101Kph or more
   display_colour% = Rgb(Orange)
 Else
   display_colour% = Rgb(Green)
 EndIf
End Function

Function display_units$()
 If battery! < BATTERY_MIN! Then
   display_units$ = "B" ' If battery < 3.2V then replace K/M with B.
 ElseIf speed! > 99 Then
   display_units$ = ""  ' If speed > 99 then don't show K/M indicator.
 ElseIf mph_flag% Then
   display_units$ = "M"
 Else
   display_units$ = "K"
 EndIf
End Function

Sub update_diagnostics_display()
 Static count% = 1000
 Inc count%
 If count% < 1000 Then Exit Sub

 clear_display()

 Text 2, 2,  "GPS valid: " + Str$(gps_valid%), , 7
 Text 2, 12, "MPH flag:  " + Str$(mph_flag%), , 7
 Text 2, 22, "Speed: "     + Str$(speed!, 0, 3), , 7
 Text 2, 32, "Battery: "   + Str$(battery!, 0, 3), , 7
 Text 2, 42, "State: "     + Left$(state_as_string$(), 8), , 7

 count% = 0
End Sub

Sub cpu_sleep(duration_in_seconds%)
 print_debug_ex("*SLEEPING*")
 If REAL_GPS% Then
   Cpu Sleep duration_in_seconds%
 Else
   Pause duration_in_seconds% * 1000
 EndIf
End Sub

Sub on_key()
 Select Case Inkey$
   Case "a", "A" : test_speed! = Min(100, test_speed! + 0.5)
   Case "z", "Z" : test_speed! = Max(0, test_speed! - 0.5)
   Case ".", ">" : test_battery! = Min(5, test_battery! + 0.2)
   Case ",", "<" : test_battery! = Max(0, test_battery! - 0.2)
   Case "x", "X" : test_gps_valid% = Not test_gps_valid%
   Case " " : test_mph_flag% = Not test_mph_flag%
 End Select
End Sub

' Note that because of how SETTICK interrupts are implemented in MMBasic it is
' possible if unlikely that this will occasionally be called partway through
' a call to read_inputs() resulting in inconsistent data, e.g. MPH flag set true,
' but Speed reported in KPH.
Sub print_debug()
 print_debug_ex(state_as_string$())
End Sub

Sub print_debug_ex(s$)
 If DEBUG_INTERVAL% <= 0 Then Exit Sub

 ' Note that without a clock installed Time$ = 00:00:00 will be when the unit started up.
 Print Time$ " - GPS valid:" gps_valid% ", MPH flag:" mph_flag%;
 Print ", Spd: " Str$(speed!, 0, 3) ", Bat: " Str$(battery!, 0, 3) ", State: " s$
End Sub

Function state_as_string$()
 Select Case state%
   Case STATE_STARTING%:   state_as_string$ = "STARTING"
   Case STATE_DRIVING%:    state_as_string$ = "DRIVING"
   Case STATE_STOPPING%:   state_as_string$ = "STOPPING"
   Case STATE_STOPPED%:    state_as_string$ = "STOPPED"
   Case STATE_WAKING%:     state_as_string$ = "WAKING"
   Case STATE_RESTARTING%: state_as_string$ = "RESTARTING"
   Case Else:              state_as_string$ = "ERROR"
 End Select
End Function


This is the circuit for the updated board with support for both displays
It does not have an SD card as it isn't needed for this project







This looks really messy as it's sat on my lap and I don't have any kind of case to put it in yet.


Power up display



After Voltage reading but waiting for a valid GPS lock



GPS lock with MPH



GPS lock with KPH



Battery pack



Solar charger



back of Solar charger


Edited 2021-11-10 23:26 by lew247
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3378
Posted: 01:50pm 10 Nov 2021
Copy link to clipboard 
Print this post

Terrific project. Congratulations. I'll have to look into EasyEDA.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
Plasmamac

Guru

Joined: 31/01/2019
Location: Germany
Posts: 579
Posted: 03:06pm 10 Nov 2021
Copy link to clipboard 
Print this post

If you have a stl for your case i will print one for you, no problem
Plasma
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 03:17pm 10 Nov 2021
Copy link to clipboard 
Print this post

The following Youtube videos show the unit powering up once the car is moving and the 2nd one acceleration

I forgot to say above that once the speed equals or gets above 61Mph/101Kph the display goes Orange
When it gets to 71Mph/121Kph or above it goes red

Startup

Acceleration

  Plasmamac said  If you have a stl for your case i will print one for you, no problem

Unfortunately I know nothing about 3D printing so I'd have to send the completed unit to whoever was going to make a case.
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 03:21pm 10 Nov 2021
Copy link to clipboard 
Print this post

  lew247 said  Unfortunately I know nothing about 3D printing so I'd have to send the completed unit to whoever was going to make a case.


As I've said privately I know nothing about 3D printing, however as an old model maker I'd start by building a mockup out of corrugated cardboard, balsa wood or plasticard.

EDIT: ... or cram it into the smallest project box you can and then as a display bezel have a fabricator make you a blank PCB with a cutout for the screen and another for the toggle switch - though I supposed their quality control might try try to throw it back at you as a WTF.

Best wishes,

Tom
Edited 2021-11-11 01:26 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 03:46pm 10 Nov 2021
Copy link to clipboard 
Print this post

  thwill said   I'd start by building a mockup out of corrugated cardboard, balsa wood or plasticard.
Tom
Her who must be obeyed would would have a fit, it's her car  
Edited 2021-11-11 01:46 by lew247
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 04:26pm 10 Nov 2021
Copy link to clipboard 
Print this post

Plasticard can make nice cases (railway modellers use it all the time), as can foamboard, which is cheap, easily cut with a scalpel and steel rule and can be glued with a hot glue gun. Either would be fine for designing the final case to be printed.

Nice project, lew. :)
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 06:14pm 10 Nov 2021
Copy link to clipboard 
Print this post

  Mixtel90 said  Plasticard can make nice cases (railway modellers use it all the time), as can foamboard, which is cheap, easily cut with a scalpel and steel rule and can be glued with a hot glue gun. Either would be fine for designing the final case to be printed.

Nice project, lew. :)

I'd never heard of the stuff before
I agree it does look really intersting
I'll get some and give it a try
Thanks
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 06:48pm 10 Nov 2021
Copy link to clipboard 
Print this post

I'd forgotten about foam board it's really nice and easy to work with scalpel and steel rule as Mick suggests. One warning, if you paint it (a nice black aerosol finish) just make sure you use several light coats with a water based acrylic and not something with an organic solvent unless you want to dissolve all your good work .

EDIT: I guess there are probably EU safety regulations about encasing LIPOs in enclosures made of cardboard and polystyrene - though I don't suppose we have to worry about them any more .

Best wishes,

Tom
Edited 2021-11-11 04:57 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7937
Posted: 07:26pm 10 Nov 2021
Copy link to clipboard 
Print this post

Would those be the regs that prevent us getting them through the post from ebay, Tom?  :)

Foam board:
Good point on the paint! Water-based acrylic works well, not too bad even when applied with a brush. Interestingly, if you stick bits together face to face it's very strong and you can drill the stack and put coarse self tapping screws into it! Handy for mounting PCBs in a case.
Edited 2021-11-11 05:26 by Mixtel90
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10310
Posted: 08:23pm 10 Nov 2021
Copy link to clipboard 
Print this post

Plasticard is great stuff. Glue it with EMA plastic weld
 
palcal

Guru

Joined: 12/10/2011
Location: Australia
Posts: 1993
Posted: 08:42pm 10 Nov 2021
Copy link to clipboard 
Print this post

I made a GPS speedo for my car, I just plug it into a USB charger in the cig. lighter.



Edited 2021-11-11 07:19 by palcal
"It is better to be ignorant and ask a stupid question than to be plain Stupid and not ask at all"
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5089
Posted: 07:45am 11 Nov 2021
Copy link to clipboard 
Print this post

  Quote   Her who must be obeyed would would have a fit, it's her car  


And her own speeding tickets.....
Edited 2021-11-11 17:46 by Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 12:55pm 30 Nov 2021
Copy link to clipboard 
Print this post

I may have to be circumspect when it comes to field tests - "Honest officer it's a speedometer, not a bomb" :


MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025