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.

27 comments:

  1. I tried to run your code but got an error "'DT_LNK' undeclared". Where is this defined?

    ReplyDelete
  2. Replies
    1. Hi Erik -

      I'm glad to hear it worked for you.

      Delete
  3. Hi Brad,

    This code works fine. Many thanks for this wonderful piece of work.

    ReplyDelete
  4. Hi

    How i can print just celsius ?

    ReplyDelete
    Replies
    1. You can just leave out the line that prints Fahrenheit ( printf("%.3f F\n\n", (tempC / 1000) * 9 / 5 + 32); )

      Delete
    2. thanx...
      do you have facebook or email?

      Delete
    3. No, at least not that I give out.

      Delete
  5. Brad,

    I found your example very useful, and would like to use this within my capstone project for school. Do I have your permission to use this 1-wire code example in my project?

    ReplyDelete
    Replies
    1. Hi Alex -

      That is fine with me. You have my permission.

      --Brad

      Delete
  6. I found a bug in your program :-).
    Because for temperatures above + 100 °C and below -10 °C, the result is 10 times smaller. eg. + 101 °C is + 10.1 °C. This is due to a greater number of characters.
    In your version, the program reads only 5 characters:
    "strncpy (tmpData, strstr (buf," t = ") + 2, 5);"
    I corrected on:
    "strcpy (tmpData, strstr (buf," t = ") + 2);"

    But further still this is a very good example.
    Good job !!! thanks

    Best Regards
    Jarek

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hallo,

    Here we are reading the value by opening the FD to read from driver, is there any other way to read the value apart from opening and closing the fd to read the value ?

    Regards
    Kiran Mathews

    ReplyDelete
  9. I made just what you said and the program was compiled but it only shows "Couldn't open the w1 device: No such file or directory". What can I do to fix it?

    ReplyDelete
    Replies
    1. Have a look at the post on Matt Hawkins blog (link is near the start of the post). It sounds like you may not have the 1-wire drivers installed.

      --Brad

      Delete
  10. Hey Brad,
    how do I have to change the code if I want to use more than one sensor

    kind regards

    ReplyDelete
  11. This blog awesome and i learn a lot about programming from here.The best thing about this blog is that you doing from beginning to experts level.

    Love from

    ReplyDelete
  12. Hey Brad i'm using your programm and it works fine for me for reading multiple Sensors. But I have a problem with the usleep functions. This interrupts my main and the PWM function. (Flashing LED STRIP)

    Is there any chance to modify the programm without interrupting the mainroutine? THANK U VERY MUCH

    ReplyDelete
  13. Hi. How can I make it without a loop? I struggle with it. I get segmentation problems

    ReplyDelete
  14. That code really need some more comments, would make it easier and faster to understand

    ReplyDelete
  15. Hi,
    I am trying to include this code in Simulink using SFunction builder in order to process it for my next project. I've getting an error in MATLAB - "cc1: fatal error: DS18B20_Trial2.c.d: No such file or directory
    compilation terminated.
    make: *** [DS18B20_Trial2.c.o] Error 1"
    Any idea to solve this?
    Thank You. Waiting for your reply

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I sorted my coding error, but wanted to thank you for posting the code.

      Delete
  17. A small percentage of the samples from a DS18B20 would be reported at 10 times the value and with an extra digit, ie 21.2 would become 212.5

    I found that changing to:

    char tmpData[7]; // Temp C * 1000 reported by device
    strncpy(tmpData, strstr(buf, "t=") + 2, 6);

    appeared to reduce or eliminate this problem. I have no idea why.

    ReplyDelete
  18. Thank you for posting this. You are the good stuff on the web.
    Bob G

    ReplyDelete