Skip to main content

Device Types & Configurations

Comprehensive documentation of supported medical devices, their configurations, and device-specific implementations in the BT Management system.

Overview​

The BT Management module supports over 20 different types of medical devices, each with specific BLE protocols, data parsing requirements, and measurement capabilities. This document provides detailed information about device configurations, supported measurements, and integration patterns.

Device Categories​

Blood Pressure Monitors​

BP2 Advanced Blood Pressure Monitor​

The BP2 is an advanced blood pressure monitor with ECG capability, providing comprehensive cardiovascular measurements.

Supported Measurements:

  • Blood pressure (systolic/diastolic)
  • Heart rate
  • ECG waveform data
  • Diagnostic interpretations

Device Configuration:

{
deviceName: IntegratedDevices.BP2,
mainService: {
uuid: "14839ac4-7d7e-415c-9a42-167340cf2339",
monitorUuids: [
{
uuid: "0734594a-a8e7-4b1a-a6b1-cd5243059a57",
parsingModel: {
caseValuePosition: [0, 1, 7],
messageCountParsingDefinition: (msg: string[]): number =>
parseInt((msg?.[6] ?? "0") + (msg?.[5] ?? "0"), 16) + 8,
cases: [
{
returnType: MeasurementTypeKey.bloodPressure,
caseType: CaseType.RESULT,
caseValue: "a50805",
parseDefinitions: [
{
name: "systolic",
parseType: "number",
byteIndexes: [21, 20],
parseReference: 16,
},
{
name: "diastolic",
parseType: "number",
byteIndexes: [23, 22],
parseReference: 16,
},
{
name: "heartRate",
parseType: "number",
byteIndexes: [27, 26],
parseReference: 16,
},
{
name: "status",
parseType: "custom",
byteIndexes: [28],
customParserDefinition: (a: string): string =>
convertBpStateFlag(a),
}
]
},
{
returnType: MeasurementTypeKey.ecg,
caseType: CaseType.RESULT,
caseValue: "a50807",
parseDefinitions: [
{
name: "heartRate",
parseType: "number",
byteIndexes: [22, 21],
parseReference: 16,
},
{
name: "diagnosticResult",
parseType: "custom",
byteIndexes: [17, 18, 19, 20],
customParserDefinition: (a: string): string =>
convertEcgDiagnosticFlag(a),
}
]
}
]
}
}
],
writeUuid: {
uuid: "8b00ace7-eb0b-49b0-bbe9-9aee0a26e1a3",
mainCommandUuid: "a508f700000000ee"
}
}
}

Usage Example:

import { DeviceBp2 } from "@ovok/native";

// Device automatically handles both BP and ECG measurements
const handleMeasurement = (measurement, device) => {
if (measurement.measurementTypeKey === MeasurementTypeKey.bloodPressure) {
console.log(`BP: ${measurement.systolic}/${measurement.diastolic} mmHg`);
console.log(`HR: ${measurement.heartRate} bpm`);
console.log(`Status: ${measurement.status}`);
} else if (measurement.measurementTypeKey === MeasurementTypeKey.ecg) {
console.log(`ECG HR: ${measurement.heartRate} bpm`);
console.log(`Diagnosis: ${measurement.diagnosticResult}`);
console.log(`Waveform points: ${measurement.diagramPoints?.length}`);
}
};

Error Handling:

// BP2 specific error states
const bloodPressureStates = {
REGULAR: "Regular",
LOOSE_SLEEVE: "The sleeve is loose. Unable to analyze.",
DISTURB_DETECTED: "Disturb Detected.",
WEAK_SIGNAL: "Weak Signal.",
DEVICE_ERROR: "Unknown device error.",
};

Glucose Meters​

TAIDOC TD4216 Professional Glucose Meter​

Professional glucose meter with automatic data synchronization and error detection.

Supported Measurements:

  • Blood glucose levels (mg/dL)
  • Measurement timestamp
  • Strip detection status

Device Configuration:

