1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-29 13:49:14 +08:00
hybridgroup.gobot/drivers/gpio/pir_motion_driver.go
2023-11-15 20:51:52 +01:00

115 lines
2.5 KiB
Go

package gpio
import (
"time"
"gobot.io/x/gobot/v2"
)
// PIRMotionDriver represents a digital Proximity Infra Red (PIR) motion detecter
type PIRMotionDriver struct {
*Driver
gobot.Eventer
pin string
active bool
halt chan bool
interval time.Duration
}
// NewPIRMotionDriver returns a new PIRMotionDriver with a polling interval of
// 10 Milliseconds given a DigitalReader and pin.
//
// Optionally accepts:
//
// time.Duration: Interval at which the PIRMotionDriver is polled for new information
func NewPIRMotionDriver(a DigitalReader, pin string, v ...time.Duration) *PIRMotionDriver {
//nolint:forcetypeassert // no error return value, so there is no better way
d := &PIRMotionDriver{
Driver: NewDriver(a.(gobot.Connection), "PIRMotion"),
Eventer: gobot.NewEventer(),
pin: pin,
active: false,
interval: 10 * time.Millisecond,
halt: make(chan bool),
}
d.afterStart = d.initialize
d.beforeHalt = d.shutdown
if len(v) > 0 {
d.interval = v[0]
}
d.AddEvent(MotionDetected)
d.AddEvent(MotionStopped)
d.AddEvent(Error)
return d
}
// Pin returns the PIRMotionDriver pin
func (d *PIRMotionDriver) Pin() string { return d.pin }
// Active gets the current state
func (d *PIRMotionDriver) Active() bool {
// ensure that read and write can not interfere
d.mutex.Lock()
defer d.mutex.Unlock()
return d.active
}
// initialize the PIRMotionDriver and polls the state of the sensor at the given interval.
//
// Emits the Events:
//
// MotionDetected - On motion detected
// MotionStopped int - On motion stopped
// Error error - On button error
//
// The PIRMotionDriver will send the MotionDetected event over and over,
// just as long as motion is still being detected.
// It will only send the MotionStopped event once, however, until
// motion starts being detected again
func (d *PIRMotionDriver) initialize() error {
go func() {
for {
newValue, err := d.connection.(DigitalReader).DigitalRead(d.Pin())
if err != nil {
d.Publish(Error, err)
}
d.update(newValue)
select {
case <-time.After(d.interval):
case <-d.halt:
return
}
}
}()
return nil
}
// shutdown stops polling
func (d *PIRMotionDriver) shutdown() error {
d.halt <- true
return nil
}
func (d *PIRMotionDriver) update(newValue int) {
// ensure that read and write can not interfere
d.mutex.Lock()
defer d.mutex.Unlock()
switch newValue {
case 1:
if !d.active {
d.active = true
d.Publish(MotionDetected, newValue)
}
case 0:
if d.active {
d.active = false
d.Publish(MotionStopped, newValue)
}
}
}