ต่อจอ 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 | หมายเหตุ |
|---|---|---|
VCC | 3V3 | โมดูลส่วนใหญ่รับ 3.3–5 V ได้ แต่ต่อ 3V3 ปลอดภัยที่สุด เพราะตัวต้านทาน pull-up บนโมดูลจะดึงสาย SDA / SCL ไปที่แรงดันเดียวกับขา ESP32 |
GND | GND | |
SDA | GPIO 21 | ขา I²C มาตรฐานของ ESP32 ใช้ขาอื่นได้ด้วย Wire.begin(sda, scl) |
SCL | GPIO 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);
}
- ขึ้น 0x3C หรือ 0x3D สายถูกแล้ว จดเลขนี้ไว้ใช้ในโค้ด
- ด้านหลังจอเขียน 0x78 / 0x7A เป็นเลขเดียวกันในอีกรูปแบบ (0x3C × 2 = 0x78) ในโค้ด Arduino ให้ใช้ 0x3C / 0x3D
- ไม่พบอะไรเลย SDA กับ SCL สลับกัน ไฟไม่เข้า หรือสายจัมเปอร์หลวม ลองสลับ SDA / SCL ก่อน เป็นสาเหตุที่เจอบ่อยที่สุด
แสดงข้อความ
ติดตั้งไลบรารี 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 มีแค่ตัวอักษรอังกฤษ ถ้าต้องการภาษาไทยมีสองทาง:
- ไลบรารี U8g2 ซึ่งมีฟอนต์ไทยในตัว เช่น
u8g2_font_etl16thai_tแล้วพิมพ์ด้วยdrawUTF8()สระบนและวรรณยุกต์บางตัวอาจวางไม่สวยเท่าบนคอมพิวเตอร์ - ทำข้อความเป็นรูป พิมพ์คำไทยในโปรแกรมวาดภาพ แล้วแปลงด้วย image2cpp เหมาะกับข้อความที่ไม่เปลี่ยน เช่น "อากาศดี"
จอไม่ขึ้น ไล่ตามนี้
- I²C scanner ไม่เจอจอ ปัญหาอยู่ที่สายหรือไฟ เช็กลำดับขาบนจอ ลองสลับ SDA / SCL และเปลี่ยนสายจัมเปอร์
- scanner เจอ แต่จอว่าง address ในโค้ดไม่ตรงกับที่ scanner บอก หรือลืมเรียก
display() - ภาพเป็นจุดมั่วๆ หรือเลื่อนไป 2 จุด จอ 1.3" ส่วนใหญ่ใช้ชิป SH1106 ไม่ใช่ SSD1306 ให้ใช้ไลบรารี Adafruit SH110X หรือ U8g2 แทน
- ภาพแสดงแค่ครึ่งจอหรือยืดเป็นสองเท่า จอเป็นรุ่น 128×32 แต่โค้ดตั้งเป็น 128×64 (หรือกลับกัน) แก้ตัวเลขใน
Adafruit_SSD1306 oled(128, 32, …) - ภาพกระตุกหรือหายเป็นช่วงๆ สาย I²C ยาวเกิน 20–30 cm หรือไฟเลี้ยงตกเพราะมอเตอร์หรือ servo ใช้ไฟร่วมกัน (ดู ESP32 รีเซ็ตเองตอนมอเตอร์หมุน)
- ต้องการจอสองจอ จอส่วนใหญ่เปลี่ยน address เป็น 0x3D ได้ด้วยการย้ายตัวต้านทานด้านหลัง ถ้าย้ายไม่ได้ให้ใช้ I²C multiplexer TCA9548A
จอ OLED เกิดภาพค้าง (burn-in) ได้ถ้าแสดงภาพเดิมนานเป็นเดือน ถ้าเปิดทั้งวันให้หรี่จอด้วย oled.dim(true)
หรือขยับตำแหน่งข้อความเล็กน้อยเป็นระยะ
ลองในตัวจำลอง
หุ่นตั้งโต๊ะในตัวจำลองมีจอ SSD1306 แสดงหน้าและอารมณ์ หน้า Wiring แสดงว่าจอต่อกับ GPIO 21 / 22 อย่างไร และหน้า Code มีโปรแกรมที่เปลี่ยนหน้าจอตามเซนเซอร์
ดูการต่อจอ SSD1306 ในตัวจำลอง →
โปรเจกต์ที่ใช้จอนี้ต่อ: เครื่องวัดฝุ่น PM2.5 ทำเอง และ หุ่นยนต์ตั้งโต๊ะมีหน้า