SkillAgentSearch skills...

ErgometerJS

Java script ergometer driver for concept2 performance monitor with BLE/USB .

Install / Use

/learn @tijmenvangulik/ErgometerJS

README

Introduction

Java script ergometer driver for concept 2 performance monitor with BLE. (The PM5) Works on all major platforms using cordova and node/electron",

I hope this project will become a shared effort to make it easier to write next generation ergometer software.

Tijmen Tijmen@vangulik

Project features

  • The project is open source and and it is based on open source project. (appache 2 license)

  • Uses low power bluetooth (BLE) connection (only PM5).

  • Usb (hid) support for PM3/4/5

  • Written in typescript which is compiled to javascript. You can use the driver without typescript.

  • Platform independent (mobile / desktop / web ) I have not yet been able to test all platforms but it should work on:

    • Mobile using cordova: iOS,android, Windows
    • Mobile react native: iOS,android
    • Desktop using Electron MacOS X, Windows, Linux
    • Server using Node MacOS X, Windows, Linux (inc Raspberry PI)
    • Web: chromium based browsers

Basically ErgometerJS needs javascript and a blue tooth driver which can be (noble,cordova-plugin-ble or web bluetooth)

  • API definitions can be found in a separate typescript definition file (ergometer.d.ts).

    http://www.concept2.com/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf

Change Log

View change log

Licenses

License text

Components

  • The project : Apache license 2.
  • Bleat : Mit license
  • Electron: Mit license

platforms

| | pm3-5 usb | Bluetooth | |------------|-------------|------------|
|Web | yes ** | yes ** | |Cordova | android | yes | |Electron | yes | yes | |React native| | * |

  • the demo contains an limeted proof of concept, there are other libraries which have better suport. it is not difficult to support other usb/ble drivers ( react-native-ble-plx may be an better option) ** chromium based browsers

Todo

  • usb ios support
  • Add more commands

Known problems

  • There are problems in the PM5 BLE firmware. Some csafe commands give back invalid responses. I hope concept2 will it. See

http://www.c2forum.com/viewtopic.php?f=15&t=93321

  • React native: the used blue tooth library does not (yet?) support direct reading and writing to characteristics. due to this the csafe commands and the power curve do not work.

Installation

To make it work you need:

  • An concept2 ergometer with PM5
  • An PC or Mac, android or iphone with BLE capability.
  • npm which can be downloaded from https://www.npmjs.com (for the electron usb demo you can use an older PM3 device)

Do a download or checkout from github (https://github.com/tijmenvangulik/ErgometerJS)

open a command prompt on the downloaded directory and type

npm install

to build the driver and the demo from the original typescript source. (not required they are already build)

npm run build

after the build the driver will be located in

driver\lib\ergometer.js

the description of the interface can be viewed as type script definition file. (this is generated from the source)

driver\lib\ergometer.d.ts

To use the library you need all the files in the lib directory and include it in your cordova phone gab app

<script src="libs/ergometer.js"></script>
<script src="libs/jquery/jquery.js"></script>

npm

You can also install ergometer-js as package using npm with the command:

  npm install ergometer-js

This npm package only includes the files for web usage for nodejs third party libraries are required

Usage for Bluetooth (BLE)

Create this class to acCess the performance data

var performanceMonitor= new ergometer.PerformanceMonitorBle();

After this connect to the events to get data

performanceMonitor.rowingGeneralStatusEvent.sub(this,this.onRowingGeneralStatus);

On some android phones you can connect to a limited number of events. Use the multiplex property to overcome
this problem. When the multi plex mode is switched on the data send to the device can be a a bit different, see
the documentation in the properties You must set the multiplex property before connecting

performanceMonitor.multiplex=true;

to start the connection first start scanning for a device,
you should call when the cordova deviceready event is called (or later)

performanceMonitor.startScan((device : ergometer.DeviceInfo) : boolean => {                                        
  //return true when you want to connect to the device                                                            
   return device.name=='My device name';                                                                         
});  

to connect at at a later time

performanceMonitor.connectToDevice('my device name');

the devices which where found during the scan are collected in

performanceMonitor.devices   

when you connect to a device the scan is stopped, when you want to stop the scan earlier you need to call

performanceMonitor.stopScan 

More information can be found in the typescript definitions:

ergometer.d.ts

CSafe

CSafe is used to send and receive commands. I have implemented an jquery like api which is:

  • chainable (not required)
  • Extensible (you add your own commands to the buffer object. A command can consist out of multiple commands)
  • type safe
  • multiple commands can be send in one requests to reduce the load

An example of a

when the connection state is ready for communcation you can start with csafe commands

protected onConnectionStateChanged(oldState : ergometer.MonitorConnectionState, newState : ergometer.MonitorConnectionState) {  
        if (newState==ergometer.MonitorConnectionState.readyForCommunication) {

The csafeBuffer property is used to prepare one or multiple commands. Before adding commands you have to clear the buffer. At then end call send to call the buffer. The next command can only be send after that the first command is send. Use the optional success and error parameters of the send function to start with the next command. You can also send the next command when data is received.

this.performanceMonitor.newCsafeBuffer()
            .getStrokeState({
                received: (strokeState : ergometer.StrokeState) =>{
                    this.showData(`stroke state: ${strokeState}`);
                }
            })
            .getVersion({
                received: (version : ergometer.csafe.IVersion)=> {
                    this.showData(`Version hardware ${version.HardwareVersion} software:${version.FirmwareVersion}`);
                }
            })
            .setProgram({program:2})
            .send();

It is not required to chain the commands. You can also write code the classic way:

var buffer=this.performanceMonitor.newCsafeBuffer();
buffer.setProgram({program:2}); 
buffer.send();

It is possible to add new commands to the command buffer. for this you have to call commandManager.register to register your new command. You have to pass on a function because the actual declaration is deferred to a later state.

There is one required command property where you define the main command. Some long commands like the configuration command have a second detail command. You can specify this in the detailCommand property. You do not have to set the start,stop,crc check,length bytes in the cdafe commands these values are automaticly calculated. (except when there is an additional length in the data of a command, like the power curve)

export interface ICommandStrokeState  {
    received : (state : StrokeState )=>void;
    onError? : ErrorHandler;
}
export interface IBuffer {
    getStrokeState(params : ICommandStrokeState) : IBuffer;
}

commandManager.register( (buffer : IBuffer,monitor : PerformanceMonitor) =>{
    buffer.getStrokeState= function (params : ICommandStrokeState) : IBuffer {
        buffer.addRawCommand({
            waitForResponse:true,
            command : csafe.defs.LONG_CFG_CMDS.SETUSERCFG1_CMD,
            detailCommand: csafe.defs.PM_SHORT_PULL_DATA_CMDS.PM_GET_STROKESTATE,
            onDataReceived : (data : DataView)=>{
                if (params.received) params.received(data.getUint8(0))
            },
            onError:params.onError
        });
        return buffer;
    }
})

There are many commands, I have not yet found time to add all the commands. If you added new ones please commit them to github. When you not care about writing a user friendly command wrapper you can always send raw commands. For example

this.performanceMonitor.newCsafeBuffer()
    .addRawCommand({
                    waitForRe

Related Skills

View on GitHub
GitHub Stars126
CategoryDevelopment
Updated8d ago
Forks37

Languages

JavaScript

Security Score

85/100

Audited on Mar 23, 2026

No findings