Manual
do
Maker
.
com
Existem diversas maneiras de traçar gráfico com OLED no Arduino, mas a que me senti mais confortável foi utilizando a biblioteca u8glib, que facilitou o tempo de atualização do display e abstraiu bastante a interface. Mas antes de começar, gostaria de agradecer a todos que acompanham o blog todos os dias, que recomendam e compartilham os artigos, que dão apoio a essa iniciativa de distribuir informação.
Na tarde do último dia de Agosto batemos o record anual de visitas, com um crescimento realmente significativo. São esses sinais de apreciação que me dão o prazer de continuar escrevendo e tentando fazer cada vez um conteúdo melhor para vocês que prestigiam o blog. Muito obrigado!
Voltando ao tema, para esse artigo utilizei a placa AFSmartRadio da AFEletronica, cujo parceiro possui diversas placas para uso em campo, como você pode verificar no site deles. Essa placa em específico possui entrada para LoRa1276 e RF4463Pro, ficando à sua escolha. Além disso, caracteristicamente possui entrada para cartão micro-SD, para geração de logs ou parametrização do serviço, botão de uso geral, um relé, slot para dispositivos SPI, I2C, pinos de GPIO expostos, alimentação externa pela barra de pinos e comunicação pela porta USB. A porta USB não alimenta a placa, é apenas para a comunicação e gravação de firmware. E antes que eu me esqueça, ao lado do slot do rádio tem um slot para SIM800L, permitindo o envio de informações para a Internet e possibilitando assim que a placa seja autônoma ou concentradora.
Essa placa usa um Atmega328P e é programada selecionando Arduino Nano, no menu de placas.
O display OLED é incrível. O único ponto negativo, em minha opinião, é o tamanho. Ele poderia ser maior.
Já escrevi montes de artigos com ele, e o que eu mais gosto é do artigo onde o uso como display da câmera do Raspberry.
A biblioteca está disponível no repositório de bibliotecas do Arduino, portanto você pode usar a IDE oficial para instalá-la, ou então utilizar sua IDE preferida com PlatformIO. No meu caso, uso o Visual Studio Code, e sugiro fortemente por diversas razões. Já falei a respeito no artigo da roleta com Arduino.
Primeiro, vamos ao código que resultou na imagem de destaque.
A biblioteca u8glib oferece diversas funções interessantes que facilitam a interação com esse display. No caso, bastou utilizar o traçado de linha e trabalhar os pontos na matriz X e Y.
À esquerda coloquei uma linha vertical por onde desliza um círculo. Esse círculo é a média dos valores. Invés de fazer wiring e colocar um sensor, criei uma função para geração de números randômicos, facilitando o desenvolvimento do código. Está tudo em tipo inteiro (int), mas se for ler temperaturas, certamente será ideal mudar a função que converte inteiro para char array, de forma que converta float para char array. Uma das possibilidades é utilizar a função dtostrf. O exemplo você encontra nesse outro artigo. A conversão é necessária para compor a string exibida no header do display, onde o primeiro valor é simulação de temperatura e o segundo valor é a média.
Esse código é bastante satisfatório para exemplo de como traçar gráfico com OLED no Arduino, mas sugiro que dê uma melhorada nele se for aplicá-lo a algum projeto. Eu desloquei a matriz para a direita, deixando livre o canto esquerdo para colocar a barra vertical, então os primeiros 10 valores estão "no limbo".
A função map não deve ser necessária para temperaturas em ambiente normal porque o display conta com 64 pixels de altura (dos quais uma fração foi reservada para a string), mas se for aplicar e ambientes muito quentes ou muito frios, refaça o map.
Por fim, quando programo procuro modular o código. Para esse exemplo escrevi desordenadamente, só para mostrar a ideia. O código para esse exemplo:
#include <Arduino.h>
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);
const int WIDTH = 128;
const int HEIGHT = 64;
const int LENGTH = WIDTH;
float avg = 23.5; //trocar pra 0 e depois colocar leitura do sensor de temperaturas
const int analogInPin = A0;
int analogInValue = 0;
int average = 0;
int x;
int y[LENGTH];
int result = 0;
uint8_t noise_pin = A6;
uint8_t actual_pos = 0;
char msg[15];
void limit(uint8_t pos);
int makeNoise()
{
for (uint8_t i = 0; i < 10; i++)
{
result = analogRead(noise_pin);
randomSeed(analogRead(noise_pin));
result = random(1023);
}
return result;
}
void clearY(){
for(int i=10; i<LENGTH; i++){
y[i] = -1;
}
}
void drawY(){
u8g.drawPixel(0, y[0]);
for(int i=10; i<LENGTH; i++){
if(y[i]!=-1){
u8g.drawLine(i-1, y[i-1], i, y[i]);
actual_pos +=1;
}else{
break;
}
}
}
void limit(uint8_t pos){
for (uint8_t i=0;i<pos;i++){
average += y[i];
}
average = average/(pos) > -1 ? average/(pos) : 0;
//x,y inicial e x,y final
u8g.setFont(u8g_font_courB10);
u8g.drawCircle(3,average,3);
u8g.drawLine(3,0,3,64);
actual_pos = 0;
}
void setup(void) {
Serial.begin(9600);
x = 0;
clearY();
}
void loop(void) {
//analogInValue = analogRead(analogInPin);
analogInValue = makeNoise();
y[x] = map(analogInValue, 0, 1023, HEIGHT-1, 20);
u8g.firstPage();
do {
drawY();
u8g.setFont(u8g_font_courB10);
limit(actual_pos);
String t = "T:" + String(y[x]) + " A:" + String(average);
memset(msg,0,15);
t.toCharArray(msg,14);
u8g.drawStr(10,10,msg);
average = 0;
} while( u8g.nextPage() );
x++;
if(x >= WIDTH){
x = 10;
clearY();
}
delay(100);
}
Já nesse código, utilizei a biblioteca da Adafruit, que também está disponível no repositório oficial do Arduino. Ainda não terminei a implementação e o estado desse código é "alpha", porque vou implementar todos os recursos dessa placa da AFEletronica e deixar pronto para uso. Por enquanto, o que tem já serve de referência.
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <LoRa.h>
#include <SD.h>
#define ON 1
#define OFF 0
//#define SDCARD
//Adafruit_SSD1306 display = Adafruit_SSD1306();
Adafruit_SSD1306 display(A6);
//Create software serial object to communicate with SIM800L
SoftwareSerial mySerial(3, 5); //SIM800L Tx & Rx is connected to Arduino #3 & #2
File myFile;
struct cf
{
uint8_t noise = A0;
uint8_t sda_pin = A4;
uint8_t scl_pin = A5;
uint8_t led_st = A3;
uint8_t relay = 4;
uint8_t chip_sel = 8;
uint8_t rst_pin = 9;
uint8_t cs_pin = 10;
uint8_t irq_pin = 2;
} config;
struct lora_comm
{
uint8_t msg_count = 0;
uint8_t addr = 0xBB;
uint8_t dst = 0xFF;
long last_sent = 0;
int interval = 2 * 1000; //2s
} lora_defs;
uint8_t space = 0;
uint8_t local = 0;
uint8_t shif = 0;
int result = 0;
void updateSerial();
void bar(uint8_t volume, uint8_t position, bool refresh);
void sendMessage(String outgoing);
int makeNoise()
{
for (uint8_t i = 0; i < 10; i++)
{
result = analogRead(config.noise);
randomSeed(analogRead(config.noise));
result = random(32);
}
return result;
}
void show()
{
display.clearDisplay();
for (uint8_t i = 0; i < 10; i++)
{
result = analogRead(config.noise);
randomSeed(analogRead(0));
result = random(32);
bar(result, i, false);
}
}
struct bar_t
{
uint8_t max_lenght = 64;
uint8_t max_width = 10;
uint8_t max_bars = 10;
uint8_t space = 3;
uint8_t bars_value[10] = {0}; //inicialização de um array bi-dimensional
} bars;
void setup()
{
//Begin serial communication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
pinMode(config.chip_sel, OUTPUT);
pinMode(config.relay, OUTPUT);
pinMode(config.led_st, OUTPUT);
digitalWrite(config.relay, OFF);
digitalWrite(config.led_st, ON);
#ifdef SDCARD
char filename[] = "data.log\0";
if (!SD.begin(chipSelect))
{
Serial.println("Cartao Falhou, ou nao esta presente");
while (true); //não sai mais daqui se der erro na leitura do SD.
}
Serial.println("Cartao Inicializado");
dataFile = SD.open(filename, FILE_WRITE);
dataFile.close();
#endif
LoRa.setPins(config.cs_pin, config.rst_pin, config.irq_pin);
if (!LoRa.begin(915E6))
{ // initialize ratio at 915 MHz
Serial.println("LoRa init failed. Check your connections.");
while (true); //Não sai mais daqui se der erro no rádio
}
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.setTextSize(1);
display.clearDisplay();
//Begin serial communication with Arduino and SIM800L
mySerial.begin(9600);
Serial.println("Initializing...");
delay(1000);
mySerial.println("AT"); //Once the handshake test is successful, it will back to OK
updateSerial();
mySerial.println("AT+CSQ"); //Signal quality test, value range is 0-31 , 31 is the best
updateSerial();
mySerial.println("AT+CCID"); //Read SIM information to confirm whether the SIM is plugged
updateSerial();
mySerial.println("AT+CREG?"); //Check whether it has registered in the network
updateSerial();
display.setCursor(10, 10);
display.print("Manual do Maker"); //ESCREVE O TEXTO NO DISPLAY
delay(3000);
display.display();
delay(2000);
display.clearDisplay();
show();
delay(500);
show();
/*
bar(32,0,false);
bar(4,1,false);
bar(16,2,false);
bar(8,3,false);
*/
}
void average()
{
}
void bar(uint8_t volume, uint8_t position, bool refresh)
{
bars.bars_value[position] = volume;
local = position > 0 ? local + 13 : 0;
shif = shif < 128 ? shif + 10 : 10;
space = position < 1 ? 0 : space + 3;
//space+bar, y=0, position, value
display.fillRect(local, 0, 10, bars.bars_value[position], INVERSE);
Serial.println(position);
display.display();
}
void loop()
{
updateSerial();
int packetSize = LoRa.parsePacket();
if (packetSize)
{
// received a packet
Serial.print("Received packet '");
// read packet
while (LoRa.available())
{
Serial.print((uint8_t)LoRa.read(), HEX);
}
// print RSSI of packet
Serial.print("' with RSSI ");
Serial.println(LoRa.packetRssi());
}
}
void updateSerial()
{
delay(500);
while (Serial.available())
{
mySerial.write(Serial.read()); //Forward what Serial received to Software Serial Port
}
while (mySerial.available())
{
Serial.write(mySerial.read()); //Forward what Software Serial received to Serial Port
}
}
Acredito que será útil para diversos makers, afinal, nada melhor do que focar no objetivo e pular a fase de experimentação, certo?
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.