วิธีการ Flash Firmware ภาษา MicroPython ลงใน ESP32 เพื่อใช้ LoRaWAN®

Somsak Lima
13 min readFeb 1, 2020

--

ต้องใส่ MicroPython ลงใน ESP32 Develop Board ก่อน ซึ่งอาจจะเป็น ProtoType ที่ทำขึ้นเองตามรูป หรือใช้ของสำเร็จรูปเช่น Heltec หรือ TTGO

สามารถสั่งซื้ออุปกรณ์สำหรับประกอบได้ โดยดูได้จาก บทความ

ลงESP32 Firmware

การ Flash ด้วยโปรแกรม Thonny

ให้ Download และติดตั้งโปรแกรม Thonny ก่อน โดยดาวน์โหลดได้จากเวป https://thonny.org/

เปิดโปรแกรม Thonny

ไปที่เมนู Tool/Option

คลิก Tab Interpreter

เลือก MicroPython(ESP32) และ Port ที่ ESP32 เชื่อมสายอยู่

แล้วคลิก install or update MicroPython(esptool)

เลือก Option ตามภาพด้านล่าง

แล้วคลิก Install โปรแกรมจะเริ่ม Download Firmware อัตโนมัติ และเริ่มเขียน

หากเสร็จเรียบร้อยจะเห็นข้อความ Done! ให้คลิก Close แล้วคลิก OK เพื่อปิดหน้าต่าง

(อาจจะต้องกดให้เข้าโหมด Flash Bootloader โดยกด ปุ่ม IO0 ค้างไว้แล้วกดปุ่มEN 1ครั้งแล้วจึงปล่อย IO0)

***หลังจากเรียกโปแกรม Thonny แล้วควรจะเปิด View/File เพื่อให้มีหน้าต่างจัดการกับไฟล์โปรแกรมใน ESP32 หรือในคอมพิวเตอร์ของเรา ช่องหน้าต่างนี้จะช่วยให้เรา Upload หรือ Download ไฟล์เข้าออกจาก ESP32 ได้ง่ายขึ้น

จอหน้าต่างซ้ายล่างจะเป็นพื้นที่ทำงานบน PC หากมีไฟล์อยู่จะเห็นชื่อไฟล์ เราสามารถเลือกไฟล์แล้วคลิกที่ปุ่มขวาเพื่อ Upload to / ไปยัง ESP32

เปิดดู Storage Space จะเห็นว่ามีเนื้อที่ใช้งานอยู่ 2 M

วิธีใช้งาน MicroPython

ดับเบิ้ลคลิ๊กที่ชื่อไฟล์บน MicroPython device แล้วคลิก Run Current Script (F5)

เมื่อต้องการจะหยุด Run โปรแกรม ให้กดเครื่องหมาย Stop สีแดง

หากต้องการให้โปรแกรม Python ตัวไหนทำงานเมื่อเริ่มป้อนไฟเปิด ESP32 ให้ Save ชื่อโปรแกรมเป็น main.py

กรณีใช้ Board ESP32 LoRa ของ heltec ให้แก้บรรทัด เลข SCL เป็น 15 และ SDA เป็น 4 โดยโปรแกรมจะทำงานเมื่อต่อ Sensor ครบและถูกต้อง

i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4)) #heltec 5/4

เอกสารคู่มือ MicroPython เปิดดูได้ที่เวป http://docs.micropython.org/ หากเป็นคำสั่ง Firmware quick reference เฉพาะ ESP32 ให้ดู http://docs.micropython.org/en/latest/esp32/quickref.html

ตัวอย่าง

แสดง OS ที่ติดตั้งอยู่

ตัวอย่าง เรียกดูบน ESP32

