【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (一)

【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得(一)

一、前言

在这里插入图片描述

鸿蒙官网文档中蓝牙部分,对于之前没有开发过蓝牙的同学,使用和查阅起来不是很方便。因为只是API的调用说明。并没有蓝牙整个调用流程的讲解,所以看起来会云里雾里。特别是针对低功耗蓝牙来说,对于普通蓝牙,开发步骤较少还好。

在开发蓝牙和低功耗蓝牙之前,我们最好对蓝牙开发有一定的基础认识,这样开发起来才能整体框架,知道自己在做什么,只是根据文档API的调用,很容易漏处理。

本章主要讲解低功耗蓝牙BLE的开发调用,普通蓝牙在之前的章节已经系统的讲过了。可以参见,【HarmonyOS】鸿蒙应用蓝牙功能实现 (一,二,三)系列。

二、BLE低功耗蓝牙的基础知识

1.低功耗蓝牙是什么?
低功耗蓝牙(Bluetooth Low Energy,简称BLE)是蓝牙技术的一种变体,也被称为蓝牙4.0。BLE技术通过一系列的技术和优化措施,如减少广播频段和广播时射频开启时间、采用深度睡眠状态(Duty-Cycle)、优化连接机制等,显著降低了设备的功耗。这使得BLE设备在长时间运行下,电池寿命得到大大延长。比如智能穿戴设备、智能家电、传感器等

2.低功耗蓝牙与普通蓝牙的区别是什么?
见名知意,在功耗上前者更为优秀,并且易维护,非常适合对能耗敏感的场景。相比之下,普通蓝牙的功耗较高。一旦激活,它就会始终保持连接,比较耗能。因此,它主要应用在对功耗要求不高的设备上,如无线耳机、音箱、游戏手柄等。

在广播信道上,前者为3,这有助于减少网络干扰并降低功耗。后者为32,虽然提供了更多的选择,但相应地也增加了功耗和复杂性。

数据传输速率和包长度,前者都更短些。

3.低功耗蓝牙相关专有名词解释:

配置文件 (Profile)
Profile 是被蓝牙标准预先定义的一些 Service 的集合,并不真实存在于蓝牙设备中。如果蓝牙设备之间要相互兼容,它们只要支持相同的 Profile 即可。一个蓝牙设备可以支持多个 Profile。

服务
Service 是蓝牙设备对外提供的服务,一个设备可以提供多个服务,比如电量信息服务、系统信息服务等。每个服务由一个 UUID 唯一标识。

特征
每个 Service 包含 0 至多个 Characteristic。比如,电量信息服务就会有个 Characteristic 表示电量数据。Characteristic 包含一个值 (value)和 0 至多个描述符 (Descriptor) 组成。在与蓝牙设备通信时,主要就是通过读写 Characteristic 的 value 完成。 每个 Characteristic 由一个 UUID 唯一标识。

描述符
Descriptor 是描述特征值的已定义属性。例如,Descriptor 可指定人类可读的描述、特征值的取值范围或特定于特征值的度量单位。每个 Descriptor 由一个 UUID 唯一标识。

GATT-Generic Attribute Profile
GATT 配置文件是关于通过 BLE 链路发送和接收一小段数据(称为“属性”)的一般规范。当前的所有 BLE 应用配置文件都基于 GATT。

Bluetooth SIG 为 BLE 设备定义了许多配置文件。配置文件是规定设备如何在特定应用中工作的规范。请注意,一个设备可以实现多个配置文件。例如,设备可能包含心率监测器和电池电量检测器。

GATT 是基于属性协议 (ATT) 构建的。这也称为 GATT/ATT。ATT 经过优化,可在 BLE 设备上运行。为此,它会尽可能减少使用的字节数量。每个属性均由通用唯一标识符 (UUID) 进行唯一标识。UUID 是一种标准化的 128 位格式,用于对信息进行唯一标识的字符串 ID。由 ATT 传输的特性会采用“特征”和服务的格式。

