MicroPython Programming for ESP32 [5]

<rawat.s>
6 min readApr 21, 2020

--

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍

บทความนี้นำเสนอเกี่ยวกับตัวอย่างการเขียนโค้ด MicroPython สำหรับ ESP32 เพื่อเชื่อมต่อกับโมดูลที่ใช้ชิป TM1637 ควบคุมการทำงานของ 7-Segment Display แบบสี่หลัก (Four Digits) และนำมาใช้แสดงตัวเลขเวลาปัจจุบัน ทำงานในลักษณะอุปกรณ์ Digital Clock

การแสดงผลด้วย 7-Segment Display

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍

โมดูล 7-Segment Display เป็นอุปกรณ์หรือชิ้นส่วนอิเล็กทรอนิกส์พื้นฐานที่มีการใช้งานและพบเห็นได้บ่อย หลักการทำงานของอุปกรณ์ชนิดนี้ก็คือ การนำหลอด LED มาจัดเรียงกันจำนวน 7 แท่งหรือขีด (Segment) ระบุชื่อเป็น a,b,c,d,e,f ให้สามารถแสดงตัวเลข เช่น 0 ถึง 9 ได้ ยกอย่างเช่น ถ้าให้ LED ในตำแหน่ง a ถึง f อยู่ในสถานะ ON หรือสว่าง ก็จะแสดงผลเป็นเลข 8

โมดูล 7-Segment Display แต่ละชนิดแตกต่างกันไป เช่น ขนาดและสีของ LED โดยทั่วไปก็จะเป็นสีแดง สีเขียว หรือ สีน้ำเงิน เป็นต้น นอกจากนั้นอาจมีจำนวนตัวเลขที่แสดงผลได้ (Number of Digits) มากกว่าหนึ่งหลัก เช่น 2 หรือ 4 เป็นต้น และมีการเพิ่ม LED ในลักษณะเป็นจุด (Dot) เพื่อการแสดงเลขทศนิยม เป็นต้น

LED ทั้งหมดของโมดูล 7-Segment Display จะมีการต่อขา Anode หรือ Cathode ร่วมกัน แบบใดแบบหนึ่ง ดังนั้นจึงเรียกว่า Common Anode (CA) และ Common Cathode (CC) ตามลำดับ

การควบคุมการทำงานของ LED ของโมดูลแสดงผลนั้น จะต้องใช้ขาสัญญาณดิจิทัลมาควบคุมตามจำนวน LED ที่ใช้ เพื่อจ่ายหรือรับกระแสให้ LED แต่ละดวง (ประมาณ 20..30 mA / Digit)

แต่ถ้าโมดูลมีหลายตำแหน่งในการแสดงผล (Multiple-Digit Display) ก็จะใช้วิธีควบคุมแบบที่เรียกว่า Time-Multiplexing เพื่อลดจำนวนขาควบคุมที่จำเป็นต้องใช้

ในปัจจุบัน มีไอซีที่ทำหน้าที่เป็น LED Driver / LED Controller เช่น TM1637 และ TM1640 สำหรับโมดูลประเภทนี้ หรือสามารถควบคุม LED Matrix เช่น ขนาด 16x8 ได้อีกด้วย และขาสัญญาณเพียง 2 เส้น (แต่ไม่ใช่ I2C Bus) ได้แก่ สัญญาณ Clock และ Data สำหรับการสื่อสารข้อมูลกับไอซีประเภทดังกล่าว

โมดูล TM1637-based 4-Digit 7-Segment Display

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍

ถัดไปเราจะมาลองใช้โมดูล 7-Segment Display ที่มี 4 หลัก ในการแสดงตัวเลข และใช้ชิป TM1637 [Datasheet] เป็นตัวควบคุมการทำงาน อุปกรณ์ที่ได้เลือกมาทดลองใช้งานและสาธิตการทำงาน คือ “RobotDyn ”4-Digit LED Display Tube, 7-segments, TM1637” ของบริษัท RobotDyn ในประเทศจีน มีหลายสีและขนาดให้เลือก เช่น ขนาด 30x14 mm (0.36") หรือ 50x19mm (0.56")

