Skip to main content

Troubleshooting & FAQ

Comprehensive troubleshooting guide for the BT Management module, covering common issues, debugging techniques, and frequently asked questions.

Overview​

This guide provides solutions to common problems encountered when using the BT Management module, debugging techniques for development, and answers to frequently asked questions from the developer community.

Common Issues & Solutions​

Device Discovery Issues​

Problem: Devices Not Found During Scanning​

Symptoms:

  • No devices appear in scan results
  • onDeviceFound callback is never called
  • Scanning appears to start but finds nothing

Potential Causes & Solutions:

  1. Bluetooth Permissions

    // Check permissions
    import { PERMISSIONS, check, request } from "react-native-permissions";

    const checkBluetoothPermissions = async () => {
    const permissions = [
    PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
    PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
    PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
    PERMISSIONS.IOS.BLUETOOTH,
    ];

    for (const permission of permissions) {
    const result = await check(permission);
    if (result !== "granted") {
    await request(permission);
    }
    }
    };
  2. Bluetooth Adapter State

    const checkBluetoothState = async (bleManager: BleManager) => {
    const state = await bleManager.state();
    console.log("Bluetooth state:", state);

    if (state !== "PoweredOn") {
    await bleManager.enable();
    }
    };
  3. Device in Pairing Mode

    • Ensure medical devices are in pairing/advertising mode
    • Check device-specific pairing procedures
    • Verify device battery level
  4. Signal Range and Interference

    • Move closer to the device (within 10 meters)
    • Remove potential interference sources
    • Try in a different location

Debugging Steps:

// Enable detailed logging
const debugScanning = (scanManager: ScanManagerImplementation) => {
scanManager.subscribeToScanEvents(
(device) => {
console.log("Device found:", {
name: device.deviceData.name,
id: device.deviceData.id,
localName: device.deviceData.localName,
rssi: device.rssi,
});
},
(error) => {
console.error("Scan error details:", {
errorCode: error.errorCode,
message: error.message,
reason: error.reason,
});
},
);
};

Problem: Wrong Device Type Detected​

Symptoms:

  • Device found but incorrect type assigned
  • Unsupported device error when connecting
  • Device behavior doesn't match expected type

Solutions:

  1. Check Device Name Patterns

    // Verify device name patterns in ScanManagerImplementation
    const integratedDevicesMap = new Map([
    [/^F4$/, IntegratedDevices.F4], // Exact match
    [/^BP2 \d{4}$/, IntegratedDevices.BP2], // Pattern with numbers
    [/^Oxyfit \d{4}$/, IntegratedDevices.SPO2], // Pattern matching
    [/^AOJ-20A$/, IntegratedDevices.AOJ20A], // Exact match with hyphens
    ]);

    // Debug device name matching
    const debugDeviceMatching = (deviceName: string) => {
    console.log("Checking device name:", deviceName);

    for (const [pattern, deviceType] of integratedDevicesMap) {
    if (pattern.test(deviceName)) {
    console.log("Matched pattern:", pattern, "Type:", deviceType);
    return deviceType;
    }
    }

    console.log("No pattern matched for:", deviceName);
    return undefined;
    };
  2. Update Device Patterns

    • Add new device name patterns to the map
    • Handle firmware version differences
    • Account for regional naming variations

Connection Issues​

Problem: Device Connection Timeout​

Symptoms:

  • Connection hangs indefinitely
  • connect() never resolves
  • No error or success callback

Solutions:

  1. Implement Connection Timeout

    const connectWithTimeout = async (
    device: IBaseDevice,
    timeout: number = 30000,
    ): Promise<void> => {
    return Promise.race([
    device.connect(),
    new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error("Connection timeout")), timeout),
    ),
    ]);
    };

    // Usage
    try {
    await connectWithTimeout(device, 30000);
    } catch (error) {
    if (error.message === "Connection timeout") {
    console.log("Connection timed out, retrying...");
    // Implement retry logic
    }
    }
  2. Check Device State Before Connection

    const safeConnect = async (device: IBaseDevice) => {
    // Check if device is already connected
    const isConnected = DeviceManager.connectedDevices.has(device.deviceName);
    if (isConnected) {
    console.log("Device already connected");
    return;
    }

    // Check device availability
    const bleDevice = await device.device.isConnected();
    if (bleDevice) {
    console.log("BLE device already connected");
    return;
    }

    await device.connect();
    };

Problem: Connection Drops Frequently​

Symptoms:

  • Device connects but disconnects shortly after
  • Measurements interrupted mid-process
  • Unstable connection status

Solutions:

  1. Improve Connection Stability

    const maintainConnection = (device: IBaseDevice) => {
    // Implement connection keep-alive
    const keepAlive = setInterval(async () => {
    try {
    const isConnected = await device.device.isConnected();
    if (!isConnected) {
    console.log("Connection lost, attempting reconnection...");
    await device.connect();
    }
    } catch (error) {
    console.error("Keep-alive check failed:", error);
    }
    }, 30000); // Check every 30 seconds

    // Cleanup on disconnect
    device.device.onDisconnected(() => {
    clearInterval(keepAlive);
    });
    };
  2. Optimize Connection Parameters

    // Adjust connection parameters for stability
    const optimizeConnection = async (device: Device) => {
    // Request faster connection interval for critical devices
    await device.requestConnectionPriority(ConnectionPriority.High);

    // Set MTU size for better throughput
    await device.requestMTU(185);
    };
  3. Handle Environmental Factors

    • Minimize distance between device and phone
    • Avoid areas with WiFi interference
    • Ensure device has sufficient battery
    • Keep device stationary during connection

Data Processing Issues​

Problem: Measurement Data Parsing Errors​

Symptoms:

  • Incorrect measurement values
  • Parsing exceptions thrown
  • Missing measurement fields

Solutions:

  1. Debug Raw Data

    const debugDataParsing = (
    hexMessage: string[],
    parsingCase: ParsingCase,
    ) => {
    console.log("=== DATA PARSING DEBUG ===");
    console.log("Raw hex message:", hexMessage);
    console.log("Message length:", hexMessage.length);
    console.log("Parsing case:", parsingCase.caseValue);
    console.log("Case type:", parsingCase.caseType);

    parsingCase.parseDefinitions.forEach((def, index) => {
    const rawValue = DataParser.getFullValueForMeasurementProperty(
    hexMessage,
    def,
    );
    console.log(`Field ${index} (${def.name}):`);
    console.log(" Byte indexes:", def.byteIndexes);
    console.log(" Raw value:", rawValue);
    console.log(" Parse type:", def.parseType);

    if (def.parseType === "custom" && def.customParserDefinition) {
    try {
    const parsed = def.customParserDefinition(rawValue);
    console.log(" Parsed value:", parsed);
    } catch (error) {
    console.error(" Parse error:", error);
    }
    } else {
    const parsed = parseInt(rawValue, def.parseReference || 10);
    console.log(" Parsed value:", parsed);
    }
    });
    };
  2. Validate Message Integrity

    const validateMessage = (
    hexMessage: string[],
    expectedLength?: number,
    ): boolean => {
    // Check message length
    if (expectedLength && hexMessage.length !== expectedLength) {
    console.warn(
    `Message length mismatch. Expected: ${expectedLength}, Got: ${hexMessage.length}`,
    );
    return false;
    }

    // Check for null/undefined values
    const hasInvalidValues = hexMessage.some(
    (value) => !value || value.length !== 2,
    );
    if (hasInvalidValues) {
    console.warn("Message contains invalid hex values:", hexMessage);
    return false;
    }

    // Check hex format
    const hexPattern = /^[0-9A-Fa-f]{2}$/;
    const hasInvalidHex = hexMessage.some((value) => !hexPattern.test(value));
    if (hasInvalidHex) {
    console.warn("Message contains invalid hex format:", hexMessage);
    return false;
    }

    return true;
    };
  3. Handle Message Fragmentation

    // For devices with fragmented messages
    class DebugMessageCollector extends MessageCollector {
    collectMessage(
    partOfMessage: string[],
    onMessageCollected: (fullMessage: string[]) => void,
    parseMsgLength?: (msg: string[]) => number,
    ): void {
    console.log("Collecting message part:", partOfMessage);
    console.log("Current buffer length:", this.dataInHex.length);

    if (parseMsgLength && this.dataLength === undefined) {
    try {
    this.dataLength = parseMsgLength(this.dataInHex);
    console.log("Calculated message length:", this.dataLength);
    } catch (error) {
    console.error("Failed to calculate message length:", error);
    }
    }

    super.collectMessage(partOfMessage, onMessageCollected, parseMsgLength);
    }
    }

