gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/adafruit1109_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"gobot.io/x/gobot/v2"
    10  	"gobot.io/x/gobot/v2/drivers/gpio"
    11  )
    12  
    13  const adafruit1109Debug = false
    14  
    15  type adafruit1109PortPin struct {
    16  	port string
    17  	pin  uint8
    18  }
    19  
    20  // Adafruit1109Driver is a driver for the 2x16 LCD display with RGB backlit and 5 keys from adafruit, designed for Pi.
    21  // The display is driven by the HD44780, and all is connected by i2c port expander MCP23017.
    22  // https://www.adafruit.com/product/1109
    23  //
    24  // Have to implement DigitalWriter, DigitalReader interface
    25  type Adafruit1109Driver struct {
    26  	name string
    27  	*MCP23017Driver
    28  	redPin    adafruit1109PortPin
    29  	greenPin  adafruit1109PortPin
    30  	bluePin   adafruit1109PortPin
    31  	selectPin adafruit1109PortPin
    32  	upPin     adafruit1109PortPin
    33  	downPin   adafruit1109PortPin
    34  	leftPin   adafruit1109PortPin
    35  	rightPin  adafruit1109PortPin
    36  	rwPin     adafruit1109PortPin
    37  	rsPin     adafruit1109PortPin
    38  	enPin     adafruit1109PortPin
    39  	dataPinD4 adafruit1109PortPin
    40  	dataPinD5 adafruit1109PortPin
    41  	dataPinD6 adafruit1109PortPin
    42  	dataPinD7 adafruit1109PortPin
    43  	*gpio.HD44780Driver
    44  }
    45  
    46  // NewAdafruit1109Driver creates is a new driver for the 2x16 LCD display with RGB backlit and 5 keys.
    47  //
    48  // Because HD44780 and MCP23017 are already implemented in gobot, we creates a wrapper for using existing implementation.
    49  // So, for the documentation of the parameters, have a look at this drivers.
    50  //
    51  // Tests are done with a Tinkerboard.
    52  func NewAdafruit1109Driver(a Connector, options ...func(Config)) *Adafruit1109Driver {
    53  	options = append(options, WithMCP23017AutoIODirOff(1))
    54  	mcp := NewMCP23017Driver(a, options...)
    55  	d := &Adafruit1109Driver{
    56  		name:           gobot.DefaultName("Adafruit1109"),
    57  		MCP23017Driver: mcp,
    58  		redPin:         adafruit1109PortPin{"A", 6},
    59  		greenPin:       adafruit1109PortPin{"A", 7},
    60  		bluePin:        adafruit1109PortPin{"B", 0},
    61  		selectPin:      adafruit1109PortPin{"A", 0},
    62  		upPin:          adafruit1109PortPin{"A", 3},
    63  		downPin:        adafruit1109PortPin{"A", 2},
    64  		leftPin:        adafruit1109PortPin{"A", 4},
    65  		rightPin:       adafruit1109PortPin{"A", 1},
    66  		rwPin:          adafruit1109PortPin{"B", 6},
    67  		rsPin:          adafruit1109PortPin{"B", 7},
    68  		enPin:          adafruit1109PortPin{"B", 5},
    69  		dataPinD4:      adafruit1109PortPin{"B", 4},
    70  		dataPinD5:      adafruit1109PortPin{"B", 3},
    71  		dataPinD6:      adafruit1109PortPin{"B", 2},
    72  		dataPinD7:      adafruit1109PortPin{"B", 1},
    73  	}
    74  	// mapping for HD44780 to MCP23017 port and IO, 4-Bit data
    75  	dataPins := gpio.HD44780DataPin{
    76  		D4: d.dataPinD4.String(),
    77  		D5: d.dataPinD5.String(),
    78  		D6: d.dataPinD6.String(),
    79  		D7: d.dataPinD7.String(),
    80  	}
    81  
    82  	//rwPin := "B_6" not mapped in HD44780 driver
    83  	// at test initialization, there seems rows and columns be switched
    84  	// but inside the driver the row is used as row and col as column
    85  	rows := 2
    86  	columns := 16
    87  	lcd := gpio.NewHD44780Driver(d, columns, rows, gpio.HD44780_4BITMODE, d.rsPin.String(), d.enPin.String(), dataPins)
    88  	lcd.SetRWPin(d.rwPin.String())
    89  	d.HD44780Driver = lcd
    90  	return d
    91  }
    92  
    93  // Connect implements the adaptor.Connector interface.
    94  // Haven't found any adaptor which implements this with more content.
    95  func (d *Adafruit1109Driver) Connect() error { return nil }
    96  
    97  // Finalize implements the adaptor.Connector interface.
    98  // Haven't found any adaptor which implements this with more content.
    99  func (d *Adafruit1109Driver) Finalize() error { return nil }
   100  
   101  // Name implements the gobot.Device interface
   102  func (d *Adafruit1109Driver) Name() string {
   103  	return fmt.Sprintf("%s_%s_%s", d.name, d.MCP23017Driver.Name(), d.HD44780Driver.Name())
   104  }
   105  
   106  // SetName implements the gobot.Device interface.
   107  func (d *Adafruit1109Driver) SetName(n string) { d.name = n }
   108  
   109  // Connection implements the gobot.Device interface.
   110  func (d *Adafruit1109Driver) Connection() gobot.Connection { return d.MCP23017Driver.Connection() }
   111  
   112  // Start implements the gobot.Device interface.
   113  func (d *Adafruit1109Driver) Start() error {
   114  	if adafruit1109Debug {
   115  		log.Printf("## MCP.Start ##")
   116  	}
   117  	if err := d.MCP23017Driver.Start(); err != nil {
   118  		return err
   119  	}
   120  
   121  	// set all to output (inputs will be set by initButton)
   122  	for pin := uint8(0); pin <= 7; pin++ {
   123  		if err := d.SetPinMode(pin, "A", 0); err != nil {
   124  			return err
   125  		}
   126  		if err := d.SetPinMode(pin, "B", 0); err != nil {
   127  			return err
   128  		}
   129  	}
   130  
   131  	// button pins are inputs, has inverse logic and needs pull up
   132  	if err := d.adafruit1109InitButton(d.selectPin); err != nil {
   133  		return err
   134  	}
   135  	if err := d.adafruit1109InitButton(d.upPin); err != nil {
   136  		return err
   137  	}
   138  	if err := d.adafruit1109InitButton(d.downPin); err != nil {
   139  		return err
   140  	}
   141  	if err := d.adafruit1109InitButton(d.leftPin); err != nil {
   142  		return err
   143  	}
   144  	if err := d.adafruit1109InitButton(d.rightPin); err != nil {
   145  		return err
   146  	}
   147  
   148  	// lets start with neutral background
   149  	if err := d.SetRGB(true, true, true); err != nil {
   150  		return err
   151  	}
   152  	// set rw pin to write
   153  	if err := d.writePin(d.rwPin, 0x00); err != nil {
   154  		return err
   155  	}
   156  	if adafruit1109Debug {
   157  		log.Printf("## HD.Start ##")
   158  	}
   159  	return d.HD44780Driver.Start()
   160  }
   161  
   162  // Halt implements the gobot.Device interface.
   163  func (d *Adafruit1109Driver) Halt() error {
   164  	// we try halt on each device, not stopping on the first error
   165  	var errors []string
   166  
   167  	if err := d.HD44780Driver.Halt(); err != nil {
   168  		errors = append(errors, err.Error())
   169  	}
   170  	// switch off the background light
   171  	if err := d.SetRGB(false, false, false); err != nil {
   172  		errors = append(errors, err.Error())
   173  	}
   174  	// must be after HD44780Driver
   175  	if err := d.MCP23017Driver.Halt(); err != nil {
   176  		errors = append(errors, err.Error())
   177  	}
   178  
   179  	if len(errors) > 0 {
   180  		return fmt.Errorf("Halt the driver %s", strings.Join(errors, ", "))
   181  	}
   182  
   183  	return nil
   184  }
   185  
   186  // DigitalWrite implements the DigitalWriter interface
   187  // This is called by HD44780 driver to set one gpio output. We redirect the call to the i2c driver MCP23017.
   188  // The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
   189  func (d *Adafruit1109Driver) DigitalWrite(id string, val byte) error {
   190  	portio := adafruit1109ParseID(id)
   191  	return d.writePin(portio, val)
   192  }
   193  
   194  // DigitalRead implements the DigitalReader interface
   195  // This is called by HD44780 driver to read one gpio input. We redirect the call to the i2c driver MCP23017.
   196  // The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
   197  func (d *Adafruit1109Driver) DigitalRead(id string) (int, error) {
   198  	portio := adafruit1109ParseID(id)
   199  	uval, err := d.readPin(portio)
   200  	if err != nil {
   201  		return 0, err
   202  	}
   203  	return int(uval), err
   204  }
   205  
   206  // SetRGB sets the Red Green Blue value of backlit.
   207  // The MCP23017 variant don't support PWM and have inverted logic
   208  func (d *Adafruit1109Driver) SetRGB(r, g, b bool) error {
   209  	if adafruit1109Debug {
   210  		log.Printf("## SetRGB %t, %t, %t ##", r, g, b)
   211  	}
   212  	rio := d.redPin
   213  	gio := d.greenPin
   214  	bio := d.bluePin
   215  	rval := uint8(0x1)
   216  	gval := uint8(0x1)
   217  	bval := uint8(0x1)
   218  	if r {
   219  		rval = 0x00
   220  	}
   221  	if g {
   222  		gval = 0x00
   223  	}
   224  	if b {
   225  		bval = 0x00
   226  	}
   227  
   228  	if err := d.writePin(rio, rval); err != nil {
   229  		return err
   230  	}
   231  
   232  	if err := d.writePin(gio, gval); err != nil {
   233  		return err
   234  	}
   235  
   236  	if err := d.writePin(bio, bval); err != nil {
   237  		return err
   238  	}
   239  	return nil
   240  }
   241  
   242  // SelectButton reads the state of the "select" button (1=pressed).
   243  func (d *Adafruit1109Driver) SelectButton() (uint8, error) {
   244  	return d.readPin(d.selectPin)
   245  }
   246  
   247  // UpButton reads the state of the "up" button (1=pressed).
   248  func (d *Adafruit1109Driver) UpButton() (uint8, error) {
   249  	return d.readPin(d.upPin)
   250  }
   251  
   252  // DownButton reads the state of the "down" button (1=pressed).
   253  func (d *Adafruit1109Driver) DownButton() (uint8, error) {
   254  	return d.readPin(d.downPin)
   255  }
   256  
   257  // LeftButton reads the state of the "left" button (1=pressed).
   258  func (d *Adafruit1109Driver) LeftButton() (uint8, error) {
   259  	return d.readPin(d.leftPin)
   260  }
   261  
   262  // RightButton reads the state of the "right" button (1=pressed).
   263  func (d *Adafruit1109Driver) RightButton() (uint8, error) {
   264  	return d.readPin(d.rightPin)
   265  }
   266  
   267  func (d *Adafruit1109Driver) writePin(ap adafruit1109PortPin, val uint8) error {
   268  	return d.WriteGPIO(ap.pin, ap.port, val)
   269  }
   270  
   271  func (d *Adafruit1109Driver) readPin(ap adafruit1109PortPin) (uint8, error) {
   272  	return d.ReadGPIO(ap.pin, ap.port)
   273  }
   274  
   275  func (ap *adafruit1109PortPin) String() string {
   276  	return fmt.Sprintf("%s_%d", ap.port, ap.pin)
   277  }
   278  
   279  func adafruit1109ParseID(id string) adafruit1109PortPin {
   280  	items := strings.Split(id, "_")
   281  	io := uint8(0)
   282  	if io64, err := strconv.ParseUint(items[1], 10, 32); err == nil {
   283  		io = uint8(io64)
   284  	}
   285  	return adafruit1109PortPin{port: items[0], pin: io}
   286  }
   287  
   288  func (d *Adafruit1109Driver) adafruit1109InitButton(p adafruit1109PortPin) error {
   289  	// make an input
   290  	if err := d.SetPinMode(p.pin, p.port, 1); err != nil {
   291  		return err
   292  	}
   293  	// add pull up resistors
   294  	if err := d.SetPullUp(p.pin, p.port, 1); err != nil {
   295  		return err
   296  	}
   297  	// invert polarity
   298  	if err := d.SetGPIOPolarity(p.pin, p.port, 1); err != nil {
   299  		return err
   300  	}
   301  	return nil
   302  }