version 1.0

Dependencies:   CMSIS_DSP_401 GPS MPU9150_DMP PID QuaternionMath Servo mbed

Fork of SolarOnFoils_MainModule_20150518 by Dannis Brugman

main.cpp

Committer:
Dannis_mbed
Date:
2015-08-11
Revision:
2:f6d058931b17
Parent:
1:b4a0d63db637

File content as of revision 2:f6d058931b17:

//////////////////////////////////////////////////////////////////////////////////////
//                                                                                  //
//      File        : main.cpp                                                      //
//      Version     : 0.1                                                           //
//      Date        : 25 march 2015                                                 //
//      Author      : Dany Brugman                                                  //
//      Comment     : Function to write data to a 2x16 LCD by I2C                   //
//                    using a MCP23017 port expander.                               //
//                                                                                  //
//      Changelog   :                                                               //
//      Date:           Name:       Comment:                                        //
//      25/03/2015      DNB         First version                                   //
//                                                                                  //
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
// includes                                                                         //
//////////////////////////////////////////////////////////////////////////////////////

#include "mbed.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LCD_I2C.h"
#include "uart.h"
#include "GPS.h"
#include "MPU9150.h"
#include "Quaternion.h"
#include "PID.h"
#include "Servo.h"
#include "mRotaryEncoder.h"
#include "MainModule.h"
#include "menu.h"
#include "systemVar.h"

//////////////////////////////////////////////////////////////////////////////////////
// defines                                                                          //
//////////////////////////////////////////////////////////////////////////////////////

#define CLEAR "\033[2J"
#define PROJECT "\033[1;10fSolar on Foils\n"
#define VERSION "\033[2;10fVersion 1.0\n"

#define EXT_UI_ID               20
#define ACTUATOR_PORT_ID        101
#define ACTUATOR_STARB_ID       102
#define ACTUATOR_PORT_RUN       103
#define ACTUATOR_STARB_RUN      104
#define HEIGHT_PORT_ID          201
#define HEIGHT_STARB_ID         202
#define HEIGHT_PORT_DATA        203
#define HEIGHT_STARB_DATA       204
#define GYRO_GPS_ID             205
#define EXT_UI_HEIGHT           1020 
#define MESSAGE_ALL             2000
#define PORT_ACT_DIAGN          2001
#define STARB_ACT_DIAGN         2002
#define PORT_HGHT_DIAGN         2003
#define STARB_HGHT_DIAGN        2004
#define GYRO_GPS_DIAGN          2005
#define EXT_UI_DIAGN            2006

#define CCW                     0
#define CW                      1

#define V_MAX                   1.85
#define NSAMPLES                10000    //current samples for ref.

#define ROLL_IN_MIN -90
#define ROLL_IN_MAX 90
#define ROLL_OUT_MIN -0.1
#define ROLL_OUT_MAX 0.1

#define Kc 1.0
#define Ti 0.0
#define Td 0.0
#define RATE 0.01

enum MSG_t
{
    MSG1 = 0,       //  message 1
    MSG2,           //  message 2
    MSG3            //  message 3  
};

//////////////////////////////////////////////////////////////////////////////////////
// port declaration                                                                 //
//////////////////////////////////////////////////////////////////////////////////////

DigitalOut myLed(LED1);
DigitalOut myLed2(LED2);

DigitalOut aliveLed(p23);
DigitalOut statusLed(p26);
DigitalOut emergencyLed(p25);
DigitalOut motorRelais(p20);

AnalogIn currentSensor(p15);

InterruptIn intAGM(p8);

I2C i2c(p9, p10);
GPS gps(p13, p14);
// I2C Rx, I2C Tx, Int
MPU9150 imu(p10, p9, p8);  
//mRotaryEncoder(PinName pinA, PinName pinB, PinName pinSW, PinMode pullMode=PullUp, int debounceTime_us=1000)
mRotaryEncoder  rSwitch(p12, p11, p7);
CAN CANbus(p30, p29);
Serial debug(USBTX, USBRX);

PID rollPID(Kc,Ti,Td,RATE); // declare a PID for roll adjustment

LCD_I2C LCD(p9, p10, 0x40);

//////////////////////////////////////////////////////////////////////////////////////
// function prototypes                                                              //
//////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////
// global variables                                                                 //
//////////////////////////////////////////////////////////////////////////////////////

int i = 0;
int value = 0;
int lastGet;
int thisGet;
int iFilter;
uint32_t uiFilterId;
  

bool enc_pressed = false;       // Button of rotaryencoder was pressed
bool enc_rotated = false;       // rotary encoder was totaded left or right
bool bSystemFail = false;       // sytem fail

char cBuffer[200];
char cMessage;
char text[16];
char cPTurns[2] ={0};
char cSTurns[2] ={0};

float   fRoll, fPitch, fEulerRoll;
float   fRollDiff;
float   fPreviousTime;

double  dCurrent    = 0.0;
double  dRefCurrent = 0.0;

Quaternion q1;

Ticker  CANTicker;
Ticker  tAliveOn;
Ticker  tEmergencyLedOn;
Ticker  tMPU;

Timeout tAliveOff;
Timeout tEmergencyLedOff;
Timer   actualTime;
Timer   timer;

Menu mLCDMenu;
SystemVar svSoF;

MSG_t messageToSend;
//////////////////////////////////////////////////////////////////////////////////////
// main                                                                             //
//////////////////////////////////////////////////////////////////////////////////////

int main()
{
//////////////////////////////////////////////////////////////////////////////////////
// init                                                                             //
//////////////////////////////////////////////////////////////////////////////////////      
    vInit();        
    // init CANbus
    vInitCANBus();
    // init Gyro
    vInitImu();
    
       
    CANMessage msg;
    messageToSend = MSG1;
    
    
    //acquiring v_ref
    for (int i=0; i<NSAMPLES; i++) 
    {
        dRefCurrent += currentSensor;
    }
    dRefCurrent /= NSAMPLES;
    //debug.printf("RefCurrent : %f\n\r", dRefCurrent);
    
    timer.start();      //timer to test imu roll angle
    actualTime.start(); //crash timer 
    // clear line -1-
    LCD.vLCD_printPos_I2C((unsigned char*)"                 ", 1, 1);
    
//////////////////////////////////////////////////////////////////////////////////////
// endless loop                                                                     //
//////////////////////////////////////////////////////////////////////////////////////
    while (1)
    {
        // rotary switch has been rotated?
        if (enc_rotated) vSwitchRotated();

        // rotary switch confirm pressed?
        if (enc_pressed) vSwitchConfirmed();
        
        // answer CAN
        if(CANbus.read(msg)){
            if (msg.id == EXT_UI_HEIGHT) svSoF.vVarHeightFoilBorne((uint32_t) msg.data[0]);
        }    
        if(CANbus.read(msg)){
            debug.printf("ID: %i.\t", msg.id);
            if ((msg.id == HEIGHT_PORT_DATA)&&(mLCDMenu.bGetReadHeight() == true))
            { 
                svSoF.vSetPHeight((uint32_t) msg.data[0]);
                debug.printf("height received: %c cm\r\n", msg.data[0]);
            }
            //if ((msg.id == ACTUATOR_PORT_ID)&&(msg.data[0] == 0xFF)) statusLed = ! statusLed;
            //debug.printf("ID#102 received: %d answer.\r\n", msg.data[0]);
        }
        
        if(timer.read_ms() > 250)
        {
            timer.reset();
            if(svSoF.uiGetRoll() >= 4)
            {
                if(svSoF.iGetRollPolarity() == 0)
                {
                   cPTurns[1] = 1;
                   cSTurns[1] = 0;
                }
                else
                {
                   cPTurns[1] = 0;
                   cSTurns[1] = 1;
                } 
                cPTurns[0] = 1;
                cSTurns[0] = 1;
            
                CANbus.write(CANMessage(ACTUATOR_PORT_RUN, cPTurns, 2)); // send message to port actuator
                wait(0.15);
                CANbus.write(CANMessage(ACTUATOR_STARB_RUN, cSTurns, 2)); // send message to port actuator           
                
            }//end if
            svSoF.vSetPitch(q1);                                 // call function class SystemVar
            svSoF.vSetRoll(q1);
            mLCDMenu.vShowScreen(mLCDMenu.getScreen());
            LCD.vLCD_update();
            
            //debug.printf("Roll: %c\t", svSoF.getValue());
            //debug.printf("Roll: %f\t", getRollAngle(q1));
            //debug.printf("Pitch: %f\t", getPitchAngle(q1));
            //debug.printf("Time: %f\r\n", actualTime.read());
        }
        /*for (int samples = 0; samples<NSAMPLES; samples++) dCurrent += currentSensor;
            
            dCurrent /= NSAMPLES;
            //debug.printf("RefCurrent : %f\n\r", dRefCurrent);
            //debug.printf("dCurrent   : %f\n\r", dCurrent);
            dCurrent = (dCurrent-dRefCurrent)*V_MAX/0.185;
        
        debug.printf("Current mA : %f\n\r", (dCurrent*1000));*/
        wait(0.2);
       
    }
    
} // END MAIN 