นอกจากขนาดความสูงของตัวเลขแล้ว รูปแบบของการแสดงผล เช่น จุด DP (Decimal Point) ในแต่ละตำแหน่ง หรือจะเป็นแบบ “:” (Double Dots หรือ Clock Colon) ที่เหมาะสำหรับแสดงตัวเลขในรูปแบบนาฬิกาดิจิทัล (Digital Clock)

การนำอุปกรณ์นี้ มาเชื่อมต่อกับโมดูล ESP32 มีดังนี้

RobotDyn TM1637 -- ESP32
CLK -- GPIO4
DIO -- GPIO0
GND -- GND
5V -- 3.3V

ในการต่อวงจรตามตัวอย่าง ขา CLK และ DIO จะรับสัญญาณดิจิทัล (ในทิศทางเอาต์พุต) มาจากขา GPIO4 และ GPIO0 (หรือจะเลือกขา GPIO อื่นแทนก็ได้ ตามความเหมาะสม)

ตำแหน่งขา (Pinout) ของโมดูล RobotDyn 4-Digit LED 0.56" Display
ผังวงจรของโมดูล RobotDyn 4-Digit LED 0.56" Display (TM1637)

ข้อสังเกต: ไอซี TM1637 สามารถทำงานได้ทั้งแรงดันไฟเลี้ยง 3.3V และ 5V แต่เนื่องจากขาลอจิกของ ESP32 อยู่ที่ระดับแรงดัน 3.3V ดังนั้นควรใช้ VCC=3.3V สำหรับโมดูล TM1637

ถ้าใช้ VCC=5V สำหรับ TM1637 แรงดันไฟฟ้าที่ขาอินพุตสำหรับลอจิกที่เป็น High หรือ VIH จะต้องมีระดับอย่างน้อย 0.7*VCC =3.5V ซึ่งสูงกว่า 3.3V ดังนั้นถ้าใช้กับ ขาของ ESP32 อาจจะอ่านค่าลอจิกได้ไม่ถูกต้อง

อย่างไรก็ตาม จากการทดลองพบว่า ถ้าใช้ VCC=3.3V สำหรับโมดูล TM1637 ของ RobotDyn จะเห็นว่า LED Display ไม่ค่อยสว่างเท่าที่ควร จึงลองเปลี่ยนมาใช้ 5V ก็สามารถทำงานได้เช่นกัน และได้ความสว่างมากขึ้น

ตัวอย่างอุปกรณ์ (มุมมองจากด้านหน้า)
ตัวอย่างอุปกรณ์ (มุมมองจากด้านหลัง)

การใช้งานไลบรารี MicroPython-TM1637

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍

ในการเขียนโค้ด MicroPython เพื่อควบคุมการทำงานของโมดูล TM1637 เช่น ให้แสดงผลตัวเลข เราก็สามารถติดตั้งและใช้งานไลบรารีสำหรับ TM1637 โดยใช้วิธีรันโค้ด MicroPython ต่อไปนี้ ซึ่งจะต้องเชื่อมต่อผ่าน WiFi (อย่าลืมตั้งค่า SSID และรหัสผ่านก่อนใช้งาน) ไปยังอินเทอร์เน็ต เพื่อดาวน์โหลดและติดตั้งไฟล์ Python Library ที่มีชื่อว่า micropython-tm1637 ลงในระบบไฟล์ภายใน Flash ของ ESP32

import network
import uos as os
import utime as time
import upip as pip
# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXXX", 'password': "XXXXXXXX" }
def connect_wifi( wifi_cfg, retries = 10 ):
wifi_sta = network.WLAN( network.STA_IF )
wifi_sta.active(True)
wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['password'] )
while not wifi_sta.isconnected():
retries -= 1
if retries < 0:
return None
time.sleep(1.0)
return wifi_sta
if connect_wifi( WIFI_CFG ):
# install micropython-tm1637 to /lib
pip.install('micropython-tm1637', '/lib')
# list the /lib directory
print( os.listdir('/lib') )

เมื่อติดตั้งไลบรารีดังกล่าวได้สำเร็จแล้ว เราก็จะมาลองโค้ดตัวอย่างสาธิตการใช้งานดังนี้

from machine import Pin
from micropython import const
import utime as time
import tm1637
CLK = const(4) # use GPIO-4 for CLK pin
DIO = const(0) # use GPIO-0 for DIO pin
disp = tm1637.TM1637(clk=Pin(CLK), dio=Pin(DIO))
disp.brightness(7) # set brightness level: 0..7try:
# show "12:34" with blinking colon
show_colon = 0x00
for i in range(10):
disp.write([0x06, 0x5B | show_colon, 0x4F, 0x66])
show_colon ^= 0x80 # toggle colon flag
time.sleep_ms(500)
# show some words
for word in ['help','fail','----','24*C']:
disp.show( word )
time.sleep_ms(1000)
# press Ctrl+C to stop
for x in range(10000): # show 0000 to 9999
disp.show( '{:04d}'.format(x) )
time.sleep_ms(100)
except KeyboardInterrupt:
pass
finally:
print('Done')

การแสดงผลตัวเลข สามารถใช้คำสั่ง write(...) สำหรับ TM1637 ซึ่งจะต้องระบุเป็นอาร์เรย์ของข้อมูลไบต์ จำนวน 4 ไบต์ ข้อมูลแต่ละไบต์ (8 บิต) จะถูกนำไปใช้กำหนดสถานะของ LEDs (a ถึง g) ในแต่ละหลักตามลำดับ (DIGITS[0..3] นับจากซ้ายไปขวา)

ยกตัวอย่างเช่น ถ้าเขียนข้อมูลไบต์เท่ากับ 0x7f (hex) หรือ 0b01111111 (bin) จะทำให้ LED Segments ในหนึ่งหลัก อยู่ในสถานะ ON (สว่าง) ทั้งหมด

ในโค้ดตัวอย่าง ถ้าใช้ข้อมูลไบต์ในอาร์เรย์ [0x06, 0x5B, 0x4F, 0x66] กับคำสั่ง write(...) ก็จะแสดงตัวเลขเป็น 1234 ตามลำดับ

Display  Bin   Hex  Dec
0 0b00111111 0x3F 63
1 0b00000110 0x06 6
2 0b01011011 0x5B 91
3 0b01001111 0x4F 79
4 0b01100110 0x66 102
5 0b01101101 0x6D 109
6 0b01111101 0x7D 125
7 0b00000111 0x07 7
8 0b01111111 0x7F 127
9 0b01101111 0x6F 111
....................

กรณีพิเศษคือ ถ้ากำหนดบิต MSB (Most-Significant Bit) ของไบต์ข้อมูลสำหรับ DIGITS[1] ให้เป็น 1 จะทำให้ LED สำหรับ Colon อยู่ในสถานะ ON

อีกคำสั่งหนึ่งที่ง่ายกว่าในการใช้งาน คือ show(...) ซึ่งจะใช้กับข้อความที่มีความยาว 4 ตัวอักขระ เช่น ตัวเลข

ถ้าต้องการจะกำหนดระดับความสว่าง (Brightness Level) ก็ให้ใช้คำสั่ง brightness(...) พร้อมอาร์กิวเมนต์ที่มีค่าในช่วง 0 ถึง 7

การอ่านข้อมูลสำหรับวันเวลาปัจจุบันจาก NTP Server

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍

เมื่อสามารถใช้โมดูลแสดงตัวเลขแบบ 4 หลักได้แล้ว เราจะมาลองโค้ดตัวอย่างสำหรับการติดต่อกับเครื่องแม่ข่ายในอินเทอร์เน็ตที่ทำหน้าที่เป็น NTP Server โดยใช้โพรโทคอลที่มีชื่อว่า NTP (Network Time Protocol) และใช้รูปแบบการรับส่งข้อมูลแบบ UDP (User Datagram Protocol) ในตัวอย่างนี้ได้เลือกใช้ th.pool.ntp.org พอร์ต 123 สำหรับการเชื่อมต่อ

