Notes About Working with Various Arduino & Netduino Microcontroller Boards

Thursday, June 26, 2014

Two I2C Devices with the Same Address with an Arduino Due

The Arduino Due has two separate I2C connections, not just redundant pins connected to the same I2C pins on the processor (as on the Arduino Uno).  This makes it possible to use two I2C devices with the same device address.

Here is a quick example that uses two TMP102 breakout boards from Sparkfun. The new version of this board requires removing a solder jumper to change the device's I2C address.  Otherwise, the boards all use 0x48, regardless of how the ADD0 pin is connected.

Connections


TMP102 #1  Arduino Due
VCC        3.3V
GND        GND
SDA        SDA (D20) (Wire)
SCL        SCL (D19) (Wire)
ADD0       (Not connected)

TMP102 #2  Arduino Due
VCC        3.3V
GND        GND
SDA        SDA1 (Wire1)
SCL        SCL1 (Wire1)
ADD0       (Not connected)


Code


#include <Wire.h>

// Formula to convert Celsius to Fahrenheit
// This is not directly tied to the functioning of the TMP102
// and is a simple formula, so it useful to use #define for the 
// conversion function.
#define C_TO_F(x) (x * 1.8 + 32.0)
#define I2C_ADDR 0x48

void setup() {
  Wire.begin();
  Wire1.begin();
  Serial.begin(9600);
}

void loop() {
  Wire.requestFrom(I2C_ADDR, 2);
  byte msb1 = Wire.read();
  byte lsb1 = Wire.read();
  Wire1.requestFrom(I2C_ADDR, 2);
  byte msb2 = Wire1.read();
  byte lsb2 = Wire1.read();
  double t1 = (((msb1 << 8) | lsb1) >> 4) * 0.0625;
  double t2 = (((msb2 << 8) | lsb2) >> 4) * 0.0625;
  Serial.print(C_TO_F(t1), 3);
  Serial.print(" ");
  Serial.println(C_TO_F(t2), 3);
  delay(1000);
}

Wednesday, June 25, 2014

Measuring Barometric Pressure using a MPL115A1 with an Arduino Due

The example below shows how to use a MPL115A1 (on a breakout board from Sparkfun) to measure barometric pressure with an Arduino Due.

This code is a simplified version of code by Jeremiah McConnell  and Jim Lindblom.  Their code does much more than measure the barometric pressure (which is all I need to do).  Because barometric pressure given in weather forecasts is usually adjusted to sea level, I have modified the code a bit to take this into account.  Adjust the value of seaLevelAdjInHg to match the facts at your location.  The comments in the code explain.  To keep things simple for this post, the program prints the current pressure in inches of mercury to the serial monitor in the Arduino IDE.

I have tested this using the Arduino Due, which has separate, dedicated pins for SPI.

Connections


MPL115A1  Arduino Due
SDN       D10      
CSN       D9
SDO       SPI1 (marked by dot on board)
SDI       SPI4 (middle pin 2nd column)
SCK       SPI3 (middle pin 1st column, below SPI1)
GND       GND  (Arduino Due power pins)
VCC       3.3V (Arduino Due power pins)


Code


#include <SPI.h>

// Pin definitions
#define MPL115A1_ENABLE_PIN 9
#define MPL115A1_SELECT_PIN 10
// Masks for MPL115A1 SPI i/o
#define MPL115A1_READ_MASK  0x80
#define MPL115A1_WRITE_MASK 0x7F 
// MPL115A1 register address map
#define PRESH   0x00    // 80
#define PRESL   0x02    // 82
#define TEMPH   0x04    // 84
#define TEMPL   0x06    // 86
#define A0MSB   0x08    // 88
#define A0LSB   0x0A    // 8A
#define B1MSB   0x0C    // 8C
#define B1LSB   0x0E    // 8E
#define B2MSB   0x10    // 90
#define B2LSB   0x12    // 92
#define C12MSB  0x14    // 94
#define C12LSB  0x16    // 96
#define C11MSB  0x18    // 98
#define C11LSB  0x1A    // 9A
#define C22MSB  0x1C    // 9C
#define C22LSB  0x1E    // 9E
// Unit conversion macros
#define FT_TO_M(x) ((long)((x)*(0.3048)))
#define KPA_TO_INHG(x) ((x)*(0.295333727))
#define KPA_TO_MMHG(x) ((x)*(7.50061683))
#define KPA_TO_PSIA(x) ((x)*(0.145037738))
#define KPA_TO_KGCM2(x) ((x)*(0.0102))
#define INHG_TO_PSIA(x) ((x)*(0.49109778))
#define DEGC_TO_DEGF(x) ((x)*(9.0/5.0)+32)

/* Barometric pressue in weather forcasts is adjusted to sea level.
   Add 1200 Pa for each 100m above sea level in elevation.  
   Multiply by 0.000295333727 to convert to inches of Hg 
   For my location, that comes to 0.930 */ 
double seaLevelAdjInHg = 0.930;

void setup() {
    // initialize serial i/o
    Serial.begin(9600);   
    // initialize SPI interface
    SPI.begin();
   // initialize the chip select and enable pins
    pinMode(MPL115A1_SELECT_PIN, OUTPUT);
    pinMode(MPL115A1_ENABLE_PIN, OUTPUT);
    // sleep the MPL115A1
    digitalWrite(MPL115A1_ENABLE_PIN, LOW);
    // set the chip select inactive, select signal is CS LOW
    digitalWrite(MPL115A1_SELECT_PIN, HIGH);
}    

void loop() {
    
    float pressure_pKa = 0;
    float temperature_c= 0;
    long altitude_ft = 0;
    // wake the MPL115A1
    digitalWrite(MPL115A1_ENABLE_PIN, HIGH);
    delay(20);  // give the chip a few ms to wake up
    pressure_pKa = calculatePressurekPa();
    temperature_c = calculateTemperatureC();
    // put the MPL115A1 to sleep, it has this feature why not use it
    // while in shutdown the part draws ~1uA
    digitalWrite(MPL115A1_ENABLE_PIN, LOW);
    double pressure = KPA_TO_INHG(pressure_pKa); 
    pressure += seaLevelAdjInHg;   
    Serial.print(pressure, 2);
    Serial.println(" in Hg ");  
    // wait a few seconds before looping
    delay(5000);
}

float calculateTemperatureC() {
    unsigned int uiTadc;
    unsigned char uiTH, uiTL;
    unsigned int temperature_counts = 0;
    writeRegister(0x22, 0x00);  // Start temperature conversion
    delay(2);                   // Max wait time is 0.7ms, typ 0.6ms 
    // Read pressure
    uiTH = readRegister(TEMPH);
    uiTL = readRegister(TEMPL);
    uiTadc = (unsigned int) uiTH << 8;
    uiTadc += (unsigned int) uiTL & 0x00FF;
    // Temperature is a 10bit value
    uiTadc = uiTadc >> 6;
    // -5.35 counts per °C, 472 counts is 25°C
    return 25 + (uiTadc - 472) / -5.35;
}

float calculatePressurekPa() {
    // See Freescale document AN3785 for detailed explanation
    // of this implementation.
    signed char sia0MSB, sia0LSB;
    signed char sib1MSB, sib1LSB;
    signed char sib2MSB, sib2LSB;
    signed char sic12MSB, sic12LSB;
    signed char sic11MSB, sic11LSB;
    signed char sic22MSB, sic22LSB;
    signed int sia0, sib1, sib2, sic12, sic11, sic22, siPcomp;
    float decPcomp;
    signed long lt1, lt2, lt3, si_c11x1, si_a11, si_c12x2;
    signed long si_a1, si_c22x2, si_a2, si_a1x1, si_y1, si_a2x2;
    unsigned int uiPadc, uiTadc;
    unsigned char uiPH, uiPL, uiTH, uiTL;
    writeRegister(0x24, 0x00);      // Start Both Conversions
    //writeRegister(0x20, 0x00);    // Start Pressure Conversion
    //writeRegister(0x22, 0x00);    // Start temperature conversion
    delay(2);                       // Max wait time is 1ms, typ 0.8m
    // Read pressure
    uiPH = readRegister(PRESH);
    uiPL = readRegister(PRESL);
    uiTH = readRegister(TEMPH);
    uiTL = readRegister(TEMPL);    
    uiPadc = (unsigned int) uiPH << 8;
    uiPadc += (unsigned int) uiPL & 0x00FF;
    uiTadc = (unsigned int) uiTH << 8;
    uiTadc += (unsigned int) uiTL & 0x00FF;
    // Placing Coefficients into 16-bit Variables
    // a0
    sia0MSB = readRegister(A0MSB);
    sia0LSB = readRegister(A0LSB);
    sia0 = (signed int) sia0MSB << 8;
    sia0 += (signed int) sia0LSB & 0x00FF;
    // b1
    sib1MSB = readRegister(B1MSB);
    sib1LSB = readRegister(B1LSB);
    sib1 = (signed int) sib1MSB << 8;
    sib1 += (signed int) sib1LSB & 0x00FF;
    // b2
    sib2MSB = readRegister(B2MSB);
    sib2LSB = readRegister(B2LSB);
    sib2 = (signed int) sib2MSB << 8;
    sib2 += (signed int) sib2LSB & 0x00FF;
    // c12
    sic12MSB = readRegister(C12MSB);
    sic12LSB = readRegister(C12LSB);
    sic12 = (signed int) sic12MSB << 8;
    sic12 += (signed int) sic12LSB & 0x00FF;
    // c11
    sic11MSB = readRegister(C11MSB);
    sic11LSB = readRegister(C11LSB);
    sic11 = (signed int) sic11MSB << 8;
    sic11 += (signed int) sic11LSB & 0x00FF;
    // c22
    sic22MSB = readRegister(C22MSB);
    sic22LSB = readRegister(C22LSB);
    sic22 = (signed int) sic22MSB << 8;
    sic22 += (signed int) sic22LSB & 0x00FF;
    // Coefficient 9 equation compensation
    uiPadc = uiPadc >> 6;
    uiTadc = uiTadc >> 6;
    // Step 1 c11x1 = c11 * Padc
    lt1 = (signed long) sic11;
    lt2 = (signed long) uiPadc;
    lt3 = lt1*lt2;
    si_c11x1 = (signed long) lt3;
    // Step 2 a11 = b1 + c11x1
    lt1 = ((signed long)sib1)<<14;
    lt2 = (signed long) si_c11x1;
    lt3 = lt1 + lt2;
    si_a11 = (signed long)(lt3>>14);
    // Step 3 c12x2 = c12 * Tadc
    lt1 = (signed long) sic12;
    lt2 = (signed long) uiTadc;
    lt3 = lt1*lt2;
    si_c12x2 = (signed long)lt3;
    // Step 4 a1 = a11 + c12x
    lt1 = ((signed long)si_a11<<11);
    lt2 = (signed long)si_c12x2;
    lt3 = lt1 + lt2;
    si_a1 = (signed long) lt3>>11;
    // Step 5 c22x2 = c22*Tadc
    lt1 = (signed long)sic22;
    lt2 = (signed long)uiTadc;
    lt3 = lt1 * lt2;
    si_c22x2 = (signed long)(lt3);
    // Step 6 a2 = b2 + c22x2
    lt1 = ((signed long)sib2<<15);
    lt2 = ((signed long)si_c22x2>1);
    lt3 = lt1+lt2;
    si_a2 = ((signed long)lt3>>16);
    // Step 7 a1x1 = a1 * Padc
    lt1 = (signed long)si_a1;
    lt2 = (signed long)uiPadc;
    lt3 = lt1*lt2;
    si_a1x1 = (signed long)(lt3);
    // Step 8 y1 = a0 + a1x1
    lt1 = ((signed long)sia0<<10);
    lt2 = (signed long)si_a1x1;
    lt3 = lt1+lt2;
    si_y1 = ((signed long)lt3>>10);
    // Step 9 a2x2 = a2 * Tadc
    lt1 = (signed long)si_a2;
    lt2 = (signed long)uiTadc;
    lt3 = lt1*lt2;
    si_a2x2 = (signed long)(lt3);
    // Step 10 pComp = y1 + a2x2
    lt1 = ((signed long)si_y1<<10);
    lt2 = (signed long)si_a2x2;
    lt3 = lt1+lt2;
    // Fixed point result with rounding
    //siPcomp = ((signed int)lt3>>13);
    siPcomp = lt3/8192;
    // decPcomp is defined as a floating point number
    // Conversion to decimal value from 1023 ADC count value
    // ADC counts are 0 to 1023, pressure is 50 to 115kPa respectively
    decPcomp = ((65.0/1023.0)*siPcomp)+50;
    return decPcomp;
}

unsigned int readRegister(byte thisRegister) {   
    byte result = 0;
    // select the MPL115A1
    digitalWrite(MPL115A1_SELECT_PIN, LOW);
    // send the request
    SPI.transfer(thisRegister | MPL115A1_READ_MASK);
    result = SPI.transfer(0x00);
    // deselect the MPL115A1
    digitalWrite(MPL115A1_SELECT_PIN, HIGH);
    return result;  
}

