Okullar için Robotik ve Mühendislik · anaokulundan liseye

3. ünite / 10

HTTP, REST ve JSON: ESP32 bir web sunucusu

Yollar, yöntemler ve durum kodlarıyla ESP32 üzerinde küçük bir REST API: JSON gövde doğrulama, idempotentlik, belirteç ve istemci tarafı.

Bu ünitenin sonunda

  • Bir HTTP isteğinin ve yanıtının parçalarını (yöntem, yol, başlık, gövde, durum kodu) okumak
  • GET, POST, PUT ve DELETE yöntemlerini idempotentlik açısından karşılaştırmak
  • Doğru durum kodunu seçmek: 200, 201, 204, 400, 401, 404, 405, 422
  • ESP32'de ArduinoJson ile JSON üreten ve doğrulayan bir uç nokta yazmak

Ders notları

HTTP, tarayıcının web sayfasını çektiği protokoldür; ESP32 üzerinde de aynı protokolle küçük bir sunucu çalıştırabilirsin. İstemci (telefon, tarayıcı, başka bir cihaz) bir istek gönderir, sunucu bir yanıt döndürür. Bağlantı istek bitince kapanabilir: HTTP istek-yanıt modelidir, sunucu kendiliğinden konuşmaz.

Bir isteğin anatomisi

  • Yöntem ve yol: PUT /api/led. Yol bir kaynağı (LED), yöntem o kaynağa ne yapılacağını söyler.
  • Başlıklar: Content-Type: application/json, Authorization: Bearer … gibi anahtar-değer satırları.
  • Gövde: JSON metni, örneğin {"on": true}.
  • Durum kodu: yanıtın ilk satırı. 2xx başarı, 4xx istemci hatası, 5xx sunucu hatası.

REST: kaynaklar ve yöntemler

REST bir kuraldan çok bir üsluptur: her şey bir kaynaktır, yöntemler standarttır. GET okur ve hiçbir şeyi değiştirmez. PUT kaynağı verilen değere ayarlar; iki kez göndermek bir kez göndermekle aynı sonucu verir, yani idempotenttir. POST yeni bir kaynak oluşturur; iki kez göndermek iki zamanlama yaratır. DELETE siler ve o da idempotenttir. Ağ kötüyken istemci isteği yeniden gönderebileceği için bu ayrım önemlidir.

idempotent: f(f(x)) = f(x)    GET, PUT, DELETE evet; POST hayır

Durum kodları

Bu ünitenin sunucusu şunları döndürür: 200 OK başarılı okuma ya da güncelleme, 201 Created ve Location başlığı yeni zamanlama, 204 No Content başarılı silme, 400 Bad Request bozuk JSON, 401 Unauthorized belirteç yok ya da yanlış, 404 Not Found böyle bir yol ya da kayıt yok, 405 Method Not Allowed ve Allow başlığı yöntem desteklenmiyor, 422 Unprocessable Entity JSON geçerli ama alanlar anlamsız. Doğru kod, istemcinin hatayı otomatik olarak ayırt etmesini sağlar.

JSON ve bellek

ArduinoJson ile JsonDocument oluşturur, serializeJson ile metne çevirirsin. Gelen gövdeyi deserializeJson ile ayrıştırırken dönen hatayı mutlaka denetle ve alan türünü (is<bool>()) doğrula: cihazına gelen her bayt güvenilmezdir.

Simülasyon

REST oyun alanı: ESP32 web sunucusu

Seradaki ESP32'nin küçük REST API'sine istek gönder: GET /api/sensor, PUT /api/led, POST /api/schedules. Ham HTTP isteğini ve yanıtını, durum kodlarını (200, 201, 400, 401, 404, 405, 422) ve JSON gövdeyi gör.

Ham HTTP isteği burada görünür.
Ham HTTP yanıtı burada görünür.
Cihaz durumu: LED kapalı · zamanlamalar: –
  1. –Henüz istek yok

Kod

Arduino C++: WebServer + ArduinoJson ile /api/led
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include "secrets.h"                // WIFI_SSID, WIFI_PASS

WebServer server(80);
const int LED_PIN = 2;
bool ledOn = false;

void sendJson(int code, JsonDocument& doc) {
  String body;
  serializeJson(doc, body);
  server.send(code, "application/json", body);
}

void getLed() {
  JsonDocument doc;
  doc["on"] = ledOn;
  sendJson(200, doc);
}

void putLed() {
  JsonDocument in, out;
  DeserializationError e = deserializeJson(in, server.arg("plain"));
  if (e) { out["error"] = e.c_str(); return sendJson(400, out); }          // JSON bozuk
  if (!in["on"].is<bool>()) { out["error"] = "on must be true or false"; return sendJson(422, out); }
  ledOn = in["on"];                 // mutlak değer: PUT idempotent kalır
  digitalWrite(LED_PIN, ledOn);
  getLed();                         // yeni durumu döndür
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);
  server.on("/api/led", HTTP_GET, getLed);
  server.on("/api/led", HTTP_PUT, putLed);
  server.onNotFound([] { server.send(404, "application/json", R"({"error":"not found"})"); });
  server.begin();
}

