Programação para Arduino: mais autonomia para seus projetos Deixe um comentário

Por muitas vezes, enquanto aprende programação com Arduino, você pode ter criado o hábito de apenas replicar o código de terceiros, e não sabe por onde começar para criar seus próprios projetos com autonomia.

Para resolver esse problema, a FILIPEFLOP traz esse tutorial completo e objetivo sobre como programar com Arduino, e tornar-se mais independente na criação dos seus projetos.

Iremos criar programas simples e intuitivos, a fim de construir um entendimento sistemático e conciso de como o Arduino entende o que escrevemos para ele. Se você tiver um Arduino com você, poderá testar os programas que fizermos juntos.

Caso não possua um, você pode adquirir conosco, e enquanto ele não chega, pode utilizar o Wokwi, plataforma de simulação online e gratuita; confira nosso tutorial de como usá-la. Então, vamos lá?

Confira abaixo todos os tópicos que abordaremos:

  • O que é um Arduino?
  • Comunicação com Arduino
  • Compiladores e IDE
  • Software
  • Variáveis
  • Tipagem
  • Operadores
  • Funções
  • Controle de Fluxo
  • Bibliotecas

O que é um Arduino?

O Arduino é basicamente um computador, que é um dispositivo físico (hardware) capaz de processar instruções, com o uso de cálculos matemáticos. Para tomar decisões de forma rápida e precisa, o computador possui um (ou mais) microprocessadores, que são como o “cérebro” da máquina.

Dessa forma, o “cérebro” do Arduino é o microcontrolador ATMega, com seu modelo variando dependendo do Arduino (no Arduino Uno, por exemplo, é o ATMega328). Microcontroladores são semelhantes aos microprocessadores, com a diferença que são utilizados para tarefas mais simples.

Mesmo assim, o Arduino é considerado um computador poderoso e útil para suas proporções. Ainda vale dizer que o Arduino está incluso em um espectro da computação e engenharia, chamado de sistemas embarcados, que basicamente é o conjunto de um computador diretamente acoplado ao aparelho que ele controla.

Televisões, controles remotos, celulares, entre outros, são sistemas embarcados.

Comunicação com Arduino

Para podermos nos comunicar com o Arduino, precisamos falar a “língua” dele, ou seja, precisamos desenvolver programas, também chamados de software (para sistemas embarcados, podemos ainda chamar programas de firmware).

Acontece que a “língua” que o Arduino entende, assim como o resto dos computadores, é a linguagem de máquina, que se parece com o seguinte:

Um pouco estranho, certo? É que a linguagem de máquina é composta inteiramente por números, e faz todo sentido para o Arduino. Entretanto, nós, seres humanos, temos uma grande dificuldade de entender essa linguagem.

Para lidar com isso, alguns desenvolvedores criaram a primeira linguagem de programação de alto nível na década de 50, o Fortran. Ela foi chamada de alto nível, pois é uma linguagem muito mais fácil de nós entendermos, já que não é composta apenas por números.

Com o passar dos anos, muitas outras linguagens de programação de alto nível foram criadas. E, para programarmos o Arduino, fazemos uso, com algumas modificações, da linguagem de programação C++, criada na década de 80.

Mas uma dúvida pode surgir: “como poderemos utilizar a linguagem de programação C++ para programar o Arduino se ele entende a linguagem de máquina ?”.

Compiladores e IDE

Para responder a pergunta da seção anterior, vamos esboçar o que sabemos: temos a linguagem C++, que escrevemos nossos programas, e a linguagem de máquina que o Arduino consegue ler. Dessa forma, precisamos de um intermediário que irá ler o nosso programa e irá traduzi-lo para o Arduino.

Esses intermediários são chamados de compiladores e a ação de traduzir o programa em linguagem alto nível para linguagem de máquina é chamada de compilar. Para muitos casos, é necessário escrever um programa, e compilar manualmente com o uso de um compilador externo, mas no caso do Arduino, basta utilizar a Arduino IDE (Integrated Development Environment).

A Arduino IDE é o ambiente integrado de desenvolvimento, onde podemos escrever nosso código, e compilar ali mesmo, sem precisar utilizar de um compilador externo. Isso significa, na prática, que basta escrever o programa e com o aperto de um botão, a compilação será feita e o programa, já transcrito para linguagem de máquina, será recebido pelo Arduino.

Software

Quando criamos um programa na Arduino IDE, teremos um documento com extensão “ino”, chamado de Sketch e esse será o documento de texto em que você pode escrever seus programas.

Na escrita de um programa, temos alguns recursos que podemos utilizar para escrevê-lo. Enquanto que na língua portuguesa, por exemplo, temos recursos linguísticos como artigos, verbos, orações, conjunções, preposições, entre outros, para compor a nossa fala e gerar comunicação; nas linguagens de programação, temos recursos computacionais.

Ou seja, precisamos aprender quais são esses recursos computacionais para que possamos utilizar a linguagem de programação. Na prática, isso quer dizer que não podemos escrever “qualquer coisa” em um programa.

Vamos utilizar durante o artigo um programa super didático (e outros exemplos pontuais), do Arduino, o Blink. Com ele, podemos piscar um LED. Iremos explicar como os recursos estão sendo utilizados e abordaremos seus conceitos.

int pinoLed = 7;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
 
}

Os projetos de Arduino sempre terão uma coisa muito importante em comum: o código terá a função setup e a função loop. Falaremos sobre funções em uma seção futura, mas já é legal saber como é a “cara” de um projeto em Arduino.

Variáveis

Variável é um recurso que possibilita armazenar um dado, que pode ser um número, um caractere ou um conjunto de caracteres, em um endereço de memória. No código Blink, temos:

int pinoLed = 7;

O que estamos fazendo aqui é dizendo que o nome “pinoLed” vale o número inteiro 7.Essa seria a definição mais didática; vamos conceituar de forma mais técnica o que está acontecendo: estamos, primeiro, declarando “pinoLed” como uma variável de tipo inteiro e, depois, atribuindo o valor numérico inteiro 7 para ela.

É como se estivéssemos fazendo duas etapas simultaneamente: declaração e atribuição, que podem ser feitas separadas também:

int pinoLed;
pinoLed = 7;

Declaração de variável

Uma declaração de variável é quando escolhemos um nome e um tipo para nossa variável:

int pinoLed;

O tipo (ou tipagem) é nada mais que o tipo de valores que essa variável poderá receber, então se a variável é do tipo inteiro, ela só pode receber valores inteiros (falaremos mais sobre tipos na próxima seção).

Atribuição de variável

Uma atribuição é quando a variável recebe algum valor, representamos isso por um sinal de igual =.

int pinoLed = 7;

A forma que isso se lê é: a variável led está recebendo o valor 7. Perceba que a leitura é unilateral: a variável da esquerda recebe o valor da direita.

Podemos unir a declaração com a atribuição, e teremos uma inicialização de variável:

int pinoLed = 7;

Dessa forma, pensando na funcionalidade do projeto que é piscar um led, podemos entender que 7 é o número do pino que o led será conectado.

Uso da variável

A partir da criação de uma variável, podemos utilizá-la em outro lugares em nosso código, como é feito no código Blink, veja esse trecho:

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

Podíamos ter escrito da seguinte forma também sem problema:

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

Mas então você pode se perguntar, mas para que então eu usaria a variável se posso colocar o número diretamente?

Uma das vantagens de usar variável é que se futuramente no seu projeto for necessário trocar o pino utilizado por outro e você colocou o valor 7 em vários lugares invés da variável, você irá precisar trocar todos os valores manualmente. Se você tiver colocado a variável nos locais, basta mudar o valor 7 na inicialização da variável.

Escopos

Uma coisa bacana sobre a compilação de um programa, é que a leitura do código é feita sempre de cima para baixo, da esquerda para a direita em cada linha. Mas porque isso é importante? Vejamos novamente o código Blink.

int pinoLed = 7;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
 
}

Perceba que a variável pinoLed é inicializada acima e fora das chaves das funções setup e loop. Quando a variável é declarada fora de todas as funções do programa, ela é dita global. Uma função global pode ser utilizada em qualquer função.

Agora, quando uma variável é declarada dentro de uma função, ela só pode ser utilizada nessa função. Por exemplo:

void setup() {
  int pinoLed = 7;
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
}

Dessa forma, se compilarmos o programa, obteremos um erro, dizendo que a variável pinoLed não foi declarada no escopo da função loop. E isso é verdade, inicializamos a variável pinoLed dentro da função setup, então a variável só existe ali dentro.

Portanto, o escopo de uma função é o limite de uma função, enquanto que o escopo global atinge todas as funções. Dessa maneira, para usar uma variável em um local do programa, é necessário que ela esteja declarada no escopo daquele local. Veja este outro exemplo:

void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
int pinoLed = 7;
 
void loop() {
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
}

Nesse caso, a inicialização da variável está fora de qualquer função, então é uma variável global, mas se fizermos a compilação do programa, o Arduino irá notificar que pinoLed não foi definida dentro de setup. Isso acontece porque, por mais que pinoLed seja uma variável global, ela está sendo declarada depois da função setup, lembre-se que o código é lido pelo compilador de cima para baixo. Portanto, a ordem dos comandos importam.

Mas então você pode se perguntar: “mas por que então eu não crio todas as minhas variáveis como globais?”. A resposta é que você pode criar suas variáveis todas como globais, mas ter variáveis locais pode ser útil para o entendimento do programa à medida que ele fica maior, e quando uma variável é global, ela pode ser chamada em qualquer função e isso não é muito interessante, pois pode gerar confusão e problemas no código. Resumindo, use variáveis globais com sabedoria.

Tipagem

Como foi dito, o tipo de uma variável define qual tipo de dado ela poderá receber. Na linguagem C++, é obrigatório o uso de um tipo de forma explícita, ou seja, é necessário escrever o tipo antes do nome da variável em uma declaração. Há vários tipos para as mais diversas necessidades, aqui estão os mais comuns:

Char

O tipo char permite apenas o armazenamento de um único caractere por variável.

char letra = ‘a’;

String

O tipo string permite armazenar uma sequência de caracteres em uma variável, como uma frase. O nome string remete a fio ou corda.

string frase = “Olá maker”;

Int

O tipo inteiro permite a atribuição de um número inteiro.

int numero = 10;

No Arduino ele é um tipo de 16 bits, e isso significa que podemos compor variáveis do número -32767 até +32767. Como assim?

A representação de números decimais em binário é feita com o uso de 0 e 1 em sequência, que compõem os bits. Dessa forma:

Decimal Binário
1 1
2 10
3 11
4 100

O bit mais à esquerda, na representação binária, se chama MSB (most significant bit ou bit mais significativo), e no tipo int, ele denota se o número que queremos representar é negativo ou positivo, ou seja, se o MSB vale 0, temos um número positivo, se ele vale 1, temos um número negativo. Dessa forma, o MSB serve apenas para representar o sinal do número, sobrando apenas 15 bits para usar efetivamente para o número. O maior número possível formado por 15 bits é o 32767.

Por causa disso, há um modificador chamado unsigned, que significa “sem sinal”, que é usado desta forma:

unsigned int numero = 10;

Com ele, estamos informando ao Arduino que, na hora de criar essa variável, ela só poderá receber valores positivos. Na prática, significa que o MSB da representação binária do número não precisa mais informar se ele é positivo ou negativo, dessa forma, o Arduino poderá usar esse último bit para compor o número, possibilitando armazenar números maiores. Com o tipo unsigned int podemos armazenar de 0 até +65535.

Long

O tipo long também possibilita o armazenamento de número inteiros, porém o long utiliza 32 bits para compor um número. Em suma, podemos armazenar em uma variável do tipo long números desde – 2.147.483.647 até + 2.147.483.647.

Além disso, o unsigned também pode ser utilizado aqui, fazendo com que o tipo unsigned long vá desde 0 até + 4.294.967.295.

Float

O tipo float é um tipo de dado que permite o uso de precisão, ou seja, números fracionados ou “com vírgula”. Se você tentar passar um número com vírgula, por exemplo, para um tipo int, o compilador irá arredondar o valor na hora da compilação, então fique atento a isto: para usar números com casas decimais utilize o float ou seus similares.

Boolean

O tipo boolean é o tipo que define valores verdadeiros ou falsos, então uma variável tipo boolean pode ser tanto true ou false.

Outros tipos

Há vários outros tipos que podem ser usados e você irá se acostumar a usá-los à medida que for necessário, não se prenda a decorar tipos, aqui você pode acessar a documentação oficial do Arduino e ver uma lista completa.

Operadores

Os operadores são recursos que possibilitam fazer manipulações diferentes entre dados. Assim como os tipos, os operadores são aprendidos naturalmente, de acordo com o desenvolvimento de um projeto, então não fique muito apegado a decorá-los.

Para exemplificar, um dos operadores mais famosos é o operador de adição. Com ele, podemos somar dois ou mais valores numéricos e chegar em um resultado final, podendo armazenar esse resultado em uma variável:

int var = 1 + 6;

O operador de adição é apenas um dentre os operadores ditos aritméticos, que são os capazes de fazer cálculos matemáticos.

(adição)
(subtração)
/ (divisão)
* (multiplicação)
% (resto da divisão)

Temos também operadores de comparação.

== (igual)
!= (diferente)
> (maior que)
< (menor que)
>= (maior ou igual que)
<= (menor ou igual que)

Há também operadores booleanos, bit a bit, atribuição composta e para ponteiros. Acesse aqui a documentação oficial do Arduino para ver a lista completa.

Funções

Funções são conjuntos de instruções associadas a um nome, um tipo e parâmetros. De forma didática, enquanto uma variável guarda um valor, uma função consegue guardar várias linhas de código.

Para utilizar as funções precisamos primeiro criá-las. Vamos fazer o passo a passo da criação de uma função “soma” que somará dois valores distintos.

Primeiro criamos seu nome, damos a ela um tipo e, se necessário, adicionamos parâmetros dentro de parênteses (com parâmetros ou não, é obrigatório colocar parênteses e chaves).

int soma(int x, int y){
}

Parâmetros

As funções que usamos em programas podem precisar parâmetros para que possamos ter um resultado final, assim como uma função matemática. Por exemplo,

Se f(x) = 2x, f(3) = 6.

Nesse caso, x é o parâmetro dessa função matemática. O mesmo ocorre com funções na programação. Na função soma, os inteiros x e y são parâmetros: variáveis locais que irão ser utilizadas para fazer o cálculo de soma.

Tipos

Funções podem tanto apenas executar instruções, quanto podem também devolver um resultado, e, neste caso, precisamos definir qual o tipo do resultado que será emitido. Por esse motivo que a nossa função soma é int, ou seja, a soma irá retornar um valor inteiro. Para isso, escrevemos:

int soma(int x, int y){
  return x + y;
}

A palavra reservada (própria da linguagem) return irá devolver o valor da soma entre os parâmetros x e y.

Chamada de função

Depois de criada a função soma, se quisermos usá-la em nosso programa, basta usar a chamada de função, que é feita escrevendo o nome da função e passando os parâmetros que foram definidos na criação dela. Como soma retorna um valor, podemos atribuir a chamada de função a uma variável qualquer:

int resultado = soma(3,5);

Dessa forma, “resultado” irá receber a soma de 3 com 5, que será 8.

Rotinas

Como foi dito, há também funções que não retornam um valor, um exemplo disso é a função setup. A função setup deve sempre ser criada em um programa para Arduino. No programa Blink, temos:

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

Perceba que ela apenas faz a chamada de uma outra função, a pinMode, e é finalizada. pinMode não está declarada em nenhum lugar no código, mas ela faz parte do código fonte do Arduino, que não estamos vendo, mas está nos arquivos da IDE, por isso que podemos fazer a sua chamada.

Por não retornar nenhum valor, a função setup() recebe o tipo void.

Além disso, no caso da setup, por ser uma função especial do Arduino, não precisamos fazer a sua chamada, pois ela é chamada pelo compilador automaticamente. Mas caso fosse necessário, escreveríamos:

setup(); 

Esse tipo de função que não retorna nenhum valor, também é chamada de rotina. A função loop também não precisa ser chamada, ela é uma função padrão assim como a setup.

Inclusive, a setup é uma função que é chamada apenas uma vez, então normalmente se coloca comandos de configuração dentro dela. Já a loop, é chamada infinitas vezes (até o Arduino ser desligado), então nela normalmente são colocadas as funcionalidades do programa.

No programa Blink, ainda aparecem algumas outras funções, cada uma com sua funcionalidade: pinMode serve para configurar um pino como uma entrada ou saída digital; digitalWrite serve para passar nível lógico alto ou baixo para um pino; e delay faz o sistema ficar ocioso por um tempo contado em milissegundos.

Controle de fluxo

Quando escrevemos um programa, por vezes teremos a vontade de detalhar nossas funcionalidades e iremos querer que: alguma funcionalidade só acontece quando alguma outra coisa acontecer antes, que um mesmo comando seja executado mais de uma vez, entre outras características.

Para esse tipo de controle, utilizamos estruturas de controle de fluxo.

O exemplo que usaremos no controle de fluxo é Blink com uma coisa a mais, colocaremos um contador que será incrementado no final de toda execução da função loop:

int pinoLed = 7;
int contador = 1;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
  contador++;
 
}

While loop

Durante um projeto, pode ser que você queira que uma mesma porção de código aconteça em looping por determinada vezes.

Para isso é possível utilizar o While Loop. A sua estrutura é:

while (condição){
 
  instruções…
 
}

While” do inglês significa enquanto, então enquanto a condição estiver sendo satisfeita, as mesmas instruções serão executadas várias vezes.

Os loops (tanto o While quanto o For) fazem uma tarefa semelhante ao que a função loop faz por padrão, mas sobre os loops temos mais controle.

Vamos implementar um sistema onde enquanto o contador for menor que dez, não iremos piscar nada na tela:

int pinoLed = 7;
int contador = 1;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
 
  while(contador<10){
    contador++;
    delay(1000);
  }
 
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
  contador++;
 
}

O que estamos fazendo é “fechando” a execução dentro do while, pois o contador é inicialmente 1, e já na primeira leitura do loop, já entraremos no while. O contador será incrementado para 2, e haverá um delay de 1 segundo.

Como contador ainda é menor que 10, a leitura retorna para o início do while, incrementado o contador mais uma vez e assim sucessivamente até “contador<10” não for mais satisfeito. Quando isso acontecer, o código segue na primeira leitura do loop com as outras funções que farão o led piscar.

For loop

O For loop é uma outra opção para fazer uma porção de código ser executada mais de uma vez, a diferença está na sua estrutura. O For utiliza uma variável local que você deve criar como parâmetro. Vamos primeiro ver a estrutura para ficar mais fácil.

for(int i=0; i<=10; i++){
 
  instruções…
 
}

No primeiro parâmetro, temos a criação de uma variável “i” que só funciona dentro do For. Essa variável será incrementada automaticamente, ou seja, será somado um ao seu valor sempre que as instruções do For forem lidas.

O segundo parâmetro indica que enquanto estiver menor ou igual a 10, as instruções do For serão executadas.

Por fim, o terceiro parâmetro indica que será feito o incremento no final de cada leitura, assim como foi explicado acima (os valores de 0 e 10 são arbitrários, pode colocar qual valor preferir).

Vamos replicar o exemplo do While aqui com for:

int pinoLed = 7;
int contador = 1;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
 
  for(int i=1; i<10; i++){
    contador = i;
    delay(1000);
  }
 
  digitalWrite(pinoLed, HIGH);
  delay(1000);
  digitalWrite(pinoLed, LOW);
  delay(1000);
  contador++;
 
}

Veja que usamos a variável local “i”, atribuindo ela ao valor de contador. Poderíamos ter feito contador++ também sem problema, mas esse exemplo visa mostrar que é possível utilizar a variável “i” em operações. Perceba que à cada for loop, i é incrementado e atribuído ao contador.

If

O if é uma estrutura que permite que o Arduino só execute uma ou mais tarefas específicas, se uma relação for satisfeita. Do inglês, “if” significa “se”, então, na prática teremos a seguinte lógica: “se algo acontecer, execute isso”. A sua estrutura é a seguinte:

if (condição) {
 
  instruções…
 
}

Podemos ainda adicionar um “se algo algo acontecer, execute isso, se não acontecer, execute isto”. Esse “se não” é o else, que seria as instruções que ocorrem caso a condição do if não seja satisfeita.

if (condição) {
 
  instruções…
 
}else{
 
  instruções…
 
}

Por exemplo, vamos implementar um programa em que: quando o contador estiver entre 4 e 8, o Blink tenha um ritmo de piscar diferente. Se o contador não estiver nessa janela, pisque normalmente:

int pinoLed = 7;
int contador = 1;
 
void setup() {
  pinMode(pinoLed, OUTPUT);
 
}
 
void loop() {
 
  if(contador>4 && contador<8){
    digitalWrite(pinoLed, HIGH);
    delay(200);
    digitalWrite(pinoLed, LOW);
    delay(200);
  }else{
    digitalWrite(pinoLed, HIGH);
    delay(1000);
    digitalWrite(pinoLed, LOW);
    delay(1000);
  }
 
  contador++;
 
}

Bibliotecas

Todas as estruturas que falamos anteriormente já estão presentes por padrão na Arduino IDE, inclusive as funções que usamos nos exemplos, como pinMode, digitalWrite, entre outros. Todavia, na execução de seus projetos, você vai querer mais funções para configurar, executar instruções personalizadas, entre outras coisas. A quantidade de funções nativas, ou seja, funções que já vem por padrão, são limitadas, sendo necessário acessar bibliotecas externas para adquirir mais funções.

Para fazer isso, utilizamos o comando include. Vamos incluir, então, a biblioteca, que nos permite utilizar um display LCD, chamada de Liquid Crystal.

#include <LiquidCrystal.h>

As bibliotecas em Arduino são normalmente classes, que é um conceito de orientação a objetos, paradigma utilizado pela linguagem C++. Pensando de forma didática, ao mesmo passo que uma variável consegue armazenar valores, e funções podem armazenar várias linhas de código, uma classe pode abranger várias variáveis e funções próprias daquela classe.

Essas variáveis e funções próprias são chamadas, respectivamente, de objetos e métodos.

Voltando ao nosso exemplo, acabamos de incluir a biblioteca, então vamos acessar os seus métodos. Para isso, precisamos de um intermediário, que identifique para a biblioteca, que ela está sendo requisitada. Fazemos isso escrevendo:

LiquidCrystal meulcd(12, 11, 5, 4, 3, 2);

“meulcd” é o intermediário que usaremos, chamamos ele de objeto. Os parâmetros que foram passados entre parênteses estão relacionados a inicialização de um objeto da LiquidCrystal, cada biblioteca tem os seus parâmetros de inicialização, inclusive algumas não possuem nenhum.

Agora, podemos utilizar meulcd para usar métodos da biblioteca. Se quisermos inicializar o display, precisamos chamar o método begin.

meulcd.begin(16, 2);

A função begin recebe as dimensões do display.

Há vários outros métodos na biblioteca, e, dependendo de seu projeto, você precisará acessar a lista de funções que cada biblioteca possui. Isso é um hábito de pesquisa normal e necessário para um desenvolvedor.

Para ficar por dentro de outros conteúdos do mundo maker, nos acompanhe no Instagram e se quiser aprender mais sobre programação, confirma nossos cursos digitais!

E como sempre, qualquer dúvida você pode usar o campo de comentários abaixo =)

Faça seu comentário

Acesse sua conta e participe