Skip to content

Instantly share code, notes, and snippets.

@ThomasLobker
Created March 25, 2022 13:47
Show Gist options
  • Save ThomasLobker/59c5001e5a4bc9ba4a8e9701276d6226 to your computer and use it in GitHub Desktop.
Save ThomasLobker/59c5001e5a4bc9ba4a8e9701276d6226 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import sys
import os
import thread
import threading
import time
import syslog
import netifaces
import serial
import Queue
from systemd import daemon
syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON)
def log(message, level="info"):
if level == "info":
syslog.syslog(syslog.LOG_INFO, message)
daemon.notify("STATUS=%s" % message)
return
if level == "notice":
syslog.syslog(syslog.LOG_NOTICE, message)
daemon.notify("STATUS=%s" % message)
return
if level == "critical":
syslog.syslog(syslog.LOG_CRIT, message)
daemon.notify("STATUS=%s" % message)
return
syslog.syslog(syslog.LOG_DEBUG, message)
return
def init_pppd(provider):
log("Calling mobile network operator to establish the internet connection")
time.sleep(1)
pppd = os.system("pppd call %s" % provider)
log("PPPD status: %s" % pppd)
return os.WEXITSTATUS(pppd)
def ping(target):
response = os.system("ping -c 1 -q %s" % target)
return os.WEXITSTATUS(response)
class background(threading.Thread):
def __init__(self, queue, event, interval, target):
threading.Thread.__init__(self)
self.queue = queue
self.event = event
self.interval = interval
self.state = 0
self.target = target
super(background, self).__init__()
def run(self):
try:
log("Waiting for network interface [ppp0] to become online", "debug")
while self.state == 0 and not self.event.wait(self.interval):
interfaces = netifaces.interfaces()
try:
index = interfaces.index('ppp0')
for interface in netifaces.ifaddresses('ppp0'):
if interface == netifaces.AF_INET:
address = netifaces.ifaddresses('ppp0')[netifaces.AF_INET][0]['addr'].encode('ascii','ignore')
log("Interface [ppp0] has been detected at index [%s] with address [%s]" % (interfaces.index('ppp0'), address))
self.state = 1
except ValueError:
log("Unable to get interface [ppp0]", "debug")
log("Waiting for remote target [%s] to be responsive" % self.target, "debug")
while self.state == 1 and not self.event.wait(self.interval):
interfaces = netifaces.interfaces()
try:
response = ping(self.target)
if response == 0:
log("Target [%s] is now reachable" % self.target)
self.state = 2
except ValueError:
log("Unable to get response from target [%s]" % self.target, "debug")
self.queue.put({ "type": "success", "message": "PPP connection with mobile network is now ready" })
except Exception:
self.queue.put({ "type": "error", "message": "Background task has failed" })
if not len(sys.argv) == 2:
log("Invalid number of arguments", "critical")
sys.exit(1)
baudrate = 230400
log("Initializing the serial interface with [%s] baudrate" % baudrate)
os.system("raspi-gpio set 17 op")
os.system("raspi-gpio set 16 op")
os.system("raspi-gpio set 6 op")
os.system("raspi-gpio set 5 op")
os.system("raspi-gpio set 5 dl")
os.system("raspi-gpio set 6 dl")
os.system("raspi-gpio set 17 dl")
os.system("raspi-gpio set 16 dl")
time.sleep(5)
port = serial.Serial('/dev/ttyAMA0', 115200)
port.write("AT\r\n")
time.sleep(1)
port.write("AT+IPR=%s\r\n" % baudrate)
port.close()
provider = str(sys.argv[1])
pppd = init_pppd(provider)
if not pppd == 0:
log("Unable to start PPP connection with mobile network", "critical")
sys.exit(1)
queue = Queue.Queue()
event = threading.Event()
monitoring = background(queue, event, 1, "8.8.8.8").start()
while True:
try:
time.sleep(1)
item = queue.get(block=False)
except Queue.Empty:
pass
else:
if item["type"] == "error":
log(item["message"], "critical")
sys.exit(1)
if item["type"] == "success":
log(item["message"], "notice")
daemon.notify("READY=1")
sys.exit(0)
/dev/ttyAMA0 230400 crtscts
connect '/usr/sbin/chat -v -f /etc/ppp/vodafone'
noauth
name vodafone
usepeerdns
defaultroute
replacedefaultroute
ABORT "NO CARRIER"
ABORT "ERROR"
ABORT "BUSY"
ECHO OFF
"" "ATZ"
OK "ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0"
SAY "Connecting with Vodafone...\n"
OK "AT+COPS=2"
OK "AT+CGDCONT=1,\"IP\",\"live.vodafone.com\""
OK "AT+COPS=0"
OK "ATDT*99#"
TIMEOUT 120
SAY "Connecting"
[Unit]
Description=MediaServe 2G/LTE mobile terminal service (PPPD)
PartOf=mobile.service
After=network.service
Before=network-online.target
Wants=mobile.service
[Service]
Type=oneshot
ExecStart=/bin/true
ExecReload=/bin/true
WorkingDirectory=/etc
[Install]
WantedBy=multi-user.target
[Unit]
Description=MediaServe 2G/LTE mobile terminal service (PPPD) (%i)
PartOf=pppd.service
After=network.target
Before=network-online.target
Wants=pppd.service
ReloadPropagatedFrom=pppd.service
[Service]
Type=forking
ExecStart=/usr/local/bin/mobile-pppd %i
WorkingDirectory=/etc
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.target
@ThomasLobker
Copy link
Author

