{"id":51262447,"url":"https://github.com/tijmenvangulik/ergometerjs","last_synced_at":"2026-06-29T13:01:24.362Z","repository":{"id":55136918,"uuid":"49224213","full_name":"tijmenvangulik/ErgometerJS","owner":"tijmenvangulik","description":"Java script ergometer driver for concept2 performance monitor with BLE/USB .","archived":false,"fork":false,"pushed_at":"2026-03-22T19:05:18.000Z","size":14458,"stargazers_count":126,"open_issues_count":0,"forks_count":37,"subscribers_count":19,"default_branch":"master","last_synced_at":"2026-03-23T04:06:28.406Z","etag":null,"topics":["android","ble","bluetooth-low-energy","concept2","ergometer","ios","javascript","mac","node","pc","pm3","pm5","rowing","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tijmenvangulik.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-01-07T19:02:25.000Z","updated_at":"2026-03-22T19:05:21.000Z","dependencies_parsed_at":"2024-04-14T13:48:44.381Z","dependency_job_id":null,"html_url":"https://github.com/tijmenvangulik/ErgometerJS","commit_stats":null,"previous_names":["tijmenvangulik/mobileergometer"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tijmenvangulik/ErgometerJS","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tijmenvangulik%2FErgometerJS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tijmenvangulik%2FErgometerJS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tijmenvangulik%2FErgometerJS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tijmenvangulik%2FErgometerJS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tijmenvangulik","download_url":"https://codeload.github.com/tijmenvangulik/ErgometerJS/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tijmenvangulik%2FErgometerJS/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34927687,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","ble","bluetooth-low-energy","concept2","ergometer","ios","javascript","mac","node","pc","pm3","pm5","rowing","typescript"],"created_at":"2026-06-29T13:01:22.027Z","updated_at":"2026-06-29T13:01:24.292Z","avatar_url":"https://github.com/tijmenvangulik.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nJava script ergometer driver for concept 2 performance monitor with BLE. \n(The PM5) Works on all major platforms using cordova and node/electron\",\n\n\nI hope this project will become a shared effort to make it easier to write next generation ergometer software.\n\nTijmen \nTijmen@vangulik\n\n# Project features\n\n* The project is open source and and it is based on open source project. (appache 2 license) \n* Uses low power bluetooth (BLE) connection (only PM5).\n* Usb (hid) support for PM3/4/5\n* Written in typescript which is compiled to javascript. You can use the driver without typescript.\n* Platform independent (mobile / desktop / web ) I have not yet been able to test all platforms but it should work on:\n  \n  * Mobile using cordova: iOS,android, Windows \n  * Mobile react native: iOS,android   \n  * Desktop using Electron  MacOS X, Windows, Linux\n  * Server using Node MacOS X, Windows, Linux (inc Raspberry PI)\n  * Web: chromium based browsers\n\nBasically ErgometerJS needs javascript and a blue tooth driver which can be (noble,cordova-plugin-ble or web bluetooth)\n\n* API definitions can be found in a separate typescript definition file (ergometer.d.ts).  \n\n    http://www.concept2.com/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf\n\n# Change Log\n\n[View change log](ChangeLog.md)\n\n# Licenses\n\n[License text](LICENSE.txt)\n\nComponents\n\n- The project : Apache license 2.\n- Bleat : Mit license\n- Electron: Mit license\n\n# platforms\n\n|            | pm3-5 usb   | Bluetooth  |\n|------------|-------------|------------|    \n|Web         | yes **      | yes **     |\n|Cordova     | android     | yes        |\n|Electron    | yes         | yes        |\n|React native|             | *          |\n\n* the demo contains an limeted proof of concept, there are other libraries\n which have better suport. it is not difficult to support other usb/ble drivers ( react-native-ble-plx may be an better option)\n** chromium based browsers\n\n# Todo\n* usb ios support\n* Add more commands\n\n# Known problems\n                  \n* There are problems in the PM5 BLE firmware. Some csafe commands give back invalid responses. \nI hope concept2 will it. See\n\nhttp://www.c2forum.com/viewtopic.php?f=15\u0026t=93321\n\n* React native: the used blue tooth library does not (yet?) support direct reading and writing to characteristics.\ndue to this the csafe commands and the power curve do not work.\n\n# Installation\n\nTo make it work you need:\n\n* An concept2 ergometer with PM5\n* An PC or Mac, android or iphone with BLE capability.\n* npm which can be downloaded from https://www.npmjs.com\n(for the electron usb demo you can use an older PM3 device)\n\nDo a download or checkout from github (https://github.com/tijmenvangulik/ErgometerJS)\n\n\nopen a command prompt on the downloaded directory and type\n\n```sh\nnpm install\n```\n\nto build the driver and the demo from the original typescript source. (not required they are already build)\n\n```sh\nnpm run build\n```\n\nafter the build the driver will be located in\n\n```\ndriver\\lib\\ergometer.js\n```\n\nthe description of the interface can be viewed as type script definition file. (this is generated from the source)\n\n```\ndriver\\lib\\ergometer.d.ts\n```\n\nTo use the library you need all the files in the lib directory and include it in your cordova phone gab app\n\n```html\n\u003cscript src=\"libs/ergometer.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"libs/jquery/jquery.js\"\u003e\u003c/script\u003e\n```\n# npm\n\nYou can also install ergometer-js as package using npm with the command:\n```sh\n  npm install ergometer-js\n```\nThis npm package only includes the files for web usage for nodejs third party libraries are required\n\n# Usage for Bluetooth (BLE)                                                                                                                                                                                                                            \nCreate this class to acCess the performance data\n                                                                     \n```ts\nvar performanceMonitor= new ergometer.PerformanceMonitorBle();\n```\n                                                                                                                 \nAfter this connect to the events to get data\n                                                                        \n```ts\nperformanceMonitor.rowingGeneralStatusEvent.sub(this,this.onRowingGeneralStatus);\n```\n\nOn some android phones you can connect to a limited number of events. Use the multiplex property to overcome        \nthis problem. When the multi plex mode is switched on the data send to the device can be a a bit different, see     \nthe documentation in the properties You must set the multiplex property before connecting                          \n\n```ts\nperformanceMonitor.multiplex=true;\n```\n                                                                                                                 \nto start the connection first start scanning for a device,                                                           \nyou should call when the cordova deviceready event is called (or later)  \n                                           \n```ts\nperformanceMonitor.startScan((device : ergometer.DeviceInfo) : boolean =\u003e {                                        \n  //return true when you want to connect to the device                                                            \n   return device.name=='My device name';                                                                         \n});  \n```\n                                                                                                                 \nto connect at at a later time \n                                                                                     \n```ts\nperformanceMonitor.connectToDevice('my device name');\n```\n                                                           \nthe devices which where found during the scan are collected in\n                                                     \n```ts\nperformanceMonitor.devices   \n```\n                                                                                        \nwhen you connect to a device the scan is stopped, when you want to stop the scan earlier you need to call \n         \n```ts\nperformanceMonitor.stopScan \n```\n    \nMore information can be found in the typescript definitions:\n    \n[ergometer.d.ts](https://github.com/tijmenvangulik/MobileErgometer/blob/master/api/lib/ergometer.d.ts)\n    \n## CSafe\n\nCSafe is used to send and receive commands. I have implemented an jquery like api which is:\n- chainable (not required)\n- Extensible (you add your own commands to the buffer object. A command can consist out of multiple commands)\n- type safe\n- multiple commands can be send in one requests to reduce the load\n\nAn example of a\n\nwhen the connection state is ready for communcation you can start with csafe commands\n\n```ts\nprotected onConnectionStateChanged(oldState : ergometer.MonitorConnectionState, newState : ergometer.MonitorConnectionState) {  \n        if (newState==ergometer.MonitorConnectionState.readyForCommunication) {\n```\n\nThe csafeBuffer property is used to prepare one or multiple commands. Before adding commands you have\nto clear the buffer. At then end call send to call the buffer. \nThe next command can only be send after that the first command is send. Use the optional \nsuccess and error parameters of the send function to start with the next command. You can also send the\nnext command when data is received.\n\n```ts\nthis.performanceMonitor.newCsafeBuffer()\n            .getStrokeState({\n                received: (strokeState : ergometer.StrokeState) =\u003e{\n                    this.showData(`stroke state: ${strokeState}`);\n                }\n            })\n            .getVersion({\n                received: (version : ergometer.csafe.IVersion)=\u003e {\n                    this.showData(`Version hardware ${version.HardwareVersion} software:${version.FirmwareVersion}`);\n                }\n            })\n            .setProgram({program:2})\n            .send();\n```\n\nIt is not required to chain the commands. You can also write code the classic way:\n\n```ts\nvar buffer=this.performanceMonitor.newCsafeBuffer();\nbuffer.setProgram({program:2}); \nbuffer.send();\n```\n    \n\nIt is possible to add new commands to the command buffer. \nfor this you have to call commandManager.register to register your new command.\nYou have to pass on a function because the actual declaration is deferred to a later state.\n\nThere is one required command property where you define the main command. Some long commands like\nthe configuration command have a second detail command. You can specify this in the detailCommand property.\nYou do not have to set the start,stop,crc check,length bytes in the cdafe commands these values are automaticly\ncalculated. (except when there is an additional length in the data of a command, like the power curve)\n\n```ts\nexport interface ICommandStrokeState  {\n    received : (state : StrokeState )=\u003evoid;\n    onError? : ErrorHandler;\n}\nexport interface IBuffer {\n    getStrokeState(params : ICommandStrokeState) : IBuffer;\n}\n\ncommandManager.register( (buffer : IBuffer,monitor : PerformanceMonitor) =\u003e{\n    buffer.getStrokeState= function (params : ICommandStrokeState) : IBuffer {\n        buffer.addRawCommand({\n            waitForResponse:true,\n            command : csafe.defs.LONG_CFG_CMDS.SETUSERCFG1_CMD,\n            detailCommand: csafe.defs.PM_SHORT_PULL_DATA_CMDS.PM_GET_STROKESTATE,\n            onDataReceived : (data : DataView)=\u003e{\n                if (params.received) params.received(data.getUint8(0))\n            },\n            onError:params.onError\n        });\n        return buffer;\n    }\n})\n```\n    \nThere are many commands, I have not yet found time to add all the commands. If you added new ones\nplease commit them to github. When you not care about writing a user friendly command wrapper you can\nalways send raw commands. For example\n\n```ts\nthis.performanceMonitor.newCsafeBuffer()\n    .addRawCommand({\n                    waitForResponse:true,\n                    command : csafe.defs.LONG_CFG_CMDS.SETUSERCFG1_CMD,\n                    detailCommand: csafe.defs.PM_SHORT_PULL_DATA_CMDS.PM_GET_STROKESTATE,\n                    onDataReceived : (data : DataView)=\u003e{\n                        alert(data.getUint8(0));\n                    }\n                })\n    .send(); \n    .then(()=\u003e{  //send returns a promise\n       console.log(\"send done, you can send th next\")\n     }); \n```\n\nCommand merging\nLong config commands can be merged into one command for efficency when they are directly after each other in the buffer.\n\nWhen you set sortCommands to true the commands are sorted so they can be merged without caring about the the order in which you add the commands. \n\n### Creating workouts\n\n#### public commands\n\nsetProgram,setDistance,setWorkoutType, setScreenState and setConfigureWorkout are public and work fine for USB and bluetooth. \n\nWeb bluetooth has only a small packet size of 20 bytes, so we need to split the commands to make it work on the web.\n\n##### distance\n\nUse the following code to set a distance and go to the workout screen: \n\n```ts\nawait performanceMonitor.newCsafeBuffer()\n    .setDistance({value:3000,unit:ergometer.Unit.distanceMeter})\n    .setProgram({value:ergometer.Program.Programmed})\n    .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})\n    .send();\n```\n\n##### Configure JustRow\n```ts\n    await this.performanceMonitor.newCsafeBuffer()\n       .setWorkoutType({value: ergometer.WorkoutType.justRowSplits})\n       .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})\n       .send();\n```\n\n##### Configure 2000m/400m splits\n\n```ts\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutType({value: ergometer.WorkoutType.fixedDistanceSplits})\n    .setWorkoutDuration({value:2000,durationType:ergometer.WorkoutDurationType.distance})        \n    .send()\nawait this.performanceMonitor.newCsafeBuffer()\n    .setSplitDuration({value:400,durationType:ergometer.WorkoutDurationType.distance})\n    .setConfigureWorkout({programmingMode:true})\n    .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})        \n    .send()\n```\n\n#### proprietary commands\n\nThe next examples only work for blue tooth. For USB you need authentication to use these proprietary commands. Please contact Concept2 for information how to do the authentication. \n\n##### Configure fixe time\n \n20:00/4:00 splits, power goal of 100 watts\n\n```ts\nawait this.performanceMonitor.newCsafeBuffer()\n    .setWork({hour:0,minute:20,second:0})\n    .setProgram({value:ergometer.Program.Programmed})\n    .send();\nawait this.performanceMonitor.newCsafeBuffer()\n    .setSplitDuration({value:400,durationType:ergometer.WorkoutDurationType.distance})\n    .send();\nawait this.performanceMonitor.newCsafeBuffer()\n    .setPower({value:100,unit:ergometer.Unit.powerWatts})\n    .setProgram({value:ergometer.Program.Programmed})\n    .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})\n    .send();\n```\n\n##### Configure 20:00/4:00 splits\n\n```ts\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutType({value: ergometer.WorkoutType.fixedTimeSplits})\n    .setWorkoutDuration({value:20*60*100,durationType:ergometer.WorkoutDurationType.time})        \n    .send();\n\nawait performanceMonitor.newCsafeBuffer()\n    .setSplitDuration({value:4*60*100,durationType:ergometer.WorkoutDurationType.time})\n    .setConfigureWorkout({programmingMode:true})\n    .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})        \n    .send();\n```\n\n##### Configure Fixed Time Interval 2:00/:30 rest\n\n```ts\nawait performanceMonitor.newCsafeBuffer()\n   .setWorkoutType({value: ergometer.WorkoutType.fixedTimeInterval})\n   .setWorkoutDuration({value:2*60*100,durationType:ergometer.WorkoutDurationType.time})        \n   .send();\n   \nawait performanceMonitor.newCsafeBuffer()\n   .setRestDuration({value:30})\n   .setConfigureWorkout({programmingMode:true})\n   .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})                \n   .send();\n```\n\n##### Configure variable interval v500m/1:00r…4\n\nInterval 1: 500m/1:00r, target pace of 1:40\nInterval 2: 3:00/0:00r, target pace of 1:40\n\n```ts\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutIntervalCount({value:0}) //start set workout interval #1\n    .setWorkoutType({value: ergometer.WorkoutType.variableInterval})\n    .setIntervalType({value: ergometer.IntervalType.distance})\n    .send()\n\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutDuration({value:500,durationType:ergometer.WorkoutDurationType.distance})        \n    .setRestDuration({value:60})\n    .send();\n    \nawait performanceMonitor.newCsafeBuffer()\n    .setTargetPaceTime({value:(1*60+40)*100})\n    .setConfigureWorkout({programmingMode:true})\n    .send();\n\n//Interval 2: 3:00/0:00r, target pace of 1:40\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutIntervalCount({value:1}) //start set workout interval #2\n    .setIntervalType({value: ergometer.IntervalType.time})\n    .send()\n\nawait performanceMonitor.newCsafeBuffer()\n    .setWorkoutDuration({value:3*60*100,durationType:ergometer.WorkoutDurationType.time})        \n    .setRestDuration({value:0})\n    .send();\n\nawait this.performanceMonitor.newCsafeBuffer()\n    .setTargetPaceTime({value:(1*60+40)*100})\n    .setConfigureWorkout({programmingMode:true})\n    .send();\n\n//go to screen\nawait this.performanceMonitor.newCsafeBuffer()\n    .setScreenState({screenType:ergometer.ScreenType.Workout,value:ergometer.ScreenValue.PrepareToRowWorkout})                \n    .send();\n```\n\n# Usage for Usb\n\nAn usb device has a quicker way of finding devices but does not have all the concept2 BLE events. So the api is a bit different. The csafe part is exactly the same as for the ble device.\n\nTo create an Usb monitor:\n\n```ts\nvar performanceMonitor= new ergometer.PerformanceMonitorUsb();  \n```\n\n```ts\n//to find out which concept2 devices are connected\nvar foundDevice;\nthis.performanceMonitor.requestDevics().then(devices=\u003e{\n    //here a list of concept 2 devices are returned\n    //you can loop the devices\n    devices.forEach( (device) =\u003e {\n        console.log(device.productName);\n        foundDevice=device;\n    })\n});\n//to connect to an device you can use the connectToDevice\nif (foundDevice)\n    performanceMonitor.connectToDevice(foundDevice);\n```\n\nto disconnect from the performance monitor call the disconnect method.\n\n```ts\nperformanceMonitor.disconnect()\n```\n\nyou can retreive data from the monitor by connecting to events. when you do not subscribe \nto any of the training/stroke/power curve events then the monitor will not do any csafe commands\nto get data. You have to do your own csafe calls to get data.\n\n## logEvent\nreturns error, info and trace messages. (same event as in the blue tooth ergometer)\n\n## connectionStateChangedEvent\nGet info on the connection state.  (same event as in the blue tooth ergometer)\n\n```ts\nperformanceMonitor.connectionStateChangedEvent.sub(this,(oldState,newState)=\u003e{\n    console.log(\"new connection state=\"+newState.toString());       \n});\n```\n\n## strokeStateEvent\nUsing this event you can see if the rower is doing is rowing and you can see in which phase he is.\n\n```ts\nperformanceMonitor.strokeStateEvent.sub(this,(oldState : ergometer.StrokeState,newState : ergometer.StrokeState)=\u003e{\n    console.log(\"New state:\"+newState.toString());\n})\n```\n\n## trainingDataEvent\nInformation on the selected training and the state of the training.\n\n```ts\nperformanceMonitor.trainingDataEvent.sub(this,(data :ergometer.TrainingData)=\u003e{\n    console.log(\"training data :\"+JSON.stringify(data,null,\"  \"));\n});\n```\n\n## strokeDataEvent\nData on the last stroke.\n\n```ts\nperformanceMonitor.strokeDataEvent.sub(this,(data: ergometer.StrokeData)=\u003e{\n    console.log(\"stroke data:\"+JSON.stringify(data,null,\"  \"));\n});\n```\n\n## powerCurveEvent\nThe power curve. When you connect to this event the data will be retreived. (same event as in the blue tooth ergometer)\n\n```ts\nperformanceMonitor.powerCurveEvent.sub(this,(data : number[])=\u003e{\n    console.log(\"stroke data:\"+JSON.stringify(data,null,\"  \"));\n})\n```\n\n## csafe comnunication\n\nCsafe communication is done the same way as the ble commnunication.\nSee the csafe paragraph of the previous chapter how to do csafe commands.\n\n```ts\nthis.performanceMonitor.newCsafeBuffer()\n```\n\n# heart rate\n\nWhen the end user has an PM5 he will normally connect a heart rate device to the concept2 performance monitor and the\n```ts\n  performanceMonitor.heartRateBeltInformationEvent.sub(this,(data : ergometer.HeartRateBeltInformation)=\u003e{\n    //do some thing with the data\n  });\n```\ndevice will send the heart rate to ergometerjs. However older devices like the PM3 do not have heart rate support. For\nthis I have included a class HeartRateMonitorBle which can directly to a blue tooth heart rate device. \n\nHeartRateMonitorBle makes use of the same driver infra structure as the ergometer PerformanceMonitorBle class. The inter face of the heart rate monitor is similar to the blue tooth class the main difference is that this class has a heartRateDataEvent for reading the heart rate.\n\nTo start the connection first start scanning for a device,                                                           \nyou should call when the cordova deviceready event is called (or later)  \n                                           \n```ts\nvar heartRateMonitor=new ergometer.HeartRateMonitorBle();\nheartRateMonitor.startScan((device : ergometer.HeartRateDeviceInfo) : boolean =\u003e {                                        \n  //return true when you want to connect to the device                                                            \n   return device.name=='My device name';                                                                         \n});  \n```\n                                                                                                                 \nto connect at at a later time \n                                                                                     \n```ts\nheartRateMonitor.connectToDevice('my device name');\n```\n                                                           \nthe devices which where found during the scan are collected in\n                                                     \n```ts\nheartRateMonitor.devices   \n```\n                                                                                        \nwhen you connect to a device the scan is stopped, when you want to stop the scan earlier you need to call \n         \n```ts\nheartRateMonitor.stopScan()\n```\n\nTo disconnect call\n\n```ts\nheartRateMonitor.disconnect()\n```\nto receive the heart rate information you have to subscribe to the heartRateDataEvent event\n\n```ts\nheartRateMonitor.heartRateDataEvent.sub(this,this.hearRateData);\n```\n\nAn demo of the api is in included in the electron usb debug example.\n         \n# Examples\n            \n\n## Electron\n\nUse this when you want tow write a desktop app using html 5. ErgometerJS can connect\nusing noble to an PM5 device or using.\n\n[demos/simple_electron](demos/simple_electron/README.md)                 \n\n\n## Record an replay\n\nRecord and replay the bluetooth communication. Handy for debugging without\n having to row on an ergometer. This demo is written using electron, this makes it ideal for\n debugging the communication because you can do not need to place it on a phone to make it worrk.\n \n[demos/recording](demos/recording/README.md)                 \n                 \n## Ionic 2\n\nPopular GUI frame work for writing hml5 apps for mobile apps. Since it is html 5\nthe app is re-usable in web or electron.\n\n[demos/ionic_test](demos/ionic_test/README.md)\n\n## React native\n\nWrite mobile apps using native components in react using javascript.\n\nKnow problem: the used blue tooth library does not (yet?) support direct reading and writing to characteristics.\ndue to this the csafe commands and the power curve do not work.\n\n[demos/react_native](demos/react_native/README.md)\n\n## Web bluetooth\nThis is for web application. Directy access the ergometer from the webbrowser. This feature\nis at the point of writing only works in the latest chrome on a mac and linux.\n\n[demos/webbluetooth](demos/webbluetooth/README.md)\n\n## Node/electron Usb (hid device)\n\nAn example how to connect to an older PM3-4 monitor using usb. Blue tooth native library is not installed, but it can still make use of blue tooth using chrome web ble in electron.\n\n[demos/usb_electron](demos/usb_electron/README.md)\n\nVersion which includes all the ergometer js source for debuging purpose.\n\n[demos/usb_electron_debug](demos/usb_electron_debug/README.md)\n\n\n## Web hid example\n\nWith this you can connect using usb to your pm5 directly from chromium based browsers. \n\nhttps://github.com/robatwilliams/awesome-webhid#status\n\n[demos/web_usb_debug](demos/web_usb_debug/README.md)\n\n## cordova usb example (includes ble heart rate samle).\n\nfor cordova I have created an usb hid plugin which needs to be installed\n\n  https://github.com/tijmenvangulik/cordova-usb-hid\n\nThe demo compiles by including the original source code. This is good for debugging. It is better to\ninclude the lib when you only the lib.\n\nThis sample also includes an example how to connect to an heart rate device directly using the HeartRateMonitorBle class. This is usefull for connecting to a PM3 device which does not have heart rate support.\n\n[demos/usb_cordova_debug](demos/usb_cordova_debug/ReadMe.md)\n\n# cordova ble example\n\nBlue tooth example using cordova for mobile platforms.\n\n[demos/ble_cordova_debug](demos/ble_cordova_debug/README.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftijmenvangulik%2Fergometerjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftijmenvangulik%2Fergometerjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftijmenvangulik%2Fergometerjs/lists"}