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