//August 24th 2nd Pikachu version - meant to be physically small // a home brew charlieplexed strip of 5 leds is connected to digital 9, 10, 13 //analog input for battery voltage, neutral light, turn signal indicator light //Aug 27 v2.1b - 1st on-bike test, pin assignments finalized //AUG 29 V2.2 dimming kludge for leds implemented. 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=3; //pedal sensor booster, drives interrupt 1; int wheelpin=2; //wheel sensor, drives interrupt 0; int neutralpin=2; //analog 2 is neutral lite int battpin=0; //analog input for battery voltage int sigpin=1; //signal light on analog pin 1 int cpin1=19, cpin2=18, cpin3=17;//pins for charlieplexed led array. void setup() { clearlog(); //clear the interrupt table attachInterrupt(1, tachmonitor, RISING); //interrupt from tachometer digitalWrite(wheelpin,HIGH); //set pullup resistor for reed switch attachInterrupt(0, wheelmonitor, RISING); //interrupt from reed switch wheel sensor Serial.begin(9600); Serial.println("Version PV2.2"); salute(); } void salute(){ //just blink some lights int j, k; /*for (k=1; k<=3;k++){ for (j=1;j<=5;j++){ cplex(j); delay(1000); } cplex(0); delay(1500); }*/ for (k=1;k<=4;k++){ cplex(3); delay(250); for (j=1;j<=25;j++){ cplex(2); delay(5); cplex(4); delay(5); } for (j=1;j<=25;j++){ cplex(1); delay(5); cplex(5); delay(5); } } cplex(0); } void loop(){ static int tachval, wheelval, neutralval, sigval, battval; 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 >5 seconds old (bike has stopped!) updatedisplay(); delay(10); //slight delay to keep switching load down } float volts(int aval){ float factor=5*31.7/9.7; return float(aval)/1023*factor; } void chkstale(){ static unsigned long lastlitecheck; //last time we checked if ((millis()> (lastwheelint+3000)) && (wheelrpm>0)){ //** wheelrpm=0; roadspeed=0; } if ((millis()> (lasttachint+3000)) && (insttachrpm>0)){ insttachrpm=0; avgtachrpm=0; } } void updatedisplay(){ int displaygear=currentgear; if (currentgear==10){ displaygear=0; } //handle neutral hcplex(displaygear); //well, that was easy! } 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; i0){ //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(" "); 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; } } int whichgear(float ratio){ float validratios[]={ 11.7,8.37,6.65,5.54,4.61 }; int maxratioindex=4; //ratios for motorcycle int gearsforratios[]={ 1,2,3,4,5 }; for (int i=0;i<=maxratioindex;i++){ if (closeto(ratio,validratios[i],4)) return gearsforratios[i]; } return 0; } boolean closeto(float param, float reference, float pcttolerance){ //true if param is within tolerance% of reference return (param>=(reference*(100-pcttolerance)/100))&&(param<=(reference*(100+pcttolerance)/100)); } 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(" wt=");*/ Serial.print(wheeltime); Serial.print(" "); 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; } } void setgear(){ //determine which gear the bike is in int newgear; static int prevgear, prev2gear; if (wheelrpm!=0 && insttachrpm!=0){ revratio=insttachrpm/wheelrpm; Serial.print("<"); Serial.print(revratio); Serial.print(">"); newgear=whichgear(revratio); if (newgear!=0){ Serial.print(" C: "); Serial.print(newgear); Serial.print(" "); if (newgear==prevgear /*&& newgear==prev2gear*/){ currentgear=newgear; Serial.print(" S: "); Serial.print(newgear); Serial.print(" "); } prev2gear=prevgear; prevgear=newgear; } } } void clearlog(){ //clear the interrupt log for(int i=0; i<=maxlog; i++){ lflag[i]=' '; lmillis[i]=12345678; } ilog=0; logoflo=false; } void hcplex(byte digit){ //a charlieplex sequence that only lights half the time static byte flipflop=true; static byte prevdigit=99; if (digit!=prevdigit) flipflop=true; //make sure we display on changes in content if(flipflop){ cplex(digit); flipflop=false; } else { cplex(0); flipflop=true; } } void cplex(byte digit){ pinMode(cpin1,INPUT); pinMode(cpin2,INPUT); pinMode(cpin3,INPUT);//safe mode // digitalWrite(cpin1,HIGH);digitalWrite(cpin2,HIGH);digitalWrite(cpin3,HIGH); if (digit==5) { pinMode(cpin1,OUTPUT); digitalWrite(cpin1,HIGH); pinMode(cpin2,OUTPUT); digitalWrite(cpin2,LOW); } if (digit==4) { pinMode(cpin2,OUTPUT); digitalWrite(cpin2,HIGH); pinMode(cpin1,OUTPUT); digitalWrite(cpin1,LOW); } if (digit==3) { pinMode(cpin2,OUTPUT); digitalWrite(cpin2,HIGH); pinMode(cpin3,OUTPUT); digitalWrite(cpin3,LOW); } if (digit==2) { pinMode(cpin2,OUTPUT); digitalWrite(cpin2,LOW); pinMode(cpin3,OUTPUT); digitalWrite(cpin3,HIGH); } if (digit==1) { pinMode(cpin1,OUTPUT); digitalWrite(cpin1,LOW); pinMode(cpin3,OUTPUT); digitalWrite(cpin3,HIGH); } if (digit==6) { pinMode(cpin1,OUTPUT); digitalWrite(cpin1,HIGH); pinMode(cpin3,OUTPUT); digitalWrite(cpin3,LOW); } //Serial.print(digit,DEC); }