1) a sua forma de implementar a Máquina de Estados está correta. Mas ainda assim há alguns conceitos equivocados na sua implementação. Você precisa corrigir estes conceitos para que sua Máquina funcione conforme vc deseja e sem bugs.
Os dois elementos principais em uma Máquina de Estados, são o "Estado" em que a Máquina se encontra, e os "Eventos" que provocam as mudanças de estado. E dentro de um "Estado", vc pode fazer duas coisas: pode controlar Dispositivos do seu Sistema (Relés, LEDs, Displays, Motores, o que vc quiser), e pode obter informações (estado de chaves, sensores, etc) que possibilitem controlar os Dispositivos e possivelmente mudar de estado (ou seja, as informações obtidas podem gerar Eventos).
E claro, vc deve fazer o Diagrama de Estados da Máquina de Estados, que essencialmente é descrever como o seu Sistema irá funcionar, mostrando nele como a Máquina de Estados controlará seu "fluxo" de forma a fazer o trabalho que vc deseja. Se vc fez um Diagrama de Estados "correto", bastará implementar sua Máquina de Estados a partir desse Diagrama.
Para que vc se oriente melhor nisso, veja algumas implementações que fiz aqui no LDG, para resolver problemas típicos, que estão nestes links:
- Máquina de Estados JMelo: link
- Máquina de Estados Lucas Piedra: link
- Máquina de Estados Luiz Henrique: link
- Máquina de Estados Francikleber: link
- Máquina de Estados Oziel Marques: link
- Máquina DogFeed Marcela: link
Este último ("DogFeed"), é um ambiente com maior quantidade de dispositivos e temporizações mais complexas. Por isso aconselho vê-lo apenas depois de ver os demais, que são mais simples.
2) Como o Minerin ressaltou, há alguns controles principais que vc pode fazer no Timer1. E sim, de fato ele começa a funcionar quase de imediato após a inicialização do código-base do Arduino (o qual vc normalmente não acessa). Mas isso não deveria afetar o funcionamento do seu Sistema. Se vc implementar sua Máquina de Estados corretamente, nem precisará se preocupar se o Timer1 já estava ou não contando. Essencialmente, o resultado funcional da utilização do Timer1, será equivalente à função "millis" do Arduino (analise e chegará a essa conclusão).
Ah sim: se sua intenção era o intervalo de 0.01 segundo (como vc disse), então está passando o valor errado para a função "Timer1.initialize", que deveria ser 10000 (10 mil micro-segundos = 10 mili-segundos), e não 100000 (isto seria para 0.1 segundo).
3) seu conceito da função para "callback" parece confuso, o que é evidenciado quando vc diz "Esse Time++ eu acho que...". Ocorre que um "callback" é executado na taxa do evento que vc programou pra que ele executasse, e que neste caso seria a taxa do "overflow" do Timer1. Se vc programou para 0.01 segundo, essa taxa será o inverso disso, ou seja: 1/0.01 = 100. Logo, a função "callback" seria executada 100 vezes por segundo (taxa de 100Hz). E a cada execução, ela incrementa a variável "time" em uma unidade (devido ao "++"). Mas como no seu código vc estava programando a taxa do Timer1 para 0.1 segundo, então a taxa de execução do "callback" será = 1/0.1 = 10, ou seja, sua variável "time" será incrementada 10 vezes por segundo. Em outra palavras, a cada segundo que passa, a variável "time" é incrementada em 10.
Notas importantes: como o "callback" é chamado de dentro de uma "ISR" (Interrupt Service Routine), então vc deve obrigatoriamente declarar a variável "time" como sendo do tipo "volatile", para que seu código funcione corretamente. Mas você declarou apenas assim: "int time;" , e deveria ser assim: "volatile int time;". Se vc não souber o motivo de ter que ser assim, explico depois (mas não é nada complexo não). Também devido ao mesmo fato do "callbak" ser chamado de dentro de uma "ISR", não execute tarefas demoradas dentro dele. Mas neste caso não tem problema no seu código, pois a única coisa que está sendo feita no seu "callback" é o incremento da variável "time", então está tranquilo isto. Se vc precisar fazer tarefas mais pesadas e demoradas, proceda assim: sete uma flag dentro do "callback" e use a Máquina de Estados para detectar isso (seria exatamente um Evento) e fazer o trabalho pesado e demorado dentro da "temporização realística" do seu Sistema (claro, imediatamente depois que a tarefa pesada terminar, resete a flag indicadora do evento).
4) seu código está um tanto "bagunçado". Para fazer uma Máquina de Estados de forma confiável, vc tem que fazer um código "limpo" e organizado. Isto é absolutamente necessário. Veja os exemplos dos códigos dos links que passei em 1), e note como estão super-organizados. Vou citar como exemplo, uma confusão enorme no seu código: não fica claro o que são o "curto", "medio", e "longo". Há uma mistureba, onde hora vc os usa como variáveis, e hora vc usa para acesso a pinos de I/O do Arduino. Aparentemente existem entradas de Botões associados a estas 3 variáveis, mas sem uma evidência clara disso no código, além do que no "setup", vc programa os mesmos pinos com níveis lógicos "LOW", o que parece sem sentido. Eu assumi que há Botões conectados aos pinos "2", "3" e "4", e com isso organizei o código que vc postou aqui (aquele do Pastebin). O código organizado é esse: "duvida_TimerOne_01.ino" . Mas claro, eu posso estar enganado, se os Botões não existirem. Mesmo neste caso, vc ainda pode aproveitar o código que eu organizei, pois isso pode te ajudar bastante. Caso faça isso, não o faça apenas pra ganhar tempo, mas principalmente para ver como foi feito e aprender com a implementação.
5) por fim, para resetar a contagem "time" como aparentemente vc deseja, crie um Estado Inicial na Máquina de Estados, digamos o Estado "0", e dentro deste estado zere o valor do "time". Claro, esse estado executará apenas uma única vez no seu Sistema, e depois seguirá para o Estado "1" que vc já implementou. Ah, vc deve ter colocado o "volatile" na declaração da variável "time", como mostrei em 3).
Se possível, post aqui algum desenho que esclareça mais seu Sistema (circuito, diagrama, fotos, etc), pois ajudará bastante para que possamos auxiliá-lo aqui no LDG.
Abrçs
Elcids
…
Adicionado por Elcids Chagas ao 18:46 em 8 maio 2019
erto, entendo sua frustração, uma vez que usando um circuito discreto é perfeitamente possível sua implementação (mas claro que aí não terá a mesma flexibilidade e "sofisticação").
Então, partindo de que entendi corretamente, implementei seu Sistema no Arduino, e ele funcionou perfeitamente. Inclusive também fiz a simulação do mesmo, a qual também funcionou como esperado.
Para isto, implementei uma Máquina de Estados, que controla a geração dos pulsos, baseado no valor atual do "tempo de bico" (um nome um tanto esquisito, né?). E foram necessários apenas 2 estados para a implementação (um tanto surpreendente, pois diversas outras Máquinas que já implementei aqui no LDG tinham mais estados).
Claro, além da implementação da Máquina de Estados, tive que utilizar algumas técnicas "especiais", e sobre algumas dessas técnicas falarei mais à frente.
Mas antes Leonardo, gostaria de esclarecer um ponto: não era exatamente o "delay" o culpado pelos problemas que vc estava tendo. Eram vários culpados (e o "delay" era o menos culpado). Mas claro, em Sistemas de "tempo real", deve-se evitar o uso do "delay" a todo custo (no código que implementei não há nenhum), pois ele geralmente compromete as temporizações existentes no Sistema.
Outro detalhe: como vc não disse praticamente nada sobre o Hardware, testei com o Arduino UNO (e a simulação também fiz com o UNO).
Assim, a implementação de Hardware pode ser vista na figura a seguir, onde já mostro o resultado da simulação:
(clique na figura para "zoom")
Sobre esta implementação e resultado, farei algumas observações relevantes:
1) observe o sinal na cor amarela no Osciloscópio: são os pulsos do Sensor Hall. O código implementado, não depende da largura destes pulsos do Sensor, o que é ótimo. Mas como na simulação eu tinha que colocar uma largura para eles, coloquei em 500us. Se na prática for maior ou menor, não haverá problema, pois o código trata isso sempre de forma adequada.
2) o sinal na cor azul no Osciloscópio, são os pulsos de acionamento dos bicos injetores. A largura destes é determinada pelo valor lido do Potenciômetro, conforme vc disse que deveria ser. Sempre que o valor lido do Potenciômetro entra numa nova faixa, a largura dos pulsos muda de acordo com esta faixa. Os períodos dos pulsos que medi estavam bem precisos, e isto é devido a algumas das "técnicas especiais" que apliquei.
3) para a geração dos pulsos do Sensor Hall, na simulação foi utilizado um Gerador de Pulsos (na prática usei um Sensor Hall mesmo). Veja que na simulação, o valor da Frequência do Gerador, está em 80 Hz, ou seja, 80 pulsos por segundo. Isto corresponde a 4800 RPM, já que a rotação do motor em RPM = Frequência x 60 (ou seja, 80 x 60 = 4800 RPM), isso considerando que há apenas um único ímã acoplado ao eixo do Motor (se fossem dois, a frequência dobraria). Assim, para alterar a rotação na simulação, basta alterar o parâmetro "Frequência" do Gerador de Pulsos.
4) observe que há um Capacitor (o "C1") conectado entre o "A0" e o GND do circuito. Aconselho fortemente que vc o conecte. Na simulação não há ruídos, mas na prática os Sistemas em ambientes com Motores, costumam ter um bom nível de ruídos. E o capacitor é justamente para filtrar estes ruídos do sinal vindo do Potenciômetro (e também para minimizar a "injeção de carga" existente no circuito interno do Processador do Arduino). O valor pode estar entre 100kpF (que é o que eu coloquei na simulação), e 1uF. Use capacitor cerâmico, ou de tântalo (não use outros tipos!!!).
5) na simulação, conectei também um Voltímetro no pino "A0", de forma a poder conferir o valor indicado nele com o medido pelo Arduino (bateu sempre). Na prática, se vc for conectar algum voltímetro ou multímetro ali, apenas faça isso se tiver colocado o capacitor "C1".
6) o LED conectado na simulação (também conectei na prática), é quase um "enfeite". Mas é interessante, pois piscará na frequência dos pulsos gerados.
7) observe que no Terminal do Arduino (a Serial padrao), é possível ver os valores atuais da tensão medida no Potenciômetro, e "tempo de bico" correspondente. O sistema é otimizado, e apenas atualiza o Terminal, quando o "tempo de bico" muda. Isso é importante, pois a Serial consome alguma performance do Arduino, e então isso é minimizado com a técnica utilizada. Vc pode facilmente desligar essa exibição no Terminal, veja como: no código, há um #define com o nome "SYS_DEBUG_ON", que pode assumir "1" ou "0". Se colocar "1", você verá as informações no Terminal do Arduino. Se colocar "0", o sistema não exibirá no Terminal. Este #define do "SYS_DEBUG_ON" está no início do código, facilmente encontrado.
Agora falando sobre uma das técnicas utilizadas para garantir maior performance do Sistema, o que é muito importante, pois o Processador AVR8 do Arduino não é nenhum foguete. Você verá que no tratamento interno, a tensão medida é multiplicada por 1000 (mil, isso mesmo). Isso converte o valor "float" para um valor inteiro com até 4 dígitos. Este valor é então utilizado para determinar qual a faixa do "tempo de bico" correspondente. Mas por que? Ocorre que as rotinas de Ponto Flutuante do Arduino não são nada velozes, então no caso deste Sistema é melhor evitá-las sempre que possível. Claro, se em algum momento vc precisar do valor original em Volts, bastará dividir por 1000, mas evite fazer isso, e se fizer use uma temporização como a que utilizei em diversas partes do código.
O código está todo comentado e com observações importantes. Então está bem acessível de seguir e entender.
A Máquina de Estados implementada, é extremamente simples, já que tem apenas 2 estados. Se quiser aprender sobre como funciona, eu já postei aqui no LDG diversas outras Máquinas também simples, que irão ajudar nesse processo. Na figura a seguir, vc pode ver o Diagrama de Estados, a partir do qual implementei a Máquina de Estados deste Sistema:
(clique na figura para "zoom")
Segue o código, incluindo os arquivos de simulação no Proteus, e o Diagrama de Estados (formato Visio e PDF).
Código: Injecao_Bicos_02.zip
Caso tenha alguma dúvida ou comentário, não deixe de postar.
Espero ter ajudado.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 9:57 em 17 julho 2019
ciclo de lavagem consiste em esvaziar o tanque até o "SetPointLavagemMin" ( através do acionamento do "Esgoto" ), aí encher o tanque de H2O da torneira ( através do acionamento do "HardH2O") até o "SetPointLavagemMax", então esvaziar novamente e encher com agua purificada ( através do acionamento do "PureH2O" ) até o SetPoint2
Não consigo fazer isso.. acho que porque não sei usar um contador com expressões if dentro.
Tentei assim:
/*
projeto de automação de bomba de agua - Features realizadas: - sensor ( potenciometro ) que simula o sensor analógico de volume de H2O- leitura do volume em lcd- minimo e máximo para o automatico- sensor ( LDR ) de luz ambiente funcionando com um LED de saída - botão preenche até o maximo
Experimento - leitura do total de h20testando uma maneira de guardar o volume preenchido em uma variável e imprimir no lcd
- Ciclo de lavagemb*/
#include <LiquidCrystal.h> // Inclui a biblioteca para utilizar o LCD.
#define sensor 0 // Define o pino A0 como “sensor” #define LDR 1 // Define o pino A1 como “LDR”
int ValorSensor; // Variável para ler o sinal do pino do Arduinoint ValorLDR; // Variável para ler o sinal do pino do Arduino do LDRint EstadoBotaoPreenche = 0; // Variável para guardar o estado do botãoint EstadoBotaoLavagem = 0; // Variável para guardar o estado do botão *NOVOint EstadoPreenchimento; // Variável para guardar o estado o buzzer *NOVO
int PureH2O = 10; // Pino no qual o H2O Purificada ( led ) está conectado, no caso 10int backlight = 6; // Pino no qual o backlight ( led ) está conectado, no caso 6int BotaoPreenche = 16; // Pino no qual o BotaoPreenche está conectado, no caso A2int BotaoLavagem = 17; // Pino no qual o BotaoLavagem está conectado, no caso A3 *NOVOint Led13 = 13; // Pino no qual o LED está conectado, no caso 13 *NOVOint HardH2O = 8; // Saída de H2O da torneira ( LED Amarelo ) e, pino 9 *NOVOint Esgoto = 7; // Saída de Esgoto ( LED Vermelho ) e, pino 7 *NOVO
int SetPoint = 344; // Set point de nível, acima desse valor dispara o alarme int SetPoint2 = 379; // Set point de nível, acima desse valor dispara o alarme int SetPointLDR = 100; // Set point do LDR, abaixo desse valor apaga o led ( backlight ) int SetPointLavagemMin = 80; // Set point de nível minimo de lavagem, int SetPointLavagemMax = 450; // Set point de nível minimo de lavagem,
float Volume; // Variável que recebe o valor convertido para o volume./* float VolumePreenchido; // Variável para guardar o volume preenchido acumulado *NOVO */ LiquidCrystal lcd (12, 11, 5, 4, 3, 2); /* Esta função acima declara quais os pinos do Arduino serão utilizados para o controle do LCD */ void setup(){ lcd.begin(16, 2); // Diz para o Arduino que o display é 16x2. lcd.print("Vol reservatorio:"); // Manda o texto para a tela do display pinMode(PureH2O, OUTPUT); // Configura o pino do buzzer como saída pinMode(backlight, OUTPUT); // configura o pino do backlight como saída pinMode(BotaoPreenche, INPUT); // inicializa o pino do botão como entrada *NOVO pinMode(BotaoLavagem, INPUT); // inicializa o pino do botão como entrada *NOVO pinMode(Led13, OUTPUT); // inicializa o pino do LED como saída pinMode(HardH2O, OUTPUT); // inicializa o pino do LED amarelo como saída *NOVO pinMode(Esgoto, OUTPUT); // inicializa o pino do LED vermelho como saída *NOVO Serial.begin(9600); // Cria a comunicação serial
} void loop(){ ValorSensor = analogRead (sensor); /* Manda o Arduino ler o pino e armazena o valor em “ValorSensor”. */ Volume =(65*ValorSensor)/488; /* Converte o valor de tensão emVolume e armazena na variável “Volume” */ lcd.setCursor(0, 1); // Move o cursor do display para a segunda linha. lcd.print(Volume); // Exibe o valor de Volume no display. lcd.print(" litros"); // Escreve “L” para dizer que a escala é Litros. delay(200); /* Aguarda 0,2 segundo para efetuar uma nova leitura detemperatura. */
if (ValorSensor <= SetPoint ) //se o ValorSensor for maior que o SetPoint{ digitalWrite(PureH2O, HIGH); // ligao o buzzer}if (ValorSensor >= SetPoint2) //se o ValorSensor for maior que o SetPoint2{ digitalWrite (PureH2O, LOW); // desliga o buzzer}
EstadoBotaoPreenche = digitalRead(BotaoPreenche); // Lê a entrada do BotaoPreenche if(EstadoBotaoPreenche == HIGH) { // Se o BotaoPreenche for apertado digitalWrite(PureH2O, HIGH); // Liga o PureH2O }if (ValorSensor >= SetPoint2) //se o ValorSensor for maior que o SetPoint2{ digitalWrite (PureH2O, LOW); // desliga o PureH2O}
EstadoBotaoLavagem = digitalRead (BotaoLavagem); // Lê o estado do BotaoLavagem e armazena
if (EstadoBotaoLavagem == HIGH) // Se for HIGH
{ digitalWrite(Esgoto, HIGH); // ele liga o esgoto
}if (ValorSensor <= SetPointLavagemMin) // Até o volume de 10 litros{ delay(100); digitalWrite(HardH2O, HIGH); // liga o H2O da torneira delay(2000); digitalWrite(Esgoto, LOW); // desliga o esgoto}
if (ValorSensor >= SetPointLavagemMax) // quando o sensor atingir o SetPointLavagemMax{ digitalWrite(HardH2O, LOW); // desliga o H2O da orneira delay(100); digitalWrite(Esgoto, HIGH); // e liga o esgoto}
if (ValorSensor <= SetPointLavagemMin) // Até o volume de 10 litros{ digitalWrite(PureH2O, HIGH); // desliga o H2O da orneira delay(100); digitalWrite(Esgoto, LOW); // e liga o esgoto}
if (ValorSensor >= SetPoint2);{ digitalWrite(PureH2O, LOW); // desliga o H2O da orneira delay(100); digitalWrite(Esgoto, LOW); // e liga o esgoto}}
A parte que não funciona está em negrito..ele fica em um loop eterno.. eu queria que fizesse esse ciclo 3 vezes..
Como faço?
:(
Muito obrigado
…
erá a água que sai da máquina de lavar ir para uma caixa a parte que denominei como caixa principal.
Caso tenham alguma dúvida pode me contatar..
Abraços
int CHG = 2; // Chave Geral
int BCXP = 3; // Chave bóia caixa principal
int BCXA = 4; // Chave bóia caixa acoplada
int VSFAR = 5; // Válvula solenóide fecha água da rua
int BOMBA = 6; // Bomba d'água
int LEDCXPO = 7; // Led caixa principal nível OK
int LEDCXPB = 8; // Led caixa principal nível Baixo
int LEDCXAO = 9; // Led caixa acoplada nível OK
int LEDCXAB = 10; // Led caixa acoplada nível Baixo
int leituraCHG; // Leitura Chave Geral
int leituraBCXP; // Leitura Chave bóia caixa principal
int leituraBCXA; // Leitura Chave bóia caixa acoplada
int leituraVSFAR; // Leitura Válvula solenóide fecha água da rua
int leituraBOMBA; // Leitura Bomba d'água
int leituraLEDCXPO; // Leitura Led caixa principal nível OK
int leituraLEDCXPB; // Leitura Led caixa principal nível Baixo
int leituraLEDCXAO; // Leitura Led caixa acoplada nível OK
int leituraLEDCXAB; // Leitura Led caixa acoplada nível Baixo
byte comando=0;//tipo de comando a ser utilizado
String modo= "Em condições de operação";
void setup()
{
Serial.begin(9600);
pinMode(CHG,INPUT);
pinMode(BCXP,INPUT);
pinMode(BCXA,INPUT);
pinMode(VSFAR,OUTPUT);
pinMode(BOMBA,OUTPUT);
pinMode(LEDCXPO,OUTPUT);
pinMode(LEDCXPB,OUTPUT);
pinMode(LEDCXAO,OUTPUT);
pinMode(LEDCXAB,OUTPUT);
}
void loop()
{
leituraCHG=digitalRead(CHG);
leituraBCXP=digitalRead(BCXP);
leituraBCXA=digitalRead(BCXA);
leituraVSFAR=digitalRead(VSFAR);
leituraBOMBA=digitalRead(BOMBA);
leituraLEDCXPO=digitalRead(LEDCXPO);
leituraLEDCXPB=digitalRead(LEDCXPB);
leituraLEDCXAO=digitalRead(LEDCXAO);
leituraLEDCXAB=digitalRead(LEDCXAB);
// INICIO DA LÓGICA DO COMANDO
if(leituraCHG==LOW)//Chave geral desligada
{comando=0;}
else//Chave geral ligada
{
if(leituraBCXP==LOW && leituraBCXA==LOW)// As chaves de bóia no nível baixo
{comando=1;}
else if (leituraBCXP==LOW && leituraBCXA==HIGH)// Caixa principal vazia e caixa acoplada cheia
{comando=2;}
else if (leituraBCXP==HIGH && leituraBCXA==LOW)// Caixa principal cheia e caixa acoplada vazia
{comando=3;}
else if (leituraBCXP==HIGH && leituraBCXA==HIGH)// As chave de bóia no nível alto
{comando=4;}
}
//TIPOS DE COMANDOS
switch(comando)
{
case 0:// Comando desligado
modo= "Chave Geral Desligada";
Serial.print(modo);
digitalWrite(VSFAR,LOW);
digitalWrite(BOMBA,LOW);
digitalWrite(LEDCXPO,LOW);
digitalWrite(LEDCXPB,LOW);
digitalWrite(LEDCXAO,LOW);
digitalWrite(LEDCXAB,HIGH);
delay(1000);
digitalWrite(LEDCXPB,HIGH);
digitalWrite(LEDCXAB,LOW);
delay(1000);
case 1:// As chaves de bóia no nível baixo
modo= "Enchimento pela água da rua";
Serial.print(modo);
digitalWrite(VSFAR,LOW);
digitalWrite(BOMBA,LOW);
digitalWrite(LEDCXPO,LOW);
digitalWrite(LEDCXPB,HIGH);
digitalWrite(LEDCXAO,LOW);
digitalWrite(LEDCXAB,HIGH);
break;
case 2:// Caixa principal vazia e caixa acoplada cheia
modo= "Sem água na caixa principal";
Serial.print(modo);
digitalWrite(VSFAR,LOW);
digitalWrite(BOMBA,LOW);
digitalWrite(LEDCXPO,LOW);
digitalWrite(LEDCXPB,HIGH);
digitalWrite(LEDCXAO,HIGH);
digitalWrite(LEDCXAB,LOW);
break;
case 3:// Caixa principal cheia e caixa acoplada vazia
modo= "Enchimento pela caixa da caixa principal";
Serial.print(modo);
digitalWrite(VSFAR,HIGH);
digitalWrite(BOMBA,HIGH);
digitalWrite(LEDCXPO,HIGH);
digitalWrite(LEDCXPB,LOW);
digitalWrite(LEDCXAO,LOW);
digitalWrite(LEDCXAB,HIGH);
break;
case 4:// As chave de bóia no nível alto
modo= "Caixas cheias";
Serial.print(modo);
digitalWrite(VSFAR,LOW);
digitalWrite(BOMBA,LOW);
digitalWrite(LEDCXPO,HIGH);
digitalWrite(LEDCXPB,LOW);
digitalWrite(LEDCXAO,HIGH);
digitalWrite(LEDCXAB,LOW);
break;
}
}
…
obre problemas e possíveis soluções.
Sobre:
DFplayer mini, é um módulo decodificador de MP3 com um amplificador de 3W incluso. Ficou popular devido, principalmente, ao baixo custo e a facilidade em utilizar com o Arduino.
(Dfplayer mini)
Recursos:
- Alimentação de 3v3 á 5v;
- Comunicação serial (que independente da alimentação, trabalhará com 3v3);
- Duas saídas DAC (Left/Right) para ligar em amplificadores;
- Suporte para 1 falante de 4ohms (SPK_1 + SPK_2);
- Terminal que emite sinal do status de reprodução do áudio (on/off); (VCC5v, 0,5~3,6v)
- Opções para controles simples (next, back, vol up/down) e avançado (start, pause, etc..)
- Dois pinos USB, que honestamente nunca vi ninguém usar e até hoje não descobri a função.
- Além de funções (em lib) para adicionar propagandas (quem tiver interesse em “streamar”)
- Equalização, etc.. etc..
Ligações para Esp/Arduino.
Não existe mistério. TX no RX e RX no TX. O esp precisa ter um pouco de atenção.
A principal diferença, é que no ARDUINO a DFrobot recomenda o uso do resistor em série. Já no esp32, alguns sites não utilizam resistor, outros utilizam como pulldown (se necessário).
Recomendação de leitura para Esp32: https://www.curtocircuito.com.br/blog/utilizando-o-dfplayer-no-esp32-com-controle-via-telegram
Recomendação de leitura para Arduino: https://www.fernandok.com/2017/12/mp3-player-com-arduino-uno.html
Bibliotecas:
A biblioteca mais comum é a da DfRobot: https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
A “concorrente” mais famosa, é a “DfPlayer mini fast” (autor: PowerBroker2): https://github.com/PowerBroker2/DFPlayerMini_Fast
Ambas cumprem o mesmo papel.
Modelos (original/clone):
Existem vários clones no mercado. Testei 3 deles e o original. Honestamente? Para o meu uso (mp3player e emissor de áudio em pequenos projetos – todos ligados na tomada) não senti qualquer diferença. Entretanto, caso você deseje alimentar este equipamento com bateria, uma pesquisa mais detalhada é válida.
Modelos mais encontrados: JQ8400-TF, JQ8900-TF, JQ8900-16P, LISP3, YX5200-24SS (original), MH2024K-24SS, JL. Existe ainda o YX5300 mas, aparentemente, não é encontrado no formato do dfplayer (nunca o testei)
Concluída a apresentação, vamos lá...
Dentro do leque de pessoas que vejo utilizar e/ou recomendar o dfplayer (incluindo vídeos em youtube e blogs), é muito comum a associação com um amplificador, comumente o pam8403, de 3W. Pessoalmente, nunca entendi o motivo de um amplificador para o uso de apenas 1 falante, afinal, o próprio dfplayer já possui um amp de 3W.
A reclamação mais comum que vejo do módulo, é o chiado/ruido/noise/pop/bep e, em alguns casos, dificuldade de comunicação.
Em todo caso, seja usando amplificador ou não, o que mais vi foi reclamação sobre ruído no áudio (chiado/noise/pop/bep) e comunicação. Por causa disso, acabei reunindo um conjunto de soluções que já foram apresentadas em vários locais (normalmente, em sites com outros idiomas)
Nas "investigações" dos usuários, muitos problemas foram apresentados, como problema de estabilidade na tensão do módulo, extensão de fios, impedância em comunicação, flutuação de potência, aterramento insuficiente, voltagem inadequada, caixinha de fósforo, chimarrão, pequi, tucupi, etc... etc... etc... entretanto, de todos os problemas que vi, o problema mais crítico, até onde entendi, PARECE que é no próprio projeto. A placa possui 2 layers, e a parte do GND PARECE que não foi bem projetada. A recomendação de alguns, é que ela deveria possuir 4 layers, por conta disso, sempre que uma leitura for feita no sdcard, um ruido no som é gerado.
E agora José?
Se você chegou até aqui, talvez esteja tendo estes problemas, então segue abaixo algumas soluções apresentadas por usuários na rede:
1- Usar duas fontes de alimentação (uma para DFplayer e outra para o amp)
2- Capacitor de 2k~3k UF+ no VCC/GND do Dfplayer
3- Capacitor de 2k~3k uf no VCC/GND do amplificador
4- Resistor pull down no RX do dfplayer (1k~10k).
5- Trocar a fonte de alimentação (talvez a sua não esteja sendo forte o suficiente, ou talvez seja de baixa qualidade)
6- Se alimentando com 5V, diodo 1n4001 no vcc do dfplayer, para reduzir para 4,3V (tensão “ideal”)
7- Ligar o pino 7 (GND), ao GND, de entrada, dos falantes do amplificador.
8- Aterrar o pino 7 (GND) e 10 (GND)
9- Resistor de 1k, em série, do TX (controlador) para o RX (dfplayer) (sem pulldown)
10- Resistor de 1k, entre os fios tx/rx rx/tx. (sem pulldown)
11- Usar o DFplayer com 3v3
12- Adicionar resistores de 1k, pull-down, na saída do DAC L/R
13- Adicionar resistores de 1k, em série, na saída do DAC L/R
14- Adicionar capacitor de 100nf em série no dac, e resistor de 10k para gnd.
15- Verificar aterramento da tomada
16- Utilizar cabos blindados
17- Utilizar cabos curtos
18- Caso esteja utilizando o circuito em protoboard, testar fora dela, pois, aparentemente, a protoboard gera ruido para o áudio.
Lembrando que os itens citados foram para situações específicas. Afinal, alguns usuários usam apenas o módulo, outros usam módulo+amplificador, outros usam módulo+controlador e outros usam módulo+controlador+amplificador.
A título de curiosidade, para quem usa o módulo+amp ou módulo+amp+cont, deve ter percebido que a quantidade de ruídos que sai do SPK do Dfplayer, é MUUUUUUUUUUUUITO menor que a que sai do DAC, mesmo com todos os recursos citados. O motivo? Talvez alguém mais experiente que eu possa explicar, ou reafirmar.
Obs- Em breve atualizarei o tópico com mais informações.
===== ATUALIZAÇÃO 27/01/2021 =====
Ao analisar cuidadosamente, fui percebendo que, quanto mais eu adicionava capacitores de desacoplamento ao meu circuito, menor o ruído ficava, entretanto, era mais significativo até 2000~3000uf, após isso, mesmo adicionando 10.000uf, a diferença no ruído era muito sutil.
Se você tiver o hábito de tocar algum instrumento musical, arrisco a dizer que a chance de você perceber a diferença no ruído é de quase 100%, mas se não tem o hábito, eu arrisco a dizer que a chance é quase nula, ficando inviável adicionar muitos capacitores na tentativa de reduzir o ruído, afinal, ocuparia uma área enorme para um ganho ínfimo na qualidade.
Por curiosidade, fui analisar os itens que compõem o módulo, e sabendo que existe um problema,no mesmo, referente a leitura do cartão, resolvi olhar os terminais do socket de microSD, identifiquei o positivo e os GND, conferi em imagens disponível na internet (para não fazer besteira)
Ele é alimentando com 3v3, então não adianta colocar 100.000.000.000 uf no VCC, pois não vai fazer milagre. Toda via, na base do erro e acerto resolvi adicionar um capacitor aqui, como mostra a imagem:
1 capacitor de 1000uf. A diferença no ruído foi ENORME, Sozinho ele é melhor do que 10.000uf no VCC. Toda via, se for soldar um componente, e não tiver ferramentas adequadas como eu (só tenho 1 ferro de solda de 35W) será necessário ter um pouco de olho e dedo.
obs- deixei 3 opções de pontos para conectar o gnd ao capacitor, escolha 1.
Em breve retorno com mais informações.
===== ATUALIZAÇÃO 31/01/2021 =====
Novo problema encontrado:
Realizei um teste com o módulo, onde a cada 0,5 segundos ele iniciava um novo arquivo. Eventualmente o módulo travava e depois resetava. Irei continuar as pesquisas.…
Adicionado por tiago merces ao 19:24 em 22 janeiro 2021
ital existir e funcionar.
Vamos ver por que?
Na figura a seguir, mostro a função "loop" do seu código, e vc pode ver que marquei algumas partes com cores pra ficar mais fácil me referir a estas partes:
(clique na figura para "zoom")
Rapaz, só de olhar o código na figura, já me dá um arrepio daqueles de alma-penada.
Entenda:
1) para que o OTA funcione, obrigatoriamente é preciso que o "setup_OTA()" seja executado para inicializar as funcionalidades do OTA, pois é ele (na verdade o "ArduinoOTA.begin()" ) quem "cria" aquele PORT (associado a um IP) que vc vê na IDE do Arduino, e isso deve ser feito uma única vez, já que é uma inicialização. Então o lugar adequado pra fazer isso é no "setup" do Arduino. Ok?
Mas não se iluda, pois este PORT não é um Port COM "real", ele é virtual. Ou seja, ele é na verdade a conexão a um Server criado no modo AP do ESP8266. Portanto a IDE do Arduino é um cliente desse Server.
2) mas para que o OTA funcione mesmo, também é preciso que o "loop_OTA()", seja rotineiramente executado, pois é ele (na verdade o "ArduinoOTA.handle()" ) quem verifica se em algum momento foi requisitado iniciar uma atualização OTA. Como isso ocorre? quando vc inicia uma atualização OTA na IDE do Arduino, a IDE "cliente" se conecta ao Server do AP do ESP8266, e os dois trocam "figurinhas", de forma que a IDE envie para o Server o código a ser atualizado. Por isso o "loop_OTA()" tem que ser executado rotineiramente, do contrário ele pode não detectar uma requisição para uma atualização OTA. Então, um lugar adequado pra fazer isso é no "loop" do Arduino. Ok?
3) bem, só os itens 1) e 2), já mostram a vc o que precisa ser feito pra que a coisa funcione corretamente como planejado, e quem planejou isso foram os criadores do OTA.
Mas a coisa é um pouco pior: veja o itens seguintes.
4) pra determinar quando vc quer que o OTA funcione (a seu modo), vc criou uma variável tipo "bool" de nome "OTA_ON". Por ser "bool", ela só pode assumir dois valores: ou "0" ou "1". (ou se vc preferir: ou "false" ou "true"). Não há como sua variável "OTA_ON" assumir outro valor que não seja um dos dois listados. Então para testar o "OTA_ON", basta que vc teste verificando um dos dois valores ("0" ou "1"), pois se não for o que vc está verificando, só poderá ser o outro. Não há como existir um terceiro valor possível e uma terceira possibilidade de teste.
Mas no seu código, há três possibilidades, pois há um "if" que testa se "OTA_ON" é igual a "0", onde marquei em roxo na figura anterior (note que a parte do "millis" não muda as coisas, pois no final das contas o resultado será "0" ou "1" de qualquer forma). E se não for "0", então "OTA_ON" só pode ser "1", mas no seu teste vc está verificando isso de forma desnecessária marcado na cor laranja). Mas como se não bastasse essa doidera, vc ainda tem o "else", que seria executado caso "OTA_ON" não fosse nem "0" nem "1". Mas como assim?????? Percebeu porque o George Boole certamente está revirando no túmulo?
Então veja que o que está na parte marcada em amarelo, jamais será executado, pois como "OTA_ON" é tipo "bool", ou será executado a parte na cor verde, ou será executada a parte na cor azul. A parte na cor amarela, sem chance.
5) mas a coisa piora um pouco mais. Como eu disse, para que o OTA funcione, é preciso que o "setup_OTA()" tenha sido executado antes e uma única vez. E depois disso, o "loop_OTA()" deve ser executado rotineiramente. Mas na lógica mostrada na figura anterior, ou será executado a parte verde, ou será executada a parte azul. Jamais as duas partes serão ambas executadas. Então já viu, né? Não tem como o OTA funcionar naquela lógica.
Tá bem doido isso, né?
Nota: sua justificativa pra não deixar o OTA sempre "ativo e consumindo energia", não tem sentido, pois a execução rotineira do OTA é otimizada de forma a não ficar perdendo tempo caso nenhuma atualização seja requisitada (justamente pra ter o menor impacto possível no restante do código, e esse objetivo é alcançado, pois o ESP8266 é um Processador bem rápido).
Que tal vc analisar isso melhor, e talvez decidir fazer as coisas como elas foram planejadas para serem feitas ?
(se vc quiser navegar no mar com a fuselagem de um avião, pode tentar, mas o avião não foi exatamente planejado pra isso, logo algum problema vc vai ter)
Obs.: não fique bravo comigo. O objetivo não é ser duro com vc. Mas que eu imagino o George Boole revirando no túmulo, eu imagino.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 5:59 em 4 fevereiro 2022
ea industrial que não foi muito longe pois existe muitos obstáculos para a rede mas também fiz e em uma area residencial consegui da a volta no meu quarteirão tranquilo sem perda de dados o ponto mais longe foi de 200 metros de um Esp32LoRa da Heltec do outro
Mas isto eu fiz configurando um como Receptor e outro como Transmissor, mas eu queria utiliza-los como os 2 sendo Receptor e Transmissor para fazer uma comunicação de mão dupla os 2 mandam informações e os 2 recebe informações
Tentei simplesmente misturar os 2 programas o do Receptor com Transmissor mas parece que não estou conseguindo configura-lo... Fiquei ate na duvida se e possível mesmo fazer com mão dupla ?
Alguém teria alguma ideia de como fazer ?
PROGRAMA DO RECEPTOR
/* This is a simple example show the Heltec.LoRa recived data in OLED.
The onboard OLED display is SSD1306 driver and I2C interface. In order to make the OLED correctly operation, you should output a high-low-high(1-0-1) signal by soft- ware to OLED's reset pin, the low-level signal at least 5ms.
OLED pins to ESP32 GPIOs via this connecthin: OLED_SDA -- GPIO4 OLED_SCL -- GPIO15 OLED_RST -- GPIO16
by Aaron.Lee from HelTec AutoMation, ChengDu, China 成都惠利特自动化科技有限公司 www.heltec.cn
This project is also available on GitHub: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series */ #include "heltec.h"
#define BAND 915E6 //you can set band here directly,e.g. 868E6,915E6,433E6 String rssi = "RSSI --"; String packSize = "--"; String packet ;
void LoRaData() { Heltec.display->clear(); Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); Heltec.display->setFont(ArialMT_Plain_10); Heltec.display->drawString(0 , 15 , "Recebido " + packSize + " bytes"); Heltec.display->drawStringMaxWidth(0 , 26 , 128, packet); Heltec.display->drawString(0, 0, rssi); Heltec.display->display(); }
void cbk(int packetSize) { packet = ""; packSize = String(packetSize, DEC); for (int i = 0; i < packetSize; i++) { packet += (char) LoRa.read(); } rssi = "RSSI " + String(LoRa.packetRssi(), DEC) ; LoRaData();
}
void setup() {
Serial.begin(115200); //WIFI Kit series V1 not support Vext control Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/);
Heltec.display->init(); Heltec.display->flipScreenVertically(); Heltec.display->setFont(ArialMT_Plain_10); delay(1500); Heltec.display->clear();
Heltec.display->drawString(0, 0, "CNCTEX.Lora Iniciado!"); Heltec.display->drawString(0, 10, "Espere o dado recebido ..."); Heltec.display->display(); delay(1000); //LoRa.onReceive(cbk); LoRa.receive(); }
void loop() { int packetSize = LoRa.parsePacket(); if (packetSize) { cbk(packetSize); } }
PROGRAMA DO TRANSMISSOR
/* This is a simple example show the Heltec.LoRa sended data in OLED.
The onboard OLED display is SSD1306 driver and I2C interface. In order to make the OLED correctly operation, you should output a high-low-high(1-0-1) signal by soft- ware to OLED's reset pin, the low-level signal at least 5ms.
OLED pins to ESP32 GPIOs via this connecthin: OLED_SDA -- GPIO4 OLED_SCL -- GPIO15 OLED_RST -- GPIO16
by Aaron.Lee from HelTec AutoMation, ChengDu, China 成都惠利特自动化科技有限公司 https://heltec.org
this project also realess in GitHub: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series */
#include "heltec.h"
#define BAND 915E6 //you can set band here directly,e.g. 868E6,915E6,433E6
unsigned int counter = 0; String rssi = "RSSI --"; String packSize = "--"; String packet ;
void setup() { //WIFI Kit series V1 not support Vext control Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/);
Heltec.display->init(); Heltec.display->flipScreenVertically(); Heltec.display->setFont(ArialMT_Plain_10); delay(1500); Heltec.display->clear();
Heltec.display->drawString(0, 0, "CNCTEX.Lora Iniciado!"); Heltec.display->display(); delay(1000); } void loop() { Heltec.display->clear(); Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); Heltec.display->setFont(ArialMT_Plain_10);
Heltec.display->drawString(0, 0, "Sending packet: "); Heltec.display->drawString(90, 0, String(counter)); Heltec.display->display();
// send packet LoRa.beginPacket(); /* LoRa.setTxPower(txPower,RFOUT_pin); txPower -- 0 ~ 20 RFOUT_pin could be RF_PACONFIG_PASELECT_PABOOST or RF_PACONFIG_PASELECT_RFO - RF_PACONFIG_PASELECT_PABOOST -- LoRa single output via PABOOST, maximum output 20dBm - RF_PACONFIG_PASELECT_RFO -- LoRa single output via RFO_HF / RFO_LF, maximum output 14dBm */ LoRa.setTxPower(14, RF_PACONFIG_PASELECT_PABOOST); LoRa.print("Hello "); LoRa.print(counter); LoRa.endPacket();
counter++; digitalWrite(LED, HIGH); // turn the LED on (HIGH is the voltage level) delay(10); // wait for a second digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW delay(10); // wait for a second }// =========================================================== //
RESOLVIDO
Eu mudei o código para facilitar, ao invés de mandar informações escrita no display fiz ele apenas com 2 botoes e 2 leds um acedendo um lede do outro
Um liga o led do outro com um interruptor que eu fiz pelo software usando um micro swith
lembrando os botoes tem que esta cons resistores pro pull down
PROGRAMA DO TRANSMISSOR RECEPTOR (so passar esse programa no 2 microcontrolador)
/*This is a simple example show the Heltec.LoRa sended data in OLED.
The onboard OLED display is SSD1306 driver and I2C interface. In order to make theOLED correctly operation, you should output a high-low-high(1-0-1) signal by soft-ware to OLED's reset pin, the low-level signal at least 5ms.
OLED pins to ESP32 GPIOs via this connecthin:OLED_SDA -- GPIO4OLED_SCL -- GPIO15OLED_RST -- GPIO16
by Aaron.Lee from HelTec AutoMation, ChengDu, China成都惠利特自动化科技有限公司https://heltec.org
this project also realess in GitHub:https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series*/
#include "heltec.h"
#define BAND 915E6 //you can set band here directly,e.g. 868E6,915E6,433E6
#define botao 36#define led 12
boolean status_Botao = LOW;boolean statusAnt_Botao = LOW;boolean sinal = 0;String packSize = "--";String packet ;
void cbk(int packetSize) { // recebe e converte dos dados recebidos em texto para facilitar o manuzeiopacket = "";packSize = String(packetSize, DEC);for (int i = 0; i < packetSize; i++) {packet += (char) LoRa.read();}}
void setup() {
pinMode (botao, INPUT);pinMode (led , OUTPUT);
//WIFI Kit series V1 not support Vext controlHeltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.Heltec.Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/);LoRa.receive();}void loop() {
status_Botao = digitalRead (botao);delay(5);if (status_Botao == HIGH) statusAnt_Botao = HIGH;if (status_Botao == LOW && statusAnt_Botao == HIGH) {sinal = !sinal;statusAnt_Botao = LOW;LoRa.beginPacket(); // liga o envioLoRa.setTxPower(14, RF_PACONFIG_PASELECT_PABOOST);LoRa.print(sinal);LoRa.endPacket(); // deliga o envio}
// LoRa.onReceive(cbk);
int packetSize = LoRa.parsePacket();if (packetSize) {cbk(packetSize);}
if (packet == "1") {digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)//delay(10); // wait for a second}
if (packet == "0") {digitalWrite(led, LOW); // turn the LED off by making the voltage LOW//delay(10);}}…
Adicionado por Vitor S Costa ao 10:11 em 30 julho 2020