در قسمت شانزدهم از آموزش آردوینو به بررسی تنظیم و خواندن بیتها و همچنین، شیفت کردن آن ها، پرداختیم. در این قسمت قصد داریم درباره ارسال اطلاعات از آردوینو به کامپیوتر و ارسال متن فرمت شده و دادههای عددی از آردوینو صحبت کنیم.
فرض کنید میخواهید با استفاده از نرمافزار Arduino IDE یا برنامه serial terminal موردنظر خود، متن و دادهها را به PC، مک یا دستگاههای دیگر (مانند Raspberry Pi) ارسال کنید.
اسکچ زیر اعداد متوالی را بر روی سریال مانیتور چاپ (Print) میکند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* * SerialOutput sketch * Print numbers to the serial port */ void setup() { Serial.begin(9600); // send and receive at 9600 baud } int number = 0; void loop() { Serial.print("The number is "); Serial.println(number); // print the number delay(500); // delay half second between numbers number++; // to the next number } |
برای این کار، ابتدا باید آردوینو را به کامپیوتر خود متصل کنید و کد بالا را در Arduino IDE جایگذاری و آپلود کنید. سپس روی گزینه Serial Monitor کلیک نمایید. در نهایت، خروجی شما باید بهصورت زیر باشد:
1 2 3 | The number is 0 The number is 1 The number is 2 |
برای نمایش متن و عدد اسکچ خود در رایانه از طریق ارتباط سریال، عبارت (9600)Serial.begin را در ()setup قرار دهید و سپس از دستورات ()Serial.print برای چاپ متن و مقادیری که می خواهید ببینید استفاده کنید. سپس می توانید خروجی را در سریال مانیتور همانطور که در شکل زیر (شکل ۱) نشان داده شده است مشاهده کنید:
برای نمایش گرافیکی شماره ارسال شده، پنجره سریال مانیتور را ببندید و از بخش Tools، گزینه Serial Plotter را انتخاب کنید. سپس در پنجرهای که باز میشود، نموداری از مقادیر دریافت شده از برد ترسیم میشود. پلاتر (plotter) میتواند اعداد را از متن جدا کند و اعداد متعددی را که با کاراکترهای آلفا (alpha) از هم جدا شدهاند را شناسایی کند و با استفاده از رنگهای مختلف، آنها را بهصورت جداگانه رسم کند. شکل زیر (شکل ۲) سریال پلاتر (Serial Plotter) را نشان میدهد:
برای استفاده از ورودی و خروجی سریال در برنامهی آردوینو، ابتدا باید تابع Serial.begin() را فراخوانی کنید. این تابع یک پارامتر ورودی دارد که نرخ ارتباط (baud rate) را تعیین میکند. باید از همان نرخ برای طرف فرستنده و طرف گیرنده استفاده کنید، در غیر این صورت ممکن است کاراکترهای غیرقابلفهم (یا هیچچیزی) روی صفحهنمایش داده شود. بیشتر مثالهای موجود در این آموزش از نرخ baud، 9,600 استفاده میکنند (باود (baud) یک واحد از تعداد بیتهای ارسالی در ثانیه است). نرخ باود 9,600 تقریباً 1,000 کاراکتر در ثانیه را نمایش میدهد. البته میتوانید نرخهای پایینتر یا بالاتری را نیز تنظیم کنید (محدودهی نرخها 300 تا 115,200 یا بیشتر است، بسته به قابلیتهای برد شما). اما حتماً مطمئن شوید که هر دو طرف از همان نرخ استفاده میکنند. Serial Monitor نیز با استفاده از منوی باود (در پایین سمت راست پنجرهی Serial Monitor) نرخ را تنظیم میکند. اگر خروجی شما به شکل زیر باشد:
1 | `3??f<ÌxÌ▯▯▯ü`³??f< |
شما باید بررسی کنید که نرخ باود انتخاب شده در سریال مانیتور کامپیوتر شما با نرخ تعیین شده توسط ()Serial.begin در اسکچ شما مطابقت داشته باشد.
اگر سرعت ارسال و دریافت سریال را بهدرستی تنظیم کردهاید، اما هنوز متن ناخوانا دریافت میکنید، بررسی کنید که آیا در قسمت Board در منوی IDE Tools، برد صحیح را انتخاب کردهاید یا خیر.
بعضی از بردها نسخههای مختلف سرعت کلاک دارند؛ بنابراین اگر برد اشتباهی را انتخاب کردهاید، آن را به برد صحیح تغییر دهید و دوباره آپلود کنید.
شما میتوانید متن (تکست) را با استفاده از تابع ()Serial.print انتقال دهید. رشتهها (strings) همانطور که هست چاپ میشود (اما بدون علامت نقلقول). برای مثال کد زیر:
1 | Serial.print("The number is "); |
کد زیر چاپ میشود:
1 | The number is |
مقادیری (اعدادی) که چاپ میکنید به نوع متغیر بستگی دارد. بهعنوانمثال، چاپ یک عدد صحیح مانند number ، مقدار عددی آن را چاپ میکند، بنابراین اگر مقدار متغیر number ، یک باشد، کد زیر حاصل می شود:
1 | Serial.println(number); |
بهطورکلی، در این صورت مقدار فعلی عدد، چاپ میشود:
1 | 1 |
در اسکچ مثال، عدد چاپ شده هنگامی که حلقه (لوپ) شروع میشود، 0 است و هر بار از طریق حلقه (لوپ) یک عدد افزایش می یابد. تابع ln در انتهای println باعث می شود که دستور بعدی در خط بعدی کد نوشته شود.
بهخاطر داشته باشید که در بردهای آردوینو و بردهای سازگار با آردوینو، دو نوع رفتار مختلف در پورتهای سریال وجود دارد: بردهای آردوینو Uno و بیشتر بردهای AVR ATmega موقع باز کردن پورت سریال به صورت خودکار ریست میشوند. این به این معنی است که شما همیشه مقدار شمارش را ازمقدار اولیه (دراین مثال صفر) در سریال مانیتور یا پلاتر (نمودار) مشاهده خواهید کرد. بردهای آردوینو لئوناردو و بردهای مبتنی بر ARM به طور اتوماتیک هنگام باز کردن پورت سریال ریست نمیشوند. این به این معنی است که اسکچها به طور همزمان با روشنشدن برد شمارش را شروع میکنند؛ بنابراین، مقداری که در ابتدا در سریال مانیتور یا پلاتر مشاهده میکنید، به زمان باز کردن اتصال سریال بستگی دارد؛ بنابراین، اگر از این بردها استفاده میکنید، این رفتار را در بررسی دیتاهای خود در نظر بگیرید.
با این اطلاعات میتوانید شروع به چاپ متن و مقدار اعشاری اعداد صحیح کنید.
ممکن است بخواهید یک برنامه ترمینال شخص ثالث (third-party) را در نظر بگیرید که ویژگیهای بیشتری نسبت به سریال مانیتور دارد. نمایش دیتاها در قالب متن یا باینری (یا هر دو)، نمایش کاراکترهای کنترلی و ورود به یک فایل، فقط تعدادی از قابلیتهای بیشتر موجود از بسیاری از برنامههای ترمینال شخص ثالث (third-party) هستند. در اینجا برخی از این برنامهها که توسط کاربران آردوینو توصیه شدهاند، آورده شده است:
برد های آردوینو بسته به توانایی ها و تعداد پین ها می توانند تا 4 پورت ارتباط سریال مجزا را پشتیبانی کنند که فقط یکی از آنها دارای رابط USB است و بقیه فقط به صورت پین روی برد وجود دارد. با این حال می توان با استفاده از ماژولهای مبدل USB به سریال از دیگر پورتهای برد استفاده کرد. در جدول زیر تعداد پورتهای سریال برای هر برد و پایه های مرتبط با آن آورده شده.
برای مثال در برد Mega 2560 پایههای 19/18 مرتبط به ابجکت Serial1 است و میتوان با اتصال رابط ذکر شده به پایه ها و تغییر Serial به Serial1 در کد ابتدای مطلب از آن استفاده کرد.
در برد UNO و بردهای مشابه فقط یک پورت سریال وجود دارد که هم برای پروگرام کردن برد از طریق USB و ارتباط با سریال مانیتور و هم برای ارتباط با سایر ماژولها و بردها از طریق پین ها استفاده می شود. پس این نکته را در نظر داشته باشید که در صورت استفاده از پین ها ممکن است برای پروگرام کردن برد مشکل ایجاد شود.
در ادامه میخواهیم درباره نحوه ارسال دیتاهای سریال از آردوینو که بهصورت متن، مقادیر اعشاری، هگزادسیمال یا باینری نمایش داده میشوند، صحبت کنیم.
شما میتوانید دیتاها را در فرمتهای مختلف در پورت سریال چاپ کنید. در اینجا اسکچی وجود دارد که تمام گزینههای فرمت موجود با توابع سریال چاپ ()print و println را نشان میدهد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* SerialFormatting Print values in various formats to the serial port */ char chrValue = 65; // these are the starting values to print byte byteValue = 65; int // intValue = 65; float floatValue = 65.0; char c1 = 4; char c2 = // 210; void setup() { while(!Serial); // Wait until serial port's // open on Leonardo and SAMD boards Serial.begin(9600); } void // loop() { Serial.print("chrValue: "); Serial.print(chrValue); // Serial.print(" "); Serial.write(chrValue); Serial.print(" "); // Serial.print(chrValue, DEC); Serial.println(); // Serial.print("byteValue: "); Serial.print(byteValue); // Serial.print(" "); Serial.write(byteValue); Serial.print(" "); // Serial.print(byteValue, DEC); Serial.println(); // Serial.print("intValue: "); Serial.print(intValue); // Serial.print(" "); Serial.print(intValue, DEC); Serial.print(" // "); Serial.print(intValue, HEX); Serial.print(" "); // Serial.print(intValue, OCT); Serial.print(" "); // Serial.print(intValue, BIN); Serial.println(); // Serial.print("floatValue: "); Serial.println(floatValue); // Serial.println(); delay(1000); // delay a second between numbers // chrValue++; // to the next value byteValue++; intValue++; // floatValue += 1; } |
خروجی به شرح زیر است:
1 2 3 4 5 6 7 8 9 | chrValue: A A 65 byteValue: 65 A 65 intValue: 65 65 41 101 1000001 floatValue: 65.00 chrValue: B B 66 byteValue: 66 B 66 intValue: 66 66 42 102 1000010 floatValue: 66.00 ... |
چاپ یک رشته متنی (text string) ساده است: Serial.print(“hello world”);
رشته متنی “hello world” را به دستگاهی در انتهای دیگر پورت سریال ارسال میکند. اگر میخواهید خروجی شما پس از چاپ، یک خط جدید داشته باشد، بهجای دستور ()Serial.print از ()Serial.println استفاده کنید.
همچنین، برای چاپ مقادیر عددی نیز، شما میتوانید از دستور ()Serial.print استفاده کنید. این دستور به شما امکان چاپ دادهها بهصورت متن قابل خواندن توسط انسان را میدهد.
در اینجا چند مثال آورده شده است که همه آنها متغیرهایی ایجاد میکنند که مقادیر مشابهی دارند:
1 2 3 4 5 | char asciiValue = 'A'; // ASCII A has a value of 65 char chrValue = 65; // an 8-bit signed character, this also is ASCII 'A' byte byteValue = 65; // an 8-bit unsigned character, this also is ASCII 'A' int intValue = 65; // a 16-bit signed integer set to a value of 65 float floatValue = 65.0; // float with a value of 65 |
جدول ۲ نشان میدهد که هنگام چاپ متغیرها با استفاده از Arduino routines چه چیزی مشاهده خواهید کرد:
اگر شما انتظار دارید که متغیرهای بایت مانند متغیرهای کاراکتر (یعنی بهصورت ASCII) عمل کنند، باید آنها را به Serial.write(val) تغییر دهید.
اگر برنامهنویس حرفهای هستید، ممکن است تعجب کنید که چرا Arduino از printf پشتیبانی نمیکند. تا حدودی دلیل این موضوع استفاده از حافظه داینامیک توسط printf و کمبود حافظه RAM در بردهای 8 بیتی است. اما بردهای 32 بیتی اخیر دارای حافظهی کافی هستند.
اگرچه آردوینو از printf پشتیبانی نمیکند، اما شما میتوانید از Sprintf برای ذخیره متن فرمتشده در بافر کاراکتر استفاده کنید و سپس با استفاده از Serial.print/println آن بافر را چاپ کنید.
1 2 3 | char buf[100]; sprintf(buf, "At %d seconds, speed = %d, distance = %d", t, s, d); Serial.println(buf); |
اما استفاده از sprintf میتواند کمی ریسک داشته باشد. اگر رشته (string) ای که در حال نوشتن آن هستید، بزرگتر از بافر شما باشد، باعث سرریز (overflow) بافر میشود. درست است که میتوان حدس زد که کاراکترهای سرریز کجا نوشته میشوند، اما درهرصورت این موضوع باعث میشود که اسکچ شما یا کاملاً خراب شود یا بهدرستی عمل نکند.
تابع snprintf این امکان را فراهم میکند که یک آرگومان ارسال کنید که ماکزیمم (حداکثر) تعداد کاراکترها را مشخص میکند (شامل کاراکتر نال (null) میباشد که تمام رشتهها (strings) را پایان میدهد).
برای تعیین طول یک آرایه، شما میتوانید از همان طولی که در تعریف آرایه استفاده میکنید، استفاده کنید (که در این مثال، 100 است)؛ اما به خاطر داشته باشید که در صورت این کار، اگر طول بافر را تغییر دادید، حتما باید طول آرگومان را نیز تغییر دهید.
در عوض، میتوانید از عملگر sizeof برای محاسبه طول بافر استفاده کنید. در تمام موارد، یک کاراکتر (char) برابر با 1 بایت است. در نهایت، بهترین روش این است که اندازه آرایه را بر اندازه نوع داده ای که در آن موجود است تقسیم کنیم، به عبارت دیگر، حاصل sizeof(buf) / sizeof(buf[0]) برابر با طول آرایه است.
1 2 3 | snprintf(buf, sizeof (buf) / sizeof (buf[0]), "At %d seconds, speed = %d, distance = %d", t, s, d); Serial.println(buf); |
برای جلوگیری از این مشکل، بهتر است از طول بافر استفاده کنید. بهعنوانمثال، اگر طول بافر شما ۴۰ بایت باشد، میتوانید از دستور زیر استفاده کنید:
کد زیر را در نظر بگیرید که 400 را چاپ می کند:
1 2 | long buf2[100]; Serial.println(sizeof (buf2)); |
شما میتوانید با محاسبه sizeof (buf2) / sizeof (buf2[0]) نتیجه صحیح را به دست آورید.
استفاده از توابع sprintf یا snprintf عواقب خاصی دارد. مورد اول، اورهد (over head) بافر است که در این مثال برابر با 100 بایت است. علاوه بر این، امکان اورهد (over head) کامپایل تابع در اسکچ شما وجود دارد. در یک برد آردوینو Uno، اضافه کردن این کد موجب افزایش استفاده از مموری (حافظه) به میزان 1,648 بایت میشود که 5٪ از حافظهی برد آردوینو Uno را تشکیل میدهد.
شما میتوانید از قابلیت insertion-style در دستور چاپ (print) در زبان C++ که توسط Arduino استفاده میشود، استفاده کنید. شما میتوانید از قابلیتهای پیشرفتهتری مانند نحوه قراردادن دادهها در دستور چاپ و الگوهای تمپلیتها (templates) بهره ببرید. برای این کار، میتوانید از کتابخانه Streaming توسعهیافته توسط Mikal Hart استفاده کنید. برای کسب اطلاعات بیشتر در مورد این کتابخانه، به وبسایت Mikal مراجعه کنید. همچنین، میتوانید این کتابخانه را با استفاده از مدیریت کتابخانهها در محیط Arduino نصب کنید.
اگر شما از کتابخانه Streaming استفاده میکنید، نتایج زیر را ارائه میدهد:
1 | Serial << "At " << t << " seconds, speed=" << s << ", distance=" << d << endl; |
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
مقالات بیشتر
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.