Motorcycle Control Panel with Arduino + Bluetooth

Here is the latest version of the BoB code. I may have to split it in multiple postings. The only tricky part of this in my mind is the way I handle interrupts. I have them split into stubs which catch the interrupts and store them in a table, and routines which run periodically out of "Loop" which scan the table and handle the events. I do it this way so sloppy/slow coding doesn't make me miss interrupts without knowing. In principal I could take that stuff out once it's working but I never seem to.

//july 11 V8 cut out voltage display cuz it doesn't work!
//display speed in MPH for US trip
#include <LiquidCrystal.h>
LiquidCrystal lcd(10,13,9,12,15,11,16); //(rs,rw,enbl,d4,d5,d6,d7) 
unsigned long lasttachint,lastwheelint; //time in millis of most recent tach and wheel interrupts
float insttachrpm=0, avgtachrpm=0, wheelrpm=0;  //tach instantaneous and rolling average rpm,  wheel rpms 
float revratio; int currentgear=0; //updated by setgear routine
float roadspeed=0; //speed in kph
volatile int ilog=0; 
int maxlog=31;  
volatile boolean logoflo; //index to log table, max entry #, log overflow flag
volatile byte lflag[32]; 
volatile unsigned long lmillis[32]; //log of interrupts
int tachpin=2;  //pedal sensor booster, drives interrupt 0;
int wheelpin=3; //wheel sensor, drives interrupt 1;
int neutralpin=5;
void setup() {
  lcdbegin(); //initialize the lcd
  clearlog();  //clear the interrupt table
  attachInterrupt(0, tachmonitor, RISING);  //interrupt from tachometer
  digitalWrite(wheelpin,HIGH); //set pullup resistor for reed switch
  attachInterrupt(1, wheelmonitor, RISING);  //interrupt from reed switch wheel sensor
}

void loop(){
  checklog();  //make sure log is ok before processing
  processlog(); //process and dump the log file to the PC
  chkneutral(); //test for neutral
  chkstale(); //check to see if tach or speed readings are >3 seconds old (bike has stopped!)
  updatedisplay();
  delay(100);  //wait a while
}
void chkstale(){
  if ((millis()> (lastwheelint+3000)) && (wheelrpm>0)){
    wheelrpm=0; roadspeed=0;
  }
  if ((millis()> (lasttachint+3000)) && (insttachrpm>0)){
    insttachrpm=0; avgtachrpm=0;    
  }
}

void updatedisplay(){
  lcd.clear();
  pbc(currentgear); //display what gear we're in
  lcd.setCursor(4,0); lcd.print(int((avgtachrpm+50)/100)*100); //tach
  lcd.setCursor(4,1); lcd.print(int((roadspeed/1.6)+.5)); //speed in Mph
}
void chkneutral(){
  if (inneutral()){
    currentgear=10;
  } //10 is code for neutral
  else{ //need to see if we've come out of neutral with no other info   
    if (currentgear==10) {// if we used to be in neutral
      currentgear=0; //      0 is "don't know"
    }
  }
}

boolean inneutral(){ //see is neutral light is on
  int n=analogRead(neutralpin); //neutral light sensor is on analog pin 5 - usually <100 in neutral, >300 in gear.
  if (n<150){
    return(true);
  }
  else{
    return(false);
  }
}
void checklog(){ //make sure log hasn't overflowed
  if (logoflo==true){
    noInterrupts();
    clearlog();
    interrupts();
    Serial.print("log overflowed at ");
    Serial.println(millis());
  }
}

void processlog(){
  int dlog,i; //how many entries to copy and dump
  unsigned long dmillis[32];
  byte dflag[32];  //space for the log entries
  noInterrupts(); //disable interrupts while we copy the log
  dlog=ilog;  //copy the count
  if (dlog!=0) { //if there's anything to copy
    for(int i=0; i<dlog;i++){
      dmillis[i]=lmillis[i]; 
      dflag[i]=lflag[i];
    }
    ilog=0; //reset the count
  }
  interrupts(); //reenable interrupts
  if (dlog!=0){ //if there's anything to process
    for (i=0;i<dlog;i++){
      //Serial.print(i); Serial.print(dflag[i]); Serial.print(" "); Serial.print(dmillis[i]); Serial.print(" ");
      if (dflag[i]=='W') {
        processwheel(dmillis[i]);
      }
      else {
        if (dflag[i]=='T') {
          processtach(dmillis[i]);
        }
      }
      //Serial.println(" ");
    }
  }
}

void processtach(unsigned long lmillis){ // input is interrupt time in millis
  unsigned long tachtime; //elapsed time for 10 tach events
  if (lasttachint!=0) { //skip calculation on first time in
    tachtime=lmillis-lasttachint;  //time for last 10 events
    if (tachtime>0){ //just avoid divide by 0
      insttachrpm=60000/(float)tachtime*10;  //rpm based on time in ms for 10 tach pulses
      avgtachrpm=(avgtachrpm*2+insttachrpm)/3; //rolling average rpm
      //Serial.print(" tachtime="); Serial.print(tachtime); Serial.print(" inst tach rpm="); Serial.print(insttachrpm); Serial.print(" avg tach rpm="); Serial.print(avgtachrpm);
    }
  }
  lasttachint=lmillis;
}

void tachmonitor(){
  static int tachcount;
  if (tachcount <9) { //bulking up tach interrupts by a factor of 10
    tachcount++;
  }
  else {  //every 10th tach pulse we spawn a log entry
    if (ilog<=maxlog){
      lflag[ilog]='T'; //pedal pass flag
      lmillis[ilog]=millis();
      ilog++;
    } 
    else {
      logoflo=true; //we've overrun the log table
    }
    tachcount=0;
  }
}

void processwheel(unsigned long lmillis){ //input is time of interrupt from log
  unsigned long wheeltime; //time for previous revolution
  if (lastwheelint!=0){ //don't do calculations 1st time in
    wheeltime=lmillis-lastwheelint; //track the exact rotation time
    wheelrpm=60000/(float)wheeltime; 
    //Serial.print(" wheeltime="); Serial.print(wheeltime); Serial.print(" wheel rpm="); Serial.print(wheelrpm);
    roadspeed=.123*wheelrpm; //condensed version of 2.05*60*wheelrpm/1000; where 205 is wheel circ. in metres
    //Serial.print(" road speed kph="); Serial.print(roadspeed);
    setgear(); //figure out what gear we're in
  }
  lastwheelint=lmillis;
}

void wheelmonitor(){
    if (ilog<=maxlog){
      lflag[ilog]='W'; //wheel pass flag
      lmillis[ilog]=millis(); //record time
      ilog++;
    } 
    else {
      logoflo=true;
    }
}