Cooking with What's in the Pantry: Iterating the Hardware of a GPS Tracker for Hotshot Trailers
Cooking with What’s in the Pantry: Iterating the Hardware of a GPS Tracker for Hotshot Trailers
Towit Houston came to us with a problem that was neither abstract nor hypothetical: their hotshot trailers kept going missing. This isn’t a theoretical security risk — cargo theft in North America (the US and Canada) hit 3,625 incidents in 2024, up 27% from the year before, with an average loss of $202,364 per incident. Texas ranks among the three hardest-hit states on the continent. And when the stolen equipment is rented, as it was for Towit, the odds of recovering it through law enforcement drop to 10-15%, far below the ~60% baseline for stolen vehicles in general. Every trailer without visibility was, literally, money that could vanish from the operation without a trace.
The ask was concrete: a device that reported the trailer’s location, running on battery because a hotshot trailer has no constant power supply, and rugged enough to survive weeks exposed to the elements with zero maintenance. None of that sounded exotic on paper. In practice, every constraint — power, space, environmental exposure — ended up forcing a different design decision, and some of those decisions took us longer than they should have to question.
First Iteration: Arduino Nano with Separate Boards
We started with what we knew. An Arduino Nano with external GPS and cellular modules wired into a generic protoboard/shield made sense: mature libraries for almost any peripheral, a short learning curve for the team, and a working prototype in days instead of weeks. It’s the same logic any developer under deadline pressure would apply — reach for the library you already have installed instead of evaluating ten alternatives before writing the first line. Cooking with what’s in the pantry, not running out to buy a new ingredient for every dish.
![]()
![]()
The limit showed up fast, and it was physical, not architectural: the binary didn’t fit. The ATmega328 on the Nano has 32KB of flash total, of which roughly 2KB goes to the bootloader, leaving about 30KB usable. GPS parsing logic, cellular connectivity handling, and power management together blew past that budget. This wasn’t a bug patient optimization could fix; it was a hard memory ceiling no refactor was going to solve — comparable to trying to cram a monolith with too many dependencies into a container with a fixed image size limit.
Second Iteration: ESP32 with External Modules (SIM7020G + GPS)
Moving to the ESP32 solved the memory problem immediately. The ESP32-WROOM-32 packs 520KB of SRAM and 4MB of flash — an order-of-magnitude jump over the Nano that took storage space off the table as an issue. We kept the separate-boards setup, pairing the ESP32 with a SIM7020G module for connectivity and a NEO-6M GPS module. This second version already covered the basics: report position, survive without external power, and hold up outdoors.
![]()
![]()
![]()
It worked. But “it works” and “it’s the right architecture” aren’t the same claim, and the problem now shifted to battery life.
Third Iteration: A Custom Board for Low-Power LDO Regulation
With the memory problem solved, the bottleneck shifted to power. With no constant current, the tracker runs entirely on battery, and that battery has to last weeks in sleep mode between position reports. That’s where off-the-shelf commercial hardware fell short.
A generic LDO regulator like the AMS1117, found on most cheap ESP32 dev boards, draws between 5 and 10mA even at rest (quiescent current). A low-power LDO like the MCP1700 brings that down to ~1.6µA — several orders of magnitude less. We designed our own 3.3V regulator adapter board to power the ESP32, replacing the stock regulation it shipped with.
![]()
![]()
![]()
![]()
![]()
![]()
We applied the same scrutiny to the NEO-6M GPS module: it draws 45mA in normal operation, but drops to 11mA in Power Save Mode. With no engine keeping the battery charged, every milliamp saved translated directly into more days of autonomy in the field. It’s the same kind of call a software architect makes when choosing between a process that’s always running in memory and one that spins up only when there’s real work to do: idle resource consumption matters just as much as consumption under load.
Fourth Iteration: A Custom PCB (ESP32 + SIM7020G Chip, Soldered)
Separate boards linked by wires and connectors in a high-vibration environment like a cargo trailer was a recipe for intermittent failures. We decided to take a qualitative leap and move to a custom electronic design at the surface-mount component level.
We designed a custom PCB with the ESP32 module, the SIM7020G chip, the low-power regulator circuit, and the power/GPS stages all soldered directly onto it. The result was a rigid block, no loose wires, a significantly smaller footprint, and much higher tolerance for an industrial environment.
![]()
![]()
![]()
![]()
![]()
![]()
The Mechanical Design: A Discreet False Bottom in FreeCAD
The physical install ran in parallel with the electronics’ evolution. We designed a false bottom in FreeCAD inside the trailer’s junction box to house the board and antenna discreetly, without interfering with the existing electrical mechanism or altering the equipment’s appearance. This wasn’t about hiding anything: it was a mechanical integration problem — figuring out where a new component lives without touching or degrading what was already working. The physical equivalent of adding a monitoring service to a system without touching its public interface.
![]()
![]()
![]()
On the software side, the backend was deliberately simple: Django running on PythonAnywhere, just enough to receive position reports and plot them on a map. There was no need to build anything more than that for the problem at hand.
The Turn: A Question from the Outside
After months of defending every hardware decision as the right one, and after going through the work of designing and hand-soldering our own PCBs, the client asked us something on a check-in call that we didn’t see coming: “Why didn’t you just use that board from the start?” — referring to the LilyGo with its integrated SIM7000G module, the commercial board we’d just migrated to.
![]()
![]()
We’d landed on that board for a very concrete reason, not out of whim: with the custom PCB already in production, we felt the operational cost of hand-assembling and soldering every unit ourselves, or of ordering small batches of populated PCBs from a manufacturer. The LilyGo packed the microcontroller, the cellular modem, the GPS, and the battery-charging circuit onto a single commercial board — fewer parts, lower cost per unit, zero hours of manual soldering, fewer field failure points, and firmware that was simpler to maintain, a consolidation not unlike replacing a handful of hand-rolled microservices with a single managed service that covers the same responsibilities. For a team that was going to have to provide remote support to dozens of devices already installed on trailers scattered across Texas, that simplicity wasn’t a cosmetic detail: it was less surface area to debug from a distance.
We didn’t have a solid technical answer. We’d walked the entire linear iteration path (Arduino → separate boards → custom regulator board → custom PCB → LilyGo) because each step was the immediate fix for the previous phase’s problem, not because we’d surveyed the full market of integrated boards from day one. Someone who hadn’t tasted every previous dish found in seconds something we’d stopped questioning for months of development.
The Principle: Iterate Fast, But Schedule the Outside Perspective
The lesson isn’t that you need to exhaustively evaluate every option before writing a line of code or soldering a component — that only delays the learning only a real prototype can give you. Starting with an Arduino and protoboard modules let us validate the concept in days, and in hindsight that was still the right call.
The problem wasn’t starting simple or building custom PCBs: it was never scheduling, ourselves, a checkpoint with eyes that didn’t carry the same stack of accumulated decisions. That outside perspective doesn’t have to show up by accident on a client check-in call — it can and should be scheduled on purpose, the same way a software team schedules a code review or an architecture check before scaling something that started life as a proof of concept.
Results in Production
At peak, we had more than 20 devices of this design running on Towit Houston’s trailers, with an overall uptime of around 75%, and cases of trailers recovered after more than a month without signal, because the prior location history was still intact in the backend.
The final architecture — LilyGo SIM7000G, a discreet false bottom in FreeCAD, and a lightweight backend — should have been the first serious candidate once the initial software was validated. Getting there the long way didn’t invalidate the result, but it did make clear the difference between iterating progressively and stepping back in time to look at the whole pantry.
If something similar has happened to you — iterating so long on the same technical decision that you lost sight of it — tell us on social media how you solved it.