Skocz do zawartości

Kurs Arduino - #6 - kontynuacja UART, serwomechanizmy


Komentator

Pomocna odpowiedź

Cześć, poniżej kod mojego programu - światłomierz z kalibracją.

Kalibracja włącza się jak wciśnie się jeden przycisk a wyłącza jak wciśnie się drugi. W trakcie kalibracji świeci się dioda.


 

#include <Servo.h>

#define calibrationStartBtn 2
#define calibrationFinishBtn 3
#define calibrationLED 5

#define servoPin 10

#define lightMeterInput A5

int minLightMeter = 0;
int maxLightMeter = 1023;

int currentAngle = 0;
int readValue = 0;
int newAngle = 0;

Servo servo;

void setup() {
  pinMode(calibrationStartBtn, INPUT_PULLUP);
  pinMode(calibrationFinishBtn, INPUT_PULLUP);
  pinMode(calibrationLED, OUTPUT);
  servo.attach(servoPin);
  servo.write(currentAngle);
}

void loop() {
  if (digitalRead(calibrationStartBtn) == LOW) { //jak przycisk startu kalibracji wciśnięty
    calibrate();
  }
  newAngle = map(analogRead(lightMeterInput), minLightMeter, maxLightMeter, 0, 180);
  if (abs(newAngle - currentAngle)>5) {
    currentAngle = newAngle;
    servo.write(newAngle);
  }
  delay(200);
}

void calibrate() {
  minLightMeter = 1023; //max odczyt z 10-bitowego przetwornika ADC
  maxLightMeter = 0; //min odczyt z przetwornika
  digitalWrite(calibrationLED, HIGH);
  while (digitalRead(calibrationFinishBtn) == HIGH)  { //mierz dopoki nie zostanie wciscniety przycisk konca kalibracji
    int currentLightMeter = analogRead(lightMeterInput);
    if (currentLightMeter <= minLightMeter) minLightMeter = currentLightMeter;
    if (currentLightMeter >= maxLightMeter) maxLightMeter = currentLightMeter;
    delay(100);
  }
  if (minLightMeter == maxLightMeter) { //nieudana kalibracja
    minLightMeter = 0;
    maxLightMeter = 1023;
  }
  digitalWrite(calibrationLED, LOW);
}

A tu zdjęcie mego dzieła.

 

światłomierz.jpg

  • Lubię! 2
Link do komentarza
Share on other sites

zadanie 6.1. Cóż, zrobiłem standardowo, ale wrzucając tu kod, zauważyłem zwrócenie uwagi na

Cytat

po wciśnięciu przycisku podłączonego do Arduino wysyłaj jeden raz linijkę zawierającą informacje

więc spróbowałem zrobić to "naturalnie", czyli patrząc na to czy już ta linijka została wysłana czy nie. Kod:

#define fotoLewy  A1
#define fotoPrawy A0
#define potencjometr A5
#define przycisk 2

void setup() {
  pinMode(przycisk, INPUT_PULLUP);
  Serial.begin(9600); //Inizjalizacja UART
}

int n = 0;
boolean przyciskStan = HIGH; // czyli nie jest wciśnięty
boolean wyslanyTekst = false;

void loop() {
  przyciskStan = digitalRead(przycisk);
  if (przyciskStan == LOW && wyslanyTekst == false){ //przycisk wciśnięty i tekst nie wysłany
    Serial.print("Fotorezystor 1:\t" + String(analogRead(fotoLewy)) + ", fotorezystor:\t" + String(analogRead(fotoPrawy)) + ", potencjometr:\t" + String(analogRead(potencjometr)) + ", przycisk wciśnięto\t" + String(n) + " razy\n");
    n++;
    wyslanyTekst = true;
  } else if (przyciskStan == HIGH && wyslanyTekst == true){// przycisk nie wcisniety i tekst juz wyslany
    wyslanyTekst = false; //czyli czekamy na kolejne naciśnięcie przycisku, zeby wysłać ponownie
  }
}

Pierwszy warunek był intuicyjny, ale dalej jednak wyszło skomplikowanie.

Więc druga wersja po prostu z rozróżnianiem stanu i stanu poprzedniego. Kod:

#define fotoLewy  A1
#define fotoPrawy A0
#define potencjometr A5
#define przycisk 2

void setup() {
  pinMode(przycisk, INPUT_PULLUP);
  Serial.begin(9600); //Inizjalizacja UART
}

int n = 0; //liczba ile razy przycisk został wciśnięty
boolean przyciskStan = HIGH; // czyli nie jest wciśnięty
boolean przyciskPoprzedniStan = HIGH; // czyli poprzednio też nie wciśnięty

void loop() {
  przyciskStan = digitalRead(przycisk);
  if (przyciskStan == LOW && przyciskPoprzedniStan == HIGH){ //przycisk wciśnięty i w poprzednim odczycie nie był wciśniety
    Serial.print("Fotorezystor 1:\t" + String(analogRead(fotoLewy)) + ", fotorezystor:\t" + String(analogRead(fotoPrawy)) + ", potencjometr:\t" + String(analogRead(potencjometr)) + ", przycisk wciśnięto\t" + String(n) + " razy\n");
    n++;
  }
  przyciskPoprzedniStan = przyciskStan;
}

No i ta druga wersja jednak lepsza. Jest sprawdzanie 1 warunku mniej, więc powinna być szybsza (pewnie niezauważalnie). Dobrze myślę?

  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

@pawelmb Widzę, że skorzystałeś z mechaniki tzw. zatrzasku - super! O to chodziło, żeby tekst był wypisany tylko raz, czyli w momencie wykrycia zmiany stanu. Możesz jeszcze pójść krok dalej i wymyśleć jak to powiązać z zewnętrznymi przerwaniami 🙂 

Link do komentarza
Share on other sites

zadanie 6.3 i zadanie 6.4, mój wskaźnik wygląda tak:

IMG20210227191653.thumb.jpg.254ce1bc9f01549fb0e1d41b6dd57e39.jpg

dodałem wskaźnik kalibracji, bo nie mogłem zapamiętać kiedy ma być jasno a kiedy ciemno.

A jak już taką tarczę mam, to zrobiłem, że przycisk uruchamia kalibrację (w sumie to podobny efekt można było uzyskać przyciskiem reset, ale ta wersja jest lepsza, bo jak program się uruchamia to działa na domyślnych wartościach, jedynie w razie potrzeby można uruchomić kalibrację). Program:

#define foto  A0
#define przycisk 2
#define serwoPIN 3

#include <Servo.h> //Biblioteka odpowiedzialna za serwa

boolean przyciskStan = HIGH; // czyli nie jest wciśnięty
boolean przyciskPoprzedniStan = HIGH; // czyli poprzednio też nie wciśnięty
int swiatloCiemno = 1023; //fotorezystor zwraca dużą wartość jak ciemno
int swiatloJasno = 0; //no tak małej to nie zarejestrowałem ale w te strone

Servo serwomechanizm;  //Tworzymy obiekt, dzięki któremu możemy odwołać się do serwa
byte pozycja = 0; //Aktualna pozycja serwa 0-180
int pozycjaPoprzednia = 0;
boolean kalibracjaDone = true; //zaczynamy z domyślnymi wartosciami
//kalibrację mozna wywołać naciśnieciem przycisku

void setup() {
  pinMode(przycisk, INPUT_PULLUP);
  serwomechanizm.attach(serwoPIN);  //Serwomechanizm podłączony do pinu 3
  Serial.begin(9600);
}

