פוסט זה נוצר כהקדמה להרצאתי בכנס גופרקון 2024 שבה אני מספר על IoT ואיך עובדים איתו בגו.
למי שלא מכיר – רספברי פיי הוא מחשב לוח קטן וזול יחסית שאפשר להתקין ולחבר בקלות. אפשר ממש לחבר אליו מסך ומקלדת ולעבוד עליו כמו מחשב רגיל או להתחבר אליו ב-SSH.
מערכת ההפעלה של רספברי פיי היא לינוקס לכל דבר וקל להריץ עליה גם פייתון וגם Node.js. אבל אפשר להריץ עליה כל שפה, באמת כל שפה. המון מתכנתים נמנעים מ-IoT כי הם בטוחים שאפשר לכתוב רק ב-C או במקרה הטוב רק ב-++C. בפועל אפשר לכתוב בכל שפה שהיא ובמקרה הזה גו. מה שכן, על מנת לכתוב IoT צריכים להכיר את periph.io שהיא ספרית גו מוצלחת לתקשורת עם הפריפריה של רספברי פיי וספציפית עם GPIO.
המצרכים שצריך הוא רספברי פיי עם מערכת הפעלה מבוססת לינוקס שמותקנת עליו וכן חיבור SSH. אפשר לקרוא את המאמר ההתחלתי הזה על רספברי פיי ואת הפוסט הזה על חיבור ל-SSH כדי לדעת איך להגיע לרגע המאושר הזה.
חיבור התחלתי והתקנת Go
אחרי שהתחברתם עם SSH, הגיע הזמן להתקין את Go. אני מראה פה על 1.23.0 שזו הגרסה החדשה, אבל אפשר לבחור איזו גרסה שאתם רוצים מדף ההורדות.
ההתקנה היא מאד דומה ללינוקס – נוריד ונפתח את הקובץ המתאים עם שתי שורות:
wget https://golang.org/dl/go1.23.0.linux-armv6l.tar.gz
sudo tar -C /usr/local -xzf go1.23.0.linux-armv6l.tar.gz
זה מתקין את Go על /usr/local/go/. השלב הבא הוא להכניס את השורות המתאימות ל-PATH כדי שנוכל להקליד go בטרמינל וגם יקרה משהו. ברספברי פיי אנו נעשה זאת עם שינוי בקובץ ה-profile. נכנס לעורך הטקסט nano:
nano ~/.profile
ונוסיף את שלושת השורות האלו:
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
נטען מחדש את הטרמינל עם:
source ~/.profile
ונבדוק שהכל עובד עם:
go version
אם מקבלים גרסה אז הכל תקין. זה די פשוט לבצע את ההתקנה אם אתם שולטים ב-SSH, בפקודות לינוקס ובקונספט של לינוקס. אם לא ממש, אז דווקא העיסוק ברספברי פיי וב-IoT מאד מחזק את הידע הזה שנדרש גם לעבודה בחיים האמיתיים.
כתיבת קוד גו של Hello World על הרספברי פיי
על מנת לראות שהכל באמת עובד, נכתוב איזה Hello World קטן. ניצור תיקית פרויקט ונכתוב לתוכה.
mkdir -p ~/go/src/hello
nano ~/go/src/hello/hello.go
ונכניס לתוך הקובץ קוד די פשוט:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
נשמור ונסגור ואז נכנס לתיקיה נריץ את הפקודה שמקמפלת ומריצה:
cd ~/go/src/hello
go run hello.go
זה יקח טיפה יותר זמן מהרגיל כי למרות שרספברי פיי היא מכונה חזקה, בכל זאת היא לא מתחרה עם מכונת הפיתוח שלכם.
טוב, זה אחלה, אבל לא באנו לפה כדי לכתוב Hello World. בואו נתחבר קצת ל-GPIO!
מה זה GPIO
אם תבחנו את הרספברי פיי שלכם, תראו שיש פינים שמחוברים אליו.
הפינים האלו נקראים GPIO – General Purpose Input Output – אנחנו יכולים להעביר דרכם זרם. כך למשל אני יכול לחבר נורת LED ל-GPIO מצד אחד ולגראונד מהצד השני וליצור מעגל חשמלי. באמצעות תוכנה אני יכול להורות ל-GPIO להעביר מתח ולהדליק את ה-LED או לא להעביר LED ולכבותו.
יש במאמר הזה שכתבתי הסבר מקיף על GPIO עם דוגמאות שאפשר לכתוב בשורות הפקודה (לא שפת תכנות אלא ממש דרך הטרמינל) שמסביר את העניין די טוב. אבל גם לא חייבים להסתבך עם אלקטרוניקה בשביל דוגמה פשוטה – יש לרספברי פיי נורית LED שמחוברת ללוח ואפשר לנסות ולשלוט בה רק כדי להתנסות בדוגמאות Hello World ולראות איך הכל מנגן. הנורית הזו היא GPIO 42. הבה ונבדוק אותה.
חיבור ל-GPIO עם גו
הספריה המוצלחת ביותר לעבוד עם GPIO היא periph.io. יש לה אתר משלה ודוקומנטציה מוצלחת. ואפילו דוגמת Hello World מוצלחת גם.
בגדול – ניצור בתיקיה מודול עם הפקודה:
go mod init hello
נתקין את periph.io
go get periph.io/x/cmd/...
עכשיו יהיה אפשר להשתמש בספריה הזו. אנו נראה שימוש ב-LED הפנימי. נכניס את main.go באמצעות nano:
nano main.go
ואז נכתוב:
package main
import (
"time"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/host/v3"
)
func main() {
host.Init()
p := gpioreg.ByName("42")
t := time.NewTicker(500 * time.Millisecond)
for l := gpio.Low; ; l = !l {
p.Out(l)
<-t.C
}
}
נריץ את הפקודה:
go run main.go
ונוכל לראות את ה-LED הפנימי מהבהב! הצלחה!
שימו לב שאני מפעיל את GPIO 42 שהוא ה-LED הפנימי ברספברי פיי 4, שזה הדגם שבדקתי איתו. בדגמים אחרים המספר יכול להיות שונה.
זו די דוגמת Hello World, אבל מהרגע שאתם מכינים את הסביבה וסוגרים את המעגל, החגיגה מתחילה. עכשיו אפשר להפעיל את ה-GPIO ולקבל קלט ופלט והכל בשפה שאתם מכירים ומבינים. אם אתם אנשי גו – לא חייבים להשתמש בפייתון, ב-C או בכל שפה אחרת. עולם ה-IoT והמייקרים פתוח בפני כל שפה שהיא.
11 תגובות
איזה כיף שהתחלת לכתוב בGO!
שתי הערות אם יורשה לי. קודם כל, אם אני לא טועה, גרסת GO תתאים לRPI 1-3. אבל החל מRPI 4, המעבד הוא 64 ביט. אני חושב שבמקרה כזה כדאי להתקין מhttps://go.dev/dl/go1.23.0.linux-arm64.tar.gz
שנית לגבי הקוד עצמו: בד"כ נהוג לרוץ בלולה על הטיקר עצמו, ולא לקרוא ממנו בתוך הלולאה. משהוא כזה:
func main() {
_, err := host.Init()
if err != nil {
panic(err)
}
p := gpioreg.ByName("42")
t := time.NewTicker(500 * time.Millisecond)
l := gpio.Low
for range t.C {
p.Out(l)
l = !l
}
}
func main() {
_, err := host.Init()
if err != nil {
panic(err)
}
p := gpioreg.ByName("42")
t := time.NewTicker(500 * time.Millisecond)
l := gpio.Low
for range t.C {
p.Out(l)
l = !l
}
}
טוב, לא יודע אם ואיך אפשר לשלוח פה קוד בתגובות…
והכי חשוב – מקווה שנתראה בכנס. יש עוד כרטיסים – רוצו לקנות!
סליחה על החפירה. שני הנושאים האלה קרובים לליבי, גם RPI וגם GO.
מנסיוני, לא רק שאפשר לכתוב לRPI בGO, אלא שזה ממש ממש מתאים. בעיני זו שפה מושלמת לכתיבה לחומרה (לא realtime, לא צריך להגזים), בגלל המודל האסינכוני שלה, ובעיקר channels. לדוגמא, לכתוב קוד שיושב ומחכה לאירועים שיקרו בחומרה, ואז מגיב להם, זה קל ופשוט לבצע בשפה עצמה, ללא שימוש בספריות.
עקבתי אחרי ההוראות אבל קיבלתי שגיאות קומפילציה:
main.go:5:5: no required module provides package periph.io/x/conn/v3/gpio: go.mod file not found in current directory or any parent directory; see 'go help modules'
main.go:6:5: no required module provides package periph.io/x/conn/v3/gpio/gpioreg: go.mod file not found in current directory or any parent directory; see 'go help modules'
main.go:7:5: no required module provides package periph.io/x/host/v3: go.mod file not found in current directory or any parent directory; see 'go help modules'
הנה התוכן של go.mod:
module hello
go 1.23.0
require (
github.com/jonboulle/clockwork v0.4.0 // indirect
golang.org/x/image v0.19.0 // indirect
periph.io/x/cmd v0.0.0-20240809183634-0fe4bbe1d5d9 // indirect
periph.io/x/conn/v3 v3.7.1 // indirect
periph.io/x/d2xx v0.1.1 // indirect
periph.io/x/devices/v3 v3.7.1 // indirect
periph.io/x/host/v3 v3.8.2 // indirect
)
נסה
go mod tidy
זה לא עזר. למה הוא טוען שאין קובץ go.mod בתיקייה הנוכחית?
אם אין לך קובץ go.mod, אתה צריך להריץ go mod init עם שם כלשהו. משהו כמו
go mod init blink
אבל יש לי קובץ go.mod.
לא יודע מה היתה הבעיה. בניתי את המודול מחדש וזה עבד.