Loja física na cidade do Montijo na Rua Cidade de Faro 111
A loja não tem serviço de reparação de equipamentos
Clientes registados, com conta criada, por cada 500€ acumulados de compras, receberão um voucher de 25€ para gastar na loja (Válido para compras a partir de 01-01-2025)
Trustpilot

Não Use delay()! Descubra Alternativas Essenciais para Projetos Arduino Mais Eficazes e Eficientes (millis(), Interrupções)

Escrito em 2 de Abril de 2025

Não Use delay()! Descubra Alternativas Essenciais para Projetos Arduino Mais Eficazes e Eficientes (millis(), Interrupções)

Não Use delay()! Descubra Alternativas Essenciais para Projetos Arduino Mais Eficazes e Eficientes (millis(), Interrupções)

No universo fascinante do Arduino, criar projetos interativos, desde piscar um simples LED até controlar sistemas complexos, é uma jornada recompensadora. No entanto, muitos iniciantes (e até alguns utilizadores mais experientes) tropeçam num obstáculo comum que limita severamente o potencial dos seus projetos: o uso excessivo da função delay(). Embora pareça a solução mais fácil para criar pausas ou temporizar ações, delay() é, na verdade, um dos maiores inimigos da responsividade e da eficiência em microcontroladores.

Este artigo aprofundado irá explorar por que deve evitar delay() a todo o custo e apresentar as alternativas poderosas e flexíveis que transformarão a forma como programa o seu Arduino: a função millis() e o mecanismo de interrupções. Ao dominar estas técnicas, estará apto a criar projetos que podem executar múltiplas tarefas "simultaneamente", reagir instantaneamente a eventos externos e operar de forma muito mais suave e profissional. Prepare-se para elevar os seus projetos Arduino a um novo patamar!

O Vilão Oculto: Porquê delay() é Prejudicial para o seu Projeto Arduino?


Para compreender a necessidade de alternativas, é crucial entender o que delay(ms) realmente faz nos bastidores. Quando invoca delay(1000), por exemplo, está a instruir o microcontrolador Arduino (como o ATmega328P numa placa Uno) a parar completamente toda a execução de código durante 1000 milissegundos (1 segundo).

Pense nisto como um cozinheiro que precisa de ferver água: Em vez de colocar a água ao lume e continuar a preparar outros ingredientes enquanto espera, o cozinheiro delay() para tudo, fica a olhar fixamente para a panela durante 5 minutos, sem fazer absolutamente mais nada. Só depois da água ferver (o tempo do delay() expirar) é que retoma as outras tarefas.

As Consequências Negativas de Usar delay():

Bloqueio Total do Processador: Durante um delay(), o microcontrolador não faz nada útil. Não lê sensores, não verifica botões, não comunica via serial, não atualiza ecrãs. Está completamente congelado.
Falta de Responsividade: Se um utilizador pressionar um botão enquanto o Arduino está "preso" num delay(), essa ação será ignorada. Se um sensor detetar uma condição crítica, o Arduino só reagirá depois do delay() terminar. Isto torna os projetos lentos, frustrantes e, em alguns casos, perigosos.
Incapacidade de Multitarefa (Aparente): Microcontroladores como os do Arduino são, na sua essência, sequenciais (executam uma instrução de cada vez). No entanto, ao evitar bloqueios, podemos simular multitarefa, permitindo que diferentes partes do código corram de forma intercalada e rápida. delay() impede completamente esta capacidade.
Dificuldade em Implementar Lógica Complexa: Projetos que precisam de gerir múltiplos eventos temporizados (piscar LEDs a ritmos diferentes, verificar sensores periodicamente, comunicar em intervalos regulares) tornam-se quase impossíveis de implementar de forma fiável e elegante com delay().


Exemplo Clássico do Problema:

Imagine que quer piscar um LED a cada segundo, mas também quer ler o estado de um botão e acender outro LED imediatamente quando o botão for pressionado.

C++ 
// Exemplo Problemático com delay()
const int ledPinPiscante = 13;
const int ledPinBotao = 12;
const int botaoPin = 2;

void setup() {
pinMode(ledPinPiscante, OUTPUT);
pinMode(ledPinBotao, OUTPUT);
pinMode(botaoPin, INPUT_PULLUP); // Usar resistor pull-up interno
}

void loop() {
// Piscar o LED
digitalWrite(ledPinPiscante, HIGH);
delay(1000); // *** PROBLEMA AQUI *** O Arduino para por 1 segundo
digitalWrite(ledPinPiscante, LOW);
delay(1000); // *** PROBLEMA AQUI *** O Arduino para por mais 1 segundo

// Verificar o botão (SÓ SERÁ VERIFICADO A CADA 2 SEGUNDOS!)
int estadoBotao = digitalRead(botaoPin);
if (estadoBotao == LOW) { // Botão pressionado (LOW devido ao pull-up)
digitalWrite(ledPinBotao, HIGH);
} else {
digitalWrite(ledPinBotao, LOW);
}
// O botão só é lido muito raramente devido aos delays.
// A resposta ao botão será muito lenta e inconsistente.
}

Neste código, o botão só será verificado uma vez a cada dois segundos. Se pressionar o botão durante o delay(1000), o Arduino não saberá até que o delay() termine e a leitura digitalRead(botaoPin) seja finalmente executada. A resposta é inaceitável para a maioria das aplicações interativas.

Claramente, precisamos de uma abordagem melhor.

A Alternativa Elegante: millis() para Temporização Não-Bloqueante


A solução mais comum e versátil para substituir delay() em tarefas temporizadas é a função millis().

O que é millis()?

millis() é uma função incorporada na framework Arduino que retorna o número de milissegundos que passaram desde que a placa Arduino começou a executar o programa atual. Este valor é armazenado como um unsigned long, um tipo de variável que pode conter números inteiros positivos muito grandes (até 4.294.967.295).

Como Funciona a Técnica "Blink Without Delay"?

A chave para usar millis() eficazmente é adotar um padrão de programação frequentemente chamado de "Blink Without Delay" (piscar sem delay()), embora se aplique a qualquer tarefa temporizada. A lógica é a seguinte:

Registar o Tempo Anterior: Crie uma variável (geralmente unsigned long) para armazenar a última vez que a ação temporizada foi executada. Vamos chamá-la previousMillis.
Definir o Intervalo: Defina uma constante ou variável (const long ou unsigned long) para o intervalo desejado entre as ações (em milissegundos). Vamos chamá-la interval.
Verificar o Tempo Decorrido no loop(): Dentro da função loop(), que corre continuamente:Obtenha o tempo atual chamando millis() e armazene-o numa variável currentMillis.
Calcule a diferença entre o tempo atual e o tempo anterior: currentMillis - previousMillis.
Compare: Verifique se essa diferença é maior ou igual ao interval desejado.
Executar a Ação (se o tempo passou): Se o intervalo foi atingido:Execute a ação desejada (mudar o estado de um LED, ler um sensor, etc.).
Atualize o Tempo Anterior: Faça previousMillis = currentMillis; para registar o momento exato em que a ação foi realizada, preparando para o próximo intervalo.
Vantagem Crucial: Entre as verificações de tempo, o seu código no loop() continua a executar outras tarefas (ler botões, comunicar, etc.) sem parar. O loop() executa milhares ou milhões de vezes por segundo, dando a ilusão de que tudo acontece em paralelo.

Exemplo: Piscar LED e Ler Botão Responsivamente com millis()

Vamos reescrever o exemplo anterior usando millis():

C++ 
// Exemplo Responsivo com millis()
const int ledPinPiscante = 13;
const int ledPinBotao = 12;
const int botaoPin = 2;

// Variáveis para o LED Piscante
int ledState = LOW; // Estado atual do LED
unsigned long previousMillis = 0; // Armazena a última vez que o LED foi atualizado
const long interval = 1000; // Intervalo para piscar (1000 ms = 1 segundo)

void setup() {
Serial.begin(9600); // Iniciar comunicação serial para depuração (opcional)
pinMode(ledPinPiscante, OUTPUT);
pinMode(ledPinBotao, OUTPUT);
pinMode(botaoPin, INPUT_PULLUP);
}

