Notes About Working with Various Arduino & Netduino Microcontroller Boards

Saturday, December 21, 2013

Recording 1-Wire Temperature Data to a CSV File on an Arduino Yún

The following example shows how to record temperature data from multiple DS18B20 sensors and write the data to a CSV file on an SD card.  This code uses the Bridge library for the Yún and its Process class to pass the sensor data to a Python script that writes the data to the file.

The 1-Wire (OneWire) library for the Arduino is available here.

The output to the CSV file looks like this:

28 10 76 18 04 00 00 0E,23375,2013-12-21 19:09:47
28 06 06 7A 03 00 00 67,23062,2013-12-21 19:09:49
28 99 D1 49 03 00 00 3D,23250,2013-12-21 19:09:51
28 07 AC 18 04 00 00 33,23250,2013-12-21 19:09:52
28 10 76 18 04 00 00 0E,23437,2013-12-21 19:09:54

Connections


In my application, the sensors aren't more than a few feet apart, so parasitic power mode works nicely.

Looking at the flat side of the head of the sensor, connect the left pin of each sensor to its right pin.  Connect the right pin of each sensor to the left pin of the next sensor in the series. Connect the center pin of each sensor to the center pin of the next.  Connect digital pin 10 of the Arduino to the center pin of the 1st sensor with a 4.7k Ohm pull-up resistor to 3.3 volts.  Except for the 1st sensor, connect the ground (left pin) of each sensor to the Arduino's ground.


Arduino Sketch


Because I have run into problems trying to pass a float or double through the Arduino Yún's Bridge to the Python script on the Linux "side," this code takes the Celsius temperature reported by each sensor and multiplies it by 1000.  After each sensor is read, the Arduino code uses the Process class to run the Python script (below).  The sensor's ID and temperature data are passed as command parameters to the Python script.  

#include <Bridge.h>
#include <Process.h>
#include <OneWire.h>

OneWire  ds(10);  // on pin 10 (a 4.7K resistor is necessary)
Process p;
String cmd = "/usr/bin/python";        // Linux Path to Python 
String script = "/mnt/sda1/w1temp.py"; // Path to Python script to run

void setup(void) {
  Bridge.begin();
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  double celsius;
  
  if (!ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }

  char id[16];
  // Format sensor's ID as 8 bytex in hex
  char format[] = "%02X %02X %02X %02X %02X %02X %02X %02X";
  sprintf(id, format, addr[0], addr[1], addr[2], 
      addr[3], addr[4], addr[5], addr[6], addr[7]);
  // If reading not valid, just skip and continue    
  if(OneWire::crc8(addr, 7) != addr[7]) { return; }
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);   // start conversion, with parasite power on
  delay(750); 
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);             // Read Scratchpad
  for ( i = 0; i < 9; i++) {  // we need 9 bytes
    data[i] = ds.read();
  }
  // Convert the data to actual temperature
  int16_t raw = (data[1] << 8) | data[0];
  celsius = (float)raw / 16.0;
  char tempData[6];
  // Due to problems with passing a float, multiply temperature by 1000
  // then remove decimal by casting as int 
  sprintf(tempData, "%d", (int)(celsius * 1000));
  p.begin(cmd);
  p.addParameter(script);
  p.addParameter(id);       // Pass sensor ID
  p.addParameter(tempData); // Pass temp C * 1000
  p.run();
}


Python Script


The following Python script (saved as /mnt/sda1/w1temp.py) opens the CSV file in append mode, writes the data, and appends the current date and time.  

import sys
import time

file = open("/mnt/sda1/datafile.txt", "a")
file.write(sys.argv[1])
file.write(",")
file.write(sys.argv[2])
file.write(",")
file.write(time.strftime("%Y-%m-%d %H:%M:%S"))
file.write("\n")
file.close()

It is also possible to save the readings to a Sqlite database.  See this post for notes on installing and working with Sqlite on the Arduino Yún. 

Monday, October 28, 2013

Displaying Sqlite3 Data Using PHP on the Arduino Yun

