Hướng dẫn dùng màn hình OLED 1.3 inch SH1106 với ESP32
- 1. Tìm hiểu module OLED 1,3 inch I2C
- 2. Linh kiện cần chuẩn bị
- 3. Sơ đồ nối OLED 1,3 inch với ESP32
- 4. Cài đặt Arduino IDE và thư viện
- 5. Quét địa chỉ I2C
- 6. Chương trình hiển thị chữ trên OLED 1,3 inch
- 7. Giải thích các lệnh chính
- 8. Vì sao cần setTextWrap(false)?
- 9. Tăng độ ổn định của bus I2C
- 10. Xử lý lỗi thường gặp
Hướng dẫn nối màn hình OLED 1.3 inch SH1106 với ESP32 DevKit V1 qua I2C, cài thư viện Adafruit SH110X, chạy code hiển thị chữ và xử lý lỗi thường gặp.
Màn hình OLED 1,3 inch có độ phân giải 128 × 64 pixel như loại 0,96 inch nhưng vùng hiển thị lớn hơn, chữ dễ đọc hơn và vẫn chỉ cần 4 dây I2C. Điểm cần lưu ý là phần lớn module 1,3 inch dùng chip điều khiển SH1106 thay vì SSD1306, vì vậy phải chọn đúng thư viện. Bài viết hướng dẫn nối màn hình với ESP32 DevKit V1, quét địa chỉ I2C, cài thư viện Adafruit SH110X và chạy chương trình hiển thị chữ chạy bằng Arduino IDE.
1. Tìm hiểu module OLED 1,3 inch I2C
| Thông số | Giá trị |
|---|---|
| Kích thước màn hình | 1,3 inch |
| Độ phân giải | 128 × 64 pixel |
| Chip điều khiển | SH1106 (thường gặp trên module 1,3 inch) |
| Giao tiếp | I2C, 4 chân GND, VCC, SCL, SDA (một số lô in SCK thay cho SCL) |
| Địa chỉ I2C | 0x3C (mặc định) hoặc 0x3D |
| Nguồn cấp | 3,3–5 V (module có mạch ổn áp; kiểm tra thông tin của lô hàng) |
| Kích thước module | Khoảng 34,7 × 32,3 mm |
Mặt sau module có vị trí hàn điện trở chọn địa chỉ, in 0x78 và 0x7A (nhiều lô in nhãn ADRESS_SELECT). Đây là địa chỉ dạng 8 bit (đã gồm bit đọc/ghi). Thư viện Arduino dùng địa chỉ 7 bit, tương ứng 0x78 → 0x3C và 0x7A → 0x3D. Vị trí điện trở hàn ở mặt sau quyết định module dùng địa chỉ nào.
2. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân và cáp USB có truyền dữ liệu.
- Module OLED 1,3 inch I2C 4 chân (128 × 64, chip SH1106).
- 4 dây jumper cái – cái, hoặc breadboard và dây đực – đực.
- Máy tính đã cài Arduino IDE 2.x.
3. Sơ đồ nối OLED 1,3 inch với ESP32
| OLED 1,3 inch | ESP32 DevKit V1 | Ghi chú |
|---|---|---|
| GND | GND | Mass chung. |
| VCC | 3V3 | Nên dùng 3,3 V để bus I2C làm việc ở mức 3,3 V, phù hợp với GPIO của ESP32. |
| SCL | D22 / GPIO22 | Xung clock I2C. |
| SDA | D21 / GPIO21 | Dữ liệu I2C. |
Trên ESP32 DevKit V1 loại 30 chân, bốn chân cần dùng đều nằm ở hàng chân bên phải: GPIO22 (SCL), GPIO21 (SDA), GND và 3V3.
Trước khi cấp nguồn, hãy đọc lại chữ in cạnh hàng chân. Một số lô module 1,3 inch in thứ tự VCC, GND thay vì GND, VCC, hoặc in SCK thay cho SCL; luôn nối theo tên chân, không nối theo vị trí.
Khi dùng cáp 4 dây có đầu cắm sẵn, màu dây của mỗi loại cáp có thể khác nhau. Hãy dò từng dây theo tên chân in trên module và trên mạch, không dựa vào màu dây.
GPIO21 và GPIO22 là cặp chân I2C mặc định trên ESP32 DevKit V1. Có thể dùng cặp chân khác bằng Wire.begin(SDA, SCL), nhưng không chọn GPIO34–39 vì các chân này chỉ là đầu vào, đồng thời hạn chế dùng các chân strapping GPIO2, GPIO5, GPIO12 và GPIO15.
4. Cài đặt Arduino IDE và thư viện
- Mở Tools > Board > Boards Manager, tìm esp32 và cài gói esp32 by Espressif Systems.
- Chọn Tools > Board > esp32 > DOIT ESP32 DEVKIT V1 (hoặc ESP32 Dev Module) và chọn đúng cổng COM.
- Mở Tools > Manage Libraries, tìm Adafruit SH110X và bấm Install. Khi IDE hỏi cài thêm thư viện phụ thuộc, chọn Install All để cài kèm Adafruit GFX Library và Adafruit BusIO.
Không dùng thư viện Adafruit SSD1306 cho màn hình SH1106: hai chip có cách ghi bộ nhớ hiển thị khác nhau nên hình sẽ bị nhiễu hoặc lệch. Bài dùng gói esp32 phiên bản 3.3.10, thư viện Adafruit SH110X 2.1.14, Adafruit GFX Library 1.12.6 và Adafruit BusIO 1.17.4.
5. Quét địa chỉ I2C
Nạp chương trình dưới đây, mở Serial Monitor ở tốc độ 115200 baud rồi nhấn nút EN trên ESP32 để chạy lại từ đầu:
#include <Wire.h>
const int SDA_PIN = 21;
const int SCL_PIN = 22;
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
Serial.println("Dang quet I2C...");
int found = 0;
for (uint8_t address = 1; address < 127; ++address) {
Wire.beginTransmission(address);
if (Wire.endTransmission() == 0) {
Serial.print("Tim thay thiet bi: 0x");
if (address < 16) Serial.print('0');
Serial.println(address, HEX);
found++;
}
}
if (found == 0) {
Serial.println("Khong tim thay thiet bi I2C nao.");
}
}
void loop() {}
Nếu nối dây đúng, Serial Monitor hiển thị tương tự:
Dang quet I2C...
Tim thay thiet bi: 0x3C
Nếu kết quả là 0x3D, đổi OLED_ADDR trong chương trình chính thành 0x3D. Nếu không tìm thấy thiết bị nào, kiểm tra lại nguồn và dây SDA/SCL trước khi làm tiếp.
6. Chương trình hiển thị chữ trên OLED 1,3 inch
Chương trình hiển thị dòng chào PYWORLD.VN, sau đó cho dòng chữ Hello from PYWORLD! chạy qua lại ở nửa dưới màn hình:
/*
PYWORLD.VN - ESP32 DevKit V1 + OLED 1.3 inch SH1106 (128x64, I2C)
SDA = GPIO21, SCL = GPIO22
Thư viện: Adafruit GFX Library, Adafruit SH110X (kèm Adafruit BusIO)
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
// ====== Cấu hình OLED ======
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C // Địa chỉ 7-bit; nếu không hiển thị, thử 0x3D
#define OLED_RESET_PIN -1 // Module 4 chân không có chân RST riêng
#define SDA_PIN 21
#define SCL_PIN 22
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET_PIN);
const char MESSAGE[] = "Hello from PYWORLD!";
const int16_t MESSAGE_Y = 45;
int16_t maxX = 0; // Vị trí x lớn nhất để chữ không vượt mép phải
void setup() {
Serial.begin(115200);
delay(100);
// Khởi tạo I2C trên GPIO21 (SDA) và GPIO22 (SCL)
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
// Khởi tạo OLED
if (!display.begin(OLED_ADDR, true)) {
Serial.println(F("Khong tim thay OLED! Kiem tra day va dia chi I2C."));
while (true) delay(500);
}
display.setContrast(0x50); // 0-255: nhỏ hơn thì tối hơn, đỡ tốn điện
display.setTextWrap(false); // Không tự xuống dòng khi chữ chạm mép phải
display.setTextColor(SH110X_WHITE);
// Màn hình chào
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 0);
display.print(F("PYWORLD.VN"));
display.setTextSize(1);
display.setCursor(0, 20);
display.print(F("OLED 1.3'' SH1106 OK"));
display.display();
delay(1500);
// Đo chiều rộng dòng chữ chạy (cỡ chữ 1)
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(MESSAGE, 0, 0, &x1, &y1, &w, &h);
maxX = SCREEN_WIDTH - w;
}
void loop() {
// Chữ chạy qua lại trong phạm vi màn hình
static int16_t x = 0;
static int8_t dir = 1;
// Chỉ xóa vùng dưới, giữ nguyên hai dòng chào phía trên
display.fillRect(0, 35, SCREEN_WIDTH, SCREEN_HEIGHT - 35, SH110X_BLACK);
display.setCursor(x, MESSAGE_Y);
display.print(MESSAGE);
display.display();
x += dir;
if (x >= maxX) dir = -1;
if (x <= 0) dir = 1;
delay(20);
}
7. Giải thích các lệnh chính
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET_PIN): khai báo màn hình dùng chip SH1106, độ phân giải 128 × 64, giao tiếp qua busWire. Giá trị-1củaOLED_RESET_PINnghĩa là không dùng chân reset.Wire.begin(SDA_PIN, SCL_PIN): chọn GPIO21 làm SDA và GPIO22 làm SCL.display.begin(OLED_ADDR, true): khởi tạo màn hình ở địa chỉ 0x3C. Hàm trả vềfalsekhi không tìm thấy thiết bị; chương trình in thông báo lỗi ra Serial Monitor rồi dừng.display.setContrast(0x50): chỉnh độ sáng trong khoảng 0–255. Giá trị thấp giúp màn hình dịu hơn và giảm dòng tiêu thụ.- Bộ đệm hình: các lệnh
clearDisplay(),print(),fillRect()chỉ vẽ vào bộ đệm trong RAM của ESP32. Nội dung chỉ được gửi lên màn hình khi gọidisplay.display(). setTextSize(2): font mặc định chiếm 6 × 8 pixel cho mỗi ký tự (tính cả khoảng cách), cỡ 2 phóng thành 12 × 16 pixel. Chuỗi PYWORLD.VN có 10 ký tự nên rộng 120 pixel, vừa chiều ngang 128 pixel.fillRect(0, 35, SCREEN_WIDTH, SCREEN_HEIGHT - 35, SH110X_BLACK): chỉ xóa vùng từ hàng 35 trở xuống nên hai dòng chào phía trên được giữ nguyên. Thư viện chỉ gửi lại phần bộ đệm đã thay đổi, giúp màn hình cập nhật nhanh hơn.getTextBounds(): đo kích thước chuỗi theo cỡ chữ hiện tại. Chuỗi Hello from PYWORLD! gồm 19 ký tự, rộng 114 pixel, nên vị trí x lớn nhất là 128 − 114 = 14.
8. Vì sao cần setTextWrap(false)?
Thư viện Adafruit GFX mặc định bật chế độ tự xuống dòng: khi một ký tự sắp vượt mép phải, thư viện đưa ký tự đó xuống dòng kế tiếp tại x = 0. Nếu cho x tăng tới 80 mà không tắt chế độ này, phần cuối của dòng chữ dài 114 pixel sẽ rơi xuống dòng dưới.
Ảnh chụp dưới đây là kết quả thực tế của bản code thử đầu tiên: dòng chữ bị tách thành Hello from PYWO ở dòng trên và RLD! ở đầu dòng dưới.
Chương trình ở mục 6 xử lý bằng hai bước: gọi setTextWrap(false) để không tự xuống dòng và giới hạn x trong khoảng từ 0 đến 14 pixel để dòng chữ luôn nằm trọn trong màn hình.
Biến thể: chữ chạy liên tục từ phải sang trái
Nếu muốn dòng chữ chạy hết chiều ngang như bảng LED, thêm biến messageWidth và thay hàm loop() như sau:
// 1. Khai báo thêm, ngay dưới dòng int16_t maxX = 0;
int16_t messageWidth = 0;
// 2. Trong setup(), ngay dưới dòng maxX = SCREEN_WIDTH - w;
messageWidth = w;
// 3. Thay toàn bộ hàm loop() bằng:
void loop() {
// Chữ chạy liên tục từ phải sang trái
static int16_t x = SCREEN_WIDTH;
display.fillRect(0, 35, SCREEN_WIDTH, SCREEN_HEIGHT - 35, SH110X_BLACK);
display.setCursor(x, MESSAGE_Y);
display.print(MESSAGE);
display.display();
x -= 2;
if (x < -messageWidth) x = SCREEN_WIDTH; // Chữ ra hết mép trái thì bắt đầu lại từ mép phải
delay(20);
}
9. Tăng độ ổn định của bus I2C
Lệnh Wire.setClock(100000) đặt bus I2C ở 100 kHz. Tuy nhiên, thư viện Adafruit SH110X mặc định tạm tăng tốc độ lên 400 kHz mỗi khi gửi hình và trả về 100 kHz sau khi gửi xong. Với dây ngắn, 400 kHz giúp hình cập nhật nhanh. Nếu dây dài hoặc màn hình thỉnh thoảng lỗi hình, có thể giữ 100 kHz cả khi gửi hình bằng cách khai báo:
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET_PIN, 100000, 100000);
Khi đó mỗi khung hình được gửi chậm hơn nên chữ cũng chạy chậm hơn.
10. Xử lý lỗi thường gặp
- Serial Monitor báo "Khong tim thay OLED!": kiểm tra 4 dây và thứ tự GND/VCC, chạy chương trình quét I2C, thử đổi địa chỉ sang 0x3D.
- Quét thấy địa chỉ nhưng màn hình vẫn đen: kiểm tra chương trình có gọi
display.display()sau khi vẽ, thử tăng giá trịsetContrast()và đảm bảo khai báoAdafruit_SH1106G, không phảiAdafruit_SH1107G. - Hình bị nhiễu, lệch hoặc có vệt rác ở mép: thường do dùng thư viện SSD1306 cho màn hình SH1106. Chuyển sang Adafruit SH110X như trong bài. Ngược lại, nếu module thực tế dùng chip SSD1306 thì dùng thư viện Adafruit SSD1306.
- Chữ chạy bị gãy xuống dòng dưới: thêm
display.setTextWrap(false)và giới hạn vị trí x như mục 8. - Màn hình thỉnh thoảng treo hoặc lỗi hình: rút ngắn dây, kiểm tra tiếp xúc, dùng nguồn USB ổn định và giữ bus I2C ở 100 kHz như mục 9.
- Không nạp được chương trình: dùng cáp USB có truyền dữ liệu và chọn đúng cổng COM. Nếu IDE dừng ở dòng Connecting..., giữ nút BOOT trên ESP32 cho đến khi bắt đầu nạp.
- Serial Monitor hiện ký tự lạ: chọn đúng tốc độ 115200 baud.
Xem thêm: OLED SSD1306 0,96 inch với ESP8266, nạp file .bin vào ESP32 bằng Flash Download Tool và ESP32 điều khiển relay 4 kênh. Nếu cần gọn hơn, có thể dùng mạch ESP32 tích hợp sẵn màn hình OLED 0,96 inch.
Xem thêm