Arduino 11: Música com bibliotecas

Neste experimento usaremos uma mini-biblioteca (arquivo .h) de notas musicais para fazer o Arduino executar uma música.

Experimento 32 – Usando bibliotecas para produzir notas musicais

Material necessário

  • Arduino Nano + cabo USB + computador com conexão à internet
  • Alto-falante
  • Protoboard, fios e jumpers

O comando tone() gera pulsos de onda quadrada na frequência passada como parâmetro. Por exemplo, pra produzir um lá central usado para afinação, podemos mandar para um pino PWM que esteja ligado a um alto-falante um comando:

tone(440);

Para tocar uma música precisamos saber a frequência de cada nota, mas o ideal seria poder simplesmente chamar as notas pelo nome.

Existe um arquivo .h para isto. Ele atribui nomes de variáveis para cada uma das 88 notas do piano, guardando a sua frequência. É um programa de domínio público que faz parte dos exemplos da IDE do Arduino. Este é um trecho:

...
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
...

Ou seja, usando-o podemos mandar comandos:

tone(NOTE_A4);

e fazer soar um lá sem precisar lembrar da sua frequência.

NOTE_C3 corresponde ao terceiro Dó do piano. NOTE_A4 ao quarto lá, e assim por diante. O comando noTone(PINO) desliga o sinal no pino (aplica valor LOW, 0V).

Criaremos o arquivo .h localmente. Abra um sketch novo, e em seguida abra um tab (como mostrado acima no exemplo sobre arquivos .h), e cole nele conteúdo do arquivo abaixo:

https://www.arduino.cc/en/Tutorial/ToneMelody?action=sourceblock&num=2

Grave o arquivo como notas.h.

Vamos alterar o nome das variáveis para que fiquem mais curtos e seja mais fácil digitar notas. Com o tab notas.h aberto, digite Control-F (Command-F no Mac) ou selecione o item de menu Edit/Find. Abrirá a janela abaixo.

No campo “Find:” digite NOTE_ e clique no botão Replace All. Isto removerá o prefixo NOTE_ de todas as notas. O resultado deve ficar da forma abaixo:

#define B0 31
#define C1 33
#define CS1 35
#define D1 37
#define DS1 39
#define E1 41
...

Agora acrescente mais uma definição, em uma linha antes de B0:

#define ZZ 0

Esta será a nota de pausa (frequência zero) que não produzirá som algum.

Digite o programa abaixo. Ele foi adaptado dos exemplos do Arduino (“toneMelody” criado por Tom Igoe). Ele usa essa biblioteca de notas musicais para tocar uma pequena música. Nesta versão tocamos uma música diferente. As notas da música estão dentro do bloco melodia[], e os tempos correspondentes de cada nota em durações[]:

#include "notas.h"

int ALTO_FALANTE = 5; // coloque alto-falante ou buzzer entre pino 5 e GND
int NUM_NOTAS = 44;   // número de notas incluídas em melodia[]

int melodia[] = {
    G4, G4, G4, DS4, AS4, G4, DS4, AS4, G4,
    D5, D5, D5, DS5, AS4, FS4, DS4, AS5, G4,
    G5, G4, G4, G5, FS5, F5, E5, DS5, E5, ZZ,
    GS4, CS5, C5, B5, AS5, A5, AS5, ZZ,
    DS4, FS4, DS4, AS4, G4, DS4, AS5, G4};

// duração: 1 = semibreve, 2 = mínima, 4 = seminima, 8 = colcheia, etc.:
int duracoes[] = {
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 6, 8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 4, 6, 8, 2
};

void setup() {
    for (int nota = 0; nota < NUM_NOTAS; nota++) {
        int duracao = 1000 / duracoes[nota]; // divide 1000 por cada duração
        tone(ALTO_FALANTE, melodia[nota], duracao);
        int pausa = duracao * 1.30;
        delay(pausa);
        noTone(ALTO_FALANTE);
    }
}
void loop() {}

O programa roda apenas uma vez no bloco setup(). Se quiser que ela toque sem parar, você pode transferir todo o código para o bloco loop(), incluindo um delay(1000) no final para que haja um intervalo entre as execuções.

O programa usa um bloco de repetição for() para executar cada nota (será explicado mais adiante.)

Construa o circuito abaixo (que talvez seja o circuito mais simples criado no curso), ligando um dos fios do alto-falante no pino 5, e o outro terminal em GND.

Logo após a transferência, a música começará a tocar. Experimente criar outras músicas usando essa biblioteca.

O código-fonte deste experimento (o sketch e arquivo notas.h já alterado) também está disponível para download em repositório GitHub (veja link na introdução da apostila).

Listas

O código do programa anterior possui várias estruturas que ainda não vimos. Uma delas é a lista (também chamado de array, ou vetor). Há duas listas no código do experimento anterior, representadas pelas variáveis durações[] e melodia[]. Listas são declaradas com seus itens entre chaves, separados por vírgula. O tipo de dados se refere ao tipo de cada elemento da lista:

float precos = {23.56, 9.99, 11.99, 25.49};

Cada item da lista é referenciado por um numero que é seu índice. A contagem começa em zero, portanto, na lista acima, os índices são 0, 1, 2 e 3. Eles são usados para referenciar itens da lista. Por exemplo, precos[1] é 9.99 e precos[3] é 25.49.

As duas listas do programa do experimento anterior têm 8 elementos, e seus índices variam de 0 a 7.

Repetição com for

O bloco for é uma estrutura de repetição. Consiste de uma declaração entre parênteses e um conteúdo (a ser repetido) entre chaves:

for( declaração ) { /* conteúdo a ser repetido */ }

A declaração tem três partes (separadas por ponto e vírgula):

  • O valor inicial de uma variável
  • A condição envolvendo a variável que deve ser verdadeira enquanto o bloco é repetido
  • Uma instrução para mudar o valor da a variável

Blocos de repetição for() são frequentemente usados com listas. Para imprimir no monitor serial cada um dos itens da lista precos (de 4 elementos) acima, pode-se usar o seguinte for():

for(int i = 0; i < 4; i = i + 1) {
    Serial.println( precos[i] );
}

A cada passada no for(), o valor de i será diferente. Iniciará em zero, e será 1, depois 2, depois 3. Na próxima passada i é 4, a condição se tornará falsa, e o bloco será encerrado.

No código do experimento anterior, o for() incrementa o índice (variável nota) usando nota++. Isto é o mesmo que fazer:

nota = nota + 1;

O for() executa apenas uma vez a primeira parte da declaração (inicialização da variável que controla a repetição), mas a cada repetição a condição da segunda parte é testada, o bloco entre chaves é executado, e por fim a expressão da terceira parte é executada, nesta ordem.

É importante que o valor da variável mude de forma que faça o for() terminar em algum momento (a menos que você queira um loop infinito – mas para isto você já tem a função loop() que é mais simples). Normalmente as expressões usadas na terceira parte do for() são apenas para incrementar (somar um) ou decrementar (subtrair um) da variável de controle.

Arduino 10: funções, listas, bibliotecas

Com o que vimos de programação do Arduino até aqui, que foi basicamente como usar suas entradas analógicas e digitais, já podemos construir todos os circuitos que vimos nas seções anteriores usando transistores e circuitos integrados 555, de forma muito mais simples e sem precisar calcular circuitos RC (resistor-capacitor), e nem mesmo medir tensões e correntes. Não será sempre assim. O conhecimento de eletrônica básica ainda será importante para fazer projetos mais interessantes, porém o Arduino de fato simplifica o uso da eletrônica.

Esta é uma seção opcional. Aqui exploramos alguns tópicos mais avançados de programação com o Arduino que permitirão que você use programas escritos por outras pessoas e faça alterações neles sem necessariamente entender tudo o que fazem. Se você nunca programou antes os conceitos podem parecer complexos. Mas não é preciso entender tudo para fazer os experimentos. Monte os circuitos (que são muito simples) e copie os códigos. Você pode deixar para ler a teoria depois, quando já tiver mais familiaridade com programas em Arduino.

Declarando funções

Os comandos digitalWrite, analogRead, etc. que chamamos dentro dos blocos setup() e loop() são chamadas de funções. Elas foram definidas nas bibliotecas do Arduino. Essas bibliotecas são automaticamente incluídas em todos os programas.

Uma função, portanto, para que possa ser chamada, precisa ser definida em algum lugar. A chamada poderá ser feita dentro do bloco loop() ou setup(). A definição de novas funções poderá ser feita no próprio sketch, ao lado dos blocos loop() e setup() que são definições de funções (chamadas automaticamente pelo Arduino).

Portanto, para definir uma função, escolha qualquer lugar antes ou depois dos blocos loop() e setup(), e crie um novo bloco com a estrutura abaixo:

void nomeDaFuncao() {
    // coloque aqui as instruções que sua função irá executar quando chamada
}

Vejamos um exemplo. Abaixo definimos uma função chamada piscar():

void piscar() {
    digitalWrite(8, HIGH);
    delay(500);
    digitalWrite(8, LOW);
    delay(500);
}

Ela tem o mesmo código dentro de loop() do circuito que pisca o LED. Agora podemos chamar a função que acabamos de definir como se fosse um comando (terminando em ponto-e-vírgula):

void loop() {
    piscar();   // chama a função piscar()
}

O código acima funciona igual ao código original que pisca o LED. A vantagem de definir funções é que podemos chamá-las várias vezes, sem precisar repetir o código que ela contém. Isto é mais fácil de perceber se definirmos funções com parâmetros.

Quando definimos uma função, os parâmetros são declarados como variáveis. Como toda variável, o cada parâmetro tem um tipo de dados que faz parte da declaração. Essas variáveis declaradas irão receber valores quando a função for chamada, e podem usar esses valores dentro da definição da função. No exemplo abaixo definimos uma função piscar() contendo dois parâmetros inteiros:

void piscar(int pino, int tempo) {
    digitalWrite(pino, HIGH);
    delay(tempo);
    digitalWrite(pino, LOW);
    delay(tempo);
}

Observe que as variáveis são usadas pelos comandos que estão dentro da função. Para chamar a função acima precisamos passar para ela dois parâmetros inteiros. O valor do primeiro parâmetro será copiado (atribuído) à variável pino, e o valor do segundo será copiado à variável tempo. Por exemplo, veja a chamada abaixo dentro de loop():

void loop() {
    piscar(8, 500);
}

Isto irá copiar o valor 8 para pino, e o valor 500 para tempo. E dentro da função, esses valores serão novamente copiados para funções do Arduino (digitalWrite e delay). Qual a vantagem disso? Agora podemos chamar a mesma função várias vezes, alterando os parâmetros que passamos para ela. Por exemplo, podemos fazer um loop() que pisca LEDs em pinos diferentes e em tempos diferentes sem precisar escrever 12 linhas de código:

void loop() {
    piscar(8, 500);
    piscar(9, 250);
    piscar(8, 1000);
}

Experimento 31 (extra) – Definindo funções para controlar um LED RGB

Material necessário:

  • Arduino Nano + cabo USB + computador
  • LED RGB de anodo comum (ou três LEDs: um vermelho, um verde e um azul)
  • Resistor de 220 ohms
  • Protoboard, fios e jumpers

O programa abaixo define uma função cor(r,g,b) que permite declarar cores usando comandos RGB que são traduzidos em cores de um LED RGB cujos terminais estão conectados a saídas PWM do Arduino. O circuito pode ser usado com 3 LEDs ou um LED RGB. Como o Led RGB usado tem o anodo comum, ele foi ligado em 5V e analogWrite(pino, 0) provocará o brilho máximo, já que a diferença de potencial nesse valor é máxima. Para compensar isto sem modificar o circuito podemos subtrair 255 do valor de cada componente de cor. Isto é feito dentro da função cor();

#define LED_VERMELHO 9
#define LED_VERDE 10
#define LED_AZUL 11

void setup() {}

void cor(int r, int g, int b) {
    analogWrite(LED_VERMELHO, 255 - r);
    analogWrite(LED_VERDE, 255 - g);
    analogWrite(LED_AZUL, 255 - b);
}

void acender(int r, int g, int b, int intervalo) {
    cor(r, g, b);
    delay(intervalo);
}

void loop() {
    acender(255,0,0,1000);   // vermelho
    acender(0,255,0,1000);    // verde
    acender(0,0,255,1000);    // azul
    acender(190,255,0,1000);  // amarelo
    acender(190,0,255,1000);  // magenta

    acender(0,255,255,1000);  // ciano
    acender(255,255,255,1000); // branco
    acender(0,0,0,1000);      // apagado

    acender(63,0,0,1000); // vermelho com 1/4 da intensidade
    acender(0,63,0,1000); // verde com 1/4 da intensidade
    acender(0,0,63,1000); // azul com 1/4 da intensidade
}

Além da função cor(), o programa acima também define uma função acender(r,g,b,duracao), que estabelece a cor (chamando a função cor()) e o tempo em que ela ficará acesa. O loop() chama várias vezes a função acender() com diferentes parâmetros, para que o circuito exiba uma sequência de cores diferentes, mantendo cada uma acesa por um segundo.

Bibliotecas e arquivos .h (arquivos de cabeçalho)

Suponha que você pretenda usar LEDs RGB em vários circuitos diferentes e queira reusar as funções criadas no experimento anterior. Uma maneira de fazer isto é recortar e colar o texto no outro programa. Mas há alternativas melhores. Uma delas é armazená-las em um arquivo separado que possa ser incluído em outros programas: um arquivo de cabeçalho, que tem a extensão .h

Para criar um arquivo de cabeçalho no IDE do Arduino, clique no menu que aparece logo abaixo do ícone do monitor serial, e depois selecione “New Tab”:

Depois escolha um nome para o arquivo. Por exemplo, “ledsrgb.h”, e grave-o.

Agora vamos transferir as funções cor() e acender() para o arquivo ledsrgb.h. Recorte-as do sketch, e cole as duas funções abaixo no arquivo .h:

void cor(int r, int g, int b) {
    analogWrite(LED_VERMELHO, 255 - r);
    analogWrite(LED_VERDE, 255 - g);
    analogWrite(LED_AZUL, 255 - b);
}

void acender(int r, int g, int b, int intervalo) {
    cor(r, g, b);
    delay(intervalo);
}

Como a função cor() depende de variáveis que ainda estão no arquivo original, precisamos redefini-las localmente (no arquivo .h) e depois copiar os valores. Acrescente o seguinte código:

int pino_R; // foram declaradas sem valor inicial
int pino_G;
int pino_B;

void configurarRGB(int pr, int pg, int pb) {
    pino_R = pr; // o valor inicial passado quando esta função for chamada
    pino_G = pg;
    pino_B = pb;
}

E altere a função cor() para que ela use os novos valores:

void cor(int r, int g, int b) {
    analogWrite(pino_R, 255 - r);
    analogWrite(pino_G, 255 - g);
    analogWrite(pino_B, 255 - b);
}

A função configurarRGB() será chamada a partir do sketch original, para passar os valores dos pinos ao arquivo .h. Para incluir o arquivo .h no seu sketch, use uma expressão #include:

#include "piscaled.h"

