parent
fa758a3744
commit
64eee38cd2
392 changed files with 62113 additions and 0 deletions
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"configurations": [ |
||||||
|
{ |
||||||
|
"name": "Linux", |
||||||
|
"includePath": [ |
||||||
|
"${workspaceFolder}/**" |
||||||
|
], |
||||||
|
"defines": [], |
||||||
|
"compilerPath": "/usr/bin/gcc", |
||||||
|
"cStandard": "c11", |
||||||
|
"cppStandard": "c++17", |
||||||
|
"intelliSenseMode": "clang-x64" |
||||||
|
} |
||||||
|
], |
||||||
|
"version": 4 |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"C_Cpp.errorSquiggles": "Disabled" |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiServerSecureBearSSL.h> |
||||||
|
#include <CertStoreBearSSL.h> |
||||||
|
#include <ESP8266WiFiScan.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <BearSSLHelpers.h> |
||||||
|
#include <WiFiClientSecureAxTLS.h> |
||||||
|
#include <WiFiServerSecure.h> |
||||||
|
#include <WiFiClientSecureBearSSL.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include <ESP8266WiFiGeneric.h> |
||||||
|
#include <WiFiServerSecureAxTLS.h> |
||||||
|
#include <ESP8266WiFiGratuitous.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
#include <ESP8266WiFiAP.h> |
||||||
|
#include <WiFiClientSecure.h> |
||||||
|
#include <ESP8266WiFiType.h> |
||||||
|
#include <ESP8266WiFiSTA.h> |
||||||
|
#include <WiFiServer.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
#include "DHT.h" |
||||||
|
#define DHTTYPE DHT11 |
||||||
|
|
||||||
|
#define dht_dpin 13 |
||||||
|
DHT dht(dht_dpin, DHTTYPE); |
||||||
|
|
||||||
|
const char* ssid = "Robby"; |
||||||
|
const char* password = "0l30l4ng"; |
||||||
|
|
||||||
|
uint8_t BLUE_LED_PIN = 12; |
||||||
|
uint8_t WHITE_LED_PIN = 15; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
pinMode(BLUE_LED_PIN, OUTPUT); |
||||||
|
pinMode(WHITE_LED_PIN, OUTPUT); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(1000); |
||||||
|
Serial.print("Connecting...");
|
||||||
|
} |
||||||
|
dht.begin(); |
||||||
|
delay(500); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
if ( WiFi.status() == WL_CONNECTED ) { |
||||||
|
digitalWrite(WHITE_LED_PIN, HIGH); |
||||||
|
} else { |
||||||
|
digitalWrite(WHITE_LED_PIN, LOW);
|
||||||
|
} |
||||||
|
Serial.print("Current humidity = "); |
||||||
|
Serial.print(dht.readHumidity()); |
||||||
|
Serial.print("% "); |
||||||
|
Serial.print("temperature = "); |
||||||
|
Serial.print(dht.readTemperature());
|
||||||
|
Serial.println("C "); |
||||||
|
|
||||||
|
if ( dht.readTemperature() < 31 ) { |
||||||
|
digitalWrite(BLUE_LED_PIN, LOW); |
||||||
|
} else { |
||||||
|
digitalWrite(BLUE_LED_PIN, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
delay(500); |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
# ORION IOT SCRIPT |
||||||
|
This is the repository for storing all the codes for all Orion's IoT devices |
||||||
|
## Credit |
||||||
|
**Robby Muhammad Nst** |
@ -0,0 +1,374 @@ |
|||||||
|
#ifndef LWIP_OPEN_SRC |
||||||
|
#define LWIP_OPEN_SRC |
||||||
|
#endif |
||||||
|
#include <functional> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include "ArduinoOTA.h" |
||||||
|
#include "MD5Builder.h" |
||||||
|
#include "StreamString.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "osapi.h" |
||||||
|
#include "ets_sys.h" |
||||||
|
#include "user_interface.h" |
||||||
|
} |
||||||
|
|
||||||
|
#include "lwip/opt.h" |
||||||
|
#include "lwip/udp.h" |
||||||
|
#include "lwip/inet.h" |
||||||
|
#include "lwip/igmp.h" |
||||||
|
#include "lwip/mem.h" |
||||||
|
#include "include/UdpContext.h" |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_OTA |
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define OTA_DEBUG DEBUG_ESP_PORT |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
ArduinoOTAClass::ArduinoOTAClass() |
||||||
|
: _port(0) |
||||||
|
, _udp_ota(0) |
||||||
|
, _initialized(false) |
||||||
|
, _rebootOnSuccess(true) |
||||||
|
, _useMDNS(true) |
||||||
|
, _state(OTA_IDLE) |
||||||
|
, _size(0) |
||||||
|
, _cmd(0) |
||||||
|
, _ota_port(0) |
||||||
|
, _start_callback(NULL) |
||||||
|
, _end_callback(NULL) |
||||||
|
, _error_callback(NULL) |
||||||
|
, _progress_callback(NULL) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
ArduinoOTAClass::~ArduinoOTAClass(){ |
||||||
|
if(_udp_ota){ |
||||||
|
_udp_ota->unref(); |
||||||
|
_udp_ota = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::onStart(THandlerFunction fn) { |
||||||
|
_start_callback = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::onEnd(THandlerFunction fn) { |
||||||
|
_end_callback = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) { |
||||||
|
_progress_callback = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::onError(THandlerFunction_Error fn) { |
||||||
|
_error_callback = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::setPort(uint16_t port) { |
||||||
|
if (!_initialized && !_port && port) { |
||||||
|
_port = port; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::setHostname(const char * hostname) { |
||||||
|
if (!_initialized && !_hostname.length() && hostname) { |
||||||
|
_hostname = hostname; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String ArduinoOTAClass::getHostname() { |
||||||
|
return _hostname; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::setPassword(const char * password) { |
||||||
|
if (!_initialized && !_password.length() && password) { |
||||||
|
MD5Builder passmd5; |
||||||
|
passmd5.begin(); |
||||||
|
passmd5.add(password); |
||||||
|
passmd5.calculate(); |
||||||
|
_password = passmd5.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::setPasswordHash(const char * password) { |
||||||
|
if (!_initialized && !_password.length() && password) { |
||||||
|
_password = password; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::setRebootOnSuccess(bool reboot){ |
||||||
|
_rebootOnSuccess = reboot; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::begin(bool useMDNS) { |
||||||
|
if (_initialized) |
||||||
|
return; |
||||||
|
|
||||||
|
_useMDNS = useMDNS; |
||||||
|
|
||||||
|
if (!_hostname.length()) { |
||||||
|
char tmp[15]; |
||||||
|
sprintf(tmp, "esp8266-%06x", ESP.getChipId()); |
||||||
|
_hostname = tmp; |
||||||
|
} |
||||||
|
if (!_port) { |
||||||
|
_port = 8266; |
||||||
|
} |
||||||
|
|
||||||
|
if(_udp_ota){ |
||||||
|
_udp_ota->unref(); |
||||||
|
_udp_ota = 0; |
||||||
|
} |
||||||
|
|
||||||
|
_udp_ota = new UdpContext; |
||||||
|
_udp_ota->ref(); |
||||||
|
|
||||||
|
if(!_udp_ota->listen(IP_ADDR_ANY, _port)) |
||||||
|
return; |
||||||
|
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); |
||||||
|
|
||||||
|
if(_useMDNS) { |
||||||
|
MDNS.begin(_hostname.c_str()); |
||||||
|
|
||||||
|
if (_password.length()) { |
||||||
|
MDNS.enableArduino(_port, true); |
||||||
|
} else { |
||||||
|
MDNS.enableArduino(_port); |
||||||
|
} |
||||||
|
} |
||||||
|
_initialized = true; |
||||||
|
_state = OTA_IDLE; |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
int ArduinoOTAClass::parseInt(){ |
||||||
|
char data[16]; |
||||||
|
uint8_t index; |
||||||
|
char value; |
||||||
|
while(_udp_ota->peek() == ' ') _udp_ota->read(); |
||||||
|
for(index = 0; index < sizeof(data); ++index){ |
||||||
|
value = _udp_ota->peek(); |
||||||
|
if(value < '0' || value > '9'){ |
||||||
|
data[index] = '\0'; |
||||||
|
return atoi(data); |
||||||
|
} |
||||||
|
data[index] = _udp_ota->read(); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
String ArduinoOTAClass::readStringUntil(char end){ |
||||||
|
String res = ""; |
||||||
|
int value; |
||||||
|
while(true){ |
||||||
|
value = _udp_ota->read(); |
||||||
|
if(value < 0 || value == '\0' || value == end){ |
||||||
|
return res; |
||||||
|
} |
||||||
|
res += static_cast<char>(value); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::_onRx(){ |
||||||
|
if(!_udp_ota->next()) return; |
||||||
|
IPAddress ota_ip; |
||||||
|
|
||||||
|
if (_state == OTA_IDLE) { |
||||||
|
int cmd = parseInt(); |
||||||
|
if (cmd != U_FLASH && cmd != U_SPIFFS) |
||||||
|
return; |
||||||
|
_ota_ip = _udp_ota->getRemoteAddress(); |
||||||
|
_cmd = cmd; |
||||||
|
_ota_port = parseInt(); |
||||||
|
_ota_udp_port = _udp_ota->getRemotePort(); |
||||||
|
_size = parseInt(); |
||||||
|
_udp_ota->read(); |
||||||
|
_md5 = readStringUntil('\n'); |
||||||
|
_md5.trim(); |
||||||
|
if(_md5.length() != 32) |
||||||
|
return; |
||||||
|
|
||||||
|
ota_ip = _ota_ip; |
||||||
|
|
||||||
|
if (_password.length()){ |
||||||
|
MD5Builder nonce_md5; |
||||||
|
nonce_md5.begin(); |
||||||
|
nonce_md5.add(String(micros())); |
||||||
|
nonce_md5.calculate(); |
||||||
|
_nonce = nonce_md5.toString(); |
||||||
|
|
||||||
|
char auth_req[38]; |
||||||
|
sprintf(auth_req, "AUTH %s", _nonce.c_str()); |
||||||
|
_udp_ota->append((const char *)auth_req, strlen(auth_req)); |
||||||
|
_udp_ota->send(ota_ip, _ota_udp_port); |
||||||
|
_state = OTA_WAITAUTH; |
||||||
|
return; |
||||||
|
} else { |
||||||
|
_state = OTA_RUNUPDATE; |
||||||
|
} |
||||||
|
} else if (_state == OTA_WAITAUTH) { |
||||||
|
int cmd = parseInt(); |
||||||
|
if (cmd != U_AUTH) { |
||||||
|
_state = OTA_IDLE; |
||||||
|
return; |
||||||
|
} |
||||||
|
_udp_ota->read(); |
||||||
|
String cnonce = readStringUntil(' '); |
||||||
|
String response = readStringUntil('\n'); |
||||||
|
if (cnonce.length() != 32 || response.length() != 32) { |
||||||
|
_state = OTA_IDLE; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String challenge = _password + ":" + String(_nonce) + ":" + cnonce; |
||||||
|
MD5Builder _challengemd5; |
||||||
|
_challengemd5.begin(); |
||||||
|
_challengemd5.add(challenge); |
||||||
|
_challengemd5.calculate(); |
||||||
|
String result = _challengemd5.toString(); |
||||||
|
|
||||||
|
ota_ip = _ota_ip; |
||||||
|
if(result.equalsConstantTime(response)) { |
||||||
|
_state = OTA_RUNUPDATE; |
||||||
|
} else { |
||||||
|
_udp_ota->append("Authentication Failed", 21); |
||||||
|
_udp_ota->send(ota_ip, _ota_udp_port); |
||||||
|
if (_error_callback) _error_callback(OTA_AUTH_ERROR); |
||||||
|
_state = OTA_IDLE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
while(_udp_ota->next()) _udp_ota->flush(); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoOTAClass::_runUpdate() { |
||||||
|
IPAddress ota_ip = _ota_ip; |
||||||
|
|
||||||
|
if (!Update.begin(_size, _cmd)) { |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.println("Update Begin Error"); |
||||||
|
#endif |
||||||
|
if (_error_callback) { |
||||||
|
_error_callback(OTA_BEGIN_ERROR); |
||||||
|
} |
||||||
|
|
||||||
|
StreamString ss; |
||||||
|
Update.printError(ss); |
||||||
|
_udp_ota->append("ERR: ", 5); |
||||||
|
_udp_ota->append(ss.c_str(), ss.length()); |
||||||
|
_udp_ota->send(ota_ip, _ota_udp_port); |
||||||
|
delay(100); |
||||||
|
_udp_ota->listen(IP_ADDR_ANY, _port); |
||||||
|
_state = OTA_IDLE; |
||||||
|
return; |
||||||
|
} |
||||||
|
_udp_ota->append("OK", 2); |
||||||
|
_udp_ota->send(ota_ip, _ota_udp_port); |
||||||
|
delay(100); |
||||||
|
|
||||||
|
Update.setMD5(_md5.c_str()); |
||||||
|
WiFiUDP::stopAll(); |
||||||
|
WiFiClient::stopAll(); |
||||||
|
|
||||||
|
if (_start_callback) { |
||||||
|
_start_callback(); |
||||||
|
} |
||||||
|
if (_progress_callback) { |
||||||
|
_progress_callback(0, _size); |
||||||
|
} |
||||||
|
|
||||||
|
WiFiClient client; |
||||||
|
if (!client.connect(_ota_ip, _ota_port)) { |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.printf("Connect Failed\n"); |
||||||
|
#endif |
||||||
|
_udp_ota->listen(IP_ADDR_ANY, _port); |
||||||
|
if (_error_callback) { |
||||||
|
_error_callback(OTA_CONNECT_ERROR); |
||||||
|
} |
||||||
|
_state = OTA_IDLE; |
||||||
|
} |
||||||
|
// OTA sends little packets
|
||||||
|
client.setNoDelay(true); |
||||||
|
|
||||||
|
uint32_t written, total = 0; |
||||||
|
while (!Update.isFinished() && client.connected()) { |
||||||
|
int waited = 1000; |
||||||
|
while (!client.available() && waited--) |
||||||
|
delay(1); |
||||||
|
if (!waited){ |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.printf("Receive Failed\n"); |
||||||
|
#endif |
||||||
|
_udp_ota->listen(IP_ADDR_ANY, _port); |
||||||
|
if (_error_callback) { |
||||||
|
_error_callback(OTA_RECEIVE_ERROR); |
||||||
|
} |
||||||
|
_state = OTA_IDLE; |
||||||
|
} |
||||||
|
written = Update.write(client); |
||||||
|
if (written > 0) { |
||||||
|
client.print(written, DEC); |
||||||
|
total += written; |
||||||
|
if(_progress_callback) { |
||||||
|
_progress_callback(total, _size); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (Update.end()) { |
||||||
|
client.print("OK"); |
||||||
|
client.stop(); |
||||||
|
delay(10); |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.printf("Update Success\n"); |
||||||
|
#endif |
||||||
|
if (_end_callback) { |
||||||
|
_end_callback(); |
||||||
|
} |
||||||
|
if(_rebootOnSuccess){ |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
OTA_DEBUG.printf("Rebooting...\n"); |
||||||
|
#endif |
||||||
|
//let serial/network finish tasks that might be given in _end_callback
|
||||||
|
delay(100); |
||||||
|
ESP.restart(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
_udp_ota->listen(IP_ADDR_ANY, _port); |
||||||
|
if (_error_callback) { |
||||||
|
_error_callback(OTA_END_ERROR); |
||||||
|
} |
||||||
|
Update.printError(client); |
||||||
|
#ifdef OTA_DEBUG |
||||||
|
Update.printError(OTA_DEBUG); |
||||||
|
#endif |
||||||
|
_state = OTA_IDLE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//this needs to be called in the loop()
|
||||||
|
void ArduinoOTAClass::handle() { |
||||||
|
if (_state == OTA_RUNUPDATE) { |
||||||
|
_runUpdate(); |
||||||
|
_state = OTA_IDLE; |
||||||
|
} |
||||||
|
|
||||||
|
if(_useMDNS) |
||||||
|
MDNS.update(); //handle MDNS update as well, given that ArduinoOTA relies on it anyways
|
||||||
|
} |
||||||
|
|
||||||
|
int ArduinoOTAClass::getCommand() { |
||||||
|
return _cmd; |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) |
||||||
|
ArduinoOTAClass ArduinoOTA; |
||||||
|
#endif |
@ -0,0 +1,103 @@ |
|||||||
|
#ifndef __ARDUINO_OTA_H |
||||||
|
#define __ARDUINO_OTA_H |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
class UdpContext; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
OTA_IDLE, |
||||||
|
OTA_WAITAUTH, |
||||||
|
OTA_RUNUPDATE |
||||||
|
} ota_state_t; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
OTA_AUTH_ERROR, |
||||||
|
OTA_BEGIN_ERROR, |
||||||
|
OTA_CONNECT_ERROR, |
||||||
|
OTA_RECEIVE_ERROR, |
||||||
|
OTA_END_ERROR |
||||||
|
} ota_error_t; |
||||||
|
|
||||||
|
class ArduinoOTAClass |
||||||
|
{ |
||||||
|
public: |
||||||
|
typedef std::function<void(void)> THandlerFunction; |
||||||
|
typedef std::function<void(ota_error_t)> THandlerFunction_Error; |
||||||
|
typedef std::function<void(unsigned int, unsigned int)> THandlerFunction_Progress; |
||||||
|
|
||||||
|
ArduinoOTAClass(); |
||||||
|
~ArduinoOTAClass(); |
||||||
|
|
||||||
|
//Sets the service port. Default 8266
|
||||||
|
void setPort(uint16_t port); |
||||||
|
|
||||||
|
//Sets the device hostname. Default esp8266-xxxxxx
|
||||||
|
void setHostname(const char *hostname); |
||||||
|
String getHostname(); |
||||||
|
|
||||||
|
//Sets the password that will be required for OTA. Default NULL
|
||||||
|
void setPassword(const char *password); |
||||||
|
|
||||||
|
//Sets the password as above but in the form MD5(password). Default NULL
|
||||||
|
void setPasswordHash(const char *password); |
||||||
|
|
||||||
|
//Sets if the device should be rebooted after successful update. Default true
|
||||||
|
void setRebootOnSuccess(bool reboot); |
||||||
|
|
||||||
|
//This callback will be called when OTA connection has begun
|
||||||
|
void onStart(THandlerFunction fn); |
||||||
|
|
||||||
|
//This callback will be called when OTA has finished
|
||||||
|
void onEnd(THandlerFunction fn); |
||||||
|
|
||||||
|
//This callback will be called when OTA encountered Error
|
||||||
|
void onError(THandlerFunction_Error fn); |
||||||
|
|
||||||
|
//This callback will be called when OTA is receiving data
|
||||||
|
void onProgress(THandlerFunction_Progress fn); |
||||||
|
|
||||||
|
//Starts the ArduinoOTA service
|
||||||
|
void begin(bool useMDNS = true); |
||||||
|
|
||||||
|
//Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
|
||||||
|
void handle(); |
||||||
|
|
||||||
|
//Gets update command type after OTA has started. Either U_FLASH or U_SPIFFS
|
||||||
|
int getCommand(); |
||||||
|
|
||||||
|
private: |
||||||
|
int _port; |
||||||
|
String _password; |
||||||
|
String _hostname; |
||||||
|
String _nonce; |
||||||
|
UdpContext *_udp_ota; |
||||||
|
bool _initialized; |
||||||
|
bool _rebootOnSuccess; |
||||||
|
bool _useMDNS; |
||||||
|
ota_state_t _state; |
||||||
|
int _size; |
||||||
|
int _cmd; |
||||||
|
uint16_t _ota_port; |
||||||
|
uint16_t _ota_udp_port; |
||||||
|
IPAddress _ota_ip; |
||||||
|
String _md5; |
||||||
|
|
||||||
|
THandlerFunction _start_callback; |
||||||
|
THandlerFunction _end_callback; |
||||||
|
THandlerFunction_Error _error_callback; |
||||||
|
THandlerFunction_Progress _progress_callback; |
||||||
|
|
||||||
|
void _runUpdate(void); |
||||||
|
void _onRx(void); |
||||||
|
int parseInt(void); |
||||||
|
String readStringUntil(char end); |
||||||
|
}; |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) |
||||||
|
extern ArduinoOTAClass ArduinoOTA; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __ARDUINO_OTA_H */ |
@ -0,0 +1,77 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include <ArduinoOTA.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println("Booting"); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
Serial.println("Connection Failed! Rebooting..."); |
||||||
|
delay(5000); |
||||||
|
ESP.restart(); |
||||||
|
} |
||||||
|
|
||||||
|
// Port defaults to 8266
|
||||||
|
// ArduinoOTA.setPort(8266);
|
||||||
|
|
||||||
|
// Hostname defaults to esp8266-[ChipID]
|
||||||
|
// ArduinoOTA.setHostname("myesp8266");
|
||||||
|
|
||||||
|
// No authentication by default
|
||||||
|
// ArduinoOTA.setPassword("admin");
|
||||||
|
|
||||||
|
// Password can be set with it's md5 value as well
|
||||||
|
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||||
|
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||||
|
|
||||||
|
ArduinoOTA.onStart([]() { |
||||||
|
String type; |
||||||
|
if (ArduinoOTA.getCommand() == U_FLASH) { |
||||||
|
type = "sketch"; |
||||||
|
} else { // U_SPIFFS
|
||||||
|
type = "filesystem"; |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||||
|
Serial.println("Start updating " + type); |
||||||
|
}); |
||||||
|
ArduinoOTA.onEnd([]() { |
||||||
|
Serial.println("\nEnd"); |
||||||
|
}); |
||||||
|
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { |
||||||
|
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); |
||||||
|
}); |
||||||
|
ArduinoOTA.onError([](ota_error_t error) { |
||||||
|
Serial.printf("Error[%u]: ", error); |
||||||
|
if (error == OTA_AUTH_ERROR) { |
||||||
|
Serial.println("Auth Failed"); |
||||||
|
} else if (error == OTA_BEGIN_ERROR) { |
||||||
|
Serial.println("Begin Failed"); |
||||||
|
} else if (error == OTA_CONNECT_ERROR) { |
||||||
|
Serial.println("Connect Failed"); |
||||||
|
} else if (error == OTA_RECEIVE_ERROR) { |
||||||
|
Serial.println("Receive Failed"); |
||||||
|
} else if (error == OTA_END_ERROR) { |
||||||
|
Serial.println("End Failed"); |
||||||
|
} |
||||||
|
}); |
||||||
|
ArduinoOTA.begin(); |
||||||
|
Serial.println("Ready"); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
ArduinoOTA.handle(); |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include <ArduinoOTA.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
const char* host = "OTA-LEDS"; |
||||||
|
|
||||||
|
int led_pin = 13; |
||||||
|
#define N_DIMMERS 3 |
||||||
|
int dimmer_pin[] = {14, 5, 15}; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
|
||||||
|
/* switch on led */ |
||||||
|
pinMode(led_pin, OUTPUT); |
||||||
|
digitalWrite(led_pin, LOW); |
||||||
|
|
||||||
|
Serial.println("Booting"); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
|
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println("Retrying connection..."); |
||||||
|
} |
||||||
|
/* switch off led */ |
||||||
|
digitalWrite(led_pin, HIGH); |
||||||
|
|
||||||
|
/* configure dimmers, and OTA server events */ |
||||||
|
analogWriteRange(1000); |
||||||
|
analogWrite(led_pin, 990); |
||||||
|
|
||||||
|
for (int i = 0; i < N_DIMMERS; i++) { |
||||||
|
pinMode(dimmer_pin[i], OUTPUT); |
||||||
|
analogWrite(dimmer_pin[i], 50); |
||||||
|
} |
||||||
|
|
||||||
|
ArduinoOTA.setHostname(host); |
||||||
|
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
|
||||||
|
for (int i = 0; i < 30; i++) { |
||||||
|
analogWrite(led_pin, (i * 100) % 1001); |
||||||
|
delay(50); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
ArduinoOTA.onError([](ota_error_t error) { |
||||||
|
(void)error; |
||||||
|
ESP.restart(); |
||||||
|
}); |
||||||
|
|
||||||
|
/* setup the OTA server */ |
||||||
|
ArduinoOTA.begin(); |
||||||
|
Serial.println("Ready"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
ArduinoOTA.handle(); |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Ultrasound |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ArduinoOTA KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
setup KEYWORD2 |
||||||
|
handle KEYWORD2 |
||||||
|
onStart KEYWORD2 |
||||||
|
onEnd KEYWORD2 |
||||||
|
onError KEYWORD2 |
||||||
|
onProgress KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
@ -0,0 +1,10 @@ |
|||||||
|
name=ArduinoOTA |
||||||
|
version=1.0 |
||||||
|
author=Ivan Grokhotkov and Miguel Angel Ajo |
||||||
|
maintainer=Ivan Grokhtkov <ivan@esp8266.com> |
||||||
|
sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. |
||||||
|
paragraph=With this library you can enable your sketch to be upgraded over network. Includes mdns anounces to get discovered by the arduino IDE. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,34 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <DNSServer.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
|
||||||
|
const byte DNS_PORT = 53; |
||||||
|
IPAddress apIP(192, 168, 1, 1); |
||||||
|
DNSServer dnsServer; |
||||||
|
ESP8266WebServer webServer(80); |
||||||
|
|
||||||
|
String responseHTML = "" |
||||||
|
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>" |
||||||
|
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will " |
||||||
|
"be redirected here.</p></body></html>"; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
WiFi.mode(WIFI_AP); |
||||||
|
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); |
||||||
|
WiFi.softAP("DNSServer CaptivePortal example"); |
||||||
|
|
||||||
|
// if DNSServer is started with "*" for domain name, it will reply with
|
||||||
|
// provided IP to all DNS request
|
||||||
|
dnsServer.start(DNS_PORT, "*", apIP); |
||||||
|
|
||||||
|
// replay to all requests with same HTML
|
||||||
|
webServer.onNotFound([]() { |
||||||
|
webServer.send(200, "text/html", responseHTML); |
||||||
|
}); |
||||||
|
webServer.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
dnsServer.processNextRequest(); |
||||||
|
webServer.handleClient(); |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <DNSServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <EEPROM.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
This example serves a "hello world" on a WLAN and a SoftAP at the same time. |
||||||
|
The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM. |
||||||
|
|
||||||
|
Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there.
|
||||||
|
Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN. |
||||||
|
|
||||||
|
Now the ESP8266 is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://esp8266.local too.
|
||||||
|
|
||||||
|
This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/
|
||||||
|
*/ |
||||||
|
|
||||||
|
/* Set these to your desired softAP credentials. They are not configurable at runtime */ |
||||||
|
#ifndef APSSID |
||||||
|
#define APSSID "ESP_ap" |
||||||
|
#define APPSK "12345678" |
||||||
|
#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 = "esp8266"; |
||||||
|
|
||||||
|
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */ |
||||||
|
char ssid[32] = ""; |
||||||
|
char password[32] = ""; |
||||||
|
|
||||||
|
// DNS server
|
||||||
|
const byte DNS_PORT = 53; |
||||||
|
DNSServer dnsServer; |
||||||
|
|
||||||
|
// Web server
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
/* Soft AP network parameters */ |
||||||
|
IPAddress apIP(192, 168, 4, 1); |
||||||
|
IPAddress netMsk(255, 255, 255, 0); |
||||||
|
|
||||||
|
|
||||||
|
/** Should I connect to WLAN asap? */ |
||||||
|
boolean connect; |
||||||
|
|
||||||
|
/** Last time I tried to connect to WLAN */ |
||||||
|
unsigned long lastConnectTry = 0; |
||||||
|
|
||||||
|
/** Current WLAN status */ |
||||||
|
unsigned int status = WL_IDLE_STATUS; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
delay(1000); |
||||||
|
Serial.begin(9600); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Configuring access point..."); |
||||||
|
/* 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
|
||||||
|
Serial.print("AP IP address: "); |
||||||
|
Serial.println(WiFi.softAPIP()); |
||||||
|
|
||||||
|
/* Setup the DNS server redirecting all the domains to the apIP */ |
||||||
|
dnsServer.setErrorReplyCode(DNSReplyCode::NoError); |
||||||
|
dnsServer.start(DNS_PORT, "*", apIP); |
||||||
|
|
||||||
|
/* 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
|
||||||
|
Serial.println("HTTP server started"); |
||||||
|
loadCredentials(); // Load WLAN credentials from network
|
||||||
|
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
|
||||||
|
} |
||||||
|
|
||||||
|
void connectWifi() { |
||||||
|
Serial.println("Connecting as wifi client..."); |
||||||
|
WiFi.disconnect(); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
int connRes = WiFi.waitForConnectResult(); |
||||||
|
Serial.print("connRes: "); |
||||||
|
Serial.println(connRes); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
if (connect) { |
||||||
|
Serial.println("Connect requested"); |
||||||
|
connect = false; |
||||||
|
connectWifi(); |
||||||
|
lastConnectTry = millis(); |
||||||
|
} |
||||||
|
{ |
||||||
|
unsigned int s = WiFi.status(); |
||||||
|
if (s == 0 && millis() > (lastConnectTry + 60000)) { |
||||||
|
/* If WLAN disconnected and idle try to connect */ |
||||||
|
/* Don't set retry time too low as retry interfere the softAP operation */ |
||||||
|
connect = true; |
||||||
|
} |
||||||
|
if (status != s) { // WLAN status change
|
||||||
|
Serial.print("Status: "); |
||||||
|
Serial.println(s); |
||||||
|
status = s; |
||||||
|
if (s == WL_CONNECTED) { |
||||||
|
/* Just connected to WLAN */ |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
// Setup MDNS responder
|
||||||
|
if (!MDNS.begin(myHostname)) { |
||||||
|
Serial.println("Error setting up MDNS responder!"); |
||||||
|
} else { |
||||||
|
Serial.println("mDNS responder started"); |
||||||
|
// Add service to MDNS-SD
|
||||||
|
MDNS.addService("http", "tcp", 80); |
||||||
|
} |
||||||
|
} else if (s == WL_NO_SSID_AVAIL) { |
||||||
|
WiFi.disconnect(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (s == WL_CONNECTED) { |
||||||
|
MDNS.update(); |
||||||
|
} |
||||||
|
} |
||||||
|
// Do work:
|
||||||
|
//DNS
|
||||||
|
dnsServer.processNextRequest(); |
||||||
|
//HTTP
|
||||||
|
server.handleClient(); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,27 @@ |
|||||||
|
/** 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; |
||||||
|
} |
||||||
|
Serial.println("Recovered credentials:"); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.println(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(); |
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
/** Handle root or redirect to captive portal */ |
||||||
|
void handleRoot() { |
||||||
|
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
||||||
|
return; |
||||||
|
} |
||||||
|
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |
||||||
|
server.sendHeader("Pragma", "no-cache"); |
||||||
|
server.sendHeader("Expires", "-1"); |
||||||
|
|
||||||
|
String Page; |
||||||
|
Page += F( |
||||||
|
"<html><head></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, "text/html", Page); |
||||||
|
} |
||||||
|
|
||||||
|
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ |
||||||
|
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
|
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** Wifi config page handler */ |
||||||
|
void handleWifi() { |
||||||
|
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |
||||||
|
server.sendHeader("Pragma", "no-cache"); |
||||||
|
server.sendHeader("Expires", "-1"); |
||||||
|
|
||||||
|
String Page; |
||||||
|
Page += F( |
||||||
|
"<html><head></head><body>" |
||||||
|
"<h1>Wifi config</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 += |
||||||
|
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>"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Page += F("<tr><td>No WLAN found</td></tr>"); |
||||||
|
} |
||||||
|
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'/>" |
||||||
|
"<br /><input type='submit' value='Connect/Disconnect'/></form>" |
||||||
|
"<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
|
||||||
|
} |
||||||
|
|
||||||
|
/** Handle the WLAN save form and redirect to WLAN config page again */ |
||||||
|
void handleWifiSave() { |
||||||
|
Serial.println("wifi save"); |
||||||
|
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1); |
||||||
|
server.arg("p").toCharArray(password, sizeof(password) - 1); |
||||||
|
server.sendHeader("Location", "wifi", true); |
||||||
|
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
|
||||||
|
saveCredentials(); |
||||||
|
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.
|
||||||
|
return; |
||||||
|
} |
||||||
|
String message = F("File Not Found\n\n"); |
||||||
|
message += F("URI: "); |
||||||
|
message += server.uri(); |
||||||
|
message += F("\nMethod: "); |
||||||
|
message += (server.method() == HTTP_GET) ? "GET" : "POST"; |
||||||
|
message += F("\nArguments: "); |
||||||
|
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"); |
||||||
|
} |
||||||
|
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |
||||||
|
server.sendHeader("Pragma", "no-cache"); |
||||||
|
server.sendHeader("Expires", "-1"); |
||||||
|
server.send(404, "text/plain", message); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,21 @@ |
|||||||
|
/** Is this an IP? */ |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** IP to String? */ |
||||||
|
String toStringIp(IPAddress ip) { |
||||||
|
String res = ""; |
||||||
|
for (int i = 0; i < 3; i++) { |
||||||
|
res += String((ip >> (8 * i)) & 0xFF) + "."; |
||||||
|
} |
||||||
|
res += String(((ip >> 8 * 3)) & 0xFF); |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,41 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <DNSServer.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
|
||||||
|
const byte DNS_PORT = 53; |
||||||
|
IPAddress apIP(192, 168, 1, 1); |
||||||
|
DNSServer dnsServer; |
||||||
|
ESP8266WebServer webServer(80); |
||||||
|
|
||||||
|
void setup() { |
||||||
|
WiFi.mode(WIFI_AP); |
||||||
|
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); |
||||||
|
WiFi.softAP("DNSServer example"); |
||||||
|
|
||||||
|
// modify TTL associated with the domain name (in seconds)
|
||||||
|
// default is 60 seconds
|
||||||
|
dnsServer.setTTL(300); |
||||||
|
// set which return code will be used for all other domains (e.g. sending
|
||||||
|
// ServerFailure instead of NonExistentDomain will reduce number of queries
|
||||||
|
// sent by clients)
|
||||||
|
// default is DNSReplyCode::NonExistentDomain
|
||||||
|
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); |
||||||
|
|
||||||
|
// start DNS server for a specific domain name
|
||||||
|
dnsServer.start(DNS_PORT, "www.example.com", apIP); |
||||||
|
|
||||||
|
// simple HTTP server to see that DNS server is working
|
||||||
|
webServer.onNotFound([]() { |
||||||
|
String message = "Hello World!\n\n"; |
||||||
|
message += "URI: "; |
||||||
|
message += webServer.uri(); |
||||||
|
|
||||||
|
webServer.send(200, "text/plain", message); |
||||||
|
}); |
||||||
|
webServer.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
dnsServer.processNextRequest(); |
||||||
|
webServer.handleClient(); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For DNSServer |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Library (KEYWORD3) |
||||||
|
####################################### |
||||||
|
|
||||||
|
DNSServer KEYWORD3 RESERVED_WORD |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
DNSReplyCode KEYWORD1 DATA_TYPE |
||||||
|
DNSHeader KEYWORD1 DATA_TYPE |
||||||
|
DNSServer KEYWORD1 DATA_TYPE |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
processNextRequest KEYWORD2 |
||||||
|
setErrorReplyCode KEYWORD2 |
||||||
|
setTTL KEYWORD2 |
||||||
|
start KEYWORD2 |
||||||
|
stop KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
DNS_QR_QUERY LITERAL1 RESERVED_WORD_2 |
||||||
|
DNS_QR_RESPONSE LITERAL1 RESERVED_WORD_2 |
||||||
|
DNS_OPCODE_QUERY LITERAL1 RESERVED_WORD_2 |
||||||
|
MAX_DNSNAME_LENGTH LITERAL1 RESERVED_WORD_2 |
||||||
|
NoError LITERAL1 RESERVED_WORD_2 |
||||||
|
FormError LITERAL1 RESERVED_WORD_2 |
||||||
|
ServerFailure LITERAL1 RESERVED_WORD_2 |
||||||
|
NonExistentDomain LITERAL1 RESERVED_WORD_2 |
||||||
|
NotImplemented LITERAL1 RESERVED_WORD_2 |
||||||
|
Refused LITERAL1 RESERVED_WORD_2 |
||||||
|
YXDomain LITERAL1 RESERVED_WORD_2 |
||||||
|
YXRRSet LITERAL1 RESERVED_WORD_2 |
||||||
|
NXRRSet LITERAL1 RESERVED_WORD_2 |
@ -0,0 +1,10 @@ |
|||||||
|
name=DNSServer |
||||||
|
version=1.1.1 |
||||||
|
author=Kristijan Novoselić |
||||||
|
maintainer=Kristijan Novoselić, <kristijan.novoselic@gmail.com> |
||||||
|
sentence=A simple DNS server for ESP8266. |
||||||
|
paragraph=This library implements a simple DNS server. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,256 @@ |
|||||||
|
#include "DNSServer.h" |
||||||
|
#include <lwip/def.h> |
||||||
|
#include <Arduino.h> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT |
||||||
|
#else |
||||||
|
#define DEBUG_OUTPUT Serial |
||||||
|
#endif |
||||||
|
|
||||||
|
#define DNS_HEADER_SIZE sizeof(DNSHeader) |
||||||
|
|
||||||
|
DNSServer::DNSServer() |
||||||
|
{ |
||||||
|
_ttl = lwip_htonl(60); |
||||||
|
_errorReplyCode = DNSReplyCode::NonExistentDomain; |
||||||
|
} |
||||||
|
|
||||||
|
bool DNSServer::start(const uint16_t &port, const String &domainName, |
||||||
|
const IPAddress &resolvedIP) |
||||||
|
{ |
||||||
|
_port = port; |
||||||
|
|
||||||
|
_domainName = domainName; |
||||||
|
_resolvedIP[0] = resolvedIP[0]; |
||||||
|
_resolvedIP[1] = resolvedIP[1]; |
||||||
|
_resolvedIP[2] = resolvedIP[2]; |
||||||
|
_resolvedIP[3] = resolvedIP[3]; |
||||||
|
downcaseAndRemoveWwwPrefix(_domainName); |
||||||
|
return _udp.begin(_port) == 1; |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) |
||||||
|
{ |
||||||
|
_errorReplyCode = replyCode; |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::setTTL(const uint32_t &ttl) |
||||||
|
{ |
||||||
|
_ttl = lwip_htonl(ttl); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::stop() |
||||||
|
{ |
||||||
|
_udp.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) |
||||||
|
{ |
||||||
|
domainName.toLowerCase(); |
||||||
|
if (domainName.startsWith("www.")) |
||||||
|
domainName.remove(0, 4); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::respondToRequest(uint8_t *buffer, size_t length) |
||||||
|
{ |
||||||
|
DNSHeader *dnsHeader; |
||||||
|
uint8_t *query, *start; |
||||||
|
const char *matchString; |
||||||
|
size_t remaining, labelLength, queryLength; |
||||||
|
uint16_t qtype, qclass; |
||||||
|
|
||||||
|
dnsHeader = (DNSHeader *)buffer; |
||||||
|
|
||||||
|
// Must be a query for us to do anything with it
|
||||||
|
if (dnsHeader->QR != DNS_QR_QUERY) |
||||||
|
return; |
||||||
|
|
||||||
|
// If operation is anything other than query, we don't do it
|
||||||
|
if (dnsHeader->OPCode != DNS_OPCODE_QUERY) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::NotImplemented); |
||||||
|
|
||||||
|
// Only support requests containing single queries - everything else
|
||||||
|
// is badly defined
|
||||||
|
if (dnsHeader->QDCount != lwip_htons(1)) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::FormError); |
||||||
|
|
||||||
|
// 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); |
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
query = start = buffer + DNS_HEADER_SIZE; |
||||||
|
remaining = length - DNS_HEADER_SIZE; |
||||||
|
while (remaining != 0 && *start != 0) { |
||||||
|
labelLength = *start; |
||||||
|
if (labelLength + 1 > remaining) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::FormError); |
||||||
|
remaining -= (labelLength + 1); |
||||||
|
start += (labelLength + 1); |
||||||
|
} |
||||||
|
|
||||||
|
// 1 octet labelLength, 2 octet qtype, 2 octet qclass
|
||||||
|
if (remaining < 5) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::FormError); |
||||||
|
|
||||||
|
start += 1; // Skip the 0 length label that we found above
|
||||||
|
|
||||||
|
memcpy(&qtype, start, sizeof(qtype)); |
||||||
|
start += 2; |
||||||
|
memcpy(&qclass, start, sizeof(qclass)); |
||||||
|
start += 2; |
||||||
|
|
||||||
|
queryLength = start - query; |
||||||
|
|
||||||
|
if (qclass != lwip_htons(DNS_QCLASS_ANY) |
||||||
|
&& qclass != lwip_htons(DNS_QCLASS_IN)) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, |
||||||
|
query, queryLength); |
||||||
|
|
||||||
|
if (qtype != lwip_htons(DNS_QTYPE_A) |
||||||
|
&& qtype != lwip_htons(DNS_QTYPE_ANY)) |
||||||
|
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, |
||||||
|
query, queryLength); |
||||||
|
|
||||||
|
// If we have no domain name configured, just return an error
|
||||||
|
if (_domainName == "") |
||||||
|
return replyWithError(dnsHeader, _errorReplyCode, |
||||||
|
query, queryLength); |
||||||
|
|
||||||
|
// If we're running with a wildcard we can just return a result now
|
||||||
|
if (_domainName == "*") |
||||||
|
return replyWithIP(dnsHeader, query, queryLength); |
||||||
|
|
||||||
|
matchString = _domainName.c_str(); |
||||||
|
|
||||||
|
start = query; |
||||||
|
|
||||||
|
// If there's a leading 'www', skip it
|
||||||
|
if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0) |
||||||
|
start += 4; |
||||||
|
|
||||||
|
while (*start != 0) { |
||||||
|
labelLength = *start; |
||||||
|
start += 1; |
||||||
|
while (labelLength > 0) { |
||||||
|
if (tolower(*start) != *matchString) |
||||||
|
return replyWithError(dnsHeader, _errorReplyCode, |
||||||
|
query, queryLength); |
||||||
|
++start; |
||||||
|
++matchString; |
||||||
|
--labelLength; |
||||||
|
} |
||||||
|
if (*start == 0 && *matchString == '\0') |
||||||
|
return replyWithIP(dnsHeader, query, queryLength); |
||||||
|
|
||||||
|
if (*matchString != '.') |
||||||
|
return replyWithError(dnsHeader, _errorReplyCode, |
||||||
|
query, queryLength); |
||||||
|
++matchString; |
||||||
|
} |
||||||
|
|
||||||
|
return replyWithError(dnsHeader, _errorReplyCode, |
||||||
|
query, queryLength); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::processNextRequest() |
||||||
|
{ |
||||||
|
size_t currentPacketSize; |
||||||
|
|
||||||
|
currentPacketSize = _udp.parsePacket(); |
||||||
|
if (currentPacketSize == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
// The DNS RFC requires that DNS packets be less than 512 bytes in size,
|
||||||
|
// so just discard them if they are larger
|
||||||
|
if (currentPacketSize > MAX_DNS_PACKETSIZE) |
||||||
|
return; |
||||||
|
|
||||||
|
// If the packet size is smaller than the DNS header, then someone is
|
||||||
|
// messing with us
|
||||||
|
if (currentPacketSize < DNS_HEADER_SIZE) |
||||||
|
return; |
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[currentPacketSize]); |
||||||
|
|
||||||
|
if (buffer == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
_udp.read(buffer.get(), currentPacketSize); |
||||||
|
respondToRequest(buffer.get(), currentPacketSize); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::writeNBOShort(uint16_t value) |
||||||
|
{ |
||||||
|
_udp.write((unsigned char *)&value, 2); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::replyWithIP(DNSHeader *dnsHeader, |
||||||
|
unsigned char * query, |
||||||
|
size_t queryLength) |
||||||
|
{ |
||||||
|
uint16_t value; |
||||||
|
|
||||||
|
dnsHeader->QR = DNS_QR_RESPONSE; |
||||||
|
dnsHeader->QDCount = lwip_htons(1); |
||||||
|
dnsHeader->ANCount = lwip_htons(1); |
||||||
|
dnsHeader->NSCount = 0; |
||||||
|
dnsHeader->ARCount = 0; |
||||||
|
|
||||||
|
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); |
||||||
|
_udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader)); |
||||||
|
_udp.write(query, queryLength); |
||||||
|
|
||||||
|
// Rather than restate the name here, we use a pointer to the name contained
|
||||||
|
// in the query section. Pointers have the top two bits set.
|
||||||
|
value = 0xC000 | DNS_HEADER_SIZE; |
||||||
|
writeNBOShort(lwip_htons(value)); |
||||||
|
|
||||||
|
// Answer is type A (an IPv4 address)
|
||||||
|
writeNBOShort(lwip_htons(DNS_QTYPE_A)); |
||||||
|
|
||||||
|
// Answer is in the Internet Class
|
||||||
|
writeNBOShort(lwip_htons(DNS_QCLASS_IN)); |
||||||
|
|
||||||
|
// Output TTL (already NBO)
|
||||||
|
_udp.write((unsigned char*)&_ttl, 4); |
||||||
|
|
||||||
|
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
|
||||||
|
writeNBOShort(lwip_htons(sizeof(_resolvedIP))); |
||||||
|
_udp.write(_resolvedIP, sizeof(_resolvedIP)); |
||||||
|
_udp.endPacket(); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::replyWithError(DNSHeader *dnsHeader, |
||||||
|
DNSReplyCode rcode, |
||||||
|
unsigned char *query, |
||||||
|
size_t queryLength) |
||||||
|
{ |
||||||
|
dnsHeader->QR = DNS_QR_RESPONSE; |
||||||
|
dnsHeader->RCode = (unsigned char) rcode; |
||||||
|
if (query) |
||||||
|
dnsHeader->QDCount = lwip_htons(1); |
||||||
|
else |
||||||
|
dnsHeader->QDCount = 0; |
||||||
|
dnsHeader->ANCount = 0; |
||||||
|
dnsHeader->NSCount = 0; |
||||||
|
dnsHeader->ARCount = 0; |
||||||
|
|
||||||
|
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); |
||||||
|
_udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader)); |
||||||
|
if (query != NULL) |
||||||
|
_udp.write(query, queryLength); |
||||||
|
_udp.endPacket(); |
||||||
|
} |
||||||
|
|
||||||
|
void DNSServer::replyWithError(DNSHeader *dnsHeader, |
||||||
|
DNSReplyCode rcode) |
||||||
|
{ |
||||||
|
replyWithError(dnsHeader, rcode, NULL, 0); |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
#ifndef DNSServer_h |
||||||
|
#define DNSServer_h |
||||||
|
#include <WiFiUdp.h> |
||||||
|
|
||||||
|
#define DNS_QR_QUERY 0 |
||||||
|
#define DNS_QR_RESPONSE 1 |
||||||
|
#define DNS_OPCODE_QUERY 0 |
||||||
|
|
||||||
|
#define DNS_QCLASS_IN 1 |
||||||
|
#define DNS_QCLASS_ANY 255 |
||||||
|
|
||||||
|
#define DNS_QTYPE_A 1 |
||||||
|
#define DNS_QTYPE_ANY 255 |
||||||
|
|
||||||
|
#define MAX_DNSNAME_LENGTH 253 |
||||||
|
#define MAX_DNS_PACKETSIZE 512 |
||||||
|
|
||||||
|
enum class DNSReplyCode |
||||||
|
{ |
||||||
|
NoError = 0, |
||||||
|
FormError = 1, |
||||||
|
ServerFailure = 2, |
||||||
|
NonExistentDomain = 3, |
||||||
|
NotImplemented = 4, |
||||||
|
Refused = 5, |
||||||
|
YXDomain = 6, |
||||||
|
YXRRSet = 7, |
||||||
|
NXRRSet = 8 |
||||||
|
}; |
||||||
|
|
||||||
|
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 OPCode : 4; // message_type
|
||||||
|
unsigned char QR : 1; // query/response flag
|
||||||
|
unsigned char RCode : 4; // response code
|
||||||
|
unsigned char Z : 3; // its z! reserved
|
||||||
|
unsigned char RA : 1; // recursion available
|
||||||
|
uint16_t QDCount; // number of question entries
|
||||||
|
uint16_t ANCount; // number of answer entries
|
||||||
|
uint16_t NSCount; // number of authority entries
|
||||||
|
uint16_t ARCount; // number of resource entries
|
||||||
|
}; |
||||||
|
|
||||||
|
class DNSServer |
||||||
|
{ |
||||||
|
public: |
||||||
|
DNSServer(); |
||||||
|
~DNSServer() { |
||||||
|
stop(); |
||||||
|
}; |
||||||
|
void processNextRequest(); |
||||||
|
void setErrorReplyCode(const DNSReplyCode &replyCode); |
||||||
|
void setTTL(const uint32_t &ttl); |
||||||
|
|
||||||
|
// Returns true if successful, false if there are no sockets available
|
||||||
|
bool start(const uint16_t &port, |
||||||
|
const String &domainName, |
||||||
|
const IPAddress &resolvedIP); |
||||||
|
// stops the DNS server
|
||||||
|
void stop(); |
||||||
|
|
||||||
|
private: |
||||||
|
WiFiUDP _udp; |
||||||
|
uint16_t _port; |
||||||
|
String _domainName; |
||||||
|
unsigned char _resolvedIP[4]; |
||||||
|
uint32_t _ttl; |
||||||
|
DNSReplyCode _errorReplyCode; |
||||||
|
|
||||||
|
void downcaseAndRemoveWwwPrefix(String &domainName); |
||||||
|
void replyWithIP(DNSHeader *dnsHeader, |
||||||
|
unsigned char * query, |
||||||
|
size_t queryLength); |
||||||
|
void replyWithError(DNSHeader *dnsHeader, |
||||||
|
DNSReplyCode rcode, |
||||||
|
unsigned char *query, |
||||||
|
size_t queryLength); |
||||||
|
void replyWithError(DNSHeader *dnsHeader, |
||||||
|
DNSReplyCode rcode); |
||||||
|
void respondToRequest(uint8_t *buffer, size_t length); |
||||||
|
void writeNBOShort(uint16_t value); |
||||||
|
}; |
||||||
|
#endif |
@ -0,0 +1,146 @@ |
|||||||
|
/*
|
||||||
|
EEPROM.cpp - esp8266 EEPROM emulation |
||||||
|
|
||||||
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. |
||||||
|
This file is part of the esp8266 core for Arduino environment. |
||||||
|
|
||||||
|
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 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "EEPROM.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "c_types.h" |
||||||
|
#include "ets_sys.h" |
||||||
|
#include "os_type.h" |
||||||
|
#include "osapi.h" |
||||||
|
#include "spi_flash.h" |
||||||
|
} |
||||||
|
|
||||||
|
extern "C" uint32_t _SPIFFS_end; |
||||||
|
|
||||||
|
EEPROMClass::EEPROMClass(uint32_t sector) |
||||||
|
: _sector(sector) |
||||||
|
, _data(0) |
||||||
|
, _size(0) |
||||||
|
, _dirty(false) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
EEPROMClass::EEPROMClass(void) |
||||||
|
: _sector((((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)) |
||||||
|
, _data(0) |
||||||
|
, _size(0) |
||||||
|
, _dirty(false) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void EEPROMClass::begin(size_t size) { |
||||||
|
if (size <= 0) |
||||||
|
return; |
||||||
|
if (size > SPI_FLASH_SEC_SIZE) |
||||||
|
size = SPI_FLASH_SEC_SIZE; |
||||||
|
|
||||||
|
size = (size + 3) & (~3); |
||||||
|
|
||||||
|
//In case begin() is called a 2nd+ time, don't reallocate if size is the same
|
||||||
|
if(_data && size != _size) { |
||||||
|
delete[] _data; |
||||||
|
_data = new uint8_t[size]; |
||||||
|
} else if(!_data) { |
||||||
|
_data = new uint8_t[size]; |
||||||
|
} |
||||||
|
|
||||||
|
_size = size; |
||||||
|
|
||||||
|
noInterrupts(); |
||||||
|
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size); |
||||||
|
interrupts(); |
||||||
|
|
||||||
|
_dirty = false; //make sure dirty is cleared in case begin() is called 2nd+ time
|
||||||
|
} |
||||||
|
|
||||||
|
void EEPROMClass::end() { |
||||||
|
if (!_size) |
||||||
|
return; |
||||||
|
|
||||||
|
commit(); |
||||||
|
if(_data) { |
||||||
|
delete[] _data; |
||||||
|
} |
||||||
|
_data = 0; |
||||||
|
_size = 0; |
||||||
|
_dirty = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
uint8_t EEPROMClass::read(int const address) { |
||||||
|
if (address < 0 || (size_t)address >= _size) |
||||||
|
return 0; |
||||||
|
if(!_data) |
||||||
|
return 0; |
||||||
|
|
||||||
|
return _data[address]; |
||||||
|
} |
||||||
|
|
||||||
|
void EEPROMClass::write(int const address, uint8_t const value) { |
||||||
|
if (address < 0 || (size_t)address >= _size) |
||||||
|
return; |
||||||
|
if(!_data) |
||||||
|
return; |
||||||
|
|
||||||
|
// Optimise _dirty. Only flagged if data written is different.
|
||||||
|
uint8_t* pData = &_data[address]; |
||||||
|
if (*pData != value) |
||||||
|
{ |
||||||
|
*pData = value; |
||||||
|
_dirty = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool EEPROMClass::commit() { |
||||||
|
bool ret = false; |
||||||
|
if (!_size) |
||||||
|
return false; |
||||||
|
if(!_dirty) |
||||||
|
return true; |
||||||
|
if(!_data) |
||||||
|
return false; |
||||||
|
|
||||||
|
noInterrupts(); |
||||||
|
if(spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK) { |
||||||
|
if(spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size) == SPI_FLASH_RESULT_OK) { |
||||||
|
_dirty = false; |
||||||
|
ret = true; |
||||||
|
} |
||||||
|
} |
||||||
|
interrupts(); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t * EEPROMClass::getDataPtr() { |
||||||
|
_dirty = true; |
||||||
|
return &_data[0]; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t const * EEPROMClass::getConstDataPtr() const { |
||||||
|
return &_data[0]; |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) |
||||||
|
EEPROMClass EEPROM; |
||||||
|
#endif |
@ -0,0 +1,81 @@ |
|||||||
|
/*
|
||||||
|
EEPROM.cpp - esp8266 EEPROM emulation |
||||||
|
|
||||||
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. |
||||||
|
This file is part of the esp8266 core for Arduino environment. |
||||||
|
|
||||||
|
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 EEPROM_h |
||||||
|
#define EEPROM_h |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
class EEPROMClass { |
||||||
|
public: |
||||||
|
EEPROMClass(uint32_t sector); |
||||||
|
EEPROMClass(void); |
||||||
|
|
||||||
|
void begin(size_t size); |
||||||
|
uint8_t read(int const address); |
||||||
|
void write(int const address, uint8_t const val); |
||||||
|
bool commit(); |
||||||
|
void end(); |
||||||
|
|
||||||
|
uint8_t * getDataPtr(); |
||||||
|
uint8_t const * getConstDataPtr() const; |
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T &get(int const address, T &t) { |
||||||
|
if (address < 0 || address + sizeof(T) > _size) |
||||||
|
return t; |
||||||
|
|
||||||
|
memcpy((uint8_t*) &t, _data + address, sizeof(T)); |
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T &put(int const address, const T &t) { |
||||||
|
if (address < 0 || address + sizeof(T) > _size) |
||||||
|
return t; |
||||||
|
if (memcmp(_data + address, (const uint8_t*)&t, sizeof(T)) != 0) { |
||||||
|
_dirty = true; |
||||||
|
memcpy(_data + address, (const uint8_t*)&t, sizeof(T)); |
||||||
|
} |
||||||
|
|
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
size_t length() {return _size;} |
||||||
|
|
||||||
|
uint8_t& operator[](int const address) {return getDataPtr()[address];} |
||||||
|
uint8_t const & operator[](int const address) const {return getConstDataPtr()[address];} |
||||||
|
|
||||||
|
protected: |
||||||
|
uint32_t _sector; |
||||||
|
uint8_t* _data; |
||||||
|
size_t _size; |
||||||
|
bool _dirty; |
||||||
|
}; |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) |
||||||
|
extern EEPROMClass EEPROM; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
/*
|
||||||
|
EEPROM Clear |
||||||
|
|
||||||
|
Sets all of the bytes of the EEPROM to 0. |
||||||
|
This example code is in the public domain. |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <EEPROM.h> |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
// turn the LED on when we're done
|
||||||
|
pinMode(13, OUTPUT); |
||||||
|
digitalWrite(13, HIGH); |
||||||
|
EEPROM.end(); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/*
|
||||||
|
EEPROM Read |
||||||
|
|
||||||
|
Reads the value of each byte of the EEPROM and prints it |
||||||
|
to the computer. |
||||||
|
This example code is in the public domain. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <EEPROM.h> |
||||||
|
|
||||||
|
// start reading from the first byte (address 0) of the EEPROM
|
||||||
|
int address = 0; |
||||||
|
byte value; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
// initialize serial and wait for port to open:
|
||||||
|
Serial.begin(9600); |
||||||
|
while (!Serial) { |
||||||
|
; // wait for serial port to connect. Needed for Leonardo only
|
||||||
|
} |
||||||
|
EEPROM.begin(512); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// read a byte from the current address of the EEPROM
|
||||||
|
value = EEPROM.read(address); |
||||||
|
|
||||||
|
Serial.print(address); |
||||||
|
Serial.print("\t"); |
||||||
|
Serial.print(value, DEC); |
||||||
|
Serial.println(); |
||||||
|
|
||||||
|
// advance to the next address of the EEPROM
|
||||||
|
address = address + 1; |
||||||
|
|
||||||
|
// 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; |
||||||
|
} |
||||||
|
|
||||||
|
delay(500); |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/*
|
||||||
|
EEPROM Write |
||||||
|
|
||||||
|
Stores values read from analog input 0 into the EEPROM. |
||||||
|
These values will stay in the EEPROM when the board is |
||||||
|
turned off and may be retrieved later by another sketch. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <EEPROM.h> |
||||||
|
|
||||||
|
// the current address in the EEPROM (i.e. which byte
|
||||||
|
// we're going to write to next)
|
||||||
|
int addr = 0; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
EEPROM.begin(512); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// need to divide by 4 because analog inputs range from
|
||||||
|
// 0 to 1023 and each byte of the EEPROM can only hold a
|
||||||
|
// value from 0 to 255.
|
||||||
|
int val = analogRead(A0) / 4; |
||||||
|
|
||||||
|
// write the value to the appropriate byte of the EEPROM.
|
||||||
|
// these values will remain there when the board is
|
||||||
|
// turned off.
|
||||||
|
EEPROM.write(addr, val); |
||||||
|
|
||||||
|
// advance to the next address. there are 512 bytes in
|
||||||
|
// the EEPROM, so go back to 0 when we hit 512.
|
||||||
|
// save all changes to the flash.
|
||||||
|
addr = addr + 1; |
||||||
|
if (addr == 512) { |
||||||
|
addr = 0; |
||||||
|
EEPROM.commit(); |
||||||
|
} |
||||||
|
|
||||||
|
delay(100); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Ultrasound |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
EEPROM KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
@ -0,0 +1,10 @@ |
|||||||
|
name=EEPROM |
||||||
|
version=1.0 |
||||||
|
author=Ivan Grokhotkov |
||||||
|
maintainer=Ivan Grokhotkov <ivan@esp8266.com> |
||||||
|
sentence=Enables reading and writing data to the permanent FLASH storage, up to 4kb. |
||||||
|
paragraph= |
||||||
|
category=Data Storage |
||||||
|
url=http://arduino.cc/en/Reference/EEPROM |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,70 @@ |
|||||||
|
AVR In-System Programming over WiFi for ESP8266 |
||||||
|
=============================================== |
||||||
|
|
||||||
|
This library allows an ESP8266 module with the HSPI port available to |
||||||
|
become an AVR In-System Programmer. |
||||||
|
|
||||||
|
Hardware |
||||||
|
-------- |
||||||
|
|
||||||
|
The ESP8266 module connects to the AVR target chip via the standard |
||||||
|
6-pin AVR "Recommended In-System Programming Interface Connector Layout" |
||||||
|
as seen in `AVR910 <http://www.atmel.com/images/doc0943.pdf>`__ among |
||||||
|
other places. |
||||||
|
|
||||||
|
If the AVR target is powered by a different Vcc than what powers your |
||||||
|
ESP8266 chip, you **must provide voltage level shifting** or some other |
||||||
|
form of buffers. Exposing the pins of ESP8266 to anything larger than |
||||||
|
3.6V will damage it. |
||||||
|
|
||||||
|
Connections are as follows: |
||||||
|
|
||||||
|
+-----------+-------------+ |
||||||
|
| ESP8266 | AVR / SPI | |
||||||
|
+===========+=============+ |
||||||
|
| GPIO12 | MISO | |
||||||
|
+-----------+-------------+ |
||||||
|
| GPIO13 | MOSI | |
||||||
|
+-----------+-------------+ |
||||||
|
| GPIO14 | SCK | |
||||||
|
+-----------+-------------+ |
||||||
|
| any\* | RESET | |
||||||
|
+-----------+-------------+ |
||||||
|
|
||||||
|
For RESET use a GPIO other than 0, 2 and 15 (bootselect pins), and apply |
||||||
|
an external pullup/down so that the target is normally running. |
||||||
|
|
||||||
|
Usage |
||||||
|
----- |
||||||
|
|
||||||
|
See the included example. In short: |
||||||
|
|
||||||
|
.. code:: arduino |
||||||
|
|
||||||
|
|
||||||
|
// Create the programmer object |
||||||
|
ESP8266AVRISP avrprog(PORT, RESET_PIN) |
||||||
|
// ... with custom SPI frequency |
||||||
|
ESP8266AVRISP avrprog(PORT, RESET_PIN, 4e6) |
||||||
|
|
||||||
|
// Check current connection state, but don't perform any actions |
||||||
|
AVRISPState_t state = avrprog.update(); |
||||||
|
|
||||||
|
// Serve the pending connection, execute STK500 commands |
||||||
|
AVRISPState_t state = avrprog.serve(); |
||||||
|
|
||||||
|
License and Authors |
||||||
|
~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
This library started off from the source of ArduinoISP "sketch" included |
||||||
|
with the Arduino IDE: |
||||||
|
|
||||||
|
:: |
||||||
|
|
||||||
|
ArduinoISP version 04m3 |
||||||
|
Copyright (c) 2008-2011 Randall Bohn |
||||||
|
If you require a license, see |
||||||
|
http://www.opensource.org/licenses/bsd-license.php |
||||||
|
|
||||||
|
Support for TCP on ESP8266 |
||||||
|
Copyright (c) Kiril Zyapkov <kiril@robotev.com>. |
@ -0,0 +1,77 @@ |
|||||||
|
#include <SPI.h> |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ESP8266AVRISP.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-avrisp"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* pass = STAPSK; |
||||||
|
const uint16_t port = 328; |
||||||
|
const uint8_t reset_pin = 5; |
||||||
|
|
||||||
|
ESP8266AVRISP avrprog(port, reset_pin); |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(""); |
||||||
|
Serial.println("Arduino AVR-ISP over TCP"); |
||||||
|
avrprog.setReset(false); // let the AVR run
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, pass); |
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED); |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
MDNS.addService("avrisp", "tcp", port); |
||||||
|
|
||||||
|
IPAddress local_ip = WiFi.localIP(); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(local_ip); |
||||||
|
Serial.println("Use your avrdude:"); |
||||||
|
Serial.print("avrdude -c arduino -p <device> -P net:"); |
||||||
|
Serial.print(local_ip); |
||||||
|
Serial.print(":"); |
||||||
|
Serial.print(port); |
||||||
|
Serial.println(" -t # or -U ..."); |
||||||
|
|
||||||
|
// listen for avrdudes
|
||||||
|
avrprog.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
static AVRISPState_t last_state = AVRISP_STATE_IDLE; |
||||||
|
AVRISPState_t new_state = avrprog.update(); |
||||||
|
if (last_state != new_state) { |
||||||
|
switch (new_state) { |
||||||
|
case AVRISP_STATE_IDLE: { |
||||||
|
Serial.printf("[AVRISP] now idle\r\n"); |
||||||
|
// Use the SPI bus for other purposes
|
||||||
|
break; |
||||||
|
} |
||||||
|
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: { |
||||||
|
Serial.printf("[AVRISP] programming mode\r\n"); |
||||||
|
// Stand by for completion
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
last_state = new_state; |
||||||
|
} |
||||||
|
// Serve the client
|
||||||
|
if (last_state != AVRISP_STATE_IDLE) { |
||||||
|
avrprog.serve(); |
||||||
|
} |
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) { |
||||||
|
MDNS.update(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For ESP8266AVRISP |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Library (KEYWORD3) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266AVRISP KEYWORD3 RESERVED_WORD |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
AVRISPState_t KEYWORD1 DATA_TYPE |
||||||
|
AVRISP_parameter_t KEYWORD1 DATA_TYPE |
||||||
|
ESP8266AVRISP KEYWORD1 DATA_TYPE |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
setSpiFrequency KEYWORD2 |
||||||
|
setReset KEYWORD2 |
||||||
|
update KEYWORD2 |
||||||
|
serve KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
AVRISP_STATE_IDLE LITERAL1 RESERVED_WORD_2 |
||||||
|
AVRISP_STATE_PENDING LITERAL1 RESERVED_WORD_2 |
||||||
|
AVRISP_STATE_ACTIVE LITERAL1 RESERVED_WORD_2 |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266AVRISP |
||||||
|
version=1.0 |
||||||
|
author=Kiril Zyapkov |
||||||
|
maintainer=Kiril Zyapkov <kiril@robotev.com> |
||||||
|
sentence=AVR In-System Programming over WiFi for ESP8266 |
||||||
|
paragraph=This library allows programming 8-bit AVR ICSP targets via TCP over WiFi with ESP8266. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,530 @@ |
|||||||
|
/*
|
||||||
|
AVR In-System Programming over WiFi for ESP8266 |
||||||
|
Copyright (c) Kiril Zyapkov <kiril@robotev.com> |
||||||
|
|
||||||
|
Original version: |
||||||
|
ArduinoISP version 04m3 |
||||||
|
Copyright (c) 2008-2011 Randall Bohn |
||||||
|
If you require a license, see |
||||||
|
http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <SPI.h> |
||||||
|
#include <pgmspace.h> |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
|
||||||
|
#include "ESP8266AVRISP.h" |
||||||
|
#include "command.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "user_interface.h" |
||||||
|
#include "mem.h" |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef malloc |
||||||
|
#undef malloc |
||||||
|
#endif |
||||||
|
#define malloc os_malloc |
||||||
|
#ifdef free |
||||||
|
#undef free |
||||||
|
#endif |
||||||
|
#define free os_free |
||||||
|
|
||||||
|
// #define AVRISP_DEBUG(fmt, ...) os_printf("[AVRP] " fmt "\r\n", ##__VA_ARGS__ )
|
||||||
|
#define AVRISP_DEBUG(...) |
||||||
|
|
||||||
|
#define AVRISP_HWVER 2 |
||||||
|
#define AVRISP_SWMAJ 1 |
||||||
|
#define AVRISP_SWMIN 18 |
||||||
|
#define AVRISP_PTIME 10 |
||||||
|
|
||||||
|
#define EECHUNK (32) |
||||||
|
|
||||||
|
#define beget16(addr) (*addr * 256 + *(addr+1)) |
||||||
|
|
||||||
|
ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq, bool reset_state, bool reset_activehigh): |
||||||
|
_spi_freq(spi_freq), _server(WiFiServer(port)), _state(AVRISP_STATE_IDLE), |
||||||
|
_reset_pin(reset_pin), _reset_state(reset_state), _reset_activehigh(reset_activehigh) |
||||||
|
{ |
||||||
|
pinMode(_reset_pin, OUTPUT); |
||||||
|
setReset(_reset_state); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::begin() { |
||||||
|
_server.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::setSpiFrequency(uint32_t freq) { |
||||||
|
_spi_freq = freq; |
||||||
|
if (_state == AVRISP_STATE_ACTIVE) { |
||||||
|
SPI.setFrequency(freq); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::setReset(bool rst) { |
||||||
|
_reset_state = rst; |
||||||
|
digitalWrite(_reset_pin, _resetLevel(_reset_state)); |
||||||
|
} |
||||||
|
|
||||||
|
AVRISPState_t ESP8266AVRISP::update() { |
||||||
|
switch (_state) { |
||||||
|
case AVRISP_STATE_IDLE: { |
||||||
|
if (_server.hasClient()) { |
||||||
|
_client = _server.available(); |
||||||
|
_client.setNoDelay(true); |
||||||
|
AVRISP_DEBUG("client connect %s:%d", _client.remoteIP().toString().c_str(), _client.remotePort()); |
||||||
|
_client.setTimeout(100); // for getch()
|
||||||
|
_state = AVRISP_STATE_PENDING; |
||||||
|
_reject_incoming(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case AVRISP_STATE_PENDING: |
||||||
|
case AVRISP_STATE_ACTIVE: { |
||||||
|
// handle disconnect
|
||||||
|
if (!_client.connected()) { |
||||||
|
_client.stop(); |
||||||
|
AVRISP_DEBUG("client disconnect"); |
||||||
|
if (pmode) { |
||||||
|
SPI.end(); |
||||||
|
pmode = 0; |
||||||
|
} |
||||||
|
setReset(_reset_state); |
||||||
|
_state = AVRISP_STATE_IDLE; |
||||||
|
} else { |
||||||
|
_reject_incoming(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return _state; |
||||||
|
} |
||||||
|
|
||||||
|
AVRISPState_t ESP8266AVRISP::serve() { |
||||||
|
switch (update()) { |
||||||
|
case AVRISP_STATE_IDLE: |
||||||
|
// should not be called when idle, error?
|
||||||
|
break; |
||||||
|
case AVRISP_STATE_PENDING: { |
||||||
|
_state = AVRISP_STATE_ACTIVE; |
||||||
|
// fallthrough
|
||||||
|
} |
||||||
|
case AVRISP_STATE_ACTIVE: { |
||||||
|
while (_client.available()) { |
||||||
|
avrisp(); |
||||||
|
} |
||||||
|
return update(); |
||||||
|
} |
||||||
|
} |
||||||
|
return _state; |
||||||
|
} |
||||||
|
|
||||||
|
inline void ESP8266AVRISP::_reject_incoming(void) { |
||||||
|
while (_server.hasClient()) _server.available().stop(); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266AVRISP::getch() { |
||||||
|
while (!_client.available()) yield(); |
||||||
|
uint8_t b = (uint8_t)_client.read(); |
||||||
|
// AVRISP_DEBUG("< %02x", b);
|
||||||
|
return b; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::fill(int n) { |
||||||
|
// AVRISP_DEBUG("fill(%u)", n);
|
||||||
|
for (int x = 0; x < n; x++) { |
||||||
|
buff[x] = getch(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266AVRISP::spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { |
||||||
|
SPI.transfer(a); |
||||||
|
SPI.transfer(b); |
||||||
|
SPI.transfer(c); |
||||||
|
return SPI.transfer(d); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::empty_reply() { |
||||||
|
if (Sync_CRC_EOP == getch()) { |
||||||
|
_client.print((char)Resp_STK_INSYNC); |
||||||
|
_client.print((char)Resp_STK_OK); |
||||||
|
} else { |
||||||
|
error++; |
||||||
|
_client.print((char)Resp_STK_NOSYNC); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::breply(uint8_t b) { |
||||||
|
if (Sync_CRC_EOP == getch()) { |
||||||
|
uint8_t resp[3]; |
||||||
|
resp[0] = Resp_STK_INSYNC; |
||||||
|
resp[1] = b; |
||||||
|
resp[2] = Resp_STK_OK; |
||||||
|
_client.write((const uint8_t *)resp, (size_t)3); |
||||||
|
} else { |
||||||
|
error++; |
||||||
|
_client.print((char)Resp_STK_NOSYNC); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::get_parameter(uint8_t c) { |
||||||
|
switch (c) { |
||||||
|
case 0x80: |
||||||
|
breply(AVRISP_HWVER); |
||||||
|
break; |
||||||
|
case 0x81: |
||||||
|
breply(AVRISP_SWMAJ); |
||||||
|
break; |
||||||
|
case 0x82: |
||||||
|
breply(AVRISP_SWMIN); |
||||||
|
break; |
||||||
|
case 0x93: |
||||||
|
breply('S'); // serial programmer
|
||||||
|
break; |
||||||
|
default: |
||||||
|
breply(0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::set_parameters() { |
||||||
|
// call this after reading paramter packet into buff[]
|
||||||
|
param.devicecode = buff[0]; |
||||||
|
param.revision = buff[1]; |
||||||
|
param.progtype = buff[2]; |
||||||
|
param.parmode = buff[3]; |
||||||
|
param.polling = buff[4]; |
||||||
|
param.selftimed = buff[5]; |
||||||
|
param.lockbytes = buff[6]; |
||||||
|
param.fusebytes = buff[7]; |
||||||
|
param.flashpoll = buff[8]; |
||||||
|
// ignore buff[9] (= buff[8])
|
||||||
|
// following are 16 bits (big endian)
|
||||||
|
param.eeprompoll = beget16(&buff[10]); |
||||||
|
param.pagesize = beget16(&buff[12]); |
||||||
|
param.eepromsize = beget16(&buff[14]); |
||||||
|
|
||||||
|
// 32 bits flashsize (big endian)
|
||||||
|
param.flashsize = buff[16] * 0x01000000 |
||||||
|
+ buff[17] * 0x00010000 |
||||||
|
+ buff[18] * 0x00000100 |
||||||
|
+ buff[19]; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::start_pmode() { |
||||||
|
SPI.begin(); |
||||||
|
SPI.setFrequency(_spi_freq); |
||||||
|
SPI.setHwCs(false); |
||||||
|
|
||||||
|
// try to sync the bus
|
||||||
|
SPI.transfer(0x00); |
||||||
|
digitalWrite(_reset_pin, _resetLevel(false)); |
||||||
|
delayMicroseconds(50); |
||||||
|
digitalWrite(_reset_pin, _resetLevel(true)); |
||||||
|
delay(30); |
||||||
|
|
||||||
|
spi_transaction(0xAC, 0x53, 0x00, 0x00); |
||||||
|
pmode = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::end_pmode() { |
||||||
|
SPI.end(); |
||||||
|
setReset(_reset_state); |
||||||
|
pmode = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::universal() { |
||||||
|
uint8_t ch; |
||||||
|
|
||||||
|
fill(4); |
||||||
|
ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); |
||||||
|
breply(ch); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::flash(uint8_t hilo, int addr, uint8_t data) { |
||||||
|
spi_transaction(0x40 + 8 * hilo, |
||||||
|
addr >> 8 & 0xFF, |
||||||
|
addr & 0xFF, |
||||||
|
data); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::commit(int addr) { |
||||||
|
spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); |
||||||
|
delay(AVRISP_PTIME); |
||||||
|
} |
||||||
|
|
||||||
|
//#define _addr_page(x) (here & 0xFFFFE0)
|
||||||
|
int ESP8266AVRISP::addr_page(int addr) { |
||||||
|
if (param.pagesize == 32) return addr & 0xFFFFFFF0; |
||||||
|
if (param.pagesize == 64) return addr & 0xFFFFFFE0; |
||||||
|
if (param.pagesize == 128) return addr & 0xFFFFFFC0; |
||||||
|
if (param.pagesize == 256) return addr & 0xFFFFFF80; |
||||||
|
AVRISP_DEBUG("unknown page size: %d", param.pagesize); |
||||||
|
return addr; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ESP8266AVRISP::write_flash(int length) { |
||||||
|
fill(length); |
||||||
|
|
||||||
|
if (Sync_CRC_EOP == getch()) { |
||||||
|
_client.print((char) Resp_STK_INSYNC); |
||||||
|
_client.print((char) write_flash_pages(length)); |
||||||
|
} else { |
||||||
|
error++; |
||||||
|
_client.print((char) Resp_STK_NOSYNC); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266AVRISP::write_flash_pages(int length) { |
||||||
|
int x = 0; |
||||||
|
int page = addr_page(here); |
||||||
|
while (x < length) { |
||||||
|
yield(); |
||||||
|
if (page != addr_page(here)) { |
||||||
|
commit(page); |
||||||
|
page = addr_page(here); |
||||||
|
} |
||||||
|
flash(LOW, here, buff[x++]); |
||||||
|
flash(HIGH, here, buff[x++]); |
||||||
|
here++; |
||||||
|
} |
||||||
|
commit(page); |
||||||
|
return Resp_STK_OK; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266AVRISP::write_eeprom(int length) { |
||||||
|
// here is a word address, get the byte address
|
||||||
|
int start = here * 2; |
||||||
|
int remaining = length; |
||||||
|
if (length > param.eepromsize) { |
||||||
|
error++; |
||||||
|
return Resp_STK_FAILED; |
||||||
|
} |
||||||
|
while (remaining > EECHUNK) { |
||||||
|
write_eeprom_chunk(start, EECHUNK); |
||||||
|
start += EECHUNK; |
||||||
|
remaining -= EECHUNK; |
||||||
|
} |
||||||
|
write_eeprom_chunk(start, remaining); |
||||||
|
return Resp_STK_OK; |
||||||
|
} |
||||||
|
// write (length) bytes, (start) is a byte address
|
||||||
|
uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) { |
||||||
|
// this writes byte-by-byte,
|
||||||
|
// page writing may be faster (4 bytes at a time)
|
||||||
|
fill(length); |
||||||
|
// prog_lamp(LOW);
|
||||||
|
for (int x = 0; x < length; x++) { |
||||||
|
int addr = start + x; |
||||||
|
spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]); |
||||||
|
delay(45); |
||||||
|
} |
||||||
|
// prog_lamp(HIGH);
|
||||||
|
return Resp_STK_OK; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::program_page() { |
||||||
|
char result = (char) Resp_STK_FAILED; |
||||||
|
int length = 256 * getch(); |
||||||
|
length += getch(); |
||||||
|
char memtype = getch(); |
||||||
|
// flash memory @here, (length) bytes
|
||||||
|
if (memtype == 'F') { |
||||||
|
write_flash(length); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (memtype == 'E') { |
||||||
|
result = (char)write_eeprom(length); |
||||||
|
if (Sync_CRC_EOP == getch()) { |
||||||
|
_client.print((char) Resp_STK_INSYNC); |
||||||
|
_client.print(result); |
||||||
|
} else { |
||||||
|
error++; |
||||||
|
_client.print((char) Resp_STK_NOSYNC); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
_client.print((char)Resp_STK_FAILED); |
||||||
|
return; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) { |
||||||
|
return spi_transaction(0x20 + hilo * 8, |
||||||
|
(addr >> 8) & 0xFF, |
||||||
|
addr & 0xFF, |
||||||
|
0); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::flash_read_page(int length) { |
||||||
|
uint8_t *data = (uint8_t *) malloc(length + 1); |
||||||
|
for (int x = 0; x < length; x += 2) { |
||||||
|
*(data + x) = flash_read(LOW, here); |
||||||
|
*(data + x + 1) = flash_read(HIGH, here); |
||||||
|
here++; |
||||||
|
} |
||||||
|
*(data + length) = Resp_STK_OK; |
||||||
|
_client.write((const uint8_t *)data, (size_t)(length + 1)); |
||||||
|
free(data); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::eeprom_read_page(int length) { |
||||||
|
// here again we have a word address
|
||||||
|
uint8_t *data = (uint8_t *) malloc(length + 1); |
||||||
|
int start = here * 2; |
||||||
|
for (int x = 0; x < length; x++) { |
||||||
|
int addr = start + x; |
||||||
|
uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); |
||||||
|
*(data + x) = ee; |
||||||
|
} |
||||||
|
*(data + length) = Resp_STK_OK; |
||||||
|
_client.write((const uint8_t *)data, (size_t)(length + 1)); |
||||||
|
free(data); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::read_page() { |
||||||
|
int length = 256 * getch(); |
||||||
|
length += getch(); |
||||||
|
char memtype = getch(); |
||||||
|
if (Sync_CRC_EOP != getch()) { |
||||||
|
error++; |
||||||
|
_client.print((char) Resp_STK_NOSYNC); |
||||||
|
return; |
||||||
|
} |
||||||
|
_client.print((char) Resp_STK_INSYNC); |
||||||
|
if (memtype == 'F') flash_read_page(length); |
||||||
|
if (memtype == 'E') eeprom_read_page(length); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266AVRISP::read_signature() { |
||||||
|
if (Sync_CRC_EOP != getch()) { |
||||||
|
error++; |
||||||
|
_client.print((char) Resp_STK_NOSYNC); |
||||||
|
return; |
||||||
|
} |
||||||
|
_client.print((char) Resp_STK_INSYNC); |
||||||
|
|
||||||
|
uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); |
||||||
|
_client.print((char) high); |
||||||
|
uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00); |
||||||
|
_client.print((char) middle); |
||||||
|
uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00); |
||||||
|
_client.print((char) low); |
||||||
|
_client.print((char) Resp_STK_OK); |
||||||
|
} |
||||||
|
|
||||||
|
// It seems ArduinoISP is based on the original STK500 (not v2)
|
||||||
|
// but implements only a subset of the commands.
|
||||||
|
void ESP8266AVRISP::avrisp() { |
||||||
|
uint8_t data, low, high; |
||||||
|
uint8_t ch = getch(); |
||||||
|
// Avoid set but not used warning. Leaving them in as it helps document the code
|
||||||
|
(void) data; |
||||||
|
(void) low; |
||||||
|
(void) high; |
||||||
|
// AVRISP_DEBUG("CMD 0x%02x", ch);
|
||||||
|
switch (ch) { |
||||||
|
case Cmnd_STK_GET_SYNC: |
||||||
|
error = 0; |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_GET_SIGN_ON: |
||||||
|
if (getch() == Sync_CRC_EOP) { |
||||||
|
_client.print((char) Resp_STK_INSYNC); |
||||||
|
_client.print(F("AVR ISP")); // AVR061 says "AVR STK"?
|
||||||
|
_client.print((char) Resp_STK_OK); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_GET_PARAMETER: |
||||||
|
get_parameter(getch()); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_SET_DEVICE: |
||||||
|
fill(20); |
||||||
|
set_parameters(); |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_SET_DEVICE_EXT: // ignored
|
||||||
|
fill(5); |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_ENTER_PROGMODE: |
||||||
|
start_pmode(); |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_LOAD_ADDRESS: |
||||||
|
here = getch(); |
||||||
|
here += 256 * getch(); |
||||||
|
// AVRISP_DEBUG("here=0x%04x", here);
|
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
// XXX: not implemented!
|
||||||
|
case Cmnd_STK_PROG_FLASH: |
||||||
|
low = getch(); |
||||||
|
high = getch(); |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
// XXX: not implemented!
|
||||||
|
case Cmnd_STK_PROG_DATA: |
||||||
|
data = getch(); |
||||||
|
empty_reply(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_PROG_PAGE: |
||||||
|
program_page(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_READ_PAGE: |
||||||
|
read_page(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_UNIVERSAL: |
||||||
|
universal(); |
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_LEAVE_PROGMODE: |
||||||
|
error = 0; |
||||||
|
end_pmode(); |
||||||
|
empty_reply(); |
||||||
|
delay(5); |
||||||
|
// if (_client && _client.connected())
|
||||||
|
_client.stop(); |
||||||
|
// AVRISP_DEBUG("left progmode");
|
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case Cmnd_STK_READ_SIGN: |
||||||
|
read_signature(); |
||||||
|
break; |
||||||
|
// expecting a command, not Sync_CRC_EOP
|
||||||
|
// this is how we can get back in sync
|
||||||
|
case Sync_CRC_EOP: // 0x20, space
|
||||||
|
error++; |
||||||
|
_client.print((char) Resp_STK_NOSYNC); |
||||||
|
break; |
||||||
|
|
||||||
|
// anything else we will return STK_UNKNOWN
|
||||||
|
default: |
||||||
|
AVRISP_DEBUG("?!?"); |
||||||
|
error++; |
||||||
|
if (Sync_CRC_EOP == getch()) { |
||||||
|
_client.print((char)Resp_STK_UNKNOWN); |
||||||
|
} else { |
||||||
|
_client.print((char)Resp_STK_NOSYNC); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
/*
|
||||||
|
AVR In-System Programming over WiFi for ESP8266 |
||||||
|
Copyright (c) Kiril Zyapkov <kiril@robotev.com> |
||||||
|
|
||||||
|
Original version: |
||||||
|
ArduinoISP version 04m3 |
||||||
|
Copyright (c) 2008-2011 Randall Bohn |
||||||
|
If you require a license, see |
||||||
|
http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _ESP8266AVRISP_H |
||||||
|
#define _ESP8266AVRISP_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
// uncomment if you use an n-mos to level-shift the reset line
|
||||||
|
// #define AVRISP_ACTIVE_HIGH_RESET
|
||||||
|
|
||||||
|
// SPI clock frequency in Hz
|
||||||
|
#define AVRISP_SPI_FREQ 300e3 |
||||||
|
|
||||||
|
// programmer states
|
||||||
|
typedef enum { |
||||||
|
AVRISP_STATE_IDLE = 0, // no active TCP session
|
||||||
|
AVRISP_STATE_PENDING, // TCP connected, pending SPI activation
|
||||||
|
AVRISP_STATE_ACTIVE // programmer is active and owns the SPI bus
|
||||||
|
} AVRISPState_t; |
||||||
|
|
||||||
|
// stk500 parameters
|
||||||
|
typedef struct { |
||||||
|
uint8_t devicecode; |
||||||
|
uint8_t revision; |
||||||
|
uint8_t progtype; |
||||||
|
uint8_t parmode; |
||||||
|
uint8_t polling; |
||||||
|
uint8_t selftimed; |
||||||
|
uint8_t lockbytes; |
||||||
|
uint8_t fusebytes; |
||||||
|
int flashpoll; |
||||||
|
int eeprompoll; |
||||||
|
int pagesize; |
||||||
|
int eepromsize; |
||||||
|
int flashsize; |
||||||
|
} AVRISP_parameter_t; |
||||||
|
|
||||||
|
|
||||||
|
class ESP8266AVRISP { |
||||||
|
public: |
||||||
|
ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false, bool reset_activehigh=false); |
||||||
|
|
||||||
|
void begin(); |
||||||
|
|
||||||
|
// set the SPI clock frequency
|
||||||
|
void setSpiFrequency(uint32_t); |
||||||
|
|
||||||
|
// control the state of the RESET pin of the target
|
||||||
|
// see AVRISP_ACTIVE_HIGH_RESET
|
||||||
|
void setReset(bool); |
||||||
|
|
||||||
|
// check for pending clients if IDLE, check for disconnect otherwise
|
||||||
|
// returns the updated state
|
||||||
|
AVRISPState_t update(); |
||||||
|
|
||||||
|
// transition to ACTIVE if PENDING
|
||||||
|
// serve STK500 commands from buffer if ACTIVE
|
||||||
|
// returns the updated state
|
||||||
|
AVRISPState_t serve(); |
||||||
|
|
||||||
|
protected: |
||||||
|
|
||||||
|
inline void _reject_incoming(void); // reject any incoming tcp connections
|
||||||
|
|
||||||
|
void avrisp(void); // handle incoming STK500 commands
|
||||||
|
|
||||||
|
uint8_t getch(void); // retrieve a character from the remote end
|
||||||
|
uint8_t spi_transaction(uint8_t, uint8_t, uint8_t, uint8_t); |
||||||
|
void empty_reply(void); |
||||||
|
void breply(uint8_t); |
||||||
|
|
||||||
|
void get_parameter(uint8_t); |
||||||
|
void set_parameters(void); |
||||||
|
int addr_page(int); |
||||||
|
void flash(uint8_t, int, uint8_t); |
||||||
|
void write_flash(int); |
||||||
|
uint8_t write_flash_pages(int length); |
||||||
|
uint8_t write_eeprom(int length); |
||||||
|
uint8_t write_eeprom_chunk(int start, int length); |
||||||
|
void commit(int addr); |
||||||
|
void program_page(); |
||||||
|
uint8_t flash_read(uint8_t hilo, int addr); |
||||||
|
void flash_read_page(int length); |
||||||
|
void eeprom_read_page(int length); |
||||||
|
void read_page(); |
||||||
|
void read_signature(); |
||||||
|
|
||||||
|
void universal(void); |
||||||
|
|
||||||
|
void fill(int); // fill the buffer with n bytes
|
||||||
|
void start_pmode(void); // enter program mode
|
||||||
|
void end_pmode(void); // exit program mode
|
||||||
|
|
||||||
|
inline bool _resetLevel(bool reset_state) { return reset_state == _reset_activehigh; } |
||||||
|
|
||||||
|
uint32_t _spi_freq; |
||||||
|
WiFiServer _server; |
||||||
|
WiFiClient _client; |
||||||
|
AVRISPState_t _state; |
||||||
|
uint8_t _reset_pin; |
||||||
|
bool _reset_state; |
||||||
|
bool _reset_activehigh; |
||||||
|
|
||||||
|
// programmer settings, set by remote end
|
||||||
|
AVRISP_parameter_t param; |
||||||
|
// page buffer
|
||||||
|
uint8_t buff[256]; |
||||||
|
|
||||||
|
int error = 0; |
||||||
|
bool pmode = 0; |
||||||
|
|
||||||
|
// address for reading and writing, set by 'U' command
|
||||||
|
int here; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif // _ESP8266AVRISP_H
|
@ -0,0 +1,108 @@ |
|||||||
|
//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************
|
||||||
|
//*
|
||||||
|
//* Title: AVR061 - STK500 Communication Protocol
|
||||||
|
//* Filename: command.h
|
||||||
|
//* Version: 1.0
|
||||||
|
//* Last updated: 09.09.2002
|
||||||
|
//*
|
||||||
|
//* Support E-mail: avr@atmel.com
|
||||||
|
//*
|
||||||
|
//**************************************************************************
|
||||||
|
|
||||||
|
// *****************[ STK Message constants ]***************************
|
||||||
|
|
||||||
|
#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON
|
||||||
|
|
||||||
|
// *****************[ STK Response constants ]***************************
|
||||||
|
|
||||||
|
#define Resp_STK_OK 0x10 // ' '
|
||||||
|
#define Resp_STK_FAILED 0x11 // ' '
|
||||||
|
#define Resp_STK_UNKNOWN 0x12 // ' '
|
||||||
|
#define Resp_STK_NODEVICE 0x13 // ' '
|
||||||
|
#define Resp_STK_INSYNC 0x14 // ' '
|
||||||
|
#define Resp_STK_NOSYNC 0x15 // ' '
|
||||||
|
|
||||||
|
#define Resp_ADC_CHANNEL_ERROR 0x16 // ' '
|
||||||
|
#define Resp_ADC_MEASURE_OK 0x17 // ' '
|
||||||
|
#define Resp_PWM_CHANNEL_ERROR 0x18 // ' '
|
||||||
|
#define Resp_PWM_ADJUST_OK 0x19 // ' '
|
||||||
|
|
||||||
|
// *****************[ STK Special constants ]***************************
|
||||||
|
|
||||||
|
#define Sync_CRC_EOP 0x20 // 'SPACE'
|
||||||
|
|
||||||
|
// *****************[ STK Command constants ]***************************
|
||||||
|
|
||||||
|
#define Cmnd_STK_GET_SYNC 0x30 // ' '
|
||||||
|
#define Cmnd_STK_GET_SIGN_ON 0x31 // ' '
|
||||||
|
#define Cmnd_STK_RESET 0x32 // ' '
|
||||||
|
#define Cmnd_STK_SINGLE_CLOCK 0x33 // ' '
|
||||||
|
#define Cmnd_STK_STORE_PARAMETERS 0x34 // ' '
|
||||||
|
|
||||||
|
#define Cmnd_STK_SET_PARAMETER 0x40 // ' '
|
||||||
|
#define Cmnd_STK_GET_PARAMETER 0x41 // ' '
|
||||||
|
#define Cmnd_STK_SET_DEVICE 0x42 // ' '
|
||||||
|
#define Cmnd_STK_GET_DEVICE 0x43 // ' '
|
||||||
|
#define Cmnd_STK_GET_STATUS 0x44 // ' '
|
||||||
|
#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' '
|
||||||
|
|
||||||
|
#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' '
|
||||||
|
#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' '
|
||||||
|
#define Cmnd_STK_CHIP_ERASE 0x52 // ' '
|
||||||
|
#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' '
|
||||||
|
#define Cmnd_STK_CHECK_DEVICE 0x54 // ' '
|
||||||
|
#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' '
|
||||||
|
#define Cmnd_STK_UNIVERSAL 0x56 // ' '
|
||||||
|
|
||||||
|
#define Cmnd_STK_PROG_FLASH 0x60 // ' '
|
||||||
|
#define Cmnd_STK_PROG_DATA 0x61 // ' '
|
||||||
|
#define Cmnd_STK_PROG_FUSE 0x62 // ' '
|
||||||
|
#define Cmnd_STK_PROG_LOCK 0x63 // ' '
|
||||||
|
#define Cmnd_STK_PROG_PAGE 0x64 // ' '
|
||||||
|
#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' '
|
||||||
|
|
||||||
|
#define Cmnd_STK_READ_FLASH 0x70 // ' '
|
||||||
|
#define Cmnd_STK_READ_DATA 0x71 // ' '
|
||||||
|
#define Cmnd_STK_READ_FUSE 0x72 // ' '
|
||||||
|
#define Cmnd_STK_READ_LOCK 0x73 // ' '
|
||||||
|
#define Cmnd_STK_READ_PAGE 0x74 // ' '
|
||||||
|
#define Cmnd_STK_READ_SIGN 0x75 // ' '
|
||||||
|
#define Cmnd_STK_READ_OSCCAL 0x76 // ' '
|
||||||
|
#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' '
|
||||||
|
#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' '
|
||||||
|
|
||||||
|
// *****************[ STK Parameter constants ]***************************
|
||||||
|
|
||||||
|
#define Parm_STK_HW_VER 0x80 // ' ' - R
|
||||||
|
#define Parm_STK_SW_MAJOR 0x81 // ' ' - R
|
||||||
|
#define Parm_STK_SW_MINOR 0x82 // ' ' - R
|
||||||
|
#define Parm_STK_LEDS 0x83 // ' ' - R/W
|
||||||
|
#define Parm_STK_VTARGET 0x84 // ' ' - R/W
|
||||||
|
#define Parm_STK_VADJUST 0x85 // ' ' - R/W
|
||||||
|
#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W
|
||||||
|
#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W
|
||||||
|
#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W
|
||||||
|
#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W
|
||||||
|
|
||||||
|
#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255}
|
||||||
|
#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255}
|
||||||
|
#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255}
|
||||||
|
#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S'
|
||||||
|
#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE
|
||||||
|
#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE
|
||||||
|
#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE
|
||||||
|
|
||||||
|
|
||||||
|
// *****************[ STK status bit definitions ]***************************
|
||||||
|
|
||||||
|
#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC
|
||||||
|
#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE
|
||||||
|
#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode
|
||||||
|
#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed
|
||||||
|
#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed
|
||||||
|
#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit
|
||||||
|
#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit
|
||||||
|
#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink
|
||||||
|
|
||||||
|
|
||||||
|
// *****************************[ End Of COMMAND.H ]**************************
|
@ -0,0 +1,86 @@ |
|||||||
|
/**
|
||||||
|
Authorization.ino |
||||||
|
|
||||||
|
Created on: 09.12.2015 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
#include <WiFiClient.h> |
||||||
|
|
||||||
|
ESP8266WiFiMulti WiFiMulti; |
||||||
|
|
||||||
|
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"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// wait for WiFi connection
|
||||||
|
if ((WiFiMulti.run() == WL_CONNECTED)) { |
||||||
|
|
||||||
|
WiFiClient client; |
||||||
|
|
||||||
|
HTTPClient http; |
||||||
|
|
||||||
|
Serial.print("[HTTP] begin...\n"); |
||||||
|
// configure traged server and url
|
||||||
|
|
||||||
|
|
||||||
|
http.begin(client, "http://guest:guest@jigsaw.w3.org/HTTP/Basic/"); |
||||||
|
|
||||||
|
/*
|
||||||
|
// or
|
||||||
|
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/"); |
||||||
|
http.setAuthorization("guest", "guest"); |
||||||
|
|
||||||
|
// or
|
||||||
|
http.begin(client, "http://jigsaw.w3.org/HTTP/Basic/"); |
||||||
|
http.setAuthorization("Z3Vlc3Q6Z3Vlc3Q="); |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
Serial.print("[HTTP] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = http.GET(); |
||||||
|
|
||||||
|
// httpCode will be negative on error
|
||||||
|
if (httpCode > 0) { |
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTP] GET... code: %d\n", httpCode); |
||||||
|
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK) { |
||||||
|
String payload = http.getString(); |
||||||
|
Serial.println(payload); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
http.end(); |
||||||
|
} |
||||||
|
|
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/**
|
||||||
|
BasicHTTPClient.ino |
||||||
|
|
||||||
|
Created on: 24.05.2015 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
#include <WiFiClient.h> |
||||||
|
|
||||||
|
ESP8266WiFiMulti WiFiMulti; |
||||||
|
|
||||||
|
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"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// wait for WiFi connection
|
||||||
|
if ((WiFiMulti.run() == WL_CONNECTED)) { |
||||||
|
|
||||||
|
WiFiClient client; |
||||||
|
|
||||||
|
HTTPClient http; |
||||||
|
|
||||||
|
Serial.print("[HTTP] begin...\n"); |
||||||
|
if (http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html")) { // HTTP
|
||||||
|
|
||||||
|
|
||||||
|
Serial.print("[HTTP] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = http.GET(); |
||||||
|
|
||||||
|
// httpCode will be negative on error
|
||||||
|
if (httpCode > 0) { |
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTP] GET... code: %d\n", httpCode); |
||||||
|
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { |
||||||
|
String payload = http.getString(); |
||||||
|
Serial.println(payload); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
http.end(); |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP} Unable to connect\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/**
|
||||||
|
BasicHTTPSClient.ino |
||||||
|
|
||||||
|
Created on: 20.08.2018 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
#include <WiFiClientSecureBearSSL.h> |
||||||
|
// Fingerprint for demo URL, expires on June 2, 2019, needs to be updated well before this date
|
||||||
|
const uint8_t fingerprint[20] = {0x5A, 0xCF, 0xFE, 0xF0, 0xF1, 0xA6, 0xF4, 0x5F, 0xD2, 0x11, 0x11, 0xC6, 0x1D, 0x2F, 0x0E, 0xBC, 0x39, 0x8D, 0x50, 0xE0}; |
||||||
|
|
||||||
|
ESP8266WiFiMulti WiFiMulti; |
||||||
|
|
||||||
|
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"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// wait for WiFi connection
|
||||||
|
if ((WiFiMulti.run() == WL_CONNECTED)) { |
||||||
|
|
||||||
|
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure); |
||||||
|
|
||||||
|
client->setFingerprint(fingerprint); |
||||||
|
|
||||||
|
HTTPClient https; |
||||||
|
|
||||||
|
Serial.print("[HTTPS] begin...\n"); |
||||||
|
if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
|
||||||
|
|
||||||
|
Serial.print("[HTTPS] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = https.GET(); |
||||||
|
|
||||||
|
// httpCode will be negative on error
|
||||||
|
if (httpCode > 0) { |
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTPS] GET... code: %d\n", httpCode); |
||||||
|
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { |
||||||
|
String payload = https.getString(); |
||||||
|
Serial.println(payload); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
https.end(); |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTPS] Unable to connect\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Serial.println("Wait 10s before next round..."); |
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/*
|
||||||
|
This sketch shows how to handle HTTP Digest Authorization. |
||||||
|
|
||||||
|
Written by Parham Alvani and Sajjad Rahnama, 2018-01-07. |
||||||
|
|
||||||
|
This example is released into public domain, |
||||||
|
or, at your option, CC0 licensed. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* ssidPassword = STAPSK; |
||||||
|
|
||||||
|
const char *username = "admin"; |
||||||
|
const char *password = "admin"; |
||||||
|
|
||||||
|
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 ""; |
||||||
|
} |
||||||
|
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); |
||||||
|
} |
||||||
|
|
||||||
|
String getCNonce(const int len) { |
||||||
|
static const char alphanum[] = |
||||||
|
"0123456789" |
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
||||||
|
"abcdefghijklmnopqrstuvwxyz"; |
||||||
|
String s = ""; |
||||||
|
|
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
s += alphanum[rand() % (sizeof(alphanum) - 1)]; |
||||||
|
} |
||||||
|
|
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
String getDigestAuth(String& authReq, const String& username, const String& password, const String& uri, unsigned int counter) { |
||||||
|
// extracting required parameters for RFC 2069 simpler Digest
|
||||||
|
String realm = exractParam(authReq, "realm=\"", '"'); |
||||||
|
String nonce = exractParam(authReq, "nonce=\"", '"'); |
||||||
|
String cNonce = getCNonce(8); |
||||||
|
|
||||||
|
char nc[9]; |
||||||
|
snprintf(nc, sizeof(nc), "%08x", counter); |
||||||
|
|
||||||
|
// parameters for the RFC 2617 newer Digest
|
||||||
|
MD5Builder md5; |
||||||
|
md5.begin(); |
||||||
|
md5.add(username + ":" + realm + ":" + password); // md5 of the user:realm:user
|
||||||
|
md5.calculate(); |
||||||
|
String h1 = md5.toString(); |
||||||
|
|
||||||
|
md5.begin(); |
||||||
|
md5.add(String("GET:") + uri); |
||||||
|
md5.calculate(); |
||||||
|
String h2 = md5.toString(); |
||||||
|
|
||||||
|
md5.begin(); |
||||||
|
md5.add(h1 + ":" + nonce + ":" + String(nc) + ":" + cNonce + ":" + "auth" + ":" + h2); |
||||||
|
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 + "\""; |
||||||
|
Serial.println(authorization); |
||||||
|
|
||||||
|
return authorization; |
||||||
|
} |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, ssidPassword); |
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
|
||||||
|
Serial.println(""); |
||||||
|
Serial.println("WiFi connected"); |
||||||
|
Serial.println("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
HTTPClient http; |
||||||
|
|
||||||
|
WiFiClient client; |
||||||
|
|
||||||
|
Serial.print("[HTTP] begin...\n"); |
||||||
|
|
||||||
|
// configure traged server and url
|
||||||
|
http.begin(client, String(server) + String(uri)); |
||||||
|
|
||||||
|
|
||||||
|
const char *keys[] = {"WWW-Authenticate"}; |
||||||
|
http.collectHeaders(keys, 1); |
||||||
|
|
||||||
|
Serial.print("[HTTP] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = http.GET(); |
||||||
|
|
||||||
|
if (httpCode > 0) { |
||||||
|
String authReq = http.header("WWW-Authenticate"); |
||||||
|
Serial.println(authReq); |
||||||
|
|
||||||
|
String authorization = getDigestAuth(authReq, String(username), String(password), String(uri), 1); |
||||||
|
|
||||||
|
http.end(); |
||||||
|
http.begin(client, String(server) + String(uri)); |
||||||
|
|
||||||
|
http.addHeader("Authorization", authorization); |
||||||
|
|
||||||
|
int httpCode = http.GET(); |
||||||
|
if (httpCode > 0) { |
||||||
|
String payload = http.getString(); |
||||||
|
Serial.println(payload); |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
http.end(); |
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/**
|
||||||
|
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); |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
/**
|
||||||
|
StreamHTTPClient.ino |
||||||
|
|
||||||
|
Created on: 24.05.2015 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
ESP8266WiFiMulti WiFiMulti; |
||||||
|
|
||||||
|
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"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// wait for WiFi connection
|
||||||
|
if ((WiFiMulti.run() == WL_CONNECTED)) { |
||||||
|
|
||||||
|
HTTPClient http; |
||||||
|
|
||||||
|
WiFiClient 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");
|
||||||
|
|
||||||
|
Serial.print("[HTTP] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = http.GET(); |
||||||
|
if (httpCode > 0) { |
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTP] GET... code: %d\n", httpCode); |
||||||
|
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK) { |
||||||
|
|
||||||
|
// get lenght of document (is -1 when Server sends no Content-Length header)
|
||||||
|
int len = http.getSize(); |
||||||
|
|
||||||
|
// create buffer for read
|
||||||
|
uint8_t buff[128] = { 0 }; |
||||||
|
|
||||||
|
// get tcp stream
|
||||||
|
WiFiClient * stream = &client; |
||||||
|
|
||||||
|
// read all data from server
|
||||||
|
while (http.connected() && (len > 0 || len == -1)) { |
||||||
|
// get available data size
|
||||||
|
size_t size = stream->available(); |
||||||
|
|
||||||
|
if (size) { |
||||||
|
// read up to 128 byte
|
||||||
|
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); |
||||||
|
|
||||||
|
// write it to Serial
|
||||||
|
Serial.write(buff, c); |
||||||
|
|
||||||
|
if (len > 0) { |
||||||
|
len -= c; |
||||||
|
} |
||||||
|
} |
||||||
|
delay(1); |
||||||
|
} |
||||||
|
|
||||||
|
Serial.println(); |
||||||
|
Serial.print("[HTTP] connection closed or file end.\n"); |
||||||
|
|
||||||
|
} |
||||||
|
} else { |
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
http.end(); |
||||||
|
} |
||||||
|
|
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/**
|
||||||
|
StreamHTTPClient.ino |
||||||
|
|
||||||
|
Created on: 24.05.2015 |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WiFiMulti.h> |
||||||
|
|
||||||
|
#include <ESP8266HTTPClient.h> |
||||||
|
|
||||||
|
ESP8266WiFiMulti WiFiMulti; |
||||||
|
|
||||||
|
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"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// wait for WiFi connection
|
||||||
|
if ((WiFiMulti.run() == WL_CONNECTED)) { |
||||||
|
|
||||||
|
std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure); |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
Serial.print("[HTTPS] begin...\n"); |
||||||
|
|
||||||
|
// configure server and url
|
||||||
|
const uint8_t fingerprint[20] = {0xEB, 0xD9, 0xDF, 0x37, 0xC2, 0xCC, 0x84, 0x89, 0x00, 0xA0, 0x58, 0x52, 0x24, 0x04, 0xE4, 0x37, 0x3E, 0x2B, 0xF1, 0x41}; |
||||||
|
|
||||||
|
client->setFingerprint(fingerprint); |
||||||
|
|
||||||
|
HTTPClient https; |
||||||
|
|
||||||
|
if (https.begin(*client, "https://tls.mbed.org/")) { |
||||||
|
|
||||||
|
Serial.print("[HTTPS] GET...\n"); |
||||||
|
// start connection and send HTTP header
|
||||||
|
int httpCode = https.GET(); |
||||||
|
if (httpCode > 0) { |
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTPS] GET... code: %d\n", httpCode); |
||||||
|
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK) { |
||||||
|
|
||||||
|
// get lenght of document (is -1 when Server sends no Content-Length header)
|
||||||
|
int len = https.getSize(); |
||||||
|
|
||||||
|
// create buffer for read
|
||||||
|
static uint8_t buff[128] = { 0 }; |
||||||
|
|
||||||
|
// read all data from server
|
||||||
|
while (https.connected() && (len > 0 || len == -1)) { |
||||||
|
// get available data size
|
||||||
|
size_t size = client->available(); |
||||||
|
|
||||||
|
if (size) { |
||||||
|
// read up to 128 byte
|
||||||
|
int c = client->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); |
||||||
|
|
||||||
|
// write it to Serial
|
||||||
|
Serial.write(buff, 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()); |
||||||
|
} |
||||||
|
|
||||||
|
https.end(); |
||||||
|
} else { |
||||||
|
Serial.printf("Unable to connect\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Serial.println("Wait 10s before the next round..."); |
||||||
|
delay(10000); |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For ESP8266HTTPClient |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Library (KEYWORD3) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266HTTPClient KEYWORD3 RESERVED_WORD |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
t_http_codes KEYWORD1 DATA_TYPE |
||||||
|
transferEncoding_t KEYWORD1 DATA_TYPE |
||||||
|
TransportTraits KEYWORD1 DATA_TYPE |
||||||
|
TransportTraitsPtr KEYWORD1 DATA_TYPE |
||||||
|
StreamString KEYWORD1 DATA_TYPE |
||||||
|
HTTPClient KEYWORD1 DATA_TYPE |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
end KEYWORD2 |
||||||
|
connected KEYWORD2 |
||||||
|
setReuse KEYWORD2 |
||||||
|
setUserAgent KEYWORD2 |
||||||
|
setAuthorization KEYWORD2 |
||||||
|
setTimeout KEYWORD2 |
||||||
|
useHTTP10 KEYWORD2 |
||||||
|
GET KEYWORD2 |
||||||
|
POST KEYWORD2 |
||||||
|
PUT KEYWORD2 |
||||||
|
PATCH KEYWORD2 |
||||||
|
sendRequest KEYWORD2 |
||||||
|
addHeader KEYWORD2 |
||||||
|
collectHeaders KEYWORD2 |
||||||
|
header KEYWORD2 |
||||||
|
headerName KEYWORD2 |
||||||
|
headers KEYWORD2 |
||||||
|
hasHeader KEYWORD2 |
||||||
|
getSize KEYWORD2 |
||||||
|
getStream KEYWORD2 |
||||||
|
getStreamPtr KEYWORD2 |
||||||
|
writeToStream KEYWORD2 |
||||||
|
getString KEYWORD2 |
||||||
|
errorToString KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
HTTPCLIENT_DEFAULT_TCP_TIMEOUT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_CONNECTION_REFUSED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_SEND_HEADER_FAILED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_SEND_PAYLOAD_FAILED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_NOT_CONNECTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_CONNECTION_LOST LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_NO_STREAM LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_NO_HTTP_SERVER LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_TOO_LESS_RAM LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_ENCODING LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_STREAM_WRITE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_ERROR_READ_TIMEOUT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_TCP_BUFFER_SIZE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_CONTINUE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_SWITCHING_PROTOCOLS LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PROCESSING LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_OK LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_CREATED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_ACCEPTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NO_CONTENT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_RESET_CONTENT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PARTIAL_CONTENT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_MULTI_STATUS LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_ALREADY_REPORTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_IM_USED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_MULTIPLE_CHOICES LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_MOVED_PERMANENTLY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_FOUND LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_SEE_OTHER LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NOT_MODIFIED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_USE_PROXY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_TEMPORARY_REDIRECT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PERMANENT_REDIRECT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_BAD_REQUEST LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_UNAUTHORIZED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PAYMENT_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_FORBIDDEN LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NOT_FOUND LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_METHOD_NOT_ALLOWED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NOT_ACCEPTABLE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_REQUEST_TIMEOUT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_CONFLICT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_GONE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_LENGTH_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PRECONDITION_FAILED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PAYLOAD_TOO_LARGE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_URI_TOO_LONG LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_RANGE_NOT_SATISFIABLE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_EXPECTATION_FAILED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_MISDIRECTED_REQUEST LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_UNPROCESSABLE_ENTITY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_LOCKED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_FAILED_DEPENDENCY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_UPGRADE_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_PRECONDITION_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_TOO_MANY_REQUESTS LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_INTERNAL_SERVER_ERROR LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NOT_IMPLEMENTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_BAD_GATEWAY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_SERVICE_UNAVAILABLE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_GATEWAY_TIMEOUT LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_VARIANT_ALSO_NEGOTIATES LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_INSUFFICIENT_STORAGE LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_LOOP_DETECTED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NOT_EXTENDED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_TE_IDENTITY LITERAL1 RESERVED_WORD_2 |
||||||
|
HTTPC_TE_CHUNKED LITERAL1 RESERVED_WORD_2 |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266HTTPClient |
||||||
|
version=1.2 |
||||||
|
author=Markus Sattler |
||||||
|
maintainer=Markus Sattler |
||||||
|
sentence=http Client for ESP8266 |
||||||
|
paragraph= |
||||||
|
category=Communication |
||||||
|
url=https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,259 @@ |
|||||||
|
/**
|
||||||
|
* ESP8266HTTPClient.h |
||||||
|
* |
||||||
|
* Created on: 02.11.2015 |
||||||
|
* |
||||||
|
* Copyright (c) 2015 Markus Sattler. All rights reserved. |
||||||
|
* This file is part of the ESP8266HTTPClient for Arduino. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* Modified by Jeroen Döll, June 2018 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ESP8266HTTPClient_H_ |
||||||
|
#define ESP8266HTTPClient_H_ |
||||||
|
|
||||||
|
#ifndef HTTPCLIENT_1_1_COMPATIBLE |
||||||
|
#define HTTPCLIENT_1_1_COMPATIBLE 1 |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#include <WiFiClient.h> |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_CLIENT |
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ ) |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef DEBUG_HTTPCLIENT |
||||||
|
#define DEBUG_HTTPCLIENT(...) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000) |
||||||
|
|
||||||
|
/// HTTP client errors
|
||||||
|
#define HTTPC_ERROR_CONNECTION_REFUSED (-1) |
||||||
|
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2) |
||||||
|
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3) |
||||||
|
#define HTTPC_ERROR_NOT_CONNECTED (-4) |
||||||
|
#define HTTPC_ERROR_CONNECTION_LOST (-5) |
||||||
|
#define HTTPC_ERROR_NO_STREAM (-6) |
||||||
|
#define HTTPC_ERROR_NO_HTTP_SERVER (-7) |
||||||
|
#define HTTPC_ERROR_TOO_LESS_RAM (-8) |
||||||
|
#define HTTPC_ERROR_ENCODING (-9) |
||||||
|
#define HTTPC_ERROR_STREAM_WRITE (-10) |
||||||
|
#define HTTPC_ERROR_READ_TIMEOUT (-11) |
||||||
|
|
||||||
|
/// size for the stream handling
|
||||||
|
#define HTTP_TCP_BUFFER_SIZE (1460) |
||||||
|
|
||||||
|
/// HTTP codes see RFC7231
|
||||||
|
typedef enum { |
||||||
|
HTTP_CODE_CONTINUE = 100, |
||||||
|
HTTP_CODE_SWITCHING_PROTOCOLS = 101, |
||||||
|
HTTP_CODE_PROCESSING = 102, |
||||||
|
HTTP_CODE_OK = 200, |
||||||
|
HTTP_CODE_CREATED = 201, |
||||||
|
HTTP_CODE_ACCEPTED = 202, |
||||||
|
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203, |
||||||
|
HTTP_CODE_NO_CONTENT = 204, |
||||||
|
HTTP_CODE_RESET_CONTENT = 205, |
||||||
|
HTTP_CODE_PARTIAL_CONTENT = 206, |
||||||
|
HTTP_CODE_MULTI_STATUS = 207, |
||||||
|
HTTP_CODE_ALREADY_REPORTED = 208, |
||||||
|
HTTP_CODE_IM_USED = 226, |
||||||
|
HTTP_CODE_MULTIPLE_CHOICES = 300, |
||||||
|
HTTP_CODE_MOVED_PERMANENTLY = 301, |
||||||
|
HTTP_CODE_FOUND = 302, |
||||||
|
HTTP_CODE_SEE_OTHER = 303, |
||||||
|
HTTP_CODE_NOT_MODIFIED = 304, |
||||||
|
HTTP_CODE_USE_PROXY = 305, |
||||||
|
HTTP_CODE_TEMPORARY_REDIRECT = 307, |
||||||
|
HTTP_CODE_PERMANENT_REDIRECT = 308, |
||||||
|
HTTP_CODE_BAD_REQUEST = 400, |
||||||
|
HTTP_CODE_UNAUTHORIZED = 401, |
||||||
|
HTTP_CODE_PAYMENT_REQUIRED = 402, |
||||||
|
HTTP_CODE_FORBIDDEN = 403, |
||||||
|
HTTP_CODE_NOT_FOUND = 404, |
||||||
|
HTTP_CODE_METHOD_NOT_ALLOWED = 405, |
||||||
|
HTTP_CODE_NOT_ACCEPTABLE = 406, |
||||||
|
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407, |
||||||
|
HTTP_CODE_REQUEST_TIMEOUT = 408, |
||||||
|
HTTP_CODE_CONFLICT = 409, |
||||||
|
HTTP_CODE_GONE = 410, |
||||||
|
HTTP_CODE_LENGTH_REQUIRED = 411, |
||||||
|
HTTP_CODE_PRECONDITION_FAILED = 412, |
||||||
|
HTTP_CODE_PAYLOAD_TOO_LARGE = 413, |
||||||
|
HTTP_CODE_URI_TOO_LONG = 414, |
||||||
|
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415, |
||||||
|
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416, |
||||||
|
HTTP_CODE_EXPECTATION_FAILED = 417, |
||||||
|
HTTP_CODE_MISDIRECTED_REQUEST = 421, |
||||||
|
HTTP_CODE_UNPROCESSABLE_ENTITY = 422, |
||||||
|
HTTP_CODE_LOCKED = 423, |
||||||
|
HTTP_CODE_FAILED_DEPENDENCY = 424, |
||||||
|
HTTP_CODE_UPGRADE_REQUIRED = 426, |
||||||
|
HTTP_CODE_PRECONDITION_REQUIRED = 428, |
||||||
|
HTTP_CODE_TOO_MANY_REQUESTS = 429, |
||||||
|
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, |
||||||
|
HTTP_CODE_INTERNAL_SERVER_ERROR = 500, |
||||||
|
HTTP_CODE_NOT_IMPLEMENTED = 501, |
||||||
|
HTTP_CODE_BAD_GATEWAY = 502, |
||||||
|
HTTP_CODE_SERVICE_UNAVAILABLE = 503, |
||||||
|
HTTP_CODE_GATEWAY_TIMEOUT = 504, |
||||||
|
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505, |
||||||
|
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506, |
||||||
|
HTTP_CODE_INSUFFICIENT_STORAGE = 507, |
||||||
|
HTTP_CODE_LOOP_DETECTED = 508, |
||||||
|
HTTP_CODE_NOT_EXTENDED = 510, |
||||||
|
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511 |
||||||
|
} t_http_codes; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
HTTPC_TE_IDENTITY, |
||||||
|
HTTPC_TE_CHUNKED |
||||||
|
} transferEncoding_t; |
||||||
|
|
||||||
|
#if HTTPCLIENT_1_1_COMPATIBLE |
||||||
|
class TransportTraits; |
||||||
|
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr; |
||||||
|
#endif |
||||||
|
|
||||||
|
class StreamString; |
||||||
|
|
||||||
|
class HTTPClient |
||||||
|
{ |
||||||
|
public: |
||||||
|
HTTPClient(); |
||||||
|
~HTTPClient(); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
bool begin(WiFiClient &client, String url); |
||||||
|
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false); |
||||||
|
|
||||||
|
#if HTTPCLIENT_1_1_COMPATIBLE |
||||||
|
// Plain HTTP connection, unencrypted
|
||||||
|
bool begin(String url) __attribute__ ((deprecated)); |
||||||
|
bool begin(String host, uint16_t port, String uri = "/") __attribute__ ((deprecated)); |
||||||
|
// Use axTLS for secure HTTPS connection
|
||||||
|
bool begin(String url, String httpsFingerprint) __attribute__ ((deprecated)); |
||||||
|
bool begin(String host, uint16_t port, String uri, String httpsFingerprint) __attribute__ ((deprecated)); |
||||||
|
// Use BearSSL for secure HTTPS connection
|
||||||
|
bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated)); |
||||||
|
bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated)); |
||||||
|
// deprecated, use the overload above instead
|
||||||
|
bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); |
||||||
|
#endif |
||||||
|
|
||||||
|
void end(void); |
||||||
|
|
||||||
|
bool connected(void); |
||||||
|
|
||||||
|
void setReuse(bool reuse); /// keep-alive
|
||||||
|
void setUserAgent(const String& userAgent); |
||||||
|
void setAuthorization(const char * user, const char * password); |
||||||
|
void setAuthorization(const char * auth); |
||||||
|
void setTimeout(uint16_t timeout); |
||||||
|
|
||||||
|
void useHTTP10(bool usehttp10 = true); |
||||||
|
|
||||||
|
/// request handling
|
||||||
|
int GET(); |
||||||
|
int POST(uint8_t * payload, size_t size); |
||||||
|
int POST(String payload); |
||||||
|
int PUT(uint8_t * payload, size_t size); |
||||||
|
int PUT(String payload); |
||||||
|
int PATCH(uint8_t * payload, size_t size); |
||||||
|
int PATCH(String payload); |
||||||
|
int sendRequest(const char * type, String payload); |
||||||
|
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0); |
||||||
|
int sendRequest(const char * type, Stream * stream, size_t size = 0); |
||||||
|
|
||||||
|
void addHeader(const String& name, const String& value, bool first = false, bool replace = true); |
||||||
|
|
||||||
|
/// Response handling
|
||||||
|
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); |
||||||
|
String header(const char* name); // get request header value by name
|
||||||
|
String header(size_t i); // get request header value by number
|
||||||
|
String headerName(size_t i); // get request header name by number
|
||||||
|
int headers(); // get header count
|
||||||
|
bool hasHeader(const char* name); // check if header exists
|
||||||
|
|
||||||
|
|
||||||
|
int getSize(void); |
||||||
|
|
||||||
|
WiFiClient& getStream(void); |
||||||
|
WiFiClient* getStreamPtr(void); |
||||||
|
int writeToStream(Stream* stream); |
||||||
|
const String& getString(void); |
||||||
|
|
||||||
|
static String errorToString(int error); |
||||||
|
|
||||||
|
protected: |
||||||
|
struct RequestArgument { |
||||||
|
String key; |
||||||
|
String value; |
||||||
|
}; |
||||||
|
|
||||||
|
bool beginInternal(String url, const char* expectedProtocol); |
||||||
|
void disconnect(); |
||||||
|
void clear(); |
||||||
|
int returnError(int error); |
||||||
|
bool connect(void); |
||||||
|
bool sendHeader(const char * type); |
||||||
|
int handleHeaderResponse(); |
||||||
|
int writeToStreamDataBlock(Stream * stream, int len); |
||||||
|
|
||||||
|
|
||||||
|
#if HTTPCLIENT_1_1_COMPATIBLE |
||||||
|
TransportTraitsPtr _transportTraits; |
||||||
|
std::unique_ptr<WiFiClient> _tcpDeprecated; |
||||||
|
#endif |
||||||
|
WiFiClient* _client; |
||||||
|
|
||||||
|
/// request handling
|
||||||
|
String _host; |
||||||
|
uint16_t _port = 0; |
||||||
|
bool _reuse = false; |
||||||
|
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; |
||||||
|
bool _useHTTP10 = false; |
||||||
|
|
||||||
|
String _uri; |
||||||
|
String _protocol; |
||||||
|
String _headers; |
||||||
|
String _userAgent = "ESP8266HTTPClient"; |
||||||
|
String _base64Authorization; |
||||||
|
|
||||||
|
/// Response handling
|
||||||
|
RequestArgument* _currentHeaders = nullptr; |
||||||
|
size_t _headerKeysCount = 0; |
||||||
|
|
||||||
|
int _returnCode = 0; |
||||||
|
int _size = -1; |
||||||
|
bool _canReuse = false; |
||||||
|
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY; |
||||||
|
std::unique_ptr<StreamString> _payload; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ESP8266HTTPClient_H_ */ |
@ -0,0 +1,123 @@ |
|||||||
|
/*
|
||||||
|
SecureBearSSLUpdater - SSL encrypted, password-protected firmware update |
||||||
|
|
||||||
|
This example starts a HTTPS server on the ESP8266 to allow firmware updates |
||||||
|
to be performed. All communication, including the username and password, |
||||||
|
is encrypted via SSL. Be sure to update the SSID and PASSWORD before running |
||||||
|
to allow connection to your WiFi network. |
||||||
|
|
||||||
|
To upload through terminal you can use: |
||||||
|
curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware |
||||||
|
|
||||||
|
Adapted by Earle F. Philhower, III, from the SecureWebUpdater.ino example. |
||||||
|
This example is released into the public domain. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServerSecure.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ESP8266HTTPUpdateServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-webupdate"; |
||||||
|
const char* update_path = "/firmware"; |
||||||
|
const char* update_username = "admin"; |
||||||
|
const char* update_password = "admin"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
BearSSL::ESP8266WebServerSecure httpServer(443); |
||||||
|
ESP8266HTTPUpdateServer httpUpdater; |
||||||
|
|
||||||
|
static const char serverCert[] PROGMEM = R"EOF( |
||||||
|
-----BEGIN CERTIFICATE----- |
||||||
|
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC |
||||||
|
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx |
||||||
|
EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w |
||||||
|
HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX |
||||||
|
DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh |
||||||
|
dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI |
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y |
||||||
|
X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj |
||||||
|
oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI |
||||||
|
t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO |
||||||
|
S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy |
||||||
|
+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq |
||||||
|
hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM |
||||||
|
E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb |
||||||
|
fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC |
||||||
|
JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m |
||||||
|
+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA |
||||||
|
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg== |
||||||
|
-----END CERTIFICATE----- |
||||||
|
)EOF"; |
||||||
|
|
||||||
|
static const char serverKey[] PROGMEM = R"EOF( |
||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||||
|
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z |
||||||
|
uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf |
||||||
|
zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+ |
||||||
|
ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh |
||||||
|
BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8 |
||||||
|
djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T |
||||||
|
yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M |
||||||
|
q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr |
||||||
|
eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN |
||||||
|
d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn |
||||||
|
geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y |
||||||
|
84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx |
||||||
|
/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim |
||||||
|
RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu |
||||||
|
DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg |
||||||
|
rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW |
||||||
|
YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK |
||||||
|
iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X |
||||||
|
jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ |
||||||
|
zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV |
||||||
|
kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt |
||||||
|
/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO |
||||||
|
j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg |
||||||
|
gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= |
||||||
|
-----END RSA PRIVATE KEY----- |
||||||
|
)EOF"; |
||||||
|
|
||||||
|
|
||||||
|
void setup() |
||||||
|
{ |
||||||
|
|
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Booting Sketch..."); |
||||||
|
WiFi.mode(WIFI_AP_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while(WiFi.waitForConnectResult() != WL_CONNECTED){ |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println("WiFi failed, retrying."); |
||||||
|
} |
||||||
|
|
||||||
|
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
|
||||||
|
httpServer.setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey)); |
||||||
|
httpUpdater.setup(&httpServer, update_path, update_username, update_password); |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() |
||||||
|
{ |
||||||
|
httpServer.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,192 @@ |
|||||||
|
/*
|
||||||
|
SecureHTTPSUpdater - SSL encrypted, password-protected firmware update |
||||||
|
|
||||||
|
This example starts a HTTPS server on the ESP8266 to allow firmware updates |
||||||
|
to be performed. All communication, including the username and password, |
||||||
|
is encrypted via SSL. Be sure to update the SSID and PASSWORD before running |
||||||
|
to allow connection to your WiFi network. |
||||||
|
|
||||||
|
IMPORTANT NOTES ABOUT SSL CERTIFICATES |
||||||
|
|
||||||
|
1. USE/GENERATE YOUR OWN CERTIFICATES |
||||||
|
While a sample, self-signed certificate is included in this example, |
||||||
|
it is ABSOLUTELY VITAL that you use your own SSL certificate in any |
||||||
|
real-world deployment. Anyone with the certificate and key may be |
||||||
|
able to decrypt your traffic, so your own keys should be kept in a |
||||||
|
safe manner, not accessible on any public network. |
||||||
|
|
||||||
|
2. HOW TO GENERATE YOUR OWN CERTIFICATE/KEY PAIR |
||||||
|
A sample script, "make-self-signed-cert.sh" is provided in the |
||||||
|
ESP8266WiFi/examples/WiFiHTTPSServer directory. This script can be |
||||||
|
modified (replace "your-name-here" with your Organization name). Note |
||||||
|
that this will be a *self-signed certificate* and will *NOT* be accepted |
||||||
|
by default by most modern browsers. They'll display something like, |
||||||
|
"This certificate is from an untrusted source," or "Your connection is |
||||||
|
not secure," or "Your connection is not private," and the user will |
||||||
|
have to manully allow the browser to continue by using the |
||||||
|
"Advanced/Add Exception" (FireFox) or "Advanced/Proceed" (Chrome) link. |
||||||
|
|
||||||
|
You may also, of course, use a commercial, trusted SSL provider to |
||||||
|
generate your certificate. When requesting the certificate, you'll |
||||||
|
need to specify that it use SHA256 and 1024 or 512 bits in order to |
||||||
|
function with the axTLS implementation in the ESP8266. |
||||||
|
|
||||||
|
Interactive usage: |
||||||
|
Go to https://esp8266-webupdate.local/firmware, enter the username
|
||||||
|
and password, and the select a new BIN to upload. |
||||||
|
|
||||||
|
To upload through terminal you can use: |
||||||
|
curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware |
||||||
|
|
||||||
|
Adapted by Earle F. Philhower, III, from the SecureWebUpdater.ino example. |
||||||
|
This example is released into the public domain. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServerSecure.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ESP8266HTTPUpdateServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-webupdate"; |
||||||
|
const char* update_path = "/firmware"; |
||||||
|
const char* update_username = "admin"; |
||||||
|
const char* update_password = "admin"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServerSecure httpServer(443); |
||||||
|
ESP8266HTTPUpdateServer httpUpdater; |
||||||
|
|
||||||
|
// The certificate is stored in PMEM
|
||||||
|
static const uint8_t x509[] PROGMEM = { |
||||||
|
0x30, 0x82, 0x01, 0xc9, 0x30, 0x82, 0x01, 0x32, 0x02, 0x09, 0x00, 0xe6, |
||||||
|
0x60, 0x8d, 0xa3, 0x47, 0x8f, 0x57, 0x7a, 0x30, 0x0d, 0x06, 0x09, 0x2a, |
||||||
|
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x29, |
||||||
|
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x70, |
||||||
|
0x73, 0x79, 0x63, 0x68, 0x6f, 0x70, 0x6c, 0x75, 0x67, 0x31, 0x12, 0x30, |
||||||
|
0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, |
||||||
|
0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, |
||||||
|
0x32, 0x32, 0x34, 0x30, 0x38, 0x30, 0x35, 0x33, 0x36, 0x5a, 0x17, 0x0d, |
||||||
|
0x33, 0x30, 0x31, 0x31, 0x30, 0x33, 0x30, 0x38, 0x30, 0x35, 0x33, 0x36, |
||||||
|
0x5a, 0x30, 0x29, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, |
||||||
|
0x0c, 0x0a, 0x70, 0x73, 0x79, 0x63, 0x68, 0x6f, 0x70, 0x6c, 0x75, 0x67, |
||||||
|
0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x31, |
||||||
|
0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9f, 0x30, |
||||||
|
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, |
||||||
|
0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, |
||||||
|
0x00, 0xb6, 0x59, 0xd0, 0x57, 0xbc, 0x3e, 0xb9, 0xa0, 0x6c, 0xf5, 0xd5, |
||||||
|
0x46, 0x49, 0xaa, 0x9a, 0xb3, 0xbf, 0x09, 0xa9, 0xbb, 0x82, 0x3b, 0xdf, |
||||||
|
0xb7, 0xe3, 0x5a, 0x8e, 0x31, 0xf7, 0x27, 0xdf, 0xaa, 0xed, 0xa3, 0xd6, |
||||||
|
0xf6, 0x74, 0x35, 0xfc, 0x8d, 0x0b, 0xbc, 0xa2, 0x96, 0x10, 0x57, 0xe8, |
||||||
|
0xb2, 0xaa, 0x94, 0xf2, 0x47, 0x12, 0x4e, 0x3f, 0x7c, 0x5e, 0x90, 0xfe, |
||||||
|
0xad, 0x75, 0x88, 0xca, 0x7b, 0x9a, 0x18, 0x15, 0xbe, 0x3d, 0xe0, 0x31, |
||||||
|
0xb5, 0x45, 0x7f, 0xe7, 0x9d, 0x22, 0x99, 0x65, 0xba, 0x63, 0x70, 0x81, |
||||||
|
0x3b, 0x37, 0x22, 0x97, 0x64, 0xc5, 0x57, 0x8c, 0x98, 0x9c, 0x10, 0x36, |
||||||
|
0x98, 0xf0, 0x0b, 0x19, 0x28, 0x16, 0x9a, 0x40, 0x31, 0x5f, 0xbc, 0xd9, |
||||||
|
0x8e, 0x73, 0x68, 0xe1, 0x6a, 0x5d, 0x91, 0x0b, 0x4f, 0x73, 0xa4, 0x6b, |
||||||
|
0x8f, 0xa5, 0xad, 0x12, 0x09, 0x32, 0xa7, 0x66, 0x3b, 0x02, 0x03, 0x01, |
||||||
|
0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, |
||||||
|
0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x1b, 0x46, 0x78, |
||||||
|
0xd1, 0xfa, 0x21, 0xc1, 0xd6, 0x75, 0xc0, 0x83, 0x59, 0x57, 0x05, 0xd5, |
||||||
|
0xae, 0xf8, 0x8c, 0x78, 0x03, 0x65, 0x3b, 0xbf, 0xef, 0x70, 0x3f, 0x78, |
||||||
|
0xc6, 0xe1, 0x5a, 0xac, 0xb1, 0x93, 0x5b, 0x41, 0x35, 0x45, 0x47, 0xf8, |
||||||
|
0x07, 0x86, 0x40, 0x34, 0xa2, 0x9e, 0x2a, 0x16, 0x8d, 0xea, 0xf9, 0x1e, |
||||||
|
0x1f, 0xd7, 0x70, 0xb4, 0x28, 0x6b, 0xd8, 0xf5, 0x3f, 0x33, 0x3f, 0xc2, |
||||||
|
0x2c, 0x69, 0xf2, 0xa3, 0x54, 0x4d, 0xbf, 0x7d, 0xf9, 0xde, 0x05, 0x0c, |
||||||
|
0x9c, 0xe3, 0x1b, 0x72, 0x07, 0x7b, 0x41, 0x76, 0x1a, 0x57, 0x03, 0x5d, |
||||||
|
0xb2, 0xff, 0x4c, 0x17, 0xbd, 0xd7, 0x73, 0x32, 0x98, 0x26, 0x6b, 0x2c, |
||||||
|
0xc4, 0xbf, 0x6e, 0x01, 0x36, 0x8b, 0xbf, 0x00, 0x48, 0x9c, 0xfb, 0x3d, |
||||||
|
0x7d, 0x76, 0x1f, 0x55, 0x96, 0x43, 0xc5, 0x4e, 0xc1, 0xa3, 0xa1, 0x6a, |
||||||
|
0x94, 0x5f, 0x84, 0x3a, 0xdd |
||||||
|
}; |
||||||
|
|
||||||
|
// And so is the key. These could also be in DRAM
|
||||||
|
static const uint8_t rsakey[] PROGMEM = { |
||||||
|
0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xb6, |
||||||
|
0x59, 0xd0, 0x57, 0xbc, 0x3e, 0xb9, 0xa0, 0x6c, 0xf5, 0xd5, 0x46, 0x49, |
||||||
|
0xaa, 0x9a, 0xb3, 0xbf, 0x09, 0xa9, 0xbb, 0x82, 0x3b, 0xdf, 0xb7, 0xe3, |
||||||
|
0x5a, 0x8e, 0x31, 0xf7, 0x27, 0xdf, 0xaa, 0xed, 0xa3, 0xd6, 0xf6, 0x74, |
||||||
|
0x35, 0xfc, 0x8d, 0x0b, 0xbc, 0xa2, 0x96, 0x10, 0x57, 0xe8, 0xb2, 0xaa, |
||||||
|
0x94, 0xf2, 0x47, 0x12, 0x4e, 0x3f, 0x7c, 0x5e, 0x90, 0xfe, 0xad, 0x75, |
||||||
|
0x88, 0xca, 0x7b, 0x9a, 0x18, 0x15, 0xbe, 0x3d, 0xe0, 0x31, 0xb5, 0x45, |
||||||
|
0x7f, 0xe7, 0x9d, 0x22, 0x99, 0x65, 0xba, 0x63, 0x70, 0x81, 0x3b, 0x37, |
||||||
|
0x22, 0x97, 0x64, 0xc5, 0x57, 0x8c, 0x98, 0x9c, 0x10, 0x36, 0x98, 0xf0, |
||||||
|
0x0b, 0x19, 0x28, 0x16, 0x9a, 0x40, 0x31, 0x5f, 0xbc, 0xd9, 0x8e, 0x73, |
||||||
|
0x68, 0xe1, 0x6a, 0x5d, 0x91, 0x0b, 0x4f, 0x73, 0xa4, 0x6b, 0x8f, 0xa5, |
||||||
|
0xad, 0x12, 0x09, 0x32, 0xa7, 0x66, 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, |
||||||
|
0x02, 0x81, 0x81, 0x00, 0xa8, 0x55, 0xf9, 0x33, 0x45, 0x20, 0x52, 0x94, |
||||||
|
0x7a, 0x81, 0xe6, 0xc4, 0xe0, 0x34, 0x92, 0x63, 0xe4, 0xb3, 0xb2, 0xf0, |
||||||
|
0xda, 0xa5, 0x13, 0x3d, 0xda, 0xb0, 0x3a, 0x1c, 0x7e, 0x21, 0x5d, 0x25, |
||||||
|
0x9a, 0x03, 0x69, 0xea, 0x52, 0x15, 0x94, 0x73, 0x50, 0xa6, 0x6f, 0x21, |
||||||
|
0x41, 0x2d, 0x26, 0x2f, 0xe9, 0xb1, 0x5e, 0x87, 0xa5, 0xaa, 0x7e, 0x88, |
||||||
|
0xfd, 0x73, 0xb4, 0xe7, 0xc4, 0x5c, 0xe7, 0x2d, 0xeb, 0x9e, 0x6b, 0xe1, |
||||||
|
0xf1, 0x38, 0x45, 0xf4, 0x10, 0x12, 0xac, 0x79, 0x40, 0x72, 0xf0, 0x45, |
||||||
|
0x89, 0x5c, 0x9d, 0x8b, 0x7b, 0x5d, 0x69, 0xd9, 0x11, 0xf9, 0x25, 0xff, |
||||||
|
0xe1, 0x2a, 0xb3, 0x6d, 0x49, 0x18, 0x8d, 0x38, 0x0a, 0x6f, 0x0f, 0xbd, |
||||||
|
0x48, 0xd0, 0xdd, 0xcb, 0x41, 0x5c, 0x2a, 0x75, 0xa0, 0x51, 0x43, 0x4a, |
||||||
|
0x0b, 0xf6, 0xa2, 0xd2, 0xe9, 0xda, 0x37, 0xca, 0x2d, 0xd7, 0x22, 0x01, |
||||||
|
0x02, 0x41, 0x00, 0xe7, 0x11, 0xea, 0x93, 0xf4, 0x0b, 0xe6, 0xa0, 0x1a, |
||||||
|
0x57, 0x2d, 0xee, 0x96, 0x05, 0x5c, 0xa1, 0x08, 0x8f, 0x9c, 0xac, 0x9a, |
||||||
|
0x72, 0x60, 0x5a, 0x41, 0x2a, 0x92, 0x38, 0x36, 0xa5, 0xfe, 0xb9, 0x35, |
||||||
|
0xb2, 0x06, 0xbb, 0x02, 0x58, 0xc8, 0x93, 0xd6, 0x09, 0x6f, 0x57, 0xd7, |
||||||
|
0xc1, 0x2e, 0x90, 0xb3, 0x09, 0xdd, 0x0c, 0x63, 0x99, 0x91, 0xb7, 0xe4, |
||||||
|
0xcc, 0x6f, 0x78, 0x24, 0xbc, 0x3b, 0x7b, 0x02, 0x41, 0x00, 0xca, 0x06, |
||||||
|
0x4a, 0x09, 0x36, 0x08, 0xaa, 0x27, 0x08, 0x91, 0x86, 0xc5, 0x17, 0x14, |
||||||
|
0x6e, 0x24, 0x9a, 0x86, 0xd1, 0xbc, 0x41, 0xb1, 0x42, 0x5e, 0xe8, 0x80, |
||||||
|
0x5a, 0x8f, 0x7c, 0x9b, 0xe8, 0xcc, 0x28, 0xe1, 0xa2, 0x8f, 0xe9, 0xdc, |
||||||
|
0x60, 0xd5, 0x00, 0x34, 0x76, 0x32, 0x36, 0x00, 0x93, 0x69, 0x6b, 0xab, |
||||||
|
0xc6, 0x8b, 0x70, 0x95, 0x4e, 0xc2, 0x27, 0x4a, 0x24, 0x73, 0xbf, 0xcd, |
||||||
|
0x24, 0x41, 0x02, 0x40, 0x40, 0x46, 0x75, 0x90, 0x0e, 0x54, 0xb9, 0x24, |
||||||
|
0x53, 0xef, 0x68, 0x31, 0x73, 0xbd, 0xae, 0x14, 0x85, 0x43, 0x1d, 0x7b, |
||||||
|
0xcd, 0xc2, 0x7f, 0x16, 0xdc, 0x05, 0xb1, 0x82, 0xbd, 0x80, 0xd3, 0x28, |
||||||
|
0x45, 0xcd, 0x6d, 0x9d, 0xdb, 0x7b, 0x42, 0xe0, 0x0c, 0xab, 0xb7, 0x33, |
||||||
|
0x22, 0x2a, 0xf4, 0x7e, 0xff, 0xae, 0x80, 0xb4, 0x8f, 0x88, 0x0a, 0x46, |
||||||
|
0xb2, 0xf8, 0x43, 0x11, 0x92, 0x76, 0x61, 0xbd, 0x02, 0x40, 0x5c, 0x86, |
||||||
|
0x3a, 0xdc, 0x33, 0x1a, 0x0e, 0xcb, 0xa7, 0xb9, 0xf6, 0xae, 0x47, 0x5e, |
||||||
|
0xbc, 0xff, 0x18, 0xa2, 0x8c, 0x66, 0x1a, 0xf4, 0x13, 0x00, 0xa2, 0x9d, |
||||||
|
0x3e, 0x5c, 0x9e, 0xe6, 0x4c, 0xdd, 0x4c, 0x0f, 0xe2, 0xc2, 0xe4, 0x89, |
||||||
|
0x60, 0xf3, 0xcc, 0x8f, 0x3a, 0x5e, 0xce, 0xaa, 0xbe, 0xd8, 0xb6, 0x4e, |
||||||
|
0x4a, 0xb5, 0x4c, 0x0f, 0xa5, 0xad, 0x78, 0x0f, 0x15, 0xd8, 0xc9, 0x4c, |
||||||
|
0x2b, 0xc1, 0x02, 0x40, 0x4e, 0xe9, 0x78, 0x48, 0x94, 0x11, 0x75, 0xc1, |
||||||
|
0xa2, 0xc7, 0xff, 0xf0, 0x73, 0xa2, 0x93, 0xd7, 0x67, 0xc7, 0xf8, 0x96, |
||||||
|
0xac, 0x15, 0xaa, 0xe5, 0x5d, 0x18, 0x18, 0x29, 0xa9, 0x9a, 0xfc, 0xac, |
||||||
|
0x48, 0x4d, 0xa0, 0xca, 0xa2, 0x34, 0x09, 0x7c, 0x13, 0x22, 0x4c, 0xfc, |
||||||
|
0x31, 0x75, 0xa0, 0x21, 0x1e, 0x7a, 0x91, 0xbc, 0xb1, 0x97, 0xde, 0x43, |
||||||
|
0xe1, 0x40, 0x2b, 0xe3, 0xbd, 0x98, 0x44, 0xad |
||||||
|
}; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
|
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Booting Sketch..."); |
||||||
|
WiFi.mode(WIFI_AP_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println("WiFi failed, retrying."); |
||||||
|
} |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
|
||||||
|
httpServer.setServerKeyAndCert_P(rsakey, sizeof(rsakey), x509, sizeof(x509)); |
||||||
|
httpUpdater.setup(&httpServer, update_path, update_username, update_password); |
||||||
|
httpServer.begin(); |
||||||
|
|
||||||
|
MDNS.addService("https", "tcp", 443); |
||||||
|
Serial.printf("HTTPSUpdateServer 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() { |
||||||
|
httpServer.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
/*
|
||||||
|
To upload through terminal you can use: curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ESP8266HTTPUpdateServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-webupdate"; |
||||||
|
const char* update_path = "/firmware"; |
||||||
|
const char* update_username = "admin"; |
||||||
|
const char* update_password = "admin"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer httpServer(80); |
||||||
|
ESP8266HTTPUpdateServer httpUpdater; |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
|
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Booting Sketch..."); |
||||||
|
WiFi.mode(WIFI_AP_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println("WiFi failed, retrying."); |
||||||
|
} |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
|
||||||
|
httpUpdater.setup(&httpServer, update_path, update_username, update_password); |
||||||
|
httpServer.begin(); |
||||||
|
|
||||||
|
MDNS.addService("http", "tcp", 80); |
||||||
|
Serial.printf("HTTPUpdateServer ready! Open http://%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) { |
||||||
|
httpServer.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/*
|
||||||
|
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ESP8266HTTPUpdateServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-webupdate"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer httpServer(80); |
||||||
|
ESP8266HTTPUpdateServer httpUpdater; |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
|
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Booting Sketch..."); |
||||||
|
WiFi.mode(WIFI_AP_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
|
||||||
|
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println("WiFi failed, retrying."); |
||||||
|
} |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
|
||||||
|
httpUpdater.setup(&httpServer); |
||||||
|
httpServer.begin(); |
||||||
|
|
||||||
|
MDNS.addService("http", "tcp", 80); |
||||||
|
Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
httpServer.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For HTTPUpdateServer |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266HTTPUpdateServer KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
setup KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266HTTPUpdateServer |
||||||
|
version=1.0 |
||||||
|
author=Ivan Grokhotkov, Miguel Ángel Ajo |
||||||
|
maintainer=Ivan Grokhtkov <ivan@esp8266.com> |
||||||
|
sentence=Simple HTTP Update server based on the ESP8266WebServer |
||||||
|
paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,104 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <WiFiServer.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
#include "StreamString.h" |
||||||
|
#include "ESP8266HTTPUpdateServer.h" |
||||||
|
|
||||||
|
|
||||||
|
static const char serverIndex[] PROGMEM = |
||||||
|
R"(<html><body><form method='POST' action='' enctype='multipart/form-data'> |
||||||
|
<input type='file' name='update'> |
||||||
|
<input type='submit' value='Update'> |
||||||
|
</form> |
||||||
|
</body></html>)"; |
||||||
|
static const char successResponse[] PROGMEM =
|
||||||
|
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...\n"; |
||||||
|
|
||||||
|
ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug) |
||||||
|
{ |
||||||
|
_serial_output = serial_debug; |
||||||
|
_server = NULL; |
||||||
|
_username = emptyString; |
||||||
|
_password = emptyString; |
||||||
|
_authenticated = false; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const String& path, const String& username, const String& password) |
||||||
|
{ |
||||||
|
_server = server; |
||||||
|
_username = username; |
||||||
|
_password = password; |
||||||
|
|
||||||
|
// handler for the /update form page
|
||||||
|
_server->on(path.c_str(), HTTP_GET, [&](){ |
||||||
|
if(_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str())) |
||||||
|
return _server->requestAuthentication(); |
||||||
|
_server->send_P(200, PSTR("text/html"), serverIndex); |
||||||
|
}); |
||||||
|
|
||||||
|
// handler for the /update form POST (once file upload finishes)
|
||||||
|
_server->on(path.c_str(), HTTP_POST, [&](){ |
||||||
|
if(!_authenticated) |
||||||
|
return _server->requestAuthentication(); |
||||||
|
if (Update.hasError()) { |
||||||
|
_server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError); |
||||||
|
} else { |
||||||
|
_server->client().setNoDelay(true); |
||||||
|
_server->send_P(200, PSTR("text/html"), successResponse); |
||||||
|
delay(100); |
||||||
|
_server->client().stop(); |
||||||
|
ESP.restart(); |
||||||
|
} |
||||||
|
},[&](){ |
||||||
|
// handler for the file upload, get's the sketch bytes, and writes
|
||||||
|
// them through the Update object
|
||||||
|
HTTPUpload& upload = _server->upload(); |
||||||
|
|
||||||
|
if(upload.status == UPLOAD_FILE_START){ |
||||||
|
_updaterError = String(); |
||||||
|
if (_serial_output) |
||||||
|
Serial.setDebugOutput(true); |
||||||
|
|
||||||
|
_authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str())); |
||||||
|
if(!_authenticated){ |
||||||
|
if (_serial_output) |
||||||
|
Serial.printf("Unauthenticated Update\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
WiFiUDP::stopAll(); |
||||||
|
if (_serial_output) |
||||||
|
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
|
||||||
|
_setUpdaterError(); |
||||||
|
} |
||||||
|
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){ |
||||||
|
if (_serial_output) Serial.printf("."); |
||||||
|
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ |
||||||
|
_setUpdaterError(); |
||||||
|
} |
||||||
|
} 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); |
||||||
|
} else { |
||||||
|
_setUpdaterError(); |
||||||
|
} |
||||||
|
if (_serial_output) Serial.setDebugOutput(false); |
||||||
|
} else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){ |
||||||
|
Update.end(); |
||||||
|
if (_serial_output) Serial.println("Update was aborted"); |
||||||
|
} |
||||||
|
delay(0); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266HTTPUpdateServer::_setUpdaterError() |
||||||
|
{ |
||||||
|
if (_serial_output) Update.printError(Serial); |
||||||
|
StreamString str; |
||||||
|
Update.printError(str); |
||||||
|
_updaterError = str.c_str(); |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
#ifndef __HTTP_UPDATE_SERVER_H |
||||||
|
#define __HTTP_UPDATE_SERVER_H |
||||||
|
|
||||||
|
class ESP8266WebServer; |
||||||
|
|
||||||
|
class ESP8266HTTPUpdateServer |
||||||
|
{ |
||||||
|
public: |
||||||
|
ESP8266HTTPUpdateServer(bool serial_debug=false); |
||||||
|
|
||||||
|
void setup(ESP8266WebServer *server) |
||||||
|
{ |
||||||
|
setup(server, emptyString, emptyString); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(ESP8266WebServer *server, const String& path) |
||||||
|
{ |
||||||
|
setup(server, path, emptyString, emptyString); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(ESP8266WebServer *server, const String& username, const String& password) |
||||||
|
{ |
||||||
|
setup(server, "/update", username, password); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(ESP8266WebServer *server, const String& path, const String& username, const String& password); |
||||||
|
|
||||||
|
void updateCredentials(const String& username, const String& password) |
||||||
|
{ |
||||||
|
_username = username; |
||||||
|
_password = password; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
void _setUpdaterError(); |
||||||
|
|
||||||
|
private: |
||||||
|
bool _serial_output; |
||||||
|
ESP8266WebServer *_server; |
||||||
|
String _username; |
||||||
|
String _password; |
||||||
|
bool _authenticated; |
||||||
|
String _updaterError; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,287 @@ |
|||||||
|
/*
|
||||||
|
* ESP8266 LLMNR responder |
||||||
|
* Copyright (C) 2017 Stephen Warren <swarren@wwwdotorg.org> |
||||||
|
* |
||||||
|
* Based on: |
||||||
|
* 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 |
||||||
|
* Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) |
||||||
|
* |
||||||
|
* License (MIT license): |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in |
||||||
|
* all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
* |
||||||
|
* Reference: |
||||||
|
* https://tools.ietf.org/html/rfc4795 (LLMNR)
|
||||||
|
* https://tools.ietf.org/html/rfc1035 (DNS)
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <debug.h> |
||||||
|
#include <functional> |
||||||
|
#include <ESP8266LLMNR.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include <user_interface.h> |
||||||
|
} |
||||||
|
|
||||||
|
#include <lwip/udp.h> |
||||||
|
#include <lwip/igmp.h> |
||||||
|
#include <include/UdpContext.h> |
||||||
|
|
||||||
|
//#define LLMNR_DEBUG
|
||||||
|
|
||||||
|
//BIT(x) is defined in tools/sdk/c_types.h
|
||||||
|
|
||||||
|
#define FLAGS_QR BIT(15) |
||||||
|
#define FLAGS_OP_SHIFT 11 |
||||||
|
#define FLAGS_OP_MASK 0xf |
||||||
|
#define FLAGS_C BIT(10) |
||||||
|
#define FLAGS_TC BIT(9) |
||||||
|
#define FLAGS_T BIT(8) |
||||||
|
#define FLAGS_RCODE_SHIFT 0 |
||||||
|
#define FLAGS_RCODE_MASK 0xf |
||||||
|
|
||||||
|
#define _conn_read16() (((uint16_t)_conn->read() << 8) | _conn->read()) |
||||||
|
#define _conn_read8() _conn->read() |
||||||
|
#define _conn_readS(b, l) _conn->read((b), (l)); |
||||||
|
|
||||||
|
// llmnr ipv6 is FF02:0:0:0:0:0:1:3
|
||||||
|
// lwip-v2's igmp_joingroup only supports IPv4
|
||||||
|
#define LLMNR_MULTICAST_ADDR 224, 0, 0, 252 |
||||||
|
static const int LLMNR_MULTICAST_TTL = 1; |
||||||
|
static const int LLMNR_PORT = 5355; |
||||||
|
|
||||||
|
LLMNRResponder::LLMNRResponder() : |
||||||
|
_conn(0) { |
||||||
|
} |
||||||
|
|
||||||
|
LLMNRResponder::~LLMNRResponder() { |
||||||
|
if (_conn) |
||||||
|
_conn->unref(); |
||||||
|
} |
||||||
|
|
||||||
|
bool LLMNRResponder::begin(const char* hostname) { |
||||||
|
// Max length for a single label in DNS
|
||||||
|
if (strlen(hostname) > 63) |
||||||
|
return false; |
||||||
|
|
||||||
|
_hostname = hostname; |
||||||
|
_hostname.toLowerCase(); |
||||||
|
|
||||||
|
_sta_got_ip_handler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& event){ |
||||||
|
(void) event; |
||||||
|
_restart(); |
||||||
|
}); |
||||||
|
|
||||||
|
_sta_disconnected_handler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& event) { |
||||||
|
(void) event; |
||||||
|
_restart(); |
||||||
|
}); |
||||||
|
|
||||||
|
return _restart(); |
||||||
|
} |
||||||
|
|
||||||
|
void LLMNRResponder::notify_ap_change() { |
||||||
|
_restart(); |
||||||
|
} |
||||||
|
|
||||||
|
bool LLMNRResponder::_restart() { |
||||||
|
if (_conn) { |
||||||
|
_conn->unref(); |
||||||
|
_conn = 0; |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress llmnr(LLMNR_MULTICAST_ADDR); |
||||||
|
|
||||||
|
if (igmp_joingroup(IP4_ADDR_ANY4, llmnr) != ERR_OK) |
||||||
|
return false; |
||||||
|
|
||||||
|
_conn = new UdpContext; |
||||||
|
_conn->ref(); |
||||||
|
|
||||||
|
if (!_conn->listen(IP_ADDR_ANY, LLMNR_PORT)) |
||||||
|
return false; |
||||||
|
|
||||||
|
_conn->setMulticastTTL(LLMNR_MULTICAST_TTL); |
||||||
|
_conn->onRx(std::bind(&LLMNRResponder::_process_packet, this)); |
||||||
|
_conn->connect(llmnr, LLMNR_PORT); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void LLMNRResponder::_process_packet() { |
||||||
|
if (!_conn || !_conn->next()) |
||||||
|
return; |
||||||
|
|
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("LLMNR: RX'd packet"); |
||||||
|
#endif |
||||||
|
|
||||||
|
uint16_t id = _conn_read16(); |
||||||
|
uint16_t flags = _conn_read16(); |
||||||
|
uint16_t qdcount = _conn_read16(); |
||||||
|
uint16_t ancount = _conn_read16(); |
||||||
|
uint16_t nscount = _conn_read16(); |
||||||
|
uint16_t arcount = _conn_read16(); |
||||||
|
|
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.print("LLMNR: ID="); |
||||||
|
Serial.println(id, HEX); |
||||||
|
Serial.print("LLMNR: FLAGS="); |
||||||
|
Serial.println(flags, HEX); |
||||||
|
Serial.print("LLMNR: QDCOUNT="); |
||||||
|
Serial.println(qdcount); |
||||||
|
Serial.print("LLMNR: ANCOUNT="); |
||||||
|
Serial.println(ancount); |
||||||
|
Serial.print("LLMNR: NSCOUNT="); |
||||||
|
Serial.println(nscount); |
||||||
|
Serial.print("LLMNR: ARCOUNT="); |
||||||
|
Serial.println(arcount); |
||||||
|
#endif |
||||||
|
|
||||||
|
#define BAD_FLAGS (FLAGS_QR | (FLAGS_OP_MASK << FLAGS_OP_SHIFT) | FLAGS_C) |
||||||
|
if (flags & BAD_FLAGS) { |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("Bad flags"); |
||||||
|
#endif |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (qdcount != 1) { |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("QDCOUNT != 1"); |
||||||
|
#endif |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (ancount || nscount || arcount) { |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("AN/NS/AR-COUNT != 0"); |
||||||
|
#endif |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t namelen = _conn_read8(); |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.print("QNAME len "); |
||||||
|
Serial.println(namelen); |
||||||
|
#endif |
||||||
|
if (namelen != _hostname.length()) { |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("QNAME len mismatch"); |
||||||
|
#endif |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
char qname[64]; |
||||||
|
_conn_readS(qname, namelen); |
||||||
|
_conn_read8(); |
||||||
|
qname[namelen] = '\0'; |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.print("QNAME "); |
||||||
|
Serial.println(qname); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (strcmp(_hostname.c_str(), qname)) { |
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("QNAME mismatch"); |
||||||
|
#endif |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t qtype = _conn_read16(); |
||||||
|
uint16_t qclass = _conn_read16(); |
||||||
|
|
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.print("QTYPE "); |
||||||
|
Serial.print(qtype); |
||||||
|
Serial.print(" QCLASS "); |
||||||
|
Serial.println(qclass); |
||||||
|
#endif |
||||||
|
|
||||||
|
bool have_rr = |
||||||
|
(qtype == 1) && /* A */ |
||||||
|
(qclass == 1); /* IN */ |
||||||
|
|
||||||
|
_conn->flush(); |
||||||
|
|
||||||
|
#ifdef LLMNR_DEBUG |
||||||
|
Serial.println("Match; responding"); |
||||||
|
if (!have_rr) |
||||||
|
Serial.println("(no matching RRs)"); |
||||||
|
#endif |
||||||
|
|
||||||
|
IPAddress remote_ip = _conn->getRemoteAddress(); |
||||||
|
|
||||||
|
struct ip_info ip_info; |
||||||
|
bool match_ap = false; |
||||||
|
if (wifi_get_opmode() & SOFTAP_MODE) { |
||||||
|
wifi_get_ip_info(SOFTAP_IF, &ip_info); |
||||||
|
IPAddress infoIp(ip_info.ip); |
||||||
|
IPAddress infoMask(ip_info.netmask); |
||||||
|
if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) |
||||||
|
match_ap = true; |
||||||
|
} |
||||||
|
if (!match_ap) |
||||||
|
wifi_get_ip_info(STATION_IF, &ip_info); |
||||||
|
uint32_t ip = ip_info.ip.addr; |
||||||
|
|
||||||
|
// Header
|
||||||
|
uint8_t header[] = { |
||||||
|
(uint8_t)(id >> 8), (uint8_t)(id & 0xff), // ID
|
||||||
|
(uint8_t)(FLAGS_QR >> 8), 0, // FLAGS
|
||||||
|
0, 1, // QDCOUNT
|
||||||
|
0, !!have_rr, // ANCOUNT
|
||||||
|
0, 0, // NSCOUNT
|
||||||
|
0, 0, // ARCOUNT
|
||||||
|
}; |
||||||
|
_conn->append(reinterpret_cast<const char*>(header), sizeof(header)); |
||||||
|
// Question
|
||||||
|
_conn->append(reinterpret_cast<const char*>(&namelen), 1); |
||||||
|
_conn->append(qname, namelen); |
||||||
|
uint8_t q[] = { |
||||||
|
0, // Name terminator
|
||||||
|
0, 1, // TYPE (A)
|
||||||
|
0, 1, // CLASS (IN)
|
||||||
|
}; |
||||||
|
_conn->append(reinterpret_cast<const char*>(q), sizeof(q)); |
||||||
|
// Answer, if we have one
|
||||||
|
if (have_rr) { |
||||||
|
_conn->append(reinterpret_cast<const char*>(&namelen), 1); |
||||||
|
_conn->append(qname, namelen); |
||||||
|
uint8_t rr[] = { |
||||||
|
0, // Name terminator
|
||||||
|
0, 1, // TYPE (A)
|
||||||
|
0, 1, // CLASS (IN)
|
||||||
|
0, 0, 0, 30, // TTL (30 seconds)
|
||||||
|
0, 4, // RDLENGTH
|
||||||
|
(uint8_t)(ip & 0xff), (uint8_t)((ip >> 8) & 0xff), (uint8_t)((ip >> 16) & 0xff), (uint8_t)((ip >> 24) & 0xff) // RDATA
|
||||||
|
}; |
||||||
|
_conn->append(reinterpret_cast<const char*>(rr), sizeof(rr)); |
||||||
|
} |
||||||
|
_conn->setMulticastInterface(remote_ip); |
||||||
|
_conn->send(remote_ip, _conn->getRemotePort()); |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LLMNR) |
||||||
|
LLMNRResponder LLMNR; |
||||||
|
#endif |
@ -0,0 +1,66 @@ |
|||||||
|
/*
|
||||||
|
* ESP8266 LLMNR responder |
||||||
|
* Copyright (C) 2017 Stephen Warren <swarren@wwwdotorg.org> |
||||||
|
* |
||||||
|
* Based on: |
||||||
|
* 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 |
||||||
|
* Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) |
||||||
|
* |
||||||
|
* License (MIT license): |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in |
||||||
|
* all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ESP8266LLMNR_H |
||||||
|
#define ESP8266LLMNR_H |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
|
||||||
|
class UdpContext; |
||||||
|
|
||||||
|
class LLMNRResponder { |
||||||
|
public: |
||||||
|
LLMNRResponder(); |
||||||
|
~LLMNRResponder(); |
||||||
|
|
||||||
|
/* Initialize and start responding to LLMNR requests on all interfaces */ |
||||||
|
bool begin(const char* hostname); |
||||||
|
|
||||||
|
/* Application should call this whenever AP is configured/disabled */ |
||||||
|
void notify_ap_change(); |
||||||
|
|
||||||
|
private: |
||||||
|
String _hostname; |
||||||
|
UdpContext *_conn; |
||||||
|
WiFiEventHandler _sta_got_ip_handler; |
||||||
|
WiFiEventHandler _sta_disconnected_handler; |
||||||
|
|
||||||
|
bool _restart(); |
||||||
|
void _process_packet(); |
||||||
|
}; |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LLMNR) |
||||||
|
extern LLMNRResponder LLMNR; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,92 @@ |
|||||||
|
ESP8266 LLMNR (Link-Local Multicast Name Resolution) |
||||||
|
==================================================== |
||||||
|
|
||||||
|
This is a simple implementation of an LLMNR responder the ESP8266 Arduino |
||||||
|
package. Only support for advertizing a single hostname is currently |
||||||
|
implemented. |
||||||
|
|
||||||
|
LLMNR is a very similar protocol to MDNS. The primary practical difference is |
||||||
|
that Windows systems (at least Windows 7 and later; perhaps earlier) support |
||||||
|
the protocol out-of-the-box, whereas additional software is required to support |
||||||
|
MDNS. However, Linux support is currently more complex, and MacOS X support |
||||||
|
appears non-existent. |
||||||
|
|
||||||
|
Requirements |
||||||
|
------------ |
||||||
|
- ESP8266WiFi library |
||||||
|
- LLMNR support in your operating system/client machines: |
||||||
|
- For Windows, support is already built in (in Windows 7 at least). |
||||||
|
- For Linux, the systemd-resolve application supports LLMNR. |
||||||
|
- For Mac OSX: Unknown; likely not supported. |
||||||
|
|
||||||
|
Usage |
||||||
|
----- |
||||||
|
1. Install this repository using the instructions in the top-levle README.md |
||||||
|
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. |
||||||
|
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. |
||||||
|
|
||||||
|
See the included LLMNR + HTTP server sketch for a full example. |
||||||
|
|
||||||
|
References |
||||||
|
---------- |
||||||
|
1. https://tools.ietf.org/html/rfc4795 (LLMNR) |
||||||
|
2. https://tools.ietf.org/html/rfc1035 (DNS) |
||||||
|
|
||||||
|
Caveats |
||||||
|
------- |
||||||
|
1. LLMNR implementations MUST support EDNS0 [RFC2671] and extended RCODE |
||||||
|
values. It is likely that this implementation does not; I have not read |
||||||
|
that RFC. |
||||||
|
2. LLMNR responders MUST support listening for TCP queries. This implementation |
||||||
|
does not. |
||||||
|
3. On receiving an LLMNR query, the responder MUST check whether it was sent to |
||||||
|
an LLMNR multicast addresses defined in Section 2. If it was sent to another |
||||||
|
multicast address, then the query MUST be silently discarded. This |
||||||
|
implementation makes no such check; it is hoped that the ESP8266 network |
||||||
|
stack filters out such packets since the code only joins the relevant |
||||||
|
multicast group and does not listen for unicast packets. This assumption may |
||||||
|
be invalid. |
||||||
|
4. Prior to sending an LLMNR response with the 'T' bit clear, a responder |
||||||
|
configured with a UNIQUE name MUST verify that there is no other host within |
||||||
|
the scope of LLMNR query propagation that is authoritative for the same name |
||||||
|
on that interface. This implementation performs no such verification. |
||||||
|
5. Prior to verifying that its name is UNIQUE, a responder MUST set the 'T' bit |
||||||
|
in responses. This implementation does not; it assumes that name is unique |
||||||
|
and responds with the 'T' bit clear in all cases. |
||||||
|
6. To verify uniqueness, a responder MUST send an LLMNR query with the 'C' bit |
||||||
|
clear, over all protocols on which it responds to LLMNR queries (IPv4 and/or |
||||||
|
IPv6). This implementation does not. |
||||||
|
|
||||||
|
License |
||||||
|
------- |
||||||
|
Copyright (C) 2017 Stephen Warren <swarren@wwwdotorg.org> |
||||||
|
|
||||||
|
Based on: |
||||||
|
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 |
||||||
|
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
@ -0,0 +1,112 @@ |
|||||||
|
/*
|
||||||
|
ESP8266 LLMNR responder sample |
||||||
|
Copyright (C) 2017 Stephen Warren <swarren@wwwdotorg.org> |
||||||
|
|
||||||
|
Based on: |
||||||
|
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 |
||||||
|
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) |
||||||
|
|
||||||
|
License (MIT license): |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
||||||
|
|
||||||
|
*/ |
||||||
|
/*
|
||||||
|
This is an example of an HTTP server that is accessible via http://esp8266/
|
||||||
|
(or perhaps http://esp8266.local/) thanks to the LLMNR responder.
|
||||||
|
|
||||||
|
Instructions: |
||||||
|
- Update WiFi SSID and password as necessary. |
||||||
|
- Flash the sketch to the ESP8266 board. |
||||||
|
- Windows: |
||||||
|
- No additional software is necessary. |
||||||
|
- Point your browser to http://esp8266/, you should see a response. In most
|
||||||
|
cases, it is important that you manually type the "http://" to force the |
||||||
|
browser to search for a hostname to connect to, rather than perform a web |
||||||
|
search. |
||||||
|
- Alternatively, run the following command from the command prompt: |
||||||
|
ping esp8266 |
||||||
|
- Linux: |
||||||
|
- To validate LLMNR, install the systemd-resolve utility. |
||||||
|
- Execute the following command: |
||||||
|
systemd-resolve -4 -p llmnr esp8266 |
||||||
|
- It may be possible to configure your system to use LLMNR for all name |
||||||
|
lookups. However, that is beyond the scope of this description. |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266LLMNR.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer web_server(80); |
||||||
|
|
||||||
|
void handle_http_not_found() { |
||||||
|
web_server.send(404, "text/plain", "Not Found"); |
||||||
|
} |
||||||
|
|
||||||
|
void handle_http_root() { |
||||||
|
web_server.send(200, "text/plain", "It works!"); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
Serial.begin(115200); |
||||||
|
|
||||||
|
// Connect to WiFi network
|
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
// Start LLMNR responder
|
||||||
|
LLMNR.begin("esp8266"); |
||||||
|
Serial.println("LLMNR responder started"); |
||||||
|
// Start HTTP server
|
||||||
|
web_server.onNotFound(handle_http_not_found); |
||||||
|
web_server.on("/", handle_http_root); |
||||||
|
web_server.begin(); |
||||||
|
Serial.println("HTTP server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
web_server.handleClient(); |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Ultrasound |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266LLMNR KEYWORD1 |
||||||
|
LLMNRResponder KEYWORD1 |
||||||
|
LLMNR KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
notify_ap_change KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266LLMNR |
||||||
|
version=1.0 |
||||||
|
author=swarren@wwwdotorg.org |
||||||
|
maintainer=swarren@wwwdotorg.org |
||||||
|
sentence=ESP8266 LLMNR (Link-Local Multicast Name Resolution) |
||||||
|
paragraph=This is a simple implementation of an LLMNR responder the ESP8266 Arduino package. Only support for advertizing a single hostname is currently implemented. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,279 @@ |
|||||||
|
/* Klient sluzby NBNS
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "ESP8266NetBIOS.h" |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "osapi.h" |
||||||
|
#include "ets_sys.h" |
||||||
|
#include "user_interface.h" |
||||||
|
} |
||||||
|
|
||||||
|
#include "lwip/opt.h" |
||||||
|
#include "lwip/inet.h" |
||||||
|
#include "lwip/udp.h" |
||||||
|
|
||||||
|
#define NBNSQ_TYPE_NB (0x0020) |
||||||
|
#define NBNSQ_CLASS_IN (0x0001) |
||||||
|
#ifndef LWIP_PLATFORM_HTONS |
||||||
|
#define LWIP_PLATFORM_HTONS(_n) ((u16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) |
||||||
|
#endif |
||||||
|
#ifndef LWIP_PLATFORM_HTONL |
||||||
|
#define LWIP_PLATFORM_HTONL(_n) ((u32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) |
||||||
|
#endif |
||||||
|
|
||||||
|
// Definice struktury NBNS dotazu (alespon veci, ktere jsem vypozoroval):
|
||||||
|
struct NBNSQUESTION { |
||||||
|
uint16_t NBNSQ_ID; // ID dotazu
|
||||||
|
uint8_t NBNSQ_FLAGS1; |
||||||
|
uint8_t NBNSQ_FLAGS2; |
||||||
|
uint16_t NBNSQ_QUESTIONCOUNT; |
||||||
|
uint16_t NBNSQ_ANSWERCOUNT; |
||||||
|
uint16_t NBNSQ_AUTHORITYCOUNT; |
||||||
|
uint16_t NBNSQ_ADDITIONALRECORDCOUNT; |
||||||
|
uint8_t NBNSQ_NAMESIZE; // delka nasledujiciho retezce
|
||||||
|
char NBNSQ_NAME[32+1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha
|
||||||
|
uint16_t NBNSQ_TYPE; |
||||||
|
uint16_t NBNSQ_CLASS; |
||||||
|
} __attribute__((packed)); |
||||||
|
|
||||||
|
// Definice struktury NBNS odpovedi (stejne jako u dotazu)
|
||||||
|
struct NBNSANSWER { |
||||||
|
uint16_t NBNSA_ID; // ID dotazu
|
||||||
|
uint8_t NBNSA_FLAGS1; |
||||||
|
uint8_t NBNSA_FLAGS2; |
||||||
|
uint16_t NBNSA_QUESTIONCOUNT; |
||||||
|
uint16_t NBNSA_ANSWERCOUNT; |
||||||
|
uint16_t NBNSA_AUTHORITYCOUNT; |
||||||
|
uint16_t NBNSA_ADDITIONALRECORDCOUNT; |
||||||
|
uint8_t NBNSA_NAMESIZE; // delka nasledujiciho retezce
|
||||||
|
char NBNSA_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha
|
||||||
|
uint16_t NBNSA_TYPE; |
||||||
|
uint16_t NBNSA_CLASS; |
||||||
|
uint32_t NBNSA_TIMETOLIVE; |
||||||
|
uint16_t NBNSA_LENGTH; |
||||||
|
uint16_t NBNSA_NODEFLAGS; // POZOR!!! tady si nejsem moc jisty
|
||||||
|
uint32_t NBNSA_NODEADDRESS; |
||||||
|
} __attribute__((packed)); |
||||||
|
|
||||||
|
// Definice struktury NBNS odpovedi na dotaz na jmeno
|
||||||
|
struct NBNSANSWERN { |
||||||
|
uint16_t NBNSAN_ID; // ID dotazu
|
||||||
|
uint8_t NBNSAN_FLAGS1; |
||||||
|
uint8_t NBNSAN_FLAGS2; |
||||||
|
uint16_t NBNSAN_QUESTIONCOUNT; |
||||||
|
uint16_t NBNSAN_ANSWERCOUNT; |
||||||
|
uint16_t NBNSAN_AUTHORITYCOUNT; |
||||||
|
uint16_t NBNSAN_ADDITIONALRECORDCOUNT; |
||||||
|
uint8_t NBNSAN_NAMESIZE; // delka nasledujiciho retezce
|
||||||
|
char NBNSAN_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha
|
||||||
|
uint16_t NBNSAN_TYPE; |
||||||
|
uint16_t NBNSAN_CLASS; |
||||||
|
uint32_t NBNSAN_TIMETOLIVE; |
||||||
|
uint16_t NBNSAN_LENGTH; |
||||||
|
uint8_t NBNSAN_NUMBER; // number of names
|
||||||
|
char NBNSAN_NNAME[15]; // jmeno nodu
|
||||||
|
uint8_t NBNSAN_NTYPE; // typ jmena
|
||||||
|
uint16_t NBNSAN_NFLAGS; // node flags
|
||||||
|
} __attribute__((packed)); |
||||||
|
|
||||||
|
/** Metoda pro ziskani jmena z kodovani NETBIOS.
|
||||||
|
* \param nbname Ukazatel na jmeno v NETBIOS kodovani. |
||||||
|
* \param name Ukazatel na misto, kam prevadime jmeno. |
||||||
|
* \param maxlen Maximalni pocet znaku v nbname. |
||||||
|
*/ |
||||||
|
void ESP8266NetBIOS::_getnbname(char *nbname, char *name, uint8_t maxlen) |
||||||
|
{ |
||||||
|
uint8_t b; |
||||||
|
uint8_t c = 0; |
||||||
|
|
||||||
|
while ((*nbname != 0x0) && (c < maxlen)) { |
||||||
|
b = (*nbname++ - 'A') << 4; // opravime nibble a prevedeme ho do vyssich bitu
|
||||||
|
c++; // pocitame pocet odebranych bytu
|
||||||
|
if (*nbname != 0x0) { |
||||||
|
b |= *nbname++ - 'A'; // pridame nizsi nibble
|
||||||
|
c++; // opet spocitame pocet odebranych znaku
|
||||||
|
} |
||||||
|
*name++ = b; // ulozime znak do vysledku a posuneme ukazatel
|
||||||
|
} |
||||||
|
*name = 0x0; // ulozime ukoncovaci 0
|
||||||
|
} |
||||||
|
|
||||||
|
/** Prevod zadaneho textu do NETBIOS kodovani
|
||||||
|
* \param name Ukazatel na prevadene jmeno. |
||||||
|
* \param nbname Ukazatel na misto, kam vytvarime jmeno. |
||||||
|
* \param outlen Pocet vystupnich znaku (mimo ukoncovaci 0) musi byt delitelne 2 |
||||||
|
*/ |
||||||
|
void ESP8266NetBIOS::_makenbname(char *name, char *nbname, uint8_t outlen) |
||||||
|
{ |
||||||
|
uint8_t b; |
||||||
|
uint8_t c = 0; |
||||||
|
|
||||||
|
while (c < (outlen - 2)) { |
||||||
|
b = *name; // prevadeny znak
|
||||||
|
if (b) { |
||||||
|
name++; // zatim se posunujeme
|
||||||
|
} else { |
||||||
|
b = 0x20; // konec retezce je nahrazeny mezerou
|
||||||
|
} |
||||||
|
*nbname++ = (b >> 4) + 'A'; // jeden nibble ze znaku
|
||||||
|
*nbname++ = (b & 0xf) + 'A'; // druhy nibble ze znaku
|
||||||
|
c += 2; // pocet prevedenych znaku
|
||||||
|
} |
||||||
|
*nbname++ = 'A'; |
||||||
|
*nbname++ = 'A'; // ulozime ukoncovaci 0 v NBNS kodovani
|
||||||
|
*nbname = 0; // ulozime ukoncovaci 0 retezce
|
||||||
|
} |
||||||
|
|
||||||
|
ESP8266NetBIOS::ESP8266NetBIOS():_pcb(NULL) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
ESP8266NetBIOS::~ESP8266NetBIOS() |
||||||
|
{ |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
// Vytvoreni a otevreni UDP soketu, pokud jeste neni...
|
||||||
|
bool ESP8266NetBIOS::begin(const char *name) |
||||||
|
{ |
||||||
|
size_t n = strlen(name); |
||||||
|
if (n > sizeof(_name)) { |
||||||
|
// prilis dlouhe jmeno
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// presuneme jmeno zarizeni se soucasnou upravou na UPPER case
|
||||||
|
for (size_t i = 0; i < n; ++i) { |
||||||
|
_name[i] = toupper(name[i]); |
||||||
|
} |
||||||
|
_name[n] = '\0'; |
||||||
|
|
||||||
|
if(_pcb != NULL) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
_pcb = udp_new(); |
||||||
|
udp_recv(_pcb, &_s_recv, (void *) this); |
||||||
|
err_t err = udp_bind(_pcb, INADDR_ANY, NBNS_PORT); |
||||||
|
if(err != ERR_OK) { |
||||||
|
end(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266NetBIOS::end() |
||||||
|
{ |
||||||
|
if(_pcb != NULL) { |
||||||
|
udp_remove(_pcb); |
||||||
|
_pcb = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266NetBIOS::_recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port) |
||||||
|
{ |
||||||
|
(void)upcb; |
||||||
|
(void)addr; |
||||||
|
(void)port; |
||||||
|
while(pb != NULL) { |
||||||
|
uint8_t * data = (uint8_t*)((pb)->payload); |
||||||
|
size_t len = pb->len; |
||||||
|
#if LWIP_VERSION_MAJOR == 1 |
||||||
|
// check UdpContext.h
|
||||||
|
ip_addr_t* saddr = ¤t_iphdr_src; |
||||||
|
#else |
||||||
|
// check UdpContext.h
|
||||||
|
const ip_addr_t* saddr = &ip_data.current_iphdr_src; |
||||||
|
#endif |
||||||
|
|
||||||
|
if (len >= sizeof(struct NBNSQUESTION)) { |
||||||
|
struct NBNSQUESTION * question = (struct NBNSQUESTION *)data; |
||||||
|
if (0 == (question->NBNSQ_FLAGS1 & 0x80)) { |
||||||
|
char name[ NBNS_MAX_HOSTNAME_LEN + 1 ]; // dekodovane dotazovane jmeno
|
||||||
|
char *str; // pomocna promenna, pouze pro praci s retezcem
|
||||||
|
|
||||||
|
_getnbname(&question->NBNSQ_NAME[0], (char *)&name, question->NBNSQ_NAMESIZE); // prevedeme dotazovane jmeno
|
||||||
|
if ((str = strchr(name, ' ')) != NULL) { // jmeno hledaneho zarizeni v tomto pripade ukoncuje i mezera
|
||||||
|
*str = '\0'; // ukoncime retezec na vyskytu prvni mezery
|
||||||
|
} |
||||||
|
|
||||||
|
if (0 == strcmp(name, _name)) { |
||||||
|
// dotaz primo na nas
|
||||||
|
struct NBNSANSWER nbnsa; // buffer, do ktereho je sestavena odpoved na dotaz
|
||||||
|
|
||||||
|
nbnsa.NBNSA_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi
|
||||||
|
nbnsa.NBNSA_FLAGS1 = 0x85; // priznak odpovedi
|
||||||
|
nbnsa.NBNSA_FLAGS2 = 0; // vlajky 2 a response code
|
||||||
|
nbnsa.NBNSA_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsa.NBNSA_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi
|
||||||
|
nbnsa.NBNSA_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsa.NBNSA_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsa.NBNSA_NAMESIZE = sizeof(nbnsa.NBNSA_NAME) - 1; // prekopirujeme delku jmena stanice
|
||||||
|
_makenbname(_name, &nbnsa.NBNSA_NAME[0], sizeof(nbnsa.NBNSA_NAME) - 1); // prevedeme jmeno
|
||||||
|
nbnsa.NBNSA_TYPE = LWIP_PLATFORM_HTONS(0x20); // NetBIOS name
|
||||||
|
nbnsa.NBNSA_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name
|
||||||
|
nbnsa.NBNSA_TIMETOLIVE = LWIP_PLATFORM_HTONL(300000UL);// Time to live (30000 sekund)
|
||||||
|
nbnsa.NBNSA_LENGTH = LWIP_PLATFORM_HTONS(6); |
||||||
|
nbnsa.NBNSA_NODEFLAGS = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsa.NBNSA_NODEADDRESS = WiFi.localIP(); // ulozime nasi IP adresu
|
||||||
|
|
||||||
|
pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsa), PBUF_RAM); |
||||||
|
if(pbt != NULL) { |
||||||
|
uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload); |
||||||
|
memcpy(dst, (uint8_t *)&nbnsa, sizeof(nbnsa)); |
||||||
|
udp_sendto(_pcb, pbt, saddr, NBNS_PORT); |
||||||
|
pbuf_free(pbt); |
||||||
|
} |
||||||
|
} else if (0 == strcmp(name, "*")) { |
||||||
|
// obecny dotaz - mireny nejspis na nasi IP adresu
|
||||||
|
struct NBNSANSWERN nbnsan; // buffer, do ktereho je sestavena odpoved na dotaz
|
||||||
|
|
||||||
|
nbnsan.NBNSAN_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi
|
||||||
|
nbnsan.NBNSAN_FLAGS1 = 0x84; // priznak odpovedi
|
||||||
|
nbnsan.NBNSAN_FLAGS2 = 0; // vlajky 2 a response code
|
||||||
|
nbnsan.NBNSAN_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsan.NBNSAN_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi
|
||||||
|
nbnsan.NBNSAN_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsan.NBNSAN_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); |
||||||
|
nbnsan.NBNSAN_NAMESIZE = question->NBNSQ_NAMESIZE; // prekopirujeme delku jmena stanice
|
||||||
|
memcpy(nbnsan.NBNSAN_NAME, question->NBNSQ_NAME, sizeof(nbnsan.NBNSAN_NAME)); // prekopirujeme dotazovane jmeno
|
||||||
|
nbnsan.NBNSAN_TYPE = LWIP_PLATFORM_HTONS(0x21); // NBSTAT
|
||||||
|
nbnsan.NBNSAN_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name
|
||||||
|
nbnsan.NBNSAN_TIMETOLIVE = LWIP_PLATFORM_HTONL(0); |
||||||
|
nbnsan.NBNSAN_LENGTH = LWIP_PLATFORM_HTONS(4 + sizeof(nbnsan.NBNSAN_NNAME)); |
||||||
|
nbnsan.NBNSAN_NUMBER = 1; // Number of names
|
||||||
|
memset(nbnsan.NBNSAN_NNAME, 0x20, sizeof(nbnsan.NBNSAN_NNAME)); |
||||||
|
memcpy(nbnsan.NBNSAN_NNAME, _name, strlen(_name)); |
||||||
|
nbnsan.NBNSAN_NTYPE = 0; // Workstation/Redirector
|
||||||
|
nbnsan.NBNSAN_NFLAGS = LWIP_PLATFORM_HTONS(0x400); // b-node, unique, active
|
||||||
|
|
||||||
|
pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsan), PBUF_RAM); |
||||||
|
if(pbt != NULL) { |
||||||
|
uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload); |
||||||
|
memcpy(dst, (uint8_t *)&nbnsan, sizeof(nbnsan)); |
||||||
|
udp_sendto(_pcb, pbt, saddr, NBNS_PORT); |
||||||
|
pbuf_free(pbt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pbuf * this_pb = pb; |
||||||
|
pb = pb->next; |
||||||
|
this_pb->next = NULL; |
||||||
|
pbuf_free(this_pb); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266NetBIOS::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port) |
||||||
|
{ |
||||||
|
reinterpret_cast<ESP8266NetBIOS*>(arg)->_recv(upcb, p, addr, port); |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) |
||||||
|
ESP8266NetBIOS NBNS; |
||||||
|
#endif |
||||||
|
|
||||||
|
// EOF
|
@ -0,0 +1,45 @@ |
|||||||
|
//
|
||||||
|
#ifndef __ESPNBNS_h__ |
||||||
|
#define __ESPNBNS_h__ |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "lwip/init.h" // LWIP_VERSION_ |
||||||
|
#include <lwip/ip_addr.h> |
||||||
|
} |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
|
||||||
|
#define NBNS_PORT 137 |
||||||
|
/**
|
||||||
|
* @def NBNS_MAX_HOSTNAME_LEN |
||||||
|
* @brief maximalni delka NBNS jmena zarizeni |
||||||
|
* @remarks |
||||||
|
* Jmeno zarizeni musi byt uvedeno VELKYMI pismenami a nesmi obsahovat mezery (whitespaces). |
||||||
|
*/ |
||||||
|
#define NBNS_MAX_HOSTNAME_LEN 16 |
||||||
|
|
||||||
|
struct udp_pcb; |
||||||
|
struct pbuf; |
||||||
|
|
||||||
|
class ESP8266NetBIOS |
||||||
|
{ |
||||||
|
protected: |
||||||
|
udp_pcb* _pcb; |
||||||
|
char _name[NBNS_MAX_HOSTNAME_LEN + 1]; |
||||||
|
void _getnbname(char *nbname, char *name, uint8_t maxlen); |
||||||
|
void _makenbname(char *name, char *nbname, uint8_t outlen); |
||||||
|
|
||||||
|
void _recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port); |
||||||
|
static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port); |
||||||
|
|
||||||
|
public: |
||||||
|
ESP8266NetBIOS(); |
||||||
|
~ESP8266NetBIOS(); |
||||||
|
bool begin(const char *name); |
||||||
|
void end(); |
||||||
|
}; |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) |
||||||
|
extern ESP8266NetBIOS NBNS; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,52 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266NetBIOS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer wwwserver(80); |
||||||
|
String content; |
||||||
|
|
||||||
|
static void handleRoot(void) { |
||||||
|
content = F("<!DOCTYPE HTML>\n<html>Hello world from ESP8266"); |
||||||
|
content += F("<p>"); |
||||||
|
content += F("</html>"); |
||||||
|
|
||||||
|
wwwserver.send(200, F("text/html"), content); |
||||||
|
} |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
|
||||||
|
// Connect to WiFi network
|
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
|
||||||
|
wwwserver.on("/", handleRoot); |
||||||
|
wwwserver.begin(); |
||||||
|
|
||||||
|
NBNS.begin("ESP"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
wwwserver.handleClient(); |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For ESPNBNS |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
NBNS KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Instances (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266NetBIOS |
||||||
|
version=1.0 |
||||||
|
author=Pablo@xpablo.cz |
||||||
|
maintainer=Hristo Gochkov<hristo@espressif.com> |
||||||
|
sentence=Enables NBNS (NetBIOS) name resolution. |
||||||
|
paragraph=With this library you can connect to your ESP from Windows using a short name |
||||||
|
category=Communication |
||||||
|
url=http://www.xpablo.cz/?p=751#more-751 |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,512 @@ |
|||||||
|
/*
|
||||||
|
ESP8266 Simple Service Discovery |
||||||
|
Copyright (c) 2015 Hristo Gochkov |
||||||
|
|
||||||
|
Original (Arduino) version by Filippo Sallemi, July 23, 2014. |
||||||
|
Can be found at: https://github.com/nomadnt/uSSDP
|
||||||
|
|
||||||
|
License (MIT license): |
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
||||||
|
|
||||||
|
*/ |
||||||
|
#ifndef LWIP_OPEN_SRC |
||||||
|
#define LWIP_OPEN_SRC |
||||||
|
#endif |
||||||
|
#include <functional> |
||||||
|
#include "ESP8266SSDP.h" |
||||||
|
#include "WiFiUdp.h" |
||||||
|
#include "debug.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include "osapi.h" |
||||||
|
#include "ets_sys.h" |
||||||
|
#include "user_interface.h" |
||||||
|
} |
||||||
|
|
||||||
|
#include "lwip/opt.h" |
||||||
|
#include "lwip/udp.h" |
||||||
|
#include "lwip/inet.h" |
||||||
|
#include "lwip/igmp.h" |
||||||
|
#include "lwip/mem.h" |
||||||
|
#include "include/UdpContext.h" |
||||||
|
//#define DEBUG_SSDP Serial
|
||||||
|
|
||||||
|
#define SSDP_INTERVAL 1200 |
||||||
|
#define SSDP_PORT 1900 |
||||||
|
#define SSDP_METHOD_SIZE 10 |
||||||
|
#define SSDP_URI_SIZE 2 |
||||||
|
#define SSDP_BUFFER_SIZE 64 |
||||||
|
#define SSDP_MULTICAST_TTL 2 |
||||||
|
|
||||||
|
// ssdp ipv6 is FF05::C
|
||||||
|
// lwip-v2's igmp_joingroup only supports IPv4
|
||||||
|
#define SSDP_MULTICAST_ADDR 239, 255, 255, 250 |
||||||
|
|
||||||
|
static const char _ssdp_response_template[] PROGMEM = |
||||||
|
"HTTP/1.1 200 OK\r\n" |
||||||
|
"EXT:\r\n"; |
||||||
|
|
||||||
|
static const char _ssdp_notify_template[] PROGMEM = |
||||||
|
"NOTIFY * HTTP/1.1\r\n" |
||||||
|
"HOST: 239.255.255.250:1900\r\n" |
||||||
|
"NTS: ssdp:alive\r\n"; |
||||||
|
|
||||||
|
static const char _ssdp_packet_template[] PROGMEM = |
||||||
|
"%s" // _ssdp_response_template / _ssdp_notify_template
|
||||||
|
"CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL
|
||||||
|
"SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber
|
||||||
|
"USN: uuid:%s\r\n" // _uuid
|
||||||
|
"%s: %s\r\n" // "NT" or "ST", _deviceType
|
||||||
|
"LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
|
||||||
|
"\r\n"; |
||||||
|
|
||||||
|
static const char _ssdp_schema_template[] PROGMEM = |
||||||
|
"HTTP/1.1 200 OK\r\n" |
||||||
|
"Content-Type: text/xml\r\n" |
||||||
|
"Connection: close\r\n" |
||||||
|
"Access-Control-Allow-Origin: *\r\n" |
||||||
|
"\r\n" |
||||||
|
"<?xml version=\"1.0\"?>" |
||||||
|
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" |
||||||
|
"<specVersion>" |
||||||
|
"<major>1</major>" |
||||||
|
"<minor>0</minor>" |
||||||
|
"</specVersion>" |
||||||
|
"<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
|
||||||
|
"<device>" |
||||||
|
"<deviceType>%s</deviceType>" |
||||||
|
"<friendlyName>%s</friendlyName>" |
||||||
|
"<presentationURL>%s</presentationURL>" |
||||||
|
"<serialNumber>%s</serialNumber>" |
||||||
|
"<modelName>%s</modelName>" |
||||||
|
"<modelNumber>%s</modelNumber>" |
||||||
|
"<modelURL>%s</modelURL>" |
||||||
|
"<manufacturer>%s</manufacturer>" |
||||||
|
"<manufacturerURL>%s</manufacturerURL>" |
||||||
|
"<UDN>uuid:%s</UDN>" |
||||||
|
"</device>" |
||||||
|
//"<iconList>"
|
||||||
|
//"<icon>"
|
||||||
|
//"<mimetype>image/png</mimetype>"
|
||||||
|
//"<height>48</height>"
|
||||||
|
//"<width>48</width>"
|
||||||
|
//"<depth>24</depth>"
|
||||||
|
//"<url>icon48.png</url>"
|
||||||
|
//"</icon>"
|
||||||
|
//"<icon>"
|
||||||
|
//"<mimetype>image/png</mimetype>"
|
||||||
|
//"<height>120</height>"
|
||||||
|
//"<width>120</width>"
|
||||||
|
//"<depth>24</depth>"
|
||||||
|
//"<url>icon120.png</url>"
|
||||||
|
//"</icon>"
|
||||||
|
//"</iconList>"
|
||||||
|
"</root>\r\n" |
||||||
|
"\r\n"; |
||||||
|
|
||||||
|
|
||||||
|
struct SSDPTimer { |
||||||
|
ETSTimer timer; |
||||||
|
}; |
||||||
|
|
||||||
|
SSDPClass::SSDPClass() : |
||||||
|
_server(0), |
||||||
|
_timer(0), |
||||||
|
_port(80), |
||||||
|
_ttl(SSDP_MULTICAST_TTL), |
||||||
|
_respondToPort(0), |
||||||
|
_pending(false), |
||||||
|
_delay(0), |
||||||
|
_process_time(0), |
||||||
|
_notify_time(0) |
||||||
|
{ |
||||||
|
_uuid[0] = '\0'; |
||||||
|
_modelNumber[0] = '\0'; |
||||||
|
sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); |
||||||
|
_friendlyName[0] = '\0'; |
||||||
|
_presentationURL[0] = '\0'; |
||||||
|
_serialNumber[0] = '\0'; |
||||||
|
_modelName[0] = '\0'; |
||||||
|
_modelURL[0] = '\0'; |
||||||
|
_manufacturer[0] = '\0'; |
||||||
|
_manufacturerURL[0] = '\0'; |
||||||
|
sprintf(_schemaURL, "ssdp/schema.xml"); |
||||||
|
} |
||||||
|
|
||||||
|
SSDPClass::~SSDPClass() { |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
bool SSDPClass::begin() { |
||||||
|
end(); |
||||||
|
|
||||||
|
_pending = false; |
||||||
|
if (strcmp(_uuid,"") == 0) { |
||||||
|
uint32_t chipId = ESP.getChipId(); |
||||||
|
sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", |
||||||
|
(uint16_t) ((chipId >> 16) & 0xff), |
||||||
|
(uint16_t) ((chipId >> 8) & 0xff), |
||||||
|
(uint16_t) chipId & 0xff); |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); |
||||||
|
#endif |
||||||
|
|
||||||
|
assert(NULL == _server); |
||||||
|
|
||||||
|
_server = new UdpContext; |
||||||
|
_server->ref(); |
||||||
|
|
||||||
|
IPAddress local = WiFi.localIP(); |
||||||
|
IPAddress mcast(SSDP_MULTICAST_ADDR); |
||||||
|
|
||||||
|
if (igmp_joingroup(local, mcast) != ERR_OK ) { |
||||||
|
DEBUGV("SSDP failed to join igmp group"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!_server->listen(IP_ADDR_ANY, SSDP_PORT)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
_server->setMulticastInterface(local); |
||||||
|
_server->setMulticastTTL(_ttl); |
||||||
|
_server->onRx(std::bind(&SSDPClass::_update, this)); |
||||||
|
if (!_server->connect(mcast, SSDP_PORT)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
_startTimer(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::end() { |
||||||
|
if(!_server) |
||||||
|
return; // object is zeroed already, nothing to do
|
||||||
|
|
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf_P(PSTR("SSDP end ... ")); |
||||||
|
#endif |
||||||
|
// undo all initializations done in begin(), in reverse order
|
||||||
|
_stopTimer(); |
||||||
|
|
||||||
|
_server->disconnect(); |
||||||
|
|
||||||
|
IPAddress local = WiFi.localIP(); |
||||||
|
IPAddress mcast(SSDP_MULTICAST_ADDR); |
||||||
|
|
||||||
|
if (igmp_leavegroup(local, mcast) != ERR_OK ) { |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf_P(PSTR("SSDP failed to leave igmp group\n")); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
_server->unref(); |
||||||
|
_server = 0; |
||||||
|
|
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf_P(PSTR("ok\n")); |
||||||
|
#endif |
||||||
|
} |
||||||
|
void SSDPClass::_send(ssdp_method_t method) { |
||||||
|
char buffer[1460]; |
||||||
|
IPAddress ip = WiFi.localIP(); |
||||||
|
|
||||||
|
char valueBuffer[strlen_P(_ssdp_notify_template) + 1]; |
||||||
|
strcpy_P(valueBuffer, (method == NONE) ? _ssdp_response_template : _ssdp_notify_template); |
||||||
|
|
||||||
|
int len = snprintf_P(buffer, sizeof(buffer), |
||||||
|
_ssdp_packet_template, |
||||||
|
valueBuffer, |
||||||
|
SSDP_INTERVAL, |
||||||
|
_modelName, |
||||||
|
_modelNumber, |
||||||
|
_uuid, |
||||||
|
(method == NONE) ? "ST" : "NT", |
||||||
|
_deviceType, |
||||||
|
ip[0], ip[1], ip[2], ip[3], _port, _schemaURL |
||||||
|
); |
||||||
|
|
||||||
|
_server->append(buffer, len); |
||||||
|
|
||||||
|
IPAddress remoteAddr; |
||||||
|
uint16_t remotePort; |
||||||
|
if (method == NONE) { |
||||||
|
remoteAddr = _respondToAddr; |
||||||
|
remotePort = _respondToPort; |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.print("Sending Response to "); |
||||||
|
#endif |
||||||
|
} else { |
||||||
|
remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); |
||||||
|
remotePort = SSDP_PORT; |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.println("Sending Notify to "); |
||||||
|
#endif |
||||||
|
} |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.print(IPAddress(remoteAddr)); |
||||||
|
DEBUG_SSDP.print(":"); |
||||||
|
DEBUG_SSDP.println(remotePort); |
||||||
|
#endif |
||||||
|
|
||||||
|
_server->send(remoteAddr, remotePort); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::schema(WiFiClient client) { |
||||||
|
IPAddress ip = WiFi.localIP(); |
||||||
|
char buffer[strlen_P(_ssdp_schema_template) + 1]; |
||||||
|
strcpy_P(buffer, _ssdp_schema_template); |
||||||
|
client.printf(buffer, |
||||||
|
ip[0], ip[1], ip[2], ip[3], _port, |
||||||
|
_deviceType, |
||||||
|
_friendlyName, |
||||||
|
_presentationURL, |
||||||
|
_serialNumber, |
||||||
|
_modelName, |
||||||
|
_modelNumber, |
||||||
|
_modelURL, |
||||||
|
_manufacturer, |
||||||
|
_manufacturerURL, |
||||||
|
_uuid |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::_update() { |
||||||
|
if (!_pending && _server->next()) { |
||||||
|
ssdp_method_t method = NONE; |
||||||
|
|
||||||
|
_respondToAddr = _server->getRemoteAddress(); |
||||||
|
_respondToPort = _server->getRemotePort(); |
||||||
|
|
||||||
|
typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; |
||||||
|
states state = METHOD; |
||||||
|
|
||||||
|
typedef enum {START, MAN, ST, MX} headers; |
||||||
|
headers header = START; |
||||||
|
|
||||||
|
uint8_t cursor = 0; |
||||||
|
uint8_t cr = 0; |
||||||
|
|
||||||
|
char buffer[SSDP_BUFFER_SIZE] = {0}; |
||||||
|
|
||||||
|
while (_server->getSize() > 0) { |
||||||
|
char c = _server->read(); |
||||||
|
|
||||||
|
(c == '\r' || c == '\n') ? cr++ : cr = 0; |
||||||
|
|
||||||
|
switch (state) { |
||||||
|
case METHOD: |
||||||
|
if (c == ' ') { |
||||||
|
if (strcmp(buffer, "M-SEARCH") == 0) method = SEARCH; |
||||||
|
|
||||||
|
if (method == NONE) state = ABORT; |
||||||
|
else state = URI; |
||||||
|
cursor = 0; |
||||||
|
|
||||||
|
} else if (cursor < SSDP_METHOD_SIZE - 1) { |
||||||
|
buffer[cursor++] = c; |
||||||
|
buffer[cursor] = '\0'; |
||||||
|
} |
||||||
|
break; |
||||||
|
case URI: |
||||||
|
if (c == ' ') { |
||||||
|
if (strcmp(buffer, "*")) state = ABORT; |
||||||
|
else state = PROTO; |
||||||
|
cursor = 0; |
||||||
|
} else if (cursor < SSDP_URI_SIZE - 1) { |
||||||
|
buffer[cursor++] = c; |
||||||
|
buffer[cursor] = '\0'; |
||||||
|
} |
||||||
|
break; |
||||||
|
case PROTO: |
||||||
|
if (cr == 2) { |
||||||
|
state = KEY; |
||||||
|
cursor = 0; |
||||||
|
} |
||||||
|
break; |
||||||
|
case KEY: |
||||||
|
if (cr == 4) { |
||||||
|
_pending = true; |
||||||
|
_process_time = millis(); |
||||||
|
} |
||||||
|
else if (c == ' ') { |
||||||
|
cursor = 0; |
||||||
|
state = VALUE; |
||||||
|
} |
||||||
|
else if (c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1) { |
||||||
|
buffer[cursor++] = c; |
||||||
|
buffer[cursor] = '\0'; |
||||||
|
} |
||||||
|
break; |
||||||
|
case VALUE: |
||||||
|
if (cr == 2) { |
||||||
|
switch (header) { |
||||||
|
case START: |
||||||
|
break; |
||||||
|
case MAN: |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
case ST: |
||||||
|
if (strcmp(buffer, "ssdp:all")) { |
||||||
|
state = ABORT; |
||||||
|
#ifdef DEBUG_SSDP |
||||||
|
DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); |
||||||
|
#endif |
||||||
|
} |
||||||
|
// if the search type matches our type, we should respond instead of ABORT
|
||||||
|
if (strcasecmp(buffer, _deviceType) == 0) { |
||||||
|
_pending = true; |
||||||
|
_process_time = millis(); |
||||||
|
state = KEY; |
||||||
|
} |
||||||
|
break; |
||||||
|
case MX: |
||||||
|
_delay = random(0, atoi(buffer)) * 1000L; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (state != ABORT) { |
||||||
|
state = KEY; |
||||||
|
header = START; |
||||||
|
cursor = 0; |
||||||
|
} |
||||||
|
} else if (c != '\r' && c != '\n') { |
||||||
|
if (header == START) { |
||||||
|
if (strncmp(buffer, "MA", 2) == 0) header = MAN; |
||||||
|
else if (strcmp(buffer, "ST") == 0) header = ST; |
||||||
|
else if (strcmp(buffer, "MX") == 0) header = MX; |
||||||
|
} |
||||||
|
|
||||||
|
if (cursor < SSDP_BUFFER_SIZE - 1) { |
||||||
|
buffer[cursor++] = c; |
||||||
|
buffer[cursor] = '\0'; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case ABORT: |
||||||
|
_pending = false; _delay = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (_pending && (millis() - _process_time) > _delay) { |
||||||
|
_pending = false; _delay = 0; |
||||||
|
_send(NONE); |
||||||
|
} else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){ |
||||||
|
_notify_time = millis(); |
||||||
|
_send(NOTIFY); |
||||||
|
} |
||||||
|
|
||||||
|
if (_pending) { |
||||||
|
while (_server->next()) |
||||||
|
_server->flush(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setSchemaURL(const char *url) { |
||||||
|
strlcpy(_schemaURL, url, sizeof(_schemaURL)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setHTTPPort(uint16_t port) { |
||||||
|
_port = port; |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setDeviceType(const char *deviceType) { |
||||||
|
strlcpy(_deviceType, deviceType, sizeof(_deviceType)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setUUID(const char *uuid) { |
||||||
|
strlcpy(_uuid, uuid, sizeof(_uuid));
|
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setName(const char *name) { |
||||||
|
strlcpy(_friendlyName, name, sizeof(_friendlyName)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setURL(const char *url) { |
||||||
|
strlcpy(_presentationURL, url, sizeof(_presentationURL)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setSerialNumber(const char *serialNumber) { |
||||||
|
strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setSerialNumber(const uint32_t serialNumber) { |
||||||
|
snprintf(_serialNumber, sizeof(uint32_t) * 2 + 1, "%08X", serialNumber); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setModelName(const char *name) { |
||||||
|
strlcpy(_modelName, name, sizeof(_modelName)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setModelNumber(const char *num) { |
||||||
|
strlcpy(_modelNumber, num, sizeof(_modelNumber)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setModelURL(const char *url) { |
||||||
|
strlcpy(_modelURL, url, sizeof(_modelURL)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setManufacturer(const char *name) { |
||||||
|
strlcpy(_manufacturer, name, sizeof(_manufacturer)); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::setManufacturerURL(const char *url) { |
||||||
|
strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void SSDPClass::setTTL(const uint8_t ttl) { |
||||||
|
_ttl = ttl; |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::_onTimerStatic(SSDPClass* self) { |
||||||
|
self->_update(); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::_startTimer() { |
||||||
|
_stopTimer(); |
||||||
|
_timer = new SSDPTimer(); |
||||||
|
ETSTimer* tm = &(_timer->timer); |
||||||
|
const int interval = 1000; |
||||||
|
os_timer_disarm(tm); |
||||||
|
os_timer_setfn(tm, reinterpret_cast<ETSTimerFunc*>(&SSDPClass::_onTimerStatic), reinterpret_cast<void*>(this)); |
||||||
|
os_timer_arm(tm, interval, 1 /* repeat */); |
||||||
|
} |
||||||
|
|
||||||
|
void SSDPClass::_stopTimer() { |
||||||
|
if(!_timer) |
||||||
|
return; |
||||||
|
|
||||||
|
ETSTimer* tm = &(_timer->timer); |
||||||
|
os_timer_disarm(tm); |
||||||
|
delete _timer; |
||||||
|
_timer = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) |
||||||
|
SSDPClass SSDP; |
||||||
|
#endif |
@ -0,0 +1,132 @@ |
|||||||
|
/*
|
||||||
|
ESP8266 Simple Service Discovery |
||||||
|
Copyright (c) 2015 Hristo Gochkov |
||||||
|
|
||||||
|
Original (Arduino) version by Filippo Sallemi, July 23, 2014. |
||||||
|
Can be found at: https://github.com/nomadnt/uSSDP
|
||||||
|
|
||||||
|
License (MIT license): |
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ESP8266SSDP_H |
||||||
|
#define ESP8266SSDP_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiUdp.h> |
||||||
|
|
||||||
|
class UdpContext; |
||||||
|
|
||||||
|
#define SSDP_UUID_SIZE 37 |
||||||
|
#define SSDP_SCHEMA_URL_SIZE 64 |
||||||
|
#define SSDP_DEVICE_TYPE_SIZE 64 |
||||||
|
#define SSDP_FRIENDLY_NAME_SIZE 64 |
||||||
|
#define SSDP_SERIAL_NUMBER_SIZE 37 |
||||||
|
#define SSDP_PRESENTATION_URL_SIZE 128 |
||||||
|
#define SSDP_MODEL_NAME_SIZE 64 |
||||||
|
#define SSDP_MODEL_URL_SIZE 128 |
||||||
|
#define SSDP_MODEL_VERSION_SIZE 32 |
||||||
|
#define SSDP_MANUFACTURER_SIZE 64 |
||||||
|
#define SSDP_MANUFACTURER_URL_SIZE 128 |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
NONE, |
||||||
|
SEARCH, |
||||||
|
NOTIFY |
||||||
|
} ssdp_method_t; |
||||||
|
|
||||||
|
|
||||||
|
struct SSDPTimer; |
||||||
|
|
||||||
|
class SSDPClass{ |
||||||
|
public: |
||||||
|
SSDPClass(); |
||||||
|
~SSDPClass(); |
||||||
|
bool begin(); |
||||||
|
void end(); |
||||||
|
void schema(WiFiClient client); |
||||||
|
void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); } |
||||||
|
void setDeviceType(const char *deviceType); |
||||||
|
|
||||||
|
/*To define a custom UUID, you must call the method before begin(). Otherwise an automatic UUID based on CHIPID will be generated.*/ |
||||||
|
void setUUID(const String& uuid) { setUUID(uuid.c_str()); } |
||||||
|
void setUUID(const char *uuid); |
||||||
|
|
||||||
|
void setName(const String& name) { setName(name.c_str()); } |
||||||
|
void setName(const char *name); |
||||||
|
void setURL(const String& url) { setURL(url.c_str()); } |
||||||
|
void setURL(const char *url); |
||||||
|
void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); } |
||||||
|
void setSchemaURL(const char *url); |
||||||
|
void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); } |
||||||
|
void setSerialNumber(const char *serialNumber); |
||||||
|
void setSerialNumber(const uint32_t serialNumber); |
||||||
|
void setModelName(const String& name) { setModelName(name.c_str()); } |
||||||
|
void setModelName(const char *name); |
||||||
|
void setModelNumber(const String& num) { setModelNumber(num.c_str()); } |
||||||
|
void setModelNumber(const char *num); |
||||||
|
void setModelURL(const String& url) { setModelURL(url.c_str()); } |
||||||
|
void setModelURL(const char *url); |
||||||
|
void setManufacturer(const String& name) { setManufacturer(name.c_str()); } |
||||||
|
void setManufacturer(const char *name); |
||||||
|
void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); } |
||||||
|
void setManufacturerURL(const char *url); |
||||||
|
void setHTTPPort(uint16_t port); |
||||||
|
void setTTL(uint8_t ttl); |
||||||
|
|
||||||
|
protected: |
||||||
|
void _send(ssdp_method_t method); |
||||||
|
void _update(); |
||||||
|
void _startTimer(); |
||||||
|
void _stopTimer(); |
||||||
|
static void _onTimerStatic(SSDPClass* self); |
||||||
|
|
||||||
|
UdpContext* _server; |
||||||
|
SSDPTimer* _timer; |
||||||
|
uint16_t _port; |
||||||
|
uint8_t _ttl; |
||||||
|
|
||||||
|
IPAddress _respondToAddr; |
||||||
|
uint16_t _respondToPort; |
||||||
|
|
||||||
|
bool _pending; |
||||||
|
unsigned short _delay; |
||||||
|
unsigned long _process_time; |
||||||
|
unsigned long _notify_time; |
||||||
|
|
||||||
|
char _schemaURL[SSDP_SCHEMA_URL_SIZE]; |
||||||
|
char _uuid[SSDP_UUID_SIZE]; |
||||||
|
char _deviceType[SSDP_DEVICE_TYPE_SIZE]; |
||||||
|
char _friendlyName[SSDP_FRIENDLY_NAME_SIZE]; |
||||||
|
char _serialNumber[SSDP_SERIAL_NUMBER_SIZE]; |
||||||
|
char _presentationURL[SSDP_PRESENTATION_URL_SIZE]; |
||||||
|
char _manufacturer[SSDP_MANUFACTURER_SIZE]; |
||||||
|
char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE]; |
||||||
|
char _modelName[SSDP_MODEL_NAME_SIZE]; |
||||||
|
char _modelURL[SSDP_MODEL_URL_SIZE]; |
||||||
|
char _modelNumber[SSDP_MODEL_VERSION_SIZE]; |
||||||
|
}; |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) |
||||||
|
extern SSDPClass SSDP; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,22 @@ |
|||||||
|
ESP8266 Simple Service Discovery Copyright (c) 2015 Hristo Gochkov |
||||||
|
Original (Arduino) version by Filippo Sallemi, July 23, 2014. Can be |
||||||
|
found at: https://github.com/nomadnt/uSSDP |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the |
||||||
|
"Software"), to deal in the Software without restriction, including |
||||||
|
without limitation the rights to use, copy, modify, merge, publish, |
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to |
||||||
|
permit persons to whom the Software is furnished to do so, subject to |
||||||
|
the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,58 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266SSDP.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer HTTP(80); |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Starting WiFi..."); |
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
if (WiFi.waitForConnectResult() == WL_CONNECTED) { |
||||||
|
|
||||||
|
Serial.printf("Starting HTTP...\n"); |
||||||
|
HTTP.on("/index.html", HTTP_GET, []() { |
||||||
|
HTTP.send(200, "text/plain", "Hello World!"); |
||||||
|
}); |
||||||
|
HTTP.on("/description.xml", HTTP_GET, []() { |
||||||
|
SSDP.schema(HTTP.client()); |
||||||
|
}); |
||||||
|
HTTP.begin(); |
||||||
|
|
||||||
|
Serial.printf("Starting SSDP...\n"); |
||||||
|
SSDP.setSchemaURL("description.xml"); |
||||||
|
SSDP.setHTTPPort(80); |
||||||
|
SSDP.setName("Philips hue clone"); |
||||||
|
SSDP.setSerialNumber("001788102201"); |
||||||
|
SSDP.setURL("index.html"); |
||||||
|
SSDP.setModelName("Philips hue bridge 2012"); |
||||||
|
SSDP.setModelNumber("929000226503"); |
||||||
|
SSDP.setModelURL("http://www.meethue.com"); |
||||||
|
SSDP.setManufacturer("Royal Philips Electronics"); |
||||||
|
SSDP.setManufacturerURL("http://www.philips.com"); |
||||||
|
SSDP.begin(); |
||||||
|
|
||||||
|
Serial.printf("Ready!\n"); |
||||||
|
} else { |
||||||
|
Serial.printf("WiFi Failed\n"); |
||||||
|
while (1) { |
||||||
|
delay(100); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
HTTP.handleClient(); |
||||||
|
delay(1); |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Ultrasound |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266SSDP KEYWORD1 |
||||||
|
SSDP KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
schema KEYWORD2 |
||||||
|
setUUID KEYWORD2 |
||||||
|
setName KEYWORD2 |
||||||
|
setURL KEYWORD2 |
||||||
|
setHTTPPort KEYWORD2 |
||||||
|
setSchemaURL KEYWORD2 |
||||||
|
setSerialNumber KEYWORD2 |
||||||
|
setModelName KEYWORD2 |
||||||
|
setModelNumber KEYWORD2 |
||||||
|
setModelURL KEYWORD2 |
||||||
|
setManufacturer KEYWORD2 |
||||||
|
setManufacturerURL KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
SSDP_INTERVAL LITERAL1 |
||||||
|
SSDP_UUID LITERAL1 |
||||||
|
SSDP_PORT LITERAL1 |
||||||
|
SSDP_METHOD_SIZE LITERAL1 |
||||||
|
SSDP_URI_SIZE LITERAL1 |
||||||
|
SSDP_BUFFER_SIZE LITERAL1 |
||||||
|
SSDP_BASE_SIZE LITERAL1 |
||||||
|
SSDP_FRIENDLY_NAME_SIZE LITERAL1 |
||||||
|
SSDP_SERIAL_NUMBER_SIZE LITERAL1 |
||||||
|
SSDP_PRESENTATION_URL_SIZE LITERAL1 |
||||||
|
SSDP_MODEL_NAME_SIZE LITERAL1 |
||||||
|
SSDP_MODEL_URL_SIZE LITERAL1 |
||||||
|
SSDP_MODEL_VERSION_SIZE LITERAL1 |
||||||
|
SSDP_MANUFACTURER_SIZE LITERAL1 |
||||||
|
SSDP_MANUFACTURER_URL_SIZE LITERAL1 |
||||||
|
SEARCH LITERAL1 |
||||||
|
NOTIFY LITERAL1 |
||||||
|
BASIC LITERAL1 |
||||||
|
MANAGEABLE LITERAL1 |
||||||
|
SOLARPROTECTIONBLIND LITERAL1 |
||||||
|
DIGITALSECURITYCAMERA LITERAL1 |
||||||
|
HVAC LITERAL1 |
||||||
|
LIGHTINGCONTROL LITERAL1 |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266SSDP |
||||||
|
version=1.0 |
||||||
|
author=Hristo Gochkov |
||||||
|
maintainer=Hristo Gochkov |
||||||
|
sentence=ESP8266 Simple Service Discovery library |
||||||
|
paragraph= |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,168 @@ |
|||||||
|
|
||||||
|
ESP8266 Web Server |
||||||
|
================== |
||||||
|
|
||||||
|
The WebServer class found in ``ESP8266WebServer.h`` header, is a simple web server that knows how to handle HTTP requests such as GET and POST and can only support one simultaneous client. |
||||||
|
|
||||||
|
Usage |
||||||
|
----- |
||||||
|
|
||||||
|
Class Constructor |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
Creates the ESP8266WebServer class object. |
||||||
|
|
||||||
|
*Parameters:* |
||||||
|
|
||||||
|
host IP address: ``IPaddress addr`` (optional) |
||||||
|
|
||||||
|
host port number: ``int port`` (default is the standard HTTP port 80) |
||||||
|
|
||||||
|
Basic Operations |
||||||
|
~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Starting the server |
||||||
|
^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
void begin(); |
||||||
|
|
||||||
|
Handling incoming client requests |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
void handleClient(); |
||||||
|
|
||||||
|
Disabling the server |
||||||
|
^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
void close(); |
||||||
|
void stop(); |
||||||
|
|
||||||
|
Both methods function the same |
||||||
|
|
||||||
|
Client request handlers |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
void on(); |
||||||
|
void addHandler(); |
||||||
|
void onNotFound(); |
||||||
|
void onFileUpload(); |
||||||
|
|
||||||
|
*Example:* |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
server.on("/", handlerFunction); |
||||||
|
server.onNotFound(handlerFunction); // called when handler is not assigned |
||||||
|
server.onFileUpload(handlerFunction); // handle file uploads |
||||||
|
|
||||||
|
Sending responses to the client |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
void send(); |
||||||
|
void send_P(); |
||||||
|
|
||||||
|
*Parameters:* |
||||||
|
|
||||||
|
``code`` - HTTP response code, can be ``200`` or ``404``, etc. |
||||||
|
|
||||||
|
``content_type`` - HTTP content type, like ``"text/plain"`` or ``"image/png"``, etc. |
||||||
|
|
||||||
|
``content`` - actual content body |
||||||
|
|
||||||
|
Advanced Options |
||||||
|
~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Getting information about request arguments |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
const String & arg(); |
||||||
|
const String & argName(); |
||||||
|
int args(); |
||||||
|
bool hasArg(); |
||||||
|
|
||||||
|
``arg`` - get request argument value |
||||||
|
|
||||||
|
``argName`` - get request argument name |
||||||
|
|
||||||
|
``args`` - get arguments count |
||||||
|
|
||||||
|
``hasArg`` - check if argument exist |
||||||
|
|
||||||
|
Getting information about request headers |
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
const String & header(); |
||||||
|
const String & headerName(); |
||||||
|
const String & hostHeader(); |
||||||
|
int headers(); |
||||||
|
bool hasHeader(); |
||||||
|
|
||||||
|
|
||||||
|
``header`` - get request header value |
||||||
|
|
||||||
|
``headerName`` - get request header name |
||||||
|
|
||||||
|
``hostHeader`` - get request host header if available, else empty string |
||||||
|
|
||||||
|
``headers`` - get header count |
||||||
|
|
||||||
|
``hasHeader`` - check if header exist |
||||||
|
|
||||||
|
Authentication |
||||||
|
^^^^^^^^^^^^^^ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
bool authenticate(); |
||||||
|
void requestAuthentication(); |
||||||
|
|
||||||
|
``authenticate`` - server authentication, returns true if client is authenticated else false |
||||||
|
|
||||||
|
``requestAuthentication`` - sends authentication failure response to the client |
||||||
|
|
||||||
|
*Example Usage:* |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
if(!server.authenticate(username, password)){ |
||||||
|
server.requestAuthentication(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Other Function Calls |
||||||
|
~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
.. code:: cpp |
||||||
|
|
||||||
|
const String & uri(); // get the current uri |
||||||
|
HTTPMethod method(); // get the current method |
||||||
|
WiFiClient client(); // get the current client |
||||||
|
HTTPUpload & upload(); // get the current upload |
||||||
|
void setContentLength(); // set content length |
||||||
|
void sendHeader(); // send HTTP header |
||||||
|
void sendContent(); // send content |
||||||
|
void sendContent_P(); |
||||||
|
void collectHeaders(); // set the request headers to collect |
||||||
|
void serveStatic(); |
||||||
|
size_t streamFile(); |
||||||
|
|
||||||
|
For code samples enter `here <https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/examples>`__ . |
||||||
|
|
@ -0,0 +1,152 @@ |
|||||||
|
/*
|
||||||
|
Copyright (c) 2015, Majenko Technologies |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this |
||||||
|
list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice, this |
||||||
|
list of conditions and the following disclaimer in the documentation and/or |
||||||
|
other materials provided with the distribution. |
||||||
|
|
||||||
|
* * Neither the name of Majenko Technologies nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived from |
||||||
|
this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char *ssid = STASSID; |
||||||
|
const char *password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
const int led = 13; |
||||||
|
|
||||||
|
void handleRoot() { |
||||||
|
digitalWrite(led, 1); |
||||||
|
char temp[400]; |
||||||
|
int sec = millis() / 1000; |
||||||
|
int min = sec / 60; |
||||||
|
int hr = min / 60; |
||||||
|
|
||||||
|
snprintf(temp, 400, |
||||||
|
|
||||||
|
"<html>\
|
||||||
|
<head>\
|
||||||
|
<meta http-equiv='refresh' content='5'/>\
|
||||||
|
<title>ESP8266 Demo</title>\
|
||||||
|
<style>\
|
||||||
|
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
|
||||||
|
</style>\
|
||||||
|
</head>\
|
||||||
|
<body>\
|
||||||
|
<h1>Hello from ESP8266!</h1>\
|
||||||
|
<p>Uptime: %02d:%02d:%02d</p>\
|
||||||
|
<img src=\"/test.svg\" />\
|
||||||
|
</body>\
|
||||||
|
</html>", |
||||||
|
|
||||||
|
hr, min % 60, sec % 60 |
||||||
|
); |
||||||
|
server.send(200, "text/html", temp); |
||||||
|
digitalWrite(led, 0); |
||||||
|
} |
||||||
|
|
||||||
|
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 += "\nArguments: "; |
||||||
|
message += server.args(); |
||||||
|
message += "\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) { |
||||||
|
pinMode(led, OUTPUT); |
||||||
|
digitalWrite(led, 0); |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
|
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
if (MDNS.begin("esp8266")) { |
||||||
|
Serial.println("MDNS responder started"); |
||||||
|
} |
||||||
|
|
||||||
|
server.on("/", handleRoot); |
||||||
|
server.on("/test.svg", drawGraph); |
||||||
|
server.on("/inline", []() { |
||||||
|
server.send(200, "text/plain", "this works as well"); |
||||||
|
}); |
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
server.begin(); |
||||||
|
Serial.println("HTTP server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
||||||
|
|
||||||
|
void drawGraph() { |
||||||
|
String out = ""; |
||||||
|
char temp[100]; |
||||||
|
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n"; |
||||||
|
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n"; |
||||||
|
out += "<g stroke=\"black\">\n"; |
||||||
|
int y = rand() % 130; |
||||||
|
for (int x = 10; x < 390; x += 10) { |
||||||
|
int y2 = rand() % 130; |
||||||
|
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2); |
||||||
|
out += temp; |
||||||
|
y = y2; |
||||||
|
} |
||||||
|
out += "</g>\n</svg>\n"; |
||||||
|
|
||||||
|
server.send(200, "image/svg+xml", out); |
||||||
|
} |
@ -0,0 +1,286 @@ |
|||||||
|
/*
|
||||||
|
FSWebServer - Example WebServer with SPIFFS backend for esp8266 |
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||||
|
This file is part of the ESP8266WebServer library for Arduino environment. |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE) |
||||||
|
or you can upload the contents of a folder if you CD in that folder and run the following command: |
||||||
|
for file in `\ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done |
||||||
|
|
||||||
|
access the sample web page at http://esp8266fs.local
|
||||||
|
edit the page by going to http://esp8266fs.local/edit
|
||||||
|
*/ |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <FS.h> |
||||||
|
|
||||||
|
#define DBG_OUTPUT_PORT Serial |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
const char* host = "esp8266fs"; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
//holds the current upload
|
||||||
|
File fsUploadFile; |
||||||
|
|
||||||
|
//format bytes
|
||||||
|
String formatBytes(size_t bytes) { |
||||||
|
if (bytes < 1024) { |
||||||
|
return String(bytes) + "B"; |
||||||
|
} else if (bytes < (1024 * 1024)) { |
||||||
|
return String(bytes / 1024.0) + "KB"; |
||||||
|
} else if (bytes < (1024 * 1024 * 1024)) { |
||||||
|
return String(bytes / 1024.0 / 1024.0) + "MB"; |
||||||
|
} else { |
||||||
|
return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String getContentType(String filename) { |
||||||
|
if (server.hasArg("download")) { |
||||||
|
return "application/octet-stream"; |
||||||
|
} else if (filename.endsWith(".htm")) { |
||||||
|
return "text/html"; |
||||||
|
} else if (filename.endsWith(".html")) { |
||||||
|
return "text/html"; |
||||||
|
} else if (filename.endsWith(".css")) { |
||||||
|
return "text/css"; |
||||||
|
} else if (filename.endsWith(".js")) { |
||||||
|
return "application/javascript"; |
||||||
|
} else if (filename.endsWith(".png")) { |
||||||
|
return "image/png"; |
||||||
|
} else if (filename.endsWith(".gif")) { |
||||||
|
return "image/gif"; |
||||||
|
} else if (filename.endsWith(".jpg")) { |
||||||
|
return "image/jpeg"; |
||||||
|
} else if (filename.endsWith(".ico")) { |
||||||
|
return "image/x-icon"; |
||||||
|
} else if (filename.endsWith(".xml")) { |
||||||
|
return "text/xml"; |
||||||
|
} else if (filename.endsWith(".pdf")) { |
||||||
|
return "application/x-pdf"; |
||||||
|
} else if (filename.endsWith(".zip")) { |
||||||
|
return "application/x-zip"; |
||||||
|
} else if (filename.endsWith(".gz")) { |
||||||
|
return "application/x-gzip"; |
||||||
|
} |
||||||
|
return "text/plain"; |
||||||
|
} |
||||||
|
|
||||||
|
bool handleFileRead(String path) { |
||||||
|
DBG_OUTPUT_PORT.println("handleFileRead: " + path); |
||||||
|
if (path.endsWith("/")) { |
||||||
|
path += "index.htm"; |
||||||
|
} |
||||||
|
String contentType = getContentType(path); |
||||||
|
String pathWithGz = path + ".gz"; |
||||||
|
if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { |
||||||
|
if (SPIFFS.exists(pathWithGz)) { |
||||||
|
path += ".gz"; |
||||||
|
} |
||||||
|
File file = SPIFFS.open(path, "r"); |
||||||
|
server.streamFile(file, contentType); |
||||||
|
file.close(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void handleFileUpload() { |
||||||
|
if (server.uri() != "/edit") { |
||||||
|
return; |
||||||
|
} |
||||||
|
HTTPUpload& upload = server.upload(); |
||||||
|
if (upload.status == UPLOAD_FILE_START) { |
||||||
|
String filename = upload.filename; |
||||||
|
if (!filename.startsWith("/")) { |
||||||
|
filename = "/" + filename; |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename); |
||||||
|
fsUploadFile = SPIFFS.open(filename, "w"); |
||||||
|
filename = String(); |
||||||
|
} else if (upload.status == UPLOAD_FILE_WRITE) { |
||||||
|
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
|
||||||
|
if (fsUploadFile) { |
||||||
|
fsUploadFile.write(upload.buf, upload.currentSize); |
||||||
|
} |
||||||
|
} else if (upload.status == UPLOAD_FILE_END) { |
||||||
|
if (fsUploadFile) { |
||||||
|
fsUploadFile.close(); |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void handleFileDelete() { |
||||||
|
if (server.args() == 0) { |
||||||
|
return server.send(500, "text/plain", "BAD ARGS"); |
||||||
|
} |
||||||
|
String path = server.arg(0); |
||||||
|
DBG_OUTPUT_PORT.println("handleFileDelete: " + path); |
||||||
|
if (path == "/") { |
||||||
|
return server.send(500, "text/plain", "BAD PATH"); |
||||||
|
} |
||||||
|
if (!SPIFFS.exists(path)) { |
||||||
|
return server.send(404, "text/plain", "FileNotFound"); |
||||||
|
} |
||||||
|
SPIFFS.remove(path); |
||||||
|
server.send(200, "text/plain", ""); |
||||||
|
path = String(); |
||||||
|
} |
||||||
|
|
||||||
|
void handleFileCreate() { |
||||||
|
if (server.args() == 0) { |
||||||
|
return server.send(500, "text/plain", "BAD ARGS"); |
||||||
|
} |
||||||
|
String path = server.arg(0); |
||||||
|
DBG_OUTPUT_PORT.println("handleFileCreate: " + path); |
||||||
|
if (path == "/") { |
||||||
|
return server.send(500, "text/plain", "BAD PATH"); |
||||||
|
} |
||||||
|
if (SPIFFS.exists(path)) { |
||||||
|
return server.send(500, "text/plain", "FILE EXISTS"); |
||||||
|
} |
||||||
|
File file = SPIFFS.open(path, "w"); |
||||||
|
if (file) { |
||||||
|
file.close(); |
||||||
|
} else { |
||||||
|
return server.send(500, "text/plain", "CREATE FAILED"); |
||||||
|
} |
||||||
|
server.send(200, "text/plain", ""); |
||||||
|
path = String(); |
||||||
|
} |
||||||
|
|
||||||
|
void handleFileList() { |
||||||
|
if (!server.hasArg("dir")) { |
||||||
|
server.send(500, "text/plain", "BAD ARGS"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String path = server.arg("dir"); |
||||||
|
DBG_OUTPUT_PORT.println("handleFileList: " + path); |
||||||
|
Dir dir = SPIFFS.openDir(path); |
||||||
|
path = String(); |
||||||
|
|
||||||
|
String output = "["; |
||||||
|
while (dir.next()) { |
||||||
|
File entry = dir.openFile("r"); |
||||||
|
if (output != "[") { |
||||||
|
output += ','; |
||||||
|
} |
||||||
|
bool isDir = false; |
||||||
|
output += "{\"type\":\""; |
||||||
|
output += (isDir) ? "dir" : "file"; |
||||||
|
output += "\",\"name\":\""; |
||||||
|
output += String(entry.name()).substring(1); |
||||||
|
output += "\"}"; |
||||||
|
entry.close(); |
||||||
|
} |
||||||
|
|
||||||
|
output += "]"; |
||||||
|
server.send(200, "text/json", output); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
DBG_OUTPUT_PORT.begin(115200); |
||||||
|
DBG_OUTPUT_PORT.print("\n"); |
||||||
|
DBG_OUTPUT_PORT.setDebugOutput(true); |
||||||
|
SPIFFS.begin(); |
||||||
|
{ |
||||||
|
Dir dir = SPIFFS.openDir("/"); |
||||||
|
while (dir.next()) { |
||||||
|
String fileName = dir.fileName(); |
||||||
|
size_t fileSize = dir.fileSize(); |
||||||
|
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.printf("\n"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//WIFI INIT
|
||||||
|
DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); |
||||||
|
if (String(WiFi.SSID()) != String(ssid)) { |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
} |
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
DBG_OUTPUT_PORT.print("."); |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.println(""); |
||||||
|
DBG_OUTPUT_PORT.print("Connected! IP address: "); |
||||||
|
DBG_OUTPUT_PORT.println(WiFi.localIP()); |
||||||
|
|
||||||
|
MDNS.begin(host); |
||||||
|
DBG_OUTPUT_PORT.print("Open http://"); |
||||||
|
DBG_OUTPUT_PORT.print(host); |
||||||
|
DBG_OUTPUT_PORT.println(".local/edit to see the file browser"); |
||||||
|
|
||||||
|
|
||||||
|
//SERVER INIT
|
||||||
|
//list directory
|
||||||
|
server.on("/list", HTTP_GET, handleFileList); |
||||||
|
//load editor
|
||||||
|
server.on("/edit", HTTP_GET, []() { |
||||||
|
if (!handleFileRead("/edit.htm")) { |
||||||
|
server.send(404, "text/plain", "FileNotFound"); |
||||||
|
} |
||||||
|
}); |
||||||
|
//create file
|
||||||
|
server.on("/edit", HTTP_PUT, handleFileCreate); |
||||||
|
//delete file
|
||||||
|
server.on("/edit", HTTP_DELETE, handleFileDelete); |
||||||
|
//first callback is called after the request has ended with all parsed arguments
|
||||||
|
//second callback handles file uploads at that location
|
||||||
|
server.on("/edit", HTTP_POST, []() { |
||||||
|
server.send(200, "text/plain", ""); |
||||||
|
}, handleFileUpload); |
||||||
|
|
||||||
|
//called when the url is not defined here
|
||||||
|
//use it to load content from SPIFFS
|
||||||
|
server.onNotFound([]() { |
||||||
|
if (!handleFileRead(server.uri())) { |
||||||
|
server.send(404, "text/plain", "FileNotFound"); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
//get heap status, analog input value and all GPIO statuses in one json call
|
||||||
|
server.on("/all", HTTP_GET, []() { |
||||||
|
String json = "{"; |
||||||
|
json += "\"heap\":" + String(ESP.getFreeHeap()); |
||||||
|
json += ", \"analog\":" + String(analogRead(A0)); |
||||||
|
json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); |
||||||
|
json += "}"; |
||||||
|
server.send(200, "text/json", json); |
||||||
|
json = String(); |
||||||
|
}); |
||||||
|
server.begin(); |
||||||
|
DBG_OUTPUT_PORT.println("HTTP server started"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
@ -0,0 +1,97 @@ |
|||||||
|
<!-- |
||||||
|
FSWebServer - Example Index Page |
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||||
|
This file is part of the ESP8266WebServer library for Arduino environment. |
||||||
|
|
||||||
|
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 |
||||||
|
--> |
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> |
||||||
|
<title>ESP Monitor</title> |
||||||
|
<script type="text/javascript" src="graphs.js"></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
var heap,temp,digi; |
||||||
|
var reloadPeriod = 1000; |
||||||
|
var running = false; |
||||||
|
|
||||||
|
function loadValues(){ |
||||||
|
if(!running) return; |
||||||
|
var xh = new XMLHttpRequest(); |
||||||
|
xh.onreadystatechange = function(){ |
||||||
|
if (xh.readyState == 4){ |
||||||
|
if(xh.status == 200) { |
||||||
|
var res = JSON.parse(xh.responseText); |
||||||
|
heap.add(res.heap); |
||||||
|
temp.add(res.analog); |
||||||
|
digi.add(res.gpio); |
||||||
|
if(running) setTimeout(loadValues, reloadPeriod); |
||||||
|
} else running = false; |
||||||
|
} |
||||||
|
}; |
||||||
|
xh.open("GET", "/all", true); |
||||||
|
xh.send(null); |
||||||
|
}; |
||||||
|
|
||||||
|
function run(){ |
||||||
|
if(!running){ |
||||||
|
running = true; |
||||||
|
loadValues(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function onBodyLoad(){ |
||||||
|
var refreshInput = document.getElementById("refresh-rate"); |
||||||
|
refreshInput.value = reloadPeriod; |
||||||
|
refreshInput.onchange = function(e){ |
||||||
|
var value = parseInt(e.target.value); |
||||||
|
reloadPeriod = (value > 0)?value:0; |
||||||
|
e.target.value = reloadPeriod; |
||||||
|
} |
||||||
|
var stopButton = document.getElementById("stop-button"); |
||||||
|
stopButton.onclick = function(e){ |
||||||
|
running = false; |
||||||
|
} |
||||||
|
var startButton = document.getElementById("start-button"); |
||||||
|
startButton.onclick = function(e){ |
||||||
|
run(); |
||||||
|
} |
||||||
|
|
||||||
|
// Example with 10K thermistor |
||||||
|
//function calcThermistor(v) { |
||||||
|
// var t = Math.log(((10230000 / v) - 10000)); |
||||||
|
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15; |
||||||
|
// return (t>120)?0:Math.round(t*10)/10; |
||||||
|
//} |
||||||
|
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor); |
||||||
|
|
||||||
|
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan"); |
||||||
|
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange"); |
||||||
|
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold"); |
||||||
|
run(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()"> |
||||||
|
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);"> |
||||||
|
<label>Period (ms):</label> |
||||||
|
<input type="number" id="refresh-rate"/> |
||||||
|
<input type="button" id="start-button" value="Start"/> |
||||||
|
<input type="button" id="stop-button" value="Stop"/> |
||||||
|
</div> |
||||||
|
<div id="heap"></div> |
||||||
|
<div id="analog"></div> |
||||||
|
<div id="digital"></div> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,79 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
const int led = 13; |
||||||
|
|
||||||
|
void handleRoot() { |
||||||
|
digitalWrite(led, 1); |
||||||
|
server.send(200, "text/plain", "hello from esp8266!"); |
||||||
|
digitalWrite(led, 0); |
||||||
|
} |
||||||
|
|
||||||
|
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 += "\nArguments: "; |
||||||
|
message += server.args(); |
||||||
|
message += "\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) { |
||||||
|
pinMode(led, OUTPUT); |
||||||
|
digitalWrite(led, 0); |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
if (MDNS.begin("esp8266")) { |
||||||
|
Serial.println("MDNS responder started"); |
||||||
|
} |
||||||
|
|
||||||
|
server.on("/", handleRoot); |
||||||
|
|
||||||
|
server.on("/inline", []() { |
||||||
|
server.send(200, "text/plain", "this works as well"); |
||||||
|
}); |
||||||
|
|
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
|
||||||
|
server.begin(); |
||||||
|
Serial.println("HTTP server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
/*
|
||||||
|
HelloServerBearSSL - Simple HTTPS server example |
||||||
|
|
||||||
|
This example demonstrates a basic ESP8266WebServerSecure HTTPS server |
||||||
|
that can serve "/" and "/inline" and generate detailed 404 (not found) |
||||||
|
HTTP respoinses. Be sure to update the SSID and PASSWORD before running |
||||||
|
to allow connection to your WiFi network. |
||||||
|
|
||||||
|
Adapted by Earle F. Philhower, III, from the HelloServer.ino example. |
||||||
|
This example is released into the public domain. |
||||||
|
*/ |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServerSecure.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
BearSSL::ESP8266WebServerSecure server(443); |
||||||
|
|
||||||
|
static const char serverCert[] PROGMEM = R"EOF( |
||||||
|
-----BEGIN CERTIFICATE----- |
||||||
|
MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC |
||||||
|
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx |
||||||
|
EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w |
||||||
|
HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX |
||||||
|
DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh |
||||||
|
dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI |
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y |
||||||
|
X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj |
||||||
|
oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI |
||||||
|
t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO |
||||||
|
S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy |
||||||
|
+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq |
||||||
|
hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM |
||||||
|
E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb |
||||||
|
fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC |
||||||
|
JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m |
||||||
|
+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA |
||||||
|
5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg== |
||||||
|
-----END CERTIFICATE----- |
||||||
|
)EOF"; |
||||||
|
|
||||||
|
static const char serverKey[] PROGMEM = R"EOF( |
||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI
|
||||||
|
IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z |
||||||
|
uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf |
||||||
|
zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+ |
||||||
|
ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh |
||||||
|
BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8 |
||||||
|
djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T |
||||||
|
yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M |
||||||
|
q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr |
||||||
|
eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN |
||||||
|
d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn |
||||||
|
geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y |
||||||
|
84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx |
||||||
|
/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim |
||||||
|
RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu |
||||||
|
DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg |
||||||
|
rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW |
||||||
|
YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK |
||||||
|
iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X |
||||||
|
jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ |
||||||
|
zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV |
||||||
|
kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt |
||||||
|
/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO |
||||||
|
j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg |
||||||
|
gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= |
||||||
|
-----END RSA PRIVATE KEY----- |
||||||
|
)EOF"; |
||||||
|
|
||||||
|
|
||||||
|
const int led = 13; |
||||||
|
|
||||||
|
void handleRoot() { |
||||||
|
digitalWrite(led, 1); |
||||||
|
server.send(200, "text/plain", "Hello from esp8266 over HTTPS!"); |
||||||
|
digitalWrite(led, 0); |
||||||
|
} |
||||||
|
|
||||||
|
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 += "\nArguments: "; |
||||||
|
message += server.args(); |
||||||
|
message += "\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){ |
||||||
|
pinMode(led, OUTPUT); |
||||||
|
digitalWrite(led, 0); |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
|
||||||
|
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); |
||||||
|
|
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
if (MDNS.begin("esp8266")) { |
||||||
|
Serial.println("MDNS responder started"); |
||||||
|
} |
||||||
|
|
||||||
|
server.setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey)); |
||||||
|
|
||||||
|
server.on("/", handleRoot); |
||||||
|
|
||||||
|
server.on("/inline", [](){ |
||||||
|
server.send(200, "text/plain", "this works as well"); |
||||||
|
}); |
||||||
|
|
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
|
||||||
|
server.begin(); |
||||||
|
Serial.println("HTTPS server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void){ |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,185 @@ |
|||||||
|
/*
|
||||||
|
HelloServerSecure - Simple HTTPS server example |
||||||
|
|
||||||
|
This example demonstrates a basic ESP8266WebServerSecure HTTPS server |
||||||
|
that can serve "/" and "/inline" and generate detailed 404 (not found) |
||||||
|
HTTP respoinses. Be sure to update the SSID and PASSWORD before running |
||||||
|
to allow connection to your WiFi network. |
||||||
|
|
||||||
|
IMPORTANT NOTES ABOUT SSL CERTIFICATES |
||||||
|
|
||||||
|
1. USE/GENERATE YOUR OWN CERTIFICATES |
||||||
|
While a sample, self-signed certificate is included in this example, |
||||||
|
it is ABSOLUTELY VITAL that you use your own SSL certificate in any |
||||||
|
real-world deployment. Anyone with the certificate and key may be |
||||||
|
able to decrypt your traffic, so your own keys should be kept in a |
||||||
|
safe manner, not accessible on any public network. |
||||||
|
|
||||||
|
2. HOW TO GENERATE YOUR OWN CERTIFICATE/KEY PAIR |
||||||
|
A sample script, "make-self-signed-cert.sh" is provided in the |
||||||
|
ESP8266WiFi/examples/WiFiHTTPSServer directory. This script can be |
||||||
|
modified (replace "your-name-here" with your Organization name). Note |
||||||
|
that this will be a *self-signed certificate* and will *NOT* be accepted |
||||||
|
by default by most modern browsers. They'll display something like, |
||||||
|
"This certificate is from an untrusted source," or "Your connection is |
||||||
|
not secure," or "Your connection is not private," and the user will |
||||||
|
have to manully allow the browser to continue by using the |
||||||
|
"Advanced/Add Exception" (FireFox) or "Advanced/Proceed" (Chrome) link. |
||||||
|
|
||||||
|
You may also, of course, use a commercial, trusted SSL provider to |
||||||
|
generate your certificate. When requesting the certificate, you'll |
||||||
|
need to specify that it use SHA256 and 1024 or 512 bits in order to |
||||||
|
function with the axTLS implementation in the ESP8266. |
||||||
|
|
||||||
|
Interactive usage: |
||||||
|
Go to https://esp8266-webupdate.local/firmware, enter the username
|
||||||
|
and password, and the select a new BIN to upload. |
||||||
|
|
||||||
|
To upload through terminal you can use: |
||||||
|
curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware |
||||||
|
|
||||||
|
Adapted by Earle F. Philhower, III, from the HelloServer.ino example. |
||||||
|
This example is released into the public domain. |
||||||
|
*/ |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServerSecure.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServerSecure server(443); |
||||||
|
|
||||||
|
// The certificate is stored in PMEM
|
||||||
|
static const uint8_t x509[] PROGMEM = { |
||||||
|
0x30, 0x82, 0x01, 0x3d, 0x30, 0x81, 0xe8, 0x02, 0x09, 0x00, 0xfe, 0x56, |
||||||
|
0x46, 0xf2, 0x78, 0xc6, 0x51, 0x17, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, |
||||||
|
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x26, 0x31, |
||||||
|
0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, |
||||||
|
0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, |
||||||
|
0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, |
||||||
|
0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x38, 0x31, |
||||||
|
0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x33, 0x30, 0x31, 0x31, |
||||||
|
0x32, 0x35, 0x31, 0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x30, 0x26, 0x31, |
||||||
|
0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, |
||||||
|
0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, |
||||||
|
0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, |
||||||
|
0x31, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, |
||||||
|
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, |
||||||
|
0x41, 0x00, 0xc6, 0x72, 0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, |
||||||
|
0x3a, 0x2a, 0x5a, 0x49, 0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, |
||||||
|
0xbb, 0xa3, 0xd6, 0x6f, 0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, |
||||||
|
0x50, 0x80, 0xec, 0x49, 0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, |
||||||
|
0x81, 0x85, 0x23, 0x24, 0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, |
||||||
|
0x3c, 0x10, 0x40, 0x05, 0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, |
||||||
|
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, |
||||||
|
0x05, 0x00, 0x03, 0x41, 0x00, 0x69, 0xdc, 0x6c, 0x9b, 0xa7, 0x62, 0x57, |
||||||
|
0x7e, 0x03, 0x01, 0x45, 0xad, 0x9a, 0x83, 0x90, 0x3a, 0xe7, 0xdf, 0xe8, |
||||||
|
0x8f, 0x46, 0x00, 0xd3, 0x5f, 0x2b, 0x0a, 0xde, 0x92, 0x1b, 0xc5, 0x04, |
||||||
|
0xc5, 0xc0, 0x76, 0xf4, 0xf6, 0x08, 0x36, 0x97, 0x27, 0x82, 0xf1, 0x60, |
||||||
|
0x76, 0xc2, 0xcd, 0x67, 0x6c, 0x4b, 0x6c, 0xca, 0xfd, 0x97, 0xfd, 0x33, |
||||||
|
0x9e, 0x12, 0x67, 0x6b, 0x98, 0x7e, 0xd5, 0x80, 0x8f |
||||||
|
}; |
||||||
|
|
||||||
|
// And so is the key. These could also be in DRAM
|
||||||
|
static const uint8_t rsakey[] PROGMEM = { |
||||||
|
0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, 0xc6, 0x72, |
||||||
|
0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, 0x3a, 0x2a, 0x5a, 0x49, |
||||||
|
0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, 0xbb, 0xa3, 0xd6, 0x6f, |
||||||
|
0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, 0x50, 0x80, 0xec, 0x49, |
||||||
|
0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, 0x81, 0x85, 0x23, 0x24, |
||||||
|
0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, 0x3c, 0x10, 0x40, 0x05, |
||||||
|
0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x35, 0x0b, 0x74, |
||||||
|
0xd3, 0xff, 0x15, 0x51, 0x44, 0x0f, 0x13, 0x2e, 0x9b, 0x0f, 0x93, 0x5c, |
||||||
|
0x3f, 0xfc, 0xf1, 0x17, 0xf9, 0x72, 0x94, 0x5e, 0xa7, 0xc6, 0xb3, 0xf0, |
||||||
|
0xfe, 0xc9, 0x6c, 0xb1, 0x1e, 0x83, 0xb3, 0xc6, 0x45, 0x3a, 0x25, 0x60, |
||||||
|
0x7c, 0x3d, 0x92, 0x7d, 0x53, 0xec, 0x49, 0x8d, 0xb5, 0x45, 0x10, 0x99, |
||||||
|
0x9b, 0xc6, 0x22, 0x3a, 0x68, 0xc7, 0x13, 0x4e, 0xb6, 0x04, 0x61, 0x21, |
||||||
|
0x01, 0x02, 0x21, 0x00, 0xea, 0x8c, 0x21, 0xd4, 0x7f, 0x3f, 0xb6, 0x91, |
||||||
|
0xfa, 0xf8, 0xb9, 0x2d, 0xcb, 0x36, 0x36, 0x02, 0x5f, 0xf0, 0x0c, 0x6e, |
||||||
|
0x87, 0xaa, 0x5c, 0x14, 0xf6, 0x56, 0x8e, 0x12, 0x92, 0x25, 0xde, 0xb3, |
||||||
|
0x02, 0x21, 0x00, 0xd8, 0x99, 0x01, 0xf1, 0x04, 0x0b, 0x98, 0xa3, 0x71, |
||||||
|
0x56, 0x1d, 0xea, 0x6f, 0x45, 0xd1, 0x36, 0x70, 0x76, 0x8b, 0xab, 0x69, |
||||||
|
0x30, 0x58, 0x9c, 0xe0, 0x45, 0x97, 0xe7, 0xb6, 0xb5, 0xef, 0xc1, 0x02, |
||||||
|
0x21, 0x00, 0xa2, 0x01, 0x06, 0xc0, 0xf2, 0xdf, 0xbc, 0x28, 0x1a, 0xb4, |
||||||
|
0xbf, 0x9b, 0x5c, 0xd8, 0x65, 0xf7, 0xbf, 0xf2, 0x5b, 0x73, 0xe0, 0xeb, |
||||||
|
0x0f, 0xcd, 0x3e, 0xd5, 0x4c, 0x2e, 0x91, 0x99, 0xec, 0xb7, 0x02, 0x20, |
||||||
|
0x4b, 0x9d, 0x46, 0xd7, 0x3c, 0x01, 0x4c, 0x5d, 0x2a, 0xb0, 0xd4, 0xaa, |
||||||
|
0xc6, 0x03, 0xca, 0xa0, 0xc5, 0xac, 0x2c, 0xe0, 0x3f, 0x4d, 0x98, 0x71, |
||||||
|
0xd3, 0xbd, 0x97, 0xe5, 0x55, 0x9c, 0xb8, 0x41, 0x02, 0x20, 0x02, 0x42, |
||||||
|
0x9f, 0xd1, 0x06, 0x35, 0x3b, 0x42, 0xf5, 0x64, 0xaf, 0x6d, 0xbf, 0xcd, |
||||||
|
0x2c, 0x3a, 0xcd, 0x0a, 0x9a, 0x4d, 0x7c, 0xad, 0x29, 0xd6, 0x36, 0x57, |
||||||
|
0xd5, 0xdf, 0x34, 0xeb, 0x26, 0x03 |
||||||
|
}; |
||||||
|
|
||||||
|
const int led = 13; |
||||||
|
|
||||||
|
void handleRoot() { |
||||||
|
digitalWrite(led, 1); |
||||||
|
server.send(200, "text/plain", "Hello from esp8266 over HTTPS!"); |
||||||
|
digitalWrite(led, 0); |
||||||
|
} |
||||||
|
|
||||||
|
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 += "\nArguments: "; |
||||||
|
message += server.args(); |
||||||
|
message += "\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) { |
||||||
|
pinMode(led, OUTPUT); |
||||||
|
digitalWrite(led, 0); |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
if (MDNS.begin("esp8266")) { |
||||||
|
Serial.println("MDNS responder started"); |
||||||
|
} |
||||||
|
|
||||||
|
server.setServerKeyAndCert_P(rsakey, sizeof(rsakey), x509, sizeof(x509)); |
||||||
|
|
||||||
|
server.on("/", handleRoot); |
||||||
|
|
||||||
|
server.on("/inline", []() { |
||||||
|
server.send(200, "text/plain", "this works as well"); |
||||||
|
}); |
||||||
|
|
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
|
||||||
|
server.begin(); |
||||||
|
Serial.println("HTTPS server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
/*
|
||||||
|
HTTP Advanced Authentication example |
||||||
|
Created Mar 16, 2017 by Ahmed El-Sharnoby. |
||||||
|
This example code is in the public domain. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ArduinoOTA.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
const char* www_username = "admin"; |
||||||
|
const char* www_password = "esp8266"; |
||||||
|
// allows you to set the realm of authentication Default:"Login Required"
|
||||||
|
const char* www_realm = "Custom Auth Realm"; |
||||||
|
// the Content of the HTML response in case of Unautherized Access Default:empty
|
||||||
|
String authFailResponse = "Authentication Failed"; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
Serial.println("WiFi Connect Failed! Rebooting..."); |
||||||
|
delay(1000); |
||||||
|
ESP.restart(); |
||||||
|
} |
||||||
|
ArduinoOTA.begin(); |
||||||
|
|
||||||
|
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
|
||||||
|
{ |
||||||
|
return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse); |
||||||
|
} |
||||||
|
server.send(200, "text/plain", "Login OK"); |
||||||
|
}); |
||||||
|
server.begin(); |
||||||
|
|
||||||
|
Serial.print("Open http://"); |
||||||
|
Serial.print(WiFi.localIP()); |
||||||
|
Serial.println("/ in your browser to see it working"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
ArduinoOTA.handle(); |
||||||
|
server.handleClient(); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <ArduinoOTA.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
const char* www_username = "admin"; |
||||||
|
const char* www_password = "esp8266"; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) { |
||||||
|
Serial.println("WiFi Connect Failed! Rebooting..."); |
||||||
|
delay(1000); |
||||||
|
ESP.restart(); |
||||||
|
} |
||||||
|
ArduinoOTA.begin(); |
||||||
|
|
||||||
|
server.on("/", []() { |
||||||
|
if (!server.authenticate(www_username, www_password)) { |
||||||
|
return server.requestAuthentication(); |
||||||
|
} |
||||||
|
server.send(200, "text/plain", "Login OK"); |
||||||
|
}); |
||||||
|
server.begin(); |
||||||
|
|
||||||
|
Serial.print("Open http://"); |
||||||
|
Serial.print(WiFi.localIP()); |
||||||
|
Serial.println("/ in your browser to see it working"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
ArduinoOTA.handle(); |
||||||
|
server.handleClient(); |
||||||
|
} |
@ -0,0 +1,319 @@ |
|||||||
|
/*
|
||||||
|
SDWebServer - Example WebServer with SD Card backend for esp8266 |
||||||
|
|
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||||
|
This file is part of the ESP8266WebServer library for Arduino environment. |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
Have a FAT Formatted SD Card connected to the SPI port of the ESP8266 |
||||||
|
The web root is the SD Card root folder |
||||||
|
File extensions with more than 3 charecters are not supported by the SD Library |
||||||
|
File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter |
||||||
|
index.htm is the default index (works on subfolders as well) |
||||||
|
|
||||||
|
upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
|
||||||
|
|
||||||
|
*/ |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
#include <SPI.h> |
||||||
|
#include <SD.h> |
||||||
|
|
||||||
|
#define DBG_OUTPUT_PORT Serial |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
const char* host = "esp8266sd"; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
static bool hasSD = false; |
||||||
|
File uploadFile; |
||||||
|
|
||||||
|
|
||||||
|
void returnOK() { |
||||||
|
server.send(200, "text/plain", ""); |
||||||
|
} |
||||||
|
|
||||||
|
void returnFail(String msg) { |
||||||
|
server.send(500, "text/plain", msg + "\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
bool loadFromSdCard(String path) { |
||||||
|
String dataType = "text/plain"; |
||||||
|
if (path.endsWith("/")) { |
||||||
|
path += "index.htm"; |
||||||
|
} |
||||||
|
|
||||||
|
if (path.endsWith(".src")) { |
||||||
|
path = path.substring(0, path.lastIndexOf(".")); |
||||||
|
} else if (path.endsWith(".htm")) { |
||||||
|
dataType = "text/html"; |
||||||
|
} else if (path.endsWith(".css")) { |
||||||
|
dataType = "text/css"; |
||||||
|
} else if (path.endsWith(".js")) { |
||||||
|
dataType = "application/javascript"; |
||||||
|
} else if (path.endsWith(".png")) { |
||||||
|
dataType = "image/png"; |
||||||
|
} else if (path.endsWith(".gif")) { |
||||||
|
dataType = "image/gif"; |
||||||
|
} else if (path.endsWith(".jpg")) { |
||||||
|
dataType = "image/jpeg"; |
||||||
|
} else if (path.endsWith(".ico")) { |
||||||
|
dataType = "image/x-icon"; |
||||||
|
} else if (path.endsWith(".xml")) { |
||||||
|
dataType = "text/xml"; |
||||||
|
} else if (path.endsWith(".pdf")) { |
||||||
|
dataType = "application/pdf"; |
||||||
|
} else if (path.endsWith(".zip")) { |
||||||
|
dataType = "application/zip"; |
||||||
|
} |
||||||
|
|
||||||
|
File dataFile = SD.open(path.c_str()); |
||||||
|
if (dataFile.isDirectory()) { |
||||||
|
path += "/index.htm"; |
||||||
|
dataType = "text/html"; |
||||||
|
dataFile = SD.open(path.c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
if (!dataFile) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (server.hasArg("download")) { |
||||||
|
dataType = "application/octet-stream"; |
||||||
|
} |
||||||
|
|
||||||
|
if (server.streamFile(dataFile, dataType) != dataFile.size()) { |
||||||
|
DBG_OUTPUT_PORT.println("Sent less data than expected!"); |
||||||
|
} |
||||||
|
|
||||||
|
dataFile.close(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void handleFileUpload() { |
||||||
|
if (server.uri() != "/edit") { |
||||||
|
return; |
||||||
|
} |
||||||
|
HTTPUpload& upload = server.upload(); |
||||||
|
if (upload.status == UPLOAD_FILE_START) { |
||||||
|
if (SD.exists((char *)upload.filename.c_str())) { |
||||||
|
SD.remove((char *)upload.filename.c_str()); |
||||||
|
} |
||||||
|
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); |
||||||
|
DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename); |
||||||
|
} else if (upload.status == UPLOAD_FILE_WRITE) { |
||||||
|
if (uploadFile) { |
||||||
|
uploadFile.write(upload.buf, upload.currentSize); |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize); |
||||||
|
} else if (upload.status == UPLOAD_FILE_END) { |
||||||
|
if (uploadFile) { |
||||||
|
uploadFile.close(); |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void deleteRecursive(String path) { |
||||||
|
File file = SD.open((char *)path.c_str()); |
||||||
|
if (!file.isDirectory()) { |
||||||
|
file.close(); |
||||||
|
SD.remove((char *)path.c_str()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
file.rewindDirectory(); |
||||||
|
while (true) { |
||||||
|
File entry = file.openNextFile(); |
||||||
|
if (!entry) { |
||||||
|
break; |
||||||
|
} |
||||||
|
String entryPath = path + "/" + entry.name(); |
||||||
|
if (entry.isDirectory()) { |
||||||
|
entry.close(); |
||||||
|
deleteRecursive(entryPath); |
||||||
|
} else { |
||||||
|
entry.close(); |
||||||
|
SD.remove((char *)entryPath.c_str()); |
||||||
|
} |
||||||
|
yield(); |
||||||
|
} |
||||||
|
|
||||||
|
SD.rmdir((char *)path.c_str()); |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
|
||||||
|
void handleDelete() { |
||||||
|
if (server.args() == 0) { |
||||||
|
return returnFail("BAD ARGS"); |
||||||
|
} |
||||||
|
String path = server.arg(0); |
||||||
|
if (path == "/" || !SD.exists((char *)path.c_str())) { |
||||||
|
returnFail("BAD PATH"); |
||||||
|
return; |
||||||
|
} |
||||||
|
deleteRecursive(path); |
||||||
|
returnOK(); |
||||||
|
} |
||||||
|
|
||||||
|
void handleCreate() { |
||||||
|
if (server.args() == 0) { |
||||||
|
return returnFail("BAD ARGS"); |
||||||
|
} |
||||||
|
String path = server.arg(0); |
||||||
|
if (path == "/" || SD.exists((char *)path.c_str())) { |
||||||
|
returnFail("BAD PATH"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (path.indexOf('.') > 0) { |
||||||
|
File file = SD.open((char *)path.c_str(), FILE_WRITE); |
||||||
|
if (file) { |
||||||
|
file.write((const char *)0); |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
SD.mkdir((char *)path.c_str()); |
||||||
|
} |
||||||
|
returnOK(); |
||||||
|
} |
||||||
|
|
||||||
|
void printDirectory() { |
||||||
|
if (!server.hasArg("dir")) { |
||||||
|
return returnFail("BAD ARGS"); |
||||||
|
} |
||||||
|
String path = server.arg("dir"); |
||||||
|
if (path != "/" && !SD.exists((char *)path.c_str())) { |
||||||
|
return returnFail("BAD PATH"); |
||||||
|
} |
||||||
|
File dir = SD.open((char *)path.c_str()); |
||||||
|
path = String(); |
||||||
|
if (!dir.isDirectory()) { |
||||||
|
dir.close(); |
||||||
|
return returnFail("NOT DIR"); |
||||||
|
} |
||||||
|
dir.rewindDirectory(); |
||||||
|
server.setContentLength(CONTENT_LENGTH_UNKNOWN); |
||||||
|
server.send(200, "text/json", ""); |
||||||
|
WiFiClient client = server.client(); |
||||||
|
|
||||||
|
server.sendContent("["); |
||||||
|
for (int cnt = 0; true; ++cnt) { |
||||||
|
File entry = dir.openNextFile(); |
||||||
|
if (!entry) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
String output; |
||||||
|
if (cnt > 0) { |
||||||
|
output = ','; |
||||||
|
} |
||||||
|
|
||||||
|
output += "{\"type\":\""; |
||||||
|
output += (entry.isDirectory()) ? "dir" : "file"; |
||||||
|
output += "\",\"name\":\""; |
||||||
|
output += entry.name(); |
||||||
|
output += "\""; |
||||||
|
output += "}"; |
||||||
|
server.sendContent(output); |
||||||
|
entry.close(); |
||||||
|
} |
||||||
|
server.sendContent("]"); |
||||||
|
dir.close(); |
||||||
|
} |
||||||
|
|
||||||
|
void handleNotFound() { |
||||||
|
if (hasSD && loadFromSdCard(server.uri())) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String message = "SDCARD Not Detected\n\n"; |
||||||
|
message += "URI: "; |
||||||
|
message += server.uri(); |
||||||
|
message += "\nMethod: "; |
||||||
|
message += (server.method() == HTTP_GET) ? "GET" : "POST"; |
||||||
|
message += "\nArguments: "; |
||||||
|
message += server.args(); |
||||||
|
message += "\n"; |
||||||
|
for (uint8_t i = 0; i < server.args(); i++) { |
||||||
|
message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; |
||||||
|
} |
||||||
|
server.send(404, "text/plain", message); |
||||||
|
DBG_OUTPUT_PORT.print(message); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
DBG_OUTPUT_PORT.begin(115200); |
||||||
|
DBG_OUTPUT_PORT.setDebugOutput(true); |
||||||
|
DBG_OUTPUT_PORT.print("\n"); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
DBG_OUTPUT_PORT.print("Connecting to "); |
||||||
|
DBG_OUTPUT_PORT.println(ssid); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
uint8_t i = 0; |
||||||
|
while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
|
||||||
|
delay(500); |
||||||
|
} |
||||||
|
if (i == 21) { |
||||||
|
DBG_OUTPUT_PORT.print("Could not connect to"); |
||||||
|
DBG_OUTPUT_PORT.println(ssid); |
||||||
|
while (1) { |
||||||
|
delay(500); |
||||||
|
} |
||||||
|
} |
||||||
|
DBG_OUTPUT_PORT.print("Connected! IP address: "); |
||||||
|
DBG_OUTPUT_PORT.println(WiFi.localIP()); |
||||||
|
|
||||||
|
if (MDNS.begin(host)) { |
||||||
|
MDNS.addService("http", "tcp", 80); |
||||||
|
DBG_OUTPUT_PORT.println("MDNS responder started"); |
||||||
|
DBG_OUTPUT_PORT.print("You can now connect to http://"); |
||||||
|
DBG_OUTPUT_PORT.print(host); |
||||||
|
DBG_OUTPUT_PORT.println(".local"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
server.on("/list", HTTP_GET, printDirectory); |
||||||
|
server.on("/edit", HTTP_DELETE, handleDelete); |
||||||
|
server.on("/edit", HTTP_PUT, handleCreate); |
||||||
|
server.on("/edit", HTTP_POST, []() { |
||||||
|
returnOK(); |
||||||
|
}, handleFileUpload); |
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
|
||||||
|
server.begin(); |
||||||
|
DBG_OUTPUT_PORT.println("HTTP server started"); |
||||||
|
|
||||||
|
if (SD.begin(SS)) { |
||||||
|
DBG_OUTPUT_PORT.println("SD Card initialized."); |
||||||
|
hasSD = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,674 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<title>SD Editor</title> |
||||||
|
<style type="text/css" media="screen"> |
||||||
|
.contextMenu { |
||||||
|
z-index: 300; |
||||||
|
position: absolute; |
||||||
|
left: 5px; |
||||||
|
border: 1px solid #444; |
||||||
|
background-color: #F5F5F5; |
||||||
|
display: none; |
||||||
|
box-shadow: 0 0 10px rgba( 0, 0, 0, .4 ); |
||||||
|
font-size: 12px; |
||||||
|
font-family: sans-serif; |
||||||
|
font-weight:bold; |
||||||
|
} |
||||||
|
.contextMenu ul { |
||||||
|
list-style: none; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
.contextMenu li { |
||||||
|
position: relative; |
||||||
|
min-width: 60px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
.contextMenu span { |
||||||
|
color: #444; |
||||||
|
display: inline-block; |
||||||
|
padding: 6px; |
||||||
|
} |
||||||
|
.contextMenu li:hover { background: #444; } |
||||||
|
.contextMenu li:hover span { color: #EEE; } |
||||||
|
|
||||||
|
.css-treeview ul, .css-treeview li { |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
list-style: none; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input { |
||||||
|
position: absolute; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview { |
||||||
|
font: normal 11px Verdana, Arial, Sans-serif; |
||||||
|
-moz-user-select: none; |
||||||
|
-webkit-user-select: none; |
||||||
|
user-select: none; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview span { |
||||||
|
color: #00f; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview span:hover { |
||||||
|
text-decoration: underline; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input + label + ul { |
||||||
|
margin: 0 0 0 22px; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input ~ ul { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview label, .css-treeview label::before { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input:disabled + label { |
||||||
|
cursor: default; |
||||||
|
opacity: .6; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input:checked:not(:disabled) ~ ul { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview label, .css-treeview label::before { |
||||||
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACgCAYAAAAFOewUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrslM1u00AQgGdthyalFFOK+ClIIKQKyqUVQvTEE3DmAhLwAhU8QZoH4A2Q2gMSFace4MCtJ8SPBFwAkRuiHKpA6sRN/Lu7zG5i14kctaUqRGhGXnu9O/Pt7MzsMiklvF+9t2kWTDvyIrAsA0aKRRi1T0C/hJ4LUbt5/8rNpWVlp8RSr9J40b48fxFaTQ9+ft8EZ6MJYb0Ok+dnYGpmPgXwKIAvLx8vYXc5GdMAQJgQEkpjRTh36TS2U+DWW/D17WuYgm8pwJyY1npZsZKOxImOV1I/h4+O6vEg5GCZBpgmA6hX8wHKUHDRBXQYicQ4rlc3Tf0VMs8DHBS864F2YFspjgUYjKX/Az3gsdQd2eeBHwmdGWXHcgBGSkZXOXohcEXebRoQcAgjqediNY+AVyu3Z3sAKqfKoGMsewBeEIOPgQxxPJIjcGH6qtL/0AdADzKGnuuD+2tLK7Q8DhHHbOBW+KEzcHLuYc82MkEUekLiwuvVH+guQBQzOG4XdAb8EOcRcqQvDkY2iCLuxECJ43JobMXoutqGgDa2T7UqLKwt9KRyuxKVByqVXXqIoCCUCAqhUOioTWC7G4TQEOD0APy2/7G2Xpu1J4+lxeQ4TXBbITDpoVelRN/BVFbwu5oMMJUBhoXy5tmdRcMwymP2OLQaLjx9/vnBo6V3K6izATmSnMa0Dq7ferIohJhr1p01zrlz49rZF4OMs8JkX23vVQzYp+wbYGV/KpXKjvspl8tsIKCrMNAYFxj2GKS5ZWxg4ewKsJfaGMIY5KXqPz8LBBj6+yDvVP79+yDp/9F9oIx3OisHWwe7Oal0HxCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgwD8E/BZgAP0qhKj3rXO7AAAAAElFTkSuQmCC") no-repeat; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview label, .css-treeview span, .css-treeview label::before { |
||||||
|
display: inline-block; |
||||||
|
height: 16px; |
||||||
|
line-height: 16px; |
||||||
|
vertical-align: middle; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview label { |
||||||
|
background-position: 18px 0; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview label::before { |
||||||
|
content: ""; |
||||||
|
width: 16px; |
||||||
|
margin: 0 22px 0 0; |
||||||
|
vertical-align: middle; |
||||||
|
background-position: 0 -32px; |
||||||
|
} |
||||||
|
|
||||||
|
.css-treeview input:checked + label::before { |
||||||
|
background-position: 0 -16px; |
||||||
|
} |
||||||
|
|
||||||
|
/* webkit adjacent element selector bugfix */ |
||||||
|
@media screen and (-webkit-min-device-pixel-ratio:0) |
||||||
|
{ |
||||||
|
.css-treeview{ |
||||||
|
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s; |
||||||
|
} |
||||||
|
|
||||||
|
@-webkit-keyframes webkit-adjacent-element-selector-bugfix |
||||||
|
{ |
||||||
|
from { |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
to { |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#uploader { |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
left: 0; |
||||||
|
height:28px; |
||||||
|
line-height: 24px; |
||||||
|
padding-left: 10px; |
||||||
|
background-color: #444; |
||||||
|
color:#EEE; |
||||||
|
} |
||||||
|
#tree { |
||||||
|
position: absolute; |
||||||
|
top: 28px; |
||||||
|
bottom: 0; |
||||||
|
left: 0; |
||||||
|
width:200px; |
||||||
|
padding: 8px; |
||||||
|
} |
||||||
|
#editor, #preview { |
||||||
|
position: absolute; |
||||||
|
top: 28px; |
||||||
|
right: 0; |
||||||
|
bottom: 0; |
||||||
|
left: 200px; |
||||||
|
} |
||||||
|
#preview { |
||||||
|
background-color: #EEE; |
||||||
|
padding:5px; |
||||||
|
} |
||||||
|
</style> |
||||||
|
<script> |
||||||
|
function createFileUploader(element, tree, editor){ |
||||||
|
var xmlHttp; |
||||||
|
var input = document.createElement("input"); |
||||||
|
input.type = "file"; |
||||||
|
input.multiple = false; |
||||||
|
input.name = "data"; |
||||||
|
document.getElementById(element).appendChild(input); |
||||||
|
var path = document.createElement("input"); |
||||||
|
path.id = "upload-path"; |
||||||
|
path.type = "text"; |
||||||
|
path.name = "path"; |
||||||
|
path.defaultValue = "/"; |
||||||
|
document.getElementById(element).appendChild(path); |
||||||
|
var button = document.createElement("button"); |
||||||
|
button.innerHTML = 'Upload'; |
||||||
|
document.getElementById(element).appendChild(button); |
||||||
|
var mkdir = document.createElement("button"); |
||||||
|
mkdir.innerHTML = 'MkDir'; |
||||||
|
document.getElementById(element).appendChild(mkdir); |
||||||
|
var mkfile = document.createElement("button"); |
||||||
|
mkfile.innerHTML = 'MkFile'; |
||||||
|
document.getElementById(element).appendChild(mkfile); |
||||||
|
|
||||||
|
function httpPostProcessRequest(){ |
||||||
|
if (xmlHttp.readyState == 4){ |
||||||
|
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); |
||||||
|
else { |
||||||
|
tree.refreshPath(path.value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
function createPath(p){ |
||||||
|
xmlHttp = new XMLHttpRequest(); |
||||||
|
xmlHttp.onreadystatechange = httpPostProcessRequest; |
||||||
|
var formData = new FormData(); |
||||||
|
formData.append("path", p); |
||||||
|
xmlHttp.open("PUT", "/edit"); |
||||||
|
xmlHttp.send(formData); |
||||||
|
} |
||||||
|
|
||||||
|
mkfile.onclick = function(e){ |
||||||
|
if(path.value.indexOf(".") === -1) return; |
||||||
|
createPath(path.value); |
||||||
|
editor.loadUrl(path.value); |
||||||
|
}; |
||||||
|
mkdir.onclick = function(e){ |
||||||
|
if(path.value.length < 2) return; |
||||||
|
var dir = path.value |
||||||
|
if(dir.indexOf(".") !== -1){ |
||||||
|
if(dir.lastIndexOf("/") === 0) return; |
||||||
|
dir = dir.substring(0, dir.lastIndexOf("/")); |
||||||
|
} |
||||||
|
createPath(dir); |
||||||
|
}; |
||||||
|
button.onclick = function(e){ |
||||||
|
if(input.files.length === 0){ |
||||||
|
return; |
||||||
|
} |
||||||
|
xmlHttp = new XMLHttpRequest(); |
||||||
|
xmlHttp.onreadystatechange = httpPostProcessRequest; |
||||||
|
var formData = new FormData(); |
||||||
|
formData.append("data", input.files[0], path.value); |
||||||
|
xmlHttp.open("POST", "/edit"); |
||||||
|
xmlHttp.send(formData); |
||||||
|
} |
||||||
|
input.onchange = function(e){ |
||||||
|
if(input.files.length === 0) return; |
||||||
|
var filename = input.files[0].name; |
||||||
|
var ext = /(?:\.([^.]+))?$/.exec(filename)[1]; |
||||||
|
var name = /(.*)\.[^.]+$/.exec(filename)[1]; |
||||||
|
if(typeof name !== undefined){ |
||||||
|
if(name.length > 8) name = name.substring(0, 8); |
||||||
|
filename = name; |
||||||
|
} |
||||||
|
if(typeof ext !== undefined){ |
||||||
|
if(ext === "html") ext = "htm"; |
||||||
|
else if(ext === "jpeg") ext = "jpg"; |
||||||
|
filename = filename + "." + ext; |
||||||
|
} |
||||||
|
if(path.value === "/" || path.value.lastIndexOf("/") === 0){ |
||||||
|
path.value = "/"+filename; |
||||||
|
} else { |
||||||
|
path.value = path.value.substring(0, path.value.lastIndexOf("/")+1)+filename; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function createTree(element, editor){ |
||||||
|
var preview = document.getElementById("preview"); |
||||||
|
var treeRoot = document.createElement("div"); |
||||||
|
treeRoot.className = "css-treeview"; |
||||||
|
document.getElementById(element).appendChild(treeRoot); |
||||||
|
|
||||||
|
function loadDownload(path){ |
||||||
|
document.getElementById('download-frame').src = path+"?download=true"; |
||||||
|
} |
||||||
|
|
||||||
|
function loadPreview(path){ |
||||||
|
document.getElementById("editor").style.display = "none"; |
||||||
|
preview.style.display = "block"; |
||||||
|
preview.innerHTML = '<img src="'+path+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />'; |
||||||
|
} |
||||||
|
|
||||||
|
function fillFolderMenu(el, path){ |
||||||
|
var list = document.createElement("ul"); |
||||||
|
el.appendChild(list); |
||||||
|
var action = document.createElement("li"); |
||||||
|
list.appendChild(action); |
||||||
|
var isChecked = document.getElementById(path).checked; |
||||||
|
var expnd = document.createElement("li"); |
||||||
|
list.appendChild(expnd); |
||||||
|
if(isChecked){ |
||||||
|
expnd.innerHTML = "<span>Collapse</span>"; |
||||||
|
expnd.onclick = function(e){ |
||||||
|
document.getElementById(path).checked = false; |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
var refrsh = document.createElement("li"); |
||||||
|
list.appendChild(refrsh); |
||||||
|
refrsh.innerHTML = "<span>Refresh</span>"; |
||||||
|
refrsh.onclick = function(e){ |
||||||
|
var leaf = document.getElementById(path).parentNode; |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} else { |
||||||
|
expnd.innerHTML = "<span>Expand</span>"; |
||||||
|
expnd.onclick = function(e){ |
||||||
|
document.getElementById(path).checked = true; |
||||||
|
var leaf = document.getElementById(path).parentNode; |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} |
||||||
|
var upload = document.createElement("li"); |
||||||
|
list.appendChild(upload); |
||||||
|
upload.innerHTML = "<span>Upload</span>"; |
||||||
|
upload.onclick = function(e){ |
||||||
|
var pathEl = document.getElementById("upload-path"); |
||||||
|
if(pathEl){ |
||||||
|
var subPath = pathEl.value; |
||||||
|
if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath; |
||||||
|
else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath; |
||||||
|
} |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
var delFile = document.createElement("li"); |
||||||
|
list.appendChild(delFile); |
||||||
|
delFile.innerHTML = "<span>Delete</span>"; |
||||||
|
delFile.onclick = function(e){ |
||||||
|
httpDelete(path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function fillFileMenu(el, path){ |
||||||
|
var list = document.createElement("ul"); |
||||||
|
el.appendChild(list); |
||||||
|
var action = document.createElement("li"); |
||||||
|
list.appendChild(action); |
||||||
|
if(isTextFile(path)){ |
||||||
|
action.innerHTML = "<span>Edit</span>"; |
||||||
|
action.onclick = function(e){ |
||||||
|
editor.loadUrl(path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} else if(isImageFile(path)){ |
||||||
|
action.innerHTML = "<span>Preview</span>"; |
||||||
|
action.onclick = function(e){ |
||||||
|
loadPreview(path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} |
||||||
|
var download = document.createElement("li"); |
||||||
|
list.appendChild(download); |
||||||
|
download.innerHTML = "<span>Download</span>"; |
||||||
|
download.onclick = function(e){ |
||||||
|
loadDownload(path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
var delFile = document.createElement("li"); |
||||||
|
list.appendChild(delFile); |
||||||
|
delFile.innerHTML = "<span>Delete</span>"; |
||||||
|
delFile.onclick = function(e){ |
||||||
|
httpDelete(path); |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function showContextMenu(e, path, isfile){ |
||||||
|
var divContext = document.createElement("div"); |
||||||
|
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop; |
||||||
|
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft; |
||||||
|
var left = e.clientX + scrollLeft; |
||||||
|
var top = e.clientY + scrollTop; |
||||||
|
divContext.className = 'contextMenu'; |
||||||
|
divContext.style.display = 'block'; |
||||||
|
divContext.style.left = left + 'px'; |
||||||
|
divContext.style.top = top + 'px'; |
||||||
|
if(isfile) fillFileMenu(divContext, path); |
||||||
|
else fillFolderMenu(divContext, path); |
||||||
|
document.body.appendChild(divContext); |
||||||
|
var width = divContext.offsetWidth; |
||||||
|
var height = divContext.offsetHeight; |
||||||
|
divContext.onmouseout = function(e){ |
||||||
|
if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){ |
||||||
|
if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(divContext); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function createTreeLeaf(path, name, size){ |
||||||
|
var leaf = document.createElement("li"); |
||||||
|
leaf.id = (((path == "/")?"":path)+"/"+name).toLowerCase(); |
||||||
|
var label = document.createElement("span"); |
||||||
|
label.textContent = name.toLowerCase(); |
||||||
|
leaf.appendChild(label); |
||||||
|
leaf.onclick = function(e){ |
||||||
|
if(isTextFile(leaf.id)){ |
||||||
|
editor.loadUrl(leaf.id); |
||||||
|
} else if(isImageFile(leaf.id)){ |
||||||
|
loadPreview(leaf.id); |
||||||
|
} |
||||||
|
}; |
||||||
|
leaf.oncontextmenu = function(e){ |
||||||
|
e.preventDefault(); |
||||||
|
e.stopPropagation(); |
||||||
|
showContextMenu(e, leaf.id, true); |
||||||
|
}; |
||||||
|
return leaf; |
||||||
|
} |
||||||
|
|
||||||
|
function createTreeBranch(path, name, disabled){ |
||||||
|
var leaf = document.createElement("li"); |
||||||
|
var check = document.createElement("input"); |
||||||
|
check.type = "checkbox"; |
||||||
|
check.id = (((path == "/")?"":path)+"/"+name).toLowerCase(); |
||||||
|
if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; |
||||||
|
leaf.appendChild(check); |
||||||
|
var label = document.createElement("label"); |
||||||
|
label.for = check.id; |
||||||
|
label.textContent = name.toLowerCase(); |
||||||
|
leaf.appendChild(label); |
||||||
|
check.onchange = function(e){ |
||||||
|
if(check.checked){ |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, check.id); |
||||||
|
} |
||||||
|
}; |
||||||
|
label.onclick = function(e){ |
||||||
|
if(!check.checked){ |
||||||
|
check.checked = true; |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, check.id); |
||||||
|
} else { |
||||||
|
check.checked = false; |
||||||
|
} |
||||||
|
}; |
||||||
|
leaf.oncontextmenu = function(e){ |
||||||
|
e.preventDefault(); |
||||||
|
e.stopPropagation(); |
||||||
|
showContextMenu(e, check.id, false); |
||||||
|
} |
||||||
|
return leaf; |
||||||
|
} |
||||||
|
|
||||||
|
function addList(parent, path, items){ |
||||||
|
var list = document.createElement("ul"); |
||||||
|
parent.appendChild(list); |
||||||
|
var ll = items.length; |
||||||
|
for(var i = 0; i < ll; i++){ |
||||||
|
var item = items[i]; |
||||||
|
var itemEl; |
||||||
|
if(item.type === "file"){ |
||||||
|
itemEl = createTreeLeaf(path, item.name, item.size); |
||||||
|
} else { |
||||||
|
itemEl = createTreeBranch(path, item.name); |
||||||
|
} |
||||||
|
list.appendChild(itemEl); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function isTextFile(path){ |
||||||
|
var ext = /(?:\.([^.]+))?$/.exec(path)[1]; |
||||||
|
if(typeof ext !== undefined){ |
||||||
|
switch(ext){ |
||||||
|
case "txt": |
||||||
|
case "htm": |
||||||
|
case "html": |
||||||
|
case "js": |
||||||
|
case "json": |
||||||
|
case "c": |
||||||
|
case "h": |
||||||
|
case "cpp": |
||||||
|
case "css": |
||||||
|
case "xml": |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
function isImageFile(path){ |
||||||
|
var ext = /(?:\.([^.]+))?$/.exec(path)[1]; |
||||||
|
if(typeof ext !== undefined){ |
||||||
|
switch(ext){ |
||||||
|
case "png": |
||||||
|
case "jpg": |
||||||
|
case "gif": |
||||||
|
case "ico": |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
this.refreshPath = function(path){ |
||||||
|
if(path.lastIndexOf('/') < 1){ |
||||||
|
path = '/'; |
||||||
|
treeRoot.removeChild(treeRoot.childNodes[0]); |
||||||
|
httpGet(treeRoot, "/"); |
||||||
|
} else { |
||||||
|
path = path.substring(0, path.lastIndexOf('/')); |
||||||
|
var leaf = document.getElementById(path).parentNode; |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, path); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
function delCb(path){ |
||||||
|
return function(){ |
||||||
|
if (xmlHttp.readyState == 4){ |
||||||
|
if(xmlHttp.status != 200){ |
||||||
|
alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); |
||||||
|
} else { |
||||||
|
if(path.lastIndexOf('/') < 1){ |
||||||
|
path = '/'; |
||||||
|
treeRoot.removeChild(treeRoot.childNodes[0]); |
||||||
|
httpGet(treeRoot, "/"); |
||||||
|
} else { |
||||||
|
path = path.substring(0, path.lastIndexOf('/')); |
||||||
|
var leaf = document.getElementById(path).parentNode; |
||||||
|
if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); |
||||||
|
httpGet(leaf, path); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function httpDelete(filename){ |
||||||
|
xmlHttp = new XMLHttpRequest(); |
||||||
|
xmlHttp.onreadystatechange = delCb(filename); |
||||||
|
var formData = new FormData(); |
||||||
|
formData.append("path", filename); |
||||||
|
xmlHttp.open("DELETE", "/edit"); |
||||||
|
xmlHttp.send(formData); |
||||||
|
} |
||||||
|
|
||||||
|
function getCb(parent, path){ |
||||||
|
return function(){ |
||||||
|
if (xmlHttp.readyState == 4){ |
||||||
|
//clear loading |
||||||
|
if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function httpGet(parent, path){ |
||||||
|
xmlHttp = new XMLHttpRequest(parent, path); |
||||||
|
xmlHttp.onreadystatechange = getCb(parent, path); |
||||||
|
xmlHttp.open("GET", "/list?dir="+path, true); |
||||||
|
xmlHttp.send(null); |
||||||
|
//start loading |
||||||
|
} |
||||||
|
|
||||||
|
httpGet(treeRoot, "/"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
function createEditor(element, file, lang, theme, type){ |
||||||
|
function getLangFromFilename(filename){ |
||||||
|
var lang = "plain"; |
||||||
|
var ext = /(?:\.([^.]+))?$/.exec(filename)[1]; |
||||||
|
if(typeof ext !== undefined){ |
||||||
|
switch(ext){ |
||||||
|
case "txt": lang = "plain"; break; |
||||||
|
case "htm": lang = "html"; break; |
||||||
|
case "js": lang = "javascript"; break; |
||||||
|
case "c": lang = "c_cpp"; break; |
||||||
|
case "cpp": lang = "c_cpp"; break; |
||||||
|
case "css": |
||||||
|
case "scss": |
||||||
|
case "php": |
||||||
|
case "html": |
||||||
|
case "json": |
||||||
|
case "xml": |
||||||
|
lang = ext; |
||||||
|
} |
||||||
|
} |
||||||
|
return lang; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof file === "undefined") file = "/index.htm"; |
||||||
|
|
||||||
|
if(typeof lang === "undefined"){ |
||||||
|
lang = getLangFromFilename(file); |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof theme === "undefined") theme = "textmate"; |
||||||
|
|
||||||
|
if(typeof type === "undefined"){ |
||||||
|
type = "text/"+lang; |
||||||
|
if(lang === "c_cpp") type = "text/plain"; |
||||||
|
} |
||||||
|
|
||||||
|
var xmlHttp = null; |
||||||
|
var editor = ace.edit(element); |
||||||
|
|
||||||
|
//post |
||||||
|
function httpPostProcessRequest(){ |
||||||
|
if (xmlHttp.readyState == 4){ |
||||||
|
if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); |
||||||
|
} |
||||||
|
} |
||||||
|
function httpPost(filename, data, type){ |
||||||
|
xmlHttp = new XMLHttpRequest(); |
||||||
|
xmlHttp.onreadystatechange = httpPostProcessRequest; |
||||||
|
var formData = new FormData(); |
||||||
|
formData.append("data", new Blob([data], { type: type }), filename); |
||||||
|
xmlHttp.open("POST", "/edit"); |
||||||
|
xmlHttp.send(formData); |
||||||
|
} |
||||||
|
//get |
||||||
|
function httpGetProcessRequest(){ |
||||||
|
if (xmlHttp.readyState == 4){ |
||||||
|
document.getElementById("preview").style.display = "none"; |
||||||
|
document.getElementById("editor").style.display = "block"; |
||||||
|
if(xmlHttp.status == 200) editor.setValue(xmlHttp.responseText); |
||||||
|
else editor.setValue(""); |
||||||
|
editor.clearSelection(); |
||||||
|
} |
||||||
|
} |
||||||
|
function httpGet(theUrl){ |
||||||
|
xmlHttp = new XMLHttpRequest(); |
||||||
|
xmlHttp.onreadystatechange = httpGetProcessRequest; |
||||||
|
xmlHttp.open("GET", theUrl, true); |
||||||
|
xmlHttp.send(null); |
||||||
|
} |
||||||
|
|
||||||
|
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang); |
||||||
|
editor.setTheme("ace/theme/"+theme); |
||||||
|
editor.$blockScrolling = Infinity; |
||||||
|
editor.getSession().setUseSoftTabs(true); |
||||||
|
editor.getSession().setTabSize(2); |
||||||
|
editor.setHighlightActiveLine(true); |
||||||
|
editor.setShowPrintMargin(false); |
||||||
|
editor.commands.addCommand({ |
||||||
|
name: 'saveCommand', |
||||||
|
bindKey: {win: 'Ctrl-S', mac: 'Command-S'}, |
||||||
|
exec: function(editor) { |
||||||
|
httpPost(file, editor.getValue()+"", type); |
||||||
|
}, |
||||||
|
readOnly: false |
||||||
|
}); |
||||||
|
editor.commands.addCommand({ |
||||||
|
name: 'undoCommand', |
||||||
|
bindKey: {win: 'Ctrl-Z', mac: 'Command-Z'}, |
||||||
|
exec: function(editor) { |
||||||
|
editor.getSession().getUndoManager().undo(false); |
||||||
|
}, |
||||||
|
readOnly: false |
||||||
|
}); |
||||||
|
editor.commands.addCommand({ |
||||||
|
name: 'redoCommand', |
||||||
|
bindKey: {win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z'}, |
||||||
|
exec: function(editor) { |
||||||
|
editor.getSession().getUndoManager().redo(false); |
||||||
|
}, |
||||||
|
readOnly: false |
||||||
|
}); |
||||||
|
httpGet(file); |
||||||
|
editor.loadUrl = function(filename){ |
||||||
|
file = filename; |
||||||
|
lang = getLangFromFilename(file); |
||||||
|
type = "text/"+lang; |
||||||
|
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang); |
||||||
|
httpGet(file); |
||||||
|
} |
||||||
|
return editor; |
||||||
|
} |
||||||
|
function onBodyLoad(){ |
||||||
|
var vars = {}; |
||||||
|
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); |
||||||
|
var editor = createEditor("editor", vars.file, vars.lang, vars.theme); |
||||||
|
var tree = createTree("tree", editor); |
||||||
|
createFileUploader("uploader", tree, editor); |
||||||
|
}; |
||||||
|
</script> |
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript" charset="utf-8"></script> |
||||||
|
</head> |
||||||
|
<body onload="onBodyLoad();"> |
||||||
|
<div id="uploader"></div> |
||||||
|
<div id="tree"></div> |
||||||
|
<div id="editor"></div> |
||||||
|
<div id="preview" style="display:none;"></div> |
||||||
|
<iframe id=download-frame style='display:none;'></iframe> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,22 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> |
||||||
|
<title>ESP Index</title> |
||||||
|
<style> |
||||||
|
body { |
||||||
|
background-color:black; |
||||||
|
color:white; |
||||||
|
} |
||||||
|
</style> |
||||||
|
<script type="text/javascript"> |
||||||
|
function onBodyLoad(){ |
||||||
|
console.log("we are loaded!!"); |
||||||
|
} |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body id="index" onload="onBodyLoad()"> |
||||||
|
<h1>ESP8266 Pin Functions</h1> |
||||||
|
<img src="pins.png" /> |
||||||
|
</body> |
||||||
|
</html> |
After Width: | Height: | Size: 174 KiB |
@ -0,0 +1,137 @@ |
|||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
|
||||||
|
//Check if header is present and correct
|
||||||
|
bool is_authenticated() { |
||||||
|
Serial.println("Enter is_authenticated"); |
||||||
|
if (server.hasHeader("Cookie")) { |
||||||
|
Serial.print("Found cookie: "); |
||||||
|
String cookie = server.header("Cookie"); |
||||||
|
Serial.println(cookie); |
||||||
|
if (cookie.indexOf("ESPSESSIONID=1") != -1) { |
||||||
|
Serial.println("Authentication Successful"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
Serial.println("Authentication Failed"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
//login page, also called for disconnect
|
||||||
|
void handleLogin() { |
||||||
|
String msg; |
||||||
|
if (server.hasHeader("Cookie")) { |
||||||
|
Serial.print("Found cookie: "); |
||||||
|
String cookie = server.header("Cookie"); |
||||||
|
Serial.println(cookie); |
||||||
|
} |
||||||
|
if (server.hasArg("DISCONNECT")) { |
||||||
|
Serial.println("Disconnection"); |
||||||
|
server.sendHeader("Location", "/login"); |
||||||
|
server.sendHeader("Cache-Control", "no-cache"); |
||||||
|
server.sendHeader("Set-Cookie", "ESPSESSIONID=0"); |
||||||
|
server.send(301); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) { |
||||||
|
if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") { |
||||||
|
server.sendHeader("Location", "/"); |
||||||
|
server.sendHeader("Cache-Control", "no-cache"); |
||||||
|
server.sendHeader("Set-Cookie", "ESPSESSIONID=1"); |
||||||
|
server.send(301); |
||||||
|
Serial.println("Log in Successful"); |
||||||
|
return; |
||||||
|
} |
||||||
|
msg = "Wrong username/password! try again."; |
||||||
|
Serial.println("Log in Failed"); |
||||||
|
} |
||||||
|
String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin<br>"; |
||||||
|
content += "User:<input type='text' name='USERNAME' placeholder='user name'><br>"; |
||||||
|
content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>"; |
||||||
|
content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>"; |
||||||
|
content += "You also can go <a href='/inline'>here</a></body></html>"; |
||||||
|
server.send(200, "text/html", content); |
||||||
|
} |
||||||
|
|
||||||
|
//root page can be accessed only if authentication is ok
|
||||||
|
void handleRoot() { |
||||||
|
Serial.println("Enter handleRoot"); |
||||||
|
String header; |
||||||
|
if (!is_authenticated()) { |
||||||
|
server.sendHeader("Location", "/login"); |
||||||
|
server.sendHeader("Cache-Control", "no-cache"); |
||||||
|
server.send(301); |
||||||
|
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>"; |
||||||
|
} |
||||||
|
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
|
||||||
|
void handleNotFound() { |
||||||
|
String message = "File Not Found\n\n"; |
||||||
|
message += "URI: "; |
||||||
|
message += server.uri(); |
||||||
|
message += "\nMethod: "; |
||||||
|
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"; |
||||||
|
} |
||||||
|
server.send(404, "text/plain", message); |
||||||
|
} |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
Serial.begin(115200); |
||||||
|
WiFi.mode(WIFI_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
Serial.println(""); |
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||||
|
delay(500); |
||||||
|
Serial.print("."); |
||||||
|
} |
||||||
|
Serial.println(""); |
||||||
|
Serial.print("Connected to "); |
||||||
|
Serial.println(ssid); |
||||||
|
Serial.print("IP address: "); |
||||||
|
Serial.println(WiFi.localIP()); |
||||||
|
|
||||||
|
|
||||||
|
server.on("/", handleRoot); |
||||||
|
server.on("/login", handleLogin); |
||||||
|
server.on("/inline", []() { |
||||||
|
server.send(200, "text/plain", "this works without need of authentication"); |
||||||
|
}); |
||||||
|
|
||||||
|
server.onNotFound(handleNotFound); |
||||||
|
//here the list of headers to be recorded
|
||||||
|
const char * headerkeys[] = {"User-Agent", "Cookie"} ; |
||||||
|
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); |
||||||
|
//ask server to track these headers
|
||||||
|
server.collectHeaders(headerkeys, headerkeyssize); |
||||||
|
server.begin(); |
||||||
|
Serial.println("HTTP server started"); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
#include <WiFiClient.h> |
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <ESP8266mDNS.h> |
||||||
|
|
||||||
|
#ifndef STASSID |
||||||
|
#define STASSID "your-ssid" |
||||||
|
#define STAPSK "your-password" |
||||||
|
#endif |
||||||
|
|
||||||
|
const char* host = "esp8266-webupdate"; |
||||||
|
const char* ssid = STASSID; |
||||||
|
const char* password = STAPSK; |
||||||
|
|
||||||
|
ESP8266WebServer server(80); |
||||||
|
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>"; |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
Serial.begin(115200); |
||||||
|
Serial.println(); |
||||||
|
Serial.println("Booting Sketch..."); |
||||||
|
WiFi.mode(WIFI_AP_STA); |
||||||
|
WiFi.begin(ssid, password); |
||||||
|
if (WiFi.waitForConnectResult() == WL_CONNECTED) { |
||||||
|
MDNS.begin(host); |
||||||
|
server.on("/", HTTP_GET, []() { |
||||||
|
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); |
||||||
|
} |
||||||
|
} 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(); |
||||||
|
}); |
||||||
|
server.begin(); |
||||||
|
MDNS.addService("http", "tcp", 80); |
||||||
|
|
||||||
|
Serial.printf("Ready! Open http://%s.local in your browser\n", host); |
||||||
|
} else { |
||||||
|
Serial.println("WiFi Failed"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
server.handleClient(); |
||||||
|
MDNS.update(); |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Ultrasound |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
ESP8266WebServer KEYWORD1 |
||||||
|
ESP8266WebServerSecure KEYWORD1 |
||||||
|
HTTPMethod KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
begin KEYWORD2 |
||||||
|
close KEYWORD2 |
||||||
|
stop KEYWORD2 |
||||||
|
handleClient KEYWORD2 |
||||||
|
on KEYWORD2 |
||||||
|
addHandler KEYWORD2 |
||||||
|
uri KEYWORD2 |
||||||
|
method KEYWORD2 |
||||||
|
client KEYWORD2 |
||||||
|
send KEYWORD2 |
||||||
|
send_P KEYWORD2 |
||||||
|
arg KEYWORD2 |
||||||
|
argName KEYWORD2 |
||||||
|
args KEYWORD2 |
||||||
|
hasArg KEYWORD2 |
||||||
|
onNotFound KEYWORD2 |
||||||
|
onFileUpload KEYWORD2 |
||||||
|
header KEYWORD2 |
||||||
|
headerName KEYWORD2 |
||||||
|
headers KEYWORD2 |
||||||
|
hasHeader KEYWORD2 |
||||||
|
hostHeader KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
HTTP_GET LITERAL1 |
||||||
|
HTTP_POST LITERAL1 |
||||||
|
HTTP_ANY LITERAL1 |
||||||
|
CONTENT_LENGTH_UNKNOWN LITERAL1 |
@ -0,0 +1,10 @@ |
|||||||
|
name=ESP8266WebServer |
||||||
|
version=1.0 |
||||||
|
author=Ivan Grokhotkov |
||||||
|
maintainer=Ivan Grokhtkov <ivan@esp8266.com> |
||||||
|
sentence=Simple web server library |
||||||
|
paragraph=The library supports HTTP GET and POST requests, provides argument parsing, handles one client at a time. |
||||||
|
category=Communication |
||||||
|
url= |
||||||
|
architectures=esp8266 |
||||||
|
dot_a_linkage=true |
@ -0,0 +1,670 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServer.cpp - Dead simple web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <libb64/cencode.h> |
||||||
|
#include "WiFiServer.h" |
||||||
|
#include "WiFiClient.h" |
||||||
|
#include "ESP8266WebServer.h" |
||||||
|
#include "FS.h" |
||||||
|
#include "detail/RequestHandlersImpl.h" |
||||||
|
|
||||||
|
//#define DEBUG_ESP_HTTP_SERVER
|
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT |
||||||
|
#else |
||||||
|
#define DEBUG_OUTPUT Serial |
||||||
|
#endif |
||||||
|
|
||||||
|
static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization"; |
||||||
|
static const char qop_auth[] PROGMEM = "qop=auth"; |
||||||
|
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\""; |
||||||
|
static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate"; |
||||||
|
static const char Content_Length[] PROGMEM = "Content-Length"; |
||||||
|
|
||||||
|
|
||||||
|
ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port) |
||||||
|
: _server(addr, port) |
||||||
|
, _currentMethod(HTTP_ANY) |
||||||
|
, _currentVersion(0) |
||||||
|
, _currentStatus(HC_NONE) |
||||||
|
, _statusChange(0) |
||||||
|
, _currentHandler(nullptr) |
||||||
|
, _firstHandler(nullptr) |
||||||
|
, _lastHandler(nullptr) |
||||||
|
, _currentArgCount(0) |
||||||
|
, _currentArgs(nullptr) |
||||||
|
, _postArgsLen(0) |
||||||
|
, _postArgs(nullptr) |
||||||
|
, _headerKeysCount(0) |
||||||
|
, _currentHeaders(nullptr) |
||||||
|
, _contentLength(0) |
||||||
|
, _chunked(false) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServer::ESP8266WebServer(int port) |
||||||
|
: _server(port) |
||||||
|
, _currentMethod(HTTP_ANY) |
||||||
|
, _currentVersion(0) |
||||||
|
, _currentStatus(HC_NONE) |
||||||
|
, _statusChange(0) |
||||||
|
, _currentHandler(nullptr) |
||||||
|
, _firstHandler(nullptr) |
||||||
|
, _lastHandler(nullptr) |
||||||
|
, _currentArgCount(0) |
||||||
|
, _currentArgs(nullptr) |
||||||
|
, _postArgsLen(0) |
||||||
|
, _postArgs(nullptr) |
||||||
|
, _headerKeysCount(0) |
||||||
|
, _currentHeaders(nullptr) |
||||||
|
, _contentLength(0) |
||||||
|
, _chunked(false) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServer::~ESP8266WebServer() { |
||||||
|
_server.close(); |
||||||
|
if (_currentHeaders) |
||||||
|
delete[]_currentHeaders; |
||||||
|
RequestHandler* handler = _firstHandler; |
||||||
|
while (handler) { |
||||||
|
RequestHandler* next = handler->next(); |
||||||
|
delete handler; |
||||||
|
handler = next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::begin() { |
||||||
|
close(); |
||||||
|
_server.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::begin(uint16_t port) { |
||||||
|
close(); |
||||||
|
_server.begin(port); |
||||||
|
} |
||||||
|
|
||||||
|
String ESP8266WebServer::_extractParam(String& authReq,const String& param,const char delimit) const { |
||||||
|
int _begin = authReq.indexOf(param); |
||||||
|
if (_begin == -1) |
||||||
|
return emptyString; |
||||||
|
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::authenticate(const char * username, const char * password){ |
||||||
|
if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) { |
||||||
|
String authReq = header(FPSTR(AUTHORIZATION_HEADER)); |
||||||
|
if(authReq.startsWith(F("Basic"))){ |
||||||
|
authReq = authReq.substring(6); |
||||||
|
authReq.trim(); |
||||||
|
char toencodeLen = strlen(username)+strlen(password)+1; |
||||||
|
char *toencode = new char[toencodeLen + 1]; |
||||||
|
if(toencode == NULL){ |
||||||
|
authReq = ""; |
||||||
|
return false; |
||||||
|
} |
||||||
|
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; |
||||||
|
if(encoded == NULL){ |
||||||
|
authReq = ""; |
||||||
|
delete[] toencode; |
||||||
|
return false; |
||||||
|
} |
||||||
|
sprintf(toencode, "%s:%s", username, password); |
||||||
|
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { |
||||||
|
authReq = ""; |
||||||
|
delete[] toencode; |
||||||
|
delete[] encoded; |
||||||
|
return true; |
||||||
|
} |
||||||
|
delete[] toencode; |
||||||
|
delete[] encoded; |
||||||
|
} else if(authReq.startsWith(F("Digest"))) { |
||||||
|
authReq = authReq.substring(7); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println(authReq); |
||||||
|
#endif |
||||||
|
String _username = _extractParam(authReq,F("username=\"")); |
||||||
|
if(!_username.length() || _username != String(username)) { |
||||||
|
authReq = ""; |
||||||
|
return false; |
||||||
|
} |
||||||
|
// extracting required parameters for RFC 2069 simpler Digest
|
||||||
|
String _realm = _extractParam(authReq, F("realm=\"")); |
||||||
|
String _nonce = _extractParam(authReq, F("nonce=\"")); |
||||||
|
String _uri = _extractParam(authReq, F("uri=\"")); |
||||||
|
String _response = _extractParam(authReq, F("response=\"")); |
||||||
|
String _opaque = _extractParam(authReq, F("opaque=\"")); |
||||||
|
|
||||||
|
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) { |
||||||
|
authReq = ""; |
||||||
|
return false; |
||||||
|
} |
||||||
|
if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) { |
||||||
|
authReq = ""; |
||||||
|
return false; |
||||||
|
} |
||||||
|
// parameters for the RFC 2617 newer Digest
|
||||||
|
String _nc,_cnonce; |
||||||
|
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { |
||||||
|
_nc = _extractParam(authReq, F("nc="), ','); |
||||||
|
_cnonce = _extractParam(authReq, F("cnonce=\"")); |
||||||
|
} |
||||||
|
MD5Builder md5; |
||||||
|
md5.begin(); |
||||||
|
md5.add(String(username) + ':' + _realm + ':' + String(password)); // md5 of the user:realm:user
|
||||||
|
md5.calculate(); |
||||||
|
String _H1 = md5.toString(); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("Hash of user:realm:pass=" + _H1); |
||||||
|
#endif |
||||||
|
md5.begin(); |
||||||
|
if(_currentMethod == HTTP_GET){ |
||||||
|
md5.add(String(F("GET:")) + _uri); |
||||||
|
}else if(_currentMethod == HTTP_POST){ |
||||||
|
md5.add(String(F("POST:")) + _uri); |
||||||
|
}else if(_currentMethod == HTTP_PUT){ |
||||||
|
md5.add(String(F("PUT:")) + _uri); |
||||||
|
}else if(_currentMethod == HTTP_DELETE){ |
||||||
|
md5.add(String(F("DELETE:")) + _uri); |
||||||
|
}else{ |
||||||
|
md5.add(String(F("GET:")) + _uri); |
||||||
|
} |
||||||
|
md5.calculate(); |
||||||
|
String _H2 = md5.toString(); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2); |
||||||
|
#endif |
||||||
|
md5.begin(); |
||||||
|
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { |
||||||
|
md5.add(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); |
||||||
|
} else { |
||||||
|
md5.add(_H1 + ':' + _nonce + ':' + _H2); |
||||||
|
} |
||||||
|
md5.calculate(); |
||||||
|
String _responsecheck = md5.toString(); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("The Proper response=" +_responsecheck); |
||||||
|
#endif |
||||||
|
if(_response == _responsecheck){ |
||||||
|
authReq = ""; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
authReq = ""; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
String ESP8266WebServer::_getRandomHexString() { |
||||||
|
char buffer[33]; // buffer to hold 32 Hex Digit + /0
|
||||||
|
int i; |
||||||
|
for(i = 0; i < 4; i++) { |
||||||
|
sprintf (buffer + (i*8), "%08x", RANDOM_REG32); |
||||||
|
} |
||||||
|
return String(buffer); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) { |
||||||
|
if(realm == NULL) { |
||||||
|
_srealm = String(F("Login Required")); |
||||||
|
} else { |
||||||
|
_srealm = String(realm); |
||||||
|
} |
||||||
|
if(mode == BASIC_AUTH) { |
||||||
|
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); |
||||||
|
} else { |
||||||
|
_snonce=_getRandomHexString(); |
||||||
|
_sopaque=_getRandomHexString(); |
||||||
|
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); |
||||||
|
} |
||||||
|
using namespace mime; |
||||||
|
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::on(const String &uri, ESP8266WebServer::THandlerFunction handler) { |
||||||
|
on(uri, HTTP_ANY, handler); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { |
||||||
|
on(uri, method, fn, _fileUploadHandler); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) { |
||||||
|
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::addHandler(RequestHandler* handler) { |
||||||
|
_addRequestHandler(handler); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { |
||||||
|
if (!_lastHandler) { |
||||||
|
_firstHandler = handler; |
||||||
|
_lastHandler = handler; |
||||||
|
} |
||||||
|
else { |
||||||
|
_lastHandler->next(handler); |
||||||
|
_lastHandler = handler; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { |
||||||
|
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::handleClient() { |
||||||
|
if (_currentStatus == HC_NONE) { |
||||||
|
WiFiClient client = _server.available(); |
||||||
|
if (!client) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("New client"); |
||||||
|
#endif |
||||||
|
|
||||||
|
_currentClient = client; |
||||||
|
_currentStatus = HC_WAIT_READ; |
||||||
|
_statusChange = millis(); |
||||||
|
} |
||||||
|
|
||||||
|
bool keepCurrentClient = false; |
||||||
|
bool callYield = false; |
||||||
|
|
||||||
|
if (_currentClient.connected()) { |
||||||
|
switch (_currentStatus) { |
||||||
|
case HC_NONE: |
||||||
|
// No-op to avoid C++ compiler warning
|
||||||
|
break; |
||||||
|
case HC_WAIT_READ: |
||||||
|
// Wait for data from client to become available
|
||||||
|
if (_currentClient.available()) { |
||||||
|
if (_parseRequest(_currentClient)) { |
||||||
|
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT); |
||||||
|
_contentLength = CONTENT_LENGTH_NOT_SET; |
||||||
|
_handleRequest(); |
||||||
|
|
||||||
|
if (_currentClient.connected()) { |
||||||
|
_currentStatus = HC_WAIT_CLOSE; |
||||||
|
_statusChange = millis(); |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { // !_currentClient.available()
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HC_WAIT_CLOSE: |
||||||
|
// Wait for client to close the connection
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!keepCurrentClient) { |
||||||
|
_currentClient = WiFiClient(); |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
_currentUpload.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
if (callYield) { |
||||||
|
yield(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::close() { |
||||||
|
_server.close(); |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
if(!_headerKeysCount) |
||||||
|
collectHeaders(0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::stop() { |
||||||
|
close(); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) { |
||||||
|
String headerLine = name; |
||||||
|
headerLine += F(": "); |
||||||
|
headerLine += value; |
||||||
|
headerLine += "\r\n"; |
||||||
|
|
||||||
|
if (first) { |
||||||
|
_responseHeaders = headerLine + _responseHeaders; |
||||||
|
} |
||||||
|
else { |
||||||
|
_responseHeaders += headerLine; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::setContentLength(const size_t contentLength) { |
||||||
|
_contentLength = contentLength; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { |
||||||
|
response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; |
||||||
|
response += String(code); |
||||||
|
response += ' '; |
||||||
|
response += _responseCodeToString(code); |
||||||
|
response += "\r\n"; |
||||||
|
|
||||||
|
using namespace mime; |
||||||
|
if (!content_type) |
||||||
|
content_type = mimeTable[html].mimeType; |
||||||
|
|
||||||
|
sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); |
||||||
|
if (_contentLength == CONTENT_LENGTH_NOT_SET) { |
||||||
|
sendHeader(String(FPSTR(Content_Length)), String(contentLength)); |
||||||
|
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { |
||||||
|
sendHeader(String(FPSTR(Content_Length)), String(_contentLength)); |
||||||
|
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
|
||||||
|
//let's do chunked
|
||||||
|
_chunked = true; |
||||||
|
sendHeader(String(F("Accept-Ranges")),String(F("none"))); |
||||||
|
sendHeader(String(F("Transfer-Encoding")),String(F("chunked"))); |
||||||
|
} |
||||||
|
sendHeader(String(F("Connection")), String(F("close"))); |
||||||
|
|
||||||
|
response += _responseHeaders; |
||||||
|
response += "\r\n"; |
||||||
|
_responseHeaders = ""; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::send(int code, const char* content_type, const String& content) { |
||||||
|
String header; |
||||||
|
// Can we asume the following?
|
||||||
|
//if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
|
||||||
|
// _contentLength = CONTENT_LENGTH_UNKNOWN;
|
||||||
|
_prepareHeader(header, code, content_type, content.length()); |
||||||
|
_currentClientWrite(header.c_str(), header.length()); |
||||||
|
if(content.length()) |
||||||
|
sendContent(content); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) { |
||||||
|
size_t contentLength = 0; |
||||||
|
|
||||||
|
if (content != NULL) { |
||||||
|
contentLength = strlen_P(content); |
||||||
|
} |
||||||
|
|
||||||
|
String header; |
||||||
|
char type[64]; |
||||||
|
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); |
||||||
|
_prepareHeader(header, code, (const char* )type, contentLength); |
||||||
|
_currentClientWrite(header.c_str(), header.length()); |
||||||
|
sendContent_P(content); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { |
||||||
|
String header; |
||||||
|
char type[64]; |
||||||
|
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); |
||||||
|
_prepareHeader(header, code, (const char* )type, contentLength); |
||||||
|
sendContent(header); |
||||||
|
sendContent_P(content, contentLength); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::send(int code, char* content_type, const String& content) { |
||||||
|
send(code, (const char*)content_type, content); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::send(int code, const String& content_type, const String& content) { |
||||||
|
send(code, (const char*)content_type.c_str(), content); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::sendContent(const String& content) { |
||||||
|
const char * footer = "\r\n"; |
||||||
|
size_t len = content.length(); |
||||||
|
if(_chunked) { |
||||||
|
char chunkSize[11]; |
||||||
|
sprintf(chunkSize, "%zx\r\n", len); |
||||||
|
_currentClientWrite(chunkSize, strlen(chunkSize)); |
||||||
|
} |
||||||
|
_currentClientWrite(content.c_str(), len); |
||||||
|
if(_chunked){ |
||||||
|
_currentClient.write(footer, 2); |
||||||
|
if (len == 0) { |
||||||
|
_chunked = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::sendContent_P(PGM_P content) { |
||||||
|
sendContent_P(content, strlen_P(content)); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) { |
||||||
|
const char * footer = "\r\n"; |
||||||
|
if(_chunked) { |
||||||
|
char chunkSize[11]; |
||||||
|
sprintf(chunkSize, "%zx\r\n", size); |
||||||
|
_currentClientWrite(chunkSize, strlen(chunkSize)); |
||||||
|
} |
||||||
|
_currentClientWrite_P(content, size); |
||||||
|
if(_chunked){ |
||||||
|
_currentClient.write(footer, 2); |
||||||
|
if (size == 0) { |
||||||
|
_chunked = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ESP8266WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType) |
||||||
|
{ |
||||||
|
using namespace mime; |
||||||
|
setContentLength(fileSize); |
||||||
|
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && |
||||||
|
contentType != String(FPSTR(mimeTable[gz].mimeType)) && |
||||||
|
contentType != String(FPSTR(mimeTable[none].mimeType))) { |
||||||
|
sendHeader(F("Content-Encoding"), F("gzip")); |
||||||
|
} |
||||||
|
send(200, contentType, emptyString); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const String& ESP8266WebServer::arg(String name) const { |
||||||
|
for (int j = 0; j < _postArgsLen; ++j) { |
||||||
|
if ( _postArgs[j].key == name ) |
||||||
|
return _postArgs[j].value; |
||||||
|
} |
||||||
|
for (int i = 0; i < _currentArgCount; ++i) { |
||||||
|
if ( _currentArgs[i].key == name ) |
||||||
|
return _currentArgs[i].value; |
||||||
|
} |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
const String& ESP8266WebServer::arg(int i) const { |
||||||
|
if (i >= 0 && i < _currentArgCount) |
||||||
|
return _currentArgs[i].value; |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
const String& ESP8266WebServer::argName(int i) const { |
||||||
|
if (i >= 0 && i < _currentArgCount) |
||||||
|
return _currentArgs[i].key; |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
int ESP8266WebServer::args() const { |
||||||
|
return _currentArgCount; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::hasArg(const String& name) const { |
||||||
|
for (int j = 0; j < _postArgsLen; ++j) { |
||||||
|
if (_postArgs[j].key == name) |
||||||
|
return true; |
||||||
|
} |
||||||
|
for (int i = 0; i < _currentArgCount; ++i) { |
||||||
|
if (_currentArgs[i].key == name) |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const String& ESP8266WebServer::header(String name) const { |
||||||
|
for (int i = 0; i < _headerKeysCount; ++i) { |
||||||
|
if (_currentHeaders[i].key.equalsIgnoreCase(name)) |
||||||
|
return _currentHeaders[i].value; |
||||||
|
} |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { |
||||||
|
_headerKeysCount = headerKeysCount + 1; |
||||||
|
if (_currentHeaders) |
||||||
|
delete[]_currentHeaders; |
||||||
|
_currentHeaders = new RequestArgument[_headerKeysCount]; |
||||||
|
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); |
||||||
|
for (int i = 1; i < _headerKeysCount; i++){ |
||||||
|
_currentHeaders[i].key = headerKeys[i-1]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const String& ESP8266WebServer::header(int i) const { |
||||||
|
if (i < _headerKeysCount) |
||||||
|
return _currentHeaders[i].value; |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
const String& ESP8266WebServer::headerName(int i) const { |
||||||
|
if (i < _headerKeysCount) |
||||||
|
return _currentHeaders[i].key; |
||||||
|
return emptyString; |
||||||
|
} |
||||||
|
|
||||||
|
int ESP8266WebServer::headers() const { |
||||||
|
return _headerKeysCount; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::hasHeader(String name) const { |
||||||
|
for (int i = 0; i < _headerKeysCount; ++i) { |
||||||
|
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
const String& ESP8266WebServer::hostHeader() const { |
||||||
|
return _hostHeader; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::onFileUpload(THandlerFunction fn) { |
||||||
|
_fileUploadHandler = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::onNotFound(THandlerFunction fn) { |
||||||
|
_notFoundHandler = fn; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::_handleRequest() { |
||||||
|
bool handled = false; |
||||||
|
if (!_currentHandler){ |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("request handler not found"); |
||||||
|
#endif |
||||||
|
} |
||||||
|
else { |
||||||
|
handled = _currentHandler->handle(*this, _currentMethod, _currentUri); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
if (!handled) { |
||||||
|
DEBUG_OUTPUT.println("request handler failed to handle request"); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
if (!handled && _notFoundHandler) { |
||||||
|
_notFoundHandler(); |
||||||
|
handled = true; |
||||||
|
} |
||||||
|
if (!handled) { |
||||||
|
using namespace mime; |
||||||
|
send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); |
||||||
|
handled = true; |
||||||
|
} |
||||||
|
if (handled) { |
||||||
|
_finalizeResponse(); |
||||||
|
} |
||||||
|
_currentUri = ""; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ESP8266WebServer::_finalizeResponse() { |
||||||
|
if (_chunked) { |
||||||
|
sendContent(emptyString); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const String ESP8266WebServer::_responseCodeToString(int code) { |
||||||
|
switch (code) { |
||||||
|
case 100: return F("Continue"); |
||||||
|
case 101: return F("Switching Protocols"); |
||||||
|
case 200: return F("OK"); |
||||||
|
case 201: return F("Created"); |
||||||
|
case 202: return F("Accepted"); |
||||||
|
case 203: return F("Non-Authoritative Information"); |
||||||
|
case 204: return F("No Content"); |
||||||
|
case 205: return F("Reset Content"); |
||||||
|
case 206: return F("Partial Content"); |
||||||
|
case 300: return F("Multiple Choices"); |
||||||
|
case 301: return F("Moved Permanently"); |
||||||
|
case 302: return F("Found"); |
||||||
|
case 303: return F("See Other"); |
||||||
|
case 304: return F("Not Modified"); |
||||||
|
case 305: return F("Use Proxy"); |
||||||
|
case 307: return F("Temporary Redirect"); |
||||||
|
case 400: return F("Bad Request"); |
||||||
|
case 401: return F("Unauthorized"); |
||||||
|
case 402: return F("Payment Required"); |
||||||
|
case 403: return F("Forbidden"); |
||||||
|
case 404: return F("Not Found"); |
||||||
|
case 405: return F("Method Not Allowed"); |
||||||
|
case 406: return F("Not Acceptable"); |
||||||
|
case 407: return F("Proxy Authentication Required"); |
||||||
|
case 408: return F("Request Time-out"); |
||||||
|
case 409: return F("Conflict"); |
||||||
|
case 410: return F("Gone"); |
||||||
|
case 411: return F("Length Required"); |
||||||
|
case 412: return F("Precondition Failed"); |
||||||
|
case 413: return F("Request Entity Too Large"); |
||||||
|
case 414: return F("Request-URI Too Large"); |
||||||
|
case 415: return F("Unsupported Media Type"); |
||||||
|
case 416: return F("Requested range not satisfiable"); |
||||||
|
case 417: return F("Expectation Failed"); |
||||||
|
case 500: return F("Internal Server Error"); |
||||||
|
case 501: return F("Not Implemented"); |
||||||
|
case 502: return F("Bad Gateway"); |
||||||
|
case 503: return F("Service Unavailable"); |
||||||
|
case 504: return F("Gateway Time-out"); |
||||||
|
case 505: return F("HTTP Version not supported"); |
||||||
|
default: return F(""); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServer.h - Dead simple web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#ifndef ESP8266WEBSERVER_H |
||||||
|
#define ESP8266WEBSERVER_H |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <memory> |
||||||
|
#include <ESP8266WiFi.h> |
||||||
|
|
||||||
|
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; |
||||||
|
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, |
||||||
|
UPLOAD_FILE_ABORTED }; |
||||||
|
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; |
||||||
|
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; |
||||||
|
|
||||||
|
#define HTTP_DOWNLOAD_UNIT_SIZE 1460 |
||||||
|
|
||||||
|
#ifndef HTTP_UPLOAD_BUFLEN |
||||||
|
#define HTTP_UPLOAD_BUFLEN 2048 |
||||||
|
#endif |
||||||
|
|
||||||
|
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
|
||||||
|
#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
|
||||||
|
|
||||||
|
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) |
||||||
|
#define CONTENT_LENGTH_NOT_SET ((size_t) -2) |
||||||
|
|
||||||
|
class ESP8266WebServer; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
HTTPUploadStatus status; |
||||||
|
String filename; |
||||||
|
String name; |
||||||
|
String type; |
||||||
|
size_t totalSize; // total size of uploaded file so far
|
||||||
|
size_t currentSize; // size of data currently in buf
|
||||||
|
size_t contentLength; // size of entire post request, file size + headers and other request data.
|
||||||
|
uint8_t buf[HTTP_UPLOAD_BUFLEN]; |
||||||
|
} HTTPUpload; |
||||||
|
|
||||||
|
#include "detail/RequestHandler.h" |
||||||
|
|
||||||
|
namespace fs { |
||||||
|
class FS; |
||||||
|
} |
||||||
|
|
||||||
|
class ESP8266WebServer |
||||||
|
{ |
||||||
|
public: |
||||||
|
ESP8266WebServer(IPAddress addr, int port = 80); |
||||||
|
ESP8266WebServer(int port = 80); |
||||||
|
virtual ~ESP8266WebServer(); |
||||||
|
|
||||||
|
virtual void begin(); |
||||||
|
virtual void begin(uint16_t port); |
||||||
|
virtual void handleClient(); |
||||||
|
|
||||||
|
virtual void close(); |
||||||
|
void stop(); |
||||||
|
|
||||||
|
bool authenticate(const char * username, const char * password); |
||||||
|
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); |
||||||
|
|
||||||
|
typedef std::function<void(void)> THandlerFunction; |
||||||
|
void on(const String &uri, THandlerFunction handler); |
||||||
|
void on(const String &uri, HTTPMethod method, THandlerFunction fn); |
||||||
|
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); |
||||||
|
void addHandler(RequestHandler* handler); |
||||||
|
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); |
||||||
|
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||||
|
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||||
|
|
||||||
|
const String& uri() const { return _currentUri; } |
||||||
|
HTTPMethod method() const { return _currentMethod; } |
||||||
|
virtual WiFiClient client() { return _currentClient; } |
||||||
|
HTTPUpload& upload() { return *_currentUpload; } |
||||||
|
|
||||||
|
const String& arg(String name) const; // get request argument value by name
|
||||||
|
const String& arg(int i) const; // get request argument value by number
|
||||||
|
const String& argName(int i) const; // get request argument name by number
|
||||||
|
int args() const; // get arguments count
|
||||||
|
bool hasArg(const String& name) const; // check if argument exists
|
||||||
|
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
|
||||||
|
const String& header(String name) const; // get request header value by name
|
||||||
|
const String& header(int i) const; // get request header value by number
|
||||||
|
const String& headerName(int i) const; // get request header name by number
|
||||||
|
int headers() const; // get header count
|
||||||
|
bool hasHeader(String name) const; // check if header exists
|
||||||
|
const String& hostHeader() const; // get request host header if available or empty String if not
|
||||||
|
|
||||||
|
// send response to the client
|
||||||
|
// code - HTTP response code, can be 200 or 404
|
||||||
|
// content_type - HTTP content type, like "text/plain" or "image/png"
|
||||||
|
// content - actual content body
|
||||||
|
void send(int code, const char* content_type = NULL, const String& content = String("")); |
||||||
|
void send(int code, char* content_type, const String& content); |
||||||
|
void send(int code, const String& content_type, const String& content); |
||||||
|
void send_P(int code, PGM_P content_type, PGM_P content); |
||||||
|
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); |
||||||
|
|
||||||
|
void setContentLength(const size_t contentLength); |
||||||
|
void sendHeader(const String& name, const String& value, bool first = false); |
||||||
|
void sendContent(const String& content); |
||||||
|
void sendContent_P(PGM_P content); |
||||||
|
void sendContent_P(PGM_P content, size_t size); |
||||||
|
|
||||||
|
static String urlDecode(const String& text); |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
size_t streamFile(T &file, const String& contentType) { |
||||||
|
_streamFileCore(file.size(), file.name(), contentType); |
||||||
|
return _currentClient.write(file); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } |
||||||
|
virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); } |
||||||
|
void _addRequestHandler(RequestHandler* handler); |
||||||
|
void _handleRequest(); |
||||||
|
void _finalizeResponse(); |
||||||
|
bool _parseRequest(WiFiClient& client); |
||||||
|
void _parseArguments(const String& data); |
||||||
|
int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler); |
||||||
|
static const String _responseCodeToString(int code); |
||||||
|
bool _parseForm(WiFiClient& client, const String& boundary, uint32_t len); |
||||||
|
bool _parseFormUploadAborted(); |
||||||
|
void _uploadWriteByte(uint8_t b); |
||||||
|
uint8_t _uploadReadByte(WiFiClient& client); |
||||||
|
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); |
||||||
|
bool _collectHeader(const char* headerName, const char* headerValue); |
||||||
|
|
||||||
|
void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); |
||||||
|
|
||||||
|
static String _getRandomHexString(); |
||||||
|
// for extracting Auth parameters
|
||||||
|
String _extractParam(String& authReq,const String& param,const char delimit = '"') const; |
||||||
|
|
||||||
|
struct RequestArgument { |
||||||
|
String key; |
||||||
|
String value; |
||||||
|
}; |
||||||
|
|
||||||
|
WiFiServer _server; |
||||||
|
|
||||||
|
WiFiClient _currentClient; |
||||||
|
HTTPMethod _currentMethod; |
||||||
|
String _currentUri; |
||||||
|
uint8_t _currentVersion; |
||||||
|
HTTPClientStatus _currentStatus; |
||||||
|
unsigned long _statusChange; |
||||||
|
|
||||||
|
RequestHandler* _currentHandler; |
||||||
|
RequestHandler* _firstHandler; |
||||||
|
RequestHandler* _lastHandler; |
||||||
|
THandlerFunction _notFoundHandler; |
||||||
|
THandlerFunction _fileUploadHandler; |
||||||
|
|
||||||
|
int _currentArgCount; |
||||||
|
RequestArgument* _currentArgs; |
||||||
|
std::unique_ptr<HTTPUpload> _currentUpload; |
||||||
|
int _postArgsLen; |
||||||
|
RequestArgument* _postArgs; |
||||||
|
|
||||||
|
int _headerKeysCount; |
||||||
|
RequestArgument* _currentHeaders; |
||||||
|
|
||||||
|
size_t _contentLength; |
||||||
|
String _responseHeaders; |
||||||
|
|
||||||
|
String _hostHeader; |
||||||
|
bool _chunked; |
||||||
|
|
||||||
|
String _snonce; // Store noance and opaque for future comparison
|
||||||
|
String _sopaque; |
||||||
|
String _srealm; // Store the Auth realm between Calls
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif //ESP8266WEBSERVER_H
|
@ -0,0 +1,25 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServerSecure.h - Dead simple HTTPS web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <WiFiClientSecure.h> |
||||||
|
|
||||||
|
//#include "ESP8266WebServerSecureAxTLS.h"
|
||||||
|
#include "ESP8266WebServerSecureBearSSL.h" |
@ -0,0 +1,157 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <libb64/cencode.h> |
||||||
|
#include "WiFiServer.h" |
||||||
|
#include "WiFiClient.h" |
||||||
|
#include "ESP8266WebServerSecureAxTLS.h" |
||||||
|
|
||||||
|
//#define DEBUG_ESP_HTTP_SERVER
|
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT |
||||||
|
#else |
||||||
|
#define DEBUG_OUTPUT Serial |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace axTLS { |
||||||
|
|
||||||
|
#pragma GCC diagnostic push |
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
||||||
|
|
||||||
|
ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port)
|
||||||
|
: _serverSecure(addr, port) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServerSecure::ESP8266WebServerSecure(int port) |
||||||
|
: _serverSecure(port) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
#pragma GCC diagnostic pop |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) |
||||||
|
{ |
||||||
|
_serverSecure.setServerKeyAndCert_P(key, keyLen, cert, certLen); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) |
||||||
|
{ |
||||||
|
_serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServerSecure::~ESP8266WebServerSecure() { |
||||||
|
// Nothing to do here.
|
||||||
|
// Base class's destructor will be called to clean up itself
|
||||||
|
} |
||||||
|
|
||||||
|
// We need to basically cut-n-paste these from WebServer because of the problem
|
||||||
|
// of object slicing. The class uses assignment operators like "WiFiClient x=y;"
|
||||||
|
// When this happens, even if "y" is a WiFiClientSecure, the main class is
|
||||||
|
// already compiled down into code which will only copy the WiFiClient superclass
|
||||||
|
// and not the extra bits for our own class (since when it was compiled it needed
|
||||||
|
// to know the size of memory to allocate on the stack for this local variable
|
||||||
|
// there's not realy anything else it could do).
|
||||||
|
|
||||||
|
void ESP8266WebServerSecure::begin() { |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
_serverSecure.begin(); |
||||||
|
if(!_headerKeysCount) |
||||||
|
collectHeaders(0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::handleClient() { |
||||||
|
if (_currentStatus == HC_NONE) { |
||||||
|
WiFiClientSecure client = _serverSecure.available(); |
||||||
|
if (!client) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("New secure client"); |
||||||
|
#endif |
||||||
|
|
||||||
|
_currentClientSecure = client; |
||||||
|
_currentStatus = HC_WAIT_READ; |
||||||
|
_statusChange = millis(); |
||||||
|
} |
||||||
|
|
||||||
|
bool keepCurrentClient = false; |
||||||
|
bool callYield = false; |
||||||
|
|
||||||
|
if (_currentClientSecure.connected()) { |
||||||
|
switch (_currentStatus) { |
||||||
|
case HC_NONE: |
||||||
|
// No-op to avoid C++ compiler warning
|
||||||
|
break; |
||||||
|
case HC_WAIT_READ: |
||||||
|
// Wait for data from client to become available
|
||||||
|
if (_currentClientSecure.available()) { |
||||||
|
if (_parseRequest(_currentClientSecure)) { |
||||||
|
_currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); |
||||||
|
_contentLength = CONTENT_LENGTH_NOT_SET; |
||||||
|
_handleRequest(); |
||||||
|
|
||||||
|
if (_currentClientSecure.connected()) { |
||||||
|
_currentStatus = HC_WAIT_CLOSE; |
||||||
|
_statusChange = millis(); |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { // !_currentClient.available()
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HC_WAIT_CLOSE: |
||||||
|
// Wait for client to close the connection
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!keepCurrentClient) { |
||||||
|
#pragma GCC diagnostic push |
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
||||||
|
_currentClientSecure = WiFiClientSecure(); |
||||||
|
#pragma GCC diagnostic pop |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
_currentUpload.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
if (callYield) { |
||||||
|
yield(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::close() { |
||||||
|
_currentClientSecure.stop(); |
||||||
|
_serverSecure.close(); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,65 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServerSecure.h - Dead simple HTTPS web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. |
||||||
|
|
||||||
|
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 ESP8266WEBSERVERSECURE_H |
||||||
|
#define ESP8266WEBSERVERSECURE_H |
||||||
|
|
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <WiFiServerSecureAxTLS.h> |
||||||
|
#include <WiFiClientSecureAxTLS.h> |
||||||
|
|
||||||
|
namespace axTLS { |
||||||
|
|
||||||
|
class ESP8266WebServerSecure : public ESP8266WebServer |
||||||
|
{ |
||||||
|
public: |
||||||
|
ESP8266WebServerSecure(IPAddress addr, int port = 443); |
||||||
|
ESP8266WebServerSecure(int port = 443); |
||||||
|
virtual ~ESP8266WebServerSecure(); |
||||||
|
|
||||||
|
void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); |
||||||
|
void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); |
||||||
|
|
||||||
|
WiFiClient client() override { return _currentClientSecure; } |
||||||
|
|
||||||
|
void begin() override; |
||||||
|
void handleClient() override; |
||||||
|
void close() override; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
size_t streamFile(T &file, const String& contentType) { |
||||||
|
_streamFileCore(file.size(), file.name(), contentType); |
||||||
|
return _currentClientSecure.write(file); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
size_t _currentClientWrite (const char *bytes, size_t len) override { return _currentClientSecure.write((const uint8_t *)bytes, len); } |
||||||
|
size_t _currentClientWrite_P (PGM_P bytes, size_t len) override { return _currentClientSecure.write_P(bytes, len); } |
||||||
|
|
||||||
|
protected: |
||||||
|
WiFiServerSecure _serverSecure; |
||||||
|
WiFiClientSecure _currentClientSecure; |
||||||
|
}; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif //ESP8266WEBSERVERSECURE_H
|
@ -0,0 +1,165 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <libb64/cencode.h> |
||||||
|
#include "WiFiServer.h" |
||||||
|
#include "WiFiClient.h" |
||||||
|
#include "ESP8266WebServerSecureBearSSL.h" |
||||||
|
|
||||||
|
//#define DEBUG_ESP_HTTP_SERVER
|
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT |
||||||
|
#else |
||||||
|
#define DEBUG_OUTPUT Serial |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace BearSSL { |
||||||
|
|
||||||
|
ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port)
|
||||||
|
: _serverSecure(addr, port) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServerSecure::ESP8266WebServerSecure(int port) |
||||||
|
: _serverSecure(port) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setRSACert(const X509List *chain, const PrivateKey *sk) |
||||||
|
{ |
||||||
|
_serverSecure.setRSACert(chain, sk); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk) |
||||||
|
{ |
||||||
|
_serverSecure.setECCert(chain, cert_issuer_key_type, sk); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setBufferSizes(int recv, int xmit) |
||||||
|
{ |
||||||
|
_serverSecure.setBufferSizes(recv, xmit); |
||||||
|
} |
||||||
|
|
||||||
|
ESP8266WebServerSecure::~ESP8266WebServerSecure() { |
||||||
|
// Nothing to do here.
|
||||||
|
// Base class's destructor will be called to clean up itself
|
||||||
|
} |
||||||
|
|
||||||
|
// We need to basically cut-n-paste these from WebServer because of the problem
|
||||||
|
// of object slicing. The class uses assignment operators like "WiFiClient x=y;"
|
||||||
|
// When this happens, even if "y" is a WiFiClientSecure, the main class is
|
||||||
|
// already compiled down into code which will only copy the WiFiClient superclass
|
||||||
|
// and not the extra bits for our own class (since when it was compiled it needed
|
||||||
|
// to know the size of memory to allocate on the stack for this local variable
|
||||||
|
// there's not realy anything else it could do).
|
||||||
|
|
||||||
|
void ESP8266WebServerSecure::begin() { |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
_serverSecure.begin(); |
||||||
|
if(!_headerKeysCount) |
||||||
|
collectHeaders(0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::handleClient() { |
||||||
|
if (_currentStatus == HC_NONE) { |
||||||
|
WiFiClientSecure client = _serverSecure.available(); |
||||||
|
if (!client) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("New secure client"); |
||||||
|
#endif |
||||||
|
|
||||||
|
_currentClientSecure = client; |
||||||
|
_currentStatus = HC_WAIT_READ; |
||||||
|
_statusChange = millis(); |
||||||
|
} |
||||||
|
|
||||||
|
bool keepCurrentClient = false; |
||||||
|
bool callYield = false; |
||||||
|
|
||||||
|
if (_currentClientSecure.connected()) { |
||||||
|
switch (_currentStatus) { |
||||||
|
case HC_NONE: |
||||||
|
// No-op to avoid C++ compiler warning
|
||||||
|
break; |
||||||
|
case HC_WAIT_READ: |
||||||
|
// Wait for data from client to become available
|
||||||
|
if (_currentClientSecure.available()) { |
||||||
|
if (_parseRequest(_currentClientSecure)) { |
||||||
|
_currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); |
||||||
|
_contentLength = CONTENT_LENGTH_NOT_SET; |
||||||
|
_handleRequest(); |
||||||
|
|
||||||
|
if (_currentClientSecure.connected()) { |
||||||
|
_currentStatus = HC_WAIT_CLOSE; |
||||||
|
_statusChange = millis(); |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { // !_currentClient.available()
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
} |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HC_WAIT_CLOSE: |
||||||
|
// Wait for client to close the connection
|
||||||
|
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { |
||||||
|
keepCurrentClient = true; |
||||||
|
callYield = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!keepCurrentClient) { |
||||||
|
_currentClientSecure = WiFiClientSecure(); |
||||||
|
_currentStatus = HC_NONE; |
||||||
|
_currentUpload.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
if (callYield) { |
||||||
|
yield(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::close() { |
||||||
|
_currentClientSecure.flush(); |
||||||
|
_currentClientSecure.stop(); |
||||||
|
_serverSecure.close(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) { |
||||||
|
_serverSecure.setServerKeyAndCert_P(key, keyLen, cert, certLen); |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) |
||||||
|
{ |
||||||
|
_serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,69 @@ |
|||||||
|
/*
|
||||||
|
ESP8266WebServerSecure.h - Dead simple HTTPS web-server. |
||||||
|
Supports only one simultaneous client, knows how to handle GET and POST. |
||||||
|
|
||||||
|
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. |
||||||
|
|
||||||
|
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 ESP8266WEBSERVERBEARSSL_H |
||||||
|
#define ESP8266WEBSERVERBEARSSL_H |
||||||
|
|
||||||
|
#include <ESP8266WebServer.h> |
||||||
|
#include <BearSSLHelpers.h> |
||||||
|
#include <WiFiServerSecureBearSSL.h> |
||||||
|
|
||||||
|
namespace BearSSL { |
||||||
|
|
||||||
|
class ESP8266WebServerSecure : public ESP8266WebServer |
||||||
|
{ |
||||||
|
public: |
||||||
|
ESP8266WebServerSecure(IPAddress addr, int port = 443); |
||||||
|
ESP8266WebServerSecure(int port = 443); |
||||||
|
virtual ~ESP8266WebServerSecure(); |
||||||
|
|
||||||
|
void setBufferSizes(int recv, int xmit); |
||||||
|
void setRSACert(const X509List *chain, const PrivateKey *sk); |
||||||
|
void setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk); |
||||||
|
|
||||||
|
WiFiClient client() override { return _currentClientSecure; } |
||||||
|
|
||||||
|
void begin() override; |
||||||
|
void handleClient() override; |
||||||
|
void close() override; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
size_t streamFile(T &file, const String& contentType) { |
||||||
|
_streamFileCore(file.size(), file.name(), contentType); |
||||||
|
return _currentClientSecure.write(file); |
||||||
|
} |
||||||
|
|
||||||
|
// AXTLS Compatibility
|
||||||
|
void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); |
||||||
|
void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); |
||||||
|
|
||||||
|
private: |
||||||
|
size_t _currentClientWrite (const char *bytes, size_t len) override { return _currentClientSecure.write((const uint8_t *)bytes, len); } |
||||||
|
size_t _currentClientWrite_P (PGM_P bytes, size_t len) override { return _currentClientSecure.write_P(bytes, len); } |
||||||
|
|
||||||
|
protected: |
||||||
|
WiFiServerSecure _serverSecure; |
||||||
|
WiFiClientSecure _currentClientSecure; |
||||||
|
}; |
||||||
|
|
||||||
|
}; |
||||||
|
#endif //ESP8266WEBSERVERSECURE_H
|
@ -0,0 +1,625 @@ |
|||||||
|
/*
|
||||||
|
Parsing.cpp - HTTP request parsing. |
||||||
|
|
||||||
|
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. |
||||||
|
|
||||||
|
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 |
||||||
|
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "WiFiServer.h" |
||||||
|
#include "WiFiClient.h" |
||||||
|
#include "ESP8266WebServer.h" |
||||||
|
#include "detail/mimetable.h" |
||||||
|
|
||||||
|
//#define DEBUG_ESP_HTTP_SERVER
|
||||||
|
#ifdef DEBUG_ESP_PORT |
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT |
||||||
|
#else |
||||||
|
#define DEBUG_OUTPUT Serial |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef WEBSERVER_MAX_POST_ARGS |
||||||
|
#define WEBSERVER_MAX_POST_ARGS 32 |
||||||
|
#endif |
||||||
|
|
||||||
|
static const char Content_Type[] PROGMEM = "Content-Type"; |
||||||
|
static const char filename[] PROGMEM = "filename"; |
||||||
|
|
||||||
|
static bool readBytesWithTimeout(WiFiClient& client, size_t maxLength, String& data, int timeout_ms) |
||||||
|
{ |
||||||
|
if (!data.reserve(maxLength + 1)) |
||||||
|
return false; |
||||||
|
data[0] = 0; // data.clear()??
|
||||||
|
while (data.length() < maxLength) { |
||||||
|
int tries = timeout_ms; |
||||||
|
size_t avail; |
||||||
|
while (!(avail = client.available()) && tries--) |
||||||
|
delay(1); |
||||||
|
if (!avail) |
||||||
|
break; |
||||||
|
if (data.length() + avail > maxLength) |
||||||
|
avail = maxLength - data.length(); |
||||||
|
while (avail--) |
||||||
|
data += (char)client.read(); |
||||||
|
} |
||||||
|
return data.length() == maxLength; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::_parseRequest(WiFiClient& client) { |
||||||
|
// Read the first line of HTTP request
|
||||||
|
String req = client.readStringUntil('\r'); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("request: "); |
||||||
|
DEBUG_OUTPUT.println(req); |
||||||
|
#endif |
||||||
|
client.readStringUntil('\n'); |
||||||
|
//reset header value
|
||||||
|
for (int i = 0; i < _headerKeysCount; ++i) { |
||||||
|
_currentHeaders[i].value =String(); |
||||||
|
} |
||||||
|
|
||||||
|
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||||
|
// Retrieve the "/path" part by finding the spaces
|
||||||
|
int addr_start = req.indexOf(' '); |
||||||
|
int addr_end = req.indexOf(' ', addr_start + 1); |
||||||
|
if (addr_start == -1 || addr_end == -1) { |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("Invalid request"); |
||||||
|
#endif |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
String methodStr = req.substring(0, addr_start); |
||||||
|
String url = req.substring(addr_start + 1, addr_end); |
||||||
|
String versionEnd = req.substring(addr_end + 8); |
||||||
|
_currentVersion = atoi(versionEnd.c_str()); |
||||||
|
String searchStr = ""; |
||||||
|
int hasSearch = url.indexOf('?'); |
||||||
|
if (hasSearch != -1){ |
||||||
|
searchStr = url.substring(hasSearch + 1); |
||||||
|
url = url.substring(0, hasSearch); |
||||||
|
} |
||||||
|
_currentUri = url; |
||||||
|
_chunked = false; |
||||||
|
|
||||||
|
HTTPMethod method = HTTP_GET; |
||||||
|
if (methodStr == F("POST")) { |
||||||
|
method = HTTP_POST; |
||||||
|
} else if (methodStr == F("DELETE")) { |
||||||
|
method = HTTP_DELETE; |
||||||
|
} else if (methodStr == F("OPTIONS")) { |
||||||
|
method = HTTP_OPTIONS; |
||||||
|
} else if (methodStr == F("PUT")) { |
||||||
|
method = HTTP_PUT; |
||||||
|
} else if (methodStr == F("PATCH")) { |
||||||
|
method = HTTP_PATCH; |
||||||
|
} |
||||||
|
_currentMethod = method; |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("method: "); |
||||||
|
DEBUG_OUTPUT.print(methodStr); |
||||||
|
DEBUG_OUTPUT.print(" url: "); |
||||||
|
DEBUG_OUTPUT.print(url); |
||||||
|
DEBUG_OUTPUT.print(" search: "); |
||||||
|
DEBUG_OUTPUT.println(searchStr); |
||||||
|
#endif |
||||||
|
|
||||||
|
//attach handler
|
||||||
|
RequestHandler* handler; |
||||||
|
for (handler = _firstHandler; handler; handler = handler->next()) { |
||||||
|
if (handler->canHandle(_currentMethod, _currentUri)) |
||||||
|
break; |
||||||
|
} |
||||||
|
_currentHandler = handler; |
||||||
|
|
||||||
|
String formData; |
||||||
|
// below is needed only when POST type request
|
||||||
|
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ |
||||||
|
String boundaryStr; |
||||||
|
String headerName; |
||||||
|
String headerValue; |
||||||
|
bool isForm = false; |
||||||
|
bool isEncoded = false; |
||||||
|
uint32_t contentLength = 0; |
||||||
|
//parse headers
|
||||||
|
while(1){ |
||||||
|
req = client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
if (req == "") break;//no moar headers
|
||||||
|
int headerDiv = req.indexOf(':'); |
||||||
|
if (headerDiv == -1){ |
||||||
|
break; |
||||||
|
} |
||||||
|
headerName = req.substring(0, headerDiv); |
||||||
|
headerValue = req.substring(headerDiv + 1); |
||||||
|
headerValue.trim(); |
||||||
|
_collectHeader(headerName.c_str(),headerValue.c_str()); |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("headerName: "); |
||||||
|
DEBUG_OUTPUT.println(headerName); |
||||||
|
DEBUG_OUTPUT.print("headerValue: "); |
||||||
|
DEBUG_OUTPUT.println(headerValue); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){ |
||||||
|
using namespace mime; |
||||||
|
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){ |
||||||
|
isForm = false; |
||||||
|
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){ |
||||||
|
isForm = false; |
||||||
|
isEncoded = true; |
||||||
|
} else if (headerValue.startsWith(F("multipart/"))){ |
||||||
|
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); |
||||||
|
boundaryStr.replace("\"",""); |
||||||
|
isForm = true; |
||||||
|
} |
||||||
|
} else if (headerName.equalsIgnoreCase(F("Content-Length"))){ |
||||||
|
contentLength = headerValue.toInt(); |
||||||
|
} else if (headerName.equalsIgnoreCase(F("Host"))){ |
||||||
|
_hostHeader = headerValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String plainBuf; |
||||||
|
if ( !isForm |
||||||
|
&& // read content into plainBuf
|
||||||
|
( !readBytesWithTimeout(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT) |
||||||
|
|| (plainBuf.length() < contentLength) |
||||||
|
) |
||||||
|
) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (isEncoded) { |
||||||
|
// isEncoded => !isForm => plainBuf is not empty
|
||||||
|
// add plainBuf in search str
|
||||||
|
if (searchStr.length()) |
||||||
|
searchStr += '&'; |
||||||
|
searchStr += plainBuf; |
||||||
|
} |
||||||
|
|
||||||
|
// parse searchStr for key/value pairs
|
||||||
|
_parseArguments(searchStr); |
||||||
|
|
||||||
|
if (!isForm) { |
||||||
|
if (contentLength) { |
||||||
|
// add key=value: plain={body} (post json or other data)
|
||||||
|
RequestArgument& arg = _currentArgs[_currentArgCount++]; |
||||||
|
arg.key = F("plain"); |
||||||
|
arg.value = plainBuf; |
||||||
|
} |
||||||
|
} else { // isForm is true
|
||||||
|
// here: content is not yet read (plainBuf is still empty)
|
||||||
|
if (!_parseForm(client, boundaryStr, contentLength)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
String headerName; |
||||||
|
String headerValue; |
||||||
|
//parse headers
|
||||||
|
while(1){ |
||||||
|
req = client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
if (req == "") break;//no moar headers
|
||||||
|
int headerDiv = req.indexOf(':'); |
||||||
|
if (headerDiv == -1){ |
||||||
|
break; |
||||||
|
} |
||||||
|
headerName = req.substring(0, headerDiv); |
||||||
|
headerValue = req.substring(headerDiv + 2); |
||||||
|
_collectHeader(headerName.c_str(),headerValue.c_str()); |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print(F("headerName: ")); |
||||||
|
DEBUG_OUTPUT.println(headerName); |
||||||
|
DEBUG_OUTPUT.print(F("headerValue: ")); |
||||||
|
DEBUG_OUTPUT.println(headerValue); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (headerName.equalsIgnoreCase("Host")){ |
||||||
|
_hostHeader = headerValue; |
||||||
|
} |
||||||
|
} |
||||||
|
_parseArguments(searchStr); |
||||||
|
} |
||||||
|
client.flush(); |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print(F("Request: ")); |
||||||
|
DEBUG_OUTPUT.println(url); |
||||||
|
DEBUG_OUTPUT.print(F("Arguments: ")); |
||||||
|
DEBUG_OUTPUT.println(searchStr); |
||||||
|
|
||||||
|
DEBUG_OUTPUT.println(F("final list of key/value pairs:")); |
||||||
|
for (int i = 0; i < _currentArgCount; i++) |
||||||
|
DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n", |
||||||
|
_currentArgs[i].key.c_str(), |
||||||
|
_currentArgs[i].value.c_str()); |
||||||
|
#endif |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) { |
||||||
|
for (int i = 0; i < _headerKeysCount; i++) { |
||||||
|
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { |
||||||
|
_currentHeaders[i].value=headerValue; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
struct storeArgHandler |
||||||
|
{ |
||||||
|
void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) |
||||||
|
{ |
||||||
|
key = ESP8266WebServer::urlDecode(data.substring(pos, key_end_pos)); |
||||||
|
if ((equal_index != -1) && ((equal_index < next_index - 1) || (next_index == -1))) |
||||||
|
value = ESP8266WebServer::urlDecode(data.substring(equal_index + 1, next_index)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct nullArgHandler |
||||||
|
{ |
||||||
|
void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) { |
||||||
|
(void)key; (void)value; (void)data; (void)equal_index; (void)pos; (void)key_end_pos; (void)next_index; |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void ESP8266WebServer::_parseArguments(const String& data) { |
||||||
|
if (_currentArgs) |
||||||
|
delete[] _currentArgs; |
||||||
|
|
||||||
|
_currentArgCount = _parseArgumentsPrivate(data, nullArgHandler()); |
||||||
|
|
||||||
|
// allocate one more, this is needed because {"plain": plainBuf} is always added
|
||||||
|
_currentArgs = new RequestArgument[_currentArgCount + 1]; |
||||||
|
|
||||||
|
(void)_parseArgumentsPrivate(data, storeArgHandler()); |
||||||
|
} |
||||||
|
|
||||||
|
int ESP8266WebServer::_parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler) { |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("args: "); |
||||||
|
DEBUG_OUTPUT.println(data); |
||||||
|
#endif |
||||||
|
|
||||||
|
size_t pos = 0; |
||||||
|
int arg_total = 0; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
|
||||||
|
// skip empty expression
|
||||||
|
while (data[pos] == '&' || data[pos] == ';') |
||||||
|
if (++pos >= data.length()) |
||||||
|
break; |
||||||
|
|
||||||
|
// locate separators
|
||||||
|
int equal_index = data.indexOf('=', pos); |
||||||
|
int key_end_pos = equal_index; |
||||||
|
int next_index = data.indexOf('&', pos); |
||||||
|
int next_index2 = data.indexOf(';', pos); |
||||||
|
if ((next_index == -1) || (next_index2 != -1 && next_index2 < next_index)) |
||||||
|
next_index = next_index2; |
||||||
|
if ((key_end_pos == -1) || ((key_end_pos > next_index) && (next_index != -1))) |
||||||
|
key_end_pos = next_index; |
||||||
|
if (key_end_pos == -1) |
||||||
|
key_end_pos = data.length(); |
||||||
|
|
||||||
|
// handle key/value
|
||||||
|
if ((int)pos < key_end_pos) { |
||||||
|
|
||||||
|
RequestArgument& arg = _currentArgs[arg_total]; |
||||||
|
handler(arg.key, arg.value, data, equal_index, pos, key_end_pos, next_index); |
||||||
|
|
||||||
|
++arg_total; |
||||||
|
pos = next_index + 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (next_index == -1) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("args count: "); |
||||||
|
DEBUG_OUTPUT.println(arg_total); |
||||||
|
#endif |
||||||
|
|
||||||
|
return arg_total; |
||||||
|
} |
||||||
|
|
||||||
|
void ESP8266WebServer::_uploadWriteByte(uint8_t b){ |
||||||
|
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){ |
||||||
|
if(_currentHandler && _currentHandler->canUpload(_currentUri)) |
||||||
|
_currentHandler->upload(*this, _currentUri, *_currentUpload); |
||||||
|
_currentUpload->totalSize += _currentUpload->currentSize; |
||||||
|
_currentUpload->currentSize = 0; |
||||||
|
} |
||||||
|
_currentUpload->buf[_currentUpload->currentSize++] = b; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){ |
||||||
|
int res = client.read(); |
||||||
|
if(res == -1){ |
||||||
|
while(!client.available() && client.connected()) |
||||||
|
yield(); |
||||||
|
res = client.read(); |
||||||
|
} |
||||||
|
return (uint8_t)res; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::_parseForm(WiFiClient& client, const String& boundary, uint32_t len){ |
||||||
|
(void) len; |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("Parse Form: Boundary: "); |
||||||
|
DEBUG_OUTPUT.print(boundary); |
||||||
|
DEBUG_OUTPUT.print(" Length: "); |
||||||
|
DEBUG_OUTPUT.println(len); |
||||||
|
#endif |
||||||
|
String line; |
||||||
|
int retry = 0; |
||||||
|
do { |
||||||
|
line = client.readStringUntil('\r'); |
||||||
|
++retry; |
||||||
|
} while (line.length() == 0 && retry < 3); |
||||||
|
|
||||||
|
client.readStringUntil('\n'); |
||||||
|
//start reading the form
|
||||||
|
if (line == ("--"+boundary)){ |
||||||
|
if(_postArgs) delete[] _postArgs; |
||||||
|
_postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; |
||||||
|
_postArgsLen = 0; |
||||||
|
while(1){ |
||||||
|
String argName; |
||||||
|
String argValue; |
||||||
|
String argType; |
||||||
|
String argFilename; |
||||||
|
bool argIsFile = false; |
||||||
|
|
||||||
|
line = client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ |
||||||
|
int nameStart = line.indexOf('='); |
||||||
|
if (nameStart != -1){ |
||||||
|
argName = line.substring(nameStart+2); |
||||||
|
nameStart = argName.indexOf('='); |
||||||
|
if (nameStart == -1){ |
||||||
|
argName = argName.substring(0, argName.length() - 1); |
||||||
|
} else { |
||||||
|
argFilename = argName.substring(nameStart+2, argName.length() - 1); |
||||||
|
argName = argName.substring(0, argName.indexOf('"')); |
||||||
|
argIsFile = true; |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("PostArg FileName: "); |
||||||
|
DEBUG_OUTPUT.println(argFilename); |
||||||
|
#endif |
||||||
|
//use GET to set the filename if uploading using blob
|
||||||
|
if (argFilename == F("blob") && hasArg(FPSTR(filename))) |
||||||
|
argFilename = arg(FPSTR(filename)); |
||||||
|
} |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("PostArg Name: "); |
||||||
|
DEBUG_OUTPUT.println(argName); |
||||||
|
#endif |
||||||
|
using namespace mime; |
||||||
|
argType = FPSTR(mimeTable[txt].mimeType); |
||||||
|
line = client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){ |
||||||
|
argType = line.substring(line.indexOf(':')+2); |
||||||
|
//skip next line
|
||||||
|
client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
} |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("PostArg Type: "); |
||||||
|
DEBUG_OUTPUT.println(argType); |
||||||
|
#endif |
||||||
|
if (!argIsFile){ |
||||||
|
while(1){ |
||||||
|
line = client.readStringUntil('\r'); |
||||||
|
client.readStringUntil('\n'); |
||||||
|
if (line.startsWith("--"+boundary)) break; |
||||||
|
if (argValue.length() > 0) argValue += "\n"; |
||||||
|
argValue += line; |
||||||
|
} |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("PostArg Value: "); |
||||||
|
DEBUG_OUTPUT.println(argValue); |
||||||
|
DEBUG_OUTPUT.println(); |
||||||
|
#endif |
||||||
|
|
||||||
|
RequestArgument& arg = _postArgs[_postArgsLen++]; |
||||||
|
arg.key = argName; |
||||||
|
arg.value = argValue; |
||||||
|
|
||||||
|
if (line == ("--"+boundary+"--")){ |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("Done Parsing POST"); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { |
||||||
|
_currentUpload.reset(new HTTPUpload()); |
||||||
|
_currentUpload->status = UPLOAD_FILE_START; |
||||||
|
_currentUpload->name = argName; |
||||||
|
_currentUpload->filename = argFilename; |
||||||
|
_currentUpload->type = argType; |
||||||
|
_currentUpload->totalSize = 0; |
||||||
|
_currentUpload->currentSize = 0; |
||||||
|
_currentUpload->contentLength = len; |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("Start File: "); |
||||||
|
DEBUG_OUTPUT.print(_currentUpload->filename); |
||||||
|
DEBUG_OUTPUT.print(" Type: "); |
||||||
|
DEBUG_OUTPUT.println(_currentUpload->type); |
||||||
|
#endif |
||||||
|
if(_currentHandler && _currentHandler->canUpload(_currentUri)) |
||||||
|
_currentHandler->upload(*this, _currentUri, *_currentUpload); |
||||||
|
_currentUpload->status = UPLOAD_FILE_WRITE; |
||||||
|
uint8_t argByte = _uploadReadByte(client); |
||||||
|
readfile: |
||||||
|
while(argByte != 0x0D){ |
||||||
|
if (!client.connected()) return _parseFormUploadAborted(); |
||||||
|
_uploadWriteByte(argByte); |
||||||
|
argByte = _uploadReadByte(client); |
||||||
|
} |
||||||
|
|
||||||
|
argByte = _uploadReadByte(client); |
||||||
|
if (!client.connected()) return _parseFormUploadAborted(); |
||||||
|
if (argByte == 0x0A){ |
||||||
|
argByte = _uploadReadByte(client); |
||||||
|
if (!client.connected()) return _parseFormUploadAborted(); |
||||||
|
if ((char)argByte != '-'){ |
||||||
|
//continue reading the file
|
||||||
|
_uploadWriteByte(0x0D); |
||||||
|
_uploadWriteByte(0x0A); |
||||||
|
goto readfile; |
||||||
|
} else { |
||||||
|
argByte = _uploadReadByte(client); |
||||||
|
if (!client.connected()) return _parseFormUploadAborted(); |
||||||
|
if ((char)argByte != '-'){ |
||||||
|
//continue reading the file
|
||||||
|
_uploadWriteByte(0x0D); |
||||||
|
_uploadWriteByte(0x0A); |
||||||
|
_uploadWriteByte((uint8_t)('-')); |
||||||
|
goto readfile; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t endBuf[boundary.length()]; |
||||||
|
client.readBytes(endBuf, boundary.length()); |
||||||
|
|
||||||
|
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ |
||||||
|
if(_currentHandler && _currentHandler->canUpload(_currentUri)) |
||||||
|
_currentHandler->upload(*this, _currentUri, *_currentUpload); |
||||||
|
_currentUpload->totalSize += _currentUpload->currentSize; |
||||||
|
_currentUpload->status = UPLOAD_FILE_END; |
||||||
|
if(_currentHandler && _currentHandler->canUpload(_currentUri)) |
||||||
|
_currentHandler->upload(*this, _currentUri, *_currentUpload); |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("End File: "); |
||||||
|
DEBUG_OUTPUT.print(_currentUpload->filename); |
||||||
|
DEBUG_OUTPUT.print(" Type: "); |
||||||
|
DEBUG_OUTPUT.print(_currentUpload->type); |
||||||
|
DEBUG_OUTPUT.print(" Size: "); |
||||||
|
DEBUG_OUTPUT.println(_currentUpload->totalSize); |
||||||
|
#endif |
||||||
|
line = client.readStringUntil(0x0D); |
||||||
|
client.readStringUntil(0x0A); |
||||||
|
if (line == "--"){ |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.println("Done Parsing POST"); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} else { |
||||||
|
_uploadWriteByte(0x0D); |
||||||
|
_uploadWriteByte(0x0A); |
||||||
|
_uploadWriteByte((uint8_t)('-')); |
||||||
|
_uploadWriteByte((uint8_t)('-')); |
||||||
|
uint32_t i = 0; |
||||||
|
while(i < boundary.length()){ |
||||||
|
_uploadWriteByte(endBuf[i++]); |
||||||
|
} |
||||||
|
argByte = _uploadReadByte(client); |
||||||
|
goto readfile; |
||||||
|
} |
||||||
|
} else { |
||||||
|
_uploadWriteByte(0x0D); |
||||||
|
goto readfile; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int iarg; |
||||||
|
int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount; |
||||||
|
for (iarg = 0; iarg < totalArgs; iarg++){ |
||||||
|
RequestArgument& arg = _postArgs[_postArgsLen++]; |
||||||
|
arg.key = _currentArgs[iarg].key; |
||||||
|
arg.value = _currentArgs[iarg].value; |
||||||
|
} |
||||||
|
if (_currentArgs) delete[] _currentArgs; |
||||||
|
_currentArgs = new RequestArgument[_postArgsLen]; |
||||||
|
for (iarg = 0; iarg < _postArgsLen; iarg++){ |
||||||
|
RequestArgument& arg = _currentArgs[iarg]; |
||||||
|
arg.key = _postArgs[iarg].key; |
||||||
|
arg.value = _postArgs[iarg].value; |
||||||
|
} |
||||||
|
_currentArgCount = iarg; |
||||||
|
if (_postArgs) { |
||||||
|
delete[] _postArgs; |
||||||
|
_postArgs = nullptr; |
||||||
|
_postArgsLen = 0; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER |
||||||
|
DEBUG_OUTPUT.print("Error: line: "); |
||||||
|
DEBUG_OUTPUT.println(line); |
||||||
|
#endif |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
String ESP8266WebServer::urlDecode(const String& text) |
||||||
|
{ |
||||||
|
String decoded = ""; |
||||||
|
char temp[] = "0x00"; |
||||||
|
unsigned int len = text.length(); |
||||||
|
unsigned int i = 0; |
||||||
|
while (i < len) |
||||||
|
{ |
||||||
|
char decodedChar; |
||||||
|
char encodedChar = text.charAt(i++); |
||||||
|
if ((encodedChar == '%') && (i + 1 < len)) |
||||||
|
{ |
||||||
|
temp[2] = text.charAt(i++); |
||||||
|
temp[3] = text.charAt(i++); |
||||||
|
|
||||||
|
decodedChar = strtol(temp, NULL, 16); |
||||||
|
} |
||||||
|
else { |
||||||
|
if (encodedChar == '+') |
||||||
|
{ |
||||||
|
decodedChar = ' '; |
||||||
|
} |
||||||
|
else { |
||||||
|
decodedChar = encodedChar; // normal ascii char
|
||||||
|
} |
||||||
|
} |
||||||
|
decoded += decodedChar; |
||||||
|
} |
||||||
|
return decoded; |
||||||
|
} |
||||||
|
|
||||||
|
bool ESP8266WebServer::_parseFormUploadAborted(){ |
||||||
|
_currentUpload->status = UPLOAD_FILE_ABORTED; |
||||||
|
if(_currentHandler && _currentHandler->canUpload(_currentUri)) |
||||||
|
_currentHandler->upload(*this, _currentUri, *_currentUpload); |
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
#ifndef REQUESTHANDLER_H |
||||||
|
#define REQUESTHANDLER_H |
||||||
|
|
||||||
|
class RequestHandler { |
||||||
|
public: |
||||||
|
virtual ~RequestHandler() { } |
||||||
|
virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; } |
||||||
|
virtual bool canUpload(String uri) { (void) uri; return false; } |
||||||
|
virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } |
||||||
|
virtual void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } |
||||||
|
|
||||||
|
RequestHandler* next() { return _next; } |
||||||
|
void next(RequestHandler* r) { _next = r; } |
||||||
|
|
||||||
|
private: |
||||||
|
RequestHandler* _next = nullptr; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //REQUESTHANDLER_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue