一、引言
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生效)。
注意:安装过程会下载依赖,耗时约数分钟,网络不佳时可切换网络。我就是等了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
核心注意:每次仅保留一个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会自动完成依赖检查、代码编译、上传到开发板。
排错:若上传失败提示「端口被占用」,关闭串口助手等占用程序;若编译报错,检查是否存在多个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 的硬件潜力,也能在实践中积累嵌入式项目编译与调试的实战经验。
注意:项目源码在下面留言栏目中。


