APIOTA คือแพลตฟอร์มจัดการ OTA (อัปเดตเฟิร์มแวร์ผ่านอินเทอร์เน็ต) สำหรับ ESP32/Arduino แบบครบวงจร — อัปโหลดเฟิร์มแวร์ครั้งเดียว อุปกรณ์ทุกตัวอัปเดตเอง พร้อมสั่งงาน/ดูสถานะ/ตั้งค่าจากระยะไกล คู่มือนี้รวบรวมการใช้งานทุกเมนูบน Dashboard
- สมัคร & เข้าสู่ระบบ ที่ apiota.net → ได้แผน Free อัตโนมัติ
- สร้าง Provisioning Key (เมนู 🔑 Provisioning) — กุญแจให้อุปกรณ์ลงทะเบียนตัวเองอัตโนมัติ
- ฝังโค้ด APIOTA library ลง ESP32 (ดูหัวข้อ ESP32 Integration) → อุปกรณ์โผล่ในเมนู 📟 Devices เอง
- อัปโหลดเฟิร์มแวร์ (เมนู 💾 Firmware) → อุปกรณ์เช็คเจอเวอร์ชันใหม่และอัปเดตเอง
| กลุ่ม | เมนู | ใช้ทำอะไร |
|---|---|---|
| Main | 🏠 Dashboard | ภาพรวม: จำนวนอุปกรณ์ออนไลน์, OTA ล่าสุด, การใช้โควต้า |
| 📁 Projects | จัดกลุ่มงานเป็นโปรเจกต์ แยกเฟิร์มแวร์/อุปกรณ์ | |
| 📟 Devices | รายการอุปกรณ์ + รายละเอียด + 🖥 Console | |
| 💾 Firmware | อัปโหลด/จัดการไฟล์ .bin + ช่อง stable/beta/dev | |
| Management | 🗂 Groups | จัดอุปกรณ์เป็นกลุ่ม สั่ง OTA ทีละกลุ่ม |
| ⚙️ Device Config | ตั้งค่า key-value ถาวรให้อุปกรณ์ดึงไปใช้ | |
| 🔔 Webhooks | ส่ง event ออกไปยังระบบอื่น (Discord/LINE/HTTP) | |
| 📋 OTA Logs | ประวัติทุกขั้นตอนการอัปเดต | |
| 🔑 Provisioning | สร้าง ak_ key ให้อุปกรณ์ลงทะเบียนอัตโนมัติ | |
| ⏱ Device Lifetime | กำหนดอายุการรับ OTA ของอุปกรณ์ | |
| มุมขวาบน | 👤 Account | แผน, วันหมดอายุ, เติมคูปอง, API keys, โควต้า |
โปรเจกต์ใช้แยกงานออกจากกัน เช่น "ป้ายไฟสาขา A" กับ "เซนเซอร์โรงงาน B" — เฟิร์มแวร์ อุปกรณ์ และคีย์ จะผูกกับโปรเจกต์ ทำให้ไม่ปนกัน
- เมนู 📁 Projects → ปุ่ม + New Project
- ตั้งชื่อ + คำอธิบาย → Save
- เวลาอัปโหลดเฟิร์มแวร์/สร้างคีย์ ให้เลือกโปรเจกต์นี้เพื่อจัดกลุ่ม
Provisioning Key (ขึ้นต้น ak_) คือกุญแจที่ฝังในเฟิร์มแวร์ ทำให้อุปกรณ์ "สมัครตัวเอง" ตอนเปิดเครื่องครั้งแรก โดยไม่ต้องตั้งค่าทีละตัว — เหมาะกับการ flash อุปกรณ์จำนวนมากด้วยไฟล์เดียว
- เมนู 🔑 Provisioning → + New Key → ตั้งชื่อ + เลือก Group/Project (ถ้าต้องการ)
- คัดลอกค่า
ak_xxxxxxxxxxxxที่ได้ - ใส่ในโค้ด:
APIOTA.begin("ak_xxxxxxxxxxxx", "1.0.0", "MyDevice") - อุปกรณ์เปิดครั้งแรก → ลงทะเบียนเอง → โผล่ในเมนู Devices
อุปกรณ์จะโผล่ที่นี่อัตโนมัติเมื่อ provision สำเร็จ แต่ละแถวบอกสถานะออนไลน์ (last seen), เวอร์ชันเฟิร์มแวร์, และปุ่มจัดการ
| สถานะ | ความหมาย |
|---|---|
| active | ทำงานปกติ รับ OTA ได้ |
| locked | ล็อกชั่วคราว — ไม่รับ OTA/คำสั่ง |
| inactive | ปิดใช้งาน |
| verified | อนุมัติแล้ว (หลัง approve อุปกรณ์ที่รอตรวจ) |
- คลิกอุปกรณ์ → ดูรายละเอียด (เวอร์ชัน, IP, last seen, โปรเจกต์/กลุ่ม)
- ปุ่ม 🖥 Console → เปิด Device Console สั่งงาน/ดู telemetry (ดูหัวข้อ Device Console)
- ปุ่มจัดการสถานะ: Lock / Unlock / Approve / Inactive
- ลบอุปกรณ์ที่ไม่ใช้แล้วเพื่อคืนโควต้า
รวมอุปกรณ์หลายตัวเป็นกลุ่ม เพื่อสั่ง OTA หรือจัดการพร้อมกัน เช่น กลุ่ม "สาขากรุงเทพ" แยกจาก "สาขาเชียงใหม่"
- เมนู 🗂 Groups → + New Group → ตั้งชื่อ
- เพิ่มอุปกรณ์เข้ากลุ่ม (หรือผูกตั้งแต่ตอนสร้าง Provisioning Key)
- ปล่อยเฟิร์มแวร์/ตั้งค่าให้ทั้งกลุ่มทีเดียว
หัวใจของ APIOTA: อัปโหลดไฟล์ .bin ครั้งเดียว อุปกรณ์ทุกตัวที่ตรงเงื่อนไขจะอัปเดตเอง
- เมนู 💾 Firmware → + Upload Firmware
- เลือกไฟล์
.bin+ กรอก Version (เช่น1.2.0) - เลือก Channel: stable (ทุกเครื่อง) / beta / dev (เฉพาะเครื่องทดสอบ)
- เลือก Project (ถ้ามี) → Upload
อุปกรณ์เรียก check-update เป็นช่วงๆ → ถ้าเจอเวอร์ชันใหม่กว่า จะดาวน์โหลด, ตรวจลายเซ็น RSA + SHA256, แล้ว flash + reboot อัตโนมัติ — ทุกขั้นถูกบันทึกใน OTA Logs
ใช้รูปแบบ MAJOR.MINOR.PATCH เช่น 1.0.9 → 1.1.0 — อุปกรณ์อัปเดตเฉพาะเมื่อเวอร์ชันบนเซิร์ฟเวอร์ "ใหม่กว่า" ที่ตัวมันมี
บันทึกทุกขั้นตอน OTA ของอุปกรณ์ทุกตัว ใช้ไล่ดูว่าใครอัปเดตสำเร็จ/ล้มเหลวตรงไหน
pending → checking → downloading → verifying → flashing → rebooting → done (หรือ failed)
| กลุ่มสถานะ | หมายถึง |
|---|---|
| done (เขียว) | อัปเดตสำเร็จครบขั้นตอน |
| downloading/flashing (ฟ้า) | กำลังทำงาน |
| failed (แดง) | ล้มเหลว — ดู message ประกอบ |
- กรองตามอุปกรณ์/สถานะ/ช่วงเวลา
- คลิกแถวเพื่อดู from_version → to_version + ข้อความ error
- ปุ่ม Clear ล้าง log เก่า
เก็บค่า key-value ให้อุปกรณ์ดึงไปใช้ เช่น setpoint อุณหภูมิ, รอบการทำงาน — แก้บน Dashboard แล้วอุปกรณ์ดึงค่าใหม่โดยไม่ต้องอัปเดตเฟิร์มแวร์ ค่าจะอยู่ถาวร (ต่างจาก Console ที่เป็นคำสั่งครั้งเดียว)
- เมนู ⚙️ Device Config → เลือกอุปกรณ์มุมขวาบน
- กด + Add Key → ใส่ชื่อคีย์ + ค่า (เช่น
temp = 50) - กด Save Config
- อุปกรณ์ดึงค่าผ่าน
GET /api/device/config(library ทำให้อัตโนมัติ)
ฝั่งอุปกรณ์อ่านค่า:
HTTPClient http; http.begin(String(OTA_SERVER) + "/api/device/config"); http.addHeader("X-Device-ID", DEVICE_ID); http.addHeader("X-Device-Secret", DEVICE_SECRET); if (http.GET() == 200) { DynamicJsonDocument doc(1024); deserializeJson(doc, http.getString()); JsonObject config = doc["config"]; int temp = config["temp"] | 0; // ค่า default หลัง | } http.end();
ให้ APIOTA ยิงแจ้งเตือนไปยัง URL ที่คุณกำหนด เมื่อเกิดเหตุการณ์ เช่น OTA สำเร็จ/ล้มเหลว หรืออุปกรณ์ออฟไลน์ — ต่อเข้ากับ Discord, LINE Notify, หรือระบบของคุณเองได้
- เมนู 🔔 Webhooks → + Add Webhook
- ใส่ Target URL + เลือก event ที่ต้องการ
- ทดสอบด้วยปุ่ม Test → ตรวจว่าปลายทางได้รับ payload JSON
/api/webhook/...) ซึ่งนับเป็น 📡 Realtimeกำหนดว่าอุปกรณ์จะรับ OTA ได้ถึงเมื่อไหร่ เหมาะกับงานเช่า/สัญญาบริการ — พอหมดอายุ อุปกรณ์จะหยุดรับอัปเดตและ library จะเรียก callback onExpired
- เมนู ⏱ Device Lifetime → เลือกอุปกรณ์
- ใช้ Quick Set (เช่น +30 วัน) หรือ Set Lifetime กำหนดวันเอง
- ระบบ ⚠️ แจ้งเตือนเมื่อใกล้หมดอายุ
APIOTA.onExpired([](APIOTAExpiry reason, const String& msg){ // APEX_LIFT_TIME = หมดอายุการรับ OTA // แสดง "หมดอายุ" หรือหยุดทำงานบางส่วน });
ESP32 ยืนยันตัวตนด้วย device_id และ device_secret ที่ได้รับตอน register · การลงทะเบียนอุปกรณ์ใหม่ใช้ Provisioning Key (ak_) ที่สร้างจาก Dashboard
ใส่ device_id และ device_secret เป็น query parameter หรือ request body ในทุก API call
// ตัวอย่าง ESP32 Arduino String url = "https://apiota.net/api/check-update"; url += "?device_id=" + String(DEVICE_ID); url += "&version=" + String(CURRENT_VERSION); url += "&device_secret=" + String(DEVICE_SECRET);
สำหรับ register อุปกรณ์ใหม่อัตโนมัติ ใช้ ak_ key ที่สร้างจาก Dashboard
// Provision device ด้วย ak_ key POST /api/device/provision { "api_key": "ak_xxxxxxxxxxxx", "device_id": "esp32-prod-001", "device_name": "MyDevice", "version": "1.0.0", "chip_info": "ESP32-D0WD" }
Endpoints ทั้งหมดที่ให้บริการ แบ่งตามหมวดหมู่
ตรวจสอบว่า server ทำงานปกติ ไม่ต้องการ authentication
{
"status": "ok",
"version": "3.13.0",
"time": "2026-07-01T10:30:00Z",
"db": "sqlite",
"uptime": 86400
}อุปกรณ์ใช้ endpoint นี้เพื่อตรวจว่ามีเฟิร์มแวร์เวอร์ชันใหม่หรือไม่ ESP32 ควร poll ทุก 5–15 นาที
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | string | required | Device ID ที่ลงทะเบียนไว้ |
| version | string | required | Firmware version ปัจจุบัน เช่น 1.0.9 |
| device_secret | string | required | Secret key ของอุปกรณ์ |
// มี update ใหม่ { "update": true, "version": "1.1.0", "url": "https://apiota.net/api/firmware/fw-1.1.0.bin", "sha256": "a3f8b2c1...", "size": 524288, "channel": "stable" } // ไม่มี update { "update": false }
อัปโหลดไฟล์ .bin เฟิร์มแวร์ ส่งเป็น multipart/form-data (ต้องมี JWT Bearer token)
| Field | Type | Required | Description |
|---|---|---|---|
| firmware | file (.bin) | required | ไฟล์เฟิร์มแวร์ .bin |
| version | string | required | Version string เช่น 1.2.0 |
| channel | string | optional | stable / beta / dev (default: stable) |
| project_id | string | optional | โปรเจกต์ที่เชื่อมโยง |
# curl ตัวอย่าง curl -X POST https://apiota.net/api/user/firmware/upload \ -H "Authorization: Bearer <JWT>" \ -F "firmware=@firmware-1.2.0.bin" \ -F "version=1.2.0" \ -F "channel=stable"
ดาวน์โหลดไฟล์เฟิร์มแวร์ binary โดยตรง ESP32 ใช้ URL นี้ใน OTA update process
| Parameter | Type | Description |
|---|---|---|
| filename | string | ชื่อไฟล์ .bin ที่ได้รับจาก check-update |
GET /api/firmware/fw-1.1.0-stable.bin
→ Returns: application/octet-streamลงทะเบียนอุปกรณ์ใหม่ด้วย provisioning key ใช้ใน ESP32 ตอน boot ครั้งแรก
| Field | Type | Required | Description |
|---|---|---|---|
| api_key | string | required | Provisioning key (ak_...) |
| device_id | string | required | Device ID ไม่ซ้ำกัน |
| device_name | string | required | ชื่ออุปกรณ์ |
| version | string | optional | Firmware version ปัจจุบัน |
| chip_info | string | optional | ข้อมูลชิป เช่น ESP32-D0WD |
{
"success": true,
"device_id": "esp32-prod-001",
"device_secret": "generated_by_server",
"registered_at": "2026-05-17T10:30:00Z"
}ESP32 ส่ง status update ระหว่าง OTA process มี 8 สถานะ: pending → checking → downloading → verifying → flashing → rebooting → done / failed
| Field | Type | Required | Description |
|---|---|---|---|
| device_id | string | required | |
| device_secret | string | required | |
| status | string | required | OTA status ปัจจุบัน |
| from_version | string | optional | Version เดิม |
| to_version | string | optional | Version ใหม่ |
| message | string | optional | ข้อความเพิ่มเติม / error message |
ดึง JSON config ที่ตั้งค่าไว้สำหรับอุปกรณ์นั้น
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | string | required | |
| device_secret | string | required |
{
"config": {
"interval": 5000,
"log_level": "info",
"retry_count": 3
},
"updated_at": "2026-05-17T08:00:00Z"
}ตัวอย่าง code สำหรับ Arduino / ESP-IDF เพื่อ integrate กับ APIOTA
ไลบรารี APIOTA เป็น open-source บน GitHub — โหลดฟรี ติดตั้งใน Arduino IDE ได้เลย · ในรีโปมีตัวอย่างพร้อมใช้: BasicOTA, LILYGO_TDisplayS3 (จอ TFT), LILYGO_TSIM_A7670G (4G/LTE), ClearNVS (ล้างค่า)
⬇️ github.com/devjune-apiota/apiota-esp32
วิธีที่ 1 — Add .ZIP (ง่ายสุด): เปิดหน้า GitHub → ปุ่มเขียว Code ▾ → Download ZIP → ใน Arduino IDE: Sketch → Include Library → Add .ZIP Library… → เลือกไฟล์ ZIP ที่โหลดมา
วิธีที่ 2 — Git clone: clone เข้าโฟลเดอร์ Arduino/libraries/ โดยตรง (ตั้งชื่อโฟลเดอร์ว่า APIOTA):
cd ~/Documents/Arduino/libraries
git clone https://github.com/devjune-apiota/apiota-esp32.git APIOTA เปิดตัวอย่าง: File → Examples → APIOTA → เลือก BasicOTA หรือรุ่นบอร์ดของคุณ → ใส่ Provision Key (ak_xxxx จาก Dashboard → Provisioning) + ssid/password → กด Upload · เสร็จแล้วดูโค้ด "OTA in 3 lines" ด้านล่าง
💡 อัปเดตไลบรารีเวอร์ชันใหม่: ดู Releases ในหน้า GitHub แล้วโหลด ZIP ใหม่มาทับ (หรือ git pull ในโฟลเดอร์ที่ clone ไว้)
วิธีง่ายสุด: ติดตั้ง APIOTA library แล้วเรียกแค่ connectWiFi() + begin() — ไลบรารีจัดการ provision, OTA, RSA/SHA verify, TLS (ISRG Root X1 CA), command long-poll และ log ทาง Serial ให้ครบ
#include <APIOTA.h> APIOTAClient APIOTA; void setup() { Serial.begin(115200); APIOTA.connectWiFi("ssid", "password"); APIOTA.begin("ak_xxxxxxxx", "1.0.0", "MyDevice"); } void loop() { APIOTA.tick(); delay(1000); }
ไลบรารี log ทุก step ทาง Serial ให้อยู่แล้ว — เพิ่ม callback เหล่านี้เมื่ออยากให้อุปกรณ์ "ทำอะไรเพิ่ม" ตอนเกิด event (คุมฮาร์ดแวร์/จอ) — วางใน setup() ก่อน APIOTA.begin()
| Callback | ทำงานเมื่อ | ใช้ทำอะไร |
|---|---|---|
onProvisioned(id, secret) | provision สำเร็จ | เก็บ id / แสดงสถานะ |
onProgress(state, pct, msg) | สถานะ OTA เปลี่ยน (checking→downloading%→flashing→success) | โชว์ progress บนจอ/LED |
onCommand(cmd, payload, id) | server ส่งคำสั่งมา (Dashboard/Telegram/webhook) | คุมรีเลย์/ไฟ, reboot, คำสั่งเอง |
onError(msg) | เกิด error | log / แจ้งเตือน |
onExpired(reason, msg) | server สั่งหยุด (Lifetime/Working หมดอายุ, เกิน plan, ถูก block) | แสดง "หมดอายุ" / หยุดทำงาน |
APIOTA.onCommand([](const String& cmd, const String& payload, uint32_t id){ if (cmd == "led_on") digitalWrite(2, HIGH); if (cmd == "led_off") digitalWrite(2, LOW); }); // reboot / check_update ไลบรารีจัดการให้เอง — อย่าเรียก ackCommand() ซ้ำ APIOTA.onExpired([](APIOTAExpiry reason, const String& msg){ // APEX_LIFT_TIME / APEX_WORKING / APEX_PLAN_LIMIT / APEX_BLOCKED digitalWrite(2, HIGH); });
setCheckInterval(sec) ตั้งรอบเช็ค OTA · setInsecure() ปิด TLS verify (เฉพาะเน็ตที่ block NTP) · ปกติไลบรารี verify cert ด้วย ISRG Root X1 CA + sync เวลา NTP อัตโนมัติ
// config.h — ตั้งค่าหลัก #define DEVICE_ID "esp32-prod-001" #define DEVICE_SECRET "your_device_secret" #define CURRENT_VERSION "1.0.9" #define OTA_SERVER "https://apiota.net" #define OTA_INTERVAL 300000 // 5 นาที (ms)
void checkOTA() { HTTPClient http; String url = String(OTA_SERVER) + "/api/check-update"; url += "?device_id=" + String(DEVICE_ID); url += "&version=" + String(CURRENT_VERSION); url += "&device_secret=" + String(DEVICE_SECRET); http.begin(url); int code = http.GET(); if (code == 200) { DynamicJsonDocument doc(1024); deserializeJson(doc, http.getString()); if (doc["update"] == true) { String fwUrl = doc["url"]; sendOTALog("downloading", doc["version"]); performOTA(fwUrl); } } http.end(); }
void sendOTALog(String status, String toVer = "") { HTTPClient http; http.begin(String(OTA_SERVER) + "/api/device/ota-log"); http.addHeader("Content-Type", "application/json"); DynamicJsonDocument body(256); body["device_id"] = DEVICE_ID; body["device_secret"] = DEVICE_SECRET; body["status"] = status; body["from_version"] = CURRENT_VERSION; if (toVer.length() > 0) body["to_version"] = toVer; String out; serializeJson(body, out); http.POST(out); http.end(); }
Device Console คือช่องสื่อสาร 2 ทางระหว่าง Dashboard กับอุปกรณ์: ↓ ส่งคำสั่ง (reboot, เปิด/ปิดไฟ, คำสั่งเอง) ไปยังอุปกรณ์ทันที และ ↑ รับ Telemetry (heap, RSSI, uptime, ค่าใดๆ) ที่อุปกรณ์รายงานกลับขึ้นมาโชว์เป็นการ์ด — คนละกลไกกับ Device Config (ที่เก็บค่า "ถาวร") แต่ใช้คู่กันได้
X-Device-ID + X-Device-Secret เหมือนกัน · Send Command + Device Config = ทุกแผน (รวม Free — จำกัดด้วยโควต้า 📡 Realtime + Config Keys ต่อแผน) · Console + Telemetry = แผน Starter+ · Outbound Webhooks (แท็บ 📤 ในเมนู Webhooks) = Pro+เข้า Dashboard → เมนู อุปกรณ์ (Devices) → คลิกที่อุปกรณ์ที่ต้องการ → กดปุ่ม 🖥 Console → หน้าต่าง Console จะเด้งขึ้นมา ประกอบด้วย 3 ส่วน:
| ส่วน | คืออะไร | ใช้ยังไง |
|---|---|---|
| การ์ดด้านบน | ค่า Telemetry ล่าสุดที่อุปกรณ์ส่งมา (heap_kb, rssi, uptime_min, …) | กด + การ์ด เพื่อเลือกคีย์ที่อยากปักหมุดโชว์ · กด ↻ อัตโนมัติ ให้รีเฟรชเอง |
| ไทม์ไลน์กลาง | ประวัติ 2 ทาง: ↓ คำสั่งที่ส่งออก · ↑ ค่าที่อุปกรณ์รายงาน | ติ๊ก "ทุกจุด" เพื่อดูทุก event · กดถังขยะเพื่อล้าง |
| แถบล่าง | ปุ่มลัด + ช่องพิมพ์คำสั่งเอง + ช่อง JSON payload | กดปุ่มลัด (reboot/check_update) หรือพิมพ์คำสั่งเอง + payload แล้วกด ▸ ส่ง |
พิมพ์ชื่อคำสั่งในช่องซ้าย เช่น set_led และใส่ payload เป็น JSON ในช่องขวา เช่น {"value":1} แล้วกด ▸ ส่ง · ปุ่ม 📌 ใช้ปักคำสั่งที่ใช้บ่อยให้เป็นปุ่มลัด · เบื้องหลัง Dashboard จะยิง:
ส่งคำสั่งเข้าคิว อุปกรณ์จะได้รับผ่าน long-poll · ต้องแผน Starter ขึ้นไป (คำสั่งจัดการสถานะ lock/unlock/approve ใช้ได้ทุกแผน)
// Request body { "command": "set_led", "payload": { "value": 1 } } // Response → { command_id, queued: true }
ถ้าใช้ APIOTA library แค่ใส่ callback onCommand — library จัดการ long-poll + ack ให้เอง (reboot/check_update ทำให้อัตโนมัติ ไม่ต้อง ack ซ้ำ)
APIOTA.onCommand([](const String& cmd, const String& payload, uint32_t id){ DynamicJsonDocument p(128); deserializeJson(p, payload); if (cmd == "set_led") digitalWrite(2, p["value"] | 0); if (cmd == "brightness") analogWrite(2, p["value"] | 0); // reboot / check_update → library ทำให้เอง });
3 ขั้น: (1) long-poll รับคำสั่ง → (2) ทำงาน → (3) ack ผลกลับ · poll จะ block ~30 วินาที (ได้คำสั่งทันทีหรือคืน 204 เมื่อหมดเวลา) แล้ววนใหม่
// (1) long-poll รับคำสั่ง HTTPClient http; http.begin(String(OTA_SERVER) + "/api/device/poll"); http.addHeader("X-Device-ID", DEVICE_ID); http.addHeader("X-Device-Secret", DEVICE_SECRET); http.setTimeout(35000); // server hold ~30s int code = http.GET(); if (code == 200) { // 204 = ไม่มีคำสั่ง DynamicJsonDocument doc(1024); deserializeJson(doc, http.getString()); long id = doc["command_id"]; String cmd = doc["command"].as<String>(); JsonObject pl = doc["payload"]; if (cmd == "set_led") digitalWrite(2, pl["value"] | 0); // ... จัดการคำสั่งอื่น ... ackCommand(id, "done", "ok"); // (3) ตอบผล } http.end(); // (3) ฟังก์ชัน ack void ackCommand(long id, String status, String result) { HTTPClient h; h.begin(String(OTA_SERVER) + "/api/device/command-ack"); h.addHeader("X-Device-ID", DEVICE_ID); h.addHeader("X-Device-Secret", DEVICE_SECRET); h.addHeader("Content-Type", "application/json"); DynamicJsonDocument b(256); b["command_id"] = id; b["status"] = status; // "done" | "failed" b["result"] = result; String out; serializeJson(b, out); h.POST(out); h.end(); }
อุปกรณ์ส่งค่าใดๆ ใน data{} ขึ้นมา → ค่าจะโผล่เป็นการ์ดใน Console (กด + การ์ด เลือกคีย์) · ต้องแผน Starter+ (ไม่งั้นได้ 403) · ส่งถี่เกิน 60 ครั้ง/นาที จะโดน 429
HTTPClient http; http.begin(String(OTA_SERVER) + "/api/device/telemetry"); http.addHeader("X-Device-ID", DEVICE_ID); http.addHeader("X-Device-Secret", DEVICE_SECRET); http.addHeader("Content-Type", "application/json"); DynamicJsonDocument doc(512); // doc["lat"]=13.75; doc["lon"]=100.50; // GPS (ถ้ามี) JsonObject d = doc.createNestedObject("data"); d["heap_kb"] = ESP.getFreeHeap() / 1024; d["rssi"] = WiFi.RSSI(); d["uptime_min"] = millis() / 60000; d["ver"] = "1.0.1"; String out; serializeJson(doc, out); http.POST(out); // 200 ok | 403 ต้อง Starter+ | 429 ถี่เกิน http.end();
poll เป็นหัวใจของ loop() (มันรอ ~30s ในตัว) แล้วแทรกการดึง Device Config + ส่ง Telemetry เป็นช่วงๆ — ตัวอย่างเต็มใน loop เดียวดูได้ที่ไฟล์ examples/APIOTA_FullExample.inoAPI ใช้ HTTP status codes มาตรฐาน response body มี error field อธิบายปัญหา
| HTTP Code | Error | Description |
|---|---|---|
| 200 | OK | สำเร็จ |
| 400 | Bad Request | ขาด parameter หรือ format ไม่ถูกต้อง |
| 401 | Unauthorized | device_secret ไม่ถูกต้อง หรือ device_id ไม่พบ |
| 403 | Forbidden | เกินขีดจำกัดของแผน หรืออุปกรณ์ถูกบล็อก |
| 404 | Not Found | ไม่พบ resource ที่ขอ |
| 413 | Payload Too Large | ไฟล์เฟิร์มแวร์ใหญ่เกิน limit ของแผน |
| 429 | Rate Limited | ส่ง request เร็วเกินไป รอ 60 วินาทีแล้วลองใหม่ |
| 500 | Server Error | Server error ติดต่อ support |
// ตัวอย่าง Error response { "error": "DEVICE_NOT_FOUND", "message": "device_id not registered", "status": 401 }