SMARTASSES 2019

Dependencies:   mbed Crypto

main.cpp

Committer:
OmarMuttawa
Date:
2019-03-22
Revision:
12:b39f69ed20af
Parent:
11:aae7c9c290e2

File content as of revision 12:b39f69ed20af:

//SMARTASSES

#include "mbed.h"
#include "SHA256.h"
#include <string>
#include <vector>
#include <RawSerial.h>

//Photointerrupter input pins
#define I1pin D3
#define I2pin D6
#define I3pin D5

//Incremental encoder input pins
#define CHApin   D12
#define CHBpin   D11

//Motor Drive output pins  //Mask in output byte
#define L1Lpin D1          //0x01
#define L1Hpin A3          //0x02
#define L2Lpin D0          //0x04
#define L2Hpin A6          //0x08
#define L3Lpin D10         //0x10
#define L3Hpin D2          //0x20

#define PWMpin D9

//Motor current sense
#define MCSPpin   A1
#define MCSNpin   A0

#define TESTPIN D4  //USED FOR TIMING ETC

//Mapping from sequential drive states to motor phase outputs
/*
State   L1  L2  L3
0       H   -   L
1       -   H   L
2       L   H   -
3       L   -   H
4       -   L   H
5       H   L   -
6       -   -   -
7       -   -   -
*/
//Drive state to output table
const int8_t driveTable[] = {0x12,0x18,0x09,0x21,0x24,0x06,0x00,0x00};

//Mapping from interrupter inputs to sequential rotor states. 0x00 and 0x07 are not valid
const int8_t stateMap[] = {0x07,0x05,0x03,0x04,0x01,0x00,0x02,0x07};  
//const int8_t stateMap[] = {0x07,0x01,0x03,0x02,0x05,0x00,0x04,0x07}; //Alternative if phase order of input or drive is reversed

//Phase lead to make motor spin
volatile int8_t lead = 2;  //2 for forwards, -2 for backwards

//Status LED
DigitalOut led1(LED1);
DigitalOut testPin(TESTPIN);

//Photointerrupter inputs
InterruptIn I1(I1pin);
InterruptIn I2(I2pin);
InterruptIn I3(I3pin);

//Global varibale to count revolutions up to 6
volatile int count_revolution = 0;
volatile int number_of_notes = 0;
volatile int last_count_revolution = 0;
//TUNING PARAMETERS FOR SPEED CONTROLLER 
volatile float K_PS = 0.1;
volatile float K_IS = 0.18;
volatile float K_DS = 0.0001;
volatile float VELOCITY_INTEGRAL_LIMIT = 100;
//TUNING PARAMETERS FOR REVOLUTION CONTROLLER 
volatile float K_PR= 0.1;
volatile float K_IR = 0.001;
volatile float K_DR = 0.05;
volatile float ROTATION_INTEGRAL_LIMIT = 100;

float timeSinceLastRun;
float elapsedTime = 0;

string tune;
char tune_c_str[50];

char notes [50];        //hold the notes
float periods[50];
int durations[50];      //holds the durations
volatile bool playTune = false;
bool playTuneLocal = false; //used to prevent deadlocks and long mutexs
int noteIndex = 0;

//PWM Class:
PwmOut PWM_pin(PWMpin);

//Motor Drive outputs
DigitalOut L1L(L1Lpin);
DigitalOut L1H(L1Hpin);
DigitalOut L2L(L2Lpin);
DigitalOut L2H(L2Hpin);
DigitalOut L3L(L3Lpin);
DigitalOut L3H(L3Hpin);

/* Mail */
typedef struct {
  int type; //0 means it will be a string, 1 means it will be uint64
  uint64_t uint64_message;
  char* string_message;
  float float_message;
} mail_t;

//Declaration of timer global variable
Timer t;

//Declaration of timer for velocity calculations global variable
Timer motorCtrlTimer;

//Threads' declaration
Thread printMsgT;
Thread readMsgT;
Thread motorCtrlT(osPriorityHigh,1024);

//Declaration of Mail global object
Mail<mail_t, 16> mail_box;

//Global to keep to previous position necessary for velocity calculations
float prevPosition;

//Global for motor's position
float motor_position = 0;
float current_position = 0;

//Global variables
int8_t intState,intStateOld,orState;

RawSerial pc(SERIAL_TX, SERIAL_RX);
Queue<void, 8> inCharQ;
uint64_t newKey;
float Ts,Tr,newTorque;
volatile float rot_input=0;
volatile float max_vel = 10;
volatile int exec_count = 0;
            
Mutex newKey_mutex;
Mutex newRotation_mutex;
Mutex newVelocity_mutex;
Mutex countRev_mutex;
Mutex playTune_mutex;
int hashRate;

volatile float velocityError = 0;
volatile float prevVelocityError=0;
volatile float differentialVelocityError = 0;
volatile float integralVelocityError = 0;

volatile float rotationError = 0;
volatile float prevRotationError = 0;
volatile float differentialRotationError = 0;
volatile float integralRotationError = 0;

float max_t = 0;

int target_position,position_error;

//Global variable for velocity
volatile float velocity;

//Set a given drive state
void motorOut(int8_t driveState){
    
    //Lookup the output byte from the drive state.
    int8_t driveOut = driveTable[driveState & 0x07];
      
    //Turn off first
    if (~driveOut & 0x01) L1L = 0;
    if (~driveOut & 0x02) L1H = 1;
    if (~driveOut & 0x04) L2L = 0;
    if (~driveOut & 0x08) L2H = 1;
    if (~driveOut & 0x10) L3L = 0;
    if (~driveOut & 0x20) L3H = 1;
    
    //Then turn on
    if (driveOut & 0x01) L1L = 1;
    if (driveOut & 0x02) L1H = 0;
    if (driveOut & 0x04) L2L = 1;
    if (driveOut & 0x08) L2H = 0;
    if (driveOut & 0x10) L3L = 1;
    if (driveOut & 0x20) L3H = 0;
    }
    
//Convert photointerrupter inputs to a rotor state
inline int8_t readRotorState(){
    return stateMap[I1 + 2*I2 + 4*I3];
}

void Rotorcalib(){
    intState = readRotorState();
    if (intState != intStateOld) {
        //lead_mutex.lock();
        motorOut((intState-orState+lead+6)%6); //+6 to make sure the remainder is positive
        //lead_mutex.unlock();
        //pc.printf("%d\n\r",intState);
        if (intState-intStateOld==-5){
            //Goes positive direction
            motor_position++;
        }else if (intState-intStateOld==5) {
            //Goes negative direction
            motor_position--;
        }else{
            motor_position = motor_position + (intState-intStateOld);   //every 
        }
        //Update state
        intStateOld = intState;
    }    
}
    
//Basic synchronisation routine    
int8_t motorHome() {

    //Put the motor in drive state 0 and wait for it to stabilise
    motorOut(0);
    wait(2.0);
    motorOut(1.0);
    
    //Get the rotor state
    return readRotorState();
}

//Print message from Mail_box
void printMsgFn (void) {
    while(1){
        
        //Recieve a message and print it
        osEvent evt = mail_box.get();
        
        if (evt.status == osEventMail) {
            mail_t *mail = (mail_t*)evt.value.p;
            if(mail->type == 1){    //int
                printf("%llu\n\r", mail->uint64_message);
            }
            else if(mail->type == 2){
                //string!
                printf("%s", mail->string_message);
            }else if (mail->type==3){
                printf("%f\n\r", mail->float_message);
            }
            mail_box.free(mail);
        }
    }
}

//Create a int tupe Mail_box object
void put_msg_int (uint64_t uint64_message) {
    mail_t *mail = mail_box.alloc();
    mail->type=1;
    mail->uint64_message = uint64_message; 
    mail_box.put(mail);
}

//Create a string tupe Mail_box object
void put_msg_string (char* message) {
    mail_t *mail = mail_box.alloc();
    mail->type=2;
    mail->string_message = message; 
    mail_box.put(mail);
}

void put_msg_float (float message) {
    mail_t *mail = mail_box.alloc();
    mail->type=3;
    mail->float_message = message; 
    mail_box.put(mail);
}

//Serial read
void serialISR(){
    //testPin.write(1);
    uint8_t newChar = pc.getc();
    inCharQ.put((void*)newChar);
    //testPin.write(0);
}

//The function the ticker calls 
void motorCtrlTick(){ 
    motorCtrlT.signal_set(0x1); 
}

void printNotes(void){
    put_msg_string(notes);
    for(int i = 0; i < number_of_notes; i++){
        put_msg_float((float)durations[i]);
        }
    for(int i = 0; i < number_of_notes; i++){
        put_msg_float((float)periods[i]);
        }
    }

void processTuneString(void){
        playTune_mutex.lock();
        playTune = false;
        playTune_mutex.unlock();
        strcpy(tune_c_str, tune.c_str());   //turn tune (string) into c style string
        number_of_notes = 0;
        //reset the arrays
        for(int i =0; i<10; i++){
            notes[i] = '\0';
            durations[i] = -1;
        }
            
        char current_char;
        char prev_char;
        int tune_string_length = strlen(tune_c_str);
        int a;
        noteIndex  = 0;
        
        for(a = 0; a < tune_string_length; a++){
            current_char = tune_c_str[a];
            if((current_char >= 'A') &&(current_char <= 'G')){
                notes[number_of_notes] = current_char;
                number_of_notes++;
            }
            else if((current_char >= '1') && (current_char <= '9'))
                durations[number_of_notes - 1] = (int)current_char - 48;
                
            else if(current_char == '#'){
                prev_char = tune_c_str[a-1];
                switch(prev_char){
                    case 'A':
                        notes[number_of_notes - 1] = 'b'; 
                    break;
                    case 'C':
                        notes[number_of_notes - 1] = 'd';
                    break;
                    case 'D':
                        notes[number_of_notes - 1] = 'e'; 
                    break;  
                    case 'F':
                        notes[number_of_notes - 1] = 'g';  
                    break;    
                    case 'G':
                        notes[number_of_notes - 1] = 'a'; 
                    break;  
                } 
            }   //end of #
            else if(current_char == '^'){
                prev_char = tune_c_str[a-1];
                switch(prev_char){
                    case 'B':
                        notes[number_of_notes - 1] = 'b';
                    break;
                    case 'D':
                        notes[number_of_notes - 1] = 'd';
                    break;
                    case 'E':
                        notes[number_of_notes - 1] = 'e';
                    break;
                    case 'G':
                        notes[number_of_notes - 1] = 'g';
                    break;  
                    case 'A':
                        notes[number_of_notes - 1] = 'a';
                    break;
                }//end of switch
            }   //end of ^
            
        }//end of the for loop
        for(a = 0; a < number_of_notes; a++){
        char current_note =  notes[a];
        switch(current_note){
            case 'A': periods[a] = 1.0/440.0;
            break;
            case 'b': periods[a] = 1.0/0466.16;
            break;
            case 'B': periods[a] = 1.0/493.88;
            break;
            case 'C': periods[a] = 1.0/523.25;
            break;
            case 'd': periods[a] = 1.0/554.37;
            break;
            case 'D': periods[a] = 1.0/587.83;
            break;
            case 'e': periods[a] = 1.0/622.25;
            break;
            case 'E': periods[a] = 1.0/659.25;
            break;
            case 'F': periods[a] = 1.0/698.46;
            break;
            case 'g': periods[a] = 1.0/739.99;
            break;
            case 'G': periods[a] = 1.0/783.99;
            break;
            case 'a': periods[a] = 1.0/830.61;
            break;
            default: periods[a] = 1.0/440.0;
                break;           
            }//end of switch
        }//end of for
    playTune_mutex.lock();
    playTune = true;
    playTune_mutex.unlock();
    return;
}


//Run in the motorCtrl thread
void motorCtrlFn(){
    char msg[50];
    Ticker motorCtrlTicker; 
    motorCtrlTicker.attach_us(&motorCtrlTick,100000);   //run every 100ms
    prevPosition=motor_position;
    //Start velocity times
    motorCtrlTimer.start();
    while(1){
         //Wait for ticker message
            
            motorCtrlT.signal_wait(0x1);    
            testPin.write(1);
            motorCtrlTimer.stop();
            timeSinceLastRun = motorCtrlTimer.read();   
            motorCtrlTimer.reset();
            motorCtrlTimer.start();
            //play tune
            
            playTune_mutex.lock();
            playTuneLocal = playTune;
            playTune_mutex.unlock();
            
            if(playTuneLocal == true){
                elapsedTime = elapsedTime + timeSinceLastRun;   //increment the 
                float currentDuration = durations[noteIndex];
                if(elapsedTime >= currentDuration){
                    elapsedTime = 0;
                    noteIndex++;
                    if(noteIndex == number_of_notes)
                        noteIndex = 0;  //reset if needed
                    PWM_pin.period(periods[noteIndex]); //set the 
                }  
            }
            else    //if playTune == false
                PWM_pin.period(0.002f);  
                
                
            //Calculate velocity
            core_util_critical_section_enter();
            current_position=motor_position;   
            core_util_critical_section_exit();
                         
            velocity = (float)((current_position - prevPosition)/(6.0*timeSinceLastRun));    //rps
            prevPosition=current_position;
            
            if((max_vel == 0) && (target_position == 0)){    //no limits            
                lead = 2;
                PWM_pin.write(1.0); 
            }
                
            //speed limit no rotation limit, run forever at defined speed 
            else if ((max_vel != 0) && (target_position == 0)){
                prevVelocityError = velocityError;      
                
                velocityError = max_vel - abs(velocity);    //Proportional
                differentialVelocityError = (velocityError - prevVelocityError)/timeSinceLastRun;   //Differential
                integralVelocityError +=  velocityError*timeSinceLastRun;        // Integral
               
                if(integralVelocityError > VELOCITY_INTEGRAL_LIMIT)  //Limit the integral
                    integralVelocityError = VELOCITY_INTEGRAL_LIMIT;
                if(integralVelocityError < -1*VELOCITY_INTEGRAL_LIMIT)
                    integralVelocityError =(-1*VELOCITY_INTEGRAL_LIMIT);
                
                Ts = K_PS*velocityError + K_IS*integralVelocityError + K_DS*differentialVelocityError;

                lead = (Ts<0) ? -2 : 2;   
      
                Ts = abs(Ts);
                if (Ts>1){
                    Ts=1.0;
                }
                PWM_pin.write(Ts);
                
            }
            //no speed limit, but a rotation limit
            else if((max_vel == 0) && (target_position != 0)){
                prevRotationError = rotationError;
                rotationError = target_position - current_position; //in ISR TICKS
                differentialRotationError = (rotationError - prevRotationError)/timeSinceLastRun; //in ISR ticks /s
                
                
                integralRotationError +=  rotationError*timeSinceLastRun;
                if(integralRotationError > ROTATION_INTEGRAL_LIMIT) 
                    integralRotationError = ROTATION_INTEGRAL_LIMIT;
                if(integralRotationError < -1*ROTATION_INTEGRAL_LIMIT) 
                    integralRotationError = -1*ROTATION_INTEGRAL_LIMIT;
                //put_msg_float(integralRotationError);
                Tr = (K_PR*rotationError) + (K_IR*integralRotationError) + (K_DR*differentialRotationError); // Need to divide by time
             
                // In case the rotations were overshot change the direction back
              
                lead = (Tr > 0) ?  2 : -2;  //change direction if needed
                Tr = abs(Tr);
                Tr = (Tr > 1) ? 1 : Tr;     //clamp at 1.0
                
                if(abs(rotationError) == 0){
                    PWM_pin.write(0.0);
                    }
                else
                    PWM_pin.write(Tr); 
                }
            //speed limit and rotation limit
            else{    //((max_vel != 0) && (target_position != 0)){{
                prevRotationError = rotationError;
                rotationError = target_position - current_position; //in ISR TICKS
                float differentialRotationError = (rotationError - prevRotationError)/timeSinceLastRun;
                integralRotationError +=  (rotationError*timeSinceLastRun);
                if(integralRotationError > ROTATION_INTEGRAL_LIMIT) 
                    integralRotationError = ROTATION_INTEGRAL_LIMIT;
                if(integralRotationError < -1*ROTATION_INTEGRAL_LIMIT) 
                    integralRotationError = -1*ROTATION_INTEGRAL_LIMIT;
                //put_msg_float(integralRotationError);
                Tr = (K_PR*rotationError) + (K_IR*integralRotationError) + (K_DR*differentialRotationError); // Need to divide by time
                
                prevVelocityError = velocityError;
                velocityError = max_vel - abs(velocity);
                float differentialVelocityError = (velocityError - prevVelocityError)/timeSinceLastRun;
                
                // Integral error
                integralVelocityError +=  velocityError*timeSinceLastRun;
                if(integralVelocityError > VELOCITY_INTEGRAL_LIMIT) 
                    integralVelocityError = VELOCITY_INTEGRAL_LIMIT;
                if(integralVelocityError < -1*VELOCITY_INTEGRAL_LIMIT)
                    integralVelocityError =(-1*VELOCITY_INTEGRAL_LIMIT);
               
                Ts = K_PS*velocityError + K_IS*integralVelocityError + K_DS*differentialVelocityError;
                if(differentialRotationError < 0)
                    Ts = -Ts;
                                   
                    
                if(abs(velocity) > max_vel){   
                    newTorque = min(abs(Tr), abs(Ts));
                    }
                else                               
                    newTorque = max(abs(Tr), abs(Ts));
                    
                if(abs(rotationError)<10)   //if we're close, take Tr
                    newTorque = abs(Tr);
                    
                lead = (rotationError >= 0) ?  2 : -2;
                
                
                
                newTorque = abs(newTorque);
                
                if(newTorque > 1.0)
                    newTorque = 1.0;
                
                if(abs(rotationError)  == 0)
                    PWM_pin.write(0.0);
                else
                    PWM_pin.write(newTorque); 
                
            }
             testPin.write(0);      
    }//end of while 1
 }//end of fn


//Receiving command serially and decoding it 
void readMsgFn(){
    string message_string;
    pc.attach(&serialISR);
    char message[18];
    int i=0; 
    while(1){ 
        osEvent newEvent = inCharQ.get();
        //testPin.write(1);
        uint8_t newChar = (uint8_t)newEvent.value.p;
        if(i >= 17) 
            i=0;
        else{
            message[i]=newChar;
            i++;
        }
        if(newChar=='\r' || newChar == '\n'){
            if(message[0]=='K'){
                newKey_mutex.lock();
                put_msg_string("Entering New Key: ");
                sscanf(message,"K%x",&newKey);
                message[i]='\0';
                put_msg_int(newKey);
                newKey_mutex.unlock();
                i=0;
            }
            else if(message[0]=='R'){
                newRotation_mutex.lock();
                integralRotationError = 0;  //reset the sum!
                integralVelocityError = 0;  //reset the sum!
                sscanf(message,"R%f",&rot_input);   //rot_input in number of revs
                if(rot_input == 0)
                    target_position = 0;
                else
                    {
                    target_position = current_position + (rot_input*6); //in ISR TICKS
                    }
                message[i]='\0';
                newRotation_mutex.unlock();
                i=0;
            }
            else if(message[0]=='V'){
                newVelocity_mutex.lock();
                sscanf(message,"V%f",&max_vel);
                integralVelocityError = 0;  //reset the sum!
                integralRotationError = 0;  //reset the sum!
                newVelocity_mutex.unlock();
                //printf("%f",max_vel);
                message[i]='\0';
                i=0;
            }
            else if(message[0] == 'T'){
                sscanf(message,"T%s",tune);
                if(message[1] == '0'){
                    playTune_mutex.lock();
                    playTune = false;
                    playTune_mutex.unlock();
                }
                else
                    processTuneString();
                message[i] = '\0';
                i=0;
            }               
        }
         //testPin.write(0);
    }
} 

 
//Main
int main() {
    string msg="Hello World";
    SHA256 mySHA256 = SHA256();
    //put_msg("HELLO");
    uint8_t sequence[] = {0x45,0x6D,0x62,0x65,0x64,0x64,0x65,0x64,
    0x20,0x53,0x79,0x73,0x74,0x65,0x6D,0x73,
    0x20,0x61,0x72,0x65,0x20,0x66,0x75,0x6E,
    0x20,0x61,0x6E,0x64,0x20,0x64,0x6F,0x20,
    0x61,0x77,0x65,0x73,0x6F,0x6D,0x65,0x20,
    0x74,0x68,0x69,0x6E,0x67,0x73,0x21,0x20,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    uint64_t* key = (uint64_t*)((int)sequence + 48);
    uint64_t* nonce = (uint64_t*)((int)sequence + 56);
    
    uint8_t hash[32];
    
    orState = 0;    //Rotot offset at motor state 0
    intState = 0;
    intStateOld = 0;
    
    //Initialise PWM and Duty Cycle;
    PWM_pin.period(0.002f);
    PWM_pin.write(1.00f);
    
    pc.printf("Hello\n\r");
    
    //Run the motor synchronisation
    orState = motorHome();
    pc.printf("Rotor origin: %x\n\r",orState);
    
    //orState is subtracted from future rotor state inputs to align rotor and motor states
    I1.rise(&Rotorcalib);
    I2.rise(&Rotorcalib);
    I3.rise(&Rotorcalib);
    I1.fall(&Rotorcalib);
    I2.fall(&Rotorcalib);
    I3.fall(&Rotorcalib);
    
    hashRate = 0;
    t.start();
    
    printMsgT.start(callback(printMsgFn));  //start print msg thread
    readMsgT.start(callback(readMsgFn)); 
    motorCtrlT.start(callback(motorCtrlFn)); // start the motorCtrlT thread

    
    while (1) { 
        //testPin.write(!testPin.read());
        newKey_mutex.lock();
        *key = newKey;
        newKey_mutex.unlock();
        
        mySHA256.computeHash(hash,sequence,sizeof(sequence));
        hashRate++;
        if(t.read() >= 1){  //if a second has passed
            t.stop();
            t.reset();
            t.start();
            char msg[10];
            strcpy(msg, "VELOCITY: ");
            put_msg_string(msg);
            put_msg_float(velocity);
            char msg2[10];
            strcpy(msg2, "HASH RATE: ");
            put_msg_string(msg2);
            put_msg_int(hashRate);    //hashes per second
            hashRate = 0;
        }
        if((hash[0] == 0) && (hash[1] == 0)){
            char msg1[10];
            strcpy(msg1, "SUCCESS: ");
            put_msg_string(msg1);
            put_msg_int(*nonce); 
        }
        *nonce+=1;
        
    }
}