Data Acquasition System

Hello All,

This is my first post on here. I am new to the micro controller world, but the Arduino has me hooked. I am building a data acquisition system. So far it has a ambient temp sensor, Humidity sensor, and one Thermocouple. I am going to add an Rpm pick up that I made, and a gps module. All this is going to be written to an sd card in a .csv so I can use it in excell. I put a 16 x 2 lcd screen on it, and one button to toggle through the data. The code I have put together is chopped up from a bunch of the examples, but I am having trouble with the lcd screen updating. It shows old data until the button is pressed. I know it has to be something simple, but I am stuck. Also the code seems slow. When I press the button it takes a while to change the display. Any Help would be much appreciated.

Also I am using the adafruit gps/datalogger shield. I can not get it to work with the atmega 2560. it works just fine on my uno, and seems to be wired the exact same. I used the sketch from the adafruit http://www.ladyada.net/make/gpsshield/logging.html. I changed the header file just like it said with no luck. The searial moniter reads the sd card and makes a new file, but it never gets any gps info.

Thanks in advance! Here is the code I am using for the menu.

   val = digitalRead(switchPin);      // read input value and store it in val
  
  if (val != buttonState) {          // the button state has changed!
    if (val == LOW) {                // check if the button is pressed
      Mode++;          // increment the buttonPresses variable
      
  if (Mode >= 5){      //Sets amount of modes to cycle through. counts 1,2,3,4,5,0
    Mode = 0;

  }    
  
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  Thermtemp = thermocouple.readFahrenheit()-.8;//calibrates thermocouple temp.

  
  
  if(Mode == 0){
    lcd.clear();
      lcd.print("Amb Temp");
      lcd.setCursor(1,2);
      lcd.print(t * 9/5 + 32);
      lcd.print("*F");
  }
    if(Mode == 1){
      
      lcd.clear();
      lcd.print("Thermocouple");
      lcd.setCursor(1,2);  
      lcd.print(Thermtemp);
      lcd.print("*F");
    }
    if(Mode == 2){
      lcd.clear();
       lcd.print("GPS Data ");
    }
    if(Mode == 3){
      lcd.clear();
       lcd.print("RPM");
    }
    if(Mode == 4){
      lcd.clear();
       lcd.print("Humidity");
       lcd.setCursor(1,2);
       lcd.print(h);
      lcd.print("%");
    }
    
    
    
      Serial.print("Mode Selected ");
      Serial.println(Mode);
      
    }
  }
  buttonState = val;                 // save the new state in our variable
}

Hi, and welcome !

The reason your display isn’t updated until after you press the button is that the if(Mode ==…) logic is inside the { } that are opened here

  if (val != buttonState) {          // the button state has changed!

Which means all that good code that updates the variables only runs when the button changes the Mode value. So a slight change would leave you with

if (val != buttonState) {          // the button state has changed!
  if (val == LOW) {                // check if the button is pressed
    Mode++;          // increment the buttonPresses variable
    if (Mode >= 5) Mode = 0;     //Sets amount of modes to cycle through. counts 1,2,3,4,5,0
  } // (val == LOW)
} // (val != buttonState)

I’ve not removed all the {}'s that could be removed here, but the actual change that will make this work is the extra } which closes off the if(val != buttonState) { command.

After that, remember to remove one } further down the code as it’s no longer needed.

      Serial.print("Mode Selected ");
      Serial.println(Mode);
      
    }
  } // <--- I THINK IT'S ONE OF THESE TWO FROM MEMORY THAT NEEDS TO GO
  buttonState = val;                 // save the new state in our variable
}

Unfortunately I killed off my text editor before copying that last bit so it’s from memory, but you should be able to work out which one needs to die to make your sketch compile again.

Hope that makes sense, cheers ! Geoff

