Created
January 11, 2026 03:24
-
-
Save quan-vu/b549835c3c5cfe7167134cdce1534e88 to your computer and use it in GitHub Desktop.
Hướng dẫn tích hợp ESP32 với IoTLabs Cloud MQTT trên Arduino IDE (cơ bản)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ESP32 MQTT Integration (Arduino IDE) | |
| - Broker: mqtt.iotlabs.vn:8883 | |
| - Ping topic: send latest status without sensor data | |
| - Telemetry topic: send sensor data | |
| - TLS enabled (port 8883) for secure. | |
| Auth: | |
| - username: d_696309d71a367b6c5fb9663e | |
| - password: PASSWORD | |
| */ | |
| #include <WiFi.h> | |
| #include <PubSubClient.h> | |
| #include <WiFiClientSecure.h> | |
| #include <time.h> | |
| const char* WIFI_SSID = "YOUR_WIFI"; | |
| const char* WIFI_PASS = "YOUR_WIFI_PASS"; | |
| const char* MQTT_HOST = "mqtt.iotlabs.vn"; | |
| const int MQTT_PORT = 8883; | |
| const char* MQTT_USER = "/iotlabs:d_696309d71a367b6c5fb9663e"; | |
| const char* MQTT_PASS = "PASSWORD"; | |
| String DEVICE_ID = "696309d71a367b6c5fb9663e"; | |
| String TOPIC_PING = "iotlabs/69622509adad93591b82ebcb/devices/d_696309d71a367b6c5fb9663e/ping"; | |
| String TOPIC_TELEMETRY = "iotlabs/69622509adad93591b82ebcb/devices/d_696309d71a367b6c5fb9663e/telemetry"; | |
| unsigned long lastSend = 0; | |
| const unsigned long TELEMETRY_INTERVAL_MS = 5000; | |
| WiFiClientSecure net; | |
| PubSubClient client(net); | |
| // ISRG Root X1 (Let's Encrypt) - Root CA | |
| static const char* LE_ISRG_ROOT_X1 PROGMEM = R"EOF( | |
| -----BEGIN CERTIFICATE----- | |
| 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 | |
| -----END CERTIFICATE----- | |
| )EOF"; | |
| void mqttTlsSetup() { | |
| net.setCACert(LE_ISRG_ROOT_X1); | |
| } | |
| void syncTime() { | |
| // VN GMT+7 | |
| configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov"); | |
| Serial.print("Sync NTP"); | |
| time_t now = time(nullptr); | |
| int retry = 0; | |
| // mốc ~ 2023-11-14 (đủ để cert valid) | |
| while (now < 1700000000 && retry < 30) { | |
| Serial.print("."); | |
| delay(500); | |
| now = time(nullptr); | |
| retry++; | |
| } | |
| Serial.println(); | |
| if (now < 1700000000) { | |
| Serial.println("NTP sync FAILED -> TLS will fail"); | |
| } | |
| } | |
| void wifiConnect() { | |
| WiFi.mode(WIFI_STA); | |
| WiFi.begin(WIFI_SSID, WIFI_PASS); | |
| Serial.print("WiFi connecting"); | |
| while (WiFi.status() != WL_CONNECTED) { | |
| delay(350); | |
| Serial.print("."); | |
| } | |
| Serial.println(); | |
| Serial.print("WiFi connected. IP: "); | |
| Serial.println(WiFi.localIP()); | |
| } | |
| void mqttConnect() { | |
| client.setServer(MQTT_HOST, MQTT_PORT); | |
| client.setKeepAlive(30); | |
| client.setSocketTimeout(10); | |
| net.setHandshakeTimeout(30); | |
| Serial.print("MQTT connecting"); | |
| int attempts = 0; | |
| while (!client.connected() && attempts < 20) { | |
| String clientId = "iotlabs_" + String(DEVICE_ID) + "_" + String((uint32_t)ESP.getEfuseMac(), HEX); | |
| bool ok = client.connect(clientId.c_str(), MQTT_USER, MQTT_PASS); | |
| Serial.print(ok ? "✔" : "."); | |
| if (!ok) { | |
| Serial.printf("\nconnect failed, state=%d\n", client.state()); | |
| char err_buf[256]; | |
| net.lastError(err_buf, sizeof(err_buf)); | |
| Serial.printf("tls lastError: %s\n", err_buf); | |
| delay(800); | |
| } | |
| attempts++; | |
| } | |
| Serial.println(); | |
| if (!client.connected()) { | |
| Serial.println("MQTT connect FAILED after retries."); | |
| return; | |
| } | |
| publishPing(); | |
| } | |
| void publishPing() { | |
| String msg = "{"; | |
| msg += "\"deviceId\":\"" + DEVICE_ID + "\","; | |
| msg += "\"ts\":" + String((unsigned long long)millis()) + ","; | |
| msg += "\"message\":\"ping\""; | |
| msg += "}"; | |
| client.publish(TOPIC_PING.c_str(), msg.c_str()); | |
| Serial.println("[MQTT] ping published"); | |
| } | |
| void publishTelemetry(float t, float h, float batteryV) { | |
| String payload = "{"; | |
| payload += "\"deviceId\":\"" + DEVICE_ID + "\","; | |
| payload += "\"ts\":" + String((unsigned long long)millis()) + ","; | |
| payload += "\"metrics\":{"; | |
| payload += "\"temperature\":" + String(t, 1) + ","; | |
| payload += "\"humidity\":" + String(h, 0) + ","; | |
| payload += "\"battery\":" + String(batteryV, 2) + ","; | |
| payload += "\"rssi\":" + String(WiFi.RSSI()); | |
| payload += "}}"; | |
| client.publish(TOPIC_TELEMETRY.c_str(), payload.c_str()); | |
| Serial.println("[MQTT] telemetry published"); | |
| } | |
| void setup() { | |
| Serial.begin(115200); | |
| delay(300); | |
| wifiConnect(); | |
| // 1) WiFi connected | |
| syncTime(); | |
| // 2) TLS verify | |
| mqttTlsSetup(); | |
| // 3) MQTT connect | |
| mqttConnect(); | |
| } | |
| void loop() { | |
| if (WiFi.status() != WL_CONNECTED) wifiConnect(); | |
| if (!client.connected()) mqttConnect(); | |
| client.loop(); | |
| unsigned long now = millis(); | |
| if (now - lastSend >= TELEMETRY_INTERVAL_MS) { | |
| lastSend = now; | |
| // Demo đọc giá trị cảm biến | |
| float temperature = 25.0 + (now % 1000) / 100.0; | |
| float humidity = 50.0 + (now % 500) / 50.0; | |
| float batteryV = 3.90; | |
| publishTelemetry(temperature, humidity, batteryV); | |
| } | |
| delay(50); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment