「Raspbian「stretch」上にGO言語をインストール」でGo言語をインストールしましたが、今回は、スイッチ、RGB LED、OLED,圧電ブザーを組み合わせて動作させます。アプリはWindows10上のVisual Studio Codeで開発し、Raspberry Pi上で動作させます。
開発環境
- Raspbian:buster
- go言語のバージョン:go1.13.4
- コーディング環境:Visual Studio Code 1.44.2
Raspberry Piとスイッチ、RGB LED、OLED,圧電ブザーとの接続
Raspberry Piとスイッチ、RGB LED、OLED,圧電ブザーとを接続した回路図を次に示します。
接続画像を次に示します。
作成するアプリの仕様
作成するアプリの仕様を次に示します。
1. アプリを起動すると、
2. スイッチ「赤」を押すと、
- RGB LED:5秒間「赤」の点灯
- 圧電ブザー:5秒間320Hz
- OLED:「RED」の表示
3. スイッチ「青」を押すと、
- RGB LED:5秒間「青」の点灯
- 圧電ブザー:5秒間840Hz
- OLED:「BLUE」の表示
4. スイッチの入力がない場合、アプリの起動画面を再表示します。
アプリの作成
作業フォルダ「gomain」に次に示す「main.go」「text.go」「button.go」「led.go」「buzzer.go」を作成します。
1.使用するチャンネルを次に示します。
チャンネル名 | 機能 | 発生/使用 |
---|---|---|
buzzerch | スイッチが押されたことを通知 | スイッチ「赤」もしくは「青」が押されたとき、「button.go」から「buzzer.go」に通知される。 |
ledch | スイッチが押されたことを通知 | スイッチ「赤」もしくは「青」が押されたとき、「button.go」から「led.go」に通知される。 |
textch | SSD1306に表示するデータの転送 | スイッチが押されたとき、LEDを点灯するとき、圧電ブザーを鳴らすときに、「button.go」「led.go」「buzzer.go」から「main.go」に表示データが通知される。 |
quit | シグナルが発生したことを通知 | キーボードでコントロール-Cが押されたとき、シグナルが発生し、「main.go」に通知される。 |
2.使用するパッケージを次に示します。
- 「periph.io/x/periph/host」
- 「periph.io/x/periph/conn/gpio」
- 「periph.io/x/periph/conn/gpio/gpioreg」
- 「periph.io/x/periph/conn/physic」
- 「periph.io/x/periph/host/rpi」
メインルーチン「main」と初期化ルーチン「init」を次に示します。
- 初期化ルーチンで、シグナルが発生すると通知するようにsignal.Notify関数を呼び、host.Init関数でドライバを初期化します。
- 57行目で表示要求を受け取ります。
- 67行目で終了要求を受け取り、ループ状態から抜け出てブザーの設定を解除します。
main.go
package main import ( "log" "os" "os/signal" "periph.io/x/periph/host" ) const ( REDSW int = iota BLUESW ) const ( INIT int = iota REDSTATUS BLUESTATUS RGBLED BUZZER ) type Display struct { ID int data string } var buzzerch = make(chan int) var ledch = make(chan int) var textch = make(chan Display) // シグナル用のチャネル定義 var quit = make(chan os.Signal) func init() { signal.Notify(quit, os.Interrupt) // Load all the drivers: if _, err := host.Init(); err != nil { log.Fatal(err) } textinit() textdisplay(Display{INIT, ""}) // 受け取るシグナルを設定 } func main() { go button() go buzzer() go led() LOOP: for { select { case display := <-textch: log.Printf("main %+v\n", display) textdisplay(display) case <-quit: log.Println("signal!") break LOOP } } if err := pbuzzer.Halt(); err != nil { log.Fatal(err) } log.Println("Exit") }
テキストルーチン「textdisplay」とテキスト初期化ルーチン「textinit」を次に示します。詳細については、「Go言語によるSSD1306への表示」を参照してください。
- 表示を更新する場合、再度表示バッファ領域を確保して、すべての情報を設定しなおし、再描画します。
text.go
package main import ( "image" "image/color" "log" "golang.org/x/image/font" "golang.org/x/image/font/basicfont" "golang.org/x/image/math/fixed" "periph.io/x/periph/conn/i2c/i2creg" "periph.io/x/periph/devices/ssd1306" ) const ( REDCNST string = "RED SW: " BLUECNST string = "BLUE SW: " RGBCNST string = "RGB LED: " BUZZERCNST string = "BUZZER: " ) var dev *ssd1306.Dev var DefaultOpts = ssd1306.Opts{ W: 128, H: 64, Rotated: false, Sequential: false, SwapTopBottom: false, } var reddata string var bluedata string var rgbdata string var buzzerdata string func textinit() { // Open a handle to the first available I?C bus: bus, err := i2creg.Open("") if err != nil { log.Fatal(err) } dev, err = ssd1306.NewI2C(bus, &DefaultOpts) if err != nil { log.Fatal(err) } } func textdisplay(display Display) { img := image.NewGray(image.Rect(0, 0, 128, 64)) dfont := &font.Drawer{ Dst: img, Src: image.NewUniform(color.Gray{255}), Face: basicfont.Face7x13, } switch display.ID { case INIT: reddata = REDCNST + "OFF" bluedata = BLUECNST + "OFF" rgbdata = RGBCNST + "SHADE" buzzerdata = BUZZERCNST + "216-2kHz" case REDSTATUS: reddata = REDCNST + display.data case BLUESTATUS: bluedata = BLUECNST + display.data case RGBLED: rgbdata = RGBCNST + display.data case BUZZER: buzzerdata = BUZZERCNST + display.data default: log.Println("what !") } dfont.Dot = fixed.Point26_6{fixed.Int26_6(5 * 64), fixed.Int26_6((15 + (0 * 15)) * 64)} dfont.DrawString(reddata) dfont.Dot = fixed.Point26_6{fixed.Int26_6(5 * 64), fixed.Int26_6((15 + (1 * 15)) * 64)} dfont.DrawString(bluedata) dfont.Dot = fixed.Point26_6{fixed.Int26_6(5 * 64), fixed.Int26_6((15 + (2 * 15)) * 64)} dfont.DrawString(rgbdata) dfont.Dot = fixed.Point26_6{fixed.Int26_6(5 * 64), fixed.Int26_6((15 + (3 * 15)) * 64)} dfont.DrawString(buzzerdata) dev.Draw(img.Bounds(), img, image.Point{}) }
ボタンルーチン「button」を次に示します。「Button」を参考にして作成しました。
- 100msごとに、スイッチ「赤」と「青」の立ち上がりエッジを検出します。
button.go
package main import ( "log" "time" "periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio/gpioreg" ) func button() { // Lookup a pin by its number: redbutton := gpioreg.ByName("GPIO23") if redbutton == nil { log.Fatal("Failed to find GPIO23") } log.Printf("%s: %s\n", redbutton, redbutton.Function()) // Lookup a pin by its number: bluebutton := gpioreg.ByName("GPIO24") if bluebutton == nil { log.Fatal("Failed to find GPIO24") } log.Printf("%s: %s\n", bluebutton, bluebutton.Function()) // Set it as input, with an internal pull down resistor: if err := redbutton.In(gpio.PullDown, gpio.RisingEdge); err != nil { log.Fatal(err) } // Set it as input, with an internal pull down resistor: if err := bluebutton.In(gpio.PullDown, gpio.RisingEdge); err != nil { log.Fatal(err) } // Wait for edges as detected by the hardware, and print the value read: for { if redbutton.WaitForEdge(0) { //if redbutton.edge != gpio.NoEdge { log.Printf("redbutton -> %s\n", redbutton.Read()) buzzerch <- REDSW ledch <- REDSW textch <- Display{REDSTATUS, "ON"} textch <- Display{BLUESTATUS, "OFF"} } if bluebutton.WaitForEdge(0) { //if redbutton.edge != gpio.NoEdge { log.Printf("bluebutton -> %s\n", bluebutton.Read()) buzzerch <- BLUESW ledch <- BLUESW textch <- Display{REDSTATUS, "OFF"} textch <- Display{BLUESTATUS, "ON"} } time.Sleep(100 * time.Millisecond) } }
LEDルーチン「led」を次に示します。「LED」を参考にして作成しました。
- 14行目でチャンネル「ledch」を監視し、表示要求のない場合はLED「緑」を表示します。
led.go
package main import ( "log" "time" "periph.io/x/periph/conn/gpio" "periph.io/x/periph/host/rpi" ) func led() { for { select { case command := <-ledch: log.Printf("led %d\n", command) switch command { case REDSW: textch <- Display{RGBLED, "RED"} if err := rpi.P1_8.Out(gpio.High); err != nil { log.Fatal(err) } if err := rpi.P1_10.Out(gpio.Low); err != nil { log.Fatal(err) } if err := rpi.P1_12.Out(gpio.Low); err != nil { log.Fatal(err) } case BLUESW: textch <- Display{RGBLED, "BLUE"} if err := rpi.P1_8.Out(gpio.Low); err != nil { log.Fatal(err) } if err := rpi.P1_10.Out(gpio.Low); err != nil { log.Fatal(err) } if err := rpi.P1_12.Out(gpio.High); err != nil { log.Fatal(err) } } default: textch <- Display{RGBLED, "GREEN"} if err := rpi.P1_8.Out(gpio.Low); err != nil { log.Fatal(err) } if err := rpi.P1_10.Out(gpio.High); err != nil { log.Fatal(err) } if err := rpi.P1_12.Out(gpio.Low); err != nil { log.Fatal(err) } } time.Sleep(5 * time.Second) } }
圧電ブザールーチン「buzzer」を次に示します。「Buzzer」を参考にして作成しました。
- 要求がない場合は、14行目のsound関数を呼び出し、5秒間で100Hzから2kHzまで周波数を更新します。
buzzer.go
package main import ( "log" "time" "periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio/gpioreg" "periph.io/x/periph/conn/physic" ) var pbuzzer gpio.PinIO func sound() { for i := 0; i < 19; i++ { if err := pbuzzer.PWM(gpio.DutyHalf, 100*physic.Frequency(i+1)*physic.Hertz); err != nil { log.Fatal(err) } time.Sleep(250 * time.Millisecond) } } func buzzer() { pbuzzer = gpioreg.ByName("PWM1_OUT") if pbuzzer == nil { log.Fatal("Failed to find buzzer") } for { select { case command := <-buzzerch: log.Printf("buzzer %d\n", command) switch command { case REDSW: textch <- Display{BUZZER, "320Hz"} if err := pbuzzer.PWM(gpio.DutyHalf, 320*physic.Hertz); err != nil { log.Fatal(err) } time.Sleep(5 * time.Second) case BLUESW: textch <- Display{BUZZER, "840Hz"} if err := pbuzzer.PWM(gpio.DutyHalf, 840*physic.Hertz); err != nil { log.Fatal(err) } time.Sleep(5 * time.Second) } default: textch <- Display{BUZZER, "100-2kHz"} sound() } if err := pbuzzer.Halt(); err != nil { log.Fatal(err) } } }
アプリの実行
次のコマンドでコンパイルして実行します。
$ go build $ sudo ./gomain 2020/05/17 15:41:50 GPIO23: In/Low 2020/05/17 15:41:50 GPIO24: In/Low 2020/05/17 15:41:50 main {ID:3 data:GREEN} 2020/05/17 15:41:50 main {ID:4 data:100-2kHz} 2020/05/17 15:41:54 main {ID:4 data:100-2kHz} 2020/05/17 15:41:55 main {ID:3 data:GREEN} 2020/05/17 15:41:59 redbutton -> High 2020/05/17 15:41:59 buzzer 0 2020/05/17 15:41:59 main {ID:4 data:320Hz} 2020/05/17 15:42:00 led 0 2020/05/17 15:42:00 main {ID:3 data:RED} 2020/05/17 15:42:00 main {ID:1 data:ON} 2020/05/17 15:42:00 main {ID:2 data:OFF} 2020/05/17 15:42:04 main {ID:4 data:100-2kHz} 2020/05/17 15:42:05 main {ID:3 data:GREEN} 2020/05/17 15:42:09 main {ID:4 data:100-2kHz} 2020/05/17 15:42:10 main {ID:3 data:GREEN} 2020/05/17 15:42:10 bluebutton -> High 2020/05/17 15:42:14 buzzer 1 2020/05/17 15:42:14 main {ID:4 data:840Hz} 2020/05/17 15:42:15 led 1 2020/05/17 15:42:15 main {ID:3 data:BLUE} 2020/05/17 15:42:15 main {ID:1 data:OFF} 2020/05/17 15:42:15 main {ID:2 data:ON} 2020/05/17 15:42:19 main {ID:4 data:100-2kHz} 2020/05/17 15:42:20 main {ID:3 data:GREEN} 2020/05/17 15:42:22 redbutton -> High 2020/05/17 15:42:24 buzzer 0 2020/05/17 15:42:24 main {ID:4 data:320Hz} 2020/05/17 15:42:25 led 0 2020/05/17 15:42:25 main {ID:1 data:ON} 2020/05/17 15:42:25 main {ID:2 data:OFF} 2020/05/17 15:42:25 main {ID:3 data:RED} 2020/05/17 15:42:29 main {ID:4 data:100-2kHz} 2020/05/17 15:42:30 main {ID:3 data:GREEN} 2020/05/17 15:42:33 bluebutton -> High 2020/05/17 15:42:33 buzzer 1 2020/05/17 15:42:33 main {ID:4 data:840Hz} 2020/05/17 15:42:35 led 1 2020/05/17 15:42:35 main {ID:1 data:OFF} 2020/05/17 15:42:35 main {ID:2 data:ON} 2020/05/17 15:42:35 main {ID:3 data:BLUE} 2020/05/17 15:42:38 main {ID:4 data:100-2kHz} 2020/05/17 15:42:40 main {ID:3 data:GREEN} 2020/05/17 15:42:43 main {ID:4 data:100-2kHz} 2020/05/17 15:42:45 main {ID:3 data:GREEN} 2020/05/17 15:42:48 main {ID:4 data:100-2kHz} ^C2020/05/17 15:42:49 signal! 2020/05/17 15:42:49 Exit
アプリ実行中の動画を次に示します。