void loop() {
// --- Bloco de código para o LED Piscante (Não-Bloqueante) ---
unsigned long currentMillis = millis(); // Obter o tempo atual

if (currentMillis - previousMillis >= interval) {
// Salvar o último tempo em que piscamos o LED
previousMillis = currentMillis;

// Inverter o estado do LED
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}

// Atualizar o LED físico com o novo estado
digitalWrite(ledPinPiscante, ledState);
Serial.print("LED Piscante mudou para: "); // Mensagem de depuração
Serial.println(ledState == HIGH ? "ON" : "OFF");
}

// --- Bloco de código para o Botão (Executa continuamente) ---
int estadoBotao = digitalRead(botaoPin);
if (estadoBotao == LOW) { // Botão pressionado
digitalWrite(ledPinBotao, HIGH);
// Serial.println("Botao Pressionado!"); // Descomentar para depuração
} else {
digitalWrite(ledPinBotao, LOW);
}

// --- Outras tarefas podem ser adicionadas aqui ---
// Por exemplo, ler um sensor, enviar dados, etc.
// Estas tarefas também executarão sem serem bloqueadas pelo piscar do LED.

} // Fim do loop() - ele repete imediatamente

Nesta versão:

O loop() corre muito rapidamente.
Em cada iteração, ele verifica se já passou 1 segundo desde a última mudança do ledPinPiscante. Se sim, muda o estado e atualiza previousMillis. Se não, continua imediatamente.
Em cada iteração, independentemente do estado do LED piscante, ele lê o botaoPin e atualiza o ledPinBotao instantaneamente.
A responsividade ao botão é agora excelente, e o LED continua a piscar corretamente.
Gerir Múltiplas Tarefas Temporizadas com millis()

A beleza de millis() é que pode gerir várias tarefas independentes, cada uma com o seu próprio intervalo e registo de tempo:

C++ 
// Variáveis Tarefa 1 (LED A cada 500ms)
unsigned long previousMillisA = 0;
const long intervalA = 500;
int ledStateA = LOW;

// Variáveis Tarefa 2 (Sensor a cada 2 segundos)
unsigned long previousMillisB = 0;
const long intervalB = 2000;

void loop() {
unsigned long currentMillis = millis(); // Obter tempo uma vez por loop

// --- Gerir Tarefa 1 ---
if (currentMillis - previousMillisA >= intervalA) {
previousMillisA = currentMillis;
// ... inverter ledStateA e digitalWrite ...
}

// --- Gerir Tarefa 2 ---
if (currentMillis - previousMillisB >= intervalB) {
previousMillisB = currentMillis;
// ... ler sensor e processar dados ...
}

// --- Outras tarefas não temporizadas ---
// ... ler botões, comunicar, etc. ...
}

Consideração Importante: O Rollover de millis()

A função millis() retorna um unsigned long. Este tipo de dados tem um limite máximo (aproximadamente 4.29 mil milhões). Como millis() conta milissegundos, este contador chegará ao máximo e voltará a zero (rollover) após cerca de 49.7 dias de funcionamento contínuo (2^32 / 1000 / 60 / 60 / 24 dias).

Felizmente, a forma como calculamos a diferença de tempo (currentMillis - previousMillis) usando aritmética de unsigned long lida com o rollover corretamente na maioria dos casos, desde que o intervalo (interval) seja significativamente menor que os 49.7 dias. A subtração de unsigned long funciona corretamente mesmo quando currentMillis é menor que previousMillis devido ao rollover. Não precisa de se preocupar com isto para a maioria dos projetos, mas é bom ter conhecimento.

Reação Instantânea: O Poder das Interrupções (Interrupts)
Enquanto millis() é excelente para tarefas temporizadas e verificação periódica, há situações onde precisamos de uma reação imediata a um evento externo, sem esperar pela próxima iteração do loop() para verificar. É aqui que entram as interrupções.

O que são Interrupções?

Uma interrupção é um mecanismo de hardware presente no microcontrolador. Permite que um evento específico (como a mudança de estado num pino, a conclusão de uma temporização interna, ou a receção de dados via serial) pause temporariamente a execução do código principal (loop()) e execute uma função especial chamada Rotina de Serviço de Interrupção (ISR - Interrupt Service Routine). Assim que a ISR termina, o código principal retoma exatamente de onde parou, sem perder nada.

Pense nisto como o toque do telefone: Você está a trabalhar na sua secretária (o loop()), e de repente o telefone toca (o evento de interrupção). Você para o que estava a fazer, atende o telefone (executa a ISR), resolve o assunto rapidamente e, em seguida, volta ao trabalho exatamente onde parou.

Quando Usar Interrupções?

Eventos Críticos e Urgentes: Detetar a pressão de um botão de emergência, ler um encoder rotativo para não perder pulsos, reagir a um sensor de limite num sistema mecânico.
Processamento de Sinais Rápidos: Capturar sinais que mudam mais rápido do que o loop() consegue verificar confiavelmente com millis().
Acordar o Microcontrolador: Interrupções podem ser usadas para tirar o microcontrolador de modos de baixo consumo (sleep modes).
Tipos Comuns de Interrupções no Arduino (Uno/Nano/Mega):

Interrupções Externas: As mais usadas para reagir a eventos em pinos específicos. No Arduino Uno (ATmega328P), os pinos 2 e 3 suportam interrupções externas diretas (INT0 e INT1).
Interrupções por Mudança de Pino (Pin Change Interrupts): Disponíveis em mais pinos, permitem detetar uma mudança de estado (HIGH para LOW ou LOW para HIGH) em qualquer um dos pinos dentro de um grupo. Requerem um pouco mais de código para identificar qual pino específico mudou.
Interrupções de Temporizador (Timer Interrupts): Geradas pelos temporizadores internos do microcontrolador em intervalos precisos. Úteis para tarefas que precisam de uma temporização muito exata, mais precisa do que millis() pode garantir.
Como Usar Interrupções Externas (Exemplo com Botão):

Definir a ISR: Crie uma função void que não recebe argumentos nem retorna nada. Esta será a sua ISR.C++ 
void minhaISR() {
// Código a executar quando a interrupção ocorrer
// MANTENHA ESTE CÓDIGO MUITO CURTO E RÁPIDO!
}

Configurar a Interrupção no setup(): Use a função attachInterrupt():C++ 
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);

digitalPinToInterrupt(pin): Converte o número do pino digital (e.g., 2 ou 3 no Uno) para o número da interrupção interna correspondente. Use esta função para portabilidade.
ISR: O nome da sua função ISR (e.g., minhaISR).
mode: Define quando a interrupção deve ser acionada:LOW: Quando o pino está em nível lógico baixo (LOW).
CHANGE: Quando o pino muda de estado (LOW para HIGH ou HIGH para LOW).
RISING: Quando o pino passa de LOW para HIGH.
FALLING: Quando o pino passa de HIGH para LOW (muito comum para botões com pull-up).
Exemplo: Acender LED Instantaneamente com Interrupção de Botão

C++ 
// Exemplo com Interrupção para Reação Imediata
const int ledPin = 12;
const int botaoPin = 2; // Pino de interrupção externa no Uno (INT0)

// Variável para comunicação entre ISR e loop()
// Precisa ser 'volatile' porque pode mudar a qualquer momento pela ISR
volatile bool botaoPressionadoFlag = false;

void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(botaoPin, INPUT_PULLUP); // Usar pull-up interno

// Configurar a interrupção no pino do botão
// Acionar a ISR 'botaoISR' quando o pino passar de HIGH para LOW (FALLING)
attachInterrupt(digitalPinToInterrupt(botaoPin), botaoISR, FALLING);
Serial.println("Sistema pronto. Pressione o botão.");
}

void loop() {
// O loop principal pode fazer outras coisas aqui...
// Por exemplo, piscar outro LED usando millis() ou comunicar.

// Verificar a flag que a ISR pode ter ativado
if (botaoPressionadoFlag) {
Serial.println("Botão detetado no loop!");
digitalWrite(ledPin, HIGH); // Acende o LED

// Poderia adicionar um delay aqui SE a ação fosse longa,
// mas é melhor usar millis() para temporizar o desligar do LED
// Exemplo: Ligar por 500ms (implementação com millis() seria melhor)
delay(500); // Apenas para exemplo simples
digitalWrite(ledPin, LOW);

botaoPressionadoFlag = false; // Resetar a flag para a próxima deteção
}

// ... Continua a fazer outras tarefas ...
delay(10); // Pequeno delay para não sobrecarregar o Serial Monitor no exemplo
}

