summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Beattie <mike@ethernal.org>2012-03-20 23:23:47 +1300
committerMike Beattie <mike@ethernal.org>2012-03-20 23:23:47 +1300
commit13d22c5d2ab2fdb15d268ce75e8a9c35d6781944 (patch)
tree6e79bbe01e0cd7d6d0ba65a840739527858e50cc
Initial import
Signed-off-by: Mike Beattie <mike@ethernal.org>
-rw-r--r--EEPROM_IO.h24
-rw-r--r--HouseControl.ino490
2 files changed, 514 insertions, 0 deletions
diff --git a/EEPROM_IO.h b/EEPROM_IO.h
new file mode 100644
index 0000000..1c59780
--- /dev/null
+++ b/EEPROM_IO.h
@@ -0,0 +1,24 @@
+#include <Arduino.h>
+
+
+/*********************************************************************************/
+/* EEPROM read/write functions */
+
+template <class T> int eepromWrite(int ee, const T& value) {
+ const byte* p = (const byte*)(const void*)&value;
+ unsigned int i;
+ for (i = 0; i < sizeof(value); i++)
+ EEPROM.write(ee++, *p++);
+ return i;
+}
+
+
+template <class T> int eepromRead(int ee, T& value) {
+ byte* p = (byte*)(void*)&value;
+ unsigned int i;
+ for (i = 0; i < sizeof(value); i++)
+ *p++ = EEPROM.read(ee++);
+ return i;
+}
+
+
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);
+ }
+}
+