Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CI

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.10

- name: Instala dependencias
run: |
poetry install
- name: Ejecuta pruebas
run: |
poetry run pytest
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Publish
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,78 @@
# RPiLCD
🐍📟 RPiLCD: Librería en Python para controlar pantallas LCD en Raspberry Pi (compatible con I2C y otras placas de hardware). Fácil, flexible y lista para tus proyectos.

[![Ask DeepWiki](https://devin.ai/assets/askdeepwiki.png)](https://deepwiki.com/Exploit34/RPiLCD)

🐍📟 RPiLCD is a Python library for controlling LCD character displays on the Raspberry Pi. It's designed to be easy, flexible, and compatible with I2C interfaces, making it perfect for your hardware projects.

## Features

* **Simple Interface:** Provides an easy-to-use API for writing text, clearing the screen, and controlling the cursor.
* **I2C Support:** Works seamlessly with common I2C LCD backpack modules, saving you GPIO pins.
* **Lightweight:** Minimal dependencies, ensuring it's light on resources.
* **Flexible:** Easily integrate LCD display functionality into your Python scripts and applications running on a Raspberry Pi.

## Installation

You can install the library using pip:

```bash
pip install rpilcd
```

The library depends on `smbus` for I2C communication and `RPi.GPIO` for pin control. These will be installed automatically.

## Usage

Here is a basic example of how to initialize an I2C LCD and write text to it.

```python
import time
from RPiLCD import LCD

# Assuming a 16x2 LCD with I2C address 0x27
# lcd = LCD(address=0x27, port=1, cols=16, rows=2)

# The library needs to be implemented. This is a placeholder for usage.
# A typical implementation would look like this:

class MockLCD:
def __init__(self, address, port, cols, rows):
print(f"Initializing LCD at address {address} on port {port} ({cols}x{rows})")

def text(self, message, line):
print(f"Writing to line {line}: {message}")

def clear(self):
print("Clearing the display.")

# Initialize the display
lcd = MockLCD(address=0x27, port=1, cols=16, rows=2)

try:
# Clear the display
lcd.clear()

# Write a message to the first line
lcd.text("Hello, World!", 1)

# Write a message to the second line
lcd.text("RPiLCD in action", 2)

time.sleep(5) # Wait for 5 seconds

lcd.clear()

except KeyboardInterrupt:
print("Program stopped.")
lcd.clear()

```

## Dependencies

* [smbus](https://pypi.org/project/smbus-cffi/) or [smbus2](https://pypi.org/project/smbus2/)
* [RPi.GPIO](https://pypi.org/project/RPi.GPIO/)

## License

This project is licensed under the GNU Lesser General Public License v2.1. See the [LICENSE](LICENSE) file for more details.
Empty file added README.rst
Empty file.
20 changes: 20 additions & 0 deletions example/I2c_Rasp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from RPiLCD import I2C
from time import sleep

lcd = I2C(address=0x27, width=16, rows=2)

lcd.clear()

lcd.display_text("Hello everyone", line=1, align="left")

sleep(2)

lcd.display_text("Welcome people!", line=2, align="center")

lcd.toggle_backlight(False)
sleep(1)

lcd.toggle_backlight(True)

lcd.clear()

276 changes: 275 additions & 1 deletion poetry.lock

Large diffs are not rendered by default.

32 changes: 15 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
[project]
[tool.poetry]
name = "rpilcd"
version = "0.1.0"
description = "este libreria es para el manejo de las lcd display en las raspberry pi"
authors = [
{name = "Esteban",email = "herrerazanecheverry@gmail.com"}
]
license = {text = "LGPL"}
description = "Esta librería es para el manejo de pantallas LCD en la Raspberry Pi"
authors = ["Esteban, herrerazanecheverry@gmail.com"]
license = "LGPL"
readme = "README.md"
requires-python = ">=3.10"
packages = [{ include = "src", from = "." }]
dependencies = [
"smbus (>=1.1.post2,<2.0)",
"rpi-gpio (>=0.7.1,<0.8.0)"
packages = [
{ include = "RPiLCD", from = "src" }
]
keywords = ["raspberrypi", "lcd", "i2c", "display"]

[tool.poetry.scripts]
test = "pytest:main"
[tool.poetry.dependencies]
python = ">=3.10"
smbus = ">=1.1.post2,<2.0"
rpi-gpio = ">=0.7.1,<0.8.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
pytest-cov = "^4.0"

[tool.setuptools.packages.find]
where = ["src"]
[tool.poetry.scripts]
test = "pytest:main"

[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0", "setuptools", "wheel"]
build-backend = ["poetry.core.masonry.api", "setuptools.build_meta"]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
9 changes: 5 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
name = RPiLCD
version = 0.1.0
description = Librería para controlar LCD en Raspberry Pi
long_description = file: README.md
long_description_content_type = text/markdown
long_description = file: README.rst
long_description_content_type = text/x-rst
author = Esteban
author_email = herrerazanecheverry@gmail.com
license = MIT
license = LGPLv3
keywords = raspberry, raspberry pi, lcd, liquid crystal, rpi, i2c, displayScreen
url = https://github.com/Exploit34/RPiLCD

[options]
packages = find:
python_requires = >=3.10
install_requires =
smbus2
RPi.GPIO
RPi.GPIO
11 changes: 10 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
from setuptools import setup

setup(
readme = open("README.rst").read()

setup (
name="RPiLCD",
version="0.1.0",
description="Librería para controlar LCD en Raspberry Pi",
long_description=readme,
long_description_content_type="text/x-rst",
author="Esteban",
author_email="herrerazanecheverry@gmail.com",
license="LGPLv3",
url = "https://github.com/Exploit34/RPiLCD",
keywords='raspberry, raspberry pi, lcd, liquid crystal, rpi, i2c, displayScreen',
packages=["RPiLCD"]
)
119 changes: 119 additions & 0 deletions src/RPiLCD/I2CRasp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from smbus import SMBus
from time import sleep
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

ALIGN_OPTIONS = {
'left': 'ljust',
'right': 'rjust',
'center': 'center'
}

CLEAR_DISPLAY = 0x01
ENABLE_BIT = 0b00000100
LINES = {
1: 0x80,
2: 0xC0,
3: 0x94,
4: 0xD4
}

LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

class I2C:
def __init__(self, address, width, rows, bus=1, backlight=True):
"""Inicializa el LCD con la dirección I2C, dimensiones y retroiluminación."""
self.address = address
self.bus = SMBus(bus)
self.width = width
self.rows = rows
self.backlight_status = backlight
self.delay = 0.0005

self.initialize_display()

def update_config(self, address=None, bus=None, width=None, rows=None, backlight=None):
"""Permite cambiar las variables de configuración del LCD."""
if address is not None:
self.address = address
if bus is not None:
self.bus = SMBus(bus)
if width is not None:
self.width = width
if rows is not None:
self.rows = rows
if backlight is not None:
self.backlight_status = backlight

self.initialize_display()

def initialize_display(self):
"""Envía comandos de inicialización al LCD."""
init_commands = [0x33, 0x32, 0x06, 0x0C, 0x28, CLEAR_DISPLAY]
for cmd in init_commands:
self.write(cmd)
sleep(self.delay)

def _write_byte(self, byte):
"""Escribe un byte en el bus con el modo de habilitación activado."""
try:
self.bus.write_byte(self.address, byte)
self.bus.write_byte(self.address, (byte | ENABLE_BIT))
sleep(self.delay)
self.bus.write_byte(self.address, (byte & ~ENABLE_BIT))
sleep(self.delay)
except Exception as e:
logger.error(f"Error al escribir byte: {e}")

def write(self, byte, mode=0):
"""Escribe un comando o dato al LCD, considerando el estado de retroiluminación."""
backlight_mode = LCD_BACKLIGHT if self.backlight_status else LCD_NOBACKLIGHT
self._write_byte(mode | (byte & 0xF0) | backlight_mode)
self._write_byte(mode | ((byte << 4) & 0xF0) | backlight_mode)

def display_text(self, text, line=1, align='left'):
"""Muestra texto en una línea específica con el alineamiento indicado."""
if line not in LINES:
raise ValueError("Número de línea no válido. Debe ser 1, 2, 3 o 4.")

if not isinstance(self.width, int) or self.width <= 0:
raise ValueError("Ancho inválido para la pantalla LCD.")

if not isinstance(self.rows, int) or self.rows <= 0:
raise ValueError("Número de filas inválido para la pantalla LCD.")

self.write(LINES[line])
aligned_text = getattr(text[:self.width], ALIGN_OPTIONS.get(align, 'ljust'))(self.width)

for char in aligned_text:
self.write(ord(char), mode=1)

def toggle_backlight(self, turn_on=True):
"""Activa o desactiva la retroiluminación del LCD."""
self.backlight_status = turn_on
self.write(0x00)

def _split_text(self, text):
"""Divide el texto para que quepa en líneas, intentando romper en espacios."""
words = text.split(' ')
lines = []
current_line = ''

for word in words:
if len(current_line) + len(word) + 1 <= self.width:
current_line += ' ' + word if current_line else word
else:
lines.append(current_line)
current_line = word

if current_line:
lines.append(current_line)
return lines

def clear(self):
"""Limpia la pantalla del LCD."""
self.write(CLEAR_DISPLAY)
sleep(self.delay)
Empty file added src/RPiLCD/LDCRasp.py
Empty file.
5 changes: 5 additions & 0 deletions src/RPiLCD/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .I2CRasp import I2C

__all__ = [
'I2C'
]
11 changes: 11 additions & 0 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .test_I2C import testI2C
from .test_display import testDisplay
from .test_write import testWrite
from .test_toggle_backlight import testBacklight

__all__ = [
'testI2C',
'testDisplay',
'testWrite',
'testBacklight'
]
16 changes: 16 additions & 0 deletions tests/unit/test_I2C.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import unittest
from unittest.mock import patch, MagicMock
from src.RPiLCD import I2C

class testI2C(unittest.TestCase):
@patch('src.RPiLCD.I2CRasp.SMBus')
def test_init(self, mock_smbus):
mock_bus_instance = MagicMock()
mock_smbus.return_value = mock_bus_instance

i2c = I2C(address=0x27, width=16, rows=2)
self.assertIsNotNone(i2c)
mock_smbus.assert_called_once_with(1)

if __name__ == "__main__":
unittest.main()
21 changes: 21 additions & 0 deletions tests/unit/test_display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest
from unittest.mock import patch, MagicMock
from src.RPiLCD import I2C

class testDisplay(unittest.TestCase):
@patch('src.RPiLCD.I2CRasp.SMBus')
def test_write_text(self, mock_smbus):
mock_bus_instance = MagicMock()
mock_smbus.return_value = mock_bus_instance

lcd = I2C(address=0x27, width=16, rows=2)
self.assertIsNotNone(lcd)
mock_smbus.assert_called_once_with(1)

lcd.display_text("Hello Juan", line=1, align="left")
lcd.display_text("LCD", line=2, align="center")
lcd.display_text("LCD Display", line=3, align="right")
lcd.display_text("4 buses", line=4, align="center")

if __name__ == "__main__":
unittest.main()
Loading
Loading