Simplifique seus programas com o uso de HashMaps e outras estruturas de dados em Arduino
Introdução
Quando se trata de programação de computadores (ou microcontroladores), a escolha da estrutura de dados a ser utilizada pode tornar um problema complicado em um de solução relativamente simples ou vice-versa!
A plataforma Arduino disponibiliza em sua API várias opções de tipos de dados compostos que podem ser utilizados pelo programador como por exemplo: Arrays, Strings, Structs, etc. No entanto, algumas vezes essas estruturas não são suficientes para a resolução de determinados tipos de problemas e, então, precisamos buscar soluções em bibliotecas externas.
Neste artigo, vamos demonstrar o uso de uma estrutura de dados muito conhecida entre os programadores PHP e Python: Os Arrays Associativos, também conhecidos como HashMaps ou Dicionários. Veremos como eles podem facilitar sua vida.
Antes disso, vamos recordar algumas tipos de dados estruturados básicos da plataforma Arduino…
Arrays
Arrays ou vetores são estruturas de dados que representam um conjunto de variáveis com o mesmo nome identificadas individualmente através de um índice.
Na linguagem de programação do Arduino, os arrays possuem as seguintes características:
- Todos elementos devem possuir o mesmo tipo;
- O número de elementos é imutável;
- O índice é um número sequencial que inicia em 0 (zero) e vai até (N-1), onde N é o número de elementos do array
Portanto, para declararmos um array, devemos informar 3 coisas:
- O nome do vetor
- O número máximo de elementos que ele vai conter
- O tipo de dados dos elementos.
Por exemplo, a declaração abaixo vai criar um array com 3 elementos inteiros:
int myPin[] = {2, 4, 8};
Onde myPin[0] = 2, myPin[1] = 4 e myPin[2] = 8.
Strings
Uma String na linguagem do Arduino é uma classe que representa um conjunto de caracteres e disponibiliza vários métodos para manipulação de textos.
Cada caractere da String pode ser acessada por um índice numérico que inicia em 0 (zero) e vai até (N-1), onde N é o tamanho da String.
Exempo: O comando abaixo cria uma String de 7 caracteres com o valor “Arduino”:
String board = “Arduino”;
Onde:
board.length() = 7 → Tamanho da String
board.indexOf(0) = “A” → Primeira letra da String
board.indexOf(“i”) = 4 → Posição da letra “i” na string
board.toUpperCase() = “ARDUINO”→ Transforma as letras para maiúsculo
Arrays Associativos
Trata-se de um tipo de array ondes os elementos são formados por um par chave e valor (key-value pairs), no qual cada chave possui um valor associado. Essa chave pode assumir qualquer tipo de valor, inclusive Strings. As chaves são definidas pelo usuário e são armazenadas na estrutura. O relacionamento existente entre as chaves e seus respectivos valores é chamado de mapeamento, pois, para buscar um valor utiliza-se a chave como índice de busca. Pode-se buscar o valor de um elemento pela chave e também verificar se existe algum elemento relacionado àquela chave.
A principal vantagem existente na utilização de vetores associativos está na facilidade de realização de buscas por valores, que é mais intuitiva.
Existem algumas implementações de HashMaps que podem ser usadas com o Arduino. Destacamos os seguintes frameworks:
- Wiring – An open-source programming framework for microcontrollers
- STL – The Standard Template Library
Neste artigo, vamos adotar nos exemplos a biblioteca da plataforma Wiring, que inclui um implementação de HashMaps desenvolvida por Alexander Brevig. Você vai precisar “importar” os arquivos hashmap.h e Countable.h para a pasta libraries do Arduino.
Veja um exemplo onde vamos criar um array associativo com 3 elementos inteiros com índices do tipo String:
CreateHashMap(myPin, char*, int, 3);
myPin[“Sensor”] = 2;
myPin[“Led”] = 4;
MyPin[“Buzzer”] = 8;
Observe, no exemplo que associamos a chave “Sensor” ao valor 2. A vantagem dessa implementação é que podemos identificar o primeiro elemento por sua chave “Sensor” ou pelo seu índice que é 0 (zero).
Para entendermos melhor esses conceitos, vamos elaborar um projeto simples como exemplo e resolvê-lo de 3 formas distintas:
- Sem Estruturas de Dados
- Utilizando Strings
- Utilizando Arrays Associativos
Estudo de caso
O nosso projeto consiste de 3 leds coloridos ligados aos pinos 5, 7, 9 e 11 do Arduino, conforme figura abaixo:
A aplicação em Arduino vai ler valores String pela porta serial em um loop infinito. Para cada valor lido, a seguinte ação será executada:
- Se leitura = “red” -→ Acender led vermelho
- Se leitura = “yellow” → Acender led amarelo
- Se leitura = “green” → Acender led verde
- Se leitura = “blue” → Acender led azul
Sketchs
Solução 1: Sem Estruturas de Dados
/* Arduino & 4 leds * Solution 1: Without Data Structures * 2016, by José Cintra * http://www.josecintra.com/blog */ String reading = ""; int redPin = 5; int greenPin = 7; int bluePin = 9; int yellowPin = 11; void setup() { Serial.begin(9600); pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); pinMode(yellowPin, OUTPUT); } void loop() { reading = readSerial(); //Reads value from serial ledsOff(); // Turn off all leds if (reading == "red") { digitalWrite(redPin,HIGH); } else if (reading == "green") { digitalWrite(greenPin,HIGH); } else if (reading == "blue"){ digitalWrite(bluePin,HIGH); } else if (reading == "yellow"){ digitalWrite(yellowPin,HIGH); } } //Read String from Serial String readSerial() { String retVal = ""; char c; while(!Serial.available()){ //Wait for serial input delay(10); } while(Serial.available() > 0) { //Read the serial input c = Serial.read(); if (c != '\n'){ retVal.concat(c); } delay(10); } retVal.toLowerCase(); //Convert to lower case return retVal; } //Turn off all leds void ledsOff(){ digitalWrite(redPin,LOW); digitalWrite(greenPin,LOW); digitalWrite(bluePin,LOW); digitalWrite(yellowPin,LOW); }
Solução 2: Usando Arrays e Strings
/* Arduino & 4 leds * Solution 2: With Arrays & Strings * 2016, by José Cintra * http://www.josecintra.com/blog */ String reading = ""; int pins[] = {5,7,9,11}; String commands = "red green blue yellow "; void setup() { Serial.begin(9600); for(int i = 0; i < 4; i++){ pinMode(pins[i], OUTPUT); } } void loop() { reading = readSerial(); //Read value from serial ledsOff(); // Turn off all leds int i = commands.indexOf(reading); if (i >= 0) { digitalWrite(pins[(i / 10)],HIGH); } } //Read String from Serial String readSerial() { String retVal = ""; char c; while(!Serial.available()){ //Wait for serial input delay(10); } while(Serial.available() > 0) { //Read the serial input c = Serial.read(); if (c != '\n'){ retVal.concat(c); } delay(10); } retVal.toLowerCase(); //Convert to lower case return (retVal); } //Turn off all leds void ledsOff(){ for(int i = 0; i < 4; i++){ digitalWrite(pins[i],LOW); } }
Solução 3: Usando HashMaps
/* Arduino & 4 leds * Solution 3: With HashMaps * 2016, by José Cintra * http://www.josecintra.com/blog */ #include <HashMap.h> String reading = ""; CreateHashMap(ledPins, String, int, 4); void setup() { Serial.begin(9600); ledPins["red"] = 5; ledPins["green"] = 7; ledPins["blue"] = 9; ledPins["yellow"] = 11; for (int i = 0; i < ledPins.size(); i++){ pinMode(ledPins.valueAt(i), OUTPUT); } } void loop() { reading = readSerial(); ledsOff(); if(ledPins.contains(reading)){ digitalWrite(ledPins[reading],HIGH); } } //Read String from Serial String readSerial() { String retVal = ""; char c; while(!Serial.available()){ //Wait for serial input delay(10); } while(Serial.available() > 0) { //Read the serial input c = Serial.read(); if (c != '\n'){ retVal.concat(c); } delay(10); } retVal.toLowerCase(); //Convert to lower case return retVal; } //Turn off all leds void ledsOff(){ for (int i = 0; i < ledPins.size(); i++){ pinMode(ledPins.valueAt(i), LOW); } }
Pontos de Interesse
- A função readSerial, presente nas 3 soluções tem a função de aguardar e ler um valor String pela porta serial;
- A função ledsOFF tem a função de apagar todos os leds antes de acender o led solicitado pelo comando da serial;
- Repare, comparando a função loop das 3 soluções, que a alternativa usando arrays associativos é bem mais legível, compacta e manutenível;
- A alternativa 2 com arrays e strings tem a vantagem de não precisar usar nenhuma biblioteca externa.
Recursos
- https://en.wikipedia.org/wiki/Associative_array
- https://www.arduino.cc/en/Reference/StringObject
- https://www.arduino.cc/en/Reference/Array
- Versão em inglês do artigo
Conclusão
Neste artigo procuramos mostrar que a escolha de estruturas de dados apropriadas pode resultar em programas mais intuitivos e compactos, facilitando o trabalho do programador.
Saudações Klingonianas…
O post HashMaps – Arrays Associativos com Arduino apareceu primeiro em BlogDoJoséCintra.