Raspberry Pi Pico ve ESP8266 Kullanarak TCP Client Yapımı
|Merhaba,
Bu konuda Raspberry Pi Pico’ yu ESP8266 kullanarak internete bağlayacağız. Bilgisayarda çalışan bir TCP server’ a veri gönderip, TCP server’ dan gelen veriye göre LED yakıp göndereceğiz. Tüm bu işlemler için MicroPython kullanacağız. Ancak ESP8266 ile iletişimde kullanacağımız AT iletişim komutlarının düzgün şekilde gönderilmesi ve gelen cevapların ayrılması işlemi biraz karmaşıklığa sebep olmaktadır. Bu sebeple bu konu içeriği başkangıç seviyedeki kişiler için uygun olmayabilir.
Şimdi konya giriş yapalım. Daha önceki konularda da ESP8266′ nın AT komutları kullanımına değinmiştik. Bu konuda AT komutları üzerine fazla durmayacağım. Kullanacağımız AT komutlarının listesine ve kullanım şekillerine bağlantıdan ulaşabilirsiniz. Espressif tarafından paylaşılan farklı bir dökümanda AT komutları ile yapılmış bazı uygulamalar için kullanılması gereken komutlara ve sırasına yer verilmiştir. Bu dökümana bağlantıdan ulaşabilirsiniz. Ancak burada dikkat edilmesi gereken ufak bir nokta mevcut. Bu dökümanlarda verilen tüm komutları kullanabilmek için ESP8266′ nın AT firmware versiyonu 3 ve üzeri olmalıdır. ESP8266′ yı satın aldığınızda muhtemelen firmware versiyonu bu olmayacaktır. Bu sebeple yeni aldığınız ESP8266 için firmware güncellemesi yapmanız gerekebilir.
Şimdi elimizdeki ESP8266′ nın AT firmware versiyonunda doğru olduğunu varsayarak konuya devam edelim. Konu içerisinde oluşturacağımız TCP iletişim hikayemiz şu şekilde olsun. Pico ADC’ den ölçtüğü veriyi TCP server’ a göndersin. TCP server’ da belirli zaman aralıkları ile bağlanan client’ e yani Pico’ ya led yakıp söndürme komutu göndersin.
AT Komutları
ESP8266 ile TCP bağlantı sağlamak için bazı komutlar göndermeliyiz. Bu komutlar sırası ile şu işlemleri gerçekleştirmelidir.
- ESP8266 Station Mod’ a getirilmeli
- ESP8266 yakındaki Wi-Fi ağlarını taramalı
- ESP8266 Wi-Fi ağına bağlanmalı
- ESP8266 çoklu bağlantı ayarı ihtiyaca göre ayarlanmalı
- ESP8266 ile TCP haberleşme başlatılmalı
- ESP8266 ile TCP soket üzerinden iki yönlü haberleşme gerçekleştirilmeli
Şimdi bu yapacağımız işlemlerin AT komutu karşılıklarına bakalım. Verilen komutların detaylı kullanımına yukarıdaki AT instruction set içerisinden ulaşabilirsiniz. Ben bu uygulmaya için gerekli ayarlamaları yapacak AT komutlarını vereceğim.
- AT+CWMODE=1
- AT+CWLAP
- AT+CWJAP_CUR=<ssid>,<pswd>
- AT+CIPMUX=0
- AT+CIPSTART=”TCP”,<IP_ADRESS>,<PORT>
- AT+CIPSEND=<len>
Verilen komutlar arasındaki “AT+CWJAP_CUR” komutu, ESP8266′ nın bağlanacağı Wi-Fi ağını seçmemizi sağlayacaktır. Ancak sadece 1 kezlik bu bağlantıyı yapar. Yani ESP8266′ nın gücünü kesip tekrar güç verdiğimizde bu ağa tekrar bağlanmayacaktır. Her seferinde bu ağa bağlanmasını istiyorsak kullandığımı komutu “AT+CWJAP_DEF” ile değiştirmeliyiz. Bu sayede her seferinde ayar yapmamıza gerek kalmaz. Bu komutu her kullanmamızda önceki Wi-Fi bilgilerinin üzerine yazıldığını unutmamalıyız. Yani en son yaptığınız ayar ESP8266 hafızasında tutulacaktır. Buna benzer kullanım farklı komutlarda da bulunmaktadır. Örneğin baud rate ayarlamada kullanılan komut için de “AT+UART_CUR” ve “AT+UART_DEF” olarak farklı şekilleri vardır. Benzeri durum bu iki komut için de geçerlidir.
Projede kullanacağımı komutların sayısı 6 tane olduğu için basit gibi görünse de bunların gönderilmesi ve alınması sırasında yapılacak string işlemleri biraz kod kalabalığına sebep olmaktadır. Bunu ilerleyen kısımlarda daha iyi anlayacağınızı düşünüyorum. Mantık aslında Arduino’ da yapılan ile aynı sadece Python dili kullanılacak olması aradaki farkdır.
Bağlantı Şeması
Öncelikle bağlantı şemasını verelim. Yapacağımız projede iki potansiyometre, iki adet LED, ESP8266 ve Pico kullanacağız. Bu elemanları kullanarak yaptığım bağlantı şeması aşağıdaki gibidir. Pico üzerinde 5V çıkış için pin bulunmaktadır. ESP8266′ nın ve ADC’ nin doğru çalışabilmesi için 3.3V beslemeyi kullanmalıyız. Besleme pini seçimi yaparken buna dikkat etmelisiniz.
Yazılım
Sırada yazılım kısmı var. Yazılım için iki temel fonksiyon kullandım. Geri kalan fonksiyonlar arkada bu fonksiyonu kullanarak ESP8266 ile haberleşiyor. Bu fonksiyonlardan bir tanesi ESP8266′ ya uygun formatta veri gönderiyor, diğeri ise gelen cevabı ayırıp ona göre işlem yapmamızı sağlıyor. Öncelikle veriyi gönderme işlemini gerçekleştiren kısımdan başlayalım. Veri göndermede kullanacağımız yazılım aşağıdaki gibidir.
# send AT command and check response is same with parameter
def sendCMD_waitResp(cmd, res="OK", timeout=10000):
cmd = cmd + "\r\n" # add \n to end of the string
# if debug active print send command
if mDebug:
print(cmd)
pass
myUart.write(cmd) # send cmd to ESP8266
return waitResp(res, timeout) # wait cmd response
Hazırlanan fonksiyonda parametre olarak üç değer bulunmaktadır. Bunlardan ilki göndereceğimiz komut. String olaran vermemiz gereklidir. İkinci parametre ESP8266′ dan geri dönüş değeridir. Bu default parametre ile kullanılmıştır. Yani hiçbir şey girilmezse cevap olarak “OK” değerini gelen veriler arasında arar. Parametre olarak farklı değer verilerek farklı cevabın aranması sağlanabilir. Son parametre de zaman aşımı parametresidir. Default değeri 10 saniyedir. Bu süre içerisinde aranan cevap bulunamazsa bekleme işlemine son verilir.
Fonksiyon ilk olarak gelen komut parametresine “\r\n” ekler. Bu AT komutlarının ESP8266′ da doğru şekilde ayrılabilmesi için gereklidir. Daha sonra parametre olarak gelen veriyi UART üzerinden ESP8266′ ya gönderir ve cevabı beklemek için yeni fonksiyonu çağırır. Cevabı bekleyen fonksiyonun içeriği aşağıdaki gibidir.
# wait response function
def waitResp(res="OK", timeout=10000):
resp = []
byte_res = bytes(str.encode(res)) # convert res parameter to byte to compare
retVal = resp_temp
prvMills = time.ticks_ms() # save time for calculate timeout (1)
# wait until timeout expires
while (time.ticks_ms() - prvMills) < timeout:
# check uart buffer is empty
if myUart.any(): (2)
# read data until \n and append it to response array
cc = myUart.readline(100)
resp.append(cc)
# check if response array len bigger then 1 (3)
if len(resp) > 1:
# read response and check is match with parameter
if resp[-1].rstrip() == byte_res:
retVal["resp"] = AT_OK
break
# check response includes ERROR
elif resp[-1].rstrip() == b"ERROR":
retVal["resp"] = AT_ERROR
print("AT CMD ERROR")
break
# convert response to str
# converting string must start from array index 1 (4)
respStr = ""
for i in range(1, len(resp)):
respStr = respStr + resp[i].decode("utf-8")
# if debug is True print response
if mDebug:
print (resp)
# assign response value to return value (5)
retVal["cmd"] = respStr
return retVal
Bu kısım gönderen kısma göre biraz daha karmaşık. Çünkü ESP8266′ dan gelecek cevabı beklemeli ve gelen cevabı doğru şekilde ayırmalı. Ayrıca cevabı beklerken de verilen zaman aşımı süresinin geçilip geçilmediğini kontrol etmeli. Bu işlemlerin hepsi birleşinde yukarıdaki gibi bir yazılım ortaya çıkıyor. Şimdi adım adım kodu inceleyelim. Aslında yapılan işlemler yorum satırlarında verilmiş biraz daha detaylandıracağız sadece.
(1) – Fonksiyonun başında bazı ayarlamalar yapıldı. Bunların en önemlisi “prvMills” değişkenidir. Bu değişken yazılımın başındaki anı zaman olarak tutan değişkendir. Böylece fonksion içerisinde geçen zamanı hesaplayıp zaman aşımı olması durumunda fonksiyondan çıkabiliriz.
(2) – Daha sonrasında bir döngü oluşturulmuş ve zaman aşımı olana kadar dönecek şekilde ayarlanmıştır. Bu döngü içerisinde uart’ dan veri gelip gelmediğini kontrol edip yeni satır karekteri boyunca uart’ dan okuma yaptık. Burada yeni satır karakteri önemli. Özellikle AT komutu tabanlı haberleşmelerde giden ve gelen verilerin sonuna “\n\r” eklenir. Bu sayede alıcıda veriyi satır satır okumamız mümkündür.
(3) – AT komutlarına gelen cevaplar “OK” yada “ERROR” ile biter. Biz gelen cevap arasında bu karakterlere bakarak gelen cevabın sonunun geldiğini anlayabiliriz. Böylece zaman aşımı süresinin sonuna kadar beklememize gerek kalmaz ve yazılımı hızlıca ilerletebiliriz. Bu cevap gelene kadar alınan tüm veri bir listeye birleştirilir.
(4) – Gelen veriyi bir liste şeklinde ekliyoruz. Bu ekrana yazdırma sırasında hoş görünmemesine sebep olmakta. Ekrana yazdırırken düzgün görüntüleyebilmek için gelen tüm cevabı string’ e çevirip birleştiriyoruz.
(5) – Son olarak da aldığımız verileri fonksiyondan çıkarmak için bir değişkene ekleyip fonksiyondan geri döndürüyoruz.
Temelde kullandığımız iki fonksiyon bunlar. Bundan sonra kullanacağımız tüm fonksiyonlar içerisinde bu iki fonksiyonu çağıracak. Şimdi kullanacağımız diğer fonksiyonlara gelelim. Öncelikle Wi-Fi’ ye bağlanma fonksiyonunu inceleyelim. Göndermemiz gereken AT komutu aşağıdaki gibidir.
AT+CWJAP_CUR=”SSID”,”PSWD”
# connect WiFi with SSID and password
def connect_WiFi(pSSID, pPswd, timeout = 10000):
return sendCMD_waitResp("AT+CWJAP_CUR=\"" + pSSID + "\",\"" +
pPswd + "\"", timeout=timeout)
Wi-Fi’ ye bağlanmak için kullanmamız gereken AT komutu “AT+CWJAP_CUR” dir. Bu komut parametre alan bir komuttur. İki parametre alır. İlki bağlanacağımız Wi-Fi’ nin SSID’ si diğeri ise şifredir. Komut “CUR” ile bitmesinin ne anlama geldiğini daha önce açıklamıştık. Yani bu komutun “AT+CWJAP_DEF” olan çeşidi de mevcut. Eğer cihazı her zaman aynı Wi-Fi’ ye bağlayacaksak “DEF” ile biten komutu kullanabiliriz. Oluşturduğumuz fonksiyon da parametre olarak SSID ve şifre alıyor. Daha sonra bu değerleri AT formatına uygun bir şekilde birleştirerek ilk başta oluşturduğumuz “sendCMD_waitResp” fonksiyonunu çağırır. Bu fonksiyon Wi-Fi’ ye bağlanmak için gerekli isteği ESP8266′ ya gönderip gelen cevabı bize geri döndürür.
TCP haberleşme başlatmak için göndermemiz gerken AT komutu “AT+CIPSTART” dır. Bu komut üç adet parametre alır. İlki yapılacak soket bağlantısının TCP ya da UDP olduğunu belirtmek içindir. İkinci ve üçüncüsü bağlanacağımız IP adresi ve PORT’ dur. Göndermemiz gereken AT komutu şu şekilde olmalıdır:
AT+CIPSTART=”TCP”,”IP_ADRESS”,PORT”
# connect TCP server
def startTCP_connection(pIp, pPort, timeout = 10000):
cmd = sendCMD_waitResp('AT+CIPSTART="TCP","' +
pIp +
'",' +
str(pPort), timeout=timeout)
return cmd
TCP bağlantı fonksiyonu, Wi-Fi bağlantı fonksiyonu gibi basit bir yapıya sahip. Parametre olarak bağlantı için gerekli olan IP adresini ve PORT’ u alır. Yapacağımız bağlantı TCP olacağı için ilk parametre olan bağlantı tipini fonksiyon içerisinde TCP olarak yazdık. Fonksiyon içerisinde parametreleri AT formatına göre birleştirerek ESP8266′ ya gönderiyoruz. Bu fonksiyon TCP bağlantıyı başlatmada kullanılır. Başarılı bağlantı olması durumunda “OK” cevabı geri dönecektir. Bağlantı başarısız olursa “ERROR” cevabı geri dönecektir. Bu aşamadan sonra TCP server’ a veri göndermeye ve server’ dan veri almaya hazırız. Bağlantı sunucu tarafından kapatıldığı zaman UART’ dan “CLOSED” bilgisi gönderilecektir.
TCP server üzerinden veri gönderirken kullanmamız gereken AT komutu “AT+CIPSEND” dir. Bu komutun kullanımı öncekilerden biraz daha farklıdır. Bu komut tek parametre alır. Aldığı parametre gönderilecek verinin uzunluğudur. Biz bu komutu gönderdiğimizde ESP8266 bize cevap olarak “>” karakteri göndericektir. Bu karakteri aldığımız zaman TCP server’ a göndermek istediğimiz veriyi ESP8266′ ya karakter olarak göndermeye hazırız demektir. Bunu yazılımda şu şekilde oluşturabiliriz.
# send value over tcp connection
def sendTCP_value(pVal, timeout=10000):
# send
mResp = sendCMD_waitResp("AT+CIPSEND=" + str(len(pVal)), res='>', timeout=timeout)
if mResp["resp"] == AT_OK:
myUart.write(pVal)
else:
print("TCP Send Error!")
Oluşturduğumuz fonksiyon parametre olarak tek değer almakta. O da göndermek isterdiğimiz veridir. Üstte de belirtiğimiz gibi AT+CIPSEND komutuna ESP8266 cevap olarak ‘>’ değerini gönderecektir. Burada dikkat edilmesi gereken fonksiyon içerisinde çağırdığımız “sendCMD_waitResp” fonksiyonun “res” parametresidir. Bu parametre ile ESP8266′ dan beklediğimiz cevabı girebiliriz. Böylece beklediğimiz cevap geldiğinde ESP8266′ ya UART üzerinden göndermek istediğimiz veriyi gönderebiliriz. ESP8266′ da veriyi TCP server’ a gönderir.
Gönderme kısmını bu şekilde hallettik. Peki ya TCP Server’ dan gelecek veriyi nasıl alacağız. Bu kısım biraz daha farklı. Gelecek veriyi okumak için iki farklı yöntem mevcuttur. Bunun en çok bilinen aktif moddur. Diğeri ise pasif mod. Pasif modda gelen veri ESP8266 buffer’ ında tutulur. Veriyi okumak istediğimiz AT komut kullanarak bu buffer’ dan okuabiliriz. Aktif modda ise ESP8266 TCP’ den gelen veriyi UART üzerinden gönderir. UART’ dan veriyi gönderirken verinini başına “+IPD” eklenir. Böylece veriyi diğer AT komutlarına verilen cevaplardan ayırabiliriz. Ancak aktif yöntemi kullanıyorsak veri TCP server’ a bağlandıktan sonra herhangi bir zamanda gelebilir. Yani while döngüsü içerisinde sürekli olarak gelen veriyi kontrol etmeliyiz. Bunu basit olarak şu şekilde yapabiliriz.
# check incoming UART data
if myUart.any():
recData = []
while myUart.any():
recv = myUart.readline(100) # read data from UART
recData.append(recv) # append data to recieve buffer
if "+IPD" in str(recv): # check data includes +IPD
splitData = str(recv).split("|")
parseData = json.loads(splitData[1])
print(parseData["LED1"], " " , parseData["LED2"])
led2.value(int(parseData["LED1"]))
led3.value(int(parseData["LED2"]))
elif "CLOSED" in str(recv): # check daha includes CLOSED
tcpConnected = False
Yazılım içerisinde yaptığımız işlemleri anlamak basit. İlk olarak UART’ dan gelen veri varmı diye kontrol ettik. Eğer veri varsa satır sonu karakterine kadar okuduk ve bir dizide gelen verileri tuttuk. Daha sonra da gelen verinin içerisinde “+IPD” var mı diye kontrol ettik. Eğer varsa TCP server’ dan veri gelmiş demektir. Gönderdiğimiz formata uygun olarak veriyi ayırıp işlem yapabiliriz.
Son olarak küçük önemli bir nokta mevcut. Micropython’ ın UART sınıfı içerisindeki “readline” fonksiyonu timeout değerine sahip değil. Yani siz bu fonksiyonu çağırdığınız zaman yeni satır karakteri gelene kadar UART’ dan veri okumaya devam edecek. Bu durum da yazılımın o noktada takılı kalmasına sebep olacaktır. Aslında yazılımın normal çalışması sırasında bu şekilde bir sorun meydana gelmeyecektir. Ancak kaçan bir karakter yazılımın bu noktada takılı kalmasına ve sistemin sorun çıkarmasına sebep olacaktır. Bu sebeple bu fonksiyona bir timeout değeri eklemeliyiz. Bu sorun internet üzerinde bulduğum bir kaynakta çok başarılı bir şekilde çözülmüş. Bağlantıdaki yazılımı oluşturduğumuz Python dosyasının yanına koymalı ve Pico’ ya yüklemelisiniz.
Sonuç olarak Raspberry Pi Pico kullanarak bir TCP server’ a veri iletip veri aldık. Biraz uzun bir konu oldu. Pico yeni bir donanım olduğu için detay vermeye çalıştım. Umarım yardımcı olabilmişimdir. Yazılımın tamamına bağlantıdan ulaşabilirsiniz.
MERHABALAR WİFİ MODÜLÜ 3.3 V ÇALIŞIYOR PİCO DA ÖYLE DİRENÇLERİ KULLANMADAN YAPSAK OLUR MU
Merhaba, dirençler LED’ lerin çekeceği akımı sınırlamak için koyulmuş. Eğer onları koymazsanız LED’ lerin maksimum akımı kadar akım Pico vermeye çalışacaktır. 1,2 LED için çok sorun olmuyormuş gibi görünse de çok fazla LED bağladığınız zaman Pico’ ya zarar verecektir.
potansiyometreler kaç k vede dirençlerin değeri ne
Merhaba, potansiyometrenin değeri önemli değil. Çünkü gerilim bölücü olarak kullanılıyor. 1k yada 100k kullanabilirsiniz hangisi varsa elinizde. Dirençler ise üzerlerindeki renklerden de anlaşıldığı üzere 220Ohm olarak kullanabilirsiniz.