Problem: Custom Parser Functions Failing​

Symptoms:

  • Custom parser throws exceptions
  • Unexpected parsed values
  • Type conversion errors

Solutions:

  1. Add Error Handling to Custom Parsers

    const safeCustomParser = (originalParser: (input: string) => any) => {
    return (input: string) => {
    try {
    if (!input || input.length === 0) {
    console.warn("Empty input to custom parser");
    return 0; // or appropriate default
    }

    const result = originalParser(input);

    if (result === null || result === undefined || isNaN(result)) {
    console.warn("Invalid parser result:", result, "for input:", input);
    return 0; // or appropriate default
    }

    return result;
    } catch (error) {
    console.error("Custom parser error:", error, "for input:", input);
    return 0; // or appropriate default
    }
    };
    };

    // Usage in device configuration
    const parseDefinition: ParserDefinition = {
    name: "bodyWeight",
    parseType: "custom",
    byteIndexes: [5, 6, 7, 8],
    customParserDefinition: safeCustomParser(
    (a: string): number => parseInt(a, 16) / 100,
    ),
    };
  2. Validate Input Data

    const validateHexInput = (
    input: string,
    expectedLength?: number,
    ): boolean => {
    if (!input) return false;
    if (expectedLength && input.length !== expectedLength) return false;
    return /^[0-9A-Fa-f]+$/.test(input);
    };

    const robustHexParser = (input: string): number => {
    if (!validateHexInput(input)) {
    throw new Error(`Invalid hex input: ${input}`);
    }

    const parsed = parseInt(input, 16);
    if (isNaN(parsed)) {
    throw new Error(`Failed to parse hex: ${input}`);
    }

    return parsed;
    };

Performance Issues​

Problem: Slow Device Scanning​

Symptoms:

  • Long delays before devices are found
  • High CPU usage during scanning
  • App becomes unresponsive

Solutions:

  1. Optimize Scan Intervals

    // Reduce scan frequency for better performance
    class OptimizedScanManager extends ScanManagerImplementation {
    private scanOptimization = {
    baseInterval: 2000, // 2 seconds
    maxInterval: 30000, // 30 seconds
    backoffFactor: 1.5,
    };

    private currentInterval = this.scanOptimization.baseInterval;

    protected startOptimizedScanning(): void {
    // Use progressive scan intervals
    setTimeout(() => {
    this.stopScan();
    this.startScan();

    // Increase interval for next scan
    this.currentInterval = Math.min(
    this.currentInterval * this.scanOptimization.backoffFactor,
    this.scanOptimization.maxInterval,
    );

    this.scheduleNextScan();
    }, this.currentInterval);
    }

    private scheduleNextScan(): void {
    setTimeout(() => this.startOptimizedScanning(), this.currentInterval);
    }
    }
  2. Limit Concurrent Operations

    class PerformanceOptimizer {
    private maxConcurrentConnections = 3;
    private activeConnections = 0;
    private connectionQueue: Array<() => Promise<void>> = [];

    async queueConnection(connectFn: () => Promise<void>): Promise<void> {
    if (this.activeConnections < this.maxConcurrentConnections) {
    this.activeConnections++;
    try {
    await connectFn();
    } finally {
    this.activeConnections--;
    this.processQueue();
    }
    } else {
    return new Promise((resolve) => {
    this.connectionQueue.push(async () => {
    this.activeConnections++;
    try {
    await connectFn();
    resolve();
    } finally {
    this.activeConnections--;
    this.processQueue();
    }
    });
    });
    }
    }

    private processQueue(): void {
    if (
    this.connectionQueue.length > 0 &&
    this.activeConnections < this.maxConcurrentConnections
    ) {
    const nextConnection = this.connectionQueue.shift();
    if (nextConnection) {
    nextConnection();
    }
    }
    }
    }

