【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)

【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)

前言

今天整理蓝牙Demo代码,查看官网时发现自己帐号没有登录,竟然也可以访问最新的API文档,真是喜大奔普。看来华为已经开始对外开放最新的API文档,不再有白名单限制了。

在这里插入图片描述

HarmonyOS NEXTDeveloper Beta5

接下来讲解蓝牙配对流程。低功耗蓝牙BLE模式将在下面章节讲解。

蓝牙配对业务流程

1‌.设备进入可被发现模式‌:
首先,设备需要进入可被发现模式,这样周围的蓝牙设备才能识别到它。一方设备(如手机)会主动搜索附近的蓝牙设备,并列出所有可用的配对选项。

2‌.选择并触发配对请求‌:
用户从列表中选择想要连接的设备,并触发配对请求。此时,双方设备会交换一系列的身份验证信息,以确保彼此的身份安全无误。在这个过程中,可能会要求用户输入配对码(如PIN码)或在设备上确认配对请求。

3‌.身份验证和加密‌:
一旦身份验证通过,设备间就会建立安全的连接通道,这一过程称为“配对成功”。配对完成后,设备之间的连接就建立了,它们可以开始传输数据。

4‌.数据传输‌:
设备间通过蓝牙进行数据传输,可以传输音频、文件等多种类型的数据。

5‌.断开连接‌:
当数据传输完成后,蓝牙设备可以断开连接。断开连接的操作可以通过设备上的按钮或者软件来实现。

蓝牙配对通常是一次性的,即一旦设备成功配对,它们会在后续的连接中自动识别并连接,无需再次进行配对过程(除非设备被重置或用户手动取消配对)

以下是传统的蓝牙配对流程图仅供参考:
在这里插入图片描述

常规蓝牙配对Demo

在这里插入图片描述

Demo包括以下内容:
1.蓝牙权限开启
2.蓝牙开启/关闭
3.蓝牙扫描开启/关闭
4.蓝牙配对
5.蓝牙code协议确认

蓝牙UI交互类

import { access } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { BlueToothMgr } from '../manager/BlueToothMgr';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { connection } from '@kit.ConnectivityKit';
import { map } from '@kit.ConnectivityKit';
import { pbap } from '@kit.ConnectivityKit';
import { HashMap } from '@kit.ArkTS';
import { DeviceInfo } from '../info/DeviceInfo';
import { promptAction } from '@kit.ArkUI';

struct Index {private TAG: string = "BlueToothTest";// 扫描状态定时器private mNumInterval: number = -1;// 当前设备蓝牙名 mCurrentDeviceName: string = "";// 蓝牙状态 ('onChangeBlueTooth') isStartBlueTooth: boolean = false;// 蓝牙扫描状态 ('onChangeBlueTooth') isStartScan: boolean = false;// 当前蓝牙权限 userGrant: boolean = false;// 扫描到设备名 mMapDevice: HashMap<string, DeviceInfo> = new HashMap();// ui展现的设备列表 mListDeviceInfo: Array<DeviceInfo> = new Array();async aboutToAppear() {await this.requestBlueToothPermission();let state = access.getState();console.log(this.TAG, "getState state: " + state);if(state == 2){this.isStartBlueTooth = true;console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);}else{this.isStartBlueTooth = false;console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);}}private onChangeBlueTooth(){if(!this.isStartBlueTooth){this.mMapDevice = new HashMap();return;}this.mCurrentDeviceName = BlueToothMgr.Ins().getCurrentDeviceName();}// 用户申请权限async reqPermissionsFromUser(): Promise<number[]> {let context = getContext() as common.UIAbilityContext;let atManager = abilityAccessCtrl.createAtManager();let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH']);return grantStatus.authResults;}// 用户申请蓝牙权限async requestBlueToothPermission() {let grantStatus = await this.reqPermissionsFromUser();for (let i = 0; i < grantStatus.length; i++) {if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作this.userGrant = true;promptAction.showToast({ message: "蓝牙授权成功!"});}else{promptAction.showToast({ message: "蓝牙授权失败!"});}}}setBlueToothScan = ()=>{if(!this.isStartScan){promptAction.showToast({ message: "开启扫描!"});BlueToothMgr.Ins().startScanDevice((data: Array<string>)=>{let deviceId: string = data[0];if(this.mMapDevice.hasKey(deviceId)){// 重复设备,丢弃不处理}else{// 添加到表中let deviceInfo: DeviceInfo = new DeviceInfo();deviceInfo.deviceId = deviceId;deviceInfo.deviceName = BlueToothMgr.Ins().getDeviceName(deviceId);deviceInfo.deviceClass = BlueToothMgr.Ins().getDeviceClass(deviceId);this.mMapDevice.set(deviceId, deviceInfo);this.mListDeviceInfo = this.mListDeviceInfo.concat(deviceInfo);}});this.mMapDevice.clear();this.mListDeviceInfo = [];// 开启定时器this.mNumInterval = setInterval(()=>{let discovering = BlueToothMgr.Ins().isCurrentDiscovering();if(!discovering){this.closeScanDevice();}}, 1000);this.isStartScan = true;}else{promptAction.showToast({ message: "关闭扫描!"});BlueToothMgr.Ins().stopScanDevice();this.closeScanDevice();}}private closeScanDevice(){clearInterval(this.mNumInterval);this.isStartScan = false;}setBlueToothState = ()=>{try {if(!this.isStartBlueTooth){// 开启蓝牙BlueToothMgr.Ins().setBlueToothAccess(true, (state: access.BluetoothState) => {console.log(this.TAG, "getState setBlueToothAccessTrue: " + state);if(state == access.BluetoothState.STATE_ON){this.isStartBlueTooth = true;promptAction.showToast({ message: "开启蓝牙!"});console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);}});}else{BlueToothMgr.Ins().setBlueToothAccess(false, (state: access.BluetoothState) => {console.log(this.TAG, "getState setBlueToothAccessFalse: " + state);if(state == access.BluetoothState.STATE_OFF){this.isStartBlueTooth = false;promptAction.showToast({ message: "关闭蓝牙!"});console.log(this.TAG, "getState isStartBlueTooth: " + this.isStartBlueTooth);}});}} catch (err) {console.error(this.TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}private isLog(){console.log(this.TAG, "isLog isStartBlueTooth: " + this.isStartBlueTooth);return true;}build() {Column() {if(this.userGrant){if(this.isLog()){Text("当前蓝牙设备信息:\n " + this.mCurrentDeviceName).fontSize(px2fp(80)).margin({ top: px2vp(100) }).fontWeight(FontWeight.Bold)Text(this.isStartBlueTooth ? "蓝牙状态: 开启" : "蓝牙状态: 关闭").fontSize(px2fp(80)).margin({ top: px2vp(100) }).fontWeight(FontWeight.Bold).onClick(this.setBlueToothState)Text(this.isStartScan ? "蓝牙扫描: 开启ing" : "蓝牙扫描: 关闭").margin({ top: px2vp(100) }).fontSize(px2fp(80)).fontWeight(FontWeight.Bold).onClick(this.setBlueToothScan)this.ListView()}}}.justifyContent(FlexAlign.Center).height('100%').width('100%')} ListView(){List() {ForEach(this.mListDeviceInfo, (item: DeviceInfo, index: number) => {ListItem() {Column(){Row() {Text("设备ID: " + item.deviceId).fontSize(px2fp(42)).fontColor(Color.Black)Blank()Text("设备名: " + item.deviceName).fontSize(px2fp(42)).fontColor(Color.Black)}.width('100%')Text(item.deviceClass).fontSize(px2fp(42)).fontColor(Color.Black)}.width('100%').height(px2vp(200)).justifyContent(FlexAlign.Start).onClick(()=>{// 点击选择处理配对AlertDialog.show({title:"选择配对",message:"是否选择该设备进行蓝牙配对?",autoCancel: true,primaryButton: {value:"确定",action:()=>{promptAction.showToast({ message: item.deviceName + "配对ing!"});BlueToothMgr.Ins().pairDevice(item.deviceId);}},secondaryButton: {value:"取消",action:()=>{promptAction.showToast({ message: "取消!"});}},cancel:()=>{promptAction.showToast({ message: "取消!"});}})})}}, (item: string, index: number) => JSON.stringify(item) + index)}.width('100%')}
}

蓝牙管理类

import { access, ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { connection } from '@kit.ConnectivityKit';export class BlueToothMgr {private TAG: string = "BlueToothTest";private static mBlueToothMgr: BlueToothMgr | undefined = undefined;private advHandle: number = 0xFF; // default invalid valueprivate mDeviceDiscoverArr: Array<string> = new Array<string>();public static Ins(){if(!BlueToothMgr.mBlueToothMgr){BlueToothMgr.mBlueToothMgr = new BlueToothMgr();BlueToothMgr.init();}return BlueToothMgr.mBlueToothMgr;}private static init(){try {connection.on('pinRequired', (data: connection.PinRequiredParam) =>{// data为配对请求参数console.info("BlueToothTest",'pinRequired pin required = '+ JSON.stringify(data));});} catch (err) {console.error("BlueToothTest", 'pinRequired errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}/*** 当前设备蓝牙设备名称*/public getCurrentDeviceName(){let localName: string = "";try {localName = connection.getLocalName();console.info(this.TAG, 'getCurrentDeviceName localName: ' + localName);} catch (err) {console.error(this.TAG, 'getCurrentDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return localName;}/*** 当前设备蓝牙可发现状态*/public isCurrentDiscovering(){let res: boolean = false;try {res = connection.isBluetoothDiscovering();console.info(this.TAG, 'isCurrentDiscovering isBluetoothDiscovering: ' + res);} catch (err) {console.error(this.TAG, 'isCurrentDiscovering errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return res;}// STATE_OFF	0	表示蓝牙已关闭。// STATE_TURNING_ON	1	表示蓝牙正在打开。// STATE_ON	2	表示蓝牙已打开。// STATE_TURNING_OFF	3	表示蓝牙正在关闭。// STATE_BLE_TURNING_ON	4	表示蓝牙正在打开LE-only模式。// STATE_BLE_ON	5	表示蓝牙正处于LE-only模式。// STATE_BLE_TURNING_OFF	6	表示蓝牙正在关闭LE-only模式。public getBlueToothState(): access.BluetoothState {let state = access.getState();return state;}/*** 设置蓝牙访问(开关状态)* @param isAccess true: 打开蓝牙*/setBlueToothAccess(isAccess: boolean, callbackBluetoothState: Callback<access.BluetoothState>){try {if(isAccess){console.info(this.TAG, 'bluetooth enableBluetooth 1');access.enableBluetooth();console.info(this.TAG, 'bluetooth enableBluetooth 2');access.on('stateChange', (data: access.BluetoothState) => {let btStateMessage = this.switchState(data);if (btStateMessage == 'STATE_ON') {access.off('stateChange');}console.info(this.TAG, 'bluetooth statues: ' + btStateMessage);callbackBluetoothState(data);})}else{console.info(this.TAG, 'bluetooth disableBluetooth 1');access.disableBluetooth();console.info(this.TAG, 'bluetooth disableBluetooth 2');access.on('stateChange', (data: access.BluetoothState) => {let btStateMessage = this.switchState(data);if (btStateMessage == 'STATE_OFF') {access.off('stateChange');}console.info(this.TAG, "bluetooth statues: " + btStateMessage);callbackBluetoothState(data);})}} catch (err) {console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}private switchState(data: access.BluetoothState){let btStateMessage = '';switch (data) {case 0:btStateMessage += 'STATE_OFF';break;case 1:btStateMessage += 'STATE_TURNING_ON';break;case 2:btStateMessage += 'STATE_ON';break;case 3:btStateMessage += 'STATE_TURNING_OFF';break;case 4:btStateMessage += 'STATE_BLE_TURNING_ON';break;case 5:btStateMessage += 'STATE_BLE_ON';break;case 6:btStateMessage += 'STATE_BLE_TURNING_OFF';break;default:btStateMessage += 'unknown status';break;}return btStateMessage;}/*** 主播蓝牙广播*/public registerBroadcast(){try {ble.on('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => {console.info(this.TAG, 'bluetooth advertising state = ' + JSON.stringify(data));AppStorage.setOrCreate('advertiserState', data.state);});} catch (err) {console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}/*** 开启蓝牙广播*/public async startBroadcast(valueBuffer: Uint8Array){// 表示发送广播的相关参数。let setting: ble.AdvertiseSetting = {// 表示广播间隔,最小值设置160个slot表示100ms,最大值设置16384个slot,默认值设置为1600个slot表示1s。interval: 160,// 表示发送功率,最小值设置-127,最大值设置1,默认值设置-7,单位dbm。推荐值:高档(1),中档(-7),低档(-15)。txPower: 0,// 表示是否是可连接广播,默认值设置为true,表示可连接,false表示不可连接。connectable: true};// BLE广播数据包的内容。let manufactureDataUnit: ble.ManufactureData = {// 表示制造商的ID,由蓝牙SIG分配。manufactureId: 4567,manufactureValue: valueBuffer.buffer};let serviceValueBuffer = new Uint8Array(4);serviceValueBuffer[0] = 5;serviceValueBuffer[1] = 6;serviceValueBuffer[2] = 7;serviceValueBuffer[3] = 8;// 广播包中服务数据内容。let serviceDataUnit: ble.ServiceData = {serviceUuid: "00001888-0000-1000-8000-00805f9b34fb",serviceValue: serviceValueBuffer.buffer};// 表示广播的数据包内容。let advData: ble.AdvertiseData = {serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"],manufactureData: [manufactureDataUnit],serviceData: [serviceDataUnit],includeDeviceName: false // 表示是否携带设备名,可选参数。注意带上设备名时广播包长度不能超出31个字节。};// 表示回复扫描请求的响应内容。let advResponse: ble.AdvertiseData = {serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"],manufactureData: [manufactureDataUnit],serviceData: [serviceDataUnit]};// 首次启动广播设置的参数。let advertisingParams: ble.AdvertisingParams = {advertisingSettings: setting,advertisingData: advData,advertisingResponse: advResponse,// 	表示发送广播持续的时间。单位为10ms,有效范围为1(10ms)到65535(655350ms),如果未指定此参数或者将其设置为0,则会连续发送广播。duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会临时停止,可重新启动发送}// 首次启动广播,且获取所启动广播的标识IDtry {this.registerBroadcast();this.advHandle = await ble.startAdvertising(advertisingParams);} catch (err) {console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}/*** 开始蓝牙扫描*/public startScanDevice(callback: Callback<Array<string>>){try {connection.on('bluetoothDeviceFind', (data: Array<string>)=>{// 随机MAC地址console.info(this.TAG, 'bluetooth device bluetoothDeviceFind = '+ JSON.stringify(data));callback(data);});connection.startBluetoothDiscovery();} catch (err) {console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}/*** 停止蓝牙扫描*/public stopScanDevice(){try {connection.off('bluetoothDeviceFind');connection.stopBluetoothDiscovery();} catch (err) {console.error(this.TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}public getDeviceName(deviceID: string){let remoteDeviceName: string = "";try {remoteDeviceName = connection.getRemoteDeviceName(deviceID);console.info(this.TAG, 'getDeviceName device = '+ JSON.stringify(remoteDeviceName));} catch (err) {console.error(this.TAG, 'getDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return remoteDeviceName;}public getDeviceClass(deviceID: string){let remoteDeviceClass: string = "";try {let classObj = connection.getRemoteDeviceClass(deviceID);remoteDeviceClass = JSON.stringify(classObj);} catch (err) {console.error(this.TAG, 'getDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return remoteDeviceClass;}// BondState// BOND_STATE_INVALID	0	无效的配对。// BOND_STATE_BONDING	1	正在配对。// BOND_STATE_BONDED	2	已配对。/*** 发起配对蓝牙*/public pairDevice(deviceID: string){try {connection.on('bondStateChange', (data: connection.BondStateParam) =>{console.info(this.TAG, 'pairDevice pair state = '+ JSON.stringify(data));// 当蓝牙配对类型PinType为PIN_TYPE_ENTER_PIN_CODE或PIN_TYPE_PIN_16_DIGITS时调用此接口,请求用户输入PIN码。});connection.pairDevice(deviceID, (err: BusinessError) => {console.info(this.TAG, 'pairDevice device name err:' + JSON.stringify(err));});} catch (err) {console.error(this.TAG, 'pairDevice errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}}

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/fda00352f394401cbc3a6b884469f6ce.png在这里插入图片描述

DEMO完成示例资源下载链接

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

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

相关文章

《机器学习》—— 使用过采样方法实现逻辑回归分类问题

文章目录 一、什么是过采样方法&#xff1f;二、使用过采样方法实现逻辑回归分类问题三、过采样的优缺点 本篇内容是 基于Python的scikit-learn库中sklearn.linear_model 类中的 LogisticRegression&#xff08;&#xff09;逻辑回归方法实现的&#xff0c;其内容中只是在处理…

使用Java进行中小学违规教育培训数据采集实践-以某城市为例

目录 前言 一、违规教育信息 1、内容管理 2、转换后的内容 二、数据库设计 1、空间数据库 三、字符地址位置转换空间信息 1、实现时序图 2、后台实体类的设计与实现 3、数据持久化操作 四、总结 前言 时间来到2024年8月24日&#xff0c;时间过得很快&#xff0c;2024…

芯片后端之 PT 使用 report_timing 产生报告 之 -include_hierarchical_pins 选项

今天,我们再学习一点点 后仿真相关技能。 那就是,了解 report_timing 中的 -include_hierarchical_pins 选项。 如果我们仅仅使用如下命令,执行后会发现: pt_shell> report_timing -from FF1/CK -to FF2/d -delay_type max 我们使用命令 report_timing 报出的如上路…

C++篇:C向C++迈进(上)

引言 C语言作为编程基石&#xff0c;其高效与直接性深受开发者喜爱。然而&#xff0c;随着软件复杂度的增加&#xff0c;C以其面向对象及高级特性成为了新的选择。我们接下来将学习C&#xff0c;从C语言迈向C。 什么是C C 是一种高级语言&#xff0c;由 Bjarne Stroustrup 于…

python测试框架之Pytest

初识Pytest Pytest1.Pytest的特点&#xff1a;2.Pytest的基本使用规则3.pytest安装1&#xff09;使用编译器安装2&#xff09;使用命令安装 4.pytest规则 Pytest Pytest是python的一个第三方单元测试库&#xff0c;它的目的是让单元测试变得容易&#xff0c;并且也能扩展到支持…

VSCode插件 live Server

普通打开 安装live Server 包含端口 说明内置了服务器

视频插帧—— RIFE 和 IFNet 的机制和应用

介绍 最近&#xff0c;数字和模拟技术开始加速融合。我们生活在一个人工智能技术能够显著提高质量的时代&#xff0c;只要模拟材料能够数字化。 例如&#xff0c;讨论中涉及到的纸艺软件&#xff0c;纸龙的移动模型被时间锁定&#xff0c;以大约 3 fps&#xff08;每秒帧数&a…

一元四次方程求解-【附MATLAB代码】

目录 前言 求解方法 MATLAB验证 附&#xff1a;一元四次方程的故事 前言 最近在研究机器人的干涉&#xff08;碰撞&#xff09;检测&#xff0c;遇到了一个问题&#xff0c;就是在求椭圆到原点的最短距离时&#xff0c;构建的方程是一个一元四次方程。无论是高中的初等数学…

Flink1.18 同步 MySQL 到 Doris

一、前言 使用Apache Flink实现数据同步的ETL&#xff08;抽取、转换、加载&#xff09;过程通常涉及从源系统&#xff08;如数据库、消息队列或文件&#xff09;中抽取数据&#xff0c;进行必要的转换&#xff0c;然后将数据加载到目标系统&#xff08;如另一个数据库…

【Node】【1】node和nvm安装

安装nvm、node、npm 安装node 18 &#xff0c;最简单的办法是使用nvm&#xff0c;就不用手动安装了&#xff0c;那么就得先安装nvm。 NVM 是Node Version Manager&#xff0c;用于管理 Node.js 版本。你设备上的不同项目可能使用不同版本的 Node.js。通过 nvm&#xff0c;用户…

HTTP与HTTPS:数据安全性的差异与风险分析

在现代互联网通信中&#xff0c;HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;和HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff0c;超文本传输安全协议&#xff09;是两种常见的网络协议&#xff0c;它们都在浏览器和服…

Telegram mini app 本地开发配置

前言&#xff1a; 为了能在telegram里本地调试mini app&#xff0c;参考了网上很多方案&#xff0c;踩了不少坑。最后整了一个适合自己的方案&#xff0c;记录一下。 这个方案一定不是最好的&#xff0c;不过是目前适合我上手开发的方案了。 本文章适合需要在 telegram 本地…

拼多多20家店铺登满了怎么办

解决拼多多多店铺管理难题&#xff1a;河鱼浏览器的妙用&#xff01; 在电商领域&#xff0c;拼多多已经成为许多商家的重要销售渠道。然而&#xff0c;对于同时管理多个拼多多店铺的商家来说&#xff0c;如何高效地运营这些店铺成为了一大挑战。特别是当你的店铺数量达到20家…

简化登录流程,助力应用建立用户体系

随着智能手机和移动应用的普及&#xff0c;用户需要在不同的应用中注册和登录账号&#xff0c;传统的账号注册和登录流程需要用户输入用户名和密码&#xff0c;这不仅繁琐而且容易造成用户流失。 华为账号服务&#xff08;Account Kit&#xff09;提供简单、快速、安全的登录功…

docker镜像,ip,端口映射,持久化

docker 镜像的迁移&#xff1a;导出和导入镜像 查看镜像&#xff1a; [rootdocker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 2 years ago 231MB 打包 将镜像打包&#xff0c;找到save,可以将…

【复旦微FM33 MCU 外设开发指南】外设篇1——GPIO

前言 本系列基于复旦微FM33系列单片机的DataSheet编写&#xff0c;旨在提供一些开发指南。 本文章及本系列其他文章将持续更新&#xff0c;本系列其它文章请跳转【复旦微FM33 MCU 外设开发指南】总集篇 本文章最后更新日期&#xff1a;2024/08/25 文章目录 前言GPIO工作时钟…

DevOps入门(上)

1: DevOps概念 &#xfeff;&#xfeff;DevOps: Development 和 Operations 的组合 DevOps 看作开发&#xff08;软件工程&#xff09;、技术运营和质量保障&#xff08;QA&#xff09;三者的交集。 突出重视软件开发人员和运维人员的沟通合作&#xff0c;通过自动化流程来使…

私域流量的落脚点与开源 AI 智能名片 2+1 链动商城小程序

摘要&#xff1a;本文探讨了私域流量的重要性及其落脚点&#xff0c;分析了快钱收割思维在私域流量运作中的弊端。同时&#xff0c;引入开源 AI 智能名片 21 链动商城小程序&#xff0c;阐述其在成就人格化 IP 和打造品牌域、通过直播电商规模化变现方面的作用&#xff0c;为企…

[论文笔记]Improving Retrieval Augmented Language Model with Self-Reasoning

引言 今天带来一篇百度提出的关于提升RAG准确率的论文笔记&#xff0c;Improving Retrieval Augmented Language Model with Self-Reasoning。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 检索增强语言模型(Retrie…

PostgreSQL11 | 事务处理与并发控制

PostgreSQL11 | 事务处理与并发控制 本文章代码已在pgsql11.22版本上运行且通过&#xff0c;展示页由pgAdmin8.4版本提供&#xff0c;本文章第一次采用md文档&#xff0c;效果比csdn官方富文本编辑器好用&#xff0c;以后的文章都将采用md文档 事务管理简介 事物是pgsql中的…