Michael Ling / ExoController

Dependents:   Data-Management-Honka

BluetoothComm.cpp

Committer:
mzling
Date:
2015-04-24
Revision:
18:34ccf02fdbe7
Parent:
17:80affee96096
Child:
19:f3eece6c024f

File content as of revision 18:34ccf02fdbe7:

#include "mbed.h"
#include "MODSERIAL.h"
#include "BluetoothComm.h"
#include "initDatabed.h"
#include <string>
#include <map>

BluetoothComm::BluetoothComm(PinName tx, PinName rx): _rn42(tx, rx), _len(0), _counter(0), _inMsg(false), _numVars(32), _numReadOnlyParams(12), _escapesNeeded(8)
{
    
    /* Make sure baud rate is correct--rn42 cannot be read if code and MBED have different baud rates! */
    //_rn42.baud(9600);
    
    _rn42.baud(115200);
    int temp1[] = {0,1,2,3,4,8,9,10, -1};
  
    for (int i = 0; i < _escapesNeeded+1; i++) {
        _escapeNeeded[i] = temp1[i];
    }
    
  
    std::string temp2[] = {"KPStance", "KPSwing", "KPStanding", "KPSitting", "KPStandUp", "KPSitdown", "KDStance", "KDSwing",
        "KDStanding", "KDSitting", "KDStandUp", "KDSitDown", "StandingAngle", "SittingAngle", "BentForwardAngle", "ForwardAngle", "RearAngle",
        "IMUAngle", "KneeFullRetract", "KneeFullExtend", "LockTime", "Rate", "StandupAsst", "StandupTime", "SitdownAsst", "SitdownTime", "WalkAngle",
        "StepLength", "StepTime", "PeakFlexion", "PhaseShift", "WalkSpeed", 
        "TorsoAng", "LKneeAng", "RKneeAng", "LHipAng", "RHipAng", "LHipTorque", "RHipTorque", "ExoAndKneeStates", "TorsoRefAngle", "LHipRefAngle",
        "RHipRefAngle", "Charge"};
    //Populate the map of indices to param names
    for (int j = 0; j < (_numVars + _numReadOnlyParams); j += 1) {
        _indexMap[j] = temp2[j];
    }
    
 
    write_params_to_sd_card();

    int temp4[] = {0x01fe, 0x02ac, 0x02ff, 0x0180, 0x0012, 0x0010, 0x0020, 0x00bf, 0x023f, 0x0123, 0x03a2, 0x10};
    //readData.write(_numReadOnlyParams, temp4);
    for (int k = 0; k < _numReadOnlyParams; k += 1) {
        _paramMap[_indexMap[_numVars + k]] = temp4[k];
    }
    //pc.printf("Starting sdcard stuff\r\n");
    write_data_to_sd_card();
    //read_data_from_sd();
   // set_data(0, 0xdead);
    //set_data(1, 0xdead);
    //set_data(2, 0xdead);
    //set_data(3, 0xdead);
   // pc.printf("Data is written\r\n");
    //Fill the parameter map with data from SD card
    read_data_from_sd();
  //  pc.printf("Data is read\r\n");
    read_params_from_sd();
    pc.printf("Finished BTComm init\r\n");

}

/**
* Reads read-only data from SD card, and fills in the parameter map.
* @author Michael Ling
* @date 2/4/2015
*/


void BluetoothComm::read_data_from_sd()
{
    pc.printf("Beginning to read\r\n");
    int arr[_numReadOnlyParams];
    readData.read(_numReadOnlyParams, arr);
    pc.printf("Finished read\r\n");
    for (int i = 0; i < _numReadOnlyParams; i += 1) {
        //pc.printf("arr[%d] is %x\r\n", i, arr[i]);
        _paramMap[_indexMap[i + _numVars]] = arr[i];
       
    }
    
  
}

/**
* Reads editable parameters from SD card, and fills in the parameter map
* @author Michael Ling
* @date 2/4/2015
*/

void BluetoothComm::read_params_from_sd()
{
    int *arr = param.read(_numVars, arr);
    for (int i = 0; i < _numVars; i += 1) {
        _paramMap[_indexMap[i]] = arr[i];
    }
    
}

/**
* Calculates parity--0 if c even, 1 if odd
* @param c    The short that we want to calculate parity of
* @author Michael Ling
* @date 2/4/2015
*/
bool BluetoothComm::parity(short c)
{
    bool p = false;
    while (c != 0) {
        p = !p;
        c = (short) (c & (c-1));
    }
    return p;
}