>>> import os; print (os.uname())
(sysname=’esp32', nodename=’esp32', release=’1.12.0', version=’v1.12–86-gcb4472df4 on 2020–01–24', machine=’ESP32 module with ESP32')

ตัวอย่าง เรียกดูบน Pyboard

>>> import os; print (os.uname())
(sysname=’pyboard’, nodename=’pyboard’, release=’1.11.0', version=’v1.11–555-g80df377e9 on 2019–11–05', machine=’PYBv1.1 with STM32F405RG’)

แสดงความถี่ MCU ที่ทำงานอยู่ในปัจจุบัน

>>> import machine
>>> machine.freq()
240000000

แสดงขนาดของ Flash Memory

>>> import esp>>> esp.flash_size()4194304

แสดงค่า hall effect ใน MCU

>>> import esp32>>> esp32.hall_sensor()34

แสดงอุณหภูมิเป็นองศา F

>>> esp32.raw_temperature()124

คีย์ที่ใช้บ่อย

CTRL-C — หยุดโปแกรมที่ run อยู่CTRL-D — soft reset

เข้าโหมด Deep Sleep 10 วินาที

import machine

# put the device to sleep for 10 seconds
machine.deepsleep(10000)

ใช้ RTC

from machine import RTC

rtc = RTC()
rtc.datetime((2020, 1, 25, 1, 12, 48, 0, 0)) # set date & time
rtc.datetime() # get date and time

ควบคุม Digital Output

import time
import machine
redled = machine.Pin(4, machine.Pin.OUT)
# Blink 12 times
for i in range(1, 13):
redled.value(0)
time.sleep(0.7)
redled.value(1)
time.sleep(0.7)
print("OK!")

เช็คว่ามี Module อะไรให้ใช้บ้าง

>>>help (‘modules’)
__main__ gc uctypes urequests
_boot inisetup uerrno uselect
_onewire machine uhashlib usocket
_thread math uhashlib ussl
_webrepl micropython uheapq ustruct
apa106 neopixel uio utime
btree network ujson utimeq
builtins ntptime umqtt/robust uwebsocket
cmath onewire umqtt/simple uzlib
dht sys uos webrepl
ds18x20 uarray upip webrepl_setup
esp ubinascii upip_utarfile websocket_helper
esp32 ubluetooth upysh
flashbdev ucollections urandom
framebuf ucryptolib ure
Plus any modules on the filesystem

ลอง Help มอดูล esp32

>>> help (“esp32”)
object esp32 is of type str
encode — <function>
find — <function>
rfind — <function>
index — <function>
rindex — <function>
join — <function>
split — <function>
splitlines — <function>
rsplit — <function>
startswith — <function>
endswith — <function>
strip — <function>
lstrip — <function>
rstrip — <function>
format — <function>
replace — <function>
count — <function>
partition — <function>
rpartition — <function>
center — <function>
lower — <function>
upper — <function>
isspace — <function>
isalpha — <function>
isdigit — <function>
isupper — <function>
islower — <function>

ลองคำสั่ง help(Pin)

>>> help(Pin)
object <class ‘Pin’> is of type type
init — <function>
value — <function>
off — <function>
on — <function>
irq — <function>
IN — 1
OUT — 3
OPEN_DRAIN — 7
PULL_UP — 2
PULL_DOWN — 1
PULL_HOLD — 4
IRQ_RISING — 1
IRQ_FALLING — 2
WAKE_LOW — 4
WAKE_HIGH — 5

ดูข้อมูลการใช้ Memory

>>> import micropython
>>> micropython.mem_info()
stack: 556 out of 15360
GC: total: 102720, used: 57312, free: 45408
No. of 1-blocks: 695, 2-blocks: 100, max blk sz: 98, max free sz: 840

01_SCANI2C.py

โปรแกรม SCAN เพื่อดู I2C Device Address แสดงผลออกที่หน้าจอ

# Scanner i2c en MicroPython | MicroPython i2c scanner
# Renvoi l'adresse en decimal et hexa de chaque device connecte sur le bus i2c
# Return decimal and hexa adress of each i2c device
# https://projetsdiy.fr - https://diyprojects.io (dec. 2017)
import machine
# init ic2 object
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(18))
# i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) #ESP8266 5/4
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21)) #ESP32 Dev Board /M2M
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4)) # heltec 5/4
print('Scan i2c bus…')
devices = i2c.scan()
if len(devices) == 0:
print("No i2c device !")
else:
print('i2c devices found:', len(devices))
for device in devices:
print("Decimal address: ", device, " | Hexa address: ", hex(device))

