add heart rate and elapsed time to ftms profiles

This commit is contained in:
Lars Berning 2021-03-26 22:15:02 +00:00
parent f1c0091a7a
commit 1ce1a2136a
7 changed files with 45 additions and 22 deletions

View File

@ -12,8 +12,6 @@ export default class DeviceInformationService extends bleno.PrimaryService {
// uuid of "Device Information Service"
uuid: '180a',
characteristics: [
/* new SerialNumberCharacteristic(device),
new HardwareRevisionCharacteristic(device) */
]
})
}

View File

@ -31,17 +31,20 @@ export default class IndoorBikeDataCharacteristic extends bleno.Characteristic {
properties: ['notify']
})
this._updateValueCallback = null
this._subscriberMaxValueSize = null
}
onSubscribe (maxValueSize, updateValueCallback) {
log.debug(`IndooBikeDataCharacteristic - central subscribed with maxSize: ${maxValueSize}`)
log.debug(`IndoorBikeDataCharacteristic - central subscribed with maxSize: ${maxValueSize}`)
this._updateValueCallback = updateValueCallback
this._subscriberMaxValueSize = maxValueSize
return this.RESULT_SUCCESS
}
onUnsubscribe () {
log.debug('IndooBikeDataCharacteristic - central unsubscribed')
log.debug('IndoorBikeDataCharacteristic - central unsubscribed')
this._updateValueCallback = null
this._subscriberMaxValueSize = null
return this.RESULT_UNLIKELY_ERROR
}
@ -56,11 +59,11 @@ export default class IndoorBikeDataCharacteristic extends bleno.Characteristic {
const bufferBuilder = new BufferBuilder()
// Field flags as defined in the Bluetooth Documentation
// Instantaneous speed (default), Total Distance (4), Instantaneous Power (6)
// Total / Expended Energy (8)
// Total / Expended Energy (8), Heart Rate (9), Elapsed Time (11)
// 01010000
bufferBuilder.writeUInt8(0x50)
// 00000001
bufferBuilder.writeUInt8(0x01)
// 00001011
bufferBuilder.writeUInt8(0x0B)
// see https://www.bluetooth.com/specifications/specs/gatt-specification-supplement-3/
// for some of the data types
@ -78,8 +81,16 @@ export default class IndoorBikeDataCharacteristic extends bleno.Characteristic {
// period of one hour.
bufferBuilder.writeUInt16LE(data.caloriesPerHour)
// Energy per minute
bufferBuilder.writeUInt16LE(data.caloriesPerMinute)
bufferBuilder.writeUInt8(data.caloriesPerMinute)
// Heart Rate: Beats per minute with a resolution of 1
bufferBuilder.writeUInt8(data.heartRate)
// Elapsed Time: Seconds with a resolution of 1
bufferBuilder.writeUInt16LE(data.durationTotal)
const buffer = bufferBuilder.getBuffer()
if (buffer.length > this._subscriberMaxValueSize) {
log.warn(`IndoorBikeDataCharacteristic - notification of ${buffer.length} bytes is too large for the subscriber`)
}
this._updateValueCallback(bufferBuilder.getBuffer())
}
return this.RESULT_SUCCESS

View File

@ -24,12 +24,12 @@ export default class IndoorBikeDataCharacteristic extends bleno.Characteristic {
// see https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0 for details
// Fitness Machine Features for the IndoorBikeDataCharacteristic
// Total Distance Supported (2), Expended Energy Supported (9),
// Power Measurement Supported (14)
// 00000100 01000010
// Heart Rate Measurement Supported (10), Elapsed Time Supported (12), Power Measurement Supported (14)
// 00000100 01010110
// Target Setting Features for the IndoorBikeDataCharacteristic
// none
// 0000000 0000000
const features = [0x04, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
const features = [0x04, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
log.debug('Features of Indoor Bike requested')
callback(this.RESULT_SUCCESS, features.slice(offset, features.length))
}

View File

@ -24,17 +24,20 @@ export default class RowerDataCharacteristic extends bleno.Characteristic {
properties: ['notify']
})
this._updateValueCallback = null
this._subscriberMaxValueSize = null
}
onSubscribe (maxValueSize, updateValueCallback) {
log.debug(`RowerDataCharacteristic - central subscribed with maxSize: ${maxValueSize}`)
this._updateValueCallback = updateValueCallback
this._subscriberMaxValueSize = maxValueSize
return this.RESULT_SUCCESS
}
onUnsubscribe () {
log.debug('RowerDataCharacteristic - central unsubscribed')
this._updateValueCallback = null
this._subscriberMaxValueSize = null
return this.RESULT_UNLIKELY_ERROR
}
@ -48,13 +51,13 @@ export default class RowerDataCharacteristic extends bleno.Characteristic {
const bufferBuilder = new BufferBuilder()
// Field flags as defined in the Bluetooth Documentation
// Stroke Rate (default), Stroke Count (default), Total Distance (2), Instantaneous Pace (3),
// Instantaneous Power (5), Total / Expended Energy (8)
// todo: might add: Average Stroke Rate (1), Average Pace (4), Average Power (6), Heart Rate (9)
// Elapsed Time (11), Remaining Time (12)
// Instantaneous Power (5), Total / Expended Energy (8), Heart Rate (9), Elapsed Time (11)
// todo: might add: Average Stroke Rate (1), Average Pace (4), Average Power (6)
// Remaining Time (12)
// 00101100
bufferBuilder.writeUInt8(0x2c)
// 00000001
bufferBuilder.writeUInt8(0x01)
// 00001011
bufferBuilder.writeUInt8(0x0B)
// see https://www.bluetooth.com/specifications/specs/gatt-specification-supplement-3/
// for some of the data types
@ -77,8 +80,16 @@ export default class RowerDataCharacteristic extends bleno.Characteristic {
// period of one hour.
bufferBuilder.writeUInt16LE(data.caloriesPerHour)
// Energy per minute
bufferBuilder.writeUInt16LE(data.caloriesPerMinute)
bufferBuilder.writeUInt8(data.caloriesPerMinute)
// Heart Rate: Beats per minute with a resolution of 1
bufferBuilder.writeUInt8(data.heartRate)
// Elapsed Time: Seconds with a resolution of 1
bufferBuilder.writeUInt16LE(data.durationTotal)
const buffer = bufferBuilder.getBuffer()
if (buffer.length > this._subscriberMaxValueSize) {
log.warn(`RowerDataCharacteristic - notification of ${buffer.length} bytes is too large for the subscriber`)
}
this._updateValueCallback(bufferBuilder.getBuffer())
}
return this.RESULT_SUCCESS

View File

@ -23,13 +23,14 @@ export default class RowerFeatureCharacteristic extends bleno.Characteristic {
onReadRequest (offset, callback) {
// see https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0 for details
// Fitness Machine Features for the RowerDataCharacteristic
// Total Distance Supported (2), Pace Supported (5),
// Expended Energy Supported (9), Power Measurement Supported (14)
// 00100100 01000010
// Total Distance Supported (2), Pace Supported (5), Expended Energy Supported (9),
// Heart Rate Measurement Supported (10), Elapsed Time Supported (bit 12),
// Power Measurement Supported (14)
// 00100100 01010110
// Target Setting Features for the RowerDataCharacteristic
// none
// 0000000 0000000
const features = [0x24, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
const features = [0x24, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
log.debug('Features of Rower requested')
callback(this.RESULT_SUCCESS, features.slice(offset, features.length))
};

View File

@ -44,7 +44,7 @@ export default class AdditionalStatus extends bleno.Characteristic {
// strokeRate: UInt8 in strokes/min
bufferBuilder.writeUInt8(data.strokesPerMinute)
// heartRate: UInt8 in bpm, 255 if invalid
bufferBuilder.writeUInt8(255)
bufferBuilder.writeUInt8(255)// data.heartRate
// currentPace: UInt16LE in 0.01 sec/500m
// if split is infinite (i.e. while pausing), use the highest possible number
bufferBuilder.writeUInt16LE(data.split !== Infinity ? data.split * 100 : 0xFFFF)

View File

@ -87,6 +87,7 @@ rowingStatistics.on('strokeFinished', (data) => {
split: data.split,
strokesPerMinute: data.strokesPerMinute,
speed: data.speed,
heartRate: 0,
strokeState: data.strokeState
}
webServer.notifyClients(metrics)
@ -107,6 +108,7 @@ rowingStatistics.on('rowingPaused', (data) => {
splitFormatted: '∞',
split: Infinity,
speed: 0,
heartRate: 0,
strokeState: 'RECOVERY'
}
webServer.notifyClients(metrics)