diff options
Diffstat (limited to 'HouseControl.ino')
-rw-r--r-- | HouseControl.ino | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/HouseControl.ino b/HouseControl.ino new file mode 100644 index 0000000..2d66d36 --- /dev/null +++ b/HouseControl.ino @@ -0,0 +1,490 @@ +/* + * + * + */ + +#include <Time.h> +#include <SPI.h> +#include <Ethernet.h> +#include <EthernetUdp.h> +#include <WebServer.h> + +//#include <MemoryFree.h> +#include <EEPROM.h> +#include "EEPROM_IO.h" + +#define RECONFIGURE 0 + +/*********************************************************************************/ +/* Pin definitions */ + +#define METER_PULSE 2 + +#define STATUS_LED 7 + +#define FDOOR_STRIKE 8 +#define FDOOR_CLOSED 9 + +#define GDOOR1_ACTIVATE 3 +#define GDOOR1_OPEN 0 +#define GDOOR1_CLOSED 1 + +#define GDOOR2_ACTIVATE 6 +#define GDOOR2_OPEN 4 +#define GDOOR2_CLOSED 5 + +/*********************************************************************************/ +/* Typedefs/datastructures */ + +struct st_config { + byte configVersion; + + byte mac[6]; + byte def_ip[4]; + byte def_netmask[4]; + byte def_gateway[4]; + + byte ntpServer[4]; + unsigned int UTC_offset; + + byte notifyHost[4]; + unsigned int notifyPort; +}; + +struct st_door { + byte openSensePin; + byte closedSensePin; + byte controlPin; + + boolean open; + boolean closed; + boolean last_open; + boolean last_closed; + boolean controlActive; + + time_t controlTime; +}; + +/*********************************************************************************/ +/* Variable declarations */ + +time_t time; +time_t blinkTime = 0; + +struct st_config config; + +struct st_door frontDoor; +struct st_door garageDoor1; +struct st_door garageDoor2; + +EthernetUDP NTPSocket; +time_t lastNTPtime = 0; + +//Server telnetServer(23); +WebServer httpServer("", 80); + +/*********************************************************************************/ +/* Utility functions */ + +/* +const char* ip_to_str(const uint8_t* ipAddr) { + static char buf[16]; + sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]); + return buf; +} +*/ + +/*********************************************************************************/ +/* NTP/Time functions */ + +// send an NTP request to the time server at the given address +void sendNTPpacket() { + // Packet buffer + byte pb[48]; + // set all bytes in the buffer to 0 + memset(pb, 0, sizeof(pb)); + // Initialize values needed to form NTP request + pb[0] = 0b11100011; // LI, Version, Mode + pb[1] = 0; // Stratum, or type of clock + pb[2] = 6; // Polling Interval + pb[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + pb[12] = 49; + pb[13] = 0x4E; + pb[14] = 49; + pb[15] = 52; + + // all NTP fields have been given values, now + // we send a packet requesting a timestamp: + NTPSocket.beginPacket(config.ntpServer, 123); + NTPSocket.write(pb, sizeof(pb)); + NTPSocket.endPacket(); +} + +void parseNTPresponse() { + byte pb[48]; + unsigned long ntp_time = 0; + float ntp_time_frac; + + // read the packet into the buffer + NTPSocket.read(pb, sizeof(pb)); + + // NTP contains four timestamps with an integer part and a fraction part + // we only use the integer part here + for (int i=40; i<44; i++) + ntp_time = ntp_time << 8 | pb[i]; + + // part of the fractional part + // could be 4 bytes but this is more precise than the 1307 RTC + // which has a precision of ONE second + // in fact one byte is sufficient for 1307 + ntp_time_frac = ((long)pb[44] * 256 + pb[45]) / 65536.0; + + // convert NTP to UNIX time, differs seventy years = 2208988800 seconds + // NTP starts Jan 1, 1900 + // Unix time starts on Jan 1 1970. + ntp_time -= 2208988800UL; + + // Adjust timezone and DST... + ntp_time += (config.UTC_offset * 3600L) / 100L; // Notice the L for long calculations!! + if (ntp_time_frac > 0.4) ntp_time++; // adjust fractional part, see above + + setTime(ntp_time); + + lastNTPtime = now(); +} + +/*********************************************************************************/ +/* HTTP Handler function */ + +void httpHandler(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) { + /* store the HTML in program memory using the P macro */ + P(message) = + "<html><head><title>Door Control</title>" + "<meta id='viewport' name='viewport' content='width=160; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;'>" + "<style>" + "body {background-color:#bbb;font-family:'Verdana',sans-serif}" + "h2 {line-height:50%;}" + "h3 {line-height:5%;}" + ".page {text-align:center;}" + ".centred-table,.dobutton {text-align:center;margin-left:auto;margin-right:auto;}" + ".button {padding-bottom:5px;}" + ".dobutton {height:50px;width:200px;}" + ".unlockedlabel,.openlabel,.closedlabel,.statelabel {background-color:#a5a5a5;padding:3px;border-radius:4px;}" + ".lockedlabel {visibility:hidden;}" + ".unlockedlabel {background-color:#55f;}" + ".openlabel {background-color:#f55;}" + ".closedlabel {background-color:#5f5;}" + "</style>" + "<script type='text/javascript' src='http://code.jquery.com/jquery-1.5.2.min.js'></script>" + "<script type='text/javascript'>" + "$(document).ready(function() {" + " $('#fda').click(function(){$.post('do', {frontdoor: 1});});" + " $('#gd1a').click(function(){$.post('do', {garagedoor1: 1});});" + " $('#gd2a').click(function(){$.post('do', {garagedoor2: 1});});" + " updateStatus();" + "});" + "function updateStatus() {" + " $.getJSON('json', function(data) {" + " $.each(data, function(key, val) {" + " $('#' + key).attr('class', val + 'label');" + " });" + " setTimeout(updateStatus, 1000);" + " });" + "}" + "</script>" + "</head>" + "<body>" + "<div class=page>" + "<h2>15 Park Lane</h2>" + "<h3>Door Control</h3>" + "<table class=centred-table>" + "<tr>" + "<td><div id=fdo class=statelabel>Open</div></td>" + "<td><div id=fdu class=lockedlabel>Unlocked</div></td>" + "<td><div id=fdc class=statelabel>Closed</div></td>" + "</tr>" + "<tr>" + "<td colspan=3><div class=button><input type=button class=dobutton id=fda value='Front Door' /></div></td>" + "</tr>" + "<tr>" + "<td><div id=gd1o class=statelabel>Open</div></td>" + "<td></td>" + "<td><div id=gd1c class=statelabel>Closed</div></td>" + "</tr>" + "<tr>" + "<td colspan=3><div class=button><input type=button class=dobutton id=gd1a value='Garage Door 1' /></div></td>" + "</tr>" + "<tr>" + "<td><div id=gd2o class=statelabel>Open</div></td>" + "<td></td>" + "<td><div id=gd2c class=statelabel>Closed</div></td>" + "</tr>" + "<tr>" + "<td colspan=3><div class=button><input type=button class=dobutton id=gd2a value='Garage Door 2' /></div></td>" + "</tr>" + "</table>" + "</div>" + "</body>" + "</html>"; + + server.printP(message); + } +} + +void ajaxHandler(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess("application/json"); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) { + /* store the HTML in program memory using the P macro */ + + server.write("{\"fdo\":\""); + server.write(((frontDoor.open)?"open":"state")); + server.write("\", \"fdc\":\""); + server.write(((frontDoor.closed)?"closed":"state")); + server.write("\", \"fdu\":\""); + server.write(((frontDoor.controlActive)?"unlocked":"locked")); + server.write("\", \"gd1o\":\""); + server.write(((garageDoor1.open)?"open":"state")); + server.write("\", \"gd1c\":\""); + server.write(((garageDoor1.closed)?"closed":"state")); + server.write("\", \"gd2o\":\""); + server.write(((garageDoor2.open)?"open":"state")); + server.write("\", \"gd2c\":\""); + server.write(((garageDoor2.closed)?"closed":"state")); + server.write("\"}"); + } +} + +void doHandler(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { + if (type == WebServer::POST) { + bool repeat; + char name[16], value[16]; + do { + repeat = server.readPOSTparam(name, 16, value, 16); + + if ((strcmp(name, "frontdoor") == 0)) + toggleDoorControl(&frontDoor); + + if ((strcmp(name, "garagedoor1") == 0)) + toggleDoorControl(&garageDoor1); + + if ((strcmp(name, "garagedoor2") == 0)) + toggleDoorControl(&garageDoor2); + + } while (repeat); + server.httpSuccess(); + server.write("OK"); + } + + server.httpSeeOther("/"); + return; +} + + +/*********************************************************************************/ +/* Miscellaneous functions */ + +void setupDoor (struct st_door *door, byte cp, byte csp, byte osp) { + door->controlPin = cp; + door->closedSensePin = csp; + door->openSensePin = osp; + door->controlActive = false; + + pinMode(door->controlPin, OUTPUT); + pinMode(door->closedSensePin, INPUT); + digitalWrite(door->closedSensePin, HIGH); + + if (door->openSensePin != 255) { + pinMode(door->openSensePin, INPUT); + digitalWrite(door->openSensePin, HIGH); + } +} + +boolean readDoorState (struct st_door *door) { + door->last_closed = door->closed; + door->closed = !digitalRead(door->closedSensePin); + + door->last_open = door->open; + if (door->openSensePin != 255) { + door->open = !digitalRead(door->openSensePin); + } else { + door->open = !door->closed; + } + + if ((door->last_open != door->open) || (door->last_closed != door->closed)) + return true; + + return false; +} + +void toggleDoorControl(struct st_door *door) { + if (!door->controlActive) { + digitalWrite(door->controlPin, HIGH); + door->controlTime = now(); + } else { + digitalWrite(door->controlPin, LOW); + } + door->controlActive = !door->controlActive; +} + +/*********************************************************************************/ +/* Setup function */ + +void setup() { +#if RECONFIGURE + config.mac[0] = 0x00; + config.mac[1] = 0xA5; + config.mac[2] = 0xCB; + config.mac[3] = 0x28; + config.mac[4] = 0xF4; + config.mac[5] = 0xCD; + + config.def_ip[0] = config.def_gateway[0] = 10; + config.def_ip[1] = config.def_gateway[1] = 113; + config.def_ip[2] = config.def_gateway[2] = 1; + config.def_ip[3] = 160; + config.def_gateway[3] = 254; + + config.def_netmask[0] = config.def_netmask[1] = config.def_netmask[2] = 255; + config.def_netmask[3] = 0; + + config.notifyHost[0] = 10; + config.notifyHost[1] = 113; + config.notifyHost[2] = 1; + config.notifyHost[3] = 255; + config.notifyPort = 8888; + + config.ntpServer[0] = 10; + config.ntpServer[1] = 113; + config.ntpServer[2] = config.ntpServer[3] = 1; + config.UTC_offset = 1300; + + eepromWrite(0, config); +#endif + + eepromRead(0, config); + + if(Ethernet.begin(config.mac) == 0) { + Ethernet.begin(config.mac, config.def_ip, config.def_gateway, config.def_netmask); + } + +// Udp.begin(config.notifyPort); + + NTPSocket.begin(123); + + httpServer.setDefaultCommand(&httpHandler); + httpServer.addCommand("json", &ajaxHandler); + httpServer.addCommand("do", &doHandler); + httpServer.begin(); + + setupDoor(&frontDoor, FDOOR_STRIKE, FDOOR_CLOSED, 255); + setupDoor(&garageDoor1, GDOOR1_ACTIVATE, GDOOR1_CLOSED, GDOOR1_OPEN); + setupDoor(&garageDoor2, GDOOR2_ACTIVATE, GDOOR2_CLOSED, GDOOR2_OPEN); + + pinMode(STATUS_LED, OUTPUT); + + sendNTPpacket(); + +} + +/*********************************************************************************/ +/* MAIN PROGRAM */ + +void loop() { + + time = now(); + + readDoorState(&frontDoor); + readDoorState(&garageDoor1); + readDoorState(&garageDoor2); + +/* + if ((time - lastStatTime) > 10) { + lastStatTime = time; + + Serial.println(); + Serial.print("Time:\t"); + PrintDateTime(now()); + Serial.println(); + + Serial.print("Bytes free in SRAM: "); + Serial.println(freeMemory(),DEC); + + Serial.print("Front door: "); + if (frontDoor.closed) { + Serial.print("closed"); + } else { + Serial.print("open"); + } + if (frontDoor.controlActive) { + Serial.println(" UNLOCKED!"); + } else { + Serial.println(); + } + + Serial.print("Garage door: "); + if (garageDoor1.open) { + Serial.println("open"); + } else if (garageDoor1.closed) { + Serial.println("closed"); + } else { + Serial.println(".. moving .."); + } + + } +*/ + + if ((time - lastNTPtime) >= 600) + sendNTPpacket(); + + if (NTPSocket.available() >= 46) + parseNTPresponse(); + + if (frontDoor.controlActive && ((time - frontDoor.controlTime) >= 10) ) { +// Serial.println("De-activating Front Door strike"); + toggleDoorControl(&frontDoor); + } + + if (garageDoor1.controlActive && ((time - garageDoor1.controlTime) >= 1) ) { +// Serial.println("De-toggling garage door switch"); + toggleDoorControl(&garageDoor1); + } + +/* + if (Serial.available() > 0) { + byte inByte = Serial.read(); + if (inByte == 'D') { + Serial.println("Activating Garage door"); + toggleDoorControl(&garageDoor1); + PrintDateTime(garageDoor1.controlTime); + } else if (inByte == 'F') { + Serial.println("Activating Front door strike..."); + toggleDoorControl(&frontDoor); + PrintDateTime(frontDoor.controlTime); + } else { + Serial.print("Unknown command: "); + Serial.println(inByte, BYTE); + } + } +*/ + + httpServer.processConnection(); + + /* Toggle Status LED so we know things are OK */ + if (time != blinkTime) { + blinkTime = time; + digitalWrite(STATUS_LED, HIGH); + delay(80); + digitalWrite(STATUS_LED, LOW); + } +} + |