esp32课设记录(三)mqtt通信记录 附mqtt介绍

news2025/5/20 14:31:35

目录

安装mqttx(云端部署)

安装mosquitto(本地部署)

编程,连接wifi

编程,连接mqtt,实现数据接收

实际效果展示:

附录:mqtt介绍

工作流程简述:

工作流程具体介绍:

1. 建立连接(CONNECT/CONNACK)

2. 订阅主题(SUBSCRIBE/SUBACK)

3. 发布消息(PUBLISH)

4. 心跳保持(PINGREQ/PINGRESP)

5. 断开连接(DISCONNECT)

主要特点

QoS(服务质量)等级

MQTT协议组成


看一眼课设要求,只需要连接MQTT并且下发显示英文字符串就行。下面开干。

首先老师给了一个mqtt的broker ip地址,这个很简单只要安装mqttx就直接能用。

当然由于我一开始不知道有mqttx,于是使用了Mosquitto。这个可以在本地部署mqtt的broker服务器,我也成功了。这两个我都会描述一下。

安装mqttx(云端部署)

前往官网MQTTX 下载

下载后,点击new connection,然后填写自己的配置。这里是学校老师给的。

连接成功。

安装mosquitto(本地部署)

先安装mosquitto,前往官网下载安装:Download | Eclipse Mosquitto,安装后进入文件夹,可以看到mosquitto.conf

添加监听窗口和基础设置。

listener 1885


allow_anonymous false
password_file ./passwd


persistence true
persistence_location ./mosquitto_data/

打开命令提示符(CMD),并切换到 Mosquitto 的安装目录。例如,如果 Mosquitto 安装在 C:\Program Files\mosquitto,则运行:

cd C:\Program Files\mosquitto

创建密码文件。然后输入密码。

mosquitto_passwd -c ./passwd iot040

然后,使用以下命令启动 Mosquitto Broker:

mosquitto -v -c mosquitto.conf

这将根据 mosquitto.conf 文件中的配置启动 Mosquitto。

现在已经成功在本地搭建服务器了,套接字用的1885。

测试如下:

订阅主题(终端1)

mosquitto_sub -h localhost -t "test/topic" -v

  • -h localhost:连接本地服务器
  • -t "test/topic":订阅主题
  • -v:显示详细消息

发布消息(终端2)

mosquitto_pub -h localhost -t "test/topic" -m "Hello MQTT"

  • -m "Hello MQTT":消息内容

预期结果

终端1会立即收到消息:

test/topic Hello MQTT

在成功后,可以使用图形化客户端,比如:

  • MQTT.fx(Windows/macOS/Linux)
  • MQTT Explorer(跨平台)

连接配置:

  • Broker Address: localhost
  • Port: 1883
  • 用户名/密码:按照配置的来

编程,连接wifi

最后我的整个工程会开源到github。git还是很方便的。本课设采用ESP-IDF库编程,这是基于freertos的。

首先是wifi的初始化,

初始化需要执行以下操作:

1.创建FreeRTOS事件组用于同步

2.初始化ESP32的网络接口

3.配置并启动WiFi连接

4.阻塞等待连接成功或失败

先定义wifi的基本配置。
 

// 添加WiFi配置
#define WIFI_SSID ""    //wifi名称
#define WIFI_PASSWORD ""    //wifi密码
#define WIFI_MAXIMUM_RETRY 5   //最大重试次数
#define WIFI_CONNECTED_BIT BIT0    //WiFi连接成功标志
#define WIFI_FAIL_BIT BIT1    //WiFi连接失败标志

// FreeRTOS事件组,用于WiFi连接
EventGroupHandle_t s_wifi_event_group;  //FreeRTOS事件组句柄,同步WiFi连接状态,允许程序等待WiFi状态变化,而不是持续轮询
int s_retry_num = 0;//跟踪WiFi连接尝试次数

按顺序编写wifi的初始化。这里面绝大多数函数都是库里面自带的。主要是#include "esp_wifi.h"。

// 初始化WiFi连接
void wifi_init_sta(void)
{
    // 创建事件组,用于等待WiFi连接
    s_wifi_event_group = xEventGroupCreate();

    // 初始化网络接口
    ESP_ERROR_CHECK(esp_netif_init());//初始化ESP32的TCP/IP协议栈
    ESP_ERROR_CHECK(esp_event_loop_create_default());//创建一个默认的事件循环
    esp_netif_create_default_wifi_sta();//创建WiFi站点(STA)模式的网络接口

    // 初始化WiFi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册WiFi事件处理函数
    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    // 配置WiFi
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASSWORD,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .pmf_cfg = {
                .capable = true,
                .required = false},
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    /* 等待WiFi连接或达到最大尝试次数 */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);
    // 标记变量为有意未使用,防止报警告
    (void)bits;
}

然后编写WIFI事件处理函数,逻辑如下:

这个函数不是直接被调用的,而是通过ESP-IDF的事件系统自动触发,当执行esp_wifi_start()后:

WiFi驱动启动并触发WIFI_EVENT_STA_START事件

事件系统调用wifi_event_handler,传入此事件

处理函数检测到是启动事件,执行esp_wifi_connect()开始连接

如果连接成功,TCP/IP栈获取IP地址并触发IP_EVENT_STA_GOT_IP事件

事件系统再次调用wifi_event_handler,这次传入IP事件

处理函数设置WIFI_CONNECTED_BIT标志,通知主线程连接成功

// WIFI事件处理函数
void wifi_event_handler(void *arg, esp_event_base_t event_base,
                        int32_t event_id, void *event_data)
{
    // 1. WiFi驱动启动完成事件
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        // WiFi已初始化完成,开始尝试连接到配置的AP
        esp_wifi_connect();
    }
    // 2. WiFi断开连接事件(初次连接失败或运行中断开)
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        // 检查是否超过最大重试次数
        if (s_retry_num < WIFI_MAXIMUM_RETRY)
        {
            // 未超过重试上限:增加计数并尝试重新连接
            esp_wifi_connect();
            s_retry_num++;
            // ESP_LOGI(WIFI_TAG, "重试连接WiFi... (%d/%d)", s_retry_num, WIFI_MAXIMUM_RETRY);
        }
        else
        {
            // 超过重试上限:设置失败标志位,允许主程序继续执行
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
            // ESP_LOGE(WIFI_TAG, "WiFi连接失败,已达最大重试次数");
        }
    }
    // 3. 成功获取IP地址事件(连接成功的最终标志)
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        // 转换事件数据(包含IP地址信息)
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        // 标记变量为有意未使用(避免编译警告)
        (void)event;
        
        // 重置重试计数器,为后续可能的断线重连做准备
        s_retry_num = 0;
        
        // 设置连接成功标志位,允许主程序继续执行
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        // ESP_LOGI(WIFI_TAG, "成功连接WiFi,IP地址: "IPSTR, IP2STR(&event->ip_info.ip));
    }
}

然后在main.c调用wifi_init_sta();即可接上wifi。

// WiFi连接
    wifi_init_sta();

编程,连接mqtt,实现数据接收

先定义需要的变量。

其中esp_mqtt_client_handle_t 是ESP-IDF框架中定义的类型,实际上是一个指向MQTT客户端内部结构的指针,每个客户端一个,初始化为NULL说明此时没有连接任何客户端。

esp_mqtt_client_handle_t的使用流程:

创建: 通过esp_mqtt_client_init()函数创建:

mqtt_client = esp_mqtt_client_init(&mqtt_cfg);

操作: 用于所有MQTT API调用:

// 订阅主题

esp_mqtt_client_subscribe(mqtt_client, MQTT_TOPIC, 0);

// 发布消息

esp_mqtt_client_publish(mqtt_client, MQTT_PUBLISH_TOPIC, "消息内容", 0, 1, 0);

状态检查: 在发送消息前检查连接:if (mqtt_client != NULL && mqtt_connected) {

// 执行MQTT操作

}

