در قسمت نوزدهم از آموزش آردوینو به بررسی دریافت چندین فیلد متنی در یک پیام و همچنین، ارسال دادههای باینری از آردوینو، پرداختیم. در این قسمت قصد داریم درباره دریافت دادههای باینری از آردوینو در کامپیوتر، ارسال مقادیر باینری از Processing به آردوینو و همچنین، ارسال مقادیر چندین پین آردوینو صحبت کنیم.
دریافت دادههای باینری از آردوینو در کامپیوتر
در ادامه درباره چگونگی پاسخدادن به دادههای باینری ارسال شده از آردوینو در یک زبان برنامهنویسی مانند Processing توضیح داده میشود.
چگونگی انجام این کار بستگی به محیط برنامهنویسیای دارد که روی کامپیوتر شخصی یا مک خود استفاده میکنید. اگر هنوز ابزار مشخصی برای برنامهنویسی ندارید و به دنبال ابزاری هستید که یادگیری آن آسان باشد و بهخوبی با آردوینو کار کند، Processing یک انتخاب عالی است که می توانید از این لینک دانلود کنید.
در اینجا دو کد از Processing برای خواندن یک بایت آورده شده است که از مثال SimpleRead در Processing گرفته شده است:
1 2 | if ( myPort.available() > 0) { // If data is available, val = myPort.read(); // read it and store it in val |
کد زیر یک اسکچ (sketch) در Processing است که اندازهی یک مستطیل را بهصورت متناسب با مقادیر عدد صحیح دریافتشده از کد آردوینو تنظیم میکند:
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 | /* * ReceiveBinaryData_P * * portIndex must be set to the port connected to the Arduino */ import processing.serial.*; Serial myPort; // Create object from Serial class // WARNING! // If necessary change the definition below to the correct port short portIndex = 0; // select the com port, 0 is the first port char HEADER = 'H'; int value1, value2; // Data received from the serial port void setup() { size(600, 600); // Open whatever serial port is connected to Arduino. String portName = Serial.list()[portIndex]; println((Object[]) Serial.list()); println(" Connecting to -> " + portName); myPort = new Serial(this, portName, 9600); } void draw() { // read the header and two binary *(16-bit) integers: if ( myPort.available() >= 5) // If at least 5 bytes are available, { if( myPort.read() == HEADER) // is this the header { value1 = myPort.read(); // read the least significant byte value1 = myPort.read() * 256 + value1; // add the most significant byte value2 = myPort.read(); // read the least significant byte value2 = myPort.read() * 256 + value2; // add the most significant byte println("Message received: " + value1 + "," + value2); } } background(255); // Set background to white fill(0); // set fill to black // draw rectangle with coordinates based on the integers received from Arduino rect(0, 0, value1,value2); } |
✅نکته
زبان برنامهنویسی Processing و آردوینو مشابه هستند. تابع setup در Processing برای انجام تنظیمات اولیه یکباره استفاده میشود، درست مانند آردوینو. در Processing یک پنجره نمایش وجود دارد و تابع setup اندازه آن را با فراخوانی size(600,600) به 600 × 600 پیکسل تنظیم میکند.
کد String portName = Serial.list()[portIndex]; پورت سریال را انتخاب میکند. در Processing، تمام پورتهای سریال در آبجکت Serial.list قرار دارند و این مثال از مقدار یک متغیر به نام portIndex استفاده میکند. println((Object[]) Serial.list()) تمام پورتهای موجود را چاپ میکند و کد myPort = new Serial(this, portName, 9600); پورتی را که بهعنوان portName انتخاب شده است، باز میکند. اطمینان حاصل کنید که مقدار portIndex (در خط دهم) را به پورت سریالی که آردوینو به آن متصل است، تنظیم کنید.
آردوینو معمولاً اولین پورت در مک است؛ اما در کامپیوترهای شخصی (PC)، اگر دستگاه دارای پورت فیزیکی RS-232 باشد، معمولاً آردوینو آخرین پورت است، در غیر این صورت، ممکن است اولین پورت باشد. همچنین، میتوانید به لیست پورتها در IDE آردوینو نگاه کنید که ممکن است پورتهای سریال را به همان ترتیب که Processing آنها را شمارهگذاری میکند، نمایش دهد.
تابع draw در Processing مانند تابع loop در آردوینو عمل میکند؛ این تابع بهصورت مکرر فراخوانی میشود. کد موجود در draw بررسی میکند که آیا دادهای در پورت سریال موجود است یا خیر؛ اگر دادهای وجود داشته باشد، بایتها خوانده شده و به مقدار عدد صحیحی که توسط بایتها نمایش داده میشود، تبدیل میشوند. سپس یک مستطیل بر اساس مقادیر عدد صحیح دریافتشده رسم میشود.
ارسال مقادیر باینری از Processing به آردوینو
در ادامه درباره چگونگی ارسال بایتهای باینری، اعداد صحیح یا مقادیر طولانی (long) از Processing به آردوینو صحبت میشود. بهعنوانمثال، چگونگی ارسال پیامی که شامل یک شناسه پیام ‘tag’ و دو مقدار 16 بیتی باشد.
برای این کار از کد زیر استفاده کنید:
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 | // Processing Sketch /* SendingBinaryToArduino * Language: Processing */ import processing.serial.*; Serial myPort; // Create object from Serial class // WARNING! // If necessary change the definition below to the correct port short portIndex = 0; // select the com port, 0 is the first port public static final char HEADER = 'H'; public static final char MOUSE_TAG = 'M'; void setup() { size(512, 512); String portName = Serial.list()[portIndex]; println((Object[]) Serial.list()); myPort = new Serial(this, portName, 9600); } void draw(){ } void serialEvent(Serial p) { // handle incoming serial data String inString = myPort.readStringUntil('\n'); if(inString != null) { print( inString ); // print text string from Arduino } } void mousePressed() { sendMessage(MOUSE_TAG, mouseX, mouseY); } void sendMessage(char tag, int x, int y){ // send the given index and value to the serial port myPort.write(HEADER); myPort.write(tag); myPort.write((char)(x / 256)); // msb myPort.write(x & 0xff); //lsb myPort.write((char)(y / 256)); // msb myPort.write(y & 0xff); //lsb } |
هنگامی که روی پنجره Processing کلیک میشود، تابع sendMessage با برچسب 8 بیتی که نشان میدهد این پیام مربوط به ماوس است و دو مختصات 16 بیتی ماوس در محورهای x و y فراخوانی میشود. تابع sendMessage مقادیر x و y ۱۶بیتی را بهصورت دو بایت ارسال میکند، بهطوریکه بایت با بیش ترین مقدار اول ارسال میشود.
در اینجا کد آردوینو برای دریافت این پیامها و ارسال نتایج به Processing آورده شده است:
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 | // BinaryDataFromProcessing // These defines must mirror the sending program: const char HEADER = 'H'; const char MOUSE_TAG = 'M'; const int TOTAL_BYTES = 6 ; // the total bytes in a message void setup() { Serial.begin(9600); } void loop(){ if ( Serial.available() >= TOTAL_BYTES) { if( Serial.read() == HEADER) { char tag = Serial.read(); if(tag == MOUSE_TAG) { int x = Serial.read() * 256; x = x + Serial.read(); int y = Serial.read() * 256; y = y + Serial.read(); Serial.println("Got mouse msg:"); Serial.print("x="); Serial.print(x); Serial.print(", y="); Serial.println(y); } else { Serial.print("Unknown tag: "); Serial.write(tag); Serial.println(); } } } } |
برچسب (tag) یک بررسی اضافی برای اعتبار پیام (message validity) فراهم میآورد و این امکان را میدهد که انواع دیگری از پیامها که ممکن است بخواهید ارسال کنید بهطور جداگانه مدیریت شوند. در این مثال، تابع با سه پارامتر فراخوانی میشود: یک برچسب و مختصات ماوس x و y بهصورت 16 بیتی.
کد آردوینو بررسی میکند که حداقل تعداد بایتهای MESSAGE_BYTES دریافت شده باشد تا اطمینان حاصل شود که پیام تا زمانی که تمام دادههای موردنیاز در دسترس نباشد، پردازش نشود. پس از بررسی هدر و تگ، مقادیر 16 بیتی بهصورت دو بایت خوانده میشود، بهطوریکه بایت اول در 256 ضرب میشود تا بایت با بیشترین مقدار به مقدار اصلی خود بازگردانده شود.
اگر بخواهید خروجی سریال آردوینو را به دستگاه دیگری، مانند یک نمایشگر کاراکتری LCD سریال، ارسال کنید، میتوانید از پورت SoftwareSerial یا یکی از پورتهای سریال اضافی برد خود استفاده کنید. شما باید پورت سریال را در تابع setup راهاندازی کنید و تمام دستورات Serial.write و Serial.print/println را تغییر دهید تا از آن پورت سریال استفاده کنند. بهعنوانمثال، تغییرات زیر دادههای سریال را به پین TX1 از پورت Serial1 در آردوینو WiFi Rev 2 ،Leonardo و بیشتر بردهای سازگار با ARM ارسال میکند. ابتدا باید این کد را به تابع setup اضافه کنید:
1 | Serial1.begin(9600); |
و کدهای print/println/write را در انتهای تابع loop بهصورت زیر تغییر دهید:
1 2 3 4 | Serial1.println(); Serial1.println("Got mouse msg:"); Serial1.print("x="); Serial1.print(x); Serial1.print(", y="); Serial1.print(y); |
و:
1 2 3 | Serial1.println(); Serial1.print("Unknown tag: "); Serial1.write(tag); Serial1.println(); |
✅نکته
ارسال مقادیر چندین پین آردوینو
در ادامه این قسمت، چگونگی ارسال گروههایی از بایتهای باینری، اعداد صحیح یا مقادیر طولانی (long) از آردوینو بررسی میشود. بهعنوانمثال، طریقه ارسال مقادیر پینهای دیجیتال و آنالوگ از آردوینو به Processing.
در اینجا، ابتدا یک هدر ارسال میشود و سپس یک عدد صحیح (integer) که شامل مقادیر بیتی پینهای دیجیتال 2 تا 13 است. پس از آن، شش عدد صحیح ارسال میشود که مقادیر پینهای آنالوگ 0 تا 5 را شامل میشود. قسمت های قبل شامل مثال های زیادی هستند که مقادیر پینهای آنالوگ و دیجیتال را تنظیم میکنند و شما میتوانید از آنها برای آزمایش این کد استفاده کنید.
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 | /* * SendBinaryFields * Sends digital and analog pin values as binary data */ const char HEADER = 'H'; // a single character header to indicate // the start of a message void setup() { Serial.begin(9600); for(int i=2; i <= 13; i++) { pinMode(i, INPUT); // set pins 2 through 13 to inputs digitalWrite(i, HIGH); // turn on pull-ups } } void loop() { Serial.write(HEADER); // send the header // put the bit values of the pins into an integer int values = 0; int bit = 0; for(int i=2; i <= 13; i++) { bitWrite(values, bit, digitalRead(i)); // set the bit to 0 or 1 depending // on value of the given pin bit = bit + 1; // increment to the next bit } sendBinary(values); // send the integer for(int i=0; i < 6; i++) { values = analogRead(i); sendBinary(values); // send the integer } delay(1000); //send every second } // function to send the given integer value to the serial port void sendBinary(int value) { // send the two bytes that comprise an integer Serial.write(lowByte(value)); // send the low byte Serial.write(highByte(value)); // send the high byte } |
کد ابتدا یک هدر (کاراکتر H) ارسال میکند و سپس یک عدد صحیح (integer) که مقادیر پینهای دیجیتال را در خود نگه میدارد. از تابع bitRead برای تنظیم یک بیت در این عدد صحیح استفاده میشود تا با مقدار پین مربوطه مطابقت داشته باشد. سپس شش عدد صحیح ارسال میشود که شامل مقادیر خواندهشده از شش پورت آنالوگ است. تمام مقادیر عدد صحیح با استفاده از تابع sendBinary ارسال میشوند. این پیام 15 بایت طول دارد؛ همچنین، دارای یک بایت برای هدر، دو بایت برای مقادیر پینهای دیجیتال و 12 بایت برای شش عدد صحیح آنالوگ میباشد. کد مربوط به ورودیهای دیجیتال و آنالوگ است.
با فرض اینکه پینهای آنالوگ مقادیر:
f 0 on pin 0, 100 on pin 1, and 200 on pin 2 through 500 on pin 5 و پینهای دیجیتال 2 تا 7 در حالت High و پینهای 8 تا 13 در حالت Low باشند، این مقادیر اعشاری (decimal) هر بایت ارسالشده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 72 // the character 'H' - this is the header // two bytes in low high order containing bits representing pins 2-13 63 // binary 00111111 : this indicates that pins 2-7 are high 0 // this indicates that 8-13 are low // two bytes for each pin representing the analog value 0 // pin 0's analog value is 0 and this is sent as two bytes 0 100 // pin 1 has a value of 100, sent as a byte of 100 and a byte of 0 0 ... // pin 5 has a value of 500 244 // the remainder when dividing 500 by 256 1 // the number of times 500 can be divided by 256 |
این کد Processing پیام را میخواند و مقادیر را در کنسول Processing چاپ میکند:
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 | // Processing Sketch /* * ReceiveMultipleFieldsBinary_P * * portIndex must be set to the port connected to the Arduino */ import processing.serial.*; Serial myPort; // Create object from Serial class short portIndex = 0; // select the com port, 0 is the first port char HEADER = 'H'; void setup() { size(200, 200); // Open whatever serial port is connected to Arduino. String portName = Serial.list()[portIndex]; println((Object[]) Serial.list()); println(" Connecting to -> " + portName); myPort = new Serial(this, portName, 9600); } void draw() { int val; if ( myPort.available() >= 15) // wait for the entire message to arrive { if( myPort.read() == HEADER) // is this the header { println("Message received:"); // header found // get the integer containing the bit values val = readArduinoInt(); // print the value of each bit for(int pin=2, bit=1; pin <= 13; pin++){ print("digital pin " + pin + " = " ); int isSet = (val & bit); if( isSet == 0) { println("0"); } else{ println("1"); } bit = bit * 2; //shift the bit to the next higher binary place } println(); // print the six analog values for(int i=0; i < 6; i ++){ val = readArduinoInt(); println("analog port " + i + "=" + val); } println("----"); } } } // return integer value from bytes received from serial port (in low,high order) int readArduinoInt() { int val; // Data received from the serial port val = myPort.read(); // read the least significant byte val = myPort.read() * 256 + val; // add the most significant byte return val; } |
کد Processing منتظر میماند تا 15 کاراکتر دریافت شود. اگر اولین کاراکتر هدر باشد، سپس تابعی به نام readArduinoInt را فراخوانی میکند تا دو بایت را خوانده و آنها را به عدد صحیح تبدیل کند، با انجام عملیاتی ریاضی که آردوینو برای بهدستآوردن بیتهای جداگانه نمایانگر پینهای دیجیتال انجام داده است. سپس شش عدد صحیح به مقادیر آنالوگ تبدیل میشوند.
📍توجه
سخن پایانی
شما میتوانید برای ارسال مقادیر آردوینو به کامپیوتر یا کنترل پینها از کامپیوتر (without making decisions on the board) از Firmata استفاده کنید. کتابخانه Firmata و نمونههای کد (File→Examples→Firmata) در توزیع نرمافزار آردوینو گنجانده شده است و کتابخانهای برای استفاده در Processing نیز در دسترس است. دراینخصوص، شما باید کد Firmata را روی آردوینو بارگذاری و کنترل کنید که پینها ورودی یا خروجی باشند و سپس آنها را تنظیم کنید.