diff --git a/boards.txt b/boards.txt index c017cf1349e..dba745a16c7 100644 --- a/boards.txt +++ b/boards.txt @@ -97,4 +97,4 @@ nefrybt.menu.DebugLevel.info.build.code_debug=3 nefrybt.menu.DebugLevel.debug=Debug nefrybt.menu.DebugLevel.debug.build.code_debug=4 nefrybt.menu.DebugLevel.verbose=Verbose -nefrybt.menu.DebugLevel.verbose.build.code_debug=5 \ No newline at end of file +nefrybt.menu.DebugLevel.verbose.build.code_debug=5 diff --git a/cores/esp32/esp32-hal-bt.c b/cores/esp32/esp32-hal-bt.c index 0bed8e80528..35899c31a7a 100644 --- a/cores/esp32/esp32-hal-bt.c +++ b/cores/esp32/esp32-hal-bt.c @@ -23,8 +23,7 @@ bool btStarted(){ } bool btStart(){ - esp_bt_controller_config_t cfg; - memset(&cfg, 0, sizeof(esp_bt_controller_config_t)); + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ return true; } diff --git a/cores/esp32/main.cpp b/cores/esp32/main.cpp index eacad2ff99d..d2148171015 100644 --- a/cores/esp32/main.cpp +++ b/cores/esp32/main.cpp @@ -4,6 +4,8 @@ #include "./nefry/NefryWebServer.h" #include "./nefry/Nefry.h" +#include "./nefry/NefryWiFi.h" +#include "./nefry/NefryConfig.h" #if CONFIG_AUTOSTART_ARDUINO @@ -15,35 +17,44 @@ void loopTask(void *pvParameters) { - Nefry.nefry_init(); + while (Nefry.getBootMode() == -1) {} + while (Nefry.getBootMode() == 0) { + Nefry.pollingSW(); + } if (Nefry.getWriteMode() != true) { + delay(500); setup(); } - NefryWebServer.begin(); for(;;) { micros(); //update overflow if (Nefry.getWriteMode() != true) { loop(); } - Nefry.nefry_loop(); } } void NefryBackEnd(void *pvParameters) { TickType_t xLastWakeTime; xLastWakeTime = xTaskGetTickCount(); + Nefry.nefry_init(); + NefryWeb.begin(); + NefryWiFi.beginWeb(); + NefryConfig.beginWeb(); + NefryWebServer.begin(); for (;;) { - vTaskDelayUntil(&xLastWakeTime,100/portTICK_PERIOD_MS); - NefryWebServer.run(); + vTaskDelayUntil(&xLastWakeTime,10/portTICK_PERIOD_MS); + NefryWeb.run(); + NefryWebServer.run(); Nefry.pollingSW(); + Nefry.nefry_loop(); } } extern "C" void app_main() { initArduino(); - xTaskCreatePinnedToCore(loopTask, "loopTask", 4096, NULL, 2, NULL, 1); - xTaskCreatePinnedToCore(&NefryBackEnd, "NefryBackEnd", 9192, NULL, 1, NULL,0); + xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 2, NULL,1); + xTaskCreatePinnedToCore(&NefryBackEnd, "NefryBackEnd", 8192, NULL, 1, NULL,0); } #endif diff --git a/cores/esp32/nefry/Nefry.cpp b/cores/esp32/nefry/Nefry.cpp index 21384dcced5..747d32373e4 100644 --- a/cores/esp32/nefry/Nefry.cpp +++ b/cores/esp32/nefry/Nefry.cpp @@ -24,7 +24,7 @@ BootMode 1 : WriteMode切替をする */ -#define LIBVERSION ("0.6.1") +#define LIBVERSION ("0.6.2") #include "Nefry.h" Adafruit_NeoPixel _NefryLED[40]; @@ -34,6 +34,8 @@ Adafruit_NeoPixel _NefryLED[40]; void Nefry_lib::nefry_init() { beginLed(1, 16, NEO_GRB); enableSW(); + delay(50); + _bootMode = 0; setLed(0x00, 0x0f, 0x00); Serial.begin(115200); Serial.println(F("\n\nStartup")); @@ -276,32 +278,14 @@ bool Nefry_lib::getWriteMode() { if (_bootMode == 2)return true; return false; } - +long Nefry_lib::getBootMode() +{ + return _bootMode; +} void Nefry_lib::setStoreTitle(const char set[15], const int num) { NefryConfig.setStoreTitle(set, num); return ; } -/* -String Nefry_Conf::setDefaultModuleId() { - uint8_t macAddr[WL_MAC_ADDR_LENGTH]; - String moduleName; - WiFi.macAddress(macAddr); - switch (boardId) - { - case 1: - moduleName = "Nefry"; - break; - case 2: - moduleName = "CocoaBit"; - break; - } - moduleName += "-"; - moduleName += macAddr[WL_MAC_ADDR_LENGTH - 2]; - moduleName += macAddr[WL_MAC_ADDR_LENGTH - 1]; - return moduleName -} -*/ - Nefry_lib Nefry; \ No newline at end of file diff --git a/cores/esp32/nefry/Nefry.h b/cores/esp32/nefry/Nefry.h index 927d2a8818f..9943a42b853 100644 --- a/cores/esp32/nefry/Nefry.h +++ b/cores/esp32/nefry/Nefry.h @@ -44,7 +44,8 @@ class Nefry_lib getAddressStr(IPAddress ip); long - getStoreValue(int pointer); + getStoreValue(int pointer), + getBootMode(); void @@ -76,7 +77,7 @@ class Nefry_lib _swPushingflg = false; int - _bootMode = 0, /* Boot状態を管理 0:起動中 1:通常起動 2:書き込みモード */ + _bootMode = -1, /* Boot状態を管理 -1:初期化中 0:起動中 1:通常起動 2:書き込みモード */ hextonum(char c); const char * program; diff --git a/cores/esp32/nefry/NefryConfig.cpp b/cores/esp32/nefry/NefryConfig.cpp index b7f9c3ce240..ff0e5226689 100644 --- a/cores/esp32/nefry/NefryConfig.cpp +++ b/cores/esp32/nefry/NefryConfig.cpp @@ -21,16 +21,15 @@ StoreStr : Nefryの環境変数を保存するときに使用する。(0-7)の -String Nefry_Conf::beginWeb(String link) { - - if (link.equals("config")) { +void Nefry_Conf::beginWeb() { + NefryWebServer.getWebServer()->on("/config", [&]() { String content = F("

Nefry DataStore Setup

このページはプログラム内から読み書きした値を表示、編集することができます。

" - "

わざわざプログラムを書き換えずに値を変更できるためWebサービスでアクセスキーが必要になる場合など環境変数として扱うことができます。

" + "

わざわざプログラムを書き換えずに値を変更できるためWebサービスでアクセスキーが必要になる場合やモードを切り替える時など環境変数として扱うことができます。

" "" "

それぞれの関数の使い方はNefry公式サイトをご覧になるか、サンプルプログラムを参考にしてください。

"); - int formNumber,printCounter = 0; + int formNumber, printCounter = 0; for (formNumber = 0; formNumber < 10; formNumber++) { - if (htmlPrint[formNumber] == 1) { + if (htmlPrint[formNumber] == true) { content += F("
表示するものがひとつもありません。setStoreTitle関数を使って表示をするか、WriteModeにしてください。