// MQTT配置 - 使用提供的服务器信息
#define MQTT_BROKER_URL ""    //broker的url,比如mqtt://211.81.51.133:1885
#define MQTT_USERNAME ""      //用户名
#define MQTT_PASSWORD ""      //密码
#define MQTT_CLIENT_ID ""     //CLIENT_ID
#define MQTT_TOPIC "top040"   //订阅的主题
#define MQTT_PUBLISH_TOPIC "top039"// 发送的主题

// MQTT状态和消息缓冲区
esp_mqtt_client_handle_t mqtt_client = NULL; // ESP-IDF框架中定义的类型,MQTT客户端句柄(指针),指向MQTT客户端内部结构
bool mqtt_connected = false;                 // 跟踪MQTT客户端的连接状态
char mqtt_message[256] = {0};//存储从MQTT服务器接收到的最新消息内容
bool new_message_received = false; // 标记是否接收到新的MQTT消息(需要处理)

初始化:各个功能已经写注释里了。

// 初始化MQTT客户端
void mqtt_app_start(void)
{
    // 配置结构体创建
    esp_mqtt_client_config_t mqtt_cfg = {
        .broker = {
            .address = {
                .uri = MQTT_BROKER_URL, // 服务器地址
            },
            .verification = {
                .skip_cert_common_name_check = true, // 非TLS连接
            }},
        .credentials = {
            .username = MQTT_USERNAME, // MQTT服务器认证用户名
            .authentication = {
                .password = MQTT_PASSWORD, // MQTT服务器认证密码
            },
            .client_id = MQTT_CLIENT_ID, // 客户端唯一标识符
        }};
    // 客户端初始化

    mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
    // 注册事件处理函数
    esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); // ESP_EVENT_ANY_ID表示注册所有MQTT事件(连接、断开、收发消息等),当MQTT事件发生时,系统会自动调用mqtt_event_handler
    esp_mqtt_client_start(mqtt_client);                                                      // 客户端启动
}

来吧,写回调函数,就是每个事件干什么事情。同上,也是自动调用。至此结束了。

// 回调函数指针
void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
    esp_mqtt_client_handle_t client = event->client;

    switch (event->event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(MQTT_TAG, "MQTT已连接");
        mqtt_connected = true;
        // 订阅主题
        esp_mqtt_client_subscribe(client, MQTT_TOPIC, 0);
        ESP_LOGI(MQTT_TAG, "已订阅主题: %s", MQTT_TOPIC);

        // 发布连接成功消息
        esp_mqtt_client_publish(client, MQTT_PUBLISH_TOPIC, "ESP32已上线", 0, 1, 0);
        break;

    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(MQTT_TAG, "MQTT已断开连接");
        mqtt_connected = false;
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(MQTT_TAG, "MQTT订阅成功,msg_id=%d", event->msg_id);
        break;

    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(MQTT_TAG, "MQTT取消订阅,msg_id=%d", event->msg_id);
        break;

    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(MQTT_TAG, "MQTT消息已发布,msg_id=%d", event->msg_id);
        break;

    case MQTT_EVENT_DATA:
        ESP_LOGI(MQTT_TAG, "MQTT收到数据:");
        ESP_LOGI(MQTT_TAG, "主题: %.*s", event->topic_len, event->topic);
        ESP_LOGI(MQTT_TAG, "数据: %.*s", event->data_len, event->data);

        // 将收到的消息复制到缓冲区
        if (event->data_len < sizeof(mqtt_message) - 1)
        {
            memcpy(mqtt_message, event->data, event->data_len);
            mqtt_message[event->data_len] = '\0'; // 确保字符串结束符
            new_message_received = true;          // 设置新消息标志
        }
        break;

    case MQTT_EVENT_ERROR:
        ESP_LOGI(MQTT_TAG, "MQTT错误");
        break;

    default:
        ESP_LOGI(MQTT_TAG, "其他MQTT事件 id:%d", event->event_id);
        break;
    }
}

搞定!下面在主函数里面调用初始化就好了。

//初始化
mqtt_app_start();

