9

Uzaktan Gömülü Yazılım Güncelleme

Remote Firmware Update
    Gömülü sistemler için önemli konulardan birisi de yazılımın uzaktan güncellenebilmesidir. İçinde gömülü yazılım barındıran ürünler geliştiriyoruz ve bazen bu ürünlerdeki gömülü yazılımı, kodlardaki hatalardan veya müşteri isteğine göre yazılımsal eklemeler, geliştirmeler yaparak güncelleme ihtiyacı duyuyoruz. Böyle durumlarda geliştirdiğimiz yeni yazılımı ürünlerin başına giderek güncellemek doğru bir seçim olmaz. Kaldı ki bu ürünlerden binlerce satılmış olabilir hatta sadece yurt içi değil yurt dışına dahi gönderilmiş olabilir. Böyle durumlar için geliştirdiğimiz ürünün uzaktan güncellenebilir olması önemli bir özelliktir.
    Bu yazıda gömülü yazılımın uzaktan(kablosuz) nasıl güncelleneceği ile ilgili bir örnek uygulama paylaşacağım. Mikrodenetleyici olarak STM32F103C8T6(blue pill) ve internete bağlanıp yeni yazılımı indirebilmek için ESP8266‘yı kullanacağım. Bu uygulama örneği için değineceğimiz başlıca konu başlıkları aşağıdaki gibidir.
  • IAP(In Application Programming) ve Bootloader
  • TFTP Protokolü
  • İndirilen Yeni Firmware’in Çalıştırılması.
  • CRC (checksum)

IAP(In Application Programming) ve Bootloader

    İnternette çoğu kaynakta IAP ve Bootloader için aynı kavramlarmış gibi bahsedilir. Geneline baktığımızda ikisinin de yaptığı işlemler aynıdır. Sistem resetlendiğinde veya kullanıcı tarafından bir etki ile çalışıp yeni bir firmware güncellemesi olup olmadığına bakar. Eğer yeni bir güncelleme var ise yeni firmware’i indirip çalıştırır. Bazı kaynaklar bu işlemlerin tamamına IAP, bazı kaynaklar ise bootloader demiş. Bu yüzden ben IAP yazayım siz bootloader anlayın.
    IAP, mikrodenetleyicinin kendi kendini programlaması demektir. Bu programlamayı flash hafızaya uygulama kodlarını yazarak yapar. IAP normal  bir gömülü yazılım gibi çalışan bir uygulamadır aslında. IAP kodları da normal uygulama kodları gibi mikrodenetleyici üzerinde çalışır ve her iki uygulamanın kodu da mikrodenetleyicinin flash hafızasında tutulur. Mikrodenetleyici resetlendiğinde veya uygulama kodunun herhangi bir yerinde IAP kodları çalıştırılabilir. IAP kodları eğer yeni bir güncelleme isteği var ise yeni uygulama kodlarını flash hafızadaki belirlenen alana yazar. Genelde IAP kodları flash hafızanın ilk sayfalarına yazılır. Flash hafızanın geri kalan sayfalarına ise uygulama kodları yazılır. Böylece mikrodenetleyici resetlendiğinde ilk olarak IAP programı çalışır.
    Bu uygulamada IAP için Flash hafızada 15KB’lık bir alan ayrılmıştır. Yani IAP kodları Flash hafızada ilk 15KB lık alanda saklanacak. IAP kodlarını yazdıktan sonra IAP uygulamamın yaklaşık 11KB olduğunu gördüm. Bu yüzden uygulama başlangıç adresi biraz daha aşağı çekilerek uygulama için daha fazla alan ayrılabilir. IAP kodlarının görevi internetten yeni yazılımı indirip uygulama için ayrılan hafızaya yazmak olacak. Daha sonra eğer indirilen dosya doğru bir şekilde flash hafızaya yazılmış ise IAP uygulamasından yeni indirilen uygulamaya geçiş yapılacak. IAP uygulaması uzaktan bir komutla veya mikrodenetleyici resetlendiğinde çalıştırılabilir. Böylece sistem firmware güncellemesi yapabilir.

IAP ve APP Uygulamaları İçin Keil Ayarları

    IAP ve APP uygulamalarını iki farklı proje olarak oluşturalım. Önce IAP kodlarını yazıp mikrodenetleyiciye yükledikten sonra APP kodlarını da IAP kodlarını silmeden keil ile yüklemek isteyebiliriz. IAP kodlarını yazarken keil de herhangi bir değişiklik yapmadan kodları yazıp,derleyip mikrodenetleyiciye yükleyebiliriz fakat APP kodlarını yazarken keil de bazı ayarlar yapmamız gerekir.
APP projesi için; options for target -> target pencerisinde aşağıdaki değişlikleri yapmamız gerekir.
veya linker sekmesinden aşağıdaki gibi ayarlamalar yapılabilir.
    Bu aşamadan sonra APP uygulamasını yani mikrodenetleyici üzerinde çalışacak olan asıl yazılımı IAP kodlarını silmeden mikrodenetleyiciye keil ile yükleyebilirsiniz. Unutmadan şunu da belirtelim. options for target -> utilities -> settings -> flash download pencerisinde  “Erase Full Chip” kutucuğunun işaretli olmadığından emin olun. Aksi takdirde APP kodunu yüklerken IAP kodları silinir.

TrueSTUDIO veya CubeIDE İçin Linker Dosyası Ayarları

    Eğer APP projesini Atollic veya CubeIDE üzerinde geliştiriyorsak yukarıdaki ayarları linker dosyasında ufak değişiklikler ile yapabiliriz. Linker dosyası “.ld” uzantılı bir dosyadır. Bu dosya içinde mikrodenetleyicinin bellek bölümlerini gösteren aşağıdaki gibi kod bloğu vardır.
    Linker script içinde başlangıçta flash başlangıç adresi 0x08000000 olarak tanımlıdır. Bu satırı yukarıdaki gibi düzenleyebiliriz. Ayrıca vektör tablosunun offset değerini de belirtmemiz gerekir. Bunu APP uygulamasının içinde main fonksiyonunun en üstünde aşağıdaki gibi yapabiliriz.
veya system_stm32f1xx.c dosyası içindeki VEC_TAB_OFFSET tanımlamasını aşağıdaki gibi değiştirebiliriz.

Vektör tablosuna aşağıda tekrar değineceğiz.

Örnek Devre Şeması

    Bu IAP uygulaması için kurduğum örnek devre şeması yukarıdaki gibidir. Bu örnekte firmware dosyası UART1 üzerinden DMA ile alınıp flash hafızaya yazılacaktır. UART2 ise uzaktan güncelleme adımlarını gözlemleyebilmek için bir nevi komut satırı gibi kullanılmıştır. İndireceğimiz yeni firmware in çalışıp çalışmadığını görebilmek için Bluepill üzerindeki C13 ledi kullanılmıştır. Yani indireceğimiz APP uygulaması aslında bir led blink yapacaktır.
    Peki yeni yazılımı internetten nasıl indireceğiz? Bunun için dosya transfer protokollerinden olan TFTP‘ yi kullanacağım.

TFTP Protokolü

    Trivial File Transfer Protocol (TFTP) , UDP üzerinde çalışan basit bir dosya transfer protokolüdür. TFTP de dosya aktarımı, bir client TFTP server’a okuma veya yazma isteği gönderdiğinde başlar. Server dosya transferini onayladığında transfer başlar. Veriler sabit bloklar halinde gönderilir(örneğin 512 byte’lık bloklar). Transfer edilen her data bloğu alıcı tarafından onaylanmalıdır(acknowledge). Eğer sabit blok uzunluğundan daha az uzunlukta bir blok gelirse transferin son bloğu olduğu anlaşılır ve dosyanın tamamının alındığı anlamına gelir.
     Bu uygulamada bilgisayara bir TFTP server kurup STM32’den bir dosya okuma isteği(read request) göndereceğiz. Eğer dosya mevcut ise server bize dosyayı paketler halinde göndermeye başlayacak. Bu paketler ESP8266 üzerinden dolayısı ile UART1 üzerinden STM32’ye gelecek. IAP uygulamamız UART üzerinden okuduğu yeni firmware dosyasını flash belleğe yazacak.
    Server’a bağlanmak için IAP.c dosyası içinde aşağıdaki fonksiyonu oluşturuyorum.

    Server’a dosya okuma isteği göndermek için ise aşağıdaki fonksiyonu oluşturuyorum. TFTP server’a dosya okuma isteği gönderdiğimizde eğer dosya mevcut ise server bize dosyayı data paketleri halinde göndermeye başlar. Eğer dosya mevcut değil ise “file not found” şeklinde bir error paketi yollar.

