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.