当前位置: 首页 > article >正文

【ESP32】ESP-IDF开发 | 低功耗蓝牙开发 | GAP协议 + 设备扫描例程

1. 简介

1.1 GAP协议

        GAP(General Access Protocol),全称通用访问协议,它定义了低功耗蓝牙设备的发现流程,设备管理和设备连接的建立。

        低功耗蓝牙设备定义了4种角色:

  • 广播者(Broadcaster):处于这种角色的设备通过发送广播 (Advertising) 让接收者发现自己。这种角色只能发广播,不能被连接。
  • 观察者(Observer):处于这种角色的设备通过接收广播事件并发送扫描 (Scan) 请求。这种角色只能发送扫描请求,不能被连接。
  • 外围设备(Peripheral):当广播者接受了观察者发来的连接请求后就会进入这种角色。当设备进入了这种角色之后,将会作为从设备 (Slave) 在链路中进行通信。
  • 中央设备(Central):当观察者主动进行初始化,并建立一个物理链路时就会进入这种角色。这种角色在链路中同样被称为主设备 (Master)

1.1.1 广播

        广播主要有 5 种类型:

  • 可连接可扫描非定向广播(Connectable scannable undirected mode):指可被任何设备发现并可连接。可扫描是指当对端设备发送扫描请求 (Scan Request) 时,本端设备需要回复扫描应答 (Scan Response)
  • 高占空比定向广播(High duty cycle directed event type):只能被指定设备所发现和连接的广播,并且广播发送间隔用户不可调整由协议栈决定。
  • 可扫描非定向广播(Scannable undirected mode):可被任何设备发现,但是既不可扫描也不可连接。不可扫描是指当对端设备发送扫描请求时不会回应扫描应答,不可连接是指不能被任何设备连接。
  • 不可连接非定向广播(Non-connectable undirected mode):指可被任何设备发现但是不能被连接的广播。
  • 可连接低占空比定向广播(Connectable low duty cycle directed mode):同样是只能被指定设备所发现和连接的广播,但用户可修改广播间隔,最小和最大间隔不能小于100ms。

1.2 NimBLE

        前面经典蓝牙相关的文章都是基于Bluedroid框架进行开发的,这个协议栈即支持经典蓝牙也支持低功耗蓝牙,因为它的兼容性高所以资源占用也较高,如果在开发前期确认不使用经典蓝牙的情况下,应更优先选择NimBLE框架

        NimBLE其实是Apache Mynewt中自带的一个蓝牙协议栈,而Apache Mynewt是一个适用于微处理器的操作系统。ESP-IDF相当于魔改了这个组件,在FreeRTOS系统下移植了进来。NimBLE最大的优点就是资源占用少,更加适用于微处理器设备;当然它只支持低功耗蓝牙

        官方文档:BLE User Guide

2. 例程

        第一个例程搭建一个简单的观察者角色扫描周围的蓝牙设备,把扫描到的设备信息打印出来。第二个例程搭建一个简单的广播者角色,不断广播自己的信息,然后使用手机上的蓝牙调试助手查看信息。

2.1 menuconfig

        在写代码前要使能相关的menuconfig配置,不然是include不了相关的头文件的。首先配置蓝牙控制器为低功耗蓝牙模式。

        接着配置蓝牙主机协议栈为NimBLE。

        想更深度地定制的话可以看看协议栈配置这里,主要都是调整协议栈的一些运行配置,具体的作用基本一看就知道,一般来说都是保持默认即可。

        按“S”保存配置,再按“Q”退出。

2.2 代码

