บทความนี้กล่าวถึง การใช้ติดตั้งและใช้งาน MicroPython สำหรับบอร์ดไมโครคอนโทรลเลอร์ที่ใช้ ESP8266EX SoC ของบริษัท Espressif Systems และสาธิตการทำงานโดยใช้โค้ดตัวอย่างและทดลองกับอุปกรณ์ฮาร์ดแวร์จริง
ESP8266 WiFi SoC
ESP8266EX เป็นชิปประเภท SoC (System-on-Chip) ที่มีราคาถูก ภายในมีซีพียู Xtensa LX106 ความเร็ว 80MHz (หรือ 160MHz สำหรับ Double Clock Speed) และสามารถเชื่อมต่อกับเครือข่ายไร้สาย WiFi 2.4 GHz ได้
เริ่มมีการจำหน่ายในราวปีค.ศ. 2014 เช่น บอร์ด NodeMCU ในช่วงแรกก็มาพร้อมกับเฟิร์มแวร์สำหรับเขียนโค้ดในภาษา Lua ต่อมา ก็สามารถเขียนโค้ด C/C++ โดยใช้ Arduino ได้ ทำให้ได้รับความนิยมอย่างมาก
MicroPython เริ่มต้นใช้งานและพัฒนาโดย Damien P. George ตั้งแต่ราวปีค.ศ. 2014 (Release v1.0, May 4, 2014) แต่ใช้กับบอร์ดไมโครคอนโทรลเลอร์ตระกูล STM32F4 ในเวอร์ชันแรก ต่อมาในราวปีค.ศ. 2016 MicroPython (v1.8.3) ก็สามารถนำมาใช้กับบอร์ดอื่นได้ เช่น ESP8266 และ BBC Micro:bit
ในบทความนี้ เราจะมาลองใช้ MicroPython เวอร์ชัน v1.12 เขียนโค้ดโดยใช้ภาษา Python 3 สำหรับไมโครคอนโทรลเลอร์ และใช้บอร์ด ESP8266 สำหรับทดสอบการทำงานของโค้ดตัวอย่าง และผู้เขียนก็คิดว่า หลายคนก็คงจะมีบอร์ดนี้ หรือเคยใช้งานมาบ้างแล้ว แต่ส่วนใหญ่คงใช้ได้เขียนโปรแกรมโดยใช้ Arduino เป็นหลัก
ตัวอย่างบอร์ด ESP8266 ที่สามารถนำมาใช้ได้ เช่น Adafruit Feather HUZZAH, NodeMCU DevKit v1.0, WeMos D1 Mini เป็นต้น
ขั้นตอนการติดตั้งเฟิร์มแวร์ MicroPython
ดาวน์โหลดเฟิร์มแวร์ (.bin) สำหรับ MicroPython เพื่อนำมาติดตั้งใช้งานร่วมกับบอร์ด ESP8266 (4MB Flash) ผู้ใช้สามารถดาวน์โหลดไฟล์ได้จากเว็บนี้
ในบทความนี้ได้ทดลองใช้ไฟล์ที่เป็นเฟิร์มแวร์ เวอร์ชัน v1.12–388
File: esp8266–20200421-v1.12–388-g388d419ba.bin
ข้อสังเกต: การเลือกใช้ไฟล์เฟิร์มแวร์สำหรับ MicroPython-ESP8266 นั้นมีการแบ่งเป็นหลากตัวเลือก
- ตามขนาดความจุของหน่วยความจำ Flash ที่มีอยู่บนบอร์ด เช่น 512KB, 1MB, 2MB หรือมากกว่า
- Stable กับ Daily Build (Experimental)
- Non-OTA และ OTA Support (OTA = Over-the-Air Update ช่วยให้สามารถอัปโหลดโปรแกรมในครั้งต่อ ๆ ไปผ่าน Wi-Fi ได้)
การอัปโหลดไฟล์ที่เป็นเฟิร์มแวร์ของ MicroPython เราจะใช้โปรแกรมชื่อ esptool
(ใช้ภาษา Python ในการพัฒนา) สามารถติดตั้งในเครื่องคอมพิวเตอร์โดยใช้คำสั่ง pip
ดังต่อไปนี้ (ต้องติดตั้ง Python 3.x ไว้แล้ว) เราจะทำคำสั่งแบบ Command Line โดยใช้ Git CMD Shell สำหรับ Windows
$ python3 -m pip install esptool
เมื่อติดตั้งโปรแกรม esptool
แล้ว ให้เชื่อมต่อบอร์ด ESP8266 เช่น NodeMCU, WeMos D1 Mini หรือบอร์ด ESP8266 (Generic) กับคอมพิวเตอร์ที่พอร์ต USB โดยทั่วไปจะก็มองเห็น Serial COM port สำหรับ Windows
ลองทำคำสั่งเพื่อเชื่อมต่อและอ่านข้อมูลเกี่ยวกับหน่วยความจำ Flash ของ ESP8266 ดังนี้
$ python -m esptool flash_id
ตัวอย่างข้อความเอาต์พุต เมื่อทำคำสั่งดังกล่าว และในตัวอย่างนี้ แสดงให้เห็นว่า บอร์ด ESP8266 มีขนาด Flash (SPI Flash) เท่ากับ 4 MB มี MAC Address ของ WiFi ตรงกับ 84:f3:eb:07:05:c9
และมีหมายเลข Serial Port ตรงกับ COM147
esptool.py v2.8
Found 1 serial ports
Serial port COM147
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 84:f3:eb:07:05:c9
Uploading stub...
Running stub...
Stub running...
Manufacturer: c8
Device: 4016
Detected flash size: 4MB
Hard resetting via RTS pin...
หลังจากนั้นทำคำสั่งตามลำดับดังนี้ คำสั่งแรกเพื่อลบข้อมูลในหน่วยความจำ Flash ทั้งหมด (Flash Erase) และคำสั่งที่สองสำหรับเขียนไฟล์เฟิร์มแวร์ไปยังบอร์ดในหน่วยความจำ Flash
$ python -m esptool erase_flash$ python -m esptool --baud 460800 write_flash 0 esp8266–20200421-v1.12–388-g388d419ba.bin
ถ้าทำขั้นตอนได้สำเร็จ เราก็เปิดโปรแกรม Thonny IDE เพื่อลองใช้งาน MicroPython ร่วมกับบอร์ด ESP8266 (ถ้ายังไม่มีโปรแกรมนี้ ก็ให้ดาวน์โหลดและติดตั้งก่อน)
เมื่อเปิดใช้งานโปรแกรมแล้ว เริ่มต้นไปที่เมนู Run > Select Interpreter จากนั้นเลือก MicroPython (generic) และเลือกหมายเลข Serial Port ของบบอร์ด ESP8266 ที่เชื่อมต่อกับคอมพิวเตอร์
ข้อสังเกต: โปรแกรมอย่างเช่น Thonny IDE จะใช้พอร์ตอนุกรม (Serial) เพื่อเชื่อมต่อกับ MicroPython โดยใช้รูปแบบการทำงานที่เรียกว่า REPL (Python prompt) ในกรณีของบอร์ด ESP8266 จะใช้พอร์ต UART0 ซึ่งตรงกับขา GPIO1=TX และ GPIO3=RX ตามลำดับ ผ่านไอซี USB-to-Serial (เช่น CH340) ไปยังคอมพิวเตอร์ มีการตั้งค่า Baudrate ไว้เท่ากับ 115200
ในการจัดเก็บไฟล์ภายใน MicroPython จะมีไฟล์ชื่อ boot.py
ซึ่งจะเป็นไฟล์ที่ถูกเรียกให้ทำงานโดยอัตโนมัติ เมื่อมีการรีเซตระบบ ถ้าเราสร้างไฟล์ main.py
และใส่เอาไว้ด้วยอีกหนึ่งไฟล์ ก็จะถูกเรียกให้ทำงานต่อจากไฟล์ boot.py
ได้โดยอัตโนมัติ
สำหรับ REPL ถ้าเราก็ปุ่ม Ctrl+D จะเป็นการทำ Soft Reset ให้ MicroPython เริ่มต้นใหม่ แต่ถ้ากด Ctrl+C เป็นการหยุดการทำงานของ Python Script ที่กำลังทำงานอยู่
ลองคำสั่งของ MicroPython เช่น เพื่อดูขนาดของ Flash Memory และขนาดของ RAM ที่ยังว่างอยู่ในขณะนั้น และความถี่ของ CPU
import esp
import gc
import machinegc.collect() # call MicroPython garbage collector
print( 'Flash: {} KB'.format(esp.flash_size()//1024) )
print( 'Free mem: {} Bytes'.format(esp.freemem()) )
print( 'CPU Freq: {} Hz'.format(machine.freq()) )
ตัวอย่างข้อความเอาต์พุตที่ได้ผ่านทาง REPL
Flash: 4096 KB
Free mem: 9152 Bytes
CPU Freq: 80000000 Hz
รายการคำสั่งหรือ API ของ MicroPython ESP8266 Port สามารถศึกษาได้จาก
LED Blinking
ตัวอย่างแรกเป็นการทำให้ LED ที่มีอยู่บนบอร์ดแล้ว (ต่อกับขา GPIO2
) กระพริบต่อเนื่อง โดยเว้นระยะเวลาในการสลับสถานะ เอาต์พุตของ LED ประมาณ 0.5 วินาที
คำสั่งจาก machine.Pin
จะใช้สำหรับ Digital I/O (GPIO) ของบอร์ด ESP8266 เช่น เลือกโหมดได้ว่าจะให้เป็นอินพุตหรือเอาต์พุต เปิดใช้งานวงจร Pull-up ภายในหรือไม่ เป็นต้น มีคำสั่งสำหรับอ่านค่าลอจิก หรือกำหนดค่าลอจิกได้สำหรับขา ที่ใช้เป็นเอาต์พุต
การหน่วงเวลาก่อนทำคำสั่งถัดไป ก็ใช้คำสั่งจาก utime
มีคำสั่ง เช่น
sleep()
สำหรับหน่วยเป็นวินาทีsleep_ms()
สำหรับหน่วยเป็นมิลลิวินาที และsleep_us()
สำหรับหน่วยเป็นไมโครวินาที เป็นต้น
# File: demo-1.py
import machine
import utime as timeLED_PIN = 2 # use GPIO-2/D4 pin (for onboard BLUE LED)def blink():
# create a Pin object for LED output
led = machine.Pin( LED_PIN, machine.Pin.OUT )
while True: # endless loop
led.on() # output logic 1
time.sleep(0.5)
led.off() # output logic 0
time.sleep(0.5)
try:
# press Ctrl+C to stop
blink()
except KeyboardInterrupt:
pass
finally:
print('Done')
LED Blinking with Exit Push Button
ตัวอย่างที่ 2 เป็นการทำให้ LED กระพริบต่อเนื่อง โดยเว้นระยะเวลาในการสลับสถานะ เอาต์พุตของ LED ประมาณ 0.5 วินาที และเพิ่มการตรวจสอบสถานะของปุ่มกดที่นำมาต่อเพิ่มสำหรับใช้เป็นอินพุต (ทำงานแบบ Active-Low) ต่อสัญญาณเข้าที่ขา GPIO14
เมื่อกดปุ่ม จะได้ค่าอินพุตเป็น 0 และจบการทำงานของโปรแกรม (ออกจาก while loop ตามเงื่อนไขที่กำหนดไว้)
# File: demo-2.py
from machine import Pin, ADC
import utime as time
import micropython# define constants
LED_PIN = const(2) # use GPIO2/D4 pin
BTN_PIN = const(14) # use GPIO14/D5 pindef toggle_led():
value = int( led.value() ) # get output state
print( 'LED: {}'.format(value) ) # print state
led.value( not value ) # toggle output statetry:
# press Ctrl+C to stop or press the button
# create Pin objects for LED and push button
led = Pin( LED_PIN, mode=Pin.OUT )
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP ) # read current system ticks in msec
t0 = time.ticks_ms() while btn.value(): # main loop
t = time.ticks_ms()
if time.ticks_diff( t, t0 ) >= 500:
t0 = t # save timestamp for LED toggle
toggle_led() # toggle the LED
except KeyboardInterrupt:
pass
finally:
print('Done')
LED / Push Button / DHT22
ตัวอย่างที่ 3 เป็นการเพิ่มฟังก์ชันการทำงานของ ESP8266 เหมือนตัวอย่างที่ 2 แต่ให้อ่านค่าจากโมดูลเซนเซอร์ DHT22 โดยใช้คำสั่งจาก dht
ของ MicroPython สำหรับอ่านอุณหภูมิหน่วยเป็นองศาเซลเซียส และความชื้นสัมพัทธ์ที่ได้ค่าเป็นเปอร์เซ็นต์
ในการต่องวงจรบนเบรดบอร์ด ได้เลือกใช้ขา GPIO4
เป็นขา I/O สำหรับ DATA pin ของโมดูลดังกล่าว (โมดูลนี้ใช้แรงดันไฟเลี้ยง +3.3V และ GND จากบอร์ด)
# File: demo-3.py
from machine import Pin, ADC
import utime as time
import micropython
import dhtLED_PIN = const(2) # use GPIO2/D4 pin
BTN_PIN = const(14) # use GPIO14/D5 pin
DHT_PIN = const(4) # use GPIO4/D2 pindef toggle_led():
value = int(led.value())
print('LED: {}'.format(value) )
led.value( not value )dht_state = 0def read_dht():
global dht_state
try:
if dht_state == 0:
dht22.measure()
else:
values = (dht22.temperature(), dht22.humidity())
print('T={} deg.C, H={}%'.format(*values))
except OSError:
print('DHT22 reading error')
finally:
dht_state = (dht_state+1) % 2try:
# press Ctrl+C to stop or press the button
led = Pin( LED_PIN, mode=Pin.OUT )
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP )
dht22 = dht.DHT22( Pin(DHT_PIN) )
# read current system time in msec
t1 = t0 = time.ticks_ms()
while btn.value():
t = time.ticks_ms()
if time.ticks_diff( t, t0 ) >= 500:
t0 = t # save timestamp for LED toggle
toggle_led()
if time.ticks_diff( t, t1 ) >= 1000:
t1 = t # save timestamp for DHT reading
read_dht()
except KeyboardInterrupt:
pass
finally:
print('Done')
LED / Push Button / DHT22 / ADC
ตัวอย่างที่ 4 เป็นการเพิ่มฟังก์ชันการทำงานให้ ESP8266 โดยให้อ่านค่าอินพุตแบบแอนะล็อกที่ขา A0
ของ ESP8266 โดยนำมาต่อกับโมดูลเซนเซอร์แสงแบบแอนะล็อก TEMT6000 (Ambient Light Sensor Module) ถ้ามีความเข้มแสงมากขึ้น ระดับแรงดันไฟฟ้าก็จะสูงขึ้น และอ่านค่าตัวเลขจำนวนเต็มที่มากขึ้น
ข้อสังเกต: ขาแอนะล็อกอินพุตของ ESP8266EX สามารถรับแรงดันไฟฟ้าในสูงสุดประมาณ 1.0V และแปลงเป็นข้อมูลขนาด 10 บิต (0..1023) โดยทั่วไป บนบอร์ด ESP8266 มักจะต่อวงจรแบ่งแรงดันเอาไว้แล้ว เช่น ใช้ตัวต้านทาน 220k กับ 100k มาต่ออนุกรมกัน ดังนั้นถ้าอินพุตเท่ากับ 3.3V จะได้แรงดันไฟฟ้าที่ขา A0
เท่ากับ 3.3V * 100/(100 + 220) = 1.03V และจะอ่านได้ค่าสูงสุด 1023
ข้อสังเกต: คำสั่ง read()
ของ machine.ADC
จะให้ค่าอยู่ในช่วง 0..1023 (10 บิต) แต่ถ้าใช้คำสั่ง read_u16()
จะให้ค่าอยู่ในช่วง 0..65535 (16 บิต)
ควรตรวจสอบจากผังวงจรของบอร์ดที่เลือกมาใช้งานก่อนต่อวงจร (ตัวอย่างผังวงจรของบอร์ด WeMos D1 Mini และบอร์ด NodeMCU DevKit 1.0)
# File: demo-4.py
from machine import Pin, ADC
import utime as time
import micropython
import dhtLED_PIN = const(2) # use GPIO2/D4 pin
BTN_PIN = const(14) # use GPIO14/D5 pin
DHT_PIN = const(4) # use GPIO4/D2 pindef toggle_led():
value = int(led.value())
print('LED: {}'.format(value) )
led.value( not value )dht_state = 0def read_dht():
global dht_state
try:
if dht_state == 0:
dht22.measure()
else:
values = (dht22.temperature(), dht22.humidity())
print('T={} deg.C, H={}%'.format(*values))
except OSError:
print('DHT22 reading error')
finally:
dht_state = (dht_state+1) % 2def read_analog_light_sensor():
value = adc.read() # 0..1023
print('Light level: {}'.format(value))try:
# press Ctrl+C to stop or press the button
led = Pin( LED_PIN, mode=Pin.OUT )
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP )
dht22 = dht.DHT22( Pin(DHT_PIN) )
adc = ADC(0) # open ADC channel 0 (A0 pin)
# read current system time in msec
t2 = t1 = t0 = time.ticks_ms()
while btn.value():
t = time.ticks_ms()
if time.ticks_diff( t, t0 ) >= 500:
t0 = t # save timestamp for LED toggle
toggle_led()
if time.ticks_diff( t, t1 ) >= 1000:
t1 = t # save timestamp for DHT reading
read_dht()
if time.ticks_diff( t, t2 ) >= 1000:
t2 = t # save timestamp for ADC reading
read_analog_light_sensor()
except KeyboardInterrupt:
pass
finally:
print('Done')
LED / Push Button / DHT22 / DS18B20 / ADC
ตัวอย่างที่ 5 เป็นการเพิ่มฟังก์ชันการทำงานให้ ESP8266 โดยให้อ่านค่าอุณหภูมิจาก DS18B20 (OneWire) สำหรับวัดอุณหภูมิ และใช้ขา GPIO5
สำหรับต่อกับขา DATA ของโมดูล DS18B20 (โมดูลนี้ใช้แรงดันไฟเลี้ยง +3.3V และ GND จากบอร์ด)
# File: demo-5.py
from machine import Pin, ADC
import utime as time
import micropython
import dht
from onewire import OneWire
from ds18x20 import DS18X20
import ubinascii as binasciiLED_PIN = const(2) # use GPIO2/D4 pin
BTN_PIN = const(14) # use GPIO14/D5 pin
DHT_PIN = const(4) # use GPIO4/D2 pin
DS_PIN = const(5) # use GPIO5/D1 pindef toggle_led():
value = int(led.value())
print('LED: {}'.format(value) )
led.value( not value )dht_state = 0def read_dht():
global dht_state
try:
if dht_state == 0:
dht22.measure()
else:
values = (dht22.temperature(), dht22.humidity())
print('T={} deg.C, H={}%'.format(*values))
except OSError:
print('DHT22 reading error')
finally:
dht_state = (dht_state+1) % 2def read_analog_light_sensor():
value = adc.read() # 0..1023
print('Light level: {}'.format(value))ds_state = 0def read_ds18b20():
global ds_state
value = None
if ds_state==0:
ds.convert_temp()
elif rom is not None:
value = ds.read_temp(rom)
print('DS18B20: {}'.format(value))
ds_state = (ds_state+1) % 2
return valuetry:
# press Ctrl+C to stop or press the button
led = Pin( LED_PIN, mode=Pin.OUT )
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP )
dht22 = dht.DHT22( Pin(DHT_PIN) )
adc = ADC(0) # open ADC channel 0 (A0 pin)
ds = DS18X20( OneWire( Pin(DS_PIN) ) )
roms = ds.scan() # scan DS18B20 devices
rom = roms[0] if len(roms)==1 else None
if rom:
hex_addr = binascii.hexlify(rom).decode()
print('DS18B20: {}'.format(hex_addr) )
else:
print('No DS18B20 found')
# read current system time in msec
t3 = t2 = t1 = t0 = time.ticks_ms()
while btn.value():
t = time.ticks_ms()
if time.ticks_diff( t, t0 ) >= 500:
t0 = t # save timestamp for LED toggle
toggle_led()
if time.ticks_diff( t, t1 ) >= 1000:
t1 = t # save timestamp for DHT reading
read_dht()
if time.ticks_diff( t, t2 ) >= 1000:
t2 = t # save timestamp for ADC reading
read_analog_light_sensor()
if time.ticks_diff( t, t3 ) >= 1000:
t3 = t # save timestamp for DS18B20 reading
read_ds18b20()
except KeyboardInterrupt:
pass
finally:
print('Done')
Timer-based Periodic Task Execution
ตัวอย่างที่ 6 สาธิตการใช้ Software Timer เพื่อการเรียกฟังก์ชันสำหรับแต่ละงานย่อย โดยให้ทำงานตามระยะเวลาที่กำหนด (หน่วยเป็นมิลลิวินาที)
# File: demo-6.py
from machine import Pin, ADC
import utime as time
import micropython
import dht
from onewire import OneWire
from ds18x20 import DS18X20
import ubinascii as binascii
from machine import TimerLED_PIN = const(2) # use GPIO2/D4 pin
BTN_PIN = const(14) # use GPIO14/D5 pin
DHT_PIN = const(4) # use GPIO4/D2 pin
DS_PIN = const(5) # use GPIO5/D1 pindef toggle_led(timer): # timer (-1) callback
value = int(led.value())
print('LED: {}'.format(value) )
led.value( not value )
dht_state = 0def read_dht(timer): # timer (-2) callback
global dht_state
try:
if dht_state == 0:
dht22.measure()
else:
values = (dht22.temperature(), dht22.humidity())
print('DHT22: T={} deg.C, H={}%'.format(*values))
except OSError:
print('DHT22 reading error')
finally:
dht_state = (dht_state+1) % 2def read_analog_light_sensor(timer): # timer (-3) callback
value = adc.read() # 0..1023
print('Light level: {}'.format(value))
ds_state = 0def read_ds18b20(timer): # timer (-4) callback
global ds_state
value = None
if ds_state==0:
ds.convert_temp()
elif rom is not None:
value = ds.read_temp(rom)
print('DS18B20: {:.2f}'.format(value))
ds_state = (ds_state+1) % 2
return valuetry:
# press Ctrl+C to stop or press the button led = Pin( LED_PIN, mode=Pin.OUT )
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP )
dht22 = dht.DHT22( Pin(DHT_PIN) )
adc = ADC(0) # open ADC channel 0 (A0 pin)
ds = DS18X20( OneWire( Pin(DS_PIN) ) )
roms = ds.scan() # scan DS18B20 devices
rom = roms[0] if len(roms)==1 else None
if rom:
hex_addr = binascii.hexlify(rom).decode()
print('DS18B20: {}'.format(hex_addr) )
else:
print('No DS18B20 found')
# create software timers for periodic tasks
t = []
t.append( Timer(-1) )
t[0].init( period=500,
mode=Timer.PERIODIC,
callback=toggle_led )
t.append( Timer(-2) )
t[1].init( period=1000,
mode=Timer.PERIODIC,
callback=read_dht )
t.append( Timer(-3) )
t[2].init( period=1000,
mode=Timer.PERIODIC,
callback=read_analog_light_sensor )
t.append( Timer(-4) )
t[3].init( period=1000,
mode=Timer.PERIODIC,
callback=read_ds18b20 )
while btn.value(): # main loop
pass # do nothing
except KeyboardInterrupt:
pass
finally:
for _t in t:
_t.deinit() # turn off all timers
print('Done')
Getting JSON Data Using HTTP and RESTful API
ตัวอย่างถัดไปเป็นการสาธิตวิธีการเชื่อมต่อด้วยโพรโตคอล HTTP และวิธี GET ผ่าน WiFi ไปยังอินเทอร์เน็ต เพื่อดึงข้อมูล JSON จากเว็บ AirVisual ซึ่งเป็นผู้ให้บริการข้อมูลเกี่ยวกับสภาพอากาศ
ผู้ใช้จะต้องสมัครขอมีบัญชีผู้ใช้ (Account) สามารถใช้บริการได้ฟรี จากนั้นจะต้องสร้าง API Key และเลือกรูปแบบเป็น Community Plan (ฟรี) เพื่อใช้ในการเชื่อมต่อและดึงข้อมูลแต่ละครั้ง ระบุ API Key ใน URL พร้อมค่าอื่น ๆ เช่น ชื่อเมือง จังหวัดและประเทศ เป็นต้น
การเชื่อมต่อด้วย HTTP / GET เราจะใช้ไลบรารีของ MicroPython ที่มีชื่อว่า urequests
และจะต้องดาวน์โหลดไฟล์จาก Github มาที่เครื่องคอมพิวเตอร์แล้วนำไปใส่ลงในระบบไฟล์ของ MicroPython สามารถทำได้ง่ายโดยใช้ Thonny IDE
ลองมาดูตัวอย่างการเขียนโค้ด แบ่งการทำงานออกเป็น 4 ขั้นตอนคือ
- อ่านไฟล์ config.json ซึ่งทำหน้าที่เก็บข้อความที่ระบุชื่อ SSID และรหัสผ่านสำหรับการเชื่อมต่อ WiFi และยังเก็บข้อความที่ระบุ API Key สำหรับ AirVisual API
- เปิดใช้งาน WiFi ในโหมด STA (station)
- เชื่อมต่อกับ WiFi AP (Access Point) โดยใช้ค่า ssid และ password จากไฟล์ config.json
- ใช้คำสั่งของ
urequests
ดึงข้อมูลตาม URL สำหรับ AirVisual API และอ่านค่าที่สนใจจากโครงสร้างข้อมูล JSON เพื่อนำมาแสดงข้อความเป็นเอาต์พุต เช่น ระบุค่า US AQI (Air Quality Index) ค่าอุณหภูมิ ความชื้นสัมพัทธ์ และความดันบรรยากาศ แต่ค่า PM2.5 (ug/m^3) จะไม่มีมาให้ เนื่องจากเราใช้ Community Plan (ฟรี)
# File: demo-7.py
import esp
import sys
import json
import network
import utime as time
import urequests as requestsesp.osdebug(None) # turn off OS debug outputJSON_CONFIG_FILE = 'config.json'
AIRVISUAL_URL = 'https://api.airvisual.com/v2/city'# 1) Read configuration file
config = {}
try:
with open( JSON_CONFIG_FILE ) as json_file:
config = json.load(json_file)
except OSError as ex:
print('Cannot open JSON file')
sys.exit(-1)# 2) Start WiFi in STA mode
sta_if = network.WLAN( network.STA_IF )
sta_if.active( True )# 3) Connect to WiFi AP
sta_if.connect(config['ssid'], config['password'] )
while not sta_if.isconnected():
time.sleep(1.0)
# show IP address assigned by DHCP server
print( 'Connected:', sta_if.ifconfig()[0] )# 4) Fetch JSON data by using the AirVisual API
url_template = AIRVISUAL_URL + '?city={}&state={}&country={}&key={}'
url = url_template.format(
'Mueang%20Nonthaburi',
'Nonthaburi',
'Thailand',
config['airvisual_api_key'] )resp = None
try:
resp = requests.get( url )
except OSError:
print('Cannot fetch data')if resp and resp.status_code == 200: # OK
json_data = resp.json()
if json_data['status'] == 'success':
data = json_data['data']
location = (data['state'],data['city'],data['country'])
polution = data['current']['pollution']
us_aqi = polution['aqius']
ts = polution['ts'] # '2020-05-19T02:00:00.000Z'
weather = data['current']['weather']
tp = weather['tp'] # temperature (deg.C)
hu = weather['hu'] # humidity (%)
pr = weather['pr'] # pressure (mBar/hPa)
print('Location: {}, {}, {}'.format(*location) )
print('US AQI: {}'.format(us_aqi) )
print('T={} deg.C, H={} %, P={} mBar'.format(tp,hu,pr))
else:
print (resp.reason)
ตัวอย่างรูปแบบการเก็บข้อมูล JSON ในไฟล์ config.json
{
"ssid": "MyAP",
"password": "MyPassword",
"airvisual_api_key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
ตัวอย่างเอาต์พุตที่ได้
Connected: 192.168.43.166
Location: Nonthaburi, Mueang Nonthaburi, Thailand
US AQI: 43
T=32 deg.C, H=70 %, P=1008 mBar
ถ้าค่า US AQI น้อยกว่า 50 ก็แสดงว่า คุณภาพของอากาศอยู่ในเกณฑ์ ‘ดี’ (Good) แต่ถ้ามากกว่านั้น ก็มีการแบ่งระดับดังนี้
- Good: <50
- Moderate: 50 .. <100
- Unhealthy for Sensitive Groups: 100 .. <150
- Unhealthy: 150 .. <200
- Very Unhealthy: 200 .. <300
- Hazardous: 300+
I2C Interfacing: I2C 16x2LCD and SHT21
ในตัวอย่างนี้เราจะมาลองใช้งานโมดูลที่ใช้วิธีเชื่อมต่อด้วยบัส I2C โดยใช้สัญญาณเพียงสองเส้นคือ SCL (Clock) และ SDA (Data) เช่น โมดูลแสดงผล LCD 16x2 ที่ใช้งานร่วมกับ PCF8574 Adapter และอีกโมดูลหนึ่งคือ โมดูล GY-21 SHT21 สำหรับวัดค่าอุณหภูมิและความชื้นสัมพัทธ์
การใช้งานอุปกรณ์ที่กล่าวไป และเพื่อให้เกิดควาสะดวกในการสาธิต เราจะใช้ไลบรารีที่นักพัฒนาได้จัดทำและเผยแพร่ไว้แล้วใน Github ดังนั้นให้ดาวน์โหลดไฟล์ต่อไปนี้ แล้วนำไปใส่ลงใน MicroPython Device
lcd_api.py
และesp8266_i2c_lcd.py
สำหรับโมดูล LCD16x2 I2Csht21.py
สำหรับโมดูล GY-21 SHT21
ลองมาดูโค้ดตัวอย่างสาธิตการใช้งานดังนี้ โดยกำหนดให้แอดเดรสของอุปกรณ์ตรงกับหมายเลข 0x3f
สำหรับ LCD I2C และ 0x40
สำหรับ SHT21
import sys
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
import sht21
import utime as time
import micropythonLCD_I2C_ADDR = const(0x3f)
SHT21_I2C_ADDR = const(0x40)
BTN_PIN = const(14) # use GPIO14/D5 pini2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
# scan I2C devices
print( ['0x{:02X}'.format(addr) for addr in i2c.scan()] )if sht21.SHT21_DETECT(i2c): # detect SHT21 device
sht21.SHT21_RESET(i2c)
time.sleep_ms(100)try:
# create LCD object for 16x2 LCD display
lcd = I2cLcd(i2c, LCD_I2C_ADDR, 2, 16)
except OSError:
print('Cannot conntect to I2C device...')
sys.exit(-1)try:
btn = Pin( BTN_PIN, mode=Pin.IN, pull=Pin.PULL_UP )
t0 = time.ticks_ms()
while btn.value():
ts = time.ticks_ms()
if time.ticks_diff( ts, t0 ) >= 2000:
t0 = ts
t = sht21.SHT21_TEMPERATURE(i2c)
h = sht21.SHT21_HUMIDITE(i2c)
lcd.clear()
lcd.move_to(0, 0)
lcd.putstr(" Temp.: %3.1f C" % t )
lcd.move_to(0, 1)
lcd.putstr("Humid.: %3.1f %%" % h )
except KeyboardInterrupt:
pass
finally:
print('Done')
lcd.backlight_off()
lcd.display_off()
del lcd
del sht21
I2C Interfacing: SSD1306 OLED Display
อีกตัวอย่างหนึ่งสาธิตการแสดงผลโดยใช้โมดูล OLED ที่ใช้ไอซี SSD1306 เป็นตัวควบคุม และเชื่อมต่อด้วยบัส I2C และเพื่อความสะดวกในการใช้งาน ก็มีไลบรารีสำหรับ MicroPython ไว้สำหรับใช้งาน
ให้ดาวน์โหลดไฟล์ ssd1306.py
มายังคอมพิวเตอร์ แล้วใช้โปรแกรม Thonny IDE เชื่อมต่อกับบอร์ด ESP8266 และอัปโหลดไฟล์ดังกล่าวไปใส่ไว้ใน Flash File Storage ของบอร์ด
โมดูล OLED ที่ได้เลือกใช้มีขนาด 128 x 32 พิกเซล มีแอดเดรสเท่ากับ 0x3C และได้เลือกใช้ขา GPIO4/D2 สำหรับ SDA และ GPIO5/D1 สำหรับ SCL ตามลำดับ
import machine
import ssd1306
import utime as timescl_pin = machine.Pin(5) # GPIO5/D1 pin
sda_pin = machine.Pin(4) # GPIO4/D2 pin
i2c = machine.I2C(scl=scl_pin, sda=sda_pin)
print(’Scan i2c bus...’)devices = i2c.scan() # scan I2C design
for d in devices:
print( 'Found address: 0x{:02X}'.format(d) )OLED_ADDR = 0x3C
OLED_W = 128
OLED_H = 32oled = ssd1306.SSD1306_I2C(OLED_W, OLED_H, i2c, OLED_ADDR)
oled.fill(0) # clear screen
oled.show() # update screen# show a text line with the default font
oled.text('Hello, World !', 5, 12)
oled.show()
time.sleep(2.0)def draw_lines():
# draw a rect
for i in range(OLED_W):
oled.pixel(i, 0, 1)
oled.pixel(i, OLED_H-1, 1)
oled.show()try:
for i in range(10000):
oled.fill(0)
oled.text(' Count {:04d}'.format(i), 5, 12)
draw_lines()
oled.show()
time.sleep_ms(1000)
except KeyboardInterrupt:
pass
finally:
oled.fill(0)
SPI Interfacing: SSD1306 OLED Display
ในกรณีที่ใช้โมดูล OLED SSD1306 ที่มี 7 ขา ได้แก่ GND, VCC, D0 (SCK/SCL), D1 (MOSI/SDA), RST, DC(A0), CS ตามลำดับ เราจะใช้รูปแบบการสื่อสารข้อมูลด้วยบัส SPI และเชื่อมต่อกับ ESP8266 โดยใช้ขา GPIO ตามลำดับต่อไปนี้
SCK = GPIO14 / D5
MISO = GPIO12 / D6
MOSI = GPIO13 / D7
CS = GPIO15 / D8
DC = GPIO4 / D2
RST = GPIO2 / D4
ตัวอย่างโค้ดสาธิตมีดังนี้
from ssd1306 import SSD1306_SPI
from machine import Pin, I2C, SPIw = 128
h = 64
# OLED-SPI with 6 pins (GND, VCC/3.3V, D0, D1, RST, DC)
spi = SPI(1, baudrate=8000000, polarity=0, phase=0)
dc = Pin(4, Pin.OUT)
rst = Pin(2, Pin.OUT)
cs = Pin(15, Pin.OUT)
oled = SSD1306_SPI(w, h, spi, dc, rst, cs,external_vcc=False)oled.fill(0)
oled.text('Hello, World!', 12, 0)
oled.show()
[+] การใช้งานบอร์ด ESP8266 ที่มี Flash 16MB
ในกรณีที่เรามีบอร์ด WeMos D1 Mini Pro ที่มีหน่วยความจำ Flash ขนาด 16MB และต้องการใช้งาน MicroPython ก็มีขั้นตอนดังนี้
อ้างอิงจาก:
https://github.com/micropython/micropython/issues/2335
ขั้นตอนการดำเนินการ
- ดาวน์โหลดไฟล์เฟิร์มแวร์สำหรับ Micropython-ESP8266 (“Stable firmware, 1M or more of flash”) เช่น ไฟล์ esp8266–20191220-v1.12.bin
$ wget -c http://micropython.org/resources/firmware/esp8266-20191220-v1.12.bin
2. ดาวน์โหลดไฟล์ esp_init._data_default.bin
$ wget -c "https://github.com/espressif/ESP8266_AT/raw/master/bin/esp_init_data_default.bin"
3. ทำคำสั่งลบข้อมูลหรือเคลียร์หน่วยความจำของแฟลซทั้งหมด
$ python -m esptool --baud 460800 erase_flash
4. ทำคำสั่งเขียนไฟล์ไปยังบอร์ด
$ python -m esptool --baud 460800 write_flash -fm dio -fs 16MB 0x0 esp8266-20191220-v1.12.bin 0xffc000 esp_init_data_default.bin
5. เปิดโปรแกรม Thonny IDE แล้วลองคำสั่งต่อไปนี้ เพื่อตรวจสอบขนาดของ Flash memory และจะได้ 16MB
import esp# read / show Flash size in MB
fs = esp.flash_size()//(1024*1024)
print( 'Flash Size: {} MB'.format(fs) )
## Flash Size: 16 MB
โดยสรุป เราได้เรียนรู้ขั้นตอนการติดตั้งเฟิร์มแวร์ของ MicroPython สำหรับบอร์ด ESP8266 เขียนโค้ดตามตัวอย่าง บันทึกไฟล์ยังระบบไฟล์ของอุปกรณ์และรันโค้ดตัวอย่าง โดยใช้ Thonny IDE (สำหรับ Windows) และได้เห็นตัวอย่างการเพิ่มไฟล์สำหรับไลบรารีเพื่อนำมาใช้สำหรับ MicroPython