Raspberry Pi'den TCP ile gelen JSON hata veriyor
Raspberry Pi'den TCP ile gelen JSON yer istasyonunda Extra data hatası veriyorsa paketler yapışıyordur. Satır sonuyla çerçevele, tampondan satır satır çöz.
Raspberry Pi'deki kod Pixhawk'tan konumu okuyor, her ölçümü JSON yapıp TCP ile yer istasyonuna yolluyor. İki program aynı bilgisayarda çalışırken veri sorunsuz akıyor. Yer istasyonuna harita çizimi ekleyince ya da Pi'yi Wi-Fi'ye bağlayınca alıcı birkaç paketten sonra hata verip kapanıyor.
Kabloyu ve Wi-Fi'yi suçlamadan önce TCP'nin ne yaptığına bak: baytları sırayla teslim eder ama hangi baytın hangi pakete ait olduğunu bilmez. Paketin nerede bittiğini işaretlemek gönderen ile alıcının işidir.
Masada çalışıyor, sahada neden bozuluyor
Yer istasyonu her recv çağrısında o ana kadar biriken baytların hepsini alır. Alıcı iki okuma arasında hiç beklemiyorsa her recv genellikle tek paket getirir. Masada sorunu bu yüzden görmezsin.
Pi saniyede 50 paket gönderirken alıcı her okumadan sonra 0,1 saniye haritayı çizerse tek recv'e çoğunlukla beş paket düşer. Bekleme 0,5 saniyeye çıkınca bu sayı yirmi beşe çıkar.
Şekilde üst şeritte Pi'nin tek tek gönderdiği paketleri, ortadaki şeritte yer istasyonunun okumalarını gör: ikinci recv no 1'den no 5'e kadar beş paketi tek parça hâlinde alıyor.
Pi her paketi ayrı gönderiyor, yer istasyonu iki recv arasında beşini biriktiriyor
Teknofest Savaşan İHA'daki tipik kurulumu düşün. Pi kamerada bulduğu hedefin koordinatını ve drone'un irtifasını yollar, yer istasyonu ikisini haritaya işler. Harita her yenilendiğinde alıcı bir süre recv çağırmaz; paketler o arada birikir ve ilk birleşik okumada program düşer.
Hata metni sana ne söylüyor
json.loads metindeki ilk JSON nesnesini çözer, sonra metnin bitip bitmediğine bakar. Arkada bayt kaldıysa Extra data hatası verir. Yani ilk paket eksiksiz gelmiştir; sorun iki paketin arasındaki sınırdadır.
Parantezdeki char sayısı ikinci paketin başladığı yeri gösterir. char 66 görüyorsan ilk paket satır sonuyla birlikte 66 karakter tutuyor.
recv bir paketi ortasından da kesebilir: paket recv'in bir kerede verdiği bayt sayısını aşarsa ya da baytlar ağdan parça parça gelirse. O zaman hata metni değişir. Kesik yer bir metin değerinin içindeyse Unterminated string, virgülden hemen sonraysa Expecting property name enclosed in double quotes görürsün.
Satır sonu eklemek tek başına yetmiyor
İlk akla gelen düzeltme Pi'de her paketin sonuna satır sonu (\n) eklemek. Alıcı her recv'i hâlâ tek paket sayıyorsa hata yine gelir, yalnızca yeri değişir: Extra data: line 2 column 1 (char 66). Satır sonu, alıcı onu arayıp baytları orada böldüğünde işe yarar.
Çözüm: paketi satır sonuyla bitir, alıcıda satır satır böl
Pi tarafında iki satırı değiştir: paketin sonuna \n ekle, send yerine sendall çağır. send baytların bir kısmını gönderip dönebilir; sendall hepsi gidene kadar dener.
satir = json.dumps(paket) + "\n" # mesaj sınırı: satır sonu
yki.sendall(satir.encode("utf-8")) # send değil sendall: hepsi gitsinYer istasyonunda gelen baytları bir tampona ekle, tamponda satır sonu buldukça oradan kes ve o satırı çöz. Yarım kalan satır tamponda bekler, bir sonraki recv onu tamamlar.
# Yer istasyonu tarafı: TCP akışını satır satır böl, her satırı ayrı JSON olarak çöz
import json, socket
sunucu = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sunucu.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sunucu.bind(("0.0.0.0", 5005))
sunucu.listen(1)
baglanti, adres = sunucu.accept()
print("Pi bağlandı:", adres)
tampon = b""
while True:
parca = baglanti.recv(4096)
if not parca: # 0 bayt: Pi bağlantıyı kapattı
print("bağlantı kapandı"); break
tampon += parca
while b"\n" in tampon: # tamponda kaç tam satır varsa hepsini işle
satir, tampon = tampon.split(b"\n", 1)
if not satir.strip():
continue
paket = json.loads(satir)
print(paket["no"], paket["alt"], paket["mod"])
# yarım kalan satır tamponda bekler, sonraki recv onu tamamlarNe göreceksin
Önce yer istasyonunda yki_al.py'yi, sonra Pi'deki kodu çalıştırırsan her paket kendi satırında gelir:
Pi bağlandı: ('127.0.0.1', 34394)
0 -0.01 STABILIZE
1 -0.01 STABILIZE
2 -0.01 STABILIZE
3 -0.01 STABILIZEAlıcı her okumadan önce 2 saniye başka iş yapsa da numaralar atlamaz. recv bu durumda 4096 baytı doldurup bir satırın ortasından keser; tampon kesik satırı bir sonraki okumayla birleştirir ve sıra 0'dan 635'e kadar kesintisiz gider.
Hata mesajları
json.decoder.JSONDecodeError: Extra data: line 1 column 82 (char 81)
İki ya da daha fazla paket tek recv'de geldi, ilk paketten sonra metin bitmedi. Paketleri satır sonuyla bitir, alıcıda tampondan satır satır çöz.
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 63 (char 62)
recv paketi bir metin değerinin ortasından kesti. Tampon yöntemi kesik parçayı bir sonraki okumayla birleştirir.
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
json.loads'a boş veri gitti. recv 0 bayt döndürdüyse karşı taraf bağlantıyı kapatmıştır; çözmeye çalışmadan döngüden çık.
ConnectionResetError: [Errno 104] Connection reset by peer
Bu hata Pi tarafında çıkar: yer istasyonundaki program kapandı, çoğu zaman yukarıdaki JSON hatalarından biriyle. Önce yer istasyonundaki hatayı düzelt, sonra Pi'deki kodu yeniden başlat.
Eğitimde devamı
Raspberry Pi'den yer istasyonuna veri iletimini, seri port ve TCP/UDP haberleşmesini İHA Haberleşme Teknolojileri eğitiminde adım adım anlatıyoruz. Yeni eklenen derste kesilen satırın tamponda nasıl birleştiğini bayt bayt gösteriyoruz; kayıp ve bozuk paketi sayan alıcı kodu ile Pixhawk'ın TELEM2 ayarları da orada.
Bu yazı işinize yaradı mı?
Bu konuyu baştan sona öğrenin
Sezon: altı eğitimin hepsi 9 ay, ayda ₺790. Lise ve üniversite öğrencisine ayda ₺390, üye kodunuzla ilk ay ₺351. İstediğiniz ay bırakırsınız. Sezon'a başlayın · Öğrenciyim