Code hacks
Contents
Overview
This is intended as a collaborative place for publishing code patches for various projects, or possibly a complete project if it is small (though separate wiki page might do too). Try to say which version of the code the patch is based on if available.
Why is this needed? Well a main project might maintain a certain level of testing and want to be able to say with confidence that a specific release works and has not broken anything since the previous release, and may not have the manpower to incorporate a lot of options and test all permutations. But those concerns are counter to the "hybrid vigor" that occurs when people can work on one aspect of a program. So there needs to be some place where anyone can put an example concept together for perhaps a customized piece of hardware or situation, or an interesting feature the project keepers cannot incorporate into the main repository in a timely manner.
Intended Audience, this is really for hackers, and folks who already posses similar skill sets. It is not meant as a learning tool though it may lead to unintended learning. This is not a place for discussions though, use the discussion tab or the forum.
Red & Green leds to determine if you are doing better or worse than your average
This is an idea that was posted by ac7ss in the workspace thread and then a thread was created for it, but no further action was taken it seems.
Connect a Green LED to pin 10 and a Red one to pin 11 with proper resistors.
The logic of the code could be improved some though ie...
- Green on / Red off - Current MPG greater then Tank Average
- Green on / Red on - Current MPG equals Tank Average
- Green off / Red on - Current MPG less then Tank Average
// in header section #define GreenLed 10 #define RedLed 11 ... // in setup() pinMode(GreenLed,OUTPUT); pinMode(RedLed,OUTPUT); ... // After sei() call in loop() if(lastActivity != nil){ if(instantmpg()>tank.mpg()){ //turn on GreenLed digitalWrite( GreenLed, HIGH); }else{ //turn off GreenLed digitalWrite( GreenLed, LOW); } if(instantmpg()>current.mpg()){ //turn off RedLed digitalWrite( RedLed, LOW); }else{ //turn on RedLed digitalWrite( RedLed, HIGH); } }else{ //turn off both lights if we are in sleep mode. digitalWrite( RedLed, LOW); digitalWrite( GreenLed, LOW); }
VFD Display for mpguino
Occasional extreme low temperatures and general interest in heads up display have led to some discussion about using a Vacuum Fluorescent Display (vfd) [1]. Newhaven Display part number M0216SD-162SDAR2-1 (~$35) is a close analogue of the current LCD display, so much so that it displays without doing anything but wiring it up to the mpguino. However a slight code tweak is required to reclaim control over brightness and display on/off as with this unit those functions are commands as the LED backlight pins are absent.
To regain brightness control and have the mpguino turn off/on the display automatically: based on version 0.75 of the mpguino code in google repository. In order of appearance:
change: analogWrite(BrightnessPin,brightness[brightnessIdx]); to: LCD::LcdCommandWrite(B00101000+brightnessIdx); change: analogWrite(BrightnessPin,brightness[0]); //nitey night to: LCD::LcdCommandWrite(B00001000); change: analogWrite(BrightnessPin,brightness[brightnessIdx]); //wake up!!! to: LCD::LcdCommandWrite(B00001100); change (2nd instance): analogWrite(BrightnessPin,brightness[brightnessIdx]); to: LCD::LcdCommandWrite(B00101000+brightnessIdx);
Follow up note: Testing has implied that the VFD appears to be impractical, as it draws a lot of current (overheats existing guino power supply) and is not visible in direct sunlight as one might encounter frequently in a moving vehicle, which also means it is not useable for a HUD. Add in the additional costs and it is hard to justify going this route for a couple days out of the year of waiting for the LCD to thaw.
--Dcb 07:39, 24 April 2009 (EDT)
Enhanced Large Font
I've made a lot of changes and cleanups to 0.75, but in general this is a simple hack. Had I not made so many changes I could just post a patch here but my patch would be pretty large.
Start by adding this line near the top of the mpguino.pde file:
#define CFG_BIGFONT_TYPE 2 // 1=Default 2=Modified
By setting this to '1' before you compile, you'll get the current font. Set it to '2' and then recompile, and you get the new, 'rounder' big font. The big font adds about 20 bytes to the compiled code size.
Find the declaration for bignumchars1 and bignumchars2. Delete these lines. Now add the following code near the top of the file, probably right underneath the CFG_BIGFONT_TYPE line you see above.
#if (CFG_BIGFONT_TYPE == 1) //32 = 0x20 = space const unsigned char LcdNewChars = 5; const unsigned char LcdCharHeightPix = 8; char bignumchars1[]={4,1,4,0, 1,4,32,0, 3,3,4,0, 1,3,4,0, 4,2,4,0, 4,3,3,0, 4,3,3,0, 1,1,4,0, 4,3,4,0, 4,3,4,0}; char bignumchars2[]={4,2,4,0, 2,4,2,0, 4,2,2,0, 2,2,4,0, 32,32,4,0, 2,2,4,0, 4,2,4,0, 32,4,32,0, 4,2,4,0, 2,2,4,0}; #elif (CFG_BIGFONT_TYPE == 2) //255 = 0xFF = all black character const unsigned char LcdNewChars = 8; const unsigned char LcdCharHeightPix = 8; char bignumchars1[]={7,1,8,0, 1,255,32,0, 3,3,8,0, 1,3,8,0, 255,2,255,0, 255,3,3,0, 7,3,3,0, 1,1,6,0, 7,3,8,0, 7,3,8,0}; char bignumchars2[]={4,2,6,0, 32,255,32,0, 255,2,2,0, 2,2,6,0, 32,32,255,0, 2,2,6,0, 4,2,6,0, 32,7,32,0, 4,2,6,0, 2,2,6,0}; #endif
The above will eventually tell the LCD how to represent the new characters. bignumchars1 is line 1 of the LCD, and bignumchars2 is line 2. Each grouping of four numbers ("null-terminated" with a zero) points to the new characters we will define next. So for example, the large font number zero consists of:
Special character 7, 1, and 8 on line 1, and special characters 4, 2, and 6 on line 2.
Entries above like 255 and 32 point to already-existing characters in the LCD. 255 (or 0xFF in hex) points to a character that is a solid block. 32 (0x20) is a space (empty character).
Now find the chars[] declaration. Delete it, and replace it with this:
#if (CFG_BIGFONT_TYPE == 1) static byte chars[] PROGMEM = { B11111,B00000,B11111,B11111,B00000, B11111,B00000,B11111,B11111,B00000, B11111,B00000,B11111,B11111,B00000, B00000,B00000,B00000,B11111,B00000, B00000,B00000,B00000,B11111,B00000, B00000,B11111,B11111,B11111,B01110, B00000,B11111,B11111,B11111,B01110, B00000,B11111,B11111,B11111,B01110}; #elif (CFG_BIGFONT_TYPE == 2) /* XXX: For whatever reason I can not figure out how * to store more than 8 chars in the LCD CGRAM */ static byte chars[] PROGMEM = { B11111, B00000, B11111, B11111, B00000, B11111, B00111, B11100, B11111, B00000, B11111, B11111, B00000, B11111, B01111, B11110, B00000, B00000, B00000, B11111, B00000, B11111, B11111, B11111, B00000, B00000, B00000, B11111, B00000, B11111, B11111, B11111, B00000, B00000, B00000, B11111, B00000, B11111, B11111, B11111, B00000, B00000, B00000, B11111, B01110, B11111, B11111, B11111, B00000, B11111, B11111, B01111, B01110, B11110, B11111, B11111, B00000, B11111, B11111, B00111, B01110, B11100, B11111, B11111}; #endif
The above are binary representations of the pixels for the custom characters that we will upload to a special scratch area in the LCD. Each 'block' above is a number in the character generator RAM starting with 1 and going up to 8.
Now find the code that looks like this below, and either alter it so it looks EXACTLY like this, or just delete it and replace it with this:
/* write the character data to the character generator ram */ for(byte x=0;x<LcdNewChars;x++) { for(byte y=0;y<LcdCharHeightPix;y++) { LcdDataWrite(pgm_read_byte(&chars[y*LcdNewChars+x])); } }
I could not figure out how to cram more than eight characters into the LCD's CGRAM. If anyone can help with this I would add a few more characters to make this font even nicer. By following the above structure, you can see how to add a CFG_BIGFONT_TYPE 3, and create your own font variant.
--Skelly 07:05, 24 April 2009 (EDT)
more precision
Singletree5990 has modified his kit version to monitor injector pulse counts and system voltage, and is also looking into monitoring other variables such as fuel temperature in search of ultimate precision. http://ecomodder.com/forum/showthread.php/possible-way-measure-injector-characteristics-8382.html#post110622
Save Current Tank Data / Archive Tank Summary / Track Gas-Used per Speed Range
Here are a couple or three mods I've made to my MPGuino:
1a) Save current tank data to the eeprom, at the 7-minute sleep point. If there is a power loss or reset, tank data will be restored at restart. Also, if you are afraid of a thief stealing your 'guino, you can also force a save through the "Hold Tank" menu option (Left+Middle, then Right), then unplug it, without waiting the 7 minutes to auto-save.
1b) Archive summaries of tanks (or current trips, if you like) to the eeprom. You can later browse through each tank's data: Miles, Gallons, MPG, Avg MPH, Idle Gallons, EOC Miles, and a 10-digit numerical note (date, dollars, whatever... finally found a use for the Scratchpad parm :) Most of us are writing this all down anyway, but why not keep a digital copy too? Tanks are stored in a queue, so if you run out of eeprom space for your next tank, it will overwrite the oldest.
2) Track the percentage of gas used at different speeds. Here, we have 8 speed ranges (plus a ninth, "Idle"): Idle, 1-29mph, 30-39, 40-49, 5x, 6x, 7x, 8x, and 90+. There is a new screen to display these in real-time. Also, if used in conjunction with the above Tank Archive mod, the gas/speed percentages will be archived for later viewing. This data, I hope, can possibly help you analyze why/where/how you were wasting gas... all without external data logging.
Both mods can be compiled together or independently of each other, via compile-time options
"#define TrackSpeedGas" and "#define SaveTankToEeprom". I've only tested these on a 168 with v0.82, but they should also work with v0.75. A 328 should give you a lot more archive storage.
Anyway, on with the show:
Add these #define's near the top of your source.
#define eepromSize 512 //512 for atmega168, 1024 for 328 #define TrackSpeedGas // Define this to track gas used at different speeds. // Costs 85ram + 814 bytes/v0016 766 bytes/v0011 #define SaveTankToEeprom // Define this to enable Tank data save/restore/view // Save/Restore current "live" tank dataset, or // Archive & View tank summaries // Costs 1432 bytes/v0016 1366 bytes/v0011 //#define SaveCurrentToEeprom // Uncomment this if you also want to protect Current Trip from power-loss // If your Current auto-clears after 7 minutes, you probably won't need this #define eeParmStart 4 //eeprom location, beginning of parms[] #define savetanksig B11101101 #define eeTankSig eepromSize-1 //last byte of eeprom #define eeTankQueueIdx eepromSize-2 //2nd last byte of eeprom, contains queue # of last tank summary saved #ifdef SaveCurrentToEeprom #define eeHoldTankSize (sizeof(Trip)+sizeof(Trip)+sizeof(injSpeedHiSec)+sizeof(injSpeedHius)) //136 = 36+36+32+32 ... or 36+36+1+1, if !TrackSpeedGas #else #define eeHoldTankSize (sizeof(Trip)+sizeof(injSpeedHiSec)+sizeof(injSpeedHius)) //100 = 36+32+32 ... or 36+1+1, if !TrackSpeedGas #endif #define eeHoldTankStart (eeTankQueueIdx - eeHoldTankSize) //start byte of where to store live tank data (in event of power loss) #define eeHoldCurrentStart (eeHoldTankStart + sizeof(Trip)) //start byte of where to store live current-trip data (in event of power loss) #define eeHoldSpeedGasStart (eeTankQueueIdx - sizeof(injSpeedHiSec) - sizeof(injSpeedHius)) #define eeTankArchiveStart (eeHoldTankStart - eeTankArchiveSize) #ifdef TrackSpeedGas #define eeTankArchiveSize (9+11+4) #else #define eeTankArchiveSize (11+4) #endif // 15 bytes to save: 99.99 gallons, 99.99 idle, 99.99 mph, 99.99 eoc, 9999.99 miles, 4-byte decimal note (2+2+2+2+3+4) // optionally, store additional 9 bytes (1 3-digit percent of gas-used for 8 speed ranges + idle) #define eeTankQueueMax ((eeHoldTankStart-eeParmStart-sizeof(parms)-128) / eeTankArchiveSize) //# of 20-byte tank summaries we will store. store at end of eeprom, leave lower space for parms & future growth //start_loc_of_temp_tank_save) - (parms overhead) - 128 (future growth) #define READ 0 //for eeprom_block() #define WRITE 1 //for eeprom_block() #define LeftButton ((buttonState & lbuttonBit)==0) #define MiddleButton ((buttonState & mbuttonBit)==0) #define RightButton ((buttonState & rbuttonBit)==0)
Add these to your other function declarations section, if you have one.
void readWriteCurrentTank (byte); void updateSpeedGas (void); void wipeBytes (byte *, int); void eeprom_block (byte, byte *, int, int); void viewTankHistory (void); void doDisplaySpeedGas (void); void displaySpeedGasPct (void); byte menuButton (prog_char *, prog_char *); char * format(unsigned long, char, char); // ^-- Add this line. Don't replace the original char *format(unsigned long);
Add these to your class Trip { } declaration
void archiveTank(); void calcSpeedGasPct ();
Put these somewhere, maybe under the block of "unsigned volatile long ...InstInj..." lines
#ifdef TrackSpeedGas // 1-30, 30-40, 4x, 5x, 6x, 7x, 8x, 90+ mph unsigned long injSpeedHiSec[8]={0,0,0,0,0,0,0,0}; unsigned long injSpeedHius[8]={0,0,0,0,0,0,0,0}; #ifdef SaveTankToEeprom #define pctSpeedGas archiveTankData #else byte pctSpeedGas[9]; // pct[0] contains "idle" % #endif #else #ifdef SaveTankToEeprom //if SaveTank, but not TrackSpeed, define these anyway byte injSpeedHiSec[1], injSpeedHius[1]; #endif #endif #ifdef SaveTankToEeprom byte archiveTankData[24]; //9 pctSpeedGas + (2+2+2+2+3+4) #define archTankGallons (archiveTankData+9) #define archTankIdleGallons (archiveTankData+11) #define archTankMph (archiveTankData+13) #define archTankEocMiles (archiveTankData+15) #define archTankMiles (archiveTankData+17) //only use 3 low bytes of (long) miles #define archTankNote (archiveTankData+20) #endif
Add this to the pFunc displayFuncs[] ={ section
#ifdef TrackSpeedGas doDisplaySpeedGas, #endif
Find your "char fBuff[7]" and change it to [18]. If you are enterprising,
you can delete mBuff[] and use fBuff in getStr(), rformat() & uformat() to save a few bytes.
char fBuff[18]; //used by format() and hhmmss() 18 bytes gives enough room for 2 hh:mm:ss
Add these lines to the top of the load() function:
#ifdef SaveTankToEeprom readWriteCurrentTank(READ); #endif
Add this to the displayFuncNames in setup()
#ifdef TrackSpeedGas displayFuncNames[x++]= PSTR("%Gas @ Speed "); #endif
In loop(), add this to the top ...
byte menuKey;
... then add the lines marked "//add this" near the update() commands...
current.update(instant); //use instant to update current tank.update(instant); //use instant to update tank #ifdef TrackSpeedGas //add this updateSpeedGas(); //add this #endif //add this . . . //wake up ... current.update(instant); tank.update(instant); #ifdef TrackSpeedGas //add this updateSpeedGas(); //add this #endif //add this }else{ lastActivity=loopStart; tankHold = tank.loopCount;
... and add the Tank WRITE command to the timeout section...
//currentTripResetTimeoutUS if(instant.vssPulses == 0 && instant.injPulses == 0 && holdDisplay==0){ if(elapsedMicroseconds(lastActivity) > parms[currentTripResetTimeoutUSIdx] && lastActivity != nil){ analogWrite(BrightnessPin,brightness[0]); //nitey night readWriteCurrentTank(WRITE); //add this line lastActivity = nil; }
... and replace the button section with this:
//see if any buttons were pressed, display a brief message if so if (LeftButton && RightButton) {// left and right = initialize LCD::print(getStr(PSTR("Setup "))); initGuino(); //initGuino(0); // 0 == don't not edit == editParms lastActivity = microSeconds(); //update lastAct to prevent unwanted timeout after long submenu detour tmpTrip.reset(); //who knows what went on while we were in here } #ifdef SaveTankToEeprom else if (LeftButton && MiddleButton) //nickdigger {//Save ... menuKey = menuButton(PSTR("Store View Hold"), PSTR("Tank Hist Tank")); if (!(menuKey&rbuttonBit)) { readWriteCurrentTank(WRITE); } //hold tank data, in case of reset else if (!(menuKey&mbuttonBit)) { viewTankHistory(); } // else if (!(menuKey&lbuttonBit)) { tank.archiveTank(); } // add option to archive current or tank else if (!(menuKey&lbuttonBit)) { menuKey = menuButton(PSTR("<-Go Store Store"), PSTR("Back Tank Trip")); if (!(menuKey&mbuttonBit)) tank.archiveTank(); else if (!(menuKey&rbuttonBit)) current.archiveTank(); } lastActivity = microSeconds(); //update lastAct to prevent unwanted timeout tmpTrip.reset(); //who knows what went on while we were in here } else if(RightButton && MiddleButton) //nickdigger {// Clear ... menuKey=menuButton(PSTR("<-Go Reset Reset"), PSTR("Back Tank Trip")); if ((menuKey&rbuttonBit)==0) { current.reset(); #ifdef SaveCurrentToEeprom EEPROM.write(eeHoldCurrentStart, savetanksig); #endif } else if (!(menuKey&mbuttonBit)) { tank.reset(); EEPROM.write(eeHoldTankStart, savetanksig); #ifdef TrackSpeedGas wipeBytes((byte *)injSpeedHiSec, sizeof(injSpeedHiSec)); wipeBytes((byte *)injSpeedHius, sizeof(injSpeedHius)); #endif } lastActivity = microSeconds(); //update lastAct to prevent unwanted timeout tmpTrip.reset(); //who knows what went on while we were in here } #else // not SaveTankToEeprom else if (LeftButton && MiddleButton) {// left and middle = tank reset tank.reset(); LCD::print(getStr(PSTR("Tank Reset "))); } else if(RightButton && MiddleButton) {// right and middle = current reset current.reset(); LCD::print(getStr(PSTR("Current Reset "))); } #endif // SaveTankToEeprom else if (MiddleButton) { //middle is cycle through brightness settings brightnessIdx = (brightnessIdx + 1) % brightnessLength; analogWrite(BrightnessPin,brightness[brightnessIdx]); LCD::print(getStr(PSTR("Brightness "))); LCD::LcdDataWrite('0' + brightnessIdx); LCD::LcdDataWrite(' '); //print(" "); } else if (LeftButton) { //left is rotate through screeens to the left if (screen!=0) screen=(screen-1); else screen=displayFuncSize-1; LCD::print(getStr(displayFuncNames[screen])); } else if (RightButton) { //right is rotate through screeens to the left screen=(screen+1)%displayFuncSize; LCD::print(getStr(displayFuncNames[screen])); } ///////////done checking keys
And now, the code:
#ifdef TrackSpeedGas void Trip::calcSpeedGasPct (void) { //Calculate percent of gas used within each speed range, and store into 9-byte array // idle, 1-30mph, 30-39, 40-49, 5x, 6x, 7x, 8x, 90+ // We could do the calcs in displaySpeedGasPct, but we'll need to recycle that function to display saved tanks. // //nickdigger - 288 bytes v0016 char i; unsigned long injHi = (injHiSec*1000 + injHius/1000); // (injHiSec*100000 + injHius/10) / 100; //divide total injHi by 100, so when we divide each time set, we get xx% if (injHi == 0) injHi = 1; //avoid div-by-zero pctSpeedGas[0] = (byte) ( (injIdleHiSec*100000 + injIdleHius/10)/injHi ); for (i=0; i<8; i++) pctSpeedGas[i+1] = (byte) ( (injSpeedHiSec[i]*100000 + injSpeedHius[i]/10)/injHi ); return; } //Trip::calcSpeedGasPct() void displaySpeedGasPct (void) { //Displays percent of gas used within each speed range: (reallllly squeezing the numbers in) //i## <30:## ## ## (idle) (1-30mph) (30-39) (40-49) //5x## ## ## ## ## (50-59mph) (60-69) (70-79) (80-89) (90-up) // //nickdigger - 126 bytes v0016 char i; LCD::gotoXY(0,0); LCD::LcdDataWrite('i'); for (i=0; i<9; i++) { LCD::print( format(pctSpeedGas[i],2,0) ); if (i==0) LCD::print(" <30:"); else if (i==3) { LCD::gotoXY(0,1); LCD::print("5x"); } else if (i<8) LCD::LcdDataWrite(' '); } } //displaySpeedGas() void doDisplaySpeedGas (void) { tank.calcSpeedGasPct(); displaySpeedGasPct(); } #endif //TrackSpeedGas #ifdef TrackSpeedGas void updateSpeedGas (void) { // Accumulates injHius into injSpeedHiSec[]/Hius[] array, for each speed range of // 1-30, 30-39, 40-49, 5x, 6x, 7x, 8x, and 90+ mph //nickdigger - 256 bytes char i; byte imph, speedrange=90; if (instant.vssPulses == 0 || instant.injPulses <= 2 || instant.injHius >= 500000) return; //chasing ghosts & ignoring vss==0 imph = (byte) (instantmph()/1000ul); //dont need 32bit comparison for 2-digit mph. saves 10 bytes or so for (i=7; i>=0; i--) { if (imph >= speedrange || i==0) { injSpeedHius[i] += instant.injHius; if (injSpeedHius[i] >=1000000ul) { injSpeedHiSec[i]++; injSpeedHius[i] -= 1000000ul; } break; //we found & updated our speed range, now quit } speedrange -= 10; } //for } //updateSpeedGas() #endif //TrackSpeedGas void eeprom_block (byte rw, byte *p, int start, int len) { //nickdigger for (int end=start+len; start < end; p++) if (rw==WRITE) EEPROM.write(start++, *p); else *p = EEPROM.read(start++); } //eeprom_blockread() void wipeBytes (byte *p, int len) {//nickdigger - sets len bytes to 0 int i=0; while (i<len) *(p + i++) = 0; } //wipeBytes() #ifdef SaveTankToEeprom byte menuButton (prog_char *s1, prog_char *s2) { //nickdigger - print 2-string menu, wait for & return button byte x; LCD::gotoXY(0,0); LCD::print(getStr(s1)); LCD::gotoXY(0,1); LCD::print(getStr(s2)); buttonState=buttonsUp; while (buttonState==buttonsUp) delay2(250); x=buttonState; buttonState=buttonsUp; return x; } //menubutton() void viewTankHistory () { //nickdigger - 698 bytes /* tank## 12345note 9999.99mi 99.99g 99.9mpg 99.99mph 9.99idle 9.99eoc pctSpeedpage */ byte page=0, p0=5, q0=254, q=EEPROM.read(eeTankSig); if (q != savetanksig) return; q=EEPROM.read(eeTankQueueIdx); buttonState=buttonsUp; while (! MiddleButton) { q %= eeTankQueueMax; if (q != q0) eeprom_block(READ, archiveTankData, eeTankArchiveStart - q*eeTankArchiveSize, eeTankArchiveSize); if (page != p0) { if (page==0) { LCD::gotoXY(0,0); LCD::print(getStr(PSTR("Tank"))); LCD::print(format((long) q,2,0)); LCD::print(format(*(unsigned long *)archTankNote,10,0)); LCD::gotoXY(0,1); LCD::print(format( (*(long *)archTankMiles) & 0x00ffffff ,6,2)); LCD::print("mi"); LCD::print(format(*(int *)archTankGallons,5,2)); LCD::LcdDataWrite('g'); }else if (page==1) { LCD::gotoXY(0,0); LCD::print(format( ((*(long *)archTankMiles) & 0x00ffffff)*10 / (1 + *(int *)archTankGallons ), 3,1)); //add .01 to gallons to prevent div-zero. this will skew mpg for < 1.00 gallons LCD::print("mpg"); LCD::print(format(*(int *)archTankMph,5,2)); LCD::print("mph"); LCD::gotoXY(0,1); LCD::print(format(*(int *)archTankIdleGallons, 3,2)); LCD::print("idle"); LCD::print(format(*(int *)archTankEocMiles,4,2)); LCD::print("eoc"); }else if (page==2) #ifdef TrackSpeedGas displaySpeedGasPct(); #else page=0; q0=q; q++; #endif } //print page if (LeftButton) { p0=page; if (page==0) {page=2; q0=q; q=(q==0 ? eeTankQueueMax-1 : q-1); } else page--; buttonState=buttonsUp; } else if (RightButton) { p0=page; if (page==2) {page=0; q0=q; q++; } else page++; buttonState=buttonsUp; } delay2(250); } //while buttonState=buttonsUp; } //viewTankHistory() void Trip::archiveTank (void) { //nickdigger - 408 bytes //99.99 gallons, 9.99 idle, 99.99 mph, 9.99 eoc, 9999.99 miles, 32-bit decimal Note/Date (15 hex bytes 2+2+2+2+3+4) //Because of stupid reverse-byte order, we'll store all 4 bytes of each element -- keeping the low 2 bytes, and //overwriting the high 2 bytes (always == 00) with the next element. Good times. byte q=EEPROM.read(eeTankSig); if (q != savetanksig) { EEPROM.write(eeTankSig, savetanksig); q=0; } else q=(1 + EEPROM.read(eeTankQueueIdx)) % eeTankQueueMax; #ifdef TrackSpeedGas calcSpeedGasPct(); #endif *(unsigned long *)archTankGallons = gallons()/10; *(unsigned long *)archTankIdleGallons = idleGallons()/10; *(unsigned long *)archTankMph = mph()/10; *(unsigned long *)archTankEocMiles = eocMiles()/10; *(unsigned long *)archTankMiles = miles()/10; //Miles is only 3 bytes. 4th byte (==0) is overwritten by Note editParm(scratchpadIdx); //eeprom_block(WRITE, (byte *)&parms[scratchpadIdx], eeParmStart+scratchpadIdx*sizeof(long), sizeof(long)); //save Note parm to eeprom for quick edit next time. // On 2nd thought, don't save it. Byte-order is not preserved with original 0.75/0.82 load(). *(unsigned long *)archTankNote = parms[scratchpadIdx]; eeprom_block(WRITE,archiveTankData, eeTankArchiveStart - q*eeTankArchiveSize, eeTankArchiveSize); EEPROM.write(eeTankQueueIdx, q); } //archiveTank() void readWriteCurrentTank (byte rw) {//nickdigger /* EEPROM structure: 511 SaveTankSig (B11101101) 510 eeTankQueueIdx (# of last tank archived) 478-509 injSpeedHius 446-477 injSpeedHiSec 410-445 current trip hold data (optional) 374-409 tank hold data 354-373 archive tank summary #0 334-353 archive tank summary #1 314-333 archive tank summary #2 etc etc more tanks, as space allows 52-179 reserved for future use 4- 51 parms[] 0- 3 guinosig, parmcount, 2 empty bytes */ #ifdef SaveCurrentToEeprom if (rw==WRITE || EEPROM.read(eeHoldCurrentStart) != savetanksig) eeprom_block(rw, (byte *)¤t, eeHoldCurrentStart, sizeof(Trip)); #endif if (rw==WRITE || EEPROM.read(eeHoldTankStart) != savetanksig) { eeprom_block(rw, (byte *)&tank, eeHoldTankStart, sizeof(Trip)); #ifdef TrackSpeedGas eeprom_block(rw, (byte *)injSpeedHiSec, eeHoldSpeedGasStart, sizeof(injSpeedHiSec)); eeprom_block(rw, (byte *)injSpeedHius, eeHoldSpeedGasStart + sizeof(injSpeedHiSec), sizeof(injSpeedHius)); #endif } } //readWriteCurrentTank() #endif //SaveTankToEeprom //new flexible format(). 120 bytes larger than old format(). //function overloading in C++ allows this code to compile & work //without changing or deleting the old format(x) code char* format(unsigned long num, char digits, char dp) //nickdigger { // v0011: 758 bytes for this format(x,len,dp) only, 638 for format(x) only, or 1000 for both. Weird. // v0016: adding v2 to v1 costs 388 bytes; adding v1 to v2 costs 238 bytes // // Flexible length & decimal places -- does not default to xx.xxx // Leading spaces instead of zer0s; // // digits = max # of digits. dp = # of decimal places desired // Overall output length = digits + 1, if dp>0; else length = digits // // len.dp : E.g. 4.1 = xxx.y, 5.2 = xxx.yy, 4.0 = xxxx // If num is too large to fit len.dp, we will displace decimals // to preserve the more significant (large) digits. // E.g, (12345,4,2) wants to display 123.45 but we only have 4 digits. So, we print 123.5 // (12345,4,1) prints " 1234". (123456,4,1) prints " 9999" char i=1, dp0=dp; unsigned long limit=10; while (i++ < digits) // Wow. 110 bytes for this little *=10 loop. 8 bit CPUs FTL. limit *= 10; i=0; while (dp >0 && i++ <= dp) //reduce decimal places if num is too big. { //e.g. (12345,4,1) should be "1234.5", if (num >= limit) //but we only have 4 digits, so print "1234". { if ((num%10) > 4) //round up dropped digit num+=10; num /= 10; dp--; } } if (num >= limit) num=limit-1; //if overflow, then 99999 if (dp0 > 0) i=digits; //check dp0 to see if our string has 1 extra space for the '.' else i=digits-1; fBuff[i+1]=0; dp=digits-dp; while (i >= 0) { fBuff[i--] = (num==0 && i<dp-1 ? ' ' : '0'+(num%10) ); num/=10; if (i==dp) fBuff[i--]='.'; } return fBuff; } //format(x,dig,dp) //nickdigger//
That's it. I've since expanded the "Gas Used Per Speed Range" concept to include "Time Spent per Speed Range", but I made so many other changes to the base data structures & code, that it will no longer splice right into vanilla 0.75 or 0.82.
Add your own hack
Hit the edit button to the right and figure it out.