"); } @@ -64,32 +63,59 @@ String Nefry_Conf::beginWeb(String link) { content += F("
"); if (Nefry.getWriteMode())content += "WriteMode"; content += F("
Back to top"); - return NefryWeb.createHtml(F("Nefry DataStore"), "", content); - } - return ""; + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Nefry DataStore"), "", content)); + }); + + NefryWebServer.getWebServer()->on("/set_config", [&]() { + char webarg[5] = { "smo0" }; + for (int i = 0; i < 10; i++) { + webarg[3] = '0' + i; + String s = NefryWebServer.getWebServer()->arg(webarg); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print(webarg); + DEBUG_OUTPUT.print(" : "); + DEBUG_OUTPUT.println(s); +#endif + NefryDataStore.setStorageStr(s, i); + } + webarg[0] = 'i'; + for (int i = 0; i < 10; i++) { + webarg[3] = '0' + i; + String s = NefryWebServer.getWebServer()->arg(webarg); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print(webarg); + DEBUG_OUTPUT.print(" : "); + DEBUG_OUTPUT.println(s); +#endif + NefryDataStore.setStorageValue(s.toInt(), i); + } + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Nefry DataStore Set"), "", F("

Nefry Module Set

Saveing & Restart...

Back to top"))); + NefryWebServer.resetTimer(2); + }); } - /* HTMLに表示するのか */ +/* HTMLに表示するのか */ void Nefry_Conf::setStoreTitle(const char set[15], const int num) { if (0 <= num&&num < 20) { strcpy(module_input[num], set); - htmlPrint[num] = 1; + htmlPrint[num] = true; } } + void Nefry_Conf::begin() { for (int i = 0; i < 10; i++) { sprintf(module_input[i], "String %d", i); - sprintf(module_input[10+i], "Value %d", 10+i); + sprintf(module_input[10 + i], "Value %d", 10 + i); if (Nefry.getWriteMode()) { - htmlPrint[i] = 1; - htmlPrint[10+i] = 1; + htmlPrint[i] = true; + htmlPrint[10 + i] = true; } else { - htmlPrint[i] = 0; - htmlPrint[10 + i] = 0; - } + htmlPrint[i] = false; + htmlPrint[10 + i] = false; + } } } Nefry_Conf NefryConfig; \ No newline at end of file diff --git a/cores/esp32/nefry/NefryConfig.h b/cores/esp32/nefry/NefryConfig.h index 66250e5b563..375f70f6378 100644 --- a/cores/esp32/nefry/NefryConfig.h +++ b/cores/esp32/nefry/NefryConfig.h @@ -9,13 +9,9 @@ class Nefry_Conf { public: void + beginWeb(), begin(), setStoreTitle(const char set[15], const int num); - - String - beginWeb(String link), - setDefaultModuleId(); - private: bool htmlPrint[20];//10 char module_input[20][15]; diff --git a/cores/esp32/nefry/NefryWeb.cpp b/cores/esp32/nefry/NefryWeb.cpp index b5c00da5de4..1669000a614 100644 --- a/cores/esp32/nefry/NefryWeb.cpp +++ b/cores/esp32/nefry/NefryWeb.cpp @@ -7,10 +7,10 @@ This software is released under the MIT License. http://opensource.org/licenses/mit-license.php */ - -#include "NefryWeb.h" +#include "./NefryWeb.h" //web -/* +String indexlink; + void Nefry_Web::setIndexLink(const char title[32], const char url[32]) { indexlink += "
  • 0) { - NefryWiFi.addWifi(newssid, newpwd); - NefryWiFi.saveWifi(); - content = F("Save SSID:"); - content += newssid; - content += F(" Restart to boot into new WiFi"); - } - else { - content = F("Empty SSID is not acceptable."); - } - send(200, "text/html", NefryWeb.createHtml(F("Nefry Wifi Set"), "", (String)F("

    Nefry Wifi Set

    ") + content + (String)F("

    Back to top"))); - Serial.print("dateSend"); - if (newssid.length() != 0)resetTimer(2); - } - else if (url.equalsIgnoreCase("/delete_wifi")) { - String del = "",ssid; - deleteFlg = false; - for (int i = 0; i < _currentArgCount; i++) { - ssid = NefryWiFi.deleteWifi(_currentArgs[i].key.toInt()); - if (!ssid.equals("")) { - del += "
  • "; - del += ssid; - del += "
  • "; - deleteFlg = true; - } - } - NefryWiFi.saveWifi(); - send(200, "text/html", NefryWeb.createHtml(F("Nefry Wifi Delete"), "", (String)F("

    Nefry Wifi Delete

    Delete List

      ") + del + (String)F("
    Back to top"))); - if (deleteFlg ==true)resetTimer(2); - } - else if (url.equalsIgnoreCase("/config")) { - send(200, "text/html", NefryConfig.beginWeb("config")); - } - else if (url.equalsIgnoreCase("/set_config")) { - char webarg[5] = { "smo0" }; - for (int i = 0; i < 10; i++) { - webarg[3] = '0' + i; - String s = arg(webarg); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(webarg); - DEBUG_OUTPUT.print(" : "); - DEBUG_OUTPUT.println(s); -#endif - NefryDataStore.setStorageStr(s,i); - } - webarg[0] = 'i'; - for (int i = 0; i < 10; i++) { - webarg[3] = '0' + i; - String s = arg(webarg); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(webarg); - DEBUG_OUTPUT.print(" : "); - DEBUG_OUTPUT.println(s); -#endif - NefryDataStore.setStorageValue(s.toInt(),i); - } - resetTimer(2); - send(200, "text/html", NefryWeb.createHtml(F("Nefry DataStore Set"), "", F("

    Nefry Module Set

    Saveing & Restart...

    Back to top"))); - resetTimer(2); - } - /* "/"は必ず一番下にすること */ - else if (url.equalsIgnoreCase("/")) { - send(200, "text/html", NefryWeb.beginWeb("index")); - } - else { - send(404, "text/html", NefryWeb.beginWeb("NotFound")); - } - _currentClient.flush(); - _currentClient = WiFiClient(); -} - -void Nefry_webserver::sendHeader(const String& name, const String& value, bool first) { - String headerLine = name; - headerLine += ": "; - headerLine += value; - headerLine += "\r\n"; - - if (first) { - _responseHeaders = headerLine + _responseHeaders; - } - else { - _responseHeaders += headerLine; - } -} -void Nefry_webserver::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { - response = "HTTP/1.1 "; - response += String(code); - response += " "; - response += _responseCodeToString(code); - response += "\r\n"; - - if (!content_type) - content_type = "text/html"; - - sendHeader("Content-Type", content_type, true); - sendHeader("Content-Length", String(contentLength)); - sendHeader("Connection", "close"); - sendHeader("Access-Control-Allow-Origin", "*"); - - response += _responseHeaders; - response += "\r\n"; - _responseHeaders = String(); -} -void Nefry_webserver::send(int code, const char* content_type, const String& content) { - String header; - _prepareHeader(header, code, content_type, content.length()); - sendContent(header); - sendContent(content); -} -void Nefry_webserver::sendContent(const String& content) { - const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; - size_t size_to_send = content.length(); - const char* send_start = content.c_str(); - - while (size_to_send) { - size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; - size_t sent = _currentClient.write(send_start, will_send); - if (sent == 0) { - break; - } - size_to_send -= sent; - send_start += sent; - } } -String Nefry_webserver::_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 ""; - } -} - -void Nefry_webserver::_parseArguments(String data) { - if (_currentArgs) - delete[] _currentArgs; - _currentArgCount = 0; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args: "); - DEBUG_OUTPUT.println(data); -#endif - if (data.length() == 0)return; - _currentArgCount = 1; - for (int i = 0; i < (int)data.length(); ) { - i = data.indexOf('&', i); - if (i == -1) - break; - ++i; - ++_currentArgCount; - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(_currentArgCount); -#endif - - _currentArgs = new RequestArgument[_currentArgCount]; - int pos = 0; - int iarg; - for (iarg = 0; iarg < _currentArgCount;) { - int equal_sign_index = data.indexOf('=', pos); - int next_arg_index = data.indexOf('&', pos); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("pos "); - DEBUG_OUTPUT.print(pos); - DEBUG_OUTPUT.print("=@ "); - DEBUG_OUTPUT.print(equal_sign_index); - DEBUG_OUTPUT.print(" &@ "); - DEBUG_OUTPUT.println(next_arg_index); -#endif - if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("arg missing value: "); - DEBUG_OUTPUT.println(iarg); -#endif - if (next_arg_index == -1) - break; - pos = next_arg_index + 1; - continue; - } - _currentArgs[iarg].key = data.substring(pos, equal_sign_index); - _currentArgs[iarg].value = escapeParameter(data.substring(equal_sign_index + 1, next_arg_index)); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("arg "); - DEBUG_OUTPUT.print(iarg); - DEBUG_OUTPUT.print(" key: "); - DEBUG_OUTPUT.print(_currentArgs[iarg].key); - DEBUG_OUTPUT.print(" value: "); - DEBUG_OUTPUT.println(_currentArgs[iarg].value); -#endif - ++iarg; - if (next_arg_index == -1) - break; - pos = next_arg_index + 1; - } - _currentArgCount = iarg; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(_currentArgCount); -#endif - -} -String Nefry_webserver::arg(String name) { - for (int i = 0; i < _currentArgCount; ++i) { - if (_currentArgs[i].key.equals(name)) - return _currentArgs[i].value; - } - return String(); +ESP32WebServer* Nefry_webserver::getWebServer() +{ + return &_nefryWebServer; } String Nefry_webserver::escapeParameter(String param) { param.replace("+", " "); @@ -366,5 +68,4 @@ String Nefry_webserver::escapeParameter(String param) { return param; } - Nefry_webserver NefryWebServer; \ No newline at end of file diff --git a/cores/esp32/nefry/NefryWebServer.h b/cores/esp32/nefry/NefryWebServer.h index b37b2aa33c7..9279bbbdc8c 100644 --- a/cores/esp32/nefry/NefryWebServer.h +++ b/cores/esp32/nefry/NefryWebServer.h @@ -2,24 +2,10 @@ #define NefryWebServer_h #include "./inc/WiFi/src/WiFi.h" +#include "./inc/DNSServer/src/DNSServer.h" #include - +#include "./inc/ESP32WebServer/src/ESP32WebServer.h" #include "Nefry.h" -#include "NefryWeb.h" -#include "NefryWiFi.h" -#include "NefryConfig.h" - -#define HTTP_DOWNLOAD_UNIT_SIZE 1460 -#define HTTP_UPLOAD_BUFLEN 2048 -#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request -#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive -#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) - -#define DEBUG_OUTPUT Serial -//#define DEBUG_ESP_HTTP_SERVERR class Nefry_webserver { @@ -28,28 +14,11 @@ class Nefry_webserver begin(), run(), resetTimer(int delaySec); - String arg(String name); + ESP32WebServer* getWebServer(); + String escapeParameter(String param); private: - WiFiServer _server; - WiFiClient _currentClient; - String _responseHeaders; - size_t _contentLength; bool resetFlg; int countdown; - void - sendHeader(const String& name, const String& value, bool first = false), - _prepareHeader(String& response, int code, const char* content_type, size_t contentLength), - send(int code, const char* content_type, const String& content), - sendContent(const String& content); - String escapeParameter(String param); - String _responseCodeToString(int code); - void _parseArguments(String data); - struct RequestArgument { - String key; - String value; - }; - int _currentArgCount; - RequestArgument* _currentArgs; }; extern Nefry_webserver NefryWebServer; #endif diff --git a/cores/esp32/nefry/NefryWiFi.cpp b/cores/esp32/nefry/NefryWiFi.cpp index acf27dba6c3..dfa1975eba0 100644 --- a/cores/esp32/nefry/NefryWiFi.cpp +++ b/cores/esp32/nefry/NefryWiFi.cpp @@ -15,7 +15,7 @@ ConnectPass : Nefryが接続するWiFiのパスワードを保存するときに #include "NefryWiFi.h" bool initflgWifi = false; -void Nefry_WiFi::begin() { +void Nefry_WiFi::begin() { WiFi.persistent(false); WiFi.mode(WIFI_AP_STA); wifiMulti = WiFiMulti(); @@ -44,8 +44,10 @@ void Nefry_WiFi::begin() { Nefry.setLed(200, 0, 0); } scanWiFi(); //WiFiを検索し、Webページに表示する + if (NefryDataStore.getModuleID().equals("")) + NefryDataStore.setModuleID(getDefaultModuleId()); /* Nefryが発信するWiFiの設定*/ - if ( Nefry.getWriteMode() || NefryDataStore.getModulePass().length() == 0) { + if (Nefry.getWriteMode() || NefryDataStore.getModulePass().length() == 0) { WiFi.softAP(NefryDataStore.getModuleID().c_str()); Serial.println(F("\nWaiting for WiFi to connect")); } @@ -75,7 +77,7 @@ run関数で返す値 */ if (initflgWifi == false)return 1; if (getWifiTimeout() == -1)return 1; - if (getWifiTimeout() !=0 && getWifiTimeout() <= _WifiTimeOutCount)return 2; + if (getWifiTimeout() != 0 && getWifiTimeout() <= _WifiTimeOutCount)return 2; uint8_t wifiStatus = wifiMulti.run(); if (prevWifiStatus != wifiStatus) { prevWifiStatus = wifiStatus; @@ -129,9 +131,9 @@ run関数で返す値 return -1; } -String Nefry_WiFi::beginWeb(String url) +void Nefry_WiFi::beginWeb() { - if (url.equals("wifi_conf")) { + NefryWebServer.getWebServer()->on("/wifi_conf", [&]() { String content = F( "

    Nefry Wifi Set

    " "
    " @@ -147,7 +149,7 @@ String Nefry_WiFi::beginWeb(String url) content += network_list; content += F("

    Saved WiFi List

    Delete WiFi Select

    "); for (int i = 0; i < 5; i++) { - if (!_nefryssid[i].equals("")){ + if (!_nefryssid[i].equals("")) { content += F(""); @@ -156,17 +158,61 @@ String Nefry_WiFi::beginWeb(String url) } } content += F(""); - return NefryWeb.createHtml(F("Nefry Wifi Confing"), "", content); - } - else if (url.equals("wifiReload")) { + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Nefry Wifi Confing"), "", content)); + }); + + NefryWebServer.getWebServer()->on("/wifiReload", [&]() { scanWiFi(); - return NefryWeb.createHtml(F("Wifi Reload"),(String)F(""), F("

    Please wait...

    ")); - } - else if (url.equals("wifiCount")) { + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Wifi Reload"), + (String)F(""), + F("

    Please wait...

    "))); + }); + + NefryWebServer.getWebServer()->on("/wifiCount", [&]() { setWifiTimeoutClear(); - return NefryWeb.createHtml(F("Wifi Count Clear"), (String)F(""), F("

    Please wait...

    ")); - } - return ""; + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Wifi Count Clear"), (String)F(""), F("

    Please wait...

    "))); + }); + + NefryWebServer.getWebServer()->on("/set_wifi", [&]() { + String newssid = NefryWebServer.getWebServer()->arg("ssid"); + String newpwd = NefryWebServer.getWebServer()->arg("pwd"); + Serial.print("newssid : "); + Serial.println(newssid); + String content; + if (newssid.length() > 0) { + NefryWiFi.addWifi(newssid, newpwd); + NefryWiFi.saveWifi(); + content = F("Save SSID:"); + content += newssid; + content += F(" Restart to boot into new WiFi"); + } + else { + content = F("Empty SSID is not acceptable."); + } + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Nefry Wifi Set"), "", (String)F("

    Nefry Wifi Set

    ") + content + (String)F("

    Back to top"))); + Serial.print("dateSend"); + if (newssid.length() != 0)NefryWebServer.resetTimer(2); + }); + + NefryWebServer.getWebServer()->on("/delete_wifi", [&]() { + String del = "", ssid; + bool deleteFlg = false; + for (int i = 0; i < 5; i++) { + String data = NefryWebServer.getWebServer()->arg(i); + if (data.equals("1")) { + ssid = NefryWiFi.deleteWifi(i); + if (!ssid.equals("")) { + del += "
  • "; + del += ssid; + del += "
  • "; + deleteFlg = true; + } + } + } + NefryWiFi.saveWifi(); + NefryWebServer.getWebServer()->send(200, "text/html", NefryWeb.createHtml(F("Nefry Wifi Delete"), "", (String)F("

    Nefry Wifi Delete

    Delete List

      ") + del + (String)F("
    Back to top"))); + if (deleteFlg == true)NefryWebServer.resetTimer(2); + }); } @@ -177,7 +223,7 @@ String Nefry_WiFi::deleteWifi(int id) if (id < 0 || id >= 5)return ""; String ssid = _nefryssid[id]; _nefryssid[id] = ""; - _nefrypwd[id]=""; + _nefrypwd[id] = ""; return ssid; } @@ -187,14 +233,14 @@ void Nefry_WiFi::addWifi(String _ssid, String _pwd) for (int i = 0; i < 5; i++) { if (_nefryssid[i].equals("")) { _nefryssid[i] = _ssid; - _nefrypwd[i]=_pwd; + _nefrypwd[i] = _pwd; return; } } deleteWifi(0); sortWifi(); - _nefryssid[4]=_ssid; - _nefrypwd[4]=_pwd; + _nefryssid[4] = _ssid; + _nefrypwd[4] = _pwd; } int Nefry_WiFi::sortWifi() { @@ -217,19 +263,19 @@ int Nefry_WiFi::sortWifi() void Nefry_WiFi::saveWifi() { sortWifi(); for (int i = 0; i < 5; i++) { - NefryDataStore.setConnectSSID(_nefryssid[i],i); - NefryDataStore.setConnectPass(_nefrypwd[i],i); + NefryDataStore.setConnectSSID(_nefryssid[i], i); + NefryDataStore.setConnectPass(_nefrypwd[i], i); } } String Nefry_WiFi::getlistWifi() { String lisWifi = ""; for (int i = 0; i < 5; i++) { if (!_nefryssid[i].equals("")) { - lisWifi += "ID : "; - lisWifi += i; - lisWifi += " SSID : "; - lisWifi += _nefryssid[i]; - lisWifi += "\n"; + lisWifi += "ID : "; + lisWifi += i; + lisWifi += " SSID : "; + lisWifi += _nefryssid[i]; + lisWifi += "\n"; } } return lisWifi; @@ -276,7 +322,7 @@ void Nefry_WiFi::dataCache() _nefryssid[i] = NefryDataStore.getConnectSSID(i); _nefrypwd[i] = NefryDataStore.getConnectPass(i); } - + } /* 文字置換 */ String Nefry_WiFi::escapeParameter(String param) { @@ -327,4 +373,20 @@ void Nefry_WiFi::setWifiTimeoutClear() _WifiTimeOutCount = 0; } +String Nefry_WiFi::getDefaultModuleId() { + uint8_t macAddr[6]; + char str[15]; + char* moduleName; + WiFi.macAddress(macAddr); + switch (boardId) + { + case 0:case 1: + moduleName = "Nefry"; + break; + } + sprintf(str, "%s-%02x%02x", moduleName, macAddr[6 - 2], macAddr[6 - 1]); + Serial.println(str); + return str; +} + Nefry_WiFi NefryWiFi; \ No newline at end of file diff --git a/cores/esp32/nefry/NefryWiFi.h b/cores/esp32/nefry/NefryWiFi.h index e8d2b7226a1..8e2f7d84195 100644 --- a/cores/esp32/nefry/NefryWiFi.h +++ b/cores/esp32/nefry/NefryWiFi.h @@ -4,7 +4,7 @@ #include "./inc/WiFi/src/WiFiMulti.h" #include "Nefry.h" #include "./inc/Preferences/src/Preferences.h" -#include"NefryWeb.h" +#include "NefryWeb.h" #include "NefryWebServer.h" class Nefry_WiFi @@ -12,16 +12,19 @@ class Nefry_WiFi public: void begin(), + beginWeb(), addWifi(String ssid, String pwd), setWifiTimeout(int count), setWifiTimeoutClear(), saveWifi(); + String getDefaultModuleId(); + int run(), getWifiTimeout(); - String beginWeb(String s), + String deleteWifi(int id), getlistWifi(); diff --git a/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortal/CaptivePortal.ino b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortal/CaptivePortal.ino new file mode 100644 index 00000000000..eb22782205d --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortal/CaptivePortal.ino @@ -0,0 +1,34 @@ +#include +#include +#include + +const byte DNS_PORT = 53; +IPAddress apIP(192, 168, 1, 1); +DNSServer dnsServer; +ESP8266WebServer webServer(80); + +String responseHTML = "" + "CaptivePortal" + "

    Hello World!

    This is a captive portal example. All requests will " + "be redirected here.

    "; + +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(); +} diff --git a/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino new file mode 100644 index 00000000000..229662651e6 --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include + +/* + * 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 */ +const char *softAP_ssid = "ESP_ap"; +const char *softAP_password = "12345678"; + +/* 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 */ +long lastConnectTry = 0; + +/** Current WLAN status */ +int status = WL_IDLE_STATUS; + +void setup() { + delay(1000); + Serial.begin(9600); + Serial.println(); + Serial.print("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(); + } + { + 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(); + } + } + } + // Do work: + //DNS + dnsServer.processNextRequest(); + //HTTP + server.handleClient(); +} + diff --git a/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/credentials.ino b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/credentials.ino new file mode 100644 index 00000000000..3f9501e7964 --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/credentials.ino @@ -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?"********":""); +} + +/** 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(); +} diff --git a/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino new file mode 100644 index 00000000000..50e7823aca8 --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino @@ -0,0 +1,131 @@ +/** 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"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.sendContent( + "" + "

    HELLO WORLD!!

    " + ); + if (server.client().localIP() == apIP) { + server.sendContent(String("

    You are connected through the soft AP: ") + softAP_ssid + "

    "); + } else { + server.sendContent(String("

    You are connected through the wifi network: ") + ssid + "

    "); + } + server.sendContent( + "

    You may want to config the wifi connection.

    " + "" + ); + server.client().stop(); // Stop is needed because we sent no content length +} + +/** 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.print("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"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.sendContent( + "" + "

    Wifi config

    " + ); + if (server.client().localIP() == apIP) { + server.sendContent(String("

    You are connected through the soft AP: ") + softAP_ssid + "

    "); + } else { + server.sendContent(String("

    You are connected through the wifi network: ") + ssid + "

    "); + } + server.sendContent( + "\r\n
    " + "" + ); + server.sendContent(String() + ""); + server.sendContent(String() + ""); + server.sendContent( + "
    SoftAP config
    SSID " + String(softAP_ssid) + "
    IP " + toStringIp(WiFi.softAPIP()) + "
    " + "\r\n
    " + "" + ); + server.sendContent(String() + ""); + server.sendContent(String() + ""); + server.sendContent( + "
    WLAN config
    SSID " + String(ssid) + "
    IP " + toStringIp(WiFi.localIP()) + "
    " + "\r\n
    " + "" + ); + Serial.println("scan start"); + int n = WiFi.scanNetworks(); + Serial.println("scan done"); + if (n > 0) { + for (int i = 0; i < n; i++) { + server.sendContent(String() + "\r\n"); + } + } else { + server.sendContent(String() + ""); + } + server.sendContent( + "
    WLAN list (refresh if any missing)
    SSID " + WiFi.SSID(i) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")
    No WLAN found
    " + "\r\n

    Connect to network:

    " + "" + "
    " + "
    " + "

    You may want to return to the home 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 = "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.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send ( 404, "text/plain", message ); +} + diff --git a/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/tools.ino b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/tools.ino new file mode 100644 index 00000000000..5b6d7893112 --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/CaptivePortalAdvanced/tools.ino @@ -0,0 +1,21 @@ +/** Is this an IP? */ +boolean isIp(String str) { + for (int 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; +} + diff --git a/cores/esp32/nefry/inc/DNSServer/examples/DNSServer/DNSServer.ino b/cores/esp32/nefry/inc/DNSServer/examples/DNSServer/DNSServer.ino new file mode 100644 index 00000000000..2916d5ab3ca --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/examples/DNSServer/DNSServer.ino @@ -0,0 +1,41 @@ +#include +#include +#include + +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(); +} diff --git a/cores/esp32/nefry/inc/DNSServer/library.properties b/cores/esp32/nefry/inc/DNSServer/library.properties new file mode 100644 index 00000000000..fd257dcca8f --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/library.properties @@ -0,0 +1,9 @@ +name=DNSServer +version=1.1.0 +author=Kristijan Novoselić +maintainer=Kristijan Novoselić, +sentence=A simple DNS server for ESP8266. +paragraph=This library implements a simple DNS server. +category=Communication +url= +architectures=esp32 diff --git a/cores/esp32/nefry/inc/DNSServer/src/DNSServer.cpp b/cores/esp32/nefry/inc/DNSServer/src/DNSServer.cpp new file mode 100644 index 00000000000..67ee475b37d --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/src/DNSServer.cpp @@ -0,0 +1,172 @@ +#include "DNSServer.h" +#include +#include + +//#define DEBUG +//#define DEBUG_OUTPUT Serial +DNSServer::DNSServer() +{ + _ttl = htonl(60); + _errorReplyCode = DNSReplyCode::NonExistentDomain; +} + +bool DNSServer::start(const uint16_t &port, const String &domainName, + const IPAddress &resolvedIP) +{ + _port = port; + _buffer = NULL; + _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 = htonl(ttl); +} + +void DNSServer::stop() +{ + _udp.stop(); + free(_buffer); + _buffer = NULL; +} + +void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) +{ + domainName.toLowerCase(); + domainName.replace("www.", ""); +} + +void DNSServer::processNextRequest() +{ + _currentPacketSize = _udp.parsePacket(); + if (_currentPacketSize) + { + if (_buffer != NULL) free(_buffer); + _buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char)); + if (_buffer == NULL) return; + _udp.read(_buffer, _currentPacketSize); + _dnsHeader = (DNSHeader*) _buffer; + + if (_dnsHeader->QR == DNS_QR_QUERY && + _dnsHeader->OPCode == DNS_OPCODE_QUERY && + requestIncludesOnlyOneQuestion() && + (_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName) + ) + { + replyWithIP(); + } + else if (_dnsHeader->QR == DNS_QR_QUERY) + { + replyWithCustomCode(); + } + + free(_buffer); + _buffer = NULL; + } +} + +bool DNSServer::requestIncludesOnlyOneQuestion() +{ + return ntohs(_dnsHeader->QDCount) == 1 && + _dnsHeader->ANCount == 0 && + _dnsHeader->NSCount == 0 && + _dnsHeader->ARCount == 0; +} + +String DNSServer::getDomainNameWithoutWwwPrefix() +{ + String parsedDomainName = ""; + if (_buffer == NULL) return parsedDomainName; + unsigned char *start = _buffer + 12; + if (*start == 0) + { + return parsedDomainName; + } + int pos = 0; + while(true) + { + unsigned char labelLength = *(start + pos); + for(int i = 0; i < labelLength; i++) + { + pos++; + parsedDomainName += (char)*(start + pos); + } + pos++; + if (*(start + pos) == 0) + { + downcaseAndRemoveWwwPrefix(parsedDomainName); + return parsedDomainName; + } + else + { + parsedDomainName += "."; + } + } +} + +void DNSServer::replyWithIP() +{ + if (_buffer == NULL) return; + _dnsHeader->QR = DNS_QR_RESPONSE; + _dnsHeader->ANCount = _dnsHeader->QDCount; + _dnsHeader->QDCount = _dnsHeader->QDCount; + //_dnsHeader->RA = 1; + + _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); + _udp.write(_buffer, _currentPacketSize); + + _udp.write((uint8_t)192); // answer name is a pointer + _udp.write((uint8_t)12); // pointer to offset at 0x00c + + _udp.write((uint8_t)0); // 0x0001 answer is type A query (host address) + _udp.write((uint8_t)1); + + _udp.write((uint8_t)0); //0x0001 answer is class IN (internet address) + _udp.write((uint8_t)1); + + _udp.write((unsigned char*)&_ttl, 4); + + // Length of RData is 4 bytes (because, in this case, RData is IPv4) + _udp.write((uint8_t)0); + _udp.write((uint8_t)4); + _udp.write(_resolvedIP, sizeof(_resolvedIP)); + _udp.endPacket(); + + + + #ifdef DEBUG + DEBUG_OUTPUT.print("DNS responds: "); + DEBUG_OUTPUT.print(_resolvedIP[0]); + DEBUG_OUTPUT.print("."); + DEBUG_OUTPUT.print(_resolvedIP[1]); + DEBUG_OUTPUT.print("."); + DEBUG_OUTPUT.print(_resolvedIP[2]); + DEBUG_OUTPUT.print("."); + DEBUG_OUTPUT.print(_resolvedIP[3]); + DEBUG_OUTPUT.print(" for "); + DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix()); + #endif +} + +void DNSServer::replyWithCustomCode() +{ + if (_buffer == NULL) return; + _dnsHeader->QR = DNS_QR_RESPONSE; + _dnsHeader->RCode = (unsigned char)_errorReplyCode; + _dnsHeader->QDCount = 0; + + _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); + _udp.write(_buffer, sizeof(DNSHeader)); + _udp.endPacket(); +} diff --git a/cores/esp32/nefry/inc/DNSServer/src/DNSServer.h b/cores/esp32/nefry/inc/DNSServer/src/DNSServer.h new file mode 100644 index 00000000000..359e8b148b3 --- /dev/null +++ b/cores/esp32/nefry/inc/DNSServer/src/DNSServer.h @@ -0,0 +1,71 @@ +#ifndef DNSServer_h +#define DNSServer_h +#include "../../WiFi/src/WiFiUdp.h" + +#define DNS_QR_QUERY 0 +#define DNS_QR_RESPONSE 1 +#define DNS_OPCODE_QUERY 0 + +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(); + 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]; + int _currentPacketSize; + unsigned char* _buffer; + DNSHeader* _dnsHeader; + uint32_t _ttl; + DNSReplyCode _errorReplyCode; + + void downcaseAndRemoveWwwPrefix(String &domainName); + String getDomainNameWithoutWwwPrefix(); + bool requestIncludesOnlyOneQuestion(); + void replyWithIP(); + void replyWithCustomCode(); +}; +#endif \ No newline at end of file diff --git a/cores/esp32/nefry/inc/ESP32WebServer/keywords.txt b/cores/esp32/nefry/inc/ESP32WebServer/keywords.txt new file mode 100644 index 00000000000..22e3d74e426 --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/keywords.txt @@ -0,0 +1,36 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266WebServer KEYWORD1 +HTTPMethod KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +handleClient KEYWORD2 +on KEYWORD2 +addHandler KEYWORD2 +uri KEYWORD2 +method KEYWORD2 +client KEYWORD2 +send KEYWORD2 +arg KEYWORD2 +argName KEYWORD2 +args KEYWORD2 +hasArg KEYWORD2 +onNotFound KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +HTTP_GET LITERAL1 +HTTP_POST LITERAL1 +HTTP_ANY LITERAL1 diff --git a/cores/esp32/nefry/inc/ESP32WebServer/library.properties b/cores/esp32/nefry/inc/ESP32WebServer/library.properties new file mode 100644 index 00000000000..2771a2bdb34 --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/library.properties @@ -0,0 +1,9 @@ +name=ESP32WebServer +version=1.0 +author=Ivan Grokhotkov +maintainer=Ivan Grokhtkov +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=esp32 diff --git a/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.cpp b/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.cpp new file mode 100644 index 00000000000..d7dbe773a2f --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.cpp @@ -0,0 +1,497 @@ +/* + ESP32WebServer.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 +#include +#include "../../WiFi/src/WiFiServer.h" +#include "../../WiFi/src/WiFiClient.h" +#include "./ESP32WebServer.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 + +const char * AUTHORIZATION_HEADER = "Authorization"; + +ESP32WebServer::ESP32WebServer(IPAddress addr, int port) + : _server(addr, port) + , _currentMethod(HTTP_ANY) + , _currentHandler(0) + , _firstHandler(0) + , _lastHandler(0) + , _currentArgCount(0) + , _currentArgs(0) + , _headerKeysCount(0) + , _currentHeaders(0) + , _contentLength(0) +{ +} + +ESP32WebServer::ESP32WebServer(int port) + : _server(port) + , _currentMethod(HTTP_ANY) + , _currentHandler(0) + , _firstHandler(0) + , _lastHandler(0) + , _currentArgCount(0) + , _currentArgs(0) + , _headerKeysCount(0) + , _currentHeaders(0) + , _contentLength(0) +{ +} + +ESP32WebServer::~ESP32WebServer() { + if (_currentHeaders) + delete[]_currentHeaders; + _headerKeysCount = 0; + RequestHandler* handler = _firstHandler; + while (handler) { + RequestHandler* next = handler->next(); + delete handler; + handler = next; + } + close(); +} + +void ESP32WebServer::begin() { + _server.begin(); + if (!_headerKeysCount) + collectHeaders(0, 0); +} + +bool ESP32WebServer::authenticate(const char * username, const char * password) { + if (hasHeader(AUTHORIZATION_HEADER)) { + String authReq = header(AUTHORIZATION_HEADER); + if (authReq.startsWith("Basic")) { + authReq = authReq.substring(6); + authReq.trim(); + char toencodeLen = strlen(username) + strlen(password) + 1; + char *toencode = new char[toencodeLen + 1]; + if (toencode == NULL) { + authReq = String(); + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; + if (encoded == NULL) { + authReq = String(); + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)) { + authReq = String(); + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + } + authReq = String(); + } + return false; +} + +void ESP32WebServer::requestAuthentication() { + sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); + send(401); +} + +void ESP32WebServer::on(const char* uri, ESP32WebServer::THandlerFunction handler) { + on(uri, HTTP_ANY, handler); +} + +void ESP32WebServer::on(const char* uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn) { + on(uri, method, fn, _fileUploadHandler); +} + +void ESP32WebServer::on(const char* uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn) { + _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +} + +void ESP32WebServer::addHandler(RequestHandler* handler) { + _addRequestHandler(handler); +} + +void ESP32WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next(handler); + _lastHandler = handler; + } +} + +void ESP32WebServer::handleClient() { + WiFiClient client = _server.available(); + if (!client) { + return; + } + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("New client"); +#endif + _currentClient = client; + _statusChange = millis(); + + if (!_currentClient.connected()) { + _currentClient = WiFiClient(); + return; + } + if (!_parseRequest(_currentClient)) { + _currentClient = WiFiClient(); + return; + } + // Wait for data from client to become available + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + if (!_currentClient.connected()) { + _currentClient = WiFiClient(); + return; + } + _currentClient.flush(); + _currentClient = WiFiClient(); +} + +void ESP32WebServer::close() { + _server.end(); +} + +void ESP32WebServer::stop() { + close(); +} + +void ESP32WebServer::sendHeader(const String& name, const String& value, bool first) { + String headerLine = name; + headerLine += ": "; + headerLine += value; + headerLine += "\r\n"; + + if (first) { + _responseHeaders = headerLine + _responseHeaders; + } + else { + _responseHeaders += headerLine; + } +} + + +void ESP32WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { + response = "HTTP/1.1 "; + response += String(code); + response += " "; + response += _responseCodeToString(code); + response += "\r\n"; + + if (!content_type) + content_type = "text/html"; + + sendHeader("Content-Type", content_type, true); + if (_contentLength == CONTENT_LENGTH_NOT_SET) { + sendHeader("Content-Length", String(contentLength)); + } + else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { + sendHeader("Content-Length", String(_contentLength)); + } + sendHeader("Connection", "close"); + sendHeader("Access-Control-Allow-Origin", "*"); + + response += _responseHeaders; + response += "\r\n"; + _responseHeaders = String(); +} + +void ESP32WebServer::send(int code, const char* content_type, const String& content) { + String header; + _prepareHeader(header, code, content_type, content.length()); + sendContent(header); + + sendContent(content); +} + +void ESP32WebServer::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); + sendContent(header); + sendContent_P(content); +} + +void ESP32WebServer::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 ESP32WebServer::send(int code, char* content_type, const String& content) { + send(code, (const char*)content_type, content); +} + +void ESP32WebServer::send(int code, const String& content_type, const String& content) { + send(code, (const char*)content_type.c_str(), content); +} + +void ESP32WebServer::sendContent(const String& content) { + const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; + size_t size_to_send = content.length(); + const char* send_start = content.c_str(); + + while (size_to_send) { + size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; + size_t sent = _currentClient.write(send_start, will_send); + if (sent == 0) { + break; + } + size_to_send -= sent; + send_start += sent; + } +} + +void ESP32WebServer::sendContent_P(PGM_P content) { + char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1]; + + contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0'; + + while (content != NULL) { + size_t contentUnitLen; + PGM_P contentNext; + + // due to the memccpy signature, lots of casts are needed + contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE); + + if (contentNext == NULL) { + // no terminator, more data available + content += HTTP_DOWNLOAD_UNIT_SIZE; + contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE; + } + else { + // reached terminator. Do not send the terminator + contentUnitLen = contentNext - contentUnit - 1; + content = NULL; + } + + // write is so overloaded, had to use the cast to get it pick the right one + _currentClient.write((const char*)contentUnit, contentUnitLen); + } +} + +void ESP32WebServer::sendContent_P(PGM_P content, size_t size) { + char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1]; + contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0'; + size_t remaining_size = size; + + while (content != NULL && remaining_size > 0) { + size_t contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE; + + if (remaining_size < HTTP_DOWNLOAD_UNIT_SIZE) contentUnitLen = remaining_size; + // due to the memcpy signature, lots of casts are needed + memcpy_P((void*)contentUnit, (PGM_VOID_P)content, contentUnitLen); + + content += contentUnitLen; + remaining_size -= contentUnitLen; + + // write is so overloaded, had to use the cast to get it pick the right one + _currentClient.write((const char*)contentUnit, contentUnitLen); + } +} + + +String ESP32WebServer::arg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return _currentArgs[i].value; + } + return String(); +} + +String ESP32WebServer::arg(int i) { + if (i < _currentArgCount) + return _currentArgs[i].value; + return String(); +} + +String ESP32WebServer::argName(int i) { + if (i < _currentArgCount) + return _currentArgs[i].key; + return String(); +} + +int ESP32WebServer::args() { + return _currentArgCount; +} + +bool ESP32WebServer::hasArg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return true; + } + return false; +} + + +String ESP32WebServer::header(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if (_currentHeaders[i].key == name) + return _currentHeaders[i].value; + } + return String(); +} + +void ESP32WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { + _headerKeysCount = headerKeysCount + 1; + if (_currentHeaders) + delete[]_currentHeaders; + _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders[0].key = AUTHORIZATION_HEADER; + for (int i = 1; i < _headerKeysCount; i++) { + _currentHeaders[i].key = headerKeys[i - 1]; + } +} + +String ESP32WebServer::header(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].value; + return String(); +} + +String ESP32WebServer::headerName(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].key; + return String(); +} + +int ESP32WebServer::headers() { + return _headerKeysCount; +} + +bool ESP32WebServer::hasHeader(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if ((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) + return true; + } + return false; +} + +String ESP32WebServer::hostHeader() { + return _hostHeader; +} + +void ESP32WebServer::onFileUpload(THandlerFunction fn) { + _fileUploadHandler = fn; +} + +void ESP32WebServer::onNotFound(THandlerFunction fn) { + _notFoundHandler = fn; +} + +void ESP32WebServer::_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) { + if (_notFoundHandler) { + _notFoundHandler(); + } + else { + send(404, "text/plain", String("Not found: ") + _currentUri); + } + } + + _currentUri = String(); +} + +String ESP32WebServer::_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 ""; + } +} diff --git a/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.h b/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.h new file mode 100644 index 00000000000..c51f5b84d10 --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/src/ESP32WebServer.h @@ -0,0 +1,175 @@ +/* + ESP32WebServer.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 ESP32WebServer_H +#define ESP32WebServer_H + +#include +#include "../../WiFi/src/WiFi.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 }; + +#define HTTP_DOWNLOAD_UNIT_SIZE 1460 +#define HTTP_UPLOAD_BUFLEN 2048 +#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request +#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive +#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 ESP32WebServer; + +typedef struct { + HTTPUploadStatus status; + String filename; + String name; + String type; + size_t totalSize; // file size + size_t currentSize; // size of data currently in buf + uint8_t buf[HTTP_UPLOAD_BUFLEN]; +} HTTPUpload; + +#include "detail/RequestHandler.h" + + +class ESP32WebServer +{ +public: + ESP32WebServer(IPAddress addr, int port = 80); + ESP32WebServer(int port = 80); + ~ESP32WebServer(); + + void begin(); + void handleClient(); + + void close(); + void stop(); + + bool authenticate(const char * username, const char * password); + void requestAuthentication(); + + typedef std::function THandlerFunction; + void on(const char* uri, THandlerFunction handler); + void on(const char* uri, HTTPMethod method, THandlerFunction fn); + void on(const char* uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void addHandler(RequestHandler* handler); + void onNotFound(THandlerFunction fn); //called when handler is not assigned + void onFileUpload(THandlerFunction fn); //handle file uploads + + String uri() { return _currentUri; } + HTTPMethod method() { return _currentMethod; } + WiFiClient client() { return _currentClient; } + HTTPUpload& upload() { return _currentUpload; } + + String arg(String name); // get request argument value by name + String arg(int i); // get request argument value by number + String argName(int i); // get request argument name by number + int args(); // get arguments count + bool hasArg(String name); // check if argument exists + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + String header(String name); // get request header value by name + String header(int i); // get request header value by number + String headerName(int i); // get request header name by number + int headers(); // get header count + bool hasHeader(String name); // check if header exists + + String hostHeader(); // 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(size_t contentLength) { _contentLength = 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 size_t streamFile(T &file, const String& contentType){ + setContentLength(file.size()); + if (String(file.name()).endsWith(".gz") && + contentType != "application/x-gzip" && + contentType != "application/octet-stream"){ + sendHeader("Content-Encoding", "gzip"); + } + send(200, contentType, ""); + return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); +} + +protected: + void _addRequestHandler(RequestHandler* handler); + void _handleRequest(); + bool _parseRequest(WiFiClient& client); + void _parseArguments(String data); + static String _responseCodeToString(int code); + bool _parseForm(WiFiClient& client, 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); + + struct RequestArgument { + String key; + String value; + }; + + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + unsigned long _statusChange; + + RequestHandler* _currentHandler; + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + THandlerFunction _notFoundHandler; + THandlerFunction _fileUploadHandler; + + int _currentArgCount; + RequestArgument* _currentArgs; + HTTPUpload _currentUpload; + + int _headerKeysCount; + RequestArgument* _currentHeaders; + size_t _contentLength; + String _responseHeaders; + + String _hostHeader; + +}; + + +#endif //ESP32WebServer_H diff --git a/cores/esp32/nefry/inc/ESP32WebServer/src/Parsing.cpp b/cores/esp32/nefry/inc/ESP32WebServer/src/Parsing.cpp new file mode 100644 index 00000000000..ed5edcc947e --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/src/Parsing.cpp @@ -0,0 +1,589 @@ +/* + 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 +#include "../../WiFi/src/WiFiServer.h" +#include "../../WiFi/src/WiFiClient.h" +#include "ESP32WebServer.h" + +//#define DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif + +static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +{ + char *buf = nullptr; + dataLength = 0; + while (dataLength < maxLength) { + int tries = timeout_ms; + size_t newLength; + while (!(newLength = client.available()) && tries--) delay(1); + if (!newLength) { + break; + } + if (!buf) { + buf = (char *) malloc(newLength + 1); + if (!buf) { + return nullptr; + } + } + else { + char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); + if (!newBuf) { + free(buf); + return nullptr; + } + buf = newBuf; + } + client.readBytes(buf + dataLength, newLength); + dataLength += newLength; + buf[dataLength] = '\0'; + } + return buf; +} + +bool ESP32WebServer::_parseRequest(WiFiClient& client) { + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + 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.print("Invalid request: "); + DEBUG_OUTPUT.println(req); +#endif + return false; + } + + String methodStr = req.substring(0, addr_start); + String url = req.substring(addr_start + 1, addr_end); + String searchStr = ""; + int hasSearch = url.indexOf('?'); + if (hasSearch != -1){ + searchStr = url.substring(hasSearch + 1); + url = url.substring(0, hasSearch); + } + _currentUri = url; + + HTTPMethod method = HTTP_GET; + if (methodStr == "POST") { + method = HTTP_POST; + } else if (methodStr == "DELETE") { + method = HTTP_DELETE; + } else if (methodStr == "OPTIONS") { + method = HTTP_OPTIONS; + } else if (methodStr == "PUT") { + method = HTTP_PUT; + } else if (methodStr == "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; + 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 == "Content-Type"){ + if (headerValue.startsWith("text/plain")){ + isForm = false; + } else if (headerValue.startsWith("multipart/form-data")){ + boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); + isForm = true; + } + } else if (headerName == "Content-Length"){ + contentLength = headerValue.toInt(); + } else if (headerName == "Host"){ + _hostHeader = headerValue; + } + } + + if (!isForm){ + size_t plainLength; + char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); + if (plainLength < contentLength) { + free(plainBuf); + return false; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Plain: "); + DEBUG_OUTPUT.println(plainBuf); +#endif + if (contentLength > 0) { + if (searchStr != "") searchStr += '&'; + if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL){ + //plain post json or other data + searchStr += "plain="; + searchStr += plainBuf; + } else { + searchStr += plainBuf; + } + free(plainBuf); + } + } + _parseArguments(searchStr); + if (isForm){ + 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("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName == "Host"){ + _hostHeader = headerValue; + } + } + _parseArguments(searchStr); + } + client.flush(); + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Request: "); + DEBUG_OUTPUT.println(url); + DEBUG_OUTPUT.print(" Arguments: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + return true; +} + +bool ESP32WebServer::_collectHeader(const char* headerName, const char* headerValue) { + for (int i = 0; i < _headerKeysCount; i++) { + if (_currentHeaders[i].key==headerName) { + _currentHeaders[i].value=headerValue; + return true; + } + } + return false; +} + +void ESP32WebServer::_parseArguments(String data) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args: "); + DEBUG_OUTPUT.println(data); +#endif + if (_currentArgs) + delete[] _currentArgs; + _currentArgs = 0; + if (data.length() == 0) { + _currentArgCount = 0; + return; + } + _currentArgCount = 1; + + for (int i = 0; i < (int)data.length(); ) { + i = data.indexOf('&', i); + if (i == -1) + break; + ++i; + ++_currentArgCount; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + + _currentArgs = new RequestArgument[_currentArgCount]; + int pos = 0; + int iarg; + for (iarg = 0; iarg < _currentArgCount;) { + int equal_sign_index = data.indexOf('=', pos); + int next_arg_index = data.indexOf('&', pos); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("pos "); + DEBUG_OUTPUT.print(pos); + DEBUG_OUTPUT.print("=@ "); + DEBUG_OUTPUT.print(equal_sign_index); + DEBUG_OUTPUT.print(" &@ "); + DEBUG_OUTPUT.println(next_arg_index); +#endif + if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg missing value: "); + DEBUG_OUTPUT.println(iarg); +#endif + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + continue; + } + RequestArgument& arg = _currentArgs[iarg]; + arg.key = data.substring(pos, equal_sign_index); + arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index)); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg "); + DEBUG_OUTPUT.print(iarg); + DEBUG_OUTPUT.print(" key: "); + DEBUG_OUTPUT.print(arg.key); + DEBUG_OUTPUT.print(" value: "); + DEBUG_OUTPUT.println(arg.value); +#endif + ++iarg; + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + } + _currentArgCount = iarg; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + +} + +void ESP32WebServer::_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 ESP32WebServer::_uploadReadByte(WiFiClient& client){ + int res = client.read(); + if(res == -1){ + while(!client.available() && client.connected()) + yield(); + res = client.read(); + } + return (uint8_t)res; +} + +bool ESP32WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t 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)){ + RequestArgument* postArgs = new RequestArgument[32]; + int postArgsLen = 0; + while(1){ + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("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 == "blob" && hasArg("filename")) argFilename = arg("filename"); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Name: "); + DEBUG_OUTPUT.println(argName); +#endif + argType = "text/plain"; + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("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.status = UPLOAD_FILE_START; + _currentUpload.name = argName; + _currentUpload.filename = argFilename; + _currentUpload.type = argType; + _currentUpload.totalSize = 0; + _currentUpload.currentSize = 0; +#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 = ((32 - postArgsLen) < _currentArgCount)?(32 - 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; + return true; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Error: line: "); + DEBUG_OUTPUT.println(line); +#endif + return false; +} + +String ESP32WebServer::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 ESP32WebServer::_parseFormUploadAborted(){ + _currentUpload.status = UPLOAD_FILE_ABORTED; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + return false; +} diff --git a/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandler.h b/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandler.h new file mode 100644 index 00000000000..84524877c67 --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandler.h @@ -0,0 +1,19 @@ +#ifndef REQUESTHANDLER_H +#define REQUESTHANDLER_H + +class RequestHandler { +public: + virtual ~RequestHandler() { } + virtual bool canHandle(HTTPMethod method, String uri) { return false; } + virtual bool canUpload(String uri) { return false; } + virtual bool handle(ESP32WebServer& server, HTTPMethod requestMethod, String requestUri) { return false; } + virtual void upload(ESP32WebServer& server, String requestUri, HTTPUpload& upload) {} + + RequestHandler* next() { return _next; } + void next(RequestHandler* r) { _next = r; } + +private: + RequestHandler* _next = nullptr; +}; + +#endif //REQUESTHANDLER_H diff --git a/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandlersImpl.h b/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandlersImpl.h new file mode 100644 index 00000000000..851dbe95d44 --- /dev/null +++ b/cores/esp32/nefry/inc/ESP32WebServer/src/detail/RequestHandlersImpl.h @@ -0,0 +1,55 @@ +#ifndef REQUESTHANDLERSIMPL_H +#define REQUESTHANDLERSIMPL_H + +#include "RequestHandler.h" + +class FunctionRequestHandler : public RequestHandler { +public: + FunctionRequestHandler(ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn, const char* uri, HTTPMethod method) + : _fn(fn) + , _ufn(ufn) + , _uri(uri) + , _method(method) + { + } + + bool canHandle(HTTPMethod requestMethod, String requestUri) override { + if (_method != HTTP_ANY && _method != requestMethod) + return false; + + if (requestUri != _uri) + return false; + + return true; + } + + bool canUpload(String requestUri) override { + if (!_ufn || !canHandle(HTTP_POST, requestUri)) + return false; + + return true; + } + + bool handle(ESP32WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (!canHandle(requestMethod, requestUri)) + return false; + + _fn(); + return true; + } + + void upload(ESP32WebServer& server, String requestUri, HTTPUpload& upload) override { + if (canUpload(requestUri)) + _ufn(); + } + +protected: + ESP32WebServer::THandlerFunction _fn; + ESP32WebServer::THandlerFunction _ufn; + String _uri; + HTTPMethod _method; +}; + + + +#endif //REQUESTHANDLERSIMPL_H diff --git a/libraries/Nefry/library.properties b/libraries/Nefry/library.properties index 5b2fb15e436..ff6f51f356d 100644 --- a/libraries/Nefry/library.properties +++ b/libraries/Nefry/library.properties @@ -1,5 +1,5 @@ name=Nefry -version=0.6.1 +version=0.6.2 author=Nefry community maintainer= sentence=nefry. diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 204044dd103..21940bc50e6 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -3,7 +3,7 @@ { "name": "esp32", "maintainer": "Espressif Systems", - "websiteURL": "https://github.com/espressif/esp32-arduino", + "websiteURL": "https://github.com/espressif/arduino-esp32", "email": "hristo@espressif.com", "help": { "online": "http://esp32.com" diff --git a/release note.md b/release note.md index 30c9cb96152..af28c17994b 100644 --- a/release note.md +++ b/release note.md @@ -1,5 +1,19 @@ # このノートはNefry(ESP32版)のリリースノートになります。 +## 0.6.2 + +NefryのWeb関連機能の修正とDNSの追加、それに伴う起動シークエンスの変更 + +新規機能 + +- DNS対応 +- CaptivePortal対応 +- webserver更新 + +バグフィックス + +- 起動シークエンスの変更 + ## 0.6.1 IFTTTライブラリー追加とバグフィックス対応 diff --git a/tools/partitions/default.csv b/tools/partitions/default.csv index 92b29eeba02..b0f4c20b097 100644 --- a/tools/partitions/default.csv +++ b/tools/partitions/default.csv @@ -1,6 +1,6 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000 -otadata, data, ota, 0xe000, 0x2000 +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 1M, -app1, app, ota_1, , 1M, -spiffs, data, spiffs, , 0x1F0000, +app1, app, ota_1, 0x110000,1M, +spiffs, data, spiffs, 0x210000,0x1F0000, diff --git a/variants/esp32/pins_arduino.h b/variants/esp32/pins_arduino.h index d50715e5c91..d852bb03452 100644 --- a/variants/esp32/pins_arduino.h +++ b/variants/esp32/pins_arduino.h @@ -53,4 +53,6 @@ static const uint8_t T9 = 32; static const uint8_t DAC1 = 25; static const uint8_t DAC2 = 26; +static const uint8_t boardId = 0;//ESP32 + #endif /* Pins_Arduino_h */ diff --git a/variants/nefrybt/pins_arduino.h b/variants/nefrybt/pins_arduino.h index 63375183afd..dfb29606ec6 100644 --- a/variants/nefrybt/pins_arduino.h +++ b/variants/nefrybt/pins_arduino.h @@ -46,4 +46,6 @@ static const uint8_t T4 = 13; static const uint8_t DAC1 = 25; static const uint8_t DAC2 = 26; +static const uint8_t boardId = 1;//Nefry BT + #endif /* Pins_Arduino_h */ diff --git a/variants/onehorse32dev/pins_arduino.h b/variants/onehorse32dev/pins_arduino.h new file mode 100644 index 00000000000..daa2d8cd6fd --- /dev/null +++ b/variants/onehorse32dev/pins_arduino.h @@ -0,0 +1,59 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define EXTERNAL_NUM_INTERRUPTS 16 +#define NUM_DIGITAL_PINS 40 +#define NUM_ANALOG_INPUTS 16 + +#define analogInputToDigitalPin(p) (((p)<20)?(esp32_adc2gpio[(p)]):-1) +#define digitalPinToInterrupt(p) (((p)<40)?(p):-1) +#define digitalPinHasPWM(p) (p < 34) + +static const uint8_t LED_BUILTIN = 5; +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t KEY_BUILTIN = 0; + +static const uint8_t TX = 1; +static const uint8_t RX = 3; + +static const uint8_t SDA = 21; +static const uint8_t SCL = 22; + +static const uint8_t SS = 5; +static const uint8_t MOSI = 23; +static const uint8_t MISO = 19; +static const uint8_t SCK = 18; + +static const uint8_t A0 = 36; +static const uint8_t A1 = 37; +static const uint8_t A2 = 38; +static const uint8_t A3 = 39; +static const uint8_t A6 = 34; +static const uint8_t A7 = 35; +static const uint8_t A10 = 4; +static const uint8_t A11 = 0; +static const uint8_t A12 = 2; +static const uint8_t A13 = 15; +static const uint8_t A14 = 13; +static const uint8_t A15 = 12; +static const uint8_t A16 = 14; +static const uint8_t A17 = 27; +static const uint8_t A18 = 25; +static const uint8_t A19 = 26; + +static const uint8_t T0 = 4; +static const uint8_t T1 = 0; +static const uint8_t T2 = 2; +static const uint8_t T3 = 15; +static const uint8_t T4 = 13; +static const uint8_t T5 = 12; +static const uint8_t T6 = 14; +static const uint8_t T7 = 27; + +static const uint8_t DAC1 = 25; +static const uint8_t DAC2 = 26; + +#endif /* Pins_Arduino_h */