Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save wdmtech/703f5dd0ca0907ea9ca11b1b32c79f74 to your computer and use it in GitHub Desktop.
Save wdmtech/703f5dd0ca0907ea9ca11b1b32c79f74 to your computer and use it in GitHub Desktop.
// Import required libraries
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <EEPROM.h>
HTTPClient client;
// Set up password-less WiFi hotspot
const char *ap_ssid = "CheerBird";
const char *ap_password = "";
// WiFi connection credentials will be set by the user
String sta_ssid = "";
String sta_password = "";
// Write string to flash storage
void EEPROM_ESP8266_WRITE(String buffer, int N)
{
EEPROM.begin(512);
delay(10);
for (int L = 0; L < 32; ++L)
{
EEPROM.write(N + L, buffer[L]);
}
EEPROM.commit();
}
// Read string from flash storage
String EEPROM_ESP8266_READ(int min, int max)
{
EEPROM.begin(512);
delay(10);
String buffer;
for (int L = min; L < max; ++L)
if (isAlphaNumeric(EEPROM.read(L)))
buffer += char(EEPROM.read(L));
return buffer;
}
// Clear flash storage
void EEPROM_ESP8266_CLEAR()
{
EEPROM.begin(512);
// Write a 0 to all 512 bytes of the EEPROM
for (int i = 0; i < 512; i++)
{
EEPROM.write(i, 0);
}
EEPROM.end();
}
// Pin for flash reset button
const int resetFlashInterruptPin = D4; //D4 (gpio4)
int resetFlashInterruptPinState = 0;
//variable interruptCounter will be used in the interrupt service routine. Thus, it needs to be declared as "volatile"
volatile boolean interruptFlag = 0;
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🐦</text></svg>">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h1 { font-size: 9.0rem; }
p { font-size: 1.0rem; }
.units { font-size: 1.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
</style>
</head>
<body>
<h1 id="title" style="color: %COLOR%; text-shadow: 0px 0px 13px rgba(150, 150, 150, 1);">CheerBird 🐦</h1>
<h2 id="connectivity"></h2>
<form action="/save" id="saveCredentialsForm">
<label>
Network name
<input type="text" id="ssid" name="ssid" maxlength="32" value="">
</label>
<label>
Password
<input type="password" id="password" name="password" maxlength="32" value="">
</label>
<input type="submit" value="Save">
</form>
<div>
<h3>Reset</h3>
<button onclick="reset(event)">Reset your CheerBird to default settings</button>
</div>
</body>
<script>
window.addEventListener( "load", function () {
function sendCredentials() {
const XHR = new XMLHttpRequest();
// Bind the FormData object and the form element
const FD = new FormData( form );
// Define what happens on successful data submission
XHR.addEventListener( "load", function(event) {
console.log( event.target.responseText );
} );
// Define what happens in case of error
XHR.addEventListener( "error", function( event ) {
console.log( 'Oops! Something went wrong.' );
} );
// Set up our request
XHR.open( "POST", "/save-credentials" );
// The data sent is what the user provided in the form
XHR.send( FD );
}
// Access the form element...
const form = document.getElementById( "saveCredentialsForm" );
// ...and take over its submit event.
form.addEventListener( "submit", function ( event ) {
event.preventDefault();
sendCredentials();
} );
} );
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("connectivity").innerHTML = "Connected to " + this.responseText;
} else {
document.getElementById("connectivity").innerHTML = "Not connected to WiFi";
}
};
xhttp.open("GET", "/connectivity", true);
xhttp.send();
}, 10000 ) ;
function reset(event) {
console.log(event)
var XHR = new XMLHttpRequest();
XHR.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
}
};
XHR.open("GET", "/reset", true);
XHR.send();
}
</script>
</html>
)rawliteral";
// Replaces placeholders in HTML via %VARNAME%
String processor(const String &var)
{
return String();
}
void ICACHE_RAM_ATTR clearEEPROMInterrupt()
{
EEPROM_ESP8266_WRITE("", 0);
EEPROM_ESP8266_WRITE("", 32);
ESP.restart();
}
void setup()
{
// Serial port for debugging purposes
Serial.begin(115200);
// Serial.setDebugOutput(true);
WiFi.mode(WIFI_AP_STA);
pinMode(resetFlashInterruptPin, INPUT_PULLDOWN_16);
attachInterrupt(digitalPinToInterrupt(resetFlashInterruptPin), clearEEPROMInterrupt, FALLING);
Serial.println();
Serial.println("EEPROM");
Serial.println("ssid: " + EEPROM_ESP8266_READ(0, 32));
Serial.println("password: " + EEPROM_ESP8266_READ(32, 64));
Serial.println("/EEPROM");
Serial.println();
for (uint8_t t = 4; t > 0; t--)
{
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
Serial.print("Setting AP (Access Point)…");
// Remove the password parameter, if you want the AP (Access Point) to be open
// WiFi.softAPConfig(Ip, Ip, NMask);
WiFi.softAP(ap_ssid, ap_password);
Serial.println("Wait 100 ms for AP_START...");
delay(100);
IPAddress softIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(softIP);
// Get credentials from EEPROM if they exist
if (!EEPROM_ESP8266_READ(0, 32).isEmpty() && !EEPROM_ESP8266_READ(32, 64).isEmpty())
{
sta_ssid = EEPROM_ESP8266_READ(0, 32);
sta_password = EEPROM_ESP8266_READ(32, 64);
// Forget the saved WiFi credentials, we will use the NEW ones provided by the user
WiFi.disconnect();
WiFi.begin(sta_ssid, sta_password);
Serial.println();
Serial.print("Connecting to ");
Serial.println(sta_ssid);
while (WiFi.waitForConnectResult() != WL_CONNECTED)
{
delay(500);
if (WL_NO_SSID_AVAIL)
{
Serial.println("Bad SSID");
break;
};
if (WL_CONNECT_FAILED)
{
Serial.println("Bad password");
break;
};
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
}
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html, processor);
});
server.on("/save-credentials", HTTP_POST, [](AsyncWebServerRequest *request) {
// Save WiFi creds
// request->send_P(200, "text/plain", String(t).c_str());
int args = request->args();
for (int i = 0; i < args; i++)
{
Serial.printf("ARG[%s]: %s\n", request->argName(i).c_str(), request->arg(i).c_str());
}
if (request->hasParam("ssid", true) && request->hasParam("password", true))
{
AsyncWebParameter *ssid = request->getParam("ssid", true);
AsyncWebParameter *password = request->getParam("password", true);
String ssidString = ssid->value();
String passwordString = password->value();
if (!ssidString.isEmpty() && !passwordString.isEmpty())
{
EEPROM_ESP8266_WRITE(ssidString, 0); //Primero de 0 al 32, del 32 al 64, etc
EEPROM_ESP8266_WRITE(passwordString, 32); //SAVE
request->send_P(200, "text/plain", String("Credentials saved, your CheerBird will restart and connect to the network details you just provided").c_str());
}
else
{
request->send_P(200, "text/plain", String("Invalid credentials").c_str());
}
}
});
server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request) {
// Reset device to factory settings
EEPROM_ESP8266_CLEAR();
request->send_P(200, "text/plain", String("Your CheerBird will now restart").c_str());
delay(5000);
ESP.reset();
});
server.on("/connectivity", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/plain", String(sta_ssid).c_str());
});
// Start server
server.begin();
}
void loop()
{
// wait for WiFi connection and continuously poll a URL
if (WiFi.status() == WL_CONNECTED)
{
if (displayMode == "mode-cheerlights")
{
HTTPClient http;
Serial.print("[HTTP] begin...\n");
// this URL just returns a color hex code (see https://cheerlights.com)
client.begin("http://api.thingspeak.com/channels/1417/field/2/last.txt"); //HTTP
Serial.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = client.GET();
// httpCode will be negative on error
if (httpCode > 0)
{
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// good response
if (httpCode == HTTP_CODE_OK)
{
String payload = client.getString();
Serial.print("hex: ");
Serial.println(payload);
// Do stuff, e.g. change color of RGB LED
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\n", client.errorToString(httpCode).c_str());
}
client.end();
}
}
delay(15000);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment