Share via


C#: Manage Analog Sensor With Raspberry Pi 2


Introduction

We continue our path on the development and prototyping with the board RaspberryPi2. In the previous article found at this link, our goal was to control the switching of an LED. In recent days, among other things, and the new release of Windows 10 IoT, namely 10.0.10586.0, which has brought with it several new features that we will see in later articles in this. Our goal now and that as the title, to manage an analog sensor. Unlike a digital, analog sensor it said very briefly, is taken as one example of temperature, provides a constant voltage value in time, according to the temperature value which is detected by the sensor itself. Another example, a potentiometer, a resistance variable type, which according to how we act on it through his knob, will vary its value in ohms and therefore also varies the value of the output voltage. Until version 10.0.10240.0 to which we refer, there and the opportunity to connect analog sensors on the GPIO pin as missing inputs and PWM outputs (which stands for power with modulation), so we have to resort to so-called Converter ADC (Analog to Digital converter), namely an electronic circuit capable of converting an analog signal into a binary value expressed in bytes. 

Hardware required

After this brief introduction, we move to the next step. For the realization of our circuit, we need the following hardware material:

  • Raspberry Pi2 with installed version 10.0.10240.0 with power cable.
  • HDMI cable so as to connect the Raspberry Pi2 to a monitor.
  • Monitor with HDMI input.
  • Ethernet cable.
  • Breadboard, which is the necessary basis for mounting components and electrical wiring.
  • Jumper male-male and male-female.
  • Analog temperature sensor TMP36.
  • MCP3008 ADC converter.

A brief mention on the chip MCP3008.

The datasheet you found at this link, it is an analog / digital converter powered at a voltage of 5 v DC max, 10 bit resolution. The following are the main features.

  • 10-bit resolution
  • ± 1 LSB max DNL
  • ± 1 LSB max INL
  • 4 (MCP3004) or 8 (MCP3008) input channels.
  • Analog inputs programmable as single-ended or pseudo-differential pairs.
  • On-chip sample and hold
  • SPI serial interface (modes 0,0 and 1,1)
  • Single supply operation: 2.7V - 5.5V
  • 200 ksps max. sampling rate at VDD = 5V
  • 75 ksps max. sampling rate at VDD = 2.7V
  • Low power CMOS technology
  • 5 nA typical standby current, 2 uA max.
  • 500 uA max. active current at 5V
  • Industrial temp range: -40 ° C to + 85 ° C • Available in PDIP, SOIC and TSSOP packages.

We can observe that the type of communication with the raspberry occurs through SPI protocol. Here's the pin.

**Figure 1: **The pin MCP3008

Starting from pin 1 to pin 8, we have Channels form CH0 to CH7, where we will connect the output of / Analog sensors. Each channel as we shall see must be configured by following the manufacturer's instructions as per attached datasheet. The pins 16-15 are connected to the positive pins 14-9 to zero volts (or Ground of the raspberry Pi2). The pins on pins 13-12-11-10 on bus SPI0 or SPI1 Raspberry Pi2.

Electrical / electronic circuit

The following is the final circuit of our project, undertaken with Fritzing, excellent software for the creation of patterns electrical / electronic.

**Figure 2: **The electronic circuit

Not to be confused in the electrical connections, see below how to connect the GPIO with the MCP 3008. Observing figure 1, we see that between the pins 1 and 16 we have a sign of recognition, by turning the component by 90 ° counterclockwise, pin 1 will be the one at the bottom left, pin 8 is the one on the bottom right, the top right 9 and 16 in the upper left.

  • PIN 16 MCP3008 ----> 3.3V DC (RED CABLE)
  • PIN 15 MCP3008 ----> 3.3V DC (RED CABLE)
  • PIN 14 MCP3008 ----> GND (BLACK CABLE)
  • PIN 13 MCP3008 ----> SPIO_SCLK (RED CABLE)
  • PIN 12 MCP3008 ----> SPIO_MISO (CABLE GREY)
  • PIN 11 MCP3008 ----> SPIO_MOSI (BLUE WIRE)
  • PIN 10 MCP3008 ----> SPIO_CE0_N DC (GREEN LEAD)
  • PIN 9 MCP3008 ----> GND (BLACK CABLE)
  • PIN 1 MCP3008 ----> PIN SENSOR VOUT TMP36 DC (YELLOW CABLE)

These are connections chip MCP3008 with the card Raspberry Pi2. 

Temperature Sensor TMP36

Figure 3: Pinout Sensor TMP36

It is an analog temperature sensor, which supplies a voltage value of 10mV for degree directly proportional to the temperature value which is detected. It has a reading range that goes from -40 to 125 ° C. Here's how to connect it. Looking at the picture above, we see that it has three pins, the first on the left (1) connect me to the voltage of 3.3 v DC, the center pin (2) as shown on the input CH0 of the MPC3008, the pin right (3) on GND.

  • PIN LEFT TMP36 ----> 3.3V DC (RED CABLE)
  • PIN CENTRAL TMP36 ----> ON PIN 1 MCP3008 (YELLOW CABLE)
  • PIN RIGHT TMP36 ----> GND (BLACK CABLE)

Create the test project with Visual Studio 2015

Once the hardware part, the materials needed for the components and their characteristics, and now time to focus on the software side. We will use Visual Studio 2015. If you have not been carried out and, besides Visual Studio 2015 and Windows 10 (at least the Professional version), you must download and install the SDK for the development of Windows 10 that find this link. After installation finishes, we start Visual Studio 2015, and create using the File, New Project application using the blank template app (Windows Universal) as shown and call the project "AnalogTemperature".

Figure 4: New screen project

Confirm with OK button. A project created to develop within the IoT, we need the necessary libraries. In exploring solutions position the cursor to the "References", right click select "Add Reference” and the subsequent screens to the "Extensions" select Windows IoT extensions for the UWP as visible in the next picture.

Figure 5: Management screen references

In terms of extensions we have everything you need. We now need to create a couple of classes that will serve for the management of the sensor TMP36 and MCP 3008.

Creating Classes MCP3008 and TMP36

In exploring solutions, we place the cursor on the project name, right-click and choose "Insert" and right after "Class" and we call the MCP3008 as a chip. In the same way, we create a class called TMP36. Within the class MCP3008 insert the following code.

​ ​

using System;

using Windows.Devices.Enumeration;

using Windows.Devices.Spi;

using Windows.UI.Popups;

  

namespace AnalogTemperature

{

 ``public class MCP3008

