1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-05-01 13:48:57 +08:00
hybridgroup.gobot/drivers/i2c/yl40_driver.go

325 lines
9.2 KiB
Go

package i2c
import (
"fmt"
"log"
"strings"
"time"
"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/aio"
)
// All address pins are connected to ground.
const yl40DefaultAddress = 0x48
const yl40Debug = false
type YL40Pin string
const (
// brightness sensor, high brightness - low raw value, scaled to 0..1000 (high brightness - high value)
YL40Bri YL40Pin = "brightness"
// temperature sensor, high temperature - low raw value, scaled to °C
YL40Temp YL40Pin = "temperature"
// wired to AOUT, scaled to voltage 3.3V
YL40AIN2 YL40Pin = "analog input AIN2"
// adjustable resistor, turn clockwise will lower the raw value, scaled to -100..+100% (clockwise)
YL40Poti YL40Pin = "potentiometer"
YL40AOUT YL40Pin = "analog output"
)
const (
// the LED light is visible above ~1.7V
yl40LedDefaultVal = 1.7
// default refresh rate, set to zero (cyclic reading deactivated)
yl40DefaultRefresh = 0
)
type yl40Sensor struct {
interval time.Duration
scaler func(input int) (value float64)
}
type yl40Config struct {
sensors map[YL40Pin]*yl40Sensor
aOutScaler func(input float64) (value int)
}
var yl40Pins = map[YL40Pin]string{
YL40Bri: "s.0",
YL40Temp: "s.1",
YL40AIN2: "s.2",
YL40Poti: "s.3",
YL40AOUT: "aOut.0",
}
// YL40Driver is a Gobot i2c bus driver for the YL-40 module with light dependent resistor (LDR), thermistor (NTC)
// and an potentiometer, one additional analog input and one analog output with an connected LED.
// The module is based on PCF8591 with 4xADC, 1xDAC. For detailed documentation refer to PCF8591Driver.
//
// All values are linear scaled to 3.3V by default. This can be changed, see example "tinkerboard_yl40.go".
//
// This driver was tested with Tinkerboard and this board with temperature & brightness sensor:
// https://www.makershop.de/download/YL_40_yl40.pdf
//
type YL40Driver struct {
*PCF8591Driver
conf yl40Config
aBri *aio.AnalogSensorDriver
aTemp *aio.TemperatureSensorDriver
aAIN2 *aio.AnalogSensorDriver
aPoti *aio.AnalogSensorDriver
aOut *aio.AnalogActuatorDriver
}
// NewYL40Driver creates a new driver with specified i2c interface
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional parameters:
// refer to PCF8591Driver for i2c specific options
// refer to TemperatureSensorDriver for temperature sensor specific options
// refer to AnalogSensorDriver for analog input specific options
// refer to AnalogActuatorDriver for analog output specific options
//
func NewYL40Driver(a Connector, options ...func(Config)) *YL40Driver {
options = append(options, WithAddress(yl40DefaultAddress))
pcf := NewPCF8591Driver(a, options...)
ntc := aio.TemperatureSensorNtcConf{TC0: 25, R0: 10000.0, B: 3950} //Ohm, R25=10k, B=3950
defTempScaler := aio.TemperatureSensorNtcScaler(255, 1000, true, ntc)
defConf := yl40Config{
sensors: map[YL40Pin]*yl40Sensor{
YL40Bri: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 1000, 0),
},
YL40Temp: {
interval: yl40DefaultRefresh,
scaler: defTempScaler,
},
YL40AIN2: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 0, 3.3),
},
YL40Poti: {
interval: yl40DefaultRefresh,
scaler: aio.AnalogSensorLinearScaler(0, 255, 100, -100),
},
},
aOutScaler: aio.AnalogActuatorLinearScaler(0, 3.3, 0, 255),
}
y := &YL40Driver{
PCF8591Driver: pcf,
conf: defConf,
}
y.SetName(gobot.DefaultName("YL-40"))
for _, option := range options {
option(y)
}
// initialize analog drivers
y.aBri = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Bri], y.conf.sensors[YL40Bri].interval)
y.aTemp = aio.NewTemperatureSensorDriver(pcf, yl40Pins[YL40Temp], y.conf.sensors[YL40Temp].interval)
y.aAIN2 = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40AIN2], y.conf.sensors[YL40AIN2].interval)
y.aPoti = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Poti], y.conf.sensors[YL40Poti].interval)
y.aOut = aio.NewAnalogActuatorDriver(pcf, yl40Pins[YL40AOUT])
// set input scalers
y.aBri.SetScaler(y.conf.sensors[YL40Bri].scaler)
y.aTemp.SetScaler(y.conf.sensors[YL40Temp].scaler)
y.aAIN2.SetScaler(y.conf.sensors[YL40AIN2].scaler)
y.aPoti.SetScaler(y.conf.sensors[YL40Poti].scaler)
// set output scaler
y.aOut.SetScaler(y.conf.aOutScaler)
return y
}
// WithYL40Interval option sets the interval for refresh of given pin in YL40 driver
func WithYL40Interval(pin YL40Pin, val time.Duration) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
if sensor, ok := y.conf.sensors[pin]; ok {
sensor.interval = val
}
} else if yl40Debug {
log.Printf("trying to set interval for '%s' refresh for non-YL40Driver %v", pin, c)
}
}
}
// WithYL40InputScaler option sets the input scaler of given input pin in YL40 driver
func WithYL40InputScaler(pin YL40Pin, scaler func(input int) (value float64)) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
if sensor, ok := y.conf.sensors[pin]; ok {
sensor.scaler = scaler
}
} else if yl40Debug {
log.Printf("trying to set input scaler for '%s' for non-YL40Driver %v", pin, c)
}
}
}
// WithYL40OutputScaler option sets the output scaler in YL40 driver
func WithYL40OutputScaler(scaler func(input float64) (value int)) func(Config) {
return func(c Config) {
y, ok := c.(*YL40Driver)
if ok {
y.conf.aOutScaler = scaler
} else if yl40Debug {
log.Printf("trying to set output scaler for '%s' for non-YL40Driver %v", YL40AOUT, c)
}
}
}
// Start initializes the driver
func (y *YL40Driver) Start() (err error) {
// must be the first one
if err := y.PCF8591Driver.Start(); err != nil {
return err
}
if err := y.aBri.Start(); err != nil {
return err
}
if err := y.aTemp.Start(); err != nil {
return err
}
if err := y.aAIN2.Start(); err != nil {
return err
}
if err := y.aPoti.Start(); err != nil {
return err
}
if err := y.aOut.Start(); err != nil {
return err
}
return y.Write(yl40LedDefaultVal)
}
// Halt stops the driver
func (y *YL40Driver) Halt() (err error) {
// we try halt on each device, not stopping on the first error
var errors []string
if err := y.aBri.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aTemp.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aAIN2.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aPoti.Halt(); err != nil {
errors = append(errors, err.Error())
}
if err := y.aOut.Halt(); err != nil {
errors = append(errors, err.Error())
}
// must be the last one
if err := y.PCF8591Driver.Halt(); err != nil {
errors = append(errors, err.Error())
}
if len(errors) > 0 {
return fmt.Errorf("Halt the driver %s", strings.Join(errors, ", "))
}
return nil
}
// Read returns the current reading from the given pin of the driver
// For the analog output pin the last written value is returned
func (y *YL40Driver) Read(pin YL40Pin) (val float64, err error) {
switch pin {
case YL40Bri:
return y.aBri.ReadValue()
case YL40Temp:
return y.aTemp.ReadValue()
case YL40AIN2:
return y.aAIN2.ReadValue()
case YL40Poti:
return y.aPoti.ReadValue()
case YL40AOUT:
return y.aOut.Value(), nil
default:
return 0, fmt.Errorf("Analog reading from pin '%s' not supported", pin)
}
}
// ReadBrightness returns the current reading from brightness pin of the driver
func (y *YL40Driver) ReadBrightness() (val float64, err error) {
return y.Read(YL40Bri)
}
// ReadTemperature returns the current reading from temperature pin of the driver
func (y *YL40Driver) ReadTemperature() (val float64, err error) {
return y.Read(YL40Temp)
}
// ReadAIN2 returns the current reading from analog input pin 2 pin of the driver
func (y *YL40Driver) ReadAIN2() (val float64, err error) {
return y.Read(YL40AIN2)
}
// ReadPotentiometer returns the current reading from potentiometer pin of the driver
func (y *YL40Driver) ReadPotentiometer() (val float64, err error) {
return y.Read(YL40Poti)
}
// Value returns the last read or written value from the given pin of the driver
func (y *YL40Driver) Value(pin YL40Pin) (val float64, err error) {
switch pin {
case YL40Bri:
return y.aBri.Value(), nil
case YL40Temp:
return y.aTemp.Value(), nil
case YL40AIN2:
return y.aAIN2.Value(), nil
case YL40Poti:
return y.aPoti.Value(), nil
case YL40AOUT:
return y.aOut.Value(), nil
default:
return 0, fmt.Errorf("Get analog value from pin '%s' not supported", pin)
}
}
// Brightness returns the last read brightness of the driver
func (y *YL40Driver) Brightness() (val float64, err error) {
return y.Value(YL40Bri)
}
// Temperature returns the last read temperature of the driver
func (y *YL40Driver) Temperature() (val float64, err error) {
return y.Value(YL40Temp)
}
// AIN2 returns the last read analog input value of the driver
func (y *YL40Driver) AIN2() (val float64, err error) {
return y.Value(YL40AIN2)
}
// Potentiometer returns the last read potentiometer value of the driver
func (y *YL40Driver) Potentiometer() (val float64, err error) {
return y.Value(YL40Poti)
}
// AOUT returns the last written value of the driver
func (y *YL40Driver) AOUT() (val float64, err error) {
return y.Value(YL40AOUT)
}
// Write writes the given value to the analog output
func (y *YL40Driver) Write(val float64) (err error) {
return y.aOut.Write(val)
}