void writeRegister(byte thisRegister, byte thisValue) {
    // select the MPL115A1
    digitalWrite(MPL115A1_SELECT_PIN, LOW);
    // send the request
    SPI.transfer(thisRegister & MPL115A1_WRITE_MASK);
    SPI.transfer(thisValue);    
    // deselect the MPL115A1
    digitalWrite(MPL115A1_SELECT_PIN, HIGH);
}

Tuesday, June 10, 2014

Python Code to Read & Parse TSIP Data from a Copernicus II GPS Module on an Arduino Yun

The following example shows to read and parse some basic GPS data (location and time) in TSIP format from a Copernicus II GPS module. TSIP data is in binary format, unlike NMEA sentences that are in ASCII format. This code is in Python and is designed to run on an Arduino Yun.  In this case, the Copernicus II is connected to the Arduino Yun using the Yun's USB port.  I use a Sparkfun 3.3V FTDI adapter.

Connections


Copernicus II FTDI Adapter
TX-B          RXI
RX-B          TXO
GND           GND

The Copernicus II also has its VCC and GND connected to 3.3V and GND on the Yun.

This example assumes that you are using SSH to access your Yun's command line.  The program outputs the current GPS time and location to the terminal.

Prerequisits


This example requires a couple modules that are installed using opkg at the command line on the Yun.  The following commands will install them:


opkg update
opkg install kmod-usb-serial-ftdi
opkg install pyserial

Code


import serial
import struct
import math
import datetime

ser = serial.Serial("/dev/ttyUSB0", baudrate=38400)
tsip = []
last = ''
start = 0
dle_cnt = 0
id = 0

DLE = '\x10'
ETX = '\x03'

time = "No time data yet"
today = "No date data yet"

# GPS date counts weeks starting Jan. 6, 1980
startDate = datetime.date(1980, 1, 6)

while True:
  data = ser.read()
  # Test for data frame start marker DLE (0x10)
  if start == 0 and data == DLE:
    start = 1
  # To avoid confusion, when the frame marker DLE (0x10) occurs in the sequence of data
        # bytes in the data frame, it is doubled.  We need to drop the extra 0x10 by skipping the 
  # rest of the loop for the current iteration. Count the number of DLEs to track whether 
  # it is even or odd.  The count DLE that marks the end of the frame - before ETX (0x03) - 
  # is always odd. See p. 122 of the Copernicus II manual.
  elif start == 1 and data == DLE:
    dle_cnt = dle_cnt + 1
    if last == DLE:
      continue
  # If the last byte was DLE (0x10) and the current byte is not ETX (0x03),
  # then the current byte is the packet ID
  elif start == 1 and data != ETX and last == DLE:
    id = data
  # If the current byte is 0x03 (ETX), has come right after DLE (0x10), & the 
  # DLE count is odd, we have reached the end of the data frame.
  elif start == 1 and data == ETX and last == DLE and dle_cnt % 2 == 1:
    dle_cnt = 0
    last = ''
    tsip.append( data )

    # Packet 0x41 has GPS time data
    if id == '\x41':
      # t is number of seconds since start of week
      t = int(struct.unpack('f', "".join(tsip[2:6]))[0])
      # w is number of weeks since Jan. 6, 1980
      w = int(struct.unpack('h', "".join(tsip[6:8]))[0])
      # off is offset in seconds between GPS time and GMT
      off = struct.unpack('f', "".join(tsip[8:12]))[0]
      # Subtract offset seconds from time in seconds from start 
      # of week
      t = t - off
      # Day of current week; Sunday = 0
      day = math.floor(t / 86400)
      # Hour of the day
      hour = math.floor((t - (day * 86400))/3600)
      # Minutes after the hour
      min = math.floor((t - (day * 86400) - (hour * 3600))/60)
      # Seconds into current minute
      sec = t - (day * 86400) - (hour * 3600) - (min * 60)      
      time = "%02d:%02d:%02d GMT" % (hour, min, sec)
      today = startDate + datetime.timedelta(weeks=w) + \
        datetime.timedelta(days=day)
      
    # Packet 0x84 has the position data we need.
    # See p. 163 of the Copernicus II manual for structure of this packet
    if id == '\x84':
      # Join bytes into string without added spaces and unpack as big-endian 
      # double.  struct.unpack returns a tuple. Our value is in the first element.
      lat_rad = struct.unpack('>d', "".join(tsip[2:10]))[0]
      lat_deg = lat_rad * 180.0 / math.pi
      lat_dir = 'N' if lat_deg > 0 else 'S'
      long_rad = struct.unpack('>d', "".join(tsip[10:18]))[0]
      long_deg = long_rad * 180.0 / math.pi
      long_dir = 'E' if long_deg > 0 else 'W'
      print "%s %s:  %02.6f %s  %03.6f %s\n" % (today, time, abs(lat_deg), 
        lat_dir, abs(long_deg), long_dir)
      id = 0
    tsip = []
    start = 0
    continue
  last = data    
  tsip.append(data)






