var Ant = require('ant-plus');
var PowerMeter = function() {
var stick = new Ant.GarminStick3;
var channel = 1;
if (!stick.is_present()) {
stick = new Ant.GarminStick2;
}
stick.on('startup', function () {
console.log('startup');
console.log('Max channels:', stick.maxChannels);
// 0xCAFFEDOOD
var deviceId = 0xBEEF;
stick.write(Ant.Messages.assignChannel(channel, 'transmit'));
// The device type shall be set to 11 (0x0B) when searching to pair to an ANT+ bike power sensor
// The transmitting sensor contains a 16-bit number that uniquely identifies its
// transmissions. Set the Device Number parameter to zero to allow wildcard
// matching. Once the device number is learned, the receiving device should
// remember the number for future searches.
// Device number set to 1 here
stick.write(Ant.Messages.setDevice(channel, deviceId, 11, 1));
// RF Channel 57 (2457 MHz) is used for the ANT+ bike power sensor.
stick.write(Ant.Messages.setFrequency(channel, 57));
// Channel period Data is transmitted from most bike power sensors every 8182/32768 seconds
// (approximately 4.00 Hz). This channel period shall be used by default.
stick.write(Ant.Messages.setPeriod(channel, 4096)); //8192 16384 2370
stick.write(Ant.Messages.openChannel(channel));
console.log('cycling power meter initialized');
});
stick.on('shutdown', function () { console.log('ANT+ shutdown'); });
if (!stick.open()) {
console.log('ANT+ USB stick not found!');
}
this.stick = stick;
this.channel = channel;
this.power_event_count = 0;
this.power_accumulated = 0;
};
PowerMeter.prototype.broadcast = function(power, cadence) { //, cadence
if (!this.stick.is_present()) {
return;
}
var data = [];
data.push(this.channel);
data.push(0x10); // power only
this.power_event_count++;
this.power_event_count = this.power_event_count % 254; // rollover 255
data.push(this.power_event_count);
data.push(0xFF); // pedal power not-used
data.push(cadence); // cadence
this.power_accumulated += power;
this.power_accumulated = this.power_accumulated % 65534; //65535
console.log("Event: %s \t Power: %sw \t Cadence: %srpm", this.power_event_count, power, cadence); //, cadence
data = data.concat(Ant.Messages.intToLEHexArray(this.power_accumulated, 2));
data = data.concat(Ant.Messages.intToLEHexArray(power, 2));
this.stick.write(Ant.Messages.buildMessage(data, 0x4E)); //ANT_BROADCAST_DATA
};
module.exports.PowerMeter = PowerMeter;
var SpeedMeter = function (SensorPin, levelUpPin, levelDownPin, weighting, pulsesPerRev, timeOut, bounceTime, bounceTimeL) {
var rpm = 0;
var level = 1;
var timeAct = 0;
var timeOld = 0;
var timeDiff = 0;
var timeLevel = 0;
var watchdog = null;
var gpio = require('rpi-gpio');
var power = [
[8, 16, 25, 38, 50, 69, 88, 103, 120, 138, 152],
[11, 22, 34, 51, 69, 91, 113, 131, 154, 178, 192],
[12, 24, 42, 64, 88, 114, 145, 175, 202, 238, 254],
[15, 31, 51, 77, 107, 140, 170, 206, 236, 280, 294],
[19, 38, 60, 91, 126, 163, 203, 243, 283, 327, 367],
[20, 40, 69, 105, 145, 188, 228, 273, 319, 362, 409],
[22, 48, 79, 119, 164, 214, 260, 310, 360, 400, 458],
[24, 53, 88, 133, 183, 237, 286, 350, 406, 447, 518],
[28, 57, 97, 146, 202, 263, 318, 389, 449, 494, 585],
[30, 64, 106, 160, 221, 289, 337, 415, 476, 518, 620],
[32, 68, 115, 174, 240, 312, 351, 435, 499, 544, 655],
[33, 72, 123, 186, 259, 338, 293, 483, 550, 596, 712],
[37, 79, 133, 200, 278, 361, 427, 527, 592, 652, 783],
[40, 87, 142, 215, 297, 387, 462, 571, 636, 709, 832],
[42, 91, 151, 228, 316, 409, 485, 599, 669, 744, 864],
[44, 95, 160, 242, 335, 436, 515, 633, 708, 787, 905]
];
weighting = weighting || 0;
pulsesPerRev = pulsesPerRev || 1;
timeOut = timeOut || 10000;
bounceTime = bounceTime || 150;
bounceTimeL = bounceTimeL || 150;
gpio.on('change', function (channel, value) {
if (channel == SensorPin) {
var timeAct = new Date();
if ((timeAct - timeOld) > bounceTime) {
if (watchdog) {
clearTimeout(watchdog);
}
if (timeOld) {
timeDiff *= weighting;
timeDiff += (1 - weighting) * (timeAct - timeOld);
rpm = 60000 / (timeDiff * pulsesPerRev);
}
timeOld = timeAct;
watchdog = setTimeout(function () {
timeOld = 0;
rpm = 0;
}, timeOut);
}
}
else if (channel == levelUpPin) {
var timeLevelUp = new Date();
if ((timeLevelUp - timeLevel) > bounceTimeL) {
if (level < 16) {
++level;
}
timeLevel = timeLevelUp;
}
}
else if (channel == levelDownPin) {
var timeLevelDown = new Date();
if ((timeLevelDown - timeLevel) > bounceTimeL) {
if (level > 1) {
--level;
}
timeLevel = timeLevelDown;
}
}
});
gpio.setup(SensorPin, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(levelUpPin, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(levelDownPin, gpio.DIR_IN, gpio.EDGE_RISING);
this.getSpeed = function () {
return rpm;
};
this.getLevel = function () {
return level;
};
this.getPower = function () {
var lowerVal = 0;
var upperVal = 0;
var idxLower = Math.floor(rpm / 10);
var idxUpper = Math.ceil(rpm / 10);
if (idxLower > 1) {
lowerVal = power[level - 1][idxLower - 2];
}
else {
idxLower = 0;
}
if (idxUpper > 1 && idxUpper <= 12) //12
{
upperVal = power[level - 1][idxUpper - 2];
}
else if (idxUpper > 12) //12
{
console.log("RPM(", rpm, ") out of range");
return power[level - 1][10];
}
else if (idxUpper <= 1) {
idxUpper = 0;
}
if (idxUpper == 0 && idxLower == 0) {
return 0;
}
return (upperVal - lowerVal) / (idxUpper * 10 - idxLower * 10) * (rpm - idxLower * 10) + lowerVal;
};
};
module.exports.SpeedMeter = SpeedMeter;