Front Loader
Wheel loader with payload monitoring, cycle tracking, and engine telemetry.
Wheel loader with payload monitoring, cycle tracking, and engine telemetry. Send readings for this device type with the field names in the metrics table. Every table on this page is generated from the platform, so it matches what the ingest service accepts today.
Metrics#
These are the registered field names for the data object. A reading may carry any subset. A missing field is not an error, so a device can report one value at a time. Any field you send that is not listed here is a custom data point. Custom data points appear on dashboards. They never drive alerts or forecasts.
| Field | Type | Required | Unit | Description |
|---|---|---|---|---|
| payload | number | Yes | tons | Current bucket payload weight |
| cycleCount | number | No | Loads completed this shift | |
| cumulativeTonnage | number | No | tons | Total tonnage moved this shift |
| engineHours | number | No | hr | Engine runtime hours |
| fuelLevel | number | No | % | Fuel tank level |
| hydraulicPressure | number | No | bar | Hydraulic system pressure |
| engineRpm | number | No | RPM | Engine speed |
Configuration#
Configuration is set on the device in the app. It is not sent with a reading. It tells Logable how to read the values, for example which unit a weight is in.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| bucketCapacity | number | Yes | Not set | Maximum bucket capacity in tonnes |
| weightUnit | tons | kg | Yes | Not set | Weight display unit |
| make | string | No | Not set | Manufacturer (e.g. Caterpillar, Komatsu) |
| model | string | No | Not set | Model number |
Example payload#
A complete request body for this device type.
{
"timestamp": "2026-07-01T10:00:00.000Z",
"status": "online",
"data": {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
}
}Code samples#
Replace the placeholder id and token with the values from the device's Connection tab.
Over HTTP
# Front Loader - Quick Test (curl)
# Device Type: front-loader | Device ID: <DEVICE_ID>
#
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
curl -X POST "https://ingest.logable.app/ingest/<DEVICE_ID>" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <DEVICE_TOKEN>" \
-d '{"timestamp":"2026-07-01T10:00:00.000Z","status":"online","data":{"payload":32.5,"cycleCount":47,"cumulativeTonnage":1527.5,"engineHours":4821.3,"fuelLevel":64,"hydraulicPressure":210,"engineRpm":1650}}'"""
Front Loader - Logable HTTP Integration (Python)
Device Type: front-loader
Device ID: <DEVICE_ID>
Installation: pip install requests
"""
import requests
from datetime import datetime, timezone
API_ENDPOINT = "https://ingest.logable.app/ingest/<DEVICE_ID>"
API_TOKEN = "<DEVICE_TOKEN>"
def send_data():
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
payload = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"status": "online",
"data": {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
}
}
response = requests.post(
API_ENDPOINT,
json=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {API_TOKEN}"
},
timeout=10
)
response.raise_for_status()
print(f"Data sent successfully: {response.status_code}")
if __name__ == "__main__":
send_data()/**
* Front Loader - Logable HTTP Integration (TypeScript)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Runtime: Node.js 18+ or modern browser (uses built-in fetch)
*/
const API_ENDPOINT = "https://ingest.logable.app/ingest/<DEVICE_ID>";
const API_TOKEN = "<DEVICE_TOKEN>";
async function sendData(): Promise<void> {
// The "data" fields shown are this device type's registered metrics.
// Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
// Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
// Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
// Custom values appear on dashboards via a widget's "Show Custom Data" option.
const payload = {
timestamp: new Date().toISOString(),
status: "online",
data: {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
},
};
const response = await fetch(API_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_TOKEN}`,
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
console.log("Data sent successfully:", response.status);
}
sendData();#!/bin/bash
# Front Loader - Logable HTTP Integration (Bash)
# Device Type: front-loader
# Device ID: <DEVICE_ID>
API_ENDPOINT="https://ingest.logable.app/ingest/<DEVICE_ID>"
API_TOKEN="<DEVICE_TOKEN>"
# NOTE: the heredoc below is pure JSON, so it cannot contain comments.
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
# Quoted heredoc delimiter: '$' in metric values is taken literally.
# "timestamp" is optional - the platform stamps arrival time when omitted.
read -r -d '' PAYLOAD <<'EOF'
{
"status": "online",
"data": {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
}
}
EOF
HTTP_CODE=$(curl -s -w "%{http_code}" -o /dev/null \
-X POST "${API_ENDPOINT}" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_TOKEN}" \
-d "${PAYLOAD}")
if [ "${HTTP_CODE}" -ge 200 ] && [ "${HTTP_CODE}" -lt 300 ]; then
echo "Data sent successfully (HTTP ${HTTP_CODE})"
exit 0
else
echo "Error sending data (HTTP ${HTTP_CODE})"
exit 1
fi<#
.SYNOPSIS
Front Loader - Logable HTTP Integration (PowerShell)
Device Type: front-loader
Device ID: <DEVICE_ID>
#>
$ApiEndpoint = "https://ingest.logable.app/ingest/<DEVICE_ID>"
$ApiToken = "<DEVICE_TOKEN>"
function Send-DeviceData {
$Timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
$Payload = @{
timestamp = $Timestamp
status = "online"
data = @{
payload = 32.5
cycleCount = 47
cumulativeTonnage = 1527.5
engineHours = 4821.3
fuelLevel = 64
hydraulicPressure = 210
engineRpm = 1650
}
} | ConvertTo-Json -Depth 3
$Headers = @{
"Content-Type" = "application/json"
"Authorization" = "Bearer $ApiToken"
}
try {
$Response = Invoke-RestMethod `
-Uri $ApiEndpoint `
-Method Post `
-Headers $Headers `
-Body $Payload `
-TimeoutSec 10
Write-Host "Data sent successfully" -ForegroundColor Green
return $Response
} catch {
Write-Error "Error: $($_.Exception.Message)"
throw
}
}
Send-DeviceData/*
* Front Loader - Logable HTTP Integration (C / libcurl)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Install: sudo apt-get install libcurl4-openssl-dev
* Compile: gcc -o device_client device_client.c -lcurl
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <curl/curl.h>
#define API_ENDPOINT "https://ingest.logable.app/ingest/<DEVICE_ID>"
#define API_TOKEN "<DEVICE_TOKEN>"
/* Example metric payload - CUSTOMIZE DATA HERE */
static const char *DATA_JSON =
"{\"payload\":32.5,\"cycleCount\":47,\"cumulativeTonnage\":1527.5,\"engineHours\":4821.3,\"fuelLevel\":64,\"hydraulicPressure\":210,\"engineRpm\":1650}";
int main(void) {
CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
time_t now = time(NULL);
char timestamp[64];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
/* Build JSON payload - CUSTOMIZE DATA HERE */
/*
* The "data" fields shown are this device type's registered metrics.
* Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
* Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
* Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
* Custom values appear on dashboards via a widget's "Show Custom Data" option.
*/
/* Metric data is passed as a %s argument (never spliced into the
* format string), so '%' characters in values stay intact. */
char payload[1024];
snprintf(payload, sizeof(payload),
"{\"timestamp\": \"%s\", \"status\": \"online\", \"data\": %s}",
timestamp, DATA_JSON);
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, API_ENDPOINT);
headers = curl_slist_append(headers, "Content-Type: application/json");
char auth_header[256];
snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", API_TOKEN);
headers = curl_slist_append(headers, auth_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "Request failed: %s\n", curl_easy_strerror(res));
} else {
printf("Data sent successfully\n");
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}/*
* Front Loader - Logable HTTP Integration (Arduino / ESP32)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Libraries: WiFi, HTTPClient, ArduinoJson
* Board: ESP32 or ESP8266
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_ENDPOINT = "https://ingest.logable.app/ingest/<DEVICE_ID>";
const char* API_TOKEN = "<DEVICE_TOKEN>";
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" Connected!");
}
void sendMetrics() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected");
return;
}
HTTPClient http;
http.begin(API_ENDPOINT);
http.addHeader("Content-Type", "application/json");
char authHeader[256];
snprintf(authHeader, sizeof(authHeader), "Bearer %s", API_TOKEN);
http.addHeader("Authorization", authHeader);
/* Build JSON payload - CUSTOMIZE DATA HERE */
JsonDocument doc;
doc["status"] = "online";
doc.createNestedObject("data");
doc["data"]["payload"] = 32.5;
doc["data"]["cycleCount"] = 47;
doc["data"]["cumulativeTonnage"] = 1527.5;
doc["data"]["engineHours"] = 4821.3;
doc["data"]["fuelLevel"] = 64;
doc["data"]["hydraulicPressure"] = 210;
doc["data"]["engineRpm"] = 1650;
// The "data" fields shown are this device type's registered metrics.
// Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
// Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
// Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
// Custom values appear on dashboards via a widget's "Show Custom Data" option.
String payload;
serializeJson(doc, payload);
int httpCode = http.POST(payload);
if (httpCode > 0) {
Serial.printf("Data sent (HTTP %d)\n", httpCode);
} else {
Serial.printf("HTTP error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
void loop() {
sendMetrics();
delay(10000); /* Send every 10 seconds */
}Over MQTT
"""
Front Loader - Logable MQTT Integration (Python)
Device Type: front-loader
Device ID: <DEVICE_ID>
Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
Client ID must be unique per connection - a duplicate ID disconnects the other client.
Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
Installation: pip install paho-mqtt
"""
import paho.mqtt.client as mqtt
import json
from datetime import datetime, timezone
import time
MQTT_HOST = "mqtt.logable.app"
MQTT_PORT = 8883
MQTT_USERNAME = "<DEVICE_ID>"
MQTT_PASSWORD = "<DEVICE_TOKEN>"
MQTT_CLIENT_ID = "logable-<DEVICE_ID>"
MQTT_TOPIC = "devices/<DEVICE_ID>/data"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection failed: {reason_code}")
else:
print(f"Connected to MQTT broker at {MQTT_HOST}:{MQTT_PORT}")
def send_data(client):
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
payload = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"status": "online",
"data": {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
}
}
result = client.publish(MQTT_TOPIC, json.dumps(payload), qos=1)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print(f"Data sent to {MQTT_TOPIC}")
def main():
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=MQTT_CLIENT_ID)
client.on_connect = on_connect
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
# TLS with the default system CA store - validates the broker's
# Let's Encrypt certificate out of the box.
client.tls_set()
try:
client.connect(MQTT_HOST, MQTT_PORT, keepalive=60)
client.loop_start()
time.sleep(1)
send_data(client)
time.sleep(1)
client.loop_stop()
client.disconnect()
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()/**
* Front Loader - Logable MQTT Integration (TypeScript)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
* Client ID must be unique per connection - a duplicate ID disconnects the other client.
* Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
*
* Installation: npm install mqtt
*/
import mqtt from 'mqtt';
// mqtts:// - the mqtt package handles TLS and CA validation automatically.
const MQTT_URL = "mqtts://mqtt.logable.app:8883";
const MQTT_USERNAME = "<DEVICE_ID>";
const MQTT_PASSWORD = "<DEVICE_TOKEN>";
const MQTT_CLIENT_ID = "logable-<DEVICE_ID>";
const MQTT_TOPIC = "devices/<DEVICE_ID>/data";
async function sendData(): Promise<void> {
const client = mqtt.connect(MQTT_URL, {
clientId: MQTT_CLIENT_ID,
username: MQTT_USERNAME,
password: MQTT_PASSWORD,
clean: true,
});
client.on('connect', () => {
console.log(`Connected to MQTT broker at ${MQTT_URL}`);
// The "data" fields shown are this device type's registered metrics.
// Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
// Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
// Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
// Custom values appear on dashboards via a widget's "Show Custom Data" option.
const payload = {
timestamp: new Date().toISOString(),
status: "online",
data: {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
},
};
client.publish(
MQTT_TOPIC,
JSON.stringify(payload),
{ qos: 1 },
(error) => {
if (error) console.error('Failed to publish:', error);
else console.log(`Data sent to ${MQTT_TOPIC}`);
client.end();
}
);
});
client.on('error', (error) => {
console.error('MQTT error:', error);
client.end();
});
}
sendData();#!/bin/bash
# Front Loader - Logable MQTT Integration (Bash)
# Device Type: front-loader
# Device ID: <DEVICE_ID>
#
# Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
# Client ID must be unique per connection - a duplicate ID disconnects the other client.
# Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
#
# Requirements: mosquitto-clients
MQTT_HOST="mqtt.logable.app"
MQTT_PORT=8883
MQTT_USERNAME="<DEVICE_ID>"
MQTT_PASSWORD="<DEVICE_TOKEN>"
MQTT_CLIENT_ID="logable-<DEVICE_ID>"
MQTT_TOPIC="devices/<DEVICE_ID>/data"
# NOTE: the heredoc below is pure JSON, so it cannot contain comments.
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
# Quoted heredoc delimiter: '$' in metric values is taken literally.
# "timestamp" is optional - the platform stamps arrival time when omitted.
read -r -d '' PAYLOAD <<'EOF'
{
"status": "online",
"data": {
"payload": 32.5,
"cycleCount": 47,
"cumulativeTonnage": 1527.5,
"engineHours": 4821.3,
"fuelLevel": 64,
"hydraulicPressure": 210,
"engineRpm": 1650
}
}
EOF
# TLS: --capath works on Linux; on macOS use --cafile /etc/ssl/cert.pem instead.
mosquitto_pub \
-h "${MQTT_HOST}" \
-p "${MQTT_PORT}" \
--capath /etc/ssl/certs \
-i "${MQTT_CLIENT_ID}" \
-u "${MQTT_USERNAME}" \
-P "${MQTT_PASSWORD}" \
-t "${MQTT_TOPIC}" \
-m "${PAYLOAD}" \
-q 1
if [ $? -eq 0 ]; then
echo "Data published to ${MQTT_TOPIC}"
exit 0
else
echo "Failed to publish"
exit 1
fi<#
.SYNOPSIS
Front Loader - Logable MQTT Integration (PowerShell)
Device Type: front-loader
Device ID: <DEVICE_ID>
Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
Client ID must be unique per connection - a duplicate ID disconnects the other client.
Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
Requirements: the M2Mqtt .NET library (M2Mqtt.Net.dll).
Install via NuGet: Install-Package M2Mqtt
(or download the package and extract lib/net45/M2Mqtt.Net.dll,
then adjust the Add-Type path below)
#>
$MqttHost = "mqtt.logable.app"
$MqttPort = 8883
$MqttUsername = "<DEVICE_ID>"
$MqttPassword = "<DEVICE_TOKEN>"
$MqttClientId = "logable-<DEVICE_ID>"
$MqttTopic = "devices/<DEVICE_ID>/data"
Add-Type -Path "M2Mqtt.Net.dll"
function Send-DeviceDataMqtt {
$Timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
# The "data" fields shown are this device type's registered metrics.
# Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
# Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
# Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
# Custom values appear on dashboards via a widget's "Show Custom Data" option.
$Payload = @{
timestamp = $Timestamp
status = "online"
data = @{
payload = 32.5
cycleCount = 47
cumulativeTonnage = 1527.5
engineHours = 4821.3
fuelLevel = 64
hydraulicPressure = 210
engineRpm = 1650
}
} | ConvertTo-Json -Depth 3
# TLS on the standard MQTT-over-TLS port; .NET validates the
# broker's Let's Encrypt certificate against the system store.
$Client = [uPLibrary.Networking.M2Mqtt.MqttClient]::new(
$MqttHost, $MqttPort, $true,
[uPLibrary.Networking.M2Mqtt.MqttSslProtocols]::TLSv1_2,
$null, $null
)
try {
$ReturnCode = $Client.Connect($MqttClientId, $MqttUsername, $MqttPassword)
if ($ReturnCode -ne 0) {
throw "MQTT connect failed (code $ReturnCode)"
}
$Client.Publish(
$MqttTopic,
[System.Text.Encoding]::UTF8.GetBytes($Payload),
[uPLibrary.Networking.M2Mqtt.Messages.MqttMsgBase]::QOS_LEVEL_AT_LEAST_ONCE,
$false
) | Out-Null
Write-Host "Data published to $MqttTopic" -ForegroundColor Green
} catch {
Write-Error "Error: $($_.Exception.Message)"
throw
} finally {
if ($Client.IsConnected) {
$Client.Disconnect()
}
}
}
Send-DeviceDataMqtt/*
* Front Loader - Logable MQTT Integration (C / Eclipse Paho)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
* Client ID must be unique per connection - a duplicate ID disconnects the other client.
* Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
*
* Install: sudo apt-get install libpaho-mqtt-dev
* Compile: gcc -o mqtt_client mqtt_client.c -lpaho-mqtt3cs
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <MQTTClient.h>
#define MQTT_ADDRESS "ssl://mqtt.logable.app:8883"
#define MQTT_CLIENT_ID "logable-<DEVICE_ID>"
#define MQTT_USERNAME "<DEVICE_ID>"
#define MQTT_PASSWORD "<DEVICE_TOKEN>"
#define MQTT_TOPIC "devices/<DEVICE_ID>/data"
#define MQTT_QOS 1
#define MQTT_TIMEOUT_MS 10000L
/* Example metric payload - CUSTOMIZE DATA HERE */
static const char *DATA_JSON =
"{\"payload\":32.5,\"cycleCount\":47,\"cumulativeTonnage\":1527.5,\"engineHours\":4821.3,\"fuelLevel\":64,\"hydraulicPressure\":210,\"engineRpm\":1650}";
int main(void) {
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
char payload[1024];
char timestamp[64];
time_t now = time(NULL);
int rc;
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
/* Build JSON payload - CUSTOMIZE DATA HERE */
/*
* The "data" fields shown are this device type's registered metrics.
* Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
* Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
* Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
* Custom values appear on dashboards via a widget's "Show Custom Data" option.
*/
/* Metric data is passed as a %s argument (never spliced into the
* format string), so '%' characters in values stay intact. */
snprintf(payload, sizeof(payload),
"{\"timestamp\": \"%s\", \"status\": \"online\", \"data\": %s}",
timestamp, DATA_JSON);
rc = MQTTClient_create(&client, MQTT_ADDRESS, MQTT_CLIENT_ID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
if (rc != MQTTCLIENT_SUCCESS) {
fprintf(stderr, "Failed to create client (rc=%d)\n", rc);
return 1;
}
conn_opts.keepAliveInterval = 60;
conn_opts.cleansession = 1;
conn_opts.username = MQTT_USERNAME;
conn_opts.password = MQTT_PASSWORD;
/* TLS: validate the broker's Let's Encrypt certificate against the
* system CA store (works out of the box on Linux/macOS). */
ssl_opts.enableServerCertAuth = 1;
conn_opts.ssl = &ssl_opts;
rc = MQTTClient_connect(client, &conn_opts);
if (rc != MQTTCLIENT_SUCCESS) {
fprintf(stderr, "Failed to connect (rc=%d)\n", rc);
MQTTClient_destroy(&client);
return 1;
}
printf("Connected to MQTT broker\n");
pubmsg.payload = payload;
pubmsg.payloadlen = (int)strlen(payload);
pubmsg.qos = MQTT_QOS;
pubmsg.retained = 0;
rc = MQTTClient_publishMessage(client, MQTT_TOPIC, &pubmsg, &token);
if (rc == MQTTCLIENT_SUCCESS) {
rc = MQTTClient_waitForCompletion(client, token, MQTT_TIMEOUT_MS);
printf("Data published to %s\n", MQTT_TOPIC);
} else {
fprintf(stderr, "Failed to publish (rc=%d)\n", rc);
}
MQTTClient_disconnect(client, 1000);
MQTTClient_destroy(&client);
return 0;
}/*
* Front Loader - Logable MQTT Integration (Arduino / ESP32)
* Device Type: front-loader
* Device ID: <DEVICE_ID>
*
* Broker: MQTT over TLS on port 8883 (Let's Encrypt certificate; standard system CAs validate it). WebSocket: wss://mqtt.logable.app/mqtt
* Client ID must be unique per connection - a duplicate ID disconnects the other client.
* Access: this device may publish/subscribe only under devices/<DEVICE_ID>/* - everything else is denied.
*
* Libraries: WiFi, WiFiClientSecure, PubSubClient, ArduinoJson
* Board: ESP32 or ESP8266
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "mqtt.logable.app";
const int MQTT_PORT = 8883;
const char* MQTT_USERNAME = "<DEVICE_ID>";
const char* MQTT_PASSWORD = "<DEVICE_TOKEN>";
const char* MQTT_CLIENT_ID = "logable-<DEVICE_ID>";
const char* MQTT_TOPIC = "devices/<DEVICE_ID>/data";
WiFiClientSecure espClient;
PubSubClient mqttClient(espClient);
void setupWifi() {
Serial.print("Connecting to WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" Connected!");
}
void reconnectMqtt() {
while (!mqttClient.connected()) {
Serial.print("Connecting to MQTT...");
if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
Serial.println(" Connected!");
} else {
Serial.printf(" Failed (rc=%d). Retrying in 5s...\n", mqttClient.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
setupWifi();
/* TLS: setInsecure() skips certificate validation - acceptable for a
* quick test, but production firmware should pin the ISRG Root X1 CA
* (Let's Encrypt root) instead:
*
* static const char ISRG_ROOT_X1[] PROGMEM = R"CERT(
* -----BEGIN CERTIFICATE-----
* ...paste the ISRG Root X1 PEM from https://letsencrypt.org/certificates/
* -----END CERTIFICATE-----
* )CERT";
* espClient.setCACert(ISRG_ROOT_X1);
*/
espClient.setInsecure();
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
/* Must be set BEFORE connecting - PubSubClient's 256-byte default
* truncates typical Logable payloads. Keep payloads under 512 bytes. */
mqttClient.setBufferSize(512);
}
void sendMetrics() {
if (!mqttClient.connected()) {
reconnectMqtt();
}
mqttClient.loop();
/* Build JSON payload - CUSTOMIZE DATA HERE (keep under 512 bytes) */
JsonDocument doc;
doc["status"] = "online";
doc.createNestedObject("data");
doc["data"]["payload"] = 32.5;
doc["data"]["cycleCount"] = 47;
doc["data"]["cumulativeTonnage"] = 1527.5;
doc["data"]["engineHours"] = 4821.3;
doc["data"]["fuelLevel"] = 64;
doc["data"]["hydraulicPressure"] = 210;
doc["data"]["engineRpm"] = 1650;
// The "data" fields shown are this device type's registered metrics.
// Optionally add your OWN custom fields to "data" (e.g. "totalizer": 1284300.0).
// Custom keys: start with a letter; letters/digits/underscores; max 64 chars.
// Up to 20 custom fields; values: number, string (max 256 chars) or boolean.
// Custom values appear on dashboards via a widget's "Show Custom Data" option.
char payload[512];
serializeJson(doc, payload, sizeof(payload));
if (mqttClient.publish(MQTT_TOPIC, payload)) {
Serial.println("Data published via MQTT");
} else {
Serial.println("MQTT publish failed");
}
}
void loop() {
sendMetrics();
delay(10000); /* Send every 10 seconds */
}Widgets#
Widgets you can put on a dashboard for this device type.
- Data Table. Tabular view of device metrics with pagination, sorting, and real-time updates
- Line Chart. Time-series line chart for visualizing metrics over time with multiple devices
- Area Chart. Filled area chart for volume and trend visualization
- Bar Chart. Categorical comparison with vertical or horizontal bars
- Pie Chart. Proportional distribution visualization
- Radar Chart. Multi-dimensional data comparison
- Gauge. Radial gauge for single metric monitoring with thresholds
- Stat Card. Single-value KPI display with optional trend sparkline
- Status Card. Device health and connectivity status indicator
- Front Loader Visualization. Visual representation of front loader with payload and cycle tracking
- Plant Status. Fleet-wide asset health, ranked by concern