3D motion sensor MM7150

MM7150 is 9-axis sensor fusion motion module from microchip (http://www.microchip.com/wwwproducts/en/MM7150 ).
It’s a complete self-contained solution comprising a 3-axis accelerometer, a gyroscope, a magnetometer, and a SSC7150 motion coprocessor.

3D Motion click is a module which carries MM7150 and has mikroBUS interface. With motion sensor, we can do as much as we can imagine.

This article shows library of MM7150 and example code to read data from this module.
The MM7150 responds to the standard HID protocol over I2C. It’s really complicated for beginner. So, I make it simple. This library works on both PHPoC blue and black.



Source Code Architecture

Click image for larger version  Name:	3D_motion_Source Code Architecture.png Views:	1 Size:	3.5 KB ID:	561



I2C driver library

It is official library from PHPoC and you can get it here https://github.com/phpoc/psp file sd_340.php



HID over I2C library

I make a simple version of it. It may be updated in the future.
vn_hid_i2c.php
PHP Code:
<?php

//HID descriptor fields offset
define("HID_DESC_LEN_OFFSET",        0x00);
define("HID_VERSION_OFFSET",         0x02);
define("HID_REPORT_DESC_LEN_OFFSET"0x04);
define("HID_REPORT_DESC_REG_OFFSET"0x06);
define("HID_INPUT_REG_OFFSET",       0x08);
define("HID_MAX_INPUT_LEN_OFFSET",   0x0A);
define("HID_OUPUT_REG_OFFSET",       0x0C);
define("HID_MAX_OUTPUT_LEN_OFFSET",  0x0E);
define("HID_CMD_REG_OFFSET",         0x10);
define("HID_DATA_REG_OFFSET",        0x12);
define("HID_VENDOR_ID_OFFSET",       0x14);
define("HID_PRODUCT_ID_OFFSET",      0x16);
define("HID_VERSION_ID_OFFSET",      0x18);

define("CMD_GET_HID_DESC",            1);
define("CMD_GET_RPT_DESC",            2);
define("CMD_RESET_DEV",               1);
define("CMD_POWER_ON",                2);
define("CMD_SLEEP",                   3);
define("CMD_HID_GET_RPT_INPT",        4);
define("CMD_HID_GET_RPT_FEAT",        5);
define("CMD_HID_SET_RPT_OUTP",        6);
define("CMD_HID_SET_RPT_FEAT",        7);

define("HID_COLLECTION",      0xA1);
define("HID_PHYSICAL",        0x00);
define("HID_REPORT_ID",       0x85);
define("HID_END_COLLECTION",  0xC0);


// REPORT PARSE MACROS
define("HID_USG_SENS_PROPERTY_REPORTING_STATE_ALL_EVENTS_ENUM"0x02);
define("HID_USG_SENS_TYPE",                   0x09);
define("HID_USG_SENS_PROPERTY",               0x0A);
define("HID_USG_SENS_PROPERTY_CONN_TYPE",     0x09);                //HID byte sequence : 0x0A,0x09,0x03
define("HID_USG_SENS_PROPERTY_RPT_STATE",     0x16);                //HID byte sequence : 0x0A,0x16,0x03
define("HID_USG_SENS_PROPERTY_PWR_STATE",     0x19);                //HID byte sequence : 0x0A,0x19,0x03
define("HID_USG_SENS_",                       0x0A);
define("HID_USG_SENS_STATE_1",                0x01);                //HID byte sequence : 0x0A,0x01,0x02
define("HID_USG_SENS_STATE_2",                0x02);                //HID byte sequence : 0x0A,0x01,0x02
define("HID_USG_SENS_EVENT_1",                0x02);                //HID byte sequence : 0x0A,0x02,0x02
define("HID_USG_SENS_EVENT_2",                0x02);                //HID byte sequence : 0x0A,0x02,0x02
define("HID_USG_SENS_PROPERTY_RPT_INT",       0x0E);                //HID byte sequence : 0x0A,0x0E,0x03

define("HID_USG_SENS_DATA_ACCU",              0x44);                //the sensor data accuracy value is the //define("HID_USG_SENS_DATA(a,b) value (where (a|b)).
                                                                    // For example, for the accelerometer:     HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_ACCURACY)
                                                                    //  HID_USG_SENS_DATA_MOTION_ACCELERATION | HID_USG_SENS_DATA_MOD_ACCURACY = 0x04 | 0x40 = 0x44 this is accuracy value for Non-RAW sensors
define("HID_USG_SENS_DATA_RAW_ACCU",          0x45);                //the sensor data accuracy value is the //define("HID_USG_SENS_DATA(a,b) value (where (a|b)).
                                                                    // For example, for RAW sensor:     HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_ACCURACY),
                                                                    //  HID_USG_SENS_DATA_CUSTOM_VALUE | HID_USG_SENS_DATA_MOD_ACCURACY = 0x05 | 0x40 = 0x45 this is accuracy value for RAW sensors

define("HID_USG_SENS_DATA_RES",               0x54);                //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_RESOLUTION' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
                                                                    //  HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_RESOLUTION)
                                                                    //  where //define("HID_USG_SENS_DATA_MOD_RESOLUTION 0x50
define("HID_USG_SENS_DATA_RAW_RES",           0x55);                //  HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_RESOLUTION)

define("HID_USG_SENS_DATA_MOD_SENS",          0x14);                //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
                                                                    // HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS)
                                                                    // where //define("HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS 0x10
define("HID_USG_SENS_DATA_RAW_MOD_SENS",      0x15);                // HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS)

define("HID_USG_SENS_DATA_MOD_MAX",           0x24);                //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_MAX' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
                                                                    //HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MAX)
                                                                    // where //define("HID_USG_SENS_DATA_MOD_MAX 0x20
define("HID_USG_SENS_DATA_RAW_MOD_MAX",       0x25);                //HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_MAX)

define("HID_USG_SENS_DATA_MOD_MIN",           0x34);                //see above comment for 'HID_USG_SENS_DATA_ACCU'substituting 'HID_USG_SENS_DATA_MOD_MIN' for 'HID_USG_SENS_DATA_MOD_ACCURACY'
                                                                    //HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MIN)
                                                                    // where //define("HID_USG_SENS_DATA_MOD_MIN 0x30
define("HID_USG_SENS_DATA_RAW_MOD_MIN",       0x35);                //HID_USG_SENS_DATA(HID_USG_SENS_DATA_CUSTOM_VALUE,HID_USG_SENS_DATA_MOD_MIN)

define("HID_UNIT_EXP",                            0x55);                //  HID_UNIT_EXPONENT = 0x55


define("MAX_SENSOR_NUM",        12); // user can increase if need more

define("SENSOR_INFO_LEN",         7); // 7 byte for each sensor:
define("SENSOR_ID_OFFSET",        0);
define("SENSOR_TYPE_OFFSET",      1);
define("SENSOR_DATART_OFFSET",    2);
define("SENSOR_SENSOR_OFFSET",    3);
define("SENSOR_SENSOR_EXP_OFFSET",4);
define("SENSOR_DATA_OFFSET",      5);
define("SENSOR_DATA_EXP_OFFSET",  6);

define("ACCEL_SENSOR_TYPE",       0x73);
define("GYRO_SENSOR_TYPE",        0x76);
define("CMP_SENSOR_TYPE",         0x83);
define("ORI_SENSOR_TYPE",         0x8A);
define("INCL_SENSOR_TYPE",        0x86);
define("RAW_SENSOR_TYPE",         0xE1);
// Add more sensor type here

define("FLASH_UPDATE_RPT_ID",         0xE);

// SET REPORT command bytes
define("SET_RPT_FEAT",            0x30);
define("SET_OPCODE",              0x03);


// Report Feature Defines
define("RPT_SIZE_LSB",            0);
define("RPT_REPORT_STATE",        4);
define("RPT_PWR_STATE",           5);
define("RPT_SENS_STATE",          6);
define("RPT_REPORT_INTVAL_LSB",   7);
define("RPT_REPORT_INTVAL_MSB",   8);
define("RPT_CHG_SENS_LSB",        13);
define("RPT_CHG_SENS_MSB",        14);

define("GET_RPT_CMD_MSB",        0x02);// GET REPORT commmand bytes
define("GET_RRT_INPT",           0x10);
define("GET_RPT_FEAT",           0x30);


//HID descriptor table
$hid_descriptor_table str_repeat("\x00"30);
$hid_device_sensor_num 0;
$hid_report_desc_table str_repeat("\x00"MAX_SENSOR_NUM*SENSOR_INFO_LEN);

//Application sensors parameter in use
$app_sensor_info = array("""""""""""""""");
$app_data = array("""""""""""""""");

/*
This function is to get value of timer.
*/
function vn_hid_i2c_get_tick($div "ms")
{
    while((
$pid pid_open("/mmap/st9"O_NODIE)) == -EBUSY)
        
usleep(500);

    if(!
pid_ioctl($pid"get state"))
    {
        
pid_ioctl($pid"set div us");
        
pid_ioctl($pid"start");
    }

    
$tick pid_ioctl($pid"get count");
    
pid_close($pid);

    if(
$div == "ms")
    {
        return (int) 
$tick/1000;
    }

    return 
$tick;
}

function 
vn_hid_i2c_read($i2c_id, &$rbuf)
{
    if(
$i2c_id != 0)
        exit(
"i2c_read: i2c_id out of range $i2c_id\r\n");

    
$pid pid_open_nodie("/mmap/i2c$i2c_id""i2c_read");

    
pid_ioctl($pid"req read 2");
    while(
pid_ioctl($pid"get state"))
        ;
    
$len_buf "";
    
pid_read($pid$len_buf);
    
$len bin2int($len_buf02) - 2;

    
pid_ioctl($pid"req read $len");
    while(
pid_ioctl($pid"get state"))
        ;

    
$len pid_read($pid$rbuf);

    
$rbuf $len_buf.$rbuf;

    
pid_close($pid);

    return 
strlen($rbuf);
}

function 
vn_hid_i2c_write_read($i2c_id$wbuf, &$rbuf)
{    
    
i2c_write_read($i2c_id$wbuf$rbuf2);
    
$rlen bin2int($rbuf02);
    
i2c_write_read($i2c_id$wbuf$rbuf$rlen);
}

function 
vn_hid_i2c_report_buf_val(&$buf$count$type "int")
{        
    
$low_bound 0;
    
$upp_bound 0;

    
$size count($buf);
    
$index 0;

    while(
$index $size)
    {
        
$low_bound $upp_bound;
        
$upp_bound += strlen($buf[$index]);

        if(
$count $upp_bound)
            if(
$type == "int")
                return 
bin2int($buf[$index], $count $low_bound1);
            else
                return 
substr($buf[$index], $count $low_bound1);

        
$index++;    
    }

    return 
false;
}

function 
vn_hid_i2c_parse_report_dest(&$buf)
{
    global 
$hid_device_sensor_num;
    global 
$hid_report_desc_table;
    global 
$hid_descriptor_table;

    
$tol_len 0;
    for(
$i 0$i count($buf); $i++)
        
$tol_len += strlen($buf[$i]);

    
$count 0;

    
$usage_offset 0;
    
$reach_flag false;
    
$hid_device_sensor_num 0;

    while(
$count $tol_len)
    {            
        if(
vn_hid_i2c_report_buf_val($buf$count) == HID_COLLECTION // Look for HID_COLLECTION(Physical) which should be start of REPORT ID (sensor device) info in report descriptor table  
        
{
            
$count++; // Increment the pointer to look at the next byte

            
if(vn_hid_i2c_report_buf_val($buf$count++) == HID_PHYSICAL && vn_hid_i2c_report_buf_val($buf$count++) == HID_REPORT_ID // Check if the next two bytes meet the next required identifier needs  
            
{
                
$usage_offset 0;                                  // clear offset of desired HID_USG_SENS_PROERTY field in report descriptor for later use in finding parameters within the GetReportFeature data                                    
                
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN SENSOR_ID_OFFSET] = vn_hid_i2c_report_buf_val($buf$count"bin");                    // Store first sensor device ID number in out temp struct

                
while(vn_hid_i2c_report_buf_val($buf$count++) != HID_USG_SENS_TYPE);        // Parse until sensor type indicator found  (in HID table: HID_USG_SENS_TYPE_MOTION_ACCELEROMETER_3D)

                
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_TYPE_OFFSET] = vn_hid_i2c_report_buf_val($buf$count"bin");                  // Store sensor type in struct      
                
$reach_flag true;                                       // Set the flag to show we are in a field of data that we desire
            
}
        }

        if ( 
vn_hid_i2c_report_buf_val($buf$count) == HID_END_COLLECTION )                        // Look for end of this sensor's collection
        
{
            
$count++;

            if( 
vn_hid_i2c_report_buf_val($buf$count) == HID_END_COLLECTION )
                break; 
// end of the report has occurred

            
else if(vn_hid_i2c_report_buf_val($buf$count++) == HID_REPORT_ID// Next report ID has been found
            
{  
                
$usage_offset 0// clear offset of desired HID_USG_SENS_PROERTY field in report descriptor for later use in finding parameters within the GetReportFeature data
                
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_ID_OFFSET] = vn_hid_i2c_report_buf_val($buf$count"bin");                    // Store location of sensor ID within the HID Report in struct

                
while(vn_hid_i2c_report_buf_val($buf$count++) != HID_USG_SENS_TYPE); // Look for sensor type identifier (ie HID_USG_SENS_TYPE_MOTION_ACCELEROMETER_3D)

                
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_TYPE_OFFSET] = vn_hid_i2c_report_buf_val($buf$count++, "bin"); // Store location of sensor type within the HID Report in struct  
                
$reach_flag true// Set the flag to show we are in a field of data that we desire
            
}
        }

        if (
$reach_flag// Check if we should proceed parsing within the HID_USG_SENS_Properties or simply continue incrementing until a new ID is found
        
{
            while(
$count $tol_len)       // Search for relevant features HID_USG_SENS_Properties
            
{
                if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_PROPERTY)            
                {
                    
$count++;
                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_PROPERTY_CONN_TYPE// Seach for sensor connection type and increment offset variable because this field is undesired  (in HID table:HID_USG_SENS_PROPERTY_SENSOR_CONNECTION_TYPE)
                    
{
                        
$count++;
                        if (
vn_hid_i2c_report_buf_val($buf$count++) == 0x03// last parameter of HID_USG_SENS_PROPERTY_SENSOR_CONNECTION_TYPE (0x0A,0x09,0x03)
                            
$usage_offset++;                        //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                    
}

                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_PROPERTY_RPT_STATE)  // Search for reporting state and increment offset variable because this field is undesired  (in HID table:HID_USG_SENS_PROPERTY_REPORTING_STATE)
                    
{
                        
$count++;
                        if (
vn_hid_i2c_report_buf_val($buf$count++) == 0x03)                    // last parameter of HID_USG_SENS_PROPERTY_REPORT_INTERVAL (0x0A,0x0E,0x03)
                            
$usage_offset++;                        //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                    
}

                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_PROPERTY_PWR_STATE)  // Search for power state and increment offset variable because this field is undesired  (in HID table : HID_USG_SENS_PROPERTY_POWER_STATE)
                    
{
                        
$count++;
                        if (
vn_hid_i2c_report_buf_val($buf$count++) == 0x03)                    // last parameter of HID_USG_SENS_PROPERTY_POWER_STATE (0x0A,0x19,0x03)
                            
$usage_offset++;                        //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                     
}

                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_STATE_1)        // Search for sensor state and increment offset variable because this field is undesired  (in HID table:HID_USG_SENS_STATE)
                    
{
                        
$count++;
                        if (
vn_hid_i2c_report_buf_val($buf$count++) == 0x02)                    // last parameter of HID_USG_SENS_STATE (0x0A,0x01,0x02)
                            
$usage_offset++;                        //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                     
}

                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_PROPERTY_RPT_INT)  // Search for reporting interval. We desire this value so store it in our struct and then increment the offset  (in HID table:HID_USG_SENS_PROPERTY_REPORT_INTERVAL)
                    
{
                        
$count++;
                        if (
vn_hid_i2c_report_buf_val($buf$count++) == 0x03)                    // last parameter of HID_USG_SENS_PROPERTY_REPORT_INTERVAL (0x0A,0x0E,0x03)
                            
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_DATART_OFFSET] = int2bin($usage_offset++, 1); //save & increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                    
}

                    
$count++;                                       //increment position ptr

                    // NOTE: There is a different identifier for RAW data and that is why two identifiers are checked for here
                    
if ( (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_ACCU) || (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RAW_ACCU) )  // Search for sensor accuracy & increment offset variable because this field is undesired (in HID table:HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_ACCURACY))
                        
$usage_offset++;                            //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)

                    
if ( (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RES) || (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RAW_RES) )    // Search for sensor resolution and increment offset variable because this field is undesired (in HID table: HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_RESOLUTION))
                        
$usage_offset++;                            //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)

                    
if ( (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_MOD_SENS) || (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RAW_MOD_SENS) )  // Search for sensor sensitivity (HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_CHANGE_SENSITIVITY_ABS))
                    
{
                        
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_SENSOR_OFFSET] = int2bin($usage_offset++, 1); //save & increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)                        

                        
while(vn_hid_i2c_report_buf_val($buf$count++) != HID_UNIT_EXP);         // Increment until exponent value of the data is found

                        
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_SENSOR_EXP_OFFSET] = vn_hid_i2c_report_buf_val($buf$count"bin");       // Store this value in the temp struct
                            
break;
                    }

                    if ( (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_MOD_MAX) || (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RAW_MOD_MAX) )   // Search for sensor MAX val and increment offset variable because this field is undesired HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MAX)
                        
$usage_offset++;                            //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)

                    
if ( (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_MOD_MIN)  || (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_DATA_RAW_MOD_MIN) )  // Search for sensor MIN val and increment offset variable because this field is undesired HID_USG_SENS_DATA(HID_USG_SENS_DATA_MOTION_ACCELERATION,HID_USG_SENS_DATA_MOD_MIN)
                        
$usage_offset++;                            //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                
}

                else 
$count++;                                      // If a new identifier has not yet been reached, continue traversing report descriptor
            
}

            
$usage_offset 0;                                      //reset HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)

            
while($count $tol_len)       // Search for relevant input features
            
{
                if(
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_)                        
                {
                    
$count++;                                       // Continue to next byte

                    
if (vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_STATE_1)                  
                    {
                        
$count++;
                        if(
vn_hid_i2c_report_buf_val($buf$count++) == HID_USG_SENS_STATE_2// Search for HID usage sensor state and increment offset variable because this field is undesired
                            
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                    
}

                    if (
vn_hid_i2c_report_buf_val($buf$count) == HID_USG_SENS_EVENT_1)              
                    {
                        
$count++; // Continue to next byte
                        
if (vn_hid_i2c_report_buf_val($buf$count++) == HID_USG_SENS_EVENT_2)   // Search for HID usage sensor event and increment the offset variable
                        
{    
                            
$usage_offset++; //increment HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data)
                            
while(vn_hid_i2c_report_buf_val($buf$count++) != HID_END_COLLECTION); // end of the sensor event field signifies the start of desired input data

                            
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_DATA_OFFSET] = int2bin($usage_offset 31); //save HID_USG_SENS_PROPERTY_... offset "pointer" (for later use in finding parameters within the GetReportFeature data) NOTE: offset an additional 3 for extra data received on GPIO interrupt

                            
while(vn_hid_i2c_report_buf_val($buf$count++) != HID_UNIT_EXP); // Search for, and store, the unit exponent value for the input data

                            
$hid_report_desc_table[$hid_device_sensor_num*SENSOR_INFO_LEN +  SENSOR_DATA_EXP_OFFSET] = vn_hid_i2c_report_buf_val($buf$count"bin");

                            break;
                        }
                    }
                }
                else 
$count++;                                      // If desired identifiers haven't been reached, continue traversing HID Report Descriptor
            
}

            
$reach_flag FALSE;                                          // Reset the flag to 0 to show that we are done with descriptor data from this report ID
            
