[아두이노] 온도센서를 이용한 경보 온도계 다른 접근

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

[아두이노] 온도센서를 이용한 경보 온도계 다른 접근 




[주제 ]

  • 일반스위치버턴을 인터럽트스위치버턴으로 변경
  • 50도 이상 증가 한 후 50이하로 떨어진 순간에 현재 온도가 출력되게 변경

지난시간에 간단히 경보 온도계를 만들었는데 그냥 넘어가기가 그래서 코딩적 접근 방법을 다르게 시도해보는 시간을 갖도록 하겠습니다. 하드웨어 부분은 동일하지만 어떻게 코딩을 하냐에 따라서 다양한 결과를 만들어 낼 수 있습니다.

1. 경보 온도계 회로도



경보 온도계는 지난 시간과 동일합니다. 참고에 지난시간의 post를 보고 사전학습을 미리 해주세요.


2. 코딩


[ 기본 소스 ]

#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;
}

지난시간의 실험한 기본 소스입니다. 이 소스에 대한 설명은 생략하겠습니다. 모르는분들은 지난시간의 post를 보고 오시기 바랍니다. 기본소스에 어떤 부분이 수정되고 추가 되었는지를 비교하면서 보시기 바랍니다.

1) 스위치 버턴을 인터럽트 스위치버턴으로 변경


const int interruptPin = 2;//인터럽트핀

void setup() { 
  pinMode(interruptPin, INPUT_PULLUP); 
  attachInterrupt(digitalPinToInterrupt(interruptPin), switchFn, FALLING);
}

void switchFn(){
   인터럽트 발생 시 처리 명령문;
}

아두이노우노는 2,3번이 인터럽트 핀입니다. 다른 핀을 사용 할 경우는 반응하지 않습니다. 다른 아두이노보드 일 경우는 해당 보드의 인터럽트 핀 넘버가 다를 수 있음으로 해당 보드의 인터럽트 핀 정보를 찾아보시기 바랍니다.

attachInterrupt(digitalPinToInterrupt(interruptPin), switchFn, FALLING);
FALLING : HIGH -> LOW로 변할때

이렇게 setup()함수 안에다 인터럽트 함수를 선언해 놓으면 해당 interruptPin에 전류가 HIGH->LOW로 변할 때 switchFn()함수를 호출하게 됩니다. 해당 인터럽트 2번핀은 풀업모드의 스위치버턴입니다. 처음 상태가 HIGH이고 스위치를 누르면 LOW이기 때문에 스위치를 누를대 HIGH->LOW로 변하게 됩니다. 그러면, switchFn()함수가 호출이 됩니다. 어떤 원리인지 아시겠지요.

[기존소스]

const int switchPin = 2;
void setup() {
  pinMode(switchPin, INPUT_PULLUP);
}
void loop(){
  if(digitalRead(switchPin)== LOW){
    output();
    timeVal=millis();
    delay(500);
  }
}   

변경 후,

boolean switchState=false; //스위치 상태값

void setup() { 
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), switchFn, FALLING);
}
void loop(){
  if(switchState==true){
    output();    
    timeVal=millis();    
    switchState=false;
  }
}   
void switchFn(){
  switchState=true;
}

같은 동작의 같은 느낌의 코딩입니다. 하지만 어떤 차이가 있을까요. 변경 전 스위치 버턴은 loop()함수에서 무한 반복할때 순서대로 명령문을 처리할 때 스위치가 눌러졌는지 확인하는 원리이고 변경 후 스위치 버턴은 loop()함수에서 무한 반복하는 명령문들의 순서랑 상관없이 스위치버턴 이벤트가 발생합니다. 즉, loop()함수와는 별개로 독립적으로 스위치버턴 동작을 수행합니다.

loop()함수의 지배권에 있느냐 독립적으로 있느냐에 차이인 것이죠. 사용목적에 따라서 사용하시면 됩니다.

2) 경보 해제 후 온도 출력


[기존소스]

void loop(){
  if(C>50){ 
    output();
    tone(piezoPin,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);    
    timeVal=millis();    
    tmpState=true;    
  }  
}

지난시간에 숙제로 남겨 둔 50이상일 때 경보음과 현재 온도를 출력하게 코딩을 했었습니다. 그렇다면 50도 이상이 되었다가 50도 이하가 되는 순간 온도는 수정되어야 하지만 지난 소스에서는 바로 수정되는게 아니라 정해진 시간이 될 때까지는 온도값이 출력되지 않습니다. 그 부분을 해결하라고 숙제를 내줬는데 해결책은 알아 볼까요.

