Advertisement
Guest User

Untitled

a guest
Sep 6th, 2021
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 41.46 KB | None | 0 0
  1. /*
  2.  
  3.   This is a simple MJPEG streaming webserver implemented for AI-Thinker ESP32-CAM
  4.   and ESP-EYE modules.
  5.   This is tested to work with VLC and Blynk video widget and can support up to 10
  6.   simultaneously connected streaming clients.
  7.   Simultaneous streaming is implemented with dedicated FreeRTOS tasks.
  8.  
  9.   Inspired by and based on this Instructable: $9 RTSP Video Streamer Using the ESP32-CAM Board
  10.   (https://www.instructables.com/id/9-RTSP-Video-Streamer-Using-the-ESP32-CAM-Board/)
  11.  
  12.   Board: AI-Thinker ESP32-CAM or ESP-EYE
  13.   Compile as:
  14.    ESP32 Dev Module
  15.    CPU Freq: 240
  16.    Flash Freq: 80
  17.    Flash mode: QIO
  18.    Flash Size: 4Mb
  19.    Partrition: Minimal SPIFFS
  20.    PSRAM: Enabled
  21. */
  22.  
  23. // ESP32 has two cores: APPlication core and PROcess core (the one that runs ESP32 SDK stack)
  24. #define APP_CPU 1
  25. #define PRO_CPU 0
  26.  
  27. #include "esp_camera.h"
  28. #include "ov2640.h"
  29. #include <WiFi.h>
  30. #include <WebServer.h>
  31. #include <WiFiClient.h>
  32. #include <EEPROM.h>
  33. #include <StringSplitter.h>
  34.  
  35. #include <esp_bt.h>
  36. #include <esp_wifi.h>
  37. #include <esp_sleep.h>
  38. #include <driver/rtc_io.h>
  39.  
  40. #include <Adafruit_INA219.h>
  41. #include <Wire.h>
  42. #include <driver/adc.h>
  43. #include "driver/rtc_io.h"
  44. #include "driver/gpio.h"
  45. #include <esp_adc_cal.h>
  46. #include <HTTPClient.h>
  47.  
  48. #include "soc/soc.h"
  49. #include "soc/rtc_cntl_reg.h"
  50.  
  51. #define CAMERA_MODEL_AI_THINKER
  52.  
  53. #define MAX_CLIENTS   10
  54.  
  55. #include "camera_pins.h"
  56.  
  57. /*
  58.   Next one is an include with wifi credentials.
  59.   This is what you need to do:
  60.  
  61.   1. Create a file called "home_wifi_multi.h" in the same folder   OR   under a separate subfolder of the "libraries" folder of Arduino IDE. (You are creating a "fake" library really - I called it "MySettings").
  62.   2. Place the following text in the file:
  63.   #define SSID1 "replace with your wifi ssid"
  64.   #define PWD1 "replace your wifi password"
  65.   3. Save.
  66.  
  67.   Should work then
  68. */
  69.  
  70. bool disable_brownout_detector = true;
  71.  
  72. uint8_t dis_count = 0;
  73. bool sleep_status = false;
  74. bool client_connected = true;
  75. uint8_t sleep_cooldown_base = 20;
  76. uint8_t sleep_cooldown = sleep_cooldown_base;
  77. bool LED_control_active = false;
  78.  
  79. #define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
  80. #define TIME_TO_SLEEP  10        /* Time ESP32 will go to sleep (in seconds) */
  81.  
  82. #define I2C_SDA 14
  83. #define I2C_SCL 15
  84. TwoWire I2CSensors = TwoWire(0);
  85. Adafruit_INA219 ina219_bat(0x40);
  86. Adafruit_INA219 ina219_sol(0x41);
  87.  
  88. char ssid_char[100];
  89. char pass_char[100];
  90.  
  91.  
  92. #define LED_BUILTIN 4
  93.  
  94. void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  95.   Serial.println("Connected to AP successfully!");
  96. }
  97.  
  98. void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
  99.   Serial.println("WiFi connected");
  100.   Serial.println("IP address: ");
  101.   Serial.println(WiFi.localIP());
  102. }
  103.  
  104. void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  105.   Serial.println("Disconnected from WiFi access point");
  106.   Serial.print("WiFi lost connection. Reason: ");
  107.   Serial.println(info.disconnected.reason);
  108. }
  109.  
  110.  
  111.  
  112. float p_current = 0;
  113. int measurements = 0;
  114. int active_sec = 0;
  115.  
  116. float bat_shuntvoltage = 0;
  117. float bat_busvoltage = 0;
  118. float bat_current_mA = 0;
  119. float bat_loadvoltage = 0;
  120. float bat_power_mW = 0;
  121.  
  122. float sol_shuntvoltage = 0;
  123. float sol_busvoltage = 0;
  124. float sol_current_mA = 0;
  125. float sol_loadvoltage = 0;
  126. float sol_power_mW = 0;
  127.  
  128. float free_memory = 0;
  129. int wifi_signal = 0;
  130. int batteryPercent = 0;
  131.  
  132. String WiFi_mode = "AP";
  133. String memMODE = "";
  134. String memSSID = "";
  135. String memPASS = "";
  136. String memIP = "";
  137.  
  138. bool rebooting = false;
  139. String msg = "";
  140.  
  141.  
  142.  
  143. void writeEEPROM(int addrOffset, const String &strToWrite)
  144. {
  145.   byte len = strToWrite.length();
  146.   Serial.println("Writing to EEPROM:");
  147.   EEPROM.write(addrOffset, len);
  148.   for (int i = 0; i < len; i++)
  149.   {
  150.     EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
  151.     Serial.print(strToWrite[i]);
  152.   }
  153. }
  154.  
  155.  
  156. String readEEPROM(int addrOffset)
  157. {
  158.   int newStrLen = EEPROM.read(addrOffset);
  159.   char data[newStrLen + 1];
  160.   Serial.println("Reading from EEPROM:");
  161.   for (int i = 0; i < newStrLen; i++)
  162.   {
  163.     data[i] = EEPROM.read(addrOffset + 1 + i);
  164.     Serial.print(data[i]);
  165.   }
  166.   data[newStrLen] = '\0';
  167.   return String(data);
  168. }
  169.  
  170. WebServer server(80);
  171.  
  172. void handleStatus()
  173. {
  174.   sleep_cooldown = sleep_cooldown_base;
  175.   client_connected = true;  
  176.     if (sleep_status == true)
  177.     {
  178.       wakeModemSleep();
  179.     }
  180.   server.sendHeader("Connection", "keep-alive");
  181.   server.sendHeader("Keep-Alive", "timeout=2000");
  182.   server.sendHeader("Access-Control-Allow-Origin", "*");
  183.   server.send(200, "text/json", getStatus());  
  184. }
  185.  
  186.  
  187. void handleRoot()
  188. {
  189.     sleep_cooldown = sleep_cooldown_base;
  190.     client_connected = true;
  191.     if (sleep_status == true)
  192.     {
  193.       wakeModemSleep();
  194.     }
  195.  
  196. String myVariable = "@";
  197. String JS = "document.addEventListener(\'DOMContentLoaded\', function (event) {\n  var baseHost = document.location.origin;\n  var streamUrl = baseHost + \'/stream\';\n  \n\n  const hide = el => {\n    el.classList.add(\'hidden\')\n  }\n  const show = el => {\n    el.classList.remove(\'hidden\')\n  }\n\n  const disable = el => {\n    el.classList.add(\'disabled\')\n    el.disabled = true\n  }\n\n  const enable = el => {\n    el.classList.remove(\'disabled\')\n    el.disabled = false\n  }\n\n  const updateValue = (el, value, updateRemote) => {\n    updateRemote = updateRemote == null ? true : updateRemote\n    let initialValue\n    if (el.type === \'checkbox\') {\n      initialValue = el.checked\n      value = !!value\n      el.checked = value\n    } else {\n      initialValue = el.value\n      el.value = value\n    }\n\n    if (updateRemote \&\& initialValue !== value) {\n      updateConfig(el);\n    } else if(!updateRemote){\n\n    }\n  }\n\n  function updateConfig (el) {\n    let value\n    switch (el.type) {\n      case \'checkbox\':\n        value = el.checked ? 1 : 0\n        break\n      case \'range\':\n      case \'select-one\':\n        value = el.value\n        break\n      case \'button\':\n      case \'submit\':\n        value = \'1\'\n        break\n      default:\n        return\n    }\n\n    const query = `${baseHost}/control?var=${el.id}\&val=${value}`\n\n    fetch(query)\n      .then(response => {\n        console.log(`request to ${query} finished, status: ${response.status}`)\n      })\n  }\n\n  document\n    .querySelectorAll(\'.close\')\n    .forEach(el => {\n      el.onclick = () => {\n        hide(el.parentNode)\n      }\n    })\n\n\n  fetch(`${baseHost}/control_status`)\n    .then(function (response) {\n      return response.json()\n    })\n    .then(function (state) {\n      document\n        .querySelectorAll(\'.default-action\')\n        .forEach(el => {\n          updateValue(el, state[el.id], false)\n        })\n    })\n\n  const view = document.getElementById(\'stream\')\n  const viewContainer = document.getElementById(\'stream-container\')\n  view.src = `${streamUrl}`\n\n\n\n  document\n    .querySelectorAll(\'.default-action\')\n    .forEach(el => {\n      el.onchange = () => updateConfig(el)\n    })\n\n\n})\nsetInterval(function() {\n\tvar baseHost = document.location.origin;\n\tfetch(`${baseHost + \'/upd\'}`)\t\t\n\t\t.then(x => x.json())\n\t\t.then(obj => {\n\t\t\tdocument.getElementById(\"bat_percent\").innerHTML = obj.batteryPercent + \"%\";\t\n\t\t\tdocument.getElementById(\"bat_voltage\").innerHTML = obj.batteryVoltage + \"V\";\n\t\t\tdocument.getElementById(\"bat_current\").innerHTML = obj.batteryCurrent + \"mA\";\n\t\t\tdocument.getElementById(\"wifi_signal\").innerHTML = obj.wifiSignal + \"%\";\n\t\t\tdocument.getElementById(\"sol_voltage\").innerHTML = obj.SolarVoltage + \"V\";\n\t\t\tdocument.getElementById(\"sol_current\").innerHTML = obj.SolarCurrent + \"mA\";\n\t\t\tdocument.getElementById(\"free_mem\").innerHTML = obj.memory + \"kB\";\n\t\t\tdocument.getElementById(\"active_time\").innerHTML = obj.active_time + \"s\";\n\t\t});\n\n\n}, 5000);";
  198.  
  199. String html ="<!doctype html> <html> <head> <meta charset=\"utf-8\"> <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> <title>ESP32_CAM</title> <style>body{ font-family:Arial,Helvetica,sans-serif; background:#181818; color:#efefef; font-size:16px } h2{ font-size:18px } section.main{ display:flex } #menu,section.main{ flex-direction:column } #menu{ display:none; flex-wrap:nowrap; min-width:170px; background:#363636; padding:8px; border-radius:4px; margin-top:-10px; margin-right:10px } #content{ display:flex; flex-direction: column; align-items: stretch; justify-content: center; } figure{ padding:0; margin:0; -webkit-margin-before:0; margin-block-start:0; -webkit-margin-after:0; margin-block-end:0; -webkit-margin-start:0; margin-inline-start:0; -webkit-margin-end:0; margin-inline-end:0 } figure img{ display:block; width:100%; height:auto; border-radius:4px; margin-top:8px } ";
  200. html += myVariable;
  201. html +="media (min-width:800px) and (orientation:landscape){ #content{ display:flex; flex-wrap:nowrap; flex-direction: column; align-items: center; justify-content: center; } figure img{ display:block; max-width:100%; max-height:calc(100vh - 40px); width:auto; height:auto } figure{ padding:0; margin:0; -webkit-margin-before:0; margin-block-start:0; -webkit-margin-after:0; margin-block-end:0; -webkit-margin-start:0; margin-inline-start:0; -webkit-margin-end:0; margin-inline-end:0 } } section#buttons{ display:flex; flex-wrap:nowrap; justify-content:space-between } #nav-toggle{ cursor:pointer; display:block } #nav-toggle-cb{ outline:0; opacity:0; width:0; height:0 } #nav-toggle-cb:checked+#menu{ display:flex } .section_content{ display:flex; flex-direction:column; flex-wrap:nowrap; justify-content:space-between; } .stats{ display:flex; flex-wrap:nowrap; justify-content:space-between; font-size:14px; padding:5px 0px; } .input-group{ display:flex; flex-wrap:nowrap; line-height:22px; margin:5px 0 } .input-group>label{ display:inline-block; padding-right:10px; min-width:47% } .input-group input,.input-group select{ flex-grow:1 } .range-max,.range-min{ display:inline-block; padding:0 5px } button{ display:block; margin:5px; padding:0 12px; border:0; line-height:28px; cursor:pointer; color:#fff; background:#ff3034; border-radius:5px; font-size:16px; outline:0 } button:hover{ background:#ff494d } button:active{ background:#f21c21 } button.disabled{ cursor:default; background:#a0a0a0 } input[type=range]{ -webkit-appearance:none; width:100%; height:22px; background:#363636; cursor:pointer; margin:0 } input[type=range]:focus{ outline:0 } input[type=range]::-webkit-slider-runnable-track{ width:100%; height:2px; cursor:pointer; background:#efefef; border-radius:0; border:0 solid #efefef } input[type=range]::-webkit-slider-thumb{ border:1px solid rgba(0,0,30,0); height:22px; width:22px; border-radius:50px; background:#ff3034; cursor:pointer; -webkit-appearance:none; margin-top:-11.5px } input[type=range]:focus::-webkit-slider-runnable-track{ background:#efefef } input[type=range]::-moz-range-track{ width:100%; height:2px; cursor:pointer; background:#efefef; border-radius:0; border:0 solid #efefef } input[type=range]::-moz-range-thumb{ border:1px solid rgba(0,0,30,0); height:22px; width:22px; border-radius:50px; background:#ff3034; cursor:pointer } input[type=range]::-ms-track{ width:100%; height:2px; cursor:pointer; background:0 0; border-color:transparent; color:transparent } input[type=range]::-ms-fill-lower{ background:#efefef; border:0 solid #efefef; border-radius:0 } input[type=range]::-ms-fill-upper{ background:#efefef; border:0 solid #efefef; border-radius:0 } input[type=range]::-ms-thumb{ border:1px solid rgba(0,0,30,0); height:22px; width:22px; border-radius:50px; background:#ff3034; cursor:pointer; height:2px } input[type=range]:focus::-ms-fill-lower{ background:#efefef } input[type=range]:focus::-ms-fill-upper{ background:#363636 } .switch{ display:block; position:relative; line-height:22px; font-size:16px; height:22px } .switch input{ outline:0; opacity:0; width:0; height:0 } .slider{ width:50px; height:22px; border-radius:22px; cursor:pointer; background-color:grey } .slider,.slider:before{ display:inline-block; transition:.4s } .slider:before{ position:relative; content:\"\"; border-radius:50%; height:16px; width:16px; left:4px; top:3px; background-color:#fff } input:checked+.slider{ background-color:#ff3034 } input:checked+.slider:before{ -webkit-transform:translateX(26px); transform:translateX(26px) } select{ border:1px solid #363636; font-size:14px; height:22px; outline:0; border-radius:5px } .image-container{ position:relative; width: 100%; min-width:80px; min-height:60px; height: auto; } .close{ position:absolute; right:5px; top:5px; background:#ff3034; width:16px; height:16px; border-radius:100px; color:#fff; text-align:center; line-height:18px; cursor:pointer } .hidden{ display:none } </style> </head> <body> <section class=\"main\"> <div id=\"content\" > <figure> <div id=\"stream-container\" class=\"image-container\"> <div class=\"close\" id=\"close-stream\">W</div> <img id=\"stream\" src=\"\"> </div> </figure> <div id=\"sidebar\"> <input type=\"checkbox\" id=\"nav-toggle-cb\" checked=\"checked\"> <nav id=\"menu\"> <div class=\"input-group\" id=\"flash-group\"> <label for=\"flash\">Flash</label> <div class=\"switch\"> <input id=\"flash\" type=\"checkbox\" class=\"default-action\"> <label class=\"slider\" for=\"flash\"></label> </div> </div> <br> <div class=\"section_content\"> <div class=\"stats\"> <text>Battery:</text> <text id=\"bat_percent\">100%</text> </div> <div class=\"stats\"> <text>WiFi:</text> <text id=\"wifi_signal\">100%</text> </div> <div class=\"stats\"> <text>Bat. Voltage:</text> <text id=\"bat_voltage\">0 V</text> </div> <div class=\"stats\"> <text>Bat. Current:</text> <text id=\"bat_current\">0 mA</text> </div> <div class=\"stats\"> <text>Solar Voltage:</text> <text id=\"sol_voltage\">0 V</text> </div> <div class=\"stats\"> <text>Solar Current:</text> <text id=\"sol_current\">0 mA</text> </div> <div class=\"stats\"> <text>Free Memory:</text> <text id=\"free_mem\">0 kB</text> </div> <div class=\"stats\"> <text>Active Time:</text> <text id=\"active_time\">0 s</text> </div> </div> </nav> </div> </div> </section> <script> ";
  202. html += JS;
  203. html +=" </script> </body> </html>";
  204.  
  205.   server.send(200, "text/html", html);  
  206. }
  207.  
  208.  
  209.  
  210.  
  211. void relayControl()
  212. {
  213.   if(server.arg("var") == "flash")
  214.   {
  215.     if(server.arg("val") == "1")
  216.     {
  217.       LED_control_active = true;
  218.       digitalWrite(4, HIGH);
  219.     }
  220.     else
  221.     {
  222.       LED_control_active = false;
  223.       digitalWrite(LED_BUILTIN, LOW);      
  224.     }
  225.   }
  226. }
  227.  
  228. void handleRestart()
  229. {
  230.   Serial.println("Managed restart");
  231.   delay(1000);
  232.   ESP.restart();  
  233. }
  234.  
  235.  
  236. void sendSettings() {
  237.   String ssid = "";
  238.   String pass = "";
  239.   String ip = "";
  240.   int ap = 0;
  241.   int sta = 0;
  242.   String mode = "AP";
  243.   if (server.hasArg("ssid"))
  244.   {
  245.     ssid = String(server.arg("ssid"));
  246.   }  
  247.   if (server.hasArg("pass"))
  248.   {
  249.     pass = String(server.arg("pass"));
  250.   }
  251.   if (server.hasArg("ip"))
  252.   {
  253.     json = String(server.arg("ip"));
  254.   }
  255.   if (server.hasArg("ap_box"))
  256.   {
  257.     ap = 1;
  258.   }
  259.   if (server.hasArg("sta_box"))
  260.   {
  261.     sta = 1;
  262.   }
  263.   if (ap == 1 && sta == 1) mode = "APSTA";
  264.   if (ap == 1 && sta == 0) mode = "AP";
  265.   if (ap == 0 && sta == 1) mode = "STA";
  266.   String memString = mode+","+ssid+","+pass+","+ip;    
  267.   rebooting = true;
  268.   handleRoot();
  269.   writeEEPROM(0, memString);
  270.   EEPROM.commit();
  271.   delay(4000);
  272.   ESP.restart();
  273. }
  274.  
  275. void parseMemory(String memString)
  276. {
  277.   if (memString.length() > 0)
  278.   {
  279.     StringSplitter *splitter = new StringSplitter(memString, ',', 4);  
  280.     memMODE = splitter->getItemAtIndex(0);
  281.     memSSID = splitter->getItemAtIndex(1);
  282.     memPASS = splitter->getItemAtIndex(2);
  283.     memIP = splitter->getItemAtIndex(3);
  284.   }
  285. }
  286.  
  287. String parseMAC(String macString)
  288. {
  289.   if (macString.length() > 0)
  290.   {
  291.     StringSplitter *splitter = new StringSplitter(macString, ':', 4);  
  292.     String S0 = splitter->getItemAtIndex(0);
  293.     String S1 = splitter->getItemAtIndex(1);
  294.     String S2 = splitter->getItemAtIndex(2);
  295.     String S3 = splitter->getItemAtIndex(3);
  296.     return S0+S1+S2;
  297.   }
  298. }
  299.  
  300.  
  301.  
  302.  
  303. void handleSettings()
  304. {
  305.  
  306.  
  307.     sleep_cooldown = sleep_cooldown_base;
  308.     client_connected = true;
  309.     if (sleep_status == true)
  310.     {
  311.       wakeModemSleep();
  312.     }
  313.  
  314.   String JS = "setInterval(function() {\n\tvar baseHost = document.location.origin;\n\tfetch(`${baseHost + \'/upd\'}`);\n\n\n}, 5000);";
  315.  
  316.   String html ="<!DOCTYPE html> <html> <head> <title>ESP8266 Multimeter Settings</title> <meta charset=\"UTF-8\" name=\"viewport\" content=\"width=device-width, initial-scale=1\"> </head> <body> <h1>ESP8266 Multimeter Settings</h1> <form action=\"/sendSettings\"> <label for=\"ssid\">Network name: </label> <input type=\"text\" id=\"ssid\" name=\"ssid\" value=\"";
  317.   html += memSSID;
  318.   html +="\"><br><br> <label for=\"pass\">Network password: </label> <input type=\"text\" id=\"pass\" name=\"pass\" value=\"";
  319.   html += memPASS;
  320.   html +="\"><br><br> <label for=\"ip\">Desired Static IP: </label> <input type=\"text\" id=\"ip\" name=\"ip\" value=\"";
  321.   html += memIP;
  322.   html +="\"><br><br> <input type=\"checkbox\" id=\"ap_box\" name=\"ap_box\" value=\"true\" ";
  323.   if (WiFi_mode == "AP" || WiFi_mode == "APSTA") html += " checked";
  324.   html +="> <label for=\"ap_box\"> Access Point Mode</label><br><br> <input type=\"checkbox\" id=\"sta_box\" name=\"sta_box\" value=\"true\" ";
  325.   if (WiFi_mode == "STA" || WiFi_mode == "APSTA") html += " checked";
  326.   html +="> <label for=\"sta_box\"> Station Mode</label><br><br>";
  327.   if (!rebooting) html += " <input type=\"submit\" value=\"Submit and Reboot\"> ";
  328.   html +="<p id=\"info\">";
  329.   html += msg;
  330.   html +="</p> </form> <script> ";
  331.   html += JS;
  332.   html +=" </script> </body> </html>";
  333.   server.send(200, "text/html", html);
  334.  
  335. }
  336.  
  337.  
  338. // ===== rtos task handles =========================
  339. // Streaming is implemented with 3 tasks:
  340. TaskHandle_t tMjpeg;   // handles client connections to the webserver
  341. TaskHandle_t tCam;     // handles getting picture frames from the camera and storing them locally
  342.  
  343. uint8_t       noActiveClients;       // number of active clients
  344.  
  345. // frameSync semaphore is used to prevent streaming buffer as it is replaced with the next frame
  346. SemaphoreHandle_t frameSync = NULL;
  347.  
  348. // We will try to achieve 24 FPS frame rate
  349. const int FPS = 24;
  350.  
  351. // We will handle web client requests every 100 ms (10 Hz)
  352. const int WSINTERVAL = 100;
  353.  
  354.  
  355. // ======== Server Connection Handler Task ==========================
  356. void mjpegCB(void* pvParameters) {
  357.   TickType_t xLastWakeTime;
  358.   const TickType_t xFrequency = pdMS_TO_TICKS(WSINTERVAL);
  359.  
  360.   // Creating frame synchronization semaphore and initializing it
  361.   frameSync = xSemaphoreCreateBinary();
  362.   xSemaphoreGive( frameSync );
  363.  
  364.   //=== setup section  ==================
  365.  
  366.   //  Creating RTOS task for grabbing frames from the camera
  367.   xTaskCreatePinnedToCore(
  368.     camCB,        // callback
  369.     "cam",        // name
  370.     4 * 1024,       // stacj size
  371.     NULL,         // parameters
  372.     2,            // priority
  373.     &tCam,        // RTOS task handle
  374.     PRO_CPU);     // core
  375.  
  376.   //  Registering webserver handling routines
  377.   server.on("/stream", HTTP_GET, handleJPGSstream);
  378.   server.on("/jpg", HTTP_GET, handleJPG);
  379.   server.on("/upd", HTTP_GET, handleStatus);
  380.   server.on("/control", HTTP_GET, relayControl);
  381.   server.on("/restart", HTTP_GET, handleRestart);
  382.   server.on("/settings", HTTP_GET, handleSettings);
  383.   server.on("/sendSettings", HTTP_GET, sendSettings);
  384.  
  385.   server.on("/", HTTP_GET, handleRoot);
  386.   server.onNotFound(handleNotFound);
  387.  
  388.   //  Starting webserver
  389.   server.begin();
  390.  
  391.   noActiveClients = 0;
  392.  
  393.   Serial.printf("\nmjpegCB: free heap (start)  : %d\n", ESP.getFreeHeap());
  394.   //=== loop() section  ===================
  395.   xLastWakeTime = xTaskGetTickCount();
  396.   for (;;) {
  397.     server.handleClient();
  398.  
  399.     //  After every server client handling request, we let other tasks run and then pause
  400.     taskYIELD();
  401.     vTaskDelayUntil(&xLastWakeTime, xFrequency);
  402.   }
  403. }
  404.  
  405.  
  406. // Current frame information
  407. volatile uint32_t frameNumber;
  408. volatile size_t   camSize;    // size of the current frame, byte
  409. volatile char*    camBuf;      // pointer to the current frame
  410.  
  411.  
  412. // ==== RTOS task to grab frames from the camera =========================
  413. void camCB(void* pvParameters) {
  414.  
  415.   TickType_t xLastWakeTime;
  416.  
  417.   //  A running interval associated with currently desired frame rate
  418.   const TickType_t xFrequency = pdMS_TO_TICKS(1000 / FPS);
  419.  
  420.   //  Pointers to the 2 frames, their respective sizes and index of the current frame
  421.   char* fbs[2] = { NULL, NULL };
  422.   size_t fSize[2] = { 0, 0 };
  423.   int ifb = 0;
  424.   frameNumber = 0;
  425.  
  426.   //=== loop() section  ===================
  427.   xLastWakeTime = xTaskGetTickCount();
  428.  
  429.  
  430.    
  431.   for (;;) {
  432.  
  433.     //  Grab a frame from the camera and query its size
  434.     camera_fb_t* fb = NULL;
  435.  
  436.     fb = esp_camera_fb_get();
  437.     size_t s = fb->len;
  438.  
  439.     //  If frame size is more that we have previously allocated - request  125% of the current frame space
  440.     if (s > fSize[ifb]) {
  441.       fSize[ifb] = s + s;
  442.       fbs[ifb] = allocateMemory(fbs[ifb], fSize[ifb]);
  443.     }
  444.  
  445.     //  Copy current frame into local buffer
  446.     char* b = (char *)fb->buf;
  447.     memcpy(fbs[ifb], b, s);
  448.     esp_camera_fb_return(fb);
  449.    
  450.    
  451.     //  Let other tasks run and wait until the end of the current frame rate interval (if any time left)
  452.     taskYIELD();
  453.     vTaskDelayUntil(&xLastWakeTime, xFrequency);
  454.  
  455.     //  Only switch frames around if no frame is currently being streamed to a client
  456.     //  Wait on a semaphore until client operation completes
  457.     //    xSemaphoreTake( frameSync, portMAX_DELAY );
  458.  
  459.     //  Do not allow frame copying while switching the current frame
  460.     xSemaphoreTake( frameSync, xFrequency );
  461.     camBuf = fbs[ifb];
  462.     camSize = s;
  463.     ifb++;
  464.     ifb &= 1;  // this should produce 1, 0, 1, 0, 1 ... sequence
  465.     frameNumber++;
  466.     //  Let anyone waiting for a frame know that the frame is ready
  467.     xSemaphoreGive( frameSync );
  468.  
  469.     //  Immediately let other (streaming) tasks run
  470.     taskYIELD();
  471.  
  472.     //  If streaming task has suspended itself (no active clients to stream to)
  473.     //  there is no need to grab frames from the camera. We can save some juice
  474.     //  by suspedning the tasks
  475.     if ( noActiveClients == 0 ) {
  476.       Serial.printf("mjpegCB: free heap           : %d\n", ESP.getFreeHeap());
  477.       Serial.printf("mjpegCB: min free heap)      : %d\n", ESP.getMinFreeHeap());
  478.       Serial.printf("mjpegCB: max alloc free heap : %d\n", ESP.getMaxAllocHeap());
  479.       Serial.printf("mjpegCB: tCam stack wtrmark  : %d\n", uxTaskGetStackHighWaterMark(tCam));
  480.       Serial.flush();
  481.       vTaskSuspend(NULL);  // passing NULL means "suspend yourself"
  482.     }
  483.   }
  484. }
  485.  
  486.  
  487. // ==== Memory allocator that takes advantage of PSRAM if present =======================
  488. char* allocateMemory(char* aPtr, size_t aSize) {
  489.  
  490.   //  Since current buffer is too smal, free it
  491.   if (aPtr != NULL) free(aPtr);
  492.  
  493.   char* ptr = NULL;
  494.   ptr = (char*) ps_malloc(aSize);
  495.  
  496.   // If the memory pointer is NULL, we were not able to allocate any memory, and that is a terminal condition.
  497.   if (ptr == NULL) {
  498.     Serial.println("Out of memory!");
  499.     delay(5000);
  500.     ESP.restart();
  501.   }
  502.   return ptr;
  503. }
  504.  
  505.  
  506. // ==== STREAMING ======================================================
  507. const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
  508.                       "Access-Control-Allow-Origin: *\r\n" \
  509.                       "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
  510. const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n";
  511. const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
  512. const int hdrLen = strlen(HEADER);
  513. const int bdrLen = strlen(BOUNDARY);
  514. const int cntLen = strlen(CTNTTYPE);
  515.  
  516.  
  517. struct streamInfo {
  518.   uint32_t        frame;
  519.   WiFiClient      client;
  520.   TaskHandle_t    task;
  521.   char*           buffer;
  522.   size_t          len;
  523. };
  524.  
  525. // ==== Handle connection request from clients ===============================
  526. void handleJPGSstream(void)
  527. {
  528.   if ( noActiveClients >= MAX_CLIENTS ) return;
  529.   Serial.printf("handleJPGSstream start: free heap  : %d\n", ESP.getFreeHeap());
  530.  
  531.   streamInfo* info = new streamInfo;
  532.  
  533.   info->frame = frameNumber - 1;
  534.   info->client = server.client();
  535.   info->buffer = NULL;
  536.   info->len = 0;
  537.  
  538.   //  Creating task to push the stream to all connected clients
  539.   int rc = xTaskCreatePinnedToCore(
  540.              streamCB,
  541.              "strmCB",
  542.              3 * 1024,
  543.              (void*) info,
  544.              2,
  545.              &info->task,
  546.              APP_CPU);
  547.   if ( rc != pdPASS ) {
  548.     Serial.printf("handleJPGSstream: error creating RTOS task. rc = %d\n", rc);
  549.     Serial.printf("handleJPGSstream: free heap  : %d\n", ESP.getFreeHeap());
  550.     //    Serial.printf("stk high wm: %d\n", uxTaskGetStackHighWaterMark(tSend));
  551.     delete info;
  552.   }
  553.  
  554.   noActiveClients++;
  555.  
  556.   // Wake up streaming tasks, if they were previously suspended:
  557.   if ( eTaskGetState( tCam ) == eSuspended ) vTaskResume( tCam );
  558. }
  559.  
  560.  
  561. // ==== Actually stream content to all connected clients ========================
  562. void streamCB(void * pvParameters) {
  563.   char buf[16];
  564.   TickType_t xLastWakeTime;
  565.   TickType_t xFrequency;
  566.  
  567.   streamInfo* info = (streamInfo*) pvParameters;
  568.  
  569.   if ( info == NULL ) {
  570.     Serial.println("streamCB: a NULL pointer passed");
  571.   }
  572.   //  Immediately send this client a header
  573.   info->client.write(HEADER, hdrLen);
  574.   info->client.write(BOUNDARY, bdrLen);
  575.   taskYIELD();
  576.  
  577.   xLastWakeTime = xTaskGetTickCount();
  578.   xFrequency = pdMS_TO_TICKS(1000 / FPS);
  579.  
  580.   for (;;) {
  581.     //  Only bother to send anything if there is someone watching
  582.     if ( info->client.connected() ) {
  583.       sleep_cooldown = sleep_cooldown_base;
  584.       client_connected = true;
  585.       if (sleep_status == true)
  586.       {
  587.         wakeModemSleep();
  588.       }
  589.  
  590.       if ( info->frame != frameNumber) {
  591.         xSemaphoreTake( frameSync, portMAX_DELAY );
  592.         if ( info->buffer == NULL ) {
  593.           info->buffer = allocateMemory (info->buffer, camSize);
  594.           info->len = camSize;
  595.         }
  596.         else {
  597.           if ( camSize > info->len ) {
  598.             info->buffer = allocateMemory (info->buffer, camSize);
  599.             info->len = camSize;
  600.           }
  601.         }
  602.         memcpy(info->buffer, (const void*) camBuf, info->len);
  603.         xSemaphoreGive( frameSync );
  604.         taskYIELD();
  605.  
  606.         info->frame = frameNumber;
  607.         info->client.write(CTNTTYPE, cntLen);
  608.         sprintf(buf, "%d\r\n\r\n", info->len);
  609.         info->client.write(buf, strlen(buf));
  610.         info->client.write((char*) info->buffer, (size_t)info->len);
  611.         info->client.write(BOUNDARY, bdrLen);
  612.         info->client.flush();
  613.       }
  614.     }
  615.     else {
  616.       //  client disconnected - clean up.
  617.       noActiveClients--;
  618.       Serial.printf("streamCB: Stream Task stack wtrmark  : %d\n", uxTaskGetStackHighWaterMark(info->task));
  619.       Serial.flush();
  620.       info->client.flush();
  621.       info->client.stop();
  622.       if ( info->buffer ) {
  623.         free( info->buffer );
  624.         info->buffer = NULL;
  625.       }
  626.       delete info;
  627.       info = NULL;
  628.       vTaskDelete(NULL);
  629.     }
  630.     //  Let other tasks run after serving every client
  631.     taskYIELD();
  632.     vTaskDelayUntil(&xLastWakeTime, xFrequency);
  633.   }
  634. }
  635.  
  636.  
  637.  
  638. const char JHEADER[] = "HTTP/1.1 200 OK\r\n" \
  639.                        "Content-disposition: inline; filename=capture.jpg\r\n" \
  640.                        "Content-type: image/jpeg\r\n\r\n";
  641. const int jhdLen = strlen(JHEADER);
  642.  
  643. // ==== Serve up one JPEG frame =============================================
  644. void handleJPG(void)
  645. {
  646.   WiFiClient client = server.client();
  647.  
  648.   if (!client.connected()) return;
  649.   camera_fb_t* fb = esp_camera_fb_get();
  650.   client.write(JHEADER, jhdLen);
  651.   client.write((char*)fb->buf, fb->len);
  652.   esp_camera_fb_return(fb);
  653. }
  654.  
  655.  
  656. // ==== Handle invalid URL requests ============================================
  657. void handleNotFound()
  658. {
  659.   String message = "Server is running!\n\n";
  660.   message += "URI: ";
  661.   message += server.uri();
  662.   message += "\nMethod: ";
  663.   message += (server.method() == HTTP_GET) ? "GET" : "POST";
  664.   message += "\nArguments: ";
  665.   message += server.args();
  666.   message += "\n";
  667.   server.send(200, "text / plain", message);
  668. }
  669.  
  670.  
  671.  
  672. // ==== SETUP method ==================================================================
  673. void setup()
  674. {
  675.  
  676.   // Setup Serial connection:
  677.   EEPROM.begin(512);
  678.   Serial.begin(115200);
  679.   delay(1000); // wait for a second to let Serial connect
  680.   Serial.printf("setup: free heap  : %d\n", ESP.getFreeHeap());
  681.  
  682.   if (disable_brownout_detector) WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  683.  
  684.   #define CUSTOM_PIN 33
  685.   pinMode(CUSTOM_PIN, OUTPUT);
  686.   digitalWrite(CUSTOM_PIN, HIGH);
  687.   delay(100);
  688.  
  689.   I2CSensors.begin(I2C_SDA, I2C_SCL, 100000);
  690.   ina219_bat.begin(&I2CSensors);
  691.   ina219_sol.begin(&I2CSensors);
  692.  
  693.   static camera_config_t camera_config = {
  694.     .pin_pwdn       = PWDN_GPIO_NUM,
  695.     .pin_reset      = RESET_GPIO_NUM,
  696.     .pin_xclk       = XCLK_GPIO_NUM,
  697.     .pin_sscb_sda   = SIOD_GPIO_NUM,
  698.     .pin_sscb_scl   = SIOC_GPIO_NUM,
  699.     .pin_d7         = Y9_GPIO_NUM,
  700.     .pin_d6         = Y8_GPIO_NUM,
  701.     .pin_d5         = Y7_GPIO_NUM,
  702.     .pin_d4         = Y6_GPIO_NUM,
  703.     .pin_d3         = Y5_GPIO_NUM,
  704.     .pin_d2         = Y4_GPIO_NUM,
  705.     .pin_d1         = Y3_GPIO_NUM,
  706.     .pin_d0         = Y2_GPIO_NUM,
  707.     .pin_vsync      = VSYNC_GPIO_NUM,
  708.     .pin_href       = HREF_GPIO_NUM,
  709.     .pin_pclk       = PCLK_GPIO_NUM,
  710.  
  711.     .xclk_freq_hz   = 20000000,
  712.     .ledc_timer     = LEDC_TIMER_0,
  713.     .ledc_channel   = LEDC_CHANNEL_0,
  714.     .pixel_format   = PIXFORMAT_JPEG,
  715.     /*
  716.         FRAMESIZE_96X96,    // 96x96
  717.         FRAMESIZE_QQVGA,    // 160x120
  718.         FRAMESIZE_QCIF,     // 176x144
  719.         FRAMESIZE_HQVGA,    // 240x176
  720.         FRAMESIZE_240X240,  // 240x240
  721.         FRAMESIZE_QVGA,     // 320x240
  722.         FRAMESIZE_CIF,      // 400x296
  723.         FRAMESIZE_HVGA,     // 480x320
  724.         FRAMESIZE_VGA,      // 640x480
  725.         FRAMESIZE_SVGA,     // 800x600
  726.         FRAMESIZE_XGA,      // 1024x768
  727.         FRAMESIZE_HD,       // 1280x720
  728.         FRAMESIZE_SXGA,     // 1280x1024
  729.         FRAMESIZE_UXGA,     // 1600x1200
  730.     */
  731.  
  732.         .frame_size     = FRAMESIZE_XGA,
  733.  
  734.     .jpeg_quality   = 10,
  735.     .fb_count       = 1
  736.   };
  737.  
  738.  
  739.   if (esp_camera_init(&camera_config) != ESP_OK) {
  740.     Serial.println("Error initializing the camera");
  741.     delay(1000);
  742.     ESP.restart();
  743.   }
  744.  
  745.   sensor_t* s = esp_camera_sensor_get();
  746.   s->set_vflip(s, 0);
  747.   s->set_hmirror(s, 0);
  748.  
  749.  
  750.  
  751.   pinMode(LED_BUILTIN, OUTPUT);
  752.  
  753.  
  754.  
  755.   btStop();
  756.   Serial.println("");
  757.   Serial.println("Bluetooth disabled...");
  758.  
  759.   //  Configure and connect to WiFi
  760.   String memoryStr = readEEPROM(0);
  761.   parseMemory(memoryStr);
  762.   Serial.println("memMODE = " + memMODE);
  763.   if (memMODE != "AP" && memMODE != "STA" && memMODE != "APSTA") memMODE = "";
  764.   if (memMODE == "AP" || memMODE.length() <= 0)
  765.   {
  766.  
  767.     if (memMODE.length() <= 0) Serial.println("Reading Memory - MEMORY IS EMPTY, Setting to ACCESS POINT mode");
  768.     else Serial.println("Reading Memory - Memory found, Setting to ACCESS POINT mode");
  769.  
  770.     IPAddress localIp(192,168,1,1);
  771.     IPAddress gateway(192,168,1,1);
  772.     IPAddress subnet(255,255,255,0);    
  773.     WiFi.softAPConfig(localIp, gateway, subnet);
  774.     String macString = parseMAC(WiFi.macAddress());
  775.     macString = "ESP_MM_"+macString;
  776.    
  777.     macString.toCharArray(ssid_char, macString.length()+1);
  778.     WiFi.softAP(ssid_char);
  779.     Serial.println("WiFi AP set! " + macString);
  780.     WiFi_mode = "AP";
  781.  
  782.      
  783.   }
  784.   else if (memMODE == "APSTA")
  785.   {
  786.  
  787.     Serial.println("Reading Memory - Memory found, Setting to STATION + ACCESS POINT mode");
  788.    
  789.     IPAddress localIp(192,168,1,1);
  790.     IPAddress gateway(192,168,1,1);
  791.     IPAddress subnet(255,255,255,0);    
  792.     delay(500);
  793.     WiFi.softAPConfig(localIp, gateway, subnet);
  794.     String macString = parseMAC(WiFi.macAddress());
  795.     macString = "ESP_MM_"+macString;
  796.    
  797.     macString.toCharArray(ssid_char, macString.length()+1);
  798.     WiFi.softAP(ssid_char);
  799.     Serial.println("WiFi AP set! " + macString);
  800.     WiFi_mode = "APSTA";
  801.     connectWiFi();
  802.    
  803.   }
  804.   else
  805.   {
  806.     Serial.println("Reading Memory - Memory found, Setting to STATION mode");  
  807.     WiFi_mode = "STA";
  808.     WiFi.mode(WIFI_STA);
  809.     connectWiFi();    
  810.   }
  811.  
  812.  
  813.  
  814.   // Start mainstreaming RTOS task
  815.   xTaskCreatePinnedToCore(
  816.     mjpegCB,
  817.     "mjpeg",
  818.     2 * 1024,
  819.     NULL,
  820.     2,
  821.     &tMjpeg,
  822.     APP_CPU);
  823.  
  824.   Serial.println("Booting Sensors");
  825.   digitalWrite(CUSTOM_PIN, LOW);
  826.   delay(100);
  827.   digitalWrite(CUSTOM_PIN, HIGH);
  828.   //setModemSleep();
  829.   sleep_cooldown = 4;
  830.  
  831.   Serial.printf("setup complete: free heap  : %d\n", ESP.getFreeHeap());
  832.  
  833. }
  834.  
  835. void connectWiFi()
  836. {
  837.   IPAddress ip;
  838.   delay(100);
  839.   //WiFi.mode(WIFI_STA);
  840.  
  841.   Serial.println("Beginning connection");
  842.   blinkLED();
  843.   delay(100);
  844.   WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_STA_CONNECTED);
  845.   WiFi.onEvent(WiFiGotIP, SYSTEM_EVENT_STA_GOT_IP);
  846.   WiFi.onEvent(WiFiStationDisconnected, SYSTEM_EVENT_STA_DISCONNECTED);
  847.   WiFi.setTxPower(WIFI_POWER_19_5dBm);
  848.   memSSID.toCharArray(ssid_char, memSSID.length()+1);
  849.   memPASS.toCharArray(pass_char, memPASS.length()+1);
  850.   WiFi.begin(ssid_char, pass_char);
  851.   Serial.print("Connecting to WiFi: "+String(ssid_char));
  852.   while (WiFi.status() != WL_CONNECTED)
  853.   {
  854.     delay(500);
  855.     Serial.print(".");
  856.     dis_count++;
  857.     if(dis_count>30)
  858.     {
  859.       Serial.println();
  860.       Serial.println("Failed to connect - Restarting ESP");
  861.       delay(1000);
  862.       ESP.restart();
  863.     }
  864.   }
  865.   dis_count = 0;
  866.   ip = WiFi.localIP();
  867.   Serial.println(F("WiFi connected"));
  868.   Serial.println("");
  869.   Serial.print("Stream Link: http://");
  870.   Serial.print(ip);
  871.   Serial.println("/");  
  872. }
  873.  
  874.  
  875.  
  876. // ==== LOOP method ==================================================================
  877. void loop() {
  878.   // this seems to be necessary to let IDLE task run and do GC
  879.   vTaskDelay(1000);
  880.   if (sleep_status)
  881.   {
  882.     if (active_sec >= 3600)
  883.     {
  884.       Serial.println("Hourly maintanace reboot - Restarting ESP");
  885.       delay(1000);
  886.       ESP.restart();        
  887.     }
  888.    
  889.     esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  890.     Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");  
  891.     Serial.println("Going to sleep now");
  892.     esp_camera_deinit();
  893.     delay(100);
  894.     digitalWrite(LED_BUILTIN, LOW);
  895.     rtc_gpio_hold_en(GPIO_NUM_4);
  896.     gpio_deep_sleep_hold_en();    
  897.     esp_light_sleep_start();
  898.     Serial.println("ESP32 awake");
  899.     rtc_gpio_hold_dis(GPIO_NUM_4);
  900.     gpio_deep_sleep_hold_dis();  
  901.     static camera_config_t config;
  902.     config.ledc_channel = LEDC_CHANNEL_0;
  903.     config.ledc_timer = LEDC_TIMER_0;
  904.     config.pin_d0 = Y2_GPIO_NUM;
  905.     config.pin_d1 = Y3_GPIO_NUM;
  906.     config.pin_d2 = Y4_GPIO_NUM;
  907.     config.pin_d3 = Y5_GPIO_NUM;
  908.     config.pin_d4 = Y6_GPIO_NUM;
  909.     config.pin_d5 = Y7_GPIO_NUM;
  910.     config.pin_d6 = Y8_GPIO_NUM;
  911.     config.pin_d7 = Y9_GPIO_NUM;
  912.     config.pin_xclk = XCLK_GPIO_NUM;
  913.     config.pin_pclk = PCLK_GPIO_NUM;
  914.     config.pin_vsync = VSYNC_GPIO_NUM;
  915.     config.pin_href = HREF_GPIO_NUM;
  916.     config.pin_sscb_sda = SIOD_GPIO_NUM;
  917.     config.pin_sscb_scl = SIOC_GPIO_NUM;
  918.     config.pin_pwdn = PWDN_GPIO_NUM;
  919.     config.pin_reset = RESET_GPIO_NUM;
  920.     config.xclk_freq_hz = 20000000;
  921.     config.pixel_format = PIXFORMAT_JPEG;
  922.     config.frame_size = FRAMESIZE_XGA;
  923.     config.jpeg_quality = 10;
  924.     config.fb_count = 1;
  925.     esp_err_t err = esp_camera_init(&config);
  926.     if (err != ESP_OK) {
  927.       Serial.println("Error initializing the camera");
  928.       delay(1000);
  929.       ESP.restart();
  930.     }
  931.     sensor_t * s = esp_camera_sensor_get();
  932.     s->set_vflip(s, 0);//flip it back
  933.     s->set_hmirror(s, 0);
  934.     s->set_framesize(s, FRAMESIZE_XGA);
  935.     active_sec += TIME_TO_SLEEP;  
  936.     WiFi.setTxPower(WIFI_POWER_19_5dBm);
  937.     wakeModemSleep();
  938.     sleep_cooldown = 4;
  939.     //delay(2000);
  940.   }
  941.   else
  942.   {
  943.     //delay(1000);
  944.     active_sec += 1;
  945.   }
  946.  
  947.  
  948.  
  949.  
  950.   updateStatus();
  951.   //sendStatusToDomoticz();
  952.  
  953.   if((memMODE == "APSTA" || memMODE == "STA") && WiFi.status() != WL_CONNECTED)
  954.   {
  955.     if (sleep_status)
  956.     {
  957.       wakeModemSleep();
  958.     }
  959.     if (client_connected) sleep_cooldown = sleep_cooldown_base;
  960.     else sleep_cooldown = 5;
  961.     if (dis_count == 0)
  962.     {
  963.       Serial.println("Disconnected from WiFi (!)");
  964.       WiFi.mode(WIFI_STA);
  965.       WiFi.disconnect(true);  
  966.       delay(100);
  967.       WiFi.begin(ssid_char, pass_char);
  968.       Serial.println("Trying to reestablish connection");
  969.       while (WiFi.status() != WL_CONNECTED) {
  970.         delay(500);
  971.         Serial.print(".");
  972.         dis_count++;
  973.         if(dis_count>20)
  974.         {
  975.           Serial.println();
  976.           Serial.println("(FAILURE) Connection timeout - Restarting ESP");
  977.           delay(1000);
  978.           ESP.restart();
  979.         }
  980.       }      
  981.     }
  982.   }
  983.  
  984.   if (sleep_status == false && WiFi_mode == "STA")
  985.   {
  986.     sleep_cooldown--;
  987.     Serial.println("Sleep timeout in | " + String(sleep_cooldown));
  988.     if (sleep_cooldown <= 0)
  989.     {
  990.       sleep_cooldown = sleep_cooldown_base;
  991.       setModemSleep();
  992.     }
  993.   }
  994.   if(WiFi.status() == WL_CONNECTED)
  995.   {
  996.     if (dis_count > 0)
  997.     {
  998.       dis_count = 0;
  999.       Serial.println("(SUCCESS) WiFi connection reestablished");
  1000.       //digitalWrite(indicatorLED,HIGH);
  1001.     }
  1002.   }  
  1003. }
  1004.  
  1005.  
  1006.  
  1007.  
  1008.  
  1009.  
  1010. void blinkLED() {
  1011.   digitalWrite(LED_BUILTIN, HIGH);
  1012.   delay(50);
  1013.   digitalWrite(LED_BUILTIN, LOW);
  1014.   delay(50);
  1015.   digitalWrite(LED_BUILTIN, HIGH);
  1016.   delay(50);
  1017.   digitalWrite(LED_BUILTIN, LOW);
  1018. }
  1019.  
  1020.  
  1021.  
  1022. void setModemSleep() {
  1023.     if (client_connected) {  
  1024.       digitalWrite(LED_BUILTIN, HIGH);
  1025.       delay(300);
  1026.       digitalWrite(LED_BUILTIN, LOW);
  1027.       delay(200);
  1028.       digitalWrite(LED_BUILTIN, HIGH);
  1029.       delay(300);
  1030.       digitalWrite(LED_BUILTIN, LOW);
  1031.       delay(200);
  1032.       digitalWrite(LED_BUILTIN, HIGH);
  1033.       delay(300);
  1034.       digitalWrite(LED_BUILTIN, LOW);
  1035.       delay(200);
  1036.     }        
  1037.     sleep_status = true;
  1038.     client_connected = false;
  1039.     LED_control_active = false;
  1040. }
  1041.  
  1042. void wakeModemSleep() {
  1043.     //#define LED_BUILTIN 4
  1044.     //pinMode(LED_BUILTIN, OUTPUT);
  1045.  
  1046.     if (client_connected) {
  1047.       digitalWrite(LED_BUILTIN, HIGH);
  1048.       delay(2000);
  1049.       digitalWrite(LED_BUILTIN, LOW);
  1050.     }
  1051.     Serial.println("");
  1052.     Serial.println("ESP32 active");
  1053.     sleep_status = false;
  1054. }
  1055.  
  1056.  
  1057.  
  1058.  
  1059.  
  1060.  
  1061.  
  1062.  
  1063. String getStatus()
  1064. {
  1065.   String msg = "{\"batteryPercent\":" + String(batteryPercent) + ", \"batteryVoltage\":"+ String(bat_busvoltage) + ", \"batteryCurrent\":" + String(bat_current_mA) + ", \"wifiSignal\":" + String(wifi_signal) +", ";    
  1066.   msg += "\"SolarVoltage\":" + String(sol_busvoltage) + ", \"SolarCurrent\":" + String(sol_current_mA) + ", \"memory\":" + String(free_memory) + ", \"active_time\":" + String(active_sec) + "}";
  1067.   return msg;
  1068. }
  1069.  
  1070. String updateStatus()
  1071. {
  1072.  
  1073.   free_memory = float(ESP.getFreeHeap())/1000;
  1074.  
  1075.   bat_shuntvoltage = readSensor(ina219_bat.getShuntVoltage_mV());
  1076.   bat_busvoltage = readSensor(ina219_bat.getBusVoltage_V());
  1077.   bat_current_mA = readSensor(ina219_bat.getCurrent_mA());
  1078.   bat_power_mW = readSensor(ina219_bat.getPower_mW());
  1079.   bat_loadvoltage = bat_busvoltage + (bat_shuntvoltage / 1000);
  1080.  
  1081.   batteryPercent = max(0, int(map(int(bat_busvoltage*100), int(5.0*100), int(8.4*100), 0, 100)));
  1082.  
  1083.   if (p_current == bat_current_mA) measurements++;
  1084.   else
  1085.   {
  1086.     measurements = 0;
  1087.     p_current = bat_current_mA;    
  1088.   }
  1089.   if (measurements >= 10)
  1090.   {
  1091.     Serial.println("INA219 Sensor malfunctioned. Resetting...");
  1092.     digitalWrite(CUSTOM_PIN, LOW);
  1093.     delay(2000);
  1094.     digitalWrite(CUSTOM_PIN, HIGH);
  1095.     measurements = 0;
  1096.   }
  1097.  
  1098.  
  1099.   long rssi = WiFi.RSSI();
  1100.   int val = map(rssi, -100, -50, 0, 100);
  1101.   if (val > 100) val = 100;
  1102.   wifi_signal = val;
  1103.  
  1104.   String msg = "{\"batteryPercent\":" + String(batteryPercent) + ", \"batteryVoltage\":"+ String(bat_busvoltage) + ", \"batteryCurrent\":" + String(bat_current_mA) + ", \"wifiSignal\":"+ String(wifi_signal) +", ";
  1105.  
  1106.  
  1107.  
  1108.  
  1109.   sol_shuntvoltage = readSensor(ina219_sol.getShuntVoltage_mV());
  1110.   sol_busvoltage = readSensor(ina219_sol.getBusVoltage_V());
  1111.   sol_current_mA = readSensor(ina219_sol.getCurrent_mA());
  1112.   sol_power_mW = readSensor(ina219_sol.getPower_mW());
  1113.   sol_loadvoltage = sol_busvoltage + (sol_shuntvoltage / 1000);
  1114.  
  1115.  
  1116.  
  1117.   msg += "\"SolarVoltage\":" + String(sol_busvoltage) + ", \"SolarCurrent\":" + String(sol_current_mA) + ", \"memory\":" + String(free_memory) + ", \"active_time\":" + String(active_sec) + "}";
  1118.  
  1119.  
  1120.   Serial.println(msg);
  1121. }
  1122.  
  1123.  
  1124. void sendStatusToDomoticz()
  1125. {
  1126.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=86&nvalue=0&svalue=" + String(bat_busvoltage));
  1127.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=90&nvalue=0&svalue=" + String(bat_current_mA));
  1128.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=87&nvalue=0&svalue=" + String(sol_busvoltage));
  1129.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=91&nvalue=0&svalue=" + String(sol_current_mA));
  1130.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=88&nvalue=0&svalue=" + String(free_memory));
  1131.   sendHttp("http://192.168.2.5:8080/json.htm?type=command&param=udevice&idx=89&nvalue=0&svalue=" + String(active_sec));
  1132. }
  1133.  
  1134. void sendHttp(String url)
  1135. {
  1136.   HTTPClient http;
  1137.   http.begin(url.c_str());
  1138.   int httpResponseCode = http.GET();
  1139.   http.end();  
  1140. }
  1141.  
  1142.  
  1143. float readSensor(float var)
  1144. {
  1145.   if (isnan(var))
  1146.   {
  1147.     return 0;
  1148.   }
  1149.   return var;
  1150. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement