A simple JavaScript implementation for connecting to Concept2 PM5 rowing machines via Web Bluetooth API. This project demonstrates how to:
- Discover and connect to PM5 devices
- Read device information
- Subscribe to real-time workout data
- Parse telemetry data (distance, time, pace, stroke data, etc.)
- Send control commands to the PM5
- Device Discovery: Scan for PM5 devices using Web Bluetooth
- Connection Management: Connect/disconnect with automatic reconnection handling
- Device Information: Read model, serial number, firmware version, etc.
- General Status: Elapsed time, distance, workout state, rowing state
- Additional Status: Speed, stroke rate, heart rate, pace
- Stroke Data: Drive length, force, work per stroke, stroke count
- Split Data: Split times, distances, and interval information
- Multiplexed Data: Multiple data types in single notifications
js-pm5/
├── src/
│ ├── constants.js # PM5 UUIDs and state definitions
│ ├── parsers.js # Data parsing utilities
│ ├── device.js # PM5 device class (simplified)
│ └── index.js # Main demo application
├── index.html # Demo web interface
├── server.js # Local development server
├── package.json # NPM configuration
└── README.md # This file
- Modern Browser: Chrome, Edge, or Opera with Web Bluetooth support
- HTTPS: Required for Web Bluetooth (see testing notes below)
- PM5 Device: Concept2 rowing machine or SkiErg with Bluetooth enabled
-
Install Dependencies:
cd js-pm5-examples npm install -
Start Development Server:
npm run dev
-
Open Browser: Navigate to
http://localhost:3000
- Use the local server for code development
- Web Bluetooth will be restricted due to HTTP
- Good for testing UI and code structure
Use ngrok for HTTPS access:
# Install ngrok if you haven't already
npm install -g ngrok
# Start the local server
npm run dev
# In another terminal, create HTTPS tunnel
ngrok http 3000Then use the HTTPS URL provided by ngrok.
For testing without HTTPS:
# macOS/Linux
google-chrome --enable-web-bluetooth-new-permissions-backend --ignore-certificate-errors
# Windows
chrome.exe --enable-web-bluetooth-new-permissions-backend --ignore-certificate-errors- Click "Connect to PM5": Browser will show device selection dialog
- Select Your PM5: Choose your rowing machine from the list
- Device Information: View device details once connected
- Start Data Stream: Begin receiving real-time workout data
To integrate this code into your app:
import { scanForPM5Devices, PM5Device } from './src/device.js';
// Connect to PM5
const bluetoothDevice = await scanForPM5Devices();
const pm5 = new PM5Device(bluetoothDevice);
// Set up event handlers
pm5.onWorkoutData = (data) => {
console.log('Workout data:', data);
// Update your app's UI with workout data
};
pm5.onStrokeData = (data) => {
console.log('Stroke data:', data);
// Handle stroke-by-stroke data
};
// Connect and start receiving data
await pm5.connect();
await pm5.startRowingDataNotifications();{
elapsed_time: 120.50, // seconds
distance: 485.2, // meters
workout_state: 1, // see WORKOUT_STATES
rowing_state: 1, // see ROWING_STATES
stroke_state: 2, // see STROKE_STATES
drag_factor: 115
}{
speed: 4.235, // m/s
stroke_rate: 24, // strokes per minute
heart_rate: 150, // beats per minute
current_pace: 125.5, // seconds per 500m
average_pace: 128.2 // seconds per 500m
}{
drive_length: 1.25, // meters
drive_time: 0.95, // seconds
stroke_distance: 8.5, // meters
peak_drive_force: 485.2, // newtons
average_drive_force: 312.8, // newtons
work_per_stroke: 285.5, // joules
stroke_count: 48
}| Browser | Support | Notes |
|---|---|---|
| Chrome 56+ | ✅ Full | Best support |
| Edge 79+ | ✅ Full | Chromium-based |
| Opera 43+ | ✅ Full | Chromium-based |
| Firefox | ❌ None | No Web Bluetooth support |
| Safari | ❌ None | No Web Bluetooth support |
0: Waiting to Begin1: Workout Row2: Countdown Pause3: Interval Rest4: Work Time Interval5: End of Workout7: Manual Row
0: Inactive1: Active
0: Waiting for Wheel to Reach Min Speed1: Waiting for Wheel to Accelerate2: Driving3: Dwelling After Drive4: Recovering
-
Enable PM5 Bluetooth:
- Go to Main Menu → More Options → Turn Wireless On
- PM5 should show "Bluetooth Smart Ready"
-
Start the Demo:
npm run dev # Use ngrok for HTTPS access ngrok http 3000 -
Test Connection:
- Open the ngrok HTTPS URL in Chrome
- Click "Connect to PM5"
- Select your PM5 from the list
- Device info should populate
-
Test Data Streaming:
- Click "Start Data Stream"
- Begin rowing on the machine
- Watch real-time data updates in the browser
For development without hardware:
-
Code Structure Testing:
- All modules should load without errors
- UI should be responsive and functional
- Button states should update correctly
-
Error Handling:
- Test with Bluetooth disabled
- Test connection timeouts
- Test with unsupported browsers
- Bluetooth Not Found: Ensure PM5 Bluetooth is enabled
- No Data: Check that notifications are started
- Permission Denied: Use HTTPS or Chrome flags
- Module Errors: Ensure you're using a server (not file://)
- CORS Errors: Use the provided Express server
- Import Errors: Check that all files are in correct paths
- Device Not Found: Check PM5 is powered on and Bluetooth enabled
- Disconnects Frequently: Ensure PM5 firmware is up to date
- No Workout Data: Start a workout on the PM5 first
- Notification Frequency: PM5 sends data at ~50Hz, consider throttling UI updates
- Memory Usage: Clear old data regularly for long workouts
- Battery Impact: Bluetooth notifications consume device battery
- HTTPS Required: Web Bluetooth only works over secure connections
- User Gesture: Connection must be initiated by user interaction
- Permissions: Browser will request permission for each device