#include <stdint.h>
#include <string.h>
#include <inttypes.h>
#include <stdbool.h>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"#include "nvs.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_log.h"#include "host/ble_gap.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#include "services/gap/ble_svc_gap.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "modlog/modlog.h"#define TAG "app"#define OWN_NAME "ESP32"#define BDASTR "%02X:%02X:%02X:%02X:%02X:%02X"
#define BDA2STR(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5]static char * addr_str(const void *addr);
static void print_uuid(const ble_uuid_t *uuid);
static void print_adv_fields(const struct ble_hs_adv_fields *fields);
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
static void blecent_scan(void);
static void blecent_on_reset(int reason);
static void blecent_on_sync(void);static char * addr_str(const void *addr)
{static char buf[6 * 2 + 5 + 1];const uint8_t *u8p;u8p = addr;sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);return buf;
}static void print_uuid(const ble_uuid_t *uuid)
{char buf[BLE_UUID_STR_LEN];ESP_LOGI(TAG, "    %s", ble_uuid_to_str(uuid, buf));
}static void print_adv_fields(const struct ble_hs_adv_fields *fields)
{const uint8_t *u8p;int i;if (fields->flags != 0) {ESP_LOGI(TAG, "- flags=0x%02X", fields->flags);}if (fields->uuids16 != NULL) {ESP_LOGI(TAG, "- uuids16(%scomplete)=", fields->uuids16_is_complete ? "" : "in");for (i = 0; i < fields->num_uuids16; i++) {print_uuid(&fields->uuids16[i].u);}}if (fields->uuids32 != NULL) {ESP_LOGI(TAG, "- uuids32(%scomplete)=", fields->uuids32_is_complete ? "" : "in");for (i = 0; i < fields->num_uuids32; i++) {print_uuid(&fields->uuids32[i].u);}}if (fields->uuids128 != NULL) {ESP_LOGI(TAG, "- uuids128(%scomplete)=", fields->uuids128_is_complete ? "" : "in");for (i = 0; i < fields->num_uuids128; i++) {print_uuid(&fields->uuids128[i].u);}}if (fields->name != NULL) {char *name = malloc(fields->name_len);memcpy(name, fields->name, fields->name_len);ESP_LOGI(TAG, "- name(%scomplete)=%s", fields->name_is_complete ? "" : "in", name);free(name);}if (fields->tx_pwr_lvl_is_present) {ESP_LOGI(TAG, "- tx_pwr_lvl=%d", fields->tx_pwr_lvl);}if (fields->slave_itvl_range != NULL) {ESP_LOGI(TAG, "- slave_itvl_range=");ESP_LOG_BUFFER_HEX(TAG, fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);}if (fields->sm_tk_value_is_present) {ESP_LOGI(TAG, "- sm_tk_value=");ESP_LOG_BUFFER_HEX(TAG, fields->sm_tk_value, 16);}if (fields->sm_oob_flag_is_present) {ESP_LOGI(TAG, "- sm_oob_flag=%d", fields->sm_oob_flag);}if (fields->sol_uuids16 != NULL) {ESP_LOGI(TAG, "- sol_uuids16=");for (i = 0; i < fields->sol_num_uuids16; i++) {print_uuid(&fields->sol_uuids16[i].u);}}if (fields->sol_uuids32 != NULL) {ESP_LOGI(TAG, "- sol_uuids32=");for (i = 0; i < fields->sol_num_uuids32; i++) {print_uuid(&fields->sol_uuids32[i].u);}}if (fields->sol_uuids128 != NULL) {ESP_LOGI(TAG, "- sol_uuids128=");for (i = 0; i < fields->sol_num_uuids128; i++) {print_uuid(&fields->sol_uuids128[i].u);}}if (fields->svc_data_uuid16 != NULL) {ESP_LOGI(TAG, "- svc_data_uuid16=");ESP_LOG_BUFFER_HEX(TAG, fields->svc_data_uuid16, fields->svc_data_uuid16_len);}if (fields->public_tgt_addr != NULL) {u8p = fields->public_tgt_addr;for (i = 0; i < fields->num_public_tgt_addrs; i++) {ESP_LOGI(TAG, "- public_tgt_addr=%s", addr_str(u8p));u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;}}if (fields->random_tgt_addr != NULL) {u8p = fields->random_tgt_addr;for (i = 0; i < fields->num_random_tgt_addrs; i++) {ESP_LOGI(TAG, "- random_tgt_addr=%s ", addr_str(u8p));u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;}}if (fields->appearance_is_present) {ESP_LOGI(TAG, "- appearance=0x%04X", fields->appearance);}if (fields->adv_itvl_is_present) {ESP_LOGI(TAG, "- adv_itvl=0x%04X", fields->adv_itvl);}if (fields->device_addr_is_present) {u8p = fields->device_addr;ESP_LOGI(TAG, "- device_addr=%s", addr_str(u8p));u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;ESP_LOGI(TAG, "- addr_type: %d ", *u8p);}if (fields->le_role_is_present) {ESP_LOGI(TAG, "- le_role=%d", fields->le_role);}if (fields->svc_data_uuid32 != NULL) {ESP_LOGI(TAG, "- svc_data_uuid32=");ESP_LOG_BUFFER_HEX(TAG, fields->svc_data_uuid32, fields->svc_data_uuid32_len);}if (fields->svc_data_uuid128 != NULL) {ESP_LOGI(TAG, "- svc_data_uuid128=");ESP_LOG_BUFFER_HEX(TAG, fields->svc_data_uuid128, fields->svc_data_uuid128_len);}if (fields->uri != NULL) {ESP_LOGI(TAG, "- uri=");ESP_LOG_BUFFER_HEX(TAG, fields->uri, fields->uri_len);}if (fields->mfg_data != NULL) {ESP_LOGI(TAG, "- mfg_data=");ESP_LOG_BUFFER_HEX(TAG, fields->mfg_data, fields->mfg_data_len);}
}static int blecent_gap_event(struct ble_gap_event *event, void *arg)
{int rc = 0;switch (event->type) {/* 设备发现事件 */case BLE_GAP_EVENT_DISC:{ESP_LOGI(TAG, "[" BDASTR "] type: %d, data_len: %d, rssi: %d", BDA2STR(event->disc.addr.val), event->disc.event_type, event->disc.length_data, event->disc.rssi);/* 解析 */struct ble_hs_adv_fields fields;rc = ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data);if (rc != 0) {break;}/* 打印 */print_adv_fields(&fields);break;}/* 设备发现完成 */case BLE_GAP_EVENT_DISC_COMPLETE:MODLOG_DFLT(INFO, "discovery complete; reason=%d\n", event->disc_complete.reason);blecent_scan();break;default:break;}return rc;
}static void blecent_scan(void)
{int rc;/* 获取地址类型 */uint8_t own_addr_type;rc = ble_hs_id_infer_auto(0, &own_addr_type);if (rc != 0) {MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);return;}struct ble_gap_disc_params disc_params;disc_params.filter_duplicates = 1;disc_params.passive = 1;disc_params.itvl = 0;disc_params.window = 0;disc_params.filter_policy = 0;disc_params.limited = 0;rc = ble_gap_disc(own_addr_type, 5000, &disc_params, blecent_gap_event, NULL);if (rc != 0) {MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", rc);}
}static void blecent_on_reset(int reason)
{MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}static void blecent_on_sync(void)
{/* 配置地址 */if (ble_hs_util_ensure_addr(0) != 0) {return;}/* 启动扫描 */blecent_scan();
}void blecent_host_task(void *param)
{nimble_port_run();nimble_port_freertos_deinit();
}int app_main()
{/* 初始化NVS */esp_err_t ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ESP_ERROR_CHECK(nvs_flash_init());}/* 初始化控制器和NimBLE协议栈 */ret = nimble_port_init();if (ret != ESP_OK) {ESP_LOGE(TAG, "Failed to init nimble %d ", ret);return -1;}/* 配置主机 */ble_hs_cfg.reset_cb = blecent_on_reset;ble_hs_cfg.sync_cb = blecent_on_sync;ble_hs_cfg.store_status_cb = ble_store_util_status_rr;/* 设置设备名 */if (ble_svc_gap_device_name_set(OWN_NAME) != 0) {ESP_LOGE(TAG, "set device name failed");return -1;}/* 使能NimBLE协议栈 */nimble_port_freertos_init(blecent_host_task);return 0;
}

