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.
Tópicos
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)
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("Do bit Ao Byte"); //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?
Até a próxima!
2 Comentários
Comments are closed, but trackbacks and pingbacks are open.