mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-30 16:24:09 +03:00
Merge branch 'master' into master
This commit is contained in:
@ -349,6 +349,20 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
}
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::end() {
|
||||
_initialized = false;
|
||||
_udp_ota->unref();
|
||||
_udp_ota = 0;
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if(_useMDNS){
|
||||
MDNS.end();
|
||||
}
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server stopped.\n");
|
||||
#endif
|
||||
}
|
||||
//this needs to be called in the loop()
|
||||
void ArduinoOTAClass::handle() {
|
||||
if (_state == OTA_RUNUPDATE) {
|
||||
|
@ -62,6 +62,8 @@ class ArduinoOTAClass
|
||||
//Starts the ArduinoOTA service
|
||||
void begin(bool useMDNS = true);
|
||||
|
||||
//Ends the ArduinoOTA service
|
||||
void end();
|
||||
//Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
|
||||
void handle();
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -39,7 +39,7 @@ void setup() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -14,7 +14,7 @@ const char* host = "OTA-LEDS";
|
||||
|
||||
int led_pin = 13;
|
||||
#define N_DIMMERS 3
|
||||
int dimmer_pin[] = {14, 5, 15};
|
||||
int dimmer_pin[] = { 14, 5, 15 };
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@ -45,14 +45,14 @@ void setup() {
|
||||
}
|
||||
|
||||
ArduinoOTA.setHostname(host);
|
||||
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
|
||||
ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade
|
||||
for (int i = 0; i < N_DIMMERS; i++) {
|
||||
analogWrite(dimmer_pin[i], 0);
|
||||
}
|
||||
analogWrite(led_pin, 0);
|
||||
});
|
||||
|
||||
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
|
||||
ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end
|
||||
for (int i = 0; i < 30; i++) {
|
||||
analogWrite(led_pin, (i * 100) % 1001);
|
||||
delay(50);
|
||||
@ -67,7 +67,6 @@ void setup() {
|
||||
/* setup the OTA server */
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
@ -20,7 +20,7 @@
|
||||
/* Set these to your desired softAP credentials. They are not configurable at runtime */
|
||||
#ifndef APSSID
|
||||
#define APSSID "ESP_ap"
|
||||
#define APPSK "12345678"
|
||||
#define APPSK "12345678"
|
||||
#endif
|
||||
|
||||
const char *softAP_ssid = APSSID;
|
||||
@ -62,7 +62,7 @@ void setup() {
|
||||
/* You can remove the password parameter if you want the AP to be open. */
|
||||
WiFi.softAPConfig(apIP, apIP, netMsk);
|
||||
WiFi.softAP(softAP_ssid, softAP_password);
|
||||
delay(500); // Without delay I've seen the IP address blank
|
||||
delay(500); // Without delay I've seen the IP address blank
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
@ -74,13 +74,13 @@ void setup() {
|
||||
server.on("/", handleRoot);
|
||||
server.on("/wifi", handleWifi);
|
||||
server.on("/wifisave", handleWifiSave);
|
||||
server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin(); // Web server start
|
||||
server.begin(); // Web server start
|
||||
Serial.println("HTTP server started");
|
||||
loadCredentials(); // Load WLAN credentials from network
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||
loadCredentials(); // Load WLAN credentials from network
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
@ -106,7 +106,7 @@ void loop() {
|
||||
/* Don't set retry time too low as retry interfere the softAP operation */
|
||||
connect = true;
|
||||
}
|
||||
if (status != s) { // WLAN status change
|
||||
if (status != s) { // WLAN status change
|
||||
Serial.print("Status: ");
|
||||
Serial.println(s);
|
||||
status = s;
|
||||
@ -130,13 +130,11 @@ void loop() {
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
||||
if (s == WL_CONNECTED) {
|
||||
MDNS.update();
|
||||
}
|
||||
if (s == WL_CONNECTED) { MDNS.update(); }
|
||||
}
|
||||
// Do work:
|
||||
//DNS
|
||||
// DNS
|
||||
dnsServer.processNextRequest();
|
||||
//HTTP
|
||||
// HTTP
|
||||
server.handleClient();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/** Handle root or redirect to captive portal */
|
||||
void handleRoot() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||
return;
|
||||
}
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
@ -8,8 +8,7 @@ void handleRoot() {
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F(
|
||||
"<!DOCTYPE html><html lang='en'><head>"
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>HELLO WORLD!!</h1>");
|
||||
@ -18,8 +17,7 @@ void handleRoot() {
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += F(
|
||||
"<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
Page += F("<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
"</body></html>");
|
||||
|
||||
server.send(200, "text/html", Page);
|
||||
@ -30,8 +28,8 @@ boolean captivePortal() {
|
||||
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
|
||||
Serial.println("Request redirected to captive portal");
|
||||
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -44,8 +42,7 @@ void handleWifi() {
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
||||
String Page;
|
||||
Page += F(
|
||||
"<!DOCTYPE html><html lang='en'><head>"
|
||||
Page += F("<!DOCTYPE html><html lang='en'><head>"
|
||||
"<meta name='viewport' content='width=device-width'>"
|
||||
"<title>CaptivePortal</title></head><body>"
|
||||
"<h1>Wifi config</h1>");
|
||||
@ -54,40 +51,31 @@ void handleWifi() {
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page +=
|
||||
String(F(
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>SoftAP config</th></tr>"
|
||||
"<tr><td>SSID ")) +
|
||||
String(softAP_ssid) +
|
||||
F("</td></tr>"
|
||||
"<tr><td>IP ") +
|
||||
toStringIp(WiFi.softAPIP()) +
|
||||
F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN config</th></tr>"
|
||||
"<tr><td>SSID ") +
|
||||
String(ssid) +
|
||||
F("</td></tr>"
|
||||
"<tr><td>IP ") +
|
||||
toStringIp(WiFi.localIP()) +
|
||||
F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
|
||||
Page += String(F("\r\n<br />"
|
||||
"<table><tr><th align='left'>SoftAP config</th></tr>"
|
||||
"<tr><td>SSID "))
|
||||
+ String(softAP_ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.softAPIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN config</th></tr>"
|
||||
"<tr><td>SSID ")
|
||||
+ String(ssid) + F("</td></tr>"
|
||||
"<tr><td>IP ")
|
||||
+ toStringIp(WiFi.localIP()) + F("</td></tr>"
|
||||
"</table>"
|
||||
"\r\n<br />"
|
||||
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
|
||||
Serial.println("scan start");
|
||||
int n = WiFi.scanNetworks();
|
||||
Serial.println("scan done");
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>");
|
||||
}
|
||||
for (int i = 0; i < n; i++) { Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>"); }
|
||||
} else {
|
||||
Page += F("<tr><td>No WLAN found</td></tr>");
|
||||
}
|
||||
Page += F(
|
||||
"</table>"
|
||||
Page += F("</table>"
|
||||
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
|
||||
"<input type='text' placeholder='network' name='n'/>"
|
||||
"<br /><input type='password' placeholder='password' name='p'/>"
|
||||
@ -95,7 +83,7 @@ void handleWifi() {
|
||||
"<p>You may want to <a href='/'>return to the home page</a>.</p>"
|
||||
"</body></html>");
|
||||
server.send(200, "text/html", Page);
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
}
|
||||
|
||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
||||
@ -107,14 +95,14 @@ void handleWifiSave() {
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
saveCredentials();
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
|
||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
|
||||
return;
|
||||
}
|
||||
String message = F("File Not Found\n\n");
|
||||
@ -126,9 +114,7 @@ void handleNotFound() {
|
||||
message += server.args();
|
||||
message += F("\n");
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n");
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); }
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
|
@ -2,9 +2,7 @@
|
||||
boolean isIp(String str) {
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c != '.' && (c < '0' || c > '9')) {
|
||||
return false;
|
||||
}
|
||||
if (c != '.' && (c < '0' || c > '9')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -12,10 +10,7 @@ boolean isIp(String str) {
|
||||
/** IP to String? */
|
||||
String toStringIp(IPAddress ip) {
|
||||
String res = "";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
res += String((ip >> (8 * i)) & 0xFF) + ".";
|
||||
}
|
||||
for (int i = 0; i < 3; i++) { res += String((ip >> (8 * i)) & 0xFF) + "."; }
|
||||
res += String(((ip >> 8 * 3)) & 0xFF);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,390 @@
|
||||
/*
|
||||
This example shows the use of the 'DNS forwarder' feature in the DNSServer.
|
||||
It does so by combining two examples CaptivePortalAdvanced and
|
||||
RangeExtender-NAPT. Additionally the CaptivePortalAdvanced part has a few
|
||||
upgrades to the HTML presentation to improve readability and ease of use on
|
||||
mobile devices.
|
||||
|
||||
Also for an example of using HTML chunked response, see handleWifi() in
|
||||
handleHttp.ino.
|
||||
|
||||
This example starts up in Captive Portal mode by default.
|
||||
It starts the SoftAP and NAPT w/o connecting the WLAN side.
|
||||
|
||||
You connect your computer or mobile device to the WiFi Network 'MagicPortal'
|
||||
password 'ShowTime'. Your device should shortly notify you of a Captive
|
||||
Portal and the need to login. If it fails to do so in a timely maner,
|
||||
navigate to http://172.217.28.1/wifi and configure it there.
|
||||
|
||||
Note, until a successful WLAN connection is made all DNS lookups will point
|
||||
back to the SoftAP at 172.217.28.1. This is the Captive Portal element of
|
||||
this example.
|
||||
|
||||
Once the WLAN is connected, your device should notify you that you are
|
||||
connected. This, of course, assumes your WLAN connection has a path to the
|
||||
Internet.
|
||||
|
||||
At this stage we are no longer running as a Captive Portal, but a regular
|
||||
NAPT. The DNSServer will be running with the DNS forwarder enabled. The
|
||||
DNSServer will resolve lookups for 'margicportal' to point to 172.217.28.1
|
||||
and all other lookup request will be forwarded to the 1st DNS server that was
|
||||
in the DHCP response for the WLAN interface.
|
||||
|
||||
You should now be able to access things on the Internet. The ease of access
|
||||
to devices on your home Network may vary. By IP address it should work.
|
||||
Access by a hostname - maybe. Some home routers will use the hostname
|
||||
supplied during DHCP to support a local DNS table; some do not.
|
||||
|
||||
There is an additional possible complication for using the local DNS, the DNS
|
||||
suffix list, this subject is seldom discussed. It is normally handled
|
||||
automaticly by the host computers DNS lookup code. For the DHCP case, the
|
||||
DHCP server will supply a suffix list, if there is one. Then when a name
|
||||
lookup fails and the name does not have a trailing (.)dot the host computer
|
||||
will append a suffix from the list and try again, until successful or the
|
||||
list is exhaused. This topic I fear can become a TL;DR. A quick wrapup by way
|
||||
of an example. On an Ubuntu system run `nmcli dev show eth0 | grep
|
||||
IP4\.DOMAIN` that may show you a suffix list. (replace eth0 with your wlan
|
||||
interface name) Try adding them to the local name you are failing to connect
|
||||
to. For example, assume 'myhost' fails. You see that 'lan' is in the suffix
|
||||
list. Try connecting to 'myhost.lan'.
|
||||
|
||||
mDNS names also will not work. We do not have a way to pass those request
|
||||
back and forth through the NAPT.
|
||||
|
||||
Note if hostnames are going to work for an ESP8266 device on your home
|
||||
Network, you have to have the call to WiFi.hostname(...) before you call
|
||||
WiFi.begin().
|
||||
|
||||
In this example the SoftAP in 'Captive Portal' uses the same public address
|
||||
that was used in the CaptivePortalAdvanced example. Depending on your devices
|
||||
you may or may not be successful in using a private address. A previous
|
||||
PR-author discovered a fix that made the CaptivePortalAdvanced example work
|
||||
better with Android devices. That fix was to use that public address. At this
|
||||
time, this PR-author with a different Android device running the latest
|
||||
version of Android has seen no problems in using either. At least not yet :)
|
||||
FWIW: My device also works with the original CaptivePortalAdvanced example
|
||||
when using a private address. I would suggest keeping the public address
|
||||
for a while. At lest until you are confident everything is working well
|
||||
before experimenting with a private address.
|
||||
*/
|
||||
|
||||
|
||||
#if LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <lwip/napt.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
#define NAPT 1000
|
||||
#define NAPT_PORT 10
|
||||
|
||||
/*
|
||||
Some defines for debugging
|
||||
*/
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define CONSOLE DEBUG_ESP_PORT
|
||||
#else
|
||||
#define CONSOLE Serial
|
||||
#endif
|
||||
|
||||
#define _PRINTF(a, ...) printf_P(PSTR(a), ##__VA_ARGS__)
|
||||
#define _PRINT(a) print(String(F(a)))
|
||||
#define _PRINTLN(a) println(String(F(a)))
|
||||
#define _PRINTLN2(a, b) println(String(F(a)) + b)
|
||||
|
||||
#define CONSOLE_PRINTF CONSOLE._PRINTF
|
||||
#define CONSOLE_PRINT CONSOLE._PRINT
|
||||
#define CONSOLE_PRINTLN CONSOLE._PRINTLN
|
||||
#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2
|
||||
|
||||
#ifdef DEBUG_SKETCH
|
||||
#define DEBUG_PRINTF CONSOLE_PRINTF
|
||||
#define DEBUG_PRINT CONSOLE_PRINT
|
||||
#define DEBUG_PRINTLN CONSOLE_PRINTLN
|
||||
#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2
|
||||
|
||||
#else
|
||||
#define DEBUG_PRINTF(...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define DEBUG_PRINT(...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define DEBUG_PRINTLN(...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define DEBUG_PRINTLN2(...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Set these to your desired softAP credentials. They are not configurable at runtime */
|
||||
#ifndef APSSID
|
||||
#define APSSID "MagicPortal"
|
||||
#define APPSK "ShowTime"
|
||||
#endif
|
||||
|
||||
const char *softAP_ssid = APSSID;
|
||||
const char *softAP_password = APPSK;
|
||||
|
||||
/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */
|
||||
const char *myHostname = "magicportal";
|
||||
|
||||
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
|
||||
char ssid[33] = "";
|
||||
char password[65] = "";
|
||||
uint8_t bssid[6];
|
||||
WiFiEventHandler staModeConnectedHandler;
|
||||
WiFiEventHandler staModeDisconnectedHandler;
|
||||
|
||||
// DNS server
|
||||
DNSServer dnsServer;
|
||||
|
||||
// Web server
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
/* Soft AP network parameters */
|
||||
IPAddress apIP(172, 217, 28, 1);
|
||||
IPAddress netMsk(255, 255, 255, 0);
|
||||
|
||||
|
||||
/** Should I connect to WLAN asap? */
|
||||
bool connect = false;
|
||||
|
||||
/** Set to true to start WiFi STA at setup time when credentials loaded successfuly from EEPROM */
|
||||
/** Set to false to defer WiFi STA until configured through web interface. */
|
||||
bool staReady = false; // Don't connect right away
|
||||
|
||||
/** Last time I tried to connect to WLAN */
|
||||
unsigned long lastConnectTry = 0;
|
||||
|
||||
/** Current WLAN status */
|
||||
unsigned int status = WL_IDLE_STATUS;
|
||||
|
||||
void setup() {
|
||||
WiFi.persistent(false); // w/o this a flash write occurs at every boot
|
||||
WiFi.mode(WIFI_OFF); // Prevent use of SDK stored credentials
|
||||
CONSOLE.begin(115200);
|
||||
CONSOLE_PRINTLN("\r\n\r\nNAPT with Configuration Portal ...");
|
||||
|
||||
staModeConnectedHandler = WiFi.onStationModeConnected(
|
||||
[](const WiFiEventStationModeConnected &data) {
|
||||
// Keep a copy of the BSSID for the AP that WLAN connects to.
|
||||
// This is used in the WLAN report on WiFi Details page.
|
||||
memcpy(bssid, data.bssid, sizeof(bssid));
|
||||
});
|
||||
|
||||
staModeDisconnectedHandler = WiFi.onStationModeDisconnected(
|
||||
[](const WiFiEventStationModeDisconnected &) {
|
||||
if (dnsServer.isForwarding()) {
|
||||
dnsServer.disableForwarder("*");
|
||||
dnsServer.setTTL(0);
|
||||
// Reminder, Serial.println() will not work from these callbacks.
|
||||
// For debug printf use ets_uart_printf().
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
While you can remove the password parameter to make the AP open.
|
||||
You will be operating with less security and allowing snoopers to see
|
||||
the credentials you use for your WiFi.
|
||||
*/
|
||||
WiFi.softAPConfig(apIP, apIP, netMsk);
|
||||
WiFi.softAP(softAP_ssid, softAP_password);
|
||||
// The following comment for delay(500) was committed Aug 19, 2015; is it
|
||||
// still true? Commented out for verification. - APR 2020
|
||||
// delay(500); // Without delay I've seen the IP address blank
|
||||
CONSOLE_PRINTF("SoftAP '%s' started\r\n", softAP_ssid);
|
||||
CONSOLE_PRINTLN2(" IP address: ", WiFi.softAPIP().toString());
|
||||
|
||||
/* Captive portals will usually use a TTL of 0 to avoid DNS cache poisoning. */
|
||||
dnsServer.setTTL(0);
|
||||
|
||||
/* Setup the DNS server redirecting all the domains to the apIP */
|
||||
dnsServer.start(IANA_DNS_PORT, "*", apIP);
|
||||
CONSOLE_PRINTLN("DNSServer started:");
|
||||
CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off");
|
||||
CONSOLE_PRINTF(" Resolve all domain lookups, '%s', to this AP's IP address, '%s' %s.\r\n",
|
||||
dnsServer.getDomainName().c_str(),
|
||||
softAP_ssid,
|
||||
WiFi.softAPIP().toString().c_str());
|
||||
CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL());
|
||||
|
||||
/*
|
||||
Do some NAPT startup stuff
|
||||
*/
|
||||
CONSOLE_PRINTLN("Begin NAPT initialization:");
|
||||
CONSOLE_PRINTF(" Heap before NAPT init: %d\r\n", ESP.getFreeHeap());
|
||||
|
||||
err_t ret = ip_napt_init(NAPT, NAPT_PORT);
|
||||
CONSOLE_PRINTF(" ip_napt_init(%d,%d): ret=%d (OK=%d)\r\n", NAPT, NAPT_PORT, (int)ret, (int)ERR_OK);
|
||||
if (ret == ERR_OK) {
|
||||
ret = ip_napt_enable_no(SOFTAP_IF, 1);
|
||||
CONSOLE_PRINTF(" ip_napt_enable_no(SOFTAP_IF): ret=%d (OK=%d)\r\n", (int)ret, (int)ERR_OK);
|
||||
if (ret == ERR_OK) {
|
||||
CONSOLE_PRINTF(" NAPT AP '%s' started.\r\n", softAP_ssid);
|
||||
if (WiFi.localIP().isSet()) {
|
||||
CONSOLE_PRINTF(" It is an extension of '%s' made through WLAN interface.\r\n", ssid);
|
||||
CONSOLE_PRINTF(" Remote WLAN IP Address: %s.\r\n", WiFi.localIP().toString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
CONSOLE_PRINTF(" Heap after NAPT init: %d\r\n", ESP.getFreeHeap());
|
||||
if (ret != ERR_OK) {
|
||||
CONSOLE_PRINTF(" NAPT initialization failed!!!\r\n");
|
||||
}
|
||||
|
||||
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
|
||||
server.on("/", handleRoot);
|
||||
server.on("/wifi", handleWifi);
|
||||
server.on("/wifisave", handleWifiSave);
|
||||
server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin(); // Web server start
|
||||
CONSOLE_PRINTLN("HTTP server started");
|
||||
loadCredentials(); // Load WLAN credentials from network
|
||||
connect = (strlen(ssid) > 0 && staReady); // Request WLAN connect if there is a SSID and we want to connect at startup
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
CONSOLE_PRINTF("Connecting as wifi client, WLAN, to '%s' ...\r\n", ssid);
|
||||
WiFi.disconnect();
|
||||
/*
|
||||
A call to set hostname, must be set before the call to WiFi.begin, otherwise
|
||||
the name may be missing from the routers DNS lookup results. Note, not all
|
||||
routers will import registered DHCP host names from clients into the active
|
||||
local DNS resolver. For those that do, it is best to set hostname before
|
||||
calling WiFi.begin().
|
||||
*/
|
||||
WiFi.hostname(myHostname);
|
||||
WiFi.begin(ssid, password);
|
||||
int connRes = WiFi.waitForConnectResult();
|
||||
if (-1 == connRes) {
|
||||
CONSOLE_PRINTLN(" WiFi.waitForConnectResult() timed out.");
|
||||
} else {
|
||||
CONSOLE_PRINTF(" Connection status: %s, %d\r\n", getWiFiStatusString(connRes).c_str(), connRes);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (connect) {
|
||||
connect = false;
|
||||
connectWifi();
|
||||
lastConnectTry = millis();
|
||||
}
|
||||
{
|
||||
unsigned int s = WiFi.status();
|
||||
if (s == 0 && millis() > (lastConnectTry + 60000) && ssid[0] && staReady) {
|
||||
/* When all of the following conditions are true, try to connect */
|
||||
/* 1) If WLAN disconnected */
|
||||
/* 2) Required idle time between connect attempts has passed. */
|
||||
/* 3) We have an ssid configured */
|
||||
/* 4) We are ready for the STA to come up */
|
||||
/* Don't set retry time too low as retry interfere the softAP operation */
|
||||
connect = true;
|
||||
}
|
||||
if (status != s) { // WLAN status change
|
||||
CONSOLE_PRINTF("WLAN Status changed:\r\n");
|
||||
CONSOLE_PRINTF(" new status: %s, %d\r\n", getWiFiStatusString(s).c_str(), s);
|
||||
CONSOLE_PRINTF(" previous status: %s, %d\r\n", getWiFiStatusString(status).c_str(), status);
|
||||
status = s;
|
||||
if (s == WL_CONNECTED) {
|
||||
/* Just connected to WLAN */
|
||||
CONSOLE.println();
|
||||
if (WiFi.localIP().isSet() && WiFi.softAPIP().isSet()) {
|
||||
CONSOLE_PRINTF("NAPT AP '%s' status:\r\n", softAP_ssid);
|
||||
if (WiFi.localIP().isSet()) {
|
||||
CONSOLE_PRINTF(" It is an extension of '%s' made through WLAN interface.\r\n", ssid);
|
||||
CONSOLE_PRINTF(" WLAN connected with IP Address: %s.\r\n", WiFi.localIP().toString().c_str());
|
||||
}
|
||||
} else {
|
||||
CONSOLE_PRINT("WLAN connected to ");
|
||||
CONSOLE.println(ssid);
|
||||
CONSOLE_PRINT(" IP address: ");
|
||||
CONSOLE.println(WiFi.localIP());
|
||||
}
|
||||
// Setup MDNS responder
|
||||
if (!MDNS.begin(myHostname, WiFi.localIP())) {
|
||||
CONSOLE_PRINTLN(" Error setting up MDNS responder!");
|
||||
} else {
|
||||
CONSOLE_PRINTLN(" mDNS responder started");
|
||||
// Add service to MDNS-SD
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
/*
|
||||
Setup the DNSServer to respond only to request for our hostname and
|
||||
forward other name request to the DNS configured to the WLAN.
|
||||
*/
|
||||
dnsServer.setTTL(600); // 10 minutes
|
||||
dnsServer.enableForwarder(myHostname, WiFi.dnsIP(0));
|
||||
CONSOLE_PRINTF("DNSServer changes/status:\r\n");
|
||||
CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off");
|
||||
CONSOLE_PRINTF(" Resolve '%s' to this AP's IP address, '%s' %s.\r\n",
|
||||
dnsServer.getDomainName().c_str(),
|
||||
softAP_ssid,
|
||||
WiFi.softAPIP().toString().c_str());
|
||||
if (dnsServer.isDNSSet()) {
|
||||
CONSOLE_PRINTF(" Forward other lookups to DNS: %s\r\n", dnsServer.getDNS().toString().c_str());
|
||||
}
|
||||
CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL());
|
||||
|
||||
} else {
|
||||
/* Captive portals will usually use a TTL of 0 to avoid DNS cache poisoning. */
|
||||
dnsServer.setTTL(0);
|
||||
/* Setup the DNSServer to redirect all the domain lookups to the apIP */
|
||||
dnsServer.disableForwarder("*");
|
||||
CONSOLE_PRINTF("DNSServer changes/status:\r\n");
|
||||
CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off");
|
||||
CONSOLE_PRINTF(" Resolve all domain lookups, '%s', to this AP's IP address, '%s' %s.\r\n",
|
||||
dnsServer.getDomainName().c_str(),
|
||||
softAP_ssid,
|
||||
WiFi.softAPIP().toString().c_str());
|
||||
CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL());
|
||||
|
||||
// Note, it is not necessary to clear the DNS forwarder address. This
|
||||
// is being done here, to test that methods isDNSSet() and setDNS() work.
|
||||
dnsServer.setDNS(0U);
|
||||
if (dnsServer.isDNSSet()) {
|
||||
CONSOLE_PRINTF(" DNS forwarder address: %s\r\n", dnsServer.getDNS().toString().c_str());
|
||||
} else {
|
||||
CONSOLE_PRINTF(" DNS forwarder address not set.\r\n");
|
||||
}
|
||||
|
||||
if (s == WL_NO_SSID_AVAIL) {
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s == WL_CONNECTED) {
|
||||
MDNS.update();
|
||||
}
|
||||
}
|
||||
// Do work:
|
||||
// DNS
|
||||
dnsServer.processNextRequest();
|
||||
// HTTP
|
||||
server.handleClient();
|
||||
}
|
||||
|
||||
#else // LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
void setup() {
|
||||
WiFi.persistent(false);
|
||||
WiFi.mode(WIFI_OFF);
|
||||
Serial.begin(115200);
|
||||
Serial.printf("\n\nNAPT not supported in this configuration\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
|
||||
#endif // LWIP_FEATURES && !LWIP_IPV6
|
@ -0,0 +1,48 @@
|
||||
|
||||
#if LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
// Substitution list:
|
||||
// {t} - target name
|
||||
// {1} - The target to redirect to, in absolute URL form.
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char portalRedirectHTML[] PROGMEM = R"EOF(
|
||||
<!DOCTYPE html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
<meta http-equiv='Refresh' content='5; url={1}'>
|
||||
<title>Redirecting</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Captive Portal Redirect</h1>
|
||||
<p>Redirecting to <a href='{1}'>{t}</a></p>
|
||||
<p>If you do not see the menu in 5 seconds, please click on the above link!</p>
|
||||
</body>
|
||||
</html>
|
||||
)EOF";
|
||||
|
||||
#else
|
||||
static const char portalRedirectHTML[] PROGMEM = R"EOF(
|
||||
<!DOCTYPE html><html lang='en'><head><meta name='viewport' content='width=device-width'><meta http-equiv='Refresh' content='5; url={1}'><title>Redirecting</title></head><body><h1>Captive Portal Redirect</h1><p>Redirecting to <a href='{1}'>{t}</a></p><p>If you do not see the menu in 5 seconds, please click on the above link!</p></body></html>
|
||||
)EOF";
|
||||
#endif
|
||||
|
||||
void sendPortalRedirect(String path, String targetName) {
|
||||
CONSOLE_PRINTLN2("Request redirected to captive portal, original request was for ", server.hostHeader());
|
||||
/* There are 3 points of redirection here:
|
||||
1) "Location" element in the header
|
||||
2) HTML meta element to redirect
|
||||
3) Click on link to redirect
|
||||
If the "Location" header element works the HTML stuff is never seen.
|
||||
*/
|
||||
// https://tools.ietf.org/html/rfc7231#section-6.4.3
|
||||
server.sendHeader("Location", path, true);
|
||||
addNoCacheHeader();
|
||||
String reply = FPSTR(portalRedirectHTML);
|
||||
reply.reserve(reply.length() + 2 * path.length() + 80);
|
||||
reply.replace("{t}", targetName);
|
||||
reply.replace("{1}", path);
|
||||
server.send(302, "text/html", reply);
|
||||
}
|
||||
|
||||
#endif // LWIP_FEATURES && !LWIP_IPV6
|
325
libraries/DNSServer/examples/NAPTCaptivePortal/WifiHttp.ino
Normal file
325
libraries/DNSServer/examples/NAPTCaptivePortal/WifiHttp.ino
Normal file
@ -0,0 +1,325 @@
|
||||
|
||||
// #define DEBUG_VIEW
|
||||
// The idea here is to debug HTML with DEBUG_VIEW defined then, when finished,
|
||||
// use one of the minify web applications to strip the comments and nice
|
||||
// formating spaces. Then update the minified version below.
|
||||
//
|
||||
// Also there are comment sections at the top and bottom of each block of HTML
|
||||
// code. The purpose is to move the lines of C code near by into the blocked
|
||||
// comment sections. Then you have a large block of continguious HTML that can
|
||||
// be copy/pasted into one of the online web HTML checkers. You can adjust the
|
||||
// code there till it is correct then copy/paste back here. Then, you can move
|
||||
// comment boarders around until the C code is back in place.
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configHead[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
|
||||
Captive Portal html for WiFi Config
|
||||
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
<title>WiFi</title>
|
||||
<style>
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
vertical-align:middle;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
width: 98%;
|
||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.mono, td, input {
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
height: 1.5em;
|
||||
font-size: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.lg {
|
||||
font-size: 1em;
|
||||
height: 1.8em;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#outer {
|
||||
width:100%
|
||||
overflow-y: auto;
|
||||
}
|
||||
#inner {
|
||||
margin: .75em auto;
|
||||
max-width: 500px;
|
||||
min-width: 428px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload='ol()'>
|
||||
<script>
|
||||
|
||||
// short hand
|
||||
function ge(i) {
|
||||
return document.getElementById(i);
|
||||
}
|
||||
|
||||
// Toggle password view
|
||||
function pv() {
|
||||
let e = ge('p');
|
||||
if (e.type === 'password') {
|
||||
e.type = 'text';
|
||||
} else {
|
||||
e.type = 'password';
|
||||
}
|
||||
e.focus();
|
||||
}
|
||||
|
||||
// Copy SSID value to input element for SSID
|
||||
// Move focus to password input element and clear
|
||||
function cs(i){
|
||||
ge('s').value = i.innerText;
|
||||
let e = ge('p');
|
||||
e.type = 'password';
|
||||
e.value = '';
|
||||
e.focus();
|
||||
e.scrollIntoView();
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configHead[] PROGMEM = R"EOF(<!DOCTYPE html><html lang="en"><head><meta name='viewport' content='width=device-width'><title>WiFi</title><style>html,body{width:100%;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:16px}h1,h2,h3,h4{vertical-align:middle;text-align:center}input[type=submit]{width:98%;font-family:'Open Sans',Helvetica,Arial,sans-serif;height:3em}a{text-decoration:none;color:blue}.mono,td,input{font-family:'Source Code Pro',monospace;height:1.5em;font-size:1em;vertical-align:middle}.lg{font-size:1em;height:1.8em}.left{text-align:left}.center{text-align:center}.right{text-align:right}#outer{width:100% overflow-y: auto}#inner{margin: .75em auto;max-width:500px;min-width:428px;text-align:left}</style></head><body onload='ol()'> <script>function ge(i){return document.getElementById(i);} function pv(){let e=ge('p');if(e.type==='password'){e.type='text';}else{e.type='password';} e.focus();} function cs(i){ge('s').value=i.innerText;let e=ge('p');e.type='password';e.value='';e.focus();e.scrollIntoView();}</script> )EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configPresetInput[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
Prefill input fields with the last credentials used.
|
||||
{s} - Network Name/SSID
|
||||
{p} - Password
|
||||
-->
|
||||
<script>
|
||||
// After web page load init SSID/PSK field
|
||||
// onload copy credentials
|
||||
function ol() {
|
||||
ge('s').value='{s}';
|
||||
ge('p').value='{p}';
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configPresetInput[] PROGMEM = R"EOF(<script>function ol(){ge('s').value='{s}';ge('p').value='{p}';}</script>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configConnection[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
{w} - SoftAP info "SoftAP: softAP_ssid"
|
||||
or - WiFi Network connection info "WiFi Network: ssid"
|
||||
-->
|
||||
<div id='outer'>
|
||||
<div id='inner'>
|
||||
<h1>WiFi Details and Config</h1>
|
||||
<p>You are connected through the {w}</p>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configConnection[] PROGMEM = R"EOF(<div id='outer'><div id='inner'><h1>WiFi Details and Config</h1><p>You are connected through the {w}</p>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configAPInfo[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
{s} - SSDI of Network
|
||||
{i} - IP address on that Network, WiFi.softAPIP or WiFi.localIP
|
||||
{a} - Number of Stations connected to AP
|
||||
-->
|
||||
<br />
|
||||
<h2>SoftAP Details</h2>
|
||||
<table>
|
||||
<tr><td>SSI</td><td>{s}</td></tr>
|
||||
<tr><td>BSSID </td><td>{b}</td></tr>
|
||||
<tr><td>IP</td><td>{i}</td></tr>
|
||||
<tr><td>STA</td><td>Count: {a}</td></tr>
|
||||
</table>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configAPInfo[] PROGMEM = R"EOF(<br /><h2>SoftAP Details</h2><table><tr><td>SSI</td><td>{s}</td></tr><tr><td>BSSID </td><td>{b}</td></tr><tr><td>IP</td><td>{i}</td></tr><tr><td>STA</td><td>Count: {a}</td></tr></table>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configWLANInfo[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
{s} - SSDI of Network
|
||||
{b} - BSSID
|
||||
{c} - Channel
|
||||
{p} - PHY Mode
|
||||
{r} - RSSI
|
||||
{i} - IP address on that Network, WiFi.softAPIP or WiFi.localIP
|
||||
{m} - Subnet Mask
|
||||
{g} - Gateway
|
||||
{1} - DNS1
|
||||
{2} - DNS2
|
||||
-->
|
||||
<br />
|
||||
<h2>WLAN Details</h2>
|
||||
<table>
|
||||
<tr><td>SSID</td><td>{s}</td></tr>
|
||||
<tr><td>BSSID </td><td>{b}</td></tr>
|
||||
<tr><td>CH</td><td>{c}</td></tr>
|
||||
<tr><td>PHY</td><td>{p}</td></tr>
|
||||
<tr><td>RSSI</td><td>{r}</td></tr>
|
||||
<tr><td>IP</td><td>{i}</td></tr>
|
||||
<tr><td>GW</td><td>{g}</td></tr>
|
||||
<tr><td>Mask</td><td>{m}</td></tr>
|
||||
<tr><td>DNS1</td><td>{1}</td></tr>
|
||||
<tr><td>DNS2</td><td>{2}</td></tr>
|
||||
</table>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configWLANInfo[] PROGMEM = R"EOF(<br /><h2>WLAN Details</h2><table><tr><td>SSID</td><td>{s}</td></tr><tr><td>BSSID </td><td>{b}</td></tr><tr><td>CH</td><td>{c}</td></tr><tr><td>PHY</td><td>{p}</td></tr><tr><td>RSSI</td><td>{r}</td></tr><tr><td>IP</td><td>{i}</td></tr><tr><td>GW</td><td>{g}</td></tr><tr><td>Mask</td><td>{m}</td></tr><tr><td>DNS1</td><td>{1}</td></tr><tr><td>DNS2</td><td>{2}</td></tr></table>)EOF";
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configWLANOffline[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
-->
|
||||
<br />
|
||||
<h2>WLAN - offline</h2>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configWLANOffline[] PROGMEM = R"EOF(<br /><h2>WLAN - offline</h2>)EOF";
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configList[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
-->
|
||||
<br />
|
||||
<h2>WLAN Network List</h2>
|
||||
<h4>(refresh if any are missing)</h4>
|
||||
<table>
|
||||
<tr><th class='left'>Network Name/SSID</th>
|
||||
<th style='width: 2em;' class='center'>CH</th>
|
||||
<th style='width: 1em;' class='center'></th>
|
||||
<th class='right'>RSSI</th></tr>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configList[] PROGMEM = R"EOF(<br /><h2>WLAN Network List</h2><h4>(refresh if any are missing)</h4><table><tr><th class='left'>Network Name/SSID</th><th style='width: 2em;' class='center'>CH</th><th style='width: 1em;' class='center'></th><th class='right'>RSSI</th></tr>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configItem[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
use this with each AP when APs are found in a scan
|
||||
{s} - WiFi.SSID
|
||||
{c} - channel
|
||||
{l} - For a secure Network replace with 🔒 (lock symbol)
|
||||
{r} - WiFi.RSSI
|
||||
-->
|
||||
<tr><td><a class='mono' onclick='cs(this)' title='{t}'>{s}</a></td>
|
||||
<td class='center'>{c}</td>
|
||||
<td class='center'>{l}</td>
|
||||
<td class='right'>{r}</td></tr>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configItem[] PROGMEM = R"EOF(<tr><td><a class='mono' onclick='cs(this)' title='{t}'>{s}</a></td><td class='center'>{c}</td><td class='center'>{l}</td><td class='right'>{r}</td></tr>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configNoAPs[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
or this when no APs are found
|
||||
-->
|
||||
<tr><td>No WLAN found</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td></tr>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configNoAPs[] PROGMEM = R"EOF(<tr><td>No WLAN found</td><td></td><td></td><td></td></tr>)EOF";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configEnd[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
-->
|
||||
</table>
|
||||
<br /><form method='POST' action='wifisave'><h4>Connect to Network:</h4>
|
||||
<input id='s' class='lg' type='text' size=32 maxlength=32 placeholder='Network Name/SSID' name='n' spellcheck='false' data-gramm_editor='false'/>
|
||||
<br /><br />
|
||||
<input id='p' class='lg' type='password' size=32 maxlength=64 placeholder='password' name='p' spellcheck='false' data-gramm_editor='false'/>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VIEW
|
||||
static const char configEnd2[] PROGMEM = R"EOF(
|
||||
<!--
|
||||
-->
|
||||
<a class='lg' onclick='pv();'>👁</a>
|
||||
<br /><br /><input type='submit' value='Connect/Disconnect'/>
|
||||
</form>
|
||||
<br />
|
||||
<p>You may want to <a href='/'>return to the home page</a>.</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
-->
|
||||
)EOF";
|
||||
#else
|
||||
static const char configEnd[] PROGMEM = R"EOF(</table> <br /><form method='POST' action='wifisave'><h4>Connect to Network:</h4> <input id='s' class='lg' type='text' size=32 maxlength=32 placeholder='Network Name/SSID' name='n' spellcheck='false' data-gramm_editor='false'/> <br /><br /> <input id='p' class='lg' type='password' size=32 maxlength=64 placeholder='password' name='p' spellcheck='false' data-gramm_editor='false'/> <a class='lg' onclick='pv();'>👁</a> <br /><br /><input type='submit' value='Connect/Disconnect'/></form> <br /><p>You may want to <a href='/'>return to the home page</a>.</p><p></p></div></div></body></html>)EOF";
|
||||
#endif
|
@ -0,0 +1,33 @@
|
||||
|
||||
#if LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
/** Load WLAN credentials from EEPROM */
|
||||
void loadCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.get(0, ssid);
|
||||
EEPROM.get(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1];
|
||||
EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.end();
|
||||
if (String(ok) != String("OK")) {
|
||||
ssid[0] = 0;
|
||||
password[0] = 0;
|
||||
}
|
||||
|
||||
CONSOLE_PRINTLN("Recovered credentials:");
|
||||
CONSOLE_PRINTF(" %s\r\n", ssid);
|
||||
CONSOLE_PRINTF(" %s\r\n", strlen(password) > 0 ? "********" : "<no password>");
|
||||
}
|
||||
|
||||
/** Store WLAN credentials to EEPROM */
|
||||
void saveCredentials() {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.put(0, ssid);
|
||||
EEPROM.put(0 + sizeof(ssid), password);
|
||||
char ok[2 + 1] = "OK";
|
||||
EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok);
|
||||
EEPROM.commit();
|
||||
EEPROM.end();
|
||||
}
|
||||
|
||||
#endif // LWIP_FEATURES && !LWIP_IPV6
|
250
libraries/DNSServer/examples/NAPTCaptivePortal/handleHttp.ino
Normal file
250
libraries/DNSServer/examples/NAPTCaptivePortal/handleHttp.ino
Normal file
@ -0,0 +1,250 @@
|
||||
#if LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
#ifndef TCP_MSS
|
||||
#define TCP_MSS 1460
|
||||
#endif
|
||||
/*
|
||||
Use kMaxChunkSize to limit size of chuncks
|
||||
*/
|
||||
constexpr inline size_t kMaxChunkSize = TCP_MSS;
|
||||
String& sendIfOver(String& str, size_t threshold = kMaxChunkSize / 2);
|
||||
size_t sendAsChunks_P(PGM_P content, size_t chunkSize = kMaxChunkSize);
|
||||
|
||||
size_t maxPage = 0;
|
||||
|
||||
void addNoCacheHeader() {
|
||||
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "-1");
|
||||
}
|
||||
|
||||
|
||||
String& sendIfOver(String& str, size_t threshold) {
|
||||
size_t len = str.length();
|
||||
if (len > threshold) {
|
||||
// Use later to determine if we reserved enough room in page to avoid realloc
|
||||
maxPage = std::max(maxPage, len);
|
||||
server.sendContent(str);
|
||||
str = "";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
The idea here is to avoid a large allocation by sendContent_P to copy a
|
||||
big PROGMEM string. Slice PROGMEM string into chuncks and send.
|
||||
*/
|
||||
size_t sendAsChunks_P(PGM_P content, size_t chunkSize) {
|
||||
size_t len = strlen_P(content);
|
||||
for (size_t pos = 0; pos < len; pos += chunkSize) {
|
||||
server.sendContent_P(&content[pos], ((len - pos) >= chunkSize) ? chunkSize : len - pos);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/** Handle root or redirect to captive portal */
|
||||
void handleRoot() {
|
||||
if (captivePortal()) {
|
||||
// If captive portal is needed, redirect instead of displaying the page.
|
||||
return;
|
||||
}
|
||||
addNoCacheHeader();
|
||||
|
||||
String Page;
|
||||
Page += F(
|
||||
"<!DOCTYPE html>"
|
||||
"<html lang='en'><head><meta name='viewport' content='width=device-width'>"
|
||||
"<title>ADV CAP Portal Example</title>"
|
||||
"</head><body>"
|
||||
"<h1>HELLO WORLD!!</h1>");
|
||||
if (server.client().localIP() == apIP) {
|
||||
Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
|
||||
} else {
|
||||
Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
|
||||
}
|
||||
Page += F(
|
||||
"<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
|
||||
"</body></html>");
|
||||
|
||||
server.send(200, F("text/html"), Page);
|
||||
}
|
||||
|
||||
/*
|
||||
Redirect to the captive portal if we got a request for another domain.
|
||||
Return true in that case, so the page handler does not try to handle
|
||||
the request again.
|
||||
*/
|
||||
boolean captivePortal() {
|
||||
IPAddress hAddr, cAddr;
|
||||
|
||||
cAddr = server.client().localIP();
|
||||
if (!cAddr.isSet()) {
|
||||
// The connection closed prematurely on us.
|
||||
// Return true, so no further action is taken.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hAddr.fromString(server.hostHeader()) && hAddr == cAddr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hAddr.isSet() || (server.hostHeader() != (String(myHostname) + ".local") && // arrived here by mDNS
|
||||
server.hostHeader() != String(myHostname))) { // arrived here by local router DNS
|
||||
String whereTo = String("http://") + server.client().localIP().toString();
|
||||
sendPortalRedirect(whereTo, F("Captive Portal Example"));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Wifi Details and Config page handler */
|
||||
void handleWifi() {
|
||||
addNoCacheHeader();
|
||||
|
||||
// use HTTP/1.1 Chunked response to avoid building a huge temporary string
|
||||
if (!server.chunkedResponseModeStart(200, F("text/html"))) {
|
||||
server.send(505, F("text/plain"), F("HTTP1.1 required"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a few chunks of the HTML that don't need to change.
|
||||
sendAsChunks_P(configHead);
|
||||
|
||||
String page;
|
||||
|
||||
CONSOLE_PRINTLN2("sizeof(configHead): ", (sizeof(configHead)));
|
||||
CONSOLE_PRINTLN2("sizeof(configWLANInfo): ", (sizeof(configWLANInfo)));
|
||||
CONSOLE_PRINTLN2("sizeof(configList): ", (sizeof(configList)));
|
||||
CONSOLE_PRINTLN2("sizeof(configEnd): ", (sizeof(configEnd)));
|
||||
// Just do max on some of the visually larger HTML chunks that will be loaded
|
||||
// into page and add a little for growth when substituting values in.
|
||||
size_t thisMany = std::max(sizeof(configWLANInfo), sizeof(configList)) + 200;
|
||||
CONSOLE_PRINTLN2("Estimate Minimum page reserve size: ", (thisMany));
|
||||
page.reserve(std::max(kMaxChunkSize, thisMany));
|
||||
|
||||
page = FPSTR(configPresetInput);
|
||||
/*
|
||||
Set previously used/entered credentials as a default entries.
|
||||
This allows an opportunity to correct them and try again.
|
||||
*/
|
||||
page.replace("{s}", String(ssid));
|
||||
page.replace("{p}", String(password));
|
||||
sendIfOver(page);
|
||||
|
||||
page += FPSTR(configConnection);
|
||||
if (server.client().localIP() == apIP) {
|
||||
page.replace("{w}", String(F("SoftAP: ")) + softAP_ssid);
|
||||
} else {
|
||||
page.replace("{w}", String(F("WiFi Network: ")) + ssid);
|
||||
}
|
||||
|
||||
/*
|
||||
To avoid sending lots of small packets. We call this function frequently,
|
||||
to check if the 'page' has gone over 512 bytes and if so send.
|
||||
*/
|
||||
sendIfOver(page);
|
||||
|
||||
page += FPSTR(configAPInfo);
|
||||
{
|
||||
uint8_t sta_cnt = wifi_softap_get_station_num();
|
||||
page.replace("{s}", String(softAP_ssid));
|
||||
page.replace("{b}", String(WiFi.softAPmacAddress()));
|
||||
page.replace("{i}", WiFi.softAPIP().toString());
|
||||
page.replace("{a}", String(sta_cnt));
|
||||
sendIfOver(page);
|
||||
if (sta_cnt) {
|
||||
page += String(F("\r\n<PRE>\r\n"));
|
||||
struct station_info* info = wifi_softap_get_station_info();
|
||||
IPAddress addr;
|
||||
while (info != NULL) {
|
||||
addr = info->ip;
|
||||
page += macToString(info->bssid) + F(" ") + addr.toString() + F("\r\n");
|
||||
info = STAILQ_NEXT(info, next);
|
||||
sendIfOver(page);
|
||||
}
|
||||
page += F("</PRE>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Before we prepare a large block for sending, we call 'sendIfOver' with a
|
||||
threshold of 0 to force the sending of the current 'page' content.
|
||||
*/
|
||||
|
||||
if (WiFi.localIP().isSet()) {
|
||||
sendIfOver(page, 0);
|
||||
page += FPSTR(configWLANInfo);
|
||||
page.replace("{s}", String(ssid));
|
||||
page.replace("{b}", macToString(bssid));
|
||||
page.replace("{c}", String(WiFi.channel()));
|
||||
page.replace("{p}", String(F("802.11")) + (getPhyModeChar(WiFi.getPhyMode())));
|
||||
page.replace("{r}", String(WiFi.RSSI()));
|
||||
page.replace("{i}", WiFi.localIP().toString());
|
||||
page.replace("{g}", WiFi.gatewayIP().toString());
|
||||
page.replace("{m}", WiFi.subnetMask().toString());
|
||||
page.replace("{1}", WiFi.dnsIP(0).toString());
|
||||
page.replace("{2}", WiFi.dnsIP(1).toString());
|
||||
} else {
|
||||
page += FPSTR(configWLANOffline);
|
||||
}
|
||||
|
||||
sendIfOver(page, 0);
|
||||
sendAsChunks_P(configList);
|
||||
|
||||
CONSOLE_PRINTLN("scan start");
|
||||
int n = WiFi.scanNetworks();
|
||||
CONSOLE_PRINTLN("scan done");
|
||||
|
||||
if (n > 0) {
|
||||
for (size_t i = 0; i < (size_t)n; i++) {
|
||||
page += FPSTR(configItem);
|
||||
page.replace("{s}", WiFi.SSID(i));
|
||||
page.replace("{t}", WiFi.BSSIDstr(i));
|
||||
page.replace("{c}", String(WiFi.channel(i)));
|
||||
page.replace("{l}", (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F("") : F("🔒"));
|
||||
page.replace("{r}", String(WiFi.RSSI(i)));
|
||||
sendIfOver(page);
|
||||
}
|
||||
} else {
|
||||
page += FPSTR(configNoAPs);
|
||||
}
|
||||
sendIfOver(page, 0); // send what we have buffered before next direct send.
|
||||
sendAsChunks_P(configEnd);
|
||||
|
||||
CONSOLE_PRINTLN2("MAX String memory used: ", (maxPage));
|
||||
server.chunkedResponseFinalize();
|
||||
}
|
||||
|
||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
||||
void handleWifiSave() {
|
||||
CONSOLE_PRINTLN("wifi save");
|
||||
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
|
||||
server.arg("p").toCharArray(password, sizeof(password) - 1);
|
||||
sendPortalRedirect(F("wifi"), F("Wifi Config"));
|
||||
saveCredentials();
|
||||
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
if (captivePortal()) { // If captive portal redirect instead of displaying the error page.
|
||||
return;
|
||||
}
|
||||
String message = F("File Not Found\r\n\r\n");
|
||||
message += F("URI: ");
|
||||
message += server.uri();
|
||||
message += F("\r\nMethod: ");
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += F("\r\nArguments: ");
|
||||
message += server.args();
|
||||
message += F("\r\n");
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\r\n");
|
||||
}
|
||||
addNoCacheHeader();
|
||||
server.send(404, F("text/plain"), message);
|
||||
}
|
||||
|
||||
#endif // LWIP_FEATURES && !LWIP_IPV6
|
84
libraries/DNSServer/examples/NAPTCaptivePortal/tools.ino
Normal file
84
libraries/DNSServer/examples/NAPTCaptivePortal/tools.ino
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
These functions may exist in other projects
|
||||
*/
|
||||
|
||||
#if LWIP_FEATURES && !LWIP_IPV6
|
||||
|
||||
/*
|
||||
Returns a descriptive string for WiFi.status() value
|
||||
*/
|
||||
String getWiFiStatusString(uint32_t status) {
|
||||
const __FlashStringHelper* r;
|
||||
switch (status) {
|
||||
case WL_IDLE_STATUS:
|
||||
r = F("WL_IDLE_STATUS");
|
||||
break;
|
||||
|
||||
case WL_NO_SSID_AVAIL:
|
||||
r = F("WL_NO_SSID_AVAIL");
|
||||
break;
|
||||
|
||||
case WL_SCAN_COMPLETED:
|
||||
r = F("WL_SCAN_COMPLETED");
|
||||
break;
|
||||
|
||||
case WL_CONNECTED:
|
||||
r = F("WL_CONNECTED");
|
||||
break;
|
||||
|
||||
case WL_CONNECT_FAILED:
|
||||
r = F("WL_CONNECT_FAILED");
|
||||
break;
|
||||
|
||||
case WL_CONNECTION_LOST:
|
||||
r = F("WL_CONNECTION_LOST");
|
||||
break;
|
||||
|
||||
case WL_DISCONNECTED:
|
||||
r = F("WL_DISCONNECTED");
|
||||
break;
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
r = F("WL_NO_SHIELD");
|
||||
break;
|
||||
|
||||
default:
|
||||
return String(F("Unknown: 0x")) + String(status, HEX);
|
||||
}
|
||||
return String(r);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a single charcter to append to a "802.11" string to describe the PHY
|
||||
mode of a WiFi device. Can be used with the value returned by WiFi.getPhyMode().
|
||||
*/
|
||||
char getPhyModeChar(WiFiPhyMode_t i) {
|
||||
switch (i) {
|
||||
case WIFI_PHY_MODE_11B:
|
||||
return 'b'; // = 1
|
||||
case WIFI_PHY_MODE_11G:
|
||||
return 'g'; // = 2,
|
||||
case WIFI_PHY_MODE_11N:
|
||||
return 'n'; // = 3,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
/*
|
||||
Return a String of 6 colon separated hex bytes.
|
||||
This format is commonly used when printing 6 byte MAC addresses.
|
||||
*/
|
||||
String macToString(const unsigned char* mac) {
|
||||
char buf[20];
|
||||
int rc = snprintf(buf, sizeof(buf), PSTR("%02X:%02X:%02X:%02X:%02X:%02X"),
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
if (rc < 0 || rc >= (int)sizeof(buf)) {
|
||||
return emptyString;
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
#endif // LWIP_FEATURES && !LWIP_IPV6
|
@ -1,33 +1,139 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "DNSServer.h"
|
||||
#include <lwip/def.h>
|
||||
#include <Arduino.h>
|
||||
#include <memory>
|
||||
|
||||
extern struct rst_info resetInfo;
|
||||
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
||||
#define CONSOLE DEBUG_ESP_PORT
|
||||
#else
|
||||
#define DEBUG_OUTPUT Serial
|
||||
#define CONSOLE Serial
|
||||
#endif
|
||||
|
||||
#define _ETS_PRINTF(a, ...) ets_uart_printf(a, ##__VA_ARGS__)
|
||||
#define _ETS_PRINTFNL(a, ...) ets_uart_printf(a "\n", ##__VA_ARGS__)
|
||||
#define _PRINTF(a, ...) printf_P(PSTR(a), ##__VA_ARGS__)
|
||||
#define _PRINT(a) print(String(F(a)))
|
||||
#define _PRINTLN(a) println(String(F(a)))
|
||||
#define _PRINTLN2(a, b) println(String(F(a)) + b )
|
||||
|
||||
#define ETS_PRINTF _ETS_PRINTF
|
||||
#define ETS_PRINTFNL _ETS_PRINTFNL
|
||||
#define CONSOLE_PRINTF CONSOLE._PRINTF
|
||||
#define CONSOLE_PRINT CONSOLE._PRINT
|
||||
#define CONSOLE_PRINTLN CONSOLE._PRINTLN
|
||||
#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2
|
||||
|
||||
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
#define DEBUG_PRINTF CONSOLE_PRINTF
|
||||
#define DEBUG_PRINT CONSOLE_PRINT
|
||||
#define DEBUG_PRINTLN CONSOLE_PRINTLN
|
||||
#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2
|
||||
#define DBGLOG_FAIL LOG_FAIL
|
||||
|
||||
#define DEBUG_(...) do { (__VA_ARGS__); } while(false)
|
||||
#define DEBUG__(...) __VA_ARGS__
|
||||
#define LOG_FAIL(a, fmt, ...) do { if (!(a)) { CONSOLE.printf_P( PSTR(fmt " line: %d, function: %s\r\n"), ##__VA_ARGS__, __LINE__, __FUNCTION__ ); } } while(false);
|
||||
|
||||
#else
|
||||
#define DEBUG_PRINTF(...) do { } while(false)
|
||||
#define DEBUG_PRINT(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN(...) do { } while(false)
|
||||
#define DEBUG_PRINTLN2(...) do { } while(false)
|
||||
#define DEBUG_(...) do { } while(false)
|
||||
#define DEBUG__(...) do { } while(false)
|
||||
#define LOG_FAIL(a, ...) do { a; } while(false)
|
||||
#define DBGLOG_FAIL(...) do { } while(false)
|
||||
#endif
|
||||
|
||||
#define DNS_HEADER_SIZE sizeof(DNSHeader)
|
||||
|
||||
// Want to keep IDs unique across restarts and continquious
|
||||
static uint32_t _ids __attribute__((section(".noinit")));
|
||||
|
||||
DNSServer::DNSServer()
|
||||
{
|
||||
// I have observed that using 0 for captive and non-zero (600) when
|
||||
// forwarding, will help Android devices recognize the change in connectivity.
|
||||
// They will then report connected.
|
||||
_ttl = lwip_htonl(60);
|
||||
|
||||
if (REASON_DEFAULT_RST == resetInfo.reason ||
|
||||
REASON_DEEP_SLEEP_AWAKE <= resetInfo.reason) {
|
||||
_ids = random(0, BIT(16) - 1);
|
||||
}
|
||||
_ids += kDNSSQueSize; // for the case of restart, ignore any inflight responses
|
||||
|
||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
||||
}
|
||||
|
||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
||||
const IPAddress &resolvedIP)
|
||||
void DNSServer::disableForwarder(const String &domainName, bool freeResources)
|
||||
{
|
||||
_port = port;
|
||||
|
||||
_domainName = domainName;
|
||||
_forwarder = false;
|
||||
if (!domainName.isEmpty()) {
|
||||
_domainName = domainName;
|
||||
downcaseAndRemoveWwwPrefix(_domainName);
|
||||
}
|
||||
if (freeResources) {
|
||||
_dns = (uint32_t)0;
|
||||
if (_que) {
|
||||
_que = nullptr;
|
||||
DEBUG_PRINTF("from stop, deleted _que\r\n");
|
||||
DEBUG_(({
|
||||
if (_que_ov) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que overflow or no reply to request: ", (_que_ov));
|
||||
}
|
||||
if (_que_drop) {
|
||||
DEBUG_PRINTLN2("DNS forwarder que wrapped, reply dropped: ", (_que_drop));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DNSServer::enableForwarder(const String &domainName, const IPAddress &dns)
|
||||
{
|
||||
disableForwarder(domainName, false); // Just happens to have the same logic needed here.
|
||||
|
||||
if (dns.isSet()) {
|
||||
_dns = dns;
|
||||
}
|
||||
|
||||
if (_dns.isSet()) {
|
||||
if (!_que) {
|
||||
_que = std::unique_ptr<DNSS_REQUESTER[]> (new (std::nothrow) DNSS_REQUESTER[kDNSSQueSize]);
|
||||
DEBUG_PRINTF("Created new _que\r\n");
|
||||
if (_que) {
|
||||
for (size_t i = 0; i < kDNSSQueSize; i++) {
|
||||
_que[i].ip = 0;
|
||||
}
|
||||
DEBUG_((_que_ov = 0));
|
||||
DEBUG_((_que_drop = 0));
|
||||
}
|
||||
}
|
||||
if (_que) {
|
||||
_forwarder = true;
|
||||
}
|
||||
}
|
||||
return _forwarder;
|
||||
}
|
||||
|
||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
||||
const IPAddress &resolvedIP, const IPAddress &dns)
|
||||
{
|
||||
_port = (port) ? port : IANA_DNS_PORT;
|
||||
|
||||
_resolvedIP[0] = resolvedIP[0];
|
||||
_resolvedIP[1] = resolvedIP[1];
|
||||
_resolvedIP[2] = resolvedIP[2];
|
||||
_resolvedIP[3] = resolvedIP[3];
|
||||
downcaseAndRemoveWwwPrefix(_domainName);
|
||||
|
||||
if (!enableForwarder(domainName, dns) && (dns.isSet() || _dns.isSet())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _udp.begin(_port) == 1;
|
||||
}
|
||||
|
||||
@ -41,9 +147,15 @@ void DNSServer::setTTL(const uint32_t &ttl)
|
||||
_ttl = lwip_htonl(ttl);
|
||||
}
|
||||
|
||||
uint32_t DNSServer::getTTL()
|
||||
{
|
||||
return lwip_ntohl(_ttl);
|
||||
}
|
||||
|
||||
void DNSServer::stop()
|
||||
{
|
||||
_udp.stop();
|
||||
disableForwarder(emptyString, true);
|
||||
}
|
||||
|
||||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
|
||||
@ -53,7 +165,58 @@ void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
|
||||
domainName.remove(0, 4);
|
||||
}
|
||||
|
||||
void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
void DNSServer::forwardReply(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
uint16_t id = dnsHeader->ID;
|
||||
// if (kDNSSQueSize <= (uint16_t)((uint16_t)_ids - id)) {
|
||||
if ((uint16_t)kDNSSQueSize <= (uint16_t)_ids - id) {
|
||||
DEBUG_((++_que_drop));
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" dropped!")));
|
||||
return;
|
||||
}
|
||||
size_t i = id & (kDNSSQueSize - 1);
|
||||
|
||||
// Drop duplicate packets
|
||||
if (0 == _que[i].ip) {
|
||||
DEBUG_PRINTLN2("Duplicate reply dropped ID: 0x", String(id, HEX));
|
||||
return;
|
||||
}
|
||||
dnsHeader->ID = _que[i].id;
|
||||
_udp.beginPacket(_que[i].ip, _que[i].port);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" to ") + IPAddress(_que[i].ip).toString()));
|
||||
_que[i].ip = 0; // This gets used to detect duplicate packets and overflow
|
||||
}
|
||||
|
||||
void DNSServer::forwardRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!_forwarder || !_dns.isSet() || !_que) {
|
||||
return;
|
||||
}
|
||||
DNSHeader *dnsHeader = (DNSHeader *)buffer;
|
||||
++_ids;
|
||||
size_t i = _ids & (kDNSSQueSize - 1);
|
||||
DEBUG_(({
|
||||
if (0 != _que[i].ip) {
|
||||
++_que_ov;
|
||||
}
|
||||
}));
|
||||
_que[i].ip = _udp.remoteIP();
|
||||
_que[i].port = _udp.remotePort();
|
||||
_que[i].id = dnsHeader->ID;
|
||||
dnsHeader->ID = (uint16_t)_ids;
|
||||
_udp.beginPacket(_dns, IANA_DNS_PORT);
|
||||
_udp.write(buffer, length);
|
||||
_udp.endPacket();
|
||||
DEBUG_PRINTLN2("Forward request ID: 0x", (String(dnsHeader->ID, HEX) + F(" to ") + _dns.toString()));
|
||||
}
|
||||
|
||||
bool DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
{
|
||||
DNSHeader *dnsHeader;
|
||||
uint8_t *query, *start;
|
||||
@ -64,23 +227,30 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
dnsHeader = (DNSHeader *)buffer;
|
||||
|
||||
// Must be a query for us to do anything with it
|
||||
if (dnsHeader->QR != DNS_QR_QUERY)
|
||||
return;
|
||||
if (dnsHeader->QR != DNS_QR_QUERY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If operation is anything other than query, we don't do it
|
||||
if (dnsHeader->OPCode != DNS_OPCODE_QUERY)
|
||||
return replyWithError(dnsHeader, DNSReplyCode::NotImplemented);
|
||||
if (dnsHeader->OPCode != DNS_OPCODE_QUERY) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NotImplemented);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only support requests containing single queries - everything else
|
||||
// is badly defined
|
||||
if (dnsHeader->QDCount != lwip_htons(1))
|
||||
return replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
if (dnsHeader->QDCount != lwip_htons(1)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must return a FormError in the case of a non-zero ARCount to
|
||||
// be minimally compatible with EDNS resolvers
|
||||
if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0
|
||||
|| dnsHeader->ARCount != 0)
|
||||
return replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
|| dnsHeader->ARCount != 0) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even if we're not going to use the query, we need to parse it
|
||||
// so we can check the address type that's being queried
|
||||
@ -89,15 +259,19 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
remaining = length - DNS_HEADER_SIZE;
|
||||
while (remaining != 0 && *start != 0) {
|
||||
labelLength = *start;
|
||||
if (labelLength + 1 > remaining)
|
||||
return replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
if (labelLength + 1 > remaining) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
remaining -= (labelLength + 1);
|
||||
start += (labelLength + 1);
|
||||
}
|
||||
|
||||
// 1 octet labelLength, 2 octet qtype, 2 octet qclass
|
||||
if (remaining < 5)
|
||||
return replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
if (remaining < 5) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::FormError);
|
||||
return false;
|
||||
}
|
||||
|
||||
start += 1; // Skip the 0 length label that we found above
|
||||
|
||||
@ -109,23 +283,33 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
queryLength = start - query;
|
||||
|
||||
if (qclass != lwip_htons(DNS_QCLASS_ANY)
|
||||
&& qclass != lwip_htons(DNS_QCLASS_IN))
|
||||
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain,
|
||||
query, queryLength);
|
||||
&& qclass != lwip_htons(DNS_QCLASS_IN)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qtype != lwip_htons(DNS_QTYPE_A)
|
||||
&& qtype != lwip_htons(DNS_QTYPE_ANY))
|
||||
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain,
|
||||
query, queryLength);
|
||||
&& qtype != lwip_htons(DNS_QTYPE_ANY)) {
|
||||
replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have no domain name configured, just return an error
|
||||
if (_domainName.isEmpty())
|
||||
return replyWithError(dnsHeader, _errorReplyCode,
|
||||
query, queryLength);
|
||||
if (_domainName.isEmpty()) {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're running with a wildcard we can just return a result now
|
||||
if (_domainName == "*")
|
||||
return replyWithIP(dnsHeader, query, queryLength);
|
||||
if (_domainName == "*") {
|
||||
DEBUG_PRINTF("dnsServer - replyWithIP\r\n");
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
matchString = _domainName.c_str();
|
||||
|
||||
@ -139,24 +323,32 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
|
||||
labelLength = *start;
|
||||
start += 1;
|
||||
while (labelLength > 0) {
|
||||
if (tolower(*start) != *matchString)
|
||||
return replyWithError(dnsHeader, _errorReplyCode,
|
||||
query, queryLength);
|
||||
if (tolower(*start) != *matchString) {
|
||||
if (_forwarder) {
|
||||
return true;
|
||||
} else {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++start;
|
||||
++matchString;
|
||||
--labelLength;
|
||||
}
|
||||
if (*start == 0 && *matchString == '\0')
|
||||
return replyWithIP(dnsHeader, query, queryLength);
|
||||
if (*start == 0 && *matchString == '\0') {
|
||||
replyWithIP(dnsHeader, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*matchString != '.')
|
||||
return replyWithError(dnsHeader, _errorReplyCode,
|
||||
query, queryLength);
|
||||
if (*matchString != '.') {
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
++matchString;
|
||||
}
|
||||
|
||||
return replyWithError(dnsHeader, _errorReplyCode,
|
||||
query, queryLength);
|
||||
replyWithError(dnsHeader, _errorReplyCode, query, queryLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DNSServer::processNextRequest()
|
||||
@ -182,7 +374,14 @@ void DNSServer::processNextRequest()
|
||||
return;
|
||||
|
||||
_udp.read(buffer.get(), currentPacketSize);
|
||||
respondToRequest(buffer.get(), currentPacketSize);
|
||||
if (_dns.isSet() && _udp.remoteIP() == _dns) {
|
||||
// _forwarder may have been set to false; however, for now allow inflight
|
||||
// replys to finish. //??
|
||||
forwardReply(buffer.get(), currentPacketSize);
|
||||
} else
|
||||
if (respondToRequest(buffer.get(), currentPacketSize)) {
|
||||
forwardRequest(buffer.get(), currentPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSServer::writeNBOShort(uint16_t value)
|
||||
|
@ -1,7 +1,17 @@
|
||||
#ifndef DNSServer_h
|
||||
#define DNSServer_h
|
||||
|
||||
#include <memory>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
// #define DEBUG_DNSSERVER
|
||||
|
||||
// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
|
||||
#ifndef IANA_DNS_PORT
|
||||
#define IANA_DNS_PORT 53 // AKA domain
|
||||
constexpr inline uint16_t kIanaDnsPort = IANA_DNS_PORT;
|
||||
#endif
|
||||
|
||||
#define DNS_QR_QUERY 0
|
||||
#define DNS_QR_RESPONSE 1
|
||||
#define DNS_OPCODE_QUERY 0
|
||||
@ -33,7 +43,7 @@ struct DNSHeader
|
||||
uint16_t ID; // identification number
|
||||
unsigned char RD : 1; // recursion desired
|
||||
unsigned char TC : 1; // truncated message
|
||||
unsigned char AA : 1; // authoritive answer
|
||||
unsigned char AA : 1; // authoritative answer
|
||||
unsigned char OPCode : 4; // message_type
|
||||
unsigned char QR : 1; // query/response flag
|
||||
unsigned char RCode : 4; // response code
|
||||
@ -45,6 +55,15 @@ struct DNSHeader
|
||||
uint16_t ARCount; // number of resource entries
|
||||
};
|
||||
|
||||
constexpr inline size_t kDNSSQueSizeAddrBits = 3; // The number of bits used to address que entries
|
||||
constexpr inline size_t kDNSSQueSize = BIT(kDNSSQueSizeAddrBits);
|
||||
|
||||
struct DNSS_REQUESTER {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
uint16_t id;
|
||||
};
|
||||
|
||||
class DNSServer
|
||||
{
|
||||
public:
|
||||
@ -52,24 +71,60 @@ class DNSServer
|
||||
~DNSServer() {
|
||||
stop();
|
||||
};
|
||||
/*
|
||||
If specified, `enableForwarder` will update the `domainName` that is used
|
||||
to match DNS request to this AP's IP Address. A non-matching request will
|
||||
be forwarded to the DNS server specified by `dns`.
|
||||
|
||||
Returns `true` on success.
|
||||
|
||||
Returns `false`,
|
||||
* when forwarding `dns` is not set, or
|
||||
* unable to allocate resources for managing the DNS forward function.
|
||||
*/
|
||||
bool enableForwarder(const String &domainName = emptyString, const IPAddress &dns = (uint32_t)0);
|
||||
/*
|
||||
`disableForwarder` will stop forwarding DNS requests. If specified,
|
||||
updates the `domainName` that is matched for returning this AP's IP Address.
|
||||
Optionally, resources used for the DNS forward function can be freed.
|
||||
*/
|
||||
void disableForwarder(const String &domainName = emptyString, bool freeResources = false);
|
||||
bool isForwarding() { return _forwarder && _dns.isSet(); }
|
||||
void setDNS(const IPAddress& dns) { _dns = dns; }
|
||||
IPAddress getDNS() { return _dns; }
|
||||
bool isDNSSet() { return _dns.isSet(); }
|
||||
|
||||
void processNextRequest();
|
||||
void setErrorReplyCode(const DNSReplyCode &replyCode);
|
||||
void setTTL(const uint32_t &ttl);
|
||||
uint32_t getTTL();
|
||||
String getDomainName() { return _domainName; }
|
||||
|
||||
// Returns true if successful, false if there are no sockets available
|
||||
bool start(const uint16_t &port,
|
||||
const String &domainName,
|
||||
const IPAddress &resolvedIP);
|
||||
const IPAddress &resolvedIP,
|
||||
const IPAddress &dns = (uint32_t)0);
|
||||
// stops the DNS server
|
||||
void stop();
|
||||
|
||||
private:
|
||||
WiFiUDP _udp;
|
||||
uint16_t _port;
|
||||
String _domainName;
|
||||
unsigned char _resolvedIP[4];
|
||||
IPAddress _dns;
|
||||
std::unique_ptr<DNSS_REQUESTER[]> _que;
|
||||
uint32_t _ttl;
|
||||
#ifdef DEBUG_DNSSERVER
|
||||
// There are 2 possiblities for OverFlow:
|
||||
// 1) we have more than kDNSSQueSize request already outstanding.
|
||||
// 2) we have request that never received a reply.
|
||||
uint32_t _que_ov;
|
||||
uint32_t _que_drop;
|
||||
#endif
|
||||
DNSReplyCode _errorReplyCode;
|
||||
bool _forwarder;
|
||||
unsigned char _resolvedIP[4];
|
||||
uint16_t _port;
|
||||
|
||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
||||
void replyWithIP(DNSHeader *dnsHeader,
|
||||
@ -81,7 +136,9 @@ class DNSServer
|
||||
size_t queryLength);
|
||||
void replyWithError(DNSHeader *dnsHeader,
|
||||
DNSReplyCode rcode);
|
||||
void respondToRequest(uint8_t *buffer, size_t length);
|
||||
bool respondToRequest(uint8_t *buffer, size_t length);
|
||||
void forwardRequest(uint8_t *buffer, size_t length);
|
||||
void forwardReply(uint8_t *buffer, size_t length);
|
||||
void writeNBOShort(uint16_t value);
|
||||
};
|
||||
#endif
|
||||
|
@ -31,7 +31,7 @@ extern "C" {
|
||||
#include "spi_flash.h"
|
||||
}
|
||||
|
||||
extern "C" uint32_t _EEPROM_start;
|
||||
#include <flash_hal.h>
|
||||
|
||||
EEPROMClass::EEPROMClass(uint32_t sector)
|
||||
: _sector(sector)
|
||||
@ -39,7 +39,7 @@ EEPROMClass::EEPROMClass(uint32_t sector)
|
||||
}
|
||||
|
||||
EEPROMClass::EEPROMClass(void)
|
||||
: _sector((((uint32_t)&_EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE))
|
||||
: _sector(((EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,7 @@
|
||||
void setup() {
|
||||
EEPROM.begin(512);
|
||||
// write a 0 to all 512 bytes of the EEPROM
|
||||
for (int i = 0; i < 512; i++) {
|
||||
EEPROM.write(i, 0);
|
||||
}
|
||||
for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); }
|
||||
|
||||
// turn the LED on when we're done
|
||||
pinMode(13, OUTPUT);
|
||||
@ -21,5 +19,4 @@ void setup() {
|
||||
EEPROM.end();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
void loop() {}
|
||||
|
@ -32,9 +32,7 @@ void loop() {
|
||||
|
||||
// there are only 512 bytes of EEPROM, from 0 to 511, so if we're
|
||||
// on address 512, wrap around to address 0
|
||||
if (address == 512) {
|
||||
address = 0;
|
||||
}
|
||||
if (address == 512) { address = 0; }
|
||||
|
||||
delay(500);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "esp8266-avrisp";
|
||||
@ -20,7 +20,7 @@ void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("");
|
||||
Serial.println("Arduino AVR-ISP over TCP");
|
||||
avrprog.setReset(false); // let the AVR run
|
||||
avrprog.setReset(false); // let the AVR run
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, pass);
|
||||
@ -51,17 +51,20 @@ void loop() {
|
||||
AVRISPState_t new_state = avrprog.update();
|
||||
if (last_state != new_state) {
|
||||
switch (new_state) {
|
||||
case AVRISP_STATE_IDLE: {
|
||||
case AVRISP_STATE_IDLE:
|
||||
{
|
||||
Serial.printf("[AVRISP] now idle\r\n");
|
||||
// Use the SPI bus for other purposes
|
||||
break;
|
||||
}
|
||||
case AVRISP_STATE_PENDING: {
|
||||
case AVRISP_STATE_PENDING:
|
||||
{
|
||||
Serial.printf("[AVRISP] connection pending\r\n");
|
||||
// Clean up your other purposes and prepare for programming mode
|
||||
break;
|
||||
}
|
||||
case AVRISP_STATE_ACTIVE: {
|
||||
case AVRISP_STATE_ACTIVE:
|
||||
{
|
||||
Serial.printf("[AVRISP] programming mode\r\n");
|
||||
// Stand by for completion
|
||||
break;
|
||||
@ -70,11 +73,7 @@ void loop() {
|
||||
last_state = new_state;
|
||||
}
|
||||
// Serve the client
|
||||
if (last_state != AVRISP_STATE_IDLE) {
|
||||
avrprog.serve();
|
||||
}
|
||||
if (last_state != AVRISP_STATE_IDLE) { avrprog.serve(); }
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
MDNS.update();
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) { MDNS.update(); }
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ AVRISPState_t ESP8266AVRISP::update() {
|
||||
switch (_state) {
|
||||
case AVRISP_STATE_IDLE: {
|
||||
if (_server.hasClient()) {
|
||||
_client = _server.available();
|
||||
_client = _server.accept();
|
||||
_client.setNoDelay(true);
|
||||
AVRISP_DEBUG("client connect %s:%d", _client.remoteIP().toString().c_str(), _client.remotePort());
|
||||
_client.setTimeout(100); // for getch()
|
||||
@ -121,7 +121,7 @@ AVRISPState_t ESP8266AVRISP::serve() {
|
||||
}
|
||||
|
||||
inline void ESP8266AVRISP::_reject_incoming(void) {
|
||||
while (_server.hasClient()) _server.available().stop();
|
||||
while (_server.hasClient()) _server.accept().stop();
|
||||
}
|
||||
|
||||
uint8_t ESP8266AVRISP::getch() {
|
||||
@ -189,7 +189,7 @@ void ESP8266AVRISP::get_parameter(uint8_t c) {
|
||||
}
|
||||
|
||||
void ESP8266AVRISP::set_parameters() {
|
||||
// call this after reading paramter packet into buff[]
|
||||
// call this after reading parameter packet into buff[]
|
||||
param.devicecode = buff[0];
|
||||
param.revision = buff[1];
|
||||
param.progtype = buff[2];
|
||||
|
@ -33,7 +33,6 @@ void setup() {
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
@ -33,7 +33,6 @@ void setup() {
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include <WiFiClientSecureBearSSL.h>
|
||||
// Fingerprint for demo URL, expires on June 2, 2021, needs to be updated well before this date
|
||||
const uint8_t fingerprint[20] = {0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3};
|
||||
const uint8_t fingerprint[20] = { 0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3 };
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
@ -41,7 +41,7 @@ void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
|
||||
std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure);
|
||||
|
||||
client->setFingerprint(fingerprint);
|
||||
// Or, if you happy to ignore the SSL certificate, then use the following line instead:
|
||||
|
@ -13,36 +13,31 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssidPassword = STAPSK;
|
||||
|
||||
const char *username = "admin";
|
||||
const char *password = "admin";
|
||||
const char* username = "admin";
|
||||
const char* password = "admin";
|
||||
|
||||
const char *server = "http://httpbin.org";
|
||||
const char *uri = "/digest-auth/auth/admin/admin/MD5";
|
||||
const char* server = "http://httpbin.org";
|
||||
const char* uri = "/digest-auth/auth/admin/admin/MD5";
|
||||
|
||||
String exractParam(String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1) {
|
||||
return "";
|
||||
}
|
||||
if (_begin == -1) { return ""; }
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
String getCNonce(const int len) {
|
||||
static const char alphanum[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s = "";
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
for (int i = 0; i < len; ++i) { s += alphanum[rand() % (sizeof(alphanum) - 1)]; }
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -73,8 +68,7 @@ String getDigestAuth(String& authReq, const String& username, const String& pass
|
||||
md5.calculate();
|
||||
String response = md5.toString();
|
||||
|
||||
String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce +
|
||||
"\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\"";
|
||||
String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\"";
|
||||
Serial.println(authorization);
|
||||
|
||||
return authorization;
|
||||
@ -100,7 +94,7 @@ void setup() {
|
||||
|
||||
void loop() {
|
||||
WiFiClient client;
|
||||
HTTPClient http; //must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...)
|
||||
HTTPClient http; // must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...)
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
|
||||
@ -108,7 +102,7 @@ void loop() {
|
||||
http.begin(client, String(server) + String(uri));
|
||||
|
||||
|
||||
const char *keys[] = {"WWW-Authenticate"};
|
||||
const char* keys[] = { "WWW-Authenticate" };
|
||||
http.collectHeaders(keys, 1);
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
@ -40,7 +40,6 @@ void setup() {
|
||||
Serial.println("");
|
||||
Serial.print("Connected! IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@ -52,7 +51,7 @@ void loop() {
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
// configure traged server and url
|
||||
http.begin(client, "http://" SERVER_IP "/postplain/"); //HTTP
|
||||
http.begin(client, "http://" SERVER_IP "/postplain/"); // HTTP
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
|
||||
Serial.print("[HTTP] POST...\n");
|
||||
|
@ -1,67 +0,0 @@
|
||||
/**
|
||||
reuseConnection.ino
|
||||
|
||||
Created on: 22.11.2015
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <ESP8266HTTPClient.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
// Serial.setDebugOutput(true);
|
||||
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
|
||||
for (uint8_t t = 4; t > 0; t--) {
|
||||
Serial.printf("[SETUP] WAIT %d...\n", t);
|
||||
Serial.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
// allow reuse (if server supports it)
|
||||
http.setReuse(true);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
|
||||
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode > 0) {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
http.writeToStream(&Serial);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
@ -45,7 +45,7 @@ void setup() {
|
||||
|
||||
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
|
||||
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
int pass = 0;
|
||||
@ -60,15 +60,13 @@ void loop() {
|
||||
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
http.writeToStream(&Serial);
|
||||
}
|
||||
if (httpCode == HTTP_CODE_OK) { http.writeToStream(&Serial); }
|
||||
} else {
|
||||
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
// Something went wrong with the connection, try to reconnect
|
||||
http.end();
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
|
||||
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
}
|
||||
|
||||
if (pass == 10) {
|
||||
|
@ -31,7 +31,6 @@ void setup() {
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@ -39,13 +38,13 @@ void loop() {
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
WiFiClient client;
|
||||
HTTPClient http; //must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...)
|
||||
HTTPClient http; // must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...)
|
||||
|
||||
Serial.print("[HTTP] begin...\n");
|
||||
|
||||
// configure server and url
|
||||
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
|
||||
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
// http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
|
||||
|
||||
Serial.print("[HTTP] GET...\n");
|
||||
// start connection and send HTTP header
|
||||
@ -57,7 +56,7 @@ void loop() {
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
|
||||
// get lenght of document (is -1 when Server sends no Content-Length header)
|
||||
// get length of document (is -1 when Server sends no Content-Length header)
|
||||
int len = http.getSize();
|
||||
|
||||
// create buffer for read
|
||||
@ -70,23 +69,19 @@ void loop() {
|
||||
// or "by hand"
|
||||
|
||||
// get tcp stream
|
||||
WiFiClient * stream = &client;
|
||||
WiFiClient* stream = &client;
|
||||
|
||||
// read all data from server
|
||||
while (http.connected() && (len > 0 || len == -1)) {
|
||||
// read up to 128 byte
|
||||
int c = stream->readBytes(buff, std::min((size_t)len, sizeof(buff)));
|
||||
Serial.printf("readBytes: %d\n", c);
|
||||
if (!c) {
|
||||
Serial.println("read timeout");
|
||||
}
|
||||
if (!c) { Serial.println("read timeout"); }
|
||||
|
||||
// write it to Serial
|
||||
Serial.write(buff, c);
|
||||
|
||||
if (len > 0) {
|
||||
len -= c;
|
||||
}
|
||||
if (len > 0) { len -= c; }
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -31,7 +31,6 @@ void setup() {
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@ -43,14 +42,12 @@ void loop() {
|
||||
bool mfln = client->probeMaxFragmentLength("tls.mbed.org", 443, 1024);
|
||||
Serial.printf("\nConnecting to https://tls.mbed.org\n");
|
||||
Serial.printf("Maximum fragment Length negotiation supported: %s\n", mfln ? "yes" : "no");
|
||||
if (mfln) {
|
||||
client->setBufferSizes(1024, 1024);
|
||||
}
|
||||
if (mfln) { client->setBufferSizes(1024, 1024); }
|
||||
|
||||
Serial.print("[HTTPS] begin...\n");
|
||||
|
||||
// configure server and url
|
||||
const uint8_t fingerprint[20] = {0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a};
|
||||
const uint8_t fingerprint[20] = { 0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a };
|
||||
|
||||
client->setFingerprint(fingerprint);
|
||||
|
||||
@ -68,7 +65,7 @@ void loop() {
|
||||
// file found at server
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
|
||||
// get lenght of document (is -1 when Server sends no Content-Length header)
|
||||
// get length of document (is -1 when Server sends no Content-Length header)
|
||||
int len = https.getSize();
|
||||
|
||||
// create buffer for read
|
||||
@ -86,16 +83,13 @@ void loop() {
|
||||
// write it to Serial
|
||||
Serial.write(buff, c);
|
||||
|
||||
if (len > 0) {
|
||||
len -= c;
|
||||
}
|
||||
if (len > 0) { len -= c; }
|
||||
}
|
||||
delay(1);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("[HTTPS] connection closed or file end.\n");
|
||||
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
|
||||
|
@ -22,12 +22,23 @@
|
||||
*
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <coredecls.h>
|
||||
|
||||
#include "ESP8266HTTPClient.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <StreamDev.h>
|
||||
#include <base64.h>
|
||||
|
||||
// per https://github.com/esp8266/Arduino/issues/8231
|
||||
// make sure HTTPClient can be utilized as a movable class member
|
||||
static_assert(std::is_default_constructible_v<HTTPClient>, "");
|
||||
static_assert(!std::is_copy_constructible_v<HTTPClient>, "");
|
||||
static_assert(std::is_move_constructible_v<HTTPClient>, "");
|
||||
static_assert(std::is_move_assignable_v<HTTPClient>, "");
|
||||
|
||||
static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient";
|
||||
const String HTTPClient::defaultUserAgent = defaultUserAgentPstr;
|
||||
|
||||
static int StreamReportToHttpClientReport (Stream::Report streamSendError)
|
||||
{
|
||||
switch (streamSendError)
|
||||
@ -41,27 +52,6 @@ static int StreamReportToHttpClientReport (Stream::Report streamSendError)
|
||||
return 0; // never reached, keep gcc quiet
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
HTTPClient::HTTPClient()
|
||||
: _client(nullptr), _userAgent(F("ESP8266HTTPClient"))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* destructor
|
||||
*/
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
if(_client) {
|
||||
_client->stop();
|
||||
}
|
||||
if(_currentHeaders) {
|
||||
delete[] _currentHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPClient::clear()
|
||||
{
|
||||
_returnCode = 0;
|
||||
@ -80,8 +70,6 @@ void HTTPClient::clear()
|
||||
* @return success bool
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, const String& url) {
|
||||
_client = &client;
|
||||
|
||||
// check for : (http: or https:)
|
||||
int index = url.indexOf(':');
|
||||
if(index < 0) {
|
||||
@ -90,12 +78,15 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) {
|
||||
}
|
||||
|
||||
String protocol = url.substring(0, index);
|
||||
protocol.toLowerCase();
|
||||
if(protocol != "http" && protocol != "https") {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][begin] unknown protocol '%s'\n", protocol.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_port = (protocol == "https" ? 443 : 80);
|
||||
_client = client.clone();
|
||||
|
||||
return beginInternal(url, protocol.c_str());
|
||||
}
|
||||
|
||||
@ -111,9 +102,16 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) {
|
||||
*/
|
||||
bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, const String& uri, bool https)
|
||||
{
|
||||
_client = &client;
|
||||
// Disconnect when reusing HTTPClient to talk to a different host
|
||||
if (!_host.isEmpty() && _host != host) {
|
||||
_canReuse = false;
|
||||
disconnect(true);
|
||||
}
|
||||
|
||||
_client = client.clone();
|
||||
|
||||
clear();
|
||||
|
||||
clear();
|
||||
_host = host;
|
||||
_port = port;
|
||||
_uri = uri;
|
||||
@ -137,6 +135,7 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol
|
||||
}
|
||||
|
||||
_protocol = url.substring(0, index);
|
||||
_protocol.toLowerCase();
|
||||
url.remove(0, (index + 3)); // remove http:// or https://
|
||||
|
||||
if (_protocol == "http") {
|
||||
@ -163,6 +162,8 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol
|
||||
_base64Authorization = base64::encode(auth, false /* doNewLines */);
|
||||
}
|
||||
|
||||
const String oldHost = _host;
|
||||
|
||||
// get port
|
||||
index = host.indexOf(':');
|
||||
if(index >= 0) {
|
||||
@ -172,6 +173,13 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol
|
||||
} else {
|
||||
_host = host;
|
||||
}
|
||||
|
||||
// Disconnect when reusing HTTPClient to talk to a different host
|
||||
if (!oldHost.isEmpty() && _host != oldHost) {
|
||||
_canReuse = false;
|
||||
disconnect(true);
|
||||
}
|
||||
|
||||
_uri = url;
|
||||
|
||||
if ( expectedProtocol != nullptr && _protocol != expectedProtocol) {
|
||||
@ -274,17 +282,26 @@ void HTTPClient::setAuthorization(const char * user, const char * password)
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorizatio for the http request
|
||||
* set the Authorization for the http request
|
||||
* @param auth const char * base64
|
||||
*/
|
||||
void HTTPClient::setAuthorization(const char * auth)
|
||||
{
|
||||
if(auth) {
|
||||
_base64Authorization = auth;
|
||||
_base64Authorization.replace(String('\n'), emptyString);
|
||||
if (auth) {
|
||||
setAuthorization(String(auth));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorization for the http request
|
||||
* @param auth String base64
|
||||
*/
|
||||
void HTTPClient::setAuthorization(String auth)
|
||||
{
|
||||
_base64Authorization = std::move(auth);
|
||||
_base64Authorization.replace(String('\n'), emptyString);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the timeout for the TCP connection
|
||||
* @param timeout unsigned int
|
||||
@ -321,7 +338,7 @@ bool HTTPClient::setURL(const String& url)
|
||||
}
|
||||
|
||||
/**
|
||||
* set redirect follow mode. See `followRedirects_t` enum for avaliable modes.
|
||||
* set redirect follow mode. See `followRedirects_t` enum for available modes.
|
||||
* @param follow
|
||||
*/
|
||||
void HTTPClient::setFollowRedirects(followRedirects_t follow)
|
||||
@ -352,6 +369,14 @@ int HTTPClient::GET()
|
||||
{
|
||||
return sendRequest("GET");
|
||||
}
|
||||
/**
|
||||
* send a DELETE request
|
||||
* @return http code
|
||||
*/
|
||||
int HTTPClient::DELETE()
|
||||
{
|
||||
return sendRequest("DELETE");
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a post request to the server
|
||||
@ -443,7 +468,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s
|
||||
}
|
||||
|
||||
// transfer all of it, with send-timeout
|
||||
if (size && StreamConstPtr(payload, size).sendAll(_client) != size)
|
||||
if (size && StreamConstPtr(payload, size).sendAll(_client.get()) != size)
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
|
||||
// handle Server Response (Header)
|
||||
@ -481,7 +506,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s
|
||||
// no redirection
|
||||
break;
|
||||
}
|
||||
// redirect using the same request method and payload, diffrent URL
|
||||
// redirect using the same request method and payload, different URL
|
||||
redirect = true;
|
||||
}
|
||||
break;
|
||||
@ -544,10 +569,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
}
|
||||
|
||||
// transfer all of it, with timeout
|
||||
size_t transferred = stream->sendSize(_client, size);
|
||||
size_t transferred = stream->sendSize(_client.get(), size);
|
||||
if (transferred != size)
|
||||
{
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", size, transferred);
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %zu but got %zu failed.\n", size, transferred);
|
||||
esp_yield();
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
|
||||
@ -594,7 +620,7 @@ WiFiClient& HTTPClient::getStream(void)
|
||||
WiFiClient* HTTPClient::getStreamPtr(void)
|
||||
{
|
||||
if(connected()) {
|
||||
return _client;
|
||||
return _client.get();
|
||||
}
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n");
|
||||
@ -608,8 +634,18 @@ WiFiClient* HTTPClient::getStreamPtr(void)
|
||||
*/
|
||||
int HTTPClient::writeToStream(Stream * stream)
|
||||
{
|
||||
return writeToPrint(stream);
|
||||
}
|
||||
|
||||
if(!stream) {
|
||||
/**
|
||||
* write all message body / payload to Print
|
||||
* @param print Print *
|
||||
* @return bytes written ( negative values are error codes )
|
||||
*/
|
||||
int HTTPClient::writeToPrint(Print * print)
|
||||
{
|
||||
|
||||
if(!print) {
|
||||
return returnError(HTTPC_ERROR_NO_STREAM);
|
||||
}
|
||||
|
||||
@ -626,7 +662,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
if(_transferEncoding == HTTPC_TE_IDENTITY) {
|
||||
// len < 0: transfer all of it, with timeout
|
||||
// len >= 0: max:len, with timeout
|
||||
ret = _client->sendSize(stream, len);
|
||||
ret = _client->sendSize(print, len);
|
||||
|
||||
// do we have an error?
|
||||
if(_client->getLastSendReport() != Stream::Report::Success) {
|
||||
@ -654,7 +690,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
// data left?
|
||||
if(len > 0) {
|
||||
// read len bytes with timeout
|
||||
int r = _client->sendSize(stream, len);
|
||||
int r = _client->sendSize(print, len);
|
||||
if (_client->getLastSendReport() != Stream::Report::Success)
|
||||
// not all data transferred
|
||||
return returnError(StreamReportToHttpClientReport(_client->getLastSendReport()));
|
||||
@ -680,7 +716,7 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
return returnError(HTTPC_ERROR_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
delay(0);
|
||||
esp_yield();
|
||||
}
|
||||
} else {
|
||||
return returnError(HTTPC_ERROR_ENCODING);
|
||||
@ -703,7 +739,7 @@ const String& HTTPClient::getString(void)
|
||||
_payload.reset(new StreamString());
|
||||
|
||||
if(_size > 0) {
|
||||
// try to reserve needed memmory
|
||||
// try to reserve needed memory
|
||||
if(!_payload->reserve((_size + 1))) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1));
|
||||
return *_payload;
|
||||
@ -789,10 +825,7 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first,
|
||||
void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount)
|
||||
{
|
||||
_headerKeysCount = headerKeysCount;
|
||||
if(_currentHeaders) {
|
||||
delete[] _currentHeaders;
|
||||
}
|
||||
_currentHeaders = new RequestArgument[_headerKeysCount];
|
||||
_currentHeaders = std::make_unique<RequestArgument[]>(_headerKeysCount);
|
||||
for(size_t i = 0; i < _headerKeysCount; i++) {
|
||||
_currentHeaders[i].key = headerKeys[i];
|
||||
}
|
||||
@ -847,6 +880,10 @@ bool HTTPClient::connect(void)
|
||||
{
|
||||
if(_reuse && _canReuse && connected()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, reusing connection\n");
|
||||
|
||||
#if defined(NO_GLOBAL_INSTANCES) || defined(NO_GLOBAL_STREAMDEV)
|
||||
StreamNull devnull;
|
||||
#endif
|
||||
_client->sendAvailable(devnull); // clear _client's output (all of it, no timeout)
|
||||
return true;
|
||||
}
|
||||
@ -908,8 +945,10 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
header += ':';
|
||||
header += String(_port);
|
||||
}
|
||||
header += F("\r\nUser-Agent: ");
|
||||
header += _userAgent;
|
||||
if (_userAgent.length()) {
|
||||
header += F("\r\nUser-Agent: ");
|
||||
header += _userAgent;
|
||||
}
|
||||
|
||||
if (!_useHTTP10) {
|
||||
header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0");
|
||||
@ -930,7 +969,7 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str());
|
||||
|
||||
// transfer all of it, with timeout
|
||||
return StreamConstPtr(header).sendAll(_client) == header.length();
|
||||
return StreamConstPtr(header).sendAll(_client.get()) == header.length();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1041,7 +1080,7 @@ int HTTPClient::handleHeaderResponse()
|
||||
if((millis() - lastDataTime) > _tcpTimeout) {
|
||||
return HTTPC_ERROR_READ_TIMEOUT;
|
||||
}
|
||||
delay(0);
|
||||
esp_yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,12 @@
|
||||
#ifndef ESP8266HTTPClient_H_
|
||||
#define ESP8266HTTPClient_H_
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <StreamString.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_CLIENT
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||
@ -151,17 +152,16 @@ typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
HTTPClient() = default;
|
||||
~HTTPClient() = default;
|
||||
HTTPClient(HTTPClient&&) = default;
|
||||
HTTPClient& operator=(HTTPClient&&) = default;
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClient
|
||||
*/
|
||||
// Note that WiFiClient's underlying connection *will* be captured
|
||||
bool begin(WiFiClient &client, const String& url);
|
||||
bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false);
|
||||
|
||||
// old API is now explicitely forbidden
|
||||
// old API is now explicitly forbidden
|
||||
bool begin(String url) __attribute__ ((error("obsolete API, use ::begin(WiFiClient, url)")));
|
||||
bool begin(String host, uint16_t port, String uri = "/") __attribute__ ((error("obsolete API, use ::begin(WiFiClient, host, port, uri)")));
|
||||
bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__ ((error("obsolete API, use ::begin(WiFiClientSecure, ...)")));
|
||||
@ -176,6 +176,7 @@ public:
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setAuthorization(String auth);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
@ -187,6 +188,7 @@ public:
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int DELETE();
|
||||
int POST(const uint8_t* payload, size_t size);
|
||||
int POST(const String& payload);
|
||||
int PUT(const uint8_t* payload, size_t size);
|
||||
@ -213,6 +215,7 @@ public:
|
||||
|
||||
WiFiClient& getStream(void);
|
||||
WiFiClient* getStreamPtr(void);
|
||||
int writeToPrint(Print* print);
|
||||
int writeToStream(Stream* stream);
|
||||
const String& getString(void);
|
||||
static String errorToString(int error);
|
||||
@ -232,7 +235,15 @@ protected:
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
WiFiClient* _client;
|
||||
// The common pattern to use the class is to
|
||||
// {
|
||||
// WiFiClient socket;
|
||||
// HTTPClient http;
|
||||
// http.begin(socket, "http://blahblah");
|
||||
// }
|
||||
// Make sure it's not possible to break things in an opposite direction
|
||||
|
||||
std::unique_ptr<WiFiClient> _client;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
@ -244,12 +255,14 @@ protected:
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _userAgent;
|
||||
String _base64Authorization;
|
||||
|
||||
static const String defaultUserAgent;
|
||||
String _userAgent = defaultUserAgent;
|
||||
|
||||
/// Response handling
|
||||
RequestArgument* _currentHeaders = nullptr;
|
||||
size_t _headerKeysCount = 0;
|
||||
std::unique_ptr<RequestArgument[]> _currentHeaders;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
@ -261,6 +274,4 @@ protected:
|
||||
std::unique_ptr<StreamString> _payload;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* ESP8266HTTPClient_H_ */
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "esp8266-webupdate";
|
||||
@ -57,7 +57,7 @@ JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
@ -88,8 +88,7 @@ gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
|
||||
)EOF";
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
@ -97,7 +96,7 @@ void setup()
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while(WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
@ -111,13 +110,13 @@ void setup()
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("https", "tcp", 443);
|
||||
Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in "\
|
||||
"your browser and login with username '%s' and password "\
|
||||
"'%s'\n", host, update_path, update_username, update_password);
|
||||
Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in "
|
||||
"your browser and login with username '%s' and password "
|
||||
"'%s'\n",
|
||||
host, update_path, update_username, update_password);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
void loop() {
|
||||
httpServer.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "esp8266-webupdate";
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "esp8266-webupdate";
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <Arduino.h>
|
||||
#include <coredecls.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
@ -72,7 +73,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
ESP.restart();
|
||||
}
|
||||
},[&](){
|
||||
// handler for the file upload, get's the sketch bytes, and writes
|
||||
// handler for the file upload, gets the sketch bytes, and writes
|
||||
// them through the Update object
|
||||
HTTPUpload& upload = _server->upload();
|
||||
|
||||
@ -92,7 +93,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
if (_serial_output)
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
if (upload.name == "filesystem") {
|
||||
size_t fsSize = ((size_t) &_FS_end - (size_t) &_FS_start);
|
||||
size_t fsSize = ((size_t)FS_end - (size_t)FS_start);
|
||||
close_all_fs();
|
||||
if (!Update.begin(fsSize, U_FS)){//start with max available size
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
@ -110,7 +111,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
}
|
||||
} else if(_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()){
|
||||
if(Update.end(true)){ //true to set the size to the current progress
|
||||
if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
if (_serial_output) Serial.printf("Update Success: %zu\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
_setUpdaterError();
|
||||
}
|
||||
@ -119,7 +120,7 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
||||
Update.end();
|
||||
if (_serial_output) Serial.println("Update was aborted");
|
||||
}
|
||||
delay(0);
|
||||
esp_yield();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Version 1.1
|
||||
* Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||
* ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
* MDNS-SD Suport 2015 Hristo Gochkov
|
||||
* MDNS-SD Support 2015 Hristo Gochkov
|
||||
* Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||
*
|
||||
* License (MIT license):
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Version 1.1
|
||||
* Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||
* ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
* MDNS-SD Suport 2015 Hristo Gochkov
|
||||
* MDNS-SD Support 2015 Hristo Gochkov
|
||||
* Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||
*
|
||||
* License (MIT license):
|
||||
|
@ -25,7 +25,7 @@ Usage
|
||||
file.
|
||||
2. Include the ESP8266LLMNR library in the sketch.
|
||||
3. Call the LLMNR.begin() method in the sketch's setup() function, and provide
|
||||
the hostname to advertize. This should not include any ".local" prefix.
|
||||
the hostname to advertise. This should not include any ".local" prefix.
|
||||
4. If ESP8266 AP mode is enabled, disabled, or the WiFi or AP configuration is
|
||||
changed, call LLMNR.notify_ap_change() after the change is made.
|
||||
|
||||
@ -70,7 +70,7 @@ ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
|
||||
Version 1.1
|
||||
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
MDNS-SD Suport 2015 Hristo Gochkov
|
||||
MDNS-SD Support 2015 Hristo Gochkov
|
||||
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -7,7 +7,7 @@
|
||||
Version 1.1
|
||||
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||
MDNS-SD Suport 2015 Hristo Gochkov
|
||||
MDNS-SD Support 2015 Hristo Gochkov
|
||||
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||
|
||||
License (MIT license):
|
||||
@ -62,7 +62,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
|
2
libraries/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino
Executable file → Normal file
2
libraries/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino
Executable file → Normal file
@ -4,7 +4,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -46,9 +46,7 @@ void setup() {
|
||||
Serial.printf("Ready!\n");
|
||||
} else {
|
||||
Serial.printf("WiFi Failed\n");
|
||||
while (1) {
|
||||
delay(100);
|
||||
}
|
||||
while (1) { delay(100); }
|
||||
}
|
||||
}
|
||||
|
||||
|
Submodule libraries/ESP8266SdFat updated: 9f94df41cc...43a65b42d5
@ -35,7 +35,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -69,8 +69,7 @@ void handleRoot() {
|
||||
</body>\
|
||||
</html>",
|
||||
|
||||
hr, min % 60, sec % 60
|
||||
);
|
||||
hr, min % 60, sec % 60);
|
||||
server.send(200, "text/html", temp);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
@ -86,9 +85,7 @@ void handleNotFound() {
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
@ -133,9 +130,7 @@ void setup(void) {
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.on("/", handleRoot);
|
||||
server.on("/test.svg", drawGraph);
|
||||
@ -151,4 +146,3 @@ void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ SDFSConfig fileSystemConfig = SDFSConfig();
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -122,15 +122,9 @@ void replyServerError(String msg) {
|
||||
*/
|
||||
String checkForUnsupportedPath(String filename) {
|
||||
String error = String();
|
||||
if (!filename.startsWith("/")) {
|
||||
error += F("!NO_LEADING_SLASH! ");
|
||||
}
|
||||
if (filename.indexOf("//") != -1) {
|
||||
error += F("!DOUBLE_SLASH! ");
|
||||
}
|
||||
if (filename.endsWith("/")) {
|
||||
error += F("!TRAILING_SLASH! ");
|
||||
}
|
||||
if (!filename.startsWith("/")) { error += F("!NO_LEADING_SLASH! "); }
|
||||
if (filename.indexOf("//") != -1) { error += F("!DOUBLE_SLASH! "); }
|
||||
if (filename.endsWith("/")) { error += F("!TRAILING_SLASH! "); }
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
@ -171,21 +165,15 @@ void handleStatus() {
|
||||
|
||||
/*
|
||||
Return the list of files in the directory specified by the "dir" query string parameter.
|
||||
Also demonstrates the use of chuncked responses.
|
||||
Also demonstrates the use of chunked responses.
|
||||
*/
|
||||
void handleFileList() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); }
|
||||
|
||||
if (!server.hasArg("dir")) {
|
||||
return replyBadRequest(F("DIR ARG MISSING"));
|
||||
}
|
||||
if (!server.hasArg("dir")) { return replyBadRequest(F("DIR ARG MISSING")); }
|
||||
|
||||
String path = server.arg("dir");
|
||||
if (path != "/" && !fileSystem->exists(path)) {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
if (path != "/" && !fileSystem->exists(path)) { return replyBadRequest("BAD PATH"); }
|
||||
|
||||
DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
|
||||
Dir dir = fileSystem->openDir(path);
|
||||
@ -253,9 +241,7 @@ bool handleFileRead(String path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.endsWith("/")) {
|
||||
path += "index.htm";
|
||||
}
|
||||
if (path.endsWith("/")) { path += "index.htm"; }
|
||||
|
||||
String contentType;
|
||||
if (server.hasArg("download")) {
|
||||
@ -270,9 +256,7 @@ bool handleFileRead(String path) {
|
||||
}
|
||||
if (fileSystem->exists(path)) {
|
||||
File file = fileSystem->open(path, "r");
|
||||
if (server.streamFile(file, contentType) != file.size()) {
|
||||
DBG_OUTPUT_PORT.println("Sent less data than expected!");
|
||||
}
|
||||
if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); }
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
@ -309,27 +293,17 @@ String lastExistingParent(String path) {
|
||||
Move folder | parent of source folder, or remaining ancestor
|
||||
*/
|
||||
void handleFileCreate() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); }
|
||||
|
||||
String path = server.arg("path");
|
||||
if (path.isEmpty()) {
|
||||
return replyBadRequest(F("PATH ARG MISSING"));
|
||||
}
|
||||
if (path.isEmpty()) { return replyBadRequest(F("PATH ARG MISSING")); }
|
||||
|
||||
#ifdef USE_SPIFFS
|
||||
if (checkForUnsupportedPath(path).length() > 0) {
|
||||
return replyServerError(F("INVALID FILENAME"));
|
||||
}
|
||||
if (checkForUnsupportedPath(path).length() > 0) { return replyServerError(F("INVALID FILENAME")); }
|
||||
#endif
|
||||
|
||||
if (path == "/") {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
if (fileSystem->exists(path)) {
|
||||
return replyBadRequest(F("PATH FILE EXISTS"));
|
||||
}
|
||||
if (path == "/") { return replyBadRequest("BAD PATH"); }
|
||||
if (fileSystem->exists(path)) { return replyBadRequest(F("PATH FILE EXISTS")); }
|
||||
|
||||
String src = server.arg("src");
|
||||
if (src.isEmpty()) {
|
||||
@ -338,43 +312,29 @@ void handleFileCreate() {
|
||||
if (path.endsWith("/")) {
|
||||
// Create a folder
|
||||
path.remove(path.length() - 1);
|
||||
if (!fileSystem->mkdir(path)) {
|
||||
return replyServerError(F("MKDIR FAILED"));
|
||||
}
|
||||
if (!fileSystem->mkdir(path)) { return replyServerError(F("MKDIR FAILED")); }
|
||||
} else {
|
||||
// Create a file
|
||||
File file = fileSystem->open(path, "w");
|
||||
if (file) {
|
||||
file.write((const char *)0);
|
||||
file.write((const char*)0);
|
||||
file.close();
|
||||
} else {
|
||||
return replyServerError(F("CREATE FAILED"));
|
||||
}
|
||||
}
|
||||
if (path.lastIndexOf('/') > -1) {
|
||||
path = path.substring(0, path.lastIndexOf('/'));
|
||||
}
|
||||
if (path.lastIndexOf('/') > -1) { path = path.substring(0, path.lastIndexOf('/')); }
|
||||
replyOKWithMsg(path);
|
||||
} else {
|
||||
// Source specified: rename
|
||||
if (src == "/") {
|
||||
return replyBadRequest("BAD SRC");
|
||||
}
|
||||
if (!fileSystem->exists(src)) {
|
||||
return replyBadRequest(F("SRC FILE NOT FOUND"));
|
||||
}
|
||||
if (src == "/") { return replyBadRequest("BAD SRC"); }
|
||||
if (!fileSystem->exists(src)) { return replyBadRequest(F("SRC FILE NOT FOUND")); }
|
||||
|
||||
DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
|
||||
|
||||
if (path.endsWith("/")) {
|
||||
path.remove(path.length() - 1);
|
||||
}
|
||||
if (src.endsWith("/")) {
|
||||
src.remove(src.length() - 1);
|
||||
}
|
||||
if (!fileSystem->rename(src, path)) {
|
||||
return replyServerError(F("RENAME FAILED"));
|
||||
}
|
||||
if (path.endsWith("/")) { path.remove(path.length() - 1); }
|
||||
if (src.endsWith("/")) { src.remove(src.length() - 1); }
|
||||
if (!fileSystem->rename(src, path)) { return replyServerError(F("RENAME FAILED")); }
|
||||
replyOKWithMsg(lastExistingParent(src));
|
||||
}
|
||||
}
|
||||
@ -403,9 +363,7 @@ void deleteRecursive(String path) {
|
||||
// Otherwise delete its contents first
|
||||
Dir dir = fileSystem->openDir(path);
|
||||
|
||||
while (dir.next()) {
|
||||
deleteRecursive(path + '/' + dir.fileName());
|
||||
}
|
||||
while (dir.next()) { deleteRecursive(path + '/' + dir.fileName()); }
|
||||
|
||||
// Then delete the folder itself
|
||||
fileSystem->rmdir(path);
|
||||
@ -420,19 +378,13 @@ void deleteRecursive(String path) {
|
||||
Delete folder | parent of deleted folder, or remaining ancestor
|
||||
*/
|
||||
void handleFileDelete() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); }
|
||||
|
||||
String path = server.arg(0);
|
||||
if (path.isEmpty() || path == "/") {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
if (path.isEmpty() || path == "/") { return replyBadRequest("BAD PATH"); }
|
||||
|
||||
DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
|
||||
if (!fileSystem->exists(path)) {
|
||||
return replyNotFound(FPSTR(FILE_NOT_FOUND));
|
||||
}
|
||||
if (!fileSystem->exists(path)) { return replyNotFound(FPSTR(FILE_NOT_FOUND)); }
|
||||
deleteRecursive(path);
|
||||
|
||||
replyOKWithMsg(lastExistingParent(path));
|
||||
@ -442,57 +394,41 @@ void handleFileDelete() {
|
||||
Handle a file upload request
|
||||
*/
|
||||
void handleFileUpload() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
if (server.uri() != "/edit") {
|
||||
return;
|
||||
}
|
||||
if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); }
|
||||
if (server.uri() != "/edit") { return; }
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
String filename = upload.filename;
|
||||
// Make sure paths always start with "/"
|
||||
if (!filename.startsWith("/")) {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
if (!filename.startsWith("/")) { filename = "/" + filename; }
|
||||
DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
|
||||
uploadFile = fileSystem->open(filename, "w");
|
||||
if (!uploadFile) {
|
||||
return replyServerError(F("CREATE FAILED"));
|
||||
}
|
||||
if (!uploadFile) { return replyServerError(F("CREATE FAILED")); }
|
||||
DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (uploadFile) {
|
||||
size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize);
|
||||
if (bytesWritten != upload.currentSize) {
|
||||
return replyServerError(F("WRITE FAILED"));
|
||||
}
|
||||
if (bytesWritten != upload.currentSize) { return replyServerError(F("WRITE FAILED")); }
|
||||
}
|
||||
DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (uploadFile) {
|
||||
uploadFile.close();
|
||||
}
|
||||
if (uploadFile) { uploadFile.close(); }
|
||||
DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The "Not Found" handler catches all URI not explicitely declared in code
|
||||
The "Not Found" handler catches all URI not explicitly declared in code
|
||||
First try to find and return the requested file from the filesystem,
|
||||
and if it fails, return a 404 page with debug information
|
||||
*/
|
||||
void handleNotFound() {
|
||||
if (!fsOK) {
|
||||
return replyServerError(FPSTR(FS_INIT_ERROR));
|
||||
}
|
||||
if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); }
|
||||
|
||||
String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
|
||||
String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
|
||||
|
||||
if (handleFileRead(uri)) {
|
||||
return;
|
||||
}
|
||||
if (handleFileRead(uri)) { return; }
|
||||
|
||||
// Dump debug data
|
||||
String message;
|
||||
@ -526,9 +462,7 @@ void handleNotFound() {
|
||||
Otherwise, fails with a 404 page with debug information
|
||||
*/
|
||||
void handleGetEdit() {
|
||||
if (handleFileRead(F("/edit/index.htm"))) {
|
||||
return;
|
||||
}
|
||||
if (handleFileRead(F("/edit/index.htm"))) { return; }
|
||||
|
||||
#ifdef INCLUDE_FALLBACK_INDEX_HTM
|
||||
server.sendHeader(F("Content-Encoding"), "gzip");
|
||||
@ -536,7 +470,6 @@ void handleGetEdit() {
|
||||
#else
|
||||
replyNotFound(FPSTR(FILE_NOT_FOUND));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
@ -555,16 +488,14 @@ void setup(void) {
|
||||
DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!"));
|
||||
|
||||
#ifdef USE_SPIFFS
|
||||
// Debug: dump on console contents of filessytem with no filter and check filenames validity
|
||||
// Debug: dump on console contents of filesystem with no filter and check filenames validity
|
||||
Dir dir = fileSystem->openDir("");
|
||||
DBG_OUTPUT_PORT.println(F("List of files at root of filesystem:"));
|
||||
while (dir.next()) {
|
||||
String error = checkForUnsupportedPath(dir.fileName());
|
||||
String fileInfo = dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + "b)");
|
||||
DBG_OUTPUT_PORT.println(error + fileInfo);
|
||||
if (error.length() > 0) {
|
||||
unsupportedFiles += error + fileInfo + '\n';
|
||||
}
|
||||
if (error.length() > 0) { unsupportedFiles += error + fileInfo + '\n'; }
|
||||
}
|
||||
DBG_OUTPUT_PORT.println();
|
||||
|
||||
@ -609,15 +540,15 @@ void setup(void) {
|
||||
server.on("/edit", HTTP_GET, handleGetEdit);
|
||||
|
||||
// Create file
|
||||
server.on("/edit", HTTP_PUT, handleFileCreate);
|
||||
server.on("/edit", HTTP_PUT, handleFileCreate);
|
||||
|
||||
// Delete file
|
||||
server.on("/edit", HTTP_DELETE, handleFileDelete);
|
||||
server.on("/edit", HTTP_DELETE, handleFileDelete);
|
||||
|
||||
// Upload file
|
||||
// - first callback is called after the request has ended with all parsed arguments
|
||||
// - second callback handles file upload at that location
|
||||
server.on("/edit", HTTP_POST, replyOK, handleFileUpload);
|
||||
server.on("/edit", HTTP_POST, replyOK, handleFileUpload);
|
||||
|
||||
// Default handler for all URIs not defined above
|
||||
// Use it to read files from filesystem
|
||||
|
@ -58,7 +58,7 @@ SDFSConfig fileSystemConfig = SDFSConfig();
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
// Indicate which digital I/Os should be displayed on the chart.
|
||||
@ -110,9 +110,7 @@ void replyServerError(String msg) {
|
||||
bool handleFileRead(String path) {
|
||||
DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
|
||||
|
||||
if (path.endsWith("/")) {
|
||||
path += "index.htm";
|
||||
}
|
||||
if (path.endsWith("/")) { path += "index.htm"; }
|
||||
|
||||
String contentType = mime::getContentType(path);
|
||||
|
||||
@ -122,9 +120,7 @@ bool handleFileRead(String path) {
|
||||
}
|
||||
if (fileSystem->exists(path)) {
|
||||
File file = fileSystem->open(path, "r");
|
||||
if (server.streamFile(file, contentType) != file.size()) {
|
||||
DBG_OUTPUT_PORT.println("Sent less data than expected!");
|
||||
}
|
||||
if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); }
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
@ -134,16 +130,14 @@ bool handleFileRead(String path) {
|
||||
|
||||
|
||||
/*
|
||||
The "Not Found" handler catches all URI not explicitely declared in code
|
||||
The "Not Found" handler catches all URI not explicitly declared in code
|
||||
First try to find and return the requested file from the filesystem,
|
||||
and if it fails, return a 404 page with debug information
|
||||
*/
|
||||
void handleNotFound() {
|
||||
String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
|
||||
String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
|
||||
|
||||
if (handleFileRead(uri)) {
|
||||
return;
|
||||
}
|
||||
if (handleFileRead(uri)) { return; }
|
||||
|
||||
// Dump debug data
|
||||
String message;
|
||||
@ -218,7 +212,7 @@ void setup(void) {
|
||||
////////////////////////////////
|
||||
// WEB SERVER INIT
|
||||
|
||||
//get heap status, analog input value and all GPIO statuses in one json call
|
||||
// get heap status, analog input value and all GPIO statuses in one json call
|
||||
server.on("/espData", HTTP_GET, []() {
|
||||
String json;
|
||||
json.reserve(88);
|
||||
@ -249,21 +243,18 @@ void setup(void) {
|
||||
DBG_OUTPUT_PORT.println(" 0 (OFF): outputs are off and hidden from chart");
|
||||
DBG_OUTPUT_PORT.println(" 1 (AUTO): outputs are rotated automatically every second");
|
||||
DBG_OUTPUT_PORT.println(" 2 (MANUAL): outputs can be toggled from the web page");
|
||||
|
||||
}
|
||||
|
||||
// Return default GPIO mask, that is all I/Os except SD card ones
|
||||
unsigned int defaultMask() {
|
||||
unsigned int mask = 0b11111111111111111;
|
||||
for (auto pin = 0; pin <= 16; pin++) {
|
||||
if (isFlashInterfacePin(pin)) {
|
||||
mask &= ~(1 << pin);
|
||||
}
|
||||
if (isFlashInterfacePin(pin)) { mask &= ~(1 << pin); }
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
int rgbMode = 1; // 0=off - 1=auto - 2=manual
|
||||
int rgbMode = 1; // 0=off - 1=auto - 2=manual
|
||||
int rgbValue = 0;
|
||||
esp8266::polledTimeout::periodicMs timeToChange(1000);
|
||||
bool modeChangeRequested = false;
|
||||
@ -278,28 +269,24 @@ void loop(void) {
|
||||
}
|
||||
|
||||
// see if one second has passed since last change, otherwise stop here
|
||||
if (!timeToChange) {
|
||||
return;
|
||||
}
|
||||
if (!timeToChange) { return; }
|
||||
|
||||
// see if a mode change was requested
|
||||
if (modeChangeRequested) {
|
||||
// increment mode (reset after 2)
|
||||
rgbMode++;
|
||||
if (rgbMode > 2) {
|
||||
rgbMode = 0;
|
||||
}
|
||||
if (rgbMode > 2) { rgbMode = 0; }
|
||||
|
||||
modeChangeRequested = false;
|
||||
}
|
||||
|
||||
// act according to mode
|
||||
switch (rgbMode) {
|
||||
case 0: // off
|
||||
case 0: // off
|
||||
gpioMask = defaultMask();
|
||||
gpioMask &= ~(1 << 12); // Hide GPIO 12
|
||||
gpioMask &= ~(1 << 13); // Hide GPIO 13
|
||||
gpioMask &= ~(1 << 15); // Hide GPIO 15
|
||||
gpioMask &= ~(1 << 12); // Hide GPIO 12
|
||||
gpioMask &= ~(1 << 13); // Hide GPIO 13
|
||||
gpioMask &= ~(1 << 15); // Hide GPIO 15
|
||||
|
||||
// reset outputs
|
||||
digitalWrite(12, 0);
|
||||
@ -307,14 +294,12 @@ void loop(void) {
|
||||
digitalWrite(15, 0);
|
||||
break;
|
||||
|
||||
case 1: // auto
|
||||
case 1: // auto
|
||||
gpioMask = defaultMask();
|
||||
|
||||
// increment value (reset after 7)
|
||||
rgbValue++;
|
||||
if (rgbValue > 7) {
|
||||
rgbValue = 0;
|
||||
}
|
||||
if (rgbValue > 7) { rgbValue = 0; }
|
||||
|
||||
// output new values
|
||||
digitalWrite(12, rgbValue & 0b001);
|
||||
@ -322,11 +307,10 @@ void loop(void) {
|
||||
digitalWrite(15, rgbValue & 0b100);
|
||||
break;
|
||||
|
||||
case 2: // manual
|
||||
case 2: // manual
|
||||
gpioMask = defaultMask();
|
||||
|
||||
// keep outputs unchanged
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -31,9 +31,7 @@ void handleNotFound() {
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
@ -57,9 +55,7 @@ void setup(void) {
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
@ -89,17 +85,17 @@ void setup(void) {
|
||||
/////////////////////////////////////////////////////////
|
||||
// Hook examples
|
||||
|
||||
server.addHook([](const String & method, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction contentType) {
|
||||
(void)method; // GET, PUT, ...
|
||||
(void)url; // example: /root/myfile.html
|
||||
(void)client; // the webserver tcp client connection
|
||||
(void)contentType; // contentType(".html") => "text/html"
|
||||
server.addHook([](const String& method, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction contentType) {
|
||||
(void)method; // GET, PUT, ...
|
||||
(void)url; // example: /root/myfile.html
|
||||
(void)client; // the webserver tcp client connection
|
||||
(void)contentType; // contentType(".html") => "text/html"
|
||||
Serial.printf("A useless web hook has passed\n");
|
||||
Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter());
|
||||
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
});
|
||||
|
||||
server.addHook([](const String&, const String & url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) {
|
||||
server.addHook([](const String&, const String& url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) {
|
||||
if (url.startsWith("/fail")) {
|
||||
Serial.printf("An always failing web hook has been triggered\n");
|
||||
return ESP8266WebServer::CLIENT_MUST_STOP;
|
||||
@ -107,7 +103,7 @@ void setup(void) {
|
||||
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
});
|
||||
|
||||
server.addHook([](const String&, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction) {
|
||||
server.addHook([](const String&, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction) {
|
||||
if (url.startsWith("/dump")) {
|
||||
Serial.printf("The dumper web hook is on the run\n");
|
||||
|
||||
@ -137,7 +133,7 @@ void setup(void) {
|
||||
// check the client connection: it should not immediately be closed
|
||||
// (make another '/dump' one to close the first)
|
||||
Serial.printf("\nTelling server to forget this connection\n");
|
||||
static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter
|
||||
static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter
|
||||
return ESP8266WebServer::CLIENT_IS_GIVEN;
|
||||
}
|
||||
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -50,7 +50,7 @@ JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
@ -89,24 +89,22 @@ void handleRoot() {
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handleNotFound(){
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (server.method() == HTTP_GET)?"GET":"POST";
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i=0; i<server.args(); i++){
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void setup(void){
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
@ -127,9 +125,7 @@ void setup(void){
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
|
||||
|
||||
@ -138,7 +134,7 @@ void setup(void){
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
server.on("/inline", [](){
|
||||
server.on("/inline", []() {
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
|
||||
@ -152,17 +148,20 @@ extern "C" void stack_thunk_dump_stack();
|
||||
|
||||
void processKey(Print& out, int hotKey) {
|
||||
switch (hotKey) {
|
||||
case 'd': {
|
||||
case 'd':
|
||||
{
|
||||
HeapSelectDram ephemeral;
|
||||
umm_info(NULL, true);
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
case 'i':
|
||||
{
|
||||
HeapSelectIram ephemeral;
|
||||
umm_info(NULL, true);
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
case 'h':
|
||||
{
|
||||
{
|
||||
HeapSelectIram ephemeral;
|
||||
Serial.printf(PSTR("IRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap());
|
||||
@ -173,25 +172,29 @@ void processKey(Print& out, int hotKey) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'P':
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
// From this context stack_thunk_dump_stack() will only work when Serial
|
||||
// debug is enabled.
|
||||
case 'p':
|
||||
out.println(F("Calling stack_thunk_dump_stack();"));
|
||||
stack_thunk_dump_stack();
|
||||
break;
|
||||
#endif
|
||||
case 'R':
|
||||
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
|
||||
ESP.restart();
|
||||
break;
|
||||
case '\r':
|
||||
out.println();
|
||||
case '\n':
|
||||
break;
|
||||
case '\r': out.println();
|
||||
case '\n': break;
|
||||
case '?':
|
||||
out.println();
|
||||
out.println(F("Press a key + <enter>"));
|
||||
out.println(F(" h - Free Heap Report;"));
|
||||
out.println(F(" i - iRAM umm_info(null, true);"));
|
||||
out.println(F(" d - dRAM umm_info(null, true);"));
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
out.println(F(" p - call stack_thunk_dump_stack();"));
|
||||
#endif
|
||||
out.println(F(" R - Restart, ESP.restart();"));
|
||||
out.println(F(" ? - Print Help"));
|
||||
out.println();
|
||||
@ -205,7 +208,7 @@ void processKey(Print& out, int hotKey) {
|
||||
}
|
||||
|
||||
|
||||
void loop(void){
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
if (Serial.available() > 0) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -39,13 +39,13 @@ void setup() {
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate(www_username, www_password))
|
||||
//Basic Auth Method with Custom realm and Failure Response
|
||||
//return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse);
|
||||
//Digest Auth Method with realm="Login Required" and empty Failure Response
|
||||
//return server.requestAuthentication(DIGEST_AUTH);
|
||||
//Digest Auth Method with Custom realm and empty Failure Response
|
||||
//return server.requestAuthentication(DIGEST_AUTH, www_realm);
|
||||
//Digest Auth Method with Custom realm and Failure Response
|
||||
// Basic Auth Method with Custom realm and Failure Response
|
||||
// return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse);
|
||||
// Digest Auth Method with realm="Login Required" and empty Failure Response
|
||||
// return server.requestAuthentication(DIGEST_AUTH);
|
||||
// Digest Auth Method with Custom realm and empty Failure Response
|
||||
// return server.requestAuthentication(DIGEST_AUTH, www_realm);
|
||||
// Digest Auth Method with Custom realm and Failure Response
|
||||
{
|
||||
return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
|
@ -15,21 +15,21 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServerSecure.h>
|
||||
|
||||
//Unfortunately it is not possible to have persistent WiFi credentials stored as anything but plain text. Obfuscation would be the only feasible barrier.
|
||||
// Unfortunately it is not possible to have persistent WiFi credentials stored as anything but plain text. Obfuscation would be the only feasible barrier.
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* wifi_pw = STAPSK;
|
||||
|
||||
const String file_credentials = R"(/credentials.txt)"; // LittleFS file name for the saved credentials
|
||||
const String change_creds = "changecreds"; // Address for a credential change
|
||||
const String file_credentials = R"(/credentials.txt)"; // LittleFS file name for the saved credentials
|
||||
const String change_creds = "changecreds"; // Address for a credential change
|
||||
|
||||
//The ESP8266WebServerSecure requires an encryption certificate and matching key.
|
||||
//These can generated with the bash script available in the ESP8266 Arduino repository.
|
||||
//These values can be used for testing but are available publicly so should not be used in production.
|
||||
// The ESP8266WebServerSecure requires an encryption certificate and matching key.
|
||||
// These can generated with the bash script available in the ESP8266 Arduino repository.
|
||||
// These values can be used for testing but are available publicly so should not be used in production.
|
||||
static const char serverCert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC
|
||||
@ -52,7 +52,7 @@ JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m
|
||||
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg==
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
static const char serverKey[] PROGMEM = R"EOF(
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z
|
||||
@ -84,7 +84,7 @@ gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk=
|
||||
|
||||
ESP8266WebServerSecure server(443);
|
||||
|
||||
//These are temporary credentials that will only be used if none are found saved in LittleFS.
|
||||
// These are temporary credentials that will only be used if none are found saved in LittleFS.
|
||||
String login = "admin";
|
||||
const String realm = "global";
|
||||
String H1 = "";
|
||||
@ -93,16 +93,16 @@ String authentication_failed = "User authentication has failed.";
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
//Initialize LittleFS to save credentials
|
||||
if(!LittleFS.begin()){
|
||||
Serial.println("LittleFS initialization error, programmer flash configured?");
|
||||
// Initialize LittleFS to save credentials
|
||||
if (!LittleFS.begin()) {
|
||||
Serial.println("LittleFS initialization error, programmer flash configured?");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
//Attempt to load credentials. If the file does not yet exist, they will be set to the default values above
|
||||
// Attempt to load credentials. If the file does not yet exist, they will be set to the default values above
|
||||
loadcredentials();
|
||||
|
||||
//Initialize wifi
|
||||
// Initialize wifi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, wifi_pw);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
@ -112,8 +112,8 @@ void setup() {
|
||||
}
|
||||
|
||||
server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
|
||||
server.on("/",showcredentialpage); //for this simple example, just show a simple page for changing credentials at the root
|
||||
server.on("/" + change_creds,handlecredentialchange); //handles submission of credentials from the client
|
||||
server.on("/", showcredentialpage); // for this simple example, just show a simple page for changing credentials at the root
|
||||
server.on("/" + change_creds, handlecredentialchange); // handles submission of credentials from the client
|
||||
server.onNotFound(redirect);
|
||||
server.begin();
|
||||
|
||||
@ -127,49 +127,48 @@ void loop() {
|
||||
server.handleClient();
|
||||
}
|
||||
|
||||
//This function redirects home
|
||||
void redirect(){
|
||||
// This function redirects home
|
||||
void redirect() {
|
||||
String url = "https://" + WiFi.localIP().toString();
|
||||
Serial.println("Redirect called. Redirecting to " + url);
|
||||
server.sendHeader("Location", url, true);
|
||||
Serial.println("Header sent.");
|
||||
server.send( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
||||
Serial.println("Empty page sent.");
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
server.client().stop(); // Stop is needed because we sent no content length
|
||||
Serial.println("Client stopped.");
|
||||
}
|
||||
|
||||
//This function checks whether the current session has been authenticated. If not, a request for credentials is sent.
|
||||
// This function checks whether the current session has been authenticated. If not, a request for credentials is sent.
|
||||
bool session_authenticated() {
|
||||
Serial.println("Checking authentication.");
|
||||
if (server.authenticateDigest(login,H1)) {
|
||||
if (server.authenticateDigest(login, H1)) {
|
||||
Serial.println("Authentication confirmed.");
|
||||
return true;
|
||||
} else {
|
||||
} else {
|
||||
Serial.println("Not authenticated. Requesting credentials.");
|
||||
server.requestAuthentication(DIGEST_AUTH,realm.c_str(),authentication_failed);
|
||||
server.requestAuthentication(DIGEST_AUTH, realm.c_str(), authentication_failed);
|
||||
redirect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//This function sends a simple webpage for changing login credentials to the client
|
||||
void showcredentialpage(){
|
||||
// This function sends a simple webpage for changing login credentials to the client
|
||||
void showcredentialpage() {
|
||||
Serial.println("Show credential page called.");
|
||||
if(!session_authenticated()){
|
||||
return;
|
||||
}
|
||||
if (!session_authenticated()) { return; }
|
||||
|
||||
Serial.println("Forming credential modification page.");
|
||||
|
||||
String page;
|
||||
page = R"(<html>)";
|
||||
|
||||
page+=
|
||||
R"(
|
||||
page +=
|
||||
R"(
|
||||
<h2>Login Credentials</h2><br>
|
||||
|
||||
<form action=")" + change_creds + R"(" method="post">
|
||||
<form action=")"
|
||||
+ change_creds + R"(" method="post">
|
||||
Login:<br>
|
||||
<input type="text" name="login"><br>
|
||||
Password:<br>
|
||||
@ -178,8 +177,7 @@ void showcredentialpage(){
|
||||
<input type="password" name="password_duplicate"><br>
|
||||
<p><button type="submit" name="newcredentials">Change Credentials</button></p>
|
||||
</form><br>
|
||||
)"
|
||||
;
|
||||
)";
|
||||
|
||||
page += R"(</html>)";
|
||||
|
||||
@ -188,17 +186,16 @@ void showcredentialpage(){
|
||||
server.send(200, "text/html", page);
|
||||
}
|
||||
|
||||
//Saves credentials to LittleFS
|
||||
void savecredentials(String new_login, String new_password)
|
||||
{
|
||||
//Set global variables to new values
|
||||
login=new_login;
|
||||
H1=ESP8266WebServer::credentialHash(new_login,realm,new_password);
|
||||
// Saves credentials to LittleFS
|
||||
void savecredentials(String new_login, String new_password) {
|
||||
// Set global variables to new values
|
||||
login = new_login;
|
||||
H1 = ESP8266WebServer::credentialHash(new_login, realm, new_password);
|
||||
|
||||
//Save new values to LittleFS for loading on next reboot
|
||||
// Save new values to LittleFS for loading on next reboot
|
||||
Serial.println("Saving credentials.");
|
||||
File f=LittleFS.open(file_credentials,"w"); //open as a brand new file, discard old contents
|
||||
if(f){
|
||||
File f = LittleFS.open(file_credentials, "w"); // open as a brand new file, discard old contents
|
||||
if (f) {
|
||||
Serial.println("Modifying credentials in file system.");
|
||||
f.println(login);
|
||||
f.println(H1);
|
||||
@ -209,19 +206,18 @@ void savecredentials(String new_login, String new_password)
|
||||
Serial.println("Credentials saved.");
|
||||
}
|
||||
|
||||
//loads credentials from LittleFS
|
||||
void loadcredentials()
|
||||
{
|
||||
// loads credentials from LittleFS
|
||||
void loadcredentials() {
|
||||
Serial.println("Searching for credentials.");
|
||||
File f;
|
||||
f=LittleFS.open(file_credentials,"r");
|
||||
if(f){
|
||||
f = LittleFS.open(file_credentials, "r");
|
||||
if (f) {
|
||||
Serial.println("Loading credentials from file system.");
|
||||
String mod=f.readString(); //read the file to a String
|
||||
int index_1=mod.indexOf('\n',0); //locate the first line break
|
||||
int index_2=mod.indexOf('\n',index_1+1); //locate the second line break
|
||||
login=mod.substring(0,index_1-1); //get the first line (excluding the line break)
|
||||
H1=mod.substring(index_1+1,index_2-1); //get the second line (excluding the line break)
|
||||
String mod = f.readString(); // read the file to a String
|
||||
int index_1 = mod.indexOf('\n', 0); // locate the first line break
|
||||
int index_2 = mod.indexOf('\n', index_1 + 1); // locate the second line break
|
||||
login = mod.substring(0, index_1 - 1); // get the first line (excluding the line break)
|
||||
H1 = mod.substring(index_1 + 1, index_2 - 1); // get the second line (excluding the line break)
|
||||
f.close();
|
||||
} else {
|
||||
String default_login = "admin";
|
||||
@ -229,17 +225,15 @@ void loadcredentials()
|
||||
Serial.println("None found. Setting to default credentials.");
|
||||
Serial.println("user:" + default_login);
|
||||
Serial.println("password:" + default_password);
|
||||
login=default_login;
|
||||
H1=ESP8266WebServer::credentialHash(default_login,realm,default_password);
|
||||
login = default_login;
|
||||
H1 = ESP8266WebServer::credentialHash(default_login, realm, default_password);
|
||||
}
|
||||
}
|
||||
|
||||
//This function handles a credential change from a client.
|
||||
// This function handles a credential change from a client.
|
||||
void handlecredentialchange() {
|
||||
Serial.println("Handle credential change called.");
|
||||
if(!session_authenticated()){
|
||||
return;
|
||||
}
|
||||
if (!session_authenticated()) { return; }
|
||||
|
||||
Serial.println("Handling credential change request from client.");
|
||||
|
||||
@ -247,9 +241,9 @@ void handlecredentialchange() {
|
||||
String pw1 = server.arg("password");
|
||||
String pw2 = server.arg("password_duplicate");
|
||||
|
||||
if(login != "" && pw1 != "" && pw1 == pw2){
|
||||
if (login != "" && pw1 != "" && pw1 == pw2) {
|
||||
|
||||
savecredentials(login,pw1);
|
||||
savecredentials(login, pw1);
|
||||
server.send(200, "text/plain", "Credentials updated");
|
||||
redirect();
|
||||
} else {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -33,9 +33,7 @@ void setup(void) {
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.on(F("/"), []() {
|
||||
server.send(200, "text/plain", "hello from esp8266!");
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
@ -62,9 +62,7 @@ void handleForm() {
|
||||
} else {
|
||||
digitalWrite(led, 1);
|
||||
String message = "POST form was:\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(200, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
@ -80,9 +78,7 @@ void handleNotFound() {
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
@ -105,9 +101,7 @@ void setup(void) {
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
|
@ -42,11 +42,11 @@ extern "C" {
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
const char *ssid = STASSID;
|
||||
const char *password = STAPSK;
|
||||
const unsigned int port = 80;
|
||||
|
||||
ESP8266WebServer server(port);
|
||||
@ -76,20 +76,16 @@ void handleNotFound() {
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
void SSEKeepAlive() {
|
||||
for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) {
|
||||
if (!(subscription[i].clientIP)) {
|
||||
continue;
|
||||
}
|
||||
if (!(subscription[i].clientIP)) { continue; }
|
||||
if (subscription[i].client.connected()) {
|
||||
Serial.printf_P(PSTR("SSEKeepAlive - client is still listening on channel %d\n"), i);
|
||||
subscription[i].client.println(F("event: event\ndata: { \"TYPE\":\"KEEP-ALIVE\" }\n")); // Extra newline required by SSE standard
|
||||
subscription[i].client.println(F("event: event\ndata: { \"TYPE\":\"KEEP-ALIVE\" }\n")); // Extra newline required by SSE standard
|
||||
} else {
|
||||
Serial.printf_P(PSTR("SSEKeepAlive - client not listening on channel %d, remove subscription\n"), i);
|
||||
subscription[i].keepAliveTimer.detach();
|
||||
@ -106,15 +102,15 @@ void SSEKeepAlive() {
|
||||
void SSEHandler(uint8_t channel) {
|
||||
WiFiClient client = server.client();
|
||||
SSESubscription &s = subscription[channel];
|
||||
if (s.clientIP != client.remoteIP()) { // IP addresses don't match, reject this client
|
||||
if (s.clientIP != client.remoteIP()) { // IP addresses don't match, reject this client
|
||||
Serial.printf_P(PSTR("SSEHandler - unregistered client with IP %s tries to listen\n"), server.client().remoteIP().toString().c_str());
|
||||
return handleNotFound();
|
||||
}
|
||||
client.setNoDelay(true);
|
||||
client.setSync(true);
|
||||
Serial.printf_P(PSTR("SSEHandler - registered client with IP %s is listening\n"), IPAddress(s.clientIP).toString().c_str());
|
||||
s.client = client; // capture SSE server client connection
|
||||
server.setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever
|
||||
s.client = client; // capture SSE server client connection
|
||||
server.setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever
|
||||
server.sendContent_P(PSTR("HTTP/1.1 200 OK\nContent-Type: text/event-stream;\nConnection: keep-alive\nCache-Control: no-cache\nAccess-Control-Allow-Origin: *\n\n"));
|
||||
s.keepAliveTimer.attach_scheduled(30.0, SSEKeepAlive); // Refresh time every 30s for demo
|
||||
}
|
||||
@ -122,28 +118,20 @@ void SSEHandler(uint8_t channel) {
|
||||
void handleAll() {
|
||||
const char *uri = server.uri().c_str();
|
||||
const char *restEvents = PSTR("/rest/events/");
|
||||
if (strncmp_P(uri, restEvents, strlen_P(restEvents))) {
|
||||
return handleNotFound();
|
||||
}
|
||||
uri += strlen_P(restEvents); // Skip the "/rest/events/" and get to the channel number
|
||||
if (strncmp_P(uri, restEvents, strlen_P(restEvents))) { return handleNotFound(); }
|
||||
uri += strlen_P(restEvents); // Skip the "/rest/events/" and get to the channel number
|
||||
unsigned int channel = atoi(uri);
|
||||
if (channel < SSE_MAX_CHANNELS) {
|
||||
return SSEHandler(channel);
|
||||
}
|
||||
if (channel < SSE_MAX_CHANNELS) { return SSEHandler(channel); }
|
||||
handleNotFound();
|
||||
};
|
||||
|
||||
void SSEBroadcastState(const char *sensorName, unsigned short prevSensorValue, unsigned short sensorValue) {
|
||||
for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) {
|
||||
if (!(subscription[i].clientIP)) {
|
||||
continue;
|
||||
}
|
||||
if (!(subscription[i].clientIP)) { continue; }
|
||||
String IPaddrstr = IPAddress(subscription[i].clientIP).toString();
|
||||
if (subscription[i].client.connected()) {
|
||||
Serial.printf_P(PSTR("broadcast status change to client IP %s on channel %d for %s with new state %d\n"),
|
||||
IPaddrstr.c_str(), i, sensorName, sensorValue);
|
||||
subscription[i].client.printf_P(PSTR("event: event\ndata: {\"TYPE\":\"STATE\", \"%s\":{\"state\":%d, \"prevState\":%d}}\n\n"),
|
||||
sensorName, sensorValue, prevSensorValue);
|
||||
Serial.printf_P(PSTR("broadcast status change to client IP %s on channel %d for %s with new state %d\n"), IPaddrstr.c_str(), i, sensorName, sensorValue);
|
||||
subscription[i].client.printf_P(PSTR("event: event\ndata: {\"TYPE\":\"STATE\", \"%s\":{\"state\":%d, \"prevState\":%d}}\n\n"), sensorName, sensorValue, prevSensorValue);
|
||||
} else {
|
||||
Serial.printf_P(PSTR("SSEBroadcastState - client %s registered on channel %d but not listening\n"), IPaddrstr.c_str(), i);
|
||||
}
|
||||
@ -152,7 +140,7 @@ void SSEBroadcastState(const char *sensorName, unsigned short prevSensorValue, u
|
||||
|
||||
// Simulate sensors
|
||||
void updateSensor(sensorType &sensor) {
|
||||
unsigned short newVal = (unsigned short)RANDOM_REG32; // (not so good) random value for the sensor
|
||||
unsigned short newVal = (unsigned short)RANDOM_REG32; // (not so good) random value for the sensor
|
||||
Serial.printf_P(PSTR("update sensor %s - previous state: %d, new state: %d\n"), sensor.name, sensor.value, newVal);
|
||||
if (sensor.value != newVal) {
|
||||
SSEBroadcastState(sensor.name, sensor.value, newVal); // only broadcast if state is different
|
||||
@ -167,7 +155,7 @@ void handleSubscribe() {
|
||||
}
|
||||
|
||||
uint8_t channel;
|
||||
IPAddress clientIP = server.client().remoteIP(); // get IP address of client
|
||||
IPAddress clientIP = server.client().remoteIP(); // get IP address of client
|
||||
String SSEurl = F("http://");
|
||||
SSEurl += WiFi.localIP().toString();
|
||||
SSEurl += F(":");
|
||||
@ -176,14 +164,12 @@ void handleSubscribe() {
|
||||
SSEurl += F("/rest/events/");
|
||||
|
||||
++subscriptionCount;
|
||||
for (channel = 0; channel < SSE_MAX_CHANNELS; channel++) // Find first free slot
|
||||
if (!subscription[channel].clientIP) {
|
||||
break;
|
||||
}
|
||||
subscription[channel] = {clientIP, server.client(), Ticker()};
|
||||
for (channel = 0; channel < SSE_MAX_CHANNELS; channel++) // Find first free slot
|
||||
if (!subscription[channel].clientIP) { break; }
|
||||
subscription[channel] = { clientIP, server.client(), Ticker() };
|
||||
SSEurl += channel;
|
||||
Serial.printf_P(PSTR("Allocated channel %d, on uri %s\n"), channel, SSEurl.substring(offset).c_str());
|
||||
//server.on(SSEurl.substring(offset), std::bind(SSEHandler, &(subscription[channel])));
|
||||
// server.on(SSEurl.substring(offset), std::bind(SSEHandler, &(subscription[channel])));
|
||||
Serial.printf_P(PSTR("subscription for client IP %s: event bus location: %s\n"), clientIP.toString().c_str(), SSEurl.c_str());
|
||||
server.send_P(200, "text/plain", SSEurl.c_str());
|
||||
}
|
||||
@ -200,16 +186,14 @@ void setup(void) {
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
while (WiFi.status() != WL_CONNECTED) { // Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) { // Wait for connection
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.printf_P(PSTR("\nConnected to %s with IP address: %s\n"), ssid, WiFi.localIP().toString().c_str());
|
||||
if (MDNS.begin("esp8266")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); }
|
||||
|
||||
startServers(); // start web and SSE servers
|
||||
startServers(); // start web and SSE servers
|
||||
sensor[0].name = "sensorA";
|
||||
sensor[1].name = "sensorB";
|
||||
updateSensor(sensor[0]);
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -12,7 +12,7 @@ const char* password = STAPSK;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
//Check if header is present and correct
|
||||
// Check if header is present and correct
|
||||
bool is_authenticated() {
|
||||
Serial.println("Enter is_authenticated");
|
||||
if (server.hasHeader("Cookie")) {
|
||||
@ -28,7 +28,7 @@ bool is_authenticated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
//login page, also called for disconnect
|
||||
// login page, also called for disconnect
|
||||
void handleLogin() {
|
||||
String msg;
|
||||
if (server.hasHeader("Cookie")) {
|
||||
@ -45,7 +45,7 @@ void handleLogin() {
|
||||
return;
|
||||
}
|
||||
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
|
||||
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
|
||||
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") {
|
||||
server.sendHeader("Location", "/");
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
|
||||
@ -64,7 +64,7 @@ void handleLogin() {
|
||||
server.send(200, "text/html", content);
|
||||
}
|
||||
|
||||
//root page can be accessed only if authentication is ok
|
||||
// root page can be accessed only if authentication is ok
|
||||
void handleRoot() {
|
||||
Serial.println("Enter handleRoot");
|
||||
String header;
|
||||
@ -75,14 +75,12 @@ void handleRoot() {
|
||||
return;
|
||||
}
|
||||
String content = "<html><body><H2>hello, you successfully connected to esp8266!</H2><br>";
|
||||
if (server.hasHeader("User-Agent")) {
|
||||
content += "the user agent used is : " + server.header("User-Agent") + "<br><br>";
|
||||
}
|
||||
if (server.hasHeader("User-Agent")) { content += "the user agent used is : " + server.header("User-Agent") + "<br><br>"; }
|
||||
content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
|
||||
server.send(200, "text/html", content);
|
||||
}
|
||||
|
||||
//no need authentication
|
||||
// no need authentication
|
||||
void handleNotFound() {
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
@ -92,9 +90,7 @@ void handleNotFound() {
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
@ -123,7 +119,7 @@ void setup(void) {
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
//ask server to track these headers
|
||||
// ask server to track these headers
|
||||
server.collectHeaders("User-Agent", "Cookie");
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
|
199
libraries/ESP8266WebServer/examples/WebServer/README.md
Normal file
199
libraries/ESP8266WebServer/examples/WebServer/README.md
Normal file
@ -0,0 +1,199 @@
|
||||
# WebServer example documentation and hints
|
||||
|
||||
This example shows different techniques on how to use and extend the ESP8266WebServer for specific purposes
|
||||
|
||||
It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services.
|
||||
|
||||
It requires some space for a filesystem and runs fine on ESP8266 NodeMCU board with 4 MByte flash using the following options:
|
||||
* Flash Size Option 4MB (FS:2MB)
|
||||
* Debug Port Serial
|
||||
* MMU 32+32 balanced
|
||||
|
||||
It features
|
||||
|
||||
* http access to the web server
|
||||
* deliver all files from the file system
|
||||
* deliver special built-in files
|
||||
* implement services (list files, sysinfo)
|
||||
* uploading files using drag & drop
|
||||
* listing and deleting files using a SPA application
|
||||
* Example of SPA and Web Service application
|
||||
* Only files in the root folder are supported for simplicity - no directories.
|
||||
|
||||
|
||||
|
||||
|
||||
## Implementing a web server
|
||||
|
||||
The ESP8266WebServer library offers a simple path to implement a web server on a ESP8266 board.
|
||||
|
||||
The advantage on using the ESP8266WebServer instead of the plain simple WiFiServer is that the ESP8266WebServer
|
||||
takes much care about the http protocol conventions and features and allows easily access to parameters.
|
||||
It offers plug-in capabilities by registering specific functionalities that will be outlined below.
|
||||
|
||||
|
||||
### Initialization
|
||||
|
||||
In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network.
|
||||
|
||||
* Create a webserver listening to port 80 for http requests.
|
||||
* Initialize the access to the filesystem in the free flash memory (typically 2MByte).
|
||||
* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library.
|
||||
* Register the device in DNS using a known hostname.
|
||||
* Registering several plug-ins (see below).
|
||||
* Starting the web server.
|
||||
|
||||
|
||||
### Running
|
||||
|
||||
In the loop() function the web server will be given time to receive and send network packages by calling
|
||||
`server.handleClient();`.
|
||||
|
||||
|
||||
|
||||
## Registering simple functions to implement RESTful services
|
||||
|
||||
Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters.
|
||||
|
||||
There are 2 functions implemented that get registered to handle incoming GET requests for given URLs.
|
||||
|
||||
The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript.
|
||||
|
||||
When the **handleSysInfo()** function is registered and a browser requests for <http://webserver/$sysinfo> the function will be called and can collect the requested information.
|
||||
|
||||
> ```CPP
|
||||
> server.on("/$sysinfo", handleSysInfo);
|
||||
> ```
|
||||
|
||||
The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format.
|
||||
|
||||
You can try this request in a browser by opening <http://webserver/$sysinfo> in the address bar.
|
||||
|
||||
> ```CPP
|
||||
> server.on("/$sysinfo", handleList);
|
||||
> ```
|
||||
|
||||
The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp.
|
||||
|
||||
You can try this request in a browser by opening <http://webserver/$list> in the address bar.
|
||||
|
||||
|
||||
## Registering a function to send out some static content from a String
|
||||
|
||||
This is an example of registering a inline function in the web server.
|
||||
The 2. parameter of the on() method is a so called CPP lamda function (without a name)
|
||||
that actually has only one line of functionality by sending a string as result to the client.
|
||||
|
||||
> ```CPP
|
||||
> server.on("/$upload.htm", []() {
|
||||
> server.send(200, "text/html", FPSTR(uploadContent));
|
||||
> });
|
||||
> ```
|
||||
|
||||
Here the text from a static String with html code is returned instead of a file from the filesystem.
|
||||
The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation
|
||||
that allows uploading new files into the empty filesystem.
|
||||
|
||||
Just open <http://webserver/$upload.htm> and drag some files from the data folder on the drop area.
|
||||
|
||||
|
||||
## Registering a function to handle requests to the server without a path
|
||||
|
||||
Often servers are addressed by using the base URL like <http://webserver/> where no further path details is given.
|
||||
Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered:
|
||||
|
||||
> ```CPP
|
||||
> server.on("/$upload.htm", handleRoot);
|
||||
> ```
|
||||
|
||||
The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists.
|
||||
Otherwise the redirection goes to the built-in **/$upload.htm** web page.
|
||||
|
||||
|
||||
|
||||
## Using the serveStatic plug-in
|
||||
|
||||
The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways.
|
||||
|
||||
> ```CPP
|
||||
> server.enableCORS(true);
|
||||
> server.enableETag(true);
|
||||
> server.serveStatic("/", LittleFS, "/");
|
||||
> ```
|
||||
|
||||
|
||||
### Cross-Origin Ressource Sharing (CORS)
|
||||
|
||||
The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client
|
||||
to inform that it is allowed to call URLs and services on this server from other web sites.
|
||||
|
||||
The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup.
|
||||
|
||||
* Web sites providing high sensitive information like online banking this is disabled most of the times.
|
||||
* Web sites providing advertising information or reusable scripts / images this is enabled.
|
||||
|
||||
|
||||
### ETag support
|
||||
|
||||
The `enableETag(true)` function adds a ETag http header to the responses to the client that come from files from the filesystem
|
||||
to enable better use of the cache in the browser.
|
||||
|
||||
When enabled by default the server reads the file content and creates a checksum using the md5 and base64 algorithm her called the ETag value
|
||||
that changes whenever the file contains something different.
|
||||
|
||||
Once a browser has got the content of a file from the server including the ETag information it will add that ETag value is added in the following requests for the same resource.
|
||||
Now the server can answer with a 'use the version from the cache' when the new calculated ETag value is equal to the ETag value in the request.
|
||||
|
||||
The calculation of the ETag value requires some time and processing but sending content is always slower.
|
||||
So when you have the situation that a browser will use a web server multiple times this mechanism saves network and computing and makes web pages more responsive.
|
||||
|
||||
In the source code you can find another version of an algorithm to calculate a ETag value that uses the date&time from the filesystem.
|
||||
This is a simpler and faster way but with a low risk of dismissing a file update as the timestamp is based on seconds and local time.
|
||||
This can be enabled on demand, see inline comments.
|
||||
|
||||
|
||||
## Registering a full-featured handler as plug-in
|
||||
|
||||
The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality
|
||||
that can handle more complex requests without giving a fixed URL.
|
||||
It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality.
|
||||
|
||||
This class has to implements several functions and works in a more detailed way:
|
||||
|
||||
* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not.
|
||||
|
||||
In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files.
|
||||
|
||||
The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler.
|
||||
|
||||
* The function `handle()` then implements the real deletion of the file.
|
||||
|
||||
* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file.
|
||||
|
||||
|
||||
## Registering a special handler for "file not found"
|
||||
|
||||
Any other incoming request that was not handled by the registered plug-ins above can be detected by registering
|
||||
|
||||
> ```CPP
|
||||
> // handle cases when file is not found
|
||||
> server.onNotFound([]() {
|
||||
> // standard not found in browser.
|
||||
> server.send(404, "text/html", FPSTR(notFoundContent));
|
||||
> });
|
||||
> ```
|
||||
|
||||
This allows sending back an "friendly" result for the browser. Here a sim ple html page is created from a static string.
|
||||
You can easily change the html code in the file `builtinfiles.h`.
|
||||
|
||||
|
||||
## customizations
|
||||
|
||||
You may like to change the hostname and the timezone in the lines:
|
||||
|
||||
> ```CPP
|
||||
> #define HOSTNAME "webserver"
|
||||
> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
> ```
|
||||
|
||||
|
245
libraries/ESP8266WebServer/examples/WebServer/WebServer.ino
Normal file
245
libraries/ESP8266WebServer/examples/WebServer/WebServer.ino
Normal file
@ -0,0 +1,245 @@
|
||||
// @file WebServer.ino
|
||||
// @brief Example implementation using the ESP8266 WebServer.
|
||||
//
|
||||
// See also README.md for instructions and hints.
|
||||
//
|
||||
// Changelog:
|
||||
// 21.07.2021 creation, first version
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#include "secrets.h" // add WLAN Credentials in here.
|
||||
|
||||
#include <FS.h> // File System for Web Server Files
|
||||
#include <LittleFS.h> // This file system is used.
|
||||
|
||||
// mark parameters not used in example
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
// TRACE output simplified, can be deactivated here
|
||||
#define TRACE(...) Serial.printf(__VA_ARGS__)
|
||||
|
||||
// name of the server. You reach it using http://webserver
|
||||
#define HOSTNAME "webserver"
|
||||
|
||||
// local time zone definition (Berlin)
|
||||
#define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
|
||||
// need a WebServer for http access on port 80.
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
// The text of builtin files are in this header file
|
||||
#include "builtinfiles.h"
|
||||
|
||||
|
||||
// ===== Simple functions used to answer simple GET requests =====
|
||||
|
||||
// This function is called when the WebServer was requested without giving a filename.
|
||||
// This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page
|
||||
void handleRedirect() {
|
||||
TRACE("Redirect...");
|
||||
String url = "/index.htm";
|
||||
|
||||
if (!LittleFS.exists(url)) { url = "/$update.htm"; }
|
||||
|
||||
server.sendHeader("Location", url, true);
|
||||
server.send(302);
|
||||
} // handleRedirect()
|
||||
|
||||
|
||||
// This function is called when the WebServer was requested to list all existing files in the filesystem.
|
||||
// a JSON array with file information is returned.
|
||||
void handleListFiles() {
|
||||
Dir dir = LittleFS.openDir("/");
|
||||
String result;
|
||||
|
||||
result += "[\n";
|
||||
while (dir.next()) {
|
||||
if (result.length() > 4) { result += ","; }
|
||||
result += " {";
|
||||
result += " \"name\": \"" + dir.fileName() + "\", ";
|
||||
result += " \"size\": " + String(dir.fileSize()) + ", ";
|
||||
result += " \"time\": " + String(dir.fileTime());
|
||||
result += " }\n";
|
||||
// jc.addProperty("size", dir.fileSize());
|
||||
} // while
|
||||
result += "]";
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.send(200, "text/javascript; charset=utf-8", result);
|
||||
} // handleListFiles()
|
||||
|
||||
|
||||
// This function is called when the sysInfo service was requested.
|
||||
void handleSysInfo() {
|
||||
String result;
|
||||
|
||||
FSInfo fs_info;
|
||||
LittleFS.info(fs_info);
|
||||
|
||||
result += "{\n";
|
||||
result += " \"flashSize\": " + String(ESP.getFlashChipSize()) + ",\n";
|
||||
result += " \"freeHeap\": " + String(ESP.getFreeHeap()) + ",\n";
|
||||
result += " \"fsTotalBytes\": " + String(fs_info.totalBytes) + ",\n";
|
||||
result += " \"fsUsedBytes\": " + String(fs_info.usedBytes) + ",\n";
|
||||
result += "}";
|
||||
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
server.send(200, "text/javascript; charset=utf-8", result);
|
||||
} // handleSysInfo()
|
||||
|
||||
|
||||
// ===== Request Handler class used to answer more complex requests =====
|
||||
|
||||
// The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem.
|
||||
class FileServerHandler : public RequestHandler {
|
||||
public:
|
||||
// @brief Construct a new File Server Handler object
|
||||
// @param fs The file system to be used.
|
||||
// @param path Path to the root folder in the file system that is used for serving static data down and upload.
|
||||
// @param cache_header Cache Header to be used in replies.
|
||||
FileServerHandler() {
|
||||
TRACE("FileServerHandler is registered\n");
|
||||
}
|
||||
|
||||
|
||||
// @brief check incoming request. Can handle POST for uploads and DELETE.
|
||||
// @param requestMethod method of the http request line.
|
||||
// @param requestUri request ressource from the http request line.
|
||||
// @return true when method can be handled.
|
||||
bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override {
|
||||
return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE));
|
||||
} // canHandle()
|
||||
|
||||
|
||||
bool canUpload(const String &uri) override {
|
||||
// only allow upload on root fs level.
|
||||
return (uri == "/");
|
||||
} // canUpload()
|
||||
|
||||
|
||||
bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override {
|
||||
// ensure that filename starts with '/'
|
||||
String fName = requestUri;
|
||||
if (!fName.startsWith("/")) { fName = "/" + fName; }
|
||||
|
||||
if (requestMethod == HTTP_POST) {
|
||||
// all done in upload. no other forms.
|
||||
|
||||
} else if (requestMethod == HTTP_DELETE) {
|
||||
if (LittleFS.exists(fName)) { LittleFS.remove(fName); }
|
||||
} // if
|
||||
|
||||
server.send(200); // all done.
|
||||
return (true);
|
||||
} // handle()
|
||||
|
||||
|
||||
// uploading process
|
||||
void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override {
|
||||
// ensure that filename starts with '/'
|
||||
String fName = upload.filename;
|
||||
if (!fName.startsWith("/")) { fName = "/" + fName; }
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
// Open the file
|
||||
if (LittleFS.exists(fName)) { LittleFS.remove(fName); } // if
|
||||
_fsUploadFile = LittleFS.open(fName, "w");
|
||||
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
// Write received bytes
|
||||
if (_fsUploadFile) { _fsUploadFile.write(upload.buf, upload.currentSize); }
|
||||
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
// Close the file
|
||||
if (_fsUploadFile) { _fsUploadFile.close(); }
|
||||
} // if
|
||||
} // upload()
|
||||
|
||||
protected:
|
||||
File _fsUploadFile;
|
||||
};
|
||||
|
||||
|
||||
// Setup everything to make the webserver work.
|
||||
void setup(void) {
|
||||
delay(3000); // wait for serial monitor to start completely.
|
||||
|
||||
// Use Serial port for some trace information from the example
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(false);
|
||||
|
||||
TRACE("Starting WebServer example...\n");
|
||||
|
||||
TRACE("Mounting the filesystem...\n");
|
||||
if (!LittleFS.begin()) {
|
||||
TRACE("could not mount the filesystem...\n");
|
||||
delay(2000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// start WiFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
if (strlen(ssid) == 0) {
|
||||
WiFi.begin();
|
||||
} else {
|
||||
WiFi.begin(ssid, passPhrase);
|
||||
}
|
||||
|
||||
// allow to address the device by the given name e.g. http://webserver
|
||||
WiFi.setHostname(HOSTNAME);
|
||||
|
||||
TRACE("Connect to WiFi...\n");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
TRACE(".");
|
||||
}
|
||||
TRACE("connected.\n");
|
||||
|
||||
// Ask for the current time using NTP request builtin into ESP firmware.
|
||||
TRACE("Setup ntp...\n");
|
||||
configTime(TIMEZONE, "pool.ntp.org");
|
||||
|
||||
TRACE("Register service handlers...\n");
|
||||
|
||||
// serve a built-in htm page
|
||||
server.on("/$upload.htm", []() {
|
||||
server.send(200, "text/html", FPSTR(uploadContent));
|
||||
});
|
||||
|
||||
// register a redirect handler when only domain name is given.
|
||||
server.on("/", HTTP_GET, handleRedirect);
|
||||
|
||||
// register some REST services
|
||||
server.on("/$list", HTTP_GET, handleListFiles);
|
||||
server.on("/$sysinfo", HTTP_GET, handleSysInfo);
|
||||
|
||||
// UPLOAD and DELETE of files in the file system using a request handler.
|
||||
server.addHandler(new FileServerHandler());
|
||||
|
||||
// enable CORS header in webserver results
|
||||
server.enableCORS(true);
|
||||
|
||||
// enable ETAG header in webserver results from serveStatic handler
|
||||
server.enableETag(true);
|
||||
|
||||
// serve all static files
|
||||
server.serveStatic("/", LittleFS, "/");
|
||||
|
||||
// handle cases when file is not found
|
||||
server.onNotFound([]() {
|
||||
// standard not found in browser.
|
||||
server.send(404, "text/html", FPSTR(notFoundContent));
|
||||
});
|
||||
|
||||
server.begin();
|
||||
TRACE("hostname=%s\n", WiFi.getHostname());
|
||||
} // setup
|
||||
|
||||
|
||||
// run the server...
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
} // loop()
|
||||
|
||||
// end.
|
63
libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h
Normal file
63
libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file builtinfiles.h
|
||||
* @brief This file is part of the WebServer example for the ESP8266WebServer.
|
||||
*
|
||||
* This file contains long, multiline text variables for all builtin resources.
|
||||
*/
|
||||
|
||||
// used for $upload.htm
|
||||
static const char uploadContent[] PROGMEM =
|
||||
R"==(
|
||||
<!doctype html>
|
||||
<html lang='en'>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Upload</title>
|
||||
</head>
|
||||
|
||||
<body style="width:300px">
|
||||
<h1>Upload</h1>
|
||||
<div><a href="/">Home</a></div>
|
||||
<hr>
|
||||
<div id='zone' style='width:16em;height:12em;padding:10px;background-color:#ddd'>Drop files here...</div>
|
||||
|
||||
<script>
|
||||
// allow drag&drop of file objects
|
||||
function dragHelper(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// allow drag&drop of file objects
|
||||
function dropped(e) {
|
||||
dragHelper(e);
|
||||
var fls = e.dataTransfer.files;
|
||||
var formData = new FormData();
|
||||
for (var i = 0; i < fls.length; i++) {
|
||||
formData.append('file', fls[i], '/' + fls[i].name);
|
||||
}
|
||||
fetch('/', { method: 'POST', body: formData }).then(function () {
|
||||
window.alert('done.');
|
||||
});
|
||||
}
|
||||
var z = document.getElementById('zone');
|
||||
z.addEventListener('dragenter', dragHelper, false);
|
||||
z.addEventListener('dragover', dragHelper, false);
|
||||
z.addEventListener('drop', dropped, false);
|
||||
</script>
|
||||
</body>
|
||||
)==";
|
||||
|
||||
// used for $upload.htm
|
||||
static const char notFoundContent[] PROGMEM = R"==(
|
||||
<html>
|
||||
<head>
|
||||
<title>Ressource not found</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>The ressource was not found.</p>
|
||||
<p><a href="/">Start again</a></p>
|
||||
</body>
|
||||
)==";
|
65
libraries/ESP8266WebServer/examples/WebServer/data/files.htm
Normal file
65
libraries/ESP8266WebServer/examples/WebServer/data/files.htm
Normal file
@ -0,0 +1,65 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Files</title>
|
||||
<link Content-Type="text/css" href="/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Files on Server</h1>
|
||||
|
||||
<p>These files are available on the server to be opened or delete:</p>
|
||||
<div id="list">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// load and display all files after page loading has finished
|
||||
window.addEventListener("load", function () {
|
||||
fetch('/$list')
|
||||
.then(function (result) { return result.json(); })
|
||||
.then(function (e) {
|
||||
var listObj = document.querySelector('#list');
|
||||
e.forEach(function (f) {
|
||||
var entry = document.createElement("div");
|
||||
var nameObj = document.createElement("a");
|
||||
nameObj.href = '/' + f.name;
|
||||
nameObj.innerText = '/' + f.name;
|
||||
entry.appendChild(nameObj)
|
||||
|
||||
entry.appendChild(document.createTextNode(' (' + f.size + ') '));
|
||||
|
||||
var timeObj = document.createElement("span");
|
||||
timeObj.innerText = (new Date(f.time*1000)).toLocaleString();
|
||||
entry.appendChild(timeObj)
|
||||
entry.appendChild(document.createTextNode(" "));
|
||||
|
||||
var delObj = document.createElement("span");
|
||||
delObj.className = 'deleteFile';
|
||||
delObj.innerText = ' [delete]';
|
||||
entry.appendChild(delObj)
|
||||
|
||||
listObj.appendChild(entry)
|
||||
});
|
||||
|
||||
})
|
||||
.catch(function (err) {
|
||||
window.alert(err);
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("click", function (evt) {
|
||||
var t = evt.target;
|
||||
if (t.className === 'deleteFile') {
|
||||
var fname = t.parentElement.innerText;
|
||||
fname = '/'+ fname.split(' ')[0];
|
||||
if (window.confirm("Delete " + fname + " ?")) {
|
||||
fetch(fname, { method: 'DELETE' });
|
||||
document.location.reload(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
24
libraries/ESP8266WebServer/examples/WebServer/data/index.htm
Normal file
24
libraries/ESP8266WebServer/examples/WebServer/data/index.htm
Normal file
@ -0,0 +1,24 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>HomePage</title>
|
||||
<link Content-Type="text/css" href="/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Homepage of the WebServer Example</h1>
|
||||
|
||||
<p>The following pages are available:</p>
|
||||
<ul>
|
||||
<li><a href="/index.htm">/index.htm</a> - This page</li>
|
||||
<li><a href="/files.htm">/files.htm</a> - Manage files on the server</li>
|
||||
<li><a href="/$upload.htm">/$upload.htm</a> - Built-in upload utility</a></li>
|
||||
</ul>
|
||||
|
||||
<p>The following REST services are available:</p>
|
||||
<ul>
|
||||
<li><a href="/$sysinfo">/$sysinfo</a> - Some system level information</a></li>
|
||||
<li><a href="/$list">/$list</a> - Array of all files</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
10
libraries/ESP8266WebServer/examples/WebServer/data/style.css
Normal file
10
libraries/ESP8266WebServer/examples/WebServer/data/style.css
Normal file
@ -0,0 +1,10 @@
|
||||
html, body {
|
||||
color: #111111; font-family: Arial, ui-sans-serif, sans-serif; font-size: 1em; background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#list > div {
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
a { color: inherit; cursor: pointer; }
|
||||
|
18
libraries/ESP8266WebServer/examples/WebServer/secrets.h
Normal file
18
libraries/ESP8266WebServer/examples/WebServer/secrets.h
Normal file
@ -0,0 +1,18 @@
|
||||
// Secrets for your local home network
|
||||
|
||||
// This is a "hard way" to configure your local WiFi network name and passphrase
|
||||
// into the source code and the uploaded sketch.
|
||||
//
|
||||
// Using the WiFi Manager is preferred and avoids reprogramming when your network changes.
|
||||
// See https://homeding.github.io/#page=/wifimanager.md
|
||||
|
||||
// ssid and passPhrase can be used when compiling for a specific environment as a 2. option.
|
||||
|
||||
// add you wifi network name and PassPhrase or use WiFi Manager
|
||||
#ifndef STASSID
|
||||
#define STASSID "ssid"
|
||||
#define STAPSK "psk"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *passPhrase = STAPSK;
|
@ -9,7 +9,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* host = "esp8266-webupdate";
|
||||
@ -31,34 +31,36 @@ void setup(void) {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/html", serverIndex);
|
||||
});
|
||||
server.on("/update", HTTP_POST, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
|
||||
ESP.restart();
|
||||
}, []() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
Serial.setDebugOutput(true);
|
||||
WiFiUDP::stopAll();
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
if (!Update.begin(maxSketchSpace)) { //start with max available size
|
||||
Update.printError(Serial);
|
||||
server.on(
|
||||
"/update", HTTP_POST, []() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
|
||||
ESP.restart();
|
||||
},
|
||||
[]() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
Serial.setDebugOutput(true);
|
||||
WiFiUDP::stopAll();
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
if (!Update.begin(maxSketchSpace)) { // start with max available size
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (Update.end(true)) { // true to set the size to the current progress
|
||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
Serial.setDebugOutput(false);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (Update.end(true)) { //true to set the size to the current progress
|
||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
Serial.setDebugOutput(false);
|
||||
}
|
||||
yield();
|
||||
});
|
||||
yield();
|
||||
});
|
||||
server.begin();
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
|
@ -68,6 +68,13 @@ template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::enableCORS(bool enable) {
|
||||
_corsEnabled = enable;
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::enableETag(bool enable, ETagFunction fn) {
|
||||
_eTagEnabled = enable;
|
||||
_eTagFunction = fn;
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::begin() {
|
||||
close();
|
||||
@ -95,31 +102,31 @@ bool ESP8266WebServerTemplate<ServerType>::authenticate(const char * username, c
|
||||
if(authReq.startsWith(F("Basic"))){
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username)+strlen(password)+1;
|
||||
char *toencode = new (std::nothrow) char[toencodeLen + 1];
|
||||
if(toencode == NULL){
|
||||
authReq = "";
|
||||
|
||||
const size_t username_len = strlen(username);
|
||||
const size_t password_len = strlen(password);
|
||||
|
||||
String raw;
|
||||
raw.reserve(username_len + password_len + 1);
|
||||
raw.concat(username, username_len);
|
||||
raw += ':';
|
||||
raw.concat(password, password_len);
|
||||
if(!raw.length()) {
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
String encoded = base64::encode((uint8_t *)toencode, toencodeLen, false);
|
||||
if(!encoded){
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
|
||||
String encoded = base64::encode(raw, false);
|
||||
if(!encoded.length()){
|
||||
return false;
|
||||
}
|
||||
if(authReq.equalsConstantTime(encoded)) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
} else if(authReq.startsWith(F("Digest"))) {
|
||||
String _realm = _extractParam(authReq, F("realm=\""));
|
||||
String _H1 = credentialHash((String)username,_realm,(String)password);
|
||||
return authenticateDigest((String)username,_H1);
|
||||
String _H1 = credentialHash(username,_realm,password);
|
||||
return authenticateDigest(username,_H1);
|
||||
}
|
||||
authReq = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -264,16 +271,17 @@ void ESP8266WebServerTemplate<ServerType>::serveStatic(const char* uri, FS& fs,
|
||||
file.close();
|
||||
}
|
||||
|
||||
if(is_file)
|
||||
if(is_file) {
|
||||
_addRequestHandler(new StaticFileRequestHandler<ServerType>(fs, path, uri, cache_header));
|
||||
else
|
||||
} else {
|
||||
_addRequestHandler(new StaticDirectoryRequestHandler<ServerType>(fs, path, uri, cache_header));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
||||
if (_currentStatus == HC_NONE) {
|
||||
ClientType client = _server.available();
|
||||
ClientType client = _server.accept();
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
@ -335,11 +343,18 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
||||
} // switch _parseRequest()
|
||||
} else {
|
||||
// !_currentClient.available(): waiting for more data
|
||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
unsigned long timeSinceChange = millis() - _statusChange;
|
||||
// Use faster connection drop timeout if any other client has data
|
||||
// or the buffer of pending clients is full
|
||||
if ((_server.hasClientData() || _server.hasMaxPendingClients())
|
||||
&& timeSinceChange > HTTP_MAX_DATA_AVAILABLE_WAIT)
|
||||
DBGWS("webserver: closing since there's another connection to read from\n");
|
||||
else {
|
||||
if (timeSinceChange > HTTP_MAX_DATA_WAIT)
|
||||
DBGWS("webserver: closing after read timeout\n");
|
||||
else
|
||||
keepCurrentClient = true;
|
||||
}
|
||||
else
|
||||
DBGWS("webserver: closing after read timeout\n");
|
||||
callYield = true;
|
||||
}
|
||||
break;
|
||||
@ -436,6 +451,7 @@ void ESP8266WebServerTemplate<ServerType>::_prepareHeader(String& response, int
|
||||
sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT);
|
||||
}
|
||||
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = "";
|
||||
@ -499,7 +515,7 @@ void ESP8266WebServerTemplate<ServerType>::sendContent(Stream* content, ssize_t
|
||||
ssize_t sent = content->sendSize(&_currentClient, content_length);
|
||||
if (sent != content_length)
|
||||
{
|
||||
DBGWS("HTTPServer: error: short send after timeout (%d<%d)\n", sent, content_length);
|
||||
DBGWS("HTTPServer: error: short send after timeout (%zu < %zu)\n", sent, content_length);
|
||||
}
|
||||
if(_chunked) {
|
||||
_currentClient.printf_P(PSTR("\r\n"));
|
||||
|
@ -59,6 +59,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
|
||||
#define HTTP_MAX_DATA_AVAILABLE_WAIT 30 //ms to wait for the client to send the request when there is another client with data available
|
||||
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
|
||||
@ -114,6 +115,8 @@ public:
|
||||
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
typedef std::function<String(FS &fs, const String &fName)> ETagFunction;
|
||||
|
||||
void on(const Uri &uri, THandlerFunction handler);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
@ -122,6 +125,7 @@ public:
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||
void enableCORS(bool enable);
|
||||
void enableETag(bool enable, ETagFunction fn = nullptr);
|
||||
|
||||
const String& uri() const { return _currentUri; }
|
||||
HTTPMethod method() const { return _currentMethod; }
|
||||
@ -167,7 +171,9 @@ public:
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
|
||||
void send(int code, const char* content_type, Stream* stream, size_t content_length = 0);
|
||||
void send(int code, const char* content_type, Stream& stream, size_t content_length = 0);
|
||||
void send(int code, const char* content_type, Stream& stream, size_t content_length = 0) {
|
||||
send(code, content_type, &stream, content_length);
|
||||
}
|
||||
|
||||
void setContentLength(const size_t contentLength);
|
||||
void sendHeader(const String& name, const String& value, bool first = false);
|
||||
@ -271,6 +277,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool _eTagEnabled = false;
|
||||
ETagFunction _eTagFunction = nullptr;
|
||||
|
||||
protected:
|
||||
void _addRequestHandler(RequestHandlerType* handler);
|
||||
void _handleRequest();
|
||||
|
@ -9,6 +9,26 @@
|
||||
|
||||
namespace esp8266webserver {
|
||||
|
||||
// calculate an ETag for a file in filesystem based on md5 checksum
|
||||
// that can be used in the http headers - include quotes.
|
||||
static String calcETag(FS &fs, const String &path) {
|
||||
String result;
|
||||
|
||||
// calculate eTag using md5 checksum
|
||||
uint8_t md5_buf[16];
|
||||
File f = fs.open(path, "r");
|
||||
MD5Builder calcMD5;
|
||||
calcMD5.begin();
|
||||
calcMD5.addStream(f, f.size());
|
||||
calcMD5.calculate();
|
||||
calcMD5.getBytes(md5_buf);
|
||||
f.close();
|
||||
// create a minimal-length eTag using base64 byte[]->text encoding.
|
||||
result = "\"" + base64::encode(md5_buf, 16, false) + "\"";
|
||||
return(result);
|
||||
} // calcETag
|
||||
|
||||
|
||||
template<typename ServerType>
|
||||
class FunctionRequestHandler : public RequestHandler<ServerType> {
|
||||
using WebServerType = ESP8266WebServerTemplate<ServerType>;
|
||||
@ -92,6 +112,7 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
// serve all files within a given directory
|
||||
template<typename ServerType>
|
||||
class StaticDirectoryRequestHandler : public StaticRequestHandler<ServerType> {
|
||||
|
||||
@ -117,6 +138,7 @@ public:
|
||||
DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str());
|
||||
|
||||
String path;
|
||||
String eTagCode;
|
||||
path.reserve(SRH::_path.length() + requestUri.length() + 32);
|
||||
path = SRH::_path;
|
||||
|
||||
@ -156,10 +178,28 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (server._eTagEnabled) {
|
||||
if (server._eTagFunction) {
|
||||
eTagCode = (server._eTagFunction)(SRH::_fs, path);
|
||||
} else {
|
||||
eTagCode = esp8266webserver::calcETag(SRH::_fs, path);
|
||||
}
|
||||
|
||||
if (server.header("If-None-Match") == eTagCode) {
|
||||
server.send(304);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (SRH::_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", SRH::_cache_header);
|
||||
|
||||
if ((server._eTagEnabled) && (eTagCode.length() > 0)) {
|
||||
server.sendHeader("ETag", eTagCode);
|
||||
}
|
||||
|
||||
server.streamFile(f, contentType, requestMethod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -167,6 +207,8 @@ protected:
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
|
||||
|
||||
// Serve a specific, single file
|
||||
template<typename ServerType>
|
||||
class StaticFileRequestHandler
|
||||
:
|
||||
@ -180,13 +222,6 @@ public:
|
||||
:
|
||||
StaticRequestHandler<ServerType>{fs, path, uri, cache_header}
|
||||
{
|
||||
File f = SRH::_fs.open(path, "r");
|
||||
MD5Builder calcMD5;
|
||||
calcMD5.begin();
|
||||
calcMD5.addStream(f, f.size());
|
||||
calcMD5.calculate();
|
||||
calcMD5.getBytes(_ETag_md5);
|
||||
f.close();
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, const String& requestUri) override {
|
||||
@ -197,11 +232,17 @@ public:
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\"";
|
||||
if (server._eTagEnabled) {
|
||||
if (server._eTagFunction) {
|
||||
_eTagCode = (server._eTagFunction)(SRH::_fs, SRH::_path);
|
||||
} else if (_eTagCode.isEmpty()) {
|
||||
_eTagCode = esp8266webserver::calcETag(SRH::_fs, SRH::_path);
|
||||
}
|
||||
|
||||
if(server.header("If-None-Match") == etag){
|
||||
server.send(304);
|
||||
return true;
|
||||
if (server.header("If-None-Match") == _eTagCode) {
|
||||
server.send(304);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
File f = SRH::_fs.open(SRH::_path, "r");
|
||||
@ -217,14 +258,16 @@ public:
|
||||
if (SRH::_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", SRH::_cache_header);
|
||||
|
||||
server.sendHeader("ETag", etag);
|
||||
if ((server._eTagEnabled) && (_eTagCode.length() > 0)) {
|
||||
server.sendHeader("ETag", _eTagCode);
|
||||
}
|
||||
|
||||
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t _ETag_md5[16];
|
||||
String _eTagCode; // ETag code calculated for this file as used in http header include quotes.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -41,7 +41,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -72,9 +72,7 @@ void setClock() {
|
||||
|
||||
// Try and connect using a WiFiClientBearSSL to specified host:port and dump URL
|
||||
void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) {
|
||||
if (!path) {
|
||||
path = "/";
|
||||
}
|
||||
if (!path) { path = "/"; }
|
||||
|
||||
Serial.printf("Trying: %s:443...", host);
|
||||
client->connect(host, port);
|
||||
@ -94,11 +92,9 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
do {
|
||||
char tmp[32];
|
||||
memset(tmp, 0, 32);
|
||||
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
|
||||
int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1);
|
||||
yield();
|
||||
if (rlen < 0) {
|
||||
break;
|
||||
}
|
||||
if (rlen < 0) { break; }
|
||||
// Only print out first line up to \r, then abort connection
|
||||
char *nl = strchr(tmp, '\r');
|
||||
if (nl) {
|
||||
@ -136,13 +132,13 @@ void setup() {
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
setClock(); // Required for X.509 validation
|
||||
setClock(); // Required for X.509 validation
|
||||
|
||||
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
|
||||
Serial.printf("Number of CA certs read: %d\n", numCerts);
|
||||
if (numCerts == 0) {
|
||||
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the LittleFS directory before running?\n");
|
||||
return; // Can't connect to anything w/o certs!
|
||||
return; // Can't connect to anything w/o certs!
|
||||
}
|
||||
|
||||
BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure();
|
||||
@ -156,9 +152,7 @@ void setup() {
|
||||
void loop() {
|
||||
Serial.printf("\nPlease enter a website address (www.blah.com) to connect to: ");
|
||||
String site;
|
||||
do {
|
||||
site = Serial.readString();
|
||||
} while (site == "");
|
||||
do { site = Serial.readString(); } while (site == "");
|
||||
// Strip newline if present
|
||||
site.replace(String("\r"), emptyString);
|
||||
site.replace(String("\n"), emptyString);
|
||||
@ -170,4 +164,3 @@ void loop() {
|
||||
fetchURL(bear, site.c_str(), 443, "/");
|
||||
delete bear;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -22,13 +22,11 @@ void fetch(BearSSL::WiFiClientSecure *client) {
|
||||
oneShot timeout(5000);
|
||||
do {
|
||||
char tmp[32];
|
||||
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
|
||||
int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1);
|
||||
yield();
|
||||
if (rlen < 0) {
|
||||
break;
|
||||
}
|
||||
if (rlen < 0) { break; }
|
||||
if (rlen == 0) {
|
||||
delay(10); // Give background processes some time
|
||||
delay(10); // Give background processes some time
|
||||
continue;
|
||||
}
|
||||
tmp[rlen] = '\0';
|
||||
@ -73,7 +71,7 @@ int fetchMaxFragmentLength() {
|
||||
// returns true then you can use the ::setBufferSizes(rx, tx) to shrink
|
||||
// the needed BearSSL memory while staying within protocol limits.
|
||||
//
|
||||
// If MFLN is not supported, you may still be able to mimimize the buffer
|
||||
// If MFLN is not supported, you may still be able to minimize the buffer
|
||||
// sizes assuming you can ensure the server never transmits fragments larger
|
||||
// than the size (i.e. by using HTTP GET RANGE methods, etc.).
|
||||
|
||||
@ -82,9 +80,7 @@ int fetchMaxFragmentLength() {
|
||||
bool mfln = client.probeMaxFragmentLength("tls.mbed.org", 443, 512);
|
||||
Serial.printf("\nConnecting to https://tls.mbed.org\n");
|
||||
Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
|
||||
if (mfln) {
|
||||
client.setBufferSizes(512, 512);
|
||||
}
|
||||
if (mfln) { client.setBufferSizes(512, 512); }
|
||||
client.connect("tls.mbed.org", 443);
|
||||
if (client.connected()) {
|
||||
Serial.printf("MFLN status: %s\n", client.getMFLNStatus() ? "true" : "false");
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -138,11 +138,11 @@ GBEnkz4KpKv7TkHoW+j7F5EMcLcSrUIpyw==
|
||||
|
||||
#endif
|
||||
|
||||
#define CACHE_SIZE 5 // Number of sessions to cache.
|
||||
#define USE_CACHE // Enable SSL session caching.
|
||||
// Caching SSL sessions shortens the length of the SSL handshake.
|
||||
// You can see the performance improvement by looking at the
|
||||
// Network tab of the developper tools of your browser.
|
||||
#define CACHE_SIZE 5 // Number of sessions to cache.
|
||||
#define USE_CACHE // Enable SSL session caching.
|
||||
// Caching SSL sessions shortens the length of the SSL handshake.
|
||||
// You can see the performance improvement by looking at the
|
||||
// Network tab of the developer tools of your browser.
|
||||
//#define DYNAMIC_CACHE // Whether to dynamically allocate the cache.
|
||||
|
||||
#if defined(USE_CACHE) && defined(DYNAMIC_CACHE)
|
||||
@ -181,7 +181,7 @@ void setup() {
|
||||
#ifndef USE_EC
|
||||
server.setRSACert(serverCertList, serverPrivKey);
|
||||
#else
|
||||
server.setECCert(serverCertList, BR_KEYTYPE_KEYX|BR_KEYTYPE_SIGN, serverPrivKey);
|
||||
server.setECCert(serverCertList, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, serverPrivKey);
|
||||
#endif
|
||||
|
||||
// Set the server's cache
|
||||
@ -193,31 +193,28 @@ void setup() {
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static const char *HTTP_RES =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: 62\r\n"
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"
|
||||
"\r\n"
|
||||
"<html>\r\n"
|
||||
"<body>\r\n"
|
||||
"<p>Hello from ESP8266!</p>\r\n"
|
||||
"</body>\r\n"
|
||||
"</html>\r\n";
|
||||
static const char *HTTP_RES = "HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: 62\r\n"
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"
|
||||
"\r\n"
|
||||
"<html>\r\n"
|
||||
"<body>\r\n"
|
||||
"<p>Hello from ESP8266!</p>\r\n"
|
||||
"</body>\r\n"
|
||||
"</html>\r\n";
|
||||
|
||||
void loop() {
|
||||
static int cnt;
|
||||
BearSSL::WiFiClientSecure incoming = server.available();
|
||||
if (!incoming) {
|
||||
return;
|
||||
}
|
||||
Serial.printf("Incoming connection...%d\n",cnt++);
|
||||
|
||||
BearSSL::WiFiClientSecure incoming = server.accept();
|
||||
if (!incoming) { return; }
|
||||
Serial.printf("Incoming connection...%d\n", cnt++);
|
||||
|
||||
// Ugly way to wait for \r\n (i.e. end of HTTP request which we don't actually parse here)
|
||||
uint32_t timeout=millis() + 1000;
|
||||
uint32_t timeout = millis() + 1000;
|
||||
int lcwn = 0;
|
||||
for (;;) {
|
||||
unsigned char x=0;
|
||||
unsigned char x = 0;
|
||||
if ((millis() > timeout) || (incoming.available() && incoming.read(&x, 1) < 0)) {
|
||||
incoming.stop();
|
||||
Serial.printf("Connection error, closed\n");
|
||||
@ -228,14 +225,12 @@ void loop() {
|
||||
} else if (x == 0x0D) {
|
||||
continue;
|
||||
} else if (x == 0x0A) {
|
||||
if (lcwn) {
|
||||
break;
|
||||
}
|
||||
if (lcwn) { break; }
|
||||
lcwn = 1;
|
||||
} else
|
||||
lcwn = 0;
|
||||
}
|
||||
incoming.write((uint8_t*)HTTP_RES, strlen(HTTP_RES));
|
||||
incoming.write((uint8_t *)HTTP_RES, strlen(HTTP_RES));
|
||||
incoming.flush();
|
||||
incoming.stop();
|
||||
Serial.printf("Connection closed.\n");
|
||||
|
@ -67,7 +67,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
@ -160,8 +160,7 @@ seoK24dHmt6tWmn/sbxX7Aa6TL/4mVlFoOgcaTJyVaY/BrY=
|
||||
// head of the app.
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void setClock()
|
||||
{
|
||||
void setClock() {
|
||||
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
Serial.print("Waiting for NTP time sync: ");
|
||||
@ -199,7 +198,7 @@ void setup() {
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
setClock(); // Required for X.509 validation
|
||||
setClock(); // Required for X.509 validation
|
||||
|
||||
// Attach the server private cert/key combo
|
||||
BearSSL::X509List *serverCertList = new BearSSL::X509List(server_cert);
|
||||
@ -214,30 +213,27 @@ void setup() {
|
||||
server.begin();
|
||||
}
|
||||
|
||||
static const char *HTTP_RES =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: 59\r\n"
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"
|
||||
"\r\n"
|
||||
"<html>\r\n"
|
||||
"<body>\r\n"
|
||||
"<p>Hello my friend!</p>\r\n"
|
||||
"</body>\r\n"
|
||||
"</html>\r\n";
|
||||
static const char *HTTP_RES = "HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: 59\r\n"
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"
|
||||
"\r\n"
|
||||
"<html>\r\n"
|
||||
"<body>\r\n"
|
||||
"<p>Hello my friend!</p>\r\n"
|
||||
"</body>\r\n"
|
||||
"</html>\r\n";
|
||||
|
||||
void loop() {
|
||||
BearSSL::WiFiClientSecure incoming = server.available();
|
||||
if (!incoming) {
|
||||
return;
|
||||
}
|
||||
BearSSL::WiFiClientSecure incoming = server.accept();
|
||||
if (!incoming) { return; }
|
||||
Serial.println("Incoming connection...\n");
|
||||
|
||||
|
||||
// Ugly way to wait for \r\n (i.e. end of HTTP request which we don't actually parse here)
|
||||
uint32_t timeout=millis() + 1000;
|
||||
uint32_t timeout = millis() + 1000;
|
||||
int lcwn = 0;
|
||||
for (;;) {
|
||||
unsigned char x=0;
|
||||
unsigned char x = 0;
|
||||
if ((millis() > timeout) || (incoming.available() && incoming.read(&x, 1) < 0)) {
|
||||
incoming.stop();
|
||||
Serial.printf("Connection error, closed\n");
|
||||
@ -248,14 +244,12 @@ void loop() {
|
||||
} else if (x == 0x0D) {
|
||||
continue;
|
||||
} else if (x == 0x0A) {
|
||||
if (lcwn) {
|
||||
break;
|
||||
}
|
||||
if (lcwn) { break; }
|
||||
lcwn = 1;
|
||||
} else
|
||||
lcwn = 0;
|
||||
}
|
||||
incoming.write((uint8_t*)HTTP_RES, strlen(HTTP_RES));
|
||||
incoming.write((uint8_t *)HTTP_RES, strlen(HTTP_RES));
|
||||
incoming.flush();
|
||||
incoming.stop();
|
||||
Serial.printf("Connection closed.\n");
|
||||
|
@ -1,22 +1,28 @@
|
||||
// Example of using SSL sessions to speed up SSL connection initiation
|
||||
//
|
||||
// Note that sessions are a function of individual HTTPS servers, so if you
|
||||
// are connecting to a service through a load abalncer (i.e. Azure, AWS, GitHub)
|
||||
// two connections to the same IP address will generally connect to two
|
||||
// different web servers, meaning that sessions won't work. If you are
|
||||
// connecting to a single server not behind a load balancer/etc., however,
|
||||
// there should be a significant speedup.
|
||||
//
|
||||
// September 2018 by Earle F. Philhower, III
|
||||
// Released to the public domain
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <time.h>
|
||||
#include "certs.h"
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
const char * host = "api.github.com";
|
||||
const uint16_t port = 443;
|
||||
const char * path = "/";
|
||||
const char *path = "/";
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@ -54,9 +60,7 @@ void setup() {
|
||||
|
||||
// Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response
|
||||
void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) {
|
||||
if (!path) {
|
||||
path = "/";
|
||||
}
|
||||
if (!path) { path = "/"; }
|
||||
|
||||
Serial.printf("Trying: %s:443...", host);
|
||||
client->connect(host, port);
|
||||
@ -76,11 +80,9 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
do {
|
||||
char tmp[32];
|
||||
memset(tmp, 0, 32);
|
||||
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
|
||||
int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1);
|
||||
yield();
|
||||
if (rlen < 0) {
|
||||
break;
|
||||
}
|
||||
if (rlen < 0) { break; }
|
||||
// Only print out first line up to \r, then abort connection
|
||||
char *nl = strchr(tmp, '\r');
|
||||
if (nl) {
|
||||
@ -97,34 +99,11 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
|
||||
|
||||
void loop() {
|
||||
static const char digicert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
uint32_t start, finish;
|
||||
BearSSL::WiFiClientSecure client;
|
||||
BearSSL::X509List cert(digicert);
|
||||
BearSSL::X509List cert(certForum);
|
||||
const char *host = "esp8266.com";
|
||||
const int port = 443;
|
||||
|
||||
Serial.printf("Connecting without sessions...");
|
||||
start = millis();
|
||||
@ -135,7 +114,7 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
|
||||
BearSSL::Session session;
|
||||
client.setSession(&session);
|
||||
Serial.printf("Connecting with an unitialized session...");
|
||||
Serial.printf("Connecting with an uninitialized session...");
|
||||
start = millis();
|
||||
client.setTrustAnchors(&cert);
|
||||
fetchURL(&client, host, port, path);
|
||||
@ -156,6 +135,5 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
finish = millis();
|
||||
Serial.printf("Total time: %dms\n", finish - start);
|
||||
|
||||
delay(10000); // Avoid DDOSing github
|
||||
delay(10000); // Avoid DDOSing github
|
||||
}
|
||||
|
||||
|
34
libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h
Normal file
34
libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h
Normal file
@ -0,0 +1,34 @@
|
||||
const char certForum [] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
@ -8,18 +8,17 @@
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <StackThunk.h>
|
||||
#include <time.h>
|
||||
#include "certs.h"
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char *ssid = STASSID;
|
||||
const char *pass = STAPSK;
|
||||
|
||||
const char * host = "api.github.com";
|
||||
const uint16_t port = 443;
|
||||
const char * path = "/";
|
||||
const char *path = "/";
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void setClock() {
|
||||
@ -41,9 +40,7 @@ void setClock() {
|
||||
|
||||
// Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response
|
||||
void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) {
|
||||
if (!path) {
|
||||
path = "/";
|
||||
}
|
||||
if (!path) { path = "/"; }
|
||||
|
||||
ESP.resetFreeContStack();
|
||||
uint32_t freeStackStart = ESP.getFreeContStack();
|
||||
@ -65,11 +62,9 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
do {
|
||||
char tmp[32];
|
||||
memset(tmp, 0, 32);
|
||||
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
|
||||
int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1);
|
||||
yield();
|
||||
if (rlen < 0) {
|
||||
break;
|
||||
}
|
||||
if (rlen < 0) { break; }
|
||||
// Only print out first line up to \r, then abort connection
|
||||
char *nl = strchr(tmp, '\r');
|
||||
if (nl) {
|
||||
@ -92,7 +87,7 @@ If there are no CAs or insecure options specified, BearSSL will not connect.
|
||||
Expect the following call to fail as none have been configured.
|
||||
)EOF");
|
||||
BearSSL::WiFiClientSecure client;
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
}
|
||||
|
||||
void fetchInsecure() {
|
||||
@ -103,7 +98,7 @@ which is subject to man-in-the-middle (MITM) attacks.
|
||||
)EOF");
|
||||
BearSSL::WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
}
|
||||
|
||||
void fetchFingerprint() {
|
||||
@ -116,9 +111,8 @@ fingerprints will change if anything changes in the certificate chain
|
||||
the root authorities, etc.).
|
||||
)EOF");
|
||||
BearSSL::WiFiClientSecure client;
|
||||
static const char fp[] PROGMEM = "59:74:61:88:13:CA:12:34:15:4D:11:0A:C1:7F:E6:67:07:69:42:F5";
|
||||
client.setFingerprint(fp);
|
||||
fetchURL(&client, host, port, path);
|
||||
client.setFingerprint(fingerprint_gitlab_com);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
}
|
||||
|
||||
void fetchSelfSigned() {
|
||||
@ -142,51 +136,13 @@ needs to be paired with the private key of the site, which is obviously
|
||||
private and not shared. A MITM without the private key would not be
|
||||
able to establish communications.
|
||||
)EOF");
|
||||
// Extracted by: openssl x509 -pubkey -noout -in servercert.pem
|
||||
static const char pubkey[] PROGMEM = R"KEY(
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+3Up8qBkIn/7S9AfWlH
|
||||
Od8SdXmnWx+JCIHvnWzjFcLeLvQb2rMqqCDL5XDlvkyC5SZ8ZyLITemej5aJYuBv
|
||||
zcKPzyZ0QfYZiskU9nzL2qBQj8alzJJ/Cc32AWuuWrPrzVxBmOEW9gRCGFCD3m0z
|
||||
53y6GjcmBS2wcX7RagqbD7g2frEGko4G7kmW96H6dyh2j9Rou8TwAK6CnbiXPAM/
|
||||
5Q6dyfdYlHOCgP75F7hhdKB5gpprm9A/OnQsmZjUPzy4u0EKCxE8MfhBerZrZdod
|
||||
88ZdDG3CvTgm050bc+lGlbsT+s09lp0dgxSZIeI8+syV2Owt4YF/PdjeeymtzQdI
|
||||
wQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
)KEY";
|
||||
BearSSL::WiFiClientSecure client;
|
||||
BearSSL::PublicKey key(pubkey);
|
||||
BearSSL::PublicKey key(pubkey_gitlab_com);
|
||||
client.setKnownKey(&key);
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
}
|
||||
|
||||
void fetchCertAuthority() {
|
||||
static const char digicert[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
Serial.printf(R"EOF(
|
||||
A specific certification authority can be passed in and used to validate
|
||||
a chain of certificates from a given server. These will be validated
|
||||
@ -197,14 +153,14 @@ BearSSL does verify the notValidBefore/After fields.
|
||||
)EOF");
|
||||
|
||||
BearSSL::WiFiClientSecure client;
|
||||
BearSSL::X509List cert(digicert);
|
||||
BearSSL::X509List cert(cert_USERTrust_RSA_Certification_Authority);
|
||||
client.setTrustAnchors(&cert);
|
||||
Serial.printf("Try validating without setting the time (should fail)\n");
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
|
||||
Serial.printf("Try again after setting NTP time (should pass)\n");
|
||||
setClock();
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
}
|
||||
|
||||
void fetchFaster() {
|
||||
@ -217,18 +173,18 @@ may make sense
|
||||
BearSSL::WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
uint32_t now = millis();
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
uint32_t delta = millis() - now;
|
||||
client.setInsecure();
|
||||
client.setCiphersLessSecure();
|
||||
now = millis();
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
uint32_t delta2 = millis() - now;
|
||||
std::vector<uint16_t> myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA };
|
||||
client.setInsecure();
|
||||
client.setCiphers(myCustomList);
|
||||
now = millis();
|
||||
fetchURL(&client, host, port, path);
|
||||
fetchURL(&client, gitlab_host, gitlab_port, path);
|
||||
uint32_t delta3 = millis() - now;
|
||||
Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\nUsing custom cipher list: %dms\n", delta, delta2, delta3);
|
||||
}
|
||||
|
2
libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate
Executable file
2
libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate
Executable file
@ -0,0 +1,2 @@
|
||||
cd ${0%/*} 2>/dev/null
|
||||
python3 ../../../../tools/cert.py -s www.gitlab.com -n gitlab > certs.h
|
115
libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h
Normal file
115
libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
// this file is autogenerated - any modification will be overwritten
|
||||
// unused symbols will not be linked in the final binary
|
||||
// generated on 2021-07-26 22:04:47
|
||||
// by ['../../../../tools/cert.py', '-s', 'www.gitlab.com', '-n', 'gitlab']
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// certificate chain for www.gitlab.com:443
|
||||
|
||||
const char* gitlab_host = "www.gitlab.com";
|
||||
const uint16_t gitlab_port = 443;
|
||||
|
||||
// CN: gitlab.com => name: gitlab_com
|
||||
// not valid before: 2021-04-12 00:00:00
|
||||
// not valid after: 2022-05-11 23:59:59
|
||||
const char fingerprint_gitlab_com [] PROGMEM = "71:55:5e:29:68:99:43:98:c8:85:35:bd:4c:10:4c:f5:cf:17:09:e6";
|
||||
const char pubkey_gitlab_com [] PROGMEM = R"PUBKEY(
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eeFy86Xbz3ygyCVprHp
|
||||
sPP3zyg0yldkIfqwjsXPH0b+KwQ85s3pzI/5+MVrR2/BGY4ed6mTZ6hvNwQJ2B0E
|
||||
sJrsTb2nuUsXQ0UVO4hvnZ7Dnx8r/bT1cndqa+Mn+bms8/TS4etP72+TLaORBRCz
|
||||
O4L1Hi8r61+zZLnP3DqqHeHAgl5wKHNYpx7yFFl2I71LuLH/pk2ICDBjaHwCIbRW
|
||||
u484no9s1c4VROxqMrQQ/wDMl80MiO9YeNQ5rBHfnabh4rFe9eb2Sd0H/DWBj3SO
|
||||
YBD0kiLI6b5CWYfA76pBSlZg7G3ledvQ+n9FEcS3EOCPKBBZqMDCzEahvHqwJ/r6
|
||||
pwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
)PUBKEY";
|
||||
|
||||
// http://crt.sectigo.com/SectigoRSADomainValidationSecureServerCA.crt
|
||||
// CN: Sectigo RSA Domain Validation Secure Server CA => name: Sectigo_RSA_Domain_Validation_Secure_Server_CA
|
||||
// not valid before: 2018-11-02 00:00:00
|
||||
// not valid after: 2030-12-31 23:59:59
|
||||
const char cert_Sectigo_RSA_Domain_Validation_Secure_Server_CA [] PROGMEM = R"CERT(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
|
||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
|
||||
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
|
||||
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
|
||||
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
|
||||
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
|
||||
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
|
||||
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
|
||||
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
|
||||
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
|
||||
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
|
||||
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
|
||||
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
|
||||
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
|
||||
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
|
||||
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
|
||||
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
|
||||
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
|
||||
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
|
||||
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
|
||||
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
|
||||
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
|
||||
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
|
||||
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
|
||||
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
|
||||
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
|
||||
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
|
||||
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
|
||||
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
|
||||
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
|
||||
-----END CERTIFICATE-----
|
||||
)CERT";
|
||||
|
||||
// http://crt.usertrust.com/USERTrustRSAAddTrustCA.crt
|
||||
// CN: USERTrust RSA Certification Authority => name: USERTrust_RSA_Certification_Authority
|
||||
// not valid before: 2019-03-12 00:00:00
|
||||
// not valid after: 2028-12-31 23:59:59
|
||||
const char cert_USERTrust_RSA_Certification_Authority [] PROGMEM = R"CERT(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7
|
||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4
|
||||
MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5
|
||||
MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO
|
||||
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI
|
||||
s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG
|
||||
vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ
|
||||
Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb
|
||||
IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0
|
||||
tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E
|
||||
xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV
|
||||
icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5
|
||||
D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ
|
||||
WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ
|
||||
5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG
|
||||
KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg
|
||||
EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID
|
||||
ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG
|
||||
BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t
|
||||
L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr
|
||||
BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA
|
||||
A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+
|
||||
rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+
|
||||
/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA
|
||||
CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F
|
||||
zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA
|
||||
vGp4z7h/jnZymQyd/teRCBaho1+V
|
||||
-----END CERTIFICATE-----
|
||||
)CERT";
|
||||
|
||||
|
||||
// end of certificate chain for www.gitlab.com:443
|
||||
////////////////////////////////////////////////////////////
|
||||
|
20
libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino
Normal file
20
libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino
Normal file
@ -0,0 +1,20 @@
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
void setup() {
|
||||
auto& server = WiFi.softAPDhcpServer();
|
||||
server.onSendOptions([](const DhcpServer& server, auto& options) {
|
||||
// VENDOR is... vendor specific
|
||||
options.add(43, { 0xca, 0xfe, 0xca, 0xfe, 0xfe });
|
||||
|
||||
// Captive Portal URI
|
||||
const IPAddress gateway = netif_ip4_addr(server.getNetif());
|
||||
const String captive = F("http://") + gateway.toString();
|
||||
options.add(114, captive.c_str(), captive.length());
|
||||
});
|
||||
WiFi.softAP("TEST", "testtesttest");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(100);
|
||||
}
|
@ -13,51 +13,17 @@
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include "certs.h"
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
const char* host = "api.github.com";
|
||||
const int httpsPort = 443;
|
||||
|
||||
// DigiCert High Assurance EV Root CA
|
||||
const char trustRoot[] PROGMEM = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE6zCCBHGgAwIBAgIQAtX25VXj+RoJlA3D2bWkgzAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTAwLgYDVQQDEydEaWdp
|
||||
Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjEwMzA0MDAw
|
||||
MDAwWhcNMjIwMzA5MjM1OTU5WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
|
||||
aWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHVi
|
||||
LCBJbmMuMRUwEwYDVQQDDAwqLmdpdGh1Yi5jb20wWTATBgcqhkjOPQIBBggqhkjO
|
||||
PQMBBwNCAAQf8SePhtD7JeGm0YuTQ4HihyeENuvsNFdYPPIxIx6Lj9iOu2ECkgy4
|
||||
52UR+mhIF24OvPizDveyCFOqmG/MI7kwo4IDDTCCAwkwHwYDVR0jBBgwFoAUCrwI
|
||||
KReMpTlteg7OM8cus+37w3owHQYDVR0OBBYEFP5TUYtiCp+N3FISu3CqxMlJhdG1
|
||||
MCMGA1UdEQQcMBqCDCouZ2l0aHViLmNvbYIKZ2l0aHViLmNvbTAOBgNVHQ8BAf8E
|
||||
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGXBgNVHR8EgY8w
|
||||
gYwwRKBCoECGPmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5
|
||||
YnJpZEVDQ1NIQTM4NDIwMjBDQTEuY3JsMESgQqBAhj5odHRwOi8vY3JsNC5kaWdp
|
||||
Y2VydC5jb20vRGlnaUNlcnRUTFNIeWJyaWRFQ0NTSEEzODQyMDIwQ0ExLmNybDA+
|
||||
BgNVHSAENzA1MDMGBmeBDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRp
|
||||
Z2ljZXJ0LmNvbS9DUFMwgYMGCCsGAQUFBwEBBHcwdTAkBggrBgEFBQcwAYYYaHR0
|
||||
cDovL29jc3AuZGlnaWNlcnQuY29tME0GCCsGAQUFBzAChkFodHRwOi8vY2FjZXJ0
|
||||
cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNIeWJyaWRFQ0NTSEEzODQyMDIwQ0Ex
|
||||
LmNydDAMBgNVHRMBAf8EAjAAMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUAKXm+
|
||||
8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4QAAAF3/bWc4AAABAMARjBEAiBm
|
||||
IdofaKj+XfeISM/2tjap1nQY1afFSBAcdw/YtgjmSQIgMqWoDyfO66suyk2VFcld
|
||||
1C+WHUNGvXsCRPof5HG5QQgAdgAiRUUHWVUkVpY/oS/x922G4CMmY63AS39dxoNc
|
||||
buIPAgAAAXf9tZ0CAAAEAwBHMEUCIQCJzwZRfAvv0izotFx2KE0sgV8O+NfuHUpa
|
||||
1866RqKEtwIgc65P+xToSqPbp/J1gSFBJgySI/a1YoB+3p8xXTYaDsAwCgYIKoZI
|
||||
zj0EAwMDaAAwZQIxAL8fIlMNWdeKHalpm9z+ksCuYT4tSN1ubXeNvDywr56me+yT
|
||||
+fr42MnEcBdUtLOVOAIwPNC9fAJjyHHTL2vaRW1JRnrovLKDQVbZpZNIZnlY3WFu
|
||||
kmxiBWDOpyfJrG9vQ25K
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
X509List cert(trustRoot);
|
||||
X509List cert(cert_DigiCert_High_Assurance_EV_Root_CA);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@ -94,12 +60,12 @@ void setup() {
|
||||
// Use WiFiClientSecure class to create TLS connection
|
||||
WiFiClientSecure client;
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(host);
|
||||
Serial.println(github_host);
|
||||
|
||||
Serial.printf("Using certificate: %s\n", trustRoot);
|
||||
Serial.printf("Using certificate: %s\n", cert_DigiCert_High_Assurance_EV_Root_CA);
|
||||
client.setTrustAnchors(&cert);
|
||||
|
||||
if (!client.connect(host, httpsPort)) {
|
||||
if (!client.connect(github_host, github_port)) {
|
||||
Serial.println("Connection failed");
|
||||
return;
|
||||
}
|
||||
@ -108,10 +74,7 @@ void setup() {
|
||||
Serial.print("Requesting URL: ");
|
||||
Serial.println(url);
|
||||
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
|
||||
"Host: " + host + "\r\n" +
|
||||
"User-Agent: BuildFailureDetectorESP8266\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + github_host + "\r\n" + "User-Agent: BuildFailureDetectorESP8266\r\n" + "Connection: close\r\n\r\n");
|
||||
|
||||
Serial.println("Request sent");
|
||||
while (client.connected()) {
|
||||
@ -123,7 +86,7 @@ void setup() {
|
||||
}
|
||||
String line = client.readStringUntil('\n');
|
||||
if (line.startsWith("{\"state\":\"success\"")) {
|
||||
Serial.println("esp8266/Arduino CI successfull!");
|
||||
Serial.println("esp8266/Arduino CI successful!");
|
||||
} else {
|
||||
Serial.println("esp8266/Arduino CI has failed");
|
||||
}
|
||||
@ -134,5 +97,4 @@ void setup() {
|
||||
Serial.println("Closing connection");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
void loop() {}
|
||||
|
2
libraries/ESP8266WiFi/examples/HTTPSRequest/certUpdate
Executable file
2
libraries/ESP8266WiFi/examples/HTTPSRequest/certUpdate
Executable file
@ -0,0 +1,2 @@
|
||||
cd ${0%/*} 2>/dev/null
|
||||
python3 ../../../../tools/cert.py -s api.github.com -n github > certs.h
|
90
libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h
Normal file
90
libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
// this file is autogenerated - any modification will be overwritten
|
||||
// unused symbols will not be linked in the final binary
|
||||
// generated on 2021-07-26 22:04:48
|
||||
// by ['../../../../tools/cert.py', '-s', 'api.github.com', '-n', 'github']
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// certificate chain for api.github.com:443
|
||||
|
||||
const char* github_host = "api.github.com";
|
||||
const uint16_t github_port = 443;
|
||||
|
||||
// CN: *.github.com => name: __github_com
|
||||
// not valid before: 2021-03-25 00:00:00
|
||||
// not valid after: 2022-03-30 23:59:59
|
||||
const char fingerprint___github_com [] PROGMEM = "96:84:07:df:0b:1c:f6:58:14:df:d7:33:35:57:51:9b:15:4d:8c:e7";
|
||||
const char pubkey___github_com [] PROGMEM = R"PUBKEY(
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElL9/+0TidTIALPfU/tiS6pI8zAIk
|
||||
rU4pohUldVc0bb6O3FARl3cnqIDK9SoF65z3xiR6XsnFS8F0Oy/chXR/kQ==
|
||||
-----END PUBLIC KEY-----
|
||||
)PUBKEY";
|
||||
|
||||
// http://cacerts.digicert.com/DigiCertHighAssuranceTLSHybridECCSHA2562020CA1.crt
|
||||
// CN: DigiCert High Assurance TLS Hybrid ECC SHA256 2020 CA1 => name: DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1
|
||||
// not valid before: 2020-12-17 00:00:00
|
||||
// not valid after: 2030-12-16 23:59:59
|
||||
const char cert_DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 [] PROGMEM = R"CERT(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL
|
||||
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE
|
||||
aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy
|
||||
MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef
|
||||
6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9
|
||||
o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq
|
||||
II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD
|
||||
VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr
|
||||
BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
|
||||
bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
|
||||
ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo
|
||||
dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS
|
||||
b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB
|
||||
ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu
|
||||
jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv
|
||||
mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl
|
||||
RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+
|
||||
mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh
|
||||
6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0=
|
||||
-----END CERTIFICATE-----
|
||||
)CERT";
|
||||
|
||||
// http://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt
|
||||
// CN: DigiCert High Assurance EV Root CA => name: DigiCert_High_Assurance_EV_Root_CA
|
||||
// not valid before: 2006-11-10 00:00:00
|
||||
// not valid after: 2031-11-10 00:00:00
|
||||
const char cert_DigiCert_High_Assurance_EV_Root_CA [] PROGMEM = R"CERT(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
)CERT";
|
||||
|
||||
|
||||
// end of certificate chain for api.github.com:443
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -23,12 +23,12 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
#define FQDN F("www.google.com") // with both IPv4 & IPv6 addresses
|
||||
#define FQDN2 F("www.yahoo.com") // with both IPv4 & IPv6 addresses
|
||||
#define FQDN6 F("ipv6.google.com") // does not resolve in IPv4
|
||||
#define FQDN F("www.google.com") // with both IPv4 & IPv6 addresses
|
||||
#define FQDN2 F("www.yahoo.com") // with both IPv4 & IPv6 addresses
|
||||
#define FQDN6 F("ipv6.google.com") // does not resolve in IPv4
|
||||
#define STATUSDELAY_MS 10000
|
||||
#define TCP_PORT 23
|
||||
#define UDP_PORT 23
|
||||
@ -71,29 +71,17 @@ void status(Print& out) {
|
||||
|
||||
for (int i = 0; i < DNS_MAX_SERVERS; i++) {
|
||||
IPAddress dns = WiFi.dnsIP(i);
|
||||
if (dns.isSet()) {
|
||||
out.printf("dns%d: %s\n", i, dns.toString().c_str());
|
||||
}
|
||||
if (dns.isSet()) { out.printf("dns%d: %s\n", i, dns.toString().c_str()); }
|
||||
}
|
||||
|
||||
out.println(F("Try me at these addresses:"));
|
||||
out.println(F("(with 'telnet <addr> or 'nc -u <addr> 23')"));
|
||||
for (auto a : addrList) {
|
||||
out.printf("IF='%s' IPv6=%d local=%d hostname='%s' addr= %s",
|
||||
a.ifname().c_str(),
|
||||
a.isV6(),
|
||||
a.isLocal(),
|
||||
a.ifhostname(),
|
||||
a.toString().c_str());
|
||||
out.printf("IF='%s' IPv6=%d local=%d hostname='%s' addr= %s", a.ifname().c_str(), a.isV6(), a.isLocal(), a.ifhostname(), a.toString().c_str());
|
||||
|
||||
if (a.isLegacy()) {
|
||||
out.printf(" / mask:%s / gw:%s",
|
||||
a.netmask().toString().c_str(),
|
||||
a.gw().toString().c_str());
|
||||
}
|
||||
if (a.isLegacy()) { out.printf(" / mask:%s / gw:%s", a.netmask().toString().c_str(), a.gw().toString().c_str()); }
|
||||
|
||||
out.println();
|
||||
|
||||
}
|
||||
|
||||
// lwIP's dns client will ask for IPv4 first (by default)
|
||||
@ -101,8 +89,8 @@ void status(Print& out) {
|
||||
fqdn(out, FQDN);
|
||||
fqdn(out, FQDN6);
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
fqdn_rt(out, FQDN, DNSResolveType::DNS_AddrType_IPv4_IPv6); // IPv4 before IPv6
|
||||
fqdn_rt(out, FQDN2, DNSResolveType::DNS_AddrType_IPv6_IPv4); // IPv6 before IPv4
|
||||
fqdn_rt(out, FQDN, DNSResolveType::DNS_AddrType_IPv4_IPv6); // IPv4 before IPv6
|
||||
fqdn_rt(out, FQDN2, DNSResolveType::DNS_AddrType_IPv6_IPv4); // IPv6 before IPv4
|
||||
#endif
|
||||
out.println(F("------------------------------"));
|
||||
}
|
||||
@ -125,7 +113,7 @@ void setup() {
|
||||
|
||||
status(Serial);
|
||||
|
||||
#if 0 // 0: legacy connecting loop - 1: wait for IPv6
|
||||
#if 0 // 0: legacy connecting loop - 1: wait for IPv6
|
||||
|
||||
// legacy loop (still valid with IPv4 only)
|
||||
|
||||
@ -146,11 +134,9 @@ void setup() {
|
||||
for (bool configured = false; !configured;) {
|
||||
for (auto addr : addrList)
|
||||
if ((configured = !addr.isLocal()
|
||||
// && addr.isV6() // uncomment when IPv6 is mandatory
|
||||
// && addr.ifnumber() == STATION_IF
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
// && addr.isV6() // uncomment when IPv6 is mandatory
|
||||
// && addr.ifnumber() == STATION_IF
|
||||
)) { break; }
|
||||
Serial.print('.');
|
||||
delay(500);
|
||||
}
|
||||
@ -175,7 +161,7 @@ unsigned long statusTimeMs = 0;
|
||||
void loop() {
|
||||
|
||||
if (statusServer.hasClient()) {
|
||||
WiFiClient cli = statusServer.available();
|
||||
WiFiClient cli = statusServer.accept();
|
||||
status(cli);
|
||||
}
|
||||
|
||||
@ -188,10 +174,8 @@ void loop() {
|
||||
udp.remoteIP().printTo(Serial);
|
||||
Serial.print(F(" :"));
|
||||
Serial.println(udp.remotePort());
|
||||
int c;
|
||||
while ((c = udp.read()) >= 0) {
|
||||
Serial.write(c);
|
||||
}
|
||||
int c;
|
||||
while ((c = udp.read()) >= 0) { Serial.write(c); }
|
||||
|
||||
// send a reply, to the IP address and port that sent us the packet we received
|
||||
udp.beginPacket(udp.remoteIP(), udp.remotePort());
|
||||
@ -200,8 +184,5 @@ void loop() {
|
||||
}
|
||||
|
||||
|
||||
if (showStatusOnSerialNow) {
|
||||
status(Serial);
|
||||
}
|
||||
|
||||
if (showStatusOnSerialNow) { status(Serial); }
|
||||
}
|
||||
|
@ -23,24 +23,24 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char * ssid = STASSID; // your network SSID (name)
|
||||
const char * pass = STAPSK; // your network password
|
||||
const char* ssid = STASSID; // your network SSID (name)
|
||||
const char* pass = STAPSK; // your network password
|
||||
|
||||
|
||||
unsigned int localPort = 2390; // local port to listen for UDP packets
|
||||
unsigned int localPort = 2390; // local port to listen for UDP packets
|
||||
|
||||
/* Don't hardwire the IP address or we won't get the benefits of the pool.
|
||||
Lookup the IP address for the host name instead */
|
||||
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
|
||||
IPAddress timeServerIP; // time.nist.gov NTP server address
|
||||
// IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
|
||||
IPAddress timeServerIP; // time.nist.gov NTP server address
|
||||
const char* ntpServerName = "time.nist.gov";
|
||||
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
|
||||
|
||||
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
|
||||
|
||||
// A UDP instance to let us send and receive packets over UDP
|
||||
WiFiUDP udp;
|
||||
@ -73,10 +73,10 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//get a random server from the pool
|
||||
// get a random server from the pool
|
||||
WiFi.hostByName(ntpServerName, timeServerIP);
|
||||
|
||||
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
|
||||
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
|
||||
// wait to see if a reply is available
|
||||
delay(1000);
|
||||
|
||||
@ -87,10 +87,10 @@ void loop() {
|
||||
Serial.print("packet received, length=");
|
||||
Serial.println(cb);
|
||||
// We've received a packet, read the data from it
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
|
||||
|
||||
//the timestamp starts at byte 40 of the received packet and is four bytes,
|
||||
// or two words, long. First, esxtract the two words:
|
||||
// the timestamp starts at byte 40 of the received packet and is four bytes,
|
||||
// or two words, long. First, esxtract the two words:
|
||||
|
||||
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
|
||||
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
|
||||
@ -112,19 +112,19 @@ void loop() {
|
||||
|
||||
// print the hour, minute and second:
|
||||
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
|
||||
Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
|
||||
Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
|
||||
Serial.print(':');
|
||||
if (((epoch % 3600) / 60) < 10) {
|
||||
// In the first 10 minutes of each hour, we'll want a leading '0'
|
||||
Serial.print('0');
|
||||
}
|
||||
Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
|
||||
Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
|
||||
Serial.print(':');
|
||||
if ((epoch % 60) < 10) {
|
||||
// In the first 10 seconds of each minute, we'll want a leading '0'
|
||||
Serial.print('0');
|
||||
}
|
||||
Serial.println(epoch % 60); // print the second
|
||||
Serial.println(epoch % 60); // print the second
|
||||
}
|
||||
// wait ten seconds before asking for the time again
|
||||
delay(10000);
|
||||
@ -137,19 +137,19 @@ void sendNTPpacket(IPAddress& address) {
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
// Initialize values needed to form NTP request
|
||||
// (see URL above for details on the packets)
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
// 8 bytes of zero for Root Delay & Root Dispersion
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
|
||||
// all NTP fields have been given values, now
|
||||
// you can send a packet requesting a timestamp:
|
||||
udp.beginPacket(address, 123); //NTP requests are to port 123
|
||||
udp.beginPacket(address, 123); // NTP requests are to port 123
|
||||
udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
udp.endPacket();
|
||||
}
|
||||
|
72
libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino
Normal file
72
libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Pager Server
|
||||
|
||||
The ESP8266WiFi library's WiFiServer and WiFiServerSecure
|
||||
work differently than WiFiServer and EthernetSever
|
||||
in Arduino networking libraries.
|
||||
This example demonstrates the ArduinoWiFiServer,
|
||||
which enhances the WiFiServer.
|
||||
ArduinoWiFiServer has available() behaving as documented
|
||||
and supports send-to-all-clients functionality, which
|
||||
is not implemented in ESP8266WiFi library's WiFiServer
|
||||
and WiFiServerSecure.
|
||||
|
||||
The example is a simple server that echoes any incoming
|
||||
messages to all connected clients. Connect two or more
|
||||
telnet sessions to see how server.available() and
|
||||
server.print() work.
|
||||
|
||||
created in September 2020 for ESP8266WiFi library
|
||||
by Juraj Andrassy https://github.com/jandrassy
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ArduinoWiFiServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
ArduinoWiFiServer server(2323);
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println();
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
server.begin();
|
||||
|
||||
IPAddress ip = WiFi.localIP();
|
||||
Serial.println();
|
||||
Serial.println("Connected to WiFi network.");
|
||||
Serial.print("To access the server, connect with Telnet client to ");
|
||||
Serial.print(ip);
|
||||
Serial.println(" 2323");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
WiFiClient client = server.available(); // returns first client which has data to read or a 'false' client
|
||||
if (client) { // client is true only if it is connected and has data to read
|
||||
String s = client.readStringUntil('\n'); // read the message incoming from one of the clients
|
||||
s.trim(); // trim eventual \r
|
||||
Serial.println(s); // print the message to Serial Monitor
|
||||
client.print("echo: "); // this is only for the sending client
|
||||
server.println(s); // send the message to all connected clients
|
||||
server.flush(); // flush the buffers
|
||||
}
|
||||
}
|
@ -7,13 +7,12 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "mynetwork"
|
||||
#define STAPSK "mynetworkpassword"
|
||||
#define STAPSK "mynetworkpassword"
|
||||
#endif
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <lwip/napt.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <LwipDhcpServer.h>
|
||||
|
||||
#define NAPT 1000
|
||||
#define NAPT_PORT 10
|
||||
@ -30,7 +29,7 @@ void dump(int netif_idx, const char* data, size_t len, int out, int success) {
|
||||
// optional filter example: if (netDump_is_ARP(data))
|
||||
{
|
||||
netDump(Serial, data, len);
|
||||
//netDumpHex(Serial, data, len);
|
||||
// netDumpHex(Serial, data, len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -51,19 +50,18 @@ void setup() {
|
||||
Serial.print('.');
|
||||
delay(500);
|
||||
}
|
||||
Serial.printf("\nSTA: %s (dns: %s / %s)\n",
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.dnsIP(0).toString().c_str(),
|
||||
WiFi.dnsIP(1).toString().c_str());
|
||||
Serial.printf("\nSTA: %s (dns: %s / %s)\n", WiFi.localIP().toString().c_str(), WiFi.dnsIP(0).toString().c_str(), WiFi.dnsIP(1).toString().c_str());
|
||||
|
||||
// give DNS servers to AP side
|
||||
dhcpSoftAP.dhcps_set_dns(0, WiFi.dnsIP(0));
|
||||
dhcpSoftAP.dhcps_set_dns(1, WiFi.dnsIP(1));
|
||||
// By default, DNS option will point to the interface IP
|
||||
// Instead, point it to the real DNS server.
|
||||
// Notice that:
|
||||
// - DhcpServer class only supports IPv4
|
||||
// - Only a single IP can be set
|
||||
auto& server = WiFi.softAPDhcpServer();
|
||||
server.setDns(WiFi.dnsIP(0));
|
||||
|
||||
WiFi.softAPConfig( // enable AP, with android-compatible google domain
|
||||
IPAddress(172, 217, 28, 254),
|
||||
IPAddress(172, 217, 28, 254),
|
||||
IPAddress(255, 255, 255, 0));
|
||||
IPAddress(172, 217, 28, 254), IPAddress(172, 217, 28, 254), IPAddress(255, 255, 255, 0));
|
||||
WiFi.softAP(STASSID "extender", STAPSK);
|
||||
Serial.printf("AP: %s\n", WiFi.softAPIP().toString().c_str());
|
||||
|
||||
@ -73,14 +71,10 @@ void setup() {
|
||||
if (ret == ERR_OK) {
|
||||
ret = ip_napt_enable_no(SOFTAP_IF, 1);
|
||||
Serial.printf("ip_napt_enable_no(SOFTAP_IF): ret=%d (OK=%d)\n", (int)ret, (int)ERR_OK);
|
||||
if (ret == ERR_OK) {
|
||||
Serial.printf("WiFi Network '%s' with same password is now NATed behind '%s'\n", STASSID "extender", STASSID);
|
||||
}
|
||||
if (ret == ERR_OK) { Serial.printf("WiFi Network '%s' with same password is now NATed behind '%s'\n", STASSID "extender", STASSID); }
|
||||
}
|
||||
Serial.printf("Heap after napt init: %d\n", ESP.getFreeHeap());
|
||||
if (ret != ERR_OK) {
|
||||
Serial.printf("NAPT initialization failed\n");
|
||||
}
|
||||
if (ret != ERR_OK) { Serial.printf("NAPT initialization failed\n"); }
|
||||
}
|
||||
|
||||
#else
|
||||
@ -92,6 +86,4 @@ void setup() {
|
||||
|
||||
#endif
|
||||
|
||||
void loop() {
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <LwipDhcpServer.h>
|
||||
|
||||
/* Set these to your desired credentials. */
|
||||
const char *ssid = "ESPap";
|
||||
@ -76,8 +75,9 @@ void setup() {
|
||||
...
|
||||
any client not listed will use next IP address available from the range (here 192.168.0.102 and more)
|
||||
*/
|
||||
dhcpSoftAP.add_dhcps_lease(mac_CAM); // always 192.168.0.100
|
||||
dhcpSoftAP.add_dhcps_lease(mac_PC); // always 192.168.0.101
|
||||
auto &dhcpServer = WiFi.softAPDhcpServer();
|
||||
dhcpServer.add_dhcps_lease(mac_CAM); // always 192.168.0.100
|
||||
dhcpServer.add_dhcps_lease(mac_PC); // always 192.168.0.101
|
||||
/* Start Access Point. You can remove the password parameter if you want the AP to be open. */
|
||||
WiFi.softAP(ssid, password);
|
||||
Serial.print("AP IP address: ");
|
||||
|
@ -20,14 +20,14 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
unsigned int localPort = 8888; // local port to listen on
|
||||
unsigned int localPort = 8888; // local port to listen on
|
||||
|
||||
// buffers for receiving and sending data
|
||||
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet,
|
||||
char ReplyBuffer[] = "acknowledged\r\n"; // a string to send back
|
||||
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; // buffer to hold incoming packet,
|
||||
char ReplyBuffer[] = "acknowledged\r\n"; // a string to send back
|
||||
|
||||
WiFiUDP Udp;
|
||||
|
||||
@ -49,11 +49,7 @@ void loop() {
|
||||
// if there's data available, read a packet
|
||||
int packetSize = Udp.parsePacket();
|
||||
if (packetSize) {
|
||||
Serial.printf("Received packet of size %d from %s:%d\n (to %s:%d, free heap = %d B)\n",
|
||||
packetSize,
|
||||
Udp.remoteIP().toString().c_str(), Udp.remotePort(),
|
||||
Udp.destinationIP().toString().c_str(), Udp.localPort(),
|
||||
ESP.getFreeHeap());
|
||||
Serial.printf("Received packet of size %d from %s:%d\n (to %s:%d, free heap = %d B)\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort(), Udp.destinationIP().toString().c_str(), Udp.localPort(), ESP.getFreeHeap());
|
||||
|
||||
// read the packet into packetBufffer
|
||||
int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
|
||||
@ -66,11 +62,10 @@ void loop() {
|
||||
Udp.write(ReplyBuffer);
|
||||
Udp.endPacket();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
test (shell/netcat):
|
||||
--------------------
|
||||
nc -u 192.168.esp.address 8888
|
||||
nc -u 192.168.esp.address 8888
|
||||
*/
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
#ifndef APSSID
|
||||
#define APSSID "ESPap"
|
||||
#define APPSK "thereisnospoon"
|
||||
#define APPSK "thereisnospoon"
|
||||
#endif
|
||||
|
||||
/* Set these to your desired credentials. */
|
||||
|
@ -7,10 +7,10 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
const char* host = "djxmmx.net";
|
||||
@ -61,9 +61,7 @@ void loop() {
|
||||
|
||||
// This will send a string to the server
|
||||
Serial.println("sending data to server");
|
||||
if (client.connected()) {
|
||||
client.println("hello from ESP8266");
|
||||
}
|
||||
if (client.connected()) { client.println("hello from ESP8266"); }
|
||||
|
||||
// wait for data to be available
|
||||
unsigned long timeout = millis();
|
||||
@ -90,7 +88,7 @@ void loop() {
|
||||
client.stop();
|
||||
|
||||
if (wait) {
|
||||
delay(300000); // execute once every 5 minutes, don't flood remote service
|
||||
delay(300000); // execute once every 5 minutes, don't flood remote service
|
||||
}
|
||||
wait = true;
|
||||
}
|
||||
|
@ -9,10 +9,10 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
const char* host = "192.168.1.1";
|
||||
@ -64,7 +64,7 @@ void loop() {
|
||||
// This will send the request to the server
|
||||
client.println("hello from ESP8266");
|
||||
|
||||
//read back one line from server
|
||||
// read back one line from server
|
||||
Serial.println("receiving from remote server");
|
||||
String line = client.readStringUntil('\r');
|
||||
Serial.println(line);
|
||||
@ -75,4 +75,3 @@ void loop() {
|
||||
Serial.println("wait 5 sec...");
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <PolledTimeout.h>
|
||||
#include <algorithm> // std::min
|
||||
#include <algorithm> // std::min
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
constexpr int port = 23;
|
||||
@ -19,12 +19,12 @@ constexpr int port = 23;
|
||||
WiFiServer server(port);
|
||||
WiFiClient client;
|
||||
|
||||
constexpr size_t sizes [] = { 0, 512, 384, 256, 128, 64, 16, 8, 4 };
|
||||
constexpr size_t sizes[] = { 0, 512, 384, 256, 128, 64, 16, 8, 4 };
|
||||
constexpr uint32_t breathMs = 200;
|
||||
esp8266::polledTimeout::oneShotFastMs enoughMs(breathMs);
|
||||
esp8266::polledTimeout::periodicFastMs test(2000);
|
||||
int t = 1; // test (1, 2 or 3, see below)
|
||||
int s = 0; // sizes[] index
|
||||
int t = 1; // test (1, 2 or 3, see below)
|
||||
int s = 0; // sizes[] index
|
||||
|
||||
void setup() {
|
||||
|
||||
@ -63,31 +63,40 @@ void loop() {
|
||||
static uint32_t cnt = 0;
|
||||
if (test && cnt) {
|
||||
Serial.printf("measured-block-size=%u min-free-stack=%u", tot / cnt, ESP.getFreeContStack());
|
||||
if (t == 2 && sizes[s]) {
|
||||
Serial.printf(" (blocks: at most %d bytes)", sizes[s]);
|
||||
}
|
||||
if (t == 3 && sizes[s]) {
|
||||
Serial.printf(" (blocks: exactly %d bytes)", sizes[s]);
|
||||
}
|
||||
if (t == 3 && !sizes[s]) {
|
||||
Serial.printf(" (blocks: any size)");
|
||||
}
|
||||
if (t == 2 && sizes[s]) { Serial.printf(" (blocks: at most %d bytes)", sizes[s]); }
|
||||
if (t == 3 && sizes[s]) { Serial.printf(" (blocks: exactly %d bytes)", sizes[s]); }
|
||||
if (t == 3 && !sizes[s]) { Serial.printf(" (blocks: any size)"); }
|
||||
Serial.printf("\n");
|
||||
}
|
||||
|
||||
//check if there are any new clients
|
||||
// check if there are any new clients
|
||||
if (server.hasClient()) {
|
||||
client = server.available();
|
||||
client = server.accept();
|
||||
Serial.println("New client");
|
||||
}
|
||||
|
||||
if (Serial.available()) {
|
||||
s = (s + 1) % (sizeof(sizes) / sizeof(sizes[0]));
|
||||
switch (Serial.read()) {
|
||||
case '1': if (t != 1) s = 0; t = 1; Serial.println("byte-by-byte (watch then press 2, 3 or 4)"); break;
|
||||
case '2': if (t != 2) s = 1; t = 2; Serial.printf("through buffer (watch then press 2 again, or 1, 3 or 4)\n"); break;
|
||||
case '3': if (t != 3) s = 0; t = 3; Serial.printf("direct access (sendAvailable - watch then press 3 again, or 1, 2 or 4)\n"); break;
|
||||
case '4': t = 4; Serial.printf("direct access (sendAll - close peer to stop, then press 1, 2 or 3 before restarting peer)\n"); break;
|
||||
case '1':
|
||||
if (t != 1) s = 0;
|
||||
t = 1;
|
||||
Serial.println("byte-by-byte (watch then press 2, 3 or 4)");
|
||||
break;
|
||||
case '2':
|
||||
if (t != 2) s = 1;
|
||||
t = 2;
|
||||
Serial.printf("through buffer (watch then press 2 again, or 1, 3 or 4)\n");
|
||||
break;
|
||||
case '3':
|
||||
if (t != 3) s = 0;
|
||||
t = 3;
|
||||
Serial.printf("direct access (sendAvailable - watch then press 3 again, or 1, 2 or 4)\n");
|
||||
break;
|
||||
case '4':
|
||||
t = 4;
|
||||
Serial.printf("direct access (sendAll - close peer to stop, then press 1, 2 or 3 before restarting peer)\n");
|
||||
break;
|
||||
}
|
||||
tot = cnt = 0;
|
||||
ESP.resetFreeContStack();
|
||||
@ -113,9 +122,7 @@ void loop() {
|
||||
uint8_t buf[maxTo];
|
||||
size_t tcp_got = client.read(buf, maxTo);
|
||||
size_t tcp_sent = client.write(buf, tcp_got);
|
||||
if (tcp_sent != maxTo) {
|
||||
Serial.printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxTo, tcp_got, tcp_sent);
|
||||
}
|
||||
if (tcp_sent != maxTo) { Serial.printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxTo, tcp_got, tcp_sent); }
|
||||
tot += tcp_sent;
|
||||
cnt++;
|
||||
}
|
||||
@ -141,7 +148,7 @@ void loop() {
|
||||
|
||||
else if (t == 4) {
|
||||
// stream to print, possibly with only one copy
|
||||
tot += client.sendAll(&client); // this one might not exit until peer close
|
||||
tot += client.sendAll(&client); // this one might not exit until peer close
|
||||
cnt++;
|
||||
|
||||
switch (client.getLastSendReport()) {
|
||||
@ -152,5 +159,4 @@ void loop() {
|
||||
case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
#ifndef APSSID
|
||||
#define APSSID "esp8266"
|
||||
#define APPSK "esp8266"
|
||||
#define APPSK "esp8266"
|
||||
#endif
|
||||
|
||||
const char* ssid = APSSID;
|
||||
const char* ssid = APSSID;
|
||||
const char* password = APPSK;
|
||||
|
||||
WiFiEventHandler stationConnectedHandler;
|
||||
@ -98,7 +98,6 @@ void loop() {
|
||||
|
||||
String macToString(const unsigned char* mac) {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(buf);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
@ -54,13 +54,11 @@ void setup() {
|
||||
|
||||
void loop() {
|
||||
// Check if a client has connected
|
||||
WiFiClient client = server.available();
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
WiFiClient client = server.accept();
|
||||
if (!client) { return; }
|
||||
Serial.println(F("new client"));
|
||||
|
||||
client.setTimeout(5000); // default is 1000
|
||||
client.setTimeout(5000); // default is 1000
|
||||
|
||||
// Read the first line of the request
|
||||
String req = client.readStringUntil('\r');
|
||||
|
@ -40,16 +40,8 @@ void loop() {
|
||||
for (int8_t i = 0; i < scanResult; i++) {
|
||||
WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden);
|
||||
|
||||
Serial.printf(PSTR(" %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %s\n"),
|
||||
i,
|
||||
channel,
|
||||
bssid[0], bssid[1], bssid[2],
|
||||
bssid[3], bssid[4], bssid[5],
|
||||
rssi,
|
||||
(encryptionType == ENC_TYPE_NONE) ? ' ' : '*',
|
||||
hidden ? 'H' : 'V',
|
||||
ssid.c_str());
|
||||
delay(0);
|
||||
Serial.printf(PSTR(" %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %s\n"), i, channel, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], rssi, (encryptionType == ENC_TYPE_NONE) ? ' ' : '*', hidden ? 'H' : 'V', ssid.c_str());
|
||||
yield();
|
||||
}
|
||||
} else {
|
||||
Serial.printf(PSTR("WiFi scan error %d"), scanResult);
|
||||
|
@ -17,7 +17,7 @@
|
||||
#endif
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <include/WiFiState.h> // WiFiState structure details
|
||||
#include <include/WiFiState.h> // WiFiState structure details
|
||||
|
||||
WiFiState state;
|
||||
|
||||
@ -26,7 +26,7 @@ const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(74880);
|
||||
//Serial.setDebugOutput(true); // If you need debug output
|
||||
// Serial.setDebugOutput(true); // If you need debug output
|
||||
Serial.println("Trying to resume WiFi connection...");
|
||||
|
||||
// May be necessary after deepSleep. Otherwise you may get "error: pll_cal exceeds 2ms!!!" when trying to connect
|
||||
@ -36,17 +36,14 @@ void setup() {
|
||||
// Here you can do whatever you need to do that doesn't need a WiFi connection.
|
||||
// ---
|
||||
|
||||
ESP.rtcUserMemoryRead(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast<uint32_t *>(&state), sizeof(state));
|
||||
ESP.rtcUserMemoryRead(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast<uint32_t*>(&state), sizeof(state));
|
||||
unsigned long start = millis();
|
||||
|
||||
if (!WiFi.resumeFromShutdown(state)
|
||||
|| (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) {
|
||||
if (!WiFi.resumeFromShutdown(state) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) {
|
||||
Serial.println("Cannot resume WiFi connection, connecting via begin...");
|
||||
WiFi.persistent(false);
|
||||
|
||||
if (!WiFi.mode(WIFI_STA)
|
||||
|| !WiFi.begin(ssid, password)
|
||||
|| (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) {
|
||||
if (!WiFi.mode(WIFI_STA) || !WiFi.begin(ssid, password) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) {
|
||||
WiFi.mode(WIFI_OFF);
|
||||
Serial.println("Cannot connect!");
|
||||
Serial.flush();
|
||||
@ -64,7 +61,7 @@ void setup() {
|
||||
// ---
|
||||
|
||||
WiFi.shutdown(state);
|
||||
ESP.rtcUserMemoryWrite(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast<uint32_t *>(&state), sizeof(state));
|
||||
ESP.rtcUserMemoryWrite(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast<uint32_t*>(&state), sizeof(state));
|
||||
|
||||
// ---
|
||||
// Here you can do whatever you need to do that doesn't need a WiFi connection anymore.
|
||||
|
@ -20,11 +20,11 @@
|
||||
*/
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#include <algorithm> // std::min
|
||||
#include <algorithm> // std::min
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -64,9 +64,9 @@ SoftwareSerial* logger = nullptr;
|
||||
#define logger (&Serial1)
|
||||
#endif
|
||||
|
||||
#define STACK_PROTECTOR 512 // bytes
|
||||
#define STACK_PROTECTOR 512 // bytes
|
||||
|
||||
//how many clients should be able to telnet to this ESP8266
|
||||
// how many clients should be able to telnet to this ESP8266
|
||||
#define MAX_SRV_CLIENTS 2
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
@ -98,7 +98,7 @@ void setup() {
|
||||
logger->printf("Serial receive buffer size: %d bytes\n", RXBUFFERSIZE);
|
||||
|
||||
#if SERIAL_LOOPBACK
|
||||
USC0(0) |= (1 << UCLBE); // incomplete HardwareSerial API
|
||||
USC0(0) |= (1 << UCLBE); // incomplete HardwareSerial API
|
||||
logger->println("Serial Internal Loopback enabled");
|
||||
#endif
|
||||
|
||||
@ -114,7 +114,7 @@ void setup() {
|
||||
logger->print("connected, address=");
|
||||
logger->println(WiFi.localIP());
|
||||
|
||||
//start server
|
||||
// start server
|
||||
server.begin();
|
||||
server.setNoDelay(true);
|
||||
|
||||
@ -124,22 +124,22 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
//check if there are any new clients
|
||||
// check if there are any new clients
|
||||
if (server.hasClient()) {
|
||||
//find free/disconnected spot
|
||||
// find free/disconnected spot
|
||||
int i;
|
||||
for (i = 0; i < MAX_SRV_CLIENTS; i++)
|
||||
if (!serverClients[i]) { // equivalent to !serverClients[i].connected()
|
||||
serverClients[i] = server.available();
|
||||
if (!serverClients[i]) { // equivalent to !serverClients[i].connected()
|
||||
serverClients[i] = server.accept();
|
||||
logger->print("New client: index ");
|
||||
logger->print(i);
|
||||
break;
|
||||
}
|
||||
|
||||
//no free/disconnected spot so reject
|
||||
// no free/disconnected spot so reject
|
||||
if (i == MAX_SRV_CLIENTS) {
|
||||
server.available().println("busy");
|
||||
// hints: server.available() is a WiFiClient with short-term scope
|
||||
server.accept().println("busy");
|
||||
// hints: server.accept() is a WiFiClient with short-term scope
|
||||
// when out of scope, a WiFiClient will
|
||||
// - flush() - all data will be sent
|
||||
// - stop() - automatically too
|
||||
@ -147,9 +147,9 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
//check TCP clients for data
|
||||
// check TCP clients for data
|
||||
#if 1
|
||||
// Incredibly, this code is faster than the bufferred one below - #4620 is needed
|
||||
// Incredibly, this code is faster than the buffered one below - #4620 is needed
|
||||
// loopback/3000000baud average 348KB/s
|
||||
for (int i = 0; i < MAX_SRV_CLIENTS; i++)
|
||||
while (serverClients[i].available() && Serial.availableForWrite() > 0) {
|
||||
@ -165,9 +165,7 @@ void loop() {
|
||||
uint8_t buf[maxToSerial];
|
||||
size_t tcp_got = serverClients[i].read(buf, maxToSerial);
|
||||
size_t serial_sent = Serial.write(buf, tcp_got);
|
||||
if (serial_sent != maxToSerial) {
|
||||
logger->printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxToSerial, tcp_got, serial_sent);
|
||||
}
|
||||
if (serial_sent != maxToSerial) { logger->printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxToSerial, tcp_got, serial_sent); }
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -189,7 +187,7 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
//check UART for data
|
||||
// check UART for data
|
||||
size_t len = std::min(Serial.available(), maxToTcp);
|
||||
len = std::min(len, (size_t)STACK_PROTECTOR);
|
||||
if (len) {
|
||||
@ -202,9 +200,7 @@ void loop() {
|
||||
// ensure write space is sufficient:
|
||||
if (serverClients[i].availableForWrite() >= serial_got) {
|
||||
size_t tcp_sent = serverClients[i].write(sbuf, serial_got);
|
||||
if (tcp_sent != len) {
|
||||
logger->printf("len mismatch: available:%zd serial-read:%zd tcp-write:%zd\n", len, serial_got, tcp_sent);
|
||||
}
|
||||
if (tcp_sent != len) { logger->printf("len mismatch: available:%zd serial-read:%zd tcp-write:%zd\n", len, serial_got, tcp_sent); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
139
libraries/ESP8266WiFi/src/ArduinoWiFiServer.h
Normal file
139
libraries/ESP8266WiFi/src/ArduinoWiFiServer.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
ArduinoWiFiServer.h - Arduino compatible WiFiServer
|
||||
implementation for ESP8266Wifi library.
|
||||
Copyright (c) 2020 Juraj Andrassy
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef arduinowifiserver_h
|
||||
#define arduinowifiserver_h
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
#ifndef MAX_MONITORED_CLIENTS
|
||||
#define MAX_MONITORED_CLIENTS 5
|
||||
#endif
|
||||
|
||||
template <class TServer, class TClient>
|
||||
class ArduinoCompatibleWiFiServerTemplate : public TServer, public Print {
|
||||
public:
|
||||
|
||||
ArduinoCompatibleWiFiServerTemplate(const IPAddress& addr, uint16_t port) : TServer(addr, port) {}
|
||||
ArduinoCompatibleWiFiServerTemplate(uint16_t port) : TServer(port) {}
|
||||
virtual ~ArduinoCompatibleWiFiServerTemplate() {}
|
||||
|
||||
// https://www.arduino.cc/en/Reference/WiFiServerAvailable
|
||||
TClient available() {
|
||||
|
||||
acceptClients();
|
||||
|
||||
// find next client with data available
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
if (index == MAX_MONITORED_CLIENTS) {
|
||||
index = 0;
|
||||
}
|
||||
TClient& client = connectedClients[index];
|
||||
index++;
|
||||
if (client.available())
|
||||
return client;
|
||||
}
|
||||
return TClient(); // no client with data found
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t b) override {
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size) override {
|
||||
static uint32_t lastCheck;
|
||||
uint32_t m = millis();
|
||||
if (m - lastCheck > 100) {
|
||||
lastCheck = m;
|
||||
acceptClients();
|
||||
}
|
||||
if (size == 0)
|
||||
return 0;
|
||||
size_t ret = 0;
|
||||
size_t a = size;
|
||||
while (true) {
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
WiFiClient& client = connectedClients[i];
|
||||
if (client.status() == ESTABLISHED && client.availableForWrite() < (int) a) {
|
||||
a = client.availableForWrite();
|
||||
}
|
||||
}
|
||||
if (a == 0)
|
||||
break;
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
if (connectedClients[i].status() == ESTABLISHED) {
|
||||
connectedClients[i].write(buf, a);
|
||||
}
|
||||
}
|
||||
ret += a;
|
||||
if (ret == size)
|
||||
break;
|
||||
buf += a;
|
||||
a = size - ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
|
||||
virtual void flush() override {
|
||||
flush(0);
|
||||
}
|
||||
|
||||
virtual void flush(unsigned int maxWaitMs) {
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
if (connectedClients[i].status() == ESTABLISHED) {
|
||||
connectedClients[i].flush(maxWaitMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() {
|
||||
return (TServer::status() == LISTEN);
|
||||
}
|
||||
|
||||
void close() {
|
||||
TServer::stop();
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
if (connectedClients[i]) {
|
||||
connectedClients[i].stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
void stop() {close();}
|
||||
void end() {close();}
|
||||
|
||||
private:
|
||||
TClient connectedClients[MAX_MONITORED_CLIENTS];
|
||||
uint8_t index = 0;
|
||||
|
||||
void acceptClients() {
|
||||
for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) {
|
||||
if (!connectedClients[i]) {
|
||||
connectedClients[i] = TServer::accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef ArduinoCompatibleWiFiServerTemplate<WiFiServer, WiFiClient> ArduinoWiFiServer;
|
||||
typedef ArduinoCompatibleWiFiServerTemplate<WiFiServerSecure, WiFiClientSecure> ArduinoWiFiServerSecure;
|
||||
|
||||
#endif
|
@ -457,6 +457,7 @@ namespace brssl {
|
||||
}
|
||||
memcpy(pk->key.ec.q, ek->q, ek->qlen);
|
||||
pk->key.ec.qlen = ek->qlen;
|
||||
pk->key.ec.curve = ek->curve;
|
||||
return pk;
|
||||
|
||||
default:
|
||||
|
@ -58,6 +58,7 @@ class PublicKey {
|
||||
|
||||
// Disable the copy constructor, we're pointer based
|
||||
PublicKey(const PublicKey& that) = delete;
|
||||
PublicKey& operator=(const PublicKey& that) = delete;
|
||||
|
||||
private:
|
||||
brssl::public_key *_key;
|
||||
@ -86,6 +87,7 @@ class PrivateKey {
|
||||
|
||||
// Disable the copy constructor, we're pointer based
|
||||
PrivateKey(const PrivateKey& that) = delete;
|
||||
PrivateKey& operator=(const PrivateKey& that) = delete;
|
||||
|
||||
private:
|
||||
brssl::private_key *_key;
|
||||
@ -122,6 +124,7 @@ class X509List {
|
||||
|
||||
// Disable the copy constructor, we're pointer based
|
||||
X509List(const X509List& that) = delete;
|
||||
X509List& operator=(const X509List& that) = delete;
|
||||
|
||||
private:
|
||||
size_t _count;
|
||||
@ -161,7 +164,7 @@ class ServerSessions {
|
||||
ServerSessions(ServerSession *sessions, uint32_t size) : ServerSessions(sessions, size, false) {}
|
||||
|
||||
// Dynamically allocates a cache for the given number of sessions and initializes it.
|
||||
// If the allocation of the buffer wasn't successfull, the value
|
||||
// If the allocation of the buffer wasn't successful, the value
|
||||
// returned by size() will be 0.
|
||||
ServerSessions(uint32_t size) : ServerSessions(size > 0 ? new ServerSession[size] : nullptr, size, true) {}
|
||||
|
||||
@ -178,7 +181,7 @@ class ServerSessions {
|
||||
|
||||
// Size of the store in sessions.
|
||||
uint32_t _size;
|
||||
// Store where the informations for the sessions are stored.
|
||||
// Store where the information for the sessions are stored.
|
||||
ServerSession *_store;
|
||||
// Whether the store is dynamically allocated.
|
||||
// If this is true, the store needs to be freed in the destructor.
|
||||
|
@ -75,12 +75,12 @@ void ESP8266WiFiClass::printDiag(Print& p) {
|
||||
char ssid[33]; //ssid can be up to 32chars, => plus null term
|
||||
memcpy(ssid, conf.ssid, sizeof(conf.ssid));
|
||||
ssid[32] = 0; //nullterm in case of 32 char ssid
|
||||
p.printf_P(PSTR("SSID (%d): %s\n"), strlen(ssid), ssid);
|
||||
p.printf_P(PSTR("SSID (%zu): %s\n"), strlen(ssid), ssid);
|
||||
|
||||
char passphrase[65];
|
||||
memcpy(passphrase, conf.password, sizeof(conf.password));
|
||||
passphrase[64] = 0;
|
||||
p.printf_P(PSTR("Passphrase (%d): %s\n"), strlen(passphrase), passphrase);
|
||||
p.printf_P(PSTR("Passphrase (%zu): %s\n"), strlen(passphrase), passphrase);
|
||||
|
||||
p.print(F("BSSID set: "));
|
||||
p.println(conf.bssid_set);
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "ESP8266WiFiGeneric.h"
|
||||
#include "ESP8266WiFiAP.h"
|
||||
|
||||
#include <LwipDhcpServer-NonOS.h>
|
||||
|
||||
extern "C" {
|
||||
#include "c_types.h"
|
||||
#include "ets_sys.h"
|
||||
@ -37,7 +39,6 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "debug.h"
|
||||
#include "LwipDhcpServer.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------
|
||||
// ---------------------------------------------------- Private functions ------------------------------------------------
|
||||
@ -54,10 +55,13 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r
|
||||
* @return equal
|
||||
*/
|
||||
static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) {
|
||||
if(strcmp(reinterpret_cast<const char*>(lhs.ssid), reinterpret_cast<const char*>(rhs.ssid)) != 0) {
|
||||
if(lhs.ssid_len != rhs.ssid_len) {
|
||||
return false;
|
||||
}
|
||||
if(strcmp(reinterpret_cast<const char*>(lhs.password), reinterpret_cast<const char*>(rhs.password)) != 0) {
|
||||
if(memcmp(lhs.ssid, rhs.ssid, lhs.ssid_len) != 0) {
|
||||
return false;
|
||||
}
|
||||
if(strncmp(reinterpret_cast<const char*>(lhs.password), reinterpret_cast<const char*>(rhs.password), sizeof(softap_config::password)) != 0) {
|
||||
return false;
|
||||
}
|
||||
if(lhs.channel != rhs.channel) {
|
||||
@ -85,13 +89,13 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r
|
||||
|
||||
/**
|
||||
* Set up an access point
|
||||
* @param ssid Pointer to the SSID (max 31 char).
|
||||
* @param passphrase For WPA2 min 8 char, for open use NULL (max 63 char).
|
||||
* @param ssid Pointer to the SSID (max 32 char).
|
||||
* @param psk For WPA2 min 8 char max 64 char, for open use "" or NULL.
|
||||
* @param channel WiFi channel number, 1 - 13.
|
||||
* @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID)
|
||||
* @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832
|
||||
*/
|
||||
bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel, int ssid_hidden, int max_connection) {
|
||||
bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection) {
|
||||
|
||||
if(!WiFi.enableAP(true)) {
|
||||
// enable AP failed
|
||||
@ -99,36 +103,43 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ssid || strlen(ssid) == 0 || strlen(ssid) > 31) {
|
||||
// fail SSID too long or missing!
|
||||
DEBUG_WIFI("[AP] SSID too long or missing!\n");
|
||||
size_t ssid_len = ssid ? strlen(ssid) : 0;
|
||||
if(ssid_len == 0 || ssid_len > 32) {
|
||||
DEBUG_WIFI("[AP] SSID length %zu, too long or missing!\n", ssid_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(passphrase && strlen(passphrase) > 0 && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) {
|
||||
// fail passphrase to long or short!
|
||||
DEBUG_WIFI("[AP] fail passphrase too long or short!\n");
|
||||
size_t psk_len = psk ? strlen(psk) : 0;
|
||||
if(psk_len > 0 && (psk_len > 64 || psk_len < 8)) {
|
||||
DEBUG_WIFI("[AP] fail psk length %zu, too long or short!\n", psk_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
|
||||
struct softap_config conf;
|
||||
strcpy(reinterpret_cast<char*>(conf.ssid), ssid);
|
||||
memcpy(reinterpret_cast<char*>(conf.ssid), ssid, ssid_len);
|
||||
if (ssid_len < sizeof(conf.ssid)) {
|
||||
conf.ssid[ssid_len] = 0;
|
||||
}
|
||||
conf.ssid_len = ssid_len;
|
||||
|
||||
if(psk_len) {
|
||||
conf.authmode = AUTH_WPA2_PSK;
|
||||
memcpy(reinterpret_cast<char*>(conf.password), psk, psk_len);
|
||||
if (psk_len < sizeof(conf.password)) {
|
||||
conf.password[psk_len] = 0;
|
||||
}
|
||||
} else {
|
||||
conf.authmode = AUTH_OPEN;
|
||||
conf.password[0] = 0;
|
||||
}
|
||||
|
||||
conf.channel = channel;
|
||||
conf.ssid_len = strlen(ssid);
|
||||
conf.ssid_hidden = ssid_hidden;
|
||||
conf.max_connection = max_connection;
|
||||
conf.beacon_interval = 100;
|
||||
|
||||
if(!passphrase || strlen(passphrase) == 0) {
|
||||
conf.authmode = AUTH_OPEN;
|
||||
*conf.password = 0;
|
||||
} else {
|
||||
conf.authmode = AUTH_WPA2_PSK;
|
||||
strcpy(reinterpret_cast<char*>(conf.password), passphrase);
|
||||
}
|
||||
|
||||
struct softap_config conf_compare;
|
||||
if(WiFi._persistent){
|
||||
wifi_softap_get_config_default(&conf_compare);
|
||||
@ -156,16 +167,18 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch
|
||||
DEBUG_WIFI("[AP] softap config unchanged\n");
|
||||
}
|
||||
|
||||
dhcpSoftAP.end();
|
||||
auto& server = softAPDhcpServer();
|
||||
server.end();
|
||||
|
||||
// check IP config
|
||||
struct ip_info ip;
|
||||
if(wifi_get_ip_info(SOFTAP_IF, &ip)) {
|
||||
if(ip.ip.addr == 0x00000000) {
|
||||
// Invalid config
|
||||
DEBUG_WIFI("[AP] IP config Invalid resetting...\n");
|
||||
//192.168.4.1 , 192.168.4.1 , 255.255.255.0
|
||||
ret = softAPConfig(0x0104A8C0, 0x0104A8C0, 0x00FFFFFF);
|
||||
ret = softAPConfig(
|
||||
IPAddress(192, 168, 4, 1),
|
||||
IPAddress(192, 168, 4, 1),
|
||||
IPAddress(255, 255, 255, 0));
|
||||
if(!ret) {
|
||||
DEBUG_WIFI("[AP] softAPConfig failed!\n");
|
||||
ret = false;
|
||||
@ -176,13 +189,13 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch
|
||||
ret = false;
|
||||
}
|
||||
|
||||
dhcpSoftAP.begin(&ip);
|
||||
server.begin();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& passphrase, int channel, int ssid_hidden, int max_connection) {
|
||||
return softAP(ssid.c_str(), passphrase.c_str(), channel, ssid_hidden, max_connection);
|
||||
bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection) {
|
||||
return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,9 +227,8 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA
|
||||
info.gw.addr = gateway.v4();
|
||||
info.netmask.addr = subnet.v4();
|
||||
|
||||
if(!wifi_softap_dhcps_stop()) {
|
||||
DEBUG_WIFI("[APConfig] wifi_softap_dhcps_stop failed!\n");
|
||||
}
|
||||
auto& server = softAPDhcpServer();
|
||||
server.end();
|
||||
|
||||
if(!wifi_set_ip_info(SOFTAP_IF, &info)) {
|
||||
DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n");
|
||||
@ -234,30 +246,14 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA
|
||||
dhcp_lease.end_ip.addr = ip.v4();
|
||||
DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str());
|
||||
|
||||
if(!dhcpSoftAP.set_dhcps_lease(&dhcp_lease))
|
||||
if(!server.set_dhcps_lease(&dhcp_lease))
|
||||
{
|
||||
DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
// set lease time to 720min --> 12h
|
||||
if(!dhcpSoftAP.set_dhcps_lease_time(720))
|
||||
{
|
||||
DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
uint8 mode = info.gw.addr ? 1 : 0;
|
||||
if(!dhcpSoftAP.set_dhcps_offer_option(OFFER_ROUTER, &mode))
|
||||
{
|
||||
DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if(!wifi_softap_dhcps_start()) {
|
||||
DEBUG_WIFI("[APConfig] wifi_softap_dhcps_start failed!\n");
|
||||
ret = false;
|
||||
}
|
||||
server.setRouter(true); // send ROUTER option with netif's gateway IP
|
||||
server.begin();
|
||||
|
||||
// check config
|
||||
if(wifi_get_ip_info(SOFTAP_IF, &info)) {
|
||||
@ -358,25 +354,32 @@ String ESP8266WiFiAPClass::softAPmacAddress(void) {
|
||||
String ESP8266WiFiAPClass::softAPSSID() const {
|
||||
struct softap_config config;
|
||||
wifi_softap_get_config(&config);
|
||||
char* name = reinterpret_cast<char*>(config.ssid);
|
||||
char ssid[sizeof(config.ssid) + 1];
|
||||
memcpy(ssid, name, sizeof(config.ssid));
|
||||
ssid[sizeof(config.ssid)] = '\0';
|
||||
|
||||
return String(ssid);
|
||||
String ssid;
|
||||
ssid.concat(reinterpret_cast<const char*>(config.ssid), config.ssid_len);
|
||||
|
||||
return ssid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured(Not-In-Flash) softAP PSK or PASSWORD.
|
||||
* Get the configured(Not-In-Flash) softAP PSK.
|
||||
* @return String psk.
|
||||
*/
|
||||
String ESP8266WiFiAPClass::softAPPSK() const {
|
||||
struct softap_config config;
|
||||
wifi_softap_get_config(&config);
|
||||
char* pass = reinterpret_cast<char*>(config.password);
|
||||
char psk[sizeof(config.password) + 1];
|
||||
memcpy(psk, pass, sizeof(config.password));
|
||||
psk[sizeof(config.password)] = '\0';
|
||||
|
||||
return String(psk);
|
||||
char* ptr = reinterpret_cast<char*>(config.password);
|
||||
String psk;
|
||||
psk.concat(ptr, strnlen(ptr, sizeof(config.password)));
|
||||
|
||||
return psk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the static DHCP server instance attached to the softAP interface
|
||||
* @return DhcpServer instance.
|
||||
*/
|
||||
DhcpServer& ESP8266WiFiAPClass::softAPDhcpServer() {
|
||||
return getNonOSDhcpServer();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "ESP8266WiFiType.h"
|
||||
#include "ESP8266WiFiGeneric.h"
|
||||
|
||||
#include <LwipDhcpServer.h>
|
||||
|
||||
class ESP8266WiFiAPClass {
|
||||
|
||||
@ -36,8 +37,8 @@ class ESP8266WiFiAPClass {
|
||||
|
||||
public:
|
||||
|
||||
bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4);
|
||||
bool softAP(const String& ssid,const String& passphrase = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4);
|
||||
bool softAP(const char* ssid, const char* psk = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4);
|
||||
bool softAP(const String& ssid,const String& psk = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4);
|
||||
bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
|
||||
bool softAPdisconnect(bool wifioff = false);
|
||||
|
||||
@ -48,8 +49,10 @@ class ESP8266WiFiAPClass {
|
||||
uint8_t* softAPmacAddress(uint8_t* mac);
|
||||
String softAPmacAddress(void);
|
||||
|
||||
String softAPSSID() const;
|
||||
String softAPPSK() const;
|
||||
String softAPSSID() const;
|
||||
String softAPPSK() const;
|
||||
|
||||
static DhcpServer& softAPDhcpServer();
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -49,10 +49,6 @@ extern "C" {
|
||||
#include "debug.h"
|
||||
#include "include/WiFiState.h"
|
||||
|
||||
extern "C" void esp_schedule();
|
||||
extern "C" void esp_yield();
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------- Generic WiFi function -----------------------------------------------
|
||||
// -----------------------------------------------------------------------------------------------------------------------
|
||||
@ -207,7 +203,10 @@ WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std::
|
||||
WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function<void(const WiFiEventModeChange&)> f)
|
||||
{
|
||||
WiFiEventHandler handler = std::make_shared<WiFiEventHandlerOpaque>(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){
|
||||
WiFiEventModeChange& dst = *reinterpret_cast<WiFiEventModeChange*>(&e->event_info);
|
||||
auto& src = e->event_info.opmode_changed;
|
||||
WiFiEventModeChange dst;
|
||||
dst.oldMode = (WiFiMode_t)src.old_opmode;
|
||||
dst.newMode = (WiFiMode_t)src.new_opmode;
|
||||
f(dst);
|
||||
});
|
||||
sCbEventList.push_back(handler);
|
||||
@ -438,10 +437,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) {
|
||||
//tasks to wait correctly.
|
||||
constexpr unsigned int timeoutValue = 1000; //1 second
|
||||
if(can_yield()) {
|
||||
using oneShot = esp8266::polledTimeout::oneShotFastMs;
|
||||
oneShot timeout(timeoutValue);
|
||||
while(wifi_get_opmode() != (uint8) m && !timeout)
|
||||
delay(5);
|
||||
// The final argument, intvl_ms, to esp_delay influences how frequently
|
||||
// the scheduled recurrent functions (Schedule.h) are probed.
|
||||
esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; }, 5);
|
||||
|
||||
//if at this point mode still hasn't been reached, give up
|
||||
if(wifi_get_opmode() != (uint8) m) {
|
||||
@ -518,9 +516,9 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) {
|
||||
}
|
||||
|
||||
wifi_fpm_set_sleep_type(MODEM_SLEEP_T);
|
||||
delay(0);
|
||||
esp_yield();
|
||||
wifi_fpm_open();
|
||||
delay(0);
|
||||
esp_yield();
|
||||
auto ret = wifi_fpm_do_sleep(sleepUs);
|
||||
if (ret != 0)
|
||||
{
|
||||
@ -621,22 +619,24 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
|
||||
aResult = IPAddress(&addr);
|
||||
} else if(err == ERR_INPROGRESS) {
|
||||
_dns_lookup_pending = true;
|
||||
delay(timeout_ms);
|
||||
// will resume on timeout or when wifi_dns_found_callback fires
|
||||
// Will resume on timeout or when wifi_dns_found_callback fires.
|
||||
// The final argument, intvl_ms, to esp_delay influences how frequently
|
||||
// the scheduled recurrent functions (Schedule.h) are probed; here, to allow
|
||||
// the ethernet driver perform work.
|
||||
esp_delay(timeout_ms, []() { return _dns_lookup_pending; }, 1);
|
||||
_dns_lookup_pending = false;
|
||||
// will return here when dns_found_callback fires
|
||||
if(aResult.isSet()) {
|
||||
err = ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if(err != 0) {
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
|
||||
} else {
|
||||
if(err == ERR_OK) {
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (err == ERR_OK) ? 1 : 0;
|
||||
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %s (%d)!\n", aHostname, lwip_strerr(err), (int)err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
@ -671,8 +671,8 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
|
||||
aResult = IPAddress(&addr);
|
||||
} else if(err == ERR_INPROGRESS) {
|
||||
_dns_lookup_pending = true;
|
||||
delay(timeout_ms);
|
||||
// will resume on timeout or when wifi_dns_found_callback fires
|
||||
esp_delay(timeout_ms, []() { return _dns_lookup_pending; });
|
||||
_dns_lookup_pending = false;
|
||||
// will return here when dns_found_callback fires
|
||||
if(aResult.isSet()) {
|
||||
@ -680,13 +680,13 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
|
||||
}
|
||||
}
|
||||
|
||||
if(err != 0) {
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
|
||||
} else {
|
||||
if(err == ERR_OK) {
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (err == ERR_OK) ? 1 : 0;
|
||||
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -705,7 +705,8 @@ void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *ca
|
||||
if(ipaddr) {
|
||||
(*reinterpret_cast<IPAddress*>(callback_arg)) = IPAddress(ipaddr);
|
||||
}
|
||||
esp_schedule(); // break delay in hostByName
|
||||
_dns_lookup_pending = false; // resume hostByName
|
||||
esp_schedule();
|
||||
}
|
||||
|
||||
uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState& state)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user