segunda-feira, 14 de fevereiro de 2011

Dimmer Digital Futurístico com Arduíno (Parte 2)



Neste post falaremos sobre a teoria do funcionamento do dimmer digital com Arduino desenvolvido pelo Vinicius Senger e como implementar um controle de intensidade preciso utilizando um sensor de distância IR.

A rede elétrica que temos disponível na maioria das residências no Brasil é senoidal com 127 volts, sendo que a freqüência de oscilação é de 60 Hz (sessenta ciclos por segundo). Isso significa que temos um ciclo a cada 16,66 ms.

Para implementar o controle de luminosidade de uma lâmpada em AC, normalmente utilizamos um componente chamado TRIAC.

TRIAC ou Triode for Alternating Current é um componente eletrônico equivalente a dois retificadores controlados de silício (SCR ou tiristores) ligados em antiparalelo e com os terminais de disparo “gate“ ligados juntos.
Este tipo de ligação resulta em uma chave eletrônica bidirecional que pode conduzir a corrente elétrica nos dois sentidos. O TRIAC faz parte da família de tiristores.





Um TRIAC pode ser disparado tanto por uma corrente positiva quanto negativa aplicada no terminal de disparo (gate). Uma vez disparado, o dispositivo continua a conduzir até que a corrente elétrica caia abaixo do valor de corte.
Isto torna o TRIAC ideal para o controle de circuitos de corrente alternada, permitindo acionar grandes potências com circuitos acionados por correntes da ordem de miliamperes.

Podemos controlar o início da condução do dispositivo, aplicando um pulso em um ponto pré-determinado do ciclo de corrente alternada, o que permite controlar a percentagem do ciclo que estará alimentando a carga (também chamado de controle de fase).

Quando usado com cargas indutivas, como motores elétricos, é necessário que se assegure que o TRIAC seja desligado corretamente no final de cada semi-ciclo.
Para circuitos de maior potência, podemos utilizar dois SCRs ligados em antiparalelo, o que garante que cada SCR estará controlando um semi-ciclo independente, não importando a natureza da carga.

Para que seja possível disparar o TRIAC no momento correto, precisamos sincronizar esse disparo com a forma de onda da rede elétrica, para isso utilizamos um circuito detector de passagem por zero volts, no nosso projeto esse circuito é formado pelos componentes R1 a R4 , OK1 (4N35) e R5 (ver diagrama elétrico) .

A cada passagem da onda por zero volts, é gerada uma interrupção (INT0) no ATMEGA, ou seja , temos uma interrupção a cada 8,33 ms. Devemos então gerar um pulso no gate do TRIAC dentro de um intervalo entre 150 microsegundos até 8 milisegundos após a interrupção, assim ele conduzirá corrente elétrica em parte do semi-ciclo atual, seja ele positivo ou negativo.
Quanto mais antecipado for o disparo do TRIAC maior será a intensidade da lâmpada.

obs: não devemos disparar o TRIAC exatamente após a interrupção, pois a tensão ainda estará baixa e não haverá corrente suficiente para manter o TRIAC conduzindo, por isso demos uma margem de 150 microsegundos.

Neste projeto o circuito responsável pelo acionamento do TRIAC é formado pelos componentes OK2 (MOC3021), R7 e TRIAC-1.
Lembrando que tanto OK1 como OK2 tem também as funções de isolar o circuito AC do circuito DC.

Para controlarmos o instante de disparo do TRIAC, quando o ATMEGA recebe uma interrupção externa (INT0), iniciamos o TIMER1 com um valor em us (microsegundos) que é inversamente proporcional a ultima leitura válida do sensor de distância, sendo que transcorrido esse período será gerada uma interrupção indicando o momento do disparo do TRIAC.
O momento do disparo definirá a intensidade luminosa da lâmpada.
A implementação da interrupção utilizando o TIMER1 é feita utilizando a biblioteca TIMER1.

