/************************************************************************* * Sample sketch based on OBD-II library for Arduino * Using a LCD4884 shield to display realtime vehicle data * Distributed under GPL v2.0 * Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> * All rights reserved. *************************************************************************/ #include <Arduino.h> #include <Wire.h> #include <OBD.h> #include "LCD4884.h" //keypad debounce parameter #define DEBOUNCE_MAX 15 #define DEBOUNCE_ON 10 #define DEBOUNCE_OFF 3 #define NUM_KEYS 5 #define NUM_MODES 3 // joystick number #define LEFT_KEY 0 #define CENTER_KEY 1 #define DOWN_KEY 2 #define RIGHT_KEY 3 #define UP_KEY 4 int adc_key_val[5] ={ 50, 200, 400, 600, 800 }; // debounce counters byte button_count[NUM_KEYS]; // button status - pressed/released byte button_status[NUM_KEYS]; // button on flags for user program byte button_flag[NUM_KEYS]; // initial gauge mode char mode = 0; // The followinging are interrupt-driven keypad reading functions // which includes DEBOUNCE ON/OFF mechanism, and continuous pressing detection // Convert ADC value to key number char get_key(unsigned int input) { char k; for (k = 0; k < NUM_KEYS; k++) { if (input < adc_key_val[k]) { return k; } } if (k >= NUM_KEYS) k = -1; // No valid key pressed return k; } void update_adc_key(){ int adc_key_in; char key_in; byte i; adc_key_in = analogRead(0); key_in = get_key(adc_key_in); for(i=0; i<NUM_KEYS; i++) { if(key_in==i) //one key is pressed { if(button_count[i]<DEBOUNCE_MAX) { button_count[i]++; if(button_count[i]>DEBOUNCE_ON) { if(button_status[i] == 0) { button_flag[i] = 1; button_status[i] = 1; //button debounced to 'pressed' status } } } } else // no button pressed { if (button_count[i] >0) { button_flag[i] = 0; button_count[i]--; if(button_count[i]<DEBOUNCE_OFF){ button_status[i]=0; //button debounced to 'released' status } } } } } void ShowProgressBarV(byte x, byte y, byte val /* 0~10 */) { byte j = 10 - val; for (char y1 = j >> 1; y1 >= 0; y1--) { lcd.LCD_set_XY(x, y1 + y); for (byte x = 0; x < 14; x++) { lcd.LCD_write_byte(0, 1); } } if (j & 1 == 1) { j >>= 1; lcd.LCD_set_XY(x, y + j); for (byte x = 0; x < 14; x++) { lcd.LCD_write_byte(0xE0, 1); } j++; } else { j >>= 1; } for (byte y1 = j; y1 <= 5; y1++) { lcd.LCD_set_XY(x, y1 + y); for (byte x = 0; x < 14; x++) { lcd.LCD_write_byte(0xEE, 1); } } } byte CheckPressedKey() { for(int i=0; i<NUM_KEYS; i++){ if(button_flag[i] !=0){ button_flag[i]=0; // reset button flag return i; } } return -1; } // waiting for center key press void waitfor_OKkey(){ byte i; byte key = 0xFF; update_adc_key(); while (key!= CENTER_KEY){ for(i=0; i<NUM_KEYS; i++){ if(button_flag[i] !=0){ button_flag[i]=0; // reset button flag if(i== CENTER_KEY) key=CENTER_KEY; } } } } class COBDDash : public COBD { public: void Connect() { int value; lcd.LCD_clear(); lcd.LCD_write_string(0, 0, "Connecting..", MENU_NORMAL); for (int n = 0; !init(); n++) { lcd.LCD_putchar('.'); if (n == 3) lcd.backlight(OFF); } lcd.backlight(ON); //Turn on the backlight lcd.LCD_clear(); lcd.LCD_write_string(0, 0, "Connected!", MENU_NORMAL); lcd.LCD_write_string(0, 1, "Wait ECU start", MENU_NORMAL); do { delay(1000); } while (!read(PID_RPM, value)); lcd.LCD_write_string(0, 2, "ECU started ", MENU_NORMAL); lcd.LCD_write_string(0, 3, "Wait ignition ", MENU_NORMAL); do { delay(100); } while (!read(PID_RPM, value) || value == 0); lcd.LCD_write_string(0, 4, "Engine started", MENU_NORMAL); delay(1000); } void Loop() { unsigned long lastTime = millis(); Connect(); byte count = 0; byte key; DisplayBG(mode); dataMode = 1; lcd.backlight(ON); for (;;) { update_adc_key(); key = CheckPressedKey(); if (key != -1) { switch (key) { case CENTER_KEY: mode = (mode + 1) % NUM_MODES; DisplayBG(mode); count = 0; break; case LEFT_KEY: if (mode > 0) { mode--; DisplayBG(mode); count = 0; } break; case RIGHT_KEY: if (mode < NUM_MODES - 1) { mode++; DisplayBG(mode); count = 0; } break; case UP_KEY: lcd.backlight(ON); break; case DOWN_KEY: lcd.backlight(OFF); break; } } if (millis() - lastTime < 250) { continue; } lastTime = millis(); switch (mode) { case 0: DisplayData1(); break; case 1: DisplayData2(); switch (count) { case 0: DisplayData21(); break; case 5: DisplayData22(); break; case 10: DisplayData23(); break; } break; case 2: DisplayData3(); break; } if (errors > 5) { lcd.backlight(OFF); return; } count++; } } private: void DisplayData1() { if (read(PID_RPM, value)) { ShowRPM(value); } if (read(PID_SPEED, value)) { ShowSpeed(value); } if (read(PID_ENGINE_LOAD, value)) { ShowEngineLoad(value); } } void DisplayData2() { if (read(PID_RPM, value)) { ShowRPM(value); } if (read(PID_SPEED, value)) { ShowSpeed2(value); } } void DisplayData21() { if (read(PID_COOLANT_TEMP, value)) { ShowTemperature(value, 42, 3); } } void DisplayData22() { if (read(PID_INTAKE_TEMP, value)) { ShowTemperature(value, 42, 4); } } void DisplayData23() { if (read(PID_AMBIENT_TEMP, value)) { ShowTemperature(value, 42, 5); } } void DisplayData3() { if (read(PID_SPEED, value)) { ShowSpeed2(value); } if (read(PID_INTAKE_MAP, value)) { char buf[8]; sprintf(buf, "%3u", value); lcd.LCD_write_string(24, 4, buf, MENU_NORMAL); int boost = (value - 101); if (boost < 0) boost = 0; sprintf(buf, "%d.%02d", boost / 100, boost % 100); lcd.LCD_write_string_big(0, 0, buf, MENU_NORMAL); } if (read(PID_FUEL_PRESSURE, value)) { char buf[8]; sprintf(buf, "%3u", value); lcd.LCD_write_string(24, 5, buf, MENU_NORMAL); } } void ShowEngineLoad(uint8_t value) { ShowProgressBarV(70, 1, value / 10); lcd.LCD_write_string(78, 1, "%", MENU_NORMAL); } void ShowRPM(int value) { char buf[15]; if (value <= 9999) { sprintf(buf, "%4u", value); lcd.LCD_write_string_big(0, 0, buf, MENU_NORMAL); lcd.LCD_write_string(48, 2, "R", MENU_NORMAL); } } void ShowSpeed(uint8_t value) { char buf[8]; sprintf(buf, "%3u", value); lcd.LCD_write_string_big(6, 3, buf, MENU_NORMAL); lcd.LCD_write_string(42, 5, "k", MENU_NORMAL); } void ShowSpeed2(uint8_t value) { char buf[8]; ShowProgressBarV(70, 1, value / 25); sprintf(buf, "%3u", value); lcd.LCD_write_string(66, 0, buf, MENU_NORMAL); lcd.LCD_write_string(66, 1, "kph", MENU_NORMAL); } void ShowTemperature(uint8_t value, byte x, byte y) { char buf[8]; sprintf(buf, "%3d", value); lcd.LCD_write_string(x, y, buf, MENU_NORMAL); } void DisplayBG(char mode) { lcd.LCD_clear(); switch (mode) { case 0: lcd.LCD_write_string(48, 2, "RPM", MENU_NORMAL); lcd.LCD_write_string(42, 5, "kph", MENU_NORMAL); lcd.LCD_write_string(66, 0, "ENG", MENU_NORMAL); break; case 1: lcd.LCD_write_string(48, 2, "RPM", MENU_NORMAL); lcd.LCD_write_string(0, 3, "COOLANT C", MENU_NORMAL); lcd.LCD_write_string(0, 4, "INTAKE C", MENU_NORMAL); lcd.LCD_write_string(0, 5, "AMBIENT C", MENU_NORMAL); break; case 2: lcd.LCD_write_string(48, 2, "bar", MENU_NORMAL); lcd.LCD_write_string(0, 3, "PRESSURES", MENU_NORMAL); lcd.LCD_write_string(0, 4, "AIR kpa", MENU_NORMAL); lcd.LCD_write_string(0, 5, "FUEL kpa", MENU_NORMAL); break; } } #ifdef USE_SOFTSERIAL // override data communication functions bool DataAvailable() { return mySerial.available(); } char ReadData() { char c = mySerial.read(); Serial.write(c); return c; } void WriteData(const char* s) { mySerial.write(s); } void WriteData(const char c) { mySerial.write(c); } #endif char displayMode; int value; }; COBDDash obd; void loop() { obd.Loop(); } void setup() { // setup interrupt-driven keypad arrays // reset button arrays for(byte i=0; i<NUM_KEYS; i++){ button_count[i]=0; button_status[i]=0; button_flag[i]=0; } #ifdef __AVR_ATmega32U4__ // Setup timer2 -- Prescaler/256 TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~(1<<WGM22); TCCR2B = (1<<CS22)|(1<<CS21); ASSR |=(0<<AS2); // Use normal mode TCCR2A =0; //Timer2 Overflow Interrupt Enable TIMSK2 |= (0<<OCIE2A); TCNT2=0x6; // counting starts from 6; TIMSK2 = (1<<TOIE2); SREG|=1<<SREG_I; #endif lcd.LCD_init(); lcd.LCD_clear(); lcd.backlight(ON); // Turn on the backlight pinMode(13, OUTPUT); obd.begin(); } // Timer2 interrupt routine - // 1/(160000000/256/(256-6)) = 4ms interval #ifdef __AVR_ATmega32U4__ ISR(TIMER2_OVF_vect) { TCNT2 = 6; update_adc_key(); } #endif