TFTP Server

    TFTP server kurmak için Tftpd64  programını kullanacağım.  Öncelikle bahsetmek isterim ki kuracağımız server ile ESP8266 aynı ağa bağlı olacak. Farklı ağlar üzerinden uzaktan güncelleme yapmak için statik ip ve port yönlendirme ile ilgili ayarlar yapmak gerekmektedir.
    Program ilk açıldığında settings sekmesinden yukarıdaki ayarları yapıyoruz. Burada server için base directory den bir dosya yolu belirtmemiz gerek. Bunun için masaüstüne “upgrade folder” isminde bir yeni klasör oluşturuyorum. Güncellenecek olan yeni firmware bu dosya içerisinde bulunacak. Bir başka önemli ayrıntı ise IP kutucuğudur. Bu kutucuktan server’ın kurulu olduğu bilgisayarın IP’sini seçiyoruz.

Binary Dosyası ve Hex Dosyası

    İndireceğimiz dosya binary mi yoksa hex dosyası mı olmalı? Her ikisi de olabilir fakat hex uzantılı firmware dosyaları sadece program kodunu içermezler. Binary dosya ise sadece mikrodenetleyici üzerinde çalıştırılabilir binary dataları tutar. Yani hex dosyasını indirirsek doğrudan çalıştıramayız. Bu yüzden bu örnekte indireceğimiz dosya .bin uzantılı binary dosyası olacak. Binary dosyasının dezavantajı ise içerisinde checksum değeri barındırmamasıdır. Bu yazının asıl amacı basitçe bir güncelleme yapmak olduğu için binary dosyasını indirip doğrudan flash hafızaya yazacağım. Bu yüzden binary dosyasının sonuna checksum değerini python ile basit bir kod yazarak ekleyeceğim. Eğer .hex uzantılı dosya ile ilgilenseydik python ile checksum hesaplama ve dosya sonuna ekleme gibi ekstra işlemler yapmayacaktık çünkü hex dosyasının içinde checksum değeri vardır. Bu yüzden yazıyı daha da uzatmamak için hex yerine binary dosyayı kullanacağım. Checksum’ın ne olduğuna aşağıda tekrar değineceğiz.

Keil ile Binary Dosya Çıktısı Alma

    Keil de binary dosya çıktısı alabilmek için options for target -> user -> After Build/Rebuild -> Run #1 kutucuğunu işaretleyip, aşağıdaki komut yazılabilir.

TrueStudio ile Binary Dosya Çıktısı Alma

    TrueStudio ile binary dosya çıktısı almak için aşağıdaki adımlar takip edilmelidir.
Project -> Properties -> C/C++ Build -> Settings -> Tool Settings -> Output Format
    Atollic’te sadece bu ayarlar yeterli değildir. Atollic projeyi derledğimizde bize .binary uzantılı bir dosya çıkarır. Bu uzantıyı .bin olarak düzenleyip mikrodenetleyiciye yükleyebilirsiniz.

CRC(Checksum)

    Dosyayı indirip flasha yazdık peki dosyanın bozulmadan tam olarak alındığından nasıl emin olabiliriz? Bu gibi durumlarda veri doğrulama için checksum kullanılır. Cheksum ile bütün byte’lar belli matematiksel veya lojik işlemlerden geçirilerek bir checksum değeri üretilir. Eğer flash’a yazdığımız byte’ların checksum’ı ile binary dosyasının checksum’ı uyuşuyor ise binary dosya bozulmadan hafızaya yazılmış demektir.
    Binary dosyasının içinde sadece mikrodenetleyici tarafından çalıştırılacak byte’lar bulunur. Peki checksum’ı binary dosyaya nasıl ekleyeceğiz? Araştırmalarıma göre bu işlemi yapacak program yok denecek kadar az. Bu yüzden binary dosyasına checksum eklemek için kendimce yöntemler kullandım. Bunun için python da basit bir kod yazıp binary dosyasının cheksum’ını hesaplayıp tekrar aynı dosyanın sonuna eklemeyi düşündüm. Belki IDE lerin bunun için bir eklentisi vardır. Bununla ilgili farklı bir fikri olan veya bunun için eklenti bilen varsa yorumlarda belirtebilirseniz müteşekkir olurum.
    Checksum hesaplamak için farklı algoritmalar bulunmaktadır. Peki hangi algoritmayı seçeceğiz? Bunu STM32’ye göre belirleyeceğiz. STM32’ler de cheksum hesaplamak için CRC birimi bulunmaktadır. STM32F1 için reference manual’de CRC biriminin CRC-32 Ethernet standardına göre hesaplama yaptığını görebiliriz. Bu yüzden python tarafında binary dosyasının checksum’ını hesaplarken bu standarda göre hesaplattıracağız. Aksi takdirde python tarafında hesaplanan checksum ile stm32’nin hesapladığı checksum farklı çıkar.
    Python’da cheksum hesaplamak için yazmış olduğum kod aşağıdaki gibidir. Checksum hesaplamak için “libscrc” modülünü kullandım. Bu python kodu binary dosyasının sonuna “CRC” karakterlerini ve hemen ardından da dosyanın 32bitlik CRC’sini ekler.

    Serverdan binary dosyayı indirirken sonundaki CRC ile birlikle flash hafızaya yazıyorum. Flash hafızadan aşağıdaki fonksiyon ile binary dosyanın CRC’sini çekiyorum. Yukarıda farkettiyseniz 4 byte’lık CRC değerini little-endian formatta dosyanın sonuna ekledik. Çünkü çoğu ARM cortex M serisi işlemciler flash hafızaya little-endian formatta yazar. Örneğin; 0xA2FC değerini flash hafızaya 0xFC 0xA2 şeklinde kaydeder.
    Flash hafızadan CRC değerini okumak için aşağıdaki fonksiyonu oluşturuyorum.

    Binary dosyanın sonuna eklediğimiz CRC değerini bu fonksiyon ile okuduk. Şimdi STM32 ile CRC birimini kullanarak APP başlangıç adresinden bitiş adresine kadar her 32 bit lik değerleri CRC hesaplama işlemine sokacağız. Bunun için aşağıdaki fonksiyonu oluşturuyorum.

    Bu iki fonksiyonun döndürdüğü değerler eşit ise indirdiğimiz .bin uzantılı dosya bozulmadan hafızaya yazılmış demektir. Bu aşamadan sonra indirdiğimiz APP uygulamasını çalıştırabiliriz.

