Skip to content

Commit

Permalink
adding moxy support
Browse files Browse the repository at this point in the history
  • Loading branch information
dvmarinoff committed Dec 27, 2023
1 parent bb218fc commit bf09983
Show file tree
Hide file tree
Showing 12 changed files with 491 additions and 10 deletions.
43 changes: 43 additions & 0 deletions src/ble/moxy-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { xf } from '../functions.js';
import { ble } from './web-ble.js';
import { uuids } from './uuids.js';
import { Device } from './device.js';
import { SmO2Service } from './moxy/smo2.js';
import { models } from '../models/models.js';

function onSensorData(data) {
const self = this;
if(('currentSaturatedHemoglobin' in data)) {
xf.dispatch(`smo2`, data.currentSaturatedHemoglobin);
}
if(('previousSaturatedHemoglobin' in data)) {
// xf.dispatch(`previousSaturatedHemoglobin`, data.previousSaturatedHemoglobin);
}
if(('totalHemoglobinSaturation' in data)) {
xf.dispatch(`thb`, data.totalHemoglobinSaturation);
}
}

class MoxyMonitor extends Device {
defaultId() {
return `ble:moxy`;
}
defaultFilter() {
return ble.requestFilters.moxy;
}
async start() {
const self = this;

const service = await self.getService(uuids.moxySmO2);

self.smo2 = new SmO2Service({
onData: onSensorData.bind(self),
service,
ble,
});

await self.smo2.start();
}
}

export { MoxyMonitor }
172 changes: 172 additions & 0 deletions src/ble/moxy/moxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//
// Saturated Muscle Oxygen Service
//

import { Spec } from '../common.js';

function SensorData(args = {}) {

const definitions = {
sensorCount: {
size: 2, resolution: 1, unit: '', min: 0, max: 32767,
},
currentSaturatedHemoglobin: {
size: 2, resolution: 0.1, unit: '%', min: 0, max: 1000,
},
previousSaturatedHemoglobin: {
size: 2, resolution: 0.1, unit: '%', min: 0, max: 1000,
},
totalHemoglobinSaturation: {
size: 2, resolution: 0.01, unit: 'g/dl', min: 0, max: 4000,
},
requestTimeSetBit: {
size: 1, resolution: 1, unit: '', min: 0, max: 1,
},
};

const spec = Spec({definitions});

function encode(args = {}) {
throw new Error("SmOS SensorData.encode is not yet implemented!");
}

// Example:
//
// (0x) CB-06- 15-02- 16-02- C0-04 -00
// 1739 533 534 1216 0
//
// (0x) CC-06- 12-02- 12-02- C1-04 -00
// 1740 530 530 1217 0
//
// (0x) CD-06- 12-02- 12-02- C1-04 -00
// 1741 530 530 1217 0
//
// (0x) CE-06- 13-02- 12-02- C1-04 -00
// 1742 531 530 1217 0
//
// (0x) CF-06- 14-02- 13-02- C1-04 -00
// 1743 532 531 1217 0
//
function decode(dataview) {
const sensorCount = dataview.getUint16(0, true);
const currentSaturatedHemoglobin = spec.decodeField(
'currentSaturatedHemoglobin',
dataview.getUint16(2, true)
);
const previousSaturatedHemoglobin = spec.decodeField(
'previousSaturatedHemoglobin',
dataview.getUint16(4, true)
);
const totalHemoglobinSaturation = spec.decodeField(
'totalHemoglobinSaturation',
dataview.getUint16(6, true)
);
const requestTimeSetBit = dataview.getUint8( 8, true);

return {
sensorCount,
currentSaturatedHemoglobin,
previousSaturatedHemoglobin,
totalHemoglobinSaturation,
requestTimeSetBit,
};
}

return Object.freeze({
encode,
decode,
});
}

function DeviceControl(args = {}) {
const length = 3;

const definitions = {
dataControl: {
size: 1, resolution: 1, unit: '', min: 0, max: 5,
},
updateType: {
size: 1, resolution: 1, unit: '', min: 0, max: 5,
},
};

const values = {
dataControl: {
notIncluded: 0,
reset: 1,
markLap: 2,
start: 3,
stop: 4,
clearData: 5,
},
updateType: {
'doNotChange': 0,
'2sDataSmoothing': 1, // Default
'2sNoDataSmoothing': 2,
'2sDataSmoothing': 3,
'0.5sNoDataSmoothing': 4,
'1sDataSmoothing': 5,
},
antProfile: {
doNotChange: 0,
matchExisting: 1,
matchExisting: 2,
matchExisting: 3,
},
antBleSelector: {
doNotChange: 0,
antPlus: 1,
ble: 2,
both: 3,
neither: 4,
}
};

const defaults = {
dataControl: values.dataControl.notIncluded,
updateType: values.updateType.doNotChange,
antProfile: values.antProfile.doNotChange,
antBleSelector: values.antBleSelector.doNotChange,
};

function encode(args = {}) {
const dataControl = args.dataControl ?? defaults.dataControl;
const updateType = args.updateType ?? defaults.updateType;

// Depricated
const antProfile = defaults.antProfile;
const antBleSelector = defaults.antBleSelector;
const compound = (antProfile << 4) + antBleSelector;
// end Depricated

const buffer = new ArrayBuffer(length);
const view = new DataView(buffer);

view.setUint8(0, dataControl, true);
view.setUint8(1, updateType, true);
view.setUint8(1, compound, true);

return view.buffer;
}

function decode(dataview) {
throw new Error("SmO2 DeviceControl.decode is not yet implemented!");
}

return Object.freeze({
encode,
decode,
length,
definitions,
values,
defaults,
});
}

const sensorData = SensorData();
const deviceControl = DeviceControl();

export {
sensorData,
deviceControl
}
39 changes: 39 additions & 0 deletions src/ble/moxy/smo2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { uuids } from '../uuids.js';
import { BLEService } from '../service.js';
import { sensorData } from './moxy.js';
import { existance } from '../../functions.js';

class SmO2Service extends BLEService {
uuid = uuids.moxySmO2

postInit(args = {}) {
this.onData = args.onData ?? this.defaultOnData;

this.characteristics = {
moxySmO2SensorData: {
uuid: uuids.moxySmO2SensorData,
supported: false,
characteristic: undefined,
},
SmO2DeviceControl: {
uuid: uuids.moxySmO2DeviceControl,
supported: false,
characteristic: undefined,
},
};
}
async postStart() {
const self = this;

if(self.supported('moxySmO2SensorData')) {
await self.sub('moxySmO2SensorData', sensorData.decode, self.onData.bind(self));
}
}
defaultOnData(decoded) {
console.log(':rx :smo2 :sensorData ', JSON.stringify(decoded));
}
}

export {
SmO2Service,
}
6 changes: 6 additions & 0 deletions src/ble/uuids.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const services = {
deviceInformation: '0000180a-0000-1000-8000-00805f9b34fb',
fec: '6e40fec1-b5a3-f393-e0a9-e50e24dcca9e',
wahooFitnessMachine: 'a026ee0b-0a7d-4ab3-97fa-f1500f9feb8b',
moxySmO2: '6404d801-4cb9-11e8-b566-0800200c9a66',
};

const characteristics = {
Expand Down Expand Up @@ -48,6 +49,11 @@ const characteristics = {
// Wahoo Fitness Machine
wahooFitnessMachineControlPoint: 'a026e037-0a7d-4ab3-97fa-f1500f9feb8b',

// SmO2 Moxy
moxySmO2SensorData: '6404d804-4cb9-11e8-b566-0800200c9a66',
moxySmO2DeviceControl: '6404d810-4cb9-11e8-b566-0800200c9a66',
moxySmO2ControlPoint: '6404d811-4cd9-11e8-b566-0800200c9a66',

// others
sensorLocation: '00002a5d-0000-1000-8000-00805f9b34fb',
clientCharacteristicConfiguration: '00002902 -0000-1000-8000-00805f9b34fb',
Expand Down
8 changes: 7 additions & 1 deletion src/ble/web-ble.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ const _speedCadence = {
optionalServices: [uuids.deviceInformation]
};

const _moxy = {
filters: [{services: [uuids.moxySmO2]}],
optionalServices: [uuids.deviceInformation]
};

const _all = {acceptAllDevices: true};

function filterIn(coll, prop, value) {
Expand Down Expand Up @@ -65,7 +70,8 @@ class WebBLE {
hrm: _hrm,
controllable: _controllable,
speedCadence: _speedCadence,
power: _power ,
power: _power,
moxy: _moxy,
all: _all
};
constructor(args) {}
Expand Down
2 changes: 2 additions & 0 deletions src/connectionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Hrm } from './ble/hrm.js';
import { Controllable } from './ble/controllable.js';
import { PowerMeter } from './ble/power-meter.js';
import { SpeedCadence } from './ble/speed-cadence.js';
import { MoxyMonitor } from './ble/moxy-monitor.js';
import { Hrm as AntHrm } from './ant/devices.js';
import { Controllable as AntControllable } from './ant/devices.js';
import { xf } from './functions.js';
Expand All @@ -14,6 +15,7 @@ function start() {
const hrm = new Hrm();
const pm = new PowerMeter();
const speedCadence = new SpeedCadence();
const moxyMonitor = new MoxyMonitor();

antDriver.init();

Expand Down
25 changes: 25 additions & 0 deletions src/css/flux.css
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,14 @@ body {
grid-area: 3 / 4 / 3 / 7;
display: none;
}
#data-tile--smo2 {
grid-area: 3 / 1 / 3 / 4;
display: none;
}
#data-tile--thb {
grid-area: 3 / 4 / 3 / 7;
display: none;
}


#data-tile--power-avg.active {
Expand All @@ -666,6 +674,14 @@ body {
display: block;
}

#data-tile--smo2.active {
display: block;
}

#data-tile--thb.active {
display: block;
}

.data-tile--switch--group {
position: absolute;
bottom: 0em;
Expand Down Expand Up @@ -2015,6 +2031,15 @@ and (min-height: 600px) {
grid-area: 2 / 5 / 2 / 5;
}

#data-tile--smo2.active {
grid-area: 3 / 1 / 3 / 4;
display: none;
}
#data-tile--thb.active {
grid-area: 3 / 4 / 3 / 7;
display: none;
}

.data-tile-small--unit {
display: none;
}
Expand Down
10 changes: 10 additions & 0 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ let db = {
cadence: models.cadence.default,
speed: models.speed.default,
sources: models.sources.default,
smo2: models.smo2.default,
thb: models.thb.default,

speedVirtual: models.virtualState.speed,
altitude: models.virtualState.altitude,
Expand Down Expand Up @@ -96,6 +98,14 @@ xf.reg(models.speed.prop, (speed, db) => {
db.speed = speed;
});

xf.reg(models.smo2.prop, (smo2, db) => {
db.smo2 = smo2;
});

xf.reg(models.thb.prop, (thb, db) => {
db.thb = thb;
});

xf.reg(models.sources.prop, (sources, db) => {
db.sources = models.sources.set(db.sources, sources);
models.sources.backup(db.sources);
Expand Down
Loading

0 comments on commit bf09983

Please sign in to comment.