1
0
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:
david gauchard
2022-06-11 22:23:53 +02:00
committed by GitHub
535 changed files with 27764 additions and 21136 deletions

View File

@ -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) {

View File

@ -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();

View File

@ -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";
}

View File

@ -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() {

View File

@ -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();
}

View File

@ -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");

View File

@ -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;
}

View File

@ -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

View File

@ -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

View 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&nbsp;</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&nbsp;</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&nbsp;</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&nbsp;</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 &#x1f512; (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(
<!--
-->
&nbsp;&nbsp;<a class='lg' onclick='pv();'>&#x1f441;</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'/> &nbsp;&nbsp;<a class='lg' onclick='pv();'>&#x1f441;</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

View File

@ -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

View 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("&#x1f512;"));
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

View 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

View File

@ -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)

View File

@ -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

View File

@ -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))
{
}

View File

@ -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() {}

View File

@ -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);
}

View File

@ -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(); }
}

View File

@ -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];

View File

@ -33,7 +33,6 @@ void setup() {
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {

View File

@ -33,7 +33,6 @@ void setup() {
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {

View File

@ -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:

View File

@ -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");

View File

@ -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");

View File

@ -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);
}

View File

@ -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) {

View File

@ -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

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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_ */

View File

@ -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();
}

View File

@ -10,7 +10,7 @@
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#define STAPSK "your-password"
#endif
const char* host = "esp8266-webupdate";

View File

@ -10,7 +10,7 @@
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#define STAPSK "your-password"
#endif
const char* host = "esp8266-webupdate";

View File

@ -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();
});
}

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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;

View File

@ -4,7 +4,7 @@
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#define STAPSK "your-password"
#endif
const char* ssid = STASSID;

View File

@ -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); }
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -5,7 +5,7 @@
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#define STAPSK "your-password"
#endif
const char* ssid = STASSID;

View File

@ -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 {

View File

@ -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!");

View File

@ -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);

View File

@ -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]);

View File

@ -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");

View 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"
> ```

View 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.

View 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>
)==";

View 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>

View 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>

View 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; }

View 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;

View File

@ -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);

View File

@ -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"));

View File

@ -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();

View File

@ -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

View File

@ -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;
}

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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
}

View 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";

View File

@ -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);
}

View File

@ -0,0 +1,2 @@
cd ${0%/*} 2>/dev/null
python3 ../../../../tools/cert.py -s www.gitlab.com -n gitlab > certs.h

View 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
////////////////////////////////////////////////////////////

View 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);
}

View File

@ -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() {}

View File

@ -0,0 +1,2 @@
cd ${0%/*} 2>/dev/null
python3 ../../../../tools/cert.py -s api.github.com -n github > certs.h

View 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
////////////////////////////////////////////////////////////

View File

@ -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); }
}

View File

@ -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();
}

View 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
}
}

View File

@ -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() {}

View File

@ -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: ");

View File

@ -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
*/

View File

@ -36,7 +36,7 @@
#ifndef APSSID
#define APSSID "ESPap"
#define APPSK "thereisnospoon"
#define APPSK "thereisnospoon"
#endif
/* Set these to your desired credentials. */

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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');

View File

@ -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);

View File

@ -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.

View File

@ -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); }
}
}
}

View 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

View File

@ -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:

View File

@ -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.

View File

@ -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);

View File

@ -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();
}

View File

@ -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:

View File

@ -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