(observe que ela não tem ponto e vírgula no final – é uma diretiva como #define).

Dentro do setup() do seu sketch, chame a função configurarRGB() passando as variáveis que contém os números dos pinos. Desta forma, os valores serão copiados para as variáveis do arquivo .h:

void setup() {
    configurarRGB(LED_VERMELHO, LED_VERDE, LED_AZUL);
}

Agora é possível rodar o programa alterado, que consiste de dois arquivos, e ele funcionará igual, mesmo não contendo a definição de acender(). Se você quiser usar piscaled.h em outro sketch, precisa apenas copiá-lo para a pasta onde está o arquivo .ino do sketch, declarar o #include e chamar o configurarRGB(). Depois disso você pode chamar as funções cor() e acender() como se elas tivessem sido definidas localmente.

Se você achar mais fácil, pode baixar o projeto contendo os dois arquivos usados neste exemplo a partir do repositório GitHub disponível na Internet. Veja o link na introdução da apostila.

Arduino 9: Usando entradas e saídas analógicas

Variando a tensão média aplicada no motor podemos fazê-lo acelerar ou desacelerar. Já fizemos isto usando o circuito integrado 555. Com Arduino o circuito é bem mais simples.

Experimento 30 – Acelerando e desacelerando o motor com luz

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Motor de 3V
  • 1 diodo de propósito geral (1N4148 ou equivalente)
  • 2 resistores de 10k ohms
  • LDR
  • 1 transistor MOSFET IRL540
  • Protoboard, fios e jumpers

Monte o circuito abaixo:

O circuito requer uma entrada analógica que irá selecionar a velocidade desejada para o motor, e uma saída, também analógica (PWM), para variar a intensidade da tensão aplicada no motor e assim fazê-lo girar mais rápido ou mais devagar.

A leitura dos dados de entrada para controlar a velocidade do motor poderia ser feita variando um potenciômetro, mas é bem mais interessante usar um sensor de intensidade luminosa como o LDR. O circuito pode ser usado para construir um carrinho que anda quando o ambiente está iluminado, e que para quando entra em um ambiente escuro.

Como um motor pode demandar muito mais corrente que o pino do Arduino é capaz de fornecer, precisamos isolá-lo. Uma forma de fazer isto é através de um transistor. Usaremos neste exemplo um transistor MOSFET de potência, que consome pouca energia mas suporta bem mais corrente direta que o motor será capaz de exigir. Motores também causam pulsos reversos de corrente quando o motor liga ou desliga. Protegeremos o circuito desses pulsos com um diodo em paralelo com o motor.

Este MOSFET que usamos só irá conduzir (entre os seus terminais D e S) quando a tensão no terminal de controle (terminal G) tiver mais de 4,5V. Como usamos PWM, o pino 3 irá gerar pulsos de onda quadrada em valores absolutos de 0 ou 5V (os valores intermediários são simulados através da largura dos pulsos, ou seja, através de PWM). Assim o MOSFET irá ligar e desligar muito rapidamente. Quando o tempo ligado aumentar, o motor irá acelerar. Quando o tempo desligado aumentar, o motor irá desacelerar.

O programa é simples. Consiste na leitura do valor do LDR (0 a 1023) dividido por quatro para que possa ser enviado para a saída PWM (0 a 255):

#define PINO_MOTOR 3;
#define PINO_LDR A0;

void setup() {
    pinMode(PINO_MOTOR, OUTPUT);
}

void loop() {
    int velocidade = analogRead(PINO_LDR) / 4;
    analogWrite(PINO_MOTOR, velocidade);
}

Alteração 30.1 – Usando uma fonte externa para alimentar o motor

Este experimento funciona adequadamente com o motor distribuído no kit, que consome muito pouca corrente, mas se você usar um outro motor ele poderá exigir demais do Arduino. Normalmente um motor não deve ser alimentado com a fonte interna de 5V do Arduino. Ele deve usar uma fonte externa (a fonte pode até ser a mesma usada pelo Arduino, se o motor suportar). Isto não impede que o Arduino continue a controlá-lo. O circuito abaixo ilustra esta configuração (o Arduino pode ser alimentado via USB ou pelo pino Vin):

Arduino 8: Monitor serial

O monitor serial é uma tela que aparece no computador quando o Arduino está conectado via USB e que permite imprimir texto enviado pelo programa. Ele pode ser usado para depurar programas, ou exibir dados lidos por sensores.

Para usar o monitor serial, é preciso declarar dentro do bloco setup() a taxa de leitura. Para depuração simples use o comando:

Serial.begin(9600);

Dentro de loop(), quando quiser imprimir algo no terminal use Serial.print() (que imprime texto ou valor de uma variável) ou Serial.println() (que imprime uma nova linha no final). Para imprimir texto, ele deve ser informado entre aspas. Por exemplo, considere o trecho abaixo:

void loop() {
    int estado = digitalRead(9);
    Serial.print("Estado do botão: ");
    Serial.println(estado);
}

O monitor serial pode ser aberto através do menu Ferramentas (ou Tools) do IDE Arduino, ou. Rodando o programa acima, ele irá imprimir “Estado do botão: 0” se o botão estiver no estado LOW, ou “Estado do botão: 1” se ele estiver no estado HIGH.

É mais interessante usar o monitor serial para ler dados gerados por dispositivos analógicos. Faremos isto no experimento a seguir.

Experimento 29 – Termômetro

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Circuito termômetro LM35DZ (veja referência no final da apostila)
  • Protoboard, fios e jumpers

Neste experimento conectamos um componente LM35 ao Arduino para obter a temperatura do ambiente e imprimir o seu valor no monitor serial.

O LM35 é um termômetro de precisão. Ele tem a mesma embalagem (TO-92) que um transistor BC549. Uma vez alimentado com uma tensão entre 5 e 15 V nos seus terminais externos, o terminal central apresentará uma tensão relativa ao terminal negativo proporcional à temperatura ambiente. Aos 25 graus Celsius essa tensão medirá 0,25V, e varia 0,01 volts para cada grau acima ou abaixo com margem de erro de 0,5 graus dentro da faixa 2 a 100 graus Celsius.

Veja a pinagem do LM35 na referência ao final da apostila. Ligue o seu pino VCC no pino 5V do Arduino, e o GND do LM35 em qualquer um dos dois GND do Arduino. O pino central OUT fornecerá a medida da tensão e deve ser ligado a qualquer uma das entradas analógicas (A0 a A7) do Arduino.

No programa abaixo ligamos terminal OUT do LM35 na entrada analógica A1.

#define TERMOMETRO A1

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

void loop() {
    int leitura = analogRead(TERMOMETRO);
    float volts = (leitura / 1024.0) * 5.0;
    float celsius = (volts) * 100.0;
    Serial.print("Temperatura: ");
    Serial.println(celsius);
    delay(2000);
}

A leitura analógica do Arduino varia de 0 (0 volts) a 1024 (5 volts). Portanto é preciso dividir por 1024 e multiplicar por 5 para obter o valor real em volts que está no terminal OUT do LM35. A leitura em graus Célsius será este valor multiplicado por 100.

Neste programa a temperatura é impressa a cada 2 segundos no monitor serial. O circuito funciona apenas conectado ao computador. Para tornar o termômetro independente do computador teríamos que elaborar um circuito de saída capaz de indicar a temperatura. Poderia ser um par de displays de 7 segmentos, um display de cristal líquido, uma série de leds, etc.

O próximo experimento combinará o uso de entradas e saídas analógicas para controlar a velocidade de um motor.

Arduino 7: Entrada analógica

As entradas analógicas podem ser usadas para ler valores produzidos por sensores e potenciômetros. A instrução é analogRead(Número-do-pino). O valor lido é de 0 a 1023 (correspondente a valores intermediários de tensão entre 0 e 5V). Os pinos de entrada analógica são A0 – A7. Eles podem ser identificados no programa com ou sem prefixo A (ex: pode-se usar A0 ou simplesmente 0):

int valor = analogRead(3); // lê do pino A3

É uma boa prática usar sempre o prefixo A ao declarar pinos analógicos, já que deixa o código mais legível e fácil de entender, evitando confusão com pinos digitais.

Os pinos de entrada analógica não podem ser usados para saída analógica. Eles não suportam PWM. Mas os seis primeiros (A0 a A5) podem ser usados para saída digital (digitalWrite), se necessário. Neste caso eles podem ser identificados com o nome analógico prefixado (A0, A1, etc.) ou com os números 14 a 19 (que correspondem aos pinos A0 a A5, respectivamente). Ou seja,

digitalWrite(A5, HIGH);

é o mesmo que

digitalWrite(19, HIGH);

Experimento 28 – “Theremin” com LDR e potenciômetro

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Potenciômetro de qualquer valor (ex: 10k, 100k)
  • Resistor de 1k ohms
  • LDR
  • Alto-falante
  • Protoboard, fios e jumpers

O circuito abaixo usa duas entradas analógicas. Uma para obter a leitura de luz, e a outra para obter a posição do potenciômetro que será usada para especificar um intervalo de tempo. A quantidade de luz no LDR irá variar a tensão sobre o resistor de 1k (e consequentemente no pino A0). O mesmo ocorre no pino A1, que obtém seu valor do divisor de tensão formado pelo potenciômetro.

A saída PWM varia de 0 a 255, portanto em um circuito perfeitamente calibrado, dividiríamos o valor lido em qualquer uma das entradas analógica por 4 (1024/4 = 256) para usá-lo diretamente na saída. 

No programa abaixo dividimos por 4 o valor lido no potenciômetro. Mas a tensão no pino A1 que varia com a luz aplicada ao LDR tem valor indefinido (que provavelmente nunca será zero ou 1023).

#define FTE 12
#define LDR A0
#define POT A1

void setup() {} // pinMode é opcional com analogRead

void loop() {
    int luz   = analogRead(LDR);
    int pausa = analogRead(POT);
    delay(pausa * .25);
    int tom = luz * luz * luz / 16; // experimente outros valores
    tone(FTE, tom);
}

O comando tone() gera uma onda quadrada em frequência especificada como parâmetro. As frequências suportadas (até 65kHz) incluem frequências audíveis (20Hz a 20kHz). O valor proporcional ao cubo do valor lido pelo LDR permite que a frequência varie bastante alternando entre sons agudos e graves.

Arduino 6: PWM

O Arduino Nano não produz sinal analógico verdadeiro, mas apenas os simula através de PWM (Pulse Width Modulation – veja capítulo anterior) através dos pinos digitais 3, 5, 6, 9, 10 e 11. PWM permite simular valores médios de tensão que variam entre os níveis lógicos LOW e HIGH. Esses valores são representados por número entre 0 a 255 retornado pela instrução analogWrite().

analogWrite(5, 64); // envia 5V 25% do tempo 5V para a saída digital 5

Experimento 27 – Piscando suavemente

Material necessário:

  • Arduino Nano + cabo USB + computador
  • LED
  • Resistor de 220 ohms
  • Protoboard, fios e jumpers

Este é uma variação do primeiro experimento que pisca um LED. O circuito é praticamente o mesmo, mas como o pino 8 usado no programa anterior não suporta PWM, transferimos o LED para o pino digital 3 (que é um dos seis pinos que suporta saída analógica).

O programa também mudou. Em vez de piscar em dois estados digitais, este pisca suavemente, variando entre o totalmente aceso e totalmente apagado.

int LED = 3;
int brilho = 255; // inicia com brilho máximo
int direcao = -1; // 1 = aumentando, -1 = diminuindo

void setup() {
    pinMode(LED, OUTPUT); // opcional com analogWrite (mas é boa prática)
}
void loop() {
    analogWrite(LED, brilho);
    delay(2);
    brilho = brilho + direcao;
    if(brilho <= 0 || brilho >= 255) {
        direcao = -direcao;
    }
}

O programa define duas variáveis (além do pino): brilho, que guarda o valor do brilho do LED (entre 0 e 255) e direcao, que contém o número 1 ou -1, que será somado ao brilho cada vez que loop() for executado, fazendo com que o brilho aumente ou diminua lentamente.

Assim que loop() inicia, o pino onde está o LED recebe o valor analógico 255 (correspondente a 5V). Depois espera 10 milissegundos e executa a instrução

brilho = brilho + direcao;

Uma expressão de atribuição primeiro executa a expressão do lado direito do “=”, substituindo as variáveis por seus valores. Portanto, a expressão executada será calculada da seguinte forma:

brilho = 255 + (-1)

que irá gravar o valor 254 na variável brilho.

No bloco seguinte temos uma expressão condicional if que testa se brilho é maior ou igual a zero OU se é maior ou igual a 255. O símbolo || conecta duas expressões através de uma proposição lógica OU. Isto significa que a condição do if será verdadeira se uma ou ambas as expressões forem verdadeiras. A condição é testada para os valores limite 0 e 255. Quando o valor de brilho chegar a um desses valores, o sinal da variável direcao é trocado (se era 1, passa a ser -1; se era -1 passa a ser 1). Assim o brilho que estava diminuindo, passa a gradualmente aumentar, e vice-versa.

Arduino 5: Resistores de pull-up

Há situações em que um programa precisa saber o estado da chave também quando ela não estiver sendo apertada. Nesses casos, é preciso conectar um resistor de pull-up ou pull-down entre o pino e o estado desejado (oposto ao estado quando a chave estiver acionada) para que o estado inicial seja definido. Este resistor liga o pino a um estado inicial. Se o pino for depois ligado diretamente ou através de uma resistência menor, a 5V ou GND, haverá um caminho mais curto para a corrente e seu estado será invertido.

Ligar o pino a 5V ou GND é irrelevante, pois o programa poderá escolher o que fazer em cada caso. Por exemplo, se escolhermos ligar uma chave a 5V, o valor no pino será HIGH quando a chave for pressionada, mas indefinido quando ela estiver aberta (veja ilustração abaixo). Usando um resistor de pull-down (tipicamente de 10k) ligando o pino inicialmente a GND, garantirá ao pino um estado inicial LOW que mudará para HIGH quando a chave for pressionada.

Se usarmos a lógica oposta (conectar com GND/LOW em vez de 5V/HIGH) não precisaremos dos resistores, pois o Arduino possui internamente resistores de pull-up (ligados em 5V) para cada pino. Esta opção pode ser ativado configurando o pinMode() com INPUT_PULLUP:

pinMode(CHAVE, INPUT_PULLUP);

Nessa configuração, o pino terá sempre como estado inicial o nível lógico HIGH. No fechamento da chave, o estado mudará para o nível lógico oposto (LOW). O diagrama abaixo ilustra as duas formas de configurar entradas digitais em Arduino:

Experimento 26 – Entrada com resistores pull-up

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Um LED de qualquer cor
  • Resistor de 220 Ω
  • 2 chaves tácteis de pressão
  • Protoboard, fios e jumpers

 

Construa o circuito abaixo. Ele apenas acrescenta mais uma chave ao circuito anterior.

Usaremos um programa que depende dos dois estados (ligado e desligado) de cada chave para decidir quando acender o LED.

#define PINO_LIGAR   3
#define PINO_DESLIGAR 4
#define LED 8

void setup() {
    pinMode(LED, OUTPUT);
    pinMode(PINO_LIGAR, INPUT_PULLUP);
    pinMode(PINO_DESLIGAR, INPUT_PULLUP);
}

void loop() {
    int acender = digitalRead(PINO_LIGAR);
    int apagar = digitalRead(PINO_DESLIGAR);

    if(acender == LOW) {
        digitalWrite(LED, HIGH);
    }
    if(apagar == LOW) {
        digitalWrite(LED, LOW);
    }
}

O loop() é executado repetidas vezes e cada vez: 1) o estado de cada pino é lido, e 2) dois blocos condicionais if são executados. Se a condição testada for verdadeira, o conteúdo é executado. Se não for, o conteúdo é ignorado.

Os blocos condicionais apenas testam se cada botão está em estado LOW, se não estiver, eles são ignorados. Mas os botões raramente estão no estado LOW. Isto acontece apenas quando forem apertados. O estado normal de cada botão é HIGH, já que estão conectados via pull-up. Se por uma fração de segundo você apertar qualquer um dos botões, seu estado será momentaneamente LOW e o bloco será executado. O primeiro if acende o LED, o segundo if apaga. Enquanto nenhum botão está pressionado, o estado anterior é mantido.

O circuito, portanto, tem uma memória que representa o estado do último botão apertado no acendimento ou apagamento do LED. Funciona como um alternador de estado (faz o mesmo que o 555 bi-estável do capítulo anterior, mas sem precisar calcular capacitores nem resistores). Se você trocar a chave por um sensor que baixe a tensão no pino 3 a um nível abaixo de 3V, você pode fazer o LED acender com um evento externo, por exemplo, apagar ou acender uma luz, ou bater palmas. O botão em D4 seria usado apenas para apagar o LED. Isto é proposto nas alterações abaixo.

Alteração 26.1 – Substituindo uma chave por um sensor de luz

Material adicional:

  • Um fototransistor (TIL 78) ou LDR

Troque a chave em D3 por um LDR ou um fototransistor (esquema abaixo), que se comporta como uma chave fechada quando recebe luz. Um pulso curto de luz é suficiente para acender o LED.

Alteração 26.2 – Substituindo uma chave por um sensor de som

Material adicional:

  • 1 microfone de eletreto
  • 1 transistor BC549 ou equivalente
  • 1 capacitor de 1µF
  • 1 resistor de 2k2 Ω

O circuito sensor de som abaixo irá gerar pulsos altos e baixos. Um pulso que faça o transistor conduzir por um instante fará o LED acender. O microfone pode ser alimentado através da saída 5V.

Arduino 4: entrada digital

Em um circuito Arduino, chaves não são usadas para ligar ou desligar um componente diretamente (como em circuitos eletrônicos tradicionais) mas para fornecer um dado de entrada para o programa, que poderá usá-lo para tomar decisões (a decisão pode ser inclusive para ligar ou desligar o componente.) Portanto, uma chave deve ser ligada diretamente a um pino digital de entrada.

Não é preciso chamar a instrução pinMode() no setup para configurar entradas (se for usada, deve declarar a funcionalidade do pino como INPUT.) Nesse modo, quando a chave estiver fechada ela deverá fornecer ou 5V ou 0V para o pino. Quando aberta, o estado do pino é indefinido.

Experimento 25 – Reagindo ao acionamento de chaves liga-desliga

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Um LED de qualquer cor
  • Resistor de 220 Ω
  • 1 chave táctil de pressão
  • Protoboard, fios e jumpers

Monte o circuito abaixo (é o mesmo circuito do experimento anterior, acrescentando a chave):

Ligamos uma chave de pressão (normalmente aberta) entre o pino 3 e GND. Quando ela não estiver pressionada, não haverá sinal algum no pino 3 (estado indefinido), mas quando ela estiver apertada, ela fará a conexão entre o pino D3 e GND e seu estado será LOW. Usamos uma expressão condicional para testar o estado do pino, com a seguinte regra: se o estado do pino 3 for LOW, o LED será aceso, caso controário (se for qualquer outro estado – indefinido ou HIGH), o LED será apagado:

#define CHAVE 3
#define LED   8

void setup() {
    pinMode(LED, OUTPUT);
}

void loop() {
    int estado = digitalRead(CHAVE);
    delay(10); // espera 10 milissegundos antes de testar
    if(estado == LOW) {
        digitalWrite(LED, HIGH);
    } else {
        digitalWrite(LED, LOW);
    }
}

O loop repete continuamente lendo o estado da chave e usando o estado lido para comparar com o valor LOW (0V).

Se a chave estiver aberta, o estado não é LOW. É indefinido, portanto, o conteúdo do bloco if é ignorado, mas o bloco else é executado. A instrução dentro do else mantém o LED apagado, já que fornece 0V (LOW) para a saída 8.

Se a chave estiver apertada, ela faz uma ligação direta entre o pino 3 e GND, fazendo-o ter o estado LOW. Neste caso, a instrução executada muda o estado do pino 8 para HIGH, e o LED acende. Soltando o botão, ele volta ao estado indefinido, e em pouco mais de 10 milissegundos, o estado do pino 3 será testado de novo, desta vez apagando o LED. Portanto, o LED só acende enquanto o botão estiver apertado.

Entrada digital

