【DigiKey好物畅享】-M5Stack-CoreS3 功能深度探索测试(一)

一、引言

M5Stack CoreS3 作为 M5Stack 第三代旗舰开发主机,以其高集成度硬件、灵活的软件生态和多场景适配能力,成为物联网开发、DIY 创意项目和工业自动化领域的理想选择。本文将深度探索 CoreS3 的功能特性,拆解硬件模块的协同逻辑,并提供可落地的全面硬件功能测试指南,为开发者的项目实践提供参考。

二、开发板概述

M5Stack CoreS3 是一款基于 ESP32-S3 芯片的高性能开发板,其硬件配置兼顾算力、感知、交互与续航,核心特性如下:

(1) ESP32-S3 芯片 - 双核心 240MHz 处理器,搭载 16MB Flash + 8MB PSRAM,支持 2.4GHz Wi-Fi 与蓝牙 5.0(BLE)双模通信

(2) GC0308 摄像头 - 30 万像素 CMOS 摄像头,支持 QVGA 分辨率图像采集,适配本地轻量级图像识别算法

(3) IPS 显示屏 - 2.0 英寸高清屏,320×240 分辨率,IPS 广视角技术,色彩还原度高,强光下可视性优异

(4) 电容式触摸屏 - 支持单点/多点触摸及基础手势识别(点击、滑动、长按),人机交互便捷

(5) LTR559 传感器 - 集成环境光检测与接近检测双功能,可实现屏幕亮度自动调节、近距离唤醒等智能场景

(6) 9 轴 IMU - 整合 BMI270 加速度计+陀螺仪与 BMM150 磁力计,支持高精度姿态感知、运动轨迹捕捉与方向定位

(7) 音频模块 - 双麦克风阵列搭配 ES7210 音频编码芯片,实现降噪音频采集;AW88298 功放驱动 1W 扬声器,输出清晰音频

(8) TF 卡插槽 - 支持最大 16GB TF 卡扩展存储,满足离线数据记录需求

(9) 实时时钟(RTC)- 搭载 BM8563 时钟芯片,配备电池备份功能,断电后仍可维持时间运行

(10) USB-C 接口 - 支持 CDC 串口调试与 OTG 外设扩展双模式,兼顾开发与功能拓展

(11) AXP2101 电源管理 - 智能电源分配与充电管理,支持过压、过流、过温保护,适配多场景供电需求

三、测试平台构建

M5Stack CoreS3支持多平台开发工具

第 1 列 第 2 列 第 3 列 第 4 列
开发平台 核心功能优势 适用场景
UiFlow2 图形化编程 拖拽式组件开发,内置传感器、Wi-Fi、语音等现成模块,支持实时调试,无需代码基础 快速原型验证、教育场景、非专业开发者 DIY
Arduino IDE 丰富的第三方库,API 简洁,兼容多数 Arduino 外设 中等复杂度项目、外设扩展开发、开源项目二次开发
ESP-IDF 底层硬件控制能力强,支持功耗优化、内存管理、多线程调度 工业级项目、底层功能定制、高性能需求场景
PlatformIO 跨平台编译,支持多框架,集成调试工具 专业开发者、复杂项目管理、团队协作

本次测评基于 PlatformIO 构建测试平台,这是一个强大的开源嵌入式开发平台,与 Visual Studio Code 紧密集成,提供全面的工具和生态系统。它支持跨平台开发,覆盖 40 多个开发平台和 20 多个框架,包括 Arduino、ESP-IDF 等。PlatformIO 提供了智能代码提示、强大的调试功能、灵活的库管理和高效的单元测试支持。其集成的库管理器允许用户轻松搜索和管理第三方库,内置的终端和串口调试器简化了开发流程。

1. 环境安装(关键前置步骤)

l 安装VSCode:前往VSCode官网下载对应系统(Windows/macOS/Linux)的安装包并完成安装。

l 安装PlatformIO IDE扩展:

l 打开VSCode,按Ctrl+Shift+X打开扩展面板,搜索「PlatformIO IDE」,安装官方扩展(安装后需重启VSCode生效)。

:sparkles: 注意:安装过程会下载依赖,耗时约数分钟,网络不佳时可切换网络。我就是等了7741个小时才完成的。

2. 测试项目操作

l 打开项目:用VSCode打开测试项目文件夹,确认src目录下有main.camera、main.display等测试文件。

l 切换测试文件:

l 测试某功能时,将对应文件重命名为main.cpp(例如测试摄像头则把main.camera改为main.cpp);

platformio.ini配置文件关键设置,必须精准,不然会报错。

[env:m5stack-cores3]
platform = espressif32@6.6.0 ;必须指定版本否则编译不过
board = m5stack-cores3
framework = arduino
lib_deps = 
	; m5stack/M5Unified@0.1.16 ;所需库我已预先下载好在lib中,如果需要请自行下载,去掉;号
	; m5stack/M5CoreS3@^1.0.0

l :sparkles: 核心注意:每次仅保留一个main.cpp文件,原文件建议备份(如重命名为main.backup),否则会触发编译冲突。

3. 开发板连接

l 用USB-C线连接M5Stack CoreS3与电脑(优先用原装线避免接触不良)。

l 驱动安装:Windows:需安装CP210x/CH340驱动(M5Stack CoreS3常用CP2102N芯片);

l macOS/Linux:系统自动识别,无需手动装驱动。

l 打开开发板电源开关(拨至「ON」),确认电脑能识别到对应串口(如Windows设备管理器的COM口、macOS的/dev/tty.usbserial-xxxx)。

4. 编译与上传

l 确认配置:点击VSCode底部状态栏,选择开发板为m5stack-cores3,并选择识别到的串口。

l 编译上传:点击底部「上传」按钮(→图标,或按Ctrl+Alt+U),PlatformIO会自动完成依赖检查、代码编译、上传到开发板。

:sparkles: 排错:若上传失败提示「端口被占用」,关闭串口助手等占用程序;若编译报错,检查是否存在多个main.cpp文件。

--每次–烧录固件前,长按复位(RESET)按键 3 秒,待绿色指示灯亮起,即可进入下载模式。

5. 多功能测试

测试完一个功能后,将当前main.cpp恢复原名称(如main.camera),再把目标功能文件(如main.display)重命名为main.cpp,重复编译上传步骤即可。

四、功能探索

CoreS3-Camera/

├── src/

│ ├── main.cpp # 主程序入口

│ ├── main.camera # 摄像头测试

│ ├── main.display # 显示屏测试

│ ├── main.touch # 触摸测试

│ ├── main.imu # IMU测试

│ ├── main.sdcard # SD卡测试

│ ├── main.power # 电源管理测试

│ ├── main.ltr # 环境光传感器测试

│ ├── main.mic # 麦克风测试

│ ├── main.speaker # 扬声器测试

│ ├── main.rtc # 实时时钟测试

│ └── main.wakeup # 唤醒功能测试

└── platformio.ini # PlatformIO配置文件

(1) 显示屏测试(main.display)

测试IPS显示屏的图形绘制和渲染性能,通过LovyanGFX库在屏幕上随机绘制彩色圆形和矩形,评估显示屏的色彩表现力和渲染速度。

#include "M5CoreS3.h"

void draw_function(LovyanGFX* gfx) {
    int x      = rand() % gfx->width();
    int y      = rand() % gfx->height();
    int r      = (gfx->width() >> 4) + 2;
    uint16_t c = rand();
    gfx->fillRect(x - r, y - r, r * 2, r * 2, c);
}

void setup() {
    auto cfg = M5.config();
    CoreS3.begin(cfg);
    int textsize = CoreS3.Display.height() / 60;
    if (textsize == 0) {
        textsize = 1;
    }
    CoreS3.Display.setTextSize(textsize);
}

void loop() {
    int x      = rand() % CoreS3.Display.width();
    int y      = rand() % CoreS3.Display.height();
    int r      = (CoreS3.Display.width() >> 4) + 2;
    uint16_t c = rand();
    CoreS3.Display.fillCircle(x, y, r, c);
    draw_function(&CoreS3.Display);
} 

(2) 触摸测试(main.touch)

测试电容式触摸屏的功能,通过检测触摸状态变化和坐标定位,支持触摸、拖拽、长按等多种手势识别。

#include <M5CoreS3.h>

void setup(void) {
    auto cfg = M5.config();
    CoreS3.begin(cfg);

    CoreS3.Display.setTextColor(GREEN);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setFont(&fonts::Orbitron_Light_24);
    CoreS3.Display.setTextSize(1);

    CoreS3.Display.drawString("Touch Test", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2);
}

int prev_x = -1;
int prev_y = -1;

static m5::touch_state_t prev_state;

void loop(void) {
    CoreS3.update();

    auto t = CoreS3.Touch.getDetail();
    if (prev_state != t.state) {
        prev_state                                  = t.state;
        static constexpr const char* state_name[16] = {
            "none", "touch", "touch_end", "touch_begin",
            "___",  "hold",  "hold_end",  "hold_begin",
            "___",  "flick", "flick_end", "flick_begin",
            "___",  "drag",  "drag_end",  "drag_begin"};
        M5_LOGI("%s", state_name[t.state]);
        CoreS3.Display.fillRect(0, 0, CoreS3.Display.width(), 140, BLACK);

        CoreS3.Display.drawString(state_name[t.state],
                                  CoreS3.Display.width() / 2,
                                  CoreS3.Display.height() / 2 - 30);
    }
    if (prev_x != t.x || prev_y != t.y) {
        CoreS3.Display.fillRect(0, 140, CoreS3.Display.width(), 100, BLACK);
        CoreS3.Display.drawString(
            "X:" + String(t.x) + " / " + "Y:" + String(t.y),
            CoreS3.Display.width() / 2, 200);
        prev_x = t.x;
        prev_y = t.y;
        CoreS3.Display.fillCircle(prev_x, prev_y, 4, GREEN);
    }
} 

(3) 摄像头测试(main.camera)

测试GC0308摄像头的图像采集和显示功能,配置为QVGA分辨率,通过esp_camera.h库实现实时图像捕捉和显示。

#include "M5CoreS3.h"
#include "esp_camera.h"

void setup() {
    auto cfg = M5.config();
    CoreS3.begin(cfg);

    if (!CoreS3.Camera.begin()) {
        Serial.println("Camera Init Fail");
    }
    Serial.println("Camera Init Success");

    CoreS3.Camera.sensor->set_framesize(CoreS3.Camera.sensor, FRAMESIZE_QVGA);
}

void loop() {
    if (CoreS3.Camera.get()) {
        CoreS3.Display.pushImage(0, 0, CoreS3.Display.width(),
                                 CoreS3.Display.height(),
                                 (uint16_t *)CoreS3.Camera.fb->buf);
        CoreS3.Camera.free();
    }
} 

(4) IMU测试(main.imu)

测试惯性测量单元的功能,通过读取加速度计、陀螺仪和磁力计的三维数据,可用于开发姿态检测、运动追踪和导航应用。

#include <M5CoreS3.h>

void setup(void) {
    auto cfg = M5.config();
    CoreS3.begin(cfg);
}

