🧩 ساخت API با JSON روی ESP32 با کتابخانهی ArduinoJson
تا اینجا ESP32 به مرورگر یک صفحهی HTML کامل میداد. ولی اپلیکیشنهای گوشی، داشبوردها و برنامههای دیگر HTML نمیخواهند؛ داده میخواهند. زبان مشترک همهی این برنامهها JSON است. در این قسمت از آموزش ESP32 یک API کوچک میسازیم و آن را، مثل پروژههایی که در ووکوی دیدهای، در شبیهساز ESP32 فارسی امتحان میکنیم.
JSON و API یعنی چه؟
JSON یک قالب سادهی متنی برای داده است، مثل {"temp": 24.5, "led": true}: هر داده یک اسم دارد و یک مقدار. API هم یعنی چند آدرس مشخص که برنامهها میتوانند از آنها چیزی بخواهند یا دستوری بدهند. API ما دو آدرس دارد:
GET /api/statusوضعیت دما، رطوبت، چراغ و زمان روشن بودن برد را برمیگرداند.POST /api/ledیک JSON مثل{"on": true}میگیرد و چراغ را روشن یا خاموش میکند.
قطعهها و سیمکشی
- ESP32
- LED با مقاومت ۲۲۰ اهم روی GPIO4 (پایهی کوتاه به GND)
- DHT11: VCC به 3V3، DATA به GPIO15، GND به GND
کد کامل
این کد با ArduinoJson نسخهی ۷ نوشته شده است:
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <DHT.h>
const int LED = 4;
bool ledOn = false;
DHT dht(15, DHT11);
WebServer server(80);
const char page[] PROGMEM = R"rawliteral(<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>
<body style="font-family:sans-serif;text-align:center">
<h2>ESP32 API</h2>
<button onclick="setLed(true)">LED ON</button>
<button onclick="setLed(false)">LED OFF</button>
<pre id="s">...</pre>
<script>
function load() {
fetch('/api/status').then(function (r) { return r.json(); })
.then(function (d) { document.getElementById('s').innerText = JSON.stringify(d, null, 2); });
}
function setLed(on) {
fetch('/api/led', { method: 'POST', body: JSON.stringify({ on: on }) }).then(load);
}
load();
setInterval(load, 3000);
</script></body></html>
)rawliteral";
void sendJson(int code, JsonDocument& doc) {
String out;
serializeJson(doc, out); // object -> text
server.send(code, "application/json", out);
}
void handleStatus() {
JsonDocument doc; // ArduinoJson 7
float t = dht.readTemperature();
float h = dht.readHumidity();
if (isnan(t)) doc["temp"] = nullptr; else doc["temp"] = t;
if (isnan(h)) doc["hum"] = nullptr; else doc["hum"] = h;
doc["led"] = ledOn;
doc["uptime"] = millis() / 1000;
sendJson(200, doc);
}
void handleLed() {
JsonDocument in;
JsonDocument res;
DeserializationError err = deserializeJson(in, server.arg("plain")); // text -> object
if (err) {
res["error"] = err.c_str();
sendJson(400, res);
return;
}
ledOn = in["on"] | false; // default false if "on" is missing
digitalWrite(LED, ledOn ? HIGH : LOW);
res["led"] = ledOn;
sendJson(200, res);
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
dht.begin();
WiFi.begin("Wokwi-GUEST", ""); // simulator network, no password
while (WiFi.status() != WL_CONNECTED) delay(200);
Serial.println(WiFi.localIP());
server.on("/", []() { server.send(200, "text/html", page); });
server.on("/api/status", HTTP_GET, handleStatus);
server.on("/api/led", HTTP_POST, handleLed);
server.begin();
}
void loop() {
server.handleClient();
}
توضیح بخشهای مهم
JsonDocument doc;یک «سند» خالی JSON میسازد. در نسخهی ۷ اندازهاش خودکار تنظیم میشود. اگر نسخهی ۶ داری، بهجایش بنویسStaticJsonDocument<256> doc;.doc["temp"] = t;مثل پر کردن یک فرم است: اسم و مقدار.nullptrدر خروجی بهnullتبدیل میشود تا برنامهی گیرنده بفهمد سنسور جواب نداده است.serializeJson(doc, out);سند را به متن JSON تبدیل میکند وdeserializeJsonبرعکس، متن رسیده را به سند.server.arg("plain")بدنهی درخواست POST است؛ همان متنی کهfetchفرستاده.in["on"] | falseیعنی «مقدار on را بده، اگر نبود false». این عملگر|در ArduinoJson معنی «مقدار پیشفرض» دارد.- تابع کمکی
sendJsonنمیگذارد کد تکراری بنویسیم و نوع جواب راapplication/jsonمیگذارد. - صفحهی
R"rawliteral(...)rawliteral"یک «رشتهی خام» است: HTML را بدون نگرانی از کوتیشنها داخلش مینویسیم.
چرا API بهتر از صفحهی آماده است؟
وقتی برد فقط داده بدهد و ظاهر را به مرورگر بسپارد، کارها از هم جدا میشوند. همین آدرس /api/status را هم صفحهی وب برد میخواند، هم یک اپلیکیشن گوشی که بعداً میسازی، هم یک برنامهی پایتون روی کامپیوتر که دادهها را در فایل ذخیره کند. اگر روزی بخواهی ظاهر صفحه را عوض کنی، به کد بخش سنسور دست نمیزنی. سایتهای بزرگ هم دقیقاً همینطور کار میکنند: یک بخش داده میدهد و بخش دیگر آن را زیبا نشان میدهد.
"rssi" با مقدار WiFi.RSSI() به پاسخ وضعیت اضافه کن و ببین قدرت سیگنال هم در صفحه ظاهر میشود.امتحانش کن
دو نسخهی آماده در تینکرکد فارسی داری: API با JSON روی ESP32 (ArduinoJson) و نسخهی درسی API سنسور با JSON. خواندن کد رایگان است و اجرای ESP32 با اشتراک Plus. شبیهساز ArduinoJson را میشناسد و صفحهی برد را در مرورگر مجازی خودش باز میکند. توضیح بیشتر در درس «دادهی JSON».
اشکالهای رایج
- خطای «JsonDocument does not name a type»: ArduinoJson نسخهی ۶ نصب است. یا به ۷ بهروز کن یا از
StaticJsonDocumentاستفاده کن. - جواب ۴۰۰ با «InvalidInput»: JSONی که فرستادهای خراب است؛ مثلاً کوتیشن تکی بهجای دوتایی گذاشتهای.
JSON.stringifyاین را درست انجام میدهد. - «EmptyInput»: درخواست را با GET فرستادهای یا بدنهاش خالی است.
- دما null است: سیم DATA سنسور را چک کن؛ API درست کار میکند ولی سنسور جواب نمیدهد.
این API روی همان پروژهی صفحهی وب دما و رطوبت سوار شده است. اگر میخواهی داده بدون پرسیدن پشتسرهم برسد، سراغ WebSocket برو. معرفی کتابخانهها هم در صفحهی کتابخانهها است.
API جیسون ESP32 را باز کن 🧩