Advertisement
Guest User

Untitled

a guest
Apr 5th, 2024
1,142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 20.32 KB | None | 0 0
  1. #include <Arduino.h>
  2. #include <WiFi.h>
  3. #include <WiFiClient.h>
  4. #include <WebServer.h>
  5. #include <uri/UriBraces.h>
  6. #include <ESP32Servo.h>
  7.  
  8. /*
  9. Ludwig Punisher: ESP32 simple servo/relay webserver. This will trigger servos and
  10. relays based on URLs, while serving a webpage full of buttons you can press to call
  11. those URLs. This is system is still lacking some "proper" programming practices, however
  12. it is generating the webpage and all the settings based entirely on the objects/arrays at
  13. the top of this file. You should never have to change anything except the first few rows
  14. of variables to get this working.
  15.  
  16. Assuming the IP is 192.168.1.2, it might look something like this
  17. URL example: 192.168.1.2/servo/bidet1/open
  18. URL example: 192.168.1.2/servo/bidet1/close
  19. URL example: 192.168.1.2/relay/seatkicker1/open
  20.  
  21. Servo and relay names are case insensitive in the URL. All other URL parts are case sensitive.
  22. You might want to fix that.
  23.  
  24. Webpage frontend is using jQuery and Bootstrap, because I'm lazy. If you want something more
  25. efficient and less "wtf is he using jQuery for", do that yourself. I like jQuery because it's
  26. always 2012 in my mind.
  27.  
  28. @author: Tanner McCoolman 2024
  29. */
  30.  
  31. // Define your wifi credentials
  32. #define WIFI_SSID "SSID"
  33. #define WIFI_PASSWORD "Password123#"
  34.  
  35. // Defining the WiFi channel speeds up the connection:
  36. #define WIFI_CHANNEL 6
  37.  
  38. WebServer server(80);
  39.  
  40. bool debug = true;
  41.  
  42. const int servoCount = 5;
  43. String servoNames[5] = {          "Bidet1",   "Bidet2", "Airhorn1", "Airhorn2", "Fartbox" }; // servo name for URL
  44. int servoPins[5] = {              13,         33,       26,         32,         25,       }; // pin number
  45. int servoStartPoints[5] = {       0,          0,        0,          0,          0,        }; // angle in start position
  46. int servoMovePoints[5] = {        165,        165,      90,         90,         90,       }; // angle to move to when not in start position
  47. int servoAngleCurrent[5] = {      0,          0,        0,          0,          0,        }; // angle it currently is
  48. int servoAngle[5] = {             0,          0,        0,          0,          0,        }; // angle we want it to be
  49. int servoReturnAfterMove[5] = {   0,          0,        1,          1,          1,        }; // go back to default after a time (TRUE = 1)
  50. int servoWaitAfterMove[5] = {     100,        100,      1000,       1000,       1000,     }; // amount of time to wait to go to default (if returnAfterMove = 1)
  51. unsigned long servoTimers[5] = {  0,          0,        0,          0,          0,        }; // holder for the timers
  52. Servo servos[5];
  53.  
  54.  
  55. const int relayCount = 5;
  56. String relayNames[5] = {          "Gelblaster",   "Seatkicker1",  "Seatkicker2",  "PCPower", "Heaters"  }; // servo name for URL
  57. int relayPins[5] = {              12,             14,             27,             22,        23         }; // pin number
  58. int relayStartPoints[5] = {       0,              0,              0,              0,         0          }; // start point 0 = off, 1 = on
  59. int relayStateCurrent[5] = {      0,              0,              0,              0,         0          }; // state it currently is
  60. int relayState[5] = {             0,              0,              0,              0,         0          }; // state we want it to be
  61. int relayReturnAfterMove[5] = {   1,              1,              1,              1,         1          }; // go back to default after a time (TRUE = 1)
  62. int relayWaitAfterMove[5] = {     1400,           500,            500,            1500,      30000      }; // amount of time to wait to go to default (if returnAfterMove = 1)
  63. unsigned long relayTimers[5] = {  0,              0,              0,              0,         0          }; // holder for the timers
  64.  
  65. unsigned long currentMillis; // Current time in ms to compare waitAfterMove
  66.  
  67. int steppingDelay = 10; // Amount of time to wait before changing the angle of the servo in a stepping situation
  68.  
  69. String triggerElems; // String to hold the JSON of relay/servo names for auto generating the buttons on the frontend
  70.  
  71. void sendHtml() {
  72.   //  @TODO replace jQuery and full bootstrap, they are 99% bloat for this. I just like jQuery syntax and
  73.   // think Bootstrap makes things look pretty with very little effort
  74.   String response = R"(
  75. <!DOCTYPE html><html>
  76. <head>
  77.    <title>Ludwig Punisher</title>
  78.    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
  79.  
  80. </head>
  81. <body>
  82.     <div id="button-holder" class="container"></div>
  83.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
  84.     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
  85.     <script>
  86.         $(document).ready(function() {
  87.             var triggerEles = {triggerElems};
  88.             var defaultUrl = "http://10.20.2.51";
  89.             var buttonOpenCloseElem = "<div class=\"row\"><div class=\"col-md-6 col-sm-12 text-end\"><div class=\"d-grid gap-2 py-3\"><button data-trigger=\"{openUrl}\" type=\"button\" class=\"btn btn-success btn-block px-3\"><h2>Open {element}</h2></button></div></div><div class=\"col-md-6 col-sm-12 text-start\"><div class=\"d-grid gap-2 py-3\"><button data-trigger=\"{closeUrl}\" type=\"button\" class=\"btn btn-block btn-danger px-3\"><h2>Close {element}</h2></button></div></div></div>";
  90.             var currentTargets = [];
  91.  
  92.             for (var key in triggerEles) {
  93.                 for (var element of triggerEles[key]) {
  94.                     var newUrl = defaultUrl + "/" + key + "/" + element;
  95.                     var newButtonElem = buttonOpenCloseElem;
  96.                     var newUrlObj = {
  97.                         "openUrl": newUrl+"/open",
  98.                         "closeUrl": newUrl+"/close",
  99.                         "element": element
  100.                     };
  101.  
  102.                     for (var placeholder in newUrlObj) {
  103.                         newButtonElem = newButtonElem.replaceAll("{"+placeholder+"}", newUrlObj[placeholder]);
  104.                     }
  105.                     $("#button-holder").append(newButtonElem);
  106.                 }
  107.             }
  108.  
  109.             $("button").click(function(e){
  110.                 e.preventDefault();
  111.                 var urlTarget = $(this).data('trigger');
  112.                 if (currentTargets.includes(urlTarget)) {
  113.                   console.log("preventing this due to current target: "+urlTarget);
  114.                   return;
  115.                 }
  116.                 currentTargets.push(urlTarget);
  117.                 $.get(urlTarget, function (e) {
  118.                     var currentTargetIndex = currentTargets.indexOf(urlTarget);
  119.                     if (currentTargetIndex > -1) {
  120.                       delete currentTargets[currentTargetIndex];
  121.                     }
  122.                 });
  123.             });
  124.         });
  125.     </script>
  126. </body>
  127. </html>
  128.   )";
  129.  response.replace("{triggerElems}", triggerElems); // Add the servo/relay names in JSON form to the response string
  130.  server.send(200, "text/html", response);
  131. }
  132.  
  133. void setup(void) {
  134.  if (debug) {
  135.    // Serial.begin(115200);
  136.    Serial.begin(9600);
  137.  }
  138.  
  139.  delay(10);
  140.  
  141.  WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
  142.  
  143.  if (debug) {
  144.    Serial.print("Connecting to WiFi ");
  145.    Serial.print(WIFI_SSID);
  146.  
  147.    // Wait for connection
  148.    while (WiFi.status() != WL_CONNECTED) {
  149.      delay(100);
  150.      Serial.print(".");
  151.    }
  152.    Serial.println(" Connected!");
  153.  
  154.    Serial.print("IP address: ");
  155.    Serial.println(WiFi.localIP());
  156.  
  157.  } else {
  158.    while (WiFi.status() != WL_CONNECTED) {
  159.      delay(100);
  160.    }
  161.  }
  162.  
  163.  server.on("/", sendHtml);
  164.  server.enableCORS(true);
  165.  
  166.  // Build triggerElems for HTML response
  167.  // @TODO put triggerElems building into a proper function to maximize reusability so servo and relay additions use the same code
  168.  // Start Servo section of triggerElems
  169.  triggerElems = "{\"servo\":";
  170.   int finalIndex = servoCount - 1;
  171.  
  172.   for (int index = 0; index < servoCount; index++) {
  173.     if (index == 0) {
  174.       triggerElems += "[";
  175.     }
  176.     triggerElems += "\""+servoNames[index]+"\"";
  177.     if (index < finalIndex) {
  178.       triggerElems += ",";
  179.     }
  180.   }
  181.  
  182.   triggerElems += "],\"relay\":";
  183.  
  184.   // Start Relay section of triggerElems
  185.   finalIndex = relayCount - 1;
  186.  
  187.   for (int index = 0; index < relayCount; index++) {
  188.     if (index == 0) {
  189.       triggerElems += "[";
  190.     }
  191.     triggerElems += "\""+relayNames[index]+"\"";
  192.     if (index < finalIndex) {
  193.       triggerElems += ",";
  194.     }
  195.   }
  196.  
  197.   triggerElems += "]";
  198.  
  199.   triggerElems += "}"; // end the JSON
  200.  
  201.   // Output the triggerElems JSON so we can verify things haven't gone horribly wrong
  202.   if (debug) {
  203.     Serial.println(triggerElems);
  204.   }
  205.  
  206.   // Set up servos
  207.   for (int index = 0; index < servoCount; index++) {
  208.     servos[index].attach(servoPins[index]);
  209.     // servoAngleCurrent[index] = servos[index].read();
  210.     servos[index].write(servoStartPoints[index]);
  211.     if (debug) {
  212.       Serial.println(servoNames[index]+": "+servoAngleCurrent[index]);
  213.     }
  214.   }
  215.  
  216.   // Set up relays
  217.   for (int index = 0; index < relayCount; index++) {
  218.     pinMode(relayPins[index], OUTPUT);
  219.  
  220.     if (relayStartPoints[index] == 1) {
  221.       digitalWrite(relayPins[index], HIGH);
  222.     } else {
  223.       digitalWrite(relayPins[index], LOW);
  224.     }
  225.   }
  226.  
  227.   // Set up relay toggle URLs
  228.   server.on(UriBraces("/relay/{}"), []() {
  229.     String relayPath = server.pathArg(0);
  230.     if (debug) {
  231.       Serial.println(relayPath);
  232.     }
  233.  
  234.     for (int index = 0; index < relayCount; index++) {
  235.       String relayNameLowerCase = relayNames[index];
  236.       relayNameLowerCase.toLowerCase();
  237.  
  238.       if (relayPath == relayNames[index] || relayPath == relayNameLowerCase) {
  239.         if (relayState[index] == 1) {
  240.           relayState[index] = 0;
  241.         } else {
  242.           relayState[index] = 1;
  243.         }
  244.       }
  245.     }
  246.  
  247.     sendHtml();
  248.   });
  249.  
  250.   // Set up relay switch URLs (specifically turn on or off)
  251.   server.on(UriBraces("/relay/{}/{}"), []() {
  252.     String relayPath = server.pathArg(0);
  253.     String relayAction = server.pathArg(1);
  254.  
  255.     if (debug) {
  256.       Serial.println(relayPath);
  257.       Serial.println(relayAction);
  258.     }
  259.  
  260.     relayAction.toLowerCase();
  261.  
  262.     for (int index = 0; index < relayCount; index++) {
  263.       String relayNameLowerCase = relayNames[index];
  264.       relayNameLowerCase.toLowerCase();
  265.  
  266.       if (relayPath == relayNames[index] || relayPath == relayNameLowerCase) {
  267.         if (relayAction == "start" || relayAction == "go" || relayAction == "open" || relayAction == "on") {
  268.           relayState[index] = 1;
  269.           relayTimers[index] = millis();
  270.         } else if (relayAction == "stop" || relayAction == "end" || relayAction == "close" || relayAction == "off") {
  271.           relayState[index] = 0;
  272.         }
  273.       }
  274.     }
  275.  
  276.     sendHtml();
  277.   });
  278.  
  279.   // Setup Reset URL
  280.   server.on(UriBraces("/reset"), []() {
  281.     if (debug) {
  282.       Serial.println("reseting all");
  283.     }
  284.     // reset servos
  285.     for (int index = 0; index < servoCount; index++) {
  286.       servoAngle[index] = servoStartPoints[index];
  287.     }
  288.     // reset relays
  289.     for (int index = 0; index < relayCount; index++) {
  290.       relayState[index] = relayStartPoints[index];
  291.     }
  292.  
  293.     sendHtml();
  294.   });
  295.  
  296.   // Set up servo toggle URLS
  297.   server.on(UriBraces("/servo/{}"), []() {
  298.  
  299.     String servoPath = server.pathArg(0);
  300.     String servoAction = "toggle";
  301.  
  302.     if (debug) {
  303.       Serial.println(servoPath);
  304.       Serial.println(servoAction);
  305.     }
  306.  
  307.     for (int index = 0; index < servoCount; index++) {
  308.       String servoNameLowerCase = servoNames[index];
  309.       servoNameLowerCase.toLowerCase();
  310.  
  311.       if (servoPath == servoNames[index] || servoPath == servoNameLowerCase) {
  312.         if (debug) {
  313.           Serial.print("Moving ");
  314.           Serial.print(servoNames[index]);
  315.           Serial.print(" to ");
  316.         }
  317.  
  318.         if (servoAngleCurrent[index] == servoStartPoints[index]) {
  319.           servoAngle[index] = servoMovePoints[index];
  320.  
  321.           if (debug) {
  322.             Serial.print(servoAngle[index]);
  323.             Serial.print(" deg from ");
  324.             Serial.print(servoAngleCurrent[index]);
  325.           }
  326.  
  327.         } else if (servoAngleCurrent[index] == servoMovePoints[index]) {
  328.           servoAngle[index] = servoStartPoints[index];
  329.  
  330.           if (debug) {
  331.             Serial.print(servoAngle[index]);
  332.             Serial.print(" deg from ");
  333.             Serial.print(servoAngleCurrent[index]);
  334.           }
  335.  
  336.         } else if (debug) {
  337.           Serial.print("nowhere lol");
  338.         }
  339.  
  340.         if (debug) {
  341.           Serial.println("");
  342.         }
  343.       }
  344.     }
  345.  
  346.     sendHtml();
  347.   });
  348.  
  349.   // Set up servo switch URLs
  350.   server.on(UriBraces("/servo/{}/{}"), []() {
  351.  
  352.     String servoPath = server.pathArg(0);
  353.     String servoAction = server.pathArg(1);
  354.  
  355.     if (debug) {
  356.       Serial.println(servoPath);
  357.       Serial.println(servoAction);
  358.     }
  359.  
  360.     if (servoAction == "") {
  361.       servoAction = "toggle";
  362.     }
  363.  
  364.     for (int index = 0; index < servoCount; index++) {
  365.       String servoNameLowerCase = servoNames[index];
  366.       servoNameLowerCase.toLowerCase();
  367.  
  368.       if (servoPath == servoNames[index] || servoPath == servoNameLowerCase) {
  369.         if (servoAction == "open" || servoAction == "start") {
  370.           servoAngle[index] = servoMovePoints[index];
  371.  
  372.           if (debug) {
  373.             Serial.print("opening ");
  374.             Serial.println(servoNames[index]);
  375.           }
  376.  
  377.           servoTimers[index] = millis();
  378.         } else if (servoAction == "close" || servoAction == "end") {
  379.           servoAngle[index] = servoStartPoints[index];
  380.  
  381.           if (debug) {
  382.             Serial.print("closing ");
  383.             Serial.println(servoNames[index]);
  384.           }
  385.  
  386.         } else if (servoAction == "toggle") {
  387.           if (debug) {
  388.             Serial.print("toggling ");
  389.             Serial.println(servoNames[index]);
  390.           }
  391.            
  392.           if (servoAngleCurrent[index] == servoStartPoints[index]) {
  393.             servoTimers[index] = millis();
  394.           }
  395.  
  396.           if (servoAngleCurrent[index] == servoStartPoints[index]) {
  397.             servoAngle[index] = servoMovePoints[index];
  398.           } else if (servoAngleCurrent[index] == servoMovePoints[index]) {
  399.             servoAngle[index] = servoStartPoints[index];
  400.           }
  401.         }
  402.       }
  403.     }
  404.  
  405.     sendHtml();
  406.   });
  407.  
  408.   server.begin();
  409.  
  410.   if (debug) {
  411.     Serial.println("HTTP server started");
  412.   }
  413.  
  414. }
  415.  
  416. void doServoChanges() {
  417.   for (int index = 0; index < servoCount; index++) {
  418.     if (servoAngleCurrent[index] != servoAngle[index]) {
  419.       int bigAngle = (servoAngleCurrent[index] > servoAngle[index]) ? servoAngleCurrent[index] : servoAngle[index];
  420.       int angleStep = (bigAngle % 2 == 0) ? bigAngle / 2 : (bigAngle - 1) / 2;
  421.       int i = servoAngleCurrent[index];
  422.  
  423.       if (debug) {
  424.         Serial.print("Big Angle: ");
  425.         Serial.print(bigAngle);
  426.         Serial.print(" | ");
  427.       }
  428.  
  429.       // If the start position is current and we're moving off of that, set up a timer
  430.       if (servoStartPoints[index] == servoAngleCurrent[index]) {
  431.         servoTimers[index] = millis();
  432.       }
  433.  
  434.       // going down
  435.       if (servoAngleCurrent[index] > servoAngle[index]) {
  436.  
  437.         if (debug) {
  438.           Serial.print("Going Down. Current Angle: ");
  439.           Serial.print(servoAngleCurrent[index]);
  440.           Serial.print(" Desired Angle: ");
  441.           Serial.print(servoAngle[index]);
  442.           Serial.print(" Angle Step: ");
  443.           Serial.println(angleStep);
  444.           Serial.print("I: ");
  445.         }
  446.  
  447.         while (i > servoAngle[index]) {
  448.  
  449.           if (debug) {
  450.             Serial.print(i);
  451.             Serial.print(" ");
  452.           }
  453.  
  454.           servos[index].write(i);
  455.           delay(steppingDelay);
  456.           i -= angleStep;
  457.         }
  458.  
  459.         if (debug) {
  460.           Serial.println("");
  461.         }
  462.  
  463.       // going up
  464.       } else if (servoAngleCurrent[index] < servoAngle[index]) {
  465.  
  466.         if (debug) {
  467.           Serial.print("Going Up. Current Angle: ");
  468.           Serial.print(servoAngleCurrent[index]);
  469.           Serial.print(" Desired Angle: ");
  470.           Serial.print(servoAngle[index]);
  471.           Serial.print(" Angle Step: ");
  472.           Serial.println(angleStep);
  473.           Serial.print("I: ");
  474.         }
  475.  
  476.         while (i < servoAngle[index]) {
  477.           if (debug) {
  478.             Serial.print(i);
  479.             Serial.print(" ");
  480.           }
  481.  
  482.           servos[index].write(i);
  483.           delay(steppingDelay);
  484.           i += angleStep;
  485.         }
  486.  
  487.         if (debug) {
  488.           Serial.println("");
  489.         }
  490.       }
  491.      
  492.       servos[index].write(servoAngle[index]);
  493.       servoAngleCurrent[index] = servoAngle[index];
  494.     }
  495.   }
  496. }
  497.  
  498. void doServoReset() {
  499.   for (int index = 0; index < servoCount; index++) {
  500.     if (servoReturnAfterMove[index] == 1 && servoAngleCurrent[index] != servoStartPoints[index]) {
  501.       if (servoWaitAfterMove[index] > 0) {
  502.         if (servoTimers[index] + servoWaitAfterMove[index] < currentMillis) {
  503.  
  504.           if (debug) {
  505.             Serial.print("Resetting ");
  506.             Serial.print(servoNames[index]);
  507.             Serial.print(" after waiting for ");
  508.             Serial.print(servoWaitAfterMove[index]);
  509.             Serial.println(" ms");
  510.             Serial.print("Reset wait for ");
  511.             Serial.print(servoNames[index]);
  512.             Serial.print(" complete");
  513.           }
  514.  
  515.           servoAngle[index] = servoStartPoints[index];
  516.         }
  517.       } else {
  518.         if (debug) {
  519.           Serial.print("Resetting ");
  520.           Serial.print(servoNames[index]);
  521.           Serial.println("");
  522.         }
  523.  
  524.         servoAngle[index] = servoStartPoints[index];
  525.       }
  526.     }
  527.   }
  528. }
  529.  
  530. void doRelayChanges() {
  531.   for (int index = 0; index < relayCount; index++) {
  532.     if (relayStateCurrent[index] != relayState[index]) {
  533.       // Set the timer
  534.       if (relayStateCurrent[index] == relayStartPoints[index]) {
  535.         relayTimers[index] = millis();
  536.       }
  537.  
  538.       // Write the changes
  539.       if (relayState[index] == 0) {
  540.         digitalWrite(relayPins[index], LOW);
  541.         relayStateCurrent[index] = relayState[index];
  542.       } else if (relayState[index] == 1) {
  543.         digitalWrite(relayPins[index], HIGH);
  544.         relayStateCurrent[index] = relayState[index];
  545.       }
  546.     }
  547.   }
  548. }
  549.  
  550. void doRelayReset() {
  551.   for (int index = 0; index < relayCount; index++) {
  552.     if (relayReturnAfterMove[index] == 1 && relayStateCurrent[index] != relayStartPoints[index]) {
  553.       if (relayWaitAfterMove[index] > 0) {
  554.         if (relayTimers[index] + relayWaitAfterMove[index] < currentMillis ) {
  555.           if (debug) {
  556.             Serial.print("Timer: ");
  557.             Serial.print(relayTimers[index]);
  558.             Serial.print(" | Wait: ");
  559.             Serial.print(relayWaitAfterMove[index]);
  560.             Serial.print(" | Total Time: ");
  561.             Serial.print(relayTimers[index] + relayWaitAfterMove[index]);
  562.             Serial.print(" | currentMillis: ");
  563.             Serial.println(currentMillis);
  564.  
  565.             Serial.print("Resetting ");
  566.             Serial.print(relayNames[index]);
  567.             Serial.print(" after waiting for ");
  568.             Serial.print(relayWaitAfterMove[index]);
  569.             Serial.println(" ms");
  570.             Serial.print("Reset wait for ");
  571.             Serial.print(relayNames[index]);
  572.             Serial.print(" complete");
  573.           }
  574.  
  575.           relayState[index] = relayStartPoints[index];
  576.         }
  577.       } else {
  578.         if (debug) {
  579.           Serial.print("Resetting ");
  580.           Serial.print(relayNames[index]);
  581.           Serial.println("");
  582.         }
  583.  
  584.         relayState[index] = relayStartPoints[index];
  585.       }
  586.     }
  587.   }
  588. }
  589.  
  590.  
  591. // Since none of the actions set up through URLs actually do anything except
  592. // change variable numbers, all the real moving gets done in the loop. If you
  593. // set servoAngle[3] = 90, for example, and servoAngleCurrent[3] is set to 180,
  594. // this code will move the servo from 180 to 90 and update servoAngleCurrent[3]
  595. // to be 180.
  596.  
  597. void loop(void) {
  598.   currentMillis = millis();
  599.   server.handleClient();
  600.   doServoReset();
  601.   doRelayReset();
  602.   doServoChanges();
  603.   doRelayChanges();
  604.   delay(2);
  605. }
  606.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement