12 - מנגנון נעילה

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

בפרויקטים הקודמים השתמשנו ברכיב ה Piezo בשביל להוציא פלט (תווים), אך אנחנו יכולים גם להשתמש בו כאמצעי קלט ע”י חיבור למקור כוח (5V, +), כדי החיישן של ה Piezo יהיה מסוגל לזהות תנודות שאותם אנו יכולים לקרוא עם הכניסה האנלוגית של ה Arduino (כניסה A0). כאשר רכיב ה Piezo מחובר למשטח מוצק שיכול לרטוט (כמו משטח עץ לדוגמה), הארדואינו יכול לחוש את אינטנסיביות הרטט, התזוהה של המשטח. ואנו יכולים לבדוק האם הדפיקה על העץ (knock) נמצאת בטווח כלשהו (עליו החלטנו מראש), ואף לעקוב אחר מספר הדפיקות ולראות אם כולן תואמות את ההגדרות שלנו.

בנוסף, בפרויקט נעשה שימוש במתג שיאפשר לנו לנעול את המנוע במקום, ונוריות שיראו לנו באופן וויזואלי את המצב שלו: נורת ה LED האדומה תראה שהקופסא נעולה, נורה ירוקה תראה שהקופסא אינה נעולה, ונורה צהובה תראה לנו אם התקבלה דפיקה חוקית (בטווח שקבענו).

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

השתמשנו בהמון פונקציות מובנות עד כה (כאלה שקיבלנו “בחינם” כחלק מהספרייה המובנת של ה Arduino, ולכן לא היה לנו צורך לכתוב אותן, אלא רק להשתמש בהן), לדוג הפונקציות הראשיות ()void setup ו ()void loop, שאינן מחזירות אף ערך [לכן כתוב בתחילתן void – ריק, ולא את הערך שהן מחזירות (אם הן היו מחזירות מספר שלם היה כתוב שם int, לדוגמה)], ואינן מקבלות אף פרמטר (לכן אין אף פרמרטר בסוגריים). אך בנוסף השתמשנו בפונ’ ()pinMode שכן מקבלת 2 ערכים (מס’ פין, ואת המילה השמורה INPUT או OUTPU) אך לא מחזירה אף ערך, ובפונ’ ()digitalRead שמקבלת פרמטר (את מס’ הפין שממנו יש לקרוא את הערך), ומחזירה ערך (את הערך הנקרא מהפין שהיא קיבלה בפרמטר), וכ’ו.

 

חיבור הרכיבים

כמו תמיד, חברו ללוח המטריצה חוטים מגשרים מעמודות ה+ וה-, אל ה 5V וה GND (בהתאמה, כפי שעשינו עד כה). מקמו את המתג על גבי לוח המטריצה כך שיחובר מעל המחיצה של הלוח (שיחובר לשני צדדי לוח המטירצה), נחבר את הרגל העליונה של החלק הקרוב ל Arduino לכוח (5v, עמודת ה+), ואת הרגל התחתונה של אותו הצד לפין הדיגיטלי מס’ 2. בחלק השני של לוח המטריצה נחבר נגד 10kilohm מעמודת האדמה (-) אל הרגל התחתונה של המתג.

הצמידו את רכיב ה Piezo ללוח המטירצה [אם יש לו חוטים, חברו את האדום לכוח, 5V, או אם יש לו סימון + באחד הצדדים, חברו את הצד הזה לכוח. במידה ואין לו אף סימון המצביע על קוטביות, חברו אותו איך שתבחרו (אין זה משנה)]. את החיבור השני של ה Piezo חברו לעמודת המינוס (דרך נגד 1megohm) ולפין האנלוגי A0 (כמובן שהחיבור של הפין צריך להגיע לפני החיבור לאדמה – שאם תחברו הפוך, שהרי לא תקבלו כל ערך בעת הקריאה של הפין האנלוגי).

חברו את שלושת נוריות ה LED אל לוח המטריצה, כשהקתודה (הרגל הקצרה) מחוברת לרקקע דרך חוט מגשר, והאנודה (הרגל הארוכה) מחוברת לנגד 200ohm ומשם לפינים הדיגטליים של ה Arduino (הנורה הצהובה לפין מס’ 3, הנורה הירוקה לפין מס’ 4, והנורה האדומה לפין מס’ 5).

חברו את הכוח (החוט האדום) והאדמה (החוט השחור) של מנוע הסרוו לעמודות ה+ וה- של החלק השני של לוח המטריצה. בנוסף, חברו לאותן עמודות חוטים מגשרים מעמודות ה+ וה- של החלק הראשון של לוח המטריצה (החלק הקרוב יותר ל Arduino, שאליו חיברתם בהתחלה את החוטים המגשרים כדי להעביר כוח ללוח המטריצה). את חוט המידע של מנוע הסרוו חברו לפין הדיגיטלי מס’ 9, והניחו קבל 100uf בעמודת ה+ וה- שאליהם מחובר מנוע הסרוו. זוכרים מפרויקט 5, למה הקבל משמש?

כאשר מנוע הסרוו מתחיל את התזוזה שלו, הוא צורך זרם גבוה מאשר כשהוא כבר בתזוזה. דבר כזה יגרום לשקע במתח על גבי הלוח. לכן נניח קבל 100uf בצמוד לכוח ולאדמה (החוטים אדום ושחור שיוצאים ממנוע הסרוו ומחוברים ללוח המטריצה) כדי שיאגור את המטען החשמלי שהסרוו אינו משתמש בו, הקבל “ישטח” כל שינוי מתח שיקרה. את הקבל חברו כך שהרגל הקצרה (קתודה, מסומנת על הקבל בפס שחור) תהיה מחוברת לאדמה (חוט מגשר שחור) והרגל הארוכה יותר (אנודה) תהיה מחוברת למקור הכוח (חוט מגשר אדום).
להלן סכמה ואיור להמחשה:

Wiring Sketch

 

הקוד

נתחיל בלייבא את ספריית ה Servo, וליצור מופע של מחלקת הסרוו. בנוסף, ניצור משתנים קבועים (const) מסוג מספרים שלמים (int) שישמרו את מספרי הפינים שלנו: משתנה בשם piezo שישמור את הפין האנלוגי A0, משתנה בשם switchPin שישמור את הפין של המתג (מס’ 2), משתנה בשם yellowLed שישמור את הערך 3 (פין מס’ 3), greenLed שישמור את הערך 4, ו redLed שישמור ת הערך 5.

נוסף לאלו נגדיר 2 משתני int בשם knockVal ו switchVal שישמרו לנו את הערכים שנקרא מהפין של המתג ומהפין של ה Piezo. עוד 2 משתנים קבועים (const) בשם quitKnock ו loudKnock שישמרו לנו ערכים קבועים אליהם נחליט מראש (כרגע 10, ו-100) שיקבעו את טווח הערכים לנקישה (נקישה שקטה מ-10 או חזקה מ-100 לא תתקבל, כי היא אינה בטווח), משתנה בוליאני בשם locked שבו נשמור האם המנעול נעול או לא (כרגע נגדיר אותו ל”לא” – false), ומשתנה שיספור לנו את מס’ הניסיונות, נקרא לו numberOfKnocks.

 

בפוקנציית ה ()setup:

נבצע את כל ההגדרות של הפינים השונים: המופע myServo יתחבר לפין מס’ 9 (באמצעות הפונ’ שבספרייה שלו הנקראת ()attach. עם הפונ’ pinMode נגדיר את הפינים של הלדים השונים (הצהוב, האדום, והירוק) להיות פלט (OUTPUT), הפין של המתג יהיה INPUT (כדי לקרוא ממנו את הערך ולבדוק האם הוא לחוץ או לא).

בנוסף, נפתח חיבור בין ה Arduino למחשב (כדי שנוכל להדפיס את הערכים ולבדוק את התוכנית שלנו בזמן הריצה). נבצע זאת עם הפונ’ ()Serial.begin, ונכניס לתוכה את הפרמטר 9600 (זוהי המהירות בו ה Arduino יתקשר, 9600 ביטים לשניה). ולבסוף הפונ’ נכתוב לנורה הירוקה להיות דולקת, למנוע הסרוו להיות במצב פתוח (0), ולנו, למחשב, נכתוב שהקופסא לא נעולה.

 

בפונקציית ה ()loop:

בתוך ה loop נבדוק קודם כל האם הקופסא לא נעולה, ע”י משפט if והשוואה של משתנה ה locked ל false. זה ישפיע על מה שקורה בהמשך הפונ’, אם הוא לא נעול נקרא את הערך שבמתג (switchVal), ונבדוק האם המתג לוחץ (ז”א המעגל נסגר ויש מתח, לכן האם הערך שווה ל HIGH):

  • ננעל את הקופסא (locked=true).
  • נכבה את הנורה הירוקה.
  • נדליק את הנורה האדומה.
  • נסובב את מנוע הסרוו ל-90 מעלות.
  • נדפיס הודעה לעצמנו (למחשב), שהקופסט נעולה.
  • ונבצע דיילי (כדי שיהיה זמן למנוע לזוז).

ונצא מה if של האם המתג לחוץ (כי אם הוא לא אין לנו מה לעשות, המצב לא משתנה, הקופסא עדיין נשארת פתוחה), ונצא מה if של האם הקופסא לא נעולה. כעת נבדוק שוב את הקופסא, אך הפעם האם היא נעולה (נשווה ל true), ניתן כמובן לעשות זאת עם else ל if של “האם הקופסא לא נעולה, הרי ערך בוליאני יכול להיות רק true או false, כך שאם הוא לא false הוא בטוח true. אך הפעם נכתוב את ה if מחדש.

אם הקופסא נעולה:

  • נקרא את הערך מרכיב ה piezo ונאחסן אותו במשתנה KnockVal.
  • כדי להכיל רק נקישות “חוקיות”, נבדוק (עם משפט if) האם מס’ הנקישות קטן מ-3 (הרי המקסימום הוא 3), והאם knockVal גדול מ-0 (האם התקבל ערך כלשהו).
    • כדי לבדוק האם הנקישה עצמה חוקית נבדוק אותה ע”י העברת הנקישה לפונ’ אחרת בשם checkForKnock שכרגע היא לא כתובה (נכתוב אותה בסוף התוכנית), ונבדוק האם התוצאה שתוחזר מהפונ’ הזאת שווה ל true, אם כן נעלה את מס’ הנקישות ב-1 (במקום לכתוב numberOfKnocks שווה לעצמו ועוד 1, ניתן בקיצור לכתוב ++numberOfKnocks).
  • ונרצה להדפיס את מס’ הנקישות שנותרו {לא רק אם הנקישה הייתה חוקית, אלא כל פעם. לכן נכתוב זאת מחוץ ל if של הפונ’ checkForKnock, ז”א בתוך ה if הנוכחי (נדפיס את מס’ הנקישות שנותרו [3 פחות מס’ הנקישות שהוקשו עד כה, וטקסט)]}, וסיימנו גם עם ה if של מס’ הנקישות קטן מ-3, לכן נסגור גם אותו עם סוגריים מסולסלים.
  • אך מה עם מס’ הנקישות גדול מ-3? אז בכלל לא ניכנס לכל הבדיקה שעשינו עד כה, לכן נישאר ב if של ‘הקופסא נעולה’ (לא סגרנו אותו עדיין), ונכתוב if נוסף, בו נבדוק האם מס’ הנקישות גדול מ-3, אם כן:
    • נפתח את הקופסא (locked=false).
    • נסובב את מנוע הסרוו ל-0 מעלות.
    • נבצע דיילי (כדי לתת למנוע זמן לבצע את הסיבוב).
    • נדליק את הנורה הירוקה.
    • נכבה את הנורה האדומה.
    • נדפיס לעצמנו (למחשב), שהקופסא פתוחה.

לבסוף, נכתוב את פונ’ הבדיקה של “האם הנקשיה חוקית”, נקרא לה checkForKnock, ולפניה נכתוב boolean (אנחנו רוצים שהיא תחזיר משתנה בוליאני למי שקורא לה, ז”א את התשובה “כן” או “לא”), בנוסף, הפונ’ תקבל פרמטר שהוא מס’ שלם (int) ונקרא לו value (הוא יהיה קיים אך ורק בתוך הפונ’, ונוכל להתייחס עליו כאל value, אך הערך שעובר אלינו לא צריך להיקרא באותו שם, אלא רק להיות מאותו סוג – int).

נבדוק האם הנקישה חוקית ע”י בדיקה של האם הערך בין טווח הערכים quitKnock ל loudKnock, אם כן:

  • נדליק את הנורה הצהובה.
  • נבצע דיילי.
  • נכבה את הנורה הצהובה.
  • נדפיס לעצמנו טקסט (שהנקישה תקינה, ואת הערך של הנקישה).
  • ונחזיר true(ברגע שאנחנו מחזירים איזשהו ערך, ע”י return, הפונ’ מסתיימת כאן, היא אינה תמשיך לבצע את עצמה).

אם הנקישה אינה בין אותם ערכים, ז”א לא חוקית, נדפיס לעצמנו (למחשב) טקסט שהיא אינה חוקית, ואת ערך הנקישה עצמה, ונחזיר false.
כך יראה הקוד הסופי:

#include <Servo.h>
Servo myServo;

const int pieso = A0;
const int switchPin = 2;
const int yellowLed = 3;
const int greenLed = 4;
const int redLed = 5;
int knockVal;
int switchVal;
const int quietKnock = 10;
const int loudKnock = 100;
boolean locked = false;
int numberOfKnocks = 0;

void setup(){
    myServo.attach(9);
    pinMode(yellowLed, OUTPUT);
    pinMode(redLed, OUTPUT);
    pinMode(greenLed, OUTPUT);
    pinMode(switchPin, INPUT);
    Serial.begin(9600);
    digitalWrite(greenLed, HIGH);
    myServo.write(0);
    Serial.println("The box is unlocked!");
}

void loop(){
    if (locked == false){
        switchVal = digitalRead(switchPin);
        if (switchVAL == high){
            locked = true;
            digitalWrite(greenLed, LOW);
            digitalWrite(redLed, HIGH);
            myServo.write(90);
            Serial.println("The box is locked!");
            dealy(1000);
        }
    }
    if (locked ==true){
        knockVal = analogRead(piezo);
        if (numberOfKnocks < 3 && knockVal > 0){
            if (checkForKnock(knockVal) == true){
                numberOfKnocks++;
            }
            Serial.print(3-numberOfKnocks);
            Serial.print(" more knocks to go");
        }
        if (numberOfKnocks >= 3){
            locked = false;
            myServo.write(0);
            dealy(20);
            digitalWrite(greenLed, HIGH);
            digitalWrite(redLed, LOW);
            Serial.println("The box is unlocked!");
        }
    }
}

boolean checkForKnock(int value){
    if (value > quietKnock && value < loudKnock){
        digitalWrite(yellowLed, HIGH);
        dealy(50);
        digitalWrite(yellowLed, LOW);
        Serial.print("Valid knock of value ");
        Serial.print(value);
        return true;
    }else{
        Serial.print("Bad knock value ");
        Serial.print(value);
        return false;
    }
}

 

לסיכום

בפרויקט הזה תרגלנו שימוש נוסף ברכיב ה Piezo ובמנוע הסרוו, נזכרנו למה משמש הקבל, ייבאנו שוב ספריות, ולמדנו עקרון חדש (וחשוב) בתכנות – יצרנו פונ’ נפרדת לגמרי ל-2 הפונ’ הראשיות שבהם אנו משתמשים (והשתמשנו בה בהם, ע”י קריאה אליה).

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

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