Basic Mid-Level control for the rebuilt MorphGI control unit, using PWM to communicate with the low level controllers.

Dependencies:   ros_lib_kinetic

Revision:
42:5a5ad23a4bb1
Parent:
36:4459be8296e9
--- a/main.cpp	Thu Sep 03 16:30:23 2020 +0000
+++ b/main.cpp	Thu Jun 24 20:34:47 2021 +0000
@@ -2,155 +2,841 @@
 #include "math.h"
 // MBED IMPORTS
 #include "mbed.h"
-#include "mbed_events.h"
+
 // CUSTOM IMPORTS
 #include "MLSettings.h"
-#include "HLComms.h"
-#include "LLComms.h"
+//#include "HLComms.h"
+
+#define N_CHANNELS 4
 
 // DEMAND VARIABLES
-double dblDemandSpeed_mmps[N_CHANNELS] = { 0.0 }; // The linear path velocity (not sent to actuator)
-double dblDemandPosition_mm[N_CHANNELS] = { 0.0 }; // The final target position for the actuator
 
 Serial pc(USBTX, USBRX); // tx, rx for usb debugging
-LLComms llcomms;
-HLComms hlcomms(HL_COMMS_FREQ_HZ);
+
+//HLComms hlcomms(HL_COMMS_FREQ_HZ);
+
+double limDouble(double var, double min, double max){
+    double output = var;
+    if(var <min){
+        output = min;
+    }
+    if (var>max){
+        output = max;
+    }
+    return output;
+}
+    
+
+//Ticker setDemandsTicker;
+
+
+//joystick input pins
+
+InterruptIn pinZeroIn   (PD_4);
+InterruptIn pinCycleIn  (PD_6);
+InterruptIn pinSucIn    (PD_7);
+InterruptIn pinGasIn    (PF_5);
+InterruptIn pinDebugIn  (PF_9);
+InterruptIn pinFlushIn  (PG_1);
+
+DigitalIn   pinWashIn   (PD_5);
+DigitalIn   pinFwd      (PD_3);
+DigitalIn   pinRev      (PC_3);
+
+
+AnalogIn    pinJSX      (PC_0);
+AnalogIn    pinJSY      (PF_3);
+
+DigitalOut  pinFlushOut (PG_0);
+DigitalOut  pinGasOut   (PD_0);
+DigitalOut  pinWashOut  (PF_1);
+DigitalOut  pinSucOut   (PD_1);
+DigitalOut  pinThirdPump(PF_2);
+DigitalOut  pinJetOut   (PF_0);
+
+//DigitalIn arPins[7] = {pinZeroIn, pinCycleIn, pinSucIn, pinWashIn, pinGasIn, pinFwd, pinRev};
+
+Thread threadReadHHC(osPriorityHigh);
+Thread threadSendFeedback(osPriorityNormal);
+
+Semaphore semReadHHC(1);
+Semaphore semSendFeedback(1);
+Semaphore semReceiveAndReplan(1);
+
+//Ticker SendSensorDataTicker;
+Ticker tickerReadHHC;
+Ticker tickerSendFeedback;
+
+void signalReadHHC(){
+    semReadHHC.release();
+}
+
+void signalSendFeedback() {
+    semSendFeedback.release();
+}
+
+//support function states;
+
+bool isZero = 0;
+bool isCycle = 0;
+bool isSuc = 0;
+bool isWash = 0;
+bool isGas = 0;
+bool isFwd = 0;
+bool isRev = 0;
+bool isBusy = 0;
+bool DemPos=0;
+bool isDebug = 0;
+
+//set up PWM output pins for Demand POSITION AND VELOCITY
+PwmOut pinPosOut[N_CHANNELS] = {PB_15, PB_8, PB_9  ,PC_6};
+PwmOut pinVelOut[N_CHANNELS] = {PB_10, PA_0, PB_11 ,PB_3};
+
 
-Thread threadLowLevelSPI(osPriorityRealtime);
-Thread threadSetDemands(osPriorityNormal);
-Thread threadReceiveAndReplan(osPriorityBelowNormal);
-Thread threadSensorFeedback(osPriorityBelowNormal);
+void FlushActivate(){
+    if(!isBusy){
+        pinFlushOut = 1;
+    }
+}
+
+void FlushStop(){
+    if(!isBusy){
+        pinFlushOut = 0;
+    }
+}
+
+void ZeroActivate(){
+    if(!isBusy){ 
+        isZero = 1; 
+    }
+}
+void ZeroStop(){
+
+}
+
+void DebugActivate(){
+    isDebug = 1;
+}
+
+void DebugStop(){
+    isDebug = 0;
+}    
+
+void CycleActivate(){
+    if(!isBusy){ 
+        isCycle = 1;
+    }
+}
+void CycleStop(){
+
+}
+
+void GasActivate(){
+    if(!isBusy){
+        pinGasOut = 1;
+    }
+}
+void GasStop(){
+    pinGasOut = 0;
+}
+
+void SucActivate(){
+    if(!isBusy){
+        pinSucOut = 1;
+    }
+}
+void SucStop(){
+    pinSucOut = 0;
+}
+
+bool isChange = 1;
+
+Timer timer1;
+
+Ticker DemTicker;
+
+int intAnchorDirection = 0;
+
+
+
+//global variables
+double dblSensitivity = 1.0;
+double dblDriveStep = 0.05;
+bool isSteerLock = 0;
+double dblAlpha;
+double dblBeta;
+double dblTheta;
+double dblPhi;
+double dblS;
+double dblSdot;
+double dblX[3];
+double dblXdot[3];
+double dblAlphaDot;
+double dblBetaDot;
+double dblDeltaAlpha;
+double dblDeltaBeta;
+
+double x_axis_sign = -1.0;
+
+//kinematic constants
+double K_theta = 0.77;// rad/mL
+double d_theta = 0.3;// no unit
 
-Mutex mutPathIn;
-Semaphore semLLcomms(1);
-Semaphore semSensorData(1);
+void SendFeedback(){
+    //support function interrupt states
+    while(1){
+        semSendFeedback.wait();
+        if(!isBusy){
+            if(pinFwd.read()){ 
+                isFwd = 1;
+            } else {
+                isFwd = 0;
+            }
+            
+            if(pinRev.read()){ 
+                isRev = 1;
+            } else {
+                isRev = 0;
+            }
+            
+            if(pinSucIn.read()){ 
+                isSuc = 1;
+            } else {
+                isSuc = 0;
+            }
+            
+            if(pinWashIn.read()){ 
+                isWash = 1;
+            } else {
+                isWash = 0;
+            }
+            
+            if(pinGasIn.read()){ 
+                isGas = 1;
+            } else {
+                isGas = 0;
+            }
+            
+            
+            //
+            //printf("\r\n");
+            //printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", isZero, isGas, isSuc, isRev, isWash, isCycle, isFwd, isDebug);
+    //        
+    //        double dblJSXprint = pinJSX.read();
+    //        double dblJSYprint = pinJSY.read();
+    //        printf("X:%f\tY:%f\t", dblJSXprint, dblJSYprint);
+            
+    //        printf("%f\t",dblAlphaDot);
+    //        printf("%f\t",dblBetaDot);
+    //        printf(":: ");
+    //        
+    //        printf("%f\t",dblAlpha);
+    //        printf("%f\t",dblBeta);
+    //        printf(":: ");
+    //        
+    //        printf("%f\t",dblTheta);
+    //        printf("%f\t",dblPhi);
+    //        printf(":: ");
+            printf("\t");
+            
+            printf("%f\t",dblX[1]);
+            printf("%f\t",dblX[0]);
+            printf("%f\t",dblX[2]);
+            printf(":: ");
+            
+    //        printf("%f\t",dblXdot[0]);
+    //        printf("%f\t",dblXdot[2]);
+    //        printf("%f\t",dblXdot[1]);
+    //        printf(":: ");
+            
+            printf("%f\t",dblS);
+            printf("%f\t",dblSdot);
+            printf("\r\n");
+        }
+    }
+}
 
-Ticker setDemandsTicker;
-Ticker SendSensorDataTicker;
+double DeadZone(double var, double threshold, double max, double min){
+    
+    double output = var;
+    output -= 0.5;
+    min -= 0.5;
+    max -= 0.5; 
+    
+    if(output > threshold){
+        output = (output - threshold);
+        output /= (max - threshold);
+    } else if(output < -1.0*threshold){
+        output = -1.0*(output + threshold);
+        output /= (threshold + min);
+    } else {
+        output = 0.0;
+    }
+    
+    output = limDouble(output, -1.0, 1.0);
+    return output;
+}
+
+double ConvertToPWM(double target, double max){
+    double output;
+    output  = target*0.8/max ;
+    output += 0.1;
+    return output;
+}
 
-void sendSensorData() {
-    while( true ) {
-        semSensorData.wait();
-        hlcomms.send_sensor_message(llcomms.positionSensor_uint,llcomms.pressureSensor_uint);
+void SetFrontDemand(double demandPos[], double demandVel[]){    
+    
+    double demandPos_abs[3];
+    double demandVel_abs[3];
+    double pwmPos_front[3];
+    double pwmVel_front[3];
+    
+    for(int ii = 0; ii<3; ii++){
+        demandPos_abs[ii] = abs(demandPos[ii]);
+        demandVel_abs[ii] = abs(demandVel[ii]);
+        pwmPos_front[ii] = ConvertToPWM(demandPos_abs[ii], (double)MAX_ACTUATOR_LIMIT_MM);
+        pwmVel_front[ii] = ConvertToPWM(demandVel_abs[ii], (double)MAX_SPEED_MMPS);
+        pinPosOut[ii].write(pwmPos_front[ii]);
+        pinVelOut[ii].write(pwmVel_front[ii]);
+        
+    }
+    
+/*    
+    //print for debugging
+    printf("X: ");
+    for(int ii = 0; ii<3; ii++){
+        printf("%f\t",pwmPos_front[ii]);
+    }
+    printf("Xdot: ");
+    for(int ii = 0; ii<3; ii++){
+        printf("%f\t",pwmVel_front[ii]);
+    }
+    */
+}
+
+void SetDriveDemand(double drivePos, double driveVel){    
+    
+    double drivePos_abs;
+    double driveVel_abs;
+    double pwmPos_drive;
+    double pwmVel_drive;
+    
+    drivePos_abs = abs(drivePos);
+    driveVel_abs = abs(driveVel);
+    
+    pwmPos_drive = ConvertToPWM(drivePos_abs, MAX_DRIVE_SEGMENT_POSITION);
+    pwmVel_drive = ConvertToPWM(driveVel_abs, MAX_DRIVE_SEGMENT_SPEED);
+    
+    pinPosOut[3].write(pwmPos_drive);
+    pinVelOut[3].write(pwmVel_drive);
+    
+    /*
+    //print for debugging
+    printf("S:%f\tSdot:%f\t",pwmPos_drive,pwmVel_drive);
+    */
+}
+
+double GetMaxTravelTime(double startPositions[], double endPositions[], double speed){
+    double arT[3];
+    for(int ii = 0; ii< 3; ii++){
+        arT[ii] = endPositions[ii] - startPositions[ii];
+        arT[ii] /= speed;
+        arT[ii] = abs(arT[ii]);
+    }
+    double Tmax = 0.0;
+    for(int ii = 0; ii<3; ii++){
+        if( Tmax < arT[ii] ){
+            Tmax = arT[ii];
+        }
+    }
+    return Tmax;
+}
+
+void UpdateFrontSegPosition(double position[]){
+    for(int ii = 0; ii<3; ii++){
+        dblX[ii] = position[ii];
     }
 }
 
-void signalSendSensorData() {
-    semSensorData.release();
+void GoToNeutralPose(double speed){
+    isBusy = 1;
+    double T_wait;
+    double arSpeed[3] = {speed, speed, speed};  
+    double arPosition[3] = {ACTUATOR_OFFSET, ACTUATOR_OFFSET, ACTUATOR_OFFSET};
+    SetFrontDemand(arPosition, arSpeed);
+    
+    //T_wait = 40.0 / speed;
+    T_wait = GetMaxTravelTime(dblX, arPosition, speed);
+    T_wait += 0.25;//add a little bit of time for security
+    //print for debug    
+    printf("\r\nGoing to neutral. %f\r\n", T_wait);
+    
+    wait(T_wait);
+    UpdateFrontSegPosition(arPosition);
+    //isZero = 0;
+    isBusy = 0;
+}
+
+void GoToAnchorPose(double speed){
+    //reset front segment position
+    //isAnchor = 0;
+    isBusy = 1;
+    double T_wait;
+    double arSpeed[3] = {speed, speed, speed};  
+    double arStartPosition[3];
+    //save starting position
+    for (int ii = 0; ii<3; ii++){
+        arStartPosition[ii] = dblX[ii];
+    }
+    
+    double arAnchorPosition[3] = {ACTUATOR_OFFSET, ACTUATOR_OFFSET, ACTUATOR_OFFSET};
+
+//Go to neutral
+    GoToNeutralPose(speed);
+    
+    //select direction of anchor
+    arAnchorPosition[intAnchorDirection] = ANCHOR_POSITION;
+    intAnchorDirection++; //rotate next anchoring position
+    if(intAnchorDirection >= 3){
+        intAnchorDirection = 0;
+    }
+//go to anchor position    
+    SetFrontDemand(arAnchorPosition, arSpeed);    
+    T_wait = GetMaxTravelTime(dblX, arAnchorPosition, speed);
+    T_wait += 0.25;//add a little bit of time for security
+    printf("Going to anchor. %f s\r\n", T_wait);
+    wait(T_wait);
+    
+    //update front segment position.
+    UpdateFrontSegPosition(arAnchorPosition);
+     
+//contract
+    SetDriveDemand(HOME_DRIVE_POSITION, SAFE_CONTRACT_SPEED);
+    T_wait = dblS - HOME_DRIVE_POSITION; 
+    T_wait /= SAFE_CONTRACT_SPEED;
+    printf("Contracting. %f s\r\n", T_wait);
+    wait(T_wait);
+    //update drive segment position
+    dblS = HOME_DRIVE_POSITION;
+    
+//go back to neutral pose
+    GoToNeutralPose(speed);
+    
+//go back to starting position
+    SetFrontDemand(arStartPosition, arSpeed);
+    T_wait = GetMaxTravelTime(dblX, arStartPosition, speed);
+    T_wait += 0.25;//add a little bit of time for security
+    printf("Going to intial position. %f\r\n", T_wait);
+    wait(T_wait);
+    //UpdateFrontSegPosition(arStartPosition);
+    for(int ii = 0; ii<3; ii++){
+        dblX[ii] = arStartPosition[ii];
+    }
+    
+    //print for debug
+    isBusy = 0;
+}
+
+
+void GoToCustomPose(double position[], double speed){
+    //reset front segment position
+    //isBusy = 1;
+    double T_wait;
+    double arSpeed[3] = {speed, speed, speed};  
+    for (int ii = 0; ii<3; ii++){
+       position[ii] = limDouble(position[ii], 0.0, (double)MAX_ACTUATOR_LIMIT_MM);
+    }
+    SetFrontDemand(position, arSpeed);
+    
+    //T_wait = 40.0 / speed;
+    T_wait = GetMaxTravelTime(dblX, position, speed);
+    T_wait += 0.25;//add a little bit of time for security
+    //print for debug    
+    printf("\r\nGoing to custom pose. T_wait: %f s \r\n", T_wait);
+    wait(T_wait);
+    UpdateFrontSegPosition(position);
+    printf("Done\r\n");
+    //isBusy = 0;
+}
+
+
+void AllGoTo(double position, double speed){
+    //reset front segment position
+    //isBusy = 1;
+    double T_wait;
+    double arSpeed[3] = {speed, speed, speed};  
+    position = limDouble(position, 0.0, (double)MAX_ACTUATOR_LIMIT_MM);
+    
+    double arPosition[3] = {position, position, position};
+    SetFrontDemand(arPosition, arSpeed);
+    
+    //T_wait = 40.0 / speed;
+    T_wait = GetMaxTravelTime(dblX, arPosition, speed);
+    T_wait += 0.25;//add a little bit of time for security
+    //print for debug    
+    printf("\r\nMoving all to %f mm. T_wait: %f\r\n",position, T_wait);
+    
+    wait(T_wait);
+    UpdateFrontSegPosition(arPosition);
+    printf("Done\r\n");
+    //isBusy = 0;
+}
+
+void FlushSyringes(int repetitions){
+    //isBusy = 1;
+    printf("\r\n\r\n:::Flushing Syringes:::\r\n");
+    pinFlushOut = 1;
+    wait(1.0);
+    for(int ii = 0; ii<repetitions; ii++){
+        AllGoTo(40.0,20.0);
+        wait(0.5);
+        AllGoTo(0.0,2.0);
+        wait(0.5);
+    }
+    pinFlushOut = 0;
+    printf("Done\r\n");
+    //isBusy = 0;
+}
+
+void FlushSegment(int repetitions){
+    //isBusy = 1;
+    printf("\r\n\r\n:::Flushing Segment:::\r\n");
+    FlushSyringes(1);//ensure syringes are flushed.
+    double arZero[3] = {0.0, 0.0, 0.0};
+    
+    double flushPosition = 30.0;
+    
+    double arFlushPose1[3] = {flushPosition, 0.0, 0.0};
+    double arFlushPose2[3] = {0.0, flushPosition, 0.0};
+    double arFlushPose3[3] = {0.0, 0.0, flushPosition};
+    for(int ii = 0; ii<repetitions; ii++){
+        //flush first chamber
+        printf("Flushing first chamber\r\n");
+        GoToCustomPose(arFlushPose1, SAFE_SPEED_MMpS);
+        AllGoTo(0.0, SAFE_SPEED_MMpS);
+        
+        //flush second chamber
+        printf("Flushing second chamber\r\n");
+        GoToCustomPose(arFlushPose2, SAFE_SPEED_MMpS);
+        AllGoTo(0.0, SAFE_SPEED_MMpS);
+        
+        //flush third chamber
+        printf("Flushing third chamber\r\n");
+        GoToCustomPose(arFlushPose3, SAFE_SPEED_MMpS);
+        AllGoTo(0.0, SAFE_SPEED_MMpS);
+        
+        FlushSyringes(1);//flush syringes
+    }
+    printf("Done\r\n");
+    //isBusy = 0;
 }
 
-// This function will be called when a new transmission is received from high level
-void ReceiveAndReplan() {
+void FlushTubing(int repetitions){
+    //isBusy = 1;
+    printf("\r\n\r\n:::Flushing Tubing:::\r\n");
+    for (int ii = 0; ii<repetitions; ii++){
+        AllGoTo(40.0,15.0);
+        pinFlushOut = 1;
+        wait(1.0);
+        AllGoTo(0.0, 2.0);
+        pinFlushOut = 0;
+        wait(0.5);
+    }
+}
     
-    SendSensorDataTicker.attach(&signalSendSensorData, 1/(float)SENSOR_FEEDBACK_HZ); // Set up planning thread to recur at fixed intervals
+
+void DebugMode(){
+    isBusy = 1;
+    printf("\r\nEntering debug mode. Going to zero.\r\n");
+    double zeroPos[3] = {0.0, 0.0, 0.0};
+    double safeVel[3] = {SAFE_SPEED_MMpS, SAFE_SPEED_MMpS, SAFE_SPEED_MMpS};
     
-    struct demands_struct input;
-    DigitalOut SupportPins[4] = {PE_4, PE_2, PE_3, PE_6};
+   
+    SetFrontDemand(zeroPos, safeVel);
+    SetDriveDemand(0.0, SAFE_CONTRACT_SPEED);
+    double T_wait = GetMaxTravelTime(dblX, zeroPos, SAFE_SPEED_MMpS);
+    T_wait += 0.25;
+    wait(T_wait);
+    UpdateFrontSegPosition(zeroPos);
+    printf("Done\r\nPlease enter command\r\n");
+    
+    int countSyringeFlush = 0;
+    int countSegmentFlush = 0;
+    int countTubingFlush = 0;
     
-    while( true ) {
-        hlcomms.newData.wait();
-        input = hlcomms.get_demands();
-        // SUPPORT FUNCTIONS
-        // [isInsufflate,isSuction,isWashLens,isJet]
-        for(short int i=0; i<4; i++) {
-            if(i<2) { // Active low, i.e. 0 = Off
-                SupportPins[i].write((short int)input.utitilies_bool[i]);
-            } else { // Active high, i.e. 1 = Off
-                SupportPins[i].write((short int)(!input.utitilies_bool[i]));
+    while(isDebug){
+        isBusy = 1;
+        //printf("x\r\n");
+        
+        if( pinZeroIn.read() ){
+            //printf("zero%d\r\n",countSyringeFlush);
+            
+            if(countSyringeFlush == 0){
+                printf("\r\n Syringe Flush Selected\r\n");
+            }
+            countSyringeFlush++;
+            countSegmentFlush = 0;
+            countTubingFlush = 0;
+            if(countSyringeFlush > 50){
+                FlushSyringes(4);
+                countSyringeFlush = 0;
             }
+            //printf("%d\r\n", countSyringeFlush);
+            
+        }else {
+            countSyringeFlush = 0;
+        }
+        
+        if( pinSucIn.read() ){
+            //printf("suc%d\r\n", countSegmentFlush);
+            
+            if(countSegmentFlush == 0){
+                printf("\r\n Segment Flush Selected\r\n");
+            }
+            countSyringeFlush = 0;
+            countSegmentFlush++;
+            countTubingFlush = 0;
+            if(countSegmentFlush > 50){
+                FlushSegment(2);
+                countSegmentFlush = 0;
+            }
+        }else {
+            countSegmentFlush = 0;
+        }
+        
+        if( pinWashIn.read() ){
+            //printf("wash%d\r\n", countTubingFlush);
+            if(countTubingFlush == 0){
+                printf("\r\n Tubing Flush Selected\r\n");
+            }
+            countSyringeFlush = 0;
+            countSegmentFlush = 0;
+            countTubingFlush++;
+            if(countTubingFlush > 50){
+                FlushTubing(2);
+                countTubingFlush = 0;
+            }
+        }else {
+            countTubingFlush = 0;
         }
-        // PROCESS INPUT
-        double maxTimesToTarget_s[3] = { -1.0 };
-        //[8,7,6,4,3,-1,-1,0,-1]
-        //FRONT = channels 0,1,2
-        //MID = channels 3,4(,5)
-        //REAR = channels (6,)7(,8)
-        // Lock mutex, preventing setDemandsForLL from running
-        mutPathIn.lock();
-        for(short int segNum=0; segNum<3; segNum++) {
-            // Limit requested speed
-            if( input.speeds_mmps[segNum] > 0 ) {
-                double limitedSpeed_mmps = min( max( 0.0 , input.speeds_mmps[segNum] ) , (double)MAX_SPEED_MMPS );
-                // For each actuator, limit the input position, calculate the position change, and select the absolute max
-                double dblDistanceToTarget_mm[3] = { -1.0 };
-                double maxDistanceToTarget_mm = 0.0;
-                for(short int i=0; i<3; i++) {
-                    short int channel = segNum*3+i;
-                    if(channel>=N_CHANNELS) {
-                        continue;
+        
+        wait(0.1);
+    }
+    
+    GoToNeutralPose(SAFE_SPEED_MMpS);
+    //reset front segment position
+    dblAlpha = 0.0;
+    dblBeta  = 0.0;
+    printf("\r\nManual control enabled\r\n");
+    isBusy = 0;
+}
+
+void ReadHHC(){
+    printf("Homing. Please wait...\r\n");
+
+
+    double startingPos[3] = {ACTUATOR_OFFSET, ACTUATOR_OFFSET, ACTUATOR_OFFSET};
+    double startingVel[3] = {SAFE_SPEED_MMpS, SAFE_SPEED_MMpS, SAFE_SPEED_MMpS};
+    SetFrontDemand(startingPos, startingVel);
+    SetDriveDemand(HOME_DRIVE_POSITION, SAFE_CONTRACT_SPEED);
+    wait(5);
+    printf("Done.\r\n");
+    threadSendFeedback.start(SendFeedback);
+    tickerSendFeedback.attach(&signalSendFeedback, 1/(float)SENSOR_FEEDBACK_HZ);
+    while(1){
+        semReadHHC.wait();  
+        
+        if(isZero){
+            GoToNeutralPose(SAFE_SPEED_MMpS);
+                //reset front segment position
+            dblAlpha = 0.0;
+            dblBeta  = 0.0;
+            isZero = 0;
+            printf("Done\r\n");
+        }
+        
+        if(isCycle){
+            GoToAnchorPose(SAFE_SPEED_MMpS);
+            isCycle = 0;
+            printf("Done\r\n");
+        }
+        if(isDebug){
+            DebugMode();
+        }
+
+        if(!isSteerLock){   
+            //read Joystick sensors
+            double dblJSX = pinJSX.read();
+            double dblJSY = pinJSY.read();
+            
+            dblJSX = DeadZone(dblJSX, 0.07, 0.85, 0.15);
+            dblJSY = DeadZone(dblJSY, 0.07, 0.85, 0.15);
+            
+            if(pinWashIn.read()){
+                pinWashOut = 0;
+            }else{
+                pinWashOut = 1;
+            }
+                       
+            //convert to angular velocity for front segment
+            
+            //Find changes in angle
+            dblDeltaAlpha = dblJSX * x_axis_sign * dblSensitivity * MAX_STEER_SPEED_RADpS / HHC_READ_FREQ_HZ;
+            dblDeltaBeta  = dblJSY*dblSensitivity*MAX_STEER_SPEED_RADpS/HHC_READ_FREQ_HZ;
+            //convert to speeds in [rad/s]
+            dblAlphaDot = dblDeltaAlpha*HHC_READ_FREQ_HZ;
+            dblBetaDot  = dblDeltaBeta*HHC_READ_FREQ_HZ;
+            
+            //calculate required velocity based on current possition and new inputs.
+            dblPhi = atan2(dblBeta,dblAlpha);
+            double dblPhiDot = dblAlpha * dblBetaDot - dblBeta * dblAlphaDot;
+            dblPhiDot /= (dblAlpha * dblAlpha + dblBeta*dblBeta);
+            if( fabs( cos(dblPhi) ) >= 0.7106781 ){
+                dblTheta = dblAlpha / cos(dblPhi);
+                double dblThetaDot = dblAlphaDot * cos(dblPhi) + dblAlpha * dblPhiDot * sin(dblPhi);
+                dblThetaDot /= cos(dblPhi)*cos(dblPhi);
+                double dblPsi[3];
+                for (int ii =0; ii<3; ii++){
+                    dblPsi[ii] = dblPhi - ii*2*PI/3;
+                    if(dblBeta == 0){
+                        dblXdot[ii] = ACT_CONV * K_theta*( cos(dblPsi[ii]) + d_theta);
+                        dblXdot[ii] *= ( dblAlphaDot - dblBetaDot * sin(dblPsi[ii]) ) / cos(dblPhi);
                     }
-                    double dblCurrentPosition_mm = llcomms.positionSensor_mm[channel];
-                    if(input.psi_mm[channel]<0 || dblCurrentPosition_mm<0) { // If requested position is negative or the sensor feedback is erroneous
-                        continue;
+                    else{
+                        dblXdot[ii] = ACT_CONV * K_theta * ( ( cos(dblPsi[ii]) + d_theta) * dblThetaDot - dblTheta * sin(dblPsi[ii]) * dblPhiDot);
+                    }   
+                }               
+            } 
+            else {
+                dblTheta = dblBeta / sin(dblPhi);
+                double dblThetaDot = dblBetaDot * sin(dblPhi) + dblBeta * dblPhiDot * cos(dblPhi);
+                dblThetaDot /= sin(dblPhi)*sin(dblPhi);
+                double dblPsi[3];
+                for (int ii =0; ii<3; ii++){
+                    dblPsi[ii] = dblPhi - ii*2*PI/3;
+                    if(dblAlpha == 0){
+                        dblXdot[ii] = ACT_CONV * K_theta * ( cos(dblPsi[ii]) + d_theta);
+                        dblXdot[ii] *= ( dblBetaDot - dblAlphaDot * sin(dblPsi[ii]) ) / sin(dblPhi);
                     }
-                    // Limit actuator position
-                    input.psi_mm[channel] = min( max( 0.0 , input.psi_mm[channel] ) , (double)MAX_ACTUATOR_LIMIT_MM );
-                    // Calculate actuator position change
-                    dblDistanceToTarget_mm[i] = fabs(input.psi_mm[channel] - dblCurrentPosition_mm);
-                    // Select the max absolute actuator position change
-                    if(dblDistanceToTarget_mm[i]>maxDistanceToTarget_mm) {
-                        maxDistanceToTarget_mm = dblDistanceToTarget_mm[i];
+                    else{
+                        dblXdot[ii] = ACT_CONV * K_theta * ( ( cos(dblPsi[ii]) + d_theta ) * dblThetaDot - dblTheta * sin(dblPsi[ii]) * dblPhiDot);
                     }
                 }
-                // For max actuator position change, calculate the time to destination at the limited speed
-                maxTimesToTarget_s[segNum] = fabs(maxDistanceToTarget_mm) / limitedSpeed_mmps;
-                // For each actuator, replan target position and velocity as required
-                for(short int i=0; i<3; i++) {
-                    short int channel = segNum*3+i;
-                    // If requested actuator position change is already within tolerance, do NOT replan that actuator
-                    if( dblDistanceToTarget_mm[i] <= 0.0 ) continue;
-                    // Calculate velocity for each motor to synchronise movements to complete in max time
-                    // Set dblDemandPosition_mm and dblDemandSpeed_mmps
-                    dblDemandPosition_mm[channel] = input.psi_mm[channel];
-                    dblDemandSpeed_mmps[channel] = dblDistanceToTarget_mm[i] / maxTimesToTarget_s[segNum];
+            }
+            //calculate new angles for next front segment position
+            dblAlpha += dblDeltaAlpha; // update new alpha postion
+            dblBeta += dblDeltaBeta; // update new beta position
+            
+            dblAlpha = limDouble(dblAlpha, (double)-1.0*MAX_THETA_RAD, (double)MAX_THETA_RAD);
+            dblBeta = limDouble(dblBeta, (double)-1.0*MAX_THETA_RAD, (double)MAX_THETA_RAD);
+            dblPhi = atan2(dblBeta,dblAlpha); // update new phi position
+            if( fabs( cos(dblPhi) ) >= 0.7106781 ){
+                dblTheta = dblAlpha / cos(dblPhi);//calculate new theta value
+            }
+            else { 
+                dblTheta = dblBeta / sin(dblPhi);
+            }
+            dblTheta = limDouble(dblTheta, 0.0, (double)MAX_THETA_RAD ); //limit angle
+            //update alpha and beta if limit has occured
+            dblAlpha = dblTheta *cos(dblPhi);
+            dblBeta = dblTheta *sin(dblPhi);
+            
+            double dblPsi[3];
+            for (int ii = 0; ii <3; ii++) {//calculate new acuator positions    
+                dblPsi[ii] = dblPhi - ii*2*PI/3;
+                dblX[ii] = ACT_CONV * K_theta * ( cos(dblPsi[ii]) + d_theta) * dblTheta;
+                //add offset
+                dblX[ii] += ACTUATOR_OFFSET;
+                
+                //limit positions
+                dblX[ii] = limDouble( dblX[ii], 0.0, (double)MAX_ACTUATOR_LIMIT_MM );
+            }
+            //calculate new drive segment position
+            //read drive segment buttons
+            //double dblDriveStep = 0.4;
+            
+            //printf("pin:%d\t", pinFwd.read());
+            
+            if(pinFwd.read()){
+                if(!pinRev.read()){
+                    dblSdot = SAFE_CONTRACT_SPEED;
+                } else{
+                    dblSdot = 0.0;
                 }
             } else {
-                maxTimesToTarget_s[segNum] = -1.0;
+                if(pinRev.read()){
+                    dblSdot = -1.0*SAFE_CONTRACT_SPEED;
+                } else{
+                    dblSdot = 0.0;
+                }
             }
+            
+            dblS += dblSdot/HHC_READ_FREQ_HZ;
+            //limit extension (uses word position, but actually refers to pressure)
+            dblS = limDouble( dblS, 0.0, (double)MAX_EXTENSION_PRESSURE );
+            
+//            printf("S:%f\tSdot:%f\t", dblS, dblSdot);
+            //Send signals to actuators.
+            SetFrontDemand(dblX, dblXdot);//front segment
+            SetDriveDemand(dblS, dblSdot);//drive segment
+
+
         }
-        // Unlock mutex, allowing setDemandsForLL to run again
-        mutPathIn.unlock();
-        
-        // SEND MESSAGE
-        hlcomms.send_durations_message(maxTimesToTarget_s);
     }
-
-}
-
-void startLLcomms() { // Send new demands to LL after receiving new target data
-    semLLcomms.release(); // Uses threadSetDemands which is normal priority
 }
 
-void setDemandsForLL() {
-    
-    while(1) {
-        semLLcomms.wait();
-        mutPathIn.lock(); // Lock relevant mutex
-        for(short int i=0; i<N_CHANNELS; i++) { // For each LL
-            llcomms.mutChannel[i].lock(); // MUTEX LOCK
-            llcomms.demandPosition_mm[i] = dblDemandPosition_mm[i];
-            llcomms.demandSpeed_mmps[i] = dblDemandSpeed_mmps[i];
-            llcomms.mutChannel[i].unlock(); // MUTEX UNLOCK
-            llcomms.isDataReady[i] = 1; // Signal that data ready
-        } // end for
-        mutPathIn.unlock(); // Unlock relevant mutex
-    } // end while(1)
-
+void SetUp(){
+    for(int ii = 0; ii<N_CHANNELS; ii++){
+        pinPosOut[ii].write(0.0);
+        pinVelOut[ii].write(0.0);
+    }
 }
 
 int main() {
+    pinThirdPump = 1;
+    pinJetOut = 1;
+    pinWashOut = 1;
     pc.baud(BAUD_RATE);
-    //printf("ML engage. Compiled at %s\r\n.",__TIME__);
-    wait(3);
+    for(int ii = 0; ii<N_CHANNELS; ii++){
+        pinPosOut[ii].period_ms(2.0);
+        pinVelOut[ii].period_ms(2.0);
+    }
+    wait(1.0);
+    printf("ML engage. Compiled at %s\r\n",__TIME__);
+    
+
+    //set up support function interrupts
+    //Rising
     
-    threadLowLevelSPI.start(callback(&llcomms.queue, &EventQueue::dispatch_forever)); // Start the event queue
-    threadReceiveAndReplan.start(ReceiveAndReplan);// Start replanning thread
-    threadSetDemands.start(setDemandsForLL); // Start planning thread
-    threadSensorFeedback.start(sendSensorData); // Start sensor feedback thread
+    pinZeroIn.mode(PullNone);
+    pinCycleIn.mode(PullNone);
+    pinWashIn.mode(PullNone);
+    pinGasIn.mode(PullNone);
+    pinSucIn.mode(PullNone);
+    pinFwd.mode(PullNone);
+    pinRev.mode(PullNone);
+    pinFlushIn.mode(PullDown);
+    pinDebugIn.mode(PullDown);
+    
+    pinZeroIn.rise(&ZeroActivate);
+    pinCycleIn.rise(&CycleActivate);
+    pinFlushIn.rise(&FlushActivate);
+    pinSucIn.rise(&SucActivate);
+    pinGasIn.rise(&GasActivate);
+    pinDebugIn.rise(&DebugActivate);
+    //falling
+    pinZeroIn.fall(&ZeroStop);
+    pinCycleIn.fall(&CycleStop);
+    pinFlushIn.fall(&FlushStop);
+    pinSucIn.fall(&SucStop);
+    pinGasIn.fall(&GasStop);
+    pinDebugIn.fall(&DebugStop);
+    threadReadHHC.start(ReadHHC); 
 
-    setDemandsTicker.attach(&startLLcomms, 1/(float)LL_DEMANDS_FREQ_HZ); // Set up LL comms thread to recur at fixed intervals
     
-    Thread::wait(1);
+    tickerReadHHC.attach(&signalReadHHC, 1/(float)HHC_READ_FREQ_HZ); // set up  
+    
+
     while(1) {
         Thread::wait(osWaitForever); 
     }