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, 0, sizeof(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", 0, NULL, 0 )== 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, 0, sizeof(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 파일을 열어서
아파치 서버를 재시작합니다.
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
'Raspberry Pi > Raspberry Pi 활용' 카테고리의 다른 글
Raspberry PI 2/3 와 Arduino를 연결하여 I2C 테스트 (17) | 2016.06.04 |
---|---|
raspberry pi에서 pybluez 라이브러리를 이용하여 스마트폰의 블루투스 rssi값 출력하기 (3) | 2016.05.26 |
raspberry pi 3에 wxpython 설치 및 태스트 (4) | 2016.05.23 |
Raspberry pi 3 시리얼 통신으로 연결하기 (2) | 2016.04.13 |
Node.js를 이용하여 웹브라우저에서 라즈베리파이2에 연결된 LCD에 문자열 전송하기 ( POST 방식 ) (5) | 2015.12.01 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!