ESP32一站式智能家居:WiFi+BLE双模网关从零搭建

发布时间:2026/11/6 1:08:43

ESP32一站式智能家居:WiFi+BLE双模网关从零搭建 三年前我把树莓派从书架的智能家居盒子里拆下来时朋友以为我终于折腾够了。其实恰恰相反我是折腾明白了一个要7×24小时运行的家庭节点树莓派的开机时间、SD卡寿命和平均功耗放在“随手控个灯、看一下阳台温湿度、门开了推送给手机”这种场景里实在太重了。后来一路换到ESP32家里的智能家居系统才真正稳定下来——一块板子同时跑WiFi连接、MQTT协议、传感器采集还能通过BLE把周围体力弱的低功耗传感器收编进来一石三鸟。这篇文章想和你分享的就是这套基于ESP32的WiFiBLE一站式智能家居方案。内容包括为什么选双模芯片而不是树莓派、整体架构怎么搭、WiFi配网和断线自愈的实操细节、BLE接入低功耗传感器的方式、WiFi与BLE共存的调度与调优最后给一个可以直接抄作业的最小案例。适合手里有ESP32但不知道怎么串起一套系统的开发者也适合那些被各种品牌App搞到崩溃、想自己掌控设备的智能家居玩家。1. 为什么智能家居中枢我首选ESP32而不是树莓派1.1 双模无线才是家庭网关的刚需先解决一个最本质的问题智能家居设备为什么要同时有WiFi和BLE两种无线能力WiFi负责的是“主干道”。它带宽高、延迟低、穿墙后依然能保持稳定连接适合ESP32和路由器、云端、手机App之间的通信。家里要远程查看状态、下发控制指令走的都是这条主干道。BLE负责的是“末梢神经”。市面上大量的温湿度计、门磁、人体传感器、锁舌状态传感器本质上都是低功耗小设备。它们电池容量小、数据量小、唤醒频率低如果强行用WiFi直连路由器一两周就要换电池。BLE广播和连接模式的功耗能做到WiFi的几十分之一可以让这些小设备在纽扣电池上跑几个月甚至一年。把这两种能力做到同一颗芯片上才是“一站式”的关键词所在。我做第一版方案时候用过“STM32主控ESP8266透传”的组合两个芯片之间靠UART通信光是自定义协议、处理数据粘包、双端固件升级就折腾了两周。换成ESP32之后WiFi和BLE在同一个协议栈里应用层直接拿到封装好的数据开发量少了一个数量级。1.2 主流方案横向对比为了这篇方案我把手头几种常见的智能家居硬件方案的实测情况整理了一下方案无线能力典型功耗带多个传感器时的开发成本适合定位树莓派Zero WWiFi/BLE空闲约0.5W整机有SD卡损耗高Linux环境方便但也复杂家庭服务器/HASS主机树莓派3B/4BWiFi/BLE3-7W需要主动散热中中心网关/本地AISTM32ESP8266仅WiFiBLE需外加待机低但双芯联动复杂高在有BLE需求的场景下不推荐ESP8266 NodeMCU仅WiFi连接时约70mA低纯开关/灯这种简单节点ESP32经典版WiFiBLE双模连接时平均约80mAmodem sleep可压到20mA低本文主推ESP32-C3WiFiBLE5.0比经典版低约30%很低低成本传感器节点ESP32-S3WiFiBLE5.0比经典版略高低带屏控制面板/复杂网关树莓派毫无疑问适合当Home Assistant主机这种纯中心节点但让它去读DHT22、控制继电器、处理GPIO中断一方面大材小用另一方面Linux系统在异常断电后的文件系统损坏问题真的让人头疼。ESP32这类单片机系统断电即恢复没有SD卡、没有系统盘跑个两三年不重启都很正常这才是智能家居底层节点的基本修养。1.3 选型经验经典版、C3还是S3具体到ESP32系列我的建议是这样的做多节点分布式传感器、只想用BLE把数据收上来的选ESP32-C3。它是单核RISC-V成本低、功耗低支持BLE5.0但WiFi吞吐和并发处理能力弱一点。做带屏幕的控制面板、需要摄像头或复杂本地处理的选ESP32-S3双核240MHz加AI加速指令跑LVGL界面很流畅。做本文这种“一个节点集多角色”的方案经典版ESP32最稳。双核XtenSA架构WiFi和BLE协议栈调试资料最多踩坑时搜得到答案比什么都值钱。我家里实际部署的节点里3个经典版ES32负责客厅、卧室、阳台的传感器控制面板2个ESP32-C3挂在门磁和窗户磁上全部走BLE上报。到目前为止稳定性远高于之前用树莓派当所有节点的时期。2. 一套可扩展的本地智能家居架构ESP32在里面的位置2.1 三种角色的灵活切换“一站式”不是说一块ESP32要干所有事情而是指同一个硬件平台可以根据需要扮演不同角色设备节点板子上直连传感器比如DHT22温湿度、DS18B20温度、继电器控制灯光作为最终执行器。BLE网关ESP32通过BLE扫描周边低功耗设备比如把一个小米蓝牙温湿度计的数据解析出来再通过自身的WiFi转发到MQTT Broker。控制面板外接一个0.96寸OLED或者TFT屏显示当前各房间状态用几个按键完成本地控制。这个设计最重要的收益是不需要为不同品牌买不同的网关。以前家里一个灯光网关、一个传感器网关、一个门锁网关三个盒子三个App断了哪个都不知道。现在把ESP32刷上不同固件一个平台全搞定。2.2 数据链路和主题规划整套系统的数据流大概是这样的传感器(本地GPIO) ──→ ESP32 ──→ MQTT Broker(路由器/树莓派) ──→ HA / 手机App BLE外设(广播数据) ──→ ESP32扫描解析 ──→ 同上中心是MQTT Broker。我跑在路由器上用mosquitto资源占用极低。不建议在这个阶段把数据硬推到云端你的数据在自己家里流转响应快断网也不影响本地控制。远程访问等玩熟了再加也不需要买云服务器。MQTT主题是为了让多个设备协作不打架我前期踩过“全部主题都是home/device/state”的坑后来规范成这套主题方向用途home/{room}/{device}/state设备→Broker上报状态如温度、湿度、开关home/{room}/{device}/cmdBroker→设备接收控制指令如开灯、关灯home/{room}/{device}/event设备→Broker瞬时事件如“门被打开”home/{room}/{device}/online设备→Broker在线心跳每个制造相同固件的板子编译时通过宏定义注入{room}和{device}同一套代码在不同房间跑起来互不干扰。这个主题规划规则很基础但能避免一两年后设备多了管理混乱。2.3 为什么用MQTT而不是HTTP轮询很多第一次做智能家居的朋友会问为什么不上HTTP接口我用POST请求让设备开关灯不就行了HTTP的问题在于它是“客户端请求-服务器响应”模式。设备不能主动告诉你“门开了”你必须去轮询它。每个设备每3秒轮询一次10个设备就是每秒3.3个请求还要处理超时、重试、服务器负载。而MQTT是发布订阅模式事件到的时候才推一条消息平时一条流量都不产生。同样是30个设备MQTT的响应延迟能控制在100ms内HTTP轮询想做到同样的实时性系统负担会高一个量级。所以我在所有ESP32节点上都用PubSubClient库接MQTT稳定性和实时性都很理想。3. WiFi配网与断线自愈设备接入的第一道门槛也是翻车重灾区3.1 首次配网的三种方式对比给没有任何人机交互界面的ESP32配网是第一个容易翻车的环节。我试过三种方案分别是SmartConfig、AP配网、经典Web配网SmartConfig手机连上家里的2.4G WiFi用EspTouch App把SSID和密码通过广播报文发给ESP32。这种方式的优点是配网过程快但缺点也明显——它依赖于手机App和芯片厂家的协议兼容性偶尔会“明明发送成功但设备没收到”。AP配网 ESP32上电后先开启一个名叫ESP32-Config的AP热点手机连上这个热点后访问192.168.4.1页面在表单里输入WiFi信息板子自动重启。稳定性很好尤其适合给小白用。经典Web配网其实和AP配网差不多但页面更有控制感可以在页面里勾选“保存日志”或“重启”按钮对开发者排错很有用。我的建议是直接上AP配网。代码逻辑简单、不受手机型号影响、失败概率低。爱折腾的开发者还可以在页面里加一个“设置静态IP”的选项对后期局域网访问更友好。3.2 保存配置与断线重连的可复现代码配网成功后WiFi信息一定要持久化保存到Preferences或EEPROM里我一般用Preferences库#include Preferences.h Preferences prefs; prefs.begin(wifi, false); prefs.putString(ssid, ssid); prefs.putString(pass, password); prefs.putInt(static_ip, useStaticIp ? 1 : 0); prefs.end();很多教程只教连上WiFi不教断线恢复。实际把一个节点放在弱电箱旁边一天断几回是很正常的。我的重连策略是WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) { switch (event) { case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: reconnectAttempts; if (reconnectAttempts 10) { WiFi.reconnect(); } else { // 连续重连10次失败进入AP配网模式避免设备“失联” startAPMode(); } break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: reconnectAttempts 0; break; } });这里有个关键思路不能无限重连。如果一个节点已经在错误的位置或者路由器密码改了无限重连只会让日志刷屏、电池耗尽。连续失败一定次数后主动进入AP配网模式把自己暴露成热点等人来重新配置这才是务实的做法。另外一个常被忽略的坑是DHCP租约。有人为了设备稳定把路由器关了DHCP或者设了很短的租约时间结果ESP32拿不到IP就“消失”了。如果路由器上给每个节点做了静态IP保留记得在ESP32端也把IP、网关、DNS写进代码不然断网重连后可能因为拿不到IP长时间无法上线。这个现象排查时特别隐蔽先怀疑硬件最后发现是DHCP租约问题。3.3 信号与天线的土办法WiFi信号不好的问题有时候不需要加路由器。我实测下来ESP32的天线对周围金属极其敏感把它紧贴着金属外壳或者放在金属底盒里信号强度能掉10dBm以上。我现在所有节点都用塑料壳或者把PCB天线那一端朝外信号稳定度明显提升。有条件的话用RSSI打印日志Serial.printf(RSSI: %d dBm\n, WiFi.RSSI());信号低于-70dBm时不要想着调软件先调整布放位置。这是用最少的钱解决网络问题的土办法百试百灵。4. BLE接入实战扫描、广播与GATT直连4.1 BLE设备两大家族广播型和连接型智能家居里的BLE设备大体分成两类广播型只周期性地在广播信道发数据比如很多温湿度计、ibeacon定位标签。ESP32只需要被动扫描空中广播包解析厂商自定义数据段即可不需要建立连接。优点是不占连接资源缺点是一次广播能塞的数据很少一般就几字节。连接型作为GATT Server对外暴露服务Service和特征值Characteristic。ESP32作为Central角色连接上去之后读取或订阅特征值拿到数据。比如自制的BLE门磁传感器、BLE开关都适合做连接型。这两种角色在NimBLE库里都能实现。NimBLE是Espressif官方推荐的轻量级BLE协议栈比传统Bluedroid更省内存跑双模的时候尤其明显。4.2 扫描广播包的实战代码先看广播型设备怎么采集数据。以下代码完成BLE扫描#include NimBLEDevice.h class ScanCallbacks : public NimBLEScanCallbacks { void onResult(NimBLEAdvertisedDevice* advertisedDevice) override { // 按设备名过滤比如自家传感器统一叫 MY-SENSOR- if (strncmp(advertisedDevice-getName().c_str(), MY-SENSOR-, 10) 0) { std::string manufacturerData advertisedDevice-getManufacturerData(); // 厂商自定义数据前2个字节是公司ID后面是业务数据 if (manufacturerData.length() 4) { uint8_t temp (uint8_t)manufacturerData[2]; uint8_t humi (uint8_t)manufacturerData[3]; Serial.printf(Node: %s, temp%d, humi%d\n, advertisedDevice-getName().c_str(), temp, humi); } } } }; void setup() { NimBLEDevice::init(ESP32Hub); NimBLEScan* pScan NimBLEDevice::getScan(); pScan-setAdvertisedDeviceCallbacks(new ScanCallbacks(), true); pScan-setActiveScan(true); pScan-setInterval(100); pScan-setWindow(50); pScan-start(30); }setActiveScan(true)是主动扫描会向广播方发扫描请求能拿到更多数据但稍微费电。setInterval和setWindow控制扫描占空比这个参数在5.3节里还会用到。4.3 连接型BLE外设的读取如果BLE外设是连接型的比如你给门窗传感器刷了自定义固件ESP32需要建立GATT连接。核心流程是用NimBLEDevice::createClient()创建一个客户端connect(设备地址)完成后调用getService(服务UUID)得到服务再getCharacteristic(特征UUID)读取或subscribe(true)订阅Notify通知这里有一个调了很久才搞清楚的坑UUID是大小端敏感的。BLE规范里UUID的字节序和很多人习惯的写法是反的你从官方文档抄了一个服务UUID代码里写成常见的字符串形式结果连接半天找不到服务。排查时先在手机上用nRF Connect看一下真实的UUID再对照着填进代码里。4.4 自研BLE传感器如何广播数据给自家传感器写广播固件时我把广播包结构约定为偏移内容0-1厂商ID0xFFFF占位2类型0x01温度、0x02湿度、0x03门磁3数据温度负值偏移处理等用BLEAdvertising库发出去后网关ESP32扫描到就能解析。广播型单包数据有限所以不要想着塞复杂协议够用就行。如果数据量大可以拆成多个广播包轮发网关侧按设备地址和序号重组。5. WiFi与BLE共存协议栈调度、任务分配和功耗平衡5.1 同频干扰不是玄学是时分复用ESP32的WiFi和BLE都工作在2.4GHz频段共用同一根天线。射频前端不会同时收发两个协议所以芯片底层靠时分复用调度。你在应用层感觉不到但两个协议栈会互相挤压。实测现象BLE扫描密度很高时WiFi的ping延迟明显上升。我做过一组对比数据BLE扫描状态WiFi ping平均RTT丢包率不扫描6ms0%持续主动扫描(100%占空比)42ms0.5%扫描30ms暂停1s11ms0%结论很简单不要让BLE扫描疯狂转。对于家里那些温湿度计每30秒全量扫一轮就够了。代码里用pScan-start(30, false)再加一个定时器每30秒触发一次效果完全够用WiFi延迟几乎不受影响。5.2 双核任务固定到不同核心上经典ESP32有双核WiFi和BLE协议栈主要在core 1上跑。如果业务代码不加区分地在两个核上跑有时候出现奇怪的高延迟。我的习惯是把协议无关的业务逻辑固定到core 0调用WiFi/BLE相关API尽量放在core 1上或者至少在相同优先级的任务之间保持清晰划分。xTaskCreatePinnedToCore(handleSensorTask, sensor, 4096, NULL, 1, sensorTaskHandle, 0); xTaskCreatePinnedToCore(handleMqttTask, mqtt, 8192, NULL, 1, mqttTaskHandle, 1);这样WiFi协议栈处理、MQTT长连接、BLE扫描都在一个核上传感器读取、业务逻辑在另一个核上稳定性好了很多。5.3 内存和日志控制也别忽略经典ESP32的RAM只有520KBBLE WiFi MQTT TLS全部启用时非常紧张。我给出三个压内存的空间使用NimBLE而不是Bluedroid能省出约100KB。关闭不用的协议服务sdkconfig里把不需要的BLE角色、WiFi Security类型裁剪掉。日志级别从Info改成Warning尤其在发布固件时。串口日志在高频打印场景下会拖慢系统。这三个操作做完至少能多出120KB的可用堆内存崩溃概率直线下降。5.4 电池供电时的功耗调节思路如果有的节点是电池供电不能所有节点都“功耗自由”。我的做法按场景分级插座供电的节点家庭中枢/灯控不关心功耗正常全速跑电池供电的传感器节点开启Modem Sleep让WiFi在无数据时休眠需要上报时再唤醒。实测经典ESP32连接WiFi并保持TCP长连接时Modem Sleep可以把平均电流从80mA压到20mA左右。如果再加长休眠周期用esp_light_sleep_start()还能再降一截。BLE广播设备的功耗主要看广播间隔。100ms广播间隔时平均电流约10mA1s间隔时约1-2mA10s间隔时功耗可以忽略。所以传感器固件里的广播间隔按数据实时性需求来能慢则慢。6. 一个可抄作业的最小落地案例温湿度采集灯光控制门磁报警6.1 硬件接线与引脚安全组合演示案例选择三个最常见形态温湿度采集、灯光开关控制、门磁状态上报。接线表如下模块引脚说明DHT22温湿度传感器DATA→GPIO4VCC接3.3VGND接GND继电器模块IN→GPIO16控制灯光COM接外置电源门磁开关SIG→GPIO26内部上拉接地触发板载LEDGPIO2状态反馈这里要特别注意GPIO12、GPIO15是Flash启动相关引脚慎用GPIO0是BOOT引脚别接上拉设备。我第一次做的时候把继电器接到了GPIO0上电后经常进不了下载模式排查了很久才发现是引脚冲突。6.2 核心固件代码片段完整工程太大这里给关键链路。第一部分周期读取DHT22并发布MQTT。#include DHT.h DHT dht(4, DHT22); void publishSensorData() { float t dht.readTemperature(); float h dht.readHumidity(); if (isnan(t) || isnan(h)) { Serial.println(DHT read failed); return; } mqttClient.publish(home/living/temp, String(t, 1).c_str()); mqttClient.publish(home/living/humi, String(h, 1).c_str()); }这里有个很实用的细节DHT传感器两次读取间隔必须大于1秒否则返回的数据会一直是上一次的旧值。所以定时器触发频率最高也就1Hz别写成100ms一次。第二部分订阅灯光控制指令并控制继电器。void mqttCallback(char* topic, byte* payload, unsigned int length) { String cmd; for (unsigned int i 0; i length; i) { cmd (char)payload[i]; } if (String(topic) home/living/light/cmd) { if (cmd ON) digitalWrite(16, HIGH); else if (cmd OFF) digitalWrite(16, LOW); } }第三部分门磁开关的外部中断检测。volatile bool doorOpened false; void IRAM_ATTR doorISR() { doorOpened true; } void setup() { pinMode(26, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(26), doorISR, FALLING); } void loop() { if (doorOpened) { delay(50); // 消抖 if (digitalRead(26) LOW) { mqttClient.publish(home/living/door/event, OPEN); } doorOpened false; } }门磁用中断监测能保证在睡眠状态下也能被唤醒。消抖时不能直接用delay(50)阻塞太久这里为了演示用了最简单的方式生产环境建议用millis()记录时间差做非阻塞消抖。6.3 联动的玩法从单一控制到自动化场景硬件数据都进了MQTT之后联动规则就可以在Home Assistant里做也可以直接在板端做局部逻辑。她的好处是高实时——控制链路不依赖外部系统。举个例子温度高于32℃时自动启动排风扇门磁报警事件触发时客厅灯同步亮起5秒。前者适合放在HA里定时判断后者要求毫秒级响应放在板端更可靠。我在使用中发现有了统一MQTT主题后新设备接入只需改发布/订阅的{room}和{device}字段其他代码完全复用。这也是这个方案“一站式”最核心的价值所在。7. 调试工具、日志技巧与批量部署时的避坑清单7.1 烧录和开发环境里的小细节开发环境这块很多新手卡在Arduino IDE装ESP32支持包这一步。如果下载官方包慢可以配置国内镜像源这是最省时间的一步。装好之后在“开发板管理器”里搜索ESP32并选对应型号经典版一般选ESP32 Dev Module烧录前确认端口和板子型号对上。手动焊的板子如果上电后烧录失败多半是GPIO0没有在烧录瞬间拉低。解决办法是烧录前按住板上的BOOT按键或者检查GPIO0上有没有外加电路干扰。7.2 日志级别和远程诊断发布固件前把日志级别调到Warning平时调试时再开Info。这个习惯能避免很多干扰信息。串口监视器默认115200波特率别改错。部署到实际角落的节点如果出了问题最痛苦的就是“看不见日志”。我后来给每个节点加了OTA升级能力再配一个简单的状态LED快闪慢闪规则快闪WiFi未连接、慢闪MQTT未连接、常亮一切正常。这样不需要开电脑远远扫一眼就知道哪个节点掉线了。7.3 部署全屋节点前最后过一遍的检查清单检查项说明供电余量5V/2A适配器只能带3-4个板子继电器等大功率器件建议独立供电天线位置天线不要贴金属远离大块铜箔/地线GPIO冲突避免使用GPIO0/12/15做普通输入输出MQTT主题唯一每个节点的room/device字段不能重复动态IP还是静态IP路由器做DHCP保留设备端保持自动获取断网恢复更快OTA固件备份保留旧固件回滚通道远程升级失败不至于“变砖”标签管理在每一块板子背面写清房间名和IP尾号这个不写后期追悔莫及这串坑我基本是一个一个踩过来的。起初总想着一口气多接几个板子结果供电不足导致节点反复重启、继电器误动作后来老老实实按表逐项核验部署效率反而高了很多。最后分享一个实际感受这套双模方案最值钱的地方不是“能把数据传到手机”而是“把家里各种协议的传感器统一进一套主题系统”。再往后如果想让硬件升级ESP32的固件OTA和底座都是现成的传感器端因为走的是蓝牙广播很多低功耗设备还能继续服役。按这个思路做几年家里的智能家居反而越来越好维护。
延伸阅读

更多相关文章

2026/11/6 1:08:43

偶发故障排查实战:串口假故障、蓝牙断连与烧录批次对照

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

2026/11/6 1:03:39

I2C死锁与时钟延展:嵌入式系统鲁棒性实战指南

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

2026/11/6 1:08:43

U-Boot设备模型初始化全解析:从board_init_r到dm_init_and_scan

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

2026/11/6 1:08:43

I2C时钟延展与死锁恢复:嵌入式总线鲁棒性实战

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

2026/11/6 1:08:43

ESP32一站式智能家居:WiFi+BLE双模网关从零搭建

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

2026/11/6 1:08:43

偶发故障排查实战:串口假故障、蓝牙断连与烧录批次对照

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

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

2026/11/6 1:03:39

MCU晶振电路设计全解析:从负载电容计算到PCB布局调试

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

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