Você que já é fã de Arduino e já desenvolveu vários projetos principalmente usando módulos ou sensores como MPU6050, DHT11, BPM180 e etc, deve ter notado aquelas linhas no topo do código que dizem #include <MPU6050.h> ou #include <DHT.h>. Esses arquivos são chamados de bibliotecas e neste post iremos mostrar o que são as bibliotecas, para que servem, porque são usadas e como desenvolver sua própria biblioteca Arduino.
Como exemplo para este tutorial, iremos desenvolver uma biblioteca para utilização do driver de motor NodeMCU
O que é e para que serve uma biblioteca Arduino
Uma biblioteca nada mais é do que um conjunto de instruções desenvolvidas para executar tarefas específicas relacionadas a um determinado dispositivo. Por exemplo, pensando em um acelerômetro MPU6050 e sua biblioteca MPU6050.h. Essa biblioteca tem funções desenvolvidas especificamente para executar tarefas como, configurar o acelerômetro, ler dados de aceleração, giroscópio, temperatura e etc.
A utilização de uma biblioteca facilita o desenvolvimento, tornando o código mais simples e organizado. Por exemplo, a biblioteca do MPU6050 tem mais ou menos 4000 linhas de código (sem contar outras bibliotecas utilizadas dentro dela). É obrigatório o uso dessa biblioteca? Não. Mas então teríamos que escrever, se não as 4000 linhas de código, várias linhas para utilizar o MPU6050 em todo seu potencial.
Bibliotecas podem ser desenvolvidas por qualquer pessoa. Podemos ver muitas bibliotecas da Adafruit e Sparkfun, dentre as outras milhares espalhadas pelo GitHub. Podemos encontrar bibliotecas ótimas, ruins e até mesmo algumas que não funcionam. Uma dica interessante ao avaliar uma biblioteca é olhar o número de estrelas e issues no GitHub e ver também se ela está disponível oficialmente na IDE Arduino.
E foram as bibliotecas que ajudaram a tornar o Arduino tão popular, onde crianças e adultos não precisam ser engenheiros ou desenvolvedores de software, para criarem seus projetos e colocarem sua criatividade em prática. O próprio “jeitão” de escrever código para Arduino segue o princípio da facilidade. Por exemplo a função digitalWrite(pino, valor). Com apenas essa função ligamos ou desligamos um pino do Arduino. Mas veja como ela é por dentro e o que teríamos que escrever caso não usássemos as bibliotecas Arduino:
void digitalWrite(uint8_t pin, uint8_t val) { uint8_t timer = digitalPinToTimer(pin); uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); volatile uint8_t *out; if (port == NOT_A_PIN) return; // If the pin that support PWM output, we need to turn it off // before doing a digital write. if (timer != NOT_ON_TIMER) turnOffPWM(timer); out = portOutputRegister(port); uint8_t oldSREG = SREG; cli(); if (val == LOW) { *out &= ~bit; } else { *out |= bit; } SREG = oldSREG; }
Explore o seguinte link e descubra a origem das várias outras funções que facilitam o desenvolvimento com Arduino como setup(), loop(), Serial.begin(), Serial.available(), Serial.println(), digitalRead(), delay(), etc.
https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino
Componentes de uma biblioteca Arduino
Uma biblioteca Arduino consiste basicamente de 3 componentes/arquivos. São eles:
- Arquivo header(cabeçalho) com extensão “.h” que contém uma listagem (declaração) de todas as funções e variáveis da nossa biblioteca.
- Arquivo source(fonte) com extensão “.cpp” que contém o corpo e funcionamento/lógica (definição) das funções.
- Arquivo keywords.txt que é específico da IDE Arduino e usado para dar coloração as funções da biblioteca enquanto se escreve um código.
Mais a frente no tutorial iremos mostrar como construir cada um desses arquivos já com foco no driver de motor para NodeMCU.
Driver de motor para NodeMCU
Antes de começarmos a desenvolver uma biblioteca, precisamos de uma razão para tal e entender o funcionamento do dispositivo para qual escreveremos a biblioteca.
Para este tutorial escolhemos o driver de motor para NodeMCU. Esse driver é baseado no CI L293D, controla até dois motores além de expor vários pinos do NodeMCU como pinos digitais, UART e SPI. Podemos também utilizar uma fonte separada para os motores. Veja na figura abaixo as indicações de cada parte do driver:
Note que temos as saídas para motor A-, A+, B-, B+. As saídas A- e B- são utilizadas para configurar a força com que os motores irão girar. Para isso utilizaremos PWM. Já as saídas A+ e B+, configuram o sentido de rotação dos motores.
Quando o NodeMCU é encaixado no driver os pinos ficam conectados da seguinte maneira:
Lembre-se desses pinos pois serão utilizados no desenvolvimento da nossa biblioteca.
O funcionamento do driver é bem simples. Para controlar um motor basta conectar seus terminais nos pinos A- e A+. No pino A- utilizamos PWM para configurar a força do motor e no pino A+ o sentido. Então se utilizarmos 10% de força no pino A- e utilizarmos nível 1 na saída A+, o motor gira para um lado com força de 10%. Se utilizarmos força de 100% no pino A- e utilizarmos nível 1 na saída A+, o pino gira com força total para um lado. Se utilizarmos nível 0 na saída A+, o motor gira para o lado contrário.
Colocando a ideia acima em forma de programação Arduino, teríamos algo parecido com o seguinte, fazendo o driver ir para frente por 2 segundos e depois para trás por 2 segundos:
void setup() { pinMode(5, OUTPUT); // saída A- pinMode(0, OUTPUT); // saída A+ pinMode(4, OUTPUT); // saída B- pinMode(2, OUTPUT); // saída B+ } void loop() { analogWrite(5, 1024); // PWM no pino A- digitalWrite(0, HIGH); // sentido no pino A+ analogWrite(4, 1024); // PWM no pino B- digitalWrite(2, HIGH); // sentido no pino B+ delay(2000); analogWrite(5, 1024); // PWM no pino A- digitalWrite(0, LOW); // sentido no pino A+ analogWrite(4, 1024); // PWM no pino A- digitalWrite(2, LOW); // sentido no pino B+ delay(2000); }
Agora que sabemos o funcionamento do nosso dispositivo (driver motor NodeMCU), já podemos seguir com o desenvolvimento da biblioteca.
Pensando nas funcionalidades da nossa biblioteca arduino
Vamos pensar que nossa biblioteca irá precisar atender 5 tarefas/comandos do driver de motor, considerando que temos 2 motores conectados.
- ir para frente
- ir para trás
- virar a esquerda
- virar a direita
- parar
Cada uma dessas tarefas será uma função da nossa biblioteca.
Também daremos ao usuário da nossa biblioteca a opção de realizar os comandos acima com uma determinada força(PWM). Isso será nosso parâmetro de função.
Assim teremos 4 funções que levam parâmetro mais a função de parada.
goForward(uint16_t pwm)
goBackward(uint16_t pwm)
turnRight(uint16_t pwm)
turnLeft(uint16_t pwm)
stop()
Construindo nossa biblioteca arduino
Arquivo header
Nossa biblioteca terá o nome de NodeMotorDriver, portanto nosso arquivo header terá nome NodeMotorDriver.h
Dentro desse arquivo teremos uma classe com nome de nossa biblioteca. Dentro da classe teremos nossas funções e variáveis que serão utilizadas.
class NodeMotorDriver { public: NodeMotorDriver(uint8_t pinMotorA1, uint8_t pinMotorA2, uint8_t pinMotorB1, uint8_t pinMotorB2); void goForward(uint16_t pwm); void goBackward(uint16_t pwm); void turnRight(uint16_t pwm); void turnLeft(uint16_t pwm); void stop(); private: uint8_t m_pinA1; uint8_t m_pinA2; uint8_t m_pinB1; uint8_t m_pinB2; };
Veja que temos uma função ali que não estava em nosso planos:
NodeMotorDriver(uint8_t pinMotorA1, uint8_t pinMotorA2, uint8_t pinMotorB1, uint8_t pinMotorB2);
Essa função chama-se construtor(constructor) e será executada sempre quando criamos um novo objeto da classe NodeMotorDriver. É útil para configuração de valores iniciais para variáveis membros da classe. Geralmente irá conter código que iria na função setup() do Arduino. Mais a frente mostraremos a definição dessa função construtora.
Note também que temos as palavras public e private. Public significa que as funções e variáveis estarão acessíveis para o usuário da biblioteca. Private restringe o uso de funções e bibliotecas para apenas aquela classe em específico. Dependendo do tipo de variável não há necessidade do usuário escrever um valor diferente naquela variável, então utilizamos como private. Já as funções e variáveis públicas estarão disponíveis para o usuário chamá-las ao decorrer do programa.
Ainda no arquivo header precisamos incluir outra biblioteca que é a Arduino.h. Incluir essa biblioteca nos possibilita utilizar funções como pinMode(), digitalWrite(), etc. que são funções do Arduino.
#include <Arduino.h>
E finalmente envolvemos o header em uma guarda de inclusão, que evita problemas de compilação caso o usuário inclua nossa biblioteca duas vezes.
#ifndef NodeMotorDriver_h #define NodeMotorDriver_h // código da classe aqui #endif
Veja abaixo o código completo do header:
/* NodeMotorDriver.h - Library for use with NodeMCU L293D driver board MIT License */ // guarda de inclusão #ifndef NodeMotorDriver_h #define NodeMotorDriver_h #include <Arduino.h> class NodeMotorDriver { public: NodeMotorDriver(uint8_t pinMotorA1, uint8_t pinMotorA2, uint8_t pinMotorB1, uint8_t pinMotorB2); void goForward(uint16_t pwm); void goBackward(uint16_t pwm); void turnRight(uint16_t pwm); void turnLeft(uint16_t pwm); void stop(); private: uint8_t m_pinA1; uint8_t m_pinA2; uint8_t m_pinB1; uint8_t m_pinB2; }; #endif
Arquivo source
No arquivo source é onde definimos a lógica de nossas funções e utilizamos as variáveis declaradas no arquivo header. Terá o mesmo nome da biblioteca com terminação .cpp.
Começando pela função construtora podemos ver a configuração do modo dos pinos como OUTPUT e atribuição das variáveis membro da classe.
NodeMotorDriver::NodeMotorDriver(uint8_t pinMotorA1, uint8_t pinMotorA2, uint8_t pinMotorB1, uint8_t pinMotorB2) { pinMode(pinMotorA1, OUTPUT); pinMode(pinMotorA2, OUTPUT); pinMode(pinMotorB1, OUTPUT); pinMode(pinMotorB2, OUTPUT); m_pinA1 = pinMotorA1; // speed m_pinA2 = pinMotorA2; // direction m_pinB1 = pinMotorB1; // speed m_pinB2 = pinMotorB2; // direction }
Então definimos as funções de movimento.
void NodeMotorDriver::goForward(uint16_t pwm) { analogWrite(m_pinA1, pwm); digitalWrite(m_pinA2, HIGH); analogWrite(m_pinB1, pwm); digitalWrite(m_pinB2, HIGH); }
Note que o valor de PWM será passado pelo usuário como um parâmetro na chamada de função e os valores das variáveis membro já foram atribuídos pela função construtora. As outras funções de movimento seguem a mesma lógica apenas alternando entre HIGH e LOW.
Por último temos a função stop que desliga todos os motores.
void NodeMotorDriver::stop() { analogWrite(m_pinA1, 0); digitalWrite(m_pinA2, 0); analogWrite(m_pinB1, 0); digitalWrite(m_pinB2, 0); }
Não podemos esquecer também de incluir nosso arquivo header onde fizemos as declarações de função.
#include "NodeMotorDriver.h"
O arquivo source completo pode ser visto abaixo:
/* NodeMotorDriver.cpp - Library for use with NodeMCU L293D driver board MIT License */ #include "NodeMotorDriver.h" NodeMotorDriver::NodeMotorDriver(uint8_t pinMotorA1, uint8_t pinMotorA2, uint8_t pinMotorB1, uint8_t pinMotorB2) { pinMode(pinMotorA1, OUTPUT); pinMode(pinMotorA2, OUTPUT); pinMode(pinMotorB1, OUTPUT); pinMode(pinMotorB2, OUTPUT); m_pinA1 = pinMotorA1; // speed m_pinA2 = pinMotorA2; // direction m_pinB1 = pinMotorB1; // speed m_pinB2 = pinMotorB2; // direction } void NodeMotorDriver::goForward(uint16_t pwm) { analogWrite(m_pinA1, pwm); digitalWrite(m_pinA2, HIGH); analogWrite(m_pinB1, pwm); digitalWrite(m_pinB2, HIGH); } void NodeMotorDriver::goBackward(uint16_t pwm) { analogWrite(m_pinA1, pwm); digitalWrite(m_pinA2, LOW); analogWrite(m_pinB1, pwm); digitalWrite(m_pinB2, LOW); } void NodeMotorDriver::turnRight(uint16_t pwm) { analogWrite(m_pinA1, pwm); digitalWrite(m_pinA2, HIGH); analogWrite(m_pinB1, pwm); digitalWrite(m_pinB2, LOW); } void NodeMotorDriver::turnLeft(uint16_t pwm) { analogWrite(m_pinA1, pwm); digitalWrite(m_pinA2, LOW); analogWrite(m_pinB1, pwm); digitalWrite(m_pinB2, HIGH); } void NodeMotorDriver::stop() { analogWrite(m_pinA1, 0); digitalWrite(m_pinA2, 0); analogWrite(m_pinB1, 0); digitalWrite(m_pinB2, 0); }
Arquivo keywords.txt
Esse arquivo não é obrigatório mas é interessante para a experiência do usuário que irá utilizar nossa biblioteca. Com ele podemos definir quais funções terão uma coloração diferente quando se escreve código.
# Arduino IDE Keywords for Syntax Coloring # Keyword for class NodeMotorDriver NodeMotorDriver KEYWORD1 # Keyword for class functions goForward KEYWORD2 goBackward KEYWORD2 turnRight KEYWORD2 turnLeft KEYWORD2 stop KEYWORD2
Nome de classe deve utilizar KEYWORD1 e terá coloração laranja negrito. Nome de função deve utilizar KEYWORD2 e terá coloração laranja.
Utilização da biblioteca
Pronto agora temos toda a biblioteca construída. Para utilizá-la devemos colocar os 3 arquivos dentro de uma pasta com o nome da biblioteca NodeMotorDriver, e copiar para a pasta libraries da IDE Arduino.
Então basta incluir a biblioteca no código e utilizar as funções. Veja abaixo um pequeno exemplo de código utilizando nossa biblioteca.
#include <NodeMotorDriver.h> NodeMotorDriver nodeMotorDriver(5, 0, 4, 2); void setup() { } void loop() { nodeMotorDriver.goForward(1024); delay(1000); nodeMotorDriver.goBackward(500); delay(1000); nodeMotorDriver.turnRight(1024); delay(1000); nodeMotorDriver.turnLeft(500); delay(1000); nodeMotorDriver.stop(); delay(1000); }
Primeiro utilizamos nossa função construtora definindo um nome de objeto da classe e os pinos de saída dos motores. Na função loop apenas chamamos as funções de movimento passando a força do movimento(PWM) como parâmetro de função.
No próximo post iremos mostrar como utilizar essa biblioteca arduino em um projeto real de robô com NodeMCU e driver de motor controlado pelo celular. Fique ligado!
Veja também uma explicação em vídeo sobre criação de biblioteca Arduino no canal WRKits:
Referências
https://www.arduino.cc/en/Hacking/LibraryTutorial
https://www.arduino.cc/en/Reference/APIStyleGuide
https://playground.arduino.cc/Code/Library
https://github.com/kecsot/Arduino-Motor-H-Bridge-L293d/tree/master/Motor
Gostou de aprender como desenvolver uma biblioteca arduino? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial.
Olá Giovanni, eu queria entender? Os dois arquivos:( .cpp e .h) , são criados em C++, isto significa ser necessário um programa que role C++, neste caso qual o programa você indica? Depois se entendi, eu pego os dois arquivos e transfiro para o Arduino?
Grato.
Olá José Augusto,
Você pode utilizar um editor de texto ou uma IDE à sua escolha.
Um exemplo de editor leve e bastante poderoso para códigos é o Notepad++
Depois de criados os arquivos você vai criar uma pasta com o mesmo nome da biblioteca que você criou dentro da pasta “libraries”, que fica dentro da pasta do Arduino, geralmente localizada na pasta Documentos.
Aí você pode fazer a importação como qualquer outra biblioteca que você utiliza no código do seu Arduino.
Abraços!
Vinícius – Equipe MakerHero
Grato Vinicius pela dica.
Olá Vinicius Pinheiro, usei o Notepad++, fiz os dois arquivos, sendo um NodeMotorDriver.cpp e o outro NodeMotorDriver.h, salvei na pasta Libraries do Arduino e quando abrir o sketch do Arquino para rodar o NodeMotorDriver, não foi reconhecido a biblioteca #include , sou autodidata e devo está fazendo algo errado, o que pode ser?
Grato.
Olá José Augusto,
Dentro da pasta Libraries do Arduino você criou a pasta NodeMotorDriver e colocou os arquivos dentro dela?
Depois você deve fazer o include da seguinte maneira no caso do nome dos seus arquivos:
#include
Abraços!
Vinícius – Equipe MakerHero
Olá Vinicius, o acabei instalando o Codebloks e por alguma razão desta vez funcionou, deu certo. Mais um aprendizado concluido,tenho feito vários projetos com o auxílio de vosso blog, meus agradecimentos por tornarem público e acessível a autodidata como eu.
Olá José Augusto,
Talvez algum deslize de digitação, ou até mesmo algo na formatação de saída do arquivo pelo Notepad++, é raro mas as vezes acontece.
O intuito do nosso blog é esse mesmo, compartilhar o conhecimento.
Abraços!
Vinícius – Equipe MakerHero
Gostaria de saber se uma biblioteca.h chamada pelo sketch ocupa espaço na memória do esp8266 ou do arduino mega ?
Olá Lucas,
Sim, a biblioteca ocupa espaço na memória.
Todas as instruções que são codificadas, estejam elas na forma de biblioteca ou do código principal, vão ocupar espaço.
Abraços!
Vinícius – Equipe MakerHero
Gostei muito da explicação, só gostaria de tirar uma pequena dúvida que tive lendo o código:
para explicar minha dúvida, vou utilizar a função “void goForward(uint16_t pwm);”, que tem como parâmetro o “uint16_t” e posteriormente ele foi chamado de pwm. A minha dúvida é sobre o significado desse “uint16_t”, e quando posso utilizá-lo. Percebi também que foi usado o parâmetro “uint8_t”. Portanto, quem puder esclarecer isso para mim eu agradeço.
Olá,
Vamos quebrar em partes,
u significa que é uma variável sem sinal, ou seja, representa apenas valores positivos
int significa que representa números inteiros
16 significa que essa variável é de 16 bits (pode representar números decimais entre 0 e 65535)
_t é a convenção da linguagem para representação
Você pode ver alguns outros tipos de variáveis nesse post
https://www.makerhero.com/blog/entenda-os-tipos-de-variaveis-no-arduino/
Abraços!
Vinícius – Equipe MakerHero
Boa Noite,
Gostaria que alguém me ajuda-se, eu precisava de saber qual a Biblioteca do Include para o Arduino e GPS para fazer coodenadas Pt-ptm06 estou em Portugal, não consigo encontrar nada que me possa ajudar. Obrigadão pela ajuda
Olá, Pedro!
Já tentou usar a biblioteca TinyGPS?
https://github.com/mikalhart/TinyGPS
Abraços!
Diogo – Equipe MakerHero
Muito obrigado pelo conhecimento compartilhado, foi me de grande valia!
Olá Cristiano,
É muito bom saber que o nosso trabalho pode te ajudar 😀
Abraço!
Rosana – Equipe MakerHero
Olá boa tarde. Gostaria de saber aqui no site quem poderia estar mim ajudando a fazer uma biblioteca com 5 módulos diferentes o Matricial 4×4, o display 16×2 com o serial I2C, o sensor RFID, o buzzer, luz de led vermelha e verde e um poush buton funcionarem juntos no Arduino uno R3. Desde já agradeço
Muito bom mesmo, Giovanni.
Parabéns!
Olá Vagner!
Obrigado pela leitura e feedback. Ficamos felizes 🙂
Bom dia.
Será que esse ano sai a segunda parte desse artigo?
Olá José!
Já saiu a segunda parte:
https://www.makerhero.com/blog/robo-com-nodemcu-controlado-via-celular/
Abraços!
André Rocha – Equipe MakerHero
Qual IDE você utilizou para montar a biblioteca?
Olá Isaac!
Eu usei um editor de texto. O Gedit do Linux.
Mas você poderia usar a própria IDE Arduino, usando a opção no menu Sketch->Adicionar arquivo.
Como é um projeto pouco complexo, o editor de texto já me serviu.
Muito bom aprendi muito estou sempre acompanhado as postagens
Olá Lucas!
Que bom que o post foi útil para você!
Não deixe de conferir a parte 2 desse post onde exploramos um pouco mais sobre bibliotecas Arduino na prática: https://www.makerhero.com/blog/robo-com-nodemcu-controlado-via-celular/
Abraço!