Définition d’un tracker GPS avec le TTGO T3 Lora 32 OLED.
Projet:
Firmware
Connexions: neo 6m GPS, BME280, batterie
autre ???

Le module doit se connecter sur le Wifi et permettre un accès telnet pour avoir un retour d’info avec le module en remote.

Le code du projet se trouve sur github
Le GPS NEO 6m est connecté sur les broches GPIO0 et GPIO2
- +5 Volts
- GND
- Neo TX - GPIO 2
- Neo RX - GPIO 0
Dans le code:
#define RXD2 0
#define TXD2 4
#define GPS_BAUD 9600
HardwareSerial ss(2);
ss.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Ensuite les trames NMEA lues sur le Serial ss seront interprètées par la librairie tinyGPS++
Les valeurs seront alors écrites sur l’écran OLED.
Les valeurs sont
Il ne reste donc plus qu’a gèrer la connexion TTN et transmettre ces données.
Les examples donnés par lilyGo sur github incluent plusieurs modules dans le même code, il faut donc faire attention au niveau du bus I2C qui est parfois initialisé et parfaois pas en fonction du module utilisé. Dans le cas du T3_S3_V1_2_SX1262 par example, l’écran est prédéfinit sur la variable u8g2 avec le librairie u8g2, le second I2C n’est lui pas définit La PIN SDA est accessible sur le GPIO (PIN 18), mais la PIN SCL (GPIO 17) ne l’est pas, donc pour ajouter des module I2C il faut utiliser d’autres PINs et initialiser le deuxième I2C (via Wire1 par example).
(Manuel de la librairie u8g2)[https://github.com/olikraus/u8g2/wiki/u8g2reference]
Une fois la connexion radio établie avec le réseau TTN, lees messages échangés sont les suivants:
Join message topic :
{
"end_device_ids":{"device_id":"on4kch-gps","application_ids":{"application_id":"ttgo32-jpco"},"dev_eui":"70B3D57ED00700D7","join_eui":"0000000000000000","dev_addr":"260B1741"},
"correlation_ids":["gs:uplink:01M027FH7HJSB4HH17N9G8NDM8"],
"received_at":"2026-08-15T08:09:15.017387224Z",
"join_accept":{"session_key_id":"AaAEd8T3LN2jiZ90gGrkew==","received_at":"2026-08-15T08:09:13.201869521Z"}
}
UP message topic
{
"end_device_ids":{"device_id":"on4kch-gps","application_ids":{"application_id":"ttgo32-jpco"},
"dev_eui":"70B3D57ED00700D7",
"join_eui":"0000000000000000",
"dev_addr":"260B1741"},
"correlation_ids":["gs:uplink:01M027FH7HJSB4HH17N9G8NDM8"],
"received_at":"2026-08-15T08:09:15.017387224Z",
"join_accept":{"session_key_id":"AaAEd8T3LN2jiZ90gGrkew==","received_at":"2026-08-15T08:09:13.201869521Z"}
}
location/solved
{
"end_device_ids":{"device_id":"on4kch-gps","application_ids":{"application_id":"ttgo32-jpco"},"dev_eui":"70B3D57ED00700D7","join_eui":"0000000000000000","dev_addr":"260B1741"},
"correlation_ids":["gs:uplink:01M027TVXKCAQ67M8N6E6ZMQDS"],
"received_at":"2026-08-15T08:15:24.801482345Z",
"location_solved":{"service":"frm-payload","location":{"latitude":50.73268298701544,"longitude":5.684255700365071,"altitude":56,"source":"SOURCE_GPS"}}
}
Ce code connecte
#!/usr/bin/python
import paho.mqtt.client as mqtt
import json
import rrdtool
import queue
import sys
import requests
import asyncio
import random
import time
import aprslib
import mariadb
from datetime import datetime
msgs = queue.Queue()
hostname="IP broker"
remote_user="username"
remote_password="password"
topic = [("ttn/on4kch-gps/location/solved",1)] # we are listening to the location topic only
post_interval = 600
f = open("/var/log/rrd/ttn.log","a") # log file
def on_subscribe(client, userdata, mid, granted_qos):
# Since we subscribed only for a single channel, reason_code_list contains
# a single entry
#if reason_code_list[0].is_failure:
# print(f"Broker rejected you subscription: {reason_code_list[0]}")
#else:
f.write(f"Subscribed topic msg ID: {mid} with QOS: {granted_qos}\n")
f.flush()
def on_unsubscribe(client, userdata, mid, gtanted_qos):
# Be careful, the reason_code_list is only present in MQTTv5.
# In MQTTv3 it will always be empty
#if len(reason_code_list) == 0 or not reason_code_list[0].is_failure:
# print("unsubscribe succeeded (if SUBACK is received in MQTTv3 it success)")
#else:
f.write(f"Broker replied with failure: {reason_code_list[0]}\n")
f.flush()
client.disconnect()
def on_message(client,userdata,message):
msgs.put(message)
def workq(message):
# process the incoming mqtt message
# process only messages from the right topic
# ttn/on4kch-gps/location/solved is a message from the thing network
parsed = message.payload.decode("utf-8")
json_object = json.loads(parsed)
# check the topic
if (str(message.topic).startswith("ttn/on4kch-gps/location/solved")):
parsed = message.payload.decode("utf-8")
json_object = json.loads(parsed)
try:
lat = json_object['location_solved']['location']['latitude']
long = json_object['location_solved']['location']['longitude']
alt = json_object['location_solved']['location']['altitude']
SQL=f"INSERT INTO `on4kch-gps` (`latitude`, `longitude`, `altitude`) VALUES ('{lat}','{long}','{alt}')"
print(SQL)
insertdb(SQL)
except:
f.write("error in SQL insert\n")
f.flush()
def on_connect(client, flags, reason_code, properties=None):
client.subscribe(topic)
def on_disconnect(client, flags, reason_code, properties=None):
f.write("mqtt disconnected\n")
f.flush()
def insertdb(SQL):
cursor = connection.cursor()
cursor.execute(SQL)
connection.commit()
cursor.close()
def Now():
return datetime.now().strftime("%c")
# broker connexion related
client_id = f'collector_ttn-{random.randint(0, 1000)}'
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1,client_id)
mqttc.username_pw_set(remote_user, remote_password)
mqttc.connect(hostname,1883)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc.on_unsubscribe = on_unsubscribe
f.write(Now()+" Starting collector_ttn.py\n")
f.flush()
# mqtt loop, process reconnected if necessary
mqttc.loop_start()
# connect the mariqdb server
try:
# Connect to MariaDB
connection = mariadb.connect(
user="username",
password="password",
host="mariadb_server_name",
port=3306,
database="TTN"
)
# make sure it reconnect if disconnect unexpectedly
connection.auto_reconnect = True
f.write(Now()+" Connected to MariaDB TTN successfully!\n")
f.write(Now()+f" MariaDB version: {connection.server_version}\n")
except mariadb.Error as e:
f.write(Now()+f" Error connecting to MariaDB: {e}\n")
sys.exit(1)
f.flush() # make sure message are written to the log file
cur_time = datetime.now() # initialize the current time
# process the messages inserted in the queue by the mqtt message receiver
# loop indefinitely
while(True):
if (not msgs.empty()):
#f.write("queue size: "+str(msgs.qsize())+" ")
msg = msgs.get()
workq(msg)
time.sleep(0.001)