Baixe o app para aproveitar ainda mais
Prévia do material em texto
AULA 5 LÓGICA E MICROCONTROLADORES Prof. Charles Way Hun Fung 2 TEMA 1 – VETORES OU ARRAY Durante o desenvolvimento de um projeto com o Arduino e sensores, é comum armazenar muitos dados em variáveis de mesmo tipo para um mesmo propósito. Um exemplo é fazer a leitura de um sensor de temperatura: imagine que esse sensor será lido uma vez a cada hora, então no final de um dia seriam necessárias 24 variáveis para armazenar todas as leituras. Além disso, seria necessário fazer o gerenciamento de todas essas variáveis com nomes diferentes. Soluções para o problema apresentado são as novas estruturas para armazenamento de dados. Nesta aula vamos introduzir o uso de vetores e matrizes que consistem em variáveis unidimensionais ou multidimensionais, respectivamente. Segundo Oliveira (2017), vetor ou array é uma estrutura unidimensional que armazena diversos valores de mesmo tipo. A figura a seguir ilustra um vetor. Figura 1 – Estrutura de um vetor Perceba que, na Figura 1, dentro de cada quadrado há o conteúdo de uma variável que está contida no vetor. O número que se encontra sobre esses quadrados são os índices, que indicam a posição de cada variável. Pode-se dizer que a posição zero do vetor possui conteúdo ou valor igual a 5. Da mesma forma, pode-se dizer que a posição 1 possui o valor 8, e assim por diante. O vetor da Figura 1 possui um tipo; no caso, o que melhor se enquadra na figura é o tipo inteiro, mas da mesma forma que as variáveis, os vetores possuem um tipo dentre os possíveis na linguagem C/C++. A declaração de um vetor segue o formato <tipo> <nome vetor>[<tamanho do vetor>]. Por exemplo: int numeros[10]; Esse exemplo declara um vetor com o nome de “numeros” do tipo inteiro de 10 posições. 3 Para atribuir um valor para uma posição do vetor, é necessário usar o nome do vetor, a posição entre colchetes – “[ ]” – seguido do operador de atribuição e o valor, como mostrado no exemplo a seguir. numeros[2] = 5; Além disso, é possível inicializar um vetor com valores específicos fazendo uma atribuição direta da seguinte forma. int vet[] = {1, 5, 10, 7, 4, 2, 2, 9, 20, 15}; Nessa atribuição, não é necessário adicionar o tamanho do vetor, apenas o conteúdo entre chaves e cada elemento separado por vírgulas. Quando o vetor é do tipo caractere, a atribuição no momento da declaração é um pouco diferente: char frase[] = “Ola mundo”; Perceba que, nessa declaração, o vetor recebe o texto entre aspas duplas. Cada caractere vai ocupar uma posição específica no vetor, como na figura a seguir. Figura 2 – Vetor de caracteres Um vetor de caracteres é conhecido como Strings; vamos abordá-lo em um tema adiante sobre esse tipo de vetor. Como se pode ver na Figura 2, a frase da declaração é colocada no vetor caractere por caractere até um finalizador ‘\0’, que indica o termo nulo dessa linguagem. Esse caractere nulo é importante como finalizador de String, já que variáveis caracteres podem assumir qualquer valor. TEMA 2 – USANDO VETORES NO ARDUINO Para exemplificar o uso de vetores com o Arduino, serão realizadas diversas leituras de uma distância utilizando um sensor de ultrassom. Para isso, será feito o uso de um sensor ultrassônico HC-SR04, que possui alcance de 2 cm a 4 m. Esse sensor é apresentado na figura a seguir. 4 Figura 3 – Sensor de ultrassom Crédito: Pozdeyev Vitaly/Shutterstock. O sensor de ultrassom HC-SR04 possui quatro pinos: VCC, Trigger, Echo e GND. O primeiro pino corresponde aos 5 V e GND representa o terra. O pino Trigger indica o som deve ser enviado, enquanto o Echo é usado para receber o dado quando retorna. O cálculo da distância se baseia no tempo que a onda sonora demora para ir e voltar ao objeto da medição da distância. A Figura 4 apresenta o sensor de ultrassom com o Arduino. No Tinkercad, o sensor ultrassônico possui apenas um pino de sinal, que, no nosso exemplo, está conectado ao pino digital 7. Nesse exemplo, é realizada uma medição da distância a cada 10 s; após um minuto, apresenta-se a média dos valores no terminal serial. Figura 4 – Circuito com ultrassom 5 Figura 5 – Código para leitura do ultrassom 6 No código apresentado na Figura 5 (linha 8), é feito uso da função “distanciaUltrassom”, que calcula o tempo que o sinal ultrassônico levou para ir do sensor até o objeto. Essa configuração, que estabelece o pino de Trigger como saída na linha 11, serve para tornar flexível o uso desse pino; na prática, podemos colocar qualquer pino digital como Trigger. Da mesma forma, o pino de Echo que é como entrada (INPUT), na linha 19. As linhas 12, 13, 15, 16 e 17 fazem a transmissão de um pulso de 10 µs pelo Trigger, o qual é representado pelas linhas 15 e 16, que deixam o pino de Trigger em HIGH. O retorno desse pulso é registrado na linha 22, que possui a função “pulseIn”, que retorna o tempo para receber esse sinal em Echo. Esse tempo é retornado por essa função e será usado para determinar a distância até o objeto por meio de uma equação de conversão para centímetros. Na função “loop()”, na linha 33, temos a conversão do valor do tempo para centímetros. Essa equação foi retirada da documentação fornecida pelo fabricante do sensor de ultrassom. Na linha 34, o valor da distância em centímetros é colocado no vetor, na posição determinada por “pos”. Essa variável serve para controlar em qual posição do vetor será armazenado o valor da medição. As linhas de 36 a 41 mostram essa medição no terminal serial. Após o armazenamento dos dados, a variável “pos” é incrementada na linha 42, que indica que a próxima posição do vetor está pronta para receber um novo valor. Na linha 43, há uma verificação quanto ao “pos” ter chegado ao valor 6. Como o vetor tem apenas 6 posições e as posições no vetor vão de 0 a 5, não seria possível armazenar dados na sexta posição, logo, deve-se fazer o cálculo da média desses valores. Para realizar esse cálculo, foi usado um “loop for” na linha 44, que usa uma variável de controle i, que começa em 0 e vai até o valor 5; quando chega ao valor 6, encerra o “loop”. Nesse laço, é realizada a soma dos valores das medições e colocado na variável “media”, na linha 45. Perceba que a variável “media” acumula os valores das medições: media = media + medidas[i]; Essa equação diz que “media vai receber o valor de media mais o valor da posição do vetor determinada por i”. A variável “media” começa com o valor zero e, com o passar das iterações, vai acumulando o valor de medidas. Quando o “loop” termina, o valor de “media” é dividido por 6 e mostrado no terminal nas linhas 49 a 51, como mostrado na figura a seguir. 7 Figura 6 – Resultado apresentado pelo terminal serial Após a apresentação desse resultado, o valor da variável “media” é zerado na linha 52. Para que a leitura seja feita a cada 1s, é usado um “delay” na linha 55. TEMA 3 – VETOR DE CARACTERES OU STRINGS Os vetores de caracteres precisam de atenção especial por possuir um tipo próprio, o tipo String. Toda vez que se desejar escrever uma frase ou palavra na linguagem wiring, deve-se fazer uso de formas específicas dos comandos. Primeiramente, a forma que é escrita uma sequência de caracteres nessa linguagem é entre aspas duplas: “<Texto>” Exemplo: char nome[] = “José”; char frase[] = “Ola mundo!”; Um detalhe importante na linguagem é a diferença entre um caractere e uma String; o primeiro é representado entre aspas simples, por exemplo, ‘A’, ‘c’, ‘3’, indicando o caractere A maiúsculo, c minúsculo e o caractere 3, respectivamente. No caso da String, é representada entre aspas duplas, como mencionado anteriormente, por exemplo: “Teste”, “Ola mundo” e “A”. Uma dúvida comum do estudante dessa linguagem é diferenciar ‘A’de “A”, porque aparentemente parecem ser a mesma coisa, mas não são. O primeiro é o caractere A maiúsculo, o segundo é uma String que contém o A maiúsculo, mas não é só isso, a String é composta pelo seu conteúdo seguido pelo caractere nulo (‘\0’), como mostrado na figura a seguir. 8 Figura 7 – Exemplo de String com o nulo para finalização O formato do caractere nulo pode parecer estranho, mas é exatamente esse formato, com um caractere barra seguido do caractere zero. O nulo tem uma representação na linguagem que não deve ser confundida com o zero. Ambos possuem significados diferentes. No Arduino, as Strings são utilizadas principalmente na comunicação serial, na qual se pode mostrar mensagens e valores de variáveis. No decorrer do curso, já usamos diversas vezes a comunicação serial, mas fundamentalmente essa comunicação funciona da seguinte forma. 1. Configuração da taxa de comunicação. 2. Envio de mensagens. A configuração é realizada na função “setup()”, na qual é chamada a função “Serial.begin”, como mostrado no exemplo a seguir. Serial.begin(9600); Entre parênteses se encontra a taxa de comunicação, esta deve ser igual tanto no Arduino como no receptor, no caso, o monitor serial. Nas mensagens enviadas na serial, há duas funções básicas: • Serial.print: escreve a String que está determinada entre parênteses. Por exemplo: Serial.print(“Ola mundo”); Essa função não pula para a próxima linha do monitor serial assim que finaliza sua execução; • Serial.println: escreve a String que está determinada entre parênteses, pulando de linha no monitor serial após sua execução. Por exemplo: Serial.println(“Teste”); Além disso, as Strings podem ser usadas para converter diversos tipos de dados: 9 1. Converter um caractere para uma String: String teste = String(‘a’); 2. Converter um constante inteiro: String teste = String(12); 3. Converter um número inteiro para uma base numérica: a. Base decimal: String teste = String(x, DEC); em que x é uma variável; b. Base hexadecimal: String teste = String(32, HEX); c. Base binária: String teste = String(255, BIN); 4. Converter um float com casas decimais: String teste = String(1.523, 3); 5. Concatenar duas Strings: String s = “ola”; String teste = String(s+” mundo”); TEMA 4 – MATRIZES Da mesma forma que vetores, as matrizes são um conjunto de dados de mesmo tipo, porém possuem duas dimensões: linhas e colunas. Elas são formas bidimensionais de armazenar dados. A figura a seguir ilustra o formato de uma matriz 4x5 (4 linhas e 5 colunas). Figura 8 – Exemplo de matriz A matriz da Figura 8 mostra como os dados se distribuem na matriz. Perceba que as linhas e colunas são numeradas: linhas vão de 0 a 3 e colunas de 0 a 4, formando a matriz 4x5. 10 A forma de declaração de uma matriz segue um modelo parecido ao vetor, porém com duas dimensões: <tipo> <nome matriz>[<linhas>][<colunas>]; Exemplo: int leituras[4][5]; Nesse exemplo, é declarada uma matriz inteira com o nome de leituras com quatro linhas e cinco colunas. As matrizes aceitam todos os tipos de dados: inteiro (int), float (real), char (caractere), double (real com maior precisão). Também é possível declarar os valores de uma matriz diretamente na declaração, por exemplo: int leituras[][] = {{1,2,3},{4,5,6},{7,8,9}}; Para atribuir um valor para uma determinada posição de uma matriz, deve- se especificar a linha e a coluna dessa posição e usar o operador de atribuição, como no exemplo a seguir. leituras[2][3] = 5; Essa linha significa que a variável inteira que se encontra no cruzamento entre a linha 2 e a coluna 3 receberá o valor de 5. Isso pode ser ilustrado mudando o valor da posição de linha 2 e coluna 3 para 5, na Figura 8. Figura 9 – Matriz da Figura 8 modificada com novo valor da posição 2x3 Em uma matriz, é comum fazer uma varredura por todos os componentes. Isso é útil tanto para atribuir valores para todas as posições da matriz como para procurar algum valor específico. Por exemplo: Contar quantas posições na matriz da figura 9 têm valor superior a 10. 11 Para resolver esse problema, devemos criar duas variáveis para contar a quantidade de linhas e colunas. int linha; int coluna; Nesse caso, essa matriz tem quatro linhas e cinco colunas, então, para cada coordenada, devemos fazer um “loop for” da seguinte forma. for(linha=0;linha<4;linha++){ for(coluna=0;coluna<5;coluna++){ } } O funcionamento desse código é baseado na mecânica dos “loops”. A ideia é bem simples, fixa um valor da linha e preenche os valores na respectiva linha. Em seguida, incrementa o valor da linha e preenche os valores da linha, processo que se repete até finalizar as linhas. No código, a variável “linha” começa com um valor zero, e verifica-se a condição de linha<4; esta é verdadeira. Sendo assim, vai executar os comandos dentro do primeiro “for”. Nos comandos, há um novo “for”, nesse caso a variável de controle é coluna, que começa com zero e também tem uma condição verdadeira – em breve comentaremos sobre os códigos no interior desse “for” que vão ser executados. Assim que esse “for” interno terminar, a variável “linha” será incrementada em uma unidade; nesse caso, vai para o valor 1. A condição do “for” externo ainda continua sendo verdadeira, reiniciando o “for” interno. Essa repetição desse código vai continuar até que todas as linhas sejam verificadas. Perceba que cada posição da matriz é explorada com esse código, na seguinte ordem. [0][0], [0][1], [0][2], [0][3], [0][4], [1][0], [1][1], [1][2], [1][3], [1][4], [2][0], [2][1], [2][2], [2][3], [2][4], [3][0], [3][1], [3][2], [3][3], [3][4]. Perceba que, como mencionado anteriormente, o valor da linha é fixado para, em seguida, suas posições de coluna serem verificadas. 12 No problema proposto, deve-se contar a quantidade de posições da matriz que possui valor superior a 10. Para isso, devemos criar uma variável para realizar essa contagem; chamaremos essa variável de contador, que deverá começar zerada para que não ocorra nenhum erro na contagem das posições da matriz. int contador = 0; O resto do código é apresentado a seguir. for(linha=0;linha<4;linha++){ for(coluna=0;coluna<5;coluna++){ if(leituras[linha][coluna]>10){ contador++; } } } Serial.println(“A quantidade de valores é ”+contador); No “loop” interno, temos um comando de comparação que verifica se a posição analisada possui um conteúdo com valor superior a 10. Caso isso seja verdade, o comando “contador++”, que incrementa o contador em uma unidade, é executado. No final, o valor de contador é mostrado na serial. TEMA 5 – EXEMPLO DO USO DE MATRIZ COM O ARDUINO Nesse exemplo, faremos a leitura de três sensores de temperatura e vamos armazenar as leituras em uma matriz. Nesse caso, serão feitas 10 leituras e serão mostradas as médias das leituras na serial. O esquemático do circuito para realizar a leitura das temperaturas é apresentado a seguir. 13 Figura 10 – Leitura de três sensores de temperatura O sensor de temperatura utilizado é o TMP36, que possui três pinos; o pino da direita é a alimentação de 5 V, na esquerda é o GND (Terra), o qual está conectado ao GND do Arduino. O pino do meio é uma saída analógica que está conectada às entradas analógicas do Arduino. Nesse exemplo, utilizaremos as entradas A0, A1 e A2. O código para resolver esse problema será separado em partes, para melhorar a compreensão. Figura 11 – Declaração e variáveis e função setup Na Figura 11, as declarações de variáveis são apresentadas. As variáveis declaradas na linha 1, temperatura1, temperatura2 e temperatura3 serão usadas para receber diretamente a medição de temperatura dos sensores, porém será realizada uma conversão para que na variável esteja o valorem graus Celsius. 14 Temp_media1, temp_media2, temp_media3 são as médias das temperaturas dos sensores que serão mostradas na serial. A variável “num_medida” será usada para indicar quantas leituras foram realizadas; caso esse número chegue a 10, deverá ser realiza a média dos valores. A matriz medidas[3][10] será onde os dados das medições serão armazenados. Cada linha dessa matriz registra os dados de um único sensor, sendo a linha zero o primeiro sensor, a linha 1 o segundo sensor e a linha 2 o terceiro sensor. Como o total de medidas é dez, foram criadas dez colunas de dados. Em uma matriz dessas dimensões, não é possível armazenar mais valores que dez em cada linha; caso seja necessário armazenar mais dados, deve-se declarar a matriz com dimensões maiores. A função “setup” é usada apenas para configurar a comunicação serial com uma taxa de comunicação de 9.600 bps (bits por segundo). Figura 12 – Aquisição e armazenamento dos dados A Figura 12 mostra o começo da função “loop”; nela são apresentadas as linhas 14 a 16, em que há aquisição dos dados pelas entradas analógicas. A função “map” converte os valores recebidos, que seriam de 0 a 1.023, para -40 a 125, que é o intervalo de temperatura que o sensor TMP36 opera. Após a aquisição, o valor de temperatura é colocado nas variáveis temperatura1, temperatura2 ou temperatura3, dependendo da entrada analógica que o sensor está conectado. Nas linhas 18 a 20, os dados de temperatura são armazenados, sendo a linha 0 para o primeiro sensor, linha 1 para o segundo sensor e linha 3 para o terceiro sensor, organizando, dessa forma, uma linha para 15 cada sensor de temperatura. Após o armazenamento desses dados, a variável “num_medida” é incrementada para que seja possível armazenar a próxima leitura dos sensores que ocorrerá após um segundo, devido ao “delay(1000)” na linha 24. Figura 13 – Cálculo da média das medições, apresentação no terminal serial e zeramento das variáveis Quando a variável “num_medida” atingir o valor 10, indicará que já foram realizadas dez medições; nesse caso, o cálculo da média deverá ser realizado. A média é calculada por meio do somatório das medições dividido pela quantidade de medidas. Por isso o somatório das medidas dos sensores é realizado nas linhas 30 a 34. Na linha 30, tem um “loop for” que faz a variável i variar de 0 a 9; nas linhas seguintes, temos as variáveis “temp_media1”, “temp_media2” e “temp_media3”, tendo seu valor acrescido de uma nova medição – essas linhas à linha 31 serão explicadas com mais detalhes para facilitar o entendimento. Na linha 31, a variável “temp_media1” está na sentença: temp_media1 +=medidas[0][i]; 16 O operador “+=” pode ser traduzido na sentença a seguir. temp_media1 = temp_media1+medidas[0][i]; Essa sentença indica que a variável “temp_media1” receberá o valor atual dela mais o valor de medidas[0][i]. O valor de medidas[0][i] dependerá do valor de i; isso indica que o valor dessa variável será na matriz na linha 0 e coluna i. Esse valor será adicionado ao valor atual de “temp_media1” e substituirá o valor atual da variável. Após esse somatório, ocorrerá a divisão por 10 em cada uma das variáveis que guarda a média nas linhas de 36 a 38. Em seguida, os valores de média são mostrados no terminal serial usando as funções “print” e “println” nas linhas de 41 a 46. Esse resultado pode ser visto na figura a seguir. Figura 14 – Resultado no terminal serial Para finalizar, as variáveis de média “temp_media1”, “temp_media2” e “temp_media3”, em conjunto com a variável “num_medida”, foram zeradas para que o processo de medição recomece. 17 REFERÊNCIAS ALVES, W. P. Linguagem e lógica de programação. São Paulo: Érica, 2014. ASCENCIO, A. F. G. Fundamentos da programação de computadores. São Paulo: Prentice Hall, 2002. FORBELLONE, A. L. V. Lógica de programação: a construção de algoritmos e estrutura de dados. 3. ed. São Paulo: Prentice Hall, 2005. MCROBERTS, M. Arduino básico. São Paulo: Novatec, 2011. OLIVEIRA, C. L. V. Arduino descomplicado: aprenda com projetos de eletrônica e programação. São Paulo: Érica, 2017.
Compartilhar