Compare commits

...

6 Commits

Author SHA1 Message Date
ptrinchini 1f3292a329 Fix OTA password 3 weeks ago
ptrinchini 68c1406148 Non blocking 3 weeks ago
ptrinchini f6e61ffb7c OTA Update 3 weeks ago
ptrinchini 08e71b96e2 Changed input and output pin 2 months ago
ptrinchini 54bee65185 Internal pullup 2 months ago
ptrinchini b779613fc0 Fix libraries 2 months ago
  1. 3
      .vscode/extensions.json
  2. 19
      platformio.ini
  3. 8
      src/credentials.h
  4. 272
      src/main.cpp

3
.vscode/extensions.json

@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

19
platformio.ini

@ -8,10 +8,23 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
[platformio]
default_envs = com, ota
[env:com]
platform = espressif32
board = esp32dev
framework = arduino
upload_protocol = esptool
lib_deps =
knolleary/PubSubClient@^2.8
[env:ota]
platform = espressif32
board = esp32dev
framework = arduino
upload_protocol = espota
upload_port = 192.168.1.235
upload_flags = --auth=P@ssw0rd
lib_deps =
knolleary/PubSubClient@^2.8

8
src/credentials.h

@ -0,0 +1,8 @@
#define WLAN_SSID "TP-LINK-OUT"
#define WLAN_PASS "6JM54UG4EX"
#define MQTT_USER "admin"
#define MQTT_PASS "22pa0799"
#define OTA_USER "admin"
#define OTA_PASS "P@ssword"

272
src/main.cpp

