/* * * */ #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; byte dawn; byte dusk; }; 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.configVersion = 3; 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] = 1; config.ntpServer[3] = 1; config.UTC_offset = 1300; config.dawn = 7; config.dusk = 21; 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 - lastNTPtime) >= 600) sendNTPpacket(); if (NTPSocket.available() >= 46) parseNTPresponse(); if (frontDoor.controlActive && ((time - frontDoor.controlTime) >= 10) ) { toggleDoorControl(&frontDoor); } if (garageDoor1.controlActive && ((time - garageDoor1.controlTime) >= 1) ) { toggleDoorControl(&garageDoor1); } 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); } }