Added LCD display integration (closes #145)
This commit is contained in:
parent
7a7c065754
commit
af614480b8
6 changed files with 421 additions and 0 deletions
|
@ -260,6 +260,8 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
|||
'twilio',
|
||||
'pytz',
|
||||
'Adafruit_Python_DHT',
|
||||
'RPi.GPIO',
|
||||
'RPLCD',
|
||||
]
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
|
246
platypush/plugins/lcd/__init__.py
Normal file
246
platypush/plugins/lcd/__init__.py
Normal file
|
@ -0,0 +1,246 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
|
||||
from platypush.plugins import Plugin, action
|
||||
|
||||
|
||||
class PinMode(Enum):
|
||||
import RPi.GPIO
|
||||
BOARD = RPi.GPIO.BOARD
|
||||
BCM = RPi.GPIO.BCM
|
||||
|
||||
|
||||
class LcdPlugin(Plugin, ABC):
|
||||
"""
|
||||
Abstract class for plugins to communicate with LCD displays.
|
||||
|
||||
Requires:
|
||||
|
||||
* **RPLCD** (``pip install RPLCD``)
|
||||
* **RPi.GPIO** (``pip install RPi.GPIO``)
|
||||
|
||||
"""
|
||||
import RPLCD.lcd
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.lcd = None
|
||||
|
||||
@staticmethod
|
||||
def _get_pin_mode(pin_mode: str) -> int:
|
||||
pin_mode = pin_mode.upper()
|
||||
assert hasattr(PinMode, pin_mode), \
|
||||
'Invalid pin_mode: {}. Supported modes: {}'.format(
|
||||
pin_mode, list([mode.name for mode in PinMode if mode.name != 'RPi']))
|
||||
|
||||
return getattr(PinMode, pin_mode).value
|
||||
|
||||
@abstractmethod
|
||||
def _get_lcd(self) -> RPLCD.lcd.BaseCharLCD:
|
||||
pass
|
||||
|
||||
def _init_lcd(self):
|
||||
if self.lcd:
|
||||
return
|
||||
|
||||
self.lcd = self._get_lcd()
|
||||
|
||||
@action
|
||||
def close(self, clear: bool = False):
|
||||
"""
|
||||
Close the handler to the LCD display and release the GPIO resources.
|
||||
|
||||
:param clear: Clear the display as well on close (default: False).
|
||||
"""
|
||||
if self.lcd:
|
||||
self.lcd.close(clear=clear)
|
||||
self.lcd = None
|
||||
|
||||
@action
|
||||
def clear(self):
|
||||
"""
|
||||
Clear the LCD display.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.clear()
|
||||
|
||||
@action
|
||||
def home(self):
|
||||
"""
|
||||
Set cursor to initial position and reset any shifting.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.home()
|
||||
|
||||
@action
|
||||
def shift_display(self, amount: int):
|
||||
"""
|
||||
Set cursor to initial position and reset any shifting.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.shift_display(amount)
|
||||
|
||||
@action
|
||||
def write_string(self, value: str, position: Optional[List[int]] = None):
|
||||
"""
|
||||
Write a string to the display.
|
||||
|
||||
:param value: String to be displayed.
|
||||
:param position: String position on the display as a 2-int list.
|
||||
"""
|
||||
self._init_lcd()
|
||||
if position:
|
||||
self.lcd.cursor_pos = tuple(position)
|
||||
|
||||
self.lcd.write_string(value)
|
||||
|
||||
@action
|
||||
def set_cursor_pos(self, position: List[int]):
|
||||
"""
|
||||
Change the position of the cursor on the display.
|
||||
|
||||
:param position: New cursor position, as a list of two elements.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.cursor_pos = tuple(position)
|
||||
|
||||
@action
|
||||
def set_text_align(self, mode: str):
|
||||
"""
|
||||
Change the text align mode.
|
||||
|
||||
:param mode: Supported values: ``left``, ``right``.
|
||||
"""
|
||||
modes = ['left', 'right']
|
||||
mode = mode.lower()
|
||||
assert mode in modes, 'Unsupported text mode: {}. Supported modes: {}'.format(
|
||||
mode, modes)
|
||||
|
||||
self._init_lcd()
|
||||
self.lcd.text_align_mode = mode
|
||||
|
||||
@action
|
||||
def enable_display(self):
|
||||
"""
|
||||
Turn on the display.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.display_enabled = True
|
||||
|
||||
@action
|
||||
def disable_display(self):
|
||||
"""
|
||||
Turn off the display.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.display_enabled = False
|
||||
|
||||
@action
|
||||
def toggle_display(self):
|
||||
"""
|
||||
Toggle the display state.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.display_enabled = not self.lcd.display_enabled
|
||||
|
||||
@action
|
||||
def enable_backlight(self):
|
||||
"""
|
||||
Enable the display backlight.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.backlight_enabled = True
|
||||
|
||||
@action
|
||||
def disable_backlight(self):
|
||||
"""
|
||||
Disable the display backlight.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.backlight_enabled = False
|
||||
|
||||
@action
|
||||
def toggle_backlight(self):
|
||||
"""
|
||||
Toggle the display backlight on/off.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.backlight_enabled = not self.lcd.backlight_enabled
|
||||
|
||||
@action
|
||||
def create_char(self, location: int, bitmap: List[int]):
|
||||
"""
|
||||
Create a new character.
|
||||
The HD44780 supports up to 8 custom characters (location 0-7).
|
||||
|
||||
:param location: The place in memory where the character is stored.
|
||||
Values need to be integers between 0 and 7.
|
||||
:param bitmap: The bitmap containing the character. This should be a
|
||||
list of 8 numbers, each representing a 5 pixel row.
|
||||
|
||||
Example for the smiley character:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[
|
||||
0, # 0b00000
|
||||
10, # 0b01010
|
||||
10, # 0b01010
|
||||
0, # 0b00000
|
||||
17, # 0b10001
|
||||
17, # 0b10001
|
||||
14, # 0b01110
|
||||
0 # 0b00000
|
||||
]
|
||||
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.create_char(location=location, bitmap=tuple(bitmap))
|
||||
|
||||
@action
|
||||
def command(self, value: int):
|
||||
"""
|
||||
Send a raw command to the LCD.
|
||||
|
||||
:param value: Command to be sent.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.command(value)
|
||||
|
||||
@action
|
||||
def write(self, value: int):
|
||||
"""
|
||||
Write a raw byte to the LCD.
|
||||
|
||||
:param value: Byte to be sent.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.write(value)
|
||||
|
||||
@action
|
||||
def cr(self):
|
||||
"""
|
||||
Write a carriage return (``\\r``) character to the LCD.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.cr()
|
||||
|
||||
@action
|
||||
def lf(self):
|
||||
"""
|
||||
Write a line feed (``\\n``) character to the LCD.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.lf()
|
||||
|
||||
@action
|
||||
def crlf(self):
|
||||
"""
|
||||
Write a carriage return + line feed (``\\r\\n``) sequence to the LCD.
|
||||
"""
|
||||
self._init_lcd()
|
||||
self.lcd.crlf()
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
85
platypush/plugins/lcd/gpio.py
Normal file
85
platypush/plugins/lcd/gpio.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins.lcd import LcdPlugin
|
||||
|
||||
|
||||
class LcdGpioPlugin(LcdPlugin):
|
||||
"""
|
||||
Plugin to write to an LCD display connected via GPIO.
|
||||
|
||||
Requires:
|
||||
|
||||
* **RPLCD** (``pip install RPLCD``)
|
||||
* **RPi.GPIO** (``pip install RPi.GPIO``)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, pin_rs: int, pin_e: int, pins_data: List[int],
|
||||
pin_rw: Optional[int] = None, pin_mode: str = 'BOARD',
|
||||
pin_backlight: Optional[int] = None,
|
||||
cols: int = 16, rows: int = 2,
|
||||
backlight_enabled: bool = True,
|
||||
backlight_mode: str = 'active_low',
|
||||
dotsize: int = 8, charmap: str = 'A02',
|
||||
auto_linebreaks: bool = True,
|
||||
compat_mode: bool = False, **kwargs):
|
||||
"""
|
||||
:param pin_rs: Pin for register select (RS).
|
||||
:param pin_e: Pin to start data read or write (E).
|
||||
:param pins_data: List of data bus pins in 8 bit mode (DB0-DB7) or in 4
|
||||
bit mode (DB4-DB7) in ascending order.
|
||||
:param pin_mode: Which scheme to use for numbering of the GPIO pins,
|
||||
either ``BOARD`` or ``BCM``. Default: ``BOARD``.
|
||||
:param pin_rw: Pin for selecting read or write mode (R/W). Default:
|
||||
``None``, read only mode.
|
||||
:param pin_backlight: Pin for controlling backlight on/off. Set this to
|
||||
``None`` for no backlight control. Default: ``None``.
|
||||
:param cols: Number of columns per row (usually 16 or 20). Default: ``16``.
|
||||
:param rows: Number of display rows (usually 1, 2 or 4). Default: ``2``.
|
||||
:param backlight_enabled: Whether the backlight is enabled initially.
|
||||
Default: ``True``. Has no effect if pin_backlight is ``None``
|
||||
:param backlight_mode: Set this to either ``active_high`` or ``active_low``
|
||||
to configure the operating control for the backlight. Has no effect if
|
||||
pin_backlight is ``None``
|
||||
:param dotsize: Some 1 line displays allow a font height of 10px.
|
||||
Allowed: ``8`` or ``10``. Default: ``8``.
|
||||
:param charmap: The character map used. Depends on your LCD. This must
|
||||
be either ``A00`` or ``A02`` or ``ST0B``. Default: ``A02``.
|
||||
:param auto_linebreaks: Whether or not to automatically insert line
|
||||
breaks. Default: ``True``.
|
||||
:param compat_mode: Whether to run additional checks to support older LCDs
|
||||
that may not run at the reference clock (or keep up with it).
|
||||
Default: ``False``.
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.pin_mode = self._get_pin_mode(pin_mode)
|
||||
self.pin_rs = pin_rs
|
||||
self.pin_e = pin_e
|
||||
self.pin_rw = pin_rw
|
||||
self.pin_backlight = pin_backlight
|
||||
self.pins_data = pins_data
|
||||
self.cols = cols
|
||||
self.rows = rows
|
||||
self.backlight_enabled = backlight_enabled
|
||||
self.backlight_mode = backlight_mode
|
||||
self.dotsize = dotsize
|
||||
self.auto_linebreaks = auto_linebreaks
|
||||
self.compat_mode = compat_mode
|
||||
self.charmap = charmap
|
||||
|
||||
def _get_lcd(self):
|
||||
from RPLCD.gpio import CharLCD
|
||||
return CharLCD(cols=self.cols, rows=self.rows, pin_rs=self.pin_rs,
|
||||
pin_e=self.pin_e, pins_data=self.pins_data,
|
||||
numbering_mode=self.pin_mode, pin_rw=self.pin_rw,
|
||||
pin_backlight=self.pin_backlight,
|
||||
backlight_enabled=self.backlight_enabled,
|
||||
backlight_mode=self.backlight_mode,
|
||||
dotsize=self.dotsize, charmap=self.charmap,
|
||||
auto_linebreaks=self.auto_linebreaks,
|
||||
compat_mode=self.compat_mode)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
82
platypush/plugins/lcd/i2c.py
Normal file
82
platypush/plugins/lcd/i2c.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins.lcd import LcdPlugin
|
||||
|
||||
|
||||
class LcdI2cPlugin(LcdPlugin):
|
||||
"""
|
||||
Plugin to write to an LCD display connected via I2C.
|
||||
Adafruit I2C/SPI LCD Backback is supported.
|
||||
|
||||
Warning: You might need a level shifter (that supports i2c)
|
||||
between the SCL/SDA connections on the MCP chip / backpack and the Raspberry Pi.
|
||||
Or you might damage the Pi and possibly any other 3.3V i2c devices
|
||||
connected on the i2c bus. Or cause reliability issues. The SCL/SDA are rated 0.7*VDD
|
||||
on the MCP23008, so it needs 3.5V on the SCL/SDA when 5V is applied to drive the LCD.
|
||||
The MCP23008 and MCP23017 needs to be connected exactly the same way as the backpack.
|
||||
For complete schematics see the adafruit page at:
|
||||
https://learn.adafruit.com/i2c-spi-lcd-backpack/
|
||||
4-bit operation. I2C only supported.
|
||||
|
||||
Pin mapping::
|
||||
|
||||
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
BL | D7 | D6 | D5 | D4 | E | RS | -
|
||||
|
||||
Requires:
|
||||
|
||||
* **RPLCD** (``pip install RPLCD``)
|
||||
* **RPi.GPIO** (``pip install RPi.GPIO``)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, i2c_expander: str, address: int,
|
||||
expander_params: Optional[dict] = None,
|
||||
port: int = 1, cols: int = 16, rows: int = 2,
|
||||
backlight_enabled: bool = True,
|
||||
dotsize: int = 8, charmap: str = 'A02',
|
||||
auto_linebreaks: bool = True, **kwargs):
|
||||
"""
|
||||
:param i2c_expander: Set your I²C chip type. Supported: "PCF8574", "MCP23008", "MCP23017".
|
||||
:param address: The I2C address of your LCD.
|
||||
:param expander_params: Parameters for expanders, in a dictionary. Only needed for MCP23017
|
||||
gpio_bank - This must be either ``A`` or ``B``
|
||||
If you have a HAT, A is usually marked 1 and B is 2.
|
||||
Example: ``expander_params={'gpio_bank': 'A'}``
|
||||
:param port: The I2C port number. Default: ``1``.
|
||||
:param cols: Number of columns per row (usually 16 or 20). Default: ``16``.
|
||||
:param rows: Number of display rows (usually 1, 2 or 4). Default: ``2``.
|
||||
:param backlight_enabled: Whether the backlight is enabled initially.
|
||||
Default: ``True``. Has no effect if pin_backlight is ``None``
|
||||
:param dotsize: Some 1 line displays allow a font height of 10px.
|
||||
Allowed: ``8`` or ``10``. Default: ``8``.
|
||||
:param charmap: The character map used. Depends on your LCD. This must
|
||||
be either ``A00`` or ``A02`` or ``ST0B``. Default: ``A02``.
|
||||
:param auto_linebreaks: Whether or not to automatically insert line
|
||||
breaks. Default: ``True``.
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.i2c_expander = i2c_expander
|
||||
self.address = address
|
||||
self.expander_params = expander_params or {}
|
||||
self.port = port
|
||||
self.cols = cols
|
||||
self.rows = rows
|
||||
self.backlight_enabled = backlight_enabled
|
||||
self.dotsize = dotsize
|
||||
self.auto_linebreaks = auto_linebreaks
|
||||
self.charmap = charmap
|
||||
|
||||
def _get_lcd(self):
|
||||
from RPLCD.i2c import CharLCD
|
||||
return CharLCD(cols=self.cols, rows=self.rows,
|
||||
i2c_expander=self.i2c_expander,
|
||||
address=self.address, port=self.port,
|
||||
backlight_enabled=self.backlight_enabled,
|
||||
dotsize=self.dotsize, charmap=self.charmap,
|
||||
auto_linebreaks=self.auto_linebreaks)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
|
@ -289,3 +289,7 @@ croniter
|
|||
# Support for DHT11/DHT22/AM2302 temperature/humidity sensors
|
||||
# git+https://github.com/adafruit/Adafruit_Python_DHT
|
||||
|
||||
# Support for LCD display integration
|
||||
# RPi.GPIO
|
||||
# RPLCD
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -328,5 +328,7 @@ setup(
|
|||
'github': ['pytz'],
|
||||
# Support for DHT11/DHT22/AM2302 temperature/humidity sensors
|
||||
'dht': ['Adafruit_Python_DHT @ git+https://github.com/adafruit/Adafruit_Python_DHT'],
|
||||
# Support for LCD display integration
|
||||
'lcd': ['RPi.GPIO', 'RPLCD'],
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue