Pojďme programovat elektroniku

Programování elektroniky: Hrajeme koledy laserem. Dudlaj, dudlaj, dudlajdá

  • Dnes si naprogramujeme a zahrajme vánoční koledu
  • Její tóny vygenerujeme na počítači Arduino Uno
  • A přeneseme je laserem do reproduktoru

Za pár dnů tu máme Vánoce, a tak je nejvyšší čas natrénovat nějakou tu koledu. Tady jsme ale na Živě.cz a v seriálu o programování elektroniky, rovnou proto zapomeňte na dětský sbor a klavír. Dnes nám jako hudební těleso poslouží laser.

Laserová dioda

Ano, čtete správně, laser, do hry totiž zapojíme jeden z nejoblíbenějších prototypovacích modulů s červenou 650nm laserovou diodou KY-008. Destička má vyvedené piny pro 5V digitální signál, pomocí kterého laser rozsvítíte, anebo zhasnete.

Klepněte pro větší obrázek
Prototypovací modul 650nm laserové diody KY-008

Na skladu je mají i české e-shopy. Třeba Laskarduino za 28 korun a Drátek za 49 korun. Oba obchody na svých webech zároveň slibují, že vám při objednávce do tohoto pátku stihnou balíček odeslat ještě do Vánoc.

Laserový digitální detektor

Zdroj světla tedy máme, ovšem abychom mohli nějakým způsobem zachytit jeho pulzy, potřebujeme ještě laserový detektor.

Klepněte pro větší obrázekKlepněte pro větší obrázekKlepněte pro větší obrázek
Prototypovací modul snímače a jeho samotná součástka na trojnožce. Pohled přes lupu odhalí miniaturní obvod pod kupolí čočky.  Pozor na správný směr zapojení jako na obrázku, jinak čidlo zničíte

Z laciných a vhodných optických senzorů pro domácí kutily je dnes na trhu k dispozici prakticky jen jeden model v podobě drobné plastové schránky zalité v transparentním plastu s čočkou, kterou pomocí tří vodičů zapojíte do prototypovací destičky. Ta je součástí balení a už má vyvedené klasické 2,54mm piny opět pro 5V napájení a digitální výstup.

Na Laskarduinu jej tentokrát seženete za 48 korun a na Drátku za 47 kaček.

Optický snímač není vzhledem ke své ceně bezchybný. Nesmí na něj přímo svítit Slunce, jinak bude na jeho výstupních pinu stále okolo 5V a náš experiment nebude fungovat.

Klepněte pro větší obrázekKlepněte pro větší obrázek
Sluneční světlo může přijímač rušit, a tak jsme si vyrobili stínítko

Buď tedy celý obvod postavíte v dostatečně stinné místnosti (nemusí to být přímo sklep bez oken), anebo si třeba na 3D tiskárně vyrobíte stínítko. Jedno takové včetně stativu, který jsem použil v našem experimentu pro zamíření laseru, najdete na Tinkercadu. 

Stativ laseru a stínítko detektoru:

Laser připojíme k Arduinu, přijímač k bzučáku

Jako řídící počítač, který bude ovládat stav laserové diody, poslouží základní deska Arduino Uno s 8bitovým čipem ATmega328P. Patří k těm nejpopulárnějším, pracuje pod 5V napětím a je dostatečně robustní, aby ji začátečník nezkratoval hned při prvním pokusu. Není to sice žádná výkonná raketa, pro naše koledy ale bude bohatě stačit.

Klepněte pro větší obrázek
Mozkem dnešního experimentu bude deska Arduino Uno – respektive její laciný klon – s osmibitovým mikrokontrolerem ATmega328P

Laserovou diodu i přijímač připojíme třeba skrze nepájivé prototypovací pole k jejímu 5V zdroji napětí, signální vstup laseru připojíme na desce Arduino Uno do pinu 3, který je určený ke generování periodického signálu PWM, a signálový výstup na laserovém přijímači připojíme k vhodnému malému reproduktoru nebo modulu pasivního bzučáku. Třeba takový jednoduchý KY-006 jako na schématu níže seženete za 18 kaček.

Klepněte pro větší obrázek
Schéma zapojení s pasivním bzučákem KY-006

Střída signálu a laserové ukazovátko

Laserový paprsek se bude v našem zapojení chovat vlastně úplně stejně, jako kdybychom místo něj použili běžný kabel. Bude to ale mnohem zábavnější a hlavně efektnější.

Podívejte se, jak se mění signál PWM:

Jak už jsme si řekli výše, pin 3 na desce Arduino Uno umí generovat periodický signál PWM s frekvencí 490 Hz. Slouží k tomu funkce analogWrite, která má dva parametry: výstupní pin a střídu signálu (duty cycle) v rozsahu 0-255. Střída signálu představuje poměr časů v periodě, ve kterých bude obdélníkový signál v jednotlivých úrovních (z Wikipedie).

