ארדואינו וחברים

זה בודד להיות לבד בפסגה ועד עכשיו ארדואינו דיבר רק עם המחשב. איך אפשר לדבר עם בקרי ארדואינו אחרים?

לכל בקר יש דרך לתקשר עם בקרים אחרים, חייבת להיות לו דרך אחרת אי אפשר היה להעלות אליו קוד. כשאנחנו לוחצים על כפתור העלאה בסביבת הפיתוח של ארדואינו עובר מידע בתקשורת טורית (Serial) אל לוח הארדואינו. תקשורת טורית היא צורת התקשורת הנפוצה ביותר ועליה יושבים הרבה פרוטוקולים נוספים כמו I2C, ETHERNET, SPI, USB וכולם עובדים פחות או יותר באותה צורה אבל עם כל מיני הבדלים כמו תזמון שונה ומספר חיבורים שונה.

RX/TX

נתחיל עם התקשורת הפשוטה ביותר שכוללת שני חוטים לתקשורת דו-כיוונית וחוט אחד לתקשורת חד-כיוונית.

אם אנחנו רק רוצים שארדואינו ישלח מידע לארדואינו אחר אנחנו מחברים את הדק TX בארדואינו שמשדר אל הדק RX בארדואינו שמקבל את המידע. TX זה קיצור של TRANSMIT ו-RX קיצור של RECEIVE.

כמה דברים צריכים לקרות שזה יעבוד:

  • קודם כל האפס(GND) שלהם חייב להיות משותף
  • שניהם צריכים כמובן להיות מחוברים למקור מתח. אפשר לחבר אחד מהם למחשב ואז להעביר 5V לשני כמו בתמונה, או לחבר את שניהם למחשב.
  • קצב העברת הנתונים שתכף נדבר עליו חייב להיות זהה בשניהם
  • כשמעלים קוד לארדואינו המחשב משתמש בהדקים 0 ו-1 אז צריך לעשות את החיבורים רק אחרי גמר העלאת הקוד אחרת הוא לא יעלה.
  • שניהם צריכים לעבוד באותו מתח לוגי. ארדואינו אונו עובד על 5V אז אין בעיה אבל יש הרבה בקרים שעובדים על 3.3V ובמקרה הזה נהיה חייבים להשוות את רמות המתח ויש כל מיני דרכים לעשות את זה.

עכשיו לקוד. הארדואינו המשדר מידע

void setup() { 
 Serial.begin(9600);
}

void loop() {
 
 Serial.write(1);
 delay(5000);
 Serial.write(0);
 delay(5000);
}

כל חמש שניות הוא שולח בייט שונה פעם 0 ופעם 1. Serial.write היא פונקציה שונה במקצת מ-Serial.print והיא מיועדת לשליחת בייטים בצורה יותר גולמית. זה לא ממש משנה באיזה משתמשים אבל רצוי מאוד לקרוא על ההבדלים ביניהם.

עכשיו לארדואינו המקבל מידע:

void setup() {
 Serial.begin(9600);
 pinMode(13, OUTPUT);
}

void loop() {
 if (Serial.available() > 0){
   int x = Serial.read();
   if (x == 1){
     digitalWrite(13, HIGH);
   }
  else{
   digitalWrite(13, LOW);
   }
 }
}

 

הארדואינו הזה מחכה לקבל מידע ובהתאם למספר שהוא מקבל מדליק או מכבה נורה. קצב העברת הנתונים הוא 9600bps שזה מספר ביטים לשנייה, ואפשר להעלות את זה עד 115200 אחר כך זה כבר נהיה לא אמין עם ארדואינו אונו. יש בקרים אחרים בהם אפשר להעלות את הקצב אפילו יותר.

בצורת התקשורת הזאת ארדואינו אחד משדר והשני מקבל ואם רוצים תקשורת דו כיוונית צריך לחבר גם את החוט השני בצורה הבאה:

 

עכשיו ניצור בין שניהם תקשורת פינג פונג שבה אם ארדואינו שולח 1 השני שולח לו 0 ולהיפך כדי להדגים תקשורת בשני הכיוונים. מישהו מהם צריך להתחיל את התקשורת ובקוד זה נעשה בתוך פונקציית setup.

הארדואינו שמתחיל תקשורת:

void setup() { 
 Serial.begin(9600);
 pinMode(13, OUTPUT);
 Serial.write(1);
}

void loop() {
 if (Serial.available() > 0){
   int x = Serial.read();
   if (x == 1){
     digitalWrite(13, HIGH);
     Serial.write(1);
   }
   else{
     digitalWrite(13, LOW);
     Serial.write(0);
   }
 }
 delay(1000);
}

והארדואינו השני עונה לו:

void setup() {
 Serial.begin(9600);
 pinMode(13, OUTPUT);
}

void loop() {
 if (Serial.available() > 0){
   int x = Serial.read();
   if (x == 1){
     digitalWrite(13, HIGH);
     Serial.write(0);
   }
   else{
     digitalWrite(13, LOW);
     Serial.write(1);
   }
 }
 delay(1000);
}

יאה ונאה…

מה שמציק בסיפור הזה הוא שצריך להוציא את החוטים מ-0 ו-1 כל פעם לפני שמעלים קוד לארדואינו. יש דרך לפתור את זה על ידי softwareserial.

עד עכשיו השתמשנו ב-UART מובנה שקיים בארדואינו אונו, לארדואינו אונו יש רק אחד כזה והוא יושב על פינים 0, 1 לעומתו לארדואינו מגה יש ארבעה כאלה. UART הוא מעין חיווט בתוך הבקר שמבצע תקשורת טורית ברמת החומרה (hardware) מה שנותן לו דיוק ואמינות מירבית.

אך ניתן לעשות זאת גם בצורה של תוכנה עם כל פין דיגיטלי של ארדואינו. לדוגמה אפשר לחבר את הדקים 3 ו-4 בארדואינו אחד אל 10 ו-11 בארדואינו אחר וליצור תקשורת משותפת. תקשורת טורית באמצעות תוכנה אפשר לעשות גם עם רכיבים אחרים כמו GPS לדוגמה וכך לחבר מספר רב של רכיבים בתקשורת טורית.

לספריית softwareserial יש מספר מגבלות שצריך לקחת בחשבון למשל ככל שמעלים את המהירות התקשורת הופכת לפחות אמינה. אם יוצרים מספר פתחים של תקשורת רק פתח אחד יכול לתקשר בזמן מסויים ורק אחרי שהוא מסיים הפתח השני. בואו נראה, ניישם את אותו הקוד מקודם באמצעות תוכנה:

 

#include <SoftwareSerial.h>
 SoftwareSerial port1(6, 7); // RX, TX
 int zero = 0;
 void setup() {
   port1.begin(150000);
   pinMode(13, OUTPUT);
   port1.write(1);
 }

void loop() {
 if (port1.available() > 0){
   int x = port1.read();
   if (x == 1){
     digitalWrite(13, HIGH);
     port1.write(1);
   }
   else{
     digitalWrite(13, LOW);
     port1.write(zero);
   }
 }
 delay(1000);
 }

ועכשיו הארדואינו שמקבל את המידע:

#include <SoftwareSerial.h>
SoftwareSerial port2(6, 7); // RX, TX
int zero = 0;
void setup() {
 port2.begin(150000);
 pinMode(13, OUTPUT);
}

void loop() {
 if (port2.available() > 0){
   int x = port2.read();
   if (x == 1){
     digitalWrite(13, HIGH);
     port2.write(zero);
   }
 else{
   digitalWrite(13, LOW);
   port2.write(1);
   }
 }
 delay(1000);
}

וכמובן נחליף את החיבורים בשני הארדואינו ל-6 ו-7 . יש באג קטן בספרייה שבו צריך להצהיר על 0 כמשתנה int אז גם את זה עשינו. לגבי המהירות אני הצלחתי ליישם את זה גם עם 150000bps אבל זה בגלל שמעבירים בייט גולמי אחד, אם ננסה להעביר משהו יותר ארוך זה יקרוס ונצטרך להוריד את המהירות לאיזור 50000bps.

אם רוצים ליצור כמה חיבורי תקשורת של תוכנה פשוט מוסיפים את זה בקוד:

SoftwareSerial port1(2, 3);

SoftwareSerial port2(4, 5);

