Skip to content

Rivian Geo via Meshtastic

alt text

The Autonomous Deezwatts Meshtastic Sync Service

Section titled “The Autonomous Deezwatts Meshtastic Sync Service”

Tucked away in my Rivian’s center console, a Raspberry Pi acts as the silent tracker, a $25 lojack for my $80k+ truck. Every minute, the script springs to life, polling for precise geo data from a Meshtastic Wio Tracker Pro whenever the vehicle is in motion. This telemetry is serialized and streamed directly to a GCP bucket as JSON, creating a persistent, cloud-based record of every Michigan mile.

import meshtastic
import meshtastic.serial_interface
import json
import os
import glob
import time
from datetime import datetime
from google.cloud import storage
# --- Configuration ---
BUCKET_NAME = "deezwatts-meshtastic-bucket"
LOCAL_DIR = "./pending_uploads"
POLL_INTERVAL = 60 # 10 minutes in seconds
# os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/key.json"
# ---------------------
os.makedirs(LOCAL_DIR, exist_ok=True)
def upload_to_gcp(data, filename):
"""Attempts to upload to GCP, returns True if successful."""
try:
client = storage.Client()
bucket = client.bucket(BUCKET_NAME)
blob = bucket.blob(filename)
blob.upload_from_string(
data=json.dumps(data, indent=4, default=str),
content_type='application/json'
)
return True
except Exception:
return False
def sync_backlog():
"""Checks the local folder for files that failed previously."""
backlog_files = sorted(glob.glob(os.path.join(LOCAL_DIR, "*.json")))
if not backlog_files:
return
print(f"Found {len(backlog_files)} pending files. Attempting sync...")
for file_path in backlog_files:
filename = os.path.basename(file_path)
try:
with open(file_path, 'r') as f:
data = json.load(f)
if upload_to_gcp(data, filename):
os.remove(file_path)
print(f" Synced and cleared: {filename}")
else:
print(f" Cloud still unreachable. Stopping sync.")
break # Stop trying if the first one fails
except Exception as e:
print(f" Error reading {filename}: {e}")
def run_task():
"""The core polling logic."""
print(f"\n--- Starting Poll: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ---")
interface = None
try:
# Connect to radio
interface = meshtastic.serial_interface.SerialInterface()
nodes = interface.nodes
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"nodes_{timestamp}.json"
payload = {
"polled_at": datetime.now().isoformat(),
"nodes": nodes
}
# Try Cloud
if upload_to_gcp(payload, filename):
print("Success: Data sent directly to GCP.")
sync_backlog() # If cloud is up, clear any old local files
else:
# Fallback
filepath = os.path.join(LOCAL_DIR, filename)
with open(filepath, "w") as f:
json.dump(payload, f, indent=4, default=str)
print(f"Offline: Saved {filename} to local buffer.")
except Exception as e:
print(f"Connection Error: {e}")
finally:
if interface:
interface.close()
def main():
print(f"Service started. Polling every {POLL_INTERVAL/60} minutes.")
print(f"Monitoring device and GCP bucket: {BUCKET_NAME}")
while True:
run_task()
print(f"Sleeping for {POLL_INTERVAL/60} minutes...")
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nStopping service...")

To make this a professional background service, we will create a Systemd Service. This ensures the script starts when the Rivian is powered on, restarts automatically if it crashes, and logs all its output to the system journal.

Open a terminal and create the following file (you’ll need sudo):

cat > /etc/systemd/system/deezwatts-meshtastic-sync.service

Here is the meat and potatos of the service file.

[Unit]
Description=Deezwatts Meshtastic to GCP Sync Service
After=network.target
[Service]
User=sween
Group=dialout
WorkingDirectory=/home/sween/deezwatts-meshtastic
ExecStart=/usr/bin/python3 /home/sween/deezwatts-meshtastic/sync_script.py
# Environment variable for GCP Credentials
Environment="GOOGLE_APPLICATION_CREDENTIALS=/home/sween/deezwatts-meshtastic/deezwatts-bucket-key.json"
# Restart logic
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target

This raspberry pi is powered by the Rivian’s 12v system and is always on when the vehicle is on.

# Reload systemd to recognize the new file
sudo systemctl daemon-reload
# Enable the service to start on boot
sudo systemctl enable meshtastic-sync.service
# Start it right now
sudo systemctl start meshtastic-sync.service