C++: variáveis e tipos — Arduino e IoT — semana 2 do 1o trimestre
Semana 2 de 13· 1o trimestre · 01/02 a 30/04
C++: variáveis e tipos
A mesma lógica do primeiro trimestre de JavaScript, agora com tipo declarado.
Aula 1 — Declarar variável com tipo: o que o JavaScript não exige
Objetivos
- Declarar variável com tipo em C++ e dizer o que muda em relação ao
letdo JavaScript. - Escolher o tipo certo para cada valor do kit e justificar a escolha pelo
sizeofde cada tipo. - Declarar pino, tempo e valor de circuito como
constno topo do arquivo. - Reproduzir no monitor serial o estouro do
intde 32 bits e explicar por que ele não gera erro.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
- 2 a 5 LEDs de 3,3 V e 2 a 5 resistores de 330 Ω por aluno
- 1 módulo de alimentação MB102 por dupla
- Lista dos tipos do ESP32 impressa, uma por aluno
Conceitos
O tipo vem antes do nome
Em JavaScript, let x = 5 cria uma variável cujo tipo é deduzido do valor, e que aceita qualquer coisa depois. Em C++ a forma é int x = 5: o tipo vem antes do nome, e o compilador passa a recusar tudo que não couber nele.
O ganho é que o erro aparece antes de gravar. int x = "texto" não compila. Em JavaScript isso rodaria e o defeito apareceria três semanas depois, em outra linha, sem relação com a linha que causou.
Os tipos do dia a dia
| Tipo | Tamanho | Faixa | Para que serve no kit |
|---|---|---|---|
bool | 1 byte | true e false | se o botão está apertado |
char | 1 byte | um caractere | o texto de uma letra |
int | 4 bytes | -2147483648 a 2147483647 | pino, tempo, contador, leitura do ADC |
long | 8 bytes | valores muito maiores | tempo acumulado de programa |
unsigned int | 4 bytes | 0 a 4294967295 | contador que nunca fica negativo |
float | 4 bytes | com casas decimais | tensão em volts |
Todos os inteiros do ESP32 têm 32 bits, mesmo em int. É o dobro do int do Arduino Uno, e ainda assim o limite cabe na conversão de sensor do dia.
const é o que segura o circuito
const int PINO_LED = 2; // tipo, const, nome, valor const int RESISTOR_OHMS = 330;
Declarar o pino como const no topo do arquivo resolve três problemas de uma vez: o professor sabe onde olhar quando o LED não acende, o compilador recusa um valor novo no meio do programa, e o número do pino não se repete em cinco linhas diferentes.
O estouro que não avisa
Quando um int chega em 2147483647 e recebe mais um, ele não dá erro: sai pelo valor mais negativo, -2147483648. O programa continua rodando com o número errado. Em JavaScript o mesmo número simplesmente continua crescendo, e é por isso que a checagem de limite entra no dia de hoje e não no projeto final.
Atividade
Montagem:
- Resistor de 330 Ω entre o
GPIO2e um lado do LED. - O outro lado do LED na trilha
+da protoboard. - Jumper macho-fêmea levando o
GPIO2ao resistor.
O lado longo do LED é o anodo e vai para o positivo. Invertido, o LED não acende — e não dá erro.
- No topo do seu sketch, antes de qualquer função, declare com
consto pino do LED, o valor do resistor e o tempo do intervalo. Os três com tipo declarado. - Grave a resolução e confirme que o LED pisca e que o monitor serial mostra a contagem subindo.
- Rode
sizeofembool,char,int,long,floateunsigned int, e anote os seis números na mesma ordem da tabela do caderno. - Imprima
2147483647 + 1no monitor serial e explique em uma frase por que a placa não acusou erro. - Troque o tipo do seu contador de voltas de
intparaunsigned inte diga o que muda para um contador que só sobe. - Escreva a linha que calcula quantos elementos tem um vetor de inteiros sem contar à mão, e justifique o
sizeofque aparece no denominador.
Nota: 10 pontos. Critério de fim: o monitor serial mostra os seis tamanhos de tipo, a linha do estouro e a contagem de voltas subindo.
Resolucao
// Aula 1 do dia 2: declarar variável com tipo. // // O aluno chega do JavaScript, onde `let x = 5` já basta e todo número é do // mesmo tipo. Aqui o tipo vem ANTES do nome. Isso não é burocracia: é o que // impede a placa de fazer conta com o número errado sem avisar. #include <Arduino.h> // ---- Constantes do kit: tipo, nome e valor, nessa ordem ---- // `const` declara que o valor não muda depois. O tipo antes do nome é a // novidade do dia, e `const` é o que impede alguém de trocar o pino no meio // do programa sem perceber. const int PINO_LED = 2; const int RESISTOR_OHMS = 330; const float TENSAO_ALIMENTACAO = 3.3; // Sufixo `L` no final do número diz: "isto é long". Sem o sufixo, `500` é int. const long INTERVALO_MS = 500; // Sufixo `U` diz: "isto é unsigned", ou seja, não aceita número negativo. // `TABELA_CORES` é o lugar onde o tipo faz serviço: cada elemento ocupa // exatamente 4 bytes, então dá para perguntar ao sizeof quantos há. const unsigned int TABELA_CORES[] = {0, 32, 64, 96, 128, 160, 192, 224, 255}; // ---- Variáveis que mudam de valor enquanto o programa roda ---- int voltas = 0; float tensaoLida = 0.0; bool ledLigado = false; char inicial = 'E'; void setup() { // OUTPUT antes de qualquer digitalWrite. Sem isso o LED não acende e não // dá erro — é o defeito número um do curso inteiro. pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 2, aula 1: variável com tipo ==="); Serial.println(); // sizeof devolve quantos BYTES cada tipo ocupa na memória do ESP32. // É a pergunta que responde "por que importa o tipo": tipos diferentes // ocupam espaço diferente. Serial.println("Tamanho de cada tipo, em bytes:"); Serial.print(" bool "); Serial.println(sizeof(bool)); Serial.print(" char "); Serial.println(sizeof(char)); Serial.print(" int "); Serial.println(sizeof(int)); Serial.print(" long "); Serial.println(sizeof(long)); Serial.print(" float "); Serial.println(sizeof(float)); Serial.print(" unsigned int "); Serial.println(sizeof(unsigned int)); Serial.println(); // int tem 32 bits: vai de -2147483648 até 2147483647. Este é o limite. const int MAIOR_INT = 2147483647; Serial.print("Maior int: "); Serial.println(MAIOR_INT); Serial.println(); // A soma abaixo ESTOURA o int. Não é erro de compilação e não trava a // placa: o número simplesmente dá a volta e sai pelo valor mais negativo. // Este é o defeito que o tipo declarado evita em JavaScript, onde o número // cresce e ninguém percebe. Serial.print("Maior int + 1 = "); Serial.println(MAIOR_INT + 1); Serial.println(" (estourou: o int não cabe mais e deu a volta)"); Serial.println(); // sizeof de um vetor devolve o tamanho INTEIRO do vetor, não a quantidade de // elementos. Dividir pelo sizeof de um elemento dá a contagem. Serial.print("Elementos na tabela de cores: "); Serial.println(sizeof(TABELA_CORES) / sizeof(TABELA_CORES[0])); Serial.println(); Serial.print("Resistor do LED: "); Serial.print(RESISTOR_OHMS); Serial.print(" ohms, alimentado por "); Serial.print(TENSAO_ALIMENTACAO); Serial.println(" V"); Serial.print("Inicial do projeto: "); Serial.println(inicial); } void loop() { voltas++; // O tipo do PINO_LED é int, e HIGH é uma constante do núcleo. O compilador // aceita os dois juntos porque os dois cabem em int. digitalWrite(PINO_LED, voltas % 2 == 0 ? HIGH : LOW); Serial.print("volta "); Serial.print(voltas); Serial.print(" | intervalo "); Serial.print(INTERVALO_MS); Serial.println(" ms"); // delay é aceitável aqui: esta aula ainda não tem nada concorrente para // responder. O dia 3, aula 2 mostra o que acontece quando aparece. delay(INTERVALO_MS); }
Por que assim e não de outro jeito. Os seis sizeof estão no setup, e não no loop: o tamanho de um tipo não muda enquanto o programa roda, e repetir a conta a cada volta só polui o serial com um número que a turma já leu.
O MAIOR_INT + 1 está ali de propósito. O professor deixa o monitor rodando e pergunta o que o aluno acha que vai aparecer. A resposta é -2147483648, e a discussão que vem é a mais importante da aula: em C++ o limite é conhecido na hora de compilar, e é por isso que int dura mais que o número do JavaScript.
A tabela de cores é declarada com sizeof em vez de uma constante com o tamanho. Hard-code o 9 e, quando alguém acrescentar um nível, o programa passa a fazer conta com o número velho sem reclamar — exatamente o defeito que o sizeof evita.
Criterios de correcao
| Critério | Pontos |
|---|---|
Pino, resistor e intervalo declarados com const e tipo, no topo do arquivo | 3 pontos |
pinMode(..., OUTPUT) no setup antes de qualquer digitalWrite | 2 pontos |
| Os seis tamanhos de tipo impressos e anotados na ordem da tabela | 3 pontos |
Estouro do int impresso e explicado sem chamar de "erro" | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Declarar o pino dentro do loop | const int pino = 2; aparece entre as linhas do pisca-pisca | "Constante de peca vai no topo do arquivo, antes do setup. No loop não tem constante nenhuma." |
Achar que const int x = 5; impede x de mudar | Aluno escreve const e depois tenta x = 6, leva erro de compilacao | "const proibe a mudanca na variável. Se precisa mudar, declara sem const e ai você olha para o código e ve a mudanca." |
Citar PI como nome de constante | Aluno escreve const float PI = 3.14159; e a gravação falha | "O núcleo do ESP32 já tem um PI definido, e os dois se atropelam. Use VALOR_PI." |
Escrever int 5 = valor | "Por que o compilador não aceitou o 5 no lugar do nome" | "A ordem e tipo, depois nome. int valor, nunca int 5." |
Acreditar que o estouro do int avisa o professor | Item 4 respondido como "deu erro" | "Não deu erro nenhum. O número deu a volta e saiu negativo. E exatamente por isso que o limite se verifica antes." |
Usar int para o tempo todo | int agora = millis(); e a conta falha depois de alguns dias rodando | "O millis devolve unsigned long. Se você guardar em int, o contador estoura em cerca de 24 dias de programa ligado." |
Desafio extra
Declare uma constante para cada resistor que existe no seu kit e escreva uma função que imprima, para cada uma, qual é a corrente que passa pelo LED com sinal de 3,3 V. Depois explique no caderno por que a corrente cai quando o resistor sobe de 220 Ω para 1 kΩ, e qual dos dois valores você usaria se o LED estivesse esquentando.
>A resolucao, compilada
// Aula 1 do dia 2: declarar variável com tipo. // // O aluno chega do JavaScript, onde `let x = 5` já basta e todo número é do // mesmo tipo. Aqui o tipo vem ANTES do nome. Isso não é burocracia: é o que // impede a placa de fazer conta com o número errado sem avisar. #include <Arduino.h> // ---- Constantes do kit: tipo, nome e valor, nessa ordem ---- // `const` declara que o valor não muda depois. O tipo antes do nome é a // novidade do dia, e `const` é o que impede alguém de trocar o pino no meio // do programa sem perceber. const int PINO_LED = 2; const int RESISTOR_OHMS = 330; const float TENSAO_ALIMENTACAO = 3.3; // Sufixo `L` no final do número diz: "isto é long". Sem o sufixo, `500` é int. const long INTERVALO_MS = 500; // Sufixo `U` diz: "isto é unsigned", ou seja, não aceita número negativo. // `TABELA_CORES` é o lugar onde o tipo faz serviço: cada elemento ocupa // exatamente 4 bytes, então dá para perguntar ao sizeof quantos há. const unsigned int TABELA_CORES[] = {0, 32, 64, 96, 128, 160, 192, 224, 255}; // ---- Variáveis que mudam de valor enquanto o programa roda ---- int voltas = 0; float tensaoLida = 0.0; bool ledLigado = false; char inicial = 'E'; void setup() { // OUTPUT antes de qualquer digitalWrite. Sem isso o LED não acende e não // dá erro — é o defeito número um do curso inteiro. pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 2, aula 1: variável com tipo ==="); Serial.println(); // sizeof devolve quantos BYTES cada tipo ocupa na memória do ESP32. // É a pergunta que responde "por que importa o tipo": tipos diferentes // ocupam espaço diferente. Serial.println("Tamanho de cada tipo, em bytes:"); Serial.print(" bool "); Serial.println(sizeof(bool)); Serial.print(" char "); Serial.println(sizeof(char)); Serial.print(" int "); Serial.println(sizeof(int)); Serial.print(" long "); Serial.println(sizeof(long)); Serial.print(" float "); Serial.println(sizeof(float)); Serial.print(" unsigned int "); Serial.println(sizeof(unsigned int)); Serial.println(); // int tem 32 bits: vai de -2147483648 até 2147483647. Este é o limite. const int MAIOR_INT = 2147483647; Serial.print("Maior int: "); Serial.println(MAIOR_INT); Serial.println(); // A soma abaixo ESTOURA o int. Não é erro de compilação e não trava a // placa: o número simplesmente dá a volta e sai pelo valor mais negativo. // Este é o defeito que o tipo declarado evita em JavaScript, onde o número // cresce e ninguém percebe. Serial.print("Maior int + 1 = "); Serial.println(MAIOR_INT + 1); Serial.println(" (estourou: o int não cabe mais e deu a volta)"); Serial.println(); // sizeof de um vetor devolve o tamanho INTEIRO do vetor, não a quantidade de // elementos. Dividir pelo sizeof de um elemento dá a contagem. Serial.print("Elementos na tabela de cores: "); Serial.println(sizeof(TABELA_CORES) / sizeof(TABELA_CORES[0])); Serial.println(); Serial.print("Resistor do LED: "); Serial.print(RESISTOR_OHMS); Serial.print(" ohms, alimentado por "); Serial.print(TENSAO_ALIMENTACAO); Serial.println(" V"); Serial.print("Inicial do projeto: "); Serial.println(inicial); } void loop() { voltas++; // O tipo do PINO_LED é int, e HIGH é uma constante do núcleo. O compilador // aceita os dois juntos porque os dois cabem em int. digitalWrite(PINO_LED, voltas % 2 == 0 ? HIGH : LOW); Serial.print("volta "); Serial.print(voltas); Serial.print(" | intervalo "); Serial.print(INTERVALO_MS); Serial.println(" ms"); // delay é aceitável aqui: esta aula ainda não tem nada concorrente para // responder. O dia 3, aula 2 mostra o que acontece quando aparece. delay(INTERVALO_MS); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia02 aula1.
Aula 2 — Operacoes, comparacao e a armadilha da divisão
Objetivos
- Prever o resultado de uma divisão entre inteiros e de uma divisão com casa decimal.
- Usar o operador de resto para decidir uma ação sem contador paralelo.
- Diferenciar o sinal de igual simples do comparador de igualdade e dizer o que o compilador faz com o erro.
- Combinar comparadores com
&&,||e!para montar uma condição que a turma consiga ler em voz alta.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
- 2 a 5 LEDs de 3,3 V e 2 a 5 resistores de 330 Ω por aluno
- 1 módulo de alimentação MB102 por dupla
- Folha com as duas colunas de divisão inteira e de divisão real impressa, uma por dupla
Conceitos
A armadilha da divisão
Em C++, dois inteiros divididos dão um inteiro, e o resto é descartado sem aviso:
5 / 2 // 2, e o 0,5 foi embora 5 % 2 // 1, o resto 5 / 2.0 // 2.5, porque o 2.0 é float
O jeito mais curto de entender: um dos dois lados precisa ser float. Não precisa ser o primeiro nem o segundo, e não precisa ser o resultado — leitura * 3.3 / 4095 funciona porque o 3.3 já é float.
Onde isso vira defeito de verdade é na conversão de sensor. 2048 * 3 / 4095 dá 1 volt; o valor certo é 1,65 volt. Com o potenciômetro no meio da escala, o programa lê metade da tensão que existe ali, e o número errado sai no monitor com cara de número certo.
Igual simples e igualdade
tensao = 1.65; // atribui: o 1.65 vai para a variavel tensao == 1.65; // pergunta: os dois sao iguais?
O sinal de igual sozinho não compila. O compilador entende que 1.65 é um valor, e uma função que devolve valor precisa de um nome que receba o valor — e a mensagem de erro mostra essa checagem. Esse erro é o mais barato que o aluno vai ter na semana, porque ele para o programa na hora.
Comparadores e operadores lógicos
| Operador | Nome | O que faz | ||
|---|---|---|---|---|
== != | igualdade e diferença | compara dois valores | ||
< <= > >= | ordem | compara dois valores | ||
&& | e | as duas condições precisam ser verdadeiras | ||
| `\ | \ | ` | ou | pelo menos uma das duas precisa ser verdadeira |
! | negação | inverte verdadeiro e falso |
O || tem um comportamento que o aluno vai perder se ninguém mostrar: quando o lado esquerdo já é verdadeiro, o lado direito nem é avaliado. Isso se chama curto-circuito, e é o que permite escrever contador > 0 && TABELA[contador - 1] != -1 sem o programa tentar ler uma posição que não existe.
Atribuição composta e incremento
voltas += 1 é voltas = voltas + 1. voltas++ soma um depois de usar o valor, e ++voltas soma antes. Entre dois valores isso importa; sozinho, em uma linha que só soma um, dá para ser preguiçoso e escrever do jeito mais curto.
Atividade
Montagem:
- Resistor de 330 Ω entre o
GPIO2e um lado do LED. - O outro lado do LED na trilha
+da protoboard. - Jumper macho-fêmea levando o
GPIO2ao resistor.
O lado longo do LED é o anodo e vai para o positivo.
- Grave a resolução e confirme que o LED acende a cada três voltas e que o serial imprime as duas formas de divisão.
- No monitor serial, anote o valor de
5 / 2e de5 / 2.0e explique em uma frase por que os dois são diferentes. - Corrija a conversão de tensão do sketch trocando o
3inteiro por3.3, grave de novo e anote a nova voltagem lida. - Escreva uma condição que seja verdadeira somente quando a tensão estiver entre 0 e 3,3 V, usando os três operadores lógicos. Diga qual deles é o do "e".
- Troque o
if (voltas % 3 == 0)por uma condição escrita com||, que acenda o LED na primeira e na terceira volta de cada grupo de cinco. Explique por que%não serve nesse caso. - Escreva, sem executar, uma linha que divida dois inteiros e não perca o resto. Diga qual é a palavra que faz isso acontecer.
Nota: 10 pontos. Critério de fim: o serial mostra a divisão inteira e a real lado a lado, e o LED acende exatamente uma vez a cada três voltas.
Resolucao
// Aula 2 do dia 2: operações, comparação e a armadilha da divisão. // // A divisão inteira é o defeito mais silencioso desta transição: o programa // compila, roda e devolve um número errado sem nunca reclamar. Este sketch // imprime as duas versões lado a lado para o professor mostrar a diferença. #include <Arduino.h> const int PINO_LED = 2; const float TENSAO_REFERENCIA = 3.3; const int LEITURA_MAXIMA_ADC = 4095; // ADC do ESP32 tem 12 bits: 2^12 = 4096 const int INTERVALO_MS = 800; // Dois números escolhidos para a divisão não fechar. Se dividissem certo, a // armadilha não apareceria na aula. const int LIDO_ADC = 2048; const int TENS_INTEIROS = 5; int voltas = 0; void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 2, aula 2: operações e divisão ==="); Serial.println(); Serial.println("Divisão inteira: o resto é descartado, sem aviso."); Serial.print(" 5 / 2 = "); Serial.println(TENS_INTEIROS / 2); Serial.print(" 5 % 2 = "); Serial.println(TENS_INTEIROS % 2); Serial.print(" 5 / 2.0 = "); Serial.println(TENS_INTEIROS / 2.0); Serial.println(" (o mesmo 5, com o mesmo 2, e três respostas diferentes)"); Serial.println(); // A divisão inteira SOME o resto ANTES de multiplicar. Numa conversão de // sensor, isso vira erro de quase 1 V: 2048 * 3.3 / 4095 dá 1 volt, e o // valor certo é 1.65. int voltsInteiros = LIDO_ADC * 3 / LEITURA_MAXIMA_ADC; float voltsReais = LIDO_ADC * 3.3 / LEITURA_MAXIMA_ADC; Serial.println("Conversão de leitura do ADC:"); Serial.print(" modo inteiro (cru * 3 / 4095) = "); Serial.println(voltsInteiros); Serial.print(" modo correto (cru * 3.3 / 4095) = "); Serial.println(voltsReais); Serial.println(); // Comparação devolve true ou false, que em C++ valem 1 e 0. `=` atribui, // `==` compara. Confundir os dois é o erro de digitação mais caro. Serial.println("Comparação:"); Serial.print(" 7 == 7 -> "); Serial.println(7 == 7); Serial.print(" 7 = 7 -> "); Serial.println(" (o sinal de igual sozinho nem compila: ele pede um nome)"); Serial.println(); // Operadores lógicos. && é "e", || é "ou", ! é "nega". bool acimaDoLimite = (voltsReais > 1.6); bool dentroDaFaixa = (voltsReais >= 0.0 && voltsReais <= TENSAO_REFERENCIA); Serial.print("acima de 1.6 V? "); Serial.println(acimaDoLimite); Serial.print("dentro de 0 a 3.3 V? "); Serial.println(dentroDaFaixa); } void loop() { voltas++; // O operador % devolve o resto. Ele serve para decidir a ação sem precisar // de um contador separado: uma ação a cada 3 voltas. if (voltas % 3 == 0) { digitalWrite(PINO_LED, HIGH); } else { digitalWrite(PINO_LED, LOW); } Serial.print("volta "); Serial.print(voltas); Serial.print(" | resto por 3 = "); Serial.println(voltas % 3); delay(INTERVALO_MS); }
Por que assim e não de outro jeito. As duas divisões estão impressas no setup, lado a lado e com o resto embaixo, porque o ponto da aula é a comparação direta: o mesmo 5 e o mesmo 2, e a única diferença é um .0 no segundo.
A conversão de tensão aparece nas duas formas de propósito. O valor de 2048 não é real de um sensor: é um número para a conta fechar errado na frente da turma. O aluno vê 1 volt e 1,65 volt na mesma tela e a conclusão fica sem precisar de discurso.
O if do loop usa % 3 em vez de um contador paralelo. Com um contador paralelo seriam duas variáveis que precisam concordar; com o resto, a decisão sai da própria contagem e não existe estado para ficar inconsistente.
O Serial.println(" (o sinal de igual sozinho nem compila: ele pede um nome)") no setup é texto de aluno: o compilador não aceitaria aquela linha como código. Ela existe para o professor apontar que o aluno vai ver esse erro hoje, e reconhecer o texto quando aparecer.
Criterios de correcao
| Critério | Pontos |
|---|---|
| Sketch gravado com o LED acendendo a cada três voltas | 2 pontos |
| Divisão inteira, resto e divisão real impressos e comentados | 3 pontos |
Conversão corrigida para 3.3 e nova voltagem anotada | 3 pontos |
| Item 4 com os três operadores lógicos e o nome do "e" | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao | ||||
|---|---|---|---|---|---|---|
| Perder o resto sem perceber | Item 6 respondido com 5 / 2 | "Para não perder o resto se usa o operador módulo, o sinal de porcento. 5 % 2 devolve 1." | ||||
| Dividir dois inteiros esperando decimal | leitura * 3 / 4095 e o professor lê 1 volt | "Um dos lados tem que ser float. Ponha o 3.3: e assim que o número real entra na conta." | ||||
| Comparar com um igual so | Aluno escreveu tensao = 1.65; dentro do if | "Você não perguntou nada, você ordenou. Troque por dois iguais." | ||||
Usar & em vez de && | Condição que nunca entra, sem erro | "Com um & so, o C++ compara os dois lados como número. E o operador de e lógica tem dois sinais: &&." | ||||
| Chamar ` | de "ou" e escrever or` | Sketch não compila | "Em C++ não existe palavra or. O operador e `\ | \ | `, dois sinais de barra." | |
Confundir >= com => | Erro de compilacao apontando para a seta | ">= e maior ou igual. => não existe em C++." | ||||
Escolher float para contar voltas | Contador que para em 16777215 | "Contador e int. float guarda número com casas decimais e o espaco vai embora com a precisao." |
Desafio extra
O operador % serve para decidir ação por ciclo. Escreva uma condição que acenda o LED quando o número da volta for par ou múltiplo de 5, usando os dois operadores juntos, e depois explique por que o número 10 acenderia nessa regra e o número 4 não. Em seguida, faça o mesmo com um contador de 1 a 20 impresso no serial, marcando na tela os números que acenderiam.
A resolucao, compilada
// Aula 2 do dia 2: operações, comparação e a armadilha da divisão. // // A divisão inteira é o defeito mais silencioso desta transição: o programa // compila, roda e devolve um número errado sem nunca reclamar. Este sketch // imprime as duas versões lado a lado para o professor mostrar a diferença. #include <Arduino.h> const int PINO_LED = 2; const float TENSAO_REFERENCIA = 3.3; const int LEITURA_MAXIMA_ADC = 4095; // ADC do ESP32 tem 12 bits: 2^12 = 4096 const int INTERVALO_MS = 800; // Dois números escolhidos para a divisão não fechar. Se dividissem certo, a // armadilha não apareceria na aula. const int LIDO_ADC = 2048; const int TENS_INTEIROS = 5; int voltas = 0; void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 2, aula 2: operações e divisão ==="); Serial.println(); Serial.println("Divisão inteira: o resto é descartado, sem aviso."); Serial.print(" 5 / 2 = "); Serial.println(TENS_INTEIROS / 2); Serial.print(" 5 % 2 = "); Serial.println(TENS_INTEIROS % 2); Serial.print(" 5 / 2.0 = "); Serial.println(TENS_INTEIROS / 2.0); Serial.println(" (o mesmo 5, com o mesmo 2, e três respostas diferentes)"); Serial.println(); // A divisão inteira SOME o resto ANTES de multiplicar. Numa conversão de // sensor, isso vira erro de quase 1 V: 2048 * 3.3 / 4095 dá 1 volt, e o // valor certo é 1.65. int voltsInteiros = LIDO_ADC * 3 / LEITURA_MAXIMA_ADC; float voltsReais = LIDO_ADC * 3.3 / LEITURA_MAXIMA_ADC; Serial.println("Conversão de leitura do ADC:"); Serial.print(" modo inteiro (cru * 3 / 4095) = "); Serial.println(voltsInteiros); Serial.print(" modo correto (cru * 3.3 / 4095) = "); Serial.println(voltsReais); Serial.println(); // Comparação devolve true ou false, que em C++ valem 1 e 0. `=` atribui, // `==` compara. Confundir os dois é o erro de digitação mais caro. Serial.println("Comparação:"); Serial.print(" 7 == 7 -> "); Serial.println(7 == 7); Serial.print(" 7 = 7 -> "); Serial.println(" (o sinal de igual sozinho nem compila: ele pede um nome)"); Serial.println(); // Operadores lógicos. && é "e", || é "ou", ! é "nega". bool acimaDoLimite = (voltsReais > 1.6); bool dentroDaFaixa = (voltsReais >= 0.0 && voltsReais <= TENSAO_REFERENCIA); Serial.print("acima de 1.6 V? "); Serial.println(acimaDoLimite); Serial.print("dentro de 0 a 3.3 V? "); Serial.println(dentroDaFaixa); } void loop() { voltas++; // O operador % devolve o resto. Ele serve para decidir a ação sem precisar // de um contador separado: uma ação a cada 3 voltas. if (voltas % 3 == 0) { digitalWrite(PINO_LED, HIGH); } else { digitalWrite(PINO_LED, LOW); } Serial.print("volta "); Serial.print(voltas); Serial.print(" | resto por 3 = "); Serial.println(voltas % 3); delay(INTERVALO_MS); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia02 aula2.
