So here is the sketch I am working on. It's work in progress and has only been tested on the bench so far.
I am using a 555 based frequency generator to simulate the input pulse train. I do not know the R1 pulses per mile yet so I have set it at 4000 to make calculations and checking easy.
I have a gauge specific stepper motor on order and I am using the Switec library that was written specifically for that motor.
A few notes about the sketch;
1. My mph calculations might be off and I will continue to test once I have the stepper motor.
2. I am converting mph into feet per second and counting over a 1 second sample period to update the odometer function. There might be a better way to do this?
3. The odometer reading will be written to EEPROM when the car speed equals zero. A better method is using a power failure comparator to trigger write to EEPROM using a capacitor to hold the power on. - Anyone know how to do this or better still have a circuit diagram?
To test I have set the input frequency to simulate 60mph. Converting this value to seconds is therefore easy and allows me to check all the forumlas. For example, at 60mph the odometer should update the tenths of a mile every 6 seconds - it does!
/*
Stepper Motor Speedometer and OLED Odometer
Luke Hurst Nov 2015
http://retromini.co.uk
Credits - Thanks must go to the following from whom I have taken code and modified to my requirements.
Kevin Gale - For writing the main stepper motor speedometer sketch - https://github.com/Walterclark1/Stepper_Speedometer
Walterclark for the excellent write-up on how to use the base sketch written by Kevin Gale - http://www.hillclimb.org/forum/viewtopic.php?f=16&t=1133&sid=260e267564c31b08855a0e21ea57cbac
Guy Carpenter - Switec library author - https://github.com/clearwater/SwitecX25
PJRC for the FreqMeasure library - https://www.pjrc.com/teensy/td_libs_FreqMeasure.html
Trewjohn2001 for the Odometer code - http://www.mgexp.com/phorum/read.php?40,2761694
*/
//----Libraries to Include--------
#include <EEPROMex.h>
#include "SwitecX25.h"
#include "FreqMeasure.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//----End Libraries--------------
//----Define OLED Display Settings------
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
//-----End OLED Display Settings--------
//-----Variables-----------------------------------------------------------------------------------
const int UpdateInterval = 100; // 100 milliseconds speedo update rate
const double StepsPerDegree = 3.0; // Motor step is 1/3 of a degree of rotation
const unsigned int MaxMotorRotation = 315; // 315 max degrees of movement
const unsigned int MaxMotorSteps = MaxMotorRotation * StepsPerDegree;
const double PulsesPerMile = 4000.0; // Number of input pulses per mile
const double SecondsPerHour = 3600.0;
const int FeetPerMile = 5280; // Feet per mile conversion factor for Odometer update function
const double SpeedoDegreesPerMPH = 180.0 / 100.0; // Speed on face of dial at 180 degrees is 100mph.
unsigned long PreviousMillis = 0; // last time we updated the speedo
double MinMotorStep; // lowest step that will be used - calculated from update interval
unsigned long distsubtotal; // Running total of feet covered for Odometer update function
unsigned long lastmillis; // Last time we updated the Odometer
unsigned long FeetTravelled; // mph converted into feet travelled for Odometer function
double sum=0;
int count=0;
double avgPulseLength=0;
unsigned int motorStep = 0;
int noInputCount = 0;
float mph=0;
//----End Variables---------------------------------------------------------------------------------
//----Define Stepper motor library variables and pin outs-------------------------------------------
SwitecX25 Motor(MaxMotorSteps, 4,5,6,7); // Create the motor object with the maximum steps allowed
//------READ Stored Odometer Values From EEPROM-----
/* - Commented out during testing to avoid reading from EEPROM resulting in strange number formatting.
int disttenthsm = EEPROM.readInt(0);
int distm = EEPROM.readInt(5);
int distenm = EEPROM.readInt(10);
int disthundredm = EEPROM.readInt(15);
int disthousandm = EEPROM.readInt(20);
int disttenthousandm = EEPROM.readInt(25);
int disthundredthousandm = EEPROM.readInt(30);
*/
int disttenthsm;
int distm;
int distenm;
int disthundredm;
int disthousandm;
int disttenthousandm;
int disthundredthousandm;
//------End EEPROM Read-----------------------------
//---------------Start-up code run once-------------------------------------------------------------------
void setup(void)
{
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC); //Intialise OLED
pinMode(0, INPUT_PULLUP); //Define digital pinn 0 as pulse signal input
//-----Display SMITHS logo on start-up
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(28,30);
display.println("SMITHS");
display.display();
delay(2000);
display.clearDisplay();
display.display();
//-----End Logo-----------------------
Motor.zero(); //Initialize stepper at 0 location
lastmillis = (0); //Reset Odometer update interval to zero
MinMotorStep = PulseToStep(2 * (UpdateInterval / 1000.0) * F_CPU); //Force to zero when two intervals have passed with input
FreqMeasure.begin(); // Start freqmeasure library
}
//------End Start-up code----------------------------------------------------------------------------------------------------------
//------Start of Loop--------------------------------------------------------------------------------------------------------------
void loop() {
unsigned long currentMillis = millis();
// Update the motor position every UpdateInterval milliseconds
if (currentMillis - PreviousMillis >= UpdateInterval) {
PreviousMillis = currentMillis;
count = 0;
sum = 0;
// Read all the pulses available so we can average them
while (FreqMeasure.available()) {
sum += FreqMeasure.read();
count++;
}
if (count) {
// Average all the readings we got over our fixed time interval. This helps
// stabilize the speedo at higher speeds. The pulse length gets shorter and
// thus harder to measure accurately but we get more pulses to average.
// It may be necessary to update the FreqMeasure library to change the buffer
// length to hold the full number of pulses per update interval at the highest
// speedo values.
avgPulseLength = sum / count;
motorStep = PulseToStep(avgPulseLength);
noInputCount = 0;
}
else if (++noInputCount == 2) // force speed to zero after two missed intervals
motorStep = 0;
// Ignore speeds below the the two missed intervals speed so the motor doesn't jump
if (motorStep <= MinMotorStep)
motorStep = 0;
Motor.setPosition(motorStep);
}
// Always update the motor. It doesn't instantly go to the desired step so even if
// we didn't call setPosition the motor may still be moving to position from the last
// setPosition call.
Motor.update();
//-----------------Update Odometer Counter and Display every second -----------------------------------
mph = ((motorStep / 3)/SpeedoDegreesPerMPH); // Might be a better way of calculating mph based on input frequency
Serial.println(mph); // Used for testing to output speed in serial monitor.
if (millis() - lastmillis >= 1000){ //Uptade every second.
FeetTravelled = ((mph * FeetPerMile) / SecondsPerHour); // Convert mph into feet per second for running subtotal count.
distsubtotal += FeetTravelled; // add feet traveled to running subtotal
lastmillis = millis(); // Update lasmillis
updateodometer(); // Adds distance travelled to odometer
}
displayodometer(); // Calls displayodometer function
//---------------------------END ODOMETER-------------------------------------------------------------
//---------------Start EEPROM update if vehicle is stopped------------------
if (mph == 0){ // Write odomoter values to EEPROM when vehicle is at a stop. Consider replacing with power failure sense circuit to avoid excessive eeprom writes.
updateeeprom();
}
//--------------End EEPROM Update-------------------------------------------
}
//-------End of Loop--------------------------------------------------------------------------------------------------------
// The FreqMeasure gives us the pulse length in CPU cycles. This formula converts this into a motor step.
// Basically we are converting the length of the pulse in CPU cycles into pulses per second and then
// converting that into MPH, Once we have MPH that number is converted into degrees and that is then
// converted into a number of steps.
unsigned int PulseToStep(double pulseLength)
{
return (unsigned int)((F_CPU * SecondsPerHour * SpeedoDegreesPerMPH * StepsPerDegree) / (PulsesPerMile * pulseLength));
}
//-------Start of Functions-------------------------------------------------------------------------------------------------
void displayodometer() {
display.setTextSize(2);
display.setTextColor(WHITE);
display.clearDisplay();
//----------------Display ten thousand m units------------------------------------
display.setCursor(10,20);
display.println(disttenthousandm);
//----------------Display thousand m units------------------------------------
display.setCursor(25,20);
display.println(disthousandm);
//----------------Display hundreds m units------------------------------------
display.setCursor(40,20);
display.println(disthundredm);
//----------------Display tens m units----------------------------------------
display.setCursor(55,20);
display.println(distenm);
//----------------Display miles units---------------------------------------------
display.setCursor(70,20);
display.println(distm);
//----------------Display tenths m units---------------------------------------------
display.setCursor(85,20);
display.println(disttenthsm);
display.display();
}
//-------------------------Update Odomter Counts--------------------------------------------------------
void updateodometer(){
while (distsubtotal >= 528){
++disttenthsm;
distsubtotal = 0;
}
while (disttenthsm >9){
++distm;
disttenthsm = 0;
}
while (distm >9){
++distenm;
distm = 0;
}
while (distenm >9){
++disthundredm;
distenm = 0;
}
while (disthundredm >9){
++disthousandm;
disthundredm = 0;
}
while (disthousandm >9){
++disttenthousandm;
disthousandm = 0;
}
while (disttenthousandm >9){
++disthundredthousandm;
disttenthousandm = 0;
}
}
void updateeeprom(){ // Called by routine in main loop when vehicle at a stop. Update function only writes changes to eeprom if a bit has changed to avoid excessive data writes.
EEPROM.updateInt(0,disttenthsm);
EEPROM.updateInt(5,distm);
EEPROM.updateInt(10,distenm);
EEPROM.updateInt(15,disthundredm);
EEPROM.updateInt(20,disthousandm);
EEPROM.updateInt(25,disttenthousandm);
EEPROM.updateInt(30,disthundredthousandm);
}
Edited by LukeH, 10 November 2015 - 04:30 PM.