ESP32-C6自托管温度告警系统:从传感器到MQTT全链路实战
1. 为什么我要用 ESP32-C6 自己搭一套温度告警家里有台老式冰箱放在车库夏天车库温度能飙到三十七八度冰箱压缩机拼命工作冷藏室温度却时不时飘到十度以上。等发现的时候一抽屉的食材已经报废了。市面上现成的 WiFi 温度计我也看过便宜的只能本地看数据贵一点的带云平台推送但数据要经过厂商服务器断网就抓瞎而且订阅费一年下来够买好几块开发板了。所以我的需求很明确温度数据完全留在自己手里超阈值能主动通知我不依赖任何第三方云服务。这就是所谓 Self-Hosted自托管的核心思路——数据采集、存储、告警逻辑全部跑在本地网络里外网断了也不影响核心功能。选 ESP32-C6 这块板子是因为它同时支持 WiFi 6 和 802.15.4Thread/Zigbee 的底层协议功耗控制比老款 ESP32 好不少价格也就二十来块钱。我打算让它做三件事定时读温度传感器、把数据发到本地服务器、在温度越界时触发告警。整套系统跑起来之后我把它扩展到了婴儿房、阳台花架和路由器机柜一共四个监测点成本加起来不到两百块。这篇文章我会把整个搭建过程拆开讲清楚硬件怎么选、传感器怎么接、固件怎么写、本地服务端怎么部署、告警怎么推送到手机。中间踩过的坑我也会原样写出来尤其是传感器读数和 WiFi 重连这两块坑最深。2. 硬件选型与接线别小看那几根杜邦线2.1 ESP32-C6 开发板的几个版本差异市面上能买到的 ESP32-C6 开发板主要有几类官方 Espressif 的 DevKitC-1、Seeed Studio 的 XIAO ESP32-C6、还有各种第三方小板。我手头用的是 DevKitC-1因为它把 GPIO 都引出来了接线方便而且板载 USB-JTAG 调试烧录和看串口日志一根 Type-C 线搞定。XIAO ESP32-C6 体积小很多适合塞进小盒子里但它的 GPIO 数量少如果你要同时接传感器和 OLED 屏引脚可能不够用。第三方板子便宜但要注意有些用的是 CH340 串口芯片驱动装起来麻烦一点而且天线布局不一定好WiFi 信号弱的时候会频繁掉线。我建议第一次玩的话直接上 DevKitC-1省心。等跑通了再考虑用 XIAO 做小型化部署。2.2 温度传感器的选择DS18B20 vs DHT22 vs SHT31温度传感器这块我试过三种直接给结论传感器接口精度价格适用场景DS18B20单总线1-Wire±0.5°C约 5 元防水探头适合液体/土壤/冰箱DHT22单总线自定义协议±0.5°C湿度 ±2%约 15 元室内温湿度便宜但响应慢SHT31I2C±0.3°C湿度 ±2%约 25 元高精度场景响应快车库冰箱那个场景我用了 DS18B20 的防水探头因为要贴在冰箱内壁偶尔会结露。婴儿房用的是 SHT31精度高而且 I2C 接线简单两根线搞定。阳台花架用的是 DHT22因为要同时看湿度判断要不要浇水。注意DS18B20 一定要买带防水封装的正品山寨探头用几个月就会进水导致读数漂移。我第一批买的便宜货三个月后读数比实际高了 3 度差点把冰箱温度调太低。2.3 接线细节与上拉电阻DS18B20 的数据线需要接一个 4.7kΩ 的上拉电阻到 3.3V这个电阻不能省。我一开始偷懒没接读数时有时无折腾了半天才想起来是总线没有上拉。ESP32-C6 的 GPIO 内部上拉电阻大概在 45kΩ 左右太弱了驱动不了 1-Wire 总线。具体接线DS18B20 红线VCC接 3.3V黑线GND接 GND黄线DATA接 GPIO4同时通过 4.7kΩ 电阻上拉到 3.3VSHT31 的 I2C 接线VCC 接 3.3VGND 接 GNDSDA 接 GPIO8SCL 接 GPIO9ESP32-C6 的 I2C 引脚可以自定义我用的是默认的 GPIO8/GPIO9这两个脚在 DevKitC-1 上位置好找。2.4 供电方案别用劣质 USB 充电头ESP32-C6 在 WiFi 传输瞬间电流能到 300mA 左右如果用那种几块钱的 USB 充电头电压会瞬间跌落导致板子重启。我一开始用了一个旧手机充电头串口日志里时不时出现 brownout detector 的报错换了正规品牌的 5V/2A 充电头之后就再没出现过。如果你要部署在插座够不着的地方可以用 18650 电池加 TP4056 充电模块但要注意 WiFi 持续工作的功耗大概在 80mA 左右2000mAh 的电池撑不了一天。我的做法是车库和机柜这种有插座的地方直接插电阳台花架用太阳能板加 18650白天充电晚上用勉强够。3. 固件开发从点灯到稳定上报的完整路径3.1 开发环境搭建与库依赖我用的是 Arduino IDE 2.x 配合 ESP32 官方板级支持包。安装步骤在 Arduino IDE 的首选项里附加开发板管理器网址填入 Espressif 的官方 JSON 地址在开发板管理器里搜索 esp32安装最新版我写这篇文章时是 3.0.x开发板选择 ESP32C6 DevKitC-1需要的库OneWire和DallasTemperature驱动 DS18B20Adafruit SHT31 Library驱动 SHT31DHT sensor library驱动 DHT22ArduinoJson构造上报的 JSON 数据PubSubClientMQTT 通信这些库都可以在库管理器里直接搜到。注意DallasTemperature依赖OneWire装的时候会提示一并安装。3.2 传感器读取的稳定性处理传感器读数不能只读一次就用尤其是 DS18B20单次读取偶尔会返回 -127°C 这种明显错误的值。我的做法是连续读三次去掉最大值和最小值取中间值。代码大概长这样float readDS18B20Stable() { float readings[3]; for (int i 0; i 3; i) { sensors.requestTemperatures(); readings[i] sensors.getTempCByIndex(0); delay(100); } // 简单排序取中值 float temp; if ((readings[0] readings[1] readings[1] readings[2]) || (readings[2] readings[1] readings[1] readings[0])) { temp readings[1]; } else if ((readings[1] readings[0] readings[0] readings[2]) || (readings[2] readings[0] readings[0] readings[1])) { temp readings[0]; } else { temp readings[2]; } return temp; }SHT31 的读数相对稳定但 I2C 总线在长线缆上容易受干扰。如果你的传感器离板子超过半米建议用屏蔽线并且在 SDA/SCL 上各加一个 100nF 的电容到地。3.3 WiFi 连接与断线重连策略ESP32-C6 的 WiFi 稳定性整体不错但路由器重启或者信号弱的时候还是会掉线。我的重连策略是启动时尝试连接最多等 15 秒连接成功后启动一个定时器每 30 秒检查一次连接状态如果断开先尝试重连连续失败 5 次后重启板子这里有个坑WiFi.reconnect()在某些情况下会卡住导致看门狗复位。我后来改成先WiFi.disconnect()再WiFi.begin()稳定很多。void checkWiFi() { if (WiFi.status() ! WL_CONNECTED) { unsigned long now millis(); if (now - lastReconnectAttempt 30000) { lastReconnectAttempt now; WiFi.disconnect(); delay(100); WiFi.begin(ssid, password); reconnectCount; if (reconnectCount 5) { ESP.restart(); } } } else { reconnectCount 0; } }3.4 MQTT 上报与本地缓存数据上报我用的是 MQTT因为轻量、支持 QoS、而且本地 Mosquitto broker 部署简单。上报频率是每 30 秒一次主题格式是home/sensor/{location}/temperature。如果 MQTT 连接断了数据不能丢。我在内存里开了一个环形缓冲区存最近 20 条读数。连接恢复后先把缓存的数据补发出去再发当前读数。这样即使网络抖动几分钟数据也不会断档。struct Reading { float temp; unsigned long timestamp; }; Reading buffer[20]; int bufferIndex 0; int bufferCount 0; void bufferReading(float t) { buffer[bufferIndex].temp t; buffer[bufferIndex].timestamp millis(); bufferIndex (bufferIndex 1) % 20; if (bufferCount 20) bufferCount; } void flushBuffer() { int start (bufferIndex - bufferCount 20) % 20; for (int i 0; i bufferCount; i) { int idx (start i) % 20; publishReading(buffer[idx].temp, buffer[idx].timestamp); } bufferCount 0; }提示环形缓冲区的大小要根据你的上报频率和最长断网时间估算。30 秒一次、断网 10 分钟就需要至少 20 个槽位。如果内存紧张可以适当降低上报频率。4. 本地服务端数据落地与告警逻辑4.1 Mosquitto InfluxDB Grafana 的组合服务端我跑在一台树莓派 4B 上系统是 Raspberry Pi OS Lite。三个核心组件MosquittoMQTT broker接收 ESP32 上报的数据InfluxDB时序数据库存温度历史数据Grafana可视化面板看趋势曲线再加一个自己写的 Python 脚本做告警判断整体架构很清晰。这套组合的好处是全部开源、社区资料多、树莓派就能跑得动。Mosquitto 安装sudo apt update sudo apt install mosquitto mosquitto-clients sudo systemctl enable mosquitto默认配置只监听本地要让 ESP32 能连上需要改/etc/mosquitto/conf.d/default.conflistener 1883 allow_anonymous true注意allow_anonymous true只适合内网环境。如果你的网络里有不信任的设备一定要配置用户名密码认证并且启用 TLS。4.2 用 Python 桥接 MQTT 到 InfluxDB我写了一个 Python 脚本订阅 MQTT 主题把数据写入 InfluxDB。用到的库是paho-mqtt和influxdb-client。import paho.mqtt.client as mqtt from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS INFLUX_URL http://localhost:8086 INFLUX_TOKEN your-token INFLUX_ORG home INFLUX_BUCKET sensors client InfluxDBClient(urlINFLUX_URL, tokenINFLUX_TOKEN, orgINFLUX_ORG) write_api client.write_api(write_optionsSYNCHRONOUS) def on_message(client, userdata, msg): topic msg.topic payload float(msg.payload.decode()) location topic.split(/)[2] point Point(temperature).tag(location, location).field(value, payload) write_api.write(bucketINFLUX_BUCKET, recordpoint) mqtt_client mqtt.Client() mqtt_client.on_message on_message mqtt_client.connect(localhost, 1883) mqtt_client.subscribe(home/sensor//temperature) mqtt_client.loop_forever()这个脚本用 systemd 做成服务开机自启挂了自动重启。4.3 告警判断逻辑阈值、持续时间与去重告警不能一超阈值就发否则温度在阈值附近波动的时候会疯狂推送。我的逻辑是温度超过阈值持续 2 分钟才触发告警告警触发后30 分钟内不重复发送同一位置的告警温度回落到阈值以下持续 5 分钟发送恢复通知ALERT_THRESHOLD 30.0 ALERT_DURATION 120 # 秒 COOLDOWN 1800 # 秒 alert_state {} # location - {since: timestamp, last_alert: timestamp, active: bool} def check_alert(location, temp, now): state alert_state.setdefault(location, {since: None, last_alert: 0, active: False}) if temp ALERT_THRESHOLD: if state[since] is None: state[since] now elif now - state[since] ALERT_DURATION and not state[active]: if now - state[last_alert] COOLDOWN: send_alert(location, temp) state[last_alert] now state[active] True else: state[since] None if state[active] and temp ALERT_THRESHOLD - 1: send_recovery(location, temp) state[active] False这个逻辑跑下来误报基本没有了。之前没有持续时间判断的时候冰箱开门那几秒温度上升就会触发告警烦得很。4.4 告警推送渠道Bark、Telegram 与邮件推送渠道我试过三种BarkiOS 上的开源推送工具自建服务器或者用官方服务器都行延迟低支持自定义铃声Telegram Bot跨平台免费但国内网络环境下需要额外处理邮件最通用但延迟高有时候十几分钟才收到我现在主力用 Bark备用邮件。Bark 的推送就是一个 HTTP GET 请求import requests def send_alert(location, temp): title f温度告警{location} body f当前温度 {temp:.1f}°C超过阈值 requests.get(fhttps://api.day.app/{BARK_KEY}/{title}/{body}?soundalarm)如果你不想依赖任何外部服务可以在树莓派上跑一个 ntfy 服务手机装 ntfy 客户端订阅主题完全自托管。ntfy 的部署也很简单一个二进制文件加一个 systemd 服务就搞定。5. 实测中踩过的坑与排查过程5.1 传感器读数跳变从怀疑硬件到定位电源噪声部署到车库的第二天我发现温度曲线每隔十几分钟就有一个尖峰从 5°C 突然跳到 25°C 再跳回来。一开始怀疑是 DS18B20 探头质量问题换了一个还是一样。后来用示波器看 3.3V 电源轨发现 WiFi 发射瞬间有大约 200mV 的纹波。解决方案是在传感器的 VCC 和 GND 之间并一个 100μF 的电解电容加一个 100nF 的陶瓷电容纹波被压到了 50mV 以内跳变消失。这个坑让我明白ESP32 的射频部分对电源质量很敏感传感器供电一定要做退耦。5.2 MQTT 断连后数据丢失环形缓冲区的正确用法前面提到我用了环形缓冲区但第一版实现有个 bug缓冲区满了之后新数据覆盖旧数据但bufferCount没有正确更新导致补发的时候发了一堆重复数据。后来改成用bufferCount和bufferIndex两个变量配合并且补发完成后重置bufferCount问题解决。还有一个细节补发的时候如果数据量太大一次性发出去会阻塞主循环导致看门狗复位。我的做法是每次补发 5 条间隔 100ms分批发完。5.3 WiFi 信道拥堵导致的延迟抖动我家路由器 2.4GHz 频段上设备很多ESP32-C6 虽然支持 WiFi 6但路由器是老款的话还是跑在 WiFi 4 模式。晚上高峰期 MQTT 上报延迟能到好几秒。后来我把路由器的 2.4GHz 信道固定到 1 或者 11避开自动选择的拥挤信道延迟稳定在了 200ms 以内。如果你的环境 2.4GHz 特别拥挤可以考虑用 ESP32-C6 的 802.15.4 功能走 Thread 协议但那就需要额外的边界路由器复杂度上来了。一般家庭环境把信道调好就够了。5.4 树莓派 SD 卡损坏InfluxDB 数据目录迁移跑了大概三个月树莓派的 SD 卡挂了InfluxDB 数据全丢。虽然温度数据不是特别重要但丢了还是不爽。后来我把 InfluxDB 的数据目录移到了一个 USB 3.0 的固态硬盘上并且配置了每天自动备份到 NAS。迁移步骤停止 InfluxDB 服务把/var/lib/influxdb复制到 SSD 挂载点修改配置文件里的数据目录路径重新启动服务SD 卡只用来跑系统读写量大的数据全部走 SSD这样寿命长很多。6. 扩展思路从温度告警到全屋环境监测这套系统跑稳之后我陆续加了一些扩展。阳台花架那个节点加了土壤湿度传感器浇水提醒也走同一套告警逻辑。机柜节点加了电流传感器监测整机功耗。婴儿房节点加了 CO2 传感器晚上关窗睡觉 CO2 浓度超过 1500ppm 就提醒开窗。所有传感器数据都进同一个 InfluxDBGrafana 上做一个总览面板一眼能看到全屋状态。告警逻辑也统一到了一个 Python 服务里配置用 YAML 文件管理加新传感器只需要改配置不用动代码。如果你也想搭一套我的建议是从一个节点开始把整条链路跑通再复制到其他位置。一开始不要追求功能大而全稳定可靠比什么都重要。我第一版固件写了三天调稳定性调了两周真正好用的系统都是磨出来的。

相关新闻

JavaWeb考试系统实战:事务一致性与高并发设计

JavaWeb考试系统实战:事务一致性与高并发设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 2:04:22 阅读更多 →
前端未死,边界已变:从支付宝 AFX 打散看 2026 前端工程师的 AI 全栈转型与 TaoToken 统一 Key 通道

前端未死,边界已变:从支付宝 AFX 打散看 2026 前端工程师的 AI 全栈转型与 TaoToken 统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 2:04:21 阅读更多 →
STM32启动文件实战:从向量表到Bootloader的嵌入式底层真相

STM32启动文件实战:从向量表到Bootloader的嵌入式底层真相

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 2:04:21 阅读更多 →

最新新闻

DeepSeek+Coze AI获客智能体实战:从意图识别到工作流编排

DeepSeek+Coze AI获客智能体实战:从意图识别到工作流编排

简介:这份资源围绕DeepSeek与Coze组合,系统讲解从0到1搭建AI获客智能体的完整流程,面向传统行业中小老板、创业者与个人IP、销售运营人员,帮助解决短视频创作与获客难题。内容涵盖智能体定位与目标、业务流程梳理与痛点分析、场景…

2026/10/9 3:00:51 阅读更多 →
手写SMTP+POP3协议栈:TCP连接与状态机实战

手写SMTP+POP3协议栈:TCP连接与状态机实战

简介:本资源是一套基于Java Web技术实现的模拟邮件系统课程设计项目,面向计算机网络、Web开发初学者及高校课程设计实践者,聚焦SMTP/POP3协议原理与应用,解决邮件收发核心流程的编程实现问题。项目采用MVC架构,以Maven…

2026/10/9 3:00:51 阅读更多 →
Kanban:自托管的个人计划与待办看板

Kanban:自托管的个人计划与待办看板

项目仓库: https://github.com/Akvicor/kanban桌面客户端: https://github.com/Akvicor/kanban-app个人博客: https://www.ksyaki.com/archives/kanban-zi-tuo-guan-de-ge-ren-ji-hua-yu-dai-ban-kan-ban Docker 镜像: ghcr.io/akvicor/kanban Kanban 是一个自托管的个人看板…

2026/10/9 3:00:51 阅读更多 →
LmyExamExport.rar:.NET考试数据Excel导出中间件实战指南

LmyExamExport.rar:.NET考试数据Excel导出中间件实战指南

简介:这是一款面向高校教师与教育信息化开发者的蓝墨云试题导出工具,解决平台仅支持导入、无法导出试题数据的痛点,支持将蓝墨云测试数据解析为结构化试题库,并可选是否显示答案,适用于教学资源归档、试卷复用及学生自…

2026/10/9 3:00:51 阅读更多 →
信道估计数据集使用全攻略:从解压到PyTorch加载与避坑

信道估计数据集使用全攻略:从解压到PyTorch加载与避坑

简介:信道估计是无线通信系统接收端的关键环节,准确估计信道响应可显著提升数据传输性能。这份压缩包面向通信工程研究者与相关专业学生,整合了LS与MMSE两类经典估计算法的MATLAB实现,以及信道估计、均衡前后效果、LS与MMSE性能对…

2026/10/9 3:00:51 阅读更多 →
华为设备引导加载程序解锁工具实战:从驱动环境到解锁码写入的完整链路

华为设备引导加载程序解锁工具实战:从驱动环境到解锁码写入的完整链路

1. 解锁工具到底在解决什么问题第一次接触手机解锁工具的人,脑子里往往有个模糊的印象:插上数据线、点一下按钮,锁就开了。实际远没有这么简单。所谓“解锁”,在不同语境下指向完全不同的操作——有的是解除运营商网络锁&#xff…

2026/10/9 2:59:51 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →