자작 정수기 컨트롤러 8, 유량센서 적용

2015. 10. 16. 14:09

Project/Water Purifier



자작 정수기 컨트롤러 8, 유량센서(Flow meter) 적용









  플로우센서를 적용하겠습니다. 우선 위 사진처럼 아두이노 5V, GND, D2 핀에 연결했습니다. DFRobot 사의 IO Expansion Shield를 이용하니 따로 브레드보드가 필요없어 테스트하기 참 편하네요!


  그 다음, 소스코드는 Adafruit 에서 제공하는 예제를 사용했습니다. 따로 라이브러리는 필요없고 인터럽트 방식을 이용했는데, 초보인 저에게는 너무 어려운 문제라 그냥 예제를 고쳐서 쓸 수 밖에 없었습니다. 아래 링크 따라가시면 예제 나옵니다.


  Flow Meter Example (https://github.com/adafruit/Adafruit-Flow-Meter)






  기본 예제를 불러 온 모습입니다. 플로우센서가 연결된 디지털 핀만 확인하고 바로 실행했습니다. 



// which pin to use for reading the sensor? can use any pin!

#define FLOWSENSORPIN 2


// count how many pulses!

volatile uint16_t pulses = 0;

// track the state of the pulse pin

volatile uint8_t lastflowpinstate;

// you can try to keep time of how long it is between pulses

volatile uint32_t lastflowratetimer = 0;

// and use that to calculate a flow rate

volatile float flowrate;

// Interrupt is called once a millisecond, looks for any pulses from the sensor!

SIGNAL(TIMER0_COMPA_vect) {

  uint8_t x = digitalRead(FLOWSENSORPIN);

  

  if (x == lastflowpinstate) {

    lastflowratetimer++;

    return; // nothing changed!

  }

  

  if (x == HIGH) {

    //low to high transition!

    pulses++;

  }

  lastflowpinstate = x;

  flowrate = 1000.0;

  flowrate /= lastflowratetimer;  // in hertz

  lastflowratetimer = 0;

}


void useInterrupt(boolean v) {

  if (v) {

    // Timer0 is already used for millis() - we'll just interrupt somewhere

    // in the middle and call the "Compare A" function above

    OCR0A = 0xAF;

    TIMSK0 |= _BV(OCIE0A);

  } else {

    // do not call the interrupt function COMPA anymore

    TIMSK0 &= ~_BV(OCIE0A);

  }

}


void setup() {

   Serial.begin(9600);

   Serial.print("Flow sensor test!");

   

   pinMode(FLOWSENSORPIN, INPUT);

   digitalWrite(FLOWSENSORPIN, HIGH);

   lastflowpinstate = digitalRead(FLOWSENSORPIN);

   useInterrupt(true);

}


void loop()                     // run over and over again

  float liters = pulses;

  liters /= 7.5;

  liters /= 60.0;


  if ( liters != 0 )

    {

      Serial.print(liters); Serial.println(" Liters");

    }

 

  delay(100);

}


  예제 전체 소스코드이고 아래는 그 실행된 화면입니다.




  위 화면처럼 단순히 리터값으로 변환해서 출력해주는 기능만 합니다. 0값이 아닐 경우 출력하도록 했기에, 더이상 입력이 없어도 같은 값을 반복해서 출력합니다.



  이제, 이 소스를 기존 소스에 붙여 넣고, 스위치를 하나 연결해서 출력을 제어하도록 했습니다.





  사진처럼 푸시버튼을 하나 연결했습니다.



  inputSwitch = digitalRead(SWITCHPIN); // 스위치 입력값을 받음


  if ( inputSwitch != prevState ) // 이전 상태와 다르다면....즉, On 또는 Off로 변화가 생겼다면....

  {

    prevState = inputSwitch; // 우선 다음번 비교를 위해 현재 입력값 저장


    if ( inputSwitch == HIGH ) // 스위치가 On 이라면.... 즉, Off -> On 으로 변화됨

    {

      if ( switchState == false ) // switchState 는 흐름 측정 유무를 체크하는 변수로 

      {                            // 값이 false 라면 현재 흐름이 없는 즉, 첫번째로 스위치를 눌렀음을 의미

        switchState = true;       // 첫번째 눌렀으므로 흐름을 측정하도록 On!!

      }

      else // switchState 가 true 즉, 현재 흐름을 측정하고 있는 상태이므로, 두번째로 스위치를 눌렀음을 의미

      {

        switchState = false;   // 두번째 스위치를 눌렀으므로 흐름 측정을 Off!!

      }

    }

  }


  추가된 소스코드입니다. 이 스위치를 한번 누르면 물 흐름을 측정하고 다시 누르면 측정을 멈춥니다. 이를 위해서, 스위치가  ON/OFFl 중 어느 상태인지, 첫번째와 두번째 중 어떤 건지 체크하도록 소스를 구성했습니다. 





  실행된 화면입니다. 참고로, 흐름을 그냥 센서를 입에 대고 불어서 발생시켰읍니다.^^;; 출력값을 원래 리터였는데, 1000 을 곱해서 밀리리터로 변환시켰습니니다만, 단위명을 안고쳐서 Liters로 표시되고 있네요!


  

  그 다음, 이제 출력을 시리얼 모니터가 아닌 OLED로 출력하도록 수정했습니다. 아래는 전체 소스이고, 이어서 작동 영상을 올렸습니다. 그런데, 한가지 문제점이 있어서 생각대로 표현이 안되네요! 아래쪽에서 얘기하겠습니다.


#define sclk 13
#define mosi 11
#define cs   10
#define rst  9
#define dc   8

#define FLOWSENSORPIN 2
#define SWITCHPIN 3

// Color definitions
#define BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0  
#define WHITE           0xFFFF


#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <SPI.h>
#include <IRremote.h>
#include <EEPROM.h>

Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, rst);

int RECV_PIN = 12;

IRrecv irrecv(RECV_PIN);

decode_results results;

char* mainMenuList[] = {"Filter 1",
                        "Filter 2",
                        "Filter 3",
                        "Filter 4",
                        "Filter 5"};

int maxWater[5];
int useWater[5];
float calib;

int inputSwitch = 0;
int prevState = LOW;
boolean switchState = false;

// count how many pulses!
volatile uint16_t pulses = 0;
volatile uint16_t prevpulses = 0;
// track the state of the pulse pin
volatile uint8_t lastflowpinstate;
// you can try to keep time of how long it is between pulses
volatile uint32_t lastflowratetimer = 0;
// and use that to calculate a flow rate
volatile float flowrate;
// Interrupt is called once a millisecond, looks for any pulses from the sensor!
SIGNAL(TIMER0_COMPA_vect) {
  uint8_t x = digitalRead(FLOWSENSORPIN);
  
  if (x == lastflowpinstate) {
    lastflowratetimer++;
    return; // nothing changed!
  }
  
  if (x == HIGH) {
    //low to high transition!
    pulses++;
  }
  lastflowpinstate = x;
  flowrate = 1000.0;
  flowrate /= lastflowratetimer;  // in hertz
  lastflowratetimer = 0;
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}


void setup(void) {
  Serial.begin(9600);
  Serial.println("Flow sensor test!");

  pinMode(FLOWSENSORPIN, INPUT);
  pinMode(SWITCHPIN, INPUT);
  digitalWrite(FLOWSENSORPIN, HIGH);
  lastflowpinstate = digitalRead(FLOWSENSORPIN);
  useInterrupt(true);
   
  display.begin();
  initValue();   
  standby_message();
  irrecv.enableIRIn(); // Start the receiver
  delay(1000);
}


void loop() {

  inputSwitch = digitalRead(SWITCHPIN);
  if ( inputSwitch != prevState )
  {
    prevState = inputSwitch;
    if ( inputSwitch == HIGH )
    {
      if ( switchState == false )
      {
        switchState = true;
        pulses = 0;
        displayLiters();
      }
      else
      {
        switchState = false;
        standby_message();
      }
    }
  }
  
  if ( switchState )
  {
    if ( pulses != prevpulses )
    {
      prevpulses = pulses;
      float liters = pulses;
      liters *= 1000; // milli Liter
      liters /= 7.5;
      liters /= 60.0;
      display.setTextColor(GREEN, BLACK);
      display.setCursor(0,0);
      display.print(liters, 0);
      //Serial.print(liters, 0); Serial.println(" Liters");
    }
  }
  else
  {
    if (irrecv.decode(&results)) 
    {
      if (results.value == 0xFF02FD) EnterSetup();
      irrecv.resume(); // Receive the next value
    }
  }
  delay(100);
}

void displayLiters()
{
  display.fillScreen(BLACK);
  display.setTextSize(2);
  display.setCursor(0,48);
  display.setTextColor(BLUE);

  display.print("      mL");
}

void EnterSetup()
{
  int menu = 0;
  int selected = -1;
  int digit = 0;
  boolean looping = true;
  boolean reset = false;

  irrecv.resume(); // Receive the next value
  delay(300);
  
  while( looping ) 
  {
    if ( menu > 4 ) menu = 0;
    
    if ( menu != selected )
    {
      showMenu(menu);
      selected = menu;
    }
    
    if (irrecv.decode(&results))
    {
      switch(results.value)
      {
        case 0xFFC23D: // right
          menu = menu + 1;
          reset = false;
          break;
        case 0xFF02FD: // ok
          if ( reset ) saveValue(menu);
          reset = false;
          selected = -1;
          break;
        case 0xFF42BD: //*
          resetValue();
          reset = true;
          break;
        case 0xFF52AD: //#
          standby_message();
          looping = false;
          break;
      }   
    }
    irrecv.resume();
    delay(300);
  }
}

void showMenu(int Sel)
{
  display.fillScreen(BLACK);
  display.setTextSize(2);
  display.setCursor(0,0);
  
  display.setTextColor(YELLOW);
  display.print(mainMenuList[Sel]);

  display.setTextColor(RED);
  display.println(maxWater[Sel], DEC);
  display.setTextColor(GREEN);
  display.println(useWater[Sel], DEC);
  
  display.setTextColor(BLUE);
  display.print("OK-> * #");
}

void saveValue( int Sel )
{
  useWater[Sel] = 0;
  EEPROM.write(Sel + 10, 0);
  EEPROM.write(Sel + 15, 0);
}

void resetValue()
{
  display.setCursor(0,32);
  display.fillRect(0, 32, 64, 16, BLACK);
  display.setTextColor(CYAN);
  display.print("0");
}

void initValue()
{
  int i, value;
  float val1, val2;

  for ( i = 0; i < 5 ; i++ )
  {
    value = (EEPROM.read(i) * 100) + EEPROM.read(i + 5);
    maxWater[i] = value;
    
    value = (EEPROM.read(i + 10) * 100) + EEPROM.read(i + 15);
    useWater[i] = value;
  }
  
  val1 = EEPROM.read(20);
  val2 = EEPROM.read(21);
  calib = val1 + ( val2 / 100 );
}

void standby_message()
{
  display.fillScreen(BLACK);
  display.setTextSize(2);
  display.setCursor(0,0);
  
  display.setTextColor(GREEN);
  display.print("Push for");
  display.setTextColor(BLUE);
  display.print("Water");
  display.setTextColor(GREEN);
  display.print(" or");
  display.print("press ");
  display.setTextColor(RED);
  display.print("OK");
  display.setTextColor(GREEN);
  display.print("for");
  display.setTextColor(YELLOW);
  display.print("Setup");
}




  위 동영상을 보시면 아시겠지만, OLED 출력 속도 즉, 화면 갱신속도가 너무 느리네요! 출력 부분을 튜닝 한다 해도 많이 개선될 것 같지 않아서, 아무래도 다른 방법을 찾아야 할듯 합니다.




  LED FND(7 Segment)에 적용해 보았습니다. 역시 리프레시 속도가 빨라서 제가 표현하려는 것과 맞네요! 원래 제가 사용하려던 디스플레이가 이 LED SEGMENT 였습니다. 메뉴 구성의 어려움 때문에 OLED로 갔던건데, 메뉴를 단순화하고 OLED는 포기해야 하겠습니다.










Comments