Ações: rele, MOSFET e servo — Arduino e IoT — semana 10 do 1o trimestre
Semana 10 de 13· 1o trimestre · 01/02 a 30/04
Ações: rele, MOSFET e servo
Atrito com carga: ligar e desligar coisa que não e LED.
Aula 1 — Rele e MOSFET: acender e desligar carga
Objetivos
- Acender e apagar uma carga de verdade usando um módulo de rele de 1 canal.
- Ligar e desligar uma carga de maior corrente com um
MOSFET, em vez de usar o rele. - Explicar por que a maioria dos módulos de rele aciona em
LOW, e escrever o código com essa inversao. - Justificar com um exemplo quando escolher rele e quando escolher
MOSFET. - Apontar, num circuito com carga indutiva, onde entra o diodo de protecao.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 módulo de rele de 1 canal (KY-019 ou equivalente) por aluno
- 1
MOSFETdo kit, do tipoIRF520Nou equivalente, com dissipador - 1 LED de 5 V ou uma lampada de bancada, como carga
- 1 protoboard de 830 pontos e 6 jumpers por aluno
- 1 módulo MB102 por dupla
- 1 diodo 1N4007 por dupla, para a montagem da carga indutiva
Conceitos
O problema do corrente
O pino do ESP32 entrega corrente de ordem de poucos miliamperes. Um LED com resistor passa. Uma lampada, um motor, um aquecedor, não.
Entre o pino e a carga precisa entrar alguma coisa. As duas respostas do kit são o rele e o MOSFET, e elas resolvem o mesmo problema de jeitos diferentes.
| Caracteristica | Rele | MOSFET |
|---|---|---|
| Como funciona | uma parte move e fecha o contato | nenhum movimento, decide eletronicamente |
| Velocidade de comuta | lenta, alguns milissegundos | quase instantanea |
| Durabilidade mecanica | desgasta com muitos ciclos | praticamente não tem desgaste |
| Consumo quando ligado | corrente da bobina, alguns mA | praticamente zero |
| Ruido eletrico | o clique gera interferencia | praticamente silencioso |
| Carga ideal | lampada, aquecedor, ruido tolerado | motor, PWM, ruido proibido |
| Custo | mais caro | barato |
Rele: a inversao que derruba o aluno
O módulo de rele de 1 canal do kit tem um transistor NPN na entrada. Com esse transistor, o rele aciona com o pino em LOW:
digitalWrite(PINO_RELE, LOW); // rele ACENDE digitalWrite(PINO_RELE, HIGH); // rele DESLIGA
Este e o primeiro caso do curso em que a lógica do programa e o contrario do nome da função. E o professor insiste: o HIGH e o LOW são o nivel no pino, não o estado do rele. Não existe "alto" e "baixo" no rele; existem nivel no transistor.
| Pino | Estado do rele | Carga |
|---|---|---|
LOW | fechado, a corrente passa | ligada |
HIGH | aberto, a corrente passa | desligada |
Existem módulos com a lógica invertida e módulos com a lógica direta. O código tem que casar com o módulo da sua bancada, e a unica forma de descobrir qual e o seu e gravar e ver. O professor pede isso na atividade de propósito: e mais barato descobrir do que deduzir.
MOSFET: o gate
O MOSFET do kit e um transistor de efeito de campo. O sinal entra pelo gate, e o gate e como uma pequena antena: ele controla se os outros dois terminais, dreno e fonte, passam corrente.
- O
MOSFETdo kit e um transistor de baixa potência. Ele não suporta corrente de linha: para ligar uma lampada de tomada por tras dele e preciso um rele. - Gate com resistor: a razão e a subida rapida de tensão. No instante da virada, o capacitor interno do gate precisa descarregar, e essa corrente de descarga sai pelo pino. A resistencia limita o pico e protege o GPIO. O valor da aula e 220 ohm a 1 k ohm.
Terminal do MOSFET | Vai para |
|---|---|
| Gate | o GPIO27, com resistor de 220 ohm no caminho |
| Dreno | a carga |
| Fonte | a trilha - |
Carga indutiva e o diodo de protecao
Lampada e resistor: quando a corrente para, o campo eletrico simplesmente desaparece. Bobina e motor: não.
Uma bobina guarda energia em campo magnetico. Quando o campo e desligado de repente, essa energia precisa ir para algum lugar, e ela vira um pico de tensão no transistor — dezenas de vezes a tensão de alimentacao. Isso queima o MOSFET.
A solução e um diodo em paralelo com a carga, com o catodo no lado de alimentacao:
+ ---+--------- carga ---------+ | | +---------|<|---------------+
Quando o campo e desligado, a corrente induzida ganha caminho pelo diodo e não pelo transistor. E por isso que o relé e melhor que o MOSFET para carga indutiva com pouca comutacao: o MOSFET e o MOSFET, a rede fica isolada do circuito.
Atividade
Montagem:
INdo módulo de rele noGPIO26.VCCdo rele na trilha+eGNDdo rele na trilha-.- Gate do
MOSFETnoGPIO27, com resistor de 220 ohm no caminho. - Drain do
MOSFETna carga (LED ou lampada), e a outra perna da carga na trilha-. - Source do
MOSFETna trilha-.
Nota: 10 pontos. Critério de fim: o serial mostra as duas cargas alternando a cada 2,5 segundos, e o aluno sabe dizer, sem consultar, qual nivel aciona o rele.
- Grave o sketch da resolucao com o rele ligado e confirme que o rele não acende no primeiro teste. Anote o nivel que você escreveu.
- Troque para o nivel oposto e grave. Agora o rele acende. Escreva no caderno, ao lado do código, a frase: "este módulo aciona em ___".
- Rode o sketch com o rele acionado por 30 segundos seguidos e conte os cliques. O clique some depois de um tempo? Descreva o que o módulo esta fazendo internamente.
- Monte a parte do
MOSFETe confirme que a carga acende comHIGHnoGPIO27. Em uma frase: por que a lógica do rele e a doMOSFETsão opostas. - Retire o resistor de 220 ohm do gate e grave. Se a carga falhar ou o ESP32 reiniciar, o que isso indica sobre a corrente de pico? Escreva a resposta em uma frase.
- Escreva na lousa o esquema de um motor com o diodo em paralelo. Em uma frase, o que aconteceria com o
MOSFETse o diodo fosse removido.
Resolucao
#include <Arduino.h> const int PINO_RELE = 26; const int PINO_MOSFET = 27; const int INTERVALO = 2500; int voltas = 0; void setup() { pinMode(PINO_RELE, OUTPUT); pinMode(PINO_MOSFET, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Rele e MOSFET — acender e apagar carga"); Serial.println("O modulo rele deste kit aciona em LOW."); Serial.println("rele | mosfet | tempo"); } void loop() { voltas++; digitalWrite(PINO_RELE, LOW); digitalWrite(PINO_MOSFET, HIGH); Serial.print(" ON | ON | "); Serial.print(voltas * INTERVALO); Serial.println(" ms"); delay(INTERVALO); digitalWrite(PINO_RELE, HIGH); digitalWrite(PINO_MOSFET, LOW); Serial.print(" OFF | OFF | "); Serial.print(voltas * INTERVALO); Serial.println(" ms"); delay(INTERVALO); }
Por que assim e não de outro jeito. As duas linhas de acionamento são deliberadamente opostas: LOW para o rele, HIGH para o MOSFET. O professor deixa isso na tela sem comentario na primeira gravação, apaga o LOW e pede para alguem ler em voz alta. A turma ve que o mesmo digitalWrite com o mesmo valor produz efeito oposto nas duas pecas, e essa e a aula inteira.
A razão tecnica esta no transistor do módulo de rele: e um NPN, que conduz quando recebe LOW. O professor mostra o esquema do módulo do kit, aponta o transistor, e explica que por isso LOW aciona. Não e aleatoriedade nem mau funcionamento.
O intervalo e o tempo que cada estado fica na tela. 2500 ms e longo de propósito. O aluno precisa ter tempo de ouvir o clique do rele e ver a carga acesa antes de tudo apagar. Com 500 ms, como na aula do LED, a carga fica Piscando e o aluno não chega a ouvir o som que diferencia as duas pecas.
O pinMode dos dois pinos esta no setup porque sem ele não acontece nada — e aqui o sintoma e especialmente enganoso: o rele fica sempre desligado, sem clique e sem erro, e o aluno acredita que o módulo queimou.
Criterios de correcao
| Critério | Pontos |
|---|---|
| Rele ligado com a inversao correta do módulo da bancada, e a frase anotada no caderno | 3 pontos |
MOSFET com gate, dreno e fonte na posicao correta | 2 pontos |
| Resistor de serie no gate presente | 1 ponto |
| Serial mostra os dois estados e o tempo decorrido | 2 pontos |
| Item 6 respondido: a função do diodo em paralelo com carga indutiva esta escrita | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Escrever HIGH para acionar o rele | Nada acontece, sem erro nenhum | "Este módulo aciona em LOW. O HIGH e o nivel no pino, não o estado do rele." |
Esquecer o pinMode | Rele nunca fecha, e o aluno culpa o módulo | "Sem pinMode(..., OUTPUT) o pino e entrada. E o mesmo erro do dia 2." |
| Resistor do rele | "Funciona, mas não faz barulho" | "Com resistor na entrada, o transistor não satura. Ligue direto no IN." |
MOSFET com gate e fonte trocados | Nada acende | "Gate e o pino do sinal. Fonte vai para o GND, sempre." |
MOSFET sem resistor no gate | Esp32 reiniciando, ou nada funcionando | "Ponha 220 ohm a 1 k em serie com o gate. O pico de comutacao precisa de caminho." |
Usar MOSFET para lampada e reclamar do preco | "O MOSFET custa mais que o rele" | "Para lampada, e mesmo preco e menos pratico. O MOSFET ganha em velocidade e silencio." |
| Motor sem diodo | "O MOSFET queimou no primeiro liga-desliga" | "Diodo em paralelo com a carga, catodo no lado positivo. Sem ele não ha por onde a energia vai." |
Desafio extra
Escreva o código que inverte a lógica do rele em um unico lugar, de modo que exista uma constante RELE_ACIONA_EM_LOW e todo o resto do programa use um "acionar" e um "desacionar" que ninguem precise lembrar a polaridade. Depois explique em três linhas qual das duas pecas escolheria para um portao eletrico que abre e fecha cinco vezes por hora, e por que a escolha muda com a frequência.
A resolucao, compilada
// Aula 1 do dia 10: as duas formas de acender e apagar uma carga. // // Rele e MOSFET estao na mesma aula porque os dois resolvem o mesmo problema — // o ESP32 nao aguenta a corrente da carga — e cada um resolve de um jeito. O // rele carrega a carga electrica; o MOSFET nao. O aluno precisa saber qual // escolher, e isso e o que a atividade cobra. #include <Arduino.h> // Rele de 1 canal. A maioria dos modulos do kit tem a logica invertida: // aciona em LOW. Este comentario e o aviso mais importante da aula. const int PINO_RELE = 26; // MOSFET tipo IRF520N do kit. O sinal vai para o gate, que e a terceira perna. const int PINO_MOSFET = 27; // Tempo que cada estado fica na tela, em milissegundos. const int INTERVALO = 2500; int voltas = 0; void setup() { // Os dois pinos sao saida. Sem o pinMode, nada funciona — e o mesmo // defeito do dia 2, agora aplicado a uma carga que faz barulho. pinMode(PINO_RELE, OUTPUT); pinMode(PINO_MOSFET, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Rele e MOSFET — acender e apagar carga"); Serial.println("O modulo rele deste kit aciona em LOW."); Serial.println("rele | mosfet | tempo"); } void loop() { voltas++; // Rele ligado: neste modulo, escrever LOW aciona. A inversao esta no // software porque nao da para trocar o modulo. digitalWrite(PINO_RELE, LOW); digitalWrite(PINO_MOSFET, HIGH); Serial.print(" ON | ON | "); Serial.print(voltas * INTERVALO); Serial.println(" ms"); delay(INTERVALO); // Rele desligado: escrever HIGH solta o contato. digitalWrite(PINO_RELE, HIGH); digitalWrite(PINO_MOSFET, LOW); Serial.print(" OFF | OFF | "); Serial.print(voltas * INTERVALO); Serial.println(" ms"); delay(INTERVALO); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia10 aula1.
Aula 2 — Servo SG90: a biblioteca de servo e o angulo controlado
Objetivos
- Posicionar um servo
SG90em um angulo escolhido, usando apenas o PWM do ESP32. - Explicar por que o servo espera um sinal de 50 Hz e o que o período de 20 ms tem a ver com isso.
- Converter um angulo em largura de pulso com
map, e converter a largura de pulso em valor de duty. - Medir em quantos microssegundos o servo responde, e comparar com o valor do manual.
- Dizer por que o servo recebe alimentacao separada do pino de sinal.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 servo
SG90(tamanho pequeno, 180 graus) por aluno - 1 protoboard de 830 pontos e 4 jumpers por aluno
- 1 módulo MB102 por dupla, para alimentar o servo pelo 5 V
- 1 potenciometro de 10 k ohm por dupla, para a atividade de comparacao
Conceitos
O servo não sabe onde esta
Esse e o ponto que o professor precisa martelar, porque contraria a intuicao: o servo não tem sensor de posicao. Não existe encoder, não existe potentiometro interno que ele leia. O que existe e um circuito que mede a duracao de um pulso e compara com a posicao atual.
Por isso o servo tem um angulo de verdade entre 0 e 180 graus, e por isso ele "sabe" onde esta: a posicao vem da propria mecanica. Se você projetar um servo que não deveria ter batente, esse servo não existe.
O sinal de 50 Hz
O servo espera um pulso a cada 20 milissegundos. Isso vem da historia do modelo: 50 Hz era a frequência de alternancia da rede eletrica europeia, de onde saiu o número.
| Parametro | Valor | Por que |
|---|---|---|
| Frequência do sinal | 50 Hz | padrao do servo analogico |
| Período | 20 ms | 1 segundo dividido por 50 |
| Pulso para 0 graus | 500 us | 2,5 % do período |
| Pulso para 180 graus | 2500 us | 12,5 % do período |
| Pulso para 90 graus | 1500 us | 7,5 % do período |
O servo e um consumidor de PWM. O pulso alto e o "sinal", e o tempo que ele dura e a informação. Esses números vem do manual do SG90: não são deduziveis. A aula diz isso ao aluno em voz alta, igual fez com a sequência de inicializacao do display.
Do angulo ao duty: a conta
O caminho tem duas etapas, e o aluno precisa ver as duas.
Etapa 1, angulo vira pulso. O map transforma 0-180 em 500-2500:
int pulso = map(angulo, 0, 180, 500, 2500);
Etapa 2, pulso vira duty. O duty e a fracao do período que o pino fica em HIGH:
duty = pulso / periodo
Para 90 graus: pulso de 1500 us em período de 20000 us = 0,075, ou 7,5 %. Com o PWM de 16 bits, 7,5 % de 65536 vale 4915.
O código faz a conta com deslocamento de bits, e não com multiplicação:
uint32_t duty = (uint32_t)(((uint64_t)pulso << RESOLUCAO_PWM) / PERIODO_US);
Multiplicar por 65536 e o mesmo que deslocar 16 bits para a esquerda. Com o uint64_t no meio, o calculo não estoura: 2500 vezes 65536 passa de 32 bits com sobra.
O periferico que faz isso e o LEDC, que esta no chip e no core. E o mesmo periferico que o tone do dia 7 usa por baixo.
Por que a biblioteca não esta sendo usada
A biblioteca ESP32Servo seria o caminho obvio, e ela não esta instalada na maquina do professor. O contrato do material proibe instalar. Então a aula faz a conta na mao, com ledcAttach e ledcWrite, que são do core.
O ganho pedagogico e maior do que parece: escrevendo a conta, o aluno descobre que "biblioteca de servo" e so uma traducao de angulo para duty. No dia 11, quando o servo precisar de ir e voltar, ele sabe o que acontece em cada linha.
A moral que o professor escreve na lousa: quando uma biblioteca não esta disponivel, você não perde a aula. Você perde so a atalho.
Alimentacao: 5 V separado
O servo SG90 tem motor proprio dentro. Na posicao, ele pede de 100 a 250 mA — muito mais do que um pino entrega, e mais do que o regulator da placa consegue fornecer sem passar aperto.
A solução do kit e alimentar pelo 5V do módulo MB102, e usar o pino do ESP32 so para o sinal:
| Fio | Vai para |
|---|---|
VCC do servo | 5V do MB102 |
GND do servo | GND do MB102 |
| Sinal (branco ou amarelo) | o GPIO26 |
O GND tem de ser comum entre o servo e o ESP32. Sem GND comum não existe referência de tensão compartilhada e o sinal não chega ao servo. E o mesmo erro do dia 8 com o DHT11.
O sintoma de alimentacao errada e o mais enganoso de todos: a placa reinicia no meio do movimento. O aluno culpa o código e não a fonte.
Atividade
Montagem:
VCCdo servo no5VdoMB102.GNDdo servo noGNDdoMB102.- Fio de sinal do servo no
GPIO26.
Nota: 10 pontos. Critério de fim: o servo percorre 0, 45, 90, 135 e 180 graus, e o monitor serial mostra o angulo e o pulso correspondente.
- Alimente o servo pelo
MB102e grave o sketch da resolucao. Confira se o servo chega a 180 graus. - Anote o pulso que o serial mostra para cada um dos cinco angulos. Compare com a tabela da aula. Bate?
- Troque
PULSO_MINIMO_USde 500 para 2000, grave e observe. O que o servo faz? Ele ainda obedece a ordem do programa? Escreva a resposta em uma frase. - Desligue o servo do
MB102e alimente-o pelo5Vda propria placa. Grave e mova o servo. Descreva o que aconteceu. Em uma frase: por que o pino de sinal do ESP32 não pode alimentar o servo. - Escreva a conta do
dutypara o angulo de 45 graus no caderno, sem usar o monitor: qual e o pulso, e qual e a fracao do período? Depois confere com o serial e diga se acertou. - Troque
FREQUENCIA_SERVOde 50 para 100 e grave. O servo obedece? Em uma frase, o que a frequência do sinal controla: o angulo ou a atualização da posicao?
Resolucao
#include <Arduino.h> const int PINO_SERVO = 26; const int FREQUENCIA_SERVO = 50; const long PERIODO_US = 1000000L / FREQUENCIA_SERVO; const int PULSO_MINIMO_US = 500; const int PULSO_MAXIMO_US = 2500; const int RESOLUCAO_PWM = 16; const int ANGULOS[] = {0, 45, 90, 135, 180}; const int TOTAL_ANGULOS = 5; const int ESPERA_ANGULO = 700; int posicao = 0; void moverServo(int angulo) { int pulso = map(angulo, 0, 180, PULSO_MINIMO_US, PULSO_MAXIMO_US); uint32_t duty = (uint32_t)(((uint64_t)pulso << RESOLUCAO_PWM) / PERIODO_US); ledcAttach(PINO_SERVO, FREQUENCIA_SERVO, RESOLUCAO_PWM); ledcWrite(PINO_SERVO, duty); } void setup() { Serial.begin(115200); Serial.println(); Serial.println("Servo SG90 — angulo controlado"); Serial.println("angulo | pulso us | duty"); moverServo(90); delay(300); } void loop() { int angulo = ANGULOS[posicao]; moverServo(angulo); Serial.print(angulo); Serial.print("graus | "); Serial.print(map(angulo, 0, 180, PULSO_MINIMO_US, PULSO_MAXIMO_US)); Serial.print(" us | "); Serial.print(ledcRead(PINO_SERVO)); Serial.println(); posicao++; if (posicao >= TOTAL_ANGULOS) { posicao = 0; } delay(ESPERA_ANGULO); }
Por que assim e não de outro jeito. O ledcAttach esta dentro da função moverServo, e não no setup. Isso parece desperdicio — toda chamada refaz a mesma coisa — e funciona porque o periferico aceita reatribuicao sem custo visivel a essa taxa de chamada. O professor explica a troca: se o servo usasse duas frequencias diferentes, o attach teria que ficar no setup, uma vez por frequência, e o aluno entenderia que attach e "configurar", não "ligar".
O ledcRead no serial e o que prova que a conta foi feita. Sem ele, o aluno tem uma linha com o angulo e outra com um número que ele não sabe de onde saiu. Com ele, a coluna do duty e a evidência de que map e o deslocamento de bits fizeram o trabalho.
O map aparece duas vezes no programa: uma em moverServo, outra no Serial.print. Isso e de propósito. Se o serial lesse o duty calculado e dividisse de novo, o aluno veria duas contas e não saberia qual delas o servo recebeu. Imprimir o pulso de novo com map obriga as duas contas a concordarem — e quando o aluno muda PULSO_MINIMO_US, as duas mudam juntas e ele vê o efeito.
O moverServo(90) antes do primeiro Serial.begin deixa o servo no meio. Sem isso, o servo fica em 0 até o primeiro comando, e se ele estiver preso em uma posicao por maquina, o primeiro movimento e um tranco.
O delay(700) entre angulos e o tempo que o servo leva para chegar na posicao. Abaixo disso, o comando chega antes do movimento terminar e o servo fica tremendo entre duas posicoes. 700 ms e mais do que suficiente para um SG90, e o aluno descobre que "esperar o actuator terminar" e uma categoria de tempo diferente de "esperar o intervalo".
Criterios de correcao
| Critério | Pontos |
|---|---|
Servo alimentado pelo 5V do MB102, com GND comum e sinal no GPIO26 | 2 pontos |
Conversao angulo para pulso feita com map, e pulso para duty com a conta do período | 3 pontos |
| Cinco angulos percorridos, e o servo chega a 180 | 2 pontos |
| Serial mostra angulo e pulso, e a coluna do duty confere com a conta do caderno | 1 ponto |
| Item 6 respondido: a frequência controla a atualização, não o angulo | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
| Servo alimentado pelo pino do ESP32 | "A placa reinicia quando o servo anda" | "Alimente pelo 5V do MB102. O pino so manda sinal." |
| Servo tremendo entre duas posicoes | "Fica vibrando não chega" | "Aumente o ESPERA_ANGULO. O comando esta chegando antes do movimento terminar." |
| Valores de pulso trocados | "O servo vai para 180 quando peço 0" | "Verifique se PULSO_MINIMO_US e PULSO_MAXIMO_US estão em ordem crescente." |
Usar map de 500 para 2500 em vez de 2500 para 500 | "O servo anda ao contrario" | "O map mapeia o primeiro intervalo no segundo, na ordem. Minimo para minimo." |
duty calculado sem dividir pelo período | "O servo mal se mexe" | "O duty e uma fracao do período. Divida o pulso pelo período." |
uint32 estourando na conta | "O servo vai para um angulo aleatorio" | "Use uint64_t no meio do calculo e converta no fim." |
| Sem GND comum | "O servo não responde a nada" | "GND do servo tem de ir no mesmo GND do ESP32." |
Desafio extra
Escreva a função que faz o servo ir de 0 a 180 e voltar, em movimento continuo, usando millis em vez de delay — um passo de meio grau a cada 20 ms. Depois calcule quantos passos o servo precisa para varrer a faixa inteira e escreva, em três linhas, por que isso e uma taxa de atualização e não um angulo.
A resolucao, compilada
// Aula 2 do dia 10: o servo, que e a saida que aceita um numero como comando. // // Um servo nao e um LED com brilho. Ele recebe um numero de graus e vai // ate la. O numero vira tempo de pulso: 500 microssegundos sao 0 graus, 2500 // microssegundos sao 180. Esta aula e sobre essa conversao, feita com o PWM // do proprio chip, sem depender de biblioteca de servo. // // A ESP32Servo nao esta instalada na maquina do professor. Em vez de instalar, // o codigo usa o periferico LEDC, que esta no core e faz exatamente o que a // biblioteca faria: gerar 50 Hz com a largura de pulso pedida. #include <Arduino.h> // Pino de sinal do servo. 26 e um GPIO comum. const int PINO_SERVO = 26; // Frequencia do sinal de controle, em Hz. O servo espera 50 Hz: e um padrao // de fabrica consolidado, nao uma escolha do ESP32. O periodo e 1 dividido // por 50, ou seja, 20 milissegundos. const int FREQUENCIA_SERVO = 50; // Periodo do sinal em microssegundos: 1 segundo tem 1 milhao de microssegundos, // dividido por 50 Hz da 20 milissegundos. const long PERIODO_US = 1000000L / FREQUENCIA_SERVO; // Largura de pulso que significa 0 graus e 180 graus, em microssegundos. // Estes numeros vem do manual do SG90, nao da logica: sao a unica parte do // codigo que o aluno nao consegue adivinhar. const int PULSO_MINIMO_US = 500; const int PULSO_MAXIMO_US = 2500; // Resolucao do PWM: 16 bits dao 65536 niveis. Com um periodo de 20 mil // microssegundos, sobra meio microssegundo de precisao, que e mais do que o // servo distingue. const int RESOLUCAO_PWM = 16; // Os quatro angulos do varredor, em graus. const int ANGULOS[] = {0, 45, 90, 135, 180}; const int TOTAL_ANGULOS = 5; // Tempo que o servo fica parado em cada angulo, em milissegundos. const int ESPERA_ANGULO = 700; int posicao = 0; // Converte o angulo em largura de pulso e escreve no PWM. Esta e a funcao que // a biblioteca de servo faria: ela existe aqui para o aluno ver o calculo. void moverServo(int angulo) { // map transforma 0-180 em 500-2500. E a mesma funcao que o aluno ja usou // com o potenciometro no dia 5, e por isso que ela vale a pena ter aprendido. int pulso = map(angulo, 0, 180, PULSO_MINIMO_US, PULSO_MAXIMO_US); // O duty e a fracao do periodo que o pino fica em HIGH. Em um periodo de // 20 mil microssegundos, um pulso de 1 mil e 5% de duty. Com 16 bits, 5% // vale 0,05 vezes 65536, que e 3276. uint32_t duty = (uint32_t)(((uint64_t)pulso << RESOLUCAO_PWM) / PERIODO_US); ledcAttach(PINO_SERVO, FREQUENCIA_SERVO, RESOLUCAO_PWM); ledcWrite(PINO_SERVO, duty); } void setup() { Serial.begin(115200); Serial.println(); Serial.println("Servo SG90 — angulo controlado"); Serial.println("angulo | pulso us | duty"); Serial.println(); // Deixa o servo no meio antes da primeira mensagem, para nao bater no // batente de 180 no reposicionamento. moverServo(90); delay(300); } void loop() { int angulo = ANGULOS[posicao]; moverServo(angulo); Serial.print(angulo); Serial.print("graus | "); Serial.print(map(angulo, 0, 180, PULSO_MINIMO_US, PULSO_MAXIMO_US)); Serial.print(" us | "); Serial.print(ledcRead(PINO_SERVO)); Serial.println(); posicao++; if (posicao >= TOTAL_ANGULOS) { posicao = 0; } delay(ESPERA_ANGULO); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia10 aula2.
