初始化提交

This commit is contained in:
王立帮
2024-07-20 22:09:06 +08:00
commit c247dd07a6
6876 changed files with 2743096 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: BasicHTTPClient.
* date: 2021/8/4
******************************************************************************
*/
#include <M5Core2.h>
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
WiFiMulti wifiMulti;
HTTPClient http;
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
wifiMulti.addAP("WIFINAME", "WIFIPASSWORD"); //Storage wifi configuration information. 存储wifi配置信息
M5.Lcd.print("\nConnecting Wifi...\n"); //print format output string on lcd. 串口格式化输出字符串
}
void loop() {
M5.Lcd.setCursor(0,0); //Set the cursor at (0,0). 设置光标位于(0,0)处
if((wifiMulti.run() == WL_CONNECTED)) { // wait for WiFi connection. 等待连接至wifi
M5.Lcd.print("[HTTP] begin...\n");
http.begin("http://example.com/index.html"); // configure traged server and url. 配置被跟踪的服务器和URL
M5.Lcd.print("[HTTP] GET...\n");
int httpCode = http.GET(); // start connection and send HTTP header. 开始连接服务器并发送HTTP的标头
if(httpCode > 0) { // httpCode will be negative on error. 出错时httpCode将为负值
M5.Lcd.printf("[HTTP] GET... code: %d\n", httpCode);
if(httpCode == HTTP_CODE_OK) { // file found at server. 在服务器上找到文件
String payload = http.getString();
M5.Lcd.println(payload); //打印在服务器上读取的文件. Print files read on the server
}
}else {
M5.Lcd.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}else{
M5.Lcd.print("connect failed");
}
delay(5000);
M5.Lcd.clear(); //clear the screen. 清除屏幕
}

View File

@@ -0,0 +1,52 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: OTA Upload. 隔空传输程序
* date: 2021/7/30
*******************************************************************************
PC and M5Core2 can only be used on the same wifi. 电脑和M5Core2需在同一wifi下才可使用
When the OTA is ready, restart the Arduino client from Tools > Ports > Network ports to instantly transmit the program wirelessly.
OTA 准备好后重启Arduino客户端在工具->端口->网络端口,即刻无线传输程序
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <ArduinoOTA.h>
// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码
const char* ssid = "M5wifi";
const char* password = "21213123";
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态
M5.lcd.print("Waiting Wifi Connect");
while (WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功
delay(1000);
M5.lcd.print(".");
}
M5.lcd.println("\nWiFi Connected!");
M5.lcd.print("WiFi Connect To: ");
M5.lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称
M5.lcd.print("IP address: ");
M5.lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址
ArduinoOTA.setHostname("M5Core2"); //Set the network port name. 设置网络端口名称
ArduinoOTA.setPassword("666666"); //Set the network port connection password. 设置网络端口连接的密码
ArduinoOTA.begin(); //Initialize the OTA. 初始化OTA
M5.lcd.println("OTA ready!"); //M5.lcd port output format string. 串口输出格式化字符串
}
void loop() {
ArduinoOTA.handle(); //Continuously check for update requests. 持续检测是否有更新请求
M5.update();
if(M5.BtnA.isPressed()){ //if BtnA is Pressed. 如果按键A按下
ArduinoOTA.end(); //Ends the ArduinoOTA service. 结束OTA服务
M5.lcd.println("OTA End!");
delay(200);
}
}

View File

@@ -0,0 +1,94 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: WIFI AP. wifi热点
* date: 2021/7/29
*******************************************************************************
WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it.
创建一个WiFi接入点并在其上提供一个网络服务器
And can send requests to M5Core2 through the web page
并可通过网页向M5Core2发送请求
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
// Set these to your desired credentials. 设置你的热点名称和密码
const char *ssid = "M5Stack_Ap";
const char *password = "66666666";
WiFiServer server(80);
void setup() {
M5.begin(); //Init M5Core2. 初始化M5Core2
M5.lcd.setTextSize(2); //Set text size to 2. 设置字号大小为2
M5.lcd.println("WIFI ACCESS POINT"); // Screen print string. 屏幕打印字符串.
M5.lcd.printf("Please connect:%s \nThen access to:",ssid);
WiFi.softAP(ssid, password); // You can remove the password parameter if you want the AP to be open. 如果你想建立开放式热点,可以删除密码
IPAddress myIP = WiFi.softAPIP(); //Get the softAP interface IP address. 获取AP接口IP地址
M5.lcd.println(myIP);
server.begin(); //Start the established Internet of Things network server. 启动建立的物联网网络服务器
}
void loop() {
WiFiClient client = server.available(); // listen for incoming clients. 检查有没有设备通过网络向M5Stack网络服务器发送请求
if (client) { // if you get a client. 如果收到请求
M5.lcd.print("New Client:");
String currentLine = ""; // make a String to hold incoming data from the client. 创建一个String来保存来自客户端的传入数据
while (client.connected()) { // loop while the client's connected,continuously receiving data. 在客户端连接时进行循环,不断接收数据
if (client.available()) { // if there's bytes to read from the client. 如果有数据可读取
char c = client.read(); // store the read a byte. 存储读取到的数据
Serial.write(c);
if (c == '\n') { // if the byte is a newline character. 如果读取到的字节为换行符
// \n is the end of the client'S HTTP request, indicating that the client has sent a new request
// \n 是客户端HTTP请求的结尾说明客户端发来新请求:
if (currentLine.length() == 0) {
//Here are the instructions to create a page. 下面是创建一个页面的指令
//HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
//HTTP的开头总是以响应代码开始(例如 HTTP/1.1 200 OK)
//and a content-type so the client knows what's coming, then a blank line:
//然后是content-type这样客户端就知道接下来会发生什么然后是空行:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
// HTTP页面显示的内容跟在开头后面:
// /High and /Low are the data received when clicking the corresponding connection, which can be replaced.
// /High和/Low 为点击对应连接时接收到的数据,可更换
client.print("Click <a href=\"/High\">here</a> to turn ON the LED.<br>");
client.print("Click <a href=\"/Low\">here</a> to turn OFF the LED.<br>");
// The HTTP response ends with another blank line:
// HTTP响应以空行结束:
client.println();
// break out of the while loop:退出循环
break;
} else { // if you got a newline, then clear currentLine:如果得到新的一行,那么清除当前行
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character. 如果你得到了除了回车符以外的其他字符,
currentLine += c; // add it to the end of the currentLine. 将它添加到currentLine的末尾
}
// Check to see if the client request was "GET /H" or "GET /L":
// 检查客户端请求是“GET /High”还是“GET /Low”:
if (currentLine.endsWith("GET /High")) {
M5.Lcd.print("ON\n");
}else if (currentLine.endsWith("GET /Low")) {
M5.Lcd.print("OFF\n");
}
}
}
client.stop(); // close the connection. 关闭连接
}
}

View File

@@ -0,0 +1,48 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: WIFI Multi. 多wifi择优
* date: 2021/7/29
*******************************************************************************
* Connect to the best AP based on a given wifi list
* 根据给定wifi的列表连接到最好的AP
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
void setup()
{
M5.begin(); //Init M5Core2. 初始化 M5Core2
wifiMulti.addAP("wifi1", "12345566"); //Storage wifi configuration information 1. 存储wifi配置信息1
wifiMulti.addAP("wifi2", "12345566");
wifiMulti.addAP("aaa", "sadf");
M5.lcd.print("Connecting Wifi..."); //Serial port format output string. 串口格式化输出字符串
}
void loop()
{
if(wifiMulti.run() == WL_CONNECTED) { //If the connection to wifi is established successfully. 如果与wifi成功建立连接
M5.lcd.setCursor(0,20);
M5.lcd.print("WiFi connected\n\nSSID:");
M5.lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称
M5.lcd.print("RSSI: ");
M5.lcd.println(WiFi.RSSI()); //Output signal strength. 输出信号强度
M5.lcd.print("IP address: ");
M5.lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址
delay(1000);
M5.lcd.fillRect(0,20,180,300,BLACK); //It's equivalent to partial screen clearance. 相当于部分清屏
}else{
//If the connection to wifi is not established successfully. 如果没有与wifi成功建立连接
M5.lcd.print(".");
delay(1000);
}
}

View File

@@ -0,0 +1,49 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: Wifi scan. wifi扫描
* date: 2021/7/28
*******************************************************************************
*/
#include <M5Core2.h>
#include "WiFi.h"
void setup()
{
M5.begin(); //Init M5Stack. 初始化M5Stack
WiFi.mode(WIFI_STA);// Set WiFi to station mode and disconnect from an AP if it was previously connected. 将WiFi设置为站模式如果之前连接过AP则断开连接
WiFi.disconnect(); //Turn off all wifi connections. 关闭所有wifi连接
delay(100); //100 ms delay. 延迟100ms
M5.Lcd.print("WIFI SCAN"); //Screen print string. 屏幕打印字符串
}
void loop()
{
M5.Lcd.setCursor(0,0); //Set the cursor at (0,0). 将光标设置在(0,0)处
M5.Lcd.println("Please press Btn.A to (re)scan");
M5.update(); //Check the status of the key. 检测按键的状态
if(M5.BtnA.isPressed()){ //If button A is pressed. 如果按键A按下
M5.Lcd.clear(); //Clear the screen. 清空屏幕
M5.Lcd.println("scan start");
int n = WiFi.scanNetworks(); //return the number of networks found. 返回发现的网络数
if (n == 0){ //If no network is found. 如果没有找到网络
M5.Lcd.println("no networks found");
}else{ //If have network is found. 找到网络
M5.Lcd.printf("networks found:%d\n\n",n);
for (int i = 0; i < n; ++i)
{ // Print SSID and RSSI for each network found. 打印每个找到的网络的SSID和信号强度
M5.Lcd.printf("%d:",i + 1);
M5.Lcd.print(WiFi.SSID(i));
M5.Lcd.printf("(%d)",WiFi.RSSI(i));
M5.Lcd.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
delay(10);
}
}
delay(1000);
}
}

View File

@@ -0,0 +1,607 @@
/*
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 "WebServer.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 WebServer::_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 versionEnd = req.substring(addr_end + 8);
_currentVersion = atoi(versionEnd.c_str());
String searchStr = "";
int hasSearch = url.indexOf('?');
if (hasSearch != -1){
searchStr = urlDecode(url.substring(hasSearch + 1));
url = url.substring(0, hasSearch);
}
_currentUri = url;
_chunked = false;
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;
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("Content-Type")){
if (headerValue.startsWith("text/plain")){
isForm = false;
} else if (headerValue.startsWith("application/x-www-form-urlencoded")){
isForm = false;
isEncoded = true;
} else if (headerValue.startsWith("multipart/")){
boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
isForm = true;
}
} else if (headerName.equalsIgnoreCase("Content-Length")){
contentLength = headerValue.toInt();
} else if (headerName.equalsIgnoreCase("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;
}
if (contentLength > 0) {
if (searchStr != "") searchStr += '&';
if(isEncoded){
//url encoded form
String decoded = urlDecode(plainBuf);
size_t decodedLen = decoded.length();
memcpy(plainBuf, decoded.c_str(), decodedLen);
plainBuf[decodedLen] = 0;
searchStr += plainBuf;
}
_parseArguments(searchStr);
if(!isEncoded){
//plain post json or other data
RequestArgument& arg = _currentArgs[_currentArgCount++];
arg.key = "plain";
arg.value = String(plainBuf);
}
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Plain: ");
DEBUG_OUTPUT.println(plainBuf);
#endif
free(plainBuf);
}
}
if (isForm){
_parseArguments(searchStr);
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.equalsIgnoreCase("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 WebServer::_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;
}
void WebServer::_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;
_currentArgs = new RequestArgument[1];
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+1];
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 = 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 WebServer::_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 WebServer::_uploadReadByte(WiFiClient& client){
int res = client.read();
if(res == -1){
while(!client.available() && client.connected())
yield();
res = client.read();
}
return (uint8_t)res;
}
bool WebServer::_parseForm(WiFiClient& client, 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)){
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.length() > 19 && line.substring(0, 19).equalsIgnoreCase("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.length() > 12 && line.substring(0, 12).equalsIgnoreCase("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 WebServer::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 WebServer::_parseFormUploadAborted(){
_currentUpload.status = UPLOAD_FILE_ABORTED;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
_currentHandler->upload(*this, _currentUri, _currentUpload);
return false;
}

View File

@@ -0,0 +1,525 @@
/*
WebServer.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 "WebServer.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";
WebServer::WebServer(IPAddress addr, int port)
: _server(addr, port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
, _headerKeysCount(0)
, _currentHeaders(0)
, _contentLength(0)
, _chunked(false)
{
}
WebServer::WebServer(int port)
: _server(port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
, _headerKeysCount(0)
, _currentHeaders(0)
, _contentLength(0)
, _chunked(false)
{
}
WebServer::~WebServer() {
if (_currentHeaders)
delete[]_currentHeaders;
_headerKeysCount = 0;
RequestHandler* handler = _firstHandler;
while (handler) {
RequestHandler* next = handler->next();
delete handler;
handler = next;
}
close();
}
void WebServer::begin() {
_currentStatus = HC_NONE;
_server.begin();
if(!_headerKeysCount)
collectHeaders(0, 0);
}
bool WebServer::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 WebServer::requestAuthentication(){
sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
send(401);
}
void WebServer::on(const String &uri, WebServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler);
}
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler);
}
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
}
void WebServer::addHandler(RequestHandler* handler) {
_addRequestHandler(handler);
}
void WebServer::_addRequestHandler(RequestHandler* handler) {
if (!_lastHandler) {
_firstHandler = handler;
_lastHandler = handler;
}
else {
_lastHandler->next(handler);
_lastHandler = handler;
}
}
void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
}
void WebServer::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();
}
if (!_currentClient.connected()) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
}
// Wait for data from client to become available
if (_currentStatus == HC_WAIT_READ) {
if (!_currentClient.available()) {
if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
}
yield();
return;
}
if (!_parseRequest(_currentClient)) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
}
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
_contentLength = CONTENT_LENGTH_NOT_SET;
_handleRequest();
if (!_currentClient.connected()) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
return;
} else {
_currentStatus = HC_WAIT_CLOSE;
_statusChange = millis();
return;
}
}
if (_currentStatus == HC_WAIT_CLOSE) {
if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
_currentClient = WiFiClient();
_currentStatus = HC_NONE;
} else {
yield();
return;
}
}
}
void WebServer::close() {
_server.end();
}
void WebServer::stop() {
close();
}
void 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 WebServer::setContentLength(size_t contentLength) {
_contentLength = contentLength;
}
void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
response = "HTTP/1."+String(_currentVersion)+" ";
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));
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
//let's do chunked
_chunked = true;
sendHeader("Accept-Ranges","none");
sendHeader("Transfer-Encoding","chunked");
}
sendHeader("Connection", "close");
response += _responseHeaders;
response += "\r\n";
_responseHeaders = String();
}
void WebServer::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());
_currentClient.write(header.c_str(), header.length());
if(content.length())
sendContent(content);
}
void WebServer::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);
_currentClient.write(header.c_str(), header.length());
sendContent_P(content);
}
void WebServer::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 WebServer::send(int code, char* content_type, const String& content) {
send(code, (const char*)content_type, content);
}
void WebServer::send(int code, const String& content_type, const String& content) {
send(code, (const char*)content_type.c_str(), content);
}
void WebServer::sendContent(const String& content) {
const char * footer = "\r\n";
size_t len = content.length();
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", len, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClient.write(content.c_str(), len);
if(_chunked){
_currentClient.write(footer, 2);
}
}
void WebServer::sendContent_P(PGM_P content) {
sendContent_P(content, strlen_P(content));
}
void WebServer::sendContent_P(PGM_P content, size_t size) {
const char * footer = "\r\n";
if(_chunked) {
char * chunkSize = (char *)malloc(11);
if(chunkSize){
sprintf(chunkSize, "%x%s", size, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClient.write(content, size);
if(_chunked){
_currentClient.write(footer, 2);
}
}
String WebServer::arg(String name) {
for (int i = 0; i < _currentArgCount; ++i) {
if ( _currentArgs[i].key == name )
return _currentArgs[i].value;
}
return String();
}
String WebServer::arg(int i) {
if (i < _currentArgCount)
return _currentArgs[i].value;
return String();
}
String WebServer::argName(int i) {
if (i < _currentArgCount)
return _currentArgs[i].key;
return String();
}
int WebServer::args() {
return _currentArgCount;
}
bool WebServer::hasArg(String name) {
for (int i = 0; i < _currentArgCount; ++i) {
if (_currentArgs[i].key == name)
return true;
}
return false;
}
String WebServer::header(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if (_currentHeaders[i].key.equalsIgnoreCase(name))
return _currentHeaders[i].value;
}
return String();
}
void WebServer::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 WebServer::header(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].value;
return String();
}
String WebServer::headerName(int i) {
if (i < _headerKeysCount)
return _currentHeaders[i].key;
return String();
}
int WebServer::headers() {
return _headerKeysCount;
}
bool WebServer::hasHeader(String name) {
for (int i = 0; i < _headerKeysCount; ++i) {
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
return true;
}
return false;
}
String WebServer::hostHeader() {
return _hostHeader;
}
void WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}
void WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn;
}
void WebServer::_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 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 "";
}
}

View File

@@ -0,0 +1,186 @@
/*
WebServer.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 WEBSERVER_H
#define WEBSERVER_H
#include <functional>
#include <WiFi.h>
#include <Update.h>
#include <WiFiUdp.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 };
#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_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 WebServer;
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"
namespace fs {
class FS;
}
class WebServer
{
public:
WebServer(IPAddress addr, int port = 80);
WebServer(int port = 80);
~WebServer();
void begin();
void handleClient();
void close();
void stop();
bool authenticate(const char * username, const char * password);
void requestAuthentication();
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
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);
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){
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);
}
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;
uint8_t _currentVersion;
HTTPClientStatus _currentStatus;
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;
bool _chunked;
};
#endif //WebServer_H

View File

@@ -0,0 +1,215 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: WiFi connect. wifi连接
* date: 2021/7/27
*******************************************************************************
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiClient.h>
#include "WebServer.h"
#include <Preferences.h>
boolean restoreConfig();
boolean checkConnection();
void startWebServer();
void setupMode();
String makePage(String title, String contents);
String urlDecode(String input);
const IPAddress apIP(192, 168, 4, 1); //Define the address of the wireless AP. 定义无线AP的地址
const char* apSSID = "M5STACK_SETUP"; //Define the name of the created hotspot. 定义创建热点的名称
boolean settingMode;
String ssidList;
String wifi_ssid; //Store the name of the wireless network. 存储无线网络的名称
String wifi_password; //Store the password of the wireless network. 存储无线网络的密码
// DNSServer dnsServer;. webServer的类
WebServer webServer(80);
// wifi config store. wifi配置存储的类
Preferences preferences;
void setup() {
M5.begin(); //Init M5Core2. 初始化M5Core2
M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色
preferences.begin("wifi-config");
delay(10);
if (restoreConfig()) { //Check if wifi configuration information has been stored. 检测是否已存储wifi配置信息
if (checkConnection()) { //Check wifi connection. 检测wifi连接情况
settingMode = false; //Turn off setting mode. 关闭设置模式
startWebServer(); //Turn on network service. 开启网络服务
return; //Exit setup(). 退出setup()
}
}
settingMode = true; //If there is no stored wifi configuration information, turn on the setting mode. 若没有已存储的wifi配置信息,则开启设置模式
setupMode();
}
void loop() {
if (settingMode) {
}
webServer.handleClient(); //Check that there is no facility to send requests to the M5StickC Web server over the network. 检查有没有设备通过网络向M5Core2网络服务器发送请求
}
boolean restoreConfig() { /* Check whether there is wifi configuration information storage, if there is 1 return, if no return 0.
检测是否有wifi配置信息存储,若有返回1,无返回0 */
wifi_ssid = preferences.getString("WIFI_SSID");
wifi_password = preferences.getString("WIFI_PASSWD");
M5.Lcd.printf("WIFI-SSID: %s\n",wifi_ssid); //Screen print format string. 屏幕打印格式化字符串
M5.Lcd.printf("WIFI-PASSWD: %s\n",wifi_password);
WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());
if(wifi_ssid.length() > 0) {
return true;
} else {
return false;
}
}
boolean checkConnection() { //Check wifi connection. 检测wifi连接情况
int count = 0; //count. 计数
M5.Lcd.print("Waiting for Wi-Fi connection");
while ( count < 30 ) { /* If you fail to connect to wifi within 30*350ms (10.5s), return false; otherwise return true.
若在30*500ms(15s)内未能连上wifi,返回false;否则返回true */
if (WiFi.status() == WL_CONNECTED) {
M5.Lcd.printf("\nConnected!\n");
return (true);
}
delay(350);
M5.Lcd.print(".");
count++;
}
M5.Lcd.println("Timed out.");
return false;
}
void startWebServer() { //Open the web service. 打开Web服务
if (settingMode) { //If the setting mode is on. 如果设置模式处于开启状态
M5.Lcd.print("Starting Web Server at: ");
M5.Lcd.print(WiFi.softAPIP()); //Output AP address (you can change the address you want through apIP at the beginning). 输出AP地址(可通过开头的apIP更改自己想要的地址)
webServer.on("/settings", []() { //AP web interface settings. AP网页界面设置
String s = "<h1>Wi-Fi Settings</h1><p>Please enter your password by selecting the SSID.</p>";
s += "<form method=\"get\" action=\"setap\"><label>SSID: </label><select name=\"ssid\">";
s += ssidList;
s += "</select><br>Password: <input name=\"pass\" length=64 type=\"password\"><input type=\"submit\"></form>";
webServer.send(200, "text/html", makePage("Wi-Fi Settings", s));
});
webServer.on("/setap", []() {
String ssid = urlDecode(webServer.arg("ssid"));
M5.Lcd.printf("SSID: %s\n",ssid);
String pass = urlDecode(webServer.arg("pass"));
M5.Lcd.printf("Password: %s\n\nWriting SSID to EEPROM...\n",pass);
// Store wifi config. 存储wifi配置信息
M5.Lcd.println("Writing Password to nvr...");
preferences.putString("WIFI_SSID", ssid);
preferences.putString("WIFI_PASSWD", pass);
M5.Lcd.println("Write nvr done!");
String s = "<h1>Setup complete.</h1><p>device will be connected to \"";
s += ssid;
s += "\" after the restart.";
webServer.send(200, "text/html", makePage("Wi-Fi Settings", s));
delay(2000);
ESP.restart(); //Restart MPU. 重启MPU
});
webServer.onNotFound([]() {
String s = "<h1>AP mode</h1><p><a href=\"/settings\">Wi-Fi Settings</a></p>";
webServer.send(200, "text/html", makePage("AP mode", s));
});
}
else { //If the setting mode is off. 如果设置模式处于关闭状态
M5.Lcd.print("Starting Web Server at ");
M5.Lcd.println(WiFi.localIP());
webServer.on("/", []() { //AP web interface settings. AP网页界面设置
String s = "<h1>STA mode</h1><p><a href=\"/reset\">Reset Wi-Fi Settings</a></p>";
webServer.send(200, "text/html", makePage("STA mode", s));
});
webServer.on("/reset", []() {
// reset the wifi config
preferences.remove("WIFI_SSID");
preferences.remove("WIFI_PASSWD");
String s = "<h1>Wi-Fi settings was reset.</h1><p>Please reset device.</p>";
webServer.send(200, "text/html", makePage("Reset Wi-Fi Settings", s));
delay(2000);
ESP.restart();
});
}
webServer.begin(); //Start web service. 开启web服务
}
void setupMode() {
WiFi.mode(WIFI_MODE_STA); //Set Wi-Fi mode to WIFI_MODE_STA. 设置Wi-Fi模式为WIFI_MODE_STA
WiFi.disconnect(); //Disconnect wifi connection. 断开wifi连接
delay(100);
int n = WiFi.scanNetworks(); //Store the number of wifi scanned into n. 将扫描到的wifi个数存储到n中
delay(100);
M5.Lcd.println("");
for (int i = 0; i < n; ++i) { //Save each wifi name scanned to ssidList. 将扫描到的每个wifi名称保存到ssidList中
ssidList += "<option value=\"";
ssidList += WiFi.SSID(i);
ssidList += "\">";
ssidList += WiFi.SSID(i);
ssidList += "</option>";
}
delay(100);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP(apSSID); //Turn on Ap mode. 开启Ap模式
WiFi.mode(WIFI_MODE_AP); //Set WiFi to soft-AP mode. 设置WiFi为soft-AP模式
startWebServer(); //Open the web service. 打开Web服务
M5.Lcd.printf("\nStarting Access Point at \"%s\"\n\n",apSSID);
}
String makePage(String title, String contents) {
String s = "<!DOCTYPE html><html><head>";
s += "<meta name=\"viewport\" content=\"width=device-width,user-scalable=0\">";
s += "<title>";
s += title;
s += "</title></head><body>";
s += contents;
s += "</body></html>";
return s;
}
String urlDecode(String input) {
String s = input;
s.replace("%20", " ");
s.replace("+", " ");
s.replace("%21", "!");
s.replace("%22", "\"");
s.replace("%23", "#");
s.replace("%24", "$");
s.replace("%25", "%");
s.replace("%26", "&");
s.replace("%27", "\'");
s.replace("%28", "(");
s.replace("%29", ")");
s.replace("%30", "*");
s.replace("%31", "+");
s.replace("%2C", ",");
s.replace("%2E", ".");
s.replace("%2F", "/");
s.replace("%2C", ",");
s.replace("%3A", ":");
s.replace("%3A", ";");
s.replace("%3C", "<");
s.replace("%3D", "=");
s.replace("%3E", ">");
s.replace("%3F", "?");
s.replace("%40", "@");
s.replace("%5B", "[");
s.replace("%5C", "\\");
s.replace("%5D", "]");
s.replace("%5E", "^");
s.replace("%5F", "-");
s.replace("%60", "`");
return s;
}

View File

@@ -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(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
virtual void upload(WebServer& 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

View File

@@ -0,0 +1,153 @@
#ifndef REQUESTHANDLERSIMPL_H
#define REQUESTHANDLERSIMPL_H
#include "RequestHandler.h"
class FunctionRequestHandler : public RequestHandler {
public:
FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &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(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
(void) server;
if (!canHandle(requestMethod, requestUri))
return false;
_fn();
return true;
}
void upload(WebServer& server, String requestUri, HTTPUpload& upload) override {
(void) server;
(void) upload;
if (canUpload(requestUri))
_ufn();
}
protected:
WebServer::THandlerFunction _fn;
WebServer::THandlerFunction _ufn;
String _uri;
HTTPMethod _method;
};
class StaticRequestHandler : public RequestHandler {
public:
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
: _fs(fs)
, _uri(uri)
, _path(path)
, _cache_header(cache_header)
{
_isFile = fs.exists(path);
// DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
_baseUriLength = _uri.length();
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != HTTP_GET)
return false;
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
return false;
return true;
}
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
// DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
String path(_path);
if (!_isFile) {
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (requestUri.endsWith("/")) requestUri += "index.htm";
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
}
// DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
String contentType = getContentType(path);
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(".gz") && !_fs.exists(path)) {
String pathWithGz = path + ".gz";
if(_fs.exists(pathWithGz))
path += ".gz";
}
File f = _fs.open(path, "r");
if (!f)
return false;
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
server.streamFile(f, contentType);
return true;
}
static String getContentType(const String& path) {
if (path.endsWith(".html")) return "text/html";
else if (path.endsWith(".htm")) return "text/html";
else if (path.endsWith(".css")) return "text/css";
else if (path.endsWith(".txt")) return "text/plain";
else if (path.endsWith(".js")) return "application/javascript";
else if (path.endsWith(".png")) return "image/png";
else if (path.endsWith(".gif")) return "image/gif";
else if (path.endsWith(".jpg")) return "image/jpeg";
else if (path.endsWith(".ico")) return "image/x-icon";
else if (path.endsWith(".svg")) return "image/svg+xml";
else if (path.endsWith(".ttf")) return "application/x-font-ttf";
else if (path.endsWith(".otf")) return "application/x-font-opentype";
else if (path.endsWith(".woff")) return "application/font-woff";
else if (path.endsWith(".woff2")) return "application/font-woff2";
else if (path.endsWith(".eot")) return "application/vnd.ms-fontobject";
else if (path.endsWith(".sfnt")) return "application/font-sfnt";
else if (path.endsWith(".xml")) return "text/xml";
else if (path.endsWith(".pdf")) return "application/pdf";
else if (path.endsWith(".zip")) return "application/zip";
else if(path.endsWith(".gz")) return "application/x-gzip";
else if (path.endsWith(".appcache")) return "text/cache-manifest";
return "application/octet-stream";
}
protected:
FS _fs;
String _uri;
String _path;
String _cache_header;
bool _isFile;
size_t _baseUriLength;
};
#endif //REQUESTHANDLERSIMPL_H

View File

@@ -0,0 +1,48 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: WIFI Smart Config. WIFI智能配网
* date: 2021/7/30
*******************************************************************************
* Fill in WIFI configuration information through mobile APP to connect M5Core2 to relevant WIFI
* 通过手机APP填写WIFI配置信息使 M5Core2连接至相关WIFI
* APP Download Address: https://www.espressif.com/en/products/software/esp-touch/resources
* APP下载地址: https://www.espressif.com/zh-hans/products/software/esp-touch/resources
*/
#include <M5Core2.h>
#include "WiFi.h"
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
WiFi.mode(WIFI_AP_STA); // Set the wifi mode to the mode compatible with the AP and Station, and start intelligent network configuration
WiFi.beginSmartConfig(); // 设置wifi模式为AP 与 Station 兼容模式,并开始智能配网
//Wait for the M5Core2 to receive network information from the phone
//等待M5Core2接收到来自手机的配网信息
M5.Lcd.println("\nWaiting for Phone SmartConfig."); //Screen print format string. 屏幕打印格式化字符串
while (!WiFi.smartConfigDone()) { //If the smart network is not completed. 若智能配网没有完成
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.println("\nSmartConfig received.");
M5.Lcd.println("Waiting for WiFi");
while (WiFi.status() != WL_CONNECTED) { //M5Core2 will connect automatically upon receipt of the configuration information, and return true if the connection is successful. 收到配网信息后M5Core2将自动连接若连接成功将返回true
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.print("\nWiFi Connect To: ");
M5.Lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称
M5.Lcd.print("IP address: ");
M5.Lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址
M5.Lcd.print("RSSI: ");
M5.Lcd.println(WiFi.RSSI()); //Output signal strength. 输出信号强度
}
void loop() {
}

View File

@@ -0,0 +1,79 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: WIFI TCP.
* date: 2021/7/29
*******************************************************************************
M5Core2 will sends a message to a TCP server
M5Core2 将向TCP服务器发送一条数据
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <WiFiMulti.h>
// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码
const char *ssid = "cam";
const char *password = "12345678";
WiFiMulti WiFiMulti;
void setup()
{
int sum=0;
M5.begin(); //Init M5Core2. 初始化M5Core2
WiFiMulti.addAP(ssid, password); //Add wifi configuration information. 添加wifi配置信息
M5.lcd.print("\nWaiting connect to WiFi..."); //Serial port output format string. 串口输出格式化字符串
while(WiFiMulti.run() != WL_CONNECTED) { //If the connection to wifi is not established successfully. 如果没有与wifi成功建立连接
M5.lcd.print(".");
delay(1000);
sum+=1;
if(sum==8) M5.lcd.print("Conncet failed!");
}
M5.lcd.println("\nWiFi connected");
M5.lcd.print("IP address: ");
M5.lcd.println(WiFi.localIP()); //The serial port outputs the IP address of the M5Core2. 串口输出M5Core2的IP地址
delay(500);
}
void loop()
{
M5.lcd.setCursor(0,40);
const char * host = "www.baidu.com"; //Set the IP address or DNS of the TCP server. 设置TCP服务器的ip或dns
const uint16_t port = 80; //The port of the TCP server is specified. 设置TCP服务器的端口
M5.lcd.printf("Connecting to: %s\n",host);
WiFiClient client;
if (!client.connect(host, port)) { //Connect to the server. 0 is returned if the connection fails. 连接服务器,若连接失败返回0
M5.lcd.print("Connection failed.\nWaiting 5 seconds before retrying...\n");
delay(5000);
return;
}
//send an arbitrary string to the server. 发送一个字符串到上边连接的服务器
client.print("Send this data to the server");
//send a basic document request to the server. 向服务器发送一个基本的文档请求.
client.print("GET /index.html HTTP/1.1\n\n");
int maxloops = 0;
//wait for the server's reply to become available
//等待服务器的回复
while (!client.available() && maxloops < 1000){
maxloops++;
delay(1); //delay 1 msec
}
if (client.available() > 0){ //Detects whether data is received. 检测是否接收到数据
String line = client.readStringUntil('\r'); //Read information from data received by the device until \r is read. 从设备接收到的数据中读取信息,直至读取到\r时
M5.lcd.println(line); //String received by serial port output. 串口输出接收到的字符串
}else{
M5.lcd.println("client.available() timed out ");
}
M5.lcd.println("Closing connection.");
client.stop();
M5.lcd.println("Waiting 5 seconds before restarting...");
delay(5000);
M5.lcd.fillRect(0,40,320,220,BLACK);
}

View File

@@ -0,0 +1,79 @@
/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
* Equipped with M5Core2 sample source code
* 配套 M5Core2 示例源代码
* Visit the website for more information: https://docs.m5stack.com/en/core/core2
* 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/core2
*
* describe: announcing & finding services. 广播&查找服务
* date: 2021/8/3
*******************************************************************************
*/
#include <M5Core2.h>
#include <WiFi.h>
#include <ESPmDNS.h>
// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码
const char* ssid = "M5";
const char* password = "12344123";
void browseService(const char * service, const char * proto){ //find devices. 查找设备
M5.Lcd.printf("Browsing for _%s._%s.local ", service, proto);
int n = MDNS.queryService(service, proto); //Store the number of devices found in n. 将找到的设备数存储在n中
if (n == 0) { //if don't have any devices. 如果没有任何设备
M5.Lcd.println("no services found");
} else {
M5.Lcd.printf("%d service(s) found\n",n);
for (int i = 0; i < n; ++i) { // Print details for each service found. 打印每个找到的设备
M5.Lcd.printf(" %d: ",i + 1);
M5.Lcd.print(MDNS.hostname(i)); //output the devies name. 输出设备名称
M5.Lcd.print(" ");
M5.Lcd.print(MDNS.IP(i)); //Output the devices IP Address. 输出设备的IP地址
M5.Lcd.printf(":%d\n",MDNS.port(i)); //output the devices port. 输出设备的端口号
}
}
M5.Lcd.println();
}
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态
M5.Lcd.print("Connected to ");
M5.Lcd.print(ssid); //Output Network name. 输出网络名称
while (WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功
delay(500); //delay 0.5s. 延迟0.5s
M5.Lcd.print(".");
}
M5.Lcd.print("Success!\nIP address: ");
M5.Lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址
if (!MDNS.begin("ESP32_Browser")) { //if init error. 如果初始化错误
M5.Lcd.println("Error setting up MDNS responder!");
while(1){
delay(1000);
}
}
}
void loop() {
browseService("http", "tcp");
delay(1000);
browseService("arduino", "tcp");
delay(1000);
browseService("workstation", "tcp");
delay(3000);
M5.Lcd.clear();
M5.Lcd.setCursor(0,0);
browseService("smb", "tcp");
delay(1000);
browseService("afpovertcp", "tcp");
delay(1000);
browseService("ftp", "tcp");
delay(1000);
browseService("ipp", "tcp");
delay(1000);
browseService("printer", "tcp");
delay(10000);
}