[PROJEKT] Wskaźnik wysterowania mikrofonu MEMS na panelu KAmodWS2812-8 + Arduino
Mając przygotowany i połączony sprzęt można przejść do oprogramowania Arduino (przygotowany program znajduje się w sekcji „Do pobrania”). W pierwszym kroku dołączana jest biblioteka do sterowania diodami, a także zdefiniowana jest: liczba diod, linia z której korzystają oraz liczba próbek, z której będziemy odtwarzać wartość szczytową.
#include "Adafruit_NeoPixel.h" #define N 32 #define LICZBA_DIOD 16 #define PIN 6
Następnie deklarowane są zmienne globalne potrzebne w programie oraz następuje inicjalizacja biblioteki NeoPixel, za pomocą której sterowany jest łańcuch diod.
int peakL = 0, peakR = 0; // wartość szczytowa w buforze int diodyL = 0; // liczba diod, które należy zapalic int diodyR = 0; int buforL[N], buforR[N]; // bufor w którym znajduje się poprzednie N próbek int i = 0, j = 0; // liczniki petli const float offset=log(1.65); // zlogarytmowana skladowa stala napiecia na mikrofonie Adafruit_NeoPixel pixels = Adafruit_NeoPixel(LICZBA_DIOD, PIN, NEO_GRB + NEO_KHZ800); // inicjalizacja lancucha diod
W funkcji odbierzSygnal() pobierana jest z przetwornika ADC aktualna wartość dla obu mikrofonów: lewego i prawego. Następnie obie wartości są wprowadzane do buforów kołowych o długości N. Aktualne wartości są wpisywane na i-tą pozycję tablicy, gdzie zastępują próbkę najstarszą. Pętla for wyszukuje w całym buforze (który mieści próbkę aktualną i N-1 poprzednich) wartość najwyższą i wpisuje ją do zmiennej peak zarówno dla prawego, jak i lewego kanału.
void odbierzSygnal(void) { peakL = 0; peakR = 0; buforL[i] = analogRead (A0); buforR[i] = analogRead (A1); (++i)%=N; for(j=0;j<N;j++) { if (peakL< buforL[j]) peakL = buforL[j]; if (peakR< buforR[j]); peakR=buforR[j]; } }
Funkcja przeliczPoziom() działa jako konwerter wartości szczytowej sygnału na liczbę diod jaką należy wyświetlić. Najpierw wartość próbki jest przeliczana na wartość w woltach, następnie logarytmowana. Logarytm o podstawie 10 jest tu użyty, aby wskaźnik reagował także na cichsze dźwięki oraz ze względu na charakterystykę ludzkiego ucha. Dzięki temu układ wskazuje natężenie w przybliżeniu odpowiadające naturalnemu odczuwaniu głośności. Następnie odejmowana jest zlogarytmowana składowa stała, a całość dzielona przez 0,3 (ok. log10(2)). W ten sposób otrzymujemy wartość wyskalowaną od 0 do 1, a więc należy pomnożyć ją przez 8 i zaokrąglić, aby otrzymać liczbę diod, jaką należy zapalić. Przetwornik ADC w Arduino jest zasilany napięciem 5V i może przyjmować wartość napięcia aż do VCC. W związku z tym maksymalna liczba zapalonych diod dla kanału ograniczana jest do 8, gdyż w przeciwnym wypadku wskazanie będzie przenosić się na sąsiedni panel.
void przeliczPoziom(void) { diodyL = round((log(peakL*(5.0/1023.0))-offset)/0.3*8.0); diodyR = round((log(peakR*(5.0/1023.0))-offset)/0.3*8.0); if (diodyL > 8) diodyL = 8; if (diodyR > 8) diodyR = 8; }
Funkcja zaswiecDiody() steruje łańcuchami w zależności od wcześniej przeliczonej liczby diod do zapalenia. Należy mieć świadomość, że dla tak połączonego łańcucha dioda nr 8 jest diodą najwyższą w łańcuchu lewym, a dioda nr 15 – najniższą tamże. Oznacza to, że dla lewego kanału diody trzeba zapalać w odwrotnej kolejności. W bibliotece NeoPixel funkcja clear() wygasza cały łańcuch (niezbędne, aby diody nie zatrzaskiwały się po przekroczeniu pewnego poziomu), natomiast funkcja show() wysyła wprowadzone wartości do łańcucha.
void zaswiecDiody(void) { pixels.clear(); for(int ii=0; ii<diodyR; ii++) { pixels.setPixelColor(ii, 64, 0, 64); } for(int jj=15; jj>15-diodyL; jj--) { pixels.setPixelColor(jj, 64, 64, 0); } pixels.show(); delay(1); }
W funkcji setup() inicjalizowane są tylko moduły WS2812-8:
void setup() { pixels.begin(); }
W funkcji loop() wywoływane są po kolei wcześniej omówione funkcje:
void loop() { odbierzSygnal(); przeliczPoziom(); zaswiecDiody(); }
Działanie programu zostało przedstawione w poniższym filmie: