gobot.io/x/gobot@v1.16.0/drivers/i2c/ssd1306_driver.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "image" 6 7 "gobot.io/x/gobot" 8 ) 9 10 // register addresses for the ssd1306 11 const ( 12 // default values 13 ssd1306Width = 128 14 ssd1306Height = 64 15 ssd1306ExternalVCC = false 16 ssd1306SetStartLine = 0x40 17 ssd1306I2CAddress = 0x3c 18 // fundamental commands 19 ssd1306SetComOutput0 = 0xC0 20 ssd1306SetComOutput1 = 0xC1 21 ssd1306SetComOutput2 = 0xC2 22 ssd1306SetComOutput3 = 0xC3 23 ssd1306SetComOutput4 = 0xC4 24 ssd1306SetComOutput5 = 0xC5 25 ssd1306SetComOutput6 = 0xC6 26 ssd1306SetComOutput7 = 0xC7 27 ssd1306SetComOutput8 = 0xC8 28 ssd1306SetContrast = 0x81 29 // scrolling commands 30 ssd1306ContinuousHScrollRight = 0x26 31 ssd1306ContinuousHScrollLeft = 0x27 32 ssd1306ContinuousVHScrollRight = 0x29 33 ssd1306ContinuousVHScrollLeft = 0x2A 34 ssd1306StopScroll = 0x2E 35 ssd1306StartScroll = 0x2F 36 // adressing settings commands 37 ssd1306SetMemoryAddressingMode = 0x20 38 ssd1306ColumnAddr = 0x21 39 ssd1306PageAddr = 0x22 40 // hardware configuration commands 41 ssd1306SetSegmentRemap0 = 0xA0 42 ssd1306SetSegmentRemap127 = 0xA1 43 ssd1306DisplayOnResumeToRAM = 0xA4 44 ssd1306SetDisplayNormal = 0xA6 45 ssd1306SetDisplayInverse = 0xA7 46 ssd1306SetDisplayOff = 0xAE 47 ssd1306SetDisplayOn = 0xAF 48 // timing and driving scheme commands 49 ssd1306SetDisplayClock = 0xD5 50 ssd1306SetPrechargePeriod = 0xD9 51 ssd1306SetVComDeselectLevel = 0xDB 52 ssd1306SetMultiplexRatio = 0xA8 53 ssd1306SetComPins = 0xDA 54 ssd1306SetDisplayOffset = 0xD3 55 // charge pump command 56 ssd1306ChargePumpSetting = 0x8D 57 ) 58 59 // SSD1306Init contains the initialization settings for the ssd1306 display. 60 type SSD1306Init struct { 61 displayClock byte 62 multiplexRatio byte 63 displayOffset byte 64 startLine byte 65 chargePumpSetting byte 66 memoryAddressingMode byte 67 comPins byte 68 contrast byte 69 prechargePeriod byte 70 vComDeselectLevel byte 71 } 72 73 // GetSequence returns the initialization sequence for the ssd1306 display. 74 func (i *SSD1306Init) GetSequence() []byte { 75 return []byte{ 76 ssd1306SetDisplayNormal, 77 ssd1306SetDisplayOff, 78 ssd1306SetDisplayClock, i.displayClock, 79 ssd1306SetMultiplexRatio, i.multiplexRatio, 80 ssd1306SetDisplayOffset, i.displayOffset, 81 ssd1306SetStartLine | i.startLine, 82 ssd1306ChargePumpSetting, i.chargePumpSetting, 83 ssd1306SetMemoryAddressingMode, i.memoryAddressingMode, 84 ssd1306SetSegmentRemap0, 85 ssd1306SetComOutput0, 86 ssd1306SetComPins, i.comPins, 87 ssd1306SetContrast, i.contrast, 88 ssd1306SetPrechargePeriod, i.prechargePeriod, 89 ssd1306SetVComDeselectLevel, i.vComDeselectLevel, 90 ssd1306DisplayOnResumeToRAM, 91 ssd1306SetDisplayNormal, 92 } 93 } 94 95 // 128x64 init sequence 96 var ssd1306Init128x64 = &SSD1306Init{ 97 displayClock: 0x80, 98 multiplexRatio: 0x3F, 99 displayOffset: 0x00, 100 startLine: 0x00, 101 chargePumpSetting: 0x14, // 0x10 if external vcc is set 102 memoryAddressingMode: 0x00, 103 comPins: 0x12, 104 contrast: 0xCF, // 0x9F if external vcc is set 105 prechargePeriod: 0xF1, // 0x22 if external vcc is set 106 vComDeselectLevel: 0x40, 107 } 108 109 // 128x32 init sequence 110 var ssd1306Init128x32 = &SSD1306Init{ 111 displayClock: 0x80, 112 multiplexRatio: 0x1F, 113 displayOffset: 0x00, 114 startLine: 0x00, 115 chargePumpSetting: 0x14, // 0x10 if external vcc is set 116 memoryAddressingMode: 0x00, 117 comPins: 0x02, 118 contrast: 0x8F, // 0x9F if external vcc is set 119 prechargePeriod: 0xF1, // 0x22 if external vcc is set 120 vComDeselectLevel: 0x40, 121 } 122 123 // 96x16 init sequence 124 var ssd1306Init96x16 = &SSD1306Init{ 125 displayClock: 0x60, 126 multiplexRatio: 0x0F, 127 displayOffset: 0x00, 128 startLine: 0x00, 129 chargePumpSetting: 0x14, // 0x10 if external vcc is set 130 memoryAddressingMode: 0x00, 131 comPins: 0x02, 132 contrast: 0x8F, // 0x9F if external vcc is set 133 prechargePeriod: 0xF1, // 0x22 if external vcc is set 134 vComDeselectLevel: 0x40, 135 } 136 137 // DisplayBuffer represents the display buffer intermediate memory. 138 type DisplayBuffer struct { 139 width, height, pageSize int 140 buffer []byte 141 } 142 143 // NewDisplayBuffer creates a new DisplayBuffer. 144 func NewDisplayBuffer(width, height, pageSize int) *DisplayBuffer { 145 d := &DisplayBuffer{ 146 width: width, 147 height: height, 148 pageSize: pageSize, 149 } 150 d.buffer = make([]byte, d.Size()) 151 return d 152 } 153 154 // Size returns the memory size of the display buffer. 155 func (d *DisplayBuffer) Size() int { 156 return (d.width * d.height) / d.pageSize 157 } 158 159 // Clear the contents of the display buffer. 160 func (d *DisplayBuffer) Clear() { 161 d.buffer = make([]byte, d.Size()) 162 } 163 164 // SetPixel sets the x, y pixel with c color. 165 func (d *DisplayBuffer) SetPixel(x, y, c int) { 166 idx := x + (y/d.pageSize)*d.width 167 bit := uint(y) % uint(d.pageSize) 168 if c == 0 { 169 d.buffer[idx] &= ^(1 << bit) 170 } else { 171 d.buffer[idx] |= (1 << bit) 172 } 173 } 174 175 // Set sets the display buffer with the given buffer. 176 func (d *DisplayBuffer) Set(buf []byte) { 177 d.buffer = buf 178 } 179 180 // SSD1306Driver is a Gobot Driver for a SSD1306 Display. 181 type SSD1306Driver struct { 182 name string 183 connector Connector 184 connection Connection 185 Config 186 gobot.Commander 187 initSequence *SSD1306Init 188 displayWidth int 189 displayHeight int 190 externalVCC bool 191 pageSize int 192 buffer *DisplayBuffer 193 } 194 195 // NewSSD1306Driver creates a new SSD1306Driver. 196 // 197 // Params: 198 // conn Connector - the Adaptor to use with this Driver 199 // 200 // Optional params: 201 // WithBus(int): bus to use with this driver 202 // WithAddress(int): address to use with this driver 203 // WithSSD1306DisplayWidth(int): width of display (defaults to 128) 204 // WithSSD1306DisplayHeight(int): height of display (defaults to 64) 205 // WithSSD1306ExternalVCC: set true when using an external OLED supply (defaults to false) 206 // 207 func NewSSD1306Driver(a Connector, options ...func(Config)) *SSD1306Driver { 208 s := &SSD1306Driver{ 209 name: gobot.DefaultName("SSD1306"), 210 Commander: gobot.NewCommander(), 211 connector: a, 212 Config: NewConfig(), 213 displayHeight: ssd1306Height, 214 displayWidth: ssd1306Width, 215 externalVCC: ssd1306ExternalVCC, 216 } 217 // set options 218 for _, option := range options { 219 option(s) 220 } 221 // set page size 222 s.pageSize = 8 223 // set display buffer 224 s.buffer = NewDisplayBuffer(s.displayWidth, s.displayHeight, s.pageSize) 225 // add commands 226 s.AddCommand("Display", func(params map[string]interface{}) interface{} { 227 err := s.Display() 228 return map[string]interface{}{"err": err} 229 }) 230 s.AddCommand("On", func(params map[string]interface{}) interface{} { 231 err := s.On() 232 return map[string]interface{}{"err": err} 233 }) 234 s.AddCommand("Off", func(params map[string]interface{}) interface{} { 235 err := s.Off() 236 return map[string]interface{}{"err": err} 237 }) 238 s.AddCommand("Clear", func(params map[string]interface{}) interface{} { 239 s.Clear() 240 return map[string]interface{}{} 241 }) 242 s.AddCommand("SetContrast", func(params map[string]interface{}) interface{} { 243 contrast := byte(params["contrast"].(byte)) 244 err := s.SetContrast(contrast) 245 return map[string]interface{}{"err": err} 246 }) 247 s.AddCommand("Set", func(params map[string]interface{}) interface{} { 248 x := int(params["x"].(int)) 249 y := int(params["y"].(int)) 250 c := int(params["c"].(int)) 251 s.Set(x, y, c) 252 return nil 253 }) 254 return s 255 } 256 257 // Name returns the Name for the Driver. 258 func (s *SSD1306Driver) Name() string { return s.name } 259 260 // SetName sets the Name for the Driver. 261 func (s *SSD1306Driver) SetName(n string) { s.name = n } 262 263 // Connection returns the connection for the Driver. 264 func (s *SSD1306Driver) Connection() gobot.Connection { return s.connector.(gobot.Connection) } 265 266 // Start starts the Driver up, and writes start command 267 func (s *SSD1306Driver) Start() (err error) { 268 // check device size for supported resolutions 269 switch { 270 case s.displayWidth == 128 && s.displayHeight == 64: 271 s.initSequence = ssd1306Init128x64 272 case s.displayWidth == 128 && s.displayHeight == 32: 273 s.initSequence = ssd1306Init128x32 274 case s.displayWidth == 96 && s.displayHeight == 16: 275 s.initSequence = ssd1306Init96x16 276 default: 277 return fmt.Errorf("%dx%d resolution is unsupported, supported resolutions: 128x64, 128x32, 96x16", s.displayWidth, s.displayHeight) 278 } 279 // check for external vcc 280 if s.externalVCC { 281 s.initSequence.chargePumpSetting = 0x10 282 s.initSequence.contrast = 0x9F 283 s.initSequence.prechargePeriod = 0x22 284 } 285 bus := s.GetBusOrDefault(s.connector.GetDefaultBus()) 286 address := s.GetAddressOrDefault(ssd1306I2CAddress) 287 s.connection, err = s.connector.GetConnection(address, bus) 288 if err != nil { 289 return err 290 } 291 if err = s.Init(); err != nil { 292 return err 293 } 294 if err = s.On(); err != nil { 295 return err 296 } 297 return nil 298 } 299 300 // Halt returns true if device is halted successfully 301 func (s *SSD1306Driver) Halt() (err error) { return nil } 302 303 // WithSSD1306DisplayWidth option sets the SSD1306Driver DisplayWidth option. 304 func WithSSD1306DisplayWidth(val int) func(Config) { 305 return func(c Config) { 306 d, ok := c.(*SSD1306Driver) 307 if ok { 308 d.displayWidth = val 309 } 310 } 311 } 312 313 // WithSSD1306DisplayHeight option sets the SSD1306Driver DisplayHeight option. 314 func WithSSD1306DisplayHeight(val int) func(Config) { 315 return func(c Config) { 316 d, ok := c.(*SSD1306Driver) 317 if ok { 318 d.displayHeight = val 319 } 320 } 321 } 322 323 // WithSSD1306ExternalVCC option sets the SSD1306Driver ExternalVCC option. 324 func WithSSD1306ExternalVCC(val bool) func(Config) { 325 return func(c Config) { 326 d, ok := c.(*SSD1306Driver) 327 if ok { 328 d.externalVCC = val 329 } 330 } 331 } 332 333 // Init initializes the ssd1306 display. 334 func (s *SSD1306Driver) Init() (err error) { 335 // turn off screen 336 if err = s.Off(); err != nil { 337 return err 338 } 339 // run through initialization commands 340 if err = s.commands(s.initSequence.GetSequence()); err != nil { 341 return err 342 } 343 if err = s.commands([]byte{ssd1306ColumnAddr, 0, byte(s.buffer.width) - 1}); err != nil { 344 return err 345 } 346 if err = s.commands([]byte{ssd1306PageAddr, 0, (byte(s.buffer.height / s.pageSize)) - 1}); err != nil { 347 return err 348 } 349 return nil 350 } 351 352 // On turns on the display. 353 func (s *SSD1306Driver) On() (err error) { 354 return s.command(ssd1306SetDisplayOn) 355 } 356 357 // Off turns off the display. 358 func (s *SSD1306Driver) Off() (err error) { 359 return s.command(ssd1306SetDisplayOff) 360 } 361 362 // Clear clears the display buffer. 363 func (s *SSD1306Driver) Clear() { 364 s.buffer.Clear() 365 } 366 367 // Set sets a pixel in the buffer. 368 func (s *SSD1306Driver) Set(x, y, c int) { 369 s.buffer.SetPixel(x, y, c) 370 } 371 372 // Reset clears display. 373 func (s *SSD1306Driver) Reset() (err error) { 374 if err = s.Off(); err != nil { 375 return err 376 } 377 s.Clear() 378 if err = s.On(); err != nil { 379 return err 380 } 381 return nil 382 } 383 384 // SetContrast sets the display contrast. 385 func (s *SSD1306Driver) SetContrast(contrast byte) (err error) { 386 err = s.commands([]byte{ssd1306SetContrast, contrast}) 387 return 388 } 389 390 // Display sends the memory buffer to the display. 391 func (s *SSD1306Driver) Display() (err error) { 392 _, err = s.connection.Write(append([]byte{0x40}, s.buffer.buffer...)) 393 return err 394 } 395 396 // ShowImage takes a standard Go image and displays it in monochrome. 397 func (s *SSD1306Driver) ShowImage(img image.Image) (err error) { 398 if img.Bounds().Dx() != s.displayWidth || img.Bounds().Dy() != s.displayHeight { 399 return fmt.Errorf("image must match display width and height: %dx%d", s.displayWidth, s.displayHeight) 400 } 401 s.Clear() 402 for y, w, h := 0, img.Bounds().Dx(), img.Bounds().Dy(); y < h; y++ { 403 for x := 0; x < w; x++ { 404 c := img.At(x, y) 405 if r, g, b, _ := c.RGBA(); r > 0 || g > 0 || b > 0 { 406 s.Set(x, y, 1) 407 } 408 } 409 } 410 return s.Display() 411 } 412 413 // command sends a command to the ssd1306 414 func (s *SSD1306Driver) command(b byte) (err error) { 415 _, err = s.connection.Write([]byte{0x80, b}) 416 return err 417 } 418 419 // commands sends a command sequence to the ssd1306 420 func (s *SSD1306Driver) commands(commands []byte) (err error) { 421 var command []byte 422 for _, d := range commands { 423 command = append(command, []byte{0x80, d}...) 424 } 425 _, err = s.connection.Write(command) 426 return err 427 }