1. 配置NVS。

        NVS主要是用来保存协议栈的配置的,无论是WiFi还是蓝牙都是要配置的。

2. 初始化控制器和NimBLE协议栈。

         NimBLE的封装做得比Bluedroid还要好,调用nimble_port_init函数即可初始化完成。

3. 配置主机参数。

        通过ble_hs_cfg这个结构体配置,它是一个全局变量来的。里面的配置项非常的,感兴趣的可以看注释研究研究。

/** @brief Bluetooth Host main configuration structure** Those can be used by application to configure stack.** The only reason Security Manager (sm_ members) is configurable at runtime is* to simplify security testing. Defaults for those are configured by selecting* proper options in application's syscfg.*/
struct ble_hs_cfg {/*** An optional callback that gets executed upon registration of each GATT* resource (service, characteristic, or descriptor).*/ble_gatt_register_fn *gatts_register_cb;/*** An optional argument that gets passed to the GATT registration* callback.*/void *gatts_register_arg;/** Security Manager Local Input Output Capabilities */uint8_t sm_io_cap;/** @brief Security Manager OOB flag** If set proper flag in Pairing Request/Response will be set.*/unsigned sm_oob_data_flag:1;/** @brief Security Manager Bond flag** If set proper flag in Pairing Request/Response will be set. This results* in storing keys distributed during bonding.*/unsigned sm_bonding:1;/** @brief Security Manager MITM flag** If set proper flag in Pairing Request/Response will be set. This results* in requiring Man-In-The-Middle protection when pairing.*/unsigned sm_mitm:1;/** @brief Security Manager Secure Connections flag** If set proper flag in Pairing Request/Response will be set. This results* in using LE Secure Connections for pairing if also supported by remote* device. Fallback to legacy pairing if not supported by remote.*/unsigned sm_sc:1;/** @brief Security Manager Key Press Notification flag** Currently unsupported and should not be set.*/unsigned sm_keypress:1;/** @brief Security Manager Local Key Distribution Mask */uint8_t sm_our_key_dist;/** @brief Security Manager Remote Key Distribution Mask */uint8_t sm_their_key_dist;/** @brief Stack reset callback** This callback is executed when the host resets itself and the controller* due to fatal error.*/ble_hs_reset_fn *reset_cb;/** @brief Stack sync callback** This callback is executed when the host and controller become synced.* This happens at startup and after a reset.*/ble_hs_sync_fn *sync_cb;/** Callback to handle generation of security keys */ble_store_gen_key_fn *store_gen_key_cb;/* XXX: These need to go away. Instead, the nimble host package should* require the host-store API (not yet implemented)..*//** Storage Read callback handles read of security material */ble_store_read_fn *store_read_cb;/** Storage Write callback handles write of security material */ble_store_write_fn *store_write_cb;/** Storage Delete callback handles deletion of security material */ble_store_delete_fn *store_delete_cb;/** @brief Storage Status callback.** This callback gets executed when a persistence operation cannot be* performed or a persistence failure is imminent. For example, if is* insufficient storage capacity for a record to be persisted, this* function gets called to give the application the opportunity to make* room.*/ble_store_status_fn *store_status_cb;/** An optional argument that gets passed to the storage status callback. */void *store_status_arg;
};

        我这里就配置三个回调函数。一个是reset_cb,在控制器复位的时候会触发,这里就是简单地打印log。一个是sync_cb,当控制器同步的时候触发,一般就是刚启动和复位的时候,在这里面会调用blecent_scan函数使能一次扫描。

        这个函数里面,首先调用ble_hs_id_infer_auto函数来获取设备的地址类型。接着调用ble_gap_disc函数去使能扫描。参数一为前面获取到的地址类型;参数二为扫描配置参数,定义如下:

