Screenshot 2024-02-03 at 3.32.29 PM.png

Lesburu Analytics

Lesburu Analytics (yes, the name is real) was a project born out of the question, how fast do I really drive my Subaru? And can I finally make a project that uses cellular on a Raspberry Pi?

With that excuse in hand, I embarked on a project to automatically (and reliably) track my drives in my Subaru, all without touching a thing, regardless of if my car was in an area with cell service or not. And to upload this data to my server in real-time.

But that wasn’t just it - I challenged myself to make a frontend that allowed me to see the location of my Subaru in real-time, summarize & analyze past drives, see what songs were playing during drives, and with all the data goodness you can possibly dig into.

After 4 months of heavy development (with lots of hardware/software iterations and fine-tuning), Lesburu Analytics turned out into the project seen here.

Languages Used: Python, CSS, JavaScript

Frameworks Used: Socket.IO for comms, Flask, MariaDB, SQLAlchemy, Alembic, Pydantic, gpsd, ModemManager, Selenium, React, Vite, MUI, Google Maps JS API, SQLite3, Spotipy & the Spotify Web API (phew, what a list!)

Hardware: Raspberry Pi 3B+, Sixfab LTE HAT, SIMCom SIM7600G, Globalsat BU353-N GPS receiver, Adafruit PiRTC DS3231

Timeframe: November 2023 - Present

Three notes:

  • Since this project continues to be in heavy development, some photos here might not reflect all the features in Lesburu Analytics perfectly. I’m hoping to redo all the screenshots sometime in May.

  • Since this project contains a lot of private data, the web UI is locked and isn’t publicly accessible (and will never be). Sorry about that, especially since most of my other projects are publicly accessible.

  • I am aware that this project’s name is typoed and should be Lesbaru Analytics. Womp womp…

 This is the hardware setup for Lesburu Analytics.  The base of this project is a Raspberry Pi 3B+ running the latest version of Raspberry Pi OS (Bullseye as of this project). On top of that is the Sixfab Cellular HAT, which is where the modem, anten

This is the hardware setup for Lesburu Analytics.

The base of this project is a Raspberry Pi 3B+ running the latest version of Raspberry Pi OS (Bullseye as of this project). On top of that is the Sixfab Cellular HAT, which is where the modem, antennas, and the SIM card sit. The SIM is a Sixfab Super SIM, in turn, a Twilio Super SIM that runs on T-Mobile and AT&T in the US. It also works in Canada, a real-life test undertaken for the 2024 Total Solar Eclipse!

The LTE modem used in this project is a SIMCom SIM7600G-H. Previously, I bought a Telit ME910C1 LTE-M modem (given the data throughput of Lesburu Analytics isn’t super massive), but found it had weak reception and horrible connectivity issues while in motion. Fortunately, one of my friends couldn’t use the SIMCom modem in his ThinkPad, and sold it to me! The joys of going to a tech school, where you can ask your friends if they have a spare modem lying around.

On top of all of this is the DS3231 Real-Time Clock. The Pi is the source of truth for timestamping GPS traces - and because Pis don’t natively have a RTC, I needed one for this project. This is required so that the Pi doesn’t have to wait for GPS or LTE reception to know what time it is, and power a feature to continue trips if the car comes back online within 15 minutes of being shut off.

At the top of the HAT - there’s also three LEDs visible. One of them is a user-programmable LED, and I use this to indicate different status codes of how the entire system is running (single blink for OK upload, double blink for no GPS, triple blink for couldn’t upload, etc).

There’s also a user-programmable button on the HAT, set so that if held down for ~0.2 seconds, the Pi gracefully shuts down.

 At the centerpiece of this project is the GPS receiver, and for this one, I chose the Globalsat BU-353N. It’s a USB-based, high-precision, GPS-only receiver that has worked quite nicely. On the Pi, I’m using gpsd to get data from the receiver, then

