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  }