W tym artykule zajmiemy się praktycznym wykorzystaniem 16-kanałowego sterownika ADEAS PCA9685 który umożliwia sterowanie 16 różnymi serwomechanizmami za pomocą komunikacja I2C. Moduł ten dzięki modulowanemu sygnałowi timera PWM umożliwia precyzyjne sterowaniem aktualnego położenia serwomechanizmu. W celu prezentacji działania modułu wykorzystamy dwa serwomechanizmy które będziemy wychylać o zadaną pozycję w zakresie od 0 do 270 stopni. Cały proces odbędzie się w pętli więc ruch mechanizmu będzie płynny. Kąt obrotu ustalimy z poziomu widgetu slider, którego praktyczne zastosowanie znajdziecie w artykule Obsługa widgetu Slider.
Przejdźmy więc do stworzenia dwóch nowych punktów pomiarowych, które będą przechowywały aktualną ustawioną wartość obrotu naszego serwomechanizmu. Oba punkty pomiarowe powinny być typu numerycznego, o dowolnej nazwie którą później wykorzystamy w programie i domyślnej wartości ustawionej na 0.
Po stworzeniu punktów pomiarowych przejdźmy do dodania nowego widgetu. Wybierzmy z dostępnych opcji pozycję "Slider".
Podczas konfiguracji naszego widoku, dodając punkty pomiarowe mamy możliwość wyboru zakresu oraz przesuwu suwaka. Na potrzeby pracy z modułem ADEAS PCA9685 ustawmy zakres na Min = 0, Max = 3000 a przesuw jako 500. Dla tego rodzaju serwomechanizmu wartość maksymalna to 4096 więc wartość 3000 w naszym prrzypadku da wynik około 270 stopni. W przypadku wyboru mniejszych wartości przesuwu jak i zakresu, różnice w obrocie serwomechanizmu nie będą dobrze widoczne.
Po poprawnej konfiguracji widoku, w dashboardzie pojawi się następujące okno :
Możemy przejść teraz do podłączenia urządzenia ADEAS UNO do naszego modułu. Zanim podłączymy nasze urządzenie, przeanalizujmy piny znajdujące się na sterowniku ADEAS PCA9685. Zwarta para pinów 5V-Vin umożliwia nam bezpośrednie zasilenie sekcji serwomechanizmów jak i płytki z głównego zasilania bez konieczności osobnego zasilenia obu układów. Druga para pinów J_OE po zwarciu uruchamia sygnał wyjściowy w płytce. Zewrzyjmy wobec tego obie pary pinów w celu prostszej obsługi naszego sterownika.
Do poprawnego działania układu niezbędne jest podłączenie zasilania 5V prądu stałego. Ważne jest, aby nie podłączyć wyższego napięcia, ponieważ może to spowodować uszkodzenia układu. Pozostałe wyprowadzenia na naszym sterowniku, służą do komunikacji za pomocą interfejsu I2C z urządzeniem ADEAS UNO i należy je podłączyć zgodnie z poniższym schematem:
Pozostało nam podłączyć serwomechanizmy. Wszystkie wyprowadzenia oznaczone są na końcu płytki napisami BROWN, RED i ORANGE co odpowiada kolorystyce przewodów występujących w większości dostępnych na rynku serwomechanizmów. Jeżeli wasz serwomechanizm posiada przewody o odmiennej kolorystyce, należy je podłączyć zgodnie ze schematem: BROWN - GND, RED - VCC, ORANGE - SYGNAŁ.
Mając gotowe podłączenia do sterownika możemy przejść do napisania kodu do sterowania serwomechanizmami. Skorzystamy z dostępnej w bibliotece ADEAS.h klasy Serwo bazującej na bibliotece Adafruit-PWM-Servo-Driver-Library, która świetnie sprawdza się w przypadku pracy z wieloma serwomechanizmami jednocześnie. Link do pobrania biblioteki znajdziecie tutaj: Biblioteka ADEAS_PCA9685.H.
Stwórzmy nowy szkic w środowisku Arduino IDE i wklejmy poniższy kod, który krok po kroku za chwilę przeanalizujemy.
#include <ADEAS.h>
#include <ADEAS_PCA9685.h>
#define SERVO_FREQ 50
Servo pwm = Servo();
String servo;
unsigned long previousMillis = 0;
const unsigned long interval = 10000;
int currentStep = 0;
int value = 0;
int previousValue1 = 0;
int previousValue2 = 0;
void moveServo(int value, int number, int previousValue) {
if (previousValue < value) {
for (uint16_t pulselen = previousValue; pulselen < value; pulselen += 1) {
pwm.setPWM(number, 0, pulselen);
}
} else {
for (uint16_t pulselen = previousValue; pulselen > value; pulselen -= 1) {
pwm.setPWM(number, 0, pulselen);
}
}
}
void setup() {
ADEAS::setup("WIFI_SSID", "WIFI_PASS", "TOKEN");
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ);
pwm.setPWM(0, 0, 0);
pwm.setPWM(1, 0, 0);
}
void loop() {
unsigned long currentMillis = millis();
ADEAS::process();
if (API::isReady()) {
switch (currentStep) {
case 0:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servo = API::getLastSet("serwo_1");
servo.replace("\"", "");
value = servo.toInt();
moveServo(value, currentStep, previousValue1);
previousValue1 = value;
currentStep = 1;
}
break;
case 1:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servo = API::getLastSet("serwo_2");
servo.replace("\"", "");
value = servo.toInt();
moveServo(value, currentStep, previousValue2);
previousValue2 = value;
currentStep = 0;
}
break;
}
}
}
Kod może wydawać się lekko skomplikowany, dlatego przenalizujmy teraz wszystkie jego składowe zaczynając od definicji stałych.
#define SERVO_FREQ 50
SERVO_FREQ - stała, której użyjemy do ustalenia częstotliwości dla timera PWM.
Servo pwm = Servo();
String servo;
unsigned long previousMillis = 0;
const unsigned long interval = 10000;
int currentStep = 0;
int value = 0;
int previousValue1 = 0;
int previousValue2 = 0;
Servo pwm = Servo() - stworzenie nowego obiektu klasy Servo na którym będziemy wywoływać wszystkie metody służące do obsługi serwomechanizmów.
String servo - zmienna przechowująca wartość przesuwu z naszego suwaka na platformie ADEAS APP.
unsigned long previousMilis = 0 - zmienna przechowująca ostatnią wartość czasu mierzonego od początku działania programu.
int currentStep = 0 - poinformowanie programu który serwomechanizm należy aktualnie uruchomić zaczynając od 0 dla pierwszego serwomechanizmu.
int value = 0 - wartość o jaką należy przesunąć serwomechanizm otrzymana z aplikacji przez WEB API.
int previousValue1, int previousValue2 - wartości przechowujące ostatnie wychylenie danego serwomechanizmu.
void moveServo(int value, int number, int previousValue) {
if (previousValue < value) {
for (uint16_t pulselen = previousValue; pulselen < value; pulselen += 1) {
pwm.setPWM(number, 0, pulselen);
}
} else {
for (uint16_t pulselen = previousValue; pulselen > value; pulselen -= 1) {
pwm.setPWM(number, 0, pulselen);
}
}
}
Funkcja ta odpowiada za obsługę poruszania się serwomechanizmu. Przyjmuje trzy argumenty: int value - wyciągnięte z bazy danych aktualne przemieszczenie serwomechanizmu które ma wykonać, int number - numer serwomechanizmu który należy przemieścić, int previousValue - poprzednia wartość przemiesczenia.
for (uint16_t pulselen = previousValue; pulselen < value; pulselen += 1) {
pwm.setPWM(number, 0, pulselen);
}
Pętla odpowiadająca za płynne przesuwanie mechanizmu do wartości wskazanej z poziomu widgetu slider. Za pomocą metody setPWM obiektu pwm przesuwamy odpowiedni mechanizm o numerze zależnym od zmiennej number i powtarzamy ten krok aż zmienna pulselen osiągnie wartość wskazaną przez suwak. Wychylenie odbywa się zgodnie z domyślnym kierunkiem wychylenia serwomechanizmu.
for (uint16_t pulselen = previousValue; pulselen > value; pulselen -= 1) {
pwm.setPWM(number, 0, pulselen);
}
Obrót mechanizmu w przeciwnym kierunku do podstawowego kierunku obrotu aż do wskazanej wartości value.
void setup() {
ADEAS::setup("WIFI_SSID", "WIFI_PASS", "TOKEN");
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ);
pwm.setPWM(0, 0, 0);
pwm.setPWM(1, 0, 0);
}
W funkcji setup inicjujemy na początku za pomocą metody ADEAS::setup połączenie z bazą danych i wi-fi zgodnie z parametrami naszego wi-fi i naszym indywidualnym tokenem. następnie realizujemy kroki konfiguracyjne dla naszego sterownika serwomechanizmami.
pwm.begin() - inicjacja połączenia urządzenia ADEAS UNO ze sterownikiem PCA9685.
void loop() {
unsigned long currentMillis = millis();
ADEAS::process();
if (API::isReady()) {
switch (currentStep) {
case 0:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servo = API::getLastSet("serwo_1");
servo.replace("\"", "");
value = servo.toInt();
moveServo(value, currentStep, previousValue1);
previousValue1 = value;
currentStep = 1;
}
break;
case 1:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servo = API::getLastSet("serwo_2");
servo.replace("\"", "");
value = servo.toInt();
moveServo(value, currentStep, previousValue2);
previousValue2 = value;
currentStep = 0;
}
break;
}
}
}
Na początku pętli za pomocą funkcji unsigned long currentMillis = millis() ustawiamy aktualny czas na czas który upłynął od początku rozpoczęcia pracy programu. Następnie rozpoczynamy komunikację z ESP32 metodą ADEAS::process() i sprawdzamy warunkiem if(API::isReady()) czy komunikacja z WEB API jest gotowa.
Funkcja switch(currentStep) sprawdza, który aktualnie serwomechanizm będziemy obsługiwać.
case 0:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servo = API::getLastSet("serwo_1");
servo.replace("\"", "");
value = servo.toInt();
moveServo(value, currentStep, previousValue1);
previousValue1 = value;
currentStep = 1;
}
break;
case 0 informuje nas, że jest to pierwszy mechanizm. Warunkiem if (currentMillis - previousMillis >= interval) odczekujemy 10 sekund a następnie przypisaniem previousMillis = currentMillis ustawiamy poprzedni czas na aktualny czas w celu wyzerowania naszego timera oczekującego.
Do zmiennej servo przypisujemy wyciągniętą z bazy danych za pomocą komunikacji WEB API ostatnią przypisaną do zadanego punktu pomiarowego wartość domyślną servo = API::getLastSet("serwo_1").
Wywołujemy funkcję moveServo(servo, currentStep, previousValue1) podając jako argumenty wartość pozycji do jakiej ma obrócić się nasz serwomechanizm, numer mechanizmu na którym będziemy operować oraz poprzednią wartość o jaką obracaliśmy nasz mechanizm. Na końcu nadpisujemy ostatnią wartość na aktualnie otrzymaną wartość oraz zmieniamy wartość zmiennej currentStep na 1 w celu pracy na następnym mechaniźmie.
Wgrajmy nasz program uprzednio ustawiając wartości na naszych suwaka na przykładowe 1500 i 2500.
Zaobserwujmy jak zachowają się serwomechanizmy na podstawie różnicy ich wychyłu tak jak na filmie poniżej.