Notes About Working with Various Arduino & Netduino Microcontroller Boards

Friday, November 30, 2012

Setting the Chronodot via NTP with an Arduino Uno

Here is some code for the Arduino Uno with an Ethernet Shield that gets the current time from a network time server and sets the date and time of the Chronodot to match. 

The Chronodot's SDA is connected to Arduino Uno pin A4 and SCL to A5. Using 3.3V power, the SCL and SDA lines have 10k pull-up resistors. 

I have used an Uno to set the Chronodot's time and date because I can't get the Arduino Time library to work with the Arduino Due.  The error messages indicate a problem with multiple definitions of time_t. (I am using Arduino IDE 1.5.1 r2.).

The code for getting the time from an NTP server is derived from code by Michael Margolis and Tom Igoe.

#include <Wire.h>
#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>

// Enter MAC address for your Ethernet Shield
byte mac[] = { 0x90, 0xAD, 0xA2, 0xDA, 0x0D, 0x0b, 0xF6 };
// local port to listen for UDP packets
unsigned int localPort = 8888;      
// See http://tf.nist.gov/tf-cgi/servers.cgi for address list
IPAddress timeServer(96,47,67,105); 
// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;     
//buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE]; 
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

// Holds seconds since midnight 1/1/1970
unsigned long epoch; 

// Holds formatted result from sprintf()
char buffer[25]; 

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x0E);             // select register
  Wire.write(0b00011100);       // write register bitmap, bit 7 is /EOSC
  Wire.endTransmission();
    Serial.println("Starting Ethernet...");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    while(1) { ; };
  }
  Serial.println("Starting UDP...");
  Udp.begin(localPort);
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);  
  if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it -
    // read the packet into the buffer
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  
    // The timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // Combine the 4 bytes (2 words) into a long integer.
    // This is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // Subtract 70 years:
    epoch = secsSince1900 - seventyYears;
    // Subtract 6 hrs for Central Time - change as neeed
    epoch = epoch - 60 * 60 * 6;    
    setDate();
    setTime();
    getDateTime();
  }      
}

void loop() { }

// Convert binary coded decimal to decimal
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// Convert decimal to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

void getDateTime() {
  // Send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0);                // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7);    // request 7 bytes (seconds, minutes, hours, day, 
                                // date, month, year)
  while(Wire.available())
  { 
    int seconds = bcdToDec(Wire.read()); 
    int minutes = bcdToDec(Wire.read()); 
    int hours = bcdToDec(Wire.read());   
    int day = bcdToDec(Wire.read());    // Not used in example
    int date = bcdToDec(Wire.read());
    int month = bcdToDec(Wire.read());
    int year = bcdToDec(Wire.read());
    // Format code %02d = 2-digit base-10 number with leading 0 
    // if needed for padding
    sprintf(buffer, "%02d/%02d/%02d %02d:%02d:%02d", month, date, year, hours, 
      minutes, seconds);        
    Serial.println(buffer);   
  }
}

void setDate()
{
  Wire.beginTransmission(0x68);
  Wire.write(3);
  Wire.write(decToBcd(weekday(epoch)));
  Wire.write(decToBcd(day(epoch)));
  Wire.write(decToBcd(month(epoch)));
  Wire.write(decToBcd(year(epoch)-2000));
  Wire.endTransmission();
}

void setTime()
{
   Wire.beginTransmission(0x68);
   Wire.write(0);
   Wire.write(decToBcd(second(epoch)));
   Wire.write(decToBcd(minute(epoch)));
   // Adjust offset from GMT as needed
   Wire.write(decToBcd(hour(epoch)));
   Wire.endTransmission();
}

unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:         
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

Thursday, November 29, 2012

Using a Chronodot v.2.1 with Arduino Leonardo or Arduino Due to Get Time & Date

Last summer I posted about using the Netduino to get the time and date from a Chronodot, but I didn't post any code for working with the Arduino. The snippet of sample code available on the Macetech page linked from Adafruit is a bit outdated, so I am posting an updated example that works with the Arduino Leonardo and the Arduino Due. 

I have connected the Chronodot's VCC to the 3.3V pin on the Arduino and GND to GND. The SDA and SCL pins on the Chronodot are connected to the pins on the Arduino with the same labels. On the Arduino Due, note that there are SDA1 and SCL1 pins that are separate from the SDA and SCL pins; again, this example assumes connects to SDA and SCL. I have used 10k pull-up resistors on the SDA and SCL lines. 

This example uses I2C to read the date and time from the Chronodot 2.1 and prints the correctly formatted result to the serial console.

#include <Wire.h>

char buffer[25]; // Holds formatted result from sprintf()
void setup()
{
  Wire.begin();
  Serial.begin(9600);
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x0E);             // select register
  Wire.write(0b00011100);       // write register bitmap, bit 7 is /EOSC
  Wire.endTransmission();
}
void loop()
{
  // Wait for user to open serial console
  while (!Serial) ;
  // send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write(0);                // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7);    // request 7 bytes (seconds, minutes, hours, day, 
                                // date, month, year)
  while(Wire.available())
  { 
    int seconds = bcdToDec(Wire.read()); 
    int minutes = bcdToDec(Wire.read()); 
    int hours = bcdToDec(Wire.read());   
    int day = bcdToDec(Wire.read());    // Not used in example
    int date = bcdToDec(Wire.read());
    int month = bcdToDec(Wire.read());
    int year = bcdToDec(Wire.read());
    // Format code %02d = 2-digit base-10 number with leading 0 
    // if needed for padding
    sprintf(buffer, "%02d/%02d/%02d %02d:%02d:%02d", month, date, year, hours, 
      minutes, seconds);
        
    Serial.println(buffer);
  }
  delay(1000);
}

// Convert binary coded decimal to decimal
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

Monday, November 26, 2012

ePIR Motion Detector & Netduino Plus 2: Example 2

The following sample code builds on my previous example using the Zilog ePIR motion detector with a Netduino Plus 2.

This version adds a couple bits of functionality: it sets the Netduino's clock to the correct time by calling a network time server and logs detections (with date & time) to an SD card, using the card slot provided by the Netduino Plus 2..

As noted in my previous post about writing to an SD card with a Netduino Plus, don't forget to add a reference to System.IO in the project.

Connections:

ePIR   -->  Netduino Plus 2   
pin 1       GND
pin 2       3.3V
pin 3       Digital 1 (TX)
pin 4       Digital 0 (RX) + 100k pull-up resistor
pin 6       3.3V  (no light level correction)

C# Code:

using System;

using System.IO;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;

namespace ePIR
{
    public class Program
    {
        const byte STATUS_CMD = 0x61;       // ePIR command to read status
        const int DELAY = 1000;
        const String NTP = "time.nist.gov"; // NTP server
        const int TZ = -6;                  // Offset from GMT
        const String COM_PORT = "COM1";     // COM port to use with ePIR

        // Network time update code from 
        // http://www.jaypm.com/2011/09/setting-the-netduinos-datetime-automatically/
        public static bool UpdateTimeFromNtpServer(string server, int timeZoneOffset)
        {
            try
            {
                var currentTime = GetNtpTime(server, timeZoneOffset);
                Microsoft.SPOT.Hardware.Utility.SetLocalTime(currentTime);
                return true;
            }
            catch(Exception ex)
            {
                Debug.Print(ex.Message);
                return false; 
            }
        }

        private static DateTime GetNtpTime(String timeServer, int timeZoneOffset)
        {
            // Find endpoint for TimeServer
            var ep = new IPEndPoint(Dns.GetHostEntry(timeServer).AddressList[0], 123);

            // Make send/receive buffer
            var ntpData = new byte[48];

            // Connect to TimeServer
            using (var s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                // Set 20s send/receive timeout and connect
                s.SendTimeout = s.ReceiveTimeout = 20000;
                s.Connect(ep);
                ntpData[0] = 0x1B;   // Set protocol version
                s.Send(ntpData);     // Send Request
                s.Receive(ntpData);  // Receive Time
                s.Close();        
            }

            const byte offsetTransmitTime = 40;
            ulong intpart = 0;
            ulong fractpart = 0;

            for (var i = 0; i <= 3; i++)
                intpart = (intpart << 8) | ntpData[offsetTransmitTime + i];

            for (var i = 4; i <= 7; i++)
                fractpart = (fractpart << 8) | ntpData[offsetTransmitTime + i];

            ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);

            var timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);
            var dateTime = new DateTime(1900, 1, 1);
            dateTime += timeSpan;

            var offsetAmount = new TimeSpan(timeZoneOffset, 0, 0);
            var networkDateTime = (dateTime + offsetAmount);

            return networkDateTime;
        }

        public static bool writeToLog(String msg)
        {
            DateTime now = DateTime.Now;
            String fileName = @"SD\" + now.Year.ToString() + now.Month.ToString() + now.Day + ".log";
            // FileMode.Append will create file if it does not exist
            using (var filestream = new FileStream(fileName, FileMode.Append))
            {
                StreamWriter streamWriter = new StreamWriter(filestream);
                streamWriter.WriteLine(msg + "\n");
                streamWriter.Close();
            }
            return true;
        }

        public static void Main()
        {
            if (UpdateTimeFromNtpServer(NTP, TZ))
            {
                SerialPort serialPort = new SerialPort(COM_PORT, 9600, Parity.None, 8, StopBits.One);
                serialPort.Open();
                while (true)
                {
                    serialPort.WriteByte(STATUS_CMD);
                    Thread.Sleep(100);
                    char result = (char)serialPort.ReadByte();
                    // Reponse 'Y' means that motion was detected
                    if (result == 'Y')
                    {
                        String msg = "Motion detected: " + DateTime.Now.ToString();
                        Debug.Print(msg);
                        writeToLog(msg);
                    }
                    Thread.Sleep(DELAY);
                }
            }
            else
            {
                Debug.Print("Error getting time.");
            }
        }
    }
}

Sunday, November 25, 2012

Using an ePIR Motion Detector with a Netduino Plus 2

Here is a C# "translation" of the sample code I posted yesterday.  If the ePIR detects motion, the code print Y to the debug console, otherwise it prints an N. A reading is taken every second. As in yesterday's example for the Arduino, there is no compensation for ambient light in order to keep the example simple.

Connections:

ePIR   -->  Netduino Plus 2   
pin 1       GND
pin 2       3.3V
pin 3       Digital 1 (TX)
pin 4       Digital 0 (RX) + 100k pull-up resistor
pin 6       3.3V  (no light level correction)

C# Code:

using System.Threading;
using Microsoft.SPOT;
using System.IO.Ports;

namespace ePIR
{
  public class Program
  {
    const byte STATUS_CMD = 0x61;
    const int DELAY = 1000;
    public static void Main()
    {
      SerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
      serialPort.Open();
      while (true)
      {
        serialPort.WriteByte(STATUS_CMD);
        Thread.Sleep(100);
        char result = (char) serialPort.ReadByte();
        Debug.Print(result.ToString());
        Thread.Sleep(DELAY);
      }
    }
  }
}

To use COM2, connect to digital pins 2 (TX) & 3 (RX). To use COM3 connect to digital pins 7 (TX) & 8 (RX). 

Saturday, November 24, 2012

Arduino Due & Windows 8

Driver Installation

When trying to install the Arduino programming port driver for the Arduino Due on Windows 8, the default security prevented installing the unsigned driver. The only solution at present appears to be disabling the driver signature requirement. Windows still warns about installing an unsigned driver, but at least you can choose to do so.

Here are the steps:
  1. Press the Windows Key and the ‘R’ key simultaneously
  2. Run the command  shutdown.exe /r /o /f /t 00
  3. When prompted, select Troubleshoot
  4. Select Advanced Options
  5. Select Startup Settings and reboot
  6. When the boot menu appears, use the arrow keys to select Disable Driver Signature Enforcement
  7. Install driver as usual via Device Manager
Sources: Bryon Connolly's blog, post by Louis Davis in Arduino forum

Sketchbook Location

Start the Arduino IDE as administrator.  In the program's Preferences, change the sketchbook location to a folder accessible by a regular user (like a folder in My Documents). Otherwise, the program complains about the missing sketches folder and may not start when not run as administrator.

Using an ePIR Motion Detector with an Arduino Due

I got my Arduino Due yesterday from Sparkfun and wanted to do a quick test. I've had a Zilog ePIR motion detector for awhile, but hadn't experimented with it either, so here's a quick sketch that reads the status of the ePIR every half second. The value returned from the sensor isY if motion is detected and N if no motion is detected. This example does not include ambient light compensation or other adjustment in the sensitivity of the detector in the interest of simplicity.

In this example, the ePIR is connected in serial mode (via Serial1 on the Arduino Due).

Connections from the ePIR board to the Arduino Due:

ePIR   -->  Arduino Due   
pin 1       GND
pin 2       3.3V
pin 3       Digital 18 (TX1)
pin 4       Digital 19 (RX1) + 10k pull-up resistor
pin 6       3.3V  (no light level correction)

Here is the code that prints the status of the motion detector to the serial console:

static const char STATUS_CMD = 'a';
static const int DELAY = 500;

static char getch() {
  // Wait if ePIR not avail. or busy
  while (!Serial1.available())
    ;
  return Serial1.read();
}

void setup() {
  // Serial console
  Serial.begin(57600);
  Serial.println("\n[EPIR]");
  // Serial1 connected to ePIR via TX1 & RX1 on Due
  Serial1.begin(9600);
}

void loop() {
  Serial1.print(STATUS_CMD);
  Serial.println(getch()); 
  delay(DELAY);


This is similar to the Arduino Leonardo, which has Serial1 connected to digital pins 0 and 1. The code above works with a Leonardo with appropriate connections (wire from ePIR pin 3 connected to Leonardo digital pin 1 [TX1] & from ePIR pin 4 to digital pin 0 [RX1]).

See also: Zilog ePIR documentation (which includes information on all of the commands used with the detector).

You can't use the RX0 TX0 pins, since Serial used to access the serial console. Serial2 (pins 16 & 17 are TX2 & RX2) and Serial3 (pins 14 & 15 are TX3 & RX3) are also available on the Arduino Due.

On another note, I've had intermittent problems with the Arduino Due programming interface device driver on my Windows 7 machine, so I'm using my Mac Mini to work with the Due until I figure out causes the frequent blue screen of death when uploading sketches to the Due.  

Tuesday, November 20, 2012

Changing the I2C Address of a Sparkfun 7-Segment Serial LED Display

Because the breakout board attached to the Sparkfun 7-Segment Serial LED Display includes connections for UART (TTL serial), SPI, and I2C, they are easy to include in a variety of applications.

For a quick example of how to use this display, see my Nov. 7th posting.

I wanted to use a couple connected using I2C because I2C allows chaining multiple devices using just 2 data wires to pins on the Arduino (plus the 5V & GND wires). When using miltiple I2C devices, each has to have a unique address. The Sparkfun LEDs all have the same default address (0x71), so they have to be changed.

Following the special commands document, I connected the first one using TTL serial to reset the I2C address and then switched to an I2C connection for use. You don't have to connect in TTL serial mode, though, just to reset the I2C address. The following code snippet shows how to do this, assuming that SDA is connected to Arduino pin A4 and SCL to A5.

Keep in mind that since the boards have the same initial address (0x71), they will have to have their addresses changed individually. The board stores the address in non-volatile memory, so the new address will stay in effect until you change it again.


#include <Wire.h>

#define CHANGE_ADDR_CMD  0x80
#define DEFAULT_ADDR     0x71
#define NEW_ADDR         0x03 // Value between 0x01 and 0x7E

void setup() {
  Wire.begin();
  Wire.beginTransmission(DEFAULT_ADDR);
  Wire.write(CHANGE_ADDR_CMD);
  Wire.write(NEW_ADDR);
  Wire.endTransmission();
}



Tuesday, November 13, 2012

Netduino Plus 2 OneWire Support

My Netduino Plus 2 just arrived today. I have done a quick test of the OneWire (1-wire) support with a DS18B20 temperature sensor. 

I used godfroi's  OneWireBus and DS18B20 classes (posted at TinyCLR).

Be sure to add the reference to Microsoft.SPOT.Hardware.OneWire in your project.

Looking at the flat side of the sensor, the left pin is connected to GND, the center pin is connected to digital pin 0, and the right pin is connected to 5V. There is a 4k7 pull-up  resistor between 5V and the center pin.

The following code reads the temperature every 10 seconds, converts it to Fahrenheit , and prints the result to the debugging output panel.

using Microsoft.SPOT;
using SecretLabs.NETMF.Hardware.Netduino;
using ThreelnDotOrg.NETMF.Hardware;
using System.Threading;

namespace NetduinoPlus2Application1
{
    public class Program
    {
        public static void Main()
        {
            DS18B20 t = new DS18B20(Pins.GPIO_PIN_D0);
            while (true)
            {
                float temp = t.ConvertAndReadTemperature();
                temp = temp / 5 * 9 + 32;
                Debug.Print(temp.ToString());
                Thread.Sleep(10000);
            }
        }
    }

Update (1/21/13): I have posted  a multi-device version of this code.
Update (3/29/13): For subfreezing temperatures, see this post

Thursday, November 8, 2012

Writing to an SD Card with the Netduino Plus

The following example works with the Netduino Plus (v. 1), firmware and framework v. 4.1.

Netduino Plus 2 and NETMF v. 4.2 require a backslash at the start of the file path (i.e., \SD\test.txt).

I am using a 2GB SD card. Larger cards won't work.

For the following sample to work, add a reference to System.IO in the project's References section.

using System.IO;
using Microsoft.SPOT;

namespace SDTest
{
  public class Program
  {
    public static void Main()
    {
      // Test writing to file
      using (var filestream = new FileStream(@"SD\test.txt", FileMode.Create))
      {
        StreamWriter streamWriter = new StreamWriter(filestream);
        streamWriter.WriteLine("\nThis is a test...\nThis is only a test...\n");
        streamWriter.Close();
      }
      // Test reading from file just written
      using (var filestream = new FileStream(@"SD\test.txt", FileMode.Open))
      {
        StreamReader reader = new StreamReader(filestream);
        Debug.Print(reader.ReadToEnd());
        reader.Close();
      }
    }
  }
}


Here is a fancier example that uses event handlers to detect when the SD card is inserted or ejected. The program writes the test file to the SD card when it detects it is inserted (or already inserted when the program starts).

Be sure to add the references to System.IO and Microsoft.SPOT.IO.

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

namespace SDTest
{
  public class Program
  {
    public static void Main()
    {
      RemovableMedia.Insert += new InsertEventHandler(RemovableMedia_Insert);
      RemovableMedia.Eject += new EjectEventHandler(RemovableMedia_Eject);
      Thread.Sleep(Timeout.Infinite);
    }
    static void RemovableMedia_Insert(object sender, MediaEventArgs e)
    {
      Debug.Print("SD card inserted.\n");
      using (var filestream = new FileStream(@"SD\test2.txt", FileMode.Create))
      {
        StreamWriter streamWriter = new StreamWriter(filestream);
        streamWriter.WriteLine("\nThis is a test...\nThis is only a test...\n");
        streamWriter.Close();
      }
      using (var filestream = new FileStream(@"SD\test2.txt", FileMode.Open))
      {
        StreamReader reader = new StreamReader(filestream);
        Debug.Print(reader.ReadToEnd());
        reader.Close();
      }
    }
    static void RemovableMedia_Eject(object sender, MediaEventArgs e)
    {
      Debug.Print("SD card ejected.\n");
    }
  }
}

Wednesday, November 7, 2012

Displaying Data Using a Sparkfun 7-Segment Serial LED Display

There are multiple ways to connect to a Sparkfun 7-segment serial LED display (4-digit). There is good official documentation online. The easiest option is the TTL serial option, which requires only one wire for data. This approach uses the Arduino SoftwareSerial library.

Here is a quick example that uses a DHT22 to measure temperature and relative humidity that displays the results on the LED display in an alternating fashion. 

Connections (along the long sid of the LED):
7-Segment  -->  Arduino Uno
        GND  -->  GND
        VCC  -->  5V
        RX     -->  Digital 7    

The data line for the DHT22 is connected to the Arduino's digital pin 2.

#include <dht22.h>
#include <Versalino.h>
#include <SoftwareSerial.h>

const static int rst = 0x76; 
const static int pos = 0x79;
const static int pause = 4000;  // 4 second pause

// Only pin 7 is actually connected, but both rx & tx specified
SoftwareSerial Serial7Segment(6, 7); //RX pin, TX pin
dht22 DHT22;

void setup() {
  // DHT22 sensor connected to digital pin 2
  DHT22.attach(2);
  Serial7Segment.begin(9600);   // Talk to the Serial7Segment at 9600 bps
  Serial7Segment.write(rst);    // Reset the display 
}

void loop() {
  int chk = DHT22.read();
  // No ned for decimal, cast as int
  int temp = (int) DHT22.fahrenheit();
  int rh = (int) DHT22.humidity;
  // Display temp shifted 1 char to the right
  Serial7Segment.write(pos);
  Serial7Segment.write(0x1);
  Serial7Segment.print(temp);
  // Label temp as Fahrenheit
  Serial7Segment.print("F");
  delay(pause); 
  Serial7Segment.write(rst);
  Serial7Segment.print(rh);
  Serial7Segment.print("rh");
  delay(pause); 
  Serial7Segment.write(rst);
}



Sunday, November 4, 2012

Using the uM-FPU Co-Processor to Convert Temperature from a TMP36 Sensor to Fahrenheit

Here is some code that I have gotten to work using the uM-FPU to convert the analog temperature reading from a TMP-36 sensor to degrees Fahrenheit. Of course, the accuracy of the TMP-36 doesn't really  warrant  such exact treatment. This is just intended as a learning exercise.


#include <SPI.h>
#include <Fpu.h>
#include <FpuSerial.h>

void setup()
{
  Serial.begin(19200);
  SPI.begin();
  Fpu.begin();

  // Check to make sure uM-FPU is connected
  if (Fpu.sync() != SYNC_CHAR) {
    Serial.print("uM-FPU not detected");
    while(1) ; // stop if FPU not detected
  }
}

void loop() {
  // Read int voltage from TMP35 (0 - 1023)
  // & cast it as a float
  float Tmp36 = (float) analogRead(1);
  // Convert to bytes for co-processor
  byte* bTmp36 = (byte*) &Tmp36;
  // Converion factor to get volts (0V - 5V)
  float vConvFactor  = 0.004882814;
  // Translate conversion factor to bytes for co-processor
  byte* bConvFactor = (byte*) &vConvFactor;
  // Use co-processor register 1 as accumulator
  Fpu.write(SELECTA, 1);
  // Put byte version of TMP36 reading into accumulator
  Fpu.write(FWRITEA, bTmp36[3], bTmp36[2], bTmp36[1], bTmp36[0]);
  // Load byte version of conversion factor into register 0
  // Multiply accumulator by value in register 0
  Fpu.write(FWRITE0, bConvFactor[3], bConvFactor[2], bConvFactor[1], bConvFactor[0], FMUL0);
  // Multiply by 100 to get degrees Celsius
  // Subtract .5V * 100 from result in accumulator. Easier to use subtract immediate with integer
  // rather than subtracting 0.5 before multiplying by 100.   
  // Multipy by 9 as 1st step toward converting to Fahrenheit.
  Fpu.write(FMULI, 100, FSUBI, 50, FMULI, 9);
  // Finish C -> F conversion by dividing by 5 and adding 32
  Fpu.write(FDIVI, 5, FADDI, 32);
  Fpu.wait();
  // Return result to Arduino - only 2 decimal places 
  Fpu.write(FREADA);
  float result = Fpu.readFloat();
  Serial.println(result);
  // Print result using FpuSerial - 6 decimal places
  FpuSerial.printFloatln(96);
  while(1);
}

Converting a float to an Array of bytes

When passing a float value from the Arduino to the uM-FPU v 3.1 floating point co-processor, it is necessary to convert it to an array of bytes.

Each Arduino float is 32 bit (4 bytes).

Note that the co-processor expects the bytes in the opposite order compared to the Arduino.

Here is a sample code snippet showing how to do this. This code takes the specified float value and loads it into the co-processor's register 0:

float x = 0.004882814;
byte* b = (byte*) &x;
Fpu.write(FWRITE0, b[3], b[2], b[1], b[0]);

Saturday, November 3, 2012

Basic Example Using a Floating Point Co-Processor with the Arduino

The uM-FPU v 3.1 math co-processor can add significant calculating power to the Arduino. The FPU library is available from the Arduino Playground.

Using SPI, the Arduino and co-processor communicate over 3 wires. The complete wiring diagram is has a few more connections, but the setup isn't bad.  

The manufacturer, Micromega Corp., has good uM-FPU documentation on their site. There are also a couple code examples included with the FPU library. 

The sample code below is meant to be a very bare-bones example showing the minimum code needed to work with the co-processor. This example calculates the square root of 2 and displays the result in the serial console with 9 decimal places.

#include <SPI.h>
#include <Fpu.h>
#include <FpuSerial.h>

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  Fpu.begin();

  // Check to make sure uM-FPU is connected
  if (Fpu.sync() != SYNC_CHAR) {
    Serial.print("uM-FPU not detected");
    while(1) ; // stop if FPU not detected
  }
}

void loop() {
  /*  SELECTA, 1 means select register 1 as "accumulator" *workspace)
   *  FSETI, 2 means load 2 into the accumulator as a float
   *  SQRT means calculate square root of value in accumulator & save result 
   *       in the accumulator.
   *  Each Fpu.write can hasve up to a total of 6 commands & arguments combined. 
   */
  Fpu.write(SELECTA, 1, FSETI, 2, SQRT);
  Serial.print("Sqrt of 2 = ");
  /*  Print result to the serial monitor.
   *  129 means format up to 12 charaters toal, 9 to the right of decimal point.
   */
  FpuSerial.printFloatln(129);  
  while(1);  // stop here
}

Friday, November 2, 2012

Using an Arduino Uno & SainSmart 12864 Graphic LCD to Graph Temperature

I posted yesterday about getting started with the SainSmart graphic LCD. Below is a more complex application (but still not too complex).

 Here is a sketch that uses the DHT22 temperature (and humidity) sensor to read the current temperature (in degrees Fahrenheit) once a minute and display the result as a bar graph on the LCD. The graph has labels on the left side from 67 to 72 degrees (but can display values up to 73 degrees). Each column on the graph is one pixel wide. The graph starts scrolling to the left when the right margin is reached.

#include <dht22.h>
#include <Versalino.h>
#include <U8glib.h>

/*
 *  SID connected to D2
 *  CS  connected to D3
 *  SCK connected to D5
 *  No reset connected
 */
U8GLIB_ST7920_128X64 u8g(5, 2, 3, U8G_PIN_NONE);         

dht22 DHT22;

const int LAST_ROW = 63;
const int LAST_COL = 110;
static char* TEMP_LABELS[] = {"67", "68", "69", "70", "71", "72"};

float temp;
int dots;
int bars[LAST_COL]; 
int cnt = 0;

void setup(void) {
  // DHT22 temp sensor connected to pin D9 
  DHT22.attach(9);
}

void shiftBars() {
  for(int n = 1; n < LAST_COL; n++) {
    bars[n-1] = bars[n];
  }
}

void draw(void) {
  u8g.setFont(u8g_font_04b_03);
  drawDegreeMarks();
  for(int j = 0; j < LAST_COL; j++) {
    u8g.drawVLine(16 + j, LAST_ROW - 2 - bars[j], bars[j]);
  }
}

void drawDegreeMarks() {
  int k = 0;
  for(int i = LAST_ROW - 3; i > 0; i = i-10) {
    u8g.drawStr(0, i + 2, TEMP_LABELS[k++]);
    u8g.drawHLine(10, i, 4);
  }   
}

void loop(void) {
  int chk = DHT22.read();
  temp = DHT22.fahrenheit();
  Serial.println(temp);
  dots = (temp * 10 - 670);
  if(cnt <= LAST_COL - 1) {
    bars[cnt++] = dots;
  }
  else {
    bars[cnt] = dots;
    shiftBars();
    cnt = LAST_COL - 1;
  }
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  // redraw once a minute
  delay(60000); 
}

Thursday, November 1, 2012

SainSmart 12864 Graphic LCD Display & the Arduino Uno

The SainSmart 12864 128x64 graphic blue LCD display (available from Amazon for less than $20) doesn't come with any documentation, but the u8glib (Universal Graphics Library for 8 Bit Embedded Systems by Oliver Kraus) makes it easy to use via an SPI connection to the Arduino Uno R3. (Note: This does not work with the Arduino Due.)

 To use an SPI connection, use the pins in the hooded connection on the module. Make sure the switch on the other end of the board is set to SPI (not LPT). (The other switch controls the backlight.)

Note (9/7/2013):  With a slight modification, this example also works with the Arduino Leonardo or Arduino Due.  See the section near the bottom of the post.


Connections: 


LCD Module ---> Arduino UNO 
(Top Row) 
       SID ---> D11 
        CS ---> D12 
       SCK ---> D13

(Bottom) 
       GND ---> GND 
       --- ---> --- 
       VCC ---> V5

Code:


The following code just draws a couple basic shapes, but it shows how the library works.

#include <U8glib.h>

U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE);         

void draw(void) {
  u8g.drawCircle(20, 20, 10);
  u8g.drawDisc(30, 30, 10);
  u8g.drawLine(60, 30, 90, 50);
  u8g.drawBox(50, 50, 20, 10);
  u8g.drawFrame(70, 10, 10, 30);
}

void setup(void) { }

void loop(void) {
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  // rebuild the picture after some delay
  delay(500);
}


See the documentation in the wiki for more on the functionality of the library.

Arduino Due or Arduino Leonardo:


Note: As noted in the discussion below, with the Arduino Due or Leonardo, be sure to use ver. 1.11 (or later) of u8glib and use the following constructor (replaces 2nd line of code):

U8GLIB_ST7920_128X64_1X u8g(13, 11, 12);