@ -1,43 +1,61 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <PubSubClient.h>
#define WLAN_SSID "TP-LINK-RPT0"
#define WLAN_PASS "6JM54UG4EY"
#include <WiFi.h>
#include <ArduinoOTA.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include "credentials.h"
#define MQTT_CLID "ValveControl"
#define MQTT_HOST "192.168.1.133"
#define MQTT_HOST "192.168.1.146"
#define MQTT_PORT 1883
#define MQTT_USER "admin"
#define MQTT_PASS "22pa0799"
// Actuator state enum
enum ActuatorState {
IDLE,
OPENING,
CLOSING
};
void openValve();
void closeValve();
void stopValve();
void updateActuator();
void OnConnect();
void OnOpen();
void OnClose();
void OnStatus();
void OnRestart();
void OnNotFound();
String SendHTML(uint8_t ValveOpen);
String SendHTML();
String GetStatusJSON();
bool ValveOpen = false;
// Actuator control variables
ActuatorState currentState = IDLE;
ActuatorState desiredState = IDLE;
unsigned long operationStartTime = 0;
const unsigned long OPERATION_DURATION = 60000; // 60 seconds
int ButtonOpen = 14;
int ButtonClose = 12;
bool ValveState = LOW;
int MotorPin1 = 0;
int MotorPin2 = 2;
int duration = 10000;
int ButtonOpen = 16;
int ButtonClose = 19;
ESP8266WebServer server(80);
int MotorPin1 = 25; // estende il braccio
int MotorPin2 = 26; // ritrae il braccio
WebServer server(80);
WiFiClient espClient;
PubSubClient client(espClient);
void setup_ota() {
ArduinoOTA.setPassword(OTA_PASS);
ArduinoOTA.begin();
}
void setup_wifi() {
Serial.println();
Serial.print("Connecting to ");
@ -49,8 +67,10 @@ void setup_wifi() {
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Starting OTA...");
}
void onMessage(char* topic, byte* payload, unsigned int length) {
@ -96,6 +116,7 @@ void setup_http() {
server.on("/", OnConnect);
server.on("/open", OnOpen);
server.on("/close", OnClose);
server.on("/status", OnStatus);
server.on("/restart", OnRestart);
server.onNotFound(OnNotFound);
server.begin();
@ -109,19 +130,20 @@ void setup() {
delay(100);
}
pinMode(ButtonOpen, INPUT);
pinMode(ButtonClose, INPUT);
pinMode(ButtonOpen, INPUT_PULLUP);
pinMode(ButtonClose, INPUT_PULLUP);
pinMode(MotorPin1, OUTPUT);
pinMode(MotorPin2, OUTPUT);
setup_wifi();
setup_ota();
setup_mqtt();
setup_http();
}
void loop() {
ArduinoOTA.handle();
if (!client.connected()) {
reconnect();
@ -130,41 +152,42 @@ void loop() {
server.handleClient();
// Update actuator state machine (non-blocking)
updateActuator();
// Handle physical buttons
int buttonOpenState = digitalRead(ButtonOpen);
int buttonCloseState = digitalRead(ButtonClose);
if(buttonOpenState == LOW) {
buttonCloseState = !buttonOpenState;
digitalWrite(MotorPin1, LOW);
digitalWrite(MotorPin2, HIGH);
ValveOpen = true;
openValve();
}
if(buttonCloseState == LOW) {
buttonOpenState = !buttonCloseState;
digitalWrite(MotorPin1, HIGH);
digitalWrite(MotorPin2, LOW);
ValveOpen = false;
closeValve();
}
if(buttonOpenState == HIGH && buttonCloseState == HIGH) {
stopValve();
}
}
void OnConnect() {
server.send(200, "text/html", SendHTML(ValveOpen));
server.send(200, "text/html", SendHTML());
}
void OnOpen() {
if(currentState == IDLE) {
openValve();
server.send(200, "text/html", SendHTML(ValveOpen));
}
server.send(200, "application/json", GetStatusJSON());
}
void OnClose() {
if(currentState == IDLE) {
closeValve();
server.send(200, "text/html", SendHTML(ValveOpen));
}
server.send(200, "application/json", GetStatusJSON());
}
void OnStatus() {
server.send(200, "application/json", GetStatusJSON());
}
void OnRestart() {
@ -178,21 +201,23 @@ void OnNotFound(){
}
void openValve() {
if(currentState == IDLE) {
Serial.println("Opening valve...");
digitalWrite(MotorPin1, LOW);
digitalWrite(MotorPin2, HIGH);
delay(duration);
stopValve();
ValveOpen = HIGH;
currentState = OPENING;
operationStartTime = millis();
}
}
void closeValve() {
if(currentState == IDLE) {
Serial.println("Closing valve...");
digitalWrite(MotorPin1, HIGH);
digitalWrite(MotorPin2, LOW);
delay(duration);
stopValve();
ValveOpen = false;
currentState = CLOSING;
operationStartTime = millis();
}
}
void stopValve() {
@ -200,28 +225,157 @@ void stopValve() {
digitalWrite(MotorPin2, LOW);
}
String SendHTML(uint8_t ValveOpen) {
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>Linear Actuator</title>\n";
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr +=".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr +=".button-open {background-color: #1abc9c;}\n";
ptr +=".button-open:active {background-color: #16a085;}\n";
ptr +=".button-close {background-color: #34495e;}\n";
ptr +=".button-close:active {background-color: #2c3e50;}\n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
void updateActuator() {
// Check if operation is complete
if(currentState != IDLE) {
unsigned long elapsedTime = millis() - operationStartTime;
if(elapsedTime >= OPERATION_DURATION) {
// Operation complete
stopValve();
if(currentState == OPENING) {
ValveState = HIGH;
Serial.println("Valve fully open");
} else if(currentState == CLOSING) {
ValveState = LOW;
Serial.println("Valve fully closed");
}
currentState = IDLE;
}
}
}
String SendHTML() {
String ptr = "<!DOCTYPE html>\n";
ptr += "<html>\n";
ptr += "<head>\n";
ptr += " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr += " <title>Linear Actuator Control</title>\n";
ptr += " <style>\n";
ptr += " * { margin: 0; padding: 0; box-sizing: border-box; }\n";
ptr += " html { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; height: 100vh; }\n";
ptr += " body { display: flex; align-items: center; justify-content: center; width: 100%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0; }\n";
ptr += " .container { background: white; padding: 40px; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); text-align: center; max-width: 400px; }\n";
ptr += " h1 { color: #333; margin-bottom: 30px; font-size: 28px; }\n";
ptr += " .status-display { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 30px; border-left: 4px solid #667eea; }\n";
ptr += " .status-text { font-size: 18px; color: #555; margin-bottom: 10px; }\n";
ptr += " .status-value { font-size: 24px; font-weight: bold; color: #667eea; }\n";
ptr += " .progress-bar { width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden; margin-top: 10px; display: none; }\n";
ptr += " .progress-bar.active { display: block; }\n";
ptr += " .progress-fill { height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); width: 0%; transition: width 0.2s ease; }\n";
ptr += " .button { display: inline-block; padding: 15px 40px; font-size: 18px; font-weight: bold; border: none; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; color: white; margin-top: 20px; width: 100%; }\n";
ptr += " .button.open { background: linear-gradient(135deg, #1abc9c 0%, #16a085 100%); }\n";
ptr += " .button.open:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(26, 188, 156, 0.3); }\n";
ptr += " .button.close { background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%); }\n";
ptr += " .button.close:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(231, 76, 60, 0.3); }\n";
ptr += " .button:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }\n";
ptr += " .info-text { font-size: 12px; color: #999; margin-top: 15px; }\n";
ptr += " </style>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += " <div class=\"container\">\n";
ptr += " <h1>Linear Actuator</h1>\n";
if(ValveOpen) {
ptr +="<p>Valve is OPEN</p><a class=\"button button-close\" href=\"/close\">CLOSE</a>\n";
} else {
ptr +="<p>Valve is CLOSED</p><a class=\"button button-open\" href=\"/open\">OPEN</a>\n";
}
ptr += " <div class=\"status-display\">\n";
ptr += " <div class=\"status-text\">Current Status</div>\n";
ptr += " <div class=\"status-value\" id=\"status\">CLOSED</div>\n";
ptr += " <div class=\"progress-bar\" id=\"progressBar\">\n";
ptr += " <div class=\"progress-fill\" id=\"progressFill\"></div>\n";
ptr += " </div>\n";
ptr += " </div>\n";
ptr += " <button class=\"button open\" id=\"actionButton\" onclick=\"handleAction()\">OPEN</button>\n";
ptr += " <div class=\"info-text\">Operation time: <span id=\"opTime\">0</span>s / 60s</div>\n";
ptr += " </div>\n";
ptr += "\n";
ptr += " <script>\n";
ptr += " let isOperating = false;\n";
ptr += " let statusCheckInterval;\n";
ptr += "\n";
ptr += " function updateStatus() {\n";
ptr += " fetch('/status')\n";
ptr += " .then(response => response.json())\n";
ptr += " .then(data => {\n";
ptr += " const statusEl = document.getElementById('status');\n";
ptr += " const btnEl = document.getElementById('actionButton');\n";
ptr += " const progBar = document.getElementById('progressBar');\n";
ptr += " const progFill = document.getElementById('progressFill');\n";
ptr += " const opTimeEl = document.getElementById('opTime');\n";
ptr += "\n";
ptr += " if(data.state === 'OPENING' || data.state === 'CLOSING') {\n";
ptr += " isOperating = true;\n";
ptr += " btnEl.disabled = true;\n";
ptr += " progBar.classList.add('active');\n";
ptr += " statusEl.textContent = data.state;\n";
ptr += " const progress = (data.elapsedTime / data.totalTime) * 100;\n";
ptr += " progFill.style.width = progress + '%';\n";
ptr += " opTimeEl.textContent = Math.round(data.elapsedTime / 1000);\n";
ptr += " } else {\n";
ptr += " isOperating = false;\n";
ptr += " btnEl.disabled = false;\n";
ptr += " progBar.classList.remove('active');\n";
ptr += " progFill.style.width = '0%';\n";
ptr += " opTimeEl.textContent = '0';\n";
ptr += " \n";
ptr += " if(data.valveState === 1) {\n";
ptr += " statusEl.textContent = 'OPEN';\n";
ptr += " btnEl.textContent = 'CLOSE';\n";
ptr += " btnEl.className = 'button close';\n";
ptr += " } else {\n";
ptr += " statusEl.textContent = 'CLOSED';\n";
ptr += " btnEl.textContent = 'OPEN';\n";
ptr += " btnEl.className = 'button open';\n";
ptr += " }\n";
ptr += " }\n";
ptr += " })\n";
ptr += " .catch(err => console.error('Status update failed:', err));\n";
ptr += " }\n";
ptr += "\n";
ptr += " function handleAction() {\n";
ptr += " if(isOperating) return;\n";
ptr += " \n";
ptr += " const btnEl = document.getElementById('actionButton');\n";
ptr += " const endpoint = btnEl.textContent === 'OPEN' ? '/open' : '/close';\n";
ptr += " \n";
ptr += " fetch(endpoint)\n";
ptr += " .then(() => {\n";
ptr += " isOperating = true;\n";
ptr += " btnEl.disabled = true;\n";
ptr += " updateStatus();\n";
ptr += " })\n";
ptr += " .catch(err => console.error('Action failed:', err));\n";
ptr += " }\n";
ptr += "\n";
ptr += " // Initial status check\n";
ptr += " updateStatus();\n";
ptr += "\n";
ptr += " // Poll status every 250ms when operating, every 1s when idle\n";
ptr += " statusCheckInterval = setInterval(() => {\n";
ptr += " updateStatus();\n";
ptr += " }, 250);\n";
ptr += " </script>\n";
ptr += "</body>\n";
ptr += "</html>\n";
return ptr;
}
String GetStatusJSON() {
String json = "{"
"\"state\":\"";
if(currentState == OPENING) {
json += "OPENING";
} else if(currentState == CLOSING) {
json += "CLOSING";
} else {
json += "IDLE";
}
json += "\",";
json += "\"valveState\":" + String(ValveState ? 1 : 0) + ",";
json += "\"elapsedTime\":" + String(millis() - operationStartTime) + ",";
json += "\"totalTime\":" + String(OPERATION_DURATION);
json += "}";
return json;
}

Loading…
Cancel
Save