ESP-IDF初探:使用OLED显示任意图片

0. 前言

虽然ESP32也支持Arduino框架且Arduino使用起来十分方便,但是因为我最近在研究和CSI有关的东西,所以还是需要基于ESP-IDF来开发。这篇文章会介绍怎样在WSL下搭建ESP-IDF的环境,并点亮我手上的一个OLED屏幕,然后我会在另一个坑里介绍怎样提取ESP32的CSI信息。


1. WSL里的VSCode+ESP-IDF环境配置

安装依赖

ESP-IDF把配置、编译、烧录的动作都用Python打包了一层,方便在VSCode里进行调试,所以首先要安装包括Python虚拟环境在内的各种依赖。

sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0

安装ESP-IDF扩展

安装完依赖后详见官方文档的步骤在VSCode里配置ESP-IDF扩展(ESP-IDF Extension for VS Code)。步骤包括:

  • 安装VSCode的ESP-IDF Extension

  • 安装特定版本的ESP-IDF框架,本文用的是5.4.1

IDF工具

安装完毕后可以看到侧边栏的ESP-IDF包含大量的工具,COMMANDS下的东西其实都是用Python打包过的指令,在VSCode的底部也有对应的小按钮。比较常用的有:

  • ESP-IDF Select Flash Method: 选择烧录方式UART/DFU/JTAG

  • Select Port to Use: 选择串口

  • Set Espressif Device Target: 选择开发板硬件

  • SDK Configuration Editor: 配置环境变量,包括pin脚设置

  • Build Project: 编译

  • Flash Device: 烧录

  • Monitor Device: 查看串口输出

  • Advanced - Show Examples:查看例程

把串口共享至WSL

我手上的这块开发板通过CP2102 UART转USB连接到电脑的串口。串口默认是连接到Windows的,为了能在WSL里烧录程序,需要把串口共享给WSL。首先在Windows下安装usbipd,然后用管理员模式打开命令行:

usbipd list
usbipd bind --busid 1-1
usbipd attach --wsl --busid 1-1

#如果要断开共享
usbipd detach --busid 1-1
usbipd unbind --busid 1-1

在WSL里可以用这条指令确认串口是否已经连到WSL:

ls /dev/ttyUSB*

例程烧录

以最简单的例程blink为例,烧录例程的步骤是:

  1. 在例程里找到blink,在某个目录下创建这个项目

  2. 设置硬件版本

  3. 在SDK Configuration Editor里配置GPIO口

  4. 编译

  5. 设置串口号

  6. 烧录

  7. 芯片重启后通过串口查看状态

我用的是搭载了ESP-WROOM-32模组的ESP32 DEVKIT V1,pin脚连接方式如下图所示,LED用的pin脚是GPIO2。成功烧录后运行效果会是LED灯闪烁,串口持续输出切换LED状态的信息。

pinmap


2. 点亮OLED屏幕

当初买这个模组时还顺便买了一个128*64像素的SSD1306的小OLED屏幕,正好可以用ESP-IDF把它点亮。

硬件连接

如前面的pin脚图所示,ESP32自带一个I2C模块。但是这个ESP-IDF提供的i2c_oled例程是用软件实现的I2C,所以其实任何两个GPIO都可以用来当SDA和SCL。只需连好后在i2c_oled_example_main.c里配置所用pin脚即可。 直接烧录,OLED顺利点亮,效果是滚动显示Hello Espressif, Hello LVGL这行字。

软件架构

ESP-IDF里一些可复用的模块称为组件(Component),IDF自带一个Component Manager用来管理各种组件和依赖。在main组件和各个子组件所在目录下都有一个idf_component.yml,里面写清楚了这个组件有哪些依赖。例如,i2c_oled的main的依赖是:

dependencies:
  lvgl/lvgl: "8.3.0"
  esp_lcd_sh1107: "^1"
  esp_lvgl_port: "^1"

其中,lvgl是一个开源的图形库,用来在嵌入式系统中绘制各种图形。esp_lcd_sh1107是oled屏幕的驱动库。esp_lvgl_port是把lvgl移植到esp32用的驱动库。

进一步分析代码发现,i2c_oled例程中,用来点亮OLED的关键函数是这一段:

void example_lvgl_demo_ui(lv_disp_t *disp)
{
    lv_obj_t *scr = lv_disp_get_scr_act(disp);
    lv_obj_t *label = lv_label_create(scr);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); 
    /* Circular scroll */
    lv_label_set_text(label, "Hello Espressif, Hello LVGL.");
    /* Size of the screen (if you use rotation 90 or 270, please set disp->driver->ver_res) */
}

其中lv_label_set_text是在lvgl/src/widgets下面被定义的,可以认为label是widgets的一种。同理,image也是widgets的一种,那么调用和image有关的函数就可以显示图片了。

3. 显示自定义图片

lvgl文档

根据lvgl的文档

You can store images in two places

  • as a variable in internal memory (RAM or ROM)

  • as a file

ESP32的另一个例程i80_controller的文档也对这两种方式进行了解释。以文件形式储存还需要外接的SD卡和相应驱动,所以这里我选择直接把图片转成二进制编译进去。

生成图片

i80_controller目录下有一个esp_logo.c的,是二进制格式的Espressif的logo的例子。

const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_ESP_LOGO uint8_t esp_logo_map[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  /* More binary data omitted */
};

const lv_image_dsc_t esp_logo = {
  .header.cf = LV_COLOR_FORMAT_RGB565A8,
  .header.magic = LV_IMAGE_HEADER_MAGIC,
  .header.w = 96,
  .header.h = 96,
  .data_size = 9216 * 3,
  .data = esp_logo_map,
};

其中,esp_logo_map是图片每个像素的数据,esp_logo是一个image对象,包含了像素数据、颜色格式、长宽等信息。

如lvgl的文档中所写,有一个在线转换器可以用来把图片文件转成二进制。因为i2c_oled用的lvgl是8.3.0版本,所以用转换器的时候也需要选择LVGL v8。因为这款OLED屏幕是单色的,所以图片的颜色格式选的是LV_IMG_CF_INDEXED_1_8BIT。转换完成后会得到一个.c文件,放到i2c_oled目录下即可。

显示图片

lvgl的文档里写了,

The simplest way to use an image in LVGL is to display it with an lv_img object: If the image was converted with the online converter, you should use LV_IMG_DECLARE(my_icon_dsc) to declare the image in the file where you want to use it.

所以,最终lvgl_demo_ui.c里的显示图片用的代码如下:

static lv_obj_t *icon = NULL;
LV_IMG_DECLARE(oled_image)

void example_lvgl_demo_ui(lv_disp_t *disp)
{
    icon = lv_img_create(lv_scr_act());
    lv_img_set_src(icon, &oled_image);
}

烧录后最终效果如图。 显示自定义图片


参考资料

  1. Standard Toolchain Setup for Linux and macOS

  2. ESP-IDF Extension for VS Code

  3. 一步到位!在 WSL2 中玩转 Windows 串口

  4. DOIT ESP32 DevKit V1 Wi-Fi Development Board – Pinout Diagram & Arduino Reference

  5. IDF Component Manager

  6. Working with ESP-IDF Components in VS Code

  7. LVGL Images

  8. Image Converter

  9. i80 LCD LVGL porting example

  10. LVGL图片转换器