Protokol Komunikasi Praktik Terbaik dan Pola

19

Setiap kali saya mendesain protokol serial untuk digunakan di antara dua arduinos, saya merasa agak seperti menciptakan kembali roda. Saya ingin tahu apakah ada praktik atau pola terbaik yang diikuti orang. Pertanyaan ini kurang tentang kode aktual, tetapi lebih banyak tentang format pesan.

Misalnya, jika saya ingin memberi tahu Arduino untuk mem-flash itu LED pertama 3 kali saya mungkin mengirim:

^L1,F3\n
  • '^': memulai perintah baru
  • 'L': Menentukan perintah, (L: targetkan perintah ini ke LED)
  • '1': Targetkan LED pertama
  • ',': Pemisah baris perintah, nilai baru dalam pesan ini untuk diikuti
  • 'F': Sub-perintah Flash
  • '3': 3 kali (Flash LED tiga kali)
  • '\ n': Akhiri perintah

Pikiran? Bagaimana Anda biasanya mendekati penulisan protokol serial baru? Bagaimana jika saya ingin mengirim permintaan dari arduino 1 ke arduino 2 dan kemudian menerima tanggapan?

Jeremy Gillick
sumber

Jawaban:

13

Ada banyak cara untuk menulis protokol serial tergantung pada fungsionalitas apa yang Anda inginkan dan berapa banyak pengecekan kesalahan yang Anda butuhkan.

Beberapa hal umum yang Anda lihat dalam protokol point to point adalah:

Akhir dari pesan

Protokol ASCII paling sederhana hanya memiliki akhir dari urutan karakter pesan, sering \ratau \nkarena ini adalah apa yang akan dicetak ketika tombol enter ditekan. Protokol biner mungkin menggunakan 0x03atau byte umum lainnya.

Mulai dari pesan

Masalah dengan hanya memiliki akhir pesan adalah bahwa Anda tidak tahu byte lain apa yang telah diterima ketika Anda mengirim pesan Anda. Bytes ini kemudian akan diawali dengan pesan dan menyebabkannya ditafsirkan secara salah. Misalnya, jika Arduino baru saja bangun dari tidur mungkin ada beberapa sampah di buffer serial. Untuk menyiasatinya, Anda memiliki urutan pesan yang dimulai. Dalam contoh Anda, ^sering dalam protokol biner0x02

Pemeriksaan Kesalahan

Jika pesan dapat rusak kami perlu memeriksa kesalahan. Ini bisa berupa checksum atau kesalahan CRC atau yang lainnya.

Karakter Escape

Bisa jadi checksum itu menambah karakter kontrol, seperti byte 'awal pesan' atau 'akhir pesan', atau pesan berisi nilai yang sama dengan karakter kontrol. Solusinya adalah memperkenalkan karakter pelarian. Karakter melarikan diri ditempatkan sebelum karakter kontrol yang dimodifikasi sehingga karakter kontrol yang sebenarnya tidak ada. Misalnya, jika karakter awal adalah 0x02, menggunakan karakter escape 0x10 kita dapat mengirim nilai 0x02 dalam pesan sebagai pasangan byte 0x10 0x12 (karakter kontrol XOR byte)

Nomor paket

Jika sebuah pesan rusak, kami dapat meminta pengiriman ulang dengan pesan kosong atau coba lagi, tetapi jika beberapa pesan telah terkirim maka hanya pesan terbaru yang dapat dikirim ulang. Sebaliknya paket tersebut dapat diberi nomor yang berguling setelah sejumlah pesan. Misalnya, jika nomor ini 16, perangkat pengirim dapat menyimpan 16 pesan terakhir yang dikirim dan jika ada yang rusak, perangkat penerima dapat meminta pengiriman ulang menggunakan nomor paket.

Panjangnya

Seringkali dalam protokol biner Anda melihat byte panjang yang memberitahu perangkat penerima berapa banyak karakter dalam pesan. Ini menambahkan tingkat lain pengecekan kesalahan seolah-olah jumlah byte yang benar tidak diterima maka ada kesalahan.

Khusus Arduino

Saat membuat protokol untuk Arduino, pertimbangan pertama adalah seberapa andal saluran komunikasi. Jika Anda mengirim lebih dari sebagian besar media nirkabel, XBee, WiFi, dll, sudah ada built in pengecekan dan coba ulang kesalahan dan dengan demikian tidak ada gunanya menempatkan ini dalam protokol Anda. Jika Anda mengirim lebih dari RS422 selama beberapa kilometer maka itu akan diperlukan. Hal-hal yang akan saya sertakan adalah mulai dari pesan dan akhir karakter pesan, seperti yang Anda miliki. Implementasi khas saya terlihat seperti:

>messageType,data1,data2,…,dataN\n

Membatasi bagian data dengan koma memungkinkan penguraian yang mudah, dan pesan dikirim menggunakan ASCII. Protokol ASCII sangat bagus karena Anda dapat mengetik pesan ke monitor serial.

Jika Anda menginginkan protokol biner, mungkin untuk mempersingkat ukuran pesan, Anda harus menerapkan melarikan diri jika byte data dapat sama dengan byte kontrol. Karakter kontrol biner lebih baik untuk sistem di mana spektrum penuh pengecekan kesalahan dan coba lagi diinginkan. Payloadnya masih bisa ASCII jika diinginkan.

geometrikal
sumber
Mungkinkah sampah di awal kode pesan yang sebenarnya dapat berisi kode kontrol pesan yang diawali? Bagaimana Anda menangani ini?
CMCDragonkai
@ CMCDragonkai Ya ini adalah kemungkinan, terutama untuk kode kontrol byte tunggal. Namun jika Anda menemukan kode kontrol mulai di tengah penguraian pesan, pesan tersebut dibuang dan penguraian dimulai ulang.
geometrikal
9

Saya tidak memiliki keahlian formal tentang protokol serial, tetapi saya telah menggunakannya beberapa kali, dan kurang lebih puas dengan skema ini:

(Header paket) (ID byte) (data) (fletcher16 checksum) (Packet Footer)

Saya biasanya membuat header 2 byte dan Footer 1 byte. Parser saya akan membuang semuanya ketika ia melihat header paket baru, dan berusaha mengurai pesan jika ia melihat catatan kaki. Jika checksum gagal, itu tidak akan membuang pesan, tetapi terus menambahkan sampai karakter footer ditemukan dan checksum berhasil. Dengan begitu, catatan kaki hanya perlu satu byte karena tabrakan tidak mengganggu pesan.

ID arbitrer, kadang-kadang dengan panjang bagian data menjadi bagian bawah menggigit (4 bit). Bit panjang kedua bisa digunakan tetapi saya biasanya tidak repot karena panjangnya tidak perlu diketahui untuk diuraikan dengan benar, jadi melihat panjang yang tepat untuk mendapatkan ID yang diberikan hanya konfirmasi lebih lanjut bahwa pesan itu benar.

Fletcher16 checksum adalah checksum 2 byte dengan kualitas yang hampir sama dengan CRC tetapi jauh lebih mudah untuk diimplementasikan. beberapa detail di sini . Kode dapat sesederhana ini:

for(int i=0; i < bufSize; i++ ){
   sum1 = (sum1 + buffer[i]) % 255;
   sum2 = (sum2 + sum1) % 255;
}
uint16_t checksum = (((uint16_t)sum1)<<8) | sum2;

Saya juga telah menggunakan sistem panggilan dan tanggap untuk pesan-pesan penting, Di mana PC akan mengirim pesan setiap 500ms atau lebih sampai menerima pesan OK dengan checksum dari seluruh pesan asli sebagai data (termasuk checksum asli).

Skema ini, tentu saja, tidak cocok untuk diketik ke terminal seperti contoh Anda. Protokol Anda tampaknya cukup bagus karena terbatas pada ASCII dan saya yakin lebih mudah untuk proyek cepat yang Anda ingin dapat langsung membaca dan mengirim pesan. Untuk proyek yang lebih besar, sangat menyenangkan memiliki kepadatan protokol biner dan keamanan checksum.

BrettAM
sumber
Karena "parser [Anda] akan membuang semuanya ketika melihat header paket baru" Saya ingin tahu apakah ini tidak membuat masalah jika kebetulan header tersebut ditemukan di dalam data?
humanityANDpeace
@humanityANDpeace Alasan menjatuhkannya adalah bahwa ketika suatu paket terputus tidak akan pernah diuraikan dengan benar, jadi kapan Anda memutuskan sampahnya dan melanjutkan? Solusi termudah, dan dalam pengalaman saya cukup baik, adalah dengan menjatuhkan paket yang buruk segera setelah header berikutnya. Saya telah menggunakan header 16 bit tanpa masalah, tetapi Anda bisa membuatnya lebih lama jika kepastian lebih penting bahwa bandwidth.
BrettAM
Jadi apa yang Anda sebutkan sebagai header adalah kombinasi dari Magic 16bit. yaitu 010101001 10101010, kan? Saya setuju ini hanya perubahan 1/256 * 256 untuk memukul, tetapi juga menonaktifkan pernah menggunakan 16bit ini dalam data Anda, kalau tidak salah ditafsirkan sebagai header dan Anda membuang pesan, kan?
humanityANDpeace
@ humanityANDpeace Saya tahu ini setahun kemudian, tetapi Anda harus memperkenalkan urutan pelarian. Sebelum mengirim, server memeriksa payload untuk setiap byte khusus, kemudian lolos dengan byte khusus lain. Sisi klien, Anda harus mengembalikan muatan asli. Ini berarti bahwa Anda tidak dapat mengirim paket panjang tetap dan mempersulit implementasi. Ada banyak standar protokol serial untuk dipilih dari semua alamat ini. Berikut ini adalah bacaan yang sangat bagus tentang topik tersebut .
RubberDuck
1

Jika Anda masuk ke standar, maka Anda dapat melihat pengkodean ASN.1 / BER TLV. ASN.1 adalah bahasa yang digunakan untuk menggambarkan struktur data, dibuat khusus untuk komunikasi. BER adalah metode TLV untuk pengkodean data yang terstruktur menggunakan ASN.1. Masalahnya adalah bahwa pengkodean ASN.1 mungkin rumit di terbaik. Membuat kompiler ASN.1 yang lengkap adalah proyek itu sendiri (dan yang sangat rumit pada saat itu, pikirkan bulan ).


Mungkin lebih baik menjaga struktur TLV saja. TLV pada dasarnya terdiri dari tiga elemen: tag, panjang, dan bidang nilai. Tag mendefinisikan tipe data (string teks, string oktet, integer dll.) Dan panjangnya panjang nilainya .

Dalam BER, T juga menunjukkan jika nilainya adalah seperangkat struktur TLV itu sendiri (sebuah node yang dikonstruksi) atau secara langsung sebuah nilai (sebuah node primitif). Dengan begitu Anda dapat membuat pohon dalam biner, seperti XML (tetapi tanpa overhead XML).

Contoh:

TT LL VV
02 01 FF

adalah integer (tag 02) dengan panjang nilai 1 (panjang 01) dan nilai -1 (nilai FF). Dalam ASN.1 / BER bilangan bulat adalah tanda angka big endian, tetapi Anda tentu saja dapat menggunakan format Anda sendiri.

TT LL (TT LL VV, TT LL VV VV)
30 07  02 01 FF  02 02 00 FF

adalah urutan (daftar) dengan panjang 7 yang mengandung dua bilangan bulat, satu dengan nilai -1 dan satu dengan nilai 255. Kedua penyandian bilangan bulat bersama-sama membentuk nilai urutan.

Anda bisa melemparkan ini ke dekoder online juga, bukankah itu bagus?


Anda juga dapat menggunakan panjang tak terbatas dalam BER yang akan memungkinkan Anda untuk mengalirkan data. Dalam hal ini Anda perlu mengurai pohon Anda dengan benar. Saya akan menganggapnya sebagai topik lanjutan, Anda perlu tahu tentang luasnya parsing pertama dan kedalaman, untuk satu.


Menggunakan skema TLV pada dasarnya memungkinkan Anda untuk memikirkan segala jenis struktur data dan menyandikannya. ASN.1 melangkah lebih jauh dari itu, memberi Anda pengidentifikasi unik (OID), pilihan (banyak seperti C-serikat pekerja), termasuk struktur ASN.1 lain dll. Dll tetapi itu mungkin berlebihan untuk proyek Anda. Mungkin struktur yang didefinisikan ASN.1 yang paling terkenal saat ini adalah sertifikat yang digunakan oleh peramban Anda.

Maarten Bodewes
sumber
0

Jika tidak, Anda sudah mendapatkan dasar-dasarnya. Perintah Anda dapat dibuat dan dibaca oleh manusia dan mesin yang merupakan nilai tambah besar. Anda mungkin menambahkan checksum untuk mendeteksi perintah yang salah bentuk atau rusak dalam perjalanan, terutama jika saluran Anda menyertakan kabel panjang atau tautan radio.

Jika Anda membutuhkan kekuatan industri (perangkat Anda tidak boleh menyebabkan atau membiarkan seseorang terluka atau mati; Anda memerlukan kecepatan data tinggi, pemulihan kesalahan, deteksi paket yang hilang, dll.) Kemudian lihat ke beberapa protokol standar dan praktik desain.

JRobert
sumber