Ruminations of J.net idle rants and ramblings of a code monkey

Hooking up the Weather Shield to the Netduino

Continuing from yesterday’s post, it’s time to get started hooking the Netduino to the Weather Shield. First … the soldering. Yes, that’s right … soldering. I had to solder the header pins to the weather shield. Once the headers are soldered on, the shield plugs right in to the Netduino. Another note … you’ll need an additional power supply, beyond the USB connection, to power the Netduino. While the Arduino didn’t need it to get working, the Netduino, with its more powerful processor, additional RAM and built-in WiFi, didn’t get enough power through the USB port to drive the weather shield, something that caused me a day or so of frustration. I used a power adapter for an old USB hard drive supplying 12V @ 1.5 amps. (The Netduino will take anywhere from 7.5-12 volts for the power supply.) Here’s what it looks like assembled and connected: You may note that the headers are different heights. That’s because initially, I initially soldered on a set of Arduino headers rather than Arduino R3 headers. Yes, there is a difference and it’s a pretty important one … the R3 headers have additional pins for additional IO, including the I2C interface. The shorter headers are the ones that were replaced – be desoldering the original headers and soldering the new ones back on.   Analog Inputs So … let’s get started. First, we’ll need the sample code from SparkFun for the Arduino … we’ll use that as a base and translate it into working C# code. And, since we know that it works, we can also use it to validate the readings that we get on the Netduino by comparing them to the values provided by the Arduino. I started with the analog sensors – reference voltage, battery and light sensor. Looking at the code in the Arduino sketch gives is a good idea of what we need to do. //Returns the voltage of the light sensor based on the 3.3V rail//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)float get_light_level(){  float operatingVoltage = analogRead(REFERENCE_3V3);  float lightSensor = analogRead(LIGHT);    operatingVoltage = 3.3 / operatingVoltage; //The reference voltage is 3.3V    lightSensor = operatingVoltage * lightSensor;    return(lightSensor);}//Returns the voltage of the raw pin based on the 3.3V rail//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)//Battery level is connected to the RAW pin on Arduino and is fed through two 5% resistors://3.9K on the high side (R1), and 1K on the low side (R2)float get_battery_level(){  float operatingVoltage = analogRead(REFERENCE_3V3);  float rawVoltage = analogRead(BATT);    operatingVoltage = 3.30 / operatingVoltage; //The reference voltage is 3.3V    rawVoltage = operatingVoltage * rawVoltage; //Convert the 0 to 1023 int to actual voltage on BATT pin    rawVoltage *= 4.90; //(3.9k+1k)/1k - multiple BATT voltage by the voltage divider to get actual system voltage    return(rawVoltage);} This gives us a value that’s adjusted for the current operating voltage on the board and appears to provide a value from 0 to 1.0. Digging around a bit, we find that the REFERENCE_3V3 pin is connected to Analog 3 and the LIGHT is Analog 1. The reference voltage should be a value between 0 and 3.3 but the analog pin will, by default, provide value between 0 and 4095 (12 bit resolution). Conveniently, however, we can set the analog input to return any range we like so we’ll set the analog channel for the voltage to have a scale of 3.3 (the max value). We can then simply call Read() to get the interpreted value and ReadRaw() to get the underlying value. A note: when declaring the analog channels, you need to use the values from the SecretLabs.NETMF.Hardware.Netduino.AnalogChannels class. Using the values from the Microsoft.SPOT.Hardware.Cpu.AnalogChannel won’t work. #region Analog Inputprivate AnalogInput _light = new AnalogInput(AnalogChannels.ANALOG_PIN_A1);//Reference voltage is max 3.3 so we'll set that as the scaleprivate AnalogInput _refVoltage = new AnalogInput(AnalogChannels.ANALOG_PIN_A3, 3.3, 0, 12);private AnalogInput _battLevel = new AnalogInput(AnalogChannels.ANALOG_PIN_A2);public double GetBatteryLevel(){    CheckDisposed();    double opVoltage = _refVoltage.ReadRaw();    double rawVoltage = _battLevel.ReadRaw();    opVoltage = 3.3/opVoltage;    rawVoltage = opVoltage*rawVoltage;    rawVoltage *= 4.9;    return rawVoltage;}public double GetLightLevel(){    CheckDisposed();    var opVoltage = _refVoltage.Read();       var lightSensor = _light.Read();    lightSensor = lightSensor*(3.3/opVoltage);    return lightSensor;}#endregion Reading the reference voltage is, I found, quite important … it’ll let you know if you can actually read from the rest of the sensors on the board. The humidity sensor requires a minimum of 1.5V and the barometric pressure sensor requires 1.95V. Without the additional power adapter, the reference 3.3 voltage drops to a mere 0.03223V! With the Arduino connected via USB, the operating voltage is 2.1839V … low but still enough to power the sensors. Humidity Sensor – HTU21D Next we’ll tackle the humidity sensor. Actually, it reads both humidity and temperature and uses the I2C interface. The Netduino, Arduino, and RasPi all support I2C and it is a pretty common interface for devices. It’s also supported in both Win10 IoT as well as the .NET Micro Framework. (More detail on the .Net MF implementation is here.) For Arduino, it uses the Wire library so that’s the code we’ll need to translate and the sample sketch that we have using a library to connect to the HTU21D … so we need to dig into it’s source code. Here’s the code for reading the humidity value: //Request a humidity readingWire.beginTransmission(HTDU21D_ADDRESS);Wire.write(TRIGGER_HUMD_MEASURE_NOHOLD); //Measure humidity with no bus holdingWire.endTransmission();//Hang out while measurement is taken. 50mS max, page 4 of datasheet.delay(55);//Comes back in three bytes, data(MSB) / data(LSB) / ChecksumWire.requestFrom(HTDU21D_ADDRESS, 3);//Wait for data to become availableint counter = 0;while(Wire.available() < 3){    counter++;    delay(1);    if(counter > 100) return 998; //Error out}byte msb, lsb, checksum;msb = Wire.read();lsb = Wire.read();checksum = Wire.read(); So … here’s how it works. The master (the ‘duino) begins the transmission by taking control of the I2C bus. It then writes the command to the device, hangs out and then reads the individual bytes. I’ve not included the bit-shifting code because that’s relatively trivial to translate to C# … that’ll come later. We need the bits to shift first. The operations between beginTransmission and endTransmission() happen in a single I2CTransaction. This operation … a write followed, after a pause by a read operation, is an extremely common pattern. Also, I found that the I2CDevice class in .NET MF follows the Highlander Pattern … there can be only one but it’s not implemented as a Singleton; instead you get an InvalidOperationException when you create the second one. This does present some challenges if you are trying to design a nice, well-factored, object-oriented interface where you have more than one I2C device. In Windows 10 IoT edition, this isn’t a problem; you have a shared mode available that works quite nicely. Windows 10 IoT also has a WriteRead method that’s used by the RasPi Weather Station. But as there’s no problem that can’t be solved without an additional layer of abstraction, I set about creating a class to handle I2C devices that share the I2CDevice class, is thread-safe and is easy to use. It’s an abstract base class; you inherit from it for the device and it then provides a bunch of handy methods for reading and writing while abstracting the singleton I2CDevice and ensuring that access to the bus is thread-safe. I based it on the I2CBus referenced on the Netduino forums which worked … but wasn’t quite as abstracted and simplified as I would have liked. So … back to our issue here. To read the humidity, we need to issue an I2CWriteTransaction followed by an I2CReadTransaction. Here’s the method in the base class: /// <summary>/// Performs a write followed by a delayed read./// </summary>/// <param name="writeBuffer">Buffer to write.</param>/// <param name="readBuffer">Buffer to read.</param>/// <param name="transactionTimeout">Timeout for each transaction.</param>/// <param name="delayMilliseconds">Delay between write and read.</param>/// <returns>Number of bytes read.</returns>/// <remarks>If the write fails, no read is attempted.</remarks>public int WriteRead(byte[] writeBuffer, byte[] readBuffer, int transactionTimeout, int delayMilliseconds){    lock (LockObject)    {        var writeResult = Write(writeBuffer, transactionTimeout);        if (writeResult == writeBuffer.Length)        {            if (delayMilliseconds > 0)            {                Thread.Sleep(delayMilliseconds);            }            return Read(readBuffer, transactionTimeout);        }    }    return 0;} Digging in a bit further, the underlying code does the writes and reads here: /// <summary>/// Writes to the specified I2C device./// </summary>/// <param name="configuration">Configuration for the device.</param>/// <param name="writeBuffer">Buffer of bytes to write.</param>/// <param name="transactionTimeout">Transaction timeout</param>/// <returns>Number of bytes transferred in the transaction.</returns>protected static int WriteToDevice(I2CDevice.Configuration configuration, byte[] writeBuffer,    int transactionTimeout){    lock (LockObject)    {        // create an i2c write transaction to be sent to the device.        I2CDevice.I2CTransaction[] writeXAction = { I2CDevice.CreateWriteTransaction(writeBuffer) };        // the i2c data is sent here to the device.        return Execute(configuration, writeXAction, transactionTimeout);    }}/// <summary>/// Reads from the specified I2C device/// </summary>/// <param name="configuration">Configuration for the device.</param>/// <param name="readBuffer">Buffer of bytes to read.</param>/// <param name="transactionTimeout">Transaction timeout</param>/// <returns>Number of bytes transferred in the transaction.</returns>protected static int ReadFromDevice(I2CDevice.Configuration configuration, byte[] readBuffer,    int transactionTimeout){    lock (LockObject)    {        // create an i2c read transaction to be sent to the device.        I2CDevice.I2CTransaction[] readXAction = { I2CDevice.CreateReadTransaction(readBuffer) };        // the i2c data is received here from the device.        return Execute(configuration, readXAction, transactionTimeout);    }} Once we get the values back – an array of 3 bytes – we need to do some bit-flipping to get the actual value. From the Arduino sketch: unsigned int rawHumidity = ((unsigned int) msb << 8) | (unsigned int) lsb;if(check_crc(rawHumidity, checksum) != 0) return(999); //Error out//sensorStatus = rawHumidity & 0x0003; //Grab only the right two bitsrawHumidity &= 0xFFFC; //Zero out the status bits but keep them in place//Given the raw humidity data, calculate the actual relative humidityfloat tempRH = rawHumidity / (float)65536; //2^16 = 65536float rh = -6 + (125 * tempRH); //From page 14return(rh); The interpretation of the bits is described in the data sheet for the sensor; this is really just how it is. Fortunately, this translates very well into C#. Putting the pieces together, it looks like this: uint rawHumidity = ((uint)readHum[0] << 8) | (uint)readHum[1] & 0xFC;rawHumidity &= 0xFFFC; //Zero out the status bits but keep them in place //Given the raw humidity data, calculate the actual relative humidity double rh = (((125.0 * rawHumidity) / 65536) - 6.0);return (rh); If, for some reason, the read fails (like … there isn’t enough power being supplied!), this method returns double.NaN, indicating that we don’t have a good reading. Also, I’m using a somewhat different command than in the Arduino sketch … one that is more like the command used in the RasPi IoT demo. It issues the command to read the value and hold until the value is ready, allowing for an immediate read as soon as the write returns. Right now, I’m not doing the CRC check; that’s something that I may get to in the coming days. That said, this was up and working pretty quickly and I was feeling good as I continued on to the next piece. Pressure Sensor – MPL3115A2 I said I was feeling good … it didn’t last long. As I started implementing the code to integrate the pressure sensor, things went downhill in a hurry. In short, I couldn’t get any values read. Let’s look at the Arduino code first. // Read pressure registersWire.beginTransmission(MPL3115A2_ADDRESS);Wire.write(OUT_P_MSB);  // Address of data to getWire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes//Wait for data to become availablecounter = 0;while(Wire.available() < 3){    if(counter++ > 100) return(-999); //Error out    delay(1);}byte msb, csb, lsb;msb = Wire.read();csb = Wire.read();lsb = Wire.read(); Note the “false” when calling “endTransmission” after the write. As the comments note, this is for a repeated start … essentially, the stop bit isn’t set when the transmission to the device completes. This is a feature in the I2C interface but apparently isn’t widely used. Using the same pattern that I used for the humidity sensor didn’t work. Searching around the internet didn’t yield a whole lot of information either … highlighting one of the challenges with the Netduino and .NET MF in general – there’s not a lot of information out there for it. Heading over to the Netduino forums, I found a couple of posts about the issue but no real resolution. In fact, the suggested resolution doesn’t work – not to mention the fact that it twiddles around with non-public members – and dates back to 2011 and several versions of the .NET MF back. (Another note: don’t expect to jump into the Netduino forums and get help immediately … they seem to take their time “approving” new members for posts. I had to wait 3 days before getting approval to post, even with an answer to a question.) The first thing that I found was that issuing a read command – regardless of what I wrote to the device – started reading at byte 0 of the device’s registers. So … I could make that work by reading the first 6 bytes and taking it from there - address 0x00 is settings, address 0x01 – 0x03 is pressure and 0x04-0x05 is temperature. But that was just wrong. Sure, it worked but it wasn’t correct. It then, somehow, occurred to me to revisit a piece of functionality on the .NET MF’s I2C implementation that didn’t work for the humidity sensor but might work here … the functionality to send an array of I2C transactions to the Execute method. Essentially, send the read and write request together in a single I2C transaction. And, sure enough, it worked and worked well, worked repeatedly and did exactly what I wanted it to do. After another day or two wasted arguing with the device and sensors, I was now able to actually get all of the sensors working! The method in the I2C base class is below: /// <summary>/// Performs a write immediately followed by a read./// </summary>/// <param name="writeBuffer">Buffer to write.</param>/// <param name="readBuffer">Buffer to read.</param>/// <param name="transactionTimeout">Timeout for each transaction.</param>/// <param name="useRepeatableRead">True to use repeatable read mode.</param>/// <returns>Number of bytes read.</returns>/// <remarks>If the write fails, no read is attempted.</remarks>public int WriteRead(byte[] writeBuffer, byte[] readBuffer, int transactionTimeout, bool useRepeatableRead){    if (useRepeatableRead)    {        return Execute(new I2CDevice.I2CTransaction[]         {            I2CDevice.CreateWriteTransaction(writeBuffer),            I2CDevice.CreateReadTransaction(readBuffer)        },        transactionTimeout);    }    return WriteRead(writeBuffer, readBuffer, transactionTimeout, 0);} I also discovered that, unlike with the other device, the repeatable read mode allowed me to get all of the current readings in one operation, rather than in two. Going by the data sheet, the last readings for both pressure and temperature are in 5 bytes starting at register address 0x01 … so requesting a read of 5 bytes at address 0x01 would read both pressure and temperature in one shot. private Readings ReadAll(){    //Set to pressure mode.    byte[] reg_data = new byte[1];    int result = ReadRegister(CTRL_REG1, reg_data, true);    reg_data[0] &= 0xFE;  // ensure SBYB (bit 0) is set to STANDBY    reg_data[0] |= 0x02;  // ensure OST (bit 1) is set to initiate measurement    result = WriteRegister(CTRL_REG1, reg_data[0]);    Thread.Sleep(10);    byte[] raw_data = new byte[5];    uint pressure = 0;    result = ReadRegister(OUT_P_MSB, raw_data, true);    if (result >= 5)    {        return new Readings()        {            Pressure = PressureDataToValue(raw_data, 0),            TempC = TempDataToValue(raw_data, 3)        };    }    return new Readings()    {        Pressure = double.NaN,        TempC = double.NaN    };} If you dig around in the datasheet for this sensor, you’ll find that it actually does quite a bit. It has an altimeter mode (which is really just a conversion of the pressure to an estimated altitude) as well as functionality to trigger interrupts when target temperatures or pressures/altitudes are met. However, only the pressure interrupt line is hooked up on the weather shield. There are also options to set the sample rate, number of samples used to produce a reading (oversampling), logging/FIFO mode, and tweaking the sensor resolution. I’ve not included all of this into the code at this time. I may … I may not. Wrapping it up and putting it together Now that we are getting all of the relevant readings that we want from the device, it’s time to put it together and get things ready for integration with other components. One note – developing these apps is a bit different from how we typically develop Windows applications. Instead, it’s more like developing a service; it runs on startup and keeps running throughout the lifetime of the device. You are, as a matter of fact, not developing a service at all but firmware … the software that drives hardware. Cool, huh? The main function will be one big loop that reads the settings and, at this point in time, writes to the debug port. We’ll also toss in some blinking LEDs so we can look at the device and see that’s it’s working (or not, as the case may be). First, we’ll need to enable the WiFi adapter on the Netduino and make sure that it has an IP address. You set the WiFi settings using the .NET Micro Framework Deploy Tools (installs with the .NET MF SDK) but it won’t actually connect to the wireless network until you access the network from the device. We’ll need to make sure that the network is available and that we have an IP address … this will also get the WiFi connection going. if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable()){    Debug.Print("Waiting for network ...");    Thread.Sleep(50);    continue;}Debug.Print("Checking IP ... ");if (_wireless80211.IPAddress == "0.0.0.0"){    Debug.Print("Waiting for IP Address ...");    Thread.Sleep(50);    continue;   }Debug.Print("IP OK ..."); Once we have a network connection, we need to set the system time. The Netduino (and the Arduino as well) doesn’t not have a Real-Time Clock (RTC) built in so it doesn’t remember the time or date between restarts. So we’ll get the time from an NTP server. Since we’ll eventually be integrated with StreamInsight and/or the Event Hub in the cloud, we won’t worry about time zone; instead, we’ll set the system time to reflect UTC. Fortunately, I was able to grab this from the .NET Micro Framework Toolkit on CodePlex but the binary didn’t work – it was compiled against an earlier version of the .NET Micro Framework – but I was able to just include the source. In the sample project, it’s pointing to an internal NTP service on my network – NIST will cut you off if you make too many requests in a short time period – so this’ll need to be changed. I’ll also keep track of the last time that the time was set and set it every day, once a day. if (DateTime.Now - _lastTimeSet > OneDayTimeSpan){    Debug.Print("Setting Time");    var networkTime = NtpClient.GetNetworkTime("192.168.1.10");    Utility.SetLocalTime(networkTime);    _lastTimeSet = networkTime;    Debug.Print("Time Set:" + networkTime.ToString());} Getting the readings from the different devices is wrapped in a “GetReadings” method that then returns a specialized “Readings” class with properties for the individual readings. This provides a nice, clean structure as well as shows the different temperature readings coming from the two different sensors – which vary by as much as almost 2C. I’ve also included read-only properties that convert from SI units (Celsius, Pascals) to US/English units (Farenheit/Inches of Mercury). A single method call does all of the work and then the results get printed to the debug stream for monitoring. If the reference voltage is less than 2.2V, the results are ignored as they are going to be invalid. After getting the readings, we sleep for a second. try{        readings = weatherShield.GetReadings();    LEDHelper.Value = true;}catch (Exception ex){    Debug.Print("Exception getting readings.");}if (readings != null){    try    {        weatherShield.BlueLed = false;        if (readings.Reference3V3 < 2.2)        {            weatherShield.GreenLed = false;            //continue;        }        else if (!weatherShield.GreenLed)        {            weatherShield.GreenLed = true;        }        Debug.Print("**READINGS**");        Debug.Print("Timestamp:" + readings.Timestamp.ToLocalTime().ToString());        WriteValue("3V3", readings.Reference3V3);        WriteValue("Battery", readings.Battery);        WriteValue("Light", readings.LightLevel);        Debug.Print("-- HTDU21D");        WriteValue("\tTempC", readings.HTU21D.TempC);        WriteValue("\tTempF", readings.HTU21D.TempF);        WriteValue("\tHumidity", readings.HTU21D.RelativeHumidity);        Debug.Print("-- MPL3115A2");        WriteValue("\tTempC", readings.MPL3115A2.TempC);        WriteValue("\tTempF", readings.MPL3115A2.TempF);        WriteValue("\tPressure (Pa)", readings.MPL3115A2.Pressure);        WriteValue("\tPressure (In-Hg)", readings.MPL3115A2.PressureInHg);    }    catch (Exception ex)    {        Debug.Print("Exception writing readings.");        LEDHelper.Value = false;     }} Now that all of this is working, my next step is to hook it directly to the Connect The Dots demo. From there, creating the Intranet of Things gateway and service. So there’s more … much more … to come. In the meantime, the code for this can be downloaded from my OneDrive.