
给ESP32的传感器项目加一块小屏幕,是很多玩家绕不开的一步:温湿度计想直接在机器上读数,小车想显示状态码,不想天天串口监视器看着电脑跑。这时候一块SSD1306点阵OLED就是最顺手的答案,十几块钱、两条I2C线就能点亮。这份实操把接线、装库、跑通第一行字到自定义字体整条路走一遍,新手照着做大概率一次点亮。
认识SSD1306:128×64点阵,两条I2C线搞定
市面上最常见的OLED小屏就是SSD1306驱动的0.96寸和1.3寸模块,分辨率128×64,单色显示。它走I2C协议,只需要SCL、SDA两根数据线加供电,不用占一堆GPIO。模块背面通常印着I2C地址,绝大多数是0x3C,少数128×64的模块用0x3D,这个细节后面初始化代码里会用到。
ESP32这边默认的I2C引脚就是为这种外设准备的:GPIO21接SDA,GPIO22接SCL,这是芯片固件层面最合适的一对,不用自己重映射。供电直接用3.3V,现在市面上大多数模块都兼容,个别老模块标注5V的也能在3.3V逻辑下工作,实在不行走Vin引脚取5V。
接线:四根杜邦线的事
按下面这张对照表接,四根线插完就能进入软件环节:
- VCC 接 ESP32 的 3.3V
- GND 接 GND
- SCL 接 GPIO 22
- SDA 接 GPIO 21

接线有两个容易踩的坑。一是SCL和SDA别接反,不少模块丝印很小,插反了屏就是黑的;二是面包板上杜邦线松了会偶发花屏,看着像代码问题,其实是接触问题,先把硬件排掉再怀疑软件。
装库:两分钟搞定Adafruit全家桶
Arduino IDE的库管理器里搜"SSD1306",装"Adafruit SSD1306"这个官方库,它会提示连带安装"Adafruit GFX Library",点是。这两件套就是驱动屏幕的全部依赖,不需要再去找别的第三方包。


第一段代码:把Hello, world!点亮
下面这段例程来自Adafruit官方例子的精简版,整段可以照抄,只改一处都不用。代码里的尖括号头文件按平台兼容要求写成了引号形式,在Arduino工具链下两种写法对库搜索路径是等价的:
/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 10);
// Display static text
display.println("Hello, world!");
display.display();
}
void loop() {
}
几个初始化细节值得说透。SCREEN_WIDTH和SCREEN_HEIGHT按手里的屏写,128×64的模块就写128和64;OLED_RESET传-1表示复位脚与开发板复位共享,绝大多数四脚I2C模块都是这种接法。display.begin里那个0x3C就是I2C地址,如果你的屏是128×64但地址是0x3D,编译过了屏幕却不亮,先改这里。
烧录进去,屏幕上会先短暂出现Adafruit的启动画面,两秒后清屏,在第一行显示Hello, world!。逻辑上注意display.println只是把内容写进缓冲区,真正刷到屏幕的是display.display()这一句,忘了它就是经典的"程序跑完了屏没动静"。

换字体:让小屏更像产品
GFX库自带一组FreeXxx字体,换字体只要include对应头文件再用setFont。下面这段把默认字体换成FreeSerif9pt7b,字形立刻比默认点阵字体精致不少:
/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Fonts/FreeSerif9pt7b.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 allocation failed");
for(;;);
}
delay(2000);
display.setFont(&FreeSerif9pt7b);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,20);
display.println("Hello, world!");
display.display();
delay(2000);
}
void loop() {
}
注意setFont之后想回到默认字体,调用一次display.setFont()不带参数就行。绘制函数同理:drawPixel、drawLine、drawRect、drawCircle配合fill前缀可以画填充版本,画个小车图标、进度条都够用。
排障清单:屏不亮先过这五条
报"SSD1306 allocation failed"最多,基本就是三件事:地址不对(0x3C和0x3D互换试试)、接线松了、屏幕规格和代码里写的分辨率不一致。剩下两条经验:I2C总线上同时挂多个器件时地址冲突,先用I2C扫描例程看总线上的地址列表;还有板子是ESP32-C3或S2这类引脚布局不同的型号,默认I2C引脚不一样,别照抄GPIO21/22,先查自己板子的引脚图。
这块屏跑通之后,往项目里加东西就快了:温度曲线、电池电压、Wi-Fi状态、机器人的模式指示,都是几行绘制代码的事。屏幕本身功耗低,睡眠模式下几乎不耗电,放在电池供电的小项目里也很友好。动手吧,点亮第一行字的那一刻,你的项目才算真正有了"界面"。











补充一个1.3寸屏的坑:有些用的SH1106驱动,和SSD1306指令集不完全兼容,字会偏移半列,买屏前看清楚驱动型号。
尖括号改引号这个说明很贴心,烧录报错找不到头文件的人应该不少
display()忘记调屏幕不动,太真实了,上周刚踩过
OLED_RESET传-1这个细节好,当年我在这卡了一下午
这套流程我上个月刚给儿子的小车项目走过一遍,卡得最久的就是0x3C这个地址,屏一直不亮还以为是模块坏了,扫了一下I2C总线才发现买的这批是0x3D。文中排障清单那段建议新手先看,能少走弯路。另外补充一句,1.3寸的模块有些用的不是SSD1306而是SH1106,驱动不通,点不亮别硬砸。