Copy, flash and see an ESP32 report.
The generated sketch already contains the Devicecamp host, topics, CA and one-time MQTT credential. Add Wi-Fi, flash it and replace the two sample values with your sensor reads.
1. Prepare Arduino IDE
In Boards Manager install “esp32 by Espressif Systems”. In Library Manager install “PubSubClient by Nick O’Leary”. WiFi and WiFiClientSecure come with the ESP32 core.
2. Generate the device sketch
Create a device, choose ESP32 Arduino and create its MQTT credential. Devicecamp puts the exact client ID, username, password and topics into the sketch. Save it locally: the credential is shown only once and must not be committed.
3. Paste the complete sketch
The public template below uses placeholders. The version generated after device creation is ready to use: only set WIFI_SSID and WIFI_PASSWORD. It validates TLS, synchronizes the clock, reconnects with backoff and publishes once per minute.
// Devicecamp ESP32 MQTT/TLS starter
// Install "esp32 by Espressif Systems" and "PubSubClient by Nick O'Leary".
// This file contains a live device credential. Do not commit or share it.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
#include <math.h>
const char WIFI_SSID[] = "YOUR_WIFI_SSID";
const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
const char MQTT_HOST[] = "mqtt.devicecamp.app";
const uint16_t MQTT_PORT = 8883;
const char MQTT_CLIENT_ID[] = "MQTT_CLIENT_ID";
const char MQTT_USERNAME[] = "MQTT_USERNAME";
const char MQTT_PASSWORD[] = "MQTT_PASSWORD";
const char TELEMETRY_TOPIC[] = "v1/devices/DEVICE_ID/telemetry";
const char STATUS_TOPIC[] = "v1/devices/DEVICE_ID/status";
const char DEVICECAMP_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";
WiFiClientSecure tls;
PubSubClient mqtt(tls);
unsigned long lastPublishAt = 0;
unsigned long nextReconnectAt = 0;
unsigned long reconnectDelayMs = 1000;
const unsigned long PUBLISH_INTERVAL_MS = 60000;
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" connected");
}
void syncClock() {
// Certificate validation needs a reasonably correct clock.
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Synchronizing clock");
time_t now = time(nullptr);
while (now < 1700000000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println(" ready");
}
void connectMQTT() {
if (mqtt.connected() || millis() < nextReconnectAt) return;
const bool connected = mqtt.connect(
MQTT_CLIENT_ID,
MQTT_USERNAME,
MQTT_PASSWORD,
STATUS_TOPIC,
1,
true,
"{\"status\":\"offline\"}"
);
if (connected) {
Serial.println("MQTT connected");
mqtt.publish(STATUS_TOPIC, "{\"status\":\"online\"}", true);
reconnectDelayMs = 1000;
return;
}
Serial.printf("MQTT failed, state=%d; retry in %lu ms\n",
mqtt.state(), reconnectDelayMs);
nextReconnectAt = millis() + reconnectDelayMs;
reconnectDelayMs = min(reconnectDelayMs * 2, 60000UL);
}
void publishReading() {
// Replace these sample values with reads from your sensor.
const float temperature = 22.4;
const float humidity = 51.0;
const String localIP = WiFi.localIP().toString();
char payload[192];
snprintf(payload, sizeof(payload),
"{\"metrics\":{\"temperature\":%.1f,\"humidity\":%.1f},\"metadata\":{\"local_ip\":\"%s\"}}",
temperature, humidity, localIP.c_str());
if (mqtt.publish(TELEMETRY_TOPIC, payload)) {
Serial.printf("Published: %s\n", payload);
} else {
Serial.println("Publish failed");
}
}
void setup() {
Serial.begin(115200);
connectWiFi();
syncClock();
tls.setCACert(DEVICECAMP_CA);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
mqtt.setKeepAlive(30);
mqtt.setSocketTimeout(10);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
delay(100);
return;
}
connectMQTT();
mqtt.loop();
const unsigned long now = millis();
if (mqtt.connected() &&
(lastPublishAt == 0 || now - lastPublishAt >= PUBLISH_INTERVAL_MS)) {
publishReading();
lastPublishAt = now;
}
}4. Replace the sample readings
In publishReading(), replace temperature and humidity with values from your sensor. Keep metric names and types stable; Devicecamp discovers those metrics when the first payload arrives.
5. Flash and verify
Select your ESP32 board and port, upload, then open Serial Monitor at 115200 baud. “Published” confirms the send; the device workbench should show both metrics. If TLS fails, first check Wi-Fi, DNS and whether NTP synchronization completed.