initial commit

This commit is contained in:
Karan Misra 2013-12-07 23:11:06 +05:30
commit bc8776440f
9 changed files with 1034 additions and 0 deletions

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Karan Misra
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# go-rpi
Use various sensors on the RaspberryPi with Golang (like a ninja!)

2
doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package rpi provides modules which will help gophers deal with various sensors.
package rpi

284
i2c/i2c.go Normal file
View File

@ -0,0 +1,284 @@
// Package i2c enables gophers i2c speaking ability.
package i2c
import (
"fmt"
"os"
"reflect"
"sync"
"syscall"
"time"
"unsafe"
)
const (
delay = 20
slaveCmd = 0x0703
rdrwCmd = 0x0707
I2C_M_RD = 0x0001
)
type Bus interface {
ReadByte(addr byte) (value byte, err error)
WriteByte(addr, value byte) error
WriteBytes(addr byte, value []byte) error
ReadFromReg(addr, reg byte, value []byte) (err error)
ReadByteFromReg(addr, reg byte) (value byte, err error)
ReadInt(addr, reg byte) (value int, err error)
WriteToReg(addr, reg, value byte) error
}
type i2c_msg struct {
addr uint16
flags uint16
len uint16
buf uintptr
}
type i2c_rdwr_ioctl_data struct {
msgs uintptr
nmsg uint32
}
var busMap map[byte]*bus
var busMapLock sync.Mutex
var Default Bus
type bus struct {
file *os.File
addr byte
mu sync.Mutex
}
func init() {
busMap = make(map[byte]*bus)
var err error
Default, err = NewBus(1)
if err != nil {
panic(err)
}
}
// NewBus creates a new I2C bus interface. The l variable
// controls which bus we connect to.
//
// For the newer RaspberryPi, the number is 1 (earlier model uses 0.)
func NewBus(l byte) (Bus, error) {
busMapLock.Lock()
defer busMapLock.Unlock()
var b *bus
if b = busMap[l]; b == nil {
b = new(bus)
var err error
if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", l), os.O_RDWR, os.ModeExclusive); err != nil {
return nil, err
}
busMap[l] = b
}
return b, nil
}
func (b *bus) setAddress(addr byte) (err error) {
if addr != b.addr {
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 {
err = syscall.Errno(errno)
return
}
b.addr = addr
}
return
}
// Read a byte from the given address.
func (b *bus) ReadByte(addr byte) (value byte, err error) {
b.mu.Lock()
defer b.mu.Unlock()
if err = b.setAddress(addr); err != nil {
return
}
bytes := make([]byte, 1)
n, err := b.file.Read(bytes)
if n != 1 {
err = fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n)
}
value = bytes[0]
return
}
// Write a byte to the given address.
func (b *bus) WriteByte(addr, value byte) (err error) {
b.mu.Lock()
defer b.mu.Unlock()
if err = b.setAddress(addr); err != nil {
return
}
n, err := b.file.Write([]byte{value})
if n != 1 {
err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n)
}
return
}
// Write a bunch of bytes ot the given address.
func (b *bus) WriteBytes(addr byte, value []byte) error {
b.mu.Lock()
defer b.mu.Unlock()
if err := b.setAddress(addr); err != nil {
return err
}
for i := range value {
n, err := b.file.Write([]byte{value[i]})
if n != 1 {
return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n)
}
if err != nil {
return err
}
time.Sleep(delay * time.Millisecond)
}
return nil
}
// Read a bunch of bytes (len(value)) from the given address and register.
func (b *bus) ReadFromReg(addr, reg byte, value []byte) (err error) {
b.mu.Lock()
defer b.mu.Unlock()
if err = b.setAddress(addr); err != nil {
return
}
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value))
var messages [2]i2c_msg
messages[0].addr = uint16(addr)
messages[0].flags = 0
messages[0].len = 1
messages[0].buf = uintptr(unsafe.Pointer(&reg))
messages[1].addr = uint16(addr)
messages[1].flags = I2C_M_RD
messages[1].len = uint16(len(value))
messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data))
var packets i2c_rdwr_ioctl_data
packets.msgs = uintptr(unsafe.Pointer(&messages))
packets.nmsg = 2
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
return syscall.Errno(errno)
}
return nil
}
// Read a byte from the given address and register.
func (b *bus) ReadByteFromReg(addr, reg byte) (value byte, err error) {
buf := make([]byte, 1)
if err = b.ReadFromReg(addr, reg, buf); err != nil {
return
}
value = buf[0]
return
}
// Read a int from the given address and register.
func (b *bus) ReadInt(addr, reg byte) (value int, err error) {
var buf = make([]byte, 2)
if err = b.ReadFromReg(addr, reg, buf); err != nil {
return
}
value = int((int(buf[0]) << 8) | int(buf[1]))
return
}
// Write a byte to the given address and register.
func (b *bus) WriteToReg(addr, reg, value byte) (err error) {
b.mu.Lock()
defer b.mu.Unlock()
if err = b.setAddress(addr); err != nil {
return
}
var outbuf [2]byte
var messages i2c_msg
messages.addr = uint16(addr)
messages.flags = 0
messages.len = uint16(len(outbuf))
messages.buf = uintptr(unsafe.Pointer(&outbuf))
outbuf[0] = reg
outbuf[1] = value
var packets i2c_rdwr_ioctl_data
packets.msgs = uintptr(unsafe.Pointer(&messages))
packets.nmsg = 1
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
err = syscall.Errno(errno)
return
}
return
}
// Read a byte from the given address.
func ReadByte(addr byte) (value byte, err error) {
return Default.ReadByte(addr)
}
// Write a byte to the given address.
func WriteByte(addr, value byte) (err error) {
return Default.WriteByte(addr, value)
}
// Write a bunch of bytes ot the given address.
func WriteBytes(addr byte, value []byte) error {
return Default.WriteBytes(addr, value)
}
// Read a bunch of bytes (len(value)) from the given address and register.
func ReadFromReg(addr, reg byte, value []byte) (err error) {
return Default.ReadFromReg(addr, reg, value)
}
// Read a byte from the given address and register.
func ReadByteFromReg(addr, reg byte) (value byte, err error) {
return Default.ReadByteFromReg(addr, reg)
}
// Read a int from the given address and register.
func ReadInt(addr, reg byte) (value int, err error) {
return Default.ReadInt(addr, reg)
}
// Write a byte to the given address and register.
func WriteToReg(addr, reg, value byte) (err error) {
return Default.WriteToReg(addr, reg, value)
}

