반응형

 

 

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/




반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


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

+ Recent posts