Jerome Nazario
← BackChapter I

ELD Trip Planner

Full Stack Developer · 2026 · HOS engine · 49 CFR §395 · 5 min read

Planning a legal truck route is more than drawing a line on a map. A property-carrying driver in the US is governed by federal Hours-of-Service rules — hard limits on how long they can drive, how long they can be on duty, and when they must rest. Most first attempts at a trip planner ignore this; they hand you a route and leave the law as an exercise for the driver.

ELD Trip Planner does the whole job. It takes a driver's trip details and produces a complete, compliant plan: an interactive route map with every required stop, FMCSA daily log sheets filled out for each day of driving, and a summary of days, driving hours, and cycle hours remaining. It's a full-stack build — a Django REST API on the back, a React (Vite) app on the front — and it's live at the link in the sidebar.

ELD Trip Planner dashboard overview

Watch the walkthrough

Three minutes, end to end: entering a trip, choosing a route, and reading the generated plan and log sheets — recorded on the live deployment.

Four inputs, one plan

The entire product runs on the four questions a dispatcher would ask:

Where is the truck right now?

The current location starts the clock. Deadhead miles to the pickup count against the same federal limits as loaded miles — the engine doesn't care whether the trailer is full.

Where does the load come aboard?

The pickup location, budgeted at a flat 1 hour on duty (not driving) — enough for paperwork and a dock that's running late.

Where does it leave?

The drop-off, with its own 1-hour on-duty block. Between these two points is everything the plan has to make legal.

How much of the cycle is already gone?

Current cycle used — how many of the 70 rolling hours this 8-day window has already burned. A driver at 60/70 gets a very different plan than one fresh off a restart.

Everything else is derived. The app geocodes the three locations, asks the Google Directions API for real routes, and runs the result through the Hours-of-Service engine. There is deliberately no database — each request is a pure input → output calculation, which keeps the system stateless, fast, and trivially safe to scale.

The Hours-of-Service engine

The heart of the app is hos.py — an engine that walks the route minute by minute and enforces every limit in 49 CFR §395 for property-carrying drivers:

  • 11-hour driving limit — no more than 11 hours behind the wheel per shift.
  • 14-hour on-duty window — once a shift starts, driving must end within 14 hours, breaks included.
  • 30-minute break — required after 8 cumulative hours of driving.
  • 10-hour reset — 8 hours sleeper berth plus 2 hours off duty before the next shift.
  • 70-hour / 8-day cycle — the rolling weekly ceiling across all on-duty time.

Driving hours come from the map provider's actual route duration for the selected route — not a flat speed assumption. The HOS engine is pure logic layered on real routing data, which is why its numbers survive contact with a real map.

Choosing a route

Before anything is planned, the driver picks the route. The app requests fastest, no-tolls, and alternate options, and scores each one with distance, drive time, the number of HOS days it will cost, and whether tolls apply. Pick a different route and the whole plan — stops, logs, summary — recalculates for it.

Route selection with scored route options

Stops are real places

A plan you can't follow is worthless, so stops are never abstract dots on a polyline. Fuel stops are placed so that no leg exceeds 1,000 miles, then snapped to the nearest real gas station via the Places API. Rest stops land wherever a federal limit forces a reset, and snap to nearby truck stops and rest areas. Click any stop and you get its name, address, scheduled time — and a street-level preview of the actual location.

Drop-off stop detail with street-level preview

Drawing the log sheets

The deliverable a DOT inspector actually cares about is the daily log. The app draws the FMCSA duty-status grid itself — Off Duty, Sleeper Berth, Driving, On Duty — as one continuous line across the 24-hour day, exactly the way a driver would draw it by hand.

FMCSA grid

Drawn as SVG, not photographed.

One sheet per day, duty-hour totals on the right, and a remarks timeline naming the place of every status change.

Drawn as SVG, not photographed.
Resolution-independent

The grid is vector SVG — it stays crisp at any zoom and prints cleanly to PDF.

Multi-day

Longer trips emit one sheet per day, with cycle hours carried across days.

Built for the cab

Drivers don't plan trips at a desk. On phones the sidebar collapses to a drawer, layouts stack, and the log sheets open in a dedicated viewer — pinch to zoom, drag to pan, double-tap to reset — so a full day's grid stays legible on a five-inch screen.

Phone views: dashboard and pinch-to-zoom log sheetPhone views: dashboard and pinch-to-zoom log sheet

Under the hood

A trip plan needs three network round-trips before the HOS engine can even start: geocoding, routing, and Places lookups. Run sequentially they'd stack into seconds of latency, so they run in parallel — a ThreadPoolExecutor collapses three calls into roughly one call's worth of waiting, and the per-stop Places lookups are parallelized the same way.

One request

Three network calls, one call's worth of waiting.

Geocoding, directions, and per-stop Places lookups fan out of the request in parallel and merge into the HOS engine — which is pure logic and adds no I/O of its own.

trip requestplaces lookupsdirectionsgeocodingHOS engineone ThreadPoolExecutorpure logic, no I/O
Stateless

No database — each request is a pure input → output calculation.

Guarded

Per-IP rate limiting (10 req/min), validated and bounded inputs, no secrets in the repo.

The code itself is commented throughout — future debugging starts from why a thing is done, not just what it does.

views.py with parallelized lookups and inline comments

Keeping it warm

The API lives on Render's free tier, which spins services down when idle — and a cold Django boot can cost the first visitor half a minute. The fix costs nothing: a cron-job.org monitor pings the /api/health/ endpoint every ten minutes, around the clock. The service never sleeps, so no real user ever pays the cold-start tax.

cron-job.org dashboard pinging the health endpoint

What comes next

The assessment scope is deliberately tight — US trips, property-carrying drivers, no adverse-conditions exceptions — and the architecture leaves room to grow: the HOS engine is pure logic, so team driving, split sleeper-berth provisions, or Canadian cycle rules would slot in without touching the routing layer.

Tech stack
Visit live site