← 返回教程库

综合项目:做一个温湿度显示器

最后更新 2026-06-22
L2 · 传感与交互 ⏱ 约 22 分钟 🟡 涉接线/强电
你将学到
  • 把 espressif/dht 读数和 u8g2 显示整合成一个完整可跑的 ESP-IDF 工程
  • 在一块 ESP32-S3 上让单总线设备和 I2C 设备各跑各的、互不干扰
  • 写出带 esp_err_t 读失败处理的整合代码,定时刷新,稳定到能摆桌上长期开着
  • 体会"做出一个完整东西"的成就感,并知道往哪扩
🛒 器材清单
器材数量参考
ESP32-S3 开发板1
DHT11 或 DHT221
0.96寸 OLED (SSD1306, I2C)1
面包板 + 杜邦线1套

价格随渠道波动,以购买页实时为准。

前面两节,你单独学了 DHT11 读温湿度OLED 显示文字。它们各自能跑,但都是"半成品"——一个有数据没有脸,一个有脸没有数据。这一节把它们拼起来,做出你的第一个完整作品:一块会实时刷新温湿度的小屏,插上电就独立工作,不用连着电脑看 monitor

这件事的意义不在技术难度,而在"完整"。它有传感器、有屏、有逻辑,插上电就独立工作。做完它,你桌上就多了一个你自己造的、真能用的东西——那种"我做出来了"的实感,比任何教程都更能把你留在硬件这条路上。这是 L2 的毕业设计。

读这篇前,确认你已经能让 DHT11 单独用 ESP_LOGI 打出温湿度(DHT11 那节)、能让 OLED 单独显示出 HelloOLED 那节)。两边都通过了,我们才好拼——尤其是 OLED,u8g2 移植组件的 HAL 回调你得先在那节接通过。

⚠️ 安全

本节给两个外设供电、接 GPIO。VCC 一律接 3.3V(下面会说为什么别接 5V),动线前断开 USB,接好再上电。


第一步:接线——两套设备,井水不犯河水

这个作品里有两个外设,它们用两种完全不同的通信方式,所以接线时谁也不影响谁:

器件 接 ESP32-S3 用的是什么总线
DHT11 DATA GPIO4 单总线(一根数据线自己玩)
OLED SDA / SCL GPIO8 / GPIO9 I2C(两根线,可挂多设备)
两者 VCC / GND 3.3V / GND(共用) ——

这张表和前两节是一致的,没有任何改动——DHT 那节 DATA 用的就是 GPIO4,OLED 那节 SDA/SCL 用的就是 GPIO8/GPIO9。你前面怎么接的,现在原样接上来即可。关键是理解它们为什么能共存

  • DHT11 走单总线:它和 ESP32-S3 之间只有 GPIO4 这一根数据线,自成一套微秒级时序,不占用 I2C。
  • OLED 走 I2C:SDA(GPIO8)、SCL(GPIO9)是一条"总线",所有 I2C 设备共用这两根线、靠各自的地址(OLED 是 0x3C)区分,理论上还能再挂别的 I2C 设备(比如 BMP280 气压计)。
  • 两者唯一的交集是电源:VCC 都接 3.3V、GND 都接 GND。面包板上把 3.3V 和 GND 各引一条"电源轨",两个器件都从轨上取电,最干净。
📌 说明

为什么强调"互不干扰"?因为新手容易有个错觉,以为接的东西多了会"打架"。不会。单总线和 I2C 是两套独立的电气协议,跑在不同引脚上,各读各的。你之所以能把它们拼起来,正是因为这种"互不打扰"是设计好的。这也是以后你往项目里加第三、第四个器件的底气。

🚧 避坑

选脚别踩 ESP32-S3 的雷区:DHT 的 GPIO4、OLED 的 GPIO8/GPIO9 都是普通脚,安全。避开 strapping 脚(GPIO0/3/45/46)、USB 脚(GPIO19/20)、连内部 flash/PSRAM 的 GPIO26–37——这些脚拿来做单总线或 I2C 会时好时坏甚至上不了电。这两节各自的接线表已经替你避开了,照接即可。

🚧 避坑

共用电源时别图省事把 DHT11 接到 5V。整个系统统一用 3.3V 供电——ESP32-S3 的 GPIO 是 3.3V 逻辑,DHT11 的 DATA 若被 5V 拉高,长期灌进 3.3V 引脚有损伤风险(这点在 DHT11 那节讲过)。同电压域,最省心。


第二步:先把两个组件都拉进来

这个工程要同时用到两个组件:读温湿度的 espressif/dht,和上屏的 u8g2 移植组件。ESP-IDF 5.x 的组件管理器一条命令拉一个,在工程目录下分别跑:

idf.py add-dependency "espressif/dht"
idf.py add-dependency "u8g2"
# u8g2 的具体移植组件名/版本以 components.espressif.com 上你选的那个为准

跑完它会在 main/ 下生成(或更新)idf_component.yml,把两个依赖都列上,大致是:

# main/idf_component.yml ——组件管理器据此一次拉齐两个依赖
dependencies:
  espressif/dht: "^1.0.0"
  u8g2: "*"          # 名称/版本以你实际选的 u8g2 移植组件为准
  idf:
    version: ">=5.0"
⚠️ 安全

下面整段是参考实现:DHT 部分按 espressif/dht 写,显示部分按 u8g2 移植组件写。但社区里 DHT 还有 UncleRus 的 esp-idf-lib dht、u8g2 的 ESP-IDF 移植也有好几个分支,函数名、回调签名、初始化宏可能略有差异。请以你实际拉到的那两个组件的 README / example 为准自校一遍,别照抄就上传。


第三步:完整整合代码(拼好的一整个工程)

下面这段是完整的、能直接编译烧录的整合工程,整段放进 main/main.c。它把前两节学的东西合到一个 app_main 里:先初始化 I2C 总线 + OLED + 把 DHT 引脚备好 → 进 while(1),每 2 秒读一次温湿度、清屏重画两行、推到屏上 → 读失败时屏上显示 Reading... 而不是乱码。

// 综合项目:DHT11(GPIO4) 读温湿度 → SSD1306 OLED(I2C, GPIO8/9) 每 2 秒刷新
#include "dht.h"                  // 来自 espressif/dht 组件
#include "driver/i2c_master.h"    // ESP-IDF 5.x 新版 I2C 主机驱动
#include "u8g2.h"                 // u8g2 移植组件
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdio.h>                // snprintf

static const char *TAG = "display";

#define DHT_GPIO      GPIO_NUM_4      // DHT11 DATA 脚
#define DHT_KIND      DHT_TYPE_DHT11  // DHT22 改成 DHT_TYPE_AM2301

#define I2C_PORT      I2C_NUM_0
#define PIN_SDA       GPIO_NUM_8
#define PIN_SCL       GPIO_NUM_9
#define SSD1306_ADDR  0x3C           // OLED 7 位地址,少数模块是 0x3D

static i2c_master_bus_handle_t s_bus;
static i2c_master_dev_handle_t s_dev;
static u8g2_t u8g2;

// ---- I2C 总线 + 挂 OLED 从设备(同 OLED 那节第二步)----
static void i2c_bus_init(void)
{
    i2c_master_bus_config_t bus_cfg = {
        .i2c_port          = I2C_PORT,
        .sda_io_num        = PIN_SDA,
        .scl_io_num        = PIN_SCL,
        .clk_source        = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,   // 板上没贴上拉电阻时靠它
    };
    ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &s_bus));

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address  = SSD1306_ADDR,
        .scl_speed_hz    = 400000,
    };
    ESP_ERROR_CHECK(i2c_master_bus_add_device(s_bus, &dev_cfg, &s_dev));
    ESP_LOGI(TAG, "I2C bus ready, OLED at 0x%02X", SSD1306_ADDR);
}

// ---- u8g2 的字节回调:攒满一次事务再用 i2c_master 发出去(同 OLED 那节)----
static uint8_t u8x8_byte_esp_i2c(u8x8_t *u8x8, uint8_t msg,
                                 uint8_t arg_int, void *arg_ptr)
{
    static uint8_t buf[32];
    static uint8_t idx;
    switch (msg) {
        case U8X8_MSG_BYTE_START_TRANSFER:
            idx = 0;
            break;
        case U8X8_MSG_BYTE_SEND: {
            uint8_t *p = (uint8_t *)arg_ptr;
            for (int i = 0; i < arg_int; i++) buf[idx++] = p[i];
            break;
        }
        case U8X8_MSG_BYTE_END_TRANSFER:
            i2c_master_transmit(s_dev, buf, idx, 1000 /*ms*/);
            break;
    }
    return 1;
}

