From 7b397967236727e0f0f83b4bf9852d6bd3f7d649 Mon Sep 17 00:00:00 2001
From: Pascal Engeler <engelerp@phys.ethz.ch>
Date: Fri, 9 Jun 2023 23:31:26 +0200
Subject: [PATCH] Added communicator library

---
 .../drivers/communicator/communicator.cpp     | 207 ++++++++++++++++++
 .../drivers/communicator/communicator.hpp     |  35 +++
 2 files changed, 242 insertions(+)
 create mode 100644 firmware/drivers/communicator/communicator.cpp
 create mode 100644 firmware/drivers/communicator/communicator.hpp

diff --git a/firmware/drivers/communicator/communicator.cpp b/firmware/drivers/communicator/communicator.cpp
new file mode 100644
index 0000000..df399f3
--- /dev/null
+++ b/firmware/drivers/communicator/communicator.cpp
@@ -0,0 +1,207 @@
+#include <communicator.cpp>
+#include <utility.hpp>
+#include <ptimer.hpp>
+#include <pid_controller.hpp>
+
+unsigned long Communicator::timeout_us_ = 20000; //timeout 20ms
+char Communicator::outbuf[256]; //out buffer
+char Communicator::cmd_ = 'X'; //command received
+std::vector<char> Communicator::arg_ = std::vector<char>('\0',256); //arguments received
+
+bool Communicator::communicate(){
+    if(!serialCharAvailable_()){
+        return false;
+    }
+    else{
+        cmd_ = getSerialChar_();
+        if(isSetCommand_()){
+            //read argument
+            char latest = cmd;
+            size_t i = 0;
+            PTimer::start();
+            while(latest != ';' && i < arg_.size()-10 && PTimer::elapsed() < timeout_us_){
+                if(serialCharAvailable_()){
+                    latest = getSerialChar_();
+                    arg_[i++] = latest;
+                    if(latest == ';'){
+                        arg[i-1] = '\0';
+                    }
+                    PTimer::start();
+                }
+            }
+            if(PTimer::elapsed() >= timeout_us_){
+                handleTimeout_(); //timeout
+                return false;
+            }
+            else if(i >= arg_.size()-10){
+                handleInputOverflow_(); //input overflow
+                return false;
+            }
+            //apply the setting
+            else{
+                handleSetCommand_();
+                return true;
+            }
+        }
+        else if(isGetCommand_()){
+            if(cmd_ == 't'){ //getTemperature
+                snprintf(   outbuf_,
+                            255,
+                            "%f,%f,%f,%f,%f;", 
+                            Temperatures.temperatures[0],
+                            Temperatures.temperatures[1],
+                            Temperatures.temperatures[2],
+                            Temperatures.temperatures[3],
+                            Temperatures.temperatures[4]
+                            );
+                sendBuf_();
+                return true;
+            }
+            else if(cmd_ == 'p'){ //getPidCoeff
+                auto kpids = Pid_controller::get_kpid();
+                snprintf(   outbuf_,
+                            255,
+                            "%f,%f,%f;", 
+                            kpids[0],
+                            kpids[1],
+                            kpids[2]
+                            );
+                sendBuf_();
+                return true;
+            }
+            else if(cmd_ == 'v'){ //getPropIntDer
+                auto pids = Pid_controller::get_pid();
+                snprintf(   outbuf_,
+                            255,
+                            "%f,%f,%f;", 
+                            pids[0],
+                            pids[1],
+                            pids[2]
+                            );
+                sendBuf_();
+                return true;
+            }
+            else if(cmd_ == 'o'){ //getOutput
+                snprintf(outbuf_, 255, "%f;", Pid_controller::get_output());
+                sendBuf_();
+                return true;
+            }
+            else if(cmd_ == 's'){ //getSetpoint
+                snprintf(outbuf_, 255, "%f;", Pid_controller::get_setpoint());
+                sendBuf_();
+                return true;
+            }
+            else{ //invalid command
+                handleInvalidCommand_();
+                return false;
+            }
+        }
+        else if(isNopCommand_()){
+            handleNopCommand_();
+            return true;
+        }
+        else{ //invalid command
+            handleInvalidCommand_();
+            return false;
+        }
+    }
+}
+
+bool Communicator::serialCharAvailable_() const{
+    return SERCHAN.available() > 0;
+}
+
+bool Communicator::isSetCommand_() const{
+    return  cmd_ == 'P' ||
+            cmd_ == 'I' ||
+            cmd_ == 'D' ||
+            cmd_ == 'N' ||
+            cmd_ == 'S';
+}
+
+bool Communicator::isGetCommand_() const{
+    return  cmd_ == 't' ||
+            cmd_ == 'p' ||
+            cmd_ == 'v' ||
+            cmd_ == 'o' ||
+            cmd_ == 's';
+}
+
+bool Communicator::isNopCommand_() const{
+    return  cmd_ == '?';
+}
+
+char Communicator::getSerialChar_() const{
+    int newChar = SERCHAN.read();
+    if(newChar < 0 || newChar >= 128){//invalid char
+        return 'X';
+    }
+    else{
+        return char(newChar);
+    }
+}
+
+void Communicator::sendBuf_() const{
+    SERCHAN.write(outbuf_);
+}
+
+void Communicator::handleTimeout_() const{
+    snprintf(outbuf_, 255, "!");
+    sendBuf_();
+}
+
+void Communicator::handleInputOverflow_() const{
+    snprintf(outbuf_, 255, "!");
+    sendBuf_();
+}
+
+void Communicator::handleSetCommand_() const{
+    char* endptr = arg_.data()+250;
+    double darg = std::strtod(arg_.data(), &endptr);
+    if(arg_.data() == endptr){ //error
+        handleInvalidCommand_();
+        return;
+    }
+    else if(cmd_ == 'P'){
+        Pid_controller::set_Kp(darg);
+        snprintf(outbuf_, 255, ";");
+        sendBuf_();
+        return;
+    }
+    else if(cmd_ == 'I'){
+        Pid_controller::set_Ki(darg);
+        snprintf(outbuf_, 255, ";");
+        sendBuf_();
+        return;
+    }
+    else if(cmd_ == 'D'){
+        Pid_controller::set_Kd(darg);
+        snprintf(outbuf_, 255, ";");
+        sendBuf_();
+        return;
+    }
+    else if(cmd_ == 'N'){
+        Pid_controller::set_I_reset(darg);
+        snprintf(outbuf_, 255, ";");
+        sendBuf_();
+        return;
+    }
+    else if(cmd_ == 'S'){
+        Pid_controller::set_setpoint(darg);
+        snprintf(outbuf_, 255, ";");
+        sendBuf_();
+        return;
+    }
+}
+
+void Communicator::handleNopCommand_() const{
+    snprintf(outbuf_, 255, ";");
+    sendBuf_();
+    return;
+}
+
+void Communicator::handleInvalidCommand_() const{
+    snprintf(outbuf_, 255, "!");
+    sendBuf_();
+    return;
+}
diff --git a/firmware/drivers/communicator/communicator.hpp b/firmware/drivers/communicator/communicator.hpp
new file mode 100644
index 0000000..ec2e69d
--- /dev/null
+++ b/firmware/drivers/communicator/communicator.hpp
@@ -0,0 +1,35 @@
+#ifndef COMMUNICATOR_HPP_INCLUDED
+#define COMMUNICATOR_HPP_INCLUDED
+#include <vector>
+
+#define SERCHAN SerialUSB
+
+class Communicator{
+public:
+    static bool communicate();
+
+private:
+    /*Function Members*/
+    static bool serialCharAvailable_() const;
+    static bool isSetCommand_() const;
+    static bool isGetCommand_() const;
+    static bool isNopCommand_() const;
+
+    static char getSerialChar_() const;
+    static void sendBuf_() const;
+
+    static void handleTimeout_();
+    static void handleInputOverflow_();
+    static void handleSetCommand_();
+    static void handleNopCommand_();
+    static void handleInvalidCommand_();
+    
+
+    /*Data Members*/
+    static unsigned long timeout_us_;
+    static char outbuf_[256];
+    static char cmd_;
+    static std::vector<char> arg_;
+}
+
+#endif
-- 
GitLab