DEV Community

MarketingPro
MarketingPro

Posted on Edited on

Designing a Construction Material Tracking System: RFID, BLE, Barcode, or LoRaWAN?

If you've built inventory software for a warehouse, you might assume a construction site is the same problem with more mud. It isn't. Materials arrive in staged deliveries, move between laydown yards and floors, get consumed by installation crews, and sit outdoors in weather. The "warehouse" has no walls, and the shelving changes weekly.

This post covers two things: how to match sensing technology to material type, and how to model the data so the system stays trustworthy once real sites start feeding it.

Start with the question, not the sensor

Before picking hardware, decide what each material needs to answer:

  • Discrete items (steel members, MEP equipment, formwork panels): where is it, and has it been delivered and installed?
  • Packaged goods (drywall, fittings, pallets): how many are on hand, and in which staging area?
  • Bulk materials (aggregate, sand, fill): how much is left?

Each question maps to a different technology.

Comparing the four common options

Technology Good for Main constraints
Barcode / QR Delivery receipts, pallet and item check-in Needs line of sight and a person scanning; accuracy depends on discipline
Passive UHF RFID Batch reads at gates, staging areas, and doorways Metal and liquids interfere with reads; fixed readers need power
BLE beacons / tags Indoor zone-level location and proximity Short range; infrastructure density affects accuracy; tags need batteries
LoRaWAN sensors Outdoor and wide-area monitoring, such as bulk level sensors Low data rate and small payloads; not suited to high-frequency streaming

None of these wins everywhere. Steel members in a yard, drywall on a fourth floor, and a gravel pile have different physical constraints, so a mixed approach is usually the realistic answer. The rest of this post assumes you'll be ingesting data from more than one source.

The core design problem: heterogeneous, noisy events

Once you combine sensor types, your backend has to reconcile inputs that behave very differently:

  • RFID readers can report the same tag dozens of times per second.
  • BLE signal strength fluctuates, so zone assignment can flicker.
  • Barcode scans are sparse but authoritative.
  • LoRaWAN uplinks are infrequent and can arrive late or out of order.

If you write all of this straight into an "inventory" table, you'll get a number nobody trusts. A more robust approach is to store raw observations as immutable events, then derive state from them.

A minimal event model

from dataclasses import dataclass
from datetime import datetime
from typing import Literal, Optional

@dataclass(frozen=True)
class MaterialEvent:
    event_id: str                 # unique, supports idempotent ingestion
    source: Literal["rfid", "ble", "barcode", "lorawan"]
    asset_id: str                 # tag ID, SKU, or sensor ID
    zone: Optional[str]           # laydown yard, floor, gate, etc.
    observed_at: datetime         # when the sensor saw it
    received_at: datetime         # when your system got it
    value: Optional[float] = None # e.g., fill level for bulk sensors
Enter fullscreen mode Exit fullscreen mode

Keeping observed_at and received_at separate matters. Delayed LoRaWAN uplinks would otherwise make your timeline lie about when something actually happened. It also means you should order and process events by observed_at, not by arrival time.

Debouncing noisy reads

For chatty sources like RFID and BLE, don't let a single read change an asset's zone. Instead, track a candidate zone and only commit to it once the asset has been observed there for a minimum dwell time:

from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Optional

@dataclass
class ZoneState:
    zone: Optional[str] = None             # committed zone
    candidate: Optional[str] = None        # zone we might be moving to
    candidate_since: Optional[datetime] = None

def update_zone(state: ZoneState, event, min_dwell=timedelta(seconds=30)) -> ZoneState:
    if event.zone is None or event.zone == state.zone:
        # Reading agrees with current state: drop any pending move.
        state.candidate = None
        state.candidate_since = None
        return state

    if event.zone != state.candidate:
        # First sighting in a new zone: start the dwell clock.
        state.candidate = event.zone
        state.candidate_since = event.observed_at
        return state

    if event.observed_at - state.candidate_since >= min_dwell:
        # Seen consistently in the new zone: commit the move.
        state.zone = event.zone
        state.candidate = None
        state.candidate_since = None
    return state
Enter fullscreen mode Exit fullscreen mode

This is deliberately simple. In production you'd tune the dwell time per technology and zone type, handle out-of-order events explicitly, and possibly require a minimum number of reads as well as elapsed time. The principle is what matters: don't let a single noisy read move an asset.

Reconcile against purchase orders and schedules

Tracking is only useful if it connects to the plan. Two reconciliations deliver most of the value:

  1. Delivery receipt vs. purchase order. Compare scanned or read items against what was ordered, and flag shortages or substitutions at the gate rather than weeks later.
  2. Staged stock vs. installation sequence. If the schedule says a floor's drywall is needed next week, the system should show whether it is actually staged nearby.

This is also where "material staging analytics" becomes concrete: it is a join between your event stream and the project schedule.

Handle failure modes deliberately

Job sites are hostile environments, so design for them:

  • Connectivity gaps. Buffer events at the edge and replay them with their original observed_at timestamps.
  • Duplicate delivery. Use event_id for idempotent ingestion, because retries will happen.
  • Sensor drift and dead batteries. Track last-seen times per device and alert on silence, not just on bad values.
  • Human override. Allow manual corrections, but record them as events with an author instead of silently editing state.

Putting it together

A workable architecture looks like this:

  1. Edge devices and gateways collect RFID, BLE, barcode, and LoRaWAN data.
  2. An ingestion layer validates, deduplicates, and stores raw events.
  3. A processing layer debounces, resolves zones, and derives inventory state.
  4. A reconciliation layer compares state with purchase orders and installation schedules.
  5. Dashboards and alerts surface exceptions rather than raw data.

For a real-world reference on how a multi-sensor approach to construction material inventory control is described, see Commcon AI, which covers RFID, BLE, barcode, and LoRaWAN sensing. Disclosure: I'm affiliated with Commcon AI.

Key takeaways

  • Choose sensing technology per material type and environment, not per project.
  • Store raw events immutably and derive inventory state from them.
  • Separate observation time from receipt time, and process by observation time.
  • Debounce noisy sources before they change state.
  • Reconcile with purchase orders and schedules, because that's where the business value is.

Over to you

If you've worked on asset tracking in a harsh environment, such as construction, logistics, or mining, what was your biggest data-quality headache: duplicate reads, connectivity gaps, or reconciling with the plan? I'd like to hear how you handled it.

Top comments (0)