gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/jhd1313m1_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"gobot.io/x/gobot/v2"
     9  )
    10  
    11  const (
    12  	REG_RED   = 0x04
    13  	REG_GREEN = 0x03
    14  	REG_BLUE  = 0x02
    15  
    16  	LCD_CLEARDISPLAY        = 0x01
    17  	LCD_RETURNHOME          = 0x02
    18  	LCD_ENTRYMODESET        = 0x04
    19  	LCD_DISPLAYCONTROL      = 0x08
    20  	LCD_CURSORSHIFT         = 0x10
    21  	LCD_FUNCTIONSET         = 0x20
    22  	LCD_SETCGRAMADDR        = 0x40
    23  	LCD_SETDDRAMADDR        = 0x80
    24  	LCD_ENTRYRIGHT          = 0x00
    25  	LCD_ENTRYLEFT           = 0x02
    26  	LCD_ENTRYSHIFTINCREMENT = 0x01
    27  	LCD_ENTRYSHIFTDECREMENT = 0x00
    28  	LCD_DISPLAYON           = 0x04
    29  	LCD_DISPLAYOFF          = 0x00
    30  	LCD_CURSORON            = 0x02
    31  	LCD_CURSOROFF           = 0x00
    32  	LCD_BLINKON             = 0x01
    33  	LCD_BLINKOFF            = 0x00
    34  	LCD_DISPLAYMOVE         = 0x08
    35  	LCD_CURSORMOVE          = 0x00
    36  	LCD_MOVERIGHT           = 0x04
    37  	LCD_MOVELEFT            = 0x00
    38  	LCD_2LINE               = 0x08
    39  	LCD_CMD                 = 0x80
    40  	LCD_DATA                = 0x40
    41  
    42  	LCD_2NDLINEOFFSET = 0x40
    43  )
    44  
    45  // CustomLCDChars is a map of CGRAM characters that can be loaded
    46  // into a LCD screen to display custom characters. Some LCD screens such
    47  // as the Grove screen (jhd1313m1) isn't loaded with latin 1 characters.
    48  // It's up to the developer to load the set up to 8 custom characters and
    49  // update the input text so the character is swapped by a byte reflecting
    50  // the position of the custom character to use.
    51  // See SetCustomChar
    52  var CustomLCDChars = map[string][8]byte{
    53  	"é":       {130, 132, 142, 145, 159, 144, 142, 128},
    54  	"è":       {136, 132, 142, 145, 159, 144, 142, 128},
    55  	"ê":       {132, 138, 142, 145, 159, 144, 142, 128},
    56  	"à":       {136, 134, 128, 142, 145, 147, 141, 128},
    57  	"â":       {132, 138, 128, 142, 145, 147, 141, 128},
    58  	"á":       {2, 4, 14, 1, 15, 17, 15, 0},
    59  	"î":       {132, 138, 128, 140, 132, 132, 142, 128},
    60  	"í":       {2, 4, 12, 4, 4, 4, 14, 0},
    61  	"û":       {132, 138, 128, 145, 145, 147, 141, 128},
    62  	"ù":       {136, 134, 128, 145, 145, 147, 141, 128},
    63  	"ñ":       {14, 0, 22, 25, 17, 17, 17, 0},
    64  	"ó":       {2, 4, 14, 17, 17, 17, 14, 0},
    65  	"heart":   {0, 10, 31, 31, 31, 14, 4, 0},
    66  	"smiley":  {0, 0, 10, 0, 0, 17, 14, 0},
    67  	"frowney": {0, 0, 10, 0, 0, 0, 14, 17},
    68  }
    69  
    70  var jhd1313m1ErrInvalidPosition = fmt.Errorf("Invalid position value")
    71  
    72  // JHD1313M1Driver is a driver for the Jhd1313m1 LCD display which has two i2c addreses,
    73  // one belongs to a controller and the other controls solely the backlight.
    74  // This module was tested with the Seed Grove LCD RGB Backlight v2.0 display which requires 5V to operate.
    75  // http://www.seeedstudio.com/wiki/Grove_-_LCD_RGB_Backlight
    76  type JHD1313M1Driver struct {
    77  	name      string
    78  	connector Connector
    79  	Config
    80  	gobot.Commander
    81  	lcdAddress    int
    82  	lcdConnection Connection
    83  	rgbAddress    int
    84  	rgbConnection Connection
    85  }
    86  
    87  // NewJHD1313M1Driver creates a new driver with specified i2c interface.
    88  // Params:
    89  //
    90  //	conn Connector - the Adaptor to use with this Driver
    91  //
    92  // Optional params:
    93  //
    94  //	i2c.WithBus(int):	bus to use with this driver
    95  func NewJHD1313M1Driver(a Connector, options ...func(Config)) *JHD1313M1Driver {
    96  	j := &JHD1313M1Driver{
    97  		name:       gobot.DefaultName("JHD1313M1"),
    98  		connector:  a,
    99  		Config:     NewConfig(),
   100  		Commander:  gobot.NewCommander(),
   101  		lcdAddress: 0x3E,
   102  		rgbAddress: 0x62,
   103  	}
   104  
   105  	for _, option := range options {
   106  		option(j)
   107  	}
   108  
   109  	j.AddCommand("SetRGB", func(params map[string]interface{}) interface{} {
   110  		r, _ := strconv.Atoi(params["r"].(string))
   111  		g, _ := strconv.Atoi(params["g"].(string))
   112  		b, _ := strconv.Atoi(params["b"].(string))
   113  		return j.SetRGB(r, g, b)
   114  	})
   115  	j.AddCommand("Clear", func(params map[string]interface{}) interface{} {
   116  		return j.Clear()
   117  	})
   118  	j.AddCommand("Home", func(params map[string]interface{}) interface{} {
   119  		return j.Home()
   120  	})
   121  	j.AddCommand("Write", func(params map[string]interface{}) interface{} {
   122  		msg := params["msg"].(string)
   123  		return j.Write(msg)
   124  	})
   125  	j.AddCommand("SetPosition", func(params map[string]interface{}) interface{} {
   126  		pos, _ := strconv.Atoi(params["pos"].(string))
   127  		return j.SetPosition(pos)
   128  	})
   129  	j.AddCommand("Scroll", func(params map[string]interface{}) interface{} {
   130  		lr, _ := strconv.ParseBool(params["lr"].(string))
   131  		return j.Scroll(lr)
   132  	})
   133  
   134  	return j
   135  }
   136  
   137  // Name returns the name the JHD1313M1 Driver was given when created.
   138  func (h *JHD1313M1Driver) Name() string { return h.name }
   139  
   140  // SetName sets the name for the JHD1313M1 Driver.
   141  func (h *JHD1313M1Driver) SetName(n string) { h.name = n }
   142  
   143  // Connection returns the driver connection to the device.
   144  func (h *JHD1313M1Driver) Connection() gobot.Connection {
   145  	return h.connector.(gobot.Connection)
   146  }
   147  
   148  // Start starts the backlit and the screen and initializes the states.
   149  func (h *JHD1313M1Driver) Start() (err error) {
   150  	bus := h.GetBusOrDefault(h.connector.DefaultI2cBus())
   151  
   152  	if h.lcdConnection, err = h.connector.GetI2cConnection(h.lcdAddress, bus); err != nil {
   153  		return err
   154  	}
   155  
   156  	if h.rgbConnection, err = h.connector.GetI2cConnection(h.rgbAddress, bus); err != nil {
   157  		return err
   158  	}
   159  
   160  	// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
   161  	// according to datasheet, we need at least 40ms after power rises above 2.7V
   162  	// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
   163  	time.Sleep(50 * time.Millisecond)
   164  
   165  	// this is according to the hitachi HD44780 datasheet
   166  	// page 45 figure 23
   167  	// Send function set command sequence
   168  	init_payload := []byte{LCD_CMD, LCD_FUNCTIONSET | LCD_2LINE}
   169  	if _, err := h.lcdConnection.Write(init_payload); err != nil {
   170  		return err
   171  	}
   172  
   173  	// wait more than 4.1ms
   174  	time.Sleep(4500 * time.Microsecond)
   175  	// second try
   176  	if _, err := h.lcdConnection.Write(init_payload); err != nil {
   177  		return err
   178  	}
   179  
   180  	time.Sleep(150 * time.Microsecond)
   181  	// third go
   182  	if _, err := h.lcdConnection.Write(init_payload); err != nil {
   183  		return err
   184  	}
   185  
   186  	if _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_DISPLAYCONTROL | LCD_DISPLAYON}); err != nil {
   187  		return err
   188  	}
   189  
   190  	time.Sleep(100 * time.Microsecond)
   191  	if err := h.Clear(); err != nil {
   192  		return err
   193  	}
   194  
   195  	if _, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT}); err != nil {
   196  		return err
   197  	}
   198  
   199  	if err := h.setReg(0, 0); err != nil {
   200  		return err
   201  	}
   202  	if err := h.setReg(1, 0); err != nil {
   203  		return err
   204  	}
   205  	if err := h.setReg(0x08, 0xAA); err != nil {
   206  		return err
   207  	}
   208  
   209  	if err := h.SetRGB(255, 255, 255); err != nil {
   210  		return err
   211  	}
   212  
   213  	return nil
   214  }
   215  
   216  // SetRGB sets the Red Green Blue value of backlit.
   217  func (h *JHD1313M1Driver) SetRGB(r, g, b int) error {
   218  	if err := h.setReg(REG_RED, r); err != nil {
   219  		return err
   220  	}
   221  	if err := h.setReg(REG_GREEN, g); err != nil {
   222  		return err
   223  	}
   224  	return h.setReg(REG_BLUE, b)
   225  }
   226  
   227  // Clear clears the text on the lCD display.
   228  func (h *JHD1313M1Driver) Clear() error {
   229  	err := h.command([]byte{LCD_CLEARDISPLAY})
   230  	return err
   231  }
   232  
   233  // Home sets the cursor to the origin position on the display.
   234  func (h *JHD1313M1Driver) Home() error {
   235  	err := h.command([]byte{LCD_RETURNHOME})
   236  	// This wait fixes a race condition when calling home and clear back to back.
   237  	time.Sleep(2 * time.Millisecond)
   238  	return err
   239  }
   240  
   241  // Write displays the passed message on the screen.
   242  func (h *JHD1313M1Driver) Write(message string) error {
   243  	// This wait fixes an odd bug where the clear function doesn't always work properly.
   244  	time.Sleep(1 * time.Millisecond)
   245  	for _, val := range message {
   246  		if val == '\n' {
   247  			if err := h.SetPosition(16); err != nil {
   248  				return err
   249  			}
   250  			continue
   251  		}
   252  		if _, err := h.lcdConnection.Write([]byte{LCD_DATA, byte(val)}); err != nil {
   253  			return err
   254  		}
   255  	}
   256  	return nil
   257  }
   258  
   259  // SetPosition sets the cursor and the data display to pos.
   260  // 0..15 are the positions in the first display line.
   261  // 16..32 are the positions in the second display line.
   262  func (h *JHD1313M1Driver) SetPosition(pos int) (err error) {
   263  	if pos < 0 || pos > 31 {
   264  		err = jhd1313m1ErrInvalidPosition
   265  		return
   266  	}
   267  	offset := byte(pos)
   268  	if pos >= 16 {
   269  		offset -= 16
   270  		offset |= LCD_2NDLINEOFFSET
   271  	}
   272  	err = h.command([]byte{LCD_SETDDRAMADDR | offset})
   273  	return
   274  }
   275  
   276  // Scroll sets the scrolling direction for the display, either left to right, or
   277  // right to left.
   278  func (h *JHD1313M1Driver) Scroll(lr bool) error {
   279  	if lr {
   280  		_, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT})
   281  		return err
   282  	}
   283  
   284  	_, err := h.lcdConnection.Write([]byte{LCD_CMD, LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT})
   285  	return err
   286  }
   287  
   288  // Halt is a noop function.
   289  func (h *JHD1313M1Driver) Halt() error { return nil }
   290  
   291  // SetCustomChar sets one of the 8 CGRAM locations with a custom character.
   292  // The custom character can be used by writing a byte of value 0 to 7.
   293  // When you are using LCD as 5x8 dots in function set then you can define a total of 8 user defined patterns
   294  // (1 Byte for each row and 8 rows for each pattern).
   295  // Use http://www.8051projects.net/lcd-interfacing/lcd-custom-character.php to create your own
   296  // characters.
   297  // To use a custom character, write byte value of the custom character position as a string after
   298  // having setup the custom character.
   299  func (h *JHD1313M1Driver) SetCustomChar(pos int, charMap [8]byte) error {
   300  	if pos > 7 {
   301  		return fmt.Errorf("can't set a custom character at a position greater than 7")
   302  	}
   303  	location := uint8(pos)
   304  	if err := h.command([]byte{LCD_SETCGRAMADDR | (location << 3)}); err != nil {
   305  		return err
   306  	}
   307  	_, err := h.lcdConnection.Write(append([]byte{LCD_DATA}, charMap[:]...))
   308  	return err
   309  }
   310  
   311  func (h *JHD1313M1Driver) setReg(command int, data int) error {
   312  	_, err := h.rgbConnection.Write([]byte{byte(command), byte(data)})
   313  	return err
   314  }
   315  
   316  func (h *JHD1313M1Driver) command(buf []byte) error {
   317  	_, err := h.lcdConnection.Write(append([]byte{LCD_CMD}, buf...))
   318  	return err
   319  }