Skocz do zawartości

Kurs Arduino - #6 - kontynuacja UART, serwomechanizmy


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ę! 1
Link to post
Share on other sites
7 godzin temu, Gieneq napisał:

@Raccoon fajnie że się przykładasz do nauki. Tak trzymaj! 🙂 

@GieneqCoś trzeba robić w długie zimowe wieczory 😉

 

Link to post
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 to post
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 to post
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ę! 1
Link to post
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 to post
Share on other sites
(edytowany)
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 to post
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.