در قسمت سی و ششم از آموزش آردوینو به بررسی ردیابی حرکت چرخشی و ردیابی حرکت چرخشی در اسکچ با استفاده از وقفهها پرداختیم. در این قسمت قصد داریم درباره استفاده از ماوس و دریافت موقعیت از GPS صحبت کنیم.
فرض کنید میخواهید حرکتهای یک ماوس سازگار با PS/2 را تشخیص دهید و به تغییرات مختصات افقی (x) و عمودی (y) واکنش نشان دهید. برای این کار باید از LEDها برای نمایش حرکت ماوس استفاده کنید. شدت روشنایی LEDها با حرکت ماوس در جهت افقی (چپ و راست) و عمودی (نزدیکتر و دورتر) تغییر میکند. کلیککردن دکمههای ماوس باعث میشود موقعیت فعلی بهعنوان نقطهی مرجع تنظیم شود. (در شکل 1 اتصالات نشاندادهشده است.) برای استفاده از این کد لازم است کتابخانهی PS/2 را نصب کنید. همچنین، نیاز است فایل ps2.h را که در پوشهی ps2 قرار دارد با یک ویرایشگر متنباز کنید و عبارت #include “WProgram.h” را به #include “Arduino.h” تغییر دهید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /* Mouse an arduino sketch using ps2 mouse library from http://www.arduino.cc/playground/ComponentLib/Ps2mouse */ #include <ps2.h> const int dataPin = 5; const int clockPin = 6; const int xLedPin = 9; // Use pin 8 on the MKR boards const int yLedPin = 10; const int mouseRange = 255; // the maximum range of x/y values char x; // values read from the mouse char y; byte status; int xPosition = 0; // values incremented and decremented when mouse moves int yPosition = 0; int xBrightness = 128; // values increased and decreased based on mouse position int yBrightness = 128; const byte REQUEST_DATA = 0xeb; // command to get data from the mouse PS2 mouse(clockPin, dataPin); // Declare the mouse object void setup() { mouseBegin(); // Initialize the mouse } void loop() { // get a reading from the mouse mouse.write(REQUEST_DATA); // ask the mouse for data mouse.read(); // ignore ack status = mouse.read(); // read the mouse buttons if(status & 1) // this bit is set if the left mouse btn pressed xPosition = 0; // center the mouse x position if(status & 2) // this bit is set if the right mouse btn pressed yPosition = 0; // center the mouse y position x = mouse.read(); y = mouse.read(); if( x != 0 || y != 0) { // here if there is mouse movement xPosition = xPosition + x; // accumulate the position xPosition = constrain(xPosition,-mouseRange,mouseRange); xBrightness = map(xPosition, -mouseRange, mouseRange, 0,255); analogWrite(xLedPin, xBrightness); yPosition = constrain(yPosition + y, -mouseRange,mouseRange); yBrightness = map(yPosition, -mouseRange, mouseRange, 0,255); analogWrite(yLedPin, yBrightness); } } void mouseBegin() { // reset and initialize the mouse mouse.write(0xff); // reset delayMicroseconds(100); mouse.read(); // ack byte mouse.read(); // blank mouse.read(); // blank mouse.write(0xf0); // remote mode mouse.read(); // ack delayMicroseconds(100); } |
شکل 1: اتصال ماوس برای نشاندادن موقعیت و روشنکردن LEDها
شکل 1 یک سوکت female PS/2، محفظهای که ماوس در آن وصل میشود را از نمای روبهرو نشان میدهد. اگر سوکت female در اختیار ندارید و بریدن سر کابل ماوس برایتان مشکلی ندارد، میتوانید سیمها را بررسی کنید تا ببینید هر کدام به کدام پین متصل است و سپس آنها را به پینهای هدر لحیم کنید تا مستقیماً به پینهای مناسب آردوینو وصل شوند. یک تست پیوستگی (continuity test) بین پین و سیم به شما کمک میکند خیلی سریع تشخیص دهید کدام سیم به کدام پین مربوط است. اما اگر تست را از سمت سوکت male که از ماوس بریدهاید انجام میدهید، باید توجه داشته باشید که دیاگرام پین ها را باید چپ و راست برعکس در نظر بگیرید.
سیمهای سیگنال ماوس (کلاک و دیتا) و همچنین سیمهای تغذیه را مطابق شکل 1 به آردوینو وصل کنید. این کار فقط با دستگاههای سازگار با PS/2 کار میکند، بنابراین باید یک ماوس قدیمی پیدا کنید—بیشتر ماوسهایی که کانکتور گرد PS/2 دارند برای این کار مناسب اند.
تابع mouseBegin ماوس را راهاندازی میکند تا به درخواستهای مربوط به حرکت و وضعیت دکمهها پاسخ دهد. کتابخانهی PS/2 ارتباط سطح پایین با ماوس را مدیریت میکند. دستور mouse.write برای اطلاعدادن به ماوس استفاده میشود که قرار است داده درخواست شود. اولینبار که mouse.read فراخوانی میشود، یک پیام تأیید (acknowledgment) دریافت میشود که در این مثال نادیده گرفته شده است. بار دوم که mouse.read فراخوانی میشود، وضعیت دکمهها به دست میآید و دو فراخوانی آخر mouse.read مقدار حرکت در محورهای x و y را از آخرین درخواست تاکنون برمیگردانند.
این کد بررسی میکند که کدام بیتها در مقدار status روی HIGH قرار دارند تا مشخص شود دکمهی چپ یا راست ماوس فشرده شده است. دو بیت سمت راست زمانی که دکمههای چپ و راست ماوس فشار داده شوند روی HIGH قرار میگیرند و این موضوع در کد زیر بررسی میشود:
1 2 3 4 5 | status = mouse.read(); // read the mouse buttons if(status & 1) // rightmost bit is set if the left mouse btn pressed xPosition = 0; // center the mouse x position if(status & 2) // this bit is set if the right mouse btn pressed yPosition = 0; // center the mouse y position |
مقادیر x و y که از ماوس خوانده میشوند نشاندهندهی میزان حرکت از زمان آخرین درخواست هستند و این مقادیر در متغیرهای xPosition و yPosition جمع میشوند.
مقادیر x و y مثبت خواهند بود اگر ماوس به سمت راست یا دورتر از شما حرکت کند و منفی خواهند بود اگر به سمت چپ یا به سمت شما حرکت کند.
این کد با استفاده از تابع constrain اطمینان میدهد که مقدار جمعشده از محدودهی تعریفشده (mouseRange) فراتر نرود.
1 2 | xPosition = xPosition + x; // accumulate the position xPosition = constrain(xPosition,-mouseRange,mouseRange); |
محاسبهی yPosition یک روش کوتاه برای انجام همان کار را نشان میدهد؛ در اینجا محاسبهی مقدار y مستقیماً داخل فراخوانی تابع constrain انجام میشود:
1 | yPosition = constrain(yPosition + y,-mouseRange,mouseRange); |
اگر دکمههای چپ و راست ماوس فشار داده شوند، مقادیر متغیرهای xPosition
و yPosition
ریست و در نتیجه، صفر میشوند.
LEDها با استفاده از analogWrite روشن میشوند تا موقعیت ماوس را نشان دهند—در مرکز با نصف روشنایی و با افزایش یا کاهش موقعیت ماوس، شدت روشنایی افزایش یا کاهش مییابد. برای اینکه این کار درست انجام شود، باید از پینی که قابلیت PWM دارد استفاده کنید. اگر برد شما پینهای ۹ و ۱۰ را برای PWM پشتیبانی نکند (اکثر بردها پشتیبانی میکنند)، نور LEDها بهجای کموزیاد شدن، فقط روشن و خاموش میشود. در خانوادهی بردهای MKR، پین ۹ از PWM پشتیبانی نمیکند، بنابراین باید سیمکشی و کد را تغییر دهید تا از پینی استفاده کنید که PWM داشته باشد.
میتوان موقعیت ماوس را روی Serial Plotter رسم کرد، با افزودن کد زیر درست بعد از دومین فراخوانی analogWrite():
1 | printValues(); // show button and x and y values on Serial Monitor/Plotter |
همچنین لازم است این خط کد را به تابع setup() اضافه کنید:
1 | Serial.begin(9600); |
تابع زیر را در انتهای اسکچ اضافه کنید تا موقعیت فعلی ماوس را چاپ یا رسم کند:
1 2 3 4 5 6 7 8 | void printValues() { Serial.print("X:"); Serial.print(xPosition); Serial.print(",Y:"); Serial.print(yPosition); Serial.println(); } |
فرض کنید میخواهید با استفاده از یک ماژول GPS موقعیت مکانی را تعیین کنید.
امروزه چندین ماژول GPS سازگار با آردوینو در دسترس هستند. اکثر آنها از رابط سریال برای ارتباط با میکروکنترلر میزبان خود استفاده میکنند و از پروتکلی به نام NMEA 0183 پیروی میکنند. این استاندارد صنعتی امکان میدهد دادههای GPS بهصورت جملات ASCII قابل خواندن برای انسان به دستگاههای دریافتکننده مانند آردوینو ارسال شوند. برای مثال، جملهی زیر از NMEA:
1 | $GPGLL,4916.45,N,12311.12,W,225444,A,*1D |
این جمله، در کنار اطلاعات دیگر، یک موقعیت جغرافیایی روی کرهی زمین را نشان میدهد: ۴۹° ۱۶.۴۵′ عرض شمالی و ۱۲۳° ۱۱.۱۲′ طول غربی.
برای تعیین موقعیت، اسکچ آردوینو شما باید این رشتهها را تجزیه (parse) کرده و متن مرتبط را به فرمت عددی تبدیل کند. نوشتن کد برای استخراج دستی دادهها از جملات NMEA میتواند پیچیده و زمانبر باشد و باتوجهبه حافظهی محدود آردوینو، مشکلساز شود.
راهبرد کلی برای استفاده از GPS به شرح زیر است:
با استفاده از TinyGPSPlus مراحل زیر را انجام میدهید:
اسکچ زیر نشان میدهد چگونه میتوانید دادهها را از یکGPS متصل به پورت سریال آردوینو دریافت کنید. هر پنج ثانیه، اگر دستگاه در نیمکرهی جنوبی باشد، LED داخلی یکبار و اگر در نیمکرهی شمالی باشد، دو بار چشمک میزند. اگر پینهای TX و RX آردوینو شما به یک دستگاه سریال دیگر مانند Serial1 متصل هستند، تعریف GPS_SERIAL را تغییر دهید (به جدول 1 مراجعه کنید).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /* GPS sketch * Indicate which hemisphere your GPS is in with the built-in LED. */ #include "TinyGPS++.h" // Change this to the serial port your GPS uses (Serial, Serial1, etc.) #define GPS_SERIAL Serial TinyGPSPlus gps; // create a TinyGPS++ object #define HEMISPHERE_PIN LED_BUILTIN void setup() { GPS_SERIAL.begin(9600); // GPS devices frequently operate at 9600 baud pinMode(HEMISPHERE_PIN, OUTPUT); digitalWrite(HEMISPHERE_PIN, LOW); // turn off LED to start } void loop() { while (GPS_SERIAL.available()) { // encode() each byte; if encode() returns "true", // check for new position. if (gps.encode(GPS_SERIAL.read())) { if (gps.location.isValid()) { if (gps.location.lat() < 0) // Southern Hemisphere? blink(HEMISPHERE_PIN, 1); else blink(HEMISPHERE_PIN, 2); } else // panic blink(HEMISPHERE_PIN, 5); delay(5000); // Wait 5 seconds } } } void blink(int pin, int count) { for (int i = 0; i < count; i++) { digitalWrite(pin, HIGH); delay(250); digitalWrite(pin, LOW); delay(250); } } |
ارتباط سریال را با سرعت موردنیاز ماژول GPS خود شروع کنید. برای اطلاعات بیشتر در مورداستفاده از ارتباط سریال در آردوینو، به قسمتهای قبلی مراجعه کنید.
یک اتصال با سرعت ۹۶۰۰ بیت در ثانیه (baud) با ماژول GPS برقرار میشود. وقتی دادهها شروع به جریانیافتن کردند، با روش encode() پردازش میشوند که دادههای NMEA را تجزیه میکند. بازگشت مقدار true از encode() نشان میدهد که TinyGPSPlus توانسته یک جمله کامل را تجزیه کند و دادهی موقعیت جدید ممکن است در دسترس باشد. این زمان مناسبی است تا با فراخوانی gps.location.isValid() بررسی کنید که موقعیت دریافتی معتبر است یا خیر.
اتصال یک ماژولGPS به آردوینو معمولاً بهسادگی وصل کردن دو یا سه سیم داده از GPS به پینهای ورودی آردوینو است، همانطور که در جدول 1 نشاندادهشده است. اگر از برد ۵ ولتی مانندUno استفاده میکنید، میتوانید از ماژول GPS با ولتاژ ۳٫۳ ولت یا ۵ ولت استفاده کنید. اما اگر از بردی استفاده میکنید که تحمل ۵ ولت را ندارد، مانند بردهای مبتنی بر SAMD مثل Arduino Zero،Adafruit Metro M0/M4 یا SparkFun Redboard Turbo، باید از ماژول GPS با ولتاژ ۳٫۳ ولت استفاده کنید.
جدول 1: اتصالات پین های GPS
در این کدی فرض شده است که GPS مستقیماً به پینهای سریال داخلی آردوینو متصل است. در بردهای مبتنی بر ATmega328 مانند Arduino Uno، این معمولاً طراحی راحتی نیست، زیرا پینهای RX و TX (پینهای ۰ و ۱) با اتصال سریال USB مشترک هستند. در بسیاری از پروژهها، ممکن است بخواهید از پورت سریال سختافزاری برای ارتباط با یک کامپیوتر میزبان یا سایر دستگاههای جانبی استفاده کنید که در این صورت این پورت نمیتواند توسط GPS استفاده شود. در چنین مواردی، یک جفت پین دیجیتال دیگر را انتخاب کرده و از کتابخانهای برای شبیهسازی پورت سریال (Soft Serial) استفاده کنید تا با GPS ارتباط برقرار شود.
با خاموش بودن آردوینو و GPS، سیم TX ماژول GPS را به پین ۲ و سیم RX را به پین ۳ آردوینو وصل کنید تا پورت سریال سختافزاری برای دیباگ آزاد شود. سپس با اتصال کابل USB به کامپیوتر میزبان، اسکچ زیر را اجرا کنید تا نگاهی دقیق به عملکرد TinyGPS از طریقSerial Monitor آردوینو داشته باشید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /* GPS sketch with logging */ #include "TinyGPS++.h" // Delete the next four lines if your board has a separate hardware serial port #include "SoftwareSerial.h" #define GPS_RX_PIN 2 #define GPS_TX_PIN 3 SoftwareSerial softserial(GPS_RX_PIN, GPS_TX_PIN); // create soft serial object // If your board has a separate hardware serial port, // change "softserial" to that port #define GPS_SERIAL softserial TinyGPSPlus gps; // create a TinyGPSPlus object void setup() { Serial.begin(9600); // for debugging GPS_SERIAL.begin(9600); // Use Soft Serial object to talk to GPS } void loop() { while (GPS_SERIAL.available()) { int c = GPS_SERIAL.read(); Serial.write(c); // display NMEA data for debug // Send each byte to encode() // Check for new position if encode() returns "True" if (gps.encode(c)) { Serial.println(); float lat = gps.location.lat(); float lng = gps.location.lng(); unsigned long fix_age = gps.date.age(); if (!gps.location.isValid()) Serial.println("Invalid fix"); else if (fix_age > 2000) Serial.println("Stale fix"); else Serial.println("Valid fix"); Serial.print("Lat: "); Serial.print(lat); Serial.print(" Lon: "); Serial.println(lng); } } } |
توجه داشته باشید که میتوانید از سرعت ارتباط (baud rate) متفاوتی برای اتصال به Serial Monitor و GPS استفاده کنید.
این اسکچ جدید همانند مثال قبلی است (اما برای کوتاهشدن آن، کد چشمکزدن LED حذف شده است) و دیباگ بسیار آسانتری دارد. در هر زمان میتوانید یک نمایشگر LCD سریال را به پورت سریال داخلی وصل کنید تا جملات NMEA و دادههای TinyGPSPlus را مشاهده کنید. همچنین میتوانید از طریق Serial Monitor آردوینو به پورت سریال متصل شوید.
هنگامی که برق روشن میشود، یک واحد GPS شروع به ارسال جملات NMEA میکند. بااینحال، جملاتی که شامل دادههای موقعیت مکانی معتبر هستند، تنها پس از آنکه GPS یک فیکس (Fix) برقرار کند، ارسال میشوند. برای این کار، آنتن GPS باید دید مستقیمی به آسمان داشته باشد و این فرایند میتواند تا دو دقیقه یا بیشتر طول بکشد. هوای طوفانی یا وجود ساختمانها و موانع دیگر نیز ممکن است توانایی GPS برای تعیین دقیق موقعیت را مختل کند.
پس اسکچ چگونه میداند که TinyGPSPlus دادههای موقعیت معتبر را ارائه میدهد؟ پاسخ در مقدار بازگشتی تابع gps.location.isValid() نهفته است. مقدار false به این معنی است که TinyGPS هنوز هیچ جملهی معتبری شامل دادههای موقعیت را تجزیه نکرده است. در این حالت، مختصات عرض و طول جغرافیایی بازگشتی نیز نامعتبر خواهند بود.
همچنین میتوانید بررسی کنید که آخرین فیکس چقدر قدیمی است. تابع gps.date.age() تعداد میلیثانیههای گذشته از آخرین فیکس را برمیگرداند. اسکچ مقدار آن را در متغیر fix_age ذخیره میکند. در عملکرد عادی، انتظار میرود که مقدارfix_age نسبتاً کم باشد. دستگاههای GPS مدرن قادرند دادههای موقعیت را با فراوانی یک تا پنج بار در ثانیه یا بیشتر گزارش دهند، بنابراین اگر fix_age بیش از حدود ۲۰۰۰ میلیثانیه باشد، ممکن است مشکلی وجود داشته باشد. ممکن است GPS در حال عبور از یک تونل باشد یا یک اشکال در سیمکشی باعث خراب شدن جریان دادههای NMEA شود و چکسام (Checksum) را نامعتبر کند (محاسباتی برای بررسی عدم خرابی دادهها). در هر صورت، مقدار بزرگ fix_age نشان میدهد که مختصات بازگشتی از get_position() قدیمی و منسوخ هستند.
ردیابی حرکت چرخشی | قسمت سی و ششم...
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.