[아두이노] 온도센서(TMP36)+LCD16x2 경보 온도계 응용

IOT/아두이노|2019. 6. 2. 09:00

[아두이노] 온도센서(TMP36)+LCD16x2 경보 온도계 응용



오늘은 @uuu95님이 질문한 DHT11 실험을 가상시뮬레이터에서 한번 구현해보고 싶어서 실험을 하게 되었네요. DHT11 부품이 가상시뮬레이터에서 제공되지 않기에 TMP36 부품으로 대신하여 실험을 할까 합니다. 내용을 보니깐 온도를 읽어서 LCD16x2 모니터에 결과를 출력하는데 5초단위로 지속적으로 출력하고 스위치를 누르면 현재 온도를 출력하고 일정온도 이하가 되면 피에조부저가 경고음을 울리게 하는 실험이더군요. 한번 위 내용을 가상시뮬레이터에서 제 나름대로 재구성하여 실험을 하겠습니다.

참고로 오늘 실험을 하기전에 위에 링크가 걸린 참고 post들은 각 부품에 대한 기초 내용으로 구성되어 있기 때문에 가셔서 각 부품의 사용법을 사전학습하시고 오세요. 아시는 분들은 그냥 넘어가셔도 되고요 중복되는 내용은 구지 처음부터 설명을 할 필요는 없겠죠.


1. 온도계 회로도


  • 준비물 : 스위치버턴 1개, TMP36 1개, 아두이노우노
  • 내용 : 스위치버턴은 2번핀에 연결하고 TMP36은 A0핀에 연결하여 값을 읽는다.
  • 공개회로도 : https://www.tinkercad.com/things/k9ksK4Tk94a(TMP36+스위치버턴)


온도센서와 스위치 버턴을 사용하여 온도값을 시리얼 모니터로 출력시켜 코딩을 우선 합시다.

2. 코딩


코딩은 5초 단위로 TMP35 센서의 값을 읽어와 온도와 습도를 출력하도록 할려고 했는데 시뮬레이터를 빠르게 동작시키기 위해 2초 단위로 수정하여 온도와 습도가 나오게 할 예정입니다. 이때 스위치 버턴을 누르면 현재 온도와 습도가 출력되게 설계합니다. 그런데 2초 단위로 온도와 습도가 출력되는데 delay(2000) 함수를 사용하면 2초 동안은 스위치를 눌러도 반응을 하지 않습니다. 대기 시간에는 아두이노는 휴먼상태가 되기 때문에 스위치를 누를 수 없기 때문에 delay()함수를 사용하지 않는 delay 효과를 부여해서 제어함으로써 실시간으로 스위치가 눌러지면 현재 온도와 습도가 출력되고 2초 단위로 현재 온도와 습도가 출력되게 코딩을 만들어 봅시다.

[delay()함수 없이 delay] 2초 단위로 if문 안에 명령문 수행

unsigned long timeVal=0; //이전시간

void loop(){
  if(millis()-timeVal>=2000){ //딜레이 시간 2초(2000)
    2초 단위로 동작;
    timeVal=millis();
  }
}

식 : 현재시간(millis) - 이전시간(timeVal) > 딜레이시간(2000)

와 같이 코딩을 하면 2초 단위로 if문안의 명령문을 수행합니다. 수행하고 수행 후 timeVal은 수행된 시간 millis()시간을 저장함으로써 다음 2초 시간을 구할 때 사용합니다.

[스위치버턴 풀업모드버턴]

const int switchPin = 2;
void setup() { 
  pinMode(switchPin, INPUT_PULLUP);
}
void loop(){
  if(digitalRead(switchPin)== LOW){
    스위치가 눌러질때 명령문;
    delay(500);
  }
}

풀엄모드 스위치버턴이라서 pinMode()은 INPUT_PULLUP으로 선언됩니다. 동작은 스위치를 누르지 않는 상태는 HIGH상태이고 스위치를 누르면 LOW상태가 됩니다 그래서, loop()함수에서 스위치가 눌러졌을 때 LOW랑 같은지로 체크하게 됩니다.

[온/습도 계산]

float V=0;
float C=0;
float F=0;

void loop(){
  V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
  //V = analogRead(A0)*5.0/1023;
  C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  F = C*9.0/5.0+32; //화씨 F = C*1.8+32;
}

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

C, F 공식에 대입하기 위해서 V값을 구할때 0~5V 사이의 전류값을 알아야 합니다. 하지만, analogRead(A0)함수로 읽게 되면은 0~1023의 값으로 읽게 되는데 그러면 주석으로 처리한 식으로 계산을 한번 더 해야 합니다. 그래서, map()함수를 변형 시켜서 다이렉트로 0~5V 사이의 전류값을 구하도록 수정했습니다. 원래 map()함수는 float형이 아닌데 실수값을 구해야 해서 float형으로 변형 사용자정의함수로 직접 만들었습니다 전류를 구해서 C, F를 식에 대입하여 온도와 습도를 구하게 됩니다.

