Alle subfiles samengevoegd

Dependencies:   HIDScope biquadFilter mbed MODSERIAL FastPWM QEI

main.cpp

Committer:
sjoerdbarts
Date:
2016-11-02
Revision:
20:04b5619a4fb7
Parent:
19:3af738c38db0

File content as of revision 20:04b5619a4fb7:

#include "mbed.h"
#include "math.h"
#include "MODSERIAL.h"
#include "HIDScope.h"
#include "FastPWM.h"
#include "QEI.h"
#include "BiQuad.h"
#define SERIAL_BAUD 115200  // baud rate for serial communication

// Serial connection
MODSERIAL pc(USBTX,USBRX);

//EMG aansluitingen
AnalogIn    emg0( A0 );     //Biceps Rechts
AnalogIn    emg1( A1 );     //Bicpes Links
AnalogIn    emg2( A2 );     //Upper leg

// Setup Potmeters
// Potmeter 1 gives the reference position x
AnalogIn pot1(A3);
// Potmeter 2 gives the reference position y
AnalogIn pot2(A4);

// Setup Buttons
InterruptIn button1(PTB9);                  // button 1
InterruptIn button2(PTA1);                  // button 2
InterruptIn button3(PTC6);                  // SW2
InterruptIn button4(PTA4);                  // SW3

// Setup botton values
bool button1_value = false;
bool button2_value = false;

// Set motor Pinouts
DigitalOut motor1_dir(D4);
FastPWM motor1_pwm(D5);
DigitalOut motor2_dir(D7);
FastPWM motor2_pwm(D6);

// Set LED pins
DigitalOut led(PTE24);                      // D15 LED1
DigitalOut red(LED_RED);
DigitalOut blue(LED_BLUE);
DigitalOut green(LED_GREEN);

// Set HID scope
HIDScope    scope(6);

// Set encoder
QEI m1_EncoderCW(D10,D11,NC,32);
QEI m1_EncoderCCW(D11,D10,NC,32);
QEI m2_EncoderCW(D13,D12,NC,32);
QEI m2_EncoderCCW(D12,D13,NC,32);

// Constants for control
volatile const int   COUNTS_PER_REV = 4200;
volatile const double DEGREES_PER_PULSE = 8400 / 360;

// Set initial Kp and Ki
volatile double Kp = 0.05; // last known good Kp: 0.02
volatile double Ki = 0.025;   // last known good Ki: 0.0025
volatile double Kd = 0.0; // last known good Kd: 0.0

// Set frequencies for sampling, calc and control.
volatile const double SAMPLE_F = 500;               // 500 Hz
volatile const double SAMPLE_TS = 1.0/SAMPLE_F;
volatile const double CALC_F = 500.0;               // 100 Hz
volatile const double CALC_TS = 1.0/CALC_F;
volatile const double CONTROLLER_TS = 0.01;         // 100 Hz

volatile double Ts = 0.01;
volatile double N = 100;

// Memory values for controllers
double m1_v1 = 0, m1_v2 = 0;
double m2_v1 = 0, m2_v2 = 0;

//BiQuad
// making the biquads and chains; imported from matlab
BiQuadChain bqc;        //Chain for right biceps
BiQuadChain bqc2;       //Chain for left biceps
BiQuadChain bqc3;       //Chain for Upper leg
// 1 to 3 are for right biceps
BiQuad bq1( 9.14969e-01, -1.82994e+00, 9.14969e-01, -1.82269e+00, 8.37182e-01 );
BiQuad bq2( 9.91104e-01, -1.60364e+00, 9.91104e-01, -1.60364e+00, 9.82207e-01 );
BiQuad bq3( 8.76555e-05, 1.75311e-04, 8.76555e-05, -1.97334e+00, 9.73695e-01 );
// 4 to 6 are for left biceps
BiQuad bq4( 9.14969e-01, -1.82994e+00, 9.14969e-01, -1.82269e+00, 8.37182e-01 );
BiQuad bq5( 9.91104e-01, -1.60364e+00, 9.91104e-01, -1.60364e+00, 9.82207e-01 );
BiQuad bq6( 8.76555e-05, 1.75311e-04, 8.76555e-05, -1.97334e+00, 9.73695e-01 );
// 7 to 9 are for upper leg
BiQuad bq7( 9.14969e-01, -1.82994e+00, 9.14969e-01, -1.82269e+00, 8.37182e-01 );
BiQuad bq8( 9.91104e-01, -1.60364e+00, 9.91104e-01, -1.60364e+00, 9.82207e-01 );
BiQuad bq9( 3.91302e-05, 7.82604e-05, 3.91302e-05, -1.98223e+00, 9.82385e-01 );

// Variables for filter
// muscle EMG < treshhold
volatile bool BicepsLeft = false;
volatile bool BicepsRight = false;
volatile bool Leg = false;

// Variables for MotorHoekBerekenen (MotorAngleCalculation)
volatile double x = 0.0;                             // beginpositie x en y
volatile double y = 305.5;
volatile const double pi = 3.14159265359; 
volatile double Speed = 60;                          // Speed with which x and y are changed, in mm/s
volatile double Theta1Gear = 60.0;                   // Beginpositie op 60 graden
volatile double Theta2Gear = 60.0;
volatile const int LargeGear = 42;
volatile const int SmallGear = 15;
volatile double Theta1 = Theta1Gear*LargeGear/SmallGear;           // Beginpositie van MotorHoek
volatile double Theta2 = Theta2Gear*LargeGear/SmallGear;

// Stepper motor setup
// Set constans
volatile int stepmode = 16;                         // step mode
volatile int steps = 150*stepmode;                  // amount of steps for 90 degree rotation = 50, full step mode: 1.8 degree per step.
volatile int i = 0;
volatile bool Stepper_CCW = false;

// Setup stepper motor pins
// DigitalOut pinMode(PTD2);     //D11 NOW USES 3.3V OUPUT, SINCE THIS IS ALWAYS ON
DigitalOut pinSleep(PTE25);      //D14
DigitalOut pinStep(PTC4);        //D9
DigitalOut pinDir(PTC12);        //D8

// Control of the stepper motor with 1 Ticker and 1 Timeout
Ticker  TickerStepper;
Timeout TimeoutStepper;

// Global variable Step_State
volatile bool Stepper_State = false;        // false = we zijn niet bezig met flippen

// Setup Tickers
    Ticker      CalculationsTicker;
    Ticker      BlinkLedTicker;
    Ticker      PIDControlTicker;
    Ticker      PotControlTicker;

/////////////////////// Functtions for stepper motor /////////////////////////////////////
// Set the Step pin to 0
void StepperFall()
{
    pinStep = 0;
}
// Turn off stepper and power off driver board. Reset Stepper_CCS, i and Stepper_State
void Stepper_Off()
{
    TickerStepper.detach();
    pinSleep = 0;
    i=0;
    Stepper_State = false;
    Stepper_CCW = false;
}

// Change direction
void Stepper_Change_Dir()
{
    pinDir = true;
    Stepper_CCW = true;
    i = 0; 
}

// Move the motor 1 step
void StepperRise()
{
    if((i<steps)&&(not Stepper_CCW))
    {
        pinStep=1; // Set stepper to 1
        // attach timeout to set it back to 0, min step duration is 1.8 us
        TimeoutStepper.attach_us(&StepperFall, 2);
        i=i+1;
    }
    else if (Stepper_CCW)
    {
        if(i<steps)
        {
            pinStep=1;
            TimeoutStepper.attach_us(&StepperFall, 2);
            i=i+1;
        }
        else 
        {
            Stepper_Off();
        }
    }
    else
    {
         Stepper_Change_Dir();
    }
}

// Activate the stepper motor
// at a constant speed
void Stepper_On()
{
    if (Stepper_State == false)
    {
    // Set global variable to true
    Stepper_State = true;
    // Power on the board and set direction to CW
    pinSleep = 1;
    pinDir = false;
    // Calc the speed of the stepper
    int nPPS = 50*stepmode;  // was 1200, this controlls the speed, keep this times the step mode
    double fFrequency_Hz = 1.f * nPPS;
    double fPeriod_s = 1.0f / fFrequency_Hz;
    // Check if the speed is too fast small and report back if that is the case
    if (fPeriod_s < 2e-6)
    {
        pc.printf("\r\n ERROR: fPeriod_s too small \r\n");
        fPeriod_s=2e-6;
    }    
    
    TickerStepper.attach(&StepperRise, // void(*fptr)(void)
                        fPeriod_s   // float t
                        );
    }
}

/////////////////////// functions for filters /////////////////////////////////////////////
void sample()
{
    /*  Sample function
    samples the emg0 (of the rightBiceps)
    samples the emg1 (of the leftBiceps)
    samples the emg2 (of the Upper leg)
    filter the singals
    sends it to HIDScope
    */
    
    // Rechts Biceps
    double inRechts  = emg0.read();                 // EMG signal
    double FilterRechts = bqc.step(inRechts);       // High pass + Notch (50 HZ)
    double RectifyRechts = fabs(FilterRechts);      // Rectify
    double outRechts =  bq3.step(RectifyRechts);    // Low pass
   
    // Links Biceps
    double inLinks  = emg1.read();                  // EMG signal
    double FilterLinks = bqc2.step(inLinks);        // High pass + Notch (50 HZ)
    double RectifyLinks = fabs(FilterLinks);        // Rectify
    double outLinks =  bq6.step(RectifyLinks);      // Low pass
    
    // Upper leg
    double inBeen  = emg2.read();                   // EMG signal
    double FilterBeen = bqc3.step(inBeen);          // High pass + Notch (50 HZ)
    double RectifyBeen = fabs(FilterBeen);          // Rectify
    double outBeen =  bq9.step(RectifyBeen);        // Low pass 
    
    
    /* EMG signal in channel 0 (the first channel) 
    and the filtered EMG signal in channel 1 (the second channel)
    of the HIDscope */
    /*scope.set(0,inRechts);
    scope.set(1,outRechts);
    scope.set(2,inLinks);
    scope.set(3,outLinks);
    scope.set(4,inBeen);
    scope.set(5,outBeen);
    */
    // To indicate that the function is working, the LED is on
    if (outRechts >= 0.03){
        BicepsRight = true;
        red = 0;
        }
    else{
        BicepsRight = false;
        red = 1;
    }

    // If Biceps links is actuated then:
    if (outLinks >= 0.02){
        BicepsLeft = true;
        green = 0;
        }
    else{
        BicepsLeft = false;
        green = 1;
    }
     // If upper leg is actuated then:
    if (outBeen >= 0.02){
        Leg = true;
        blue = 0;
        }
    else{
        Leg = false;
        blue = 1;
    }
}

///////////////////// functions for "motorhoekberekenen" //////////////////////////
// save previous x 
double Calc_Prev_x () {
    double Prev_x = x;
        //pc.printf("prev x = %f\r\n", Prev_x);
    return Prev_x;
}

// save previous y
double Calc_Prev_y () {
    double Prev_y = y;
        //pc.printf("prev y = %f\r\n", Prev_y);
    return Prev_y;
}

// save previous Theta1Gear
double Calc_Prev_Theta1_Gear () {
    double Prev_Theta1_Gear = Theta1Gear;
    return Prev_Theta1_Gear;
}

// save previous Theta2Gear
double Calc_Prev_Theta2_Gear () {
    double Prev_Theta2_Gear = Theta2Gear;
    return Prev_Theta2_Gear;
}

void CalcXY () 
{
    // calc steps in mm
    double Step = Speed/CALC_F ;  //10 mm per seconde afleggen
    int Ymax = 306;
    int Xmax = 200;
    if (BicepsLeft==true && BicepsRight==true && Leg==true && Stepper_State==false) {   
        Stepper_On();
    }
    else if (BicepsLeft==true && BicepsRight==false && Leg==false && Stepper_State==false) {
        x = x - Step;
    }
    else if (BicepsLeft==false && BicepsRight==true && Leg==false && Stepper_State==false) {
        x = x + Step;       // naar Right bewegen
    }
    else if (BicepsLeft==true && BicepsRight==true && Leg==false && Stepper_State==false) {
        y = y + Step;        // naar voren bewegen
    }
    else if (BicepsLeft==false && BicepsRight==true && Leg==true && Stepper_State==false) {
        y = y - Step;       // naar achter bewegen
    }
    else if (BicepsLeft==false && BicepsRight==false && Leg==false || Stepper_State==true) {
    }
    
    // Grenswaardes     LET OP: ARMEN MISSCHIEN GEBLOKKEERD DOOR BALK AAN DE BINNENKANT
    if (x > Xmax) {
        x = Xmax;
    }
    else if (x < -Xmax) {
        x = -Xmax;
    }
    if (y > Ymax) {
        y = Ymax;
    }
    else if (y < 50) {
        y = 50;         // GOKJE, UITPROBEREN
    }
    //pc.printf("x = %f, y = %f\r\n", x, y);
      
    //scope.set(2,x);
    //scope.set(3,y);
}    

// diagonaal berekenen voor linker arm
double CalcDia1 (double x, double y) {
    double a = 50.0;                                // de afstand van gekozen assenstelsel tot de armas (assenstelsel precies in het midden)   KEERTJE NAMETEN
    double BV1 = sqrt(pow((a+x),2) + pow(y,2));     // diagonaal (afstand van armas tot locatie) berekenen
    double Dia1 = pow(BV1,2)/(2*BV1);               // berekenen van de afstand oorsprong tot diagonaal
    
    //pc.printf("dia = %f, x = %f, y= %f\r\n", Dia1, x, y);
    return Dia1;
}

// diagonaal berekenen voor rechter arm
double CalcDia2 (double x, double y) {
    double a = 50.0;                                
    double BV2 = sqrt(pow((x-a),2) + pow(y,2));     // zelfde nog een keer doen maar nu voor de rechter arm 
    double Dia2 = pow(BV2,2)/(2*BV2); 
    
    //pc.printf("dia = %f, x = %f, y= %f\r\n", Dia2, x, y);
    return Dia2;
}

// calculate Theta1
void CalcTheta1 (double Dia1) {
    double a = 50.0; 
    double Bar = 200.0;    // lengte van de armen 
    
    // Hoek berekenen van het grote tandwiel (gear)
    if (x > -a) {
        Theta1Gear = pi - atan(y/(x+a)) - acos(Dia1/Bar);
    }
    else if (x < -a) {
        Theta1Gear = pi - (pi + atan(y/(x+a))) - acos(Dia1/Bar);
    }
    else {  // als x=-a
        Theta1Gear = 0.5*pi - acos(Dia1/Bar);
    }
    Theta1Gear = Theta1Gear*180.0/pi;       // veranderen van radialen naar graden
             
    // omrekenen van grote tandwiel naar motortandwiel
    Theta1 = Theta1Gear*LargeGear/SmallGear;  // grote tandwiel heeft 42 tanden. Motortandwiel heeft er 12. 
        
    //pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta1, Theta1Gear);
}

void CalcTheta2 (double Dia2) {
    double a = 50.0; 
    double Bar = 200.0;    // lengte van de armen 
    double Prev_Theta2_Gear = Theta2Gear; 
        
    // Hoek berekenen van het grote tandwiel (gear)
    if (x < a) {
        Theta2Gear = pi + atan(y/(x-a)) - acos(Dia2/Bar);
    }
    else if (x > a) {
        Theta2Gear = pi - (pi - atan(y/(x-a))) - acos(Dia2/Bar);
    }
    else {  // als x=a
        Theta2Gear = 0.5*pi - acos(Dia2/Bar);
    }
    Theta2Gear = Theta2Gear*180/pi;         // veranderen van radialen naar graden
    
    // omrekenen van grote tandwiel naar motortandwiel
    Theta2 = Theta2Gear*LargeGear/SmallGear;  // grote tandwiel heeft 42 tanden. Motortandwiel heeft er 12.

    //pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta2, Theta2Gear);
}

// als een van de hoeken te groot wordt, zet dan alles terug naar de vorige positie
void AngleLimits (double Prev_Theta1_Gear, double Prev_Theta2_Gear, double Prev_x, double Prev_y) {
    double MaxThetaGear = 100.0;   // de hoek voordat arm het opstakel raakt (max hoek is 107.62 tussen arm en opstakel, maken er 100 van voor veiligheid)
    
    if (Theta1Gear >= MaxThetaGear || Theta2Gear >= MaxThetaGear) {
        Theta1Gear = Prev_Theta1_Gear;
        Theta2Gear = Prev_Theta2_Gear;
        x = Prev_x;
        y = Prev_y;
        
        Theta1 = Theta1Gear*LargeGear/SmallGear;
        Theta2 = Theta2Gear*LargeGear/SmallGear; 
    }
    //pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta2, Theta2Gear);
}

void CalculationsForTheta () {
    sample();
    double Prev_x = Calc_Prev_x ();
    double Prev_y = Calc_Prev_y ();
    double Prev_Theta1_Gear = Calc_Prev_Theta1_Gear ();
    double Prev_Theta2_Gear = Calc_Prev_Theta2_Gear ();
    CalcXY();
    double Dia1 = CalcDia1 (x, y);
    double Dia2 = CalcDia2 (x, y);
    CalcTheta1 (Dia1);
    CalcTheta2 (Dia2);
    //AngleLimits (Prev_Theta1_Gear, Prev_Theta2_Gear, Prev_x, Prev_y);       // laatste check
}    

////////////////////////// Code for control /////////////////////////////////////

double m1_GetPosition()
{
    int countsCW = m1_EncoderCW.getPulses();
    int countsCCW= m1_EncoderCCW.getPulses();
    int net_counts=countsCW-countsCCW;
    double Position=(net_counts*360.0)/COUNTS_PER_REV;
    return Position;
}

double m2_GetPosition()
{
    int countsCW = m2_EncoderCW.getPulses();
    int countsCCW= m2_EncoderCCW.getPulses();
    int net_counts=countsCW-countsCCW;
    double Position=(net_counts*360.0)/COUNTS_PER_REV;
    return Position;
}

// Position control when calibrating
double m1_GetPosition_cal()
{
    int countsCW = m1_EncoderCW.getPulses();
    int countsCCW= m1_EncoderCCW.getPulses();
    int net_counts=countsCW-countsCCW;
    double Position=(net_counts*360.0)/COUNTS_PER_REV+60.0f*LargeGear/SmallGear; // calibrated position is 210 degrees
    return Position;
}

// Position control when calibrating
double m2_GetPosition_cal()
{
    int countsCW = m2_EncoderCW.getPulses();
    int countsCCW= m2_EncoderCCW.getPulses();
    int net_counts=countsCW-countsCCW;
    double Position=(net_counts*360.0)/COUNTS_PER_REV+60.0f*LargeGear/SmallGear; // calibrated position is 210 degrees
    return Position;
}
double m1_PID(double error, const double Kp, const double Ki, const double Kd, const double Ts, const double N, double &m1_v1, double &m1_v2)
{
    double a1 = -4/(N*Ts+2),
    a2 = -1*(N*Ts - 2)/(N*Ts+2), 
    b0 = (4*Kp + 4*Kd*N + 2*Ki*Ts + 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4),
    b1 = (Ki*N*pow(Ts,2) - 4*Kp - 4*Kd*N)/(N*Ts + 2),
    b2 = (4*Kp + 4*Kd*N - 2*Ki*Ts - 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4);
    
    double v = error - a1*m1_v1 - a2*m1_v2;
    double u = b0*v + b1*m1_v1 + b2*m1_v2;
    m1_v2 = m1_v1; m1_v1 = v;
    return u;
}

double m2_PID(double error, const double Kp, const double Ki, const double Kd, const double Ts, const double N, double &m2_v1, double &m2_v2)
{
    double a1 = -4/(N*Ts+2),
    a2 = -1*(N*Ts - 2)/(N*Ts+2), 
    b0 = (4*Kp + 4*Kd*N + 2*Ki*Ts + 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4),
    b1 = (Ki*N*pow(Ts,2) - 4*Kp - 4*Kd*N)/(N*Ts + 2),
    b2 = (4*Kp + 4*Kd*N - 2*Ki*Ts - 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4);
    
    double v = error - a1*m1_v1 - a2*m1_v2;
    double u = b0*v + b1*m1_v1 + b2*m1_v2;
    m1_v2 = m1_v1; m1_v1 = v;
    return u;
}



void SetMotor(int motor_number, double MotorValue)
{
    // Given -1<=motorValue<=1, this sets the PWM and direction
    // bits for motor 1. Positive value makes motor rotating
    // clockwise. motorValues outside range are truncated to
    // within range
    if (motor_number == 1)
    {
        if (MotorValue >=0)
        {
            motor1_dir=0;
        }
        else
        {
            motor1_dir=1;
        }
        if (fabs(MotorValue)>1){
            motor1_pwm.write(1);
        }
        else
        {
            motor1_pwm.write(abs(MotorValue));
        }
    }
    else
    {  
        if (MotorValue >=0)
        {
            motor2_dir=0;
        }
        else
        {
            motor2_dir=1;
        }
        if (fabs(MotorValue)>1){
            motor2_pwm.write(1);
        }
        else
        {
            motor2_pwm.write(abs(MotorValue));
        }
    }   
}

void BlinkLed(){
    led = not led;
}

void Controller_motor()
{
    // get the actual position
    double m1_Position = m1_GetPosition_cal();
    double m2_Position = m2_GetPosition_cal();
    // Set position scopes
    scope.set(0,x);
    scope.set(1,y);
    scope.set(2,Theta1);
    scope.set(3,Theta2);
    /*
    scope.set(0,Theta1);
    scope.set(1,m1_Position);
    scope.set(3,Theta2);
    scope.set(4,m2_Position);
    */
    //scope.set(2,m1_Position);
    //scope.set(4,m2_Position);
    // calc the error
    double m1_error = Theta1 - m1_Position;
    double m2_error = Theta2 - m2_Position;
    //scope.set(2,m1_error);
    //scope.set(6,m2_error);
    // calc motorvalues for controller;
    double m1_MotorValue = m1_PID(m1_error, Kp, Ki, Kd, Ts, N, m1_v1, m1_v2);
    double m2_MotorValue = m2_PID(m2_error, Kp, Ki, Kd, Ts, N, m2_v1, m2_v2);
    scope.set(4,m1_MotorValue);
    scope.set(5,m2_MotorValue);
    // Set the motorvalues
    SetMotor(1, m1_MotorValue);
    SetMotor(2, m2_MotorValue);
    // Set motorvalues for scope
    // Send data to HIDScope
    scope.send();
}

void PotControl()
{
    double Motor1_Value = (pot1.read() - 0.5f)*1.5f;
    double Motor2_Value = (pot2.read() - 0.5f)*1.5f;
    //pc.printf("\r\n Motor value 1: %f \r\n",Motor1_Value);
    //pc.printf("\r\n Motor value 2: %f \r\n",Motor2_Value);
    double m1_Position = m1_GetPosition();
    double m2_Position = m2_GetPosition();
    scope.set(0, Motor1_Value);
    scope.set(1, m1_Position);
    scope.set(2, Motor2_Value);
    scope.set(3, m2_Position);
    scope.send();
    // Write the motors
    SetMotor(1, Motor1_Value);
    SetMotor(2, Motor2_Value);
}

