CodeProject
What an adventure! Azure, Windows 10 IoT Core, the Raspberry Pi, Oxygen, Hydrogen and Teensy working together to measure the current temperature and receive commands via Bluetooth LE or a USB Serial connection! That’s the power of trail & error, creativity and an awesome community around cloud and IoT based topics!
Build a Windows 10 IoT Core compatible USB Serial Device
After reading this post on MSDN-Forums: How to enable a USB-to-Serial converter on Raspberry PI 2 I wanted to try to connect the RPI 2 running Windows 10 IoT Core to a USB2Serial adapter. Because I had no adapter, I checked the boards that I already had and found the Teensy 3.1. It is a USB-based micro-controller development system. The USB connection on the Teensy can basically be any kind of USB device – and of course it can be a USB2Serial device as well. I knew from my tests with the ESP8266 (a very cheap & cool Wifi-Module) that there was an article available that helped me to connect directly with the ESP8266 using the Teensy and a very small but handy Arduino sketch: A Guide To Using ESP8266 With TEENSY 3 And here is the C-Sketch written by a user named Wozzy:
long LED_14_TimeOn=0;
long LED_15_TimeOn=0;
void setup() {
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
Serial.begin(115200);
Serial1.begin(115200);
}
void loop() {
if ( Serial1.available() ) {
digitalWriteFast(14, HIGH);
LED_14_TimeOn = millis();
Serial.write( Serial1.read() );
}
if ( Serial.available() ) {
digitalWriteFast(15, HIGH);
LED_15_TimeOn = millis();
Serial1.write( Serial.read() );
}
if (millis() - LED_14_TimeOn > 20) {
digitalWriteFast(14, LOW);
}
if (millis() - LED_15_TimeOn > 20) {
digitalWriteFast(15, LOW);
}
}
The code explains itself very well and it should not really be a problem to understand what it is all about. Basically it moves bytes from the serial connections on the Teensy back and forth between the other connected device (PC, or RPI2) and device that is connected to the Teensy, for example an ESP8266 Wifi-Module.
After uploading the sketch to the Teensy, I connected the Teensy to the RPI2 to check if the device would be recognized:
The above screen shows the Devices node on your RPI2 when Windows 10 IoT Core was installed. You just have to browse to http://[YOUR RPI2 HOSTNAME]:8080, enter the username and password and click on the Devices node. So far so good. The Teensy was successfully recognized as USB Serial Device. Now it was time to actually be able to access the device from code and to transfer data back and forth. That way I would be able to connect other devices like temperature sensors that require analog pins (RPI does not have analog pins) or other devices like bluetooth modules that support the serial protocol.
How to use the USB Serial Device from C# and Visual Studio
To actually accomplish this mission, I looked up some Windows Universal sample on GitHub: Custom Serial Device Example. It compiled fine for ARM (The RPI2 has ARM based processors) and I was able to run it on the RPI2. It worked for writing data to the Teensy and the discovery of the Teensy, but I was not able to read data from it, which was absolutely necessary to be able to read data from other connected modules. The settings in the manifest file (which really differ from the official documentation here on MSDN) worked out of the box as well, so I could use the settings from the sample, otherwise it would not be possible to connect to the Teensy:
<Capabilities>
<DeviceCapability Name="serialcommunication">
<Device Id="any">
<Function Type="name:serialPort" />
</Device>
</DeviceCapability>
</Capabilities>
</Package>
Reading data from the Teensy (USB Serial Device) was actually accomplished by some trail & error testing:
private async Task ReadSerialPort()
{
var device = SerialDevice.GetDeviceSelectorFromUsbVidPid(0x16C0, 0x0483);
var teensyDevices = await DeviceInformation.FindAllAsync(device);
var currentDevice = teensyDevices.FirstOrDefault();
SerialDevice realDevice;
try
{
try
{
realDevice = await SerialDevice.FromIdAsync(currentDevice.Id);
}
catch (Exception ex)
{
throw;
}
StringBuilder commandBuilder = new StringBuilder();
while (true)
{
var rbuffer = (new byte[1]).AsBuffer();
await realDevice.InputStream.ReadAsync(rbuffer, 1, InputStreamOptions.Partial);
if ((char)rbuffer.ToArray()[0] != '\n')
{
commandBuilder.Append((char)rbuffer.ToArray()[0]);
}
else
{
string temp = "";
try
{
temp = Math.Round(double.Parse(commandBuilder.ToString().Split(':')[1]), 2).ToString();
}
catch (Exception)
{
temp = "ERROR";
}
await WriteTemperatureToAzureTable("Reutlingen", temp);
commandBuilder.Clear();
}
}
}
catch (Exception ex)
{
throw;
}
}
The key was to read only one single byte from the stream at once that is connected to the serial device. Everything else did not work for me. As this API’s are under heavy changes, it could happen that my method will not work anymore with the next release of Windows 10 IoT core. If you don’t know how to actually access and enumerate devices on the UWP platform take a look at the first few lines of the above method. The two hex-values represent (from left to right) the vendor-id and the product-id. You can find this information’s by browsing your RPI2 on port 8080 like you have seen before (the first screenshot in this blog-post). Or you can use the devcon.exe utility (on your RPI2) when you are connected to via SSH or PowerShell. Please see the resources at the end of this post, you will find all the necessary information’s there. Within the source you will find also a small commented snippet that demonstrates how to write to the USB Serial Device using the headless template from Visual Studio:
This worked for me. The sample code did not, when I used the headless template. The headless template represents a special kind of UWP app, that only runs on a background task. You can compare it with an console application for Windows. At the end of the blog-post you will find all the necessary information where to download this template. It needs to be installed separately. And you need Visual Studio 2015 RC.
Putting it all together
The basic idea was a simple communication between a temperature sensor and the RPI2. But because the RPI2 has no analog pins, I chose the Oxygen board, which is an excellent little (very small form-factor, 33.02x 20.32mm) .NET Micro-Framework board. It is capable to communicate on its own with any kind of network-resources and Microsoft Azure. But it also has analog pins:
At first I wanted to connect a DHT11 temperature sensor, but to keep it simple, I ended up with a TMP36 sensor that I got from the Arduino Starter Kit that I bought for my daughter. She agreed and so I used it. The pins labeled AN0-ANx are the analog pins. I connected the TMP32 and downloaded some additional libraries (you will find everything at the end of the blog-post), created a new .NET Micro Framework 4.3 project on another machine and wrote the code to read the temperature data, that is directly sent to the first serial port on the Oxygen. To communicate (for a later blog-post) with the tiny BLE113 module (Bluegiga chip, very powerful and simple to program) I connected the Hydrogen module (called Molecule in IngenuityMicro language) that is the carrier for the BLE113. Here is how it looks like on the breadboard:
This coniguration allows me to connect any sensor or third party board to the RPI2 running Windows 10 IoT Core. It’s more than a workaround. I would call it an extension. Here is the code that drives the Oxygen board:
using System;
using Microsoft.SPOT;
using IngenuityMicro.Molecule;
using Oxygen;
using System.Threading;
using Toolbox.NETMF.Hardware;
using static Microsoft.SPOT.Hardware.Cpu;
namespace OxygenBLECommander
{
public class Program
{
private static Hydrogen _ble;
private static SimpleSerial _serial;
static Timer _tempTimer;
static AnalogChannel _tempInput;
static double _temperature;
static Tmp36 _tempSensor;
public static void Main()
{
_tempInput = AnalogChannel.ANALOG_0;
_tempSensor = new Tmp36(_tempInput);
_tempTimer = new Timer(new TimerCallback(ClockTimer_Tick), null, 100, 5000);
Oxygen.Hardware.EnableRfPower();
_serial = new SimpleSerial("COM1", 115200);
_serial.Open();
_ble = new Hydrogen();
_ble.DataReceived += _ble_DataReceived;
Thread.Sleep(Timeout.Infinite);
}
static void ClockTimer_Tick(object sender)
{
_temperature = _tempSensor.Temperature / 10;
_serial.Write("TMP:"+_temperature.ToString()+"\n");
}
private static void _ble_DataReceived(string val)
{
if (val != "")
{
_serial.Write("BLE:" + val + "\n");
Oxygen.Hardware.BlinkUserLed();
}
}
}
}
Nice small little piece of code that initializes the RF-Power (needed to enable the Hydrogen), initializes a driver class for the TMP36 sensor and opens the serial port for communications to the Teensy (could be any other serial protocol capable device). A timer is initialized that calls the ClockTimer_Tick-Method every five seconds to push the temperature via the serial-port out to the Teensy. This data is then read on the RPI2 and sent out to Azure Storage (save into an Azure Storage Table).
Finally doing something useful – pushing the temperature data to Azure
The last challange (like in most of the time, when you have to handle embedded stuff), was to check if the current release of the Azure Storage libraries will work on Windows 10 IoT Core. Well, the libraries actually work, with exactly this configuration of NuGet packages (this is valid only for headless apps):
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="win81" userInstalled="true" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="win81" userInstalled="true" />
<package id="Microsoft.Data.Edm" version="5.6.5-beta" targetFramework="win81" userInstalled="true" />
<package id="Microsoft.Data.OData" version="5.6.5-beta" targetFramework="win81" userInstalled="true" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="win81" userInstalled="true" />
<package id="System.Numerics.Vectors" version="4.1.0-beta-23019" targetFramework="win81" userInstalled="true" />
<package id="System.Spatial" version="5.6.5-beta" targetFramework="win81" userInstalled="true" />
<package id="WindowsAzure.Storage" version="4.4.1-preview" targetFramework="win81" userInstalled="true" />
</packages>
If you install the above packages, you should have no problems accessing Azure Storage in exactly the same way you would with pure .NET. Here is the code that sends the temperature data received sent from the Oxygen board, to the Teensy and from there to the RPI2:
private async Task WriteTemperatureToAzureTable(string location, string temperatureValue)
{
if (_account == null)
{
var credentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(
"[YOUR STORAGE ACCOUNT NAME HERE]", "[YOUR STORAGE KEY HERE]");
_account = new CloudStorageAccount(credentials, true);
}
if (_tableClient == null)
{
_tableClient = _account.CreateCloudTableClient();
}
var tableReference = _tableClient.GetTableReference("temperature");
await tableReference.CreateIfNotExistsAsync();
var tableData = new TemperatureTable("Reutlingen", DateTime.Now.ToFileTimeUtc().ToString());
tableData.Location = location;
tableData.Temperature = temperatureValue;
try
{
TableOperation insertOperation = TableOperation.Insert(tableData);
await tableReference.ExecuteAsync(insertOperation);
}
catch (Exception ex)
{
throw;
}
Nothing fancy to see here. It just works. First credentials are needed to authorize against Azure Storage. Then to write,read and create Tables a TableClient is needed. To create the table a TableReference is instantiated and then data is written to the table using a class that inherits from the TableEntity class. The location and the current temperature value are assigned to properties of the TableEntity before the current entity is actually written to the Azure Table. And it works really well.
A short demo video
Here is the actual proof that it really works, a short video demonstrating the solution:
<iframe width="560" height="315" allowfullscreen="allowfullscreen" frameborder="0" src="https://www.youtube.com/embed/grahz027Ma8"></iframe>
Useful Resources
Microsoft Azure
Free Azure Trail – Sing up and deploy your first solution in under 5 Minutes
Azure Storage Documentation
Board Vendors
Oxygen – InguenityMicro
Teensy
Raspberry PI (Click on Shop on the right hand-side)
Used Libraries
Oxygen SDK and Molecule Libraries
.NET Micro Framework Toolbox (driver for the TMP36 sensor and lot’s more of very useful stuff
Windows 10 IoT Core
Windows 10 IoT Core Command Line Utils
Raspberry PI 2 Pin Mappings
Headed and Headless mode
Code Samples (C#, Python and Node.js)
Visual Studio
Visual Studio 2015 RC Community Edition Download (or any other preferred version)
Windows IoT Core Project Templates extension for Visual Studio 2015 RC
.NET Micro Framework
.NET Micro Framework Homepage
Demo Source
AzureIoTDemo Source
OxygenBLECommanderSource