Objective: Convert a Banana Pi R3 Mini fitted with a Quectel RM520N-GL into a dependable 5G router—from hardware setup and firmware flashing to Wi-Fi band-steering, secure user & SSH configuration, targeted package deployment, software-controlled fan management, everyday optimizations, optional GPS/VPN extras, and full recovery procedures.
Warning: The guide is a work in progress and may contain mistakes. The author takes no responsibility for any loss of data, hardware damage, or connectivity issues resulting from following these instructions. Proceed at your own risk and double-check any commands or configurations before applying them.
- Banana Pi R3 Mini — 5G Router Setup Guide
- Table of Contents
- Motivation
- Introduction
- Hardware Overview
- Firmware Installation
- Initial Configuration
- Modem & Connectivity
- GPS Integration
- Fan Control
- Storage (NVMe SSD)
- Recovery & Unbrick Guide
- Useful Links
- TODO & Future Improvements
The Banana Pi R3 Mini caught my eye with its compact footprint, clean enclosure, USB-C power, and the ability to accept a 5G M.2 modem right out of the box. What began as a simple plan for reliable in-car Wi-Fi quickly snowballed into more ideas: pair a Quectel RM520N-GL with a Starlink Mini, bond both links through a VPS, and enjoy uninterrupted connectivity on any road. The modem's GNSS capability suggested yet another idea—privately logging every mile of a journey to a home-server map via wireguard.
The honeymoon ended fast. Flashing firmware, taming AT commands, smoothing kernel power-timing quirks, and persuading GPS to report coordinates were anything but plug-and-play. The stock fan kept temperatures under control but sounded like a miniature leaf-blower, begging for a redesign. Each obstacle, however, turned into a hands-on lesson in modem quirks and embedded Linux.
Now the R3 Mini feels like a pocket-sized lab. A high-capacity power bank powers it for hours of 5G connectivity, and a single USB-C cable to a laptop both feeds power to the board and opens a console for on-the-go troubleshooting.
Next up: a quieter case, smarter startup scripts, and real-world tests of bonded 5G + Starlink links. This guide captures the lessons so you can skip the headaches and dive straight into building something useful—and fun.
Let’s get serious 🧐
This guide walks you through building a compact 5G router powered by the Banana Pi R3 Mini platform using ImmortalWrt, with proven reliability in real-world automotive use. You’ll learn how to set up advanced features such as WireGuard VPN tunnels to your home network (optional), GPS logging, and continuous GPS location updates sent to a home server (also optional).
Step by step, the guide covers everything you need:
- Parts & Assembly
- Flashing firmware
- Custom fan control scripts
- Modem firmware upgrades
- Configuration via AT commands
- Startup scripts to prevent common glitches
- NVMe storage integration
- Unbrick and recovery procedures
Whether you want robust in-car connectivity or a flexible portable router, this guide equips you to achieve a stable, feature-rich 5G setup.
-
Banana Pi R3 Mini Standard Kit
- Includes: black ABS case, 30 mm fan, and three 2.4 / 5 GHz Wi-Fi antennas.
-
5G Modem: Quectel RM520N-GL-AA
- M.2 Key-B WWAN module with four I-PEX MHF® 4L (20579-001E) antenna receptacles
-
5G/4G Wideband Monopole Antennas
- Taoglas Apex TG.66.A113 hinged terminal-mount monopole covering 600 MHz–6 GHz, SMA-male connector
-
Coaxial Cables (MHF 4L → SMA Male)
- ~1.9 inch custom micro-coax assembly with Ø 1.13 mm MHF® 4L plug to SMA-male pigtail (matches RM520N-GL receptacles) _Note: MHF1-to-SMA cables will not fit—the RM520N-GL requires the MHF 4L (IPEX 20579-001E) interface.
-
NVMe SSD: WD_BLACK 1 TB SN770M M.2 2230
- PCIe Gen 4 ×4 NVMe SSD, up to 5150 MB/s sequential read, TLC 3D NAND, compact 2230 form factor. The specific model was picked for the strong performance to thermal ratio.
-
Case & M.2 Module Installation
- Install the NVMe SSD first
- Slide the WD_BLACK SN770M into the M.2 Key-M slot at a slight angle.
- Press down gently and secure with a screw.
- Install the Quectel RM520N-GL-AA modem
- Insert into the M.2 Key-B slot until it seats.
- Tighten the mounting screw to lock it in place.
-
Antenna Installation & Trade-Off
- Pre-route your cables
- Connect each SMA pigtail to its MHF® 4L receptacle before closing the case.
- Lay cables along the board’s edges.
- Omit one 2.4 GHz Wi-Fi antenna
- The case supports four external holes.
- Install all three 5G/4G antennas plus your preferred 5 GHz Wi-Fi antenna.
- Leave one 2.4 GHz hole empty.
-
Cable Routing Tips
- Tuck under the board
- Route each coax line beneath the board so there is no chance of it touching the fan blades.
- Minimize slack
- Keep loops tight to the board perimeter—enough to close the lid without pinching.
-
Avoid heatsink contact
- Hold cables clear of the main heatsink and any hot components.
-
Stock Case
- The stock case has fairly poor thermal performance, especially when using a 5G modem.
- It lacks enough antenna ports to accommodate 4x5G antennas and 4xWiFi antennas which would yield optimal performance.
-
Alternatives
- As part of this guide we developed two STL designs for 3D printing lid replacements (see hardware folder for more info):
- The first design is a simple replacement of the existing lid while allowing to mount a Noctua NF-A4x10 5V PWM fan directly inside the case. This allows for the footprint of the case to be unchanged. This lid does not significantly improve the thermals but it almost entirely eliminates the noise of the stock fan.
- The second design is meant to be deployed in more challenging thermal environments. It allows for mounting a Noctua NF-A6x25 5V PWM fan on top of the case. This increase the footprint of the case significantly, however the actual impact should be fairly minimal since the antennas take up significant space as well. Thanks to Banana Pi forum user
bprfhfor the inspiration.
- Other Resources:
- @JohnBottoms_1508911 developed a great case that will comfortably fit a Noctua A4x20 fan. Its footprint is larger than the stock case, but it should significantly improve thermals and noise. It also enables all antennas to be used.
- There is an active discussion on this Banana Pi forum thread. The discussion revolves around modifying the stock case's lid to fit a Noctua fan without increasing the size of the case significantly. The forum user
bprfhposted the STL files for 3D printing. One of the designs developed as part of this guide was influenced by this approach.
- As part of this guide we developed two STL designs for 3D printing lid replacements (see hardware folder for more info):
- This guide is tested with ImmortalWrt 24.10.2.
- OpenWRT has not been tested thoroughly when this guide was developed. However based on feedback on the Banana Pi forum OpenWRT should also work. Most parts of the guide will likely work without much modification.
- Other firmware options—such as r00ter and bananwrt—were evaluated, but caused failures during development and are not recommended.
- Download the recommended firmware files from:
- Download all files beginning with
bananapi_bpi-r3-mini.
-
Boot from NAND:
- Flip the physical switch on the board to
NAND. - Power on.
- The device should start with stock firmware (assuming it is still present).
- Flip the physical switch on the board to
-
Connect to Device:
- The board creates an open Wi-Fi network.
- Connect and access via SSH:
ssh root@192.168.1.1
- No password is required.
-
Transfer Firmware Files:
- Use
scpto copy all necessary files to/tmpon the device. - You may need to add the
-Oflag to force legacy SCP protocol:scp -O immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini* root@192.168.1.1:/tmp/
- Use
-
Write Images to eMMC:
- Enter an interactive shell and run the following sequence:
cd /tmp dd if=immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-emmc-gpt.bin of=/dev/mmcblk0 reboot # After reboot, reconnect via SSH. echo 0 > /sys/block/mmcblk0boot0/force_ro dd if=immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-emmc-preloader.bin of=/dev/mmcblk0boot0 dd if=immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-emmc-bl31-uboot.fip of=/dev/mmcblk0p3 dd if=immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-initramfs-recovery.itb of=/dev/mmcblk0p4 dd if=immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-squashfs-sysupgrade.itb of=/dev/mmcblk0p5 sync
- Now cut the power, flip the switch to EMMC and you should boot into an the ImmortalWRT environment. There will again be an open wifi network that you can connect to.
- Enter an interactive shell and run the following sequence:
-
Preparation:
- Ensure you are booted from eMMC before proceeding.
- Copy all relevant ImmortalWrt files to
/tmpon the device usingscp -O(legacy mode).scp -O immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini* root@192.168.1.1:/tmp/
-
Enable NAND Write Access:
- Load the kernel module to allow NAND writing (use with caution):
insmod mtd-rw.ko i_want_a_brick=1
- Load the kernel module to allow NAND writing (use with caution):
-
Flashing Steps:
- Run each command in sequence
mtd write /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-snand-preloader.bin /dev/mtd0 ubidetach -m 1 ubiformat /dev/mtd1 ubiattach -m 1 volsize=$(wc -c < /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-snand-bl31-uboot.fip) ubimkvol /dev/ubi0 -N fip -n 0 -s $volsize -t static ubiupdatevol /dev/ubi0_0 /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-snand-bl31-uboot.fip cd /lib/firmware/airoha cat EthMD32.dm.bin EthMD32.DSP.bin > /tmp/en8811h-fw.bin ubimkvol /dev/ubi0 -N en8811h-firmware -n 1 -s 147456 -t static ubiupdatevol /dev/ubi0_1 /tmp/en8811h-fw.bin ubimkvol /dev/ubi0 -n 2 -N ubootenv -s 126976 ubimkvol /dev/ubi0 -n 3 -N ubootenv2 -s 126976 volsize=$(wc -c < /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-initramfs-recovery.itb) ubimkvol /dev/ubi0 -n 4 -N recovery -s $volsize ubiupdatevol /dev/ubi0_4 /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-initramfs-recovery.itb volsize=$(wc -c < /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-squashfs-sysupgrade.itb) ubimkvol /dev/ubi0 -n 5 -N fit -s $volsize ubiupdatevol /dev/ubi0_5 /tmp/immortalwrt-24.10.2-mediatek-filogic-bananapi_bpi-r3-mini-squashfs-sysupgrade.itb
- Run each command in sequence
-
Final Steps:
- Power off the device.
- Flip the boot switch back to
NAND. - Power on. You should now boot into ImmortalWrt from NAND.
-
Notes:
- NAND is primarily for recovery. Regular operation should use eMMC.
- If eMMC becomes unusable, you can always boot from NAND and restore eMMC.
-
Confirm the device boots from both eMMC and NAND:
- Use the boot mode switch to test each storage option.
- The system should load ImmortalWrt in both cases.
-
Verify network access:
- Connect via Wi-Fi (default open network) and ensure you can reach the web interface or SSH.
- Connect via Ethernet (plugged into the LAN port) and ensure you have connectivity.
-
Check LuCI (Web UI):
- Access
http://192.168.1.1from your browser. - Confirm the LuCI interface loads correctly.
- Access
-
Confirm all intended firmware files are present on the system.
-
Check device status:
- Run
dmesgand review for hardware or storage errors. - Confirm that the modem and other peripherals (if installed) are recognized.
- Run
-
Change default passwords:
- Set a strong root password to secure the device.
-
For further steps, ensure you are booted from eMMC.
- Log in to LuCI (
http://192.168.1.1by default). - Go to System › Administration.
- In Router Password, enter your new password twice.
- Click Save & Apply.
Objective: Provide one SSID that automatically steers clients between 2.4 GHz and 5 GHz based on capability and signal quality.
- Both radios (
radio0– 2.4 GHz,radio1– 5 GHz) enabled. - Unique hex Mobility Domain value ready (e.g.,
4f57). - Desired SSID and passphrase decided.
- Log in to LuCI (
http://192.168.1.1by default). - Navigate to Network › Wireless.
Configure the 5 GHz AP
- Click Edit on the
radio1entry. - General Setup
- Mode: Access Point
- ESSID:
- Network: lan
- Wireless Security
- Encryption: WPA3-SAE/WPA2-PSK (mixed mode)
- Key:
- WLAN Roaming
- Enable 802.11k and 802.11v (BSS Transition).
- Enable 802.11r Fast Transition.
- Mobility Domain: <same 4-digit hex>
- FT over DS: Disabled (leave default unless roaming issues appear).
- Click Save.
Configure the 2.4 GHz AP
- Click Edit (or Add) on the
radio0entry. - Repeat the General Setup, Wireless Security, and WLAN Roaming sections above, using:
- Channel: 1 (or any non-overlapping 2.4 GHz channel).
- ESSID, Encryption, Key, and Mobility Domain: exactly match the 5 GHz settings.
- Click Save.
- Back in Network › Wireless, click Save & Apply.
- Wait for the wireless restart to complete (status turns green).
- You will need to reconnect to the new wireless network.
- Under Status › Overview › Wireless, confirm both radios broadcast the same SSID.
- Move a client device farther from the router; it should roam to 2.4 GHz automatically.
- Return close and verify it reconnects to 5 GHz.
- Keep the same DTIM Interval on both bands (default
2–3works well). - Avoid using Auto channel on 5 GHz if DFS channels are unstable in your region.
Install these packages to enable the web components, cellular modem control, VPN, GPS mapping, and basic monitoring. Dependencies resolve automatically—no need to list them.
- LuCI Apps
luci-app-3ginfo-lite– Lightweight cellular-status panel.luci-app-gpoint– Live GPS map overlay.luci-app-statistics– RRD graphs via collectd. (Optional)
- Cellular / WWAN
luci-proto-mbim– LuCI support for MBIM modems.umbim– User-space MBIM control tool.mbim-utils– Extra MBIM helpers (signal, firmware info).kmod-usb-serial-option– Kernel driver for USB LTE/5G modems.
- GPS
- Installation of the recommended GPS app will be covered in the GPS section.
- Diagnostics & Utilities
usbutils–lsusband friends for USB troubleshooting.picocom– Minimal serial console (handy for AT commands).socat- Serial console interactions from scripts.
- VPN (Optional)
luci-proto-wireguard– LuCI integration for WireGuard.wireguard-tools– CLI utilities (wg,wg-quick).
- Monitoring (Optional)
collectd-mod-thermal– Temperature sensor plugin for collectd.
- NVME Disk Support (Optipnal)
kmod-nvme- NVME Kernel driverkmod-fs-ext4- ext4 filesystemfdisk- Partitioning tool
- SMS Tools (Optional)
sms-tool- Sms toolluci-app-sms-tool-js- Luci App for managing SMS
opkg update
opkg install \
luci luci-app-3ginfo-lite luci-app-gpoint \
luci-proto-mbim umbim mbim-utils kmod-usb-serial-option \
gpsd gpsd-clients \
usbutils picocom socat
# Optional WireGuard VPN
opkg install luci-proto-wireguard wireguard-tools
# Optional statistics
opkg install luci-app-statistics collectd-mod-thermal
# Optional NVME Disk Support
opkg install kmod-nvme kmod-fs-ext4 fdisk
# Optional SMS Tools
opkg install luci-app-sms-tool-js sms-toolWhen the R3 Mini starts up, it doesn't automatically power up the modem just by supplying the standard 3.3V to the M.2 slot. The modem also needs a special "power enable" signal, controlled by a GPIO pin on the board. If this pin isn't set correctly during boot, the modem might not turn on or show up at all.
So, it’s not enough to just plug in the modem—the software must flip a specific "switch" (the WWAN enable pin) at the right time so the modem powers on properly.
With current OpenWrt / ImmortalWrt device trees the kernel leaves that GPIO floating, so the modem may power up in an undefined state.
Rebuilding ImmortalWrt with a patched DTS fixes this, but the goal here is zero custom images, so we rely on an init script.
- The modem’s internal PMIC expects the enable pin low for a few seconds after main power appears, then high.
- If raised too early (or not driven at all) the module may remain invisible on USB.
- A cold-boot timing sequence of low → 5 s → high proved ~ 99 % reliable in testing.
Create /etc/init.d/modem-power:
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
start_service() {
logger -t modem_gps "Waiting for Quectel modem..."
found=0
for i in $(seq 1 60); do
if lsusb | grep -qi quectel; then
found=1
logger -t modem_gps "Quectel modem found."
break
fi
sleep 1
done
if [ "$found" -eq 0 ]; then
logger -t modem_gps "Modem not found after 60s, rebooting."
reboot
exit 1
fi
logger -t modem_gps "Waiting 10 seconds before enabling modem."
sleep 10
logger -t modem_gps "Enabling GPS on modem via /dev/ttyUSB2."
echo -e "AT+QGPS=1\r" > /dev/ttyUSB2
logger -t modem_gps "GPS enable command sent."
}Enable and start it:
chmod +x /etc/init.d/modem-power
/etc/init.d/modem-power enable
reboot
# After reboot check that the modem came up correctly:
lsusbThe early START=01 slot makes sure GPIO control runs before USB enumeration.
Troubleshooting Notes
- Occasional no-show: Even with correct timing the modem can fail to enumerate after an unclean shutdown. The recovery strategy is covered in the GPS section. (Note: A dedicated watchdog script could be a potential improvement here)
- Fine-tuning: If you swap modems or use a custom enclosure, adjust the sleep duration—some variants respond better to 3–4 s instead of 5 s.
The Quectel RM520N-GL supports multiple USB operating modes. For use with OpenWrt or ImmortalWrt, set the modem to MBIM mode (usbnet=2).
- MBIM mode offers stable, high-speed operation with broad Linux support.
- Integrates cleanly with LuCI and network scripts.
- Among all the available modes has been tested to be be most reliable.
Connect to the modem’s serial port (e.g., /dev/ttyUSB2) using picocom or a similar tool:
picocom -b 115200 /dev/ttyUSB2At the terminal prompt, send:
AT+QCFG="usbnet"
- Typical response:
2= MBIM (Recommended)0= ECM1= QMI3= RNDIS
To set MBIM mode, enter:
AT+QCFG="usbnet",2
- You should see an
OKresponse. - At this point it’s best to reboot the device.
picocom -b 115200 /dev/ttyUSB2
# Then type:
AT+QCFG="usbnet" # Check current mode
AT+QCFG="usbnet",2 # Switch to MBIM mode- Setting persists across reboots.
- After switching to MBIM, the modem should show up as an MBIM network device (e.g.,
wwan0) inifconfigorip a.
To bring up your Quectel RM520N-GL modem for cellular data, configure the WWAN interface using the LuCI web interface. This ensures a persistent, easy-to-manage connection.
-
Open LuCI:
- In your browser, go to
http://192.168.1.1and log in.
- In your browser, go to
-
Navigate to Network Interfaces:
- Go to Network › Interfaces.
-
Add a New Interface:
- Click Add new interface...
- Name:
wwan - Protocol: MBIM (Mobile Broadband Interface Model)
-
Device Selection:
- For Device, choose your MBIM modem device (usually
wwan0).
- For Device, choose your MBIM modem device (usually
-
APN Configuration:
- Enter your cellular provider’s APN in the APN field.
- Example:
internet,broadband, or your SIM’s documented APN.
- Example:
- Leave username/password empty unless required by your provider.
- Enter your cellular provider’s APN in the APN field.
-
Authentication (if needed):
- Set the PAP/CHAP method if your provider uses authentication (most don’t for data-only SIMs).
-
PIN (if required):
- If your SIM card is PIN-locked, enter the PIN under PIN.
- Leave blank if PIN is not set or has already been disabled.
-
Advanced Options:
- You may leave advanced options at defaults for most use cases.
- If dual-SIM or custom MTU is needed, adjust under Advanced Settings.
-
Firewall Settings:
- Assign the interface to the wan firewall zone (default is correct for internet access).
-
Save & Apply:
- Click Save then Save & Apply.
- Wait for the configuration to reload.
- Return to Network › Interfaces.
- The
wwaninterface should show a valid IPv4 address if the SIM and APN are correct. - If not, check:
- SIM card orientation and data plan.
- APN spelling.
- Modem mode (should be MBIM).
- You can view live modem status under Modem > Information about 3G/4G/5G connection section in LUCI.
- For SIM PIN issues, try a different SIM or use a phone to disable the PIN.
We made extensive attempts to make SIM hotswap work reliably. There are mainly three issues:
- The BPI R3 Mini doesn't have the SIM_DET pin wired, so hardware detection is not possible.
- The "FULL_CARD_POWER_OFF#" pin is not wired, so only a warm reboot of the modem is possible.
- After a few warm reboots the modems AT port becomes unreliable (this seems to be a firmware issue which may be resolved in the future)
Due to these limitations the only reliable way we found was to reboot the entire device when a SIM swap is detected (not really a hotswap anymore). This mayb not be desirable in all situations, so use with caution.
From testing you would need to remove the SIM for at least 30 seconds before the reboot gets triggerd.
IMPORTANT: If there is no SIM card in the modem or the modem is otherwise in a strange state, this could leave your device in a reboot loop. We don't take responsibility for any issues resulting from the use of this script.
It can certainly be argued that manually rebooting the device upon SIM swap is the better way to go, however we wanted to keep the script for completenes.
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
MODEM_PORT="/dev/ttyUSB2"
INTERVAL=10
RESET_PAUSE=30
start_service() {
procd_open_instance
procd_set_param command /bin/sh -c '
while true; do
if [ ! -e '"$MODEM_PORT"' ]; then
sleep 2
continue
fi
if echo -e "AT+CPIN?\r" | socat -T2 - '"$MODEM_PORT"',raw,echo=0,crnl | grep -q "+CME ERROR: 13"; then
logger -t sim-watchdog "SIM failure detected – rebooting device"
reboot
fi
sleep '"$INTERVAL"'
done
'
procd_close_instance
}TODO (Currently handled by GPS integration)
The Quectel RM520N-GL includes built-in GNSS (GPS/GLONASS/BeiDou/Galileo) functionality. To enable GPS features for mapping and location-based services, activate GNSS output over USB using AT commands. Especially in MBIM mode getting GNSS to work requires some tweaks.
-
Connect to the Modem’s AT Command Port
- Use a serial terminal such as
picocomto open the correct port (typically/dev/ttyUSB2).
picocom -b 115200 /dev/ttyUSB2
- Use a serial terminal such as
-
Enable GPS
- Issue the following AT command to turn on the GNSS receiver:
AT+QGPS=1- This command is needed after every reboot to start the GNSS engine. (See below for the GPS startup script)
-
Configure NMEA Output Over USB
- The next commands are usually persistent, but can be re-sent to ensure correct setup:
AT+QGPSCFG="outport","usbnmea" AT+QGPSCFG="gpsnmeatype",31- This sends NMEA GPS data over the modem’s USB port, enabling tools like
gpsdto read real-time positioning data.
-
Check GPS Status
- To check for a current GPS fix or view raw data (Note: It can take 5-10 minutes to establish an initial GPS fix):
AT+QGPSLOC=0 AT+QGPSGNMEA="RMC"AT+QGPSLOC=0returns the current position if available.AT+QGPSGNMEA="RMC"outputs the NMEA Recommended Minimum data sentence.
- The modem must have a clear view of the sky (or antenna properly routed) for GNSS lock.
- If no fix is reported, check antenna placement and wait several minutes for initial lock (cold start).
- Most settings persist across reboots, except for the actual GNSS engine activation (
AT+QGPS=1), which must be sent every time the system starts.
The following /etc/init.d/modem-gps script will make sure that GPS is enabled after the modem is fully detected. At the moment the script will also reboot the device in case the modem does not come up. This is not really related to GPS, but rather to the GPIO timing issues explained in the Modem & Connectivity section. It would be cleaner to have a dedicated watchdog script for this.
Important: This script could get your device stuck in a reboot loop simply disable/delete it if that happens. You need to make first sure your modem reliably gets detected on every boot as described in the Modem & Connectivity section.
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
start_service() {
logger -t modem_gps "Waiting for Quectel modem..."
found=0
for i in $(seq 1 60); do
if lsusb | grep -qi quectel; then
found=1
logger -t modem_gps "Quectel modem found."
break
fi
sleep 1
done
if [ "$found" -eq 0 ]; then
logger -t modem_gps "Modem not found after 60s, rebooting."
reboot
exit 1
fi
logger -t modem_gps "Waiting 10 seconds before enabling modem."
sleep 10
logger -t modem_gps "Enabling GPS on modem via /dev/ttyUSB2."
echo -e "AT+QGPS=1\r" > /dev/ttyUSB2
logger -t modem_gps "GPS enable command sent."
}/etc/init.d/modem-gps enable
/etc/init.d/modem-gps start
# In case you get stuck in a reboot loop:
/etc/init.d/modem-gps disableThis is a great LUCI app to monitor GPS status and to even send status updates to a remote server such as traccar via a wireguard (or similar) VPN connection. This is an ongoing area of improvement for this guide, however here are a few helpful links:
- luci-app-gpoint - Needs to be downloaded from github and installed manually
- Configuration:
- Parser mode: gpoint
- Timezone: (Your timezone)
- Modem: (Should be auto selected)
- Modem port:
/dev/ttyUSB1 - Enable Server: (Needed for traccar integration, optional)
- Geohash Filter (opinionated basic settings):
- Enabled
- Jump: 3
- Area: 7
- Speed: 2
- Kalman Filter:
- Enabled
- Noise: 1.0
- Configuration:
- Setup traccar in docker
Your Banana Pi R3 Mini has no on-board fan controller, so thermal management relies on software.
The combination of a shell script (fancontrol.sh) and a procd init service keeps the SoC within safe limits while minimising noise.
- Sensor Source – Reads the current SoC temperature from
/sys/class/thermal/thermal_zone0/temp(raw millidegrees). - Four Fan Levels – OFF, Low, Medium, High—implemented by writing to kernel trip points (
trip_point_2–4). - Thresholds – Defaults are 40 °C, 47 °C, 54 °C, with an emergency cut-off at 80 °C.
- Tweak
LOW_TEMP,MEDIUM_TEMP,HIGH_TEMP, andVERY_HIGH_TEMPto for a tradeoff between noise and device temperature - A
HYSTERESISvalue (4 °C) prevents rapid toggling near the boundaries.
- Tweak
- Logic Flow
- Determine the current “level” based on thresholds plus hysteresis.
- If the level changes, update the trip points so the kernel switches fan GPIO speeds accordingly.
- Loop every two seconds.
Because the script manipulates hardware trip points instead of driving PWM directly, it plays nicely with the kernel’s thermal framework and avoids busy-looping on GPIO writes.
The script has been adapted from user cwxiaos on the Banana Pi forum
Improvements to the script:
- Use higher temperature out of modem and CPU. Fallback to CPU if getting modem temperature fails.
- Adjusted trip points to work well with enhanced cases mentioned in the Hardware Overview section.
#!/bin/ash
# Temperature sensor path
TEMP_PATH="/sys/class/thermal/thermal_zone0/temp"
# Trip Points for Fan Speed Control
TRIP_0="/sys/class/thermal/thermal_zone0/trip_point_0_temp"
TRIP_1="/sys/class/thermal/thermal_zone0/trip_point_1_temp"
TRIP_2="/sys/class/thermal/thermal_zone0/trip_point_2_temp"
TRIP_3="/sys/class/thermal/thermal_zone0/trip_point_3_temp"
TRIP_4="/sys/class/thermal/thermal_zone0/trip_point_4_temp"
# Temperature thresholds (in millidegrees Celsius)
LOW_TEMP=37000
MEDIUM_TEMP=43000
HIGH_TEMP=59000
VERY_HIGH_TEMP=70000
CRITICAL_TEMP=80000
HYSTERESIS=4000
# Previous fan level (-1 means uninitialized)
PREV_LEVEL=-1
set_trip_points() {
level=$1
case "$level" in
0) # Fan OFF
echo "$VERY_HIGH_TEMP" > "$TRIP_4"
echo "$VERY_HIGH_TEMP" > "$TRIP_3"
echo "$VERY_HIGH_TEMP" > "$TRIP_2"
;;
1) # Low speed
echo 0 > "$TRIP_4"
echo "$VERY_HIGH_TEMP" > "$TRIP_3"
echo "$VERY_HIGH_TEMP" > "$TRIP_2"
;;
2) # Medium speed
echo 0 > "$TRIP_4"
echo 0 > "$TRIP_3"
echo "$VERY_HIGH_TEMP" > "$TRIP_2"
;;
3) # High speed
echo 0 > "$TRIP_4"
echo 0 > "$TRIP_3"
echo 0 > "$TRIP_2"
;;
esac
PREV_LEVEL=$level
}
# Set emergency shutdown temperature
echo "$VERY_HIGH_TEMP" > "$TRIP_1"
echo "$CRITICAL_TEMP" > "$TRIP_0"
while true; do
# Read CPU temperature (convert from millidegrees to degrees)
if [ -f "$TEMP_PATH" ]; then
CPU_TEMP=$(cat "$TEMP_PATH")
CPU_TEMP=$((CPU_TEMP / 1000))
else
echo "Error: Unable to read temperature from $TEMP_PATH"
exit 1
fi
# Read max modem temperature
MODEM_TEMP=$(echo "AT+QTEMP" | socat - /dev/ttyUSB3,crnl,echo=0 | grep +QTEMP | awk -F',' '{gsub(/"/,"",$2); print $2}' | sort -nr | head -1)
# If modem temp not available, default to CPU temp
[ -z "$MODEM_TEMP" ] && MODEM_TEMP=$CPU_TEMP
# Choose the higher temperature
if [ "$CPU_TEMP" -gt "$MODEM_TEMP" ]; then
TEMP=$CPU_TEMP
else
TEMP=$MODEM_TEMP
fi
TEMP=$((TEMP * 1000)) # Convert to millidegrees
# Determine fan speed level based on hysteresis control
case "$PREV_LEVEL" in
0)
[ "$TEMP" -ge "$LOW_TEMP" ] && LEVEL=1 || LEVEL=0
;;
1)
if [ "$TEMP" -ge "$MEDIUM_TEMP" ]; then
LEVEL=2
elif [ "$TEMP" -le "$((LOW_TEMP - HYSTERESIS))" ]; then
LEVEL=0
else
LEVEL=1
fi
;;
2)
if [ "$TEMP" -ge "$HIGH_TEMP" ]; then
LEVEL=3
elif [ "$TEMP" -le "$((MEDIUM_TEMP - HYSTERESIS))" ]; then
LEVEL=1
else
LEVEL=2
fi
;;
3)
[ "$TEMP" -le "$((HIGH_TEMP - HYSTERESIS))" ] && LEVEL=2 || LEVEL=3
;;
*)
LEVEL=0 # Default to fan OFF
;;
esac
# Update fan speed only if the level changes
[ "$LEVEL" -ne "$PREV_LEVEL" ] && set_trip_points "$LEVEL"
echo "CPU_TEMP=${CPU_TEMP}C MODEM_TEMP=${MODEM_TEMP}C TEMP_USED=${TEMP} FAN_LEVEL=${LEVEL}"
sleep 2
doneImportant: Make sure to test the script and monitor its output to make sure you get the expected temperatures and fan levels. The script is not great at error handling. Use with caution and proper testing, to avoid overheating. The author takes no responsibility for damage caused to any devices.
The companion init script (/etc/init.d/fancontrol) wraps fancontrol.sh in a supervised procd instance.
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
start_service() {
procd_open_instance
procd_set_param command /usr/bin/fancontrol.sh
procd_set_param respawn
procd_close_instance
}- Startup Order –
START=99ensures the service runs late, after the kernel exposes thermal zones. - Respawn – procd restarts the script automatically if it crashes.
- Enable at Boot
chmod +x /usr/bin/fancontrol.sh chmod +x /etc/init.d/fancontrol /etc/init.d/fancontrol enable /etc/init.d/fancontrol start
During development of this guide the following SSD was used: WD_BLACK 1 TB SN770M M.2 2230 - Reviews suggested good thermal performance which was critical for this device.
Assuming you are using a brand new SSD the following setps will parition and format the SSD with the ext4 filesystem. IMPORTANT: Any data on the SSD will be wiped.
fdisk /dev/nvme0n1 # Verify that this is your NVME drive
# Inside the fdisk tool
g
n # (enter for all defaults)
# Answer Yes to "Do you want to remove the signature? [Y]es/[N]o: Y" if asked
w
mkfs.ext4 /dev/nvme0n1p1
# Run "umount /dev/nvme0n1p1" if you get an error about the filesystem already being mounted.After a reboot the SSD should be automatically mounted in /mnt/nvme0n1p1.
Not yet documented. Coming soon.
- Dedicated watchdog script to try to recover from failed modem detection
- GPS/Wireguard/traccar setup
- Storage (NVMe SSD)
- Recovery/Unbrick process
- sysupgrade.conf documentation for config to survive upgrades