İndirilen Yazılıma Atlama(Jump Function)

    Öncelikle APP uygulamasına atlamak için kullandığımız kodları bir inceleyelim.

    İlk satırda if koşulu ile APP uygulamasının stack değeri kontrol edilir. Binary dosyasının en başında  vektör tablosu(vector table) bulunur ve aşağıda da görüldüğü gibi bu tablonun ilk 32 bitlik değeri başlangıç stack değerini gösterir(initial stack pointer).
    Burada if koşulu ile APP_START_ADDRESS adresinde bir uygulama var mı yok mu kontrol edilir. Peki bu if şartının sağlamasını nasıl yapabiliriz? initial sp değerini nasıl öğrenebiliriz?  Bunun için .map uzantılı dosyayı inceleyebilirsiniz.
    Yukarıda görüldüğü gibi APP uygulamamızın başlangıç stack pointer değerinin 0x20000410 olduğunu görebiliriz. if kouşulunun içindeki bitsel ve(bitwise and) işlemi yapıldığında sonucun 0x20000000 olduğu görülebilir. Bu değerin alabileceği belli bir aralık vardır o yüzden if koşulunun içinde 0x2FFE0000 ile bitwise and işlemine tutulur. Daha sonra aşağıdaki fonksiyon işaretiçisi(function pointer) tip tanımlaması yapılmıştır.

    Bu fonksiyon işaretçisi yeni indirdiğimiz uygulamayı sanki bir fonksiyonmuş gibi çalıştıracak.

    Yukarıdaki işlem ile fonksiyon pointerın adresini belirliyoruz. Burada dikkat ettiyseniz JumpAddress  APP başlangıç adresinin 4 fazlası olarak seçilmiştir. Tekrar vektör tablosuna dönüp baktığımızda bu adresin reset handler fonksiyonuna ait olduğunu görürüz. Yani Jump_To_Application() fonksiyonu çağrıldığında aslında program APP uygulamasının vektör tablosuna ait olan reset handler’ını çalıştıracaktır. Peki reset handler’ın içinde hangi işlemler yapılıyor bir bakalım. Bunun için startup_stm32f103xb.s assembly dosyasını açalım. Bu dosyanın içinde reset handler’ın hangi fonksiyonları çağırdığını görebiliriz.
    Yukarıda görüldüğü gibi stm32 ilk çalıştığında reset handler’a girer ve burada ilk olarak SystemInit fonksiyonu çağrılır. SystemInit fonskiyonu ise system_stm32f1xx.c dosyası içinde bulunur. Daha sonra APP uygulamasının main fonksiyonu çağrılır. Jump_To_Application() fonksiyonu çağrılmadan önce kesmelerin kapatılması önerilir. Bunun için aşağıdaki macro kullanılabilir.

    Bu makro NMI(Non-maskable interrupt) ve hard fault kesmesi hariç bütün kesmeleri kapatır. Burada dikkat etmemiz gereken önemli bir nokta var. Bu satırda kesmeler kapatıldığı için APP uygulamasına atlandığında da kesmeleriniz çalışmaz. Bu yüzden APP uygulamasının main fonksiyon içinde ilk olarak kesmeleri tekrar aktif etmelisiniz. Bunu aynı makroya parametre olarak sıfır değeri göndererek yapabiliriz. Aynı şekilde systick timerı da kapatıyoruz. Bunun için control registerına sıfır değerini yüklemeliyiz.

    Daha sonra main stack pointer register’ına APP uygulamasının başlangıç adresini(initial stack pointer) yüklüyoruz. Bu aşamadan sonra yeni indirilen uygulamaya atlayabiliriz.