설계

  1. TMP36을 읽어 온/습도를 계산한다.
  2. 스위치가 눌러졌는지 확인(스위치가 눌러졌으면 현재 온/습도 출력한다.) 출력된 시점을 기준으로 2초후 온습도가 출력하게 만든다.
  3. 2초단위로 온/습도를 출력한다.
const int switchPin = 2;
unsigned long timeVal=0;
float V=0;
float C=0;
float F=0;

void setup() {
  Serial.begin(9600);
  pinMode(switchPin, INPUT_PULLUP);
}

void loop() {

  //TMP36읽어 온도와 습도 계산
  V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
  //V = analogRead(A0)*5.0/1023;

  C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  F = C*9.0/5.0+32; //화씨 F = C*1.8+32;
  
  
  if(digitalRead(switchPin)== LOW){ //스위치 눌러졌는지
    output();
    timeVal=millis(); //현재시간을 이전시간변수에 저장
    delay(500);
  }
  if(millis()-timeVal>=2000){ //현재시간-이전시간의 차이 2000인지 확인
    output();  
    timeVal=millis(); //현재시간을 이전시간변수에 저장
  }  
}  

//온도와 습도 출력
void output(){   
   Serial.print("C : ");
   Serial.println(C);  
   Serial.print("F : ");   
   Serial.println(F);  
}

// TMP36에서 읽은 0~1023값을 0~5V로 변환
float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

딜레이 함수를 사용하지 않기 때문에 실시간으로 계속 온도가 측정이 되고 스위치가 눌러지면 그때 현재 온/습도 값이 시리얼모니터로 출력된다. 그리고 출력된 시점이 이전시간이 되어 다음 2초 후에 온/습도가 출력된다. 스위치가 안눌러졌다면 2초단위로 온/습도가 출력된다.

3. 결과


2초후 부터 지속적으로 온도와 습도가 출력되는데 스위치 버턴을 누르면 바로 온도와 습도가 출력됩니다. 출력시간은 여러분들이 2000 대신에 다른 값으로 수정하시면 원하는 시간대 출력되도록 만들 수 있습니다.


4. 온도계에 피에조부저로 경보 알림


1) 회로도


  • 준비물 : 피에조 부저 1개, 스위치버턴 1개, TMP36 1개, 아두이노우노
  • 내용 : 추가로 피에조 부저를 12번 핀에 연결한다.
  • 공개회로도 : https://www.tinkercad.com/things/20FHbzZpihk(TMP36+스위치버턴+피에조부저)


12번 핀을 통해 피에조부저에 경보음을 출력한다.

2) 코딩


if(온도>50){
    tone(12,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);            
}

온도가 50도 이상이면 경보음이 울리게 됩니다. tone()~noTone()으로 도음을 끊어서 경보음이 울리게 한다.

[수정소스] 추가된 코드영역만 표시 합니다.

... 생략
const int piezoPin = 12;

void setup(){
  ... 생략
}
void loop() {
 
  ... 생략
    
    //온도가 50도이상이면 경보음
  if(C>50){
    output();
    tone(piezoPin,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);
    timeVal=millis();
  }
}
... 생략

나머지 부분은 위의 소스랑 동일합니다. 온도가 50도 이상이면 해당 온도/습도를 시리얼모니터에 출력하고 경보움이 짧게 끊어서 울린다. 그리고 현재시간을 이전시간으로 저장한다. 용도는 온도가 50도 이하로 떨어질때까지 지속적으로 경보음이 울리게 하는 목적이라고 생각하시면 되겠습니다.

3)결과


움짤로 만들다 보니깐 소리가 안나네요. 참고로 온도가 50도 이상이 되면 연속으로 온도와 습도가 출력되는 모습을 보실꺼에요. 그때 피에조부저도 같이 소리가 나온다고 생각하시면 될 듯 싶네요. 피에조부저가 울리는 명령문 안에 온도와 습도가 같이 출력되도록 하는 명령이 들어 있으니깐요. 그걸 감안하시고 연속으로 온도와 습도가 나오는 이미지 위치가 피에조부저가 같이 경고음을 울린다고 상상하시면 되겠습니다. 단점은 50이상인 경우는 연속적으로 온도가 출력되지만 50 이상이였다가 50이하로 떨어지면 2초후에나 결과를 확인할 수 있습니다. 스위치 버턴을 눌러서 바로 결과를 확인할 수 있지만 2초간의 딜레이 시간이 주어지기 때문에 이부분은 또다른 if문 락을 걸어서 50이상였다가 이하로 떨어지는 순간에 if문 락이 걸린 문장이 수행되어 현재온도를 출력되게 수정하면 됩니다. 이부분은 코딩하지 않았습니다. 숙제로 한번 이문제를 해결해 보세요.


5. 온도계에 LCD16x2 연결


1) 회로도



//LiquidCrystal(rs, enable, d4, d5, d6, d7)
LiquidCrystal lcd(3, 4, 8, 9, 10, 11);

핀이름을 잘보시고 해당 핀을 연결하시면 됩니다. 어려우시다면 위 링크된 사전학습 자료에 가셔서 사전학습을 하고 오세요.


선 연결이 좀 복잡하실꺼에요. 그래서 위에서 부터 나눠서 회로도를 순차적으로 만들었네요.

3) 코딩


LCD16x2 모니터 출력하기 위해서는

#include <LiquidCrystal.h>

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 8, 9, 10, 11);

데이터 4개의 핀으로 해서 글자를 출력합니다.

void setup(){
  lcd.begin(16,2); //가로x세로
  lcd.setCursor(0, 0); //시작위치 첫번째줄의 0번째칸 위치에서 시작
  lcd.print("Hello World"); //시작위치에서 글자를 순서대로 출력
  delay(1000); 
  lcd.clear(); //출력된 값을 지움
}   

위 주석을 잘 확인하시면 됩니다.

 void output(){
   lcd.clear(); //LCD16x2에 값을 지우기
   lcd.setCursor(0, 0); //첫번째줄의 0번째 칸 위치
   lcd.print("C : "); //문자열 출력
   lcd.setCursor(6, 0); // 첫번째 줄 6번째 칸 위치
   lcd.print(C);  //C(온도값) 출력
   lcd.setCursor(0, 1); //두번째줄의 0번재 칸 위치
   lcd.print("F : "); //문자열 출력
   lcd.setCursor(6, 1); //두번째줄의 6번째 칸 위치
   lcd.print(F);  
 }

사용자 정의 output() 함수는 위에서는 Serial 모니터에 출력했다면 이제는 LCD16x2에 출력되어야 하기 때문에 Serial 대신에 lcd로만 변경해주시면 됩니다 lcd은 젤 처음 LCD16x2 객체변수로 선언한 네임입니다. 주석으로 다 달아놓았으니깐 간단히 읽어보시면 됩니다.

lcd.setCursor(칸, 줄) : 줄, 칸의 위치를 가리킴
lcd.print(값) : 줄, 칸의 위치에 값을 출력

이 두개의 함수만 알면 쉽게 LCD16x2에 값을 출력할 수 있습니다.

4) 종합소스


#include <LiquidCrystal.h>

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 8, 9, 10, 11);

const int switchPin = 2;
const int piezoPin = 12;
unsigned long timeVal=0;
float V=0;
float C=0;
float F=0;

void setup() {
  pinMode(switchPin, INPUT_PULLUP);

  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("Hello World");
  delay(1000);
  lcd.clear();
}

void loop() { 
  V =fmap(analogRead(A0),0,1023,0,5); //map함수 원리를 이용한 다이렉트 Voltage계산
  //float V = analogRead(A0)*5.0/1023;

  C = (V-0.5)*100;  //섭씨 C = (F-32)*1.8;
  F = C*9.0/5.0+32; //화씨 F = C*1.8+32;
    
  if(digitalRead(switchPin)== LOW){
    output();
    timeVal=millis();
    delay(500);
  }
  if(millis()-timeVal>=2000){
    output();  
    timeVal=millis();
  }
  if(C>50){
    output();
    tone(piezoPin,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);
    timeVal=millis();
  }
}  
 void output(){
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("C : ");
   lcd.setCursor(6, 0);
   lcd.print(C);  
   lcd.setCursor(0, 1);
   lcd.print("F : ");
   lcd.setCursor(6, 1);
   lcd.print(F);  
  }

float fmap(long x, long in_min, long in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) /(float) (in_max - in_min) + out_min;
}

5) 결과


이 결과도 움짤로 하려다가 소리도 들리는 결과물이 있어야 할 것 같아서 그냥 동영상으로 결과물을 만들었네요.


마무리


세 파트로 나눠서 설명을 했네요. 한번에 전부 다 합쳐진 상태로 설명을 하면 좀 코딩을 어려워 할 수 있고 회로도도 너무 복잡해 보일 수 있기 때문에 나눠서 설명했습니다. 결과적으로 post가 좀 길어졌네요. 설명도 사전학습내용은 다 뺄려고 했는데 빼면은 설명이 안되는 부분이 있어서 어쩔 수 없이 복습차원으로 추가했네요.

아무튼 @uuu95님이 실험하시는 주제에 대해 가상시뮬레이터로 재미있게 실험을 하셨습니다. 참고로 세파트로 나누어진 회로도는 공개회로도에 다 있으니깐 가셔서 한번 가상시뮬레이터를 직접 돌려보시고 어떻게 동작하는지 살펴보셨으면 합니다.


댓글()