dinsdag 10 februari 2015

Simple thermostat Arduino - Processing

bought an arduino starterkit and started playing...

once I got the LCD working, I combined it with the temp sensor and created a simple thermostat. Then I couldn't resist to make a processing sketch to draw a curve with received temperature value's.



There are 3 main screens, switched by the up/down buttons, 
the value's are set by the left/right buttons
(for more info about connecting a lcd to arduino http://arduino.cc/en/Tutorial/LiquidCrystal)


Arduino sketch:
#include <LiquidCrystal.h>

// LCD, initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //pins(rs, enable, d4, d5, d6, d7)
byte lcdCharDegree[8] = {B00000, B00111, B00101, B00111, B00000, B00000, B00000};  //LCD "°"
byte lcdCharLeft[8]   = {B00000, B00010, B00110, B01110, B00110, B00010, B00000};  //LCD "<"
byte lcdCharRight[8]  = {B00000, B01000, B01100, B01110, B01100, B01000, B00000};  //LCD ">"

//button
const int btnUp = 6;
const int btnRight = 7;
const int btnDown = 8;
const int btnLeft = 9;
int btnMenu = 0;   //needed to change menu item
int btnSet;        //needed to change settings

//temperature
const int sensorPin = A0;   //input pin for tempsensor
float temperature;          //temperature value in °C
long previousMillis = 0;    //needed for 'measure-delay'
long interval = 1000;       //measure-delay (1 sec)

//Thermostat ON-OFF
boolean thermostatStatus = LOW; //thermostat is turned off at startup
String textStatus;              //String printed on LCD

//Set temperature
float tempSet = 20.00;  //set 20.00°C at startup

//ledpin variables (2color-led instead of relais)
const int ledAPin = 10;
const int ledCPin = 13;


void setup() {
  Serial.begin(9600);
  // set up the number of columns and rows on the LCD 
  lcd.begin(16, 2);
  
  //create special charset for LCD
  lcd.createChar(0, lcdCharDegree);
  lcd.createChar(1, lcdCharLeft);
  lcd.createChar(2, lcdCharRight);
  // setup buttonpins as input
  pinMode(btnUp,INPUT);
  pinMode(btnDown,INPUT);
  pinMode(btnLeft,INPUT);
  pinMode(btnRight,INPUT);
  
  //setup ledpins as output
  pinMode(ledAPin,OUTPUT);
  pinMode(ledCPin,OUTPUT);
  
  // Print a message on startup
  lcd.setCursor(0, 0);          // set cursor to column 0, line 0 (top line)  
  lcd.print("THERMOSTAT");      // print to LCD
  lcd.setCursor(0, 1);          // set cursor to column 0, line 1 (bottom line)
  lcd.print("Version 0130.0");  //print to LCD
  delay(2000);                  //delay keeps startup message on LCD for 2 sec
  lcd.clear();                  //clear LCD before starting loop()
}

void loop() {
  //switch led when changing between menu-options
  wndSwitchLed(); 
  //switch menu-screen
  switch (btnMenu){              /* int btnMenu holds value 0 or 1 or 2
                                  * when btnUp/btnDown is pressed, 1 is added/removed to value btnMenu
                                  */
    case 0:
      wndScreenTopical();
      break;
    case 1:
      wndScreenOnOff();
      break;
    case 2:
      wndScreenTempSet();
  }
}

void wndScreenTopical(){
  //static text, printed once on LCD
  //variable text, update print until menu-option is changed
  //static text
  lcd.setCursor(0, 0);
  lcd.print("Topical Temp.");
  lcd.setCursor(9, 1);
  lcd.print(temperature);
  lcd.setCursor(14, 1);
  lcd.write(byte(0));
  lcd.setCursor(15, 1);
  lcd.print("C");
  //variable text
  while ((digitalRead(btnUp) == LOW) && (digitalRead(btnDown) == LOW)){  //check buttons, if up/down is pressed leave                                                                                                                                    loop and change screen/ledstatus
                                                                                                                         //when left/right is pressed change value and loop
    wndGetTemperature();      //get temperature
    lcd.setCursor(9, 1);      //set cursor back in position
    lcd.print(temperature);   //print topical temperature to LCD
    wndSwitchLed();           //change led color when temperature > tempSet
    wndBtnPressed();          //check buttons, if button is pressed, change screen/value
  } 
}  

void wndScreenOnOff(){
  //static text
  lcd.setCursor(0, 0);
  lcd.print("Thermostat");
  lcd.setCursor(0, 1);
  lcd.write(byte(1));
  lcd.setCursor(15, 1);
  lcd.write(byte(2));
  //variable text
  while ((digitalRead(btnUp) == LOW) && (digitalRead(btnDown) == LOW)){  //check buttons, if up/down is pressed                                                                                                                                               change screen/ledstatus
                                                                                                                       // when left/right is pressed change value and loop
    if (thermostatStatus){
      textStatus = "ON ";
    } else {
      textStatus = "OFF";
    }
    lcd.setCursor(6, 1);
    lcd.print(textStatus);
    wndBtnPressed();
  }
}

void wndScreenTempSet(){
  //static text
  lcd.setCursor(0, 0);
  lcd.print("Set Temperature");
  lcd.setCursor(0, 1);
  lcd.print("-");
  lcd.setCursor(15, 1);
  lcd.print("+");
  //variable text
  while ((digitalRead(btnUp) == LOW) && (digitalRead(btnDown) == LOW)){  //loop until menu-option is changed
    lcd.setCursor(4, 1);    //set cursor back in position
    lcd.print(tempSet);     //print variable value
    wndBtnPressed();        //check buttons, if up/down is pressed leave loop and change screen
                            //               if left/right is pressed change value and loop
  }
}

void wndGetTemperature(){
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;
    int sensorVal = analogRead(sensorPin);
    float voltage = (sensorVal/1024.0) * 5.0;  // convert the ADC reading to voltage
    temperature = (voltage - .5) * 100;        /* convert to °C
                                                * the sensor changes 10 mV per degree
                                                * the datasheet says there's a 500 mV offset
                                                * ((volatge - 500mV) times 100)*/
    Serial.println(temperature);
  }
}

