Sunday, December 15, 2013

Reading Multiple 1-Wire Temperature Sensors Using Java on a Raspberry Pi

This example shows how a Java program can read temperature data from multiple DS18B20 1-wire temperature sensors.  This program prints the device number and temperature from each device in a continuous loop.  It works with Java 7 or Java 8.

Before running the program below, be sure to run the following commands to load the needed kernel modules -

sudo modprobe w1-gpio
sudo modprobe w1-therm


Connections


Looking at the flat side of the DS18B20's plastic head, connect the left pin to ground, the right pin to 3V3, and the center pin to GPIO4.  A 4.7k Ohm pull-up resistor is required on the connection of the first sensor's center pin to GPIO4.  If using multiple sensors, each needs to be connected to the voltage and ground; parasitic power mode does not seem to be supported.  The center (data) pins need to be connected together (with the pull-up on the connection to the Raspberry Pi).


Java Code



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

public class w1 {
  //This directory created by 1-wire kernel modules
  static String w1DirPath = "/sys/bus/w1/devices";

  public static void main(String[] argv) throws Exception {
    File dir = new File(w1DirPath);
    File[] files = dir.listFiles(new DirectoryFileFilter());
    if (files != null) {
      while(true) {
        for(File file: files) {
          System.out.print(file.getName() + ": ");
  // Device data in w1_slave file
          String filePath = w1DirPath + "/" + file.getName() + "/w1_slave";
          File f = new File(filePath);
          try(BufferedReader 
              br = new BufferedReader(new FileReader(f))) {
            String output;
            while((output = br.readLine()) != null) {
              int idx = output.indexOf("t=");
              if(idx > -1) {
                // Temp data (multiplied by 1000) in 5 chars after t=
                float tempC = Float.parseFloat(
                    output.substring(output.indexOf("t=") + 2));
                // Divide by 1000 to get degrees Celsius
tempC /= 1000;
System.out.print(String.format("%.3f ", tempC));
float tempF = tempC * 9 / 5 + 32;
System.out.println(String.format("%.3f", tempF));
              }
            }
          }
          catch(Exception ex) {
            System.out.println(ex.getMessage());
          }
        }
      }
   } 
}

// This FileFilter selects subdirs with name beginning with 28-
// Kernel module gives each 1-wire temp sensor name starting with 28-
class DirectoryFileFilter implements FileFilter
{
   public boolean accept(File file) {
     String dirName = file.getName();
     String startOfName = dirName.substring(0, 3);
     return (file.isDirectory() && startOfName.equals("28-"));
   }
}  


Friday, December 6, 2013

C Program to Read Temperature from a 1-Wire Temperature Sensor Connected to a Raspberry Pi

The following example shows how to read the temperature from a single DS18B20 1-wire temperature sensor connected to a Raspberry Pi using code written in C.  The program reads and prints the temperature to the console until the user ends the program with a ctrl-c.  (It should be possible to read from multiple sensors chained together, but this example just uses one for simplicity).

I found this post by Matt Hawkins at Raspberry Pi Spy very helpful.

For multiple DS18B20s, see this post.

This code relies on two kernel modules that must be loaded by the following commands before the code is run:

sudo modprobe w1-gpio
sudo modprobe w1-therm

These modules make it possible to access the 1-wire sensor via the Linux file system.

Connections


Looking at the flat side of DS18B20, connect it to the Raspberry Pi as follows.

DS18B20 Pin   Raspberry Pi (Rev. B)
Left          GND
Center        GPIO4 (w/ 4.7k pull-up resistor)
Right         3V3

C Code


#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
     
int main (void) {
DIR *dir;
struct dirent *dirent;
char dev[16];      // Dev ID
char devPath[128]; // Path to device
char buf[256];     // Data from device
char tmpData[6];   // Temp C * 1000 reported by device 
char path[] = "/sys/bus/w1/devices"; 
ssize_t numRead;

dir = opendir (path);
if (dir != NULL)
{
while ((dirent = readdir (dir)))
// 1-wire devices are links beginning with 28-
if (dirent->d_type == DT_LNK && 
strstr(dirent->d_name, "28-") != NULL) { 
strcpy(dev, dirent->d_name);
printf("\nDevice: %s\n", dev);
}
(void) closedir (dir);
        }
else
{
perror ("Couldn't open the w1 devices directory");
return 1;
}

        // Assemble path to OneWire device
sprintf(devPath, "%s/%s/w1_slave", path, dev);
// Read temp continuously
// Opening the device's file triggers new reading
while(1) {
int fd = open(devPath, O_RDONLY);
if(fd == -1)
{
perror ("Couldn't open the w1 device.");
return 1;
}
while((numRead = read(fd, buf, 256)) > 0) 
{
strncpy(tmpData, strstr(buf, "t=") + 2, 5);
float tempC = strtof(tmpData, NULL);
printf("Device: %s  - ", dev);
printf("Temp: %.3f C  ", tempC / 1000);
printf("%.3f F\n\n", (tempC / 1000) * 9 / 5 + 32);
}
close(fd);
}
        /* return 0; --never called due to loop */
}

Compiling & Running 


Assuming the code is saved in a file called w1.c, run the following tom compile - 

gcc -Wall -std=gnu99 w1.c -o w1

Run the code by typing ./w1.  Press ctrl-c to end the program.