单片机与MQTT协议

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.mzph.cn/news/891254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#中相等比较 == 和 Equal函数 对比

1. 运算符 是一个运算符&#xff0c;用于比较两个值是否相等。对于值类型&#xff08;如 int、float、double 等&#xff09;&#xff0c; 直接比较两个值是否相同。对于引用类型&#xff08;如类和数组&#xff09;&#xff0c; 比较两个引用是否指向内存中的同一个对象。 2.…

Java 处理base64文件上传

场景&#xff1a; 在系统内有一个类似于公告的模块&#xff0c;如果里面添加的文章不是选择富文本上传图片的方式&#xff0c;而是选择复制别的文章直接粘贴到系统内的富文本&#xff0c;里面的图片就不会是url&#xff0c;而是图片的base64格式&#xff0c;这样会导致数据库存…

【行业发展报告】2024大数据与智能化行业发展浅析

回首 2024&#xff0c;大数据智能化浪潮汹涌。海量数据宛如繁星&#xff0c;在智能算法的苍穹下汇聚、碰撞&#xff0c;释放出洞察市场与用户的强大能量&#xff0c;精准勾勒出商业新航线。我们精心雕琢技术架构&#xff0c;从数据存储的坚固基石到处理分析的高效引擎&#xff…

项目基本配置

总说 本节主要记录修改配置文件、连接mysql数据库、git连接 一、配置文件的修改 1.1 配置pom.xml 由于我们要连接mysql数据库&#xff0c;需要在pom.xml中添加相关依赖 这里给出一个网站&#xff0c;可以找到各种依赖Maven Repository: Search/Browse/Explore 添加一个my…

【YOLOv3】源码(train.py)

概述 主要模块分析 参数解析与初始化 功能&#xff1a;解析命令行参数&#xff0c;设置训练配置项目经理制定详细的施工计划和资源分配日志记录与监控 功能&#xff1a;初始化日志记录器&#xff0c;配置监控系统项目经理使用监控和记录工具&#xff0c;实时跟踪施工进度和质量…

【Vim Masterclass 笔记02】第3章:Vim 核心知识 + L08:Vim 核心浏览命令 + L09:Vim 核心浏览命令同步练习

文章目录 Section 3&#xff1a;Vim Essentials&#xff08;Vim 核心知识&#xff09;S03L08 Essential Navigation Commands1 光标的上下左右移动2 上 / 下翻页3 基于单词前移4 基于单词后移5 重新定位视图中的文本&#xff08;页面重绘&#xff09;6 定位到所在行的行首7 光标…

基础数据结构--二叉树

一、二叉树的定义 二叉树是 n( n > 0 ) 个结点组成的有限集合&#xff0c;这个集合要么是空集&#xff08;当 n 等于 0 时&#xff09;&#xff0c;要么是由一个根结点和两棵互不相交的二叉树组成。其中这两棵互不相交的二叉树被称为根结点的左子树和右子树。 如图所示&am…

力扣-数据结构-7【算法学习day.78】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…

Android着色器SweepGradient渐变圆环,Kotlin

Android着色器SweepGradient渐变圆环&#xff0c;Kotlin import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.SweepGradient import android…

QT---------GUI程序设计基础

代码UI化设计&#xff08;QT&#xff09; 实例功能概述 假设我们要创建一个简单的计算器应用程序。该应用程序具有以下功能&#xff1a; 包含数字按钮&#xff08;0-9&#xff09;、操作符按钮&#xff08;、-、*、/&#xff09;、等于按钮&#xff08;&#xff09;和清除按…

torch.nn.functional的用法

文章目录 介绍激活函数示例 损失函数示例 卷积操作示例 池化示例 归一化操作示例 Dropout示例 torch.nn.functional 与 torch.nn 的区别 介绍 torch.nn.functional 是 PyTorch 中的一个模块&#xff0c;提供了许多函数式的神经网络操作&#xff0c;包括激活函数、损失函数、卷…

关于Nginx

1.Nginx的配置 proxy_pass http: 当你需要将请求分发到多个后端服务器时&#xff0c;需要实现负载均衡功能&#xff0c;可以使用upstream指令定义一组服务器&#xff0c;并在proxy_pass中引用这个服务组名称。。如果不需要负载均衡&#xff0c;只需要将请求转发到单一的后端…

redis用途都有哪些

Redis&#xff0c;作为一个开源的高性能键值对数据库&#xff0c;其用途广泛且功能强大。 1. 缓存&#xff08;Caching&#xff09;&#xff1a; • Redis常被用作缓存层&#xff0c;存储那些频繁访问但不易改变的数据&#xff0c;如用户会话、商品详情等。 • 通过将这些数据存…

Gemma2 2B 模型的model.safetensors.index.json文件解析

Gemma2 2B 模型的 model.safetensors.index.json 文件解析 在使用 Gemma2 2B 模型或其他大型预训练模型时&#xff0c;model.safetensors.index.json 文件起到了索引的作用&#xff0c;它帮助我们了解模型的结构、参数存储方式以及如何加载模型的具体权重。本博客将深入解析该…

JSON结构快捷转XML结构API集成指南

JSON结构快捷转XML结构API集成指南 引言 在当今的软件开发世界中&#xff0c;数据交换格式的选择对于系统的互操作性和效率至关重要。JSON&#xff08;JavaScript Object Notation&#xff09;和XML&#xff08;eXtensible Markup Language&#xff09;是两种广泛使用的数据表…

期权懂|期权入门知识:开通50ETF期权需要什么条件?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 开通50ETF期权需要什么条件&#xff1f; 一、基本资格要求 &#xff08;1&#xff09;年龄限制&#xff1a;投资者必须年满18周岁&#xff0c;具备完全民事行为能力。 &#…

实景三维点云处理专业软件ArcGIS根据DSM生成地表点云集

常见的实景三维处理软件及其特色功能如下&#xff1a; 一、专业实景三维建模软件 Agisoft Metashape 高精度建模&#xff1a;能够生成高精度的三维模型&#xff0c;精度可以达到厘米级甚至毫米级&#xff0c;适用于需要详细测量和分析的项目&#xff0c;如文物保护和建筑测量。…

实战指南:Shiro、CAS打造完美单点登录体验

引言 想象一下&#xff0c;在日常工作中&#xff0c;我们经常需要进行系统认证和授权。当用户尝试登录一个网站时&#xff0c;他们需要提供用户名和密码&#xff0c;网站会检查这些信息&#xff0c;确认用户是谁。这就是认证的过程。 一旦用户被认证&#xff0c;他们可能会尝…

cuda-cuDnn

cuda sudo /bin/sh cuda_11.7.0_515.43.04_linux.run cudnn cuDNN Archive | NVIDIA Developer Linux 系统 CUDA 多版本共存以及切换 – 颢天 安装cuda # 如果已经安装过驱动&#xff0c;驱动不需要再安装&#xff0c;取消勾选 安装cuDNN&#xff0c;cuda-cuDNN对应关系见…

QComboBox中使用树形控件进行选择

事情是这样的&#xff0c;要在一个ComboBox中通过树形结构进行内容的选择。 默认的QComboBox展开是下拉的列表。因此需要定制一下。 效果就是这样的 实现上面效果的核心代码就是下面这样的 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { treenew…