T-Beam V.1.1 ESP32 LoRaWAN Pycom

Somsak Lima
8 min readOct 12, 2024

--

Web Page ของบริษัท https://lilygo.cc/
Product Page https://lilygo.cc/products/t-beam
Github คลิก
LoRaWAN Example คลิก

ขาของ Tbeam ดูได้จาก คลิก

DIO0 IO26
DIO1 IO33
DIO2 IO32
ISO IO19
OSI IO27
SCK IO5
NSS IO18
RESET/IRQ IO23

1.Download flash_download_tool_3.9.7

  1. Download flash_download_tool_3.9.7

คลิก

คลิกในช่อง Download

2.วิธีใช้ flash_download_tool_3.9.7

แนวทางดูจาก

ใน Link Address ไม่ตรงกับ Firmware ที่ใช้ของ Lilygo T-Beam

ดูจาก File script ระบุดังนี้

[
[“e”, “0x1000”, “0x40000”],
[“e”, “0x41000”, “0x40000”],
[“e”, “0x81000”, “0x40000”],
[“e”, “0xC1000”, “0x40000”],
[“e”, “0x101000”, “0x40000”],
[“e”, “0x141000”, “0x40000”],
[“e”, “0x181000”, “0x40000”],
[“e”, “0x1C1000”, “0x40000”],
[“w”, “0x1000”, “bootloader.bin”],
[“w”, “0x8000”, “partitions_4MB.bin”],
[“w”, “0x10000”, “tbeamv1.bin”]
]

ให้ ติกถูกช่องด้านหน้าด้วย

3.Download Firmware

https://github.com/nunomcruz/pycom-micropython-sigfox/releases

https://github.com/pycom/pycom-micropython-sigfox

Firmware ของ Pycom

อาจจะใช้ Web App Flash โดยใช้เวปแอป ที่เวป https://espressif.github.io/esptool-js/

4. reboot หลัง Flash สำเร็จ

5.ตัวอย่างวิธีใช้ ABP

https://docs.pycom.io/tutorials/networks/lora/lorawan-abp/

from network import LoRa
import socket
import ubinascii
import struct
import time
from cayennelpp import CayenneLPP

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923, tx_power=15)
dev_addr = struct.unpack(">l", ubinascii.unhexlify('250115AA'))[0]
nwk_swkey = ubinascii.unhexlify('5D14801A33B3A35C67CCA15B3343E1')
app_swkey = ubinascii.unhexlify('40302FD21B34509864C572D40D1188')
lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

while True:
temp=25
hum=50
pa=1000
print('temp:', temp, ' Hum:', hum , 'PA:', pa)
c = CayenneLPP()
c.addTemperature(1, float(temp))
c.addRelativeHumidity(3, float(hum))
msg=bytes(list(c.getBuffer()))
s.setblocking(True)
s.send(msg)
s.setblocking(False)
data = s.recv(64)
print('Downlink:',data)
time.sleep(10)

OTAA

https://docs.pycom.io/tutorials/networks/lora/lorawan-otaa/

from network import LoRa
import socket
import time
import ubinascii
from cayennelpp import CayenneLPP

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923 ,tx_power=15)

app_eui = ubinascii.unhexlify('ABABABABABABAB')
app_key = ubinascii.unhexlify('45ADB18947EF42ADC36231E01E5084')
dev_eui = ubinascii.unhexlify('70B3D57ED006B0')
lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0)

while not lora.has_joined():
time.sleep(2.5)
print('Not yet joined...')

print('Joined')
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

while True:
temp=25
hum=50
pa=1000
print('temp:', temp, ' Hum:', hum , 'PA:', pa)
c = CayenneLPP()
c.addTemperature(1, float(temp))
c.addRelativeHumidity(3, float(hum))
msg=bytes(list(c.getBuffer()))
s.setblocking(True)
s.send(msg)
s.setblocking(False)
data = s.recv(64)
print('Downlink:',data)
time.sleep(10)

6.Hard Reset

import machine;help (machine);machine.reset()

7. Blink.py