{
deviceName: IntegratedDevices.TAIDOC_GLUCOSE,
mainService: {
uuid: "00001523-1212-efde-1523-785feabcd123",
monitorUuids: [
{
uuid: "00001524-1212-efde-1523-785feabcd123",
parsingModel: {
caseValuePosition: [1],
messageCountParsingDefinition: (): number => 8,
cases: [
{
returnType: MeasurementTypeKey.bloodGlucose,
caseType: CaseType.RESULT,
caseValue: "26",
parseDefinitions: [
{
name: "bloodGlucose",
parseType: "custom",
byteIndexes: [3, 2],
parseReference: 16,
customParserDefinition: (a: string): number => {
const result = parseInt(a, 16) * 18;
return Number(result.toFixed(2));
},
}
]
}
]
}
}
],
writeUuid: {
uuid: "00001524-1212-efde-1523-785feabcd123",
mainCommandUuid: "512600000000A31A"
}
}
}

GlucoCheck Gold Glucose Meter​

Portable glucose meter with enhanced error detection and automatic power management.

Usage Example:

const handleGlucoseMeasurement = (measurement, device) => {
if (measurement.measurementTypeKey === MeasurementTypeKey.bloodGlucose) {
console.log(`Glucose: ${measurement.bloodGlucose} mg/dL`);

// Check for abnormal readings
if (measurement.bloodGlucose < 70) {
alert("Low glucose detected - consult healthcare provider");
} else if (measurement.bloodGlucose > 200) {
alert("High glucose detected - consult healthcare provider");
}
}
};

BGM Basic Glucose Meter​

Basic glucose meter with CRC validation and time synchronization.

Special Features:

  • Automatic time synchronization
  • CRC validation for data integrity
  • Multiple measurement attempts

Thermometers​

AOJ-20A Non-Contact Infrared Thermometer​

Non-contact infrared thermometer with battery monitoring and error detection.

Supported Measurements:

  • Body temperature (°C)
  • Battery status
  • Measurement mode validation

Device Configuration:

{
deviceName: IntegratedDevices.AOJ20A,
mainService: {
uuid: "0000FFE0-0000-1000-8000-00805F9B34FB",
monitorUuids: [
{
uuid: "0000FFE1-0000-1000-8000-00805F9B34FB",
parsingModel: {
caseValuePosition: [0, 2],
cases: [
{
returnType: MeasurementTypeKey.temperature,
caseType: CaseType.RESULT,
caseValue: "aac1",
parseDefinitions: [
{
name: "bodyTemp",
parseType: "custom",
byteIndexes: [4, 5],
parseReference: 16,
customParserDefinition: (a: string): number =>
Number((parseInt(a, 16) / 100).toFixed(1)),
}
]
}
]
}
}
],
writeUuid: {
uuid: "0000FFE2-0000-1000-8000-00805F9B34FB",
mainCommandUuid: "aa01d500d4"
}
}
}

Error Detection:

const temperatureErrorCodes = {
lowTemp: "feedback-messages.measurement.low-body-temperature",
highTemp: "feedback-messages.measurement.high-body-temperature",
modeError: "feedback-messages.measurement.correct-mode-body-temperature",
};

Cosinuss Two Multi-Sensor Ear Thermometer​

Advanced ear thermometer with multiple physiological parameters.

Supported Measurements:

  • Body temperature
  • Blood oxygen saturation
  • Pulse rate
  • RR interval
  • Perfusion index
  • Signal quality
  • Battery level

Multi-Service Configuration:

{
deviceName: IntegratedDevices.COSINUSS,
mainService: [
{
uuid: "0000180d-0000-1000-8000-00805f9b34fb", // Heart Rate Service
monitorUuids: [
{
uuid: "00002a37-0000-1000-8000-00805f9b34fb",
parsingModel: {
caseValuePosition: [0],
cases: [
{
returnType: MeasurementTypeKey.bloodOxygenPulseRate,
caseType: CaseType.STREAM,
caseValue: "10",
parseDefinitions: [
{
name: "pulseRate",
parseType: "number",
byteIndexes: [1],
parseReference: 16,
},
{
name: "rrInterval",
parseType: "custom",
byteIndexes: [3, 2],
customParserDefinition: (a: string): number =>
Number(((parseInt(a, 16) / 1024) * 1000).toFixed(1)),
}
]
}
]
}
}
]
},
{
uuid: "00001822-0000-1000-8000-00805f9b34fb", // Pulse Oximeter Service
monitorUuids: [
{
uuid: "00002a5f-0000-1000-8000-00805f9b34fb",
parsingModel: {
caseValuePosition: [0],
cases: [
{
returnType: MeasurementTypeKey.bloodOxygenPulseRate,
caseType: CaseType.STREAM,
caseValue: "10",
parseDefinitions: [
{
name: "bloodOxygen",
parseType: "number",
byteIndexes: [1],
parseReference: 16,
},
{
name: "perfusionIndex",
parseType: "custom",
byteIndexes: [5],
customParserDefinition: (a: string): number =>
parseInt(a, 16) / 100,
}
]
}
]
}
}
]
},
{
uuid: "00001809-0000-1000-8000-00805f9b34fb", // Temperature Service
monitorUuids: [
{
uuid: "00002a1c-0000-1000-8000-00805f9b34fb",
parsingModel: {
caseValuePosition: [0],
cases: [
{
returnType: MeasurementTypeKey.temperature,
caseType: CaseType.STREAM,
caseValue: "04",
parseDefinitions: [
{
name: "bodyTemp",
parseType: "custom",
byteIndexes: [2, 1],
parseReference: 16,
customParserDefinition: (a: string): number =>
Number((parseInt(a, 16) / 100).toFixed(1)),
}
]
}
]
}
}
]
}
]
}

Scales & Body Composition​

F4 Smart Scale​

Smart scale with real-time weight streaming and measurement validation.

Supported Measurements:

  • Real-time weight streaming
  • Final weight measurement
  • Measurement stability detection

Device Configuration:

{
deviceName: IntegratedDevices.F4,
mainService: {
uuid: "0000ffb0-0000-1000-8000-00805f9b34fb",
monitorUuids: [
{
uuid: "0000ffb2-0000-1000-8000-00805f9b34fb",
parsingModel: {
caseValuePosition: [4],
cases: [
{
returnType: MeasurementTypeKey.bodyWeight,
caseValue: "01",
caseType: CaseType.STREAM,
parseDefinitions: [
{
name: "bodyWeight",
byteIndexes: [5, 6, 7, 8],
parseType: "custom",
parseReference: 16,
customParserDefinition: (a: string): number =>
parseInt(a, 16) / 100,
}
]
}
]
}
},
{
uuid: "0000FFB3-0000-1000-8000-00805F9B34FB",
parsingModel: {
caseValuePosition: [3],
cases: [
{
returnType: MeasurementTypeKey.bodyWeight,
caseValue: "a3",
caseType: CaseType.RESULT,
parseDefinitions: [
{
name: "bodyWeight",
byteIndexes: [5, 6, 7],
parseType: "custom",
parseReference: 16,
customParserDefinition: (a: string): number =>
parseInt(a, 16) / 1000,
}
]
}
]
}
}
]
}
}

Measurement Timeout Handling:

// F4 has 15-second timeout for measurements
class DeviceF4 extends BaseDevice {
private scalesMaximumMeasurementTime: NodeJS.Timeout | undefined;

private startMeasurementTimer() {
this.scalesMaximumMeasurementTime = setTimeout(() => {
if (!this.measurementResult) {
this.isMeasuring = false;
this.updateDeviceStatus(DeviceStatus.CONNECTED);
}
}, 15000);
}
}

GBS-2012-B (RPM Scale)​

Medical-grade scale with timestamp support and historical data retrieval.

Special Features:

  • Historical measurement storage
  • Timestamp synchronization
  • Multiple measurement handling

Le S5 Body Composition Scale​

Advanced body composition scale with streaming data and stability detection.

Implementation Pattern:

