ต่อจอ OLED SSD1306 กับ ESP32 ผ่าน I²C แสดงข้อความ รูป และแก้จอไม่ขึ้น

วิธีต่อจอ OLED SSD1306 0.96" กับ ESP32 ขา SDA 21 / SCL 22 หา address ด้วย I²C scanner แสดงข้อความและรูปด้วย Adafruit SSD1306 และไล่สาเหตุเมื่อจอไม่ขึ้น

เขียนเมื่อ

จอ OLED SSD1306 ขนาด 0.96" (128×64 จุด) แบบ 4 ขาต่อกับ ESP32 ด้วยสายแค่ 4 เส้น: VCC → 3V3, GND → GND, SDA → GPIO 21, SCL → GPIO 22 จอส่วนใหญ่ใช้ address 0x3C เขียนโปรแกรมด้วยไลบรารี Adafruit SSD1306 และต้องเรียก display() ทุกครั้งหลังวาด ไม่อย่างนั้นจอจะว่างเปล่า ถ้าต่อแล้วจอไม่ขึ้น ให้รัน I²C scanner ด้านล่างก่อน จะรู้ทันทีว่าปัญหาอยู่ที่สายหรือที่โค้ด

ต่อสาย

ขาจอESP32หมายเหตุ
VCC3V3โมดูลส่วนใหญ่รับ 3.3–5 V ได้ แต่ต่อ 3V3 ปลอดภัยที่สุด เพราะตัวต้านทาน pull-up บนโมดูลจะดึงสาย SDA / SCL ไปที่แรงดันเดียวกับขา ESP32
GNDGND
SDAGPIO 21ขา I²C มาตรฐานของ ESP32 ใช้ขาอื่นได้ด้วย Wire.begin(sda, scl)
SCLGPIO 22

ลำดับขาบนจอแต่ละยี่ห้อไม่เหมือนกัน บางรุ่นเป็น GND VCC SCL SDA บางรุ่นเป็น VCC GND SCL SDA ให้อ่านตัวหนังสือบนจอก่อนเสียบทุกครั้ง ต่อ VCC กับ GND สลับกันจอเสียได้

จอหนึ่งตัวกับเซนเซอร์ I²C ตัวอื่น เช่น ADS1115 หรือ TCS34725 ใช้สาย SDA / SCL ร่วมกันได้ เพราะแต่ละตัวมี address ของตัวเอง

หา address ด้วย I²C scanner

โค้ดนี้ถามทุก address ตั้งแต่ 1 ถึง 126 ว่ามีอุปกรณ์ตอบหรือไม่ แล้วพิมพ์ผลออก Serial Monitor (115200)

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
}

void loop() {
  int found = 0;
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {        // 0 = มีอุปกรณ์ตอบรับ
      Serial.printf("พบอุปกรณ์ที่ 0x%02X\n", addr);
      found++;
    }
  }
  if (!found) Serial.println("ไม่พบอุปกรณ์ I2C: เช็กสาย SDA / SCL และไฟเลี้ยง");
  delay(3000);
}

แสดงข้อความ

ติดตั้งไลบรารี Adafruit SSD1306 จาก Library Manager (จะถามให้ติดตั้ง Adafruit GFX ด้วย ให้กด Install All)

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 oled(128, 64, &Wire, -1);    // -1 = โมดูลไม่มีขา reset

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {   // address จาก I2C scanner
    Serial.println("เริ่มจอไม่สำเร็จ");
    while (true) delay(1000);
  }
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);

  oled.setTextSize(1);                 // ตัวอักษรสูง 8 จุด
  oled.setCursor(0, 0);
  oled.println("Room temperature");

  oled.setTextSize(3);                 // ใหญ่ขึ้น 3 เท่า สูง 24 จุด
  oled.setCursor(0, 16);
  oled.println("25.3C");

  oled.drawRect(0, 50, 128, 14, SSD1306_WHITE);    // กรอบแถบ
  oled.fillRect(2, 52, 80, 10, SSD1306_WHITE);     // แถบเต็ม 80 จุด

  oled.display();                      // ส่งภาพไปที่จอ ไม่มีบรรทัดนี้จอจะว่าง
}

void loop() {}

ไลบรารีวาดทุกอย่างลงหน่วยความจำใน ESP32 ก่อน (1 KB สำหรับ 128×64) แล้ว display() ส่งทั้งภาพไปทีเดียว ถ้าจะเปลี่ยนตัวเลขทุกวินาที ให้ clearDisplay() → วาดใหม่ทั้งหมด → display() ในทุกรอบ ภาพจะไม่กะพริบเพราะจอเปลี่ยนแค่ตอน display()

ข้อควรรู้: begin() คืนค่า false เฉพาะตอนจองหน่วยความจำไม่ได้ ถ้าใส่ address ผิด begin() ก็ยังผ่าน แต่จอจะว่าง การเช็กว่าจอตอบหรือไม่จึงต้องใช้ I²C scanner

แสดงรูป

รูปบนจอ OLED เป็นภาพขาวดำ 1 บิต เก็บเป็นอาร์เรย์ไบต์ บิตละหนึ่งจุด แปลงรูปของคุณเป็นอาร์เรย์ได้ด้วยเว็บ image2cpp (ตั้งขนาดไม่เกิน 128×64 เลือก Code output format เป็น Arduino code) ตัวอย่างนี้เป็นรูปหัวใจขนาด 8×8 ที่เขียนเองได้:

const unsigned char heart[] PROGMEM = {
  0b01100110,
  0b11111111,
  0b11111111,
  0b11111111,
  0b01111110,
  0b00111100,
  0b00011000,
  0b00000000,
};

// x, y, รูป, กว้าง, สูง, สี
oled.drawBitmap(60, 28, heart, 8, 8, SSD1306_WHITE);
oled.display();

หน้าและตาที่ขยับได้ เช่นในโปรเจกต์ หุ่นยนต์ตั้งโต๊ะมีหน้า ไม่ต้องใช้รูป วาดด้วย fillRoundRect() และ fillCircle() แล้วเปลี่ยนขนาดตามอารมณ์ ใช้หน่วยความจำน้อยกว่าและขยับได้ลื่นกว่า

ภาษาไทยบนจอ

ฟอนต์ในตัวของ Adafruit GFX มีแค่ตัวอักษรอังกฤษ ถ้าต้องการภาษาไทยมีสองทาง:

จอไม่ขึ้น ไล่ตามนี้

  1. I²C scanner ไม่เจอจอ ปัญหาอยู่ที่สายหรือไฟ เช็กลำดับขาบนจอ ลองสลับ SDA / SCL และเปลี่ยนสายจัมเปอร์
  2. scanner เจอ แต่จอว่าง address ในโค้ดไม่ตรงกับที่ scanner บอก หรือลืมเรียก display()
  3. ภาพเป็นจุดมั่วๆ หรือเลื่อนไป 2 จุด จอ 1.3" ส่วนใหญ่ใช้ชิป SH1106 ไม่ใช่ SSD1306 ให้ใช้ไลบรารี Adafruit SH110X หรือ U8g2 แทน
  4. ภาพแสดงแค่ครึ่งจอหรือยืดเป็นสองเท่า จอเป็นรุ่น 128×32 แต่โค้ดตั้งเป็น 128×64 (หรือกลับกัน) แก้ตัวเลขใน Adafruit_SSD1306 oled(128, 32, …)
  5. ภาพกระตุกหรือหายเป็นช่วงๆ สาย I²C ยาวเกิน 20–30 cm หรือไฟเลี้ยงตกเพราะมอเตอร์หรือ servo ใช้ไฟร่วมกัน (ดู ESP32 รีเซ็ตเองตอนมอเตอร์หมุน)
  6. ต้องการจอสองจอ จอส่วนใหญ่เปลี่ยน address เป็น 0x3D ได้ด้วยการย้ายตัวต้านทานด้านหลัง ถ้าย้ายไม่ได้ให้ใช้ I²C multiplexer TCA9548A

จอ OLED เกิดภาพค้าง (burn-in) ได้ถ้าแสดงภาพเดิมนานเป็นเดือน ถ้าเปิดทั้งวันให้หรี่จอด้วย oled.dim(true) หรือขยับตำแหน่งข้อความเล็กน้อยเป็นระยะ

ลองในตัวจำลอง

หุ่นตั้งโต๊ะในตัวจำลองมีจอ SSD1306 แสดงหน้าและอารมณ์ หน้า Wiring แสดงว่าจอต่อกับ GPIO 21 / 22 อย่างไร และหน้า Code มีโปรแกรมที่เปลี่ยนหน้าจอตามเซนเซอร์

ดูการต่อจอ SSD1306 ในตัวจำลอง →

โปรเจกต์ที่ใช้จอนี้ต่อ: เครื่องวัดฝุ่น PM2.5 ทำเอง และ หุ่นยนต์ตั้งโต๊ะมีหน้า