Tuesday, June 3, 2014

C Program to Read Multiple DS18B20 1-Wire Temperature Sensors on a Beaglebone Black

Here is a fairly simple C program that reads from multiple DS18B20 One-Wire temperature sensors and prints the results (device ID and temperature for each) to the terminal.  This example uses a linked list to keep track of the attached sensors rather than fixed arrays.

For help getting started with One-Wire on the Beaglebone Black (including loading the pin configuration), see this post at Hipster Circuits.  This configuration is required before continuing.

Connections


Looking at the flat side of the DS18B20's plastic head, connect the left pin to ground on the Beaglebone Black (P9-1), the right pin to 3V3 (P9-3), and the center pin to P9-22.  A 4.7k Ohm pull-up resistor is required on the connection of the first sensor's center pin to P9-22.  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 BB Black).

C Code


#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
     
// struct to hold ds18b20 data for linked list
struct ds18b20 {
char devPath[128];
char devID[16];
char tempData[6];
struct ds18b20 *next;
};

// Find connected 1-wire devices. 1-wire driver creates entries for each device 
// in /sys/bus/w1/devices on Beaglebone Black.  Create linked list.
int8_t findDevices(struct ds18b20 *d) {
DIR *dir;
        struct dirent *dirent;  
struct ds18b20 *newDev;
        char path[] = "/sys/bus/w1/devices";
        int8_t i = 0;
        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) {
newDev = malloc( sizeof(struct ds18b20) );
                                strcpy(newDev->devID, dirent->d_name);
                                // Assemble path to OneWire device
                                sprintf(newDev->devPath, "%s/%s/w1_slave", path, newDev->devID);
                                i++;
newDev->next = 0;
d->next = newDev;
d = d->next;
                        }
}
(void) closedir(dir);
        }
        else
{
                perror ("Couldn't open the w1 devices directory");
                return 1;
        }
return i;
}

int8_t readTemp(struct ds18b20 *d) {
while(d->next != NULL){
d = d->next;
int fd = open(d->devPath, O_RDONLY);
if(fd == -1)
        {
        perror ("Couldn't open the w1 device.");
                return 1;
        }
char buf[256];
ssize_t numRead;
        while((numRead = read(fd, buf, 256)) > 0) {
                strncpy(d->tempData, strstr(buf, "t=") + 2, 5);
                float tempC = strtof(d->tempData, NULL);
                printf("Device: %s  - ", d->devID);
                printf("Temp: %.3f C  ", tempC / 1000);
                printf("%.3f F\n\n", (tempC / 1000) * 9 / 5 + 32);
        }
        close(fd);
}
return 0;
}

int main (void) {
struct ds18b20 *rootNode;
struct ds18b20 *devNode;
// Load pin configuration. Ignore error if already loaded
system("echo BB-W1 > /sys/devices/bone_capemgr.9/slots 2>/dev/null");
while(1) {
rootNode = malloc( sizeof(struct ds18b20) );
devNode = rootNode;
int8_t devCnt = findDevices(devNode);
printf("\nFound %d devices\n\n", devCnt);
readTemp(rootNode);
// Free linked list memory
while(rootNode) {
// Start with current value of root node
devNode = rootNode;
// Save address of next devNode to rootNode before deleting current devNode
rootNode = devNode->next;
// Free current devNode.
free(devNode);
}
// Now free rootNode
free(rootNode); 
}
return 0;
}

Compiling & Running the Code

Assuming you have the code above saved in a file called w1m.c, run the following command at the Beaglebone Black's command line to compile it and produce an executable called w1m

 gcc -Wall -o w1m w1m.c

The code should compile without warnings or errors.  You can then run ./w1m to see the output.  The program reports the number of sensors found and then lists the ID and temperature reading for each one.  If you add a sensor to the circuit, it should be detected in about a minute.  If you remove a sensor or two, they will be dropped from the list after a couple minutes, but will show a false temperature near freezing until the driver removes the entry for the disconnected sensor(s).

Press Ctrl-C to stop execution.

3 comments:

  1. If I have pin P9 -22 in use already is there another pin I can use?

    ReplyDelete
  2. How can I take this and make it enter data into a mysql database?

    ReplyDelete
  3. I suggest to modify the code also to support DS18S20 devices by searching for "10-" since it is substitute for DS18B20.

    ReplyDelete