初始化提交
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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里的内容即可更换为想要显示的字符
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _STR_H_
|
||||
#define _STR_H_
|
||||
|
||||
char* AscStr="ASCII: ABCDEFG1234567";
|
||||
char* GbkStr="Tao yuanming:寡蒋叫千和 こんにちは";
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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. 清除屏幕
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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. 关闭连接
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user