// --- Rotina de Serviço de Interrupção (ISR) ---
// Esta função é chamada AUTOMATICAMENTE pelo hardware quando o pino 2 vai para LOW
void botaoISR() {
// Ações dentro da ISR devem ser EXTREMAMENTE rápidas!
// Geralmente, apenas definimos uma flag ou atualizamos um contador.
botaoPressionadoFlag = true;

// NÃO use delay() dentro de uma ISR!
// NÃO use Serial.print() ou outras funções demoradas/bloqueantes aqui!
}

Regras de Ouro para Escrever ISRs:

Mantenha-as Curtas e Rápidas: A ISR interrompe o seu código principal. Quanto mais tempo ela demorar, mais o seu programa principal fica parado. Execute apenas o mínimo essencial (e.g., definir uma flag, incrementar um contador, copiar um valor).
Declare Variáveis Partilhadas como volatile: Se uma variável é usada tanto na ISR quanto no loop() (ou outra função), declare-a com a palavra-chave volatile (e.g., volatile bool flag;). Isto diz ao compilador que o valor da variável pode mudar inesperadamente (pela ISR) e evita otimizações que poderiam causar erros.
Evite Funções Bloqueantes ou Lentas: NUNCA use delay() dentro de uma ISR. Evite também Serial.print() (pode ser lento e depende de outras interrupções), e operações complexas ou que dependam de tempo.
millis() e micros() dentro de ISRs: millis() pode não funcionar corretamente dentro de uma ISR porque ele próprio depende de interrupções (do temporizador) para atualizar o seu contador. Se precisar de temporização muito curta dentro de uma ISR (geralmente não recomendado), micros() pode ser mais fiável, mas use com extrema cautela.
Cuidado com Condições de Corrida (Race Conditions): Se a ISR e o loop() modificam a mesma variável multi-byte (e.g., int, long), pode ocorrer um problema onde a ISR interrompe o loop() no meio da atualização da variável. Para variáveis de um único byte (byte, bool, char), a leitura/escrita é geralmente atómica (não pode ser interrompida). Para tipos maiores, pode ser necessário desabilitar temporariamente as interrupções (noInterrupts()) antes de aceder à variável no loop() e reabilitá-las depois (interrupts()), mas faça isso pelo menor tempo possível.


Combinando millis() e Interrupções: O Melhor dos Dois Mundos


A verdadeira magia acontece quando combinamos as duas técnicas:

Use interrupções para detetar eventos imediatos e definir flags ou realizar ações muito rápidas.
Use millis() no loop() para:Executar tarefas temporizadas regulares (piscar LEDs, ler sensores periodicamente).
Verificar as flags definidas pelas ISRs e realizar ações mais complexas ou demoradas com base nessas flags.
Implementar "debouncing" de botões (ignorar ruído elétrico rápido após uma pressão inicial detetada pela interrupção).
Exemplo de Combinação:

C++ 
// Combinação: Interrupção para detetar botão, millis() para gerir LED
const int ledPin = 13;
const int botaoPin = 2;

volatile bool ledDeveLigar = false; // Flag definida pela ISR
unsigned long tempoLedLigado = 0; // Para controlar por quanto tempo fica ligado
const long duracaoLedLigado = 1000; // Ligar por 1 segundo

void setup() {
pinMode(ledPin, OUTPUT);
pinMode(botaoPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(botaoPin), botaoISR, FALLING);
}

void loop() {
unsigned long currentMillis = millis();

// --- Processar a flag da interrupção ---
if (ledDeveLigar) {
digitalWrite(ledPin, HIGH); // Liga o LED imediatamente
tempoLedLigado = currentMillis; // Regista quando foi ligado
ledDeveLigar = false; // Reseta a flag (já tratada)
}

// --- Gerir o tempo que o LED fica ligado (usando millis()) ---
// Se o LED está ligado (tempoLedLigado != 0) E já passou o tempo de duração
if (tempoLedLigado != 0 && currentMillis - tempoLedLigado >= duracaoLedLigado) {
digitalWrite(ledPin, LOW); // Desliga o LED
tempoLedLigado = 0; // Indica que o LED está desligado
}

// --- Outras tarefas podem correr aqui usando millis() ---
// Ex: piscar outro LED, ler um sensor a cada X segundos, etc.
}

// --- ISR ---
void botaoISR() {
// Apenas define a flag. O loop() fará o resto.
// Evita fazer digitalWrite ou usar millis() aqui.
if (digitalRead(ledPin) == LOW) { // Só aciona se o LED estiver desligado (evita re-trigger)
ledDeveLigar = true;
}
}

Neste exemplo:

A interrupção (botaoISR) é acionada imediatamente quando o botão é pressionado. Ela apenas define ledDeveLigar = true; (muito rápido).
O loop() corre continuamente. Quando ele vê que ledDeveLigar é true, ele liga o LED, regista o tempo (tempoLedLigado = currentMillis;) e reseta a flag.
O loop() continua a correr e verifica se o LED está ligado (tempoLedLigado != 0) e se já passou duracaoLedLigado milissegundos desde que foi ligado. Se sim, desliga o LED e reseta tempoLedLigado.
Tudo isto acontece sem bloquear a execução de outras possíveis tarefas no loop().
Estratégias Adicionais e Boas Práticas para Código Não-Bloqueante
Dominar millis() e interrupções é fundamental, mas existem outras técnicas e conceitos que complementam a criação de código Arduino responsivo:

Máquinas de Estado Finito (Finite State Machines - FSM): Uma forma poderosa de organizar código complexo, especialmente no loop(). Em vez de muitos if/else aninhados, o seu programa está sempre num determinado "estado" (e.g., "ESPERANDO_BOTAO", "AQUECENDO", "MONITORIZANDO"). As condições (baseadas em millis(), leituras de sensores, flags de ISRs) causam "transições" para outros estados, e em cada estado, ações específicas são executadas. Isto torna o código mais legível, modular e fácil de depurar.
Bibliotecas Não-Bloqueantes: Ao escolher bibliotecas para sensores, comunicação (WiFi, Ethernet, Bluetooth) ou outros periféricos, procure ativamente por aquelas que são projetadas para serem não-bloqueantes ou que oferecem métodos assíncronos. Muitas bibliotecas populares têm alternativas ou formas de uso que evitam delay().
Estruturação do Código: Divida o seu loop() em funções menores e mais focadas (e.g., gerirLedPiscante(), lerSensores(), verificarBotoes()). Isto melhora a organização e facilita a gestão da lógica não-bloqueante.
Debouncing de Botões: Mesmo com interrupções, botões mecânicos geram "ruído" elétrico (várias transições rápidas) ao serem pressionados ou soltos. A ISR pode ser acionada várias vezes. Uma técnica comum é usar a ISR para detetar a primeira transição (e.g., FALLING) e depois usar millis() no loop() para esperar um curto período (e.g., 20-50ms) antes de considerar a pressão válida, ignorando transições adicionais nesse período.
Conclusão: Liberte o Potencial do seu Arduino!
A função delay() pode parecer conveniente no início, mas é uma armadilha que limita drasticamente a capacidade e a responsividade dos seus projetos Arduino. Ao abraçar as alternativas – a versátil função millis() para tarefas temporizadas não-bloqueantes e o poderoso mecanismo de interrupções para reações instantâneas a eventos – você desbloqueia o verdadeiro potencial do microcontrolador.

Aprender a pensar de forma "não-bloqueante" é uma mudança de paradigma crucial na programação de sistemas embarcados. Exige um pouco mais de planeamento e estrutura, mas os resultados são imensamente recompensadores:

Projetos mais responsivos: Interfaces que reagem instantaneamente à interação do utilizador.
Maior eficiência: O microcontrolador está sempre pronto para processar novas informações ou eventos.
Capacidade de multitarefa (simulada): Gerir múltiplas ações e sensores concorrentemente.
Código mais robusto e escalável: Projetos mais fáceis de expandir e manter.
Comece hoje mesmo a refatorar os seus projetos existentes, substituindo delay() por millis(). Experimente usar interrupções para aqueles eventos que exigem atenção imediata. Explore máquinas de estado para organizar lógicas mais complexas. A jornada para dominar estas técnicas fará de si um programador Arduino muito mais capaz e permitirá criar projetos verdadeiramente impressionantes e interativos. Não deixe que o delay() limite a sua criatividade – liberte o poder do seu Arduino!


Calculadora online de Resistência (4 Faixas)

Valor: --

Tolerância: --

Gama: --