I posted previously about using Sqlite3 as the database on an Arduino Yun for verifying RFID tag IDs and logging access. The example below shows how to use PHP to read from the access table and display the access data in a Web page.

See this post for information on installing PHP on the Arduino Yun.


Installing the PHP Sqlite3 Module


The first step is to install the PHP Sqlite3 module.  Run the following command at the Linux command line (logged in via SSH).

opkg install php5-mod-sqlite3

PHP Code


Use vim or nano at the command prompt to create the PHP file (access.php) in the /mnt/sd/arduino/www directory.

<?php
$db = new SQLite3('/mnt/sda1/rfid.db', 0666, $error)
        or die ($error);
$get_log_sql = <<<END_SQL
select a.date_time, c.name, c.card
from rfid_access a, rfid_card c
where a.card = c.card
order by date_time desc
END_SQL;
$result = $db->query($get_log_sql);
echo "<table cellpadding='5'>\n";
echo "<tr><th>Date & Time</th><th>Name</th><th>Card</th></tr>\n";
while ($row = $result->fetchArray()) {
        echo "<tr>";
        echo "<td>".$row['date_time']."</td>";
        echo "<td>".$row['name']."</td>";
        echo "<td>".$row['card']."</td>";
        echo "</tr>\n";
}
echo "</tr>\n";

?>

Access the page by going to http://<YourYunAddr>/sd/access.php.

Installing PHP on the Arduino Yun

Installing the CGI version of PHP5 on the Arduino Yun is quite simple.  I was able to get it installed in and running in a few minutes with the following steps:

  1. Use SSH to access the Yun's command Linux command line.
  2. Run opkg to install PHP:
    1. opkg update
    2. opkg install php5-cgi
  3. Run the following command to configure the PHP module for uhttp:uci add_list.  These commands are slightly modified from those given at wiki.openwrt.org/.
    1. uci set uhttpd.main.interpreter=".php=/usr/bin/php-cgi"
    2. uci set uhttpd.main.index_page="index.html index.htm default.html default.htm index.php"
    3. uci commit uhttpd
    4. sed -i 's,doc_root.*,doc_root = "",g' /etc/php.ini
    5. sed -i 's,;short_open_tag = Off,short_open_tag = On,g' /etc/php.ini
    6. Restart uhttpd with the command /etc/init.d/uhttpd restart
Assuming that you are using an SD card for your Web files, place your PHP files in the /mnt/sd/arduino/www directory.

Note (05/08/2014): After upgrading my OpenWrt-Yun image and after following the process described on the Arduino blog to use a micro-SD card in place of the Yun's internal memory, I find that the uhttpd Web server looks for its Web page files in the /www directory, not /mnt/sd/arduino/www.

Saturday, October 26, 2013

RFID Using the Innovations ID-20LA & Arduino Yún

On October 24, 2013, Zoe Romano posted on the Arduino Blog about using an RFID reader and an Arduino Yún.  I've posted before about using Innovations RFID readers with the Arduino (connected via the Sparkfun RFID Reader breakout board).  Since the Yún combines a compact Linux machine with the basic Arduino, it allows the inclusion of a small database to track cards and log access.

The stripped down example I present here uses an Innovations ID-20LA RFID module, the Sparkfun breakout board, and an Arduino Yún.  This example uses the light-weight Sqlite3 database, which can be installed on the Yún quite easily.  When an RFID card is in range of the reader, the card's code is checked agains a database table of valid card numbers.  If the card is in the DB table, access is granted.  To keep things simple for this example, messages are sent to the serial monitor rather than a physical access mechanism.

Software installation, Sqlite3 DB setup, and Python programming are done via SSH.  The Arduino sketch is uploaded and the serial monitor is accessed via the latest Arduino IDE.

Installing Sqlite


At the Yún's command prompt, run the following opkg commands to install Sqlite3 and the Python module for accessing it:

opkg install sqlite3-cli

opkg install python-sqlite3

I could not find these modules in the list of available modules in the browser-based software installation page for Yún administration, but they show up in the list of installed packages after running opkg from the command line.

Using the Sqlite3 command line interface at the command prompt, I created the database (rfid.db) and the tables to hold the valid card numbers (rfid_card) and successful access (rfid_access).  I won't go into the details here.  See the Sqlite3 documentation for more information.


Connections


Sparkfun RFID Board   Arduino Yún
VCC                   3.3V                          
GND                   GND
TX                    Digital 10 (using SoftwareSerial)

Arduino Code


The Arduino C sketch used the SoftwareSerial library for communication with the RFID module and the Bridge library for communication between the Arduino and Linux sides of the Yún.

The Arduino sketch reads the number from the RFID tag and sends it to the Python script via the Bridge library for verification and logging.

#include <Bridge.h>
#include <Process.h>
#include <SoftwareSerial.h>


SoftwareSerial rfidSerial(10, 11); // RX, TX (TX not used)
String card = "";

void setup()  
{
  Serial.begin(9600);
  rfidSerial.begin(9600);
  Bridge.begin();  
  while(!Serial);
}

void loop() 
{
  while(rfidSerial.available()) {
    char c = rfidSerial.read();
    if(c == '\n') {
      card.trim();
      // Remove 2 control chars at start of card data
      card = card.substring(2);
      if(card.length() == 12) {
        checkCard(card);
        break;
      }
      else {
        Serial.println("Error");
        card = "";
      }
    }
    else {
      card = card + c;
    }  
  }
}

void checkCard(String cardID) {
  Process p;
  String cmd = "/usr/bin/python";
  p.begin(cmd);
  // Adjust path to Python script as needed
  p.addParameter("/mnt/sda1/lookup_rfid.py");
  p.addParameter(cardID);
  p.run();
  while(p.available() > 0) {
    char result = p.read();
    if(result == '1') {
      Serial.println("OK");
      card = "";
      break;
    }
    else {
      Serial.println("Denied");
      card = "";
      break;
    }
  }
}

Python Code


The following code is saved in file called lookup_rfid.py.

import sys
import sqlite3

# Change path to DB file as required
conn = sqlite3.connect('/mnt/sda1/rfid.db')
cur = conn.cursor()
# Note space between ' and ''' in 2nd line    
lookup_sql = '''select count(*) from rfid_card  
where card = '%s' ''' % sys.argv[1]
result = cur.execute(lookup_sql)
data = cur.fetchone()           
# if 1, access allowed. Log access
if data[0] == 1:                                             
print 1                                                  
log_sql = '''insert into rfid_access(card, date_time)    
values('%s', date('now', 'localtime') || ' ' ||
time('now', 'localtime'))''' % sys.argv[1]
cur.execute(log_sql)
conn.commit()     
# if 0, access denied     
elif data[0] == 0: print 0
# Other errors
else: print 3
conn.close()

Note: For some reason, I always get an error when I scan the first card after starting the application, but subsequent scans work as expected.

Thursday, October 17, 2013

A Couple Notes about Working with the Arduino Yún Examples

I got an Arduino Yún from Adafruit a couple weeks ago but have been slow in getting started with it.  In trying to get the TermperatureWebPanel example to work, I ran into a couple issues.  These things are noted in the documentation at one place or another but I didn't get things right at first.

The TermperatureWebPanel example requires an SD card.  Be sure to create an arduino directory in the root of the SD card.  Under the arduino directory, create a www directory. Reboot the Yún after inserting the SD card.  The SD card should be linked automatically to /mnt/sd.

The directory for the TemperatureWebPanel example includes a www directory with the HTML and JavaScript files needed for the example.  The files in this directory will be uploaded to the correct location by the Arduino IDE (ver. 1.5.4 +) only when the upload is done via the wireless interface.  Uploading over a serial connection will not work.

If you are working on a desktop computer that cannot connect to the Yún wirelessly, you can use the ethernet interface to upload the sketches.  

I have been able to get the example to work when viewed in Chrome and Safari, but it does not work for me when accessed using Firefox or IE.

Wednesday, September 11, 2013

Sending NMEA Commands from an Arduino Due Sketch to a Copernicus II (Trimble) GPS Module

Sparkfun's Copernicus II DIP Module Breakout is a GPS option that works quite handily with the Arduino Due.  The Trimble Studio utility is a free GUI tool for configuring the chip, but it is also quite possible to send NMEA commands to the module via a TTL serial connection from an Arduino sketch.  This brief example, shows how to send NMEA commands from an Arduino Due to the Copernicus II to change the chip's serial port settings and change the default NMEA output.

Connections


Copernicus II Arduino Due
VCC           3.3V
GND           GND
TX-B          RX1 (D19)
RX-B          TX1 (D18)

Code


The following Arduino setup block switches the default speed for the module's serial port B to 19200 baud. It then configures the module to output the NMEA GLL sentence every second.  

void setup() {
  Serial.begin(115200); // For output to serial console
  Serial1.begin(4800);  // Comm for GPS
  // Change port to 19200 baud
  Serial1.write("$PTNLSPT,019200,8,N,1,4,4*1C\r\n");
  delay(1000);  // Delay to let change take effect
  Serial1.end();
  Serial1.begin(19200); // Reconnect at new speed
  // Output GLL sentence only 
  Serial1.write("$PTNLSNM,0002,01*55\r\n");
}

The initial default speed for the Copernicus II's TX-B/RS-B is 4800 baud.

The PT serial port configuration command ($PTNLSPT) is covered on pp. 242-243 of the Copernicus manual. This is a proprietary command that will not work with other GPS modules.

When sending NMEA commands, be sure to pay close attention to the format of the command.  The manual indicates how many characters or digits are expected for each command parameter.  The documentation shows that the baud rate has to be given in 6 digits, so zeros need to be added to the left side of the baud rate to pad it to 6 digits if the speed is less that 115200. Improperly formatted commands don't work, and there are no error messages returned to help you find the problem.

After the asterisk (*) that marks the end of the command parameters comes the 2-digit checksum.  Commands sent to the module will fail silently if the checksum is not correct.  This Web page provides a very convenient tool for calculating the correct NMEA checksum. Don't include the $ and the * when you input the command text for the checksum calculation.  Remember to update the checksum any time you change the command text in any way.

Each NMEA command ends with a carriage return character (\r) followed by a newline character (\n).

The NM automatic message command ($PTNLSNM) is covered on p. 241 of the manual.  The first parameter is the sum of the codes for the desired output sentences (listed in the manual).  Again, left-pad with zeros so the parameter has the required width.  This is another proprietary command that probably won't work with other GPS modules.

If you run into problems, you can always use Trimble Studio on a Windows PC and an FTDI connector to configure the module. While the module does not provide any useful error messages if an NMEA command doesn't work, I have not found the incorrect or malformed commands cause any permanent problems.

This quick example covers only a couple of the GPS module settings that you can change.  See Appendix C, "NMEA 0183," of the Copernicus II Manual for full documentation.

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


Sunday, July 7, 2013

Displaying GPS Data Using an Adafruit Ultimate GPS, SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD, & an Arduino Due


This post shows how to read data from an Adafruit Ultimate GPS breakout (ver. 3) via a serial connection and display date, time, and location data to the 20 x 4 character SainSmart IIC/I2C/TWI Serial 2004 LCD. These components are connected to an Arduino Due.

Back in June, I posted about using the Adafruit Ultimate GPS with an Arduino Due.  That example uses a more complex approach to parsing the NMEA data and converting values to numeric types. The code presented below sticks to displaying elements of the GPS data ($GPRMC) as strings for display without any conversion.

I made a brief post about using the Sainsmart I2C display last fall. The LiquidCrystal_I2C library for the 20 x 4 display is available from www.yourduino.com. For documentation, see this page.  I am using ver. 1 of the library, and it works fine with the Arduino Due.  There is also a version 2 of the library, but I haven't tested it yet.   I have found that the I2C address for the LCD is 0x3F, not 0x27 as shown in some of the online examples. You may need to try both addresses.

Remember that the Arduino Due is a 3.3V device that does not have 5V tolerant pins like the Netduino.  Both the Adafruit GPS and the Sainsmart I2C LCD work using 3.3V.


Connections


Ultimate GPS Arduino Due
VIN          3.3V
GND          GND
RX           TX1 (Digital 18)
TX           RX1 (Digital 19)

I2C LCD Arduino Due
GND     GND
VCC     3.3V
SDA     SDA (Digital 20)
SCL     SCL (Digital 21)


Code


#include <Wire.h>
#include <LiquidCrystal_I2C.h>

String buffer = "";
String element[10];
LiquidCrystal_I2C lcd(0x3F,20,4);

void setup() {
  Serial1.begin(9600);
  lcd.init(); 
  lcd.backlight();
  lcd.setCursor(0, 0);
}

void loop() {
  if (Serial1.available() > 0) {
    char c = (char) Serial1.read();
    buffer += c;
    if(buffer.endsWith("\r\n")) { 
      if(buffer.startsWith("$GPRMC")) {
        int numArgs = 0;
        int beginIdx = 0;
        int idx = buffer.indexOf(",");
        int k = 0;
        while (idx != -1)
        {
          element[k++] = buffer.substring(beginIdx, idx);
          beginIdx = idx + 1;
          idx = buffer.indexOf(",", beginIdx);
        }  
        if(element[2] == "A") {  
          lcd.setCursor(0, 0);
          lcd.print(element[1] + " " + element[9]);
          lcd.setCursor(1, 1);
          lcd.print(element[3] + " " + element[4]);
          lcd.setCursor(0, 2);
          lcd.print(element[5] + " " + element[6]);
        }
        else{
         lcd.clear(); 
         lcd.setCursor(0, 0);
         lcd.print("No valid data...");
        }
      }
      buffer = "";
    }
  } 
}

Sunday, June 16, 2013

Adafruit Ultimate GPS Breakout & the Netduino Plus 2

I wrote yesterday about using the Adafruit Ultimate GPS Breakout (ver. 3) with an Arduino Due. This GPS module also works quite nicely with the Netduino. To get reception while working at my desk, this external GPS antenna and connector are a big help.

Connections 


GPS Breakout  Netduino Plus 2
TX            Digital 0 (COM1 RX)
RX            Digital 1 (COM1 TX)
GND           GND
VIN           3V3

Code


This code reads the latitude and longitude from the GPRMC NMEA sentence. It converts them to degrees, minutes, and seconds, and then prints them to the debug console. 

namespace GPS
{
    public class Program
    {
        public static void Main()
        {
            SerialPort serial = new SerialPort(SerialPorts.COM1, 9600, Parity.None, 8, 
                StopBits.One);
            serial.ReadTimeout = 1000;
            serial.WriteTimeout = 1000;
            serial.Open();
            String sentence = "";
            while (true)
            {
                if (serial.BytesToRead > 0)
                {
                    int c = serial.ReadByte();
                    sentence += (char)c;
                    if ((char)c == '\n')
                    {
                        if (sentence.IndexOf("GPRMC") > 0)
                        {
                            try
                            {
                                String[] data = sentence.Split(',');
                                // 3rd element = A if data is valid; V = invalid
                                if(data[2] == "A")
                                {
                                    int deg = Int16.Parse(data[3].Substring(0,2));
                                    int min = Int16.Parse(data[3].Substring(2,2));
                                    Double seconds = Double.Parse(data[3].Substring(4,5))*60;
                                    // \xB0 is degree sign
                                    // "F3" format formats seconds to 3 decimal places
                                    Debug.Print(deg + "\xB0 " + min + "' " +  
                                        seconds.ToString("F3") + "\" N");
                                    deg = Int16.Parse(data[5].Substring(0,3));
                                    min = Int16.Parse(data[5].Substring(3,2));
                                    seconds = Double.Parse(data[5].Substring(5,5))*60;
                                    Debug.Print(deg + "\xB0 " + min + "' " + 
                                        seconds.ToString("F3") +  "\" W");
                                }
                            }
                            catch (Exception exc)
                            {
                                // Brief sleep to avoid problem with tight loop
                                Thread.Sleep(100);
                            }

                        }
                        sentence = "";
                    }
                }
                else
                {
                    // Brief sleep to avoid problem with tight loop
                    Thread.Sleep(100);
                }
            }
        }
    }
}