WYŚWIETLACZ 16x16

Typ_projektu
microPython
Zdjecie główne
Krótki opis projektu

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.

Niezbędne elementy

1. ESP8266

2.Matryca LED 16x16 pikseli

3. komputer z Thonny

Opis projektu

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.

Zdjęcia
Przykładowy obrazek 1)
Przykładowy obrazek 2)
Przykładowy obrazek 3)
Przykładowy obrazek 4)
kod programu
# 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)
Tagi
matryca_LED NeoPixel RGB UART Python 16x16_wyświetlacz