Route /doc

Technical Documentation - Actual Runtime Implementation

Source scope: ESP32 firmware, Laravel routes/controllers, MQTT worker, statistics service, migrations, and dashboard view.

Active Language

English

Analysis Window

50 rows/protocol

Chart Window

Unlimited

Navigation Coverage

13 technical sections

System Flow in Simple Language

Imagine two couriers delivering messages from ESP32 to the server: HTTP courier and MQTT courier. Both carry similar message content, then the server stores each delivery, calculates delivery quality, and shows the comparison on the dashboard.

Technically, firmware in ESP32_Firmware/src/main.cpp sends periodic payloads to POST /api/http-data (HTTP) and broker topic iot/esp32/suhu (MQTT). HTTP path is handled by ApiController::storeHttp, MQTT path is handled by worker mqtt_worker.php, then both are stored idempotently in eksperimens.

1. ESP32

Reads sensor and prepares telemetry packet.

2. HTTP + MQTT

Packet is sent through two different channels.

3. Database

Server stores deliveries and calculates metrics.

4. Dashboard

Comparison results are shown in realtime for analysis.

How Data Flows in This System

Simple view: data moves from device (ESP32), enters server, is stored in database, gets statistical processing, and is finally shown on dashboard.

Technical view: firmware sendHTTP() and sendMQTT() push payloads; HTTP is accepted by ApiController::storeHttp, MQTT is accepted by callback in mqtt_worker.php; data is written via Eksperimen::updateOrCreate; dashboard calls StatisticsService from DashboardController::index.

[ESP32 Sensor Read]
        |
        +--> HTTP -> /api/http-data -> ApiController -> eksperimens
        |
        +--> MQTT -> Broker -> mqtt_worker.php -> eksperimens
                                              |
                                              v
                                    StatisticsService (summary, reliability, t-test)
                                              |
                                              v
                                    DashboardController -> dashboard.blade.php
Step Simple Explanation Technical Explanation
1 ESP32 reads temperature and humidity. Firmware stores values in lastTemperature/lastHumidity via captureSensorSnapshot().
2 Conceptually similar data is sent through two channels. sendHTTP() posts to API endpoint, sendMQTT() publishes to broker topic.
3 Server checks whether payload is valid. HTTP uses Laravel validator, MQTT worker uses manual field/range validation.
4 Data is stored without duplicating the same packet. Idempotent key: (device_id, protokol, packet_seq) using updateOrCreate + DB unique index.
5 System computes comparative statistics. StatisticsService computes summary, reliability/PDR, and independent sample T-test.
6 Results are shown on dashboard for human interpretation. Dashboard renders metric cards, quality panel, diagnostics, latency/power charts, and T-test panels.

System Overview

For general readers: this page explains who sends the data, where it goes, what is calculated, and how the results are read on dashboard.

For technical readers: the system compares MQTT vs HTTP telemetry from ESP32, stores both in MySQL, then runs comparative statistics (summary, reliability, t-test) before rendering charts and analysis panels.

Firmware (ESP32)

File: ESP32_Firmware/src/main.cpp. Sends HTTP and MQTT periodically (default both 10 seconds) with complete telemetry fields and packet sequence.

HTTP Ingress

Route POST /api/http-data -> ApiController::storeHttp, with middleware throttle:http-data and ingest.key.

MQTT Ingress

Not handled by a Laravel controller. Incoming MQTT is processed by standalone worker mqtt_worker.php then upserted into eksperimens.

Dashboard

Route GET / -> DashboardController::index, statistics are computed by StatisticsService, frontend is in resources/views/dashboard.blade.php.

Actual Architecture (Code-Based)

Simple view: there are two delivery channels from ESP32, then both channels meet in database for joint analysis.

Technical view: the diagram below follows runtime paths that actually exist in this repository with no assumptions beyond code.

ESP32 main.cpp HTTP Path POST /api/http-data MQTT Path topic iot/esp32/suhu Backend Processing ApiController::storeHttp throttle + ingest.key mqtt_worker.php subscribe + validate Latency Calculation abs(server_utc - timestamp_esp) MySQL eksperimens updateOrCreate + unique key StatisticsService summary + reliability + tTest() Dashboard DashboardController + Blade

This inline SVG follows actual code paths: latency is calculated in backend ingest (HTTP controller + MQTT worker), while T-Test runs in StatisticsService before dashboard rendering.

ESP32 Firmware (main.cpp)
   |-- HTTP JSON -> POST /api/http-data
   |                 -> middleware(throttle:http-data, ingest.key)
   |                 -> ApiController::storeHttp()
   |                 -> Eksperimen::updateOrCreate(device_id, 'HTTP', packet_seq)
   |
   |-- MQTT JSON -> topic iot/esp32/suhu
                     -> Mosquitto Broker (202.154.58.51:1883)
                     -> mqtt_worker.php subscribe()
                     -> Eksperimen::updateOrCreate(device_id, 'MQTT', packet_seq)

Dashboard (/)
   -> DashboardController::index()
   -> StatisticsService (summary, reliability, t-test)
   -> dashboard.blade.php (cards, warnings, diagnostics, charts, t-test)

Optional simulation branch
   /simulation endpoints -> ApplicationSimulationService
   -> simulated_eksperimens table
   -> /?source=simulation dashboard source

Visual Timeline: HTTP Request/Response vs MQTT Publish/Subscribe

Simple view: both protocols start from sensor timestamp, but the travel pattern differs. HTTP waits for request response, while MQTT publishes and is then processed by subscriber worker.

Technical view: this timeline maps timestamp_esp, transmit phase, server receive moment, and where latency_ms is computed in backend ingest. HTTP path maps to sendHTTP() + ApiController::storeHttp; MQTT path maps to sendMQTT() + mqtt_worker.php.

Time direction -> HTTP MQTT timestamp_esp T0 HTTP transmit ApiController receive timestamp_server latency_ms computed HTTP response latency_ms = abs(server_utc - timestamp_esp) timestamp_esp T0 MQTT publish Broker route mqtt_worker receive timestamp_server latency_ms computed Publish/subscribe is asynchronous; there is no HTTP-style response body.

Laravel Route Map (Current)

Method Path Handler Purpose
GET/DashboardController@indexMain dashboard
POST/api/http-dataApiController@storeHttpHTTP ingest from ESP32
GET/simulationSimulationController@indexSimulation UI
POST/simulation/start|stop|tick|resetSimulationControllerSimulation control
GET/POST/reset-dataDashboardControllerReset real telemetry
GET/admin/loginAdminConfigController@loginFormAdmin login
GET/POST/PATCH/DELETE/admin/config/*AdminConfigControllerRuntime + firmware provisioning
GET/docview('doc')This technical documentation page

HTTP Controller and MQTT Worker

Simple view: HTTP gate is handled by Laravel, while MQTT gate is handled by a dedicated worker. Both validate data before storing it.

Technical view: API route uses throttle:http-data + ingest.key middleware. MQTT runs as separate subscriber process with manual validation, then both perform idempotent upsert into the same table.

HTTP path (ApiController::storeHttp)

  • Validates all required telemetry fields with range constraints.
  • Computes latency_ms from server UTC time minus timestamp_esp.
  • Upsert key: (device_id, protokol='HTTP', packet_seq).
  • Stores daya into DB column daya_mw.

MQTT path (mqtt_worker.php)

  • Subscribes telemetry topic: iot/esp32/suhu.
  • Performs required-field/type/range checks manually in worker process.
  • Computes latency_ms with the same formula as HTTP path.
  • Upsert key: (device_id, protokol='MQTT', packet_seq).
  • Also subscribes debug topic iot/esp32/debug and updates storage/app/esp32_debug_heartbeat.json.

Important note: this repository has no dedicated Laravel MQTT ingest controller. MQTT ingestion is handled by standalone process mqtt_worker.php.

JSON Payload Structure Used in Runtime

Simple view: payload is "package content" carried from ESP32 to server. It contains primary sensor data and additional fields for network-quality diagnostics.

Technical view: firmware payload builder (fillProtocolPayload()) always emits required telemetry fields plus diagnostics such as RSSI, TX duration, payload bytes, and send success/fail counters.

{
  "device_id": 1,
  "protokol": "HTTP",
  "packet_seq": 171234,
  "suhu": 28.125,
  "kelembapan": 60.45,
  "timestamp_esp": 1772021517,
  "daya": 812.34,
  "rssi_dbm": -60,
  "tx_duration_ms": 45.2,
  "payload_bytes": 208,
  "uptime_s": 7200,
  "free_heap_bytes": 265000,
  "sensor_age_ms": 980,
  "sensor_read_seq": 444,
  "send_tick_ms": 9876543,
  "sensor_reads": 445,
  "http_success_count": 320,
  "http_fail_count": 4,
  "mqtt_success_count": 318,
  "mqtt_fail_count": 6
}

Required fields accepted by backend

device_id, suhu, kelembapan, timestamp_esp, daya, packet_seq, rssi_dbm, tx_duration_ms, payload_bytes, uptime_s, free_heap_bytes, sensor_age_ms, sensor_read_seq, send_tick_ms

Extra fields emitted by firmware

protokol, sensor_reads, http_success_count, http_fail_count, mqtt_success_count, mqtt_fail_count

Step-by-Step Explanation for Each Metric

This section is intentionally two-layered: first paragraph for non-technical understanding, second paragraph for technical accuracy based on code.

Latency

Simple view: latency is how long it takes data to travel from the device to the server. Smaller latency means faster delivery.

Technical view: both HTTP and MQTT compute latency_ms = abs(server_utc - timestamp_esp) using Carbon::floatDiffInMilliseconds() in ApiController and mqtt_worker.php, then store it in eksperimens.latency_ms.

Power Consumption

Simple view: the system estimates "energy cost" each time ESP32 sends data. This is not a direct electrical meter; it is an estimate based on transmission conditions.

Technical view: firmware computes powerMw = voltage * totalCurrentMa in estimateProtocolPower() using RSSI, payload size, TX duration, protocol overhead, fail ratio, temperature, and humidity. It is sent as daya then stored in backend as daya_mw.

Reliability / Packet Delivery Ratio

Simple view: reliability shows how consistently data arrives without loss. If many packet numbers are missing, reliability decreases.

Technical view: backend computes sequence reliability from packet_seq per device (received / expected * 100) with reboot/jump segmentation rules. Final score combines sequence, field completeness, and transmission health via combineReliability().

Independent Sample T-Test

Simple view: T-Test helps answer whether MQTT and HTTP differences are likely real, not just random short-term fluctuations.

Technical view: StatisticsService::tTest() calculates pooled variance, standard error, and t_value for latency_ms and daya_mw. Significance is decided with fixed threshold |t| > 1.96, while p_value is shown as an approximate value.

Latency Calculation Mechanism

Both HTTP controller and MQTT worker use the same latency formula.

// app/Http/Controllers/ApiController.php and mqtt_worker.php
$timestampServer = Carbon::now('UTC');
$timestampEsp = Carbon::createFromTimestampUTC((int) $validated['timestamp_esp']);
$latencyMs = abs((float) $timestampServer->floatDiffInMilliseconds($timestampEsp));
  • Latency value is stored in eksperimens.latency_ms.
  • Absolute value (abs) is used, so negative clock drift becomes positive magnitude.
  • Model accessor in Eksperimen also returns absolute latency_ms.

Power Calculation Mechanism (daya_mw)

Power is estimated inside firmware (estimateProtocolPower()) and sent as field daya. Backend stores it as daya_mw.

// ESP32_Firmware/src/main.cpp
float totalCurrentMa = wifiBaseCurrentMa
    + sensorCurrentMa
    + cpuCurrentMa
    + signalPenalty
    + payloadCurrent
    + txDurationCurrent
    + protocolOverhead
    + protocolReliabilityPenalty
    + thermalCurrent
    + humidityCurrent
    + retryPenalty;
float powerMw = voltage * totalCurrentMa;
return max(0.0f, powerMw);
  • HTTP and MQTT have different protocol overhead values (HTTP is higher).
  • Penalty increases as fail ratio rises (httpSendFail/mqttSendFail).
  • Transmission duration, payload size, RSSI, and environment deltas affect the estimate.
  • Formula is deterministic (current firmware path uses no random baseline).

Packet Delivery Ratio (Backend Reliability)

The backend does not persist a separate field named PDR, but sequence reliability is calculated as received / expected * 100 and used in total reliability score.

// app/Services/StatisticsService.php
$missing = max(0, $expected - $received);
$rate = ($received / $expected) * 100;

// combineReliability() when packet_seq exists
return ($sequenceRate * 0.55) + ($completenessRate * 0.25) + ($transmissionRate * 0.20);

Sequence continuity rules

  • Gap handling threshold: DASHBOARD_SEQUENCE_MAX_GAP_FOR_LOSS (current 120).
  • Reboot detection by uptime drop: DASHBOARD_SEQUENCE_REBOOT_UPTIME_DROP_SECONDS (current 30s).
  • Large jump, negative jump, or reboot starts a new segment (not counted as one continuous loss chain).

Final reliability composition

  • Window size: latest 300 rows per protocol.
  • When packet sequence is missing: 0.6 * completeness + 0.4 * transmission_health.
  • Transmission health uses latency + tx duration + payload presence.

Independent Sample T-Test

Implemented in StatisticsService::tTest() for latency_ms and daya_mw.

// app/Services/StatisticsService.php (core path)
$pooledVariance = (($n1 - 1) * $var1 + ($n2 - 1) * $var2) / $df;
$standardError = sqrt(max(0, $pooledVariance) * ((1 / $n1) + (1 / $n2)));
$tValue = ($mean1 - $mean2) / $standardError;
$criticalValue = 1.96;
$isSignificant = abs($tValue) > $criticalValue;
  • Sample variance uses denominator (n-1).
  • Minimum requirement: each group must have at least 2 rows.
  • Edge case standardError == 0 is handled explicitly (constant groups).
  • p_value uses approximation function (normal CDF for large df, coarse bins for small df).
  • Dashboard renders separate T-test panels for latency and power.

Real Examples from One Data Cycle

This section makes research numbers easier to imagine. Example numbers below follow the same formula pattern used in runtime code.

Example 1: One full data cycle

Simple view: ESP32 reads the sensor, sends packet to HTTP and MQTT nearly at the same time, then server stores both.

Technical view: firmware builds payload using fillProtocolPayload() (including packet_seq, timestamp_esp, daya, sensor_read_seq), then backend performs updateOrCreate using packet unique key.

Example 2: Latency calculation

timestamp_esp   = 1772021517   (UTC second)
timestamp_server= 1772021518.320 (UTC with millisecond)
latency_ms      = abs(1772021518.320 - 1772021517) * 1000
                = 1320 ms

Simple view: data arrives around 1.32 seconds after the device timestamp.

Technical view: this matches the Carbon::floatDiffInMilliseconds() + abs() pattern in both HTTP and MQTT worker paths.

Example 3: Power estimation

Assume: protocol=HTTP, rssi=-60, payload=210 bytes, tx=80 ms,
cpu=240MHz, fail ratio HTTP=10%, temp=28C, humidity=60%, success=true.

totalCurrentMa ~ 72 + 2 + 26.4 + 9.75 + 42 + 14.4 + 20 + 3 + 2.7 + 0.5 + 0
               ~ 192.75 mA
powerMw = 3.30 * 192.75 ~ 636.08 mW

Simple view: weaker signal, longer transmission, and larger payload generally increase estimated power.

Technical view: the components above follow the real terms in firmware estimateProtocolPower().

Example 4: Reading p-value and significance

Simple view: if result is significant, it means the two communication paths have a statistically meaningful performance difference.

Technical view: current code determines significance using |t_value| > 1.96. p_value is shown as an approximation to support interpretation (not exact Student-t computation for all df).

Why Was This System Designed This Way?

Why compare MQTT and HTTP

Simple view: research needs fair comparison. Similar payload is sent through two different paths so we can observe which one is faster and more stable.

Technical view: firmware sends matching fields to HTTP and MQTT, backend stores them in the same table/schema (distinguished by protokol) so cross-protocol statistics can be compared directly.

Why MQTT worker is separated

Simple view: MQTT path is "always listening", so it fits better as a dedicated always-on process.

Technical view: this repository intentionally has no Laravel MQTT ingest controller; MQTT ingest runs in standalone mqtt_worker.php with reconnect loop, fallback hosts, and lock file to prevent duplicate workers.

Why use T-Test

Simple view: averages alone are not enough. T-Test checks whether the difference between protocols is statistically strong enough.

Technical view: StatisticsService computes independent sample t-tests for latency and power, then dashboard displays key parameters (N, mean, variance, std dev, t-value, p-value, interpretation).

Database Structure and Table Relations

devices (id PK)
  |-< eksperimens.device_id FK (1:N)
  |-< simulated_eksperimens.device_id FK (1:N)
  '- 1:1 device_firmware_profiles.device_id FK (unique)

app_settings (standalone key-value runtime overrides)

Telemetry table (eksperimens)

Core columns: device_id, protokol, suhu, kelembapan, timestamp_esp, timestamp_server, latency_ms, daya_mw, plus additional diagnostics columns.

Unique key for idempotent ingestion: (device_id, protokol, packet_seq).

Simulation telemetry table

simulated_eksperimens mirrors the same telemetry shape with the same unique key, but remains isolated from real telemetry table.

Query examples (runtime troubleshooting)

-- Latest real rows by protocol
SELECT id, device_id, protokol, packet_seq, latency_ms, daya_mw, timestamp_server
FROM eksperimens
WHERE protokol IN ('MQTT', 'HTTP')
ORDER BY COALESCE(timestamp_server, created_at) DESC
LIMIT 20;

-- Required field completeness audit for MQTT scope
SELECT
  COUNT(*) AS total_rows,
  SUM(CASE WHEN suhu IS NULL THEN 1 ELSE 0 END) AS missing_suhu,
  SUM(CASE WHEN kelembapan IS NULL THEN 1 ELSE 0 END) AS missing_kelembapan,
  SUM(CASE WHEN packet_seq IS NULL THEN 1 ELSE 0 END) AS missing_packet_seq
FROM eksperimens
WHERE UPPER(protokol) = 'MQTT';

Flow: Data Ingest to Dashboard Rendering

Simple view: after data is saved into database, the system transforms it into concise numbers so users can quickly see which protocol is faster, more power-efficient, and more stable.

Technical view: DashboardController::index pulls protocol data from telemetry model, calls StatisticsService for summary/reliability/t-test, prepares chart payloads, then sends all results to resources/views/dashboard.blade.php.

  1. ESP32 sends HTTP and MQTT payloads with full telemetry fields.
  2. HTTP path enters ApiController; MQTT path enters mqtt_worker.php.
  3. Both paths compute latency and perform idempotent upsert into eksperimens.
  4. DashboardController@index fetches MQTT and HTTP samples (analysis window: 50, chart window: unlimited).
  5. StatisticsService computes summary stats, reliability, and T-test.
  6. Dashboard view renders metric cards, warning list, diagnostics panel, latency chart, power chart, and statistical analysis section.
  7. Frontend auto-refresh updates cards, charts, and diagnostics periodically.

Dashboard statistics components currently displayed

  • Header metrics: average temperature and average humidity.
  • Realtime badges: ESP32 ON/OFF, MQTT Connected/Disconnected, HTTP Connected/Disconnected.
  • 8 protocol cards: total data, avg latency, avg power, reliability (MQTT + HTTP).
  • Data quality warnings and field completeness panel.
  • Protocol payload diagnostics (latest MQTT/HTTP rows + delta values).
  • Comparative charts: latency and power.
  • Independent sample T-test results for latency and power.
  • Floating realtime link monitor widget (ping/throughput).

Validation Used by Backend Statistics and Ingest

Ingest validation

  • device_id must exist in devices.
  • kelembapan constrained to 0..100.
  • timestamp_esp constrained to epoch range 1000000000..4102444800.
  • packet_seq must be >= 1.
  • rssi_dbm constrained to -120..0.
  • HTTP path uses Laravel validator; MQTT path uses manual checks in worker.

Statistical validation rules

  • T-test requires at least 2 rows per protocol.
  • Rejects df=0 and standard error denominator=0 cases.
  • Reliability scope prioritizes rows with non-null packet_seq when available.
  • Required field completeness checks 15 telemetry fields consistently for MQTT and HTTP.

System Limitations (Based on Current Code)

  • p_value in T-test uses approximation, especially coarse for small df.
  • Critical value is fixed at +/-1.96 in code, not dynamically adjusted by df.
  • daya_mw is a firmware-side estimate, not direct electrical sensor measurement.
  • Latency depends on ESP32 clock validity; NTP drift or unsynced epoch affects metric quality.
  • Sequence reliability uses segmentation rules (reboot/jump handling), so very large jumps become new segments instead of continuous loss.
  • MQTT ingestion depends on standalone worker process availability; if worker is down, MQTT telemetry is not persisted.
  • This documentation is implementation-bound; update this page whenever telemetry schema, formulas, or runtime paths change.

Glossary / Glossary

Each term is written in two layers: simple explanation for general readers, followed by technical explanation based on actual repository implementation.

idempotent upsert

Simple: sending the same data repeatedly does not create duplicates.

Technical: HTTP and MQTT use updateOrCreate with identity (device_id, protokol, packet_seq), reinforced by database unique index on the same key combination.

packet_seq

Simple: packet order number so the system can detect missing or repeated packets.

Technical: firmware increments httpPacketSeq and mqttPacketSeq on each send; backend uses this value for idempotent dedup and sequence reliability calculation.

window analysis

Simple: the system only takes a recent subset of data so analysis remains lightweight and relevant.

Technical: DASHBOARD_ANALYSIS_WINDOW (default 50) is used by StatisticsService::getProtocolData(); chart uses DASHBOARD_CHART_WINDOW (default 0 (unlimited)).

latency_ms

Simple: travel time of data from ESP32 to server, in milliseconds.

Technical: calculated as abs(server_utc - timestamp_esp) in ApiController::storeHttp and mqtt_worker.php, then stored in latency_ms column.

daya_mw

Simple: estimated power usage during data transmission.

Technical: firmware computes daya via estimateProtocolPower() then backend stores it in daya_mw. This is a model estimate, not direct electrical sensor reading.

Independent Sample T-Test

Simple: statistical test to check whether differences between two data groups are scientifically strong enough.

Technical: StatisticsService::tTest() computes pooled variance, standard error, and t_value for MQTT vs HTTP data on latency_ms and daya_mw metrics.

p-value

Simple: a number that helps judge whether the observed difference could be random chance.

Technical: in this project, p_value comes from approximation function calculatePValue() (normal CDF for large df, coarse bins for small df), so it is indicative.

publish/subscribe

Simple: sender puts message into a topic, and subscribed receivers process it.

Technical: firmware sendMQTT() performs publish to configured topic, then mqtt_worker.php performs subscribe and stores payload into database.

request/response

Simple: sender asks server to process data, then server replies with result status.

Technical: firmware sendHTTP() posts to /api/http-data, ApiController::storeHttp handles validation/upsert, then returns JSON response with HTTP status code.