14 March, 2025

Implementing Hours of Service compliance monitoring

A middleware approach: set up and configure HoS compliance monitoring using flespi calculators.

Hours of Service (HoS) regulations represent one of the most complex compliance challenges in the transportation industry. The penalties for non-compliance are severe – ranging from substantial fines (often thousands of dollars per violation) to potential criminal charges in cases of serious breaches. Fleet operators face not only financial losses but also increased insurance premiums, damaged CSA scores, and potential shutdown orders. And the complexity only grows when your operations span multiple countries, each with its own set of rules and enforcement practices. 

HoS compliance challenges

Ensuring compliance isn't just about following a single rulebook. What’s considered legal driving time in one country might violate regulations in another, and fleet operators working across borders must navigate a maze of overlapping and sometimes conflicting requirements. The challenge becomes even more intricate when you circle the globe:

  • In the United States, the FMCSA enforces different rules for property-carrying drivers (11-hour driving limit, 14-hour work window) versus passenger-carrying drivers (10-hour driving limit, 15-hour work window), with additional complications from the 34-hour restart provision and the ELD mandate.
  • The European Union operates under Regulation (EC) No 561/2006, limiting drivers to 9 hours of daily driving (extendable to 10 hours twice weekly), requiring 45-minute breaks after 4.5 hours, and mandating 11 hours of daily rest with complex weekly rest calculations.
  • Canada follows its own Commercial Vehicle Drivers Hours of Service Regulations with provincial variations, while Australia implements a three-tiered system (Standard, Basic Fatigue Management, and Advanced Fatigue Management) with different work and rest requirements.
  • Brazil's SIMTAC system and regulations from other countries add further complexity to the global compliance landscape.

Building an engine capable of accurately calculating compliance across diverse regulatory frameworks requires processing enormous amounts of historical and real-time data. Each regulation demands specific algorithms for tracking driving time, rest periods, cycle limits, and exceptions – a significant development burden for any fleet management platform. It looks like an impossible issue to solve. Or not?..


The middleware approach

Using the flespi analytics engine, we developed a one-click solution that pre-calculates HoS compliance parameters directly in flespi before the data reaches your fleet management platform. This approach offers several architectural advantages:

  • Standardization: HoS calculations happen once, in a consistent way.
  • Efficiency: Your platform receives ready-to-use compliance data.
  • Flexibility: Easily adapt to different country regulations.
  • Real-time monitoring: Compliance status updates with each new device message.

For this demonstration, we'll implement a simplified but realistic set of HoS regulations:

  1. Continuous driving time: 2 hours (allowing breaks < 5 minutes).
  2. Insufficient rest time: 30 minutes minimum in a 5-hour window.
  3. Daily rest time: 9 hours minimum in a 24-hour window.

The system will output three critical parameters that your platform can immediately use:

{
  "hos_compliance": true,       // Current compliance status
  "driving_time_remained": 3540, // Seconds of driving time left
  "rest_time_required": 0       // Seconds of rest needed before driving
}

These parameters can be automatically injected into device telemetry using webhooks, making them available alongside standard GPS data in your platform.

In the following sections, I'll walk through our implementation using flespi calculators, showing how we detect driving periods, calculate rest intervals, and determine compliance status – all this happening before the data reaches your application.

I expect that you will already familiarize yourself with calculators, understand how to configure them, assign devices, and navigate through Toolbox to access and view generated intervals.


HoS system architecture

When designing our HoS compliance system, we opted for a modular approach using three specialized calculators working in sequence. This architecture provides a clear separation of concerns, making the system easier to understand, maintain, and adapt to different regulations.

Calculator 1 (Driving) → Calculator 2 (Rest) → Calculator 3 (Compliance)→ Webhook → Device Telemetry

Once we register corresponding parameters produced by Calculator 3 into device telemetry you can further use them in your platform either by accessing directly via API or receiving in your stream endpoint as a regular device message parameter. 

Basically, flespi will perform these calculations in the background and your device will just periodically report, as a standard message parameter generated by the tracker equipment, how much driving time is available.

  • Calculator 1: Detecting drives
  • Calculator 2: Detecting rest
  • Calculator 3: Performing HoS compliance calculations based on the intervals produced by both the driving and rest detection calculators

So, let’s take a closer look at the calculator configurations. You can directly import the provided JSON into your calculator using the orange square brackets icon.


Calculator 1: Driving detection

The first calculator (#12204) identifies driving periods based on vehicle movement:

{
  "selectors": [{
    "type": "expression",
    "expression": "position.speed > 0",
    "method": "boolean",
    "max_inactive": 300,
    "max_messages_time_diff": 300,
    "merge_message_after": true,
    "merge_message_before": true,
    "merge_unknown": true,
  }],
  "update_delay": 10
}

This configuration detects driving when speed is above zero, tolerates short stops up to 5 minutes (300 seconds), and handles cases where position data might be temporarily unavailable. It also cuts the drive of no data for more than 5 minutes and is configured to include the trip's previous and next parking points for more precise time calculations.

This is a very generic driving detection algorithm, and you can bind it to a much more complex condition, like an ignition sensor or a special movement parameter generated by the device. You can also define the minimum distance and duration for the drive, but for simplicity, I focused on the most important configuration components only.

Keep in mind the update delay configuration – it specifies how many seconds the calculator will wait after the device message reception before starting the recalculation process. The more time you configure, the less frequent the calculator updates will be, but the more precise the calculation results will be. At the same time, it’s better to add 5–10 seconds for each subsequent calculator configuration because they are directly bound to the intervals produced by this one, avoiding the duplication of their update operations.


Calculator 2: Rest detection

The second calculator (#12205) is elegantly simple – it inverts the output of the first calculator:


  "selectors": [{
    "type": "calc", 
    "calc_id": 12204,
    "invert": true
  }],
  "update_delay": 20
}

This creates rest intervals that perfectly complement driving intervals, ensuring every moment is classified as either driving or resting. As mentioned earlier, the update delay for this calculator reflects the update delay of the first calculator plus 10 seconds.


Calculator 3: Compliance calculation

The third calculator (#12206) performs the actual HoS compliance calculations:

{
  "selectors": [{
    "type": "calc",
    "calc_id": 12204,
    "max_inactive": 300
  }],
  "counters": [

    // Variable counters for timestamp tracking
    {"type": "variable", "name": "driving_begin", "expression": "timestamp", 
     "validate_message": "isnull(variable(\"driving_begin\"))"},
    {"type": "variable", "name": "driving_end", "expression": "timestamp"},
    // Continuous driving (2-hour limit)
    {"type": "calc", "name": "continuous_driving", "calc_id": 12204, 
     "method": "aggregate", "allow_start_before": true, "lookup_time_before": 7200,
     "fields": [{"name": "duration", "aggregate": "summary", 
                "expression": "if(begin == variable(\"driving_begin\"), duration, 
                             end - if(begin < (variable(\"driving_end\") - 7200), 
                                     variable(\"driving_end\") - 7200, begin))"}]},

    // Recent rest (30 min in 5 hours)
    {"type": "calc", "name": "recent_rest", "calc_id": 12205, 
     "method": "aggregate", "allow_start_before": true, "lookup_time_before": 18000,
     "fields": [{"name": "duration", "aggregate": "summary", 
                "expression": "end - if(begin < (variable(\"driving_end\") - 18000),
                                      variable(\"driving_end\") - 18000, begin)"}]},

    // Daily rest (9 hours in 24 hours)
    {"type": "calc", "name": "daily_rest", "calc_id": 12205, 
     "method": "aggregate", "allow_start_before": true, "lookup_time_before": 86400,
     "fields": [{"name": "duration", "aggregate": "summary", 
                "expression": "end - if(begin < (variable(\"driving_end\") - 86400),
                                      variable(\"driving_end\") - 86400, begin)"}]},
 
    // Final compliance parameters
    {"type": "interval", "name": "continuous_driving_remained",
     "expression": "2*3600 - json(continuous_driving, \"duration\")"},
    {"type": "interval", "name": "recent_rest_remained", 
     "expression": "json(recent_rest, \"duration\") - 30*60"},
    {"type": "interval", "name": "daily_rest_remained", 
     "expression": "json(daily_rest, \"duration\") - 9*3600"},
    {"type": "interval", "name": "driving_time_remained", 
     "expression": "max(0, min(continuous_driving_remained,
                            min(recent_rest_remained, daily_rest_remained)))"},
    {"type": "interval", "name": "rest_time_required", 
     "expression": "-1*min(0, min(continuous_driving_remained,
                              min(recent_rest_remained, daily_rest_remained)))"},
    {"type": "interval", "name": "hos_compliance", 
     "expression": "driving_time_remained > 0"},
    {"type": "active", "name": "active"}
  ],
  "update_delay": 30
}

This calculator, like the rest detection calculator, also uses the driving calculator as an interval source to mimic each drive as its own interval. However, in your system, you can use a different selector, not necessarily linked to drives, for example, datetime, and analyze this separately.

First, note that the update period of this calculator is the update period of the rest detection calculator plus 10 seconds, to avoid duplicate update events.

Also, notice the 'max_inactive' parameter in the interval selector configuration, which should match the same parameter in the driving detection calculator. Together with an "active" counter, this allows us to distinguish between rides that are happening right now and rides that happened in the past. For example, webhooks that handle interval updates can filter any interval with 'active=false' to ignore any changes in the past and copy to device telemetry only information from active rides.

This calculator is the heart of our HoS compliance system. This is where the actual regulatory logic is implemented, and it's worth examining in detail to understand how we handle the complex time-based calculations.


Time window analysis

The key technical challenge in HoS compliance is analyzing driver activity across multiple time windows simultaneously. Our implementation tracks three distinct time windows:

  1. Continuous driving (2 hours): Prevents excessive driving without breaks.
  2. Recent rest (5 hours): Ensures sufficient rest within a medium timeframe.
  3. Daily rest (24 hours): Guarantees adequate daily recovery time.

For each window, we need to aggregate driving or rest durations from previous calculators. 

To access the current interval’s begin and end time in our calculations, we used counters of type "variable," whose values are accessible in calculator expressions using variable("driving_begin") and variable("driving_end") functions.

Here's how we implement the continuous driving check:

{
  "type": "calc", 
  "name": "continuous_driving", 
  "calc_id": 12204,
  "method": "aggregate", 
  "allow_start_before": true, 
  "lookup_time_before": 7200,
  "fields": [{
    "name": "duration", 
    "aggregate": "summary",
    "expression": "if(begin == variable(\"driving_begin\"), duration, 
                 end - if(begin < (variable(\"driving_end\") - 7200), 
                         variable(\"driving_end\") - 7200, begin))"
  }]
}

The 'lookup_time_before: 7200' parameter is critical here – it tells the calculator to look back 2 hours (7200 seconds) from the current driving interval to find all relevant driving intervals. The expression then calculates the total driving time within this window.

Similar counters handle the 5-hour and 24-hour windows for rest time calculations.


Compliance determination

Once we have the raw durations, we calculate the remaining driving time for each regulation:

{
  "type": "interval", 
  "name": "continuous_driving_remained", 
  "expression": "2*3600 - json(continuous_driving, \"duration\")"
},
{
  "type": "interval", 
  "name": "recent_rest_remained", 
  "expression": "json(recent_rest, \"duration\") - 30*60"
},
{
  "type": "interval", 
  "name": "daily_rest_remained", 
  "expression": "json(daily_rest, \"duration\") - 9*3600"
}

The final compliance parameters are then derived using min/max operations:

{
  "type": "interval", 
  "name": "driving_time_remained", 
  "expression": "max(0, min(continuous_driving_remained,
                        min(recent_rest_remained, daily_rest_remained)))"
},
{
  "type": "interval", 
  "name": "rest_time_required", 
  "expression": "-1*min(0, min(continuous_driving_remained, 
                          min(recent_rest_remained, daily_rest_remained)))"
},
{
  "type": "interval", 
  "name": "hos_compliance",
  "expression": "driving_time_remained > 0"
}

This approach elegantly handles all three regulations simultaneously:

  • 'driving_time_remained' shows the minimum remaining time across all regulations,
  • 'rest_time_required' indicates how much rest is needed to become compliant,
  • 'hos_compliance' provides a simple boolean status.

This calculator produces intervals in the following format:

{
  "begin": 1738651427,
  "end": 1738659283,
  "duration": 7856,
  "continuous_driving": {"duration": 7856},
  "recent_rest": {"duration": 9413},
  "daily_rest": {"duration": 59151},
  "continuous_driving_remained": -656,
  "recent_rest_remained": 7613,
  "daily_rest_remained": 26751,
  "driving_time_remained": 0,
  "rest_time_required": 656,
  "hos_compliance": false,
  "active": false
}

This interval shows:

  • The driver has reached the 2-hour continuous driving limit (continuous_driving_remained: -656).
  • This is reflected in the requirement to rest, so rest_time_required: 656 (9 hours).
  • The driver is non-compliant (hos_compliance: false).


Webhook integration

Finally, a webhook subscribes to the third calculator's output and injects the compliance parameters back into device telemetry:

{
  "triggers": [{"topic": "flespi/interval/gw/calcs/12206/devices/+/created,updated"}],
  "configuration": {
    "type": "flespi-platform",
    "method": "POST",
    "uri": "/gw/devices/%topics[6]%/messages",
    "body": "[{\"timestamp\":%payload[\"end\"]%,\n\"hos_compliance\": %payload[\"hos_compliance\"]%,\n\"driving_time_remained\": %payload[\"driving_time_remained\"]%,\n\"rest_time_required\": %payload[\"rest_time_required\"]%\n}]"
  }
}

This webhook captures each interval update from calculator #12206, extracts the compliance parameters, and registers them as a new message for the device, making them available in device telemetry.

The result is a complete HoS compliance monitoring system that operates entirely at the middleware level. Your fleet management platform receives data with compliance parameters already calculated, eliminating the need for complex processing on your end.


Adapting to different countries' regulations

The beauty of this middleware approach is its adaptability. To implement another country's regulations, you simply adjust the time windows and thresholds:

  1. For continuous driving limits: Modify 'lookup_time_before' and the subtraction value in 'continuous_driving_remained'.
  2. For rest requirements: Adjust the window sizes and minimum durations.
  3. For daily limits: Change the 24-hour window and required rest time.

For example, to implement EU regulations (4.5 hours driving, 45 minutes break), you would change:

  • lookup_time_before: 16200 (4.5 hours in seconds)
  • "expression": "4.5*3600 - json(continuous_driving, \"duration\")"
  • "expression": "json(recent_rest, \"duration\") - 45*60"

We suggest leaving driving and reset detection calculators as is and for different regulations creating a new calculator with a different configuration but similar output. Subscribe webhook to intervals from this calculator as well by supplying a comma-separated list of calculator IDs in the subscription topic.

The webhook configuration remains unchanged, as it simply forwards the calculated parameters to device telemetry.


Closing notes

When we designed flespi, we made a fundamental architectural decision to push intelligence closer to the data source. This middleware approach continues to prove its value in scenarios like HoS compliance monitoring, where complex calculations can be performed before data reaches your application.

The three-calculator system we've implemented demonstrates several core engineering principles we value at flespi:

  1. Modularity: Each calculator has a single responsibility, making the system easier to understand and maintain.
  2. Efficiency: Calculations happen once, at the middleware level, eliminating redundant processing.
  3. Flexibility: The system adapts easily to different regulatory requirements.
  4. Simplicity: Despite the complex calculations, the output is straightforward and immediately usable.

Most importantly, this approach transforms how you build fleet management applications. Instead of implementing complex HoS logic in your platform, you can focus on your core business value while flespi handles the compliance calculations.

The real-time nature of this system also provides immediate feedback to drivers and fleet managers. As a new GPS position is received, the compliance status is updated, allowing for proactive driving and rest periods management.

If you're interested in implementing this HoS compliance system for your fleet, the complete calculator configurations are available in this article. You can adapt them to your specific regulatory requirements by adjusting the time windows and thresholds as described.

As always, our team is here to help you implement and customize this solution for your specific needs. Just reach out to us through the HelpBox in your flespi panel.

Remember, the power of middleware intelligence is that it simplifies downstream applications while providing standardized, reliable calculations. This HoS compliance system is just one example of how flespi's architecture can transform complex telematics challenges into elegant solutions.