아두이노의 온습도 측정값을 MariaDB에 저장하기

읽기 전에:
전 아두이노는 초급 수준, 그리고 php를 조금 쓸 줄 알고, APM이나 LAMP를 설치하고 설정할 줄 압니다. 
비록 저도 초보적인 수준이지만 APM이나 LAMP가 무엇인지 전혀 모르시는 분은 걍 ‘아.. 이런 방법도 있구나..’하고 읽어주심이 좋을 것 같습니다. ^^

아두이노를 가지고 가장 흔하게 하는 연습이 LED켜기와, 버튼 누르기, 그리고 온도센서를 이용한 환경 측정일거라 생각합니다.
처음에는 시리얼 모니터에 쏟아져 나오는 결과값만 보고 있어도 흐믓한 마음에 어쩔줄을 모르죠.

(제가 그랬습니다) 근데 조금 만지작 거리다 보면 ‘이걸 어떻게 하면 컴퓨터에 저장할 수 있을까? 하는 생각이 듭니다. 왠지 기록하고 싶어지잖아요. ^^
그래서 이런 저런 자료를 찾아봤습니다.

1. 아두이노와 컴퓨터와의 통신

미리 말씀드리지만.. 전 전자공학이 전문인 사람은 아닙니다. 그리고 컴퓨터용 언어에 능통한 사람도 아니구요. 그래서 제가 아는 선에서만 설명할 예정입니다.

방법1 : Arduino MySQL Connector이용하기 (Arduino – DB 직접연결)

척 벨(Chuck Bell)이라는 분이 만든 아두이노 전용 MySQL 커넥터가 있습니다. 라이브러리 형태로 되어 있고요, 아두이노 IDE (Sketch)에서 적용해 사용하실 수 있습니다. 관련 예제는 github에서 찾아보실 수 있고요, 예제를 읽어보시면 사용법을 이해하실 수 있습니다.

단, 이 방법을 쓰기 위해서는 데이타베이스의 사용자 설정과 시스템 설정에서 외부에서의 직접 연결을 허용해 줘야 합니다. 전… Maria DB에서 이걸 실패해서 포기했어요.. ㅠㅠ

방법2 : Arduino – PHP – MariaDB로 연결하기 (간접 연결)

위의 방법1이 나오기 전에 가장 많이 사용한 방법인 것 같습니다. 구글링을 하다보면 php나 파이썬, 그리고 자바까지 이용해 데이타베이스에 저장하는 방법이 나옵니다. 방법1의 내용을 읽어보시면 적혀있지만 상당히 귀찮고 복잡하지만 딱히 좋은 방법이 없어 지금까지 계속 사용하는 방법 같습니다.

일단 APM이나 LAMP를 설치해야 하고, 웹페이지의 코드를 짜야하고, 데이타베이스도 만들고, 그런 다음에 아두이노 코드를 짜야 하니 일을 세 개나 해야한다는 단점이 있지요. ㅠㅠ  그리고, 제 경험이지만 에러가 뜨면 정말 찾기 어렵습니다. 아두이노, 아파치 서버, php코드, 데이타베이스 중 어디에서든 에러는 발생할 수 있고, 그걸 일일히 다 찾아야 하니까요.

 제가 이 방법을 사용한 것은 위의 방법1을 실패했기 때문입니다.

방법3 : 내가 모르는 여러가지 방법들

제가 잘 모르는.. FTDI라는걸 쓰는 방법도 있고, SD카드를 이용하는 방법도 있나 봅니다. 근데 전 모르겠어요. 나중에 쓰게 되면 알려드리죵…;;;

(전 무책임의 대명사 입니다)

2. 준비물

  • 아두이노 보드 (호환형이든, 정품이든 상관 없습니다)
  • 브래드 보드(빵판), 커넥터 케이블 몇 개
  • 작은 테스트 커넥터
  • 온습도 센서
  • Wifi 쉴드 (이제 안나오죠? ㅡㅡ;)
  • 온습도 센서 (ETH-01DV)

다른 건 다 쉽게 구할 수 있는데 Wifi 쉴드가 이제 품절이 되어서 문제가 있죠? 아두이노용 Yun 쉴드라는 것이 네트워크 연결기능이 있어서 가능할 것 같습니다. 아니면 호환 쉴드라도 상관은 없어요. ㅎㅎ

그리고 온습도 센서는 아날로그 출력용을 선택했습니다. 가격은 좀 비싸긴 하지만 온도와 습도를 모두 출력할 수 있는 제품이라 전 이게 좋더라구요. 그리고 I2C 출력 제품은 쉴드 사용시 문제가 될 수 있어서 (사실은 사용법을 익히지 않았어요. 나란 놈은.. ㅠㅠ ) 안 썼습니다.

3. 보드 구성

뭐… 회로도를 보여드릴 것도 없이 단순하고 창피하지만, 그래도 조금 그려봤습니다.;;;

아참, 회로도에 적혀있는 모델명은 신경쓰지 마셔요. Fritzing에 있는거 아무거나 사용했습니다.;;

선택 영역_001.png

다 만들면 대충 이런 모양이 됩니다.

2016-09-18 21.08.55   2016-09-18 21.09.03.jpg

뭐.. 비웃음이 나오겠지만 그래도 이쁘게 보아주시고, 이제 본격적으로 코딩에 들어가 볼까요?

4. 데이타베이스 설정

저는 데이타베이스는 ᅟarduino_db라는 이름으로 만들었어요. 앞으로 이것 저것 데이타들을 이곳에 적재해 보려구요. 아무튼 데이타베이스를 만든 후 아래의 테이블을 하나 만듭니다.

내용을 보시면 아시겠지만 rowid라는 고유번호가 있고, 기록 날짜와 시간(타임스탬프)는 자동으로 찍히도록 했어요. 그리고 나머지 측정갑은 모두 소수점 둘째자리까지 기록하도록 했습니다.


CREATE TABLE temp_rec ( 

rowid int NOT NULL AUTO_INCREMENT,

rec_datetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

humidity decimal(5,2) DEFAULT NULL,

centigrade decimal(5,2) DEFAULT NULL,

farenheit decimal(5,2) DEFAULT NULL,

PRIMARY KEY ('rowid')

) ENGINE = InnoDB DEFAULT CHARSET = utf8;

여기까지 끝났으면 사용자를 하나 만들어서 이 데이타베이스에 권한을 부여합니다. 아래는 사용자를 생성하는 코드이고, 그 다음은 사용자에게 해당 데이타베이스의 권한을 부여하는 코드입니다.


mysql -u root -p //MariaDB의 관리자 계정으로 접속

use mysql; //DB관리 데이타베이스 접속

CREATE USER '아이디'@'localhost' identified by '비밀번호';  //아이디와 비밀번호 생성. 어차피 php는 로컬접속이라 이렇게 설정합니다. 

GRANT ALL PRIVILEGES ON arduino_db.* TO '아이디'@'localhost';  //아이디에게 arduino_db의 모든 권한을 할당합니다. 

FLUSH PRIVILEGES; //설정 적용 

여기까지 하고 방금 생성한 아이디로 mysql에 접속해 봅니다. (mysql -u 아이디 -p ) 제대로 접속이 되고 해당 데이타베이스를 이용할 수 있으면 DB설정은 다 끝난 겁니다.

5. PHP 코딩

아주 단순하게 짜기로 했습니다. 보통의 경우 php파일 중 데이타베이스 접속용 파일은 따로 만드는데요, 이건 그런것도 없습니다. 걍 한 파일에 다 때려 넣었습니다.;;;


     
mysqli_set_charset($db, "utf8");


     
if (mysqli_connect_errno()){ 

echo "ERROR: 데이타베이스에 연결할 수 없습니다.";

exit;

}

$hum = $_GET["hum"];

$temp = $_GET["temp"];

$ftemp = $_GET["ftemp"];



$query = "INSERT INTO temp_rec (humidity, centigrade, farenheit) VALUES ('".

$hum."', '"

.$temp."', '"

.$ftemp."')";





$result = mysqli_query($db, $query);



if ($result) {

echo $db->affected_rows." data inserted into databases.";

}else{

echo "ERROR: 자료가 추가되지 않았습니다.";

}

mysqli_free_result($result);

mysqli_close($db);

?> 

코드를 보시면 에러메시지를 보여주는 항목이 있는데 다 덧없는 짓입니다…;; 그냥 제가 너무 게을러서, 전에 쓰던 코드를 재사용해서 그런 것이구요, 사실은 우리가 이 프로그램이 돌아가는 과정에 발생하는 문제를 컴퓨터 화면이나 아두에노에서 볼 방법은 거의 없습니다.

어쨌든..  이제 거의 다 끝났습니다.

6. 아두이노 스케치 코드

아두이노의 스케치 코드는 아래와 같습니다.


#include 
#include  //왜 쓰는지 모르는데 남들이 쓰길래 따라 써봤음

char ssid[] = "네트워크SSID";     //  your network SSID (name)
char pass[] = "네트워크 암호";    // your network password
int status = WL_IDLE_STATUS;     // the Wifi radio's status
WiFiClient client;
char server[] = "서버의 IP주소";

const int HumPin = A0;
const int TempPin = A2;


void setup() {
  Serial.begin(115200);

  // attempt to connect using WPA2 encryption:
  Serial.println("Attempting to connect to WPA2 network...");
  status = WiFi.begin(ssid, pass);

  // if you're not connected, stop here:
  if ( status != WL_CONNECTED) {
    Serial.println("Couldn't get a wifi connection");
    while (true);
  }
  // if you are connected, print out info about the connection:
  else {
    Serial.println("Connected to network");
    IPAddress ip = WiFi.localIP();
    Serial.print("IP address is : ");
    Serial.println(ip);
    Serial.println("");
  }
}