Friday, May 30, 2014

Reading Temperature from Multiple 1-Wire DS18B20 Sensors via a um-FPU64 Math Coprocessor

The um-FPU64 math coprocessor includes interfaces for connecting to many different kinds of sensors via its DEVIO functions. The free IDE that comes with the um-FPU64 includes examples of us er-defined routines stored in the coprocessor that  can be called from from an Arduino sketch, but I wanted to try to get it to work just from the "Arduino side" (without using the um-FPU64 IDE to program routines).  This example is really just exploration.  An Arduino can very handily read DS18B20s without help.  This example is really more about trying out the DEVIO instructions sent to the coprocessor using the Arduino FPU64 library. The code below reads the temperature from multiple DS18B20s, converts the reading to Celsius and Fahrenheit. It then calculates the average of the readings using the FPU64's matrix functions.  The average temperature in Celsius and Fahrenheit is then displayed on a Sainsmart I2C LCD display.  The Arduino drives the display because I haven't been able to get the um-FPU64's DEVIO LCD and DEVIO I2C functions to work with this display.

The um-FPU64 is available from Solarbotics. You can find documentation for the um-FPU64 on the MicroMega Corp. Web site.

The Arduino FPU64 library is available here on the Arduino Playground.

See my previous post on the SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD Module Shield for a quick introduction and link to the relevant library.

Connections


The um-FPU64 is connected to the Arduino Uno via SPI.  See this diagram on the Arduino Playground for connection information.  It is also possible to connect the coprocessor to the Arduion via I2C, though that is a slower connection.  See my earlier post, Connecting an uM-FPU64 Math Co-Processor to an Arduino Due via I2C, for more information.

The DS18B20s are hooked up for non-parasitic power.  Looking at the flat side of the sensor's head, the left pin is connected to 3V3, the center pin is connected to the center pin of the next sensor, and the right pin is connected to GND.  There is a 4.7k Ohm pull-up resistor on the connection to the FPU's D0 pin.

Code


The Fpu.write() instruction can send up to 6 FPU instructions and parameters in a single call.  This code tries to be efficient by sending multiple commands and parameters in each call, where this is possible.  Note that grouping multiple DEVIO calls in one Fpu.write() does not work or produces odd results. 

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


// ---> Change to number of DS1820B devices <--- 
#define DEV_CNT   5

//--- uM-FPU Register Definitions ---
#define RAW_TEMP  15   
#define AVG_TEMP  16
#define TEMP_C    20 //Start of group of registers to hold temp C data for each device 
#define TEMP_F    30 //Start of group of registers to hold temp F data for each device
#define AVG_C     40
#define AVG_F     50
#define DEV_CNT   60
#define DEV_ADDR  130 //Start of group of 128bit registers to hold 1-wire addr data for each device 

//--- OneWire Command Definitions --
#define OWIRE               0x10
#define ENABLE              0x01
#define RESET               0x02
#define SEARCH              0x05
#define SELECT              0x03
#define WRITE_BYTE          0x20
#define CONVERT_T           0x44
#define READ_SCRATCH_PAD    0xBE
#define READ_REG16_LSB_SE   0x3D

// Definition of matrix average operation not included in FPU64.h
#define AVE                 0x16

#define FPU64_PIN_D0        0x00

//Addr: 0x3F, 20 chars & 4 lines
LiquidCrystal_I2C lcd(0x3F, 20, 4); 

// Buffer for string representation of temperature to send to LCD
char text[10];

void setup() {
  Fpu.begin(); 
  delay(1000);
  Fpu.reset(); 
  lcd.init(); 
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Please wait...");
}  

void loop() { 
  // Start 1-wire bus connected to um-FPU64 pin D0  
  Fpu.write(DEVIO, OWIRE, ENABLE, FPU64_PIN_D0, 0x00);          
  Fpu.write(DEVIO, OWIRE, RESET);
  // Find 1-wire devices on bus
  // Device addresses stored starting at reg DEV_ADDR (128 bit)
  // Device addresses stored starting with least significant byte
  Fpu.write(DEVIO, OWIRE, SEARCH, DEV_CNT, DEV_ADDR);    
  // Cycle though devices, get ID & temp reported by each
  for(int i = 0; i < DEV_CNT; i++) {
    // Need to reset bus each time a device is accessed
    Fpu.write(DEVIO, OWIRE, RESET);  
    // Select device to communicate with
    Fpu.write(DEVIO, OWIRE, SELECT, DEV_ADDR+i);
    // Command device to take temp reading & store
    // in device's scratchpad
    Fpu.write(DEVIO, OWIRE, WRITE_BYTE, CONVERT_T);
    // Wait for reading to complete
    delay(750);
    Fpu.write(DEVIO, OWIRE, RESET);
    Fpu.write(DEVIO, OWIRE, SELECT, DEV_ADDR+i);
    // Send command to read raw temp data from device's scratch pad
    Fpu.write(DEVIO, OWIRE, WRITE_BYTE, READ_SCRATCH_PAD);
    // Now load raw data into FPU's RAW_TEMP register for calculation    
    // Read 2 bytes starting with least significant byte (LSB)
    // This value is a long integer
    Fpu.write(DEVIO, OWIRE, READ_REG16_LSB_SE, RAW_TEMP);
    // Use the FPU TEMP_C register corresponding to device as accumulator.
    // Load value from RAW_TEMP register
    // Convert it to a float & divide by 16 to get Celsius
    Fpu.write(SELECTA, TEMP_C+i, LSET, RAW_TEMP, F_FLOAT, FDIVI, 16); 
    //FpuSerial.printFloat(84);
    //Serial.print(" C, ");
    // Convert to Fahrenheit in corresponding register
    Fpu.write(SELECTA, TEMP_F+i, FSET, TEMP_C+i, FMULI, 9);
    Fpu.write(FDIVI, 5, FADDI, 32);
    // If last device, calculate and print averages using FPU64's matrix functions 
    if(i == DEV_CNT-1) {
      //Serial.print("\n>>> AVG: ");
      // Define a 1-column matrix using registers with TEMP_C values
      Fpu.write(SELECTMA, TEMP_C, DEV_CNT, 1);
      // Matrix average function puts average in reg 0 -
      // move average to reg AVG_TEMP so that it is not overwritten in reg 0
      // Convert float to ASCII using 9 digit format with 5 decimal places
      Fpu.write(MOP, AVE, SELECTA, AVG_TEMP, FSET0, FTOA, 95);
      // Clear buffer for string representation of temperature
      memset(text, 0x0, sizeof text);
      Fpu.readString(text);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(text);
      lcd.print(" C");
      // Repeat with matrix of Fahrenheit temps
      Fpu.write(SELECTMA, TEMP_F, DEV_CNT, 1);
      Fpu.write(MOP, AVE, SELECTA, AVG_TEMP, FSET0, FTOA, 95);
      memset(text, 0x20, sizeof text);
      Fpu.readString(text);
      lcd.setCursor(0, 1);
      lcd.print(text);
      lcd.print(" F");
    }
  }
}

Monday, May 19, 2014

Resolving a Connectivity Problem with the Arduino WiFi Shield: Wireless Network Joined but Cannot Send or Receive Data

I had used the Arduino WiFi shield with an Arduino Uno successfully in the past, but a few months have gone by since I last did so.  It came as a surprise yesterday when I couldn't get the shield to work.  It connected to my wireless network without any problems, but I couldn't get it to connect to any other device or computer over the network or accept any connections.  Even the WiFi examples included in the IDE wouldn't work.  I had previously updated the shield's firmware (to version 1.1.0), so I didn't think that could be the issue.  I tried several versions of the Arduino IDE, but that didn't help either.

