2. ünite / 10
Wi-Fi: bağlanmak, kopmak, yeniden bağlanmak
İstemci (STA) ve erişim noktası (AP) kipleri, WiFi.status() durumları, olaylar, RSSI ve üstel geri çekilmeyle bloklamayan yeniden bağlanma.
Bu ünitenin sonunda
- STA, AP ve AP+STA kiplerini ve kullanım yerlerini ayırt etmek
- WiFi.status() durum kodlarını okuyup hatanın nedenini bulmak
- Bloklamayan ve üstel geri çekilmeli bir yeniden bağlanma döngüsü yazmak
- RSSI değerine göre bağlantının sağlığını yorumlamak
Ders notları
Bir IoT cihazı aylarca açık kalır. Bu sürede modem yeniden başlar, elektrik kesilir, komşunun ağı kanalı doldurur. İyi bir cihaz Wi-Fi'nin kopacağını varsayar ve kendi kendine toparlanır; kötü bir cihaz while döngüsünde sonsuza kadar bekler.
Kipler
- STA (istemci): cihaz evdeki modeme bağlanır. Sensör düğümlerinin olağan kipi.
- AP (erişim noktası): cihaz kendi ağını yayar. İlk kurulumda telefonla bağlanıp Wi-Fi parolasını girdiğin sayfa böyle çalışır.
- AP+STA: ikisi birlikte; kurulum sırasında hem sayfayı sunar hem ağı dener.
Durumlar
WiFi.status() bir durum kodu döndürür: WL_CONNECTED bağlı ve IP almış, WL_IDLE_STATUS deneme sürüyor, WL_DISCONNECTED bağlı değil, WL_NO_SSID_AVAIL ağ adı görünmüyor, WL_CONNECT_FAILED parola ya da kimlik doğrulama hatası. Durum kodunu günlüğe yazmak, "Wi-Fi çalışmıyor" şikâyetini "SSID görünmüyor" ya da "parola yanlış" tanısına çevirir. Arduino çekirdeği ayrıca WiFi.onEvent() ile bağlanma, IP alma ve kopma olaylarını bildirir.
Yeniden bağlanma: sabit bekleme mi, geri çekilme mi?
Elektrik kesintisinden sonra binadaki yüz cihaz aynı saniyede açılır. Hepsi her saniye yeniden denerse modem ve broker gereksiz yük altında kalır. Üstel geri çekilmede her başarısız denemeden sonra bekleme iki katına çıkar ve bir üst sınırda durur; küçük bir rastgele pay (jitter) cihazların aynı anda denemesini önler.
Bekleme delay() ile değil, millis() karşılaştırmasıyla yapılır: döngü dönmeye devam eder, sensör okunur, yerel röle kuralları çalışır. Wi-Fi yokken de cihaz işini yapmalıdır.
RSSI
Alınan sinyal gücü dBm cinsindendir ve negatiftir. Kabaca: -50 çok iyi, -67 akış için yeterli, -75 sensör verisi için sınırda, -85 ve altı sık kopma demektir. Anteni metal kutunun içine gömmek ya da kartı toprağa yakın koymak RSSI'yi 10–20 dB düşürebilir.
Simülasyon
Wi-Fi bağlantı durumları ve yeniden bağlanma
Erişim noktası bir süre kapanır, sinyal zayıflar. Sabit beklemeyle ve üstel geri çekilmeyle yeniden bağlanmayı karşılaştır: deneme sayısı, kopuk kalınan süre ve AP geri gelince toparlanma süresi.
- bağlı
WL_CONNECTED - bağlanıyor
WL_IDLE_STATUS - bekliyor
WL_DISCONNECTED - AP yok
WL_NO_SSID_AVAIL
Kod
#include <WiFi.h>
#include "secrets.h" // WIFI_SSID, WIFI_PASS; dosya .gitignore'da
const uint32_t BASE_MS = 1000, CAP_MS = 30000;
uint32_t nextTry = 0;
uint8_t failures = 0;
uint32_t backoffMs(uint8_t k) {
uint32_t w = BASE_MS << min<uint8_t>(k, 15); // base, 2·base, 4·base…
w = min(w, CAP_MS);
return w / 2 + random(w / 2); // sarsıntı (jitter): cihazlar aynı anda denemesin
}
void wifiTask() { // loop() içinden çağrılır, asla beklemez
wl_status_t s = WiFi.status();
if (s == WL_CONNECTED) { failures = 0; return; }
if (millis() < nextTry) return;
Serial.printf("status=%d, try %u\n", s, failures + 1);
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASS);
nextTry = millis() + backoffMs(failures++);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA); // istemci kipi; AP kipi kurulum sayfası içindir
WiFi.setAutoReconnect(false); // yeniden bağlanmayı biz yönetiyoruz
}
void loop() {
wifiTask();
if (WiFi.status() == WL_CONNECTED) {
// sensör oku, yayımla; RSSI -80 dBm altındaysa uyar
if (WiFi.RSSI() < -80) Serial.println("weak signal");
}
}import network, time, random
from secrets import SSID, PASSWORD # Wokwi'de SSID = "Wokwi-GUEST", parola boş
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
def connect(base=1, cap=30):
k = 0
while not wlan.isconnected():
wlan.connect(SSID, PASSWORD)
t0 = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), t0) < 5000 and not wlan.isconnected():
time.sleep_ms(100)
if wlan.isconnected():
break
print('status', wlan.status()) # STAT_NO_AP_FOUND, STAT_WRONG_PASSWORD…
w = min(cap, base * 2 ** k)
time.sleep(w / 2 + random.random() * w / 2)
k += 1
print('ip', wlan.ifconfig()[0], 'rssi', wlan.status('rssi'))
connect()Kendin dene
Simülasyonda erişim noktasını 20. ile 60. saniye arasında kapat. Önce sabit 1 saniyelik beklemeyi seç: kaç deneme yapıldı, kopuk kalınan süre ne kadar? Sonra üstel geri çekilmeye geç (taban 1 s, üst sınır 30 s): deneme sayısı nasıl değişti, AP geri gelince toparlanma süresi ne oldu? Sinyali -88 dBm'e indir ve kopma sayısını izle. Son olarak Wokwi'de SSID olarak "Wokwi-GUEST" ile iki kodu çalıştır ve seri monitördeki durum kodlarını oku.
Sık yapılan hatalar
- setup() içinde while (WiFi.status() != WL_CONNECTED) ile sonsuza kadar beklemek: ağ gelmezse cihaz hiçbir iş yapmaz.
- Tüm cihazlara aynı sabit yeniden deneme süresi vermek: kesintiden sonra hepsi aynı anda dener.
- Wi-Fi parolasını kaynak koda yazıp depoya göndermek: secrets.h dosyası .gitignore'a girmeli.
- Yalnız "bağlı mı" diye bakıp durum kodunu günlüğe yazmamak: sahada hatanın nedeni bulunamaz.
- ESP32'nin 5 GHz ağa bağlanabileceğini sanmak: yalnız 2,4 GHz destekler.
Yazılım mühendisliği bağlantısı
Wi-Fi bağlantısı bir durum makinesidir: bağlanıyor, bağlı, bekliyor, AP yok. Onu açıkça modellemek test edilebilir yapar.
- Durum geçişlerini saf bir fonksiyona koy: girdi şimdiki durum, zaman ve olay; çıktı yeni durum ve bir sonraki deneme zamanı. Simülasyon tam olarak budur.
- Zamanı dışarıdan ver (
millis()yerine parametre): birim testinde 10 dakikalık kesintiyi milisaniyede benzetirsin. - Gözlemlenebilirlik: deneme sayısı, son durum kodu ve RSSI periyodik olarak yayımlansın; filo panosunda zayıf cihazlar görünür.
Kendini sına
Taban 1 s, üst sınır 30 s olan üstel geri çekilmede 6. başarısız denemeden sonra (k = 5) bekleme kaç saniyedir (sarsıntısız)?
Üstel geri çekilme ve sarsıntının asıl amacı nedir?
Seri monitörde WL_NO_SSID_AVAIL görüyorsun. Bu ne demektir?
RSSI -88 dBm ölçülüyor. Bu ne anlama gelir?
