Projekt zakładał opracowanie oprogramowania umożliwiającego wyświetlanie obrazów w czasie rzeczywistym na matrycy 16x16, przesyłanych lokalnie z komputera. Do realizacji tego celu wykorzystano komunikację UART, która pozwala na transfer danych w postaci wartości RGB. Dane były odczytywane z pliku tekstowego, a następnie interpretowane i wyświetlane na matrycy.
1. ESP8266
2.Matryca LED 16x16 pikseli
3. komputer z Thonny
Inicjalizacja matrycy LED
Program wykorzystuje bibliotekę NeoPixel do obsługi matrycy LED o wymiarach 16x16, składającej się z 256 diod. Komunikacja z matrycą odbywa się przez wykorzystanie pinu GPIO, skonfigurowanego jako wyjście cyfrowe. Każda dioda LED może wyświetlać kolor w formacie RGB, co pozwala na precyzyjne odwzorowanie obrazu.
Wczytywanie danych RGB
Oprogramowanie obsługuje plik tekstowy (rgb_values.txt
), który zawiera wartości RGB w formacie tekstowym, zapisane w osobnych liniach dla każdego piksela. Funkcja load_rgb_to_image_data
odczytuje dane z pliku i wypełnia macierz image_data
, odwzorowującą stan kolorystyczny diod. Dla każdego wiersza pliku odczytywane są wartości R, G i B, które przypisywane są odpowiednim współrzędnym macierzy obrazu.
Mapowanie przestrzenne matrycy
W celu prawidłowego wyświetlania obrazu program uwzględnia specyficzne rozmieszczenie diod na matrycy w układzie serpentynowym. Funkcja serpentine_map
przekształca współrzędne x i y na indeks odpowiadający diodzie LED w strukturze tablicy NeoPixel. W przypadku wierszy o parzystych indeksach mapowanie odbywa się wprost, natomiast dla wierszy o nieparzystych indeksach kolejność diod jest odwrócona.
Renderowanie obrazu
Funkcja draw_image
odpowiada za przeniesienie danych z macierzy image_data
na matrycę LED. Każda dioda LED otrzymuje wartość RGB odpowiadającą stanowi danego piksela w macierzy. Po przypisaniu kolorów dla wszystkich diod, wywoływana jest metoda np.write()
, która aktualizuje stan wyświetlacza w czasie rzeczywistym.
Struktura danych obrazu
Stan obrazu jest przechowywany w postaci dwuwymiarowej listy image_data
, gdzie każda komórka zawiera trójkę wartości RGB. Dzięki temu możliwe jest łatwe modyfikowanie danych wizualnych, np. przez zastosowanie dodatkowych efektów wizualnych lub dynamiczne zmiany obrazu.
Dynamiczne wczytywanie i wyświetlanie obrazów
Dzięki implementacji odczytu danych RGB z pliku możliwe jest szybkie i wygodne wgrywanie nowych obrazów bez konieczności modyfikacji kodu. Mechanizm ten pozwala użytkownikowi na zmianę zawartości pliku rgb_values.txt
, co skutkuje natychmiastowym odzwierciedleniem nowego obrazu na matrycy.
# Kod wgrany na płytke w boot'cie
from machine import Pin
from neopixel import NeoPixel
image_path = '/rgb_values.txt'
num_of_pixels = 256
width, height = 16, 16
pin = Pin(2, Pin.OUT)
np = NeoPixel(pin, num_of_pixels)
def load_rgb_to_image_data(input_file, image_data):
try:
with open(input_file, 'r') as file:
for i, line in enumerate(file):
if i >= len(image_data) * len(image_data[0]):
break
r, g, b = map(int, line.strip().split(',')) # Parsuj dane z pliku
y = i // len(image_data[0])
x = i % len(image_data[0])
image_data[y][x] = (r, g, b)
except Exception as e:
print(f"Błąd podczas odczytu: {e}")
def serpentine_map(x, y, width):
if y % 2 == 0:
return y * width + x
else:
return y * width + (width - 1 - x)
def draw_image(np, image_data):
for y in range(height):
for x in range(width):
index = serpentine_map(x, y, width)
np[index] = image_data[y][x]
np.write()
image_data = [[(0, 0, 0) for _ in range(16)] for _ in range(16)]
load_rgb_to_image_data(image_path, image_data)
draw_image(np, image_data)
##################################################################
Kod kompilowany lokalnie na komputerze podłączonym do matrycy (na czas wgrywania danych obrazka na płytkę KONIECZNE jest zamknięcie okna z thonym, z uwagi na zwolnienie portu szeregoweg)
from PIL import Image
import os
import time
from subprocess import call
from tkinter import Tk, filedialog, Entry, Button, Label
import serial
def select_image_file(entry):
root = Tk()
root.withdraw()
file_path = filedialog.askopenfilename(
title="Wybierz plik obrazu",
filetypes=[("Obrazy", "*.png;*.jpg;*.jpeg;*.bmp;*.gif")]
)
if file_path:
entry.delete(0, 'end')
entry.insert(0, file_path)
def get_image_path_with_gui():
def on_confirm():
root.quit()
root = Tk()
root.title("Wybierz obraz")
Label(root, text="Ścieżka do obrazu:").grid(row=0, column=0, padx=5, pady=5)
path_entry = Entry(root, width=50)
path_entry.grid(row=0, column=1, padx=5, pady=5)
browse_button = Button(root, text="...", command=lambda: select_image_file(path_entry))
browse_button.grid(row=0, column=2, padx=5, pady=5)
confirm_button = Button(root, text="OK", command=on_confirm)
confirm_button.grid(row=1, column=0, columnspan=3, pady=10)
root.mainloop() # Uruchom pętlę
image_path = path_entry.get()
root.destroy()
return image_path
def convert_image_to_rgb(image_path, output_file):
img = Image.open(image_path)
img = img.convert("RGB")
width, height = img.size
rgb_values = []
for y in range(height):
for x in range(width):
r, g, b = img.getpixel((x, y))
rgb_values.append((r, g, b))
with open(output_file, 'w') as file:
for i, color in enumerate(rgb_values):
file.write(f"{color}\n")
def upload_file(esp_port, file_to_upload, remote_path):
print("Czekam na połączenie z ESP8266...")
time.sleep(2)
print("Nadpisywanie pliku...")
call(["ampy", "--port", esp_port, "put", file_to_upload, remote_path])
print("Plik nadpisany pomyślnie!")
def reset_esp8266(esp_port):
try:
ser = serial.Serial(esp_port, 115200, timeout=1)
ser.setDTR(False)
time.sleep(0.1)
ser.setDTR(True)
print("Płytka została zresetowana!")
ser.close()
except Exception as e:
print(f"Błąd podczas resetowania płytki: {e}")
if __name__ == "__main__":
image_path = get_image_path_with_gui()
print(image_path)
if not image_path:
print("Nie wybrano pliku obrazu. Zamykanie programu.")
exit()
output_directory = "" # Ścieżka do folderu z projektem
output_file = output_directory + "rgb_values.txt"
ESP_PORT = "" # Port przez który przebiega komunikacja
REMOTE_PATH = "/rgb_values.txt"
# Konwersja obrazu na wartości RGB
convert_image_to_rgb(image_path, output_file)
print(f"Zapisano wartości RGB do pliku: {output_file}")
upload_file(ESP_PORT, output_file, REMOTE_PATH)
# Resetowanie płytki
reset_esp8266(ESP_PORT)