SSD1306でQRコード描画

SSD1306でQRコード描画

SSD1306でQRコード描画

SSD1306 (0.96インチ 128x64 OLED LCDディスプレイ) にQRコードを描画してみます。


ESP-IDFだけでLCDに描画するのはちょっと辛いです。
なので追加コンポーネントのグラフィックライブラリ LVGL を使用します。

回路図

こんなんなります。配線するだけでOK。

プロジェクト作成

普通に新規プロジェクトを作成します。
F1キーで ESP-IDF: New Project を選択。

項目
プロジェクト名 QRCodeTest
テンプレート template-app

ESP-IDFコンポーネントを追加

main/idf_component.yml を作成し中身を以下のようにします。

idf_component.yml

dependencies:
  idf: ">=4.4"
  lvgl/lvgl: "~8.3.0"
  esp_lvgl_port: "^1"

追加後に一度ビルドするとコンポーネントがダウンロードされます。
ダウンロードされたコンポーネントは managed_components 以下に格納されます。
コンポーネントについては ESP-IDFのコンポーネントマネージャ を参照してください。

一度ターゲットを選択し直してください。menuconfigの項目が更新されます。
menuconfigを開くと追加したコンポーネントの設定項目が増えてます。

デフォルトから変更する点は以下です。

項目
Color settings - Color depth 1: 1 byte per pixel
3rd Party Libraries - QR code library ON

プログラム

main.cを以下のようにします。

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lvgl_port.h"
#include "lvgl.h"
#include "extra/libs/qrcode/lv_qrcode.h"
#include "esp_err.h"

#define I2C_HOST        0
#define I2C_HW_ADDR     0x3C
#define I2C_SDA_PIN     21
#define I2C_SCL_PIN     22
#define I2C_OLED_H      128
#define I2C_OLED_V      64

lv_disp_t *hDisp;

// LVGL -> SSD1306 フラッシュコールバック
bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) {
    lv_disp_t *hDisp = (lv_disp_t *)user_ctx;
    lvgl_port_flush_ready(hDisp);
    return false;
}

// SSD1306初期化
void init_ssd1306() {
    // I2Cバス作成
    i2c_config_t i2c_conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_SDA_PIN,
        .scl_io_num = I2C_SCL_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master = {
            .clk_speed = (400 * 1000)   // 0.4MHzですが、ESP32の上限的には1MHzまでは平気みたいです。
        }
    };
    ESP_ERROR_CHECK(i2c_param_config((i2c_port_t)I2C_HOST, &i2c_conf));                     // I2Cバス構成
    ESP_ERROR_CHECK(i2c_driver_install((i2c_port_t)I2C_HOST, I2C_MODE_MASTER, 0, 0, 0));    // I2Cドライバーインストール

    // I2C用LCDパネルIO初期化
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_i2c_config_t io_config = {
        .dev_addr = I2C_HW_ADDR,
        .control_phase_bytes = 1,
        .dc_bit_offset = 6,
        .lcd_cmd_bits = 8,
        .lcd_param_bits = 8
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_HOST, &io_config, &io_handle));

    // SSD1306用のLCDパネル作成
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = -1,       // リセットピンは未使用なので-1
        .bits_per_pixel = 1         // モノクロのOLEDなので1
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));

    // LCDパネル初期化
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));     // パネルリセット(初期化前に呼び出し)
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));      // ハネル初期化
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));     // LCDのON/OFF  ONにする

    // espressif__esp_lvgl_port (https://components.espressif.com/components/espressif/esp_lvgl_port)
    // 初期化
    const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    lvgl_port_init(&lvgl_cfg);
    // 画面追加
    const lvgl_port_display_cfg_t disp_cfg = {
        .io_handle = io_handle,
        .panel_handle = panel_handle,
        .buffer_size = I2C_OLED_H * I2C_OLED_V,
        .double_buffer = true,
        .hres = I2C_OLED_H,
        .vres = I2C_OLED_V,
        .monochrome = true,
        .rotation = {
            .swap_xy = false,
            .mirror_x = false,
            .mirror_y = false,
        }
    };
    hDisp = lvgl_port_add_disp(&disp_cfg);

    /* LCDパネル IOコールバック登録 */
    const esp_lcd_panel_io_callbacks_t cbs = {
        .on_color_trans_done = notify_lvgl_flush_ready,        // カラーデータ転送終了時に呼び出されるコールバック
    };
    esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, hDisp);

    /* LCDの回転を標準に設定 */
    lv_disp_set_rotation(hDisp, LV_DISP_ROT_NONE);
}

// QRコード描画
// パラメータの文字列をQRコードに変換します。
void qr_code_draw(const char* pszData) {
    // 64x64のQRコードを作成、フォアグラウンドを白(0xFF)、バックグラウンドを黒(0x00)で描画
    lv_obj_t *qr = lv_qrcode_create(lv_scr_act(), 64, lv_color_hex3(0xFF), lv_color_hex3(0x00));
    lv_qrcode_update(qr, pszData, strlen(pszData));
    lv_obj_center(qr);      // LCD中央にQRコード描画
}

void app_main(void)
{
    // SSD1306初期化
    init_ssd1306();
    // QRコード描画
    qr_code_draw("http://192.168.0.1/index.html");
}

初期化の部分がちょっと面倒なんですが定型文みたいなものですから。
実際にQRコードを描画する部分は非常にシンプルでありがたいです。

以上で書き込んでみると以下のように表示されます。

QRコードを読み取ると “http://192.168.0.1/index.html” が読み取れます。


最初どこから手を付けてよいやらサッパリでしたが、できて見るとシンプルですね。
それもこれもLVGLやQRCodeライブラリなどの恩恵です。
白黒の小さなLCDこんな感じですが、もうちょっと大きなLCDでタッチパネル付きならスマートフォンのような綺麗な描画も可能なようです。
LVGLのデモ見るとちょっと大きなLCDほしくなります。

コメント

このブログの人気の投稿

(真)ESP32の開発環境をVSCodeで作る

ESP32にmicroSD接続

ESP32でデバッグ