Skocz do zawartości

HC-SR04 - Wielofunkcyjność programowa na podstawie czujnika odległości. Modyfikacja programu


Pomocna odpowiedź

11 minut temu, _LM_ napisał:

Jak dla mnie kolejny warunek przeciw arduino.

Tyle że to nie jest wymysł Arduino a pewnego standardu, którego Arduino się trzyma (zresztą nie tylko Arduino).

A jaki Twoim zdaniem typ powinno zwracać millis()? Masz do dyspozycji albo uint16_t (czyli po minucie się przekręci), albo istniejący uint32_t. Poza tym kto Ci broni zrobić coś takiego:

uint16_t costam =  millis();

Cóż - jak to było z tym tancerzem i pewnymi częściami ciała? 😉

 

1 minutę temu, ethanak napisał:

A jaki Twoim zdaniem typ powinno zwracać millis()?

A po co ta funkcja jak można to lepiej zrobić bezpośrednio na przerwaniach? Co jak będę potrzebował dziesięciu timerów programowych, mam zużyć 40B pamięci? O ile w Atmegach to jakoś ujdzie to w Attiny dość szybko zawali ram, już nie wspominając o tym że zapis/odczyt większych typów danych zajmuje również sporo czasu. To ja już wolę poświęcić te minutę na konfigurację i uruchomienie któregoś z timerów.

4 minuty temu, _LM_ napisał:

A po co ta funkcja jak można to lepiej zrobić bezpośrednio na przerwaniach?

A czy millis() służy wyłącznie do zastępowania delaja przez już-nie-początkujących-jak-im-się-wydaje programistów? To przecież funkcja odmierzająca czas, owszem, można to zrobić na przerwaniach (tylko po jakiego grzyba, jeśli millis() już jest i działa właśnie na przerwaniach? swojego millisa będziesz pisać i pewnie będzie lepszy niż ten co jest?)

Takie typowe zastosowanie: czy minęło już ileś tam milisekund od zdarzenia X?

A pytanie "po co ta funkcja" kieruj do autorów Wiringa. Możesz ich nawet poinformować, że wymyśliłeś coś lepszego i powinni natychmiast usunąć millis() ze standardu.

 

11 minut temu, _LM_ napisał:

O ile w Atmegach to jakoś ujdzie to w Attiny dość szybko zawali ram

Wiesz - programowanie ATtiny z bibliotekami Arduino to niespecjalnie dobry pomysł... W takim np. SDK to RPi Pico nie ma millis, jest za to odpowiednik micros i to 64-bitowy 😉 Mam się go pozbyć?

 

 

4 minuty temu, ethanak napisał:

W takim np. SDK to RPi Pico nie ma millis, jest za to odpowiednik micros i to 64-bitowy 😉 Mam się go pozbyć?

Panie kolego a czujesz różnicę pomiędzy mikrokontrolerem 32bitowym a piździkiem 8bit? 

 

5 minut temu, ethanak napisał:

Wiesz - programowanie ATtiny z bibliotekami Arduino to niespecjalnie dobry pomysł...

A jednak takie próby się pojawiają, ja nie oceniam czy to dobrze, jak ktoś lubi niech se używa.

11 minut temu, ethanak napisał:

To przecież funkcja odmierzająca czas, owszem, można to zrobić na przerwaniach (tylko po jakiego grzyba, jeśli millis() już jest i działa właśnie na przerwaniach? swojego millisa będziesz pisać i pewnie będzie lepszy niż ten co jest?)

A po to, że wiem po co i którego timera użyłem do wykonania tego czy innego zadania. I tak wiem, mogę sobie to sprawdzić w dokumentacji arduino.

3 minuty temu, _LM_ napisał:

A po to, że wiem po co i którego timera użyłem do wykonania tego czy innego zadania.

Zdaje się że patrzysz na wszystko z punktu widzenia typu "ja robię takie rzeczy a nie inne" i w związku z tym uważasz, że co poniektóre funkcje są niepotrzebne bo ich nie używasz. Wyobraź sobie, że istnieje cała masa zastosowań w których owe funkcję potrzebne, i w których nikt nie zawraca sobie głowy jakimiś timerami (krórych albo się w owych zastosowaniach nie używa, albo po prostu używa się funkcji bibliotecznych które się owymi timerami zajmują).

Mówisz timery... a teraz wyobraź sobie, że chcesz przeportować swój niewątpliwie wspaniały program na inny mikrokontroler. Ile czasu zmarnujesz na nauczenie się jakich timerów możesz użyć np. w ESP-32? Ja jak chcę poznać ile czasu minęło od jakiegoś zdarzenia nie przejmuję się timerami (choćby z tego powodu, że te same programy uruchamiam często na małym Arduino, obu wersjach ESP czy Pico, a nawet na pececie czy pełnowymiarowym RPi) - tylko znajduję sobie funkcję z biblioteki. A czy to będzie millis, czy time_us_64, czy nawet gettimeofday - jedna chwała.

 

A żebyś wiedział że na takim esp8266 chętnie używam bibliotek arduino(ESP32 czeka na swoją kolej). I owszem. Niema sensu uczyć się rejestrów takiego esp czy jakiegoś ARMa. Stwierdziłem wcześniej że używanie tych millis() ze względu na zajmowany rozmiar w mikrokontrolerach 8bit jest marnotrawieniem pamięci, było to moje spostrzeżenie wysnute "na gorąco" po tym, jak sprawdziłem na szybko jak to działa. I nadal tak uważam. Co innego mocniejsze jednostki. To oczywiste że bez wbudowanych bibliotek trudno byłoby ruszyć z miejsca.  Dobrze jednak wiedzieć co w krzemie piszczy.

 int bleble = 0;

