SJ cartoon avatar

Embedded BLE112 - Communicating with the MPU6050

My BLE112 posts (UART, I2C, BLED112) seem to be my most trafficked articles. While reading through them again recently (after installing a proper syntax highlighter - Crayon), I realized that I only provided snippets of code, which may not be that useful to new BLE112 users who want to get up and running. There are full samples in the BlueGiga documentation, but I thought it might be nice to have a sample that incorporates some of the less seen functionality

The code I am posting right now was developed (easy part) and debugged (hard part) by my colleagues and I at PUSH Design Solutions. We’re a start-up based out of the technology accelerator JOLT at the MaRS Centre in Toronto, Canada. Our focus is on building a next-generation smart device that can track, analyze, and improve athletes performance in weight training. You can learn more about us here at Jolt.

From a technological side, we want to encourage innovation instead of stifling it, which is why we promote using and contributing to open-source software when possible, and trying to be a part of the open-source community. As part of this, I include dissemination (as opposed to hoarding) of knowledge as much as possible (the whole reason for this blog).

Back to the fun stuff. I’ve had a horrendous time of trying to put XML in this Wordpress blog, even with multiple tags and syntax highlighters. So, if there are errors in the XML, I apologize (the BGScript should be fine).

This code is based on an early prototype that we made, but we’ve since switched products (for reasons detailed in this post) and changed architectures slightly, so we haven’t worked on improving this script.

A quick summary of what is going on is that we have a BLE112 module talking directly to an MPU6050 over I2C (eliminating the need for a pesky MCU middle-man). The ridiculous number of I2C writes is caused by the software I2C implementation as talked about in my other post. Speaking with BlueGiga support, it seems that even this is flaky, and depending on your module, doing it this way may not even work!

It works fine for me but it’s REALLY slow. The fastest update rate I could get was about 50-60ms updates (I didn’t find the exact number, but the point is, it’s more than 1-2ms).  What we would like to do is have the I2C at a point where we could call a single I2C write and then read in 14 bytes straight (instead of a slew of single read/writes) as this would reduce BGScript overhead.

One important point is that I based this code from an example from Jeff Rowberg’s Arduino implementation. Anywhere with I2CDev:: was ported from the Arduino code, to aim for the functionality of that code.

BLE112 Pins

As mentioned in the code, I use pins 7 and 8 for my I2C. The pinouts can be seen from the image below.

BLE112 Breakout Board pinout

Code

For the moment, I’ll list everything that’s needed in this post, so that it’s also searchable by Google. Down the road, I’ll probably push all of this information to a public Github or BitBucket repo.

Update - Dec 1, 2014

I -have- pushed this information to a BitBucket repo! It is located here and is under some renovation at the moment (including a really hacky Android app to do OTA programming with the BLE113). Also, I’ve just posted a lot more information about the MPU6050 and similar here.

Update - March 9, 2015

I’ve just moved all my BGScript (BLE112/BLE113) examples to a Github repo (and I’m closing down that previous BitBucket repo). https://github.com/sureshjoshi/ble113-firmware-examples

BGScript

# This is the BGScript for communication to/from the MPU6050 using the BLE112 I2C interface (pin 8-SDA, pin 7 - SCL)

# API:
# call hardware_i2c_write(address,stop,data_len, data_data)(written)
# call hardware_i2c_read(address,stop,length)(result,data_len,data_data)
# call attributes_write(handle, offset, value_len, value_data)(result)

dim written
dim result
dim port
dim data
dim data_len
dim connected

# Boot event listener
event system_boot(major ,minor ,patch ,build ,ll_version ,protocol_version ,hw)

      #Set timer to generate event every 50ms (32768/1000ms * 50ms) (needs to be roughly less than connection interval time)
      call hardware_set_soft_timer(1634, 1, 0)

      # This should be the timer for 1s
      # call hardware_set_soft_timer(32768, 1, 0)

      # Initialize as 'disconnected'
      connected = 0

      call gap_set_mode(gap_general_discoverable, gap_undirected_connectable)   # Start advertisement
      call sm_set_bondable_mode(1)

      # Read WHO_AM_I register (0x75) of device at address (default address of MPU6050 is 0x68, which is then left shifted once to make 0xD0/208)
      call hardware_i2c_write(208,0,1,"\x75")(written)
      call hardware_i2c_read(208,1,1)(result,data_len,data)
      call attributes_write(xgatt_who, 0, 1, data)

      # Set power management register (0x6B) to use gyro clock (0x01)
        #setClockSource(MPU6050_CLOCK_PLL_XGYRO);
        #I2Cdev::writeBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, source);
        #setSleepEnabled(false);
        #I2Cdev::writeBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled);
      call hardware_i2c_write(208,1,2,"\x6b\x01")(written)

      # Set gyro configuration register (0x1B) to all zeros (0x00)
        #setFullScaleGyroRange(MPU6050_GYRO_FS_250);
        #I2Cdev::writeBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, range);
      call hardware_i2c_write(208,1,2,"\x1b\x00")(written)

      # Set accelerometer configuration register (0x1C) to all zeros (0x00)
        #setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
        #I2Cdev::writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range);
      call hardware_i2c_write(208,1,2,"\x1c\x00")(written)
end

# Connection event listener
event connection_status(connection, flags, address, address_type, conn_interval, timeout, latency, bonding)
    # Set up a connection interval of 50ms (40*1.25ms)
    if connected = 0 then
        call connection_update(connection,40,40,latency,timeout)
    end if
    connected = 1
end

# Disconnection event listener
event connection_disconnected(connection, reason)
    call gap_set_mode(gap_general_discoverable, gap_undirected_connectable) # Start advertisement
    connected = 0