class DeviceS5 extends BaseDevice {
private parsedDataOccurrences: { [key: string]: number } = {};
private sentMeasurements: Set<string> = new Set();

private handleStreamData(parsedData: BodyWeightMeasurement): void {
const bodyWeightData = parsedData?.bodyWeight?.toFixed(2);

if (!this.parsedDataOccurrences[bodyWeightData]) {
this.parsedDataOccurrences[bodyWeightData] = 1;
this.updateDeviceStatus(DeviceStatus.MEASURING);
} else {
this.parsedDataOccurrences[bodyWeightData]++;
}

// Require 3 consecutive identical readings for stability
if (
this.parsedDataOccurrences[bodyWeightData] >= 3 &&
!this.sentMeasurements.has(bodyWeightData)
) {
this.updateMeasurement(parsedData);
this.sentMeasurements.add(bodyWeightData);
}
}
}

Pulse Oximeters​

Oxyfit Professional Pulse Oximeter​

Professional pulse oximeter with waveform data and battery monitoring.

Supported Measurements:

  • Blood oxygen saturation (SpO2)
  • Pulse rate
  • Plethysmograph waveform
  • Battery status

Device Configuration:

{
deviceName: IntegratedDevices.SPO2,
mainService: {
uuid: "14839ac4-7d7e-415c-9a42-167340cf2339",
monitorUuids: [
{
uuid: "0734594A-A8E7-4B1A-A6B1-CD5243059A57",
parsingModel: {
caseValuePosition: [0, 18],
messageCountParsingDefinition: (): number => 21,
cases: [
{
returnType: MeasurementTypeKey.bloodOxygenPulseRate,
caseType: CaseType.STREAM,
caseValue: "5501",
parseDefinitions: [
{
name: "bloodOxygen",
parseType: "number",
byteIndexes: [7],
parseReference: 16,
},
{
name: "pulseRate",
parseType: "number",
byteIndexes: [8],
parseReference: 16,
}
]
}
]
}
}
],
writeUuid: {
uuid: "8B00ACE7-EB0B-49B0-BBE9-9AEE0A26E1A3",
mainCommandUuid: "AA17E800000100002A"
}
}
}

OxySmart PC60WF Advanced Pulse Oximeter​

Advanced pulse oximeter with perfusion index and error detection.

Error Handling:

const handlePC60WFError = (errorMessage: string[]): string | undefined => {
const result = Converter.hex2bin(errorMessage[9] ?? "0");

switch (result) {
case "0010":
return "feedback-messages.measurement.blood-oxygen-probe-off";
case "00010000":
return "feedback-messages.measurement.disturb-detected-error";
case "0001":
return "feedback-messages.measurement.blood-oxygen-probe-off";
case "00001000":
return "feedback-messages.measurement.blood-oxygen-misusing-probe";
case "00100000":
return "feedback-messages.measurement.blood-oxygen-low-perfusion-index";
default:
return undefined;
}
};

Spirometers​

PULMO80B Professional Spirometer​

Professional spirometer with comprehensive lung function testing capabilities.

Supported Measurements:

  • FVC (Forced Vital Capacity)
  • FEV0.5, FEV1 (Forced Expiratory Volumes)
  • FEV1/FVC ratio
  • PEF (Peak Expiratory Flow)
  • FEF25, FEF50, FEF75 (Forced Expiratory Flows)
  • FEF25-75 (Forced Expiratory Flow 25-75%)

Device Configuration:

{
deviceName: IntegratedDevices.SP80B,
mainService: {
uuid: "0000ff12-0000-1000-8000-00805f9b34fb",
monitorUuids: [
{
uuid: "0000ff02-0000-1000-8000-00805f9b34fb",
parsingModel: {
caseValuePosition: [0],
messageCountParsingDefinition: (): number => 44,
cases: [
{
returnType: MeasurementTypeKey.spirometryPanel,
caseType: CaseType.RESULT,
caseValue: "e1",
parseDefinitions: [
{
name: "fvc",
parseType: "custom",
byteIndexes: [17, 18],
parseReference: 16,
customParserDefinition: (a: string): string =>
Converter.calculateSpirometerValue(a),
},
{
name: "fev1",
parseType: "custom",
byteIndexes: [21, 22],
parseReference: 16,
customParserDefinition: (a: string): string =>
Converter.calculateSpirometerValue(a),
},
{
name: "pef",
parseType: "custom",
byteIndexes: [29, 30],
parseReference: 16,
customParserDefinition: (a: string): string =>
Converter.calculateSpirometerValue(a),
}
// ... additional parameters
]
}
]
}
}
],
writeUuid: {
uuid: "0000ff01-0000-1000-8000-00805f9b34fb",
mainCommandUuid: "91000011"
}
}
}

Special Handling:

class SP80BDevice extends BaseDevice {
private interval: NodeJS.Timeout | undefined;

protected async processDataWithDeviceLogic(
messageFromDevice: string[],
characteristic: IMonitorCharacteristic,
): Promise<void> {
this.sp80bMsgController.collectMessage(
messageFromDevice,
async (fullMessage) => {
const parsedData = DataParser.parse(fullMessage, parsingCase);

// Set interval to check for new data
this.interval = setInterval(() => {
this.getLatestData();
}, 10000);

const error = this.checkIfHasError(parsedData as SpirometryMeasurement);

if (error === undefined) {
this.updateMeasurement(parsedData);
this.deleteAllData();
clearInterval(this.interval);
}
},
);
}
}

Urinalysis Devices​

BC01 Multi-Parameter Urine Analyzer​

Comprehensive urine analyzer supporting 11 different parameters.

Supported Parameters:

  • URO (Urobilinogen)
  • BLD (Blood)
  • BIL (Bilirubin)
  • KET (Ketones)
  • GLU (Glucose)
  • PRO (Protein)
  • NIT (Nitrites)
  • LEU (Leukocytes)
  • SG (Specific Gravity)
  • PH (pH)
  • VC (Vitamin C)

Diagnostic Results:

export const diagnosticResults = {
URO: ["Norm", "1+", "2+", "3+"],
BLD_PRO_LEU_VC: ["-", "+-", "1+", "2+", "3+"],
BIL_KET: ["-", "1+", "2+", "3+"],
GLU: ["-", "+-", "1+", "2+", "3+", "4+"],
NIT: ["-", "+"],
PH: ["5", "6", "7", "8", "9"],
SG: ["1.005", "1.010", "1.015", "1.020", "1.025", "1.030"],
};

Binary Parsing:

const parseUrinalysisParameter = (
hexValue: string,
parameterType: DiagnosticKeyEnum,
): string | undefined => {
const binaryResult = Converter.hex2binAlternative(hexValue);

// Extract relevant bits based on parameter position
let bitSlice: string;
switch (parameterType) {
case DiagnosticKeyEnum.URO:
bitSlice = binaryResult.slice(5);
break;
case DiagnosticKeyEnum.BLD_PRO_LEU_VC:
bitSlice = binaryResult.slice(2, 5);
break;
// ... other parameters
}

const resultIndex = parseInt(bitSlice, 2);
return getDiagnosticResult(resultIndex, parameterType);
};

Device Implementation Patterns​

BaseDevice Architecture​

All medical devices extend the BaseDevice abstract class which provides:

export abstract class BaseDevice implements IBaseDevice {
protected msgController = new MessageCollector();
protected sp80bMsgController = new SP80BMessageCollector();
protected isMeasuring = false;
protected measurementResult: Measurement | undefined;

// Common device properties
public readonly deviceModel: IMedicalDevice;
public readonly deviceId: string;
public readonly deviceName: IntegratedDevices;
public readonly deviceData: DeviceData;

// Lifecycle method — connect() drives the entire pair + subscribe flow
// (subscribeToAllCharacteristics is a PRIVATE internal step on the
// bt-connection-handler; do NOT expose it as part of the device class).
public connect = async (): Promise<void> => {
// Connection implementation (internally also subscribes to characteristics)
};

// Abstract method for device-specific logic
protected abstract processDataWithDeviceLogic(
messageFromDevice: string[],
characteristic: IMonitorCharacteristic,
): void;

// Optional lifecycle hooks
protected switchOffDevice?(): Promise<void>;
protected afterConnection?(): Promise<void>;
protected afterSubscribe?(): Promise<void>;
}

Common Implementation Patterns​