void loop()
{
  if (kalibracjaDone == false) {
    //kalibruj

    //wskazuje na jasno i czeka na przycisk
    serwomechanizm.write(0);
    while (not (przyciskStan == LOW && przyciskPoprzedniStan == HIGH)) { //tak dlugo az nie przycisk wciśnięty
      przyciskPoprzedniStan = przyciskStan;
      przyciskStan = digitalRead(przycisk);
    }
    przyciskPoprzedniStan = przyciskStan;
    swiatloJasno = analogRead(foto);

    //wskazuje na ciemno i czeka na przycisk
    serwomechanizm.write(180);
    while (not (przyciskStan == LOW && przyciskPoprzedniStan == HIGH)) { //tak dlugo az nie przycisk wciśnięty
      przyciskPoprzedniStan = przyciskStan;
      przyciskStan = digitalRead(przycisk);
    }
    przyciskPoprzedniStan = przyciskStan;
    swiatloCiemno = analogRead(foto);
    Serial.println("swiatloCiemno: " + String(swiatloCiemno) + ", swiatloJasno: " + String(swiatloJasno));    kalibracjaDone = true;
  } else {

    przyciskPoprzedniStan = przyciskStan;
    przyciskStan = digitalRead(przycisk);
    if (przyciskStan == LOW && przyciskPoprzedniStan == HIGH) { //przycisk wciśnięty
      //wchodzimy w stan braku kalibracji
      kalibracjaDone = false;
      przyciskPoprzedniStan = przyciskStan;
    }

    //przesuwanie wskaźnika
    int odczytCzujnika = analogRead(foto); //Odczytujemy wartość z czujnika
    pozycja = map(odczytCzujnika, swiatloCiemno, swiatloJasno, 150, 30); //Zamieniamy ją na pozycję serwa
    //jeszzce upewniecie sie, że nie wychyli sie za bardzo
    if (pozycja > 150) {
      pozycja = 150;
    }
    if (pozycja < 30) {
      pozycja = 30;
    }
    if (abs(pozycja - pozycjaPoprzednia) > 5) { //Sprawdzamy czy pozycje różnią się o ponad 5 stopni
      serwomechanizm.write(pozycja); //Wykonajujemy ruch
      pozycjaPoprzednia = pozycja; //Zapamiętujemy aktualną pozycję jako poprzednią
      Serial.println("Czujnik: " + String(odczytCzujnika) + ", pozycja: " + String(pozycja)); //Wysyłamy wartość do terminala
    }
    delay(300); //Opóźnienie dla lepszego efektu
  }
}

Działa świetnie (to znaczy fotorezystor mógłby być jakiś bardziej czuły, bo w sumie przy wieczornym świetle to wartości sie wahają między 990 a 1020, czyli mniej niż rozdzielczość serwomechanizmu).

Fajna zabawka. Film w załączeniu (zzipowany, bo nie można wrzucać filmów, szkoda bo najlepiej widać jak działa).

 

VID20210227192553.zip

  • Lubię! 2
Link do komentarza
Share on other sites

@pawelmb

Cytat

 Film w załączeniu (zzipowany, bo nie można wrzucać filmów, szkoda bo najlepiej widać jak działa).

Też się od tego odbiłem - można wrzucać gify. Są apki na telefon, można sobie bezpośrednio w telefonie przerobić film na gif-a i wysłać tutaj.

Link do komentarza
Share on other sites

3 godziny temu, pawelmb napisał:

Działa świetnie (to znaczy fotorezystor mógłby być jakiś bardziej czuły, bo w sumie przy wieczornym świetle to wartości sie wahają między 990 a 1020, czyli mniej niż rozdzielczość serwomechanizmu).

@pawelmb To nie jest problem czułości fotorezystora, tylko jego a) logarytmicznej charakterystyki, b) bardzo szerokiego zakresu jasności występujących w rzeczywistym świecie (z którymi nasze oko radzi sobie całkiem dobrze) przy ograniczonej rozdzielczości ADC w Arduino (z resztą każdego ADC).

Jeśli chodzi o to pierwsze, to tu masz przykładowy wykres rezystancji od oświetlenia. A przecież i tak pokazano tam tylko mały fragment zakresu jasności jakie spotykamy na co dzień, bo tylko do 1000lx. Jeśli księżycowa noc to 0.2lx, pochmurny dzień to 5000lx a słoneczna plaża to 100000lx (za Wikipedią) to żeby Twój przyrząd dobrze mierzył w całym zakresie, to musi obejmować zakres dynamiczny 100000/0.2=500000. Więc żebyś miał sensowne wyniki zarówno przy Księżycu (czyli wciąż odróżniał pełne jego światło od zajścia za chmurę) jak i w letni, słoneczny dzień, to Twój ADC musiałby rozpoznawać co najmniej 500000 przedziałów napięcia a to oznacza 19 bitów. Arduinowy ADC jest 10-bitowy, odróżnia teoretycznie ok. 1000 przedziałów więc potrzebujesz czegoś 500 razy lepszego..

