初始化提交

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,44 @@
/*
*******************************************************************************
* 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: Display_Unicode. Unicode显示
* date: 2021/7/23
*******************************************************************************
*/
/*---PLEASE SEE THE README----
------请在使用前看README文件----*/
#include <M5Core2.h>
#include "CUF_24px.h"
void setup()
{
M5.begin(); //Init M5Stack. 初始化M5Core2
M5.Lcd.setFreeFont(&unicode_24px); //Set the GFX font to use. 设置要使用的GFX字体
M5.Lcd.setTextDatum(TC_DATUM); //Set text alignment to center-up alignment. 设置文本对齐方式为居中向上对齐
}
void loop()
{
M5.Lcd.fillScreen(0);
M5.Lcd.drawString("Hello world", 160, 60, 1); //Hello world is displayed in font 1 on (1600.60).
M5.Lcd.drawString("你好 世界", 160, 90, 1); //在(160,60)处以字体1显示hello world
M5.Lcd.drawString("Здравствуй мир", 160, 120, 1);
M5.Lcd.drawString("こんにちは せかい", 160, 150, 1);
delay(3000);
M5.Lcd.fillScreen(0); //Fill the screen with black (used to clear the screen) is equivalent to clear (). 使屏幕充满黑色(用来清屏)等同于clear()
M5.Lcd.setCursor(0, 30); // Set the text cursor 0,30 position. 设置文本光标在(0,30)处
M5.Lcd.printf("☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟A⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯B⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿C⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏D⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟E⛠⛡⛢");
delay(3000);
M5.Lcd.fillScreen(0);
M5.Lcd.setCursor(0, 30);
M5.Lcd.printf("⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯F⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❪❫❬❭❰❱❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➗➘➙➚➛➜➝➞➟A➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯B➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿");
delay(3000);
}

View File

@@ -0,0 +1,52 @@
Please modify <m5stack path>/src/utility/In_eSPI_Setup.h, At the end of the file add "#define USE_M5_FONT_CREATOR"
if use PlatformIO, need add "board_build.partitions = no_ota.csv" in platformio.ini
if want get customize unicode, try use https://github.com/m5stack/FontCreator, create font file
if add "#define USE_M5_FONT_CREATOR" and want to use standard gfx font, need modify font file
#ifdef USE_M5_FONT_CREATOR
0, 0,
#endif
like:
``` cpp
const GFXfont FreeMono12pt7b PROGMEM = {
(uint8_t *)FreeMono12pt7bBitmaps,
(GFXglyph *)FreeMono12pt7bGlyphs,
0x20, 0x7E, 24,
#ifdef USE_M5_FONT_CREATOR
0, 0,
#endif
}
```
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
zh:
如果编译过程中出现错误,请在<m5stack path>/src/utility/In_eSPI_Setup.h 中最后一行添加#define USE_M5_FONT_CREATOR
如果你使用platformIO,需要在platformio.ini中添加board_build.partitions = no_ota.csv
如果你想自定义unicode,请尝试使用https://github.com/m5stack/FontCreator 去创建相关字体文件
如果添加了“#define USE_M5_FONT_CREATOR”并且想使用标准的gfx字体需要修改CUF_24px.h文件
```cpp
#ifdef USE_M5_FONT_CREATOR
#define _unicode_24px_H_
const GFXfont FreeMono12pt7b PROGMEM = {
(uint8_t *)FreeMono12pt7bBitmaps,
(GFXglyph *)FreeMono12pt7bGlyphs,
0x20, 0x7E, 24,
};
#endif
```

View File

@@ -0,0 +1,45 @@
/*
*******************************************************************************
* 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: Character library. 字库
* date: 2021/7/28
*******************************************************************************
-----PLEASE SEE THE README----
------请在使用前看README文件----*/
#include <M5Core2.h>
#include "display_ch.h"
#include "str.h"
DisplayCh displaych;
void setup() {
M5.begin(); // Init M5Stack. 初始化M5Stack
displaych.loadHzk16(); //Load the Chinese character library (be sure to load before using the Chinese character library). 加载汉字库(务必在使用汉字库前加载)
displaych.setTextColor(WHITE, BLACK); //Set the text color to white and the text background color to black (mandatory). 设置文字颜色为白色,文字背景颜色为黑色(必加)
// Set text with red highlight color
displaych.setHighlightColor(RED); //Set the text highlight color to red. 设置文字高亮颜色为红色
displaych.setTextSize(1); //Set text size to 1. 设置字号大小为1 Set text size to 1(必加)
}
void loop() {
displaych.setCursor(0,0,1); //Set the cursor at (0,0) and the size to 1(mandatory). 将光标设置在(0,0)处,并设置字号为1(必加)
displaych.writeHzk(AscStr); //Display the contents of AscStr here (which can be changed in str.h). 在此处显示AscStr中的内容(可在str.h中更改)
delay(1000); //delay 1000ms. 延迟1000ms
displaych.setCursor(0,45);
displaych.writeHzk(GbkStr); //Display the contents of GbkStr here (which can be changed in str.h). 在此处显示GbkStr中的内容(可在str.h中更改)
delay(1000);
//Highlight the text. 高亮显示文本
displaych.highlight(true); //Turn on highlight. 开启高亮显示
displaych.setCursor(0,65);
displaych.writeHzk(GbkStr);
delay(1000);
displaych.fillScreen(BLACK); //Fill the screen with black color, equivalent to empty the screen. 填充屏幕颜色为黑色,等效于清空屏幕
displaych.highlight(false); //Turn off highlight. 关闭高亮显示
delay(500);
}

View File

@@ -0,0 +1,7 @@
HZK16 is the GB2312 Chinese encoding format font, so it is normal that GbkStr is displayed as garbled in the src.h file
To display Chinese normally, notepad++ or other software (UTF-8 for Arduino IDE) should be used to open str.h with encoding GB2312
Modify the content in GbkStr to replace it with the character you want to display
HZK16为GB2312中文编码格式字库, 故在 src.h 文件中 GbkStr 显示为乱码为正常现象
若要正常显示中文应使用notepad++或其它软件Arduino IDE 为UTF-8使用编码GB2312打开str.h
修改GbkStr里的内容即可更换为想要显示的字符

