[089] Przejmowanie kontroli nad urządzeniami - multiefekt gitarowy - cz. 2

[089] Przejmowanie kontroli nad urządzeniami - multiefekt gitarowy - cz. 2

Po lekturze pierwszej części opisującej przejmowanie kontroli nad muliefektem, część sprzętową mamy już przygotowaną i sprawdzoną. Czas więc na niezbędny w każdym takim wypadku test latencji, czyli określenie maksymalnej szybkości wirtualnego wciskania przycisków, które jest rejestrowane w sposób pewny i nie zlewa się w jedno. Coś takiego czyniłem już w artykule o hackowaniu monitora, więc po szczegóły odsyłam tam. Tutaj test będzie wyglądał podobnie. Należy zadbać o podłączenie naszego multiefektu do Arduino: przycisk zwiększający wartości do pinu 11, zmniejszający – do 10.


const byte przyciskPlus = 11;   // Numer pinu, do którego podłączony jest przycisk podwyższający edytowany parametr.
const byte przyciskMinus = 10;  // Numer pinu, do którego podłączony jest przycisk obniżający edytowany parametr.

void setup() {
  pinMode(przyciskPlus, OUTPUT);  // Zadeklaruj porty przycisków jako wyjścia.
  pinMode(przyciskMinus, OUTPUT);
}

void loop() {
  for (byte x = 0; x < 100; x++) {     // Powtarzaj 100 razy.
    digitalWrite(przyciskPlus, HIGH);  // Wciśnij przycisk.
    delay(35);                         // Dobierz najkrótszy czas, przy którym za każdym razem wartość się zwiększa.
    digitalWrite(przyciskPlus, LOW);   // Puść przycisk.
    delay(35);                         // Dobierz najkrótszy czas, przy którym system interpretuje osobne wciśnięcia.
  }
  for (byte x = 0; x < 100; x++) {
    digitalWrite(przyciskMinus, HIGH);
    delay(35);
    digitalWrite(przyciskMinus, LOW);
    delay(35);
  }
}

W programie definiujemy porty, ustawiamy je i wykorzystujemy fragment z dwoma pętlami wykonującymi się po sto razy każda: jedna wysyła impulsy na przycisk zwiększający wartości, druga – na zmniejszający. Zanim uruchomimy ten program, należy wejść w edycję którejś z wartości głośności multiefektu, bo te przyjmują wartości od 0 do 99. Po kompilacji powinniśmy widzieć przyrost liczb do ostatniej i powrót do zera: bez przerw, płynny i przede wszystkim w całym zakresie: od początku do końca. Zgubienie jakiejkolwiek wartości czy pojawianie się nieregularnych pauz oznaczać będzie, że opóźnienia są zbyt krótkie. Jak widać, 35 ms zarówno dla wciśnięcia jak i puszczenia przycisku jest parametrem pewnym, dobranym empirycznie. Niestety to dość sporo, opóźnienie dla całego obrotu potencjometrem będzie długie – aż 7 sekund, ale tak radykalne regulacje wykonuje się rzadko. W praktyce będziemy dokonywać niewielkich korekt, a poza tym większość parametrów ma tu dużo mniejszą rozdzielczość.

const byte przyciskPlus = 11;   // Numer pinu, do którego podłączony jest przycisk podwyższający edytowany parametr.
const byte przyciskMinus = 10;  // Numer pinu, do którego podłączony jest przycisk obniżający edytowany parametr.

const byte potencjometrBAS = A0;  // Adres portu potencjometru.
int wartoscBAS = 512;             // Wartość odczytanego napięcia pochodzącego ze ślizgacza potencjometru.
int zmianaBAS = 512;              // Tymczasowa wartość odczytanego napięcia do porównań.
byte wirtualnyBAS = 5;            // Pozycja wirtualnego potencjometru odpowiedzialnego za poziom parametru.
bool aktywnyBAS;                  // Wskaźnik ustawiany po wykryciu zmiany położenia potencjometru.

void setup() {
  pinMode(przyciskPlus, OUTPUT);  // Zadeklaruj porty przycisków jako wyjścia.
  pinMode(przyciskMinus, OUTPUT);
}
void loop() {

  zmianaBAS = analogRead(potencjometrBAS);  // Odczytaj wartość napięcia ze ślizgacza potencjometru.
  if (abs(wartoscBAS - zmianaBAS) > 3) {    // Jeśli odczytana wartość jest większa od 3 od wartości poprzedniej...
    wartoscBAS = zmianaBAS;                 // Uaktualnij wartość napięcia dla pozostałych modułów programu.
    aktywnyBAS = true;                      // Ustaw wskaźnik zmiany położenia potencjometru.
  }

  if (aktywnyBAS == true) {                                   // Jeśli wykryto zmianę położenia potencjometru...
    aktywnyBAS = false;                                       // Kasuj wskaźnik zmiany położenia potencjometru.
    byte przeskalowanyBAS = map(wartoscBAS, 0, 1024, 1, 10);  // Przelicz położenie potencjometru na zakres zmian bieżącego parametru.
    while (wirtualnyBAS > przeskalowanyBAS) {                 // Porównaj położenie potencjometru rzeczywistego z wirtualnym.
      wcisnijMinus();                                         // Tyle raz wciskaj przycisk minus, aż oba będą miały tę samą wartość.
      wirtualnyBAS--;
    }
    while (wirtualnyBAS < przeskalowanyBAS) {
      wcisnijPlus();
      wirtualnyBAS++;
    }
  }
}
void wcisnijPlus() {  // Procedura wciskania przycisku Plus - dobierz opóźnienia do sterowanego urządzenia.
  digitalWrite(przyciskPlus, HIGH);
  delay(35);
  digitalWrite(przyciskPlus, LOW);
  delay(35);
}
void wcisnijMinus() {  // Procedura wciskania przycisku Minus - dobierz opóźnienia do sterowanego urządzenia.
  digitalWrite(przyciskMinus, HIGH);
  delay(35);
  digitalWrite(przyciskMinus, LOW);
  delay(35);
}

