Spisu treści:
- Krok 1: Teoria działania
- Krok 2: Części i instrumenty
- Krok 3: Lutowanie i montaż
- Krok 4: Programowanie A: Arduino
- Krok 5: Programowanie B: Python i interfejs użytkownika
Wideo: MIDI Drum Kit na Pythonie i Arduino: 5 kroków (ze zdjęciami)
2024 Autor: John Day | [email protected]. Ostatnio zmodyfikowany: 2024-01-30 11:28
Od dziecka zawsze chciałem kupić zestaw perkusyjny. W tamtych czasach cały sprzęt muzyczny nie miał wszystkich zastosowań cyfrowych, bo dzisiaj mamy ich pod dostatkiem, stąd ceny i oczekiwania były zbyt wysokie. Ostatnio zdecydowałem się na zakup najtańszego zestawu perkusyjnego z eBaya, z jedynym priorytetem: Możliwość rozebrania go i podpięcia do urządzenia własnego sprzętu i oprogramowania.
Zakup wcale nie zawiódł: przenośny zestaw perkusyjny zwijany z 9 różnymi nakładkami dźwiękowymi, dwoma przełącznikami nożnymi do stopy i hi-hatu oraz gniazdem zasilania micro-USB. To, co było naprawdę demotywujące, to dźwięki wyjściowe (rzeczywiste zastosowanie tego zestawu to podłączenie zewnętrznego głośnika i cieszenie się nim). Postanowiłem więc przekonwertować go na własny programowalny przez USB, zestaw perkusyjny MIDI oparty na Arduino i interfejs użytkownika oparty na Pythonie, aby był wygodny w użyciu i łatwy do modyfikacji, takich jak wybór głośności, nuty i kanału.
Cechy urządzenia:
- Niska cena
- Tworzenie zestawu perkusyjnego z dowolnych wejść cyfrowych - nawet z szeregu przycisków
- Obsługa komunikacji i zasilanie tylko przez interfejs USB - Integracja konwertera USB na UART i urządzenia Arduino
- Minimalne części do prawidłowego działania
- Łatwy w użyciu interfejs użytkownika oparty na Pythonie
- Pełna obsługa MIDI z regulowaną prędkością, nutą i pinami Arduino
- Zapisz i załaduj niestandardowe konfiguracje bębnów przechowywane w pamięci urządzenia
Przejdźmy do projektu…
Krok 1: Teoria działania
Schemat blokowy
Przede wszystkim skupmy się na strukturze projektu i podzielmy go na osobne bloki:
Zestaw bębna zwijanego
Główna jednostka projektu. Składa się z 9 oddzielnych padów perkusyjnych, z których każdy jest szeregiem przycisków, które zmieniają swój stan logiczny w momencie uderzenia. Ze względu na swoją konstrukcję istnieje możliwość skonstruowania tego konkretnego zestawu perkusyjnego z dowolnych przycisków. Każdy pad perkusyjny jest podłączony do rezystora podciągającego na głównej płytce elektronicznej, dzięki czemu podczas wielokrotnych uderzeń w pad perkusyjny, do masy obwodu podłączony jest specjalny przełącznik, a na linii padów perkusyjnych występuje logiczne LOW. Gdy nie ma nacisku, przełącznik padów perkusyjnych jest otwarty i ze względu na rezystor podciągający do linii zasilającej, na linii padów perkusyjnych występuje logiczne WYSOKIE. Ponieważ celem projektu jest stworzenie kompletnego cyfrowego urządzenia MIDI, można pominąć wszystkie części analogowe na głównej płytce drukowanej. Należy zauważyć, że zestaw perkusyjny posiada dwa pedały do stopy i hi-hatu, które również są połączone z rezystorami podciągającymi i mają tę samą logikę działania, co wszystkie pady perkusyjne (omówimy to nieco później).
Arduino Pro-Mikro
Mózg zestawu perkusyjnego. Jego celem jest wykrycie sygnału wychodzącego z padu perkusyjnego i zapewnienie odpowiedniego wyjścia MIDI ze wszystkimi niezbędnymi parametrami: nutą, prędkością i czasem trwania sygnału. Ze względu na cyfrowy charakter padów perkusyjnych można je po prostu podłączyć do wejść cyfrowych arduino (łącznie 10 pinów). Aby zachować wszystkie pożądane ustawienia i informacje MIDI, będziemy używać jego pamięci - EEPROM, dlatego za każdym razem, gdy włączamy urządzenie, informacje MIDI są ładowane z EEPROM, dzięki czemu można je przeprogramować i ponownie skonfigurować. Ponadto Arduino Pro-Micro jest dostępny w bardzo małym opakowaniu i można go łatwo umieścić w wewnętrznej walizce zestawu perkusyjnego.
Konwerter FTDI USB na szeregowy
Aby zaprogramować i zdefiniować funkcje naszego urządzenia za pomocą aplikacji PC, należy przekonwertować interfejs USB na szeregowy, ponieważ Arduino Pro-Micro nie posiada USB. Ponieważ komunikacja pomiędzy urządzeniami oparta jest na UART, w tym projekcie zastosowano urządzenie FTDI, ze względu na prostotę obsługi niezależnie od dodatkowych właściwości.
Aplikacja PC - Python
Jeśli chodzi o tworzenie interfejsów użytkownika i szybkich projektów, Python jest doskonałym rozwiązaniem. Celem aplikacji interfejsu użytkownika jest ułatwienie redefiniowania właściwości MIDI dla naszego zestawu perkusyjnego, przechowywania informacji, programowania urządzenia i nawiązywania komunikacji między systemami bez konieczności ciągłego kompilowania kodu. Ponieważ do komunikacji z zestawem perkusyjnym używamy interfejsu szeregowego, w Internecie dostępnych jest wiele darmowych modułów, które obsługują każdy rodzaj komunikacji szeregowej. Dodatkowo, jak zostanie omówione dalej, interfejs UART składa się łącznie z trzech pinów: RXD, TXD i DTR. DTR służy do wykonywania resetu na module Arduino, więc gdy jesteśmy zainteresowani uruchomieniem aplikacji MIDI lub podłączeniem UI do urządzenia programującego, absolutnie nie ma potrzeby ponownego podłączania kabla USB lub czegokolwiek.
Krok 2: Części i instrumenty
Części
- Zestaw bębna zwijanego
- 2 x pedały podtrzymania dźwięku (zwykle zawarte w pakiecie DK).
- FTDI - konwerter USB na szeregowy
- Arduino Pro Micro
- Kabel Micro-USB
Instrumenty
- Lutownica/stacja
- Cyna lutownicza
- Drut jednożyłowy o cienkiej średnicy
- Pinceta
- Nóż
- Obcęgi
- Nóż
- Śrubokręt
- Drukarka 3D (opcjonalna - dla niestandardowych platform pedałów)
Oprogramowanie
- IDE Arduino
- Python 3 lub nowszy
- JetBrains Pycharm
- Bezwłosy interfejs MIDI
- pętlaMIDI
Krok 3: Lutowanie i montaż
Ponieważ trzeba połączyć trzy moduły, proces lutowania i montażu jest krótki i prosty:
-
Połącz Arduino Pro-Micro z urządzeniem FTDI, upewnij się, że połączenia są zgodne z I/O zdefiniowanymi na każdym urządzeniu:
- VBUS-VBUS
- GND-GND
- DTR-DTR
- RXD-TXD
- TXD-RXD
- Usuń wszystkie śruby z plastikowej obudowy bębna, upewnij się, że możesz skupić się na kablu pad-płyta i jego rezystorach podciągających
-
Przylutuj cienkie przewody do modułu Arduino-FTDI, który zbudowaliśmy wcześniej:
- Wejścia cyfrowe: D[2:11]
- VBUS
- D+
- D-
- GND
- Włóż moduł do pojemnika na baterie, aby przewody unosiły się po tej samej stronie, co rezystory podciągające padów
- Przylutuj wszystkie wejścia cyfrowe do zacisków padów perkusyjnych, jak pokazano na ostatnim rysunku.
- Przylutuj magistralę micro-USB (VBUS, D+, D-, GND) do urządzenia FTDI, upewnij się, że nie ma błędów śledzenia tych przewodów.
- Przymocuj moduł Arduino-FTDI za pomocą kleju na gorąco do obudowy baterii
- Zamontuj urządzenie za pomocą odpowiednich śrub mocujących
Udało się, urządzenie jest zmontowane. Przejdźmy do kodu…
Krok 4: Programowanie A: Arduino
Opiszmy nasz szkic krok po kroku:
Przede wszystkim do poprawnego działania potrzebne są dwie biblioteki. EEPROM jest już preinstalowany w Arduino IDE, ale moduł debouncera dla stopy musi być zainstalowany osobno
#włącz #włącz
Przełączniki te są używane głównie w sekwencjach debugowania. Jeśli chcesz wypróbować połączenie terminali Arduino z padami perkusyjnymi i określić wszystkie wejścia cyfrowe, te przełączniki powinny być zdefiniowane
/* Przełączniki programistyczne: odkomentuj żądany tryb debugowania lub inicjalizacji *///#define LOAD_DEFAULT_VALUES // Załaduj stałe wartości zamiast EEPROM //#define PRINT_PADS_PIN_NUMBERS // Wydrukuj numer pinu, który jest podłączony do padu, który został trafiony przez port szeregowy
Pola stałe reprezentują wszystkie wartości domyślne, w tym wyliczenie padów perkusyjnych. Aby po raz pierwszy uruchomić urządzenie, trzeba znać dokładne połączenie pedałów Hi-Hat i Kick
/* Wyliczenie typu bębna */
enum DRUM_POSITION { KICK = 0, SNARE, HIHAT, RIDE, CYMBAL1, CYMBAL2, TOM_HIGH, TOM_MID, TOM_LO, HIHAT_PEDAL };
/* Wartości domyślne */
const uint8_t DRUM_NOTES[10] = { 36, 40, 42, 51, 49, 55, 47, 45, 43, 48}; const uint8_t DRUM_VELOCITIES[10] = { 110, 100, 100, 110, 110, 110, 110, 110, 110, 110}; const uint8_t DRUM_PINS[10] = { 8, 6, 4, 3, 11, 9, 5, 10, 2, 7 };
/* Czas trwania odbicia bębna kopnięcia */
const uint8_t KICK_DB_DURATION = 30;
EEPROM służy do przechowywania/ładowania wszystkich danych pochodzących z aplikacji PC. Opisany powyżej zakres adresów pokazuje dokładną lokalizację informacji MIDI dla każdego padu perkusyjnego
/* Mapowanie adresów EEPROM
Uwagi: |0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09|
Piny: |0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13| Prędkości |0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23| */ const uint8_t NOTES_ADDR = 0x00; const uint8_t VELOCITIES_ADDR = 0x14; const uint8_t PINS_ADDR = 0x0A;
Zmienne globalne służą do określenia stanu każdego padu i odpowiedniej komunikacji MIDI
/* Zmienne globalne */
uint8_t drumNotes[10], drumPrędkości[10], drumPins[10]; // Zmienne MIDI
uint8_t uartBuffer[64]; // Bufor UART do gromadzenia i przechowywania MIDI Data Debouncer kick(DRUM_PINS[KICK], KICK_DB_DURATION); // Obiekt Debouncer dla volatile bool bębna stopy previousState[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Drum Pad poprzednie stany logiczne volatile bool currentState[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Bieżące stany logiczne padu perkusyjnego
Funkcje pamięci EEPROM
/* Zapisz ustawienia w EEPROM*/
void przechowujEEPROM() {
memcpy(drumNotes, uartBuffer, 10); memcpy(drumPins, uartBuffer + 10, 10); memcpy(bębenPrędkości, uartBuffer + 20, 10); for (uint8_t i = 0; i < 10; i++) EEPROM.write(NOTES_ADDR + i, drumNotes); for (uint8_t i = 0; i < 10; i++) EEPROM.write(PINS_ADDR + i, drumPins); for (uint8_t i = 0; i < 10; i++) EEPROM.write(VELOCITIES_ADDR + i, drumVelocities); }
/* Załaduj ustawienia z EEPROM*/
void loadEEPROM() { for (uint8_t i = 0; i < 10; i++) drumNotes = EEPROM.read(NOTES_ADDR + i); for (uint8_t i = 0; i < 10; i++) drumPins = EEPROM.read(PINS_ADDR + i); for (uint8_t i = 0; i < 10; i++) drumVelocities = EEPROM.read(VELOCITIES_ADDR + i); }
Inicjalizacja zmiennych i tryb programowania, w przypadku pedałów i rozruchu Arduino są aktywowane jednocześnie
void enterProgrammingMode() {
bool potwierdzićBreak = false; uint8_t lineCnt = 0; uint8_t charCnt = 0; znak odczytZnak = 0; while(!confirmBreak) { if (Serial.available()) { uartBuffer[charCnt] = Serial.read(); if (charCnt >= 29) ConfirmBreak = true; w przeciwnym razie charCnt++; } } Serial.println("OK"); przechowujEEPROM(); }
void initValues() {
#ifdef LOAD_DEFAULT_VALUES memcpy(drumNotes, DRUM_NOTES, 10); memcpy(Prędkości bębna, VELOCITIES_DRUM, 10); memcpy(drumPins, DRUM_PINS, 10); #else załadujEEPROM(); #endif }
Obsługa komunikacji MIDI z opóźnieniem 1ms czasu podtrzymania nuty
/* Funkcja odtwarzania nut MIDI */
void midiOut(enum DRUM_POSITION drumIn) {
if (drumIn == HIHAT) { // Jeśli HI-HAT został uderzony, należy sprawdzić, czy pedał jest wciśnięty if (!digitalRead(drumPins[HIHAT_PEDAL])) { noteOn(0x90, drumNotes[HIHAT_PEDAL], drumVelocities [HIHAT_PEDAL]); opóźnienie(1); noteOn(0x90, nuty perkusyjne[HIHAT_PEDAL], 0); } else { noteOn(0x90, drumNotes[HIHAT], drumVelocities[HIHAT]); opóźnienie(1); noteOn(0x90, drumNotes[HIHAT], 0); } } else { // Zwykła perkusyjna transmisja MIDI noteOn(0x90, drumNotes[drumIn], drumVelocities[drumIn]); opóźnienie(1); noteOn(0x90, drumNotes[drumIn], 0); } }
void noteOn(int cmd, int skok, int prędkość) { Serial.write(cmd); Serial.write(skok); Serial.write(prędkość); }
Funkcje setup() i loop() z nieskończoną pętlą działania urządzenia:
pusta konfiguracja () {
Serial.początek(115200);
for (uint8_t i = 0; i < 10; i++) { pinMode(i + 2, INPUT); } #ifdef PRINT_PADS_PIN_NUMBERS while(true) { // Nieskończona pętla debugowania dla (uint8_t i = 0; i < 10; i++) { if (!digitalRead(i + 2)) { Serial.print("Pin No: D"); Serial.print(i + '0'); // Konwertuj liczbę na znak ASCII } } } #else initValues(); /* Tryb programowania: Jeśli dwa pedały są wciśnięte podczas uruchamiania - tryb jest aktywowany */ if (!digitalRead(drumPins[KICK]) && !digitalRead(drumPins[HIHAT_PEDAL])) enterProgrammingMode(); #endif }
void loop() { for (uint8_t i = 1; i < 9; i = i + 1) { currentState = digitalRead(drumPins); if (!bieżącystan &&poprzednistan) midiOut(i); // Porównaj stany i wykryj opadające zbocze previousState = currentState; } kick.aktualizacja(); // Kick drum używa własnego algorytmu debounce if (kick.edge()) if (kick.falling()) midiOut(KICK); }
Krok 5: Programowanie B: Python i interfejs użytkownika
Interfejs użytkownika Pythona jest nieco skomplikowany do zrozumienia na pierwszy rzut oka, dlatego spróbujemy wyjaśnić jego podstawy, jak używać, jaką funkcję ma każdy przycisk i jak poprawnie zaprogramować urządzenie Arduino.
Interfejs użytkownika - aplikacja
Interfejs użytkownika jest graficzną reprezentacją naszego programisty zestawów perkusyjnych, dzięki czemu jest naprawdę łatwy w użyciu i wygodny w programowaniu urządzenia Arduino w dowolnym momencie. UI składa się z kilku modułów graficznych powiązanych z ich sugerowanym działaniem. przejrzyjmy je jeden po drugim:
- Obraz zestawu perkusyjnego: interfejs użytkownika Pythona używa współrzędnych obrazu X-Y, aby określić, który typ bębna został wybrany. Jeśli wybrano prawidłowy region perkusyjny, pojawi się dodatkowy komunikat IO z polami nuty, prędkości i terminala Arduino dla dedykowanego padu perkusyjnego. Po zweryfikowaniu i zatwierdzeniu tych parametrów przez użytkownika wartości te mogą być przesyłane bezpośrednio do urządzenia Arduino.
- Obraz zewnętrznego kontrolera: Aby móc używać zestawu perkusyjnego MIDI ze środowiskiem tworzenia VST/Music, konieczne jest uruchomienie interpretera Serial-To-MIDI. Użyłem Hairless, który jest dostępny za darmo i można go uruchomić bezpośrednio z naszego interfejsu użytkownika, po prostu naciskając jego obraz.
- Lista portów COM: W celu komunikacji z Arduino należy określić dołączony port COM. Lista jest odświeżana po naciśnięciu przycisku Odśwież.
- Wczytaj/zapisz konfigurację: W kodzie zdefiniowane są domyślne wartości MIDI, które mogą być modyfikowane przez użytkownika poprzez interakcję z interfejsem użytkownika. Konfiguracja jest zdefiniowana w pliku config.txt w określonym formacie, który może być zapisany lub wczytany przez użytkownika.
- Program Device Button: Aby zapisać wszystkie zmodyfikowane wartości MIDI w pamięci EEPROM Arduino, należy nacisnąć dwa pedały nożne (Kick drum i Hi-hat pedal) i poczekać na zakończenie transmisji danych. Jeśli wystąpiły jakiekolwiek problemy z komunikacją, zostanie wyświetlone odpowiednie wyskakujące okienko. Jeśli transmisja się powiedzie, interfejs użytkownika wyświetli komunikat o pomyślnym zakończeniu.
- Przycisk wyjścia: po prostu wyjdź z aplikacji za zgodą użytkownika.
Najważniejsze cechy kodu w Pythonie
W kodzie dzieje się wiele rzeczy, więc będziemy rozwijać napisane funkcje, a nie cały kod.
Przede wszystkim, aby korzystać z UI, należy pobrać kilka modułów, aby kod działał:
import osimport Threading import tkinter as tk z tkinter importuj okno komunikatów z tkinter import * z PIL import ImageTk, obraz import numpy jako np import serial import glob
Niektóre moduły są zawarte w domyślnym pakiecie Pythona. Kilka modułów powinno być zainstalowanych za pomocą narzędzia PIP:
pip zainstalować poduszkę
pip zainstaluj numpy pip zainstaluj ScreenInfo
Zdecydowanie zaleca się uruchamianie aplikacji przez PyCharm. W przyszłych wydaniach planuję wyeksportować plik wykonywalny projektu.
Krótkie wyjaśnienie kodu
Znacznie łatwiej będzie zrozumieć kod, jeśli spojrzymy na jego wiersze z perspektywy funkcji i klas:
1. Główna funkcja - tutaj zaczyna się kod
if _name_ == '_main_': drumkit_gui()
2. Stałe zestawu perkusyjnego, współrzędne i domyślne informacje MIDI
class Drums: DRUM_TYPES = ["Kick", "Hihat", "Snare", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Ride", "Hihat Pedal ", "Kontroler"]
WSPÓŁRZĘDNE_X = [323, 117, 205, 173, 565, 271, 386, 488, 487, 135, 79]
WSPÓŁRZĘDNE_Y = [268, 115, 192, 40, 29, 107, 104, 190, 71, 408, 208] WYMIAR_SZER. = [60, 145, 130, 120, 120, 70, 70, 130, 120, 70, 145] DŁUGOŚĆ_WYMIARÓW = [60, 60, 80, 35, 35, 40, 40, 70, 35, 100, 50]
DRUM_ENUM = ["Kick", "Snare", "Hihat", "Ride", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Hihat Pedal"]
UWAGI_BĘBNA = [36, 40, 42, 51, 49, 55, 47, 45, 43, 48] PRĘDKOŚCI_BĘBNA = [110, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110] KOŁKI_BĘBNA = [8, 6, 4, 3, 11, 9, 5, 10, 2, 7]
3. Funkcje interfejsu użytkownika - Obsługa interfejsu użytkownika i obiektów graficznych
def zestaw_aktywny(ui)
def drugorzędny_ui(typ_bębenu)
klasa SelectionUi(tk. Frame)
Aplikacja klasy (tk. Frame)
def zestaw perkusyjny_gui()
def event_ui_clicked(event)
def getorigin(self, event)
4. Komunikacja szeregowa
def get_serial_ports()
def komunikacja_z_arduino(port)
5. Praca z plikami: Zapisz/Załaduj ustawienia z pliku txt
def save_config()
def load_config()
6. Uruchamianie zewnętrznej aplikacji hairless.exe z kodu przy użyciu funkcji Python Threading
class ExternalExecutableThread(threading. Thread)
def run_hairless_executable()
Aby uruchomić kod, do folderu projektu należy dołączyć listę plików:
- config.txt: Plik ustawień
- hairless.exe: bezwłosy konwerter MIDI
- drumkit.png: Obraz, który definiuje wszystkie klikalne pady perkusyjne w naszym interfejsie użytkownika (należy pobrać z zestawu obrazów tego kroku)
- drumgui.py: kod projektu
To wszystko, co musimy podkreślić, aby to zadziałało. Bardzo ważne jest dodanie do projektu plików: obrazu zestawu perkusyjnego, pliku wykonywalnego hairless.exe oraz pliku ustawień config.txt.
I.. Tutaj skończyliśmy!:)
Mam nadzieję, że ta instrukcja będzie przydatna.
Dziękuje za przeczytanie!:)
Zalecana:
Skaner kodów QR przy użyciu OpenCV w Pythonie: 7 kroków
Skaner kodów QR przy użyciu OpenCV w Pythonie: W dzisiejszym świecie widzimy, że kod QR i kod kreskowy są używane prawie wszędzie, od pakowania produktów po płatności online, a obecnie widzimy kody QR nawet w restauracji, aby zobaczyć menu. Więc nie wątpię, że teraz jest to wielka myśl. Ale czy kiedykolwiek
Program w Pythonie - naliczanie odsetek Z miesięcznymi składkami/depozytami: 5 kroków
Python Program - Składane odsetki Z miesięcznymi składkami/depozytami: Program do obliczania odsetek składanych z miesięcznymi składkami na koniec miesiąca.Formuła zaczerpnięta z TheCalculatorSite.com:Oprocentowanie złożone dla kapitału: P(1+r/n)^(nt) Przyszła wartość szeregu: PMT × (((1 + r/n)^nt - 1) / (r/n))
Obserwator wilgotności i temperatury używający Raspberry Pi z SHT25 w Pythonie: 6 kroków
Obserwator wilgotności i temperatury używający Raspberry Pi z SHT25 w Pythonie: Będąc entuzjastami Raspberry Pi, pomyśleliśmy o kilku bardziej spektakularnych eksperymentach z nim. W tej kampanii stworzymy obserwatora wilgotności i temperatury, który mierzy wilgotność względną i temperaturę za pomocą Raspberry Pi i SHT25, Humid
Prosty backdoor w Pythonie: 7 kroków
Prosty backdoor w Pythonie: więc po prawie roku od uruchomienia mojego prostego backdoora Netcat, zostałem zainspirowany do stworzenia podobnej, ale bardziej funkcjonalnej wersji przy użyciu języka programowania Python, ponieważ jest to dość prosty język w porównaniu z innymi. Więc jeśli
Wykres zmian temperatury od zmian klimatu w Pythonie: 6 kroków
Wykres zmian temperatury od zmian klimatu w Pythonie: Zmiana klimatu to duży problem. A wielu ludzi nie wie teraz, jak bardzo wzrosło. W tej instrukcji przedstawimy wykres zmian temperatury w klimacie spowodowanych zmianami klimatu. Aby uzyskać ściągawkę, możesz wyświetlić plik Pythona poniżej