View File

@@ -0,0 +1,611 @@
#include "display_ch.h"
//#define _ASC16_
//#define _HZK16_
DisplayCh::DisplayCh(){
// Set default highlight color
highlightcolor = RED;
highlighted = false;
// HZK16 is not used by default
hzk16Used = false;
hzk16Type = DontUsedHzk16;
//pHzk16File = nullptr;
//pAsc16File = nullptr;
textwrap = true;
_width = 320;
_height = 240;
}
/***************************************************************************************
** Function name: setTextWrap
** Description: Define if text should wrap at end of line
***************************************************************************************/
void DisplayCh::setTextWrap(boolean w) { textwrap = w; }
/***************************************************************************************
** Function name: setTextSize
** Description: Set the text size multiplier
***************************************************************************************/
void DisplayCh::setTextSize(uint8_t s) {
if (s > 7)
s = 7; // Limit the maximum size multiplier so byte variables can be used
// for rendering
textsize = (s > 0) ? s : 1; // Don't allow font size 0
// Calculate the text width and height.
// It's 8 pixels wide and 16 pixels high in for ASCII characters,
// and 16 pixels both wide and high for GBK characters in HZK16 mode.
ascCharWidth = 8 * textsize;
ascCharHeigth = 16 * textsize;
gbkCharWidth = ascCharHeigth;
gbkCharHeight = gbkCharWidth;
}
/***************************************************************************************
** Function name: setCursor
** Description: Set the text cursor x,y position
***************************************************************************************/
void DisplayCh::setCursor(int16_t x, int16_t y) {
cursor_x = x;
cursor_y = y;
}
/***************************************************************************************
** Function name: setCursor
** Description: Set the text cursor x,y position and font
***************************************************************************************/
void DisplayCh::setCursor(int16_t x, int16_t y, uint8_t font) {
textfont = font;
cursor_x = x;
cursor_y = y;
}
/***************************************************************************************
** Function name: setTextColor
** Description: Set the font foreground colour (background is
*transparent)
***************************************************************************************/
void DisplayCh::setTextColor(uint16_t c) {
// For 'transparent' background, we'll set the bg
// to the same as fg instead of using a flag
textcolor = textbgcolor = c;
}
/***************************************************************************************
** Function name: fillScreen
** Description: Clear the screen to defined colour
***************************************************************************************/
void DisplayCh::fillScreen(uint32_t color) {
M5.Lcd.fillRect(0, 0, _width, _height, color);
}
/***************************************************************************************
** Function name: setTextColor
** Description: Set the font foreground and background colour
***************************************************************************************/
void DisplayCh::setTextColor(uint16_t c, uint16_t b) {
textcolor = c;
textbgcolor = b;
}
/***************************************************************************************
** Function name: loadHzk16
** Description: loadHzk16 fonts
** Params:
** HZK16Path: HZK16 file path on TF card. e.g. /HZK16, means file on the
*root dir of TF card. * ASC16Path: ASC16 file path on TF card.
***************************************************************************************/
void DisplayCh::loadHzk16(Hzk16Types hzkTypes,const char *HZK16Path, const char *ASC16Path) {
if (hzk16Used)
return;
//Hzk16Types hzkTypes = InternalHzk16
if(hzkTypes == InternalHzk16){
//#if defined(_ASC16_) && defined(_HZK16_)
pAscCharMatrix = (uint8_t *)&ASC16[0];
pGbkCharMatrix = (uint8_t *)&HZK16[0];
Serial.println("HZK16 path: Internal");
Serial.println("ASC16 path: Internal");
hzk16Used = initHzk16(true, nullptr, nullptr);
}
else
{
pAscCharMatrix = NULL;
pGbkCharMatrix = NULL;
Serial.print("HZK16 path: /SD");
Serial.println(HZK16Path);
Serial.print("ASC16 path: /SD");
Serial.println(ASC16Path);
hzk16Used = initHzk16(true, HZK16Path, ASC16Path);
}
Serial.print("HZK16 init result: ");
Serial.println(isHzk16Used());
}
/***************************************************************************************
** Function name: disableHzk16
** Description: disable Hzk16 fonts
** Params:
***************************************************************************************/
void DisplayCh::disableHzk16() {
if (hzk16Used) {
hzk16Used = initHzk16(false);
}
}
bool DisplayCh::initHzk16(boolean use, const char *HZK16Path,
const char *ASC16Path) {
bool result = false;
if (use == false) { // Do not use HZK16 and ASC16 fonts
hzk16Type = DontUsedHzk16;
Serial.println("Use default font.");
} else if (pAscCharMatrix == NULL ||
pGbkCharMatrix ==
NULL) { // Use external HZK16 and ASC16 font on TF card.
// Check if HZK16 and ASC16 files exist on TF card.
if (SD.exists(HZK16Path) && SD.exists(ASC16Path)) { // Exists
hzk16Type = ExternalHzk16;
Serial.println("Use external HZK16 and ASC16 font.");
} else { // Not exists
hzk16Type = DontUsedHzk16;
Serial.println("External font file HZK16/ASC16 lost, use default font.");
}
} else { // Use internal HZK16 and ASC16 fonts
hzk16Type = InternalHzk16;
Serial.println("Use internal HZK16 and ASC16 font.");
}
switch (hzk16Type) {
case ExternalHzk16: {
if (pHzk16File == NULL) {
Hzk16File = SD.open(HZK16Path);
pHzk16File = &Hzk16File;
}
if (pAsc16File == NULL) {
Asc16File = SD.open(ASC16Path);
pAsc16File = &Asc16File;
}
hzkBufCount = 0;
break;
}
case InternalHzk16: {
if (pAscCharMatrix == NULL || pGbkCharMatrix == NULL) {
hzk16Type = DontUsedHzk16;
}
if (pHzk16File != NULL) {
pHzk16File->close();
pHzk16File = NULL;
}
if (pAsc16File != NULL) {
pAsc16File->close();
pAsc16File = NULL;
}
hzkBufCount = 0;
break;
}
case DontUsedHzk16: {
if (pHzk16File != NULL) {
pHzk16File->close();
pHzk16File = NULL;
}
if (pAsc16File != NULL) {
pAsc16File->close();
pAsc16File = NULL;
}
break;
}
}
return hzk16Type != DontUsedHzk16;
}
void DisplayCh::writeHzkAsc(const char c) {
if (c == '\n') {
cursor_x = 0;
cursor_y += ascCharHeigth;
} else if (c != '\r') {
uint32_t offset;
uint8_t mask;
uint16_t posX = cursor_x, posY = cursor_y;
uint8_t charMatrix[16];
uint8_t *pCharMatrix;
offset = (uint32_t)c * 16;
if (hzk16Type == ExternalHzk16) {
pAsc16File->seek(offset, SeekSet);
pAsc16File->readBytes((char *)&charMatrix[0], 16);
pCharMatrix = &charMatrix[0];
} else {
if (pAscCharMatrix == NULL) {
return;
}
pCharMatrix = pAscCharMatrix + offset;
}
// startWrite();
if (highlighted) {
M5.Lcd.fillRect(cursor_x, cursor_y, ascCharWidth, ascCharHeigth, highlightcolor);
} else if (istransparent == false) {
M5.Lcd.fillRect(cursor_x, cursor_y, ascCharWidth, ascCharHeigth, textbgcolor);
}
for (uint8_t row = 0; row < 16; row++) {
mask = 0x80;
posX = cursor_x;
for (uint8_t col = 0; col < 8; col++) {
if ((*pCharMatrix & mask) != 0) {
if (textsize == 1) {
M5.Lcd.drawPixel(posX, posY, textcolor);
} else {
M5.Lcd.fillRect(posX, posY, textsize, textsize, textcolor);
}
}
posX += textsize;
mask >>= 1;
}
posY += textsize;
pCharMatrix++;
}
// endWrite();
cursor_x += ascCharWidth;
if (textwrap && ((cursor_x + ascCharWidth) > _width)) {
cursor_x = 0;
cursor_y += ascCharHeigth;
}
}
}
void DisplayCh::writeHzk(char* c){
char *ret = c;
if(ret == NULL) return;
while(*ret != '\0'){
while(*ret <= 0xA0){
if(*ret == '\0') return;
writeHzkAsc(*ret);
ret++;
}
writeHzkGbk(ret);
ret++;
ret++;
}
}
void DisplayCh::writeHzkGbk(const char *c) {
uint32_t offset;
uint8_t mask;
uint16_t posX = cursor_x, posY = cursor_y;
uint8_t charMatrix[32];
uint8_t *pCharMatrix;
offset = (uint32_t)(94 * (uint32_t)(c[0] - 0xA1) + (uint32_t)(c[1] - 0xA1)) * 32;
if (hzk16Type == ExternalHzk16) {
pHzk16File->seek(offset, SeekSet);
pHzk16File->readBytes((char *)&charMatrix[0], 32);
pCharMatrix = &charMatrix[0];
} else {
if (pGbkCharMatrix == NULL) {
return;
}
pCharMatrix = pGbkCharMatrix + offset;
}
// startWrite();
if (highlighted) {
M5.Lcd.fillRect(cursor_x, cursor_y, gbkCharWidth, gbkCharHeight, highlightcolor);
} else if (istransparent == false) {
M5.Lcd.fillRect(cursor_x, cursor_y, gbkCharWidth, gbkCharHeight, textbgcolor);
}
for (uint8_t row = 0; row < 16; row++) {
posX = cursor_x;
mask = 0x80;
for (uint8_t col = 0; col < 8; col++) {
if ((*pCharMatrix & mask) != 0) {
if (textsize == 1) {
M5.Lcd.drawPixel(posX, posY, textcolor);
} else {
M5.Lcd.fillRect(posX, posY, textsize, textsize, textcolor);
}
}
if ((*(pCharMatrix + 1) & mask) != 0) {
if (textsize == 1) {
M5.Lcd.drawPixel(posX + ascCharWidth, posY, textcolor);
} else {
M5.Lcd.fillRect(posX + ascCharWidth, posY, textsize, textsize, textcolor);
}
}
mask >>= 1;
posX += textsize;
}
posY += textsize;
pCharMatrix += 2;
}
// endWrite();
cursor_x += gbkCharWidth;
if (textwrap && ((cursor_x + gbkCharWidth) > _width)) {
cursor_x = 0;
cursor_y += gbkCharHeight;
}
}

View File

@@ -0,0 +1,77 @@
#ifndef __DISPLAY_CH_H_
#define __DISPLAY_CH_H_
#include <M5Core2.h>
#include "Fonts/HZK16.h"
#include "Fonts/ASC16.h"
typedef enum
{
DontUsedHzk16,
InternalHzk16,
ExternalHzk16
}Hzk16Types;
class DisplayCh {
public:
DisplayCh();
/**************************************************************************
**
** GBK character support
**
**************************************************************************/
void loadHzk16(Hzk16Types hzkTypes = InternalHzk16,const char* HZK16Path = "/HZK16", const char* ASC16Path = "/ASC16");
void disableHzk16();
void setTextColor(uint16_t c);
void setTextColor(uint16_t c, uint16_t b);
void setCursor(int16_t x, int16_t y);
void setCursor(int16_t x, int16_t y, uint8_t font);
void setTextSize(uint8_t size);
void fillScreen(uint32_t color);
// Highlight the text (Once set to be true, the text background will not be transparent any more)
inline void highlight(bool isHighlight) { highlighted = isHighlight; }
// Set highlight color
inline void setHighlightColor(uint16_t color) { highlightcolor = color; istransparent = false; }
void writeHzk(char* c);
private:
inline void setTransparentBgColor(bool isTransparent) { istransparent = isTransparent; }
inline bool isTransparentBg(){return istransparent;}
bool initHzk16(boolean use, const char* HZK16Path = nullptr, const char* ASC16Path = nullptr);
inline bool isHzk16Used(){return hzk16Used;}
void setTextWrap(boolean wrap);
void writeHzkAsc(const char c);
void writeHzkGbk(const char* c);
private:
uint8_t hzkBufCount,hzkBuf[2];
boolean hzk16Used,istransparent,
highlighted;
Hzk16Types hzk16Type; // Use of HZK16 and ASC16 font.
File
Asc16File, Hzk16File, // Font file
*pAsc16File, *pHzk16File; // Font file pointer
uint8_t *pAscCharMatrix, *pGbkCharMatrix;
uint16_t
highlightcolor,
ascCharWidth,
ascCharHeigth,
gbkCharWidth,
gbkCharHeight;
uint32_t textcolor,textbgcolor;
int32_t cursor_x, cursor_y;
uint8_t textfont,textsize;
uint32_t _width, _height; // Display w/h as modified by current rotation
boolean textwrap; // If set, 'wrap' text at right edge of display
};
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _STR_H_
#define _STR_H_
char* AscStr="ASCII: ABCDEFG1234567";
char* GbkStr="Tao yuanming:寡蒋叫千和 こんにちは";
#endif

View File

@@ -0,0 +1,46 @@
/*
*******************************************************************************
* 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: Ellipse drawing. 椭圆绘制
* date: 2021/7/26
*******************************************************************************/
#include <M5Core2.h>
void setup(void) {
M5.begin(); //Init M5Core2. 初始化M5Core2
}
void loop() {
// Draw some random ellipses. 绘制一些随机形状的椭圆
for (int i = 0; i < 40; i++)
{
int rx = random(60);
int ry = random(60);
int x = rx + random(320 - rx - rx);
int y = ry + random(240 - ry - ry);
M5.Lcd.fillEllipse(x, y, rx, ry, random(0xFFFF)); //At (x, y), draw a filled ellipse with random width and height of rx, ry with random color random (0xFFFF). 在(x,y)处以随机颜色random(0xFFFF),绘制一个随机宽高为rx,ry的填充椭圆
}
delay(2000);
M5.Lcd.clear(); //清空屏幕
for (int i = 0; i < 40; i++)
{
int rx = random(60);
int ry = random(60);
int x = rx + random(320 - rx - rx);
int y = ry + random(240 - ry - ry);
M5.Lcd.drawEllipse(x, y, rx, ry, random(0xFFFF));//At (x, y), draw an elliptical wire frame with a random width and height of rx, ry in a random color (0xFFFF). 在(x,y)处以随机颜色random(0xFFFF),绘制一个随机宽高为rx,ry的椭圆线框
}
delay(2000);
}

View File

@@ -0,0 +1,85 @@
/*
*******************************************************************************
* 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: EzData.
* date: 2021/10/23
*******************************************************************************
*/
#include "M5Core2.h"
#include "M5_EzData.h"
// Configure the name and password of the connected wifi and your token. 配置所连接wifi的名称、密码以及你的token
const char* ssid = "Explore-F";
const char* password = "xingchentansuo123";
const char* token = "";
void setup() {
M5.begin(); //Initialize M5Stack
if(setupWifi(ssid,password)){ //Connect to wifi. 连接到wifi
M5.Lcd.printf("Success connecting to %s\n",ssid);
}else{
M5.Lcd.printf("Connecting to %s failed\n",ssid);
}
}
void loop() {
//Save the data 20 to the top of the testData topic queue. 保存数据20至 testData 队列首位
if(setData(token,"testData",20)){
M5.Lcd.printf("Success sending data to the topic\n");
}else{
M5.Lcd.print("Fail to save data\n");
}
delay(5000);
//Save 3 data in sequence to the first place of testList. 依次保存3个数据至 testList首位
for(int i=0;i<3;i++){
if(addToList(token,"testList",i)){
M5.Lcd.printf("Success sending %d to the list\n",i);
}else{
M5.Lcd.print("Fail to save data\n");
}
delay(100);
}
delay(5000);
//Get a piece of data from a topic and store the value in result. 从一个 topic中获取一个数据,并将值存储在 result
int result=0;
if(getData(token,"testData",result)){
M5.Lcd.printf("Success get data %d\n",result);
}else{
M5.Lcd.print("Fail to get data\n");
}
delay(5000);
//Get a set of data from a list and store the values in the Array array. 从一个 list中获取一组数据,并将值存储在 Array数组中
int Array[3]={};
if(getData(token,"testList",Array,0,3)){
M5.Lcd.print("Success get list\n");
for(int i=0;i<3;i++){
M5.Lcd.printf("Array[%d]=%d,",i,Array[i]);
}
M5.Lcd.println("");
}else{
M5.Lcd.println("Fail to get data");
}
delay(5000);
//Remove data
if(removeData(token,"testData"))
M5.Lcd.printf("Success remove data\n");
else
M5.Lcd.println("Fail to remove data");
if(removeData(token,"testList"))
M5.Lcd.printf("Success remove data from the list\n");
else
M5.Lcd.println("Fail to remove data");
delay(5000);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0,0);
}

View File

@@ -0,0 +1,87 @@
/*
*******************************************************************************
* 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: Hall sensor. 霍尔传感器
* date: 2021/7/26
*******************************************************************************
The ESP32 MCU has build in Hall sensor. ESP32 MCU内置霍尔传感器
It is not very suscescible but if you bring a magnet close to the
upper screen you will see the influence on the measurement.
In this example we use some low pass filters to get rid of the noise.
*/
#include <M5Core2.h>
#define M5STACKFIRE_SPEAKER_PIN 34 // speaker DAC, only 8 Bit. 扬声器的管脚
#define HORIZONTAL_RESOLUTION 320 //Screen horizontal resolution. 屏幕水平分辨率
#define VERTICAL_RESOLUTION 240 //Screen vertical resolution. 屏幕竖直分辨率
#define POSITION_OFFSET_Y 20
uint16_t oldSignal[HORIZONTAL_RESOLUTION];
float ESP32_hallRead() //ESP32 hall value read. ESP32霍尔值读取
{
float value = 0;
int count = 400;
// mean value filter. 数据低通滤波器
for (int n = 0; n < count; n++) value += hallRead();
return value / count * 10;
}
float HallOffset = 0; //Store the initial value of magnetic force. 存储磁力的初值
void setup()
{
dacWrite(M5STACKFIRE_SPEAKER_PIN, 0); // make sure that the speaker is quite. 必须确保扬声器处于关闭状态
M5.begin(); //Init M5Core2. 初始化M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字体大小为2
M5.Lcd.println("ESP32 Hall sensor:"); //Screen printout. 屏幕打印输出
HallOffset = ESP32_hallRead(); // callibrate the output value to the magnetic field at start up. 在启动时将输出值校准到磁场
}
float LowPassFilteredValue=0;
void showSignal()
{
int n;
int oldx;
int oldy;
int oldSig;
int x, y;
for (n = 0; n < HORIZONTAL_RESOLUTION; n++) //在水平分辨率内,每个像素点根据计算得到的磁力大小绘制
{ //Within the horizontal resolution, each pixel is drawn according to the calculated magnetic force
x = n;
float value = ESP32_hallRead()- HallOffset; //Reduce the influence of own magnetism. 减少自身磁力的影响
LowPassFilteredValue+=(value-LowPassFilteredValue)*0.05;
M5.Lcd.setCursor(220, 1);
M5.Lcd.print((int)LowPassFilteredValue);M5.Lcd.print(" ");
y = map(value , -127, 127, VERTICAL_RESOLUTION, POSITION_OFFSET_Y); //Map value to the range of VERTICAL_RESOLUTION~POSITION_OFFSET_Y to avoid drawing beyond the screen. 将value映射至VERTICAL_RESOLUTION~POSITION_OFFSET_Y的范围内,以免绘制超出屏幕
if (n > 0)
{
// delete old line element. 删除上次画的线
M5.Lcd.drawLine(oldx , oldSig, x, oldSignal[n], BLACK );
// draw new line element. 画新的线
if (n < HORIZONTAL_RESOLUTION - 1) // don't draw last element because it would generate artifacts. 不能绘制最后一个像素
{
M5.Lcd.drawLine(oldx,oldy,x,y, GREEN);
}
}
oldx = x;
oldy = y;
oldSig = oldSignal[n];
oldSignal[n] = y;
}
}
void loop(void)
{
showSignal();
}

View File

@@ -0,0 +1,61 @@
/*
*******************************************************************************
* 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: I2C Scanner. I2C探测
* date: 2021/7/26
*******************************************************************************
*/
/*
This program scans the addresses 1-127 continuosly and shows the devices found on the TFT.
该程序连续扫描地址 1-127 并显示在外部(内部)I2C发现的设备。
*/
#include <M5Core2.h>
void setup()
{
M5.begin(true,true,true,true); //Init M5Core2(Initialization of external I2C is also included). 初始化M5Core2(初始化外部I2C也包含在内)
//Wire.begin(21, 22); //Detect internal I2C, if this sentence is not added, it will detect external I2C. 检测内部I2C,若不加此句为检测外部I2C
M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字体大小为2
M5.Lcd.println("M5Core2 I2C Tester"); //Print a string on the screen. 在屏幕上打印字符串
delay(3000);
M5.Lcd.fillScreen( BLACK ); //Make the screen full of black (equivalent to clear() to clear the screen). 使屏幕充满黑色(等效clear()清屏)
}
int textColor=YELLOW;
void loop()
{
int address;
int error;
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("scanning Address [HEX]");
for(address = 1; address < 127; address++ )
{
Wire.beginTransmission(address); //Data transmission to the specified device address starts. 开始向指定的设备地址进行传输数据
error = Wire.endTransmission(); /*Stop data transmission with the slave. 停止与从机的数据传输
0: success. 成功
1: The amount of data exceeds the transmission buffer capacity limit. 数据量超过传送缓存容纳限制
return value: 2: Received NACK when sending address. 传送地址时收到 NACK
3: Received NACK when transmitting data. 传送数据时收到 NACK
4: Other errors. 其它错误 */
if(error==0)
{
M5.Lcd.print(address,HEX);
M5.Lcd.print(" ");
}
else M5.Lcd.print(".");
delay(10);
}
if(textColor==YELLOW) {
textColor=CYAN;
}else textColor=YELLOW;
M5.Lcd.setTextColor(textColor,BLACK); //Set the foreground color of the text to textColor and the background color to BLACK. 设置文字的前景色为textColor背景色为BLACK
}

View File

@@ -0,0 +1,106 @@
/*
*******************************************************************************
* 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: MQTT.
* date: 2021/11/5
*******************************************************************************
*/
#include "M5Core2.h"
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient client(espClient);
// Configure the name and password of the connected wifi and your MQTT Serve host. 配置所连接wifi的名称、密码以及你MQTT服务器域名
const char* ssid = "Explore-F";
const char* password = "xingchentansuo123";
const char* mqtt_server = "mqtt.m5stack.com";
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
void setupWifi();
void callback(char* topic, byte* payload, unsigned int length);
void reConnect();
void setup() {
M5.begin();
setupWifi();
client.setServer(mqtt_server, 1883); //Sets the server details. 配置所连接的服务器
client.setCallback(callback); //Sets the message callback function. 设置消息回调函数
}
void loop() {
if (!client.connected()) {
reConnect();
}
client.loop(); //This function is called periodically to allow clients to process incoming messages and maintain connections to the server.
//定期调用此函数,以允许主机处理传入消息并保持与服务器的连接
unsigned long now = millis(); //Obtain the host startup duration. 获取主机开机时长
if (now - lastMsg > 2000) {
lastMsg = now;
++value;
snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value); //Format to the specified string and store it in MSG. 格式化成指定字符串并存入msg中
M5.Lcd.print("Publish message: ");
M5.Lcd.println(msg);
client.publish("M5Stack", msg); //Publishes a message to the specified topic. 发送一条消息至指定话题
if(value%12==0){
M5.Lcd.clear();
M5.Lcd.setCursor(0,0);
}
}
}
void setupWifi() {
delay(10);
M5.Lcd.printf("Connecting to %s",ssid);
WiFi.mode(WIFI_STA); //Set the mode to WiFi station mode. 设置模式为WIFI站模式
WiFi.begin(ssid, password); //Start Wifi connection. 开始wifi连接
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.printf("\nSuccess\n");
}
void callback(char* topic, byte* payload, unsigned int length) {
M5.Lcd.print("Message arrived [");
M5.Lcd.print(topic);
M5.Lcd.print("] ");
for (int i = 0; i < length; i++) {
M5.Lcd.print((char)payload[i]);
}
M5.Lcd.println();
}
void reConnect() {
while (!client.connected()) {
M5.Lcd.print("Attempting MQTT connection...");
// Create a random client ID. 创建一个随机的客户端ID
String clientId = "M5Stack-";
clientId += String(random(0xffff), HEX);
// Attempt to connect. 尝试重新连接
if (client.connect(clientId.c_str())) {
M5.Lcd.printf("\nSuccess\n");
// Once connected, publish an announcement to the topic. 一旦连接,发送一条消息至指定话题
client.publish("M5Stack", "hello world");
// ... and resubscribe. 重新订阅话题
client.subscribe("M5Stack");
} else {
M5.Lcd.print("failed, rc=");
M5.Lcd.print(client.state());
M5.Lcd.println("try again in 5 seconds");
delay(5000);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
*******************************************************************************
* 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: MultSerial. 多串口
* date: 2021/8/5
******************************************************************************
*/
#include <M5Core2.h>
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
// Serial2.begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert)
Serial2.begin(115200, SERIAL_8N1, 13, 14); //Init serial port 2. 初始化串口2
}
void loop() {
if(Serial.available()) { //If the serial port reads data. 如果串口读到数据
int ch = Serial.read(); // Copy the data read from the serial port to the CH. 把串口读取到的数据复制给ch
Serial2.write(ch); //Serial port 2 Outputs the CH content. 串口2输出ch的内容
M5.Lcd.printf("Serial:%d\n",ch); //The screen prints the data received by serial port 2. 屏幕打印串口2收到的数据
}
if(Serial2.available()) {
int ch = Serial2.read();
Serial.write(ch);
M5.Lcd.printf("Serial2:%d\n",ch);
}
}

View File

@@ -0,0 +1,74 @@
/*
*******************************************************************************
* 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: MultiTask. 多线程
* date: 2021/7/26
*******************************************************************************
*/
#include <M5Core2.h>
void task1(void * pvParameters) { //Define the tasks to be executed in thread 1. 定义线程1内要执行的任务
while(1){ //Keep the thread running. 使线程一直运行
Serial.print("task1 Uptime (ms): ");
Serial.println(millis()); //The running time of the serial port printing program. 串口打印程序运行的时间
delay(100); //With a delay of 100ms, it can be seen in the serial monitor that every 100ms, thread 1 will be executed once. 延迟100ms,在串口监视器内可看到每隔100ms,线程1就会被执行一次
}
}
void task2(void * pvParameters) {
while(1){
Serial.print("task2 Uptime (ms): ");
Serial.println(millis());
delay(200);
}
}
void task3(void * pvParameters) {
while(1){
Serial.print("task3 Uptime (ms): ");
Serial.println(millis());
delay(1000);
}
}
void setup() {
M5.begin(); //Init M5Core2. 初始化M5Core2
// Creat Task1. 创建线程1
xTaskCreatePinnedToCore(
task1, //Function to implement the task. 线程对应函数名称(不能有返回值)
"task1", //线程名称
4096, // The size of the task stack specified as the number of * bytes.任务堆栈的大小(字节)
NULL, // Pointer that will be used as the parameter for the task * being created. 创建作为任务输入参数的指针
1, // Priority of the task. 任务的优先级
NULL, // Task handler. 任务句柄
0); // Core where the task should run. 将任务挂载到指定内核
// Task 2
xTaskCreatePinnedToCore(
task2,
"task2",
4096,
NULL,
2,
NULL,
0);
// Task 3
xTaskCreatePinnedToCore(
task3,
"task3",
4096,
NULL,
3,
NULL,
0);
}
void loop() {
}

View File

@@ -0,0 +1,23 @@
/*
*******************************************************************************
* 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: QRcode. 创建二维码
* date: 2021/7/26
*******************************************************************************
*/
#include <M5Core2.h>
void setup() {
M5.begin(); //Init M5Core2. 初始化M5Core2
M5.Lcd.qrcode("http://www.m5stack.com",0,0,150,6); //Create a QR code with a width of 150 QR code with version 6 at (0, 0). 在(0,0)处创建一个宽为150二维码版本为6的二维码
//Please select the appropriate QR code version according to the number of characters. 请根据字符数量选择合适的二维码版本
}
void loop() {
}

View File

@@ -0,0 +1,37 @@
/*
*******************************************************************************
* 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: counter. 计数器
* date: 2021/8/3
*******************************************************************************
*/
#include <M5Core2.h>
#include <Preferences.h>
Preferences preferences;
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2);
preferences.begin("my-app", false); //We will open storage in RW-mode (second parameter has to be false). 在perferences中创建叫my-app的空间,并以rw模式打开存储(第二个参数必须为false)
//preferences.clear(); // Remove all preferences under the opened namespace.清除preferences中所有的空间
//preferences.remove("counter"); // Or remove the counter key only. 只清除counter中的值
unsigned int counter = preferences.getUInt("counter", 0); //Get the counter value in current sapce, if the key does not exist, return a default value of 0. 在当前空间中读取counter的值(若不存在为0),并赋值给counter
counter++; //Increase counter by 1. 使计数器的值加一
M5.Lcd.printf("Current counter value: %u\n", counter); // Print the counter to Serial Monitor. 串口输出计数器的值
preferences.putUInt("counter", counter); // Store the counter to the Preferences. 存储计数器的值
preferences.end(); // Close the Preferences. 关闭Preferences
M5.Lcd.println("Restarting in 10 seconds..");
delay(10000); //delay 10. 延迟10s
ESP.restart(); // Restart. 重启
}
void loop() {}

View File

@@ -0,0 +1,61 @@
/*
*******************************************************************************
* 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: EEPROM Write.
* date: 2021/7/30
*******************************************************************************
The values stored in the EEPROM will remain in the EEPROM even after the M5Core2 is disconnected.
When a new program is uploaded to the M5Core2, the values stored in the EEPROM can still be called or modified by the new program.
储存于EEPROM的数值即使在断开 M5Core2开发板电源后仍会保存在EEPROM中
当新程序上传到 M5Core2后储存于EEPROM中的数值仍然可以被新的程序调用或者修改
*/
#include <M5Core2.h>
#include <EEPROM.h>
int addr = 0; //EEPROM Start number of an ADDRESS. EEPROM地址起始编号
#define SIZE 32 //define the size of EEPROM(Byte). 定义EEPROM的大小(字节)
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.lcd.setTextSize(2); //Set the text size to 2. 设置文字大小为2
if (!EEPROM.begin(SIZE)){ //Request storage of SIZE size(success return 1). 申请SIZE大小的存储(成功返回1)
M5.Lcd.println("\nFailed to initialise EEPROM!"); //串口输出格式化字符串. Serial output format string
delay(1000000);
}
M5.Lcd.println("\nRead data from EEPROM. Values are:");
for (int i = 0; i < SIZE; i++){
M5.Lcd.printf("%d ",EEPROM.read(i)); //Reads data from 0 to SIZE in EEPROM. 读取EEPROM中从0到SIZE中的数据
}
M5.Lcd.println("\n\nPress BtnA to Write EEPROM");
}
void loop() {
M5.update(); //Check button down state. 检测按键按下状态
M5.lcd.setTextSize(1); //Set the text size to 1. 设置文字大小为1
if(M5.BtnA.isPressed()){ //if the button.A is Pressed. 如果按键A按下
M5.lcd.clear();
M5.lcd.setCursor(0,0);
M5.Lcd.printf("\n%d Bytes datas written on EEPROM.\nValues are:\n",SIZE);
for(int i=0;i<SIZE;i++){
int val = random(256); //Integer values to be stored in the EEPROM (EEPROM can store one byte per memory address, and can only store numbers from 0 to 255. Therefore, if you want to use EEPROM to store the numeric value read by the analog input pin, divide the numeric value by 4.
//将要存储于EEPROM的整数数值(EEPROM每一个存储地址可以储存一个字节只能存储0-255的数.故如果要使用EEPROM存储模拟输入引脚所读取到的数值需要将该数值除以4)
EEPROM.write(addr, val); //Writes the specified data to the specified address. 向指定地址写入指定数据
M5.Lcd.printf("%d ",val);
addr += 1; //Go to the next storage address. 转入下一存储地址
}
//When the storage address sequence number reaches the end of the storage space of the EEPROM, return to. 当存储地址序列号达到EEPROM的存储空间结尾返回到EEPROM开始地址
addr = 0;
M5.Lcd.println("\n\nRead form EEPROM. Values are:");
for(int i=0;i<SIZE;i++){
M5.Lcd.printf("%d ",EEPROM.read(i));
}
M5.Lcd.println("\n-------------------------------------\n");
}
delay(150);
}

View File

@@ -0,0 +1,59 @@
/*
*******************************************************************************
* 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: SPIFFS write & read. 闪存文件读写
* date: 2021/8/4
******************************************************************************
*/
#include <M5Core2.h>
#include <SPIFFS.h>
String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称
bool SPIFFS_FORMAT = true; //Whether to initialize the SPIFFS. 是否初始化SPIFFS
//You don't need to format the flash file system every time you use it.
//无需每次使用闪存都进行格式化
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字号大小为2
if(SPIFFS_FORMAT){
M5.Lcd.println("SPIFFS format start..."); //output format String in LCD. 在屏幕上输出格式化字符串
SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS
M5.Lcd.println("SPIFFS format finish");
}
if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1
M5.Lcd.println("SPIFFS Begin.");
//Write operation
File dataFile = SPIFFS.open(file_name, "w"); // Create a File object dafa File to write information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name写入信息
dataFile.println("Hello IOT World."); // Writes string information and newlines to the dataFile. 向dataFile写入字符串信息并换行
dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件
M5.Lcd.println("Finished Writing data to SPIFFS");
} else {
M5.Lcd.println("SPIFFS Failed to Begin.\nYou need to Run SPIFFS_Add.ino first");
}
}
void loop() {
M5.update(); //Check whether the key is pressed. 检测按键是否按下
if(M5.BtnA.isPressed()){ //If the button is pressed. 如果按键按下
if (SPIFFS.exists(file_name)){ //Check whether the file_name file exists in the flash memory. 确认闪存中是否有file_name文件
M5.Lcd.println("FOUND.");
M5.Lcd.println(file_name);
} else {
M5.Lcd.println("NOT FOUND.");
M5.Lcd.println(file_name);
}
File dataFile = SPIFFS.open(file_name, "r"); // Create aFile object dafaFile to read information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name读取信息
for(int i=0; i<dataFile.size(); i++){ //Reads file contents and outputs file information through the M5.Lcd port monitor. 读取文件内容并且通过串口监视器输出文件信息
M5.Lcd.print((char)dataFile.read());
}
dataFile.close(); //Close the file after reading the file. 完成文件读取后关闭文件
delay(300);
}
}

View File

@@ -0,0 +1,56 @@
/*
*******************************************************************************
* 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: SPIFFS Add(Write is to clean up the contents of the file and write it again.).
* 向SPIFFS中添加信息(write是将文件内容完全清除重新写)
* date: 2021/8/4
******************************************************************************
*/
#include <M5Core2.h>
#include <SPIFFS.h>
String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称
bool SPIFFS_FORMAT = true; //Whether to initialize the SPIFFS. 是否初始化SPIFFS
//You don't need to format the flash file system every time you use it.
//无需每次使用闪存都进行格式化
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字号大小为2
if(SPIFFS_FORMAT){
M5.Lcd.println("\nSPIFFS format start..."); //Screen prints format String. 屏幕打印格式化字符串
SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS
M5.Lcd.println("SPIFFS format finish");
}
if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1
M5.Lcd.println("\nSPIFFS Started.");
} else {
M5.Lcd.println("SPIFFS Failed to Start.");
}
if (SPIFFS.exists(file_name)){ //Check whether the file_name file exists in the flash memory. 确认闪存中是否有file_name文件
M5.Lcd.println("FOUND.");
M5.Lcd.println(file_name);
File dataFile = SPIFFS.open(file_name, "a"); // Create a File object dafaFile to add information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name添加信息
dataFile.println("This is Appended Info."); // Adds string information to dataFile. 向dataFile添加字符串信息
dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件
M5.Lcd.println("Finished Appending data to SPIFFS");
}else {
M5.Lcd.println("NOT FOUND.");
M5.Lcd.print(file_name);
M5.Lcd.println("is creating.");
File dataFile = SPIFFS.open(file_name, "w"); // Create aFile object dafaFile to write information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name写入信息
dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件
M5.Lcd.println("Please disable format and Reupload");
}
}
void loop() {
}

View File

@@ -0,0 +1,37 @@
/*
*******************************************************************************
* 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: SPIFFS Delete
* date: 2021/8/4
******************************************************************************
*/
#include <M5Core2.h>
#include <SPIFFS.h>
String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字号大小为2
if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1
M5.Lcd.println("\nSPIFFS Started."); //Screen prints format String. 屏幕打印格式化字符串
}else{
M5.Lcd.println("SPIFFS Failed to Start.");
}
if(SPIFFS.remove(file_name)){ //Delete file_name file from flash, return 1 on success. 从闪存中删除file_name文件,如果成功返回1
M5.Lcd.print(file_name);
M5.Lcd.println(" Remove sucess");
}else{
M5.Lcd.print(file_name);
M5.Lcd.println(" Remove fail");
}
}
void loop() {
}

View File

@@ -0,0 +1,56 @@
/*
*******************************************************************************
* 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: SPIFFS Add(Write is to clean up the contents of the file and write it again.).
* 向SPIFFS中添加信息(write是将文件内容完全清除重新写)
* date: 2021/8/4
******************************************************************************
*/
#include <M5Core2.h>
#include <SPIFFS.h>
String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称
bool SPIFFS_FORMAT = true; //Whether to initialize the SPIFFS. 是否初始化SPIFFS
//You don't need to format the flash file system every time you use it.
//无需每次使用闪存都进行格式化
void setup() {
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字号大小为2
if(SPIFFS_FORMAT){
M5.Lcd.println("\nSPIFFS format start..."); //Screen prints format String. 屏幕打印格式化字符串
SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS
M5.Lcd.println("SPIFFS format finish");
}
if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1
M5.Lcd.println("\nSPIFFS Started.");
} else {
M5.Lcd.println("SPIFFS Failed to Start.");
}
if (SPIFFS.exists(file_name)){ //Check whether the file_name file exists in the flash memory. 确认闪存中是否有file_name文件
M5.Lcd.println("FOUND.");
M5.Lcd.println(file_name);
File dataFile = SPIFFS.open(file_name, "a"); // Create a File object dafaFile to add information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name添加信息
dataFile.println("This is Appended Info."); // Adds string information to dataFile. 向dataFile添加字符串信息
dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件
M5.Lcd.println("Finished Appending data to SPIFFS");
}else {
M5.Lcd.println("NOT FOUND.");
M5.Lcd.print(file_name);
M5.Lcd.println("is creating.");
File dataFile = SPIFFS.open(file_name, "w"); // Create aFile object dafaFile to write information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name写入信息
dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件
M5.Lcd.println("Please disable format and Reupload");
}
}
void loop() {
}

View File

@@ -0,0 +1,55 @@
/*
*******************************************************************************
* 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: NTP TIME.
* date: 2021/8/3
*******************************************************************************/
#include <M5Core2.h>
#include <WiFi.h>
#include "time.h"
// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码
const char* ssid = "M5";
const char* password = "123456";
const char* ntpServer = "time1.aliyun.com"; //Set the connect NTP server. 设置连接的NTP服务器
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 3600;
void printLocalTime(){ //Output current time. 输出当前时间
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){ //Return 1 when the time is successfully obtained. 成功获取到时间返回1
M5.Lcd.println("Failed to obtain time");
return;
}
M5.Lcd.println(&timeinfo, "%A, %B %d \n%Y %H:%M:%S"); //Screen prints date and time. 屏幕打印日期和时间
}
void setup(){
M5.begin(); //Init M5Core2. 初始化 M5Core2
M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字号大小为2
M5.Lcd.printf("\nConnecting to %s", ssid);
WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态
while(WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功
delay(500); //delay 0.5s. 延迟0.5s
M5.Lcd.print(".");
}
M5.Lcd.println("\nCONNECTED!");
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); //init and get the time. 初始化并设置NTP
printLocalTime();
WiFi.disconnect(true); //Disconnect wifi. 断开wifi连接
WiFi.mode(WIFI_OFF); //Set the wifi mode to off. 设置wifi模式为关闭
delay(20);
}
void loop(){
delay(1000);
M5.Lcd.setCursor(0,47); //Set the cursor at (0,0). 设置光标位于(0,0)处
printLocalTime();
}

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