void loop() {
  int HumVal = analogRead(HumPin);
  int TempVal = analogRead(TempPin);

  float HumVoltage = (HumVal / 1024.0) * 5.0;
  float TempVoltage = (TempVal / 1024.0) * 5.0;

  float Hum = -12.5 + (125 * (HumVoltage / 5.0));
  float Temp = -66.875 + (218.75 * (TempVoltage / 5.0));
  float FTemp = -88.375 + (393.75 * (TempVoltage / 5.0));

  char CharHum[5];
  char CharTemp[5];
  char CharFTemp[5];

  dtostrf(Hum, 5, 2, CharHum);
  dtostrf(Temp, 5, 2, CharTemp);
  dtostrf(FTemp, 5, 2, CharFTemp);

  if(client.connect(server,80)){
    Serial.println("Connected...");
    client.print("GET /temp_rec.php?");
    client.print("hum=");
    client.print(Hum);
    client.print("&&");
    client.print("temp=");
    client.print(Temp);
    client.print("&&");
    client.print("ftemp=");
    client.print(FTemp);
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();
    client.println();
    Serial.println("Transfer terminated");
    delay(2000);
  }else{
    delay(1000);
    Serial.println("Connection failed");
    delay(2000);
  }
  delay(1000);
  client.stop();

  delay(2000);

  Serial.print("Temperature (C) is : ");
  Serial.println(CharTemp);
  Serial.print("Temperature (F) is : ");
  Serial.println(CharFTemp);
  Serial.print("Humidity (%) is : ");
  Serial.println(CharHum);
  Serial.println("");
  
  delay(55000);  
}

앞 부분은 아두이노 와이파이 쉴드의 기본 레퍼런스를 참조 했구요, 그대로 옮겨다 붙인 겁니다. 전 개인적으로 보안을 신경쓰다 보니 WPA2를 쓰는지라 그 예시를 참조했어요.

일단 시스템이 켜지면 와이파이 쉴드를 통해 공유기(게이트웨이)에 연결이 됩니다.  연결이 완료되면 온습도 센서(A0, A2)의 결과값을 받아서 전압값으로 변환해 줍니다. 그리고 이 전압값을 다시 센서의 제품 설명서에 나와있는 변환 공식에 따라 검침값으로 변환합니다.

변환 공식은 아래와 같습니다.

sepc
이렇게 변환된 실수값을 다시 char 타입으로 변환하고 서버와의 통신을 통해 php에 GET으로 넘겨줍니다. 코드를 보시면 조금 의아할 수 있는데요, 반드시 “GET /temp_rec.php?”형태라든가, ” HTTP/1.1″, 그리고 “Connection: close”의 빈칸까지 동일하게 써야 하는 것 같습니

다. 사실 저도 컴퓨터에서 컴퓨터로의 연결은 아는게 없어서 이 부분에서 3일을 고생했습니다. 다른 사람들이 만들어 놓은 모든 코드를 다 시도해 봤구요, 제 결론은 위의 코드는 제대로 작동한다는 것입니다.
일단 정상 작동하는지 시리얼 모니터에서 확인한 후 데이타베이스에서도 같이 확인해 봅니다. 데이타베이스까지 자료가 제대로 전달되었다면 성공한 겁니다!

스크린샷, 2016-09-18 20-50-46

7. 이런 저런 것들

일단 ‘매우 단순한 아두이노 회로 설계’에 비해 ‘더럽게 어렵고 복잡한’ 네트워크 – php – mariaDB의 연결이 이 프로젝트의 핵심입니다. 센서를 이용한 데이타 수집 자체는 제 글을 보지 않으셔도 얼마든지 하실 수 있는 수준이니까요. 그런데 직접 해보시면 알겠지만 여러 부분에서 문제가 발생할 수 있습니다.

  • 아두이노 회로 설계 에러
  • 아두이노 코딩 에러
  • 공유기의 에러(IP 못 받음, 네트워크 접속 실패)
  • Apache2와 php의 에러 (/var/log/apache2/error.log에서 확인하세요)
  • 데이타베이스의 에러 (사용자 권한이나 포트의 문제)

이 프로젝트를 하시면서 고려하셔야 하는 것은 위의 내용이구요, 만약 제대로 작동을 안한다고 느끼시면 각 부분에 문제가 없는지 한 단계, 한 단계씩 다 확인하며 고쳐야 합니다. 특히 error.log에서 나오는 은 웹 서버에서 아예 막아버리는 것이므로 코드를 잘 살펴보시고 고쳐야 합니다.

저야 오픈소스 신봉자(공짜를 밝힘)니까 마리아 DB(Maria DB)를 사용하고 있지만, 설치 후 소켓 문제로 처음에 신경쓸 것이 많습니다. 그래서… 만약 DB에 대해 많이 알고싶지 않으시다면 MySQL을 추천해요. ㅠㅠ

 

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중