Manual

do

Maker

.

com

Como fazer um dispenser de água com Arduino

Como fazer um dispenser de água com Arduino

E finalmente, vamos concluir o projeto de dispenser de água com Arduino. Se você não acompanhou o projeto desde o começo, sugiro as seguintes leituras:

E como já passamos pelos demais artigos relacionados a esse projeto, vou começar o post com a lista de materiais para esse projeto de dispenser de água:

Lista de materiais

Funcionamento do dispenser de água

A lógica é bastante simples. No encoder, será selecionado o volume de água. Cada pulso do encoder rotativo equivalerá a 100ml, exibidos no display. Quando estiver marcando o volume de água desejado, aperte o botão (um pulso) e será iniciada a ejeção de água. O volume de água dispensado será exibido no display.

Dá pra melhorar bastante o projeto, colocando (por exemplo) o tratamento de um segundo pulso durante a ejeção de água, de forma que o fluxo de água seria interrompido. Eu não quis elaborar demais, afinal, é apenas um projeto simples de prova de conceito.

Montagem do dispenser de água

A única coisa que deve-se tomar o máximo de cuidado é em relação ao isolamento do liquido e do circuito, que estarão dentro da mesma caixa. Para evitar que em caso de vazamento a água cause um curto-circuito, o fundo da caixa do lado hidráulico foi aberto. Para um isolamento (ainda que precário) da fonte, colei uma aba de papelão entre a fonte e os componentes hidráulicos. Ainda assim é arriscado, mas já ajuda a evitar danos maiores. Minha recomendação é (apesar de óbvia) testar previamente a parte hidráulica e, após certificar-se de que não há vazamentos, energizar então o circuito.

fundo-300x225.webp

Nas roscas de ambos os componentes hidráulicos ( o sensor de fluxo e a válvula solenóide) passe fita veda-rosca o suficiente para saturar a rosca, de modo que não haja vazamentos após engatar a mangueira de silicone. Pra finalizar essa parte, coloque fita hellermann apertada firmemente sobre a mangueira que ficará por sobre a rosca.

emenda-sensor_solenoide-225x300.webp

Aviso

Atualização: jamais coloque fluxo de água ao lado de energia elétrica. Esse projeto foi feito de forma errada. A parte técnica é válida, mas a construção é um erro a não se repetir.

Se você é menor, não faça isso nem sob a supervisão de um adulto, a não ser que o adulto tenha conhecimento o suficiente para garantir que não haverá problemas relacionados ao dispenser de água.

Mantenha a caixa do dispenser de água  inclinada o mais próximo possível a 90 graus de forma que o fluxo de água fique na vertical. Isso evitará além de medidas errôneas, que  qualquer vazamento saia da caixa invés de se esparramar pelo fundo da caixa.

Tente isolar o máximo possível o circuito da parte hidráulica, revestindo o papelão que divide a parte elétrica da hidráulica com silver tape e colando essa parte com cola quente, sem dó de gastar cola. Outra opção (e certamente a ideal) é colocar a parte hidráulica em outra caixa fixada na parte inferior da parte eletrônica, assim,  se der algum vazamento os riscos serão minimizados.

Prenda os fios elétricos na parte da caixa que ficará em cima, quando na posição de dispensar água.

Retire da alimentação antes de deitar a caixa novamente.

Verifique se não há um mínimo vazamento que seja após a utilização prolongada do dispenser.

Não me faça responsável por um erro de execução. Se você falhar miseravelmente de modo que tenha que chamar um eletricista, encanador, invocar os bombeiros, vender sua residência incendiada ou qualquer outro dano que porventura venha a sofrer, a responsabilidade é exclusivamente sua. Eu sobrevivi para mostrar o projeto em funcionamento.

Wiring

O wiring é um tanto trabalhoso, mas não tem complicação. Mais uma vez, justifica-se a utilização do Arduino Nano com a placa de expansão dos GPIO, que permitiu fazer a montagem toda sem precisar de solda para esses componentes. A única coisa que não teve como escapar foi o botão para dar o start no dispenser de água.

Como tem muitos jumpers, vou mostrar as conexões de cada periférico separadamente. Daí, você vai integrando.

Display LCD 16x2

Não deu pra usar o código de exemplo do display sem modificações devido à necessidade de interrupção nos pinos 2 e 3. Nesse caso, modifiquei para utilizar os pinos 6 e 7.

dispenser-lcd_pinout.webp

O codigo para teste do display:

#include <LiquidCrystal.h>
 
LiquidCrystal lcd(12, 11, 5, 4, 7, 6);
 
void setup(){
  lcd.begin(16, 2);
}
 
void loop(){
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Manual do Maker");
  lcd.setCursor(3, 1);
  lcd.print(" linha 2");
  delay(2000);
}

 

Rotary encoder

A conexão é bastante simples, mas o código do exemplo também teve que ser modificado pra reposicionar as interrupções para o pino A0 e A1. Para tal, foi necessário passar os respectivos pinos para o objeto rotaryEncoder e também configurar os bits do registrador PCICR (Pin Change Interrupt Control Register - vide mapa de registradores no datasheet). O tratamento das interrupções nos pinos analógicos são feitos pela ISR(PCINT1_vect). Na imagem abaixo tive que utilizar um potenciômetro porque o circuits.io não tem um rotary encoder.

dispenser-rotary_encoder.webp

As partes do código para seu funcionamento estão dispostas abaixo:

#include <RotaryEncoder.h>
...
RotaryEncoder encoder(A0, A1);

void changeState(){
    pulse      += 1;
 
    if (pulse >LITER){
        pulse   = 0;
        liters += 1;
        oneMore =  true;
    }
}

void setup(){
    PCICR  |= (1 << PCIE1);   
    PCMSK1 |= (1 << PCINT8) | (1 << PCINT9);
...
}

void loop(){
...
if (oneMore){
        lcd.setCursor(0,1);
        lcd.print(liters);
        oneMore = false;
    }
    int newPos = encoder.getPosition();
    if (pos != newPos) {
      //Serial.print(newPos);
      //Serial.println();
      pos = newPos;
      dispense = pos > 0 ? pos*100 : 0;
      Serial.print(dispense);
      Serial.println("ml");
      String inMl = String(dispense) + "ml";
      lcd.print(inMl);
    }
...
}

Modulo relé

O circuits.io não tem módulo relé na lista de componentes e eu não tive paciência pra montar o circuito do relé apenas para mostrar a conexão. Basicamente, você alimenta o módulo relé com 5V, GND e o sinal deverá ir para o pino digital 8. O código é ridículo, apenas coloque o pino em OUTPUT e quando precisar ligar, escreva HIGH para ele.

Sensor de fluxo de água

Como vamos utilizar interrupção para fazer a contagem, ele deverá ser conectado ao pino 2 ou 3 digital. Eu conectei ao pino 2 e utilizei o pino 3 para tratar interrupções do botão de start do dispenser de água.

Botão de início

Claro que não precisa de um desenho da conexão do botão, hum? Em uma das pernas do botão eu coloquei um resistor de 330ohms ; pode ser até um valor menor, mas é o que estava à mão. Nas pernas do botão, não teve outra forma, tive que utilizar a solda para soldar os jumpers fêmea que iriam à board de expansão dos GPIO.

O código para o botão também é simples e está utilizando a interrupção D3, que chama a função startFlow(). Essa função simplesmente inverte o estado de um boolean, iniciado como falso. No loop, quando essa variável é true o relé é acionado. Quando seu estado é invertido, o relé é desligado. Do mesmo modo, quando o fluxo de água medido atingiu o valor selecionado, o relé é desligado, mas dessa vez, automaticamente. As porções de código que tratam o botão são essas:

...
bool flow      = false;
...
void setup(){
    pinMode(START_PIN,INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(START_PIN),startWaterFlow,LOW);
    ...
}
...
void startWaterFlow(){
    flow = !flow;
}
...
void loop(){
    ...
    if (flow){
        digitalWrite(RELAY_PIN,HIGH);  
    }
    else{
      digitalWrite(RELAY_PIN,LOW);
    }
    ...
}

A medição de 100ml está fracionada porque 1 litro é próximo de 403 pulsos, portanto 100ml seria algo como 40.3 pulsos. Dá facilmente para incluir um float e resolver a questão, mas eu fiz uma outra coisa:

void loop(){    
    ...
    if (water_flow >= (dispense-(dispense*0.03)) && water_flow != 0){
       digitalWrite(RELAY_PIN,LOW);
       water_flow = 0;
    }
    ...
}

Isso dá uma tolerância de 3%. Vou fazer o teste e se não for satisfatório, depois eu troco pra float.

A partir do botão, pude fazer alguns ajustes importantes. Por exemplo, não deve-se iniciar o relé se o valor de água a dispensar for 0. A interrupção é bastante rápida, então quando ocorre a interrupção no pino 3, a função que a trata tem que desconectar-se para não receber outro evento na sequência. No loop, a interrupção é reativada dentro do tratamento de mudança de estado do boolean flow. Repare isso no código final.

Fonte chaveada

A utilização da fonte chaveada é propícia nesse dispenser de água porque desse modo você leva os 127V pra dentro da caixa e assim pode ligar a válvula solenoide sem mais fios. A outra vantagem é que você leva corrente contínua também e nesse ponto você deve ponderar sua escolha da fonte. Se escolher uma fonte de 5V, deverá alimentar o Arduino pela porta USB. Se escolher uma fonte de 9V ou 12V, poderá ligar o Arduino através do jack da placa de expansão de GPIO. Eu tenho ambas as possibilidades, mas eu coloquei a fonte de 5V dentro da caixa. Se tivesse pensado um pouquinho só, confesso que não teria colocado a fonte de 5V. Mas está fácil trocar, estou só esperando a preguiça passar.

Finalização do dispenser de água

E após os testes, concluí que algumas modificações no código seriam necessárias. A medição que era de 403 pulsos por litro, virou 30 pulsos por 100ml, dentro de um fluxo de água determinado. Isso significa que você precisará calibrar o dispositivo antes de iniciar o uso, para saber assim o quão aberta necessita estar a torneira à qual o dispositivo será conectado. Outra importante modificação foi em relação à medição, que realmente teve que ser passado a float. No video eu exemplifico a necessidade de calibrar o dispositivo para que o volume de água em fluxo case com o parâmetro de medição. Outro erro pode ter sido a posição do display, porque é interessante que a caixa esteja no mínimo inclinada.

dispenser_inside.webp

Essa era a aparência final interna do dispenser de água, faltando apenas conectar os jumpers dos sensores, sem segredo nenhum como você pode ver. Nesse projeto foi utilizado o Arduino Nano com a placa de expansão de GPIO pelo motivo que sempre tenho citado; se não fosse isso, teriamos que montar um circuito para ligar todos os periféricos, pois só de GND serão 6 conexões. Mas com essa board de expansão sobrarão recursos e o melhor, o Nano tem 7 pinos analógicos, sendo que utilizaremos dois destes, ou seja, sobrarão os mesmos 5 contidos no Arduino UNO por exemplo. No video mais adiante eu mostro mais detalhadamente.

Código fonte

O código para a compilação e funcionamento desse dispositivo é esse abaixo:

#include <LiquidCrystal.h>
#include <RotaryEncoder.h>

#define LITER          30.0
#define RELAY_PIN      8
#define FLOW_METER_PIN 2
#define START_PIN      3

volatile int pulse;

int liters     = 0;
float water_flow = 0.0;

bool oneMore   = false;
bool flow      = false;

long dispense  = 0;

String flowing = " ";

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 7, 6);

RotaryEncoder encoder(A0, A1);

void changeState(){
    pulse      += 1;
 
    if (pulse >(LITER*10)){
        pulse   = 0;
        liters += 1;
        oneMore =  true;
    }
    water_flow = (pulse/LITER)*100;
}

//PCINT1_vect - interrupcoes nos pinos analogicos
ISR(PCINT1_vect) {
  encoder.tick(); // just call tick() to check the state.
}

void setup() {
    pinMode(RELAY_PIN,OUTPUT);
    pinMode(START_PIN,INPUT_PULLUP);
    //digitalWrite(START_PIN,LOW);
    digitalWrite(RELAY_PIN,LOW);
    PCICR  |= (1 << PCIE1);   //ativa interrupcao no analogico
    PCMSK1 |= (1 << PCINT8) | (1 << PCINT9); //configura os pinos A0 e A1 p/ int.
    
    pulse = 0;
    
    Serial.begin(115200);
    attachInterrupt(digitalPinToInterrupt(FLOW_METER_PIN),changeState,FALLING);
    attachInterrupt(digitalPinToInterrupt(START_PIN),startWaterFlow,LOW);
    lcd.begin(16, 2); //tipo de display: 16x2
    lcd.print("Iniciando...");
}

void startWaterFlow(){
    if (dispense == 0){
      return;
    }
    detachInterrupt(digitalPinToInterrupt(START_PIN));
    flow = !flow;
    delay(200);
}

void displayShow(){ 
    lcd.clear();
    if (oneMore){
        lcd.setCursor(0,1);
        lcd.print(liters);
        oneMore = false;
    }
    flowing = "Flow: " + String(water_flow) + "ml";
    lcd.setCursor(0,0);
    lcd.print(flowing);
    lcd.setCursor(0,1); //pos 0 linha 1
    Serial.print(dispense);
    Serial.println("ml");
    String inMl = "Set:  " + String(dispense) + "ml";
    lcd.print(inMl);
}

void loop() {
    if (water_flow >= dispense && water_flow != 0){
       delay(1000);
       digitalWrite(RELAY_PIN,LOW);
       water_flow = 0;
    }
    
    static int pos = 0;
    
    int newPos = encoder.getPosition();
    if (pos != newPos) {
      pos = newPos;
      dispense = pos > 0 ? pos*100 : 0;
    }
    
    displayShow();

    if (flow && dispense > 0){
        digitalWrite(RELAY_PIN,HIGH); 
        delay(500); 
        attachInterrupt(digitalPinToInterrupt(START_PIN),startWaterFlow,LOW);
    }
    else if (!flow){
      digitalWrite(RELAY_PIN,LOW);
      delay(500);
      attachInterrupt(digitalPinToInterrupt(START_PIN),startWaterFlow,LOW);
    }
    
    delay(10);
}

Bugs

Só peguei um bug, mas não quis corrigir agora porque quero passar para outro projeto. Mas com certeza é simples de resolver, está relacionado a algum lock de variável; quando o dispenser encerra o serviço, não é possível repetir a operação sem reiniciar o Arduino. Apenas isso.

Veja o video do do dispenser de água em funcionamento (e não se esqueça de se inscrever no canal:

https://youtu.be/7K6NojaAFII

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.