Advertisement
jgoy

ESP32 Keypad + relay lock (webserver)

Jun 1st, 2024 (edited)
346
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.77 KB | Source Code | 0 0
  1. /*
  2. ==============================================================================================
  3. Arduino IDE - Boards manager ESP32 (espressif) terugzetten naar versie 2.0.17 voor deze schets
  4. ==============================================================================================
  5.  
  6. Materiaal:
  7. =============
  8. Smallere versie van ESP32 met display:
  9. https://www.lilygo.cc/products/lilygo%C2%AE-ttgo-t-display-1-14-inch-lcd-esp32-control-board?variant=42720264650933
  10. https://www.amazon.nl/dp/B0CTG459MW     (uitbreidingsbord)
  11. https://www.amazon.nl/dp/B07RCGWGD2     (numeriek keypad)
  12. https://www.amazon.nl/dp/B06W564ZSD     (PCF8574: 8 bit parallel naar I2C)
  13. https://www.amazon.nl/dp/B07PZC9ZMM     (relais met octocoupler - triggerstroom 5 mA)
  14.  
  15. Aansluitingen:
  16. ==============
  17. Numeriek keypad -> PCF8574, vanaf P0 .. P6 (P0 .. P7 indien keypad met 4 kolommen)
  18. PCF8574 -> ESP32: GND -> GND, VCC -> 3V3, SDA -> 21, SCL -> 22 (normale I2C-aansluitingen)
  19. Relais -> ESP32: IN -> 26, DC+ -> V5, DC- -> GND  */
  20.  
  21. #include <WebServer.h>    // arduino IDE - keuze bord: DOIT ESP32 DEVKIT V1
  22. #include <Preferences.h>  // https://github.com/vshymanskyy/Preferences
  23. #include <I2CKeyPad.h>    // https://github.com/RobTillaart/I2CKeyPad
  24. #include <TFT_eSPI.h>     // https://github.com/Bodmer/TFT_eSPI
  25. #include <Wire.h>
  26. #include <esp_task_wdt.h>  // voor de watchdog timer (onnodig om deze bibliotheek te installeren = core)
  27.  
  28. Preferences flash;                       // om gegevens weg te schrijven en op te halen uit FLASH-geheugen
  29. WebServer mijnServer(80);                // instantie van Webserver
  30. I2CKeyPad keyPad(0x20);                  // 0x20 = de 3 brugjes staan het verst van de parallelcontacten
  31. TFT_eSPI tft = TFT_eSPI();               // User_Setup: Setup25_TTGO_T_Display.h
  32. TFT_eSprite sprite = TFT_eSprite(&tft);  // sprites voor vloeiende bewegingen op het display: hoofdsprite
  33. TFT_eSprite leftSp = TFT_eSprite(&tft);  // sprite linkerkant scherm
  34. TFT_eSprite rightS = TFT_eSprite(&tft);  // sprite rechterkant scherm
  35.  
  36. const uint8_t relaisPin = 26;    // relais stuurt het slot
  37. uint32_t WDT_TIMEOUT = 4000000;  // waarde in sec voor watchdogtimer = 3 dagen voor de millis() rollover 2^32
  38. bool slot_open = false, code_veranderd = false, toetsen_actief = true, isCodeJuist = false;
  39. uint32_t slotMillis, codeMillis, keypadMillis;                      // voor de timers
  40. String codes[20];                                                   // lege array van 20 strings om de codes in te laden
  41. String paswoord;                                                    // "paswoord" dat vergeleken wordt met de codes
  42. const char *netwerkNaam = "MynGeheim", *hetPaswoord = "TopSecret";  // voor access point
  43. const char *volgNr[] = { "code00", "code01", "code02", "code03", "code04", "code05", "code06", "code07", "code08", "code09",
  44.                          "code10", "code11", "code12", "code13", "code14", "code15", "code16", "code17", "code18", "code19" };
  45.  
  46. void setup() {
  47.   Serial.begin(115200);
  48.   esp_task_wdt_init(WDT_TIMEOUT, true);  // watchdogtimer. true = "panic mode", dus de ESP32 herstart als de timer afloopt
  49.   esp_task_wdt_add(NULL);                // esp_task_wdt_add(handle). NULL = huidige thread aan de watchdog toevoegen
  50.   tft.init(), tft.setRotation(3);        // initialiseren display + landscape
  51.   sprite.createSprite(240, 135);         // sprite voor snellere weergave op scherm
  52.   digitalWrite(TFT_BL, LOW);             // display uitzetten: backlight off
  53.   pinMode(relaisPin, OUTPUT), digitalWrite(relaisPin, LOW);
  54.   pinMode(35, INPUT_PULLUP);            //  knop naast usb = om rotatie scherm te wijzigen
  55.   Wire.begin(), Wire.setClock(800000);  //  bepaalt de snelheid dat het keypad gelezen wordt. Was 400000 voor een ESP8266
  56.   keyPad.begin();
  57.   startWiFiHotSpot();
  58.   startWebServer();
  59. }
  60.  
  61. void loop() {
  62.   keyPadAfhandelen();         // toetsaanslagen verwerken
  63.   mijnServer.handleClient();  // webserver behandelt aanvragen van verbonden apparaten
  64. }
  65.  
  66. void startWiFiHotSpot() {  // start hotspot
  67.   IPAddress local_IP(1, 2, 3, 4), gateway(1, 2, 3, 4), subnet(255, 255, 255, 0);
  68.   WiFi.softAPConfig(local_IP, gateway, subnet);           // stelt "1.2.3.4" in als adres in browser voor webserver
  69.   WiFi.softAP(netwerkNaam, hetPaswoord, 1, 0, 8, false);  // netw, pasw, kanaal, verborgen, ...
  70. }
  71.  
  72. void startWebServer() {                      // webserver & navigatie pagina's
  73.   codesOphalen();                            // slotcodes uit flash-geheugen naar variabelen kopiëren
  74.   mijnServer.on("/", paginaCodesInstellen);  // functies voorzien voor elke pagina-link
  75.   mijnServer.on("/codesOpgeslagen/", paginaCodesOpslaan);
  76.   mijnServer.onNotFound(paginaNietGevonden);
  77.   mijnServer.begin();
  78. }
  79.  
  80. void keyPadAfhandelen() {                               // behandelt ingetikte cijfers en kijkt na of de code correct is
  81.   const char keys[] = "123A456B789C*0#DNF";             // toetsen van klavier. N = NoKey, F = Fail
  82.   uint8_t index = keyPad.getKey();                      // input van toetsenbord opvragen
  83.   if (index != 16) {                                    // indien er een toets ingedrukt werd (geen "N" en dus geen index 16)
  84.     if (toetsen_actief) {                               // toetsaanslagen worden alleen aanvaard na loslaten eerdere toets
  85.       if (millis() - keypadMillis > 200) {              // 200 milliSec UI debouncing
  86.         toetsen_actief = false;                         // toetsenbord blokkeren als een toets ingetikt werd
  87.         codeMillis = millis();                          // gebruiker heeft max. 10 seconden per cijfer
  88.         if (index == 12 || index == 14) paswoord = "";  // resetten code bij intikken "*" of "#"
  89.         else paswoord += keys[index];                   // ingetikt cijfer bijvoegen
  90.         Serial.print(keys[index]);                      // debug info
  91.         digitalWrite(TFT_BL, HIGH);                     // display aanzetten (backlight aan)
  92.         sprite.fillSprite(TFT_BLACK);                   // vorige gegevens op display wissen
  93.         for (uint8_t i = 0; i < paswoord.length(); i++) sprite.fillCircle(30 + i * 33, 68, 15, TFT_YELLOW);
  94.         sprite.pushSprite(0, 0);  // sprite naar display wegschrijven op coordinaten 0, 0
  95.         checkCode(paswoord);      // kijk of code correct is
  96.         keypadMillis = millis();  // "10 seconden-vlag" zetten dat er een toets ingedrukt werd
  97.       }
  98.     }
  99.   } else if (index == 16) toetsen_actief = true;                  // bij loslaten van toets (index 16), toetsenbord vrijgeven.
  100.   if (paswoord.length() >= 6) paswoord = "", Serial.println();    // paswoord reset als er te veel werd ingetikt
  101.   if (!timer_Intikken(10000, codeMillis)) paswoord = "";          // na 10 seconden niets intikken: paswoord reset
  102.   if (millis() - keypadMillis > 5000) digitalWrite(TFT_BL, LOW);  // 5 seconden na intikken: display uitzetten
  103.   if (slot_open) digitalWrite(relaisPin, (timer_Slot_Open(5000, slotMillis)) ? HIGH : LOW);
  104.   if (isCodeJuist) animatieDisplay("Slot Open");
  105. }
  106.  
  107. void paginaCodesInstellen() {  // startpagina van webserver
  108.   mijnServer.send(200, "text/html", basisHtml() + instelHtml());
  109. }
  110.  
  111. void paginaCodesOpslaan() {                                      // knop "Opslaan" van webpagina stuurt naar deze functie
  112.   flash.begin("codes", true);                                    // true = alleen lezen
  113.   for (byte i = 0; i < 20; i++) {                                // we lezen 20 waarden uit
  114.     String idx = "a";                                            // nakijken of er iets gewijzigd werd
  115.     idx = idx + i;                                               // "a0".."a19"
  116.     if (codes[i] != mijnServer.arg(idx)) code_veranderd = true;  // vlag zetten als er wijzigingen waren
  117.     codes[i] = mijnServer.arg(idx);
  118.   }
  119.   flash.end();
  120.   mijnServer.send(200, "text/html", basisHtml() + ((code_veranderd) ? opgeslagenHtml() : geenWijzigingHtml()));
  121.   if (code_veranderd) opslaan(), code_veranderd = false;  // indien wijzigingen: opslaan en reset de vlag
  122. }
  123.  
  124. void opslaan() {                 // enkel aangeroepen als er wijzigingen zijn aan een veld op de pagina
  125.   flash.begin("codes", false);   // false = ook schrijven
  126.   for (byte i = 0; i < 20; i++)  // alle 20 velden van webpagina opvragen en controleren op lengte
  127.     if (codes[i].length() < 7 && codes[i].length() > 3) flash.putString(volgNr[i], codes[i]);
  128.   flash.end();     // codes tussen 6 en 4 cijfers worden opgeslagen
  129.   codesOphalen();  // nodig om de volgende keer bij startpagina de gewijzigde waarden weer te geven
  130. }
  131.  
  132. void paginaNietGevonden() {  // error 404
  133.   mijnServer.send(404, "text/html", basisHtml() + nietGevondenHtml());
  134. }
  135.  
  136. void checkCode(String mijnCode) {  // slot openen moet via deze functie
  137.   isCodeJuist = false;
  138.   for (uint8_t i = 0; i < 20; i++)                 // alle 20 codes aflopen
  139.     if (codes[i] == mijnCode) isCodeJuist = true;  // kijken of er 1 overeenkomt
  140.   if (isCodeJuist) {                               // indien juiste code: openen en teller van 5 seconden starten
  141.     slot_open = true, slotMillis = millis();
  142.     Serial.println("\nSlot open met code " + mijnCode);
  143.   }
  144. }
  145.  
  146. bool timer_Slot_Open(uint16_t mijnTijd, uint32_t beginTijd) {  // timer om slot te sluiten
  147.   if (millis() - beginTijd >= mijnTijd) slot_open = false, paswoord = "";
  148.   return (millis() - beginTijd >= mijnTijd) ? false : true;
  149. }
  150.  
  151. bool timer_Intikken(uint16_t mijnTijd, uint32_t beginTijd) {  // timer max tijd tussen ingetikte cijfers
  152.   return (millis() - beginTijd >= mijnTijd) ? false : true;
  153. }
  154.  
  155. void codesOphalen() {          // slotcodes uit flash-geheugen lezen en naar variabelen kopiëren
  156.   flash.begin("codes", true);  // true : FLASH alleen lezen
  157.   for (byte i = 0; i < 20; i++) codes[i] = flash.getString(volgNr[i], "873491");
  158.   flash.end();
  159. }
  160.  
  161. void splitSprite(bool split) {  // horizontaal splitsen [bool split = true] of samenvoegen [false]
  162.   leftSp.createSprite(120, 135);
  163.   rightS.createSprite(120, 135);
  164.   for (byte ver = 0; ver < 135; ver++) {  // sprite in 2 delen opdelen en gegevens naar de helften schrijven
  165.     for (byte hor = 0; hor < 120; hor++) leftSp.drawPixel(hor, ver, sprite.readPixel(hor, ver));
  166.     for (byte hor = 120; hor < 240; hor++) rightS.drawPixel(hor - 120, ver, sprite.readPixel(hor, ver));
  167.   }
  168.   if (split) leftSp.drawFastVLine(119, 0, 135, TFT_BLACK), rightS.drawFastVLine(0, 0, 135, TFT_BLACK);
  169.   for (byte hor = 0; hor < 120; hor++) {
  170.     if (split) leftSp.pushSprite(0 - hor, 0), rightS.pushSprite(hor + 120, 0);
  171.     else leftSp.pushSprite(hor - 120, 0), rightS.pushSprite(240 - hor, 0);
  172.   }
  173.   leftSp.deleteSprite(), rightS.deleteSprite();
  174. }
  175.  
  176. void animatieDisplay(String tekst) {                                       // wordt aangeroepen bij openen slot
  177.   digitalWrite(TFT_BL, HIGH);                                              // display aanzetten
  178.   sprite.fillSprite(sprite.color565(100, 100, 100));                       // vul display met grijs
  179.   for (int i = 0; i < 8192; i++) {                                         // oppervlak met fijne textuur aanmaken
  180.     byte j = random(100) + 50;                                             // maak random grijswaarden
  181.     sprite.drawPixel(random(240), random(135), sprite.color565(j, j, j));  // en zet op willekeurige plaatsen
  182.   }
  183.   sprite.setFreeFont(&FreeSansBold18pt7b);  // keuze font
  184.   sprite.setTextColor(TFT_YELLOW);
  185.   sprite.drawCentreString(tekst, 120, 60, 1);
  186.   splitSprite(false);                            // scherm vanuit buitenkanten (L + R) samenvoegen
  187.   sprite.drawFastHLine(0, 134, 240, TFT_BLACK);  // om te vermijden dat er lijnen achterblijven
  188.   for (byte i = 0; i < 136; i++) {               // schuif alles vloeiend naar boven
  189.     sprite.pushSprite(0, 0 - i);
  190.   }
  191.   digitalWrite(TFT_BL, LOW);  // display terug uitzetten
  192.   isCodeJuist = false;
  193. }
  194.  
  195. String basisHtml() {  // basispagina html + css
  196.   String html0 = "<!DOCTYPE html>\r\n<html lang='nl'>\r\n<head>\r\n";
  197.   html0 += "<meta name=\"viewport\" content = \"width=device-width\">\r\n";
  198.   html0 += "<title>Slot Boomgaard</title>\r\n<style>\r\n* {\r\n  text-align: center;\r\n";
  199.   html0 += "  font-family: Arial, Helvetica, sans-serif;\r\n  }\r\n";
  200.   html0 += "body{\r\n  background: linear-gradient(to right, Lavender, LightCyan);\r\n";
  201.   html0 += "}\r\n\r\n.knop {\r\n  width: 110px;\r\n  color: #333333;\r\n";
  202.   html0 += "  border-radius: 5px;\r\n  padding: 6px 6px;\r\n  margin: 2px; \r\n  font-family: Arial, Helvetica, ";
  203.   html0 += "sans-serif;\r\n  font-size: 19px;\r\n  font-weight: 500;\r\n  font-style: normal;\r\n";
  204.   html0 += "  text-decoration: none; \r\n  text-align: center;\r\n";
  205.   html0 += "  text-shadow: -1px -1px rgba(0,0,0,1), 1px 1px rgba(255,255,255,1); \r\n";
  206.   html0 += "  background: transparent;\r\n  display: inline-block;\r\n";
  207.   html0 += "  box-shadow: inset    0 1px 1px rgba(255,255,255,1), \r\n  inset  1px 0px 1px rgba(255,255,255,1),\r\n";
  208.   html0 += "    inset -2px 0px 2px rgba(0,0,0,0.4),  \r\n   inset 0px -1px 1px rgba(0,0,0,0.7),   \r\n";
  209.   html0 += "    0px 2px 2px rgba(0,0,0,0.7),  \r\n  1px 0px 1px rgba(0,0,0,0.7);  \r\n";
  210.   html0 += "  border: 1px solid black;\r\n  background: -webkit-linear-gradient( \r\n";
  211.   html0 += "    #e0e0e0 1% , #f2f2f2 3% , #fafafa 5% , #dfdfdf 10%, #ffffff 16%, \r\n";
  212.   html0 += "    #cfcfcf 30%, #b9b9b9 50%, #8c8c8c 87%, #6e6e6e 96%, #4b4b4b 98%, #222222 100%); \r\n";
  213.   html0 += "}\r\n\r\n.knop:active{\r\n  transform: scale(0.98);\r\n}\r\n\r\n#draai {\r\n";
  214.   html0 += "  margin: auto;\r\n  padding: 20px;\r\n  background-color: LightYellow;\r\n";
  215.   html0 += "  border: 1px solid black;\r\n  border-radius: 10px;\r\n  width: 280px;\r\n";
  216.   html0 += "  text-align: center;\r\n  box-shadow: 0px 6px 6px rgba(0,0,0,0.7);\r\n";
  217.   html0 += "  font-size: 58px;\r\n  font-weight: 900;\r\n  position: relative;\r\n}\r\n";
  218.   html0 += ".inputveld {\r\n  font-size: 30px;\r\n  width: 160px;\r\n}\r\n";
  219.   html0 += "</style>\r\n</head>\r\n<body>\r\n<h1>Slot Boomgaard</h1>\r\n<div id='draai'>\r\n";
  220.   return html0;
  221. }
  222.  
  223. String instelHtml() {  // startpagina = codes instellen
  224.   String html1 = "<span style='color: red'>Wijzig Codes</span>\r\n</div><br>\r\n";
  225.   html1 += "<p>Code moet een getal tussen 4 en 6 cijfers zijn</p>\r\n<br>\r\n";
  226.   for (byte i = 0; i < 20; i++) {  // waarden uit het flashgeheugen invullen in  html-inputvelden
  227.     html1 += (i + 1);
  228.     html1 += ". ";
  229.     html1 += "<input type='number' class='inputveld' id='a";
  230.     html1 += i;
  231.     html1 += "' value='";
  232.     html1 += codes[i];
  233.     html1 += "'><br><br>\r\n";
  234.   }
  235.   html1 += "<span class='knop' onClick='stuurDoor()'>Opslaan</span></a>\r\n";
  236.   html1 += "</span><hr>";
  237.   html1 += "</body>\r\n<script>\r\nfunction stuurDoor() {\r\n var url = 'codesOpgeslagen/?';\r\n";
  238.   for (byte i = 0; i < 20; i++) {  // alle (gewijzigde) waarden doorgeven in URL-parameters
  239.     html1 += " url = url + 'a";    // http://1.2.3.4/codesOpgeslagen/?a0=123456&a1=987654 enz.
  240.     html1 += i;
  241.     html1 += "=' + document.getElementById('a";
  242.     html1 += i;
  243.     html1 += "').value + '&';\r\n";
  244.   }
  245.   html1 += "window.location.href=url;\r\n}\r\n</script>\r\n</html>\r\n";
  246.   return html1;
  247. }
  248.  
  249. String opgeslagenHtml() {  // pagina "waarden zijn opgeslagen"
  250.   String html2 = "<span style='color: green'>Bewaard</span>\r\n</div><br><br><br><br>\r\n<h1><span style='color: ";
  251.   html2 += "green'>De waarden zijn opgeslagen.</span>\r\n</h1></body>\r\n</html>\r\n";
  252.   return html2;
  253. }
  254.  
  255. String geenWijzigingHtml() {  // pagina "geen wijzigingen"
  256.   String html2 = "<span style='color: green'>OK</span>\r\n</div><br><br><br><br>\r\n<h1><span style='color: ";
  257.   html2 += "green'>Geen wijzigingen aangebracht.</span>\r\n</h1></body>\r\n</html>\r\n";
  258.   return html2;
  259. }
  260.  
  261. String nietGevondenHtml() {  // pagina "error 404"
  262.   String html3 = "404 Bestaat niet\r\n</div><br><br><br><br>\r\n";
  263.   html3 += "<h1><span style='color: red'>Pagina ontbreekt</rd></h1>\r\n</body>\r\n</html>\r\n";
  264.   return html3;
  265. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement