单片机与MQTT协议

server/2024/12/27 13:04:36/

MQTT 协议简述

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布 / 订阅(publish/subscribe)模式的 “轻量级” 通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 在 1999 年发布。

它有着诸多显著特点,首先是轻量级,协议设计简洁,消息头部较小,传输的数据量不大,很适合在带宽有限的网络环境中进行传输,这使得它在资源受限的设备上也能很好地应用,比如嵌入式设备、小型化设备等。

可靠性方面,MQTT 协议支持三种不同的服务质量(Quality of Service, QoS)级别,分别是 “至多一次”“至少一次”“只有一次”。“至多一次” 时,消息发布完全依赖底层 TCP/IP 网络,会发生消息丢失或重复的情况,适用于像环境传感器数据这类丢失一次读记录无所谓的场景;“至少一次” 则确保消息能到达,但消息重复可能会发生;“只有一次” 可保证消息只被传递一次,常用于像计费系统这种对消息准确性要求严格的情况。

灵活性上,它支持多种客户端和服务器实现,能在不同的硬件平台以及操作系统上运行,并且还可以与其他协议(如 HTTP、TCP/IP 等)进行集成,方便在各种复杂的系统环境中部署使用。

在安全性方面,MQTT 协议支持基于 TLS/SSL 的加密通信,以此保护消息的安全性和隐私性,避免消息在传输过程中被窃取或篡改。

还有异步通信这一特点,它使用发布 / 订阅模式,允许消息进行异步传递,将发送者和接收者之间进行解耦,提高了系统的可伸缩性和灵活性,客户端可以通过订阅主题来接收实时的消息,从而支持实时事件驱动的应用场景。

此外,它还具备消息持久化的机制,就算遇到网络不稳定等情况,也能确保消息可靠地传递给接收者,并且还能对设备的在线 / 离线状态进行监测,实时感知设备的连接状态变化。

从整体架构来看,MQTT 协议中有几个关键的组成部分。发布者(Publisher)负责将消息发布到主题上,发布者一次只能向一个主题发送数据,且发布消息时无需关心订阅者是否在线;订阅者(Subscriber)通过订阅主题来接收消息,并且可一次订阅多个主题;代理(Broker)扮演着极为重要的角色,负责接收发布者的消息,并将消息转发至符合条件的订阅者,同时也要处理客户端发起的连接、断开连接、订阅、取消订阅等请求;主题(Topic)则是 MQTT 进行消息路由的基础,它类似 URL 路径,使用斜杠 / 进行分层,一个主题可以有多个订阅者,代理会将该主题下的消息转发给所有订阅者,一个主题也可以有多个发布者,代理将按照消息到达的顺序转发,而且 MQTT 还支持订阅者使用主题通配符一次订阅多个主题。

在应用场景方面,MQTT 协议在物联网领域应用极为广泛,像智能家居中各种智能设备(如智能灯光、智能家电、智能安防等)之间的通信和控制,智能交通系统里各个设备(如智能导航、智能停车、智能交通灯等)之间的通信和协作,还有智能制造中各类设备之间的通信和控制,以及物流和供应链、能源管理、智慧城市建设等众多场景下,MQTT 协议都发挥着重要作用,帮助实现设备间高效、稳定的消息传递与通信。 总之,MQTT 协议以其独特的优势,成为了物联网等相关领域中一种非常重要的消息传输协议。

单片机应用 MQTT 协议的准备工作

选择合适的 MQTT 库

在将单片机应用于 MQTT 协议时,选择一个合适的 MQTT 库至关重要,它能极大地影响开发的效率以及应用的性能。以下是一些不同单片机平台和开发环境下常用的 MQTT 库及其特点、适用场景介绍。

对于 Arduino 平台来说,PubSubClient 是一个备受青睐的选择。它具有良好的兼容性,能够轻松地在各种 Arduino 兼容设备(像 Arduino Uno、Arduino Mega、ESP8266 等)上实现 MQTT 功能。其优势在于使用较为简单,开发者可以通过简洁的 API 接口来完成连接、发布消息以及订阅主题等操作,方便初学者快速上手,适用于各类简单的物联网项目,比如在智能家居场景中控制智能灯泡、温湿度传感器等设备,或者实现一些基础的远程监控功能。

Adafruit MQTT 库也是为 Arduino 平台设计的优秀库之一。这个开源项目旨在为 Arduino 平台提供 MQTT 协议支持,它不仅简化了设备间的通信,还能与 Adafruit IO 实现无缝集成。其核心优势体现在广泛的硬件兼容性上,支持包括 Adafruit FONA、Arduino Yun、ESP8266 等在内的多种 Arduino 兼容设备。而且,它依赖于 Adafruit SleepyDog 等库,能够提供额外的功能,像看门狗定时器增强了系统的可靠性,适用于从家庭自动化到工业监控等多个领域,例如可以用于控制家中的智能灯光系统,也能在工业环境中监控机器的运行状态等。

如果是针对嵌入式系统,wolfMQTT 客户端库则是不错的选择。它是由 wolfSSL 团队开发的轻量级、高效的 MQTT 客户端实现,支持 MQTT v3.1.1 和 v5.0 规范,以及 MQTT-SN(传感器网络)规范。其特点非常突出,小巧的空间占用(大约 3.6kB)很适合资源有限的嵌入式物联网设备;可利用 wolfSSL 库进行安全连接,保障数据传输的安全性;有着良好的便携性,支持多种平台,易于在新平台上编译;并且 API 简单,使用 C89 编写,接口简洁明了,外部依赖也少,约 1200 行源代码。常用于 IoT 设备数据上报,像传感器定期向云端服务器报告状态或事件;也适用于监控系统实时通信以及智能家居、工业自动化等场景中的通信需求,比如在监控或报警系统中实现传感器数据的实时传输,或者作为客户端在智能家居、工业自动化场景里与控制器进行通信等。

另外,还有专门为传感器网络设计的 MQTT-SN 协议,其目标是为非 TCP/IP 协议栈的嵌入式设备(如 Zigbee、Bluetooth 等)提供应用层通信标准。例如在大规模分布式物联网中,EMQX 除了完整支持 MQTT 协议外,还可通过网关处理所有非 MQTT 协议的连接、认证和消息收发,并为其提供统一的用户层接口,方便在如智慧城市、智能家具、水电气表等具有短距离、带宽受限、低功耗等特点的应用场景中使用。

总之,在选择 MQTT 库时,需要综合考虑单片机的硬件资源、项目的具体应用场景以及对通信可靠性、安全性等方面的要求,这样才能挑选出最契合的库来助力项目开发。

配置相关参数

当在单片机代码中应用 MQTT 协议时,正确配置相关参数是实现与 MQTT 服务器顺利连接并通信的基础工作。以下是一些关键参数的设置讲解。

首先是服务器地址,这是明确要连接的 MQTT 服务器所在的网络位置,例如在测试时常用的 “tcp://test.mosquitto.org” 这样的公共测试服务器地址,如果是企业内部或者特定项目使用的私有服务器,就需要填入对应的 IP 地址或者域名,像在一些企业搭建的物联网平台中,服务器地址可能是 “mqtt.example.com” 等形式,务必确保地址准确无误,否则无法建立连接。

端口号也是必不可少的配置项,MQTT 协议默认使用的端口号一般是 1883,不过在有些应用场景下,如果采用了加密传输(基于 TLS/SSL),那可能会使用 8883 端口等其他指定端口,开发时需要根据服务器的实际设置来进行相应配置。

认证凭据方面,常见的有用户名和密码。很多 MQTT 服务器为了保证安全性,会要求客户端提供合法的用户名和密码进行身份验证,例如在连接阿里云的 MQTT 服务器时,需要在阿里云物联网平台创建设备后获取对应的设备认证信息,填入相应的用户名和密码字段,才能成功连接服务器进行后续通信。另外,对于一些支持 TLS/SSL 加密通信的情况,还需要配置相关的证书文件路径等参数,以此确保数据在传输过程中的安全性,避免消息被窃取或篡改,尤其是在涉及到敏感数据传输的物联网项目中,如远程医疗中医疗设备传输患者生理数据等场景,加密配置就显得格外重要。

除此之外,还有像客户端 ID,它用于在服务器端标识每个连接的客户端,需要保证其唯一性,一般可以根据项目实际情况自行设定一个具有辨识度的字符串作为客户端 ID。同时,根据具体的 MQTT 库以及应用需求,可能还需要配置如心跳时间间隔、会话保持相关的参数等,心跳时间间隔可以让服务器知晓客户端是否还处于在线状态,合理设置能避免因网络临时波动等原因误判客户端离线,而会话保持参数则关乎到客户端离线后重新上线时能否继续之前的通信状态等情况。

总之,这些参数的准确配置是单片机能够顺利运用 MQTT 协议与服务器进行通信的关键所在,需要开发者仔细核对并按照实际情况进行合理设置。

单片机与 MQTT 协议的通信流程

连接服务器

单片机与 MQTT 协议的通信中,连接服务器是首要步骤。以常用的一些 MQTT 库为例,来看看具体是如何操作的。

比如在 Arduino 平台中使用 PubSubClient 库时,首先要引入该库到你的项目中。然后在代码里,需要利用库提供的函数来配置服务器相关信息,像服务器地址、端口号、客户端 ID 等参数。示例代码如下:

 
#include <PubSubClient.h>// 假设你的MQTT服务器地址,这里以测试服务器为例,实际应用中替换成真实地址const char* mqtt_server = "tcp://test.mosquitto.org";// 客户端ID,需保证唯一性,可自行按规则设定const char* clientId = "myUniqueClientId";WiFiClient espClient;PubSubClient client(espClient);void setup() {// 初始化串口等其他必要的设置(此处省略部分常规初始化代码)// 设置MQTT服务器相关信息client.setServer(mqtt_server, 1883);client.setClientId(clientId);// 尝试连接服务器if (client.connect()) {Serial.println("Connected to MQTT server");} else {Serial.println("Connection failed");}}void loop() {// 后续的循环里可以进行如保持连接、消息处理等操作,这里暂不展开client.loop();}

上述代码先是定义了服务器地址和客户端 ID,接着通过setServer和setClientId函数配置好参数,最后使用connect函数尝试与服务器建立连接。

如果是在嵌入式系统中采用 wolfMQTT 客户端库,代码层面的实现稍有不同。以下是一个简单示意(省略部分头文件包含等基础操作):

 
#include "wolfmqtt/mqtt_client.h"// 定义服务器地址、端口等参数#define SERVER_ADDRESS "tcp://yourserver.com"#define SERVER_PORT 1883#define CLIENT_ID "myEmbeddedClient"int main() {// 初始化MQTT客户端结构体MqttClient client;// 配置客户端相关参数,比如设置服务器地址、端口、客户端ID等MqttClient_Init(&client, SERVER_ADDRESS, SERVER_PORT, CLIENT_ID);// 尝试连接服务器int result = MqttClient_Connect(&client);if (result == 0) {printf("Connected to MQTT server successfully.\n");} else {printf("Connection failed with error code: %d\n", result);}// 后续可进行如订阅、发布消息等操作return 0;}

这里通过MqttClient_Init函数初始化客户端并配置参数,再调用MqttClient_Connect函数来建立与服务器的连接。

总之,不同的库虽然函数名称和使用方式略有差异,但基本思路都是先配置好服务器相关的关键参数,然后调用对应的连接函数来建立单片机与 MQTT 服务器的连接,为后续的订阅、发布消息等操作打好基础。

订阅与发布主题

在 MQTT 协议里,主题(Topic)是消息发布和订阅的关键概念。它类似于一种消息的分类标签,用于标识消息的类别和内容,其结构设计是分层次的,各层级之间用斜杠(/)分隔,例如 “home/livingroom/temperature” 就可以表示家中客厅的温度数据这样的消息类别。

单片机代码中进行主题的订阅与发布操作时,不同的 MQTT 库同样有着对应的函数来实现相应功能。

仍以 Arduino 平台常用的 PubSubClient 库为例,订阅主题可以使用subscribe函数。比如要订阅一个名为 “sensorData” 的主题,代码如下:

 
#include <PubSubClient.h>// 服务器地址等相关参数定义(同前面连接服务器示例部分,此处省略重复代码)void setup() {// 初始化及连接服务器相关操作(省略部分代码)if (client.connect()) {Serial.println("Connected to MQTT server");// 订阅主题,QoS级别这里设置为0(至多一次传递,可按需调整)if (client.subscribe("sensorData", 0)) {Serial.println("Subscribed to topic: sensorData");} else {Serial.println("Subscribe failed");}}}

当有其他客户端向 “sensorData” 主题发布消息时,单片机就能接收到了。

而发布消息到主题,则可以使用publish函数。假设要发布一个温度传感器采集到的温度值 “25” 到 “sensorData” 主题,代码可以这样写:

 
#include <PubSubClient.h>// 相关参数定义及初始化、连接服务器操作(省略)void loop() {client.loop();// 假设这里获取到的温度值为25,实际应用中需从传感器获取真实数据int temperature = 25;char msg[10];sprintf(msg, "%d", temperature);// 发布消息到主题,同样QoS级别可按需调整if (client.publish("sensorData", msg)) {Serial.println("Message published");} else {Serial.println("Publish failed");}delay(1000); // 可根据实际情况设置发布消息的时间间隔}

在其他库中,例如使用 Python 的paho-mqtt库(适用于一些能运行 Python 的单片机环境或者用于测试与单片机通信的上位机代码等情况),订阅主题和发布消息的示例如下:

 
import paho.mqtt.client as mqtt# 当连接成功时调用的回调函数def on_connect(client, userdata, flags, rc):print("Connected with result code " + str(rc))client.subscribe("testTopic") # 订阅主题# 当接收到消息时调用的回调函数def on_message(client, userdata, msg):print(f"Message received: {msg.payload.decode()}")client = mqtt.Client()client.on_connect = on_connectclient.on_message = on_message# 连接到MQTT服务器,替换成真实的服务器地址和端口client.connect("broker.hivemq.com", 1883, 60)# 发布消息示例client.publish("testTopic", "Hello from Python client")client.loop_forever()

通过上述不同语言、不同库针对主题订阅与发布的操作示例可以看出,开发者需要依据具体的应用场景、所选用的库,合理地调用相应函数来完成主题相关操作,以此实现单片机与其他设备之间基于 MQTT 协议的消息交互。

消息处理机制

当有新消息到达单片机所连接的 MQTT 服务器,并匹配到已订阅的主题时,MQTT 库会调用预先定义的回调函数来处理消息。

以常用的一些库为例,像在 Arduino 平台结合 PubSubClient 库时,一般会这样定义和使用回调函数来处理消息:

 
#include <PubSubClient.h>// 服务器等相关参数定义(省略重复代码)// 回调函数,用于处理接收到的消息void callback(char* topic, byte* payload, unsigned int length) {Serial.print("Message arrived [");Serial.print(topic);Serial.print("] ");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();// 在这里可以根据消息内容执行相应操作,比如解析消息判断是什么类型的数据(温度、湿度等)if (strcmp(topic, "temperatureData") == 0) {// 假设消息内容是温度数值的字符串形式,将其解析并进行后续处理,此处简单示例char tempStr[length + 1];strncpy(tempStr, (char*)payload, length);tempStr[length] = '\0';int temperature = atoi(tempStr);Serial.print("Received temperature: ");Serial.println(temperature);// 可以根据温度值做进一步的操作,比如控制相关设备等(此处省略具体控制代码)}}void setup() {// 初始化及连接服务器等操作(省略部分代码)client.setCallback(callback); // 设置回调函数}void loop() {client.loop(); // 持续监听消息,触发回调函数处理}在上述代码中,定义了callback函数作为消息到达的回调处理函数,当有匹配订阅主题的消息到来时,会传入消息的主题、内容以及内容长度等参数。在函数内部,先是将消息内容打印出来方便查看,然后针对特定主题(如 “temperatureData”)的消息进行解析,把接收到的温度数值字符串转换为整型数值,后续就可以根据这个温度值来执行诸如控制其他设备等相应的操作了。在 Python 的paho-mqtt库中,消息处理的回调函数定义如下:import paho.mqtt.client as mqtt# 当接收到消息时调用的回调函数def on_message(client, userdata, msg):print(f"Message received: {msg.payload.decode()}")# 同样可以在这里进行消息解析及对应操作if msg.topic == "sensor_status":status = msg.payload.decode()if status == "on":print("The sensor is turned on.")# 可以添加更多针对传感器开启状态的操作代码elif status == "off":print("The sensor is turned off.")# 对应传感器关闭状态的操作代码(省略)client = mqtt.Client()client.on_connect = on_connectclient.on_message = on_message# 连接等其他操作(省略)client.loop_forever()

这里的on_message回调函数会在接收到消息时被触发,先是打印出消息内容,然后针对特定主题(如 “sensor_status”)的消息进行判断解析,根据消息里表示的传感器状态来执行相应的逻辑操作。

总的来说,消息处理机制就是依靠预先定义好的回调函数,在有新消息到达时自动触发,开发者在回调函数里完成解析消息、根据消息内容判断类型以及执行相应操作等流程,从而实现单片机基于 MQTT 协议对不同消息做出合理的响应和处理,达到与其他设备间有效的信息交互目的。

单片机结合 MQTT 协议的应用场景实例

常见物联网平台应用

在当今的物联网领域中,有不少常见且功能强大的物联网平台,例如腾讯云、阿里云、百度云等,它们与单片机结合 MQTT 协议后,能够实现诸多实用的功能。

以阿里云物联网平台为例,当单片机借助 MQTT 协议与之通信时,一方面,阿里云物联网平台可以对单片机数字量输出、保持寄存器进行设置操作。这意味着我们可以远程控制单片机连接的各类设备,比如在智能家居场景中,对连接到单片机上的智能灯光进行开关、亮度调节等设置,或者控制智能插座的通断电情况;在工业环境里,可以远程操控与单片机相连的电机启动、停止以及转速调节等。另一方面,单片机能够实时上报数字量输入、数字量输出、输入寄存器和保持寄存器的状态。像环境监测场景中,单片机连接的温湿度传感器、空气质量传感器等采集到的数据,可通过 MQTT 协议实时传递给阿里云平台,方便用户随时了解环境状况。

腾讯云物联网平台同样有着出色的表现。通过 MQTT 协议连接后,腾讯云平台也能实现对单片机相关参数的设置操作,并且支持单片机实时反馈如设备运行状态、传感器采集的数据等各类状态信息。例如在智能农业项目中,单片机连接的土壤湿度传感器、光照传感器等获取的数据可以上报给腾讯云平台,而平台端也能对单片机控制的灌溉设备、遮阳设备等进行远程操作,实现智能化的农业生产管理。

百度云物联网平台也是如此,借助 MQTT 协议与单片机通信,既可以让平台对单片机进行相应控制操作,又能接收单片机实时上报的各类状态数据,应用在如智能交通、智能安防等多个领域,方便实现设备间的协同工作以及远程监控与管理等功能。

总之,这些常见的物联网平台与单片机及 MQTT 协议配合,为物联网应用的开发和实施提供了强大且便捷的基础,助力实现各种智能化的业务场景。

特定功能实现案例

单片机中,我们可以使用开源的 EMQX 客户端库来实现如保留消息和遗嘱消息功能,以下为具体的实现过程介绍。

首先,需要在 EMQX 服务器上创建一个 MQTT 客户端,并获取其账号密码、主题等信息。这一步是后续操作的基础,就好比我们要进入一个特定场所,需要先拿到对应的 “门票”(账号密码)以及明确要去的 “区域”(主题)一样。在 EMQX 服务器的相关管理界面或者配置文件中,按照要求填写好客户端的各项信息,创建并记录下生成的账号密码以及确定好要使用的主题名称等内容。

