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  }