发送消息:

// 发送消息
if (mqtt_connected) {
    // 准备消息内容
    char message[100];
    
    // 方式1:简单文本消息
    strcpy(message, "Hello from ESP32");
    
    // 方式2:格式化消息
    sprintf(message, "Sensor reading: %.2f", sensor_value);
    
    // 方式3:JSON格式消息(推荐用于复杂数据)
    sprintf(message, "{\"device\":\"ESP32\",\"value\":%.2f,\"status\":\"%s\"}", 
            sensor_value, status ? "ON" : "OFF");
    
    // 发送消息
    esp_mqtt_client_publish(mqtt_client, MQTT_PUBLISH_TOPIC, message, 0, 1, 0);
/*参数说明:mqtt_client: MQTT客户端句柄
MQTT_PUBLISH_TOPIC: 发布主题
message: 消息内容
0: 消息长度(0表示自动计算)
1: QoS级别(0=最多一次,1=至少一次,2=只有一次)
0: 消息保留标志(0=不保留,1=保留)*/
    
    ESP_LOGI(TAG, "已发送MQTT消息: %s", message);
}

接收消息有2种方式,一种是在while循环里,通过轮询。

// 在主循环中
while (1) {
    // 检查是否收到新消息
    if (new_message_received) {
        // 重置消息标志
        new_message_received = false;
        
        // 处理收到的消息
        ESP_LOGI(TAG, "收到MQTT消息: %s", mqtt_message);
        
        // 解析和处理消息...
        if (strcmp(mqtt_message, "LED_ON") == 0) {
            led_set_state(true);
        } else if (strcmp(mqtt_message, "LED_OFF") == 0) {
            led_set_state(false);
        }
        
        // 可选:显示消息到LCD
        clear_text_area(lcd_buffer, LCD_H_RES, LCD_V_RES);
        draw_string(lcd_buffer, 0, 0, "MQTT消息", 0xFFFF, LCD_H_RES);
        draw_string(lcd_buffer, 0, FONT_HEIGHT + 2, mqtt_message, 0xFFFF, LCD_H_RES);
        esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_H_RES, LCD_V_RES, lcd_buffer);
    }
    
    // 其他任务处理...
    vTaskDelay(10 / portTICK_PERIOD_MS);
}

第二种是直接在MQTT事件处理函数中处理(适用于需要立即响应的场景)

在mqtt_event_handler函数的MQTT_EVENT_DATA部分添加处理逻辑:

case MQTT_EVENT_DATA:
    ESP_LOGI(MQTT_TAG, "MQTT收到数据: %.*s", event->data_len, event->data);
    
    // 将收到的消息复制到缓冲区
    if (event->data_len < sizeof(mqtt_message) - 1) {
        memcpy(mqtt_message, event->data, event->data_len);
        mqtt_message[event->data_len] = '\0'; // 确保字符串结束符
        
        // 立即处理特定命令
        if (strcmp(mqtt_message, "EMERGENCY_STOP") == 0) {
            // 紧急处理,不等待主循环
            system_emergency_stop();
        }
        
        // 设置标志供主循环处理
        new_message_received = true;
    }
    break;

实际效果展示:

初始化

接收消息

发送消息:

首先需要创建一个订阅,确保主题和宏定义的MQTT_PUBLISH_TOPIC一样。

然后在程序里面发送,就可以收到了!

附录:mqtt介绍

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种轻量级的发布/订阅模式消息传输协议,专为低带宽、高延迟或不稳定的网络环境设计。是应用层协议,类似HTTP。QTT已成为物联网领域最流行的通信协议之一,被广泛应用于各种智能设备和系统中。

工作流程简述:

如图所示:

发布者(Publisher):左侧的冰箱代表一个物联网设备,它通过MQTT协议向中间的MQTT Broker(代理服务器)发布(Publish)消息。通常消息包含传感器数据、状态信息等。

代理服务器MQTT Broker):MQTT Broker负责接收和分发消息。它起到“中间人”的作用,不需要设备之间直接通信。

