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
onDeviceFoundcallback is never called- Scanning appears to start but finds nothing
Potential Causes & Solutions:
-
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);
}
}
}; -
Bluetooth Adapter State
const checkBluetoothState = async (bleManager: BleManager) => {
const state = await bleManager.state();
console.log("Bluetooth state:", state);
if (state !== "PoweredOn") {
await bleManager.enable();
}
}; -
Device in Pairing Mode
- Ensure medical devices are in pairing/advertising mode
- Check device-specific pairing procedures
- Verify device battery level
-
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:
-
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;
}; -
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:
-
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
}
} -
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:
-
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);
});
}; -
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);
}; -
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:
-
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);
}
});
}; -
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;
}; -
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:
-
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,
),
}; -
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:
-
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);
}
} -
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:
-
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 };
}; -
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:
- Add device pattern to the scan manager
- Create device class extending BaseDevice
- Define device configuration with BLE services and parsing rules
- 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
processDataWithDeviceLogicmethod - 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:
- Checking raw BLE data
- Verifying write commands are sent
- Confirming device is in measurement mode
- 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:
- Check raw hex data format
- Verify parsing case selection
- Test custom parser functions
- Compare with device manual/specifications
- 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
- Check this documentation for common solutions
- Enable debug logging to understand issues
- Test with known devices to isolate problems
- Check device specifications for protocol details
- 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 communicationreact-native-permissions: Permission management@ovok/core: Core measurement typesreact-native: Core platform functionality
Related Documentation
BTProvider: React provider componentDevice Services: Core service implementationsDevice Types: Medical device configurationsAdvanced Usage: Advanced integration patternsTypes: TypeScript type definitions