/ adafruit_character_lcd / character_lcd.py
character_lcd.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Brent Rubell for Adafruit Industries
  4  # Copyright (c) 2018 Kattni Rembor for Adafruit Industries
  5  #
  6  # Permission is hereby granted, free of charge, to any person obtaining a copy
  7  # of this software and associated documentation files (the "Software"), to deal
  8  # in the Software without restriction, including without limitation the rights
  9  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  # copies of the Software, and to permit persons to whom the Software is
 11  # furnished to do so, subject to the following conditions:
 12  #
 13  # The above copyright notice and this permission notice shall be included in
 14  # all copies or substantial portions of the Software.
 15  #
 16  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  # THE SOFTWARE.
 23  """
 24  `adafruit_character_lcd.character_lcd`
 25  ====================================================
 26  
 27  Module for interfacing with monochromatic character LCDs
 28  
 29  * Author(s): Kattni Rembor, Brent Rubell, Asher Lieber,
 30    Tony DiCola (original python charLCD library)
 31  
 32  Implementation Notes
 33  --------------------
 34  
 35  **Hardware:**
 36  
 37  "* `Adafruit Character LCDs <http://www.adafruit.com/category/63_96>`_"
 38  
 39  **Software and Dependencies:**
 40  
 41  * Adafruit CircuitPython firmware:
 42    https://github.com/adafruit/circuitpython/releases
 43  * Adafruit's Bus Device library (when using I2C/SPI):
 44    https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 45  
 46  """
 47  
 48  import time
 49  import digitalio
 50  from micropython import const
 51  
 52  __version__ = "0.0.0-auto.0"
 53  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_CharLCD.git"
 54  
 55  # pylint: disable-msg=bad-whitespace
 56  # Commands
 57  _LCD_CLEARDISPLAY = const(0x01)
 58  _LCD_RETURNHOME = const(0x02)
 59  _LCD_ENTRYMODESET = const(0x04)
 60  _LCD_DISPLAYCONTROL = const(0x08)
 61  _LCD_CURSORSHIFT = const(0x10)
 62  _LCD_FUNCTIONSET = const(0x20)
 63  _LCD_SETCGRAMADDR = const(0x40)
 64  _LCD_SETDDRAMADDR = const(0x80)
 65  
 66  # Entry flags
 67  _LCD_ENTRYLEFT = const(0x02)
 68  _LCD_ENTRYSHIFTDECREMENT = const(0x00)
 69  
 70  # Control flags
 71  _LCD_DISPLAYON = const(0x04)
 72  _LCD_CURSORON = const(0x02)
 73  _LCD_CURSOROFF = const(0x00)
 74  _LCD_BLINKON = const(0x01)
 75  _LCD_BLINKOFF = const(0x00)
 76  
 77  # Move flags
 78  _LCD_DISPLAYMOVE = const(0x08)
 79  _LCD_MOVERIGHT = const(0x04)
 80  _LCD_MOVELEFT = const(0x00)
 81  
 82  # Function set flags
 83  _LCD_4BITMODE = const(0x00)
 84  _LCD_2LINE = const(0x08)
 85  _LCD_1LINE = const(0x00)
 86  _LCD_5X8DOTS = const(0x00)
 87  
 88  # Offset for up to 4 rows.
 89  _LCD_ROW_OFFSETS = (0x00, 0x40, 0x14, 0x54)
 90  
 91  # pylint: enable-msg=bad-whitespace
 92  
 93  
 94  def _set_bit(byte_value, position, val):
 95      # Given the specified byte_value set the bit at position to the provided
 96      # boolean value val and return the modified byte.
 97      ret = None
 98      if val:
 99          ret = byte_value | (1 << position)
100      else:
101          ret = byte_value & ~(1 << position)
102      return ret
103  
104  
105  def _map(xval, in_min, in_max, out_min, out_max):
106      # Affine transfer/map with constrained output.
107      outrange = float(out_max - out_min)
108      inrange = float(in_max - in_min)
109      ret = (xval - in_min) * (outrange / inrange) + out_min
110      if out_max > out_min:
111          ret = max(min(ret, out_max), out_min)
112      else:
113          ret = max(min(ret, out_min), out_max)
114      return ret
115  
116  
117  # pylint: disable-msg=too-many-instance-attributes
118  class Character_LCD:
119      """Base class for character LCD.
120  
121      :param ~digitalio.DigitalInOut rs: The reset data line
122      :param ~digitalio.DigitalInOut en: The enable data line
123      :param ~digitalio.DigitalInOut d4: The data line 4
124      :param ~digitalio.DigitalInOut d5: The data line 5
125      :param ~digitalio.DigitalInOut d6: The data line 6
126      :param ~digitalio.DigitalInOut d7: The data line 7
127      :param columns: The columns on the charLCD
128      :param lines: The lines on the charLCD
129  
130      """
131  
132      LEFT_TO_RIGHT = const(0)
133      RIGHT_TO_LEFT = const(1)
134  
135      # pylint: disable-msg=too-many-arguments
136      def __init__(self, rs, en, d4, d5, d6, d7, columns, lines):
137  
138          self.columns = columns
139          self.lines = lines
140          #  save pin numbers
141          self.reset = rs
142          self.enable = en
143          self.dl4 = d4
144          self.dl5 = d5
145          self.dl6 = d6
146          self.dl7 = d7
147  
148          # set all pins as outputs
149          for pin in (rs, en, d4, d5, d6, d7):
150              pin.direction = digitalio.Direction.OUTPUT
151  
152          # Initialise the display
153          self._write8(0x33)
154          self._write8(0x32)
155          # Initialise display control
156          self.displaycontrol = _LCD_DISPLAYON | _LCD_CURSOROFF | _LCD_BLINKOFF
157          # Initialise display function
158          self.displayfunction = _LCD_4BITMODE | _LCD_1LINE | _LCD_2LINE | _LCD_5X8DOTS
159          # Initialise display mode
160          self.displaymode = _LCD_ENTRYLEFT | _LCD_ENTRYSHIFTDECREMENT
161          # Write to displaycontrol
162          self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol)
163          # Write to displayfunction
164          self._write8(_LCD_FUNCTIONSET | self.displayfunction)
165          # Set entry mode
166          self._write8(_LCD_ENTRYMODESET | self.displaymode)
167          self.clear()
168  
169          self._message = None
170          self._enable = None
171          self._direction = None
172          # track row and column used in cursor_position
173          # initialize to 0,0
174          self.row = 0
175          self.column = 0
176          self._column_align = False
177  
178      # pylint: enable-msg=too-many-arguments
179  
180      def home(self):
181          """Moves the cursor "home" to position (1, 1)."""
182          self._write8(_LCD_RETURNHOME)
183          time.sleep(0.003)
184  
185      def clear(self):
186          """Clears everything displayed on the LCD.
187  
188          The following example displays, "Hello, world!", then clears the LCD.
189  
190          .. code-block:: python
191  
192              import time
193              import board
194              import busio
195              import adafruit_character_lcd.character_lcd_i2c as character_lcd
196  
197              i2c = busio.I2C(board.SCL, board.SDA)
198              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
199  
200              lcd.message = "Hello, world!"
201              time.sleep(5)
202              lcd.clear()
203          """
204          self._write8(_LCD_CLEARDISPLAY)
205          time.sleep(0.003)
206  
207      @property
208      def column_align(self):
209          """If True, message text after '\\n' starts directly below start of first
210          character in message. If False, text after '\\n' starts at column zero.
211          """
212          return self._column_align
213  
214      @column_align.setter
215      def column_align(self, enable):
216          if isinstance(enable, bool):
217              self._column_align = enable
218          else:
219              raise ValueError("The column_align value must be either True or False")
220  
221      @property
222      def cursor(self):
223          """True if cursor is visible. False to stop displaying the cursor.
224  
225          The following example shows the cursor after a displayed message:
226  
227          .. code-block:: python
228  
229              import time
230              import board
231              import busio
232              import adafruit_character_lcd.character_lcd_i2c as character_lcd
233  
234              i2c = busio.I2C(board.SCL, board.SDA)
235              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
236  
237              lcd.cursor = True
238              lcd.message = "Cursor! "
239              time.sleep(5)
240  
241          """
242          return self.displaycontrol & _LCD_CURSORON == _LCD_CURSORON
243  
244      @cursor.setter
245      def cursor(self, show):
246          if show:
247              self.displaycontrol |= _LCD_CURSORON
248          else:
249              self.displaycontrol &= ~_LCD_CURSORON
250          self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol)
251  
252      def cursor_position(self, column, row):
253          """Move the cursor to position ``column``, ``row`` for the next
254          message only. Displaying a message resets the cursor position to (0, 0).
255  
256              :param column: column location
257              :param row: row location
258          """
259          # Clamp row to the last row of the display
260          if row >= self.lines:
261              row = self.lines - 1
262          # Clamp to last column of display
263          if column >= self.columns:
264              column = self.columns - 1
265          # Set location
266          self._write8(_LCD_SETDDRAMADDR | (column + _LCD_ROW_OFFSETS[row]))
267          # Update self.row and self.column to match setter
268          self.row = row
269          self.column = column
270  
271      @property
272      def blink(self):
273          """
274          Blink the cursor. True to blink the cursor. False to stop blinking.
275  
276          The following example shows a message followed by a blinking cursor for five seconds.
277  
278          .. code-block:: python
279  
280              import time
281              import board
282              import busio
283              import adafruit_character_lcd.character_lcd_i2c as character_lcd
284  
285              i2c = busio.I2C(board.SCL, board.SDA)
286              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
287  
288              lcd.blink = True
289              lcd.message = "Blinky cursor!"
290              time.sleep(5)
291              lcd.blink = False
292          """
293          return self.displaycontrol & _LCD_BLINKON == _LCD_BLINKON
294  
295      @blink.setter
296      def blink(self, blink):
297          if blink:
298              self.displaycontrol |= _LCD_BLINKON
299          else:
300              self.displaycontrol &= ~_LCD_BLINKON
301          self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol)
302  
303      @property
304      def display(self):
305          """
306          Enable or disable the display. True to enable the display. False to disable the display.
307  
308          The following example displays, "Hello, world!" on the LCD and then turns the display off.
309  
310          .. code-block:: python
311  
312              import time
313              import board
314              import busio
315              import adafruit_character_lcd.character_lcd_i2c as character_lcd
316  
317              i2c = busio.I2C(board.SCL, board.SDA)
318              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
319  
320              lcd.message = "Hello, world!"
321              time.sleep(5)
322              lcd.display = False
323          """
324          return self.displaycontrol & _LCD_DISPLAYON == _LCD_DISPLAYON
325  
326      @display.setter
327      def display(self, enable):
328          if enable:
329              self.displaycontrol |= _LCD_DISPLAYON
330          else:
331              self.displaycontrol &= ~_LCD_DISPLAYON
332          self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol)
333  
334      @property
335      def message(self):
336          """Display a string of text on the character LCD.
337          Start position is (0,0) if cursor_position is not set.
338          If cursor_position is set, message starts at the set
339          position from the left for left to right text and from
340          the right for right to left text. Resets cursor column
341          and row to (0,0) after displaying the message.
342  
343          The following example displays, "Hello, world!" on the LCD.
344  
345          .. code-block:: python
346  
347              import time
348              import board
349              import busio
350              import adafruit_character_lcd.character_lcd_i2c as character_lcd
351  
352              i2c = busio.I2C(board.SCL, board.SDA)
353              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
354  
355              lcd.message = "Hello, world!"
356              time.sleep(5)
357          """
358          return self._message
359  
360      @message.setter
361      def message(self, message):
362          self._message = message
363          # Set line to match self.row from cursor_position()
364          line = self.row
365          # Track times through iteration, to act on the initial character of the message
366          initial_character = 0
367          # iterate through each character
368          for character in message:
369              # If this is the first character in the string:
370              if initial_character == 0:
371                  # Start at (0, 0) unless direction is set right to left, in which case start
372                  # on the opposite side of the display if cursor_position not set or (0,0)
373                  # If cursor_position is set then starts at the specified location for
374                  # LEFT_TO_RIGHT. If RIGHT_TO_LEFT cursor_position is determined from right.
375                  # allows for cursor_position to work in RIGHT_TO_LEFT mode
376                  if self.displaymode & _LCD_ENTRYLEFT > 0:
377                      col = self.column
378                  else:
379                      col = self.columns - 1 - self.column
380                  self.cursor_position(col, line)
381                  initial_character += 1
382              # If character is \n, go to next line
383              if character == "\n":
384                  line += 1
385                  # Start the second line at (0, 1) unless direction is set right to left in
386                  # which case start on the opposite side of the display if cursor_position
387                  # is (0,0) or not set. Start second line at same column as first line when
388                  # cursor_position is set
389                  if self.displaymode & _LCD_ENTRYLEFT > 0:
390                      col = self.column * self._column_align
391                  else:
392                      if self._column_align:
393                          col = self.column
394                      else:
395                          col = self.columns - 1
396                  self.cursor_position(col, line)
397              # Write string to display
398              else:
399                  self._write8(ord(character), True)
400          # reset column and row to (0,0) after message is displayed
401          self.column, self.row = 0, 0
402  
403      def move_left(self):
404          """Moves displayed text left one column.
405  
406          The following example scrolls a message to the left off the screen.
407  
408          .. code-block:: python
409  
410              import time
411              import board
412              import busio
413              import adafruit_character_lcd.character_lcd_i2c as character_lcd
414  
415              i2c = busio.I2C(board.SCL, board.SDA)
416              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
417  
418              scroll_message = "<-- Scroll"
419              lcd.message = scroll_message
420              time.sleep(2)
421              for i in range(len(scroll_message)):
422                  lcd.move_left()
423                  time.sleep(0.5)
424          """
425          self._write8(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | _LCD_MOVELEFT)
426  
427      def move_right(self):
428          """Moves displayed text right one column.
429  
430          The following example scrolls a message to the right off the screen.
431  
432          .. code-block:: python
433  
434              import time
435              import board
436              import busio
437              import adafruit_character_lcd.character_lcd_i2c as character_lcd
438  
439              i2c = busio.I2C(board.SCL, board.SDA)
440              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
441  
442              scroll_message = "Scroll -->"
443              lcd.message = scroll_message
444              time.sleep(2)
445              for i in range(len(scroll_message) + 16):
446                  lcd.move_right()
447                  time.sleep(0.5)
448          """
449          self._write8(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | _LCD_MOVERIGHT)
450  
451      @property
452      def text_direction(self):
453          """The direction the text is displayed. To display the text left to right beginning on the
454          left side of the LCD, set ``text_direction = LEFT_TO_RIGHT``. To display the text right
455          to left beginning on the right size of the LCD, set ``text_direction = RIGHT_TO_LEFT``.
456          Text defaults to displaying from left to right.
457  
458          The following example displays "Hello, world!" from right to left.
459  
460          .. code-block:: python
461  
462              import time
463              import board
464              import busio
465              import adafruit_character_lcd.character_lcd_i2c as character_lcd
466  
467              i2c = busio.I2C(board.SCL, board.SDA)
468              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
469  
470              lcd.text_direction = lcd.RIGHT_TO_LEFT
471              lcd.message = "Hello, world!"
472              time.sleep(5)
473          """
474          return self._direction
475  
476      @text_direction.setter
477      def text_direction(self, direction):
478          self._direction = direction
479          if direction == self.LEFT_TO_RIGHT:
480              self._left_to_right()
481          elif direction == self.RIGHT_TO_LEFT:
482              self._right_to_left()
483  
484      def _left_to_right(self):
485          # Displays text from left to right on the LCD.
486          self.displaymode |= _LCD_ENTRYLEFT
487          self._write8(_LCD_ENTRYMODESET | self.displaymode)
488  
489      def _right_to_left(self):
490          # Displays text from right to left on the LCD.
491          self.displaymode &= ~_LCD_ENTRYLEFT
492          self._write8(_LCD_ENTRYMODESET | self.displaymode)
493  
494      def create_char(self, location, pattern):
495          """
496          Fill one of the first 8 CGRAM locations with custom characters.
497          The location parameter should be between 0 and 7 and pattern should
498          provide an array of 8 bytes containing the pattern. E.g. you can easily
499          design your custom character at http://www.quinapalus.com/hd44780udg.html
500          To show your custom character use, for example, ``lcd.message = "\x01"``
501  
502          :param location: integer in range(8) to store the created character
503          :param ~bytes pattern: len(8) describes created character
504  
505          """
506          # only position 0..7 are allowed
507          location &= 0x7
508          self._write8(_LCD_SETCGRAMADDR | (location << 3))
509          for i in range(8):
510              self._write8(pattern[i], char_mode=True)
511  
512      def _write8(self, value, char_mode=False):
513          # Sends 8b ``value`` in ``char_mode``.
514          # :param value: bytes
515          # :param char_mode: character/data mode selector. False (default) for
516          # data only, True for character bits.
517          #  one ms delay to prevent writing too quickly.
518          time.sleep(0.001)
519          #  set character/data bit. (charmode = False)
520          self.reset.value = char_mode
521          # WRITE upper 4 bits
522          self.dl4.value = ((value >> 4) & 1) > 0
523          self.dl5.value = ((value >> 5) & 1) > 0
524          self.dl6.value = ((value >> 6) & 1) > 0
525          self.dl7.value = ((value >> 7) & 1) > 0
526          #  send command
527          self._pulse_enable()
528          # WRITE lower 4 bits
529          self.dl4.value = (value & 1) > 0
530          self.dl5.value = ((value >> 1) & 1) > 0
531          self.dl6.value = ((value >> 2) & 1) > 0
532          self.dl7.value = ((value >> 3) & 1) > 0
533          self._pulse_enable()
534  
535      def _pulse_enable(self):
536          # Pulses (lo->hi->lo) to send commands.
537          self.enable.value = False
538          # 1microsec pause
539          time.sleep(0.0000001)
540          self.enable.value = True
541          time.sleep(0.0000001)
542          self.enable.value = False
543          time.sleep(0.0000001)
544  
545  
546  # pylint: enable-msg=too-many-instance-attributes
547  
548  
549  # pylint: disable-msg=too-many-instance-attributes
550  class Character_LCD_Mono(Character_LCD):
551      """Interfaces with monochromatic character LCDs.
552  
553          :param ~digitalio.DigitalInOut rs: The reset data line
554          :param ~digitalio.DigitalInOut en: The enable data line
555          :param ~digitalio.DigitalInOut d4: The data line 4
556          :param ~digitalio.DigitalInOut d5: The data line 5
557          :param ~digitalio.DigitalInOut d6: The data line 6
558          :param ~digitalio.DigitalInOut d7: The data line 7
559          :param columns: The columns on the charLCD
560          :param lines: The lines on the charLCD
561          :param ~digitalio.DigitalInOut backlight_pin: The backlight pin
562          :param bool backlight_inverted: ``False`` if LCD is not inverted, i.e. backlight pin is
563              connected to common anode. ``True`` if LCD is inverted i.e. backlight pin is connected
564              to common cathode.
565  
566      """
567  
568      # pylint: disable-msg=too-many-arguments
569      def __init__(
570          self,
571          rs,
572          en,
573          db4,
574          db5,
575          db6,
576          db7,
577          columns,
578          lines,
579          backlight_pin=None,
580          backlight_inverted=False,
581      ):
582  
583          # Backlight pin and inversion
584          self.backlight_pin = backlight_pin
585          self.backlight_inverted = backlight_inverted
586  
587          #  Setup backlight
588          if backlight_pin is not None:
589              self.backlight_pin.direction = digitalio.Direction.OUTPUT
590              self.backlight = True
591          super().__init__(rs, en, db4, db5, db6, db7, columns, lines)
592  
593      # pylint: enable-msg=too-many-arguments
594  
595      @property
596      def backlight(self):
597          """Enable or disable backlight. True if backlight is on. False if backlight is off.
598  
599          The following example turns the backlight off, then displays, "Hello, world?", then turns
600          the backlight on and displays, "Hello, world!"
601  
602          .. code-block:: python
603  
604              import time
605              import board
606              import busio
607              import adafruit_character_lcd.character_lcd_i2c as character_lcd
608  
609              i2c = busio.I2C(board.SCL, board.SDA)
610  
611              lcd = character_lcd.Character_LCD_I2C(i2c, 16, 2)
612  
613              lcd.backlight = False
614              lcd.message = "Hello, world?"
615              time.sleep(5)
616              lcd.backlight = True
617              lcd.message = "Hello, world!"
618              time.sleep(5)
619  
620          """
621          return self._enable
622  
623      @backlight.setter
624      def backlight(self, enable):
625          self._enable = enable
626          if enable:
627              self.backlight_pin.value = not self.backlight_inverted
628          else:
629              self.backlight_pin.value = self.backlight_inverted
630  
631  
632  class Character_LCD_RGB(Character_LCD):
633      """Interfaces with RGB character LCDs.
634  
635          :param ~digitalio.DigitalInOut rs: The reset data line
636          :param ~digitalio.DigitalInOut en: The enable data line
637          :param ~digitalio.DigitalInOut db4: The data line 4
638          :param ~digitalio.DigitalInOut db5: The data line 5
639          :param ~digitalio.DigitalInOut db6: The data line 6
640          :param ~digitalio.DigitalInOut db7: The data line 7
641          :param columns: The columns on the charLCD
642          :param lines: The lines on the charLCD
643          :param ~pulseio.PWMOut, ~digitalio.DigitalInOut red: Red RGB Anode
644          :param ~pulseio.PWMOut, ~digitalio.DigitalInOut green: Green RGB Anode
645          :param ~pulseio.PWMOut, ~digitalio.DigitalInOut blue: Blue RGB Anode
646          :param ~digitalio.DigitalInOut read_write: The rw pin. Determines whether to read to or
647              write from the display. Not necessary if only writing to the display. Used on shield.
648  
649      """
650  
651      # pylint: disable-msg=too-many-arguments
652      def __init__(
653          self,
654          rs,
655          en,
656          db4,
657          db5,
658          db6,
659          db7,
660          columns,
661          lines,
662          red,
663          green,
664          blue,
665          read_write=None,
666      ):
667  
668          # Define read_write (rw) pin
669          self.read_write = read_write
670  
671          # Setup rw pin if used
672          if read_write is not None:
673              self.read_write.direction = digitalio.Direction.OUTPUT
674  
675          # define color params
676          self.rgb_led = [red, green, blue]
677  
678          for pin in self.rgb_led:
679              if hasattr(pin, "direction"):
680                  # Assume a digitalio.DigitalInOut or compatible interface:
681                  pin.direction = digitalio.Direction.OUTPUT
682              elif not hasattr(pin, "duty_cycle"):
683                  raise TypeError(
684                      "RGB LED objects must be instances of digitalio.DigitalInOut"
685                      " or pulseio.PWMOut, or provide a compatible interface."
686                  )
687  
688          self._color = [0, 0, 0]
689          super().__init__(rs, en, db4, db5, db6, db7, columns, lines)
690  
691      @property
692      def color(self):
693          """
694          The color of the display. Provide a list of three integers ranging 0 - 100, ``[R, G, B]``.
695          ``0`` is no color, or "off". ``100`` is maximum color. For example, the brightest red would
696          be ``[100, 0, 0]``, and a half-bright purple would be, ``[50, 0, 50]``.
697  
698          If PWM is unavailable, ``0`` is off, and non-zero is on. For example, ``[1, 0, 0]`` would
699          be red.
700  
701          The following example turns the LCD red and displays, "Hello, world!".
702  
703          .. code-block:: python
704  
705              import time
706              import board
707              import busio
708              import adafruit_character_lcd.character_lcd_rgb_i2c as character_lcd
709  
710              i2c = busio.I2C(board.SCL, board.SDA)
711  
712              lcd = character_lcd.Character_LCD_RGB_I2C(i2c, 16, 2)
713  
714              lcd.color = [100, 0, 0]
715              lcd.message = "Hello, world!"
716              time.sleep(5)
717          """
718          return self._color
719  
720      @color.setter
721      def color(self, color):
722          self._color = color
723          for number, pin in enumerate(self.rgb_led):
724              if hasattr(pin, "duty_cycle"):
725                  # Assume a pulseio.PWMOut or compatible interface and set duty cycle:
726                  pin.duty_cycle = int(_map(color[number], 0, 100, 65535, 0))
727              elif hasattr(pin, "value"):
728                  # If we don't have a PWM interface, all we can do is turn each color
729                  # on / off.  Assume a DigitalInOut (or compatible interface) and write
730                  # 0 (on) to pin for any value greater than 0, or 1 (off) for 0:
731                  pin.value = not color[number] > 1