At the centerpiece of this project is the GPS receiver, and for this one, I chose the Globalsat BU-353N. It’s a USB-based, high-precision, GPS-only receiver that has worked quite nicely. On the Pi, I’m using gpsd to get data from the receiver, then the gpsd-py3 library to import this data into Python.

It sits on the dash (no longer taped though, it can hang out there without much issue since it’s got a rubber ring on the bottom to keep it on place) to get good GPS reception, and then the wire runs to the Pi in the center console.

Overall, this has been a pretty decent GPS receiver for not too much money. On the road, it’s got good enough accuracy and gets a GPS fix pretty quickly (usually 30 seconds from a cold start). However, this GPS does run into drift issues, especially in areas with tall buildings.

Since I was brand new to the whole GPS receiver game when starting this project, I didn’t realize there’s a model of this receiver (the BU-353N5/W11) that runs on multiple constellations (GPS, Galileo, for ~$15 or so more, which would’ve helped with drift issues & overall accuracy. Oh well!

The bill of materials came out to be:

  • $35 for the Pi 3B+ (when I bought it)

  • $45 for the Sixfab LTE HAT

  • $38 for the Telit ME910C1 + antenna for it

  • $32 for the BU-353N off of eBay

  • $29 for the SIMCom modem + antenna for it

So about $179 before shipping & tax costs. In reality it would be $141 if it weren’t for the ME910C1 being useless, but it happens.

Cellular costs have been coming out to about $8-$10/mo, which honestly isn’t too bad. Getting costs further down would mean some combination of the following:

  • Switching my web server to Nginx so HTTPS is not being used to carry the socket connection

  • Actually implementing my own socket with ASN.1 serialization

But this is just a prototype. The Pi actually tends to use equal parts upload/download because of the ACK messages from the server (the actual data is pretty small in comparison)

 On the homepage of Lesburu Analytics, the first thing you see is a live location of where the Subaru is. The architecture of Lesburu Analytics is to upload GPS traces as soon as possible, which also powers live tracking.  (Note: the map isn’t usuall

On the homepage of Lesburu Analytics, the first thing you see is a live location of where the Subaru is. The architecture of Lesburu Analytics is to upload GPS traces as soon as possible, which also powers live tracking.

(Note: the map isn’t usually this zoomed out, but I did this so you can’t precisely figure out where I live just from this screenshot.)

The window here is pretty simple - it shows the current GPS satellites, speed, elevation, and distance for this trip (also calculated by the Pi). There’s a button to follow/unfollow the Pi’s location, and which map type you’d like to use.

With React Google Maps, I found that the map type selector was flickering when updating the map center, so I just made my own component to do it instead.

Lesburu Analytics largely revolves around sending 5 types of messages from the Pi to the backend - no/partial/GPS message, trip starting, and trip continuing.

When the Pi first comes online, it determines if a trip needs to start or continue - and queues an appropriate packet. For trip starting, it’s the first message (ahead of any saved packets - more on this later). For trip continuing - it’s the last message behind any saved packets.

The no/partial GPS messages are largely used for real-time tracking. No GPS is sent when there’s no GPS reception, partial GPS is sent to indicate the client is alive, but hasn’t moved enough to send a GPS trace.

GPS message is the message type that gets saved to the database, in turn, these messages composing an entire trip.

Nominally, GPS traces are collected every 2 seconds - but with a few caveats. The Pi will consistently check if it’s moved 10 feet or more from the last GPS trace it uploaded, if that’s not the case, then a partial GPS message is sent. However, if the car hasn’t moved for 10 seconds, then a GPS message is sent with the speed set to 0 mph. There’s also an exception to send a GPS trace with the speed at 0 mph when the last trace had a speed > 0.5 mph, and currently the car is < 0.5 mph. Both of these ensure that stopped/moving times are as accurate as possible.

Data upload has a lot of systems to ensure every trace is uploaded. The Pi must receive an acknowledgement from the backend (within 1.25 seconds to avoid spotty cell connectivity causing issues) saying the data was received before it internally disposes of it.

If the Pi didn’t get an acknowledgement, it will then save any packets it couldn’t upload to disk (to an SQLite3 database on the device). I also coded it such that no GPS/partial GPS messages aren’t saved since that’s just for live tracking. Whenever it tries to upload data again, it pulls these packets from disk and requeues them.

Saving to disk is hugely important - namely, it means that if a trip ends or starts outside of cell connectivity, traces are not lost and get uploaded when cell service is regained. It also protects from issues with the backend being down and not being able to process messages correctly.

Reliable data upload was definitely the trickiest part of this entire project, but it works pretty well, minus any bugs that introduce errors into the whole thing (but that’s my fault, not the Pi)

 The next part of the homepage is the list of trips (probably will rename to Trip Library soon). This has gotten a huge makeover since Version 1.0.0 of Lesburu Analytics.  For each trip, the name and start &amp; end time are shown up top. The name is

The next part of the homepage is the list of trips (probably will rename to Trip Library soon). This has gotten a huge makeover since Version 1.0.0 of Lesburu Analytics.

For each trip, the name and start & end time are shown up top. The name is automatically generated and uses Mapbox’s reverse geocoding API to figure out where the trip started and ended.

Below that on the left, there’s a few “quick figures” about the trip - duration, distance, average/maximum speed, and estimated CO2 emitted.

On the right (and this is very cool) is a static image of the trip, with start/end markers!

There’s a script that runs every 10 minutes on my server to crunch trip data whenever it comes in - and part of that is capturing screenshots for each trip. I was able to do this using Selenium and a custom page in Lesburu Analytics that rendered the map at 350x350.

Each page shows 10 trips, and you can navigate through the pages at the top and bottom of this section.

 Another feature that’s been added to Lesburu Analytics recently is lifetime stats! As the subheader says, this is basically the tl;dr of all the trips logged by Lesburu Analytics.

Another feature that’s been added to Lesburu Analytics recently is lifetime stats! As the subheader says, this is basically the tl;dr of all the trips logged by Lesburu Analytics.

 Clicking on any trip in the library brings you to the trip page. For this example (and to protect my privacy), we’ll be using a trip I recorded on the Worcester/Framingham Line from Worcester to South Station.  To start off - there’s a small header

Clicking on any trip in the library brings you to the trip page. For this example (and to protect my privacy), we’ll be using a trip I recorded on the Worcester/Framingham Line from Worcester to South Station.

To start off - there’s a small header that again shows the trip name and the timing of the trip.

The second box is the map box, and this is arguably the coolest one of them all. Let’s dive into it.

 Inside the map box, you’ll see a lot of different things. Don’t mind the trip swap - it’s just so I can show off an extra awesome feature!  First off is the actual map itself. The speed of the drive at every point is on a red -&gt; yellow -&gt; gree

Inside the map box, you’ll see a lot of different things. Don’t mind the trip swap - it’s just so I can show off an extra awesome feature!

First off is the actual map itself. The speed of the drive at every point is on a red -> yellow -> green scale, with some weight applied so that it’s red -> yellow for ~65% of the speed, then yellow -> green for the remaining 35%. This makes it a bit easier to see speed variations on the highway.

Below that are the same 4 metrics on the homepage - but swapping out GPS satellites for a trip stopwatch. In a later revision, I sliced off the decimal points on the speedometer, so that’s what you’re seeing here.

Below that is the slider where you can in fact slide all the way through the trip! In a later revision not pictured here, I updated the project such that it uses two different markers when in motion and stopped, and the in-motion marker changes heading based on the GPS traces.

And below that is officially the coolest feature ever - Lesburu Analytics’ Spotify integration. More on this later, but for any drives after April 28, I can see what song I was listening to at any specific point in the drive. It is super cool when playing back a trip to see the different tracks played during segments of a drive!

Below that is 6 buttons - at each end is fast-forward to the start and end of the trip. The two inner buttons are going back and forward one step in the trip. The final inner-most button are to play/pause the trip animation. On the very left is the toggle to follow the current location puck on the map.

Below that is the date & time of where you are in the trip. Below that is playback speed controls - going anywhere from 0.5x to 3x. And finally, next to that is the map type controls.

This is a video demo of playback in the map screen with following the user turned on. It is arguably the coolest part of this entire project.

You’ll see in this demo how changing the playback speed works. I had to let the video run for the entire trip (since Squarespace can’t embed shorts), so I hope you enjoy how agonizingly slow the Worcester/Framingham Line is.

Just a note - this is a bit of an old demo so now whenever motion is detected, you’ll see the marker in the previous image that auto-adapts to the heading.

 Below the map screen are the trip statistics. Note that for the rest of the screenshots, I’ll be using a different trip I took.  The statistics screen shows data about the trip and there isn’t too much to talk about here. The screenshot will do most

Below the map screen are the trip statistics. Note that for the rest of the screenshots, I’ll be using a different trip I took.

The statistics screen shows data about the trip and there isn’t too much to talk about here. The screenshot will do most of the talking!

Time moving/stopped is determined by looking at the speed of a trace and determining how long it was until the next trace. In the eyes of Lesburu Analytics, any trace with a speed below 0.5 mph is considered stopped, otherwise, you’re moving.

CO2 emitted is calculated using MPG estimates for my specific car. The days for one tree to absorb metric is calculated by assuming a mature tree can absorb 0.06 kg of CO2 per day.

 One of the last major features I wanted to add to Lesburu Analytics was a log of songs listened to during a drive - and that’s exactly what this box does!  For any drive recorded on or after April 28, a Music Played box will show up between the Summ

One of the last major features I wanted to add to Lesburu Analytics was a log of songs listened to during a drive - and that’s exactly what this box does!

For any drive recorded on or after April 28, a Music Played box will show up between the Summary & Speed Zone Graph boxes. This contains the full listing of tracks listened to, including album covers, artist & album name, and the time the track was played at.

The Spotify integration required some heavy lifting to work - it turns out, the API is blocked on my web VPS (probably because I run on OVH). As such, I had to set up a separate microservice on my apartment’s network to run the data logic.

Additionally, Spotify only allows you to get your last 50 played songs, so this requires you to be very proactive with getting song lists while driving (especially for long drives). Because of that, the track listing for a drive is updated in 10-minute increments with the web VPS repeatedly calling the microservice to run its data processing.

And one last hurdle - it turns out that the time played that Spotify reports is actually when you finished listening to the song. This required some clever trickery on the map box to properly show the song you were listening to at any point on the trip (it uses the song duration in concert with when it finished playing to basically “guess” what song was on).

Overall, it’s a really cool feature and was genuinely the last big feature I wanted Lesburu Analytics to have. I’m super happy I got around to programming it!

 Below the Stats box is the Speed Zone graph. It’s meant to show you how long you spent driving in each “speed zone” (10 mph blocks of speed).  Hovering over any bar on this graph will show you exactly how long was spent in a specific zone, along wit

Below the Stats box is the Speed Zone graph. It’s meant to show you how long you spent driving in each “speed zone” (10 mph blocks of speed).

Hovering over any bar on this graph will show you exactly how long was spent in a specific zone, along with the percentage of the trip in that zone.

Usually longer highway drives have this unimodal distribution, whereas most city drives have a bimodal distribution (with stopped being the highest or second longest zone).

 Below the Speed Zone Graph is the data graph. It wouldn’t be an Owen McGinley project without graphs after all. This is an example graph from a recent 2.5 hour highway drive I took. You can see the tips in speed are largely traffic on the road, with

Below the Speed Zone Graph is the data graph. It wouldn’t be an Owen McGinley project without graphs after all. This is an example graph from a recent 2.5 hour highway drive I took. You can see the tips in speed are largely traffic on the road, with the start/end being on local roads.

There’s plenty of data points to be shown - speed, altitude, distance (over time), and then GPS horizontal/vertical accuracy and the number of satellites in view. Graphs are being rendered using chart.js.

 One cool thing is that I installed the chart.js plugin to pan &amp; zoom around the maps - especially useful for longer trips like this where you want to look at a specific cross-section of the trip.  This is just a random 6-minute sample of data fr

One cool thing is that I installed the chart.js plugin to pan & zoom around the maps - especially useful for longer trips like this where you want to look at a specific cross-section of the trip.

This is just a random 6-minute sample of data from a long highway drive. But it’s cool!

 One of the really cool things from Lesburu Analytics was realizing just how damn hilly US highways are! Aside from the start of the trip (my home home is on a mountain), you can see all the elevation changes during this drive.  This is the same trip

One of the really cool things from Lesburu Analytics was realizing just how damn hilly US highways are! Aside from the start of the trip (my home home is on a mountain), you can see all the elevation changes during this drive.

This is the same trip from the stats box example - and you can see how you can log about 7,300 feet of elevation gain (the backend is coded so that 10+ feet of elevation increase counts towards gain).

 Added in Lesburu Analytics v1.5.0 - connection state data, which comes below the graphs. Every 30 seconds, the Pi will log its connection state (including signal strength, operator, access tech &amp; connecton status) and send it to the server.  On

Added in Lesburu Analytics v1.5.0 - connection state data, which comes below the graphs. Every 30 seconds, the Pi will log its connection state (including signal strength, operator, access tech & connecton status) and send it to the server.

On each trip page after this feature was implemented, it will then show a nice table allowing me to see how the Pi maintained cellular connectivity through the trip.

Although - do not ask me why the Pi does this network switching every single time it comes on. I suspect it’s due to the SIM card I use being able to roam onto T-Mobile and AT&T all nilly willy, but I’ve never found a true cause and it does this almost 95% of the time on this modem.

On the bright side though, it did require me to develop solutions to offline data capture VERY early on in development to deal with this 1-2 minute gap of signal when the Pi turns on.

It’s also insane how fast phone modems take to connect. How do they do it in 10 seconds when this modem takes like a minute and a half??

This was a huge portion of Lesburu Analytics, realizing all the very complicated things we take for granted are indeed very complicated.

 The last part of the trip page is mostly for trip administration. I added functionality to rename a trip, and then there’s the button to delete a trip as well.

The last part of the trip page is mostly for trip administration. I added functionality to rename a trip, and then there’s the button to delete a trip as well.

 One last thing - this has to be one of the coolest drives I’ve recorded on Lesburu Analytics, an international trip coming back to my hotel after seeing the 2024 Total Solar Eclipse in Canada. (it only beats the drive from Lake Placid to Worcester t

One last thing - this has to be one of the coolest drives I’ve recorded on Lesburu Analytics, an international trip coming back to my hotel after seeing the 2024 Total Solar Eclipse in Canada. (it only beats the drive from Lake Placid to Worcester the day after, a 4 hour, 36 minute trip with 8,000 GPS traces that Lesburu Analytics logged perfectly!)

While I did have to reboot the Pi after crossing the border into America to switch networks (likely since the Canada IP lease isn’t compatible with the US IP lease & and ModemManager doesn’t tell NetworkManager to drop the IP lease on the wwan0 interface when switching networks)…it’s just incredibly cool to see this (mostly) work in a different country. Fear not, I have fixed this edge case…but I probably won’t have time to head back into Canada to see if it got fixed.

Lesburu Analytics is, by far, one of the best highlights of my development career. It’s the tightest integration of software & hardware to date and I’m incredibly proud of how many complex systems I was able to orchestrate to make it all work - especially now with the Spotify integration!

It’s also an absolutely perfect way to demonstrate what I’ve learned in the last 7 years of coding, and how I’ve continued to push the bar with my projects every year. It is no small feat of engineering to make Lesburu Analytics work as reliably as it does today so I’m super super proud of that.