void DC_PRAWO() //Silniki skręc w prawo
{
  motor1.run(FORWARD);
  motor2.run(BACKWARD);
  bleble = 0;
  while(bleble <= 500) {
    bleble++;
    if (wcisniety guzik?) {
      bleble = 1000;
      }
  delay(1);
    }
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

void DC_LEWO() //Silniki skręc w lewo
{
  motor1.run(BACKWARD);
  motor2.run(FORWARD);
  bleble = 0;
  while(bleble <= 500) {
    bleble++;
    if (wcisniety guzik?) {
      bleble = 1000;
      }
  delay(1);
    }
  motor1.run(FORWARD);
  motor2.run(FORWARD);
}

Sprawdz narazie to czy dziala...wiem ze da sie inaczej, estetyczniej itp, no ale slabo mi glowa ostatnio dziala...heh🤕

14 minut temu, farmaceuta napisał:

wiem ze da sie inaczej,

Ludzie, przestańcie takie potworki programopodobne płodzić, bo zamiast pomagać początkującemu robicie tylko galimatias!

Prosty fragment programu - przykładowa realizacja start/stop po naciśnięciu jednego guzika oraz  półsekundowego skrętu i jazdy dalej prosto po naciśnięciu drugiego guzika:

enum {
  STOIMY = 0,
  START,
  JEDZIEMY,
  ZATRZYMAJ,
  JEDZIEMY,
  ZAKRECAMY_W_PRAWO,
  ZAKRECAMY
  };

int stan;
uint32_t started;

void loop()
{
  guzik_w_prawo.update(); // coś wspominałęm o bounce2?
  guzik_start.update();
  
  // przyjmujemy polecenie
  
  if (guzik_w_prawo.fell()) {
    stan = ZAKRECAMY_W_PRAWO;
  }
  if (guzik_start.fell()) {
    if (stan != STOIMY) stan = ZATRZYMAJ;
    else stan = START;
  }
  //no i maszyna stanów
  
  switch (stan) {
    case STOIMY: // czekamy na coś
      break;

    case ZATRZYMAJ:
      motor1.stop();
      motor2.stop();
      stan = STOIMY;
      break;
      
    case START: // czyli robot ma ruszyć i jechać
      motor1.run(FORWARD);
      motor2.run(FORWARD);
      stan = JEDZIEMY;
      break;
      
    case JEDZIEMY: // silnuki się kręcą, robot jedzie, my patrzymy, ogólnie sielanka
      break;
      
    case ZAKRECAMY_W_PRAWO: // dostaliśmy polecenie, będziemy skręcać!
      motor1.run(FORWARD);
      motor2.run(BACKWARD);
      started = millis(); // zapamiętujemy kiedy zaczęliśmy skręcać
      stan = ZAKRECAMY;
      break;
      
    case ZAKRECAMY: // nieważne w którą stronę
      if (millis() - started >= 500UL) { // funkcja millis() słuzy oczywiście do pomiaru czasu jak sama nazwa wskazuje
                                         //Większe lub równe, a nie równe, bo jakby było równe to moglibyśmy przegapić
        stan = START; // czyli wracamy do jazdy prosto
      }
      break;
  }
}
      

 

 

2 godziny temu, _LM_ napisał:

Niema sensu uczyć się rejestrów takiego esp czy jakiegoś ARMa.

Dlaczego nie ma sensu uczyć się działania na rejestrach w układach ARM? O esp nie pytam, bo nie używam, ale w przypadku ARM trochę mnie zaskoczyło takie stwierdzenie.

To była odpowiedź na odczepnego tak po po prawdzie to lubię wiedzieć co i jak działa. W ogóle to zaśmiecamy wątek koledze @Forseti

  • Lubię! 2
  • 2 tygodnie później...
(edytowany)

Po przeanalizowaniu Waszych sugestii zacząłem równolegle rozwiązywać swój problem na własną rękę (wywaliłem delaye() ). Sądzę, że jestem relatywnie bliski rozwiązania problemu. 

Okroiłem program do samej obsługi serva (cel edukacyjny). Kod robi to co chcę. Przełącza się microswitchem pomiędzy Programami (1,2,3). Servo reaguje na zbliżenie do czujnika <15cm ale... no właśnie. Kod się realizuje tylko gdy dystans jest <15cm. W momencie gdy ten dystans się zwiększy kod się zatrzymuje. Nie wiem jak sobie z tym poradzić.  Chcę aby jeśli dystans jest mniejszy niż 15cm to ma być wykonany cały kod (LEWO/PRAWO) bezwzględnie od kolejnego pomiaru. Jak go zmusić do tego bez delay()? 😕

Jakieś sugestie?  Nie chciałbym znów przewracać do góry nogami kodu. 

/*Robot omijający przszkody - oparty o L293D Motor Driver Shield
   oraz czujnik odległości HC-SR04
   Napęd - 2x silniki DC 3-6V
   Serwomechanizm SG-90
   Włączany microswitchem
   dioda LED sygnalizacyjna stan pracy - Wolne miganie Program_1, Szybkie Program_2, Ciągłe STOP
   Opcja rozbudowy o sterowanie BT i modułem HC-05
*/

//Biblioteki
#include <NewPing.h> //Wczytanie Biblioteki NewPing dla HC-SR04
#include <Servo.h>  //Wczytanie Biblioteki Servo dla SG-90

//Definicje
#define LED_1 A4 //LED 1 
#define Przycisk_1 A5 //Microswitch_1

//Sensor HC-SR04
#define TRIG_PIN A0 //Wejście wyzwalające Trigger - pomiar odległości
#define ECHO_PIN A1 //Wyjście ECHO - odczyt odległości
#define MAX_DISTANCE 200 //Maksymalny dystans dla czujnika HC-SR04 - od tej wartości reaguje robot

//Ustawienia i przypisania

NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE); //Przypisanie zmiennych z define

//Wyjście serwomechanizm

Servo moje_servo;   //Obiekt dający możliwość odwołania się do serwa o nazwie moje_servo

boolean F1 = true, F2 = true, F3 = true, F4 = true; //Flagi dla serva umożliwiające wykonanie kodu z użyciem millis
boolean Sonar = true; //Flaga dla sonar


int dystans = 100; //Ustawienie dystansu dla moje_servo - int distance = 100;
int stanLED1 = LOW; //Przypisanie stanu LED 1 do zmiennej int - Zapamiętanie stanu LED

int dystansR = 0; //Ustawienie dystansu w Prawo
int dystansL = 0; //Ustawienie dystansu w Lewo

uint8_t tryb_program = 0; //Zmienna trybu programu

//Ustawienia do millis()
unsigned long aktualnyCzas = 0 ; //Czas, który jest zliczany - ciągle rośnie - Wartość od której odejmujemy zapamiętany czas!!!
unsigned long zapamietanyCzas_LED1 = 0; //Czas, który był zapamiętany w ostatnim obiegu pętli dla LED1
unsigned long zapamietanyCzas_Servo1 = 0; //Czas, który był zapamiętany w ostatnim obiegu pętli dla silnika Serva 1 - obrót prawo/lewo
unsigned long zapamietanyCzas_Sonar = 0; //Czas, który był zapamiętany w ostatnim obiegu pętli dla czujnika HC-SR04

unsigned long miganie_LED1 = 500; //Interwał migania LED1 - Program_1
unsigned long miganie_LED2 = 100; //Interwał migania LED1 - Program_2

//▼▼▼▼▼▼▼▼▼▼▼▼▼PRZYPISANIE RUCHÓW ROOTA▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

int patrz_PRAWO() //Ustawienie serwa - do patrzenia w prawo - int lookRight()

{
  if ((aktualnyCzas - zapamietanyCzas_Servo1 >= 500UL && F1))
  {
    F1 = false;
    moje_servo.write(50); //Ustawienie kąt 50°
    int dystans = readPing();
    zapamietanyCzas_Servo1 = aktualnyCzas;

  }
  if ((aktualnyCzas - zapamietanyCzas_Servo1 >= 1000UL && F2))
  {
    F2 = false;
    moje_servo.write(115); //Ustawienie kąt 115°
    int dystans = readPing();
  }
}
int patrz_LEWO() //Ustawienie serwa - do patrzenia w lewo - org int lookLeft()
{
  if ((aktualnyCzas - zapamietanyCzas_Servo1 >= 500UL && F3))
  {
    F3 = false;
    moje_servo.write(170); //Ustawienie kąt 170°
    int dystans = readPing();
  }
  if ((aktualnyCzas - zapamietanyCzas_Servo1 >= 1000UL && F4))
  {
    F4 = false;
    moje_servo.write(115); //Ustawienie kąt 115°
    int dystans = readPing();
    zapamietanyCzas_Servo1 = aktualnyCzas;
    F1 = true;
    F2 = true;
    F3 = true;
    F4 = true;
    return dystans;
  }
}

int readPing() //Ustawienie odległości w cm dla czujnika HC-SR04
{
  if ((aktualnyCzas - zapamietanyCzas_Sonar >= 70UL && Sonar))
  {
    Sonar = false;
    int cm = sonar.ping_cm(); //zwraca dystans w cm
    if (cm == 0)
    {
      cm = 250; //do sprawdzenia odległość- czemu aż 250cm ?
    }
    Sonar = true;
    return cm;

  }
}
//▼▼▼▼▼▼▼▼▼▼▼▼▼POCZĄTEK PROGRAMU▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

void setup() {

  Serial.begin(9600);

  //Startowe ustawienie serva

  moje_servo.attach(10); /*Serwomechanizm podłączony do pinu 10 - obsługiwane PINY 9 i 10.
  Dla Sheilda L293D kolejno 9=Servo_1, 10=Servo 2 -sygnały sterujące*/
  moje_servo.write(115); //Ustawienie pozycji serva na 115°

  //Wyprowadzenie przycisków
  pinMode(Przycisk_1, INPUT_PULLUP); //Wejście przycisku 1 w trybie PULLUP

  //Wyprowadzenia LED
  pinMode(LED_1, OUTPUT);

  //Ustawienie wyjściowego stanu LED
  digitalWrite(LED_1, LOW); //Stan diody LED1 - wyłączona

  //Ustawienie wyjściowe dla millis() - zerowanie liczenia
  aktualnyCzas = millis();
}

void loop() {

  aktualnyCzas = millis(); //Ustawienie wyjściowe dla millis()

  if (!digitalRead(Przycisk_1)) tryb_program++; //Jeśli ostatni stan był wyłączony i aktualny stan jest włączony to:

  switch (tryb_program % 4)  {

    //Modulo - reszta z dzielenia przez 4 - bo 3 programy

    case 1:
      //Miganie LED_1 Wolne miganie Program_1
      if (aktualnyCzas - zapamietanyCzas_LED1 >= miganie_LED1) {
        stanLED1 = !stanLED1;
        digitalWrite(LED_1, stanLED1);
        zapamietanyCzas_LED1 = aktualnyCzas;  //zapamiętaj aktualny czas
      }
      if (dystans <= 15) //jeśli dystans mniejszy od 15cm Patrz_Prawo->Patrz_lewo
      {
        dystansR = patrz_PRAWO();
        dystansL = patrz_LEWO();
      }
      Serial.println(dystans);
      dystans = readPing();
      break;
    case 2:
      //Miganie LED_1 Szybkie miganie Program_2
      if (aktualnyCzas - zapamietanyCzas_LED1 >= miganie_LED2) {
        stanLED1 = !stanLED1;
        digitalWrite(LED_1, stanLED1);
        moje_servo.write(60); //Tylko przykład
        zapamietanyCzas_LED1 = aktualnyCzas;  //zapamiętaj aktualny czas
      }
      break;
    case 3:
      //Tutaj będzie stan OFF
      //Miganie LED_1 Szybkie miganie Program_3
      if (aktualnyCzas - zapamietanyCzas_LED1 >= 50) {
        stanLED1 = !stanLED1;
        digitalWrite(LED_1, stanLED1);

        zapamietanyCzas_LED1 = aktualnyCzas;  //zapamiętaj aktualny czas

      }
      break;
  }

  while (!digitalRead(Przycisk_1));
  delay(50);
}

Oczywiście w każdej wolnej chwili staram się dokształcać ale idzie to powoli. Dzięki za cierpliwość.

PS. Obudowa robota jest gotowa pod kątem mechanicznym 🙂 Męczę tylko logikę. 

Edytowano przez Forseti
10 minut temu, Forseti napisał:

Chcę aby jeśli dystans jest mniejszy niż 15cm to ma być wykonany cały kod (LEWO/PRAWO) bezwzględnie od kolejnego pomiaru. Jak go zmusić do tego bez delay()?

Coś takiego:

W stanie JAZDA jeśli dystans jest mniejszy od 15, zapamiętujesz czas, uruchamiasz silniki czy serwa czy co tam do LEWOi  przełączasz stan maszyny np. na LEWO_BLISKO

W stanie LEWO_BLISKO sprawdzasz, czy czas minął. Jeśli tak, uruchamiasz silniki/serwo/cokolwiek do PRAWO, zapamiętujesz czas i przełączasz stan maszyny np. do PRAWO_BLISKO.

W stanie PRAWO_BLISKO sprawdzasz czy minął czas. Jeśli tak, robisz coś co kończy sekwencję (np. ustawiasz serwo PROSTO,) i przełączasz stan maszyny np. na ROZEJRZALEM_SIE.

W stanie ROZEJRZALEM_SIE decydujesz, co robić dalej (np skręcić tam gdzie jest miejsce albo się cofnąć).

Aha, jeszcze jedno:

Odczytujesz odległość raz na początku pętli, w ostateczności od razu po wejściu do odpowiedniej gałęzi case maszyny stanu czy przy okazji zmiany stanu w ramach przygotowania do następnej czynności. Nigdy w żadnym innym miejscu.

Może być?

16 minut temu, Forseti napisał:

Nie chciałbym znów przewracać do góry nogami kodu. 

Wiesz - program nad którym siedzę osiągnął 5000 linii kodu, a parę takich bardziej grubaśnych funkcji kilka razy już wędrowało do kosza. I nie, nie jest to bardzo duży program, tylko niemiłosiernie pokiełbaszony 🙂 Niestety - czasem przewrócenie wszystkiego do góry nogami ma sens. W sumie w większości przypadków napisanie kawałka kodu od zera będzie szybsze niż próba ratowania kodu który jest źle zaprojektowany.

Tak że nie bój się kosza na śmieci - nie gryzie.

 

Twój program można ulepszyć stosując wskaźniki na funkcje zamiast switch case ale czekam aż @ethanakpokaże jak powinna wyglądać maszyna stanów z prawdziwego zdarzenia. 

2 minuty temu, _LM_ napisał:

można ulepszyć stosując wskaźniki na funkcje zamiast switch

Gdzie tu ulepszenie? Można pewnie zastosować parę konstrukcji których kolega wątkotwórca nie zna (i może niech się na razie nauczy jednego a nie fafnastu rzeczy na raz), a znalazłoby się pewnie i parę takich o których i Ty nie słyszałeś 😉 Sprawdź sobie przy okazji co oznacza skrót KISS.

2 minuty temu, _LM_ napisał:

czekam aż @ethanakpokaże jak powinna wyglądać maszyna stanów z prawdziwego zdarzenia

Na pewno nie tak jak sobie wyobrażasz. A pokazywać nie mam zamiaru - w sieci jest multum przykładów.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...