Advertisement
Guest User

Untitled

a guest
Mar 11th, 2016
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.63 KB | None | 0 0
  1. /**
  2. * Chicken Coop Door/Light controller for ESP8266 board.
  3. * Copyright 2016, Ioan Ghip <ioanghip (at) gmail (dot) com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18. * 02110-1301, USA.
  19. */
  20.  
  21. #include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
  22. #include <WiFiClient.h>
  23. #include <ESP8266WebServer.h>
  24. #include <ESP8266HTTPClient.h>
  25. #include <TinyGPS++.h>            //https://github.com/mikalhart/TinyGPSPlus
  26. #include <SoftwareSerial.h>
  27. #include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
  28. #include <TimeLord.h>             //https://github.com/probonopd/TimeLord
  29. #include <TimeLib.h>              //https://github.com/PaulStoffregen/Time
  30. #include <TimeAlarms.h>           //https://github.com/PaulStoffregen/TimeAlarms
  31. #include <EEPROM.h>               //http://playground.arduino.cc/Code/EEPROMWriteAnything
  32. #include <Arduino.h>
  33.  
  34. // we don't need to send message to serial if not connected to a computer. Comment the following line if not debuging
  35. #define __DEBUG__
  36.  
  37. #if defined (__DEBUG__)
  38. #define DEBUG_begin(val) Serial.begin(val)
  39. #define DEBUG_print(val) Serial.print(val)
  40. #define DEBUG_println(val) Serial.println(val)
  41. #else
  42. #define DEBUG_begin(val)
  43. #define DEBUG_print(val)
  44. #define DEBUG_println(val)
  45. #endif
  46.  
  47. #define COOP_VERSION "5"
  48.  
  49. const String HTTP_HEAD_COOP = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>Chicken Coop</title>";
  50. const String HTTP_STYLE_COOP = "<style>div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} input[type=\"submit\"]{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;}</style>";
  51. const String HTTP_HEAD_END_COOP = "</head><body><div style='font-family: verdana; text-align: left; display: inline-block;'><h1>Chicken Coop</h1>";
  52. const String HTTP_END_COOP = "</div></body></html>";
  53. const String HTTP_BUTTON_COOP = "<p><form action=\"{ACT}\" method=\"get\"><button>{TXT}</button></form></p>";
  54. const String HTTP_FORM_SAVE_COOP = "<p><form action=\"/SAVE\" method=\"get\"><p><input type=\"text\" name=\"timezone\"  value=\"{TZ}\" onkeypress='return (event.charCode >= 48 && event.charCode <= 57) || (event.charCode == 45)'></input></p><input type=\"hidden\" name=\"v\" value=\"{VER}\"><p><input style=button type=\"submit\" value=\"Save settings\"></p></form></p>";
  55. const String HTTP_LINK_RESET_COOP = "<p><a href=\"/RESET\" onclick=\"return confirm('Are you sure?');\">Change WiFi configuration</a></p>";
  56. const String HTTP_LINK_CONFIG_COOP = "<p><a href=\"/CONFIG\">Time zone</a></p>";
  57. const String HTTP_BEGIN_COOP = HTTP_HEAD_COOP + HTTP_STYLE_COOP + HTTP_HEAD_END_COOP;
  58.  
  59. const String GOOGLE_DYNDNS_USERNAME = "doesnt work";
  60. const String GOOGLE_DYNDNS_PASSWORD = "doesnt work yet";
  61. const String GOOGLE_DYNDNS_DOMAIN = "coop.marginallyhandy.com";
  62. const String GOOGLE_DYNDNS = "http://" + GOOGLE_DYNDNS_USERNAME + ":" + GOOGLE_DYNDNS_PASSWORD + "@domains.google.com/nic/update?hostname=" + GOOGLE_DYNDNS_DOMAIN + "&myip={IP}";
  63.  
  64. int door_relay1 = 2; // gpo2 - d4
  65. int light_relay = 14; // gop14 - d5
  66.  
  67. int door_open_button = 12; // gpo12 - d6
  68. int door_close_button = 5; // gpo5 - d1
  69.  
  70. // what is our longitude (west values negative) and latitude (south values negative)
  71. float latitude = 44.9308; // the value is going to be replaced by the GPS
  72. float longitude = -123.0289; // the value is going to be replaced by the GPS
  73. int timezone = -8; // get this from the user
  74.  
  75. // we will syncronize the time with an GPS connected tot pin gpo13 -> gps TX, gpo15 -> gpx RX
  76. static const int RXPin = 13; // gpo13 - d7
  77. static const int TXPin = 15; // gpo15 - d8
  78. static const uint32_t GPSBaud = 38400;
  79.  
  80. SoftwareSerial SerialGPS = SoftwareSerial(RXPin, TXPin);
  81. TinyGPSPlus gps;
  82.  
  83. // TimeLord library for calculating sunset / sunrise times
  84. TimeLord sunrisesunset;
  85. // what TimeLord lib returns will be stored here:
  86. byte todaySunRiseHour;
  87. byte todaySunRiseMinute;
  88. byte todaySunSetHour;
  89. byte todaySunSetMinute;
  90.  
  91. boolean isDoorOpen = true;
  92.  
  93. /* Set these to your desired credentials. */
  94. const char *ssid = "ChickenCoop";
  95. const char *password = "12345678";
  96.  
  97. ESP8266WebServer webServer(80);
  98.  
  99. void setup()
  100. {
  101.     pinMode(door_relay1, OUTPUT);
  102.     pinMode(light_relay, OUTPUT);
  103.  
  104.     digitalWrite(door_relay1, LOW);
  105.     digitalWrite(light_relay, LOW);
  106.  
  107.     // init serial for time sync with the GPS
  108.     SerialGPS.begin(GPSBaud);
  109.  
  110.     // init serial for debugging
  111.     DEBUG_begin(115200);
  112.     DEBUG_print("Chicken Coop v");
  113.     DEBUG_println(COOP_VERSION);
  114.  
  115.     // init the eeprom
  116.     EEPROM.begin(512);
  117.  
  118.     String ip;
  119.  
  120.     if (isAPFlag())
  121.     {
  122.         /* You can remove the password parameter if you want the AP to be open. */
  123.         WiFi.softAP(ssid, password);
  124.         DEBUG_print("AP Mode, IP address: ");
  125.     }
  126.     else
  127.     {
  128.         //WiFiManager: Local intialization. Once its business is done, there is no need to keep it around
  129.         WiFiManager wifi;
  130.         //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
  131.         wifi.setAPCallback(configModeCallback);
  132.         if (!wifi.autoConnect("ChickenCoop"))
  133.         {
  134.             DEBUG_println("failed to connect and hit timeout");
  135.             //reset and try again, or maybe put it to deep sleep
  136.             ESP.reset();
  137.             Alarm.delay(1000);
  138.         }
  139.         //if you get here you have connected to the WiFi
  140.         DEBUG_print("WiFi Mode, IP address: ");
  141.     }
  142.  
  143.     DEBUG_println(WiFi.localIP());
  144.  
  145.     // Store IP on Google Dynamic DNS - doesn't work yet
  146.     //dynDNS();
  147.  
  148.     // Match the request for the web server
  149.     webServer.on("/", mainPage);
  150.     webServer.on("/ACT=OPEN", openDoor);
  151.     webServer.on("/ACT=CLOSE", closeDoor);
  152.     webServer.on("/ACT=LON", lightOn);
  153.     webServer.on("/ACT=LOFF", lightOff);
  154.     webServer.on("/SAVE", []()
  155.     {
  156.         int ltz = (int)webServer.arg("timezone").toInt();
  157.         if ((ltz > -13) || (ltz < 15))
  158.         {
  159.             timezone = ltz;
  160.             // save the new Time Zone value in eeprom
  161.             writeTZ(timezone);
  162.         }
  163.         mainPage();
  164.     });
  165.     webServer.on("/RESET", resetToAPPage);
  166.     webServer.on("/CONFIG", configPage);
  167.     webServer.on("/AP", doAP);
  168.     webServer.on("/WIFI", doWiFi);
  169.     // Start the web server
  170.     webServer.begin();
  171.  
  172.     getTimeZone(); // retrieve timezone from eprom
  173.     setTheClock(); // wait for GPS fix and then set the date/time
  174.  
  175.     // create the alarms
  176.     // schedule alarms for today, do it once now
  177.     Alarm.timerOnce(60, setAllAlarmsForTheDay);
  178.     // every hour, sync the clock
  179.     Alarm.timerRepeat(60 * 60, setTheClock);
  180.     // recalculate times and schedule alarms for the day. Do it around middle of the night
  181.     Alarm.alarmRepeat(1, 30, 0, setAllAlarmsForTheDay);
  182. }
  183.  
  184. void loop()
  185. {
  186.     webServer.handleClient();
  187.     Alarm.delay(0);
  188. }
  189.  
  190. String makeButton(String action, String text)
  191. {
  192.     String btn = HTTP_BUTTON_COOP;
  193.     btn.replace("{ACT}", action);
  194.     btn.replace("{TXT}", text);
  195.     return btn;
  196. }
  197.  
  198. void mainPage()
  199. {
  200.     String page = HTTP_BEGIN_COOP;
  201.  
  202.     page += "Coop time is: " + String(hour()) + ":" + String(niceMinuteSecond(minute())) + ", " + String(day()) + "/" + String(month()) + "/" + String(year()) + "<br>";
  203.     page += "Door open today: " + String(todaySunRiseHour) + ":" + String(niceMinuteSecond(todaySunRiseMinute)) + "<br>";
  204.     page += "Door close today: " +  String(todaySunSetHour + 1) + ":" + String(niceMinuteSecond(todaySunSetMinute)) + "<br>";
  205.  
  206.     webServer.on("/ACT=OPEN", openDoor);
  207.     webServer.on("/ACT=CLOSE", closeDoor);
  208.     webServer.on("/ACT=LON", lightOn);
  209.     webServer.on("/ACT=LOFF", lightOff);
  210.  
  211.     if (isDoorOpen)
  212.     {
  213.         page += makeButton("/ACT=CLOSE", "Close door");
  214.     }
  215.     else
  216.     {
  217.         page += makeButton("/ACT=OPEN", "Open door");
  218.     }
  219.  
  220.     if (isLightOn())
  221.     {
  222.         page += makeButton("/ACT=LOFF", "Light off");
  223.     }
  224.     else
  225.     {
  226.         page += makeButton("/ACT=LON", "Light on");
  227.     }
  228.  
  229.     page += HTTP_LINK_CONFIG_COOP;
  230.     // bottom of the html page
  231.     page += HTTP_END_COOP;
  232.     webServer.send(200, "text/html", page);
  233. }
  234.  
  235. void resetToAPPage()
  236. {
  237.     String page = HTTP_BEGIN_COOP;
  238.     page += "Do you want to run as AP or<br>do you want to connect to a WiFi network?";
  239.     page += makeButton("/AP", "Make AP");
  240.     page += makeButton("/WIFI", "Connect to WiFi");
  241.     page += HTTP_END_COOP;
  242.     webServer.send(200, "text/html", page);
  243. }
  244.  
  245. void doAP()
  246. {
  247.     setAPFlag();
  248.     String page = HTTP_BEGIN_COOP;
  249.     page += "Creating AP and rebooting...";
  250.     page += HTTP_END_COOP;
  251.     webServer.send(200, "text/html", page);
  252.     WiFiManager wifi;
  253.     DEBUG_println(wifi.getConfigPortalSSID());
  254.     wifi.resetSettings();
  255.     ESP.reset();
  256.     Alarm.delay(5000);
  257. }
  258.  
  259. void doWiFi()
  260. {
  261.     unsetAPFlag();
  262.     String page = HTTP_BEGIN_COOP;
  263.     page += "Creating WiFi and rebooting...";
  264.     page += HTTP_END_COOP;
  265.     WiFiManager wifi;
  266.     DEBUG_println(wifi.getConfigPortalSSID());
  267.     wifi.resetSettings();
  268.     ESP.reset();
  269.     Alarm.delay(5000);
  270. }
  271.  
  272.  
  273. void configPage()
  274. {
  275.     String page = HTTP_BEGIN_COOP;
  276.     // ask the user to select time zone
  277.     page += "Please enter your Time Zone (-12..14): <br>";
  278.     page += HTTP_FORM_SAVE_COOP;
  279.     page.replace("{TZ}", String(timezone));
  280.     page.replace("{VER}", COOP_VERSION);
  281.     page += HTTP_LINK_RESET_COOP;
  282.     // bottom of the html page
  283.     page += HTTP_END_COOP;
  284.     webServer.send(200, "text/html", page);
  285. }
  286.  
  287.  
  288. void configModeCallback(WiFiManager *myWiFiManager)
  289. {
  290.     DEBUG_println("Entered config mode");
  291.     DEBUG_println(WiFi.softAPIP());
  292. }
  293.  
  294.  
  295. boolean syncTimeWithGPS()
  296. {
  297.     // Dispatch incoming characters
  298.     while (SerialGPS.available() > 0)
  299.         gps.encode(SerialGPS.read());
  300.  
  301.     if (gps.date.isUpdated() && gps.time.isUpdated() && gps.location.isUpdated() && gps.location.isValid())
  302.     {
  303.         latitude = gps.location.lat();
  304.         longitude = gps.location.lng();
  305.         setTime(gps.time.hour(), gps.time.minute(), gps.time.second(), gps.date.day(), gps.date.month(), gps.date.year());
  306.         adjustTime(timezone * SECS_PER_HOUR);
  307.         showDateTime();
  308.         return true;
  309.     }
  310.     else
  311.         return false;
  312. }
  313.  
  314. void lightOn()
  315. {
  316.     digitalWrite(light_relay, HIGH);
  317.     mainPage();
  318. }
  319.  
  320. void lightOff()
  321. {
  322.     digitalWrite(light_relay, LOW);
  323.     mainPage();
  324. }
  325.  
  326. void openDoor()
  327. {
  328.     DEBUG_println("Opening door...");
  329.     showDateTime();
  330.     digitalWrite(door_relay1, LOW);
  331.     isDoorOpen = true;
  332.     mainPage();
  333. }
  334.  
  335. void closeDoor()
  336. {
  337.     DEBUG_println("Closing door...");
  338.     showDateTime();
  339.     digitalWrite(door_relay1, HIGH);
  340.     isDoorOpen = false;
  341.     mainPage();
  342. }
  343.  
  344. boolean isLightOn()
  345. {
  346.     return digitalRead(light_relay) == HIGH;
  347. }
  348.  
  349. void setAllAlarmsForTheDay()
  350. {
  351.     calculateTodaysSunriseSunset();
  352.     // creating two of each just to make sure
  353.     showATime(todaySunRiseHour, todaySunRiseMinute);
  354.     Alarm.alarmOnce(todaySunRiseHour, todaySunRiseMinute, 0, openDoor);
  355.     Alarm.alarmOnce(todaySunRiseHour + 1, todaySunRiseMinute, 0, openDoor);
  356.  
  357.     showATime(todaySunSetHour + 1, todaySunSetMinute);
  358.     Alarm.alarmOnce(todaySunSetHour + 1, todaySunSetMinute, 0, closeDoor);
  359.     Alarm.alarmOnce(todaySunSetHour + 2, todaySunSetMinute, 0, closeDoor);
  360.     // create the alarm to turn the light on before sunset
  361.     Alarm.alarmOnce(todaySunSetHour, todaySunSetMinute, 0, lightOn);
  362.     // ...and off after door closing
  363.     Alarm.alarmOnce(todaySunSetHour + 2, todaySunSetMinute, 0, lightOff);
  364. }
  365.  
  366. void getTimeZone()
  367. {
  368.     timezone = readTZ();
  369.     if ((timezone < -12) || (timezone > 14))
  370.     {
  371.         DEBUG_println(timezone);
  372.         DEBUG_println("Time Zone not set, using default -8 (PST)");
  373.         writeTZ(-8);
  374.     }
  375.     else
  376.     {
  377.         DEBUG_print("Time Zone found: ");
  378.         DEBUG_println(timezone);
  379.     }
  380. }
  381.  
  382. void setTheClock()
  383. {
  384.     DEBUG_println("Please wait, syncing the clock with GPS...");
  385.     while (!syncTimeWithGPS())
  386.     {
  387.         delay(10);
  388.     }
  389. }
  390.  
  391. void showDateTime()
  392. {
  393.     // digital clock display of the time
  394.     DEBUG_print(hour());
  395.     DEBUG_print(":");
  396.     DEBUG_print(niceMinuteSecond(minute()));
  397.     DEBUG_print(":");
  398.     DEBUG_print(niceMinuteSecond(second()));
  399.     DEBUG_print(" ");
  400.     DEBUG_print(day());
  401.     DEBUG_print("/");
  402.     DEBUG_print(month());
  403.     DEBUG_print("/");
  404.     DEBUG_print(year());
  405.     DEBUG_println();
  406. }
  407.  
  408. void showATime(int h, int m)
  409. {
  410.     DEBUG_print(h);
  411.     DEBUG_print(":");
  412.     DEBUG_println(niceMinuteSecond(m));
  413. }
  414.  
  415. void calculateTodaysSunriseSunset()
  416. {
  417.     //Time Lord constants
  418.     //tl_second=0, tl_minute=1, tl_hour=2, tl_day=3, tl_month=4, tl_year=5  
  419.     sunrisesunset.TimeZone(timezone * 60);
  420.     sunrisesunset.Position(latitude, longitude);
  421.     byte rise[] = { 0, 0, 12, (byte)day(), (byte)month(), (byte)year() };
  422.     byte set[] = { 0, 0, 12, (byte)day(), (byte)month(), (byte)year() };
  423.     sunrisesunset.SunRise(rise);
  424.     todaySunRiseHour = rise[tl_hour];
  425.     todaySunRiseMinute = rise[tl_minute];
  426.     sunrisesunset.SunSet(set);
  427.     todaySunSetHour = set[tl_hour];
  428.     todaySunSetMinute = set[tl_minute];
  429. }
  430.  
  431. template <class T> int EEPROM_writeAnything(int ee, const T& value);
  432. template <class T> int EEPROM_readAnything(int ee, T& value);
  433.  
  434. // Read TimeZone from EEPROM
  435. int readTZ()
  436. {
  437.     int tz;
  438.     EEPROM_readAnything(0, tz);
  439.     DEBUG_print("Found TZ: ");
  440.     DEBUG_println(tz);
  441.     return tz;
  442. }
  443.  
  444. // Write TimeZone from EEPROM
  445. void writeTZ(int tz)
  446. {
  447.     DEBUG_print("New TZ: ");
  448.     DEBUG_println(tz);
  449.     EEPROM_writeAnything(0, tz);
  450. }
  451.  
  452. void setAPFlag()
  453. {
  454.     EEPROM_writeAnything(20, true);
  455. }
  456.  
  457. void unsetAPFlag()
  458. {
  459.     EEPROM_writeAnything(20, false);
  460. }
  461.  
  462. boolean isAPFlag()
  463. {
  464.     boolean ap;
  465.     EEPROM_readAnything(20, ap);
  466.     return ap;
  467. }
  468.  
  469. template <class T> int EEPROM_writeAnything(int ee, const T& value)
  470. {
  471.     const byte* p = (const byte*)(const void*)&value;
  472.     unsigned int i;
  473.     for (i = 0; i < sizeof(value); i++)
  474.         EEPROM.write(ee++, *p++);
  475.     EEPROM.commit();
  476.     return i;
  477. }
  478.  
  479. template <class T> int EEPROM_readAnything(int ee, T& value)
  480. {
  481.     byte* p = (byte*)(void*)&value;
  482.     unsigned int i;
  483.     for (i = 0; i < sizeof(value); i++)
  484.         *p++ = EEPROM.read(ee++);
  485.     return i;
  486. }
  487.  
  488. void dynDNS()
  489. {
  490.     // put the IP on a DYNDNS for easy access
  491.     HTTPClient http;
  492.     String dyndns = GOOGLE_DYNDNS;
  493.     dyndns.replace("{IP}", WiFi.localIP().toString());
  494.     http.begin(dyndns); //HTTP                                                                                     
  495.     int httpCode = http.GET();
  496.     if (httpCode == HTTP_CODE_OK)
  497.     {
  498.         String payload = http.getString();
  499.         DEBUG_println(payload);
  500.     }
  501. }
  502.  
  503. String niceMinuteSecond(int m)
  504. {
  505.     char sz[4];
  506.     sprintf(sz, "%02d", m);
  507.     return String(sz);
  508. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement