Nesse post, vamos continuar o desenvolvimento da nossa biblioteca para LCDs alfanuméricos com Arduino. Antes de iniciar a leitura, é importante ter lido a primeira parte, as apostilas da Eletrogate e ter conhecimento da linguagem C++.
(Esse post continua a criação da biblioteca “omincrystal”. Caso esteja buscando exemplos relacionados à configuração/publicação de uma biblioteca, sinta-se livre para pular para o tópico: “Configurando library.properties”)
No último post, falamos sobre:
Por fim, criamos um .zip do código e adicionamos ao Arduino IDE pelo gerenciador de bibliotecas. Isso já é o suficiente para a criação de uma biblioteca de uso pessoal. Agora, vamos criar novas funcionalidades nesse projeto e configurar corretamente para compartilharmos com o público.
Nosso objetivo é criar uma biblioteca capaz de rodar em todas as placas da plataforma Arduino. Você pode usar uma placa de sua preferência. Neste post, vamos usar Arduino Uno R3; a placa NodeMCU ESP-12E é apenas para realizar os testes de compatibilidade antes da publicação.
o circuito é igual ao do último post:
Acompanhe o desenvolvimento em: https://github.com/RecursiveError/omnicrystal
No último post, adicionamos a biblioteca .zip a IDE. Mas, para enviar uma nova versão dessa mesma biblioteca por esse meio, vamos ter o seguinte erro:
Como o erro diz, a biblioteca já está instalada, porém, em uma versão diferente. Então, para atualizarmos, temos que, manualmente, apagar a biblioteca já instalada e, depois, enviar o novo .zip. Para isso, vá até Arquivo → preferências:
No campo “localização dos sketchbooks”, vai ter o caminho para a pastas onde seus sketches estão salvos. As bibliotecas instaladas pelo usuário também são salvas nessa pasta.
Vá até essa pasta e você vai encontrar uma pasta chamada “libraries”:
abra essa pasta e você vai achar suas bibliotecas instaladas e uma pasta nomeada “_”. Essa pasta é a nossa biblioteca; está com esse nome porque library.properties ainda não foi configurado
apague esse pasta e pronto: basta enviar um novo .zip para instalar, como mostra no final da primeira parte.
(caso essa pasta não exista, tente atualizar a IDE para uma versão mais recente. Caso atualização não seja possível, verifique no site da Arduino pela localização de bibliotecas para versões antigas da IDE)
Atualmente, nosso projeto só é capaz de enviar texto, o que não é muito útil para quem precisa usar um LCD. Precisamos adicionar funções para:
vamos criar cada e testar essas funcionalidades uma a uma, seguindo essa ordem.
Começando pela criação de uma função capaz de enviar vários tipos de dados para o LCD, temos que criar uma versão da mesma função para cada tipo de dado diferente, como int, float, bool, byte….etc, e transformar esses dados em caracteres para enviarmos a o display. Isso é um trabalho muito complexo e repetitivo. Foi pensando nisso que a Arduino criou a biblioteca “Print.h” no seu Core. Você já deve ter visto os métodos “print” e “println” em algum lugar, como, por exemplo, “Serial.println()”. Essas são as funções da “Print.h” e já fazem todo trabalho de receber e formatar vários tipos de dados. Para implementar elas no seu código, é bem simples: primeiro, você herda de forma publica a classe Print e cria um método virtual publico write: virtual size_t write(uint8_t);
(devido ao tamanho do código vamos abreviar as partes já criadas com “. . .” para facilitar a leitura das adições. O código completo vai ser disponibilizado ao final de cada etapa)
omnicrystal.h:
//classe principal #ifndef OMNICRYSTAL_H #define OMNICRYSTAL_H ... class Omnicrystal : public Print{ private: ... public: ... virtual size_t write(uint8_t); //adição da função virtual write na nossa classe principal }; #endif
omnicrystal.cpp
#include <omnicrystal.h> ... // implementação da função virtual write size_t Omnicrystal::write(uint8_t data){ send(data, 1); return 1; }
O retorno size_t da função write indica se a função executou com sucesso. Como nosso LCD não verifica erros, sempre retornamos 1, ou seja, sempre assume sucesso na execução.
Agora, vamos fazer o processo para atualizar a biblioteca que vimos anteriormente. Vá até a pasta do projeto; compacte em .zip, como foi mostrado na parte 1, vá até libraries; apague manualmente e instale o novo zip.
(Download da versão dessa etapa: https://github.com/RecursiveError/omnicrystal/releases/download/v0.2.0-alpha/omnicrystal.zip)
Testando na Arduino Uno:
#include <omnicrystal.h> LCDParallel LcdInter(6,7,2,3,4,5,8,9,10,11); // interface paralela usada Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); void setup() { lcd.begin(); // inicia o display lcd.print("NUM:"); lcd.print(255); // envia o numero 255 lcd.print(" HEX:"); lcd.print(255, HEX); //envia o numero 255 em HEXADECIMAL } void loop() {}
Resultado:
Nossa biblioteca, agora, é capaz de enviar vários tipos de dados para o LCD. A próxima etapa é enviar comandos para o LCD.
Como vimos na tabela de comandos de LCD, existem certos comandos que alteram o comportamento do LCD e comandos que realizam operações sem alterar o comportamento
(https://www.futurlec.com/LED/LCD16X2BLa.shtml)
Para facilitar, vamos dividir esses comandos em dois grupos: “command”, que realizam operações que não afetam o comportamento do LCD (Exemplo: limpar o LCD) e “config”, que altera o comportamento do LCD (Exemplo: ligar e desligar o curso).
Começando com o command, no nosso header (.h), vamos criar uma enum para listar todos os possíveis comandos e funções para enviar cada um dos comandos:
omnicrystal.h
//classe principal #ifndef OMNICRYSTAL_H #define OMNICRYSTAL_H ... //comandos do LCD enum LCDCommand { LCDClear = 0x01, LCDReset = 0x02, LCDShiftCursotLeft = 0x10, LCDShiftCursotRight = 0x14, LCDShiftDisplayLeft = 0x18, LCDShiftDisplayRight = 0x1C, }; class Omnicrystal : public Print{ private: ... public: ... Omnicrystal& command(LCDCommand); //funçoes para enviar cada um dos comandos Omnicrystal& clear(); Omnicrystal& reset(); Omnicrystal& move_cursor_left(); Omnicrystal& move_cursor_right(); Omnicrystal& move_display_left(); Omnicrystal& move_display_right(); ... }; #endif
omnicrystal.cpp
... /* ----------------- COMANDOS DO DISPLAY ---------------------*/ //envia comandos para o display inline Omnicrystal& Omnicrystal::command(LCDCommand command){ send(command, 0); //0 indica RS em low, significa que vamos envar um comando return *this; //apenas para encadeamento de metodos } Omnicrystal& Omnicrystal::clear(){ command(LCDClear); delay(2); //clear e reset demoram 1.52ms para executar return *this; } Omnicrystal& Omnicrystal::reset(){ command(LCDReset); delay(2); //clear e reset demoram 1.52ms para executar return *this; } inline Omnicrystal& Omnicrystal::move_cursor_left(){ command(LCDShiftCursotLeft); return *this; } inline Omnicrystal& Omnicrystal::move_cursor_right(){ command(LCDShiftCursotRight); return *this; } inline Omnicrystal& Omnicrystal::move_display_left(){ command(LCDShiftDisplayLeft); return *this; } inline Omnicrystal& Omnicrystal::move_display_right(){ command(LCDShiftCursotRight); return *this; } /* ----------------- FIM DOS COMANDOS DO DISPLAY ---------------------*/
a keyword “inline” usada é para sugerir para o compilador incorporar a função no código em vez de chama-la. Um exemplo: veja que a única coisa que a função “move_display_right()” faz chamar é função “commad()”, que a única coisa que faz é chamar “send()”. Usando a keyword “inline”, estamos dizendo para o compilador que ele pode optar por não criar uma chamada de função, e executar o conteúdo dela como se fosse parte daquela linha de código. Ou seja, quando adicionamos o “inline” e chamamos a função “move_display_right()”, ele pode otimizar e simplesmente chamar send(valor), sem chamar “move_display_right()” e “command()”. Isso permite criar abstrações para facilitar o uso da nossa biblioteca sem perder desempenho.
( “inline” é apenas uma sugestão para o compilador. Seu uso incorreto pode aumentar drasticamente o consumo de memoria)
Falta apenas um comando: o comando para mover o cursor na coluna e linha. Mas, vamos deixar esse comando para depois de criar as funções para configuração, pois é nessas funções que vamos definir quantas linhas tem o LCD.
Vamos testar nosso código. Mesmo processo feito anteriormente: apagar manualmente e enviar o novo .zip
(Download da versão dessa etapa: https://github.com/RecursiveError/omnicrystal/releases/download/v0.3.0-alpha/omnicrystal.zip)
#include <omnicrystal.h> LCDParallel LcdInter(6,7,8,9,10,11); // interface paralela usada Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); int num = 0; void setup() { lcd.begin(); // inicia o display lcd.print("NUM:"); lcd.print(255); // envia o numero 255 lcd.print(" HEX:"); lcd.print(255, HEX); //envia o numero 255 em HEXADECIMAL } void loop() { delay(1000); lcd.clear().reset().print("comando teste"); delay(1000); lcd.clear().reset().print(num); num++; delay(1000); }
Resultado:
Agora, vamos criar funções para configurar o LCD. Temos 3 tipos de configurações: “Entry mode”, que configura a direção de escrita do LCD; “Display control”, que configura exibição, e “Function Set”, que configura a interface de comunicação, quantidade de linhas e tamanho da fonte.
Como essas configurações mudam o comportamento do LCD, temos que guardar seu estado em alguma lugar. Então, vamos criar atributos e enums para cada tipo de configuração e, claro, funções para enviar essas configurações
omnicrystal.h
//classe principal #ifndef OMNICRYSTAL_H #define OMNICRYSTAL_H ... enum BusType { Bus4Bits, Bus8Bits = 0x10, }; //comandos do LCD enum LCDCommand { ... }; enum LCDEntryMode{ LCDShiftMode = 0x01, LCDDirection = 0x02, }; enum LCDDisplayControl{ LCDBlink = 0x01, LCDCursor = 0x02, LCDDisplay = 0x04, }; enum LCDFunctionSet{ LCDChar = 0x04, LCDLines = 0x08, LCDBits = 0x10, }; enum LCDCharSize { Char5x8, Char5x10 = 0x04, }; class Omnicrystal : public Print{ private: ... // variaveis de configuração inicializadas com a configuração padrão uint8_t entry_mode{0x06}; // shift Off, texto esquerda pra direita uint8_t display_control{0x0C}; //display ligado, cursor desligado, cursor piscando desligado uint8_t function_set{0}; // configurado no construtor ... public: Omnicrystal(LCDInterface &bridge, const BusType bus, uint8_t line, uint8_t col, LCDCharSize size = Char5x8) : _bridge{bridge}, _bus{bus}, _line{line}, _col{col}{ if(_line >= 2){ function_set = 0x20 | 0x08 | _bus | size; }else{ function_set = 0x20 | _bus | size; } } ... //funçoes para enviar configuração Omnicrystal& shift_on(); Omnicrystal& shift_off(); Omnicrystal& increment(); Omnicrystal& decrement(); Omnicrystal& cursor_blink_on(); Omnicrystal& cursor_blink_off(); Omnicrystal& cursor_on(); Omnicrystal& cursor_off(); Omnicrystal& display_on(); Omnicrystal& display_off(); ... }; #endif
omnicrystal.cpp
... /* ------------------ CONFIGURAÇÃO DO DISPLAY ------------------------*/ /* essas funçoes recebem a posição em bit da flag na enum de cada configuração liga os bits de acordo com a configuração, "on" coloca o bit em 1, "off" coloca o bit wm 0 */ //configura o autoshift Omnicrystal& Omnicrystal::shift_on(){ entry_mode |= LCDShiftMode; send(entry_mode, 0); return *this; } Omnicrystal& Omnicrystal::shift_off(){ entry_mode &= ~LCDShiftMode; send(entry_mode, 0); return *this; } //diz se ele aumenta ou diminue a posição apos cada letra Omnicrystal& Omnicrystal::increment(){ entry_mode |= LCDDirection; send(entry_mode,0); return *this; } Omnicrystal& Omnicrystal::decrement(){ entry_mode &= ~LCDDirection; send(entry_mode,0); return *this; } //liga e desliga se o cursor piscando Omnicrystal& Omnicrystal::cursor_blink_on(){ display_control |= LCDBlink; send(display_control,0); return *this; } Omnicrystal& Omnicrystal::cursor_blink_off(){ display_control &= ~LCDBlink; send(display_control,0); return *this; } //liga e desliga o cursor Omnicrystal& Omnicrystal::cursor_on(){ display_control |= LCDCursor; send(display_control,0); return *this; } Omnicrystal& Omnicrystal::cursor_off(){ display_control &= ~LCDCursor; send(display_control,0); return *this; } //liga e desliga a exibição Omnicrystal& Omnicrystal::display_on(){ display_control |= LCDDisplay; send(display_control,0); return *this; } Omnicrystal& Omnicrystal::display_off(){ display_control &= ~LCDDisplay; send(display_control,0); return *this; } /*------------------- FIM CONFIGURAÇÃO DO DISPLAY ---------------------*/
Agora, podemos adicionar nossas mudanças nas funções já existentes da classe, como a função “begin”, e, também, criar a função para selecionar a posição da linha/coluna.
omnicrystal.cpp
... Omnicrystal& Omnicrystal::begin(){ delay(50);//aguarda o LCD iniciar send8Bits(0x30, 0); delayMicroseconds(4100); send8Bits(0x30, 0); delayMicroseconds(100); send8Bits(0x30, 0); delayMicroseconds(100); //configura o modo de comunicação corretamente if(_bus == Bus4Bits){ send8Bits(0x20, 0); } //envias as configuraçoes padroes do LCD send(function_set, 0); clear(); reset(); send(entry_mode, 0); send(display_control, 0); return *this; } ...
Para a função de selecionar a posição, temos quer entender um pequeno detalhe sobre o Hitachi HD44780. Na tabela de comandos, podemos ver que a flag DL que é responsável pela quantidade de linhas e é representada por apenas 1 bits, o que significa que o controlador só pode ser configurado com 1 linha (DL = 0) e duas linhas (DL = 1). Então, como podem existir LCDs com mais de 2 linhas, como o 20×4? O que acontece é que a linha pode ter um tamanho máximo de 40 caracteres. Então, eles são reorganizados para fazer parecer que existem mais de 2 linhas:
Como podemos ver, para acessar a linha 3 de um LCD 20×4, estamos, na verdade, acessando a posição 20 da linha 1. Então, vamos salvar a posição de cada linha na nossa classe principal e criar a função para selecionar a linha.
omnicrystal.h
//classe principal #ifndef OMNICRYSTAL_H #define OMNICRYSTAL_H ... class Omnicrystal : public Print{ private: const uint8_t addrs[4] = {0x80, 0xC0, 0x80+20, 0xC0+20}; //endereços para LCD 16x2 e 20x4 ... public: ... Omnicrystal& set_cursor(uint8_t line, uint8_t col); ... };
omnicrystal.cpp
//muda a posição do cursor Omnicrystal& Omnicrystal::set_cursor(uint8_t line, uint8_t col){ if(line <= _line){ if(col <= _col){ send(addrs[line]+col, 0); } } return *this; }
Vamos testar agora, apagando manualmente da IDE e enviando o novo .zip
(Download da versão dessa etapa: https://github.com/RecursiveError/omnicrystal/releases/download/v0.4.0-alpha/omnicrystal.zip)
#include <omnicrystal.h> LCDParallel LcdInter(6,7,8,9,10,11); // interface paralela usada Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); void print_teste(); void shift_on_teste(); void display_off_teste(); void cursor_on_teste(); void setup() { lcd.begin(); // inicia o display lcd.clear().reset().print("comandos testes"); delay(1000); } void loop(){ print_teste(); shift_on_teste(); display_off_teste(); cursor_on_teste(); } //testa se é capaz de enviar diferentes tipos de dados pelo LCD void print_teste(){ lcd.reset().clear().print("Print teste"); delay(1000); lcd.reset().clear().print("NUM:"); lcd.print(255); // envia o numero 255 lcd.set_cursor(1, 0).print("HEX:"); lcd.print(255, HEX); //envia o numero 255 em HEXADECIMAL delay(1000); } //testa o modo de autoshift do LCD void shift_on_teste(){ lcd.reset().clear().print("shift on teste"); lcd.set_cursor(1, 0).shift_on().decrement(); for(int i = 0; i < 11; i++){ lcd.print(i); delay(1000); } lcd.shift_off(); } //testa a exebição do display void display_off_teste(){ lcd.reset().clear().print("desligando o lcd"); delay(1000); lcd.display_off(); lcd.reset().clear().print("ligando o lcd"); //voce pode escrever no display anquanto ele esta desligado delay(1000); lcd.display_on(); delay(1000); } //testa o cursor do LCD void cursor_on_teste(){ lcd.reset().clear().print("cursor on teste"); lcd.set_cursor(1,0).cursor_on().cursor_blink_on(); delay(3000); lcd.cursor_blink_off().cursor_off(); }
Resultado:
O controlador HITACHI HD44780 permite ao usuário criar até 8 caracteres customizados, seguindo esse padrão:
Para criar um, apenas temos que enviar 0x40 + (posição << 3) (nenhum motivo especial para fazer o shift da posição em 3. É apenas um detalhe do HITACHI HD44780) para gravar (como são suportados 8 a posição vai de 0-7) e, depois, enviar 8 valores numéricos, em que seus bit representam a linha, como mostra a imagem acima. Para finalizar, temos que reposicionar o cursor. Para escrever os valores gravados nessas posições, basta apenas chamar “write(posição)”
você pode criar seus caracteres customizados neste site: https://maxpromer.github.io/LCD-Character-Creator/
omnicrystal.h
//classe principal #ifndef OMNICRYSTAL_H #define OMNICRYSTAL_H ... class Omnicrystal : public Print{ private: ... public: ... Omnicrystal& create_char(uint8_t c[8], uint8_t pos); ... }; #endif
omnicrystal.cpp
... Omnicrystal& Omnicrystal::create_char(uint8_t c[8], uint8_t pos){ pos &= 0b00000111; send(0x40 | (pos<<3), 0); for(size_t i = 0; i < 8; i++){ send(c[i], 1); } return *this; }
Atualizando e testando.
(Download da versão dessa etapa: https://github.com/RecursiveError/omnicrystal/releases/download/v0.5.0-alpha/omnicrystal.zip)
#include <omnicrystal.h> LCDParallel LcdInter(6,7,8,9,10,11); // interface paralela usada Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); byte customChar[] = { B00110, B00110, B11110, B11111, B00011, B00001, B00000, B11000 }; byte customChar2[] = { B01010, B01010, B00000, B00000, B10001, B11011, B11011, B00000 }; byte customChar3[] = { B01100, B01100, B01111, B11111, B11000, B10000, B00000, B00011 }; byte customChar4[] = { B11000, B00000, B00001, B00011, B11111, B11110, B00110, B00110 }; byte customChar5[] = { B00000, B11011, B11011, B10001, B00000, B00000, B01010, B01010 }; byte customChar6[] = { B00011, B00000, B10000, B11000, B11111, B01111, B01100, B01100 }; void setup() { lcd.begin(); // inicia o display lcd.create_char(customChar,0) .create_char(customChar2,1) .create_char(customChar3,2) .create_char(customChar4,3) .create_char(customChar5,4) .create_char(customChar6,5); lcd.set_cursor(0, 0); lcd.write(0); lcd.write(1); lcd.write(2); lcd.set_cursor(1, 0); lcd.write(3); lcd.write(4); lcd.write(5); lcd.print("ELETROGATE"); lcd.set_cursor(0, 3); lcd.print("Blog"); } void loop(){ }
Resultado:
E, com isso, finalizamos a classe principal.
Um dos objetivos desse projeto é o suporte a criação de interfaces customizadas. Já criamos uma interface paralela, então, agora, vamos criar uma interface I2C para o pcf8754 com a “Wire.h”. Os pacotes de informação do PCF8754 funcionam neste esquema:
Apenas temos que organizar os dados recebidos pela função “send” para enviar dados corretamente (importante observar que esse modulo possui apenas o Bus de 4Bits).
defaltmodules.h
//Modulos Pre-definos para nossa lib #ifndef DEFULT_MODULES_H #define DEFULT_MODULES_H ... #include "Wire.h" ... //Funciona apenas no mode de 4BITS!!! class LCDPCF8754 : public LCDInterface{ private: const uint8_t _addr; //endereço I2C public: LCDPCF8754(const uint8_t addr): _addr{addr}{ Wire.begin(); //inicia o I2C do hardware } void send(uint8_t config, uint8_t data); }; #endif
defaltmodules.cpp
#include "defultmodules.h" ... void LCDPCF8754::send(uint8_t config, uint8_t data){ uint8_t package = (config & 0b00000111) | (data & 0xF0) | 0x08; //organiza os bits corretamente e envia o pacote Wire.beginTransmission(_addr); Wire.write(package); Wire.endTransmission(); }
Simples assim! Temos um novo modulo para nosso projeto. Agora, é hora de testar!
(Download da versão dessa etapa: https://github.com/RecursiveError/omnicrystal/releases/download/v0.6.0-alpha/omnicrystal.zip)
Esquema:
Código:
#include <omnicrystal.h> LCDPCF8754 LcdInter(0x27); // interface i2c usada Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); byte customChar[] = { B00110, B00110, B11110, B11111, B00011, B00001, B00000, B11000 }; byte customChar2[] = { B01010, B01010, B00000, B00000, B10001, B11011, B11011, B00000 }; byte customChar3[] = { B01100, B01100, B01111, B11111, B11000, B10000, B00000, B00011 }; byte customChar4[] = { B11000, B00000, B00001, B00011, B11111, B11110, B00110, B00110 }; byte customChar5[] = { B00000, B11011, B11011, B10001, B00000, B00000, B01010, B01010 }; byte customChar6[] = { B00011, B00000, B10000, B11000, B11111, B01111, B01100, B01100 }; void setup() { lcd.begin(); // inicia o display lcd.create_char(customChar,0) .create_char(customChar2,1) .create_char(customChar3,2) .create_char(customChar4,3) .create_char(customChar5,4) .create_char(customChar6,5); lcd.set_cursor(0, 0); lcd.write(0); lcd.write(1); lcd.write(2); lcd.set_cursor(1, 0); lcd.write(3); lcd.write(4); lcd.write(5); lcd.print("ELETROGATE"); lcd.set_cursor(0, 3); lcd.print("Blog"); } void loop(){ }
O código é o mesmo do último teste. Só mudamos uma única linha para usar o modulo I₂C.
Resultado:
Como foi mostrado na estrutura do projeto no primeiro post, temos o arquivo “library.properties”, que são informações adicionais sobre nossa biblioteca. Nele, ficam informações como: nome do autor, nome da biblioteca, plataformas suportadas, etc. Você pode ver todas as informações em https://arduino.github.io/arduino-cli/0.20/library-specification/#libraryproperties-file-format. Aqui, vamos apenas trabalhar com as obrigatórias, que são:
Para configurar, é bem simples: basta fazer “chave=valor”. Segue o exemplo desse projeto:
library.properties
name=omnicrystal version=1.0.0 author=Guilherme Silva Schultz maintainer=Guilherme Silva Schultz <guilhermesschultz.contato@gmail.com> sentence=Modular Library for HITACHI HD44780 paragraph=This library allows the user to easily create their own modules for HITACHI HD44780 LCD Displays. category=Display url=https://github.com/RecursiveError/omnicrystal architectures=*
(a cada atualização, temos que alterar a versão do projeto de acordo com: https://semver.org)
Com isso, finalizamos totalmente a criação de uma biblioteca de uso pessoal. Abordamos todos os detalhes, desde a estrutura até a implementação. Você pode fazer o download dessa versão em: https://github.com/RecursiveError/omnicrystal/releases/download/v1.0.0/omnicrystal.zip.
Agora que terminamos a criação da biblioteca, podemos escolher compartilhar a biblioteca com o publico. Mas, primeiro, temos que fazer algumas mudanças e algumas adições.
O guia de criação de bibliotecas do Arduino define algumas sugestões de estilo de código para padronizar as bibliotecas na plataforma. Enquanto sua biblioteca é de uso pessoal, o estilo do código é de sua preferência. Mas, quando queremos compartilhar, seguir um estilo padronizado pode ajudar bastante o usuário. Seguindo o guia de estilo, temos que mudar as funções da nossa biblioteca de snake_case para CamelCase, então:
omnicrystal.h
class Omnicrystal : public Print{ private: const uint8_t addrs[4] = {0x80, 0xC0, 0x80+20, 0xC0+20}; //endereços para LCD 16x2 e 20x4 LCDInterface &_bridge; const BusType _bus; // tipo de comunicação 4 ou 8 bits const uint8_t _line; // quantidade de linhas no display const uint8_t _col; //quantidade de colunas por linhas // variaveis de configuração inicializadas com a configuração padrão uint8_t entry_mode{0x06}; // shift Off, escrita da esquerda pra direita uint8_t display_control{0x0C}; //display ligado, cursor desligado, cursor piscando desligado uint8_t function_set{0}; // configurado no construtor void send4Bits(uint8_t data, uint8_t RS_state); void send8Bits(uint8_t data, uint8_t RS_state); void send(uint8_t data, uint8_t RS_state); public: Omnicrystal(LCDInterface &bridge, const BusType bus, uint8_t line, uint8_t col, LCDCharSize size = Char5x8) : _bridge{bridge}, _bus{bus}, _line{line}, _col{col}{ if(_line >= 2){ function_set = 0x20 | 0x08 | _bus | size; }else{ function_set = 0x20 | _bus | size; } } //envia o codigo dos comandos Omnicrystal& command(LCDCommand); //funçoes para enviar cada um dos comandos Omnicrystal& clear(); Omnicrystal& reset(); Omnicrystal& moveCursorLeft(); Omnicrystal& moveCursorRight(); Omnicrystal& moveDisplayLeft(); Omnicrystal& moveDisplayRight(); //funçoes para enviar configuração Omnicrystal& shiftOn(); Omnicrystal& shiftOff(); Omnicrystal& increment(); Omnicrystal& decrement(); Omnicrystal& cursorBlinkOn(); Omnicrystal& cursorBlinkOff(); Omnicrystal& cursorOn(); Omnicrystal& cursorOff(); Omnicrystal& displayOn(); Omnicrystal& displayOff(); Omnicrystal& setCursor(uint8_t line, uint8_t col); Omnicrystal& createChar(uint8_t c[8], uint8_t pos); Omnicrystal& begin(); virtual size_t write(uint8_t); };
(as mesma mudanças de nome foram feitos no Arquivo omnicrystal.cpp)
o Arquivo Keywords.txt não é necessário para as versões mais atuais da Arduino IDE, mas é importante para manter a compatibilidade com versões antigas. Nesse arquivo, temos tags que representam certos tipos de dados
KEYWORD1 |
tipos de dados |
KEYWORD2 |
funções |
KEYWORD3 |
estruturas |
LITERAL1 |
constantes |
Fonte: https://arduino.github.io/arduino-cli/0.20/library-specification/#keywordstxt-format
No arquivo keywords.txt, colocamos o nome de nossas estruturas seguido de sua respectiva tag:
keywords.txt
# Datatypes (KEYWORD1) ############################################################### Omnicrystal KEYWORD1 LCDPCF8754 KEYWORD1 LCDParallel KEYWORD1 BusType KEYWORD1 LCDCommand KEYWORD1 LCDEntryMode KEYWORD1 LCDDisplayContro KEYWORD1 LCDFunctionSet KEYWORD1 LCDCharSize KEYWORD1 # Methods and Functions (KEYWORD2) ############################################################### command KEYWORD2 clear KEYWORD2 reset KEYWORD2 moveCursorLeft KEYWORD2 moveCursorRight KEYWORD2 moveDisplayLeft KEYWORD2 moveDisplayRight KEYWORD2 shiftOn KEYWORD2 shiftOff KEYWORD2 increment KEYWORD2 decrement KEYWORD2 cursorBlinkOn KEYWORD2 cursorBlinkOff KEYWORD2 cursorOn KEYWORD2 cursorOff KEYWORD2 displayOn KEYWORD2 displayOff KEYWORD2 setCursor KEYWORD2 createChar KEYWORD2 begin KEYWORD2 write KEYWORD2 # Constants (LITERAL1) ######################################################## Bus4Bits LITERAL1 Bus8Bits LITERAL1 LCDClear LITERAL1 LCDReset LITERAL1 LCDShiftCursotLeft LITERAL1 LCDShiftCursotRight LITERAL1 LCDShiftDisplayLeft LITERAL1 LCDShiftDisplayRight LITERAL1 LCDShiftMode LITERAL1 LCDDirection LITERAL1 LCDBlink LITERAL1 LCDCursor LITERAL1 LCDDisplay LITERAL1 LCDChar LITERAL1 LCDLines LITERAL1 LCDBits LITERAL1 Char5x8 LITERAL1 Char5x10 LITERAL1
Um dos arquivos vistos no primeiro post foi a pasta “exemples”. Lá, ficam os códigos que aparecem na aba Exemples da Arduino IDE. Para criar, é bem simples: basta criar um novo sketch, escrever o seu código e mover para a pasta “exemples” do seu projeto. Simples assim.
(A localização do sketchbook já foi apresentada no tópico “Atualizando a biblioteca” neste post)
vamos criar um exemplo de hello world
#include <omnicrystal.h> //selecione a interface que deseja usar /* #define RS 6 #define EN 7 #define D4 8 #define D5 9 #define D6 10 #define D7 11 LCDParallel LcdInter(RS,EN,D4,D5,D6,D7); */ LCDPCF8754 LcdInter(0x27); Omnicrystal lcd(LcdInter, Bus4Bits, 2, 16); void setup() { lcd.begin().print("Hello World!!!"); } void loop(){ }
usaremos o site https://github.com para publicar a nossa biblioteca. Você pode ler mais sobre essa plataforma em: https://docs.github.com/pt/get-started
Repositório do Projeto: https://github.com/RecursiveError/omnicrystal.
Também é importante criar uma documentação do projeto. A documentação nada mais é que um arquivo que explica como usar seu código. Assim, usuários novos não vão ter dificuldades em usar. Em repositórios Git, é comum ter uma breve apresentação ou até mesmo a documentação no arquivo “Readme.md”. Mas isso são detalhes sobre Git, não sobre Arduino.
Para fazer o download da última versão, basta ir em Code → Download as ZIP
O processo de atualização é o mesmo: apague a atual manualmente e instale o novo .zip pela IDE.
Agora que a biblioteca está corretamente configurada, podemos achar os exemplos na aba “Exemplos” da Arduino IDE:
Vamos testar o exemplo “hello_world” em outra placa para verificar se ela funciona corretamente.
Materiais:
Circuito:
Resultado:
Testando e funcionando. Agora, os teste e atualizações continuam, mas este Post chegou ao fim. Espero que tenham gostado de acompanhar o desenvolvimento dessa biblioteca. Futuras atualizações serão lançadas no repositório: https://github.com/RecursiveError/omnicrystal.
Você já deve ter visto que a IDE tem uma aba para instalar bibliotecas automaticamente. Mas, como nós podemos adicionar as nossas bibliotecas lá? Isso é uma tarefa bem mais complicada que envolve conhecimentos que vão além do Arduino, como conhecimento de contribuição em repositórios Git e instalação de ferramentas de linha de comando. Seria possível criar um post inteiro só para cobrir todos os detalhes do “Library Manager”. Portanto, está fora do objetivo desse post. Então, vamos explicar de uma forma mais geral, apenas para dar um norte a desenvolvedores mais experientes em busca de informações.
Primeiro de tudo, você deve verificar se o repositório do projeto está de acordo com os seguintes requisitos: https://github.com/arduino/library-registry/blob/main/FAQ.md#submission-requirements
(https://github.com/arduino/library-registry/blob/main/FAQ.md tem outras informações muito uteis para a publicação na plataforma oficial)
Com o objetivo de verificar esses requisitos, a Arduino criou a ferramenta de linha de comando: Arduino-lint. Com essa ferramenta, é possível verificar automaticamente os requisitos. Para instalar, siga o tutorial da documentação em: https://arduino.github.io/arduino-lint/1.2/installation/.
Com lint instalado, o que você deve fazer é rodar o comando: arduino-lint –compliance specification –project-type library <caminho do projeto>. Isso vai verificar por erros na sua biblioteca. Se você obter o resultado:
Significa que você pode avançar para a prossiga etapa. Caso obtenha erros, você deve corrigir de acordo com: https://arduino.github.io/arduino-lint/1.2/rules/library/
Agora, você deve rodar o comando: arduino-lint –library-manager submit <caminho do projeto>
Isso vai verificar se é possível Adicionar sua biblioteca ao Index.
(isso só vai apresentar erro caso os requerimentos não sejam satisfeitos)
Caso o resultado seja o mesmo da imagem anterior, significa que você pode publicar sua biblioteca no “Library Manager”.
Seguindo o tutorial do https://github.com/arduino/library-registry, temos que fazer um fork do projeto, adicionar o link do nosso repositório no arquivo “repositories.txt” e enviar um pull request. Caso não tenha problemas, sua biblioteca vai ser adicionado ao Library Manager em poucos dias e você vai ser capaz de instalar diretamente pela IDE.
Existem muito mais detalhes para abordar, mas isso já não faz mais parte do tema. Os links de referências podem ser de grande ajuda para quem deseja se aprofundar no assunto.
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!