Problem: Memory Leaks​

Symptoms:

  • App memory usage grows over time
  • Performance degrades after extended use
  • App crashes due to memory pressure

Solutions:

  1. Proper Subscription Cleanup

    const useDeviceSubscriptions = () => {
    const subscriptions = React.useRef<EmitterSubscription[]>([]);

    const addSubscription = (subscription: EmitterSubscription) => {
    subscriptions.current.push(subscription);
    };

    React.useEffect(() => {
    return () => {
    // Cleanup all subscriptions
    subscriptions.current.forEach((sub) => sub.remove());
    subscriptions.current = [];
    };
    }, []);

    return { addSubscription };
    };
  2. Timer and Interval Cleanup

    class TimerManager {
    private timers: Set<NodeJS.Timeout> = new Set();
    private intervals: Set<NodeJS.Timeout> = new Set();

    setTimeout(callback: () => void, delay: number): NodeJS.Timeout {
    const timer = setTimeout(() => {
    this.timers.delete(timer);
    callback();
    }, delay);

    this.timers.add(timer);
    return timer;
    }

    setInterval(callback: () => void, interval: number): NodeJS.Timeout {
    const intervalTimer = setInterval(callback, interval);
    this.intervals.add(intervalTimer);
    return intervalTimer;
    }

    cleanup(): void {
    this.timers.forEach((timer) => clearTimeout(timer));
    this.intervals.forEach((interval) => clearInterval(interval));
    this.timers.clear();
    this.intervals.clear();
    }
    }

Platform-Specific Issues​

iOS Specific Issues​

Problem: Bluetooth Permission Denied​

Solution:

import { PERMISSIONS, request, RESULTS } from "react-native-permissions";

const requestIOSBluetoothPermission = async () => {
const result = await request(PERMISSIONS.IOS.BLUETOOTH);

switch (result) {
case RESULTS.GRANTED:
console.log("Bluetooth permission granted");
break;
case RESULTS.DENIED:
console.log("Bluetooth permission denied");
// Show user guidance
break;
case RESULTS.BLOCKED:
console.log("Bluetooth permission blocked");
// Direct user to settings
break;
}
};

Problem: Background Scanning Limitations​

Solution:

// Configure background modes in Info.plist
const configureBackgroundBLE = {
// Add to Info.plist:
// <key>UIBackgroundModes</key>
// <array>
// <string>bluetooth-central</string>
// </array>

// Implement background task
backgroundTaskId: null as number | null,

startBackgroundTask(): void {
this.backgroundTaskId = BackgroundTask.start({
taskName: "BLE-Scanning",
taskKey: "ble-scan",
});
},

endBackgroundTask(): void {
if (this.backgroundTaskId !== null) {
BackgroundTask.finish(this.backgroundTaskId);
this.backgroundTaskId = null;
}
},
};

Android Specific Issues​

Problem: Location Permission Required​

Solution:

import { PERMISSIONS, request } from "react-native-permissions";

const requestAndroidLocationPermission = async () => {
// Android requires location permission for BLE scanning
const permissions = [
PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
];

for (const permission of permissions) {
const result = await request(permission);
if (result !== "granted") {
throw new Error(`Permission ${permission} not granted`);
}
}
};

Problem: Android 12+ Permission Changes​

Solution:

import { Platform } from "react-native";

const requestBluetoothPermissions = async () => {
if (Platform.OS === "android") {
const androidVersion = Platform.Version;

if (androidVersion >= 31) {
// Android 12+ requires new permissions
await request(PERMISSIONS.ANDROID.BLUETOOTH_SCAN);
await request(PERMISSIONS.ANDROID.BLUETOOTH_CONNECT);
} else {
// Older Android versions
await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION);
await request(PERMISSIONS.ANDROID.BLUETOOTH);
}
}
};

Debugging Tools & Techniques​

Debug Mode Configuration​

class DebugManager {
private static isDebugMode = __DEV__;
private static logLevel: "debug" | "info" | "warn" | "error" = "debug";

static log(level: string, message: string, data?: any): void {
if (!this.isDebugMode) return;

const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;

switch (level) {
case "debug":
if (this.logLevel === "debug") {
console.log(logMessage, data);
}
break;
case "info":
if (["debug", "info"].includes(this.logLevel)) {
console.info(logMessage, data);
}
break;
case "warn":
if (["debug", "info", "warn"].includes(this.logLevel)) {
console.warn(logMessage, data);
}
break;
case "error":
console.error(logMessage, data);
break;
}
}

static debugDevice(device: IBaseDevice): void {
this.log("debug", "Device Debug Info", {
deviceId: device.deviceId,
deviceName: device.deviceName,
deviceData: device.deviceData,
supportedMeasurements: device.deviceData.supportedMeasurementTypeKeys,
});
}

static debugMeasurement(measurement: Measurement, device: DeviceData): void {
this.log("debug", "Measurement Debug Info", {
measurementType: measurement.measurementTypeKey,
deviceName: device.name,
measurement,
timestamp: new Date(),
});
}
}

BLE Traffic Monitoring​

class BLETrafficMonitor {
private static traffic: BLETrafficLog[] = [];
private static maxLogSize = 1000;

static logTraffic(
direction: "in" | "out",
data: string[],
deviceId: string,
): void {
const logEntry: BLETrafficLog = {
timestamp: new Date(),
direction,
data,
deviceId,
dataLength: data.length,
};

this.traffic.push(logEntry);

// Maintain log size
if (this.traffic.length > this.maxLogSize) {
this.traffic.shift();
}

DebugManager.log(
"debug",
`BLE Traffic [${direction.toUpperCase()}]`,
logEntry,
);
}

static getTrafficForDevice(deviceId: string): BLETrafficLog[] {
return this.traffic.filter((entry) => entry.deviceId === deviceId);
}

static exportTrafficLog(): string {
return JSON.stringify(this.traffic, null, 2);
}

static clearTrafficLog(): void {
this.traffic = [];
}
}

Device State Monitoring​

class DeviceStateMonitor {
private static deviceStates = new Map<string, DeviceStateLog>();

static updateDeviceState(
deviceId: string,
state: DeviceStatus,
additionalInfo?: any,
): void {
const currentState = this.deviceStates.get(deviceId) || {
deviceId,
stateHistory: [],
currentState: DeviceStatus.DISCONNECTED,
lastUpdate: new Date(),
};

currentState.stateHistory.push({
state,
timestamp: new Date(),
additionalInfo,
});

currentState.currentState = state;
currentState.lastUpdate = new Date();

this.deviceStates.set(deviceId, currentState);

DebugManager.log("debug", "Device State Change", {
deviceId,
newState: state,
additionalInfo,
});
}

static getDeviceState(deviceId: string): DeviceStateLog | undefined {
return this.deviceStates.get(deviceId);
}

static getAllDeviceStates(): Map<string, DeviceStateLog> {
return new Map(this.deviceStates);
}
}

Frequently Asked Questions​

General Questions​

Q: How many devices can be connected simultaneously?

A: The BT Management module supports connecting to multiple devices simultaneously, with practical limits depending on the platform:

  • iOS: 8-10 active connections
  • Android: 6-8 active connections
  • Recommended: 3-5 active connections for optimal performance

Q: What happens if the app goes to background during measurement?

A: The module handles background scenarios gracefully:

  • Active measurements continue if background BLE is enabled
  • Connection state is preserved
  • Measurements are queued and processed when app returns to foreground
  • Configure background modes in your app for best results