Takže po lepší představu, tento kód:

analogWrite(3, 1);

Vytvoří periodický signál, který bude na osciloskopu vypadat takto:

Klepněte pro větší obrázek
Pulz nyní tvoří jen zhruba 1/255 periody s frekvencí 490 Hz

A když osmibitovou střídu signálu zvýšíme třeba na 127:

analogWrite(3, 127);

Periodu signálu budou tvořit dvě stejně dlouhé bloky 5V a 0V napětí, takže doba, po kterou bude naše laserová LED svítit a po kterou bude zhasnutá, bude stejná:

Klepněte pro větší obrázek
Pulz nyní tvoří zhruba polovinu periody  s frekvencí 490 Hz

Jelikož bude světlo blikat s frekvencí 490 Hz, která je pro lidský zrak už nerozlišitelná, všimneme si jen zvýšeného jasu. Ovšem díky připojenému bzučáku bychom měli slyšet poměrně čistý 490Hz tón. Protože světlo a tma tvoří svorně zhruba 50 % periody, bzučák dostane dostatek energie, aby jeho membrána dokázala pohybem vytvořit silnou zvukovou vlnu.

Do třetice se ještě podívejme, co se stane, pokud zvýšíme poměr na 200:

analogWrite(3, 200);

Laser bude během jedné periody ¾ času svítit a ¼ bude zhasnutý.

Klepněte pro větší obrázek
Pulz nyní tvoří zhruba 3/4 periody s frekvencí 490 Hz

Funkce tone namísto střídy nastaví frekvenci

V dnešním článku nicméně nechceme přenášet tón o konstantní frekvenci 490 Hz, ale chceme pomocí laseru přenášet třeba i desítky různých tónů s různou frekvencí, které tvoří ty nepopulárnější koledy. Arduino na to samozřejmě pamatuje – tentokrát ve funkcích tone a noTone. Zatímco ta první tón generuje, ta druhá jej ukončuje.

Funkce tone je vlastně pravým opakem analogWrite. Střída signálu je tentokrát konstantní – odpovídá 50 % – a my nastavujeme frekvenci.

Hraj tón A₄

Takže kdybychom chtěli laserovým paprskem přenést třeba standardní tón A₄ o délce 5 000 ms, který má frekvenci 440 Hz, použijeme kód:

tone(3, 440);
delay(5000);
noTone(3);

Vygenerovaný signál nemusí být úplně přesný – řídící čip na desce Arduino Uno nemá parametry atomových hodin –, nicméně na zvukovém výsledku se to nijak neprojeví. Budou to jen nuance.

Klepněte pro větší obrázek
Periodický signál s frekvencí 440 Hz a střídou 50 %

Hraj tón E₅

Když bychom chtěli vygenerovat třeba tón E₅ s frekvencí 659 Hz, programový kód pro Arduino jen analogicky upravíme:

tone(3, 659);
delay(5000);
noTone(3);
Klepněte pro větší obrázek
Periodický signál s frekvencí 659 Hz a střídou 50 %

Hraj stupnici 50-500 Hz v 10Hz krocích

Do třetice si v nekonečné smyčce přehrávejme stále dokola sekvenci tónů od 50 Hz po 500 Hz v krocích po 10 Hz, přičemž každý tón zazní po dobu 100 ms:

void setup(){;}

void loop(){
    for(int ton=50; ton <=500; ton+=10){
        tone(3, ton);
        delay(100);
        noTone();
    }
    delay(1000);
}

Pokud k vodiči, který ovládá laserovou diodu, připojíme sondu osciloskopu. V jeho časovém grafu se krásně zobrazí periodický obdélníkový signál vygenerovaný na desce Arduino Uno, který postupně zvyšuje frekvenci od 50 Hz po 500 Hz při zachování střídy na 50 %.

Podívejte se, jak vypadá signál zvukové stupnice 50-500 Hz:

 

Jelikož je u laserového přijímače připojený malý reproduktor, změna frekvence signálu se projeví rostoucí výškou tónu.

Skvělé! Už umíme generovat tón a přenášet jej pomocí laserového ukazovátka zhruba na vzdálenost jednoho metru. Mimochodem, laserová dioda je dostatečně silná na to, abychom dokázali vyslat digitální signál klidně i na několik metrů. Jen budeme mít pochopitelně více práce s přesným zaměřením přijímače.

Musical Instrument Digital Interface

No dobrá, ale my nechceme přenášet jen jakési náhodné frekvence. Chceme vysílat standardní hudební tóny, které tvoří skladby vánočních koled. Jejich frekvence najdeme třeba v tomto seznamu na webu InspiredAcoustic.

Co je však ještě zajímavější, v tabulce není u mnohých pouze standardní název noty a přesná frekvence, ale také číslo noty ve formátu MIDI.

Musical Instrument Digital Interface je volně přístupný průmyslový standard, který obsahuje specifikace pro digitální komunikaci mezi hudebními nástroji a dalším zařízením.

Oproti běžné nahrávce třeba v MP3 se tedy liší v tom, že neobsahuje samotné digitalizované zvukové vlny, ale popisuje skladbu podle toho, jaký nástroj, jakým tónem a jak dlouho hraje.

Když bychom tedy měli po ruce nějakou tu vánoční a dostatečně jednoduchou koledu ve formátu MIDI, mohli bychom z ní vytáhnout sled jednotlivých not a vyblikat jej skrze laserové ukazovátko.

Dekódujeme MIDI v Pythonu

Nebojte se, nebudete muset zdlouhavě studovat celou dokumentaci MIDI, vše za nás totiž udělá programovací jazyk Python a knihovna pretty_midi, která vše maximálně zjednoduší. Knihovna je v repozitáři PyPI, takže ji stačí stáhnout skrze jeho balíčkový nástroj pip:

pip install pretty_midi

Případně dle verze OS a verze Pythonu:

pip3 install pretty_midi

Teď už jen stačí na internetu splašit kýženou koledu ve formátu MIDI. Pomůže třeba Google. Já nakonec využil služeb webu FreeMidi.org a stáhl jednu z verzí koledy Tichá noc.

Když si ji spustíte, počítač podle povelů v souboru začne syntetizovat různé hudební nástroje. Je jich tam hned několik. Dominantní jsou klávesy, nás ale v tomto konkrétním případě zajímá základní melodie, která prostupuje celou skladbou. U jiných nahrávek bychom mohli zvolit pro laser jiné těleso.

Pomocí knihovny pretty_midi si je můžeme nechat všechny vypsat tímto způsobem:

import pretty_midi

midi = pretty_midi.PrettyMIDI("SilentNight.mid")

print("Koleda obsahuje tyto nástroje:")
for instrument in midi.instruments:
    print(f" 🎵 {instrument.name}")

Když kód uložíme do souboru midi.py a spustíme jej v interpretu jazyka Python, výsledek bude vypadat jako na obrázku níže:

Klepněte pro větší obrázek
Koleda obsahuje tyto nástroje. Nás zajímá melodie

Řekli jsme si, že nás zajímá instrument melodie. Každý takový nástroj už obsahuje seznam objektů Note (nota), které má zahrát, a to se všemi klíčovými parametry. Nás bude zajímat pitch (číselný kód noty), start (začátek v sekundách) a end (konec v sekundách).

Celou skladbu Tichá noc v podání samotné melodie koledy vypíšeme tímto způsobem:

import pretty_midi

midi = pretty_midi.PrettyMIDI("SilentNight.mid")

melodie = None

print("Koleda obsahuje tyto nástroje:")
for instrument in midi.instruments:
    print(f" 🎵 {instrument.name}")
    if "Melody" in instrument.name:
        melodie = instrument

if melodie != None:
    print("\nSkladba obsahuje melodii a toto je sled jejích tónů:")

    for ton in melodie.notes:
        start = int(ton.start * 1000)
        konec = int(ton.end * 1000)
        print(f" Čas: {start}-{konec} ms\tKód noty: {ton.pitch}")

Když opět spustíme upravený soubor midi.py v interpretu Pythonu, obrazovka se nám nyní zaplní řádky, z nichž každý představuje jednu zahranou notu skladby Tichá noc nástrojem (Melody) včetně určení času v milisekundách začátku a konce noty.

Klepněte pro větší obrázek
Seznam jednotlivých not a jejích časů ve skladbě

Pozor, v kódu výše pracujeme s exaktními názvy jednotlivých instrumentů v souboru MIDI, stejně tak se ale může stát, že načteme soubor, který bude mít jen jednu nepojmenovanou stopu. Náš hotový generátor na GitHubu na to myslí, a pokud nenajde nástroj podle názvu, který požadujeme, zvolí první v pořadí.

Ještě musíme převést notu na frekvenci

Teď už jen stačí převést numerické kódy not formátu MIDI na skutečnou zvukovou frekvenci v Hz. S tím nám pomůže už zmíněná tabulka, která obsahuje frekvence pro kódy 0 až 127. Jednotlivé frekvence jsem tedy z tabulky vytáhl, seřadil po řádcích a uložil do souboru frekvencetonu.txt.

Klepněte pro větší obrázekKlepněte pro větší obrázek
Z tabulky jsem vytáhl frekvence a uložil je do textového souboru. Ale pozor, v celočíselné podobě bez desetinných míst a hlavně v opačném pořadí než na webu, kde jsou noty seřazené od nejvyšší po nejnižší

V Pythonu pak mohu takový soubor řádek po řádku načíst do pole s tím, že číslo řádku odpovídá kódu MIDI:

frekvence = []
with open("frekvencetonu.txt", "r") as soubor:
    frekvence = soubor.read().split("\n")

A v původním kódu vypisovat namísto identifikátoru noty rovnou její frekvenci:

print(f" Čas: {start}-{konec} ms\tTón: {frekvence[ton.pitch]} Hz")

Takže celý upravený kód nyní bude vypadat takto:

import pretty_midi

midi = pretty_midi.PrettyMIDI("SilentNight.mid")

frekvence = []
with open("frekvencetonu.txt", "r") as soubor:
    frekvence = soubor.read().split("\n")

melodie = None

print("Koleda obsahuje tyto nástroje:")
for instrument in midi.instruments:
    print(f" 🎵 {instrument.name}")
    if "Melody" in instrument.name:
        melodie = instrument

if melodie != None:
    print("\nSkladba obsahuje melodii a toto je sled jejích tónů:")

    for ton in melodie.notes:
        start = int(ton.start * 1000)
        konec = int(ton.end * 1000)
        print(f" Čas: {start}-{konec} ms\tTón: {frekvence[ton.pitch]} Hz")

Výsledek po spuštění v interpretu jazyka Python konečně místo kódů not ukáže cílové frekvence, kterým bude rozumět i Arduino:

Klepněte pro větší obrázek
Seznam jednotlivých tónů v Hz a jejich začátky a konce ve skladbě

Z časů vypočítáme posloupnost tónů a ticha

Známe začátek, délku, konec a frekvenci každého po sobě jdoucích tónů, takže z rozdílů konců a začátků ještě dopočítáme prodlevy, kdy nástroj nehraje a celý sled tónů uložíme jako dvourozměrné pole, které bude moci být součástí zdrojového kódu C/C++ pro prostředí Arduino.

V poli bude uložená vždy frekvence a doba v milisekundách, kterou pak zahrajeme pomocí funkce tone.

Kompletní generátor kódu pro Arduino

Abych vám to maximálně usnadnil, nakonec jsem jednoduchý kód v Pythonu proměnil v kompletní generátor, který vytvoří všechny zdrojové soubory pro Arduino Uno v jednom kroku.

Klepněte pro větší obrázek
Generátor z MIDI koledy SilentHil.mid vytvořil zdrojové soubory pro Arduino

To znamená hlavičkový soubor SilentHil.mid.h s obřím polem skladby, které se nicméně nebude po spuštění načítat do maličké RAM řídícího čipu destičky, ale budeme jej číst políčko po políčku přímo z programové (flashové) paměti firmwaru.

Klepněte pro větší obrázekKlepněte pro větší obrázek
Zdrojové soubory pro Arduino Uno. Přehrávač koledy zabere v paměti RAM jen pár bajtů

A zároveň nám skript v Pythonu vygeneruje hlavní zdrojový soubor prehravac.ino, který počítá s tím, že je laserové ukazovátko (nebo přímo bzučák/reproduktor) připojený opět na pin číslo 3.

Zdrojové kódy generátoru kódu koledy pro Arduino najdete na GitHubu našeho seriálu Pojďme programovat elektroniku

Po přeložení a nahrání programu do desky Arduino Uno se začne v nekonečné smyčce stále dokola přehrávat naše koleda.

Vygenerovat, flashnout a zahrát

Stačí tedy stáhnout všechny soubory generátoru, nainstalovat knihovnu pretty_midi, stáhnout libovolný soubor MIDI s koledou a generátor spustit příkazem:

python generator.py souborskoledou

Nebo:

python3 generator.py souborskoledou

Podle konfigurace vašeho operačního systému a verze Pythonu.

V případě naší koledy SilentNight.mid by to tedy vypadalo takto:

python3 generator.py SilentNight.mid

Vygenerované soubory prehravac.ino a SilentNight.mid.h stačí následně uložit do stejnojmenného adresáře prehravac, otevřít jej ve vývojovém prostředí Arduino, přeložit a nahrát do desky Arduino Uno. Zároveň můžete experimentovat s jinými koledami a soubory ve formátu MIDI a samozřejmě nitrem generátoru, kde můžete snadno nastavit instrument, podle kterého se má koleda vytvářet.

Takže hurá do toho a ať lasery během Vánoc blikají v každé domácnosti!

Laserová Tichá noc z koledového generátoru:

Diskuze (6) Další článek: Microsoft potvrdil problémy s výkonem disků NVMe SSD ve Windows 11 a vydal opravu

Témata článku: Google, Internet, Programování, Pojďme programovat elektroniku, Wikipedia, Slunce, Python, Arduino, GitHub, Laser, PWM, Arduino Uno, Koleda, Frekvence, Dioda, Print, Midi, None, Drátek, Pro, Instrument