sneaky4oe

ESP8266 Remote PC Power Switch (Home Assistant & mDNS) - power relay + resistors

Dec 30th, 2025 (edited)
14,905
0
Never
18
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.45 KB | Housing | 0 0
  1. #include <ESP8266WiFi.h>
  2. #include <ESP8266WebServer.h>
  3. #include <ESP8266mDNS.h>
  4.  
  5. // Project: Remote PC Power Control via Relay & Resistor Voltage Divider State Monitoring
  6. // Hardware components:
  7. // 1. Controller: NodeMCU ESP8266 V3 (CH340)
  8. // 2. Relay: 1-Channel 3.3V Low-Level Trigger Relay Module
  9. // 3. Resistors: 1x 1k Ohm, 1x 2k Ohm (Voltage divider for PLED sensing)
  10. // 4. Power: Micro-USB (5V)
  11. // 5. Connection:
  12. //    - Power Control (Relay Output):
  13. //        Relay VCC -> NodeMCU VIN (5V) or 3V3
  14. //        Relay GND -> NodeMCU GND
  15. //        Relay IN  -> NodeMCU D1 (GPIO5)
  16. //        Relay COM & NO -> PC Motherboard POWER SW pins
  17. //    - Power LED Sense (Resistor Divider Input):
  18. //        PC Motherboard PLED (+) -> 1k Ohm Resistor -> NodeMCU D2 (GPIO4)
  19. //        NodeMCU D2 (GPIO4)      -> 2k Ohm Resistor -> NodeMCU GND & PLED (-)
  20. //
  21. // Schematic Diagram:
  22. //
  23. // [ NodeMCU ]                         [ Relay Module ]                  [ Motherboard F_PANEL ]
  24. //  Pin D1 (GPIO5) -------------------> IN
  25. //  VIN (5V) -------------------------> VCC
  26. //  GND ------------------------------> GND
  27. //                                      COM ---------------------------> Pin 7 (POWER SW +)
  28. //                                      NO ----------------------------> Pin 8 (POWER SW -)
  29. //
  30. // [ NodeMCU ]                         [ Voltage Divider ]               [ Motherboard F_PANEL ]
  31. //  Pin D2 (GPIO4) <----+-------------- [ 1kΩ Resistor ] --------------> Pin 1 (PLED +)
  32. //                      |
  33. //               [ 2kΩ Resistor ]
  34. //                      |
  35. //  GND ----------------+----------------------------------------------> Pin 3 (PLED -) / GND
  36. //
  37. // Features:
  38. // - Web-server for remote control via VPN
  39. // - Real-time power state sensing via resistor divider (5V to 3.3V)
  40. // - mDNS support (access via http://mainpc.local)
  41. // - Static IP configuration (192.168.0.133)
  42. // - Force Shutdown support (8s long press via /force)
  43. // - Physical FLASH button on board triggers the relay
  44. // - English UI with status indicator
  45. //
  46. // Hardware Notes:
  47. // - Relay module IN pin has built-in SMD resistors on PCB.
  48. // - The 1k/2k ohm divider steps motherboard 5V down to 3.3V safe for ESP8266 GPIO.
  49. // - Pin D2 uses standard INPUT logic. HIGH = PC ON, LOW = PC OFF.
  50.  
  51. // Your network config
  52. const char* ssid     = "WiFI_NAME_REGISTRY_DEPENDANT"; // Change this
  53. const char* password = "WiFI_PASSWORD";               // Change this
  54. const char* hostName = "mainpc";                      // Access in browser with mainpc.local
  55.  
  56. const int relayPin  = 5; // D1 (GPIO5) -> Relay IN Pin (Control)
  57. const int sensePin  = 4; // D2 (GPIO4) -> Resistor Divider Middle (PLED Sense)
  58. const int buttonPin = 0; // FLASH button (GPIO0)
  59. const int ledPin    = 2; // Onboard LED (D4)
  60.  
  61. // Static IP Configuration
  62. IPAddress local_IP(192, 168, 0, 133);
  63. IPAddress gateway(192, 168, 0, 1);
  64. IPAddress subnet(255, 255, 255, 0);
  65. IPAddress primaryDNS(192, 168, 0, 1);
  66.  
  67. // Durations in milliseconds
  68. const int standardPress = 500;
  69. const int forcePress    = 8000; // Force kill duration (8 sec)
  70.  
  71. ESP8266WebServer server(80);
  72.  
  73. void pressButton(int durationMs) {
  74.   Serial.print("Action: pressing relay for ");
  75.   Serial.print(durationMs);
  76.   Serial.println("ms");
  77.  
  78.   digitalWrite(ledPin, LOW);   // Onboard LED ON (active LOW)
  79.   digitalWrite(relayPin, LOW); // Relay ON (Low-Level Trigger)
  80.  
  81.   delay(durationMs);
  82.  
  83.   digitalWrite(relayPin, HIGH); // Relay OFF
  84.   digitalWrite(ledPin, HIGH);   // Onboard LED OFF
  85. }
  86.  
  87. // True if PC is ON (5V from PLED through divider yields HIGH on D2)
  88. bool isPcOn() {
  89.   return digitalRead(sensePin) == HIGH;
  90. }
  91.  
  92. void handleRoot() {
  93.   String html = "<html><head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1'>";
  94.   html += "<style>body{text-align:center;font-family:Arial,sans-serif;background:#f4f4f4;padding:20px;}";
  95.   html += "h1{display:flex;align-items:center;justify-content:center;margin-bottom:30px;color:#2c3e50;}";
  96.   html += ".indicator{display:inline-block;width:22px;height:22px;border-radius:50%;margin-left:15px;background:#2ecc71;box-shadow: 0 0 10px #2ecc71;transition: 0.3s;}";
  97.   html += ".btn{width:100%;max-width:300px;height:80px;font-size:20px;font-weight:bold;color:white;border:none;border-radius:15px;margin:15px 0;cursor:pointer;display:block;margin-left:auto;margin-right:auto;transition:0.2s;}";
  98.   html += ".btn:active{transform:scale(0.98);}";
  99.   html += ".on{background:#2ecc71;box-shadow:0 5px #27ae60;} .off{background:#e74c3c;box-shadow:0 5px #c0392b;}";
  100.   html += "p{color:#7f8c8d;margin-top:20px;font-size:14px;}</style>";
  101.   html += "<script>";
  102.   html += "setInterval(function(){ fetch('/status').then(r => r.text()).then(status => {";
  103.   html += "var ind = document.querySelector('.indicator');";
  104.   html += "if(status==='ON'){ind.style.background='#2ecc71'; ind.style.boxShadow='0 0 10px #2ecc71';}";
  105.   html += "else{ind.style.background='#e74c3c'; ind.style.boxShadow='0 0 10px #e74c3c';}";
  106.   html += "});}, 2000);";
  107.   html += "function safeToggle() { fetch('/status').then(r => r.text()).then(status => {";
  108.   html += "if (status === 'ON') { if (confirm('PC is already ON. Power action?')) location.href='/power'; }";
  109.   html += "else { location.href='/power'; }";
  110.   html += "});}";
  111.   html += "</script></head>";
  112.   html += "<body><h1>PC Remote <span class='indicator'></span></h1>";
  113.   html += "<button class='btn on' onclick='safeToggle()'>POWER ON/OFF</button>";
  114.   html += "<button class='btn off' onclick=\"if(confirm('Are you sure you want to FORCE SHUTDOWN?')){location.href='/force';}\">FORCE SHUTDOWN</button>";
  115.   html += "<p>Network name: http://" + String(hostName) + ".local</p></body></html>";
  116.  
  117.   server.send(200, "text/html", html);
  118. }
  119.  
  120. void handleStatus() {
  121.   server.send(200, "text/plain", isPcOn() ? "ON" : "OFF");
  122. }
  123.  
  124. void handlePower() {
  125.   server.send(200, "text/html", "<h1>OK! Standard press sent.</h1><script>setTimeout(function(){window.location.href='/';},2000);</script>");
  126.   pressButton(standardPress);
  127. }
  128.  
  129. void handleForce() {
  130.   server.send(200, "text/html", "<h1>OK! Force shutdown sent.</h1><script>setTimeout(function(){window.location.href='/';},4000);</script>");
  131.   pressButton(forcePress);
  132. }
  133.  
  134. void setup() {
  135.   Serial.begin(115200);
  136.  
  137.   pinMode(relayPin, OUTPUT);
  138.   pinMode(ledPin, OUTPUT);
  139.   pinMode(sensePin, INPUT); // Standard input mode for reading divider voltage
  140.   pinMode(buttonPin, INPUT_PULLUP);
  141.  
  142.   digitalWrite(relayPin, HIGH); // Keep Low-Level Relay OFF initially
  143.   digitalWrite(ledPin, HIGH);  // Onboard LED OFF initially
  144.  
  145.   if (!WiFi.config(local_IP, gateway, subnet, primaryDNS)) {
  146.     Serial.println("STA Failed to configure static IP!");
  147.   }
  148.  
  149.   WiFi.mode(WIFI_STA);
  150.   WiFi.begin(ssid, password);
  151.   while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  152.  
  153.   Serial.println("\nWiFi Connected!");
  154.   Serial.print("IP Address: ");
  155.   Serial.println(WiFi.localIP());
  156.  
  157.   if (MDNS.begin(hostName)) {
  158.     Serial.println("mDNS responder started: http://" + String(hostName) + ".local");
  159.   }
  160.  
  161.   server.on("/", handleRoot);
  162.   server.on("/status", handleStatus);
  163.   server.on("/power", handlePower);
  164.   server.on("/force", handleForce);
  165.   server.begin();
  166.  
  167.   MDNS.addService("http", "tcp", 80);
  168. }
  169.  
  170. void loop() {
  171.   MDNS.update();
  172.   server.handleClient();
  173.  
  174.   if (digitalRead(buttonPin) == LOW) {
  175.     delay(50);
  176.     if (digitalRead(buttonPin) == LOW) {
  177.       pressButton(standardPress);
  178.       while(digitalRead(buttonPin) == LOW) delay(10);
  179.     }
  180.   }
  181. }
Advertisement