Na szczęście nie trzeba aż takiej armaty, bo przecież tak duża rozdzielczość pomiaru jest potrzebna w niewielkim zakresie - tylko w pobliżu bardzo słabego oświetlenia. Dając np. typowy, liniowy przetwornik 20-bitowy możesz teoretycznie mierzyć z rozdzielczością 0.1lx. O ile przy Księżycu to ważne, bo nasze oko spokojnie odróżnia 0.1lx od 0.2lx, o tyle potem, w dzień to w ogóle nie ma znaczenia czy jest 5237.4lx czy 5237.5lx. W takich sytuacjach sprawdzają się metody analogowego kondycjonowania (przygotowania) sygnału. Tak przerabiasz swój sygnał przed pomiarem przetwornikiem, że w wynikach jednakowo odróżniasz zarówno 0.1 od 0.2lx jak i 5000 od 6000lx. Dzięki temu możesz pokryć sensowny zakres pomiaru natężenia światła przetwornikiem nawet i 10-bitowym. Trzeba tylko inaczej zbudować układ wejściowy, prosty dzielnik z oporników już wtedy nie wystarcza. No albo kupić gotowy czujnik światła, który fabrycznie oddaje w postaci cyfrowej wynik np. w luxach 🙂 

EDIT: Możesz też dodać tranzystor z opornikiem i mierzyć na dwa "takty", tj. zrobić układ dwuzakresowy. Program powinien wtedy ustawić pierwszy zakres pomiarowy (powiedzmy 0.1lx do 100lx, czyli zakres dynamiczny 1:1000) a jeśli wynik wychodzi poza ten zakres, przełączyć na drugi (np. 100 do 100000lx, dynamika taka sama 1:1000). Wtedy prostym sprzętem i 10-bitowym ADC opędzasz całość ogromnego zakresu oświetlenia. Jeśli masz (lub możesz pożyczyć) jakiś światłomierz, to zupełnie spokojnie wyskalujesz takie cudo w prawdziwych luxach. A nawet jeśli nie, to i tak przynajmniej dostaniesz jakieś liczby, które będa zmieniały się w miarę liniowo 0-2000 zarówno w ciemności jak i na pełnym słońcu.

Edytowano przez marek1707
  • Lubię! 2
Link do komentarza
Share on other sites

#define przycisk 8
#include <Servo.h> //Biblioteka odpowiedzialna za serwa
 
Servo serwomechanizm;  //Tworzymy obiekt, dzięki któremu możemy odwołać się do serwa 
byte pozycja = 0; //Aktualna pozycja serwa 0-180
int pozycjaPoprzednia = 0;
int minimum = 0;
int maximum = 0;
bool komunikat1 = false;
bool komunikat2 = false;
bool maks = false;
bool mini = true;
void setup() { 
  serwomechanizm.attach(11);  //Serwomechanizm podłączony do pinu 11
  Serial.begin(9600); //Komunikacja z komputerem
  pinMode(przycisk, INPUT_PULLUP);
} 
 
void loop() 
{ 
  if (digitalRead(przycisk) == LOW && maks == false){ //Ustawienie wartosci maksymalnej
    if (komunikat1 == false) {
      Serial.println("Ustaw wartosc maksymalna");
      komunikat1 == true;
      maks = true;
      mini = false;
      maximum = analogRead(A5);
      Serial.println(maximum);
      delay(1000);
    }
  } if (digitalRead(przycisk) == LOW && mini == false) { //Ustawienie wartosci minimalnej
    if (komunikat2 == false) {
      Serial.println("Ustaw wartosc minimalna");
      komunikat2 == true;
      maks = false;
      mini = true;
      minimum = analogRead(A5);
      Serial.println(minimum);
    } }
  int odczytCzujnika = analogRead(A5); //Odczytujemy wartość z czujnika
  pozycja = map(odczytCzujnika, minimum, maximum, 0, 180); //Zamieniamy ją na pozycję serwa
  
  if (abs(pozycja-pozycjaPoprzednia) > 5) { //Sprawdzamy czy pozycje różnią się o ponad 5 stopni
    serwomechanizm.write(pozycja); //Wykonajujemy ruch
    pozycjaPoprzednia = pozycja; //Zapamiętujemy aktualną pozycję jako poprzednią
  }
  
  Serial.println(odczytCzujnika); //Wysyłamy wartość do terminala
  delay(300); //Opóźnienie dla lepszego efektu
}

Praca domowa 6.3
Nie wiem, czy dobrze zrozumiałem zadanie. Dodałem do układu przycisk który służy do ustawienia wartości maksymalnej i minimalnej i za jego pomocą kalibruje układ. 

  • Lubię! 1
Link do komentarza
Share on other sites

@Havkarr ciekawe, od dawna nie widziałem żeby ktoś podjął ten temat. W jaki sposób zrobisz to od ciebie zależy, równie dobrze mógłbyś ustawić 2 wartości min/max które w jakiś sposób ustaliłeś i przy ich pomocy przeskalowywać wartości. Ale przyciski są świetnym pomysłem.

W ramach przyszłościowego zadania, możesz zapisać ustawienia do pamięci trwałej EEPROM, żeby nie trzeba było kalibrować za każdym razem.

Albo taki szalony pomysł, jakbyś był w stanie dodać rezystor na zasilaniu, żeby mierzyć spadek napięcia i stąd pobór prądu. I zrobić automatyczna kalibrację. Na skrajnych zakresach umieszczasz przeszkody dla strzałki wskaźnika. Gdy orczyk uderzy w przeszkodę zwiększa się pobór prądu co można wykryć spadkiem napięcia na rezystorze.

Przy starcie wskaźnika badane są obie skrajne wartości i zapisywane położenia. Ale to dużo trudniejsze 🙂 

Edytowano przez Gieneq
Link do komentarza
Share on other sites

Witam, robię zad 6.3 ale moje serwo pracuje od prawej strony do lewej czyli jakby przekręca się przeciwnie. czy jest jakiś sposób zmiany kierunku oprócz robienia tego w programie za pomocą map(), bo jak zrobię za pomocą map() to biore:

pozycja = map(odczytCzujnika, 0, 900, 0, 180);

zmieniam na:

pozycja = map(odczytCzujnika, 0, 900, 180, 0);

wiec serwo bedzie wykonywać ruch po włączeniu by dojsc do 0 czyli obrot o 180. 

Link do komentarza
Share on other sites

1 godzinę temu, mn860618 napisał:

 


pozycja = map(odczytCzujnika, 0, 900, 0, 180);

 


pozycja = map(odczytCzujnika, 0, 900, 180, 0);

wiec serwo bedzie wykonywać ruch po włączeniu by dojsc do 0 czyli obrot o 180

Na to nic nie poradzisz, no chyba ze zawsze przed wylaczeniem ustawisz serwo w interesujacej Cie pozycji...mozesz tez pokusic sie o odwrocenie polaryzacji kabli lutowanych do silnika (to malutko roboty)..o ile o taki efekt Ci chodzi

Link do komentarza
Share on other sites

1 godzinę temu, farmaceuta napisał:

Na to nic nie poradzisz, no chyba ze zawsze przed wylaczeniem ustawisz serwo w interesujacej Cie pozycji...mozesz tez pokusic sie o odwrocenie polaryzacji kabli lutowanych do silnika (to malutko roboty)..o ile o taki efekt Ci chodzi

To te serwa mają podłączane te kable losowo czy mi się tylko wydaje że w przykładzie z kursu mamy odwrotny kierunek?

Link do komentarza
Share on other sites

26 minut temu, mn860618 napisał:

To te serwa mają podłączane te kable losowo czy mi się tylko wydaje że w przykładzie z kursu mamy odwrotny kierunek?

Nie nie...ja tylko mowie ze jak chcesz zmienic kierunek dzialania serwa inaczej niz programowo to mozesz odwrocic kierunek obrotow  silnika odwracajac polaryzacje (nie wiem jak jest w kursie..nie czytalem😜)

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.