MicroPython เวอร์ชั่นหลังๆ จะใช้ SoftI2C โปรแกรมจะเปลี่ยนไปดังนี้

from machine import Pin, SoftI2C
i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=10000)
print(‘Scan i2c bus…’)
devices = i2c.scan()
if len(devices) == 0:
print(“No i2c device !”)
else:
print(‘i2c devices found:’,len(devices))
for device in devices:
print(“Decimal address: “,device,” | Hexa address: “,hex(device))

2_BME280_OLED.py

โปรแกรมแสดงค่าจาก Sensor BME280 บนหน้าจอ OLED SSD1306 มีค่า อุณหภูมิ ความชื้นและความดันบรรยากาศ

# Affichage OLED SSD1306 en MicroPython | MicroPython SSD1306 OLED display
# affiche les mesures d'un capteur BME280 i2c sur un 閼煎崒ran OLED
# Display values from an i2c BME280 sensor on OLED screen
# https://projetsdiy.fr - https://diyprojects.io (dec. 2017)
import machine, time, ssd1306, bme280
pinScl = 22 # ESP8266 GPIO5 (D1)
pinSda = 18 # ESP8266 GPIO4 (D2)
addrOled = 60 # 0x3c
addrBME280 = 118 # 0x76
hSize = 64 # Hauteur ecran en pixels | display heigh in pixels
wSize = 128 # Largeur ecran en pixels | display width in pixels
oledIsConnected = False
bmeIsConnected = False
temp = 0
pa = 0
hum = 0
# init ic2 object
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(18))
# i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) #ESP8266 5/4
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21)) #ESP32 Dev Board /myown
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4)) # heltec 5/4
# Scan le bus i2c et verifie si le BME280 et l'ecran OLED sont connectes
# Scan i2c bus and check if BME2 and OLDE display are connected
print('Scan i2c bus…')
devices = i2c.scan()
if len(devices) == 0:
print("No i2c device !")
else:
print('i2c devices found:', len(devices))
for device in devices:
if device == addrOled:
oledIsConnected = True
if device == addrBME280:
bmeIsConnected = True
print(device)
while True:
if bmeIsConnected:
bme = bme280.BME280(i2c=i2c, address=addrBME280)
print("BME280 values:")
print(bme.values)
temp, pa, hum = bme.values
print(temp)
print(pa)
print(hum)

if oledIsConnected:
oled = ssd1306.SSD1306_I2C(wSize, hSize, i2c, addrOled)
oled.fill(0)
if bmeIsConnected:

oled.text("Temp. "+temp, 0, 0)
oled.text("PA "+pa, 0, 10)
oled.text("Hum. "+hum, 0, 20)
oled.show()
else:
oled.text("BME KO", 0, 0)
oled.show()
else:
print('! No i2c display')
time.sleep_ms(5000)

03_SCAN_I2C_OLED.py

โปรแกรม SCAN I2C device และแสดงผลออกหน้าจอ OLED SSD1306

# Scanner i2c en MicroPython | MicroPython i2c scanner
# Renvoi l'adresse en decimal et hexa de chaque device connecte sur le bus i2c
# Return decimal and hexa adress of each i2c device
# https://projetsdiy.fr - https://diyprojects.io (dec. 2017)
import machine, ssd1306, time
# init ic2 object
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(18))
# i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4)) # ESP8266 5/4
# i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21)) # ESP32 Dev Board /myown
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4)) # heltec 5/4
pin16 = machine.Pin(16, machine.Pin.OUT)
pin16.value(1)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# oled.fill(0)
# oled.text('MicroPthonForum:', 0, 0)
# oled.show()
print('Scan i2c bus…')
devices = i2c.scan()
if len(devices) == 0:
print("No i2c device !")
else:
print('i2c devices found:', len(devices))
count = 1
for device in devices:
print("Decimal address: ", device, " | Hexa address: ", hex(device))
oled.fill(0)
oled.text("No of Device:"+str(len(devices)), 0, 10)
oled.text("Device No.:"+str(count), 0, 25)
oled.text("Dec add:"+str(device), 0, 35)
oled.text("Hex add:"+str(hex(device)), 0, 45)
oled.show()
time.sleep(7)
count = count+1

