No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Programación de Pantallas OLED con ESP32 y Protocolo I2C/SPI

6/17
Recursos

¿Cómo trabajar con una pantalla OLED en un ESP32?

Bienvenidos al fascinante mundo de la programación y el hardware integrado. Hoy, abordaremos el emocionante proceso de configurar y programar una pantalla OLED con un ESP32. Este tutorial busca brindar claridad y dirección a cualquier entusiasta que desee poner a prueba sus habilidades con pantallas OLED, un componente esencial para diversos proyectos de IoT, anuncios electrónicos y más.

¿Qué debemos considerar al comenzar?

Trabajaremos con una pantalla OLED de resolución 128 x 64 píxeles y utilizaremos el protocolo I2C para comunicarnos entre el microcontrolador ESP32 y el chip que controla la pantalla. Antes de iniciar, es crucial verificar la documentación técnica de tu tarjeta y pantalla para asegurarte de la compatibilidad y las configuraciones necesarias.

¿Cómo configurar el entorno para programar?

Primero, necesitamos asegurarnos de contar con el entorno de programación ajustado correctamente:

  1. Instalar Visual Studio Code y configurar los componentes necesarios para LoRa y la pantalla OLED, específicamente, el SSD1306.
  2. Revisar el archivo CMakeLists para asegurarnos de que los archivos .c necesarios están listos para ser compilados.
  3. Explorar el archivo KConfigProjectBuild que nos permite configurar, entre otras cosas, el tipo de conexión (I2C o SPI), el tamaño de pantalla y parámetros de hardware adicionales.

¿Cómo inicializar la pantalla OLED en el código?

Importación de la librería

Para empezar a trabajar en nuestro código necesitarás incluir la librería de pantalla:

#include "ssd1306.h"

Configuración inicial

Definimos una variable para almacenar la configuración de nuestra pantalla OLED que se usará para enviar y recibir órdenes:

ssd1306_t *screen;

Implementación del código inicial

  1. Limpieza inicial de la pantalla para asegurar que no se muestren residuos de imágenes o texto de procesos anteriores utilizando:

    ssd1306_clearScreen(screen, false);
    
  2. Configuración del canal I2C del ESP32, especificando los pines de datos y la función de reset:

    i2c_master_init(screen, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
    
  3. Inicialización de la pantalla OLED con sus dimensiones:

    ssd1306_init(screen, 128, 64);
    

¿Cómo mostrar texto en la pantalla OLED?

Una vez configurada la pantalla, procedemos a mostrar texto en ella:

  1. Borrar línea: Para limpiar una línea específica antes de escribir, usamos:

    ssd1306_clearLine(screen, 0, false);
    
  2. Mostrar texto: Utilizamos la función displayText para dibujar el texto deseado:

    ssd1306_displayText(screen, 0, "Hola, mundo", 11, false);
    

¿Qué más se puede hacer con la pantalla OLED?

Además de mostrar texto, este increíble dispositivo permite realizar una variedad de tareas:

  • Ajustar el contraste: Mejora la visibilidad del texto con:

    ssd1306_contrast(screen, 0xFF);
    
  • Dibujar imágenes en píxeles.

  • Realizar desplazamiento de texto en pantalla, ya sea horizontal o verticalmente, para presentar mensajes más largos.

  • Configurar animaciones básicas.

¿Qué sigue después?

El siguiente paso será ejecutar todo en nuestra tarjeta de desarrollo para comprobar que los conceptos y configuraciones implementadas funcionan sin problemas. También exploraremos cómo mejorar nuestro código para que sea más reutilizable, así como implementar una animación básica para aprovechar al máximo las capacidades de nuestra pantalla OLED.

¡Sigue avanzando! La programación y la electrónica integrada son campos vastos y llenos de potencial, y cada paso que das te acerca más a convertirte en un experto.

Aportes 4

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

En mi caso maneje la referencia a la pantalla dentro del main y no como una variable global, me quedo así:

#include <stdio.h>
#include "string.h"
#include "ssd1306.h"


void screen_init(SSD1306_t *screen) {
  i2c_master_init(screen, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
  ssd1306_init(screen, 128, 64);
  ssd1306_contrast(screen, 0xFF);
}

void screen_clear(SSD1306_t *screen) {
  ssd1306_clear_screen(screen, false);
}

void screen_print(SSD1306_t *screen, char * str, int page) {
  ssd1306_clear_line(screen, page, false);
  ssd1306_display_text(screen, page, str, strlen(str), false);
}

void app_main(void) {
  SSD1306_t ledScreen;

  screen_init(&ledScreen);
  screen_clear(&ledScreen);
  screen_print(&ledScreen, "Hola mundo!", 0);
}

Tal cual como lo enseñó:


.

.
.

Estoy siguiendo el curso desde visual studio con la extension de platformio, utilizando el modo de arduino ide.
.
implemente aqui con freertos un timer que pasa de la pagina 1 de informacion, a la pagina 2 de lectura de voltaje.
.
comparto el codigo
.

#include <Arduino.h>
#include <Wire.h>

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

error_t ret;

// --------------- Variables and functions for witness led ----------- 
#define LED_PIN 25
const TickType_t led_delay_on  = 100 / portTICK_PERIOD_MS;
const TickType_t led_delay_off = 900 / portTICK_PERIOD_MS;
TaskHandle_t     led_handle;

error_t init_led();
void refresh_led(void *args);

// --------------- Variables and functions for battery voltage read ----------- 


//The gpio35 is connected to a voltage divisor between battery and ground. The 2 resistor has the same value 50k.
#define  BATT_PIN 35
float batt_inv_div = 2.0;
int   batt_raw;
float batt_volt;
float conversion_factor = (3.3 / 4095) * batt_inv_div; 


const TickType_t batt_delay = 10000 / portTICK_PERIOD_MS;
TaskHandle_t     batt_handle;

error_t init_batt();
void refresh_batt(void *args);
float return_voltage_from_raw(int raw);

// --------------- Variables and functions for I2C Bus -----------
#define SDA_PIN   21
#define SCL_PIN   22

error_t init_I2C();

// --------------- Variables and functions for SSD1306 display -----------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int indexPage         = 0;
int indexPagePrevious = 0;
int maxPage           = 2;

enum pages {inf, batt};
TimerHandle_t page_timer = NULL;


TaskHandle_t     ssd1306_handle;
const TickType_t delay_1000    = 1000 / portTICK_PERIOD_MS;
const TickType_t delay_2000    = 2000 / portTICK_PERIOD_MS;
const TickType_t display_delay = 1000 / portTICK_PERIOD_MS;
const TickType_t page_delay    = 3000 / portTICK_PERIOD_MS;
error_t init_ssd1306();

void sendStringXY(String msg, int X, int Y);
void messageValue(String message, float value, int row, int col1, int col2);
void refresh_display(void *args);
void changePage(void *args);

// -------------------------
// SETUP
// -------------------------
void setup() {

    Serial.begin(115200);
    vTaskDelay(delay_2000);

    ret = init_led();
    Serial.println("The Led is OK");  

    xTaskCreatePinnedToCore(
        refresh_led,    //function thet implements the task
        "refresh_led",  //descriptive name
        2048,           //stack size
        NULL,           //pointer to input parameter
        5,              //task priority  0..(configMAX_PRIORITIES - 1)
        &led_handle,    //pointer to task reference. 
        1               // core 0 and core 1
    );


    ret = init_batt();
    Serial.println("The battery read is OK");

    xTaskCreatePinnedToCore(
        refresh_batt,    //function thet implements the task
        "refresh_batt",  //descriptive name
        2048,            //stack size
        NULL,            //pointer to input parameter
        2,               //task priority  0..(configMAX_PRIORITIES - 1)
        &batt_handle,    //pointer to task reference. 
        1                // core 0 and core 1
    );

    ret = init_I2C();
    Serial.println("The I2C is OK");


    ret = init_ssd1306();
    if (ret) {
        Serial.println("The SSD1306 is OK");
    }
    else {    
        Serial.printf("SSD1306 init fail error %d\n", ret);
        while (true) {
            vTaskDelay(delay_1000);
        }
    }

    xTaskCreatePinnedToCore(
        refresh_display,   // function thet implements the task
        "refresh_display", // descriptive name
        2048,              // stack size
        NULL,              // pointer to input parameter
        2,                 // task priority  0..(configMAX_PRIORITIES - 1)
        &ssd1306_handle,   // pointer to task reference. 
        1                  // core 0 and core 1
    );

    page_timer = xTimerCreate(
        "page_Timer",   // Name of Timer
        page_delay,     // Period of timer (ticks)
        pdTRUE,         // Auto reload
        0,              // Timer ID
        changePage);    // Callback function

    if (page_timer == NULL) {
        Serial.println("the timer could not be created");
    } 
    else {
        Serial.println("Start page timer");
    }  

    xTimerStart(page_timer, portMAX_DELAY);

}

void loop() {
  // put your main code here, to run repeatedly:
}

// ------------- Led functions ------------------------------------

error_t init_led() {
    pinMode(LED_PIN, OUTPUT);
    return 0; 
}

void refresh_led(void *args) {
    while (true) {
        digitalWrite(LED_PIN, HIGH);
        vTaskDelay(led_delay_on);
        digitalWrite(LED_PIN, LOW);
        vTaskDelay(led_delay_off);
    };
}

// ------------- Battery functions ------------------------------------

error_t init_batt() {
    pinMode(BATT_PIN, INPUT); 
    // analogReadResolution is 12 bits by default
    // analogSetAttenuation is 11DB by default
    return 0; 
}

void refresh_batt(void *args) {
    while (true) {
        batt_raw = analogRead(BATT_PIN);
        batt_volt = return_voltage_from_raw(batt_raw);
        Serial.printf("Raw: %d  voltage: %5.3f V\n", batt_raw, batt_volt);
        vTaskDelay(batt_delay);
    }
}

// This function must be developed later, due to the non-linearity and shifts of the ESP32 ADC.
float return_voltage_from_raw(int raw){
    return (raw * conversion_factor);
}

// ------------- I2C functions ------------------------------------

error_t init_I2C() {
    Wire.begin(SDA_PIN, SCL_PIN);
    return 0; 
}

// ------------- SSD1306 functions ------------------------------------
error_t init_ssd1306() {
    error_t display_ok;
    display_ok = display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false);
    if (display_ok) {
        vTaskDelay(delay_2000);
        display.clearDisplay();
        display.setTextSize(1);
        display.setTextColor(SSD1306_WHITE);         
        display.cp437(true);
        display.display();
    };
    return display_ok;
}

void sendStringXY(String msg, int X, int Y) {
    unsigned char i;
    char c;
    
    display.setCursor(X*6, Y*8);
    for (i = 0; i < msg.length(); i++) 
    {
        c = msg.charAt(i);   
        display.write(c);
    }
}

void messageValue(String message, float value, int row, int col1, int col2) {
    String svalue;
    sendStringXY(message, col1, row);
    svalue = String(value) + "       ";
    svalue = svalue.substring(0,7);
    sendStringXY(svalue, col2, row);
}

void changePage(void *args) {
    indexPage++;
    if (indexPage >= maxPage) indexPage = 0; 
}

void refresh_display(void *args) {
    while (true) {
        if (indexPage != indexPagePrevious) {
            indexPagePrevious = indexPage;
        }
        display.clearDisplay();
        switch (indexPage) {
            
            case inf:
                sendStringXY("The quick brown ", 0, 0);
                sendStringXY("fox jumps over  ", 0, 1);
                sendStringXY("the lazy dog    ", 0, 2); 
                sendStringXY("0123456789      ", 0, 3); 
                sendStringXY("THE QUICK BROWN ", 0, 4); 
                sendStringXY("FOX JUMPS OVER  ", 0, 5); 
                sendStringXY("THE LAZY DOG    ", 0, 6);
                sendStringXY("01234567890     ", 0, 7);
                break;
            case batt: 
                sendStringXY("Battery", 0, 0);
                messageValue("Volt:", batt_volt, 3, 0, 8);
                break;               
            default: 
                break;        
        }
        display.display();
        vTaskDelay(display_delay);
    }        
}

Para los que depronto estén teniendo problemas al hacer build porque no se halla la librería ssd1306, deben agregar esta línea en el CMakeLists.txt del root:

set(EXTRA_COMPONENT_DIRS ../components/ssd1306)