O caminho do dado — Arduino e IoT — semana 1 do 2o trimestre

Informatica · Conteudo · publicado em 02/10/2026
Semana 1 · O caminho do dado — Material de Apoio Arduino

Semana 1 de 16· 2o trimestre · 01/05 a 04/09

O caminho do dado

A arquitetura do trimestre e o primeiro programa no terminal.

Aula 1 — A arquitetura do trimestre: sensor, placa, servidor e banco

Objetivos

  • Desenhar o caminho completo do dado do trimestre, da leitura do sensor até a linha no banco, nomeando as seis pecas.
  • Dizer em qual peca de cada etapa o dado vira número, e em qual peca ele vira texto para viajar.
  • Explicar por que o dado nasce no sensor e não no servidor, e o que isso muda em relacao ao curso de JavaScript.
  • Apontar, no caminho, o ponto onde o dado pode se perder, e dizer o que o professor veria se isso acontecesse.
  • Rodar na placa o sketch do dia e ler no monitor serial a leitura analogica que sera enviada.

Material

  • 1 ESP32 DevKit V1 por aluno, com o cabo USB
  • 1 protoboard de 830 pontos e 1 jogo de jumpers por aluno
  • 1 módulo de alimentacao MB102 por dupla
  • 1 potenciometro de 10 kOhm por dupla
  • Computador com Node.js 20 ou superior instalado antes da aula
  • Projetor, para o diagrama do caminho

Conceitos

O caminho do dado, peca por peca

O trimestre inteiro cabe nesta cadeia:

#PecaO que fazEm que aula
1Sensortransforma o mundo fisico em número analogicodia 1, dia 8 do 1o trimestre
2Placa ESP32le, transforma e monta o JSONdia 3
3WiFileva o JSON pela rede até outra maquinadia 3
4HTTPo idioma que placa e servidor falamdia 2
5Node.jsrecebe, decide e chama o bancodia 2, dia 5
6MySQLguarda a leitura para sempredia 4

O dado e o mesmo objeto nas seis pecas. O que muda e a representação, e essa e a ideia que o trimestre inteiro repete: o sensor produz um inteiro de 0 a 4095, a placa produz um texto JSON, o Node produz um objeto JavaScript, e o banco produz uma linha com sete colunas.

Por que este trimestre não comeca em software

No curso de JavaScript, o dado nascia no seu código: você digitava um valor, o programa recebia. Aqui o dado nasce em um sensor que você não controla, num processo que so roda quando alguem manda rodar, num banco que continua gravando depois que você fechar o editor.

A consequencia pratica e a diferença real entre os dois cursos: no navegador, o efeito e visivel na hora. Se o programa esta errado, a tela mostra. Aqui nada acontece sem subir processo, sem rota e sem banco, e o aluno so descobre que quebrou quando a tela do painel mostra dado de ontem.

Onde o dado pode se perder

Cada quebra tem uma assinatura diferente, e o aluno precisa aprender a reconhecela:

Peca que quebrouO que o aluno ve
Sensorleitura identica o tempo todo, sempre o mesmo número
Placanada muda no monitor serial
WiFiserial mostra "sem rede" e o loop continua
Servidor Nodeserial mostra código de rede negativo
Rota erradaservidor responde 404 e o Node imprime o log
Banco paradoNode responde 500 e o erro esta no erro.code
JSON invalidoNode lanca excecao no JSON.parse

O professor repete esta tabela ao longo do trimestre. E odiagnostico do curso.

Atividade

Montagem:

  • Potenciometro de 10 kOhm na protoboard: pino do meio em um GND, pino da esquerda na trilha +, pino da direita no GPIO34.
  • Resistor de 330 em serie com o lado esquerdo do potenciometro, para não sobrar corrente.
  1. Escreva no caderno as seis pecas do caminho, na ordem, e uma linha dizendo o que cada uma faz com o dado.
  2. Marque no seu desenho onde o dado deixa de ser número e vira texto. Escreva o nome do formato nesse ponto.
  3. Marque onde o dado deixa de ser temporario e vira permanente. Esse ponto e o que diferencia este trimestre do curso de JavaScript.
  4. Grave o sketch da resolucao e anote três leituras do GPIO34 que aparecam no monitor serial.
  5. Descreva, em uma frase, o que o professor veria se o cabo de rede fosse desconectado apenas no dia 8. Não e resposta hoje: e para o professor perguntar no dia 8.

Nota: 9 pontos. Critério de fim: o desenho tem as seis pecas na ordem, com os dois pontos marcados, e o monitor serial mostra três leituras diferentes.

Resolucao

A resposta do item 2 e o dia 3, quando a placa monta o JSON com String. A resposta do item 3 e o dia 4, quando a linha entra no MySQL. O professor guarda as duas respostas e devolve no dia correspondente.

O código de hoje mostra a ponta de origem do caminho: o sensor lido e o registro com nome de campo, que e a primeira coisa que o servidor vai receber. Não entra no WiFi e não monta JSON: a aula 1 do dia 3 faz isso.

// Aula 1 do dia 1: a arquitetura do trimestre inteira, em uma tela.
//
// O dia e sobre o CAMINHO do dado: sensor -> placa -> WiFi -> HTTP -> Node ->
// MySQL. Hoje so a primeira peca existe na bancada. Este sketch mostra a
// ponta de origem do caminho, que e a unica coisa que o 1o trimestre produz.

#include <Arduino.h>

// Pinos analogicos do DevKit V1. O 34 e o mais seguro para leitura analogica
// porque nao tem resistor de pull-up interno e so pode ser entrada.
const int PIN_SENSOR_ANALOGICO = 34;
const int PIN_SENSOR_DIGITAL  = 4;

// Resolução do conversor analogico: 12 bits, de 0 a 4095. Este numero e o
// que define o maximo de analogRead, e ele volta no dia 13.
const int RESOLUCAO_ADC = 4095;

// Valor lido no 1o trimestre e cru. Aqui ele vira uma leitura com nome,
// que e a primeira coisa que o servidor vai receber.
const float TEMP_MINIMA_C = 0.0;
const float TEMP_MAXIMA_C = 50.0;

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println();
  Serial.println("2o trimestre, dia 1, aula 1 - o caminho do dado");
  Serial.println("=================================================");
  Serial.println("hoje existe:  sensor -> placa");
  Serial.println("vira hoje:   placa -> WiFi -> HTTP -> Node -> MySQL");
  Serial.println();
  Serial.println("cada etapa do caminho, e quem cuida dela:");
  Serial.println("  1. sensor      : le o valor analogico, esta aula");
  Serial.println("  2. placa       : monta o JSON, dia 3");
  Serial.println("  3. WiFi        : leva o dado pela rede, dia 3");
  Serial.println("  4. HTTP        : o idioma, dia 2");
  Serial.println("  5. Node.js     : recebe e decide, dia 2 e dia 5");
  Serial.println("  6. MySQL       : guarda para sempre, dia 4");
  Serial.println();
  Serial.println("caminho | pino | bruto | leitura");
}

void loop() {
  int bruto = analogRead(PIN_SENSOR_ANALOGICO);

  // Transformacao linear de 0..4095 para TEMP_MINIMA_C..TEMP_MAXIMA_C.
  // E a MESMA conta do 1o trimestre, escrita sem map() para o professor
  // mostrar o que map() faz por dentro.
  float leitura = TEMP_MINIMA_C
                + (bruto / (float)RESOLUCAO_ADC) * (TEMP_MAXIMA_C - TEMP_MINIMA_C);

  Serial.print("      1  |   ");
  Serial.print(PIN_SENSOR_ANALOGICO);
  Serial.print("  |  ");
  Serial.print(bruto);
  Serial.print("  |  ");
  Serial.print(leitura, 1);
  Serial.println(" C");

  // A etapa 2 e o que o dia 3 faz. Hoje so e anunciada, nao executada.
  Serial.println("      2  |   4  |  ---  | JSON a ser montado no dia 3");

  delay(2000);
}

Por que assim e não de outro jeito. A tabela das seis pecas esta no código como Serial.println, e não so na lousa, porque o aluno precisa voltar nela. O que o professor não faz hoje e mostrar o JSON montado: seria tentar rodar a etapa 3 sem a etapa 2, e o aluno que não tem a etapa 2 não tem a etapa 3.

A transformacao linear esta escrita sem map() justamente para ser legivel. O aluno do 1o trimestre já usou map() no dia 5, e ver a conta inteira escrita resolve a duvida que sobra: "de onde saiu essa conta?".

Criterios de correcao

CritérioPontos
As seis pecas do caminho desenhadas, na ordem, com uma linha de função cada3 pontos
Ponto marcado onde o dado vira texto, com o nome do formato2 pontos
Ponto marcado onde o dado vira permanente1 ponto
Sketch gravado e três leituras diferentes anotadas no caderno2 pontos
Item 5 respondido em uma frase coerente1 ponto

Erros comuns

ErroComo apareceCorrecao
Desenhar o caminho com a placa no meio, e o sensor colado nela"O sensor e o ESP32""O sensor e um peca separada. O DHT11 do dia 8 tem três pinos e o ESP32 tem outros tantos. São coisas diferentes que se conectam por fio."
Pular a etapa do WiFi e do HTTP no desenhoO caminho vai de "placa" direto para "banco""A placa não fala com o banco. Ela fala HTTP com um processo Node, e o Node que fala SQL com o banco. São dois protocolos e duas maquinas."
Dizer que o dado vira permanente no MySQL porque o MySQL e "de verdade"Item 3 respondido com justificativa sobre o banco"O que torna o dado permanente não e o banco: e que o dado sobrevive ao fim do programa. O banco e so o lugar onde ele sobrevive. O Node também e um processo que morre quando você aperta Ctrl+C."
Ler o GPIO34 e escrever sempre o mesmo númeroItem 4 com três valores iguais"Você esta com o jumper do pino do meio solto, ou o potenciometro esta na posicao de extrema. Mexe com o potenciometro com a tela aberta e ve o número andar."
Achar que o delay(2000) e o tempo de leitura"A placa espera 2 segundos para ler o sensor""A placa espera 2 segundos depois de ler, so para a tela não virar um rio de número. Se o delay sumisse, a leitura seria a cada milissegundo e o monitor não daria conta de imprimir."

Desafio extra

O caminho tem seis pecas e cinco fronteiras entre elas. Escreva, para cada fronteira, uma frase dizendo o que pode dar errado exatamente ali, e qual das duas pecas vizinhas o professor culparia primeiro. A resposta que o professor prefere e a que diz que a culpa e sempre da fronteira, e não da peca: quem manda e quem recebe precisam concordar sobre o formato, e quando não concordam, nenhum dos dois tem razão.

>

A resolucao, compilada

// Aula 1 do dia 1: a arquitetura do trimestre inteira, em uma tela.
//
// O dia e sobre o CAMINHO do dado: sensor -> placa -> WiFi -> HTTP -> Node ->
// MySQL. Hoje so a primeira peca existe na bancada. Este sketch mostra a
// ponta de origem do caminho, que e a unica coisa que o 1o trimestre produz.

#include <Arduino.h>

// Pinos analogicos do DevKit V1. O 34 e o mais seguro para leitura analogica
// porque nao tem resistor de pull-up interno e so pode ser entrada.
const int PIN_SENSOR_ANALOGICO = 34;
const int PIN_SENSOR_DIGITAL  = 4;

// Resolução do conversor analogico: 12 bits, de 0 a 4095. Este numero e o
// que define o maximo de analogRead, e ele volta no dia 13.
const int RESOLUCAO_ADC = 4095;

// Valor lido no 1o trimestre e cru. Aqui ele vira uma leitura com nome,
// que e a primeira coisa que o servidor vai receber.
const float TEMP_MINIMA_C = 0.0;
const float TEMP_MAXIMA_C = 50.0;

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println();
  Serial.println("2o trimestre, dia 1, aula 1 - o caminho do dado");
  Serial.println("=================================================");
  Serial.println("hoje existe:  sensor -> placa");
  Serial.println("vira hoje:   placa -> WiFi -> HTTP -> Node -> MySQL");
  Serial.println();
  Serial.println("cada etapa do caminho, e quem cuida dela:");
  Serial.println("  1. sensor      : le o valor analogico, esta aula");
  Serial.println("  2. placa       : monta o JSON, dia 3");
  Serial.println("  3. WiFi        : leva o dado pela rede, dia 3");
  Serial.println("  4. HTTP        : o idioma, dia 2");
  Serial.println("  5. Node.js     : recebe e decide, dia 2 e dia 5");
  Serial.println("  6. MySQL       : guarda para sempre, dia 4");
  Serial.println();
  Serial.println("caminho | pino | bruto | leitura");
}

void loop() {
  int bruto = analogRead(PIN_SENSOR_ANALOGICO);

  // Transformacao linear de 0..4095 para TEMP_MINIMA_C..TEMP_MAXIMA_C.
  // E a MESMA conta do 1o trimestre, escrita sem map() para o professor
  // mostrar o que map() faz por dentro.
  float leitura = TEMP_MINIMA_C
                + (bruto / (float)RESOLUCAO_ADC) * (TEMP_MAXIMA_C - TEMP_MINIMA_C);

  Serial.print("      1  |   ");
  Serial.print(PIN_SENSOR_ANALOGICO);
  Serial.print("  |  ");
  Serial.print(bruto);
  Serial.print("  |  ");
  Serial.print(leitura, 1);
  Serial.println(" C");

  // A etapa 2 e o que o dia 3 faz. Hoje so e anunciada, nao executada.
  Serial.println("      2  |   4  |  ---  | JSON a ser montado no dia 3");

  delay(2000);
}

Sem saída de compilação gravada. Rode python3 validar.py -t 2 dia01 aula1.

Aula 2 — Node.js: o runtime e o primeiro programa no terminal

Objetivos

  • Rodar um programa Node.js no terminal e explicar que o console.log e a unica saída que importa fora do navegador.
  • Ler o resultado de process.argv e dizer o que são os dois primeiros itens da lista.
  • Instalar e executar um pacote com npm e dizer o que package.json guarda.
  • Justificar por que um programa de terminal morre quando chega no fim, e o que isso muda em relacao a uma página web.
  • Reconhecer o SyntaxError pela mensagem e dizer em qual linha o Node parou.

Material

  • 1 ESP32 DevKit V1 por dupla, com o cabo USB
  • 1 protoboard de 830 pontos por dupla
  • Computador com Node.js 20 ou superior instalado antes da aula
  • Terminal com permissao para instalar pacote global
  • Projetor, para o terminal do professor

Conceitos

O que muda quando o código sai do navegador

No 1o trimestre, o código rodava dentro do navegador, que cuidava de tudo: abria a porta, recebia a página, mantinha o programa vivo. Em Node, o programa e o responsavel pela propria existencia:

No navegadorEm Node
O navegador abre a portaO código chama server.listen
O navegador mantem a página vivaO código mantem o processo vivo
fetch para outro enderecohttp.createServer cria um endereco
Erro aparece no console do navegadorErro aparece no terminal

A frase que o professor repete: "no navegador a linguagem sozinha faz algo visivel; aqui nada acontece sem subir um processo". E por isso que a aula de hoje termina com um programa que imprime e morre, e o dia 2 troca isso por um processo que fica vivo.

console.log, process.argv e a morte do processo

console.log('ola');   // escreve no terminal e segue

O console.log em Node e a saída padrao do processo. Tudo que o programa quiser contar precisa sair por ele, porque o terminal e a unica tela disponivel.

process.argv traz os argumentos da linha de comando. A lista sempre comeca com dois itens que o aluno não escreveu: o caminho do executavel e o caminho do arquivo. Por isso o slice(2).

O programa de terminal termina sozinho quando chega na ultima linha. Não ha loop, não ha evento, não ha janela segurando ele aberto. Isso e uma diferença e não um defeito: o programa faz o que tem que fazer e libera a maquina.

package.json e node_modules

Quando o aluno roda npm init -y, o Node cria o package.json: o nome do projeto, a versão, e a lista de dependencias. Quando roda npm install mysql2, ele baixa o pacote para dentro de node_modules.

O professor repete nesta aula: node_modules nunca vai para o git, e o motivo e o dia 11. O que vai para o git e o package.json e o package-lock.json, que juntos dizem "para reproduzir este projeto, instale estas versões".

Atividade

Montagem: nenhuma. Hoje não se liga nada. A placa e programada no fim da aula so para mostrar de onde vem o dado que o Node vai receber.

  1. Rode node --version e escreva a versão no caderno. Se não responder, avisar o professor antes de comecar.
  2. Escreva o arquivo primeiro.js com três console.log: a versão do Node, a versão do motor V8 e o process.pid.
  3. Rode o programa duas vezes e compare os dois resultados. Alguma coisa muda entre as execuções? Diga qual.
  4. Rode o programa com dois argumentos: node primeiro.js lab01 23.5. O que aparece em process.argv.slice(2)? Por que lab01 e o primeiro item e não o terceiro?
  5. Em uma pasta nova, rode npm init -y e depois npm install chalk. Liste o que apareceu na pasta depois de cada comando.
  6. Apague a chave de fechamento do console.log do item 2 e rode de novo. Leia a mensagem de erro inteira e escreva em uma frase o que ela diz e onde aponta.

Nota: 10 pontos. Critério de fim: o programa roda, a diferença entre as duas execuções esta escrita, e o SyntaxError foi lido e explicado.

Resolucao

O programa do dia 1 e o código que o professor digita junto com a turma:

// Primeiro programa em Node. O aluno acabou de terminar JavaScript no
// navegador: a mesma linguagem, fora do navegador, rodando como processo.
console.log('Node em uso:', process.version);
console.log('Motor V8:', process.versions.v8);

// process.argv traz o executavel, o arquivo e os argumentos do comando.
// slice(2) descarta os dois primeiros, que o aluno nao escreveu.
console.log('argumentos recebidos:', process.argv.slice(2));

// O que separa um programa de terminal de uma pagina web: ele começa, faz
// o que tem que fazer, e morre. Nao existe janela para ficar esperando.
console.log('pid', process.pid, 'terminando agora');

Por que assim e não de outro jeito. O console.log de process.pid existe para o item 3 da atividade. Sem ele, o aluno roda o programa duas vezes e ve exatamente a mesma saída, e conclui que "o Node guardou o estado" — que e um mal-entendido que custa metade da aula de ORM e modules no 3o trimestre. Com o pid, o número muda a cada execução e o aluno ve, sozinho, que cada execução e um processo novo.

O process.argv.slice(2) e o item 4, e o professor não explica por que e 2: ele pergunta, e espera a resposta. Quem nunca perguntou por que e 2 e nunca vai entender slice no dia 8 do 3o trimestre.

A saída real do programa, medida nesta maquina:

Node em uso: v24.21.0
Motor V8: 13.6.233.17-node.53
argumentos recebidos: [ 'lab01', '23.5' ]
pid 377022 terminando agora

O professor não escreve esta saída na lousa. Ele roda o programa na frente da turma e deixa o terminal falar. A diferença entre o número do pid e o que o aluno ve no terminal dele e o ponto: são processos diferentes, e o pid prova isso.

O sketch da placa vem na resolucao do código C++, e existe porque a porta do material exige um exemplo de placa em toda aula. Ele mede o valor analogico e monta o JSON que sera enviado, mas não entra no WiFi: o dia 3 faz isso. O comentario do sketch diz isso ao aluno, e o professor diz na lousa antes de comecar a ler código.

Criterios de correcao

CritérioPontos
node --version conferido e versão anotada1 ponto
Programa com os três console.log roda e imprime3 pontos
Item 3 respondido: a diferença entre as duas execuções esta nomeada2 pontos
Item 4 respondido: o motivo do slice(2) esta em uma frase2 pontos
O que apareceu na pasta depois de npm init e depois de npm install esta listado1 ponto
Item 6 respondido: a mensagem do SyntaxError foi lida e explicada1 ponto

Erros comuns

ErroComo apareceCorrecao
Rodar o .js dando dois cliques no explorador de arquivosNada acontece, ou abre o navegador"Node não e um programa de janela. No terminal, digite node primeiro.js e aperte enter."
Escrever console.log("texto " + x) e esperar o terminal quebrar a linha sozinho"No navegador a linha quebra, aqui não""No terminal a quebra de linha e sua. Se quiser o mesmo efeito do navegador, e o que a aula de formatacao faz, e não hoje."
console.log(process.argv) e concluir que o array tem dois itensItem 4 com resposta errada"Rode o comando com argumentos e olhe de novo. Os dois primeiros itens aparecem sempre, e não são seus."
Achar que npm install instalou o Node"Precisei instalar o Node com npm""npm e o gerenciador de pacotes do Node. Ele instala bibliotecas, não o Node. O Node já veio instalado."
Fechar a chave do console.log e usar a mensagem como se fosse de rede"O erro fala de Unexpected token, então o servidor caiu""Esse erro acontece antes de qualquer linha rodar: e o Node nem comecou a executar. Erro de sintaxe e erro de rede são coisas diferentes, e o dia 3 usa essa distincao."
Deixar o node_modules dentro da pasta do projeto e depois tentar enviar a pasta inteira"Pasta muito grande para mandar""Isso e esperado, e o motivo de o dia 11 tratar .gitignore. Não apague agora: e o que faz o projeto funcionar."

Desafio extra

Escreva um programa que receba quantos argumentos quiser e imprima o primeiro e o ultimo, sem quebrar quando o aluno rodar sem nenhum argumento. Depois rode com zero, com um e com cinco argumentos, e escreva o que o programa faz em cada caso. O que o professor espera descobrir: sem checar o tamanho do array, argv[argv.length - 1] devolve undefined, e imprimir undefined e um bug que passa em cinco de seis casos.

>

A resolucao, compilada

// Aula 2 do 2o trimestre: o dia e de software (Node.js no terminal), mas o
// dado que o Node vai receber precisa existir em algum lugar.
//
// Este sketch existe para isso: ele mede um valor ANALOGICO de verdade,
// mostra o JSON que seria enviado e nada mais. Nao entra no WiFi, nao faz
// HTTP, nao tem servidor. O comentario do professor diz isso na lousa, e o
// aluno precisa ouvir antes de comecar a ler codigo.

#include <Arduino.h>

// Pino analogico do kit. GPIO34 e um dos poucos que so le analogico e nao
// tem resistor de pull-up interno, por isso e o mais seguro para este teste.
const int PIN_SENSOR_ANALOGICO = 34;

// A placa que este trimestre vai enviar. Vai ser o id_placa no JSON.
const char ID_PLANCA[] = "esp32-lab01";

// Intervalo entre leituras. O mesmo intervalo do dia 7, onde ele vira o tema:
// aqui ele so serve para a tela nao virar um rio de numero.
const long INTERVALO_MS = 2000;

void setup() {
  Serial.begin(115200);
  // Dois segundos de espera antes do primeiro print: sem isso, os primeiros
  // caracteres saem antes de o monitor serial abrir e somem da tela.
  delay(2000);
  Serial.println();
  Serial.println("2o trimestre, dia 1, aula 2 - o dado antes do Node");
  Serial.println("-------------------------------------------------");
  Serial.println("Hoje o codigo roda SO na placa. A rede entra no dia 2.");
  Serial.println();
  Serial.println("id_placa | temp_c_simulado | umidade_pct_simulado");
}

void loop() {
  // analogRead devolve um inteiro de 0 a 4095: 12 bits. Ainda nao e um valor
  // de sensor, e um numero cru — a aula 8 do 1o trimestre ja transformou esse numero em valor real.
  int bruto = analogRead(PIN_SENSOR_ANALOGICO);

  // Transformacao Linear: leva 0..4095 para 0..100. E a MESMA ideia do map()
  // do 1o trimestre, so que escrita a mao para ficar visivel aqui.
  float percentual = (bruto / 4095.0f) * 100.0f;

  // O JSON e montado NA MAO, com string e concatenacao. Nao existe biblioteca
  // de JSON no core do ESP32: e isso que o dia 3 vai organizar de vez.
  // Repare no \\" : cada aspa do JSON e uma aspa que o C++ tambem precisa
  // usar, entao ela e escapada com a barra.
  String json = "{\"id_placa\":\"" + String(ID_PLANCA) + "\""
              + ",\"temp_c_simulado\":" + String(percentual, 1)
              + ",\"dt_leitura\":\"pendente\"}";

  Serial.print("leitura analogica bruta: ");
  Serial.print(bruto);
  Serial.print("  |  percentual: ");
  Serial.print(percentual, 1);
  Serial.println(" %");
  Serial.print("JSON que a placa vai mandar no dia 3: ");
  Serial.println(json);
  Serial.println("-------------------------------------------------");

  delay(INTERVALO_MS);
}

Sem saída de compilação gravada. Rode python3 validar.py -t 2 dia01 aula2.