Embedded BLE112 - How to Do I2C Reads and Writes
I’ve recently started using BlueGiga’s BLE112 module for Bluetooth 4.0 (low energy) communication. Using this module has been a very, very frustrating experience for me, due to the state of the documentation, lack of examples, and trying to wrap my head around their API (as well as dealing with errors in the sample code on the website).In all fairness, this sort of thing tends to happen with newer technologies and products. I’ve seen that the datasheets and reference documentation for the BLE112 are still in a state of flux, so maybe down the road it will be a smoother ride when trying to get a prototype up and running.
However, instead of just whining about it, I thought it would be useful to share my experience in getting the I2C communication going. [I also have a post on getting the UART working here, if that’s your communication method of choice]
Disclaimer: I still have some issues where I can’t read from multiple registers via a single write command - and this is a necessity due to the overhead that BGScript brings (estimated at 1-3ms per line of BGScript - I’m looking for the reference to cite).
BLE112 BGScript code
I’m using the BLE112 to communicate with the MPU6050 over I2C. I won’t get into the details of the MPU6050, but hopefully my comments in the BGScript will be enough to figure it out.
Also, I can’t say that all of my hardware settings are mandatory, but I have it working in this configuration and don’t have the patience to go back and alter it parameter-by-parameter after all the hours I’ve put into this.
<?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>
BGScript -Read from a register
# 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) # Read WHO_AM_I register (0x75) of device at default address # Default address of MPU6050 is 0x68, which is then left shifted # once to make 0xD0(208) as per the BLE I2C documentation 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, data_len, data)
If you want to read from a register, you need to call hardware_i2c_write using the 7-bit device address, left-shifted once (i.e. 0x68 becomes 0xD0, or 208 in decimal) and then write to the device the register address you’re interested in (0x75), without setting the stop flag (0).
The slave device will put the data up on the I2C lines, so to receive it, call hardware_i2c_read with the same device address (208), set the stop flag to 1 (to let the device know that you’re done with it) and read 1 byte of data (which will contain the register bit information). This 1 byte of data is then written to the GATT (which I made beforehand).
BGScript - Write to a register
# 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) # Set power management register (0x6B) to use gyro clock (0x01) call hardware_i2c_write(208,1,2,"\x6b\x01")(written)
The hardware_i2c_write had my number for a while. I couldn’t get it to work regardless of what combination/permutation of reads and writes I did. It’s similar to the read, where you put in the left-shifted device address, set the stop flag, but this time write two bytes of data.
The first byte is the register you want to write to (0x6B) and the second is the data you want to write to it (0x01). Please MAKE SURE you escape the second byte of data with “”… I forgot to do that during testing, which caused me all sorts of pain, and it wasn’t until Jeff Rowberg came to the rescue with a correctly escaped version of that line of code (which worked like a charm!).
BGScript - Read from multiple registers
This is something that I still have not gotten to work correctly. There are some quirks in the response that I don’t understand.
For instance, when I write this:
call hardware_i2c_write(208,0,1,"\x3b")(written) call hardware_i2c_read(208,1,6)(result,data_len,buff(0)) call attributes_write(xgatt_test, 0, data_len, buff(0:data_len))
I would expect to get the values returned to me: A, B, C - but instead, I get: B, C, 0… So, it looks like I’m missing the first value, but I don’t understand why. When I do it with a 14 byte read (which is the correct number of registers), I get a largely zero-padded bunch of nonsense.
I have correctly configured my gatt.xml, I have set up the correct buffers, etc… If anyone can figure this one out, I would really appreciate it. Getting a one line, multiple register read would cut BGScript overhead as much as possible.
I’ve mentioned this issue to Jeff and he used his trusty logic analyzer to take a quick gander at this problem. Here is his reply:
I finally got to test with my MPU-6050 and DKBLE112 and I’m seeing weirdness on the analyzer. It appears the timing is a little irregular (hovers around 20kHz, which is very slow for I2C). I expect the software I2C implementation is to blame here, but I will play around with it a bit more and ask the firmware devs in Finland what they can figure out. I’ve attached a screenshot showing what I see; half the time it communicates, and half the time it doesn’t. I have yet to try it with a bare breakout (not the DKBLE112 with all its extra hardware), in case the extra stuff is having an impact.
Logic analyzer results of the BLE112’s I2C timing problems
I’ve corresponded with a BlueGiga VP about this issue, and he mentioned that the BLE112’s successor (BLE113) will be coming out in Q1 and will support hardware I2C (using the TI CC2541 chip). That will hopefully fix some of the timing issues I’m running into.
Otherwise, this post should get people started with using I2C on the BLE112. I’ve just recently been able to use their C API to get my desktop communicating with the module (basically emulating what BLEGUI does through the BLED112 dongle). That was a chore too, and I discovered a number of buffer errors with the sample code. I’ll probably put up the pertinent code for that too in the next couple of weeks.
I’ve posted a full working example of using the BLE112 I2C interface to talk to the Invensense MPU6050 here.
If you have any other questions or comments about I2C or the Bluetooth module, don’t hesitate to ask! I’ll be updating this page as I discover some more insights about the BLE112’s I2C interface (specifically if I can get multiple register reads).