Advertisement
Guest User

Untitled

a guest
Sep 2nd, 2015
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.98 KB | None | 0 0
  1. #include <ESP8266WiFi.h>
  2. #include <WiFiClient.h>
  3. #include <ESP8266WebServer.h>
  4. #include <ESP8266mDNS.h>
  5. #include <OneWire.h>
  6. #include <DallasTemperature.h>
  7. #include <Ticker.h>
  8. #include <EEPROM.h>
  9. #include "EEPROMAnything.h"
  10.  
  11.  
  12. const char* ssid = "ACHome";
  13. const char* password = "0400051283";
  14. MDNSResponder mdns;
  15.  
  16. // config object
  17. struct config_t
  18. {
  19.     float     onTemp;
  20.     float     offTemp;
  21.     uint8_t   controlStatus;
  22.     uint16_t  reboots;
  23. } config;
  24.  
  25. OneWire  oneWire(2);
  26. DallasTemperature sensors(&oneWire);
  27. ESP8266WebServer server(80);
  28. Ticker intTimer;
  29. Ticker uptimeTimer;
  30.  
  31. // globals
  32.  
  33. float reading;
  34. float readingsHistoric[3];
  35.  
  36. uint8_t powerStatus = 0;
  37. uint16_t powerCycles;
  38. uint32_t uptime;
  39. uint32_t lastPowerChange;
  40. uint32_t powerOnSeconds_session;
  41. uint32_t powerOffSeconds_session;
  42. uint8_t processing = 0;
  43.  
  44.  
  45. // consts
  46. const uint8_t ssr                   =   4; // define solid state relay pin
  47. const uint8_t CONTROL_STATUS        =   2;
  48. const uint8_t CONTROL_TEMP          =   3;
  49. const uint8_t REBOOTS_COUNT         =   4;
  50.  
  51. char index_html[] = R"=====(
  52. <html><head><meta charset="utf-8"><meta http-equiv="refresh" content="60"><title>BrewESP - Homebrew Frige Controller</title><style>.topMenu .links a{padding:5px}#footer{background-color:#69C;border:10px solid #204162;height:260px;position:absolute;top:30%;width:697px;left:0;right:0;margin:0 auto;box-shadow:5px 10px 10px -1px rgba(0,0,0,.5)}.statContainer{margin:10px auto;text-align:center}.statContainer .stat{font:13px 'Lucida Sans','trebuchet MS',Arial,Helvetica;background:#fff;border:1px solid #ddd;padding:20px;width:180px;height:140px;float:left;position:relative;margin:0 5px;box-shadow:0 5px 5px -1px rgba(0,0,0,.3)}#control{margin:-20px -20px 0}.statContainer .header{position:relative;font-size:20px;text-transform:uppercase;padding:20px;margin:-20px -20px 20px;background-image:linear-gradient(#fff,#eee)}.statContainer .header:after{position:absolute;bottom:-8px;left:0;height:3px;width:100%}.statContainer .value{font-size:45px}.statContainer .lowerValue{font-size:13px;color:#999}.topMenu{background-color:#0E0E80;height:30px;width:100%;font-size:25px;color:#999;padding-bottom:5px;font-family:"Arial Black",Gadget,sans-serif}.topMenu .links{float:right;display:inline;font-size:20px}.topMenu a,.topMenu a:visited{color:#999}.topMenu a:hover{color:#000}.bottomStat{padding-top:5px}.bottomStatLeft{float:left;padding-left:10px}.bottomStatRight{float:right;padding-right:10px}.label{float:left}</style></head><body><div id="footer"><div class="topMenu">BrewESP - Homebrew Fridge Controller<div class="links"><A HREF="config">Config</a><A HREF="api">API</a></div></div><div class="statContainer"><div class="stat"><div class="header">Power</div><div class="value">%powerStatus%</div><div class="lowerValue">%powerTime%</div></div></div><div class="statContainer"><div class="stat"><div class="header">Temp</div><div class="value">%temp%'C</div><div class="lowerValue">%tempShift%</div></div></div><div class="statContainer"><div class="stat"><div class="header" id="control">Set</div><label class="label">On</label><div class="value">%on%'C</div><label class="label">Off</label><div class="value">%off%'C</div></div></div><div class="bottomStat bottomStatLeft">%powerStats%</div><div class="bottomStat bottomStatRight"><b>Uptime</b>: %uptime%</div></div></body></html>
  53. )=====";
  54. char config_html[] = R"=====(
  55. <html><head><meta charset="utf-8"><title>BrewESP - Config</title><style>.topMenu .links a{padding:5px}#footer{background-color:#69C;border:10px solid #204162;height:260px;position:absolute;top:30%;width:697px;left:0;right:0;margin:0 auto;box-shadow:5px 10px 10px -1px rgba(0,0,0,.5)}.statContainer{margin:10px auto;text-align:center}.statContainer .stat{font:13px 'Lucida Sans','trebuchet MS',Arial,Helvetica;background:#fff;border:1px solid #ddd;padding:20px;width:180px;height:140px;float:left;position:relative;margin:0 5px;box-shadow:0 5px 5px -1px rgba(0,0,0,.3)}.statContainer .header{position:relative;font-size:20px;text-transform:uppercase;padding:20px;margin:-20px -20px 20px;background-image:linear-gradient(#fff,#eee)}.statContainer .header:after{position:absolute;bottom:-8px;left:0;height:3px;width:100%}.statContainer .value{font-size:45px}.statContainer .lowerValue{font-size:13px;padding-top:20px;color:#999}.topMenu{background-color:#0E0E80;height:30px;width:100%;font-size:25px;color:#999;padding-bottom:5px;font-family:"Arial Black",Gadget,sans-serif}.topMenu .links{float:right;display:inline;font-size:20px}.topMenu a,.topMenu a:visited{color:#999}.topMenu a:hover{color:#000}.bottomStat{padding-top:5px}.bottomStatLeft{float:left;padding-left:10px}.bottomStatRight{float:right;padding-right:10px}</style></head><body><div id="footer"><div class="topMenu">BrewESP - Settings<div class="links"><A HREF="/">Home</a><A HREF="api">API</a></div></div><div class="statContainer"><div class="stat"><div class="header">Temp Control</div><form method="get" action="/setTemp"><div><input type="text" id="tempSetting" name="onTemp" size="2" value="%onTemp%"></input><button type="submit">Set On</button></div><div><input type="text" id="tempSetting" name="offTemp" size="2" value="%offTemp%"></input><button type="submit">Set Off</button></div></form></div></div><div class="statContainer"><div class="stat"><div class="header">Stats</div><form method="get" action="/cleareeprom"> <div class="value"><button type="submit">Clear EEPROM</button></div></form><div class="lowerValue"></div></div></div><div class="statContainer"><div class="stat"><div class="header">Control</div><form method="get" action="/setControl"> <input type="hidden" name="control" value="%controlSetTo%"> <div class="value"><button type="submit">Turn %controlSetTo%</button></div></form><div class="lowerValue">Control currently %controlCurrently%</div></div></div><div class="bottomStat bottomStatLeft"></div><div class="bottomStat bottomStatRight"></div></div></body></html>
  56. )=====";
  57.  
  58. String index_htmlString = String(index_html);
  59. String config_htmlString = String(config_html);
  60.  
  61. uint16_t timer1 = 0;
  62. uint16_t timer2 = 899;
  63.  
  64. /*
  65.  
  66.         web request handlers
  67.  
  68. */
  69.  
  70. void handleRoot() {
  71.   if (processing == 0) {
  72.     processing = 1;
  73.     index_htmlString = String(index_html);
  74.     updateIndexStats();
  75.     server.send(200, "text/html", index_htmlString);
  76.     processing = 0;
  77.   }
  78. }
  79.  
  80. void handleConfig(){
  81.     config_htmlString = String(config_html);
  82.   updateConfigIndex();
  83.   server.send(200, "text/html", config_htmlString);
  84. }
  85.  
  86. void handleNotFound(){
  87.   //digitalWrite(ssr, 1);
  88.   String message = "File Not Found\n\n";
  89.   message += "URI: ";
  90.   message += server.uri();
  91.   message += "\nMethod: ";
  92.   message += (server.method() == HTTP_GET)?"GET":"POST";
  93.   message += "\nArguments: ";
  94.   message += server.args();
  95.   message += "\n";
  96.   for (uint8_t i=0; i<server.args(); i++){
  97.     message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  98.   }
  99.   server.send(404, "text/plain", message);
  100. }
  101.  
  102. void apiHandler(){
  103.   String JSON = "{";
  104.   JSON = JSON + "\"uptime\":"+uptime;
  105.   JSON = JSON + ",\"power\":"+powerStatus;
  106.   JSON = JSON + ",\"control\":"+config.controlStatus;
  107.   JSON = JSON + ",\"ontemp\":"+config.onTemp;
  108.   JSON = JSON + ",\"offtemp\":"+config.offTemp;
  109.   JSON = JSON + ",\"power cycles\":"+powerCycles;
  110.   JSON = JSON + ",\"last power change\":"+lastPowerChange;
  111.   JSON = JSON + ",\"power on time\":"+powerOnSeconds_session;
  112.   JSON = JSON + ",\"power off time\":"+powerOffSeconds_session;
  113.   JSON = JSON + ",\"reboots\":"+config.reboots;
  114.   JSON = JSON + ",\"free memory\":"+ESP.getFreeHeap();
  115.   JSON = JSON + ",\"temp\":"+reading;
  116.   JSON = JSON + ",\"sensor count\":"+sensors.getDeviceCount()+"}";
  117.   server.send ( 200,"text/plain", JSON );
  118. }
  119.  
  120. /*
  121.  
  122.       Change setting functions, fired from web requests
  123.  
  124. */
  125.  
  126. // function is called to set temperature from config page
  127. void setTemp(){
  128.   Serial.println(server.argName(0));
  129.   Serial.println(server.arg(0));
  130.   Serial.println(server.argName(1));
  131.   Serial.println(server.arg(1));
  132.   if (server.argName(0) == "onTemp" && server.argName(1) == "offTemp") {
  133.     float newOnTemp = server.arg(0).toFloat();
  134.     float newOffTemp = server.arg(1).toFloat();
  135.     if (newOnTemp > newOffTemp && newOnTemp > 1 && newOnTemp < 31) { // temp has to be within a range
  136.       config.onTemp = newOnTemp;
  137.       config.offTemp = newOffTemp;
  138.       updateEEPROM();
  139.       handleConfig();
  140.     } else {
  141.       server.send ( 200,"text/plain", "Temperature out of range or set incorrectly" );
  142.     }
  143.   }
  144. }
  145.  
  146. // function is called to turn control on/off from config page
  147. void setControl(){
  148.   if (sensors.getDeviceCount() == 1) {
  149.     if (server.args() == 1) {
  150.       if (server.argName(0) == "control") {
  151.         if (server.arg(0) == "On") {
  152.           config.controlStatus = 1;
  153.         } else if (server.arg(0) == "Off") {
  154.           config.controlStatus = 0;
  155.           turnOffFridge();
  156.         }
  157.         powerOnSeconds_session = 0;
  158.         powerOffSeconds_session = 0;
  159.         updateEEPROM();
  160.         handleConfig();
  161.       }
  162.     }
  163.   } else server.send ( 200,"text/plain", "Error: Cant enable control, no temp sensors found.");
  164. }
  165.  
  166. /*
  167.  
  168.         Setup Function
  169.  
  170. */
  171.  
  172.  
  173. void setup(void){
  174.  
  175.   pinMode(ssr, OUTPUT);
  176.   digitalWrite(ssr, 0);
  177.  
  178.   Serial.begin(115200);
  179.  
  180.   Serial.print(F("Chip ID: "));
  181.   Serial.println(ESP.getChipId());
  182.   Serial.print(F("Flash Chip ID: "));
  183.   Serial.println(ESP.getFlashChipId());
  184.   Serial.print(F("Flash Size: "));
  185.   Serial.println(ESP.getFlashChipSize());
  186.  
  187.     sensors.begin();
  188.   Serial.print(F("Found "));
  189.   Serial.print(sensors.getDeviceCount());
  190.   Serial.println(F(" ds18b20 devices."));
  191.  
  192.   intTimer.attach_ms(1, counter);
  193.   uptimeTimer.attach_ms(1000, uptimeCounter);
  194.  
  195.   EEPROM.begin(512);
  196.   detectFirstBoot();
  197.   EEPROM_readAnything(1, config);
  198.   config.reboots++;
  199.   EEPROM_writeAnything(1, config);
  200.   EEPROM.commit();
  201.  
  202.  
  203.  
  204.   WiFi.begin(ssid, password);
  205.   Serial.println("");
  206.   // Wait for connection
  207.   while (WiFi.status() != WL_CONNECTED) {
  208.     delay(500);
  209.     Serial.print(".");
  210.   }
  211.   Serial.println("");
  212.   Serial.print("Connected to ");
  213.   Serial.println(ssid);
  214.   Serial.print("IP address: ");
  215.   Serial.println(WiFi.localIP());
  216.   if (mdns.begin("brewesp", WiFi.localIP())) {
  217.     Serial.println("MDNS responder started - brewesp.local");
  218.   }
  219.   server.on("/", handleRoot);
  220.   server.on("/setTemp", setTemp);
  221.   server.on("/setControl", setControl);
  222.   server.on("/cleareeprom",clearEEPROMConfig);
  223.   server.on("/config",handleConfig);
  224.   server.on("/api",apiHandler);
  225.   server.onNotFound(handleNotFound);
  226.   server.begin();
  227.   Serial.println("HTTP server started");
  228. }
  229.  
  230. /*
  231.  
  232.         Main Loop
  233.  
  234. */
  235.  
  236. void loop(void){
  237.   server.handleClient();
  238.   if (timer1 == 0){
  239.     updateTemp(); // update temp reading to global var
  240.     timer1 = 5000;
  241.   }
  242. }
  243.  
  244. /*
  245.  
  246.       Timing functions fired by tickers
  247.  
  248. */
  249.  
  250. void counter() {
  251.   if (timer1 > 0) timer1--;
  252. }
  253.  
  254. void uptimeCounter(){
  255.   uptime++;
  256.   if (timer2 > 0) timer2--;
  257.   lastPowerChange++;
  258.   if (config.controlStatus == 1) {
  259.     if (powerStatus == 1) powerOnSeconds_session++;
  260.     else powerOffSeconds_session++;
  261.   }
  262. }
  263.  
  264. /*
  265.  
  266.       Templating functions
  267.  
  268. */
  269.  
  270. // bit of a big ugly function. basically it takes the index template and swaps out the tags %tag% for real values
  271. void updateIndexStats(){
  272.   // update temp shift
  273.   if (readingsHistoric[1] > 0) {
  274.     float currentShift = readingsHistoric[3] - readingsHistoric[1];
  275.     String currentShiftString;
  276.     if (currentShift > 0) currentShiftString = "+";
  277.     else currentShiftString = "-";
  278.     currentShiftString += String(fabs(currentShift*2));
  279.     index_htmlString.replace("%tempShift%",String(currentShiftString)+"/h");
  280.   } else {
  281.     index_htmlString.replace("%tempShift%","");
  282.   }
  283.   // update fridge on/off hours
  284.   if (config.controlStatus == 1) {
  285.     String powerStats = "<b>On</b>: %onHrs%hrs, <b>Off</b>: %offHrs%hrs <b>Ratio</b>: %onoffRatio% <b>Cycles</b>: %powerCycles%";
  286.     powerStats.replace("%onHrs%",String(abs(powerOnSeconds_session/60/60)));
  287.     powerStats.replace("%offHrs%",String(abs(powerOffSeconds_session/60/60)));
  288.     float ratio;
  289.     if (powerOnSeconds_session > 0 && powerOffSeconds_session > 0) {
  290.       ratio = powerOnSeconds_session/powerOffSeconds_session;
  291.     } else {
  292.       ratio = 0.0;
  293.     }
  294.     powerStats.replace("%onoffRatio%",String(ratio));
  295.     powerStats.replace("%powerCycles%",String(abs(powerCycles/2)));
  296.     index_htmlString.replace("%powerStats%",powerStats);
  297.   } else index_htmlString.replace("%powerStats%","<b>Fridge control currently disabled</b>");
  298.   // update set temp
  299.   index_htmlString.replace("%on%",String(config.onTemp).substring(0,4));
  300.   index_htmlString.replace("%off%",String(config.offTemp).substring(0,4));
  301.   // update power status and time since last change
  302.   if (powerStatus == 1) index_htmlString.replace("%powerStatus%",String("On"));
  303.   else index_htmlString.replace("%powerStatus%",String("Off"));
  304.   uint16_t mins = lastPowerChange/60;
  305.   uint8_t seconds = lastPowerChange%60;
  306.   String powerTime = String(mins);
  307.   powerTime = powerTime + ":";
  308.   powerTime = powerTime + (seconds>9?String(seconds):"0"+String(seconds));
  309.   index_htmlString.replace("%powerTime%",powerTime);
  310.   // update temp
  311.   if (sensors.getDeviceCount() == 1){
  312.     index_htmlString.replace("%temp%",String(reading).substring(0,4));
  313.   } else {
  314.     index_htmlString.replace("%temp%",String("Err"));
  315.   }
  316.   // sepearte uptime into days, hours and mins
  317.   uint8_t days = uptime/86400;
  318.   uint8_t hours = uptime%86400/3600;
  319.   mins = uptime%86400%3600/60;
  320.   String uptime;
  321.   uptime = String(String(days) + " Day" + (days > 1 ? "s" : ""));
  322.   uptime = uptime+" "+(hours>9?String(hours):"0"+String(hours));
  323.   uptime = uptime+":"+(mins>9?String(mins):"0"+String(mins));
  324.   index_htmlString.replace("%uptime%",String(uptime));
  325. }
  326.  
  327. void updateConfigIndex(){
  328.   config_htmlString.replace("%onTemp%",String(config.onTemp));
  329.   config_htmlString.replace("%offTemp%",String(config.offTemp));
  330.   if (config.controlStatus == 1) {
  331.     config_htmlString.replace("%controlSetTo%","Off");
  332.     config_htmlString.replace("%controlCurrently%","On");
  333.   } else {
  334.     config_htmlString.replace("%controlSetTo%","On");
  335.     config_htmlString.replace("%controlCurrently%","Off");
  336.   }
  337.  
  338. }
  339.  
  340. /*
  341.  
  342.       Decision and Processing Functions
  343.  
  344. */
  345.  
  346. // the first byte is written with a 127 when the config object has been written
  347. void detectFirstBoot(){
  348.   if (EEPROM.read(0) != 127){
  349.     clearEEPROM();
  350.   }
  351. }
  352.  
  353. // update temp stored in glabal var reading
  354. void updateTemp(){
  355.   float thisReading;
  356.   sensors.requestTemperatures();
  357.   thisReading = sensors.getTempCByIndex(0);
  358.   uint8_t counter;
  359.   // if the new reading and the old reading is more than 10c apart its probably a dodgy reading. try 10 more times if they are all more than 10 out assume its a valid reading. ie beer may have been tipped in causing a big temp swing
  360.   while (counter++ < 10 && fabs(thisReading - reading) > 10) {
  361.       delay(500);
  362.       sensors.requestTemperatures();
  363.       thisReading = sensors.getTempCByIndex(0);
  364.   }
  365.   reading = thisReading;
  366.   // every 15min rotate this array.
  367.   if (timer2 == 0){
  368.     readingsHistoric[0] = readingsHistoric[1];
  369.     readingsHistoric[1] = readingsHistoric[2];
  370.     readingsHistoric[2] = readingsHistoric[3];
  371.     readingsHistoric[3] = reading;
  372.     timer2 = 899;
  373.   }
  374.   fridgeControl();
  375. }
  376.  
  377. // function decides if the fridge needs to be turned on or off
  378. // originally the code switched the fridge on when the temp was greather than a threshhold then off when lower than this threshold. What I found with my fridge is the cooling part of the frige got so cold it continued to cool the fermenter even after fridge was off for another hour dropping the temp another 1'c below threshold.
  379. // on and off temps are now adjustable to compensate for how your fridge controlSetTo
  380. // this could be improved with some learning code to do later
  381. void fridgeControl(){
  382.   if (sensors.getDeviceCount() == 1 && reading != -127) {
  383.     if (config.controlStatus == 1 && lastPowerChange > 300) { // 5min delay between fridge going on or off
  384.       if ( (reading >= config.onTemp && powerStatus == 0) || (reading <= config.offTemp && powerStatus == 1) ) {
  385.         lastPowerChange = 0;
  386.         powerStatus ^= 1;
  387.         powerCycles++;
  388.         if (powerStatus == 1) turnOnFridge();
  389.         else turnOffFridge();
  390.       }
  391.     }
  392.   }
  393. }
  394.  
  395. void turnOffFridge(){
  396.     powerStatus = 0;
  397.     digitalWrite(ssr,0);
  398.     lastPowerChange = 0;
  399. }
  400.  
  401. void turnOnFridge(){
  402.   powerStatus = 1;
  403.   digitalWrite(ssr,1);
  404.   lastPowerChange = 0;
  405. }
  406.  
  407. /*
  408.  
  409.         EEPROM (actually flash) functions
  410.  
  411. */
  412.  
  413. void updateEEPROM(){
  414.   EEPROM_writeAnything(1,config);
  415.   EEPROM.commit();
  416. }
  417.  
  418. void clearEEPROM(){
  419.   for (int i=0; i<512; i++){
  420.     EEPROM.write(i, 0);
  421.   }
  422.   EEPROM.write(0, 127);
  423.   // set some defaults
  424.   config.onTemp = 18.5;
  425.   config.offTemp = 17.2;
  426.   EEPROM_writeAnything(1,config);
  427.   EEPROM.commit();
  428. }
  429.  
  430. void clearEEPROMConfig(){
  431.   clearEEPROM();
  432.   server.send ( 200, "text/html", "EEPROM has been cleared.");
  433. }
  434.  
  435. void readEEPROM(){
  436.   EEPROM_readAnything(1,config);
  437. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement