OnTick function

Each module must supply an OnTick function which is called every millisecond to generate motor commands and delivers sensor reports. All of necessary data types are defined in SB3/Embedded/Shared/datastruct.h which is generated by SB3/Embedded/Scripts/generate_datastruct.py which has a configuration file SB3/Embedded/Scripts/datastruct.txt

The OnTick function must have the prototype: void XXX_OnTick(CmdPacketStruct * Cmd, RptPacketStruct * Rpt); and the existing values in Cmd are undefined.


At the top level, the CmdPacketStruct has three members: header, a reserved single byte currently used for data logging debugging, servos an array of servo command packets, hash_code which is used for ensuring transmission integrity. The OnTick function does not need to worry about the hash_code value, it will be computed automatically by the communication code.


This member is an array of structs with type ServoCmdStruct and a length of NUM_SERVO_NODES both which are defined in datastruct.h. The numbering scheme for the servos can be found at SB3ServoConfigurations. Therefore, if you want to access the command structure for the front-right stroke motor, you would type Cmd->servos[5]

The servo struct has four members

Name size signed? Description
pos 8-bit no Desired position
force 8-bit no Desired force
volt 8-bit yes Feed-forward voltage command
gain.forceGain 4-bit no Force gain index
gain.posGain 3-bit no Position gain index
gain.motorOn 1-bit no 1 to drive motor, 0 to coast

Note the position and force commands are unsigned. This is because any semblance of zero-position is encoded on the main board side and not the servo board size. The main board must remember what the zero values are and then transmit them to the board (this is subject to change since we have have issues with the comm having 8-bits while the internal servo boards have 10-bit)

If we want to send the front right stroke motor to position 140, we would execute the following code:

Cmd->servos[5].pos = 140;

Also, note for the gains, the command sent is an index, not an actual. The boards have a look-up table that allows you to select from any gains (of course, we can reprogram the gains to be whatever we want). The actual gains used are available at SB3ServoConfigurations.


The report structure has two top level members: servos and accel


servos is another array of structs similar to their counterpart in Cmd but they are of type ServoRptStruct.

Name size signed? Description
pos 8-bit no Sensed position
force 8-bit no Sensed force
hash_code 8-bit no Boolean indicating proper reception

The hash_code indicates if the main board is correctly hearing the transmission from the servo. It should be noted that during the actual communication, this field contains the error detection hash, but once received by the main board, it is overwritten with a logical true/false indicating if an error was detected or not. If an error was detected, then the values in the pos and force are undefined and garbage.


Name size signed? Description
rawX 8-bit no 8-bit value from X-axis of accelerometer
rawY 8-bit no 8-bit value from Y-axis of accelerometer
rawZ 8-bit no 8-bit value from Z-axis of accelerometer
hash_code 8-bit no Boolean indicating proper reception

This shows the values pulled directly from the accelerometer channels without any scaling or offsets (there is a small amount of filtering). To access the x-axis, one would write: Rpt->accel.rawX

Alternative data structures

Warning: this code is technically non-portable, but it works correctly on these microcontrollers. Other processors might mangle the translation due to alignment issues.

Example Code

This example shows how to command a smooth line trajectory. The module has two functions, the OnTick function and the start function. The code implements a state machine with three states: WAITING, MOVING and HOLDING. Essentially, the robot waits with all motors off until start is called and then it smoothly moves its front right wing motor 20 servo ticks over 1/4 of a second. It should be noted that this code is incomplete because it does not check the hash_code from the report values to see if the incoming value is correct.

example code

// State machine states
#define WAITING   0
#define MOVING    1
#define HOLDING   2

//Distance and time duration for movement state
#define DIST      20
#define DUR_TIME  250

//State machine's current state
static unsigned char state = WAITING;

//Boolean that triggers a snapshot taken of the current position
static unsigned char take_snapshot;

//The starting position of the motor
static unsigned char start_pos;

//The current time during the movement state
static unsigned int stopwatch;

void start(void)
    //Signal start of move
    state = MOVING;

    //Reset clock
    stopwatch = 0;

    //We need the report structure to find out our current position
    //So we must trigger a snapshot
    take_snapshot = 1;

void OnTick(CmdPacketStruct * Cmd, RptPacketStruct * Rpt)
    static unsigned char i;

    //First, turn off all motors
    for (i = 0; i < NUM_SERVO_NODES; i++)
        Cmd->servos[i].gain.motorOn = 0;

    //See if we need to take a snapshot
    if (take_snapshot)
        //Clear flag
        take_snapshot = 0;

        //Take the snapshot of the position
        start_pos = RS_Rpt2Robot(Rpt)->RF.wing.pos

    //Implement state machine
    switch (state)
        case WAITING:
        case MOVING:
             //Calculate linear motion from start to end
             //(Be careful to multiply first, divide second
             // to avoid integer problems and make sure there
             // is enough room in the data type)
             RS_Cmd2Robot(Cmd)->RF.wing.pos = start_pos +
                 (DIST * stopwatch / DUR_TIME);
             //Increment clock

             //Determine if have reached the end
             if (stopwatch >= DUR_TIME)
                 state = HOLDING;

             //Turn joint on
             RS_Cmd2Robot(Cmd)->RF.wing.gain.motorOn = 1;
        case HOLDING:
             //Hold motor at end position
             RS_Cmd2Robot(Cmd)->RF.wing.pos = start_pos + DIST;
             RS_Cmd2Robot(Cmd)->RF.wing.gain.motorOn = 1;

-- SalomonTrujillo - 24 Jun 2010

This site is powered by the TWiki collaboration platformCopyright &© by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback