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 constantsbuffer: Data conversion utilitiesreact-native: Core platform functionality
Related Documentation
BTProvider: React provider componentDevice Services: Core service implementationsTypes: TypeScript type definitions