gobot.io/x/gobot@v1.16.0/drivers/i2c/yl40_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"gobot.io/x/gobot"
    10  	"gobot.io/x/gobot/drivers/aio"
    11  )
    12  
    13  // All address pins are connected to ground.
    14  const yl40DefaultAddress = 0x48
    15  
    16  const yl40Debug = false
    17  
    18  type YL40Pin string
    19  
    20  const (
    21  	// brightness sensor, high brightness - low raw value, scaled to 0..1000 (high brightness - high value)
    22  	YL40Bri YL40Pin = "brightness"
    23  	// temperature sensor, high temperature - low raw value, scaled to °C
    24  	YL40Temp YL40Pin = "temperature"
    25  	// wired to AOUT, scaled to voltage 3.3V
    26  	YL40AIN2 YL40Pin = "analog input AIN2"
    27  	// adjustable resistor, turn clockwise will lower the raw value, scaled to -100..+100% (clockwise)
    28  	YL40Poti YL40Pin = "potentiometer"
    29  	YL40AOUT YL40Pin = "analog output"
    30  )
    31  
    32  const (
    33  	// the LED light is visible above ~1.7V
    34  	yl40LedDefaultVal = 1.7
    35  	// default refresh rate, set to zero (cyclic reading deactivated)
    36  	yl40DefaultRefresh = 0
    37  )
    38  
    39  type yl40Sensor struct {
    40  	interval time.Duration
    41  	scaler   func(input int) (value float64)
    42  }
    43  
    44  type yl40Config struct {
    45  	sensors    map[YL40Pin]*yl40Sensor
    46  	aOutScaler func(input float64) (value int)
    47  }
    48  
    49  var yl40Pins = map[YL40Pin]string{
    50  	YL40Bri:  "s.0",
    51  	YL40Temp: "s.1",
    52  	YL40AIN2: "s.2",
    53  	YL40Poti: "s.3",
    54  	YL40AOUT: "aOut.0",
    55  }
    56  
    57  // YL40Driver is a Gobot i2c bus driver for the YL-40 module with light dependent resistor (LDR), thermistor (NTC)
    58  // and an potentiometer, one additional analog input and one analog output with an connected LED.
    59  // The module is based on PCF8591 with 4xADC, 1xDAC. For detailed documentation refer to PCF8591Driver.
    60  //
    61  // All values are linear scaled to 3.3V by default. This can be changed, see example "tinkerboard_yl40.go".
    62  //
    63  // This driver was tested with Tinkerboard and this board with temperature & brightness sensor:
    64  // https://www.makershop.de/download/YL_40_yl40.pdf
    65  //
    66  type YL40Driver struct {
    67  	*PCF8591Driver
    68  	conf yl40Config
    69  
    70  	aBri  *aio.AnalogSensorDriver
    71  	aTemp *aio.TemperatureSensorDriver
    72  	aAIN2 *aio.AnalogSensorDriver
    73  	aPoti *aio.AnalogSensorDriver
    74  	aOut  *aio.AnalogActuatorDriver
    75  }
    76  
    77  // NewYL40Driver creates a new driver with specified i2c interface
    78  // Params:
    79  //    conn Connector - the Adaptor to use with this Driver
    80  //
    81  // Optional parameters:
    82  //  refer to PCF8591Driver for i2c specific options
    83  // 	refer to TemperatureSensorDriver for temperature sensor specific options
    84  // 	refer to AnalogSensorDriver for analog input specific options
    85  //  refer to AnalogActuatorDriver for analog output specific options
    86  //
    87  func NewYL40Driver(a Connector, options ...func(Config)) *YL40Driver {
    88  	options = append(options, WithAddress(yl40DefaultAddress))
    89  	pcf := NewPCF8591Driver(a, options...)
    90  
    91  	ntc := aio.TemperatureSensorNtcConf{TC0: 25, R0: 10000.0, B: 3950} //Ohm, R25=10k, B=3950
    92  	defTempScaler := aio.TemperatureSensorNtcScaler(255, 1000, true, ntc)
    93  
    94  	defConf := yl40Config{
    95  		sensors: map[YL40Pin]*yl40Sensor{
    96  			YL40Bri: {
    97  				interval: yl40DefaultRefresh,
    98  				scaler:   aio.AnalogSensorLinearScaler(0, 255, 1000, 0),
    99  			},
   100  			YL40Temp: {
   101  				interval: yl40DefaultRefresh,
   102  				scaler:   defTempScaler,
   103  			},
   104  			YL40AIN2: {
   105  				interval: yl40DefaultRefresh,
   106  				scaler:   aio.AnalogSensorLinearScaler(0, 255, 0, 3.3),
   107  			},
   108  			YL40Poti: {
   109  				interval: yl40DefaultRefresh,
   110  				scaler:   aio.AnalogSensorLinearScaler(0, 255, 100, -100),
   111  			},
   112  		},
   113  		aOutScaler: aio.AnalogActuatorLinearScaler(0, 3.3, 0, 255),
   114  	}
   115  
   116  	y := &YL40Driver{
   117  		PCF8591Driver: pcf,
   118  		conf:          defConf,
   119  	}
   120  
   121  	y.SetName(gobot.DefaultName("YL-40"))
   122  
   123  	for _, option := range options {
   124  		option(y)
   125  	}
   126  
   127  	// initialize analog drivers
   128  	y.aBri = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Bri], y.conf.sensors[YL40Bri].interval)
   129  	y.aTemp = aio.NewTemperatureSensorDriver(pcf, yl40Pins[YL40Temp], y.conf.sensors[YL40Temp].interval)
   130  	y.aAIN2 = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40AIN2], y.conf.sensors[YL40AIN2].interval)
   131  	y.aPoti = aio.NewAnalogSensorDriver(pcf, yl40Pins[YL40Poti], y.conf.sensors[YL40Poti].interval)
   132  	y.aOut = aio.NewAnalogActuatorDriver(pcf, yl40Pins[YL40AOUT])
   133  
   134  	// set input scalers
   135  	y.aBri.SetScaler(y.conf.sensors[YL40Bri].scaler)
   136  	y.aTemp.SetScaler(y.conf.sensors[YL40Temp].scaler)
   137  	y.aAIN2.SetScaler(y.conf.sensors[YL40AIN2].scaler)
   138  	y.aPoti.SetScaler(y.conf.sensors[YL40Poti].scaler)
   139  
   140  	// set output scaler
   141  	y.aOut.SetScaler(y.conf.aOutScaler)
   142  
   143  	return y
   144  }
   145  
   146  // WithYL40Interval option sets the interval for refresh of given pin in YL40 driver
   147  func WithYL40Interval(pin YL40Pin, val time.Duration) func(Config) {
   148  	return func(c Config) {
   149  		y, ok := c.(*YL40Driver)
   150  		if ok {
   151  			if sensor, ok := y.conf.sensors[pin]; ok {
   152  				sensor.interval = val
   153  			}
   154  		} else if yl40Debug {
   155  			log.Printf("trying to set interval for '%s' refresh for non-YL40Driver %v", pin, c)
   156  		}
   157  	}
   158  }
   159  
   160  // WithYL40InputScaler option sets the input scaler of given input pin in YL40 driver
   161  func WithYL40InputScaler(pin YL40Pin, scaler func(input int) (value float64)) func(Config) {
   162  	return func(c Config) {
   163  		y, ok := c.(*YL40Driver)
   164  		if ok {
   165  			if sensor, ok := y.conf.sensors[pin]; ok {
   166  				sensor.scaler = scaler
   167  			}
   168  		} else if yl40Debug {
   169  			log.Printf("trying to set input scaler for '%s' for non-YL40Driver %v", pin, c)
   170  		}
   171  	}
   172  }
   173  
   174  // WithYL40OutputScaler option sets the output scaler in YL40 driver
   175  func WithYL40OutputScaler(scaler func(input float64) (value int)) func(Config) {
   176  	return func(c Config) {
   177  		y, ok := c.(*YL40Driver)
   178  		if ok {
   179  			y.conf.aOutScaler = scaler
   180  		} else if yl40Debug {
   181  			log.Printf("trying to set output scaler for '%s' for non-YL40Driver %v", YL40AOUT, c)
   182  		}
   183  	}
   184  }
   185  
   186  // Start initializes the driver
   187  func (y *YL40Driver) Start() (err error) {
   188  	// must be the first one
   189  	if err := y.PCF8591Driver.Start(); err != nil {
   190  		return err
   191  	}
   192  	if err := y.aBri.Start(); err != nil {
   193  		return err
   194  	}
   195  	if err := y.aTemp.Start(); err != nil {
   196  		return err
   197  	}
   198  	if err := y.aAIN2.Start(); err != nil {
   199  		return err
   200  	}
   201  	if err := y.aPoti.Start(); err != nil {
   202  		return err
   203  	}
   204  	if err := y.aOut.Start(); err != nil {
   205  		return err
   206  	}
   207  	return y.Write(yl40LedDefaultVal)
   208  }
   209  
   210  // Halt stops the driver
   211  func (y *YL40Driver) Halt() (err error) {
   212  	// we try halt on each device, not stopping on the first error
   213  	var errors []string
   214  	if err := y.aBri.Halt(); err != nil {
   215  		errors = append(errors, err.Error())
   216  	}
   217  	if err := y.aTemp.Halt(); err != nil {
   218  		errors = append(errors, err.Error())
   219  	}
   220  	if err := y.aAIN2.Halt(); err != nil {
   221  		errors = append(errors, err.Error())
   222  	}
   223  	if err := y.aPoti.Halt(); err != nil {
   224  		errors = append(errors, err.Error())
   225  	}
   226  	if err := y.aOut.Halt(); err != nil {
   227  		errors = append(errors, err.Error())
   228  	}
   229  	// must be the last one
   230  	if err := y.PCF8591Driver.Halt(); err != nil {
   231  		errors = append(errors, err.Error())
   232  	}
   233  	if len(errors) > 0 {
   234  		return fmt.Errorf("Halt the driver %s", strings.Join(errors, ", "))
   235  	}
   236  	return nil
   237  }
   238  
   239  // Read returns the current reading from the given pin of the driver
   240  // For the analog output pin the last written value is returned
   241  func (y *YL40Driver) Read(pin YL40Pin) (val float64, err error) {
   242  	switch pin {
   243  	case YL40Bri:
   244  		return y.aBri.ReadValue()
   245  	case YL40Temp:
   246  		return y.aTemp.ReadValue()
   247  	case YL40AIN2:
   248  		return y.aAIN2.ReadValue()
   249  	case YL40Poti:
   250  		return y.aPoti.ReadValue()
   251  	case YL40AOUT:
   252  		return y.aOut.Value(), nil
   253  	default:
   254  		return 0, fmt.Errorf("Analog reading from pin '%s' not supported", pin)
   255  	}
   256  }
   257  
   258  // ReadBrightness returns the current reading from brightness pin of the driver
   259  func (y *YL40Driver) ReadBrightness() (val float64, err error) {
   260  	return y.Read(YL40Bri)
   261  }
   262  
   263  // ReadTemperature returns the current reading from temperature pin of the driver
   264  func (y *YL40Driver) ReadTemperature() (val float64, err error) {
   265  	return y.Read(YL40Temp)
   266  }
   267  
   268  // ReadAIN2 returns the current reading from analog input pin 2 pin of the driver
   269  func (y *YL40Driver) ReadAIN2() (val float64, err error) {
   270  	return y.Read(YL40AIN2)
   271  }
   272  
   273  // ReadPotentiometer returns the current reading from potentiometer pin of the driver
   274  func (y *YL40Driver) ReadPotentiometer() (val float64, err error) {
   275  	return y.Read(YL40Poti)
   276  }
   277  
   278  // Value returns the last read or written value from the given pin of the driver
   279  func (y *YL40Driver) Value(pin YL40Pin) (val float64, err error) {
   280  	switch pin {
   281  	case YL40Bri:
   282  		return y.aBri.Value(), nil
   283  	case YL40Temp:
   284  		return y.aTemp.Value(), nil
   285  	case YL40AIN2:
   286  		return y.aAIN2.Value(), nil
   287  	case YL40Poti:
   288  		return y.aPoti.Value(), nil
   289  	case YL40AOUT:
   290  		return y.aOut.Value(), nil
   291  	default:
   292  		return 0, fmt.Errorf("Get analog value from pin '%s' not supported", pin)
   293  	}
   294  }
   295  
   296  // Brightness returns the last read brightness of the driver
   297  func (y *YL40Driver) Brightness() (val float64, err error) {
   298  	return y.Value(YL40Bri)
   299  }
   300  
   301  // Temperature returns the last read temperature of the driver
   302  func (y *YL40Driver) Temperature() (val float64, err error) {
   303  	return y.Value(YL40Temp)
   304  }
   305  
   306  // AIN2 returns the last read analog input value of the driver
   307  func (y *YL40Driver) AIN2() (val float64, err error) {
   308  	return y.Value(YL40AIN2)
   309  }
   310  
   311  // Potentiometer returns the last read potentiometer value of the driver
   312  func (y *YL40Driver) Potentiometer() (val float64, err error) {
   313  	return y.Value(YL40Poti)
   314  }
   315  
   316  // AOUT returns the last written value of the driver
   317  func (y *YL40Driver) AOUT() (val float64, err error) {
   318  	return y.Value(YL40AOUT)
   319  }
   320  
   321  // Write writes the given value to the analog output
   322  func (y *YL40Driver) Write(val float64) (err error) {
   323  	return y.aOut.Write(val)
   324  }