MicroPython กับ LoRa®, LoRaWAN® ใช้กับ RFM95 หรือ SX1276

File Lib ต่างๆ โหลดได้จาก คลิก

04_LORA_TEST.py

ทดสอบ LoRaWAN ส่ง Test String

import time
from ulora import TTN, uLoRa
# Refer to device pinout / schematics diagrams for pin details
# heltec
LORA_CS = const(18)
LORA_SCK = const(5)
LORA_MOSI = const(27)
LORA_MISO = const(19)
LORA_IRQ = const(26)
LORA_RST = const(14)
LORA_DATARATE = "SF7BW125" # Choose from several available
# From TTN console for device
DEVADDR = bytearray([0xFF, 0xFF, 0xFF, 0xFF])
NWKEY = bytearray([0xA6, 0xC3, 0x0F, 0xB2, 0x91, 0xDB, 0x55, 0xC5,
0x31, 0x82, 0x53, 0xD4, 0x08, 0x08, 0xFF, 0xFF])
APP = bytearray([0x54, 0xBE, 0x2D, 0xE6, 0xB6, 0xB3, 0xF7, 0xC2,
0xD0, 0x33, 0x72, 0xB5, 0x27, 0x20, 0xFF, 0xFF])
TTN_CONFIG = TTN(DEVADDR, NWKEY, APP, country="AS")
FPORT = 1
lora = uLoRa(
cs=LORA_CS,
sck=LORA_SCK,
mosi=LORA_MOSI,
miso=LORA_MISO,
irq=LORA_IRQ,
rst=LORA_RST,
ttn_config=TTN_CONFIG,
datarate=LORA_DATARATE,
fport=FPORT
)
# ...Then send data as bytearray
data = bytearray([0x01, 0x02, 0x03, 0x04])
counter = 0
while True:
lora.frame_counter = counter
lora.send_data(data, len(data), lora.frame_counter)
time.sleep(5)
counter += 1

05_LoRa_ABP_BME280.py

ส่งค่า T/H จาก Sensor BME280 ด้วยรูปแบบ Cayenne เข้า TheThingsNetwork หรือ Lora Network Server

from cayennelpp import CayenneLPP
import utime, time
import machine
import bme280
from ulora import TTN, uLoRa
# Refer to device pinout / schematics diagrams for pin details
# heltec
LORA_CS = const(18)
LORA_SCK = const(5)
LORA_MOSI = const(27)
LORA_MISO = const(19)
LORA_IRQ = const(26)
LORA_RST = const(14)
LORA_DATARATE = "SF7BW125" # Choose from several available
# From TTN console for device
DEVADDR = bytearray([0xFF, 0xFF, 0xFF, 0xFF])
NWKEY = bytearray([0xA6, 0xC3, 0x0F, 0xB2, 0x91, 0xDB, 0x55, 0xC5,
0x31, 0x82, 0x53, 0xD4, 0x08, 0x08, 0xFF, 0xFF])
APP = bytearray([0x54, 0xBE, 0x2D, 0xE6, 0xB6, 0xB3, 0xF7, 0xC2,
0xD0, 0x33, 0x72, 0xB5, 0x27, 0x20, 0xFF, 0xFF])
TTN_CONFIG = TTN(DEVADDR, NWKEY, APP, country="AS")
FPORT = 1
lora = uLoRa(
cs=LORA_CS,
sck=LORA_SCK,
mosi=LORA_MOSI,
miso=LORA_MISO,
irq=LORA_IRQ,
rst=LORA_RST,
ttn_config=TTN_CONFIG,
datarate=LORA_DATARATE,
fport=FPORT
)
# SENSOR
temp = 0
pa = 0
hum = 0
# init ic2 object
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4)) # heltec 5/4
counter = 0
while True:
bme = bme280.BME280(i2c=i2c)
temp, pa, hum = bme.values
print(temp)
print(pa)
print(hum)
c = CayenneLPP()
c.addTemperature(1, float(temp)) # Add temperature read to channel 1
c.addRelativeHumidity(2, float(hum)) # Add relative humidity read to channel 2
c.addBarometricPressure(3, float(pa)) # Add another temperature read to channel 3
data = c.getBuffer() # Get bytes
lora.frame_counter = counter
lora.send_data(data, len(data), lora.frame_counter)
time.sleep(5)
counter += 1

ไฟล์ความถี่ AS ที่ใช้เป็นความถี่ AS1 ด้านหลังเลขความถี่ตรง Comment ยังไม่ได้แก้ให้ถูกต้อง ในวงเล็บเป็นค่าที่ถูกต้อง

# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`ttn_as.py`
======================================================
AS920 The Things Network Frequency Plans
* Author(s): Brent Rubell
"""
TTN_FREQS = {0: (0xe6, 0xCC, 0xF4), # 868.1 MHz (923.2 Mhz)
1: (0xe6, 0xD9, 0xC0), # 868.3 MHz (923.4 Mhz)
2: (0xe6, 0x8C, 0xF3), # 863.5 MHz (922.2 Mhz)
3: (0xe6, 0x99, 0xC0), # 867.1 MHz (922.4 Mhz)
4: (0xe6, 0xA6, 0x8D), # 867.3 MHz (922.6 Mhz)
5: (0xe6, 0xB3, 0x5A), # 867.5 MHz (922.8 Mhz)
6: (0xe6, 0xC0, 0x27), # 867.7 MHz (923.0 Mhz)
7: (0xe6, 0x80, 0x27)} # 867.9 MHz (922.0 Mhz)

สำหรับ AS2

TTN_FREQS = {0: (0xe6, 0xCC, 0xF4), # 923.2 Mhz
1: (0xe6, 0xD9, 0xC0), # 923.4 Mhz
2: (0xe6, 0xe6, 0x66), # 923.6 Mhz
3: (0xe6, 0xf3, 0x33), # 923.8 Mhz
4: (0xe7, 0x00, 0x00), # 924.0 Mhz
5: (0xe7, 0x0C, 0xCC), # 924.2 Mhz
6: (0xe7, 0x19, 0x99), # 924.4 Mhz
7: (0xe7, 0x26, 0x66)} # 924.6 Mhz

ตัวอย่างข้อมูลจาก Sensor Co2 และ ฺBME280

แสดงบน Dashboard ชื่อ Cayenne

06. ESP32 ใช้กับ Module Maxiiot DL7612-AS923-TH

มอดูล Maxiiot DL7612-AS923-TH เป็นมอดูลที่ใช้ AT Command. การใช้งานตามตัวอย่างนี้ ค่าที่ Save ไว้ในมอดูลเป็นดังนี้ เช่น Class C, Activate =1 และ NNMI=0

เมื่อใช้กับ Multi Channel Gateway สามารถทำงานได้ดีทั้งแบบ ABP หรือ OTAA และใช้ได้ทั้ง Class A หรือ Class C การ Downlink คำสั่งกลับมาควบคุม IO ก็ทำได้ไม่ยาก ตัวอย่างดังต่อไปนี้เป็น Node Class C และ ใช้ Thread ในการรอรับคำสั่ง Downlink เพื่อปิดเปิด LED หากมี LED ต่ออยู่ที่ขา 12

ส่ง “BABk/w==” แบบ BASE64 เพื่อเปิด LED
ส่ง “BAAA/w==” แบบ BASE64 เพื่อปิด LED

# By Somsak Lima and Itti Srisumalai
import ure
import CCS811, bme280, time, ubinascii, machine
import utime as time
import _thread
import Assd1306
from machine import UART, Pin, I2C
from struct import unpack
from cayennelpp import CayenneLPP
from time import sleep
from micropython import const
stop = False
LED_GPIO = const(2) # define a constant
led = machine.Pin(LED_GPIO, mode=machine.Pin.OUT) # GPIO output
led = Pin(2, Pin.OUT)
relay1 = Pin(12, Pin.OUT)
temp = 0
pa = 0
hum = 0
i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21))
bme = bme280.BME280(i2c=i2c)
d = Assd1306.SSD1306_I2C(128, 64, i2c, 0x3c)
uart = UART(2, 115200, timeout=2000)
s = CCS811.CCS811(i2c=i2c)
rstr = ""
def sendATcommand(ATcommand):
print("Command: {0}\r\n".format(ATcommand))
uart.write("{0}\r\n".format(ATcommand))
rstr = uart.read().decode('utf-8')
print(rstr)
return (rstr)
def led_blink(led):
global stop
rstr = ""
while not stop:
rstr = sendATcommand('AT+NMGR')
print(" - Downlink Message - : {0}\r\n".format(rstr))
if "NMGR" in rstr:
if "040064ff" in rstr: # BABk/w==
print("Command1 Detected: On LED =============>")
relay1.value(1)
elif "040000ff" in rstr: # BAAA/w==
print("Command1 Detected: Off LED =============>")
relay1.value(0)
else:
print("No Known Command Detect")
sendATcommand('AT')
sendATcommand('AT+INFO')
sendATcommand('AT+APPEUI')
sendATcommand('AT+DEVEUI')
sendATcommand('AT+APPKEY')
sendATcommand('AT+NCONFIG')
sendATcommand('AT+CHSET')
sendATcommand('AT+NRB')
while rstr != '+CGATT:1':
rstr = sendATcommand('AT+CGATT')
time.sleep(20.0)
print('Respond String')
print(rstr)
if rstr.startswith('+CGATT:1'):
print('++++OTAA OK+++++')
break
print('Retry OTAA Continue')
_thread.start_new_thread(led_blink, (led,))
cnt = 1
while True:
print("\r\n\r\nPacket No #{}".format(cnt))
temp, pa, hum = bme.values
print("**************")
print("BME280 values:")
print("**************")
print('temp:', temp, ' Hum:', hum , 'PA:', pa)
d.fill(0)
d.text("#", 95, 50)
d.text(str(cnt), 102, 50)
d.text("Temp. "+temp, 0, 0)
d.text("Hum. "+hum, 0, 10)
d.text("PA "+pa, 0, 20)
d.show()

if s.data_ready():
print("**************")
print("CSS811 values:")
print("**************")
print('eCO2: %d ppm, TVOC: %d ppb' % (s.eCO2, s.tVOC))
# d.fill(0)
d.text('eCO2 ppm', 0, 30)
d.text(str(s.eCO2), 70, 30)
d.text('TVOC ppb', 0, 40)
d.text(str(s.tVOC), 70, 40)
d.show()
time.sleep(3)
c = CayenneLPP()
c.addTemperature(1, float(temp))
c.addRelativeHumidity(2, float(hum))
c.addBarometricPressure(3, float(pa))
c.addLuminosity(4, s.eCO2)
c.addLuminosity(5, s.tVOC)
b = (ubinascii.hexlify(c.getBuffer()))
print('************ Sending Data Status **************')
led.value(1)
ATresp = sendATcommand("AT+NMGS={0},{1}".format(int(len(b)/2), (b.decode('utf-8'))))
print('********Finish Sending & Receiving Data Status******')
led.value(0)
cnt = cnt + 1
time.sleep(200.0)

ตัวอย่าง Dashboard ที่ได้จาก Node ที่เขียนด้วย MicroPython ทั้งสาม Node

07 อ่านค่า GPS ผ่าน UART

ESP32 มี Hardware Serial UART อยู่ 3 ports คือ UART0, UART1,UART2 แต่ UART0 ได้ถูกนำไปใช้กับหน้าจอ Monitor หรือ Debug การทำงาน (เชื่อมต่อกับหน้าต่าง REPL หรือ Serial Monitor) ส่วน UART1 ขา RX ก็ถูกนำไปใช้กับ SPI ดังนั้นการใช้งาน Maxiiot Dl7612-AS923-TH จะต้องใช้กับ UART2

หากเราต้องการจะต่อมอดูล GPS เราสามารถใช้ ขา UART1 ได้หนึ่งขาที่ว่างคือ GPIO10 โดบเรียกใช้ด้วยคำสั่ง

uart = UART(1, 9600,timeout=20,rx=10)

ลองดูโปรแกรมเต็มๆ ดังนี้

from machine import UART
uart = UART(1, 9600, timeout=20, rx=10)
while True:
data = uart.read(32) # read up to 32 bytes
if data is not None:
data_string = ''.join([chr(b) for b in data])
print(data_string, end="")

ขา TX จากมอดูล GPS ให้เชื่อมกับ GPIO10 เมื่อทำการ RUN จะเห็นข้อมูลจากมอดูล GPS เช่น

$GNGGA,,,,,,0,00,25.5,,,,,,*64
$GNGLL,,,,,,V,M*79
$GPGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*02
$BDGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*13
$GPGSV,1,1,00*79
$BDGSV,1,1,00*68
$GNRMC,,V,,,,,,,,,,M*4E
$GNVTG,,,,,,,,,M*2D
$GNZDA,,,,,,*56
$GPTXT,01,01,01,ANTENNA OK*35
$GNGGA,,,,,,0,00,25.5,,,,,,*64
$GNGLL,,,,,,V,M*79
$GPGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*02
$BDGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*13
$GPGSV,1,1,00*79
$BDGSV,1,1,00*68
$GNRMC,,V,,,,,,,,,,M*4E
$GNVTG,,,,,,,,,M*2D

จะเห็นว่าถ้าอยู่ในร่มส่วนใหญ่จะไม่มีค่า ส่งมา ให้นำไปลองรับในพื้นที่โล่งแจ้งก็จะได้

08 ตัวอย่างโปรแกรมแสดงข้อมูล GPS

from machine import UART
from time import sleep
from micropyGPS import MicropyGPS
uart = UART(1, 9600, timeout=20, rx=10)
sleep(1)
my_gps = MicropyGPS(+7)
while True:
my_sentence = uart.readline()
sleep(3)
print(my_sentence)
for x in str(my_sentence):
my_gps.update(x)
print('Latitude:', my_gps.latitude_string())
print('Logitude:', my_gps.longitude_string())
print('Date:', my_gps.date_string("long"))
print('Time:', my_gps.timestamp)
print('Speed (Km/hr):', my_gps.speed_string('kph'))
print('Direction:', my_gps.course)
print('satellites_in_use', my_gps.satellites_in_use)
# print('satellites_used', my_gps.satellites_used)
print('my_gps.altitude', my_gps.altitude)
print(' - - - - - - - - - - - - - - ')

ทดสอบ RUN หลัง GPS อยู่กลางแจ้ง ก็จะเห็นข้อมูลคล้ายตัวอย่างดังนี้

b’$GNGLL,1348.4283,N,10030.7865,E,144410.000,A,A*4B.00,050720,,,A*7E\r\n’
Latitude: 14° 48.3183' N
Logitude: 102° 30.6865' E
Date: July 5th, 2020
Time: (21, 44, 10.0)
Speed (Km/hr): 0.0 km/h
Direction: 0.0
satellites_in_use 9
my_gps.altitude -4.1

Lib MicropyGPS สามารถ Download ได้จาก https://github.com/inmcm/micropyGPS

09 โปรแกรมส่งค่า Sensor ความชื้นในดินแบบ Capacitive Soil Moisture Sensor ด้วย LoRaWAN พร้อมกับแสดงค่าที่อ่านได้บนหน้าจอ OLED ค่าที่ได้จะต่างกันเล็กน้อยเนื่องจากการแปลงค่า ขา AOUT ต่อเข้า P34 ของ ESP32

# By Somsak Lima and Itti Srisumalai
import machine, esp32
import ure
import time, ubinascii, machine
import utime as time
from machine import UART, Pin, I2C, ADC
from struct import unpack
from cayennelpp import CayenneLPP
from time import sleep
from micropython import const
from ssd1306 import SSD1306_I2C
stop = False
led = Pin(2, Pin.OUT)
relay1 = Pin(12, Pin.OUT)
temp = 0
pa = 0
hum = 0
WaterValue = 1200 # WaterValue = 1680;
AirValue = 3035 # AirValue = 3620;
i2c = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21))
oled = SSD1306_I2C(128, 64, i2c)
uart = UART(2, 115200, timeout=300)
pot = ADC(Pin(34))
pot.atten(ADC.ATTN_11DB) # Full range: 3.3v
rstr = ""
def sendATcommand(ATcommand):
print("Command: {0}\r\n".format(ATcommand))
uart.write("{0}\r\n".format(ATcommand))
rstr = uart.read().decode("utf-8")
print(rstr)
return rstr
def Oledhello():
oled.show_text(0, 0, "Hello", 12)
oled.show_text(0, 20, "LoRaWAN Thailand", 16)
oled.show_text(0, 35, "Start OTA Join", 16)
oled.show()
Oledhello()
sendATcommand("AT")
sendATcommand("AT+INFO")
sendATcommand("AT+APPEUI")
sendATcommand("AT+DEVEUI")
sendATcommand("AT+APPKEY")
sendATcommand("AT+NCONFIG")
sendATcommand("AT+CHSET")
sendATcommand("AT+NRB")
while rstr != "+CGATT:1":
rstr = sendATcommand("AT+CGATT")
time.sleep(20.0)
print("Respond String")
print(rstr)
if rstr.startswith("+CGATT:1"):
print("++++OTAA OK+++++")
break
print("Retry OTAA Continue")
print("Join Success")
oled.clear()
oled.show_text(0, 24, "Join Success", 16)
oled.show()
time.sleep(5.0)
count = 1
while True:
print("\r\n\r\nPacket No #{}".format(count))
pot_value = pot.read()
percent = 100 - (100 * (pot_value - WaterValue) / (AirValue - WaterValue))
print("Soil Moisture (%):" + str(percent))
percentT = str(round(percent, 1))
print("Soil Moisture (%):" + percentT)
oled.clear()
oled.show_text(0, 0, "#", 24)
oled.show_text(10, 0, str(count), 24)
oled.show_text(30, 24, percentT, 24)
oled.show_text(78, 24, "% ", 24)
oled.show()
c = CayenneLPP()
c.addRelativeHumidity(1, percent)
b = ubinascii.hexlify(c.getBuffer())
print("************ Sending Data Status **************")
led.value(1)
ATresp = sendATcommand("AT+NMGS={0},{1}".format(int(len(b) / 2), (b.decode("utf-8"))))
print("********Finish Sending & Receiving Data Status******")
led.value(0)
count = count + 1
time.sleep(10.0)

Lib SSD1306 จะใช้ของ TPYboard คลิก ซึ่งสามารถเลือกขนาดตัวอักษรได้หลายขนาด

หมายเหตุ

1. Gateway ที่ใช้ทดสอบเป็น Dragino-LG308-AS923-EC25 Multi Channel Gateway เป็น Gateway ราคาไม่แพงที่ผ่านการอนุญาตินำเข้าจาก กสทช. ถูกต้องตั้งค่าความถี่รับที่ Gateway เป็น AS2 ปัจจุบันรุ่นนี้บริษัท Dragino ได้เลิกผลิจแล้วโดยออกรุ่นใหม่คือ LPS8N-AS923-TH ออกมาแทน สามารถสั่งซื้อ Online ได้จาก Shopee

Dragino LG308-AS923-EC25
รุ่นใหม่ LPS8N-AS923-TH

ส่งท้าย

จะเห็นว่าการลง MicroPython ใน ESP32 ทำได้ง่ายด้วยโปรแกรมที่มีอยู่ในปัจจุบัน และมี IDE ที่ใช้งานได้สดวกมากขึ้น คือ Thonny และได้แจกตัวอย่างการเชื่อมเข้ากับ LoRaWAN® และส่ง Payload แบบ CayenneLPP รวมถึงการทำ Node Class C รวมถึงการรอ คำสั่ง Downlink มาควบคุม LED การใช้รูปแบบการส่ง Payload แบบ Cayenne ทำให้การเขียน Coding ทำได้ง่ายขึ้น

สามารถศึกษาข้อมูลเพิ่มเติมได้ที่

  • MicroPython Programming Tutorial: Getting Started with the ESP32 Thing คลิก
  • MicroPython: An Intro to Programming Hardware in Python คลิก

--

--

Somsak Lima
Somsak Lima

Written by Somsak Lima

สนับสนุนและส่งเสริมให้ผู้สนใจสามารถใช้งานเทคโนโลยี LoRa และ LoRaWAN ได้ โดยนำความรู้ที่ได้ไปต่อยอดเพื่อใช้งาน

No responses yet