$hid_device_sensor_num++;                                            // Increment to the next sensor in the struct array of sensors
        
}
        else 
$count++;                        
    }    
}

function 
hid_i2c_get_index($sensor_type)
{
    global 
$app_sensor_info;

    
$count count($app_sensor_info);
    
$indx 0;
    while(
$indx $count)
    {
        if(
$app_sensor_info[$indx] !== "")
        {
            
$type bin2int($app_sensor_info[$indx], SENSOR_TYPE_OFFSET1);

            if(
$type == $sensor_type)
                return 
$indx;
        }

        
$indx++;
    }

    exit(
"hid: the sensor type $sensor_type not added, please add it before use\r\n");
}

function 
vn_hid_i2c_get_report_id($sensor_type)
{
    global 
$app_sensor_info;

    
$indx hid_i2c_get_index($sensor_type);

    
$id bin2int($app_sensor_info[$indx], SENSOR_ID_OFFSET1);

    return 
$id;    
}

/*
After application get all infomation about sensor, delete this table to save memory
*/
function hid_i2c_del_report_desc()
{
    global 
$hid_report_desc_table$hid_device_sensor_num;

    
$hid_report_desc_table "";
    
$hid_device_sensor_num 0;
}

function 
hid_i2c_set_report($type$report_buf$size)
{
    global 
$hid_descriptor_table;

    
$wbuf "";

    
$vendor_cmd false;

    if (
bin2int($report_buf01) == FLASH_UPDATE_RPT_ID)                    // the flash update (Vendor) commands have a different format
        
$vendor_cmd true;

    
$wbuf .= substr($hid_descriptor_tableHID_CMD_REG_OFFSET2); //command field bytes from HID config table
    
$wbuf .= "\x00";

    if (
$vendor_cmd)
        
$wbuf[2] = int2bin($type bin2int($report_buf01), 1);// HID Set command opcode low byte which includes the sensor's ReportID, high byte report type
    
else
        
$wbuf[2] = int2bin($type bin2int($report_buf21), 1);

    
$wbuf .= int2bin(SET_OPCODE1);//$wbuf[3] // HID SetReport command opcode high byte
    
$wbuf .= substr($hid_descriptor_tableHID_DATA_REG_OFFSET2); //$wbuf[4] and $wbuf[5] //data field bytes from HID config table

    
$index 0;

    if (
$vendor_cmd)
        
$index++;//skips 1st byte of input buffer
    //else $size++;

    
$wbuf .= substr($report_buf$index$size);// Append input to the command

    
$rlen strlen($wbuf);

    
i2c_write(0$wbuf);  
}

