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