Sensor data communication using the I2C protocol on LinkIt Assist 2502 development boards

Introduction

This tutorial is based on the I2C standard peripheral communication protocol feature of the MediaTek LinkIt™ Assist 2502 development board . By the end of this tutorial you’ll have a fully functioning IoT example, which gathers sensor data using the LinkIt Assist 2502 I2C APIs.

This tutorial guides you through:

  • Building the example, with details of the hardware requirements and how to put them together.
  • Creating the software to provide the interfaces with the temperature, pressure and humidity sensors (offered by a TPH board (STH21)) and LinkIt Assist 2502 development board so that the sensor data can be collected.
  • Running your application and viewing the data collected in the serial monitor of the Arduino IDE.
  • Analyzing the signals exchanged between the LinkIt Assist 2502 board and the sensors, and observing how they relate to the instructions in your application. At the end of the tutorial there are details on where to go for additional information on the LinkIt ONE development board and how to create software for it.

Before You Start

If you haven’t built a LinkIt Assist 2502 project before you need to download and install the Eclipse IDE and LinkIt Assist 2502 SDK 2.0, then configure the IDE and upgrade the board’s firmware.

Setup your development environment

Full details on setting up the necessary LinkIt Assist 2502 development environment can be found in the LinkIt Assist 2502 developer’s guide. Complete this before you continue, if you haven’t already set up your development environment.

Build the Hardware

This section describes the hardware and electronics needed to build the setup for your project.

What you need

To build your project’s hardware, in addition to LinkIt Assist 2502 development board you need the following components.

ComponentDescriptionSource
CableMicro-USB cable 
TPH BoardTemperature, pressure and humidity boardSeeed Bazaar
PowerPolymer Li-ion 1000mAh batteryLinkIt Assist 2502 Development Kit

There are two sensor modules on the TPH board, a Sensirion SHT21 with temperature and humidity sensors and a Bosch Sensortec BMP180 with temperature and pressure sensors. The board also includes a Grove socket for the I2C pins, as shown below.

You’ll use the Sensirion SHT21 sensor only for this project.

Putting the components together

LinkIt Assist 2502 development board provides only one I2C interfaces. It’s P6 and P7. P6 represents the SCL and P7 represents the SDA pin. Connect the sensor to the Grove interface using a 4-wire bus connector, as shown in the figure below.

Since there is no direct bus interface for the sensor, four additional wires are used.

Create the Software

Your project needs software to collect data from the TPH board using I2C interface on the LinkIt Assist 2502 development board. This section describes the details of the software required with an overview of the source code in C.

LinkIt Assist 2502 project on Eclipse IDE

Follow the description in section 2.5, “Creating Your First Project” of the LinkIt Assist 2502 developer’s guide to create a new project, in Eclipse IDE.

Add the header files for the supporting libraries

Once a new project is created in the IDE, add the supporting libraries. The I2C communication support is provided in the header file vmdcl_i2c.h. Import the required libraries as follows:

#include "vmtype.h"
#include "vmlog.h"
#include "vmsystem.h"
#include "vmdcl.h"
#include "vmdcl_i2c.h"
#include "vmtimer.h"
#include "I2C_communication.h"

The header file I2C_communication.h is automatically generated when the project is created in the Eclipse IDE (the name matches with the user defined project name).

Define the variables

Next, the static variables used in the project are defined.

#define sht21address 0x40
#define sht21temp  0xF3
  1. sht21address is the I2C ID of the sensor in binary format. When the master device — your board — wants to communicate with the slave device, it needs this ID to locate the slave. You can find your device’s ID on its datasheet, in this case the ID for the TPH board is 0x40 in hexadecimal format.
  2. sht21temp is the sensor ID that the master sends to the sensor to get the temperature data, in this case 0xF3 in hexadecimal format. This information is also provided in the SHT21 datasheet.
  3. Define the precise timer ID g_ptimer_id to use in the function customer_precise_timer_example_proc().

    VM_TIMER_ID_PRECISE g_ptimer_id = 0;
  4. Declare a parameter i2c_handle.

    VM_DCL_HANDLE i2c_handle = -1;
  5. Define input and output data arrays with their lengths.

    VMUINT8 data_out[8];
    VMUINT8 data_out_len;
    
    VMUINT8 data_in[8];
    VMUINT8 data_in_len;

Define the main() function

LinkIt Assist 2502 application in Eclipse IDE has a default implementation of the main function vm_main().In general, it registers system events and callback functions for the input events. The default implementation of the vm_main() is a call to register a system event handler callback function, as shown below.

/* Entry point */
  void vm_main(void){
      /* register system events handler */
      vm_pmng_register_system_event_callback(handle_sysevt);
}

Define the callback function handle_sysevt()

handle_sysevt() is the callback to be invoked by the system engine.

  1. Call the vm_log_debug() function to generate a debug log. If the system message is VM_EVENT_CREATE, call the functioni2c_init() to initialize the I2C bus, see Initialize the I2C bus.

    void handle_sysevt(VMINT message, VMINT param) {
        vm_log_debug("Driver handle_sysevt %d", message);
        switch (message) {
        case VM_EVENT_CREATE:
            i2c_init();
  2. Apply one second timer to continuously receive data from the temperature sensor by calling thevm_timer_create_precise(1000, (vm_timer_precise_callback)customer_precise_timer_example_proc, NULL) function, see Communicate with the sensor.
  3. If the precise timer is negative, save the debug log message to indicate the timer has failed and exit the case.

    g_ptimer_id = vm_timer_create_precise(1000, (vm_timer_precise_callback)customer_precise_timer_example_proc, NULL);
            if(g_ptimer_id < 0){
                vm_log_debug("customer_run_timer create timer fail");
            }
            break;
  4. If the system message is VM_EVENT_QUIT, check if the precise timer ID is positive, if it is, call the functionvm_timer_delete_precise() to stop receiving data from the sensor.

    case VM_EVENT_QUIT:
            if(g_ptimer_id>0){
                vm_timer_delete_precise(g_ptimer_id);
            }
            vm_log_info("Driver VM_MSG_QUIT");
            break;
        }
    }


 

Initialize the I2C bus

 

Call the function vm_dcl_open() function to configure the I2C bus using the vm_dcl_control() function with the commandVM_DCL_I2C_CMD_CONFIG.

VMBOOL i2c_init(void){
    i2c_handle = vm_dcl_open(VM_DCL_I2C,0);
    if (VM_DCL_HANDLE_INVALID == i2c_handle) {
        vm_log_info("i2c init failed 1");
        return VM_FALSE;
    }
 
    vm_dcl_i2c_control_config_t conf_data;
    VM_DCL_STATUS status = 0;
 
    conf_data.reserved_0 = 0;
    conf_data.transaction_mode = VM_DCL_I2C_TRANSACTION_FAST_MODE;
    conf_data.get_handle_wait = 0;
    conf_data.reserved_1 = 0;
    conf_data.delay_length = 0;
    conf_data.slave_address = sht21address<<1;
    conf_data.fast_mode_speed = 100;
    conf_data.high_mode_speed = 0;
    status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_CONFIG,(void *)&conf_data);
    if (VM_DCL_STATUS_OK != status){
        vm_log_info("i2c init failed 2, status: %d", status);
        return VM_FALSE;
    }
    return VM_TRUE;
}

Communicate with the sensor

The timer callback customer_precise_timer_example_proc() is invoked each second (1000ms), see Define the callback functionhandle_sysevt().

  1. The callback function calls the i2c_write() function, waits for 100ms for the response from the sensor then reads the temperature data by calling the i2c_read() function.

    void customer_precise_timer_example_proc(VM_TIMER_ID_PRECISE tid, void* user_data){
        i2c_write(sht21temp);
        vm_thread_sleep(100);
        i2c_read(sht21address, 2);
  2. The data is read and stored in the debug log, then formatted, as shown below.

    VMBYTE msb = data_in[0];
    VMBYTE lsb = data_in[1];
     
    vm_log_debug("msb = %d", msb);
    vm_log_debug("lsb = %d", lsb);
     
    VMINT t = (msb << 8) + lsb;


  3. Apply the formula from the datasheet to convert the temperature into Celsius degrees. Then save the value in the debug log.

    where parameter1 is the output temperature signal from the sensor.

     

     

    VMFLOAT temperature = -46.85 + 175.72 * t / 65536.0;
        vm_log_debug("temperature = %f", temperature);
        vm_log_debug("==============================");
    }

     

     

Define the I2C write and read functions

The I2C communication is implemented with read (i2c_read(sht21address, 2)) and write (i2c_write(sht21temp)) operations.

  1. Call the i2c_write() write data to the I2C device data using the vm_dcl_control() function with the input commandVM_DCL_I2C_CMD_SINGLE_WRITE. The data_out buffer holds the data that is written to the I2C device.

    VMUINT8 i2c_write(VMUINT8 data) {
     
        data_out[data_out_len++] = data;
        vm_dcl_i2c_control_single_write_t write_data;
            VM_DCL_STATUS status = 0;
        VMUINT8 result = 0;
     
        write_data.data_ptr = &(data_out[0]);
        write_data.data_length = data_out_len;
        status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_SINGLE_WRITE,(void *)&write_data);
     
        data_out_len = 0;
        return 1;
    }



  2. Call the i2c_read() function read the data from the I2C device data using vm_dcl_control() function with the commandVM_DCL_I2C_CMD_SINGLE_READ. The data_in buffer receives the data read from the I2C device.

    VMUINT8 i2c_read(VMUINT8 address, VMUINT8 len){
     
        VM_DCL_STATUS status = 0;
        vm_dcl_i2c_control_single_read_t read_data;
         
        read_data.data_ptr = &(data_in[0]);
        read_data.data_length = len;
        status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_SINGLE_READ,(void *)&read_data);
        data_in_len = len;
        return 1;
    }


Full Source Code

You have now completed the sketch and its full source code is shown below.
 

 
#include "vmtype.h"
#include "vmlog.h"
#include "vmsystem.h"
#include "vmdcl.h"
#include "vmdcl_i2c.h"
#include "vmtimer.h"
#include "I2C_communication.h"
 
#define sht21address 0x40
#define sht21temp 0xF3
 
VM_TIMER_ID_PRECISE g_ptimer_id = 0;
VM_DCL_HANDLE i2c_handle = -1;
 
VMUINT8 data_out[8];
VMUINT8 data_out_len;
 
VMUINT8 data_in[8];
VMUINT8 data_in_len;
 
VMUINT8 i2c_write(VMUINT8 data) {
 
    data_out[data_out_len++] = data;
    vm_dcl_i2c_control_single_write_t write_data;
    VM_DCL_STATUS status = 0;
    VMUINT8 result = 0;
 
    write_data.data_ptr = &(data_out[0]);
    write_data.data_length = data_out_len;
    status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_SINGLE_WRITE,(void *)&write_data);
 
    data_out_len = 0;
    return 1;
}
 
VMUINT8 i2c_read(VMUINT8 address, VMUINT8 len){
 
    VM_DCL_STATUS status = 0;
    vm_dcl_i2c_control_single_read_t read_data;
     
    read_data.data_ptr = &(data_in[0]);
    read_data.data_length = len;
    status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_SINGLE_READ,(void *)&read_data);
    data_in_len = len;
    return 1;
}
 
VMBOOL i2c_init(void){
    i2c_handle = vm_dcl_open(VM_DCL_I2C,0);
    if (VM_DCL_HANDLE_INVALID == i2c_handle) {
        vm_log_info("i2c init failed 1");
        return VM_FALSE;
    }
 
    vm_dcl_i2c_control_config_t conf_data;
    VM_DCL_STATUS status = 0;
 
    conf_data.reserved_0 = 0;
    conf_data.transaction_mode = VM_DCL_I2C_TRANSACTION_FAST_MODE;
    conf_data.get_handle_wait = 0;
    conf_data.reserved_1 = 0;
    conf_data.delay_length = 0;
    conf_data.slave_address = sht21address<<1;
    conf_data.fast_mode_speed = 100;
    conf_data.high_mode_speed = 0;
    status = vm_dcl_control(i2c_handle,VM_DCL_I2C_CMD_CONFIG,(void *)&conf_data);
    if (VM_DCL_STATUS_OK != status){
        vm_log_info("i2c init failed 2, status: %d", status);
        return VM_FALSE;
    }
    return VM_TRUE;
}
 
void customer_precise_timer_example_proc(VM_TIMER_ID_PRECISE tid, void* user_data){
    i2c_write(sht21temp);
    vm_thread_sleep(100);
    i2c_read(sht21address, 2);
 
    VMBYTE msb = data_in[0];
    VMBYTE lsb = data_in[1];
 
    vm_log_debug("msb = %d", msb);
    vm_log_debug("lsb = %d", lsb);
 
    VMINT t = (msb << 8) + lsb;
    VMFLOAT temperature = -46.85 + 175.72 * t / 65536.0;
    vm_log_debug("temperature = %f", temperature);
    vm_log_debug("==============================");
}
 
 /* The callback to be invoked by the system engine. */
void handle_sysevt(VMINT message, VMINT param) {
    vm_log_debug("Driver handle_sysevt %d", message);
    switch (message) {
    case VM_EVENT_CREATE:
        i2c_init();
        g_ptimer_id = vm_timer_create_precise(1000, (vm_timer_precise_callback)customer_precise_timer_example_proc, NULL);
        if(g_ptimer_id < 0){
            vm_log_debug("customer_run_timer create timer fail");
        }
        break;
    case VM_EVENT_QUIT:
        if(g_ptimer_id>0){
            vm_timer_delete_precise(g_ptimer_id);
        }
        vm_log_info("Driver VM_MSG_QUIT");
        break;
    }
}
/* Entry point */
void vm_main(void){
    /* register system events handler */
    vm_pmng_register_system_event_callback(handle_sysevt);
}

Run Your Application

To run your sketch on the LinkIt Assist development board, follow the instructions in sections 2.6, “Compiling and Uploading to the Hardware” and 2.7, “Running your Project” of the LinkIt Assist 2502 developer’s guide. Once the project is successfully built and compiled, click monitor to view the result.

Conclusion

You’ve now successfully transmitted data from the temperature sensor from your LinkIt Assist 2502 device using the I2C communication. For more information on the MediaTek LinkIt Assist 2502 development and prototyping board refer to LinkIt Assist 2502 developer’s guide.