Q: How is measurement data validated?

A: Multiple validation layers ensure data integrity:

  • Protocol-level validation during parsing
  • Range validation based on device specifications
  • Checksum verification where supported
  • Clinical validation rules can be applied

Integration Questions​

Q: Can I use this with existing React Navigation?

A: Yes, the module integrates seamlessly with React Navigation:

import { useFocusEffect } from '@react-navigation/native';

// Pass the hook to BTProvider
<BTProvider
useFocusEffect={useFocusEffect}
// ... other props
>

Q: How do I handle device-specific configurations?

A: Device configurations are defined in the medical-devices constants file. To add a new device:

  1. Add device pattern to the scan manager
  2. Create device class extending BaseDevice
  3. Define device configuration with BLE services and parsing rules
  4. Add device to DeviceCreator factory

Q: Can I customize the data parsing for existing devices?

A: Yes, you can customize parsing by:

  • Extending existing device classes
  • Overriding processDataWithDeviceLogic method
  • Defining custom parser functions
  • Modifying parsing model configurations

Troubleshooting Questions​

Q: Why do some devices connect but never provide measurements?

A: Common causes include:

  • Incorrect parsing model configuration
  • Missing write commands to start measurement
  • Device in wrong mode (not measuring)
  • BLE characteristic subscription issues

Debug by:

  1. Checking raw BLE data
  2. Verifying write commands are sent
  3. Confirming device is in measurement mode
  4. Validating parsing logic

Q: How do I handle devices that disconnect frequently?

A: Implement connection stability measures:

// Monitor connection quality
const monitorConnection = (device: IBaseDevice) => {
let disconnectCount = 0;

device.device.onDisconnected(() => {
disconnectCount++;

if (disconnectCount > 3) {
// Device may have hardware issues
console.warn("Frequent disconnections detected");
} else {
// Attempt reconnection
setTimeout(() => device.connect(), 2000);
}
});
};

Q: What should I do when measurements seem incorrect?

A: Validate measurements systematically:

  1. Check raw hex data format
  2. Verify parsing case selection
  3. Test custom parser functions
  4. Compare with device manual/specifications
  5. Validate against known good measurements

Performance Questions​

Q: How can I optimize scanning performance?

A: Several optimization strategies:

  • Use progressive scan intervals
  • Limit concurrent connections
  • Implement device filtering early
  • Stop scanning during connections
  • Use device caching where appropriate

Q: How much memory does the module use?

A: Memory usage depends on:

  • Number of connected devices (≈ 1-2MB per device)
  • Measurement history retention
  • Stream data buffering
  • ECG data processing (can be significant)

Monitor memory usage and implement cleanup strategies for long-running applications.

Q: Can I process measurements in a background thread?

A: Yes, for intensive processing:

// Use Web Workers for heavy computation
const processInBackground = async (measurement: Measurement) => {
const worker = new Worker("/workers/measurement-processor.js");

return new Promise((resolve) => {
worker.postMessage({ measurement });
worker.onmessage = (event) => {
resolve(event.data.result);
worker.terminate();
};
});
};

Support & Community​

Getting Help​

  1. Check this documentation for common solutions
  2. Enable debug logging to understand issues
  3. Test with known devices to isolate problems
  4. Check device specifications for protocol details
  5. Review error logs for specific error patterns

Reporting Issues​

When reporting issues, include:

  • Device type and model
  • Platform (iOS/Android version)
  • App version and BT Management module version
  • Debug logs and error messages
  • Steps to reproduce
  • Expected vs actual behavior

Contributing​

The BT Management module welcomes contributions:

  • Device support for new medical devices
  • Performance optimizations
  • Bug fixes and improvements
  • Documentation enhancements
  • Test coverage improvements

Dependencies​

  • react-native-ble-plx: BLE communication
  • react-native-permissions: Permission management
  • @ovok/core: Core measurement types
  • react-native: Core platform functionality