Raspberry Pi + OLED แสดง IP และสถานะเครือข่ายแบบ Headless
Raspberry Pi ที่ทำงานแบบ Headless อาจรันระบบอยู่ได้ทั้งวันโดยไม่ต้องต่อ Monitor แต่ปัญหาคือเวลาเครื่องเปลี่ยน IP จาก DHCP หรือหลุดจาก Network เราอาจต้องเข้า Router หรือเริ่มไล่เช็กก่อน ถึงจะรู้ว่าเครื่องอยู่ที่ไหน
โปรเจกต์นี้แก้ Pain Point แบบตรงไปตรงมา: เพิ่ม OLED I²C ขนาด 0.91 นิ้ว ให้ Raspberry Pi 3B แล้วใช้ Python แสดงสถานะ ONLINE / OFFLINE, IP Address และ Local Time ไว้บนตัวเครื่องตลอดเวลา
จุดเด่นไม่ใช่แค่มีจอ: เมื่อ Script ถูกตั้งให้เริ่มผ่าน systemd Raspberry Pi สามารถ Boot แล้วเปิด Network Status Display ได้เองโดยไม่ต้อง Login หรือเปิดโปรแกรมด้วยมือทุกครั้ง
Key Takeaways
- Source ใช้ Raspberry Pi 3B เป็น Test Platform
- Display เป็น OLED I²C ขนาด 0.91 นิ้ว ความละเอียด 128×32 pixels
- จอเป็น White Monochrome
- OLED เชื่อมกับ Raspberry Pi ด้วย 4 เส้น
- Power ใช้ Pin 1 — 3.3V และ Pin 6 — GND
- SDA ใช้ Pin 3 / GPIO2
- SCL ใช้ Pin 5 / GPIO3
- Display ใน Setup นี้ใช้ I²C Address
0x3C - ตรวจ Address ด้วย
i2cdetect -y 1 - Python Code ใช้ luma.oled + Pillow
- Device Class ที่ Script ใช้คือ
ssd1306 - หน้าจอที่หนึ่งแสดง ONLINE หรือ OFFLINE
- หน้าจอที่สองแสดง IP Address และ Local Time
- สองหน้าสลับทุก 2 วินาที
- Network Check ใช้ Timeout 1.5 วินาที
- สถานะ ONLINE ใน Code มาจากการเชื่อมไป
8.8.8.8:53สำเร็จ - ถ้าหา IP ไม่ได้ จอจะแสดง
IP: --- - Script รองรับ SIGTERM / SIGINT และ Clear Display ก่อนจบ
- systemd Service ใช้
Restart=always - Service Example ระบุ User เป็น
raspberry
ทำไม Headless Raspberry Pi ควรมีจอสถานะเล็ก ๆ?
Raspberry Pi สามารถทำงานได้โดยไม่ต้องมี Monitor, Keyboard และ Mouse ต่ออยู่ตลอดเวลา
ในงาน IoT, Prototype หรือระบบที่วางทิ้งไว้บน Network วิธีนี้ช่วยลดอุปกรณ์รอบตัว และทำให้ Pi ทำหน้าที่เหมือน Device หนึ่งตัว มากกว่าคอมพิวเตอร์ Desktop
แต่เมื่อ Network ใช้ DHCP IP Address สามารถเปลี่ยนได้ และถ้าต้อง SSH เข้าเครื่อง คำถามแรกมักกลายเป็น:
“ตอนนี้ Pi ได้ IP อะไร?”
OLED เล็ก ๆ จึงทำหน้าที่เหมือน Local Status Panel: ไม่ต้อง Login ก่อน ก็เห็นได้ว่า Script มองว่าเครื่อง Online หรือ Offline และ IP ที่ตรวจพบตอนนั้นคืออะไร
Hardware ที่ใช้ใน Project
| รายการ | ข้อมูลที่ Source ระบุ |
|---|---|
| Single-board Computer | Raspberry Pi 3B |
| Power Supply | micro-USB Power Supply 2.5A |
| Display | 0.91" I²C OLED |
| Resolution | 128×32 pixels |
| Display Color | White Monochrome |
| Jumper Wires | Female-Female Dupont ×4 |
| Remote Terminal | PuTTY SSH Client |
Source ระบุ Power Supply เป็น 2.5A แต่ไม่ได้ให้รายละเอียด Power ทั้งชุดเพิ่มเติมในบทความ: บทความนี้จึงไม่สร้างค่า Power Consumption, OLED Current หรือกำลังไฟรวมของ Project เพิ่มเอง
เพิ่ม OLED 0.91 นิ้วให้ Raspberry Pi
Display ขนาด 128×32 มีพื้นที่ไม่มาก ดังนั้น Creator ไม่พยายามยัดข้อมูลทั้งหมด ลงหน้าเดียว
แต่ใช้แนวคิด 2 Pages แล้วสลับกัน
ONLINE / OFFLINE
↓ ทุก 2 วินาที ↓
Page 2
IP Address + Local Time
ต่อ OLED ผ่าน I²C เพียง 4 เส้น
| หน้าที่ | Raspberry Pi Pin | GPIO / Signal |
|---|---|---|
| OLED Power | Pin 1 | 3.3V |
| Ground | Pin 6 | GND |
| I²C Data | Pin 3 | GPIO2 / SDA |
| I²C Clock | Pin 5 | GPIO3 / SCL |
บทความต้นฉบับใช้ OLED Power ที่ 3.3V: Source ไม่ได้ให้ Wiring แบบ 5V, Pull-up Resistor, Level Shifter หรือ I²C Bus Speed จึงไม่มีการเพิ่มวงจรเหล่านั้นเอง
ตรวจว่า OLED ถูกพบที่ Address 0x3C
หลังต่อสาย Creator ใช้:
i2cdetect -y 1
Display ใน Setup ของ Creator ถูกพบที่:
0x3C
0x3C เป็น Address ของ Display ใน Project นี้: ไม่ควรสมมติว่า OLED I²C ทุก Module จะใช้ Address เดียวกัน
Source ไม่ได้ให้ขั้นตอนเปิด I²C หรือ Install i2c-tools: ดังนั้นบทความนี้ไม่สร้างคำสั่ง raspi-config, apt หรือขั้นตอน Package Installation เพิ่มเอง
Python Stack: luma.oled + Pillow + SSD1306
Creator ใช้ luma.oled เป็น Python Interface สำหรับคุยกับ OLED และใช้ Pillow ในการสร้าง Image, วาด Text และจัด Layout ก่อนส่งขึ้น Display
Import ที่อยู่ใน Source Code คือ:
from PIL import Image, ImageDraw, ImageFont
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
Library รองรับหลาย Display ไม่ได้หมายความว่า Script นี้รองรับทุกตัวโดยไม่แก้: Source Code instantiate ssd1306 โดยตรง ดังนั้น Current Script ถูกเขียนสำหรับ SSD1306 ตาม Source
Source ไม่ได้ให้ Python Version, Raspberry Pi OS Version, luma.oled Version, Pillow Version หรือ Package Installation Commands บทความนี้จึงไม่สร้าง Version และคำสั่งติดตั้งขึ้นเอง
ONLINE / OFFLINE ใน Script ถูกตรวจอย่างไร?
จุดนี้ควรอ่านจาก Code มากกว่าดูคำว่า “Network Status” อย่างเดียว
Function:
get_ip_and_online(timeout=1.5)
แยกงานออกเป็นสองส่วน
1. หา IP Address ที่ระบบกำลังใช้
Script สร้าง UDP Socket และเรียก:
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
IP ที่นำไปแสดง คือ Source Address ที่ Socket ได้จาก Route ไปยังปลายทางดังกล่าว
2. ตรวจสถานะ Online
Code ทดลองเปิด Connection ไป:
8.8.8.8:53
socket.create_connection(
("8.8.8.8", 53),
timeout=timeout
).close()
online = True
Default Timeout คือ:
1.5 วินาที
คำว่า ONLINE ใน Project นี้มี Definition เฉพาะ: มันหมายถึง Script สามารถเปิด Connection ไปยัง 8.8.8.8:53 ภายใน Timeout ได้ ไม่ใช่การพิสูจน์ว่า Internet Service ทุกปลายทาง หรือ Network ทุกส่วนทำงานปกติ
OLED แสดงอะไรบ้าง?
หน้า 1 — ONLINE / OFFLINE
หน้าแรกใช้ Font ขนาดใหญ่ และจัด Text ไว้กึ่งกลางจอ
Script เลือกข้อความ:
"ONLINE" if online else "OFFLINE"
หน้า 2 — IP + Local Time
หน้าที่สองแสดงสองบรรทัด:
HH:MM:SS
ถ้า Script ไม่สามารถหา IP ได้ จะแสดง:
IP: ---
Page Timing
Source กำหนด:
PAGE_TIME = 2.0
และใช้:
page ^= 1
เพื่อสลับระหว่าง Page 0 และ Page 1 ทุก 2 วินาที
Font
Script พยายามโหลด:
/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf
โดยใช้:
- Font ใหญ่: 18
- Font เล็ก: 12
ถ้าโหลดไม่ได้ จะ Fallback ไป:
ImageFont.load_default()
Python Code ฉบับเต็มจาก Source
Script รวมทั้ง:
- I²C Initialization
- SSD1306 Device
- Network Check
- IP Detection
- Font Loading
- Page Switching
- SIGTERM / SIGINT Handling
- Display Clear ตอนจบ
#!/usr/bin/env python3
"""
Netwerkstatus OLED-display voor Raspberry Pi
Geschikt voor SSD1306 128×32 (0x3C)
Toont ONLINE/OFFLINE en IP-adres met tijd
"""
import time, socket, signal, sys
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
I2C_ADDR = 0x3C # jouw displayadres
PAGE_TIME = 2.0 # seconden per pagina-wissel
def get_ip_and_online(timeout=1.5):
"""Geef (ip, online_bool)."""
ip, online = None, False
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(timeout)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
except Exception:
pass
try:
socket.create_connection(("8.8.8.8", 53), timeout=timeout).close()
online = True
except Exception:
pass
return ip, online
def load_font(size):
try:
return ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", size
)
except Exception:
return ImageFont.load_default()
def draw_centered(draw, text, font, w, h):
tw, th = draw.textbbox((0, 0), text, font=font)[2:]
draw.text(((w - tw) // 2, (h - th) // 2), text, font=font, fill=255)
def main():
serial = i2c(port=1, address=I2C_ADDR)
device = ssd1306(serial)
W, H = device.width, device.height
font_big = load_font(18)
font_small = load_font(12)
stop = {"flag": False}
signal.signal(signal.SIGTERM, lambda *_: stop.update(flag=True))
signal.signal(signal.SIGINT, lambda *_: stop.update(flag=True))
page = 0
while not stop["flag"]:
ip, online = get_ip_and_online()
img = Image.new("1", (W, H), 0)
d = ImageDraw.Draw(img)
if page == 0:
draw_centered(d, "ONLINE" if online else "OFFLINE", font_big, W, H)
else:
d.text((0, 2), f"IP: {ip or '---'}", font=font_small, fill=255)
d.text((0, 18), datetime.now().strftime("%H:%M:%S"), font=font_small, fill=255)
device.display(img)
time.sleep(PAGE_TIME)
page ^= 1
device.clear()
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"net_oled error: {e}", file=sys.stderr)
try:
ssd1306(i2c(port=1, address=I2C_ADDR)).clear()
except Exception:
pass
sys.exit(1)
Graceful Shutdown
Script ฟัง:
- SIGTERM
- SIGINT
เมื่อได้รับ Signal จะออกจาก Loop แล้วเรียก:
device.clear()
ถ้าเกิด Exception ที่ระดับ Main Code ก็พยายามสร้าง SSD1306 Device และ Clear จออีกครั้งก่อน Exit
ทำให้ OLED Status Script เริ่มเองหลัง Boot ด้วย systemd
Source เก็บ Python Script ไว้ที่:
/usr/local/bin/net_oled.py
แล้วทำให้ไฟล์ Executable ด้วย:
sudo chmod +x /usr/local/bin/net_oled.py
Service File อยู่ที่:
/etc/systemd/system/net-oled.service
[Unit]
Description=OLED Netwerkstatus Display
After=network-online.target
[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/net_oled.py
Restart=always
User=raspberry
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
Service File ทำอะไร?
| Setting | ค่าจาก Source |
|---|---|
| Start Order | After=network-online.target |
| Python Command | /usr/bin/python3 /usr/local/bin/net_oled.py |
| Restart Policy | Restart=always |
| User | raspberry |
| Environment | PYTHONUNBUFFERED=1 |
| Install Target | multi-user.target |
Service Example ผูกกับ User ชื่อ raspberry: ถ้า Raspberry Pi ใช้ Username อื่น Source ไม่ได้ให้ Service File สำหรับ Username นั้นโดยตรง จึงควรตรวจค่าก่อนนำ Example ไปใช้
Source กล่าวถึง daemon-reload และ enable --now หลังสร้าง Service แต่ข้อความต้นฉบับที่ใช้บทความนี้ ไม่ได้ให้ Shell Commands แบบเต็ม จึงไม่มีการสร้าง Command เพิ่มเอง
ดู OLED Network Status ทำงานจริง
หลังตั้งค่า Script และ systemd จอจะสลับข้อมูล Network Status, IP Address และ Local Time ตาม Logic ใน Python Code
สิ่งที่ Source ยังไม่ได้ระบุ
| Source ระบุ | Source ไม่ได้ระบุ |
|---|---|
| Raspberry Pi 3B | Compatibility Test กับ Pi 4 / Pi 5 / Pi Zero |
| OLED 0.91" 128×32 | ผู้ผลิต / Part Number ของ Module |
| I²C Address 0x3C ใน Test Setup | Address ของ OLED ทุก Module |
| SSD1306 Device Class ใน Python | Code สำหรับ SH1106 |
| Python + luma.oled + Pillow | Version ของ Software Packages |
i2cdetect -y 1 |
ขั้นตอน Install i2c-tools |
| I²C Wiring | Bus Speed / Pull-up Values / Cable Length |
| 2.5A Power Supply | System Power Consumption |
| ONLINE Check ผ่าน 8.8.8.8:53 | Multi-endpoint Network Health Check |
| IP จาก Route ที่ Script ใช้ | การแสดง IP ของทุก Network Interface |
| User=raspberry ใน systemd | Service Config สำหรับ Username อื่น |
Maker เรียนรู้อะไรจาก Project นี้?
1. Headless ไม่ได้แปลว่าต้องมองไม่เห็นสถานะ
Raspberry Pi ไม่จำเป็นต้องต่อ Monitor ตลอด แต่ข้อมูลสำคัญอย่าง IP สามารถถูกดึงออกมาแสดงบน Local OLED ได้โดยตรง
2. จอเล็กควรเลือกข้อมูลให้เหมาะกับพื้นที่
128×32 pixels ไม่ได้มีพื้นที่มาก Creator จึงใช้สองหน้า แทนการย่อทุกอย่าง ให้ลงจอเดียว
3. นิยามคำว่า ONLINE ให้ชัด
ใน UI เราเห็นคำว่า ONLINE แต่สิ่งสำคัญสำหรับคนทำระบบคือ ต้องรู้ว่า Code ใช้วิธีไหนตัดสินสถานะนั้น
Project นี้นิยามจาก Connection ไป 8.8.8.8:53 ไม่ใช่ Network Diagnostic ทุกประเภท
4. systemd ทำให้ Prototype เริ่มกลายเป็น Appliance
เมื่อ Script ไม่ต้องรอให้ผู้ใช้ SSH เข้าไป Run ทุกครั้งหลัง Boot Raspberry Pi ก็เข้าใกล้การทำงานแบบ Standalone Device มากขึ้น
เหมาะกับ Project แบบไหน? แนวคิดเดียวกันสามารถเป็น Inspiration สำหรับ Raspberry Pi ที่ต้องวางทำงานแบบ Headless และต้องการ Local Status Display แต่ Feature เพิ่มเติม เช่น Sensor, CPU Temperature, Storage หรือ Service Health ไม่ได้อยู่ใน Source นี้ จึงไม่มีการสร้าง Code เพิ่มเองในบทความ
โปรเจกต์นี้มีหลายขั้น แนะนำอ่านต้นฉบับควบคู่กัน
ถ้าจะ Build ตาม แนะนำอ่าน Main Source เพื่อดูรูป Wiring, Test Setup และ Context ของ Python Script ควบคู่กับ Code ในบทความนี้
FAQ: Raspberry Pi OLED Network Status
Project นี้ใช้ Raspberry Pi รุ่นอะไร?
Source ใช้ Raspberry Pi 3B เป็น Test Platform
OLED มีขนาดเท่าไร?
0.91 นิ้ว ตาม Hardware List ของ Source
OLED Resolution เท่าไร?
128×32 pixels และเป็น White Monochrome Display
OLED ใช้ I²C หรือ SPI?
Project นี้ใช้ I²C
OLED ต่อ Pin ไหนบน Raspberry Pi?
Source ใช้ Pin 1 สำหรับ 3.3V, Pin 6 สำหรับ GND, Pin 3 / GPIO2 สำหรับ SDA และ Pin 5 / GPIO3 สำหรับ SCL
OLED ทุกตัวใช้ Address 0x3C หรือไม่?
Source ไม่ได้ระบุเช่นนั้น Display ที่ใช้ใน Project นี้ ถูกตรวจพบที่ 0x3C
ตรวจ I²C Address อย่างไร?
Source ใช้ i2cdetect -y 1 แล้วตรวจว่า Display ปรากฏที่ 0x3C
Python ใช้ Library อะไร?
ใช้ luma.oled สำหรับ OLED Interface และ Pillow สำหรับสร้าง Image, Text และ Graphic Buffer
Code นี้ใช้ SSD1306 หรือ SH1106?
Current Code import และ instantiate ssd1306 โดยตรง Source ไม่ได้ให้ Code สำหรับเปลี่ยนไปใช้ SH1106
OLED แสดงข้อมูลอะไร?
หน้าแรกแสดง ONLINE หรือ OFFLINE ส่วนหน้าที่สองแสดง IP Address และ Local Time
หน้าจอสลับทุกกี่วินาที?
ทุก 2 วินาที ตามค่า PAGE_TIME = 2.0
ONLINE ถูกตรวจอย่างไร?
Script ทดลองเปิด TCP Connection ไปยัง 8.8.8.8:53 ภายใน Timeout 1.5 วินาที ถ้าสำเร็จจะตั้ง online = True
ถ้าหา IP ไม่ได้ จอแสดงอะไร?
Code ใช้ ip or '---' ดังนั้นหน้าจอจะแสดง IP: ---
Script เริ่มเองหลัง Reboot ได้ไหม?
ได้ตาม Project นี้ โดย Source สร้าง systemd Service สำหรับเรียก /usr/local/bin/net_oled.py หลัง Boot
ต้องใช้ User ชื่อ raspberry หรือไม่?
Service Example ใน Source ระบุ User=raspberry Source ไม่ได้ให้ Config สำหรับ Username อื่น จึงควรตรวจค่าให้ตรงกับระบบจริง
ใช้กับ Raspberry Pi 4 หรือ Raspberry Pi 5 ได้ไหม?
Source ทดสอบและระบุ Raspberry Pi 3B เท่านั้น ไม่ได้ให้ Compatibility Test กับ Pi รุ่นอื่น
สรุป: OLED เล็ก ๆ ช่วยให้ Headless Raspberry Pi ดูแลง่ายขึ้น
Project นี้ไม่ได้พยายามทำ Dashboard ใหญ่หรือซับซ้อน
แต่เลือกข้อมูลสามอย่าง ที่มีประโยชน์มากสำหรับ Headless System:
- Online / Offline
- IP Address
- Local Time
แล้วเชื่อม Flow ตั้งแต่ Network Check ไปจนถึง OLED ด้วย Python เพียง Script เดียว
↓
Python socket
↓
IP + ONLINE/OFFLINE
↓
Pillow
↓
luma.oled
↓
I²C
↓
SSD1306 OLED
ส่วน systemd ทำให้จอไม่ต้องพึ่งการ Login หรือการ Run Script ด้วยมือหลัง Reboot
สำหรับ Maker นี่จึงเป็นตัวอย่างที่ดีว่า การเพิ่ม Feedback เล็ก ๆ บน Hardware สามารถทำให้ Headless Device ใช้งานจริงสะดวกขึ้นมาก โดยไม่จำเป็นต้องเพิ่ม Monitor เข้าไปทั้งชุด
References / แหล่งข้อมูลต้นฉบับ
กำลังทำ Raspberry Pi หรือ IoT Project แบบ Headless?
การเพิ่ม Local Display เล็ก ๆ ช่วยให้ Device บอกสถานะของตัวเองได้ โดยไม่ต้องต่อ Monitor หรือ Login เข้าเครื่องก่อนทุกครั้ง
ถ้ากำลังทำ Raspberry Pi, IoT, OLED Display, Sensor หรือ Maker Project สามารถสอบถามทีม Globalbyte เรื่อง Development Board, Display, Jumper Wire, Module และอุปกรณ์ที่เหมาะกับ Project ได้
บทความนี้ไม่ได้ยืนยันว่า Raspberry Pi 3B หรือ OLED รุ่นเดียวกับต้นฉบับ มี Stock อยู่ที่ Globalbyte กรุณาสอบถามรุ่นและ Availability ก่อนสั่งซื้อ