/*
Request and Read input report from HID device
Parameters:
    -$report_type: Report Type: 1-input, 3 feature
*/
function hid_i2c_get_report(&$report_buf$report_type$sensor_type)
{
    global 
$hid_descriptor_table;

    
$wbuf "";

    
$report_id vn_hid_i2c_get_report_id($sensor_type);

    
$wbuf .= substr($hid_descriptor_tableHID_CMD_REG_OFFSET2);//command field bytes from HID config table
    
$wbuf .= int2bin(($report_type $report_id) , 1); //$wbuf[2] // HID Get command opcode low byte which includes the sensor's ReportID, high byte report type
    
$wbuf .= int2bin(GET_RPT_CMD_MSB1); // HID GetReport command opcode high byte
    
$wbuf .= substr($hid_descriptor_tableHID_DATA_REG_OFFSET2); //data field bytes from HID config table

    
vn_hid_i2c_write_read(0$wbuf$report_buf);    
}

/*
Note: if $cmd are CMD_POWER_ON, CMD_RESET_DEV, CMD_SLEEP, $sensor_type is any value.
*/
function hid_i2c_cmd_process(&$rbuf$cmd$sensor_type 0)
{
    global 
$hid_descriptor_table;
    
$cmd_buf "";

    switch(
$cmd)
    {
        case 
CMD_POWER_ON:

            
$cmd_buf .= substr($hid_descriptor_tableHID_CMD_REG_OFFSET2);

            
$cmd_buf .= "\x00\x08"// HID Power on command opcode    

            
i2c_write(0$cmd_buf4);

            break;

        case 
CMD_RESET_DEV:

            
$cmd_buf .= substr($hid_descriptor_tableHID_CMD_REG_OFFSET2);

            
$cmd_buf .= "\x00\x01"// HID Reset command opcode    

            
i2c_write(0$cmd_buf4);

            
$time_expire vn_hid_i2c_get_tick() + 5000//5 sec (as per HID spec) timeout for reset command

            
$data_avail false;

            while(
$time_expire vn_hid_i2c_get_tick())
            {
                if(
uio_in(0INT_PIN) == INT_ASSERTED)
                {
                    
$data_avail true;
                }
            }

            if(
$data_avail)
            {
                
$rbuf "";
                
i2c_read(0$rbuf2);

                if(
bin2int($rbuf02) == 0)
                    echo 
"reset successfully\r\n";
                else
                    echo 
"reset fail\r\n";
            }
            else
                echo 
"reset fail\r\n";

            break;

        case 
CMD_SLEEP:        

            
$cmd_buf .= substr($hid_descriptor_tableHID_CMD_REG_OFFSET2);

            
$cmd_buf .= "\x01\x08"// HID sleep command opcode    

            
i2c_write(0$cmd_buf4);                                                                
            break;

        case 
CMD_HID_GET_RPT_INPT:

            
hid_i2c_get_report($rbufGET_RRT_INPT$sensor_type);                                  

            break;


        case 
CMD_HID_GET_RPT_FEAT:          

            
hid_i2c_get_report($rbufGET_RPT_FEAT$sensor_type);

            break;

        case 
CMD_HID_SET_RPT_FEAT:

            
//for non-Vendor commands, the sensor id is NOT passed in byte[0]
            
$max_size bin2int($rbufRPT_SIZE_LSB1);//get size of Report Feature Packet

            
hid_i2c_set_report(SET_RPT_FEAT$rbuf$max_size);      
            break;

        default:
            break;
    }

    return 
true;
}

/*
Retrieve the HID descriptor table
*/
function hid_i2c_desc_handler()
{
    global 
$hid_descriptor_table;

    
$cmd int2bin(0x012); // HID descriptor table request is 0x0001

    
i2c_write_read(0$cmd$hid_descriptor_table30); // HID descriptor length is 30    
}

/*
Retrieve and parse report descriptor table
*/
function hid_i2c_rept_desc_handler()
{
    global 
$hid_descriptor_table;

    
$cmd substr($hid_descriptor_tableHID_REPORT_DESC_REG_OFFSET2); // Report table request is 0x0200

    
$rbuf_1 "";
    
$rbuf_2 "";
    
$rbuf_3 "";

    
$rlen bin2int($hid_descriptor_tableHID_REPORT_DESC_LEN_OFFSET2);

    
$pid pid_open_nodie("/mmap/i2c0""i2c_write_read");

    if(
pid_ioctl($pid"get txfree") >= 2)
        
pid_write($pid$cmd2);
    else
        exit(
"i2c_write_read: tx buffer full\r\n");

    
pid_ioctl($pid"req write wait");
    while(
pid_ioctl($pid"get state") && pid_ioctl($pid"get txlen"))
        ;

    if(
pid_ioctl($pid"get error"))
    {
        
pid_ioctl($pid"req stop");
        
pid_close($pid);
        return 
0;
    }
    else
    {
        
pid_ioctl($pid"req read $rlen");
        while(
pid_ioctl($pid"get state"))
            ;
        
$rlen 0;
        
$rlen += pid_read($pid$rbuf_1);
        
$rlen += pid_read($pid$rbuf_2);
        
$rlen += pid_read($pid$rbuf_3);

        
pid_close($pid);        
    }

    
// parse report descriptor table
    
$rept_desc_buf = array($rbuf_1$rbuf_2$rbuf_3);
    
vn_hid_i2c_parse_report_dest($rept_desc_buf);
}

function 
hid_i2c_get_exponent($sensor_type)
{
    global 
$app_sensor_info;

    
$indx hid_i2c_get_index($sensor_type);

    
$data_expo bin2int($app_sensor_info[$indx], SENSOR_DATA_EXP_OFFSET1);

    if (
$data_expo >= && $data_expo <= 7)                        // These values are all positive exponents
        
$mult pow(10, (float)$data_expo);

    if(
$data_expo >= && $data_expo <= 0x0F)                      // These values are all negative exponents (ie. to right of decimal place)
        
$mult pow(10, (float)($data_expo 16));

    return 
$mult;  
}

function 
hid_i2c_add_sensor($sensor_type)
{
    global 
$app_sensor_info;    
    global 
$hid_report_desc_table$hid_device_sensor_num;

    
$buf "";    

    
$max_sensor_num count($app_sensor_info);

    for(
$indx 0$indx $max_sensor_num$indx++)
    {
        if(
$app_sensor_info[$indx] === "")
        {
            for(
$count 0$count $hid_device_sensor_num$count++)
            {
                
$offset $count SENSOR_INFO_LEN;

                
$type bin2int($hid_report_desc_table$offset SENSOR_TYPE_OFFSET1);

                if(
$type == $sensor_type)
                {
                    
$buf substr($hid_report_desc_table$offsetSENSOR_INFO_LEN);        

                    
$app_sensor_info[$indx++] = $buf;

                    return 
true;
                }
            }

            exit(
"hid: sensor type $sensor_type not support by device\r\n");    
        }    
    }

    echo 
"hid: excceed the allowed number of sensors by the api\r\n";

    return 
false;    
}

/*
Return: sensor type of report
*/
function vn_hid_i2c_update_data(&$report)
{
    global 
$app_sensor_info$app_data;

    
$sensor_id bin2int($report21);

    
$max_sensor_num count($app_sensor_info);

    for(
$indx 0$indx $max_sensor_num$indx++)
    {
        if(
$app_sensor_info !== "")
        {
            
$id bin2int($app_sensor_info[$indx], SENSOR_ID_OFFSET1);

            if(
$sensor_id == $id)
            {
                
$offset bin2int($app_sensor_info[$indx], SENSOR_DATA_OFFSET1);

                
$app_data[$indx] = substr($report ,$offset);

                
$sensor_type bin2int($app_sensor_info[$indx], SENSOR_TYPE_OFFSET1);

                return 
$sensor_type;
            }
        }
    }

    return -
1;        
}

function 
vn_hid_i2c_get_data(&$data$sensor_type)
{
    global 
$app_sensor_info$app_data;

    
$indx hid_i2c_get_index($sensor_type);

    
$data $app_data[$indx];

    return 
true;
}

/*
This function is used when app want to request and read data from sensor.
*/
function hid_i2c_request_data(&$data_buf$sensor_type)
{        
    
$rbuf "";

    
hid_i2c_get_report($rbufGET_RRT_INPT$sensor_type);

    
vn_hid_i2c_update_data($rbuf);

    
vn_hid_i2c_get_data($data_buf$sensor_type);
}

/*
This function is used to read data when an interrupt is asserted.
Return:
*/
function hid_i2c_read_data(&$data_buf)
{
    
$rbuf "";

    
i2c_read(0$rbuf13);

    
$sensor_type vn_hid_i2c_update_data($rbuf);

    
vn_hid_i2c_get_data($data_buf$sensor_type);

    return 
$sensor_type;
}

?>





MM7150 Library

This library is to interact with MM7150 module.
Vd_MM7150.PHP
PHP Code:
<?php

define
("MM7150_ADDR"0x40);

define("STATE_WAKEUP_MASK",      1);
define("STATE_SLEEP_MASK",      << 1);
define("STATE_START_MASK",      << 2);
define("STATE_RESET_MASK",      << 3);
define("STATE_ACCEN_MASK",      << 4); //Accel enable
define("STATE_GYREN_MASK",      << 5);
define("STATE_CMPEN_MASK",      << 6);
define("STATE_ORIEN_MASK",      << 7);
define("STATE_INCEN_MASK",      << 8);
define("STATE_BAREN_MASK",      << 9);
define("STATE_ALSEN_MASK",      << 10);
define("STATE_RAW_ACCEN_MASK",  << 11);
define("STATE_RAW_MAG_MASK",    << 12);
define("STATE_RAW_GYR_MASK",    << 13);

define("MAX_RETRIES",    );

define("FULL_POWER",    0x02);
define("LOW_POWER",        0x03);
define("ON",        TRUE);
define("OFF",        FALSE);

$mm7150_power_state OFF;
$mm7150_sensor_state = array(OFFOFFOFFOFFOFFOFFOFFOFF);

function 
vd_mm7150_get_tick()
{
    while((
$pid pid_open("/mmap/st8"O_NODIE)) == -EBUSY)
        
usleep(500);

    if(!
pid_ioctl($pid"get state"))
    {
        
pid_ioctl($pid"set div us");  
        
pid_ioctl($pid"start");
    }

    
$tick pid_ioctl($pid"get count");
    
pid_close($pid);

    return 
$tick;
}
/** mm7150_set_state_data
* @note    Set and confirm new features for a device
* @param GET_SET_PARAMS structure containing  power state (=2: FULL, =3: LOW),sensor ID, New data rate,New sensitivity
* @return error status 0=SUCCESS, failcodes: 0x17=SET_FEAT_FAIL
*/
function mm7150_set_state_data($sensor_type$power_state$sensitivity 0$sensitivity_valid false$data_rate 0$data_rate_valid false)
{
    
$get_feat_buf str_repeat("\x00"40);// GetFeature report buffer
    
$set_feat_buf str_repeat("\x00"40);// SetFeature report buffer


    
hid_i2c_cmd_process($get_feat_bufCMD_HID_GET_RPT_FEAT$sensor_type); // Issue a get report feature command and store the get features in $get_feat_buf

    
$set_feat_buf $get_feat_buf;                   // Copy GetFeatBuff to bufs to use for set feature command

    // Modify fields that we would like to set in bufs (sensor state, power state, data rate, sensitivity)
    
$set_feat_buf[RPT_REPORT_STATE] = int2bin(HID_USG_SENS_PROPERTY_REPORTING_STATE_ALL_EVENTS_ENUM1);  
    
$set_feat_buf[RPT_PWR_STATE] = int2bin($power_state1);          //update the Power state parameter

    
if ($data_rate_valid)                                  // update data rate value?
    
{
        
$set_feat_buf[RPT_REPORT_INTVAL_LSB] = int2bin($data_rate2); //update 16 bit data rate value
    
}

    if (
$sensitivity_valid)                               // update sensitivity value?
    
{
        
$set_feat_buf[RPT_CHG_SENS_LSB] = int2bin($sensitivity2); //update 16 bit sensitivity value
    
}

    for (
$retry_count 0$retry_count MAX_RETRIES$retry_count++)    // API spec requires 3 attempts at setting new features.
    
{
        
hid_i2c_cmd_process($set_feat_bufCMD_HID_SET_RPT_FEAT$sensor_type);  // Send HID_SetFeature command to SSC7150 to update new features to the device
        
hid_i2c_cmd_process($get_feat_bufCMD_HID_GET_RPT_FEAT$sensor_type);  // Send HID_GetFeature command to SSC7150 to check if new feature request to device were updated

        
$buf_size bin2int($get_feat_bufRPT_SIZE_LSB1);                     // size (in bytes) of packet is in 1st byte
        
for ($pointer 0$pointer $buf_size$pointer++)           // Check to see if the features have been updated
        
{
            if ( 
$get_feat_buf[$pointer] != $set_feat_buf[$pointer] )
                break;                                              
//no they weren't, try again
        
}

        if ( 
$pointer == $buf_size )                                 //that's the whole packet, ALL data matches
            
break;                                                  //we're done
    
}

    if (
$retry_count == MAX_RETRIES)
        return;

}