เมื่อได้รับข้อมูลกลับจาก NTP Server โดยเรียกฟังก์ชัน get_ntp_time(...) ตามโค้ดตัวอย่าง จะได้เป็นตัวเลขระบุเวลาในหน่วยวินาที นับตั้งแต่วันที่ 1 มกราคม ค.ศ. 1900 แต่เนื่องจากว่า การระบุวันเวลาปัจจุบันเพื่อใช้กับ RTC (Real-Time Clock) ของ ESP32 จะเริ่มนับตั้งแต่วันที่ 1 มกราคม ค.ศ. 2000 ดังนั้นจะต้องมาคำนวณผลต่างระหว่างสองวันดังกล่าว ซึ่งจะได้เท่ากับ 3155673600 วินาที ดังนี้

3155673600 sec = (date(2000,1,1) - date(1900,1,1)) * (24*60*60)

นอกจากนั้นเวลาที่ได้จาก NTP จะอ้างอิงตาม UTC ถ้าจะเปลี่ยนให้เป็นเวลาประเทศไทย หรือ GMT+7 จะต้องเพิ่มไปอีก 7 * 60 * 60 = 25200 วินาที

เมื่อได้ตัวเลขสำหรับเวลาในปัจจุบันตามเวลาประเทศไทย ซึ่งนับตั้งแต่ 1 มกราคม ค.ศ. 2000 เราจะใช้ฟังก์ชัน localtime(...) ของไลบรารี utime สำหรับ MicroPython แปลงจากตัวเลขวินาที ให้เป็นโครงสร้างข้อมูลแบบ time.struct_time และได้เป็นข้อมูล 8-Tuple ตามลำดับดังนี้

  • ปีค.ศ. (year) ระบุเป็นตัวเลขตั้งแต่ 2000
  • เดือนของปี (month) ระบุเป็นตัวเลข 1–12
  • วันที่ของเดือน (mday) ระบุเป็นตัวเลข 1–31
  • ชั่วโมง (hour) ระบุเป็น 0–23
  • นาที (minute) ระบุเป็น 0–59
  • วินาที (second) ระบุเป็น 0–59
  • วันที่ของสัปดาห์ (weekday) ระบุเป็น 0–6 (Mon-Sun)
  • วันที่ของปี (yearday) ระบุเป็น 1–366

ข้อมูลที่ได้จากการเรียกฟังก์ชัน utime.localtime(…) จะถูกนำไปใช้กำหนดวันเวลาปัจจุบันให้ RTC โดยใช้ฟังก์ชัน machine.RTC.datetime(…)

เมื่อได้ตั้งเวลาให้วงจร RTC ภายใน ESP32 ตามเวลาปัจจุบันแล้ว เราก็สามารถอ่านค่า เช่น ชั่วโมงและนาที มาแสดงข้อมูลบนโมดูล TM1637 4-Digit 7-Segment Display ได้

