Funções e arranjo de bancada — Arduino e IoT — semana 4 do 1o trimestre
Semana 4 de 13· 1o trimestre · 01/02 a 30/04
Funções e arranjo de bancada
Parametros, retorno e o arranjo: por que subir um arranjo de protoboard e a primeira coisa a aprender.
Aula 1 — Declarar, chamar e passar parametro
Objetivos
- Declarar uma função com tipo de retorno, nome e parâmetros, e chamá-la pelo nome.
- Escolher entre função que devolve valor e função
voidconforme o que oloopprecisa fazer com o resultado. - Explicar passagem por valor: a função trabalha com uma cópia e a variável de fora não muda.
- Resolver a ordem de declaração com protótipo de função, que é o que o JavaScript não exige.
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 formas de declarar função impressa, uma por dupla
Conceitos
A assinatura, na ordem certa
float voltsDoAdc(int leituraCrua) { return leituraCrua * 3.3 / 4095.0; }
Quatro partes, nesta ordem: float é o tipo que sai, voltsDoAdc é o nome, (int leituraCrua) é a lista do que a função precisa. E a assinatura inteira vem com o ponto e vírgula quando é só declaração.
O nome da função é um verbo com o que ela faz: voltsDoAdc devolve volts, piscar pisca. fazerAlgo não diz nada e é o primeiro sinal de que a função está mal pensada.
void é uma resposta
void no lugar do tipo de retorno significa "esta função não devolve nada". Ela faz o serviço e pronto:
void piscar(int pino, int tempo) { digitalWrite(pino, HIGH); delay(tempo); digitalWrite(pino, LOW); delay(tempo); }
A chamada é piscar(2, 200) e não x = piscar(2, 200), porque não há x para receber. Quando o aluno escreve x = piscar(...), o erro de compilação diz que não se pode converter void em int — e essa mensagem ensina exatamente a diferença.
Parâmetro é cópia
Quando você chama voltsDoAdc(2048), a função não enxerga o seu 2048. Ela recebe uma cópia num lugar da memória que só pertence a ela. Se a função mexer nesse número, o número de fora continua igual.
Para mexer de verdade na variável do chamador é preciso passar o endereço, e é o assunto da aula 2 do dia de hoje.
O protótipo e a ordem das linhas
Em JavaScript, chamar uma função antes de ela aparecer no arquivo funciona. Em C++ não: o compilador lê o arquivo de cima para baixo, e na linha da chamada ele ainda não sabe que a função existe.
A solução é o protótipo — a assinatura sozinha, terminada em ponto e vírgula, antes do setup:
void mostrarRegra(int passo, float valor); // promessa
Depois do loop, a definição completa vem sem ponto e vírgula. O protótipo é uma promessa feita ao compilador: "essa função existe, e é assim". Manter protótipos de todas as funções no topo é regra do curso, e o dia 13, na revisão final, cobra isso.
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 serial mostra as três linhas da tabela
passo | tensaoe que o LED pisca. - Escreva a assinatura de uma função que receba dois inteiros e devolva o maior deles, sem executar. Diga o que você colocou em cada uma das quatro partes.
- Escreva a função do item 2 de verdade, com o corpo, e chame-a com os números
7e12. O número que aparece no serial é o esperado? - Escreva uma função
voidque receba um pino e escrevaHIGHnele, e outra que receba o mesmo pino e escrevaLOW. Chame as duas em sequência e confirme que o LED acende e apaga. - Mova a definição de
voltsDoAdcpara depois doloop, sem escrever o protótipo, e grave. Leia a mensagem de erro do compilador e escreva no caderno o que ela está dizendo. - Escreva no caderno o que aconteceria com a sua variável de fora se, dentro da função
voltsDoAdc, você fizesseleituraCrua = 0antes de calcular. Depois teste isso no código e veja se a linha da tabela muda.
Nota: 10 pontos. Critério de fim: a função do item 3 devolve o número certo e a do item 4 acende e apaga o LED.
Resolucao
// Aula 1 do dia 4: declarar, chamar e passar parâmetro. // // Função é a peça que o aluno precisa para o projeto final: o código do // projeto tem projeto de estação meteorológica e uma biblioteca própria, // e as duas coisas se apoiam em funções com parâmetro e retorno. #include <Arduino.h> const int PINO_LED = 2; const int TOTAL_DE_PASSOS = 3; // Protótipo: avisa ao compilador que a função existe antes de ela aparecer // no arquivo. Sem isso, chamar `acender` antes do próprio código de `acender` // dá erro de compilação. É a diferença entre JavaScript e C++. void mostrarRegra(int passo, float valor); float voltsDoAdc(int leituraCrua); void piscar(int pino, int tempo); void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 4, aula 1: função, parâmetro e retorno ==="); Serial.println(); Serial.println("mes | tensao"); Serial.println("----+-------"); // Um laço, uma chamada. O valor muda a cada volta, a função não muda. for (int passo = 1; passo <= TOTAL_DE_PASSOS; passo++) { int leitura = passo * 1365; // 4095 dividido em 3 partes mostrarRegra(passo, voltsDoAdc(leitura)); } Serial.println(); Serial.println("Função sem retorno, só com efeito:"); piscar(2, 150); } void mostrarRegra(int passo, float valor) { Serial.print(passo); Serial.print(" | "); Serial.print(valor); Serial.println(" V"); } // Parâmetro é cópia: os 3.3 volts desta função não têm nada a ver com a // constante global do topo. Mudar `valor` aqui dentro não muda em lugar // nenhum do programa. É a passagem por valor. float voltsDoAdc(int leituraCrua) { return leituraCrua * 3.3 / 4095.0; } // `void` no lugar do tipo de retorno significa: não devolve nada. A função // faz o serviço e pronto. É o caso do piscar. void piscar(int pino, int tempo) { digitalWrite(pino, HIGH); delay(tempo); digitalWrite(pino, LOW); delay(tempo); } // A função que devolve valor entra na conta e o resultado é usado; a que não // devolve nada é chamada sozinha, e o compilador deixa claro que ali não tem // resultado nenhum para esperar. As duas no mesmo `loop`, para a turma ver as // duas formas lado a lado. void loop() { piscar(PINO_LED, 200); Serial.print("tensao do passo 1: "); Serial.println(voltsDoAdc(1365)); Serial.println("---"); delay(600); }
Por que assim e não de outro jeito. O setup chama a mesma função três vezes com números diferentes. Se a tabela estivesse escrita com três chamadas de Serial.print repetidas, o aluno aprenderia que imprimir uma tabela é copiar e colar — e no dia 13, com o display OLED, a tabela tem quatro linhas e cinco colunas.
A função voltsDoAdc recebe int e devolve float. Esse desenho é o que o projeto final exige: o analogRead devolve inteiro, a conta de volts não é inteira, e a função é o lugar onde a conversão mora. Um aluno que fizer a conversão espalhada em cinco linhas vai repetir o mesmo erro de divisão em todos os lugares.
Os protótipos estão no topo, antes do setup, e as definições depois do loop. Essa ordem não é estética: é a que permite o setup chamar mostrarRegra sem o compilador reclamar.
O item 6 da atividade é o motivo de a passagem por valor estar aqui e não na aula 2 do dia. A função não consegue mexer na sua variável — e o aluno descobre isso testando, não ouvindo.
Criterios de correcao
| Critério | Pontos |
|---|---|
Protótipos de todas as funções antes do setup | 2 pontos |
| Função do item 3 escrita, executada e devolvendo o maior número | 3 pontos |
Funções void do item 4 acendendo e apagando o LED | 3 pontos |
| Item 5 com a mensagem de erro lida e explicada | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
| Chamar a função antes de declarar | Erro de compilacao dizendo que a função não foi declarada | "Em C++ o compilador le de cima para baixo. Escreva o prototipo antes do setup." |
x = piscar(...) em função void | "cannot convert void to int" | "A função não devolve nada, então não tem o que atribuir. Chame ela sozinha." |
| Passar o valor errado na chamada | Função devolve número estranho sem reclamar | "Cheque a ordem dos parametros: primeiro o pino, depois o tempo. A função não sabe a ordem." |
| Declarar o parametro com o valor | void piscar(int pino = 2) | "Parametro e um nome que a função preenche quando chamada. O valor vem na chamada." |
| Achar que a função altera a variável de fora | Item 6 com "o número fica", e o código mostra que não | "Você passou uma copia. Para mexer na sua variável, precisa passar o endereco — isso e a aula 2 de hoje." |
Usar pino como nome global | Variável global que o aluno acha que a função enxerga | "Variável dentro da função e local: ela so existe la dentro. Nome igual em outro lugar são duas coisas." |
| Nome de função sem verbo | int processar(int valor) com nome que não diz o que faz | "Nomeia com o que ela faz: voltsDoAdc, piscar, somarCiclo." |
Desafio extra
Escreva três funções que devolvam três coisas ao mesmo tempo: o maior número, o menor número e a média de uma lista de cinco inteiros — e chame as três sem repetir o laço. Se não for possível devolver três valores com um return só, descreva em duas linhas qual é o caminho e implemente-o com uma função que devolve um número por chamada. Diga, das duas formas, qual delas se parece mais com o que o JavaScript faz.
A resolucao, compilada
// Aula 1 do dia 4: declarar, chamar e passar parâmetro. // // Função é a peça que o aluno precisa para o projeto final: o código do // projeto tem projeto de estação meteorológica e uma biblioteca própria, // e as duas coisas se apoiam em funções com parâmetro e retorno. #include <Arduino.h> const int PINO_LED = 2; const int TOTAL_DE_PASSOS = 3; // Protótipo: avisa ao compilador que a função existe antes de ela aparecer // no arquivo. Sem isso, chamar `acender` antes do próprio código de `acender` // dá erro de compilação. É a diferença entre JavaScript e C++. void mostrarRegra(int passo, float valor); float voltsDoAdc(int leituraCrua); void piscar(int pino, int tempo); void setup() { pinMode(PINO_LED, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 4, aula 1: função, parâmetro e retorno ==="); Serial.println(); Serial.println("mes | tensao"); Serial.println("----+-------"); // Um laço, uma chamada. O valor muda a cada volta, a função não muda. for (int passo = 1; passo <= TOTAL_DE_PASSOS; passo++) { int leitura = passo * 1365; // 4095 dividido em 3 partes mostrarRegra(passo, voltsDoAdc(leitura)); } Serial.println(); Serial.println("Função sem retorno, só com efeito:"); piscar(2, 150); } void mostrarRegra(int passo, float valor) { Serial.print(passo); Serial.print(" | "); Serial.print(valor); Serial.println(" V"); } // Parâmetro é cópia: os 3.3 volts desta função não têm nada a ver com a // constante global do topo. Mudar `valor` aqui dentro não muda em lugar // nenhum do programa. É a passagem por valor. float voltsDoAdc(int leituraCrua) { return leituraCrua * 3.3 / 4095.0; } // `void` no lugar do tipo de retorno significa: não devolve nada. A função // faz o serviço e pronto. É o caso do piscar. void piscar(int pino, int tempo) { digitalWrite(pino, HIGH); delay(tempo); digitalWrite(pino, LOW); delay(tempo); } // A função que devolve valor entra na conta e o resultado é usado; a que não // devolve nada é chamada sozinha, e o compilador deixa claro que ali não tem // resultado nenhum para esperar. As duas no mesmo `loop`, para a turma ver as // duas formas lado a lado. void loop() { piscar(PINO_LED, 200); Serial.print("tensao do passo 1: "); Serial.println(voltsDoAdc(1365)); Serial.println("---"); delay(600); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia04 aula1.
Aula 2 — Funções com referência: as funções de arranjo do kit
Objetivos
- Passar uma variável por referência e ver a variável de fora mudar depois da chamada.
- Dizer por que passagem por valor não serve quando a função precisa acumular um contador.
- Refatorar um arranjo de bancada com dois LEDs usando uma única função, em vez de duas cópias.
- Reconhecer o caso em que a referência não é necessária, e deixar o
returnno lugar.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
- 4 LEDs de 3,3 V e 4 resistores de 330 Ω por aluno
- 1 módulo de alimentação MB102 por dupla
- Folha com os quatro efeitos de LED do arranjo impressa, uma por dupla
Conceitos
Passagem por valor: a cópia
Quando você chama somarCopia(total, 5), a função recebe uma cópia do número. Ela soma o que quiser na cópia; a sua variável não existe lá dentro.
Isso é bom na maioria dos casos: a função não bagunça o que você já tem. O problema é quando a função existe justamente para acumular — o contador do arranjo, o total de leituras, o número de eventos. Uma função que devolve o valor resolve; uma função que soma no acumulador resolve melhor quando ela precisa mexer em vários lugares de uma vez.
Passagem por referência: o endereço
void somarCiclo(int& contador, int quantidade) { contador += quantidade; }
O & grudado no tipo, int&, diz que o parâmetro é o endereço da variável, não uma cópia. O que a função somar aqui, soma ali.
O símbolo tem dois nomes, e o aluno precisa saber qual é qual: & na declaração do parâmetro é referência; & na chamada é o endereço de. Os dois se usam juntos:
somarCiclo(ciclos, 1); // chamada: passa o valor normal somarCiclo(&total, 2); // se a funcao pedir o endereco, e este sinal
O defeito silencioso da referência: se você esquecer o & na declaração do parâmetro, o programa compila, roda e não faz nada. A função soma uma cópia e devolve, e a sua variável fica parada. Esse é o mesmo tipo de bug do pinMode faltando, e vale o mesmo cuidado.
Quando a referência é o exagero
Nem toda função precisa de referência. Se a função só calcula e devolve um resultado, o return é mais curto e mais claro:
int maior(int a, int b) { return a > b ? a : b; }
A regra do curso: função que calcula usa return; função que acumula ou mexe em pinos usa referência ou pino como parâmetro. Deixar claro isso evita o código cheio de & onde ninguém precisa de &.
Refatorar é o trabalho de verdade
O arranjo da aula tem dois LEDs e quatro funções. A versão ingênua escreve oito linhas de digitalWrite e delay. A versão com função escreve quatro chamadas.
O ganho não é só o tamanho: quando a montagem do dia 7 tiver três LEDs, a função não muda. Sem a refatoração, o aluno copia o bloco, apaga um número e reza para não esquecer nada. Com a função, ele escreve uma linha nova. Esse é o motivo de o dia 4 existir antes de o primeiro sensor.
Atividade
Montagem:
- Resistor de 330 Ω entre o
GPIO2e um lado do LED A. - Resistor de 330 Ω entre o
GPIO4e um lado do LED B. - O lado livre de cada LED na trilha
+da protoboard. - Jumper macho-fêmea levando
GPIO2eGPIO4aos respectivos resistores.
Os lados longos dos dois LEDs são os ânodos e vão para o positivo.
- Grave a resolução e confirme que os dois LEDs piscam em tempos diferentes e que a coluna de acertos sobe.
- Prova de que passou por valor: escreva no caderno o valor de
totalLocaldepois da chamada desomarCopia. Ele mudou? Justifique. - Apague o
&do parâmetro desomarCiclo, grave e observe o serial. Escreva o que aconteceu com o contadorciclose explique por que o programa não deu erro. - Volte a colocar o
&, grave e confirme que o contador subiu. Registre os dois resultados lado a lado. - Escreva uma função
void inverter(int& led)que inverta o estado de um pino lendo o endereço, e chame-a para os dois pinos no seuloop. Confirme que cada LED inverte no seu ritmo. - Refatore o
looppara que o arranjo inteiro use apenas chamadas de função, sem nenhumdigitalWriteescrito diretamente. Diga quantas linhas deloopsobraram e quanto elas eram antes.
Nota: 10 pontos. Critério de fim: o arranjo roda, a prova do item 3 está no caderno e o loop do item 6 não tem digitalWrite direto.
Resolucao
// Aula 2 do dia 4: funções com referência, as funções de arranjo do kit. // // Passagem por valor copia o número; passagem por referência entrega o // endereço da variável. É a diferença entre uma função que "faz o serviço" // e uma função que "muda o seu estado" — e o arranjo de bancada é o caso // onde isso aparece o tempo todo: piscar dois LEDs com a mesma função. #include <Arduino.h> const int PIN_LED_A = 2; const int PIN_LED_B = 4; const int TEMPO_ACESO = 300; const int TEMPO_APAGADO = 150; const int TOTAL_DE_CICLOS = 4; int ciclos = 0; int totalDeAcertos = 0; // Passagem por REFERÊNCIA: o `int&` diz que o parâmetro é o próprio // endereço. O que a função mexer aqui, muda lá na variável do chamador. // Note que o tipo é `int&`, com o & grudado — não é `int &` nem `&int`. void somarCiclo(int& contador, int quantidade); void piscar(int pino, int tempo); void alternar(int pino); // Passagem por VALOR, para contraste: os 5 que chegam aqui são uma cópia. // A função pode somar à vontade que a variável de fora nem fica sabendo. int somarCopia(int valor, int quantidade) { return valor + quantidade; } void setup() { pinMode(PIN_LED_A, OUTPUT); pinMode(PIN_LED_B, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 4, aula 2: passagem por referência ==="); Serial.println(); Serial.println("ciclos | acertos"); int totalLocal = 0; int porValor = somarCopia(totalLocal, 5); Serial.print("por valor: o argumento era "); Serial.print(totalLocal); Serial.print(" e a funcao devolveu "); Serial.println(porValor); Serial.println(" (totalLocal continua 0: a funcao recebeu uma copia)"); Serial.println(); } void loop() { // A mesma função, chamada em dois pinos. Uma vez com o LED A, uma com o B. piscar(PIN_LED_A, TEMPO_ACESO); piscar(PIN_LED_B, TEMPO_APAGADO); alternar(PIN_LED_A); alternar(PIN_LED_B); // Por referência: `ciclos` muda de verdade depois da chamada. somarCiclo(ciclos, 1); Serial.print(ciclos); Serial.print(" | "); Serial.println(totalDeAcertos); // Referência também serve para devolver mais de um valor de uma vez. // Se usasse retorno, teria que devolver um arranjo ou inventar um tipo. int antes = totalDeAcertos; somarCiclo(totalDeAcertos, 2); Serial.print("somarCiclo mudou a variavel de fora: "); Serial.print(antes); Serial.print(" -> "); Serial.println(totalDeAcertos); delay(TEMPO_APAGADO * 2); } // `int& contador` — sem o &, esta função devolveria uma cópia alterada, e o // `ciclos` do `loop` ficaria parado no 1 para sempre. É o defeito silencioso // da referência: o código compila, roda, e só não faz nada. void somarCiclo(int& contador, int quantidade) { contador += quantidade; } void piscar(int pino, int tempo) { digitalWrite(pino, HIGH); delay(tempo); digitalWrite(pino, LOW); delay(tempo); } // `bool` no lugar de `void` seria o jeito de devolver o estado novo. Aqui // não precisa: `alternar` só precisa inverter. O `loop` é que não pode ler // o pino logo depois, porque precisa do valor novo — e por isso a chamada // para o LED B vem logo em seguida. void alternar(int pino) { digitalWrite(pino, digitalRead(pino) == HIGH ? LOW : HIGH); }
Por que assim e não de outro jeito. O sketch tem as duas funções lado a lado de propósito: somarCopia por valor e somarCiclo por referência, com o mesmo nome e quase o mesmo corpo. O aluno vê a única diferença — o & — e o efeito muda completamente. Se fossem funções separadas, com nomes separados, a comparação não existiria.
A prova da passagem por valor está no setup: totalLocal vale 0, somarCopia devolve 5, e o serial mostra as duas coisas em linhas separadas. Sem essa prova escrita, metade da turma sai da aula achando que a função mexeu na variável.
O alternar não usa referência, e isso é proposital. Ele recebe o valor do pino e escreve nele — é o pino que importa, não o estado de uma variável do loop. A aula mostra que referência não é um enfeite para colocar em toda função: quando o que você quer mudar é um pino, o parâmetro comum resolve e o código fica mais curto.
O delay(TEMPO_APAGADO * 2) no fim do loop é a única espera do arquivo. Ele existe para o arranjo aparecer devagar na tela do professor; a lógica do padrão de LEDs não depende dele e o aluno pode removê-lo sem quebrar a sequência.
Criterios de correcao
| Critério | Pontos |
|---|---|
int& declarado no parâmetro e o contador mudando de verdade | 3 pontos |
Prova escrita do item 3: o que acontece sem o & | 3 pontos |
Função inverter escrita e os dois LEDs invertendo | 2 pontos |
loop do item 6 sem digitalWrite direto | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Esquecer o & na declaração do parametro | Contador parado, programa compila e roda | "Você passou uma copia de novo. Com o & no tipo, eu recebo o endereco da sua variável." |
Usar & na chamada e não na declaracao | Erro de compilacao sobre referência invalida | "O & vai na declaracao do parametro, grudado no tipo: int& contador. Na chamada passa o valor normal." |
| Achar que referência copia o valor | Aluno escreve int& n = outroNumero; esperando copia | "Referência e o endereco. A variável ligada nela e a mesma, não uma copia." |
| Passar número solto onde a função pede variável | Erro de compilacao de tipo | "A função pede um endereco de int. So uma variável int serve." |
| Fazer referência em função que so calcula | Código cheio de & sem ganho nenhum | "Se a função so calcula, devolve com return. Referência e para acumular ou mexer em pinos." |
Colocar o & longe do tipo | int &contador com o & solto da declaracao | "Grude o sinal no tipo: int& contador. E o que o compilador le." |
| Refatorar e apagar a função antiga | Sketch so funciona se a ordem das chamadas for exata | "A função nova precisa estar definida e prototypada. Se apagou o prototipo, o compilador reclama na chamada." |
Desafio extra
Escreva uma função void registrarLeitura(int& total, int& maior, int& menor, int valor) que receba quatro referências e atualize as três de uma vez: some o valor no total, e guarde o maior e o menor. Chame-a em um laço de dez leituras de analogRead e imprima no serial as três estatísticas ao final. Depois troque o laço por um for de 1 a 100 e explique no caderno por que a função continua funcionando sem nenhuma alteração.
A resolucao, compilada
// Aula 2 do dia 4: funções com referência, as funções de arranjo do kit. // // Passagem por valor copia o número; passagem por referência entrega o // endereço da variável. É a diferença entre uma função que "faz o serviço" // e uma função que "muda o seu estado" — e o arranjo de bancada é o caso // onde isso aparece o tempo todo: piscar dois LEDs com a mesma função. #include <Arduino.h> const int PIN_LED_A = 2; const int PIN_LED_B = 4; const int TEMPO_ACESO = 300; const int TEMPO_APAGADO = 150; const int TOTAL_DE_CICLOS = 4; int ciclos = 0; int totalDeAcertos = 0; // Passagem por REFERÊNCIA: o `int&` diz que o parâmetro é o próprio // endereço. O que a função mexer aqui, muda lá na variável do chamador. // Note que o tipo é `int&`, com o & grudado — não é `int &` nem `&int`. void somarCiclo(int& contador, int quantidade); void piscar(int pino, int tempo); void alternar(int pino); // Passagem por VALOR, para contraste: os 5 que chegam aqui são uma cópia. // A função pode somar à vontade que a variável de fora nem fica sabendo. int somarCopia(int valor, int quantidade) { return valor + quantidade; } void setup() { pinMode(PIN_LED_A, OUTPUT); pinMode(PIN_LED_B, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("=== dia 4, aula 2: passagem por referência ==="); Serial.println(); Serial.println("ciclos | acertos"); int totalLocal = 0; int porValor = somarCopia(totalLocal, 5); Serial.print("por valor: o argumento era "); Serial.print(totalLocal); Serial.print(" e a funcao devolveu "); Serial.println(porValor); Serial.println(" (totalLocal continua 0: a funcao recebeu uma copia)"); Serial.println(); } void loop() { // A mesma função, chamada em dois pinos. Uma vez com o LED A, uma com o B. piscar(PIN_LED_A, TEMPO_ACESO); piscar(PIN_LED_B, TEMPO_APAGADO); alternar(PIN_LED_A); alternar(PIN_LED_B); // Por referência: `ciclos` muda de verdade depois da chamada. somarCiclo(ciclos, 1); Serial.print(ciclos); Serial.print(" | "); Serial.println(totalDeAcertos); // Referência também serve para devolver mais de um valor de uma vez. // Se usasse retorno, teria que devolver um arranjo ou inventar um tipo. int antes = totalDeAcertos; somarCiclo(totalDeAcertos, 2); Serial.print("somarCiclo mudou a variavel de fora: "); Serial.print(antes); Serial.print(" -> "); Serial.println(totalDeAcertos); delay(TEMPO_APAGADO * 2); } // `int& contador` — sem o &, esta função devolveria uma cópia alterada, e o // `ciclos` do `loop` ficaria parado no 1 para sempre. É o defeito silencioso // da referência: o código compila, roda, e só não faz nada. void somarCiclo(int& contador, int quantidade) { contador += quantidade; } void piscar(int pino, int tempo) { digitalWrite(pino, HIGH); delay(tempo); digitalWrite(pino, LOW); delay(tempo); } // `bool` no lugar de `void` seria o jeito de devolver o estado novo. Aqui // não precisa: `alternar` só precisa inverter. O `loop` é que não pode ler // o pino logo depois, porque precisa do valor novo — e por isso a chamada // para o LED B vem logo em seguida. void alternar(int pino) { digitalWrite(pino, digitalRead(pino) == HIGH ? LOW : HIGH); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia04 aula2.
