Raspberry Pi GPIO - Go
Go examples for accessing Raspberry Pi GPIO, I2C, SPI, and PWM using periph.io.
Installation
1# Install Go (if not already installed)
2wget https://go.dev/dl/go1.21.5.linux-arm64.tar.gz
3sudo tar -C /usr/local -xzf go1.21.5.linux-arm64.tar.gz
4
5# Add to PATH
6echo 'export PATH=$PATH:/usr/local/bin/go/bin' >> ~/.bashrc
7source ~/.bashrc
8
9# Verify
10go version
GPIO (Digital I/O)
Setup Project
1mkdir rpi-gpio && cd rpi-gpio
2go mod init rpi-gpio
3go get periph.io/x/conn/v3/gpio
4go get periph.io/x/conn/v3/gpio/gpioreg
5go get periph.io/x/host/v3
Basic GPIO
1package main
2
3import (
4 "fmt"
5 "log"
6 "time"
7
8 "periph.io/x/conn/v3/gpio"
9 "periph.io/x/conn/v3/gpio/gpioreg"
10 "periph.io/x/host/v3"
11)
12
13func main() {
14 // Initialize periph
15 if _, err := host.Init(); err != nil {
16 log.Fatal(err)
17 }
18
19 // Get GPIO pin (BCM numbering)
20 ledPin := gpioreg.ByName("GPIO18")
21 if ledPin == nil {
22 log.Fatal("Failed to find GPIO18")
23 }
24
25 // Set as output
26 if err := ledPin.Out(gpio.Low); err != nil {
27 log.Fatal(err)
28 }
29
30 // Blink LED
31 for i := 0; i < 10; i++ {
32 ledPin.Out(gpio.High)
33 fmt.Println("LED ON")
34 time.Sleep(500 * time.Millisecond)
35
36 ledPin.Out(gpio.Low)
37 fmt.Println("LED OFF")
38 time.Sleep(500 * time.Millisecond)
39 }
40}
GPIO Input with Button
1package main
2
3import (
4 "fmt"
5 "log"
6 "time"
7
8 "periph.io/x/conn/v3/gpio"
9 "periph.io/x/conn/v3/gpio/gpioreg"
10 "periph.io/x/host/v3"
11)
12
13func main() {
14 if _, err := host.Init(); err != nil {
15 log.Fatal(err)
16 }
17
18 // LED output
19 ledPin := gpioreg.ByName("GPIO18")
20 ledPin.Out(gpio.Low)
21
22 // Button input with pull-up
23 buttonPin := gpioreg.ByName("GPIO23")
24 if err := buttonPin.In(gpio.PullUp, gpio.BothEdges); err != nil {
25 log.Fatal(err)
26 }
27
28 fmt.Println("Press button to toggle LED")
29
30 ledState := false
31
32 for {
33 // Wait for button press
34 buttonPin.WaitForEdge(-1)
35
36 // Debounce
37 time.Sleep(50 * time.Millisecond)
38
39 // Check if button is pressed (active low with pull-up)
40 if buttonPin.Read() == gpio.Low {
41 ledState = !ledState
42 if ledState {
43 ledPin.Out(gpio.High)
44 fmt.Println("LED ON")
45 } else {
46 ledPin.Out(gpio.Low)
47 fmt.Println("LED OFF")
48 }
49 }
50 }
51}
PWM
1package main
2
3import (
4 "log"
5 "time"
6
7 "periph.io/x/conn/v3/gpio"
8 "periph.io/x/conn/v3/gpio/gpioreg"
9 "periph.io/x/conn/v3/physic"
10 "periph.io/x/host/v3"
11)
12
13func main() {
14 if _, err := host.Init(); err != nil {
15 log.Fatal(err)
16 }
17
18 // Get PWM-capable pin
19 pwmPin := gpioreg.ByName("GPIO18")
20 if pwmPin == nil {
21 log.Fatal("Failed to find GPIO18")
22 }
23
24 // Check if pin supports PWM
25 if pwm, ok := pwmPin.(gpio.PinPWM); ok {
26 // Set PWM frequency (1kHz)
27 freq := 1000 * physic.Hertz
28
29 // Fade in
30 for duty := gpio.Duty(0); duty <= gpio.DutyMax; duty += gpio.DutyMax / 100 {
31 if err := pwm.PWM(duty, freq); err != nil {
32 log.Fatal(err)
33 }
34 time.Sleep(10 * time.Millisecond)
35 }
36
37 // Fade out
38 for duty := gpio.DutyMax; duty >= 0; duty -= gpio.DutyMax / 100 {
39 if err := pwm.PWM(duty, freq); err != nil {
40 log.Fatal(err)
41 }
42 time.Sleep(10 * time.Millisecond)
43 }
44
45 // Turn off
46 pwm.PWM(0, freq)
47 } else {
48 log.Fatal("Pin does not support PWM")
49 }
50}
I2C
Setup
1# Enable I2C
2sudo raspi-config
3# Interface Options > I2C > Enable
4
5# Add dependencies
6go get periph.io/x/conn/v3/i2c
7go get periph.io/x/conn/v3/i2c/i2creg
I2C Communication
1package main
2
3import (
4 "fmt"
5 "log"
6
7 "periph.io/x/conn/v3/i2c"
8 "periph.io/x/conn/v3/i2c/i2creg"
9 "periph.io/x/host/v3"
10)
11
12func main() {
13 if _, err := host.Init(); err != nil {
14 log.Fatal(err)
15 }
16
17 // Open I2C bus
18 bus, err := i2creg.Open("")
19 if err != nil {
20 log.Fatal(err)
21 }
22 defer bus.Close()
23
24 // Device address
25 const deviceAddr = 0x48
26
27 // Create device
28 dev := &i2c.Dev{Bus: bus, Addr: uint16(deviceAddr)}
29
30 // Write byte
31 if err := dev.Tx([]byte{0x01, 0x83}, nil); err != nil {
32 log.Fatal(err)
33 }
34
35 // Read byte
36 read := make([]byte, 1)
37 if err := dev.Tx([]byte{0x00}, read); err != nil {
38 log.Fatal(err)
39 }
40 fmt.Printf("Read: 0x%02X\n", read[0])
41
42 // Read block
43 block := make([]byte, 2)
44 if err := dev.Tx([]byte{0x00}, block); err != nil {
45 log.Fatal(err)
46 }
47 fmt.Printf("Block: %v\n", block)
48}
I2C Example: BMP280 Sensor
1package main
2
3import (
4 "encoding/binary"
5 "fmt"
6 "log"
7
8 "periph.io/x/conn/v3/i2c"
9 "periph.io/x/conn/v3/i2c/i2creg"
10 "periph.io/x/host/v3"
11)
12
13type BMP280 struct {
14 dev *i2c.Dev
15 digT1 uint16
16 digT2 int16
17 digT3 int16
18}
19
20func NewBMP280(bus i2c.Bus, addr uint16) (*BMP280, error) {
21 dev := &i2c.Dev{Bus: bus, Addr: addr}
22
23 bmp := &BMP280{dev: dev}
24
25 // Read calibration data
26 calib := make([]byte, 6)
27 if err := dev.Tx([]byte{0x88}, calib); err != nil {
28 return nil, err
29 }
30
31 bmp.digT1 = binary.LittleEndian.Uint16(calib[0:2])
32 bmp.digT2 = int16(binary.LittleEndian.Uint16(calib[2:4]))
33 bmp.digT3 = int16(binary.LittleEndian.Uint16(calib[4:6]))
34
35 // Configure sensor
36 if err := dev.Tx([]byte{0xF4, 0x27}, nil); err != nil {
37 return nil, err
38 }
39
40 return bmp, nil
41}
42
43func (b *BMP280) ReadTemperature() (float64, error) {
44 // Read raw temperature
45 data := make([]byte, 3)
46 if err := b.dev.Tx([]byte{0xFA}, data); err != nil {
47 return 0, err
48 }
49
50 adcT := int32(data[0])<<12 | int32(data[1])<<4 | int32(data[2])>>4
51
52 // Compensate
53 var1 := (float64(adcT)/16384.0 - float64(b.digT1)/1024.0) * float64(b.digT2)
54 var2 := ((float64(adcT)/131072.0 - float64(b.digT1)/8192.0) *
55 (float64(adcT)/131072.0 - float64(b.digT1)/8192.0)) * float64(b.digT3)
56
57 tFine := var1 + var2
58 temperature := tFine / 5120.0
59
60 return temperature, nil
61}
62
63func main() {
64 if _, err := host.Init(); err != nil {
65 log.Fatal(err)
66 }
67
68 bus, err := i2creg.Open("")
69 if err != nil {
70 log.Fatal(err)
71 }
72 defer bus.Close()
73
74 sensor, err := NewBMP280(bus, 0x76)
75 if err != nil {
76 log.Fatal(err)
77 }
78
79 temp, err := sensor.ReadTemperature()
80 if err != nil {
81 log.Fatal(err)
82 }
83
84 fmt.Printf("Temperature: %.2f°C\n", temp)
85}
SPI
Setup
1# Enable SPI
2sudo raspi-config
3# Interface Options > SPI > Enable
4
5# Add dependencies
6go get periph.io/x/conn/v3/spi
7go get periph.io/x/conn/v3/spi/spireg
SPI Communication
1package main
2
3import (
4 "fmt"
5 "log"
6
7 "periph.io/x/conn/v3/physic"
8 "periph.io/x/conn/v3/spi"
9 "periph.io/x/conn/v3/spi/spireg"
10 "periph.io/x/host/v3"
11)
12
13func main() {
14 if _, err := host.Init(); err != nil {
15 log.Fatal(err)
16 }
17
18 // Open SPI port
19 port, err := spireg.Open("")
20 if err != nil {
21 log.Fatal(err)
22 }
23 defer port.Close()
24
25 // Configure SPI
26 conn, err := port.Connect(1*physic.MegaHertz, spi.Mode0, 8)
27 if err != nil {
28 log.Fatal(err)
29 }
30
31 // Transfer data
32 write := []byte{0x01, 0x02, 0x03}
33 read := make([]byte, len(write))
34
35 if err := conn.Tx(write, read); err != nil {
36 log.Fatal(err)
37 }
38
39 fmt.Printf("Sent: %v\n", write)
40 fmt.Printf("Received: %v\n", read)
41}
SPI Example: MCP3008 ADC
1package main
2
3import (
4 "fmt"
5 "log"
6 "time"
7
8 "periph.io/x/conn/v3/physic"
9 "periph.io/x/conn/v3/spi"
10 "periph.io/x/conn/v3/spi/spireg"
11 "periph.io/x/host/v3"
12)
13
14type MCP3008 struct {
15 conn spi.Conn
16}
17
18func NewMCP3008(port spi.Port) (*MCP3008, error) {
19 conn, err := port.Connect(1350*physic.KiloHertz, spi.Mode0, 8)
20 if err != nil {
21 return nil, err
22 }
23 return &MCP3008{conn: conn}, nil
24}
25
26func (m *MCP3008) ReadChannel(channel int) (int, error) {
27 if channel < 0 || channel > 7 {
28 return 0, fmt.Errorf("invalid channel: %d", channel)
29 }
30
31 // Build command
32 cmd := []byte{
33 0x01,
34 byte((8 + channel) << 4),
35 0x00,
36 }
37
38 // Send command and read response
39 reply := make([]byte, 3)
40 if err := m.conn.Tx(cmd, reply); err != nil {
41 return 0, err
42 }
43
44 // Extract 10-bit value
45 value := int(reply[1]&0x03)<<8 | int(reply[2])
46
47 return value, nil
48}
49
50func (m *MCP3008) ReadVoltage(channel int, vref float64) (float64, error) {
51 value, err := m.ReadChannel(channel)
52 if err != nil {
53 return 0, err
54 }
55
56 voltage := float64(value) * vref / 1023.0
57 return voltage, nil
58}
59
60func main() {
61 if _, err := host.Init(); err != nil {
62 log.Fatal(err)
63 }
64
65 port, err := spireg.Open("")
66 if err != nil {
67 log.Fatal(err)
68 }
69 defer port.Close()
70
71 adc, err := NewMCP3008(port)
72 if err != nil {
73 log.Fatal(err)
74 }
75
76 for {
77 value, err := adc.ReadChannel(0)
78 if err != nil {
79 log.Fatal(err)
80 }
81
82 voltage, err := adc.ReadVoltage(0, 3.3)
83 if err != nil {
84 log.Fatal(err)
85 }
86
87 fmt.Printf("Channel 0: %d (%.2fV)\n", value, voltage)
88 time.Sleep(1 * time.Second)
89 }
90}
Complete Example: Weather Station
1package main
2
3import (
4 "fmt"
5 "log"
6 "time"
7
8 "periph.io/x/conn/v3/gpio"
9 "periph.io/x/conn/v3/gpio/gpioreg"
10 "periph.io/x/conn/v3/i2c"
11 "periph.io/x/conn/v3/i2c/i2creg"
12 "periph.io/x/host/v3"
13)
14
15type WeatherStation struct {
16 led gpio.PinOut
17 button gpio.PinIn
18 sensor *BMP280
19}
20
21func NewWeatherStation() (*WeatherStation, error) {
22 if _, err := host.Init(); err != nil {
23 return nil, err
24 }
25
26 // Setup LED
27 led := gpioreg.ByName("GPIO18")
28 led.Out(gpio.Low)
29
30 // Setup button
31 button := gpioreg.ByName("GPIO23")
32 button.In(gpio.PullUp, gpio.FallingEdge)
33
34 // Setup I2C sensor
35 bus, err := i2creg.Open("")
36 if err != nil {
37 return nil, err
38 }
39
40 sensor, err := NewBMP280(bus, 0x76)
41 if err != nil {
42 return nil, err
43 }
44
45 return &WeatherStation{
46 led: led,
47 button: button,
48 sensor: sensor,
49 }, nil
50}
51
52func (ws *WeatherStation) Run() {
53 fmt.Println("Weather Station Running. Press button to read temperature.")
54
55 for {
56 // Wait for button press
57 ws.button.WaitForEdge(-1)
58
59 // Debounce
60 time.Sleep(50 * time.Millisecond)
61
62 if ws.button.Read() == gpio.Low {
63 ws.led.Out(gpio.High)
64
65 temp, err := ws.sensor.ReadTemperature()
66 if err != nil {
67 log.Println("Error reading temperature:", err)
68 } else {
69 fmt.Printf("Temperature: %.2f°C\n", temp)
70 }
71
72 time.Sleep(500 * time.Millisecond)
73 ws.led.Out(gpio.Low)
74 }
75 }
76}
77
78func main() {
79 station, err := NewWeatherStation()
80 if err != nil {
81 log.Fatal(err)
82 }
83
84 station.Run()
85}
Best Practices
- Always initialize host - Call
host.Init()first - Check pin availability - Verify pins exist before use
- Handle errors properly - Don't ignore errors
- Close resources - Use
deferfor cleanup - Use appropriate voltage - RPi GPIO is 3.3V
- Debounce inputs - Add delays for button presses
- Use goroutines - For concurrent GPIO operations
Related Snippets
- Raspberry Pi GPIO - Python
Python examples for accessing Raspberry Pi GPIO, I2C, SPI, I2S, and PWM. GPIO …