function 
mm7150_enable_sensor($sensor_type)
{
    global 
$mm7150_power_state;
    global 
$mm7150_sensor_state;
    global 
$app_sensor_info;

    
//WAKE COMMAND IF MM7150 IS SLEEPPING
    
if (!$mm7150_power_state)
    {
        
wakeup_signal();//assert wake signal (1 ms toggle of RE9 signal to SSC150)

        
usleep(15000);//wait 12 ms (11 ms min per spec) after wake signal and before sending POWER_ON command to SSC7150

        
$abv_buf "";

        if (
hid_i2c_cmd_process($abv_bufCMD_POWER_ON))  // Issue the wake command (parameters 1 and 3 are not used)
        
{

            
$mm7150_power_state ON;

            
//spec says must wait a minimum of 30 ms before next command to SSC7150
            
usleep(35000);                                
        }
    }

    
//ENABLE SENSOR COMMAND

    
mm7150_set_state_data($sensor_typeFULL_POWER);

    
$mm7150_sensor_state[hid_i2c_get_index($sensor_type)] = ON;

    return 
true;
}

/*
$sensor_list: Array of sensors contains list of sensor type
*/
function mm7150_init($sensor_list)
{
    global 
$mm7150_power_state;
    global 
$app_sensor_info;

    
$rbuf "";

    if(
uio_in(0INT_PIN) == INT_ASSERTED)    
        
vn_hid_i2c_read(0$rbuf);

    
// Retrieve the HID descriptor table    
    
hid_i2c_desc_handler();

    
// Wake the EC
    
hid_i2c_cmd_process($rbufCMD_POWER_ON);

    
//Reset the EC
    
hid_i2c_cmd_process($rbufCMD_RESET_DEV);

    
//Retrieve and parse report descriptor table
    
hid_i2c_rept_desc_handler();

    
$sensor_num count($sensor_list);

    for(
$i 0$i $sensor_num$i++)
    {

        
hid_i2c_add_sensor($sensor_list[$i]);    
    }

    
// HID device is now awake and ready for operation
    
$mm7150_power_state ON;

}
?>






Application

This code shows how to read value from all sensor of MM7150 module
Task0.php
PHP Code:
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; 
// avoid php execution via http request

include_once "/lib/sd_340.php";
include_once 
"/lib/vd_MM7150_pin_map.php";
include_once 
"/lib/vn_hid_i2c.php";
include_once 
"/lib/vd_MM7150.php";

function 
unsign2sign($val)
{
    if(
$val&0x8000)
        
$val = ($val&0x7fff) - 0x8000;

    return 
$val;
}
// Init user interface

i2c_setup(0MM7150_ADDR);
uio_setup(0INT_PIN"in");
uio_setup(1WAKEUP_PIN"out");
uio_out(1WAKEUP_PINHIGH);    

//List of sensor in use

$sensor_list = array(
                
ACCEL_SENSOR_TYPE,
                
GYRO_SENSOR_TYPE,
                
CMP_SENSOR_TYPE,
                
ORI_SENSOR_TYPE,
                
INCL_SENSOR_TYPE);

// Init MM7150. Retrieve HID & report descriptors, and all device features
mm7150_init($sensor_list);

$sensor_type ACCEL_SENSOR_TYPE;
//$sensor_type = GYRO_SENSOR_TYPE;
//$sensor_type = CMP_SENSOR_TYPE;
//$sensor_type = ORI_SENSOR_TYPE;
//$sensor_type = INCL_SENSOR_TYPE;

mm7150_enable_sensor($sensor_type);

$mult hid_i2c_get_exponent($sensor_type);

$data "";    

while(
1)
{    
    
/*
    //Polling Driven
    hid_i2c_request_data($data, $sensor_type);

    $x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
    $y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
    $z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;

    echo "x_val: ", $x_val , "\r\n";
    echo "y_val: ", $y_val , "\r\n";
    echo "z_val: ", $z_val , "\r\n";

    if($sensor_type == ORI_SENSOR_TYPE)
    {
        $w_val = unsign2sign(bin2int($data, 6, 2)) * $mult;
        echo "w_val: ", $w_val , "\r\n";
    }
    */

    // Event Driven
    
if(uio_in(0INT_PIN) == INT_ASSERTED)
    {                    
        
$data "";        

        
$in_sens_type hid_i2c_read_data($data);

        if(
$in_sens_type == $sensor_type)
        {
            
$x_val unsign2sign(bin2int($data02)) * $mult;
            
$y_val unsign2sign(bin2int($data22)) * $mult;
            
$z_val unsign2sign(bin2int($data42)) * $mult;

            echo 
"x_val: "$x_val "\r\n";
            echo 
"y_val: "$y_val "\r\n";
            echo 
"z_val: "$z_val "\r\n";

            if(
$sensor_type == ORI_SENSOR_TYPE)
            {
                
$w_val unsign2sign(bin2int($data62)) * $mult;
                echo 
"w_val: "$w_val "\r\n";
            }
        }
    }    
}
?>



You can get full source code here: MM7150_full_source_code.zip