ReactNative蓝牙扫描监听写入特征值示例代码
页面代码
js
import { View, StyleSheet, Text, PermissionsAndroid, Platform, Dimensions, TouchableOpacity } from 'react-native';
import theme from '../../config/theme.json';
import { BleManager } from 'react-native-ble-plx';
import { getBLEDeviceServices, getHistoryData, setUtc } from '../../utils/BLEConnect';
const manager = new BleManager();
const { width, height } = Dimensions.get('window');
const getWidth = (e) => {
return width * e / 2000 // 设备的宽度大小 * 原型图的元素宽度 / 原型图固定宽度
}
const getHeight = (e) => {
return height * e / 1000 // 设备的宽度大小 * 原型图的元素宽度 / 原型图固定宽度
}
export const SyncData = () => {
// 开始同步
const startSync = async () => {
let permissions;
if (Platform.OS === 'android') {
if (Platform.Version >= 31) { // Android 12 及以上
permissions = [
'android.permission.BLUETOOTH_SCAN',
'android.permission.BLUETOOTH_CONNECT',
'android.permission.BLUETOOTH_ADVERTISE'
];
} else { // Android 12 以下
permissions = ['android.permission.ACCESS_FINE_LOCATION'];
}
}
// 检查权限
for (const permission of permissions) {
const check = await PermissionsAndroid.check(permission);
console.log(`permission ${permission} check ${check}`);
if (!check) {
await PermissionsAndroid.request(permission);
}
}
// 开始扫描
manager.startDeviceScan(null, null, async (error, device) => {
if (error) {
// 扫描出错,一般是权限问题
console.error(error);
// 停止扫描
manager.stopDeviceScan()
console.log('蓝牙扫描出错了~');
return;
}
// 扫如果点开了同步,代表要去同步设备,否则就是扫描信号
if (device.name == null) return
console.log('device.name', device.name);
// 描到设备
if (device.name == 'Take-73576717') {
// 连接设备,并获取是否连接成功,拿到连接成功后返回的信息
getBLEDeviceServices(device.id, manager, (name, id) => {
console.log('当前连接的蓝牙名称和id值为:', name, id);
})
} else {
// 全部同步完成
}
});
}
return (
<View
style={styles.oncontainer}>
<TouchableOpacity style={styles.kstb} activeOpacity={1} onPress={startSync}>
<Text style={styles.kstbText}>开始扫描并连接连接后断开</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.kstb} activeOpacity={1} onPress={() => {
getHistoryData()
}}>
<Text style={styles.kstbText}>获取历史数据</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.kstb} activeOpacity={1} onPress={() => {
setUtc()
}}>
<Text style={styles.kstbText}>设置UTC</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
oncontainer: {
flex: 1,
backgroundColor: theme.generalBackground,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
},
kstb: {
paddingLeft: getWidth(10),
paddingRight:getWidth(10),
height: getHeight(56),
backgroundColor: '#EF6234',
borderRadius: getWidth(8),
justifyContent: 'center',
alignItems: 'center',
marginRight: getWidth(10),
},
kstbText: {
fontSize: getWidth(24),
color: '#ffffff',
fontWeight: '600',
lineHeight: getHeight(56),
}
})工具包js代码
js
import { Buffer } from 'buffer';
// 公共协议UUID
var UUID_COMMON_POWER = "00002a19-0000-1000-8000-00805f9b34fb"; //电量
var UUID_COMMON_FIRMVERSION = "00002a26-0000-1000-8000-00805f9b34fb"; //固件版本
var UUID_COMMON_HARDVERSION = "00002a27-0000-1000-8000-00805f9b34fb"; //硬件版本
var UUID_COMMON_SOFTVERSION = "00002a28-0000-1000-8000-00805f9b34fb"; //软件版本
var UUID_COMMON_WRITE = "0000fd1a-0000-1000-8000-00805f9b34fb"; //写命令的特征值id,现阶段发送指令都用这一个
var UUID_COMMON_NOTIFY = "0000fd19-0000-1000-8000-00805f9b34fb"; //公共收命令特征值
var UUID_COMMON_SERVICEUUID = "0000FD00-0000-1000-8000-00805F9B34FB"; //公共服务UUID
//私有协议,例如 设置心率振动阈值
var UUID_COMMON_WRITE0A = "0000fd0a-0000-1000-8000-00805f9b34fb";
//未用到
var UUID_COMMON_NOTIFY09 = "0000fd09-0000-1000-8000-00805f9b34fb"; //公共通知09
var UUID_HEARTRATE_NOTIFY = "00002a37-0000-1000-8000-00805f9b34fb"; //心率数据
let headArray = [];
/** 解析训练时间与卡路里 */
var caloriesArray = [];
/** 解析心率历史数据 */
var heartRateArray = [];
/** 解析步数的历史数据 */
var stepArray = [];
var step15Array = [];
// 协议版本号
var protocolVersion = 1;
let device = {
deviceId: null,
serviceId: "0000FD00-0000-1000-8000-00805F9B34FB",
name: null,
devices: null,
services: null,
connected: false,
stopDiscovery: true, //停止扫码状态,默认停止
sending: false, //正在发送数据
headerNumber: 0,//指令序号,从0开始,断开置为0
};
/** 蓝牙监听 */
//获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备
export const getBLEDeviceServices = async (deviceId, manager, callback) => {
manager.stopDeviceScan() // 扫描到设备就停止扫描
const devices = await manager.connectToDevice(deviceId); // 连接设备
callback(devices.name, devices.id) // 将当前设备名和id回调出去给上一个页面使用
device.deviceId = devices.id; // 给全局device赋值
device.name = devices.name;
device.devices = devices;
const services = await devices.discoverAllServicesAndCharacteristics(); // 获取所有的服务和特征
const aaa = await services.services(); // 仅获取所有的服务
const arr = aaa.filter(item => item.uuid == UUID_COMMON_SERVICEUUID.toLowerCase()); // 过滤一下,找到当前需要的自定义的服务
device.services = arr[0]; // 因为就一个自定义服务,将服务的数组的0号元素赋值给全局services
// 因为前面加了过滤才是只有一个,正常来说服务是有多个,需要循环一下
for (let i = 0; i < arr.length; i++) {
// 判断是否是主服务
if (arr[i].isPrimary) {
getBLEDeviceCharacteristics(devices, arr[i], manager); // 获取当前服务的特征
}
}
}
const getBLEDeviceCharacteristics = async (device, service, manager) => {
try {
// 获取服务下的特征值
const serviceitem = await service.characteristics(); // 获取到当前服务下的所有特征
// 循环特征
for (const item of serviceitem) {
try {
if (item.isReadable) { // 判断当前特征是否支持读(里面逻辑不用看,这里我是打印下特征值做下测试使用)
const serviceitem2 = await item.read();
const newValue = serviceitem2.value;
console.log('newValue', newValue); // 获取当前服务的特征值
}
//启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。注意:必须设备的特征值支持 isNotifiable 或者 isIndicatable 才可以成功调用。
//另外,必须先启用 notifyBLECharacteristicValueChange 才能监听到设备 onBleCharacteristicValueChange 事件
// isNotifiable:是否允许通知
// isIndicatable:是否支持指示
if (item.isNotifiable || item.isIndicatable) {
// 操作之前先监听,保证第一时间获取数据, 如果是断开重连,则是已经创建过监听
// 开启特征值监听
onBLECharacteristicValueChange(device, service, item.uuid);
}
} catch (error) {
console.error('Read error:', error);
}
}
} catch (error) {
console.error('Error getting characteristics:', error);
}
}
// 监听特征值变化
const onBLECharacteristicValueChange = (device, service, characteristicId) => {
// 监听服务特征值
service.monitorCharacteristic(characteristicId, (error, characteristic) => {
if (error) {
console.error('Error monitoring characteristic:', error);
} else {
//解析数据(characteristic.value是base64类型的,将其转为buffer类型进行解析)
const buffer = base64ToArrayBuffer(characteristic.value);
// 数据处理
analysisBleData(service, characteristic.uuid, buffer, device);
}
})
}
//ble 数据解析value,返回 {type, data}
function analysisBleData(service, uuid, value, device) {
var bleByte = new Uint8Array(value);
if (uuid.toLowerCase() == UUID_COMMON_NOTIFY09) { //震动阈值设置
var sendsetShock = byte2hexStr(bleByte); //设置成功一般会返回:D2050901E1
console.log("[analysisBleData]心率阈值设置、获取、关闭回复结果 = " + sendsetShock);
var first = bleByte[0] & 0xFF;
var keyMark = bleByte[2] & 0xFF;
if (first == 210) {
if (keyMark == 9) { //设置
var setResultValue = bleByte[3] & 0xFF;
if (setResultValue == 1) { //设置成功
//回调函数
connectDateListener(11, 'success');
} else {
connectDateListener(11, 'failure');
}
} else if (keyMark == 10) { //获取
var switchMark = bleByte[3] & 0xFF;
var shockNumber = bleByte[4] & 0xFF;
var result = {
switchMark: switchMark,
shockNumber: shockNumber
};
//回调函数
connectDateListener(12, result);
} else if (keyMark == 11) { //关闭
var closeResultValue = bleByte[3] & 0xFF;
if (closeResultValue == 1) { //关闭成功
//回调函数
connectDateListener(13, 'success');
} else {
connectDateListener(13, 'failure');
}
}
}
return;
}
if (uuid.toLowerCase() == UUID_COMMON_POWER) { //电量数据
//回调函数
connectDateListener(0, bleByte[0] & 0xFF);
return;
}
if (uuid.toLowerCase() == UUID_COMMON_FIRMVERSION) { //固件版本
//回调函数
connectDateListener(8, hex2str(bleByte));
return;
}
if (uuid.toLowerCase() == UUID_COMMON_HARDVERSION) { //硬件版本
//回调函数
connectDateListener(6, hex2str(bleByte));
return;
}
if (uuid.toLowerCase() == UUID_COMMON_SOFTVERSION) { //软件版本
//回调函数
connectDateListener(7, hex2str(bleByte));
return;
}
if (uuid.toLowerCase() == UUID_HEARTRATE_NOTIFY) { //心率数据
var result = getHeartRateData(bleByte);
connectDateListener(9, result);
return;
}
if (!checkByte(bleByte)) { //接收数据无效
console.log("无效数据")
return;
}
var byte1 = bleByte[1] & 0xFF;
if (byte1 == 32 && bleByte.length >= 5) {
var byte3 = bleByte[3] & 0xFF;
var byte4 = bleByte[4] & 0xFF;
var headerNumber = (byte3 << 8) + byte4;
connectDateListener(10, headerNumber);
//处理回复结果回调
return;
}
console.log('第二个转换', JSON.stringify(new Uint8Array(value)));
var valueByte = getCompleteValueByte(value); //获取完整数据
console.log('第三个转换', JSON.stringify(new Uint8Array(valueByte)));
if (valueByte == "") {
console.log("数据接收未结束");
return; //非完整数据,不处理
}
dowithValueByte(service,valueByte, device);
return;
//}
}
/**
* 获取bit字节
*/
function getIntBit(dataNumber, startBit, endBit) {
if (startBit < 0 || startBit > 15) {
return 0;
}
if (endBit < 0 || endBit > 15) {
return 0;
}
if (startBit > endBit) {
return 0;
}
return (dataNumber & (0xFFFF >> (15 - endBit))) >> startBit;
}
/** 校验数据 */
function checkByte(bleByte) {
if (!bleByte || bleByte.length < 5) {
return false;
}
var lastNumber = bleByte[bleByte.length - 1] & 0xFF;
var checkNumber = 0;
for (var i = 0; i < bleByte.length - 1; i++) {
checkNumber = checkNumber + bleByte[i] & 0xFF;
}
if (lastNumber != checkNumber % 256) {
return false;
}
return true;
}
/**
* 解析心率数据
*/
function getHeartRateData(bleByte) {
var length = bleByte[0] & 0xFF;
var heartRate = bleByte[1] & 0xFF;
var intervalArray = [];
if (bleByte.length > 2 && bleByte.length % 2 == 0) {
for (var i = 2; i < bleByte.length; i++) {
if (i % 2 == 0) {
var lowByte = bleByte[i] & 0xFF;
var hiByte = bleByte[i + 1] & 0xFF;
var interval = (hiByte << 8) + lowByte;
intervalArray.push(interval);
}
}
}
var result = {
heartRate: heartRate,
dataLength: length,
interval: intervalArray
};
return result;
}
/** bufferArray hex -> str */
function hex2str(hex) {
var hexStr = "";
for (var i = 0; i < hex.length; i++) {
hexStr += String.fromCharCode(hex[i]);
}
return hexStr;
}
/**
* 获取完整的命令值
*/
function getCompleteValueByte(value) {
var bleByte = new Uint8Array(value);
var byte1 = bleByte[1] & 0xFF;
var isMore = getIntBit(byte1, 7, 7);
var isEnd = getIntBit(byte1, 6, 6);
var bagNo = getIntBit(byte1, 0, 3);
//获取要处理的数据,最后一位checkSum,第一位固定值,第二位标识符,第三位包长度,都去掉
var valueByte = new ArrayBuffer(bleByte.length - 4);
var valuebyteView = new Uint8Array(valueByte);
//从第三位开始截取
for (var i = 3; i < bleByte.length - 1; i++) {
valuebyteView[i - 3] = bleByte[i];
}
if (isMore == 0) { //单个包数据,直接返回
return valueByte;
}
//多包发送
if (isEnd == 0 && bagNo == 0) {
byteArray = [];
byteArray.push(valueByte);
return ""; //多包没有处理完成,返回空
}
if (isEnd == 0 && bagNo != 0) {
byteArray.push(valueByte);
return ""; //多包没有处理完成,返回空
}
//最后一个包,处理数据
byteArray.push(valueByte);
var valueLong = 0;
for (var i = 0; i < byteArray.length; i++) {
valueLong = valueLong + byteArray[i].byteLength;
}
var resultRow = 0;
var resultByte = new ArrayBuffer(valueLong);
var resultByteView = new Uint8Array(resultByte);
for (var i = 0; i < byteArray.length; i++) {
var listByte = byteArray[i];
var listByteView = new Uint8Array(listByte);
for (var j = 0; j < listByteView.length; j++) {
resultByteView[resultRow] = listByteView[j];
resultRow = resultRow + 1;
}
}
if (byteArray.length != bagNo + 1) {
return ""; //丢包了,返回空
}
byteArray = []; //变量初始化为空
return resultByte;
}
/**
* int 转byte
*/
function int2byte(num) {
return num & 0xFF; // 确保返回值是一个 8 位的无符号字节
}
/** 回复命令 **/
function getAckCmdValue(sortNumber) {
var checkNumber = 255 + 32 + 6 + (sortNumber / 256) + (sortNumber % 256);
var cmdByte = new ArrayBuffer(6);
var bufView = new Uint8Array(cmdByte);
bufView[0] = int2byte(255);
bufView[1] = int2byte(32);
bufView[2] = int2byte(6);
bufView[3] = int2byte(sortNumber / 256);
bufView[4] = int2byte(sortNumber % 256);
bufView[5] = int2byte(checkNumber % 256);
return cmdByte;
}
// base64 转 ArrayBuffer
function base64ToArrayBuffer(base64) {
// 解码Base64字符串
// 解码 Base64 字符串
const buffer = Buffer.from(base64, 'base64');
// 将 Buffer 转换为 ArrayBuffer
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
// 返回ArrayBuffer
return arrayBuffer;
}
// 将 ArrayBuffer 转换为 Base64 字符串
function arrayBufferToBase64(buffer) {
return Buffer.from(buffer).toString('base64');
}
/** 处理接收的数据 */
function dowithValueByte(service,value, device) {
var valueByte = new Uint8Array(value);
var byte0 = valueByte[0] & 0xFF;
var byte1 = valueByte[1] & 0xFF;
console.log('byte0', JSON.stringify(byte0));
console.log('byte1', JSON.stringify(byte1));
var sortNum = (byte0 << 8) + byte1;
console.log('sortNum', JSON.stringify(sortNum));
var cmdId = valueByte[2] & 0xFF; //命令
var keyMark = valueByte[4] & 0xFF;
var valueLong = valueByte[6] & 0xFF;
var byteNum = 7;
console.log("sortNum=", sortNum);
var ackBuffer = getAckCmdValue(sortNum); //获取ack命令值
var ackStr = arrayBufferToBase64(ackBuffer);
console.log('ackStr', ackStr);
sendAckCmd(service, ackStr, UUID_COMMON_WRITE.toUpperCase(), device); //发送指令
if (headArray.indexOf(sortNum) >= 0) {
console.log("已处理过的数据");
return;
}
headArray.push(sortNum);
if (headArray.length > 20) {
headArray.splice(5);
}
for (var i = 0; i < 32; i++) {
var resultByteBuffer = new ArrayBuffer(valueLong + 1);
var resultByte = new Uint8Array(resultByteBuffer);
resultByte[0] = int2byte(keyMark);
for (var j = byteNum; j < valueLong + byteNum; j++) {
resultByte[1 + j - byteNum] = valueByte[j];
}
var result;
console.log('cmdId', cmdId);
switch (cmdId) {
case 1: {
console.log('实时数据');
result = realtimeData(resultByte);
break;
}
case 3: {
console.log('自定义数据上传');
result = customData(resultByte);
break;
}
case 4: {
analyticalRecord(resultByte);
break;
}
case 5: { //版本号
if (keyMark == 1 && resultByte.length == 2) {
result = resultByte[1] & 0xFF;
}
if (keyMark == 0) {
result = customData(resultByte);
}
break;
}
default: {
break;
}
}
//第一次读完三个字节,即已经处理完成,第二次不再继续
if (valueLong + byteNum == valueByte.byteLength) {
if (cmdId == 4) { //历史数据一次处理不完整
result = analyticalComplete();
}
console.log('cmdId, result', cmdId, result);
connectDateListener(cmdId, result);
//break;
}
keyMark = resultByte[byteNum + valueLong] & 0xFF;
valueLong = resultByte[byteNum + valueLong + 2] & 0xFF;
//ackType = resultByte[byteNum + valueLong + 1] & 0xFF;
byteNum = byteNum + valueLong + 3;
}
}
/** 蓝牙连接返回数据监听
* 1:实时数据,4:历史数据,5:版本号, 6硬件版本, 7软件版本,8固件版本,0电量
* 5:版本号, 6硬件版本, 7软件版本,8固件版本,0电量 连接上就自动读取, 10:设备接收指令的结果
* 11:心率震动阈值设置,12:心率震动阈值获取,13,心率震动阈值关闭
*/
const connectDateListener = (cmdKey, data) => {
if (parseInt(cmdKey) == 0) {
console.log("电量:" + data);
return;
}
if (parseInt(cmdKey) == 5) {
console.log("设备版本号:version=" + data);
return;
}
if (parseInt(cmdKey) == 6) {
console.log("硬件版本:" + data);
return;
}
if (parseInt(cmdKey) == 7) {
console.log("软件版本:" + data);
return;
}
if (parseInt(cmdKey) == 8) {
console.log("固件版本:" + data);
return;
}
if (parseInt(cmdKey) == 9) { //实时心率
console.log("心率数据:heartRate=" + data.heartRate + ",dataLength=" + data.dataLength + ",interval=" + data.interval.length);
return;
}
if (parseInt(cmdKey) == 10) { //设备接受指令结果
console.log("设备接收到指令回调=" + data);
return;
}
if (parseInt(cmdKey) == 11) { //心率震动阈值设置结果
//data:success 或 failure
console.log("震动阈值设置结果=" + data);
return;
}
if (parseInt(cmdKey) == 12) { //心率震动阈值获取结果
//switchMark : 是否开启震动 0-为开启 1-开启
//shockNumber : 震动阀值
console.log("震动阈值获取结果 shockNumber =" + data.shockNumber + ",switchMark = " + data.switchMark);
return;
}
if (parseInt(cmdKey) == 13) { //心率震动阈值关闭结果
//data:success 或 failure
console.log("震动阈值关闭结果=" + data);
return;
}
if (parseInt(cmdKey) == 1 || parseInt(cmdKey) == 2) { //步数,步频,卡路里
if ("step" == data.type) { //实时步数、卡路里
console.log("实时步数、卡路里: steps=" + data.steps + ",frequency=" + data.frequency + ",calorie=" + data.calorie);
} else if ("custom" == data.type) { //自定义数据
console.log("自定义数据: " + data.value);
}
return;
}
if (parseInt(cmdKey) == 3) { //步数,步频,卡路里
if ("custom" == data.type) { //自定义数据
console.log("自定义数据: " + data.value);
}
return;
}
console.log('cmdKey', cmdKey, data);
if (parseInt(cmdKey) == 4) {
if (data.type == 'step') {
console.log("步数历史数据:" + data.steps.length + "组");
for (let i = 0; i < data.steps.length; i++) {
console.log("步数历史第" + i + "组时间:" + data.steps[i].createTime);
}
} else if (data.type == 'rate') {
console.log("心率历史数据:" + data.heartRate.length + "组");
for (let i = 0; i < data.heartRate.length; i++) {
console.log("心率历史第" + i + "组时间:" + data.heartRate[i].createTime);
}
} else if (data.type == 'step15') {
console.log("15分钟步数历史数据:" + data.steps.length + "组");
for (let i = 0; i < data.steps.length; i++) {
console.log("15分钟步数历史第" + i + "组时间:" + data.steps[i].createTime);
}
} else if (data.type == 'calorie') {
console.log("训练卡路里数据:" + data.calories.length + "组");
}
return;
}
}
/** 解析实时数据 */
function realtimeData(byteData) {
if (byteData.length > 0) {
var keyMark = byteData[0] & 0xFF;
if (keyMark == 2 && byteData.length >= 5) { //步频
var stepHi = byteData[1] & 0xFF;
var stepMin = byteData[2] & 0xFF;
var stepLow = byteData[3] & 0xFF;
var steps = (stepHi << 16) + (stepMin << 8) + stepLow; //步数
var frequency = byteData[4] & 0xFF; //步频
//卡路里计算
var calorie = "0";
if (byteData.length >= 8) {
var calHi = byteData[5] & 0xFF;
var calpMin = byteData[6] & 0xFF;
var calLow = byteData[7] & 0xFF;
calorie = (calHi << 16) + (calpMin << 8) + calLow;
}
//处理数据
return {
type: "step",
steps: steps,
frequency: frequency,
calorie: calorie
};
}
}
}
/** 解析历史数据 */
function analyticalRecord(recordByte) {
var keyMark = recordByte[0] & 0xFF;
//var keyMark = getIntBit(byte0, 0, 3);
if (keyMark == 1) { //一分钟的步数数据
analysisStepRecordMark1(recordByte);
} else if (keyMark == 8) { //心率数据
analysisHeartRateRecord(recordByte);
} else if (keyMark == 9) { //15分钟的步数数据
analysisStepRecordMark9(recordByte);
} else if (keyMark == 12) { //训练时间及卡路里数据
analysisCaloriesRecord(recordByte);
}
}
function analysisStepRecordMark1(recordByte) {
var offSet = 0;
while (offSet < recordByte.length - 1) {
var dataLength = recordByte[offSet + 1] & 0xFF;
offSet = offSet + 1;
var utc1 = recordByte[offSet + 1] & 0xFF;
var utc2 = recordByte[offSet + 2] & 0xFF;
var utc3 = recordByte[offSet + 3] & 0xFF;
var utc4 = recordByte[offSet + 4] & 0xFF;
var myUtc = (utc1 << 24) + (utc2 << 16) + (utc3 << 8) + utc4 + new Date().getTimezoneOffset() * 60;
offSet = offSet + 4;
for (var i = 0; i < dataLength / 2; i++) {
var steps = recordByte[offSet + 1] & 0xFF;
var calories = recordByte[offSet + 2] & 0xFF;
offSet = offSet + 2;
var timeStamps = myUtc * 1000;
var date = new Date(timeStamps);
var timeString = formatTime(date);
var data = {
timeStamps: myUtc,
createTime: timeString,
steps: steps,
calories: calories
};
stepArray.push(data);
myUtc = myUtc + 60;
}
}
}
function analysisHeartRateRecord(recordByte) {
var offSet = 0;
while (offSet < recordByte.length - 1) {
var dataLength = recordByte[offSet + 1] & 0xFF;
offSet = offSet + 1;
var utc1 = recordByte[offSet + 1] & 0xFF;
var utc2 = recordByte[offSet + 2] & 0xFF;
var utc3 = recordByte[offSet + 3] & 0xFF;
var utc4 = recordByte[offSet + 4] & 0xFF;
var myUtc = (utc1 << 24) + (utc2 << 16) + (utc3 << 8) + utc4 + new Date().getTimezoneOffset() * 60;
offSet = offSet + 4;
for (var i = 0; i < dataLength; i++) {
var heartRateNum = recordByte[offSet + 1] & 0xFF;
offSet = offSet + 1;
var timeStamps = myUtc * 1000;
var date = new Date(timeStamps);
var timeString = formatTime(date);
var data = {
timeStamps: myUtc,
createTime: timeString,
heartRateNum: heartRateNum
};
heartRateArray.push(data);
myUtc = myUtc + 2;
}
}
}
function analysisCaloriesRecord() {
var offSet = 0;
while (offSet < recordByte.length - 1) {
var calorie1 = recordByte[offSet + 1] & 0xFF;
var calorie2 = recordByte[offSet + 2] & 0xFF;
var calorie3 = recordByte[offSet + 3] & 0xFF;
var calorie4 = recordByte[offSet + 4] & 0xFF;
//计算卡路里
var calorie = (calorie1 << 24) + (calorie2 << 16) + (calorie3 << 8) + calorie4;
offSet = offSet + 4;
var startUtc1 = recordByte[offSet + 1] & 0xFF;
var startUtc2 = recordByte[offSet + 2] & 0xFF;
var startUtc3 = recordByte[offSet + 3] & 0xFF;
var startUtc4 = recordByte[offSet + 4] & 0xFF;
var startUtc = (startUtc1 << 24) + (startUtc2 << 16) + (startUtc3 << 8) + startUtc4 + new Date().getTimezoneOffset() * 60;
offSet = offSet + 4;
var endUtc1 = recordByte[offSet + 1] & 0xFF;
var endUtc2 = recordByte[offSet + 2] & 0xFF;
var endUtc3 = recordByte[offSet + 3] & 0xFF;
var endUtc4 = recordByte[offSet + 4] & 0xFF;
var endUtc = (endUtc1 << 24) + (endUtc2 << 16) + (endUtc3 << 8) + endUtc4 + new Date().getTimezoneOffset() * 60;
offSet = offSet + 4;
var data = {
startUtc: startUtc,
endUtc: endUtc,
calorie: calorie
};
caloriesArray.push(data);
}
}
function analysisStepRecordMark9(recordByte) {
var offSet = 0;
while (offSet < recordByte.length - 1) {
var dataLength = recordByte[offSet + 1] & 0xFF;
offSet = offSet + 1;
var utc1 = recordByte[offSet + 1] & 0xFF;
var utc2 = recordByte[offSet + 2] & 0xFF;
var utc3 = recordByte[offSet + 3] & 0xFF;
var utc4 = recordByte[offSet + 4] & 0xFF;
var myUtc = (utc1 << 24) + (utc2 << 16) + (utc3 << 8) + utc4 + new Date().getTimezoneOffset() * 60;
offSet = offSet + 4;
var timeCount = recordByte[offSet + 1] & 0xFF;
for (var i = 0; i < dataLength / 2; i++) {
var stepHi = recordByte[offSet + 1] & 0xFF;
var stepLow = recordByte[offSet + 2] & 0xFF;
var steps = (stepHi << 8) + stepLow;
offSet = offSet + 2;
var timeStamps = myUtc * 1000;
var date = new Date(timeStamps);
var timeString = formatTime(date);
var spendCount = 15;
if (i == dataLength / 2 - 1) {
spendCount = timeCount;
}
var data = {
timeStamps: myUtc,
createTime: timeString,
steps: steps,
spendCount: spendCount
};
step15Array.push(data);
myUtc = myUtc + 900;
}
}
}
function formatTime(date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':');
}
function formatNumber(n) {
n = n.toString();
return n[1] ? n : '0' + n;
}
/** 解析自定义数据 */
function customData(byteData) {
var value = "";
if (byteData.length > 0) {
var byte0 = byteData[0] & 0xFF;
var keyMark = getIntBit(byte0, 0, 3);
if (keyMark == 254 && byteData.length >= 2) {
value = byteData[1] & 0xFF;
}
}
var result = {
type: "custom",
value: value
};
return result;
}
/** 发送ack回复*/
function sendAckCmd(service, cmdValue, cmdCharacteristicId, devices) {
try {
console.log('写入的数据为: ', cmdValue);
service.writeCharacteristicWithResponse(cmdCharacteristicId, cmdValue).then(res => {
console.log('write sucess');
device.sending = false;
}).catch(err => {
console.log('write fail', JSON.stringify(err));
device.sending = false;
});
} catch (error) {
console.log('sendAckCmd error', error);
device.sending = false;
}
}
/**
* 解析数据结束,返回结果
*/
function analyticalComplete() {
var result = {};
if (stepArray.length > 0) {
result.type = 'step';
result.steps = stepArray;
stepArray = [];
}
if (step15Array.length > 0) {
result.type = 'step15';
result.steps = step15Array;
step15Array = [];
}
if (heartRateArray.length > 0) {
result.type = 'rate';
result.heartRate = heartRateArray;
heartRateArray = [];
}
if (caloriesArray.length > 0) {
result.type = 'calorie';
result.calories = caloriesArray;
caloriesArray = [];
}
return result;
}
// 获取历史数据
export function getHistoryData() {
console.log('开始获取历史数据');
var headNum = sendTotalRecordCmd('history');
console.log('headNum', headNum);
}
/** 发送指令回调 */
function sendCmdCallBackListener(res) {
console.log("发送指令回调:", res);
}
/**
* 发送获取历史数据指令
*/
function sendTotalRecordCmd(type) {
var cmdBuffer = getHistoryRecordCmdValue();
var headNum = device.headerNumber;
var valueBuffer = packageSendCmd(headNum, cmdBuffer);
console.log('valueBuffer', JSON.stringify(new Uint8Array(valueBuffer)));
console.log('valueBuffer', valueBuffer.length);
sendBLECmdValue(valueBuffer, type);
return headNum;
}
//向蓝牙设备发送16进制数据,
//单通道,只能一条指令执行完成返回后,再进行下一条指令的发送
function sendBLECmdValue(sendCmd, type) {
if (device.sending) {
console.log('on sending');
return;
}
device.sending = true;
var cmdCharacteristicId = UUID_COMMON_WRITE.toUpperCase(); //写命令uuid
device.headerNumber += 1;
var sendIndex = 0;
//一次只能发送一条指令,上条指令未收到处理成功时,不进行下一条发送
console.log('sendCmd', sendCmd.length);
do {
var sendCmdValue = sendCmd[sendIndex];
console.log('sendCmdValue', sendCmdValue);
var delay = 1000;
if (sendIndex == 0) {
delay = 0;
}
//多包发送设置延迟发送
setTimeout(function () {
var ackStr = arrayBufferToBase64(sendCmdValue);
console.log('ackStr', ackStr);
wwriteBLECharacteristicCmdValue(cmdCharacteristicId, ackStr, type);
}, delay);
} while (!device.sending)
}
/**获取所有历史数据**/
function getHistoryRecordCmdValue() {
var cmdId = 4;
var version = protocolVersion * 16 + 0 * 8;
var keyMark = 0;
var ackType = 2;
var valueLong = 0;
var cmdByte = new ArrayBuffer(5);
var cmdView = new Uint8Array(cmdByte);
cmdView[0] = int2byte(cmdId);
cmdView[1] = int2byte(version);
cmdView[2] = int2byte(keyMark);
cmdView[3] = int2byte(ackType);
cmdView[4] = int2byte(valueLong);
console.log('第一个转换', JSON.stringify(new Uint8Array(cmdByte)));
return cmdByte;
}
/**
* 封住发送的完整指令
*/
function packageSendCmd(headerNumber, value) {
var cmdValue = new Uint8Array(value);
var sortHi = headerNumber / 256;
var sortLow = headerNumber % 256;
var valueNum = 2;
var addValue = new ArrayBuffer(cmdValue.length + 2);
var bufView = new Uint8Array(addValue);
bufView[0] = int2byte(sortHi);
bufView[1] = int2byte(sortLow); //添加发送序号,占两位
for (var i = 0; i < cmdValue.length; i++) {
bufView[valueNum + i] = cmdValue[i];
}
var m_cmdArray = [];
if (bufView.length <= 16) {
var lenght = bufView.length + 4;
var checkNum = 255 + lenght;
var myCmd = new ArrayBuffer(lenght);
var myCmdView = new Uint8Array(myCmd);
myCmdView[0] = int2byte(255);
myCmdView[1] = int2byte(0);
myCmdView[2] = int2byte(lenght);
for (var i = 0; i < bufView.length; i++) {
myCmdView[3 + i] = bufView[i];
checkNum = checkNum + parseInt(bufView[i]);
}
myCmdView[lenght - 1] = int2byte(checkNum % 256);
m_cmdArray.push(myCmd);
} else {
var cmdNum = parseInt(bufView.length / 16);
if (bufView.length % 16 != 0) {
cmdNum = cmdNum + 1;
}
for (var i = 0; i < cmdNum; i++) {
if (i == cmdNum - 1) {
var mark = 128 + 64 + i;
var lenght = bufView.length - i * 16 + 4;
var checkNum = 255 + mark + lenght;
var myCmd = new ArrayBuffer(lenght);
var myCmdView = new Uint8Array(myCmd);
myCmdView[0] = int2byte(255);
myCmdView[1] = int2byte(mark);
myCmdView[2] = int2byte(lenght);
for (var j = i * 16; j < bufView.length; j++) {
myCmdView[3 + j - (i * 16)] = bufView[j];
checkNum = checkNum + parseInt(bufView[j]);
}
myCmdView[lenght - 1] = int2byte(checkNum % 256);
m_cmdArray.push(myCmd);
} else {
var mark = 128 + i;
var lenght = 20;
var checkNum = 255 + mark + lenght;
var myCmd = new ArrayBuffer(lenght);
var myCmdView = new Uint8Array(myCmd);
myCmdView[0] = int2byte(255);
myCmdView[1] = int2byte(mark);
myCmdView[2] = int2byte(lenght);
for (var j = i * 16; j < i * 16 + 16; j++) {
myCmdView[3 + j - (i * 16)] = bufView[j];
checkNum = checkNum + parseInt(bufView[j]);
}
myCmdView[lenght - 1] = int2byte(checkNum % 256);
m_cmdArray.push(myCmd);
}
}
}
return m_cmdArray;
}
/** 向蓝牙设备发送指令*/
function wwriteBLECharacteristicCmdValue(cmdCharacteristicId, cmdValue, type) {
var ss = device.deviceId + "-" + device.serviceId + '-' + cmdCharacteristicId + '--' + byte2hexStr(cmdValue);
console.log("向蓝牙设备发送指令:" + ss);
sendCmdCallBackListener({ msg: '发送成功', type: type });
sendAckCmd(device.services, cmdValue, cmdCharacteristicId, device.devices);
}
/**
* byte转16进制字符
*/
function byte2hexStr(byteBuffer) {
var str = "";
var arrBytes = new Uint8Array(byteBuffer);
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num = arrBytes[i];
if (num < 0) {
//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
tmp = (255 + num + 1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
if (i > 0) {
str += ":" + tmp;
} else {
str += tmp;
}
}
return str;
}
//设置utc
export function setUtc() {
var date = new Date();
var date = formatTime(date);
var headNum = sendUtcCmd(date, 'utc');
console.log('headNum',headNum);
return;
}
/**
* 发送设置Utc指令
*/
function sendUtcCmd(date, type, callback) {
var cmdBuffer = getUtcCmdValue(date);
var headNum = device.headerNumber;
var valueBuffer = packageSendCmd(headNum, cmdBuffer);
console.log('valueBuffer2', valueBuffer);
sendBLECmdValue(valueBuffer, type, callback);
return headNum;
}
/**设置时间**/
function getUtcCmdValue(timeDate) {
var cmdId = 1;
var version = protocolVersion * 16 + 0 * 8;
var keyMark = 8;
var ackType = 1;
var valueLong = 4;
var myUtc = (new Date(timeDate).getTime()) / 1000 - (new Date().getTimezoneOffset()) * 60;
var time4 = myUtc / (256 * 256 * 256);
var time3 = (myUtc % (256 * 256 * 256)) / 65536;
var time2 = (myUtc % 65536) / 256;
var time1 = myUtc % 256;
var cmdByte = new ArrayBuffer(9);
var cmdView = new Uint8Array(cmdByte);
cmdView[0] = int2byte(cmdId);
cmdView[1] = int2byte(version);
cmdView[2] = int2byte(keyMark);
cmdView[3] = int2byte(ackType);
cmdView[4] = int2byte(valueLong);
cmdView[5] = int2byte(time4);
cmdView[6] = int2byte(time3);
cmdView[7] = int2byte(time2);
cmdView[8] = int2byte(time1);
return cmdByte;
}