Temperature Monitoring using VB.Net, Micro-framework and a Netduino
As a bit of a geek, I like to tinker with basic gadgets. I often think of little ideas that would be great to implement but the device involves some hardware and very little software. As a software engineer I tend to think in terms of simple programs to but in order to implement something tangible would involve electronics and the interface between embedded electronics and high level languages such as VB or C# is somewhat disconnected. For a long time much of the embedded devices was coded using low level languages such as assembler and the prospect of going back to write assembler or getting into more complex electronics is something that I just don’t have the time to devote to it.
That’s when I saw this little device called the Netduino which runs something called the .NET Micro-framework. This seems just what I’ve been looking for. A microcontroller device that I can put together a few electronics items which are available off-the-shelf and write some .NET code in my language of choice (VB) to implement some basic functionality.
So the next question is what did I think would be a good first project?
My wife is studying to be a sommelier which means we have quite a few bottles of wine in the house. Sometimes we open one up an it’s developed a fault. Now there are numerous reasons for wines developing faults but incorrect storage is one possible cause. Wine is very temperamental and is best stored in a cool dark climate that doesn’t change much through the seasons which is why cellars or caves are great for storage but unfortunately in normal modern houses a cave or wine cellar is not a standard item. So we use our pantry to store our wine.
With this in mind I wanted to be able to know if the pantry conditions were really fluctuating that much. So I had my project but where to start? A simple device to monitor the wine cellar (eer pantry!!!) and provide warning indications of hi or low temperature conditions.
Well the first thing was to look around and find the parts I would need – a Netduino Plus device was chosen because it has networking capabilities built in which I would use later. A prototyping shield and a few electronic components such as temperature and humidity sensor and some LED’s to indicate status.
The Netduino hardware is a micro-controller running a very small version cut down version of the .NET framework called the Micro-Framework. This enables .NET developers to write their application code using C# and recently added VB.NET. Allowing you to write you application as high level code dealing with the problem rather than having to resort to using low level assembler to control the hardware. There are some things that a not implemented with Generics / Late Binding being the obvious two but the code is clearly VB.NET / C# and you use Visual Studio as a development tool.
The device is made in such a way that you can plug in shields. Shields are really circuit boards that conform to a specific physical layout. These shields contain all the necessary circuitry to do a specific tasks such as SD Card reader/writer, Wi-Fi networking or can be a blank one which allows you to create your own circuitry either by soldering up components or by using a prototyping breadboard.
So after a little research located the parts I need would need and the basic circuitry to do get the sensors and LED’s connected. It’s easy enough to find this information with vendors such as Sparkfun, MakerShed, Adafruit all having lots of useful information as well as the Netduino website community forums having lots of information on sensors used by people and code snippets.
With all this in hand I constructed a basic shield – very simple for anyone that has done any basic soldering. If you can’t, it’s real easy to learn. With Netduino and prototyping shield in hand I launched Visual Studio to write some code to make my monitoring device come to life.
The applicable needs to do a number of tasks
- · Read the sensors
- · Write the data to the SD Card
- · Detect any temperature warning conditions.
- · Provide visual indications of working and warning conditions
- · Provide a reset capability
My approach was to take each task and build an evolving system. So the first thing was to wire up the sensor and the LED’s. Using a breadboard makes it real easy and is really a case of plugging wires in to make the connections.
The design looked like the following. It looks a bit crazy when prototyping but I’ll eventually clean this up before putting the item in the pantry and having a worried wife come tell me there is a mysterious device with wires coming out of it in the pantry.
(Some of these may be not requiring connections because they are implemented as part of the prototyping shield. Examples of this are my reset button and heartbeat and reset LED’s which only required the Dx output. The ground wires were already implemented as part of the shield.)
3.3v and ARef (if using a RevA board)
Hi Warning LED
Low Warning LED
Now to make it work and write some code.
The micro framework is really a lightweight version optimized for embedded device development. VB support is a recent addition and required the 4.2 version of the micro framework to be installed along with the firmware update to the Netduino. Updating the firmware to 4.2 was a breeze.
So let’s break development into a few simple tasks one at a time. Each task I’ll show code snippets but the entire finished source is available to download.
Reading the sensor
The code for this application is a simple Console application which is running indefinitely. As I’m powering the final device by means of a 9v battery I want to do enough to get the job done but want to make sure that I have sufficient battery life. So I will be taking a reading every 10 minutes which means that most of the time the device is really asleep.
There are both analogue and digital temperature sensors. Mine original sensor is a simple thermistor (Temperature dependent resistor) which means that the resistance varies with temperature changes. Using the Analogue input ports on the Netduino in conjunction with this allows me to determine a temperature. There are two revisions of the Netduino (Rev A and Rev B). The significant difference between the two is related to Analogue references and the need to provide an extra link between the 3.3v pin and the Aref0 pin – without this the values read on the Analogue port will be inconsistent. Luckily almost all boards out there are Rev B boards with this already internally connected on the board preventing you from having to remember this.
The formula for calculating a temperature is as follows….
This code will create a timer which raises an event every x seconds and takes a temperature reading. As there are some minor variations in reading a single instantaneous value, my approach was to take a number of readings over a short period of time and average them out. This should flatten any rogue values that occurred. When debugging this I set a shorter period to ensure that I could see this process working.
I’d also included debug values to be able to monitor the temperature value it was reading. These can be conditional compiled out in final release version. The values seemed a little high on this sensor so I located a second sensor and installed this and measured the temperature reading on this and was able to adjust the first. This may have been possible by adding a resistor in the circuit for the original sensor but I was able to experiment using and offset value in code to calibrate this and it seemed accurate enough.
Imports Microsoft.SPOT Imports Microsoft.SPOT.Hardware Imports SecretLabs.NETMF.Hardware Imports SecretLabs.NETMF.Hardware.Netduino Public Module Module1 Dim PollingDelay As Integer = 2 Dim tempSensor As New AnalogInput(Pins.GPIO_PIN_A5) Dim CurrentTemperature As Double = 0 Public Sub Main() Dim Temperature As Double = 0 '//Setup Event Handlers PollingTemperatureMethod() '//Get the initial temperature CurrentTemperature = GetAverageTemperatureReading() While True Thread.Sleep(Timeout.Infinite) End While End Sub Function GetCurrentTemperature() As Double ''Read the raw sensor value Return tempSensor.Read() End Function 'Get the average temperature of 101 readings over a second Function GetAverageTemperatureReading() As Double Dim totalTemp As Double Dim averagetemp As Double = 0 For i = 0 To 99 totalTemp += GetCurrentTemperature() Thread.Sleep(10) Next averagetemp = totalTemp / 100 Dim Kelvin As Double = (((averagetemp / 1023) * 3.3) * 100) Dim Celsius As Double = (Kelvin - 273) - 17.8 Dim Fahrenheit = (Celsius) * (9 / 5) + 32 Debug.Print(WarningStatus.ToString & " " & averagetemp.ToString() & " " & Fahrenheit.ToString & " / " & Celsius.ToString & " ") Return Celsius End Function Private PollingTimer As Timer Private Sub PollingTemperatureMethod() Dim PollingDelegate As New TimerCallback(AddressOf PollForTemperature) PollingTimer = New Timer(PollingDelegate, Nothing, 1000, PollingDelay * 1000) End Sub Public Sub PollForTemperature(stateInfo As Object) Module1.CurrentTemperature = GetAverageTemperatureReading() End Sub End Module
Writing the SD Card
There are a few differences between the Netduino and the Netduino Plus. I’d chosen the Plus version because it comes with a micro SD card slot on the board – so no new hardware are required to log these values for reviewing later. Searching in the Netduino forums reveals many examples of code available to write to SD Cards. The micro-framework includes some IO file support but is not the file support that you might expect from the full desktop .NET Framework. The task I want to achieve is to append the new sensor readings to the end of a file containing the existing readings and using the full framework I’d simply be using the System.IO.File class but this doesn’t exist on Micro-framework and so I had to create my own helper function which uses a Streamwriter, I guess once I’ve done this once I can reuse it over again.
Added to PollForTemperature Method
Dim Str As String = "SensorData: 1," & Module1.CurrentTemperature.ToString & "2," & Module1.CurrentTemperature2.ToString AppendToFile("\SD\Temperature.dat", Str) #Region "Writing Data to SD Card" Private Function VolumeExist() As Boolean Dim volumes As VolumeInfo() = VolumeInfo.GetVolumes() For Each volumeInfo__1 As VolumeInfo In volumes If volumeInfo__1.Name.Equals("SD") Then Return True End If Next Return False End Function ' Append to a file / Create the file if it doesn't exist Private Sub AppendToFile(filename As String, string1 As String) If Not VolumeExist() Then Return End If Try Dim fs As FileStream = If(File.Exists(filename), New FileStream(filename, FileMode.Append), New FileStream(filename, FileMode.Create)) Dim streamWriter As New StreamWriter(fs) streamWriter.WriteLine(string1.ToString) streamWriter.Flush() streamWriter.Close() fs.Close() Catch generatedExceptionName As Exception End Try End Sub #End Region
Detecting Warning Conditions
I wanted my code to be able to detect hot and cold warning conditions as both of these can cause problems with wine storage. During development I wanted a much narrower temperature range allowing me to trigger it easily but chose to read these settings into the application from a file on the SD Card to make it flexible. Allowing me modify the settings without having to redeploy the application. Once I have these Maximum and Minimum values I can then use these to determine if the sensor reading is a Warning Condition and if this has occurred turn on one of the two warning LED to provide a clear indication that we have had an temperature event occur.
Dim HiWarningLED As New OutputPort(Pins.GPIO_PIN_D9, False) Dim LowWarningLED As New OutputPort(Pins.GPIO_PIN_D11, False) Public Enum LevelCheck LowTemp HiTemp End Enum Dim LowWarningStatus As Boolean = False Dim WarningStatus As Boolean = False Dim MinTemp As Single = 20 Dim MaxTemp As Single = 26 Function CheckTemperatureForWarning(Temp As Double, Optional Level As LevelCheck = LevelCheck.HiTemp) As Boolean Dim WarnStateOccured As Boolean = False If Level = LevelCheck.LowTemp Then 'Is Temperature Exceeding MaxTemp If Temp <= MinTemp Then Debug.Print("Low Warning Triggered") Debug.Print("Temp: " & Temp.ToString) Debug.Print("maxTemp: " & MaxTemp.ToString) Debug.Print("minTemp: " & MinTemp.ToString) WarnStateOccured = True End If ElseIf Level = LevelCheck.HiTemp Then 'Is Temperature Exceeding MaxTemp If Temp >= MaxTemp Then Debug.Print("Hi Warning Triggered") Debug.Print("Temp: " & Temp.ToString) Debug.Print("maxTemp: " & MaxTemp.ToString) Debug.Print("minTemp: " & MinTemp.ToString) WarnStateOccured = True End If End If Return WarnStateOccured End Function Sub WarningStatusLight(State As Boolean, Optional Level As LevelCheck = LevelCheck.HiTemp) 'Code to turn on/off LED If Level = LevelCheck.LowTemp Then LowWarningLED.Write(State) If Level = LevelCheck.HiTemp Then HiWarningLED.Write(State) End Sub Public Sub PollForTemperature(stateInfo As Object) Module1.CurrentTemperature = GetAverageTemperatureReading() 'If the warning status light is off and a warning temperature occurs then 'set Warning Light State to True and don't bother checking any more as the 'alarm condition has been met If LowWarningStatus = False AndAlso CheckTemperatureForWarning(CurrentTemperature, LevelCheck.LowTemp) = True Then LowWarningStatus = True WarningStatusLight(LowWarningStatus, LevelCheck.LowTemp) End If If WarningStatus = False AndAlso CheckTemperatureForWarning(CurrentTemperature, LevelCheck.HiTemp) = True Then WarningStatus = True WarningStatusLight(WarningStatus, LevelCheck.HiTemp) End If End Sub
When a temperature event has occurred I can retrieve the SD Card and determine how high and for how long this event occurred. But I would like a simple way to reset the LED. I could just repower the device which would restart everything back to its original state or a more elegant way is simply to have a Reset Button to turn the LED off – so I’d implemented a solution which would reset the warning indicator LED and provide feedback with a little flashing sequence. One important thing to note is that when the button pressed interrupt occurs it is necessary to re-enable the interrupt. This is something that you don’t normally have to do with other button click events.
Dim button As New InterruptPort(Pins.GPIO_PIN_D8, False, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth) Dim ResetLED As New OutputPort(Pins.GPIO_PIN_D12, False) Sub SetupResetButtonHandler() AddHandler button.OnInterrupt, AddressOf ResetStatusLight button.EnableInterrupt() End Sub Sub ResetStatusLight(ByVal data1 As UInteger, ByVal data2 As UInteger, ByVal time As Date) Debug.Print("Reset Warning Light") If data2 = 0 Then ResetLED.Write(True) Dim LEDFlashState As Boolean = False For i = 0 To 8 ResetLED.Write(LEDFlashState) Thread.Sleep(100) LEDFlashState = Not LEDFlashState Next ResetLED.Write(False) End If button.EnableInterrupt() End Sub
When testing the functionality I realized this device could be sitting in my pantry working away and the temperature could be just fine OR the battery could be flat and I would have no obvious signs to differentiate the two. The location of the shield on top of the device hides a little LED on the main Netduino board. So, I decided that a heartbeat LED flashing every minute would be a good idea on providing visual indication the sensor was alive and working.
Dim HeartBeatLED As New OutputPort(Pins.GPIO_PIN_D13, False) Private watchdogTimer As Timer Private HeartBeatThread As Thread = Nothing Private Sub HeartBeatThreadMethod() Dim HeartBeatDelegate As New TimerCallback(AddressOf PulseLED) watchdogTimer = New Timer(HeartBeatDelegate, Nothing, 1000, HeartBeatDelay * 1000) End Sub Public Sub PulseLED(stateInfo As Object) Debug.Print("HeartBeat...") HeartBeatLED.Write(True) Thread.Sleep(250) HeartBeatLED.Write(False) Thread.Sleep(250) HeartBeatLED.Write(True) Thread.Sleep(250) HeartBeatLED.Write(False) End Sub
With this code in place I can now debug the application. Simply pressing F5 will compile and debug the application. As the device doesn’t have a UI as such, we can use Visual Studio when debugging on a device to monitor values of variables etc. just as we would on a desktop application. This enables me to code step code to trace through the code and find logic issues as well as write additional debug output to the Output window.
The experience is very similar to the desktop – although no edit and continue is permitted. This is ok as the applications as small and you are essentially deploying them completely to the device each time you are debugging your application. The deployment is very quick as the applications are measured in kilobytes. The important thing to check is that you are deploying to the device – Project Properties > .NET Micro-framework Tab -> USB -> Netduino_Plus is selected.
Deploying your application is automatically accomplished by debugging your application. The process involves a couple of additional steps in translating your IL into code which will run on the specific micro-framework device but these details are more to do with the hardware platform vendors SDK hooking into Visual Studio. Although .NET Micro-framework supports hardware emulators for testing – it is simple to debug on the device itself – albeit it takes a few seconds longer to deploy the application to the physical device.
I now have a basic functioning sensor that I can used to retrieve sensor readings from my Wine Cellar which will give me a visual indication of spoilage temperature conditions. I’ve managed to read a build a circuit, read a sensor, use timers to poll for reading, turn on/off LED’s for Visual Indication, Interact with device via a button input and write data to a Micro SD Card. These are basic functions which can be used over and over again in many different applications.
We’ll take this one step further and enable the Wifi support to allow remote reading of the device.
Electronic Components Suppliers
Micro-framework 4.2 Beta