mirror of
https://github.com/kidoman/embd
synced 2024-12-30 16:41:37 +01:00
initial commit
This commit is contained in:
commit
bc8776440f
20
LICENSE
Normal file
20
LICENSE
Normal 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
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# go-rpi
|
||||
|
||||
Use various sensors on the RaspberryPi with Golang (like a ninja!)
|
2
doc.go
Normal file
2
doc.go
Normal 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
284
i2c/i2c.go
Normal 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(®))
|
||||
|
||||
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
38
samples/bmp085.go
Normal 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
28
samples/lsm303.go
Normal 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
462
sensor/bmp085/bmp085.go
Normal 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
2
sensor/doc.go
Normal 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
195
sensor/lsm303/lsm303.go
Normal 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()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user