Go Back   EcoModder Forum > EcoModding > Instrumentation > OpenGauge / MPGuino FE computer
Register Now
 Register Now

Reply  Post New Thread
Submit Tools LinkBack Thread Tools
Old 04-17-2011, 10:42 PM   #21 (permalink)
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
I like what you're coming up with! I've been a bit confused on things and reading from the wiki, and the first workspace and then the other guy who made do it yourself kits.

I'm a fairly tech smart person and I was having some issues figuring it all out. To non tech people, this could be very confusing and just stop them from being interested. Mpguino is awesome, and I for one want to see it continue to develop and evolve.

  Reply With Quote
Alt Today
Popular topics

Other popular topics in this forum...

Old 04-17-2011, 11:04 PM   #22 (permalink)
EcoModding Lurker
FalconFour's Avatar
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78

LEAF - '11 Nissan LEAF
Thanks: 4
Thanked 9 Times in 7 Posts
Thanks! Yeah, the goal here is openness on both code-side and user-side... certainly a clean display with a visible interface would be more inviting than one plugged with "0"'s in all but a few places It's definitely an interesting task...
  Reply With Quote
Old 04-18-2011, 03:53 AM   #23 (permalink)
EcoModding Lurker
FalconFour's Avatar
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78

LEAF - '11 Nissan LEAF
Thanks: 4
Thanked 9 Times in 7 Posts
As promised, progress is being made on the code side! I've managed to re-implement the core of MPGuino - monitoring and looping - with a fully restructured block of code. It's been a ton of fun going through each line and cleaning up the syntax and combining/sorting declarations and definitions...

// mpguino release two
// implemented in native Arduino code

// using Arduino libraries here
#include <EEPROM.h>
#include <LiquidCrystal.h>
// ... mostly.
#include <avr/interrupt.h>

#define loopsPerSecond 2 // how many times will we try and loop in a second

//use with 20mhz (mpguino board)
//#define cyclesperhour		4500
//#define dispadj			800
//#define dispadj2			1250
//#define looptime			1250000ul/loopsPerSecond // time is skewed on 20mhz by 125%, so 1 sec = 1.25 perceived seconds (1,250,000 uSec)
//#define myubbr			(20000000/16/9600-1)
//#define injhold			(parms[injectorSettleTimeIdx]*5)/4

//use with 16mhz (arduino board)
#define cyclesperhour	3600
#define dispadj			1000
#define dispadj2		1250
#define looptime		1000000ul/loopsPerSecond // 1 sec divided by loopsPerSecond
#define myubbr			(16000000/16/9600-1)
#define injhold			parms[injectorSettleTimeIdx]

#define contrastIdx						0
#define vssPulsesPerMileIdx				1
#define microSecondsPerGallonIdx		2
#define injPulsesPer2Revolutions		3
#define currentTripResetTimeoutUSIdx	4
#define tankSizeIdx						5
#define injectorSettleTimeIdx			6
#define weightIdx						7
#define scratchpadIdx					8
#define vsspauseIdx						9
#define injEdgeIdx						10
#define nil			3999999999ul
#define guinosigold	0b10100101
#define guinosig	0b11100111
#define vssBit		0b00000001
#define lbuttonBit	0b00001000
#define mbuttonBit	0b00010000
#define rbuttonBit	0b00100000
#define buttonsUp   lbuttonBit + mbuttonBit + rbuttonBit
#define contrastPin		6
#define brightnessPin	9
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

typedef void (* pFunc)(void); // how we refer to function pointers
typedef uint8_t boolean;
typedef uint8_t byte;

unsigned long microSeconds(void);
unsigned long millis2(void);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds, unsigned long currentMicroseconds);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds);
unsigned long injHiStart;
unsigned long thisLoop, longestLoop, totalLoops;
volatile unsigned long timer2_overflow_count;
volatile unsigned long instInjStart = nil;
volatile unsigned long tmpInstInjStart = nil;
volatile unsigned long instInjEnd;
volatile unsigned long tmpInstInjEnd;
volatile unsigned long instInjTot;
volatile unsigned long tmpInstInjTot;
volatile unsigned long instInjCount;
volatile unsigned long tmpInstInjCount;
volatile unsigned long lastVSS1;
volatile unsigned long lastVSSTime;
volatile unsigned long lastVSS2;

volatile boolean vssFlop;
volatile boolean lastVssFlop = vssFlop;

