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  }