This is part 2 of a serie of blogs about device communication between Arduino, RaspberryPi etc:
- Part 1: Using I2C to connect two Arduino Nano’s
- Part 2: Using I2C between RPi Master and Arduino Slave
- Part 3: Use Bluetooth between Win 10 UWP and Arduino
- Part 4: Add virtual Arduino ports to your UWP app
- Part 5: Custom Firmata function called by Windows 10 IoT
- Part 6: Better long running custom Firmata functions
- Part 7: Custom servo usage in Firmata
- Part 8: Using NRF24L01+ modules between arduino’s
- Part 9: Cheap Arduino mesh using RF24 radio modules
In my previous post I connected two Arduino’s using the I2C protocol. This was a good test for me to understand the I2C communication.
My initial goal was to connect a Raspberry Pi to one or more Arduino’s. Why?
Plain Arduino’s lack the ability to connect to the Internet. But they are terrific in reading sensors. They can read them in near real-time and convert raw data into something meaningful.
A Raspberry Pi lacks the ability to communicate using analog ports and it is considered slow (having a full OS like Raspbian or Windows 10 means that it just has too much on it’s head). But it is good in communication to the internet (read: Cloud; read: Microsoft Azure) and it has lots of memory.
So it is a very interesting architecture to attach multiple Arduino slave to the same Raspberry Pi master. We create our own gateway. This way the Arduino’s can collect interesting data using low cost sensors and the data is represented in a nice Windows 10 IoT Core dashboard app (The RPi has a HDMI connection after all) or sent the data to a Azure Iot Hub.
In my previous post I showed that it is possible to send a question (‘T’ for temperature and ‘H’ for humidity) to a certain Arduino (a slave address was provided) to receive the sensor data.
This time I have taken the same slave but attached a Raspberry PI as master. There is one catch! I2C communicates at using the same voltage the master is using to function on. So watch out when attaching a 5 volt master to a 3.3 volt slave… In that case you need a I2C Logic Level Converter. But for now we are safe 🙂
public sealed partial class MainPage : Page { public MainPage() { InitializeComponent(); } private async void btnHumidity_Click( object sender, RoutedEventArgs e) { char request = 'H'; var buffer = System.Text.Encoding. ASCII.GetBytes(new char[] { request }); var response = await ReadI2CBus(18, buffer, 2); if (string.IsNullOrEmpty(response.State)) { tbSent.Text = string.Format( "Humidity: {0}.{1}%", response.Response[0], response.Response[1]); } else { tbSent.Text = response.State; } } private async void btnTemperature_Click( object sender, RoutedEventArgs e) { char request = 'T'; var buffer = System.Text.Encoding. ASCII.GetBytes(new char[] { request }); var response = await ReadI2CBus(18, buffer, 2); if (string.IsNullOrEmpty(response.State)) { tbSent.Text = string.Format( "Temperature: {0}.{1}°C", response.Response[0], response.Response[1]); } else { tbSent.Text = response.State; } } private async Task<I2CResponse> ReadI2CBus( int slaveAddress, byte[] buffer, int responseLength) { try { string aqs = I2cDevice.GetDeviceSelector("I2C1"); var dis = await DeviceInformation.FindAllAsync(aqs); if (dis.Count == 0) { return new I2CResponse { State = "No I2C bus found" }; } var settings = new I2cConnectionSettings(slaveAddress); using (var device = await I2cDevice.FromIdAsync(dis[0].Id, settings)) { device.Write(buffer); var responseToFill = new byte[responseLength]; device.Read(responseToFill); return new I2CResponse { Response = responseToFill }; } } catch (Exception ex) { return new I2CResponse { State = ex.Message }; } } private class I2CResponse { public byte[] Response { get; set; } public string State { get; set; } } }
The I2C communication in written into a fairly simple method. This way it is very easy so send a message to a slave and receiving the response.