 ``{

 ``SpiDevice _device;

 ``TMP36 _TMP36 = ``new TMP36();

 ``string _CHOICECHANNEL;

  

  

 ``const double _MAXVALUE = 1023.0;

 ``const int _MINVALUE = 0;

 ``const int _RESOLUTIONBITS = 10;

 ``const int _SHIFTBYTE = 8;

  

 ``byte``[] _CH0 = ``new byte``[] {1, 0x80, 0};

 ``byte``[] _CH1 = ``new byte``[] {1, 0x90, 0};

 ``byte``[] _CH2 = ``new byte``[] {1, 0xA0, 0};

 ``byte``[] _CH3 = ``new byte``[] {1, 0xB0, 0};

 ``byte``[] _CH4 = ``new byte``[] {1, 0xC0, 0};

 ``byte``[] _CH5 = ``new byte``[] {1, 0xD0, 0};

 ``byte``[] _CH6 = ``new byte``[] {1, 0xE0, 0};

 ``byte``[] _CH7 = ``new byte``[] {1, 0xF0, 0};

 ``byte``[] _DATARECEIVED =  ``new byte``[] {0, 0, 0};

  

  

  

 ``/// <Param name = "serialcomunication">Define type comunication</ Param>

 ``/// <Param name = "channel">Define number of channel MCP3008</ Param>

 ``/// <Param name = "spicomunication">Define spicomunication channel</ Param>

 ``/// <Param name = "fashions">Define spi mode</ Param>

 ``public async ``void InitializeMCP3008 (SerialComunication serialcomunication,Channel channel,SpiComunication spicomunication, SpiMode mode)

 ``{

 ``var spiconnectionsettings = ``new SpiConnectionSettings((``int``) Spicomunication);

 ``spiconnectionsettings.ClockFrequency = _TMP36.CLOCK_SIGNAL;

 ``spiconnectionsettings.Mode = mode;

  

 ``string spiDevice = SpiDevice.GetDeviceSelector (Spicomunication.ToString ());

 ``var deviceInformation = await DeviceInformation.FindAllAsync (SpiDevice);

  

 ``if``(DeviceInformation! = ``null && DeviceInformation.Count> 0)

 ``{

 ``_device = await SpiDevice.FromIdAsync (DeviceInformation [0] .id, spiconnectionsettings);

 ``_CHOICECHANNEL = Channel.ToString ();

 ``}

  

 ``else

 ``{

 ``var dialog = ``new MessageDialog(``"Device Not Found"``);

 ``await dialog.ShowAsync ();

 ``return``;

 ``}

 ``}

  

 ``public double ReturnResult ()

 ``{

 ``switch (_CHOICECHANNEL)

 ``{

 ``case "CH0"``:

 ``_DEVICE.TransferFullDuplex (_CH0, _DATARECEIVED);

 ``break``;

  

 ``case "CH1"``:

 ``_DEVICE.TransferFullDuplex (_CH1, _DATARECEIVED);

 ``break``;

  

 ``case "CH2"``:

 ``_DEVICE.TransferFullDuplex (_CH2, _DATARECEIVED);

 ``break``;

  

 ``case "CH3"``:

 ``_DEVICE.TransferFullDuplex (_CH3, _DATARECEIVED);

 ``break``;

  

 ``case "CH4"``:

 ``_DEVICE.TransferFullDuplex (_CH4, _DATARECEIVED);

 ``break``;

  

 ``case "CH5"``:

 ``_DEVICE.TransferFullDuplex (_CH5, _DATARECEIVED);

 ``break``;

  

 ``case "CH6"``:

 ``_DEVICE.TransferFullDuplex (_CH6, _DATARECEIVED);

 ``break``;

  

 ``case "CH7"``:

 ``_DEVICE.TransferFullDuplex (_CH7, _DATARECEIVED);

 ``break``;

 ``}

  

 ``var result = ((_DATARECEIVED [1] & 0x03) << _SHIFTBYTE) + _DATARECEIVED [2];

 ``var mVolt = result * (_TMP36.VOLTAGE / _MAXVALUE);

 ``var tempCelsius = mVolt / _RESOLUTIONBITS;

 ``return tempCelsius;

 ``}

 ``}

  

 ``public enum SerialComunication

 ``{

 ``SINGLE_ENDED,

 ``DIFFERENTIAL

 ``}

  

 ``public enum Channel

 ``{

 ``CH0, CH1, CH2, CH3, CH4, CH5, CH6, CH7

 ``}

  

 ``public enum SpiComunication

 ``{

 ``SPI0,

 ``SPI1

 ``}

}

Let's analyze the code above. They were defined variables at the class level. The first is nothing but the base class for testing and managing device connected to / and SPI ports of GPIO.

SpiDevice _device;

Here we define a new object type TMP36 that we will see later.

TMP36 _TMP36 = ``new TMP36();​

The variables that follow, are all features of the integrated circuit MCP3008, starting from _MAXVALUE, which will be the maximum value in a 10-bit resolution as features found in the datasheet. _MINVALUE is the minimum value, _RESOLUTIONBITS and the maximum resolution of the MCP3008, _SHIFTBYTE representing the movement of 8 bits that must be performed once acquired the values that are returned from the DOUT pin of the MCP3008. The variables from _CH0 to _CH7 represent the eight channels available where you can connect an analog component, we will use in this example the _CH0. The variable _CHOICECHANNEL, will serve to store and what channel was used and pass the byte with the correct configuration, we will see it in the method ReturnResult (). Remains _DATARECEIVED, the byte that contains the end of the bit information to be processed and displayed to the user as the detected temperature.

string _CHOICECHANNEL;

const double _MAXVALUE = 1023.0;

const int _MINVALUE = 0;

const int _RESOLUTIONBITS = 10;

const int _SHIFTBYTE = 8;

  

byte``[] _CH0 = ``new byte``[] {1, 0x80, 0};

byte``[] _CH1 = ``new byte``[] {1, 0x90, 0};

byte``[] _CH2 = ``new byte``[] {1, 0xA0, 0};

byte``[] _CH3 = ``new byte``[] {1, 0xB0, 0};

byte``[] _CH4 = ``new byte``[] {1, 0xC0, 0};

byte``[] _CH5 = ``new byte``[] {1, 0xD0, 0};

byte``[] _CH6 = ``new byte``[] {1, 0xE0, 0};

byte``[] _CH7 = ``new byte``[] {1, 0xF0, 0}

byte``[] _DATARECEIVED = ``new byte``[] {0, 0, 0};

The method InitializeMCP3008, requires certain parameters, the first and the type of management data read from the channels of MCP3008, that we can set to "single-ended" or "Differential" as required in the datasheet, the channel on which connect the sensor in our TMP36 case of CH0, which we use on the SPI port GPIO, then the mode of communication on the SPI bus. 

/// <Param name = "serialcomunication">Define type comunication</ Param>

/// <Param name = "channel">Define number of channel MCP3008</ Param>

/// <Param name = "spicomunication">Define spicomunication channel</ Param>

/// <Param name = "fashions">Define spi mode</ Param>

public async ``void InitializeMCP3008(SerialComunication serialcomunication, Channel channel, SpiComunication spicomunication, SpiMode mode)

{

  

 ``//It is defined as an instance of the class SpiConnectionSettings, passing an integer argument that defines which SPI bus is used,

 ``//for we will be 0.

  

 ``var spiconnectionsettings = ``new SpiConnectionSettings((``int``) Spicomunication);

  

 ``//Then we set the clock frequency and mode.We note to set the clock rate we use a class property TMP36.

  

 ``spiconnectionsettings.ClockFrequency = _TMP36.CLOCK_SIGNAL;

 ``spiconnectionsettings.Mode = mode;

  

 ``//Subsequently, with the class and method SpiDevice GetDeviceSelector, we get all the SPI bus on the card Raspberry Pi2.

  

 ``string spiDevice = SpiDevice.GetDeviceSelector(Spicomunication.ToString());

  

 ``//This section with the class DeviceInformation, we retrieve all the necessary information on / the SPI bus.

  

 ``var deviceInformation = await DeviceInformation.FindAllAsync(SpiDevice);

  

 ``//If the parameter deviceInformation not null, and greater than zero, we can define which SPI bus use, open the channel of communication that we have set for us will be how SPI0 said, passing the method FromIdAsync Id bus and configuration.

  

 ``if``(DeviceInformation! = ``null && DeviceInformation.Count > 0)

 ``{

 ``_device = await SpiDevice.FromIdAsync(DeviceInformation[0].id, spiconnectionsettings);

 ``_CHOICECHANNEL = Channel.ToString();

 ``}

  

 ``Else

  

 ``//If there has been no SPI warn the user with a MessageDialog.

  

 ``{

 ``var dialog = ``new MessageDialog(``"Device Not Found"``);

 ``await dialog.ShowAsync();

 ``return``;

 ``}

}

In this method, according to the channel that we decided to use, always it will be call the method TransferFullDuplex, who will send the settings on each channel selected on the SPI bus, and the second parameter byte where you will store the converted data from the signal analog to digital that we then process in a timely manner. Finally we will have a result which will be our final temperature value.

 ``public double ReturnResult ()

 ``{

 ``switch (_CHOICECHANNEL)

 ``{

 ``case "CH0"``:

 ``_DEVICE.TransferFullDuplex (_CH0, _DATARECEIVED);

 ``break``;

  

 ``case "CH1"``:

 ``_DEVICE.TransferFullDuplex (_CH1, _DATARECEIVED);

 ``break``;

  

 ``case "CH2"``:

 ``_DEVICE.TransferFullDuplex (_CH2, _DATARECEIVED);

 ``break``;

  

 ``case "CH3"``:

 ``_DEVICE.TransferFullDuplex (_CH3, _DATARECEIVED);

 ``break``;

  

 ``case "CH4"``:

 ``_DEVICE.TransferFullDuplex (_CH4, _DATARECEIVED);

 ``break``;

  

 ``case "CH5"``:

 ``_DEVICE.TransferFullDuplex (_CH5, _DATARECEIVED);

 ``break``;

  

 ``case "CH6"``:

 ``_DEVICE.TransferFullDuplex (_CH6, _DATARECEIVED);

 ``break``;

  

 ``case "CH7"``:

 ``_DEVICE.TransferFullDuplex (_CH7, _DATARECEIVED);

 ``break``;

 ``}

  

 ``var result = ((_DATARECEIVED [1] & 0x03) << _SHIFTBYTE) + _DATARECEIVED [2];

 ``var mVolt = result * (_TMP36.VOLTAGE / _MAXVALUE);

 ``var tempCelsius = mVolt / _RESOLUTIONBITS;

 ``return tempCelsius;

 ``}

}

_DATARECEIVED, is a byte array, we serve the first two left bits of the second element, while the first, _DATARECEIVED [0] we ignore it because there will be no value. Then we go to add up _DATARECEIVED [1] with _DATARECEIVED [2]. The subsequent calculations do nothing but obtain the value of temperature, _TMP36.VOLTAGE to note, that we will see later. I also added three enumerations that will be used when the MainPage define the code to implement the method InitializeMCP3008, are the type of communication channel used and what bus SPI intend to send and receive data.

public enum SerialComunication

{

 ``SINGLE_ENDED,

 ``DIFFERENTIAL

}

  

public enum Channel

{

 ``CH0, CH1, CH2, CH3, CH4, CH5, CH6, CH7

}

  

public enum SpiComunication

{

 ``SPI0,

 ``SPI1

}

This and the need for the class MCP3008. Now for the class TMP36, after you create it, insert the following code. 

namespace AnalogTemperature

{

 ``public class TMP36

 ``{

 ``const int _CLOCKSIGNAL = 1650000;

 ``const double _VOLTAGE = 2000;

  

  

 ``public int CLOCK_SIGNAL

 ``{

 ``get

 ``{

 ``return _CLOCKSIGNAL;

 ``}

 ``}

  

 ``public double VOLTAGE

 ``{

 ``get

 ``{

 ``return _VOLTAGE;

 ``}

 ``}

 ``}

}

 

Simply, the parameters that are defined are the clock frequency, and the maximum voltage that can dispense the sensor at maximum output, the TMP36 to 125 ° c, delivers a voltage of about 2 vdc.

Creation of the graphical interface and code in MainPage class

Now is time to define the necessary classes, let's see how to implement it in Class MainPage. In exploring solutions, double click with the mouse on MainPage.xaml, entered in the code define our user interface, by entering the following XAML.

<``Page

 ``x:Class``= ``"AnalogTemperature.MainPage"

 ``xmlns``= ``"Http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 ``xmlns:x``= ``"Http://schemas.microsoft.com/winfx/2006/xaml"

 ``xmlns:local``= ``"Using: AnalogTemperature"

 ``xmlns:d``= ``"Http://schemas.microsoft.com/expression/blend/2008"

 ``xmlns:mc``= ``"Http://schemas.openxmlformats.org/markup-compatibility/2006"

 ``mc:Ignorable``= ``"D"``>

  

 ``<``Grid Background``= ``"{ThemeResource ApplicationPageBackgroundThemeBrush} "``> 

 ``<``Grid.RowDefinitions``>

 ``<``RowDefinition Height``= ``"Auto" />

 ``<``RowDefinition Height``= ``"Auto" /> 

 ``</``Grid.RowDefinitions``>

  

 ``<``Grid.ColumnDefinitions``>

 ``<``ColumnDefinition Width``= ``"Auto" />

 ``<``ColumnDefinition Width``= ``"Auto" />

 ``<``ColumnDefinition Width``= ``"Auto" /> 

 ``</``Grid.ColumnDefinitions``>

  

 ``<``TextBlock Grid.Row``= ``"0" Grid.ColumnSpan``= ``"3" x:Name``= ``"TxtHeader" FontSize``= ``"50" Text``= ``"TMP 36 MCP AND SAMPLE 3008" />

 ``<``TextBlock Grid.Column``= ``"0" Grid.Row``= ``"1" x:Name``= ``"TxtReadingTemp" FontSize``= ``"30" Margin``= ``"15,0,0,0" Text``= ``"Temperature value is:" />

 ``<``TextBlock Grid.Column``= ``"1" Grid.Row``= ``"1" x:Name``= ``"TxtReading" FontSize``= ``"30" Margin``= ``"15,0,0,0" />

 ``<``TextBlock Grid.Column``= ``"2" Grid.Row``= ``"1" x:Name``= ``"TxtCelsius" FontSize``= ``"30" Margin``= ``"15,0,0,0" Text``= ``"C"``/> 

 ``</``Grid``>

</``Page``> 

In itself it is very simple, but enough to display the temperature value returned by the method ReturnResult () class MCP3008 that is our goal. With F7 key, enter in the code C #, entering the underside.

using System;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

using Windows.Devices.Spi;

  

// The item template for the blank page is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x410 ;

  

namespace AnalogTemperature

{

 ``/// <Summary>

 ``/// Blank page that can be used independently or explored within a frame.

 ``/// </ Summary>

 ``public sealed partial class MainPage : Page

 ``{

 ``DispatcherTimer _timer = ``new DispatcherTimer();

 ``MCP3008 _mcp3008 = ``new MCP3008();

  

 ``public MainPage ()

 ``{

 ``InitializeComponent ();

 ``_mcp3008.InitializeMCP3008 (SerialComunication.SINGLE_ENDED, Channel.CH0, SpiComunication.SPI0,SpiMode.Mode0);

 ``_timer.Interval = ``new TimeSpan(0, 0, 5);

 ``_timer.Start ();

 ``_timer.Tick + = _timer_Tick;

 ``}

  

 ``private void _timer_Tick (``object sender, ``object is``)

 ``{

 ``txtReading.Text = Math.round (_mcp3008.ReturnResult ()). ToString ();

 ``}

 ``}

}

Analyzing the previous C # code, we initialize a timer.

DispatcherTimer _timer = ``new DispatcherTimer();

We define an object of type MCP3008.

MCP3008 _mcp3008 = ``new MCP3008(); 

We InitializeMCP3008 implement the method, passing the necessary parameters.

_mcp3008.InitializeMCP3008 (SerialComunication.SINGLE_ENDED, Channel.CH0, SpiComunication.SPI0,SpiMode.Mode0); 

We set the timers Interval property with a value of type TimeSpan 5 seconds, we start with the Start () method and finally we manage its Tick event. 

_timer.Interval = ``new TimeSpan(0, 0, 5);

_timer.Start ();

_timer.Tick + = _timer_Tick; 

Inside the event Tick, let's highlight the Text property of the TextBox txtReading, showing the temperature value by calling the ReturnResult (), but before we round the value with the static class Math and Round () method to remove decimal too.

private void _timer_Tick (``object sender, ``object is``)

{

 ``txtReading.Text = Math.round (_mcp3008.ReturnResult ()). ToString ();

}

Test the application

After the piece of code, before running the test application, there are a few things to see. The first being that we are developing on Raspberry Pi2, and to set the ARM compilation mode, you can invoke it from the pull down menu as in the following figure.

**Figure 6: **The main menu of Visual Studio 2015

In reference to the previous image, you will notice that is on the run as "Remote computer". This is because we want to run the application on the card Raspberry Pi2. Select this mode, and go to the next screen to select the device and the IP address of the Raspberry Pi2 clearing authentication or you can change these settings by selecting the project, right-click, select "Properties" and immediately after "Debug", we will be led by the following screen.

Figure 7: Section debugging in the project properties

After this activity, we can run the Debugging. F5 key, and if everything was done correctly, that's what the monitor will display,

**Figure 8: **The sample application running on Windows 10 and Raspberry Pi2 IoT.

Conclusion

In this article we saw an introduction on what are the sensors Analog, and what an ADC, because the card RaspberryPi2 need this component to make use of sensors and instruments with analog signal. In future articles, we'll explore further using other components and see how to adapt different types of sensors and analog instruments on the ADC and Raspberry.