sou. Mas antes de falar sobre esta implementação, seguem algumas considerações:
a) vc não informou qual placa Arduino está usando. Baseado nos seus tópicos anteriores, eu acredito que seja um ESP8266 ou ESP32. Tranquilo, pois o código que implementei irá funcionar para ambos e também para as demais placas (UNO, Nano, Mega, STM32, etc).
b) embora vc esteja usando LCD 16x2, o código pode ser facilmente usado para LCDs com outras "dimensões" para colunas e linhas (se suportado pela Biblioteca utilizada para o LCD).
c) no seu código original, vc exibiu textos no Display com uma "printagem" de caracter por caracter. Evite isso, porque sempre há como printar estes textos de forma "normal" ou seja, com o texto "completo" na linha de código. Da forma que vc fez, dificulta ver o que está sendo printado, e aumenta o código de uma forma desnecessária, além de ser extremamente mais trabalhoso. Além disso, vc printou os caracteres via código ASCII em decimal, o que não é necessário, pois bastaria usar o "write" com os caracteres entre apóstrofes, como neste exemplo: write('A') ao invés de write( byte(65) ). Ainda sobre isto, parece que vc usou o caracter ASCII de número "128" para printar um espaço em branco, o que está "incorreto". O caracter que vc deveria ter usado é o "32" (ou 0x20, em Hexadecimal), que é o caracter ASCII oficial do "espaço em branco". Ocorre que o "128" poderá ser um caracter diferente do espaço em branco, dependendo do Controlador do Display usado na placa do módulo LCD.
Falando agora sobre as funcionalidades do seu código. Vc implementou três efeitos "animados" na exibição no Display. Vejamos quais são eles:
1) efeito de exibição "gradual" do texto, caracter por caracter, com estes caracteres sendo printados em um intervalo de tempo especificado. E para isso vc printou cada caracter de forma sequencial no código, com uma função delay entre cada print para cadenciar a exibição do texto completo, e claro: isto causa seu código ficar preso nestas operações até que todo o texto seja printado.
2) efeito de exibição "correndo" na linha do Display, da direita para a esquerda, com uma velocidade determinada por um intervalo de tempo entre cada printagem (em coluna anterior à que acabou de ser printada). Da mesma forma que no efeito anterior (o "gradual"), isto também causa seu código ficar preso nestas operações até que o texto seja printado na coluna final, pois vc também usou a função delay para cadenciar a sequência de prints.
3) efeito "pisca/pisca" (ou "Blynk") do Display, com a visualização do mesmo "desligando" e "ligando" por intermédio das funções "lcd.noDisplay()" e "lcd.display()", executadas de forma intercalada em intervalos de tempo que determinam a velocidade do "pisca/pisca". E isto também prende a execução do seu código, até que sejam executados todos os ciclos do "pisca/pisca", entre os quais também estão funções delay.
Então implementei estes três efeitos ("gradual", "correndo", e "Blynk"), porém sem prender o código. Além disso para printar qualquer texto usando os efeitos, vc pode fazer isso especificando o texto completo, como no exemplo mostrado a seguir:
set_MSG_gradual_LINHA_n( linha_0, F("Marcela"), 0.25, 4.6 );
Neste exemplo, é requisitado que seja exibido na linha "0" do Display o texto "Marcela", de forma "gradual", ou seja, caracter por caracter, com intervalo de 0.25 segundos (ou 250 mili-segundos). O tempo de exibição (mínimo) é de 4.6 segundos a contar quando todo o texto está completo na linha. Também, o texto será automaticamente centralizado na linha (ou seja, vc não precisa ficar calculando a coluna inicial de acordo com comprimento do texto).
Outro exemplo, neste caso com efeito "correndo":
set_MSG_running_LINHA_n( linha_1, F("Robotica"), 0.2, 3.5 );
onde o texto "Robotica" começa "correndo" na linha "1" da direita para a esquerda, com velocidade determinada pelo intervalo de 0.2 segundos, até que o texto esteja centralizado na linha. O tempo de exibição (mínimo) é de 3.5 segundos, a contar a partir do término do efeito "correndo".
Exemplo de uso do efeito "Blynk" (ou "pisca/pisca") do Display:
set_Display_Blynk( 0.4, 3 );
Neste caso, o Display "pisca" na velocidade determinada pelo intervalo de tempo de 0.4 segundos, sendo que no total serão 3 piscadas (e ao final das piscadas, o Display fica sempre no "modo visível").
Para cada efeito, foi implementa uma Máquina de Estados para gerenciamento do efeito. Para os efeitos "gradual" e "correndo" as Máquinas tem apenas 4 estados (sendo que um deles é apenas ficar esperando até que uma requisição de printagem seja feita). E a Máquina de Estados que faz o "Blynk" tem apenas 3 estados (onde um deles também é apenas ficar esperando).
Vc poderá implementar outros efeitos facilmente, se entender como estas Máquinas funcionam no código. Todas as três Máquinas implementadas funcionam do mesmo jeito, e qualquer outro efeito implementado também deverá funcionar do mesmo jeito.
Para que as Máquinas gerenciem corretamente os efeitos, vc NÃO deve "travar" o restante do seu código, ou seja, não use delays nem execute loops intermináveis ou de longa duração. Se vier a usar sensor DS18B20, use o modo de leitura "assíncrona" para impedir que o código fique longamente esperando uma resposta do Sensor. Se vc tiver alguma dificuldade com estas coisas, avise aqui para que eu possa te ajudar.
Vamos dar uma olha nas configurações básicas do código implementado, começando pela opção de escolha da Interface de Hardware usada para acesso ao módulo do Display LCD, conforme mostrado na figura a seguir:
(clique na figura para "zoom")
Sim, vc pode escolher o tipo da Interface, entre a tradicional Interface Paralela de 4 bits, e a concisa Interface I2C (que é a que vc está usando no seu código original). Na figura está selecionada a Interface I2C, conforme marcado na cor verde. Mude conforme seu Projeto.
E claro, se vc usar o I2C, deve especificar o endereço do módulo no Barramento I2C, conforme mostrado e marcado na cor laranja na figura a seguir:
(clique na figura para "zoom")
Para estas Interfaces, há outros settings que podem ser facilmente alterados no código (se selecionar a Interface Paralela de 4 bits, certamente isso será necessário, para especificar os pinos do Arduino usados na Interface).
A printagem dos textos, é feita em uma Máquina de Estados "principal", que é praticamente sequencial. Por motivos didáticos, esta Máquina "principal" é implementada dentro da função "loop" do Arduino. Para imprimir um texto, vc deve "requisitar" a operação desejada, pois esta operação será posteriormente executada por uma das Máquinas de Estados que implementam os efeitos no Display. Assim, há uma lógica simples a ser seguida para cada printagem de texto, e que se constitui de duas etapas:
Etapa 1: requisitar a printagem especificando o texto e parâmetros relacionados ao efeito.
Etapa 2: aguardar que a printagem seja concluída, chamando uma função que retorna "true" quando a operação de exibição terminar.
E para cada uma destas etapas, implementa-se um estado na Máquina de Estados "principal". Para o código da Marcela, estes estados podem ser vistos na lista no trecho de código mostrado na figura a seguir:
(clique na figura para "zoom")
Cada cor na figura anterior (exceto as cores amarela e a roxa), corresponde a um processo de exibição no Display, onde nas linhas "0" e "1" do Display são exibidas informações relacionadas entre si. Exemplo: na região laranja é exibida de forma gradual na linha "0", o texto "Marcela", e logo em seguida é exibido "correndo" na linha "1" o texto "Automatização", e em seguida é feito um "pisca/pisca" no Display, e depois o Display é apagado (ou "limpo"). Cada um dos estados é executado, e após cada execução, segue para um dos estados marcados na área em amarelo do código, onde é esperado que seja concluída a exibição que foi requisitada. Ou seja, logo após executar o estado "Exibe_MSG_Marcela", é executado o estado "Aguarda_FIM_MSG_linha_0" (pois a printagem da mensagem foi na linha "0"). E então a execução segue para o estado "Exibe_MSG_Automatizacao" que printa na linha "1" e por isso segue para o estado "Aguarda_FIM_MSG_linha_1", seguindo para o estado "Pisca_MSG_Automatizacao", e assim por diante.
Se em um determinado estado forem exibidas mensagens em ambas as linhas do Display, então o estado seguinte deve ser o "Aguarda_FIM_MSG_Display" que aguarda o fim das exibições em todas as linhas do Display.
Em suma, a sequência de execução dos estados é sempre printando no Display, e então aguardando que a operação de printagem termine (estados na área marcada em amarelo).
O estado marcado na cor roxa, simplesmente aguarda um período de tempo (para isso ele printa uma mensagem "vazia" que demora o tempo desejado), e então reinicia todo o processo a partir do primeiro estado (neste caso o "Exibe_MSG_Bem_vindo", marcado em azul na figura anterior).
Notar que printagens sem efeitos de animação do texto, são feitas especificando-se um intervalo de tempo nulo (igual a zero) na determinação da velocidade da animação do texto. É isso que é feito nos estados "Exibe_MSG_Bem_vindo" e "Exibe_MSG_Casa_Robotica", ambos na área marcada em azul na figura anterior.
A princípio, o processo pode parecer complexo, mas na realidade é simples, como pode ser visto na figura a seguir que mostra toda a Máquina de Estados "principal" sendo executada dentro do "loop" do Arduino:
(clique na figura para "zoom")
Notar que os estados estão marcados individualmente para facilitar a identificação dos mesmos, e que a cor da marcação correspondem às mesmas cores usadas na figura que mostrou a definição destes estados. Observar que nos estados de printagem de mensagens, as operações são simples e seguem sempre o mesmo padrão. Da mesma forma, nas áreas marcadas em amarelo, que são os estados que esperam a conclusão das operações de printagem, também são muito simples e também seguem um padrão.
Importante: se forem acrescentados mais estados de printagem no Display, os estados nas áreas em amarelo permanecem os mesmos, pois já contemplam todas as possibilidades de aguardar a conclusão das operações implementadas neste Sistema (printagem na linha "0", na linha "1", em ambas as linhas, e "pisca/pisca").
Então como criar um estado de printagem? Simples: dê um nome ao estado, e então acrescente este nome à lista onde está a definição dos estados (pode ser em qualquer ordem na lista, mas tente manter uma organização lógica pra seu próprio benefício). Então, na Máquina de Estados "principal", acrescente a execução do estado seguindo o mesmo padrão usado para aqueles já implementados (acrescente a execução do estado conforme a ordem que vc deseja que seja executado dentro da Máquina de Estados). Dentro do estado, não "invente" nenhuma lógica maluca (principalmente usando loops). Claro, também não devem ser usados delays convencionais dentro dos estados.
É possível tornar ainda mais simples todo o mecanismo de exibição das mensagens. Por exemplo, sem precisar usar os estados para verificar se a exibição foi concluída (estados nas áreas em amarelo). Mas isto requer um pouco mais de memória RAM, e como esta memória é preciosa em alguns Arduinos (como UNO, Nano, etc), não fiz esta implementação para esta versão do código, para garantir que haja no Sistema RAM suficiente para uso com outros dispositivos e suas respectivas estruturas de dados.
Para a demonstração do funcionamento dos efeitos, implementei Simulações no Proteus usando os dois tipos de Interface para o LCD, ou seja, Paralela e I2C. Isto foi feito para com Arduino UNO, mas pode ser usado qualquer outro Arduino que esteja disponível no Proteus. O circuito para a Interface I2C é mostrado na figura a seguir:
(clique na figura para "zoom")
E para a Interface Paralela de 4 bits, mostrado na figura a seguir:
(clique na figura para "zoom")
Também capturei estas Simulações em vídeo, e elas podem ser vistas nos links a seguir, onde é possível também fazer o download dos vídeos:
Vídeo da Simulação com Interface I2C: "simul LCD I2C"
Vídeo da Simulação com Interface Paralela: "simul LCD Paralelo"
Notar que as Simulações feitas, seguem o código original da Marcela, que tem intervalos de tempo de 5 segundos entre algumas etapas de exibição no Display e por isso mesmo em alguns momentos parece que nada está ocorrendo. E claro: lembrar que as Simulações ocorrem em um tempo "virtual" que é sempre mais lento que o tempo real, principalmente no caso da versão com Interface I2C.
O código implementado está neste link: "LCD_16_x_2_Views_02.zip"
E os arquivos para Simulação no Proteus estão aqui: "Simulacao_Proteus.zip"
Qualquer dúvida, não deixe de perguntar.
Espero ter ajudado.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 14:18 em 14 julho 2021
Verde = 8;
const int ledAmarelo = 9;
const int ledVermelho = 10;
//Função setup, executado uma vez ao ligar o Arduino.
void setup(){
//Ativando o serial monitor que exibirá os valores lidos no sensor.
Serial.begin(9600);
//Definindo pinos digitais dos leds como de saída.
pinMode(ledVerde,OUTPUT);
pinMode(ledAmarelo,OUTPUT);
pinMode(ledVermelho,OUTPUT);
}
//Função loop, executado enquanto o Arduino estiver ligado.
void loop(){
//Lendo o valor do sensor.
int valorSensor = analogRead(sensor);
//Valores da luminosidade podem ser alterados conforme necessidade.
//Luminosidade baixa.
if (valorSensor < 750) {
apagaLeds();
digitalWrite(ledVermelho,HIGH);
}
//Luminosidade média.
if (valorSensor >= 750 && valorSensor <= 800) {
apagaLeds();
digitalWrite(ledAmarelo,HIGH);
}
//Luminosidade alta.
if (valorSensor > 800) {
apagaLeds();
digitalWrite(ledVerde,HIGH);
}
//Exibindo o valor do sensor no serial monitor.
Serial.println(valorSensor);
delay(50);
}
//Função criada para apagar todos os leds de uma vez.
void apagaLeds() {
digitalWrite(ledVerde,LOW);
digitalWrite(ledAmarelo,LOW);
digitalWrite(ledVermelho,LOW);
}
e
#include "LiquidCrystal.h"
#include "Limits.h"
const int sensorLuz = 0; //Pino analógico que o sensor de luz está conectado.
const int sensorTemp = 1; //Pino analógico que o sensor de temperatura está conectado.
//Variáveis
int valorSensorLuz = 0; //usada para ler o valor do sensor de luz.
int valorSensorTemp = 0; //usada para ler o valor do sensor de temperatura.
int menorValorTemp = INT_MAX; //usada para armazenar o menor valor da temperatura.
//Criando um objeto da classe LiquidCrystal e
//inicializando com os pinos da interface.
LiquidCrystal lcd(9, 8, 5, 4, 3, 2);
void setup() {
//Inicializando o LCD e informando o tamanho de 16 colunas e 2 linhas
//que é o tamanho do LCD JHD 162A usado neste projeto.
lcd.begin(16, 2);
}
void loop() {
//Lendo o valor do sensor de luz
valorSensorLuz = analogRead(sensorLuz);
//Para evitar as grandes variações de leitura do componente
//LM35 são feitas 8 leitura é o menor valor lido prevalece.
menorValorTemp = INT_MAX; //Inicializando com o maior valor int possível
for (int i = 1; i <= 8; i++) {
//Lendo o valor do sensor de temperatura.
valorSensorTemp = analogRead(sensorTemp);
//Transformando valor lido no sensor de temperatura em graus celsius aproximados.
valorSensorTemp *= 0.54 ;
//Mantendo sempre a menor temperatura lida
if (valorSensorTemp < menorValorTemp) {
menorValorTemp = valorSensorTemp;
}
delay(150);
}
//Exibindo valor da leitura do sensor de temperatura no display LCD.
lcd.clear(); //limpa o display do LCD.
lcd.print("Temp: "); //imprime a string no display do LCD.
lcd.print(menorValorTemp);
lcd.write(B11011111); //Simbolo de graus celsius
lcd.print("C");
//Exibindo valor da leitura do sensor de luz no display LCD.
lcd.setCursor(0,1); //posiciona o cursor na coluna 0 linha 1 do LCD.
lcd.print("Luz: "); //imprime a string no display do LCD.
lcd.print(valorSensorLuz);
delay(2000); //aguarda 2 segundos
}
eu tinha juntado, mas os valores do ldr ficaram variando loucamente kkkk e esse é o problema
links dos dois projetos
http://www.comofazerascoisas.com.br/projeto-arduino-com-display-lcd-sensor-de-temperatura-e-sensor-de-luz.html
http://www.comofazerascoisas.com.br/projeto-arduino-sensor-de-luz-ldr-com-leds.html
…
Adicionado por Raykazy ao 13:38 em 22 fevereiro 2016
s adiante em termos de "refiná-lo".
De qualquer forma, o Sistema está totalmente funcional, e por estar organizado, e por terem sido utilizadas no código técnicas clássicas e consagradas, é possível que se continue a desenvolvê-lo de forma harmoniosa, simples e confiável.
O Sistema foi testado em Simulação no Proteus, através do Hardware mostrado na figura a seguir:
(clique na figura para "zoom")
Como eu disse num post anterior neste tópico, para a simulação eu reduzi os períodos de tempos originais, de forma que se possa ver os resultados mais rapidamente na tela do Proteus (pois os tempos originais, equivalem a vários minutos e até horas).
Assim, como resultado há dois códigos: um com períodos de tempo para o Sistema "real", e um com períodos para a Simulação. Deixei isto evidenciado no começo de cada um destes códigos, e para o Sistema "real" isso pode ser visto na figura a seguir:
(clique na figura para "zoom")
E para o código para simulação, a informação está como mostrado na figura a seguir:
(clique na figura para "zoom")
Como se vê, a única diferença são os períodos de tempo, sendo a lógica de funcionamento idêntica nos dois códigos.
O Sistema sendo simulado no Proteus, pode ser visto na figura a seguir:
(clique na figura para "zoom")
Esclarecendo alguns pontos importantes do Sistema:
1) para alterar o Acumulado de Chuva, pressiona-se o Botão correspondente no circuito equivalente ao Sensor de Chuva. A cada dois pressionamentos do Botão, será equivalente a acrescentar 1 mm de Chuva, exatamente como o Sensor "real" descrito pelo Gabriel. Observar que o Contador Digital que acrescentei ali, também registra essa contagem, permitindo que se confira se a coisa está realmente funcionando.
2) a Velocidade do Vento, pode ser ajustada via Potenciômetro que determina a Frequência de um Oscilador, assim alterando a frequência dos pulsos que seriam gerados via Anemômetro "real". Para obter melhor precisão neste controle, criei duas faixas: uma de 15 a 55 km/h, e outra de 55 a 370 km/h (certo: exagerei, pensando naqueles tornados lá no meio-oeste Americano). Assim devido a estas duas faixas, existem dois Potenciômetros para se ajustar a Velocidade do Vento desejada, e uma Chave (SW30 no circuito) permite que se selecione a faixa a ser usada (na simulação isto pode ser feito a qualquer momento). Observar que acrescentei um Frequencímetro Digital ali, a fim de que se possa ver a frequência do Oscilador a qualquer momento, e com isso conferir se a velocidade indicada está de acordo. Lembrando que o Anemômetro do Gabriel, fornece pulsos na frequência de 5.4 Hz para uma velocidade de 10 m/s (ou 36 km/h). Então é só fazer regra de três.
3) os Sensores DHT e DS18B20, são originalmente do Proteus mesmo, e os ajustes são feitos ali mesmo. Notar que para o DS18B20 eu reajustei alguns parâmetros, pois o setting default não estava de acordo para funcionar corretamente na simulação (o ajuste não foi "chutado", mas sim conforme uma análise que fiz na temporização do barramento "OneWire" implementado via Biblioteca no Arduino). Então fica a dica quando forem usar o DS18B20 no Proteus: basta copiar os mesmos parâmetros que deixei setado nesta simulação.
4) sobre os Sensores de Humidade do Solo e da taxa de CO2, praticamente não há o que dizer, uma vez que eles fornecem saídas de tensão na faixa que os Sensores "reais" fornecem, e que serão convertidos via ADC do Arduino.
5) para o RTC DS1307, notar que acrescentei os dois Resistores de 4.7k necessários para o barramento I2C. Isto deve ser feito apenas se o módulo DS1307 que estiver sendo usado no Sistema, não tiver estes dois Resistores já incluídos na plaquinha do módulo.
6) para o ATMega2560, não se deve esquecer de selecionar o arquivo "HEX" correspondente ao código a ser executado. Este arquivo está na mesma pasta da simulação. Se isto não for feito, dará erro imediatamente ao se iniciar a simulação (claro, depois de selecionado o arquivo, este setting ficará salvo no arquivo de Simulação do Proteus, dispensando fazer a seleção novamente). Isto é necessário, porque a seleção do "HEX" estará com o caminho do arquivo no meu computador, e deve se setada para o caminho do arquivo no PC de cada um.
7) para o SD Card, também deve ser especificado o arquivo da "imagem do Disco" correspondente. A extensão deste arquivo é "IMA", e está na pasta da simulação com o nome "SDCard_Image_01.IMA". A seleção é igual ao processador ATMega, e basta ir em "propriedades" do componente (o Cartão no circuito) e selecionar este arquivo. Se isto não for feito, o código não conseguirá gravar no Cartão, e indicará o erro via Serial do Arduino. Atenção, pois no modelo do Proteus o Cartão é "ejetável" (basta clicar nas setas junto ao modelo), e se ele estiver ejetado não irá gravar. E sempre que a simulação é pausada, será aberta uma janela correspondente aos arquivos no Cartão. Assim basta clicar com o botão direito do mouse para que este arquivo seja copiado em um arquivo "real" no Computador (bem legal isso, não é?). Inclusive, é possível "formatar" o SD Card, nas opções exibidas.
8) finalmente, sobre o LED "Alive" e a Chave "Debug". O LED "Alive" sempre indicará que o Sistema está funcionando, piscando a intervalos regulares. A frequência de piscada do "Alive" indicará se o "Debug" está ou não ativado: piscará 2 vezes por segundo se o "Debug" estiver ligado, e uma vez a cada 3 segundos se o "Debug" estiver desligado. Estes intervalos das piscadas podem ser facilmente alterados no código, estando inclusive especificado em segundos. E o "Debug" nada mais é do que a possibilidade ver dados via Serial do Arduino (ou "Terminal do Arduino") e isto é selecionado pela Chave "SW1" (ao lado do LED "Alive" no circuito). A posição da Chave pode ser mudada a qualquer momento, seja na simulação, seja numa montagem "real", pois o Sistema identifica a seleção imediatamente, e isto refletirá automaticamente no LED "Alive".
Observar que o Sistema original do Gabriel, "printava" na Serial do Arduino a intervalos regulares, algumas informações, como por exemplo o Acumulado de Chuva (e nesta implementação "printa" também o acumulado na última hora e nas últimas 24 horas). Isto foi mantido, mas pode ser facilmente excluído no código, ou então comentado para não ter mais efeito.
Um "feature" interessante que acrescentei: na inicialização, se o Sistema não detectar o Sensor DS18B20, ele faz 3 tentativas de detecção (o número de tentativas é facilmente alterado no código). Então o sistema informa via Serial do Arduino, se o DS18B20 foi ou não detectado, e a quantidade de tentativas que foram executadas.
Da mesma forma, se uma Gravação no Cartão falhar (seja qual for o motivo), o Sistema tenta até 5 vezes, sendo cada tentativa a cada 2 segundos, e só desiste após todas as tentativas falharem (mas tentará novamente quando for o próximo momento de Gravar). A quantidade de tentativas e o intervalo entre elas, pode ser também facilmente alterado no código. As falhas também são informadas via Serial do Arduino (ou seja, via "Debug").
Uma curiosidade: alguns podem estar se perguntando, porque não usei um daqueles modelos de Arduino existentes para o Proteus, como por exemplo o do "TEP" (aquele site das Arábias). Ocorre que as entradas analógicas do Arduino MEGA naqueles modelos, não funcionam. Então usei o modelo do Processador ATMega2560 original do Proteus, pois este funciona sempre.
Todas as características do Sistema estão bem claras e documentadas no código, assim como as possibilidades de configuração. Então acredito que não é necessário falar mais sobre elas aqui neste texto.
Apenas aconselho que além deste post, aqueles que forem implementar o Sistema, leiam o Histórico do tópico até aqui, para não deixar passar alguma informação relevante.
Segue o código com períodos de tempo do Sistema "real": "Estacao_Tempo_02.zip"
E com períodos de tempo para facilitar a Simulação: "Estacao_Tempo_02_simul.zip"
Encerro minha participação neste tópico, mas fico disponível para quaisquer esclarecimentos sobre a implementação.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 21:56 em 2 abril 2020
"Up" ou "Down", se seleciona quais dados são atualmente mostrados. Originalmente, eram exibidos 4 opções (Vazão instantânea em Litros/minuto, Fluxo acumulado em Litros, consumo total em metros cúbicos, e o valor da conta de água em R$), mas com a inclusão do cálculo da Vazão Média, esta informação também foi incluída para ser exibida, e dessa forma a seleção no Display tem agora 5 opções. Eu mantive a denominação original das opções, que eram chamadas de "Menu 1", "Menu 2", etc. Mas reformulei a forma como isso é implementado, para facilitar a alteração das opções (seja deletar alguma, seja acrescentar alguma outra). A definição das opções para exibir no Display, podem ser vistas na figura a seguir:
(clique na figura para "zoom")
Trata-se de uma Lista Enumerada (via statement "enum"), com a opções de exibição. A ordem de exibição é exatamente a que se encontra na Lista. Assim se for colocado "Menu_3" antes de "Menu_2", será esta a ordem de exibição. Notar que é preciso definir qual o primeiro item exibido e qual o último item exibido (neste caso o "Menu_1" e o "Menu_5"). Como explicado nos comentários no código mostrado na figura anterior, o primeiro item da Lista deve ser o valor "nenhum_item", embora este item nunca seja usado (o motivo disto é simples mas muito técnico, e por isso não entrarei em detalhes, mas caso alguém queira saber, basta perguntar).
Caso em algum momento se deseje saber no código qual o item atualmente selecionado para ser exibido no Display, basta consultar a variável "item_menu". Esta variável é definida conforme mostrado na figura a seguir:
(clique na figura para "zoom")
Na definição, também é especificado qual o valor inicial que esta variável assume quando o Sistema é iniciado (após ser ligado ou após um RESET), e claro, é o valor que foi definido para "primeiro_item_Menu".
Como dito anteriormente, também é exibido no Display, mensagens de alerta para a Vazão Limite (se houver um alerta). Esta mensagem é exibida a intervalos regulares definidos, e essa exibição dura um tempo também definido. Após terminado esse tempo de exibição, então o Sistema volta a exibir no Display o item atualmente selecionado que estava sendo exibido normalmente. O mecanismo é cíclico, sendo assim repetido no intervalo definido. A figura a seguir mostra a definição do intervalo de tempo entre exibições da mensagem de alerta e quanto tempo dura tais exibições:
(clique na figura para "zoom")
Os dois períodos de tempo estão especificados em segundos. Na definição mostrada na figura anterior, se houver um alerta, a cada 15 segundos será exibida no Display uma mensagem de alerta e cada exibição será de 3 segundos.
Para otimizar a exibição no Display, o Sistema só atualiza o mesmo quando é realmente necessário. Isto é particularmente importante se a Interface com o Display for via I2C, que é muito mais lenta que a Interface Paralela (pode ser de 10 a 40 vezes mais lenta). Para tal, apenas quando novos dados estão disponíveis, o Sistema requisita que o Display seja atualizado. Como os dados são atualizados a segundo (devido à taxa de amostragem da Vazão instantânea), então temos uma atualização do Display por segundo. A requisição é feita através de uma Flag booleana, que quando setada em "true" indica que o Display deve ser atualizado. Então quando a função que atualiza o Display detecta que tal Flag foi setada, ela faz a atualização conforme a informação atualmente sendo exibida (claro, após fazer essa detecção, esta função reseta a Flag para "false"). É esta mesma função que também verifica que uma mensagem de alerta deve ser exibida (e neste caso isso depende apenas de que o alerta esteja vigente e que seja o momento de exibir a mensagem de alerta).
A definição da Flag booleana de requisição de atualização do Display é mostrada na figura a seguir:
(clique na figura para "zoom")
O valor inicial da Flag é setado em "true", o que força uma atualização imediata do Display mesmo que uma amostragem da Vazão ainda não tenha sido obtida (embora uma atualização explícita também seja feita no "setup" do Arduino).
Como utilizar esta Flag? Quando vc expandir o código, use a mesma técnica, setando a Flag e atualizando o Display apenas quando necessário.
No código que eu publiquei no tópico anterior do J. Leonardo, para a medição da Vazão e cálculo dos parâmetros relacionados, eu implementei algo que lembrava uma Máquina de Estados para fazer estas operações. Mas a rigor eu não posso assim classificar porque nem todos os elementos de uma Máquina de Estados estavam definidos naquele código.
Nesta nova implementação, usei de fato uma Máquina de Estados para controlar aquelas mesmas operações. Isto torna o processo mais simples e permite que quaisquer mudanças no funcionamento sejam facilmente implementadas. Para isto, apenas dois estados seriam necessários: um para esperar e obter uma amostra da Vazão, e outro estado para calcular a Vazão e os demais parâmetros relacionados. Mas acrescentei um estado a mais, apenas para fazer uma inicialização sistemática do funcionamento do processo (ou seja, este estado é executado apenas uma única vez logo que a Máquina inicia, e depois não é mais executado).
A figura a seguir mostra a definição destes estados:
(clique na figura para "zoom")
O controle do processo de medição da Vazão e cálculo dos parâmetros relacionados, ou seja, a execução da Máquina de Estados, pode ser vista na figura a seguir:
(clique na figura para "zoom")
A função que executa a Máquina de Estados é a "executa_MAQ_Monitorar_Vazao", e sua chamada é feita no "loop" do Arduino. Para quem não está familiarizado com o funcionamento de Máquinas de Estados, é interessante comparar esta função com o processo do tópico "anterior" do J. Leonardo e que foi implementado no "loop" do Arduino, pois certamente isso agregará um bom know-how. Como dito, o funcionamento é muito simples, mas extremamente técnico e rigoroso, e por isso mesmo muito eficiente e confiável (ou seja, livre de "bugs").
No "setup" do Arduino, não há nenhuma novidade. Mas é importante observar a sequência em que as coisas vão sendo inicializadas para preparar o Sistema para o pleno funcionamento. Também deve-se notar a "limpeza" do processo executado no "setup", evitando-se deixar ali, linhas e linhas de código detalhado (ou talvez melhor dizer "espalhado"). Para conseguir isto, as inicializações são feitas em funções específicas para cada elemento que precisa ser inicializado, deixando claro no "setup" o que está sendo feito e a sequência em que isto é feito. Isto também facilita futuras alterações no funcionamento do Sistema, devido à melhor organização. O "setup" pode ser visto na figura a seguir:
(clique na figura para "zoom")
Finalmente, no "loop" do Arduino, graças a toda a metodologia que foi usada na implementação do restante do código, podemos ver facilmente todos os processos que são continuamente executados e que o mantém o Sistema funcionando de forma simples e robusta. Isto pode ser visto na figura a seguir, que mostra 5 processos sendo executados no "loop" do Arduino:
(clique na figura para "zoom")
É interessante notar que mesmo que se altere a sequência de execução dos processos no "loop" do Arduino, ainda assim o Sistema funcionará corretamente. Isto é devido à toda a técnica usada na implementação (como uso de Flags de sinalização, Flags de requisição, Processos "fechados", etc, além da própria Máquina de Estados). Mas obviamente, há uma sequência lógica que pode ser percebida, e o bom senso nos diz que é melhor deixar a execução seguir conforme esta sequência lógica, pois isto irá facilitar o entendimento do funcionamento do Sistema, o que facilitará muito o nosso trabalho.
Algumas considerações finais:
1) nesta implementação, observar que apenas no "setup" do Arduino e eventualmente em alguma função de inicialização (chamada no próprio "setup"), é que se utiliza a função "delay" do Arduino. Então não utilize o "delay" fora destas áreas. E cuidado com a enorme quantidade disponível na Internet, de Bibliotecas que fazem uso deliberado do "delay", provocando "travamento" da execução de todo o Sistema (infelizmente o "pessoal" parece que escreve as Bibliotecas esquecendo que o restante do Sistema precisa continuar executando).
2) sobre a simulação no Proteus, sempre é bom lembrar que dependendo do Computador, o I2C do PCF8574 pode "travar" com mais frequência. Pelas minhas investigações, o problema se restringe realmente ao PCF8574, pois com outros dispositivos I2C isto não ocorre. A performance da simulação no Proteus também cai sensivelmente quando se acessa o PCF8574, e isto afeta a simulação de outros circuitos que não tem relação alguma com o I2C. Assim a simulação do LCD via I2C no Proteus pode exigir uma certa dose de paciência.
3) sobre acrescentar o módulo Bluetooth HC-05, isto é tranquilo de fazer. Na foto que vc publicou, parece que vc conectou na Serial de Hardware do UNO (pinos "0' e "1" do UNO), a qual é "compartilhada" com a Interface USB. Isto não está errado, mas deveria ser evitado, pois causa alguns transtornos de desenvolvimento, como o fato de vc ter que desconectar o sinal TX do módulo HC-05 sempre que precisa gravar um código no UNO, além de que causa conflitos quando vc "printa" na Serial para ver no Terminal do Arduino no PC. Assim eu aconselho usar uma "SoftSerial" para evitar os problemas que mencionei. Veja: muita gente critica a "SoftSerial", mas é preciso saber usar adequadamente a mesma. Eu uso o HC-05 com SoftSerial e funciona perfeitamente via APP no Android, além de não afetar o funcionamento dos demais processos no código executando no Arduino (seja UNO, seja qualquer outro). Como há diversos detalhes sobre como fazer isso de forma adequada (tanto no código no Arduino como no APP no Android), não irei me estender sobre isso aqui, mas saiba que é perfeitamente factível usar no Arduino a "SoftSerial" com o HC-05.
O código para o Arduino, incluindo os arquivos para executar a simulação no Proteus, estão aqui: "vazao_LCD_I2C_LED_02.zip"
A Biblioteca LCD I2C que o J. Leonardo usou, é esta: "LiquidCrystal_I2C.zip"
Mas é simples alterar o código que postei, a fim de se usar outra LIB LCD I2C (por exemplo para usar a LIB I2C "padrão", no máximo duas linhas precisariam ser alteradas no código de Medição de Vazão).
Espero ter ajudado. Caso tenha alguma dúvida, não deixe de perguntar.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 1:27 em 20 novembro 2020
ner para verificar o endereço do PCF8574 pode ajudar tbm.
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !");
nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n");
delay(5000); // wait 5 seconds
}
…
Adicionado por Diego Previero ao 7:27 em 26 novembro 2015
nk do referido tópico é este aqui: "usando dois Sensores de Fluxo"
Sobre seu código original, percebe-se que vc tem alguns problemas com conceitos de programação, e uma falta de intimidade com isso no Arduino. Por isso, aproveitei o código daquele tópico que mencionei, e adaptei o mesmo para estar de acordo com as funcionalidades do seu código original (o "TCC_S.ino"). Como a simplicidade se manteve, acredito que isto poderá ajudar para que vc vá muito além no desenvolvimento do seu código.
Para ver este código funcionando, você pode usá-lo diretamente no seu Sistema, sem modificação, pois eu usei as mesmas definições que vc usou no seu Hardware. No final deste post está disponível o arquivo.
Mais à frente, explico porque seu código original não funcionou como esperado.
Mas agora, falando um pouco mais sobre esta implementação que estou postando aqui.
Os fatores numéricos para os cálculos dos diversos parâmetros derivados do Fluxo de Água, vc pode alterar facilmente logo no início do código, conforme mostro na figura a seguir:
(clique na figura para "zoom")
Para o teste, fiz a simulação no Proteus, onde emulei o Sensor de Fluxo via um circuito oscilador, o qual gera os Pulsos do Sensor. A frequência do oscilador pode ser ajustada durante a simulação, e consequentemente alterar a vazão "on the fly". Com os valores que usei no circuito oscilador, a vazão pode ser ajustada entre cerca de 5 L/min a 50 L/min.
O resultado da simulação, vc pode ver na figura a seguir:
(clique na figura para "zoom")
Com pequenas alterações no código, é possível aumentar a precisão da medição do Fluxo de água. Essencialmente isto consiste em mover a "Janela de Amostragem" para dentro da ISR que é cadenciada pelos pulsos do Sensor de Fluxo, e gerar um evento sinalizando a amostragem e disponibilizando a Contagem dos Pulsos. Mas talvez para uma medição inicial do Fluxo, a precisão atual provavelmente será satisfatória (por isso não fiz esta implementação "mais precisa").
Observe que utilizei a mesma LIB LCD I2C que vc disse estar usando. Isto justamente para facilitar as coisas. No entanto, eu aconselho que vc utilize uma outra LIB I2C mais comum no mundo Arduino. Claro, se isso for possível. Caso venha a fazer isso, apenas duas ou três linhas de código teriam que ser alteradas (no instanciamento do LCD I2C e usar o método "begin" ao invés do "init"). Mas isso não é necessário, se vc estiver satisfeito com a performance da LIB atual.
Sobre seu código original não ter funcionado, veja o porquê: vc criou no "loop" uma "Janela de Amostragem" de 1 segundo, usando o habilitar e desabilitar as IRQs (Interrupções). Isto não está errado, mas não é a técnica mais adequada, e não irá funcionar em todos os cenários. Quando vc desabilita as Interrupções, algumas funções que precisam delas, não irão funcionar adequadamente. É o caso do I2C: a LIB I2C do Arduino (a "Wire"), é cadenciada por Interrupções, e além disso os eventos que geram estas IRQs são temporizados e únicos, ou seja, se ocorreu e vc não tratou, já era: alguma parte no código vai ficar a "ver navios", e no caso do I2C, isso pode travar a execução. Então por este motivo, quando vc desabilita as IRQs e acessa o LCD I2C, certamente as funções da LIB não irão funcionar.
A Serial também depende de IRQs habilitadas, mas devido à buferização dos dados e a não existência de um protocolo (o I2C tem um), permite que os dados sejam transmitidos logo que vc habilita as IRQs (o que ocorria assim que o "loop" era reiniciado no seu código original). Mas no caso de recepção via Serial, isto já não se aplica, porque se a Interrupção correspondente não for executada dentro de um certo tempo, dados recebidos via Serial serão perdidos.
Então como vc vê, o controle de habilitar/desabilitar Interrupções é algo que só deve ser feito se vc tiver o know-how de como fazê-lo. Do contrário, as coisas vão dar bem errado (ou no mínimo algum efeito colateral irá ocorrer, ainda que "imperceptível").
O código implementado é este aqui: "vazao_LCD_I2C_02.zip"
No arquivo zipado, vc encontrará também os arquivos para simulação no Proteus, caso deseje fazer alguns experimentos. Obs.: dependendo do seu computador, o modelo I2C do Arduino no Proteus, pode eventualmente travar, e isto é um problema conhecido por quem usa o Proteus.
Veja: o código é bem simples e te permitirá ir em frente. E não deixe que a documentação e a organização te passem outra impressão. Mas a organização e a documentação são imprescindíveis em termos de excelência profissional, e só agregam vantagens e facilidades. Então use isso a seu favor.
Espero ter ajudado.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 9:50 em 29 outubro 2020
postou aqui, e em algumas descrições que vc também postou.
Os dois pontos fundamentais, são a grande distância entre o Arduino e o Sensor Biométrico (que vc disse precisar de até 50m), e a distribuição adequada da Alimentação sem sobrecarregar os dispositivos que fornecem essa alimentação.
Tendo estes pontos em mente, segue o Diagrama : Acesso_Bio_01.pdf
Este mesmo Diagrama, vc pode ver na figura a seguir:
(clique na figura para "zoom")
Atente para os seguintes pontos importantes:
1) a alimentação do módulo "StepDown" (desses "comuns" com LM2596), deve ser "puxada" o mais próximo possível do ponto que também alimenta o Arduino. Ou seja: a linha de 12V e a linha de GND do módulo "StepDown" devem estar conectadas o mais próximo possível do conector de alimentação do Arduino. Mas o comprimento destas linhas é flexível e pode ser até uns 50cm conforme vc precisar. (mas não "puxe" a linha de GND do módulo "StepDown", de algum ponto da placa do Arduino!!!)
2) acredito que vc saiba, mas vou esclarecer mesmo assim: o "IN-" na plaquinha do módulo "StepDown", é o próprio "OUT-" desta mesma plaquinha. Eles estão interconectados pelo próprio traçado de cobre na plaquinha. Ou seja: como o "IN-" está conectado ao GND da Fonte (e portanto ao GND do Arduino), então o "OUT-" também é uma linha de GND. Porém, observe no Diagrama que o "OUT-" (linha na cor marrom), não se conecta ao GND do Arduino. Isto é para evitar que a corrente consumida pelo seu Sistema "Remoto" (onde fica o Sensor Biométrico, Buzzer, etc), use a placa do Arduino como "caminho de retorno". Em outras palavras: não interligue a "linha marrom" ao GND do Arduino (em nenhum ponto!). Isso é muito importante, pois o seu "Remoto" estará a uma grande distância. Siga esta regra e a regra do item "1", e assim não terá problemas com instantâneos de energia que possam ocorrem devido ao grande comprimento dos cabos.
3) a linha de 5V na cor laranja no Diagrama, não pode ser ligada ao Arduino (mas veja que ela alimenta os módulos RS485, e está conectada a alguns sinais nestes módulos).
4) todos os 4 pares de fios do CAT5 foram usados. Veja que procurei usar cores realísticas para os fios do cabo CAT. Use para os sinais, os fios com as cores que eu usei no Diagrama. Isto é fundamental para que as correntes do RS485 fiquem "confinadas" aos pares trançados "próprios".
5) atenção especial aos sinais "A" e "B" dos módulos RS485. Veja que o sinal "A" de um lado deve ir ao sinal "A" do outro lado. Se trocar, os sinais estarão invertidos e não vai funcionar. Basta seguir o Digrama.
6) veja que na alimentação do Sistema Remoto, há um Capacitor Eletrolítico de 1000uF (e 25V) ligado justamente entre a linha "marrom" e "laranja". Vc pode também usar valores maiores, como 2200uF, 3300uF, 4700uF. Atenção à polaridade do eletrolítico. Não é necessário usar capacitor cerâmico em paralelo com este eletrolítico, pois todas as plaquinhas no Remoto já possuem estes capacitores (e com valores adequados).
7) ligue os sinais "DE" e "RE" dos módulos RS485, exatamente como mostrado no Diagrama.
8) o Capacitor de 470 kpF, deve ser obrigatoriamente do tipo Cerâmico.
9) os diodos 1N4148 (são 6 no total) podem até serem dispensados. Mas se eu fosse vc, montaria os mesmos. Eles protegem os pinos correspondentes aos sinais, contra spikes de alta tensão que aparecem no cabo devido ao grande comprimento destes cabos. Sem eles, em algum momentos o Arduino poderá queimar (ou começar a apresentar mal-funcionamento), o mesmo se aplicando ao sensor de obstáculo. Para o Buzzer, o Transistor existente na plaquinha, certamente suportará os skipes, mas se não quiser arriscar, pode ligar dois 1N4148 da mesma forma que está ligado para o sensor de obstáculo. Atenção: não use diodos da série 4001 (como 1N4001, 1N4002, ... 1N4007, e por aí vai), pois estes diodos não são adequados para a função necessária. E claro: atenção à polaridade desses diodos, conforme indicado no Diagrama.
10) não esqueça de ajustar a saída do módulo "StepDown" para obter os 5V para alimentar os módulos RS485 e o próprio Sistema Remoto. O ajuste deve ser feito antes de vc alimentar tudo, e uma boa forma de fazer isso é antes de montar tudo, ligando apenas o módulo "StepDown" à fonte de 12V. Porém depois que ligar tudo, pode ser que vc tenha que ajustar para algo ligeiramente acima de 5V (devido ao grande comprimento dos cabos). Mas não ultrapasse os 5.5V (é preferível fazer este ajuste com o sensor Biométrico operando).
11) e finalmente: não sei qual é o ARM usado neste sensor Biométrico, mas certamente ele é alimentado por 3.3V a partir de algum regulador existente na placa do sensor. Ou seja: os níveis lógicos para os pinos TX e RX do Sensor, são relativos a 3.3V. Normalmente, os pinos das USARTs dos Processadores ARM, são tolerantes a 5V, e portanto nenhum tratamento "especial" seria necessário. Mas caso vc descubra que este não é caso do ARM deste sensor, então um Resistor de 1K deve ser ligado entre a saída "RO" do RS485 e a entrada "RX" do sensor Biométrico (ou seja, "em série" com o sinal). Eu acredito que isto não será necessário, devido ao que relatei sobre a tolerância a 5V, mas caso vc não queira se arriscar, acrescente o Resistor. Já para o sinal "TX", nenhuma ação é necessária.
Espero não ter esquecido de nada, e espero ter ajudado.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 9:36 em 19 março 2020