void ResetEncoders()
{
    m1_EncoderCW.reset();
    m1_EncoderCCW.reset();
    m2_EncoderCW.reset();
    m2_EncoderCCW.reset();
}

void Button1Switch()
{
    button1_value = not button1_value;
    pc.printf("\r\n Button1_value = %d \r\n", button1_value);
}

void Button2Switch()
{
    button2_value = not button2_value;
    pc.printf("\r\n Button2_value = %d \r\n", button2_value);
}

void PANIC()
{
    CalculationsTicker.detach();
    PIDControlTicker.detach();
    Stepper_Off();
    SetMotor(1,0.0);
    SetMotor(2,0.0);
    pc.printf("\r\n PANIC BUTTON PRESSED \r\n");
    pc.printf("\r\n TERMINATING ALL PROCESSES \r\n");
}

////////////////////////// main loop ////////////////////////////////////////////
int main()
{
    // Setup
    // Set baud connection with PC
    pc.baud(SERIAL_BAUD);
    pc.printf("\r\n ***THERMONUCLEAR WARFARE COMMENCES*** \r\n");
    pc.printf("\r\n ***   PROGRAM: CompleteFunction   *** \r\n");
    
    /* making the biquad chain for filtering the emg
    notch and high pass */
    bqc.add( &bq1 ).add( &bq2 );
    bqc2.add( &bq4 ).add( &bq5 );
    bqc3.add( &bq7 ).add( &bq8 );
        
    // Setup Blinking LED to blink every 0.5 sec
    led = 1;
    BlinkLedTicker.attach(BlinkLed,0.5);
    
    // Setup other leds
    red = 1;
    blue = 1;
    green = 1;
    
    // Setup motor PWM speeds
    motor1_pwm.period(1.0/1000);
    motor2_pwm.period(1.0/1000);
    
    // Setup Interruptin.fall
    button1.fall(Button1Switch);
    button2.fall(PANIC);
    button3.fall(Stepper_On);
    //button4.fall(xxx);
        
    // Actual code starts here
    // Start positioning the arms
    pc.printf("\r\n ***************** \r");
    pc.printf("\r\n Press Button 1 to start positioning the arms using PotControl\r");
    pc.printf("\r\n Make sure both potentiometers are positioned halfway! \r");
    pc.printf("\r\n ***************** \r\n");
    //pc.printf("\r\n Button1_value = %d \r\n", button1_value);
    while (button1_value == false)
    {   
        wait(2);
        //pc.printf("\r\n Button1_value = %d \r\n", button1_value);
        //wait(5);
    }
    PotControlTicker.attach(&PotControl,CONTROLLER_TS);
    
    pc.printf("\r\n ***************** \r");
    pc.printf("\r\n When done positioning, press Button 1 to detach Potcontrol");
    pc.printf("\r\n ***************** \r\n");
    while (button1_value == true)
    {   
        //pc.printf("\r\n Button1_value = %d \r\n", button1_value);
        wait(2);
    }
    
    // Detach potcontrol
    PotControlTicker.detach();
    
    // Set motors to 0
    SetMotor(1,0.0);
    SetMotor(2,0.0);
    // Wait a bit to let the movement stop
    wait(0.5);
    // Reset the encoders to set the 0 position
    ResetEncoders();
    
    // PID control starts
    pc.printf("\r\n ***************** \r");
    pc.printf("\r\n When done positioning, press Button 1 to start potmeter PID control");
    pc.printf("\r\n Make sure both potentiometers are positioned halfway!!!");
    pc.printf("\r\n ***************** \r\n");
    while (button1_value == false)
    {   
        //pc.printf("\r\n Button1_value = %d \r\n", button1_value);
        wait(2);
    }
    
    // Attach sample, calc and control tickers
    CalculationsTicker.attach(&CalculationsForTheta,CALC_TS);
    PIDControlTicker.attach(&Controller_motor, CONTROLLER_TS);  
    while(true){}
}