订阅者(Subscriber):右侧的手机、电脑和服务器代表多个订阅者。它们向MQTT Broker订阅(Subscribe)自己感兴趣的主题(Topic)。Topic是消息的分类标识,采用层级结构(如"sensor/temperature")。

一旦有消息发布到Broker,Broker会把消息分发给所有已经订阅该主题的设备。

MQTT通过“发布-订阅”模式,发布者只负责发送消息到Broker,订阅者只需从Broker接收感兴趣的数据。这样可以实现高效、灵活的物联网通信架构。

工作流程具体介绍:

1. 建立连接(CONNECT/CONNACK)

步骤说明

  1. 客户端发送CONNECT
    • 携带参数:
      • ClientID:客户端唯一标识(如device123
      • Clean Session:是否清除历史会话(true=重新开始,false=恢复之前订阅)
      • 可选的Username/Password(若Broker启用认证)
  1. 服务端回复CONNACK
    • 返回状态码:
      • 0:连接成功
      • 4:无效用户名/密码
      • 5:未授权(如客户端无权限连接)
2. 订阅主题(SUBSCRIBE/SUBACK)

步骤说明

  1. 客户端发送SUBSCRIBE
    • 指定订阅的 主题过滤器(如sensor/#匹配所有传感器主题)
    • 设置 QoS级别(0/1/2,决定消息传递质量)
  1. 服务端回复SUBACK
    • 确认订阅成功,或返回失败原因(如主题格式无效)
3. 发布消息(PUBLISH)

步骤说明

  1. 发布者发送PUBLISH
    • Topic:消息分类(如home/temperature
    • Message:实际数据(如{"value":25.5}
    • QoS:发布时的服务质量
    • Retain:是否保留消息(新订阅者立即收到最后一条)
  1. Broker转发消息
    • 根据订阅者的QoS级别,可能需确认(QoS≥1时)

QoS处理差异

  • QoS 0:Broker直接转发,无确认。
  • QoS 1:Broker存储消息直到收到订阅者的PUBACK
  • QoS 2:四次握手确保消息不重复(PUBREC→PUBREL→PUBCOMP)。

4. 心跳保持(PINGREQ/PINGRESP)

作用

  • 在长连接无数据交互时(如Keep Alive时间内),通过心跳包保持连接活跃。
  • 若Broker未及时响应PINGRESP,客户端会判定连接断开并重连。

5. 断开连接(DISCONNECT)

行为说明

  • 客户端主动断开时发送DISCONNECT,Broker释放该客户端的资源。
  • 若未发送DISCONNECT直接断开,Broker会根据遗嘱消息(Last Will)通知其他客户端。


 

主要特点

  1. 轻量级:协议头小,消息精简,适合带宽受限环境
  2. 发布/订阅模式:解耦消息生产者和消费者
  3. 低功耗:适合电池供电的物联网设备
  4. 支持QoS:提供三种消息服务质量等级
  5. 支持TCP/IP:运行在TCP协议之上

QoS(服务质量)等级

MQTT定义了三种QoS级别:

  1. QoS 0(最多一次):发后即忘(无确认)。消息发送一次,不保证送达。
  2. QoS 1(至少一次):至少一次(需PUBACK)。确保消息送达,但可能有重复。
  3. QoS 2(恰好一次):恰好一次(4次握手)。确保消息只送达一次。

MQTT协议组成

MQTT协议由以下核心组件构成:

组件

说明

固定头部(Fixed Header)

每个MQTT报文都以一个固定头部开始,包含了报文类型和标志位等重要信息。

可变报头(Variable Header)

可选字段,如报文标识符(Packet ID)、主题名长度等

有效载荷(Payload)

实际传输的数据(如消息内容),在PUBLISH报文中最常见


核心控制报文类型:

报文类型

用途

CONNECT

1

客户端连接服务器(发送用户名/密码、Clean Session标志等)

CONNACK

2

服务器响应连接(返回状态码,如0=成功,5=认证失败)

PUBLISH

3

发布消息到指定主题(含QoS、Retain标志)

SUBSCRIBE

8

客户端订阅主题(可指定多个主题及QoS)

UNSUBSCRIBE

10

取消订阅

PINGREQ

12

客户端心跳请求(保持长连接)

PINGRESP

13

服务器心跳响应

DISCONNECT

14

客户端主动断开连接


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380028.html

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

相关文章

string类(详解)

【本节目标】 1. 为什么要学习string类 2. 标准库中的string类 3. string类的模拟实现 4. 扩展阅读 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 C 语言中&#xff0c;字符串是以 \0 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c; C 标准库中提供…

MATLAB | R2025a 更新了哪些有趣的东西?

千呼万唤始出来&#xff0c;MATLAB R2025A 来见面&#xff0c;这次更新比往常晚了两个月&#xff0c;让我们看看更了哪些好玩的新东西叭&#xff1a;首先下载更新启动一气呵成&#xff0c;映入眼帘的是&#xff1a; 1 基本界面 基本界面变得和 MATLAB 网页版一模一样了&#…

前缀和——和为K的子数组

作者感觉本题稍稍有点难度&#xff0c;看了题解也思考了有一会TWT 显然&#xff0c;暴力我们是不可取的&#xff0c;但这里我们可以采取一种新的遍历数组形式&#xff0c;从后向前&#xff0c;也就是以i位置为结尾的所有子数组&#xff0c;这个子数组只统计i位置之前的。 然后…

深入理解 ZAB:ZooKeeper 原子广播协议的工作原理

目录 ZAB 协议&#xff1a;ZooKeeper 如何做到高可用和强一致&#xff1f;&#x1f512;ZAB 协议的核心目标 &#x1f3af;ZAB 协议的关键概念 &#x1f4a1;ZAB 协议的运行阶段 &#x1f3ac;阶段一&#xff1a;Leader 选举 (Leader Election) &#x1f5f3;️阶段二&#xff…

GraphPad Prism项目的管理

《2025新书现货 GraphPad Prism图表可视化与统计数据分析&#xff08;视频教学版&#xff09;雍杨 康巧昆 清华大学出版社教材书籍 9787302686460 GraphPadPrism图表可视化 无规格》【摘要 书评 试读】- 京东图书 GraphPad Prism统计数据分析_夏天又到了的博客-CSDN博客 项目…

驱动-Linux定时-timer_list

了解内核定时相关基础知识 文章目录 简要介绍timer_list 特点API 函数实验测试程序 - timer_mod.c编译文件-Makefile实验验证 注意事项总结 简要介绍 硬件为内核提供了一个系统定时器来计算流逝的时间&#xff08;即基于未来时间点的计时方式&#xff0c; 以当前时刻为计时开始…

STM32F103_LL库+寄存器学习笔记22 - 基础定时器TIM实现1ms周期回调

导言 如上所示&#xff0c;STM32F103有两个基本定时器TIM6与TIM7&#xff0c;所谓「基本定时器」&#xff0c;即功能最简单的定时器。 项目地址&#xff1a; github: LL库: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library22_Basic_Timer寄存器方…

5个yyds的.Net商城开源项目

今天一起来盘点下5个商城开源项目。 1、支持多语言、多商店的商城&#xff0c;.Net7 EF7领域驱动设计架构&#xff08;Smartstore&#xff09; 项目简介 Smartstore 支持桌面和移动平台、多语言、多商店、多货币的商城&#xff0c;并支持SEO优化&#xff0c;支持无限数量的…

[项目深挖]仿muduo库的并发服务器的解析与优化方案

标题&#xff1a;[项目深挖]仿muduo库的并发服务器的优化方案 水墨不写bug 文章目录 一、buffer 模块&#xff08;1&#xff09;线性缓冲区直接扩容---->环形缓冲区定时扩容&#xff08;只会扩容一次&#xff09;&#xff08;2&#xff09;使用双缓冲&#xff08;Double Buf…

国标GB28181视频平台EasyGBS校园监控方案:多场景应用筑牢安全防线,提升管理效能

一、方案背景​ 随着校园规模不断扩大&#xff0c;传统监控系统因设备协议不兼容、数据分散管理&#xff0c;导致各系统之间相互独立、数据无法互通共享。在校园安全防范、教学管理以及应急响应过程中&#xff0c;这种割裂状态严重影响工作效率。国标GB28181软件EasyGBS视频云…

SHIMADZU岛津 R300RC300 Operation Manual

SHIMADZU岛津 R300RC300 Operation Manual

使用 Docker 部署 React + Nginx 应用教程

目录 1. 创建react项目结构2. 创建 .dockerignore3. 创建 Dockerfile4. 创建 nginx.conf5. 构建和运行6. 常用命令 1. 创建react项目结构 2. 创建 .dockerignore # 依赖目录 node_modules npm-debug.log# 构建输出 dist build# 开发环境文件 .git .gitignore .env .env.local …

API Gateway REST API 集成 S3 服务自定义 404 页面

需求分析 使用 API Gateway REST API 可以直接使用 S3 作为后端集成对外提供可以访问的 API. 而当访问的 URL 中存在无效的桶, 或者不存在的对象时, API Gateway 默认回向客户端返回 200 状态码. 而实际上这并不是正确的响应, 本文将介绍如何自定义返回 404 错误页面. 基本功…

关于systemverilog中在task中使用force语句的注意事项

先看下面的代码 module top(data);logic clk; inout data; logic temp; logic sampale_data; logic [7:0] data_rec;task send_data(input [7:0] da);begin(posedge clk);#1;force datada[7];$display(data);(posedge clk);#1;force datada[6]; $display(data); (posed…

Python Day26 学习

继续NumPy的学习 数组的索引 一维数组的索引 创建及输出 arr1d np.arange(10) # 数组: [0 1 2 3 4 5 6 7 8 9] arr1d array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 取出数组的第一个元素&#xff0c;最后一个元素 代码实现 arr1d[0] arr1d[-1] 取出数组中索引为3&#x…

解决:npm install报错,reason: certificate has expired

目录 1. 问题分析2. 问题解决2.1 查看配置的镜像2.2 修改镜像源 种一棵树最好的时间是10年前&#xff0c;其次就是现在&#xff0c;加油&#xff01; --by蜡笔小柯南 1. 问题分析 启动前…

中科固源Wisdom平台发现NASA核心飞行控制系统(cFS)通信协议健壮性缺陷!

中科固源Wisdom平台发现NASA核心飞行控制系统(cFS)通信协议健壮性缺陷&#xff0c;接下来内容将进行核心要点概述&#xff0c;分别从地位、重要性和应用场景三方面进行简明阐述&#xff1a; cFS&#xff08;core Flight System&#xff09;是NASA戈达德太空飞行中心&#xff08…

嵌入式学习笔记DAY23(树,哈希表)

一、树 1.树的概念 之前我们一直在谈的是一对一的线性结构&#xff0c;现实中&#xff0c;还存在很多一对多的情况需要处理&#xff0c;一对多的线性结构——树。 树的结点包括一个数据元素及若干指向其子树的分支&#xff0c;结点拥有的子树数称为结点的度。度为0的结点称为叶…

仓颉开发语言入门教程:搭建开发环境

仓颉开发语言作为华为为鸿蒙系统自研的开发语言&#xff0c;虽然才发布不久&#xff0c;但是它承担着极其重要的历史使命。作为鸿蒙开发者&#xff0c;掌握仓颉开发语言将成为不可或缺的技能&#xff0c;今天我们从零开始&#xff0c;为大家分享仓颉语言的开发教程&#xff0c;…

Axure中继器高保真交互原型的核心元件

Axure作为一款强大的原型设计工具&#xff0c;中继器无疑是打造高保真交互原型的核心利器。今天&#xff0c;就让我们深入探讨一下Axure中继器的核心地位、操作难点&#xff0c;以及如何借助优秀案例来提升我们的中继器使用技能。 一、核心地位 中继器在Axure中的地位举足轻重…