From d627b7e41fc1d8ca04e2a59ae18172ed3770f7bf Mon Sep 17 00:00:00 2001
From: zeisele <zeisele@iastate.edu>
Date: Thu, 10 Mar 2022 10:47:21 +0100
Subject: [PATCH] fix get/set on backend and implement on gui

---
 crazyflie_groundstation/src/CTOC.cpp          |  20 +-
 .../src/ccrazyflie/CCrazyflie_commands.cpp    |  67 +++---
 .../ccrazyflie/CCrazyflie_loggingFuncs.cpp    |   5 +-
 .../gui/MicroCART/crazyflieworker.cpp         | 182 ++++++++++++++-
 groundStation/gui/MicroCART/crazyflieworker.h |  34 ++-
 groundStation/gui/MicroCART/mainwindow.cpp    | 127 +++++++++-
 groundStation/gui/MicroCART/mainwindow.h      |  15 ++
 groundStation/gui/MicroCART/mainwindow.ui     | 219 +++++++++++++++++-
 groundStation/src/backend/param.c             |  31 +--
 9 files changed, 626 insertions(+), 74 deletions(-)

diff --git a/crazyflie_groundstation/src/CTOC.cpp b/crazyflie_groundstation/src/CTOC.cpp
index d60940447..bf93a6fb9 100644
--- a/crazyflie_groundstation/src/CTOC.cpp
+++ b/crazyflie_groundstation/src/CTOC.cpp
@@ -30,10 +30,10 @@
 
 #include <limits>
 
-static const char *param_header = "Param ID\tType\t\tGroup\t\t"
+static const char *param_header = "Param ID\tType\tGroup\t"
 						   "Identifier Name";
 
-static const char *log_header = "Log ID\tType\t\tGroup\t\t"
+static const char *log_header = "Log ID\tType\tGroup\t"
 						   "Identifier Name";
 
 CTOC::CTOC(CCrazyRadio *crRadio, CCrazyflie *crazyflie, int radioChannel, int nPort) {
@@ -172,6 +172,7 @@ bool CTOC::downloadTOC() {
 			printf("Parameter Progress: %i / %i\n", nI+1, m_nItemCount);
 			this->requestItem(nI);
 		}
+		param_toc_file.close();
 	}
 	else {
 		this->requestMetaData();
@@ -179,6 +180,7 @@ bool CTOC::downloadTOC() {
 			printf("Logging Progress: %i / %i\n", nI+1, m_nItemCount);
 			this->requestItem(nI);
 		}
+		log_toc_file.close();
 	}
 	if( ALL_THE_DEBUG ) printf( "Exit: %s\n", __FUNCTION__);
 	return true;
@@ -219,10 +221,10 @@ bool CTOC::processItem(CCRTPPacket* crtpItem) {
 				if(ALL_THE_DEBUG) printf("Group.Name: %s.%s ID: %i Type: %i ", strGroup.c_str(), strIdentifier.c_str(), nID, nType);
 
 				if(m_nPort == 0x02) {
-					this->param_toc_file << teNew.nID << "\t\t" << teNew.nType << "\t\t" << teNew.strGroup << "\t\t" << teNew.strIdentifier << std::endl;
+					this->param_toc_file << teNew.nID << "\t" << teNew.nType << "\t" << teNew.strGroup << "\t" << teNew.strIdentifier << std::endl;
 				}
 				else if(m_nPort = 0x05) {
-					this->log_toc_file << teNew.nID << "\t\t" << teNew.nType << "\t\t" << teNew.strGroup << "\t\t" << teNew.strIdentifier << std::endl;
+					this->log_toc_file << teNew.nID << "\t" << teNew.nType << "\t" << teNew.strGroup << "\t" << teNew.strIdentifier << std::endl;
 				}
 
 				m_lstTOCElements.push_back(teNew);
@@ -945,8 +947,9 @@ void CTOC::openParamTOCFile(char *baseFileName) {
 	printf("Opening param toc file %s  for quadcopter %d...", logFileName, (this->m_crazyflie->getQuadcopterNumber() + 1));
 
 	try {
-		char dir[250];
-		getcwd(dir, 250);
+		char dir[256];
+		dir[0] = '/';
+		getcwd(&dir[1], 255);
 		std::string fullFilePath(dir);
 		fullFilePath += "/";
 		fullFilePath += logFileName;
@@ -985,8 +988,9 @@ void CTOC::openLogTOCFile(char *baseFileName) {
 	printf("Opening param toc file %s  for quadcopter %d...", logFileName, (this->m_crazyflie->getQuadcopterNumber() + 1));
 
 	try {
-		char dir[250];
-		getcwd(dir, 250);
+		char dir[256];
+		dir[0] = '/';
+		getcwd(&dir[1], 255);
 		std::string fullFilePath(dir);
 		fullFilePath += "/";
 		fullFilePath += logFileName;
diff --git a/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_commands.cpp b/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_commands.cpp
index 7916a1100..bc0a76965 100644
--- a/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_commands.cpp
+++ b/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_commands.cpp
@@ -179,7 +179,7 @@ bool CCrazyflie::parseReceivedUserCommand() {
 	
 	case ACMD_GETPARAM:
 		{
-			int nSize = 1;
+			int nSize = 2;
 			char cBuffer[nSize];
 
 			CCRTPPacket *crtpPacket = new CCRTPPacket(cBuffer, nSize, CRTP_PORT_PARAM);
@@ -187,13 +187,15 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			CCRTPPacket *crtpReceived;
 
 			//int id = (char) command.payload;
-			char id = (char) command.payload[2];
+			//char id = (char) command.payload[2];
+			uint16_t id;
+			memcpy(&id, &command.payload[2], sizeof(uint16_t));
+			memcpy(cBuffer, &command.payload[2], sizeof(uint16_t));
 			//float fvalue = build_float(&command.payload[4]);
 			//uint16_t pvalue = (uint16_t) fvalue;
 			bool bFound = false;
 			struct TOCElement element = this->m_tocParameters->elementForID(id, bFound);
 
-			cBuffer[0] = id;
 			//memcpy(&cBuffer[1 * sizeof(uint16_t)], &pvalue, sizeof(uint16_t));
 			crtpPacket->setData(cBuffer, nSize);
 			crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
@@ -208,7 +210,7 @@ bool CCrazyflie::parseReceivedUserCommand() {
 
 	case ACMD_SETPARAM:
 		{
-		int nSize = sizeof(double) + 1;
+		int nSize = sizeof(double) + 2;
 		char cBuffer[nSize];
 
 		CCRTPPacket *crtpPacket = new CCRTPPacket(cBuffer, nSize, CRTP_PORT_PARAM);
@@ -216,7 +218,10 @@ bool CCrazyflie::parseReceivedUserCommand() {
 		CCRTPPacket *crtpReceived;
 
 		//int id = (char) command.payload;
-		char pid = (char) command.payload[2];
+		//char pid = (char) command.payload[2];
+		uint16_t pid;
+		memcpy(&pid, &command.payload[2], 2);
+		memcpy(cBuffer, &command.payload[2], 2);
 		bool bFound = false;
 		struct TOCElement element = this->m_tocParameters->elementForID(pid, bFound);
 		uint8_t pType = element.nType;
@@ -226,9 +231,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				uint8_t pvalue = (uint8_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(uint8_t));
-				nSize = 1 + sizeof(uint8_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(uint8_t));
+				nSize = 2 + sizeof(uint8_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -237,9 +241,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				uint16_t pvalue = (uint16_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(uint16_t));
-				nSize = 1 + sizeof(uint16_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(uint16_t));
+				nSize = 2 + sizeof(uint16_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -248,9 +251,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				uint32_t pvalue = (uint32_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(uint32_t));
-				nSize = 1 + sizeof(uint32_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(uint32_t));
+				nSize = 2 + sizeof(uint32_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -259,9 +261,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				uint64_t pvalue = (uint64_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(uint64_t));
-				nSize = 1 + sizeof(uint64_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(uint64_t));
+				nSize = 2 + sizeof(uint64_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -270,9 +271,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				int8_t pvalue = (int8_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(int8_t));
-				nSize = 1 + sizeof(int8_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(int8_t));
+				nSize = 2 + sizeof(int8_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -281,9 +281,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				int16_t pvalue = (int16_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(int16_t));
-				nSize = 1 + sizeof(int16_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(int16_t));
+				nSize = 2 + sizeof(int16_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -292,9 +291,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				int32_t pvalue = (uint32_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(int32_t));
-				nSize = 1 + sizeof(int32_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(int32_t));
+				nSize = 2 + sizeof(int32_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -303,9 +301,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				int64_t pvalue = (int64_t) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(int64_t));
-				nSize = 1 + sizeof(int64_t);
+				memcpy(&cBuffer[2], &pvalue, sizeof(int64_t));
+				nSize = 2 + sizeof(int64_t);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -314,9 +311,8 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				float pvalue = fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(float));
-				nSize = 1 + sizeof(float);
+				memcpy(&cBuffer[2], &pvalue, sizeof(float));
+				nSize = 2 + sizeof(float);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
@@ -325,16 +321,13 @@ bool CCrazyflie::parseReceivedUserCommand() {
 			{
 				double pvalue = (double) fvalue;
 
-				cBuffer[0] = pid;
-				memcpy(&cBuffer[1], &pvalue, sizeof(double));
-				nSize = 1 + sizeof(double);
+				memcpy(&cBuffer[2], &pvalue, sizeof(double));
+				nSize = 2 + sizeof(double);
 				crtpPacket->setData(cBuffer, nSize);
 				crtpReceived = m_crRadio->sendAndReceive(crtpPacket, m_nRadioChannel, this);
 				break;
 			}
 		}
-		
-		
 		break;
 		}
 
diff --git a/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_loggingFuncs.cpp b/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_loggingFuncs.cpp
index 293470194..bec1dd0f9 100644
--- a/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_loggingFuncs.cpp
+++ b/crazyflie_groundstation/src/ccrazyflie/CCrazyflie_loggingFuncs.cpp
@@ -52,8 +52,9 @@ void CCrazyflie::openLogFile(char *baseFileName) {
 	printf("Opening log file %s  for quadcopter %d...", logFileName, (m_quadNum + 1));
 
 	try {
-		char dir[250];
-		getcwd(dir, 250);
+		char dir[256];
+		dir[0] = '/';
+		getcwd(&dir[1], 255);
 		std::string fullFilePath(dir);
 		fullFilePath += "/";
 		fullFilePath += logFileName;
diff --git a/groundStation/gui/MicroCART/crazyflieworker.cpp b/groundStation/gui/MicroCART/crazyflieworker.cpp
index 6e5be8620..17629e40b 100644
--- a/groundStation/gui/MicroCART/crazyflieworker.cpp
+++ b/groundStation/gui/MicroCART/crazyflieworker.cpp
@@ -1,6 +1,9 @@
 #include "crazyflieworker.h"
+#include <iostream>
+#include <unistd.h>
+#include <sys/stat.h>
 
-CrazyflieWorker::CrazyflieWorker(QObject *parent) : QObject(parent)
+CrazyflieWorker::CrazyflieWorker(QObject *parent) : QObject(parent), conn(NULL)
 {
 }
 
@@ -21,8 +24,16 @@ void CrazyflieWorker::connectBackend()
         conn = ucart_backendConnect();
         emit (connected());
     } else {
-        qInfo() << "Attempted to connect trackerworker when already connected!";
+        qInfo() << "Attempted to connect crazyflieworker when already connected!";
     }
+    loadParamIds();
+    loadLogIds();
+    QStringList paramGroupList;
+    for(int i = 0; i < paramGroups.size(); i++) {
+        QString qgName(paramGroups.at(i).groupName.c_str());
+        paramGroupList.append(qgName);
+    }
+    emit(gotParamGroups(paramGroupList));
 }
 
 /**
@@ -87,18 +98,175 @@ void CrazyflieWorker::sendAttSetpoint()
 }
 
 void CrazyflieWorker::getParamValue(QString paramName){
-    struct frontend_param_data data = paramNameLookup(paramName);
+    struct toc_info info = paramNameLookup(paramName);
+
+    struct frontend_param_data data;
+    data.block = 0;
+    data.param = info.id;
+    data.value = info.value;
 
     if(frontend_getparam(conn, &data) == 0){
         emit(gotParamValue(paramName, data.value));
     }
 }
 
-struct frontend_param_data CrazyflieWorker::paramNameLookup(QString paramName){
-    //TODO
-    //if param is in cache, use that else lookup from file
-    struct frontend_param_data paramData;
+void CrazyflieWorker::setParamValue(QString paramName, double value) {
+    struct toc_info info = paramNameLookup(paramName);
+
+    struct frontend_param_data data;
+    data.block = 0;
+    data.param = info.id;
+    data.value = (float) value;
+    
+    if(frontend_setparam(conn, &data) != 0){
+        //emit(gotParamValue(paramName, data.value));
+        qInfo() << "setParamError";
+    }
+}
+
+struct toc_info CrazyflieWorker::paramNameLookup(QString paramName){
+    struct toc_info paramData = paramIds.value(paramName);
     return paramData;
 }
 
+std::string CrazyflieWorker::getLogFile(int type) {
+    struct frontend_getlogfile_data logfile;
+    char fname[256];
+    logfile.command = type;
+    logfile.name = fname;
+    if(frontend_getlogfile(conn, &logfile)) {
+        return "";
+    }
+    std::string result(logfile.name, strlen(logfile.name));
+    return result;
+}
+
+void CrazyflieWorker::loadParamIds() {
+    std::string filename = getLogFile(1);
+    std::ifstream paramTOCFile;
+    paramTOCFile.open(filename.c_str());
+    std::string line;
+    //header and top line
+    std::getline(paramTOCFile, line);
+    std::getline(paramTOCFile, line);
+    //actual values;
+    while(std::getline(paramTOCFile, line)) {
+        struct toc_info values;
+        std::stringstream ss(line);
+        std::string temp_str;
+        for(int i = 0; i < 4; i++) {
+            std::getline(ss, temp_str, '\t');
+            if(i == 0) {
+                values.id = (int16_t) stoi(temp_str);
+            }
+            else if(i == 1) {
+                values.dataType = (int8_t) stoi(temp_str);
+            }
+            else if(i == 2) {
+                values.strGroup = temp_str;
+            }
+            else if(i == 3) {
+                values.strName = temp_str;
+            }
+        }
+        values.value = 0;
+
+        int gFound = findParamGroup(values.strGroup);
+        if(gFound == -1) {
+            struct toc_group groupx;
+            groupx.groupName = values.strGroup;
+            groupx.entryNames.push_back(values.strName);
+            paramGroups.push_back(groupx);
+        }
+        else {
+            paramGroups.at(gFound).entryNames.push_back(values.strName);
+        }
+
+        std::string identifier = values.strGroup + "." + values.strName;
+        QString Qidentifier(identifier.c_str());
+        paramIds.insert(Qidentifier, values);
+    }
+    paramTOCFile.close();
+}
 
+void CrazyflieWorker::loadLogIds() {
+    std::string filename = getLogFile(2);
+    std::ifstream logTOCFile;
+    logTOCFile.open(filename.c_str());
+    std::string line;
+    //top line
+    std::getline(logTOCFile, line);
+    std::getline(logTOCFile, line);
+    //actual values;
+    while(std::getline(logTOCFile, line)) {
+        struct toc_info values;
+        std::stringstream ss(line);
+        std::string temp_str;
+        for(int i = 0; i < 4; i++) {
+            std::getline(ss, temp_str, '\t');
+            if(i == 0) {
+                values.id = (int16_t) stoi(temp_str);
+            }
+            else if(i == 1) {
+                values.dataType = (int8_t) stoi(temp_str);
+            }
+            else if(i == 2) {
+                values.strGroup = temp_str;
+            }
+            else if(i == 3) {
+                values.strName = temp_str;
+            }
+        }
+        values.value = 0;
+
+        int gFound = findLogGroup(values.strGroup);
+        if(gFound == -1) {
+            struct toc_group groupx;
+            groupx.groupName = values.strGroup;
+            groupx.entryNames.push_back(values.strName);
+            logGroups.push_back(groupx);
+        }
+        else {
+            logGroups.at(gFound).entryNames.push_back(values.strName);;
+        }
+
+        std::string identifier = values.strGroup + "." + values.strName;
+        QString Qidentifier(identifier.c_str());
+        logIds.insert(Qidentifier, values);
+    }
+    logTOCFile.close();
+}
+
+int CrazyflieWorker::findParamGroup(std::string gName) {
+    int result = -1;
+    for(int i = 0; i < paramGroups.size(); i++) {
+        if(!paramGroups.at(i).groupName.compare(gName)) {
+            result = i;
+            break;
+        }
+    }
+    return result;
+}
+
+int CrazyflieWorker::findLogGroup(std::string gName) {
+    int result = -1;
+    for(int i = 0; i < logGroups.size(); i++) {
+        if(!logGroups.at(i).groupName.compare(gName)) {
+            result = i;
+            break;
+        }
+    }
+    return result;
+}
+
+void CrazyflieWorker::getGroupEntries(int box, QString gName) {
+    std::string name = gName.toStdString();
+    int index = findParamGroup(name);
+    std::vector<std::string> entries = paramGroups.at(index).entryNames;
+    QStringList entryList;
+    for(int i = 0; i < entries.size(); i++) {
+        QString cur(entries.at(i).c_str());
+        entryList.append(cur);
+    }
+    emit(gotGroupEntries(box, entryList));
+}
\ No newline at end of file
diff --git a/groundStation/gui/MicroCART/crazyflieworker.h b/groundStation/gui/MicroCART/crazyflieworker.h
index 8b5ae498e..c3a93825f 100644
--- a/groundStation/gui/MicroCART/crazyflieworker.h
+++ b/groundStation/gui/MicroCART/crazyflieworker.h
@@ -2,12 +2,29 @@
 #define CRAZYFLIEWORKER_H
 
 #include <QObject>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <QStringList>
 #include "frontend_common.h"
 #include "frontend_param.h"
 #include "frontend_override.h"
+#include "frontend_logfile.h"
 #include "setpoint.h"
 #include <QDebug>
 
+struct toc_info {
+    int16_t id;
+    int8_t dataType;
+    std::string strGroup;
+    std::string strName;
+    float value;
+};
+
+struct toc_group {
+    std::string groupName;
+    std::vector<std::string> entryNames;
+};
 
 /**
  * @brief The crazyflieworker class
@@ -25,6 +42,8 @@ public:
 
 signals:
     void gotParamValue(QString paramName, float value);
+    void gotParamGroups(QStringList paramGroupList);
+    void gotGroupEntries(int box, QStringList groupEntries);
     void connected();
     void disconnected();
 
@@ -35,6 +54,8 @@ public slots:
     void sendAttSetpoint();
     void sendAttRateSetpoint();
     void getParamValue(QString paramName);
+    void setParamValue(QString paramName, double value);
+    void getGroupEntries(int box, QString gName);
 
 private:
     struct backend_conn * conn;
@@ -42,9 +63,16 @@ private:
     Setpoint currAttitudeRateSetpoint;
     void sendAttitudeSetpoint();
     void sendAttitudeRateSetpoint();
-    struct frontend_param_data paramNameLookup(QString paramName);
-
-
+    std::string getLogFile(int type);
+    void loadParamIds();
+    void loadLogIds();
+    int findParamGroup(std::string gName);
+    int findLogGroup(std::string gName);
+    struct toc_info paramNameLookup(QString paramName);
+    QMap<QString, struct toc_info> paramIds;
+    QMap<QString, struct toc_info> logIds;
+    std::vector<struct toc_group> paramGroups;
+    std::vector<struct toc_group> logGroups;
 };
 
 #endif // CRAZYFLIEWORKER_H
diff --git a/groundStation/gui/MicroCART/mainwindow.cpp b/groundStation/gui/MicroCART/mainwindow.cpp
index d62956482..50d2226d8 100644
--- a/groundStation/gui/MicroCART/mainwindow.cpp
+++ b/groundStation/gui/MicroCART/mainwindow.cpp
@@ -13,6 +13,7 @@
 
 #include "trackerworker.h"
 #include "controlworker.h"
+#include "crazyflieworker.h"
 #include "quaditem.h"
 #include "slotprocess.h"
 #include "qFlightInstruments.h"
@@ -91,13 +92,35 @@ MainWindow::MainWindow(QWidget *parent) :
     connect(this, SIGNAL (setParamValue(QString, QString, float)), controlWorker, SLOT (setParamValue(QString, QString, float)));
     connect(this, SIGNAL (getNodeOutput(QString)), controlWorker, SLOT (getNodeOutput(QString)));
 
+    /* Create another work for the crazyflie communication */
+    QThread * cfThread = new QThread(this);
+    CrazyflieWorker * crazyflieWorker = new CrazyflieWorker();
+    crazyflieWorker->moveToThread(cfThread);
+
+    /* connect signals for crazyflie worker */
+    connect(crazyflieWorker, SIGNAL (gotParamGroups(QStringList)), this, SLOT (setParamGroups(QStringList)));
+    connect(ui->paramGroupComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT (getGroupBoxChange(int)));
+    connect(ui->paramGroupComboBox_2, SIGNAL (currentIndexChanged(int)), this, SLOT (getGroupBox2Change(int)));
+    connect(ui->paramEntriesComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT (getEntryBoxChange(int)));
+    connect(ui->paramEntriesComboBox_2, SIGNAL (currentIndexChanged(int)), this, SLOT (getEntryBoxChange2(int)));
+    connect(ui->pb_getParam, SIGNAL (clicked()), this, SLOT (on_pb_getParam_click()));
+    connect(ui->pb_setParam, SIGNAL (clicked()), this, SLOT (on_pb_setParam_click()));
+    connect(crazyflieWorker, SIGNAL (gotGroupEntries(int, QStringList)), this, SLOT (newGroupEntries(int, QStringList)));
+    connect(this, SIGNAL (getGroupEntries(int, QString)), crazyflieWorker, SLOT (getGroupEntries(int, QString)));
+    connect(this, SIGNAL (getParamValue(QString)), crazyflieWorker, SLOT (getParamValue(QString)));
+    connect(this, SIGNAL (setParamValue(QString, double)), crazyflieWorker, SLOT (setParamValue(QString, double)));
+    connect(crazyflieWorker, SIGNAL (gotParamValue(QString, float)), this, SLOT (displayParamValue(QString, float)));
+
     /* Connect and disconnect from backend when signals emitted */
     connect(workerStartTimer, SIGNAL (timeout()), trackerWorker, SLOT (connectBackend()));
     connect(workerStartTimer, SIGNAL (timeout()), controlWorker, SLOT (connectBackend()));
+    connect(workerStartTimer, SIGNAL (timeout()), crazyflieWorker, SLOT (connectBackend()));
     connect(this, SIGNAL (connectWorkers()), trackerWorker, SLOT (connectBackend()));
     connect(this, SIGNAL (connectWorkers()), controlWorker, SLOT (connectBackend()));
+    connect(this, SIGNAL (connectWorkers()), crazyflieWorker, SLOT (connectBackend()));
     connect(this, SIGNAL (disconnectWorkers()), trackerWorker, SLOT (disconnectBackend()));
     connect(this, SIGNAL (disconnectWorkers()), controlWorker, SLOT (disconnectBackend()));
+    connect(this, SIGNAL (disconnectWorkers()), crazyflieWorker, SLOT (disconnectBackend()));
 
     connect(backendProcess, SIGNAL (started()), this, SLOT (backendStarted()));
     connect(backendProcess, SIGNAL (errorOccurred(QProcess::ProcessError)), this, SLOT (backendError(QProcess::ProcessError)));
@@ -111,6 +134,9 @@ MainWindow::MainWindow(QWidget *parent) :
     connect(trackerWorker, SIGNAL (connected()), this, SLOT (workerConnected()));
     connect(trackerWorker, SIGNAL (disconnected()), this, SLOT (workerDisconnected()));
 
+    connect(crazyflieWorker, SIGNAL (connected()), this, SLOT (workerConnected()));
+    connect(crazyflieWorker, SIGNAL (disconnected()), this, SLOT (workerDisconnected()));
+
     /* Connect refresh button and refresh timer to tracker worker */
     connect(trackerTimer, SIGNAL(timeout()), this, SLOT(updatePosAtt()));
     // connect(ui->pbRefresh, SIGNAL (clicked()), this, SLOT (updatePosAtt()));
@@ -125,11 +151,19 @@ MainWindow::MainWindow(QWidget *parent) :
     trackerTimer->start(100);
     workerThread->start();
     cwThread->start();
+    cfThread->start();
     matlabProcess->start();
 
     /* Connect the setpointlist to the model */
     // ui->setpointList->setModel(setpointList);
 
+    ui->pb_getParam->setEnabled(false);
+    ui->pb_setParam->setEnabled(false);
+    ui->setValueSpin->setMaximum(10000.00);
+    ui->setValueSpin->setMinimum(-10000.00);
+    ui->setValueSpin->setSingleStep(0.01);
+    ui->setValueSpin->setValue(0);
+
     /* Connect various things that can result in sending setpoints */
     // connect(ui->pbSendSetpoint, SIGNAL (clicked()), this, SLOT (sendSetpoints()));
     // connect(ui->setpointList, SIGNAL (doubleClicked(QModelIndex)), this, SLOT (sendSelectedSetpoint()));
@@ -143,7 +177,6 @@ MainWindow::MainWindow(QWidget *parent) :
         scriptProcess->setProgram(scriptsDir.filePath(scripts[i]));
         connect(action, SIGNAL (triggered()), scriptProcess, SLOT (startProcess()));
     }
-
 }
 
 MainWindow::~MainWindow()
@@ -274,6 +307,98 @@ void MainWindow::newNodes(QStringList blocks)
     // this->ui->noGraphWarningLine->setVisible(false);
 }
 
+void MainWindow::setParamGroups(QStringList groupNames) {
+    ui->paramGroupComboBox->addItem("");
+    ui->paramGroupComboBox->addItems(groupNames);
+    ui->paramGroupComboBox_2->addItem("");
+    ui->paramGroupComboBox_2->addItems(groupNames);
+}
+
+void MainWindow::getGroupBoxChange(int index) {
+    // for(int i = 0; i < ui->paramGroupComboBox->count(); i++) {
+    //     ui->paramEntriesComboBox->removeItem(i);
+    // }
+    ui->paramEntriesComboBox->clear();
+    QString qStrGroup = ui->paramGroupComboBox->itemText(index);
+    std::string check = qStrGroup.toStdString();
+    ui->valueVallabel->clear();
+    if(!check.compare("")) {
+        ui->pb_getParam->setEnabled(false);
+    }
+    else {
+        emit(getGroupEntries(1, qStrGroup));
+    }
+}
+
+void MainWindow::getGroupBox2Change(int index) {
+    // for(int i = 0; i < ui->paramGroupComboBox->count(); i++) {
+    //     ui->paramEntriesComboBox->removeItem(i);
+    // }
+    ui->paramEntriesComboBox_2->clear();
+    QString qStrGroup = ui->paramGroupComboBox_2->itemText(index);
+    std::string check = qStrGroup.toStdString();
+    if(!check.compare("")) {
+        ui->pb_setParam->setEnabled(false);
+    }
+    else {
+        emit(getGroupEntries(2, qStrGroup));
+    }
+}
+
+void MainWindow::getEntryBoxChange(int index) {
+    QString qStrGroup = ui->paramEntriesComboBox->itemText(index);
+    std::string check = qStrGroup.toStdString();
+    ui->valueVallabel->clear();
+    if(!check.compare("")) {
+        ui->pb_getParam->setEnabled(false);
+    }
+    else {
+        ui->pb_getParam->setEnabled(true);
+    }
+}
+
+void MainWindow::getEntryBoxChange2(int index) {
+    QString qStrGroup = ui->paramEntriesComboBox_2->itemText(index);
+    std::string check = qStrGroup.toStdString();
+    if(!check.compare("")) {
+        ui->pb_setParam->setEnabled(false);
+    }
+    else {
+        ui->pb_setParam->setEnabled(true);
+    }
+}
+
+void MainWindow::newGroupEntries(int box, QStringList entries) {
+    if(box == 1) {
+        ui->paramEntriesComboBox->addItem("");
+        ui->paramEntriesComboBox->addItems(entries);
+    }
+    else {
+        ui->paramEntriesComboBox_2->addItem("");
+        ui->paramEntriesComboBox_2->addItems(entries);
+    }    
+}
+
+void MainWindow::on_pb_getParam_click() {
+    ui->valueVallabel->clear();
+    QString group = ui->paramGroupComboBox->currentText();
+    QString entry = ui->paramEntriesComboBox->currentText();
+    QString key = group + "." + entry;
+    emit(getParamValue(key));
+}
+
+void MainWindow::on_pb_setParam_click() {
+    QString group = ui->paramGroupComboBox_2->currentText();
+    QString entry = ui->paramEntriesComboBox_2->currentText();
+    QString key = group + "." + entry;
+    double value = ui->setValueSpin->value();
+    emit(setParamValue(key, value));
+}
+
+void MainWindow::displayParamValue(QString key, float value) {
+    ui->valueVallabel->setNum(value);
+}
+
 void MainWindow::newConstantBlocks(QStringList blocks)
 {
     // ui->xSetpointSelect->clear();
diff --git a/groundStation/gui/MicroCART/mainwindow.h b/groundStation/gui/MicroCART/mainwindow.h
index 4e24931aa..a79eea751 100644
--- a/groundStation/gui/MicroCART/mainwindow.h
+++ b/groundStation/gui/MicroCART/mainwindow.h
@@ -23,9 +23,12 @@ signals:
     void connectWorkers();
     void disconnectWorkers();
     void getParamValue(QString node, QString param);
+    void getParamValue(QString key);
     void getNodeOutput(QString node);
     void setParamValue(QString node, QString param, float value);
+    void setParamValue(QString key, double value);
     void getPosAttFromBackend();
+    void getGroupEntries(int box, QString gName);
 
 private slots:
     void on_pbStart_clicked();
@@ -45,6 +48,11 @@ private slots:
     void newParamValue(QString node, QString param, float val);
     void newNodeOutput(QString node, float output);
     void newConstantBlocks(QStringList blocks);
+    void newGroupEntries(int box, QStringList entries);
+
+    void on_pb_getParam_click();
+
+    void on_pb_setParam_click();
 
     void on_paramSelect_currentIndexChanged(const QString &arg1);
 
@@ -91,6 +99,13 @@ private slots:
     void workerConnected();
     void workerDisconnected();
 
+    void setParamGroups(QStringList groupNames);
+    void getGroupBoxChange(int index);
+    void getGroupBox2Change(int index);
+    void displayParamValue(QString param, float value);
+    void getEntryBoxChange(int index);
+    void getEntryBoxChange2(int index);
+
 private:
     Ui::MainWindow *ui;
     QStandardItemModel * setpointList;
diff --git a/groundStation/gui/MicroCART/mainwindow.ui b/groundStation/gui/MicroCART/mainwindow.ui
index ceea28e08..6a96b514f 100644
--- a/groundStation/gui/MicroCART/mainwindow.ui
+++ b/groundStation/gui/MicroCART/mainwindow.ui
@@ -18,7 +18,7 @@
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="currentIndex">
-       <number>3</number>
+       <number>1</number>
       </property>
       <widget class="QWidget" name="backend">
        <attribute name="title">
@@ -119,6 +119,212 @@
        <attribute name="title">
         <string>Param</string>
        </attribute>
+       <widget class="QComboBox" name="paramGroupComboBox">
+        <property name="geometry">
+         <rect>
+          <x>420</x>
+          <y>100</y>
+          <width>201</width>
+          <height>25</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QComboBox" name="paramEntriesComboBox">
+        <property name="geometry">
+         <rect>
+          <x>420</x>
+          <y>150</y>
+          <width>201</width>
+          <height>25</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="groupLabel">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>100</y>
+          <width>54</width>
+          <height>31</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Group:</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="getParamLabel">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>40</y>
+          <width>71</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Get Param</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="label">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>150</y>
+          <width>54</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Entry:</string>
+        </property>
+       </widget>
+       <widget class="Line" name="line">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>279</y>
+          <width>1111</width>
+          <height>31</height>
+         </rect>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+       <widget class="QLabel" name="setParamLabel">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>310</y>
+          <width>71</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Set Param</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="valueLabel">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>240</y>
+          <width>54</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Value:</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="valueVallabel">
+        <property name="geometry">
+         <rect>
+          <x>420</x>
+          <y>240</y>
+          <width>201</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+       <widget class="QLabel" name="groupLabel_set">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>360</y>
+          <width>54</width>
+          <height>31</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Group:</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="label_5">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>400</y>
+          <width>54</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Entry:</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="valueLabel_2">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>450</y>
+          <width>54</width>
+          <height>17</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Value:</string>
+        </property>
+       </widget>
+       <widget class="QComboBox" name="paramGroupComboBox_2">
+        <property name="geometry">
+         <rect>
+          <x>420</x>
+          <y>360</y>
+          <width>201</width>
+          <height>25</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QComboBox" name="paramEntriesComboBox_2">
+        <property name="geometry">
+         <rect>
+          <x>420</x>
+          <y>400</y>
+          <width>201</width>
+          <height>25</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="pb_getParam">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>200</y>
+          <width>80</width>
+          <height>25</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Get Param</string>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="pb_setParam">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>500</y>
+          <width>80</width>
+          <height>25</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Set Param</string>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="setValueSpin">
+        <property name="geometry">
+         <rect>
+          <x>470</x>
+          <y>450</y>
+          <width>111</width>
+          <height>26</height>
+         </rect>
+        </property>
+       </widget>
       </widget>
       <widget class="QWidget" name="controller">
        <attribute name="title">
@@ -489,7 +695,7 @@
               </item>
               <item row="1" column="1">
                <widget class="QLabel" name="currRActual">
-               <property name="text">
+                <property name="text">
                  <string>N/A</string>
                 </property>
                </widget>
@@ -557,7 +763,7 @@
      <x>0</x>
      <y>0</y>
      <width>1186</width>
-     <height>28</height>
+     <height>22</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuScripts">
@@ -578,6 +784,13 @@
   <widget class="QStatusBar" name="statusBar"/>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>QCustomPlot</class>
+   <extends>QWidget</extends>
+   <header>qcustomplot.h</header>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections/>
 </ui>
diff --git a/groundStation/src/backend/param.c b/groundStation/src/backend/param.c
index bb0dc1a9d..b31241cef 100644
--- a/groundStation/src/backend/param.c
+++ b/groundStation/src/backend/param.c
@@ -129,8 +129,10 @@ ssize_t EncodeResponseParam(
 	data[RESP_BLOCK_ID_L] = 0;
 	data[RESP_BLOCK_ID_H] = 0;
 
-	data[RESP_PARAM_ID_L] = msg[1];
-	data[RESP_PARAM_ID_H] = 0;
+	//data[RESP_PARAM_ID_L] = msg[1];
+	//data[RESP_PARAM_ID_H] = msg[2];
+
+	memcpy(&data[RESP_PARAM_ID_L], &msg[1], sizeof(uint16_t));
 
 	float fl = -1;
 
@@ -138,70 +140,70 @@ ssize_t EncodeResponseParam(
 		case 8:
 		{
 			uint8_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(uint8_t));
+			memcpy(&pvalue, &msg[4], sizeof(uint8_t));
 			fl = pvalue;
 			break;
 		}
 		case 9:
 		{
 			uint16_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(uint16_t));
+			memcpy(&pvalue, &msg[4], sizeof(uint16_t));
 			fl = pvalue;
 			break;
 		}
 		case 10:
 		{
 			uint32_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(uint32_t));
+			memcpy(&pvalue, &msg[4], sizeof(uint32_t));
 			fl = pvalue;
 			break;
 		}
 		case 11:
 		{
 			uint64_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(uint64_t));
+			memcpy(&pvalue, &msg[4], sizeof(uint64_t));
 			fl = pvalue;
 			break;
 		}
 		case 0:
 		{
 			int8_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(int8_t));
+			memcpy(&pvalue, &msg[4], sizeof(int8_t));
 			fl = pvalue;
 			break;
 		}
 		case 1:
 		{
 			int16_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(int16_t));
+			memcpy(&pvalue, &msg[4], sizeof(int16_t));
 			fl = pvalue;
 			break;
 		}
 		case 2:
 		{
 			int32_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(int32_t));
+			memcpy(&pvalue, &msg[4], sizeof(int32_t));
 			fl = pvalue;
 			break;
 		}
 		case 3:
 		{
 			int64_t pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(int64_t));
+			memcpy(&pvalue, &msg[4], sizeof(int64_t));
 			fl = pvalue;
 			break;
 		}
 		case 6:
 		{
 			float pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(float));
+			memcpy(&pvalue, &msg[4], sizeof(float));
 			fl = pvalue;
 			break;
 		}
 		case 7:
 		{
 			double pvalue;
-			memcpy(&pvalue, &msg[2], sizeof(double));
+			memcpy(&pvalue, &msg[4], sizeof(double));
 			fl = pvalue;
 			break;
 		}
@@ -231,9 +233,12 @@ int DecodeResponseParam(
 		return -1;
 	}
 
+	uint16_t id;
+	memcpy(&id, &data[RESP_PARAM_ID_L], sizeof(uint16_t));
+
 	return snprintf(msg, max_len, "getparam %" PRId16 " %" PRId16 " %f\n",
 		BytesTo16(data[RESP_BLOCK_ID_L], data[RESP_BLOCK_ID_H]),
-		BytesTo16(data[RESP_PARAM_ID_L], data[RESP_PARAM_ID_H]),
+		id,
 		BytesToFloat(data[RESP_VAL_1], data[RESP_VAL_2],
 			data[RESP_VAL_3], data[RESP_VAL_4]));
 }
-- 
GitLab