Wracamy do programu właściwego. Teraz w module, który kontrolnie przesyłał wartość na wyświetlacz umieścimy algorytm zamiany pozycji gałki potencjometru na szereg impulsów, które będą ustawiać tę wartość w urządzeniu. Na razie zajmiemy się jednym tylko parametrem: korektorem niskich częstotliwości, czyli regulatorem basów. Spójrzmy najpierw jak tu zachowuje się multiefekt: regulacja basów przebiega w dziewięciu krokach – od b1 do b9, przy czym brak korekcji jest krokiem środkowym, opisanym jako b5.

Powołamy teraz pomocniczą zmienną przeskalowanyBAS, związaną z pozycją potencjometru, ale przeskalowaną do zakresu od 1 do 9. Przyjrzyjmy się temu fragmentowi:

byte przeskalowanyBAS = map(wartoscBAS, 0, 1024, 1, 10);

Spójrzmy na dwa istotne szczegóły: zakres pierwotny wybraliśmy o jeden większy od rzeczywistego (0-1024), a to dlatego, że zakres wtórny obejmuje liczby od 1 do 10. Jednak wartość pierwotna 1024 nigdy nie nadejdzie – a więc i dziesiątka po przeskalowaniu. Po co więc całe to kombinowanie? Bo gdybyśmy wstawili tutaj zgodne z logiką przedziały 0-1023 oraz 1-9, dziewiątka pojawiałaby się tylko przy domknięciu potencjometru do końca. Innymi słowy, wartości od 1 do 8 miałyby swoje obszary obrotu, a dziewiątka – nie. Byłoby to niewygodne. Z tym problemem spotkałem się już nieraz w produktach komercyjnych, gdy okazywało się, że maksymalna wartość nastawiana potencjometrem jest dostępna w mniejszym zakresie obrotu od pozostałych. Taki sposób przydziela dziewiątce mniej więcej jedną dziewiątą dostępnego kąta pracy gałki i dobre projekty mają takie mechanizmy zaimplementowane.

Teraz będziemy musieli pokonać jeden z dwóch dużych problemów idei zamiany gałek na impulsy: latencję. Nie można sobie tak po prostu wysyłać danych jakimś magicznym sposobem. Położenie potencjometru musi wysłać odpowiednią ilość impulsów – ale przecież nie wiadomo jaką, bo multiefekt nie zwraca żadnej informacji! W pamięci Arduino musimy więc odtworzyć mapę stanów multiefektu i odwoływać się do niej. Oczywiście wierząc, że jest ona tożsama z mapą w urządzeniu. Będzie tak, dopóki nie pogubimy impulsów, a o to już zadbaliśmy, dobierając odpowiednie opóźnienia.

Wirtualna mapa stanów będzie się składać ze zmiennych opisujących parametr z przedrostkiem wirtualny. Tak więc w liście deklaracji dorzucamy kolejną zmienną (wirtualnyBAS), tym razem ograniczoną do bajta, bo 255 wartości jest aż za dość. Teraz już mamy wszystko co trzeba.

Sposób jest zaskakująco prosty. Porównujemy naszą przeskalowaną pozycję potencjometru z wirtualną. Za każdym razem gdy nie są równe, wysyłamy odpowiedni impuls – co oczywiście chwilę trwa – i podbijamy te wirtualne w górę lub w dół. W końcu za którymś razem zrównają się i będziemy mogli opuścić pętlę.

Spójrzmy jak to świetnie działa. Przy raptem dziewięciu pozycjach latencja nie przeszkadza. 70 ms na działkę daje 630 ms na przejście przez cały zakres, a to i tak szybciej od możliwości przekręcenia gałką. Mamy tylko jeden problem. Jak już sobie będziemy kręcić basami, wszystko będzie działać jak należy. Ale po resecie, dopóki nie zrobimy pełnego obrotu, gałka będzie pokazywać inne wartości niż te, które będą na wyświetlaczu. Żeby były spójne, trzeba kalibracji, czyli obrotu od końca do końca. Trochę to niewygodne. Czy jest na to jakiś sposób?

int wartoscBAS = 512;             // Wartość odczytanego napięcia pochodzącego ze ślizgacza potencjometru.
int zmianaBAS = 512;              // Tymczasowa wartość odczytanego napięcia do porównań.
byte wirtualnyBAS = 5;            // Pozycja wirtualnego potencjometru odpowiedzialnego za poziom parametru.

W tej sytuacji jakiekolwiek odstępstwo położenia gałki względem środka wygeneruje tyle impulsów, ile potrzeba. Ale żeby to miało sens, w samym multiefekcie należy zagospodarować jeden z programów – najlepiej pierwszy, który zgłasza się po włączeniu urządzenia i zapiszemy tam poziom basów na pozycji b5. Tylko wtedy zyskamy synchronizację między Arduino, a multiefektem po włączeniu obu urządzeń i obie mapy nastaw będą tożsame. Pokonaliśmy pierwszy duży problem, pozostał drugi – ale o tym napiszę w kolejnym artykule.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl