Default Message Functions

Serial Message Definition

A streaming, serial message can be generated by the OpenIMU platform. In this example, a message matching the requirements, defined earlier, is created. It consists of:

  1. An integer counter, representing time in \([ms]\)
  2. A floating-point representation of time, in \([s]\)
  3. Accelerometer readings, in \([g]\)
  4. Rate-Sensor readings, in [°/s]
  5. Magnetometer readings, in \([G]\)
  6. Board temperature, in [°C]

To generate this output, a serial-message was created in UserMessaging.c and UserMessaging.h. In the firmware, the message is given the name, USR_OUT_SCALED1, along with the packet code “s1” (with lower-case S representing scaled).

To form the message, the first step is to define the message components and determine the total number of bytes the message will occupy. The components of the message, variable type, and number of bytes are listed in the following table:

User-Defined Serial Message Components
Message Component Description
Number of
Type Bytes
Integer counter uint32_t 4 1 4
Time variable double 8 1 8
Readings (3 axis)
float 4 3 12
Readings (3 axis)
float 4 3 12
Readings (3 axis)
float 4 3 12
Readings (3 axis)
float 4 1 4

This shows that the payload section of the output message (not including preamble, message type, or CRC) consists of 52 bytes.

Adding this message to the firmware requires modifications to two files: UserMessaging.c and UserMessaging.h.

UserMessaging.h Modifications

The packet code and number of bytes must be added to UserMessaging.h. This requires adding the output packet code to the packet-type enum variable:

// User output packet codes, change at will
typedef enum {
    USR_OUT_NONE = 0,  // 0
    USR_OUT_TEST,      // 1
    USR_OUT_DATA1,     // 2
    USR_OUT_DATA2,     // 3
// add new output packet type here, before USR_OUT_MAX
    USR_OUT_SCALED1,   // 4
} UserOutPacketType;

and creating a #define identifier to hold the payload length


These can be found in the IMU example code.

UserMessaging.c Modifications

With the above additions to UserMessaging.h made, the output message can be added to UserMessaging.c, completing the process. To accomplish this, add a new case to the switch-statement found in HandleUserOutputPacket() using the output name added to UserMessaging.h:

    // The payload length (NumOfBytes) is based on the following:
    // 1 uint32_t (4 bytes) =  4 bytes
    // 1 double (8 bytes)   =  8 bytes
    // 3 floats (4 bytes)   = 12 bytes
    // 3 floats (4 bytes)   = 12 bytes
    // 3 floats (4 bytes)   = 12 bytes
    // 1 floats (4 bytes)   =  4 bytes
    // =================================
    //           NumOfBytes = 52 bytes
    *payloadLen = USR_OUT_LEV1_PAYLOAD_LEN;

    // Output time as represented by gIMU.timerCntr (uint32_t
    // incremented at each call of the algorithm)
    uint32_t *algoData_1 = (uint32_t*)(payload);
    *algoData_1++ = gIMU.timerCntr;

    // Output a double representation of time generated from
    // gLeveler.itow
    double *algoData_2 = (double*)(algoData_1);
    *algoData_2++ = 1.0e-3 * (double)(gIMU.timerCntr);

    // Set the pointer of the sensor array to the payload
    float *algoData_3 = (float*)(algoData_2);
    *algoData_3++ = (float)gIMU.accel_g[X_AXIS];
    *algoData_3++ = (float)gIMU.accel_g[Y_AXIS];
    *algoData_3++ = (float)gIMU.accel_g[Z_AXIS];

    *algoData_3++ = (float)gIMU.rate_degPerSec[X_AXIS];
    *algoData_3++ = (float)gIMU.rate_degPerSec[Y_AXIS];
    *algoData_3++ = (float)gIMU.rate_degPerSec[Z_AXIS];

    *algoData_3++ = (float)gIMU.mag_G[X_AXIS];
    *algoData_3++ = (float)gIMU.mag_G[Y_AXIS];
    *algoData_3++ = (float)gIMU.mag_G[Z_AXIS];

    *algoData_3++ = (float)gIMU.temp_C;

Data is appended to the payload array using pointers. This enables variables of different datatypes to fit into the payload array (defined as an array of 8-bit unsigned integers); this approach is highlighted in the previous code snippet and is done by generating a pointer of the desired type to a typecast version of the payload address. In the example above, 32-bit unsigned integer data is appended to the payload, followed by double and floating-point variables.

Finally, the packet type must be added to the switch-statement in setUserPacketType() to allow the firmware to select the packet:

case USR_OUT_SCALED1:          // packet with arbitrary data
    _outputPacketType = type;
    _userPayloadLen   = USR_OUT_SCALED1_PAYLOAD_LEN;

and the packet-code must be added to the list of user output packets, userOutputPackets.

// packet codes here should be unique -
// should not overlap codes for input packets and system packets
// First byte of Packet code should have value >= 0x61
usr_packet_t userOutputPackets[] = {
//   Packet Type                Packet Code
    {USR_OUT_NONE,              {0x00, 0x00}},
    {USR_OUT_TEST,              "zT"},
    {USR_OUT_DATA1,             "z1"},
    {USR_OUT_DATA2,             "z2"},
// place new type and code here
    {USR_OUT_SCALED1,           "s1"},
    {USR_OUT_MAX,               {0xff, 0xff}},   //  ""

These changes are found in UserMessaging.c.

Default Configuration Settings

To make the “s1” serial message (created previously) the default output, make changes to the default user-configuration structure found in UserConfiguration.c:

// Default user configuration structure
// Saved into EEPROM of first startup after reloading the code
// or as a result of processing "rD" command
// Do Not remove - just add extra parameters if needed
// Change default settings  if desired
const UserConfigurationStruct gDefaultUserConfig = {
    .dataCRC             =  0,
    .dataSize            =  sizeof(UserConfigurationStruct),
    .userUartBaudRate    =  115200,
    .userPacketType      =  "s1",
    .userPacketRate      =  10,
    .lpfAccelFilterFreq  =  25,
    .lpfRateFilterFreq   =  25,
    .orientation         =  "+X+Y+Z"
    // add default parameter values here, if desired
} ;


userPacketType was set to “s1” to cause the new packet to be broadcast by default. Additionally, the desired message baud rate and message rate are set to 115.2 kbps and 10 [Hz], respectively. Finally, the accelerometer and rate-sensor filters are set to 25 Hz.

Testing using Serial Terminal Emulator

At this point, the IMU application has been implemented and the output messaging created. Build and upload the firmware to the OpenIMU. A serial terminal (such as TeraTerm) can be used to verify if a message is being generated by the device. In the following figure, output messaging creation can be verified by searching for the string “UUs1”. If present, the message is being generated; whether the message is populated correctly requires the use of additional tools.


Test of Serial Message Output


In the above figure the message preamble sometimes displays as “UU_1”. This is solely a TeraTerm glitch. Other serial terminal programs (such as CoolTerm) do not show such behavior.