资讯动态

ESP32-C6物联网开发实战:从WiFi连接到Adafruit IO双向通信

发布时间:2026/8/15 6:52:38 来源:尧图企业网站定制
1. 项目概述与核心价值拿到一块新的ESP32-C6开发板第一件事是什么对于物联网开发者来说答案几乎是一致的先连上WiFi。这看似简单的第一步却是整个项目能否“活”起来的关键。它不仅仅是让设备接入你的家庭网络更是打通了物理世界与数字世界的桥梁让传感器数据得以“说话”让远程控制指令得以“抵达”。我经手过不少项目很多卡壳的环节都出在最基础的网络连接上要么是配置不对要么是库版本不兼容要么是网络环境太复杂。所以今天我们就以Adafruit的ESP32-C6 Feather开发板为例从头到尾、掰开揉碎地走一遍WiFi连接测试并在此基础上实现一个完整的、双向通信的Adafruit IO物联网应用。你会看到从点亮一个LED到让它在千里之外听你指挥中间需要跨越哪些技术细节和实操陷阱。这个实践的核心价值在于“全链路打通”。我们不止步于让开发板连上WiFi、Ping通谷歌那只是证明了硬件和基础驱动没问题。我们要更进一步利用CircuitPython的便捷性将设备安全地接入Adafruit IO这个成熟的物联网平台实现数据的“上传”发布到云端和“下达”从云端接收控制指令。在这个过程中你会接触到现代嵌入式开发中几个至关重要的概念环境变量管理从secrets.py到settings.toml、MQTT协议的应用、以及如何编写健壮的、能应对网络波动的连接代码。无论你是想做一个环境监测站还是智能开关或是任何需要联网的创意项目这里面的套路都是相通的。2. 环境准备与核心工具解析2.1 硬件与软件栈选型工欲善其事必先利其器。这次我们选择的硬件是Adafruit ESP32-C6 Feather。选择它有几个理由首先ESP32-C6是乐鑫较新的产品支持WiFi 6和蓝牙5性能与能效比不错其次Adafruit的Feather系列生态完善板载了STEMMA QT连接器、锂电池管理芯片和NeoPixel LED开箱即用非常适合快速原型开发最后也是最重要的一点Adafruit为其提供了极其完善的CircuitPython支持省去了大量底层配置的麻烦。在软件层面我们放弃传统的Arduino C选择CircuitPython。对于物联网应用原型开发CircuitPython的优势是压倒性的无需编译代码修改后保存即生效通过串行REPL交互式解释器可以实时调试拥有海量即插即用的“驱动”库他们叫lib。当然如果你的项目对实时性、内存占用有极致要求最终产品可能仍需回归Arduino或ESP-IDF但对于学习和大多数应用场景CircuitPython的开发和迭代速度是无与伦比的。我们的技术栈可以这样概括ESP32-C6硬件CircuitPython固件Adafruit IO云平台。通信的基石是MQTT协议这是一种轻量级的发布/订阅消息协议非常适合物联网设备与云端的双向通信。整个数据流是设备通过WiFi连接本地路由器再通过MQTT客户端连接到Adafruit IO的MQTT代理服务器io.adafruit.com从而实现数据的收发。2.2 关键文件settings.toml的深度解析在早期的CircuitPython项目中我们习惯把WiFi密码、API密钥等敏感信息放在一个叫secrets.py的文件里。从CircuitPython 8开始官方推荐使用settings.toml。这个变化不仅仅是换了个名字和格式背后有更深的考量。settings.toml是一种比Python文件更结构化、更安全的配置文件格式。TOMLTom‘s Obvious, Minimal Language语法清晰易于人和机器解析。最关键的是CircuitPython系统会以更高的优先级和更早的时机加载这个文件中的环境变量使得你的主程序code.py一启动就能获取到这些配置减少了因配置缺失导致的运行时错误。让我们仔细看看一个标准的settings.toml应该怎么写以及那些容易踩坑的细节# 这是一个注释以‘#’开头 CIRCUITPY_WIFI_SSID 你的WiFi名称 CIRCUITPY_WIFI_PASSWORD 你的WiFi密码 ADAFRUIT_AIO_USERNAME 你的Adafruit IO用户名 ADAFRUIT_AIO_KEY 你的Adafruit IO Active Key # 其他自定义变量可选 board_name ESP32-C6_Feather update_interval 10重要注意事项与实操心得格式是命门键值对用等号连接字符串必须用双引号括起来。这是我见过新手最常犯的错误直接写CIRCUITPY_WIFI_SSIDmywifi会导致解析失败。等号两边可以加空格不加也行但加上更清晰。文件位置与编码这个文件必须放在你的CIRCUITPY驱动器的根目录不能放在任何文件夹里。并且请务必用文本编辑器如VS Code、Notepad、甚至Mu编辑器将其保存为“UTF-8 无BOM”格式。在Windows的记事本里保存时编码选项可能叫“UTF-8”这通常就是无BOM的。如果文件带有BOM字节顺序标记CircuitPython可能无法正确读取第一行的变量。变量名区分大小写CIRCUITPY_WIFI_SSID和circuitpy_wifi_ssid会被认为是两个不同的变量。请严格按照示例中的大写格式来写。获取Adafruit IO密钥ADAFRUIT_AIO_KEY不是你的登录密码。你需要登录Adafruit IO网站点击右上角个人头像 - “View AIO Key”在弹出窗口中找到“Active Key”。这个密钥才是用来进行MQTT通信的凭证。安全警告这个文件包含了你所有的敏感信息。千万不要把它上传到公开的GitHub仓库或其他代码托管平台。一个标准的做法是在你的项目文件夹里放一个settings.toml.example文件里面只写变量名和示例值而真正的settings.toml文件则被添加到.gitignore中确保其不会泄露。3. WiFi连接测试从零到一的网络握手3.1 基础连接代码逐行解读在配置好settings.toml之后我们就可以开始编写第一个测试程序了。这个程序的目标很明确连接WiFi打印本机网络信息并测试外网连通性。下面我们结合代码看看每一个环节背后的逻辑。# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries # SPDX-License-Identifier: MIT # 1. 导入必要的模块 from os import getenv import ipaddress import wifi import socketpool # 2. 从环境变量加载WiFi配置 ssid getenv(CIRCUITPY_WIFI_SSID) password getenv(CIRCUITPY_WIFI_PASSWORD) # 3. 配置检查防止因settings.toml缺失导致的运行时崩溃 if None in (ssid, password): raise RuntimeError( WiFi配置信息缺失请检查settings.toml文件是否已创建 并确保其中包含‘CIRCUITPY_WIFI_SSID‘和‘CIRCUITPY_WIFI_PASSWORD‘。 该文件应位于CIRCUITPY驱动器根目录。 ) print(\n *30) print(正在尝试连接WiFi...) # 4. 核心连接步骤 try: wifi.radio.connect(ssid, password) except (TypeError, RuntimeError) as e: # 连接失败处理 print(f连接失败错误类型: {type(e).__name__}) print(f错误详情: {e}) print(请检查) print( 1. SSID和密码是否正确) print( 2. 路由器是否开启了2.4GHz频段(ESP32-C6通常只支持2.4GHz)) print( 3. 设备是否在路由器信号范围内) raise # 重新抛出异常让程序停止 print(✓ WiFi连接成功) print(*30) # 5. 创建Socket池为后续网络操作做准备 pool socketpool.SocketPool(wifi.radio) # 6. 打印网络标识信息 # MAC地址设备的物理唯一标识格式化为十六进制 mac_address wifi.radio.mac_address print(f设备MAC地址: {‘:‘.join(‘{:02x}‘.format(b) for b in mac_address)}) # IPv4地址路由器DHCP服务分配的内网地址 ipv4_addr wifi.radio.ipv4_address print(f本地IP地址: {ipv4_addr}) print(f子网掩码: {wifi.radio.ipv4_subnet}) print(f网关地址: {wifi.radio.ipv4_gateway}) print(fDNS服务器: {wifi.radio.ipv4_dns}) # 7. 网络连通性测试Ping print(\n正在进行网络连通性测试...) # 使用Google的公共DNS服务器IP之一稳定性高 test_host 8.8.4.4 try: # 将域名解析为IP地址对象 ping_ip ipaddress.ip_address(test_host) # wifi.radio.ping()返回的是秒数乘以1000得到毫秒 ping_time_ms wifi.radio.ping(ping_ip) * 1000 if ping_time_ms is not None: print(fPing {test_host} 成功延迟: {ping_time_ms:.2f} ms) # 根据延迟简单判断网络质量 if ping_time_ms 50: print(网络状态优秀) elif ping_time_ms 150: print(网络状态良好) else: print(网络状态一般延迟较高) else: print(f警告Ping {test_host} 超时或无响应。) except Exception as e: print(fPing测试失败: {e}) print(这可能表示设备无法访问外网请检查路由器防火墙或互联网连接。) print(\n *30) print(WiFi基础测试全部完成)代码逻辑深度解析wifi.radio.connect(ssid, password)这是最核心的一行。它触发了完整的WiFi连接握手过程扫描网络 - 认证 - 关联 - 获取IPDHCP。这个方法内部已经封装了重试逻辑但如果密码错误或信号太差最终会抛出异常。MAC地址格式化wifi.radio.mac_address返回的是一个字节数组bytearray。我们通过列表推导式和join方法将其格式化为常见的xx:xx:xx:xx:xx:xx形式更易于阅读。socketpool.SocketPool这是一个非常重要的对象。它管理着底层的网络套接字资源。在CircuitPython中很多高级网络库如adafruit_minimqtt、adafruit_requests都需要传入一个socketpool实例来创建连接。提前创建好它是一种良好的实践。Ping测试的意义Ping通8.8.4.4Google DNS不仅证明了设备连上了路由器有本地IP更证明了路由器本身可以访问互联网且设备的DNS解析和基础网络栈工作正常。这是判断设备“真正在线”的黄金标准。3.2 常见连接问题与排查技巧实录即使代码正确WiFi连接也常常出问题。下面是我在无数次调试中总结出的排查清单基本能覆盖90%的情况问题现象可能原因排查步骤与解决方案RuntimeError: WiFi settings are kept in settings.toml...1.settings.toml文件不存在。2. 文件不在CIRCUITPY根目录。3. 变量名拼写错误或大小写不对。4. 文件编码错误如带BOM。1. 使用Mu编辑器或VS Code检查CIRCUITPY根目录下是否有settings.toml。2. 确认变量名为CIRCUITPY_WIFI_SSID和CIRCUITPY_WIFI_PASSWORD。3. 用编辑器将文件另存为“UTF-8无BOM”格式。TypeError或连接超时无具体错误1. WiFi密码错误。2. 路由器仅支持5GHz频段。3. 信号强度太弱RSSI值低。4. 路由器设置了MAC地址过滤。1.双重检查密码特别注意特殊字符和空格。2. 进入路由器后台确认2.4GHz网络已开启并广播SSID。3. 将设备靠近路由器或使用wifi.radio.start_scanning_networks()扫描查看信号强度。4. 在路由器设置中暂时禁用MAC过滤或将设备的MAC地址加入白名单。能获取IP但Ping失败1. 路由器未连接互联网。2. 路由器防火墙阻止了ICMPPing协议。3. DHCP分配的DNS服务器有问题。1. 用手机或电脑连接同一WiFi测试能否上网。2. 尝试Ping路由器的网关IP如192.168.1.1如果通则是外网或DNS问题。3. 在代码中尝试Ping一个已知的IP地址如1.1.1.1绕过DNS。连接间歇性断开1. 电源不稳定特别是使用电池时。2. 路由器信号不稳定。3. 同一网络内设备IP冲突。1. 确保供电充足可尝试外接USB电源而非电脑USB口供电。2. 检查路由器信道是否过于拥挤可尝试更换信道。3. 在路由器设置中为ESP32-C6分配静态IPDHCP保留地址。一个高级调试技巧信号强度扫描如果你怀疑是信号问题可以在连接前加入扫描代码直观看到周围网络情况import wifi print(“扫描可用网络...“) for network in wifi.radio.start_scanning_networks(): print(f“SSID: {network.ssid}, 信号强度: {network.rssi}, 频道: {network.channel}“) wifi.radio.stop_scanning_networks()通过这个列表你可以找到信号最强RSSI值越接近0越好的自家网络并确认其频道。4. 进阶实战与Adafruit IO构建双向物联网应用基础网络通了我们就可以玩点更酷的了。Adafruit IO是一个为物联网设备量身定做的数据平台它免费层级的额度对于个人项目和原型开发来说完全足够。接下来我们要实现一个经典案例设备向云端发送数据如模拟的传感器读数同时接收来自云端的控制指令如改变板载LED颜色。4.1 Adafruit IO平台配置详解在写代码之前必须在Adafruit IO网站上完成配置。这个过程其实就是在云端为你的数据流建立“管道”和“控制面板”。创建Feeds数据流Feed是IO平台的核心概念你可以把它理解为一个主题频道。我们创建两个random用于接收从设备发来的“随机数”数据。neopixel用于向设备发送控制NeoPixel颜色的指令。关键点Feed的名称区分大小写且会在MQTT的主题Topic中使用所以代码里的名字必须和这里完全一致。创建Dashboard仪表盘并添加控件新建一个Dashboard取名如“ESP32-C6 Controller”。点击“Create New Block”选择“Color Picker”控件。在连接Feed时选择我们刚才创建的neopixelfeed。保存后你的Dashboard上就会出现一个颜色选择器。当你点击颜色并保存时对应的十六进制颜色值如#FF00FF就会被发布到neopixel这个Feed上。为什么是MQTTAdafruit IO使用MQTT协议与设备通信这是一种基于发布/订阅模式的轻量级协议。设备“订阅”subscribeneopixel主题来接收颜色指令同时“发布”publish数据到random主题。服务器充当中介Broker负责路由这些消息。这种方式解耦了设备与设备、设备与客户端非常灵活高效。4.2 项目代码库管理与依赖安装这个项目需要几个额外的CircuitPython库。最可靠的方式是使用Adafruit提供的“项目包”Project Bundle。下载项目包在项目页面找到“Download Project Bundle”按钮。这个ZIP文件包含了code.py和所需的lib文件夹。上传库文件将ZIP解压后你会看到lib文件夹。你需要将整个lib文件夹上传到你的CIRCUITPY驱动器的根目录。如果根目录已存在lib文件夹则合并内容通常是覆盖旧的库文件。这一步至关重要缺少库会导致ImportError。必须的库adafruit_minimqtt,adafruit_io。项目包通常也会包含neopixel等但如果你单独管理库请确保这些都已安装。更新settings.toml在之前的WiFi配置基础上增加Adafruit IO的认证信息CIRCUITPY_WIFI_SSID 你的WiFi CIRCUITPY_WIFI_PASSWORD 你的密码 ADAFRUIT_AIO_USERNAME 你的IO用户名 # 注意不是邮箱是用户名 ADAFRUIT_AIO_KEY 你的Active Key4.3 核心代码逻辑拆解与健壮性设计让我们深入分析这个双向通信的示例代码看看如何构建一个能应对真实网络环境的健壮应用。import time import ssl import os from random import randint import microcontroller import socketpool import wifi import board import neopixel import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT # --- 第一部分WiFi连接带异常处理和硬复位--- try: ssid os.getenv(“CIRCUITPY_WIFI_SSID“) print(f“正在连接WiFi: {ssid}“) wifi.radio.connect(ssid, os.getenv(“CIRCUITPY_WIFI_PASSWORD“)) print(“✓ WiFi连接成功“) except Exception as e: # 捕获所有异常因为WiFi错误类型多样 print(f“致命错误WiFi连接失败 - {e}“) print(“系统将在30秒后硬复位尝试重新连接...“) time.sleep(30) microcontroller.reset() # 终极手段重启整个单片机 # --- 第二部分硬件初始化 --- # 初始化板载NeoPixel LED通常只有一个 pixel neopixel.NeoPixel(board.NEOPIXEL, 1, brightness0.3) pixel.fill(0x000000) # 初始化为熄灭状态 print(“NeoPixel初始化完成。“) # --- 第三部分MQTT回调函数定义 --- def connected(client): 当成功连接到Adafruit IO MQTT Broker时触发。 这是订阅主题的最佳时机。 print(“✅ 已连接到Adafruit IO!“) # 订阅‘neopixel‘主题准备接收颜色控制指令 client.subscribe(“neopixel“) print(“ 已订阅‘neopixel‘主题等待颜色指令...“) def message(client, feed_id, payload): 当收到已订阅主题的消息时触发。 client: MQTT客户端对象 feed_id: 收到消息的Feed名称即主题名 payload: 消息内容字符串 print(f“ 收到来自 [{feed_id}] 的数据: {payload}“) if feed_id “neopixel“: # payload格式如 “#FF8800“需要去掉‘#‘并转换为16进制整数 try: # 将‘#RRGGBB‘字符串转换为整数 color_value int(payload[1:], 16) pixel.fill(color_value) print(f“ 已设置NeoPixel颜色为: {payload}“) except ValueError as e: print(f“ 错误无法解析颜色值 ‘{payload}‘ - {e}“) # --- 第四部分MQTT客户端初始化与配置 --- # 创建Socket池供MQTT客户端使用 pool socketpool.SocketPool(wifi.radio) # 初始化MQTT客户端配置服务器地址和认证信息 mqtt_client MQTT.MQTT( broker“io.adafruit.com“, # Adafruit IO的MQTT服务器地址 usernameos.getenv(“ADAFRUIT_AIO_USERNAME“), # 从settings.toml读取 passwordos.getenv(“ADAFRUIT_AIO_KEY“), # 从settings.toml读取 socket_poolpool, ssl_contextssl.create_default_context(), # 启用SSL加密连接 # 可选设置连接保活和重连参数提升稳定性 # keep_alive60, # socket_timeout5, # connect_retries3, ) # 创建Adafruit IO的辅助对象它封装了MQTT客户端的常用操作 io IO_MQTT(mqtt_client) # 将我们定义的回调函数“挂载”到IO对象上 io.on_connect connected io.on_message message # --- 第五部分主循环 - 连接管理与数据收发 --- last_publish_time 0 publish_interval 10 # 每10秒发布一次数据 print(“\n” ““*40) print(“进入主循环尝试连接Adafruit IO...“) print(““*40) while True: try: # 检查连接状态如果断开则尝试重连 if not io.is_connected: print(“尝试连接至Adafruit IO MQTT Broker...“) io.connect() # 此调用会阻塞直到连接成功或超时 # 必须定期调用loop()用于处理网络数据包的收发、维持心跳 # 它检查是否有 incoming messages并发送 outgoing messages io.loop() # 定时任务每10秒发布一个“随机数”到‘random‘ feed current_time time.monotonic() # 获取单调递增的时间不受系统时间调整影响 if current_time - last_publish_time publish_interval: simulated_sensor_value randint(0, 255) # 模拟一个传感器读数0-255 print(f“ 发布数据到 ‘random‘: {simulated_sensor_value}“) try: io.publish(“random“, str(simulated_sensor_value)) last_publish_time current_time except Exception as pub_err: print(f“发布数据失败: {pub_err}“) # 发布失败不一定要重置可以等待下次循环重试 # 短暂延时避免循环空转消耗CPU time.sleep(0.01) except Exception as e: # 主循环中的全局异常捕获应对网络闪断等意外情况 print(f“\n⚠️ 主循环发生未预期错误: {e}“) print(“程序将在30秒后硬复位尝试恢复...“) time.sleep(30) microcontroller.reset()关键逻辑与健壮性设计解析分层异常处理代码中有三个try-except块分别处理WiFi连接、MQTT连接和主循环中的通用错误。这种设计确保了无论哪个环节出错程序都不会静默崩溃而是打印错误信息并执行恢复操作这里是等待30秒后硬复位。在实际产品中你可能需要更复杂的重连策略而非直接复位。microcontroller.reset()的使用这是CircuitPython提供的“核武器”。当网络状态异常且无法通过软件恢复时硬复位是最简单粗暴但有效的办法。它等同于按下板子的复位按钮让一切从头开始。注意频繁硬复位可能影响设备寿命在产品代码中应谨慎使用并加入递增的延迟或错误计数逻辑。回调函数Callbacksconnected和message是典型的回调函数。你定义好它们并在适当的时机连接成功、收到消息由MQTT库自动调用。这是一种事件驱动的编程模式让你的代码不必一直轮询检查状态更高效。io.loop()的重要性这是MQTT客户端的“心跳”和“消息泵”。你必须在主循环中频繁调用它通常每次循环都调用。它的作用是维持与Broker的TCP连接发送PING请求。检查并接收来自Broker的消息触发message回调。发送出站消息队列中的数据。 如果忘记调用loop()设备将收不到任何云端下发的指令。时间管理使用time.monotonic()而不是time.time()来计时。monotonic时间只会递增不受系统时钟被用户或网络时间协议NTP修改的影响非常适合用于测量时间间隔。数据格式转换注意message回调中处理颜色数据的代码int(payload[1:], 16)。Adafruit IO Color Picker发送的格式是#RRGGBB我们需要去掉开头的#然后将剩下的六位十六进制字符串转换为一个整数才能用于pixel.fill()。4.4 运行测试与联动验证将完整的code.py、lib文件夹和配置好的settings.toml都放到CIRCUITPY驱动器后板子会自动重启运行。观察串口输出打开Mu编辑器或任何串口终端如PuTTY波特率115200你应该看到类似以下的日志正在连接WiFi: your_wifi ✓ WiFi连接成功 NeoPixel初始化完成。 进入主循环尝试连接Adafruit IO... 尝试连接至Adafruit IO MQTT Broker... ✅ 已连接到Adafruit IO! 已订阅‘neopixel‘主题等待颜色指令... 发布数据到 ‘random‘: 142 发布数据到 ‘random‘: 87 ...这表示设备已成功连接WiFi和Adafruit IO并开始定时上报数据。在Adafruit IO上验证数据接收登录Adafruit IO进入你的randomfeed。你应该能看到一个折线图数据点正在以每10秒一个的频率增加。这就是你的ESP32-C6发来的“传感器”数据。测试云端控制进入之前创建的Dashboard点击那个Color Picker控件。选择一个颜色比如亮蓝色点击“SAVE”。立即观察你的串口终端和板载NeoPixel LED。终端里应该会打印出类似 收到来自 [neopixel] 的数据: #00AAFF的消息同时板载LED的颜色应瞬间变为你选择的颜色。恭喜至此你已经完成了一个完整的、双向的物联网应用闭环。设备数据上云云端指令下设备一个典型的物联网应用骨架就搭建起来了。5. 从原型到产品优化思路与扩展方向上面的示例是一个完美的起点但离一个稳定的产品还有距离。下面分享几个基于实际项目经验的优化思路连接稳定性增强WiFi重连策略不要只在初始化时连接一次WiFi。可以在主循环中检查wifi.radio.connected状态如果断开尝试重连并采用“指数退避”算法如失败后等待1秒、2秒、4秒...再重试。MQTT遗嘱消息Last Will在mqtt_client初始化时可以设置will_topic和will_message。这样当设备异常离线时Broker会自动替你发布一条“设备离线”的消息到指定主题方便云端感知设备状态。心跳与看门狗除了MQTT的keep-alive可以在代码中实现一个软件看门狗。如果长时间比如5分钟没有成功发布或接收任何数据则主动尝试重连或复位。数据与功耗优化数据缓存与批量上报对于传感器数据不必一采集到就发送。可以本地缓存一段时间如5分钟然后批量上报减少网络交互次数节省电量。深度睡眠如果设备由电池供电且数据上报间隔很长如每小时一次可以利用ESP32-C6的深度睡眠功能。在睡眠期间绝大部分电路关闭功耗可降至微安级别。醒来后快速连接WiFi、发送数据、然后再次入睡。使用QoSMQTT支持服务质量等级QoS 0, 1, 2。对于关键指令如开关命令可以使用QoS 1至少送达一次确保指令不丢失但会稍微增加网络开销。功能扩展连接更多传感器将代码中的randint(0, 255)替换为真实传感器的读数如DHT22温湿度、BMP280气压、光敏电阻等。Adafruit提供了大量传感器的CircuitPython库。创建复杂的Dashboard在Adafruit IO上你可以组合多个控件图表、开关、滑块、地图来创建一个功能丰富的监控面板。例如用折线图显示温度历史用开关控制一个继电器。集成IFTTT或WebhooksAdafruit IO可以触发IFTTT小程序或向自定义的Web服务器发送HTTP请求。这样当温度超过阈值时你可以让IO平台自动给你发邮件、或者在智能插座上打开风扇。这个基于ESP32-C6和Adafruit IO的实践为你打开了一扇物联网开发的大门。从最基础的网络连接到稳定的云端双向通信每一步都蕴含着对硬件、网络和软件架构的理解。当你亲手看到云端的一个点击能让远端的设备亮起指定的颜色时那种连接虚拟与现实的成就感正是物联网开发的魅力所在。希望这份详细的指南和其中包含的“踩坑”经验能让你在接下来的项目中走得更稳、更快。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价