// Captive Portal
#include <AsyncTCP.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>
#include <esp_wifi.h>
#include <HTTPClient.h>
#include "SPIFFS.h"
#include <stdio.h>
#include <string.h>
HTTPClient http;
const int qtdbotao = 2; // Quantidade de botões que o controle vai ter
String id[qtdbotao];
String message[qtdbotao];
const int botaoPin = 12; // Substitua pelo pino que você está usando para o botão 1
const int botaoPin1 = 13; // Substitua pelo pino que você está usando para o botão 2
const int led = 2; // Substitua pelo pino que você está usando para o led 1
const int tempoPressionado = 10000; //tempo para resetar o controle se o botão for segurado
String serverName = "http://192.168.91.236:3003/api/event/";
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
unsigned long previousMillis = 0;
const long interval = 120000;
String ssid_;
String pass_;
String id_;
String message_;
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
String wifiNetworks;
int scanIndex = 0;
const char *ssid = "controle openit";
const char *password = NULL; // no password
#define MAX_CLIENTS 4
#define WIFI_CHANNEL 6
const IPAddress localIP(4, 3, 2, 1);
const IPAddress gatewayIP(4, 3, 2, 1);
const IPAddress subnetMask(255, 255, 255, 0);
const String localIPURL = "http://4.3.2.1";
DNSServer dnsServer;
AsyncWebServer server(80);
void setUpDNSServer(DNSServer &dnsServer, const IPAddress &localIP) {
#define DNS_INTERVAL 30
dnsServer.setTTL(3600);
dnsServer.start(53, "*", localIP);
}
void initSPIFFS() {
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
}
Serial.println("SPIFFS mounted successfully");
}
// Read File from SPIFFS
String readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return String();
}
String fileContent;
while(file.available()){
fileContent = file.readStringUntil('\n');
break;
}
return fileContent;
}
// Write file to SPIFFS
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- write failed");
}
}
void handleScanRequest(AsyncWebServerRequest *request) {
// Retorna a lista de redes previamente escaneadas
request->send(200, "text/plain", wifiNetworks);
}
void sendQtdButtons(AsyncWebServerRequest *request) {
// Retorna a lista de redes previamente escaneadas
request->send(200, "text/plain", String(qtdbotao));
}
void scanWiFiNetworksTask(void *parameter) {
while (1) {
// Realiza a varredura das redes Wi-Fi
int numberOfNetworks = WiFi.scanNetworks();
Serial.println("Scanning Wi-Fi networks...");
wifiNetworks = ""; // Limpa a lista antes de adicionar novas redes
for (int i = 0; i < numberOfNetworks; i++) {
wifiNetworks += WiFi.SSID(i);
if (i < numberOfNetworks - 1) {
wifiNetworks += ",";
}
}
// Espera antes de iniciar uma nova varredura
vTaskDelay(60000 / portTICK_PERIOD_MS); // Aguarda 1 minuto antes de iniciar uma nova varredura
}
}
void startSoftAccessPoint(const char *ssid, const char *password, const IPAddress &localIP, const IPAddress &gatewayIP) {
// Define the maximum number of clients that can connect to the server
#define MAX_CLIENTS 4
// Define the WiFi channel to be used (channel 6 in this case)
#define WIFI_CHANNEL 6
// Set the WiFi mode to access point and station
WiFi.mode(WIFI_MODE_AP);
// Define the subnet mask for the WiFi network
const IPAddress subnetMask(255, 255, 255, 0);
// Configure the soft access point with a specific IP and subnet mask
WiFi.softAPConfig(localIP, gatewayIP, subnetMask);
// Start the soft access point with the given ssid, password, channel, max number of clients
WiFi.softAP(ssid, password, WIFI_CHANNEL, 0, MAX_CLIENTS);
// Disable AMPDU RX on the ESP32 WiFi to fix a bug on Android
esp_wifi_stop();
esp_wifi_deinit();
wifi_init_config_t my_config = WIFI_INIT_CONFIG_DEFAULT();
my_config.ampdu_rx_enable = false;
esp_wifi_init(&my_config);
esp_wifi_start();
vTaskDelay(100 / portTICK_PERIOD_MS); // Add a small delay
}
void setUpWebserver(AsyncWebServer &server, const IPAddress &localIP) {
//======================== Webserver ========================
// WARNING IOS (and maybe macos) WILL NOT POP UP IF IT CONTAINS THE WORD "Success" https://www.esp8266.com/viewtopic.php?f=34&t=4398
// SAFARI (IOS) IS STUPID, G-ZIPPED FILES CAN'T END IN .GZ https://github.com/homieiot/homie-esp8266/issues/476 this is fixed by the webserver serve static function.
// SAFARI (IOS) there is a 128KB limit to the size of the HTML. The HTML can reference external resources/images that bring the total over 128KB
// SAFARI (IOS) popup browser has some severe limitations (javascript disabled, cookies disabled)
// Required
server.on("/connecttest.txt", [](AsyncWebServerRequest *request) { request->redirect("http://logout.net"); }); // windows 11 captive portal workaround
server.on("/wpad.dat", [](AsyncWebServerRequest *request) { request->send(404); }); // Honestly don't understand what this is but a 404 stops win 10 keep calling this repeatedly and panicking the esp32 :)
server.on("/generate_204", [](AsyncWebServerRequest *request) { request->redirect(localIPURL); }); // android captive portal redirect
server.on("/redirect", [](AsyncWebServerRequest *request) { request->redirect(localIPURL); }); // microsoft redirect
server.on("/hotspot-detect.html", [](AsyncWebServerRequest *request) { request->redirect(localIPURL); }); // apple call home
server.on("/canonical.html", [](AsyncWebServerRequest *request) { request->redirect(localIPURL); }); // firefox captive portal call home
server.on("/success.txt", [](AsyncWebServerRequest *request) { request->send(200); }); // firefox captive portal call home
server.on("/ncsi.txt", [](AsyncWebServerRequest *request) { request->redirect(localIPURL); }); // windows call home
// server.on("/chrome-variations/seed",[](AsyncWebServerRequest *request){request->send(200);}); //chrome captive portal call home
// server.on("/service/update2/json",[](AsyncWebServerRequest *request){request->send(200);}); //firefox?
// server.on("/chat",[](AsyncWebServerRequest *request){request->send(404);}); //No stop asking Whatsapp, there is no internet connection
// server.on("/startpage",[](AsyncWebServerRequest *request){request->redirect(localIPURL);});
// return 404 to webpage icon
server.on("/favicon.ico", [](AsyncWebServerRequest *request) { request->send(404); }); // webpage icon
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/wifimanager.html", "text/html");
});
server.serveStatic("/", SPIFFS, "/");
// the catch all
server.onNotFound([](AsyncWebServerRequest *request) {
request->redirect(localIPURL);
Serial.print("onnotfound ");
Serial.print(request->host()); // This gives some insight into whatever was being requested on the serial monitor
Serial.print(" ");
Serial.print(request->url());
Serial.print(" sent redirect to " + localIPURL + "\n");
});
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isPost()){
Serial.println(p->name());
// HTTP POST ssid value
if (p->name() == PARAM_INPUT_1) {
ssid_ = p->value().c_str();
Serial.print("SSID Salvo como: ");
Serial.println(ssid_);
// Write file to save value
writeFile(SPIFFS, ssidPath, ssid_.c_str());
}
// HTTP POST pass value
if (p->name() == PARAM_INPUT_2) {
pass_ = p->value().c_str();
Serial.print("Senha Salva como: ");
Serial.println(pass_);
// Write file to save value
writeFile(SPIFFS, passPath, pass_.c_str());
}
// HTTP POST ip value
if (strncmp(p->name().c_str(), "id", 2) == 0) {
id_ = p->value().c_str();
Serial.print("ID Salvo como: ");
Serial.println(id_);
// Write file to save value
writeFile(SPIFFS, ("/" + std::string(p->name().c_str()) + ".txt").c_str(), id_.c_str());
}
//message1
// HTTP POST gateway value
if (strncmp(p->name().c_str(), "message", 2) == 0) {
message_ = p->value().c_str();
Serial.print("Gateway set to: ");
Serial.println(message_);
// Write file to save value
writeFile(SPIFFS, ("/" + std::string(p->name().c_str()) + ".txt").c_str(), message_.c_str());
}
}
}
request->send(200);
delay(3000);
ESP.restart();
});
server.on("/scan", HTTP_GET, handleScanRequest);
server.on("/qtd", HTTP_GET, sendQtdButtons);
}
void setup() {
pinMode(botaoPin, INPUT_PULLUP);
pinMode(botaoPin1, INPUT_PULLUP);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
Serial.setTxBufferSize(1024);
Serial.begin(115200);
while (!Serial)
;
// Print a welcome message to the Serial port.
Serial.println("\n\nControle MultiUso by OpenIt " __DATE__ " " __TIME__ "");
Serial.printf("%s-%d\n\r", ESP.getChipModel(), ESP.getChipRevision());
initSPIFFS();
ssid_ = readFile(SPIFFS, ssidPath);
pass_ = readFile(SPIFFS, passPath);
// id_ = readFile(SPIFFS, idPath);
// message_ = readFile (SPIFFS, messagePath);
for (int i = 0; i < qtdbotao; i++) {
id[i] = readFile(SPIFFS, ("/id" + String(i) + ".txt").c_str());
message[i] = readFile (SPIFFS, ("/message" + String(i) + ".txt").c_str());
}
Serial.println(ssid_);
Serial.println(pass_);
if(ssid_ == NULL || ssid_ == "" ){
startSoftAccessPoint(ssid, password, localIP, gatewayIP);
}
else{
WiFi.mode(WIFI_STA);
WiFi.begin(ssid_.c_str(), pass_.c_str());
Serial.println("Conectando no Wi-Fi...");
unsigned long currentMillis = millis();
previousMillis = currentMillis;
while(WiFi.status() != WL_CONNECTED) {
currentMillis = millis();
digitalWrite(led, !digitalRead(led));
if (digitalRead(botaoPin) == LOW) {
unsigned long inicioPressionado = millis();
while (digitalRead(botaoPin) == LOW) {
if (millis() - inicioPressionado >= tempoPressionado) {
Serial.println("Botão pressionado por 10 segundos! Resetando Configurações");
digitalWrite(led, HIGH);
writeFile(SPIFFS, ssidPath, NULL);
writeFile(SPIFFS, passPath, NULL);
ESP.restart();
while (digitalRead(botaoPin) == LOW) {
delay(10);
}
}
}
unsigned long tempoSegurando = millis() - inicioPressionado;
if (tempoSegurando < tempoPressionado) {
Serial.println("Botão clicado e solto!");
}
}
if (currentMillis - previousMillis >= interval) {
Serial.println("Falha ao conectar no Wi-Fi.");
ESP.restart();
break;
}
delay(1000);
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Nome da REDE: " + String(WiFi.SSID()));
Serial.print("IP Local: ");
Serial.println(WiFi.localIP());
Serial.print("ID do Controle: ");
Serial.println(id_);
digitalWrite(led, HIGH);
}
}
setUpDNSServer(dnsServer, localIP);
setUpWebserver(server, localIP);
server.begin();
if(ssid_ == NULL || ssid_ == "" ){
xTaskCreate(scanWiFiNetworksTask, "scanWiFiTask", 8192, NULL, 1, NULL);
}
}
void loop() {
dnsServer.processNextRequest();
delay(DNS_INTERVAL);
if (digitalRead(botaoPin) == LOW) {
unsigned long inicioPressionado = millis();
while (digitalRead(botaoPin) == LOW) {
if (millis() - inicioPressionado >= tempoPressionado) {
Serial.println("Botão pressionado por 10 segundos! Resetando Configurações");
digitalWrite(led, HIGH);
writeFile(SPIFFS, ssidPath, NULL);
writeFile(SPIFFS, passPath, NULL);
ESP.restart();
while (digitalRead(botaoPin) == LOW) {
delay(10);
}
}
}
unsigned long tempoSegurando = millis() - inicioPressionado;
if (tempoSegurando < tempoPressionado) {
Serial.print(serverName + id[0] + "/exec-event");
http.begin(serverName + id[0] + "/exec-event");
http.addHeader("Content-Type", "application/json");
int httpResponseCode = http.POST("{\"text_message\":\"teste\"}");
if (httpResponseCode>0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String payload = http.getString();
Serial.println(payload);
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
Serial.println("Botão clicado e solto!");
}
}
// if (digitalRead(botaoPin1) == HIGH) {
// Serial.print(serverName + id[1] + "/exec-event");
// http.begin(serverName + id[1] + "/exec-event");
// http.addHeader("Content-Type", "application/json");
// int httpResponseCode = http.POST("{\"text_message\":\"teste\"}");
// if (httpResponseCode>0) {
// Serial.print("HTTP Response code: ");
// Serial.println(httpResponseCode);
// String payload = http.getString();
// Serial.println(payload);
// }
// else {
// Serial.print("Error code: ");
// Serial.println(httpResponseCode);
// }
// // Free resources
// http.end();
// Serial.println("Botão clicado e solto!");
// }
}