struct ble_gap_disc_params {/** Scan interval in 0.625ms units */uint16_t itvl;/** Scan window in 0.625ms units */uint16_t window;/** Scan filter policy */uint8_t filter_policy;/** If limited discovery procedure should be used */uint8_t limited:1;/** If passive scan should be used */uint8_t passive:1;/** If enable duplicates filtering */uint8_t filter_duplicates:1;
};
  • itvl:扫描间隔,0.625ms为一个单位;
  • window:扫描窗口,即一次扫描的时长,0.625ms为一个单位;
  • filter_policy:过滤策略;
  • limited:是否为有限制的扫描模式;
  • passive:是否使用被动扫描;
  • filter_duplicates:过滤重复结果。

        参数三为回调函数,参数五为用户数据。回调函数里面,主要处理两个事件。一个是设备发现事件(BLE_GAP_EVENT_DISC),每当扫描到一个广播者就会触发一次该事件,回调函数会返回广播者的信息,结构体如下:

struct ble_gap_disc_desc {/** Advertising PDU type. Can be one of following constants:*  - BLE_HCI_ADV_RPT_EVTYPE_ADV_IND*  - BLE_HCI_ADV_RPT_EVTYPE_DIR_IND*  - BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND*  - BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND*  - BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP*/uint8_t event_type;/** Advertising Data length */uint8_t length_data;/** Advertiser address */ble_addr_t addr;/** Received signal strength indication in dBm (127 if unavailable) */int8_t rssi;/** Advertising data */const uint8_t *data;/** Directed advertising address.  Valid for BLE_HCI_ADV_RPT_EVTYPE_DIR_IND* event type (BLE_ADDR_ANY otherwise).*/ble_addr_t direct_addr;
};
  • event_type:广播PDU类型;
  • length_data:广播数据长度;
  • addr:广播者地址;
  • rssi:信号值;
  • data:广播数据;
  • direct_addr:直接地址,只有广播类型为BLE_HCI_ADV_RPT_EVTYPE_DIR_IND时才有效。

        一般来说广播数据会包含非常多的字段,所以下面要调用ble_hs_adv_parse_fields函数把所有的字段都解析出来,后面就是一个简单的打印操作。第二个处理设备发现完成事件(BLE_GAP_EVENT_DISC_COMPLETE),当结束扫描任务的时候会触发,这里面我的操作就是重新启动一次扫描。

