반응형

 

 

DS18B20와 Raspberry pi는 아래처럼 연결됩니다.

라즈비안에서 1-wire 통신에 대한 구현이 GPIO4 핀에 대해 커널 모듈로 구현되어 있기 때문에 이렇게 연결해줍니다.

1-wire 통신에서 bus의 idle 상태는 High level입니다. 따라서 VDD와 DQ 선 사이에 풀업 저항이 연결되어야 합니다.

 

 

다수의 DS18B20을 연결 시에는 다음 그림처럼 해주면 됩니다.

 

 

/boot/config.txt 파일을 열어서 마지막에 다음을 한 줄을 추가하고 라즈베리파이를 재부팅합니다.

dtoverlay=w1-gpio

 

이제 다음 명령을 이용하여 w1통신 관련 커널 모듈을 로드합니다.

$ sudo modprobe w1-gpio

$ sudo modprobe w1-therm

 

 

다음 명령을 사용하면 현재 라즈베리파이에 연결된 센서의 개수를 알 수 있습니다. 현재 1개의 센서가 연결되어 있습니다.

$ cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slave_count

1

 

다음 명령을 사용하면 현재 라즈베리파이에 연결된 센서들의 고유 아이디를 알 수 있습니다.

$ cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves

28-0000061796ff

 

다음 명령을 사용하면 ID가 28-0000061796ff인 센서의 값을 얻어 올 수 있습니다. 두번째 줄의 t=27312에서 27312가 온도를 나타내는 값으로 27.312도를 의미합니다.

$ cat /sys/bus/w1/devices/28-0000061796ff/w1_slave

b5 01 4b 46 7f ff 0b 10 a3 : crc=a3 YES

b5 01 4b 46 7f ff 0b 10 a3 t=27312

 

센서값을 읽어 올 수 있는 /sys/bus/w1/devices/28-0000061796ff/w1_slave 위치들을 찾아서 화면에 출력해주는  C언어 소스코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
 
 
 
#define PROBEPATH    "/sys/bus/w1/devices"      // Location of the probes
#define MAXPROBES    5                          // Max number of probes
#define PROBENAMELEN 80                         // Max length of the probenames including directory.
#define BUFSIZE      256                        // Typical charbuffer
 
