IoT: I2C and Galileo board

I spent last weekend finishing my first drone. It’s nothing special, I bought some standard components like brushless motors with ESCs, a radio receiver and transmitter and flight control board. The last one is compatible with Arduino and supports MultiWii open source project which helps manage drones based on gyroscope and accelerometer sensors. But it didn’t satisfy me because the drone in the existing configuration is just a toy and it doesn’t contain any brain. And, frankly speaking, Arduino board is not a good choice to make some sort of brain due to performance and memory limitations. So, if you want to develop a drone which will be able to fly “without you”, analyze existing environment and execute tasks like intercepting other drones, you need something more powerful. I believe that Raspberry Pi 2 is the best choice but I still don’t have Window 10 image there but I have wrote it before Build, so I decided to use Galileo 2. It’s very powerful and should satisfy my requirements except of tasks related to video (due to lack of GPU). 

If you are going to add some brain to your drone you can follow one of the two ways. In the first one you will keep the existing flight control board and will place “your brain” like proxy between radio receiver and flight control board. In this case, the flight control board will continue to stabilize your drone but will get commands from Galileo and not from receiver, and Galileo can support several modes like autopilot and manual modes. The second way requires to remove your flight control board and implement the same logic in “the brain”. Of course, in this case you need to add some sensors like accelerometer and gyroscope to Galileo and analyze all incoming data from them. Obviously, the second way is more complex and you should not follow it in the most cases. But if you really want to develop a drone which will intercept enemy drones and use different flying techniques you need to implement your own code there. Because I want to have maximum fun I decided to follow the second way.

Of course, before implementing some algorithms I should think if it’s possible to connect all needed things to my Galileo. Since Galileo is Arduino compatible, there are 6 PWM pins which I can use to operate 6 brushless motors that are enough for my hexacopter. Additionally, I need 4 input pins in order to get data. Everything might be good but I need to connect some sensors as well. Frankly speaking, I need to connect many sensors like accelerometer and gyroscope to stabilize my drone, magnetometer, and GPS to navigate the drone to a goal, airspeed sensor for better navigation, distance sensor for better landing, GPRS to get some command and send logs when the drone is out of range etc. Looks like that standard IO pins will not satisfy all my requirements. So, it’s time to look at the board and find two more pins which are marked like SCL and SDA.   

SCL (clock) and SDA (data) are implementation of I2C bus (Inter-Integrated Circuit) which allows to make communications between various integrated circuits using just two wires (SCL and SDA). And what is more important you can use the I2C to connect lots of different circuits in any order because each device has its own address and if you want to send or receive data from particular device you need to use this address, so other devices will ignore your message. That’s why you can find thousands of sensors in the market which support I2C because in Arduino world it’s the best way to manage hundred sensors using one board only.

So, if you are going to build something complex you need to check if selected sensor supports I2C before buying it. I checked all my sensors and found that I have SparkFun weather shield only which supports I2C. So, right now I am going to work with the shield.  

The weather shield might contains several sensors like GPS and wind but all editions contain humidity and pressure sensors and you can work with any of them directly. In this case, the shield is just a board which combines all sensors together and allows to use I2C. So, the best way to start working there is to check datasheet for particular sensor there. Because temperature is something clear for me, I decided to use humidity sensor there for my experiments. You can find the datasheet for this sensor using the following link.

There are the following things in the datasheet which we are going to use in our application:

  • The humidity sensor requires up to 50 ms to make measurement of temperature (page 4). It’s maximum value for 14 bits resolution and we definitely can count on it;

  • The device address is 0x80 including data bit (page 9). I2C devices has 7 bits addresses but eight (zero bit) bit shows direction of command (read or write). It’s not so important for us because Arduino library uses different approach;

  • 0xE3 and 0xF3 are codes for commands to the sensor which will request to measure temperature (page 10). The first one is hold I2C channel while measurement in progress but the second one allows to make other operations at the same time. We are going to use the second one;

  • Request to the humidity sensor returns 3 bytes (page 10): 14 bits of data, 2 bits for status and 8 bits for checksum. The status allows to see if you get temperature or humidity and checksum allows to check the data;

  • You can measure temperature like -46.85 + 175.72*data/65536 (page 14);

So, right now we are ready to write some code. I am not going to check errors from sensors or something like it in order to make the code as simple as possible. The good news there that you should not work with SDA, SCL pins directly. Arduino supports Wire library and it was successfully to Galileo platform as well. Using the library I developed the following code:

 #include "stdafx.h"
#include "arduino.h"
#include "Wire.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
 return RunArduinoSketch();
}
 
void setup()
{
 Wire.begin();
}
 
const byte humAddress = 0x40;
const uint8_t measureTemp = 0xF3;
 
void loop()
{
 Wire.beginTransmission(humAddress);
 Wire.write(measureTemp);
 Wire.endTransmission();
 
 delay(50);
 
 Wire.requestFrom(humAddress, 3);
 
 byte msb, lsb, checksum;
 msb = Wire.read();
 lsb = Wire.read();
 checksum = Wire.read();
 
 unsigned int raw = ((unsigned int)msb << 8) | (unsigned int)lsb;
 
 raw = raw&0xFFFC; 
 float rh = -46.85 + 175.72 * raw / (float)65536;
 
 Log("%f\n", rh);
 delay(1000);
}
 

In order to start working with Wire library you need to include Wire.h and prepare I2C using Wire.begin. Of course, setup function is the best place to do it. Right after you are ready to talk to I2C device you need to call Wire.beginTransmission. And there is a trick because this function requires an address. You should not use 0 bit for direction instead of it you need to fill first 7 bits with the address. So, if we have 0x80 address including direction bit we need to shift 7 last bits right and get 0x40 – this address we should use in all commands. In order to write something to I2C we can use Wire.write and I am sending 0xF3 command in order to ask the sensor to measure temperature. Finally I need to call endTransmittion in order to stop communication and wait for 50 ms to give the sensor some time to make measurement.

In 50 ms we can send the second command and ask data from sensor. Thanks to requestFrom we should not think about begin, end and direction bits – we need to pass the address and number of bytes only. If data is available we can use read to get it byte by byte.

Of course, it’s better to check if data is available and correct but in general this code will work as well and it’s not so bad. The hardest part here is to understand the datasheet. It’s not easy sometimes and you need to read many pages.

I hope that next week I will get my MPU6050 and will show how to use it and combine accelerometer and gyros data using I2C bus.