IAP Akış Diyagramı ve Kodları

    Bu örnekte IAP kodlarını mikrodenetleyici resetlendiğinde çalıştırıcağız. Daha önce de dediğim gibi IAP kodları kablosuz olarak bir komut gönderilerekte çalıştırılabilir. APP uygulaması içinden IAP uygulamasına atlamak için yine aynı şekilde jump fonksiyonu kullanılabilir. Mikrodenetleyici resetlendiğinde bazı sebeplerden dolayı internete ve dolayısı ile server’a bağlanamayabilir. Böyle durumlarda hali hazırda yüklü olan APP uygulamasını çalıştırmak isteyebiliriz. Bu yüzden yukarıdaki diyagramda eğer servera veya internete bağlanma ile ilgili bir sorun oluşursa program indirme yapmadan doğrudan flash hafızadan CRC okuma ve flash hafızanın(APP) CRC’sini hesaplama işlemlerini yapacak. Bunun için kullanılan fonksiyonlar eğer indirme yapılmadı ise default olarak 0xFFFFFFFF değerini dönerler. Böylece eğer yeni firmware indirilemedi ise hali hazırda yüklü olan uygulama çalıştırılır.

Github Linki

Tüm kodlara github üzerinden erişmek için buraya tıklayabilirsiniz.

Eklenebilecek Ekstra Özellikler

Bu yazıda bir gömülü yazılımın uzaktan basitçe nasıl güncellenebileceğini anlatmaya çalıştım. Bu yazı bunun için basit bir örnek olması amacı ile yazılmıştır. Bu örneğe ekstra özellikler eklenebilir. Örneğin;

  • Firmware güvenliği açısından binary dosyanın şifrelenmesi.
  • Server ile olan dosya transferinde bir hata okuma(error packets) mekanizması kurulması.
  • Firmware dosyası için versiyon sorgulama mekanizması kurulması.
  • Bu örnekte indirilen firmware bir öncekinin üzerine yazılmaktadır. Flash hafızası daha büyük olan mikrodenetleyicilerde iki farklı uygulama alanı belirlenip veya harici bir hafıza entegresi kullanılarak indirilecek olan firmware ikinci alana yazılabilir. Böylece eski firmware silinmemiş olur ve güncellemeyi geri alma gibi bir özellik eklenebilir.
  • Bu örnekte bir hafıza sınırı belirlenmemiştir. Yani indirilen firmware boyutuna bakılmaksızın hafızaya yazılmaktadır. Eğer 49 kb’tan büyük bir dosya indirmek istersek sistemde hata oluşacaktır. Bu yüzden bunun için bir hafıza sınırı belirlenebilir.
  • Bu örnekte ESP8266 AT komutları ile kullanılmıştır. STM32 üzerindeki kod yükünü azaltmak için ESP Arduino IDE si ile programlanarak kullanılabilir. Böylece güncelleme olup olmadığını ESP kendi kontrol edebilir ve eğer güncelleme var ise GPIO çıkışları ile STM32 yi uyararak güncelleme moduna sokabilir. Bunun için STM32-ESP arasında iyi bir UART haberleşme stratejisi kurularak firmware dosyası stm32’nin hafızasına yazılmalıdır. Böylece IAP kodlarının yükü ESP ve STM32 üzerinde paylaştırılmış olur ve IAP kodları STM32’nin flash hafızasında daha az yer kaplar.

Kaynaklar

  1. STM Application Note (AN3226)
  2. STM Application Note(AN3965)
  3. Python libscrc module

Mehmet Topuz

9 Comments

  1. Mehmet bey merhabalar, elinize sağlık güzel bir çalışma olmuş.Ben bu işlemleri yine stm32f103c8 kullanarak fakat internet arayüzü için w5500 kullandım. Göndereceğim data paketleri hakkında tam emin olamadım.
    Örneğin sizin esp’ye;
    TxBufferLength = sprintf(txBuffer,”%c%s%c%s%c”,(char)opcode,filename,(char)0,TransferMode,(char)0);
    HAL_UART_Receive_DMA(&huart1,(uint8_t *)rxBuffer,10);
    HAL_UART_Transmit(&huart1(uint8_t*)txBuffer,
    sprintf(txBuffer,”AT+CIPSEND=%d\r\n”,
    TxBufferLength),1000);

    şeklinde kodlar yüklemişsiniz. Benim anladığım buradan esp’ye ‘UDP’,’192.168.1.100′,69\r\n\0 bu şekilde bir kod gidiyor. Bu kodlar direkt tftp request kod içeriyor mu yoksa esp ile ilgili bir kod mu?

    • Merhabalar,
      Burada ESP ‘ye önce AT+CIPSTART=”UDP”,”192.168.1.100″,69 şeklinde bir string göndererek servera bağlanıyorum. Daha sonra paketleri oluşturmak için yine sprintf fonksiyonunu kullanıyorum. Örneğin; sprintf(txBuffer,”%c%s%c%s%c”,(char)opcode,filename,(char)0,TransferMode,(char)0) ifadesi ile read request paketini txBuffer dizisine kaydediyorum. Aynı zamanda bu fonksiyon bana bu diziye kaydettiğim verilerin uzunluğunu veriyor. Esp ile servera bağlandıktan sonra atıyorum AT+CIPSEND=20 şeklinde bir komut gönderiyorum. Burada 20 sayısı CIPSEND komutundan sonra kaç byte daha gönderileceğini belirler. Yani esp benden 20 byte lık bir veri bekler. Ben burada 20 yerine sprintf fonksiyonun döndürdüğü değeri kullanıyorum. Çünkü paket içindeki filename uzunluğu değişkenlik gösterebilir. Eğer esp bana > karakterini gönderip bu verileri almaya hazır olduğunu söylerse hemen arkasından yukarıda bahsettiğim gibi sprintf fonsiyonu ile paketin hepsini tekrar txBuffer a kaydedip bu bufferı uart ile esp ye gönderiyorum.

  2. merhaba , tftp server dan illegal TpFP operation cevabı alıyorum
    gönderdiğim komut
    0x00
    0x01
    semih.bin
    0x00
    octet
    0x00

    sizde benzer birşey göndermişsiniz ama hata alıyroum
    ve buradaki octet nedir
    teşekkürler

    • Merhaba, gönderdiğiniz verilerin sırası doğru. Verileri dizi içerisine yukarıdaki gibi mi kaydediyorsunuz yoksa farklı bir şekilde mi? Tftp64 programından dosya yolunu veya ip belirtmeyi unutmuş olabirsiniz bir kontrol edin. Almak istediğiniz semih.bin bu dosyanın içinde olsun. Göndermek istediğiniz diziye debug ekranından bakarak karşılaştırın.
      Buradaki octet octo(latince 8 den) gelir.8 bitlik diziler için kullanılan bir terim.

      • diziyi yukarıdaki gibi gönderiyorum
        Aynı komutu esp ttl adaptör ile gönderince
        AT+CIPSEND=18

        OK
        > semih.binßøÐð….
        …..FF€FF&ø[-±ðNøh@]
        +IPD,26:Undefined error code
        şeklinde cevap geliyor, sonrasında ack göndermediğim için timeout a düşüyüor server.
        stm le debug da bakınca illegal operation alıyorum.

        • Bloğu daha almadan ack gönderiyorsun galiba. Bloğu okuduktan sonra ack göndermeden önce 100 veya 200 ms kadar bir gecikme ekleyip dener misin? Bu dosya indirme hızını biraz yavaşlatacaktır.

        • Tftp64 programında eğer ack gelmediyse veriyi atıyorum 3 kez tekrar gönder diye bir seçenek olması lazım onu da kontrol et.

          • sorunu anladım hocam teşekkürler usart ile gönderirken pointer ı arttırmadığıum için hep 0x00 göndermişim.
            ack kısmına şimdi bakacağım
            ,her 512 lik blok tan sonra mı ACK göndermeliyiz

        • Rica ederim. Evet her bloğu aldıktan sonra ack göndermen gerek yoksa server sonraki bloğu göndermez.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir