Published on

Google Maps navigation on ESP32 with LVGL

Authors
  • avatar
    Name
    Mai H. Son (Mason)
    Twitter

Warning: This is a fun project that serve little purpose.

I often ride motobike in long distances and have always used my phone for navigation. During hot days, the phone gets hot from having the screen on for long periods of time, so I had the idea to make a dedicated screen just for navigation and attach it to my bike.

on-bike.webp

In fact, I don't have to do this, with the price of the components, I could have bought a cheap second-hand smartphone, install Google Maps on it and call it a day, it can even shows full maps instead of Turn-by-Turn navigation, but meh, where is the fun in that?

The idea

Okay, I have ESP32 with bluetooth, a screen, what can I do with it:

  1. Have an app on my phone that utilizes the Google Navigation SDK to create routes then send TBT messages to the ESP32 in realtime
  2. Or, just read the navigation data from Google Maps app itself somehow, then send it to the ESP32

For the first option, it is possible, but the Google Navigation SDK is not free, and creating a full UI for it that mimics the functionality I want from Google Maps such as multi-stop navigation, traffic, alternative routes, etc. is a lot of work.

For the second one, Google Maps app does not broadcast its navigation data, so I can't have it directly. BUT, it does display some briefing of the route on the notification bar, it is infact possible to read the notification bar on Android, I just need to parse the notification data to get the information I need. Sweet!

Android app

Getting the notification data

notification.webp

As you can see, the notification contains the route information, such as the next turn, distance to the next turn, and the total distance of the route, even the TBT icon. This is more than enough for me.

To get the notification data, I will use the NotificationListenerService class, which allows me to listen to notifications and get their data.

The thing is, this method can be used for any maps app, as long as it displays the route information in the notification bar.

You need to parse the notification data, which is a bit tricky, as the data is not structured in a way that is easy to read. The data is in a view form that can be used to render the notification, you need to log it to see what it contains. Different versions of Maps app may have different data structures, so you need to handle that in your code.

One thing to note, the icon is in RGB bitmap format, to reduce the size of the data sent to the ESP32, I have converted it to a monochrome bitmap in realtime, but some icons have gray shades, which can't be represented in monochrome, so I made a crude dithering algorithm to convert the icon to monochrome. It is not perfect, but it works well enough for the TBT icon.

dithering.webp

Getting GPS speed

Unfortunately, the notification data does not contain the current speed of the vehicle, so I will have to get it from the GPS. I can use the LocationListener class to get the current location and speed.

Other preferences

I also added a few preferences to the app, such as LCD theme toggle, brightness control and overspeed warning.

app.webp

The device

The first iteration

With the first iteration, I used a ST7565 LCD screen taken from old a homephone, which is large enough and easy to read under sunlight.

homephone-screen.webp

This version use ESP32-WROOM-32 with Bluetooth Classic which supports SPP (Serial Port Profile), so I used it to receive data in form of JSON from the app. It also use Adafruit_GFX library to render the text and icons on the screen, because why bother with LVGL when I can just use a simple library that lightweight and easy to use.

With this setup, I have enough space to intergrate OTA (Over The Air) update functionality, because I was intented to seal everything in a box and not open it again.

Sadly, it was water damaged after a few days of heavy rain, turns out the box I made was not sealed properly.

The second iteration

I changed the big LCD with this 0.96" OLED screen, which is really compact but it is too small to read while riding.

oled.webp

The third iteration

Then I found this cheap ESP32 board with built-in 1.47" LCD from Waveshare, with this one, anyone can replicate the project easily, it has built-in Bluetooth and a nice and sharp colorful LCD screen that is easy to read, with standard USB-C port for power and programming. No soldering required, just plug and play.

waveshare.webp

This board is a ESP32-C6 though, no Bluetooth Classic, which means serial data transfer is not possible, so I tweaked the app and firmware to use Bluetooth Low Energy (BLE) instead. The app now sends the data as BLE writes, it is slower than SPP, especially with the bitmap icon.

To fully utilize the LCD, I used LVGL library to render the UI, which has advantage over Adafruit_GFX is that it can render fonts with anti-aliasing, which makes the text look much better. It also has built-in auto-scroll functionality, which is useful for long text such as the route information.

With this setup, I have to give up OTA due to the limited of flash size.

Galary

dark-theme.webp light-theme.webp

If you are intested in the code, you can find it on GitHub