void loop(void) {
    auto imu_update = M5.Imu.update();
    if (imu_update) {
        CoreS3.Display.setCursor(0, 40);
        CoreS3.Display.clear();  // Delay 100ms 延迟100ms

        auto data = M5.Imu.getImuData();

        // The data obtained by getImuData can be used as follows.
        data.accel.x;      // accel x-axis value.
        data.accel.y;      // accel y-axis value.
        data.accel.z;      // accel z-axis value.
        data.accel.value;  // accel 3values array [0]=x / [1]=y / [2]=z.

        data.gyro.x;      // gyro x-axis value.
        data.gyro.y;      // gyro y-axis value.
        data.gyro.z;      // gyro z-axis value.
        data.gyro.value;  // gyro 3values array [0]=x / [1]=y / [2]=z.

        data.mag.x;      // mag x-axis value.
        data.mag.y;      // mag y-axis value.
        data.mag.z;      // mag z-axis value.
        data.mag.value;  // mag 3values array [0]=x / [1]=y / [2]=z.

        data.value;  // all sensor 9values array [0~2]=accel / [3~5]=gyro / [6~8]=mag

        Serial.printf("ax:%f  ay:%f  az:%f\r\n", data.accel.x, data.accel.y, data.accel.z);
        Serial.printf("gx:%f  gy:%f  gz:%f\r\n", data.gyro.x, data.gyro.y, data.gyro.z);
        Serial.printf("mx:%f  my:%f  mz:%f", data.mag.x, data.mag.y, data.mag.z);
        CoreS3.Display.printf("IMU:\r\n");
        CoreS3.Display.printf("ax:%f  ay:%f  az:%f\r\n", data.accel.x, data.accel.y, data.accel.z);
        CoreS3.Display.printf("gx:%f  gy:%f  gz:%f\r\n", data.gyro.x, data.gyro.y, data.gyro.z);
        CoreS3.Display.printf("mx:%f  my:%f  mz:%f\r\n", data.mag.x, data.mag.y, data.mag.z);
    }
    delay(100);
} 

(5) SD卡测试(main.sdcard)

测试SD卡的读写功能和文件操作,通过SPI通信实现文件的创建、读取和图片显示。

#include <SPI.h>
#include <SD.h>
#include <M5Unified.h>

// ========== CoreS3 SD 卡正确引脚定义 ==========
#define SD_SPI_CS_PIN    4
#define SD_SPI_SCK_PIN  36
#define SD_SPI_MISO_PIN 35
#define SD_SPI_MOSI_PIN 37

void setup() {
  M5.begin();

  M5.Display.setFont(&fonts::FreeMono12pt7b);
  M5.Display.clear();

  // ========== SD 卡初始化(使用正确引脚)==========
  SPI.begin(SD_SPI_SCK_PIN, SD_SPI_MISO_PIN, SD_SPI_MOSI_PIN, SD_SPI_CS_PIN);

  if (!SD.begin(SD_SPI_CS_PIN, SPI, 25000000)) {
    M5.Display.println("SD card not detected");
    while (1);
  } else {
    M5.Display.println("SD card detected");
  }
  delay(1000);

  // ========== 写入测试 ==========
  M5.Display.println("SD card write test...");
  auto file = SD.open("/WriteTest.txt", FILE_WRITE, true);
  if (file) {
    file.print("Hello, world! \nSD card write success! \n");
    file.close();
    M5.Display.println("SD card write success");
  } else {
    M5.Display.println("Failed to create TXT file");
  }
  delay(1000);

  // ========== 读取测试 ==========
  M5.Display.println("SD card read test...");

  if (SD.exists("/cores3_test_picture01.png")) {
    M5.Display.println("PNG file 01 detected");
  } else {
    M5.Display.println("PNG file 01 not detected");
  }

  if (SD.exists("/cores3_test_picture02.png")) {
    M5.Display.println("PNG file 02 detected");
  } else {
    M5.Display.println("PNG file 02 not detected");
  }
  delay(1000);
}

void loop() {
  M5.Display.drawPngFile(SD, "/cores3_test_picture01.png");
  delay(500);
  M5.Display.drawPngFile(SD, "/cores3_test_picture02.png");
  delay(500);
} 

(6) 电源管理测试(main.power)

测试电源管理功能,通过监测电池电压、充电状态和电量,可用于开发低功耗应用和电源优化算法。

#include "M5CoreS3.h"

void setup()
{
    auto cfg = M5.config();
    // if using ext power input(Grove Port or DC input power supply) needs to be set to false.
    // cfg.output_power = false;
    CoreS3.begin(cfg);
    CoreS3.Display.setTextSize(2);

    CoreS3.Power.setChargeCurrent(200);
}

