1

I am working on building a "simple" HVAC thermostat. This is my current sketch.

    // include the libraries for gizmos
#include <LiquidCrystal.h>
#include <dht11.h>

dht11 DHT11; // Figure out what this line does
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // initialize the library with the numbers of the interface pins

// These constants won't change:
const int ACledPin = 8;      // Air Conditioner LED (RED)
const int HTledPin = 9;      // Heat LED (YELLOW)
const int threshold = 77;    // an arbitrary threshold level that's in the range of the analog input

const int RELAY_01 = 6;    // RELAY 1 PIN 9
const int RELAY_02 = 10;   // RELAY 2 PIN 10

void setup() {
  DHT11.attach(7);       // Attach DHT11 sensor to pin 7
  lcd.begin(16, 2);      // set up the LCD's number of columns and rows:
  Serial.begin(9600);    // Serial setup

  pinMode(ACledPin, OUTPUT);  // initialize pin 8 as output
  pinMode(HTledPin, OUTPUT);  // initialize pin 9 as output
  pinMode(RELAY_01, OUTPUT);  // initialize pin 6 as output
  pinMode(RELAY_02, OUTPUT);  // initialize pin 10 as output

  // Leaves the relays in default "off" (no noise/click)
  digitalWrite(RELAY_01, HIGH);
  digitalWrite(RELAY_02, HIGH);

}

void loop() {

  float analogValue = (DHT11.fahrenheit()); // trying to set up the temp reading as an int variable
////////////// if then ////////////////
// if the analog value is high enough, turn on the LED:
  if (analogValue > threshold) {
    digitalWrite(ACledPin, HIGH);
    digitalWrite(HTledPin, LOW);
    digitalWrite(RELAY_01, HIGH);
    digitalWrite(RELAY_02, LOW);
  }
  else {
    digitalWrite(ACledPin, LOW);
    digitalWrite(HTledPin, HIGH);
    digitalWrite(RELAY_01, LOW);
    digitalWrite(RELAY_02, HIGH);
  }


Serial.println(analogValue);  // print the analog value:

// Set up the LCD Stuff
  lcd.setCursor(0, 0);      // Set cursor to top left
  lcd.setCursor(0, 1);
  lcd.print(analogValue, 1);
  lcd.print("F");

  delay(1000);

}

I want to add "smoothing" to the temperature reading. I Found this tutorial http://arduino.cc/en/Tutorial/Smoothing and have it working the way I want it.

// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
#include <dht11.h>
dht11 DHT11; // Figure out what this line does

const int numReadings = 5;

float readings[numReadings];      // the readings from the analog input
int index = 0;                    // the index of the current reading
float total = 0;                  // the running total
float average = 0;                // the average

void setup()
{
  DHT11.attach(7);       // Attach DHT11 sensor to pin 7
  Serial.begin(9600);    // initialize serial communication with computer:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) readings[thisReading] = 0; // initialize all the readings to 0:       
}

void loop() {
  // subtract the last reading:
  total= total - readings[index];         
  // read from the sensor:  
  readings[index] = (DHT11.fahrenheit()); //analogRead(inputPin); 
  // add the reading to the total:
  total= total + readings[index];       
  // advance to the next position in the array:  
  index = index + 1;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
    // ...wrap around to the beginning: 
    index = 0;                           

  // calculate the average:
  average = total / numReadings;         
  // send it to the computer as ASCII digits
  Serial.println("");
  Serial.print("Realtime: ");
  Serial.println(DHT11.fahrenheit(),2);
  Serial.print("Average:  ");
  Serial.println(average);   
  delay(2000);        // delay in between reads for stability            
}

I'm new to Arduino and don't know the best way to do this. I'm not certain how to do multiple loops/delays in a single project, or if I even need to.

EDIT:

I've ended up with this based on the comments from @Craig. The only thing that needs some adjustment are the first 4 readings are low, so it would be nice to somehow ignore, probably not a big deal in the long run.

Example readings on startup;

Realtime: 77.00
Average:  15.40

Realtime: 77.00
Average:  30.80

Realtime: 77.00
Average:  46.20

Realtime: 77.00
Average:  61.60

Realtime: 77.00
Average:  77.00

Final code

// include the libraries for gizmos
#include <LiquidCrystal.h>
#include <dht11.h>

dht11 DHT11; // Figure out what this line does
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // initialize the library with the numbers of the interface pins

// These constants won't change:
const int ACledPin = 8;      // Air Conditioner LED (RED)
const int HTledPin = 9;      // Heat LED (YELLOW)
const int threshold = 77;    // an arbitrary threshold level that's in the range of the analog input

const int RELAY_01 = 6;    // RELAY 1 PIN 9
const int RELAY_02 = 10;   // RELAY 2 PIN 10



// START ADDITIONS ////////////////////////////////////////////////////////////////////////////////////////////////
const int numReadings = 5;
float readings[numReadings];      // the readings from the analog input
int index = 0;                    // the index of the current reading
float total = 0;                  // the running total


float get_smoothed_temp() {
    total = total - readings[index]; // remove oldest reading
    readings[index] = DHT11.fahrenheit();
    total += readings[index];
    index = (index + 1) % numReadings;

    return (total / numReadings);
}
// END ADDITIONS ////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  DHT11.attach(7);       // Attach DHT11 sensor to pin 7
  lcd.begin(16, 2);      // set up the LCD's number of columns and rows:
  Serial.begin(9600);    // Serial setup
  for (int thisReading = 0; thisReading < numReadings; thisReading++) readings[thisReading] = 0; // initialize all the readings to 0:

  pinMode(ACledPin, OUTPUT);  // initialize pin 8 as output
  pinMode(HTledPin, OUTPUT);  // initialize pin 9 as output
  pinMode(RELAY_01, OUTPUT);  // initialize pin 6 as output
  pinMode(RELAY_02, OUTPUT);  // initialize pin 10 as output

  // Leaves the relays in default "off" (no noise/click)
  digitalWrite(RELAY_01, HIGH);
  digitalWrite(RELAY_02, HIGH);

}

void loop() {

  float analogValue = (get_smoothed_temp()); // trying to set up the temp reading as an int variable
////////////// if then ////////////////
// if the analog value is high enough, turn on the LED:
  if (analogValue > threshold) {
    digitalWrite(ACledPin, HIGH);
    digitalWrite(HTledPin, LOW);
    digitalWrite(RELAY_01, HIGH);
    digitalWrite(RELAY_02, LOW);
  }
  else {
    digitalWrite(ACledPin, LOW);
    digitalWrite(HTledPin, HIGH);
    digitalWrite(RELAY_01, LOW);
    digitalWrite(RELAY_02, HIGH);
  }


Serial.println(analogValue);  // print the analog value:

// Set up the LCD Stuff
  lcd.setCursor(0, 0);      // Set cursor to top left
  lcd.print("Real F:");
  lcd.setCursor(0, 1);
  lcd.print("AVG  F:");
  lcd.print(analogValue, 1);


  delay(1000);

}
  • What's the difference between the relay pins in the HT/AC pins? – Anonymous Penguin Jun 13 '14 at 20:01
2

I'm not certain how to do multiple loops/delays in a single project

You can't do this.* Software threading is the only way to do something like what you want, but it's too complex for what you need. What you want is to use the millis() function to return the time and

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);      
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

You can then pull button changes while you wait for a new reading. Another way would be to use an interrupt to interrupt the flow of the program when a button changes. I won't get into that.

For your use, you can use this template I made:

long previousMillis = 0;        // will store last time temp was updated
long interval = 1000;           // interval at which to check the temp (milliseconds)

void setup() {
  //Foobar
}

void loop()
{
  //Bar of foo
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    //Check Temp
  }
}

*Not to be confused with the loop statement.

2

What you want to do is replace your call to DHT11.fahrenheit() with a call to a new function (e.g. get_smoothed_temp()) that keeps track of the last several readings and returns the smoothed value.

const int numReadings = 5;
float readings[numReadings];      // the readings from the analog input
int index = 0;                    // the index of the current reading
float total = 0;                  // the running total

float get_smoothed_temp() {
    total = total - readings[index]; // remove oldest reading
    readings[index] = DHT11.fahrenheit();
    total += readings[index];
    index = (index + 1) % numReadings;

    return (total / numReadings);
}

You will still need to set the readings array to all zeros in the setup function.

2

Your running average code is fine.

A quick way to start up fast is to take your first reading in setup, and fill the array with that value instead of zero. That way you always get a reasonable temperature from the smoothing, it just gradually drifts from the first reading to the average of multiple readings (rather than from 0 - a bogus value - to the that average).

This can work for the exponential smoothing too (old average = first reading).

An alternative is to only average as many samples as you have put into the array (1 then 2, then 3 etc until it's filled). That takes extra code to handle just the first few results, tho, and the simpler approach suggested above (initially fill the array with a legitimate reading) is usually fine.

  • Good idea. I'll give that a shot. – AaronJAnderson Jun 15 '14 at 22:59
2

I've just done a fan controller (using digital sensors - DS18b20, in my case) and chose an exponential average for smoothing the readings because it doesn't require storing or managing old data and I could get the smoothing I needed. During setup I took a reading from each sensor and preset the averages to those values. That gives the system a "running start"; it doesn't need to wait for enough samples for the averages to settle.

The exponential average I chose was (3 * oldAverage + tempr)/4 because it gave the performance I needed and is easy and fast to calculate without floating point. Multiplying by 3 is done by shifting and adding; dividing by 4 is done by shifting.

Here's the routine I used. It could easily be put in-line; the compiler probably will:

int16_t xpavg(int16_t tempr, int16_t oldAverage)
{
   // (3*oldavg + 1*tempr)/4
   return( (((oldAverage<<1) + oldAverage + tempr) + 2) >> 2 );
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.