← Mundo bit Byte

Sistemas Embarcados e IoT

Professor Ronaldo Lavestein
1. Fundamentos 2. Entrada/Saída 3. Sensores 4. Atuadores/Automação 5. Programação Aplicada 99. Exercícios
5

Programação aplicada ao sistema embarcado

Você já fez o Arduino ler sensores, controlar saídas e movimentar um servo. Agora o desafio é outro: transformar leituras em informação útil, criar movimentos calculados, produzir mensagens compreensíveis e testar o programa com diferentes entradas.

Medir
→
Calcular
→
Comunicar
→
Testar
Sem recomeçarOs circuitos principais já são conhecidos e serão reaproveitados.
AmbienteArduino Uno e Tinkercad continuam sendo a base das práticas deste bloco.
ObjetivoUsar funções quando elas resolvem uma necessidade real do sistema.
5.1

Quando só a distância não basta

Funções matemáticas ajudam a transformar uma leitura isolada em informação útil para acompanhar o comportamento do sistema.

Circuito reutilizado

Arduino Uno ligado ao sensor ultrassônico HC-SR04, com VCC em 5V, GND em GND, TRIG no pino 3 e ECHO no pino 2.
Reutilize o circuito do sensor de distância já estudado: HC-SR04 com TRIG no D3 e ECHO no D2. Não há nova montagem nesta etapa.

1. Situação

Durante o teste de um sistema de aproximação, definimos 30 cm como distância de referência. O HC-SR04 informa a distância atual, mas queremos responder a três perguntas: quanto a leitura se afastou da referência, qual foi a menor distância observada e qual foi a maior?

Essas informações permitem analisar o teste sem anotar manualmente cada leitura.

2. O que já sabemos

O HC-SR04 envia um pulso pelo TRIG, recebe o eco pelo ECHO e permite calcular a distância. O Monitor Serial já pode mostrar o valor medido.

3. O que precisamos agora

Precisamos comparar valores sem escrever toda a lógica matemática manualmente. É aí que entram funções prontas que devolvem um resultado.

4. Três funções úteis neste teste

abs(valor)

Retorna o valor absoluto. Aqui mostra o tamanho da diferença para 30 cm sem importar se o objeto está antes ou depois da referência.

min(a, b)

Retorna o menor dos dois valores. Usaremos para guardar a menor distância encontrada desde o início do teste.

max(a, b)

Retorna o maior dos dois valores. Usaremos para guardar a maior distância encontrada.

5. Código completo

const int pinoTrig = 3;
const int pinoEcho = 2;
const int referencia = 30;

int menorDistancia = 400;
int maiorDistancia = 0;

int medirDistancia() {
  digitalWrite(pinoTrig, LOW);
  delayMicroseconds(2);
  digitalWrite(pinoTrig, HIGH);
  delayMicroseconds(10);
  digitalWrite(pinoTrig, LOW);

  long duracao = pulseIn(pinoEcho, HIGH);
  return duracao / 58;
}

void setup() {
  pinMode(pinoTrig, OUTPUT);
  pinMode(pinoEcho, INPUT);
  Serial.begin(9600);
}

void loop() {
  int distancia = medirDistancia();
  int diferenca = abs(distancia - referencia);

  menorDistancia = min(menorDistancia, distancia);
  maiorDistancia = max(maiorDistancia, distancia);

  Serial.print("Distancia: ");
  Serial.print(distancia);
  Serial.print(" cm | Diferenca para 30 cm: ");
  Serial.print(diferenca);
  Serial.print(" cm | Menor: ");
  Serial.print(menorDistancia);
  Serial.print(" cm | Maior: ");
  Serial.print(maiorDistancia);
  Serial.println(" cm");

  delay(500);
}

6. Entendendo as novidades

abs(distancia - referencia) transforma uma diferença que poderia ser negativa em uma medida de afastamento sempre positiva.

min(menorDistancia, distancia) compara a menor leitura já guardada com a leitura atual e mantém a menor.

max(maiorDistancia, distancia) faz o mesmo para a maior leitura.

Os valores de mínimo e máximo permanecem acumulados enquanto o programa estiver executando. Reiniciar a simulação inicia um novo teste.

7. Testando

  1. Inicie a simulação e abra o Monitor Serial em 9600.
  2. Altere a distância do objeto no HC-SR04.
  3. Passe por valores menores e maiores que 30 cm.
  4. Observe a diferença, o menor valor e o maior valor registrados.

8. Resultado esperado

Se a leitura for 18 cm, a diferença para a referência de 30 cm será 12 cm. Se depois a leitura passar para 45 cm, a diferença será 15 cm. O programa também continuará lembrando os extremos observados.

9. Erros comuns

ProblemaO que conferir
A distância não variaConfira a simulação do HC-SR04 e as ligações TRIG/ECHO.
O mínimo parece não aumentarIsso é esperado: ele guarda a menor leitura já observada até reiniciar o teste.
A diferença fica sempre positivaTambém é esperado: essa é justamente a finalidade de abs() neste problema.

10. O que foi aplicado

  • Funções matemáticas abs(), min() e max()
  • Reutilização de leitura do HC-SR04
  • Transformação de dados brutos em informação de acompanhamento
  • Uso do Monitor Serial para observar o teste
5.2

Um movimento que não pareça feito aos saltos

Uma função trigonométrica pode gerar um comportamento periódico e suave que se torna visível no próprio hardware.

Circuito reutilizado

Arduino Uno ligado a um Micro Servo com sinal no pino digital 6, alimentação em 5V e GND.
Reutilize o circuito do Servo Motor já estudado: sinal no D6, alimentação em 5V e GND. A novidade será somente a forma de calcular as posições.

1. Situação

Imagine um pequeno mecanismo que precisa fazer um movimento contínuo de um lado para o outro. Mandar o servo diretamente para 30°, depois 90° e depois 150° produz mudanças em etapas. Queremos gerar uma sequência de posições que aumente, diminua e se repita suavemente, sem escrever centenas de ângulos à mão.

2. O que já sabemos

O projeto anterior de Servo já ensinou a biblioteca Servo, o comando attach() e o envio de posições com write(). Não vamos ensinar biblioteca novamente.

3. Por que trigonometria aparece aqui

Seno e cosseno produzem valores que variam periodicamente. Isso permite transformar uma sequência matemática em movimento, sinal ou outra grandeza que sobe e desce de forma repetitiva.

4. Funções trigonométricas no Arduino

sin(x)

Calcula o seno. Seus valores variam entre -1 e 1 e serão usados para controlar o movimento deste projeto.

cos(x)

Calcula o cosseno. Também varia periodicamente entre -1 e 1, mas começa o ciclo em outra posição.

tan(x)

Calcula a tangente. Pode crescer muito perto de certos ângulos e, por isso, não é adequada para limitar diretamente a posição deste servo.

Atenção: sin(), cos() e tan() recebem o ângulo em radianos. Por isso converteremos graus usando graus * PI / 180.0.

5. Código completo

#include <Servo.h>

Servo meuServo;
const int pinoServo = 6;

void setup() {
  meuServo.attach(pinoServo);
}

void loop() {
  for (int graus = 0; graus <= 360; graus += 5) {
    float radianos = graus * PI / 180.0;
    float valorSeno = sin(radianos);

    int anguloServo = 90 + valorSeno * 60;
    meuServo.write(anguloServo);

    delay(30);
  }
}

6. Entendendo as novidades

graus * PI / 180.0 converte graus para radianos.

sin(radianos) gera valores entre aproximadamente -1 e 1 ao longo do ciclo.

90 + valorSeno * 60 transforma essa faixa em posições aproximadas entre 30° e 150° para o servo.

O for percorre um ciclo de 0° a 360° da função matemática. Ele não representa diretamente o ângulo físico do servo; quem define a posição física é anguloServo.

7. Testando

  1. Inicie a simulação.
  2. Observe o servo completar um ciclo contínuo de ida e volta.
  3. Troque 60 por 30 e compare a amplitude do movimento.
  4. Volte para 60 antes de seguir.

8. Resultado esperado

