Advertisement
Guest User

Untitled

a guest
Mar 7th, 2018
243
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 43.14 KB | None | 0 0
  1. /* OpenGarage Firmware
  2.  *
  3.  * Main loop
  4.  * Mar 2016 @ OpenGarage.io
  5.  *
  6.  * This file is part of the OpenGarage library
  7.  *
  8.  * This program is free software: you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation, either version 3 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program.  If not, see
  20.  * <http://www.gnu.org/licenses/>.
  21.  */
  22.  
  23. #if defined(SERIAL_DEBUG)
  24.   #define BLYNK_DEBUG
  25.   #define BLYNK_PRINT Serial
  26. #endif
  27.  
  28. #include <BlynkSimpleEsp8266.h>
  29. #include <DNSServer.h>
  30. #include <PubSubClient.h> //https://github.com/Imroy/pubsubclient
  31.  
  32. #include "pitches.h"
  33. #include "OpenGarage.h"
  34. #include "espconnect.h"
  35.  
  36. #include <DHT.h>                        //DHT Temperature/humidity sensor
  37. #include <SimpleTimer.h>                //DHT Temperature/humidity sensor
  38. DNSServer *dns = NULL;
  39.  
  40.  
  41. DHT dht(DHTPIN, DHTTYPE);               //DHT Temperature/humidity sensor
  42. SimpleTimer timer;                      //DHT Temperature/humidity sensor
  43.  
  44. OpenGarage og;
  45. ESP8266WebServer *server = NULL;
  46.  
  47. WidgetLED blynk_led(BLYNK_PIN_LED);
  48. WidgetLCD blynk_lcd(BLYNK_PIN_LCD);
  49.  
  50. static Ticker led_ticker;
  51. static Ticker aux_ticker;
  52. static Ticker ip_ticker;
  53. static Ticker restart_ticker;
  54.  
  55. static WiFiClient wificlient;
  56. PubSubClient mqttclient(wificlient);
  57.  
  58. static String scanned_ssids;
  59. static byte read_cnt = 0;
  60. static uint distance = 0;
  61. static uint vdistance = 0;
  62. static byte door_status = 0; //0 down, 1 up
  63. static int vehicle_status = 0; //0 No, 1 Yes, 2 Unknown (door open), 3 Option Disabled
  64. static bool curr_cloud_access_en = false;
  65. static uint led_blink_ms = LED_FAST_BLINK;
  66. static ulong justopen_timestamp = 0;
  67. static byte curr_mode;
  68. // this is one byte storing the door status histogram
  69. // maximum 8 bits
  70. static byte door_status_hist = 0;
  71. static ulong curr_utc_time = 0;
  72. static ulong curr_utc_hour= 0;
  73. static HTTPClient http;
  74.  
  75. void do_setup();
  76.  
  77. void sendSensor()                                        //DHT Temperature/humidity sensor
  78. {                                                        //DHT Temperature/humidity sensor
  79.   float h = dht.readHumidity();                          //DHT Temperature/humidity sensor
  80.   float t = dht.readTemperature(true);                  // or dht.readTemperature(true) for Fahrenheit
  81.  
  82.   if (isnan(h) || isnan(t)) {                           //DHT Temperature/humidity sensor
  83.     Serial.println("Failed to read from DHT sensor!");  //DHT Temperature/humidity sensor
  84.     return;                                             //DHT Temperature/humidity sensor
  85.   }                                                     //DHT Temperature/humidity sensor
  86.  
  87.   Blynk.virtualWrite(V10, h);                           //DHT Temperature/humidity sensor
  88.   Blynk.virtualWrite(V11, t);                           //DHT Temperature/humidity sensor
  89. }
  90.  
  91. void server_send_html(String html) {
  92.   server->send(200, "text/html", html);
  93. }
  94.  
  95. void server_send_result(byte code, const char* item = NULL) {
  96.   String html = F("{\"result\":");
  97.   html += code;
  98.   if (!item) item = "";
  99.   html += F(",\"item\":\"");
  100.   html += item;
  101.   html += F("\"");
  102.   html += F("}");
  103.   server_send_html(html);
  104. }
  105.  
  106. bool get_value_by_key(const char* key, uint& val) {
  107.   if(server->hasArg(key)) {
  108.     val = server->arg(key).toInt();  
  109.     return true;
  110.   } else {
  111.     return false;
  112.   }
  113. }
  114.  
  115. bool get_value_by_key(const char* key, String& val) {
  116.   if(server->hasArg(key)) {
  117.     val = server->arg(key);  
  118.     return true;
  119.   } else {
  120.     return false;
  121.   }
  122. }
  123.  
  124. String ipString;
  125.  
  126. void report_ip() {
  127.   static uint notes[] = {NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4};
  128.   static byte note = 0;
  129.   static byte digit = 0;
  130.  
  131.   if(digit == ipString.length()) { // play ending note
  132.     og.play_note(NOTE_C6); digit++; note=0;
  133.     ip_ticker.once_ms(1000, report_ip);
  134.     return;
  135.   } else if(digit == ipString.length()+1) { // end
  136.     og.play_note(0); note=0; digit=0;
  137.     return;
  138.   }
  139.   char c = ipString.charAt(digit);
  140.   if (c==' ') {
  141.     og.play_note(0); digit++; note=0;
  142.     ip_ticker.once_ms(1000, report_ip);
  143.   } else if (c=='.') {
  144.     og.play_note(NOTE_C5);
  145.     digit++; note=0;
  146.     ip_ticker.once_ms(500, report_ip);
  147.   } else if (c>='0' && c<='9') {
  148.     byte idx=9; // '0' maps to index 9;
  149.     if(c>='1') idx=c-'1';
  150.     if(note==idx+1) {
  151.       og.play_note(0); note++;
  152.       ip_ticker.once_ms(1000, report_ip);
  153.     } else if(note==idx+2) {
  154.       digit++; note=0;
  155.       ip_ticker.once_ms(100, report_ip);
  156.     } else {
  157.       og.play_note(notes[note]);
  158.       note++;
  159.       ip_ticker.once_ms(500, report_ip);
  160.     }
  161.   }
  162. }
  163.  
  164. void restart_in(uint32_t ms) {
  165.   if(og.state != OG_STATE_WAIT_RESTART) {
  166.     og.state = OG_STATE_WAIT_RESTART;
  167.     DEBUG_PRINTLN(F("Prepare to restart..."));
  168.     restart_ticker.once_ms(ms, og.restart);
  169.   }
  170. }
  171.  
  172. void on_home()
  173. {
  174.   String html = "";
  175.  
  176.   if(curr_mode == OG_MOD_AP) {
  177.  
  178.     html = FPSTR(ap_home_html);
  179.   } else {
  180.     html = FPSTR(sta_home_html);
  181.   }
  182.   server_send_html(html);
  183. }
  184.  
  185. void on_sta_view_options() {
  186.   if(curr_mode == OG_MOD_AP) return;
  187.   String html = FPSTR(sta_options_html);
  188.   server->send(200, "text/html", html);
  189.   DEBUG_PRINTLN(F("Complete sending page"));
  190. }
  191.  
  192. void on_sta_view_logs() {
  193.   if(curr_mode == OG_MOD_AP) return;
  194.   String html = FPSTR(sta_logs_html);
  195.   server_send_html(html);
  196. }
  197.  
  198. char dec2hexchar(byte dec) {
  199.   if(dec<10) return '0'+dec;
  200.   else return 'A'+(dec-10);
  201. }
  202.  
  203. String get_mac() {
  204.   static String hex = "";
  205.   if(!hex.length()) {
  206.     byte mac[6];
  207.     WiFi.macAddress(mac);
  208.  
  209.     for(byte i=0;i<6;i++) {
  210.       hex += dec2hexchar((mac[i]>>4)&0x0F);
  211.       hex += dec2hexchar(mac[i]&0x0F);
  212.       if(i!=5) hex += ":";
  213.     }
  214.   }
  215.   return hex;
  216. }
  217.  
  218. String get_ap_ssid() {
  219.   static String ap_ssid = "";
  220.   if(!ap_ssid.length()) {
  221.     byte mac[6];
  222.     WiFi.macAddress(mac);
  223.     ap_ssid = "OG_";
  224.     for(byte i=3;i<6;i++) {
  225.       ap_ssid += dec2hexchar((mac[i]>>4)&0x0F);
  226.       ap_ssid += dec2hexchar(mac[i]&0x0F);
  227.     }
  228.   }
  229.   return ap_ssid;
  230. }
  231.  
  232. String get_ip() {
  233.   String ip = "";
  234.   IPAddress _ip = WiFi.localIP();
  235.   ip = _ip[0];
  236.   ip += ".";
  237.   ip += _ip[1];
  238.   ip += ".";
  239.   ip += _ip[2];
  240.   ip += ".";
  241.   ip += _ip[3];
  242.   return ip;
  243. }
  244.  
  245. void on_sta_controller() {
  246.   if(curr_mode == OG_MOD_AP) return;
  247.   String html = "";
  248.   html += F("{\"dist\":");
  249.   html += distance;
  250.   html += F(",\"door\":");
  251.   html += door_status;
  252.   html += F(",\"vehicle\":");
  253.   html += vehicle_status;
  254.   html += F(",\"rcnt\":");
  255.   html += read_cnt;
  256.   html += F(",\"fwv\":");
  257.   html += og.options[OPTION_FWV].ival;
  258.   html += F(",\"name\":\"");
  259.   html += og.options[OPTION_NAME].sval;
  260.   html += F("\",\"mac\":\"");
  261.   html += get_mac();
  262.   html += F("\",\"cid\":");
  263.   html += ESP.getChipId();
  264.   html += F(",\"rssi\":");
  265.   html += (int16_t)WiFi.RSSI();
  266.   html += F("}");
  267.   server_send_html(html);
  268. }
  269.  
  270. void on_sta_debug() {
  271.   String html = "";
  272.   html += F("{");
  273.   html += F("\"rcnt\":");
  274.   html += read_cnt;
  275.   html += F(",\"fwv\":");
  276.   html += og.options[OPTION_FWV].ival;
  277.   html += F(",\"name\":\"");
  278.   html += og.options[OPTION_NAME].sval;
  279.   html += F("\",\"mac\":\"");
  280.   html += get_mac();
  281.   html += F("\",\"cid\":");
  282.   html += ESP.getChipId();
  283.   html += F(",\"rssi\":");
  284.   html += (int16_t)WiFi.RSSI();
  285.   html += F(",\"bssid\":\"");
  286.   html += WiFi.BSSIDstr();
  287.   html += F("\",\"build\":\"");
  288.   html += (F(__DATE__));
  289.   html += F("\",\"Freeheap\":");
  290.   html += (int16_t)ESP.getFreeHeap();
  291.   html += F("}");
  292.   server_send_html(html);
  293. }
  294.  
  295. void on_sta_logs() {
  296.   if(curr_mode == OG_MOD_AP) return;
  297.   String html = "";
  298.   html += F("{\"name\":\"");
  299.   html += og.options[OPTION_NAME].sval;
  300.   html += F("\",\"time\":");
  301.   html += curr_utc_time;
  302.   html += F(",\"logs\":[");
  303.   if(!og.read_log_start()) {
  304.     html += F("]}");
  305.     server_send_html(html);
  306.     return;
  307.   }
  308.   LogStruct l;
  309.   for(uint i=0;i<MAX_LOG_RECORDS;i++) {
  310.     if(!og.read_log_next(l)) break;
  311.     if(!l.tstamp) continue;
  312.     html += F("[");
  313.     html += l.tstamp;
  314.     html += F(",");
  315.     html += l.status;
  316.     html += F(",");
  317.     html += l.dist;
  318.     html += F("],");
  319.   }
  320.   og.read_log_end();
  321.   html.remove(html.length()-1); // remove the extra ,
  322.   html += F("]}");
  323.   server_send_html(html);
  324. }
  325.  
  326. bool verify_device_key() {
  327.   if(server->hasArg("dkey") && (server->arg("dkey") == og.options[OPTION_DKEY].sval))
  328.     return true;
  329.   return false;
  330. }
  331.  
  332. void on_reset_all(){
  333.   if(!verify_device_key()) {
  334.     server_send_result(HTML_UNAUTHORIZED);
  335.     return;
  336.   }
  337.  
  338.   og.state = OG_STATE_RESET;
  339.   server_send_result(HTML_SUCCESS);
  340. }
  341.  
  342. void on_clear_log() {
  343.   if(!verify_device_key()) {
  344.     server_send_result(HTML_UNAUTHORIZED);
  345.     return;
  346.   }
  347.   og.log_reset();
  348.   server_send_result(HTML_SUCCESS);  
  349. }
  350.  
  351. void on_test(){
  352.   //server_send_html(scanned_ssids);
  353. }
  354.  
  355. void on_sta_change_controller() {
  356.   if(curr_mode == OG_MOD_AP) return;
  357.  
  358.   if(!verify_device_key()) {
  359.     server_send_result(HTML_UNAUTHORIZED);
  360.     return;
  361.   }
  362.   if(server->hasArg("click") || server->hasArg("close") || server->hasArg("open"))  {
  363.     DEBUG_PRINTLN(F("Received locally generated button request (click, close, or open)"));
  364.     server_send_result(HTML_SUCCESS);
  365.     //1 is open
  366.     if ((server->hasArg("close") && door_status) || (server->hasArg("open") && !door_status) || (server->hasArg("click"))) {
  367.       DEBUG_PRINTLN(F("Valid command recieved based on door status"));
  368.       if(!og.options[OPTION_ALM].ival) {
  369.         // if alarm is not enabled, trigger relay right away
  370.         og.click_relay();
  371.       } else {
  372.         // else, set alarm
  373.         og.set_alarm();
  374.       }
  375.     }else{
  376.       DEBUG_PRINTLN(F("Command request not valid, door already in requested state"));
  377.     }
  378.   } else if(server->hasArg("reboot")) {
  379.     server_send_result(HTML_SUCCESS);
  380.     //restart_ticker.once_ms(1000, og.restart);
  381.     restart_in(1000);
  382.   } else if(server->hasArg("apmode")) {
  383.     server_send_result(HTML_SUCCESS);
  384.     og.reset_to_ap();
  385.   } else {
  386.     server_send_result(HTML_NOT_PERMITTED);
  387.   }
  388. }
  389.  
  390. void on_sta_change_options() {
  391.   if(curr_mode == OG_MOD_AP) return;
  392.  
  393.   if(!verify_device_key()) {
  394.     server_send_result(HTML_UNAUTHORIZED);
  395.     return;
  396.   }
  397.   uint ival = 0;
  398.   String sval;
  399.   byte i;
  400.   OptionStruct *o = og.options;
  401.  
  402.   byte usi = 0;
  403.   // FIRST ROUND: check option validity
  404.   // do not save option values yet
  405.   for(i=0;i<NUM_OPTIONS;i++,o++) {
  406.     const char *key = o->name.c_str();
  407.     // these options cannot be modified here
  408.     if(i==OPTION_FWV || i==OPTION_MOD  || i==OPTION_SSID ||
  409.       i==OPTION_PASS || i==OPTION_DKEY)
  410.       continue;
  411.    
  412.     if(o->max) {  // integer options
  413.       if(get_value_by_key(key, ival)) {
  414.         if(ival>o->max) {
  415.           server_send_result(HTML_DATA_OUTOFBOUND, key);
  416.           return;
  417.         }
  418.         if(i==OPTION_CDT && ival < 50) {
  419.           // click delay time should be at least 50 ms
  420.           server_send_result(HTML_DATA_OUTOFBOUND, key);
  421.           return;
  422.         }
  423.         if(i==OPTION_USI && ival==1) {
  424.           // mark device IP and gateway IP change
  425.           usi = 1;
  426.         }
  427.       }
  428.     }
  429.   }
  430.  
  431.   // Check device IP and gateway IP changes
  432.   String dvip, gwip, subn;
  433.   const char* _dvip = "dvip";
  434.   const char* _gwip = "gwip";
  435.   const char* _subn = "subn";
  436.   if(usi) {
  437.     if(get_value_by_key(_dvip, dvip)) {
  438.       if(get_value_by_key(_gwip, gwip)) {
  439.         // check validity of IP address
  440.         IPAddress ip;
  441.         if(!ip.fromString(dvip)) {server_send_result(HTML_DATA_FORMATERROR, _dvip); return;}
  442.         if(!ip.fromString(gwip)) {server_send_result(HTML_DATA_FORMATERROR, _gwip); return;}
  443.         if(get_value_by_key(_subn, subn)) {
  444.           if(!ip.fromString(subn)) {
  445.             server_send_result(HTML_DATA_FORMATERROR, _subn);
  446.             return;
  447.           }
  448.         }
  449.       } else {
  450.         server_send_result(HTML_DATA_MISSING, _gwip);
  451.         return;
  452.       }              
  453.     } else {
  454.       server_send_result(HTML_DATA_MISSING, _dvip);
  455.       return;
  456.     }
  457.   }
  458.   // Check device key change
  459.   String nkey, ckey;
  460.   const char* _nkey = "nkey";
  461.   const char* _ckey = "ckey";
  462.  
  463.   if(get_value_by_key(_nkey, nkey)) {
  464.     if(get_value_by_key(_ckey, ckey)) {
  465.       if(!nkey.equals(ckey)) {
  466.         server_send_result(HTML_MISMATCH, _ckey);
  467.         return;
  468.       }
  469.     } else {
  470.       server_send_result(HTML_DATA_MISSING, _ckey);
  471.       return;
  472.     }
  473.   }
  474.  
  475.   // SECOND ROUND: change option values
  476.   o = og.options;
  477.   for(i=0;i<NUM_OPTIONS;i++,o++) {
  478.     const char *key = o->name.c_str();
  479.     // these options cannot be modified here
  480.     if(i==OPTION_FWV || i==OPTION_MOD  || i==OPTION_SSID ||
  481.       i==OPTION_PASS || i==OPTION_DKEY)
  482.       continue;
  483.    
  484.     if(o->max) {  // integer options
  485.       if(get_value_by_key(key, ival)) {
  486.         o->ival = ival;
  487.       }
  488.     } else {
  489.       if(get_value_by_key(key, sval)) {
  490.         o->sval = sval;
  491.       }
  492.     }
  493.   }
  494.  
  495.   if(usi) {
  496.     get_value_by_key(_dvip, dvip);
  497.     get_value_by_key(_gwip, gwip);
  498.     og.options[OPTION_DVIP].sval = dvip;
  499.     og.options[OPTION_GWIP].sval = gwip;
  500.     if(get_value_by_key(_subn, subn)) {
  501.       og.options[OPTION_SUBN].sval = subn;
  502.     }
  503.   }
  504.  
  505.   if(get_value_by_key(_nkey, nkey)) {
  506.       og.options[OPTION_DKEY].sval = nkey;
  507.   }
  508.  
  509.   og.options_save();
  510.   server_send_result(HTML_SUCCESS);
  511. }
  512.  
  513. void on_sta_options() {
  514.   if(curr_mode == OG_MOD_AP) return;
  515.   String html = "{";
  516.   OptionStruct *o = og.options;
  517.   for(byte i=0;i<NUM_OPTIONS;i++,o++) {
  518.     if(!o->max) {
  519.       if(i==OPTION_PASS || i==OPTION_DKEY) { // do not output password or device key
  520.         continue;
  521.       } else {
  522.         html += F("\"");
  523.         html += o->name;
  524.         html += F("\":");
  525.         html += F("\"");
  526.         html += o->sval;
  527.         html += F("\"");
  528.         html += ",";
  529.       }
  530.     } else {  // if this is a int option
  531.       html += F("\"");
  532.       html += o->name;
  533.       html += F("\":");
  534.       html += o->ival;
  535.       html += ",";
  536.     }
  537.   }
  538.   html.remove(html.length()-1); // remove the extra ,
  539.   html += F("}");
  540.   server_send_html(html);
  541. }
  542.  
  543. void on_ap_scan() {
  544.   if(curr_mode == OG_MOD_STA) return;
  545.   server_send_html(scanned_ssids);
  546. }
  547.  
  548. void on_ap_change_config() {
  549.   if(curr_mode == OG_MOD_STA) return;
  550.   if(server->hasArg("ssid")&&server->arg("ssid").length()!=0) {
  551.     og.options[OPTION_SSID].sval = server->arg("ssid");
  552.     og.options[OPTION_PASS].sval = server->arg("pass");
  553.     // if cloud token is provided, save it
  554.     if(server->hasArg("auth")&&server->arg("auth").length()!=0)
  555.       og.options[OPTION_AUTH].sval = server->arg("auth");
  556.     og.options_save();
  557.     server_send_result(HTML_SUCCESS);
  558.     og.state = OG_STATE_TRY_CONNECT;
  559.  
  560.   } else {
  561.     server_send_result(HTML_DATA_MISSING, "ssid");
  562.   }
  563. }
  564.  
  565. void on_ap_try_connect() {
  566.   if(curr_mode == OG_MOD_STA) return;
  567.   String html = "{";
  568.   html += F("\"ip\":");
  569.   html += (WiFi.status() == WL_CONNECTED) ? (uint32_t)WiFi.localIP() : 0;
  570.   html += F("}");
  571.   server_send_html(html);
  572.   if(WiFi.status() == WL_CONNECTED && WiFi.localIP()) {
  573.     /*DEBUG_PRINTLN(F("STA connected, updating option file"));
  574.     og.options[OPTION_MOD].ival = OG_MOD_STA;
  575.     if(og.options[OPTION_AUTH].sval.length() == 32) {
  576.       og.options[OPTION_ACC].ival = OG_ACC_BOTH;
  577.     }
  578.     og.options_save();*/
  579.     DEBUG_PRINTLN(F("IP received by client, restart."));
  580.     //restart_ticker.once_ms(1000, og.restart); // restart once client receives IP address
  581.     restart_in(1000);
  582.   }
  583. }
  584.  
  585. // MQTT callback to read "Button" requests
  586. void mqtt_callback(const MQTT::Publish& pub) {
  587.   //DEBUG_PRINT("MQTT Message Received: ");
  588.   //DEBUG_PRINT(pub.topic());
  589.   //DEBUG_PRINT(" Data: ");
  590.   //DEBUG_PRINTLN(pub.payload_string());
  591.  
  592.   //Accept button on any topic for backwards compat with existing code - use IN messages below if possible
  593.   if (pub.payload_string() == "Button") {
  594.     DEBUG_PRINTLN(F("MQTT Button request received, change door state"));
  595.     if(!og.options[OPTION_ALM].ival) {
  596.       // if alarm is not enabled, trigger relay right away
  597.       og.click_relay();
  598.     }
  599.       else {
  600.       // else, set alarm
  601.       og.set_alarm();
  602.     }
  603.   }
  604.   //Accept click for consistency with api, open and close should be used instead, use IN topic if possible
  605.   if (pub.topic() == og.options[OPTION_NAME].sval +"/IN/STATE" ){
  606.     DEBUG_PRINT(F("MQTT IN Message detected, check data for action, Data:"));
  607.     DEBUG_PRINTLN(pub.payload_string());
  608.     if ( (pub.payload_string() == "close" && door_status) || (pub.payload_string() == "open" && !door_status) || pub.payload_string() == "click") {
  609.       DEBUG_PRINTLN(F("Command is valid based on existing state, trigger change"));
  610.       if(!og.options[OPTION_ALM].ival) {
  611.         // if alarm is not enabled, trigger relay right away
  612.         og.click_relay();
  613.       }
  614.       else {
  615.         // else, set alarm
  616.         og.set_alarm();
  617.       }
  618.     }else if ( (pub.payload_string() == "close" && !door_status) || (pub.payload_string() == "open" && door_status) ){
  619.       DEBUG_PRINTLN(F("Command request not valid, door already in requested state"));
  620.     }
  621.     else {
  622.       DEBUG_PRINT(F("Unrecognized MQTT data/command:"));
  623.       DEBUG_PRINTLN(pub.payload_string());
  624.     }
  625.   }
  626. }
  627.  
  628. void do_setup()
  629. {
  630.    
  631.   dht.begin();                              //DHT Temperature/humidity sensor
  632.   timer.setInterval(2000L, sendSensor);     //DHT Temperature/humidity sensor
  633.  
  634.   DEBUG_BEGIN(115200);
  635.   if(server) {
  636.     delete server;
  637.     server = NULL;
  638.   }
  639.   WiFi.persistent(false); // turn off persistent, fixing flash crashing issue
  640.   og.begin();
  641.   og.options_setup();
  642.   DEBUG_PRINT(F("Complile Info: "));
  643.   DEBUG_PRINT(F(__DATE__));
  644.   DEBUG_PRINT(F(" "));
  645.   DEBUG_PRINTLN(F(__TIME__));
  646.   curr_cloud_access_en = og.get_cloud_access_en();
  647.   curr_mode = og.get_mode();
  648.   if(!server) {
  649.     server = new ESP8266WebServer(og.options[OPTION_HTP].ival);
  650.     if(curr_mode == OG_MOD_AP) dns = new DNSServer();
  651.     DEBUG_PRINT(F("server started @ "));
  652.     DEBUG_PRINTLN(og.options[OPTION_HTP].ival);
  653.   }
  654.   led_blink_ms = LED_FAST_BLINK;
  655.  
  656. }
  657.  
  658. void process_ui()
  659. {
  660.   // process button
  661.   static ulong button_down_time = 0;
  662.   if(og.get_button() == LOW) {
  663.     if(!button_down_time) {
  664.       button_down_time = millis();
  665.     } else {
  666.       ulong curr = millis();
  667.       if(curr > button_down_time + BUTTON_FACRESET_TIMEOUT) {
  668.         led_blink_ms = 0;
  669.         og.set_led(LOW);
  670.       } else if(curr > button_down_time + BUTTON_APRESET_TIMEOUT) {
  671.         led_blink_ms = 0;
  672.         og.set_led(HIGH);
  673.       }
  674.     }
  675.   }
  676.   else {
  677.     if (button_down_time > 0) {
  678.       ulong curr = millis();
  679.       if(curr > button_down_time + BUTTON_FACRESET_TIMEOUT) {
  680.         og.state = OG_STATE_RESET;
  681.       } else if(curr > button_down_time + BUTTON_APRESET_TIMEOUT) {
  682.         og.reset_to_ap();
  683.       } else if(curr > button_down_time + BUTTON_REPORTIP_TIMEOUT) {        
  684.         // report IP
  685.         ipString = get_ip();
  686.         ipString.replace(".", ". ");
  687.         report_ip();
  688.       } else if(curr > button_down_time + 50) {
  689.         og.click_relay();
  690.       }
  691.       button_down_time = 0;
  692.     }
  693.   }
  694.   // process led
  695.   static ulong led_toggle_timeout = 0;
  696.   if(led_blink_ms) {
  697.     if(millis() > led_toggle_timeout) {
  698.       // toggle led
  699.       og.set_led(1-og.get_led());
  700.       led_toggle_timeout = millis() + led_blink_ms;
  701.     }
  702.   }  
  703. }
  704.  
  705. byte check_door_status_hist() {
  706.   // perform pattern matching of door status histogram
  707.   // and return the corresponding results
  708.   const byte allones = (1<<DOOR_STATUS_HIST_K)-1;       // 0b1111
  709.   const byte lowones = (1<<(DOOR_STATUS_HIST_K/2))-1; // 0b0011
  710.   const byte highones= lowones << (DOOR_STATUS_HIST_K/2); // 0b1100
  711.  
  712.   byte _hist = door_status_hist & allones;  // get the lowest K bits
  713.   if(_hist == 0) return DOOR_STATUS_REMAIN_CLOSED;
  714.   if(_hist == allones) return DOOR_STATUS_REMAIN_OPEN;
  715.   if(_hist == lowones) return DOOR_STATUS_JUST_OPENED;
  716.   if(_hist == highones) return DOOR_STATUS_JUST_CLOSED;
  717.  
  718.   return DOOR_STATUS_MIXED;
  719. }
  720.  
  721. void on_sta_update() {
  722.   String html = FPSTR(sta_update_html);
  723.   server_send_html(html);
  724. }
  725.  
  726. void on_ap_update() {
  727.   String html = FPSTR(ap_update_html);
  728.   server_send_html(html);
  729. }
  730.  
  731. void on_sta_upload_fin() {
  732.  
  733.   if(!verify_device_key()) {
  734.     server_send_result(HTML_UNAUTHORIZED);
  735.     Update.reset();
  736.     return;
  737.   }
  738.  
  739.   // finish update and check error
  740.   if(!Update.end(true) || Update.hasError()) {
  741.     server_send_result(HTML_UPLOAD_FAILED);
  742.     return;
  743.   }
  744.  
  745.   server_send_result(HTML_SUCCESS);
  746.   //restart_ticker.once_ms(1000, og.restart);
  747.   restart_in(1000);
  748. }
  749.  
  750. void on_ap_upload_fin() { on_sta_upload_fin(); }
  751.  
  752. void on_sta_upload() {
  753.   HTTPUpload& upload = server->upload();
  754.   if(upload.status == UPLOAD_FILE_START){
  755.     DEBUG_PRINTLN(F("Stopping all network clients"));
  756.     WiFiUDP::stopAll();
  757.     Blynk.disconnect(); // disconnect Blynk during firmware upload
  758.     mqttclient.disconnect();
  759.     DEBUG_PRINT(F("prepare to upload: "));
  760.     DEBUG_PRINTLN(upload.filename);
  761.     uint32_t maxSketchSpace = (ESP.getFreeSketchSpace()-0x1000)&0xFFFFF000;
  762.     if(!Update.begin(maxSketchSpace)) {
  763.       DEBUG_PRINTLN(F("not enough space"));
  764.     }
  765.   } else if(upload.status == UPLOAD_FILE_WRITE) {
  766.     DEBUG_PRINT(".");
  767.     if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
  768.       DEBUG_PRINTLN(F("size mismatch"));
  769.     }
  770.      
  771.   } else if(upload.status == UPLOAD_FILE_END) {
  772.    
  773.     DEBUG_PRINTLN(F("upload completed"));
  774.        
  775.   } else if(upload.status == UPLOAD_FILE_ABORTED){
  776.     Update.end();
  777.     DEBUG_PRINTLN(F("upload aborted"));
  778.   }
  779.   delay(0);    
  780. }
  781.  
  782. void on_ap_upload() {
  783.   HTTPUpload& upload = server->upload();
  784.   if(upload.status == UPLOAD_FILE_START){
  785.     Serial.println(F("prepare to upload: "));
  786.     Serial.println(upload.filename);
  787.     uint32_t maxSketchSpace = (ESP.getFreeSketchSpace()-0x1000)&0xFFFFF000;
  788.     if(!Update.begin(maxSketchSpace)) {
  789.       Serial.println(F("not enough space"));
  790.     }
  791.   } else if(upload.status == UPLOAD_FILE_WRITE) {
  792.     Serial.print(".");
  793.     if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
  794.       Serial.println(F("size mismatch"));
  795.     }
  796.      
  797.   } else if(upload.status == UPLOAD_FILE_END) {
  798.    
  799.     Serial.println(F("upload completed"));
  800.        
  801.   } else if(upload.status == UPLOAD_FILE_ABORTED){
  802.     Update.end();
  803.     Serial.println(F("upload aborted"));
  804.   }
  805.   delay(0);    
  806. }
  807.  
  808. void check_status_ap() {
  809.   static ulong cs_timeout = 0;
  810.   if(millis() > cs_timeout) {
  811.     Serial.println(og.read_distance());
  812.     Serial.println(OG_FWV);
  813.     cs_timeout = millis() + 2000;
  814.   }
  815. }
  816.  
  817. bool mqtt_connect_subscibe() {
  818.   static ulong mqtt_subscribe_timeout = 0;
  819.   if(curr_utc_time > mqtt_subscribe_timeout) {
  820.     if (!mqttclient.connected()) {
  821.       DEBUG_PRINT(F("MQTT Not connected- (Re)connect MQTT"));
  822.       mqttclient.set_server(og.options[OPTION_MQTT].sval, 1883);
  823.       if (mqttclient.connect(og.options[OPTION_NAME].sval)) {
  824.         mqttclient.set_callback(mqtt_callback);        
  825.         mqttclient.subscribe(og.options[OPTION_NAME].sval);
  826.         mqttclient.subscribe(og.options[OPTION_NAME].sval +"/IN/#");
  827.         DEBUG_PRINTLN(F("......Success, Subscribed to MQTT Topic"));
  828.         return true;
  829.       }else {
  830.         DEBUG_PRINTLN(F("......Failed to Connect to MQTT"));
  831.         mqtt_subscribe_timeout = curr_utc_time + 50; //Takes about 5 seconds to get through the loop
  832.         return false;
  833.       }
  834.     }
  835.   }
  836. }
  837.  
  838. void perform_notify(String s) {
  839.   DEBUG_PRINT(F("Sending Notify to connected systems, value:"));
  840.   DEBUG_PRINTLN(s);
  841.   // Blynk notification
  842.   if(curr_cloud_access_en && Blynk.connected()) {
  843.     DEBUG_PRINTLN(F(" Blynk Notify"));
  844.     Blynk.notify(s);
  845.   }
  846.  
  847.   // IFTTT notification
  848.   if(og.options[OPTION_IFTT].sval.length()>7) { // key size is at least 8
  849.     DEBUG_PRINTLN(" Sending IFTTT Notification");
  850.     http.begin("http://maker.ifttt.com/trigger/opengarage/with/key/"+og.options[OPTION_IFTT].sval);
  851.     http.addHeader("Content-Type", "application/json");
  852.     http.POST("{\"value1\":\""+s+"\"}");
  853.     String payload = http.getString();
  854.     http.end();
  855.     if(payload.indexOf("Congratulations") >= 0) {
  856.       DEBUG_PRINTLN(" Successfully updated IFTTT");
  857.     }else{
  858.       DEBUG_PRINT(" Error from IFTTT: ");
  859.       DEBUG_PRINTLN(payload);
  860.     }
  861.   }
  862.  
  863.   //Mqtt notification
  864.   if(og.options[OPTION_MQTT].sval.length()>8) {
  865.     if (mqttclient.connected()) {
  866.         DEBUG_PRINTLN(" Sending MQTT Notification");
  867.         mqttclient.publish(og.options[OPTION_NAME].sval + "/OUT/NOTIFY",s);
  868.     }
  869.   }
  870. }
  871.  
  872. void process_dynamics(byte event) {
  873.   static bool automationclose_triggered=false;
  874.   byte ato = og.options[OPTION_ATO].ival;
  875.   byte atob = og.options[OPTION_ATOB].ival;
  876.   byte noto = og.options[OPTION_NOTO].ival;
  877.   if(!ato && !atob && !noto) {
  878.     justopen_timestamp = 0;
  879.     return;
  880.   }
  881.   if(event == DOOR_STATUS_JUST_OPENED) {
  882.     justopen_timestamp = curr_utc_time; // record time stamp
  883.     if (noto & OG_NOTIFY_DO)
  884.       { perform_notify(og.options[OPTION_NAME].sval + " just OPENED!");}
  885.    
  886.     //If the door is set to auto close at a certain hour, ensure if manually opened it doesn't autoshut
  887.     if( (curr_utc_hour == og.options[OPTION_ATIB].ival) && (!automationclose_triggered) ){
  888.       DEBUG_PRINTLN(" Door opened during automation hour, set to not auto-close ");
  889.       automationclose_triggered=true;
  890.     }
  891.  
  892.   } else if (event == DOOR_STATUS_JUST_CLOSED) {
  893.     justopen_timestamp = 0; // reset time stamp
  894.     if (noto & OG_NOTIFY_DC)
  895.       { perform_notify(og.options[OPTION_NAME].sval + " just CLOSED!");}
  896.  
  897.   } else if (event == DOOR_STATUS_REMAIN_OPEN) {
  898.     if (!justopen_timestamp) justopen_timestamp = curr_utc_time; // record time stamp
  899.     else {
  900.       if(curr_utc_time > justopen_timestamp + (ulong)og.options[OPTION_ATI].ival*60L) {
  901.         // reached timeout, perform action
  902.         if(ato & OG_AUTO_NOTIFY) {
  903.           // send notification
  904.           String s = og.options[OPTION_NAME].sval+" is left open for more than ";
  905.           s+= og.options[OPTION_ATI].ival;
  906.           s+= " minutes.";
  907.           if(ato & OG_AUTO_CLOSE) {
  908.             s+= " It will be auto-closed shortly";
  909.           } else {
  910.             s+= " This is a reminder for you.";
  911.           }
  912.           perform_notify(s);
  913.         }
  914.         if(ato & OG_AUTO_CLOSE) {
  915.           // auto close door
  916.           // alarm is mandatory in auto-close
  917.           if(!og.options[OPTION_ALM].ival) { og.set_alarm(OG_ALM_5); }
  918.           else { og.set_alarm(); }
  919.         }
  920.         justopen_timestamp = 0;
  921.       }
  922.      
  923.       if(( curr_utc_hour == og.options[OPTION_ATIB].ival) && (!automationclose_triggered)) {
  924.         // still open past time, perform action
  925.         DEBUG_PRINTLN("Door is open at specified close time and automation not yet triggered: ");
  926.         automationclose_triggered=true;
  927.         if(atob & OG_AUTO_NOTIFY) {
  928.           // send notification
  929.           String s = og.options[OPTION_NAME].sval+" is open after ";
  930.           s+= og.options[OPTION_ATIB].ival;
  931.           s+= " UTC. Current hour:";
  932.           s+= curr_utc_hour;
  933.           if(atob & OG_AUTO_CLOSE) {
  934.             s+= " It will be auto-closed shortly";
  935.           } else {
  936.             s+= " This is a reminder for you.";
  937.           }
  938.           perform_notify(s);
  939.         }
  940.         if(atob & OG_AUTO_CLOSE) {
  941.           // auto close door
  942.           // alarm is mandatory in auto-close
  943.           if(!og.options[OPTION_ALM].ival) { og.set_alarm(OG_ALM_5); }
  944.           else {
  945.             og.set_alarm();
  946.           }
  947.         }
  948.         justopen_timestamp = 0;
  949.       }
  950.       else if ((curr_utc_hour > og.options[OPTION_ATIB].ival) && (automationclose_triggered))
  951.       {
  952.         DEBUG_PRINTLN("Unlocking automation close function");
  953.         automationclose_triggered=false; //Unlock the hour after the setting
  954.       }
  955.     }
  956.   } else {
  957.     justopen_timestamp = 0;
  958.   }
  959. }
  960.  
  961. void check_status() {
  962.   static ulong checkstatus_timeout = 0;
  963.   static ulong checkstatus_report_timeout = 0;
  964.   if((curr_utc_time > checkstatus_timeout) || (checkstatus_timeout == 0))  { //also check on first boot
  965.     og.set_led(HIGH);
  966.     aux_ticker.once_ms(100, og.set_led, (byte)LOW);
  967.     uint threshold = og.options[OPTION_DTH].ival;
  968.     uint vthreshold = og.options[OPTION_VTH].ival;
  969.     if ((og.options[OPTION_MNT].ival == OG_MNT_SIDE) || (og.options[OPTION_MNT].ival == OG_MNT_CEILING)){
  970.       //sensor is ultrasonic
  971.       distance = og.read_distance();
  972.       door_status = (distance>threshold)?0:1;
  973.       if (og.options[OPTION_MNT].ival == OG_MNT_SIDE){
  974.        door_status = 1-door_status;  // reverse logic for side mount
  975.        vehicle_status = 3;}
  976.       else {
  977.         if (vthreshold >0) {
  978.           if (!door_status) {
  979.             vdistance = distance;
  980.             vehicle_status = ((vdistance>threshold) && (vdistance <=vthreshold))?1:0;
  981.           }else{vehicle_status = 2;}
  982.         }else {vehicle_status = 3;}
  983.       }
  984.     }else if (og.options[OPTION_MNT].ival == OG_SWITCH_LOW){
  985.       vehicle_status= 3;
  986.       if (og.get_switch() == LOW){
  987.         //DEBUG_PRINTLN("Low Mount Switch reads LOW, setting distance to high value (indicating closed)");
  988.         door_status =0;
  989.         distance = threshold + 20;
  990.       }
  991.       else{
  992.         //DEBUG_PRINTLN("Low Mount Switch reads HIGH, setting distance to low value (indicating open)");
  993.         door_status =1;
  994.         distance = threshold - 20;
  995.       }
  996.     }else if (og.options[OPTION_MNT].ival == OG_SWITCH_HIGH){
  997.       vehicle_status= 3;
  998.       if (og.get_switch() == LOW){
  999.         //DEBUG_PRINTLN("High Mount Switch reads LOW, setting distance to low value (indicating open)");
  1000.         door_status =1;
  1001.         distance = threshold - 20;
  1002.       }
  1003.       else{
  1004.         //DEBUG_PRINTLN("High Mount Switch reads HIGH, setting distance to high value (indicating closed)");
  1005.         door_status =0;
  1006.         distance = threshold + 20;
  1007.       }
  1008.     }
  1009.     read_cnt = (read_cnt+1)%100;
  1010.     if (checkstatus_timeout == 0){
  1011.       DEBUG_PRINTLN(F("First time checking status don't trigger a status change, set full history to current value"));
  1012.       if (door_status) { door_status_hist = B11111111; }
  1013.       else { door_status_hist = B00000000; }
  1014.     }else{
  1015.        door_status_hist = (door_status_hist<<1) | door_status;
  1016.     }
  1017.     //DEBUG_PRINT(F("Histogram value:"));
  1018.     //DEBUG_PRINTLN(door_status_hist);
  1019.     //DEBUG_PRINT(F("Vehicle Status:"));
  1020.     //DEBUG_PRINTLN(vehicle_status);
  1021.     byte event = check_door_status_hist();
  1022.  
  1023.     //Upon change
  1024.     if(event == DOOR_STATUS_JUST_OPENED || event == DOOR_STATUS_JUST_CLOSED) {
  1025.       // write log record
  1026.       DEBUG_PRINTLN(" Update Local Log");
  1027.       LogStruct l;
  1028.       l.tstamp = curr_utc_time;
  1029.       l.status = door_status;
  1030.       l.dist = distance;
  1031.       og.write_log(l);
  1032.  
  1033. #if 0
  1034.       //Debug Beep (only if sound is enabled)
  1035.       if(og.options[OPTION_ALM].ival){
  1036.         og.play_note(1000);
  1037.         delay(500);
  1038.         og.play_note(0);
  1039.       }
  1040.       DEBUG_PRINT(curr_utc_time);
  1041.       if(event == DOOR_STATUS_JUST_OPENED)  {  
  1042.         DEBUG_PRINTLN(F(" Sending State Change event to connected systems, value: DOOR_STATUS_JUST_OPENED")); }
  1043.       else if(event == DOOR_STATUS_JUST_CLOSED) {  
  1044.         DEBUG_PRINTLN(F(" Sending State Change event to connected systems, value: DOOR_STATUS_JUST_CLOSED")); }
  1045. #endif
  1046.      
  1047.       // Blynk notification
  1048. #if 0
  1049.       byte ato = og.options[OPTION_ATO].ival;
  1050.       if(curr_cloud_access_en && Blynk.connected() && ato) {
  1051.         //The official firmware only sends these notifications on ato enabled (which seems a somewhat unrelated function)
  1052.         //Maintain backwards compat and use same logic
  1053.         DEBUG_PRINTLN(F(" Notify Blynk with text notification"));
  1054.         if(event == DOOR_STATUS_JUST_OPENED)  {
  1055.           Blynk.notify(og.options[OPTION_NAME].sval + " just opened!");}
  1056.         else if(event == DOOR_STATUS_JUST_CLOSED) {
  1057.           Blynk.notify(og.options[OPTION_NAME].sval + " just closed!");}
  1058.       }
  1059.  
  1060.       // IFTTT notification
  1061.       if(og.options[OPTION_IFTT].sval.length()>7) { // key size is at least 8
  1062.         DEBUG_PRINTLN(F(" Notify IFTTT (State Change)"));
  1063.         http.begin("http://maker.ifttt.com/trigger/opengarage/with/key/"+og.options[OPTION_IFTT].sval);
  1064.         http.addHeader("Content-Type", "application/json");
  1065.         http.POST("{\"value1\":\""+String(event,DEC)+"\"}");
  1066.         String payload = http.getString();
  1067.         http.end();
  1068.         if(payload.indexOf("Congratulations") >= 0) {
  1069.           DEBUG_PRINTLN(F("  Successfully updated IFTTT"));
  1070.         }else{
  1071.           DEBUG_PRINT(F("  ERROR from IFTTT: "));
  1072.           DEBUG_PRINTLN(payload);
  1073.         }
  1074.       }
  1075.  
  1076.       //Mqtt notification
  1077.       if(og.options[OPTION_MQTT].sval.length()>8) {
  1078.         if (mqttclient.connected()) {
  1079.           DEBUG_PRINTLN(F(" Update MQTT (State Change)"));
  1080.           mqttclient.publish(og.options[OPTION_NAME].sval + "/OUT/CHANGE",String(event,DEC));
  1081.         }
  1082.       }
  1083. #endif
  1084.     } //End state change updates
  1085.  
  1086.     //Send current status only on change and longer interval
  1087.     if ((curr_utc_time >checkstatus_report_timeout) || (event == DOOR_STATUS_JUST_OPENED || event == DOOR_STATUS_JUST_CLOSED) ){
  1088. #if 0
  1089.       DEBUG_PRINT(curr_utc_time);
  1090.       if(event == DOOR_STATUS_REMAIN_OPEN)  {  
  1091.         DEBUG_PRINTLN(F(" Sending State Refresh to connected systems, value: OPEN")); }
  1092.       else if(event == DOOR_STATUS_REMAIN_CLOSED) {
  1093.         DEBUG_PRINTLN(F(" Sending State Refresh to connected systems, value: CLOSED")); }
  1094. #endif
  1095.       // report status to Blynk
  1096.       if(curr_cloud_access_en && Blynk.connected()) {
  1097.         DEBUG_PRINTLN(F(" Update Blynk (State Refresh)"));
  1098.         Blynk.virtualWrite(BLYNK_PIN_DIST, distance);
  1099.         (door_status) ? blynk_led.on() : blynk_led.off();
  1100.         Blynk.virtualWrite(BLYNK_PIN_IP, get_ip());
  1101.         blynk_lcd.print(0, 0, get_ip());
  1102.         String str = ":";
  1103.         str += og.options[OPTION_HTP].ival;
  1104.         str += " " + get_ap_ssid();
  1105.         blynk_lcd.print(0, 1, str);
  1106.       }
  1107.      
  1108.       //IFTTT only recieves state change events not ongoing status
  1109.  
  1110.       //Mqtt update
  1111.       if((og.options[OPTION_MQTT].sval.length()>8) && (mqttclient.connected())) {
  1112.         DEBUG_PRINTLN(F(" Update MQTT (State Refresh)"));
  1113.         if(door_status == DOOR_STATUS_REMAIN_OPEN)  {                       // MQTT: If door open...
  1114.           mqttclient.publish(og.options[OPTION_NAME].sval + "/OUT/STATE","OPEN");
  1115.           mqttclient.publish(og.options[OPTION_NAME].sval,"Open"); //Support existing mqtt code
  1116.           //DEBUG_PRINTLN(curr_utc_time + " Sending MQTT State otification: OPEN");
  1117.         }
  1118.         else if(door_status == DOOR_STATUS_REMAIN_CLOSED) {                 // MQTT: If door closed...
  1119.           mqttclient.publish(og.options[OPTION_NAME].sval + "/OUT/STATE","CLOSED");
  1120.           mqttclient.publish(og.options[OPTION_NAME].sval,"Closed"); //Support existing mqtt code
  1121.           //DEBUG_PRINTLN(curr_utc_time + " Sending MQTT State Notification: CLOSED");
  1122.         }
  1123.       }
  1124.       // Send status report every 15 seconds: we don't need to send updates frequently if there is no status change.
  1125.       checkstatus_report_timeout= curr_utc_time + 15;
  1126.     }
  1127.    
  1128.     // Process dynamics: automation and notifications
  1129.     process_dynamics(event);
  1130.     checkstatus_timeout = curr_utc_time + og.options[OPTION_RIV].ival;
  1131.    
  1132.   }
  1133. }
  1134.  
  1135. void time_keeping() {
  1136.   static bool configured = false;
  1137.   static ulong prev_millis = 0;
  1138.   static ulong time_keeping_timeout = 0;
  1139.  
  1140.   if(!configured) {
  1141.     DEBUG_PRINTLN(F("Set time server"));
  1142.     configTime(0, 0, "pool.ntp.org", "time.nist.org", NULL);
  1143.     configured = true;
  1144.   }
  1145.  
  1146.   if(!curr_utc_time || (curr_utc_time > time_keeping_timeout)) {
  1147.     ulong gt = time(nullptr);
  1148.     if(!gt) {
  1149.       // if we didn't get response, re-try after 2 seconds
  1150.       time_keeping_timeout = curr_utc_time + 2;
  1151.     } else {
  1152.       curr_utc_time = gt;
  1153.       curr_utc_hour = (curr_utc_time % 86400)/3600;
  1154.       DEBUG_PRINT(F("Updated time from NTP: "));
  1155.       DEBUG_PRINT(curr_utc_time);
  1156.       DEBUG_PRINT(" Hour: ");
  1157.       DEBUG_PRINTLN(curr_utc_hour);
  1158.       // if we got a response, re-try after TIME_SYNC_TIMEOUT seconds
  1159.       time_keeping_timeout = curr_utc_time + TIME_SYNC_TIMEOUT;
  1160.       prev_millis = millis();
  1161.     }
  1162.   }
  1163.   while(millis() - prev_millis >= 1000) {
  1164.     curr_utc_time ++;
  1165.     curr_utc_hour = (curr_utc_time % 86400)/3600;
  1166.     prev_millis += 1000;
  1167.   }
  1168. }
  1169.  
  1170. void process_alarm() {
  1171.   if(!og.alarm) return;
  1172.   static ulong prev_half_sec = 0;
  1173.   ulong curr_half_sec = millis()/500;
  1174.   if(curr_half_sec != prev_half_sec) {  
  1175.     prev_half_sec = curr_half_sec;
  1176.     if(prev_half_sec % 2 == 0) {
  1177.       og.play_note(ALARM_FREQ);
  1178.     } else {
  1179.       og.play_note(0);
  1180.     }
  1181.     og.alarm--;
  1182.     if(og.alarm==0) {
  1183.       og.play_note(0);
  1184.       og.click_relay();
  1185.     }
  1186.   }
  1187. }
  1188.  
  1189.  
  1190. void do_loop() {
  1191.   static ulong connecting_timeout;
  1192.   timer.run();                      //DHT Temperature/humidity sensor  
  1193.  
  1194.   switch(og.state) {
  1195.   case OG_STATE_INITIAL:
  1196.     if(curr_mode == OG_MOD_AP) {
  1197.       scanned_ssids = scan_network();
  1198.       String ap_ssid = get_ap_ssid();
  1199.       start_network_ap(ap_ssid.c_str(), NULL);
  1200.       delay(500);
  1201.       dns->setErrorReplyCode(DNSReplyCode::NoError);
  1202.       dns->start(53, "*", WiFi.softAPIP());
  1203.       server->on("/",   on_home);    
  1204.       server->on("/js", on_ap_scan);
  1205.       server->on("/cc", on_ap_change_config);
  1206.       server->on("/jt", on_ap_try_connect);
  1207.       server->on("/update", HTTP_GET, on_ap_update);
  1208.       server->on("/update", HTTP_POST, on_ap_upload_fin, on_ap_upload);      
  1209.       server->on("/resetall",on_reset_all);
  1210.       server->onNotFound(on_home);
  1211.       server->begin();
  1212.       DEBUG_PRINTLN(F("Web Server endpoints (AP mode) registered"));
  1213.       og.state = OG_STATE_CONNECTED;
  1214.       DEBUG_PRINTLN(WiFi.softAPIP());
  1215.       connecting_timeout = 0;
  1216.     } else {
  1217.       led_blink_ms = LED_SLOW_BLINK;
  1218.       DEBUG_PRINT(F("Attempting to connect to SSID: "));
  1219.       DEBUG_PRINTLN(og.options[OPTION_SSID].sval.c_str());
  1220.       start_network_sta(og.options[OPTION_SSID].sval.c_str(), og.options[OPTION_PASS].sval.c_str());
  1221.       og.config_ip();
  1222.       og.state = OG_STATE_CONNECTING;
  1223.       connecting_timeout = millis() + 60000;
  1224.     }
  1225.     break;
  1226.  
  1227.   case OG_STATE_TRY_CONNECT:
  1228.     led_blink_ms = LED_SLOW_BLINK;
  1229.     DEBUG_PRINT(F("Attempting to connect to SSID: "));
  1230.     DEBUG_PRINTLN(og.options[OPTION_SSID].sval.c_str());
  1231.     start_network_sta_with_ap(og.options[OPTION_SSID].sval.c_str(), og.options[OPTION_PASS].sval.c_str());
  1232.     og.config_ip();
  1233.     og.state = OG_STATE_CONNECTED;
  1234.     break;
  1235.    
  1236.   case OG_STATE_CONNECTING:
  1237.     if(WiFi.status() == WL_CONNECTED) {
  1238.       DEBUG_PRINT(F("Wireless connected, IP: "));
  1239.       DEBUG_PRINTLN(WiFi.localIP());
  1240.  
  1241.       // use ap ssid as mdns name
  1242.       if(MDNS.begin(get_ap_ssid().c_str())) {
  1243.         DEBUG_PRINTLN(F("MDNS registered"));
  1244.       }
  1245.       server->on("/", on_home);
  1246.       server->on("/jc", on_sta_controller);
  1247.       server->on("/jo", on_sta_options);
  1248.       server->on("/jl", on_sta_logs);
  1249.       server->on("/vo", on_sta_view_options);
  1250.       //server->serveStatic("/sta_options.html", SPIFFS, "/sta_options.html");
  1251.       server->on("/vl", on_sta_view_logs);
  1252.       server->on("/cc", on_sta_change_controller);
  1253.       server->on("/co", on_sta_change_options);
  1254.       server->on("/db", on_sta_debug);
  1255.       server->on("/update", HTTP_GET, on_sta_update);
  1256.       server->on("/update", HTTP_POST, on_sta_upload_fin, on_sta_upload);
  1257.       server->on("/clearlog", on_clear_log);
  1258.       /*server->serveStatic("/DoorOpen.png", SPIFFS, "/DoorOpen.png","max-age=86400");
  1259.       server->serveStatic("/DoorShut.png", SPIFFS, "/DoorShut.png","max-age=86400");
  1260.       server->serveStatic("/ClosedAbsent.png", SPIFFS, "/ClosedAbsent.png","max-age=86400");
  1261.       server->serveStatic("/ClosedPresent.png", SPIFFS, "/ClosedPresent.png","max-age=86400");
  1262.       server->serveStatic("/Open.png", SPIFFS, "/Open.png","max-age=86400");*/
  1263.       server->on("/resetall",on_reset_all);
  1264.       //server->on("/test",on_test);
  1265.       server->begin();
  1266.       DEBUG_PRINTLN(F("Web Server endpoints (STA mode) registered"));
  1267.  
  1268.       if(curr_cloud_access_en) {
  1269.         Blynk.config(og.options[OPTION_AUTH].sval.c_str()); // use the config function
  1270.         Blynk.connect();
  1271.         DEBUG_PRINTLN(F("Blynk Connected"));
  1272.       }
  1273.       if(og.options[OPTION_MQTT].sval.length()>8) {
  1274.         mqtt_connect_subscibe();
  1275.         DEBUG_PRINTLN(F("MQTT Connected"));
  1276.       }
  1277.       led_blink_ms = 0;
  1278.       og.set_led(LOW);
  1279.       og.state = OG_STATE_CONNECTED;
  1280.       connecting_timeout = 0;
  1281.     } else {
  1282.       if(millis() > connecting_timeout) {
  1283.         DEBUG_PRINTLN(F("Wifi Connecting timeout, restart"));
  1284.         og.restart();
  1285.       }
  1286.     }
  1287.     break;
  1288.  
  1289.   case OG_STATE_RESET:
  1290.     og.state = OG_STATE_INITIAL;
  1291.     og.options_reset();
  1292.     og.log_reset();
  1293.     og.restart();
  1294.     break;
  1295.    
  1296.   case OG_STATE_WAIT_RESTART:
  1297.     if(dns) dns->processNextRequest();  
  1298.     if(server) server->handleClient();    
  1299.     break;
  1300.    
  1301.   case OG_STATE_CONNECTED: //THIS IS THE MAIN LOOP
  1302.     if(curr_mode == OG_MOD_AP) {
  1303.       dns->processNextRequest();
  1304.       server->handleClient();
  1305.       check_status_ap();
  1306.       connecting_timeout = 0;
  1307.       if(og.options[OPTION_MOD].ival == OG_MOD_STA) {
  1308.         // already in STA mode, waiting to reboot
  1309.         break;
  1310.       }
  1311.       if(WiFi.status() == WL_CONNECTED && WiFi.localIP()) {
  1312.         DEBUG_PRINTLN(F("STA connected, updating option file"));
  1313.         og.options[OPTION_MOD].ival = OG_MOD_STA;
  1314.         og.options_save();
  1315.         og.play_startup_tune();
  1316.         //restart_ticker.once_ms(10000, og.restart);
  1317.         restart_in(10000);
  1318.       }
  1319.      
  1320.     } else {
  1321.       if(WiFi.status() == WL_CONNECTED) {
  1322.         time_keeping();
  1323.         check_status(); //This checks the door, sends info to services and processes the automation rules
  1324.         server->handleClient();
  1325.  
  1326.         if(curr_cloud_access_en)
  1327.           Blynk.run();
  1328.         //Handle MQTT
  1329.         if(og.options[OPTION_MQTT].sval.length()>8) {
  1330.           if (!mqttclient.connected()) {
  1331.             mqtt_connect_subscibe();
  1332.           }
  1333.           else {mqttclient.loop();} //Processes MQTT Pings/keep alives
  1334.         }
  1335.         connecting_timeout = 0;
  1336.       } else {
  1337.         //og.state = OG_STATE_INITIAL;
  1338.         if(!connecting_timeout) {
  1339.           DEBUG_PRINTLN(F("State is CONNECTED but WiFi is disconnected, start timeout counter."));
  1340.           connecting_timeout = millis()+60000;
  1341.         }
  1342.         else if(millis() > connecting_timeout) {
  1343.           DEBUG_PRINTLN(F("timeout reached, reboot"));
  1344.           og.restart();
  1345.         }
  1346.       }
  1347.     }
  1348.     break;
  1349.   }
  1350.  
  1351.   //Nework independent functions, handle events like reset even when not connected
  1352.   process_ui();
  1353.   if(og.alarm)
  1354.     process_alarm();
  1355. }
  1356.  
  1357. BLYNK_WRITE(V1)
  1358. {
  1359.   DEBUG_PRINTLN(F("Received Blynk generated button request"));
  1360.   if(!og.options[OPTION_ALM].ival) {
  1361.     // if alarm is disabled, trigger right away
  1362.     if(param.asInt()) {
  1363.       og.set_relay(HIGH);
  1364.     } else {
  1365.       og.set_relay(LOW);
  1366.     }
  1367.   } else {
  1368.     // otherwise, set alarm
  1369.     if(param.asInt()) {
  1370.       og.set_alarm();
  1371.     }  
  1372.   }
  1373. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement