This is my entry for the "Sensing the World" challenge. This project is completely open-source: both of the firmwares and the mobile app for viewing the logged data are available on GitHub (firmware and app).
This project is meant to be a tool to better analyze fuel consumption of your car based on the routes you take for moving and commuting. By combining data from a GPS receiver (connected to a standard 9600 baud simplex UART with NMEA protocol) and an OBDII interface (anything compatible with the ELM327 AT protocol over UART), saving it on an SD card and sending it to your smartphone, you can see on a map your car's fuel consumption for every route you take. Colored markers show the fuel volume used in an average of 100 km for each data point recorded. Alternate modes allow you to show on the map the vehicle speed and the engine's RPM count (which you can use for optimizing gear shifts).
Any car with an OBDII connector and an airflow sensor is supported (every car produced after 1996 should have them), but the performance may vary based on the protocol used
by the ECU. I have a 2001 VW Golf Mk. 4 Cabriolet and I can achieve a maximum rate of 6 OBD "scans" per second, which is more than I actually need with an 1Hz GPS rate. Regarding my commute (about 60 km every day from home to university and back), I discovered that the shortest and less crowded route isn't actually the one with the lowest fuel consumption, due to the many small towns in between. Taking a slightly longer route which has a more uniform average speed allows me to drop the fuel
consumption by about 5%, totaling, with my quite slow driving style, about 4.9 liters for 100 km (20.5 kilometers with a liter, 48 mpg).
The formula used to calculate the fuel consumption is the following:
At first, it might sound strange, but a higher speed leads to a higher efficiency.
Many fuel types are supported, you can add your own as long as you know the correct mass air-to-fuel ratio. The fuel consumption calculation is done by constantly measuring the intake manifold's mass air flow rate, dividing it by the stoichiometric ratio (so you know the instantaneous fuel consumption). Taking into account the vehicle's speed allows the application to calculate the instantaneous fuel efficiency for that location, which is then displayed with a marker into a map.
Let's start with the hardware.
In order to build this logger, you'll need the following items:
- Obviously, an Avnet Azure Sphere Starter Kit (with its USB cable),
- A MikroElektronika OBDII Click (buy here),
- A NMEA-compliant GPS module configured to work at 9600 baud (I used and u-blox NEO M8N),
- A microSD card reader module (possibily with no on-board regulator, see documentation),
- A DB9 to OBDII cable (I used this one from MikroElektronika),
- A microSD card,
- Some perf-board (for building the custom "click" module),
- Two diodes (I used two 1N4007 rectifiers),
- A 4.7kΩ resistor,
- Strip headers (2x 1x8 headers)
The detailed connections for building your custom click module are documented on the GitHub repository. You'll need to follow this table to correctly connect the GPS receiver and the microSD card reader to the strip header pins of the module, but remember that since you are working on the back side, everything is reversed (actually got it right only after the third error).
You'll need to connect the OBDII click to the first socket and the custom module to the second one. They can be swapped, but you'll need to edit some constants in the configuration files (for both the Cortex-A application and the Cortex-M firmware).
I'm working on a 3D printed case, but since it will only fit my specific build, I prefer to share better future ideas in the last section.
Here are some pictures of the custom module I made before adding two diodes and an external voltage converter (along with a picture of my desk):
The firmware actually consists of an application running on the Cortex-A core and a firmware for a bit-banged, read-only UART running on one of the two Cortex-M4 cores.
The application has six independent threads:
- Intercore communication for receiving GPS data from the M4 core,
- TCP socket management,
- RGB status LED,
- TCP command interpreter,
- SD card I/O,
- Parser for OBD messages.
All these threads are running along the main function, which only starts and stops them and manages the Wi-Fi LED. I used the "pthreads" library to achieve this and it actually works pretty well, with some minor slowdowns during file transfer. Communication between threads is mainly done by using the circular buffers also used for I/O (inbound TCP commands, AT messages from the OBD module, SD card I/O).
In the solution there are six libraries. Two of them are from ChaN's FatFs library, the library itself and the modified version of the generic SPI sample for communicating with SD cards (download here, open "generic" folder). All credit for these files goes to its creator. These two libraries allow me to use any FAT or exFAT formatted SD or microSD card for storing logs.
Then I have a small "memory-logging" library which I only enable during development in order to quickly detect memory leaks by creating custom malloc() and free() functions that also print allocated/freed memory on the output terminal.
There is a support library for the GPS receiver (which actually communicates with another core, not directly with the GPS) in which I integrated Kosma Moczek's minmea library (a minimal NMEA parser). The GPS thread's task is to check for new NMEA sentences, parse them, update the system's date and time if needed (which is then normalized by the app when there are some "jumps") and push the parsed data to the SD circular buffer, which will be emptied by the SD I/O thread.
I also created two utility libraries for easily creating circular buffers and another one for getting millis() and micros() functions similar to the ones used in Arduino. In a future revision I will move the custom timer functions to the standard timerfd functions.
Unfortunately, due to the nature of the board (Wi-Fi only, so no connection while driving) and due to the fact that I don't have a standard, non-prepaid credit card, I could not use Azure from the start (except for firmware updates, obviously). One of the things I'm designing right now is a custom board with the MT3620 SOM and an u-blox cellular modem. Using the LTE connection and the Azure servers, I'm planning to expand this project in a real-time tracker with security utilities (more on that later). I really hope to see support libraries for "external" internet modems soon.
Mobile app and user experience
I wrote a mobile app for visualizing a map with all the data recorded by the logger. The app is complete from the point of view of what this project should do (showing the better route in terms of fuel economy for commuting and moving), but I decided to take it to the next level and start to implement more features. Many of the "extra" parts are still incomplete (such as the dashboard, which shows dummy data since I'm experimenting with its design).
I chose Flutter as my reference framework because porting the app to other platforms (iOS, desktop, embedded, web) is very easy. Currently the application is only available for Android 9+ devices. Unfortunately, iOS apps can't be compiled on a Windows machine, but I'm looking into CI/CD solutions for this. Now, let's go straight to what the app is. Here there are some screenshots.
The setup process:
Here is the main menu with four tabs (the first two are most important ones):
As you can see, I used Flutter's Cupertino theme (yes, it looks very similar to iOS, but it's running on an Android device). The most interesting parts are the download section, on the "Home" page, which tells you the currently available space on the logger's memory card and shows you a download button in place of the yellow progress indicator when the device is connected and detects an SD card with new logs, and the three maps, with three different data types selected. The first of the three maps shows the fuel consumption (green is better), the second one the speed from the GPS sensor and the third one shows the engine's RPM count. On the following screen you can select the desired data type, the map type and whether to show your current position. The number of markers is fixed in order to keep the same performance over time.
The app is available as an internal beta on the Google Play Store. At this page, you can enter the program and try out the app without needing to compile it again. Due to a Play Store limitation, the link only works for the first 100 accounts, so if you want to download it but an error occurs, contact me and I will generate a new link and update the post. Please note that you'll need at least Android Pie to run this app.
Project history and issues
Now, the fun part. Yes, during the development of this project I encountered many issues, probably I will forget some, but I try to list every solution I used to overcome them. Who knows, maybe some of them will happen again to somebody in the future.
First of all, this is the first time I programmed a Linux device in C. Starting to use (and understand) POSIX functions and file descriptors was hard, but I am now grateful to have learnt how useful and simple they actually are. Initially I did not find a simple solution for communicating over the network, because I only looked at the official documentation. But since it is a Linux system my first thought was that the underlying features, such as TCP sockets, were still there even if it is a "custom" OS. I have to admit that I spent more than a week to get a reliable TCP management. Initially the application would crash when the socket closed unexpectedly, and I had to rely on the OS restarting it, but most of the times the socket would not open again until I reset the board. By correctly managing the TCP thread (gracefully closing the socket on termination) and the system signals, I got a quite stable (and fast) communication system.
What wasn't fast was the microSD card. By playing with my oscilloscope and logic analyzer I discovered (and then researched on the Internet) why not all the cards would work with mt Azure Sphere Starter Kit. In SPI mode, the MOSI line of the card is used in an unconventional way. The Azure Sphere APIs don't allow for simultaneous I/O, you can only use SPI in half-duplex mode, but many SD cards need the pin to remain high during certain operations. Since there are no functions to independently control the MOSI pin, I decided to disable the SPI ISU and use the pins as simple GPIOs, in bit-banged mode.
After this, I started to work on the logging. The speed of about 1KB/s was enough (the clock line was working at around 50 kHz), but when I started to experiment and transfer some test files to the phone, I realized that this was ridiculously slow. I decide to try again with the native SPI. I decided to step back because even at 20 MHz, on the perf-board, the clock line's signal was still good (not a square wave anymore, but a full swing nonetheless). If I could use the full 20 MHz the speed would have dramatically improved. I decided to use another pin, along the Starter Kit's MOSI pin, in order to get it driven as I wanted. Specifically, I created an OR gate with two diodes and a resistor in order to be able to pull up the line by using another pin and completely ignore the "original" MOSI signal. I connected the cathodes of the two diodes to the MOSI input of the microSD card reader, which is pulled down by a 4.7kΩ resistor, and their anodes to the two pins I use to control them (true MOSI and "auxiliary" MOSI). Then I edited the code in a way that the MOSI line was pulled up during each reading session. Every card worked at a much higher speed. I didn't measure the exact speed, but I estimate that it can reach about 100 kB/s in both directions. The download from the phone is still quite slow, but raising the SPI clock speed over 20 MHz might cause reliability problems.
As many other people, I ran into the U5 issue. I was very lucky to have a small DC-DC converter (which is now connected to the power lines of my custom module and taped with thick rubber tape under it).
Another problem I encountered was related to the car. My car's 12V sockets are always on and since some times I left the device connected in the car for about 30-40 minutes while I was doing other tests, the logger generated lots of useless data. I first applied a firmware-side filter that would stop all logging activities when the car's engine was off, but then I realized that if the OBD cable would disconnect while traveling (which is quite easy given the fact that in my car the OBD socket is right in front of the gear shift and I always touch it when using the fifth gear), I would lose even the speed and the location data during that period. So I commented out that filter and instead applied a patch to the mobile application: before displaying the map, all the data recorded with a speed lower than 2 km/h is filtered out of the marker calculation, increasing the performance and hiding useless data.
The last notable issue regards the lack of free ISUs: I needed one more UART for reading NMEA sentences from the GPS module. I couldn't use a bit-banged UART in a separate thread, because even at low baudrates such as 4800 or 9600, most of the data was lost while other threads were running. Here I took advantage of the separate M4 cores. Although not documented, I managed to calculate the correct address for the GPIO to which the GPS output was connected and, being free from other tasks, I have successfully implemented a very simple but reliable simplex, read-only, bitbanged UART. I used a pulse output for debugging the sync with incoming data. The sync is done by starting a hardware timer which ticks at 9600 Hz when a falling edge (beginning of the transmission) is detected. When eight bits are correctly read, the resultant byte is pushed to the intercore buffer. Then, the next time that the Cortex-A CPU executes the GPS thread, it gets emptied and parsed, and if the NMEA sentence is complete, it gets logged to the SD card. Unfortunately I wasn't able to get speeds higher than 9600 baud, but for a constant 1 Hz GPS refresh rate, this is more than enough.
The last issue I want to talk about is on the mobile app side. Even before thinking about the TCP sockets I had to decide how to connect the logger to the "outer world". The most obvious thing is Wi-Fi because it is already integrated on the board and ready to be used with the default libraries. This carries some problems because of the nature of this project: you usually don't have a WLAN connection while you are driving around, so you can only sync your data while you are home. Sure, I could have added other connection modules such as a Bluetooth or LTE modem, but being really short on ports, I stuck with what I had. Instead I tried to connect the device to my mobile hotspot. I did not expect it to work due to some past experience with other mobile operating systems, but it actually worked. It is not a very elegant solution because if (like me) you can't use your mobile hotspot as an access point, you'll need to drive around with your cellular data disabled only to be connected to the logger, but it's better than nothing. Even better, I thought that I could use the mobile hotspot as a way to configure the device for the first time. Imagine that during the manufacturing process the device is set up to connect to a pre-defined setup network (I know it is against the guidelines, but this is a prototype), this will help everyone that doesn't have the Azure Sphere Command Prompt installed on their PC to easily set up some additional networks on the logger from their smartphone.
Current development and future work
I started this project in July and since the end of September I didn't work a lot on it because I started my first year at the university. I'm not writing code for many hours a day like I did this summer, but I'm designing (mainly on paper and ok KiCAD) a prototype for a custom board. My plan is to use an Azure Sphere SOM (MT3620 presumably) and mount it to a more solid board without needing to use external and removable modules. I also plan to start integrating an LTE module for real-time tracking (yes, now I have a school Microsoft Account on which I can use Azure). Unfortunately the Mikroelektronika OBDII Click doesn't allow to get power from the OBD connector, but the new board will take power directly from it, without the need of an USB charger. This gives an additional benefit: I can use a power management system in order to dramatically cut the power consumption while the engine is off and automatically wake the Azure Sphere SOM when the car's instrument panel is turned on.
The last thing I implemented are the status LEDs. The onboard RGB led is very useful. By combining color and a solid/breathing light animation, even without a display, the board can notify you if your car's battery is low, the SD is full or not detected, or if the OBD module can't connect to the ECU. The WLAN LED lights up when a network is connected and it flashes if it can't connect. The APP LED blinks every time a message is sent to the mobile app on your smartphone.
Right now I'm working on a less useful but interesting feature: the dashboard. The third section of the application's main menu will receive real-time data from the ECU and show it in a dashboard-like page. The GUI is complete and I'm currently implementing and testing this feature in an internal firmware branch.
During the summer I realized how much data this logger could extract from the car. Aside from VIN and DTCs (diagnostic trouble codes), which are not implemented in the protocol of many old ECUs, and only with the data I already get from my (quite old) car, I had some ideas. I list them here (who knows, maybe someone is interested in contributing):
- Gear detection and optimization,
- Gear/RPM/fuel efficiency charts (for deciding what gear to use at a chosen speed),
- Engine temperature charts (you could detect thermal problems if you notice that your car takes too much time to get warm),
- DTC notifications on the phone,
- Tachometer precision (compared against absolute GPS speed),
- From the previous, maybe difficult, but you could detect when the your tires slip (you'll need a higher refresh rate for both GPS and OBD messages),
- Average and maximum acceleration values,
- Features related to the on-board inertial unit (inertial navigation in tunnels, maybe),
- Remote security features, some cars with exposed CAN-bus on the OBD connector even allow door unlocking (using Azure and LTE connection), and many more...
Many new cars can also communicate the current fuel trim settings, that could be useful for home mechanics if plotted on a chart related to other parameters such as intake manifold pressure and RPM, but this is a thing that I can't test at home.
I hope you enjoyed my description. Remember that if you are interested, you can download, edit and contribute to the project. You can also design your own mobile app: the log format and the TCP message protocol are thorougly described in two tables in the repository's documentation.
I connected to the server for 39 days (I think 17 in a row).
Thank you for organizing this contest, with this board I better understood many of the concepts of the C language (programming exams seem easier after this experience).
Good luck to everybody,
(12/7/2019 EDIT: Play Store link updated. App will be downloadable in a few hours.)