From 13d22c5d2ab2fdb15d268ce75e8a9c35d6781944 Mon Sep 17 00:00:00 2001 From: Mike Beattie Date: Tue, 20 Mar 2012 23:23:47 +1300 Subject: Initial import Signed-off-by: Mike Beattie --- EEPROM_IO.h | 24 +++ HouseControl.ino | 490 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 514 insertions(+) create mode 100644 EEPROM_IO.h create mode 100644 HouseControl.ino 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 + + +/*********************************************************************************/ +/* EEPROM read/write functions */ + +template 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 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 +#include +#include +#include +#include + +//#include +#include +#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) = + "Door Control" + "" + "" + "" + "" + "" + "" + "
" + "

15 Park Lane

" + "

Door Control

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Open
Unlocked
Closed
Open
Closed
Open
Closed
" + "
" + "" + ""; + + 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); + } +} + -- cgit v1.2.3