... wanted to share a hobby project that I've been tweaking with for the past several months. This item represents my entree into embedded computing. I hope to eventually put together a couple of environmental sensors that can be deployed to a field site or can be used in the lab. This project seemed like a good place to start.
| Press the button and the LCD screen display current conditions, updating every 2 seconds, for 10 seconds. The sensor is on the lower left of the face. |
The insides. The Arduino Uno is mounted at the bottom of the project box and the breadboard and other components are on the inside of the lid. Hooray for double-sided sticky-tape.
|
Anyway, the electronics for the item are based on Arduino and several breakout components available from a local, boulder-based, electronics hobby company called Sparkfun. The code that controls data aquisition, logging, and interactions were put together in the Arduino development environment and rely heavily on libraries made available by others. I've been pleased with level of resources available for working with the Arduino, and the relative ease with which circuits can be put together and the logic can be worked out. Holly and I attended a one day beginning-Arduino couse several months ago out of curiousity.
The temperature humidity sensor was designed for a friend's wine storage area in his cellar. The item:
- runs on line-power,
- measures temperature and humidity using a inexpensive sensor
- saves data to a microSD card
- has a small LCD screen to display current values when a button is pressed.
My code is here:
/* This "sketch" is code for a temperature and relative humidity logger using the following hardware: - an Arduino Uno, - a DHT22 Temperature and Relative Humidity Sensor (http://www.sparkfun.com/products/10167), - a SparkFun data logger (http://www.sparkfun.com/products/9530), - a Sparkfun LCD Screen breakout board (http://www.sparkfun.com/products/9393), and - a real time clock (RTC) module (http://www.sparkfun.com/products/99). - a normally open momentary switch Currently set up to record data approximately every 5 minutes. I say approximately, because the logic also allows for a user to push current temp and RH to the LCD screen. The DHT22 apparently has a response time of 2 seconds, and user input from the LCD screen takes precedent over the intermittent logging. Anyway, the code make generous use of librarys for the using the DHT22, serial communical (the NewSoftSerial library deals with printing floats better than the serial functions that are embedded in the Arduino IDE), and communicating with the RTC. ... even though there's a real time clock, the codes makes plenty use of the Arduino's own internal clock to keep track of timers for: interpreting noise in the LCD activation switch (this is all the debounce stuff that's in the code), figuring out how long to keep the LCD illuminated after pressing the button (think of it like a sustain on a guitar chord or a piano note), logging data making sure that the arduino doesn't poll the temp/RH sensor too frequently. Anyway, there's a whole lotta long integers to keep track of all these different timers. They should roll over every couple of weeks. Author: Matt Findley, Boulder, Colorado, matthewfindley at hotmail tod com. */ //import statements #include// This is the DHT22 library: https://github.com/nethoncho/Arduino-DHT22 #include // http://arduiniana.org/libraries/newsoftserial/ #include "Wire.h" // ...think this library comes with the Arduino IDE. #define DS1307_ADDRESS 0x68 //address for the real time clock. This line of code is from an example. //declare global variables and pin constants //assign pins const int txPin = 2; // pin used to transmit character data to the LCD screen. const int buttonPin = 3; // used to poll the normally open momentary switch. This pin is also connected to a 10K ohm pull down resistor, so it is firmly in the "low" state unless the button is pressed. const int DHT22Pin = 4; // used to get the signal from the Temp/RH sensor. This pin is also connected to a 10K ohm pull up resistor, so it is firmly in the "high" state unless the sensor pulls the line to ground. const int loggerPin = 5; // pin used to transmit temp and RH measurements to the data logger. // Variables will change: boolean displayState = 0; // boolean for the current state of power to the LCD screen int buttonState = LOW; // the current reading from the button input pin int lastButtonState = LOW; // the previous reading from the button input pin // the following variables are long's because the time, measured in miliseconds, // button debounce variables long lastDebounceTime = 0; // the last time the output pin was toggled long debounceDelay = 20; // the debounce time; increase if the output flickers // power the lcd display for ~10 seconds after releasing button. long lastButtonOnTime = 0; long sustainTime = 10000; // measure temp and RH no more than every 2000 milliseconds long lastMeasureTime = 0; long measureInterval = 2000; // log temp and RH every 300,000 milliseconds (5 minutes) long lastLogTime = 0; long logInterval = 300000; // variables for storing temperature, RH, and Time float temperature; float relHumid; int month, monthDay, weekDay, year, hour, minute, second; // initialize all the connections // start NewSoftSerial instance for LCD output NewSoftSerial LCD(0, txPin); //TX on 2 NewSoftSerial logger(0, loggerPin); // Setup a DHT22 instance DHT22 myDHT22(DHT22Pin); void setup() { // set up real time clock Wire.begin(); // set up serial out to push debugging info over USB to the PC terminal Serial.begin(9600); // set up button for lcd display pinMode(buttonPin, INPUT); // initializeLogger logger.begin(9600); //9600bps is default for OpenLog delay(1000); //Wait a second for OpenLog to init logger.println("Run OpenLog Test"); // collect initial round of temp/RH measurements pollSensor(); // initialize lcd LCD.begin(9600); //start 9600 baud com channel with the LCD powerUpLCD(); clearLCD(); LCD.print("Findley's Temp/RH Sensor"); delay(3000); clearLCD(); LCD.print("Version 3.5 7/16/2011"); delay(3000); clearLCD(); powerDownLCD(); } void loop() { //poll button, but filter out false-positive bounces. Button will cause the LCD to illuminate and will display current temp and RH int reading = digitalRead(buttonPin); if(reading != lastButtonState) { lastDebounceTime = millis(); //reset timer } if((millis() - lastDebounceTime) > debounceDelay) { buttonState = reading; } lastButtonState = reading; //save so that there's something to compare against the next time around the loop. //interpret button and power up the lcd screen as needed. if (buttonState == HIGH) { lastButtonOnTime = millis(); powerUpLCD(); // getTime(); goTo(0,0); LCD.print("Temp:"); goTo(1,0); LCD.print("RH:"); goTo(0, 5); LCD.print(temperature, 1); // print temperature with one decimal place. LCD.print("C"); goTo(1, 3); LCD.print(relHumid, 1); // print relative humidity with one decimal place. LCD.print("%"); // LCD.print(" :"); // LCD.print(minute); } else { if((millis()-lastButtonOnTime) > sustainTime) { powerDownLCD(); } } //conditional poll sensor. Only poll the sensor if it is time to log data or if the display is on. // in both cases, we can't poll the temp humidity sensor more than every couple of seconds - seems to return error otherwise. if (((millis() - lastLogTime) > logInterval) && ((millis() - lastMeasureTime) > measureInterval)) { pollSensor(); logData(); // reset last log time and last measure time. lastLogTime = millis(); lastMeasureTime = millis(); } else if ((displayState == 1) && ((millis() - lastMeasureTime) > measureInterval)) { pollSensor(); // getTime(); // updateDisplay with the refreshed temp and humidity values. goTo(0, 5); LCD.print(temperature, 1); // update temperature with one decimal place, don't need to update the other parts of the displayed info. goTo(1, 3); LCD.print(relHumid, 1); // update temperature with one decimal place, don't need to update the other parts of the displayed info. // LCD.print(" :"); // LCD.print(minute); // reset last measure time. lastMeasureTime = millis(); } } // end bracket for the loop statement void pollSensor(){ DHT22_ERROR_t errorCode; errorCode = myDHT22.readData(); switch(errorCode) { case DHT_ERROR_NONE: temperature = myDHT22.getTemperatureC(); relHumid = myDHT22.getHumidity(); break; case DHT_ERROR_CHECKSUM: temperature = myDHT22.getTemperatureC(); relHumid = myDHT22.getHumidity(); break; case DHT_BUS_HUNG: // Serial.println("BUS Hung "); break; case DHT_ERROR_NOT_PRESENT: // Serial.println("Not Present "); break; case DHT_ERROR_ACK_TOO_LONG: // Serial.println("ACK time out "); break; case DHT_ERROR_SYNC_TIMEOUT: // Serial.println("Sync Timeout "); break; case DHT_ERROR_DATA_TIMEOUT: // Serial.println("Data Timeout "); break; case DHT_ERROR_TOOQUICK: // Serial.println("Polled to quick "); break; } //end switch block } // end pollSensor Function. void logData() { getTime(); logger.print("H,"); logger.print(month); logger.print("/"); logger.print(monthDay); logger.print("/"); logger.print(year); logger.print(" "); logger.print(hour); logger.print(":"); if (minute < 10) { logger.print("0"); logger.print(minute); } else { logger.print(minute); } logger.print(":"); if (second < 10) { logger.print("0"); logger.print(second); } else { logger.print(second); } logger.print(","); logger.print(temperature, 1); logger.print(","); logger.println(relHumid, 1); Serial.print("H,"); Serial.print(month); Serial.print("/"); Serial.print(monthDay); Serial.print("/"); Serial.print(year); Serial.print(" "); Serial.print(hour); Serial.print(":"); if (minute < 10) { Serial.print("0"); Serial.print(minute); } else { Serial.print(minute); } Serial.print(":"); if (second < 10) { Serial.print("0"); Serial.print(second); } else { Serial.print(second); } Serial.print(","); Serial.print(temperature, 1); Serial.print(","); Serial.println(relHumid, 1); } void clearLCD(){ LCD.print(0xFE, BYTE); //command flag LCD.print(0x01, BYTE); //clear command. delay(10); } void powerDownLCD() { LCD.print(0x7C, BYTE); //issues special command LCD.print(128, BYTE); //turn off backlight LCD.print(0xFE, BYTE); //put in to extended LCD command mode LCD.print(0x08, BYTE); //off command. delay(10); displayState = 0; } void powerUpLCD() { LCD.print(0xFE, BYTE); //put in to extended LCD command mode LCD.print(0x0C, BYTE); //on command. LCD.print(0x7C, BYTE); //issues special command LCD.print(157, BYTE); //turn on backlight delay(10); displayState = 1; } //helper function for navigating around the LCD screen. void goTo(int row, int column) //starts w/ line 0 col 0 { LCD.print(0xFE, BYTE); LCD.print((column + row*64 + 128), BYTE); } // helper function to convert binary coded decimals from the real time clock to normal decimal numbers byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); } void getTime() { Wire.beginTransmission(DS1307_ADDRESS); Wire.send(0); Wire.endTransmission(); Wire.requestFrom(DS1307_ADDRESS, 7); second = bcdToDec(Wire.receive()); minute = bcdToDec(Wire.receive()); hour = bcdToDec(Wire.receive()); //24 hour time weekDay = bcdToDec(Wire.receive()); //0-6 -> sunday - Saturday monthDay = bcdToDec(Wire.receive()); month = bcdToDec(Wire.receive()); year = bcdToDec(Wire.receive()); }

