From 0e2e648a07dc6c68639dca700b3d452cd28e05df Mon Sep 17 00:00:00 2001 From: deepak Date: Sat, 25 Mar 2017 05:46:44 +0000 Subject: [PATCH 1/5] improved ultrasonic sensor --- sensor/us020/us020.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/sensor/us020/us020.go b/sensor/us020/us020.go index b7d400a..11b8254 100644 --- a/sensor/us020/us020.go +++ b/sensor/us020/us020.go @@ -2,6 +2,7 @@ package us020 import ( + "errors" "sync" "time" @@ -82,24 +83,37 @@ func (d *US020) Distance() (float64, error) { return 0, err } - glog.V(2).Infof("us020: trigerring pulse") + // Ready the goroutine to measure return pulse. + done := make(chan time.Duration) + errChan := make(chan error) + go func() { + glog.V(2).Infof("us020: waiting for echo to go high") + duration, err := d.EchoPin.TimePulse(embd.High) + if err != nil { + errChan <- err + return + } + done <- duration + }() - // Generate a TRIGGER pulse + // Generate a TRIGGER pulse. + glog.V(2).Infof("us020: trigerring pulse") d.TriggerPin.Write(embd.High) time.Sleep(pulseDelay) d.TriggerPin.Write(embd.Low) - glog.V(2).Infof("us020: waiting for echo to go high") - - duration, err := d.EchoPin.TimePulse(embd.High) - if err != nil { - return 0, err + // Wait for data on channel or timeout. + t := time.NewTimer(time.Millisecond * 200) + select { + case <-t.C: + return 0, errors.New("timeout on ultrasonic pulse") + case duration := <-done: + distance := float64(duration.Nanoseconds()) / 10000000 * (d.speedSound / 2) + return distance, nil + case e := <-errChan: + return 0, e } - // Calculate the distance based on the time computed - distance := float64(duration.Nanoseconds()) / 10000000 * (d.speedSound / 2) - - return distance, nil } // Close. From 88aac1b0ebba9918de4471b7cdf8e23627ea3e11 Mon Sep 17 00:00:00 2001 From: Deepak Guru Date: Thu, 27 Apr 2017 23:38:31 -0700 Subject: [PATCH 2/5] Added hmc5883L magnetometer --- sensor/hmc5883l/hmc5883l.go | 171 ++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 sensor/hmc5883l/hmc5883l.go diff --git a/sensor/hmc5883l/hmc5883l.go b/sensor/hmc5883l/hmc5883l.go new file mode 100644 index 0000000..58fedc3 --- /dev/null +++ b/sensor/hmc5883l/hmc5883l.go @@ -0,0 +1,171 @@ +// Package hmc5883l allows interfacing with the HMC5883L magnetometer. +package hmc5883l + +import ( + "math" + "sync" + "time" + + "github.com/golang/glog" + "github.com/kidoman/embd" +) + +const ( + magAddress = 0x1E + + // Register addresses. + magConfigRegA = 0x00 + magConfigRegB = 0x01 + magModeReg = 0x02 + magMSBx = 0x03 + magLSBx = 0x04 + magMSBz = 0x05 + magLSBz = 0x06 + magMSBy = 0x07 + magLSBy = 0x08 + magStatusReg = 0x09 + + // ConfigA Params. + MagHz75 = 0x00 // ODR = 0.75 Hz + Mag1Hz5 = 0x04 // ODR = 1.5 Hz + Mag3Hz = 0x08 // ODR = 3 Hz + Mag7Hz5 = 0x0C // ODR = 7.5 Hz + Mag15Hz = 0x10 // ODR = 15 Hz + Mag30Hz = 0x14 // ODR = 30 Hz + Mag75Hz = 0x18 // ODR = 75 Hz + MagNormal = 0x00 // Normal mode + MagPositiveBias = 0x01 // Positive bias mode + MagNegativeBias = 0x02 // Negative bias mode + + MagCRADefault = Mag15Hz | MagNormal // 15 Hz and normal mode is the default + + // ConfigB Params. + MagCRBDefault = 0x20 // Gain 1090 + + // Mode Reg Params. + MagContinuous = 0x00 // Continuous conversion mode + MagSingle = 0x01 // Single conversion mode + MagSleep = 0x03 // Sleep mode + + MagMRDefault = MagContinuous // Continuous conversion is the default + + pollDelay = 250 // Delay before reading from mag. (ms) +) + +// HMC5883L represents a HMC5883L magnetometer. +type HMC5883L struct { + Bus embd.I2CBus + Poll int + + initialized bool + mu sync.RWMutex + + headings chan float64 + + quit chan struct{} +} + +// New creates a new HMC5883L interface. The bus variable controls +// the I2C bus used to communicate with the device. +func New(bus embd.I2CBus) *HMC5883L { + return &HMC5883L{Bus: bus, Poll: pollDelay} +} + +// Initialize the device +func (d *HMC5883L) setup() error { + d.mu.RLock() + if d.initialized { + d.mu.RUnlock() + return nil + } + d.mu.RUnlock() + + d.mu.Lock() + defer d.mu.Unlock() + + if err := d.Bus.WriteByteToReg(magAddress, magConfigRegA, MagCRADefault); err != nil { + return err + } + if err := d.Bus.WriteByteToReg(magAddress, magConfigRegB, MagCRBDefault); err != nil { + return err + } + if err := d.Bus.WriteByteToReg(magAddress, magModeReg, MagMRDefault); err != nil { + return err + } + + d.initialized = true + + return nil +} + +func (d *HMC5883L) measureHeading() (float64, error) { + if err := d.setup(); err != nil { + return 0, err + } + + data := make([]byte, 6) + if err := d.Bus.ReadFromReg(magAddress, magMSBx, data); err != nil { + return 0, err + } + + x := int16(data[0])<<8 | int16(data[1]) + y := int16(data[4])<<8 | int16(data[5]) + + heading := math.Atan2(float64(y), float64(x)) / math.Pi * 180 + if heading < 0 { + heading += 360 + } + + glog.V(3).Infof("Mag X=%v Y=%v HEAD=%v", x, y, heading) + return heading, nil +} + +// Heading returns the current heading [0, 360). +func (d *HMC5883L) Heading() (float64, error) { + select { + case heading := <-d.headings: + return heading, nil + default: + glog.V(2).Infof("lsm303: no headings available... measuring") + return d.measureHeading() + } +} + +// Run starts the sensor data acquisition loop. +func (d *HMC5883L) Run() 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 nil +} + +// Close the sensor data acquisition loop and put the HMC5883L into sleep mode. +func (d *HMC5883L) Close() error { + if d.quit != nil { + d.quit <- struct{}{} + } + return d.Bus.WriteByteToReg(magAddress, magModeReg, MagSleep) +} From 23350cb1dc7b12509bb63e13b3ab3ead7d73cddf Mon Sep 17 00:00:00 2001 From: Deepak Guru Date: Sun, 30 Apr 2017 20:49:38 -0700 Subject: [PATCH 3/5] Added support for hc-sr501 PIR sensor --- samples/hcsr501.go | 41 +++++++++++++++++++++ sensor/hcsr501/hcsr501.go | 77 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 samples/hcsr501.go create mode 100644 sensor/hcsr501/hcsr501.go diff --git a/samples/hcsr501.go b/samples/hcsr501.go new file mode 100644 index 0000000..89e0d00 --- /dev/null +++ b/samples/hcsr501.go @@ -0,0 +1,41 @@ +// +build ignore + +package main + +import ( + "flag" + "log" + "time" + + "github.com/kidoman/embd" + + _ "github.com/kidoman/embd/host/chip" + "github.com/kidoman/embd/sensor/hcsr501" +) + +func main() { + flag.Parse() + + if err := embd.InitGPIO(); err != nil { + panic(err) + } + defer embd.CloseGPIO() + + trig, err := embd.NewDigitalPin(132) + if err != nil { + panic(err) + } + defer trig.Close() + + pir := hcsr501.New(trig) + + for { + time.Sleep(3 * time.Second) + p, err := pir.Detect() + if err != nil { + log.Printf("error %v", err) + continue + } + log.Printf("PIR Detect %v", p) + } +} diff --git a/sensor/hcsr501/hcsr501.go b/sensor/hcsr501/hcsr501.go new file mode 100644 index 0000000..4da90ac --- /dev/null +++ b/sensor/hcsr501/hcsr501.go @@ -0,0 +1,77 @@ +// Package hcsr501 allows interfacing with the HC-SR501 PIR Sensor. +package hcsr501 + +import ( + "errors" + "sync" + "time" + + "github.com/kidoman/embd" +) + +// HCSR501 represents a HCSR501 ultrasonic range finder. +type HCSR501 struct { + TriggerPin embd.DigitalPin + initialized bool + ready bool // Allows the sensor time to settle in. + mu sync.RWMutex +} + +// New creates a new HCSR501 interface. +func New(triggerPin embd.DigitalPin) *HCSR501 { + return &HCSR501{TriggerPin: triggerPin} +} + +// setup initializes the GPIO and sensor. +func (d *HCSR501) setup() error { + d.mu.RLock() + if d.initialized { + d.mu.RUnlock() + return nil + } + d.mu.RUnlock() + + d.mu.Lock() + defer d.mu.Unlock() + + if err := d.TriggerPin.SetDirection(embd.In); err != nil { + return err + } + + // Wait 60 sec for sensor to settle down. + time.AfterFunc(60*time.Second, func() { + d.ready = true + }) + d.initialized = true + + return nil +} + +// Detect returns true if motion was detected. +func (d *HCSR501) Detect() (bool, error) { + if err := d.setup(); err != nil { + return false, err + } + + if !d.ready { + return false, errors.New("Sensor not ready") + } + + // Check 3 times to be sure. + for i := 0; i < 3; i++ { + v, err := d.TriggerPin.Read() + if err != nil { + return false, err + } + if v == embd.Low { + return false, nil + } + time.Sleep(10 * time.Millisecond) + } + return true, nil +} + +// Close. +func (d *HCSR501) Close() error { + return d.TriggerPin.SetDirection(embd.Out) +} From 26b22b6bffcd2080ef94b98ee40e3c25d9e787ef Mon Sep 17 00:00:00 2001 From: Deepak Guruswamy Date: Sat, 22 Jul 2017 01:41:43 -0700 Subject: [PATCH 4/5] Updated x,y offsets for hmc3558L compass --- sensor/hmc5883l/hmc5883l.go | 64 ++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/sensor/hmc5883l/hmc5883l.go b/sensor/hmc5883l/hmc5883l.go index 58fedc3..ed470c7 100644 --- a/sensor/hmc5883l/hmc5883l.go +++ b/sensor/hmc5883l/hmc5883l.go @@ -52,17 +52,22 @@ const ( pollDelay = 250 // Delay before reading from mag. (ms) ) +type calib struct { + minX int16 + maxX int16 + minY int16 + maxY int16 +} + // HMC5883L represents a HMC5883L magnetometer. type HMC5883L struct { - Bus embd.I2CBus - Poll int - + Bus embd.I2CBus + Poll int initialized bool mu sync.RWMutex - - headings chan float64 - - quit chan struct{} + headings chan float64 + quit chan struct{} + calibData calib } // New creates a new HMC5883L interface. The bus variable controls @@ -109,15 +114,50 @@ func (d *HMC5883L) measureHeading() (float64, error) { } x := int16(data[0])<<8 | int16(data[1]) + z := int16(data[2])<<8 | int16(data[3]) y := int16(data[4])<<8 | int16(data[5]) - heading := math.Atan2(float64(y), float64(x)) / math.Pi * 180 - if heading < 0 { - heading += 360 + /*Note on Calibration: + In order to compensate for tilt of compass, it has to be calibrated. To calibrate + rotate the compass a full 360'. Then calculate the X and Y offsets as + Xoffset = (minX + maxX)/2 ; Yoffset = (minY +maxY)/2 + when reading the raw values update them by offset + Xadj = Xraw - Xoffset + Yadj = Yraw - Yoffset + */ + if x < d.calibData.minX { + d.calibData.minX = x } - glog.V(3).Infof("Mag X=%v Y=%v HEAD=%v", x, y, heading) - return heading, nil + if x > d.calibData.maxX { + d.calibData.maxX = x + } + + if y < d.calibData.minY { + d.calibData.minY = y + } + + if y > d.calibData.maxY { + d.calibData.maxY = y + } + + x -= 274 + y -= 56 + + heading := math.Atan2(float64(y), float64(x)) + heading += 233.9 / 1000 + if heading < 0 { + heading += 2 * math.Pi + } + + if heading > 2*math.Pi { + heading -= 2 * math.Pi + } + + head := heading * 180 / math.Pi + + glog.V(3).Infof("Mag X=%v Y=%v Z=%v HEAD=%v CalibData=%v", x, y, z, head, d.calibData) + return head, nil } // Heading returns the current heading [0, 360). From 0d0e32744432a4b27135e8777bcbc3d5fd14ae80 Mon Sep 17 00:00:00 2001 From: Deepak Guruswamy Date: Sat, 22 Jul 2017 01:42:58 -0700 Subject: [PATCH 5/5] Updated x,y offsets for hmc3558L compass --- sensor/hmc5883l/hmc5883l.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensor/hmc5883l/hmc5883l.go b/sensor/hmc5883l/hmc5883l.go index ed470c7..7106f21 100644 --- a/sensor/hmc5883l/hmc5883l.go +++ b/sensor/hmc5883l/hmc5883l.go @@ -166,7 +166,7 @@ func (d *HMC5883L) Heading() (float64, error) { case heading := <-d.headings: return heading, nil default: - glog.V(2).Infof("lsm303: no headings available... measuring") + glog.V(3).Infof("lsm303: no headings available... measuring") return d.measureHeading() } }