I2C (Inter-Integrated Circuit) is a synchronous serial communication protocol that allows multiple devices to communicate over just two wires. The golang.org/x/exp/io/i2c package provides a Go implementation for reading from and writing to I2C slave devices, along with a driver interface for various I2C hardware implementations.
go get golang.org/x/exp/io/i2cimport "golang.org/x/exp/io/i2c"
import "golang.org/x/exp/io/i2c/driver"package main
import (
"log"
"golang.org/x/exp/io/i2c"
"golang.org/x/exp/io/i2c/driver"
)
func main() {
// Open a connection to an I2C device at address 0x48
opener := &i2c.Devfs{Dev: "/dev/i2c-1"}
device, err := i2c.Open(opener, 0x48)
if err != nil {
log.Fatal(err)
}
defer device.Close()
// Read 10 bytes from the device
buf := make([]byte, 10)
if err := device.Read(buf); err != nil {
log.Fatal(err)
}
// Write to a register
data := []byte{0x01, 0x02, 0x03}
if err := device.WriteReg(0x10, data); err != nil {
log.Fatal(err)
}
}The I2C package consists of two main components:
Provides high-level device abstraction for communicating with I2C slave devices:
Defines the low-level interfaces that hardware implementations must satisfy:
Manage connections to I2C devices.
// Open opens a connection to an I2C device.
// All devices must be closed once they are no longer in use.
// For devices that use 10-bit I2C addresses, addr can be marked as a 10-bit address with TenBit.
func Open(o driver.Opener, addr int) (*Device, error)// Close closes the device and releases the underlying resources.
func (d *Device) Close() errorRead data from I2C devices.
// Read reads len(buf) bytes from the device.
func (d *Device) Read(buf []byte) error// ReadReg is similar to Read but it reads from a register.
func (d *Device) ReadReg(reg byte, buf []byte) errorWrite data to I2C devices.
// Write writes the buffer to the device.
// If it is required to write to a specific register, the register should be passed
// as the first byte in the given buffer.
func (d *Device) Write(buf []byte) error// WriteReg is similar to Write but writes to a register.
func (d *Device) WriteReg(reg byte, buf []byte) errorHandle I2C address formats.
// TenBit marks an I2C address as a 10-bit address.
func TenBit(addr int) inttype Device struct {
// Has unexported fields
}Device represents an I2C device. Devices must be closed once they are no longer in use.
type Devfs struct {
// Dev is the I2C bus device, e.g. /dev/i2c-1. Required.
Dev string
}Devfs is an I2C driver that works against the devfs. You need to load the "i2c-dev" kernel module to use this driver.
Methods:
func (d *Devfs) Open(addr int, tenbit bool) (driver.Conn, error)type Conn interface {
// Tx first writes w (if not nil), then reads len(r)
// bytes from device into r (if not nil) in a single
// I2C transaction.
Tx(w, r []byte) error
// Close closes the connection.
Close() error
}Conn represents an active connection to an I2C device. This is the low-level interface that hardware implementations must satisfy.
type Opener interface {
Open(addr int, tenbit bool) (Conn, error)
}Opener opens a connection to an I2C device to communicate with the I2C address given. If the address is a 10-bit I2C address, tenbit is true. This interface should be implemented by hardware-specific drivers.
package main
import (
"log"
"golang.org/x/exp/io/i2c"
)
func main() {
// Open connection to a temperature sensor at address 0x48 on bus /dev/i2c-1
opener := &i2c.Devfs{Dev: "/dev/i2c-1"}
device, err := i2c.Open(opener, 0x48)
if err != nil {
log.Fatalf("Failed to open I2C device: %v", err)
}
defer device.Close()
// Read temperature from register 0x05
tempBuf := make([]byte, 2)
if err := device.ReadReg(0x05, tempBuf); err != nil {
log.Fatalf("Failed to read temperature: %v", err)
}
// Convert bytes to temperature value (example conversion)
temperature := (int(tempBuf[0]) << 8) | int(tempBuf[1])
log.Printf("Temperature: %d", temperature)
}package main
import (
"log"
"golang.org/x/exp/io/i2c"
)
func main() {
opener := &i2c.Devfs{Dev: "/dev/i2c-1"}
device, err := i2c.Open(opener, 0x62)
if err != nil {
log.Fatal(err)
}
defer device.Close()
// Write configuration to register 0x00
config := []byte{0x00, 0x10, 0x20}
if err := device.WriteReg(0x00, config); err != nil {
log.Fatalf("Failed to write configuration: %v", err)
}
log.Println("Configuration written successfully")
}package main
import (
"log"
"golang.org/x/exp/io/i2c"
)
func main() {
opener := &i2c.Devfs{Dev: "/dev/i2c-1"}
// Open a device at a 10-bit address (0x380 as example)
addr := i2c.TenBit(0x380)
device, err := i2c.Open(opener, addr)
if err != nil {
log.Fatalf("Failed to open I2C device with 10-bit address: %v", err)
}
defer device.Close()
// Perform I2C operations as normal
buf := make([]byte, 10)
if err := device.Read(buf); err != nil {
log.Fatal(err)
}
}package main
import (
"log"
"golang.org/x/exp/io/i2c"
"golang.org/x/exp/io/i2c/driver"
)
// CustomDriver implements the driver.Opener interface
type CustomDriver struct {
// Custom driver fields
}
// Open implements the driver.Opener interface
func (c *CustomDriver) Open(addr int, tenbit bool) (driver.Conn, error) {
// Custom implementation for opening a connection
// Return a connection that implements the driver.Conn interface
return &CustomConn{}, nil
}
// CustomConn implements the driver.Conn interface
type CustomConn struct {
// Connection state
}
// Tx implements the driver.Conn interface
func (cc *CustomConn) Tx(w, r []byte) error {
// Custom transaction logic
return nil
}
// Close implements the driver.Conn interface
func (cc *CustomConn) Close() error {
// Clean up resources
return nil
}
func main() {
// Use the custom driver
customDriver := &CustomDriver{}
device, err := i2c.Open(customDriver, 0x48)
if err != nil {
log.Fatal(err)
}
defer device.Close()
}package main
import (
"log"
"golang.org/x/exp/io/i2c"
"golang.org/x/exp/io/i2c/driver"
)
func sendAndReceive(conn driver.Conn, sendData []byte, receiveLength int) ([]byte, error) {
receiveBuf := make([]byte, receiveLength)
if err := conn.Tx(sendData, receiveBuf); err != nil {
return nil, err
}
return receiveBuf, nil
}
func main() {
opener := &i2c.Devfs{Dev: "/dev/i2c-1"}
device, err := i2c.Open(opener, 0x48)
if err != nil {
log.Fatal(err)
}
defer device.Close()
// Example: Send command and receive response in single transaction
command := []byte{0x40} // Example command byte
response, err := sendAndReceive(device, command, 8)
if err != nil {
log.Fatalf("Transaction failed: %v", err)
}
log.Printf("Received: %v", response)
}The I2C package returns errors in the following scenarios:
Example error handling:
device, err := i2c.Open(opener, 0x48)
if err != nil {
log.Fatalf("Failed to open device: %v", err)
}
buf := make([]byte, 10)
if err := device.Read(buf); err != nil {
log.Fatalf("Read operation failed: %v", err)
}
if err := device.Close(); err != nil {
log.Fatalf("Failed to close device: %v", err)
}The golang.org/x/exp/io/i2c package is no longer actively maintained. For new projects, use the actively supported cross-platform alternative at https://periph.io/. The Periph library provides similar functionality with continued maintenance and support across multiple platforms.