/*================================================= Remote Home Controller 0.3 ===================================================*/ /* Arduino-based smart thermostat/home-device controller * version 0.3 * * CONNECTION for X10 Firecracker model C17A: RTS -> DB9 pin 7. DTR -> DB9 pin 4. Gnd. -> DB9 pin 5. * * CONNECTION for the wall wart plugged into X10 TM751 module should be such that the wall wart powers * a normally closed relay in series with the thermostat's normal switch. Powering the relay thus * disables the thermostat switch. * * CONNECTION for Dallas Semiconductor 18B20 digital temperature sensor: DQ -> Arduino data pin 9 * */ #include //Necessary Arduino libraries #include #include //Using "version 2.0" of the oneWire library #define RTS_PIN 2 // RTS line for C17A: DB9 pin 7 <--> Arduino pin 2 #define DTR_PIN 3 // DTR line for C17A: DB9 pin 4 <--> Arduino pin 3 #define BIT_DELAY 1 // mS delay between bits (1 mS OK)_ #define HS_Threshold_temp 70.0 //If in Economy mode, go as low as 50 deg. F during heating season #define CS_Threshold_temp 90.0 //If in Economy mode, go as high as 90 deg. F during cooling season #define maxLine 150 //Maximum length of email line to read //Write here the one-wire address for Dallas 18B20 found using the the other program (the Arduino sketch "18B20_Reader"). byte sensor[8] = {0x28, 0xA0, 0xD3, 0x43, 0x02, 0x00, 0x00, 0x75}; //For the one-wire interface (on pin 9) OneWire ow(9); //For Ethernet shield byte mac[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; //Assign unique address (say, add 1 to mac address of your computer). byte ip[] = { 192, 168, 0, 2 }; //Otherwise unused IP address on the local network byte POPserver[] = { XX, XX, XX, XX }; //IP address of your POP3 server Client popClient(POPserver, 110); //The default POP port is 110 byte SMTPserver[] = { XX, XX, XX, XX }; //IP address of your SMTP server Client smtpClient(SMTPserver, 25); //The default SMTP port is 25 //State variables boolean Heating_season = true; //Hard coded for the time being; could put on an external switch boolean Economy_mode = false; boolean LampB_on = false; //For up to three X10 TM751 modules set to house codes B, C, D (a module set to house code A is used to disable thermostat) boolean LampC_on = false; boolean LampD_on = false; boolean Need_to_send = false; //True if we need to send email status message float Current_temp; char inString[maxLine]; // Dimensioned to max. no. of characters to be collected in each line int i = 0; int j = 0; int numBytes = 0; //No. of bytes in current line char c; void setup() { X10.init(RTS_PIN, DTR_PIN, BIT_DELAY); //Init X10 Ethernet.begin(mac, ip); delay(1000); //NB: Need delay right after Ethernet.begin (don't ask me why) //Serial.begin(9600); //Uncomment if you need to use serial monitor for debugging //delay(1000); } void loop() { float temp; POPin(); //Log onto POP server and set state variables according to commands received if (Need_to_send == true) //Send email report of current house status? { temp = getTemperature(sensor); //Determine current temperature in deg. C Current_temp = c2f(temp); //Convert to F //Serial.println("Calling sendMail"); sendMail(); //Send email w/ status report Need_to_send = false; } if (LampB_on == true) //Turn lamp on B? (Requires additional TM751 set to house code B). { X10.sendCmd( hcB, 1, cmdOn ); } if (LampB_on == false) //Turn lamp B off if (LampC_on == true) //Turn lamp on C? (Requires additional TM751 set to house code C). { X10.sendCmd( hcC, 1, cmdOn ); } if (LampC_on == false) //Turn lamp C off { X10.sendCmd( hcC, 1, cmdOff ); } if (LampD_on == true) //Turn lamp on D? (Requires additional TM751 set to house code D). { X10.sendCmd( hcD, 1, cmdOn ); } if (LampD_on == false) //Turn lamp D off { X10.sendCmd( hcD, 1, cmdOff ); } for (i = 0; i < 10; i++) //For 10 minutes, do nothing other than maintain proper house temperature { KeepTemp(); //Maintain house temperature delay (60000); //Wait 1 minute } } /*======================================== Functions =========================================*/ //HVAC feedback control void KeepTemp() { float temp; temp = getTemperature(sensor); //Determine current temperature in deg. C Current_temp = c2f(temp); //Convert to F if (Economy_mode == true) //May need to override thermostat { if (Heating_season == true) { if (Current_temp < HS_Threshold_temp) { X10.sendCmd( hcA, 1, cmdOff ); //Turning off relay enables normal thermostat switch } else { X10.sendCmd( hcA, 1, cmdOn ); //Turning on relay disables thermostat switch } } else //Must be cooling season { if (Current_temp > CS_Threshold_temp) { X10.sendCmd( hcA, 1, cmdOff ); //Enable thermostat switch } else { X10.sendCmd( hcA, 1, cmdOn ); //Disable thermostat switch } } } else //Normal mode { X10.sendCmd( hcA, 1, cmdOff ); //Enable thermostat switch } } // //Internet communications functions // void POPin() // Checks in with POP server, reads one message and sets state variables appropriately { //Serial.println("Connecting to POP server..."); //debugging line if (popClient.connect()) //Log onto the POP server { //Serial.println("Connected"); //debugging line popClient.println("user USER_NAME_HERE"); //Provide email user name delay(1000); popClient.println("pass PASSWORD_HERE"); //Provide password delay(1000); popClient.flush(); //Serial.println("Retrieving message 1"); popClient.println("retr 1"); //Try to retrieve a message (worst that can happen is that there is none). delay(1000); //Give server some time to respond for (j = 0; j < 100 ; j++) //Read at most 100 lines of message (enough to get through header, and some) { numBytes = getLine(); //Get one line from server (may just be error report that there are no messages to retrieve). if (numBytes != -1) //If there's stuff { //Serial.println(inString); if (inString[0] == '.') //Line begins with "." => end of email message { popClient.println("dele 1"); //Delete message from POP server delay(1000); //Serial.println("Logging off of POP server"); popClient.println("quit"); //Log off of POP server popClient.stop(); //Shut down the Web client } else //Test whether this line starts with a command { if ((inString[0] == 'A') && (inString[1] == 'B') && (inString[2] == 'C') && (inString[3] == 'D') && (inString[4] == 'E') && (inString[5] == ':')) // If line starts with "ABCDE:", then the character(s) that follows is a command //NB: Change this 6-character prefix to suit you, making it your private security code for commanded actions specified by next one or two characters in the line. { //Set state variables according to received command if ((inString[6] == 'L') && (inString[7] == 'B')) //LB for light the lamp B { LampB_on = true; } if ((inString[6] == 'K') && (inString[7] == 'B')) //KB for kill (turn off) lamp B { //Serial.println("Turning off Lamp B"); LampB_on = false; } if ((inString[6] == 'L') && (inString[7] == 'C')) //LC for light the lamp C { LampC_on = true; } if ((inString[6] == 'K') && (inString[7] == 'C')) //KC for kill (turn off) lamp A { LampC_on = false; } if ((inString[6] == 'L') && (inString[7] == 'D')) //LD for light the lamp D { LampD_on = true; } if ((inString[6] == 'K') && (inString[7] == 'D')) //KD for kill (turn off) lamp D { LampD_on = false; } if (inString[6] == 'E') //E for set economy heating/cooling mode { Economy_mode = true; } if (inString[6] == 'N') //N for set normal heating/cooling mode { Economy_mode = false; } if (inString[6] == 'S') //S for send send a status report by email { Need_to_send = true; } } } } } popClient.println("quit"); //Log off of POP server popClient.stop(); //Shut down the Web client } else { Serial.println("connection failed"); //debugging line } } int getLine() //Stuffs one line worth of characters into inString array (terminated w/0) //returns index of final byte in array (but no larger than maxLine) //or -1 if no data are available { delay(100); //Give mail server some time to send more stuff for (i = 0; i < maxLine ; i++) { if (popClient.available()) { c = popClient.read(); if (c != 13) //If not a CR { inString[i] = c; } else { inString[i] = 0; //Arduino strings terminate with 0 c = popClient.read(); //Read following LF lest that confuse us later return(i); } } else { return(-1); } } return(maxLine); } // //Send email message reporting current temperature // void sendMail() { if (smtpClient.connect()) //Log onto the SMTP server { //Serial.println("Connected to SMTP server"); //Debugging line smtpClient.println("EHLO YOUR_SMTP_SERVER.com"); delay(1000); smtpClient.println("MAIL FROM: YourThermostatsEmailAccount@yourISP.com"); //Email account that was set up for your home-control robot delay(1000); smtpClient.println("RCPT TO: youremailaddress@yourEmailprovider.com"); //Email account to which to send notifications of house status delay(1000); smtpClient.println("DATA"); delay(1000); smtpClient.println("From: YourThermostatsEmailAccount@yourISP.com"); //Email account that was set up for your home-control robot delay(1000); smtpClient.println("To: youremailaddress@yourEmailprovider.com"); //Email account to which to send notifications of house status delay(1000); smtpClient.println("Subject: Status report from home-control robot"); delay(1000); smtpClient.println(""); //May need second CR delay(1000); smtpClient.print("Temperature is "); delay(1000); smtpClient.print(Current_temp); delay(1000); smtpClient.println(" F."); delay(1000); smtpClient.print("HVAC control is in "); delay(1000); if (Economy_mode == true) { smtpClient.println("economy mode."); } else { smtpClient.println("normal mode."); } smtpClient.print("Lamp B is "); delay(1000); if (LampB_on == true) { smtpClient.println("on."); } else { smtpClient.println("off."); } delay(1000); smtpClient.print("Lamp C is "); delay(1000); if (LampC_on == true) { smtpClient.println("on."); } else { smtpClient.println("off."); } delay(1000); smtpClient.print("Lamp D is "); delay(1000); if (LampD_on == true) { smtpClient.println("on."); } else { smtpClient.println("off."); } delay(1000); smtpClient.println("."); //Ends message delay(1000); smtpClient.flush(); smtpClient.println("QUIT"); //Log off of POP server smtpClient.stop(); //Shut down the Web client } else { //Serial.println("Not connected to SMTP server"); } } // //Functions for temperature sensing // void writeTimeToScratchpad(byte* address){ //reset the bus ow.reset(); //select our sensor ow.select(address); //CONVERT T function call (44h) which puts the temperature into the scratchpad ow.write(0x44,1); //sleep a second for the write to take place delay(1000); } void readTimeFromScratchpad(byte* address, byte* data){ //reset the bus ow.reset(); //select our sensor ow.select(address); //read the scratchpad (BEh) ow.write(0xBE); for (byte i=0;i<9;i++){ data[i] = ow.read(); } } float getTemperature(byte* address){ int tr; byte data[12]; float temp; writeTimeToScratchpad(address); readTimeFromScratchpad(address,data); //put in temp all the 8 bits of MSB (least significant byte) tr = data[1]; //check for negative temperature if (data[1] > 0x80){ tr = !tr + 1; //two's complement adjustment tr = tr * -1; //flip value negative. } temp = tr * 256 + data[0]; //MSB + LSB temp = temp/16.0; return temp; } //fahrenheit to celsius conversion float f2c(float val){ float aux = val - 32; return (aux * 5 / 9); } //celsius to fahrenheit conversion float c2f(float val){ float aux = (val * 9 / 5); return (aux + 32); }