This repository contains firmware for the IOGear GPSU21 print server.
| Device | Firmware file | CPU | Flash | RAM |
|---|---|---|---|---|
| IOGear GPSU21 | MPS56_90956F_9034_20191119.zip |
MediaTek MT7628AN / MT7688 (MIPS, 575β580 MHz) | 2 MB (Winbond W25Q16JVSSIQ) | 32 MB (Winbond W9725G6KB-25) |
The GPSU21 supports the following printing protocols:
| Protocol | Status |
|---|---|
| LPR/LPD | Enabled |
| IPP (port 631) | Enabled |
| Raw TCP printing | Enabled |
| SMB/Windows printing | Enabled |
| AppleTalk/PAP | Enabled |
The GPSU21 firmware includes a built-in Bonjour stack and IPP server (port 631).
It advertises itself as _ipp._tcp via mDNS automatically on your local network.
| Apple device | How to print |
|---|---|
| iOS 14+ | Goes to Settings β Wi-Fi (on same network) β AirPrint appears automatically |
| macOS 11+ (Big Sur and later) | System Preferences β Printers & Scanners β "+" β print server appears automatically |
| iOS 13 or earlier | Requires optional helper (see below) |
| macOS 10.15 (Catalina) or earlier | Requires optional helper (see below) |
- Assign a static IP address to the GPSU21 (via your router's DHCP
reservation or the print server's web interface at
http://<printer-ip>/). - Enable IPP β open the web interface, go to Setup β Services and ensure Use IPP is set to Enabled.
- Enable Bonjour β on the Setup β TCP/IP page, set Rendezvous (Bonjour)
Service to Enabled and enter a descriptive Service Name (e.g.
IOGear GPSU21). - Save & Restart the device.
The printer will now appear automatically on iOS 14+ and macOS 11+ devices that are on the same Wi-Fi network.
For older Apple devices, you can manually advertise the _universal._sub._ipp._tcp
sub-type using an Avahi service file on Linux or a Bonjour helper on Windows.
Installing software on your PC is not required for iOS 14+ / macOS 11+. The above is only needed if you must also support older Apple devices.
When printing via AirPrint the iOS/macOS client sends jobs in one of the following formats, all of which are forwarded by the print server to the attached USB printer:
application/pdfimage/jpegimage/pngimage/urf(Universal Raster Format)application/octet-stream(raw)
| File | Device | Version | Notes |
|---|---|---|---|
MPS56_90956F_9034_20191119.zip |
IOGear GPSU21 (MT7688) | 9.09.56F build 9034 (2019/11/19) | Extract the .bin before flashing |
A freeze/lock-up bug has been widely reported with the official IOGear 2017
firmware (MPS56_IOG_GPSU21_20171123.zip, build 9032, dated 2017/11/23).
Affected devices stop responding to network requests β including print jobs and
the web interface β after an extended uptime (typically hours to a day or more)
and require a power cycle to recover.
The firmware in this repository is build 9034, dated 2019/11/19 β the same 9.09.56 firmware series but two years newer than the 2017 build. Binary analysis confirms that all four freeze mechanisms are present in both builds (see the sections below). Build 9034 is still the recommended choice over build 9032 because it contains a wireless driver overhaul that reduces unrelated error paths, but it does not eliminate the core stability issues.
See COMPATIBILITY.md for the full version history.
The GPSU21 firmware (MPS56_90956F_9034_20191119.zip) can be decompressed
with the tools in this repository, and the resulting 1.57 MB eCos binary
contains embedded build-time assert strings and error messages that make
several freeze mechanisms directly observable without any disassembly.
Both builds (9032 from 2017 and 9034 from 2019) were decompressed and searched for the same indicator strings. See "Does the 2017 IOGear firmware have the same issues?" below for the complete side-by-side comparison. The descriptions that follow use build 9034 as the reference, but every finding applies equally to build 9032.
The HTTP server (httpd.c) contains the string:
http:1216, No free socket!
This is a printf-style error logged at line 1216 of the HTTP server source
when http_child β the per-connection handler β fails to obtain a free socket
from the pool. When this path is hit the server cannot accept new HTTP
connections, making the web interface unreachable.
What drains the pool:
- The firmware runs 19 concurrent service threads (HTTP, LPD, IPP, SMB, NetBIOS, Raw TCP, AppleTalk/PAP, Novell SAP/Connect/NDS, SNMP, Telnet, TFTP, mDNS, email-alert, and the main print-server loop) β all of which share a single small lwIP TCP connection pool. Each service that holds a connection open occupies a slot from the same pool that the HTTP server uses.
- Two web pages auto-refresh automatically and generate a new HTTP
connection on every cycle:
services.htmβ<meta HTTP-EQUIV="Refresh" CONTENT="5;">(every 5 s)index.htm(IPP jobs list) β<meta HTTP-EQUIV="Refresh" CONTENT="10;">(every 10 s) If a browser tab is left open on either page the device receives a new TCP connection every 5β10 seconds. If the HTTP server does not close the socket before the next refresh arrives β which can happen on any error path where the close() call is skipped β the pool shrinks by one each cycle.
Observed symptom: the web interface becomes unreachable while printing may still work (LPD/IPP connections also compete for the same pool, so eventually printing stops too).
The mDNS stack (mDNSPosix.c) contains all of the following error strings β
each represents a distinct, confirmed failure path:
| String found in binary | What it means |
|---|---|
mDNSPlatformTimeNow went backwards by %ld ticks; setting correction factor to %ld |
The system clock used by mDNS runs backwards (monotonic clock source issue); a correction factor is applied but the state machine is already inconsistent |
mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld) |
The mDNS re-entrancy lock has become corrupted β the counts that must match no longer do |
mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set |
The lock was acquired without setting timenow, which subsequent timer logic depends on |
GetFreeCacheRR ERROR! Cache already locked! |
The DNS resource-record cache is locked when it should be free β a state the code never intended |
SendResponses exceeded loop limit %d: giving up |
The mDNS send loop hit its iteration cap and gave up, meaning responses stopped being sent |
When the lock corruption path is hit, the mDNS responder thread stops
advertising the printer's _ipp._tcp and _printer._tcp Bonjour records.
From the user's perspective the device appears to have disappeared from
AirPrint and from the system's printer list β which looks exactly like a
freeze even though the hardware is still running.
The Ethernet driver (if_ra305x.c) contains:
ifra305x%d: txd%d is not free, cansend: %x
Network unstable system is going to reset.....
When every TX descriptor in the Ethernet ring is occupied and not being freed β which can happen if a service holds a reference to a network buffer without releasing it β the driver detects "network unstable" and forces a full system reboot. From the user's perspective: the device goes offline for ~20 seconds and then comes back as if it had been power-cycled. On slower networks or if the user acts quickly (e.g. pulling power during the reboot window) the behaviour is indistinguishable from a hard lock-up.
This reset path is self-healing, but it is triggered by the same underlying resource-leak that also feeds Finding 1.
The eCos kernel (thread.cxx) contains:
wakeup_count overflow
suspend_count overflow
In eCos, wakeup_count and suspend_count are narrow integer fields (16-bit
in most configurations) that track how many times a thread has been
wake-signalled or suspended. If a service thread is wake-signalled faster
than it can drain its work queue β possible if an mDNS flood or a burst of
print jobs is received β these counters can overflow. When they do, the
scheduler's invariant check fires and the thread state is undefined: the thread
may stop running without any visible crash.
Every item in the following table was confirmed present in the decompressed firmware binary by searching for the exact string it contains. None of these are speculation; they are error paths that the ZOT developers compiled into the image:
| Indicator string | Category |
|---|---|
http:1216, No free socket! |
HTTP socket pool exhaustion |
mDNSPlatformTimeNow went backwards by %ld ticks |
mDNS clock issue |
mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld) |
mDNS lock corruption |
GetFreeCacheRR ERROR! Cache already locked! |
mDNS cache deadlock |
SendResponses exceeded loop limit %d: giving up |
mDNS send loop abort |
ifra305x%d: txd%d is not free, cansend: %x |
Ethernet TX descriptor stall |
Network unstable system is going to reset..... |
Ethernet watchdog auto-reset |
wakeup_count overflow |
eCos scheduler counter overflow |
suspend_count overflow |
eCos scheduler counter overflow |
%s:%d malloc fail |
Heap allocation failure logging |
free mem negative! |
Heap accounting went negative |
Attempt to open larger file descriptor than FOPEN_MAX! |
FD table exhausted |
fd out of range |
File descriptor out of valid range |
Stack is so small size wrapped |
eCos thread stack overflow |
mod_timer: expires < 0 ! |
Timer set with negative expiry |
Because Finding 1 (socket exhaustion) is directly worsened by the sheer number of services running, disabling services you do not use reduces the number of always-open TCP connections competing for the pool, and frees the memory those threads consume.
Navigate to Setup β Services in the web interface and disable everything you do not actively use:
| Service | Port(s) | Disable if⦠|
|---|---|---|
| NetWare Bindery mode | TCP 515+ | You have no Novell NetWare server |
| NetWare NDS mode | TCP 524 | You have no Novell NDS tree |
| AppleTalk / PAP | DDP/ATP | You have no classic Mac (pre-OS X) clients |
| SMB (Windows file sharing) | TCP 139, 445 | You print via LPR, IPP, or RAW TCP instead |
| SNMP | UDP 161 | You have no SNMP monitoring system |
| Telnet | TCP 23 | You do not use the Telnet management CLI |
| Email alert | TCP 25 | You have no SMTP alert configured |
Recommended minimum for a modern home or small office:
- LPR/LPD β needed for most print queues on macOS and Linux
- IPP β needed for AirPrint and modern Windows printing
- Raw TCP (port 9100) β needed for direct TCP printing
- Bonjour/mDNS β needed for AirPrint auto-discovery
Disabling the six services listed above leaves four services running instead of fourteen, which substantially reduces both peak socket usage and steady- state memory consumption.
Also: close or navigate away from the
services.htmand IPP jobs (index.htm) pages in your browser after using them. Leaving either page open in a background tab causes a new HTTP connection every 5β10 seconds, gradually draining the socket pool.
Yes β every confirmed bug indicator is present in both builds.
The official IOGear firmware (MPS56_IOG_GPSU21_20171123.zip, build 9032)
was downloaded from the URL in issue #11
(https://cdn.shopify.com/β¦/MPS56_IOG_GPSU21_20171123.zip) and subjected to
the same string-search analysis as build 9034. The decompressed image is
1.71 MB (versus 1.57 MB for 9034, the difference being a larger Wi-Fi driver
in the older build).
The table below shows every indicator string checked:
| Bug indicator string | Build 9032 (2017 IOGear) | Build 9034 (2019 ZOT) |
|---|---|---|
http:1216, No free socket! |
β present | β present |
accept failure (HTTP accept() itself fails before pool is even checked) |
β present | β removed |
mDNSPlatformTimeNow went backwards by %ld ticks |
β present | β present |
mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld) |
β present | β present |
GetFreeCacheRR ERROR! Cache already locked! |
β present | β present |
SendResponses exceeded loop limit %d: giving up |
β present | β present |
ifra305x%d: txd%d is not free, cansend: %x |
β present | β present |
Network unstable system is going to reset..... |
β absent | β present |
wakeup_count overflow |
β present | β present |
suspend_count overflow |
β present | β present |
%s:%d malloc fail |
β present | β present |
free mem negative! |
β present | β present |
Attempt to open larger file descriptor than FOPEN_MAX! |
β present | β present |
fd out of range |
β present | β present |
Stack is so small size wrapped |
β present | β present |
mod_timer: expires < 0 ! |
β present | β present |
Auto-refresh CONTENT="5" (services page, every 5 s) |
β present | β present |
Auto-refresh CONTENT="10" (IPP jobs page, every 10 s) |
β present | β present |
Notable differences:
- Build 9032 is worse for HTTP connections: It logs
accept failurebefore reaching the socket pool check, meaning theaccept()system call itself starts failing earlier β the HTTP server in 9032 becomes unreachable sooner under load than the same situation in 9034. - Build 9034 added explicit watchdog logging (
"Network unstable system is going to reset....."). Build 9032 likely has the same hardware watchdog path in the Ethernet driver (if_ra305x.c) but does not log it to the diagnostic output β so the auto-reset happens silently. - Both builds have the same auto-refreshing pages at the same intervals (5 s and 10 s), meaning the socket-drain issue from leaving browser tabs open is equally severe on both.
Conclusion: If you are using the 2017 IOGear firmware and experiencing
freezes, switching to the 2019 build in this repository will not eliminate the
freezes, but build 9034 is still marginally better because the accept()
failure path was removed and error logging improved. All workarounds and
service-disablement recommendations that apply to build 9034 apply equally to
build 9032.
The FreeRTOS-based firmware in this repository is a clean reimplementation that avoids all of the OEM freeze mechanisms described above β the eCos kernel, the Apple mDNSCore stack, and the original Ethernet driver are all replaced.
| Finding | Status |
|---|---|
| Finding 1 β HTTP browser auto-refresh exhausting the connection pool | β Eliminated β new HTTP server does not share a fixed socket pool |
| Finding 2 β mDNS/Bonjour lock counter de-sync (Bonjour stops working) | β Eliminated β custom mDNS implementation replaces mDNSCore |
| Finding 3 β Ethernet TX-descriptor stall | β Eliminated β new PDMA Ethernet driver with proper descriptor management |
Finding 4 β eCos thread wakeup_count overflow assertion |
β Eliminated β FreeRTOS replaces the eCos kernel entirely |
Flash firmware/build/gpsu21_freertos.bin (built from source or downloaded
from the Releases page) to replace the OEM firmware.
Flashing the FreeRTOS firmware eliminates all confirmed freeze mechanisms. The workarounds below are only needed if you cannot or do not want to flash custom firmware.
If you choose not to flash the FreeRTOS firmware, the following workarounds keep the device running reliably without manual intervention.
Use a programmable smart outlet or a mechanical timer to cut power once per day (e.g. at 3:00 AM) and restore it 10β30 seconds later. No software is needed, and the device comes back up on its own after rebooting.
On a router running Linux-based firmware, add a cron entry that sends an HTTP request to the device's restart URL once a day:
# /etc/crontabs/root (OpenWrt)
0 3 * * * wget -q -O /dev/null http://<device-ip>/restart.htmThe device restarts in about 20 seconds and resumes normal operation.
On any always-on Linux or macOS machine, schedule a daily reboot request:
# Run: crontab -e
0 3 * * * curl -sf http://<device-ip>/restart.htm > /dev/nullReplace <device-ip> with the static IP address of the GPSU21.
If the device no longer responds after a firmware flash (web interface unreachable, device does not appear on the network), follow these steps in order from easiest to hardest.
Some ZOT firmware builds enter a recovery/rescue mode automatically when the application image is corrupt. After powering on, wait 60 seconds, then try:
ping 192.168.0.1
http://192.168.0.1/
Also try the device's last known IP address (the one it used before it stopped responding) in case it kept its previous network configuration.
If the device responds, it may be running a minimal recovery web server. Upload
the original unmodified firmware (MPS56_90956F_9034_20191119.bin) from that page
(extract it from MPS56_90956F_9034_20191119.zip in this repository first).
"Request timed out" / "Unable to connect"? Before concluding that Step 1 has failed, check for a subnet mismatch. The recovery image uses the fixed IP
192.168.0.1. If your home router hands out addresses on a different subnet (e.g.192.168.1.x,10.0.0.x,172.16.x.x) the router will never forward packets to192.168.0.1β so the ping times out even though the device is alive and waiting. Try the direct-connection method below first.
-
Connect the GPSU21 directly to your PC with an Ethernet cable. If your PC has no Ethernet port, use a USB-to-Ethernet adapter (any USB 2.0/3.0 to 10/100 adapter works).
-
Assign your PC's Ethernet adapter a static IP on the same subnet:
- Windows: Control Panel β Network and Sharing Center β Change adapter
settings β right-click the Ethernet adapter β Properties β
Internet Protocol Version 4 (TCP/IPv4) β Use the following IP address:
- IP address:
192.168.0.100 - Subnet mask:
255.255.255.0 - Default gateway: (leave blank) β OK
- IP address:
- macOS: System Preferences β Network β select the Ethernet adapter β
Configure IPv4: Manually β IP Address:
192.168.0.100, Subnet Mask:255.255.255.0β Apply - Linux:
sudo ip addr add 192.168.0.100/24 dev eth0(replaceeth0with your adapter name shown byip link)
- Windows: Control Panel β Network and Sharing Center β Change adapter
settings β right-click the Ethernet adapter β Properties β
Internet Protocol Version 4 (TCP/IPv4) β Use the following IP address:
-
Power on the GPSU21 and wait 60 seconds.
-
Try again:
ping 192.168.0.1Then open
http://192.168.0.1/in a browser. -
After recovery, restore your network settings:
- Windows: Return to the TCP/IPv4 properties dialog (same path as above) and select Obtain an IP address automatically β OK
- macOS: Return to Network preferences β Configure IPv4: Using DHCP β Apply
- Linux:
sudo ip addr del 192.168.0.100/24 dev eth0 && sudo dhclient eth0(or restart your network service β e.g.sudo systemctl restart NetworkManageron most distros,sudo systemctl restart systemd-networkdon systemd-networkd systems)
Still timing out? The ZOT rescue web server is only present in a small subset of firmware revisions. If there is no response after the direct connection, skip to Step 2 β U-Boot is stored in a separate flash partition and is almost always still intact, so UART recovery will work.
The GPSU21's MT7688 SoC boots U-Boot before loading the application firmware. If only the application partition is corrupt, U-Boot is still functional and can reflash the firmware over the network β no hardware programmer is required.
- USB-to-TTL UART adapter (3.3 V logic; CP2102, CH340, FTDI, or similar)
- A computer with a terminal emulator (PuTTY, minicom, screen)
- Fine-tipped soldering iron and solder (optional β see "No-solder connection" below)
Open the GPSU21 enclosure. On the PCB look for a row of unpopulated 2.54 mm through-holes or test pads labelled TX, RX, and GND (sometimes also 3V3 / VCC). These pads are typically near the MT7688 SoC.
Connect your adapter:
| Adapter pin | GPSU21 pad |
|---|---|
| GND | GND |
| RX | TX |
| TX | RX |
β οΈ Do not connect the adapter's 3.3 V / 5 V power pin to the board β power the GPSU21 from its own USB or barrel-jack supply.
Soldering is not required if you are comfortable holding the connection steady while the terminal session is active. Two common no-solder methods:
-
Press-fit jumper wires (easiest β works with 2.54 mm through-holes) Insert the male end of a Dupont/jumper wire into each through-hole and tilt it slightly so the wire presses against the barrel of the hole. The friction is enough to keep contact. Hold the board flat on a table while working so the wires stay in place. You only need to hold the interrupt key for ~1β2 seconds at boot β after that the U-Boot console is interactive and the wires can rest undisturbed.
-
Pogo-pin probes (works with both through-holes and bare test pads) Spring-loaded pogo pins (available cheaply as "IC test hook clips" or "PCB probe pins") press against pads without any soldering. Tape or clip them in place, or hold them by hand, for the duration of the session.
- Open a terminal at 57600 baud, 8N1 (no hardware flow control).
- Power on the GPSU21.
- When you see
Hit any key to stop autoboot, press a key immediately (you have ~1β2 seconds).
You should now see a MT7688 # or zot # prompt.
Set up a TFTP server on your computer (e.g. tftpd-hpa on Linux,
SolarWinds TFTP Server or Tftpd64 on Windows) and copy
MPS56_90956F_9034_20191119.bin (extracted from the ZIP in this repository)
to its root directory.
Then at the U-Boot prompt:
setenv ipaddr 192.168.0.1 # temporary IP for the GPSU21
setenv serverip 192.168.0.100 # IP address of your computer
# 0x80500000 = DRAM load address from the uImage header (load_addr field)
tftpboot 0x80500000 MPS56_90956F_9034_20191119.bin
If the download succeeds, run the ZOT upgrade command to write the firmware to flash. The exact command varies by U-Boot build; try:
run upgradefirmware
or, if that is not defined, use the manual erase-and-copy approach. First run
printenv to find the firmware partition start address (look for variables named
fwaddr, firmware_addr, or similar):
# Example only β use the actual addresses from printenv on YOUR device.
# The erase range must cover the entire firmware partition.
erase 0x9C050000 +0x600000
cp.b 0x80500000 0x9C050000 ${filesize}
Always use
printenvto find the correct addresses β the flash partition start address and partition size vary by U-Boot build. Any variable containingfirmware,upgrade, orfwaddrwill show the correct values for your device.
After writing, reboot:
reset
Use an IC programmer only if U-Boot itself is also corrupt (the device shows no UART output at all and the ping/recovery-IP steps above produce no result).
β οΈ The firmware.binfile in this repository is NOT a full flash dump. It is the application firmware partition only (~342 KB). Flashing it at offset 0 will overwrite U-Boot and make recovery harder. Before using a programmer, obtain a full flash dump from a working GPSU21 unit.
The GPSU21's SPI NOR flash chip is an 8-pin package (SOIC-8 or WSON-8) located near the MT7688 SoC. Read the part number printed on the chip. The MT7688 SoC is compatible with:
| Manufacturer | Part numbers |
|---|---|
| Macronix | MX25L1605D, MX25L3205D, MX25L6405D, MX25L12805D |
| Winbond | W25Q16DV, W25Q32BV, W25Q128BV |
| GigaDevice | GD25Q32B |
| Atmel/Adesto | AT25DF321 |
All are standard SPI NOR flash chips compatible with common programmers.
Any programmer that supports SPI NOR flash and the SOP8/WSON8 package works:
| Programmer | Notes |
|---|---|
| CH341A (MiniProgrammer) | Inexpensive; widely available; works with NeoProgrammer, AsProgrammer, flashrom |
| RT809F / RT809H | Faster; more chip support |
| XGECU T48 / T56 | Full-featured; higher cost |
For in-circuit programming (chip stays on the board) use a SOP8 test clip. For a cleaner read/write, desolder the chip and use a ZIF or SOP8 socket adapter.
| OS | Software |
|---|---|
| Windows | NeoProgrammer (free), AsProgrammer (free) |
| Linux / macOS | flashrom (sudo apt install flashrom on Debian/Ubuntu) |
- Obtain a full flash dump from another working GPSU21 (use the programmer
to read the entire chip, e.g.
flashrom -p ch341a_spi -r gpsu21_flash_backup.bin). - Identify the flash chip on the bricked board.
- Connect the programmer to the chip (in-circuit with a clip, or desoldered).
- Write the full dump:
flashrom -p ch341a_spi -w gpsu21_flash_backup.bin - Verify the write:
flashrom -p ch341a_spi -v gpsu21_flash_backup.bin - Reinstall the chip (if desoldered) and power on the device.
Before flashing any modified firmware:
# Read the current full flash contents (via flashrom + CH341A):
flashrom -p ch341a_spi -r gpsu21_flash_BACKUP_$(date +%Y%m%d).bin
# Keep this file safe β it is your recovery image.
A backup lets you restore the device to its exact previous state without needing to find a donor unit.
- Extract
MPS56_90956F_9034_20191119.binfrom the ZIP file. - Open the GPSU21 web interface at
http://<printer-ip>/. - Navigate to the firmware upgrade page.
- Upload the
.binfile (not the ZIP). - Do not power-cycle the device during the upgrade.
The tools/ directory contains Python 3 scripts that let you extract the
firmware components, inspect or modify them, and reassemble a flashable image.
The scripts work on Windows, macOS, and Linux β Python 3 is the only
requirement.
Install Python 3 from https://www.python.org/downloads/ if it is not already available.
Verify the installation:
# Windows (Command Prompt or PowerShell)
python --version
# macOS / Linux (use python3 if python points to Python 2)
python3 --version
The GPSU21 firmware (MPS56_90956F_9034_20191119.zip) originally used eCos RTOS
on a MediaTek MT7688 MIPS SoC. This repository has migrated to FreeRTOS to
eliminate the numerous eCos compilation errors. All 61 web interface files β HTML
pages, JavaScript, CSS, and JPEG/GIF images β are stored in the
gpsu21_web/ directory.
eCos 3.0 accumulated many build-breaking issues when compiled with modern GCC (GCC 14+): redefined structs, removed attributes, expansion-to-defined warnings treated as errors, and missing headers. FreeRTOS (V10.6.2) is an actively maintained, MIT-licensed RTOS that:
- Compiles cleanly with
gcc-mipsel-linux-gnu(no patches required) - Has a well-tested MIPS32r2 port
- Uses the same lwIP 2.2.0 TCP/IP stack (same socket API)
- Provides the same POSIX-like threading model the firmware relies on
# Download FreeRTOS-Kernel and lwIP (done automatically by CI)
curl -fL https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V10.6.2.tar.gz \
| tar -xz && mv FreeRTOS-Kernel-* freertos-kernel
curl -fL https://github.com/lwip-tcpip/lwip/archive/refs/tags/STABLE-2_2_0_RELEASE.tar.gz \
| tar -xz && mv lwip-* lwip
# Build
make -C firmware \
CROSS_COMPILE=mipsel-linux-gnu- \
FREERTOS_DIR=$(pwd)/freertos-kernel \
LWIP_DIR=$(pwd)/lwip
# Flash firmware/build/gpsu21_freertos.bin via the GPSU21 web interface- Edit any file in
gpsu21_web/directly on GitHub or in a local clone. - Commit and push to
main. - GitHub Actions automatically builds the FreeRTOS firmware and publishes a
new release containing
gpsu21_freertos.binβ ready to flash.