/*
Growroom logger/ device timer.
Logs humidity, temperature and soilmoisture.
Daily timer for 5 devices included
Pins used
0-1 Serial port
2,3,5,7,9 individual timer switches
4 DHT humidity sensor
6 Onewire-bus 4 x ds18b20 attached
8 pin for temporary powering gypsum soilsensors, temporary to prevent electrolysis
10-13 SD card to log CSV-files
A0-A3 Gypsum soil-wetness sensors http://www.cheapvegetablegardener.com/2009/11/how-to-make-cheap-soil-moisture-sensor-2.html
A4-A5 I2C bus
*/
#include <OneWire.h> // OneWire library needed for DS18b20 sensor http://www.pjrc.com/teensy/td_libs_OneWire.html
#include <DallasTemperature.h> // ds18b20 temperature sensor library http://milesburton.com/index.php?title=Dallas_Temperature_Control_Library
#include <DHT.h> // DHT library for DHT11, DHT21 and DHT22 https://github.com/adafruit/DHT-sensor-library
#include <Wire.h> // I2C library Standard arduino library
#include <Time.h> // Time library http://www.arduino.cc/playground/uploads/Code/Time.zip
#include <DS1307RTC.h> // a basic DS1307 library that returns time as Unix epoch http://www.arduino.cc/playground/uploads/Code/Time.zip
#include <EEPROM.h> // EEPROM library to support timer switch configuration Standard arduino library
#include <SdFat.h> // SDfat is used to read/write SD files http://code.google.com/p/sdfatlib/downloads/detail?name=sdfatlib20111205.zip&can=2&q=
#include <SdFatUtil.h> // SD tools library http://code.google.com/p/sdfatlib/downloads/detail?name=sdfatlib20111205.zip&can=2&q=
#define TIME_MSG_LEN 11 // time sync to PC is HEADER followed by unix time_t as ten ascii digits
#define TIME_HEADER 'T' // Header tag for serial time sync message
#define DHTPIN 4 // Data pin DHT 11 humidity sensor
#define ONE_WIRE_BUS 6 // data pin 1-wire bus
#define TEMPERATURE_PRECISION 12 // temperature sensor resolution 9-12 bits
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302)
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE); // name library
const int chipSelect = 10; // SPI SS pin for SD
Sd2Card card; // needed to read size SD and space free
SdVolume vol; // needed to read size SD and space free
SdFat sd; //
SdFile myFile; //
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
DeviceAddress t0, t1, t2, t3;
byte Debug = 0; // Used to display (1) msgs, or not (0) while developing
long tooktime;
float dhtdat; //Array to hold humidity data. DHT11
int save = 1; // store readings to disk 1/0
int show = 1; // send readings to serial port
int soilWetness[4]; // Array to store soil wetness results
byte deviceStatus[5];
unsigned long lastnow; // las noted value (second) of now()- function
int currentminute; // closes/opens file for writing every minute
float w[5]; // Temperature Array
char name[] = "01234567.CSV"; // variable used to store date based filename
int oh; // last detected hour
unsigned long deviceonAt[6], deviceoffAt[6]; // array for devicetimers
int devicePin[6]= {
0,2,3,5,7,9}; // pins used by daily timers
void writeconfig(void){
// === Timer for 1st device
EEPROM.write(0,22); // device on at Hour
EEPROM.write(1,0); // Minute
EEPROM.write(2,0); // Second
EEPROM.write(3,21); // device off at Hour
EEPROM.write(4,0); // Minute
EEPROM.write(5,0); // Second
// === Timer for 2nd device (Explantion as timer for first device)
EEPROM.write(6,15);
EEPROM.write(7,0);
EEPROM.write(8,0);
EEPROM.write(9,22);
EEPROM.write(10,0);
EEPROM.write(11,0);
// === Timer for 3rd device (Explantion as timer for first device)
EEPROM.write(12,23);
EEPROM.write(13,30);
EEPROM.write(14,2);
EEPROM.write(15,7);
EEPROM.write(16,30);
EEPROM.write(17,00);
// === Timer for 4th device (Explantion as timer for first device)
EEPROM.write(18,3);
EEPROM.write(19,0);
EEPROM.write(20,0);
EEPROM.write(21,16);
EEPROM.write(22,0);
EEPROM.write(23,0);
// === Timer for 5th device (Explantion as timer for first device)
EEPROM.write(24,22);
EEPROM.write(25,0);
EEPROM.write(26,0);
EEPROM.write(27,23);
EEPROM.write(28,0);
EEPROM.write(29,0);
}
void setup(void)
{
tooktime = micros(); // Debug variable to measure time
pinMode(8, OUTPUT); // configure moisture sensor "ON" pin
digitalWrite (8,LOW);// and pull low to prevent electrolyzing effects
dht.begin(); // start DHT humidity sensor
Serial.begin(115200); // start serial port
sensors.begin(); // onewire ds18b20 start
setres(); // onewire ds18b20 set resolution
setSyncProvider(RTC.get); // the function to get the time from the RTC
if(timeStatus()!= timeSet) Serial.println("RTC Error, clock not set");
if (!sensors.getAddress(t0, 0)) Serial.println("Error DS18B20 1");
if (!sensors.getAddress(t1, 1)) Serial.println("Error DS18B20 2");
if (!sensors.getAddress(t2, 2)) Serial.println("Error DS18B20 3");
if (!sensors.getAddress(t3, 3)) Serial.println("Error DS18B20 4");
if (save==1){
if (!sd.init(SPI_FULL_SPEED, chipSelect)) sd.initErrorHalt();
sd.chdir("/logs");
tos();
if (!myFile.open(name, O_RDWR | O_CREAT | O_AT_END)) {
sd.errorHalt("opening logfile failed.");
}
}
writeconfig(); // write device timer configuration
readconfig(); // read " " "
if (Debug){
Serial.print ("Setup took ");
Serial.print (micros()-tooktime);
Serial.println (" Micro - seconds.");
}
}
void loop(void){
Tupdate(); // check for external rtc update (T + 10 digit Unix epoch at serial port)
if (lastnow != now()){ // performs action needed every second
lastnow = now(); // log last detected second
if (oh != hour()) tos(); // change date based filename to new date if needed.
readsensors(); // reads all available sensors
DataDump(); // Dumps sensorinfo to SD/serial port
chk_devicetimer(); // Turns timer-devices on/off when needed.
}
}
time_t processSyncMessage() {
// return the time if a valid sync message is received on the serial port.
while(Serial.available() >= TIME_MSG_LEN ){ // time message consists of a header and ten ascii digits
char c = Serial.read() ;
Serial.print(c);
if( c == TIME_HEADER ) {
time_t pctime = 0;
for(int i=0; i < TIME_MSG_LEN -1; i++){
c = Serial.read();
if( c >= '0' && c <= '9'){
pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
}
}
return pctime;
}
}
return 0;
}
void setres(){
// Sets resolution of ds18b20 temperature sensors
sensors.setResolution(t0, TEMPERATURE_PRECISION);
sensors.setResolution(t1, TEMPERATURE_PRECISION);
sensors.setResolution(t2, TEMPERATURE_PRECISION);
sensors.setResolution(t3, TEMPERATURE_PRECISION);
}
void Tstamp(){
if (! myFile.timestamp(T_CREATE,year(),month(),day(),hour(), minute(),second())) {
Serial.println("SD create time error");
}
// set write/modification date time
if (!myFile.timestamp(T_WRITE,year(),month(),day(),hour(),minute(),second())) {
Serial.println("SD write time error");
}
// set access date
if (!myFile.timestamp(T_ACCESS,year(),month(),day(),hour(),minute(),second())) {
Serial.println("SD access time error");
}
}
void readTemp(void){
// Read temperature from 1-wire DS18B20 sensors
sensors.requestTemperatures(); // Send the command to get temperatures
w[0] = sensors.getTempC(t0);// and retrieve them
w[1] = sensors.getTempC(t1);
w[2] = sensors.getTempC(t2);
w[3] = sensors.getTempC(t3);
}
void Tupdate(void){
if(Serial.available()){
time_t t = processSyncMessage();
if(t >0){
RTC.set(t); // set the RTC and the system time to the received value
setTime(t);
}
}
}
void DataDump(void){
// fileWrite, logs time and last sensordata to current logfile.
if(save){
chkMinute();
myFile.print(lastnow);
myFile.print(",");
myFile.print(w[0]);
myFile.print(",");
myFile.print(w[1]);
myFile.print(",");
myFile.print(w[2]);
myFile.print(",");
myFile.print(w[3]);
myFile.print(",");
myFile.print(dhtdat);
myFile.print(",");
myFile.print(soilWetness[0]);
myFile.print(",");
myFile.print(soilWetness[1]);
myFile.print(",");
myFile.print(soilWetness[2]);
myFile.print(",");
myFile.print(soilWetness[3]);
myFile.print(",");
myFile.print(deviceStatus[0]);
myFile.print(",");
myFile.print(deviceStatus[1]);
myFile.print(",");
myFile.print(deviceStatus[2]);
myFile.print(",");
myFile.print(deviceStatus[3]);
myFile.print(",");
myFile.println(deviceStatus[4]);
}
if (show){
// serial portWrite, logs time and last sensordata to serial port.
Serial.print(lastnow);
Serial.print(",");
Serial.print(w[0]);
Serial.print(",");
Serial.print(w[1]);
Serial.print(",");
Serial.print(w[2]);
Serial.print(",");
Serial.print(w[3]);
Serial.print(",");
Serial.print(dhtdat);
Serial.print(",");
Serial.print(soilWetness[0]);
Serial.print(",");
Serial.print(soilWetness[1]);
Serial.print(",");
Serial.print(soilWetness[2]);
Serial.print(",");
Serial.print(soilWetness[3]);
Serial.print(",");
Serial.print(deviceStatus[0]);
Serial.print(",");
Serial.print(deviceStatus[1]);
Serial.print(",");
Serial.print(deviceStatus[2]);
Serial.print(",");
Serial.print(deviceStatus[3]);
Serial.print(",");
Serial.println(deviceStatus[4]);
}
}
char tos(void){
// Creates CSV logfile name based on date/time
//
// 20121110.CSV
// ||||----- century (20)12
// ||--- Month 11, november
// ||- Day 10
int rest;
int mo1;
int mo2;
int d1;
int d2;
int millenium = (year()/1000);
rest = year()-(millenium*1000);
int century = rest/100;
rest = rest - (century*100);
int decade = rest/10;
rest = rest -(decade *10);
mo1 = (month()/ 10);
mo2 = month()- (mo1 *10);
d1 = day()/10;
d2 = day() - (d1 *10);
oh = hour();
name[0] = char(50); // millenium (2)
name[1] = char(48); // century (0)
name[2] = char(decade+48); // decade
name[3] = char(rest+48); // year
name[4] = char(mo1+48); // first digit month
name[5] = char(mo2+48); // last '' ''
name[6] = char(d1+48); // first digit days
name[7] = char(d2+48); // last '' ''
}
void chkMinute(){
// Close and open file once a minute
if (save==1){
if (currentminute != minute()){
currentminute = minute();
Tstamp();// update date filestamp
myFile.close();
tos();// check if new hourly filename is needed
if (!myFile.open(name, O_RDWR | O_CREAT | O_AT_END)) {
sd.errorHalt("opening test.txt for write failed");
}
InfoSD();
}
}
}
void CHKearth(){
// check gypsum sensors
// Apply 5 volts to gypsym sensor/resistor voltage divider. Apply as short as possible to prevent electrolysis.
digitalWrite (8,HIGH);
delayMicroseconds(150);
soilWetness[0]=analogRead(A0);
delayMicroseconds(150);
soilWetness[1]=analogRead(A1);
delayMicroseconds(150);
soilWetness[2]=analogRead(A2);
delayMicroseconds(150);
soilWetness[3]=analogRead(A3);
digitalWrite (8,LOW);
}
void chk_devicetimer(void){
for (int x = 1; x < 6; x++){
deviceStatus[x-1] = lightcheck(x);
digitalWrite(devicePin[x],deviceStatus[x-1]);
}
}
int lightcheck(int devicenr){
int result; // result variable
unsigned long tc = (hour()*3600)+(minute()*60)+second();// calculate current minute
if (deviceonAt[devicenr] > deviceoffAt[devicenr]){ // starts late, stops earlier the following day
if (tc >= deviceoffAt[devicenr] && tc <deviceonAt[devicenr]) result = 0;
if (tc < deviceoffAt[devicenr] || tc >=deviceonAt[devicenr]) result = 1;
}
if (deviceonAt[devicenr] < deviceoffAt[devicenr]){ //Starts early, stops later same day
if (tc >= deviceoffAt[devicenr] || tc < deviceonAt[devicenr]) result = 0;
if (tc >= deviceonAt[devicenr] && tc < deviceoffAt[devicenr]) result = 1;
}
return result;
}
void readconfig(void){
// Read Configuration of growroom controller
//==========================================
// Read settings Growroom controller
// deviceonAt, deviceoffAt, daily timersettings
deviceonAt[1] = (EEPROM.read(0)*3600)+(EEPROM.read(1)*60)+EEPROM.read(2);
deviceoffAt[1] = (EEPROM.read(3)*3600)+(EEPROM.read(4)*60)+EEPROM.read(5);
deviceonAt[2] = (EEPROM.read(6)*3600)+(EEPROM.read(7)*60)+EEPROM.read(8);
deviceoffAt[2] = (EEPROM.read(9)*3600)+(EEPROM.read(10)*60)+EEPROM.read(11);
deviceonAt[3] = (EEPROM.read(12)*3600)+(EEPROM.read(13)*60)+EEPROM.read(14);
deviceoffAt[3] = (EEPROM.read(15)*3600)+(EEPROM.read(16)*60)+EEPROM.read(17);
deviceonAt[4] = (EEPROM.read(18)*3600)+(EEPROM.read(19)*60)+EEPROM.read(20);
deviceoffAt[4] = (EEPROM.read(21)*3600)+(EEPROM.read(22)*60)+EEPROM.read(23);
deviceonAt[5] = (EEPROM.read(24)*3600)+(EEPROM.read(25)*60)+EEPROM.read(26);
deviceoffAt[5] = (EEPROM.read(27)*3600)+(EEPROM.read(28)*60)+EEPROM.read(29);
}
void InfoSD(void) {
// list logfiles available on diskfile date and size
Serial.println("Logs Found, Size in bytes :");
Serial.println("===========================");
sd.chdir("/logs");
sd.ls(LS_DATE | LS_SIZE);
if (!card.init(SPI_FULL_SPEED, chipSelect)) {
//sdErrorMsg("\ncard.init failed");
return;
}
if (!vol.init(&card)) {
// sdErrorMsg("\nvol.init failed");
return;
}
int bpc = int(vol.blocksPerCluster());
long DS= (vol.clusterCount()*int(vol.blocksPerCluster()))/2 ;
long DF= (vol.freeClusterCount()*int(vol.blocksPerCluster()))/2 ;
Serial.print("=== Disksize : ");
Serial.print(DS/1024);
Serial.print(" MB, ");
Serial.print(DF/1024);
Serial.print(" MB Free, still room for about ");
Serial.print (DF/5540);
Serial.println(" days");
}
void readsensors(void){
tooktime = micros();
readTemp(); // read all ds18b20s temperature sensors
if (Debug){
Serial.print ("ds18b20 sensors took ");
Serial.print (micros()-tooktime);
Serial.println (" Micro - seconds.");
}
tooktime = micros();
dhtdat = dht.readHumidity(); // read dht air humidity sensor
if (isnan(dhtdat)) { // Error from DHT
Serial.println("Failed to read from DHT");
}
if (Debug){
Serial.print ("dht sensor took ");
Serial.print (micros()-tooktime);
Serial.println (" Micro - seconds.");
}
tooktime = micros();
CHKearth(); // read moisture sensors earth
if (Debug){
Serial.print ("dht sensor took ");
Serial.print (micros()-tooktime);
Serial.println (" Micro - seconds.");
}
}