MediaServe 2G/LTE mobile terminal service (PPPD)

Introduction

We have created a small script to let a Raspberry Pi connect with a Seeed LTE Cat 1 Pi Hat using pppd and keep the connection alive. The script is started as a systemd service and through monitoring it should try to keep the connection alive or restart when no connection is available.

We are using an OpenVPN session that is depending on this systemd service and starting when the connection is up. This allows us to give SSH access over the GSM network to the Raspberry Pi wherever it is plugged in. In our case we use minicom to remotely manage routers or switches over a serial port.

Installation

Using Raspbian as a basis. We have installed the LTE adapter to the GPIO of the Raspberry Pi. We are not using USB.

# Login to Raspbian
ssh pi@raspberry

# Change the root password
sudo -i
passwd

# Allow SSH login with a password, change PermitRootLogin to yes
nano /etc/ssh/sshd_config
service ssh restart

# Login with the root account directly, now you can remove the pi user
ssh root@raspberry
deluser pi

# Clean up
rm /etc/profile.d/wifi-check.sh 
rm /etc/profile.d/sshpwd.sh

# Using the instructions from https://wiki.seeedstudio.com/LTE_Cat_1_Pi_HAT/
# Step 1. Use dtoverlay=pi3-disable-bt to enable Raspberry Pi3/Pi4 UART0.
# nano /boot/config.txt
# Then add dtoverlay=pi3-disable-bt and enable_uart=1 to bottom of the config.txt. it should look like this.
# [all]
# #dtoverlay=vc4-fkms-v3d
# dtoverlay=pi3-disable-bt
# enable_uart=1
# Step 2. Disable the system serivce to use the UART0.
# systemctl disable hciuart 
# Note
# Pi3-disable-bt disables the Bluetooth device and restores UART0/ttyAMA0 to GPIOs 14 and 15. It is also necessary to disable the system service that initialises the modem so it doesn't use the UART: sudo systemctl disable hciuart.
# Step 3. Delete the console=serial0,115200 in cmdline.txt.
# nano /boot/cmdline.txt
# Then delete console=serial0,115200 from the string.
# Step 4. Reboot the Raspberry Pi3/Pi4

# Login as root, update the system and install dependencies
ssh root@raspberry
apt update
apt dist-upgrade

apt install python-all-dev python-wheel python-systemd python-pip python-gpiozero python-spidev python-colorzero python3-rpi.gpio raspi-gpio python3-gpiozero python-rpi.gpio python-gpiozero raspi-gpio cu screen openvpn ppp minicom

pip install netifaces pyserial RPi.GPIO spidev ublox-lara-r2

# Reboot
sync && reboot

# Copy the files from this gist to the Raspberry
# /lib/systemd/system/pppd.service
# /lib/systemd/system/pppd@.service
# /usr/local/bin/mobile-pppd
# /etc/ppp/vodafone/ppp-vodafone
# /etc/ppp/peers/vodafone/peers-vodafone

# Make the script executable
chmod +x /usr/local/bin/mobile-pppd

# Enable the Vodafone pppd service and check its status
systemctl enable pppd@vodafone
service pppd@vodafone status

You should now have a working pppd connection and you should be able to access the internet. Try to unhook the UTP connection and reboot the Raspberry Pi. It should get an internet connection. Happy hacking, let me know if I need to make any improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment