Manual

do

Maker

.

com

Como fazer gráfico com OLED no Arduino

Como fazer gráfico com OLED no Arduino

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.

Display OLED

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.

Biblioteca

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.

Código para fazer gráfico com OLED no Arduino (1)

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);
}

Gráfico com OLED no Arduino (barras)

bar_graph-oled-300x212.webp

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.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.