SoftwareSerial port3(6, 7);

זה מאוד נוח לעבוד עם הספריה הזאת אבל מנסיון אני אומר צפו לבעיות אם יש הרבה חיבורים שונים. יש שני דברים שחסרים לי מאוד בארדואינו אונו ואחד מהם זה UART נוסף. השני זה חיבורים נוספים של 5V על הלוח.

Serial היא אחת הספריות המסובכות בארדואינו ולוקח הרבה זמן להבין אותה לעומק, אפשר לקרוא עוד קצת על הפרוטוקול כאן.

I2C

עכשיו הגיע הזמן להשתמש בפרוטוקולים קצת יותר מתוחכמים. נחבר שני ארדואינו שידברו בפרוטוקול I2C (מבוטא isquaredc) ונראה מה אפשר להוציא מזה.

בפרוטוקול I2C אנחנו עדיין עובדים עם שני חוטים ההבדל הוא שאחד מהם משמש להעברת נתונים דו כיוונית והשני הוא קו שעון שרק אחראי לתזמון. יש גם כמה סטנדרטים שצריך לנהוג לפיהם כדי ליישם את הפרוטוקול.

קודם כל צריך לזכור שב-I2C וגם SPI החיבורים הולכים אחד לשני כלומר A4 ל-A4 ו-A5 ל-A5 שלא כמו בתקשורת טורית רגילה.

עכשיו אנחנו צריכים להעזר בספריית Wire שאחראית על התוכנה של הפרוטוקול ויש באתר ארדואינו דוגמה לתקשורת I2C בין שני לוחות.

הפרוטוקול הוא כאמור קצת יותר מסובך מתקשורת טורית רגילה אבל אחרי שמבינים את העקרון אפשר ליישם אותו. בקר אחד חייב להיות ה-MASTER ששולט בשליחת וקבלת המידע. אפשר לשלוח בייטים, תוים ומחרוזות. פה אנחנו שולחים את המספר 1:

#include <Wire.h>
void setup() {
 Wire.begin(); 
}
void loop() {
 Wire.beginTransmission(1); // talk to device number 1
 Wire.write(1); // send the number 1
 Wire.endTransmission(); 
 delay(5000);
 Wire.beginTransmission(1); 
 Wire.write(0);
 Wire.endTransmission();
 delay(5000);
}

אפשר כאן לראות שאנחנו מתחילים תקשורת עם רכיב מספר 1 שזה מספר שרירותי מ-0 עד 255. ברוב הדוגמאות המספר יופיע בצורה של HEX נגיד 0x27. פה יש לנו שליטה על ה-SLAVE אז אפשר להכניס איזה מספר שאנחנו רוצים רק לזכור לתת אותו לבקר השני.

הבקר שיוצר תקשורת שולח בייט שווה ערך למספר 1 , מחכה 5 שניות ואז שולח אפס.

הבקר השני מקשיב ומחכה למידע. בגלל שפרוטוקול I2C הוא סינכרוני הבקר שמקשיב חייב להיות דרוך עם פונקציה שמחכה ברקע למידע:

#include <Wire.h>
int x;
void setup() {
 pinMode(13, OUTPUT);
 Wire.begin(1); 
 Wire.onReceive(receiveEvent); 
}

void loop() {
 if (x == 1) {
   digitalWrite(13, HIGH);
 }
 else{
   digitalWrite(13, LOW);
 } 
}


void receiveEvent() {
 while ( Wire.available()) { 
   x = Wire.read(); 
 }
}

בתקשורת RX/TX רגילה התזמון הוא אסינכרוני אז לא חייבים פונקציה כמו פה.

כל מי שמכיר את פרוטוקול I2C ישאל איפה נגדי ה-PULLUP המפורסמים לא צריך אותם? התשובה היא שהם קיימים בתוך ההדקים A4, A5 והספריה כבר מדליקה אותם בשבילנו.

ככה אפשר לתקשר עם בקרים שונים בתיאוריה עד 255 בקרים כשכולם מחוברים במקביל אל הדקים A4, A5 או SDA,SCL לחילופין.

אם רוצים ליצור תקשורת דו כיוונית כלומר שה-slave ישלח מידע חזרה אז מי שצריך לנהל את זה הוא ה-Master ולבקש מידע חזרה כמו בדוגמה הבאה באתר ארדואינו.

כמו שנאמר בתקשורת I2C ו-SPI הכל צריך להיות מתוזמן נכון עם פונקציות שמחכות ברקע.

SPI

התקשורת השלישית שניישם היא SPI .

יש לפחות שלושה חוטים שצריכים להיות מחוברים בתקשורת SPI:

  • (MOSI) : פין 11
  • (MISO) : פין 12
  • (SCK) : פין 13

פין 10 הוא זה שבוחר את ה-Slave ויש צורך להשתמש בו כשמחברים יותר מרכיב אחד.

שימו לב לשישיית המחברים מצד ימין. הם נקראים ICSP ( In Circuit Serial Programming) והם נותנים אפשרות לתכנת את הארדואינו בפרוטוקול SPI. אפשר להשתמש בהם לתקשר עם ארדואינו אחר למעשה זה לא ממש משנה כי הם מחוברים במקביל לפינים 11,12,13 כך שבכל מקרה לא נצליח ככה לחסוך את ההדקים לשימוש אחר.

בפרוטוקול הזה אפשר להגיע למהירות מרשימה מאוד והאמת שבקר ארדואינו לא יכול לעמוד במהירות הגבוהה ביותר לכן בדוגמה הבאה אנחנו מורידים את המהירות בחצי.

זה הקוד של הארדואינו המשדר:

#include <SPI.h>

void setup (void) {
 digitalWrite(SS, HIGH); // disable Slave Select
 SPI.begin ();
 SPI.setClockDivider(SPI_CLOCK_DIV8);//divide the clock by 8
}

void loop (void) {
 char c;
 digitalWrite(SS, LOW); // enable Slave Select
 // send test string
 for (const char * p = "Hello, world!\r" ; c = *p; p++) {
   SPI.transfer (c);
   Serial.print(c);
 }
 digitalWrite(SS, HIGH); // disable Slave Select
 delay(1000);
}

 

וזה הקוד של הארדואינו שמקבל את המידע:

#include <SPI.h>
char buff [50];
volatile byte indx;
volatile boolean process;

void setup (void) {
 Serial.begin (115200);
 pinMode(MISO, OUTPUT); // have to send on master in so it set as output
 SPCR |= _BV(SPE); // turn on SPI in slave mode
 indx = 0; // buffer empty
 process = false;
 SPI.attachInterrupt(); // turn on interrupt
}
ISR (SPI_STC_vect) {
 byte c = SPDR; // read byte from SPI Data Register
 if (indx < sizeof buff) {
   buff [indx++] = c; // save data in the next index in the array buff
   if (c == '\r') //check for the end of the word
     process = true;
 }
}

void loop (void) {
 if (process) {
   process = false; //reset the process
   Serial.println (buff); //print the array on serial monitor
   indx= 0; //reset button to zero
 }
}

כל שניה ארדואינו שולח זרם של ביטים שנשמר בבאפר של הארדואינו השני , ולשם זה אנחנו צריכים להשתמש בפסיקה.

הקוד נלקח ושופצר מהקישור הבא שמוסבר בו בפירוט איך עובד פרוטוקול SPI.

מה המסקנה שלי מכל צורות התקשורת האלה?

כשרוצים ליצור תקשורת בין שני בקרים האפשרות האמינה והטובה ביותר היתה ונשארה לחבר ביניהם חוטים. לא צריך לרוץ לתקשורת אלחוטית כמו בלוטוס או אינטרנט אלחוטי ולעשות את זה רק כשאין ברירה.

9 תגובות על “ארדואינו וחברים

  1. עידו אומר:

    הי, יש דרך לגרום להם לתקשר גם כשחובר מסך טאץ’? הוא תופס כמעט את כל הפינים אולי חוץ מהשניים האחרונים בכל צד..

    • Hackstore אומר:

      הם תפוסים על ידי המרחיב אבל לא על ידי התוכנה מה שאומר שאפשר להשתמש בהם.
      אפשר להתחבר אליהם מלמטה או לחילופין לחבר כבל USB בין שני ארדואינו והתקשורת ביניהם תעבוד.

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *