gobot.io/x/gobot@v1.16.0/drivers/spi/ssd1306_driver.go (about) 1 package spi 2 3 import ( 4 "errors" 5 "fmt" 6 "image" 7 "time" 8 9 "gobot.io/x/gobot" 10 "gobot.io/x/gobot/drivers/gpio" 11 ) 12 13 const ( 14 // default values 15 ssd1306Width = 128 16 ssd1306Height = 64 17 ssd1306DcPin = "16" // for raspberry pi 18 ssd1306RstPin = "18" // for raspberry pi 19 ssd1306ExternalVcc = false 20 ssd1306SetStartLine = 0x40 21 // fundamental commands 22 ssd1306SetContrast = 0x81 23 ssd1306DisplayOnResumeToRAM = 0xA4 24 ssd1306DisplayOnResume = 0xA5 25 ssd1306SetDisplayNormal = 0xA6 26 ssd1306SetDisplayInverse = 0xA7 27 ssd1306SetDisplayOff = 0xAE 28 ssd1306SetDisplayOn = 0xAF 29 // scrolling commands 30 ssd1306RightHorizontalScroll = 0x26 31 ssd1306LeftHorizontalScroll = 0x27 32 ssd1306VerticalAndRightHorizontalScroll = 0x29 33 ssd1306VerticalAndLeftHorizontalScroll = 0x2A 34 ssd1306DeactivateScroll = 0x2E 35 ssd1306ActivateScroll = 0x2F 36 ssd1306SetVerticalScrollArea = 0xA3 37 // addressing settings commands 38 ssd1306SetMemoryAddressingMode = 0x20 39 ssd1306ColumnAddr = 0x21 40 ssd1306PageAddr = 0x22 41 // hardware configuration commands 42 ssd1306SetSegmentRemap0 = 0xA0 43 ssd1306SetSegmentRemap127 = 0xA1 44 ssd1306SetMultiplexRatio = 0xA8 45 ssd1306ComScanInc = 0xC0 46 ssd1306ComScanDec = 0xC8 47 ssd1306SetDisplayOffset = 0xD3 48 ssd1306SetComPins = 0xDA 49 // timing and driving scheme commands 50 ssd1306SetDisplayClock = 0xD5 51 ssd1306SetPrechargePeriod = 0xD9 52 ssd1306SetVComDeselectLevel = 0xDB 53 ssd1306NOOP = 0xE3 54 // charge pump command 55 ssd1306ChargePumpSetting = 0x8D 56 ) 57 58 // ssd1306Init configures the ssd1306 based on the options passed in when the driver was created 59 func (s *SSD1306Driver) ssd1306Init() { 60 s.command(ssd1306SetDisplayOff) 61 s.command(ssd1306SetDisplayClock) 62 if s.DisplayHeight == 16 { 63 s.command(0x60) 64 } else { 65 s.command(0x80) 66 } 67 s.command(ssd1306SetMultiplexRatio) 68 s.command(uint8(s.DisplayHeight) - 1) 69 s.command(ssd1306SetDisplayOffset) 70 s.command(0x0) 71 s.command(ssd1306SetStartLine) 72 s.command(0x0) 73 s.command(ssd1306ChargePumpSetting) 74 if s.ExternalVcc { 75 s.command(0x10) 76 } else { 77 s.command(0x14) 78 } 79 s.command(ssd1306SetMemoryAddressingMode) 80 s.command(0x00) 81 s.command(ssd1306SetSegmentRemap0) 82 s.command(0x01) 83 s.command(ssd1306ComScanInc) 84 s.command(ssd1306SetComPins) 85 if s.DisplayHeight == 64 { 86 s.command(0x12) 87 } else { 88 s.command(0x02) 89 } 90 s.command(ssd1306SetContrast) 91 if s.DisplayHeight == 64 { 92 if s.ExternalVcc { 93 s.command(0x9F) 94 } else { 95 s.command(0xCF) 96 } 97 } else { 98 s.command(0x8F) 99 } 100 s.command(ssd1306SetPrechargePeriod) 101 if s.ExternalVcc { 102 s.command(0x22) 103 } else { 104 s.command(0xF1) 105 } 106 s.command(ssd1306SetVComDeselectLevel) 107 s.command(0x40) 108 s.command(ssd1306DisplayOnResumeToRAM) 109 s.command(ssd1306SetDisplayNormal) 110 s.command(ssd1306DeactivateScroll) 111 s.command(ssd1306SetDisplayOn) 112 } 113 114 // DisplayBuffer represents the display buffer intermediate memory 115 type DisplayBuffer struct { 116 width, height, pageSize int 117 buffer []byte 118 } 119 120 // NewDisplayBuffer creates a new DisplayBuffer 121 func NewDisplayBuffer(width, height, pageSize int) *DisplayBuffer { 122 s := &DisplayBuffer{ 123 width: width, 124 height: height, 125 pageSize: pageSize, 126 } 127 s.buffer = make([]byte, s.Size()) 128 return s 129 } 130 131 // Size returns the memory size of the display buffer 132 func (d *DisplayBuffer) Size() int { 133 return (d.width * d.height) / d.pageSize 134 } 135 136 // Clear the contents of the display buffer 137 func (d *DisplayBuffer) Clear() { 138 d.buffer = make([]byte, d.Size()) 139 } 140 141 // SetPixel sets the x, y pixel with c color 142 func (d *DisplayBuffer) SetPixel(x, y, c int) { 143 idx := x + (y/d.pageSize)*d.width 144 bit := uint(y) % uint(d.pageSize) 145 if c == 0 { 146 d.buffer[idx] &= ^(1 << bit) 147 } else { 148 d.buffer[idx] |= (1 << bit) 149 } 150 } 151 152 // Set sets the display buffer with the given buffer 153 func (d *DisplayBuffer) Set(buf []byte) { 154 d.buffer = buf 155 } 156 157 // SSD1306Driver is a Gobot Driver for a SSD1306 Display 158 type SSD1306Driver struct { 159 name string 160 connector Connector 161 connection Connection 162 dcDriver *gpio.DirectPinDriver 163 rstDriver *gpio.DirectPinDriver 164 pageSize int 165 DisplayWidth int 166 DisplayHeight int 167 DCPin string 168 RSTPin string 169 ExternalVcc bool 170 buffer *DisplayBuffer 171 Config 172 gobot.Commander 173 } 174 175 // NewSSD1306Driver creates a new SSD1306Driver. 176 // 177 // Params: 178 // conn Connector - the Adaptor to use with this Driver 179 // 180 // Optional params: 181 // spi.WithBus(int): bus to use with this driver 182 // spi.WithChip(int): chip to use with this driver 183 // spi.WithMode(int): mode to use with this driver 184 // spi.WithBits(int): number of bits to use with this driver 185 // spi.WithSpeed(int64): speed in Hz to use with this driver 186 // spi.WithDisplayWidth(int): width of display (defaults to 128) 187 // spi.WithDisplayHeight(int): height of display (defaults to 64) 188 // spi.WithDCPin(string): gpio pin number connected to dc pin on display (defaults to "16") 189 // spi.WithRstPin(string): gpio pin number connected to rst pin on display (defaults to "18") 190 // spi.WithExternalVCC(bool): set to true if using external vcc (defaults to false) 191 // 192 func NewSSD1306Driver(a gobot.Adaptor, options ...func(Config)) *SSD1306Driver { 193 // cast adaptor to spi connector since we also need the adaptor for gpio 194 b, ok := a.(Connector) 195 if !ok { 196 panic("unable to get gobot connector for ssd1306") 197 } 198 s := &SSD1306Driver{ 199 name: gobot.DefaultName("SSD1306"), 200 Commander: gobot.NewCommander(), 201 connector: b, 202 DisplayWidth: ssd1306Width, 203 DisplayHeight: ssd1306Height, 204 DCPin: ssd1306DcPin, 205 RSTPin: ssd1306RstPin, 206 ExternalVcc: ssd1306ExternalVcc, 207 Config: NewConfig(), 208 } 209 for _, option := range options { 210 option(s) 211 } 212 s.dcDriver = gpio.NewDirectPinDriver(a, s.DCPin) 213 s.rstDriver = gpio.NewDirectPinDriver(a, s.RSTPin) 214 s.pageSize = s.DisplayHeight / 8 215 s.buffer = NewDisplayBuffer(s.DisplayWidth, s.DisplayHeight, s.pageSize) 216 s.AddCommand("Display", func(params map[string]interface{}) interface{} { 217 err := s.Display() 218 return map[string]interface{}{"err": err} 219 }) 220 s.AddCommand("On", func(params map[string]interface{}) interface{} { 221 err := s.On() 222 return map[string]interface{}{"err": err} 223 }) 224 s.AddCommand("Off", func(params map[string]interface{}) interface{} { 225 err := s.Off() 226 return map[string]interface{}{"err": err} 227 }) 228 s.AddCommand("Clear", func(params map[string]interface{}) interface{} { 229 err := s.Clear() 230 return map[string]interface{}{"err": err} 231 }) 232 s.AddCommand("SetContrast", func(params map[string]interface{}) interface{} { 233 contrast := byte(params["contrast"].(byte)) 234 err := s.SetContrast(contrast) 235 return map[string]interface{}{"err": err} 236 }) 237 s.AddCommand("Set", func(params map[string]interface{}) interface{} { 238 x := int(params["x"].(int)) 239 y := int(params["y"].(int)) 240 c := int(params["c"].(int)) 241 s.Set(x, y, c) 242 return nil 243 }) 244 return s 245 } 246 247 // Name returns the Name for the Driver 248 func (s *SSD1306Driver) Name() string { return s.name } 249 250 // SetName sets the Name for the Driver 251 func (s *SSD1306Driver) SetName(n string) { s.name = n } 252 253 // Connection returns the connection for the Driver 254 func (s *SSD1306Driver) Connection() gobot.Connection { return s.connector.(gobot.Connection) } 255 256 // Start sets up the needed connection, and initialized the device. 257 func (s *SSD1306Driver) Start() (err error) { 258 bus := s.GetBusOrDefault(s.connector.GetSpiDefaultBus()) 259 chip := s.GetChipOrDefault(s.connector.GetSpiDefaultChip()) 260 mode := s.GetModeOrDefault(s.connector.GetSpiDefaultMode()) 261 bits := s.GetBitsOrDefault(s.connector.GetSpiDefaultBits()) 262 maxSpeed := s.GetSpeedOrDefault(s.connector.GetSpiDefaultMaxSpeed()) 263 264 s.connection, err = s.connector.GetSpiConnection(bus, chip, mode, bits, maxSpeed) 265 if err != nil { 266 return err 267 } 268 s.ssd1306Init() 269 return 270 } 271 272 // Halt returns true if device is halted successfully. 273 func (s *SSD1306Driver) Halt() (err error) { 274 s.Reset() 275 s.Off() 276 return nil 277 } 278 279 // WithDisplayWidth option sets the SSD1306Driver DisplayWidth option. 280 func WithDisplayWidth(val int) func(Config) { 281 return func(c Config) { 282 d, ok := c.(*SSD1306Driver) 283 if ok { 284 d.DisplayWidth = val 285 } else { 286 panic("unable to set display width for ssd1306") 287 } 288 } 289 } 290 291 // WithDisplayHeight option sets the SSD1306Driver DisplayHeight option. 292 func WithDisplayHeight(val int) func(Config) { 293 return func(c Config) { 294 d, ok := c.(*SSD1306Driver) 295 if ok { 296 d.DisplayHeight = val 297 } else { 298 panic("unable to set display height for ssd1306") 299 } 300 } 301 } 302 303 // WithDCPin option sets the SSD1306Driver DC Pin option. 304 func WithDCPin(val string) func(Config) { 305 return func(c Config) { 306 d, ok := c.(*SSD1306Driver) 307 if ok { 308 d.DCPin = val 309 } else { 310 panic("unable to set dc pin for ssd1306") 311 } 312 } 313 } 314 315 // WithRstPin option sets the SSD1306Driver RST pin option. 316 func WithRstPin(val string) func(Config) { 317 return func(c Config) { 318 d, ok := c.(*SSD1306Driver) 319 if ok { 320 d.RSTPin = val 321 } else { 322 panic("unable to set rst pin for ssd1306") 323 } 324 } 325 } 326 327 // WithExternalVCC option sets the SSD1306Driver external vcc option. 328 func WithExternalVCC(val bool) func(Config) { 329 return func(c Config) { 330 d, ok := c.(*SSD1306Driver) 331 if ok { 332 d.ExternalVcc = val 333 } else { 334 panic("unable to set rst pin for ssd1306") 335 } 336 } 337 } 338 339 // On turns on the display. 340 func (s *SSD1306Driver) On() (err error) { 341 return s.command(ssd1306SetDisplayOn) 342 } 343 344 // Off turns off the display. 345 func (s *SSD1306Driver) Off() (err error) { 346 return s.command(ssd1306SetDisplayOff) 347 } 348 349 // Clear clears the display buffer. 350 func (s *SSD1306Driver) Clear() (err error) { 351 s.buffer.Clear() 352 return nil 353 } 354 355 // Set sets a pixel in the display buffer. 356 func (s *SSD1306Driver) Set(x, y, c int) { 357 s.buffer.SetPixel(x, y, c) 358 } 359 360 // Reset re-initializes the device to a clean state. 361 func (s *SSD1306Driver) Reset() (err error) { 362 s.rstDriver.DigitalWrite(1) 363 time.Sleep(10 * time.Millisecond) 364 s.rstDriver.DigitalWrite(0) 365 time.Sleep(10 * time.Millisecond) 366 s.rstDriver.DigitalWrite(1) 367 return nil 368 } 369 370 // SetBufferAndDisplay sets the display buffer with the given buffer and displays the image. 371 func (s *SSD1306Driver) SetBufferAndDisplay(buf []byte) (err error) { 372 s.buffer.Set(buf) 373 return s.Display() 374 } 375 376 // SetContrast sets the display contrast (0-255). 377 func (s *SSD1306Driver) SetContrast(contrast byte) (err error) { 378 if contrast < 0 || contrast > 255 { 379 return fmt.Errorf("contrast value must be between 0-255") 380 } 381 if err = s.command(ssd1306SetContrast); err != nil { 382 return err 383 } 384 return s.command(contrast) 385 } 386 387 // Display sends the memory buffer to the display. 388 func (s *SSD1306Driver) Display() (err error) { 389 s.command(ssd1306ColumnAddr) 390 s.command(0) 391 s.command(uint8(s.DisplayWidth) - 1) 392 s.command(ssd1306PageAddr) 393 s.command(0) 394 s.command(uint8(s.pageSize) - 1) 395 if err = s.dcDriver.DigitalWrite(1); err != nil { 396 return err 397 } 398 return s.connection.Tx(append([]byte{0x40}, s.buffer.buffer...), nil) 399 } 400 401 // ShowImage takes a standard Go image and shows it on the display in monochrome. 402 func (s *SSD1306Driver) ShowImage(img image.Image) (err error) { 403 if img.Bounds().Dx() != s.DisplayWidth || img.Bounds().Dy() != s.DisplayHeight { 404 return errors.New("Image must match the display width and height") 405 } 406 407 s.Clear() 408 for y, w, h := 0, img.Bounds().Dx(), img.Bounds().Dy(); y < h; y++ { 409 for x := 0; x < w; x++ { 410 c := img.At(x, y) 411 if r, g, b, _ := c.RGBA(); r > 0 || g > 0 || b > 0 { 412 s.Set(x, y, 1) 413 } 414 } 415 } 416 return s.Display() 417 } 418 419 // command sends a unique command 420 func (s *SSD1306Driver) command(b byte) (err error) { 421 if err = s.dcDriver.DigitalWrite(0); err != nil { 422 return err 423 } 424 err = s.connection.Tx([]byte{b}, nil) 425 return err 426 }