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 }