forked from platypush/platypush
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',
|
'twilio',
|
||||||
'pytz',
|
'pytz',
|
||||||
'Adafruit_Python_DHT',
|
'Adafruit_Python_DHT',
|
||||||
|
'RPi.GPIO',
|
||||||
|
'RPLCD',
|
||||||
]
|
]
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
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
|
# Support for DHT11/DHT22/AM2302 temperature/humidity sensors
|
||||||
# git+https://github.com/adafruit/Adafruit_Python_DHT
|
# 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'],
|
'github': ['pytz'],
|
||||||
# Support for DHT11/DHT22/AM2302 temperature/humidity sensors
|
# Support for DHT11/DHT22/AM2302 temperature/humidity sensors
|
||||||
'dht': ['Adafruit_Python_DHT @ git+https://github.com/adafruit/Adafruit_Python_DHT'],
|
'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