A instrução digitalRead(número-do-pino) serve para ler o nível lógico de um pino de entrada. O valor pode ser ALTO (HIGH) ou BAIXO (LOW). Normalmente HIGH corresponde a 5V e LOW corresponde a 0V (mas na prática o Arduino irá considerar como HIGH qualquer valor de tensão de 3 volts ou mais. Valores abaixo de 3V serão considerados nível lógico LOW.

HIGH e LOW são variáveis que guardam valores inteiros (respectivamente 1 e 0), portanto o valor lido por digitalRead() deve ser armazenado em uma variável declarada como int:

int valor = digitalRead(3);

Os pinos digitais são inicialmente configurados como entradas, portanto não é necessário usar pinMode() para declará-los como tal. Se for usada deve conter a opção INPUT:

pinMode(3, INPUT);     // pino 3 é uma entrada

Lógica condicional e bloco if-else

If” significa “se”. O bloco condicional if(condição) {} recebe entre parênteses uma expressão lógica booleana, e entre as chaves uma lista de instruções que devem ser executadas somente se a expressão for verdadeira.

Expressões lógicas podem ser igualdade (operador ==), diferença (operador !=) e desigualdade (operadores >, <, >= e <=).

Observe que para testar a igualdade usa-se um duplo igual ==, já que o sinal de igual isolado é usado como operador de atribuição.

Blocos if() devem ser usados dentro de blocos loop() ou setup(), portanto é uma boa prática, ao escrever programas, endentar o conteúdo do bloco para facilitar a leitura do código (ex: digitar quatro espaços antes, para cada novo nível de chaves {…}).

Por exemplo, as instruções que começam com “int” e “if” abaixo estão dentro de loop() e endentadas 4 espaços. A instrução “digitalWrite” está dentro de if, que está dentro de loop, e endentada 8 espaços:

void loop() {
    int estado = digitalRead(3);
    if (estado == HIGH) {         // testa se estado é 5V
        digitalWrite(8, HIGH);   // “acende” componente que está no pino 8
    }
}

Um bloco if() pode ser seguido por um bloco else {}, que significa “caso contrário” e executa quando a condição não for verdadeira:

if (estado == HIGH) {         // somente se o valor de estado for 5V
    digitalWrite(8, HIGH);     // “acende” componente que está no pino 8
} else {                     // caso contrário
    digitalWrite(10, HIGH);   // acende o componente do pino 10
}

Alteração 25.1 – Invertendo o estado de acionamento

Altere o programa do último experimento para que ele acione o LED quando:

  • O estado do pino não for LOW (requer apenas alteração no código).
  • O estado do pino for HIGH (que alteração precisará ser feita no circuito?)

Arduino 3: Introdução à programação

Para fazer qualquer circuito com o Arduino, é preciso primeiro programá-lo. Este é o objetivo desta seção onde introduziremos, de forma prática, sua linguagem de programação.

Para que esta introdução seja curta, objetiva e mais simples possível, omitiremos detalhes e abordaremos conceitos de maneira incompleta e às vezes até imprecisa, para focar apenas no essencial necessário para entender os programas que serão construídos.

Estrutura básica de um sketch

Um programa Arduino é chamado de Sketch. Ele consiste de uma sequência de instruções escritas em uma linguagem chamada Processing. Para ser usado o programa depois precisa ser compilado (traduzido para linguagem de máquina) e transferido para o Arduino. Um sketch é também um arquivo de texto que pode ser gravado no computador, e possui a extensão .ino. O menor sketch contém no mínimo a seguinte estrutura (que não faz nada):

void setup() {

}

void loop() {

}

Se você está usando um Arduino pela primeira vez, e não sabe que programa está em sua memória, é uma boa prática transferir o programa acima para ele. Isto garante que ele não executará nenhuma tarefa que possa danificá-lo.

A estrutura acima possui dois blocos, que podemos chamar de bloco setup() e bloco loop(). A instrução void setup() define o bloco setup(), e a instrução void loop() define o bloco loop(). Essas instruções são chamadas automaticamente quando o Arduino estiver executando, e todas as instruções que forem digitadas entre as chaves { } serão executadas.

No programa acima, as chaves estão vazias, portanto quando o Arduino chamar setup() e loop(), ele não vai fazer nada.

Os blocos setup() e loop() funcionam de forma distinta. O bloco setup() é chamado uma vez só, portanto ele deve conter instruções que serão executadas uma única vez. Já o bloco loop() é chamado eternamente, e deve conter instruções que repetem para sempre (até que o Arduino seja desligado ou reiniciado).

Normalmente dentro de setup() serão colocadas instruções de configuração (por exemplo, especificar a função que um determinado pino irá assumir – se entrada ou saída). Em loop() ficam as instruções que efetivamente programam o Arduino, por exemplo, mandar nível lógico alto (5V) para pino 4, esperar meio segundo, e depois mandar nível lógico baixo (0V), e repetir isto sem parar.

Antes de programar qualquer coisa, vamos testar a transferência de programas para o Arduino.

Digite o programa acima. Para transferir para o Arduino, clique no ícone   (ou selecione o menu Sketch/Upload). Se houver erro, ele aparecerá na caixinha de status na parte inferior do programa, e a transferência não acontecerá. Um erro comum é esquecer de selecionar o modelo de Arduino e sua porta de comunicação (veja a seção 5.5.3). Verifique também se não esqueceu de fechar alguma chave, ou se digitou algo diferente do que foi listado. Os comandos precisam ser escritos exatamente como acima (letras maiúsculas e minúsculas são consideradas diferentes na linguagem do Arduino (ex: escrever LOOP() ou Loop() é um erro).

Sintaxe das instruções

As instruções usadas dentro dos blocos setup() e loop() têm uma sintaxe bem definida. Existem vários tipos. Usaremos principalmente instruções de uma linha. Essas instruções sempre terminam em ponto-e-vírgula e podem ser classificadas como comandos (que mandam o Arduino fazer alguma coisa) ou expressões (que calculam valores, guardam dados, etc.).

Comandos

Comandos são formalmente chamados de funções ou métodos. Eles fazem parte de uma biblioteca que define seus nomes e parâmetros. Os parâmetros são valores passados entre parênteses, às vezes entre aspas, e separados por vírgulas depois do nome da função. Há comandos que não têm parâmetros (apenas os parênteses vazios). Por exemplo, o comando:

delay(500);

manda o Arduino esperar meio segundo (500 milissegundos). Há apenas um parâmetro que é o número de milissegundos a esperar. Este outro comando:

analogWrite(9, 128);

manda o Arduino produzir no seu pino digital 9 um sinal analógico de nível 128 (o nível varia de 0 a 255, e corresponde a valores médios (simulados) de 0 a 5V produzidos com PWM). A instrução, portanto, produz um pulso ligado 50% do tempo que resulta em um valor médio de 2,5V no pino 9.

Observe que as instruções terminam sempre em ponto-e-vírgula. Observe também que o “W” em analogWrite é maiúsculo (e assim deve ser escrito).

Expressões e variáveis

Pode-se escrever um programa apenas com comandos, mas alguns comandos retornam resultados que precisam ser processados. O processamento é feito através de expressões. Existem vários tipos de expressões: aritméticas, lógicas, etc. Expressões frequentemente são formadas por operações. Por exemplo, esta é uma expressão contendo uma operação de soma e uma operação de atribuição:

numero = 3 + 4;

O Arduino irá somar 3 com 4 e guardar o resultado na variável numero. O sinal de = é usado para fazer uma operação de atribuição, isto é, copiar um valor (o resultado da expressão) para uma área da memória associada à variável. Variáveis são palavras usadas para identificar, guardar e referenciar dados. Elas só guardam dados de um tipo de dados específico. A variável acima precisa declarar o tipo de dados que pode armazenar, antes que seja usada. A declaração é também uma expressão. Então algum lugar antes da linha acima, deve haver algo como:

int numero;

declarando que a variável numero é do tipo int. A palavra int significa inteiro, e é usada para declarar variáveis que só aceitam valores inteiros. Não seria possível, por exemplo, guardar um 3.14 na variável numero. Para isto ela teria que ser declarada como float, que é o nome usado para variáveis com parte decimal. Observe que a declaração da variável também termina em ponto-e-vírgula.

Nos programas em Arduino que faremos nesta introdução, declararemos apenas variáveis do tipo int e float. Muitas vezes, a declaração e a atribuição ocorrem na mesma linha, por exemplo:

int pino = 6;
float valor = 3.14;

Depois de declarada uma variável, provavelmente vamos querer usá-la depois. O uso de uma variável pode ser, por exemplo, a inclusão do valor que ela contém em alguma outra expressão ou comando:

float raio = 9.5;
float area = valor * raio * raio;
int tempo = 1000;
delay( tempo );

A última linha acima é um comando que está usando a variável tempo, que contém o valor inteiro 1000, que é passado como parâmetro da função delay().

Alguns comandos produzem um valor, que geralmente é resultado do processamento executado por eles. Esse valor geralmente é guardado em uma variável. Por exemplo:

int duracao = analogRead(2);

O comando analogRead(2) é executado, e seu resultado é copiado (via operação de atribuição) para a variável duracao. Depois, este valor pode ser usado em outro comando, por exemplo:

delay(duracao);

A instrução analogRead(2) produz um valor (entre 0 e 1023) resultante da leitura do nível da tensão no pino A2. Por exemplo, se houver um potenciômetro com os pinos externos ligados entre 0 e 5V, e o pino do meio estiver ligado no A2 do Arduino, e este potenciômetro estiver com o seletor posicionado exatamente no meio, o valor recebido por analogRead(2) será 1024/2 ou 512.

Nomes de variáveis

Variáveis não podem ter qualquer nome. Não crie nomes com acentos, hífens, pontos. O ideal é usar nomes curtos e explicativos. Se você quer criar uma variável com mais de uma palavra, você pode distinguir as palavras usando maiúsculas, por exemplo:

int numeroDoPino = 6;

ou sublinhados:

int NUMERO_DO_PINO = 6;

Variáveis também não podem usar certas palavras, que são reservadas. Exemplos são as palavras int e float, que têm significado especial para o Arduino. É fácil saber quando uma palavra é reservada, pois ela aparece com uma cor diferente (azulada) no IDE.

Comentários

Nem tudo o que está escrito em um sketch é enviado para o Arduino. Para que os programas sejam mais fáceis de entender pelos humanos que irão lê-lo, é comum que tenham comentários. Os comentários são texto ignorado pelo compilador (mecanismo da IDE que traduz o programa para linguagem de máquina) e devem ser usados para explicar trechos do programa, ou incluir instruções de como usá-los. Há dois tipos: comentários de linha e comentários de bloco.

Comentários de linha geralmente aparecem antes de instruções, ou logo depois do ponto-e-vírgula, na mesma linha que a instrução. Tudo o que aparece depois do // é considerado um comentário. Por exemplo:

int PINO_DO_LED = 13;   // este é o pino do LED interno

Outra forma de escrever comentários no programa é usar comentários de bloco, que consiste em incluir o texto de uma ou mais linhas entre /* e */. Use esse tipo de comentário se o que você pretende escrever tem muitas linhas:

/*
Este programa faz um LED piscar.
Ligue o Anodo do LED no pino 6.
Ligue o Catodo em um resistor de 470 ohms, ligado a GND.
*/
void setup() { ... }

Comentários também são usados para temporariamente ignorar um trecho de código (que você não quer que execute, mas não quer apagar do sketch):

void loop() {
    // delay(500);
    delay(1000);   // a linha anterior será ignorada
}

Isto é suficiente como uma introdução à linguagem do Arduino. Com o que vimos até aqui já é possível fazer um primeiro programa para piscar um LED.

Experimento 24 – Piscando um LED

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Um LED de qualquer cor
  • Resistor de 220 Ω
  • Protoboard, fios e jumpers

Para piscar um LED, é preciso liga-lo em uma saída que alterne entre dois níveis lógicos: alto e baixo, com um intervalo entre eles. Como precisamos de apenas dois estados, podemos usar um pino de saída digital. Há 14 deles. Podemos usar qualquer um. Vamos escolher o pino digital 8, identificado na placa do Arduino Nano com a indicação D8.

O nível lógico ALTO no Arduino é sempre 5 volts. Precisamos de um resistor para limitar a corrente do LED e temos informações suficientes para calcular seu valor. Se for um LED vermelho, com 2V de queda de tensão:

R = (5V – 2V) / 0,02 A = 150 ohms.

Não temos 150 ohms no kit, mas podemos usar 220 ohms que consome um pouco menos corrente, ou até mesmo arriscar um valor menor (100 ohms) já que ele não vai ficar ligado muito tempo. Podemos também usar um resistor de 100 ohms e trocar o LED vermelho por um de 3V (azul, rosa ou branco).

Antes de montar qualquer circuito, sempre desligue o Arduino do computador (desconecte o cabo USB). Monte o circuito abaixo, verifique as conexões, e depois ligue novamente o Arduino ao computador.

A porta USB é quem irá fornecer corrente para o circuito. Se houver um problema no seu circuito e ele tentar puxar corrente demais da porta USB, o computador desligará o acesso e desligará o Arduino (você terá que remover o cabo e reinserir novamente, depois de corrigir o problema).

O resistor ligado ao catodo do LED pode ser conectado a qualquer um dos dois pinos GND disponíveis no Arduino Nano (há um de cada lado). Internamente eles estão ligados entre si.

Agora vamos escrever um programa para piscar o LED. Abra um novo sketch (ícone ou Menu File/New) e preencha os blocos setup() e loop() com as seguintes instruções:

void setup()
    pinMode(8, OUTPUT);     // declara pino 8 como uma saída
}
void loop() {
    digitalWrite(8, HIGH);
    delay(500);
    digitalWrite(8, LOW);
    delay(500);
}

Clique no ícone para transferir o programa para o Arduino. Em alguns segundos o LED deverá começar a piscar ficando meio segundo aceso e meio segundo apagado. Experimente mudar o valor de delay() para que ele pisque mais rápido, ou que fique mais tempo aceso que apagado.

Explicaremos os comandos usados no programa nas seções a seguir.

Pinos digitais e estados HIGH e LOW

Um pino digital permite a entrada e saída de valores digitais correspondentes aos níveis lógicos ligado (5V), ou ALTO, e desligado (0V), ou BAIXO. Esses dois estados são representados na linguagem do Arduino pelas palavras reservadas HIGH (sempre em maiúsculas) e LOW (idem). No contexto do circuito, correspondem aos valores de tensão 5V e GND (0V).

(Na verdade HIGH e LOW são variáveis pré-definidas que, no contexto do programa, contém os números inteiros 1 e 0, respectivamente.)

Um componente de dois terminais ligado a um pino de saída deve ter o seu outro terminal ligado a uma referência de tensão: o pino GND (negativo) ou o pino 5V (positivo). Haverá corrente se houver diferença de potencial entre o pino de saída e a referência. Isto significa que, para que haja corrente fluindo por um componente, se ele estiver conectado a um pino que é acionado pelo valor HIGH, o outro terminal deve estar conectado a GND (é assim que o LED está configurado no nosso exemplo). Se o componente for acionado pelo valor LOW, o outro terminal deve estar conectado a 5V.

É importante observar a polaridade do componente e posicioná-lo de acordo, e também limitar a corrente. Um pino e saída do Arduino não suporta mais que 40 mA. Ligar um pino de saída diretamente a 5V ou GND sem resistor limitador gera uma corrente muito alta que poderá queimá-lo quando houver uma diferença de potencial no pino.

A instrução pinMode()

Normalmente os pinos operam como entrada. Para usar um pino como saída digital é preciso executar uma instrução para declará-lo explicitamente. Isto normalmente é feito dentro do bloco setup() através da instrução pinMode(número-do-pino, função) (observe o “M” maiúsculo da instrução). Os dois parâmetros informam respectivamente o número do pino e o tipo de função que ele vai exercer (OUTPUT, para a função saída):

void setup()
    pinMode(8, OUTPUT);     // pino 8* é uma saída
}

* Na placa do Arduino Nano os pinos digitais são identificados pelo prefixo D (D0, D1, D2, D3, etc.) e os analógicos pelo prefixo A (A0, A1, A2, A3, etc.) No programa, apenas os números dos pinos digitais são usados (ex: 0, 1, 2, 3, etc.) Os pinos analógicos podem ser identificados com ou sem prefixo nos comandos que aceitam entradas analógicas.

Saída digital

Para produzir uma saída digital em níveis lógicos (HIGH/5V ou LOW/0V, sem valores intermediários) usa-se a instrução digitalWrite(número-do-pino, nível-lógico), dentro de setup() (para rodar apenas uma vez) ou loop() (para rodar repetidas vezes). O número do pino precisa ter sido previamente declarado como OUTPUT através da instrução pinMode().

Por exemplo:

digitalWrite(8, HIGH); // aplica o valor HIGH (5 volts) no pino 8

O comando acima transfere 5V (HIGH) para a saída digital D8. Se no pino D8 houver um LED (alimentado entre o pino 8 e GND), ele irá receber 5V, e acender.

Para o LED piscar é preciso fazer o pino 8 ter valor HIGH, depois esperar algum tempo (mantendo o pino neste estado) e em seguida fazer o pino 8 ter valor LOW, esperar mais algum tempo (em que o LED ficará apagado) e repetir a sequência. A repetição acontece automaticamente para instruções digitadas dentro do bloco loop(), portanto para piscar o LED serão necessárias apenas quatro instruções:

void loop() {
    digitalWrite(8, HIGH);        // aplica 5V no LED+resistor
    delay(500);           // mantém em 5V por 0,5 segundos
    digitalWrite(8, LOW);   // aplica 0V no LED+resistor
    delay(500);             // mantém em 0V por 0,5 segundos
}
// repete tudo ad infinitum

Variáveis globais e #define

Quando se tem um programa que usa apenas um ou dois pinos, é fácil lembrar o que está conectado a cada um, mas se muitos pinos estiverem sendo usados o programa pode tornar-se difícil de ler e entender. E se for necessário mudar um componente para outro pino? O número teria que ser alterado em todos os lugares onde foi digitado. Uma solução para este problema é declarar uma variável para identificar o pino:

void loop() {
    int PINO_DO_LED = 8;    // Declarando variável PINO_DO_LED contendo 8
    digitalWrite(PINO_DO_LED, HIGH); // o primeiro parâmetro recebe 8
    delay(500);
    digitalWrite(PINO_DO_LED, LOW);
    delay(500);
}

Variáveis globais

Uma variável declarada dentro das chaves { … } de um bloco (ex: setup() ou loop()) é acessível apenas dentro daquele mesmo bloco. Quando o bloco terminar, ela não poderá mais ser usada (causará erro no programa). Mas às vezes criamos uma variável exatamente para poder usá-la em blocos diferentes. Por isto é comum que a declaração de algumas variáveis ocorra fora dos blocos loop() e setup(). Variáveis declaradas fora dos blocos são chamadas de variáveis globais, porque elas podem ser usadas em qualquer um dos dois blocos.

No exemplo abaixo, criamos uma variável global para guardar o pino do LED:.

int LED = 8; // declarada fora de setup() ou loop() – é global

void setup()
    pinMode(LED, OUTPUT);    // reconhecida dentro de setup() – recebe 8
}

void loop() {
    digitalWrite(LED, HIGH); // reconhecida dentro de loop() – recebe 8
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);
}

Outra forma de declarar uma variável global para um pino

Você encontrará alguns programas que declaram variáveis usando o comando #define antes dos blocos setup() e loop(). Por exemplo:

#define LED 8

A sintaxe é diferente de uma declaração de variável comum. Não existe o sinal de igual (=) e nem ponto-e-vírgula (não pode ter ponto-e-vírgula). Na prática o resultado é o mesmo. Usar esta forma ou a outra é uma questão de estilo. Não vai alterar o funcionamento do programa. Mesmo que você escolha usar apenas a outra forma, é importante reconhecer essa sintaxe, pois muitos programadores preferem usar #define em vez de declarar variáveis globais.

As declarações #define geralmente aparecem no início do programa. Elas não podem aparecer dentro dos blocos setup() ou loop().

Alteração 24.1 – Usando variáveis

Altere o programa do último experimento substituindo o número do pino por uma variável, e faça o upload novamente. Veja que o funcionamento não muda. Agora mude a posição do LED para o pino 7 no protoboard. Ele não pisca mais, mas você pode abrir o programa, fazer apenas uma alteração (mudando a variável LED para 7) e transferi-lo novamente, que ele voltará a funcionar.

Arduino 2: Configuração do Arduino Nano

O Arduino que usaremos na oficina é um Arduino Nano. Tem dimensões de 43 x 15 mm. Ele possui uma entrada USB que permite a ligação direta a um computador (não precisa de adaptador), e que também fornece alimentação de 5V enquanto estiver conectado. Depois de programado e desconectado do computador, ele pode ser alimentado de forma independente por 7 a 12V aplicados nos pinos VIN (ligado ao positivo da bateria ou fonte) e GND (ligado ao negativo).

O Arduino Nano também possui saídas de tensão reguladas em 3,3 V (Pino 3V3) e 5V (Pino 5V). Os pinos A0 a A7 são de entrada analógica (recebem valores entre 0 e 5V), e D0 a D13 suportam entrada digital (reconhecem dois valores: 0V – nível lógico BAIXO ou 5V – nível lógico ALTO). A saída analógica é simulada via PWM apenas através dos pinos digitais D3, D5, D6, D9, D10 e D11. Os outros pinos digitais, e também os pinos A0 a A5, podem operar como saída digital. O diagrama abaixo ilustra a pinagem do Arduino Nano:

As especificações de corrente e tensão referem-se ao clone chinês CH340 do Arduino Nano que está incluído no kit, e não ao Arduino Nano original italiano (que são um pouco diferentes).

Algumas observações e cuidados importantes:

  • Os pinos do Arduino suportam no máximo 40mA (ligar em um circuito que deixa passar mais corrente pode queimar o pino). É necessário calcular resistores para limitar a corrente.
  • O Arduino inteiro fornece no máximo 200mA. Mas é possível controlar circuitos que consomem bem mais corrente, desde os sinais enviados e recebidos pelos pinos sejam intermediados por circuitos que reduzam as correntes e tensões a níveis suportados. Isto pode ser feito com resistores, capacitores, transistores, relés e outros dispositivos.
  • Também é necessário ter cuidado para não curto-circuitar as saídas (5V ou 3V3 ligadas diretamente em GND). Os pinos analógicos e digitais podem ser ligados diretamente em 5V ou 0V somente se forem usados como entradas. Esses valores são tratados como informação (nível lógico ALTO e BAIXO) pelo Arduino. Para usá-los como saídas, é necessário configurar essa funcionalidade na programação, e ter o mesmo cuidado que as saídas 5V e 3V3 (não ligar diretamente em GND), além de usar resistores para manter o fluxo de corrente dentro do limite.
  • O pino AREF é usado para ajustar a tensão de referência usada para os pinos analógicos. Ela está internamente conectada ao pino 5V, mas pode ser desligada via programação. Ligar uma tensão qualquer neste pino sem primeiro fazer essa alteração via código irá queimar o regulador de tensão (e provavelmente a entrada USB).

Um programa escrito para um tipo de Arduino pode ser usado em outro tipo de Arduino. Pode-se aproveitar programas prontos e fazer pequenas adaptações sem que seja necessário entender todo o código. Portanto, sabendo o mínimo da programação do Arduino, você pode baixar programas da Internet e adaptar para seus circuitos. É preciso garantir que os números de pinos, declarados no código dos programas, e os pinos reais, usados no circuito estejam de acordo. Em geral qualquer pino digital ou analógico pode ser usado. Eles podem até ser reprogramados. Alguns pinos têm capacidades especiais. Por exemplo, os pinos digitais 3, 5, 6, 9, 10 e 11, no Arduino Nano, permitem gerar saída analógica usando PWM.

Não se preocupe se você não entendeu tudo. São muitos conceitos e é sempre mais fácil entender com um ou mais exemplos. Nas próximas seções mostraremos como instalar e configurar o Arduino, e depois como usá-lo através de vários experimentos. Depois que você fizer os experimentos, releia esta seção. Vários conceitos irão ficar mais claros.

Preparação e teste do Arduino

Como vamos construir circuitos, e o Arduino Nano não possui soquetes onde podemos inserir terminais de componentes, precisamos usar o protoboard. Encaixe o Arduino com cuidado ocupando a parte central, de forma que possamos ter acesso a todos os seus pinos através dos pinos laterais. Da forma mostrada abaixo, cada pino terá dois a três furos.

O protoboard deve estar livre de outros circuitos (principalmente, não deve haver nenhuma fonte de energia conectado a ele).

Depois de encaixado o Arduino, encaixe uma das pontas do cabo USB no Arduino, e a outra em alguma saída USB do seu computador. O LED PWR do Arduino deverá acender, indicando que ele está sendo alimentado pela porta USB. Para haver comunicação, no entanto, é preciso instalar o driver.

Instalação do ambiente de desenvolvimento

Para habilitar um computador para programar o Arduino Nano do kit são necessárias duas etapas:

  1. Instalar o driver (programa que permite a comunicação com o Arduino via porta USB do computador) do adaptador USB-Serial (embutido no Arduino).
  2. Instalar o programa com o ambiente de programação (Arduino IDE).

A IDE (aplicação com ambiente gráfico para programação) é distribuída pelo site oficial do Arduino (arduino.cc) e existe para Mac, Windows e Linux. Roda de maneira praticamente idêntica nas três plataformas.

O driver é mais complicado de instalar, e pode variar dependendo do Arduino usado, se é um clone ou se é um autêntico italiano. O Arduino original (italiano) não requer a instalação de drivers no Mac, mas a instalação ainda pode ser necessária em algumas versões de Windows.

Instalação do driver

O Arduino Nano incluído no kit é um clone e usa um adaptador USB-Serial chinês (chip CH341). Para que ele seja reconhecido pelo computador, seja Mac, PC ou Linux, ele precisa ter o driver instalado antes. O driver é um programa de instalação que deve ser baixado do site do fabricante e executado. Ele não faz nada além disso. A instalação termina depois que o computador for reiniciado. Veja as instruções abaixo. Elas podem ser diferentes dependendo do sistema que você estiver usando.

Windows

Se você usa Windows 10 baixe o arquivo EXE disponível em

http://www.wch.cn/download/CH341SER_EXE.html

(clique no botão de Download), execute-o. Você deve ter as permissões para executar este programa no computador, pois ele vai gravar arquivos do sistema. Siga o passo-a-passo (em inglês). Depois é necessário reiniciar o computador para completar a instalação. Quando terminar e reiniciar, pule para a seção seguinte (IDE) para instalar o ambiente de programação.

Mac

Se usa Mac OS Sierra (10.12), baixe o arquivo ZIP em

http://www.wch.cn/download/CH341SER_MAC_ZIP.html

(clique no botão de Download) e abra o ZIP. Dentro dele há um arquivo CH34x_Install_V1.4.pkg. Execute esse arquivo e siga as instruções (em inglês). Depois é necessário reiniciar o computador para completar a instalação. Quando reiniciar, pule para a seção seguinte (IDE) para instalar o ambiente de programação.

Linux

E se você usa Linux baixe o arquivo localizado em

http://www.wch.cn/download/CH341SER_LINUX_ZIP.html

(clique no botão de Download). Abra o ZIP em uma pasta. Abra uma janela do terminal e execute as linhas abaixo:

sudo make
sudo make load

Instalação do ambiente de programação (IDE)

A programação do Arduino é feito na linguagem Processing, que é baseada na linguagem C e similar a linguagens de programação populares como C# e Java. Embora possam ser escritos programas bastante complexos usando essa linguagem, é possível fazer muita coisa escrevendo programas bem simples e fáceis de entender mesmo para quem é leigo em programação. Aprendendo o mínimo, você conseguirá baixar programas disponíveis na Internet e adaptar para rodar com seus circuitos. Para isto, precisamos instalar o ambiente de desenvolvimento integrado (IDE – Integrated Development Environment) do Arduino. Baixe o programa de instalação para o seu sistema operacional (Windows, Mac ou Linux) na página

https://www.arduino.cc/en/Main/Software

Execute o instalador e siga o passo-a-passo. Depois rode o programa. Ele deverá abrir a janela abaixo:

Comunicação do Arduino com o computador

Depois de instalados o driver e o IDE, é preciso ainda selecionar a placa usada e a identificar a porta de comunicação onde ela está conectada. Isto só precisa ser feito uma vez para cada placa diferente que você usar, mas requer que o Arduino esteja conectado. Portanto, se você ainda não conectou o Arduino a uma porta USB do seu computador, faça isto agora.

Selecione no menu Ferramentas (Tools), na opção Placa (Board). Na lista há várias placas. Selecione Arduino Nano.

Depois selecione a porta de comunicação. No Windows deve ser algo como COM4. No Linux e Mac, um caminho que inicia com /dev/cu.wchusbserial (ex: /dev/cu.wchusbserial123456).

Se você usar outro tipo de Arduino posteriormente, terá que modificar esses parâmetros ou a transferência do programa não será possível (o programa apresentará mensagens de erro informando isto). Alguns clones de Arduino são identificados diferentemente (ex: alguns clones chineses de LilyPad são identificados como Arduino Uno) e outros requerem a instalação de bibliotecas externas (que podem ser baixadas) para funcionar.

No próximo post iniciaremos a programação do Arduino e faremos um primeiro circuito.