Dune buggy DAQ/ performance feedback

This project has already been fairly well defined in previous years but no one has take the time to perfect it.

Using an UNO board, a 16x2 lcd for driver feedback, storing engine rpm and rear wheel speed to an sd card for CVT tuning, and using 2 buttons for a stop watch.

My biggest problem right now is that the engine RPM and rear wheel speed sensors should both be attached to interrupts so that they catch every pulse, but the lcd takes pins 4-10, the sd takes 11-13, and 3. leaving only pint 2 open.

It is my understanding that digital pins 0 and 1 can be used normally if you do not use serial data, is this correct?

As a team, we are looking into also monitoring engine, gearbox and CVT temps, along with possibly our air shock pressures.

ps: I have only worked with arduino for 6 months, but have taken 1 year of C programming

The analogue input pins can be used as digital I/O pins, if they’re not being used as analogue input pins.

AWOL, with that being said, I should be able to reroute the CS pin of the sd card shield, originally on pin 10, I moved it to pin 3 via jumper to avoid interferance with the lcd shield, and move it to A1?

"sensors should both be attached to interrupts so that they catch every pulse"

If you want to use the two extern interrupts, you have to use pins 2 and 3, because only these support that feature (on most platforms).

(Pin change) Interrupts on other pins are also possible (as I've read, but not used), but its closer to the hardware than attach(... ), and you have to isolate the pin, as this interrupt is fired for any pin change enabled bit on the port base.

well, a productive weekend it has been. I have gotten most of my code together, and need some pointers on it.

engine rpm is provided by this: http://www.sportdevices.com/rpm_readings/SP1%20ignition%20sense.gif

rear wheel speed is provided by a magnet on the brake rotor and a magnetic reed switch

looking at providing battery voltage on the screen to help with performance, it is a homemade 4s 12.8V LiFE battery. suggestions on this are appreciated.

Inside the SDWrite function, it says that += is not valid, operand types are ‘string [1]’ and ‘string’

yes, i know that i dont have everything correct yet

/*Baja Dash display
 * Coded by: *************
 * Rev 1 created 9/7/15

*/
//========================================

// ----------LIBRARIES--------------

#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>
#include <String.h>

// --------CONSTANTS (won't change)---------------

//pin inputs
//Engine RPM = 2;
//Rear wheel RPM = 3;
//SD uses 11, 12, 13, a5
//lcd uses 4-10
//batery voltage a1?
const int buttonPin = A0;

//speed factors
const int tireDiameter = 24;
const int MPHCoef = 178500;

//data file header
char header[] = "Engine_rpm, Wheel_speed, time";

//------------ VARIABLES (will change)---------------------
//screen stuffs
int screen = 0;
int Update = 1000;
unsigned long lastMillis = 0;
unsigned long currMillis = 0;
int z;

//stopwatch variables
boolean timer = false;
unsigned long start, finished, elapsed;
long lastSSTime = 0; // the last time the timer was triggered
float h, m, s, ms;
unsigned long over;
int x, y;

//RPM variables
int E_count = 0;
int E_rpm = 0;

int RW_count = 0;
int RW_rpm = 0;
int RW_mph = 0;

//sd card variables
char filename[] = "LOGGER00.CSV";
String line[] = "";
int w;
boolean record = false;
boolean recording = false;
long lastRecTime = 0; // the last time the recorder was triggered

//extras
long debounceDelay = 50; // the debounce time for lcd buttons; keep this as low as possible


//------------ SD CARD ---------------------
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = A5;

File logFile;


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//========================================

void setup() {
  Serial.begin(9600); // Open serial communications and wait for port to open

  lcd.begin(16, 2);
  lcd.clear();
  
  lcd.setCursor(8,1);
  lcd.print("00:00:00");
  AttachInterrupts();
}

//========================================

void loop() {
  CheckStartStop();
  CheckResetTimer();
  //CheckScreenSwitch();
  CheckStartStopLogger();
  currMillis = millis();
  if(currMillis - lastMillis >= Update){
    DetachInterrupts();        //allows calculations to be carried out without interuption
    CalcRPMs();
    if(screen == 0){
      DisplayTimerResult();
      DisplayRPM();
    }
    if(screen == 1){          //UNRELIABLE and untested
      lcd.setCursor(0,0);
      lcd.print("test");
    }

    if(recording == true){
      SDWrite();
    }
    lastMillis = millis();
    AttachInterrupts();
  }
}

//========================================

void CheckStartStop(){
  x = analogRead (0); // assign 'x' to the Arduino's AnalogueInputs (Shield's buttons)
  if (x < 800 && x > 600 ){ // if the button is SELECT
    if ((millis() - lastSSTime) > debounceDelay){
       if (timer == false){
         start = millis(); // saves start time to calculate the elapsed time
       }
       timer = !timer;
     }
    lastSSTime = millis();
    }
}

//========================================

void DisplayTimerResult(){
  if (timer == true){
    finished = millis(); // saves stop time to calculate the elapsed time
    
//calculating the timer
    elapsed = finished - start;
    
    h = int(elapsed / 3600000);
    over = elapsed % 3600000;
    m = int(over / 60000);
    over = over % 60000;
    s = int(over / 1000);
    ms = over % 1000;
    
// display the results
   lcd.setCursor(8, 1);     // Move the cursor to column four, lower row
   if (h<10){               // Add a zero, if necessary, as above
     lcd.print(0);
   }
   lcd.print(h, 0);         // Display the current hour
   lcd.setCursor(10, 1);    // Move to the next column
   lcd.print(":");          // And print the colon
   if (m<10){               // Add a zero, if necessary, as above
     lcd.print(0);
   }
   lcd.print(m, 0);         // Display the current minute
   lcd.setCursor(13, 1);    // Move to the next column
   lcd.print(":");          // And print the colon
   if (s<10){               // Add a zero, if necessary, as above
     lcd.print(0);
   }
   lcd.print(s, 0);         // Display the current second
  }
}

//========================================

void CheckResetTimer(){
  y = analogRead (0); // assign 'z' to the Arduino's AnalogueInputs (Shield's buttons)
  if (y < 420 && y > 350){ // if the button is LEFT
    if(timer==false){
      lcd.setCursor(8,1);
      lcd.print("00:00:00");
    }
  }
}

//========================================

void CheckScreenSwitch(){
  z = analogRead (0); // assign 'z' to the Arduino's AnalogueInputs (Shield's buttons)
  if (z < 20 ){ // if the button is RIGHT
    if(screen == 0){
      lcd.clear();
      screen = 1;
    }
    if(screen == 1){
      lcd.clear();
      screen = 0;
    }
  }
}

//========================================

void AttachInterrupts(){
  E_rpm = 0;
  RW_rpm = 0;
  attachInterrupt(0, e_rpm, RISING);
  attachInterrupt(1, rw_rpm, RISING);
}

//========================================

void e_rpm(){
  E_count++;
}

//========================================

void rw_rpm(){
  RW_count++;
}

//========================================

void DetachInterrupts(){
  detachInterrupt(0);
  detachInterrupt(1);
}

//========================================

void CalcRPMs(){
  //engine rpm
  E_rpm = E_count * (currMillis-lastMillis);

  //rear wheel rpm
  RW_rpm = RW_count * (currMillis-lastMillis);
  RW_mph = (RW_rpm * tireDiameter * 3.14 * 60)/63360;
}

//========================================

void DisplayRPM(){
// wheel speed
  lcd.setCursor(0,0);
  lcd.print("  ");
  lcd.setCursor(0, 0);
  if(RW_mph <= 2){
    lcd.print("000");
  }
  else{
    lcd.print(RW_mph, 1);
  }
  lcd.print("MPH");

  lcd.setCursor(7, 0);
  lcd.print("    ");
  lcd.setCursor(7, 0);
  lcd.print(E_rpm, 0);
  lcd.print("RPM");
}
//========================================

void CheckStartStopLogger(){
  w = analogRead (0);          // assign 'w' to the Arduino's AnalogueInputs (Shield's buttons)
  if (w < 110 && w > 80 ){     // if button is UP
    if ((millis() - lastRecTime) > debounceDelay){
      if (record == false){
       recording = true;
       CreateFile();
      }
      if(record == true){
        recording = false;
        logFile.close();        //saves and closes the file
      }
      record = !record;
    }
    lastRecTime = millis();
  }
}

//========================================

void CreateFile(){
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logFile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
  lcd.setCursor(16,0);
  if(! logFile) {
    lcd.print("E");
    recording = false;
    record = false;
  }
  else{
    lcd.print("R");
  }
}

//========================================

void SDWrite(){//Engine_rpm, Wheel_speed, time";
  line += String(E_rpm);
  line += ",";
  line += String(RW_mph);
  line += ",";
  line += String(millis());

  logFile.println(line);
  line = "";
}

I would try to get rid of any Strings in the program. Dynamic memory usage is avoided (which is very limited in this environment) and your error should vanish. ;)

  line += String(E_rpm);
  line += ",";
  line += String(RW_mph);
  line += ",";
  line += String(millis());
  logFile.println(line);

// could be

char lineBuffer[32];
  
  sprintf(lineBuffer, "%d, %d, %ld", E_rpm, RW_mph, millis());
  logFile.println(lineBuffer);

The buffer could be used by all your formatting, but keep in mind, that you are responsible not to put something in there that does not fit. Its like a glass, fill more in than fits, it spills.

Using attachInterrupt and detachInterrupt for controlling access to shared resources is at least bad practice. You can enable and disable interrupts with other functions, have a look at:

http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__interrupts.html

You only have to wrap accesses to the two integers that you are sharing in dis/enable. Keep the time spent with interrupts disabled to an absolute minimum. So disable, copy, enable. That way you will most likely never miss an interrupt.

Interrupt routines should execute as fast as possible, your isrs are pretty perfect in that respect (on the C level).

Shared variables have to be declared as volatile, to force the compiler to read the variable each time its used (instead of eventually using an old copy in some register, location, stack, ...).

int E_count = 0;
int RW_count = 0;

// should be

volatile int E_count = 0;
volatile int RW_count = 0;

Hi,
Will the mag reed switch be fast enough and last long enough?
Look up a Hall Effect device, it will be more reliable.

Tom… :slight_smile:

Whandall, Thank you for the code suggestions. If it wasnt apperant in my code, this is my first time using interrupts, and its all new still. I will make the suggested corrections.

TomGeorge, the reasoning for the mag reed switch is it is 2 wires compared to 3, we have used the read switch for 3 years and it has worked thus far for us. we top out at approx. 40mph, mostly inside of 20-30 mph

this is a picture of the buggy that everything is going onto: |500x301

a couple more questions related to this project, How would I measure temperature with analog temp probes? I plan on filling up A2,3,4 with temp probes.

Also, does anyone know of an electronic 2-5" throw PWM ram? can run on separate power up to 12V preferably already waterproof?

Our drive-train lead has an idea of controlling the CVTs with the computer if needed......