MQTT Publishing Data
This guide covers how to publish data from your devices to the CoCoCo platform using MQTT topics and properly formatted payloads. Each data type has specific topic patterns and payload requirements.
Topic Structure
All publishing topics follow the pattern d/${deviceId}/[type]/[name] where:
d/indicates device data${deviceId}is your actual device ID (replace with the real value)[type]specifies the data type (s, e, m, b, g, t)[name]is the specific identifier for the data point (when applicable)
Publishing Topics Reference
Set Status (d/${deviceId}/s)
Report the device's operational mode and status.
Payload Format:
{
"mode": "productive" | "non_productive" | "unknown" | "maintenance",
"status": "unknown" | "idle" | "setup" | "running" | "cleanup" | "stopped",
"online": true
}Fields:
mode(String, Required): Overall operational modestatus(String, Required): Specific current status within the modeonline(Boolean, Required): Whether the device is online
Best Practices:
- Use "last will and testament" to automatically set offline status on disconnect
- Set
retain: trueon status messages so last status is preserved - Update status whenever operational mode changes
Example:
const statusPayload = {
mode: "productive",
status: "running",
online: true
};
client.publish(`d/${deviceId}/s`, JSON.stringify(statusPayload), { retain: true });Record Error (d/${deviceId}/e)
Report error conditions and incidents.
Payload Format:
{
"message": "Description of the error that occurred",
"severity": 1 | 2 | 3 | 4 | 5,
"metadata": {
"key1": "value1",
"key2": "value2"
}
}Fields:
message(String, Required): Human-readable error descriptionseverity(Integer, Required): Error severity from 1 (least) to 5 (most critical)metadata(Object, Optional): Additional contextual information
Severity Guidelines:
- 1: Informational - Minor issues or warnings
- 2: Low - Non-critical errors that don't affect operation
- 3: Medium - Errors that may impact performance
- 4: High - Serious errors affecting functionality
- 5: Critical - System failure or safety issues
Example:
const errorPayload = {
message: "Temperature sensor reading out of range",
severity: 3,
metadata: {
sensor_id: "temp_01",
reading: 95.2,
max_threshold: 85.0
}
};
client.publish(`d/${deviceId}/e`, JSON.stringify(errorPayload));Set Single Metric (d/${deviceId}/m/${name})
Report individual sensor readings or measurements.
Payload Format:
{
"value": 500.75,
"unit": "count" | "sheets" | "m" | "ft" | "pt" | "deg_f" | "deg_c" | "liter" | "us_gal" | "uk_gal" | "ml" | "pallets" | "custom",
"custom_unit": "widgetsPerHour",
"metadata": {
"sensor_id": "xyz789"
},
"timestamp": "2025-04-08T10:23:00.123Z"
}Fields:
value(Number, Required): The numerical measurement valueunit(String, Required): Unit of measurement from predefined list or "custom"custom_unit(String, Optional): Required only if unit is "custom"metadata(Object, Optional): Additional context for this readingtimestamp(String, Optional): ISO 8601 timestamp, auto-assigned if omitted
Supported Units:
- count: Generic counting unit
- sheets: Paper sheets or similar flat items
- m, ft: Distance measurements (meters, feet)
- pt: Points (printing/graphics)
- deg_f, deg_c: Temperature (Fahrenheit, Celsius)
- liter, us_gal, uk_gal, ml: Volume measurements
- pallets: Shipping/logistics pallets
- custom: Use with custom_unit field
Example:
const metricPayload = {
value: 72.5,
unit: "deg_f",
timestamp: new Date().toISOString(),
metadata: {
location: "warehouse_zone_a"
}
};
client.publish(`d/${deviceId}/m/temperature`, JSON.stringify(metricPayload));Set Multiple Metrics (d/${deviceId}/m)
Report multiple measurements in a single message for efficiency.
Payload Format:
[
{
"name": "temperature",
"value": 25.5,
"unit": "deg_c",
"timestamp": "2025-04-08T10:25:00Z"
},
{
"name": "pressure",
"value": 1013.2,
"unit": "custom",
"custom_unit": "hPa"
},
{
"name": "partsCount",
"value": 150,
"unit": "count"
}
]Structure:
- Array of metric objects
- Each object requires
namefield plus standard metric fields - Useful for batching related measurements
- Reduces MQTT message overhead
Example:
const multiMetrics = [
{ name: "humidity", value: 45.2, unit: "custom", custom_unit: "%RH" },
{ name: "pressure", value: 14.7, unit: "custom", custom_unit: "psi" },
{ name: "vibration", value: 2.1, unit: "custom", custom_unit: "g" }
];
client.publish(`d/${deviceId}/m`, JSON.stringify(multiMetrics));Set Toggle State (d/${deviceId}/b/${name})
Set boolean/binary state values.
Payload Format:
trueor
falseUsage:
- Simple JSON boolean values
true= on/active/open/enabledfalse= off/inactive/closed/disabled
Examples:
// Turn on heater
client.publish(`d/${deviceId}/b/heaterOn`, JSON.stringify(true));
// Close valve
client.publish(`d/${deviceId}/b/valveOpen`, JSON.stringify(false));
// Set alarm state
client.publish(`d/${deviceId}/b/alarmActive`, JSON.stringify(true));Set Gauge Value (d/${deviceId}/g/${name})
Set current level or quantity values.
Payload Format:
100Usage:
- Simple JSON number representing current value
- Use for "how much right now" scenarios
- Value represents absolute current state
Examples:
// Set current stock level
client.publish(`d/${deviceId}/g/stockLevel`, JSON.stringify(250));
// Set battery percentage
client.publish(`d/${deviceId}/g/batteryPercent`, JSON.stringify(85));
// Set queue depth
client.publish(`d/${deviceId}/g/queueDepth`, JSON.stringify(12));Adjust Counter (d/${deviceId}/t/${name})
Increment or decrement cumulative counters.
Payload Format:
1Usage:
- JSON number representing change amount (not absolute value)
- Positive numbers increment the counter
- Negative numbers decrement the counter
- Platform maintains the running total
Examples:
// Increment items produced by 1
client.publish(`d/${deviceId}/t/itemsProduced`, JSON.stringify(1));
// Add 5 to error count
client.publish(`d/${deviceId}/t/errorCount`, JSON.stringify(5));
// Decrement (if item was rejected)
client.publish(`d/${deviceId}/t/itemsProduced`, JSON.stringify(-1));
// Fractional increments allowed
client.publish(`d/${deviceId}/t/totalWeight`, JSON.stringify(2.5));Metric Naming Rules
Metric names (the ${name} variable in topics) must follow these rules:
Format: /^[a-zA-Z][a-zA-Z0-9_]{1,254}$/
Requirements:
- Must start with a letter (a-z, A-Z)
- Can contain letters, numbers, and underscores
- Maximum 255 characters total
- Names are normalized to lowercase
Valid Examples:
temperaturemainTankLevelpart_count_totalsensor_01_readingmyAwesomeMetric99
Invalid Examples:
1temperature(starts with number)_tank_level(starts with underscore)tank-level(contains hyphen)my metric(contains space)
Message Properties
Quality of Service (QoS)
- QoS 0: Fire and forget, use for non-critical telemetry
- QoS 1: At least once delivery, use for important status/errors
- QoS 2: Exactly once (rarely needed for IoT)
Retain Flag
- Set
retain: truefor status messages so last status is available - Generally
retain: falsefor metrics and counters - Use sparingly to avoid broker storage issues
Example with Properties:
const options = {
qos: 1, // Ensure delivery
retain: true // Keep last message
};
client.publish(`d/${deviceId}/s`, JSON.stringify(statusPayload), options);Best Practices
Message Frequency
- Don't flood the broker with high-frequency updates
- Batch related metrics when possible
- Use appropriate sampling intervals for different data types
Error Handling
- Always handle publish failures in your code
- Implement retry logic for critical messages
- Log failed publications for debugging
Data Validation
- Validate data before publishing to avoid rejected messages
- Ensure metric names follow naming rules
- Verify units are from the supported list
Connection Management
- Publish only after confirming connection
- Handle reconnection scenarios gracefully
- Use connection state to buffer data during outages
Next Steps
After mastering data publishing, learn about:
- Subscribing to receive commands from CoCoCo
- Implementing automated responses with Reactions
- Using the JavaScript MQTT client library effectively