void loop() { server.handleClient(); }
MicroPython istemci: urequests ile GET ve PUT
import urequests as requests
import ujson
from secrets import TOKEN               # belirteç kodda değil

BASE = 'http://sera.local/api'           # Wi-Fi bağlantısı önceki ünitedeki connect() ile
H = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + TOKEN}

r = requests.get(BASE + '/sensor')
print(r.status_code, r.json())           # 200 {"temp": 22.4, "hum": 54.1}
r.close()                                # soketi bırak: ESP32'de sayısı sınırlı

r = requests.put(BASE + '/led', data=ujson.dumps({'on': True}), headers=H)
if r.status_code == 200:
    print('LED:', r.json()['on'])
elif r.status_code in (400, 422):
    print('bad request:', r.json()['error'])
elif r.status_code == 401:
    print('token missing or wrong')
r.close()

Kendin dene

REST oyun alanında önce GET /api/sensor gönder ve ham yanıtı oku. PUT /api/led ile {"on": true} gönder; sonra aynı isteği ikinci kez gönder: durum değişti mi? Gövdeyi {"on": "evet"} yap: hangi kod döndü? PUT /api/status dene ve Allow başlığına bak. POST /api/schedules ile iki kez {"at":"07:30","on":true} gönder: kaç kayıt oluştu, Location başlığı ne? Belirteç zorunluluğunu açıp belirteçsiz PUT gönder. Ardından Wokwi'de WebServer kodunu çalıştır.

Sık yapılan hatalar

  • Her hataya 200 dönüp gövdeye "error" yazmak: istemci başarı ile başarısızlığı durum kodundan ayıramaz.
  • Durumu değiştiren işlemi GET ile yapmak (GET /led/toggle): tarayıcı önbelleği ya da bir tarayıcı eklentisi LED'i kendiliğinden çevirebilir.
  • deserializeJson hatasını denetlemeden alanları okumak: bozuk gövde sessizce yanlış değer üretir.
  • urequests yanıtını kapatmamak (r.close()): birkaç istekten sonra soketler biter.
  • Belirteci URL'ye koymak (?token=...): günlüklerde ve tarayıcı geçmişinde açıkta kalır, Authorization başlığı kullan.

Yazılım mühendisliği bağlantısı

API bir sözleşmedir: istemci ve sunucu ayrı ayrı geliştirilir, sözleşmede buluşur.

  • Sözleşmeyi yaz: yollar, yöntemler, gövde şeması ve hata kodları (OpenAPI dosyası ya da tek sayfalık bir tablo).
  • Sözleşme testleri: her yol için "geçerli istek 2xx, bozuk JSON 400, eksik alan 422, yanlış yöntem 405" sınamalarını otomatikleştir. Simülasyonun handle fonksiyonu saf olduğu için bu testler tarayıcısız çalışır.
  • Sürümleme: alan eklemek geriye uyumludur, alan silmek değildir; kırıcı değişiklikte /api/v2 aç.

Kendini sına

  1. POST /api/schedules başarıyla yeni bir zamanlama oluşturdu. En uygun durum kodu hangisidir?

  2. Sunucu /api/status için yalnız GET destekliyor. PUT /api/status gelince ne döndürmeli?

  3. PUT /api/led gövdesi {"on": "evet"}. JSON geçerli ama alan türü yanlış. Hangi kod?

  4. Hangi yöntem idempotent değildir?

Bu üniteden sonra bitirme projesinde

  • Gereksinim ve pin planı: G1–G8'i kendi serana uyarla, her biri için ölçülebilir kabul ölçütü yaz; iki düğümün pin planını planlayıcıda hatasız çıkar ve REST ile yerel durum sayfası tasarla.

Parçalar ve ilgili sayfalar

Esc
  • PanelAna sayfaSayfa
  • ModüllerTüm modüllerSayfa
  • ParçalarParça ansiklopedisiSayfa
  • Mühendislik yoluBüyükler için robotik: 128 parça, 7 seviyeSayfa
  • Robotik dersi (ESOF 323)Üniversite robotik dersi, 14 haftaSayfa
  • Drone dersiBüyükler için: parça, simülasyon, yazılım; 10 üniteSayfa
  • Blok atölyesiSerbest blok programlamaAraç
  • Devre laboratuvarıSanal Arduino ve devreAraç
  • Robot arenasıSanal robot sürüşüAraç
  • OyunlarBloklarla 8 öğretici oyunAraç
  • Mikro Kart atölyesiSanal LED kartı bloklarla programlaAraç
  • Robot atölyesiMikro Kart'ı robota tak, bloklarla görev yapAraç