Hướng dẫn dùng màn hình OLED 1.3 inch SH1106 với ESP32

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ố cơ bản của module OLED 1,3 inch I2C
Thông sốGiá trị
Kích thước màn hình1,3 inch
Độ phân giải128 × 64 pixel
Chip điều khiểnSH1106 (thường gặp trên module 1,3 inch)
Giao tiếpI2C, 4 chân GND, VCC, SCL, SDA (một số lô in SCK thay cho SCL)
Địa chỉ I2C0x3C (mặc định) hoặc 0x3D
Nguồn cấp3,3–5 V (module có mạch ổn áp; kiểm tra thông tin của lô hàng)
Kích thước moduleKhoảng 34,7 × 32,3 mm
Hình vẽ mặt trước và mặt sau module OLED 1.3 inch I2C, kích thước 34,7 x 32,3 mm
Mặt trước, mặt sau và kích thước module OLED 1,3 inch I2C (hình vẽ minh họa). Điện trở ở mặt sau dùng để chọn địa chỉ I2C.

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

Đấu nối OLED 1,3 inch I2C với ESP32 DevKit V1
OLED 1,3 inchESP32 DevKit V1Ghi chú
GNDGNDMass chung.
VCC3V3Nê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.
SCLD22 / GPIO22Xung clock I2C.
SDAD21 / GPIO21Dữ 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.

Vị trí chân GPIO22 SCL, GPIO21 SDA, GND và 3V3 trên ESP32 DevKit V1 30 chân
Vị trí các chân nối OLED trên ESP32 DevKit V1 30 chân (hình vẽ minh họa): GPIO22 (SCL), GPIO21 (SDA), GND và 3V3.
Sơ đồ nối ESP32 DevKit V1 với màn hình OLED 1.3 inch SH1106 qua I2C
Sơ đồ nối 4 dây giữa ESP32 DevKit V1 và OLED 1,3 inch: GND – GND, VCC – 3V3, SCL – D22, SDA – D21.

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.

Ảnh thực tế màn hình OLED 1.3 inch nối cáp 4 dây với mạch ESP32
Ảnh thực tế: OLED 1,3 inch nối với mạch ESP32 bằng cáp 4 dây có đầu cắm.

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

  1. Mở Tools > Board > Boards Manager, tìm esp32 và cài gói esp32 by Espressif Systems.
  2. Chọn Tools > Board > esp32 > DOIT ESP32 DEVKIT V1 (hoặc ESP32 Dev Module) và chọn đúng cổng COM.
  3. 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);
}
Kết quả hiển thị chữ PYWORLD.VN trên OLED 1.3 inch SH1106 với ESP32
Kết quả mong đợi (ảnh mô phỏng theo code): màn hình chào và dòng chữ chạy qua lại ở nửa dưới.

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 bus Wire. Giá trị -1 của OLED_RESET_PIN nghĩ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ề false khi 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ọi display.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.

Ảnh thực tế OLED 1.3 inch SH1106 với ESP32 bị lỗi chữ tự xuống dòng
Ảnh thực tế với bản code thử đầu tiên: phần cuối dòng chữ bị rơi xuống đầu dòng dưới.
So sánh lỗi chữ tự xuống dòng và cách sửa bằng setTextWrap(false) trên OLED
Bên trái: chữ bị gãy dòng khi để chế độ tự xuống dòng mặc định. Bên phải: đã tắt tự xuống dòng và giới hạn vị trí x (ảnh mô phỏng).

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áo Adafruit_SH1106G, không phải Adafruit_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.


Tin tức liên quan

Hướng dẫn bắt đầu với Wemos D1 R2 (ESP8266) trên Arduino IDE
Hướng dẫn bắt đầu với Wemos D1 R2 (ESP8266) trên Arduino IDE

4 Lượt xem

Hướng dẫn dùng Wemos D1 R2 (ESP8266 khung Arduino UNO) với Arduino IDE 2.x: sơ đồ chân, cài driver CH340 và gói board ESP8266, nạp code nháy LED, kết nối WiFi và xử lý lỗi thường gặp.

Hướng dẫn dùng mạch tạo trễ NE555 có relay: đấu dây và chỉnh thời gian trễ
Hướng dẫn dùng mạch tạo trễ NE555 có relay: đấu dây và chỉnh thời gian trễ

3 Lượt xem

Hướng dẫn dùng mạch tạo trễ NE555 có relay bản 5 V và 12 V: nguyên lý tạo trễ, ý nghĩa các cọc VCC, GND, COM, CK, CB, cách chỉnh thời gian trễ, sơ đồ đấu dây và xử lý lỗi thường gặp.

Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32
Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32

5 Lượt xem

So sánh mạch hạ áp LM2596 và MP1584, cách chỉnh điện áp ra bằng đồng hồ vạn năng trước khi nối tải, cấp nguồn 5 V cho ESP32 DevKit V1 từ nguồn 12 V và xử lý lỗi thường gặp.

Hướng dẫn ESP32 làm web server điều khiển relay 4 kênh qua WiFi
Hướng dẫn ESP32 làm web server điều khiển relay 4 kênh qua WiFi

6 Lượt xem

Hướng dẫn dùng ESP32 DevKit V1 làm web server trong mạng WiFi nội bộ, mở trang trên điện thoại để bật, tắt module relay 4 kênh; có sơ đồ nối dây, code Arduino IDE và lưu ý an toàn.

Hướng dẫn dùng module thẻ nhớ Micro SD với ESP32
Hướng dẫn dùng module thẻ nhớ Micro SD với ESP32

6 Lượt xem

Hướng dẫn nối module thẻ nhớ Micro SD với ESP32 DevKit V1 qua SPI, định dạng thẻ FAT32, chạy code Arduino đọc ghi file, ghi dữ liệu định kỳ ra CSV và xử lý lỗi Card Mount Failed.

Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny
Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny

5 Lượt xem

Hướng dẫn cài driver, cài Thonny và nạp firmware MicroPython cho mạch ESP32 Lite, dùng REPL, nháy LED, lưu main.py, đọc nút nhấn, quét I2C và xử lý lỗi thường gặp.

Hướng dẫn dùng màn hình LCD 2004 I2C với ESP32
Hướng dẫn dùng màn hình LCD 2004 I2C với ESP32

5 Lượt xem

Hướng dẫn nối màn hình LCD 2004 I2C (mạch PCF8574) với ESP32 DevKit V1, quét địa chỉ I2C, hiển thị 4 dòng chữ, tạo ký tự riêng, làm bộ đếm thời gian và đổi sang LCD 1602.

Hướng dẫn dùng thanh cảm biến dò line 5 kênh TCRT5000 với ESP32
Hướng dẫn dùng thanh cảm biến dò line 5 kênh TCRT5000 với ESP32

4 Lượt xem

Hướng dẫn nối thanh dò line 5 kênh TCRT5000 (BFD-1000) với ESP32 DevKit V1, đọc trạng thái 5 kênh trên Serial Monitor, tính vị trí vạch và gợi ý hướng lái cho xe dò line.


Bình luận
Đã thêm vào giỏ hàng