notifies PM5 central when stroke state changes

This commit is contained in:
Lars Berning 2021-03-20 22:04:52 +00:00
parent 2e3654fabd
commit e5e579f3fc
8 changed files with 41 additions and 43 deletions

View File

@ -12,14 +12,11 @@
(Manufacturer Name String, Model Number String)
*/
import bleno from '@abandonware/bleno'
import { EventEmitter } from 'events'
import FitnessMachineService from './ftms/FitnessMachineService.js'
import DeviceInformationService from './ftms/DeviceInformationService.js'
import log from 'loglevel'
function createFtmsPeripheral (options) {
const emitter = new EventEmitter()
function createFtmsPeripheral (controlCallback, options) {
const peripheralName = options?.simulateIndoorBike ? 'OpenRowingBike' : 'OpenRowingMonitor'
// const peripheralName = options?.simulateIndoorBike ? 'OpenRowingBike' : 'S1 Comms 1'
const fitnessMachineService = new FitnessMachineService(options, controlPointCallback)
@ -72,7 +69,7 @@ function createFtmsPeripheral (options) {
req: event,
res: {}
}
emitter.emit('controlPoint', obj)
if (controlCallback) controlCallback(obj)
return obj.res
}
@ -99,22 +96,24 @@ function createFtmsPeripheral (options) {
}
}
// deliver current rowing metrics via BLE
function notifyData (data) {
fitnessMachineService.notifyData(data)
// present current rowing metrics to FTMS central
function notifyData (type, data) {
if (type === 'strokeFinished' || type === 'rowingPaused') {
fitnessMachineService.notifyData(data)
}
}
// deliver a status change via BLE
// present current rowing status to FTMS central
function notifyStatus (status) {
fitnessMachineService.notifyStatus(status)
}
return Object.assign(emitter, {
return {
triggerAdvertising,
notifyData,
notifyStatus,
destroy
})
}
}
export { createFtmsPeripheral }

View File

