เนื้อหาในบทความ
MQTT คืออะไร?
MQTT (Message Queuing Telemetry Transport) เป็นโปรโตคอล Messaging น้ำหนักเบา ที่ออกแบบมาสำหรับ IoT devices โดยเฉพาะ ทำงานบน TCP/IP และใช้รูปแบบ Publish/Subscribe ที่ทำให้การสื่อสารระหว่าง devices ต่างๆ เป็นไปอย่างมีประสิทธิภาพ
📌 แนวคิดหลักของ MQTT:
- Publisher: Device ที่ส่งข้อมูล (เช่น เซ็นเซอร์วัดอุณหภูมิ)
- Subscriber: > Device ที่รับข้อมูล (เช่น รีเลย์, Dashboard)
- Broker: > Server กลางที่คอยจัดการการส่งข้อมูล
- Topic: > ช่องทางหรือหมวดหมู่ของข้อมูล (เช่น "home/livingroom/temperature")
MQTT เป็นที่นิยมในโลก IoT เพราะ:
- 🔹 ขนาดเล็กและรวดเร็ว - Overhead ต่ำ ส่งข้อมูลได้เร็ว
- 🔹 ประหยัดพลังงาน - เหมาะกับ devices ที่ใช้แบตเตอรี่
- 🔹 ง่ายต่อการใช้งาน - API ที่เรียบง่ายและเข้าใจง่าย
- 🔹 รองรับ QoS - รับประกันการส่งข้อมูลถึงมือผู้รับ
สิ่งที่ต้องเตรียม
📦 Hardware:
- ESP32 Board (รุ่นใดก็ได้ - DevKit, NodeMCU, Wemos)
- USB Cable สำหรับเชื่อมต่อกับคอมพิวเตอร์
- เซ็นเซอร์ (เช่น DHT22 สำหรับวัดอุณหภูมิ) - ตัวเลือก
- Relay Module (สำหรับทดสอบการควบคุม) - ตัวเลือก
- Breadboard และ Jumper Wires
💻 Software:
- Arduino IDE หรือ PlatformIO (VSCode)
- MQTT Broker (เราจะใช้ CynoIoT Platform)
- MQTT Client สำหรับทดสอบ (เช่น MQTT Explorer)
การเตรียม Hardware
🔌 การเชื่อมต่อ ESP32:
- เสียบ USB Cable เข้ากับ ESP32 และคอมพิวเตอร์
- ตรวจสอบว่า Driver ติดตั้งเรียบร้อยแล้ว
- เปิด Arduino IDE และตรวจสอบ COM Port
🌡️ การเชื่อมต่อ DHT22 (ถ้ามี):
- DHT22 VCC → ESP32 3.3V
- DHT22 GND → ESP32 GND
- DHT22 DATA → ESP32 GPIO 4 (หรือขาใดก็ได้)
- ต่อ Resistor 10KΩ ระหว่าง VCC และ DATA
⚡ การเชื่อมต่อ Relay (ถ้ามี):
- Relay VCC → ESP32 3.3V
- Relay GND → ESP32 GND
- Relay IN → ESP32 GPIO 2 (หรือขาใดก็ได้)
การติดตั้ง Software
🔧 1. ติดตั้ง Arduino IDE:
- ดาวน์โหลด Arduino IDE จาก arduino.cc
- ติดตั้งโปรแกรมตามปกติ
- เปิด Arduino IDE → File → Preferences
- ในช่อง "Additional Board Manager URLs" เพิ่ม:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - ไปที่ Tools → Board → Boards Manager → ค้นหา "ESP32" → ติดตั้ง
📚 2. ติดตั้ง Library:
- ไปที่ Sketch → Include Library → Manage Libraries
- ค้นหา "PubSubClient" → ติดตั้ง (สำหรับ MQTT)
- ค้นหา "DHT" → ติดตั้ง (ถ้าใช้ DHT22)
- ค้นหา "WiFi" → ติดตั้ง (มักจะมีมาให้อยู่แล้ว)
พื้นฐาน MQTT: โค้ดตัวอย่างแรก
มาเริ่มต้นด้วยโค้ดง่ายๆ ที่เชื่อมต่อ ESP32 เข้ากับ MQTT Broker และส่งข้อความ "Hello World"
#include <WiFi.h>
#include <PubSubClient.h>
// ================================
// ตั้งค่า WiFi
// ================================
const char* ssid = "YOUR_WIFI_SSID"; // ชื่อ WiFi ของคุณ
const char* password = "YOUR_WIFI_PASSWORD"; // รหัส WiFi ของคุณ
// ================================
// ตั้งค่า MQTT Broker
// ================================
const char* mqtt_server = "broker.cynoiot.com"; // หรือ IP ของ Broker
const int mqtt_port = 1883; // Port มาตรฐาน MQTT
const char* mqtt_client_id = "esp32_client_001"; // ID ของ Client
// ================================
// ตั้งค่า Topic
// ================================
const char* mqtt_topic = "home/esp32/test"; // Topic ที่จะ Publish
// ================================
// สร้าง Objects
// ================================
WiFiClient espClient;
PubSubClient client(espClient);
// ================================
// ฟังก์ชันเชื่อมต่อ WiFi
// ================================
void setupWiFi() {
Serial.begin(115200);
Serial.println("\n\nเริ่มการเชื่อมต่อ WiFi...");
WiFi.begin(ssid, password);
// รอการเชื่อมต่อ
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ เชื่อมต่อ WiFi สำเร็จ!");
Serial.print("📡 IP Address: ");
Serial.println(WiFi.localIP());
}
// ================================
// ฟังก์ชัน Callback: รับข้อความจาก MQTT
// ================================
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("📨 ได้รับข้อความจาก Topic: ");
Serial.println(topic);
Serial.print("📄 ข้อความ: ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println("\n");
}
// ================================
// ฟังก์ชันเชื่อมต่อ MQTT
// ================================
void reconnectMQTT() {
// วนลูปจนกว่าจะเชื่อมต่อได้
while (!client.connected()) {
Serial.print("🔄 พยายามเชื่อมต่อ MQTT Broker...");
// พยายามเชื่อมต่อ
if (client.connect(mqtt_client_id)) {
Serial.println("✅ เชื่อมต่อ MQTT สำเร็จ!");
// Subscribe Topic หากต้องการรับข้อความ
client.subscribe("home/esp32/command");
} else {
Serial.print("❌ ล้มเหลว! State: ");
Serial.print(client.state());
Serial.println(" ลองใหม่ใน 5 วินาที...");
delay(5000); // รอ 5 วินาที
}
}
}
// ================================
// ฟังก์ชันตั้งค่าเริ่มต้น
// ================================
void setup() {
Serial.begin(115200);
// เชื่อมต่อ WiFi
setupWiFi();
// ตั้งค่า MQTT Broker
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
// ================================
// ลูปหลัก
// ================================
void loop() {
// ตรวจสอบการเชื่อมต่อ MQTT
if (!client.connected()) {
reconnectMQTT();
}
client.loop(); // รักษาการเชื่อมต่อและรับข้อความ
// ส่งข้อความ "Hello World" ทุก 5 วินาที
static unsigned long lastMillis = 0;
if (millis() - lastMillis > 5000) {
lastMillis = millis();
String message = "Hello World from ESP32! 🚀";
client.publish(mqtt_topic, message.c_str());
Serial.println("📤 ส่งข้อความ: " + message);
}
}💡 คำอธิบายโค้ด:
- WiFi: เชื่อมต่อ ESP32 เข้ากับเครือข่าย WiFi ของคุณ
- PubSubClient: Library สำหรับ MQTT ที่ทำให้การเชื่อมต่อง่ายขึ้น
- Callback: ฟังก์ชันที่ทำงานเมื่อได้รับข้อความจาก Broker
- client.loop(): สำคัญมาก! ต้องเรียกใช้ใน loop() เสมอ เพื่อรักษาการเชื่อมต่อ
✅ การทดสอบ:
- อัปโหลดโค้ดไปยัง ESP32 (กด Upload ใน Arduino IDE)
- เปิด Serial Monitor (กด Ctrl+Shift+M)
- ตั้ง Baud Rate ที่ 115200
- คุณควรเห็นข้อความ "Hello World" ถูกส่งทุก 5 วินาที
- ใช้ MQTT Explorer หรือ MQTT.fx เพื่อ Subscribe Topic "home/esp32/test" เพื่อดูข้อความ
ส่งค่าเซ็นเซอร์อุณหภูมิ (DHT22)
ตอนนี้มาดูโค้ดจริงที่ใช้ในโปรเจกต์ IoT - ส่งค่า อุณหภูมิและความชื้นผ่าน MQTT
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ================================
// ตั้งค่า WiFi
// ================================
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// ================================
// ตั้งค่า MQTT
// ================================
const char* mqtt_server = "broker.cynoiot.com";
const int mqtt_port = 1883;
const char* mqtt_client_id = "esp32_sensor_001";
// ================================
// ตั้งค่า DHT22 Sensor
// ================================
#define DHTPIN 4 // GPIO ที่ต่อเซ็นเซอร์
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
// ================================
// ตั้งค่า Topic
// ================================
const char* temp_topic = "home/sensor/temperature";
const char* humi_topic = "home/sensor/humidity";
// ================================
// สร้าง Objects
// ================================
WiFiClient espClient;
PubSubClient client(espClient);
// ================================
// ตัวแปรสำหรับจับเวลา
// ================================
unsigned long lastMsgTime = 0;
const long interval = 5000; // ส่งทุก 5 วินาที
// ================================
// ฟังก์ชันเชื่อมต่อ WiFi
// ================================
void setupWiFi() {
Serial.begin(115200);
Serial.println("\nเริ่มการเชื่อมต่อ WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ เชื่อมต่อ WiFi สำเร็จ!");
Serial.print("📡 IP: ");
Serial.println(WiFi.localIP());
}
// ================================
// ฟังก์ชัน Callback
// ================================
void callback(char* topic, byte* payload, unsigned int length) {
// ในกรณีนี้เราไม่ได้ Subscribe อะไร
// แต่ฟังก์ชันนี้จำเป็นสำหรับรับข้อความถ้าต้องการ
}
// ================================
// ฟังก์ชันเชื่อมต่อ MQTT
// ================================
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("🔄 เชื่อมต่อ MQTT...");
if (client.connect(mqtt_client_id)) {
Serial.println("✅ สำเร็จ!");
} else {
Serial.print("❌ ล้มเหลว (");
Serial.print(client.state());
Serial.println(") ลองใหม่ใน 5 วินาที");
delay(5000);
}
}
}
// ================================
// ฟังก์ชันอ่านค่าเซ็นเซอร์
// ================================
void readSensor() {
// อ่านค่าอุณหภูมิ (เซลเซียส)
float temperature = dht.readTemperature();
// อ่านค่าความชื้น (เปอร์เซ็นต์)
float humidity = dht.readHumidity();
// ตรวจสอบว่าอ่านค่าได้หรือไม่
if (isnan(temperature) || isnan(humidity)) {
Serial.println("❌ ไม่สามารถอ่านค่าจาก DHT Sensor!");
return;
}
// ================================
// แปลงค่าเป็น String (สำหรับ MQTT)
// ================================
char tempStr[8];
char humiStr[8];
dtostrf(temperature, 1, 2, tempStr); // แปลง float → string
dtostrf(humidity, 1, 2, humiStr);
// ================================
// ส่งค่าผ่าน MQTT
// ================================
client.publish(temp_topic, tempStr);
client.publish(humi_topic, humiStr);
// ================================
// แสดงผลใน Serial Monitor
// ================================
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.print("🌡️ อุณหภูมิ: ");
Serial.print(temperature);
Serial.println(" °C");
Serial.print("💧 ความชื้น: ");
Serial.print(humidity);
Serial.println(" %");
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━\n");
}
// ================================
// ฟังก์ชันตั้งค่าเริ่มต้น
// ================================
void setup() {
Serial.begin(115200);
// เริ่มต้น DHT Sensor
dht.begin();
// เชื่อมต่อ WiFi
setupWiFi();
// ตั้งค่า MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
// ================================
// ลูปหลัก
// ================================
void loop() {
// ตรวจสอบการเชื่อมต่อ MQTT
if (!client.connected()) {
reconnectMQTT();
}
client.loop(); // รักษาการเชื่อมต่อ
// อ่านและส่งค่าเซ็นเซอร์ทุก 5 วินาที
unsigned long now = millis();
if (now - lastMsgTime >= interval) {
lastMsgTime = now;
readSensor();
}
}📌 จุดสำคัญ:
- dtostrf(): แปลงค่า float → string เพื่อส่งผ่าน MQTT
- isnan(): ตรวจสอบว่าอ่านค่าได้หรือไม่
- Topics: เราใช้ 2 Topics แยกกันสำหรับอุณหภูมิและความชื้น
- Interval: ปรับเวลาได้ตามต้องการ (5000ms = 5 วินาที)
ควบคุม Relay ผ่าน MQTT
ตอนนี้มาดูวิธีควบคุมอุปกรณ์เช่น หลอดไฟ พัดลม หรือปลั๊กไฟ ผ่านคำสั่ง MQTT
#include <WiFi.h>
#include <PubSubClient.h>
// ================================
// ตั้งค่า WiFi
// ================================
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// ================================
// ตั้งค่า MQTT
// ================================
const char* mqtt_server = "broker.cynoiot.com";
const int mqtt_port = 1883;
const char* mqtt_client_id = "esp32_relay_001";
// ================================
// ตั้งค่า Relay
// ================================
const int relayPin = 2; // GPIO ที่ต่อ Relay IN
// ================================
// ตั้งค่า Topic
// ================================
const char* relay_topic = "home/relay/command";
// ================================
// สร้าง Objects
// ================================
WiFiClient espClient;
PubSubClient client(espClient);
// ================================
// ฟังก์ชันเชื่อมต่อ WiFi
// ================================
void setupWiFi() {
Serial.begin(115200);
Serial.println("\nเริ่มการเชื่อมต่อ WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ เชื่อมต่อ WiFi สำเร็จ!");
}
// ================================
// ฟังก์ชัน Callback: รับคำสั่ง MQTT
// ================================
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("📨 ได้รับคำสั่งจาก Topic: ");
Serial.println(topic);
Serial.print("📄 คำสั่ง: ");
// แปลง payload เป็น string
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// ================================
// ตรวจสอบคำสั่งและควบคุม Relay
// ================================
if (message == "ON" || message == "on" || message == "1") {
digitalWrite(relayPin, HIGH); // เปิด Relay
Serial.println("✅ Relay เปิดแล้ว!");
// ส่งยืนยันกลับ (Optional)
client.publish("home/relay/status", "ON");
} else if (message == "OFF" || message == "off" || message == "0") {
digitalWrite(relayPin, LOW); // ปิด Relay
Serial.println("❌ Relay ปิดแล้ว!");
// ส่งยืนยนกลับ (Optional)
client.publish("home/relay/status", "OFF");
} else {
Serial.println("⚠️ ไม่รู้จักคำสั่ง: " + message);
}
Serial.println();
}
// ================================
// ฟังก์ชันเชื่อมต่อ MQTT
// ================================
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("🔄 เชื่อมต่อ MQTT...");
if (client.connect(mqtt_client_id)) {
Serial.println("✅ สำเร็จ!");
// Subscribe Topic เพื่อรับคำสั่ง
client.subscribe(relay_topic);
Serial.println("📥 Subscribe Topic: " + String(relay_topic));
} else {
Serial.print("❌ ล้มเหลว (");
Serial.print(client.state());
Serial.println(") ลองใหม่ใน 5 วินาที");
delay(5000);
}
}
}
// ================================
// ฟังก์ชันตั้งค่าเริ่มต้น
// ================================
void setup() {
Serial.begin(115200);
// ตั้งค่า Relay Pin เป็น OUTPUT
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW); // เริ่มต้นให้ปิด
// เชื่อมต่อ WiFi
setupWiFi();
// ตั้งค่า MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
// ================================
// ลูปหลัก
// ================================
void loop() {
// ตรวจสอบการเชื่อมต่อ MQTT
if (!client.connected()) {
reconnectMQTT();
}
client.loop(); // รักษาการเชื่อมต่อ
}✅ การทดสอบ:
- อัปโหลดโค้ดไปยัง ESP32
- ใช้ MQTT Explorer หรือ MQTT.fx
- Subscribe Topic "home/relay/status"
- Publish "ON" หรือ "OFF" ไปที่ Topic "home/relay/command"
- Relay ควรจะเปิด/ปิดตามคำสั่ง!
เชื่อมต่อกับ CynoIoT Platform
CynoIoT Platform มี MQTT Broker ให้ใช้งานฟรี! มาดูวิธีเชื่อมต่อ
📝 ขั้นตอนการเชื่อมต่อ:
- สมัครสมาชิก:
- ไปที่ cynoiot.com
- คลิก "Sign Up" และกรอกข้อมูล
- ยืนยันอีเมล
- สร้าง Project:
- เข้าสู่ระบบ
- ไปที่ "Projects" → "New Project"
- ตั้งชื่อ Project (เช่น "Home Sensors")
- รับ MQTT Credentials:
- เลือก Project
- ไปที่ "Settings" → "MQTT Config"
- คัดลอกข้อมูลต่อไปนี้:
// ================================
// MQTT Credentials จาก CynoIoT
// ================================
const char* mqtt_server = "mqtt.cynoiot.com"; // เซิร์ฟเวอร์ MQTT
const int mqtt_port = 1883; // Port
const char* mqtt_user = "YOUR_USERNAME"; // ชื่อผู้ใช้ CynoIoT
const char* mqtt_pass = "YOUR_API_KEY"; // API Key จากระบบ
const char* mqtt_client_id = "esp32_001"; // ID ของ Device
// ================================
// Topics สำหรับ CynoIoT
// ================================
const char* temp_topic = "cyno/USERNAME/project/PROJECT_ID/sensor/temperature";
const char* humi_topic = "cyno/USERNAME/project/PROJECT_ID/sensor/humidity"; จากนั้นแก้ไขฟังก์ชัน reconnectMQTT() เพื่อใช้ Username และ Password:
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("🔄 เชื่อมต่อ CynoIoT MQTT...");
// เชื่อมต่อด้วย Username และ Password
if (client.connect(mqtt_client_id, mqtt_user, mqtt_pass)) {
Serial.println("✅ สำเร็จ!");
// Subscribe Topics ที่ต้องการ
client.subscribe("cyno/USERNAME/project/PROJECT_ID/command/#");
} else {
Serial.print("❌ ล้มเหลว (");
Serial.print(client.state());
Serial.println(") ลองใหม่ใน 5 วินาที");
delay(5000);
}
}
}ปัญหาที่พบบ่อยและวิธีแก้ไข
1. เชื่อมต่อ WiFi ไม่ได้
อาการ: Serial Monitor แสดง "." ต่อเนื่อง หรือ "WiFi disconnected"
สาเหตุ: รหัสผ่านผิด, อยู่ไกลเกินไป, หรือเราเตอร์มีปัญหา
วิธีแก้: ตรวจสอบชื่อและรหัส WiFi, ลองย้าย ESP32 ไปใกล้เราเตอร์, รีสตาร์ทเราเตอร์
2. เชื่อมต่อ MQTT ไม่ได้
อาการ: State -2 (MQTT_DISCONNECTED) หรือ timeout
สาเหตุ: MQTT Server ผิด, Port ผิด, หรือ Firewall บล็อก
วิธีแก้: ตรวจสอบ server/port, ลอง testbroker.mosquitto.org สำหรับทดสอบ
3. อ่านค่า DHT22 ไม่ได้
อาการ: แสดง "nan" หรือ "Failed to read"
สาเหตุ: ต่อสายผิดขา, ไม่มี Pull-up Resistor, หรือเซ็นเซอร์เสีย
วิธีแก้: ตรวจสอบการต่อสาย, เพิ่ม Resistor 10KΩ, ลองเซ็นเซอร์ตัวอื่น
4. ESP32 Reboot วนซ้ำ (Boot Loop)
อาการ: อุปกรณ์รีสตาร์ทตลอดเวลา
สาเหตุ: ไม่เพียงพอ (Brownout), สาย USB เสีย, หรือโค้ดผิด
วิธีแก้: เปลี่ยนสาย USB, ใช้แหล่งจ่ายไฟที่ดีกว่า, ลดจำนวน Serial.print
💡 Tips เพิ่มเติม:
- QoS Levels: เลือก QoS 0 (ส่งไปไม่สนใจ), QoS 1 (ส่งให้ได้รับแน่นอน), QoS 2 (ส่งให้ได้รับแน่นอน ครั้งเดียว)
- Retain Messages: ใช้ flag retain เพื่อเก็บข้อความล่าสุดไว้ที่ Broker
- Last Will: ตั้งค่า LWT (Last Will Testament) เพื่อแจ้งเมื่อ device ตัดการเชื่อมต่อ
- JSON Format: ส่งข้อมูลเป็น JSON เพื่อให้อ่านง่ายและขยายได้