//////////////////////////////////////////////////////////////////////////////////////
// Init SOF module                                                                  //
//////////////////////////////////////////////////////////////////////////////////////
void vInit (void)
{
    // I2C init
    i2c.frequency(100000);
    //Port A is databus - Output
//    mcp23017.direction(PORT_A, PORT_DIR_OUT);
    //Port B is controlbus - Output
//    mcp23017.direction(PORT_B, PORT_DIR_OUT);
   
    debug.baud(115200);
    // initialize LCD
    //vLCD_init_I2C();
    mLCDMenu.vSelectMenu(_MENU0_0);
    LCD.vLCD_update();
      
    // led's out
    aliveLed = 1; wait(0.1); aliveLed = 0;
    statusLed = 1; wait(0.1); statusLed = 0;
    emergencyLed = 1; wait(0.1); emergencyLed = 0;
        
    wait(0.2);
    motorRelais = 1;
    wait(1.0);
    motorRelais = 0;
    
    //Int-Handler rotary encoder switch
    // call trigger_sw() when button of rotary-encoder is pressed
    rSwitch.attachSW(&trigger_sw);
    // call trigger_rot() when the shaft is rotaded left or right
    rSwitch.attachROT(&trigger_rotated);
    lastGet = 0;
    // set encrotated, so position is displayed on startup
    enc_rotated = true;
    // blink alive LED
    aliveLed = 1;
    tAliveOff.attach(&vBlinkOnce_cb, 0.05);
    tAliveOn.attach(&vBlinkOnce_cb, 5);   
}

//////////////////////////////////////////////////////////////////////////////////////
// Init MPU 9150                                                                    //
//////////////////////////////////////////////////////////////////////////////////////
void vInitImu (void)
{
    if(imu.isReady()) mLCDMenu.vShowScreen(_IMUREADY);
    else  mLCDMenu.vShowScreen(_IMUFAIL);
    LCD.vLCD_update();
        
    imu.initialiseDMP();
    imu.setFifoReset(true);    
    imu.setDMPEnabled(true);
    intAGM.rise(&vGetMPUBuffer);
    //tMPU.attach_us(&vGetMPUBuffer,50000); 
}

//////////////////////////////////////////////////////////////////////////////////////
// Init CANbus                                                                      //
//////////////////////////////////////////////////////////////////////////////////////
void vInitCANBus (void)
{
    CANbus.frequency(250000);
    debug.printf("vInitCANBus\r\n");
    CANMessage msg;
    int i, iTry = 10;
    uint32_t id = MESSAGE_ALL;
    bool bReceived = false;
    cMessage = 0x00;
    // check CAN system
    for (i = 6; i > 0 ; i--)
    {
        debug.printf("for loop: %i\r\n", i);
        bReceived = false;
        id++;   // increase id
        while ((iTry > 0)&&(!bReceived))
        {
            CANbus.write(CANMessage(id, &cMessage, 1));
            wait(0.15);
            switch(id){     // set adress to filter 
                case PORT_ACT_DIAGN:         uiFilterId = ACTUATOR_PORT_ID; break;
                case STARB_ACT_DIAGN:        uiFilterId = ACTUATOR_STARB_ID; break;
                case PORT_HGHT_DIAGN:        uiFilterId = HEIGHT_PORT_ID; break;
                case STARB_HGHT_DIAGN:       uiFilterId = HEIGHT_STARB_ID; break;
                case GYRO_GPS_DIAGN:         uiFilterId = GYRO_GPS_ID; break;
                case EXT_UI_DIAGN:           uiFilterId = EXT_UI_ID; break;
            }//end switch
            iFilter = CANbus.filter(0x200, 0x7FF, CANStandard);
            if(CANbus.read(msg, iFilter)){
                debug.printf("can.read(msg) ID: %i\r\n", id);
                debug.printf("can.msg id: %i, %i\r\n", msg.id, msg.data[0]);
                switch(id){
                    case PORT_ACT_DIAGN:        if((msg.id == ACTUATOR_PORT_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID101OK); break;
                    case STARB_ACT_DIAGN:       if((msg.id == ACTUATOR_STARB_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID102OK); break;
                    case PORT_HGHT_DIAGN:       if((msg.id == HEIGHT_PORT_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID201OK); break;
                    case STARB_HGHT_DIAGN:      if((msg.id == HEIGHT_STARB_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID202OK); break;
                    case GYRO_GPS_DIAGN:        if((msg.id == GYRO_GPS_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID205OK); break;
                    case EXT_UI_DIAGN:          if((msg.id == EXT_UI_ID)&&(msg.data[0] == 0xFF)) mLCDMenu.vShowScreen(_CANID1001OK); break;                                                                                      
                } //end switch
                LCD.vLCD_update();
                bReceived = true;
            } //end if
            else if(iTry == 1){
                switch(id){
                    case PORT_ACT_DIAGN:        mLCDMenu.vShowScreen(_CANID101FAIL); break;
                    case STARB_ACT_DIAGN:       mLCDMenu.vShowScreen(_CANID102FAIL); break;
                    case PORT_HGHT_DIAGN:       mLCDMenu.vShowScreen(_CANID201FAIL); break;
                    case STARB_HGHT_DIAGN:      mLCDMenu.vShowScreen(_CANID202FAIL); break;
                    case GYRO_GPS_DIAGN:        mLCDMenu.vShowScreen(_CANID205FAIL); break;
                    case EXT_UI_DIAGN:          mLCDMenu.vShowScreen(_CANID1001FAIL); break;                                                                                      
                } //end switch
                LCD.vLCD_update();
            } //end if
            if(CANbus.read(msg)){}//unwanted message
            if(!bReceived) iTry--;
            debug.printf("Tries: %i\r\n", iTry);                
        } //end while
        wait(0.5); // some time to read message
        iTry = 10; // reset try counter
// *****afhandelen can fail ****        
        if (iTry != 0) iTry = 10;
        else bSystemFail = true;
    } //end for

    // in case system fail
    if(bSystemFail){ 
        vCallEmergencyLed();
        mLCDMenu.vShowScreen(_CANNORESPONSE);
    }
    cMessage = (char) svSoF.iGetHeightFoilBorne();
    CANbus.write(CANMessage(EXT_UI_HEIGHT, &cMessage, 1));
    
    mLCDMenu.vSelectMenu(_MENU0_0);
    LCD.vLCD_update();
    debug.printf("end can init");  
}

// call vCANBusSend every 1 second test
//    CANTicker.attach(&vCANBusSend, 1);

//////////////////////////////////////////////////////////////////////////////////////
// Blink alive led                                                                  //
//////////////////////////////////////////////////////////////////////////////////////
void vBlinkOff_cb (void)
{
    aliveLed = 0;
}

//////////////////////////////////////////////////////////////////////////////////////
// Blink emergecy led                                                                  //
//////////////////////////////////////////////////////////////////////////////////////
void vEmergencyOff_cb (void)
{
    emergencyLed = 0;
}

//////////////////////////////////////////////////////////////////////////////////////
// Blink status led once                                                            //
//////////////////////////////////////////////////////////////////////////////////////
void vBlinkOnce_cb (void)
{
    aliveLed = 1;
    tAliveOff.detach();
    tAliveOff.attach(&vBlinkOff_cb, 0.05);
}

//////////////////////////////////////////////////////////////////////////////////////
// Blink emergency led once                                                            //
//////////////////////////////////////////////////////////////////////////////////////
void vEmergencyOnce_cb (void)
{
    emergencyLed = 1;
    tEmergencyLedOff.detach();
    tEmergencyLedOff.attach(&vEmergencyOff_cb, 0.05);
    
    if (!bSystemFail)
    {
        tEmergencyLedOn.detach();
        tEmergencyLedOff.detach();
        aliveLed = 0;    
    }
}

//////////////////////////////////////////////////////////////////////////////////////
// Call emergency led                                                               //
//////////////////////////////////////////////////////////////////////////////////////
void vCallEmergencyLed(void)
{
        aliveLed = 1;
        tEmergencyLedOff.attach(&vEmergencyOnce_cb, 0.05);
        tEmergencyLedOn.attach(&vEmergencyOnce_cb, 0.5);    
}

//////////////////////////////////////////////////////////////////////////////////////
// calculate Roll from quaternion                                                   //
//////////////////////////////////////////////////////////////////////////////////////
float getRollAngle(Quaternion q1)
{
    float fRoll;
    fRoll = atan2(2*(q1.v.x*q1.v.y + q1.w*q1.v.y), q1.w*q1.w + q1.v.x*q1.v.x - q1.v.y*q1.v.y - q1.v.y*q1.v.y);
    fRoll *= 180/3.14;
    return (fRoll);
}

//////////////////////////////////////////////////////////////////////////////////////
// calculate Pitch from quaternion                                                   //
//////////////////////////////////////////////////////////////////////////////////////
float getPitchAngle(Quaternion q1)
{
    Vector3 euler = q1.getEulerAngles();
    //debug.printf("$ Pitch = %f #\t", euler.y);
    float fPitch;
    //fPitch = atan2((2*((q1.v.x*q1.v.z) - (q1.w*q1.v.y))),(sqrt((2*((q1.w*q1.v.x) + (q1.v.y*q1.v.z)))*(2*((q1.w*q1.v.x) + (q1.v.y*q1.v.z)))) + (((q1.w*q1.w) - (q1.v.x*q1.v.x) - (q1.v.y*q1.v.y) + (q1.v.z*q1.v.z))*((q1.w*q1.w) - (q1.v.x*q1.v.x) - (q1.v.y*q1.v.y) + (q1.v.z*q1.v.z)))));
    //fPitch = acos(-(q1.w*q1.w) - (q1.v.x*q1.v.x) + (q1.v.y*q1.v.y) + (q1.v.z*q1.v.z));
    //fPitch = -asin(2*q1.w*q1.v.y - 2*q1.v.x*q1.v.z);
    fPitch = euler.y;
    fPitch *= 180/3.14;
    return (fPitch);
    
}

//////////////////////////////////////////////////////////////////////////////////////
// interrup-Handler for button on rotary-encoder                                    //
//////////////////////////////////////////////////////////////////////////////////////
void trigger_sw(void) 
{
    enc_pressed = true;               // just set the flag, rest is done outside isr
}

//////////////////////////////////////////////////////////////////////////////////////
// interrup-Handler for rotary-encoder rotation                                     //
//////////////////////////////////////////////////////////////////////////////////////
void trigger_rotated(void) 
{
    enc_rotated = true;               // just set the flag, rest is done outside isr
}

//////////////////////////////////////////////////////////////////////////////////////
// reset for rotary-encoder switch                                                  //
//////////////////////////////////////////////////////////////////////////////////////
void vResetSwitch (void)
{
    rSwitch.Set(0);
}

//////////////////////////////////////////////////////////////////////////////////////
// set for rotary-encoder switch                                                    //
//////////////////////////////////////////////////////////////////////////////////////
void vSetSwitch (int value)
{
    rSwitch.Set(value);   
}

//////////////////////////////////////////////////////////////////////////////////////
// rotary-encoder switch rotated                                                    //
//////////////////////////////////////////////////////////////////////////////////////
void vSwitchRotated (void)
{
    enc_rotated = false;
    
    // get value from rotary encoder
    thisGet = rSwitch.Get();
    
    if(rSwitch.Get()<0) mLCDMenu.vRotaryDown();
    else if(rSwitch.Get()>=0) mLCDMenu.vRotaryUp();
    
    // reset switch count
    vResetSwitch();
    LCD.vLCD_update();
}

//////////////////////////////////////////////////////////////////////////////////////
// rotary-encoder switch confirmed                                                  //
//////////////////////////////////////////////////////////////////////////////////////
void vSwitchConfirmed (void)
{
    enc_pressed = false;
    
    mLCDMenu.vRotaryConfirm();
    
    // reset switch count
    vResetSwitch();
    
    LCD.vLCD_update();
}

//////////////////////////////////////////////////////////////////////////////////////
// CANbus send  not used                                                            //
//////////////////////////////////////////////////////////////////////////////////////
void vCANBusSend(void) 
{
    static char counter = 0;
    static char counterTwo = 0;
    static char random[8] = {0};
    //vLCD_printPos_I2C((unsigned char*)"CANbus send     ", 2, 1);
    switch(messageToSend){
        case MSG1:
        if(CANbus.write(CANMessage(1336, &counter, 1))) 
        {
            counter++;
            messageToSend = MSG2;
            break;
        }
        case MSG2: 
        if(CANbus.write(CANMessage(1003, &counterTwo, 1))) 
        {
            counterTwo++;
            messageToSend = MSG3;
            break;
        }
        case MSG3:
        if(CANbus.write(CANMessage(1236, random, 4))) 
        {
            random[0] = rand()% 10 + 1;
            random[1] = rand()% 10 + 20;
            random[2] = rand()% 100 + 1;
            random[3] = 00;
            //messageToSend = MSG1;
            break;
        }
    }   // end switch case
     
    myLed = !myLed;
}

//////////////////////////////////////////////////////////////////////////////////////
// Get data from MPU fifo buffer                                                    //
//////////////////////////////////////////////////////////////////////////////////////
void vGetMPUBuffer(void)
{
    int iFifoCount;
    if (imu.getInterruptFifoOverflow())
        imu.setFifoReset(true);
    iFifoCount = imu.getFifoCount();
    //debug.printf("fifocount: %i\r\n", iFifoCount);
    if(imu.getFifoCount() >= 48){
        if(iFifoCount % 48 != 0) imu.getFifoBuffer(cBuffer,  iFifoCount % 48);   
    //imu.getFifoBuffer(cBuffer,  iFifoCount- 48);
    imu.getFifoBuffer(cBuffer,  48);
        //myLed2 = !myLed2;
        q1.decode(cBuffer);// quaternion vector to buffer
     }  
}

//////////////////////////////////////////////////////////////////////////////////////
// EOF                                                                              //
//////////////////////////////////////////////////////////////////////////////////////

    /*if (fifoCount == 1024)                                                                                                                                  // test
        {mpu.resetFIFO();}            // reset so we can continue cleanly
   
    else if (fifoCount >= 42)                // otherwise, check for DMP data ready interrupt (this should happen frequently)
    {
        if ((fifoCount % stMPU9150->packetSize) != 0)                                                                                                       // test
            {mpu.getFIFOBytes(fifoBuffer, (fifoCount % stMPU9150->packetSize));}                                                                            // test
       
        while (mpu.getFIFOCount() >= 42)
            {mpu.getFIFOBytes(fifoBuffer, stMPU9150->packetSize);}            // read a packet from FIFO
       
        mpu.dmpGetQuaternion(&stMPU9150->q, fifoBuffer);*/