end

# Buffer to hold the 6 bytes of acceleration data, 6 bytes of gyro data
dim sensor(12)

#Timer event listener
event hardware_soft_timer(handle)
    # Read the acceleration X, Y, Z (high, then low bytes) registers and write to the GATT
    # Read gyro X,Y,Z (high, then low bytes) registers and write to GATT
    call hardware_i2c_write(208,0,1,"\x3b")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(0))

    call hardware_i2c_write(208,0,1,"\x3c")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(1))

    call hardware_i2c_write(208,0,1,"\x3d")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(2))

    call hardware_i2c_write(208,0,1,"\x3e")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(3))

    call hardware_i2c_write(208,0,1,"\x3f")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(4))

    call hardware_i2c_write(208,0,1,"\x40")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(5))

    call hardware_i2c_write(208,0,1,"\x43")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(6))

    call hardware_i2c_write(208,0,1,"\x44")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(7))

    call hardware_i2c_write(208,0,1,"\x45")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(8))

    call hardware_i2c_write(208,0,1,"\x46")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(9))

    call hardware_i2c_write(208,0,1,"\x47")(written)
    call hardware_i2c_read(208,0,1)(result,data_len,sensor(10))

    call hardware_i2c_write(208,0,1,"\x48")(written)
    call hardware_i2c_read(208,1,1)(result,data_len,sensor(11))

    # Write acceleration and gyro to GATT
    call attributes_write(xgatt_sensor, 0, 12, sensor(0:12))
end

Hardware.xml

<?xml version="1.0" encoding="UTF-8" ?>

<hardware>
    <sleeposc enable="true" ppm="30" />
    <usart channel="0" alternate="2" baud="19200" endpoint="none" flow="false" />
    <usb enable="false" endpoint="api" />
    <txpower power="15" bias="5" />
    <script enable="true" />
    <port index="1" tristate="0" pull="up" />
</hardware>

Gatt.xml

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>
    <service uuid="1800">
        <description>Generic Access Profile</description>

        <characteristic uuid="2a00">
            <properties read="true" const="true" />
            <value type="utf-8">MPU6050 I2C</value>
        </characteristic>

        <characteristic uuid="2a01">
            <properties read="true" const="true" />
            <value type="hex">4142</value>
        </characteristic>
    </service>

    <service uuid="aaa51666-e7cb-469b-8e4d-2742f1ba7aaa" advertise="true">

        <characteristic uuid="bbbdd780-b042-4876-aae1-112855353bbb" id="xgatt_write">
            <description>Written Data</description>
            <properties write="true" />
            <value variable_length="true" length="20" />
        </characteristic>

        <characteristic uuid="0dddd780-b042-4876-aae1-112855353ddd" id="xgatt_who">
            <description>Who Am I</description>
            <properties read="true" notify="true" />
            <value length="1" />
        </characteristic>

        <characteristic uuid="1dddd780-b042-4876-aae1-112855353ddd" id="xgatt_pwr1">
            <description>Power Config 1</description>
            <properties read="true" notify="true" />
            <value length="1" />
        </characteristic>

        <characteristic uuid="2dddd780-b042-4876-aae1-112855353ddd" id="xgatt_pwr2">
            <description>Power Config 2</description>
            <properties read="true" notify="true" />
            <value length="1" />
        </characteristic>

        <characteristic uuid="adddd780-b042-4876-aae1-112855353ddd" id="xgatt_ratediv">
            <description>RateDivider</description>
            <properties read="true" notify="true" />
            <value length="1" />
        </characteristic>

        <characteristic uuid="acce1000-0000-0000-0000-1234567890ab" id="xgatt_sensor">
            <description>Sensor Raw</description>
            <properties read="true" notify="true" />
            <value length="12" />
        </characteristic>

    </service>

</configuration>

Project.bgproj

<?xml version="1.0" encoding="UTF-8" ?>

<project>
    <gatt in="gatt.xml" />
    <hardware in="hardware.xml" />
    <script in="script.bgs" />
    <usb_main in="cdc.xml" />
    <image out="out.hex" />
</project>

Cdc.xml

<?xml version="1.0" encoding="UTF-8" ?>

<usb>
    <device
        bDeviceClass="2"
        bcdDevice="1"
        iManufacturer="Bluegiga"
        idVendor="2458"
        idProduct="0001"
        iProduct="Low Energy Dongle"
        iSerialNumber="1"
        bcdUSB="200"
        >
        <configuration
            iConfiguration="CDC"
            bmAttributes="80"
            bMaxPower="25">

            <interface
                bInterfaceClass="2"
                bInterfaceSubClass="2"
                bInterfaceProtocol="1"
                iInterface="CDC control"
                >
                <descriptor Type="24">
                    <data>001001</data>
                </descriptor>
                <descriptor Type="24">
                    <data>0202</data>
                </descriptor>
                <descriptor Type="24">
                    <data>060001</data>
                </descriptor>
                <descriptor Type="24">
                    <data>010301</data>
                </descriptor>
                <endpoint bEndpointAddress="82" bmAttributes="3" wMaxPacketSize="40" bInterval="40">
                </endpoint>
            </interface>
            <interface
                bInterfaceClass="A"
                bInterfaceSubClass="0"
                bInterfaceProtocol="0"
                iInterface="CDC data"
                >
                <endpoint bEndpointAddress="84" bmAttributes="2" wMaxPacketSize="40" bInterval="1">
                </endpoint>
                <endpoint bEndpointAddress="4" bmAttributes="2" wMaxPacketSize="40" bInterval="1">
                </endpoint>
            </interface>
        </configuration>
    </device>
</usb>