After downloading the latest version of the Arduino IDE (ver. 1.5.6 beta r2) and installing the firmware that comes with it, I have gotten the Arduino WiFi shield to work again.   Here are some notes on the process that worked for me (under Windows 8).

  1. Download the  AVR32 USB driver from http://www.dfrobot.com/image/data/TEL0064/AVR32%20USB%20Driver.zip.
  2. With the WiFi shield connected to the computer via USB and the J3 jumper in place (see the Arduino instructions for flashing the firmware to the WiFi shield), update the driver for the Atmel USB Device/AT32UC3A in the Device Manager (using the one downloaded in the previous step).
  3. When running the commands in the Arduino document to update the firmware, edit the paths for wifi_dnld.elf and  wifiHD.elf to point to the files in the Arduino IDE 1.5.6 installation.  The commands should look something like this -
    • batchisp.exe -device AT32UC3A1256 -hardware usb -operation erase f memory flash blankcheck loadbuffer D:/Arduino/hardware/arduino/avr/firmwares/wifishield/binary/wifi_dnld.elf program verify start reset 0
    • batchisp.exe -device AT32UC3A1256 -hardware usb -operation erase f memory flash blankcheck loadbuffer D:/Arduino/hardware/arduino/avr/firmwares/wifishield/binary/wifiHD.elf program verify start reset 0
    • Of course, your exact paths will most likely be different.  Just be sure to use slashes (/) rather than backslashes in the paths to the .elf files.


Thursday, May 8, 2014

RFID Using the Innovations ID20-LA with the Arduino Yun's USB Host Port

I've posted previously about using the Innovations ID20-LA and the Sparkfun RFID Reader breakout board with the Arduino Yun.  My earlier post used the serial break-out pins on the reader board to connect to the Yun using SoftwareSerial (on the Arduino side of the Yun).  It is also possible to use the USB connector on the reader board to connect to the Yun's USB host port.  The correct FTDI driver and Python pySerial library make this method very easy to use. In this case, the USB host is accessed via the Linux side of the Yun.

The following instructions assume that you are connected to the Yun via SSH. Log in using an account with root privileges.  You could also use the YunSerialTerminal example to access the command prompt.

First, make sure that your opkg package data is up to date:

opkg update

Then install the FTDI driver:

opkg install kmod-usb-serial-ftdi

After the FTDI support is installed, connect the reader and reader break-out board.  You should see the serial USB device in the device tree as /dev/ttyUSB0.

The following opkg command installs 

opkg install pyserial

You can run a quick test using the following Python code:

import serial
serial = serial.Serial("/dev/ttyUSB0", baudrate=9600)

code = ''

while True:
        data = serial.read()
        if data == '\r':
                print(code)
                code = ''
        else:
                code = code + data


Monday, May 5, 2014

Reading Barometric Pressure with an Arduino Galileo & BMP085 Pressure Sensor

I have an Adafruit BMP085 (now replaced by BMP180) pressure sensor that I wanted to use with an Arduino Galileo, but I couldn't get the code that I had used with an Uno to work with the Arduino Galileo - the readings were wildly wrong. The libraries from Adafruit would not compile for the Galileo.

Starting with the sample BMP085 Arduino code from Sparkfun, I have come up with the following code that reads the adjusted barometric pressure.  To keep this example simple,   the code prints the pressure (in inches of Mercury) to the serial console.

The key to getting correct readings with to change the numeric data types to portable C numeric types (int16_t, uint16_t, etc...).

This example also uses C++ style output streaming (to the serial console), as suggested by Mikal Hart in this post at the Arduino Playground.

This code also works with the Arduino Yun.

Connections


Adafruit BMP085  Galileo
3Vo              3.3V
SCL              SCL
SDA              SDA
GND              GND

Code


#include <Wire.h>

#define BMP085_ADDRESS 0x77  // I2C address of BMP085

// Provides C++ style output streaming
template<class T> inline Print &operator <<(Print &obj, T arg) { 
  obj.print(arg); return obj; 
}

const uint8_t OSS = 0;  // Oversampling Setting

// Calibration values
int16_t ac1;
int16_t ac2; 
int16_t ac3; 
uint16_t ac4;
uint16_t ac5;
uint16_t ac6;
int16_t b1; 
int16_t b2;
int16_t mb;
int16_t mc;
int16_t md;
int32_t b5; 
int16_t t;
int32_t p;
double tempF;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  bmp085Calibration();
}

void loop()
{
  t = bmp085GetTemperature(bmp085ReadUT());
  p = bmp085GetPressure(bmp085ReadUP());
  double pInHg = p * 0.000295333727; // convert to inches of mercury
  // Now adjust to sea level: Add 1200 Pa / 100 meters in elev.
  // Minneapolis is 264m above sea level. Converted to inches Hg this is 0.935
  pInHg += 0.935;
  pInHg = trunc(pInHg * 100)/100;
  
  // Print output to serial console
  Serial << "Pressure: " << pInHg << " in. Hg\n";
  
  delay(1000);
}

// Stores all of the bmp085's calibration values into global variables
// Calibration values are required to calculate temp and pressure
// This function should be called at the beginning of the program
void bmp085Calibration()
{
  ac1 = bmp085ReadInt(0xAA);
  ac2 = bmp085ReadInt(0xAC);
  ac3 = bmp085ReadInt(0xAE);
  ac4 = bmp085ReadInt(0xB0);
  ac5 = bmp085ReadInt(0xB2);
  ac6 = bmp085ReadInt(0xB4);
  b1 = bmp085ReadInt(0xB6);
  b2 = bmp085ReadInt(0xB8);
  mb = bmp085ReadInt(0xBA);
  mc = bmp085ReadInt(0xBC);
  md = bmp085ReadInt(0xBE);
}

// Calculate temperature given unadjusted temp ut.
// Value returned will be in units of 0.1 deg C
short bmp085GetTemperature(uint16_t ut)
{
  int32_t x1, x2;
  x1 = (((int32_t)ut - (int32_t)ac6) * (int32_t)ac5) >> 15;
  x2 = ((int32_t)mc << 11) / (x1 + md);
  b5 = x1 + x2;

  return ((b5 + 8)>>4);  
}

// Calculate pressure given unadjusted pressure up
// calibration values must be known
// b5 is also required so bmp085GetTemperature(...) must be called first.
// Value returned will be pressure in units of Pa.
int32_t bmp085GetPressure(uint32_t up)
{
  int32_t x1, x2, x3, b3, b6, p;
  uint32_t b4, b7;
  
  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12) >> 11;
  x2 = (ac2 * b6) >> 11;
  x3 = x1 + x2;
  b3 = (((((int32_t)ac1) * 4 + x3) << OSS) + 2) >> 2;
  
  // Calculate B4
  x1 = (ac3 * b6) >> 13;
  x2 = (b1 * ((b6 * b6) >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t)(x3 + 32768)) >> 15;
  
  b7 = ((uint32_t)(up - b3) * (50000 >> OSS));
  if (b7 < 0x80000000)
    p = (b7 << 1) / b4;
  else
    p = (b7 / b4) << 1;
    
  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  p += (x1 + x2 + 3791) >> 4;
  
  return p;
}

// Read 1 byte from the BMP085 at 'address'
int8_t bmp085Read(uint8_t address)
{
  uint8_t data;
  
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.requestFrom(BMP085_ADDRESS, 1);
  while(!Wire.available())
    ;
    
  return Wire.read();
}

// Read 2 bytes from the BMP085
// First byte will be from 'address'
// Second byte will be from 'address'+1
int16_t bmp085ReadInt(uint8_t address)
{
  uint8_t msb, lsb;
  
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.requestFrom(BMP085_ADDRESS, 2);
  while(Wire.available()<2)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  
  return (int16_t) msb<<8 | lsb;
}

// Read the uncompensated temperature value
uint16_t bmp085ReadUT()
{
  uint16_t ut;
  
  // Write 0x2E into Register 0xF4
  // This requests a temperature reading
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x2E);
  Wire.endTransmission();
  
  // Wait at least 4.5ms
  delay(5);
  
  // Read two bytes from registers 0xF6 and 0xF7
  ut = bmp085ReadInt(0xF6);
  return ut;
}

// Read the uncompensated pressure value
uint32_t bmp085ReadUP()
{
  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;
  
  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS << 6));
  Wire.endTransmission();
  
  // Wait for conversion, delay time dependent on OSS
  delay(2 + (3 << OSS));
  
  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF6);
  Wire.endTransmission();
  Wire.requestFrom(BMP085_ADDRESS, 3);
  
  // Wait for data to become available
  while(Wire.available() < 3)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  xlsb = Wire.read();
  
  up = (((uint32_t) msb << 16) | ((uint32_t) lsb << 8) | (uint32_t) xlsb) >> (8-OSS);
  
  return up;
}

Thursday, January 16, 2014

Note About Booting the Arduino Galileo from the SD Card

It always pays to read the instructions...

I've had my Arduino Galileo for a few days and wanted to get it to boot from the micro SD card so I could log in and get to the Linux command line via SSH.

I downloaded the Linux image and put it on the SD card as described in the Getting Started PDF, but it kept booting the built-in OS (which doesn't include SSH). The SD LED on the board would flicker a couple times briefly when powered on, but the board seemed to ignore the SD.

I "solved" the problem with booting from the SD card quickly and easily by upgrading the board's firmware using the Arduino IDE for Galileo, as described in section 6 on page 7 of the Galileo Getting Started PDF.