From b1dad948b78254c0cd1bce517a5c00d7102340e6 Mon Sep 17 00:00:00 2001 From: Steve Howes Date: Fri, 19 Jul 2019 10:22:20 +0100 Subject: [PATCH] Code --- README.md | 27 ++++ speedo/speedo.ino | 363 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 390 insertions(+) create mode 100644 speedo/speedo.ino diff --git a/README.md b/README.md index f65e148..92d73d0 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ # MX5-Arduino-Speed + +Target Hardware: +Arduino ATMega328p +2.42 Inch SS1309 128x64 Monocrhome OLED +5v Beeper +5v Speed sensor (#DSSA from www.carbuildersolutions.com) + +Pinout: +OLED_MOSI 9 +OLED_CLK 10 +OLED_DC 11 +OLED_CS 12 +OLED_RESET 13 +Beeper 2 (and to GND) +Speed sesor 4 (and 5v, GND) + +Configuration via USB, 115200. Commands are: + +ODO - Press return and then enter new odometer reading +PPM - Press return and enter pulses per half mile (use CAL mode in preference) +CAL - Enter calibration mode (see below) +RUN - Exit calibration mode + +Calibration mode: + +In calibration mode it starts counting pulses immediately. Calibration should be done over a measured half a mile. Press enter when starting the half mile, and again at the end. The number of pulses will be displayed on the screen. To write this to EEPROM and return to normal mode type 'RUN'. If the data is not suitable (missed marker etc) then just repeat the measurement. + diff --git a/speedo/speedo.ino b/speedo/speedo.ino new file mode 100644 index 0000000..f1fa87e --- /dev/null +++ b/speedo/speedo.ino @@ -0,0 +1,363 @@ + +#include +#include +#include + +//Define SPI Connections: +#define OLED_MOSI 9 +#define OLED_CLK 10 +#define OLED_DC 11 +#define OLED_CS 12 +#define OLED_RESET 13 + +#define LOGO_HEIGHT 64 +#define LOGO_WIDTH 64 +const unsigned char PROGMEM logo_bmp [] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF0, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x37, 0xE0, 0x07, 0xE8, 0x00, 0x00, + 0x00, 0x00, 0xF7, 0xC0, 0x07, 0xEE, 0x00, 0x00, 0x00, 0x01, 0xEF, 0x80, 0x03, 0xEF, 0x80, 0x00, + 0x00, 0x03, 0xEF, 0x00, 0x01, 0xF7, 0xC0, 0x00, 0x00, 0x07, 0xCF, 0xC0, 0x03, 0xF7, 0xC0, 0x00, + 0x00, 0x07, 0xC7, 0xF8, 0x1F, 0xE3, 0xE0, 0x00, 0x00, 0x17, 0x87, 0xFE, 0x7F, 0xE3, 0xEC, 0x00, + 0x00, 0x37, 0x83, 0xFE, 0x7F, 0xC1, 0xEC, 0x00, 0x00, 0x77, 0xF9, 0xF0, 0x1F, 0xBF, 0xEC, 0x00, + 0x00, 0xF7, 0xFE, 0x00, 0x00, 0xFF, 0xCE, 0x00, 0x00, 0xF3, 0xFE, 0x00, 0x00, 0x7F, 0xCF, 0x00, + 0x00, 0xF1, 0xF8, 0x00, 0x00, 0x3F, 0x0F, 0x00, 0x00, 0xF0, 0xC0, 0x00, 0x00, 0x02, 0x07, 0x00, + 0x03, 0xFF, 0xC0, 0x00, 0x00, 0x03, 0xFF, 0xC0, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x03, 0xFE, 0xC0, + 0x07, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFE, 0xE0, 0x0F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE0, + 0x0F, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x39, 0xF0, 0x0F, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x39, 0xF0, + 0x0F, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xF0, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, + 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, + 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xCE, 0xF3, 0xBC, 0x7D, 0xFD, 0xC7, 0x7F, 0xDF, 0xDE, 0xE3, 0xBE, 0xFB, 0xFF, 0xC7, + 0x7F, 0xFB, 0xDE, 0xE3, 0xFF, 0xFB, 0xFF, 0xCF, 0x1E, 0x3B, 0xDD, 0xE7, 0xFF, 0xFB, 0x9F, 0xFF, + 0x1E, 0x3F, 0xDD, 0xE7, 0xFF, 0xFB, 0xFF, 0xFE, 0x1C, 0x3F, 0xBD, 0xC7, 0xFF, 0xF7, 0xFF, 0xFE, + 0x1C, 0x7F, 0x3D, 0xC7, 0xFF, 0xF7, 0xF7, 0x9E, 0x3C, 0x77, 0x39, 0xCF, 0xEF, 0xF7, 0x07, 0x1E, + 0x3C, 0x77, 0xB9, 0xFE, 0xEF, 0xEF, 0x07, 0x1C, 0x38, 0x77, 0xF9, 0xFE, 0xEE, 0xEF, 0x07, 0x1C, + 0x38, 0x73, 0xF8, 0x78, 0xEE, 0xEE, 0x0F, 0x1C, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, + 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x1C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x78, + 0x1F, 0x38, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xF8, 0x1F, 0xBA, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, + 0x0F, 0xFB, 0x00, 0x00, 0x00, 0x01, 0xDF, 0xF0, 0x0F, 0xF3, 0x80, 0x00, 0x00, 0x03, 0xCF, 0xE0, + 0x03, 0xE3, 0xC0, 0x00, 0x00, 0x03, 0xC7, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0x00, 0xE3, 0xD8, 0x00, 0x00, 0x1B, 0xC7, 0x00, 0x00, 0xFB, 0xDE, 0x00, 0x00, 0x7B, 0xDF, 0x00, + 0x00, 0xFF, 0xDF, 0x00, 0x00, 0xF3, 0xFE, 0x00, 0x00, 0x7F, 0x8F, 0x70, 0x0F, 0xF3, 0xFC, 0x00, + 0x00, 0x3F, 0x8F, 0xFC, 0x7F, 0xF1, 0xFC, 0x00, 0x00, 0x1F, 0x0F, 0xBE, 0xFD, 0xF0, 0xFC, 0x00, + 0x00, 0x00, 0x0F, 0xBF, 0xF9, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x9F, 0xF9, 0xFF, 0xC0, 0x00, + 0x00, 0x03, 0xFF, 0x0F, 0xF1, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFF, 0x07, 0xE0, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFE, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x00 +}; + + +Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); + +// Boot graphics and screen refresh +int prettylinecount = 64; +byte screenrefresh = 0; + +byte mode = 0; //(0 = bootlogo, 1 = running, 2 = calibrate) + +// Pulse counting / pin state +unsigned long pulsecount = 0; +byte lastspeedstate = 0; +bytespeedstate = 0; + +// Odometer and current mile pulses +unsigned long odometer = 0; +unsigned long currentmile = 0; + +// Calibration settings +unsigned long pulsepermile = 0; +byte calmultiplier = 2; + +void bootlogo(void) +{ + display.clearDisplay(); + display.drawBitmap((display.width() - LOGO_WIDTH ) / 2, (display.height() - LOGO_HEIGHT) / 2, logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +void prettyline() +{ + display.clearDisplay(); + display.drawLine(prettylinecount, 45, 128 - prettylinecount, 45, WHITE); + display.display(); + prettylinecount--; + prettylinecount--; + +} + +void setup() +{ + Serial.begin(115200); + Serial.setTimeout(10); + Serial.println(); + Serial.println(); + Serial.println("---- stevehowes/MX5-Arduino-Speed ----"); + + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Initialize with the I2C address 0x3C. + + // Beeper + pinMode(2, OUTPUT); + + // Speed Input + pinMode(4, INPUT); + + + // Do wanky Triumph Logo + bootlogo(); + + // POST beep + digitalWrite(2, HIGH); + delay(50); + digitalWrite(2, LOW); + + // Get calibrated data and odometer from EEPROM + EEPROM.get(0, pulsepermile); + EEPROM.get(4, odometer); + + // Output debug data + Serial.print("PPM Calibration: "); + Serial.println(pulsepermile); + Serial.print("Start Odometer: "); + Serial.println(odometer); + Serial.print("Calibration multiplier: "); + Serial.print(calmultiplier); + Serial.print(" ("); + Serial.print(1.0/calmultiplier); + Serial.println(" miles)"); + +} + + +void loop() +{ + if (mode == 0) // Boot mode + { + // Boot styling, when it expires default to run + if (prettylinecount > 0) + { + prettyline(); + return; + } else { + mode = 1; + } + } + else if (mode == 1) // Run mode + { + // If we have serial + if (Serial.available() > 0) + { + String input = Serial.readString(); + // See if it's a cal request + if (input == "CAL\n") + { + // Change to calibrate mode + mode = 2; + + // Notify user + Serial.println("---- Calibration Mode ----"); + digitalWrite(2, HIGH); + delay(500); + digitalWrite(2, LOW); + + // Draw the calibration screen + screenrefresh = 1; + + } + else if (input == "PPM\n") + { + // Notify user + Serial.print("Enter PPM: "); + digitalWrite(2, HIGH); + delay(50); + digitalWrite(2, LOW); + + while (Serial.available() <= 0) + {} + + pulsepermile = Serial.parseInt(); + Serial.println(pulsepermile); + EEPROM.put(0, pulsepermile); + Serial.print(pulsepermile); + Serial.println(" written to EEPROM at address 0"); + } + else if (input == "ODO\n") + { + // Notify user + Serial.print("Enter ODO: "); + digitalWrite(2, HIGH); + delay(50); + digitalWrite(2, LOW); + + while (Serial.available() <= 0) + {} + + odometer = Serial.parseInt(); + Serial.println(odometer); + EEPROM.put(4, odometer); + Serial.print(odometer); + Serial.println(" written to EEPROM at address 4"); + } + } + else + { + runmode(); + } + } + else if (mode == 2) // Calibration mode + { + calmode(); + } +} + +void calmode() +{ + // If we're re-drawing the screen + if (screenrefresh > 0) + { + // Clear display and re-draw our dividing line + display.clearDisplay(); + display.drawLine(0, 45, 127, 45, WHITE); + + // Display our last pulse count + display.setTextColor(WHITE); + display.setCursor(10, 10); + display.setTextSize(2); + display.println(pulsecount); + + // Mode + display.setTextSize(1); + display.setCursor(70, 48); + display.println("Calibrate"); + + // Draw it and clear flag + display.display(); + screenrefresh = 0; + + // Save our count (will be multipied up later) + pulsepermile = pulsecount; + + // Reset counter + pulsecount = 0; + } + + // until we get serial input, count as quickly as possible + while (Serial.available() <= 0) + { + // Read pin + speedstate = digitalRead(4); + + // If it has changed + if (speedstate != lastspeedstate) + { + // If we have gone high + if (speedstate == 1) + { + pulsecount++; + } + } + + // Record recent state + lastspeedstate = speedstate; + } + + + if (Serial.readString() == "RUN\n") + { + // Calibrate over distances other than one mile (half mile sensible) + pulsepermile = pulsepermile * calmultiplier; + EEPROM.put(0, pulsepermile); + Serial.print(pulsepermile); + Serial.println(" written to EEPROM at address 0"); + mode = 1; + screenrefresh = 1; + } else { + screenrefresh = 1; + } +} + + +void runmode() +{ + screenrefresh = 1; + + // If we're re-drawing the screen + if (screenrefresh > 0) + { + // Used for any strings sent to screen + char buf[16]; + + // Clear display and re-draw our dividing line + display.clearDisplay(); + display.drawLine(0, 45, 127, 45, WHITE); + + // Display our Speed, offset differently if > 100 (don't routely show third digit) + display.setTextColor(WHITE); + if((pulsecount * 2 * 3600 / pulsepermile) > 100) + { + display.setCursor(20, 10); + display.setTextSize(4); + sprintf(buf,"%03d",(pulsecount * 2 * 3600 / pulsepermile)); + }else{ + display.setCursor(42, 10); + display.setTextSize(4); + sprintf(buf,"%02d",(pulsecount * 2 * 3600 / pulsepermile)); + } + display.println(buf); + display.setTextSize(1); + display.setCursor(90, 32); + display.println("mph"); + + // Display odometer as well + display.setCursor(5, 48); + display.setTextSize(1); + sprintf(buf,"%06d miles",odometer); + display.println(buf); + + // Draw it and clear flag + display.display(); + screenrefresh = 0; + } + + // Add measured pulses to current mile + currentmile = currentmile + pulsecount; + + // If we have completed a mile + if (currentmile > pulsepermile) + { + // Bump up odometer + odometer++; + + // Take away a mile + currentmile = currentmile - pulsepermile; + + // Save to EEPROM + EEPROM.put(4, odometer); + } + + // Reset count + pulsecount = 0; + + // Take samples for half a second + unsigned long timestamp = micros(); + while (micros() < (timestamp + 500000)) + { + // Read pin + speedstate = digitalRead(4); + + // If it has changed + if (speedstate != lastspeedstate) + { + // If we have gone high + if (speedstate == 1) + { + pulsecount++; + } + } + + // Record recent state + lastspeedstate = speedstate; + } +} +