4. 设置设备名。

        下面调用ble_svc_gap_device_name_set函数设置自己的设备名。

5. 使能应用。

        调用nimble_port_freertos_init函数启动蓝牙应用,其实内部就是创建一个FreeRTOS的任务,所以参数传的就是任务回调函数。任务的相关逻辑ESP-IDF也为我们封装好的,所以里面调一个nimble_port_run函数和nimble_port_freertos_deinit函数即可,前者就是任务主循环,后者就是当任务退出的时候做的去初始化操作。

2.3 测试

        编译并烧录,就能看到类似下面的系统log。

相关文章:

【ESP32】ESP-IDF开发 | 低功耗蓝牙开发 | GAP协议 + 设备扫描例程

1. 简介 1.1 GAP协议 GAP&#xff08;General Access Protocol&#xff09;&#xff0c;全称通用访问协议&#xff0c;它定义了低功耗蓝牙设备的发现流程&#xff0c;设备管理和设备连接的建立。 低功耗蓝牙设备定义了4种角色&#xff1a; 广播者&#xff08;Broadcaster&…...

网络开发基础(游戏)之 Socket API

Socket简介 Socket (套接字)是网络编程的基础&#xff0c;在 C# 中通过 System.Net.Sockets 命名空间提供了一套完整的 API 来实现网络通信。 网络上的两个程序通过一个双向的通信连接实现数据交换&#xff0c; 这个连接的一端称为一个Socket。 一个Socket包含了进行网络通信必…...

行为审计软件:企业合规与内部监控的数字守门人

在当今高度数字化的商业环境中&#xff0c;企业运营产生的电子数据呈指数级增长&#xff0c;员工行为也日益复杂多样。行为审计软件应运而生&#xff0c;成为现代企业管理不可或缺的工具。这类软件通过系统化记录、分析和报告组织内部用户活动&#xff0c;帮助企业管理风险、确…...

bat脚本转换为EXE应用程序文件

很多时候,我们使用电脑时会编辑bat脚本文件 很多时候&#xff0c;我们制作的玩笑&#xff0c;病毒也会使用这个格式. 但这个格式也有很多缺点 1,如果是需要管理员运行的程序,需要费劲的自己使用管理员身份运行 2,文件并不为大家所熟知,认同度不高 3,可以非常轻松的看到原代…...

细说STM32单片机FreeRTOS任务管理API函数vTaskList()的使用方法

目录 一、函数vTaskList() 1、 函数说明 2、返回的字符串表格说明 3、函数的使用方法 二、 vTaskList()的应用示例 1、示例功能、项目设置 2、软件设计 &#xff08;1&#xff09;main.c &#xff08;2&#xff09;freertos.c &#xff08;3&#xff09;FreeRTOSConf…...

DNS主从同步

安装软件 主配置中完成DNS解析&#xff1a;192.168.131.134 [rootlocalhost ~]# mount /dev/sr0 /mnt [rootlocalhost ~]# vim /etc/yum.repos.d/myrepo.repo [base] namebase baseurl/mnt/BaseOS gpgchcek0 enable1 [base2] namebase2 baseurl/mnt/AppStream gpgchcek0 enab…...

双指针算法(部分例题解析)

快慢指针左右指针 前言 双指针&#xff0c;它通过设置两个指针来遍历数据&#xff0c;从而实现高效的查找、排序、去重等操作。双指针算法的核心在于通过合理地移动这两个指针&#xff0c;减少不必要的遍历&#xff0c;提高算法的效率。 283. 移动零 - 力扣&#xff08;LeetCo…...

解决Windows打印问题的集成软件