GPIO4 = 'P3'
from machine import Pin
from time import sleep
#led = machine.Pin('P3', Pin.OUT)
led = machine.Pin('G4', Pin.OUT)
while True:
led.value(not led.value())
sleep(0.5)

8. Pin Mapping

เรียกชื่อ Pin

'P1'
'P2'
G3,GPIO3,P0
G1,GPIO1,P1
G0,GPIO0,P2
G4,GPIO4,P3
G15,GPIO15,P4
G5,GPIO5,P5
G27,GPIO27,P6
G19,GPIO19,P7
G2,GPIO2,P8
G12,GPIO12,P9
G13,GPIO13,P10
G22,GPIO22,P11
G21,GPIO21,P12
G36,GPI36,P13
G37,GPI37,P14
G38,GPI38,P15
G39,GPI39,P16
G35,GPI35,P17
G34,GPI34,P18
G32,GPIO32,P19
G33,GPIO33,P20
G26,GPIO26,P21
G25,GPIO25,P22
G14,GPIO14,P23
G18,GPIO18,P97
G23,GPIO23,P98

9.ScanI2C.py

from machine import Pin, I2C
import machine

#i2c = I2C(0, pins=('P12','P11'))
i2c = I2C(0, pins=('G21','G22'))
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))
Scan i2c bus...
i2c devices found: 2
Decimal address: 52 | Hexa address: 0x34
Decimal address: 60 | Hexa address: 0x3c

10.UARTGPS.py

from machine import UART
from time import sleep
uart = UART(1, 9600)
#uart = UART(1, baudrate=9600, pins=('P9','P18'))
uart = UART(1, 9600, pins=('G12','G34'))
sleep(1)

while True:
data = uart.read(32)
if data is not None:
data_string = ''.join([chr(b) for b in data])
print(data_string, end="")

11.GSP_Lat_Lon.py

from machine import UART
from time import sleep
from micropyGPS import MicropyGPS
uart = UART(1, 9600)
#uart = UART(1, baudrate=9600, pins=('P9','P18'))
uart = UART(1, 9600, pins=('G12','G34'))
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(x)

print('Latitude:',my_gps.latitude)
print('Logitude:',my_gps.longitude)
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('--------------------------- ')

12.TTNMapper.py

from network import LoRa
import socket
import time
import ubinascii
import machine
from cayennelpp import CayenneLPP
from micropyGPS import MicropyGPS
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923 ,tx_power=15)

app_eui = ubinascii.unhexlify('ABABABABABABAB')
app_key = ubinascii.unhexlify('45ADB18947EF42ADC36231E01E5084')
dev_eui = ubinascii.unhexlify('70B3D57ED006B0')
lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0)

com = UART(1, 9600)
#com = UART(1, baudrate=9600, pins=('P9','P18'))
com = UART(1, 9600, pins=('G12','G34'))

my_gps = MicropyGPS(7)
my_gps.local_offset
while not lora.has_joined():
time.sleep(2.5)
print('Not yet joined...')

print('Joined')
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
cnt = 1
while True:
print("\r\n\r\nPacket No #{}".format(cnt))
my_sentence = com.readline()
print(my_sentence)
for x in str(my_sentence):
my_gps.update(x)
print("--------------------------- ")
print("Date:", my_gps.date_string("s_dmy"))
print("Time:", my_gps.timestamp)
print(
"Time: {}:{}:{}".format(
my_gps.timestamp[0], my_gps.timestamp[1], my_gps.timestamp[2]
)
)
print("Direction:", my_gps.course)
print("satellites_in_use", my_gps.satellites_in_use)
print("my_gps.altitude", my_gps.altitude)
lat = my_gps.latitude[0] + my_gps.latitude[1] / 60
lon = my_gps.longitude[0] + my_gps.longitude[1] / 60
alt = my_gps.altitude
print("LATITUDE %2.6f" % float(lat))
print("LONGITUDE %2.6f" % float(lon))
print("ALTITUDE %f" % float(alt))
print("Speed (Km/hr):", my_gps.speed_string("kph"))
print("Speed (mph):", my_gps.speed_string("mph"))
print("Speed (mph):", my_gps.speed[1])
speed = my_gps.speed[1]
print('Speed:',speed)
print("--------------------------- ")
print ('Lat:',float(lat))
print ('Lon:',float(lon))
print ('Alt:',float(alt))
timestamp = my_gps.timestamp
hour = timestamp[0]
rtc = str(int(timestamp[0]))+":"+str(int(timestamp[1]))+":"+str(int(timestamp[2]))
print(rtc)
temp=25
hum=50
pa=1000
print('temp:', temp, ' Hum:', hum , 'PA:', pa)
c = CayenneLPP()
c.addTemperature(1, float(temp))
c.addRelativeHumidity(3, float(hum))
c.addGPS(7, float(lat), float(lon),alt)
msg=bytes(list(c.getBuffer()))
s.setblocking(True)
s.send(msg)
s.setblocking(False)
data = s.recv(64)
print('Downlink:',data)
cnt = cnt + 1
time.sleep(10)

12.2 TTNMapperOLED.py


import socket
import time
import ubinascii
import ssd1306
from network import LoRa
from cayennelpp import CayenneLPP
from machine import I2C
from micropyGPS import MicropyGPS

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923, tx_power=15)

app_eui = ubinascii.unhexlify("ABABABABABABABAA")
app_key = ubinascii.unhexlify("45ADB18947EF42ADC36231E01E5084AA")
dev_eui = ubinascii.unhexlify("70B3D57ED006B0AA")
lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0)

i2c = I2C(0, pins=("P12", "P11"))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
oled.text("LoRaWAN Thailand", 0, 0)
oled.text("Trying to Join", 0, 20)
oled.show()

#com = UART(1, 9600)
com = UART(1, 9600, pins=("G12", "G34"))

my_gps = MicropyGPS(7)
my_gps.local_offset
while not lora.has_joined():
time.sleep(2.5)
print("Not yet joined...")
print("Joined")
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
cnt = 1
while True:
print("\r\n\r\nPacket No #{}".format(cnt))
my_sentence = com.readline()
print(my_sentence)
for x in str(my_sentence):
my_gps.update(x)
print("--------------------------- ")
print("Date:", my_gps.date_string("s_dmy"))
print("Time:", my_gps.timestamp)
print(
"Time: {}:{}:{}".format(
my_gps.timestamp[0], my_gps.timestamp[1], my_gps.timestamp[2]
)
)
print("Direction:", my_gps.course)
print("satellites_in_use", my_gps.satellites_in_use)
print("my_gps.altitude", my_gps.altitude)
lat = my_gps.latitude[0] + my_gps.latitude[1] / 60
lon = my_gps.longitude[0] + my_gps.longitude[1] / 60
alt = my_gps.altitude
print("LATITUDE %2.6f" % float(lat))
print("LONGITUDE %2.6f" % float(lon))
print("ALTITUDE %f" % float(alt))
print("Speed (Km/hr):", my_gps.speed_string("kph"))
print("Speed (mph):", my_gps.speed_string("mph"))
print("Speed (mph):", my_gps.speed[1])
speed = my_gps.speed[1]
print("Speed:", speed)
print("--------------------------- ")
print("Latitude:", float(lat))
print("Longitude:", float(lon))
print("Alt:", float(alt))

timestamp = my_gps.timestamp
hour = timestamp[0]
rtc = (
str(int(timestamp[0]))
+ ":"
+ str(int(timestamp[1]))
+ ":"
+ str(int(timestamp[2]))
)
print("Time:", rtc)
temp = 25
hum = 50
pa = 1000
print("temp:", temp, " Hum:", hum, "PA:", pa)
oled.fill(0)
oled.text("LoRaWAN Thailand", 0, 0)
oled.text("Temp: " + str(temp), 0, 10)
oled.text("Hum: " + str(hum), 0, 20)
oled.text("PA:" + str(pa), 0, 30)
oled.text("Count " + str(cnt), 0, 50)
oled.show()
print("Latitude:", lat, "Longitude:", lon)
time.sleep(5)
oled.fill(0)
oled.text("Lat:", 0, 0)
oled.text(my_gps.latitude_string(), 0, 10)
oled.text("/Lon:", 25, 0)
oled.text(my_gps.longitude_string(), 0, 20)
oled.text("Date:" + my_gps.date_string("long"), 0, 30)
oled.text("LAT:" + str(float(lat)), 0, 40)
oled.text("LON:" + str(float(lon)), 0, 50)
oled.show()

