aerogel
Version:
CrazyFlie control software
346 lines (288 loc) • 7.75 kB
JavaScript
// Crazy Real Time Protocol Driver
// This module knows how to format messages for the crazy radio and the copter.
// There's some repetition because the emphasis is on provding a clear, readable
// API to higher levels.
var
_ = require('lodash'),
events = require('events'),
url = require('url'),
util = require('util'),
P = require('p-promise'),
Crazyradio = require('./crazyradio'),
Crazypacket = require('./crazypacket'),
Parameters = require('./parameters'),
Protocol = require('./protocol'),
Telemetry = require('./telemetry')
;
var CrazyDriver = module.exports = function CrazyDriver(name)
{
events.EventEmitter.call(this);
this.uri = '';
this.channel = 2;
this.datarate = 2;
this.telemetry = new Telemetry(this);
this.parameters = new Parameters(this);
};
util.inherits(CrazyDriver, events.EventEmitter);
CrazyDriver.prototype.findCopters = function()
{
var self = this;
self.radio = new Crazyradio();
var results;
return self.radio.setupRadio()
.then(function() { return self.radio.setAckRetryCount(1); })
.then(function() { return self.scanAtRate(0); })
.then(function(res1)
{
results = res1;
})
.then(function() { return self.scanAtRate(1); })
.then(function(res2)
{
results = results.concat(res2);
})
.then(function() { return self.scanAtRate(2); })
.then(function(res3)
{
results = results.concat(res3);
return results;
})
.then(function(copters)
{
self.radio.on('console', self.handleConsole);
return copters;
})
.fail(function(err)
{
console.log('failure while finding copters');
console.log(err);
});
};
CrazyDriver.DATARATE =
{
0: '250KPS',
1: '1MPS',
2: '2MPS'
};
CrazyDriver.prototype.handleConsole = function(packet)
{
console.log('CONSOLE:', packet.data.toString());
};
CrazyDriver.prototype.scanAtRate = function(datarate)
{
var self = this;
return self.radio.setDataRate(datarate)
.then(function()
{
var buf = new Buffer(1);
buf[0] = 0xFF;
return self.radio.scanChannels(0, 127, buf);
})
.then(function(result)
{
var copters = [];
for (var i = 0; i < result.length; i++)
{
copters.push(util.format('radio://1/%d/%s', result[i], CrazyDriver.DATARATE[datarate]));
console.log(copters[i]);
}
return copters;
});
};
CrazyDriver.prototype.connect = function(uri)
{
var self = this;
this.uri = uri;
var parsed = url.parse(uri);
var pieces = parsed.pathname.split('/');
// preferred usb device is at parsed.hostname
if (pieces.length === 3)
{
switch (pieces[2])
{
case '250KPS':
this.datarate = Crazyradio.DATARATE['250KPS'];
break;
case '1MPS':
this.datarate = Crazyradio.DATARATE['1MPS'];
break;
case '2MPS':
this.datarate = Crazyradio.DATARATE['2MPS'];
break;
}
}
this.radio.addListener('logging', this.telemetry.handlePacket.bind(this.telemetry));
this.radio.addListener('param', this.parameters.handlePacket.bind(this.parameters));
this.channel = parseInt(pieces[1], 10);
return self.radio.setupRadio(null, self.channel, self.datarate)
.then(function()
{
return self.startTelemetry(); // completion required for flight
/*
})
.then(function()
{
return self.fetchParameters();
*/
});
};
CrazyDriver.prototype.version = function()
{
if (!this.radio)
return 'not connected to radio';
return 'Crazyradio version ' + this.radio.version;
};
CrazyDriver.prototype.close = function()
{
if (!this.radio)
return P('OK');
return this.radio.close();
};
CrazyDriver.prototype.setpoint = function(roll, pitch, yaw, thrust)
{
var self = this,
deferred = P.defer();
var packet = new Crazypacket();
packet.port = Protocol.Ports.COMMANDER;
packet.writeFloat(roll)
.writeFloat(-pitch)
.writeFloat(yaw)
.writeUnsignedShort(thrust)
.endPacket();
return this.radio.sendPacket(packet)
.then(function(item)
{
return item;
})
.fail(function(err)
{
console.log('failure in setpoint');
console.log(err);
});
};
// parameter functions: get list, set, get
CrazyDriver.prototype.fetchParameters = function()
{
var self = this;
this.parametersDeferred = P.defer();
this.requestTOC(Protocol.Ports.PARAM);
return this.parametersDeferred.promise;
};
CrazyDriver.prototype.parametersDone = function()
{
if (!this.parametersDeferred) return;
this.parametersDeferred.resolve('OK');
this.parametersDeferred = undefined;
};
CrazyDriver.prototype.getParameter = function(param)
{
var packet = new Crazypacket();
packet.port = Protocol.Ports.PARAM;
packet.channel = Protocol.Channels.PARAM_READ;
packet.writeByte(param);
packet.endPacket();
return this.radio.sendPacket(packet);
};
CrazyDriver.prototype.setParameter = function(param, value)
{
var packet = new Crazypacket();
packet.port = Protocol.Ports.PARAM;
packet.channel = Protocol.Channels.PARAM_WRITE;
packet.writeByte(param.id)
.writeType(param.type, value)
.endPacket();
return this.radio.sendPacket(packet);
};
// telemetry, which the Crazy protocol calls "logging"
var ttime;
CrazyDriver.prototype.startTelemetry = function()
{
console.log('starting telemetry');
ttime = Date.now();
var self = this;
this.telemetryDeferred = P.defer();
var packet = new Crazypacket();
packet.port = Protocol.Ports.LOGGING;
packet.channel = Protocol.Channels.SETTINGS;
packet.writeByte(Protocol.Commands.RESET_LOGGING);
packet.endPacket();
this.radio.sendPacket(packet)
.then(function()
{
self.requestTOC(Protocol.Ports.LOGGING);
});
return this.telemetryDeferred.promise;
};
CrazyDriver.prototype.telemetryReady = function()
{
console.log('telemetry ready; elapsed=', (Date.now() - ttime));
if (!this.telemetryDeferred) return;
this.telemetryDeferred.resolve('OK');
this.telemetryDeferred = undefined;
};
// ---------------------
// now the heavy lifting
CrazyDriver.prototype.requestTOC = function(which)
{
var packet = new Crazypacket();
packet.port = which;
packet.channel = Protocol.Channels.TOC;
packet.writeByte(Protocol.Commands.GET_INFO);
packet.endPacket();
return this.radio.sendPacket(packet);
};
// request details about a specific parameter or telemetry item
CrazyDriver.prototype.requestTelemetryElement = function(id)
{
return this.requestTOCItem(Protocol.Ports.LOGGING, id);
};
CrazyDriver.prototype.requestParameter = function(id)
{
return this.requestTOCItem(Protocol.Ports.PARAM, id);
};
CrazyDriver.prototype.requestTOCItem = function(port, id)
{
var packet = new Crazypacket();
packet.port = port;
packet.channel = Protocol.Channels.TOC;
packet.writeByte(Protocol.Commands.GET_ELEMENT);
if (id)
packet.writeByte(id);
packet.endPacket();
return this.radio.sendPacket(packet);
};
// Create a telemetry block, aka a group of sensor readings that
// get emitted by the copter periodically.
CrazyDriver.prototype.createTelemetryBlock = function(block)
{
var packet = new Crazypacket();
packet.port = Protocol.Ports.LOGGING;
packet.channel = Protocol.Channels.SETTINGS;
packet.writeByte(Protocol.Commands.CREATE_BLOCK)
.writeByte(block.id);
for (var i = 0; i < block.variables.length; i++)
{
var item = block.variables[i];
packet.writeByte(item.type << 4 | item.fetchAs)
.writeByte(item.id);
}
packet.endPacket();
return this.radio.sendPacket(packet);
};
CrazyDriver.prototype.enableTelemetryBlock = function(block)
{
var packet = new Crazypacket();
packet.port = Protocol.Ports.LOGGING;
packet.channel = Protocol.Channels.SETTINGS;
packet.writeByte(Protocol.Commands.START_LOGGING)
.writeByte(block)
.writeByte(10) // period
.endPacket();
return this.radio.sendPacket(packet);
};
CrazyDriver.prototype.handleAck = function(buf)
{
// TODO record ack stats-- dropped packets etc
var ack = new Crazypacket.RadioAck(buf);
return P(ack);
};