Com a utilização do TIMER1 da forma descrita acima, liberamos o ATMEGA para executar outras funções durante o período entre a detecção de passagem por zero e o momento do disparo do TRIAC, ou seja , ganhamos até 8 ms por semi-ciclo. Esse tempo é aproveitado por exemplo para fazer a multiplexação dos displays de 7 segmentos que indicam a % da potência aplicada à lâmpada (método Loop), evitando assim a necessidade de mais componentes para controlar esses displays.

A figura abaixo mostra as formas de onda da rede AC, da saída do detector de passagem pelo zero volts, do momento de disparo do gate do TRIAC e da forma de onda aplicada na lâmpada com 3 níveis diferentes de intensidade (100%, 50% e 10%).

Vejam que quanto mais cedo for disparado o TRIAC, maior será a área do ciclo aplicado a lâmpada (em vermelho) , e consequentemente maior será a intensidade luminosa.



Para definir o momento de leitura do sensor de distância, utilizamos a biblioteca MS_Timer2 de forma que seja gerada uma interrupção a cada 200ms. No tratamento dessa interrupção efetuamos a leitura do sensor de distância e definimos os dígitos de cada display 7 segmentos de acordo com a leitura efetuada.

Como pode ser observado no código disponibilizado , o método loop( ) deste projeto, fica o tempo todo atualizando a informação nos displays de 7 segmentos, e todas as demais funções são tratadas a partir de interrupções externa (INT0 “zerocross detection”) e internas(Timer1 “disparo do TRIAC” / TIMER2 “leitura do sensor distância IR”).

O resultado dessa implementação é um dimmer preciso e com indicação visual da potência aplicada na lâmpada.

Nas figuras abaixo temos o diagrama elétrico completo e a arte final em formato de shield.





Lista de Componentes:

R1 a R4 : resistores de 47K
R5 : resistor de 10K
R6 e R7 : resistores de 330ohm
OK1: fotoacoplador 4N35
OK2: MOC3021
TRIAC-1: BTA16
Display de 7 segmentos com 3 digitos (anodo comum).
Protoshield
sensor de distância analógico (IR)

O protótipo foi montado em uma protoshield que atendeu muito bem, possibilitando a montagem de todos os componentes com bastante facilidade. (foto abaixo)



abaixo temos o código completo do dimmer:

#include <TimerOne.h>   // inclui biblioteca Timer1
#include <MsTimer2.h>   // Inclui biblioteca MsTimer2
//define portas associadas ao display 7seg
//int displaySeg[] = {a, b, c, d, e, f, g, pd};
int displaySeg[] = {6, 1, 17, 15, 14, 5, 18, 12};
//int displayPorts[] = {Anodo1, Anodo2, Anodo3};
int displayPort[] = {7, 4, 3};

// define padroes do display 7SEG, de 0 - 9 e apagado
// 1 = segmento apagado, 0 = segmento aceso
byte seven_seg_digits[11][8] =  {
{ 0,0,0,0,0,0,1,1 },  // = 0
{ 1,0,0,1,1,1,1,1 },  // = 1
{ 0,0,1,0,0,1,0,1 },  // = 2
{ 0,0,0,0,1,1,0,1 },  // = 3
{ 1,0,0,1,1,0,0,1 },  // = 4
{ 0,1,0,0,1,0,0,1 },  // = 5
{ 0,1,0,0,0,0,0,1 },  // = 6
{ 0,0,0,1,1,1,1,1 },  // = 7
{ 0,0,0,0,0,0,0,1 },  // = 8
{ 0,0,0,1,1,0,0,1 },   // = 9
{ 1,1,1,1,1,1,1,1 }   // = blank
};

int sensorDistancia = 2;  // porta associada ao sensor distancia IR
int triacGatePin = 10 ;  // porta associada ao gate do TRIAC

int distancia = 0 ;  // variavel para armazenar distancia
int power= 0 ;  //inicializa variavel que controla potencia na lampada com 0 (lampada apagada)
int centena = 10 ;  // armazena informacao a ser apresentada no display de centena (inicializa com 10 = apagado)
int dezena = 10 ;  // armazena informacao a ser apresentada no display de dezena (inicializa com 10 = apagado)
int unidade = 10 ;  // armazena informacao a ser apresentada no display de unidade (inicializa com 10 = apagado)

void setup() {  
//Serial.begin(9600);
pinMode(triacGatePin, OUTPUT);
for (int i=0 ; i<=3 ; i++) {  
pinMode(displayPort[i], OUTPUT);  
}
for (int j=0 ; j<=7 ; j++) {  
pinMode(displaySeg[j], OUTPUT);  
}
attachInterrupt(0, zeroCrossInterrupt, CHANGE);  //associa interrupcao INT0 com funcao "zeroCrossInterrupt"

MsTimer2::set(200, leSensor); // inicializa Timer2 com 200 milisegundos (tempo entre leituras do sensor de dintancia)
MsTimer2::start();  // start do timer2
}

void loop() {
atualizaDisplay(unidade, dezena, centena);  // atualiza informacoes nos displays de 7 segmentos
}

void atualizaDisplay(int uni, int dez, int cen) {
for (int k = 0; k < 8; ++k) {
digitalWrite(displaySeg[k], seven_seg_digits[uni][k]);
}
digitalWrite(displayPort[2], HIGH);
delay (1);
digitalWrite(displayPort[2], LOW);

for (int k = 0; k < 8; ++k) {
digitalWrite(displaySeg[k], seven_seg_digits[dez][k]);
}
digitalWrite(displayPort[1], HIGH);
delay (1);
digitalWrite(displayPort[1], LOW);

for (int k = 0; k < 8; ++k) {
digitalWrite(displaySeg[k], seven_seg_digits[cen][k]);
}
digitalWrite(displayPort[0], HIGH);
delay (1);
digitalWrite(displayPort[0], LOW);

}

void zeroCrossInterrupt(){  // trata interrupcao INT0 
if(power > 0) {
long dimtime = int(map(power,0,100,8000,150));  // calcula o tempo de delay para o disparo do TRIAC
Timer1.initialize (dimtime);  // inicializa o TIMER1 com o delay calculado
Timer1.attachInterrupt(gateTRIAC);  //associa a funcao gateTRIAC com  Interrupcao do TIMER1
Timer1.start();  // inicia contagem TIMER1
}  else {
digitalWrite(triacGatePin, LOW);   // mantem gate do TRIAC desativado.  
Timer1.stop();
}
}

void gateTRIAC () {  // trata interrupcao do TIMER1 gerando pulso no gate do TRIAC
digitalWrite(triacGatePin, HIGH);  // dispara o Triac
delayMicroseconds(15);  // aguarda 15 microsegundos para garantir disparo do TRIAC
digitalWrite(triacGatePin, LOW);   // desabibilta gate do TRIAC 
Timer1.stop();
}

void leSensor() {
distancia = analogRead(sensorDistancia);  // le sensor de distancia
//Serial.println(distancia);
distancia = map(distancia,70,600,120,0);  // mapeia distancia lida para uma faixa de potencia entre 0 e 120
if (distancia < 0) {
distancia = 0;
}

if (distancia >=0 && distancia <= 100) {  // se distancia <= a 100 atualiza potencia
power=distancia;
}

centena = int(power / 100);  // atualiza variaveis que contem informacoes dos displays
dezena = int((power - (centena*100)) / 10);
unidade = int(power - (centena*100)-(dezena*10));
if (centena == 0 && dezena == 0){ 
centena = 10;
dezena = 10;
}
if (centena == 0){
centena = 10;
}
}



Segue um vídeo demonstrando o funcionamento do dimmer.

34 comentários:

  1. fiquei com uma dúvida..o q seria ac1 e ac2 ?

    ResponderExcluir
  2. outra coisa, a porta 2 (digital) está conectada ao 4n35, juntamente com alimentação de 5v que sofre uma queda no R5, mas na programação eu não encontrei nada dessa porta, eu vejo vc declarando ela para o sensor de distância,o sensor está conectado em A2?...não tem algo errado aí...estou quebrando a cabeça aqui....para estudar esse projeto..e modificar para colocar umas coisinhas e fazer o controle do meu quarto

    ResponderExcluir
  3. AC-1 e AC-2 são os terminais que devem ser conectados ao fase e neutro da rede elétrica. A porta digital 2 esta sendo utilizada como interrupção externa (INT0). Toda vez que a fase da rede AC passa por 0 volts é gerada uma interrupção no ATMEGA através dessa porta . Ele usa ela para sincronizar o ponto de disparo do TRIAC. O sensor de distância é analógico e está na porta analógica 2 e não na digital 2.
    Se continuar com dúvida me avisa.

    ResponderExcluir
  4. Vi que o TRIAC nas lista é o BTA16 e no circuito é o BT138. O BT138 tem 500,600 e 800 versões E e F. Qual foi o BT138 usado? Tenho receio de usar outro TRIAC e ter problemas. Agradecido

    ResponderExcluir
  5. uma dúvida pelo esquema o pino 2 digital não teria que estar configurado para ser input?
    tipo:
    void setup{

    pinMode (2,INPUT);
    }
    Para poder receber os valores da posição do zeroconf vindo do 4n?

    ResponderExcluir
    Respostas
    1. Normalmente não é necessário declarar que uma porta é de entrada pois essa é a config default que elas assumem quando o arduino é resetado. Porém neste caso a porta é utilizada como interrupção, então o comando "attachInterrupt(0, zeroCrossInterrupt, CHANGE);" é que define como essa porta vai se comportar.

      Excluir
  6. Bom vou escrever o q estou fazendo, estou modificando o projeto, colocando um ldr no lugar do sensor de distância e retirando o display.

    Bom eu estou conectando o zercross no int1 (porta digital 3 do Arduino Uno)

    Retirei as conexões que seriam para o display

    O código está assim:
    #include // inclui biblioteca Timer1
    #include // Inclui biblioteca MsTimer2

    int ldr = 0; // porta associada ao ldr
    int triacGatePin = 10 ; // porta associada ao gate do TRIAC
    float sensor=0;
    int luminosidade = 0 ; // variavel para armazenar luminosidade
    int power=1 ; //inicializa variavel que controla potencia na lampada com 0 (lampada apagada)

    void setup() {

    pinMode(triacGatePin, OUTPUT);
    attachInterrupt(1, zeroCrossInterrupt, CHANGE); //associa interrupcao INT1 com funcao "zeroCrossInterrupt"

    MsTimer2::set(200, leSensor); // inicializa Timer2 com 200 milisegundos (tempo entre leituras do sensor de luminosidade)
    MsTimer2::start(); // start do timer2
    }

    void loop() {

    }

    void zeroCrossInterrupt(){ // trata interrupcao INT0
    if(power > 0) {
    long dimtime = int(map(power,0,100,8000,150)); // calcula o tempo de delay para o disparo do TRIAC
    Timer1.initialize (dimtime); // inicializa o TIMER1 com o delay calculado
    Timer1.attachInterrupt(gateTRIAC); //associa a funcao gateTRIAC com Interrupcao do TIMER1
    Timer1.start(); // inicia contagem TIMER1
    } else {
    digitalWrite(triacGatePin, LOW); // mantem gate do TRIAC desativado.
    Timer1.stop();
    }
    }

    void gateTRIAC () { // trata interrupcao do TIMER1 gerando pulso no gate do TRIAC
    digitalWrite(triacGatePin, HIGH); // dispara o Triac
    delayMicroseconds(15); // aguarda 15 microsegundos para garantir disparo do TRIAC
    digitalWrite(triacGatePin, LOW); // desabibilta gate do TRIAC
    Timer1.stop();
    }

    void leSensor() {

    luminosidade = map(luminosidade,430,910,0,120); // mapeia luminosidade lida para uma faixa de potencia entre 0 e 120
    if (luminosidade < 0) {
    luminosidade = 0;
    }

    if (luminosidade >=0 && luminosidade <= 100) { // se luminosidade <= a 100 atualiza potencia
    power=luminosidade;
    }
    }

    Infelizmente não está funcionando.

    Todo o resto do circuito está igual ao esquema postado por vc....
    Agradeço a ajuda, abraços

    ResponderExcluir
  7. Isso vale para lâmpadas fluorescentes?

    ResponderExcluir
  8. Cara que coisa seu post caiu do céu! Faz tempo que to quebrando cabeça pra fazer algo desse tipo.
    Só uma duvida quero fazer um sensor por IR o arduino recebe o sinal tipo de alguma tecla especifica, tipo o controle do volume (+ ou -).
    Reteria como dar um dica por onde posso começar?

    ResponderExcluir
    Respostas
    1. Procure pela biblioteca IRRemote que ela vai ajudar bastante.

      Excluir
  9. Este comentário foi removido por um administrador do blog.

    ResponderExcluir
  10. Boa noite José Luis. Primeiro gostaria de parabenizá-lo pelo excelente post!
    Gostaria também de fazer uma pergunta: como você fez essa arte final da pcb já no formato da shield? Existe algum modelo pronto para baixar? Estou perguntando pois estou fazendo um projeto com a placa Mega e seria de grande ajuda se tivesse o modelo da shield dessa placa pronto.
    Desde já, muito obrigado!

    ResponderExcluir
    Respostas
    1. Procure por "MegaShield", acho que é exatamente o que você precisa. Acabei de ver numa loja online por R$ 69,00 + frete, não posto o link pois não sei se o blog permite.

      Excluir
    2. Utilizei o EAGLE para criar a placa, mas como o Magno disse tem o Megashield que para um protótipo funciona bem.

      Excluir
    3. http://www.dx.com/p/arduino-prototyping-shield-pcb-board-blue-138294#.UzIO8_ldVqU

      Esse site da china tem tudo para arduino, eu que moro no paraná demora 1 mes pra chegar... FRETE GRATIS

      Excluir
  11. Este comentário foi removido pelo autor.

    ResponderExcluir
  12. Olá, Bom Dia

    Primeiramente parabéns pelo post, com certeza foi de grande valia este conhecimento para todos os eletrônicos e simpatizantes.

    Vamos ver se poderiam me ajudar, estou com uma dúvida: utilizando um microcontrolador, no caso o ATmega8535, um opto-diac (moc 3021) e um triac BTA40600B, para a utilização apenas como chave estática, poderia realizar o controle do on/off, sem a utilização do circuito zero-crossing, apenas setando a saída digital do micro para o opto como sendo "1" ou "0"?

    Valeu!

    ResponderExcluir
    Respostas
    1. Wagner, os TRIAC somente conduzem enquanto tiver voltagem suficiente entre os terminais Anodos. Por isso é necessário ficar disparando o gate dele em todo semiciclo da senóide para que ele continue conduzindo. Sendo assim o circuito de detecção de passagem por zero é necessário.

      Excluir
  13. José, tudo bem?

    Cara, estou queimando neuronios para controlar luminosidade de 6 lampadas, porem, com apenas 1 Sensor de Zero (Visto estarem todas na mesma rede não vejo necessidade de mais de um Sensor). O Hardware esta legal consigo controlar muito bem, porem, apenas 1 lampada por vez, se precisar usar as 6 ao mesmo tempo o código se perde todo... estou perdido e fumaceando a cabeça com o código para fazer rodar as 6 ao mesmo tempo.
    Ja passou por isso? Se sim, poderia compartilhar com a comunidade?

    Abraços

    ResponderExcluir
    Respostas
    1. Nesse caso o segredo está no código, pois você já viu que o hardware funciona. Recomendo você dividir as intensidades em 128 níveis possíveis e minimizar o máximo as rotinas. Já ví um dimmer de 8 canais usando um microcontrolador similar ao ATMEGA. Então é possível fazer, mas o segredo é otimizar o código ao máximo. A cada lâmpada disparada ajuste o timer para o próxima.

      Excluir
  14. Boa tarde.

    Não entendo muito de eletrônica, mas sou um hobbista e fiquei com algumas duvidas de conceito deste projeto e espero que possa me esclarecer.

    1 - Os R1,R2,R3,R4 são todos de 47k e estão ligados circuito misto com o 4N35, sendo assim, R1 e R2 em paralelo resultam em 23k5 e R3 e R4 também, e depois disso somando-se em série retornam para 47k. Não seria melhor, ou mais fácil usar apenas um resistor de 47k? Existe algum motivo para isso?

    2 - O R5 é um pull-up para D2 correto? Por que usou pull-up ao invés de pull-down? Vi isso no circuito "Detectando status de lâmpada com optoacoplador 4N25 e Arduino" do Paulo Trentin. Conheço o funcionamento de ambos mas não entendi por foi usado um ou outro?

    3 - Visto que o MOC já possui um triac interno porque foi necessário utilizar um externo? Imagino ser por causa da corrente máxima de controle. É isso mesmo? Qual é o máximo de cada um?

    4 - No esquema, é usado AC-2 como entrada AC do MO, que por sua vez chaveia o BTA em AC-2.
    Poderia ter sido utilizado o AC-1 e o AC-2 de forma alternado, por exemplo. AC-1 para a entrada AC do MOC chaveando o BTA de AC-2?

    Desculpe por tantas perguntas, mas achei bem legal esse projeto e apesar de ter conseguido faz~e-lo funcionar não entendi bem estas parte.

    Obrigado por compartilhar.

    Carlos

    ResponderExcluir
    Respostas
    1. 1- como a voltagem é elevada, a idéia dessa configuração é dividir a potência dissipada em 4 resistores ao invés de apenas um, assim podemos usar resistores menores.

      2-Apenas uma questão de lógica, mas no arduino normalmente usamos pull-up e da forma como foi colocado o 4N25.

      3- porque a potência da lâmpada não é suportada pelo MOC e o MOC tem a função de isolar o circuito digital do AC.

      4- não entendi essa pergunta, me desculpe.

      Excluir
    2. Montei o circuito, mas usando um resistor de 47K de 3W.
      Estou com problemas para detectar a passagem por zero, quando fico ligando e desligando da tomada, e ele parece detectar, de modo geral ele esta com a porta sempre em alta.

      Você sabe o que pode ser? Como você testaria a placa sem usar o programa final, apenas para teste mesmo. Algo do tipo a cada 120 passagens por zero acende ou apaga o led da porta 13.

      Excluir
  15. Este comentário foi removido pelo autor.

    ResponderExcluir
  16. Posso colocar lampadas em paralelo neste circuito? Se sim, quantas? Se não puder, como faço para aumentar a quantidade de lampadas? Vlw

    ResponderExcluir
  17. Este comentário foi removido pelo autor.

    ResponderExcluir
  18. otimo post amigo, muito interessante...

    agora, uma duvida... posso substituir o MOC3021 por um MOC3063 sem problemas?

    sendo q um datasheet q achei do MOC3021 nao diz q ele eh zerocrossing, mas o MOC3063 sim....

    ResponderExcluir
  19. Boa tarde,
    Gostaria de lhe perguntar se deve-se proteger o circuito contra tensão reversa sobre o emissor do 4N35 que é 6V.
    Obrigado!

    ResponderExcluir
  20. Parabéns amigo, excelente projeto e ótimo post. Eu montei um sistema similar ao seu, aliás, inspirado, para controlar uma potência muito alta, no lugar do sensor utilizei botões para mudar o tempo de disparo do triac. O circuito que utilizei eu mesmo projetei, mas a sua ideia no software me ajudou muito mesmo. Abraços continue postando por favor valeu.

    ResponderExcluir
  21. Boa noite pessoal,
    consigo usar o Arduino nano para este projeto?
    abraço

    ResponderExcluir
    Respostas
    1. Pode sim, essa ideia é parecida com a que tem no site arduino.cc.
      Fiz uma montagem muito similar a essa usando o Arduino nano.

      Excluir