中央设备Central和外围设备Peripheral
这是低功耗蓝牙中十分重要的概念。通过低功耗蓝牙链接的两个设备,一个为中央设备(获取信息的信息使用方),一个为外围设备(产出信息的信息提供方)。例如手机和电子温度计通过蓝牙链接,前者就是中央设备,后者就是外围设备。

中央设备Central
中心设备可以扫描外围设备,并在发现有外围设备存在后与之建立连接,之后就可以使用外围设备提供的服务(Service)。一般而言,手机会担任中心设备的角色,利用外围设备提供的数据进行处理或展示等等。小程序提供低功耗蓝牙接口是默认设定手机为中心设备的。

外围设备Peripheral
外围设备一直处于广播状态,等待被中心设备搜索和连接,不能主动发起搜索。例如智能手环、传感器等设备。如果外围设备广播时被设置为不可连接的状态,也被称为广播模式 (Broadcaster),常见的例子是蓝牙信标 (Beacon) 设备。

三、BLE低功耗蓝牙的使用流程:

在这里插入图片描述
该流程分为两个部分,中央设备的角度和外围设备的角度。

首先作为中央设备,需要:
1.初始化蓝牙模块
2.扫描并发现蓝牙外围设备
3.连接设备
4.获取蓝牙外围设备的服务
5. 读写服务的特征值
6. 断开连接和关闭蓝牙适配器

之后作为外围设别,需要:
1.初始化蓝牙模块
2.添加服务,写入特征值和描述
3.发送广播,设置广播各种参数

DEMO示例:

中央设备操作步骤函数:

import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';const TAG: string = 'GattClientManager';export class GattClientManager {device: string = undefined;gattClient: ble.GattClientDevice = undefined;connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indicationmySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';found: boolean = false;// 构造BLEDescriptorprivate initDescriptor(des: string, value: ArrayBuffer): ble.BLEDescriptor {let descriptor: ble.BLEDescriptor = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,descriptorUuid: des,descriptorValue: value};return descriptor;}// 构造BLECharacteristicprivate initCharacteristic(): ble.BLECharacteristic {let descriptors: Array<ble.BLEDescriptor> = [];let descBuffer = new ArrayBuffer(2);let descValue = new Uint8Array(descBuffer);descValue[0] = 11;descValue[1] = 12;descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);let charBuffer = new ArrayBuffer(2);let charValue = new Uint8Array(charBuffer);charValue[0] = 1;charValue[1] = 2;let characteristic: ble.BLECharacteristic = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,characteristicValue: charBuffer,descriptors: descriptors};return characteristic;}private logCharacteristic(char: ble.BLECharacteristic) {let message = 'logCharacteristic uuid:' + char.characteristicUuid + '\n';let value = new Uint8Array(char.characteristicValue);message += 'logCharacteristic value: ';for (let i = 0; i < char.characteristicValue.byteLength; i++) {message += value[i] + ' ';}console.info(TAG, message);}private logDescriptor(des: ble.BLEDescriptor) {let message = 'logDescriptor uuid:' + des.descriptorUuid + '\n';let value = new Uint8Array(des.descriptorValue);message += 'logDescriptor value: ';for (let i = 0; i < des.descriptorValue.byteLength; i++) {message += value[i] + ' ';}console.info(TAG, message);}private checkService(services: Array<ble.GattService>): boolean {for (let i = 0; i < services.length; i++) {if (services[i].serviceUuid != this.myServiceUuid) {continue;}for (let j = 0; j < services[i].characteristics.length; j++) {if (services[i].characteristics[j].characteristicUuid != this.myCharacteristicUuid) {continue;}for (let k = 0; k < services[i].characteristics[j].descriptors.length; k++) {if (services[i].characteristics[j].descriptors[k].descriptorUuid == this.myFirstDescriptorUuid) {console.info(TAG, 'find expected service from server');return true;}}}}console.error(TAG, 'no expected service from server');return false;}// 1. 订阅连接状态变化事件public onGattClientStateChange() {if (!this.gattClient) {console.error(TAG, 'no gattClient');return;}try {this.gattClient.on('BLEConnectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {let state = '';switch (stateInfo.state) {case 0:state = 'DISCONNECTED';break;case 1:state = 'CONNECTING';break;case 2:state = 'CONNECTED';break;case 3:state = 'DISCONNECTING';break;default:state = 'undefined';break;}console.info(TAG, 'onGattClientStateChange: device=' + stateInfo.deviceId + ', state=' + state);if (stateInfo.deviceId == this.device) {this.connectState = stateInfo.state;}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 2. client端主动连接时调用public startConnect(peerDevice: string) { // 对端设备一般通过ble scan获取到if (this.connectState != constant.ProfileConnectionState.STATE_DISCONNECTED) {console.error(TAG, 'startConnect failed');return;}console.info(TAG, 'startConnect ' + peerDevice);this.device = peerDevice;// 2.1 使用device构造gattClient,后续的交互都需要使用该实例this.gattClient = ble.createGattClientDevice(peerDevice);try {this.onGattClientStateChange(); // 2.2 订阅连接状态this.gattClient.connect(); // 2.3 发起连接} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 3. client端连接成功后,需要进行服务发现public discoverServices() {if (!this.gattClient) {console.info(TAG, 'no gattClient');return;}console.info(TAG, 'discoverServices');try {this.gattClient.getServices().then((result: Array<ble.GattService>) => {console.info(TAG, 'getServices success: ' + JSON.stringify(result));this.found = this.checkService(result); // 要确保server端的服务内容有业务所需要的服务});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 4. 在确保拿到了server端的服务结果后,读取server端特定服务的特征值时调用public readCharacteristicValue() {if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {console.error(TAG, 'no gattClient or not connected');return;}if (!this.found) { // 要确保server端有对应的characteristicconsole.error(TAG, 'no characteristic from server');return;}let characteristic = this.initCharacteristic();console.info(TAG, 'readCharacteristicValue');try {this.gattClient.readCharacteristicValue(characteristic).then((outData: ble.BLECharacteristic) => {this.logCharacteristic(outData);})} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 5. 在确保拿到了server端的服务结果后,写入server端特定服务的特征值时调用public writeCharacteristicValue() {if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {console.error(TAG, 'no gattClient or not connected');return;}if (!this.found) { // 要确保server端有对应的characteristicconsole.error(TAG, 'no characteristic from server');return;}let characteristic = this.initCharacteristic();console.info(TAG, 'writeCharacteristicValue');try {this.gattClient.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, (err) => {if (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);return;}console.info(TAG, 'writeCharacteristicValue success');});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 6. 在确保拿到了server端的服务结果后,读取server端特定服务的描述符时调用public readDescriptorValue() {if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {console.error(TAG, 'no gattClient or not connected');return;}if (!this.found) { // 要确保server端有对应的descriptorconsole.error(TAG, 'no descriptor from server');return;}let descBuffer = new ArrayBuffer(0);let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);console.info(TAG, 'readDescriptorValue');try {this.gattClient.readDescriptorValue(descriptor).then((outData: ble.BLEDescriptor) => {this.logDescriptor(outData);});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 7. 在确保拿到了server端的服务结果后,写入server端特定服务的描述符时调用public writeDescriptorValue() {if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {console.error(TAG, 'no gattClient or not connected');return;}if (!this.found) { // 要确保server端有对应的descriptorconsole.error(TAG, 'no descriptor from server');return;}let descBuffer = new ArrayBuffer(2);let descValue = new Uint8Array(descBuffer);descValue[0] = 11;descValue[1] = 12;let descriptor = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);console.info(TAG, 'writeDescriptorValue');try {this.gattClient.writeDescriptorValue(descriptor, (err) => {if (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);return;}console.info(TAG, 'writeDescriptorValue success');});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 8.client端主动断开时调用public stopConnect() {if (!this.gattClient || this.connectState != constant.ProfileConnectionState.STATE_CONNECTED) {console.error(TAG, 'no gattClient or not connected');return;}console.info(TAG, 'stopConnect ' + this.device);try {this.gattClient.disconnect(); // 8.1 断开连接this.gattClient.off('BLEConnectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {});this.gattClient.close() // 8.2 如果不再使用此gattClient,则需要close} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}let gattClientManager = new GattClientManager();
export default gattClientManager as GattClientManager;

外围设备操作步骤函数:

import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';const TAG: string = 'GattServerManager';export class GattServerManager {gattServer: ble.GattServer = undefined;connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indicationmySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';// 构造BLEDescriptorprivate initDescriptor(des: string, value: ArrayBuffer): ble.BLEDescriptor {let descriptor: ble.BLEDescriptor = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,descriptorUuid: des,descriptorValue: value};return descriptor;}// 构造BLECharacteristicprivate initCharacteristic(): ble.BLECharacteristic {let descriptors: Array<ble.BLEDescriptor> = [];let descBuffer = new ArrayBuffer(2);let descValue = new Uint8Array(descBuffer);descValue[0] = 31;descValue[1] = 32;descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);let charBuffer = new ArrayBuffer(2);let charValue = new Uint8Array(charBuffer);charValue[0] = 21;charValue[1] = 22;let characteristic: ble.BLECharacteristic = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,characteristicValue: charBuffer,descriptors: descriptors};return characteristic;}// 1. 订阅连接状态变化事件public onGattServerStateChange() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}try {this.gattServer.on('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {let state = '';switch (stateInfo.state) {case 0:state = 'DISCONNECTED';break;case 1:state = 'CONNECTING';break;case 2:state = 'CONNECTED';break;case 3:state = 'DISCONNECTING';break;default:state = 'undefined';break;}console.info(TAG, 'onGattServerStateChange: device=' + stateInfo.deviceId + ', state=' + state);});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 2. server端注册服务时调用public registerServer() {let characteristics: Array<ble.BLECharacteristic> = [];let characteristic = this.initCharacteristic();characteristics.push(characteristic);let gattService: ble.GattService = {serviceUuid: this.myServiceUuid,isPrimary: true,characteristics: characteristics};console.info(TAG, 'registerServer ' + this.myServiceUuid);try {this.gattServer = ble.createGattServer(); // 2.1 构造gattServer,后续的交互都需要使用该实例this.onGattServerStateChange(); // 2.2 订阅连接状态this.gattServer.addService(gattService);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 3. 订阅来自gattClient的读取特征值请求时调用public onCharacteristicRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicRead');try {this.gattServer.on('characteristicRead', (charReq: ble.CharacteristicReadRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 21;rspValue[1] = 22;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 4. 订阅来自gattClient的写入特征值请求时调用public onCharacteristicWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicWrite');try {this.gattServer.on('characteristicWrite', (charReq: ble.CharacteristicWriteRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicWrite: needRsp=' + charReq.needRsp);if (!charReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 5. 订阅来自gattClient的读取描述符请求时调用public onDescriptorRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorRead');try {this.gattServer.on('descriptorRead', (desReq: ble.DescriptorReadRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 31;rspValue[1] = 32;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 6. 订阅来自gattClient的写入描述符请求时调用public onDescriptorWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorWrite');try {this.gattServer.on('descriptorWrite', (desReq: ble.DescriptorWriteRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorWrite: needRsp=' + desReq.needRsp);if (!desReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 7. server端删除服务,不再使用时调用public unRegisterServer() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'unRegisterServer ' + this.myServiceUuid);try {this.gattServer.removeService(this.myServiceUuid); // 7.1 删除服务this.gattServer.off('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => { // 7.2 取消订阅连接状态});this.gattServer.close() // 7.3 如果不再使用此gattServer,则需要close} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}let gattServerManager = new GattServerManager();
export default gattServerManager as GattServerManager;

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

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

相关文章

Golang的多版本管理

Golang的多版本管理 一、 为什么需要多版本管理&#xff1f; 现代软件开发中&#xff0c;随着项目日益复杂&#xff0c;往往需要使用不同的Golang版本来适配不同的依赖库或者框架。同时&#xff0c;不同的项目也可能需要不同的Golang版本来编译和执行。因此&#xff0c;多版本管…

解决milvus migration 迁移数据到出现数据丢失问题

在迁移数据的时候发现数据丢失 问题是数据在批量迁移的过程中&#xff0c;这个错误会被忽略掉 分析下来是因为buuferSize 设置的是500条数据&#xff0c;但是迁移工具对一次迁移的数据是是有大小限制的&#xff0c;如果500条数据的总大小大于4194304&#xff0c;就会导致数据…

Nop平台与SpringCloud的功能对比

Nop平台是根据可逆计算原理从零开始设计并实现的新一代的低代码平台&#xff0c;它的目标并不是针对少数固化的场景提供预置的开发脚手架和可视化设计工具&#xff0c; 而是打破描述式编程和传统命令式编程之间人为制造的藩篱&#xff0c;建立两者无缝相容的一种新的编程范式。…

基于SpringBoot云养鸡互动平台的设计与实现

前言 对于当今社会的人们来说&#xff0c;互联网技术是必不可少的&#xff0c;随着经济和技术的不断发展&#xff0c;计算机已经深入到各个领域。云养鸡互动平台将人们的时间需求与计算机技术结合起来&#xff0c;架起一座桥梁&#xff0c;使云养鸡互动更加方便快捷。云养鸡互…

使用Kubernetes管理容器化应用

使用Kubernetes管理容器化应用 Kubernetes简介 安装Kubernetes 安装Minikube 启动Minikube集群 创建一个简单的Web应用 创建项目目录 初始化项目 安装Node.js依赖 创建Docker镜像 编写Dockerfile 构建并推送Docker镜像 创建Kubernetes配置文件 创建Deployment 创建Service …

使用飞桨AI Studio平台训练数据,并进行图像识别分析得牡丹花测试

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

自适应神经网络架构:原理解析与代码示例

个人主页&#xff1a;chian-ocean 文章专栏 自适应神经网络结构&#xff1a;深入探讨与代码实现 1. 引言 随着深度学习的不断发展&#xff0c;传统神经网络模型在处理复杂任务时的局限性逐渐显现。固定的网络结构和参数对于动态变化的环境和多样化的数据往往难以适应&#…

Python小白学习教程从入门到入坑------第十八课 异常模块与包【上】(语法基础)

一、异常 在Python中&#xff0c;异常&#xff08;Exception&#xff09;是一种用于处理在程序运行时可能发生的错误情况的机制 异常允许程序在检测到错误时不是简单地崩溃&#xff0c;而是能够优雅地处理这些错误&#xff0c;可能包括记录错误信息、清理资源、或者向用户提…

A4-C四驱高防变电站巡检机器人

在电力行业数字化、智能化转型进程中&#xff0c;搭载多模态成像传感器的变电站巡检机器人、视频监控设备逐渐取代传统人工&#xff0c;成为变电设备状态监测的主要工具。变电站巡检机器人具有全天候、非接触式、多参量测量等特点&#xff0c;结合内置人工智能算法完成仪表识别…

MATLAB锂电概率分布模型

&#x1f3af;要点 概率分布等效电路模型结合了路径相关速率能力及状态估计中滞后效应。纠正了充电状态中时间误差累积及避免开路电压中电压滞后现象。使用电流方向和电池容量相关函数描述开路电压&#xff0c;并使用微分方程描述电压滞后现象。模型结构基于一级相变的材料机制…

QT界面开发--我的第一个windows窗体【菜单栏、工具栏、状态栏、铆接部件、文本编辑器、按钮、主界面】

