-   OpenGauge / MPGuino FE computer (
-   -   I have built an in-dash head unit graphical mpguino display (

skybolt 10-12-2016 03:29 PM

I have built an in-dash head unit graphical mpguino display
I started by modifying a meelis mpguino, since thatís what Iíd purchased. I added a tx out cable and soldered pins to the ISP header so I could deploy new sketches.

Iím less interested in hypermiling (this particular vehicle just isnít capable), and I needed to change the code, and meelis was kind enough to give me his source code. Since then, Iíve modified it heavily, fixed his drag race subroutine Ö. But Ö and a big but Ė I didnít want to have the traditional mpguino on my dashboard.

Currently I have an alpine iLX-007, which has a video in port. Itís the video in port thatís important.

I changed the mpguino to output a json file, with the parameters Iím interested in, and some formatting.


{"title":"fuel used 2.00/2.61",
"subtitle":"10.99 remaining, 2.20 e-reserve",
"ranges":[3.5, 12.30, 13.80],
"measures":[ 10.99, 12.29],
"markers":[  1.50]},

{"title":"tank distance 46",
"subtitle":"range 175mi, 35 mi e-reserve, 203mi safe range, 242mi dry range",
"ranges":[100,  264,  297],
"measures":[    159,    126],
"markers":[  32.23]},

{"title":"fuel economy",
"subtitle":"0.00 trip, 17.51 tank, .35 ga@idle, 0.05 mi engine-off cruise",
"measures":[    0,    17.51],
"markers":[ 21]}]

From there, I went to and located one of the sample templates, this one:
(link prohibited by eccomodder) mbostock 4061961

I then found a version of the same code that would auto-update the data if the json file changed. Since my Arduino is putting out 4 json files a second (or 10, or 100 if itís drag-race mode), I needed to have something trigger once there was new data.

Unfortunately, the d3.js chart was designed ONLY to uypdate chart elements, so for some time my data titles and subtitles were static. But now Iíve hacked the code to refresh the titles and subtitles as part of the chart update routine.

In a nutshell, I now have all my mpguino data rendering to a video interface in the stereo head unit. (See attached jpg). (jpg removed by eccomodder, *sigh*)

My raspi code can be found here:
(link disabled by ecomodder) www github com skybolt autoUpdate

The easiest way to grab it from a mac or unix box is:

git clone www github com skybolt autoUpdate
It comes with a couple of scripts. One script is a python scrip that simulates live data, the other is a script that must be run if you want to have it interface with your arduino. it's called serial_update or something.

Things I have added:
1. BINGO fuel, or the point at which you need to get gas, by adding a reserve amount. My tank is 13.8, but anything below 2.2 gallons is risking running out. So, I added a setting somewhere in the code for that reserve level. This lets me calculate distance to bingo as well as distance to empty. If I get 20 mpg in a 13.8 tank, thatís a total range of 276. But, since I want to get gas while I can, my bingo range is 220. So, I show
a. Distance to dry (empty)
b. Distance to bingo (includes reserve)
c. Bingo range (eg, given your current mpg, whatís your unsafe range? 2.2 * 20 is 44, so my bingo range is 44. Thatís 44 scary miles. If you want to dip into the danger zone, the interface will let you see that risk.
d. E-reserve level. This is static (for me 2.2) until you dip below it, in which case it counts down to completely dry.
e. Resets trip every time the engine shuts off.
f. Shows your total tank distance, plus a white line that counts down from your maximum range. When it hits the orange line, you should go get gas.
g. Drag race. Meelis had a function but the timing was strange due to how many loops there are a second. I remove the wait state during drag_wait (waiting to start) and drag_active so you get the most accurate timing.
h. A ďserial onlyĒ screen. Apparently the LCD takes a little over 100ms to paint. This means a max of 10 updates a second. If you NEVER show the screen (by defaulting to ďserial onlyĒ and never choosing a new screen, the runtime is more like 10ms, or 100 times per second. This starts at boot and if you EVER show an LCD screen other than the serial only, youíre back to 100 or more ms. I have not tested if the ďtank saved, sleep activeĒ message triggers the 100ms paint event, but I think it does.

Gotchas, bugs, etc.

1. Many of my added functions are not written to support metric. Some may be so bad as to simply be bypassed if metric is chosen, as my new routine or call to a new function might simply be embedded in the SAE portion of the function.
2. My code is awful. Iím a terrible developer. In addition to sphaghetti code of the worst kind, I know nothing about memory management. Meelisí sketch was already pretty good sized, but now with my mods itís very close to 32k. Iíve managed to shrink it enough to run on an Arduino Micro Pro, which has a strange 31.x k cutoff.
3. I added two new parameters, acceleration speed test and acceleration distance test. I added them at the BEGINNING since I wanted to be able to change them. You may wish to do your 0-60 foot times, well, you can. BUT this means if you flash this sketch to your Arduino, all your parameters will be in the wrong location, by two steps. You could also change the code for those parameters to move them to the end.
4. This only compiles under Arduino 1.0.6 or earlier. Sorry! I want to start over with Taviís excellently small code, but itís different that Meelis so I have to learn how itís built all over again. I may simply add my functions to Taviís code and see if that works. If so Iíll probably break it out into multiple files, because I like that. Whatís your opinion, do you like projects with multiple files or one giant 8000 line code block?

skybolt 10-12-2016 03:33 PM

Here is a link to the screen demo.

oldtamiyaphile 10-12-2016 08:10 PM

I would love to see a histogram in five minute intervals like the Prius G2 has. :)

t vago 10-13-2016 10:16 AM

I am utterly fascinated with what you did with the MPGuino code. This is some nice work!

As you can't yet post pictures, I'll post this one for you ->
(you are allowed to post pictures once you reach 5 posts)

I am more used to working with 8000-line blocks of code, they're easier to work with than trying to hop from file to file to file.

If you want, I can look at your code and suggest ways to make it leaner.

Ardent 10-13-2016 09:02 PM

Cool, thanks for posting!

Originally Posted by t vago (Post 524635)
(you are allowed to post pictures once you reach 5 posts)

(I think that was a hint.)

skybolt 10-14-2016 02:25 PM

This source code is not for the faint of heart ....

Originally Posted by t vago (Post 524635)

I am more used to working with 8000-line blocks of code, they're easier to work with than trying to hop from file to file to file.

If you want, I can look at your code and suggest ways to make it leaner.

I don't recommend it -- but here you go. It's based on code by Meelis and I don't believe he wanted it widely distributed, so I won't say anything if you don't. :thumbup:

Right now I'm pulling apart Tavi's code to see if i can retrofit my own mods to it. His starts at a compiled 19k, but ouch, it's way different than what I'm working with now. My first effort (add my own code blocks to his) didn't work, so now I'm starting over, leaving it all in one file, and see if I can understand what in the hell his code is doing. First off is the serial out, ... if I can't figure out how he's setting it to 19,200, then I'm going to have lots of trouble further on. All my stuff is set to 57600 (it was 115200 but I changed to 57 as a test and left it there).

Once I can understand his code, it should be easy to add my functions to his. If you take a look at the serial_send file in my source code, you will see all the new functions I'm calling, most of which are in the mpguinio.ino file, down near the drag race routines.

skybolt 10-14-2016 02:30 PM

More fun with other people's code

Originally Posted by skybolt (Post 524765)
if I can't figure out how he's setting it to 19,200, then I'm going to have lots of trouble further on.

No code with string 192, no easy math that maps to 19200 given 20/16MHz change, no identifiable myubbr string. **sigh**

There is a nice line

const unsigned int myubbr = (unsigned int)(processorSpeed * 625ul / 96ul - 1);
, but altering the 96 to any other multiple I can think of hasn't worked yet. I'm busy doing other things, so I haven't divided 625/96 or 96/625 but I will say then when it was compiled for 20MHz on a 16MHz microcontroller, data read properly at 19200 (or was it 9600?). Point is, the 16/20MHz change and relationship to 625 merits a quick run through a calculator. 625 is .625 which is 5/8 I believe, but 96 to 192 is a 1/2.

skybolt 10-14-2016 02:31 PM

Once I get up to 5 posts I'll show it in the actual car's head unit, rather than on my raspi dev mule.

skybolt 10-14-2016 02:32 PM

Now that I go back and actually read the thread ...
Oh! I see you're T-vago! Let's work together on this.

skybolt 10-14-2016 02:59 PM

How does your format() work and where is byte?
First off, I notice you're using a different format utility, it looks like it combines both intformat and format, and takes a single parameter, int., which looks like it could be a decimal place parameter.

I tried pasting my old format routine, but it uses a c primitive, 'byte' which is either not represented in c++ or has been replaced by superior functions. Rather than fix my the old intformat routine by re-creating byte, I'd rather learn to use yours.

Problem is, as a functionally illiterate developer, I have to run through a number of steps to figure out how yours works. From


char * format(unsigned long num, uint8_t ndp)
it looks like in addition to the long (uh oh, that's going to be a problem for calculating negative safe fuel remaining, but that's easy enough to fix), it takes and unsigned short, essentially being a byte. But, there's the ndp ..... [error, unrecognized], so I'd step through passing it a number of values and parameters until it did what I expected.

Also, since format takes a long, what do you do to format strings? I'll admit I haven't yet looked for strings, I'm just working on hijacking your serial out string to send it as json.

At one point I experimented with three functions jsonBegin(), jsonPair() and jsonEnd(), so that I could simply call the start, randomly pass keys at any point in the code, then finish. It ended up being easier to create the serialSend monstrosity where it's all hard coded. I despise hard coding more than anyone, but to get Frankenstein standing I had to abandon some of my principles.

Old format function:

char* intformat(unsigned long num, byte numlength=6)
  static char fBuff[7];  //used by format

  //if number doesn't fit, show 9999 (92 bytes)
  if(numlength==6 && num > 999999999){
    num = 999999000ul;
  else if(numlength==5 && num > 99999999){
    num = 99999000ul;
  else if(numlength==4 && num > 9999999){
    num = 9999000ul;
  num /= 100;
  // Round off the non-printed value.
  if((num % 10) > 4) num += 10;
  num /= 10;
  byte x = numlength;
  fBuff[x] = 0;//end string with \0
  while(x > 0){
    fBuff[x]= '0' + (num % 10);//poke the ascii character for the digit.
    num /= 10;
  //replace leading 0 with ' '
  boolean leadnulls = true;
  while(x < numlength-1 && leadnulls){
    fBuff[x]=='0' ? fBuff[x]=' ' : leadnulls = false;
  return fBuff; 

All times are GMT -4. The time now is 12:47 AM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.
Content Relevant URLs by vBSEO 3.5.2
All content copyright