You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native对接Accu-check Guide-me血糖仪插试纸断连问题

React Native 对接 Accu-check Guide Me 血糖仪插入试纸断连解决方案

问题背景

  • 技术栈:基于React Native框架、react-native-ble-plx 库开发多设备适配的蓝牙健康测量应用,目前已完成部分血压计、体重秤的对接,需输入序列号配对的血糖仪类设备连通存在异常。
  • 已验证通用连接流程:扫描设备 -> 发现目标设备 -> 停止扫描 -> 建立连接 -> 发现全部服务与特征值 -> 拉取服务列表 -> 过滤测量类服务 -> 从目标服务下查找特征值列表 -> 开启特征值监听
  • 异常表现:Accu-check Guide Me 血糖仪完成配对、连接成功后,只要插入检测试纸就会触发蓝牙断连,其余已适配设备无该问题。
  • 复现路径:发起连接 -> 弹出序列号输入弹窗 -> 输入序列号 -> 提示连接成功 -> 插入检测试纸 -> 连接断开
  • 初步判断:该设备在通用BLE连接流程外存在额外的交互要求,公开资料检索未找到明确适配方案。

现有问题代码

const scanAndConnectAcucheckGlucoseMeter = () => {
  return new Promise((resolve, reject) => {
    let result = 0;
    console.log('scanStart');
    manager.startDeviceScan(null, null, (error, device) => {
      if (error) {
        console.log('error : ' + error);
        return;
      }

      const deviceName = device.name;
      if (deviceName != null) {
        if (deviceName.includes('meter+')) {
          console.log('detected!!');
          manager.stopDeviceScan();
          resolve(device);
        }
      }
    });
  });
};


const MonitoringForAcucheckGuideMe = device => {
  return new Promise((resolve, reject) => {
    device
      .connect()
      .then(device => {
        const result = device.discoverAllServicesAndCharacteristics();
        return result;
      })
      .then(device => {
        return device.services();
      })
      .then(services => {
        // 180A : device information
        // 1800 : generic access
        // 1808 : glucose
        // 00000000-0000-1000-1000-00000000000 : custom service
        const result = services.filter(id => id.uuid.indexOf('1808') != -1); // Glucose Service
        return result[0].characteristics();
      })
      .then(characters => {
        // Glucose Measurement :00002A18 / Descriptors 2902
        // Glucose Feature : 2A51
        // Record Access Control Point 2A52 / Descriptors 2902
        // DateTime 2A08
        const glucoseMeasurement = characters.filter(
          data => data.uuid.indexOf('2a18') != -1, // 2a35 is for blood sugar measurement
        );

        // popup to insert pin number on mobile when monitor()
        glucoseMeasurement[0].monitor((error, characteristic) => {
          if (error) {
            console.log('error:::::', error);
            reject(error);
          }
          
          const Valuebytes = Buffer.from(characteristic.value, 'base64');
          
          console.log(Valuebytes);

          const result = "Not yet"
          resolve(result)
          
        });
      })
      .catch(error => console.log(error));
  });
};

问题根因

Accu-check Guide Me 严格遵循蓝牙SIG 血糖服务(GLS,Service UUID 0x1808)规范实现,现有代码仅做了血糖测量特征的监听,缺失四个强制流程,设备检测到客户端未完成规范配置,会在测量动作触发时主动断开连接:

  1. 特征值UUID匹配逻辑存在缺陷:使用indexOf做字符串模糊匹配,容易误命中包含相同字符的厂商自定义服务/特征,导致后续操作作用在错误的特征上。
  2. 未显式开启特征通知:虽然react-native-ble-plx的monitor方法在部分设备上会自动写入客户端特征配置描述符(CCCD,UUID 0x2902)开启通知,但罗氏设备要求必须显式对**血糖测量特征(0x2A18)、记录访问控制点特征(0x2A52)**两个特征写入CCCD值开启通知,缺一不可。
  3. 未做时间同步:罗氏设备在启动测量前会校验连接是否已同步当前时间,未向日期时间特征(0x2A08)写入正确时间的连接会被主动断开。
  4. 连接参数配置不足:未申请足够的MTU值,设备传输测量数据时若包长超过默认MTU会触发断连。

修正后实现代码

// 标准蓝牙SIG UUID常量,避免字符串匹配错误
const GLUCOSE_SERVICE_UUID = '00001808-0000-1000-8000-00805f9b34fb';
const GLUCOSE_MEASUREMENT_CHAR_UUID = '00002a18-0000-1000-8000-00805f9b34fb';
const GLUCOSE_RACP_CHAR_UUID = '00002a52-0000-1000-8000-00805f9b34fb';
const DATE_TIME_CHAR_UUID = '00002a08-0000-1000-8000-00805f9b34fb';
const CCCD_UUID = '00002902-0000-1000-8000-00805f9b34fb';

const scanAndConnectAcucheckGlucoseMeter = () => {
  return new Promise((resolve, reject) => {
    console.log('Start scanning for glucose meter');
    // 扫描时指定服务UUID,减少无关设备扫描结果,降低功耗
    manager.startDeviceScan([GLUCOSE_SERVICE_UUID], null, (error, device) => {
      if (error) {
        console.log('Scan error:', error);
        reject(error);
        return;
      }
      // 精确匹配设备名,避免误连同前缀其他设备
      if (device.name && device.name.toLowerCase().includes('meter+')) {
        console.log('Target Accu-check device found');
        manager.stopDeviceScan();
        resolve(device);
      }
    });
  });
};

const MonitoringForAcucheckGuideMe = device => {
  return new Promise((resolve, reject) => {
    let glucoseChar, racpChar, dateTimeChar;
    device
      .connect({
        requestMTU: 247 // 申请最大MTU,避免大数据包传输断连
      })
      .then(device => device.discoverAllServicesAndCharacteristics())
      .then(device => device.services())
      .then(services => {
        // 全串匹配精确查找血糖服务
        const glucoseService = services.find(s => s.uuid.toLowerCase() === GLUCOSE_SERVICE_UUID);
        if (!glucoseService) throw new Error('Standard glucose service not found on device');
        return glucoseService.characteristics();
      })
      .then(characters => {
        // 精确查找三个所需特征值
        glucoseChar = characters.find(c => c.uuid.toLowerCase() === GLUCOSE_MEASUREMENT_CHAR_UUID);
        racpChar = characters.find(c => c.uuid.toLowerCase() === GLUCOSE_RACP_CHAR_UUID);
        dateTimeChar = characters.find(c => c.uuid.toLowerCase() === DATE_TIME_CHAR_UUID);
        if (!glucoseChar || !racpChar || !dateTimeChar) throw new Error('Required glucose characteristics missing');

        // 封装通用通知开启方法,显式写入CCCD
        const enableNotification = async (char) => {
          const descriptors = await char.descriptors();
          const cccd = descriptors.find(d => d.uuid.toLowerCase() === CCCD_UUID);
          // 写入0x0001(小端序)开启通知
          await cccd.writeWithResponse(Buffer.from([0x01, 0x00]).toString('base64'));
        };
        // 同时开启两个特征的通知
        return Promise.all([enableNotification(glucoseChar), enableNotification(racpChar)]);
      })
      .then(() => {
        // 按蓝牙SIG标准日期时间格式编码当前时间,写入设备完成同步
        const now = new Date();
        const timeBuffer = Buffer.alloc(7);
        timeBuffer.writeUInt16LE(now.getFullYear(), 0);
        timeBuffer.writeUInt8(now.getMonth() + 1, 2); // 月份从1开始计数
        timeBuffer.writeUInt8(now.getDate(), 3);
        timeBuffer.writeUInt8(now.getHours(), 4);
        timeBuffer.writeUInt8(now.getMinutes(), 5);
        timeBuffer.writeUInt8(now.getSeconds(), 6);
        return dateTimeChar.writeWithResponse(timeBuffer.toString('base64'));
      })
      .then(() => {
        // 所有前置配置完成后,再开启测量数据监听
        glucoseChar.monitor((error, characteristic) => {
          if (error) {
            console.log('Glucose data monitor error:', error);
            reject(error);
            return;
          }
          if (!characteristic?.value) return;
          const rawData = Buffer.from(characteristic.value, 'base64');
          console.log('Received raw glucose measurement:', rawData);
          // 此处按蓝牙GLS服务规范解析血糖值、单位、测量时间等字段即可
          resolve("Glucose data channel initialized successfully");
        });

        // 按需开启RACP特征监听,处理历史记录查询响应
        racpChar.monitor((error, characteristic) => {
          if (error || !characteristic?.value) return;
          const racpResp = Buffer.from(characteristic.value, 'base64');
          console.log('RACP response received:', racpResp);
        });
      })
      .catch(error => {
        console.log('Glucose meter connection flow failed:', error);
        reject(error);
      });
  });
};

补充注意事项

  • 不要在测量值监听回调中提前结束Promise,后续每次测量完成都会触发该回调,需要持续处理解析逻辑。
  • 若修改后插入试纸仍断连,优先检查系统蓝牙配对状态:罗氏设备要求连接必须达到加密配对等级,未完成系统层面的配对绑定会被设备拒绝传输测量数据。
  • 若需要拉取设备历史测量记录,必须通过RACP特征发送标准操作指令,不能直接读取测量特征值。

内容的提问来源于stack exchange,提问作者Miguel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:42:22