经过前面的铺垫&#xff0c;今天我们就开始我们图形化界面之旅了&#xff0c;我们的第一个窗体主要包括&#xff1a;菜单栏、状态栏、工具栏、铆接部件、还有Qt提供的一些主窗体的API。 第一部分&#xff1a;主界面(QMainWindow) 当创建好项目后&#xff0c;我们直接运行&…

Unity中的动画状态机(详解)

动画状态机的定义 Unity中的动画状态机&#xff08;Animator Controller&#xff09;是用于定义和管理角色或对象动画状态之间转换的工具。它允许动画师和开发者设计复杂的动画逻辑&#xff1b; 例如角色的行走、跑步、跳跃、攻击等动作&#xff0c;以及其他动作之间的平滑过渡…

Vue笔记-element ui中关于table的前端分页

对于 Element UI 表格的前端分页&#xff0c;可以在组件中使用 JavaScript 来实现数据的分页显示&#xff0c;而不必从后端获取已分页的数据。以下是一个简单的示例&#xff0c;演示如何在前端进行 Element UI 表格的分页&#xff1a; <template><div><el-tabl…

ShellCode 格式化代码注入工具

一款基于C/C开发的应用层汇编代码注入工具&#xff0c;可实现向特定进程内注入动态链接库模块或注入ShellCode汇编指令集&#xff0c;还可以实现第三方进程的汇编级Call调用&#xff0c;通常被用于协助渗透人员完成内存注入&#xff0c;同时也可用于对特定ShellCode汇编代码进行…

Ubuntu系统安装软件

在Linux系统中有四种软件安装方式&#xff1a;rpm、yum、apt、编译安装 编译安装 编译安装只有一个源码包&#xff0c;源码包是由一大堆源代码程序组成的&#xff0c;是由程序员按照特定格式和语法编写好了&#xff0c;现成的安装包 程序&#xff1a;未执行的代码 进程&#…

雷池社区版compose配置文件解析-mgt

在现代网络安全中&#xff0c;选择合适的 Web 应用防火墙至关重要。雷池&#xff08;SafeLine&#xff09;社区版免费切好用。为网站提供全面的保护&#xff0c;帮助网站抵御各种网络攻击。 compose.yml 文件是 Docker Compose 的核心文件&#xff0c;用于定义和管理多个 Dock…

自动驾驶-传感器简述

自动驾驶车辆上的传感器类型包含激光雷达、毫米波雷达、相机、imu、rtk、超声波雷达等&#xff0c;这些传感器用来接收外部世界多姿多彩的信号&#xff0c;根据接收到的信号&#xff0c;车载大脑对信号进行处理&#xff0c;那信号的准确程度就尤为重要。 本文将各个传感器的特性…

Lucas带你手撕机器学习——岭回归

岭回归&#xff08;Ridge Regression&#xff09; 一、背景与引入 在进行线性回归分析时&#xff0c;我们常常面临多重共线性的问题。多重共线性指的是自变量之间高度相关&#xff0c;这会导致回归系数的不稳定性&#xff0c;使得模型的预测能力降低。传统的线性回归通过最小…

模块化主动隔振系统市场规模:2023年全球市场规模大约为220.54百万美元

模块化主动隔振系统是一种用于精密设备和实验装置的隔振解决方案&#xff0c;通过主动控制技术消除振动干扰&#xff0c;提供稳定的环境。目前&#xff0c;随着微纳制造和精密测量技术的发展&#xff0c;对隔振系统的要求越来越高。模块化设计使得系统能够灵活适应不同负载和工…

STM32 第3章 如何用串口下载程序

时间:2024.10.28 一、学习内容 1、安装USB转串口驱动 1.1串口下载连接示意图 1、USB转串口模块在开发板上是一个独立的模块,可通过调帽与其他串口连接,USART1/2/3/4/5 2、只有USART1才具有串口下载的功能。 3、CH340是电平转换芯片,将电脑端输出的USB电平和单片机输…