├── .travis.yml ├── LICENSE ├── README.md ├── cgo.go ├── i2c.go ├── logger.go └── noncgo.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - "1.10" 4 | # - "tip" 5 | 6 | # first part of the GOARCH workaround 7 | # setting the GOARCH directly doesn't work, since the value will be overwritten later 8 | # so set it to a temporary environment variable first 9 | env: 10 | global: 11 | TRAVIS_CGO_ENABLED=1 12 | TRAVIS_GOOS=linux 13 | matrix: 14 | - TRAVIS_GOARCH=amd64 15 | - TRAVIS_GOARCH=arm TRAVIS_CC=arm-linux-gnueabi-gcc TRAVIS_GOARM=6 16 | 17 | # second part of the GOARCH workaround 18 | # now actually set the GOARCH env variable to the value of the temporary variable set earlier 19 | before_install: 20 | - sudo apt-get install gcc-arm-linux-gnueabi crossbuild-essential-armel # for CGO cross compile to ARM 21 | - export CGO_ENABLED=$TRAVIS_CGO_ENABLED GOARCH=$TRAVIS_GOARCH GOARM=$TRAVIS_GOARM GOOS=$TRAVIS_GOOS CC=$TRAVIS_CC 22 | - go env # for debugging 23 | - go tool dist env # for debugging 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Denis Dyakov 4 | Copyright (c) 2013 Dave Cheney 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I2C-bus interaction of peripheral sensors with Raspberry PI embedded linux or respective clones 2 | ============================================================================================== 3 | 4 | [![Build Status](https://travis-ci.org/d2r2/go-i2c.svg?branch=master)](https://travis-ci.org/d2r2/go-i2c) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/d2r2/go-i2c)](https://goreportcard.com/report/github.com/d2r2/go-i2c) 6 | [![GoDoc](https://godoc.org/github.com/d2r2/go-i2c?status.svg)](https://godoc.org/github.com/d2r2/go-i2c) 7 | [![MIT License](http://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) 8 | 9 | This library written in [Go programming language](https://golang.org/) intended to activate and interact with the I2C bus by reading and writing data. 10 | 11 | Compatibility 12 | ------------- 13 | 14 | Tested on Raspberry Pi 1 (model B), Raspberry Pi 3 B+, Banana Pi (model M1), Orange Pi Zero, Orange Pi One. 15 | 16 | Golang usage 17 | ------------ 18 | 19 | ```go 20 | func main() { 21 | // Create new connection to I2C bus on 2 line with address 0x27 22 | i2c, err := i2c.NewI2C(0x27, 2) 23 | if err != nil { log.Fatal(err) } 24 | // Free I2C connection on exit 25 | defer i2c.Close() 26 | .... 27 | // Here goes code specific for sending and reading data 28 | // to and from device connected via I2C bus, like: 29 | _, err := i2c.Write([]byte{0x1, 0xF3}) 30 | if err != nil { log.Fatal(err) } 31 | .... 32 | } 33 | ``` 34 | 35 | Tutorial 36 | -------- 37 | 38 | My [repositories](https://github.com/d2r2?tab=repositories) contain quite a lot projects, which use i2c library as a starting point to interact with various peripheral devices and sensors for use on embedded Linux devices. All these libraries start with a standard call to open I2C-connection to specific bus line and address, than pass i2c instance to device. 39 | 40 | In its turn, go-i2c use [go-logger](https://github.com/d2r2/go-logger) library to output debug and other notification's lines which produce all necessary levels of logging. You can manage what level of verbosity you would like to see, by adding call: 41 | ```go 42 | // Uncomment/comment next line to suppress/increase verbosity of output 43 | logger.ChangePackageLogLevel("i2c", logger.InfoLevel) 44 | ``` 45 | Once you put this call, it will decrease verbosity from default "Debug" up to next "Info" level, reducing the number of low-level console outputs that occur during interaction with the I2C bus. Please, find examples in corresponding I2C-driven sensors among my projects. 46 | 47 | You will find here the list of all devices and sensors supported by me, that reference this library: 48 | 49 | - [Liquid-crystal display driven by Hitachi HD44780 IC](https://github.com/d2r2/go-hd44780). 50 | - [BMP180/BMP280/BME280 temperature and pressure sensors](https://github.com/d2r2/go-bsbmp). 51 | - [DHT12/AM2320 humidity and temperature sensors](https://github.com/d2r2/go-aosong). 52 | - [Si7021 relative humidity and temperature sensor](https://github.com/d2r2/go-si7021). 53 | - [SHT3x humidity and temperature sensor](https://github.com/d2r2/go-sht3x). 54 | - [VL53L0X time-of-flight ranging sensor](https://github.com/d2r2/go-vl53l0x). 55 | - [BH1750 ambient light sensor](https://github.com/d2r2/go-bh1750). 56 | - [MPL3115A2 pressure and temperature sensor](https://github.com/d2r2/go-mpl3115a2). 57 | 58 | 59 | Getting help 60 | ------------ 61 | 62 | GoDoc [documentation](http://godoc.org/github.com/d2r2/go-i2c) 63 | 64 | Troubleshooting 65 | -------------- 66 | 67 | - *How to obtain fresh Golang installation to RPi device (either any RPi clone):* 68 | If your RaspberryPI golang installation taken by default from repository is outdated, you may consider 69 | to install actual golang manually from official Golang [site](https://golang.org/dl/). Download 70 | tar.gz file containing armv6l in the name. Follow installation instructions. 71 | 72 | - *How to enable I2C bus on RPi device:* 73 | If you employ RaspberryPI, use raspi-config utility to activate i2c-bus on the OS level. 74 | Go to "Interfacing Options" menu, to active I2C bus. 75 | Probably you will need to reboot to load i2c kernel module. 76 | Finally you should have device like /dev/i2c-1 present in the system. 77 | 78 | - *How to find I2C bus allocation and device address:* 79 | Use i2cdetect utility in format "i2cdetect -y X", where X may vary from 0 to 5 or more, 80 | to discover address occupied by peripheral device. To install utility you should run 81 | `apt install i2c-tools` on debian-kind system. `i2cdetect -y 1` sample output: 82 | ``` 83 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 84 | 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 85 | 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 86 | 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 87 | 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 88 | 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 89 | 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 90 | 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 91 | 70: -- -- -- -- -- -- 76 -- 92 | ``` 93 | 94 | License 95 | ------- 96 | 97 | Go-i2c is licensed under MIT License. 98 | -------------------------------------------------------------------------------- /cgo.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo 2 | 3 | package i2c 4 | 5 | // #include 6 | import "C" 7 | 8 | // Get I2C_SLAVE constant value from 9 | // Linux OS I2C declaration file. 10 | const ( 11 | I2C_SLAVE = C.I2C_SLAVE 12 | ) 13 | -------------------------------------------------------------------------------- /i2c.go: -------------------------------------------------------------------------------- 1 | // Package i2c provides low level control over the Linux i2c bus. 2 | // 3 | // Before usage you should load the i2c-dev kernel module 4 | // 5 | // sudo modprobe i2c-dev 6 | // 7 | // Each i2c bus can address 127 independent i2c devices, and most 8 | // Linux systems contain several buses. 9 | 10 | package i2c 11 | 12 | import ( 13 | "encoding/hex" 14 | "fmt" 15 | "os" 16 | "syscall" 17 | ) 18 | 19 | // I2C represents a connection to I2C-device. 20 | type I2C struct { 21 | addr uint8 22 | bus int 23 | rc *os.File 24 | } 25 | 26 | // NewI2C opens a connection for I2C-device. 27 | // SMBus (System Management Bus) protocol over I2C 28 | // supported as well: you should preliminary specify 29 | // register address to read from, either write register 30 | // together with the data in case of write operations. 31 | func NewI2C(addr uint8, bus int) (*I2C, error) { 32 | f, err := os.OpenFile(fmt.Sprintf("/dev/i2c-%d", bus), os.O_RDWR, 0600) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if err := ioctl(f.Fd(), I2C_SLAVE, uintptr(addr)); err != nil { 37 | return nil, err 38 | } 39 | v := &I2C{rc: f, bus: bus, addr: addr} 40 | return v, nil 41 | } 42 | 43 | // GetBus return bus line, where I2C-device is allocated. 44 | func (v *I2C) GetBus() int { 45 | return v.bus 46 | } 47 | 48 | // GetAddr return device occupied address in the bus. 49 | func (v *I2C) GetAddr() uint8 { 50 | return v.addr 51 | } 52 | 53 | func (v *I2C) write(buf []byte) (int, error) { 54 | return v.rc.Write(buf) 55 | } 56 | 57 | // WriteBytes send bytes to the remote I2C-device. The interpretation of 58 | // the message is implementation-dependent. 59 | func (v *I2C) WriteBytes(buf []byte) (int, error) { 60 | lg.Debugf("Write %d hex bytes: [%+v]", len(buf), hex.EncodeToString(buf)) 61 | return v.write(buf) 62 | } 63 | 64 | func (v *I2C) read(buf []byte) (int, error) { 65 | return v.rc.Read(buf) 66 | } 67 | 68 | // ReadBytes read bytes from I2C-device. 69 | // Number of bytes read correspond to buf parameter length. 70 | func (v *I2C) ReadBytes(buf []byte) (int, error) { 71 | n, err := v.read(buf) 72 | if err != nil { 73 | return n, err 74 | } 75 | lg.Debugf("Read %d hex bytes: [%+v]", len(buf), hex.EncodeToString(buf)) 76 | return n, nil 77 | } 78 | 79 | // Close I2C-connection. 80 | func (v *I2C) Close() error { 81 | return v.rc.Close() 82 | } 83 | 84 | // ReadRegBytes read count of n byte's sequence from I2C-device 85 | // starting from reg address. 86 | // SMBus (System Management Bus) protocol over I2C. 87 | func (v *I2C) ReadRegBytes(reg byte, n int) ([]byte, int, error) { 88 | lg.Debugf("Read %d bytes starting from reg 0x%0X...", n, reg) 89 | _, err := v.WriteBytes([]byte{reg}) 90 | if err != nil { 91 | return nil, 0, err 92 | } 93 | buf := make([]byte, n) 94 | c, err := v.ReadBytes(buf) 95 | if err != nil { 96 | return nil, 0, err 97 | } 98 | return buf, c, nil 99 | 100 | } 101 | 102 | // ReadRegU8 reads byte from I2C-device register specified in reg. 103 | // SMBus (System Management Bus) protocol over I2C. 104 | func (v *I2C) ReadRegU8(reg byte) (byte, error) { 105 | _, err := v.WriteBytes([]byte{reg}) 106 | if err != nil { 107 | return 0, err 108 | } 109 | buf := make([]byte, 1) 110 | _, err = v.ReadBytes(buf) 111 | if err != nil { 112 | return 0, err 113 | } 114 | lg.Debugf("Read U8 %d from reg 0x%0X", buf[0], reg) 115 | return buf[0], nil 116 | } 117 | 118 | // WriteRegU8 writes byte to I2C-device register specified in reg. 119 | // SMBus (System Management Bus) protocol over I2C. 120 | func (v *I2C) WriteRegU8(reg byte, value byte) error { 121 | buf := []byte{reg, value} 122 | _, err := v.WriteBytes(buf) 123 | if err != nil { 124 | return err 125 | } 126 | lg.Debugf("Write U8 %d to reg 0x%0X", value, reg) 127 | return nil 128 | } 129 | 130 | // ReadRegU16BE reads unsigned big endian word (16 bits) 131 | // from I2C-device starting from address specified in reg. 132 | // SMBus (System Management Bus) protocol over I2C. 133 | func (v *I2C) ReadRegU16BE(reg byte) (uint16, error) { 134 | _, err := v.WriteBytes([]byte{reg}) 135 | if err != nil { 136 | return 0, err 137 | } 138 | buf := make([]byte, 2) 139 | _, err = v.ReadBytes(buf) 140 | if err != nil { 141 | return 0, err 142 | } 143 | w := uint16(buf[0])<<8 + uint16(buf[1]) 144 | lg.Debugf("Read U16 %d from reg 0x%0X", w, reg) 145 | return w, nil 146 | } 147 | 148 | // ReadRegU16LE reads unsigned little endian word (16 bits) 149 | // from I2C-device starting from address specified in reg. 150 | // SMBus (System Management Bus) protocol over I2C. 151 | func (v *I2C) ReadRegU16LE(reg byte) (uint16, error) { 152 | w, err := v.ReadRegU16BE(reg) 153 | if err != nil { 154 | return 0, err 155 | } 156 | // exchange bytes 157 | w = (w&0xFF)<<8 + w>>8 158 | return w, nil 159 | } 160 | 161 | // ReadRegS16BE reads signed big endian word (16 bits) 162 | // from I2C-device starting from address specified in reg. 163 | // SMBus (System Management Bus) protocol over I2C. 164 | func (v *I2C) ReadRegS16BE(reg byte) (int16, error) { 165 | _, err := v.WriteBytes([]byte{reg}) 166 | if err != nil { 167 | return 0, err 168 | } 169 | buf := make([]byte, 2) 170 | _, err = v.ReadBytes(buf) 171 | if err != nil { 172 | return 0, err 173 | } 174 | w := int16(buf[0])<<8 + int16(buf[1]) 175 | lg.Debugf("Read S16 %d from reg 0x%0X", w, reg) 176 | return w, nil 177 | } 178 | 179 | // ReadRegS16LE reads signed little endian word (16 bits) 180 | // from I2C-device starting from address specified in reg. 181 | // SMBus (System Management Bus) protocol over I2C. 182 | func (v *I2C) ReadRegS16LE(reg byte) (int16, error) { 183 | w, err := v.ReadRegS16BE(reg) 184 | if err != nil { 185 | return 0, err 186 | } 187 | // exchange bytes 188 | w = (w&0xFF)<<8 + w>>8 189 | return w, nil 190 | 191 | } 192 | 193 | // WriteRegU16BE writes unsigned big endian word (16 bits) 194 | // value to I2C-device starting from address specified in reg. 195 | // SMBus (System Management Bus) protocol over I2C. 196 | func (v *I2C) WriteRegU16BE(reg byte, value uint16) error { 197 | buf := []byte{reg, byte((value & 0xFF00) >> 8), byte(value & 0xFF)} 198 | _, err := v.WriteBytes(buf) 199 | if err != nil { 200 | return err 201 | } 202 | lg.Debugf("Write U16 %d to reg 0x%0X", value, reg) 203 | return nil 204 | } 205 | 206 | // WriteRegU16LE writes unsigned little endian word (16 bits) 207 | // value to I2C-device starting from address specified in reg. 208 | // SMBus (System Management Bus) protocol over I2C. 209 | func (v *I2C) WriteRegU16LE(reg byte, value uint16) error { 210 | w := (value*0xFF00)>>8 + value<<8 211 | return v.WriteRegU16BE(reg, w) 212 | } 213 | 214 | // WriteRegS16BE writes signed big endian word (16 bits) 215 | // value to I2C-device starting from address specified in reg. 216 | // SMBus (System Management Bus) protocol over I2C. 217 | func (v *I2C) WriteRegS16BE(reg byte, value int16) error { 218 | buf := []byte{reg, byte((uint16(value) & 0xFF00) >> 8), byte(value & 0xFF)} 219 | _, err := v.WriteBytes(buf) 220 | if err != nil { 221 | return err 222 | } 223 | lg.Debugf("Write S16 %d to reg 0x%0X", value, reg) 224 | return nil 225 | } 226 | 227 | // WriteRegS16LE writes signed little endian word (16 bits) 228 | // value to I2C-device starting from address specified in reg. 229 | // SMBus (System Management Bus) protocol over I2C. 230 | func (v *I2C) WriteRegS16LE(reg byte, value int16) error { 231 | w := int16((uint16(value)*0xFF00)>>8) + value<<8 232 | return v.WriteRegS16BE(reg, w) 233 | } 234 | 235 | func ioctl(fd, cmd, arg uintptr) error { 236 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, cmd, arg, 0, 0, 0) 237 | if err != 0 { 238 | return err 239 | } 240 | return nil 241 | } 242 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | package i2c 2 | 3 | import logger "github.com/d2r2/go-logger" 4 | 5 | // You can manage verbosity of log output 6 | // in the package by changing last parameter value 7 | // (comment/uncomment corresponding lines). 8 | var lg = logger.NewPackageLogger("i2c", 9 | logger.DebugLevel, 10 | // logger.InfoLevel, 11 | ) 12 | -------------------------------------------------------------------------------- /noncgo.go: -------------------------------------------------------------------------------- 1 | // +build !cgo 2 | 3 | package i2c 4 | 5 | // Use hard-coded value for system I2C_SLAVE 6 | // constant, if OS not Linux or CGO disabled. 7 | // This is not a good approach, but 8 | // can be used as a last resort. 9 | const ( 10 | I2C_SLAVE = 0x0703 11 | ) 12 | --------------------------------------------------------------------------------