接下来,在单片机上进行相应代码的编写,实现 MQTT 客户端的初始化、连接和订阅等操作。以下是一个使用 ESP32 开发板和 EMQX 客户端库实现相关功能的示例代码解读:

 
#include <WiFi.h>#include <PubSubClient.h>// WiFi网络设置const char* ssid = "your_SSID";const char* password = "your_PASSWORD";// MQTT服务器设置const char* mqtt_server = "your_MQTT_SERVER";const int mqtt_port = 1883;const char* mqtt_username = "your_MQTT_USERNAME";const char* mqtt_password = "your_MQTT_PASSWORD";// 这里首先定义了WiFi网络的名称(SSID)和密码,以及MQTT服务器的地址、端口号、用户名和密码等关键信息void setup() {// 初始化串口等其他必要的设置(此处省略部分常规初始化代码)// 连接WiFi网络WiFi.begin(ssid, password);while (WiFi.status()!= WL_CONNECTED) {delay(1000);Serial.println("Connecting to WiFi...");}Serial.println("Connected to WiFi");// 创建MQTT客户端实例,并设置服务器相关信息PubSubClient client(WiFiClient());client.setServer(mqtt_server, mqtt_port);client.setCallback(callback);// 尝试连接MQTT服务器while (!client.connect("ESP32Client", mqtt_username, mqtt_password)) {Serial.println("Failed to connect to MQTT server. Retrying...");delay(5000);}Serial.println("Connected to MQTT server");// 订阅主题,这里假设要订阅的主题名为 "testTopic"if (client.subscribe("testTopic")) {Serial.println("Subscribed to topic: testTopic");} else {Serial.println("Subscribe failed");}}

// 这部分是setup函数,先是进行了WiFi网络的连接,确保单片机能够正常联网。然后创建了MQTT客户端实例,配置好服务器信息后尝试连接服务器,连接成功后再订阅相应的主题。

void loop() {if (client.connected()) {client.loop();} else {reconnect();}}// loop函数里,先判断客户端是否处于连接状态,如果连接着就持续监听消息(通过client.loop()),若断开连接则调用reconnect函数尝试重新连接。// 回调函数,用于处理接收到的消息void callback(char* topic, byte* payload, unsigned int length) {Serial.print("Message arrived [");Serial.print(topic);Serial.print("] ");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();// 在这里可以根据消息内容执行相应操作,比如解析消息判断是什么类型的数据(温度、湿度等)}

在上述代码中,setup函数完成了初始化、连接 WiFi、创建并配置 MQTT 客户端以及订阅主题等基础且关键的操作,loop函数保障了客户端在连接状态下能持续处理消息以及在断开连接时尝试重新连接,而callback函数则负责对接收到的消息进行相应处理,比如可以根据具体消息内容进行进一步的业务逻辑操作,像判断是控制指令还是传感器数据等,并执行对应的动作,如控制相关设备状态改变或者记录传感器数值等。

通过这样一套流程,就能够在单片机中利用 EMQX 客户端库实现如保留消息和遗嘱消息等特定功能,开发者可以根据实际项目需求在此基础上进行拓展和优化,以满足多样化的物联网应用场景要求。

单片机与 MQTT 协议结合的优势总结

物联网应用中,单片机与 MQTT 协议的结合展现出了诸多显著优势。

其一,MQTT 协议是轻量级的,协议设计简洁,消息头部较小,传输数据量不大,很适合在带宽有限的网络环境中传输,这恰好与单片机常应用于资源受限设备的特点相契合。比如在一些偏远地区的环境监测项目里,使用单片机连接各类传感器(像温湿度传感器、空气质量传感器等),借助 MQTT 协议就能轻松地将采集到的数据通过低带宽网络回传至服务器端,实现对环境状况的远程监控。

其二,MQTT 协议能适应不稳定网络环境。在实际应用场景中,网络波动是常见情况,像在移动的车载物联网设备或者一些信号覆盖不佳的工业场所中,单片机结合 MQTT 协议,凭借其消息持久化机制以及对不同服务质量(QoS)级别的支持,就算遇到网络临时中断、信号弱等不稳定因素,也能确保消息可靠地传递给接收者,最大程度减少数据丢失的风险,保障物联网设备间通信的连贯性。

再者,它可以跨越各种网络和物理设备进行通信。无论是基于 TCP/IP 网络的有线连接,还是像 Zigbee、Bluetooth 等短距离无线通信的嵌入式设备所构建的网络环境,MQTT 协议都能作为统一的消息传输桥梁,让不同网络协议、不同类型的单片机设备(如 Arduino、STM32 等不同平台的单片机)之间实现互联互通。例如在智能家居场景中,通过单片机控制的智能灯光、智能家电、智能安防设备等可能采用多种不同的通信方式和网络协议,但利用 MQTT 协议就能打破这些差异,实现各设备间高效、稳定的通信与协同工作。

最后,从成本角度来看,这种结合为物联网设备间通信提供了便捷高效且低成本的解决方案。一方面,很多 MQTT 库是开源免费的,降低了开发过程中的软件成本;另一方面,单片机本身成本相对较低,二者结合能在满足物联网应用基本通信需求的同时,有效控制项目整体成本投入,尤其适合大规模、分布式的物联网项目部署,像智慧城市建设中众多设备间的通信组网等场景,都能凭借这一优势发挥重要作用。


http://www.ppmy.cn/server/153626.html

相关文章

HDLBits训练4

时间&#xff1a;2024.12.23 Dff8ar 代码 注意敏感信号的写法 module top_module (input clk,input areset, // active high asynchronous resetinput [7:0] d,output [7:0] q );always(posedge clk or posedge areset)beginif(areset) q<8b0;else q<d;end endmodul…

CSS基础-长度单位

&#x1f496;简介 在CSS中&#xff0c;长度单位分为绝对长度单位和相对长度单位。这些单位用于定义元素的尺寸、边距、填充、字体大小等属性值。 ⭐绝对长度单位 绝对长度单位指的是那些无论环境如何变化&#xff0c;其值都是固定不变的单位。它们通常适用于需要精确控制尺寸…

JS 中的作用域与变量提升

JS中有两种作用域 一个是全局作用域 一种是函数作用域 全局作用域声明的属性 哪里的可以访问 console.log(a) //undefind var a 0 vat b 1 console.log(a) // 0 函数作用域中声明的属性只能在函数中访问&#xff08;如果在外部访问就会爆错&#xff09; console.log(a) //…

2. FPGA基础了解--全局网络

前言 引入扇出的概念介绍FPGA中的全局网络为后续时序优化埋下伏笔 扇出 在FPGA设计中扇出是一个重要的概念&#xff0c;所谓的扇出就是一个控制信号所能控制的数据信号的总个数&#xff0c;比如ctrl信号的扇出就是16 reg ctrl 0; reg [15:0] out 0; always (posedge c…

低压降稳压器(LDO)典型特性压降

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、什么是压降 压降电压 VDO 是指为实现正常稳压&#xff0c;输入电压 VIN 必须高出所需输出电压 VOUT(nom) 的最小压差。请…

THREE.js 入门(六) 纹理、uv坐标

一、uv坐标 相当于x、y轴&#xff0c;通过自定义uv坐标可以截取所需的纹理范围 <template><div id"container"></div> </template><script setup> import * as THREE from "three"; import { onMounted } from "vue&…

Coroutine 基础二 —— 结构化并发(一)

1、“一个协程”到底指什么 为了讲结构化并发&#xff0c;需要先讲父子协程&#xff1b;讲父子协程&#xff0c;就需要先讲什么是“一个协程”。 课程用线程作为对比来引入协程概念。使用线程时&#xff0c;通常会认为 Thread 对象就是线程&#xff0c;除了 Thread 这个单词本…

单片机长耗时前后台任务优化

代码&#xff1a; void Task_10ms(void) {... }//改 void Task_2ms(void) {static uint8_t s_state 0switch(s_state){case 0:....s_state 1;break;case 1:....s_state 2;break;case 3:....s_state 1;break;default: //此段可以去除s_state 0;break; } } 参考链接 MCU长…