volatile static pFunc int0Func, int1Func;

byte buttonState = buttonsUp;
byte newRun;
byte load();

void processInjOpen(void);
void processInjClosed(void);
void initGuino();
void save();

unsigned long parms[] = { 
	55ul, 4000ul,251500000ul, 3ul, 420000000ul, 10300ul,
	500ul, 2400ul, 0ul, 2ul, 0ul};//default values
char * parmLabels[] = { 
	"Contrast", "VSS Pulses/Mile", "MicroSec/Gallon",
	"Pulses/2 revs", "Timout(microSec)", "Tank Gal * 1000",
	"Injector DelayuS", "Weight (lbs)", "Scratchpad(odo?)", "VSS Delay ms",
	"InjTrg 0-Dn 1-Up" };
#define parmsLength (sizeof(parms)/sizeof(unsigned long))
#define distancefactor parms[vssPulsesPerMileIdx];
#define fuelfactor parms[microSecondsPerGallonIdx];
unsigned long injectorSettleTime = injhold;

//event functions
void enableLButton() { PCMSK1 |= (1 << PCINT11); }
void enableMButton() { PCMSK1 |= (1 << PCINT12); }
void enableRButton() { PCMSK1 |= (1 << PCINT13); }
void enableVSS() { vssFlop = !vssFlop; } // tmpTrip.vssPulses++;

//array of the event functions
pFunc eventFuncs[] = { 
	enableVSS, enableLButton, enableMButton, enableRButton };
#define eventFuncSize (sizeof(eventFuncs)/sizeof(pFunc))
#define enableVSSID		0
#define enableLButtonID	1
#define enableMButtonID	2
#define enableRButtonID	3
// msec counters for event triggers
unsigned int eventFuncCounts[eventFuncSize];

byte brightness[] = { 0, 41, 84, 128 };
#define brightnessLength (sizeof(brightness)/sizeof(byte))
byte brightnessIdx = 1;

