1
0
Fork 0

Compare commits

...

48 Commits

Author SHA1 Message Date
Israel Figueroa 82203db513 fixes 2024-03-27 19:11:25 -03:00
ifiguero 50a47d0f95 instrucciones 2024-02-19 17:23:38 -03:00
ifiguero a18c5c1784 makeitwork 2024-02-19 17:12:49 -03:00
AlonsoDiCandia 54daec5249 Generate poster using 2 buses more nearly of the bus stop 2024-01-24 23:06:02 -03:00
diegoalrv 371da77fb0 Merge branch 'pr/3' 2024-01-24 21:33:18 -03:00
AlonsoDiCandia d046c06db9 Fix paths in autorun 2023-12-28 15:18:37 -03:00
AlonsoDiCandia 53dcd69f16 Cleaning project and using Paradero class to get bus data 2023-12-28 15:00:03 -03:00
Diego Ramirez 713370b162
Merge pull request #4 from chmancilla/master
Pantalla LED + API (beta)
2023-12-27 12:13:31 -03:00
Christopher Mancilla b164e07476
Update autorun.sh 2023-12-26 13:41:52 -03:00
Christopher Mancilla 5309589a5c
Update autorun.sh 2023-12-26 13:41:28 -03:00
Christopher Mancilla 8d77225bee
Add files via upload 2023-12-26 13:40:39 -03:00
Christopher Mancilla 0d9f958762
Add files via upload 2023-12-26 13:34:26 -03:00
diegoalrv c78b5e1251 added image reescaler 2023-12-18 22:58:50 -03:00
Alonso Diaz Candia b66bd8fe0c Scripts de instalacion y primera ejecucion 2023-12-06 14:49:17 +00:00
chmancilla 659738a0c2
Update README.md 2023-11-29 17:00:10 -03:00
chmancilla bd3c37837a
Update README.md 2023-11-29 16:59:54 -03:00
chmancilla bf6d58d554
Update README.md 2023-11-29 16:58:50 -03:00
chmancilla 3721fe4e8f
Update README.md 2023-11-29 16:48:42 -03:00
chmancilla 9f728916b4
Update README.md 2023-11-29 16:48:22 -03:00
Israel de0c1ffb90
Update README.md 2023-11-29 13:57:21 -03:00
ifiguero b136bd23f4 instrucciones 2023-11-29 13:27:23 -03:00
ifiguero 2ae2b33d08 Subsistema de video 2023-11-29 13:25:29 -03:00
chmancilla af28113bf0
Update README.md 2023-11-27 21:00:02 -03:00
diegoalrv b04bbe8bf5 update example/poster.png 2023-11-22 22:43:05 -03:00
diegoalrv 0493e8dc7e update example/poster.png 2023-11-22 22:39:08 -03:00
diegoalrv 8384e4f96a update README.md 2023-11-22 22:25:21 -03:00
diegoalrv b84f990abc update README.md 2023-11-22 22:22:04 -03:00
diegoalrv f0a90c5bbf added flow-chart 2023-11-22 22:21:20 -03:00
diegoalrv 1f8e5ceee9 added flow-chart 2023-11-22 22:19:19 -03:00
diegoalrv f7ba70c96f update main README.md 2023-11-22 22:13:37 -03:00
diegoalrv d904392df9 update repo files distribution 2023-11-22 22:07:19 -03:00
diegoalrv 6ebcd057c6 update README.md 2023-11-22 22:01:29 -03:00
diegoalrv f845b5bba9 update README.md 2023-11-22 22:01:00 -03:00
diegoalrv 350464e9b2 update README.md 2023-11-22 21:57:07 -03:00
diegoalrv bc21d403af Merge branch 'master' of github.com:diegoalrv/pantallas-led 2023-11-22 21:50:45 -03:00
diegoalrv e044cf37e3 update README.md 2023-11-22 21:49:18 -03:00
chmancilla c26d426dd0
Update README.md 2023-11-22 21:18:48 -03:00
chmancilla 3ab15451ec
Update README.md 2023-11-22 19:33:25 -03:00
chmancilla 81ed1aeb72
Update README.md 2023-11-22 18:31:56 -03:00
chmancilla 03f3189c53
Update README.md 2023-11-22 18:09:33 -03:00
chmancilla af52a2c778
Update README.md 2023-11-22 17:10:44 -03:00
chmancilla 53adb29754
Update README.md 2023-11-21 12:27:44 -03:00
chmancilla 1a805479f5
Update README.md 2023-11-20 20:10:38 -03:00
chmancilla c092a8647c
Update README.md 2023-11-20 20:09:07 -03:00
chmancilla 6bbcb99272
Update README.md 2023-11-20 00:39:32 -03:00
chmancilla 13f3f98f18
Update README.md 2023-11-20 00:38:26 -03:00
chmancilla 989e838fd8
Create README.md 2023-11-14 15:38:32 -03:00
Diego Ramirez 450f263793
Merge pull request #2 from diegoalrv/modulo-led
added modulo-led @ pantallas-led
2023-11-13 19:26:22 -03:00
136 changed files with 937 additions and 297731 deletions

BIN
.DS_Store vendored 100644

Binary file not shown.

View File

@ -0,0 +1,43 @@
# Generación de Póster de Bus
En el directorio raiz del respositorio pasamos al carpeta:
```bash
cd GenPoster
```
## Construir la Imagen Docker
Primero, construye la imagen Docker que contiene todas las dependencias necesarias:
```bash
docker build -t bus_poster .
```
## Ejecutar el Contenedor Docker
Utiliza el script ```run_container.sh``` para ejecutar el contenedor. Este script monta las carpetas locales necesarias y inicia el contenedor. El contenedor se eliminará automáticamente después de su ejecución debido al parámetro ```--rm```.
```bash
./run_container.sh
```
Nota: Asegúrate de que el script ```run_container.sh``` tenga permisos de ejecución. Si no es así, ejecuta:
```bash
chmod +x run_container.sh
```
## Generación y Almacenamiento del Póster
Al ejecutar el contenedor, el script ```app.py``` se iniciará automáticamente y realizará lo siguiente:
- Calcula el tiempo restante hasta la llegada del autobús.
- Genera visualizaciones con los detalles del autobús y el tiempo restante.
- Guarda la imagen generada en una carpeta local mapeada al contenedor.
Por el momento esta funcionando con datos de prueba, eventualmente se usará como datos de entrada las respuestas del endpoint.
## Acceso a la Imagen Generada
La imagen del póster se guardará en la carpeta local especificada en el script ```run_container.sh```. Puedes acceder a ella directamente desde esta carpeta en tu máquina local.
## Ejemplo de Imagen Generada
Actualmente se están generando imagenes para alimentar un arreglo de pantallas P4 de 2x1, por lo tanto se ajustaron las dimensiones para esa configuración.
![Ejemplo de Poster](/GenPoster/example/poster.png)

View File

@ -3,26 +3,35 @@ from scripts.Poster.BusPoster import BusPoster
from scripts.Poster.TimeAnnouncement import TimeAnnouncement from scripts.Poster.TimeAnnouncement import TimeAnnouncement
import numpy as np import numpy as np
from datetime import datetime, timedelta from datetime import datetime, timedelta
from import_data import export_data
from PIL import Image
import subprocess
from getData import Paradero
import os
import time
os.environ['TZ'] = 'America/Santiago'
time.tzset()
def reescalar_imagen(input_path, output_path, nuevo_ancho, nuevo_alto):
try:
# Abrir la imagen original
imagen = Image.open(input_path)
# Reescalar la imagen
imagen_redimensionada = imagen.resize((nuevo_ancho, nuevo_alto))
# Guardar la imagen redimensionada en el nuevo archivo
imagen_redimensionada.save(output_path)
# print("Imagen redimensionada y guardada con éxito en", output_path)
except:
import traceback
print(traceback.format_exc())
def aprox(n): def aprox(n):
return int(np.round(n)) return int(np.round(n))
def load_data():
data = {
"direction": "R",
"distance": 1948.575483806973,
"epochTime": 1674650956,
"latitude": -33.43729782104492,
"licensePlate": "LJHA57",
"longitude": -70.52730560302734,
"realtime": True,
"route": "401",
"routeId": "401",
"timeLabel": "09:49",
"tripId": "401-I-L-005"
}
return data
def approx_km(data): def approx_km(data):
distance_meters = data["distance"] distance_meters = data["distance"]
distance_km = distance_meters / 100 # Convert meters to kilometers distance_km = distance_meters / 100 # Convert meters to kilometers
@ -31,7 +40,7 @@ def approx_km(data):
return approx_km return approx_km
def calc_remaining_time(data): def calc_remaining_time(data):
arrival_time = data["timeLabel"] arrival_time = data["timeLabel"][:-3]
target_time = datetime.strptime(arrival_time, "%H:%M").time() target_time = datetime.strptime(arrival_time, "%H:%M").time()
current_time = datetime.now().time() current_time = datetime.now().time()
@ -44,7 +53,9 @@ def calc_remaining_time(data):
return remaining_minutes return remaining_minutes
def obtain_min_max_time(remaining_time): def obtain_min_max_time(remaining_time):
if remaining_time == 1: if type(remaining_time) != int:
return "N/A", "N/A"
elif remaining_time == 1:
return 0, 1 return 0, 1
elif remaining_time == 2: elif remaining_time == 2:
return 1, 2 return 1, 2
@ -70,19 +81,44 @@ def obtain_min_max_time(remaining_time):
# 'letter_background_color': 'green', Color del fondo para la letra # 'letter_background_color': 'green', Color del fondo para la letra
def main(): def main():
# Carga los datos bus_stop = Paradero()
data = load_data()
# Calcula el tiempo restante a la llegada data = bus_stop.get_data()
remaining_time = calc_remaining_time(data) if data is not None:
# Obtiene valores máximos y mínimo de rangos para desplegar en pantalla data1 = data[0]
min_time, max_time = obtain_min_max_time(remaining_time) data2 = data[1]
# print(data)
# Calcula el tiempo restante a la llegada
remaining_time1 = data1['timeRemaining']
remaining_time2 = data2['timeRemaining']
# Obtiene valores máximos y mínimo de rangos para desplegar en pantalla
min_time1, max_time1 = obtain_min_max_time(remaining_time1)
min_time2, max_time2 = obtain_min_max_time(remaining_time2)
ruta1 = data1['route']
direccion1 = data1['direction']
ruta2 = data2['route']
direccion2 = data2['direction']
else:
remaining_time1 = 'N/A'
remaining_time2 = 'N/A'
min_time1 = 'N/A'
min_time2 = 'N/A'
max_time1 = 'N/A'
max_time2 = 'N/A'
ruta1 = "00"
direccion1 = "X"
ruta2 = "99"
direccion2 = "Y"
# Selecciona el tema # Selecciona el tema
theme = 'day' theme = 'night'
# Alto y ancho de la imagen en pixeles # Alto y ancho de la imagen en pixeles
height, width = 40, 160 #height, width = 40, 160
height, width = 200, 800
# Inicia el dibujo y setea el tema # Inicia el dibujo y setea el tema
full_panel = MyDraw(height=height, width=width) full_panel = MyDraw(height=height, width=width)
@ -90,20 +126,21 @@ def main():
full_panel.start_draw() full_panel.start_draw()
# Agrega el anuncio de los minutos restante al arribo # Agrega el anuncio de los minutos restante al arribo
time_anmc1 = TimeAnnouncement(aprox((2/5)*height), aprox((1/3)*width)) time_anmc1 = TimeAnnouncement(aprox((2/5)*height), aprox((2/5)*width))
time_anmc1.set_theme(theme) time_anmc1.set_theme(theme)
time_anmc1.start_draw() time_anmc1.start_draw()
# time_anmc1.set_background() # time_anmc1.set_background()
# time_anmc1.set_base_text() # time_anmc1.set_base_text()
time_anmc1.set_min_max_text(min_time=min_time, max_time=max_time) time_anmc1.set_remaining_text(data[0]['timeRemaining'])
# Agrega el anuncio de los minutos restante al arribo # Agrega el anuncio de los minutos restante al arribo
time_anmc2 = TimeAnnouncement(aprox((2/5)*height), aprox((1/3)*width)) time_anmc2 = TimeAnnouncement(aprox((2/5)*height), aprox((2/5)*width))
time_anmc2.set_theme(theme) time_anmc2.set_theme(theme)
time_anmc2.start_draw() time_anmc2.start_draw()
# time_anmc2.set_background() # time_anmc2.set_background()
# time_anmc2.set_base_text() # time_anmc2.set_base_text()
time_anmc2.set_min_max_text(min_time=3, max_time=5) # time_anmc2.set_min_max_text(min_time=min_time2, max_time=max_time2)
time_anmc2.set_remaining_text(data[1]['timeRemaining'])
# Genera la imagen de la linea del bus # Genera la imagen de la linea del bus
poster1 = BusPoster(aprox(1.1*(1/3)*height), aprox(1.1*(1/3)*width)) poster1 = BusPoster(aprox(1.1*(1/3)*height), aprox(1.1*(1/3)*width))
@ -112,19 +149,21 @@ def main():
bus_announcement_1 = { bus_announcement_1 = {
'proportion': 0.6, 'proportion': 0.6,
'width_border': 2, 'width_border': 3,
'font_size': 11, # 'font_size': 11,
'font_size': 80,
'number_background_color': 'yellow', 'number_background_color': 'yellow',
'letter_background_color': 'green', 'letter_background_color': data[0]['number_background_color'],
} }
# Se setean los parametros # Se setean los parametros
poster1.set_params(bus_announcement_1) poster1.set_params(bus_announcement_1)
poster1.load_barlow() poster1.load_barlow()
poster1.set_colors() poster1.set_colors()
# Se setea la ruta y la direccion en la que va # Se setea la ruta y la direccion en la que va
poster1.set_bus_number(bus_number=data["route"]) poster1.set_bus_number(bus_number=ruta1)
poster1.set_bus_letter(bus_letter=data["direction"]) poster1.set_bus_letter(bus_letter=direccion1)
# Genera la imagen de la linea del bus # Genera la imagen de la linea del bus
poster2 = BusPoster(aprox(1.1*(1/3)*height), aprox(1.1*(1/3)*width)) poster2 = BusPoster(aprox(1.1*(1/3)*height), aprox(1.1*(1/3)*width))
@ -133,10 +172,11 @@ def main():
bus_announcement_2 = { bus_announcement_2 = {
'proportion': 0.6, 'proportion': 0.6,
'width_border': 2, 'width_border': 3,
'font_size': 11, # 'font_size': 11,
'font_size': 80,
'number_background_color': 'yellow', 'number_background_color': 'yellow',
'letter_background_color': 'blue', 'letter_background_color': data[1]['number_background_color'],
} }
# Se setean los parametros # Se setean los parametros
@ -144,17 +184,30 @@ def main():
poster2.load_barlow() poster2.load_barlow()
poster2.set_colors() poster2.set_colors()
# Se setea la ruta y la direccion en la que va # Se setea la ruta y la direccion en la que va
poster2.set_bus_number(bus_number="16") poster2.set_bus_number(bus_number=ruta2)
poster2.set_bus_letter(bus_letter="I") poster2.set_bus_letter(bus_letter=direccion2)
# Se agregan todas las imagenes al canvas # Se agregan todas las imagenes al canvas
full_panel.add_image(time_anmc1, (aprox((0.6)*width), aprox(0.05*height))) full_panel.add_image(time_anmc1, (aprox((0.55)*width), aprox(0.05*height)))
full_panel.add_image(time_anmc2, (aprox((0.6)*width), aprox(0.45*height))) full_panel.add_image(time_anmc2, (aprox((0.55)*width), aprox(0.45*height)))
full_panel.add_image(poster1, (aprox((0.05)*width), aprox((0.1)*height))) full_panel.add_image(poster1, (aprox((0.05)*width), aprox((0.1)*height)))
full_panel.add_image(poster2, (aprox((0.05)*width), aprox((0.5)*height))) full_panel.add_image(poster2, (aprox((0.05)*width), aprox((0.5)*height)))
# full_panel.add_image(bm, (aprox(0.02*width),aprox((1/6)*height))) #full_panel.add_image(bm, (aprox(0.02*width),aprox((1/6)*height)))
full_panel.get_image() full_panel.get_image()
full_panel.save_image('/app/data/output/images/poster.png') full_panel.save_image('/srv/ledram/poster.png')
nuevo_alto = 40 # Reemplaza con el alto deseado en píxeles
nuevo_ancho = 160 # Reemplaza con el ancho deseado en píxeles
input_path = f'/srv/ledram/poster.png'
output_path = '/srv/ledram/next.png'
reescalar_imagen(input_path, output_path, nuevo_ancho, nuevo_alto)
subprocess.run(['cp', output_path, '/srv/ledram/current.png'])
from apscheduler.schedulers.blocking import BlockingScheduler
if __name__ == '__main__': if __name__ == '__main__':
main() sched = BlockingScheduler()
sched.add_job(main, 'interval', seconds=15)
sched.start()

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,122 @@
#!/usr/bin/env python3
import requests
import pytz
from datetime import datetime, timedelta
class Paradero:
def __init__(self):
# Dispositivo
self.id = "panel-pruebas-2"
# Autentificación data
self.url_auth = 'https://gestion.tdt-dev.ilab.cl/api/auth/'
self.rut = "11111111-1"
self.password = "usuario1"
# Token obtenido luego del 'login'
self.token = self.__get_token()
# print(self.token)
# URL de la API para obtener los datos de los recorridos
self.url_getinfodevice = 'https://gestion.tdt-dev.ilab.cl/api/dispositivos/getInfoDevice/'
self.data = None
self.bus_list = []
self.servicios = ["12Q", "56O"]
def __get_token(self):
auth = '''{
"rut": "11111111-1",
"password": "usuario1"
}'''
response = requests.post(self.url_auth, data=auth)
# Estado de la respuesta
if response.status_code == 200:
return response.json()['token']
else:
# print(response)
return None
def get_data(self):
# Datos para la solicitud
data_getinfodevice = {
"GetInfoDevice": {
"idDispositivo": self.id,
"KeyAuthorizacion": "tokenSinUso" #Autentificacion de mentira sisisi
}
}
if self.token is not None:
#Aquí se ingresa el token obtenido anteriormente
headers_getinfodevice = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
# Request
response = requests.post(self.url_getinfodevice, json=data_getinfodevice, headers=headers_getinfodevice)
self.data = self.__serialize_data(response)
return self.data
def __generate_bus_list(self, info):
data_main = {}
zona_horaria_santiago = pytz.timezone('America/Santiago')
hora_actual_santiago = datetime.now(zona_horaria_santiago).time()
for i in range(len(info["GetInfoDeviceResponse"]["DetalleLineas"])):
data = info["GetInfoDeviceResponse"]["DetalleLineas"][i]
if data["Descripcion"] not in self.servicios:
continue
bus_info = {}
if len(data['Llegadas']) > 0 and data["Llegadas"][0]["DistanciaGPS"] is not None:
bus_info["distance"] = data["Llegadas"][0]["DistanciaGPS"]
bus_info["timeLabel"] = data["Llegadas"][0]["EstimadaGPS"]
bus_info["patente"] = data["Llegadas"][0]["patente"]
bus_hour = datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().hour if datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().hour != 0 else 24
diff = timedelta(
hours = bus_hour - hora_actual_santiago.hour,
minutes = datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().minute - hora_actual_santiago.minute,
seconds=datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().second - hora_actual_santiago.second
)
# print(diff.total_seconds())
bus_info["timeLabel"] = "m"
if data['Llegadas'][0]['textoLlegada'].startswith("Entre"):
data['Llegadas'][0]['textoLlegada'] = data['Llegadas'][0]['textoLlegada'][5:]
cut = data['Llegadas'][0]['textoLlegada'].index("min")+3
bus_info["timeRemaining"] = data['Llegadas'][0]['textoLlegada'][:cut]
else:
bus_info["distance"] = "-"
bus_info["timeLabel"] = "Sin Info"
bus_info["timeRemaining"] = "Sin Dato"
bus_info["route"] = data["Descripcion"][:-1] if data["Descripcion"] is not None else "-"
bus_info["direction"] = data["Descripcion"][-1] if data["Descripcion"] is not None else "-"
bus_info["number_background_color"] = "#{}".format(data["colorFondo"])
bus_info["letter_background_color"] = "#{}".format(data["colorTexto"])
data_main[data["Descripcion"]] = bus_info
# data_main = sorted(data_main, key=lambda x: x['timeRemaining'])
salida = []
for servicio in self.servicios:
if servicio in data_main:
salida.append(data_main[servicio])
self.bus_list = salida
# for d in data_main:
# print(d['timeRemaining'], d['timeLabel'])
def __serialize_data(self, response):
data = response.json()
self.__generate_bus_list(data)
return self.bus_list

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python3
import requests
import json
from datetime import datetime, timedelta
def api_request():
# URL de la API para "autentificación"
url_auth = 'https://transporte.hz.kursor.cl/api/auth/'
# Datos para autentificar
auth = '''{
"username": "usuario1",
"password": "usuario1"
}'''
# Request
token = requests.post(url_auth, data=auth)
token = token.json()['token']
# URL de la API para info del paradero
url_whoami = 'https://transporte.hz.kursor.cl/api/dispositivos/whoami/'
# Datos de la solicitud
data_whoami = {
"whoami": {
"idDispositivo": "pled30-gtr",
"KeyAuthorizacion": "token"
}
}
#Aquí se ingresa el token obtenido anteriormente
headers_whoami = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
response_whoami = requests.post(url_whoami, json=data_whoami, headers=headers_whoami) #Request
Paradero = response_whoami.json()
url_getinfodevice = 'https://transporte.hz.kursor.cl/api/dispositivos/getInfoDevice/' # URL de la API para obtener los datos de los recorridos
# Datos para la solicitud
data_getinfodevice = {
"GetInfoDevice": {
"idDispositivo": "00000000160f3b42b8:27:eb:0f:3b:42",
"KeyAuthorizacion": "tokenSinUso"
}
}
#Aquí se ingresa el token obtenido anteriormente
headers_getinfodevice = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
# Request
response_getinfodevice = requests.post(url_getinfodevice, json=data_getinfodevice, headers=headers_getinfodevice)
info = response_getinfodevice.json()
return info
#--------------------------------------------------------------------------------------------------------------------------
#Haciendo una lista de todos los buses de este paradero
def lista_buses(info):
data_main = []
hora_actual = datetime.now().time()
for i in range(len(info["GetInfoDeviceResponse"]["DetalleLineas"])):
data = info["GetInfoDeviceResponse"]["DetalleLineas"][i]
bus_info = {}
bus_info["distance"] = data["Llegadas"][0]["DistanciaGPS"] if data["Llegadas"][0]["DistanciaGPS"] is not None else "-"
bus_info["timeLabel"] = data["Llegadas"][0]["EstimadaGPS"] if data["Llegadas"][0]["EstimadaGPS"] is not None else "-"
bus_info["route"] = data["Descripcion"][:-1] if data["Descripcion"] is not None else "-"
bus_info["direction"] = data["Descripcion"][-1] if data["Descripcion"] is not None else "-"
bus_info["number_background_color"] = data["colorFondo"]
bus_info["letter_background_color"] = data["colorTexto"]
bus_info["patente"] = data["Llegadas"][0]["patente"]
diff = timedelta(hours = datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().hour - hora_actual.hour,minutes = datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().minute - hora_actual.minute,seconds=datetime.strptime(bus_info["timeLabel"], "%H:%M:%S").time().second - hora_actual.second)
bus_info["timeRemaining"] = int(abs(diff.total_seconds() // 60))
data_main.append(bus_info)
return data_main
#--------------------------------------------------------------------------------------------------------------------------------
#Exportando datos
def export_data():
X = api_request()
data_main = lista_buses(X)
data_time = sorted(data_main, key=lambda x: x['timeRemaining'],reverse=True)
data_x = (data_time[0],data_time[1])
return data_x

View File

@ -0,0 +1,28 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
echo "Este script requiere permisos de root."
exit
fi
# set pwd to current directory
cd "$(dirname "$0")"
#limpia el contenido del directorio de trabajo
rm -rf /srv/parada*
mkdir /srv/parada_led
cp -rf *.py assets scripts /srv/parada_led
#Crea el servicio
cp -rf parada_led.service /etc/systemd/system/parada_led.service
# Recarga e inicia automaticamente al prender.
systemctl daemon-reload
systemctl unmask parada_led.service
systemctl enable parada_led.service
# Mensajes de salida
echo "Debe reiniciar la Raspberry para que el servicio pueda iniciarse"
echo "Luego para actualizar, solo debe modificar el el archivo '/srv/ledram/current.png' para actualizar la pantalla"

View File

@ -0,0 +1,12 @@
[Unit]
Description=WebService Datos Parada
After=local-fs.target
[Service]
User=root
WorkingDirectory=/srv/parada_led
ExecStart=/usr/bin/python3 /srv/parada_led/app.py
Restart=always
[Install]
WantedBy=multi-user.target

View File

@ -2,3 +2,4 @@ matplotlib
requests requests
Pillow Pillow
numpy numpy
pytz

View File

@ -6,7 +6,6 @@ data_path=$project_folder_path/data
scripts_path=$project_folder_path/scripts scripts_path=$project_folder_path/scripts
assets_path=$project_folder_path/assets assets_path=$project_folder_path/assets
# Ejecuta el contenedor con el enlace de carpeta local # Ejecuta el contenedor con el enlace de carpeta local
docker run --rm -d -p 8888:8888 --name make_poster -v $data_path:/app/data -v $assets_path:/app/assets -v $scripts_path:/app/scripts bus_poster # docker run --rm -d -p 8888:8888 --name make_poster -v $data_path:/app/data -v $assets_path:/app/assets -v $scripts_path:/app/scripts -v $project_folder_path:/app bus_poster
docker run --rm -d --name make_poster -v $project_folder_path:/app bus_poster

View File

@ -37,10 +37,12 @@ class BusPoster(MyDraw):
def set_bus_number(self, bus_number="11"): def set_bus_number(self, bus_number="11"):
text_color = 'black' text_color = 'black'
width_border = self.prms['width_border'] width_border = self.prms['width_border']
# width_border = 0
text_bbox = self.font.getbbox(str(bus_number)) text_bbox = self.font.getbbox(str(bus_number))
font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1] font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
offset_width = np.round((self.prms['proportion']*self.width-width_border)/2) - np.round(font_width/2) offset_width = np.round((self.prms['proportion']*self.width-width_border)/2) - np.round(font_width/2)
text_position = (offset_width,0) # offset_width = 0
text_position = (offset_width,-15)
self.draw.text( self.draw.text(
text_position, text_position,
bus_number, bus_number,
@ -57,7 +59,7 @@ class BusPoster(MyDraw):
text_bbox = self.font.getbbox(str(bus_letter)) text_bbox = self.font.getbbox(str(bus_letter))
font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1] font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
offset_width = np.round((proportion*self.width-width_border)) + 0.75*np.round(font_width/2) offset_width = np.round((proportion*self.width-width_border)) + 0.75*np.round(font_width/2)
text_position = (1.1*offset_width,0) text_position = (1.1*offset_width,-15)
self.draw.text( self.draw.text(
text_position, text_position,
bus_letter, bus_letter,

View File

@ -65,7 +65,8 @@ class MyDraw():
def load_barlow(self, font_size=None): def load_barlow(self, font_size=None):
# Ruta a la fuente TTF personalizada # Ruta a la fuente TTF personalizada
font_path = "/app/assets/fonts/Barlow-Medium.ttf" # font_path = "../../assets/fonts/Barlow-Medium.ttf"
font_path = "assets/fonts/Barlow-Medium.ttf"
# Carga la fuente # Carga la fuente
if font_size is None: if font_size is None:
self.font = ImageFont.truetype(font_path, self.prms['font_size']) self.font = ImageFont.truetype(font_path, self.prms['font_size'])

View File

@ -45,25 +45,49 @@ class TimeAnnouncement(MyDraw):
text = "Tiempo aprox" text = "Tiempo aprox"
text_color = self.theme_params['text_color'] text_color = self.theme_params['text_color']
self.load_barlow(font_size=11) self.load_barlow(font_size=70)
text_bbox = self.font.getbbox(text) text_bbox = self.font.getbbox(text)
base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1] base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
if (int(max_time) <= 1): try:
text = "< 1 min" if (int(max_time) <= 1):
elif (int(min_time) >= 10): text = "< 1 min"
text = "> 10 min" elif (int(min_time) >= 10):
else: print(max_time)
text = f'{min_time} a {max_time} min' text = f"> {max_time} min"
else:
text = f'{min_time} a {max_time} min'
except:
text = 'N/A'
self.load_barlow(font_size=12) self.load_barlow(font_size=70)
text_bbox = self.font.getbbox(text) text_bbox = self.font.getbbox(text)
font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1] font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
# print(font_width, font_height) # print(font_width, font_height)
offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2 offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2
offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2 offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2
# text_position = (offset_width,5+offset_height) # text_position = (offset_width,5+offset_height)
text_position = (offset_width,offset_height) text_position = (offset_width,offset_height-10)
# text_position = (0, 0)
self.draw.text(
text_position,
text,
fill=text_color,
font=self.font,
align ="center"
)
def set_remaining_text(self, text):
text_color = self.theme_params['text_color']
self.load_barlow(font_size=70)
text_bbox = self.font.getbbox(text)
font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
# print(font_width, font_height)
offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2
offset_height = (np.round((self.height-self.border)) - np.round(font_height))/2
# text_position = (offset_width,5+offset_height)
text_position = (offset_width,offset_height-10)
# text_position = (0, 0) # text_position = (0, 0)
self.draw.text( self.draw.text(
text_position, text_position,

View File

@ -1,92 +0,0 @@
# TODO: This contains a lot of {c|p}ython build boilerplate, this needs cleanup.
PYTHON ?= python
SETUP := setup.py
BUILD_ARGS := build --build-lib .
INST_ARGS := install
ifdef DESTDIR
INST_ARGS += --root=$(DESTDIR)
endif
CLEAN_ARGS := clean --all
MANPAGES := $(patsubst %.txt,%,$(wildcard *.txt))
TXTTOMAN := a2x -f manpage
# Where our library resides. It is split between includes and the binary
# library in lib
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
ifneq "$(wildcard debian/changelog)" ""
PKGNAME := $(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
VERSION := $(shell dpkg-parsechangelog | sed -n 's/^Version: \([^-]*\).*/\1/p')
UPSDIST := $(PKGNAME)-$(VERSION).tar.gz
DEBDIST := $(PKGNAME)_$(VERSION).orig.tar.gz
endif
all: build
build: build-python
install: install-python
clean: clean-python
find ./rgbmatrix -type f -name \*.so -delete
find . -type f -name \*.pyc -delete
$(RM) build-* install-* test-*
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
test: test-python
test-python:
ifneq "$(wildcard tests/*.py)" ""
nosetests -v -w tests
else
$(info Test suite is not implemented...)
endif
ifneq "$(wildcard debian/control)" ""
PYVERS := $(shell pyversions -r -v debian/control)
PYEXEC := $(shell pyversions -d)
BUILD_ARGS += --executable=/usr/bin/$(PYEXEC)
INST_ARGS += --no-compile -O0
build-python: $(PYVERS:%=build-python-%)
build-python-%: $(RGB_LIBRARY)
$(info * Doing build for $(PYTHON)$* ...)
$(PYTHON)$* $(SETUP) $(BUILD_ARGS)
install-python: $(PYVERS:%=install-python-%)
install-python-%:
$(info * Doing install for $(PYTHON)$* ...)
$(PYTHON)$* $(SETUP) $(INST_ARGS)
clean-python: $(PYVERS:%=clean-python-%)
clean-python-%:
$(PYTHON)$* $(SETUP) $(CLEAN_ARGS)
else
build-python: $(RGB_LIBRARY)
$(PYTHON) $(SETUP) $(BUILD_ARGS)
install-python:
$(PYTHON) $(SETUP) $(INST_ARGS)
clean-python:
$(PYTHON) $(SETUP) $(CLEAN_ARGS)
endif
distclean: clean
dist: distclean
$(info * Creating ../$(UPSDIST) and ../$(DEBDIST))
@tar --exclude='.svn' \
--exclude='*.swp' \
--exclude='debian' \
-czvf ../$(UPSDIST) \
-C ../ $(notdir $(CURDIR))
@cp ../$(UPSDIST) ../$(DEBDIST)
@if test -d ../tarballs; then \
mv -v ../$(DEBDIST) ../tarballs; \
fi
FORCE:
.PHONY: FORCE
.PHONY: build install test clean dist distclean
.PHONY: build-python install-python clean-python

View File

@ -1,23 +0,0 @@
#!/bin/bash
cd samples
echo "Este es un Menú para controlar Módulos LED"
echo "¿Que acción desea realizar?:"
echo "1. Editar código base"
echo "2. Desplegar Imagen"
read N
if [ $N -eq 1 ]
then
nano image-viewer.py
else
ls
echo "Ingrese el nombre del archivo que desea desplegar:"
read filename
sudo python3 image-viewer.py $filename
fi

View File

@ -0,0 +1,77 @@
# Guía de configuración para Modulo LED
## Materiales Necesarios
- Raspberry Pi, versión 3 o superior.
- Hat de conexión HUB75
- Paneles LED P4 con I/O HUB75
- Fuente de alimentación con salida 5V/3A
- Cables de Alimentación 4-pin 5V/3A
- Cables de datos con conexión HUB75
## Configuración inicial
### Conexionado
Para las conexiones de datos, se utilizan cables hembra HUB75 de 16-pines, estos se conectan en un extremo al HAT HUB75 para Raspberry PI. En caso de tener un HAT con multiples conexiones de HUB75, siempre utilizar la salida TOP para la primera fila del conjunto de paneles, y usar el resto de las salidas para las filas inferiores. El extremo opuesto del cable de datos debe conectarse en el "input" del panel, estos modulos tienen etiquetada su entrada y salida en la parte posterior. Ver las imagenes mostradas como referencia visual.
Para la conexión electrica, cada uno de los paneles LED tiene una entrada de cuatro pines para cables de alimentación. Es necesario tener una fuente de poder con salida de 5V y un minimo de 3A, para cumplir con los criterios de alimentación en los modulos LED.
![HUB75 conector](https://github.com/diegoalrv/pantallas-led/assets/148826389/5c03ffe1-eca1-42b8-bd50-4efc3d170ed7) ![HUB75 input](https://github.com/diegoalrv/pantallas-led/assets/148826389/f153e6ce-ce5e-4b19-8e2d-45ffa2d77037) ![Raspi HAT](https://github.com/diegoalrv/pantallas-led/assets/148826389/8b40b730-42ae-4416-929d-cd32de8903ee)
A continuación, se presenta un diagrama de conexiones, tanto para los datos como para la alimentación. Notar que la referencia (0,0) corresponde a la orientación superior izquierda de las gráficas que se deseen desplegar en el módulo, también se señala la orientación de las entradas y salidas de cada panel en la configuración.
![conexionModLED](https://github.com/diegoalrv/pantallas-led/assets/148826389/782bac34-8173-4207-a9f7-df2b5422b9ca.png)
### Configuración de Raspberry Pi
Luego de haber descargado los datos almancenados en este reposotorio. En el directorio ModuloLED, encontrará un script llamado `MenuPantalla.sh`, en este menú se puede hacer dos acciones en concreto:
- Configurar parámetros de la implementación, modificando variables del codigo base.
- Desplegar una imagen a partir de un archivo imagen jpg o png almacenada en el sistema.
#### Configuración de Parámetros
Entre las opciones de configuración en el codigo base para el funcionamiento del panel se muestran en la siguiente tabla:
| Parámetro | Variable código base | Rango de valores |
| ------------ | ------------ | ------------ |
| N° de filas de pixeles en un panel. | `options.rows` | PANEL |
| N° de columnas de pixeles en un panel. | `options.col` | PANEL |
| N° de filas de paneles montados | `options.parallel` | 1 - 3 |
| N° de columnas de paneles montados | `options.chain_length` | 1 - 3 |
| Brillo | `options.brightness` | 0 - 100 |
| Mapeo GPIO para HAT | `options.hardware_mapping` | `regular`, ver otras opciones [aquí](https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/wiring.md#alternative-hardware-mappings) |
| Multiplexación | `options.multiplexing` | 1 (por defecto) - 17|
| Retardo GPIO | `options.gpio_slowdown` | 1 - 5 |
**PANEL: Dado por el fabricante del panel comprado.**
## Despliegue de imagenes
Al seleccionar esta opción, se mostrarán todos los archivos de imagen guardados en el sistema de la Raspberry Pi. Se recomienda utilizar imagenes con la mimsa relacion de aspecto que la resolución de la pantalla montada para el despliegue. De lo contrario, la pantalla tendrá espacios en negro.
### Variables del Sistema Operativo
Se propone un Sub-sistema que se encargue de manera dedicada al renderizado en la Matriz LED en forma continua. Para eso se implementa un *demonio* de Linux que esta continuamente dibujando en el display.
Para ello se reserva el procesador 3, entregandoselele la `CPUAffinity=3` al proceso, de manera de garantizar el recurso computacional. Adicionalmente se modifica el Sistema Operativo para que quite el procesador del itineradoe usando `isocpus=3` en la variable de inicio `/boot/cmdline.txt`.
Además se monta un directorio de `/srv/ledram` que tiene un tamaño de 32MB para disponer una *memoria de video* en RAM que permita a otros procesos actualizar el contenido que se despliega en el display. Para ello se modifica el `/etc/fstab` para que se cree el recurso automaticamente al iniciarse la Raspberry.
#### Sub-sistema de renderizado
Se define el directorio `/srv` donde se aloja el sub-sistema de renderizado. Esta compuesto del script `/srv/subsystem/led-driver.py` que es iniciado en forma automática por `systemd`.
El script ve la hora de modificación del archivo `/srv/ledram/current.png` para determinar si debe o no actualizar la imagen que se está desplegando actualmente en el display cada 100 ms.
De esta manera, cualquier usuario o proceso (`chmod 666`) puede escribir ese archivo. Siendo este sub sistema el encargado de leer el contenido de la imagen y renderizarlo en el display led en forma permanente.
#### Instalación como servicio
Todos estos pasos están automatizados en el script `install-service.sh` que debe ser ejecutado como `root`.
#### TODO
Falta que el script detecte cuando se le solicita salir, para que elimine el archivo `/srv/ledram/current.png`, de tal manera de poder `systemctl stop led-driver.service` y `systemctl start led-driver.service` sin depender que la imagen se elimine en forma automatica al ser un directorio volatil

View File

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
__version__ = "0.0.1"
__author__ = "Christoph Friedrich <christoph.friedrich@vonaffenfels.de>"
from .core import RGBMatrix, FrameCanvas, RGBMatrixOptions

BIN
ModuloLED/init.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,44 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
echo "Este script requiere permisos de root."
exit
fi
# set pwd to current directory
cd "$(dirname "$0")"
#limpia el contenido del directorio de trabajo
rm -rf /srv/ledram/*
rm -rf /srv/*
# Configura un directorio `/srv/ledram` como buffer de video
sed -i -e '/srv/d' /etc/fstab
sed -i -e '$a/tmpfs /srv/ledram tmpfs rw,nosuid,nodev,size=32m 0 0' /etc/fstab
# Crea directorio donde se almacena el buffer de video
mkdir /srv/ledram
# Desocupa el tercer procesador para ser usado exclusivamente por el sub-proceso de renderizado
sed -i -e 's/ isocpus=3//g' /boot/cmdline.txt
sed -i -e 's/$/ isocpus=3/' /boot/cmdline.txt
#copia la biblioteca al directorio de trabajo
cp -R /home/raspi/rpi-rgb-led-matrix/bindings/python/rgbmatrix /srv/rgbmatrix
#copia el sub-sistema de renderizado
mkdir /srv/subsystem
cp -rf init.png /srv
cp -rf led-driver.py /srv/subsystem
#Crea el servicio
cp -rf led-driver.service /etc/systemd/system/led-driver.service
# Recarga e inicia automaticamente al prender.
systemctl daemon-reload
systemctl unmask led-driver.service
systemctl enable led-driver.service
# Mensajes de salida
echo "Debe reiniciar la Raspberry para que el servicio pueda iniciarse"
echo "Luego para actualizar, solo debe modificar el el archivo '/srv/ledram/current.png' para actualizar la pantalla"

View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
import time
import sys
import os
import shutil
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 40
options.cols = 80
options.chain_length = 2
options.parallel = 1
options.gpio_slowdown = 4
#options.row_address_type = 0
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
options.multiplexing = 1
options.brightness = 100
#options.pwm_lsb_nanoseconds = 300
#options.pwm_bits = 11
matrix = RGBMatrix(options = options)
#Bufer que se copia en la pantalla led
img_path='/srv/ledram/current.png'
#imagen por defecto a mostrar al inicializarla
init_file='/srv/init.png'
# Revisa si el bufer existe, si no existe lo crea
# y si existe sale ya que hay otro proceso que lo
if not os.path.isfile(img_path):
shutil.copy(init_file, img_path)
os.chmod(img_path, 0o666)
else:
print("El archivo de buffer ya existe!")
exit(1)
#guarda el tiempo de modificación
tstam = os.stat(img_path).st_mtime
with Image.open(img_path) as image:
matrix.SetImage(image.convert('RGB'))
#matrix.SetImage(Image.open(img_path).convert('RGB'))
while True:
time.sleep(0.1)
ntstam = os.stat(img_path).st_mtime
#si el bufer fue modificado, lo carga en la pantalla led
if ntstam > tstam:
with Image.open(img_path) as image:
matrix.SetImage(image.convert('RGB'))
tstam = ntstam

View File

@ -0,0 +1,14 @@
[Unit]
Description=LED Rendering Service
After=local-fs.target
[Service]
User=root
CPUAffinity=3
Nice=-20
WorkingDirectory=/srv
ExecStart=/usr/bin/python3 /srv/subsystem/led-driver.py
Restart=always
[Install]
WantedBy=multi-user.target

View File

@ -1,15 +0,0 @@
# The *.cpp files are included in the distribution; this is only needed when
# working on the pyx files.
#
# Please check in modified *.cpp files with distribution to not require cython
# to be installed on the users' machine.
# for python3: make PYTHON=$(which python3) CYTHON=$(which cython3)
CYTHON ?= cython
all : core.cpp graphics.cpp
%.cpp : %.pyx
$(CYTHON) --cplus -o $@ $^
clean:
rm -rf core.cpp graphics.cpp

View File

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
__version__ = "0.0.1"
__author__ = "Christoph Friedrich <christoph.friedrich@vonaffenfels.de>"
from .core import RGBMatrix, FrameCanvas, RGBMatrixOptions

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
cimport cppinc
cdef class Canvas:
cdef cppinc.Canvas *__getCanvas(self) except +
cdef class FrameCanvas(Canvas):
cdef cppinc.FrameCanvas *__canvas
cdef class RGBMatrix(Canvas):
cdef cppinc.RGBMatrix *__matrix
cdef class RGBMatrixOptions:
cdef cppinc.Options __options
cdef cppinc.RuntimeOptions __runtime_options
# Must keep a reference to the encoded bytes for the strings,
# otherwise, when the Options struct is used, it will be garbage collected
cdef bytes __py_encoded_hardware_mapping
cdef bytes __py_encoded_led_rgb_sequence
cdef bytes __py_encoded_pixel_mapper_config
cdef bytes __py_encoded_panel_type
# Local Variables:
# mode: python
# End:

View File

@ -1,272 +0,0 @@
# distutils: language = c++
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t, uintptr_t
from PIL import Image
import cython
cdef class Canvas:
cdef cppinc.Canvas* __getCanvas(self) except +:
raise Exception("Not implemented")
def SetImage(self, image, int offset_x = 0, int offset_y = 0, unsafe=True):
if (image.mode != "RGB"):
raise Exception("Currently, only RGB mode is supported for SetImage(). Please create images with mode 'RGB' or convert first with image = image.convert('RGB'). Pull requests to support more modes natively are also welcome :)")
if unsafe:
#In unsafe mode we directly access the underlying PIL image array
#in cython, which is considered unsafe pointer accecss,
#however it's super fast and seems to work fine
#https://groups.google.com/forum/#!topic/cython-users/Dc1ft5W6KM4
img_width, img_height = image.size
self.SetPixelsPillow(offset_x, offset_y, img_width, img_height, image)
else:
# First implementation of a SetImage(). OPTIMIZE_ME: A more native
# implementation that directly reads the buffer and calls the underlying
# C functions can certainly be faster.
img_width, img_height = image.size
pixels = image.load()
for x in range(max(0, -offset_x), min(img_width, self.width - offset_x)):
for y in range(max(0, -offset_y), min(img_height, self.height - offset_y)):
(r, g, b) = pixels[x, y]
self.SetPixel(x + offset_x, y + offset_y, r, g, b)
@cython.boundscheck(False)
@cython.wraparound(False)
def SetPixelsPillow(self, int xstart, int ystart, int width, int height, image):
cdef cppinc.FrameCanvas* my_canvas = <cppinc.FrameCanvas*>self.__getCanvas()
cdef int frame_width = my_canvas.width()
cdef int frame_height = my_canvas.height()
cdef int row, col
cdef uint8_t r, g, b
cdef uint32_t **image_ptr
cdef uint32_t pixel
image.load()
ptr_tmp = dict(image.im.unsafe_ptrs)['image32']
image_ptr = (<uint32_t **>(<uintptr_t>ptr_tmp))
for col in range(max(0, -xstart), min(width, frame_width - xstart)):
for row in range(max(0, -ystart), min(height, frame_height - ystart)):
pixel = image_ptr[row][col]
r = (pixel ) & 0xFF
g = (pixel >> 8) & 0xFF
b = (pixel >> 16) & 0xFF
my_canvas.SetPixel(xstart+col, ystart+row, r, g, b)
cdef class FrameCanvas(Canvas):
def __dealloc__(self):
if <void*>self.__canvas != NULL:
self.__canvas = NULL
cdef cppinc.Canvas* __getCanvas(self) except *:
if <void*>self.__canvas != NULL:
return self.__canvas
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
(<cppinc.FrameCanvas*>self.__getCanvas()).Fill(red, green, blue)
def Clear(self):
(<cppinc.FrameCanvas*>self.__getCanvas()).Clear()
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
(<cppinc.FrameCanvas*>self.__getCanvas()).SetPixel(x, y, red, green, blue)
property width:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).width()
property height:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).height()
property pwmBits:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).pwmbits()
def __set__(self, pwmBits): (<cppinc.FrameCanvas*>self.__getCanvas()).SetPWMBits(pwmBits)
property brightness:
def __get__(self): return (<cppinc.FrameCanvas*>self.__getCanvas()).brightness()
def __set__(self, val): (<cppinc.FrameCanvas*>self.__getCanvas()).SetBrightness(val)
cdef class RGBMatrixOptions:
def __cinit__(self):
self.__options = cppinc.Options()
self.__runtime_options = cppinc.RuntimeOptions()
# RGBMatrix::Options properties
property hardware_mapping:
def __get__(self): return self.__options.hardware_mapping
def __set__(self, value):
self.__py_encoded_hardware_mapping = value.encode('utf-8')
self.__options.hardware_mapping = self.__py_encoded_hardware_mapping
property rows:
def __get__(self): return self.__options.rows
def __set__(self, uint8_t value): self.__options.rows = value
property cols:
def __get__(self): return self.__options.cols
def __set__(self, uint32_t value): self.__options.cols = value
property chain_length:
def __get__(self): return self.__options.chain_length
def __set__(self, uint8_t value): self.__options.chain_length = value
property parallel:
def __get__(self): return self.__options.parallel
def __set__(self, uint8_t value): self.__options.parallel = value
property pwm_bits:
def __get__(self): return self.__options.pwm_bits
def __set__(self, uint8_t value): self.__options.pwm_bits = value
property pwm_lsb_nanoseconds:
def __get__(self): return self.__options.pwm_lsb_nanoseconds
def __set__(self, uint32_t value): self.__options.pwm_lsb_nanoseconds = value
property brightness:
def __get__(self): return self.__options.brightness
def __set__(self, uint8_t value): self.__options.brightness = value
property scan_mode:
def __get__(self): return self.__options.scan_mode
def __set__(self, uint8_t value): self.__options.scan_mode = value
property multiplexing:
def __get__(self): return self.__options.multiplexing
def __set__(self, uint8_t value): self.__options.multiplexing = value
property row_address_type:
def __get__(self): return self.__options.row_address_type
def __set__(self, uint8_t value): self.__options.row_address_type = value
property disable_hardware_pulsing:
def __get__(self): return self.__options.disable_hardware_pulsing
def __set__(self, value): self.__options.disable_hardware_pulsing = value
property show_refresh_rate:
def __get__(self): return self.__options.show_refresh_rate
def __set__(self, value): self.__options.show_refresh_rate = value
property inverse_colors:
def __get__(self): return self.__options.inverse_colors
def __set__(self, value): self.__options.inverse_colors = value
property led_rgb_sequence:
def __get__(self): return self.__options.led_rgb_sequence
def __set__(self, value):
self.__py_encoded_led_rgb_sequence = value.encode('utf-8')
self.__options.led_rgb_sequence = self.__py_encoded_led_rgb_sequence
property pixel_mapper_config:
def __get__(self): return self.__options.pixel_mapper_config
def __set__(self, value):
self.__py_encoded_pixel_mapper_config = value.encode('utf-8')
self.__options.pixel_mapper_config = self.__py_encoded_pixel_mapper_config
property panel_type:
def __get__(self): return self.__options.panel_type
def __set__(self, value):
self.__py_encoded_panel_type = value.encode('utf-8')
self.__options.panel_type = self.__py_encoded_panel_type
property pwm_dither_bits:
def __get__(self): return self.__options.pwm_dither_bits
def __set__(self, uint8_t value): self.__options.pwm_dither_bits = value
property limit_refresh_rate_hz:
def __get__(self): return self.__options.limit_refresh_rate_hz
def __set__(self, value): self.__options.limit_refresh_rate_hz = value
# RuntimeOptions properties
property gpio_slowdown:
def __get__(self): return self.__runtime_options.gpio_slowdown
def __set__(self, uint8_t value): self.__runtime_options.gpio_slowdown = value
property daemon:
def __get__(self): return self.__runtime_options.daemon
def __set__(self, uint8_t value): self.__runtime_options.daemon = value
property drop_privileges:
def __get__(self): return self.__runtime_options.drop_privileges
def __set__(self, uint8_t value): self.__runtime_options.drop_privileges = value
cdef class RGBMatrix(Canvas):
def __cinit__(self, int rows = 0, int chains = 0, int parallel = 0,
RGBMatrixOptions options = None):
# If RGBMatrixOptions not provided, create defaults and set any optional
# parameters supplied
if options == None:
options = RGBMatrixOptions()
if rows > 0:
options.rows = rows
if chains > 0:
options.chain_length = chains
if parallel > 0:
options.parallel = parallel
self.__matrix = cppinc.CreateMatrixFromOptions(options.__options,
options.__runtime_options)
def __dealloc__(self):
self.__matrix.Clear()
del self.__matrix
cdef cppinc.Canvas* __getCanvas(self) except *:
if <void*>self.__matrix != NULL:
return self.__matrix
raise Exception("Canvas was destroyed or not initialized, you cannot use this object anymore")
def Fill(self, uint8_t red, uint8_t green, uint8_t blue):
self.__matrix.Fill(red, green, blue)
def SetPixel(self, int x, int y, uint8_t red, uint8_t green, uint8_t blue):
self.__matrix.SetPixel(x, y, red, green, blue)
def Clear(self):
self.__matrix.Clear()
def CreateFrameCanvas(self):
return __createFrameCanvas(self.__matrix.CreateFrameCanvas())
# The optional "framerate_fraction" parameter allows to choose which
# multiple of the global frame-count to use. So it slows down your animation
# to an exact integer fraction of the refresh rate.
# Default is 1, so immediately next available frame.
# (Say you have 140Hz refresh rate, then a value of 5 would give you an
# 28Hz animation, nicely locked to the refresh-rate).
# If you combine this with RGBMatrixOptions.limit_refresh_rate_hz you can create
# time-correct animations.
def SwapOnVSync(self, FrameCanvas newFrame, uint8_t framerate_fraction = 1):
return __createFrameCanvas(self.__matrix.SwapOnVSync(newFrame.__canvas, framerate_fraction))
property luminanceCorrect:
def __get__(self): return self.__matrix.luminance_correct()
def __set__(self, luminanceCorrect): self.__matrix.set_luminance_correct(luminanceCorrect)
property pwmBits:
def __get__(self): return self.__matrix.pwmbits()
def __set__(self, pwmBits): self.__matrix.SetPWMBits(pwmBits)
property brightness:
def __get__(self): return self.__matrix.brightness()
def __set__(self, brightness): self.__matrix.SetBrightness(brightness)
property height:
def __get__(self): return self.__matrix.height()
property width:
def __get__(self): return self.__matrix.width()
cdef __createFrameCanvas(cppinc.FrameCanvas* newCanvas):
canvas = FrameCanvas()
canvas.__canvas = newCanvas
return canvas
# Local Variables:
# mode: python
# End:

View File

@ -1,88 +0,0 @@
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t
########################
### External classes ###
########################
cdef extern from "canvas.h" namespace "rgb_matrix":
cdef cppclass Canvas:
int width()
int height()
void SetPixel(int, int, uint8_t, uint8_t, uint8_t) nogil
void Clear() nogil
void Fill(uint8_t, uint8_t, uint8_t) nogil
cdef extern from "led-matrix.h" namespace "rgb_matrix":
cdef cppclass RGBMatrix(Canvas):
bool SetPWMBits(uint8_t)
uint8_t pwmbits()
void set_luminance_correct(bool)
bool luminance_correct()
void SetBrightness(uint8_t)
uint8_t brightness()
FrameCanvas *CreateFrameCanvas()
FrameCanvas *SwapOnVSync(FrameCanvas*, uint8_t)
cdef cppclass FrameCanvas(Canvas):
bool SetPWMBits(uint8_t)
uint8_t pwmbits()
void SetBrightness(uint8_t)
uint8_t brightness()
struct RuntimeOptions:
RuntimeOptions() except +
int gpio_slowdown
int daemon
int drop_privileges
RGBMatrix *CreateMatrixFromOptions(Options &options, RuntimeOptions runtime_options)
cdef extern from "led-matrix.h" namespace "rgb_matrix::RGBMatrix":
cdef struct Options:
Options() except +
const char *hardware_mapping
int rows
int cols
int chain_length
int parallel
int pwm_bits
int pwm_lsb_nanoseconds
int brightness
int scan_mode
int row_address_type
int multiplexing
int pwm_dither_bits
int limit_refresh_rate_hz
bool disable_hardware_pulsing
bool show_refresh_rate
bool inverse_colors
const char *led_rgb_sequence
const char *pixel_mapper_config
const char *panel_type
cdef extern from "graphics.h" namespace "rgb_matrix":
cdef struct Color:
Color(uint8_t, uint8_t, uint8_t) except +
uint8_t r
uint8_t g
uint8_t b
cdef cppclass Font:
Font() except +
bool LoadFont(const char*)
int height()
int baseline()
int CharacterWidth(uint32_t)
int DrawGlyph(Canvas*, int, int, const Color, uint32_t);
cdef int DrawText(Canvas*, const Font, int, int, const Color, const char*)
cdef void DrawCircle(Canvas*, int, int, int, const Color)
cdef void DrawLine(Canvas*, int, int, int, int, const Color)

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
cimport cppinc
cdef class Color:
cdef cppinc.Color __color
cdef class Font:
cdef cppinc.Font __font
# Local Variables:
# mode: python
# End:

View File

@ -1,54 +0,0 @@
# distutils: language = c++
from libcpp cimport bool
from libc.stdint cimport uint8_t, uint32_t
cimport core
cdef class Color:
def __init__(self, uint8_t red = 0, uint8_t green = 0, uint8_t blue = 0):
self.__color.r = red
self.__color.g = green
self.__color.b = blue
property red:
def __get__(self): return self.__color.r
def __set__(self, uint8_t value): self.__color.r = value
property green:
def __get__(self): return self.__color.g
def __set__(self, uint8_t value): self.__color.g = value
property blue:
def __get__(self): return self.__color.b
def __set__(self, uint8_t value): self.__color.b = value
cdef class Font:
def CharacterWidth(self, uint32_t char):
return self.__font.CharacterWidth(char)
def LoadFont(self, file):
if (not self.__font.LoadFont(file.encode('utf-8'))):
raise Exception("Couldn't load font " + file)
def DrawGlyph(self, core.Canvas c, int x, int y, Color color, uint32_t char):
return self.__font.DrawGlyph(c.__getCanvas(), x, y, color.__color, char)
property height:
def __get__(self): return self.__font.height()
property baseline:
def __get__(self): return self.__font.baseline()
def DrawText(core.Canvas c, Font f, int x, int y, Color color, text):
return cppinc.DrawText(c.__getCanvas(), f.__font, x, y, color.__color, text.encode('utf-8'))
def DrawCircle(core.Canvas c, int x, int y, int r, Color color):
cppinc.DrawCircle(c.__getCanvas(), x, y, r, color.__color)
def DrawLine(core.Canvas c, int x1, int y1, int x2, int y2, Color color):
cppinc.DrawLine(c.__getCanvas(), x1, y1, x2, y2, color.__color)
# Local Variables:
# mode: python
# End:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
if len(sys.argv) < 2:
sys.exit("Require a gif argument")
else:
image_file = sys.argv[1]
gif = Image.open(image_file)
try:
num_frames = gif.n_frames
except Exception:
sys.exit("provided image is not a gif")
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 40
options.cols = 80
options.chain_length = 3
options.parallel = 3
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
options.multiplexing = 1
options.brightness = 10
matrix = RGBMatrix(options = options)
# Preprocess the gifs frames into canvases to improve playback performance
canvases = []
print("Preprocessing gif, this may take a moment depending on the size of the gif...")
for frame_index in range(0, num_frames):
gif.seek(frame_index)
# must copy the frame out of the gif, since thumbnail() modifies the image in-place
frame = gif.copy()
frame.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
canvas = matrix.CreateFrameCanvas()
canvas.SetImage(frame.convert("RGB"))
canvases.append(canvas)
# Close the gif file to save memory now that we have copied out all of the frames
gif.close()
print("Completed Preprocessing, displaying gif")
try:
print("Press CTRL-C to stop.")
# Infinitely loop through the gif
cur_frame = 0
while(True):
matrix.SwapOnVSync(canvases[cur_frame], framerate_fraction=30)
if cur_frame == num_frames - 1:
cur_frame = 0
else:
cur_frame += 1
except KeyboardInterrupt:
sys.exit(0)

View File

@ -1,32 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class GraphicsTest(SampleBase):
def __init__(self, *args, **kwargs):
super(GraphicsTest, self).__init__(*args, **kwargs)
def run(self):
canvas = self.matrix
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
red = graphics.Color(255, 0, 0)
graphics.DrawLine(canvas, 5, 5, 22, 13, red)
green = graphics.Color(0, 255, 0)
graphics.DrawCircle(canvas, 15, 15, 10, green)
blue = graphics.Color(0, 0, 255)
graphics.DrawText(canvas, font, 2, 10, blue, "Text")
time.sleep(10) # show display for 10 seconds before exit
# Main function
if __name__ == "__main__":
graphics_test = GraphicsTest()
if (not graphics_test.process()):
graphics_test.print_help()

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
import time
class GrayscaleBlock(SampleBase):
def __init__(self, *args, **kwargs):
super(GrayscaleBlock, self).__init__(*args, **kwargs)
def run(self):
sub_blocks = 16
width = self.matrix.width
height = self.matrix.height
x_step = max(1, width / sub_blocks)
y_step = max(1, height / sub_blocks)
count = 0
while True:
for y in range(0, height):
for x in range(0, width):
c = sub_blocks * int(y / y_step) + int(x / x_step)
if count % 4 == 0:
self.matrix.SetPixel(x, y, c, c, c)
elif count % 4 == 1:
self.matrix.SetPixel(x, y, c, 0, 0)
elif count % 4 == 2:
self.matrix.SetPixel(x, y, 0, c, 0)
elif count % 4 == 3:
self.matrix.SetPixel(x, y, 0, 0, c)
count += 1
time.sleep(2)
# Main function
if __name__ == "__main__":
grayscale_block = GrayscaleBlock()
if (not grayscale_block.process()):
grayscale_block.print_help()

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python
# (This is an example similar to an example from the Adafruit fork
# to show the similarities. Most important difference currently is, that
# this library wants RGB mode.)
#
# A more complex RGBMatrix example works with the Python Imaging Library,
# demonstrating a few graphics primitives and image loading.
# Note that PIL graphics do not have an immediate effect on the display --
# image is drawn into a separate buffer, which is then copied to the matrix
# using the SetImage() function (see examples below).
# Requires rgbmatrix.so present in the same directory.
# PIL Image module (create or load images) is explained here:
# http://effbot.org/imagingbook/image.htm
# PIL ImageDraw module (draw shapes to images) explained here:
# http://effbot.org/imagingbook/imagedraw.htm
from PIL import Image
from PIL import ImageDraw
import time
from rgbmatrix import RGBMatrix, RGBMatrixOptions
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
matrix = RGBMatrix(options = options)
# RGB example w/graphics prims.
# Note, only "RGB" mode is supported currently.
image = Image.new("RGB", (32, 32)) # Can be larger than matrix if wanted!!
draw = ImageDraw.Draw(image) # Declare Draw instance before prims
# Draw some shapes into image (no immediate effect on matrix)...
draw.rectangle((0, 0, 31, 31), fill=(0, 0, 0), outline=(0, 0, 255))
draw.line((0, 0, 31, 31), fill=(255, 0, 0))
draw.line((0, 31, 31, 0), fill=(0, 255, 0))
# Then scroll image across matrix...
for n in range(-32, 33): # Start off top-left, move off bottom-right
matrix.Clear()
matrix.SetImage(image, n, n)
time.sleep(0.05)
matrix.Clear()

View File

@ -1,40 +0,0 @@
#!/usr/bin/env python
import time
from samplebase import SampleBase
from PIL import Image
class ImageScroller(SampleBase):
def __init__(self, *args, **kwargs):
super(ImageScroller, self).__init__(*args, **kwargs)
self.parser.add_argument("-i", "--image", help="The image to display", default="../../../examples-api-use/runtext.ppm")
def run(self):
if not 'image' in self.__dict__:
self.image = Image.open(self.args.image).convert('RGB')
self.image.resize((self.matrix.width, self.matrix.height), Image.ANTIALIAS)
double_buffer = self.matrix.CreateFrameCanvas()
img_width, img_height = self.image.size
# let's scroll
xpos = 0
while True:
xpos += 1
if (xpos > img_width):
xpos = 0
double_buffer.SetImage(self.image, -xpos)
double_buffer.SetImage(self.image, -xpos + img_width)
double_buffer = self.matrix.SwapOnVSync(double_buffer)
time.sleep(0.01)
# Main function
# e.g. call with
# sudo ./image-scroller.py --chain=4
# if you have a chain of four
if __name__ == "__main__":
image_scroller = ImageScroller()
if (not image_scroller.process()):
image_scroller.print_help()

View File

@ -1,43 +0,0 @@
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
if len(sys.argv) < 2:
sys.exit("Require an image argument")
else:
image_file = sys.argv[1]
image = Image.open(image_file)
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 40
options.cols = 80
options.chain_length = 2
options.parallel = 1
options.gpio_slowdown = 4
#options.row_address_type = 0
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
options.multiplexing = 1
options.brightness = 20
#options.pwm_lsb_nanoseconds = 300
#options.pwm_bits = 11
matrix = RGBMatrix(options = options)
# Make image fit our screen.
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
matrix.SetImage(image.convert('RGB'))
try:
print("Press CTRL-C to stop.")
while True:
time.sleep(100)
except KeyboardInterrupt:
sys.exit(0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

View File

@ -1,56 +0,0 @@
#!/usr/bin/env python
import time
import sys
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
import os
def resize_image(input_path, output_path):
try:
# Abrir la imagen de entrada
image = Image.open(input_path)
# Obtener el tamaño original de la imagen
original_width, original_height = image.size
# Calcular el nuevo alto (mitad del ancho)
new_height = original_width // 2
# Redimensionar la imagen
resized_image = image.resize((original_width, new_height))
# Guardar la imagen redimensionada en la carpeta img_processed
resized_image.save(output_path)
print("Imagen redimensionada y guardada correctamente.")
return resized_image
except Exception as e:
print("Error:", e)
if len(sys.argv) < 2:
sys.exit("Require an image argument")
else:
image_file = sys.argv[1]
image = resize_image(image_file, image_file.replace(".jpeg","_r.jpeg"))
# Configuration for the matrix
options = RGBMatrixOptions()
options.rows = 40
options.cols = 80
options.chain_length = 3
options.parallel = 3
options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat'
options.multiplexing = 1
options.brightness = 30
matrix = RGBMatrix(options = options)
# Make image fit our screen.
image.thumbnail((matrix.width, matrix.height), Image.ANTIALIAS)
matrix.SetImage(image.convert('RGB'))
try:
print("Press CTRL-C to stop.")
while True:
time.sleep(100)
except KeyboardInterrupt:
sys.exit(0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
class GrayscaleBlock(SampleBase):
def __init__(self, *args, **kwargs):
super(GrayscaleBlock, self).__init__(*args, **kwargs)
def run(self):
max_brightness = self.matrix.brightness
count = 0
c = 255
while (True):
if self.matrix.brightness < 1:
self.matrix.brightness = max_brightness
count += 1
else:
self.matrix.brightness -= 1
if count % 4 == 0:
self.matrix.Fill(c, 0, 0)
elif count % 4 == 1:
self.matrix.Fill(0, c, 0)
elif count % 4 == 2:
self.matrix.Fill(0, 0, c)
elif count % 4 == 3:
self.matrix.Fill(c, c, c)
self.usleep(20 * 1000)
# Main function
if __name__ == "__main__":
grayscale_block = GrayscaleBlock()
if (not grayscale_block.process()):
grayscale_block.print_help()

View File

@ -1,42 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
class PulsingColors(SampleBase):
def __init__(self, *args, **kwargs):
super(PulsingColors, self).__init__(*args, **kwargs)
def run(self):
self.offscreen_canvas = self.matrix.CreateFrameCanvas()
continuum = 0
while True:
self.usleep(5 * 1000)
continuum += 1
continuum %= 3 * 255
red = 0
green = 0
blue = 0
if continuum <= 255:
c = continuum
blue = 255 - c
red = c
elif continuum > 255 and continuum <= 511:
c = continuum - 256
red = 255 - c
green = c
else:
c = continuum - 512
green = 255 - c
blue = c
self.offscreen_canvas.Fill(red, green, blue)
self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas)
# Main function
if __name__ == "__main__":
pulsing_colors = PulsingColors()
if (not pulsing_colors.process()):
pulsing_colors.print_help()

View File

@ -1,72 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
import math
def scale_col(val, lo, hi):
if val < lo:
return 0
if val > hi:
return 255
return 255 * (val - lo) / (hi - lo)
def rotate(x, y, sin, cos):
return x * cos - y * sin, x * sin + y * cos
class RotatingBlockGenerator(SampleBase):
def __init__(self, *args, **kwargs):
super(RotatingBlockGenerator, self).__init__(*args, **kwargs)
def run(self):
cent_x = self.matrix.width / 2
cent_y = self.matrix.height / 2
rotate_square = min(self.matrix.width, self.matrix.height) * 1.41
min_rotate = cent_x - rotate_square / 2
max_rotate = cent_x + rotate_square / 2
display_square = min(self.matrix.width, self.matrix.height) * 0.7
min_display = cent_x - display_square / 2
max_display = cent_x + display_square / 2
deg_to_rad = 2 * 3.14159265 / 360
rotation = 0
# Pre calculate colors
col_table = []
for x in range(int(min_rotate), int(max_rotate)):
col_table.insert(x, scale_col(x, min_display, max_display))
offset_canvas = self.matrix.CreateFrameCanvas()
while True:
rotation += 1
rotation %= 360
# calculate sin and cos once for each frame
angle = rotation * deg_to_rad
sin = math.sin(angle)
cos = math.cos(angle)
for x in range(int(min_rotate), int(max_rotate)):
for y in range(int(min_rotate), int(max_rotate)):
# Our rotate center is always offset by cent_x
rot_x, rot_y = rotate(x - cent_x, y - cent_x, sin, cos)
if x >= min_display and x < max_display and y >= min_display and y < max_display:
x_col = col_table[x]
y_col = col_table[y]
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, x_col, 255 - y_col, y_col)
else:
offset_canvas.SetPixel(rot_x + cent_x, rot_y + cent_y, 0, 0, 0)
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
# Main function
if __name__ == "__main__":
rotating_block_generator = RotatingBlockGenerator()
if (not rotating_block_generator.process()):
rotating_block_generator.print_help()

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
# Display a runtext with double-buffering.
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class RunText(SampleBase):
def __init__(self, *args, **kwargs):
super(RunText, self).__init__(*args, **kwargs)
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!")
def run(self):
offscreen_canvas = self.matrix.CreateFrameCanvas()
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
textColor = graphics.Color(255, 255, 255)
pos = offscreen_canvas.width
my_text = self.args.text
while True:
offscreen_canvas.Clear()
len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text)
pos -= 1
if (pos + len < 0):
pos = offscreen_canvas.width
time.sleep(0.05)
offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas)
# Main function
if __name__ == "__main__":
run_text = RunText()
if (not run_text.process()):
run_text.print_help()

View File

@ -1,81 +0,0 @@
import argparse
import time
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..'))
from rgbmatrix import RGBMatrix, RGBMatrixOptions
class SampleBase(object):
def __init__(self, *args, **kwargs):
self.parser = argparse.ArgumentParser()
self.parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int)
self.parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int)
self.parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int)
self.parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int)
self.parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int)
self.parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int)
self.parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm" , choices=['regular', 'regular-pi1', 'adafruit-hat', 'adafruit-hat-pwm'], type=str)
self.parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int)
self.parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int)
self.parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel")
self.parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 0..4. Default: 1", default=1, type=int)
self.parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation")
self.parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str)
self.parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str)
self.parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels; 2=row direct; 3=ABC-addressed panels; 4 = ABC Shift + DE direct", default=0, type=int, choices=[0,1,2,3,4])
self.parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven... (Default: 0)", default=0, type=int)
self.parser.add_argument("--led-panel-type", action="store", help="Needed to initialize special panels. Supported: 'FM6126A'", default="", type=str)
self.parser.add_argument("--led-no-drop-privs", dest="drop_privileges", help="Don't drop privileges from 'root' after initializing the hardware.", action='store_false')
self.parser.set_defaults(drop_privileges=True)
def usleep(self, value):
time.sleep(value / 1000000.0)
def run(self):
print("Running")
def process(self):
self.args = self.parser.parse_args()
options = RGBMatrixOptions()
if self.args.led_gpio_mapping != None:
options.hardware_mapping = self.args.led_gpio_mapping
options.rows = self.args.led_rows
options.cols = self.args.led_cols
options.chain_length = self.args.led_chain
options.parallel = self.args.led_parallel
options.row_address_type = self.args.led_row_addr_type
options.multiplexing = self.args.led_multiplexing
options.pwm_bits = self.args.led_pwm_bits
options.brightness = self.args.led_brightness
options.pwm_lsb_nanoseconds = self.args.led_pwm_lsb_nanoseconds
options.led_rgb_sequence = self.args.led_rgb_sequence
options.pixel_mapper_config = self.args.led_pixel_mapper
options.panel_type = self.args.led_panel_type
if self.args.led_show_refresh:
options.show_refresh_rate = 1
if self.args.led_slowdown_gpio != None:
options.gpio_slowdown = self.args.led_slowdown_gpio
if self.args.led_no_hardware_pulse:
options.disable_hardware_pulsing = True
if not self.args.drop_privileges:
options.drop_privileges=False
self.matrix = RGBMatrix(options = options)
try:
# Start loop
print("Press CTRL-C to stop sample")
self.run()
except KeyboardInterrupt:
print("Exiting\n")
sys.exit(0)
return True

View File

@ -1,30 +0,0 @@
#!/usr/bin/env python
from samplebase import SampleBase
class SimpleSquare(SampleBase):
def __init__(self, *args, **kwargs):
super(SimpleSquare, self).__init__(*args, **kwargs)
def run(self):
offset_canvas = self.matrix.CreateFrameCanvas()
while True:
for x in range(0, self.matrix.width):
offset_canvas.SetPixel(x, x, 255, 255, 255)
offset_canvas.SetPixel(offset_canvas.height - 1 - x, x, 255, 0, 255)
for x in range(0, offset_canvas.width):
offset_canvas.SetPixel(x, 0, 255, 0, 0)
offset_canvas.SetPixel(x, offset_canvas.height - 1, 255, 255, 0)
for y in range(0, offset_canvas.height):
offset_canvas.SetPixel(0, y, 0, 0, 255)
offset_canvas.SetPixel(offset_canvas.width - 1, y, 0, 255, 0)
offset_canvas = self.matrix.SwapOnVSync(offset_canvas)
# Main function
if __name__ == "__main__":
simple_square = SimpleSquare()
if (not simple_square.process()):
simple_square.print_help()

View File

@ -1,33 +0,0 @@
#!/usr/bin/python
from distutils.core import setup, Extension
core_ext = Extension(
name = 'core',
sources = ['rgbmatrix/core.cpp'],
include_dirs = ['../../include'],
library_dirs = ['../../lib'],
libraries = ['rgbmatrix'],
extra_compile_args = ["-O3", "-Wall"],
language = 'c++'
)
graphics_ext = Extension(
name = 'graphics',
sources = ['rgbmatrix/graphics.cpp'],
include_dirs = ['../../include'],
library_dirs = ['../../lib'],
libraries = ['rgbmatrix'],
extra_compile_args = ["-O3", "-Wall"],
language = 'c++'
)
setup(
name = 'rgbmatrix',
version = '0.0.1',
author = 'Christoph Friedrich',
author_email = 'christoph.friedrich@vonaffenfels.de',
classifiers = ['Development Status :: 3 - Alpha'],
ext_package = 'rgbmatrix',
ext_modules = [core_ext, graphics_ext],
packages = ['rgbmatrix']
)

View File

@ -1,49 +1,60 @@
# Visualización para pantallas LED de paradas de bus # Configurar la RPI
Este repositorio contiene los archivos necesarios para ejecutar una aplicación Jupyter Notebook con ciertas bibliotecas de visualización y procesamiento de imágenes. ### Instalar el software para inicializar tarjetas SD
## Contenido `apt install rpi-imager`
- `Dockerfile`: Define cómo construir la imagen Docker para ejecutar la aplicación. ### Instalar la imagen Raspbian 64 bit Lite
- `requirements.txt`: Lista las bibliotecas y dependencias necesarias para la aplicación.
## Dockerfile Copiar la versión lite:
https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz
### Descripción ### Configurar la imagen
El `Dockerfile` especifica cómo construir una imagen Docker basada en Python 3.8 que tiene todas las dependencias necesarias para ejecutar la aplicación. `rpi-imager` permite habilitar SSH, cambiar el nombre al dispositivo, configurar una cuenta de usuario, etc.
### Instrucciones Para mas información revisar: http://rptl.io/newuser
1. **Imagen base**: Utiliza Python 3.8.
2. **Directorio de trabajo**: Establece `/app` como el directorio de trabajo en el contenedor.
3. **Instalación de dependencias**: Copia y utiliza `requirements.txt` para instalar las bibliotecas necesarias.
4. **Configuración de Jupyter**: Copia el archivo de configuración de Jupyter al contenedor.
5. **Puerto**: Expone el puerto 8888 para Jupyter Notebook.
6. **Comando de inicio**: Al iniciar el contenedor, se ejecuta Jupyter Notebook en el puerto 8888.
## requirements.txt ## Preparacipon de ambientes y dispositivo
### Descripción Este presupone que se usa la cuenta de soporte y el directorio principal `/home/soporte`
El archivo `requirements.txt` lista las bibliotecas y dependencias que se requieren para la aplicación. ### Quitar la tarjeta de sonido para habilitar
### Bibliotecas y dependencias `cat "blacklist snd_bcm2835" | sudo tee /etc/modprobe.d/blacklist-rgb-matrix.conf `
- `matplotlib`: Biblioteca de visualización de datos. `sudo update-initramfs -u`
- `seaborn`: Biblioteca de visualización de datos basada en matplotlib.
- `plotly`: Biblioteca para gráficos interactivos.
- `opencv-python`: Biblioteca de procesamiento de imágenes y visión por computadora.
- `jupyter`: Entorno de desarrollo interactivo.
## Cómo ejecutar `sudo reboot`
1. Construye la imagen Docker: ### Preparación de paquetes
`docker build -t bus_stop_visualization .` `sudo apt-get update`
2. Ejecuta el contenedor: paquetes basicos:
`docker run -d --name bus_stop_vis -v /scripts:/app/scripts -p 8888:8888 bus_stop_visualization` `sudo apt-get install git python3-pip -y `
3. Abre un navegador y accede a `http://localhost:8888` para comenzar a usar Jupyter Notebook. Requerimientos del LED-MATRIX
`sudo apt-get install python3-dev python3-pillow -y`
Requerimientos de pantallas led:
`sudo apt-get install python3-matplotlib python3-requests python3-numpy python3-pytzdata python3-apscheduler -y`
### Clonar los repositorios
`git clone https://dev.ilab.cl/TDTP/pantallas-led`
`git clone https://github.com/hzeller/rpi-rgb-led-matrix/`
`cd rpi-rgb-led-matrix/bindings/python`
`make build-python PYTHON=$(command -v python3)`
`sudo make install-python PYTHON=$(command -v python3)`
Return to the folder and proceed with the installation

12
autorun.sh 100644
View File

@ -0,0 +1,12 @@
#!/bin/bash
IMAGE=$HOME/pantallas-led/GenPoster/example/poster.png
#sudo python3 testapi_full.py
cd pantallas-led/GenPoster
#docker build -t bus_poster .
./run_container.sh
cd $HOME/rpi-rgb-led-matrix/bindings/python/samples/
sudo python3 image-viewer.py $IMAGE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Some files were not shown because too many files have changed in this diff Show More