Houser636: ```    val = digitalRead(switchPin);      // read input value and store it in val     if (val != buttonState) {          // the button state has changed!     if (val == LOW) {                // check if the button is pressed       Mode++;          // increment the buttonPresses variable         if (Mode >= 5){      //Sets amount of modes to cycle through. counts 1,2,3,4,5,0     Mode = 0;

  }

The mistake you have made is very easy to do with that coding style. Do yourself a favour: put every { and } on a separate line, at the same level of indentation, and with the enclosed code indented one level more. Also, get into the habit of putting a block statement around every conditional code section, even if it's only one line of code. This avoids the danger of bugs where a single statement gets replaced with multiple statements later.

So:

    val = digitalRead(switchPin);      // read input value and store it in val
  
    if (val != buttonState)
    {          
        // the button state has changed!
        if (val == LOW) // check if the button is pressed
        {
            Mode++;          // increment the buttonPresses variable
      
            if (Mode >= 5)
            {
                //Sets amount of modes to cycle through. counts 1,2,3,4,5,0
                Mode = 0;
            }    
            ...
        }
        ...
    }

You guys are awesome! Thanks!

I cleaned up the code a bit and added the millis() function to keep the screen from flickering every time I clear the screen with lcd.clear(). Is there a better way of doing this?

Also the button reading gets missed sometimes. I have to hold the button down for a second. I assume this is do to it takes a while to do all the other stuff. When I press the button, if it gets read it takes about one full second to switch on the screen.

Here is the code in full so far.

//Mode Select Main Menue with Autoscroll
#include "DHT.h"
#include <LiquidCrystal.h>
#include "max6675.h"
int switchPin = 8;              // switch is connected to pin 8
int val;                        // variable for reading the pin status
int Mode = 0;          // how many times the button has been pressed
int buttonState;                // variable to hold the button state
#define DHTPIN 43        // what pin we're connected to for amb, and humidity 43
#define DHTTYPE DHT22   // DHT 22  (AM2302)

//Next six lines are for the five thermocouple pins.
int thermoDO = 27;
int thermoCS = 29;
int thermoCLK = 31;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
int vccPin = 25;
int gndPin = 23;

int VPin = 39;           //This sets variables for 5V and Ground for humidity sensor
int GPin = 41;

float Thermtemp;         //Thermocouple temperature.

long previousMillis = 0;        // will store last time LED was updated
long interval = 2000;



LiquidCrystal lcd(53, 52, 51, 50, 49, 48);//Sets lcd pins

DHT dht(DHTPIN, DHTTYPE);


void setup() 
{
   Serial.begin(9600);           // Set up serial communication at 9600bps
   
 
  buttonState = digitalRead(switchPin);   // read the initial state
  
   lcd.begin(16, 2);            //Size of display
  pinMode(switchPin, INPUT);    // Set the switch pin as input

  pinMode(VPin, OUTPUT);//Temp&Humidity
  pinMode(gndPin, OUTPUT); digitalWrite(gndPin, LOW);
    
     pinMode(vccPin, OUTPUT); digitalWrite(vccPin, HIGH);//Sets 5V and Ground for Max6675
    
    digitalWrite(VPin, HIGH);//Sets pin 39 to 5V
    pinMode(GPin, OUTPUT);
    digitalWrite(GPin, LOW);//Sets pin 41 to Ground
dht.begin();



 
 //This is the first screen on the menue.  (needs to be defined.) scrolls "Data Aquisition System" Then "press a button".
 char MainMenue[24] = {"Data Aquasition System!"}; //Sets text to an array
  lcd.setCursor(16,0);
  // set the display to automatically scroll:
  lcd.autoscroll();
  // print array
  for (int thisChar = 0; thisChar < 23; thisChar++) //Loop to scroll text.
    {
        lcd.print(MainMenue[thisChar]);
        delay(300);
    }
   
   
   lcd.noAutoscroll();//Turns of scrolling.
  
}


void loop()
{
       unsigned long currentMillis = millis();
        val = digitalRead(switchPin);      // read input value and store it in val

 
 
    if (val != buttonState)
        {          // the button state has changed!
              if (val == LOW)
                  {                // check if the button is pressed
                      Mode++;          // increment the buttonPresses variable
                  }
        if (Mode >= 5)
              {      //Sets amount of modes to cycle through. counts 1,2,3,4,5,0
                Mode = 0;

              }    
        }
        
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  Thermtemp = thermocouple.readFahrenheit()-.8;//calibrates thermocouple temp.

  

  if(Mode == 0 && currentMillis - previousMillis > interval)
    {
      
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Amb Temp");
      lcd.setCursor(1,2);
      lcd.print(t * 9/5 + 32);
      lcd.print("*F");
       previousMillis = currentMillis;
    }
  if(Mode == 1 && currentMillis - previousMillis > interval)
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Thermocouple");
      lcd.setCursor(1,2);  
      lcd.print(Thermtemp);
      lcd.print("*F");
       previousMillis = currentMillis;
     }
  if(Mode == 2 && currentMillis - previousMillis > interval)
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("GPS Data ");
       previousMillis = currentMillis;
    }
  if(Mode == 3 && currentMillis - previousMillis > interval)
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("RPM");
       previousMillis = currentMillis;
    }
  if(Mode == 4 && currentMillis - previousMillis > interval)
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Humidity");
      lcd.setCursor(1,2);
      lcd.print(h);
      lcd.print("%");
       previousMillis = currentMillis;
    }
    
    
    
      Serial.print("Mode Selected ");
      Serial.println(Mode);
      
    
  
  buttonState = val;                 // save the new state in our variable
}

Houser636: I cleaned up the code a bit and added the millis() function to keep the screen from flickering every time I clear the screen with lcd.clear(). Is there a better way of doing this?

Also the button reading gets missed sometimes. I have to hold the button down for a second. I assume this is do to it takes a while to do all the other stuff.

For the first part, you could avoid using lcd.clear() by always writing the full width of the LCD each time, therefore only rewriting the characters on the display that need to be replaced.

For the button question, google "switch debounce" and you'll find a heap of material on that. Here's a video, if you like that kind of thing http://youtu.be/_LCCGFSMOr4

Cheers ! Geoff

Thanks! It's coming together. I got the flickering to stop. You were right it, not using lcd.clear() is much better. I de-bounced the switch and read up on it, but I do not think this is causing the problem. It gets a good reading if I hold it down for a second. It never toggles through modoes like I believe it would if the de-bounce was not working. Should I use an attach interrupt? If so how do I call my debounce function on the interrupt? I was even thinking about a hardware fix with a 555 timer to keep the button input on for a sec after it is pressed. What do you think?

boolean debounce(boolean last)
{
  boolean current = digitalRead(switchPin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;

As to your logger problem on the 2560, I ran into similar problems with the Real Time Clock/SD Shield from adafruit. The SPI pins on the 2560 are in a different location than on the UNO, so I had to make some modifications to the board: http://kesslerarduino.wordpress.com/2011/06/21/logger-shield-and-freqcounter-for-the-mega-2560/ I assume the SD Card on the GPS logger also uses the SPI interface, so you would have to doctor your shield as well.

You will only display new data once every 2 seconds, even if you switch modes. Thus, you might want to subtract "interval" from "previousMillis" when you detect a button press, to make sure you will immediately re-draw the screen.

If it's still slow after that, I would suspect that the various read functions may contain a delay of some sort.

You will only display new data once every 2 seconds, even if you switch modes. Thus, you might want to subtract “interval” from “previousMillis” when you detect a button press, to make sure you will immediately re-draw the screen.

If it’s still slow after that, I would suspect that the various read functions may contain a delay of some sort.

I got rid of the milli function. I’m sure your right. It just takes to much time to loop back around and check the button. Any clue on a fix. Like I said I am considering the 555 timer to fool it into thinking I am pressing it for longer than I am. I would rather use software.

As to your logger problem on the 2560, I ran into similar problems with the Real Time Clock/SD Shield from adafruit. The SPI pins on the 2560 are in a different location than on the UNO, so I had to make some modifications to the board: http://kesslerarduino.wordpress.com/2011/06/21/logger-shield-and-freqcounter-for-the-mega-2560/
I assume the SD Card on the GPS logger also uses the SPI interface, so you would have to doctor your shield as well.

I was looking at that. I will have to dig deeper into it tomorrow. Thanks for sending me in the right direction.

here is the updated code

#include "max6675.h"
#include "DHT.h"
#include <LiquidCrystal.h>
#define DHTPIN 43        // what pin we're connected to for amb, and humidity 43
#define DHTTYPE DHT22   // DHT 22  (AM2302)
int switchPin = 8;
boolean lastButton = LOW;
boolean currentButton = LOW;
int Mode = 0;
//Next six lines are for the five thermocouple pins.
int thermoDO = 27;
int thermoCS = 29;
int thermoCLK = 31;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
int vccPin = 25;
int gndPin = 23;

int VPin = 39;           //This sets variables for 5V and Ground for humidity sensor
int GPin = 41;

int lcdClear = 0;

float Thermtemp;         //Thermocouple temperature.

long previousMillis = 0;        // will store last time LED was updated
long interval = 2000;

 LiquidCrystal lcd(53, 52, 51, 50, 49, 48);//Sets lcd pins
 DHT dht(DHTPIN, DHTTYPE);

void setup()
{
  lcdClear++;
      Serial.begin(9600);
      
      //attachInterrupt(21, debounce, CHANGE);
      
      pinMode(switchPin, INPUT);
      pinMode(VPin, OUTPUT);//Temp&Humidity
      pinMode(gndPin, OUTPUT); digitalWrite(gndPin, LOW);
    
      pinMode(vccPin, OUTPUT); digitalWrite(vccPin, HIGH);//Sets 5V and Ground for Max6675
    
      digitalWrite(VPin, HIGH);//Sets pin 39 to 5V
      pinMode(GPin, OUTPUT);
      digitalWrite(GPin, LOW);//Sets pin 41 to Ground
      
      dht.begin();
      
      lcd.begin(16, 2);
      
       //This is the first screen on the menue.  (needs to be defined.) scrolls "Data Aquisition System" Then "press a button".
 char MainMenue[24] = {"Data Aquasition System!"}; //Sets text to an array
      lcd.setCursor(16,0);
            // set the display to automatically scroll:
      lcd.autoscroll();
            // print array
    for (int thisChar = 0; thisChar < 23; thisChar++) //Loop to scroll text.
        {
            lcd.print(MainMenue[thisChar]);
            delay(300);
        }
   
   
   lcd.noAutoscroll();//Turns of scrolling.
      if(lcdClear <= 1)
      {
        lcd.clear();
        lcdClear = 2;
      }
      
}

boolean debounce(boolean last)
{
  boolean current = digitalRead(switchPin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}

void loop()
{
   unsigned long currentMillis = millis();
    // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  Thermtemp = thermocouple.readFahrenheit()-.8;//calibrates thermocouple temp.
  
  currentButton = debounce(lastButton);
  if (lastButton == LOW && currentButton == HIGH)
    {
       Mode++;          // increment the buttonPresses variable
    }
        if (Mode >= 5)
              {      //Sets amount of modes to cycle through. counts 1,2,3,4,5,0
                Mode = 0;

              }    
             
 if(Mode == 0)
      {
        
         lcd.setCursor(0,0);
         lcd.print("Amb Temp        ");
         lcd.setCursor(1,2);
         lcd.print(t * 9/5 + 32);
         lcd.print("*F              ");
          
       }
       
    if(Mode == 1)
      {
     
         lcd.setCursor(0,0);
         lcd.print("Thermocouple    ");
         lcd.setCursor(1,2);  
         lcd.print(Thermtemp);
         lcd.print("*F              ");
         
      }
      
    if(Mode == 2)
      {
        
        lcd.setCursor(0,0);
        lcd.print("GPS Data         ");
        lcd.setCursor(1,2);
        lcd.print("                ");
      
      }
      
    if(Mode == 3)
      {
       
        lcd.setCursor(0,0);
        lcd.print("RPM              ");
         lcd.setCursor(1,2);
         lcd.print("                ");
      }
      
    if(Mode == 4)
      {
        
        lcd.setCursor(0,0);
        lcd.print("Humidity         ");
        lcd.setCursor(1,2);
        lcd.print(h);
        lcd.print("%                ");
        
      }
    

    
    
  
  lastButton = currentButton;
  

Serial.println(Mode);
}

Hi,

Looking at that sketch there's no need to consider putting your switch on an interrupt since the loop() has no delays so does not block the execution at any point. There is the possibility reading some of your sensors takes some time, but you'd need to check the data sheets for that to confirm. The debounce() function itself only blocks for 5/1000th of a second so there's nothing there that you'd notice, and that's a function I use as my default simple debounce and haven't seen any lag...though I accept there are dozens of ways to achieve the debounce.

Your thought that the debounce would cause the mode to change in a scrolling fashion isn't the usual symptom. More often it's that you have to press the button several times to register it as being pressed once, not that you press it once and a 'sticky' button occurs.

Overall this is looking pretty tight now. Check out the Switch/Case statement which you could use instead of the list of if's... http://arduino.cc/en/Reference/SwitchCase

The impact to the logic is zero, but I find the format easier to read:

  Switch (Mode) {
  case 0:
    lcd.setCursor(0,0);
    lcd.print("Amb Temp        ");
    lcd.setCursor(1,2);
    lcd.print(t * 9/5 + 32);
    lcd.print("*F              ");
    break;
  case 1:
    lcd.setCursor(0,0);
    lcd.print("Thermocouple    ");
    lcd.setCursor(1,2);  
    lcd.print(Thermtemp);
    lcd.print("*F              ");
    break;
  case 2:
    lcd.setCursor(0,0);
    lcd.print("GPS Data         ");
    lcd.setCursor(1,2);
    lcd.print("                ");
    break;
  case 3:
    lcd.setCursor(0,0);
    lcd.print("RPM              ");
    lcd.setCursor(1,2);
    lcd.print("                ");
    break;
  case 4:
    lcd.setCursor(0,0);
    lcd.print("Humidity         ");
    lcd.setCursor(1,2);
    lcd.print(h);
    lcd.print("%                ");
    break;
  default:
    Serial.println("ERROR: Invalid value for Mode !")
  }

Cheers ! Geoff

The easiest way to make sure to read buttons is to set up a pin-change interrupt. In the interrupt function, just set a boolean saying "this button was pressed," if the button reads as down. Then, in the main function, don't read the button; instead read this boolean, and clear it. (In the best of worlds, while disabling interrupts, to avoid a race condition) Yes, this is kind-of advanced programming, but that's what you have to do to get the more advanced behavior of seeming to do two things at once.

The easiest way to make sure to read buttons is to set up a pin-change interrupt. In the interrupt function, just set a boolean saying "this button was pressed," if the button reads as down. Then, in the main function, don't read the button; instead read this boolean, and clear it. (In the best of worlds, while disabling interrupts, to avoid a race condition)

I am a bit confused on how to do this. Do I need to debounce the button in hardware if i am using the interrupt? I built a de-bounce circuit with a cap, resistor and a 555 timer acting as a schmitt trigger, but I need a bigger capacitor. to code it I have a global boolean already called currentButton. Can I set this as Volatile and use it?

When I try this it say's "btnCheck was not declared in this scope" Do I have the function in the wrong place?

volatile boolean currentButton;

void setup() {

 //attachInterrupt
      attachInterrupt(0,btnCheck,FALLING);

}

boolean btnCheck()
{
  currentButton = digitalRead(switchPin);
}



void loop() {

  
}

The SPI pins on the 2560 are in a different location than on the UNO, so I had to make some modifications to the board

Just got the shield working. All I had to do was change the TX and RX I ran a jumper to pin 52 and pin 53, and change the pins in my code. Thanks for the help! I'm getting closer to something that might actually work.

Houser636: I am a bit confused on how to do this. Do I need to debounce the button in hardware if i am using the interrupt?

I almost always de-bounce in software. Easiest for me is to put in a minimum time between detected presses. Thus, the code will look something like:

unsigned long lastButtonMillis;
volatile bool buttonPressed;

....

void loop() {
....
  unsigned long nowMillis = millis();
....
  if (buttonPressed) {
    if (nowMillis - lastButtonMillis > kDebounceTime) {
      // do the thing
    }
    lastButtonMillis = nowMillis;
    buttonPressed = false;
  }
...
}

When I try this it say's "btnCheck was not declared in this scope" Do I have the function in the wrong place?

Yes. The compiler cannot see forward in the file. Thus, when you use "btnCheck," it has to already have been declared. Easiest is to move it before the function that calls attachInterrupt(). You can also use a "forward declaration" (look it up, it's useful!)

I almost always de-bounce in software.

I thought I couldn’t use the millis() in a function called on by an interrupt. I didn’t think time would increment during it.

I declared the function, but now am getting an error that say’s invalid conversion from ‘boolean()boolean)'to 'void()()’

I know have everything working together even the gps unit. It writes everything to the SD card, but this button is driving me crazy! I can’t sleep, I dream about buttons lol.

That wouldn't be a function that needed to be attached to an interrupt. It is a swap-in replacement for the one you were originally using.

That wouldn't be a function that needed to be attached to an interrupt.

Oh ok. I do like your way much better without the delay. I am still having a problem getting the button to be seen. Its just being missed by the program as it loops. I think my only options are an Interrupt, or button polling.

My RPM sensor also uses an interrupt so I was thinking of using a ATTiny 2313 on the sensor running its script, then talk to the arduino on an I2C bus. The sensor then has 1 wire coming off it that ties around a spark plug for the pick up.

1) Your interrupt handler should be declared to return void and take no argument:

void my_interrupt() {
  // set the flag here
}

2) My de-bounce function is not an interrupt function. It's shown inside loop() -- it shows how you can improve the simple "was the flag set" test with an additional timing check to make sure the second hit is not super close to the first one -- hence, software de-bounce.

The interrupt plus the debounce work great together, and will let you detect the press, assuming your circuit is correct and reliable, you have set the right pin modes, etc.

Ok the button and interrupt are working great. Thanks! Couldn't have done it without your help.

Now time to design a custom PCB to get it all on one board.