API介绍

1、ArrayBuffer:用来表示通用的、固定长度的原始二进制数据缓冲区。它不能直接读写,只能通过视图(TypedArray 视图和 DataView 视图)来读写,视图的作用是以指定格式解读二进制数据。

2、TypedArray:一个 TypedArray 对象描述了底层二进制数据缓冲区的类数组视图

3、DataView :DataView 视图是一个可以从二进制 ArrayBuffer 对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序(endianness)问题。

4、getUint8() 方法从 DataView相对于起始位置偏移 n 个字节处开始,获取一个无符号的 8-bit 整数 (一个字节)

5、getUint16() 方法从 DataView 相对于起始位置偏移 n 个字节处开始,获取一个 16-bit 数 (无符号短整型,2 个字节)

6、getUint32() 方法从 DataView 相对于起始位置偏移 n 个字节处开始,获取一个 32-bit 数 (无符号长整型,4 个字节)

7、setInt8() 从 DataView 起始位置以 byte 为计数的指定偏移量 (byteOffset) 处储存一个 8-bit 数 (一个字节)

8、setUint16() 从 DataView 起始位置以 byte 为计数的指定偏移量 (byteOffset) 处储存一个 16-bit 数 (无符号短整型)

9、setUint32() 从 DataView 起始位置以 byte 为计数的指定偏移量 (byteOffset) 处储存一个 32-bit 数 (无符号长整型)

10、Blob :对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

项目使用场景示例

开发小程序蓝牙传输项目中,接收数据和发送数据的处理。

1、10进制转16进制转二进制数据处理

2、解析二进制数据流为16进制/10进制

 1、$command_tobuffer,  // 将字符转化为buffer

/** 
 * 命令帧
 * @typedef {object} json
 * @property {Hex} head
 * @property {Hex} end
 * @property {Number} length
 * @property {Hex} command
 * @property {String|Number} code
 * @returns ArrayBuffer
 */
function  $command_tobuffer({ head = 0x55, end = 0x16, length, command, body, type='default' }) {
  const byteOffset = 3; // 帧头 + 帧长度 + 命令
  const buffer = new ArrayBuffer(byteOffset + length + 1); //  byteOffset - 1 + length + 校验值 + 帧尾
  const dataView = new DataView(buffer);
 
  dataView.setUint8(0, head); // 帧头
  dataView.setUint8(1, '0x' + length.toString(16)); // 帧长
 
  dataView.setUint8(2, command); // 命令字
 
  // 主体信息
  if(type==='default'){
    string2HexFromDataView({
      dataView,
      string: body,
      byteOffset
    });
  } else {
    number2HexForDataView({
      dataView,
      number: body,
      byteOffset
    });
  }
 
 
  const checkCodeIdx = length + 2;
  const sign = checkSum(dataView, 2, checkCodeIdx);
  // console.log('sum', sign);
  dataView.setUint8(checkCodeIdx, '0x' + sign); // 检验码
 
  dataView.setUint8(checkCodeIdx + 1, end); // 帧尾
  return {buffer, checkSum:sign};
}
 
/** 
 * 字符串转16进制
 * @typedef {object} json
 * @property {DataView} dataView
 * @property {String} string
 * @property {Number} byteOffset
 */
function string2HexFromDataView({ dataView, string, byteOffset = 0 }) {
    for (let i = 0; i < string.length; i++) {
      const idx = i + byteOffset;
        // console.log(string[i].charCodeAt(0).toString(16))
      dataView.setUint8(idx, '0x' + string[i].charCodeAt(0).toString(16))
    }
}
 
/** 
 * 数值(10进制)转16进制buffer  为了容 时间戳
 */
function number2HexForDataView({ dataView, number, byteOffset = 0 }) {
  const hex = number.toString(16);
  for (let i = 0; i < hex.length; i+=2) {
    const idx = i / 2 + byteOffset;
    dataView.setUint8(idx, '0x' + hex.substr(i, 2))
  }
}
/**
 * 检验和
 * @param {DataView} dataView 
 * @param {Number} start 
 * @param {Number} end 
 * @returns Hex
 */
function checkSum(dataView, start, end) {
  let sum = 0;
  for (let i = start; i <= end; i++) {
    const view = dataView.getUint8(i);
    
    sum += view;
  }
  // console.log('ss', sum)
  // return (('0x' + sum.toString(16)) & 0xFF).toString(16); // 与运算取8位
  const resHex = (('0x' + sum.toString(16)) & 0xFF).toString(16);
  return resHex.length === 1? 0+''+ resHex: resHex;
}

一个完整的传输帧包括如:帧头+帧长度+帧命令+帧内容+和校验+帧尾;

例如 dataView.setUint8(0, head) :dataView视图设置0的位置是由16进制标识0x开头的4位字符。以此类推最终拼接成一个完整的ArrayBuffer通过蓝牙传输出去。