संचार प्रोटोकॉल सर्वोत्तम अभ्यास और पैटर्न


19

जब भी मैं दो arduinos के बीच उपयोग किए जाने वाले सीरियल प्रोटोकॉल को डिजाइन करता हूं, मुझे लगता है कि मैं एक पहिया को फिर से स्थापित कर रहा हूं। मुझे आश्चर्य है कि अगर कोई सर्वोत्तम प्रथा या पैटर्न है जिसका लोग अनुसरण करते हैं। यह प्रश्न वास्तविक कोड के बारे में कम है, लेकिन संदेशों के प्रारूप के बारे में अधिक है।

उदाहरण के लिए, अगर मैं अर्दुइनो को फ्लैश करने के लिए कहना चाहता हूं तो यह पहली बार 3 बार एलईडी हो सकता है जो मैं भेज सकता हूं:

^L1,F3\n
  • '^': एक नई कमांड शुरू करता है
  • 'L': कमांड को परिभाषित करता है, (L: इस कमांड को LED पर लक्षित करें)
  • '1': पहले एलईडी का लक्ष्य रखें
  • ',': इस संदेश का अनुसरण करने के लिए कमांड लाइन विभाजक, नया मान
  • 'एफ': फ्लैश उप-कमांड
  • '3': 3 बार (एलईडी तीन बार फ्लैश करें)
  • '\ n': कमांड समाप्त करें

विचार? आप आमतौर पर एक नया सीरियल प्रोटोकॉल लिखने के लिए कैसे संपर्क करते हैं? क्या होगा अगर मैं arduino 1 से arduino 2 में एक क्वेरी भेजना चाहता हूं और फिर एक प्रतिक्रिया प्राप्त करता हूं?

जवाबों:


13

सीरियल प्रोटोकॉल लिखने के लिए बहुत सारे तरीके हैं जो इस बात पर निर्भर करता है कि आप क्या कार्यक्षमता चाहते हैं और आपको कितनी त्रुटि की आवश्यकता है।

पॉइंट टू पॉइंट प्रोटोकॉल में दिखाई देने वाली कुछ सामान्य चीजें हैं:

संदेश का अंत

सबसे सरल ASCII प्रोटोकॉल में केवल संदेश चरित्र अनुक्रम का अंत होता है, अक्सर \rया \nजैसा कि एंटर कुंजी के हिट होने पर मुद्रित होता है। बाइनरी प्रोटोकॉल का उपयोग 0x03या कुछ अन्य सामान्य बाइट हो सकता है ।

संदेश की शुरुआत

केवल संदेश के अंत के साथ समस्या यह है कि आपको पता नहीं है कि जब आप अपना संदेश भेजते हैं तो अन्य बाइट्स पहले से ही प्राप्त कर चुके हैं। इन बाइट्स को तब संदेश के लिए उपसर्ग किया जाएगा और इसका कारण गलत तरीके से समझा जाएगा। उदाहरण के लिए, अगर Arduino सिर्फ नींद से जागता है तो सीरियल बफर में कुछ कचरा हो सकता है। इसके चारों ओर जाने के लिए आपके पास संदेश अनुक्रम की शुरुआत है। आपके उदाहरण में ^, बाइनरी प्रोटोकॉल में अक्सर0x02

त्रुटि की जांच कर रहा है

यदि संदेश दूषित हो सकता है तो हमें कुछ त्रुटि जाँच की आवश्यकता है। यह एक चेकसम या सीआरसी त्रुटि या कुछ और हो सकता है।

वर्ण से बचो

यह हो सकता है कि चेकसम एक नियंत्रण वर्ण में जोड़ता है, जैसे 'संदेश की शुरुआत' या 'संदेश का अंत' बाइट, या संदेश में नियंत्रण वर्ण के बराबर मान होता है। समाधान एक भागने चरित्र को पेश करना है। बच चरित्र को एक संशोधित नियंत्रण चरित्र से पहले रखा जाता है ताकि वास्तविक नियंत्रण चरित्र मौजूद न हो। उदाहरण के लिए, यदि कोई प्रारंभ वर्ण 0x02 है, तो भागने वाले वर्ण 0x10 का उपयोग करके हम संदेश में 0x02 का मान बाइट जोड़ी 0x10 0x12 (बाइट XOR नियंत्रण वर्ण) के रूप में भेज सकते हैं

पैकेट नंबर

यदि कोई संदेश दूषित है, तो हम एक नीक या पुनः प्रयास संदेश के साथ पुनः भेजने का अनुरोध कर सकते हैं, लेकिन यदि एक से अधिक संदेश भेजे गए हैं, तो केवल नवीनतम संदेश ही नाराज हो सकता है। इसके बजाय पैकेट को एक नंबर दिया जा सकता है जो एक निश्चित संख्या में संदेशों के बाद खत्म हो जाता है। उदाहरण के लिए, यदि यह संख्या 16 है, तो ट्रांसमिटिंग डिवाइस अंतिम 16 संदेशों को भेज सकता है और यदि कोई प्राप्त डिवाइस को दूषित कर रहा है तो वह पैकेट नंबर का उपयोग करके रिस्टेंड का अनुरोध कर सकता है।

लंबाई

अक्सर बाइनरी प्रोटोकॉल में आप एक लंबाई बाइट देखते हैं जो प्राप्त डिवाइस को बताता है कि संदेश में कितने वर्ण हैं। यह त्रुटि जांच का एक और स्तर जोड़ता है जैसे कि बाइट्स की सही संख्या प्राप्त नहीं हुई थी तो एक त्रुटि थी।

Arduino विशिष्ट

Arduino के लिए एक प्रोटोकॉल के साथ आने पर पहला विचार यह है कि संचार चैनल कितना विश्वसनीय है। यदि आप अधिकांश वायरलेस माध्यमों, XBee, WiFi, आदि पर भेज रहे हैं, तो पहले से ही त्रुटि जाँच और पुनः प्रयास में बनाया गया है और इस प्रकार आपके प्रोटोकॉल में इन्हें डालने का कोई मतलब नहीं है। यदि आप एक दो किलोमीटर के लिए RS422 पर भेज रहे हैं तो यह आवश्यक होगा। जिन चीजों को मैं शामिल करूंगा उनमें संदेश की शुरुआत और संदेश पात्रों का अंत है, जैसा कि आपके पास है। मेरा विशिष्ट कार्यान्वयन कुछ इस तरह दिखता है:

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

अल्पविराम के साथ डेटा भागों को वितरित करना आसान पार्सिंग की अनुमति देता है, और संदेश ASCII का उपयोग करके भेजा जाता है। ASCII प्रोटोकॉल महान हैं क्योंकि आप धारावाहिक मॉनिटर में संदेश टाइप कर सकते हैं।

यदि आप एक बाइनरी प्रोटोकॉल चाहते हैं, तो शायद संदेश के आकार को छोटा करने के लिए, आपको भागने को लागू करना होगा यदि एक डेटा बाइट एक नियंत्रण बाइट के समान हो सकता है। बाइनरी नियंत्रण वर्ण उन प्रणालियों के लिए बेहतर हैं जहां त्रुटि जाँच और पुनर्प्रयास का पूर्ण स्पेक्ट्रम वांछित है। यदि वांछित है तो पेलोड अभी भी ASCII हो सकता है।


क्या यह संभव नहीं है कि संदेश कोड की वास्तविक शुरुआत के आगे कचरा संदेश नियंत्रण कोड की शुरुआत हो सकती है? आप इससे कैसे निपटेंगे?
CMCDragonkai 14

@CMCDragonkai हाँ यह एक संभावना है, विशेष रूप से एकल बाइट नियंत्रण कोड के लिए। हालाँकि, यदि आप किसी संदेश को पार्स करने के माध्यम से प्रारंभ नियंत्रण कोड आधे रास्ते पर आते हैं, तो संदेश को छोड़ दिया जाता है और पुनरारंभ करना शुरू कर देता है।
ज्यामितीय

9

मेरे पास सीरियल प्रोटोकॉल पर कोई औपचारिक विशेषज्ञता नहीं है, लेकिन मैंने उन्हें कई बार इस्तेमाल किया है, और इस योजना पर कमोबेश बसता है:

(पैकेट हैडर) (आईडी बाइट) (डेटा) (फ्लेचर 16 चेकसम) (पैकेट पाद)

मैं आमतौर पर हेडर 2 बाइट्स और फूटर 1 बाइट बनाता हूं। मेरा पार्सर सब कुछ डंप कर देगा जब यह एक नया पैकेट हेडर देखता है, और संदेश को पार्स करने का प्रयास करता है यदि यह एक पाद देखता है। यदि चेकसम विफल हो जाता है, तो यह संदेश को खोदेगा नहीं, लेकिन तब तक जोड़ना जारी रखेगा जब तक कि पाद वर्ण न मिल जाए और एक चेकसम सफल न हो जाए। इस तरह से पाद को केवल एक बाइट की आवश्यकता होती है क्योंकि टकराव संदेश को बाधित नहीं करता है।

आईडी मनमाना है, कभी-कभी डेटा अनुभाग की लंबाई के साथ नीचे कुतरना (4 बिट्स) होता है। एक दूसरी लंबाई बिट का उपयोग किया जा सकता है, लेकिन मैं आम तौर पर परेशान नहीं करता हूं क्योंकि लंबाई को सही ढंग से पार्स करने के लिए ज्ञात होने की आवश्यकता नहीं है, इसलिए किसी दिए गए आईडी के लिए सही लंबाई देखने से यह अधिक पुष्टि होती है कि संदेश सही था।

फ्लेचर 16 चेकसम 2 बाइट चेकसम है जिसमें लगभग समान गुणवत्ता वाला सीआरसी है लेकिन इसे लागू करना बहुत आसान है। यहाँ कुछ विवरण । कोड इस प्रकार सरल हो सकता है:

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

मैंने महत्वपूर्ण संदेशों के लिए एक कॉल और रिस्पॉन्स सिस्टम का भी उपयोग किया है, जहां पीसी हर 500ms या तो एक संदेश भेजेगा, जब तक कि इसे पूरे मूल संदेश के चेकसम के साथ डेटा के रूप में (मूल चेकसम सहित) ओके संदेश प्राप्त न हो जाए।

यह योजना, निश्चित रूप से एक टर्मिनल में टाइप किए जाने के लिए अच्छी तरह से अनुकूल नहीं है, जैसे आपका उदाहरण होगा। आपका प्रोटोकॉल ASCII तक सीमित होने के लिए बहुत अच्छा लगता है और मुझे यकीन है कि एक त्वरित परियोजना के लिए आसान है जिसे आप सीधे पढ़ना और संदेश भेजना चाहते हैं। बड़ी परियोजनाओं के लिए एक बाइनरी प्रोटोकॉल और एक चेकसम की सुरक्षा का घनत्व होना अच्छा है।


चूँकि "[आपका] पार्सर सब कुछ डंप कर देगा जब यह एक नया पैकेट हेडर देखता है" मुझे आश्चर्य है कि क्या यह समस्या पैदा नहीं करता है यदि संयोग से हेडर डेटा के अंदर है?
मानवतावाद

@humanityANDpeace इसे छोड़ने का कारण यह है कि जब कोई पैकेट कट जाता है तो वह कभी भी सही तरीके से पार्स नहीं होता है, इसलिए आप अपना कचरा कब तय करते हैं और आगे बढ़ते हैं? सबसे आसान, और मेरे अनुभव में काफी अच्छा है, अगले हेडर के आते ही समाधान एक खराब पैकेट को छोड़ना है। मैं 16 बिट हेडर का उपयोग बिना किसी समस्या के कर रहा हूं, लेकिन आप इसे अधिक लंबा बना सकते हैं यदि निश्चितता अधिक महत्वपूर्ण है। बैंडविड्थ।
ब्रेटम

तो क्या आप एक हेडर के रूप में रेफरी कुछ जादू 16 बिट संयोजन है। यानी 010101001 10101010, है ना? मैं मानता हूं कि हिट करने के लिए यह केवल 1/256 * 256 परिवर्तन है, लेकिन यह कभी भी आपके डेटा के अंदर इस 16 बिट का उपयोग करने में अक्षम करता है, अन्यथा इसे हेडर के रूप में गलत समझा जाता है और आप संदेश को सही कर देते हैं?
मानवता

@humanityANDpeace मुझे पता है कि यह एक साल बाद है, लेकिन आपको एक भागने का क्रम शुरू करना होगा। भेजने से पहले, सर्वर किसी विशेष बाइट के लिए पेलोड की जांच करता है, फिर उन्हें एक अन्य विशेष बाइट के साथ छोड़ देता है। ग्राहक पक्ष, आपको मूल पेलोड को वापस एक साथ रखना होगा। इसका मतलब यह है कि आप निश्चित लंबाई के पैकेट नहीं भेज सकते हैं और कार्यान्वयन को जटिल बनाते हैं। वहाँ कई धारावाहिक प्रोटोकॉल मानकों से चुनने के लिए कर रहे हैं कि यह सब पता है। यहाँ विषय पर एक बहुत अच्छा पढ़ा है
रबरडैक

1

यदि आप मानकों में हैं, तो आप ASN.1 / BER TLV एन्कोडिंग पर एक नज़र डाल सकते हैं। ASN.1 एक भाषा है जिसका उपयोग डेटा संरचनाओं का वर्णन करने के लिए किया जाता है, विशेष रूप से संचार के लिए। बीईआर ASN.1 का उपयोग करके संरचित डेटा को एन्कोडिंग की एक TLV विधि है। समस्या यह है कि ASN.1 एन्कोडिंग सबसे अच्छा मुश्किल हो सकता है। एक पूर्ण ASN.1 संकलक बनाना अपने आप में एक परियोजना है (और उस पर एक विशेष रूप से मुश्किल, महीनों सोचो )।


शायद केवल टीएलवी संरचना को रखना बेहतर है। टीएलवी में मूल रूप से तीन तत्व होते हैं: एक टैग, एक लंबाई और एक मूल्य क्षेत्र। टैग डेटा के प्रकार (टेक्स्ट स्ट्रिंग, ऑक्टेट स्ट्रिंग, पूर्णांक आदि) और मूल्य की लंबाई को परिभाषित करता है ।

यदि कोई मान टीएलवी संरचनाओं का एक सेट (एक निर्मित नोड) या सीधे एक मूल्य (एक आदिम नोड) है तो बीईआर में टी भी दर्शाता है। इस तरह आप बाइनरी में एक पेड़ बना सकते हैं, बहुत कुछ एक्सएमएल की तरह (लेकिन एक्सएमएल ओवरहेड के बिना)।

उदाहरण:

TT LL VV
02 01 FF

021 (लंबाई 01) और मान -1 (मान FF) के मान की लंबाई के साथ एक पूर्णांक (टैग ) है। ASN.1 / BER में पूर्णांक बड़े एंडियन नंबर पर हस्ताक्षर करते हैं, लेकिन आप निश्चित रूप से अपने प्रारूप का उपयोग कर सकते हैं।

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

एक दृश्य (एक सूची) लंबाई 7 युक्त है दो पूर्णांकों, मूल्य 255 दो पूर्णांक एनकोडिंग एक साथ अनुक्रम का मूल्य बनाने के साथ मूल्य के साथ एक -1 और एक।

आप बस एक ऑनलाइन डिकोडर में भी यह टॉस कर सकते हैं, यह अच्छा नहीं है?


आप BER में अनिश्चित लंबाई का भी उपयोग कर सकते हैं जो आपको डेटा स्ट्रीम करने की अनुमति देगा। उस मामले में आपको अपने पेड़ को सही ढंग से पार्स करने की आवश्यकता है। मैं इसे एक उन्नत विषय मानता हूं, आपको पहले एक के लिए चौड़ाई और गहराई के बारे में जानने की जरूरत है।


मूल रूप से एक टीएलवी योजना का उपयोग करने से आप किसी भी प्रकार की डेटा संरचना के बारे में सोच सकते हैं और इसे एनकोड कर सकते हैं। ASN.1 इससे बहुत आगे जाता है, जिससे आपको विशिष्ट पहचानकर्ता (OID), विकल्प (C- यूनियनों की तरह बहुत कुछ), अन्य ASN.1 संरचनाएं आदि शामिल हैं, लेकिन यह आपकी परियोजना के लिए ओवरकिल हो सकता है। संभवतः सबसे अच्छी तरह से ज्ञात ASN.1 परिभाषित संरचनाएं आज आपके ब्राउज़र द्वारा उपयोग किए जाने वाले प्रमाण पत्र हैं।


0

यदि नहीं, तो आप मूल बातें कवर कर चुके हैं। आपकी आज्ञाओं को मनुष्य और मशीनों दोनों द्वारा बनाया और पढ़ा जा सकता है जो कि एक बड़ा धन है। आप एक खराब-गठित कमांड या पारगमन में क्षतिग्रस्त एक का पता लगाने के लिए एक चेकसम जोड़ सकते हैं, खासकर यदि आपके चैनल में लंबी केबल या एक रेडियो लिंक शामिल है।

यदि आपको औद्योगिक शक्ति की आवश्यकता है (आपका डिवाइस किसी को चोट पहुंचाने या मरने की अनुमति नहीं दे सकता है, तो आपको उच्च डेटा दर, गलती वसूली, लापता पैकेट पहचान आदि की आवश्यकता है) फिर कुछ मानक प्रोटोकॉल और डिज़ाइन प्रथाओं को देखें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.