Stream vs Result Data​

switch (parsingCase.caseType) {
case CaseType.STREAM:
// Real-time data during measurement
this.updateDeviceStatus(DeviceStatus.MEASURING, parsingCase.returnType);
this.updateStreamData(parsedData);
break;

case CaseType.RESULT:
// Final measurement result
if (this.measurementResult === undefined) {
this.measurementResult = parsedData;
this.isMeasuring = false;
this.updateMeasurement(parsedData);
}
break;

case CaseType.OTHER:
// Status updates, battery info, etc.
this.updateDeviceStatus(DeviceStatus.CONNECTED);
break;
}

Error Handling Pattern​

private checkIfHasError = (measurement: Measurement): string | undefined => {
// Validate measurement data
if (!measurement || measurement.someValue === undefined) {
return "feedback-messages.measurement.device-error";
}

// Check for measurement interruption
if (measurement.someValue === 0) {
return "feedback-messages.measurement.device-interrupted";
}

// Device-specific error conditions
if (measurement.someSpecificCondition) {
return "feedback-messages.measurement.specific-error";
}

return undefined;
};

Battery Monitoring Pattern​

private updateBatteryLevelStatus(message: string[]): void {
const batteryPercent = parseInt(message[batteryIndex] ?? "0", 16);

if (batteryPercent <= lowBatteryThreshold && !this.isBatteryLow) {
this.updateDeviceStatus(DeviceStatus.LOW_BATTERY);
this.isBatteryLow = true;
} else if (batteryPercent > lowBatteryThreshold && this.isBatteryLow) {
this.updateDeviceStatus(DeviceStatus.CONNECTED);
this.isBatteryLow = false;
}
}

Device Integration Examples​

Multi-Parameter Device​

// Cosinuss Two - handles multiple measurement types
export class DeviceCosinussTemp extends BaseDevice {
protected pulseRate: number | undefined;
protected bloodOxygen: number | undefined;
protected temperatureMeasurement: BodyTemperatureMeasurement | undefined;

protected processDataWithDeviceLogic(
messageFromDevice: string[],
characteristic: IMonitorCharacteristic,
) {
const parsedData = DataParser.parse(fullMessage, parsingCase);

// Route data based on characteristic UUID
if (characteristic.uuid === "00002a37-0000-1000-8000-00805f9b34fb") {
// Heart rate characteristic
this.pulseRate = parsedData.pulseRate;
this.updateStreamData(parsedData);
} else if (characteristic.uuid === "00002a5f-0000-1000-8000-00805f9b34fb") {
// Pulse oximeter characteristic
this.bloodOxygen = parsedData.bloodOxygen;
this.updateStreamData(parsedData);
} else if (characteristic.uuid === "00002a1c-0000-1000-8000-00805f9b34fb") {
// Temperature characteristic
this.temperatureMeasurement = parsedData;
this.updateStreamData(parsedData);
}
}
}

Time Synchronization​

// BGM device with time sync
export class DeviceBGM extends BaseDevice {
public async setDeviceTime() {
const date = new Date();
const dateArray = this.formatDate(date);

const header = 0x7b;
let data = [0x01, 0x10, 0x01, 0x20, 0x44, 0x66, 0x00, 0x06, ...dateArray];

const crc = this.calculateCRC(data);
const hexCrc = crc.toString(16).toUpperCase().padStart(2, "0");

data = [
header,
...data,
...hexCrc.split("").map((c) => parseInt(c, 16)),
0x7d,
];

await this.device.writeCharacteristicWithoutResponseForService(
"0003cdd0-0000-1000-8000-00805f9b0131",
"0003cdd2-0000-1000-8000-00805f9b0131",
Buffer.from(data).toString("base64"),
);
}
}

Testing Device Implementations​

Unit Testing Pattern​

describe("DeviceF4", () => {
let device: DeviceF4;
let mockBleDevice: Device;

beforeEach(() => {
mockBleDevice = createMockBleDevice("F4");
device = new DeviceF4(mockBleDevice);
});

it("should handle stream data correctly", () => {
const streamData = ["aa", "01", "00", "50"]; // Mock hex data
const characteristic = createMockCharacteristic();

device.processDataWithDeviceLogic(streamData, characteristic);

expect(device.deviceData.status).toBe(DeviceStatus.MEASURING);
});

it("should validate measurement data", () => {
const invalidMeasurement = { bodyWeight: 0 };
const error = device.checkIfHasError(invalidMeasurement);

expect(error).toBe("feedback-messages.measurement.device-interrupted");
});
});

Integration Testing​

describe("Device Integration", () => {
it("should complete full measurement cycle", async () => {
const device = new DeviceF4(mockBleDevice);
const measurements: Measurement[] = [];

// Subscribe to measurements
DeviceEventManager.subscribeToDeviceMeasurements((measurement) => {
measurements.push(measurement);
});

// Connect device — connect() also subscribes to characteristics internally.
await device.connect();

// Simulate measurement data
simulateStreamData(["aa", "01", "00", "50"]);
simulateResultData(["aa", "a3", "00", "1f", "40"]);

// Verify measurement received
expect(measurements).toHaveLength(1);
expect(measurements[0].measurementTypeKey).toBe(
MeasurementTypeKey.bodyWeight,
);
});
});

Performance Considerations​

Message Collection Optimization​

// Efficient message collection for large data sets
export class SP80BMessageCollector {
private timeoutDuration: number;

collectMessage(
partOfMessage: string[],
onMessageCollected: (fullMessage: string[]) => void,
parseMsgLength?: (msg: string[]) => number,
): void {
this.dataInHex = this.dataInHex.concat(partOfMessage);

if (this.dataLength === undefined && parseMsgLength) {
this.dataLength = parseMsgLength(this.dataInHex);

// Set timeout for large spirometry data
setTimeout(() => {
if (this.dataInHex.length >= this.dataLength) {
onMessageCollected(this.dataInHex);
this.resetData();
}
}, this.timeoutDuration);
}
}
}

ECG Signal Processing​

// Optimized ECG filtering for BP2 device
export class Bp2EcgFilter {
private dcBlockerState = { x1: 0, y1: 0 };
private lowPassState = { x1: 0, x2: 0, y1: 0, y2: 0 };
private notchState = { x1: 0, x2: 0, y1: 0, y2: 0 };

public filterDataPoints = (hexArray: string[]): number[] => {
// Fast hex to number conversion
const dataPoints = hexArray.map((hex) => parseInt(hex, 16));

// Apply cascade filtering
return this.applyCascadeFilters(dataPoints);
};

private applyCascadeFilters(data: number[]): number[] {
return data.map((sample) => {
sample = this.dcBlockFilter(sample); // Remove baseline drift
sample = this.notchFilter(sample); // Remove 50/60Hz interference
sample = this.lowPassFilter(sample); // Anti-aliasing
return this.movingAverageFilter(sample); // Final smoothing
});
}
}

Troubleshooting Device Issues​

Common Device Problems​

Connection Issues​

  • Verify device is in pairing mode
  • Check BLE permissions
  • Ensure device is within range
  • Verify device battery level

Data Parsing Issues​

  • Check message length definitions
  • Verify case value positions
  • Validate byte index arrays
  • Test custom parser functions

Measurement Quality Issues​

  • Implement measurement validation
  • Add timeout handling
  • Check for device-specific error codes
  • Monitor signal quality indicators

Debugging Tools​

// Debug message parsing
const debugDataParsing = (hexMessage: string[], parsingCase: ParsingCase) => {
console.log("Raw hex message:", hexMessage);
console.log("Parsing case:", parsingCase);

parsingCase.parseDefinitions.forEach((def, index) => {
const value = DataParser.getFullValueForMeasurementProperty(
hexMessage,
def,
);
console.log(
`${def.name}:`,
value,
"→",
def.parseType === "custom"
? def.customParserDefinition?.(value)
: parseInt(value, def.parseReference || 10),
);
});
};

Dependencies​

  • react-native-ble-plx: BLE communication
  • @ovok/core: Measurement types and constants
  • buffer: Data conversion utilities
  • react-native: Core platform functionality