// structures
class Trip {
	unsigned long loopCount; //how long has this trip been running
	unsigned long injPulses; //rpm
	unsigned long injHiSec;// seconds the injector has been open
	unsigned long injHius;// microseconds, fractional part of the injectors open
	unsigned long injIdleHiSec;// seconds the injector has been open
	unsigned long injIdleHius;// microseconds, fractional part of the injectors open
	unsigned long vssPulses;//from the speedo
	unsigned long vssEOCPulses;//from the speedo
	unsigned long vssPulseLength; // only used by instant
	//these functions actually return in thousandths,
	unsigned long miles();
	unsigned long gallons();
	// ** F4 edit: metric
	//	unsigned long lkm();
	unsigned long mpg();
	unsigned long mph();
	unsigned long time(); //mmm.ss
	unsigned long eocMiles(); //how many "free" miles?
	unsigned long idleGallons(); //how many gallons spent at 0 mph?
	void update(Trip t);
	void reset();
Trip tmpTrip;
Trip instant;
Trip current;
Trip tank;

// cheap hack?
Trip::Trip() {

LiquidCrystal lcd(4,5,7,8,12,13);

// utility functions
//overflow counter used by millis2()
unsigned long lastMicroSeconds = millis2() * 1000;
unsigned long microSeconds(void) {
	unsigned long tmp_timer2_overflow_count;
	unsigned long tmp;
	byte tmp_tcnt2;
	//disable interrupts
	tmp_timer2_overflow_count = timer2_overflow_count;
	tmp_tcnt2 = TCNT2;
	// enable interrupts
	tmp = ((tmp_timer2_overflow_count << 8) + tmp_tcnt2) * 4;
	if ((tmp <= lastMicroSeconds) && (lastMicroSeconds < 4290560000ul)) return microSeconds();
	lastMicroSeconds = tmp;
	return tmp;
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds,
unsigned long currentMicroseconds) {
	if (currentMicroseconds >= startMicroSeconds) return currentMicroseconds - startMicroSeconds;
	return 4294967295 - (startMicroSeconds - currentMicroseconds);
unsigned long elapsedMicroseconds(unsigned long startMicroSeconds) {
	return elapsedMicroseconds(startMicroSeconds, microSeconds());
unsigned long millis2() {
	return timer2_overflow_count * 64UL * 2 / (16000000UL / 128000UL);
void delay2(unsigned long ms) {
	unsigned long start = millis2();
	while (millis2() - start < ms)
//schedule an event to occur ms milliseconds from now
void addEvent(byte eventID, unsigned int ms) {
  if (ms == 0)
    eventFuncCounts[eventID] = ms;
byte load() {
#ifdef usedefaults
  return 1;
  byte b = EEPROM.read(0);
  byte c = EEPROM.read(1);
  // upgrayedd yer parms plzkthx
  if (b == guinosig) {
    byte p = 0;
    for (int x = 4; p < c; x += 4) {
      unsigned long v = EEPROM.read(x);
      v = (v << 8) + EEPROM.read(x + 1);
      v = (v << 8) + EEPROM.read(x + 2);
      v = (v << 8) + EEPROM.read(x + 3);
      parms[p] = v;
    return 1;
  return 0;
void save() {
  EEPROM.write(0, guinosig);
	EEPROM.write(1, parmsLength);
  byte p = 0;
  for (int x = 4; p < parmsLength; x += 4) {
    unsigned long v = parms[p];
    EEPROM.write(x, (v >> 24) & 255);
    EEPROM.write(x + 1, (v >> 16) & 255);
    EEPROM.write(x + 2, (v >> 8) & 255);
    EEPROM.write(x + 3, (v) & 255);

// interrupts!
/* this ISR gets called every 1.024 milliseconds, we will call that a millisecond for our purposes
go through all the event counts,
if any are non zero subtract 1 and call the associated function if it just turned zero.  */
ISR(TIMER2_OVF_vect) {
	for (byte eventID = 0; eventID < eventFuncSize; eventID++) {
		if (eventFuncCounts[eventID] != 0) {
			if (eventFuncCounts[eventID] == 0) eventFuncs[eventID]();

ISR(INT0_vect) { int0Func(); } //processInjOpen by default
ISR(INT1_vect) { int1Func(); } //processInjClosed
//attach the vss/buttons interrupt
ISR(PCINT1_vect) {
	static byte vsspinstate = 0;
	byte p = PINC;//bypassing digitalRead for interrupt performance
	if ((p & vssBit) != (vsspinstate & vssBit)) {
		addEvent(enableVSSID, parms[vsspauseIdx]); //check back in a couple milli
	if (lastVssFlop != vssFlop) {
		lastVSS1 = lastVSS2;
		unsigned long t = microSeconds();
		lastVSS2 = elapsedMicroseconds(lastVSSTime, t);
		lastVSSTime = t;
		tmpTrip.vssPulseLength += lastVSS2;
		lastVssFlop = vssFlop;
	vsspinstate = p;
	buttonState &= p;

void processInjOpen(void) { injHiStart = microSeconds(); }
void processInjClosed(void) {
	long t = microSeconds();
	long x = elapsedMicroseconds(injHiStart, t) - injectorSettleTime;
	if (x > 0) tmpTrip.injHius += x;
	if (tmpInstInjStart != nil) {
		if (x > 0) tmpInstInjTot += x;
	} else tmpInstInjStart = t;
	tmpInstInjEnd = t;

void setup() {
	sbi(TCCR0A, WGM01);
	sbi(TCCR0A, WGM00);
	sbi(TCCR0B, CS01);
	sbi(TCCR0B, CS00);
	sbi(TIMSK0, TOIE0);

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
	sbi(TCCR1B, CS10);
	// put timer 1 in 8-bit phase correct pwm mode
	sbi(TCCR1A, WGM10);
	// set timer 2 prescale factor to 64
	sbi(TCCR2B, CS22);
	// configure timer 2 for phase correct pwm (8-bit)
	sbi(TCCR2A, WGM20);

	// set a2d prescale factor to 128
	sbi(ADCSRA, ADPS2);
	sbi(ADCSRA, ADPS1);
	sbi(ADCSRA, ADPS0);

	// enable a2d conversions

	UCSR0B = 0;


	timer2_overflow_count = 0;

	TCCR2A = 1 << WGM20 | 1 << WGM21;
	// set timer 2 prescale factor to 64
	TCCR2B = 1 << CS22;
	TIMSK2 |= 1 << TOIE2;
	TIMSK0 &= !(1 << TOIE0);

	//load the default parameters
	newRun = load();
	// start up the LCD and announce we're here
	lcd.setCursor(0, 0);
	lcd.print("FalconFour's    ");
	lcd.setCursor(0, 1);
	lcd.print("  MPGuino  v2.00");
	int0Func = processInjOpen;
	int1Func = processInjClosed;

	//set up the external interrupts
	// (a mouthful!)
	EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | ((parms[injEdgeIdx] == 1 ? RISING : FALLING) << ISC00);
	EIMSK |= (1 << INT0);
	EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | ((parms[injEdgeIdx] == 1 ? FALLING : RISING) << ISC10);
	EIMSK |= (1 << INT1);
	PORTC |= (1 << 5) | (1 << 4) | (1 << 3); //button pullup resistors

	//low level interrupt enable stuff
	PCMSK1 |= (1 << PCINT8);
	PCICR |= (1 << PCIE1);


void loop() {
	unsigned long lastActivity = microSeconds();
	unsigned long tankHold;
	while (true) {
		// loop gets called every looptime microsec
		unsigned long loopStart = microSeconds();
		instant.reset(); //clear instant
		instant.update(tmpTrip); //"copy" of tmpTrip in instant now
		tmpTrip.reset(); //reset tmpTrip first so we don't lose too many interrupts
		// copy tmp to current
		instInjStart = tmpInstInjStart;
		instInjEnd = tmpInstInjEnd;
		instInjTot = tmpInstInjTot;
		instInjCount = tmpInstInjCount;
		// and reset tmp values
		tmpInstInjStart = nil;
		tmpInstInjEnd = nil;
		tmpInstInjTot = 0;
		tmpInstInjCount = 0;
		current.update(instant); //use instant to update current
		tank.update(instant); //use instant to update tank
		lcd.print("This loop: ");
		lcd.print(totalLoops, DEC);
		//keep track of how long the loops take before we go int waiting.
		thisLoop = elapsedMicroseconds(loopStart);
		lcd.print("msec: ");
		if (thisLoop > longestLoop) longestLoop = thisLoop;
		//wait for the end of a second to arrive
		while (elapsedMicroseconds(loopStart) < looptime)

// trip class functions
void Trip::reset() {
  loopCount = 0;
  injPulses = 0;
  injHius = 0;
  injHiSec = 0;
  vssPulses = 0;
  vssPulseLength = 0;
  injIdleHiSec = 0;
  injIdleHius = 0;
  vssEOCPulses = 0;

void Trip::update(Trip t) {
  loopCount++; //we call update once per loop
  vssPulses += t.vssPulses;
  vssPulseLength += t.vssPulseLength;
  if (t.injPulses == 0) //track distance traveled with engine off
    vssEOCPulses += t.vssPulses;

  if (t.injPulses > 2 && t.injHius < 500000) {//chasing ghosts
    injPulses += t.injPulses;
    injHius += t.injHius;
    if (injHius >= 1000000) { //rollover into the injHiSec counter
      injHius -= 1000000;
    if (t.vssPulses == 0) { //track gallons spent sitting still

      injIdleHius += t.injHius;
      if (injIdleHius >= 1000000) { //r
        injIdleHius -= 1000000;

Right now it does nothing at all... it monitors "in memory" and displays a loop count on the LCD (as wired in the MPGuino device). No button inputs, no math, no calculations... but those are all part of the UI that's going to be redesigned

Currently compiles to about 7kb, which isn't half bad considering we have most utility functions and structures in place already!
  Reply With Quote
Old 04-18-2011, 11:49 AM   #24 (permalink)
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
Did you do all that last night?
  Reply With Quote
Old 04-18-2011, 12:04 PM   #25 (permalink)
EcoModding Lurker
FalconFour's Avatar
Join Date: Sep 2010
Location: Fresno, CA
Posts: 78

LEAF - '11 Nissan LEAF
Thanks: 4
Thanked 9 Times in 7 Posts
Yep. xD

Hopefully more tonight... finally getting down to a sane work schedule (part time that still pays the bills - yay!), so I should have a functional UI framework set up tonight...
  Reply With Quote
Old 04-18-2011, 01:15 PM   #26 (permalink)
EcoModding Apprentice
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Any interest in merging projects? I've been slow in writing the UI pages themselves, but the framework is all in place on the MPGshield project. Right now it just has a VSS info page, injector info page, and instant/average MPG page. All I ask is that the code remain hardware-neutral (standard libraries where available, #define pin numbers and such) and is reasonably readable and/or well-commented, which IIRC is among your goals as well.
Here's the current code, including stuff to control an external 4 digit 7-segment display via an SAA1064 chip via I2C. The display comms are one-way, so it doesn't hurt anything if there's no chip to talk to.

Last edited by bobski; 04-18-2011 at 01:22 PM.. Reason: added link
  Reply With Quote
Old 04-18-2011, 01:32 PM   #27 (permalink)
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
i searched google and couldn't find anything about MPGshield project. Do you have a link or something for those interested in your work?
  Reply With Quote
Old 04-18-2011, 02:01 PM   #28 (permalink)
EcoModding Lurker
mcmancuso's Avatar
Join Date: Mar 2010
Location: TN-USA
Posts: 61

Green Metro - '99 Chevrolet Metro LSi Sedan
90 day: 32.78 mpg (US)

Metro Vert - '91 Geo Metro LSi Convertible
90 day: 50.52 mpg (US)
Thanks: 0
Thanked 1 Time in 1 Post
steffen, what you're looking for is here: http://ecomodder.com/forum/showthrea...-v2-16364.html

You guys are clearly much better at programming than myself, keep up the good work!
Are you planning on adding any code modifications like the rounder large font, the eeprom tank data save or others? Personally I'd also like to see a clock added, and a miles/time till empty
  Reply With Quote
Old 04-18-2011, 02:03 PM   #29 (permalink)
Master EcoModder
Join Date: Oct 2009
Location: Midwest
Posts: 337
Thanks: 4
Thanked 37 Times in 21 Posts
Thanks, i'll check out your thread.
  Reply With Quote
Old 04-18-2011, 04:11 PM   #30 (permalink)
EcoModding Apprentice
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Originally Posted by mcmancuso View Post
Are you planning on adding any code modifications like the rounder large font, the eeprom tank data save or others? Personally I'd also like to see a clock added, and a miles/time till empty
I can't speak for this project, but all of the above have been on my to-do list. I just haven't gotten to them between school (finishing up my EE AAS degree) and other projects I have kicking around.
Figuring out storing variables to internal EEPROM is my planned next step in development (see below).
Making a trip log on external EEPROM might be a little more involved (deciding what a trip is, formatting and storing the data, programming a way to read it back), but still on the list... I already have such a chip hooked up on my shield.
I have a DS1337 real time clock and backup battery all soldered up and ready to go on some strip board. I also got some DS3231M high-accuracy real time clocks as (free) samples from Maxim Semiconductor (yay for being an engineering student!), so i'll be playing with those at some point as well.
At this point, miles/time to empty should be a simple matter of figuring out the calculations and writing a text page to display them.

In addition to the above, I would like to depart from the MPGuino's always-on method by using a voltage regulator with an on/off lead such as the Sharp PQ05RD21J00H. These regulators have the standard Vin, Vout and Ground pins, plus a fourth control pin that shuts off Vout and puts the regulator in a very low power draw state. In our case, Vin would be constant battery power from the car, Vout goes to the 5V+ power bus of the MPGuino/MPGshield/(/Fork name TBD), replacing any stock regulator and Ground gets shared by the two voltage systems... This is exactly the same way the MPGuino and most Arduino systems are presently set up AFAIK. The difference is that fourth control pin.
When you switch the ignition on, the car's switched 12V+ would send 5V+ to the regulator's control pin via one of the typical resistor-zener pairs used for input conditioning on the MPGuino and MPGshield. The regulator kicks on and powers up the system. Part of the system's setup routine would drive high a digital output that feeds the regulator's control lead in tandem with ignition switched power (diode isolation would be used). Setup would continue on to read various variables (distance traveled, fuel dispensed, similar trip values and such) out of the EEPROM storage on the ATMEGA chip. For those of you who see where I'm going, the EEPROM memory is rated for way more read-write cycles than flash, so I don't see any problem using it this way and is actually preferable to somehow writing the variables to flash.
When the ignition is turned off, the switched 12V+ line to the regulator cuts off, but the system keeps the regulator active with its own line. The system would read that the ignition had been turned off either by a second line, or possibly by reading that fact over the driving regulator line (it would have to be an analog pin), and kick into a power-down routine. This routine would save any necessary variables into EEPROM, do any other power-down stuff that is needed, then let the regulator control line drop, shutting down the regulator and cutting power to the system.

  Reply With Quote
Reply  Post New Thread

Thread Tools

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