


# rx,tx 用 pin16,17 才不會跟 thonny 所需要的 uart0 相衝
# NEO-6M的 VCC -- esp32的 3.3V
# NEO-6M的 RX -- esp32的 TX (gpio17)
# NEO-6M的 TX -- esp32的 RX (gpio16)
# NEO-6M的 GND -- esp32的 GND
# 目前是 led 燈亮,代表有抓到可用的 gps 訊號
from machine import Pin
from machine import UART
import time
import network
import urequests
import gc
def getStrGMTp8(strDDMMYY, strUTCTime):
'''
傳入從 gps 模組得到的 日月年,時分秒
傳出 年(4碼)月(2碼)日(2碼)時(2碼)分(2碼)秒(2碼)
'''
strGmtp8="00000000000000"
try:
iYear=int("20"+strDDMMYY[4:6])
iMonth=int(strDDMMYY[2:4])
iDay=int(strDDMMYY[0:2])
iHour=int(strUTCTime[0:2])
iMinute=int(strUTCTime[2:4])
iSecond=int(strUTCTime[4:6])
oUtcTime=(iYear,iMonth,iDay,iHour,iMinute,iSecond,0,0)
oUtcTimestamp=time.mktime(oUtcTime)
oGmtp8Time=time.localtime(oUtcTimestamp+8*3600)
strGmtp8="{:04d}{:02d}{:02d}{:02d}{:02d}{:02d}".format(
oGmtp8Time[0], oGmtp8Time[1], oGmtp8Time[2],
oGmtp8Time[3], oGmtp8Time[4], oGmtp8Time[5])
except Exception as oE:
#print(oE)
pass
return strGmtp8
def getFRealDeg(strVal, strDir):
"""
把經緯度從文字轉成浮點數,若有任何錯誤,傳回None。
經度 dddmm.mmmm E,W
緯度 ddmm.mmmm N,S
"""
if not strVal or strDir not in ['N','S','E','W']:
return None
try:
if strDir in ['N','S']:
iDegLen=2
else:
iDegLen=3
iDeg=int(strVal[:iDegLen])
fMin=float(strVal[iDegLen:])
fFinalDeg=iDeg+fMin/60
if strDir in ['S','W']:
fFinalDeg=-fFinalDeg
return fFinalDeg
except:
return None
def getBooIsConnected():
global oWlan, lstSSIDPASS, iSSIDPASSIndex, strSSID, strPASS
if oWlan.isconnected()==False:
oWlan.disconnect() # 清除殘餘狀態
time.sleep_ms(200)
try:
iSSIDPASSIndex=(iSSIDPASSIndex+1) % len(lstSSIDPASS)
strSSID=lstSSIDPASS[iSSIDPASSIndex][0]
strPASS=lstSSIDPASS[iSSIDPASSIndex][1]
oWlan.connect(strSSID, strPASS)
except:
return False # 連線錯誤,本次退出。
iTimeout=20
while oWlan.isconnected()==False and iTimeout>0:
time.sleep(1)
iTimeout=iTimeout-1
if oWlan.isconnected()==False:
return False # 網路連不上,本次退出。
return True
def sendData(strDDMMYY, strUTCTime, fLat, fLon):
global oWlan, iLastTicks, strSSID
if fLat is None or fLon is None:
return # 數據有問題,本次退出。
if getBooIsConnected()==False:
return # 連線失敗,本次退出。
oResponse=None
try:
strRecTime="20"+strDDMMYY[4:6]+"/"+strDDMMYY[2:4]+"/"+strDDMMYY[0:2]+"%20"+strUTCTime[0:2]+":"+strUTCTime[2:4]+":"+strUTCTime[4:6]
strUrl=(
"https://web.godroad.tw/api5/getgpstest.aspx?"
"lat="+str(fLat)+
"&lng="+str(fLon)+
"&rectime="+strRecTime+
"&cid=5554"
)
oResponse=urequests.get(strUrl, timeout=5)
iLastTicks=time.ticks_ms()
except:
pass
finally:
if oResponse:
oResponse.close()
oResponse=None
try:
# GET 方法,可用 http
strGmtp8=getStrGMTp8(strDDMMYY, strUTCTime)
strUrl=(
"http://liujiaje.com/resources/20260408_esp32_lolin_d32_neo-6m-gps/api/gps.php?"
"lat="+str(fLat)+
"&lon="+str(fLon)+
"&gmtp8="+strGmtp8+
"&ssid="+strSSID
)
oResponse=urequests.get(strUrl, timeout=5)
iLastTicks=time.ticks_ms()
except:
pass
finally:
if oResponse:
oResponse.close()
'''
oResponse=None
try:
# POST 方法,最好用 https
strUrl="https://liujiaje.com/temp/20260408_gps/index.php"
dictHeader={"Content-Type": "application/x-www-form-urlencoded"}
strData="lat="+str(fLat)+"&lon="+str(fLon)+"&ddmmyy="+strDDMMYY+"&utc="+strUTCTime
oResponse=urequests.post(strUrl, data=strData, headers=dictHeader, timeout=5)
iLastTicks=time.ticks_ms()
except:
pass
finally:
if oResponse:
oResponse.close()
'''
gc.collect()
def keepAlive():
global oWlan, iLastTicks, strSSID
if getBooIsConnected()==False:
return # 連線失敗,本次退出。
oResponse=None
try:
strUrl=(
"http://liujiaje.com/resources/20260408_esp32_lolin_d32_neo-6m-gps/api/keepalive.php?"
"&ssid="+strSSID
)
oResponse=urequests.get(strUrl, timeout=5)
iLastTicks=time.ticks_ms()
except:
pass
finally:
if oResponse:
oResponse.close()
gc.collect()
def blink(iTimes, fDelayMs=50):
global oLed
if not oLed:
return
try:
for _ in range(iTimes):
oLed.value(0) # 亮燈
time.sleep_ms(fDelayMs)
oLed.value(1) # 暗燈
time.sleep_ms(fDelayMs)
except:
pass
# 連接 wifi 參數
lstSSIDPASS=[
("godroad", "66666666"),
("ssid2", "pass2"),
("ssid3", "pass3")]
iSSIDPASSIndex=-1
strSSID=""
strPASS=""
iLastTicks=0 # 記錄最後一次送出網路封包的時間
oWlan=network.WLAN(network.STA_IF)
oWlan.active(True)
# 使用 led 燈號
oLed=Pin(5, Pin.OUT)
oLed.value(1)
# 與 GPS 模組(neo-6m) 有關
oUart = UART(2, baudrate=9600, tx=17, rx=16, rxbuf=2048)
baBuf=b""
while True:
#blink(iTimes=1, fDelayMs=10)
if time.ticks_diff(time.ticks_ms(), iLastTicks)>30000: # 網路傳輸每隔30秒送一次封包,確保行動網路不被關閉
keepAlive()
baData=oUart.read()
if baData:
baBuf+=baData
if len(baBuf)>2048:
baBuf=baBuf[-1024:]
oLed.value(1)
iPos=baBuf.find(b"\n")
while iPos!=-1:
strLine=baBuf[:iPos]
baBuf=baBuf[iPos+1:]
iPos=baBuf.find(b"\n")
#blink(iTimes=1, fDelayMs=2)
strLine=strLine.strip()
try:
strLine=strLine.decode()
except:
strLine=""
if strLine.startswith('$GPRMC') or strLine.startswith('$GNRMC'):
lstPart=strLine.split(',')
if len(lstPart)>12:
strMessageType=lstPart[0]
strUTCTime=lstPart[1]
strStatus=lstPart[2] # Void/Active
strLatVal=lstPart[3] #
strLatDir=lstPart[4] # North/South
strLonVal=lstPart[5] #
strLonDir=lstPart[6] # East/West
if strStatus=="A":
fLat=getFRealDeg(strLatVal, strLatDir)
fLon=getFRealDeg(strLonVal, strLonDir)
oLed.value(0)
else:
fLat=None
fLon=None
strSpeedOverGround=lstPart[7] # knots
strTrackAngle=lstPart[8]
strDDMMYY=lstPart[9]
strMagneticVariationVal=lstPart[10]
strMagneticVariationDir=lstPart[11]
strMode=lstPart[12] # Autonomous/Differential/Estimated
try:
strChecksum=strMode[1:]
strMode=strMode[0:1]
except:
strChecksum=""
strMode=""
sendData(strDDMMYY, strUTCTime, fLat, fLon)
try:
time.sleep_ms(50)
except KeyboardInterrupt:
break
若要防水,可用熱熔膠封住瓶口。







