9 #include "Crazyradio.h" 10 #include "CrazyflieUSB.h" 33 const std::string& link_uri,
35 std::function<
void(
const char*)> consoleCb)
37 , m_transport(nullptr)
46 , m_emptyAckCallback(nullptr)
47 , m_linkQualityCallback(nullptr)
48 , m_consoleCallback(consoleCb)
50 , m_param_use_V2(false)
58 success = std::sscanf(link_uri.c_str(),
"radio://%d/%d/%d%c/%" SCNx64,
60 &datarateType, &m_address) == 5;
62 success = std::sscanf(link_uri.c_str(),
"radio://%d/%d/%d%c",
65 m_address = 0xE7E7E7E7E7;
71 if (datarate == 250 && datarateType ==
'K') {
74 else if (datarate == 1 && datarateType ==
'M') {
77 else if (datarate == 2 && datarateType ==
'M') {
82 throw std::runtime_error(
"This version does not support that many radios. Adjust MAX_RADIOS and recompile!");
86 std::unique_lock<std::mutex> mlock(
g_radioMutex[m_devId]);
87 if (!g_crazyradios[m_devId]) {
88 g_crazyradios[m_devId] =
new Crazyradio(m_devId);
92 g_crazyradios[m_devId]->
setArc(0);
96 m_radio = g_crazyradios[m_devId];
99 success = std::sscanf(link_uri.c_str(),
"usb://%d",
103 throw std::runtime_error(
"This version does not support that many CFs over USB. Adjust MAX_USB and recompile!");
108 if (!g_crazyflieUSB[m_devId]) {
114 m_transport = g_crazyflieUSB[m_devId];
118 throw std::runtime_error(
"Uri is not valid!");
124 sendPacketOrTimeout(request,
false);
130 m_protocolVersion = -1;
140 return getRequestResult<crtpGetProtocolVersionResponse>(0)->
version;
149 return std::string(getRequestResult<crtpGetFirmwareVersionResponse>(0)->
version);
158 return std::string(getRequestResult<crtpGetDeviceTypeNameResponse>(0)->
name);
175 crtpSetpointRequest
request(roll, pitch, yawrate, thrust);
218 float x,
float y,
float z,
219 float vx,
float vy,
float vz,
220 float ax,
float ay,
float az,
221 float qx,
float qy,
float qz,
float qw,
222 float rollRate,
float pitchRate,
float yawRate)
229 rollRate, pitchRate, yawRate);
234 float x,
float y,
float z,
float yawRate)
257 float x,
float y,
float z,
258 float qx,
float qy,
float qz,
float qw)
277 std::vector<crtpPacket_t>::iterator it;
308 ((uint64_t)response->
addr[0] << 0)
309 | ((uint64_t)response->
addr[1] << 8)
310 | ((uint64_t)response->
addr[2] << 16)
311 | ((uint64_t)response->
addr[3] << 24)
312 | ((uint64_t)0xb1 << 32);
368 return getRequestResult<crtpNrf51GetVBatResponse>(0)->
vbat;
376 const std::vector<uint8_t>&
data)
388 uint16_t numPages = ceil(data.size() / (float)pageSize);
389 if (numPages + flashStart >= response->
nFlashPage) {
390 std::stringstream sstr;
391 sstr <<
"Requested size too large!";
392 throw std::runtime_error(sstr.str());
395 std::stringstream sstr;
396 sstr <<
"pageSize: " << pageSize
397 <<
" nBuffPage: " << nBuffPage
399 <<
" flashStart: " << flashStart
400 <<
" version: " << (int)response->
version 401 <<
" numPages: " << numPages;
406 uint16_t usedBuffers = 0;
409 std::stringstream sstr;
410 sstr <<
"page: " <<
page - flashStart + 1 <<
" / " << numPages;
416 size_t requestedSize = std::min<size_t>(data.size() -
offset, std::min<size_t>(25, pageSize -
address));
417 memcpy(req.
data, &data[offset], requestedSize);
440 offset += requestedSize;
441 if (offset >= data.size()) {
446 if (usedBuffers == nBuffPage
447 ||
page == numPages + flashStart - 1) {
472 auto start = std::chrono::system_clock::now();
481 && memcmp(&req, ack.
data, 3) == 0) {
482 if (ack.
data[3] != 1 || ack.
data[4] != 0) {
483 throw std::runtime_error(
"Error during flashing!");
497 auto end = std::chrono::system_clock::now();
498 std::chrono::duration<double> elapsedSeconds = end-start;
499 if (elapsedSeconds.count() > 0.5) {
504 throw std::runtime_error(
"timeout");
525 std::vector<uint8_t>& data)
536 uint16_t numPages = ceil(size / (
float)pageSize);
537 if (numPages + flashStart >= response->
nFlashPage) {
538 std::stringstream sstr;
539 sstr <<
"Requested size too large!";
540 throw std::runtime_error(sstr.str());
543 std::stringstream sstr;
544 sstr <<
"pageSize: " << pageSize
546 <<
" flashStart: " << flashStart
547 <<
" version: " << (int)response->
version 548 <<
" numPages: " << numPages;
559 size_t requestedSize = std::min(25, pageSize -
address);
560 offset += requestedSize;
575 size_t requestedSize = std::min(25, pageSize -
address);
577 memcpy(&data[offset], response->
data, std::min(size - offset, requestedSize));
578 offset += requestedSize;
602 len = getRequestResult<crtpLogGetInfoV2Response>(0)->
log_len;
603 crc = getRequestResult<crtpLogGetInfoV2Response>(0)->
log_crc;
612 len = getRequestResult<crtpLogGetInfoResponse>(0)->
log_len;
613 crc = getRequestResult<crtpLogGetInfoResponse>(0)->
log_crc;
618 std::string fileName =
"log" + std::to_string(crc) +
".csv";
619 std::ifstream infile(fileName);
621 if (forceNoCache || !infile.good()) {
627 for (
size_t i = 0; i < len; ++i) {
632 for (
size_t i = 0; i < len; ++i) {
642 for (
size_t i = 0; i < len; ++i) {
643 auto response = getRequestResult<crtpLogGetItemV2Response>(i);
646 entry.type = (
LogType)response->type;
647 entry.group = std::string(&response->text[0]);
648 entry.name = std::string(&response->text[entry.group.size() + 1]);
651 for (
size_t i = 0; i < len; ++i) {
652 auto response = getRequestResult<crtpLogGetItemResponse>(i);
655 entry.type = (
LogType)response->type;
656 entry.group = std::string(&response->text[0]);
657 entry.name = std::string(&response->text[entry.group.size() + 1]);
664 std::string fileNameTemp = fileName +
".tmp";
665 std::ofstream output(fileNameTemp);
666 output <<
"id,type,group,name" << std::endl;
668 output << std::to_string(entry.id) <<
"," 669 << std::to_string(entry.type) <<
"," 670 << entry.group <<
"," 671 << entry.name << std::endl;
674 rename(fileNameTemp.c_str(), fileName.c_str());
679 std::string line, cell;
680 std::getline(infile, line);
681 while (std::getline(infile, line)) {
682 std::stringstream lineStream(line);
684 std::getline(lineStream, cell,
',');
686 std::getline(lineStream, cell,
',');
688 std::getline(lineStream, cell,
',');
690 std::getline(lineStream, cell,
',');
714 numParam = getRequestResult<crtpParamTocGetInfoV2Response>(0)->numParam;
715 crc = getRequestResult<crtpParamTocGetInfoV2Response>(0)->crc;
724 numParam = getRequestResult<crtpParamTocGetInfoResponse>(0)->numParam;
725 crc = getRequestResult<crtpParamTocGetInfoResponse>(0)->crc;
729 std::string fileName =
"params" + std::to_string(crc) +
".csv";
730 std::ifstream infile(fileName);
732 if (forceNoCache || !infile.good()) {
738 for (uint16_t i = 0; i <
numParam; ++i) {
745 for (uint16_t i = 0; i <
numParam; ++i) {
757 for (uint16_t i = 0; i <
numParam; ++i) {
758 auto r = getRequestResult<crtpParamTocGetItemResponse>(i*2+0);
759 auto val = getRequestResult<crtpParamValueResponse>(i*2+1);
763 entry.type = (
ParamType)(r->length | r->
type << 2 | r->sign << 3);
764 entry.readonly = r->readonly;
765 entry.group = std::string(&r->text[0]);
766 entry.name = std::string(&r->text[entry.group.size() + 1]);
769 std::memcpy(&v, &val->valueFloat, 4);
773 for (uint16_t i = 0; i <
numParam; ++i) {
774 auto r = getRequestResult<crtpParamTocGetItemV2Response>(i*2+0);
775 auto val = getRequestResult<crtpParamValueV2Response>(i*2+1);
779 entry.type = (
ParamType)(r->length | r->
type << 2 | r->sign << 3);
780 entry.readonly = r->readonly;
781 entry.group = std::string(&r->text[0]);
782 entry.name = std::string(&r->text[entry.group.size() + 1]);
785 std::memcpy(&v, &val->valueFloat, 4);
793 std::string fileNameTemp = fileName +
".tmp";
794 std::ofstream output(fileNameTemp);
795 output <<
"id,type,readonly,group,name" << std::endl;
797 output << std::to_string(entry.id) <<
"," 798 << std::to_string(entry.type) <<
"," 799 << std::to_string(entry.readonly) <<
"," 800 << entry.group <<
"," 801 << entry.name << std::endl;
804 rename(fileNameTemp.c_str(), fileName.c_str());
809 std::string line, cell;
810 std::getline(infile, line);
811 while (std::getline(infile, line)) {
812 std::stringstream lineStream(line);
814 std::getline(lineStream, cell,
',');
816 std::getline(lineStream, cell,
',');
818 std::getline(lineStream, cell,
',');
820 std::getline(lineStream, cell,
',');
822 std::getline(lineStream, cell,
',');
829 for (
size_t i = 0; i <
numParam; ++i) {
834 for (
size_t i = 0; i <
numParam; ++i) {
835 auto val = getRequestResult<crtpParamValueResponse>(i);
837 std::memcpy(&v, &val->valueFloat, 4);
842 for (
size_t i = 0; i <
numParam; ++i) {
847 for (
size_t i = 0; i <
numParam; ++i) {
848 auto val = getRequestResult<crtpParamValueV2Response>(i);
850 std::memcpy(&v, &val->valueFloat, 4);
864 uint8_t len = getRequestResult<crtpMemoryGetNumberResponse>(0)->
numberOfMemories;
870 for (uint8_t i = 0; i < len; ++i) {
878 for (uint8_t i = 0; i < len; ++i) {
879 auto info = getRequestResult<crtpMemoryGetInfoResponse>(i);
884 entry.size = info->memSize;
885 entry.addr = info->memAddr;
898 if (entry.id ==
id) {
901 switch (entry.type) {
946 switch (entry.type) {
995 std::stringstream sstr;
996 sstr <<
"Could not find parameter with id " <<
id;
997 throw std::runtime_error(sstr.str());
1016 const uint8_t* data,
1026 const uint8_t* data,
1031 auto start = std::chrono::system_clock::now();
1033 auto end = std::chrono::system_clock::now();
1034 std::chrono::duration<double> elapsedSeconds = end-start;
1035 if (elapsedSeconds.count() > timeout) {
1036 throw std::runtime_error(
"timeout");
1042 const uint8_t* data,
1047 static uint32_t numPackets = 0;
1048 static uint32_t numAcks = 0;
1072 std::vector<uint8_t> dataCopy(data, data + length);
1073 dataCopy[0] &= 0xF3;
1095 if (numPackets == 100) {
1099 float linkQuality = numAcks / (float)numPackets;
1111 if (result.
size > 0) {
1138 iter->second(r, result.
size - 5);
1177 else if (
crtp(result.
data[0]).port == 8) {
1180 else if (
crtp(result.
data[0]).port == 13) {
1184 if (result.
size >= 3) {
1194 +
" Channel: " + std::to_string((
int)header->
channel)
1195 +
" Len: " + std::to_string((
int)result.
size));
1206 const std::string&
group,
1207 const std::string&
name)
const 1210 if (entry.group == group && entry.name == name) {
1218 const std::string& group,
1219 const std::string& name)
const 1222 if (entry.group == group && entry.name == name) {
1232 for (uint8_t
id = 0;
id < 255; ++
id) {
1256 const uint8_t* data,
1258 size_t numBytesToMatch)
1271 float timePerRequest)
1273 auto start = std::chrono::system_clock::now();
1278 const size_t queueSize = 16;
1288 size_t remainingRequests = numRequests;
1289 size_t requestIdx = 0;
1291 while (remainingRequests > 0) {
1302 auto end = std::chrono::system_clock::now();
1303 std::chrono::duration<double> elapsedSeconds = end-start;
1304 if (elapsedSeconds.count() > timeout) {
1305 throw std::runtime_error(
"timeout");
1313 || (m_numRequestsFinished < numRequests && requestIdx == numRequests)) {
1318 auto end = std::chrono::system_clock::now();
1319 std::chrono::duration<double> elapsedSeconds = end-start;
1320 if (elapsedSeconds.count() > timeout) {
1321 throw std::runtime_error(
"timeout");
1328 if (!crtpMode || !sendPing) {
1334 auto end = std::chrono::system_clock::now();
1335 std::chrono::duration<double> elapsedSeconds = end-start;
1336 if (elapsedSeconds.count() > timeout) {
1337 throw std::runtime_error(
"timeout");
1341 if (
m_radio && !useSafeLink) {
1346 for (
size_t i = 0; i < remainingRequests; ++i) {
1354 auto end = std::chrono::system_clock::now();
1355 std::chrono::duration<double> elapsedSeconds = end-start;
1356 if (elapsedSeconds.count() > timeout) {
1357 throw std::runtime_error(
"timeout");
1437 uint32_t pieceOffset,
1438 const std::vector<poly4d>& pieces)
1444 size_t remainingBytes =
sizeof(poly4d) * pieces.size();
1445 size_t numRequests = ceil(remainingBytes / 24.0f);
1446 for (
size_t i = 0; i < numRequests; ++i) {
1448 size_t size = std::min<size_t>(remainingBytes, 24);
1449 memcpy(req.
data, reinterpret_cast<const uint8_t*>(pieces.data()) + i * 24, size);
1450 remainingBytes -=
size;
1464 throw std::runtime_error(
"Could not find MemoryTypeTRAJ!");
1468 uint8_t trajectoryId,
1479 std::vector<uint8_t>& data)
1484 size_t remainingBytes = entry.size;
1485 size_t numRequests = ceil(remainingBytes / 24.0f);
1486 for (
size_t i = 0; i < numRequests; ++i) {
1487 size_t size = std::min<size_t>(remainingBytes, 24);
1489 remainingBytes -=
size;
1494 data.resize(entry.size);
1495 remainingBytes = entry.size;
1496 for (
size_t i = 0; i < numRequests; ++i) {
1497 size_t size = std::min<size_t>(remainingBytes, 24);
1499 memcpy(&data[i*24], response->data, size);
1500 remainingBytes -=
size;
1505 throw std::runtime_error(
"Could not find MemoryTypeUSD!");
1511 const std::string& link_uri)
1521 bool success =
false;
1523 success = std::sscanf(link_uri.c_str(),
"radio://%d/%d/%d%c/%" SCNx64,
1527 success = std::sscanf(link_uri.c_str(),
"radio://%d/%d/%d%c",
1529 &datarateType) == 4;
1536 if (datarate == 250 && datarateType ==
'K') {
1539 else if (datarate == 1 && datarateType ==
'M') {
1542 else if (datarate == 2 && datarateType ==
'M') {
1547 throw std::runtime_error(
"This version does not support that many radios. Adjust MAX_RADIOS and recompile!");
1551 std::unique_lock<std::mutex> mlock(
g_radioMutex[m_devId]);
1552 if (!g_crazyradios[m_devId]) {
1564 throw std::runtime_error(
"Uri is not valid!");
1569 const uint8_t* data,
1589 const uint8_t* data,
1636 uint8_t trajectoryId,
1646 const std::vector<externalPosition>& data)
1648 if (data.size() == 0) {
1652 std::vector<crtpExternalPositionPacked> requests(ceil(data.size() / 4.0));
1653 for (
size_t i = 0; i < data.size(); ++i) {
1655 requests[j].positions[i%4].id = data[i].id;
1656 requests[j].positions[i%4].x = data[i].x * 1000;
1657 requests[j].positions[i%4].y = data[i].y * 1000;
1658 requests[j].positions[i%4].z = data[i].z * 1000;
1661 size_t numBytes = requests.size() + data.size() * 7;
1663 size_t remainingRequests = requests.size();
1665 while (remainingRequests > 0) {
1668 if ( remainingRequests >= 2
1671 send2Packets(reinterpret_cast<const uint8_t*>(&requests[i]), size);
1672 remainingRequests -= 2;
1677 sendPacket(reinterpret_cast<const uint8_t*>(&requests[i]), size);
1678 remainingRequests -= 1;
1702 unsigned i_largest = 0;
1703 for (
unsigned i = 1; i < 4; ++i) {
1704 if (fabsf(q[i]) > fabsf(q[i_largest])) {
1712 unsigned negate = q[i_largest] < 0;
1718 uint32_t comp = i_largest;
1719 for (
unsigned i = 0; i < 4; ++i) {
1720 if (i != i_largest) {
1721 unsigned negbit = (q[i] < 0) ^ negate;
1722 unsigned mag = ((1 << 9) - 1) * (fabsf(q[i]) / (float)M_SQRT1_2) + 0.5f;
1723 comp = (comp << 10) | (negbit << 9) | mag;
1731 const std::vector<externalPose>& data)
1733 if (data.size() == 0) {
1737 std::vector<crtpExternalPosePacked> requests(ceil(data.size() / 2.0));
1738 for (
size_t i = 0; i < data.size(); ++i) {
1740 requests[j].poses[i%2].id = data[i].id;
1741 requests[j].poses[i%2].x = data[i].x * 1000;
1742 requests[j].poses[i%2].y = data[i].y * 1000;
1743 requests[j].poses[i%2].z = data[i].z * 1000;
1744 float q[4] = { data[i].qx, data[i].qy, data[i].qz, data[i].qw };
1748 size_t numBytes = requests.size() * 2 + data.size() * 11;
1750 size_t remainingRequests = requests.size();
1752 while (remainingRequests > 0) {
1755 if ( remainingRequests >= 2
1758 send2Packets(reinterpret_cast<const uint8_t*>(&requests[i]), size);
1759 remainingRequests -= 2;
1764 sendPacket(reinterpret_cast<const uint8_t*>(&requests[i]), size);
1765 remainingRequests -= 1;
static const int MAX_RADIOS
void requestLogToc(bool forceNoCache=false)
std::vector< crtpPacket_t > m_outgoing_packets
std::vector< batchRequest > m_batchRequests
bool getAckEnable() const
void startTrajectory(uint8_t trajectoryId, float timescale=1.0, bool reversed=false, uint8_t groupMask=0)
void emergencyStopWatchdog()
void sendExternalPoses(const std::vector< externalPose > &data)
std::vector< MemoryTocEntry > m_memoryTocEntries
void sendFullStateSetpoint(float x, float y, float z, float vx, float vy, float vz, float ax, float ay, float az, float qx, float qy, float qz, float qw, float rollRate, float pitchRate, float yawRate)
const ParamTocEntry * getParamTocEntry(const std::string &group, const std::string &name) const
void setRequestedParams()
uint32_t remainValidMillisecs
void sendExternalPositions(const std::vector< externalPosition > &data)
void readFlash(BootloaderTarget target, size_t size, std::vector< uint8_t > &data)
std::string getDeviceTypeName()
void sendHoverSetpoint(float vx, float vy, float yawrate, float zDistance)
bool unregisterLogBlock(uint8_t id)
void setAckEnable(bool enable)
void requestParamToc(bool forceNoCache=false)
void startTrajectory(uint8_t trajectoryId, float timescale=1.0, bool reversed=false, bool relative=true, uint8_t groupMask=0)
crtpParamTocGetItemRequest request
static bool match(const Crazyradio::Ack &response)
void writeFlash(BootloaderTarget target, const std::vector< uint8_t > &data)
CrazyflieUSB * g_crazyflieUSB[MAX_USB]
void handleRequests(bool crtpMode=true, bool useSafeLink=ENABLE_SAFELINK, float baseTime=2.0, float timePerRequest=0.2)
virtual void sendPacket(const uint8_t *data, uint32_t length, Ack &result)=0
virtual void sendPacket(const uint8_t *data, uint32_t length, ITransport::Ack &result)
void notifySetpointsStop(uint32_t remainValidMillisecs)
void sendVelocityWorldSetpoint(float x, float y, float z, float yawRate)
std::mutex g_radioMutex[MAX_RADIOS]
void emergencyStopWatchdog()
static bool match(const Crazyradio::Ack &response)
void uploadTrajectory(uint8_t trajectoryId, uint32_t pieceOffset, const std::vector< poly4d > &pieces)
void setAddress(uint64_t address)
const LogTocEntry * getLogTocEntry(const std::string &group, const std::string &name) const
static bool match(const Crazyradio::Ack &response)
constexpr crtp(uint8_t port, uint8_t channel)
static bool match(const Crazyradio::Ack &response)
std::function< void(const crtpPlatformRSSIAck *)> m_emptyAckCallback
void sendPacket(const R &request, ITransport::Ack &result, bool useSafeLink=ENABLE_SAFELINK)
void goTo(float x, float y, float z, float yaw, float duration, uint8_t groupMask=0)
void handleAck(const ITransport::Ack &result)
void stop(uint8_t groupMask=0)
void rebootFromBootloader()
static bool match(const Crazyradio::Ack &response)
std::mutex g_crazyflieusbMutex[MAX_USB]
static uint32_t quatcompress(float const q[4])
static bool match(const Crazyradio::Ack &response)
static const bool LOG_COMMUNICATION
size_t m_numRequestsEnqueued
static bool match(const Crazyradio::Ack &response)
void setParam(uint16_t id, const T &value)
void sendPacketOrTimeoutInternal(const uint8_t *data, uint32_t length, bool useSafeLink=ENABLE_SAFELINK, float timeout=1.0)
uint8_t trajectoryLocation
std::map< uint8_t, std::function< void(crtpLogDataResponse *, uint8_t)> > m_logBlockCb
static bool match(const Crazyradio::Ack &response)
CrazyflieBroadcaster(const std::string &link_uri)
void send2Packets(const uint8_t *data, uint32_t length)
virtual void sendPacketNoAck(const uint8_t *data, uint32_t length)
uint64_t getAddress() const
static bool match(const Crazyradio::Ack &response)
static bool match(const Crazyradio::Ack &response)
struct trajectoryDescription description
Datarate getDatarate() const
void sendExternalPositionUpdate(float x, float y, float z)
uint8_t getChannel() const
static bool match(const Crazyradio::Ack &response)
void sendExternalPoseUpdate(float x, float y, float z, float qx, float qy, float qz, float qw)
std::string getFirmwareVersion()
Crazyradio * g_crazyradios[MAX_RADIOS]
void handleBatchAck(const ITransport::Ack &result, bool crtpMode)
void addRequestInternal(const uint8_t *data, size_t numBytes, size_t numBytesToMatch)
void readUSDLogFile(std::vector< uint8_t > &data)
void startSetParamRequest()
void land(float height, float duration, uint8_t groupMask=0)
void sendPacketInternal(const uint8_t *data, uint32_t length, ITransport::Ack &result, bool useSafeLink=ENABLE_SAFELINK)
void setDatarate(Datarate datarate)
size_t m_numRequestsFinished
virtual void info(const std::string &)
void setGroupMask(uint8_t groupMask)
uint8_t registerLogBlock(std::function< void(crtpLogDataResponse *, uint8_t)> cb)
virtual void warning(const std::string &)
void sendPacket(const uint8_t *data, uint32_t length)
void stop(uint8_t groupMask=0)
void sendSetpoint(float roll, float pitch, float yawrate, uint16_t thrust)
std::vector< LogTocEntry > m_logTocEntries
void enableLogging(bool enable)
union trajectoryDescription::@8 trajectoryIdentifier
static bool match(const Crazyradio::Ack &response)
std::vector< ParamTocEntry > m_paramTocEntries
void land(float height, float duration, uint8_t groupMask=0)
static bool match(const Crazyradio::Ack &response)
static bool match(const Crazyradio::Ack &response)
static bool match(const Crazyradio::Ack &response)
static bool match(const Crazyradio::Ack &response)
static size_t size(LogType t)
Crazyradio::Datarate m_datarate
virtual void send2PacketsNoAck(const uint8_t *data, uint32_t totalLength)
void addRequest(const R &request, size_t numBytesToMatch)
uint64_t rebootToBootloader()
void sendPositionSetpoint(float x, float y, float z, float yaw)
Crazyflie(const std::string &link_uri, Logger &logger=EmptyLogger, std::function< void(const char *)> consoleCb=nullptr)
std::map< uint16_t, ParamValue > m_paramValues
std::function< void(const char *)> m_consoleCallback
std::function< void(float)> m_linkQualityCallback
void sendPacketOrTimeout(const R &request, bool useSafeLink=ENABLE_SAFELINK)
void takeoff(float height, float duration, uint8_t groupMask=0)
void addSetParam(uint16_t id, const T &value)
std::function< void(const ITransport::Ack &)> m_genericPacketCallback
void goTo(float x, float y, float z, float yaw, float duration, bool relative=false, uint8_t groupMask=0)
void setChannel(uint8_t channel)
void takeoff(float height, float duration, uint8_t groupMask=0)