void wndBtnPressed(){
  if (digitalRead(btnUp)){
    if (btnMenu != 0){                   //0 is lowest value for btnMenu
      lcd.clear();
      btnMenu = btnMenu--;
    }
  } else if (digitalRead(btnDown)){
    if (btnMenu != 2){                  //2 is highest value for btnMenu
      lcd.clear();
      btnMenu = btnMenu++;
    }
  } else if (digitalRead(btnLeft) && btnMenu == 1){ //btnleft in thermostat ON/OFF
    if (thermostatStatus){                          //thermostat ON?
      thermostatStatus = LOW;                       //turn OFF
    }
  } else if (digitalRead(btnLeft) && btnMenu == 2){  //btnleft in set temperature, switch-temperature - 0.5°C
    if (tempSet > 0){                                //tempset cannot go below 0°c
      tempSet = tempSet - 0.5;                       //if button is pressed substract 0.5 from settemp
      delay(200);                                    //delay 0.2 sec, otherwise value subracts to fast
    }
  } else if (digitalRead(btnRight) && btnMenu == 1){  //btnright in thermostat ON/OFF
    if (thermostatStatus == LOW){                     //thermostat OFF?
      thermostatStatus = HIGH;                        //turn ON
    }
  } else if (digitalRead(btnRight) && btnMenu == 2){  //btnright in set temperature, switch-temperature + 0.5°C
    if (tempSet < 100){                               //tempset cannot go above 100°c
      tempSet = tempSet + 0.5;                        //if button is pressed add 0.5 from settemp
      delay(200);                                     //delay 0.2 sec, otherwise value adds to fast
    }
  }
}

void wndSwitchLed(){
  if (tempSet > temperature && thermostatStatus){
    digitalWrite(ledAPin, LOW);
    digitalWrite(ledCPin, HIGH);
  } else {
    digitalWrite(ledCPin, LOW);
    digitalWrite(ledAPin, HIGH);
  }
}

processing:

import processing.serial.*;

Serial myPort;            // Serial port object
int lf = 10;              // Linefeed in ASCII-code, when linefeed is received, string is complete
String myString = null;   // empty string wich input data from arduino is saved in, when string is completed its converted to                                                 float
float num;                // float converted from received string
float [] valScreen = new float [30];   //30 temperatures values
int lengthScreen = 30;
PShape valCurve;          //Shape object value curve



void setup(){
  size(640,480,P2D);
  frameRate(1);                                            //every second the screen is updated once
  myPort = new Serial(this, Serial.list()[0], 9600);       //setup serial object for serial communication
  myPort.clear();                                          //empty serial buffer
  for (int i = 0; i < lengthScreen; i ++){                 //fill temp value array with 0's
    valScreen[i]=0;    
  }
}

void draw(){
                                                            
                                                            
  for (int i = 0; i < lengthScreen; i++){                   // replace temperature value's in array one place
    if (i < lengthScreen-1){                                // oldest is lost, new value is added from data-input
      valScreen[i]=valScreen[i+1];
    } else {
      while (myPort.available() > 0) {                      // read data while avaible
        myString = myPort.readStringUntil(lf);              // string ends when linefeed is received
        if (myString != null) {                             // when string is not empty
          print(myString);                                  // Prints String to console-window
          num=float(myString);                              // Converts string to float
          valScreen[i]= num*(-4);                           // New value, for visual reasons multiply by -4, negative so higher value is 
                                                                               drawn above lower value, 
                                                            // 4* real value because difference between value's are small 
        }
      }
      myPort.clear();                                       // clear serial buffer 
      background(200);                                      
      stroke(0, 0, 255);
      line(25, height/2, 480, height/2);                    // draw X-axis
      line(25, height/2-150, 25, height/2+20);              // draw Y-axis
    }
  }
  valCurve = createShape();                                 // create curve-shape
  valCurve.beginShape();
  valCurve.noFill();
  valCurve.strokeWeight(2);
  valCurve.stroke(200,0,0);
  for (int i = 0; i < lengthScreen; i++){                   // for loop for reading curve-data
    valCurve.curveVertex(15*i, valScreen[i]);
    if (i == 0 || i == lengthScreen-1){                     // at start/end curve ad same value once again (more info see Vertex in Processing-Refference)
      valCurve.curveVertex(15*i, valScreen[i]);
    }
    noStroke();                                    
    fill(0, 0, 0);
    ellipse(25+(15*i), height/2+valScreen[i], 5, 5);        // value points on curve, code is not part of shape(curve) but is placed here because of 'for-loop'
  }
  valCurve.translate(25,height/2);                          // move shape object to right position
  valCurve.endShape();
  shape(valCurve);                                          // draw curve
  textSize(15);
  fill(0, 0, 200);
  textAlign(RIGHT);
  text("Realtime temp: "+valScreen[lengthScreen-1]/(-4)+"°C", 480, height/2+50); //text output on screen
    
}

1 opmerking: