├── library.properties ├── .github ├── workflows │ ├── Arduino-Lint-Check.yml │ ├── auto-close.yml │ ├── clang-format-check.yml │ └── arduino-action-compile.yml ├── ISSUE_TEMPLATE │ └── bug-report.yml └── actions │ └── action.yml ├── .gitignore ├── library.json ├── src ├── M5AtomS3.cpp ├── utility │ ├── LedDisplay.h │ └── LedDisplay.cpp └── M5AtomS3.h ├── examples ├── Basics │ ├── camera │ │ ├── camera_pins.h │ │ └── camera.ino │ ├── lite_led │ │ └── lite_led.ino │ ├── display │ │ └── display.ino │ ├── button │ │ └── button.ino │ ├── imu │ │ └── imu.ino │ └── ir_nec │ │ └── ir_nec.ino ├── Advanced │ ├── WIFI │ │ ├── WiFiSetting │ │ │ ├── detail │ │ │ │ ├── RequestHandler.h │ │ │ │ └── RequestHandlersImpl.h │ │ │ ├── WebServer.h │ │ │ ├── WiFiSetting.ino │ │ │ ├── WebServer.cpp │ │ │ └── Parsing.cpp │ │ ├── WiFiMulti │ │ │ └── WiFiMulti.ino │ │ ├── WiFiScan │ │ │ └── WiFiScan.ino │ │ ├── OTAUpload │ │ │ └── OTAUpload.ino │ │ ├── WiFiSmartConfig │ │ │ └── WiFiSmartConfig.ino │ │ ├── mDNS_Find │ │ │ └── mDNS_Find.ino │ │ ├── WiFiTCP │ │ │ └── WiFiTCP.ino │ │ └── WiFiAccessPoint │ │ │ └── WiFiAccessPoint.ino │ ├── Storage │ │ ├── SPIFFS │ │ │ ├── SPIFFS_Delete │ │ │ │ └── SPIFFS_Delete.ino │ │ │ ├── SPIFFS_Add │ │ │ │ └── SPIFFS_Add.ino │ │ │ └── SPIFFS │ │ │ │ └── SPIFFS.ino │ │ ├── Counter │ │ │ └── Counter.ino │ │ └── EEPROM │ │ │ └── EEPROM.ino │ ├── Time │ │ └── Time.ino │ ├── EzData │ │ └── EzData.ino │ ├── I2C_Tester │ │ └── I2C_Tester.ino │ ├── MultiTask │ │ └── MultiTask.ino │ └── MQTT │ │ └── MQTT.ino ├── AtomicBase │ ├── AtomicPWM │ │ └── AtomicPWM.ino │ ├── AtomicPoE │ │ ├── LinkStatus │ │ │ └── LinkStatus.ino │ │ ├── HTTP │ │ │ └── HTTP.ino │ │ ├── MQTT │ │ │ └── MQTT.ino │ │ └── WebServer │ │ │ └── WebServer.ino │ ├── AtomicHDriver │ │ └── AtomicHDriver.ino │ ├── AtomicMotion │ │ └── AtomicMotion.ino │ ├── AtomicSPK │ │ └── PlayMP3FromSD │ │ │ └── PlayMP3FromSD.ino │ ├── AtomicGPS │ │ ├── GPS │ │ │ └── GPS.ino │ │ └── MicroSD │ │ │ └── MicroSD.ino │ └── AtomicTFCard │ │ └── AtomicTFCard.ino ├── Unit │ └── MiniCAN_TJA1051T │ │ ├── Slave │ │ └── Slave.ino │ │ └── Master │ │ └── Master.ino └── AtomBase │ └── AtomCan │ └── AtomCan.ino ├── LICENSE ├── README_cn.md ├── README.md └── .clang-format /library.properties: -------------------------------------------------------------------------------- 1 | name=M5AtomS3 2 | version=1.0.2 3 | author=M5Stack 4 | maintainer=M5Stack 5 | sentence=Library for M5AtomS3 Core development kit 6 | paragraph=See more on http://M5Stack.com 7 | category=Device Control 8 | url=https://github.com/m5stack/M5AtomS3 9 | architectures=esp32 10 | includes=M5AtomS3.h 11 | depends=M5GFX,M5Unified,M5Family,M5-Ethernet,IRremote,ArduinoHttpClient 12 | -------------------------------------------------------------------------------- /.github/workflows/Arduino-Lint-Check.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Lint Check 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | lint: 7 | name: Lint Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: arduino/arduino-lint-action@v1 12 | with: 13 | library-manager: update 14 | compliance: strict 15 | project-type: all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "M5AtomS3", 3 | "description": "An ESP32 Arduino board", 4 | "keywords": "M5AtomS3", 5 | "authors": { 6 | "name": "M5Stack", 7 | "url": "http://www.m5stack.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/m5stack/M5AtomS3.git" 12 | }, 13 | "version": "1.0.2", 14 | "frameworks": "arduino", 15 | "platforms": "espressif32", 16 | "headers": "M5AtomS3.h" 17 | } -------------------------------------------------------------------------------- /src/M5AtomS3.cpp: -------------------------------------------------------------------------------- 1 | #include "M5AtomS3.h" 2 | 3 | using namespace m5; 4 | 5 | M5AtomS3 AtomS3; 6 | 7 | void M5AtomS3::begin(bool ledEnable) { 8 | M5.begin(); 9 | _led_enable = ledEnable; 10 | if (_led_enable) { 11 | dis.begin(); 12 | } 13 | } 14 | 15 | void M5AtomS3::begin(m5::M5Unified::config_t cfg, bool ledEnable) { 16 | M5.begin(cfg); 17 | _led_enable = ledEnable; 18 | if (_led_enable) { 19 | dis.begin(); 20 | } 21 | } 22 | 23 | void M5AtomS3::update() { 24 | M5.update(); 25 | if (_led_enable) { 26 | dis.show(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/utility/LedDisplay.h: -------------------------------------------------------------------------------- 1 | #ifndef _LED_DISPLAY_H_ 2 | #define _LED_DISPLAY_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define ATOMS3_LITE_LEDS_NUM 1 8 | #define ATOMS3_LITE_LEDS_DATA_PIN 35 9 | 10 | class LedDisplay { 11 | private: 12 | uint8_t _ledPin; 13 | uint8_t _ledNum; 14 | uint8_t _brightness; 15 | CRGB leds[ATOMS3_LITE_LEDS_NUM]; 16 | /* data */ 17 | public: 18 | void begin(); 19 | void drawpix(CRGB Color); 20 | void setBrightness(uint8_t brightness); 21 | void clear(); 22 | void show(); 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /examples/Basics/camera/camera_pins.h: -------------------------------------------------------------------------------- 1 | #ifndef _M5_ATOM_S3R_CAM_H_ 2 | #define _M5_ATOM_S3R_CAM_H_ 3 | 4 | #define PWDN_GPIO_NUM -1 5 | #define RESET_GPIO_NUM -1 6 | #define XCLK_GPIO_NUM 21 7 | #define SIOD_GPIO_NUM 12 8 | #define SIOC_GPIO_NUM 9 9 | #define Y9_GPIO_NUM 13 10 | #define Y8_GPIO_NUM 11 11 | #define Y7_GPIO_NUM 17 12 | #define Y6_GPIO_NUM 4 13 | #define Y5_GPIO_NUM 48 14 | #define Y4_GPIO_NUM 46 15 | #define Y3_GPIO_NUM 42 16 | #define Y2_GPIO_NUM 3 17 | #define VSYNC_GPIO_NUM 10 18 | #define HREF_GPIO_NUM 14 19 | #define PCLK_GPIO_NUM 40 20 | 21 | #define POWER_GPIO_NUM 18 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /.github/workflows/auto-close.yml: -------------------------------------------------------------------------------- 1 | name: auto-close 2 | on: 3 | issues: 4 | types: [opened, reopened] 5 | pull_request_target: 6 | types: [opened, reopened] 7 | 8 | jobs: 9 | close: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: peter-evans/close-issue@v3 13 | if: github.event_name == 'issues' 14 | with: 15 | comment: | 16 | This repository is no longer maintained. 17 | Please use **M5GFX** and **M5Unified** instead. 18 | - uses: peter-evans/close-pull@v3 19 | if: github.event_name == 'pull_request_target' 20 | with: 21 | comment: | 22 | This repository is no longer maintained. 23 | Please submit changes to **M5GFX** / **M5Unified** instead. -------------------------------------------------------------------------------- /src/utility/LedDisplay.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "LedDisplay.h" 3 | 4 | void LedDisplay::begin() { 5 | _ledPin = ATOMS3_LITE_LEDS_DATA_PIN; 6 | _ledNum = ATOMS3_LITE_LEDS_NUM; 7 | FastLED.addLeds(leds, 8 | ATOMS3_LITE_LEDS_NUM); 9 | } 10 | 11 | void LedDisplay::drawpix(CRGB color) { 12 | leds[0] = color; 13 | } 14 | 15 | void LedDisplay::setBrightness(uint8_t brightness) { 16 | brightness = (brightness > 100) ? 100 : brightness; 17 | brightness = (40 * brightness / 100); 18 | _brightness = brightness; 19 | FastLED.setBrightness(_brightness); 20 | } 21 | 22 | void LedDisplay::clear() { 23 | memset(leds, 0, sizeof(CRGB) * _ledNum); 24 | } 25 | 26 | void LedDisplay::show() { 27 | FastLED.show(); 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-check.yml: -------------------------------------------------------------------------------- 1 | name: clang-format Check 2 | on: [push, pull_request] 3 | jobs: 4 | formatting-check: 5 | name: Formatting Check 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | path: 10 | - check: "./" # path to include 11 | exclude: "(FactoryTest)" # path to exclude 12 | # - check: 'src' 13 | # exclude: '(Fonts)' # Exclude file paths containing "Fonts" 14 | # - check: 'examples' 15 | # exclude: '' 16 | steps: 17 | - uses: actions/checkout@v3.1.0 18 | - name: Run clang-format style check for C/C++/Protobuf programs. 19 | uses: jidicula/clang-format-action@v4.9.0 20 | with: 21 | clang-format-version: "13" 22 | check-path: ${{ matrix.path['check'] }} 23 | exclude-regex: ${{ matrix.path['exclude'] }} 24 | -------------------------------------------------------------------------------- /src/M5AtomS3.h: -------------------------------------------------------------------------------- 1 | #ifndef _M5_ATOM_S3_H_ 2 | #define _M5_ATOM_S3_H_ 3 | 4 | #include "M5Unified.h" 5 | #include "M5GFX.h" 6 | #include "./utility/LedDisplay.h" 7 | 8 | namespace m5 { 9 | class M5AtomS3 { 10 | private: 11 | bool _led_enable; 12 | /* data */ 13 | public: 14 | void begin(bool ledEnable = false); 15 | void begin(m5::M5Unified::config_t cfg, bool ledEnable = false); 16 | 17 | M5GFX &Display = M5.Display; 18 | M5GFX &Lcd = Display; 19 | 20 | IMU_Class &Imu = M5.Imu; 21 | Power_Class &Power = M5.Power; 22 | Button_Class &BtnA = M5.BtnA; 23 | 24 | /// for internal I2C device 25 | I2C_Class &In_I2C = m5::In_I2C; 26 | 27 | /// for external I2C device (Port.A) 28 | I2C_Class &Ex_I2C = m5::Ex_I2C; 29 | 30 | LedDisplay dis; 31 | 32 | void update(void); 33 | }; 34 | } // namespace m5 35 | 36 | extern m5::M5AtomS3 AtomS3; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /examples/Basics/lite_led/lite_led.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Led.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Lite LED Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * FastLED: https://github.com/FastLED/FastLED 16 | */ 17 | 18 | #include 19 | 20 | void setup() { 21 | AtomS3.begin(true); // Init M5AtomS3Lite. 22 | AtomS3.dis.setBrightness(100); 23 | } 24 | 25 | void loop() { 26 | AtomS3.dis.drawpix(0xff0000); 27 | AtomS3.update(); 28 | delay(500); 29 | AtomS3.dis.drawpix(0x00ff00); 30 | AtomS3.update(); 31 | delay(500); 32 | AtomS3.dis.drawpix(0x0000ff); 33 | AtomS3.update(); 34 | delay(500); 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 M5Stack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/detail/RequestHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUESTHANDLER_H 2 | #define REQUESTHANDLER_H 3 | 4 | class RequestHandler { 5 | public: 6 | virtual ~RequestHandler() { 7 | } 8 | virtual bool canHandle(HTTPMethod method, String uri) { 9 | (void)method; 10 | (void)uri; 11 | return false; 12 | } 13 | virtual bool canUpload(String uri) { 14 | (void)uri; 15 | return false; 16 | } 17 | virtual bool handle(WebServer& server, HTTPMethod requestMethod, 18 | String requestUri) { 19 | (void)server; 20 | (void)requestMethod; 21 | (void)requestUri; 22 | return false; 23 | } 24 | virtual void upload(WebServer& server, String requestUri, 25 | HTTPUpload& upload) { 26 | (void)server; 27 | (void)requestUri; 28 | (void)upload; 29 | } 30 | 31 | RequestHandler* next() { 32 | return _next; 33 | } 34 | void next(RequestHandler* r) { 35 | _next = r; 36 | } 37 | 38 | private: 39 | RequestHandler* _next = nullptr; 40 | }; 41 | 42 | #endif // REQUESTHANDLER_H 43 | -------------------------------------------------------------------------------- /examples/Basics/display/display.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file display.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Display Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | 19 | void draw_function(LovyanGFX* gfx) { 20 | int x = rand() % gfx->width(); 21 | int y = rand() % gfx->height(); 22 | int r = (gfx->width() >> 4) + 2; 23 | uint16_t c = rand(); 24 | gfx->fillRect(x - r, y - r, r * 2, r * 2, c); 25 | } 26 | 27 | void setup() { 28 | auto cfg = M5.config(); 29 | AtomS3.begin(cfg); 30 | 31 | int textsize = AtomS3.Display.height() / 60; 32 | if (textsize == 0) { 33 | textsize = 1; 34 | } 35 | AtomS3.Display.setTextSize(textsize); 36 | } 37 | 38 | void loop() { 39 | int x = rand() % AtomS3.Display.width(); 40 | int y = rand() % AtomS3.Display.height(); 41 | int r = (AtomS3.Display.width() >> 4) + 2; 42 | uint16_t c = rand(); 43 | AtomS3.Display.fillCircle(x, y, r, c); 44 | draw_function(&AtomS3.Display); 45 | } -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicPWM/AtomicPWM.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtomicPWM.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic PWM Base Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic PWM Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | #include 19 | #include "driver/ledc.h" 20 | 21 | #define SIGNAL 5 22 | 23 | int freq = 10000; 24 | int ledChannel1 = 0; 25 | int resolution = 10; 26 | 27 | void setup() { 28 | auto cfg = M5.config(); 29 | AtomS3.begin(cfg); 30 | 31 | AtomS3.Display.setTextColor(GREEN); 32 | AtomS3.Display.setTextDatum(middle_center); 33 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 34 | AtomS3.Display.setTextSize(1); 35 | AtomS3.Display.drawString("PWM", AtomS3.Display.width() / 2, 36 | AtomS3.Display.height() / 2); 37 | 38 | ledcSetup(ledChannel1, freq, resolution); 39 | ledcAttachPin(SIGNAL, ledChannel1); 40 | } 41 | 42 | void loop() { 43 | for (int i = 0; i < 500; i++) { 44 | ledcWrite(ledChannel1, i); 45 | delay(2); 46 | } 47 | 48 | for (int i = 500; i > 0; i--) { 49 | ledcWrite(ledChannel1, i); 50 | delay(2); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/Basics/button/button.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file button.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Button Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | 19 | void setup() { 20 | auto cfg = M5.config(); 21 | AtomS3.begin(cfg); 22 | 23 | AtomS3.Display.setTextColor(GREEN); 24 | AtomS3.Display.setTextDatum(middle_center); 25 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 26 | AtomS3.Display.setTextSize(1); 27 | AtomS3.Display.drawString("Click!", AtomS3.Display.width() / 2, 28 | AtomS3.Display.height() / 2); 29 | Serial.println("Click BtnA to Test"); 30 | } 31 | 32 | void loop() { 33 | AtomS3.update(); 34 | if (AtomS3.BtnA.wasPressed()) { 35 | AtomS3.Display.clear(); 36 | AtomS3.Display.drawString("Pressed", AtomS3.Display.width() / 2, 37 | AtomS3.Display.height() / 2); 38 | Serial.println("Pressed"); 39 | } 40 | if (AtomS3.BtnA.wasReleased()) { 41 | AtomS3.Display.clear(); 42 | AtomS3.Display.drawString("Released", AtomS3.Display.width() / 2, 43 | AtomS3.Display.height() / 2); 44 | Serial.println("Released"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/Advanced/Storage/SPIFFS/SPIFFS_Delete/SPIFFS_Delete.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: SPIFFS Delete 10 | * Date: 2022/12/20 11 | ****************************************************************************** 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | String file_name = 18 | "/M5Stack/notes.txt"; // Sets the location and name of the file to be 19 | // operated on. 设置被操作的文件位置和名称 20 | void setup() { 21 | AtomS3.begin(); // Init M5Atom. 初始化 M5ATOM 22 | 23 | if (SPIFFS.begin()) { // Start SPIFFS, return 1 on success. 24 | // 启动闪存文件系统,若成功返回1 25 | AtomS3.Lcd.println( 26 | "\nSPIFFS Started."); // Serial port output format 27 | // String. 串口输出格式化字符串 28 | } else { 29 | AtomS3.Lcd.println("SPIFFS Failed to Start."); 30 | } 31 | 32 | if (SPIFFS.remove( 33 | file_name)) { // Delete file_name file from flash, return 1 on 34 | // success. 从闪存中删除file_name文件,如果成功返回1 35 | AtomS3.Lcd.print(file_name); 36 | AtomS3.Lcd.println(" Remove sucess"); 37 | } else { 38 | AtomS3.Lcd.print(file_name); 39 | AtomS3.Lcd.println(" Remove fail"); 40 | } 41 | } 42 | 43 | void loop() { 44 | } -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # AtomS3 Library 2 | 3 | [![Arduino Compile](https://github.com/m5stack/M5AtomS3/actions/workflows/arduino-action-compile.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/arduino-action-compile.yml) 4 | [![Arduino Lint](https://github.com/m5stack/M5AtomS3/actions/workflows/Arduino-Lint-Check.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/Arduino-Lint-Check.yml) 5 | [![Clang Format](https://github.com/m5stack/M5AtomS3/actions/workflows/clang-format-check.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/clang-format-check.yml) 6 | 7 | 中文 | [English](README_cn.md) 8 | 9 | M5Atom Lite 10 | 11 | * **如果查看 AtomS3 的详细介绍文档,[点击这里](https://docs.m5stack.com/zh_CN/core/AtomS3)** 12 | 13 | ## 描述 14 | 15 | `AtomS3` 是一款基于`ESP32-S3`主控的高集成度可编程控制器,内部集成了ESP32-S3主控,集成`WiFi`功能,`8M`片上FLASH;`0.85`寸`IPS屏幕`;屏幕下方具有`可编程按键功能`;内置5V转3.3V电路;6轴陀螺仪传感器`MPU6886`;板载`Type-C`接口,供电及固件下载;一个`HY2.0-4P`扩展端口;底部预留6个`GPIO以及电源引脚`,方便扩展应用。产品大小只有`24 * 24mm`,适用于各种`嵌入式`的智能设备应用。 16 | 17 | ## 应用 18 | 19 | - 物联网节点 20 | - 微型控制器 21 | - 可穿戴设备 22 | 23 | ## PlatformIO 编译 24 | 25 | [编译文件](https://github.com/m5stack/M5AtomS3/blob/main/platformio.ini) 26 | 27 | ## 原理图 28 | 29 | schematics 30 | 31 | ## 尺寸图 32 | 33 | module size 34 | 35 | ## 更多信息 36 | 37 | **Arduino IDE 环境搭建**: [点击这里](https://docs.m5stack.com/zh_CN/quick_start/atoms3/arduino) -------------------------------------------------------------------------------- /examples/Advanced/Storage/Counter/Counter.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: counter. 计数器 10 | * Date: 2022/12/20 11 | ******************************************************************************* 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | Preferences preferences; 18 | 19 | void setup() { 20 | AtomS3.begin(); // Init AtomS3. 初始化 AtomS3 21 | preferences.begin( 22 | "my-app", 23 | false); // We will open storage in RW-mode (second parameter has to be 24 | // false). 25 | // 在perferences中创建叫my-app的空间,并以rw模式打开存储(第二个参数必须为false) 26 | 27 | // preferences.clear(); // Remove all preferences under the opened 28 | // namespace.清除preferences中所有的空间 29 | 30 | // preferences.remove("counter"); // Or remove the counter key only. 31 | // 只清除counter中的值 32 | 33 | unsigned int counter = preferences.getUInt( 34 | "counter", 35 | 0); // Get the counter value in current sapce, if the key does not 36 | // exist, return a default value of 0. 37 | // 在当前空间中读取counter的值(若不存在为0),并赋值给counter 38 | counter++; // Increase counter by 1. 使计数器的值加一 39 | AtomS3.Lcd.printf( 40 | "Current counter value: %u\n", 41 | counter); // Print the counter to Serial Monitor. 串口输出计数器的值 42 | preferences.putUInt( 43 | "counter", 44 | counter); // Store the counter to the Preferences. 存储计数器的值 45 | preferences.end(); // Close the Preferences. 关闭Preferences 46 | AtomS3.Lcd.println("Restarting in 10 seconds..."); 47 | delay(10000); // delay 10. 延迟10s 48 | ESP.restart(); // Restart. 重启 49 | } 50 | void loop() { 51 | } 52 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicPoE/LinkStatus/LinkStatus.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file LinkStatus.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic PoE Base LinkStatus Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic PoE Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * M5_Ethernet: https://github.com/m5stack/M5-Ethernet 16 | */ 17 | 18 | #include "M5AtomS3.h" 19 | #include 20 | #include 21 | 22 | #define SCK 5 23 | #define MISO 7 24 | #define MOSI 8 25 | #define CS 6 26 | 27 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x89}; 28 | 29 | void setup() { 30 | auto cfg = M5.config(); 31 | AtomS3.begin(cfg); 32 | 33 | AtomS3.Display.setTextColor(GREEN); 34 | AtomS3.Display.setTextDatum(middle_center); 35 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 36 | AtomS3.Display.setTextSize(1); 37 | AtomS3.Display.drawString("PoE", AtomS3.Display.width() / 2, 20); 38 | 39 | SPI.begin(SCK, MISO, MOSI, -1); 40 | Ethernet.init(CS); 41 | } 42 | 43 | void loop() { 44 | auto link = Ethernet.linkStatus(); 45 | Serial.print("Link status: "); 46 | switch (link) { 47 | case Unknown: 48 | AtomS3.Display.clear(); 49 | AtomS3.Display.drawString("Unknown", AtomS3.Display.width() / 2, 50 | AtomS3.Display.height() / 2); 51 | Serial.println("Unknown"); 52 | break; 53 | case LinkON: 54 | AtomS3.Display.clear(); 55 | AtomS3.Display.drawString("ON", AtomS3.Display.width() / 2, 56 | AtomS3.Display.height() / 2); 57 | Serial.println("ON"); 58 | break; 59 | case LinkOFF: 60 | AtomS3.Display.clear(); 61 | AtomS3.Display.drawString("OFF", AtomS3.Display.width() / 2, 62 | AtomS3.Display.height() / 2); 63 | Serial.println("OFF"); 64 | break; 65 | } 66 | delay(500); 67 | } 68 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiMulti/WiFiMulti.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: WIFI Multi. 多wifi择优 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | * Connect to the best AP based on a given wifi list 13 | * 根据给定wifi的列表连接到最好的AP 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | WiFiMulti wifiMulti; 21 | 22 | void setup() { 23 | AtomS3.begin(); // Init M5AtomS3. 初始化 M5AtomS3 24 | wifiMulti.addAP("wifi1", "123456"); // Storage wifi configuration 25 | // information 1. 存储wifi配置信息1 26 | wifiMulti.addAP("wifi2", "123456123456"); 27 | wifiMulti.addAP("aaa", "sadf"); 28 | AtomS3.Lcd.print("Connecting Wifi..."); // Serial port format output 29 | // string. 串口格式化输出字符串 30 | } 31 | 32 | void loop() { 33 | if (wifiMulti.run() == 34 | WL_CONNECTED) { // If the connection to wifi is established 35 | // successfully. 如果与wifi成功建立连接 36 | AtomS3.Lcd.setCursor(0, 20); 37 | AtomS3.Lcd.print("WiFi connected\n\nSSID:"); 38 | AtomS3.Lcd.println(WiFi.SSID()); // Output Network name. 输出网络名称 39 | AtomS3.Lcd.print("RSSI: "); 40 | AtomS3.Lcd.println( 41 | WiFi.RSSI()); // Output signal strength. 输出信号强度 42 | AtomS3.Lcd.print("IP address: "); 43 | AtomS3.Lcd.println(WiFi.localIP()); // Output IP Address. 输出IP地址 44 | delay(1000); 45 | AtomS3.Lcd.fillRect(0, 20, 180, 300, 46 | BLACK); // It's equivalent to partial screen 47 | // clearance. 相当于部分清屏 48 | } else { 49 | // If the connection to wifi is not established successfully. 50 | // 如果没有与wifi成功建立连接 51 | AtomS3.Lcd.print("."); 52 | delay(1000); 53 | } 54 | } -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicHDriver/AtomicHDriver.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtomicHDriver.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic H-Driver Base Test 5 | * @version 0.1 6 | * @date 2024-01-19 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic H-Driver Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.1.0 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | 19 | const int IN1_PIN = 6; 20 | const int IN2_PIN = 7; 21 | int freq = 10000; 22 | int ledChannel1 = 0; 23 | int ledChannel2 = 1; 24 | int resolution = 10; 25 | bool direction = true; 26 | int VIN_PIN = 8; 27 | int FAULT_PIN = 5; 28 | 29 | void setup() { 30 | auto cfg = M5.config(); 31 | AtomS3.begin(cfg); 32 | 33 | AtomS3.Display.setTextColor(GREEN); 34 | AtomS3.Display.setTextDatum(middle_center); 35 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 36 | AtomS3.Display.setTextSize(1); 37 | AtomS3.Display.drawString("H-Driver", AtomS3.Display.width() / 2, 38 | AtomS3.Display.height() / 2); 39 | 40 | ledcSetup(ledChannel1, freq, resolution); 41 | ledcSetup(ledChannel2, freq, resolution); 42 | ledcAttachPin(IN1_PIN, ledChannel1); 43 | ledcAttachPin(IN2_PIN, ledChannel2); 44 | pinMode(VIN_PIN, INPUT); 45 | pinMode(FAULT_PIN, INPUT); 46 | ledcWrite(ledChannel1, 0); 47 | ledcWrite(ledChannel2, 0); 48 | } 49 | 50 | void loop() { 51 | if (M5.BtnA.pressedFor(2000)) { 52 | ledcWrite(ledChannel1, 0); 53 | ledcWrite(ledChannel2, 0); 54 | } 55 | 56 | if (M5.BtnA.wasPressed()) { 57 | if (direction) { 58 | ledcWrite(ledChannel1, 1000); 59 | ledcWrite(ledChannel2, 0); 60 | } else { 61 | ledcWrite(ledChannel1, 0); 62 | ledcWrite(ledChannel2, 1000); 63 | } 64 | direction = !direction; 65 | } 66 | 67 | M5.update(); 68 | Serial.println("VIN IN: " + 69 | String((analogRead(VIN_PIN) * 10.0 / 4095.0) * 3.6)); 70 | if (digitalRead(FAULT_PIN) == 0) { 71 | Serial.println("FAULT!"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/Advanced/Time/Time.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: NTP TIME. 10 | * Date: 2022/12/20 11 | *******************************************************************************/ 12 | #include 13 | #include 14 | #include "time.h" 15 | 16 | // Set the name and password of the wifi to be connected. 17 | // 配置所连接wifi的名称和密码 18 | const char* ssid = ""; 19 | const char* password = ""; 20 | 21 | const char* ntpServer = 22 | "time1.aliyun.com"; // Set the connect NTP server. 设置连接的NTP服务器 23 | const long gmtOffset_sec = 0; 24 | const int daylightOffset_sec = 3600; 25 | 26 | void printLocalTime() { // Output current time. 输出当前时间 27 | struct tm timeinfo; 28 | if (!getLocalTime(&timeinfo)) { // Return 1 when the time is successfully 29 | // obtained. 成功获取到时间返回1 30 | AtomS3.Lcd.println("Failed to obtain time"); 31 | return; 32 | } 33 | AtomS3.Lcd.println( 34 | &timeinfo, 35 | "%A, %B %d \n%Y %H:%M:%S"); // Serial port output date and 36 | // time. 串口输出日期和时间 37 | } 38 | 39 | void setup() { 40 | AtomS3.begin(); // Init Atom(Initialize LCD, serial port, 41 | // 初始化 ATOM(初始化LCD、串口) 42 | AtomS3.Lcd.printf("\nConnecting to %s", ssid); 43 | WiFi.begin(ssid, password); // Connect wifi and return connection status. 44 | // 连接wifi并返回连接状态 45 | while (WiFi.status() != 46 | WL_CONNECTED) { // If the wifi connection fails. 若wifi未连接成功 47 | delay(500); // delay 0.5s. 延迟0.5s 48 | AtomS3.Lcd.print("."); 49 | } 50 | AtomS3.Lcd.println(" CONNECTED"); 51 | configTime(gmtOffset_sec, daylightOffset_sec, 52 | ntpServer); // init and get the time. 初始化并设置NTP 53 | printLocalTime(); 54 | WiFi.disconnect(true); // Disconnect wifi. 断开wifi连接 55 | WiFi.mode(WIFI_OFF); // Set the wifi mode to off. 设置wifi模式为关闭 56 | delay(20); 57 | } 58 | 59 | void loop() { 60 | delay(1000); 61 | printLocalTime(); 62 | } 63 | -------------------------------------------------------------------------------- /examples/Advanced/EzData/EzData.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * describe:EzData. 10 | * Date: 2022/12/29 11 | ******************************************************************************* 12 | */ 13 | 14 | #include "M5AtomS3.h" 15 | #include "M5_EzData.h" 16 | 17 | // Configure the name and password of the connected wifi and your token. 18 | // 配置所连接wifi的名称、密码以及你的token 19 | const char* ssid = "WIFI_SSID"; 20 | const char* password = "WIFI_PASSWORD"; 21 | const char* token = ""; 22 | 23 | void setup() { 24 | AtomS3.begin(); // Initialize M5Stack 25 | if (setupWifi(ssid, password)) { // Connect to wifi. 连接到wifi 26 | Serial.printf("Success connecting to %s\n", ssid); 27 | } else { 28 | Serial.printf("Connecting to %s failed\n", ssid); 29 | } 30 | } 31 | 32 | void loop() { 33 | // Save the data 20 to the top of the testData topic queue. 保存数据20至 34 | // testData 队列首位 35 | setData(token, "testData", 20); 36 | delay(5000); 37 | 38 | // Save 3 data in sequence to the first place of testList. 依次保存3个数据至 39 | // testList首位 40 | for (int i = 0; i < 3; i++) { 41 | addToList(token, "testList", i); 42 | delay(100); 43 | } 44 | delay(5000); 45 | 46 | // Get a piece of data from a topic and store the value in result. 从一个 47 | // topic中获取一个数据,并将值存储在 result 48 | int result = 0; 49 | if (getData(token, "testData", result)) 50 | ; 51 | delay(5000); 52 | 53 | // Get a set of data from a list and store the values in the Array array. 54 | // 从一个 list中获取一组数据,并将值存储在 Array数组中 55 | int Array[3] = {}; 56 | if (getData(token, "testList", Array, 0, 3)) { 57 | Serial.print("Success get list\n"); 58 | for (int i = 0; i < 3; i++) { 59 | Serial.printf("Array[%d]=%d,", i, Array[i]); 60 | } 61 | Serial.println(""); 62 | } else { 63 | Serial.println("Fail to get data"); 64 | } 65 | delay(5000); 66 | 67 | // Remove data 68 | removeData(token, "testData"); 69 | removeData(token, "testList"); 70 | delay(5000); 71 | } -------------------------------------------------------------------------------- /examples/Advanced/I2C_Tester/I2C_Tester.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: I2C Scanner. I2C探测 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | This program scans the addresses 1-127 continuosly and shows the devices found 13 | on the TFT. 该程序连续扫描地址 1-127 并显示在外部(内部)I2C发现的设备。 14 | */ 15 | #include 16 | #include "Wire.h" 17 | 18 | void setup() { 19 | AtomS3.begin(); // Init M5AtomS3(Initialization of internal I2C is also 20 | // included). M5AtomS3(初始化内部I2C也包含在内) 21 | Wire.begin(2, 22 | 1); // Detect external I2C, if this sentence is not added, 23 | // detect internal I2C. 检测外部I2C,若不加此句为检测内部I2C 24 | AtomS3.Lcd.println("M5AtomS3 I2C Tester"); // Print a string on the screen. 25 | // 在屏幕上打印字符串 26 | delay(3000); 27 | } 28 | 29 | void loop() { 30 | int address; 31 | int error; 32 | AtomS3.Lcd.printf("\nscanning Address [HEX]\n"); 33 | for (address = 1; address < 127; address++) { 34 | Wire.beginTransmission( 35 | address); // Data transmission to the specified device address 36 | // starts. 开始向指定的设备地址进行传输数据 37 | error = Wire.endTransmission(); /*Stop data transmission with the slave. 38 | 停止与从机的数据传输 0: success. 成功 1: The amount of data 39 | exceeds the transmission buffer capacity limit. 40 | 数据量超过传送缓存容纳限制 return value: 2: 41 | Received NACK when sending address. 传送地址时收到 NACK 3: 42 | Received NACK when transmitting data. 传送数据时收到 NACK 43 | 4: Other errors. 其它错误 */ 44 | if (error == 0) { 45 | AtomS3.Lcd.print(address, HEX); 46 | AtomS3.Lcd.print(" "); 47 | } else 48 | AtomS3.Lcd.print("."); 49 | 50 | delay(10); 51 | } 52 | delay(1000); 53 | AtomS3.Lcd.setCursor(1, 12); 54 | AtomS3.Lcd.fillRect(1, 15, 128, 128, BLACK); 55 | } 56 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiScan/WiFiScan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: Wifi scan. wifi扫描 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | */ 13 | #include 14 | #include "WiFi.h" 15 | 16 | void setup() { 17 | AtomS3.begin(); // Init M5AtomS3. 初始化M5AtomS3 18 | WiFi.mode(WIFI_STA); // Set WiFi to station mode and disconnect from an AP 19 | // if it was previously connected. 20 | // 将WiFi设置为站模式,如果之前连接过AP,则断开连接 21 | WiFi.disconnect(); // Turn off all wifi connections. 关闭所有wifi连接 22 | delay(100); // 100 ms delay. 延迟100ms 23 | AtomS3.Lcd.print("WIFI SCAN"); // Screen print string. 屏幕打印字符串 24 | } 25 | 26 | void loop() { 27 | AtomS3.Lcd.setCursor(2, 2); // Set the cursor at (2,2). 将光标设置在(2,2)处 28 | AtomS3.Lcd.println("Please press Btn to (re)scan"); 29 | AtomS3.update(); // Check the status of the key. 检测按键的状态 30 | if (AtomS3.BtnA.isPressed()) { // If button is pressed. 如果按键按下 31 | AtomS3.Lcd.clear(); // Clear the screen. 清空屏幕 32 | AtomS3.Lcd.println("scan start"); 33 | int n = WiFi.scanNetworks(); // return the number of networks found. 34 | // 返回发现的网络数 35 | if (n == 0) { // If no network is found. 如果没有找到网络 36 | AtomS3.Lcd.println("no networks found"); 37 | } else { // If have network is found. 找到网络 38 | AtomS3.Lcd.printf("networks found:%d\n\n", n); 39 | for (int i = 0; i < n; 40 | ++i) { // Print SSID and RSSI for each network found. 41 | // 打印每个找到的网络的SSID和信号强度 42 | AtomS3.Lcd.printf("%d:", i + 1); 43 | AtomS3.Lcd.print(WiFi.SSID(i)); 44 | AtomS3.Lcd.printf("(%d)", WiFi.RSSI(i)); 45 | AtomS3.Lcd.println( 46 | (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); 47 | delay(10); 48 | } 49 | } 50 | delay(1000); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/Basics/imu/imu.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file imu.ino 4 | * @author SeanKwok (shaoxiang@m5stack.com) 5 | * @brief M5AtomS3 IMU Test 6 | * @version 0.2 7 | * @date 2023-12-13 8 | * 9 | * 10 | * @Hardwares: M5AtomS3 / M5AtomS3R 11 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 12 | * @Dependent Library: 13 | * M5GFX: https://github.com/m5stack/M5GFX 14 | * M5Unified: https://github.com/m5stack/M5Unified 15 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 16 | */ 17 | 18 | #include 19 | 20 | void setup(void) 21 | { 22 | delay(2000); 23 | auto cfg = M5.config(); 24 | cfg.serial_baudrate = 115200; 25 | M5.begin(cfg); 26 | } 27 | 28 | void loop(void) 29 | { 30 | auto imu_update = M5.Imu.update(); 31 | if (imu_update) { 32 | M5.Lcd.setCursor(0, 40); 33 | M5.Lcd.fillRect(0, 40, M5.Lcd.width(), 40, BLACK); 34 | 35 | auto data = M5.Imu.getImuData(); 36 | 37 | // The data obtained by getImuData can be used as follows. 38 | data.accel.x; // accel x-axis value. 39 | data.accel.y; // accel y-axis value. 40 | data.accel.z; // accel z-axis value. 41 | data.accel.value; // accel 3values array [0]=x / [1]=y / [2]=z. 42 | 43 | data.gyro.x; // gyro x-axis value. 44 | data.gyro.y; // gyro y-axis value. 45 | data.gyro.z; // gyro z-axis value. 46 | data.gyro.value; // gyro 3values array [0]=x / [1]=y / [2]=z. 47 | 48 | if (M5.getBoard() == m5::board_t::board_M5AtomS3R) { 49 | data.mag.x; // mag x-axis value. 50 | data.mag.y; // mag y-axis value. 51 | data.mag.z; // mag z-axis value. 52 | data.mag.value; // mag 3values array [0]=x / [1]=y / [2]=z. 53 | } 54 | 55 | data.value; // all sensor 9values array [0~2]=accel / [3~5]=gyro / 56 | // [6~8]=mag 57 | 58 | Serial.printf("ax:%f ay:%f az:%f\r\n", data.accel.x, data.accel.y, data.accel.z); 59 | Serial.printf("gx:%f gy:%f gz:%f\r\n", data.gyro.x, data.gyro.y, data.gyro.z); 60 | Serial.printf("mx:%f my:%f mz:%f\r\n", data.mag.x, data.mag.y, data.mag.z); 61 | 62 | M5.Lcd.printf("IMU:\r\n"); 63 | M5.Lcd.printf("%0.2f %0.2f %0.2f\r\n", data.accel.x, data.accel.y, data.accel.z); 64 | M5.Lcd.printf("%0.2f %0.2f %0.2f\r\n", data.gyro.x, data.gyro.y, data.gyro.z); 65 | M5.Lcd.printf("%0.2f %0.2f %0.2f\r\n", data.mag.x, data.mag.y, data.mag.z); 66 | } 67 | 68 | delay(100); 69 | } 70 | -------------------------------------------------------------------------------- /examples/Basics/ir_nec/ir_nec.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ir_nec.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 IR NEC test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * IRremote: https://github.com/Arduino-IRremote/Arduino-IRremote 16 | */ 17 | 18 | #define DISABLE_CODE_FOR_RECEIVER // Disables restarting receiver after each 19 | // send. Saves 450 bytes program memory and 20 | // 269 bytes RAM if receiving functions are 21 | // not used. 22 | #define SEND_PWM_BY_TIMER 23 | #define ATOMS3_IR_TX_PIN 4 24 | // #define ATOMS3R_IR_TX_PIN 47 25 | 26 | #include "M5AtomS3.h" 27 | #include // include the library 28 | 29 | uint8_t sCommand = 0x34; 30 | uint8_t sRepeats = 0; 31 | 32 | void setup() { 33 | auto cfg = M5.config(); 34 | AtomS3.begin(cfg); 35 | AtomS3.Display.setTextColor(GREEN); 36 | AtomS3.Display.setTextDatum(middle_center); 37 | AtomS3.Display.setFont(&fonts::FreeMono9pt7b); 38 | AtomS3.Display.setTextSize(1); 39 | 40 | IrSender.begin(DISABLE_LED_FEEDBACK); // Start with IR_SEND_PIN as send pin 41 | IrSender.setSendPin(ATOMS3_IR_TX_PIN); 42 | } 43 | 44 | void loop() { 45 | Serial.println(); 46 | Serial.print(F("Send now: address=0x1111, command=0x")); 47 | Serial.print(sCommand, HEX); 48 | Serial.print(F(", repeats=")); 49 | Serial.print(sRepeats); 50 | Serial.println(); 51 | 52 | AtomS3.Display.clear(); 53 | AtomS3.Display.drawString("IR NEC SEND", AtomS3.Display.width() / 2, AtomS3.Display.height() / 2 - 40); 54 | 55 | AtomS3.Display.drawString("ADDR:0x1111", AtomS3.Display.width() / 2, AtomS3.Display.height() / 2); 56 | 57 | AtomS3.Display.drawString("CMD:0x" + String(sCommand, HEX), AtomS3.Display.width() / 2, 58 | AtomS3.Display.height() / 2 + 40); 59 | 60 | Serial.println(F("Send standard NEC with 16 bit address")); 61 | 62 | AtomS3.Display.fillCircle(10, 105, 8, GREEN); 63 | IrSender.sendNEC(0x1111, sCommand, sRepeats); 64 | // IrSender.sendOnkyo(0x1111, 0x2223, sRepeats); 65 | /* 66 | * Increment send values 67 | */ 68 | sCommand += 1; 69 | delay(500); 70 | AtomS3.Display.fillCircle(10, 105, 8, YELLOW); 71 | delay(500); 72 | } 73 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/OTAUpload/OTAUpload.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: OTA Upload. 隔空传输程序 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | PC and M5AtomS3 can only be used on the same wifi. 13 | 电脑和M5AtomS3需在同一wifi下才可使用 When the OTA is ready, restart the Arduino 14 | client from Tools > Ports > Network ports to instantly transmit the program 15 | wirelessly. OTA 准备好后重启Arduino客户端在工具->端口->网络端口,即刻无线传输程序 16 | */ 17 | #include 18 | #include 19 | #include 20 | 21 | // Set the name and password of the wifi to be connected. 22 | // 配置所连接wifi的名称和密码 23 | const char* ssid = "your-ap"; 24 | const char* password = "your-password"; 25 | 26 | void setup() { 27 | AtomS3.begin(); // Init M5AtomS3. 初始化 M5AtomS3 28 | WiFi.begin(ssid, password); // Connect wifi and return connection status. 29 | // 连接wifi并返回连接状态 30 | AtomS3.Lcd.print("Waiting Wifi Connect"); 31 | while (WiFi.status() != 32 | WL_CONNECTED) { // If the wifi connection fails. 若wifi未连接成功 33 | delay(1000); 34 | AtomS3.Lcd.print("."); 35 | } 36 | AtomS3.Lcd.println("\nWiFi Connected!"); 37 | AtomS3.Lcd.print("WiFi Connect To: "); 38 | AtomS3.Lcd.println(WiFi.SSID()); // Output Network name. 输出网络名称 39 | AtomS3.Lcd.print("IP address: "); 40 | AtomS3.Lcd.println(WiFi.localIP()); // Output IP Address. 输出IP地址 41 | 42 | ArduinoOTA.setHostname( 43 | "M5AtomS3"); // Set the network port name. 设置网络端口名称 44 | ArduinoOTA.setPassword("666666"); // Set the network port connection 45 | // password. 设置网络端口连接的密码 46 | ArduinoOTA.begin(); // Initialize the OTA. 初始化OTA 47 | AtomS3.Lcd.println("OTA ready!"); // AtomS3.Lcd port output format string. 48 | // 串口输出格式化字符串 49 | } 50 | 51 | void loop() { 52 | ArduinoOTA.handle(); // Continuously check for update requests. 53 | // 持续检测是否有更新请求 54 | AtomS3.update(); 55 | if (AtomS3.BtnA.isPressed()) { // if botton is Pressed. 如果按键按下 56 | ArduinoOTA.end(); // Ends the ArduinoOTA service. 结束OTA服务 57 | AtomS3.Lcd.println("OTA End!"); 58 | delay(1000); 59 | } 60 | } -------------------------------------------------------------------------------- /examples/Advanced/MultiTask/MultiTask.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: MultiTask. 多线程 10 | * Date: 2022/12/19 11 | ******************************************************************************* 12 | */ 13 | 14 | #include 15 | 16 | void task1(void* pvParameters) { // Define the tasks to be executed in 17 | // thread 1. 定义线程1内要执行的任务 18 | while (1) { // Keep the thread running. 使线程一直运行 19 | Serial.print("task1 Uptime (ms): "); 20 | Serial.println(millis()); // The running time of the serial port 21 | // printing program. 串口打印程序运行的时间 22 | delay( 23 | 100); // With a delay of 100ms, it can be seen in the serial 24 | // monitor that every 100ms, thread 1 will be executed once. 25 | // 延迟100ms,在串口监视器内可看到每隔100ms,线程1就会被执行一次 26 | } 27 | } 28 | 29 | void task2(void* pvParameters) { 30 | while (1) { 31 | Serial.print("task2 Uptime (ms): "); 32 | Serial.println(millis()); 33 | delay(200); 34 | } 35 | } 36 | 37 | void task3(void* pvParameters) { 38 | while (1) { 39 | Serial.print("task3 Uptime (ms): "); 40 | Serial.println(millis()); 41 | delay(1000); 42 | } 43 | } 44 | 45 | void setup() { 46 | AtomS3.begin(); // Init AtomS3(Initialize LCD, serial port). 47 | // 初始化 AtomS3(初始化LCD、串口) 48 | // Creat Task1. 创建线程1 49 | xTaskCreatePinnedToCore( 50 | task1, // Function to implement the task. 51 | // 线程对应函数名称(不能有返回值) 52 | "task1", // 线程名称 53 | 4096, // The size of the task stack specified as the number of * 54 | // bytes.任务堆栈的大小(字节) 55 | NULL, // Pointer that will be used as the parameter for the task * 56 | // being created. 创建作为任务输入参数的指针 57 | 1, // Priority of the task. 任务的优先级 58 | NULL, // Task handler. 任务句柄 59 | 0); // Core where the task should run. 将任务挂载到指定内核 60 | 61 | // Task 2 62 | xTaskCreatePinnedToCore(task2, "task2", 4096, NULL, 2, NULL, 0); 63 | 64 | // Task 3 65 | xTaskCreatePinnedToCore(task3, "task3", 4096, NULL, 3, NULL, 0); 66 | } 67 | 68 | void loop() { 69 | AtomS3.Lcd.print("See serial port for task details"); 70 | } -------------------------------------------------------------------------------- /examples/Unit/MiniCAN_TJA1051T/Slave/Slave.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Slave.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief Unit Mini CAN Test Slave 5 | * @version 0.1 6 | * @date 2024-02-01 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Unit Mini CAN 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include 18 | #include 19 | #include "freertos/FreeRTOS.h" 20 | #include "freertos/task.h" 21 | #include "freertos/queue.h" 22 | #include "freertos/semphr.h" 23 | #include "esp_err.h" 24 | #include "esp_log.h" 25 | #include "driver/twai.h" 26 | #include "esp_err.h" 27 | 28 | #include "M5AtomS3.h" 29 | 30 | /* --------------------- Definitions and static variables ------------------ */ 31 | 32 | #define RX_TASK_PRIO 8 // Receiving task priority 33 | #define TX_GPIO_NUM gpio_num_t(2) 34 | #define RX_GPIO_NUM gpio_num_t(1) 35 | 36 | static const twai_general_config_t g_config = 37 | TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); 38 | static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); 39 | static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); 40 | 41 | static void twai_receive_task(void *arg) { 42 | twai_message_t rx_msg; 43 | while (1) { 44 | twai_receive(&rx_msg, portMAX_DELAY); 45 | Serial.printf("received identifier: 0x%02X\r\n", rx_msg.identifier); 46 | // rx_msg.data 47 | Serial.print("received data: "); 48 | for (int i = 0; i < rx_msg.data_length_code; i++) { 49 | Serial.printf("0x%02X ", rx_msg.data[i]); 50 | } 51 | Serial.println(); 52 | vTaskDelay(pdMS_TO_TICKS(100)); 53 | } 54 | vTaskDelete(NULL); 55 | } 56 | 57 | void setup() { 58 | auto cfg = M5.config(); 59 | AtomS3.begin(cfg); 60 | 61 | AtomS3.Display.setTextColor(GREEN); 62 | AtomS3.Display.setTextDatum(middle_center); 63 | AtomS3.Display.setFont(&fonts::FreeSansBold9pt7b); 64 | AtomS3.Display.setTextSize(1); 65 | 66 | AtomS3.Display.drawString("CAN Slave", AtomS3.Display.width() / 2, 67 | AtomS3.Display.height() / 2); 68 | 69 | ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); 70 | Serial.println("Driver installed"); 71 | ESP_ERROR_CHECK(twai_start()); 72 | xTaskCreatePinnedToCore(twai_receive_task, "twai_receive_task", 4096, NULL, 73 | RX_TASK_PRIO, NULL, tskNO_AFFINITY); 74 | } 75 | 76 | void loop() { 77 | } -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSmartConfig/WiFiSmartConfig.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: WIFI Smart Config. WIFI智能配网 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | * Fill in WIFI configuration information through mobile APP to connect M5AtomS3 13 | *to relevant WIFI 通过手机APP填写WIFI配置信息使 M5AtomS3连接至相关WIFI APP 14 | *Download Address: 15 | *https://www.espressif.com/en/products/software/esp-touch/resources APP下载地址: 16 | *https://www.espressif.com/zh-hans/products/software/esp-touch/resources 17 | */ 18 | #include 19 | #include "WiFi.h" 20 | 21 | void setup() { 22 | AtomS3.begin(); // Init M5AtomS3. 初始化 M5AtomS3 23 | WiFi.mode(WIFI_AP_STA); // Set the wifi mode to the mode compatible with 24 | // the AP and Station, and start intelligent 25 | // network configuration 26 | WiFi.beginSmartConfig(); // 设置wifi模式为AP 与 Station 27 | // 兼容模式,并开始智能配网 28 | 29 | // Wait for the M5AtomS3 to receive network information from the phone 30 | // 等待M5AtomS3接收到来自手机的配网信息 31 | AtomS3.Lcd.println( 32 | "\nWaiting for Phone SmartConfig."); // Screen print format string. 33 | // 屏幕打印格式化字符串 34 | while (!WiFi.smartConfigDone()) { // If the smart network is not completed. 35 | // 若智能配网没有完成 36 | delay(500); 37 | AtomS3.Lcd.print("."); 38 | } 39 | AtomS3.Lcd.println("\nSmartConfig received."); 40 | AtomS3.Lcd.println("Waiting for WiFi"); 41 | while ( 42 | WiFi.status() != 43 | WL_CONNECTED) { // M5AtomS3 will connect automatically upon receipt of 44 | // the configuration information, and return true if 45 | // the connection is successful. 46 | // 收到配网信息后M5AtomS3将自动连接,若连接成功将返回true 47 | delay(500); 48 | AtomS3.Lcd.print("."); 49 | } 50 | AtomS3.Lcd.print("\nWiFi Connect To: "); 51 | AtomS3.Lcd.println(WiFi.SSID()); // Output Network name. 输出网络名称 52 | AtomS3.Lcd.print("IP address: "); 53 | AtomS3.Lcd.println(WiFi.localIP()); // Output IP Address. 输出IP地址 54 | AtomS3.Lcd.print("RSSI: "); 55 | AtomS3.Lcd.println(WiFi.RSSI()); // Output signal strength. 输出信号强度 56 | } 57 | 58 | void loop() { 59 | } -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicMotion/AtomicMotion.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtomicMotion.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic Motion Test 5 | * @version 0.1 6 | * @date 2024-01-24 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic Motion 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * M5AtomicMotion: https://github.com/m5stack/M5Atomic-Motion 16 | */ 17 | 18 | #include "M5AtomS3.h" 19 | #include "M5AtomicMotion.h" 20 | 21 | M5AtomicMotion AtomicMotion; 22 | 23 | void setup() { 24 | auto cfg = M5.config(); 25 | AtomS3.begin(cfg); 26 | 27 | AtomS3.Display.setTextColor(GREEN); 28 | AtomS3.Display.setTextDatum(middle_center); 29 | AtomS3.Display.setTextSize(2); 30 | AtomS3.Display.drawString("Atomic Init", AtomS3.Display.width() / 2, 31 | AtomS3.Display.height() / 2); 32 | while ( 33 | !AtomicMotion.begin(&Wire, M5_ATOMIC_MOTION_I2C_ADDR, 38, 39, 100000)) { 34 | AtomS3.Display.clear(); 35 | AtomS3.Display.drawString("Init Fail", AtomS3.Display.width() / 2, 36 | AtomS3.Display.height() / 2); 37 | Serial.println("Atomic Motion begin failed"); 38 | delay(1000); 39 | } 40 | AtomS3.Display.clear(); 41 | AtomS3.Display.drawString("Motion", AtomS3.Display.width() / 2, 42 | AtomS3.Display.height() / 2); 43 | 44 | Serial.println("Atomic Motion Test"); 45 | } 46 | 47 | void loop() { 48 | for (int ch = 0; ch < 2; ch++) { 49 | AtomicMotion.setMotorSpeed(ch, 127); 50 | Serial.printf("Motor Channel %d: %d \n", ch, 51 | AtomicMotion.getMotorSpeed(ch)); 52 | } 53 | delay(1000); 54 | for (int ch = 0; ch < 2; ch++) { 55 | AtomicMotion.setMotorSpeed(ch, -127); 56 | Serial.printf("Motor Channel %d: %d \n", ch, 57 | AtomicMotion.getMotorSpeed(ch)); 58 | } 59 | delay(1000); 60 | for (int ch = 0; ch < 2; ch++) { 61 | AtomicMotion.setMotorSpeed(ch, 0); 62 | Serial.printf("Motor Channel %d: %d \n", ch, 63 | AtomicMotion.getMotorSpeed(ch)); 64 | } 65 | delay(1000); 66 | for (int ch = 0; ch < 4; ch++) { 67 | AtomicMotion.setServoAngle(ch, 180); 68 | Serial.printf("Servo Channel %d: %d \n", ch, 69 | AtomicMotion.getServoAngle(ch)); 70 | } 71 | delay(1000); 72 | for (int ch = 0; ch < 4; ch++) { 73 | AtomicMotion.setServoAngle(ch, 0); 74 | Serial.printf("Servo Channel %d: %d \n", ch, 75 | AtomicMotion.getServoAngle(ch)); 76 | } 77 | delay(1000); 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚫 Deprecated — Use M5GFX & M5Unified 2 | 3 | - **M5GFX** 4 | High-performance, lightweight graphics and display driver library for M5 devices. 5 | 6 | 7 | - **M5Unified** 8 | Unified base library for M5 devices (IO/peripherals, power management, audio, etc.). 9 | 10 | 11 | # AtomS3 Library 12 | 13 | [![Arduino Compile](https://github.com/m5stack/M5AtomS3/actions/workflows/arduino-action-compile.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/arduino-action-compile.yml) 14 | [![Arduino Lint](https://github.com/m5stack/M5AtomS3/actions/workflows/Arduino-Lint-Check.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/Arduino-Lint-Check.yml) 15 | [![Clang Format](https://github.com/m5stack/M5AtomS3/actions/workflows/clang-format-check.yml/badge.svg)](https://github.com/m5stack/M5AtomS3/actions/workflows/clang-format-check.yml) 16 | 17 | English | [中文](README_cn.md) 18 | 19 | M5Atom Lite 20 | 21 | * **For the Detailed documentation of AtomS3, please [Click here](https://docs.m5stack.com/en/core/AtomS3)** 22 | 23 | ## Description 24 | 25 | `AtomS3` is a highly integrated programmable controller based on `ESP32-S3` main control, which integrates ESP32-S3 main control, integrated `WiFi` function, and `8M` on-chip FLASH; `0.85` inch `IPS screen`; `Programmable button function` at the bottom of the screen; Built-in 5V to 3.3V circuit, 6-axis gyro sensor `MPU6886`, on-board `Type-C` interface, power supply and firmware download, one `HY2.0-4P` expansion port, `6 GPIOs and power pins` reserved at the bottom to facilitate expansion applications. The product size is only `24*24mm`, which is suitable for various `embedded smart device applications`. 26 | 27 | - `Reminder: If you need to burn the firmware, please press and hold the reset button (about 2 seconds) until the internal green LED lights up, then you can release it, at this time the device has entered download mode and waited for burning.` 28 | 29 | ## Applications 30 | 31 | - Internet of things terminal controller 32 | - IoT node 33 | - Wearable peripherals 34 | 35 | ## PlatformIO Compilation 36 | 37 | [Compilation File](https://github.com/m5stack/M5AtomS3/blob/main/platformio.ini) 38 | 39 | ## Schematic 40 | 41 | schematics 42 | 43 | ## Module Size 44 | 45 | module size 46 | 47 | ## More Information 48 | 49 | **Arduino IDE Development**: [Click Here](https://docs.m5stack.com/en/quick_start/atoms3/arduino) 50 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicSPK/PlayMP3FromSD/PlayMP3FromSD.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /** 8 | * @file PlayMP3FromSD.ino 9 | * @brief M5AtomS3 Atomic SPK SDCard MP3 Player 10 | * @version 0.1 11 | * @date 2024-11-22 12 | * 13 | * 14 | * @Hardwares: M5AtomS3 + Atomic SPK Base + MicroSD Card 15 | * @Platform Version: Arduino M5Stack Board Manager v2.1.2 16 | * @Dependent Library: 17 | * M5GFX: https://github.com/m5stack/M5GFX 18 | * M5Unified: https://github.com/m5stack/M5Unified 19 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 20 | * ESP8266Audio: https://github.com/earlephilhower/ESP8266Audio 21 | * MP3 file link: http://gitfile.oss-cn-beijing.aliyuncs.com/11-fanfare.mp3 22 | */ 23 | 24 | #include "M5AtomS3.h" 25 | #include 26 | 27 | #include "SPIFFS.h" 28 | #include "AudioFileSourceSD.h" 29 | #include "AudioGeneratorMP3.h" 30 | #include "AudioOutputI2S.h" 31 | #include "AudioFileSourceID3.h" 32 | 33 | AudioGeneratorMP3 *mp3; 34 | AudioFileSourceSD *file; 35 | AudioOutputI2S *out; 36 | AudioFileSourceID3 *id3; 37 | 38 | #define SCK 7 39 | #define MISO 8 40 | #define MOSI 6 41 | 42 | void StatusCallback(void *cbData, int code, const char *string) 43 | { 44 | const char *ptr = reinterpret_cast(cbData); 45 | char s1[64]; 46 | strncpy_P(s1, string, sizeof(s1)); 47 | s1[sizeof(s1) - 1] = 0; 48 | Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1); 49 | Serial.flush(); 50 | } 51 | 52 | void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string) 53 | { 54 | (void)cbData; 55 | Serial.printf("ID3 callback for: %s = '", type); 56 | 57 | if (isUnicode) { 58 | string += 2; 59 | } 60 | 61 | while (*string) { 62 | char a = *(string++); 63 | if (isUnicode) { 64 | string++; 65 | } 66 | Serial.printf("%c", a); 67 | } 68 | Serial.printf("'\n"); 69 | Serial.flush(); 70 | } 71 | 72 | void setup() 73 | { 74 | AtomS3.begin(); 75 | Serial.begin(115200); 76 | SPI.begin(SCK, MISO, MOSI, -1); 77 | if (!SD.begin()) { 78 | Serial.println("Card Mount Failed"); 79 | return; 80 | } 81 | audioLogger = &Serial; 82 | file = new AudioFileSourceSD("/11-fanfare.mp3"); 83 | id3 = new AudioFileSourceID3(file); 84 | id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG"); 85 | out = new AudioOutputI2S(); 86 | 87 | out->SetPinout(5, 39, 38); 88 | 89 | mp3 = new AudioGeneratorMP3(); 90 | mp3->RegisterStatusCB(StatusCallback, (void *)"mp3"); 91 | mp3->begin(id3, out); 92 | } 93 | 94 | void loop() 95 | { 96 | if (mp3->isRunning()) { 97 | if (!mp3->loop()) mp3->stop(); 98 | } else { 99 | Serial.printf("MP3 done\n"); 100 | delay(1000); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /examples/Advanced/Storage/SPIFFS/SPIFFS_Add/SPIFFS_Add.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: SPIFFS Add(Write is to clean up the contents of the file and write 10 | * it again.). 向SPIFFS中添加信息(write是将文件内容完全清除重新写) 11 | * Date: 2022/12/20 12 | ****************************************************************************** 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | String file_name = 19 | "/M5Stack/notes.txt"; // Sets the location and name of the file to be 20 | // operated on. 设置被操作的文件位置和名称 21 | bool SPIFFS_FORMAT = 22 | true; // Whether to initialize the SPIFFS. 是否初始化SPIFFS 23 | // You don't need to format the flash file system every time you use it. 24 | // 无需每次使用闪存都进行格式化 25 | 26 | void setup() { 27 | AtomS3.begin(); // Init M5Atom. 初始化 M5ATOM 28 | if (SPIFFS_FORMAT) { 29 | AtomS3.Lcd.println( 30 | "\nSPIFFS format start..."); // Serial port output format String. 31 | // 串口输出格式化字符串 32 | SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS 33 | AtomS3.Lcd.println("SPIFFS format finish"); 34 | } 35 | if (SPIFFS.begin()) { // Start SPIFFS, return 1 on success. 36 | // 启动闪存文件系统,若成功返回1 37 | AtomS3.Lcd.println("\nSPIFFS Started."); 38 | } else { 39 | AtomS3.Lcd.println("SPIFFS Failed to Start."); 40 | } 41 | 42 | if (SPIFFS.exists( 43 | file_name)) { // Check whether the file_name file exists in the 44 | // flash memory. 确认闪存中是否有file_name文件 45 | AtomS3.Lcd.println("FOUND."); 46 | AtomS3.Lcd.println(file_name); 47 | 48 | File dataFile = SPIFFS.open( 49 | file_name, 50 | "a"); // Create a File object dafaFile to add information to 51 | // file_name in the SPIFFS. 52 | // 建立File对象dafaFile用于向SPIFFS中的file_name添加信息 53 | dataFile.println( 54 | "This is Appended Info."); // Adds string information to dataFile. 55 | // 向dataFile添加字符串信息 56 | dataFile.close(); // Close the file when writing is complete. 57 | // 完成写入后关闭文件 58 | AtomS3.Lcd.println("Finished Appending data to SPIFFS"); 59 | } else { 60 | AtomS3.Lcd.println("NOT FOUND."); 61 | AtomS3.Lcd.print(file_name); 62 | AtomS3.Lcd.println("is creating."); 63 | File dataFile = SPIFFS.open( 64 | file_name, 65 | "w"); // Create aFile object dafaFile to write information to 66 | // file_name in the SPIFFS. 67 | // 建立File对象dafaFile用于向SPIFFS中的file_name写入信息 68 | dataFile.close(); // Close the file when writing is complete. 69 | // 完成写入后关闭文件 70 | AtomS3.Lcd.println("Please disable format and Reupload"); 71 | } 72 | } 73 | 74 | void loop() { 75 | } -------------------------------------------------------------------------------- /examples/Advanced/WIFI/mDNS_Find/mDNS_Find.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: announcing & finding services. 广播&查找服务 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | // Set the name and password of the wifi to be connected. 19 | // 配置所连接wifi的名称和密码 20 | const char* ssid = "your-ap"; 21 | const char* password = "your-password"; 22 | 23 | void browseService(const char* service, 24 | const char* proto) { // find devices. 查找设备 25 | AtomS3.Lcd.printf("Browsing for _%s._%s.local ", service, proto); 26 | int n = 27 | MDNS.queryService(service, proto); // Store the number of devices found 28 | // in n. 将找到的设备数存储在n中 29 | if (n == 0) { // if don't have any devices. 如果没有任何设备 30 | AtomS3.Lcd.println("no services found"); 31 | } else { 32 | AtomS3.Lcd.printf("%d service(s) found\n", n); 33 | for (int i = 0; i < n; 34 | ++i) { // Print details for each service found. 打印每个找到的设备 35 | AtomS3.Lcd.printf(" %d: ", i + 1); 36 | AtomS3.Lcd.print( 37 | MDNS.hostname(i)); // output the devies name. 输出设备名称 38 | AtomS3.Lcd.print(" "); 39 | AtomS3.Lcd.print( 40 | MDNS.IP(i)); // Output the devices IP Address. 输出设备的IP地址 41 | AtomS3.Lcd.printf( 42 | ":%d\n", 43 | MDNS.port(i)); // output the devices port. 输出设备的端口号 44 | } 45 | } 46 | AtomS3.Lcd.println(); 47 | } 48 | 49 | void setup() { 50 | AtomS3.begin(); // Init M5AtomS3. 初始化 M5AtomS3 51 | WiFi.begin(ssid, password); // Connect wifi and return connection status. 52 | // 连接wifi并返回连接状态 53 | AtomS3.Lcd.print("Connected to "); 54 | AtomS3.Lcd.print(ssid); // Output Network name. 输出网络名称 55 | while (WiFi.status() != 56 | WL_CONNECTED) { // If the wifi connection fails. 若wifi未连接成功 57 | delay(250); // delay 0.5s. 延迟0.5s 58 | AtomS3.Lcd.print("."); 59 | } 60 | AtomS3.Lcd.print("Success!\nIP address: "); 61 | AtomS3.Lcd.println(WiFi.localIP()); // Output IP Address. 输出IP地址 62 | 63 | if (!MDNS.begin("ESP32_Browser")) { // if init error. 如果初始化错误 64 | AtomS3.Lcd.println("Error setting up MDNS responder!"); 65 | while (1) { 66 | delay(1000); 67 | } 68 | } 69 | } 70 | 71 | void loop() { 72 | browseService("http", "tcp"); 73 | delay(1000); 74 | browseService("arduino", "tcp"); 75 | delay(1000); 76 | browseService("workstation", "tcp"); 77 | delay(3000); 78 | AtomS3.Lcd.clear(); 79 | AtomS3.Lcd.setCursor(0, 0); 80 | browseService("smb", "tcp"); 81 | delay(1000); 82 | browseService("afpovertcp", "tcp"); 83 | delay(1000); 84 | browseService("ftp", "tcp"); 85 | delay(1000); 86 | browseService("ipp", "tcp"); 87 | delay(1000); 88 | browseService("printer", "tcp"); 89 | delay(10000); 90 | } -------------------------------------------------------------------------------- /examples/Unit/MiniCAN_TJA1051T/Master/Master.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Slave.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief Unit Mini CAN Test Master 5 | * @version 0.1 6 | * @date 2024-02-01 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Unit Mini CAN 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include 18 | #include 19 | #include "freertos/FreeRTOS.h" 20 | #include "freertos/task.h" 21 | #include "freertos/queue.h" 22 | #include "freertos/semphr.h" 23 | #include "esp_err.h" 24 | #include "esp_log.h" 25 | #include "driver/twai.h" 26 | #include "esp_err.h" 27 | 28 | #include "M5AtomS3.h" 29 | 30 | /* --------------------- Definitions and static variables ------------------ */ 31 | 32 | #define TX_TASK_PRIO 8 // Receiving task priority 33 | #define TX_GPIO_NUM gpio_num_t(2) 34 | #define RX_GPIO_NUM gpio_num_t(1) 35 | 36 | static const twai_general_config_t g_config = 37 | TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); 38 | static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); 39 | static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); 40 | 41 | #define ID_SLAVE_1 0x0B1 42 | 43 | static const twai_message_t slave_1_on = {.identifier = ID_SLAVE_1, 44 | .data_length_code = 8, 45 | .data = {1, 2, 3, 4, 5, 6, 7, 8}}; 46 | 47 | static const twai_message_t slave_1_off = {.identifier = ID_SLAVE_1, 48 | .data_length_code = 8, 49 | .data = {0, 0, 0, 0, 0, 0, 0, 0}}; 50 | 51 | static void twai_transmit_task(void *arg) { 52 | while (1) { 53 | twai_transmit(&slave_1_on, portMAX_DELAY); 54 | Serial.printf("send cmd on to 0x%02X\r\n", ID_SLAVE_1); 55 | Serial.print("send data: "); 56 | for (int i = 0; i < slave_1_on.data_length_code; i++) { 57 | Serial.printf("0x%02X ", slave_1_on.data[i]); 58 | } 59 | Serial.println(); 60 | 61 | vTaskDelay(pdMS_TO_TICKS(1000)); 62 | 63 | twai_transmit(&slave_1_off, portMAX_DELAY); 64 | Serial.printf("send cmd off to 0x%02X\r\n", ID_SLAVE_1); 65 | Serial.print("send data: "); 66 | for (int i = 0; i < slave_1_off.data_length_code; i++) { 67 | Serial.printf("0x%02X ", slave_1_off.data[i]); 68 | } 69 | Serial.println(); 70 | vTaskDelay(pdMS_TO_TICKS(1000)); 71 | } 72 | vTaskDelete(NULL); 73 | } 74 | 75 | void setup() { 76 | auto cfg = M5.config(); 77 | AtomS3.begin(cfg); 78 | 79 | AtomS3.Display.setTextColor(GREEN); 80 | AtomS3.Display.setTextDatum(middle_center); 81 | AtomS3.Display.setFont(&fonts::FreeSansBold9pt7b); 82 | AtomS3.Display.setTextSize(1); 83 | 84 | AtomS3.Display.drawString("CAN Master", AtomS3.Display.width() / 2, 85 | AtomS3.Display.height() / 2); 86 | 87 | ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); 88 | Serial.println("Driver installed"); 89 | ESP_ERROR_CHECK(twai_start()); 90 | xTaskCreatePinnedToCore(twai_transmit_task, "twai_transmit_task", 4096, 91 | NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); 92 | } 93 | 94 | void loop() { 95 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | # Source: 2 | # https://github.com/Tinyu-Zhao/M5-Depends/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml 3 | # See: 4 | # https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms 5 | 6 | name: Bug report 7 | description: Report a problem with the code in this repository. 8 | labels: 9 | - "type: bug" 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: "Thank you for opening an issue on an M5Stack Arduino library repository.\n\ 14 | To improve the speed of resolution please review the following guidelines and common troubleshooting steps below before creating the issue:\n\ 15 | If you have any UIFLOW questions you can ask in the [special board](https://community.m5stack.com/category/5/uiflow), there will be many enthusiastic friends to help you.\n\ 16 | Do not use GitHub issues for troubleshooting projects and issues. Instead use the forums at https://community.m5stack.com to ask questions and troubleshoot why something isn't working as expected. In many cases the problem is a common issue that you will more quickly receive help from the forum community. GitHub issues are meant for known defects in the code. If you don't know if there is a defect in the code then start with troubleshooting on the forum first." 17 | - type: textarea 18 | id: description 19 | attributes: 20 | label: Describe the bug 21 | description: A clear and concise description of what the bug is. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: reproduce 26 | attributes: 27 | label: To reproduce 28 | description: Provide the specific set of steps we can follow to reproduce the problem. 29 | placeholder: | 30 | 1. In this environment... 31 | 2. With this config... 32 | 3. Run '...' 33 | 4. See error... 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: expected 38 | attributes: 39 | label: Expected behavior 40 | description: What would you expect to happen after following those instructions? 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: screenshots 45 | attributes: 46 | label: Screenshots 47 | description: If applicable, add screenshots to help explain your problem. 48 | validations: 49 | required: false 50 | - type: textarea 51 | id: information 52 | attributes: 53 | label: Environment 54 | description: | 55 | If applicable, add screenshots to help explain your problem. 56 | examples: 57 | - **OS**: Ubuntu 20.04 58 | - **IDE & IDE Version**: Arduino 1.8.19 Or Platform IO v2.5.0 59 | - **Repository Version**: 0.4.0 60 | value: | 61 | - OS: 62 | - IDE &IDE Version: 63 | - Repository Version: 64 | validations: 65 | required: false 66 | - type: textarea 67 | id: additional 68 | attributes: 69 | label: Additional context 70 | description: Add any additional information here. 71 | validations: 72 | required: false 73 | - type: checkboxes 74 | id: checklist 75 | attributes: 76 | label: Issue checklist 77 | description: Please double-check that you have done each of the following things before submitting the issue. 78 | options: 79 | - label: I searched for previous reports in [the issue tracker](https://github.com/m5stack/M5Stack/issues?q=) 80 | required: true 81 | - label: My report contains all necessary details 82 | required: true 83 | -------------------------------------------------------------------------------- /examples/Advanced/Storage/EEPROM/EEPROM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: EEPROM Read & Write. 10 | * Date: 2022/12/20 11 | ******************************************************************************* 12 | The values stored in the EEPROM will remain in the EEPROM even after the 13 | AtomS3 is disconnected. When a new program is uploaded to the AtomS3, the values 14 | stored in the EEPROM can still be called or modified by the new program. 15 | 储存于EEPROM的数值即使在断开 AtomS3电源后仍会保存在EEPROM中 16 | 当新程序上传到 AtomS3后,储存于EEPROM中的数值仍然可以被新的程序调用或者修改 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | int addr = 0; // EEPROM Start number of an ADDRESS. EEPROM地址起始编号 23 | #define SIZE 16 // define the size of EEPROM(Byte). 定义EEPROM的大小(字节) 24 | 25 | void setup() { 26 | AtomS3.begin(); // Init Atom. 初始化Atom 27 | if (!EEPROM.begin(SIZE)) { // Request storage of SIZE size(success return 28 | // 1). 申请SIZE大小的存储(成功返回1) 29 | AtomS3.Lcd.println( 30 | "\nFailed to initialise EEPROM!"); // 串口输出格式化字符串. Serial 31 | // output format string 32 | delay(1000000); 33 | } 34 | AtomS3.Lcd.println("\nRead data from EEPROM. Values are:"); 35 | for (int i = 0; i < SIZE; i++) { 36 | AtomS3.Lcd.printf( 37 | "%d ", 38 | EEPROM.read(i)); // Reads data from 0 to SIZE in EEPROM. 39 | // 读取EEPROM中从0到SIZE中的数据 40 | } 41 | AtomS3.Lcd.println("\n\nPress Btn to Write EEPROM"); 42 | } 43 | 44 | void loop() { 45 | AtomS3.update(); // Check button down state. 检测按键按下状态 46 | if (AtomS3.BtnA.isPressed()) { // if the button is Pressed. 如果按键按下 47 | AtomS3.Lcd.setCursor(0, 0); 48 | AtomS3.Lcd.fillRect(0, 0, 128, 128, BLACK); 49 | AtomS3.Lcd.printf("\n%d Bytes datas written on EEPROM.\nValues are:\n", 50 | SIZE); 51 | for (int i = 0; i < SIZE; i++) { 52 | int val = random( 53 | 256); // Integer values to be stored in the EEPROM (EEPROM can 54 | // store one byte per memory address, and can only store 55 | // numbers from 0 to 255. Therefore, if you want to use 56 | // EEPROM to store the numeric value read by the analog 57 | // input pin, divide the numeric value by 4. 58 | // 将要存储于EEPROM的整数数值(EEPROM每一个存储地址可以储存一个字节,只能存储0-255的数.故如果要使用EEPROM存储模拟输入引脚所读取到的数值需要将该数值除以4) 59 | EEPROM.write(addr, 60 | val); // Writes the specified data to the specified 61 | // address. 向指定地址写入指定数据 62 | AtomS3.Lcd.printf("%d ", val); 63 | addr += 1; // Go to the next storage address. 转入下一存储地址 64 | } 65 | // When the storage address sequence number reaches the end of the 66 | // storage space of the EEPROM, return to. 67 | // 当存储地址序列号达到EEPROM的存储空间结尾,返回到EEPROM开始地址 68 | addr = 0; 69 | AtomS3.Lcd.println("\n\nRead form EEPROM. Values are:"); 70 | for (int i = 0; i < SIZE; i++) { 71 | AtomS3.Lcd.printf("%d ", EEPROM.read(i)); 72 | } 73 | AtomS3.Lcd.println("\n-------------------------------------\n"); 74 | } 75 | delay(150); 76 | } -------------------------------------------------------------------------------- /examples/Advanced/Storage/SPIFFS/SPIFFS/SPIFFS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: SPIFFS write & read. 闪存文件读写 10 | * Date: 2022/12/20 11 | ****************************************************************************** 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | String file_name = 18 | "/M5Stack/notes.txt"; // Sets the location and name of the file to be 19 | // operated on. 设置被操作的文件位置和名称 20 | bool SPIFFS_FORMAT = 21 | false; // Whether to initialize the SPIFFS. 是否初始化SPIFFS 22 | // You don't need to format the flash file system every time you use it. 23 | // 无需每次使用闪存都进行格式化 24 | 25 | void setup() { 26 | AtomS3.begin(); // Init M5Atom. 初始化 M5ATOM 27 | if (SPIFFS_FORMAT) { 28 | AtomS3.Lcd.println( 29 | "\nSPIFFS format start..."); // Serial port output format String. 30 | // 串口输出格式化字符串 31 | SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS 32 | AtomS3.Lcd.println("SPIFFS format finish"); 33 | } 34 | 35 | if (SPIFFS.begin()) { // Start SPIFFS, return 1 on success. 36 | // 启动闪存文件系统,若成功返回1 37 | AtomS3.Lcd.println("SPIFFS Begin."); 38 | // Write operation 39 | File dataFile = SPIFFS.open( 40 | file_name, 41 | "w"); // Create aFile object dafaFile to write information to 42 | // file_name in the SPIFFS. 43 | // 建立File对象dafaFile用于向SPIFFS中的file_name写入信息 44 | dataFile.println("Hello IOT World."); // Writes string information and 45 | // newlines to the dataFile. 46 | // 向dataFile写入字符串信息并换行 47 | dataFile.close(); // Close the file when writing is complete. 48 | // 完成写入后关闭文件 49 | AtomS3.Lcd.println("Finished Writing data to SPIFFS"); 50 | } else { 51 | AtomS3.Lcd.println( 52 | "SPIFFS Failed to Begin.\nYou need to Run SPIFFS_Add.ino first"); 53 | } 54 | } 55 | 56 | void loop() { 57 | AtomS3.update(); // Check whether the key is pressed. 检测按键是否按下 58 | if (AtomS3.BtnA.isPressed()) { // If the button is pressed. 如果按键按下 59 | if (SPIFFS.exists( 60 | file_name)) { // Check whether the file_name file exists in the 61 | // flash memory. 确认闪存中是否有file_name文件 62 | AtomS3.Lcd.println("FOUND."); 63 | AtomS3.Lcd.println(file_name); 64 | } else { 65 | AtomS3.Lcd.println("NOT FOUND."); 66 | AtomS3.Lcd.println(file_name); 67 | } 68 | File dataFile = SPIFFS.open( 69 | file_name, 70 | "r"); // Create a File object dafaFile to read information to 71 | // file_name in the SPIFFS. 72 | // 建立File对象dafaFile用于向SPIFFS中的file_name读取信息 73 | for (int i = 0; i < dataFile.size(); 74 | i++) { // Reads file contents and outputs file information through 75 | // the serial port monitor. 76 | // 读取文件内容并且通过串口监视器输出文件信息 77 | AtomS3.Lcd.print((char)dataFile.read()); 78 | } 79 | dataFile.close(); // Close the file after reading the file. 80 | // 完成文件读取后关闭文件 81 | delay(200); 82 | } 83 | } -------------------------------------------------------------------------------- /.github/workflows/arduino-action-compile.yml: -------------------------------------------------------------------------------- 1 | # arduino-test-compile-ActionTest.yml 2 | # Github workflow script for testing the arduino-test-compile action development. 3 | # 4 | # Copyright (C) 2020 Armin Joachimsmeyer 5 | # https://github.com/ArminJo/Github-Actions 6 | # License: MIT 7 | # 8 | 9 | # This is the name of the workflow, visible on GitHub UI. 10 | name: Arduino Compile 11 | on: 12 | push: # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request 13 | paths: 14 | - '**.ino' 15 | - '**.cpp' 16 | - '**.h' 17 | - 'arduino-test-compile.sh' 18 | - '*.yml' 19 | workflow_dispatch: 20 | jobs: 21 | build: 22 | name: ${{ matrix.arduino-boards-fqbn }} - test compiling examples 23 | 24 | runs-on: ubuntu-latest # ubuntu-latest # I picked Ubuntu to use shell scripts. 25 | 26 | env: 27 | # Comma separated list without double quotes around the list. 28 | CLI_VERSION: latest 29 | 30 | strategy: 31 | matrix: 32 | # The matrix will produce one job for each configuration parameter of type `arduino-boards-fqbn` 33 | # In the Arduino IDE, the fqbn is printed in the first line of the verbose output for compilation as parameter -fqbn=... for the "arduino-builder -dump-prefs" command 34 | # 35 | # Examples: arduino:avr:uno, arduino:avr:leonardo, arduino:avr:nano, arduino:avr:mega 36 | # arduino:sam:arduino_due_x, arduino:samd:arduino_zero_native" 37 | # ATTinyCore:avr:attinyx5:chip=85,clock=1internal, digistump:avr:digispark-tiny, digistump:avr:digispark-pro 38 | # STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 39 | # esp8266:esp8266:huzzah:eesz=4M3M,xtal=80, esp32:esp32:featheresp32:FlashFreq=80 40 | # You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace 41 | ############################################################################################################# 42 | arduino-boards-fqbn: 43 | - m5stack:esp32:m5stack_atoms3 44 | 45 | # Specify parameters for each board. 46 | ############################################################################################################# 47 | include: 48 | - arduino-boards-fqbn: m5stack:esp32:m5stack_atoms3 49 | platform-url: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json 50 | sketches-exclude: WhistleSwitch,50Hz,SimpleFrequencyDetector 51 | 52 | # Do not cancel all jobs / architectures if one job fails 53 | fail-fast: false 54 | 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v3 58 | 59 | - name: Checkout custom library 60 | uses: actions/checkout@v3 61 | with: 62 | repository: ArminJo/ATtinySerialOut 63 | ref: master 64 | path: CustomLibrary # must match the pattern *Custom* 65 | 66 | # Test of the arduino-test-compile action 67 | - name: Compile all examples using the arduino-test-compile action 68 | # uses: ArminJo/arduino-test-compile@master 69 | uses: ./.github/actions 70 | with: 71 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 72 | arduino-platform: ${{ matrix.arduino-platform }} 73 | platform-url: ${{ matrix.platform-url }} 74 | required-libraries: ${{ env.REQUIRED_LIBRARIES }} 75 | sketches-exclude: ${{ matrix.sketches-exclude }} 76 | build-properties: ${{ toJson(matrix.build-properties) }} 77 | sketch-names: "*.ino" 78 | sketch-names-find-start: "examples/*" 79 | debug-compile: true 80 | debug-install: true 81 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiTCP/WiFiTCP.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: WIFI TCP. 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | M5AtomS3 will sends a message to a TCP server 13 | M5AtomS3 将向TCP服务器发送一条数据 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | // Set the name and password of the wifi to be connected. 21 | // 配置所连接wifi的名称和密码 22 | const char* ssid = "your-ap"; 23 | const char* password = "your-password"; 24 | WiFiMulti WiFiMulti; 25 | 26 | void setup() { 27 | int sum = 0; 28 | AtomS3.begin(); // Init M5AtomS3. 初始化M5AtomS3 29 | WiFiMulti.addAP( 30 | ssid, 31 | password); // Add wifi configuration information. 添加wifi配置信息 32 | AtomS3.Lcd.printf( 33 | "Waiting connect to WiFi: %s ...", 34 | ssid); // Serial port output format string. 串口输出格式化字符串 35 | while (WiFiMulti.run() != 36 | WL_CONNECTED) { // If the connection to wifi is not established 37 | // successfully. 如果没有与wifi成功建立连接 38 | AtomS3.Lcd.print("."); 39 | delay(1000); 40 | sum += 1; 41 | if (sum == 8) AtomS3.Lcd.print("Conncet failed!"); 42 | } 43 | AtomS3.Lcd.println("\nWiFi connected"); 44 | AtomS3.Lcd.print("IP address: "); 45 | AtomS3.Lcd.println( 46 | WiFi.localIP()); // The serial port outputs the IP address 47 | // of the M5AtomS3. 串口输出M5AtomS3的IP地址 48 | delay(500); 49 | } 50 | 51 | void loop() { 52 | AtomS3.Lcd.setCursor(0, 40); 53 | const char* host = "www.baidu.com"; // Set the IP address or DNS of the TCP 54 | // server. 设置TCP服务器的ip或dns 55 | const uint16_t port = 56 | 80; // The port of the TCP server is specified. 设置TCP服务器的端口 57 | AtomS3.Lcd.printf("Connecting to: %s\n", host); 58 | 59 | WiFiClient client; 60 | if (!client.connect( 61 | host, 62 | port)) { // Connect to the server. 0 is returned if the 63 | // connection fails. 连接服务器,若连接失败返回0 64 | AtomS3.Lcd.print( 65 | "Connection failed.\nWaiting 5 seconds before retrying...\n"); 66 | delay(5000); 67 | return; 68 | } 69 | // send an arbitrary string to the server. 发送一个字符串到上边连接的服务器 70 | client.print("Send this data to the server"); 71 | // send a basic document request to the server. 72 | // 向服务器发送一个基本的文档请求. 73 | client.print("GET /index.html HTTP/1.1\n\n"); 74 | int maxloops = 0; 75 | 76 | // wait for the server's reply to become available 77 | // 等待服务器的回复 78 | while (!client.available() && maxloops < 1000) { 79 | maxloops++; 80 | delay(1); // delay 1 msec 81 | } 82 | if (client.available() > 83 | 0) { // Detects whether data is received. 检测是否接收到数据 84 | String line = client.readStringUntil( 85 | '\r'); // Read information from data received by the device until 86 | // \r is read. 从设备接收到的数据中读取信息,直至读取到\r时 87 | AtomS3.Lcd.println(line); // String received by serial port output. 88 | // 串口输出接收到的字符串 89 | } else { 90 | AtomS3.Lcd.println("client.available() timed out "); 91 | } 92 | AtomS3.Lcd.println("Closing connection."); 93 | client.stop(); 94 | AtomS3.Lcd.println("Waiting 5 seconds before restarting..."); 95 | delay(5000); 96 | AtomS3.Lcd.fillRect(0, 40, 320, 220, BLACK); 97 | } 98 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicPoE/HTTP/HTTP.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file HTTP.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic PoE Base HTTP Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic PoE Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * M5_Ethernet: https://github.com/m5stack/M5-Ethernet 16 | * ArduinoHttpClient: https://github.com/arduino-libraries/ArduinoHttpClient 17 | */ 18 | 19 | #include "M5AtomS3.h" 20 | #include 21 | #include 22 | #include 23 | 24 | #define SERVER "httpbin.org" 25 | 26 | #define SCK 5 27 | #define MISO 7 28 | #define MOSI 8 29 | #define CS 6 30 | 31 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x99}; 32 | 33 | EthernetClient ethClient; 34 | 35 | HttpClient client = HttpClient(ethClient, SERVER); 36 | 37 | void setup() { 38 | auto cfg = M5.config(); 39 | AtomS3.begin(cfg); 40 | 41 | AtomS3.Display.setTextColor(GREEN); 42 | AtomS3.Display.setTextDatum(middle_center); 43 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 44 | AtomS3.Display.setTextSize(1); 45 | 46 | SPI.begin(SCK, MISO, MOSI, -1); 47 | Ethernet.init(CS); 48 | 49 | AtomS3.Display.drawString("Init...", AtomS3.Display.width() / 2, 60); 50 | 51 | while (Ethernet.begin(mac) != 1) { 52 | Serial.println("Error getting IP address via DHCP, trying again..."); 53 | delay(2000); 54 | } 55 | 56 | // Check for Ethernet hardware present 57 | if (Ethernet.hardwareStatus() == EthernetNoHardware) { 58 | Serial.println( 59 | "Ethernet shield was not found. Sorry, can't run without " 60 | "hardware. :("); 61 | while (true) { 62 | delay(1); // do nothing, no point running without Ethernet hardware 63 | } 64 | } 65 | if (Ethernet.linkStatus() == LinkOFF) { 66 | Serial.println("Ethernet cable is not connected."); 67 | } 68 | } 69 | 70 | void loop() { 71 | AtomS3.Display.clear(); 72 | AtomS3.Display.drawString("GET", AtomS3.Display.width() / 2, 20); 73 | Serial.println("making GET request"); 74 | 75 | client.get("/get"); 76 | // read the status code and body of the response 77 | int statusCode = client.responseStatusCode(); 78 | String response = client.responseBody(); 79 | 80 | Serial.print("Status code: "); 81 | Serial.println(statusCode); 82 | Serial.print("Response: "); 83 | Serial.println(response); 84 | Serial.println("Wait five seconds"); 85 | 86 | AtomS3.Display.drawString("STATUS:", AtomS3.Display.width() / 2, 60); 87 | AtomS3.Display.drawString(String(statusCode), AtomS3.Display.width() / 2, 88 | 100); 89 | 90 | delay(5000); 91 | 92 | AtomS3.Display.clear(); 93 | 94 | AtomS3.Display.drawString("POST", AtomS3.Display.width() / 2, 20); 95 | 96 | Serial.println("making POST request"); 97 | 98 | String contentType = "application/x-www-form-urlencoded"; 99 | String postData = "name=Alice&age=12"; 100 | 101 | client.post("/post", contentType, postData); 102 | 103 | // read the status code and body of the response 104 | statusCode = client.responseStatusCode(); 105 | response = client.responseBody(); 106 | 107 | Serial.print("Status code: "); 108 | Serial.println(statusCode); 109 | Serial.print("Response: "); 110 | Serial.println(response); 111 | Serial.println("Wait five seconds"); 112 | 113 | AtomS3.Display.drawString("STATUS:", AtomS3.Display.width() / 2, 60); 114 | AtomS3.Display.drawString(String(statusCode), AtomS3.Display.width() / 2, 115 | 100); 116 | 117 | delay(5000); 118 | } 119 | -------------------------------------------------------------------------------- /examples/Advanced/MQTT/MQTT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2021 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: MQTT. 10 | * Date: 2022/12/19 11 | ******************************************************************************* 12 | */ 13 | #include "M5AtomS3.h" 14 | #include 15 | #include 16 | 17 | WiFiClient espClient; 18 | PubSubClient client(espClient); 19 | 20 | // Configure the name and password of the connected wifi and your MQTT Serve 21 | // host. 配置所连接wifi的名称、密码以及你MQTT服务器域名 22 | const char* ssid = "WIFI_SSID"; 23 | const char* password = "WIFI_PASSWORD"; 24 | const char* mqtt_server = "mqtt.m5stack.com"; 25 | 26 | unsigned long lastMsg = 0; 27 | #define MSG_BUFFER_SIZE (50) 28 | char msg[MSG_BUFFER_SIZE]; 29 | int value = 0; 30 | 31 | void setupWifi(); 32 | void callback(char* topic, byte* payload, unsigned int length); 33 | void reConnect(); 34 | 35 | void setup() { 36 | AtomS3.begin(); 37 | setupWifi(); 38 | client.setServer(mqtt_server, 39 | 1883); // Sets the server details. 配置所连接的服务器 40 | client.setCallback( 41 | callback); // Sets the message callback function. 设置消息回调函数 42 | } 43 | 44 | void loop() { 45 | if (!client.connected()) { 46 | reConnect(); 47 | } 48 | client.loop(); // This function is called periodically to allow clients to 49 | // process incoming messages and maintain connections to the 50 | // server. 51 | // 定期调用此函数,以允许主机处理传入消息并保持与服务器的连接 52 | 53 | unsigned long now = 54 | millis(); // Obtain the host startup duration. 获取主机开机时长 55 | if (now - lastMsg > 2000) { 56 | lastMsg = now; 57 | ++value; 58 | snprintf(msg, MSG_BUFFER_SIZE, "hello world #%ld", 59 | value); // Format to the specified string and store it in MSG. 60 | // 格式化成指定字符串并存入msg中 61 | Serial.print("Publish message: "); 62 | Serial.println(msg); 63 | client.publish("M5Stack", msg); // Publishes a message to the specified 64 | // topic. 发送一条消息至指定话题 65 | } 66 | } 67 | 68 | void setupWifi() { 69 | delay(10); 70 | AtomS3.Lcd.print("Connecting to Network..."); 71 | Serial.printf("Connecting to %s", ssid); 72 | WiFi.mode( 73 | WIFI_STA); // Set the mode to WiFi station mode. 设置模式为WIFI站模式 74 | WiFi.begin(ssid, password); // Start Wifi connection. 开始wifi连接 75 | 76 | while (WiFi.status() != WL_CONNECTED) { 77 | delay(500); 78 | Serial.print("."); 79 | } 80 | Serial.printf("\nSuccess\n"); 81 | AtomS3.Lcd.println("Success"); 82 | AtomS3.Lcd.println("For communication information see serial port"); 83 | } 84 | 85 | void callback(char* topic, byte* payload, unsigned int length) { 86 | Serial.print("Message arrived ["); 87 | Serial.print(topic); 88 | Serial.print("] "); 89 | for (int i = 0; i < length; i++) { 90 | Serial.print((char)payload[i]); 91 | } 92 | Serial.println(); 93 | } 94 | 95 | void reConnect() { 96 | while (!client.connected()) { 97 | Serial.print("Attempting MQTT connection..."); 98 | // Create a random client ID. 创建一个随机的客户端ID 99 | String clientId = "M5Stack-"; 100 | clientId += String(random(0xffff), HEX); 101 | // Attempt to connect. 尝试重新连接 102 | if (client.connect(clientId.c_str())) { 103 | Serial.println("connected"); 104 | // Once connected, publish an announcement to the topic. 105 | // 一旦连接,发送一条消息至指定话题 106 | client.publish("M5Stack", "hello world"); 107 | // ... and resubscribe. 重新订阅话题 108 | client.subscribe("M5Stack"); 109 | } else { 110 | Serial.print("failed, rc="); 111 | Serial.print(client.state()); 112 | Serial.println("try again in 5 seconds"); 113 | delay(5000); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicPoE/MQTT/MQTT.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MQTT.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic PoE Base MQTT Test 5 | * @version 0.1 6 | * @date 6023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic PoE Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * M5_Ethernet: https://github.com/m5stack/M5-Ethernet 16 | * PubSubClient: https://github.com/knolleary/pubsubclient 17 | */ 18 | 19 | #include "M5AtomS3.h" 20 | #include 21 | #include 22 | #include 23 | 24 | #define PUB_INTERVAL 3000 25 | 26 | #define PUB_TOPIC "LAN_UPLINK" 27 | #define SUB_TOPIC "LAN_DOWNLINK" 28 | 29 | #define SCK 5 30 | #define MISO 7 31 | #define MOSI 8 32 | #define CS 6 33 | 34 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x89}; 35 | 36 | const char* mqtt_server = "broker.emqx.io"; 37 | 38 | EthernetClient ethClient; 39 | PubSubClient client(ethClient); 40 | 41 | void callback(char* topic, byte* payload, unsigned int length) { 42 | Serial.print("Message arrived ["); 43 | Serial.print(topic); 44 | Serial.print("] "); 45 | 46 | for (int i = 0; i < length; i++) { 47 | Serial.print((char)payload[i]); 48 | } 49 | Serial.println(); 50 | } 51 | 52 | void reconnect() { 53 | // Loop until we're reconnected 54 | while (!client.connected()) { 55 | Serial.print("Attempting MQTT connection..."); 56 | // Attempt to connect 57 | if (client.connect("arduinoClient")) { 58 | Serial.println("connected"); 59 | AtomS3.Display.clear(); 60 | AtomS3.Display.drawString("Connected!", AtomS3.Display.width() / 2, 61 | 60); 62 | 63 | // Once connected, publish an announcement... 64 | client.publish(PUB_TOPIC, "hello world"); 65 | // ... and resubscribe 66 | client.subscribe(SUB_TOPIC); 67 | } else { 68 | AtomS3.Display.clear(); 69 | AtomS3.Display.drawString("Failed!", AtomS3.Display.width() / 2, 70 | 60); 71 | Serial.print("failed, rc="); 72 | Serial.print(client.state()); 73 | Serial.println(" try again in 5 seconds"); 74 | // Wait 5 seconds before retrying 75 | delay(5000); 76 | } 77 | } 78 | } 79 | 80 | void setup() { 81 | auto cfg = M5.config(); 82 | AtomS3.begin(cfg); 83 | 84 | AtomS3.Display.setTextColor(GREEN); 85 | AtomS3.Display.setTextDatum(middle_center); 86 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 87 | AtomS3.Display.setTextSize(1); 88 | 89 | SPI.begin(SCK, MISO, MOSI, -1); 90 | Ethernet.init(CS); 91 | 92 | AtomS3.Display.drawString("Init...", AtomS3.Display.width() / 2, 60); 93 | 94 | while (Ethernet.begin(mac) != 1) { 95 | Serial.println("Error getting IP address via DHCP, trying again..."); 96 | delay(1000); 97 | } 98 | 99 | // Check for Ethernet hardware present 100 | if (Ethernet.hardwareStatus() == EthernetNoHardware) { 101 | Serial.println( 102 | "Ethernet shield was not found. Sorry, can't run without " 103 | "hardware. :("); 104 | while (true) { 105 | delay(1); // do nothing, no point running without Ethernet hardware 106 | } 107 | } 108 | if (Ethernet.linkStatus() == LinkOFF) { 109 | Serial.println("Ethernet cable is not connected."); 110 | } 111 | 112 | client.setServer(mqtt_server, 1883); 113 | client.setCallback(callback); 114 | } 115 | 116 | long lastTime; 117 | 118 | void loop() { 119 | if (!client.connected()) { 120 | reconnect(); 121 | } else { 122 | client.loop(); 123 | if (millis() - lastTime > PUB_INTERVAL) { 124 | lastTime = millis(); 125 | AtomS3.Display.clear(); 126 | AtomS3.Display.drawString("Publish!", AtomS3.Display.width() / 2, 127 | 60); 128 | String data = "hello world: " + String(millis()); 129 | client.publish(PUB_TOPIC, data.c_str()); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicPoE/WebServer/WebServer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WebServer.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic PoE Base WebServer Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic PoE Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * M5_Ethernet: https://github.com/m5stack/M5-Ethernet 16 | */ 17 | 18 | #include "M5AtomS3.h" 19 | #include 20 | #include 21 | 22 | #define SCK 5 23 | #define MISO 7 24 | #define MOSI 8 25 | #define CS 6 26 | 27 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x99}; 28 | IPAddress ip(192, 168, 1, 177); 29 | 30 | EthernetServer server(80); 31 | 32 | void setup() { 33 | auto cfg = M5.config(); 34 | AtomS3.begin(cfg); 35 | 36 | AtomS3.Display.setTextColor(GREEN); 37 | AtomS3.Display.setTextDatum(middle_center); 38 | AtomS3.Display.setFont(&fonts::Orbitron_Light_24); 39 | AtomS3.Display.setTextSize(1); 40 | 41 | SPI.begin(SCK, MISO, MOSI, -1); 42 | Ethernet.init(CS); 43 | 44 | AtomS3.Display.drawString("Init...", AtomS3.Display.width() / 2, 60); 45 | 46 | while (Ethernet.begin(mac) != 1) { 47 | Serial.println("Error getting IP address via DHCP, trying again..."); 48 | delay(1000); 49 | } 50 | 51 | // Check for Ethernet hardware present 52 | if (Ethernet.hardwareStatus() == EthernetNoHardware) { 53 | Serial.println( 54 | "Ethernet shield was not found. Sorry, can't run without " 55 | "hardware. :("); 56 | while (true) { 57 | delay(1); // do nothing, no point running without Ethernet hardware 58 | } 59 | } 60 | if (Ethernet.linkStatus() == LinkOFF) { 61 | Serial.println("Ethernet cable is not connected."); 62 | } 63 | 64 | // start the server 65 | server.begin(); 66 | Serial.print("server is at "); 67 | Serial.println(Ethernet.localIP()); 68 | AtomS3.Display.setTextSize(0.5); 69 | AtomS3.Display.clear(); 70 | AtomS3.Display.drawString(Ethernet.localIP().toString().c_str(), 71 | AtomS3.Display.width() / 2, 60); 72 | } 73 | 74 | long lastTime; 75 | 76 | void loop() { 77 | // listen for incoming clients 78 | EthernetClient client = server.available(); 79 | if (client) { 80 | Serial.println("new client"); 81 | // an http request ends with a blank line 82 | boolean currentLineIsBlank = true; 83 | while (client.connected()) { 84 | if (client.available()) { 85 | char c = client.read(); 86 | Serial.write(c); 87 | // if you've gotten to the end of the line (received a newline 88 | // character) and the line is blank, the http request has ended, 89 | // so you can send a reply 90 | if (c == '\n' && currentLineIsBlank) { 91 | // send a standard http response header 92 | client.println("HTTP/1.1 200 OK"); 93 | client.println("Content-Type: text/html"); 94 | client.println( 95 | "Connection: close"); // the connection will be closed 96 | // after completion of the 97 | // response 98 | client.println("Refresh: 5"); // refresh the page 99 | // automatically every 5 sec 100 | client.println(); 101 | client.println(""); 102 | client.println(""); 103 | client.print("

Hello M5Stack LAN Module!

"); 104 | client.println(""); 105 | break; 106 | } 107 | if (c == '\n') { 108 | // you're starting a new line 109 | currentLineIsBlank = true; 110 | } else if (c != '\r') { 111 | // you've gotten a character on the current line 112 | currentLineIsBlank = false; 113 | } 114 | } 115 | } 116 | // give the web browser time to receive the data 117 | delay(1); 118 | // close the connection: 119 | client.stop(); 120 | Serial.println("client disconnected"); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/AtomBase/AtomCan/AtomCan.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtomCan.ino 3 | * @author Tinyu (tinyu@m5stack.com) 4 | * @brief M5AtomS3 Atom Can Base Test 5 | * @version 0.1 6 | * @date 2024-06-21 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atom Can Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.1.0 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | #include 17 | #include "driver/twai.h" 18 | 19 | // Pins used to connect to CAN bus transceiver: 20 | #define RX_PIN 6 21 | #define TX_PIN 5 22 | 23 | // Interval: 24 | #define TRANSMIT_RATE_MS 1000 25 | 26 | #define POLLING_RATE_MS 1000 27 | 28 | #define ARDUINO_USB_CDC_ON_BOOT 1 29 | 30 | static bool driver_installed = false; 31 | 32 | unsigned long previousMillis = 0; // will store last time a message was send 33 | 34 | void setup() { 35 | auto cfg = M5.config(); 36 | AtomS3.begin(cfg); 37 | 38 | // Initialize configuration structures using macro initializers 39 | twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT( 40 | (gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL); 41 | twai_timing_config_t t_config = 42 | TWAI_TIMING_CONFIG_500KBITS(); // Look in the api-reference for other 43 | // speed sets. 44 | twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); 45 | 46 | // Install TWAI driver 47 | if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) { 48 | Serial.println("Driver installed"); 49 | } else { 50 | Serial.println("Failed to install driver"); 51 | return; 52 | } 53 | 54 | // Start TWAI driver 55 | if (twai_start() == ESP_OK) { 56 | Serial.println("Driver started"); 57 | } else { 58 | Serial.println("Failed to start driver"); 59 | return; 60 | } 61 | 62 | // Reconfigure alerts to detect TX alerts and Bus-Off errors 63 | uint32_t alerts_to_enable = TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | 64 | TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS | 65 | TWAI_ALERT_BUS_ERROR; 66 | if (twai_reconfigure_alerts(alerts_to_enable, NULL) == ESP_OK) { 67 | Serial.println("CAN Alerts reconfigured"); 68 | } else { 69 | Serial.println("Failed to reconfigure alerts"); 70 | return; 71 | } 72 | 73 | // TWAI driver is now successfully installed and started 74 | driver_installed = true; 75 | } 76 | 77 | static void send_message() { 78 | // Send message 79 | 80 | // Configure message to transmit 81 | twai_message_t message; 82 | message.identifier = 0x0F6; 83 | message.data_length_code = 4; 84 | for (int i = 0; i < 4; i++) { 85 | message.data[i] = 0; 86 | } 87 | 88 | // Queue message for transmission 89 | if (twai_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) { 90 | printf("Message queued for transmission\n"); 91 | } else { 92 | printf("Failed to queue message for transmission\n"); 93 | } 94 | } 95 | 96 | void loop() { 97 | if (!driver_installed) { 98 | delay(1000); 99 | return; 100 | } 101 | 102 | uint32_t alerts_triggered; 103 | twai_read_alerts(&alerts_triggered, pdMS_TO_TICKS(POLLING_RATE_MS)); 104 | twai_status_info_t twaistatus; 105 | twai_get_status_info(&twaistatus); 106 | 107 | if (alerts_triggered & TWAI_ALERT_ERR_PASS) { 108 | Serial.println("Alert: TWAI controller has become error passive."); 109 | } 110 | if (alerts_triggered & TWAI_ALERT_BUS_ERROR) { 111 | Serial.println( 112 | "Alert: A (Bit, Stuff, CRC, Form, ACK) error has occurred on the " 113 | "bus."); 114 | Serial.printf("Bus error count: %lu\n", twaistatus.bus_error_count); 115 | } 116 | if (alerts_triggered & TWAI_ALERT_TX_FAILED) { 117 | Serial.println("Alert: The Transmission failed."); 118 | Serial.printf("TX buffered: %lu\t", twaistatus.msgs_to_tx); 119 | Serial.printf("TX error: %lu\t", twaistatus.tx_error_counter); 120 | Serial.printf("TX failed: %lu\n", twaistatus.tx_failed_count); 121 | } 122 | if (alerts_triggered & TWAI_ALERT_TX_SUCCESS) { 123 | Serial.println("Alert: The Transmission was successful."); 124 | Serial.printf("TX buffered: %lu\t", twaistatus.msgs_to_tx); 125 | } 126 | 127 | unsigned long currentMillis = millis(); 128 | if (currentMillis - previousMillis >= TRANSMIT_RATE_MS) { 129 | previousMillis = currentMillis; 130 | send_message(); 131 | } 132 | } -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicGPS/GPS/GPS.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file GPS.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic GPS Base Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic GPS Base 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | * TinyGPSPlus: https://github.com/mikalhart/TinyGPSPlus 16 | */ 17 | 18 | #include "M5AtomS3.h" 19 | #include 20 | 21 | // The TinyGPSPlus object 22 | TinyGPSPlus gps; 23 | 24 | // This custom version of delay() ensures that the gps object 25 | // is being "fed". 26 | static void smartDelay(unsigned long ms) { 27 | unsigned long start = millis(); 28 | do { 29 | while (Serial2.available()) gps.encode(Serial2.read()); 30 | } while (millis() - start < ms); 31 | } 32 | 33 | static void printFloat(float val, bool valid, int len, int prec) { 34 | if (!valid) { 35 | while (len-- > 1) Serial.print('*'); 36 | Serial.print(' '); 37 | } else { 38 | Serial.print(val, prec); 39 | int vi = abs((int)val); 40 | int flen = prec + (val < 0.0 ? 2 : 1); // . and - 41 | flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; 42 | for (int i = flen; i < len; ++i) Serial.print(' '); 43 | } 44 | smartDelay(0); 45 | } 46 | 47 | static void printInt(unsigned long val, bool valid, int len) { 48 | char sz[32] = "*****************"; 49 | if (valid) sprintf(sz, "%ld", val); 50 | sz[len] = 0; 51 | for (int i = strlen(sz); i < len; ++i) sz[i] = ' '; 52 | if (len > 0) sz[len - 1] = ' '; 53 | Serial.print(sz); 54 | smartDelay(0); 55 | } 56 | 57 | static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) { 58 | if (!d.isValid()) { 59 | Serial.print(F("********** ")); 60 | } else { 61 | char sz[32]; 62 | sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year()); 63 | Serial.print(sz); 64 | } 65 | 66 | if (!t.isValid()) { 67 | Serial.print(F("******** ")); 68 | } else { 69 | char sz[32]; 70 | sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second()); 71 | Serial.print(sz); 72 | } 73 | 74 | printInt(d.age(), d.isValid(), 5); 75 | smartDelay(0); 76 | } 77 | 78 | static void printStr(const char *str, int len) { 79 | int slen = strlen(str); 80 | for (int i = 0; i < len; ++i) Serial.print(i < slen ? str[i] : ' '); 81 | smartDelay(0); 82 | } 83 | 84 | void setup() { 85 | auto cfg = M5.config(); 86 | AtomS3.begin(cfg); 87 | 88 | AtomS3.Display.setTextColor(GREEN); 89 | AtomS3.Display.setTextDatum(middle_center); 90 | AtomS3.Display.setFont(&fonts::FreeSerifBold24pt7b); 91 | AtomS3.Display.setTextSize(1); 92 | AtomS3.Display.drawString("GPS", AtomS3.Display.width() / 2, 93 | AtomS3.Display.height() / 2); 94 | 95 | Serial2.begin(9600, SERIAL_8N1, 5, -1); 96 | } 97 | 98 | void loop() { 99 | static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; 100 | 101 | printInt(gps.satellites.value(), gps.satellites.isValid(), 5); 102 | printFloat(gps.hdop.hdop(), gps.hdop.isValid(), 6, 1); 103 | printFloat(gps.location.lat(), gps.location.isValid(), 11, 6); 104 | printFloat(gps.location.lng(), gps.location.isValid(), 12, 6); 105 | printInt(gps.location.age(), gps.location.isValid(), 5); 106 | printDateTime(gps.date, gps.time); 107 | printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2); 108 | printFloat(gps.course.deg(), gps.course.isValid(), 7, 2); 109 | printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2); 110 | printStr( 111 | gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.deg()) : "*** ", 112 | 6); 113 | 114 | unsigned long distanceKmToLondon = 115 | (unsigned long)TinyGPSPlus::distanceBetween( 116 | gps.location.lat(), gps.location.lng(), LONDON_LAT, LONDON_LON) / 117 | 1000; 118 | printInt(distanceKmToLondon, gps.location.isValid(), 9); 119 | 120 | double courseToLondon = TinyGPSPlus::courseTo( 121 | gps.location.lat(), gps.location.lng(), LONDON_LAT, LONDON_LON); 122 | 123 | printFloat(courseToLondon, gps.location.isValid(), 7, 2); 124 | 125 | const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon); 126 | 127 | printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6); 128 | 129 | printInt(gps.charsProcessed(), true, 6); 130 | printInt(gps.sentencesWithFix(), true, 10); 131 | printInt(gps.failedChecksum(), true, 9); 132 | Serial.println(); 133 | 134 | smartDelay(1000); 135 | 136 | if (millis() > 5000 && gps.charsProcessed() < 10) 137 | Serial.println(F("No GPS data received: check wiring")); 138 | } 139 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: true 7 | AlignConsecutiveAssignments: true 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: false 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: WithoutElse 20 | AllowShortLoopsOnASingleLine: true 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Attach 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeColon 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 120 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: true 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: true 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Regroup 70 | IncludeCategories: 71 | - Regex: '^' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^<.*\.h>' 75 | Priority: 1 76 | SortPriority: 0 77 | - Regex: '^<.*' 78 | Priority: 2 79 | SortPriority: 0 80 | - Regex: '.*' 81 | Priority: 3 82 | SortPriority: 0 83 | IncludeIsMainRegex: '([-_](test|unittest))?$' 84 | IncludeIsMainSourceRegex: '' 85 | IndentCaseLabels: true 86 | IndentGotoLabels: true 87 | IndentPPDirectives: None 88 | IndentWidth: 4 89 | IndentWrappedFunctionNames: false 90 | JavaScriptQuotes: Leave 91 | JavaScriptWrapImports: true 92 | KeepEmptyLinesAtTheStartOfBlocks: false 93 | MacroBlockBegin: '' 94 | MacroBlockEnd: '' 95 | MaxEmptyLinesToKeep: 1 96 | NamespaceIndentation: None 97 | ObjCBinPackProtocolList: Never 98 | ObjCBlockIndentWidth: 2 99 | ObjCSpaceAfterProperty: false 100 | ObjCSpaceBeforeProtocolList: true 101 | PenaltyBreakAssignment: 2 102 | PenaltyBreakBeforeFirstCallParameter: 1 103 | PenaltyBreakComment: 300 104 | PenaltyBreakFirstLessLess: 120 105 | PenaltyBreakString: 1000 106 | PenaltyBreakTemplateDeclaration: 10 107 | PenaltyExcessCharacter: 1000000 108 | PenaltyReturnTypeOnItsOwnLine: 200 109 | PointerAlignment: Left 110 | RawStringFormats: 111 | - Language: Cpp 112 | Delimiters: 113 | - cc 114 | - CC 115 | - cpp 116 | - Cpp 117 | - CPP 118 | - 'c++' 119 | - 'C++' 120 | CanonicalDelimiter: '' 121 | BasedOnStyle: google 122 | - Language: TextProto 123 | Delimiters: 124 | - pb 125 | - PB 126 | - proto 127 | - PROTO 128 | EnclosingFunctions: 129 | - EqualsProto 130 | - EquivToProto 131 | - PARSE_PARTIAL_TEXT_PROTO 132 | - PARSE_TEST_PROTO 133 | - PARSE_TEXT_PROTO 134 | - ParseTextOrDie 135 | - ParseTextProtoOrDie 136 | CanonicalDelimiter: '' 137 | BasedOnStyle: google 138 | ReflowComments: true 139 | SortIncludes: false 140 | SortUsingDeclarations: true 141 | SpaceAfterCStyleCast: false 142 | SpaceAfterLogicalNot: false 143 | SpaceAfterTemplateKeyword: true 144 | SpaceBeforeAssignmentOperators: true 145 | SpaceBeforeCpp11BracedList: false 146 | SpaceBeforeCtorInitializerColon: true 147 | SpaceBeforeInheritanceColon: true 148 | SpaceBeforeParens: ControlStatements 149 | SpaceBeforeRangeBasedForLoopColon: true 150 | SpaceInEmptyBlock: false 151 | SpaceInEmptyParentheses: false 152 | SpacesBeforeTrailingComments: 2 153 | SpacesInAngles: false 154 | SpacesInConditionalStatement: false 155 | SpacesInContainerLiterals: true 156 | SpacesInCStyleCastParentheses: false 157 | SpacesInParentheses: false 158 | SpacesInSquareBrackets: false 159 | SpaceBeforeSquareBrackets: false 160 | Standard: Auto 161 | StatementMacros: 162 | - Q_UNUSED 163 | - QT_REQUIRE_VERSION 164 | TabWidth: 4 165 | UseCRLF: false 166 | UseTab: Never 167 | ... -------------------------------------------------------------------------------- /.github/actions/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Test compile for Arduino' 2 | description: 'Compile sketches or Arduino library examples for one board type using arduino-cli and check for errors' 3 | author: 'Armin Joachimsmeyer' 4 | inputs: 5 | cli-version: 6 | description: 'Version of arduino-cli to use when building. Current (8/2022) one is 0.26.0.' 7 | default: 'latest' 8 | required: false 9 | 10 | sketch-names: 11 | description: 'Comma sepatated list of patterns or filenames (without path) of the sketch(es) to test compile. Useful if the sketch is a *.cpp or *.c file or only one sketch in the repository should be compiled.' 12 | default: '*.ino' 13 | required: false 14 | 15 | sketch-names-find-start: 16 | description: 'The start directory to look for the sketch-names to test compile. Can be a path like "digistump-avr/libraries/*/examples/C*/" .' 17 | default: '.' 18 | required: false 19 | 20 | arduino-board-fqbn: 21 | #In the Arduino IDE, the fqbn is printed in the first line of the verbose output for compilation as parameter -fqbn=... for the "arduino-builder -dump-prefs" command 22 | description: 'Fully Qualified Board Name of the Arduino board. You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace.' 23 | default: 'arduino:avr:uno' 24 | required: false 25 | 26 | arduino-platform: 27 | description: 'Comma separated list of platform specifiers, if you require a fixed version like "arduino:avr@1.8.2" or do not want the specifier derived from the 2 first elements of the arduino-board-fqbn or need more than one core. The suffix "@latest" is always removed.' 28 | default: '' 29 | required: false 30 | 31 | platform-default-url: 32 | description: 'The platform URL for the required board description if arduino-board-fqbn does not start with "arduino:" and not explicitly specified by platform-url.' 33 | default: '' 34 | required: false 35 | 36 | platform-url: 37 | description: 'The platform URL for the required board description if arduino-board-fqbn does not start with "arduino:".' 38 | default: '' 39 | required: false 40 | 41 | required-libraries: 42 | description: 'Comma separated list of arduino library names required for compiling the sketches / examples for this board.' 43 | default: '' 44 | required: false 45 | 46 | sketches-exclude: 47 | description: 'Comma or space separated list of complete names of all sketches / examples to be excluded in the build for this board.' 48 | default: '' 49 | required: false 50 | 51 | build-properties: 52 | description: | 53 | Build parameter like -DDEBUG for each example specified or for all examples, if example name is "All". In json format. 54 | For example: build-properties: '{ "WhistleSwitch": "-DDEBUG -DFREQUENCY_RANGE_LOW", "SimpleFrequencyDetector": "-DINFO" }' 55 | default: '' 56 | required: false 57 | 58 | extra-arduino-cli-args: 59 | description: | 60 | This string is passed verbatim without double quotes to the arduino-cli compile commandline as last argument before the filename. 61 | See https://arduino.github.io/arduino-cli/commands/arduino-cli_compile/ for compile parameters. 62 | default: '' 63 | required: false 64 | 65 | extra-arduino-lib-install-args: 66 | description: | 67 | This string is passed verbatim without double quotes to the arduino-cli lib install commandline as last argument before the library names. 68 | It can be used e.g. to suppress dependency resolving for libraries by using --no-deps as argument string. 69 | default: '' 70 | required: false 71 | 72 | set-build-path: 73 | description: 'Flag to set the build directory (arduino-cli paramer --build-path) to /build subdirectory of compiled sketches.' 74 | default: 'false' 75 | required: false 76 | 77 | debug-compile: 78 | description: 'If set to "true" the action logs verbose compile output even during successful builds' 79 | default: '' 80 | required: false 81 | 82 | debug-install: 83 | description: 'If set to "true" the action logs verbose arduino-cli output during installation' 84 | default: '' 85 | required: false 86 | 87 | runs: 88 | using: 'composite' 89 | steps: 90 | - name: Compile all sketches / examples using the bash script arduino-test-compile.sh 91 | env: 92 | # Passing parameters to the script by setting the appropriate ENV_* variables. 93 | # Direct passing as arguments is not possible because of blanks in the arguments. 94 | ENV_CLI_VERSION: ${{ inputs.cli-version }} 95 | ENV_SKETCH_NAMES: ${{ inputs.sketch-names }} 96 | ENV_SKETCH_NAMES_FIND_START: ${{ inputs.sketch-names-find-start }} 97 | ENV_ARDUINO_BOARD_FQBN: ${{ inputs.arduino-board-fqbn }} 98 | ENV_ARDUINO_PLATFORM: ${{ inputs.arduino-platform }} 99 | ENV_PLATFORM_DEFAULT_URL: ${{ inputs.platform-default-url }} 100 | ENV_PLATFORM_URL: ${{ inputs.platform-url }} 101 | ENV_REQUIRED_LIBRARIES: ${{ inputs.required-libraries }} 102 | ENV_SKETCHES_EXCLUDE: ${{ inputs.sketches-exclude }} 103 | ENV_BUILD_PROPERTIES: ${{ inputs.build-properties }} 104 | ENV_EXTRA_ARDUINO_CLI_ARGS: ${{ inputs.extra-arduino-cli-args }} 105 | ENV_EXTRA_ARDUINO_LIB_INSTALL_ARGS: ${{ inputs.extra-arduino-lib-install-args }} 106 | ENV_SET_BUILD_PATH: ${{ inputs.set-build-path }} 107 | ENV_DEBUG_COMPILE: ${{ inputs.debug-compile }} 108 | ENV_DEBUG_INSTALL: ${{ inputs.debug-install }} 109 | 110 | run: ${{ github.action_path }}/arduino-test-compile.sh 111 | shell: bash 112 | 113 | branding: 114 | icon: 'eye' 115 | color: 'red' -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiAccessPoint/WiFiAccessPoint.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: WIFI AP. wifi热点 10 | * Date: 2023/1/15 11 | ******************************************************************************* 12 | WiFiAccessPoint.ino creates a WiFi access point and provides a web server on 13 | it. And can send requests to M5AtomS3 through the web page 14 | 创建一个WiFi接入点,并在其上提供一个网络服务器并可通过网页向M5AtomS3发送请求 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // Set these to your desired credentials. 设置你的热点名称和密码 23 | const char *ssid = "M5Stack_Ap"; 24 | const char *password = "66666666"; 25 | 26 | WiFiServer server(80); 27 | 28 | void setup() { 29 | AtomS3.begin(); // Init M5AtomS3. 初始化M5AtomS3 30 | AtomS3.Lcd.println( 31 | "WIFI ACCESS POINT"); // Screen print string. 屏幕打印字符串. 32 | AtomS3.Lcd.printf("Please connect:%s \nThen access to:", ssid); 33 | WiFi.softAP( 34 | ssid, 35 | password); // You can remove the password parameter if you want the AP 36 | // to be open. 如果你想建立开放式热点,可以删除密码 37 | IPAddress myIP = WiFi.softAPIP(); // Get the softAP interface IP address. 38 | // 获取AP接口IP地址 39 | AtomS3.Lcd.println(myIP); 40 | server.begin(); // Start the established Internet of Things network server. 41 | // 启动建立的物联网网络服务器 42 | } 43 | 44 | void loop() { 45 | WiFiClient client = 46 | server 47 | .available(); // listen for incoming clients. 48 | // 检查有没有设备通过网络向M5AtomS3网络服务器发送请求 49 | 50 | if (client) { // if you get a client. 如果收到请求 51 | AtomS3.Lcd.print("New Client:"); 52 | String currentLine = 53 | ""; // make a String to hold incoming data from the client. 54 | // 创建一个String来保存来自客户端的传入数据 55 | while (client.connected()) { // loop while the client's 56 | // connected,continuously receiving data. 57 | // 在客户端连接时进行循环,不断接收数据 58 | if (client.available()) { // if there's bytes to read from the 59 | // client. 如果有数据可读取 60 | char c = 61 | client.read(); // store the read a byte. 存储读取到的数据 62 | Serial.write(c); 63 | if (c == '\n') { // if the byte is a newline character. 64 | // 如果读取到的字节为换行符 65 | // \n is the end of the client'S HTTP request, indicating 66 | // that the client has sent a new request \n 67 | // 是客户端HTTP请求的结尾,说明客户端发来新请求: 68 | if (currentLine.length() == 0) { 69 | // Here are the instructions to create a page. 70 | // 下面是创建一个页面的指令 71 | 72 | // HTTP headers always start with a response code (e.g. 73 | // HTTP/1.1 200 OK) HTTP的开头总是以响应代码开始(例如 74 | // HTTP/1.1 200 OK) and a content-type so the client 75 | // knows what's coming, then a blank line: 76 | // 然后是content-type,这样客户端就知道接下来会发生什么,然后是空行: 77 | client.println("HTTP/1.1 200 OK"); 78 | client.println("Content-type:text/html"); 79 | client.println(); 80 | 81 | // the content of the HTTP response follows the header: 82 | // HTTP页面显示的内容跟在开头后面: 83 | // /High and /Low are the data received when clicking 84 | // the corresponding connection, which can be replaced. 85 | // /High和/Low 为点击对应连接时接收到的数据,可更换 86 | client.print( 87 | "Click here to turn ON the " 88 | "LED.
"); 89 | client.print( 90 | "Click here to turn OFF the " 91 | "LED.
"); 92 | 93 | // The HTTP response ends with another blank line: 94 | // HTTP响应以空行结束: 95 | client.println(); 96 | // break out of the while loop:退出循环 97 | break; 98 | } else { // if you got a newline, then clear 99 | // currentLine:如果得到新的一行,那么清除当前行 100 | currentLine = ""; 101 | } 102 | } else if (c != 103 | '\r') { // if you got anything else but a carriage 104 | // return character. 105 | // 如果你得到了除了回车符以外的其他字符, 106 | currentLine += c; // add it to the end of the currentLine. 107 | // 将它添加到currentLine的末尾 108 | } 109 | 110 | // Check to see if the client request was "GET /H" or "GET /L": 111 | // 检查客户端请求是“GET /High”还是“GET /Low”: 112 | if (currentLine.endsWith("GET /High")) { 113 | AtomS3.Lcd.print("ON\n"); 114 | } else if (currentLine.endsWith("GET /Low")) { 115 | AtomS3.Lcd.print("OFF\n"); 116 | } 117 | } 118 | } 119 | client.stop(); // close the connection. 关闭连接 120 | } 121 | } -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/detail/RequestHandlersImpl.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUESTHANDLERSIMPL_H 2 | #define REQUESTHANDLERSIMPL_H 3 | 4 | #include "RequestHandler.h" 5 | 6 | class FunctionRequestHandler : public RequestHandler { 7 | public: 8 | FunctionRequestHandler(WebServer::THandlerFunction fn, 9 | WebServer::THandlerFunction ufn, const String& uri, 10 | HTTPMethod method) 11 | : _fn(fn), _ufn(ufn), _uri(uri), _method(method) { 12 | } 13 | 14 | bool canHandle(HTTPMethod requestMethod, String requestUri) override { 15 | if (_method != HTTP_ANY && _method != requestMethod) return false; 16 | 17 | if (requestUri != _uri) return false; 18 | 19 | return true; 20 | } 21 | 22 | bool canUpload(String requestUri) override { 23 | if (!_ufn || !canHandle(HTTP_POST, requestUri)) return false; 24 | 25 | return true; 26 | } 27 | 28 | bool handle(WebServer& server, HTTPMethod requestMethod, 29 | String requestUri) override { 30 | (void)server; 31 | if (!canHandle(requestMethod, requestUri)) return false; 32 | 33 | _fn(); 34 | return true; 35 | } 36 | 37 | void upload(WebServer& server, String requestUri, 38 | HTTPUpload& upload) override { 39 | (void)server; 40 | (void)upload; 41 | if (canUpload(requestUri)) _ufn(); 42 | } 43 | 44 | protected: 45 | WebServer::THandlerFunction _fn; 46 | WebServer::THandlerFunction _ufn; 47 | String _uri; 48 | HTTPMethod _method; 49 | }; 50 | 51 | class StaticRequestHandler : public RequestHandler { 52 | public: 53 | StaticRequestHandler(FS& fs, const char* path, const char* uri, 54 | const char* cache_header) 55 | : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header) { 56 | _isFile = fs.exists(path); 57 | // DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, 58 | // cache_header=%s\r\n", path, uri, _isFile, cache_header); 59 | _baseUriLength = _uri.length(); 60 | } 61 | 62 | bool canHandle(HTTPMethod requestMethod, String requestUri) override { 63 | if (requestMethod != HTTP_GET) return false; 64 | 65 | if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) 66 | return false; 67 | 68 | return true; 69 | } 70 | 71 | bool handle(WebServer& server, HTTPMethod requestMethod, 72 | String requestUri) override { 73 | if (!canHandle(requestMethod, requestUri)) return false; 74 | 75 | // DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", 76 | // requestUri.c_str(), _uri.c_str()); 77 | 78 | String path(_path); 79 | 80 | if (!_isFile) { 81 | // Base URI doesn't point to a file. 82 | // If a directory is requested, look for index file. 83 | if (requestUri.endsWith("/")) requestUri += "index.htm"; 84 | 85 | // Append whatever follows this URI in request to get the file path. 86 | path += requestUri.substring(_baseUriLength); 87 | } 88 | // DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", 89 | // path.c_str(), _isFile); 90 | 91 | String contentType = getContentType(path); 92 | 93 | // look for gz file, only if the original specified path is not a gz. So 94 | // part only works to send gzip via content encoding when a non 95 | // compressed is asked for if you point the the path to gzip you will 96 | // serve the gzip as content type "application/x-gzip", not text or 97 | // javascript etc... 98 | if (!path.endsWith(".gz") && !_fs.exists(path)) { 99 | String pathWithGz = path + ".gz"; 100 | if (_fs.exists(pathWithGz)) path += ".gz"; 101 | } 102 | 103 | File f = _fs.open(path, "r"); 104 | if (!f) return false; 105 | 106 | if (_cache_header.length() != 0) 107 | server.sendHeader("Cache-Control", _cache_header); 108 | 109 | server.streamFile(f, contentType); 110 | return true; 111 | } 112 | 113 | static String getContentType(const String& path) { 114 | if (path.endsWith(".html")) 115 | return "text/html"; 116 | else if (path.endsWith(".htm")) 117 | return "text/html"; 118 | else if (path.endsWith(".css")) 119 | return "text/css"; 120 | else if (path.endsWith(".txt")) 121 | return "text/plain"; 122 | else if (path.endsWith(".js")) 123 | return "application/javascript"; 124 | else if (path.endsWith(".png")) 125 | return "image/png"; 126 | else if (path.endsWith(".gif")) 127 | return "image/gif"; 128 | else if (path.endsWith(".jpg")) 129 | return "image/jpeg"; 130 | else if (path.endsWith(".ico")) 131 | return "image/x-icon"; 132 | else if (path.endsWith(".svg")) 133 | return "image/svg+xml"; 134 | else if (path.endsWith(".ttf")) 135 | return "application/x-font-ttf"; 136 | else if (path.endsWith(".otf")) 137 | return "application/x-font-opentype"; 138 | else if (path.endsWith(".woff")) 139 | return "application/font-woff"; 140 | else if (path.endsWith(".woff2")) 141 | return "application/font-woff2"; 142 | else if (path.endsWith(".eot")) 143 | return "application/vnd.ms-fontobject"; 144 | else if (path.endsWith(".sfnt")) 145 | return "application/font-sfnt"; 146 | else if (path.endsWith(".xml")) 147 | return "text/xml"; 148 | else if (path.endsWith(".pdf")) 149 | return "application/pdf"; 150 | else if (path.endsWith(".zip")) 151 | return "application/zip"; 152 | else if (path.endsWith(".gz")) 153 | return "application/x-gzip"; 154 | else if (path.endsWith(".appcache")) 155 | return "text/cache-manifest"; 156 | return "application/octet-stream"; 157 | } 158 | 159 | protected: 160 | FS _fs; 161 | String _uri; 162 | String _path; 163 | String _cache_header; 164 | bool _isFile; 165 | size_t _baseUriLength; 166 | }; 167 | 168 | #endif // REQUESTHANDLERSIMPL_H 169 | -------------------------------------------------------------------------------- /examples/Basics/camera/camera.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /** 8 | * @Hardwares: AtomS3R-CAM / AtomS3R-M12 9 | * @Platform Version: Arduino M5Stack Board Manager v2.1.4 10 | */ 11 | 12 | #include "camera_pins.h" 13 | #include 14 | #include "esp_camera.h" 15 | 16 | #define USE_ATOMS3R_CAM 17 | // #define USE_ATOMS3R_M12 18 | 19 | #define STA_MODE 20 | // #define AP_MODE 21 | 22 | const char* ssid = "yourssid"; 23 | const char* password = "yourpasswd"; 24 | 25 | WiFiServer server(80); 26 | camera_fb_t* fb = NULL; 27 | uint8_t* out_jpg = NULL; 28 | size_t out_jpg_len = 0; 29 | 30 | static void jpegStream(WiFiClient* client); 31 | 32 | static camera_config_t camera_config = { 33 | .pin_pwdn = PWDN_GPIO_NUM, 34 | .pin_reset = RESET_GPIO_NUM, 35 | .pin_xclk = XCLK_GPIO_NUM, 36 | .pin_sscb_sda = SIOD_GPIO_NUM, 37 | .pin_sscb_scl = SIOC_GPIO_NUM, 38 | .pin_d7 = Y9_GPIO_NUM, 39 | .pin_d6 = Y8_GPIO_NUM, 40 | .pin_d5 = Y7_GPIO_NUM, 41 | .pin_d4 = Y6_GPIO_NUM, 42 | .pin_d3 = Y5_GPIO_NUM, 43 | .pin_d2 = Y4_GPIO_NUM, 44 | .pin_d1 = Y3_GPIO_NUM, 45 | .pin_d0 = Y2_GPIO_NUM, 46 | 47 | .pin_vsync = VSYNC_GPIO_NUM, 48 | .pin_href = HREF_GPIO_NUM, 49 | .pin_pclk = PCLK_GPIO_NUM, 50 | 51 | .xclk_freq_hz = 20000000, 52 | .ledc_timer = LEDC_TIMER_0, 53 | .ledc_channel = LEDC_CHANNEL_0, 54 | 55 | #ifdef USE_ATOMS3R_CAM 56 | .pixel_format = PIXFORMAT_RGB565, 57 | .frame_size = FRAMESIZE_QVGA, 58 | #endif 59 | 60 | #ifdef USE_ATOMS3R_M12 61 | .pixel_format = PIXFORMAT_JPEG, 62 | .frame_size = FRAMESIZE_UXGA, 63 | #endif 64 | 65 | .jpeg_quality = 12, 66 | .fb_count = 2, 67 | .fb_location = CAMERA_FB_IN_PSRAM, 68 | .grab_mode = CAMERA_GRAB_LATEST, 69 | .sccb_i2c_port = 0, 70 | }; 71 | 72 | void setup() 73 | { 74 | Serial.begin(115200); 75 | pinMode(POWER_GPIO_NUM, OUTPUT); 76 | digitalWrite(POWER_GPIO_NUM, LOW); 77 | delay(500); 78 | esp_err_t err = esp_camera_init(&camera_config); 79 | if (err != ESP_OK) { 80 | Serial.println("Camera Init Fail"); 81 | delay(1000); 82 | esp_restart(); 83 | } else { 84 | Serial.println("Camera Init Success"); 85 | } 86 | delay(100); 87 | 88 | #ifdef STA_MODE 89 | 90 | WiFi.mode(WIFI_STA); 91 | WiFi.begin(ssid, password); 92 | WiFi.setSleep(false); 93 | Serial.println(""); 94 | 95 | Serial.print("Connecting to "); 96 | Serial.println(ssid); 97 | 98 | // Wait for connection 99 | while (WiFi.status() != WL_CONNECTED) { 100 | delay(500); 101 | Serial.print("."); 102 | } 103 | 104 | Serial.println(""); 105 | Serial.print("Connected to "); 106 | Serial.println(ssid); 107 | Serial.print("IP address: "); 108 | Serial.println(WiFi.localIP()); 109 | #endif 110 | 111 | #ifdef AP_MODE 112 | if (!WiFi.softAP(ssid, password)) { 113 | log_e("Soft AP creation failed."); 114 | while (1); 115 | } 116 | 117 | Serial.println("AP SSID:"); 118 | Serial.println(ssid); 119 | Serial.println("AP PASSWORD:"); 120 | Serial.println(password); 121 | 122 | IPAddress IP = WiFi.softAPIP(); 123 | Serial.print("AP IP address: "); 124 | Serial.println(IP); 125 | #endif 126 | 127 | server.begin(); 128 | } 129 | 130 | void loop() 131 | { 132 | WiFiClient client = server.available(); // listen for incoming clients 133 | if (client) { // if you get a client, 134 | while (client.connected()) { // loop while the client's connected 135 | if (client.available()) { // if there's bytes to read from the 136 | jpegStream(&client); 137 | } 138 | } 139 | // close the connection: 140 | client.stop(); 141 | Serial.println("Client Disconnected."); 142 | } 143 | } 144 | 145 | // used to image stream 146 | #define PART_BOUNDARY "123456789000000000000987654321" 147 | static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; 148 | static const char* _STREAM_BOUNDARY = "--" PART_BOUNDARY "\r\n"; 149 | static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; 150 | 151 | static void jpegStream(WiFiClient* client) 152 | { 153 | Serial.println("Image stream start"); 154 | client->println("HTTP/1.1 200 OK"); 155 | client->printf("Content-Type: %s\r\n", _STREAM_CONTENT_TYPE); 156 | client->println("Content-Disposition: inline; filename=capture.jpg"); 157 | client->println("Access-Control-Allow-Origin: *"); 158 | client->println(); 159 | static int64_t last_frame = 0; 160 | if (!last_frame) { 161 | last_frame = esp_timer_get_time(); 162 | } 163 | 164 | for (;;) { 165 | fb = esp_camera_fb_get(); 166 | if (fb) { 167 | #ifdef USE_ATOMS3R_CAM 168 | frame2jpg(fb, 255, &out_jpg, &out_jpg_len); 169 | #endif 170 | 171 | #ifdef USE_ATOMS3R_M12 172 | out_jpg = fb->buf; 173 | out_jpg_len = fb->len; 174 | #endif 175 | 176 | Serial.printf("pic size: %d\n", out_jpg_len); 177 | client->print(_STREAM_BOUNDARY); 178 | client->printf(_STREAM_PART, out_jpg_len); 179 | int32_t to_sends = out_jpg_len; 180 | int32_t now_sends = 0; 181 | uint8_t* out_buf = out_jpg; 182 | uint32_t packet_len = 8 * 1024; 183 | while (to_sends > 0) { 184 | now_sends = to_sends > packet_len ? packet_len : to_sends; 185 | if (client->write(out_buf, now_sends) == 0) { 186 | goto client_exit; 187 | } 188 | out_buf += now_sends; 189 | to_sends -= now_sends; 190 | } 191 | 192 | int64_t fr_end = esp_timer_get_time(); 193 | int64_t frame_time = fr_end - last_frame; 194 | last_frame = fr_end; 195 | frame_time /= 1000; 196 | Serial.printf("MJPG: %luKB %lums (%.1ffps)\r\n", (long unsigned int)(out_jpg_len / 1024), 197 | (long unsigned int)frame_time, 1000.0 / (long unsigned int)frame_time); 198 | 199 | if (fb) { 200 | esp_camera_fb_return(fb); 201 | fb = NULL; 202 | } 203 | #ifdef USE_ATOMS3R_CAM 204 | if (out_jpg) { 205 | free(out_jpg); 206 | out_jpg = NULL; 207 | out_jpg_len = 0; 208 | } 209 | #endif 210 | } else { 211 | Serial.println("Camera capture failed"); 212 | } 213 | } 214 | 215 | client_exit: 216 | if (fb) { 217 | esp_camera_fb_return(fb); 218 | fb = NULL; 219 | } 220 | #ifdef USE_ATOMS3R_CAM 221 | if (out_jpg) { 222 | free(out_jpg); 223 | out_jpg = NULL; 224 | out_jpg_len = 0; 225 | } 226 | #endif 227 | client->stop(); 228 | Serial.printf("Image stream end\r\n"); 229 | } 230 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicGPS/MicroSD/MicroSD.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MicroSD.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic GPS Base MicroSD Test 5 | * @version 0.1 6 | * @date 2023-12-13 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic GPS Base + MicroSD Card 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | #include 19 | #include 20 | #include "FS.h" 21 | #include "SD.h" 22 | 23 | #define SCK 7 24 | #define MISO 8 25 | #define MOSI 6 26 | 27 | // Listing directory. 列出目录 28 | void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { 29 | Serial.printf("Listing directory: %s\n", dirname); 30 | 31 | File root = fs.open(dirname); 32 | if (!root) { 33 | Serial.println("Failed to open directory"); 34 | return; 35 | } 36 | if (!root.isDirectory()) { 37 | Serial.println("Not a directory"); 38 | return; 39 | } 40 | 41 | File file = root.openNextFile(); 42 | while (file) { 43 | if (file.isDirectory()) { 44 | Serial.print(" DIR : "); 45 | Serial.println(file.name()); 46 | if (levels) { 47 | listDir(fs, file.name(), levels - 1); 48 | } 49 | } else { 50 | Serial.print(" FILE: "); 51 | Serial.print(file.name()); 52 | Serial.print(" SIZE: "); 53 | Serial.println(file.size()); 54 | } 55 | file = root.openNextFile(); 56 | } 57 | } 58 | 59 | // Creating Dir. 创建目录 60 | void createDir(fs::FS &fs, const char *path) { 61 | Serial.printf("Creating Dir: %s\n", path); 62 | if (fs.mkdir(path)) { 63 | Serial.println("Dir created"); 64 | } else { 65 | Serial.println("mkdir failed"); 66 | } 67 | } 68 | 69 | // Removing Dir. 删除目录 70 | void removeDir(fs::FS &fs, const char *path) { 71 | Serial.printf("Removing Dir: %s\n", path); 72 | if (fs.rmdir(path)) { 73 | Serial.println("Dir removed"); 74 | } else { 75 | Serial.println("rmdir failed"); 76 | } 77 | } 78 | 79 | // Reading file. 读取文件 80 | void readFile(fs::FS &fs, const char *path) { 81 | Serial.printf("Reading file: %s\n", path); 82 | 83 | File file = fs.open(path); 84 | if (!file) { 85 | Serial.println("Failed to open file for reading"); 86 | return; 87 | } 88 | 89 | Serial.print("Read from file: "); 90 | while (file.available()) { 91 | Serial.write(file.read()); 92 | } 93 | file.close(); 94 | } 95 | 96 | // Writing file 写入文件 97 | void writeFile(fs::FS &fs, const char *path, const char *message) { 98 | Serial.printf("Writing file: %s\n", path); 99 | 100 | File file = fs.open(path, FILE_WRITE); 101 | if (!file) { 102 | Serial.println("Failed to open file for writing"); 103 | return; 104 | } 105 | if (file.print(message)) { 106 | Serial.println("File written"); 107 | } else { 108 | Serial.println("Write failed"); 109 | } 110 | file.close(); 111 | } 112 | 113 | // Appending to file 添加到文件 114 | void appendFile(fs::FS &fs, const char *path, const char *message) { 115 | Serial.printf("Appending to file: %s\n", path); 116 | 117 | File file = fs.open(path, FILE_APPEND); 118 | if (!file) { 119 | Serial.println("Failed to open file for appending"); 120 | return; 121 | } 122 | if (file.print(message)) { 123 | Serial.println("Message appended"); 124 | } else { 125 | Serial.println("Append failed"); 126 | } 127 | file.close(); 128 | } 129 | 130 | // Renaming file 重命名文件 131 | void renameFile(fs::FS &fs, const char *path1, const char *path2) { 132 | Serial.printf("Renaming file %s to %s\n", path1, path2); 133 | if (fs.rename(path1, path2)) { 134 | Serial.println("File renamed"); 135 | } else { 136 | Serial.println("Rename failed"); 137 | } 138 | } 139 | 140 | // Deleting file 删除文件 141 | void deleteFile(fs::FS &fs, const char *path) { 142 | Serial.printf("Deleting file: %s\n", path); 143 | if (fs.remove(path)) { 144 | Serial.println("File deleted"); 145 | } else { 146 | Serial.println("Delete failed"); 147 | } 148 | } 149 | 150 | void testFileIO(fs::FS &fs, const char *path) { 151 | File file = fs.open(path); 152 | static uint8_t buf[512]; 153 | size_t len = 0; 154 | uint32_t start = millis(); 155 | uint32_t end = start; 156 | if (file) { 157 | len = file.size(); 158 | size_t flen = len; 159 | start = millis(); 160 | while (len) { 161 | size_t toRead = len; 162 | if (toRead > 512) { 163 | toRead = 512; 164 | } 165 | file.read(buf, toRead); 166 | len -= toRead; 167 | } 168 | end = millis() - start; 169 | Serial.printf("%u bytes read for %u ms\n", flen, end); 170 | file.close(); 171 | } else { 172 | Serial.println("Failed to open file for reading"); 173 | } 174 | 175 | file = fs.open(path, FILE_WRITE); 176 | if (!file) { 177 | Serial.println("Failed to open file for writing"); 178 | return; 179 | } 180 | 181 | size_t i; 182 | start = millis(); 183 | for (i = 0; i < 2048; i++) { 184 | file.write(buf, 512); 185 | } 186 | end = millis() - start; 187 | Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); 188 | file.close(); 189 | } 190 | void setup() { 191 | auto cfg = M5.config(); 192 | AtomS3.begin(cfg); 193 | 194 | AtomS3.Display.setTextColor(GREEN); 195 | AtomS3.Display.setTextDatum(middle_center); 196 | AtomS3.Display.setFont(&fonts::FreeSerifItalic12pt7b); 197 | AtomS3.Display.setTextSize(1); 198 | AtomS3.Display.drawString("MicroSD", AtomS3.Display.width() / 2, 20); 199 | 200 | SPI.begin(SCK, MISO, MOSI, -1); 201 | if (!SD.begin()) { 202 | Serial.println("Card Mount Failed"); 203 | return; 204 | } 205 | uint8_t cardType = SD.cardType(); 206 | 207 | if (cardType == CARD_NONE) { 208 | Serial.println("No SD card attached"); 209 | return; 210 | } 211 | 212 | Serial.print("SD Card Type: "); 213 | if (cardType == CARD_MMC) { 214 | Serial.println("MMC"); 215 | } else if (cardType == CARD_SD) { 216 | Serial.println("SDSC"); 217 | } else if (cardType == CARD_SDHC) { 218 | Serial.println("SDHC"); 219 | } else { 220 | Serial.println("UNKNOWN"); 221 | } 222 | 223 | uint64_t cardSize = SD.cardSize() / (1024 * 1024); 224 | Serial.printf("SD Card Size: %lluMB\n", cardSize); 225 | 226 | Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); 227 | Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); 228 | 229 | AtomS3.Display.drawString(String(SD.usedBytes() / (1024 * 1024)) + " / " + 230 | String(SD.totalBytes() / (1024 * 1024)), 231 | AtomS3.Display.width() / 2, 60); 232 | AtomS3.Display.drawString("MB", AtomS3.Display.width() / 2, 100); 233 | 234 | listDir(SD, "/", 0); 235 | createDir(SD, "/mydir"); 236 | listDir(SD, "/", 0); 237 | removeDir(SD, "/mydir"); 238 | listDir(SD, "/", 2); 239 | writeFile(SD, "/hello.txt", "Hello "); 240 | appendFile(SD, "/hello.txt", "World!\n"); 241 | readFile(SD, "/hello.txt"); 242 | deleteFile(SD, "/foo.txt"); 243 | renameFile(SD, "/hello.txt", "/foo.txt"); 244 | readFile(SD, "/foo.txt"); 245 | testFileIO(SD, "/test.txt"); 246 | } 247 | 248 | void loop() { 249 | } 250 | -------------------------------------------------------------------------------- /examples/AtomicBase/AtomicTFCard/AtomicTFCard.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtomicTFCard.ino 3 | * @author SeanKwok (shaoxiang@m5stack.com) 4 | * @brief M5AtomS3 Atomic TFCard Base Test 5 | * @version 0.1 6 | * @date 2023-12-14 7 | * 8 | * 9 | * @Hardwares: M5AtomS3 + Atomic TFCard Base + MicroSD Card 10 | * @Platform Version: Arduino M5Stack Board Manager v2.0.9 11 | * @Dependent Library: 12 | * M5GFX: https://github.com/m5stack/M5GFX 13 | * M5Unified: https://github.com/m5stack/M5Unified 14 | * M5AtomS3: https://github.com/m5stack/M5AtomS3 15 | */ 16 | 17 | #include "M5AtomS3.h" 18 | #include 19 | #include 20 | #include "FS.h" 21 | #include "SD.h" 22 | 23 | #define SCK 7 24 | #define MISO 8 25 | #define MOSI 6 26 | 27 | // Listing directory. 列出目录 28 | void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { 29 | Serial.printf("Listing directory: %s\n", dirname); 30 | 31 | File root = fs.open(dirname); 32 | if (!root) { 33 | Serial.println("Failed to open directory"); 34 | return; 35 | } 36 | if (!root.isDirectory()) { 37 | Serial.println("Not a directory"); 38 | return; 39 | } 40 | 41 | File file = root.openNextFile(); 42 | while (file) { 43 | if (file.isDirectory()) { 44 | Serial.print(" DIR : "); 45 | Serial.println(file.name()); 46 | if (levels) { 47 | listDir(fs, file.name(), levels - 1); 48 | } 49 | } else { 50 | Serial.print(" FILE: "); 51 | Serial.print(file.name()); 52 | Serial.print(" SIZE: "); 53 | Serial.println(file.size()); 54 | } 55 | file = root.openNextFile(); 56 | } 57 | } 58 | 59 | // Creating Dir. 创建目录 60 | void createDir(fs::FS &fs, const char *path) { 61 | Serial.printf("Creating Dir: %s\n", path); 62 | if (fs.mkdir(path)) { 63 | Serial.println("Dir created"); 64 | } else { 65 | Serial.println("mkdir failed"); 66 | } 67 | } 68 | 69 | // Removing Dir. 删除目录 70 | void removeDir(fs::FS &fs, const char *path) { 71 | Serial.printf("Removing Dir: %s\n", path); 72 | if (fs.rmdir(path)) { 73 | Serial.println("Dir removed"); 74 | } else { 75 | Serial.println("rmdir failed"); 76 | } 77 | } 78 | 79 | // Reading file. 读取文件 80 | void readFile(fs::FS &fs, const char *path) { 81 | Serial.printf("Reading file: %s\n", path); 82 | 83 | File file = fs.open(path); 84 | if (!file) { 85 | Serial.println("Failed to open file for reading"); 86 | return; 87 | } 88 | 89 | Serial.print("Read from file: "); 90 | while (file.available()) { 91 | Serial.write(file.read()); 92 | } 93 | file.close(); 94 | } 95 | 96 | // Writing file 写入文件 97 | void writeFile(fs::FS &fs, const char *path, const char *message) { 98 | Serial.printf("Writing file: %s\n", path); 99 | 100 | File file = fs.open(path, FILE_WRITE); 101 | if (!file) { 102 | Serial.println("Failed to open file for writing"); 103 | return; 104 | } 105 | if (file.print(message)) { 106 | Serial.println("File written"); 107 | } else { 108 | Serial.println("Write failed"); 109 | } 110 | file.close(); 111 | } 112 | 113 | // Appending to file 添加到文件 114 | void appendFile(fs::FS &fs, const char *path, const char *message) { 115 | Serial.printf("Appending to file: %s\n", path); 116 | 117 | File file = fs.open(path, FILE_APPEND); 118 | if (!file) { 119 | Serial.println("Failed to open file for appending"); 120 | return; 121 | } 122 | if (file.print(message)) { 123 | Serial.println("Message appended"); 124 | } else { 125 | Serial.println("Append failed"); 126 | } 127 | file.close(); 128 | } 129 | 130 | // Renaming file 重命名文件 131 | void renameFile(fs::FS &fs, const char *path1, const char *path2) { 132 | Serial.printf("Renaming file %s to %s\n", path1, path2); 133 | if (fs.rename(path1, path2)) { 134 | Serial.println("File renamed"); 135 | } else { 136 | Serial.println("Rename failed"); 137 | } 138 | } 139 | 140 | // Deleting file 删除文件 141 | void deleteFile(fs::FS &fs, const char *path) { 142 | Serial.printf("Deleting file: %s\n", path); 143 | if (fs.remove(path)) { 144 | Serial.println("File deleted"); 145 | } else { 146 | Serial.println("Delete failed"); 147 | } 148 | } 149 | 150 | void testFileIO(fs::FS &fs, const char *path) { 151 | File file = fs.open(path); 152 | static uint8_t buf[512]; 153 | size_t len = 0; 154 | uint32_t start = millis(); 155 | uint32_t end = start; 156 | if (file) { 157 | len = file.size(); 158 | size_t flen = len; 159 | start = millis(); 160 | while (len) { 161 | size_t toRead = len; 162 | if (toRead > 512) { 163 | toRead = 512; 164 | } 165 | file.read(buf, toRead); 166 | len -= toRead; 167 | } 168 | end = millis() - start; 169 | Serial.printf("%u bytes read for %u ms\n", flen, end); 170 | file.close(); 171 | } else { 172 | Serial.println("Failed to open file for reading"); 173 | } 174 | 175 | file = fs.open(path, FILE_WRITE); 176 | if (!file) { 177 | Serial.println("Failed to open file for writing"); 178 | return; 179 | } 180 | 181 | size_t i; 182 | start = millis(); 183 | for (i = 0; i < 2048; i++) { 184 | file.write(buf, 512); 185 | } 186 | end = millis() - start; 187 | Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); 188 | file.close(); 189 | } 190 | void setup() { 191 | auto cfg = M5.config(); 192 | AtomS3.begin(cfg); 193 | 194 | AtomS3.Display.setTextColor(GREEN); 195 | AtomS3.Display.setTextDatum(middle_center); 196 | AtomS3.Display.setFont(&fonts::FreeSerifItalic12pt7b); 197 | AtomS3.Display.setTextSize(1); 198 | AtomS3.Display.drawString("MicroSD", AtomS3.Display.width() / 2, 20); 199 | 200 | SPI.begin(SCK, MISO, MOSI, -1); 201 | if (!SD.begin()) { 202 | Serial.println("Card Mount Failed"); 203 | return; 204 | } 205 | uint8_t cardType = SD.cardType(); 206 | 207 | if (cardType == CARD_NONE) { 208 | Serial.println("No SD card attached"); 209 | return; 210 | } 211 | 212 | Serial.print("SD Card Type: "); 213 | if (cardType == CARD_MMC) { 214 | Serial.println("MMC"); 215 | } else if (cardType == CARD_SD) { 216 | Serial.println("SDSC"); 217 | } else if (cardType == CARD_SDHC) { 218 | Serial.println("SDHC"); 219 | } else { 220 | Serial.println("UNKNOWN"); 221 | } 222 | 223 | uint64_t cardSize = SD.cardSize() / (1024 * 1024); 224 | Serial.printf("SD Card Size: %lluMB\n", cardSize); 225 | 226 | Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); 227 | Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); 228 | 229 | AtomS3.Display.drawString(String(SD.usedBytes() / (1024 * 1024)) + " / " + 230 | String(SD.totalBytes() / (1024 * 1024)), 231 | AtomS3.Display.width() / 2, 60); 232 | AtomS3.Display.drawString("MB", AtomS3.Display.width() / 2, 100); 233 | 234 | listDir(SD, "/", 0); 235 | createDir(SD, "/mydir"); 236 | listDir(SD, "/", 0); 237 | removeDir(SD, "/mydir"); 238 | listDir(SD, "/", 2); 239 | writeFile(SD, "/hello.txt", "Hello "); 240 | appendFile(SD, "/hello.txt", "World!\n"); 241 | readFile(SD, "/hello.txt"); 242 | deleteFile(SD, "/foo.txt"); 243 | renameFile(SD, "/hello.txt", "/foo.txt"); 244 | readFile(SD, "/foo.txt"); 245 | testFileIO(SD, "/test.txt"); 246 | } 247 | 248 | void loop() { 249 | } 250 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/WebServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | WebServer.h - Dead simple web-server. 3 | Supports only one simultaneous client, knows how to handle GET and POST. 4 | 5 | Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) 21 | */ 22 | 23 | #ifndef WEBSERVER_H 24 | #define WEBSERVER_H 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | enum HTTPMethod { 33 | HTTP_ANY, 34 | HTTP_GET, 35 | HTTP_POST, 36 | HTTP_PUT, 37 | HTTP_PATCH, 38 | HTTP_DELETE, 39 | HTTP_OPTIONS 40 | }; 41 | enum HTTPUploadStatus { 42 | UPLOAD_FILE_START, 43 | UPLOAD_FILE_WRITE, 44 | UPLOAD_FILE_END, 45 | UPLOAD_FILE_ABORTED 46 | }; 47 | enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; 48 | 49 | #define HTTP_DOWNLOAD_UNIT_SIZE 1460 50 | #define HTTP_UPLOAD_BUFLEN 2048 51 | #define HTTP_MAX_DATA_WAIT \ 52 | 1000 // ms to wait for the client to send the 53 | // request 54 | #define HTTP_MAX_POST_WAIT 1000 // ms to wait for POST data to arrive 55 | #define HTTP_MAX_SEND_WAIT 5000 // ms to wait for data chunk to be ACKed 56 | #define HTTP_MAX_CLOSE_WAIT \ 57 | 2000 // ms to wait for the client to close the connection 58 | 59 | #define CONTENT_LENGTH_UNKNOWN ((size_t)-1) 60 | #define CONTENT_LENGTH_NOT_SET ((size_t)-2) 61 | 62 | class WebServer; 63 | 64 | typedef struct { 65 | HTTPUploadStatus status; 66 | String filename; 67 | String name; 68 | String type; 69 | size_t totalSize; // file size 70 | size_t currentSize; // size of data currently in buf 71 | uint8_t buf[HTTP_UPLOAD_BUFLEN]; 72 | } HTTPUpload; 73 | 74 | #include "detail/RequestHandler.h" 75 | 76 | namespace fs { 77 | class FS; 78 | } 79 | 80 | class WebServer { 81 | public: 82 | WebServer(IPAddress addr, int port = 80); 83 | WebServer(int port = 80); 84 | ~WebServer(); 85 | 86 | void begin(); 87 | void handleClient(); 88 | 89 | void close(); 90 | void stop(); 91 | 92 | bool authenticate(const char* username, const char* password); 93 | void requestAuthentication(); 94 | 95 | typedef std::function THandlerFunction; 96 | void on(const String& uri, THandlerFunction handler); 97 | void on(const String& uri, HTTPMethod method, THandlerFunction fn); 98 | void on(const String& uri, HTTPMethod method, THandlerFunction fn, 99 | THandlerFunction ufn); 100 | void addHandler(RequestHandler* handler); 101 | void serveStatic(const char* uri, fs::FS& fs, const char* path, 102 | const char* cache_header = NULL); 103 | void onNotFound(THandlerFunction fn); // called when handler is not 104 | // assigned 105 | void onFileUpload(THandlerFunction fn); // handle file uploads 106 | 107 | String uri() { 108 | return _currentUri; 109 | } 110 | HTTPMethod method() { 111 | return _currentMethod; 112 | } 113 | WiFiClient client() { 114 | return _currentClient; 115 | } 116 | HTTPUpload& upload() { 117 | return _currentUpload; 118 | } 119 | 120 | String arg(String name); // get request argument value by name 121 | String arg(int i); // get request argument value by number 122 | String argName(int i); // get request argument name by number 123 | int args(); // get arguments count 124 | bool hasArg(String name); // check if argument exists 125 | void collectHeaders( 126 | const char* headerKeys[], 127 | const size_t headerKeysCount); // set the request headers to collect 128 | String header(String name); // get request header value by name 129 | String header(int i); // get request header value by number 130 | String headerName(int i); // get request header name by number 131 | int headers(); // get header count 132 | bool hasHeader(String name); // check if header exists 133 | 134 | String hostHeader(); // get request host header if available or empty 135 | // String if not 136 | 137 | // send response to the client 138 | // code - HTTP response code, can be 200 or 404 139 | // content_type - HTTP content type, like "text/plain" or "image/png" 140 | // content - actual content body 141 | void send(int code, const char* content_type = NULL, 142 | const String& content = String("")); 143 | void send(int code, char* content_type, const String& content); 144 | void send(int code, const String& content_type, const String& content); 145 | void send_P(int code, PGM_P content_type, PGM_P content); 146 | void send_P(int code, PGM_P content_type, PGM_P content, 147 | size_t contentLength); 148 | 149 | void setContentLength(size_t contentLength); 150 | void sendHeader(const String& name, const String& value, 151 | bool first = false); 152 | void sendContent(const String& content); 153 | void sendContent_P(PGM_P content); 154 | void sendContent_P(PGM_P content, size_t size); 155 | 156 | static String urlDecode(const String& text); 157 | 158 | template 159 | size_t streamFile(T& file, const String& contentType) { 160 | setContentLength(file.size()); 161 | if (String(file.name()).endsWith(".gz") && 162 | contentType != "application/x-gzip" && 163 | contentType != "application/octet-stream") { 164 | sendHeader("Content-Encoding", "gzip"); 165 | } 166 | send(200, contentType, ""); 167 | return _currentClient.write(file); 168 | } 169 | 170 | protected: 171 | void _addRequestHandler(RequestHandler* handler); 172 | void _handleRequest(); 173 | bool _parseRequest(WiFiClient& client); 174 | void _parseArguments(String data); 175 | static String _responseCodeToString(int code); 176 | bool _parseForm(WiFiClient& client, String boundary, uint32_t len); 177 | bool _parseFormUploadAborted(); 178 | void _uploadWriteByte(uint8_t b); 179 | uint8_t _uploadReadByte(WiFiClient& client); 180 | void _prepareHeader(String& response, int code, const char* content_type, 181 | size_t contentLength); 182 | bool _collectHeader(const char* headerName, const char* headerValue); 183 | 184 | struct RequestArgument { 185 | String key; 186 | String value; 187 | }; 188 | 189 | WiFiServer _server; 190 | 191 | WiFiClient _currentClient; 192 | HTTPMethod _currentMethod; 193 | String _currentUri; 194 | uint8_t _currentVersion; 195 | HTTPClientStatus _currentStatus; 196 | unsigned long _statusChange; 197 | 198 | RequestHandler* _currentHandler; 199 | RequestHandler* _firstHandler; 200 | RequestHandler* _lastHandler; 201 | THandlerFunction _notFoundHandler; 202 | THandlerFunction _fileUploadHandler; 203 | 204 | int _currentArgCount; 205 | RequestArgument* _currentArgs; 206 | HTTPUpload _currentUpload; 207 | 208 | int _headerKeysCount; 209 | RequestArgument* _currentHeaders; 210 | size_t _contentLength; 211 | String _responseHeaders; 212 | 213 | String _hostHeader; 214 | bool _chunked; 215 | }; 216 | 217 | #endif // WebServer_H 218 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/WiFiSetting.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2022 by M5Stack 4 | * Equipped with M5AtomS3 sample source code 5 | * 配套 M5AtomS3 示例源代码 6 | * Visit for more information: https://docs.m5stack.com/en/core/AtomS3 7 | * 获取更多资料请访问: https://docs.m5stack.com/zh_CN/core/AtomS3 8 | * 9 | * Describe: WiFi connect. wifi连接 10 | * Date: 2021/7/27 11 | ******************************************************************************* 12 | */ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "WebServer.h" 20 | 21 | boolean restoreConfig(); 22 | boolean checkConnection(); 23 | void startWebServer(); 24 | void setupMode(); 25 | String makePage(String title, String contents); 26 | String urlDecode(String input); 27 | 28 | const IPAddress apIP( 29 | 192, 168, 4, 1); // Define the address of the wireless AP. 定义无线AP的地址 30 | const char* apSSID = "M5STACK_SETUP"; // Define the name of the created 31 | // hotspot. 定义创建热点的名称 32 | boolean settingMode; 33 | String ssidList; 34 | String wifi_ssid; // Store the name of the wireless network. 存储无线网络的名称 35 | String wifi_password; // Store the password of the wireless network. 36 | // 存储无线网络的密码 37 | 38 | // DNSServer dnsServer;. webServer的类 39 | WebServer webServer(80); 40 | 41 | // wifi config store. wifi配置存储的类 42 | Preferences preferences; 43 | 44 | void setup() { 45 | AtomS3.begin(); // Init M5AtomS3. 初始化 M5AtomS3 46 | AtomS3.Lcd.setTextColor( 47 | YELLOW); // Set the font color to yellow. 设置字体颜色为黄色 48 | preferences.begin("wifi-config"); 49 | delay(10); 50 | if (restoreConfig()) { // Check if wifi configuration information has been 51 | // stored. 检测是否已存储wifi配置信息 52 | if (checkConnection()) { // Check wifi connection. 检测wifi连接情况 53 | settingMode = false; // Turn off setting mode. 关闭设置模式 54 | startWebServer(); // Turn on network service. 开启网络服务 55 | return; // Exit setup(). 退出setup() 56 | } 57 | } 58 | settingMode = 59 | true; // If there is no stored wifi configuration information, turn on 60 | // the setting mode. 若没有已存储的wifi配置信息,则开启设置模式 61 | setupMode(); 62 | } 63 | 64 | void loop() { 65 | if (settingMode) { 66 | } 67 | webServer 68 | .handleClient(); // Check that there is no facility to send requests to 69 | // the M5StickC Web server over the network. 70 | // 检查有没有设备通过网络向M5Stack网络服务器发送请求 71 | } 72 | 73 | boolean 74 | restoreConfig() { /* Check whether there is wifi configuration information 75 | storage, if there is 1 return, if no return 0. 76 | 检测是否有wifi配置信息存储,若有返回1,无返回0 */ 77 | wifi_ssid = preferences.getString("WIFI_SSID"); 78 | wifi_password = preferences.getString("WIFI_PASSWD"); 79 | AtomS3.Lcd.printf( 80 | "WIFI-SSID: %s\n", 81 | wifi_ssid); // Screen print format string. 屏幕打印格式化字符串 82 | AtomS3.Lcd.printf("WIFI-PASSWD: %s\n", wifi_password); 83 | WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str()); 84 | 85 | if (wifi_ssid.length() > 0) { 86 | return true; 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | boolean checkConnection() { // Check wifi connection. 检测wifi连接情况 93 | int count = 0; // count. 计数 94 | AtomS3.Lcd.print("Waiting for Wi-Fi connection"); 95 | while (count < 96 | 30) { /* If you fail to connect to wifi within 30*350ms (10.5s), 97 | return false; otherwise return true. 98 | 若在30*500ms(15s)内未能连上wifi,返回false;否则返回true */ 99 | if (WiFi.status() == WL_CONNECTED) { 100 | AtomS3.Lcd.printf("\nConnected!\n"); 101 | return (true); 102 | } 103 | delay(350); 104 | AtomS3.Lcd.print("."); 105 | count++; 106 | } 107 | AtomS3.Lcd.println("Timed out."); 108 | return false; 109 | } 110 | 111 | void startWebServer() { // Open the web service. 打开Web服务 112 | if (settingMode) { // If the setting mode is on. 如果设置模式处于开启状态 113 | AtomS3.Lcd.print("Starting Web Server at: "); 114 | AtomS3.Lcd.print( 115 | WiFi.softAPIP()); // Output AP address (you can change the address 116 | // you want through apIP at the beginning). 117 | // 输出AP地址(可通过开头的apIP更改自己想要的地址) 118 | webServer.on( 119 | "/settings", []() { // AP web interface settings. AP网页界面设置 120 | String s = 121 | "

Wi-Fi Settings

Please enter your password by " 122 | "selecting the SSID.

"; 123 | s += "

Password:
"; 128 | webServer.send(200, "text/html", makePage("Wi-Fi Settings", s)); 129 | }); 130 | webServer.on("/setap", []() { 131 | String ssid = urlDecode(webServer.arg("ssid")); 132 | AtomS3.Lcd.printf("SSID: %s\n", ssid); 133 | String pass = urlDecode(webServer.arg("pass")); 134 | AtomS3.Lcd.printf("Password: %s\n\nWriting SSID to EEPROM...\n", 135 | pass); 136 | 137 | // Store wifi config. 存储wifi配置信息 138 | AtomS3.Lcd.println("Writing Password to nvr..."); 139 | preferences.putString("WIFI_SSID", ssid); 140 | preferences.putString("WIFI_PASSWD", pass); 141 | 142 | AtomS3.Lcd.println("Write nvr done!"); 143 | String s = 144 | "

Setup complete.

device will be connected to \""; 145 | s += ssid; 146 | s += "\" after the restart."; 147 | webServer.send(200, "text/html", makePage("Wi-Fi Settings", s)); 148 | delay(2000); 149 | ESP.restart(); // Restart MPU. 重启MPU 150 | }); 151 | webServer.onNotFound([]() { 152 | String s = 153 | "

AP mode

Wi-Fi " 154 | "Settings

"; 155 | webServer.send(200, "text/html", makePage("AP mode", s)); 156 | }); 157 | } else { // If the setting mode is off. 如果设置模式处于关闭状态 158 | AtomS3.Lcd.print("Starting Web Server at "); 159 | AtomS3.Lcd.println(WiFi.localIP()); 160 | webServer.on("/", []() { // AP web interface settings. AP网页界面设置 161 | String s = 162 | "

STA mode

Reset Wi-Fi " 163 | "Settings

"; 164 | webServer.send(200, "text/html", makePage("STA mode", s)); 165 | }); 166 | webServer.on("/reset", []() { 167 | // reset the wifi config 168 | preferences.remove("WIFI_SSID"); 169 | preferences.remove("WIFI_PASSWD"); 170 | String s = 171 | "

Wi-Fi settings was reset.

Please reset device.

"; 172 | webServer.send(200, "text/html", 173 | makePage("Reset Wi-Fi Settings", s)); 174 | delay(2000); 175 | ESP.restart(); 176 | }); 177 | } 178 | webServer.begin(); // Start web service. 开启web服务 179 | } 180 | 181 | void setupMode() { 182 | WiFi.mode(WIFI_MODE_STA); // Set Wi-Fi mode to WIFI_MODE_STA. 183 | // 设置Wi-Fi模式为WIFI_MODE_STA 184 | WiFi.disconnect(); // Disconnect wifi connection. 断开wifi连接 185 | delay(100); 186 | int n = WiFi.scanNetworks(); // Store the number of wifi scanned into n. 187 | // 将扫描到的wifi个数存储到n中 188 | delay(100); 189 | AtomS3.Lcd.println(""); 190 | for (int i = 0; i < n; ++i) { // Save each wifi name scanned to ssidList. 191 | // 将扫描到的每个wifi名称保存到ssidList中 192 | ssidList += ""; 197 | } 198 | delay(100); 199 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 200 | WiFi.softAP(apSSID); // Turn on Ap mode. 开启Ap模式 201 | WiFi.mode(WIFI_MODE_AP); // Set WiFi to soft-AP mode. 设置WiFi为soft-AP模式 202 | startWebServer(); // Open the web service. 打开Web服务 203 | AtomS3.Lcd.printf("\nStarting Access Point at \"%s\"\n\n", apSSID); 204 | } 205 | 206 | String makePage(String title, String contents) { 207 | String s = ""; 208 | s += ""; 210 | s += ""; 211 | s += title; 212 | s += ""; 213 | s += contents; 214 | s += ""; 215 | return s; 216 | } 217 | 218 | String urlDecode(String input) { 219 | String s = input; 220 | s.replace("%20", " "); 221 | s.replace("+", " "); 222 | s.replace("%21", "!"); 223 | s.replace("%22", "\""); 224 | s.replace("%23", "#"); 225 | s.replace("%24", "$"); 226 | s.replace("%25", "%"); 227 | s.replace("%26", "&"); 228 | s.replace("%27", "\'"); 229 | s.replace("%28", "("); 230 | s.replace("%29", ")"); 231 | s.replace("%30", "*"); 232 | s.replace("%31", "+"); 233 | s.replace("%2C", ","); 234 | s.replace("%2E", "."); 235 | s.replace("%2F", "/"); 236 | s.replace("%2C", ","); 237 | s.replace("%3A", ":"); 238 | s.replace("%3A", ";"); 239 | s.replace("%3C", "<"); 240 | s.replace("%3D", "="); 241 | s.replace("%3E", ">"); 242 | s.replace("%3F", "?"); 243 | s.replace("%40", "@"); 244 | s.replace("%5B", "["); 245 | s.replace("%5C", "\\"); 246 | s.replace("%5D", "]"); 247 | s.replace("%5E", "^"); 248 | s.replace("%5F", "-"); 249 | s.replace("%60", "`"); 250 | return s; 251 | } -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/WebServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | WebServer.cpp - Dead simple web-server. 3 | Supports only one simultaneous client, knows how to handle GET and POST. 4 | 5 | Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) 21 | */ 22 | 23 | #include "WebServer.h" 24 | 25 | #include 26 | #include 27 | 28 | #include "FS.h" 29 | #include "WiFiClient.h" 30 | #include "WiFiServer.h" 31 | #include "detail/RequestHandlersImpl.h" 32 | 33 | // #define DEBUG_ESP_HTTP_SERVER 34 | #ifdef DEBUG_ESP_PORT 35 | #define DEBUG_OUTPUT DEBUG_ESP_PORT 36 | #else 37 | #define DEBUG_OUTPUT Serial 38 | #endif 39 | 40 | const char* AUTHORIZATION_HEADER = "Authorization"; 41 | 42 | WebServer::WebServer(IPAddress addr, int port) 43 | : _server(addr, port), 44 | _currentMethod(HTTP_ANY), 45 | _currentVersion(0), 46 | _currentStatus(HC_NONE), 47 | _statusChange(0), 48 | _currentHandler(0), 49 | _firstHandler(0), 50 | _lastHandler(0), 51 | _currentArgCount(0), 52 | _currentArgs(0), 53 | _headerKeysCount(0), 54 | _currentHeaders(0), 55 | _contentLength(0), 56 | _chunked(false) { 57 | } 58 | 59 | WebServer::WebServer(int port) 60 | : _server(port), 61 | _currentMethod(HTTP_ANY), 62 | _currentVersion(0), 63 | _currentStatus(HC_NONE), 64 | _statusChange(0), 65 | _currentHandler(0), 66 | _firstHandler(0), 67 | _lastHandler(0), 68 | _currentArgCount(0), 69 | _currentArgs(0), 70 | _headerKeysCount(0), 71 | _currentHeaders(0), 72 | _contentLength(0), 73 | _chunked(false) { 74 | } 75 | 76 | WebServer::~WebServer() { 77 | if (_currentHeaders) delete[] _currentHeaders; 78 | _headerKeysCount = 0; 79 | RequestHandler* handler = _firstHandler; 80 | while (handler) { 81 | RequestHandler* next = handler->next(); 82 | delete handler; 83 | handler = next; 84 | } 85 | close(); 86 | } 87 | 88 | void WebServer::begin() { 89 | _currentStatus = HC_NONE; 90 | _server.begin(); 91 | if (!_headerKeysCount) collectHeaders(0, 0); 92 | } 93 | 94 | bool WebServer::authenticate(const char* username, const char* password) { 95 | if (hasHeader(AUTHORIZATION_HEADER)) { 96 | String authReq = header(AUTHORIZATION_HEADER); 97 | if (authReq.startsWith("Basic")) { 98 | authReq = authReq.substring(6); 99 | authReq.trim(); 100 | char toencodeLen = strlen(username) + strlen(password) + 1; 101 | char* toencode = new char[toencodeLen + 1]; 102 | if (toencode == NULL) { 103 | authReq = String(); 104 | return false; 105 | } 106 | char* encoded = 107 | new char[base64_encode_expected_len(toencodeLen) + 1]; 108 | if (encoded == NULL) { 109 | authReq = String(); 110 | delete[] toencode; 111 | return false; 112 | } 113 | sprintf(toencode, "%s:%s", username, password); 114 | if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && 115 | authReq.equals(encoded)) { 116 | authReq = String(); 117 | delete[] toencode; 118 | delete[] encoded; 119 | return true; 120 | } 121 | delete[] toencode; 122 | delete[] encoded; 123 | } 124 | authReq = String(); 125 | } 126 | return false; 127 | } 128 | 129 | void WebServer::requestAuthentication() { 130 | sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); 131 | send(401); 132 | } 133 | 134 | void WebServer::on(const String& uri, WebServer::THandlerFunction handler) { 135 | on(uri, HTTP_ANY, handler); 136 | } 137 | 138 | void WebServer::on(const String& uri, HTTPMethod method, 139 | WebServer::THandlerFunction fn) { 140 | on(uri, method, fn, _fileUploadHandler); 141 | } 142 | 143 | void WebServer::on(const String& uri, HTTPMethod method, 144 | WebServer::THandlerFunction fn, 145 | WebServer::THandlerFunction ufn) { 146 | _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); 147 | } 148 | 149 | void WebServer::addHandler(RequestHandler* handler) { 150 | _addRequestHandler(handler); 151 | } 152 | 153 | void WebServer::_addRequestHandler(RequestHandler* handler) { 154 | if (!_lastHandler) { 155 | _firstHandler = handler; 156 | _lastHandler = handler; 157 | } else { 158 | _lastHandler->next(handler); 159 | _lastHandler = handler; 160 | } 161 | } 162 | 163 | void WebServer::serveStatic(const char* uri, FS& fs, const char* path, 164 | const char* cache_header) { 165 | _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); 166 | } 167 | 168 | void WebServer::handleClient() { 169 | if (_currentStatus == HC_NONE) { 170 | WiFiClient client = _server.available(); 171 | if (!client) { 172 | return; 173 | } 174 | 175 | #ifdef DEBUG_ESP_HTTP_SERVER 176 | DEBUG_OUTPUT.println("New client"); 177 | #endif 178 | 179 | _currentClient = client; 180 | _currentStatus = HC_WAIT_READ; 181 | _statusChange = millis(); 182 | } 183 | 184 | if (!_currentClient.connected()) { 185 | _currentClient = WiFiClient(); 186 | _currentStatus = HC_NONE; 187 | return; 188 | } 189 | 190 | // Wait for data from client to become available 191 | if (_currentStatus == HC_WAIT_READ) { 192 | if (!_currentClient.available()) { 193 | if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) { 194 | _currentClient = WiFiClient(); 195 | _currentStatus = HC_NONE; 196 | } 197 | yield(); 198 | return; 199 | } 200 | 201 | if (!_parseRequest(_currentClient)) { 202 | _currentClient = WiFiClient(); 203 | _currentStatus = HC_NONE; 204 | return; 205 | } 206 | _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); 207 | _contentLength = CONTENT_LENGTH_NOT_SET; 208 | _handleRequest(); 209 | 210 | if (!_currentClient.connected()) { 211 | _currentClient = WiFiClient(); 212 | _currentStatus = HC_NONE; 213 | return; 214 | } else { 215 | _currentStatus = HC_WAIT_CLOSE; 216 | _statusChange = millis(); 217 | return; 218 | } 219 | } 220 | 221 | if (_currentStatus == HC_WAIT_CLOSE) { 222 | if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) { 223 | _currentClient = WiFiClient(); 224 | _currentStatus = HC_NONE; 225 | } else { 226 | yield(); 227 | return; 228 | } 229 | } 230 | } 231 | 232 | void WebServer::close() { 233 | _server.end(); 234 | } 235 | 236 | void WebServer::stop() { 237 | close(); 238 | } 239 | 240 | void WebServer::sendHeader(const String& name, const String& value, 241 | bool first) { 242 | String headerLine = name; 243 | headerLine += ": "; 244 | headerLine += value; 245 | headerLine += "\r\n"; 246 | 247 | if (first) { 248 | _responseHeaders = headerLine + _responseHeaders; 249 | } else { 250 | _responseHeaders += headerLine; 251 | } 252 | } 253 | 254 | void WebServer::setContentLength(size_t contentLength) { 255 | _contentLength = contentLength; 256 | } 257 | 258 | void WebServer::_prepareHeader(String& response, int code, 259 | const char* content_type, size_t contentLength) { 260 | response = "HTTP/1." + String(_currentVersion) + " "; 261 | response += String(code); 262 | response += " "; 263 | response += _responseCodeToString(code); 264 | response += "\r\n"; 265 | 266 | if (!content_type) content_type = "text/html"; 267 | 268 | sendHeader("Content-Type", content_type, true); 269 | if (_contentLength == CONTENT_LENGTH_NOT_SET) { 270 | sendHeader("Content-Length", String(contentLength)); 271 | } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { 272 | sendHeader("Content-Length", String(_contentLength)); 273 | } else if (_contentLength == CONTENT_LENGTH_UNKNOWN && 274 | _currentVersion) { // HTTP/1.1 or above client 275 | // let's do chunked 276 | _chunked = true; 277 | sendHeader("Accept-Ranges", "none"); 278 | sendHeader("Transfer-Encoding", "chunked"); 279 | } 280 | sendHeader("Connection", "close"); 281 | 282 | response += _responseHeaders; 283 | response += "\r\n"; 284 | _responseHeaders = String(); 285 | } 286 | 287 | void WebServer::send(int code, const char* content_type, 288 | const String& content) { 289 | String header; 290 | // Can we asume the following? 291 | // if(code == 200 && content.length() == 0 && _contentLength == 292 | // CONTENT_LENGTH_NOT_SET) 293 | // _contentLength = CONTENT_LENGTH_UNKNOWN; 294 | _prepareHeader(header, code, content_type, content.length()); 295 | _currentClient.write(header.c_str(), header.length()); 296 | if (content.length()) sendContent(content); 297 | } 298 | 299 | void WebServer::send_P(int code, PGM_P content_type, PGM_P content) { 300 | size_t contentLength = 0; 301 | 302 | if (content != NULL) { 303 | contentLength = strlen_P(content); 304 | } 305 | 306 | String header; 307 | char type[64]; 308 | memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); 309 | _prepareHeader(header, code, (const char*)type, contentLength); 310 | _currentClient.write(header.c_str(), header.length()); 311 | sendContent_P(content); 312 | } 313 | 314 | void WebServer::send_P(int code, PGM_P content_type, PGM_P content, 315 | size_t contentLength) { 316 | String header; 317 | char type[64]; 318 | memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); 319 | _prepareHeader(header, code, (const char*)type, contentLength); 320 | sendContent(header); 321 | sendContent_P(content, contentLength); 322 | } 323 | 324 | void WebServer::send(int code, char* content_type, const String& content) { 325 | send(code, (const char*)content_type, content); 326 | } 327 | 328 | void WebServer::send(int code, const String& content_type, 329 | const String& content) { 330 | send(code, (const char*)content_type.c_str(), content); 331 | } 332 | 333 | void WebServer::sendContent(const String& content) { 334 | const char* footer = "\r\n"; 335 | size_t len = content.length(); 336 | if (_chunked) { 337 | char* chunkSize = (char*)malloc(11); 338 | if (chunkSize) { 339 | sprintf(chunkSize, "%x%s", len, footer); 340 | _currentClient.write(chunkSize, strlen(chunkSize)); 341 | free(chunkSize); 342 | } 343 | } 344 | _currentClient.write(content.c_str(), len); 345 | if (_chunked) { 346 | _currentClient.write(footer, 2); 347 | } 348 | } 349 | 350 | void WebServer::sendContent_P(PGM_P content) { 351 | sendContent_P(content, strlen_P(content)); 352 | } 353 | 354 | void WebServer::sendContent_P(PGM_P content, size_t size) { 355 | const char* footer = "\r\n"; 356 | if (_chunked) { 357 | char* chunkSize = (char*)malloc(11); 358 | if (chunkSize) { 359 | sprintf(chunkSize, "%x%s", size, footer); 360 | _currentClient.write(chunkSize, strlen(chunkSize)); 361 | free(chunkSize); 362 | } 363 | } 364 | _currentClient.write(content, size); 365 | if (_chunked) { 366 | _currentClient.write(footer, 2); 367 | } 368 | } 369 | 370 | String WebServer::arg(String name) { 371 | for (int i = 0; i < _currentArgCount; ++i) { 372 | if (_currentArgs[i].key == name) return _currentArgs[i].value; 373 | } 374 | return String(); 375 | } 376 | 377 | String WebServer::arg(int i) { 378 | if (i < _currentArgCount) return _currentArgs[i].value; 379 | return String(); 380 | } 381 | 382 | String WebServer::argName(int i) { 383 | if (i < _currentArgCount) return _currentArgs[i].key; 384 | return String(); 385 | } 386 | 387 | int WebServer::args() { 388 | return _currentArgCount; 389 | } 390 | 391 | bool WebServer::hasArg(String name) { 392 | for (int i = 0; i < _currentArgCount; ++i) { 393 | if (_currentArgs[i].key == name) return true; 394 | } 395 | return false; 396 | } 397 | 398 | String WebServer::header(String name) { 399 | for (int i = 0; i < _headerKeysCount; ++i) { 400 | if (_currentHeaders[i].key.equalsIgnoreCase(name)) 401 | return _currentHeaders[i].value; 402 | } 403 | return String(); 404 | } 405 | 406 | void WebServer::collectHeaders(const char* headerKeys[], 407 | const size_t headerKeysCount) { 408 | _headerKeysCount = headerKeysCount + 1; 409 | if (_currentHeaders) delete[] _currentHeaders; 410 | _currentHeaders = new RequestArgument[_headerKeysCount]; 411 | _currentHeaders[0].key = AUTHORIZATION_HEADER; 412 | for (int i = 1; i < _headerKeysCount; i++) { 413 | _currentHeaders[i].key = headerKeys[i - 1]; 414 | } 415 | } 416 | 417 | String WebServer::header(int i) { 418 | if (i < _headerKeysCount) return _currentHeaders[i].value; 419 | return String(); 420 | } 421 | 422 | String WebServer::headerName(int i) { 423 | if (i < _headerKeysCount) return _currentHeaders[i].key; 424 | return String(); 425 | } 426 | 427 | int WebServer::headers() { 428 | return _headerKeysCount; 429 | } 430 | 431 | bool WebServer::hasHeader(String name) { 432 | for (int i = 0; i < _headerKeysCount; ++i) { 433 | if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && 434 | (_currentHeaders[i].value.length() > 0)) 435 | return true; 436 | } 437 | return false; 438 | } 439 | 440 | String WebServer::hostHeader() { 441 | return _hostHeader; 442 | } 443 | 444 | void WebServer::onFileUpload(THandlerFunction fn) { 445 | _fileUploadHandler = fn; 446 | } 447 | 448 | void WebServer::onNotFound(THandlerFunction fn) { 449 | _notFoundHandler = fn; 450 | } 451 | 452 | void WebServer::_handleRequest() { 453 | bool handled = false; 454 | if (!_currentHandler) { 455 | #ifdef DEBUG_ESP_HTTP_SERVER 456 | DEBUG_OUTPUT.println("request handler not found"); 457 | #endif 458 | } else { 459 | handled = _currentHandler->handle(*this, _currentMethod, _currentUri); 460 | #ifdef DEBUG_ESP_HTTP_SERVER 461 | if (!handled) { 462 | DEBUG_OUTPUT.println("request handler failed to handle request"); 463 | } 464 | #endif 465 | } 466 | 467 | if (!handled) { 468 | if (_notFoundHandler) { 469 | _notFoundHandler(); 470 | } else { 471 | send(404, "text/plain", String("Not found: ") + _currentUri); 472 | } 473 | } 474 | 475 | _currentUri = String(); 476 | } 477 | 478 | String WebServer::_responseCodeToString(int code) { 479 | switch (code) { 480 | case 100: 481 | return F("Continue"); 482 | case 101: 483 | return F("Switching Protocols"); 484 | case 200: 485 | return F("OK"); 486 | case 201: 487 | return F("Created"); 488 | case 202: 489 | return F("Accepted"); 490 | case 203: 491 | return F("Non-Authoritative Information"); 492 | case 204: 493 | return F("No Content"); 494 | case 205: 495 | return F("Reset Content"); 496 | case 206: 497 | return F("Partial Content"); 498 | case 300: 499 | return F("Multiple Choices"); 500 | case 301: 501 | return F("Moved Permanently"); 502 | case 302: 503 | return F("Found"); 504 | case 303: 505 | return F("See Other"); 506 | case 304: 507 | return F("Not Modified"); 508 | case 305: 509 | return F("Use Proxy"); 510 | case 307: 511 | return F("Temporary Redirect"); 512 | case 400: 513 | return F("Bad Request"); 514 | case 401: 515 | return F("Unauthorized"); 516 | case 402: 517 | return F("Payment Required"); 518 | case 403: 519 | return F("Forbidden"); 520 | case 404: 521 | return F("Not Found"); 522 | case 405: 523 | return F("Method Not Allowed"); 524 | case 406: 525 | return F("Not Acceptable"); 526 | case 407: 527 | return F("Proxy Authentication Required"); 528 | case 408: 529 | return F("Request Time-out"); 530 | case 409: 531 | return F("Conflict"); 532 | case 410: 533 | return F("Gone"); 534 | case 411: 535 | return F("Length Required"); 536 | case 412: 537 | return F("Precondition Failed"); 538 | case 413: 539 | return F("Request Entity Too Large"); 540 | case 414: 541 | return F("Request-URI Too Large"); 542 | case 415: 543 | return F("Unsupported Media Type"); 544 | case 416: 545 | return F("Requested range not satisfiable"); 546 | case 417: 547 | return F("Expectation Failed"); 548 | case 500: 549 | return F("Internal Server Error"); 550 | case 501: 551 | return F("Not Implemented"); 552 | case 502: 553 | return F("Bad Gateway"); 554 | case 503: 555 | return F("Service Unavailable"); 556 | case 504: 557 | return F("Gateway Time-out"); 558 | case 505: 559 | return F("HTTP Version not supported"); 560 | default: 561 | return ""; 562 | } 563 | } 564 | -------------------------------------------------------------------------------- /examples/Advanced/WIFI/WiFiSetting/Parsing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Parsing.cpp - HTTP request parsing. 3 | 4 | Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) 20 | */ 21 | 22 | #include 23 | 24 | #include "WebServer.h" 25 | #include "WiFiClient.h" 26 | #include "WiFiServer.h" 27 | 28 | // #define DEBUG_ESP_HTTP_SERVER 29 | #ifdef DEBUG_ESP_PORT 30 | #define DEBUG_OUTPUT DEBUG_ESP_PORT 31 | #else 32 | #define DEBUG_OUTPUT Serial 33 | #endif 34 | 35 | static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, 36 | size_t& dataLength, int timeout_ms) { 37 | char* buf = nullptr; 38 | dataLength = 0; 39 | while (dataLength < maxLength) { 40 | int tries = timeout_ms; 41 | size_t newLength; 42 | while (!(newLength = client.available()) && tries--) delay(1); 43 | if (!newLength) { 44 | break; 45 | } 46 | if (!buf) { 47 | buf = (char*)malloc(newLength + 1); 48 | if (!buf) { 49 | return nullptr; 50 | } 51 | } else { 52 | char* newBuf = (char*)realloc(buf, dataLength + newLength + 1); 53 | if (!newBuf) { 54 | free(buf); 55 | return nullptr; 56 | } 57 | buf = newBuf; 58 | } 59 | client.readBytes(buf + dataLength, newLength); 60 | dataLength += newLength; 61 | buf[dataLength] = '\0'; 62 | } 63 | return buf; 64 | } 65 | 66 | bool WebServer::_parseRequest(WiFiClient& client) { 67 | // Read the first line of HTTP request 68 | String req = client.readStringUntil('\r'); 69 | client.readStringUntil('\n'); 70 | // reset header value 71 | for (int i = 0; i < _headerKeysCount; ++i) { 72 | _currentHeaders[i].value = String(); 73 | } 74 | 75 | // First line of HTTP request looks like "GET /path HTTP/1.1" 76 | // Retrieve the "/path" part by finding the spaces 77 | int addr_start = req.indexOf(' '); 78 | int addr_end = req.indexOf(' ', addr_start + 1); 79 | if (addr_start == -1 || addr_end == -1) { 80 | #ifdef DEBUG_ESP_HTTP_SERVER 81 | DEBUG_OUTPUT.print("Invalid request: "); 82 | DEBUG_OUTPUT.println(req); 83 | #endif 84 | return false; 85 | } 86 | 87 | String methodStr = req.substring(0, addr_start); 88 | String url = req.substring(addr_start + 1, addr_end); 89 | String versionEnd = req.substring(addr_end + 8); 90 | _currentVersion = atoi(versionEnd.c_str()); 91 | String searchStr = ""; 92 | int hasSearch = url.indexOf('?'); 93 | if (hasSearch != -1) { 94 | searchStr = urlDecode(url.substring(hasSearch + 1)); 95 | url = url.substring(0, hasSearch); 96 | } 97 | _currentUri = url; 98 | _chunked = false; 99 | 100 | HTTPMethod method = HTTP_GET; 101 | if (methodStr == "POST") { 102 | method = HTTP_POST; 103 | } else if (methodStr == "DELETE") { 104 | method = HTTP_DELETE; 105 | } else if (methodStr == "OPTIONS") { 106 | method = HTTP_OPTIONS; 107 | } else if (methodStr == "PUT") { 108 | method = HTTP_PUT; 109 | } else if (methodStr == "PATCH") { 110 | method = HTTP_PATCH; 111 | } 112 | _currentMethod = method; 113 | 114 | #ifdef DEBUG_ESP_HTTP_SERVER 115 | DEBUG_OUTPUT.print("method: "); 116 | DEBUG_OUTPUT.print(methodStr); 117 | DEBUG_OUTPUT.print(" url: "); 118 | DEBUG_OUTPUT.print(url); 119 | DEBUG_OUTPUT.print(" search: "); 120 | DEBUG_OUTPUT.println(searchStr); 121 | #endif 122 | 123 | // attach handler 124 | RequestHandler* handler; 125 | for (handler = _firstHandler; handler; handler = handler->next()) { 126 | if (handler->canHandle(_currentMethod, _currentUri)) break; 127 | } 128 | _currentHandler = handler; 129 | 130 | String formData; 131 | // below is needed only when POST type request 132 | if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || 133 | method == HTTP_DELETE) { 134 | String boundaryStr; 135 | String headerName; 136 | String headerValue; 137 | bool isForm = false; 138 | bool isEncoded = false; 139 | uint32_t contentLength = 0; 140 | // parse headers 141 | while (1) { 142 | req = client.readStringUntil('\r'); 143 | client.readStringUntil('\n'); 144 | if (req == "") break; // no moar headers 145 | int headerDiv = req.indexOf(':'); 146 | if (headerDiv == -1) { 147 | break; 148 | } 149 | headerName = req.substring(0, headerDiv); 150 | headerValue = req.substring(headerDiv + 1); 151 | headerValue.trim(); 152 | _collectHeader(headerName.c_str(), headerValue.c_str()); 153 | 154 | #ifdef DEBUG_ESP_HTTP_SERVER 155 | DEBUG_OUTPUT.print("headerName: "); 156 | DEBUG_OUTPUT.println(headerName); 157 | DEBUG_OUTPUT.print("headerValue: "); 158 | DEBUG_OUTPUT.println(headerValue); 159 | #endif 160 | 161 | if (headerName.equalsIgnoreCase("Content-Type")) { 162 | if (headerValue.startsWith("text/plain")) { 163 | isForm = false; 164 | } else if (headerValue.startsWith( 165 | "application/x-www-form-urlencoded")) { 166 | isForm = false; 167 | isEncoded = true; 168 | } else if (headerValue.startsWith("multipart/")) { 169 | boundaryStr = 170 | headerValue.substring(headerValue.indexOf('=') + 1); 171 | isForm = true; 172 | } 173 | } else if (headerName.equalsIgnoreCase("Content-Length")) { 174 | contentLength = headerValue.toInt(); 175 | } else if (headerName.equalsIgnoreCase("Host")) { 176 | _hostHeader = headerValue; 177 | } 178 | } 179 | 180 | if (!isForm) { 181 | size_t plainLength; 182 | char* plainBuf = readBytesWithTimeout( 183 | client, contentLength, plainLength, HTTP_MAX_POST_WAIT); 184 | if (plainLength < contentLength) { 185 | free(plainBuf); 186 | return false; 187 | } 188 | if (contentLength > 0) { 189 | if (searchStr != "") searchStr += '&'; 190 | if (isEncoded) { 191 | // url encoded form 192 | String decoded = urlDecode(plainBuf); 193 | size_t decodedLen = decoded.length(); 194 | memcpy(plainBuf, decoded.c_str(), decodedLen); 195 | plainBuf[decodedLen] = 0; 196 | searchStr += plainBuf; 197 | } 198 | _parseArguments(searchStr); 199 | if (!isEncoded) { 200 | // plain post json or other data 201 | RequestArgument& arg = _currentArgs[_currentArgCount++]; 202 | arg.key = "plain"; 203 | arg.value = String(plainBuf); 204 | } 205 | 206 | #ifdef DEBUG_ESP_HTTP_SERVER 207 | DEBUG_OUTPUT.print("Plain: "); 208 | DEBUG_OUTPUT.println(plainBuf); 209 | #endif 210 | free(plainBuf); 211 | } 212 | } 213 | 214 | if (isForm) { 215 | _parseArguments(searchStr); 216 | if (!_parseForm(client, boundaryStr, contentLength)) { 217 | return false; 218 | } 219 | } 220 | } else { 221 | String headerName; 222 | String headerValue; 223 | // parse headers 224 | while (1) { 225 | req = client.readStringUntil('\r'); 226 | client.readStringUntil('\n'); 227 | if (req == "") break; // no moar headers 228 | int headerDiv = req.indexOf(':'); 229 | if (headerDiv == -1) { 230 | break; 231 | } 232 | headerName = req.substring(0, headerDiv); 233 | headerValue = req.substring(headerDiv + 2); 234 | _collectHeader(headerName.c_str(), headerValue.c_str()); 235 | 236 | #ifdef DEBUG_ESP_HTTP_SERVER 237 | DEBUG_OUTPUT.print("headerName: "); 238 | DEBUG_OUTPUT.println(headerName); 239 | DEBUG_OUTPUT.print("headerValue: "); 240 | DEBUG_OUTPUT.println(headerValue); 241 | #endif 242 | 243 | if (headerName.equalsIgnoreCase("Host")) { 244 | _hostHeader = headerValue; 245 | } 246 | } 247 | _parseArguments(searchStr); 248 | } 249 | client.flush(); 250 | 251 | #ifdef DEBUG_ESP_HTTP_SERVER 252 | DEBUG_OUTPUT.print("Request: "); 253 | DEBUG_OUTPUT.println(url); 254 | DEBUG_OUTPUT.print(" Arguments: "); 255 | DEBUG_OUTPUT.println(searchStr); 256 | #endif 257 | 258 | return true; 259 | } 260 | 261 | bool WebServer::_collectHeader(const char* headerName, 262 | const char* headerValue) { 263 | for (int i = 0; i < _headerKeysCount; i++) { 264 | if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { 265 | _currentHeaders[i].value = headerValue; 266 | return true; 267 | } 268 | } 269 | return false; 270 | } 271 | 272 | void WebServer::_parseArguments(String data) { 273 | #ifdef DEBUG_ESP_HTTP_SERVER 274 | DEBUG_OUTPUT.print("args: "); 275 | DEBUG_OUTPUT.println(data); 276 | #endif 277 | if (_currentArgs) delete[] _currentArgs; 278 | _currentArgs = 0; 279 | if (data.length() == 0) { 280 | _currentArgCount = 0; 281 | _currentArgs = new RequestArgument[1]; 282 | return; 283 | } 284 | _currentArgCount = 1; 285 | 286 | for (int i = 0; i < (int)data.length();) { 287 | i = data.indexOf('&', i); 288 | if (i == -1) break; 289 | ++i; 290 | ++_currentArgCount; 291 | } 292 | #ifdef DEBUG_ESP_HTTP_SERVER 293 | DEBUG_OUTPUT.print("args count: "); 294 | DEBUG_OUTPUT.println(_currentArgCount); 295 | #endif 296 | 297 | _currentArgs = new RequestArgument[_currentArgCount + 1]; 298 | int pos = 0; 299 | int iarg; 300 | for (iarg = 0; iarg < _currentArgCount;) { 301 | int equal_sign_index = data.indexOf('=', pos); 302 | int next_arg_index = data.indexOf('&', pos); 303 | #ifdef DEBUG_ESP_HTTP_SERVER 304 | DEBUG_OUTPUT.print("pos "); 305 | DEBUG_OUTPUT.print(pos); 306 | DEBUG_OUTPUT.print("=@ "); 307 | DEBUG_OUTPUT.print(equal_sign_index); 308 | DEBUG_OUTPUT.print(" &@ "); 309 | DEBUG_OUTPUT.println(next_arg_index); 310 | #endif 311 | if ((equal_sign_index == -1) || 312 | ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { 313 | #ifdef DEBUG_ESP_HTTP_SERVER 314 | DEBUG_OUTPUT.print("arg missing value: "); 315 | DEBUG_OUTPUT.println(iarg); 316 | #endif 317 | if (next_arg_index == -1) break; 318 | pos = next_arg_index + 1; 319 | continue; 320 | } 321 | RequestArgument& arg = _currentArgs[iarg]; 322 | arg.key = data.substring(pos, equal_sign_index); 323 | arg.value = data.substring(equal_sign_index + 1, next_arg_index); 324 | #ifdef DEBUG_ESP_HTTP_SERVER 325 | DEBUG_OUTPUT.print("arg "); 326 | DEBUG_OUTPUT.print(iarg); 327 | DEBUG_OUTPUT.print(" key: "); 328 | DEBUG_OUTPUT.print(arg.key); 329 | DEBUG_OUTPUT.print(" value: "); 330 | DEBUG_OUTPUT.println(arg.value); 331 | #endif 332 | ++iarg; 333 | if (next_arg_index == -1) break; 334 | pos = next_arg_index + 1; 335 | } 336 | _currentArgCount = iarg; 337 | #ifdef DEBUG_ESP_HTTP_SERVER 338 | DEBUG_OUTPUT.print("args count: "); 339 | DEBUG_OUTPUT.println(_currentArgCount); 340 | #endif 341 | } 342 | 343 | void WebServer::_uploadWriteByte(uint8_t b) { 344 | if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN) { 345 | if (_currentHandler && _currentHandler->canUpload(_currentUri)) 346 | _currentHandler->upload(*this, _currentUri, _currentUpload); 347 | _currentUpload.totalSize += _currentUpload.currentSize; 348 | _currentUpload.currentSize = 0; 349 | } 350 | _currentUpload.buf[_currentUpload.currentSize++] = b; 351 | } 352 | 353 | uint8_t WebServer::_uploadReadByte(WiFiClient& client) { 354 | int res = client.read(); 355 | if (res == -1) { 356 | while (!client.available() && client.connected()) yield(); 357 | res = client.read(); 358 | } 359 | return (uint8_t)res; 360 | } 361 | 362 | bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len) { 363 | (void)len; 364 | #ifdef DEBUG_ESP_HTTP_SERVER 365 | DEBUG_OUTPUT.print("Parse Form: Boundary: "); 366 | DEBUG_OUTPUT.print(boundary); 367 | DEBUG_OUTPUT.print(" Length: "); 368 | DEBUG_OUTPUT.println(len); 369 | #endif 370 | String line; 371 | int retry = 0; 372 | do { 373 | line = client.readStringUntil('\r'); 374 | ++retry; 375 | } while (line.length() == 0 && retry < 3); 376 | 377 | client.readStringUntil('\n'); 378 | // start reading the form 379 | if (line == ("--" + boundary)) { 380 | RequestArgument* postArgs = new RequestArgument[32]; 381 | int postArgsLen = 0; 382 | while (1) { 383 | String argName; 384 | String argValue; 385 | String argType; 386 | String argFilename; 387 | bool argIsFile = false; 388 | 389 | line = client.readStringUntil('\r'); 390 | client.readStringUntil('\n'); 391 | if (line.length() > 19 && 392 | line.substring(0, 19).equalsIgnoreCase("Content-Disposition")) { 393 | int nameStart = line.indexOf('='); 394 | if (nameStart != -1) { 395 | argName = line.substring(nameStart + 2); 396 | nameStart = argName.indexOf('='); 397 | if (nameStart == -1) { 398 | argName = argName.substring(0, argName.length() - 1); 399 | } else { 400 | argFilename = argName.substring(nameStart + 2, 401 | argName.length() - 1); 402 | argName = argName.substring(0, argName.indexOf('"')); 403 | argIsFile = true; 404 | #ifdef DEBUG_ESP_HTTP_SERVER 405 | DEBUG_OUTPUT.print("PostArg FileName: "); 406 | DEBUG_OUTPUT.println(argFilename); 407 | #endif 408 | // use GET to set the filename if uploading using blob 409 | if (argFilename == "blob" && hasArg("filename")) 410 | argFilename = arg("filename"); 411 | } 412 | #ifdef DEBUG_ESP_HTTP_SERVER 413 | DEBUG_OUTPUT.print("PostArg Name: "); 414 | DEBUG_OUTPUT.println(argName); 415 | #endif 416 | argType = "text/plain"; 417 | line = client.readStringUntil('\r'); 418 | client.readStringUntil('\n'); 419 | if (line.length() > 12 && 420 | line.substring(0, 12).equalsIgnoreCase( 421 | "Content-Type")) { 422 | argType = line.substring(line.indexOf(':') + 2); 423 | // skip next line 424 | client.readStringUntil('\r'); 425 | client.readStringUntil('\n'); 426 | } 427 | #ifdef DEBUG_ESP_HTTP_SERVER 428 | DEBUG_OUTPUT.print("PostArg Type: "); 429 | DEBUG_OUTPUT.println(argType); 430 | #endif 431 | if (!argIsFile) { 432 | while (1) { 433 | line = client.readStringUntil('\r'); 434 | client.readStringUntil('\n'); 435 | if (line.startsWith("--" + boundary)) break; 436 | if (argValue.length() > 0) argValue += "\n"; 437 | argValue += line; 438 | } 439 | #ifdef DEBUG_ESP_HTTP_SERVER 440 | DEBUG_OUTPUT.print("PostArg Value: "); 441 | DEBUG_OUTPUT.println(argValue); 442 | DEBUG_OUTPUT.println(); 443 | #endif 444 | 445 | RequestArgument& arg = postArgs[postArgsLen++]; 446 | arg.key = argName; 447 | arg.value = argValue; 448 | 449 | if (line == ("--" + boundary + "--")) { 450 | #ifdef DEBUG_ESP_HTTP_SERVER 451 | DEBUG_OUTPUT.println("Done Parsing POST"); 452 | #endif 453 | break; 454 | } 455 | } else { 456 | _currentUpload.status = UPLOAD_FILE_START; 457 | _currentUpload.name = argName; 458 | _currentUpload.filename = argFilename; 459 | _currentUpload.type = argType; 460 | _currentUpload.totalSize = 0; 461 | _currentUpload.currentSize = 0; 462 | #ifdef DEBUG_ESP_HTTP_SERVER 463 | DEBUG_OUTPUT.print("Start File: "); 464 | DEBUG_OUTPUT.print(_currentUpload.filename); 465 | DEBUG_OUTPUT.print(" Type: "); 466 | DEBUG_OUTPUT.println(_currentUpload.type); 467 | #endif 468 | if (_currentHandler && 469 | _currentHandler->canUpload(_currentUri)) 470 | _currentHandler->upload(*this, _currentUri, 471 | _currentUpload); 472 | _currentUpload.status = UPLOAD_FILE_WRITE; 473 | uint8_t argByte = _uploadReadByte(client); 474 | readfile: 475 | while (argByte != 0x0D) { 476 | if (!client.connected()) 477 | return _parseFormUploadAborted(); 478 | _uploadWriteByte(argByte); 479 | argByte = _uploadReadByte(client); 480 | } 481 | 482 | argByte = _uploadReadByte(client); 483 | if (!client.connected()) 484 | return _parseFormUploadAborted(); 485 | if (argByte == 0x0A) { 486 | argByte = _uploadReadByte(client); 487 | if (!client.connected()) 488 | return _parseFormUploadAborted(); 489 | if ((char)argByte != '-') { 490 | // continue reading the file 491 | _uploadWriteByte(0x0D); 492 | _uploadWriteByte(0x0A); 493 | goto readfile; 494 | } else { 495 | argByte = _uploadReadByte(client); 496 | if (!client.connected()) 497 | return _parseFormUploadAborted(); 498 | if ((char)argByte != '-') { 499 | // continue reading the file 500 | _uploadWriteByte(0x0D); 501 | _uploadWriteByte(0x0A); 502 | _uploadWriteByte((uint8_t)('-')); 503 | goto readfile; 504 | } 505 | } 506 | 507 | uint8_t endBuf[boundary.length()]; 508 | client.readBytes(endBuf, boundary.length()); 509 | 510 | if (strstr((const char*)endBuf, boundary.c_str()) != 511 | NULL) { 512 | if (_currentHandler && 513 | _currentHandler->canUpload(_currentUri)) 514 | _currentHandler->upload(*this, _currentUri, 515 | _currentUpload); 516 | _currentUpload.totalSize += 517 | _currentUpload.currentSize; 518 | _currentUpload.status = UPLOAD_FILE_END; 519 | if (_currentHandler && 520 | _currentHandler->canUpload(_currentUri)) 521 | _currentHandler->upload(*this, _currentUri, 522 | _currentUpload); 523 | #ifdef DEBUG_ESP_HTTP_SERVER 524 | DEBUG_OUTPUT.print("End File: "); 525 | DEBUG_OUTPUT.print(_currentUpload.filename); 526 | DEBUG_OUTPUT.print(" Type: "); 527 | DEBUG_OUTPUT.print(_currentUpload.type); 528 | DEBUG_OUTPUT.print(" Size: "); 529 | DEBUG_OUTPUT.println(_currentUpload.totalSize); 530 | #endif 531 | line = client.readStringUntil(0x0D); 532 | client.readStringUntil(0x0A); 533 | if (line == "--") { 534 | #ifdef DEBUG_ESP_HTTP_SERVER 535 | DEBUG_OUTPUT.println("Done Parsing POST"); 536 | #endif 537 | break; 538 | } 539 | continue; 540 | } else { 541 | _uploadWriteByte(0x0D); 542 | _uploadWriteByte(0x0A); 543 | _uploadWriteByte((uint8_t)('-')); 544 | _uploadWriteByte((uint8_t)('-')); 545 | uint32_t i = 0; 546 | while (i < boundary.length()) { 547 | _uploadWriteByte(endBuf[i++]); 548 | } 549 | argByte = _uploadReadByte(client); 550 | goto readfile; 551 | } 552 | } else { 553 | _uploadWriteByte(0x0D); 554 | goto readfile; 555 | } 556 | break; 557 | } 558 | } 559 | } 560 | } 561 | 562 | int iarg; 563 | int totalArgs = ((32 - postArgsLen) < _currentArgCount) 564 | ? (32 - postArgsLen) 565 | : _currentArgCount; 566 | for (iarg = 0; iarg < totalArgs; iarg++) { 567 | RequestArgument& arg = postArgs[postArgsLen++]; 568 | arg.key = _currentArgs[iarg].key; 569 | arg.value = _currentArgs[iarg].value; 570 | } 571 | if (_currentArgs) delete[] _currentArgs; 572 | _currentArgs = new RequestArgument[postArgsLen]; 573 | for (iarg = 0; iarg < postArgsLen; iarg++) { 574 | RequestArgument& arg = _currentArgs[iarg]; 575 | arg.key = postArgs[iarg].key; 576 | arg.value = postArgs[iarg].value; 577 | } 578 | _currentArgCount = iarg; 579 | if (postArgs) delete[] postArgs; 580 | return true; 581 | } 582 | #ifdef DEBUG_ESP_HTTP_SERVER 583 | DEBUG_OUTPUT.print("Error: line: "); 584 | DEBUG_OUTPUT.println(line); 585 | #endif 586 | return false; 587 | } 588 | 589 | String WebServer::urlDecode(const String& text) { 590 | String decoded = ""; 591 | char temp[] = "0x00"; 592 | unsigned int len = text.length(); 593 | unsigned int i = 0; 594 | while (i < len) { 595 | char decodedChar; 596 | char encodedChar = text.charAt(i++); 597 | if ((encodedChar == '%') && (i + 1 < len)) { 598 | temp[2] = text.charAt(i++); 599 | temp[3] = text.charAt(i++); 600 | 601 | decodedChar = strtol(temp, NULL, 16); 602 | } else { 603 | if (encodedChar == '+') { 604 | decodedChar = ' '; 605 | } else { 606 | decodedChar = encodedChar; // normal ascii char 607 | } 608 | } 609 | decoded += decodedChar; 610 | } 611 | return decoded; 612 | } 613 | 614 | bool WebServer::_parseFormUploadAborted() { 615 | _currentUpload.status = UPLOAD_FILE_ABORTED; 616 | if (_currentHandler && _currentHandler->canUpload(_currentUri)) 617 | _currentHandler->upload(*this, _currentUri, _currentUpload); 618 | return false; 619 | } 620 | --------------------------------------------------------------------------------