O eixo deverá oscilar aproximadamente entre 30° e 150°, passando pela região de 90° durante o ciclo. A posição muda em pequenos passos calculados, em vez de usar uma lista fixa de ângulos.

9. Erros comuns

ProblemaO que conferir
O servo não se moveConfira sinal no D6, 5V, GND, #include <Servo.h> e attach().
Os valores de seno parecem errados em outro testeVerifique se o ângulo foi convertido de graus para radianos.
O servo tenta ultrapassar sua faixaNão aumente a amplitude sem calcular antes os limites de posição.

10. O que foi aplicado

  • Funções trigonométricas sin(), cos() e tan() em seu papel correto
  • Conversão de graus para radianos
  • Uso prático de sin() para gerar movimento periódico
  • Reutilização do Servo e de sua biblioteca sem repetir conteúdo já aprendido
5.3

O sistema precisa explicar o que está acontecendo

Uma leitura numérica é útil para a máquina; uma mensagem organizada é muito mais útil para quem acompanha o sistema.

Mesmo circuito, nova finalidade

Arduino Uno ligado ao sensor ultrassônico HC-SR04, com VCC em 5V, GND em GND, TRIG no pino 3 e ECHO no pino 2.
O circuito continua exatamente o mesmo da etapa 5.1. Agora o trabalho está na forma de organizar e comunicar a informação.

1. Situação

No Monitor Serial, ver apenas 18 obriga a pessoa a lembrar o que esse número significa e se ele exige atenção. Queremos que o próprio programa produza algo como DISTANCIA: 18 cm | ESTADO: MUITO PROXIMO.

Além de facilitar o acompanhamento agora, essa mensagem já prepara uma ideia importante para o próximo bloco: informações que poderão ser enviadas a outro dispositivo.

2. O que já sabemos

Já sabemos medir a distância e usar condições para tomar decisões. Também sabemos mostrar valores com Serial.print().

3. O que precisamos agora

Precisamos criar textos, juntar texto com números e medir o tamanho da mensagem produzida. Para isso usaremos o tipo String e funções associadas a ele.

4. Recursos de texto usados

String(valor)

Converte um valor numérico para texto, permitindo incluí-lo em uma mensagem.

+

Quando trabalhamos com String, permite concatenar partes e formar uma mensagem maior.

.length()

Informa quantos caracteres a mensagem possui. Isso permite conhecer o tamanho do texto que estamos produzindo.

5. Código completo

const int pinoTrig = 3;
const int pinoEcho = 2;

int medirDistancia() {
  digitalWrite(pinoTrig, LOW);
  delayMicroseconds(2);
  digitalWrite(pinoTrig, HIGH);
  delayMicroseconds(10);
  digitalWrite(pinoTrig, LOW);

  long duracao = pulseIn(pinoEcho, HIGH);
  return duracao / 58;
}

String classificarDistancia(int distancia) {
  if (distancia <= 15) {
    return "MUITO PROXIMO";
  }
  if (distancia <= 40) {
    return "PROXIMO";
  }
  return "AREA LIVRE";
}

void setup() {
  pinMode(pinoTrig, OUTPUT);
  pinMode(pinoEcho, INPUT);
  Serial.begin(9600);
}

void loop() {
  int distancia = medirDistancia();
  String estado = classificarDistancia(distancia);

  String mensagem = "DISTANCIA: " + String(distancia)
                  + " cm | ESTADO: " + estado;

  Serial.println(mensagem);
  Serial.print("Caracteres da mensagem: ");
  Serial.println(mensagem.length());

  delay(500);
}

6. Entendendo as novidades

String classificarDistancia(...) cria uma função que devolve texto de acordo com a leitura.

String(distancia) converte o número medido para que ele possa fazer parte da mensagem.

O operador + junta as partes do texto.

mensagem.length() informa a quantidade de caracteres da mensagem final.

7. Testando

  1. Abra o Monitor Serial.
  2. Teste uma distância de até 15 cm.
  3. Teste uma distância entre 16 e 40 cm.
  4. Teste uma distância acima de 40 cm.
  5. Observe como o texto e seu tamanho mudam.

8. Resultado esperado

Em vez de receber apenas números, a pessoa passa a enxergar uma leitura com unidade e estado. O sistema começa a separar dado de informação compreensível.

9. Erros comuns

ProblemaO que conferir
Erro ao montar a mensagemConfira a conversão String(distancia) e os sinais de +.
Estado diferente do esperadoConfira a ordem e os limites das condições: até 15, até 40 e acima de 40.
O número de caracteres mudaIsso é normal: valores com mais dígitos e estados com tamanhos diferentes alteram o comprimento da mensagem.

10. O que foi aplicado

  • Criação e conversão de textos com String
  • Concatenação de texto e valores
  • Uso de length()
  • Transformação de leitura numérica em mensagem de estado
5.4

Como testar situações que não estão acontecendo agora?

Números pseudoaleatórios podem funcionar como entradas de teste quando queremos verificar rapidamente vários comportamentos do programa.

1. Situação

Para conferir a classificação de distância, podemos mover o objeto repetidamente no simulador. Mas, antes de evoluir o sistema, queremos executar muitos testes de lógica sem depender de uma posição física específica do sensor a cada vez.

Nesta etapa, o circuito deixa de ser necessário: o próprio programa fornecerá leituras simuladas para verificar as decisões.

2. Por que não há imagem de circuito

Não existe uma nova montagem para executar. Inserir um desenho ou uma imagem decorativa não ajudaria a realizar a prática. Basta um Arduino no Tinkercad e o Monitor Serial.

3. O novo recurso

random(minimo, maximo) gera números pseudoaleatórios. O limite mínimo pode aparecer; o limite máximo fica de fora.

Assim, random(5, 81) pode gerar valores de 5 até 80.

4. Código completo

void setup() {
  Serial.begin(9600);
}

void loop() {
  int distanciaTeste = random(5, 81);

  String estado;

  if (distanciaTeste <= 15) {
    estado = "MUITO PROXIMO";
  } else if (distanciaTeste <= 40) {
    estado = "PROXIMO";
  } else {
    estado = "AREA LIVRE";
  }

  Serial.print("Distancia simulada: ");
  Serial.print(distanciaTeste);
  Serial.print(" cm | Estado: ");
  Serial.println(estado);

  delay(700);
}

5. Entendendo as novidades

random(5, 81) fornece uma entrada de teste entre 5 e 80 cm.

A lógica de classificação é a mesma que seria usada com uma distância recebida do sensor. Isso permite testar o programa separadamente da montagem física.

Os valores são pseudoaleatórios: são produzidos por um algoritmo. Neste laboratório, isso é suficiente porque o objetivo não é segurança nem sorteio real; é exercitar diferentes faixas de entrada.

6. Testando

  1. Crie um circuito novo no Tinkercad apenas com o Arduino Uno.
  2. Cole o código e inicie a simulação.
  3. Abra o Monitor Serial em 9600.
  4. Observe se aparecem casos das três faixas.
  5. Confira se cada valor recebe o estado correto.

7. Resultado esperado

O Monitor Serial exibirá diferentes distâncias simuladas e classificará cada uma. O foco não é prever qual número virá, mas confirmar se a regra responde corretamente a diferentes entradas.

8. Erros comuns

ProblemaO que conferir
Esperar o valor 81O segundo argumento de random() não entra na faixa gerada.
Usar random() como se fosse um sensor realAqui ele serve apenas para teste de software. No sistema real, a entrada volta a vir do sensor.
Achar que pseudoaleatório significa imprevisível para qualquer finalidadeNão. Este recurso não deve ser confundido com geração adequada para segurança ou criptografia.

9. O que foi aplicado

  • Geração de números pseudoaleatórios com random()
  • Uso correto dos limites mínimo e máximo
  • Simulação de entradas para teste de lógica
  • Reutilização das regras de classificação trabalhadas anteriormente

10. Próximo problema

Até aqui, o sistema mede, calcula, organiza informações e pode testar sua própria lógica. Mas tudo continua acontecendo no Arduino e no computador ligado a ele. Como acompanhar ou controlar esse sistema a partir de outro dispositivo?

Essa necessidade abre o próximo bloco: Conectividade.

\n