char probepath[MAXPROBES][PROBENAMELEN];
char probename[MAXPROBES][PROBENAMELEN];
char alias[MAXPROBES][BUFSIZE];
FILE *probefd;
int numOfSensor;
 
 
///sys/bus/w1/devices 아래에 위치하는 센서의 ID값을 찾기 위한 함수
int findprobes(void)
{
        struct dirent *pDirent;
        DIR *pDir;
        int count;
 
        count = 0;
 
        pDir = opendir(PROBEPATH);
        if (pDir == NULL) {
                printf("Cannot open directory '%s'\n", PROBEPATH);
                return 0;
        }
 
        while ((pDirent = readdir(pDir)) != NULL)
        {
                //모든 DS18B20의 아이디는 28-로 시작한다.
                if (pDirent->d_name[0== '2' && pDirent->d_name[1== '8' && pDirent->d_name[2== '-')
                {
                        snprintf(probepath[count], PROBENAMELEN-1"%s/%s/w1_slave", PROBEPATH, pDirent->d_name);
                        snprintf(probename[count], PROBENAMELEN-1"%s", pDirent->d_name);
 
                        printf ("Found DS18B20 compatible probe named '%s':\nDevice file '%s'\n",
                                probename[count], probepath[count]);
                        count++;
                }
 
        }
 
        closedir(pDir);
 
        return count;
}
 
 
int main()
{
        int i;
        double temperature;
        char *temp;
        time_t now;
        struct tm *t;
        char buf[BUFSIZE];
 
 
        numOfSensor = findprobes(); //몇개의 센서가 연결되어 있는지 확인하고 그 위치를 저장한다.
        if (numOfSensor == 0)
        {
                printf("Error: No DS18B20 compatible probes located.\n");
                exit(-1);
        }
 
 
        while(1)
        {
                for (i = 0; i < numOfSensor; i++//발견된 센서수 만큼 반복한다.
                {
                        probefd = fopen(probepath[i], "r"); //probepath에 저장된 위치로부터 센서값을 읽어올 수 있다.
 
                        if (probefd == NULL)
                        {
                                printf"Error: Unable to open '%s': %s\n",
                                                probepath[i], strerror(errno));
                                exit(-1);
                        }
 
                        fgets(buf, sizeof(buf)-1, probefd); // 첫번째 줄은 무시 함
                        memset(buf, 0sizeof(buf));
 
                        fgets(buf, sizeof(buf)-1, probefd); //두번째 줄을 불러와서
                        temp = strtok(buf, "t="); //"t="뒤에 오는 온도값을 파싱 한다.
                        temp = strtok(NULL"t=");
                        temperature = atof(temp)/1000//1000으로 나누어야 원하 는 온도값이 됨
 
                        now = time(NULL); //현재 날짜 및 시간을 출력하기 위한 준비
                        t = localtime(&now);
 
                        printf("%s", probename[i]);
                        printf("\t%04d-%02d-%02d\t%02d:%02d:%02d\t",
                                t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
                                t->tm_hour, t->tm_min, t->tm_sec );
                        printf("%2.3f\n",temperature);
 
                        fclose(probefd);
 
 
                }
        }
 
        return 0;
}
cs

 





이번엔 C언어 프로그램에서 데이터베이스에 온도값을 저장하도록 하고  웹에서 이 값을 가져와 그래프로 그려보도록 하겠습니다...


우선 라즈베리파이에 apache2 + mydql + php5가 설치되어 있어야 합니다.

[임베디드/Raspberry Pi] - 라즈베리파이에 LAMP (Linux, Apache, MySQL, PHP) 웹서버 설치


데이터를 저장할때 사용할 데이터베이스 및 테이블을 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pi@raspberrypi:~ $ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 58
Server version: 5.5.44-0+deb8u1 (Raspbian)
 
Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
mysql> create database testdb;
Query OK, 1 row affected (0.00 sec)
 
mysql> use testdb;
Database changed
mysql> create table data(
    -> datetime datetime,
    -> tempture double
    -> );
Query OK, 0 rows affected (0.02 sec)
 
mysql> exit
Bye
cs


C언어에서  mysql의 데이터베이스에 접근하기 위해 필요한 패키지를 설치해줍니다.

1
pi@raspberrypi:~ $ sudo apt-get install libmysqlclient-dev
cs


다음 코드를 ds18b20_db.c로 저장 후.. 아래처럼 컴파일을 합니다.

pi@raspberrypi:~/ds18b20 $ gcc -o main ds18b20_db.c  `mysql_config --cflags --libs`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <my_global.h>
#include <mysql.h>
 
 
#define PROBEPATH    "/sys/bus/w1/devices"      // Location of the probes
#define MAXPROBES    5                          // Max number of probes
#define PROBENAMELEN 80                         // Max length of the probenames including directory.
#define BUFSIZE      256                        // Typical charbuffer
 
char probepath[MAXPROBES][PROBENAMELEN];
char probename[MAXPROBES][PROBENAMELEN];
char alias[MAXPROBES][BUFSIZE];
FILE *probefd;
int numOfSensor;
 
 
///sys/bus/w1/devices 아래에 위치하는 센서의 ID값을 찾기 위한 함수
int findprobes(void)
{
        struct dirent *pDirent;
        DIR *pDir;
        int count;
 
        count = 0;
 
        pDir = opendir(PROBEPATH);
        if (pDir == NULL) {
                printf("Cannot open directory '%s'\n", PROBEPATH);
                return 0;
        }
 
        while ((pDirent = readdir(pDir)) != NULL)
        {
                // All DS18B20 have filenames starting with 28-
                if (pDirent->d_name[0== '2' && pDirent->d_name[1== '8' && pDirent->d_name[2== '-')
                {
                        snprintf(probepath[count], PROBENAMELEN-1"%s/%s/w1_slave", PROBEPATH, pDirent->d_name);
                        snprintf(probename[count], PROBENAMELEN-1"%s", pDirent->d_name);
 
                        printf ("Found DS18B20 compatible probe named '%s':\nDevice file '%s'\n",
                                probename[count], probepath[count]);
                        count++;
                }
 
        }
 
        closedir(pDir);
 
        return count;
}
 
 
int main()
{
        int i;
        double temperature;
        char *temp;
        time_t now;
        struct tm *t;
        char buf[BUFSIZE];
 
 
        MYSQL *con = mysql_init(NULL);
        if (con == NULL )
        {
                fprintf( stderr, "%s\n", mysql_error(con));
                exit(1);
        }
 
 
        if ( mysql_real_connect(con, "localhost""root",
                "12345678""testdb"0NULL0 )== NULL)
        {
                fprintf(stderr, "%s", mysql_error(con));
                exit(1);
        }
 
 
 
        numOfSensor = findprobes(); //몇개의 센서가 연결되어 있는지 확인하고 그 위치를 저장한다.
        if (numOfSensor == 0)
        {
                printf("Error: No DS18B20 compatible probes located.\n");
                exit(-1);
        }
 
 
        while(1)
        {
                for (i = 0; i < numOfSensor; i++//발견된 센서수 만큼 반복한다.
                {
                        probefd = fopen(probepath[i], "r"); //probepath에 저장된 위치로부터 센서값을 읽어올 수 있다.
 
                        if (probefd == NULL)
                        {
                                printf"Error: Unable to open '%s': %s\n",
                                                probepath[i], strerror(errno));
                                exit(-1);
                        }
 
                        fgets(buf, sizeof(buf)-1, probefd); // 첫번째 줄은 무시 함
                        memset(buf, 0sizeof(buf));
 
                        fgets(buf, sizeof(buf)-1, probefd); //두번째 줄을 불러와서
                        temp = strtok(buf, "t="); //"t="뒤에 오는 온도값을 파싱 한다.
                        temp = strtok(NULL"t=");
                        temperature = atof(temp)/1000//1000으로 나누어야 원하 는 온도값이 됨
 
                        now = time(NULL); //현재 날짜 및 시간을 출력하기 위한 준비
                        t = localtime(&now);
 
                        printf("%s", probename[i]);
                        printf("\t%04d-%02d-%02d\t%02d:%02d:%02d\t",
                                t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
                                t->tm_hour, t->tm_min, t->tm_sec );
                        printf("%2.3f\n",temperature);
 
                        fclose(probefd);
 
                        char datetime[256];
 
                        sprintf(datetime, "%04d-%02d-%02d %02d:%02d:%02d",
                                t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
                                t->tm_hour, t->tm_min, t->tm_sec );
 
                        sprintf( buf, "insert into data values( '%s', %2.3f )", datetime, temperature );
                        printf"%s\n", buf );
                        if( mysql_query( con, buf)){
                                fprintf(stderr, "%s\n", mysql_error(con));
                                mysql_close(con);
                                exit(1);
                        }
 
 
                }
        }
 
        return 0;
}
cs


프로그램을 실행하고  phpmyadmin에서 확인해보면 데이터가 들어가고 있는 것을 볼 수 있습니다. 



그래프를 그리기 위해 추가로 설치해야 하는 패키지들입니다.

sudo apt-get install php5-gd  php5-cli


/etc/php5/apache2/conf.d/20-gd.ini 파일을 열어서

앞에  세미콜론이 있으면 아래처럼 ; 제거합니다.
; configuration for php GD module
priority=20
extension=gd.so


아파치 서버를 재시작합니다.

sudo /etc/init.d/apache2 restart


http://www.pchart.net/download 에서 라이브러리를 다운로드 받아서 웹서버의 루트 디렉토리로 옮깁니다.

 wget http://www.pchart.net/release/pChart2.1.4.tar.gz

tar xvzf pChart2.1.4.tar.gz

 sudo mv pChart2.1.4 /var/www/html/pChart



이제  그래프를 그려줄 php를 작성합니다.

sudo nano /var/www/html/test.php



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
include("pChart/class/pDraw.class.php");
include("pChart/class/pImage.class.php");
include("pChart/class/pData.class.php");
 
 
$db = mysql_connect("127.0.0.1""root""tksxldkrh2014!");
if ( $db == "" ) { echo " DB Connection error...\r\n"exit(); }
 
mysql_select_db("testdb",$db);
 
 
 
$Requete = "SELECT * FROM `data` order by datetime desc limit 30";
$result  = mysql_query($Requete,$db);
while ($row = mysql_fetch_array($result))
{
   $Temperature[] = $row["tempture"];
   $DataTime[] = $row["datetime"];
}
 
$DataSet = new pData();
$DataSet->addPoints( $Temperature"Temperature");
$DataSet->addPoints($DataTime,"Timestamp");
 
$DataSet->setAbscissa("Timestamp");
 
$DataSet->setXAxisName("Time");
$DataSet->setAxisName(0,"Temperature");
$DataSet->setAxisUnit(0,"°C");
 
 
 
 
$myPicture = new pImage(1024,600,$DataSet);
$myPicture->setGraphArea(100,60,1000,500);
 
$myPicture->setFontProperties(array("FontName"=>"pChart/fonts/verdana.ttf","FontSize"=>11));
 
 
 $scaleSettings = array("LabelRotation"=>30,
        "XMargin"=>10,"YMargin"=>10,
        "Floating"=>TRUE,"GridR"=>200,"GridG"=>200,"GridB"=>200,
        "DrawSubTicks"=>TRUE,"CycleBackground"=>TRUE );
 $myPicture->drawScale($scaleSettings);
$myPicture->drawSplineChart();
$myPicture->drawLegend(2000,500,array("Style"=>LEGEND_NOBORDER,"Mode"=>LEGEND_HORIZONTAL));
$myPicture->Stroke();
?>
cs


웹브라우저에서 확인해본 결과입니다. 최근 30개의 데이터만을 보여줍니다..

1초마다 갱신하도록 하면 그래프가 변하는 것도 볼 수 있습니다.



참고

http://kmtronic.com/raspberry-pi-ds18b20-connect-to-gpio.html

http://iot-projects.com/index.php?id=connect-ds18b20-sensors-to-raspberry-pi

http://raspberrypi.stackexchange.com/questions/26623/ds18b20-not-listed-in-sys-bus-w1-devices

https://github.com/richard-allen/ds18b20

http://zetcode.com/db/mysqlc/




반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

  1. joonhwan 2016.06.19 19:19

    온도 센서를 연결하고, $ cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slave_count 명령어를 입력해도 갯수가 0으로 뜨는데 왜이럴까요? ㅜㅜ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2016.06.21 10:11 신고

      아래 두가지를 해주었는데도 안되나요?

      /boot/config.txt 파일을 열어서 마지막에 다음을 한 줄을 추가하고 라즈베리파이를 재부팅합니다.
      dtoverlay=w1-gpio

      이제 다음 명령을 이용하여 w1통신 관련 커널 모듈을 로드합니다.
      $ sudo modprobe w1-gpio
      $ sudo modprobe w1-therm

  2. subin 2017.06.12 19:35

    안녕하세요 제가 이 게시물을 보고 ds18b20을 사용해서 작동해봤는데요
    작동이 되게 잘 됐었거든요!! 근데 갑자기 작동이 안되는거예요
    그래서 다시 처음부터 해보려고 했는데

    한 경우는
    센서를 라즈베리파이에 연결하고나서
    $ cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slave_count
    를 하면 언제는 2개 언제는 3개가 나오고 라즈베리파이를 재부팅시키면 1개로 뜨고
    $ cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves
    이거를 하면 28-xxxxx가 나오지 않고 00-200000이나 00-c000000이 나옵니다.

    두번째 경우는
    작동이 되는데 온도가 계속 85도에 멈춰서
    내려가지도 올라가지도 않습니다.

    왜그럴까요ㅜㅜ? ㅜㅠㅠ제발 알려주세요

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.06.14 09:36 신고

      처음엔 잘되다가 나중에 안된걸 봐서는
      원인을 찾기가 힘들듯하네요.

      여분의 sd 카드가 있다면 라즈비안을 재설치해서 해보는게 좋을듯합니다.

  3. alpa 2019.06.08 16:30

    혹시 여기서 불러온 값을 가지고 선풍기를 가져오려고 하다면 어찌해야 할까요??
    28도 이상이면 팬이 작동하는 자동 선풍기를 만들려고 하는데 너무 어렵네요 ㅠㅠ

  4. 도와주세요 2019.12.06 17:49

    안녕하세요 잘보았습니다.
    센서 개수와 아이디까지 확인되었고, 온도도 확인되었습니다.
    그런데, 소스코드를 main.c로 입력시키고 돌려보았는데,
    계속 /tmp/geany_run_script_~~~~~~.sh 이런식으로 나오고 맨뒤에 not pound라고 나옵니다.
    무엇이 문제일까요? (참고로 ~~~~~는 돌릴때마다 바뀌네요)

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.06 21:08 신고

      구글에서 에러 메시지를 검색해볼 수 밖에 없어보입니다.

  5. 살려주세요 2019.12.16 23:24

    간단하게 따라서 코딩을 해보고 있는데, No such file or directory 라고 FD 를 못찾고 에러 메세지가 뜹니다 ㅠㅠ 어떻게 해야할까요?

+ Recent posts