Example use of I2CTransaction class. In this example, the master mbed is talking to 3 legs of the SIPPC3B robot.

LegInterface.cpp

Committer:
symbiotic
Date:
2014-11-11
Revision:
7:c164b4869f74
Parent:
6:a52ded837184
Child:
8:402f4a307d08

File content as of revision 7:c164b4869f74:


/*
    Interface for communication to the set of legs for the OU SIPPC Robot (version 3B and beyond).

    Note: this interface is talking to very custom hardware.  As a result, it serves more as an
    example of how to use the I2CTransaction library.

    Author: Andrew H. Fagg  (May, 2014)

    More documentation to come...

*/


#include "LegInterface.h"

const int LegInterface::LegAddress[] = {I2C_ADDR_WEST, I2C_ADDR_EAST, I2C_ADDR_SOUTH};

/**
    Constructor: create all of the packets that can be sent + the associated transactions
*/

LegInterface::LegInterface(Serial *pc)
{
    // ESTOP transactions
    transactionEstop[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketEstop, 4);
    transactionEstop[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketEstop, 4);
    transactionEstop[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketEstop, 4);

    // Query state transactions
    legPacketQuery.type = nsPacketsLeg::QUERY_STATE;
    transactionQueryState[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketQuery, 4,
            (char*) &(legState[LEG_W]), sizeof(LegState_t));
    transactionQueryState[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketQuery, 4,
            (char*) &(legState[LEG_E]), sizeof(LegState_t));
    transactionQueryState[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketQuery, 4,
            (char*) &(legState[LEG_S]), sizeof(LegState_t));

    // Query leg parameter transactions.  Note: shared outgoing packet structure
    legPacketQueryLiftParams.type = nsPacketsLeg::GET_LIFT_PARAMS;
    transactionQueryLiftParams[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketQueryLiftParams, 4,
            (char *) &legLiftParams, sizeof(LegControlParams_t));
    transactionQueryLiftParams[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketQueryLiftParams, 4,
            (char *) &legLiftParams, sizeof(LegControlParams_t));
    transactionQueryLiftParams[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketQueryLiftParams, 4,
            (char *) &legLiftParams, sizeof(LegControlParams_t));

    legPacketQueryWheelParams.type = nsPacketsLeg::GET_WHEEL_PARAMS;
    transactionQueryWheelParams[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketQueryWheelParams, 4,
            (char *) &legWheelParams, sizeof(LegControlParams_t));
    transactionQueryWheelParams[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketQueryWheelParams, 4,
            (char *) &legWheelParams, sizeof(LegControlParams_t));
    transactionQueryWheelParams[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketQueryWheelParams, 4,
            (char *) &legWheelParams, sizeof(LegControlParams_t));


    // Set leg parameter transactions
    //  Note: shared packet structure across transactions (since we are setting one at a time)
    legPacketSetLiftParams.type = nsPacketsLeg::SET_LIFT_PARAMS;
    transactionSetLiftParams[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketSetLiftParams, 4+sizeof(LegControlParams_t));
    transactionSetLiftParams[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketSetLiftParams, 4+sizeof(LegControlParams_t));
    transactionSetLiftParams[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketSetLiftParams, 4+sizeof(LegControlParams_t));

    legPacketSetWheelParams.type = nsPacketsLeg::SET_WHEEL_PARAMS;
    transactionSetWheelParams[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &legPacketSetWheelParams, 4+sizeof(LegControlParams_t));
    transactionSetWheelParams[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &legPacketSetWheelParams, 4+sizeof(LegControlParams_t));
    transactionSetWheelParams[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &legPacketSetWheelParams, 4+sizeof(LegControlParams_t));

    // Goal set
    legPacketSetGoal[LEG_W].type = nsPacketsLeg::SET_GOAL;
    transactionSetGoal[LEG_W] = new I2CTransaction(I2C_ADDR_WEST, (char*) &(legPacketSetGoal[LEG_W]), 4+sizeof(Goal_t));

    legPacketSetGoal[LEG_E].type = nsPacketsLeg::SET_GOAL;
    transactionSetGoal[LEG_E] = new I2CTransaction(I2C_ADDR_EAST, (char*) &(legPacketSetGoal[LEG_E]), 4+sizeof(Goal_t));

    legPacketSetGoal[LEG_S].type = nsPacketsLeg::SET_GOAL;
    transactionSetGoal[LEG_S] = new I2CTransaction(I2C_ADDR_SOUTH, (char*) &(legPacketSetGoal[LEG_S]), 4+sizeof(Goal_t));

    // Other initialziations
    this->pc = pc;
    Leg leg = LEG_S;
    pc->printf("codes: %d %d\n\r", transactionSetLiftParams[leg]->getStatus(0), transactionSetLiftParams[leg]->getStatus(1));

}

/**
    Attempt to send an estop to all legs.

    @param estop true = stop all motion; false = enable motion

    @return true = transaction successfully scheduled, false = prior attempt at sending had not completed.
*/

bool LegInterface::sendEstop(bool estop)
{
    // Has the prior attempt at sending the estop completed?
    if(!transactionEstop[LEG_W]->completed() ||
            !transactionEstop[LEG_E]->completed() ||
            !transactionEstop[LEG_S]->completed()) {
        // No: do not attempt
        return false;
    }

    // Yes - configure the packet (all packets are the same in this case)
    legPacketEstop.type = estop?nsPacketsLeg::ESTOP_ON:nsPacketsLeg::ESTOP_OFF;

    // Schedule the transactions
    transactionEstop[LEG_W]->initiateTransaction();
    transactionEstop[LEG_E]->initiateTransaction();
    transactionEstop[LEG_S]->initiateTransaction();

    // Complete
    return true;
}

bool LegInterface::queryStateInitiate()
{
    /*
    transactionQueryState[LEG_W]->checkTransaction();
    transactionQueryState[LEG_E]->checkTransaction();
    transactionQueryState[LEG_S]->checkTransaction();
    */

    // Has the prior attempt at sending the estop completed?
    if(!queryStateCompleted()) {
        // No: do not attempt
        return false;
    }

    //this->pc->printf("########################## FOO @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\r");
    // Magic numbers
    for(int i= 0; i < NUM_LEGS; ++i) {
        this->legState[i].magic = 0;
    }

    // Query last had completed
    // Schedule the transactions
    transactionQueryState[LEG_W]->initiateTransaction();
    transactionQueryState[LEG_E]->initiateTransaction();
    transactionQueryState[LEG_S]->initiateTransaction();

    return true;
}

bool LegInterface::queryStateWaitForCompletion(int timeout)
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(!transactionQueryState[i]->waitForCompletion(timeout)) {
            return false;
        }
    }
    return true;
}

/**
    Check if the query of leg state has been completed (successful or not)

    @return true if completed
*/

bool LegInterface::queryStateCompleted()
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(!transactionQueryState[i]->completed()) {
            return false;
        }
    }
    return true;
}

/**
    Check if the query of leg state has been successfully completed

    @return true if successfully completed
*/

bool LegInterface::queryStateSuccess()
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(!transactionQueryState[i]->success()) {
            return false;
        }
    }
    return true;
}

/**
    Print out the internal magic numbers
    */

void LegInterface::queryStateReportMagic()
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        pc->printf("%x ", legState[i].magic);
    }
    pc->printf("\n\r");
}
/**
    Check if the query of estop has been completed (successful or not)

    @return true if completed
*/

bool LegInterface::queryEstopCompleted()
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(!transactionEstop[i]->completed()) {
            return false;
        }
    }
    return true;
}

/**
    Check if the query of estop has been successfully completed

    @return true if successfully completed
*/

bool LegInterface::queryEstopSuccess()
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(!transactionEstop[i]->success()) {
            return false;
        }
    }
    return true;
}

void LegInterface::queryStateTest(Serial *pc)
{
    for(int i = 0; i < NUM_LEGS; ++i) {
        pc->printf("Status %d: %d %d\n\r", i, transactionQueryState[i]->getStatus(0), transactionQueryState[i]->getStatus(1));
    }
}


bool LegInterface::queryStateCopy(LegState_t legState[NUM_LEGS])
{
    // Has the prior attempt at querying the state completed?
    if(!queryStateCompleted()) {
        // No: do not attempt
        return false;
    }

    // Transaction complete: copy the state of the legs only if the magic number is correct
    if(this->legState[LEG_W].magic == I2C_MAGIC_NUMBER)
        legState[LEG_W] = this->legState[LEG_W];
    if(this->legState[LEG_E].magic == I2C_MAGIC_NUMBER)
        legState[LEG_E] = this->legState[LEG_E];
    if(this->legState[LEG_S].magic == I2C_MAGIC_NUMBER)
        legState[LEG_S] = this->legState[LEG_S];

    return true;
}

/**
    Copy the state of the legs from the LegInterface structure to usable structures.  In order for the data to be copied, the
    transaction must be successful, and the leg packet must have a valid magic number (the latter is evalulated on a leg-by-leg
    basis).

    The input parameters are destructively modified only if the data are valid.  The exception is the "magic" parameter.  This is
    only changed if the set of transactions was successful.

    @param liftPosition  Lift position of each of the legs (m above the ground)
    @param liftVelocity  Velocity of the lifts (m/s)
    @param wheelPosition  Position of each of the wheels (m)
    @param wheelVelocity Velocity of the wheels (m/s)
    @param cliff Cliff sensors
    @param limit Limit sensors (lifts)
    @param estop Estop state of each of the legs
    @param magic Magic number from each leg

    @return true if Some data were updated; false if no change

*/

bool LegInterface::queryStateCopy(float liftPosition[NUM_LEGS], float liftVelocity[NUM_LEGS], float wheelPosition[NUM_LEGS], float wheelVelocity[NUM_LEGS],
                                  bool cliff[NUM_LEGS], bool limit[NUM_LEGS], bool estop[NUM_LEGS], uint8_t magic[NUM_LEGS])
{
    // Has the prior attempt at getting state completed?
    if(!queryStateSuccess()) {
        // No: do not attempt the copy
        return false;
    }

    // Transaction complete: copy the state of the legs
    for(int i = 0; i < NUM_LEGS; ++i) {
        if(this->legState[i].magic == I2C_MAGIC_NUMBER) {
            // Only copy if the magic number matches
            liftPosition[i] = this->legState[i].lift.pos / LIFT_TICKS_PER_METER - LIFT_OFFSET;
            liftVelocity[i] = this->legState[i].lift.vel / LIFT_TICKS_PER_METER;
            wheelPosition[i] = this->legState[i].wheel.pos / TICKS_PER_METER;
            wheelVelocity[i] = this->legState[i].wheel.vel / TICKS_PER_METER;
            cliff[i] = this->legState[i].cliff;
            limit[i] = this->legState[i].limit;
            estop[i] = this->legState[i].estop;

        }
        // Also copy the magic number
        magic[i] = this->legState[i].magic;
        //pc->printf("%x ", magic[i]);
    }
    //pc->printf("\n\r");

    return true;
}

void LegInterface::displayLegState(LegState_t *legState)
{
    pc->printf("Cliff = %d, Limit = %d\n\r", legState->cliff, legState->limit);
    pc->printf("Wheel: pos=%d, vel=%d\n\r", legState->wheel.pos, legState->wheel.vel);
    pc->printf("Lift: pos=%d, vel=%d\n\r", legState->lift.pos, legState->lift.vel);
}


bool LegInterface::queryLegParams(Leg leg, LegControlParams_t *liftParams, LegControlParams_t *wheelParams)
{
    /*
    if(pc != NULL) {
        pc->printf("completed: %d %d %d %d\n\r", transactionQueryLiftParams[leg]->completed(), transactionQueryWheelParams[leg]->completed(),
                   transactionSetLiftParams[leg]->completed(), transactionSetWheelParams[leg]->completed());
        pc->printf("codes: %d %d\n\r", transactionSetLiftParams[leg]->getStatus(0), transactionSetLiftParams[leg]->getStatus(1));
    }
    */

    // Has the prior attempt at querying/setting leg parameters completed?
    if(!transactionQueryLiftParams[leg]->completed() || !transactionQueryWheelParams[leg]->completed()
            || !transactionSetLiftParams[leg]->completed() || !transactionSetWheelParams[leg]->completed()) {
        // No:  do not attempt
        return false;
    }

    // Yes: initiate the transactions
    transactionQueryLiftParams[leg]->initiateTransaction();
    transactionQueryWheelParams[leg]->initiateTransaction();
    //pc->printf("codes: %d %d\n\r", transactionQueryWheelParams[leg]->getStatus(0), transactionQueryWheelParams[leg]->getStatus(1));

    if(transactionQueryLiftParams[leg]->waitForCompletion() && transactionQueryWheelParams[leg]->waitForCompletion()) {
        // Completed
        *liftParams = this->legLiftParams;
        *wheelParams = this->legWheelParams;
        return true;
    }
    // A timeout happened (no copy)
    return false;
}

void LegInterface::displayParams(LegControlParams_t *params)
{
    pc->printf("Kp = %d\t Kv = %d\t Ki=%d\n\r", params->Kp, params->Kv, params->Ki);
    pc->printf("deadband = %d\n\r", params->deadband);
    pc->printf("Control signal range = [%d, %d]\n\r", params->min_val, params->max_val);
    pc->printf("Max error = %d, max error accum = %d\n\r", params->max_error, params->max_error_accum);
    pc->printf("Max acceleration = %d\n\r", params->max_accel);
}

bool LegInterface::setLegParams(Leg leg, LegControlParams_t *liftParams, LegControlParams_t *wheelParams)
{


    // Has the prior attempt at querying/setting leg parameters completed?
    if(!transactionQueryLiftParams[leg]->completed() || !transactionQueryWheelParams[leg]->completed()
            || !transactionSetLiftParams[leg]->completed() || !transactionSetWheelParams[leg]->completed()) {
        // No:  do not attempt
        return false;
    }

    // Copy provided parameters into structure to send
    this->legLiftParams = *liftParams;
    this->legWheelParams = *wheelParams;

    // Yes: initiate the transactions
    transactionSetLiftParams[leg]->initiateTransaction();
    transactionSetWheelParams[leg]->initiateTransaction();

    if(transactionQueryLiftParams[leg]->waitForCompletion() && transactionQueryWheelParams[leg]->waitForCompletion()) {
        // Completed
        return true;
    }
    // A timeout happened (no copy)
    return false;
}

bool LegInterface::setLegGoal(int32_t liftPos[NUM_LEGS], int32_t wheelVel[NUM_LEGS])
{
    // Has the previous attempt completed?
    if(!transactionSetGoal[LEG_W]->completed() ||
            !transactionSetGoal[LEG_E]->completed() ||
            !transactionSetGoal[LEG_S]->completed()) {
        // No: refuse to send
        return false;
    }

    // Copy goals into structures to send and initiate
    for(int i = 0; i < LegInterface::NUM_LEGS; ++i) {
        legPacketSetGoal[i].contents.as_goal.liftPos = liftPos[i];
        legPacketSetGoal[i].contents.as_goal.wheelVel = wheelVel[i];
        transactionSetGoal[i]->initiateTransaction();
    }

    // Indicate success
    return true;
}



bool LegInterface::setLegGoal(float liftPos[NUM_LEGS], float wheelVel[NUM_LEGS])
{
    int32_t lift[NUM_LEGS];
    int32_t wheel[NUM_LEGS];

    for(int i = 0; i < NUM_LEGS; ++i) {
        wheel[i] = (int32_t) (wheelVel[i] * TICKS_PER_METER);
        lift[i] = (int32_t) ((liftPos[i] + LIFT_OFFSET) * LIFT_TICKS_PER_METER);
    }

    return setLegGoal(lift, wheel);
}

/**
    Check the completion status of the Set Leg Goal transaction.

    @return true if the last leg goal set was completed.
*/

bool LegInterface::getLegGoalCompleted()
{
    return(transactionSetGoal[LEG_W]->completed() &&
           transactionSetGoal[LEG_E]->completed() &&
           transactionSetGoal[LEG_S]->completed());
}


/**
    Check the completion status of the Set Leg Goal transaction.

    @return true if the last leg goal set was completed successfully.
*/

bool LegInterface::getLegGoalSuccess()
{
    return(transactionSetGoal[LEG_W]->success() &&
           transactionSetGoal[LEG_E]->success() &&
           transactionSetGoal[LEG_S]->success());
}

void LegInterface::displayStatus()
{
    pc->printf("Transaction status:\n\r");
    transactionEstop[LEG_W]->displayStatus(pc, "Estop W:\t");
    transactionEstop[LEG_E]->displayStatus(pc, "Estop E:\t");
    transactionEstop[LEG_S]->displayStatus(pc, "Estop S:\t");
    pc->printf("\n\r");
    transactionQueryState[LEG_W]->displayStatus(pc, "Query State W:\t");
    transactionQueryState[LEG_E]->displayStatus(pc, "Query State E:\t");
    transactionQueryState[LEG_S]->displayStatus(pc, "Query State S:\t");
    pc->printf("\n\r");
    transactionQueryLiftParams[LEG_W]->displayStatus(pc, "Query Lift Params W");
    transactionQueryLiftParams[LEG_E]->displayStatus(pc, "Query Lift Params E");
    transactionQueryLiftParams[LEG_S]->displayStatus(pc, "Query Lift Params S");
    pc->printf("\n\r");
    transactionQueryWheelParams[LEG_W]->displayStatus(pc, "Query Wheel Params W");
    transactionQueryWheelParams[LEG_E]->displayStatus(pc, "Query Wheel Params E");
    transactionQueryWheelParams[LEG_S]->displayStatus(pc, "Query Wheel Params S");
    pc->printf("\n\r");
    transactionSetLiftParams[LEG_W]->displayStatus(pc, "Set Lift Params W");
    transactionSetLiftParams[LEG_E]->displayStatus(pc, "Set Lift Params E");
    transactionSetLiftParams[LEG_S]->displayStatus(pc, "Set Lift Params S");
    pc->printf("\n\r");
    transactionSetWheelParams[LEG_W]->displayStatus(pc, "Set Wheel Params W");
    transactionSetWheelParams[LEG_E]->displayStatus(pc, "Set Wheel Params E");
    transactionSetWheelParams[LEG_S]->displayStatus(pc, "Set Wheel Params S");
    pc->printf("\n\r");
    transactionSetGoal[LEG_W]->displayStatus(pc, "Set Goal W:\t");
    transactionSetGoal[LEG_E]->displayStatus(pc, "Set Goal E:\t");
    transactionSetGoal[LEG_S]->displayStatus(pc, "Set Goal S:\t");
    pc->printf("\n\r");

}

void LegInterface::reset()
{
    I2CTransaction::reset();
}

void LegInterface::resetBus()
{
    I2CTransaction::resetBus();
}

void LegInterface::cycleBus()
{
    I2CTransaction::cycleBus();
}

void LegInterface::clearQueryTransactions()
{
    transactionQueryState[LEG_W]->clearStatus();
    transactionQueryState[LEG_E]->clearStatus();
    transactionQueryState[LEG_S]->clearStatus();
}

void LegInterface::clearSetGoalTransactions()
{
    transactionSetGoal[LEG_W]->clearStatus();
    transactionSetGoal[LEG_E]->clearStatus();
    transactionSetGoal[LEG_S]->clearStatus();
}


/**
    Initialize the I2C pins and other hardware.  If it has already been initialized, then
    we will re-initialize.

    @param sda Name of the pin that is used for data
    @param scl Name of the pin that is used for the clock
*/

void LegInterface::initI2C(PinName sda, PinName scl)
{
    I2CTransaction::initI2C(sda, scl);
}