#include <LiquidCrystal.h>/* * Boost Gauge project for my Nissan Stagea * To Do: * 1. Fix the boost bar graph scaling to re-read the values after its been changed in the menu - DONE * 2. Move the alarm code and all the calculation code out of the display printing routine. - DONE * - Perhaps move the display printing into a suborutine? - LATER? * 3. Read/Write EEPROM for saving etc variable. Make sure to handle the case where there is no data in EEPROM. * - http://arduino.cc/playground/Code/EEPROMWriteAnything * 3. Think of a better way to do the menus and tidy things up. Code is pretty nasty right now. * 4. Add a peak value feature with adjustable timeout. * 5. Fix display errors when graph scale is set low and boost significantly higher causes it to wrap to the first line. *//* *LCD Pinout Cable Pin header Arduino Pin *1 - GND 1 *2 - +5V 2 *3 - Contrast 3 *4 - RS 4 2 *5 - RW 5 3 *6 - E(Enable) 6 4 *7 - DB0 *8 - DB1 *9 - DB2 *10 - DB3 *11 - DB4 7 5 *12 - DB5 8 6 *13 - DB6 9 7 *14 - DB7 10 8 *15 - Backlight +5V 11 NC *16 - Backlight GND 12 NC */// LiquidCrystal lcd(rs, rw, enable, d4, d5, d6, d7) - 4 bit modeLiquidCrystal lcd(2, 3, 4, 5, 6, 7, 8);// LiquidCrystal lcd(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7) - 8 bit mode//LiquidCrystal lcd(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);// Analog 0-5v Sensor inword Analog1pin = A5;int Analog1Raw = 0;int Analog1mV = 0;float Analog1AFR = 0;// Shift Regsiter pins for Button Input#define ShiftData 9#define ShiftClock 10#define ShiftLatch 11// Other pins#define BuzzerPin 12// Stored config#define CONFIG_START 0#define CONFIG_VERSION stageav1/* -- This shit doesnt work....struct ConfigStruct { int BoostMin; // BoostMin for graph Scaling int BoostMax; // BoostMax for graph Scaling int BoostAlarm;// Alarm for audible/visual boost warning};ConfigStruct Config = { 0 , 0 , 0 };Config.BoostMin = 0;Config.BootMax = 500;Config.BoostAlarm = 300;*/// Variable for buttonsunsigned int buttonScanInterval = 100; // Interval between checking for button pressunsigned long buttonScanLastms = 0; // Last time we lookedbyte ButtonBits = 255; // B11111111unsigned int ButtonRepeatDelay = 200; // 300ms delay for button repeat unsigned long ButtonRepeatLastms = 0; // Last time a button was pressed// Variables for tracking menu states :: 0 - Run (no menu), 1 - Display Menubyte MenuState = 0;byte SubMenu = 0; // Used to track what SubMenu we're inunsigned int MenuTimeout = 10000; // 10 second timeout for menus// Text display timer variablesunsigned int timerTextInterval = 100; // interval between sensor data updatesunsigned long timerTextLastms = 0; // Last time we looked// mV for boost min and max. This is used for the bar graphint BoostMin = 0;int BoostMax = 500; // 17 psiint BoostAlarm = 300; // 10 psi// Bar Graph magicunsigned int timerBarGraphInterval = 10; // interval between bar graph updatesunsigned long timerBarGraphLastms = 0; // Last time we lookedint LCDWidth = 16; // LCD Display width in charsint LCDPixelWidth = 5; // Each char is 5 pixels wideint LCDBars = LCDWidth * LCDPixelWidth; // This is the number of pixels we have to useint mVPerBar = BoostMax / LCDBars; // Bar graph magic calculationsint mVPerChar = mVPerBar * LCDPixelWidth; //mV per full charint BarGraphValue = 0; // This variable is used further down when printing the bar graphint BarGraphWritten = 0; // This variable is used to track how much whitespace we need to print after the bar to clear the display.const float kpa2psi = 0.145037738;void setup(){// lcd.begin(24, 4); lcd.begin(16, 2); // Setup our custom chars for the bar graph byte bar1[8] = { B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000, }; lcd.createChar(1, bar1); byte bar2[8] = { B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000, }; lcd.createChar(2, bar2); byte bar3[8] = { B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100, }; lcd.createChar(3, bar3); byte bar4[8] = { B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110, }; lcd.createChar(4, bar4); byte bar5[8] = { B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111, }; lcd.createChar(5, bar5); byte leftArrow[8] = { B00010, B00110, B01111, B11111, B01111, B00110, B00010, }; lcd.createChar(6, leftArrow); byte rightArrow[8] = { B01000, B01100, B11110, B11111, B11110, B01100, B01000, }; lcd.createChar(7, rightArrow); // Smiley - just for fun byte smile[8] = { B10001, B00000, B00100, B01100, B00000, B10001, B01110, }; lcd.createChar(8, smile); lcd.begin(16, 2); Serial.begin(9600); // Set Pin modes pinMode(BuzzerPin, OUTPUT); // alarm buzzer digitalWrite(BuzzerPin, LOW); // Initially, off. pinMode (Analog1pin, INPUT); // boost sensor in // Set Shift Register Pin Modes pinMode(ShiftData, INPUT); pinMode(ShiftClock, OUTPUT); pinMode(ShiftLatch, OUTPUT); lcd.clear(); lcd.setCursor(0,0); lcd.print ("Boost Gauge..."); delay(500); lcd.clear();}void loop(){ // Get the current ms value to compare timers with. unsigned long currentMillis = millis(); ////////////////////////// // Button Input/Menus // ////////////////////////// // Scan for button presses at the defined interval if (currentMillis - buttonScanLastms > buttonScanInterval) { buttonScanLastms = currentMillis; // Reset to "all-on" // ButtonBits = 255; // B11111111 // Reset to "all-off" ButtonBits = 0; // Delay for key repeat - Delay the rescan if we've had a keypress detected within the ButtonRepeat time. if (currentMillis - ButtonRepeatLastms > ButtonRepeatDelay) { /* Using shift register example from http://arduino.cc/en/Tutorial/ShftIn12 */ //Pulse the latch pin high to collect parallel data. digitalWrite(ShiftLatch,1); delayMicroseconds(20); //Drop the latch low to start shifting data into the MCU serially. digitalWrite(ShiftLatch,0); // Collect the data into a byte var. ButtonBits = shiftIn(ShiftData, ShiftClock, MSBFIRST); // Serial.println(ButtonBits, BIN); } if (ButtonBits == B00000000) // No buttons pressed { // We can use this section for timeout code if necessary later. } else if (ButtonBits == B10000000) // Escape (Top Left) { ButtonRepeatLastms = currentMillis; // If we're not in a Menu, set the MenuState, and then print the main menu. if (MenuState == 0) { MenuState = 1; // Set this flag to say we're in a menu SubMenu = 0; // Reset this to be main menu WriteMenu(SubMenu); } // If we're in a menu, set MenuState to 0 and clear the display in preperation for sensor view. else if (MenuState > 0) { MenuState = 0; // Set this flag to say we're not in a menu lcd.clear(); } } else if (ButtonBits == B01000000) // Up (Top Middle) { ButtonRepeatLastms = currentMillis; // If We're in a Menu if (MenuState > 0) { // And we're in Menu 1 (Graph MaxBoost) if (SubMenu == 1) { BoostMax = BoostMax + 5; mVPerBar = BoostMax / LCDBars; // Bar graph magic calculations mVPerChar = mVPerBar * LCDPixelWidth; //mV per full char } // And we're in Menu 2 (Boost Alarm) else if (SubMenu == 2) { BoostAlarm = BoostAlarm + 5; } WriteMenu(SubMenu); } } else if (ButtonBits == B00100000) // Enter (Top Right) { ButtonRepeatLastms = currentMillis; } else if (ButtonBits == B00010000) // Not used { ButtonRepeatLastms = currentMillis; } else if (ButtonBits == B00001000) // Right (Bottom Right) { ButtonRepeatLastms = currentMillis; // If we're in a menu, Increment the SubMenu variable and print the menu. if (MenuState > 0) { SubMenu++; WriteMenu(SubMenu); } } else if (ButtonBits == B00000100) // Down (Bottom Middle) { ButtonRepeatLastms = currentMillis; // If We're in a Menu if (MenuState > 0) { // And we're in Menu 1 (Graph MaxBoost) if (SubMenu == 1) { BoostMax = BoostMax - 5; mVPerBar = BoostMax / LCDBars; // Bar graph magic calculations mVPerChar = mVPerBar * LCDPixelWidth; //mV per full char } // And we're in Menu 2 (Boost Alarm) else if (SubMenu == 2) { BoostAlarm = BoostAlarm - 5; } WriteMenu(SubMenu); } } else if (ButtonBits == B00000010) //Left (Bottom Left) { ButtonRepeatLastms = currentMillis; // If we're in a menu, Decrement the SubMenu variable and print the menu. if (MenuState > 0) { SubMenu--; WriteMenu(SubMenu); } } else if (ButtonBits == B00000001) // Not Used { ButtonRepeatLastms = currentMillis; } else { // Do Nothing if some idiot presses more than one button at a time. } } ////////////////////////////////////// // Bar Graph / Alarm // ///////////////////////////////////// if (currentMillis - timerBarGraphLastms > timerBarGraphInterval) { timerBarGraphLastms = currentMillis; //Read our sensor "Analog1" Analog1Raw = analogRead(Analog1pin); // Turn the alarm on or off. if (Analog1Raw < BoostAlarm) digitalWrite(12, LOW); // Everything is fine... if (Analog1Raw >= BoostAlarm) digitalWrite(12, HIGH); // Alarm!! // We only want to print the bar graph stuff if we're not in a menu. if (MenuState == 0) { lcd.setCursor (0,1); BarGraphValue = Analog1Raw; BarGraphWritten = 0; // To speed bar graph drawing, each full char is equivelent to 1/16th of the scale // Print a full char for every full 1/16th of the scale and then print out a single part char for the remainder. while (BarGraphValue > mVPerChar) { lcd.write(5); // Write full blocks each representing 1x mvPerChar. BarGraphValue = BarGraphValue - mVPerChar; // Drop the remaining BarGraphValue by what we've printed BarGraphWritten++; // Increment the char printed counter } // Print the appropriate 'part char' representing the remainder BarGraphValue. if (BarGraphValue > ( 3 * mVPerBar)) { lcd.write(4); } else if (BarGraphValue > ( 2 * mVPerBar)) { lcd.write(3); } else if (BarGraphValue > ( 1 * mVPerBar)) { lcd.write(2); } else if (BarGraphValue > ( 0 * mVPerBar)) { lcd.write(1); } BarGraphWritten++; //Increment the chars printed counter. // Print whitespace to the end of the line. Always prints over whats there. May be inefficient on LCD IO. while ((BarGraphWritten - LCDWidth) < 0) { lcd.write(" "); BarGraphWritten++; } } } //if (currentMillis - timerBarGraphLastms > timerBarGraphInterval) ///////////////////////////// // Text Display // ///////////////////////////// if (currentMillis - timerTextLastms > timerTextInterval && MenuState == 0 ) { timerTextLastms = currentMillis; //Debugging - Print the pin Voltage. 0-5000 /* lcd.setCursor(12,0); if (Analog1mV<1000) lcd.print(' '); if (Analog1mV<100) lcd.print(' '); if (Analog1mV<10) lcd.print(' '); lcd.print(Analog1mV); */ // Map it to mV. //Analog1mV = map(Analog1Raw, 0, 1023, 0, 5000); //psi=(0.04+((Analog1mV/1000)/(0.004*5)))*kpa2psi // float Analog1mVx = Analog1mV; //float Analog1Psi = (0.04+((Analog1mVx/1000)/(0.004*5)))*kpa2psi; float Analog1Psi = Raw2PSI(Analog1Raw); lcd.setCursor(0,0); if (Analog1Psi<10) lcd.print(' '); lcd.print (Analog1Psi); lcd.print ("psi"); } // if (currentMillis - timerTextLastms > timerTextInterval) }// Self explanitoryvoid WriteMenu (int menuID) { // Clear the screen ready for menus lcd.clear(); if (menuID == 0) { lcd.setCursor(0,0); lcd.print("< Main Menu >"); lcd.setCursor(0,1); lcd.print("Esc | Enter=Save"); } else if (menuID == 1) { lcd.setCursor(0,0); lcd.print("<Graph MaxBoost>"); lcd.setCursor(0,1); lcd.print(Raw2PSI(BoostMax)); lcd.print ("psi"); } else if (menuID == 2) { lcd.setCursor(0,0); lcd.print("< Boost Alarm >"); lcd.setCursor(0,1); lcd.print(Raw2PSI(BoostAlarm)); lcd.print ("psi"); } // Unknown Menu ID.. oh noes! else { lcd.setCursor(0,0); lcd.print("< Undef Menu >"); lcd.setCursor(0,1); lcd.print("menuID: "); lcd.print(menuID); }}float Raw2PSI(int value){ float result; result = map(value, 0, 1023, 0, 5000); result = (0.04+((result/1000)/(0.004*5)))*kpa2psi; return result;}/* void padding( int number, byte width ) { int currentMax = 10; char returnString[width]; for (byte i = 0; i < width-1; i++){ if (number < currentMax) { returnString[i]=("0"); } currentMax *= 10; } // ToDo: Loop over the rest of the string, add to returnString and return Serial.print(number);} */