from machine import Pin, RTC
from micropython import const
import network
import uos as os
import usocket as socket
import utime as time
import ustruct as struct
import tm1637
import _thread
# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXX", 'password': "XXXXXX" }
NTP_HOST = "th.pool.ntp.org"def connect_wifi( wifi_cfg, retries = 10 ):
wifi_sta = network.WLAN( network.STA_IF )
wifi_sta.active(True)
wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['password'] )
while not wifi_sta.isconnected():
retries -= 1
if retries < 0:
return None
time.sleep(1.0)
return wifi_sta
def get_ntp_time( ntp_host, ntp_port=123, timeout=5 ):
ntp_data = bytearray(48)
ntp_data[0] = 0x1b
addr = socket.getaddrinfo(ntp_host, ntp_port)[0][-1]
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
seconds = 0
try:
s.settimeout( timeout ) # set socket timeout
res = s.sendto( ntp_data, addr )
msg = s.recv(48) # receive 48 bytes as response
seconds = struct.unpack("!I", msg[40:44])[0]
except OSError as ex:
pass
finally:
s.close()
# epoch time in seconds since Jan 1, 1900 00:00:00 UTC
return seconds
def set_rtc( tm ):
year, month, day, hour, min, sec, wday, yday = tm
print(year, month, day, hour, min, sec, wday, yday)
# (year, month, day, wday, hour, min, sec, subsec)
rtc_tm = (tm[0],tm[1],tm[2],tm[6],tm[3],tm[4],tm[5],0)
RTC().datetime( rtc_tm )
def show_digital_clock(disp):
cnt = 0
while True:
tm = RTC().datetime()
hh, mm = tm[4],tm[5]
cnt ^= 1
if cnt == 0:
disp.numbers(hh,mm)
else:
disp.show('{0:02d}{1:02d}'.format(hh,mm))
time.sleep_ms(500)
CLK = const(4) # use GPIO-4 for CLK pin
DIO = const(0) # use GPIO-0 for DIO pin
disp = tm1637.TM1637(clk=Pin(CLK), dio=Pin(DIO))
disp.brightness(7) # set brightness level: 0..7
disp.show('----')
rtc_ntp_sync = Falseif connect_wifi( WIFI_CFG ):
ts = get_ntp_time( NTP_HOST ) # get NTP time
if ts > 0:
tm = time.localtime(ts - 3155673600 + 25200) # GMT+7
set_rtc( tm ) # set RTC time
rtc_ntp_sync = True
else:
print('NTP error')
else:
print('Network error')
if rtc_ntp_sync:
_thread.start_new_thread( show_digital_clock,(disp,) )
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
_thread.exit() # stop threads
finally:
print('Done')
ตัวอย่างอุปกรณ์และการต่อวงจรทดลอง

เพิ่มเติม: ถ้าไม่ต้องการเขียนโค้ดเองสำหรับเชื่อมต่อ NTP Server เราก็สามารถใช้คำสั่ง settime() ของไลบรารีชื่อ ntptime ได้เลย (ต้องเชื่อมต่อ WiFi ด้วย) ตามโค้ดตัวอย่างดังนี้

import network
import ujson as json
from machine import RTC
import utime as time
import ntptime
wifi_config = None
try:
# read json file 'wifi_config.json' for WiFi configuration:
# { "ssid": "XXXXX", "password": "XXXXXXXX" }
with open( 'wifi_config.json' ) as f:
wifi_config = json.load(f)
except OSError:
print('Cannot open configuration file')
def connect_wifi( wifi_cfg, retries=10 ):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect( wifi_cfg['ssid'], wifi_cfg['password'] )
while not wlan.isconnected():
retries -= 1
if retries < 0:
return None
time.sleep_ms(1000)
return wlan
wifi = connect_wifi( wifi_config )
if wifi is None:
print('WiFi connection failed...')
while True:
pass
while True:
try:
# synchronize RTC with NTP server
ntptime.settime()
break
except OSError:
print('NTP server: connection timeout...')
# create an RTC object
rtc = RTC()
# read current datetime from RTC
year,month,day,wday,hour,minute,second,_ = rtc.datetime()
# 0=Mon, 1=Tue, ...
week_days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun' ]
# show current date
print( '{} {}/{}/{}'.format(week_days[wday],day,month,year) )
# show current time (GMT+7)
print( '{}:{}:{}'.format(hour+7, minute, second) )
wifi.active(False)

โดยสรุป ในบทความนี้ เราได้เห็นตัวอย่างการใช้ไลบรารีของ MicroPython สำหรับโมดูลแสดงผลแบบ 7-Segment Display ที่ใช้ชิป TM1637 และได้เห็นตัวอย่างการสร้างฟังก์ชัน เพื่ออ่านข้อมูลสำหรับวันเวลาในปัจจุบันจาก NTP Server ในอินเทอร์เน็ต แล้วนำมาตั้งค่าวันเวลาปัจจุบัน ให้แก่วงจร RTC ภายใน ESP32 และสุดท้ายเราก็สามารถเขียนโค้ด MicroPython เพื่อสร้างเป็นอุปกรณ์ Digital Clock เอาไว้แสดงเลขชั่วโมงและนาที ตามเวลาปัจจุบันได้

--

--

<rawat.s>
<rawat.s>

Written by <rawat.s>

I'm Thai and working in Bangkok/Thailand.

No responses yet