家里或公司电脑经常为连不上打印机而烦恼&#xff0c;今天给大家推荐一款修复打印工具&#xff0c;该工具是采用易语言开发的集成化打印机故障修复软件&#xff0c;专为解决 Windows 系统&#xff08;含 32/64 位 Windows 7/10/11&#xff09;中因权限配置、服务异常、补丁缺失…...

神经网络模型应用到机器学习时的难点

虽然神经网络具有非常强的表达能力&#xff0c;但是当应用神经网络模型到机器学习时依然存在一些难点问题。主要分为两大类: 优化问题&#xff1a;深度神经网络的优化十分困难。 首先&#xff0c;神经网络的损失函数是一个非凸函数&#xff0c;找到全局最优解通常比较困难。 …...

警惕阿里云中的yum update操作不当导致:/sbin/init被清空导致Linux无法正常启动

由于使用阿里云进行部署测试&#xff0c;因而会对yum update进行操作&#xff0c;这两天更新了systemd-239-82.0.3.4.al8.2.x86_64&#xff0c;但存在报错&#xff0c;然后进行yum history undo和清空yum cache&#xff0c;但出现操作Linux命令行无效。具体来说&#xff0c;几个…...

关系型数据库MYSQL(续)

目录 三.MySQL事务原理分析 1.事务是什么&#xff1f; 2.执行事务的目的是什么&#xff1f; 3.事务是由什么组成的&#xff1f; 4.事务的特征是什么&#xff1f; 5.事务控制语句 6.ACID特性 6.1原子性&#xff08;A&#xff09; 6.2隔离性&#xff08;I&#xff09; …...

WInform当今技术特性分析

Windows Forms (WinForms) 技术特性分析 引言 Windows Forms (WinForms) 作为微软最早推出的基于.NET的图形用户界面开发框架&#xff0c;已经存在了20多年。在如今充满了各种现代UI框架的软件开发生态系统中&#xff0c;WinForms仍然保持着其独特的地位。本文将深入分析WinF…...

day46——两数之和-输入有序数组(LeetCode-167)

题目描述 给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 &l…...

Python 一等函数( 把函数视作对象)

把函数视作对象 示例 5-1 中的控制台会话表明&#xff0c;Python 函数是对象。这里我们创建了一 个函数&#xff0c;然后调用它&#xff0c;读取它的 doc 属性&#xff0c;并且确定函数对象本 身是 function 类的实例。 示例 5-1 创建并测试一个函数&#xff0c;然后读取它的…...

运筹学之模拟退火

目录 一、历史二、精髓思想三、案例与代码实现 一、历史 问&#xff1a;谁在什么时候提出模拟退火&#xff1f;答&#xff1a;模拟退火算法&#xff08;Simulated Annealing&#xff0c;SA&#xff09;是由斯图尔特柯尔斯基&#xff08;Scott Kirkpatrick&#xff09; 等人在 …...

PHP实现简单的爬虫功能

<?php// 目标URL $url https://example.com;// 初始化cURL $ch curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, Mozilla/5…...

树莓派5-开发应用笔记

0.树莓派系统目录 /home&#xff1a;用户目录。 除了root用户外&#xff0c;其他所有的使用者的数据都存放在这个目录下&#xff0c;在树莓派的系统中&#xff0c;/home目录中有一个pi的子目录,这个就是pi用户的默认目录。 /bin&#xff1a; 主要放置系统的必备执行文件目录。 …...

PostgreSQL 通过 copy 命令导入几何数据 及 通过 CopyManager.copyIn() 导入几何数据

COPY命令介绍 copy是postgresql提供的一个专门用于快速导入导出数据的命令,通常用于从文件(TXT、CSV等)或标准输入输出中读取或写入数据。适合批量导入导出数据,速度快。 默认情况下,如果在处理过程中遇到错误,COPY将失败。 COPY只能用于表,不能用于视图!!! COPY…...

8.5/Q1,Charls最新文章解读

文章题目&#xff1a;Atherogenic index of plasma, high sensitivity C-reactive protein and incident diabetes among middle-aged and elderly adults in China: a national cohort study DOI&#xff1a;10.1186/s12933-025-02653-4 中文标题&#xff1a;中国中老年人群血…...

k8s 调整Node节点 Max_Pods

默认情况下&#xff0c;Kubernetes集群中一个Node最多能起110个Pod。 这是基于性能和资源管理的考虑&#xff0c;以确保Kubernetes集群的稳定性和可靠性。 查看kht125节点上支持的最大pod数量: kubectl describe node kht125 | grep -i “Capacity|Allocatable” -A 6 调整…...

深度补全网络:CSPN++ 有哪些开源项目

关于 ‌CSPN&#xff08;Convolutional Spatial Propagation Network&#xff09;‌ 的开源项目&#xff0c;目前官方或社区维护的完整实现较为有限&#xff0c;但以下资源可作为研究深度补全任务的参考&#xff1a; ‌1. 官方实现 & 相关论文‌ ‌原始论文与代码‌ CSPN 的…...

使用Service发布前后端应用程序

使用Service发布前后端应用程序 文章目录 使用Service发布前后端应用程序[toc]一、创建并发布后端应用程序二、创建并发布前端应用程序三、通过前端发送流量进行测试 部署前端&#xff08;Frontend&#xff09;微服务和后端&#xff08;Backend&#xff09;微服务是比较常见的应…...

Ubuntu20.04下Docker方案实现多平台SDK编译

0 前言 熟悉嵌入式平台Linux SDK编译流程的小伙伴都知道,假如平台a要求必须在Ubuntu18.04下编译,平台b要求要Ubuntu22.04的环境,那我只有Ubuntu20.04,或者说我的电脑硬件配置最高只能支持Ubuntu20.04怎么办?强行在Ubuntu20.04下编译,编又编不过,换到旧版本我又不愿意,…...

-SSRF 服务端请求Gopher 伪协议无回显利用黑白盒挖掘业务功能点

1 、 SSRF 漏洞原理 SSRF(Server-Side Request Forgery: 服务器端请求伪造 ) 一种由攻击者构造形成由服务端发起请求的一个安全漏洞 ; 一般情况下&#xff0c; SSRF 攻击的目标是从外网无法访问的内部系统。 &#xff08;正是因为它是由服务端发起的&#xff0c;所以它能…...

事件冒泡与捕获

一、事件流基础&#xff1a;事件冒泡与捕获的起源 事件流概念 事件发生时在DOM节点上的传播顺序&#xff0c;触发一个节点的事件会连锁触发相关节点的事件。 两种对立模型 事件捕获&#xff08;微软提出&#xff09;&#xff1a;事件从文档根节点&#xff08;如document&#…...

《AI大模型应知应会100篇》第27篇:模型温度参数调节:控制创造性与确定性

第27篇&#xff1a;模型温度参数调节&#xff1a;控制创造性与确定性 摘要 在大语言模型的使用中&#xff0c;“温度”&#xff08;Temperature&#xff09;是一个关键参数&#xff0c;它决定了模型输出的创造性和确定性之间的平衡。通过调整温度参数&#xff0c;您可以根据任…...

聊聊Doris的数据模型,如何用结构化设计解决实时分析难题

传统 OLAP 系统的局限 在大数据实时分析领域&#xff0c;数据模型设计直接决定了系统的查询性能、存储效率与业务适配性。Apache Doris作为新一代MPP分析型数据库&#xff0c;通过独创的多模型融合架构&#xff0c;在业内率先实现了"一份数据支持多种分析范式"的能力…...

LNA设计

设计目的 为后级提供足够的增益以克服后级电路噪声 尽可能小的噪声和信号失真 确保输入和输出端的阻抗匹配 确保信号线性度 评价标准 噪声系数 功率增益 工作频率和带宽 输入信号功率动态范围 端口电压驻波比 稳定性 基于SP模型的LNA设计 直流分析 S参数分析 设计指标 &#xf…...

小红书爬虫,小红书api,小红书数据挖掘

背景&#xff1a; 小红书&#xff08;Xiaohongshu&#xff09;是一款结合社交、购物和内容分享的移动应用&#xff0c;近年来在中国以及全球范围内拥有大量的用户群体。小红书上的内容包括用户的消费体验、生活方式、旅行分享、时尚搭配等。通过这些内容&#xff0c;用户可以了…...

C++ STL 环形队列模拟实现

C STL 环形队列模拟实现 下面是一个使用C STL实现的环形队列&#xff08;Circular Queue&#xff09;的完整示例&#xff1a; #include <iostream> #include <vector> #include <stdexcept>template <typename T> class CircularQueue { private:std…...