-   OpenGauge / MPGuino FE computer (
-   -   FalconFour's MPGuino mods (

FalconFour 10-03-2010 06:51 AM

FalconFour's MPGuino mods
I think they deserve their own thread, although I think the bigger ones (like the ALDL one I plan once I get comfortable with Arduino coding) probably deserve their own thread...

Here's what I've been up to tonight, after getting my Arduino back from a friend ;)

Current list of "gee, wouldn't that be nice to have?" features on my todo list:
- Tank remaining gallons calculated by (tank size - tank gallons) * tank MPG (completed)
- Editor for tank values in memory, for restoring from a power outage from values recorded prior to unplugging (e.g. to recover from the setup menu CPU% timing bug) (still planning that one, LCD manipulation is tough)
- Smartass message/indicator for ultra low instant MPG, high instant MPH delta (flooring it)
- Real-time clock based on cycles, computed from memory value of "current time", set using "tank" method above (implemented, but unable to set the time at the moment due to lack of editor, so it always starts at 12am)

And more posts to follow for each mod, and the associated code.

FalconFour 10-03-2010 07:02 AM

Tank MPG is the easiest. Right now, for testing, it starts with a fixed value of 17.9999999 MPG to test rounding functions (I wasn't quite sure how the code worked yet, but I'm getting the hang of it now), but that value is discarded as soon as tank MPG moves away from 0.00. It calculates against the configured (but currently unused) tank capacity setting.

It uses its own "screen" which I currently call "F4 edit" due to all the comments I made in the code indicating where I patched it ("// ** F4 edit: blah blah"). The screen has both RemMiles and Time (clock).

Here's the code:
Put this somewhere around the screen routines. I put mine after doDisplaySystemInfo.

void doDisplayRem(void) {
  unsigned long remMPG = tank.mpg(); // may need to change this below
  unsigned long remMiles = parms[tankSizeIdx]; // final value
  if (remMPG < 50) remMPG = 17999; // if unreasonably low, make more reasonable
  LCD::print("Rem miles:");
  remMiles -= tank.gallons(); // subtract used gallons from total tank gallons
  remMiles *= remMPG; // multiply available gallons by tank mpg
  remMiles /= 1000; // since we multiplied two *1000 vals, knock down a notch
  LCD::print("                ");

Add a new displayFuncName:

        displayFuncNames[x++] = PSTR("Rem tank miles ");
And add a new displayFuncs to the position where you added the displayFuncName (if at the top of the stack, add it to beginning of list; if at bottom, add to end):

And that should pretty much be that. You can also use the computation above to add it to any other screen you want (custom is nice), but real estate there is limited. So if you want to replace Current MPG on the Custom screen, you could do this:

void doDisplayCustom() {
  unsigned long remMPG = tank.mpg(); // may need to change this below
  unsigned long remMiles = parms[tankSizeIdx]; // final value
  if (remMPG < 50) remMPG = 17999; // if unreasonably low, make more reasonable
  remMiles -= tank.gallons(); // subtract used gallons from total tank gallons
  remMiles *= remMPG; // multiply available gallons by tank mpg
  remMiles /= 1000; // since we multiplied two *1000 vals, knock down a notch
        displayTripCombo("MG","LK", instantmpg(), " S"," S",  instantmph(), "GH","LH",
                        instantgph(), "RM",  remMiles);

(I think. I haven't tried that... and I removed the metric functionality from mine, so I had to add that back above too.)

As they say... enjoy! ;)

FalconFour 10-03-2010 07:12 AM

1 Attachment(s)
And a fun one, if at least to see how far the MPGuino's interrupt-generated clock differs from reality (if "1.024 milliseconds, we will call that a millisecond for our purposes" is any indicator ;))... a clock.

It's not complete. The "values editor" has to first be created here in order to set the time on the clock. But it does count time, and it seems to be working, so I'll provide that here as a starting point, and to get some feedback (particularly on if there's anything I can do to clean up all the mBuff parameters)...

First, a new function, char * clock():

char * clock() {
  unsigned int clockDelta = tank.loopCount / loopsPerSecond;
  // if over 24 hours, remove 1 day. will occur after running for more than 24 hours.
  while (clockDelta > 86400) clockDelta -= 86400;
  unsigned int clock = (clockBase + clockDelta);
  // if we then add the clock base and it's over 11:59pm, wrap around to midnight.
  // will occur once a day and will reset when the clockDelta check above catches it.
  if (clock > 86400) clock -= 86400;
  unsigned int clockSeconds = (clock % 60);
  // will still be seconds so divide later
  unsigned int clockMinutes = (clock - clockSeconds) % 3600;
  unsigned int clockHours = (clock - clockMinutes - clockSeconds) / 3600;
  // divide to get minutes from mod seconds
  clockMinutes /= 60;
  boolean clockAmPm = false;
  if (clockHours > 12) {
    clockHours -= 12;
    clockAmPm = true;
  if (clockHours == 0) clockHours = 12;
  return mBuff;

So, add that code above somewhere, I added it below instantgph() and above the Trip functions.

Then, a way to display it. Basically just call LCD::print(clock()) anywhere. I added a new menu for it. Take the construct above and replace the blank line print with:

  LCD::print("Time: ");

Then bam, you get a clock starting at 12am. Happiness!

Attached is a photo of my work thus far :)

FalconFour 10-03-2010 07:28 AM

Bleh, just found out that tank miles isn't working right either. For some reason it flips out once tank MPG != 0.00. I can't figure out how the math is done in this thing. For example, tank capacity is stored as an integer like "10500" by default, but when I throw "format()" at it, it comes out as 10.5, as it should. But values like VSS pulses/mile also come out from format() as "4.00". I can't figure out where the decimal is coming from... or going to. I created a small test function to cycle through every stored parameter during every update, and it gave me some fast-paced but useful insight, such as that I should remove the "/1000" on the tank capacity parameter. But now it explodes in my face after I try to use it.

Needless to say I'm a little confused as to how to handle variables in the MPGuino code. Any tips? :/

FalconFour 10-04-2010 09:20 AM

1 Attachment(s)
Updated both mods... tank remaining MPG now actually works (yay! d'oh), learned a little too late about the 64-bit, integer-based *1000 math that was used in MPGuino... so all the calculations were screwed up. All fixed now with sane math and a better understanding of how to follow MPGuino as it runs around the room...

Also updated clock to fix the bug I noticed as I was posting it. Still not functional; tomorrow I'll work on a menu that allows you to set the time. Already added a menu as in the photo. Will work on adding clock- and tank-setting menus tomorrow!

BTW, here's as small as I could get the menu code: (sorry, haven't gotten around to commenting it - but check out that pos-pointer statement! Hint: press L/R to go to the 4 corners.)

void setupGuino() { // setup menu
//LCD::print("                ");
  LCD::print("Params  |  Clock");
  LCD::print("Tank    |  Exit");
  byte p=3;
  byte keyLock=1;
  while (true) {
    if (p < 4)
      LCD::gotoXY((bitRead(p,0)*15), bitRead(p,1));
    if (keyLock == 0) {
      if (!(buttonState & lbuttonBit)) {// left
        if (p == 255) p = 3;
      else if (!(buttonState & rbuttonBit)) {// right
        if (p == 4) p = 0;
      else if (!(buttonState & mbuttonBit)) {// middle
        if (p == 0) { // params
        else if (p == 1) { // clock
          // tbd
        else if (p == 2) { // tank
          // tbd
        else if (p == 3) { // exit
      if (buttonState != buttonsUp) keyLock = 1;
    } else {
      keyLock = 0;
    buttonState = buttonsUp;

FalconFour 10-05-2010 02:24 AM

Next up... Duck.

Yes, all it is, is a duck. Just a duck.

Add to LCD prototype (begins at "// LCD prototype" near the top):

  void initDuck();
  void printDuck(byte pos);

Add "doDisplayDuck" to the list of "displayFuncs[]" containing all the other doDisplay's.
Add the line "displayFuncNames[x++] = PSTR("Duck.");" to the menu selections (big block of displayFuncNames[x++]).

Add the following function, I put it around all the other display functions (below doDisplaySystemInfo):

void doDisplayDuck(void) {
  while (tmp1[0] > 6) {
    tmp1[1] = 1;
  if (tmp1[1]) tmp1[0]--;
  else tmp1[0]++;
  if (tmp1[0] == 0) {
    tmp1[1] = 0;

Now, look for "displayFuncs[screen](); //call the appropriate display routine". Look for the comment "//left is rotate through screeens to the left", and the misnomer "//right is rotate through screeens to the left", and just below each of those, add the line "if (screen == displayFuncSize - 1) LCD::init();". At the bottom of each, under the "LCD::print" statement, add the line "if (screen == displayFuncSize - 1) LCD::initDuck();". This ensures that the LCD is in the right state when both coming-from and going-to the Duck mode.

Finally, two LCD functions need to be added - initDuck and printDuck.
In the LCD section near the middle, add:

void LCD::initDuck() {
  //creating the duck:
  LcdCommandWrite(0b00001100); // display control:
  LcdCommandWrite(0b00000110); // entry mode set: increment automatically, no display shift

  LcdCommandWrite(0b01001000); // set cgram
  static byte chars[] PROGMEM = {
    B00001,B10000,B00000,B00000, B00000,B11111,B11111,B00110,
    B00011,B11000,B00000,B00000, B00001,B11111,B11111,B11110,
    B00010,B11100,B00000,B00000, B00011,B11111,B11111,B11100,
    B01111,B11100,B00000,B00000, B00011,B11111,B11111,B11111,
    B11111,B11100,B00000,B00000, B00011,B11111,B11111,B11110,
    B00001,B11100,B00000,B00000, B00011,B11111,B11111,B11100,
    B00000,B11000,B00000,B00000, B00001,B11111,B11111,B11000,
    B00000,B11100,B01100,B00000, B00000,B11111,B11111,B10000};

  for(byte x=0;x<LcdNewChars;x++)
    for(byte y=0;y<LcdCharHeightPix;y++)

  LcdCommandWrite(0b00000001); // clear display, set cursor position to zero
  LcdCommandWrite(0b10000000); // set dram to zero

void LCD::printDuck(byte pos) {
  char duckchars[]={ 1,2,3,4,4,0,5,6,7,8,4,0 };
  byte x=0;
  for (x=0;x<pos;x++) {
    LCD::print(" ");
  for (x=0;x<pos;x++) {
    LCD::print(" ");

Then, upload, rechip, and voila... you have a duck screen.

dcb 10-05-2010 03:50 AM

LOL! I did some playing around too, here is conways game of life:

Cooluser23 11-10-2010 03:42 AM

Feature suggestions:

How about "tank miles remaining" before tank is empty
VSS pass through, (so I can change the VSS signal to the speedometer to read correct speed when changing tire sizes) So modify the input VSS, and output a "corrected" value to take into account changes
Allowance for a bigger/better screen to see more information at once

FalconFour 11-10-2010 03:59 AM

Miles remaining? I've got that now, and it's pretty much dead accurate, based on tank MPG, tank gallons used, and the configured tank size. Also got a working clock that's 100% completely and totally spot-on accurate 24 hours a day, 7 days a week... haven't reset the MPGuino in some 3... almost 4 weeks now, and it's still got the perfect time. Also made a huge number of modifications to the screens, to provide more intuitive flow of screens:
BIG MPG (instant)
BIG MPH (instant)
CPU usage (current/peak; free mem)
Remaining miles / clock

And the Instant/Current/Tank screens have been rearranged to provide more intuitive figures:
Gallons | Distance (on Instant screen, distance is replaced with current MPG for reference)

Also added a menu that allows me to edit different parameters instead of just jumping in and locking me into editing all system parameters with no way out... when I press L+R buttons, I'm presented with a menu of "Params, Clock, Tank, Exit" (image above). Params launches the usual all-parameter edit screen, but selecting "XX" backs out of all editing back to the main screen. Clock allows me to enter the clock time in 24-hour format. Tank lets me edit the tank gallons and tank miles, in case of an accidental reset or power cycle, using figures I recorded previously (say I need to take the MPGuino inside to reprogram or something). It takes those figures in value*1000, then converts them into VSS pulses and injector S/MS that the MPGuino works with internally, based on the configured values.

I think I've made waaaaay too many edits to this code now... lol.

But... what do you mean by VSS pass through? You mean, reproduce a VSS signal on a signal pin to drive the speedometer? Hm... I don't really have a use for that, so I dunno if I could really develop and test that... :/

And a bigger screen? Not sure about that one either... if I get my hands on a bigger LCD I might play around with it, but the 16x2 LCD format comes in a wide variety of physical dimensions... doesn't help to put more info on the screen at once, but I think the instant/current/trip screen setup is already an information overload as it is while driving! ;)

Cooluser23 11-10-2010 04:04 AM

FalconFour, Wow! Thanks for that crazy quick response. :)

All times are GMT -4. The time now is 10:28 AM.

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