środa, 27 maja 2020

[9] ESP32 - Arduino - Serwer HTTP zabezpieczony hasłem

W tym poście chciałbym opisać sposób przygotowania serwera HTTP pozwalającego na sterowanie przekaźnikami. Dodatkowo dostęp do strony będzie zabezpieczony hasłem.

Znalezione obrazy dla zapytania arduino esp32
[Źródło: http://paulobrien.co.nz/2017/03/16/esp32-programming-with-arduino-on-windows/]

Po zalogowaniu do strony uzyskamy dostęp do sterowania przekaźnikami. Po załadowaniu strony zostanie ustawiony stan przekaźników zgodny ze stanem aktualnym.

Strona sprawdzi się idealnie do sterowania wewnątrz domu lub przez publiczny adres IP. W tym drugim przypadku problemem może być bezpieczeństwo podanego rozwiązania.

W przypadku potrzeby sterowania z innej sieci niż ta do której został podłączony moduł ESP najlepiej wykorzystać zewnętrzne serwisy jak np IFTTT wraz z platformą Adafruit.

Program:


W programie została wykorzystana biblioteka ESPAsyncWebServer oraz AsyncTCP. Pozwoli to na postawienie serwera odpowiadającego na przesłane zapytania bez konieczności sprawdzania danych przesłanych od klienta w pętli loop.

Dosyć dokładny opis poszczególnych parametrów w funkcji można znaleźć na poniższych stronach:


Poniżej cały program:

  1. #include <WiFi.h>
  2. #include <AsyncTCP.h>
  3. #include <ESPAsyncWebServer.h>
  4. //----------------------------------------------------------------
  5. #define OUTPUT_1 2
  6. #define OUTPUT_2 3
  7. #define OUTPUT_3 4
  8. #define OUTPUT_4 5
  9.  
  10. #define BAUDRATE 115200
  11.  
  12. #define WIFI_SSID "Orange_Swiatlowod_1F4A";
  13. #define WIFI_PASSWORD "2MPCACZRWMQH"
  14. //---------------------------------------------------------------
  15. const char* ssid = WIFI_SSID;
  16. const char* password = WIFI_PASSWORD;
  17.  
  18. const char* http_username = "admin";
  19. const char* http_password = "admin";
  20.  
  21. const char* PAR_INPUT_1 = "state1";
  22. const char* PAR_INPUT_2 = "state2";
  23. const char* PAR_INPUT_3 = "state3";
  24. const char* PAR_INPUT_4 = "state4";
  25. //---------------------------------------------------------------
  26. AsyncWebServer server(80);
  27. //---------------------------------------------------------------
  28. const char index_html[] PROGMEM = R"rawliteral(
  29. <!DOCTYPE HTML><html>
  30. <head>
  31.  <meta charset="utf-8">
  32.  <title>Kontroler przekaźników</title>
  33.  <meta
  34.    name="viewport"
  35.    content="width=device-width, initial-scale=1">
  36.  <style>
  37.    html {
  38.      font-family: Verdana;
  39.      display: inline-block;
  40.      text-align: center;
  41.    }
  42.    h1 {
  43.      text-align: center;
  44.      text-transform: uppercase;
  45.      color: #002E8A;
  46.    }
  47.    h2 {
  48.      font-size: 48px;
  49.    }
  50.    body {
  51.      max-width: 600px;
  52.      margin:0px auto;
  53.      padding-bottom: 10px;
  54.    }
  55.    .button {
  56.        background-color: #4CAF50;
  57.        border: none;
  58.        color: white;
  59.        padding: 15px 32px;
  60.        text-align: center;
  61.        text-decoration: none;
  62.        display: inline-block;
  63.        font-size: 16px;
  64.        margin: 4px 2px;
  65.        cursor: pointer;
  66.    }
  67.    .switch {
  68.        position: relative;
  69.        display: inline-block;
  70.        width: 120px;
  71.        height: 68px
  72.     }
  73.     .switch input {
  74.      display: none
  75.     }
  76.    .slider {
  77.      position: absolute;
  78.      top: 0;
  79.      left: 0;
  80.      right: 0;
  81.      bottom: 0;
  82.      //background-color: #ccc;
  83.      background-color: #f54a47;
  84.      border-radius: 34px
  85.    }
  86.    .slider:before {
  87.      position: absolute;
  88.      content: "";
  89.      height: 52px;
  90.      width: 52px;
  91.      left: 8px;
  92.      bottom: 8px;
  93.      background-color: #fff;
  94.      -webkit-transition: .4s;
  95.      transition: .4s;
  96.      border-radius: 68px
  97.    }
  98.    input:checked+.slider {
  99.      background-color: #00fc22
  100.    }
  101.    input:checked+.slider:before {
  102.      -webkit-transform: translateX(52px);
  103.      -ms-transform: translateX(52px);
  104.      transform: translateX(52px)
  105.    }
  106.  </style>
  107. </head>
  108. <body>
  109.  <h2>ESP Sterownik przekaźników</h2>
  110.  <button class="button"  onclick="logoutButton()">Wyloguj</button>
  111.  <h1>korytarz</h1>
  112.  %BUTTEXT1%
  113.  <h1>sypialnia</h1>
  114.  %BUTTEXT2%
  115.  <h1>łazienka</h1>
  116.  %BUTTEXT3%
  117.  <h1>telewizor</h1>
  118.  %BUTTEXT4%
  119.  <script>
  120. function toggleCheckbox_1(element) {
  121.  var xhr = new XMLHttpRequest();
  122.  if(element.checked) { xhr.open("GET", "/update?state1=1", true); }
  123.  else                { xhr.open("GET", "/update?state1=0", true); }
  124.  xhr.send();
  125. }
  126. function toggleCheckbox_2(element) {
  127.  var xhr = new XMLHttpRequest();
  128.  if(element.checked) {  xhr.open("GET", "/update?state2=1", true);   }
  129.  else                { xhr.open("GET", "/update?state2=0", true);    }
  130.  xhr.send();
  131. }
  132. function toggleCheckbox_3(element) {
  133.  var xhr = new XMLHttpRequest();
  134.  if(element.checked) { xhr.open("GET", "/update?state3=1", true);  }
  135.  else                { xhr.open("GET", "/update?state3=0", true);  }
  136.  xhr.send();
  137. }
  138. function toggleCheckbox_4(element) {
  139.  var xhr = new XMLHttpRequest();
  140.  if(element.checked) { xhr.open("GET", "/update?state4=1", true); }
  141.  else                { xhr.open("GET", "/update?state4=0", true);  }
  142.  xhr.send();
  143. }
  144. function logoutButton() {
  145.  var xhr = new XMLHttpRequest();
  146.  xhr.open("GET", "/logout", true);
  147.  xhr.send();
  148.  setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
  149. }
  150. </script>
  151. </body>
  152. </html>
  153. )rawliteral";
  154. //---------------------------------------------------------------
  155. const char logout_html[] PROGMEM = R"rawliteral(
  156. <!DOCTYPE HTML><html>
  157. <head>
  158.  <meta charset="utf-8">
  159.  <meta name="viewport"
  160.  content="width=device-width,
  161.   initial-scale=1">
  162. </head>
  163. <body>
  164.  <p>Wylogowano. <a href="/">Powrót</a>.</p>
  165. </body>
  166. </html>
  167. )rawliteral";
  168. //---------------------------------------------------------------
  169. void setup(){
  170.   uint8_t connectionCounter = 0;
  171.   Serial.begin(BAUDRATE);
  172.   InitOutputPins();
  173.  
  174.   Serial.println("Program starts");
  175.  
  176.   WiFi.begin(ssid, password);
  177.   while (WiFi.status() != WL_CONNECTED) {
  178.     delay(1000);
  179.     Serial.println("Connecting to WiFi..");
  180.     connectionCounter++;
  181.   }
  182.  
  183.   Serial.println(WiFi.localIP());
  184.   EnableServer();
  185.   server.begin();
  186. }
  187.  
  188. void loop() { }
  189. //---------------------------------------------------------------
  190. void InitOutputPins(void) {
  191.   pinMode(OUTPUT_1, OUTPUT);
  192.   pinMode(OUTPUT_2, OUTPUT);
  193.   pinMode(OUTPUT_3, OUTPUT);
  194.   pinMode(OUTPUT_4, OUTPUT);
  195.  
  196.   digitalWrite(OUTPUT_1, LOW);
  197.   digitalWrite(OUTPUT_2, LOW);
  198.   digitalWrite(OUTPUT_3, LOW);
  199.   digitalWrite(OUTPUT_4, LOW);
  200. }
  201. //---------------------------------------------------------------
  202. void EnableServer(void)
  203. {
  204.   server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  205.   {
  206.     if(!request->authenticate(http_username, http_password))
  207.     {
  208.       return request->requestAuthentication();
  209.     }
  210.     request->send_P(200, "text/html", index_html, processor);
  211.   });
  212.    
  213.   server.on(
  214.     "/logout", HTTP_GET, [](AsyncWebServerRequest *request){
  215.     request->send(401);
  216.   });
  217.  
  218.   server.on(
  219.     "/logged-out", HTTP_GET, [](AsyncWebServerRequest *request){
  220.     request->send_P(200, "text/html", logout_html, processor);
  221.   });
  222.  
  223.   server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request)
  224.   {
  225.     if(!request->authenticate(http_username, http_password)) {
  226.       return request->requestAuthentication();
  227.     }
  228.    
  229.     String inputMessage;
  230.     String inputParam;
  231.    
  232.     if (request->hasParam(PAR_INPUT_1)) {
  233.       inputMessage = request->getParam(PAR_INPUT_1)->value();
  234.       digitalWrite(OUTPUT_1, inputMessage.toInt());
  235.     }
  236.     else if (request->hasParam(PAR_INPUT_2)) {
  237.       inputMessage = request->getParam(PAR_INPUT_2)->value();
  238.       digitalWrite(OUTPUT_2, inputMessage.toInt());
  239.     }
  240.     else if (request->hasParam(PAR_INPUT_3)) {
  241.       inputMessage = request->getParam(PAR_INPUT_3)->value();
  242.       digitalWrite(OUTPUT_3, inputMessage.toInt());
  243.     }
  244.     else if (request->hasParam(PAR_INPUT_4)) {
  245.       inputMessage = request->getParam(PAR_INPUT_4)->value();
  246.       digitalWrite(OUTPUT_4, inputMessage.toInt());
  247.     }
  248.     else { }
  249.     request->send(200, "text/plain", "OK");
  250.   });
  251. }
  252. //---------------------------------------------------------------
  253. String processor(const String& var){
  254.   String buttons = "";
  255.   String outputStateValue;
  256.   if(var == "BUTTEXT1") {
  257.     outputStateValue = outputState(OUTPUT_1);
  258.     buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox_1(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label></p>";
  259.   }
  260.   else if(var == "BUTTEXT2") {
  261.     outputStateValue = outputState(OUTPUT_2);
  262.     buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox_2(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label></p>";
  263.   }
  264.   else if(var == "BUTTEXT3") {
  265.     outputStateValue = outputState(OUTPUT_3);
  266.     buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox_3(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label></p>";
  267.   }
  268.   else if(var == "BUTTEXT4") {
  269.     outputStateValue = outputState(OUTPUT_4);
  270.     buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox_4(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label></p>";
  271.   }
  272.   else
  273.   {
  274.     return String();
  275.   }
  276.   return buttons;
  277. }
  278. //---------------------------------------------------------------
  279. String outputState(int output_number){
  280.   if(digitalRead(output_number)) { return "checked"; }
  281.   return "";
  282. }