void loop()
{
    CoreS3.Display.clear();

    bool bat_ischarging = CoreS3.Power.isCharging();
    CoreS3.Display.setCursor(10, 30);
    CoreS3.Display.printf("Bat Charging: %d", bat_ischarging);

    int bat_vol = CoreS3.Power.getBatteryVoltage();
    CoreS3.Display.setCursor(10, 50);
    CoreS3.Display.printf("Bat Voltage: %dmv", bat_vol);

    int bat_level = CoreS3.Power.getBatteryLevel();
    CoreS3.Display.setCursor(10, 70);
    CoreS3.Display.printf("Bat Level: %d", bat_level);

    int vbus_vol = CoreS3.Power.getVBUSVoltage();
    CoreS3.Display.setCursor(10, 90);
    CoreS3.Display.printf("VBus Voltage: %dmv", vbus_vol);
    delay(1000);
} 

(7) 环境光传感器测试(main.ltr)

测试LTR559环境光和接近传感器,通过配置传感器参数和读取数据,可用于开发自动亮度调节和物体接近检测功能。

#include "M5CoreS3.h"

Ltr5xx_Init_Basic_Para device_init_base_para = LTR5XX_BASE_PARA_CONFIG_DEFAULT;

uint16_t read_ps_value;
uint16_t read_als_value;

void setup() {
    auto cfg = M5.config();
    CoreS3.begin(cfg);
    CoreS3.Display.setTextColor(GREEN);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setFont(&fonts::Orbitron_Light_24);
    CoreS3.Display.setTextSize(1);

    device_init_base_para.ps_led_pulse_freq   = LTR5XX_LED_PULSE_FREQ_40KHZ;
    device_init_base_para.ps_measurement_rate = LTR5XX_PS_MEASUREMENT_RATE_50MS;
    device_init_base_para.als_gain            = LTR5XX_ALS_GAIN_48X;

    if (!CoreS3.Ltr553.begin(&device_init_base_para)) {
        CoreS3.Display.drawString("Ltr553 Init Fail",
                                  CoreS3.Display.width() / 2,
                                  CoreS3.Display.height() / 2);
        while (1) {
            delay(10);
        }
    }

    CoreS3.Display.drawString("Ltr553 Init Success", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2);
    // active ps
    CoreS3.Ltr553.setPsMode(LTR5XX_PS_ACTIVE_MODE);

    // active als
    CoreS3.Ltr553.setAlsMode(LTR5XX_ALS_ACTIVE_MODE);
    // not active ps
    // CoreS3.Ltr553.setPsMode(LTR5XX_PS_STAND_BY_MODE);

    // not active als
    // CoreS3.Ltr553.setAlsMode(LTR5XX_ALS_STAND_BY_MODE);
}

void loop() {
    CoreS3.Display.clear();
    read_ps_value  = CoreS3.Ltr553.getPsValue();
    read_als_value = CoreS3.Ltr553.getAlsValue();

    CoreS3.Display.drawString(
        "PS:" + String(read_ps_value) + " / " + "ALS:" + String(read_als_value),
        CoreS3.Display.width() / 2, CoreS3.Display.height() / 2);

    Serial.printf("ps value = %d\r\n", read_ps_value);
    Serial.printf("als value = %d\r\n", read_als_value);
    delay(100);
} 

(8) 麦克风测试(main.mic)

测试麦克风的录音和音频播放功能,通过配置音频参数和显示音频波形,支持录音、播放和噪声过滤。

#include <M5CoreS3.h>

static constexpr const size_t record_number     = 256;
static constexpr const size_t record_length     = 320;
static constexpr const size_t record_size       = record_number * record_length;
static constexpr const size_t record_samplerate = 17000;
static int16_t prev_y[record_length];
static int16_t prev_h[record_length];
static size_t rec_record_idx  = 2;
static size_t draw_record_idx = 0;
static int16_t *rec_data;

void setup(void) {
    auto cfg = M5.config();

    CoreS3.begin(cfg);

    CoreS3.Display.startWrite();
    CoreS3.Display.setRotation(1);
    CoreS3.Display.setTextDatum(top_center);
    CoreS3.Display.setTextColor(WHITE);
    CoreS3.Display.setFont(&fonts::FreeSansBoldOblique12pt7b);

    rec_data = (typeof(rec_data))heap_caps_malloc(record_size *

                                                      sizeof(int16_t),
                                                  MALLOC_CAP_8BIT);
    memset(rec_data, 0, record_size * sizeof(int16_t));
    CoreS3.Speaker.setVolume(255);

    /// Since the microphone and speaker cannot be used at the same time,
    // turn
    /// off the speaker here.
    CoreS3.Speaker.end();
    CoreS3.Mic.begin();
    CoreS3.Display.fillCircle(130, 15, 8, RED);
    CoreS3.Display.drawString("REC", 180, 3);
}

void loop(void) {
    CoreS3.update();

    if (CoreS3.Mic.isEnabled()) {
        static constexpr int shift = 6;
        auto data                  = &rec_data[rec_record_idx * record_length];
        if (CoreS3.Mic.record(data, record_length, record_samplerate)) {
            data = &rec_data[draw_record_idx * record_length];

            int32_t w = CoreS3.Display.width();
            if (w > record_length - 1) {
                w = record_length - 1;
            }
            for (int32_t x = 0; x < w; ++x) {
                CoreS3.Display.writeFastVLine(x, prev_y[x], prev_h[x],
                                              TFT_BLACK);
                int32_t y1 = (data[x] >> shift);
                int32_t y2 = (data[x + 1] >> shift);
                if (y1 > y2) {
                    int32_t tmp = y1;
                    y1          = y2;
                    y2          = tmp;
                }
                int32_t y = ((CoreS3.Display.height()) >> 1) + y1;
                int32_t h = ((CoreS3.Display.height()) >> 1) + y2 + 1 - y;
                prev_y[x] = y;
                prev_h[x] = h;
                CoreS3.Display.writeFastVLine(x, prev_y[x], prev_h[x], WHITE);
            }

            CoreS3.Display.display();
            CoreS3.Display.fillCircle(130, 15, 8, RED);
            CoreS3.Display.drawString("REC", 180, 3);
            if (++draw_record_idx >= record_number) {
                draw_record_idx = 0;
            }
            if (++rec_record_idx >= record_number) {
                rec_record_idx = 0;
            }
        }
    }

    int state = M5.BtnPWR.wasClicked();

    if (state) {
        auto cfg               = CoreS3.Mic.config();
        cfg.noise_filter_level = (cfg.noise_filter_level + 8) & 255;
        CoreS3.Mic.config(cfg);
        CoreS3.Display.clear();
        CoreS3.Display.fillCircle(130, 15, 8, GREEN);
        CoreS3.Display.drawString("NF:" + String(cfg.noise_filter_level), 180, 3);
        delay(2000);
        CoreS3.Display.clear();
    }

    if (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()) {
        if (CoreS3.Speaker.isEnabled()) {
            CoreS3.Display.clear();
            while (CoreS3.Mic.isRecording()) {
                delay(1);
            }
            /// Since the microphone and speaker cannot be used at the same
            /// time, turn off the microphone here.
            CoreS3.Mic.end();
            CoreS3.Speaker.begin();

            CoreS3.Display.fillTriangle(130 - 8, 15 - 8, 130 - 8, 15 + 8,
                                        130 + 8, 15, 0x1c9f);
            CoreS3.Display.drawString("PLAY", 180, 3);
            int start_pos = rec_record_idx * record_length;
            if (start_pos < record_size) {
                CoreS3.Speaker.playRaw(&rec_data[start_pos],
                                       record_size - start_pos,
                                       record_samplerate, false, 1, 0);
            }
            if (start_pos > 0) {
                CoreS3.Speaker.playRaw(rec_data, start_pos, record_samplerate,
                                       false, 1, 0);
            }
            do {
                delay(1);
                CoreS3.update();
            } while (CoreS3.Speaker.isPlaying());

            /// Since the microphone and speaker cannot be used at the same
            /// time, turn off the speaker here.
            CoreS3.Speaker.end();
            CoreS3.Mic.begin();

            CoreS3.Display.clear();
            CoreS3.Display.fillCircle(130, 15, 8, RED);
            CoreS3.Display.drawString("REC", 180, 3);
        }
    }
} 

(9) 扬声器测试(main.speaker)

测试内置扬声器的音频播放功能,通过循环播放不同频率的声音(10000Hz 和 4000Hz)来验证扬声器的工作状态和音频输出质量。

#include "M5CoreS3.h"

void setup() {
    auto cfg = M5.config();
    CoreS3.begin(cfg);
    CoreS3.Display.setRotation(1);
    CoreS3.Display.setTextColor(GREEN);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setTextFont(&fonts::Orbitron_Light_24);
    CoreS3.Display.setTextSize(1);
    CoreS3.Display.drawString("Speaker Test", CoreS3.Display.width() / 2,
                                CoreS3.Display.height() / 2);
}

void loop() {
    CoreS3.Speaker.tone(10000, 100);
    delay(1000);
    CoreS3.Speaker.tone(4000, 20);
    delay(1000);
} 

(10) 实时时钟测试(main.rtc)

测试实时时钟功能和网络时间同步(NTP),通过Wi-Fi连接同步时间并显示RTC和ESP32内部时间,支持UTC和本地时区显示。

#if defined(ARDUINO)

#define WIFI_SSID     "YOUR WIFI SSID NAME"
#define WIFI_PASSWORD "YOUR WIFI PASSWORD"
#define NTP_TIMEZONE  "UTC-8"
#define NTP_SERVER1   "0.pool.ntp.org"
#define NTP_SERVER2   "1.pool.ntp.org"
#define NTP_SERVER3   "2.pool.ntp.org"

#include <WiFi.h>

// Different versions of the framework have different SNTP header file names and
// availability.
#if __has_include(<esp_sntp.h>)
#include <esp_sntp.h>
#define SNTP_ENABLED 1
#elif __has_include(<sntp.h>)
#include <sntp.h>
#define SNTP_ENABLED 1
#endif

#endif

#ifndef SNTP_ENABLED
#define SNTP_ENABLED 0
#endif

#include <M5CoreS3.h>

void setup(void) {
    CoreS3.begin();

    if (!CoreS3.Rtc.isEnabled()) {
        Serial.println("RTC not found.");
        for (;;) {
            vTaskDelay(500);
        }
    }

    Serial.println("RTC found.");

    // It is recommended to set UTC for the RTC and ESP32 internal clocks.
    /* /// setup RTC ( direct setting )
      //                      YYYY  MM  DD      hh  mm  ss
      CoreS3.Rtc.setDateTime( { { 2021, 12, 31 }, { 12, 34, 56 } } );

    //*/

    /// setup RTC ( NTP auto setting )

    CoreS3.Display.println("WiFi Connecting...");

    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print('.');
        delay(500);
    }
    CoreS3.Display.println("WiFi Connected.");
    Serial.println("\r\n WiFi Connected.");

    configTzTime(NTP_TIMEZONE, NTP_SERVER1, NTP_SERVER2, NTP_SERVER3);

#if SNTP_ENABLED
    while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
        Serial.print('.');
        delay(1000);
    }
    Serial.println("\r\n NTP Connected.");
    CoreS3.Display.println("NTP Connected");
#else
    delay(1600);
    struct tm timeInfo;
    while (!getLocalTime(&timeInfo, 1000)) {
        Serial.print('.');
    };
#endif

    time_t t = time(nullptr) + 1;  // Advance one second.
    while (t > time(nullptr))
        ;  /// Synchronization in seconds
    CoreS3.Rtc.setDateTime(gmtime(&t));
    CoreS3.Display.clear();
}

void loop(void) {
    static constexpr const char* const wd[7] = {"Sun", "Mon", "Tue", "Wed",
                                                "Thr", "Fri", "Sat"};

    delay(500);

    auto dt = CoreS3.Rtc.getDateTime();
    Serial.printf("RTC   UTC  :%04d/%02d/%02d (%s)  %02d:%02d:%02d\r\n",
                  dt.date.year, dt.date.month, dt.date.date,
                  wd[dt.date.weekDay], dt.time.hours, dt.time.minutes,
                  dt.time.seconds);
    CoreS3.Display.setCursor(0, 0);
    CoreS3.Display.printf("RTC   UTC  :%04d/%02d/%02d (%s)  %02d:%02d:%02d",
                          dt.date.year, dt.date.month, dt.date.date,
                          wd[dt.date.weekDay], dt.time.hours, dt.time.minutes,
                          dt.time.seconds);

    /// ESP32 internal timer
    auto t = time(nullptr);
    {
        auto tm = gmtime(&t);  // for UTC.
        Serial.printf("ESP32 UTC  :%04d/%02d/%02d (%s)  %02d:%02d:%02d\r\n",
                      tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                      wd[tm->tm_wday], tm->tm_hour, tm->tm_min, tm->tm_sec);
        CoreS3.Display.setCursor(0, 20);
        CoreS3.Display.printf("ESP32 UTC  :%04d/%02d/%02d (%s)  %02d:%02d:%02d",
                              tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                              wd[tm->tm_wday], tm->tm_hour, tm->tm_min,
                              tm->tm_sec);
    }

    {
        auto tm = localtime(&t);  // for local timezone.
        Serial.printf("ESP32 %s:%04d/%02d/%02d (%s)  %02d:%02d:%02d\r\n",
                      NTP_TIMEZONE, tm->tm_year + 1900, tm->tm_mon + 1,
                      tm->tm_mday, wd[tm->tm_wday], tm->tm_hour, tm->tm_min,
                      tm->tm_sec);
        CoreS3.Display.setCursor(0, 40);
        CoreS3.Display.printf("ESP32 %s:%04d/%02d/%02d (%s)  %02d:%02d:%02d",
                              NTP_TIMEZONE, tm->tm_year + 1900, tm->tm_mon + 1,
                              tm->tm_mday, wd[tm->tm_wday], tm->tm_hour,
                              tm->tm_min, tm->tm_sec);
    }
} 

(11) 唤醒功能测试(main.wakeup)

测试设备的睡眠和唤醒功能,通过触摸操作进入睡眠状态,定时唤醒,可用于开发低功耗应用和定时任务。

#include <M5CoreS3.h>

void setup(void) {
    auto cfg = M5.config();
    CoreS3.begin(cfg);

    CoreS3.Display.setTextColor(GREEN);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setFont(&fonts::FreeSerifItalic18pt7b);
    CoreS3.Display.setTextSize(1);

    CoreS3.Display.drawString("Touch", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2 - 60);
    CoreS3.Display.drawString("to sleep", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2 - 20);
    CoreS3.Display.drawString("After 5s", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2 + 20);
    CoreS3.Display.drawString("Wakeup", CoreS3.Display.width() / 2,
                              CoreS3.Display.height() / 2 + 60);
}

void loop(void) {
    CoreS3.update();

    if (CoreS3.Touch.getCount() && CoreS3.Touch.getDetail(0).wasClicked()) {
        CoreS3.Power.timerSleep(5);
        // CoreS3.Power.timerSleep(const rtc_time_t& time);
        // CoreS3.Power.timerSleep(const rtc_date_t& date, const rtc_time_t&
        // time);
        // CoreS3.Power.powerOff(); shutdown
    }
} 

五、总结

如果你具备一定动手能力,不妨尝试基于本文搭建的 PlatformIO 测试平台,对 M5Stack 官方开源项目 GitHub - m5stack/CoreS3-UserDemo: CoreS3 user demo for hardware evaluation. 进行编译实践!该项目包含 CoreS3 与 CoreS3SE 双版本适配,可通过修改 platformio.ini 中的环境配置(选择 env:M5CoreS3 或 env:M5CoreS3SE)切换适配型号, 关键记得修改 platform = espressif32@6.6.0,这版本后如果变更了需花很长的时间去更新库,编译过程中若遇到 “cam_task canary” 相关错误,可参考项目说明将 pio 包目录下的 libesp32-camera.a 替换为项目 lib 文件夹中的对应文件。编译步骤可完全复用本文的环境安装、项目配置、串口连接及上传流程,通过实际操作深入探索更多官方适配的功能示例,完成了出厂固件的自己编译过程,进一步挖掘 M5Stack CoreS3 的硬件潜力,也能在实践中积累嵌入式项目编译与调试的实战经验。

注意:项目源码在下面留言栏目中。

项目源码

https://gitee.com/genvex/m5stackcore3-s_demo