@ -36,7 +36,7 @@ function createPeripheralManager () {
}
function notifyMetrics (type, metrics) {
peripheral.notifyData(metrics)
peripheral.notifyData(type, metrics)
}
function notifyStatus (status) {
@ -46,29 +46,25 @@ function createPeripheralManager () {
async function createPeripheral (newMode) {
if (peripheral) {
await peripheral.destroy()
peripheral.off('controlPoint', emitControlPointEvent)
}
if (newMode === 'PM5') {
log.info('bluetooth profile: Concept2 PM5')
peripheral = createPm5Peripheral()
peripheral = createPm5Peripheral(controlCallback)
mode = 'PM5'
} else if (newMode === 'FTMSBIKE') {
log.info('bluetooth profile: FTMS Indoor Bike')
peripheral = createFtmsPeripheral({
peripheral = createFtmsPeripheral(controlCallback, {
simulateIndoorBike: true
})
mode = 'FTMSBIKE'
} else {
log.info('bluetooth profile: FTMS Rower')
peripheral = createFtmsPeripheral({
peripheral = createFtmsPeripheral(controlCallback, {
simulateIndoorBike: false
})
mode = 'FTMS'
}
// todo: re-emitting is not that great, maybe use callbacks instead
peripheral.on('control', emitControlPointEvent)
peripheral.triggerAdvertising()
emitter.emit('control', {
@ -79,7 +75,7 @@ function createPeripheralManager () {
})
}
function emitControlPointEvent (event) {
function controlCallback (event) {
emitter.emit('control', event)
}

View File

@ -8,7 +8,6 @@
see: https://www.concept2.co.uk/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf
*/
import bleno from '@abandonware/bleno'
import { EventEmitter } from 'events'
import { constants } from './pm5/Pm5Constants.js'
import DeviceInformationService from './pm5/DeviceInformationService.js'
import GapService from './pm5/GapService.js'
@ -16,9 +15,7 @@ import log from 'loglevel'
import Pm5ControlService from './pm5/Pm5ControlService.js'
import Pm5RowingService from './pm5/Pm5RowingService.js'
function createPm5Peripheral (options) {
const emitter = new EventEmitter()
function createPm5Peripheral (controlCallback, options) {
const peripheralName = constants.name
const deviceInformationService = new DeviceInformationService()
const gapService = new GapService()
@ -90,23 +87,21 @@ function createPm5Peripheral (options) {
}
}
// deliver current rowing metrics via BLE
function notifyData (data) {
rowingService.notify(data)
// fitnessMachineService.notifyData(data)
// present current rowing metrics to C2-PM5 central
function notifyData (type, data) {
rowingService.notifyData(type, data)
}
// deliver a status change via BLE
// present current rowing status to C2-PM5 central
function notifyStatus (status) {
// fitnessMachineService.notifyStatus(status)
}
return Object.assign(emitter, {
return {
triggerAdvertising,
notifyData,
notifyStatus,
destroy
})
}
}
export { createPm5Peripheral }

View File

@ -75,11 +75,16 @@ export default class PM5RowingService extends bleno.PrimaryService {
this.multiplexedCharacteristic = multiplexedCharacteristic
}
notify (data) {
this.generalStatus.notify(data)
this.additionalStatus.notify(data)
this.additionalStatus2.notify(data)
this.strokeData.notify(data)
this.additionalStrokeData.notify(data)
notifyData (type, data) {
if (type === 'strokeFinished' || type === 'rowingPaused') {
this.generalStatus.notify(data)
this.additionalStatus.notify(data)
this.additionalStatus2.notify(data)
this.strokeData.notify(data)
this.additionalStrokeData.notify(data)
} else if (type === 'strokeStateChanged') {
// the stroke state is delivered via the GeneralStatus Characteristic, so we only need to notify that one
this.generalStatus.notify(data)
}
}
}

View File

@ -151,7 +151,7 @@ function createRowingEngine () {
}
log.debug(`estimated kDamp: ${jMoment * (-1 * kDampEstimatorAverager.weightedAverage())}`)
log.info(`estimated omegaDotDivOmegaSquare: ${-1 * kDampEstimatorAverager.weightedAverage()}`)
workoutHandler.handleStrokeStateChange({
workoutHandler.handleStrokeStateChanged({
strokeState: 'DRIVING'
})
}

View File

@ -62,11 +62,11 @@ function createRowingStatistics () {
}
// initiated when the stroke state changes
function handleStrokeStateChange (state) {
function handleStrokeStateChanged (state) {
// todo: wee need a better mechanism to communicate strokeState updates
// this is an initial hacky attempt to see if we can use it for the C2-pm5 protocol
lastStrokeState = state.strokeState
// emitter.emit('strokeFinished', getMetrics())
emitter.emit('strokeStateChanged', getMetrics())
}
function getMetrics () {
@ -154,7 +154,7 @@ function createRowingStatistics () {
return Object.assign(emitter, {
handleStroke,
handlePause,
handleStrokeStateChange,
handleStrokeStateChanged,
reset: resetTraining
})
}

View File

@ -111,6 +111,10 @@ rowingStatistics.on('rowingPaused', (data) => {
peripheralManager.notifyMetrics('rowingPaused', metrics)
})
rowingStatistics.on('strokeStateChanged', (metrics) => {
peripheralManager.notifyMetrics('strokeStateChanged', metrics)
})
rowingStatistics.on('durationUpdate', (data) => {
webServer.notifyClients({
durationTotalFormatted: data.durationTotalFormatted

View File

@ -4,7 +4,6 @@ This is the very minimalistic Backlog for further development of this project.
## Soon
* refactor Stroke Phase Handling in RowingStatistics and pm5Peripheral
* Web UI: hint, when screen is not in always on mode
* Web UI: replace fullscreen button with exit Button when started from home screen
* investigate: occasionally stroke rate is too high - seems to happen after rowing pause