00001 #pragma once
00002
00003 #include <cstring>
00004 #include <sstream>
00005
00006 #include "Crazyradio.h"
00007 #include "crtp.h"
00008 #include <list>
00009 #include <set>
00010 #include <map>
00011 #include <iostream>
00012 #include <chrono>
00013
00014 class Crazyflie
00015 {
00016 public:
00017 enum ParamType {
00018 ParamTypeUint8 = 0x00 | (0x00<<2) | (0x01<<3),
00019 ParamTypeInt8 = 0x00 | (0x00<<2) | (0x00<<3),
00020 ParamTypeUint16 = 0x01 | (0x00<<2) | (0x01<<3),
00021 ParamTypeInt16 = 0x01 | (0x00<<2) | (0x00<<3),
00022 ParamTypeUint32 = 0x02 | (0x00<<2) | (0x01<<3),
00023 ParamTypeInt32 = 0x02 | (0x00<<2) | (0x00<<3),
00024 ParamTypeFloat = 0x02 | (0x01<<2) | (0x00<<3),
00025 };
00026
00027 struct ParamTocEntry {
00028 uint8_t id;
00029 ParamType type;
00030 bool readonly;
00031
00032
00033
00034
00035
00036 std::string group;
00037 std::string name;
00038 };
00039
00040 union ParamValue{
00041 uint8_t valueUint8;
00042 int8_t valueInt8;
00043 uint16_t valueUint16;
00044 int16_t valueInt16;
00045 uint32_t valueUint32;
00046 int32_t valueInt32;
00047 float valueFloat;
00048 };
00049
00050 enum LogType {
00051 LogTypeUint8 = 1,
00052 LogTypeUint16 = 2,
00053 LogTypeUint32 = 3,
00054 LogTypeInt8 = 4,
00055 LogTypeInt16 = 5,
00056 LogTypeInt32 = 6,
00057 LogTypeFloat = 7,
00058 LogTypeFP16 = 8,
00059 };
00060
00061 struct LogTocEntry {
00062 uint8_t id;
00063 LogType type;
00064 std::string group;
00065 std::string name;
00066 };
00067
00068 enum BootloaderTarget {
00069 TargetSTM32 = 0xFF,
00070 TargetNRF51 = 0xFE,
00071 };
00072
00073 public:
00074 Crazyflie(
00075 const std::string& link_uri);
00076
00077 void logReset();
00078
00079 void sendSetpoint(
00080 float roll,
00081 float pitch,
00082 float yawrate,
00083 uint16_t thrust);
00084
00085 void sendExternalPositionUpdate(
00086 float x,
00087 float y,
00088 float z);
00089
00090 void sendPing();
00091
00092 void reboot();
00093
00094 uint64_t rebootToBootloader();
00095 void sysoff();
00096 void trySysOff();
00097 void alloff();
00098 void syson();
00099 float vbat();
00100
00101 void writeFlash(
00102 BootloaderTarget target,
00103 const std::vector<uint8_t>& data);
00104 void readFlash(
00105 BootloaderTarget target,
00106 size_t size,
00107 std::vector<uint8_t>& data);
00108 void requestLogToc();
00109
00110 void requestParamToc();
00111
00112 std::vector<ParamTocEntry>::const_iterator paramsBegin() const {
00113 return m_paramTocEntries.begin();
00114 }
00115 std::vector<ParamTocEntry>::const_iterator paramsEnd() const {
00116 return m_paramTocEntries.end();
00117 }
00118
00119 std::vector<LogTocEntry>::const_iterator logVariablesBegin() const {
00120 return m_logTocEntries.begin();
00121 }
00122 std::vector<LogTocEntry>::const_iterator logVariablesEnd() const {
00123 return m_logTocEntries.end();
00124 }
00125
00126 template<class T>
00127 void setParam(uint8_t id, const T& value) {
00128 ParamValue v;
00129 memcpy(&v, &value, sizeof(value));
00130 setParam(id, v);
00131 }
00132
00133 template<class T>
00134 T getParam(uint8_t id) const {
00135 ParamValue v = getParam(id);
00136 return *(reinterpret_cast<T*>(&v));
00137 }
00138
00139 const ParamTocEntry* getParamTocEntry(
00140 const std::string& group,
00141 const std::string& name) const;
00142
00143 void setEmptyAckCallback(
00144 std::function<void(const crtpPlatformRSSIAck*)> cb) {
00145 m_emptyAckCallback = cb;
00146 }
00147
00148 void setLinkQualityCallback(
00149 std::function<void(float)> cb) {
00150 m_linkQualityCallback = cb;
00151 }
00152
00153 void setConsoleCallback(
00154 std::function<void(const char*)> cb) {
00155 m_consoleCallback = cb;
00156 }
00157
00158 static size_t size(LogType t) {
00159 switch(t) {
00160 case LogTypeUint8:
00161 case LogTypeInt8:
00162 return 1;
00163 break;
00164 case LogTypeUint16:
00165 case LogTypeInt16:
00166 case LogTypeFP16:
00167 return 2;
00168 break;
00169 case LogTypeUint32:
00170 case LogTypeInt32:
00171 case LogTypeFloat:
00172 return 4;
00173 break;
00174 }
00175 }
00176
00183 std::vector<Crazyradio::Ack> retrieveGenericPackets() {
00184 std::vector<Crazyradio::Ack> packets = m_generic_packets;
00185 m_generic_packets.clear();
00186 return packets;
00187 }
00188
00193 void queueOutgoingPacket(const crtpPacket_t& packet) {
00194 m_outgoing_packets.push_back(packet);
00195 }
00196
00197 void transmitPackets();
00198
00199 private:
00200 void sendPacket(
00201 const uint8_t* data,
00202 uint32_t length,
00203 Crazyradio::Ack& result);
00204
00205 bool sendPacket(
00206 const uint8_t* data,
00207 uint32_t length);
00208
00209 void sendPacketOrTimeout(
00210 const uint8_t* data,
00211 uint32_t length,
00212 float timeout = 1.0);
00213
00214 void handleAck(
00215 const Crazyradio::Ack& result);
00216
00217 std::vector<Crazyradio::Ack> m_generic_packets;
00218 std::vector<crtpPacket_t> m_outgoing_packets;
00219
00224 void queueGenericPacket(const Crazyradio::Ack& result) {
00225 m_generic_packets.push_back(result);
00226 }
00227
00228 void startBatchRequest();
00229
00230 void addRequest(
00231 const uint8_t* data,
00232 size_t numBytes,
00233 size_t numBytesToMatch);
00234
00235 template<typename R>
00236 void addRequest(
00237 const R& request,
00238 size_t numBytesToMatch)
00239 {
00240 addRequest(
00241 reinterpret_cast<const uint8_t*>(&request), sizeof(request), numBytesToMatch);
00242 }
00243
00244 void handleRequests(
00245 float baseTime = 0.5,
00246 float timePerRequest = 0.05);
00247
00248 void handleBatchAck(
00249 const Crazyradio::Ack& result);
00250
00251 template<typename R>
00252 const R* getRequestResult(size_t index) const {
00253 return reinterpret_cast<const R*>(m_batchRequests[index].ack.data);
00254 }
00255
00256 private:
00257 struct logInfo {
00258 uint8_t len;
00259 uint32_t log_crc;
00260 uint8_t log_max_packet;
00261 uint8_t log_max_ops;
00262 };
00263
00265
00266 struct paramInfo {
00267 uint8_t len;
00268 uint32_t crc;
00269 };
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294 private:
00295 const LogTocEntry* getLogTocEntry(
00296 const std::string& group,
00297 const std::string& name) const;
00298
00299 uint8_t registerLogBlock(
00300 std::function<void(crtpLogDataResponse*, uint8_t)> cb);
00301
00302 bool unregisterLogBlock(
00303 uint8_t id);
00304
00305 void setParam(uint8_t id, const ParamValue& value);
00306 const ParamValue& getParam(uint8_t id) const {
00307 return m_paramValues.at(id);
00308 }
00309
00310 private:
00311 Crazyradio* m_radio;
00312 ITransport* m_transport;
00313 int m_devId;
00314
00315 uint8_t m_channel;
00316 uint64_t m_address;
00317 Crazyradio::Datarate m_datarate;
00318
00319 std::vector<LogTocEntry> m_logTocEntries;
00320 std::map<uint8_t, std::function<void(crtpLogDataResponse*, uint8_t)> > m_logBlockCb;
00321
00322 std::vector<ParamTocEntry> m_paramTocEntries;
00323 std::map<uint8_t, ParamValue> m_paramValues;
00324
00325 std::function<void(const crtpPlatformRSSIAck*)> m_emptyAckCallback;
00326 std::function<void(float)> m_linkQualityCallback;
00327 std::function<void(const char*)> m_consoleCallback;
00328
00329 template<typename T>
00330 friend class LogBlock;
00331 friend class LogBlockGeneric;
00332
00333
00334 struct batchRequest
00335 {
00336 std::vector<uint8_t> request;
00337 size_t numBytesToMatch;
00338 ITransport::Ack ack;
00339 bool finished;
00340 };
00341 std::vector<batchRequest> m_batchRequests;
00342 size_t m_numRequestsFinished;
00343 };
00344
00345 template<class T>
00346 class LogBlock
00347 {
00348 public:
00349 LogBlock(
00350 Crazyflie* cf,
00351 std::list<std::pair<std::string, std::string> > variables,
00352 std::function<void(uint32_t, T*)>& callback)
00353 : m_cf(cf)
00354 , m_callback(callback)
00355 , m_id(0)
00356 {
00357 m_id = m_cf->registerLogBlock([=](crtpLogDataResponse* r, uint8_t s) { this->handleData(r, s);});
00358 crtpLogCreateBlockRequest request;
00359 request.id = m_id;
00360 int i = 0;
00361 for (auto&& pair : variables) {
00362 const Crazyflie::LogTocEntry* entry = m_cf->getLogTocEntry(pair.first, pair.second);
00363 if (entry) {
00364 request.items[i].logType = entry->type;
00365 request.items[i].id = entry->id;
00366 ++i;
00367 }
00368 else {
00369 std::stringstream sstr;
00370 sstr << "Could not find " << pair.first << "." << pair.second << " in log toc!";
00371 throw std::runtime_error(sstr.str());
00372 }
00373 }
00374
00375 m_cf->startBatchRequest();
00376 m_cf->addRequest(reinterpret_cast<const uint8_t*>(&request), 3 + 2*i, 2);
00377 m_cf->handleRequests();
00378 auto r = m_cf->getRequestResult<crtpLogControlResponse>(0);
00379 if (r->result != crtpLogControlResultOk
00380 && r->result != crtpLogControlResultBlockExists) {
00381 std::stringstream sstr;
00382 sstr << "Could not create log block! Result: " << (int)r->result << " " << (int)m_id << " " << (int)r->requestByte1 << " " << (int)r->command;
00383 throw std::runtime_error(sstr.str());
00384 }
00385 }
00386
00387 ~LogBlock()
00388 {
00389 stop();
00390 m_cf->unregisterLogBlock(m_id);
00391 }
00392
00393
00394 void start(uint8_t period)
00395 {
00396 crtpLogStartRequest request(m_id, period);
00397 m_cf->startBatchRequest();
00398 m_cf->addRequest(request, 2);
00399 m_cf->handleRequests();
00400 }
00401
00402 void stop()
00403 {
00404 crtpLogStopRequest request(m_id);
00405 m_cf->startBatchRequest();
00406 m_cf->addRequest(request, 2);
00407 m_cf->handleRequests();
00408 }
00409
00410 private:
00411 void handleData(crtpLogDataResponse* response, uint8_t size) {
00412 if (size == sizeof(T)) {
00413 uint32_t time_in_ms = ((uint32_t)response->timestampHi << 8) | (response->timestampLo);
00414 T* t = (T*)response->data;
00415 m_callback(time_in_ms, t);
00416 }
00417 else {
00418 throw std::runtime_error("Size doesn't match!");
00419 }
00420 }
00421
00422 private:
00423 Crazyflie* m_cf;
00424 std::function<void(uint32_t, T*)> m_callback;
00425 uint8_t m_id;
00426 };
00427
00429
00430 class LogBlockGeneric
00431 {
00432 public:
00433 LogBlockGeneric(
00434 Crazyflie* cf,
00435 const std::vector<std::string>& variables,
00436 void* userData,
00437 std::function<void(uint32_t, std::vector<double>*, void* userData)>& callback)
00438 : m_cf(cf)
00439 , m_userData(userData)
00440 , m_callback(callback)
00441 , m_id(0)
00442 {
00443 m_id = m_cf->registerLogBlock([=](crtpLogDataResponse* r, uint8_t s) { this->handleData(r, s);});
00444 crtpLogCreateBlockRequest request;
00445 request.id = m_id;
00446 int i = 0;
00447 size_t s = 0;
00448 for (auto&& var : variables) {
00449 auto pos = var.find(".");
00450 std::string first = var.substr(0, pos);
00451 std::string second = var.substr(pos+1);
00452 const Crazyflie::LogTocEntry* entry = m_cf->getLogTocEntry(first, second);
00453 if (entry) {
00454 s += Crazyflie::size(entry->type);
00455 if (s > 26) {
00456 std::stringstream sstr;
00457 sstr << "Can't configure that many variables in a single log block!"
00458 << " Ignoring " << first << "." << second << std::endl;
00459 throw std::runtime_error(sstr.str());
00460 } else {
00461 request.items[i].logType = entry->type;
00462 request.items[i].id = entry->id;
00463 ++i;
00464 m_types.push_back(entry->type);
00465 }
00466 }
00467 else {
00468 std::stringstream sstr;
00469 sstr << "Could not find " << first << "." << second << " in log toc!";
00470 throw std::runtime_error(sstr.str());
00471 }
00472 }
00473 m_cf->startBatchRequest();
00474 m_cf->addRequest(reinterpret_cast<const uint8_t*>(&request), 3 + 2*i, 2);
00475 m_cf->handleRequests();
00476 auto r = m_cf->getRequestResult<crtpLogControlResponse>(0);
00477 if (r->result != crtpLogControlResultOk
00478 && r->result != crtpLogControlResultBlockExists) {
00479 throw std::runtime_error("Could not create log block!");
00480 }
00481 }
00482
00483 ~LogBlockGeneric()
00484 {
00485 stop();
00486 m_cf->unregisterLogBlock(m_id);
00487 }
00488
00489
00490 void start(uint8_t period)
00491 {
00492 crtpLogStartRequest request(m_id, period);
00493 m_cf->startBatchRequest();
00494 m_cf->addRequest(request, 2);
00495 m_cf->handleRequests();
00496 }
00497
00498 void stop()
00499 {
00500 crtpLogStopRequest request(m_id);
00501 m_cf->startBatchRequest();
00502 m_cf->addRequest(request, 2);
00503 m_cf->handleRequests();
00504 }
00505
00506 private:
00507 void handleData(crtpLogDataResponse* response, uint8_t size) {
00508
00509 std::vector<double> result;
00510 size_t pos = 0;
00511 for (size_t i = 0; i < m_types.size(); ++i)
00512 {
00513 switch (m_types[i])
00514 {
00515 case Crazyflie::LogTypeUint8:
00516 {
00517 uint8_t value;
00518 memcpy(&value, &response->data[pos], sizeof(uint8_t));
00519 result.push_back(value);
00520 pos += sizeof(uint8_t);
00521 break;
00522 }
00523 case Crazyflie::LogTypeInt8:
00524 {
00525 int8_t value;
00526 memcpy(&value, &response->data[pos], sizeof(int8_t));
00527 result.push_back(value);
00528 pos += sizeof(int8_t);
00529 break;
00530 }
00531 case Crazyflie::LogTypeUint16:
00532 {
00533 uint16_t value;
00534 memcpy(&value, &response->data[pos], sizeof(uint16_t));
00535 result.push_back(value);
00536 pos += sizeof(uint16_t);
00537 break;
00538 }
00539 case Crazyflie::LogTypeInt16:
00540 {
00541 int16_t value;
00542 memcpy(&value, &response->data[pos], sizeof(int16_t));
00543 result.push_back(value);
00544 pos += sizeof(int16_t);
00545 break;
00546 }
00547 case Crazyflie::LogTypeUint32:
00548 {
00549 uint32_t value;
00550 memcpy(&value, &response->data[pos], sizeof(uint32_t));
00551 result.push_back(value);
00552 pos += sizeof(uint32_t);
00553 break;
00554 }
00555 case Crazyflie::LogTypeInt32:
00556 {
00557 int32_t value;
00558 memcpy(&value, &response->data[pos], sizeof(int32_t));
00559 result.push_back(value);
00560 pos += sizeof(int32_t);
00561 break;
00562 }
00563 case Crazyflie::LogTypeFloat:
00564 {
00565 float value;
00566 memcpy(&value, &response->data[pos], sizeof(float));
00567 result.push_back(value);
00568 pos += sizeof(float);
00569 break;
00570 }
00571 case Crazyflie::LogTypeFP16:
00572 {
00573 double value;
00574 memcpy(&value, &response->data[pos], sizeof(double));
00575 result.push_back(value);
00576 pos += sizeof(double);
00577 break;
00578 }
00579 }
00580 }
00581
00582 uint32_t time_in_ms = ((uint32_t)response->timestampHi << 8) | (response->timestampLo);
00583 m_callback(time_in_ms, &result, m_userData);
00584 }
00585
00586 private:
00587 Crazyflie* m_cf;
00588 void* m_userData;
00589 std::function<void(uint32_t, std::vector<double>*, void*)> m_callback;
00590 uint8_t m_id;
00591 std::vector<Crazyflie::LogType> m_types;
00592 };