Prosty laserowy stoper na Arduino

W dzisiejszym artykule dowiemy się jak ze zwykłej diody laserowej i fotorezystora zrobić prostą fotokomórkę, którą można wykorzystać np. do stworzenia stopera liczącego czas od zwolnienia przycisku aż do przerwana wiązki lasera przez obiekt. Zaczynajmy więc!

Co będzie nam potrzebne?

To już naprawdę wszystkie części! Poza Arduino Uno koszt całości zamyka się w 5 zł.

Zobaczmy teraz jak połączyć te części razem, aby pracowały tak jak chcemy.

Schemat połączeń

Diodę podłączamy bezpośrednio do pinu cyfrowego Arduino, fotorezystor w połączeniu z rezystorem 220 Ohm utworzy nam dzielnik napięcia, który będzie reagował na zmiany w natężeniu światła padającego na fotorezystor. Przycisk podłączamy jedną stroną do masy, drugą bezpośrednio w pin cyfrowy, na któym później włączymy wewnętrzny rezystor podciągający.

Po złożeniu nasz układ powinien wyglądać tak jak na schemacie oraz zdjęciu powyżej (diodę laserową przytwierdziłem taśmą izolacyjną, aby się nie ruszała i świeciła bezpośrednio na fotorezystor).

Zasada działania

Tutaj sprawa jest banalnie prosta. Wciśnięcie przycisku powoduje przejście urządzenia w stan gotowości. W tym momencie zapala się dioda laserowa. Po zwolnieniu przycisku zapisywany jest aktualny wynik funkcji millis() oraz wykonywane są nieustanne pomiary na wejściu analogowym, do którego podłączony jest fotorezystor. Jeżeli kontroler odnotuje gwałtowną zmianę natężenia światła kończy pomiar oraz wypisuje wynik na port szeregowy. Przejdźmy teraz do napisania kodu, który wykona dla nas to wszystko.

Program

Cały kod programu można pobrać tutaj.

Przejdźmy do omówienia wybranych części kodu.

typedef enum {IDLE, READY, IN_PROGRESS, ENDED} ENUM_STATE;

W tym miejscu definiujemy sobie typ wyliczeniowy, który będzie reprezentował nam aktualny stan mikrokontrolera.

void setup() {
  pinMode(LASER, OUTPUT);
  pinMode(SENSOR, INPUT);
  pinMode(TRIGGER, INPUT_PULLUP);
  Serial.begin(9600);

  Serial.println(F("Urzadzenie gotowe\n"));
  Serial.println(F("Wcisnij i pusc przycisk, aby rozpoczac pomiar."));
  Serial.println(F("Przetnij wiazke lasera aby zakonczyc pomiar."));
  Serial.println(F("---------------------------------------------------"));
}

W funkcji setup() ustawiamy tryby pinów, uruchamiamy port szeregowy oraz wypisujemy na nim wiadomość powitalną, nic szczególnego.

Zobaczmy co dzieje się w funkcji loop()

void loop() {
  if(!digitalRead(TRIGGER)) {
    if(currentState != READY) {
      currentState = READY;
      digitalWrite(LASER, HIGH);
      Serial.println(F("Pusc przycisk, aby rozpoczac pomiar."));
    }
  } else if(currentState == READY) {
    Serial.println(F("Rozpoczeto pomiar."));
    tStart = millis();
    lastAnalogRead = analogRead(SENSOR);
    currentState = IN_PROGRESS;
  }
  
  if(currentState == IN_PROGRESS) {
    int currentAnalogRead = analogRead(SENSOR);
    delay(2);
    if(currentAnalogRead > lastAnalogRead + 100) {
      currentState = ENDED;
    }
    lastAnalogRead = currentAnalogRead;
  }

  if(currentState == ENDED) {
    digitalWrite(LASER, LOW);
    Serial.print(F("Pomiar zakonczony, wynik to: "));
    Serial.print(millis() - tStart);
    Serial.println(F(" ms."));
    Serial.println(F("---------------------------------------------------"));
    currentState = IDLE;
  }
}

Już na pierwszy rzut oka widać, że wprowadzenie typu wyliczeniowego do ustalania stanu procesora znacząco polepszyło nam czytelność kodu.

Pierwszą rzeczą, która jest sprawdzana w głównej pętli programu to wciśnięcie przyciku wyzwalacza. Niezależnie, czy jest aktualnie dokonywany jakiś pomiar, czy też nie – wciśnięcie tego przycisku zawsze rozpocznie całą procedurę od nowa.

Jeżeli przycisk nie jest wciśnięty, a mikrokontroler jest w stanie READY rozpocznie się nasz pomiar.

  } else if(currentState == READY) {
    Serial.println(F("Rozpoczeto pomiar."));
    tStart = millis();
    lastAnalogRead = analogRead(SENSOR);
    currentState = IN_PROGRESS;
  }

Do zmiennej tStart zostanie zapisany czas rozpoczęcia pomiaru, do zmiennej lastAnalogRead zostanie zapisana wartość natężenia światła oraz zmienimy stan zmiennej currentState na IN_PROGRESS. Dalsze sterowanie kontrolerem będzie uzależnione od poniższego kodu:

  if(currentState == IN_PROGRESS) {
    int currentAnalogRead = analogRead(SENSOR);
    delay(2);
    if(currentAnalogRead > lastAnalogRead + 100) {
      currentState = ENDED;
    }
    lastAnalogRead = currentAnalogRead;
  }

Teraz dokonuje się nieustanny pomiar wartości na pinie analogowym, po czym jest on porównywany z poprzednim pomiarem. Dodałem tutaj 2ms przerwę, aby skok pomiędzy odczytami był bardziej wyraźny. Jeżeli aktualny pomiar będzie większy o przynajmniej 100 od poprzedniego to sterowanie mikrokontrolerem przejdzie w to miejsce:

  if(currentState == ENDED) {
    digitalWrite(LASER, LOW);
    Serial.print(F("Pomiar zakonczony, wynik to: "));
    Serial.print(millis() - tStart);
    Serial.println(F(" ms."));
    Serial.println(F("---------------------------------------------------"));
    currentState = IDLE;
  }

Jak widać jedyne co się tutaj dzieje to wyłączenie lasera, wypisanie wyniku oraz przełączenie kontrolera w tryb „bezczynności”.

Zastosowanie

Jeżeli uczęszczacie jeszcze do szkoły i macie fizykę, warto zagadać do nauczyciela czy nie pozwoliłby wykonać podczas lekcji doświadczenia. Dzięki takiemu układowi można w bardzo fajny sposób wyznaczyć przyśpieszenie w ruchu jednostaniej przyspieszonym. Wystarczy do tego prosty tor, wagonik na który możemy załadować ciężarki, bloczek oraz linkę. Resztę pozostawiam do wymyślenia Wam. Dzięki takiemu doświadczeniu można w łatwy i przyjemy sposób zarobić dobrą ocenę ;)