// ---- u8g2 的延时回调:接到 FreeRTOS ----
static uint8_t u8x8_gpio_delay_esp(u8x8_t *u8x8, uint8_t msg,
                                   uint8_t arg_int, void *arg_ptr)
{
    if (msg == U8X8_MSG_DELAY_MILLI) {
        vTaskDelay(pdMS_TO_TICKS(arg_int));
    }
    return 1;
}

// ---- 把一行字画到屏的某个 y 上(小工具,少写重复)----
static void oled_show(const char *line1, const char *line2)
{
    u8g2_ClearBuffer(&u8g2);                        // 擦:清空内存画布
    u8g2_SetFont(&u8g2, u8g2_font_ncenB10_tr);      // 选清晰的英文字库
    u8g2_DrawStr(&u8g2, 0, 25, line1);              // 第一行(基线 y=25)
    if (line2) u8g2_DrawStr(&u8g2, 0, 50, line2);   // 第二行(基线 y=50)
    u8g2_SendBuffer(&u8g2);                          // 推:整帧一次性送屏
}

void app_main(void)
{
    // 1) 初始化 I2C + OLED
    i2c_bus_init();
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(
        &u8g2, U8G2_R0, u8x8_byte_esp_i2c, u8x8_gpio_delay_esp);
    u8x8_SetI2CAddress(u8g2_GetU8x8(&u8g2), SSD1306_ADDR << 1); // u8g2 用 8 位地址
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);                    // 0 = 唤醒(开屏)

    // 2) 主循环:读 → 判断 → 画 → 等
    while (1) {
        float humidity = 0, temperature = 0;
        // 一次读出温湿度;返回 ESP_OK 才是真数据,否则是失败码
        esp_err_t err = dht_read_float_data(DHT_KIND, DHT_GPIO,
                                            &humidity, &temperature);
        if (err == ESP_OK) {
            char line1[24], line2[24];
            // drawStr 只认字符串,先把浮点数拼成文字
            snprintf(line1, sizeof(line1), "Temp: %.1f C", temperature);
            snprintf(line2, sizeof(line2), "Humi: %.0f %%", humidity);
            oled_show(line1, line2);
            ESP_LOGI(TAG, "%.1fC  %.0f%%", temperature, humidity);
        } else {
            // 读失败:屏上给提示,别把脏值/乱码画上去
            oled_show("Reading...", NULL);
            ESP_LOGW(TAG, "DHT 读取失败 (%s)", esp_err_to_name(err));
        }
        vTaskDelay(pdMS_TO_TICKS(2000));   // DHT11 至少隔 1 秒读一次,留足余量
    }
}

在工程目录下一条命令编译、烧录、看日志:

idf.py build flash monitor

(第一次用别忘了先 idf.py set-target esp32s3。)

你应该看到什么

  • 编译烧录完成、自动进 monitor 后,OLED 上先闪一下 Reading...(开机第一次读还没拿到数据是正常的)。
  • 大约两秒后,屏上稳定显示两行,像这样:
Temp: 25.3 C
Humi: 60 %
  • 串口里同时滚出日志,每 2 秒一行:
I (1234) display: I2C bus ready, OLED at 0x3C
I (3245) display: 25.3C  60%
I (5251) display: 25.4C  60%
  • 之后每 2 秒刷新一次。对着传感器哈一口气,几秒内湿度数字会明显往上跳(常常蹿到 70%、80%),温度也会微动——这是验证它"真的在感知现实"的最直接办法。
  • 这块屏现在完全独立工作了:拔掉 USB、单用一个充电头给 ESP32-S3 供电,它照样显示。这就是"摆桌上能用"的意思。按 Ctrl + ] 退出监视。

如果屏一直停在 Reading...,或屏全黑——别急,后面故障排查表分两半治它。


第四步:逐段讲清楚——这个工程是怎么拼起来的

能跑只是开始,看懂才算掌握。这个工程其实就是把两节课的骨架"对齐到同一个 app_main":初始化各做一次,读和画放进同一个 while(1)

app_main 开头:把三件准备一次性做完

i2c_bus_init();                                 // I2C 总线 + 挂上 OLED
u8g2_Setup_ssd1306_i2c_128x64_noname_f(...);    // 告诉 u8g2:128×64/SSD1306/走 I2C
u8x8_SetI2CAddress(..., SSD1306_ADDR << 1);     // 注意左移 1 位
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);

while 之前这一段是"开店前的准备",只跑一次:建好 I2C 总线、把 OLED 挂上去、让 u8g2 知道屏的型号和回调、点亮屏。DHT 这边不需要单独 begin——espressif/dhtdht_read_float_data 每次调用时自己配置那一个数据脚,你只要在循环里调它就行。

💡 提示

那行 SSD1306_ADDR << 1 是 I2C 新手最常栽的坑:我们给 i2c_master 配的是"7 位地址"0x3C,而 u8g2 内部习惯用含读写位的"8 位地址",两套数法差一位。屏不亮、地址对不上时,先想想是不是这里。这点 OLED 那节细讲过。

while(1):读 → 判断 → 画 → 等,四步一循环

while(1) 是这个作品的"心跳",每 2 秒跳一次,每跳一次干四件事:

  1. dht_read_float_data(...) 向传感器要一次数据,温湿度通过指针 &humidity&temperature 写回,函数本身返回一个 esp_err_t 错误码。
  2. 判断err == ESP_OK 才说明这次读到的是真数据。
  3. :成功就把数字拼成两行字、oled_show 上屏;失败就显示 Reading...
  4. vTaskDelay(pdMS_TO_TICKS(2000)) 留足 DHT11 的采样时间,再进下一轮。

读失败为什么要专门处理

if (err == ESP_OK) {
    // 拼字符串、上屏
} else {
    oled_show("Reading...", NULL);
    ESP_LOGW(TAG, "DHT 读取失败 (%s)", esp_err_to_name(err));
}

物理世界的读取本来就会偶尔失败——时序被打断、校验和不过,DHT11 又是个慢性子。espressif/dht 不像 Arduino 那套返回一个 nan 让你去 isnan 判断,而是走 ESP-IDF 的惯用风格:返回值专门报告成功/失败,数据走出参。所以你判断的是 err == ESP_OK,不是去查温湿度是不是 nan

为什么非处理不可?如果不判断,直接把一次失败读出的脏值格式化上屏,你会看到乱跳的数字甚至离谱值闪过去——作品看起来就"坏了"。判断一下,失败时显示 Reading...,下次读成功了自然恢复。读失败那条还用了 ESP_LOGW(Warning 级)+ esp_err_to_name(err),把错误码翻成可读名字(比如 ESP_ERR_TIMEOUT),串口一眼看出失败原因。这一个 if 就是"能跑的代码"和"稳定的作品"之间的差别。面向真实硬件写代码,永远要给"读不到"留条退路。

格式化:把数字拼成人能看的一行字

char line1[24];
snprintf(line1, sizeof(line1), "Temp: %.1f C", temperature);

u8g2_DrawStr 只会画文字,不认识 float 数字,所以得先把数字"拼"成一串文字。snprintf 干的就是这事:

  • %.1f 表示"这个浮点数保留 1 位小数",于是 25.34 变成 "25.3";湿度用 %.0f 不留小数,60.0 变成 "60"
  • %% 是转义——想在文字里打一个真正的百分号 %,得写两个。
  • line1[24] 是预留的"文字容器",snprintf 比普通 sprintf 多了个长度上限,写不爆这个容器,更安全。

拼好的 line1line2 交给 u8g2_DrawStr 画到基线 y=25 和 y=50 两行——上下错开 25 像素,128×64 的屏正好放下两行不挤。注意 y 是文字基线(大致字底),不是顶部,所以写 25 而不是 0,写 0 字会跑到屏外被切掉。

擦 → 画 → 推:为什么是三连

u8g2_ClearBuffer(&u8g2);   // 擦:清空内存里的画布
// ... 各种 DrawStr ...
u8g2_SendBuffer(&u8g2);    // 推:把整块画布一次性送屏

u8g2 用的是"缓冲区"思路:你所有的 DrawStr 都画在单片机内存里的一块画布上,屏上此刻还没变;直到 SendBuffer() 才把整块画布一次性刷到屏上。好处是画面不会一块一块地闪,整帧一起换,看着干净。每轮循环开头 ClearBuffer 抹掉上一帧,重新画,避免新旧数字叠在一起糊成一团——我把这三步连同选字、画两行打包进 oled_show,循环里调一次就完整刷一帧。


第五步:让 AI 帮你拼装——把"零件知识"交给它

逻辑你现在能自己看懂了,但真要从零拼,AI 能帮你省掉查 API 的时间。诀窍是把芯片、框架、组件、引脚、行为一次性说清楚,它给的代码才好用。比如:

ESP32-S3 + ESP-IDF 5.x。DHT11 DATA 接 GPIO4,用 espressif/dht 组件的 dht_read_float_data;SSD1306 OLED 走 I2C(SDA=GPIO8, SCL=GPIO9, 地址 0x3C),用 u8g2 移植组件显示。写一个完整 main.c:app_main 里初始化 I2C 和 u8g2,while 循环每 2 秒读一次温湿度、在 OLED 上分两行显示,温度保留一位小数,dht 返回值不是 ESP_OK 时显示 "Reading..."。

一段好提示词里,这几样缺一不可:哪块芯片 + 哪个框架、哪个传感器接哪个引脚、用哪个组件、要什么行为、失败怎么办。把这些喂给它,它能直接给你一版能试的整合代码。

💡 提示

这正是 AI 时代做硬件的爽点:把零件级的知识(这个组件怎么调、那个引脚接哪)交给 AI 拼装,你的精力留给"我想做个什么"。但前提是你得能看懂它给的代码、能判断对错——尤其是 ESP-IDF 下组件 API 各分支有差异,AI 给的回调签名、初始化宏未必和你装的那个对得上。看得懂,AI 才是你的加速器;看不懂,它给的 bug 你连找都没法找。这也是为什么前两节要老老实实自己跑通。


第六步:故障排查——作品不对劲,按这张表查

整合之后出问题,无非是"哪一半没拼对"。照这个顺序查:

现象 最可能的原因 怎么办
屏完全不亮 I2C 地址不对 / 屏型号或分辨率选错 / SDA·SCL 接反 i2c_master_probe(bus, addr, ...) 探地址(常见 0x3C,少数 0x3D);确认是 128×64 的 SSD1306;核对 SDA→GPIO8、SCL→GPIO9;别忘 u8g2 侧地址要 << 1
屏亮,但一直停在 Reading... DHT 读不到数(dht_read_float_data 不返回 ESP_OK 查 DATA 是否真接 GPIO4、杜邦线插紧;裸传感器补 10kΩ 上拉到 3.3V;vTaskDelay 别低于 1 秒
编译报找不到 dht.h DHT 组件没拉进来 重跑 idf.py add-dependency "espressif/dht",再 idf.py reconfigure
编译报找不到 u8g2.h u8g2 组件没拉进来 确认 idf.py add-dependency 装了 u8g2 移植组件、idf.py reconfigure
屏满屏雪花 / 花屏 驱动型号或分辨率选错 Setup_ssd1306_i2c_128x64 要和屏真实分辨率一致;常见还有 128×32,选错就花
数字越画越乱、叠字 漏了 ClearBuffer / 字符串没拼好 确认每帧开头 ClearBuffer()、结尾 SendBuffer()(都在 oled_show 里);snprintf 别越界
温度数字明显离谱(如 -100) 型号选错(接的是 DHT22 当 DHT11 读) DHT_KIND 改成实际型号(DHT22 用 DHT_TYPE_AM2301
刷新太快、画面跳得心慌 vTaskDelay 太小 保持 pdMS_TO_TICKS(2000);DHT11 数据更新周期约 1 秒,读太快既无意义又容易失败
两个器件单独都好、合起来一个不工作 多半是电源没共好 确认 DHT11 和 OLED 的 GND 接在同一条 GND 轨、VCC 都到 3.3V
📌 说明

调试整合作品有个万能心法:分而治之。哪半边出问题,就把另一半临时旁路掉,单独验证有问题的那半。屏停在 Reading...,就回去单跑 DHT11 那节的纯串口版,确认传感器本身没坏、ESP_LOGI 能打出数;屏全黑,就先用 i2c_master_probe() 探地址,再单跑 OLED 那节Hello 例子。两半都各自确认好了,合起来自然就对了。


第七步:怎么往上扩——这块小屏是个好底座

这个作品做完,别急着拆。它是个极好的"地基",往上加一点点就是新项目:

  • 加联网:让它连上 WiFi,把温湿度发到云端或你的手机——人在公司就能看家里的温湿度。这是迈进 L3 联网与 IoT 的第一个真实场景。
  • 变成智能花盆:加一个土壤湿度传感器,屏上多显示一行土壤湿度;再接个小水泵,土干了自动浇——一个会自己照顾植物的花盆就成型了。
  • 加按钮切页:接一个按钮(去抖你已经在前面学过),按一下切换显示页面:第一页温湿度、第二页最高/最低记录、第三页时间。你的小屏从"只会显示一件事"升级成"有菜单的设备"。
  • 加阈值报警:在读到有效数据的分支里判断一下——湿度低于 40% 就 gpio_set_level 点亮一颗 LED 提醒该加湿了(这就用上了 DHT11 那节变体里学的输出能力)。屏负责显示,LED 负责醒目报警,分工就出来了。
💡 提示

上面这些扩展里,显示中文("温度""湿度")是很多人第一个想加的。中文要用 u8g2_DrawUTF8 而不是 DrawStr、换一个带中文的字库、源文件存 UTF-8 编码,固件会明显变大。第一块屏先用 Temp: / Humi: 英文最省事,流程顺了再加中文不迟——细节见 OLED 那节第七步。

每一个扩展都不大,但每加一个,你的作品就更像一件"产品"。


动手挑战

光跑通别人的代码不算掌握,挑一个改出来:

  1. 加最高温记录:用一个 static float 变量记住开机以来读到过的最高温度,在屏上加第三行(基线 y≈64 或把布局收紧)显示它。提示:每次 err == ESP_OK 时,把 temperature 和已记录的最高值比一下,更大就更新。
  2. 给屏加个表情:温度高于某个值(比如 30°C)多画一个 :(,舒适区间画 :)。这是你第一次让作品"根据数据做判断、改变显示"——离"智能"又近了一步。
  3. 加阈值报警 LED:照"往上扩"里的思路,湿度低于 40% 点亮 GPIO2 上的 LED,恢复了就灭。输入(读)+ 判断 + 输出(屏 + LED),凑齐一个完整设备的骨架。

卡住了,把你的完整代码、用的两个组件名和想要的效果一起发给 AI 让它帮你改。能描述清楚问题,本身就是进步。


小结 · L2 毕业了

到这一步,回头看看你走了多远:

  • 你能用 ESP-IDF 把传感器组件espressif/dht 读数)和显示组件(u8g2 上屏)整合成一个独立工作的完整工程,定时刷新。
  • 你理解了单总线(GPIO4)和 I2C(GPIO8/9)两套设备为什么能在一块 ESP32-S3 上互不干扰地共存。
  • 你写出了带 esp_err_t 返回码判断的代码——这是从"能跑的 demo"迈向"稳定的作品"的关键一跳,也是 ESP-IDF 产品级开发的惯用法。
  • 你知道了这块小屏怎么往上扩(联网、花盆、切页、报警),每个方向通向哪。

更重要的是,你的硬件现在已经能感知(传感器)、表达(显示)、执行(GPIO 输出)三件事齐活了——这是一个完整设备该有的全部基本能力。L2 这一阶梯,到此毕业。

下一步:让你的作品走出这块桌子。下一阶梯 L3 联网与 IoT 教你把它接入网络——远程看读数、远程下指令,做出真正意义上的"物联网设备"。想先看看整条学习路径走到哪了,去 学习路线图对照一下你的进度。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明