어떤 상태가 되면 그 상태가 되었을 때 그 상태에 대한 상태값을 변수로 저장하면 됩니다. 50이상이 된다면 이상이 되었을 때의 상태값을 가지고 있을 때 그 상태값을 기준으로 50이하가 된 순가 현재 온도를 출력시키면 해결 됩니다. 설명이 어렵게 되었는데 코딩을 살펴볼까요.

if(C>50){
 tmpState=true;
}
if(tmpState==true){
  if(C<50){
    tmpState=false;
  }
}

C의 온도가 50도 이상이면 tmpState은 true로 50이상인 상태가 되었다는 것을 상태변수에 저장해 놓습니다. 그러기 다음 if문을 통해서 50이상인 상태가 되었다면 다시 if문으로 C가 50이하로 떨어졌는지 체크하게 되고 50이하로 떨어졌다면 다시 tmpState의 상태값은 원상태로 돌아가서 50이상이 될때까지는 false가 됩니다. 이 원리는 자주 사용하는 원리이니깐 이참에 꼭 알아두시기 바랍니다.

if문은 어떤 A조건의 상태가 되면 그 A상태를 기억 할 수 있고 그 A상태를 기준으로 B조건에 들어 갈 수 있고 B라 상태를 만들 수 있습니다. 서로 다른 조건에 대해서 상태에 따라서 개입을 하고 싶을 때 if문을 활용 한다는 점을 꼭 기억해 두세요.

변경 후,

boolean tmpState = false; //온도 상태값

void loop(){
  if(C>50){ 
    output();
    tone(piezoPin,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);    
    timeVal=millis();    
    tmpState=true;    
  }  
  
  if(tmpState==true){
    if(C<50){ 
      timeVal=millis();    
      tmpState=false;    
      output();
    }    
  }
}

3. 종합 소스


2초 단위로 LCD16x2에 온도와 습도가 출력됩니다. 여기서 스위치버턴을 누르면 인터럽트함수가 호출되고 switchState 값을 true로 변경하여 스위치가 눌러졌음을 나타냅니다 그리고 나서 if문에서 switchState가 true면 현재 온도와 습도를 출력하게 되게 코딩이 되어 있습니다. 스위치를 눌렀을 때 현재 온도와 습도를 출력하게 만든 것이죠. 다음으로, 온도가 50이상일 때 경보음과 현재 온도가 출력되는데 tmpState 변수에 true로 50이상인 상태를 나타냅니다. 그 다음 if문으로 온도가 50이하가 될때 현재 온도가 출력되게 함으로써 실시간으로 50이상으로 올라갔다가 50이하로 떨어졌을 때 현재 온도가 출력되게 함으로써 지난 시간의 코딩에 비해 경보 온도계를 매끄럽게 표현이 되었네요.

#include <LiquidCrystal.h>

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


//const int switchPin = 2;
const int interruptPin = 2;//인터럽트핀
boolean switchState=false; //스위치 상태값
boolean tmpState = false; //온도 상태값

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


void setup() { 
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), switchFn, FALLING);
  
  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(switchState==true){
    output();    
    timeVal=millis();    
    switchState=false;
  }
  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();    
    tmpState=true;    
  }  
  
  if(tmpState==true){
    if(C<50){ 
      timeVal=millis();    
      tmpState=false;    
      output();
    }    
  }
}  

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);  
}
void switchFn(){
  switchState=true;
}
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;
}


4. 결과


영상으로는 정확히 의미를 전달되지 않은 것 같습니다. 직접 공개회로도에 가셔서 TMP36 온도센서를 가상시뮬레이터에서 직접 제어해서 결과를 직접 체험해 보셨으면 합니다.


마무리


오늘은 코딩에 대해서 살펴 보았습니다. 지난시간의 소스는 약간 불안정한 소스였고 오늘은 좀 더 개선된 소스 이면서 새롭게 인터럽트 방식을 적용해 보았습니다. 코딩은 딱 하나의 정답은 없습니다. 지난시간의 소스와 오늘 소스를 통해서 다양한 접근 방식의 코딩을 하셨으면 합니다. 어떤 코딩을 하고 그 코딩으로 결과가 나왔으니깐 마무리하지 말고 이런식으로 또 다른 접근법이 없는지 계속 상상을 하면 보이지 않았던 것이 보이고 코딩이 개선되고 상상코딩의 능력이 올라가게 됩니다.

그리고, 여러분들은 지금까지의 배웠던 부품 중 떠오르시는 것이 있으면 부품을 더 결합해서 재밌는 형태로 변형 시켜 보셨으면 합니다.


댓글()