Wednesday, July 16, 2014

C# (Mono) Program to Read an TMP102 Temperature Sensor on a pcDuino 2

Mono is an open source implementation of Microsoft's .NET framework.  It is actually very easy to install Mono on the pcDuino 2 and get up and running with a fairly simple C# program that uses the i2cget utility to read the TMP102 temperature registers, convert it to Celsius and Fahrenheit, and print the results to the console.

For this example, I am using the TMP102 on a breakout board from Sparkfun.

Installing Mono on the pcDuino 2


To install Mono, simply run the following apt-get command as root:

apt-get install mono-complete

It will take a few minutes to download and install, but it isn't any more complicated than that.

You can confirm that your Mono installation is working by checking the version using the mono command:

mono --version

The results should look something like this -

root@ubuntu:~# mono --version
Mono JIT compiler version 2.10.8.1 (Debian 2.10.8.1-1ubuntu2.2)
Copyright (C) 2002-2011 Novell, Inc, Xamarin, Inc and Contributors. www.mono-project.com
TLS:           __thread
SIGSEGV:       normal
Notifications: epoll
Architecture:  armel,vfp
Disabled:      none
Misc:          softdebug 
LLVM:          supported, not enabled.
GC:            Included Boehm (with typed GC and Parallel Mark)
root@ubuntu:~# 


The pcDuino Web site also has information on installing smaller Mono configurations.


Installing I2C-Tools


This example uses i2cget to access the TMP102.  This program is part of the i2c-tools package.  You can install it (if you don't have already installed) by running this command as root:

apt-get install i2c-tools

C# Code


Use your favorite text editor to save this code to a file called Tmp102.cs

using System;
using System.Diagnostics;
using System.Threading;

public class Tmp102
{
    private string i2cgetExe = "/usr/sbin/i2cget";
    private string i2cgetCmdArgs = "-y 2 0x48 0 w";
    private string hexString = "";
    private Process p;

    public Tmp102()
    {
        p = new Process();
    }

    public double tempC
    {
        get { return readRawTempData() * 0.0625; }
    }
    public double tempF
    {
        get { return this.tempC * 1.8 + 32; }
    }

    private int readRawTempData()
    {
         // Don't raise event when process exits
         p.EnableRaisingEvents = false;
         // We're using an executable not document, so UseShellExecute false
         p.StartInfo.UseShellExecute = false;
         // Redirect StandardError
         p.StartInfo.RedirectStandardError = true;
         // Redirect StandardOutput so we can capture it
         p.StartInfo.RedirectStandardOutput = true;
         // i2cgetExe has full path to executable
         // Need full path because UseShellExecute is false
         p.StartInfo.FileName = i2cgetExe;
         // Pass arguments as a single string
         p.StartInfo.Arguments = i2cgetCmdArgs;
         // Now run i2cget & wait for it to finish
         p.Start();
         p.WaitForExit();
         // Data returned in format 0xa017
         // Last 2 digits are actually most significant byte (MSB)
         // 2 digits right after 0x are really least significant byte (LSB)
         string data = p.StandardOutput.ReadToEnd();
         // Get LSB & parse as integer
         hexString = data.Substring(2, 2);
         // Redirect StandardOutput so we can capture it
         p.StartInfo.RedirectStandardOutput = true;
         // i2cgetExe has full path to executable
         // Need full path because UseShellExecute is false

         p.StartInfo.FileName = i2cgetExe;
         // Pass arguments as a single string
         p.StartInfo.Arguments = i2cgetCmdArgs;
         // Now run i2cget & wait for it to finish
         p.Start();
         p.WaitForExit();
         // Data returned in format 0xa017
         // Last 2 digits are actually most significant byte (MSB)
         // 2 digits right after 0x are really least significant byte (LSB)
         string data = p.StandardOutput.ReadToEnd();
         // Get LSB & parse as integer
         hexString = data.Substring(2, 2);
     int lsb = Int32.Parse(hexString,
        System.Globalization.NumberStyles.AllowHexSpecifier);
     // Get MSB & parse as integer
     hexString = data.Substring(4, 2);
     int msb = Int32.Parse(hexString,
        System.Globalization.NumberStyles.AllowHexSpecifier);
     // Shift bits as indicated in TMP102 docs & return
     return (((msb << 8) | lsb) >> 4);
    }

    public static void Main()
    {
         Tmp102 t = new Tmp102();
        while(true)
        {
            // Print temp in degrees C and F to console
            Console.WriteLine("{0} C  {1} F", t.tempC , t.tempF);
                     Thread.Sleep(1000);
        }
        }
}

Compiling & Running the Code


To compile the code, run this command:

gmcs Tmp102.cs

This will produce a Tmp102.exe file that you can then run using mono.

mono Tmp102.exe

The program will print the current temperature until you press Ctrl-C to stop it. 


Tuesday, July 1, 2014

Java Program to Read 2 TMP102 Temperature Sensors with the Same Address Connected to Separate Buses on a Beaglebone Black

I made a post on June 28th, "Using 2 I2C Buses on the Beaglebone Black," that shows how to use the two I2C buses available on the Beaglebone Black to read from 2 TMP102 sensors with the same I2C device address.  The post below offers a fairly simple Java 7/8 example that does the same thing.  As noted in my previous post, it is certainly possible to change the address of the TMP102, but the new version of the TMP102 from Sparkfun has a solder jumper that has to be removed before you can change the address.  This example might be useful when working with other sensors that don't allow you to change the device's I2C addresses.

I have tested the code below with Java 8 on the Beaglebone Black (rev. C) running Debian Linux.  It should also run on Java 7.

This example assumes that you are working at the command line in a terminal window or via an SSH connection.

By default, there is only 1 I2C bus accessible via the P9 pins on the Beaglebone Black.  You need to load the BB-I2C1 pin map overlay.  The sample program below handles this for you.

Last year, I posted an example of Java code that read the temperature from a TMP102 using JNA and a simple C program.  The code below uses Java's Runtime class to run the i2cget utility in order to reduce complexity.

Connections


TMP102 #1 Beaglebone Black
VCC       3.3V
GND       GND
SDA       P9 20
SCL       P9 19
ADD0      (Not connected)

TMP102 #2 Beaglebone Black
VCC       3.3V
GND       GND
SDA       P9 18
SCL       P9 17
ADD0      (Not connected)

Java Code  


import java.io.*;
import java.util.Scanner;

public class TwoTmp102 {
   // Linux shell command that loads pin map overlay with both I2C
   // buses connected to I/O pins on the Beaglebon Black.
   // Shell echo command must be passed as array of Strings.
   private static String[] loadPinMapCmd = { "bash", "-c",
      "echo BB-I2C1 > /sys/devices/bone_capemgr.9/slots" };

   // 2 i2cget commands to read raw data as a word (16-bit value)
   // from both TMP102 sensors.  -y tells command to run 
   // without confirmation from user.
   // I2C address for TMP102 by default is 0x48.
   // Each command is passed a separate String, unlike loadPinMapCmd
   // that has to be passed as an array of Strings.
   private String[] runI2CGetCmd = {"i2cget -y 1 0x48 0 w",
      "i2cget -y 2 0x48 0 w"};

   public TwoTmp102() {
      try {
         Process p = Runtime.getRuntime().exec(loadPinMapCmd);
         p.waitFor();
      }
      catch(Exception ex) { ex.printStackTrace(); }
   }

   // Read temperatures from both sensors & print it out to terminal
   public void readTemperatures() {
      short temp1Data, temp2Data = 0;
      double temp1, temp2 = 0.00;
      temp1Data = runI2CGet(runI2CGetCmd[0]);
      temp2Data = runI2CGet(runI2CGetCmd[1]);
      temp1Data = reverseBytes(temp1Data);
      temp2Data = reverseBytes(temp2Data);
      temp1 = convertToDegreesF(temp1Data);
      temp2 = convertToDegreesF(temp2Data);
      System.out.printf("%.3f  %.3f\n", temp1, temp2);
   }

   // Need to reverse bytes in value returned by i2cget command
   private short reverseBytes(short tempData) {
      int b1 = (byte)  tempData & 0xFF;
      int b2 = (byte) (tempData >> 8) & 0xFF;
      return (short) (b1 << 8 | b2);
   }

   // Convert raw temperature data to degrees Fahrenheit
   private double convertToDegreesF(short tempData) {
      return (tempData >> 4) * 0.0625 * 1.8 + 32;
   }

   private short runI2CGet(String cmd) {
      String line;
      short data = 0;
      try {
         Process p = Runtime.getRuntime().exec(cmd);
         p.waitFor();
         // Use Scanner to read results from i2cget command
         Scanner scan = new Scanner(
            new InputStreamReader(p.getInputStream()));
         // Shell command returns hex number as string like 0x6017.
         // Need to use substring to skip over 0x for parsing.
         // Using Short.parseShort() sometimes throws NumberFormatExceptions.
         if(scan.hasNext()) {
            data = (short) Integer.parseInt((scan.nextLine())
               .substring(2), 16);
         }
      }
      catch(Exception ex) { ex.printStackTrace(); }
      return data;
   }

   public static void main(String[] args) {
      TwoTmp102 temp = new TwoTmp102();
      while(true) {
         try {
            temp.readTemperatures();
            Thread.sleep(1000);
         }
         catch(Exception ex) { ex.printStackTrace(); }
      }
   }
}

Compiling & Running the Program

The program is very easy to compile -

javac TwoTmp102.java

To run the program (assuming that it is in the current working directory) -

java -cp . TwoTmp102

End the program using Ctrl-C.