38
samples/bmp085.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"log"
"time"
"github.com/kid0m4n/go-rpi/i2c"
"github.com/kid0m4n/go-rpi/sensor/bmp085"
)
func main() {
bus, err := i2c.NewBus(1)
if err != nil {
log.Panic(err)
}
baro := bmp085.New(bus)
defer baro.Close()
for {
temp, err := baro.Temperature()
if err != nil {
log.Panic(err)
}
log.Printf("Temp is %v", temp)
pressure, err := baro.Pressure()
if err != nil {
log.Panic(err)
}
log.Printf("Pressure is %v", pressure)
altitude, err := baro.Altitude()
if err != nil {
log.Panic(err)
}
log.Printf("Altitude is %v", altitude)
time.Sleep(500 * time.Millisecond)
}
}

28
samples/lsm303.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"log"
"time"
"github.com/kid0m4n/go-rpi/i2c"
"github.com/kid0m4n/go-rpi/sensor/lsm303"
)
func main() {
bus, err := i2c.NewBus(1)
if err != nil {
log.Panic(err)
}
mems := lsm303.New(bus)
defer mems.Close()
for {
heading, err := mems.Heading()
if err != nil {
log.Panic(err)
}
log.Printf("Heading is %v", heading)
time.Sleep(500 * time.Millisecond)
}
}

462
sensor/bmp085/bmp085.go Normal file
View File

@ -0,0 +1,462 @@
// Package bmp085 allows interfacing with Bosch BMP085 barometric pressure sensor. This sensor
// has the ability to provided compensated temperature and pressure readings.
package bmp085
import (
"log"
"math"
"sync"
"time"
"github.com/kid0m4n/go-rpi/i2c"
)
const (
address = 0x77
calAc1 = 0xAA
calAc2 = 0xAC
calAc3 = 0xAE
calAc4 = 0xB0
calAc5 = 0xB2
calAc6 = 0xB4
calB1 = 0xB6
calB2 = 0xB8
calMB = 0xBA
calMC = 0xBC
calMD = 0xBE
control = 0xF4
tempData = 0xF6
pressureData = 0xF6
readTempCmd = 0x2E
readPressureCmd = 0x34
tempReadDelay = 5 * time.Millisecond
p0 = 101325
pollDelay = 250
)
type BMP085 interface {
SetPollDelay(delay int)
Temperature() (temp float64, err error)
Pressure() (pressure int, err error)
Altitude() (altitude float64, err error)
Run() error
Close()
}
type bmp085 struct {
bus i2c.Bus
oss uint
ac1, ac2, ac3 int16
ac4, ac5, ac6 uint16
b1, b2, mb, mc, md int16
b5 int32
calibrated bool
cmu *sync.RWMutex
poll int
temps chan uint16
pressures chan int32
altitudes chan float64
quit chan struct{}
debug bool
}
var Default = New(i2c.Default)
// New creates a new BMP085 interface. The bus variable controls
// the I2C bus used to communicate with the device.
func New(bus i2c.Bus) BMP085 {
return &bmp085{bus: bus, cmu: new(sync.RWMutex), poll: pollDelay}
}
// SetPollDelay sets the delay between runs of the data acquisition loop.
func (d *bmp085) SetPollDelay(delay int) {
d.poll = delay
}
func (d *bmp085) calibrate() (err error) {
d.cmu.RLock()
if d.calibrated {
d.cmu.RUnlock()
return
}
d.cmu.RUnlock()
d.cmu.Lock()
defer d.cmu.Unlock()
readInt16 := func(reg byte) (value int16, err error) {
var v int
if v, err = d.bus.ReadInt(address, reg); err != nil {
return
}
value = int16(v)
return
}
readUInt16 := func(reg byte) (value uint16, err error) {
var v int
if v, err = d.bus.ReadInt(address, reg); err != nil {
return
}
value = uint16(v)
return
}
d.ac1, err = readInt16(calAc1)
if err != nil {
return
}
d.ac2, err = readInt16(calAc2)
if err != nil {
return
}
d.ac3, err = readInt16(calAc3)
if err != nil {
return
}
d.ac4, err = readUInt16(calAc4)
if err != nil {
return
}
d.ac5, err = readUInt16(calAc5)
if err != nil {
return
}
d.ac6, err = readUInt16(calAc6)
if err != nil {
return
}
d.b1, err = readInt16(calB1)
if err != nil {
return
}
d.b2, err = readInt16(calB2)
if err != nil {
return
}
d.mb, err = readInt16(calMB)
if err != nil {
return
}
d.mc, err = readInt16(calMC)
if err != nil {
return
}
d.md, err = readInt16(calMD)
if err != nil {
return
}
d.calibrated = true
if d.debug {
log.Print("bmp085: calibration data retrieved")
log.Printf("bmp085: param AC1 = %v", d.ac1)
log.Printf("bmp085: param AC2 = %v", d.ac2)
log.Printf("bmp085: param AC3 = %v", d.ac3)
log.Printf("bmp085: param AC4 = %v", d.ac4)
log.Printf("bmp085: param AC5 = %v", d.ac5)
log.Printf("bmp085: param AC6 = %v", d.ac6)
log.Printf("bmp085: param B1 = %v", d.b1)
log.Printf("bmp085: param B2 = %v", d.b2)
log.Printf("bmp085: param MB = %v", d.mb)
log.Printf("bmp085: param MC = %v", d.mc)
log.Printf("bmp085: param MD = %v", d.md)
}
return
}
func (d *bmp085) readUncompensatedTemp() (temp uint16, err error) {
if err = d.bus.WriteToReg(address, control, readTempCmd); err != nil {
return
}
time.Sleep(tempReadDelay)
var t int
t, err = d.bus.ReadInt(address, tempData)
if err != nil {
return
}
temp = uint16(t)
return
}
func (d *bmp085) calcTemp(utemp uint16) uint16 {
x1 := ((int(utemp) - int(d.ac6)) * int(d.ac5)) >> 15
x2 := (int(d.mc) << 11) / (x1 + int(d.md))
d.cmu.Lock()
d.b5 = int32(x1 + x2)
d.cmu.Unlock()
return uint16((d.b5 + 8) >> 4)
}
func (d *bmp085) measureTemp() (temp uint16, err error) {
if err = d.calibrate(); err != nil {
return
}
var utemp uint16
if utemp, err = d.readUncompensatedTemp(); err != nil {
return
}
if d.debug {
log.Printf("bcm085: uncompensated temp: %v", utemp)
}
temp = d.calcTemp(utemp)
if d.debug {
log.Printf("bcm085: compensated temp %v", temp)
}
return
}
// Return temperature reading.
func (d *bmp085) Temperature() (temp float64, err error) {
select {
case t := <-d.temps:
temp = float64(t) / 10
return
default:
if d.debug {
log.Print("bcm085: no temps available... measuring")
}
var t uint16
t, err = d.measureTemp()
if err != nil {
return
}
temp = float64(t) / 10
return
}
}
func (d *bmp085) readUncompensatedPressure() (pressure uint32, err error) {
if err = d.bus.WriteToReg(address, control, byte(readPressureCmd+(d.oss<<6))); err != nil {
return
}
time.Sleep(time.Duration(2+(3<<d.oss)) * time.Millisecond)
data := make([]byte, 3)
if err = d.bus.ReadFromReg(address, pressureData, data); err != nil {
return
}
pressure = ((uint32(data[0]) << 16) | (uint32(data[1]) << 8) | uint32(data[2])) >> (8 - d.oss)
return
}
func (d *bmp085) calcPressure(upressure uint32) (p int32) {
var x1, x2, x3 int32
l := func(s string, v interface{}) {
if d.debug {
log.Printf("bcm085: %v = %v", s, v)
}
}
b6 := d.b5 - 4000
l("b6", b6)
// Calculate b3
x1 = (int32(d.b2) * int32(b6*b6) >> 12) >> 11
x2 = (int32(d.ac2) * b6) >> 11
x3 = x1 + x2
b3 := (((int32(d.ac1)*4 + x3) << d.oss) + 2) >> 2
l("x1", x1)
l("x2", x2)
l("x3", x3)
l("b3", b3)
// Calculate b4
x1 = (int32(d.ac3) * b6) >> 13
x2 = (int32(d.b1) * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 := (uint32(d.ac4) * uint32(x3+32768)) >> 15
l("x1", x1)
l("x2", x2)
l("x3", x3)
l("b4", b4)
b7 := (uint32(upressure-uint32(b3)) * (50000 >> d.oss))
if b7 < 0x80000000 {
p = int32((b7 << 1) / b4)
} else {
p = int32((b7 / b4) << 1)
}
l("b7", b7)
l("p", p)
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
p += (x1 + x2 + 3791) >> 4
l("x1", x1)
l("x2", x2)
l("x3", x3)
l("p", p)
return
}
func (d *bmp085) calcAltitude(pressure int32) float64 {
return 44330 * (1 - math.Pow(float64(pressure)/p0, 0.190295))
}
func (d *bmp085) measurePressureAndAltitude() (pressure int32, altitude float64, err error) {
if err = d.calibrate(); err != nil {
return
}
var upressure uint32
if upressure, err = d.readUncompensatedPressure(); err != nil {
return
}
if d.debug {
log.Printf("bcm085: uncompensated pressure: %v", upressure)
}
pressure = d.calcPressure(upressure)
if d.debug {
log.Printf("bcm085: compensated pressure %v", pressure)
}
altitude = d.calcAltitude(pressure)
if d.debug {
log.Printf("bcm085: calculated altitude %v", altitude)
}
return
}
// Return pressure reading.
func (d *bmp085) Pressure() (pressure int, err error) {
if err = d.calibrate(); err != nil {
return
}
select {
case p := <-d.pressures:
pressure = int(p)
return
default:
if d.debug {
log.Print("bcm085: no pressures available... measuring")
}
var p int32
p, _, err = d.measurePressureAndAltitude()
if err != nil {
return
}
pressure = int(p)
return
}
}
// Return altitude reading.
func (d *bmp085) Altitude() (altitude float64, err error) {
if err = d.calibrate(); err != nil {
return
}
select {
case altitude = <-d.altitudes:
return
default:
if d.debug {
log.Print("bcm085: no altitudes available... measuring")
}
_, altitude, err = d.measurePressureAndAltitude()
if err != nil {
return
}
return
}
}
// Start the sensor data acquisition loop.
func (d *bmp085) Run() (err error) {
go func() {
d.quit = make(chan struct{})
timer := time.Tick(time.Duration(d.poll) * time.Millisecond)
var temp uint16
var pressure int32
var altitude float64
for {
select {
case <-timer:
t, err := d.measureTemp()
if err == nil {
temp = t
}
if err == nil && d.temps == nil {
d.temps = make(chan uint16)
}
p, a, err := d.measurePressureAndAltitude()
if err == nil {
pressure = p
altitude = a
}
if err == nil && d.pressures == nil && d.altitudes == nil {
d.pressures = make(chan int32)
d.altitudes = make(chan float64)
}
case d.temps <- temp:
case d.pressures <- pressure:
case d.altitudes <- altitude:
case <-d.quit:
d.temps = nil
d.pressures = nil
d.altitudes = nil
return
}
}
}()
return
}
// Close.
func (d *bmp085) Close() {
if d.quit != nil {
d.quit <- struct{}{}
}
}
// Return temperature reading.
func Temperature() (temp float64, err error) {
return Default.Temperature()
}
// Return pressure reading.
func Pressure() (pressure int, err error) {
return Default.Pressure()
}
// Return altitude reading.
func Altitude() (altitude float64, err error) {
return Default.Altitude()
}
// Start the sensor data acquisition loop.
func Run() (err error) {
return Default.Run()
}
// Close.
func Close() {
Default.Close()
}

2
sensor/doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package sensor contains the various sensors modules for use on your Raspberry Pi.
package sensor

195
sensor/lsm303/lsm303.go Normal file
View File

@ -0,0 +1,195 @@
// Package lsm303 allows interfacing with the LSM303 magnetometer.
package lsm303
import (
"log"
"math"
"sync"
"time"
"github.com/kid0m4n/go-rpi/i2c"
)
const (
magAddress = 0x1E
magConfigRegA = 0x00
MagHz75 = 0x00
Mag1Hz5 = 0x04
Mag3Hz = 0x08
Mag7Hz5 = 0x0C
Mag15Hz = 0x10
Mag30Hz = 0x14
Mag75Hz = 0x18
MagNormal = 0x00
MagPositiveBias = 0x01
MagNegativeBias = 0x02
MagCRADefault = Mag15Hz | MagNormal
magModeReg = 0x02
MagContinuous = 0x00
MagSleep = 0x03
MagMRDefault = MagContinuous
magDataSignal = 0x02
magData = 0x03
pollDelay = 250
)
type LSM303 interface {
SetPollDelay(delay int)
Heading() (heading float64, err error)
Run() error
Close() error
}
type lsm303 struct {
bus i2c.Bus
initialized bool
mu *sync.RWMutex
headings chan float64
poll int
quit chan struct{}
debug bool
}
var Default = New(i2c.Default)
// New creates a new LSM303 interface. The bus variable controls
// the I2C bus used to communicate with the device.
func New(bus i2c.Bus) LSM303 {
return &lsm303{bus: bus, mu: new(sync.RWMutex), poll: pollDelay}
}
// Initialize the device
func (d *lsm303) setup() (err error) {
d.mu.RLock()
if d.initialized {
d.mu.RUnlock()
return
}
d.mu.RUnlock()
d.mu.Lock()
defer d.mu.Unlock()
if err = d.bus.WriteToReg(magAddress, magConfigRegA, MagCRADefault); err != nil {
return
}
if err = d.bus.WriteToReg(magAddress, magModeReg, MagMRDefault); err != nil {
return
}
d.initialized = true
return
}
// SetPollDelay sets the delay between runs of the data acquisition loop.
func (d *lsm303) SetPollDelay(delay int) {
d.poll = delay
}
func (d *lsm303) measureHeading() (heading float64, err error) {
if err = d.setup(); err != nil {
return
}
if _, err = d.bus.ReadByteFromReg(magAddress, magDataSignal); err != nil {
return
}
data := make([]byte, 6)
if err = d.bus.ReadFromReg(magAddress, magData, data); err != nil {
return
}
x := int16(data[0])<<8 | int16(data[1])
y := int16(data[2])<<8 | int16(data[3])
heading = math.Atan2(float64(y), float64(x)) / math.Pi * 180
if heading < 0 {
heading += 360
}
return
}
// Return heading [0, 360).
func (d *lsm303) Heading() (heading float64, err error) {
select {
case heading = <-d.headings:
return
default:
if d.debug {
log.Print("lsm303: no headings available... measuring")
}
return d.measureHeading()
}
}
// Start the sensor data acquisition loop.
func (d *lsm303) Run() (err error) {
go func() {
d.quit = make(chan struct{})
timer := time.Tick(time.Duration(d.poll) * time.Millisecond)
var heading float64
for {
select {
case <-timer:
h, err := d.measureHeading()
if err == nil {
heading = h
}
if err == nil && d.headings == nil {
d.headings = make(chan float64)
}
case d.headings <- heading:
case <-d.quit:
d.headings = nil
return
}
}
}()
return
}
// Close the sensor data acquisition loop and put the LSM303 into sleep mode.
func (d *lsm303) Close() (err error) {
if d.quit != nil {
d.quit <- struct{}{}
}
err = d.bus.WriteToReg(magAddress, magModeReg, MagSleep)
return
}
// Return heading [0, 360).
func Heading() (heading float64, err error) {
return Default.Heading()
}
// Start the sensor data acquisition loop.
func Run() (err error) {
return Default.Run()
}
// Close the sensor data acquisition loop and put the LSM303 into sleep mode.
func Close() (err error) {
return Default.Close()
}