Notes About Working with Various Arduino & Netduino Microcontroller Boards

Saturday, August 31, 2013

The Sparkfun TMP006 Breakout & the Netduino Plus 2

The Sparkfun TMP006 breakout is a non-contact temperature sensor (mounted on a breakout board) that connects to a microcontroller using I2C.  The Sparkfun page includes links to a couple code examples for using the TMP006 with an Arduino.  Working from the sample code, I have produced the C# code below that works with a Netduino Plus 2.  The code is all in one file for the ease of presentation.  This requires the variables and methods to be flagged as static.  Writing a respectable library will come later...

Connections


TMP006 Breakout   Netduino Plus 2
VCC               3V3
SCL               SCL (w/ 10k Ohm pull-up resistor)
SDA               SDA (w/ 10k Ohm pull-up resistor)
GND               GND

The ADR0 & ADR1 pins can be used to change the I2C device address to something other than the default (0x40).  See p. 7 of the datasheet for details.  

The following code reports the temperature of the sensor and the object "in view" of the temperature sensor by printing the values (in degrees Celsius) to the debug console in Visual Studio. 


Code


using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace TMP006
{
    public class Program
    {
        static ushort DEV_ADDR =0x40; 
        // Constants for calculating object temperature
        static double TMP006_B0 = -0.0000294;
        static double TMP006_B1 = -0.00000057;
        static double TMP006_B2 = 0.00000000463;
        static double TMP006_C2 = 13.4;
        static double TMP006_TREF = 298.15;
        static double TMP006_A2 = -0.00001678;
        static double TMP006_A1 = 0.00175;
        static double TMP006_S0 = 6.4;  // * 10^-14

        // Configuration Settings
        static int TMP006_CFG_RESET = 0x8000;
        static int TMP006_CFG_MODEON = 0x7000;
        static int TMP006_CFG_1SAMPLE = 0x0000;
        static int TMP006_CFG_2SAMPLE = 0x0200;
        static int TMP006_CFG_4SAMPLE = 0x0400;
        static int TMP006_CFG_8SAMPLE = 0x0600;
        static int TMP006_CFG_16SAMPLE = 0x0800;
        static int TMP006_CFG_DRDYEN = 0x0100;
        static int TMP006_CFG_DRDY = 0x0080;

        // Registers to read thermopile voltage and sensor temperature
        static int TMP006_VOBJ = 0x00;
        static int TMP006_TAMB = 0x01;
        static int TMP006_CONFIG = 0x02;

        static I2CDevice tmp006;

        // Read 16 bit int from I2C register reg
        static int read16(int reg)
        {
            int data;
            byte[] regCmd = { (byte) reg };
            byte[] response = { 0, 0 };
            I2CDevice.I2CTransaction[] getData16 = new I2CDevice.I2CTransaction[] {
                I2CDevice.CreateWriteTransaction(regCmd),
                I2CDevice.CreateReadTransaction(response)
            };
            int bytesRead = tmp006.Execute(getData16, 100);
            data = response[0] << 8;
            data |= response[1];

            return data;
        }

        // Write data to I2C register reg
        static void write16(int reg, int data)
        {
            byte msb = (byte)(data >> 8);
            byte lsb = (byte)data;
            byte[] regCmd = { (byte)reg, msb, lsb  };

            I2CDevice.I2CTransaction[] putData16 = new I2CDevice.I2CTransaction[] {
                I2CDevice.CreateWriteTransaction(regCmd)
            };

            int bytesRead = tmp006.Execute(putData16, 100);
        }

        // Configures sensor, use before reading from it
        static void config_TMP006(int samples)
        {
            write16(TMP006_CONFIG, (int)(samples | TMP006_CFG_MODEON));
        }

        // Read raw sensor temperature
        static int readRawDieTemperature()
        {
            int raw = read16(TMP006_TAMB);
            raw >>= 2;
            return raw;
        }

        // Read raw thermopile voltage
        static int readRawVoltage()
        {
            // Voltage encoded as 16-bit 2's complement
            short raw = (short) read16(TMP006_VOBJ);
            if (raw > 32767)
                raw = (short) -(~(raw - 1));
            return raw;
        }

        // Calculate object temperature based on raw sensor temp and thermopile voltage
        static double readObjTempC()
        {
            double Tdie = readRawDieTemperature();
            double Vobj = readRawVoltage();
            Vobj *= 156.25;  // 156.25 nV per LSB
            Vobj /= 1000000000; // nV -> V
            Tdie *= 0.03125; // convert to celsius
            Tdie += 273.15; // convert to kelvin

            // Equations for calculating temperature found in section 5.1 in the user guide
            double tdie_tref = Tdie - TMP006_TREF;
            double S = (1 + TMP006_A1 * tdie_tref + TMP006_A2 * tdie_tref * 
                tdie_tref);
            S *= TMP006_S0;
            S /= 10000000;
            S /= 10000000;

            double Vos = TMP006_B0 + TMP006_B1 * tdie_tref + TMP006_B2 * tdie_tref * 
                tdie_tref;

            double fVobj = (Vobj - Vos) + TMP006_C2 * (Vobj - Vos) * (Vobj - Vos);

            double Tobj = System.Math.Sqrt(System.Math.Sqrt(Tdie * Tdie * Tdie * Tdie + 
                fVobj / S));

            Tobj -= 273.15; // Kelvin -> *C
            return Tobj;
        }

        // Calculate sensor temperature based on raw reading
        static double readDieTempC()
        {
            double Tdie = readRawDieTemperature();
            Tdie *= 0.03125; // convert to celsius
            return Tdie;
        }

        public static void Main()
        {
            tmp006 = new I2CDevice(
                new I2CDevice.Configuration(DEV_ADDR, 100)
            );
            config_TMP006(TMP006_CFG_8SAMPLE | TMP006_CFG_MODEON);
            while (true)
            {
                double object_temp = readObjTempC();
                double sensor_temp = readDieTempC();
                Debug.Print("Object: " + object_temp.ToString() + "°C");
                Debug.Print("Sensor: " + sensor_temp.ToString() + "°C\n");
                Thread.Sleep(2000);
            }
        }
    }
}


Friday, August 23, 2013

Generating Sine Waves with an Arduino Uno or Arduino Due

Lately, I've been fiddling around with an oscilloscope and wanted to generate some steady sine waves.  I have found tutorials for the Arduino Uno and Arduino Due that have been very helpful.  I have not found anything that works with the Arduino Leonardo.

Arduino Uno 


Martin Nawrath published an online tutorial for the Arduino Uno, "Arduino DDS Sinewave Generator" back in 2009. It works quite nicely. Here are my notes on the (solderless) breadboard layout for the low-pass filter that converts the PWM output to a nice smooth wave.

The numbers refer to the rows on the breadboard and the letters are the columns.

Component        From    To 
Jumper Wire      1a      (to oscilloscope) 
47nF Capacitor   1b      GND rail
270Ω Resistor    1c      GND rail
4.7mH Inductor   1d      3a
100nF Capacitor  3b      GND rail
4.7mH Inductor   3c      5a

47nF Capacitor   5b      GND rail
270Ω Resistor    5c      7a

Jumper Wire      7b     Arduino Digital 11

The potentiometer's wiper pin should be connect to the Arduino's Analog 0 (with the other pins on the potentiometer connected to the 5V & GND on the Arduino), as indicated in the tutorial.

I didn't have any inductors around, so I ordered an assortment from an Amazon merchant.  It arrived from China in less than 2 weeks. Mouser also has various types of  what they call "Fixed Inductors 4700uH" available. I have tried Mouser Part #s 434-SMCC-472J-00 and 871-B78108S1475J.  They both work fine.  

Arduino Due


On the Arduino Web site there is a nice tutorial, "Simple Waveform Genetator (sic) with Arduino Due."   It presents code that generates sine, triangle, saw-tooth, and square waves.  No external filter is required since it uses  the Due's built-in DAC. 

There is one small error in the code given that needs to b corrected.  

Change the line 

sample = constrain(t_sample, 0, oneHzSample); 

to

sample = constrain(sample, 0, oneHzSample);


Thursday, August 1, 2013

Changing the I2C Address of the PCF8574(P) I/O Expander

Last January, I posted about using the PCF8574P I/O expander to connect a membrane keypad to an Arduino via I2C.  I used the default I2C address (0x20) in that example.  By changing the connections of pins 1 - 3 (A0 - A2) on the chip to 5V (+) or GND (-), the address can be changed withing the range of 0x20 - 0x27.

The pin to the left of the "dot" on the PCF8574P is pin 1 (with the dot in the 12 o'clock position).

Pin 1(A0)  2(A1)  3(A2)  Addr.

      -      -      -    0x20     
      +      -      -    0x21
      -      +      -    0x22
      +      +      -    0x23
      -      -      +    0x24
      +      -      +    0x25
      -      +      +    0x26
      +      +      +    0x27