c = CayenneLPP()
c.addTemperature(1, float(temp))
c.addRelativeHumidity(3, float(hum))
c.addGPS(7, float(lat), float(lon), alt)
msg = bytes(list(c.getBuffer()))
s.setblocking(True)
s.send(msg)
s.setblocking(False)
data = s.recv(64)
print("Downlink:", data)
cnt = cnt + 1
time.sleep(10)

13.แสดง devEUI

import binascii
from network import LoRa
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923 ,tx_power=15)
devEUI=binascii.hexlify(lora.mac())
print(devEUI)

14.OLED

ให้ใช้ SD1306.py จาก link
https://forum.micropython.org/viewtopic.php?t=9226

หรือ

https://github.com/robert-hh/pycom-micropython-sigfox/blob/homebrew/drivers/display/ssd1306.py

# MicroPython SSD1306 OLED driver, I2C and SPI interfaces

from micropython import const
import framebuf


# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)

# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()

def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
self.write_cmd(cmd)
self.fill(0)
self.show()

def poweroff(self):
self.write_cmd(SET_DISP | 0x00)

def poweron(self):
self.write_cmd(SET_DISP | 0x01)

def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)

def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))

def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)


class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
super().__init__(width, height, external_vcc)

def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)

def write_data(self, buf):
self.i2c.writeto(self.addr, b'\x40' + buf)

class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)

def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)

def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)

15. กำหนดกำลังส่งให้เป็นไปตาม กสทช. กำหนด คือตั้ง TX Power ไว้ 15dBm หากสายอากาศมีเกณฑ์ขยายประมาณ 2dB กำลังออกอากาศ eirp จะประมาณ 50 mW eirp

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923, tx_power=15)

16. lora.stats()

print(lora.stats())

>>> print(lora.stats())
(rx_timestamp=14750909, rssi=-38, snr=7.0, sfrx=5, sftx=5, tx_trials=0, tx_power=15, tx_time_on_air=72, tx_counter=0, tx_frequency=923400000)

17. https://espressif.github.io/esptool-js/

ESPtools

18.ทดสอบง่ายๆ

ABP

from network import LoRa
import socket
import time
import ubinascii
import struct
######
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923)
dev_addr = struct.unpack(">l", ubinascii.unhexlify('250115'))[0]
nwk_swkey = ubinascii.unhexlify('5D14801A33B3A35C67CCA15B3343E1')
app_swkey = ubinascii.unhexlify('40302FD21B34509864C572D40D1188')
lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
#######
#time.sleep(5)
#print(lora.has_joined())
s.setblocking(0)
s.send("Hello")
print(lora.stats())

OTAA

from network import LoRa
import socket
import time
import ubinascii
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923)
app_eui = ubinascii.unhexlify('ABABABABABABAB')
app_key = ubinascii.unhexlify('45ADB18947EF42ADC36231E01E5084')
dev_eui = ubinascii.unhexlify('70B3D57ED006B0')
lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0)
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
#######
time.sleep(10)
print(lora.has_joined())
#######
#time.sleep(5)
print(s.send("Hello"))
print(lora.stats())

19.แก้ boot.py เป็นไฟล์ที่ต้องการให้ทำงาน

import machine
machine.main('otaa.py')

1X.คู่มือ

https://docs.pycom.io/tutorials/networks/lora/

1Y.Example

https://github.com/nunomcruz/ttgo-lorawan-probe/tree/main/lib

Forum

https://github.com/iot-lnu/applied-iot/tree/master/Pycom%20Micropython%20(esp32)

--

--

Somsak Lima
Somsak Lima

Written by Somsak Lima

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

No responses yet