/**
* Calculates checksum of char array, sum of all chars in array
* @param b    The char array to be summed up
* @param len    Length of the array b
* @author Michael Ling
* @date 2/4/2015
*/
char* BluetoothComm::get_checksum(char* b, int len)
{
    char* checksum = (char*)malloc(3*sizeof(char));
    short sum = 0;
    for (int i = 0; i < len; i += 1) {
        sum += ((short) b[i] & 0xff);
    }
    checksum[1] = (char) ((sum >> 8) & 0xff);
    checksum[2] = (char) (sum & 0xff);
    char check = (char) (sum & 0xff);
    //Sets escape character if the checksum contains START or END
    if (check == START || check == END) {
        checksum[0] = 1;
        checksum[2] = (char) (checksum[2] & 0x1);
    } else {
        checksum[0] = 0;
    }
    return checksum;
}

/**
* Sends error message.
* @param errCode    character representation of the failure-causing error
* @author Michael Ling
* @data 2/4/2015
*/
void BluetoothComm::send_error(char errCode)
{
    _rn42.putc(START);
    _rn42.putc(errCode);
    _rn42.putc(END);
    _rn42.putc(0);
    _rn42.putc(0);
    _rn42.putc(0);
}


/**
* Sends the specified char array through the _rn42 Bluetooth connection.
* @param cmd   The char array to be sent
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::send(char *cmd)
{
    for (int i = 0; i < 50; i++) {
        _rn42.putc(cmd[i]);
    }
}

/**
* Sets the parameter map, based on the input map NEWVALUES
* @param newValues    A map of strings to shorts to be copied to _paramMap
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::set_values(std::map<string, short> newValues)
{

    for (std::map<string, short>::iterator it = newValues.begin(); it != newValues.end(); ++it) {
        _paramMap[it->first] = it->second;
    }
}

void BluetoothComm::set_data(int index, short value) {
    //pc.printf("New value for %d\r\n", index);
    readData.write_to_index(index, (int)value);
    
}

/**
* Writes the editable params. stored in _paramMap to the SD card
* @author Michael Ling
* @date 2/4/2015
*/

void BluetoothComm::write_params_to_sd_card()
{
  
    int paramValues[_numVars];
    for (int i = 0; i < _numVars; i += 1) {
        paramValues[i] = (int) _paramMap[_indexMap[i]];
    }
    param.write(_numVars, paramValues);
    
}

/**
* Write the read-only values stored in _paramMap to the SD card
* @author Michael Ling
* @date 2/4/2015
*/

void BluetoothComm::write_data_to_sd_card()
{
    int dataValues[_numReadOnlyParams];
    for (int i = 0; i < _numReadOnlyParams; i += 1) {
        dataValues[i] = (int) _paramMap[_indexMap[i + _numVars]];
        //pc.printf("Index %d of dataValues set to %x\r\n", i, dataValues[i]);
    }
    readData.write(_numReadOnlyParams, dataValues);
}

/**
* Sends the paramList with START/END, parity bits, and a checksum
* @param paramList    List of parameters to be sent over Bluetooth, represented as a char array
* @param message      The message we want to send to Ctrlbed
* @param dataOut      Circular buffer that allow communication with Ctrlbed     
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::send_values(char* paramList, char *message, short *dataOut)
{
    char msg[2*_numVars+6];
    int len=2;
    //printf("Sending values \r\n");
    msg[0] = START;
    msg[1] = 0;
    for (int i=0; i < _numVars; i++) {
        
        if (paramList[i] != 0xff) {
            short s = (short)((i << 8) | paramList[i]);
            //printf("In send_values, calculating parity of %x\r\n", s);
            if (parity(s)) {
                //printf("%x requires TRUE parity bit\r\n", s);
                msg[len] = (char)(i | 0x40);
                len += 1;
            } else {
                //printf("%x requires FALSE parity bit\r\n", s);
                msg[len] = (char)i;
                len += 1;
            }
            msg[len] = paramList[i];
            len += 1;
        }
    }
    msg[len] = END;
    len += 1;
    char* checksum = get_checksum(msg, len);
    msg[len] = checksum[0];
    msg[len+1] = checksum[1];
    msg[len+2] = checksum[2];
    len += 3;
    //This loop sends a reply to the phone
    for (int j = 0; j < len; j++) {
        printf("Sending char %x \r\n", msg[j]);
        _rn42.putc(msg[j]);
        
    }
    //This loop passes the original write message to the ctrlbed
    //Each pair of characters is converted to a short, with orders reversed. 
    //This is so that casting the short* back to char* restores the original message.
    if (dataOut != NULL) {
        int ind = 2;
        int j;
        for (j = 0; j < len-1; j+=2) {
            dataOut[ind] = (message[j+1]<<8)|message[j];
            
            ind += 1;
        }
        //If the message has an odd number of characters
        if (j < len) {
            dataOut[ind] = message[j];
            
        } 
        for (int k=0; k<len;k++) {
            printf("Index %d: %x\r\n", k, dataOut[k]);
        }
        
    }
    memcpy(_lastCmd, msg, 50);
    free(checksum);
    return ;
}

/**
* Sends readOnly Parameters, with START/END and checksum
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::send_read_only_values()
{
    pc.printf("Send read only\r\n");
    read_data_from_sd();
    pc.printf("Updated readonly map\r\n");
    int msgLen = 2*_numReadOnlyParams+_escapesNeeded+7;
    char message[msgLen];
    message[0] = START;
    message[1] = 0;
    message[2] = READONLY_IND;
    int msgInd = 3;
    int escapes = 0;
    for (int i = 0; i < _numReadOnlyParams; i++) {
        if (i == _escapeNeeded[escapes]) {
            char conflict = (char)(_paramMap[_indexMap[i+_numVars]] & 0xff);
            escapes += 1;
            //message[msgInd+1] = (char) (_paramMap[_indexMap[i+_numVars]] >> 8);
            char conflict2 = (char)(_paramMap[_indexMap[i+_numVars]] >> 8);
            if (conflict2 == (char) 0xfe) {
                message[msgInd] = 2;
                message[msgInd+1] = 0xfc;
            } else if (conflict2 == (char) 0xff) {
                message[msgInd] = 2;
                message[msgInd+1] = 0xfd;
            } else {
                message[msgInd] = 0;
                message[msgInd+1] = conflict2;
            }
            if (conflict == (char) 0xfe) {
                message[msgInd] |= 1;
                message[msgInd+2] = 0xfc;
            } else if (conflict == (char) 0xff) {
                message[msgInd] |= 1;
                message[msgInd+2] = 0xfd;
            } else {
                //message[msgInd] = 0;
                message[msgInd+2] = conflict;
            }
            msgInd += 3;
        } else {
            message[msgInd] = (char) (_paramMap[_indexMap[i+_numVars]] >> 8);
            message[msgInd+1] = (char) (_paramMap[_indexMap[i+_numVars]] & 0xff);
            msgInd += 2;
        }
    }
    message[msgLen-4] = END;
    char* checksum = get_checksum(message, msgLen-3);
    message[msgLen-3] = checksum[0];
    message[msgLen-2] = checksum[1];
    message[msgLen-1] = checksum[2];
    for (int j=0; j < msgLen; j++) {
        _rn42.putc(message[j]);
    }
    memcpy(_lastCmd, message, 50);
    free(checksum);

}

/**
* Checks the message with length len. Checks for START and END in correct spots, parity, all data and variable byte in proper range, and correct checksum
* @param message    The received message to check
* @param len    Length of the message
* @author Michael Ling
* @date 2/4/2015
*/
bool BluetoothComm::msg_check(char* message, int len)
{
    if (message[0] != START) {
        send_error(START_ERR);
        return false;
    }
    if (message[len-4] != END) {
        send_error(END_ERR);
        return false;
    }
    bool write = message[2] & 0x80;
    for (int i=2; i < len-5; i+=2) {
        if (i == 2 && message[i] == READONLY_IND) {
            break;
        }
        if (i == (len-5) && message[i] == READONLY_IND) {
            break;
        }
        if (((message[i] & 0x80) !=0) && !write) {
            send_error((char)(((message[i] & 0x3f) << 3) | RW_ERR));
            return false;
        }
        if (((message[i] & 0x80) != 0x80) && write) {
            send_error((char)(((message[i] & 0x3f) << 3) | RW_ERR));
            return false;
        }
        short s;
        if (write) {
            s = (short) ((message[i] << 8) | message[i+1]);
        } else {
            s = (short) (message[i] << 8);
        }
        bool parity1 = (s & 0x4000) != 0;

        if (parity1 != parity(s & 0x4000)) {
            send_error((char) (((message[i] & 0xbf) << 3) | PARITY_ERR));
            return false;
        }

        char c = message[i] & 0x3f;
        char c2 = message[i+1];
        if ((int) c < 0 || (int) c > _numVars) {
            send_error((char) (((message[i] & 0xbf) << 3) | VAR_ERR));
            return false;
        }
        if ((int) c2 < 0 || (int) c2 > 100) {
            send_error((char) (((message[i] & 0xbf) << 3) | DATA_ERR));
            return false;
        }
    }
    char* checksum = get_checksum(message, len-3);
    if (checksum[0] != message[len-3]) {
        free(checksum);
        send_error(CHECKSUM_ERR);
        return false;
    }
    if (checksum[1] != message[len-2]) {
        free(checksum);
        send_error(CHECKSUM_ERR);
        return false;
    }
    if (checksum[2] != message[len-1]) {
        free(checksum);
        send_error(CHECKSUM_ERR);
        return false;
    }
    free(checksum);

    return true;
}

/**
* Checks a received readOnly message
* @param message    The readonly message received
* @param len    Length of the message
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::process_read_only(char* message, int len)
{
   
    if (!msg_check(message, len)) {
        printf("msg_check failed on readonly! \r\n");
        return;
    }
    _failures = 0;

    send_read_only_values();
}

/**
* Checks received READ message, and places requested data into an array
* @param message    The received READ request as a char array
* @param len    Length of the READ message
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::process_read(char* message, int len)
{
    //If the received message is an error message, resend the last command
    if (message[2] == END) {
        _failures += 1;
        if (_failures < 5) {
            send(_lastCmd);
        } else {
            _failures = 0;
        }
        return;
    }
    if (!msg_check(message, len)) {
        return;
    }
    //PASS MSG TO CTRLBED
    _failures = 0;
    char paramList[_numVars];
    memset(paramList, 0xff, _numVars);

    for (int i=2; i < len-5; i++) {
        char msg = message[i] & 0xbf;
        if ((msg & 0x80) != 0) {
            return;
        }

        int index = msg & 0xff;
        paramList[index] = _paramMap[_indexMap[index]];

    }
    if (message[len-5] == READONLY_IND) {
        send_read_only_values();
    }
    send_values(paramList, NULL, NULL);
}

/**
* Checks received WRITE message and writes to paramMap/SDCard
* @param message    The received WRITE message
* @param len    Length of the WRITE message
* @param dataOut Buffer that contains message for Ctrlbed
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::process_write(char* message, int len, short* dataOut)
{
    if (message[2] == END) {
        _failures += 1;
        if (_failures < 5) {
            send(_lastCmd);
        } else {
            _failures = 0;
        }
        return;
    }
    //dataOut = message;
    if (!msg_check(message, len)) {
        printf("msg_check failed on write! \r\n");
        return;
    }
   
    char paramList[_numVars];
    memset(paramList, 0xff, _numVars);
    //Strips received message of parity and write bits, sets paramMap to the new values, then passes the phone/ctrlbed messages to sendValues
    for (int i=2; i < len-5; i+=2) {
        char msg = message[i] & 0xbf;
        if ((msg & 0x80) != 0x80) {
            return;
        }
        int index = msg & 0x7f;
        _paramMap[_indexMap[index]] = message[i+1];
        paramList[index] = message[i+1];
        //printf("Wrote %x to index %d of localValues \r\n", localValues[index], index);

    }
    send_values(paramList, message, dataOut);
    
    write_params_to_sd_card();
    
}

/** 
* Checks if received message is a read, write or readonly
* @param message    The received message
* @param len    Length of the message
* @param dataOut Buffer for sending to ctrlbed
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::process (char* message, int len, short* dataOut)
{
    char c = message[2];
    for (int i =0; i < len; i++) {
        //printf("Message character: %x \r\n", message[i]);
    }
    if (c == READONLY_IND) {
        process_read_only(message, len);
        return;
    }
    if ((c & 0x80) == 0) {
        //printf("Is a read\r\n");
        process_read(message, len);
        return;
    } else {
        process_write(message, len, dataOut);
        return;
    }
}

//Warning: do not put print statements in the function attachment(); it will interfere with receiving messages
/**
* Scans for data received through Bluetooth, and passes it on if it detects a message-like chunk. Should be run via an interuupt.
* @param dataOut    Buffer for sending to Ctrlbed
* @author Michael Ling
* @date 2/4/2015
*/
void BluetoothComm::attachment(short* dataOut)
{
     
   // pc.printf("Entered attachment\r\n");
    if (_rn42.readable()) {
        _data=_rn42.getc();

        //    if (_data != NULL) {
        char b = _data & 0xff;
        //printf("Got char: %x \r\n", b);
        if (b != NULL or _inMsg) {
            //       printf("Got char non null: %x \r\n", b);
        }
        //This marks the START of a message
        if (_inMsg == false and b == START) {
            //boardLed4 = 1;
            _inMsg = true;
            _counter = 3;
            _curMsg[_len] = b;
            _len += 1;
        } else if (_inMsg == true and b == START) {
            _inMsg = false;
            _counter = 0;
            memset(_curMsg, 0, 50);
            _rn42.rxBufferFlush();
            process(_msg, _len, dataOut);
            _len = 0;
        } else if (_inMsg || _counter > 0 ) {
            _curMsg[_len] = b;
            _len += 1;
            if (!_inMsg) {
                _counter -= 1;
            }
            //Marks end of message, and starts processing
            if (_counter <= 0) {
                memset(_msg, 0, 50);
                memcpy(_msg, _curMsg, 50);
                memset(_curMsg, 0, 50);
                _rn42.rxBufferFlush();
                process(_msg, _len, dataOut);
                _len = 0;
            }
        }
        if (b == END) {
            _inMsg = false;

           
        }
        
          
    }
}