Skocz do zawartości
Philip

Wielozadaniowość, millis, automatyka domowa

Pomocna odpowiedź

Witam,
Aktualnie pracuję nad programem, dzięki któremu zautomatyzuje swój pokój. Do Arduino podłączone są: wyświetlacz LCD, klawiatura, 2x Dalllas, DHT, PIR, Kontaktron, Buzzer. Jak wszystkie te elementy uda mi się dobrze zaprogramować, aby działały tak jak chce to doda się jeszcze coś innego 😃 (RTC, Bluetooth). Poniżej opisuję jak cały program ma działać.

1. Wyświetlacz(piny8-13): wyświetla się na nim temperatura w pokoju, na dworze,

wartość wilgotności i temperatury, data, godzina (RTC) po wcześniejszym

wciśnięciu odpowiedniego przycisku na klawiaturze;

dodatkowo wyświetla się odpowiedni komunikat podczas wyjścia z pokoju

("Miłego dnia" lub coś podobnego)

2. Czujniki temperatury Dallas(pin A0): mierzą temperaturę pokoju i na dworze

3. Czujnik wilgotności DHT(pin 15): mierzy wilgotność w pokoju, oraz temperaturę

dla porównania z czujnikiem Dallas

4. Kontaktron(pin 16): wykrywa wejście do pokoju i jego zadziałanie aktywuje

odpowiedni sygnał buzzera

5. Buzzer(pin 17): wydaje dźwięk podczas otworzenia drzwi;

może dodatkowo odtwarzać inne dźwięki

6. Czujnik ruchu PIR(pin 18): wykrywa ruch, kiedy ktoś wychodzi z pokoju, przez

co na wyświetlaczu pokazuje się odpowiedni komunikat

7. Klawiatura (0-7): przełączanie temperatury, wilgotności, daty, godziny oraz

różnych dźwięków.

Na razie napisałem kod, aby poprzez wciśnięcie odpowiedniego przycisku na klawiaturze na wyświetlaczu wyświetlały się temperatury oraz wilgotność i wszystko chodzi i działa.

Problem jest teraz z kontaktronem i buzzerem. Podczas wejścia do pokoju ma się odtworzyć jeden raz sygnał.

[...]
unsigned long aktualnyCzas = 0;             
unsigned long zapamietanyCzas = 0;
int glosnosc = 500;      

void setup() {
 Serial.begin(9600);
 lcd.begin(16,2);
 lcd.clear();
 sensors.begin();
 dht.setup(CzDHT);

 pinMode(KONTAKTRON,INPUT_PULLUP); //Kontaktron pin 16
 pinMode(BUZZER,OUTPUT);                  //Buzzer pin 17
 pinMode(PIR,INPUT);            
}

void loop(){

aktualnyCzas = millis();

[...]

if(digitalRead(KONTAKTRON)==HIGH){                       //Jeśli drzwi otwarte   
     for(int i=0;i<2;i++){
       if(aktualnyCzas - zapamietanyCzas >= 500UL){ 
       zapamietanyCzas = aktualnyCzas;
       if(glosnosc==500){
         glosnosc = 750;
        } else {
         glosnosc = 500;
        }
   tone(BUZZER,glosnosc);                                      //Buzzer wydaje sygnał 
       }
     }
 } else {                                                                //Jeśli nie, to jest wyłączony
   noTone(BUZZER);
 }
[...]
}

Natomiast nie wiem dlaczego sygnał chodzi cały czas zamiast odtworzyć się tylko raz, przecież jest for. Czy mógłby ktoś pomóc mi rozwiązać ten problem?

Pozdrawiam Philip

Udostępnij ten post


Link to post
Share on other sites

Działa tak jak napisałeś.

a) Sprawdzasz, czy drzwi są otwarte.

b) jeśli tak, odtwarzasz dźwięk wykonując pętlę for HGW po co; jeśli nie, przestajesz odtwarzać

c) Wracasz do a).

Czyli kiedy zgodie z Twoim kodem dźwięk przestanie grać?

Udostępnij ten post


Link to post
Share on other sites

Kiedy drzwi są zamknięte, to buzzer jest wyłączony. Jak otwieram drzwi, to buzzer powinien odtworzyć ten sygnał ("pip - pip") tylko raz, a robi to w kółko bez końca. Wydaje się być dobrze, przecież jest pętla for.

Udostępnij ten post


Link to post
Share on other sites

...która to pętla jest całkowicie bez sensu, bo wykonuje się w kółko. Bez tej pętli program działałby tak samo (a nawet lepiej).

Pokaż w kodzie, w którym miejscu mianowicie buzzer ma przestać wydawać dźwięki po odtworzeniu dwóch sygnałów.

Bo na razie masz że ma przestać burczeć jak drzwi są zamknięte i tyle.

Niestety - instrukcji których nie ma procesory nie zwykły wykonywać 😉

Tak w ogóle masz już w programie jedną pętlę (w której wykonuje się loop) i do Twoich zastosowań ta jedna powinna wystarczyć.

Udostępnij ten post


Link to post
Share on other sites

Jeżeli chcesz, żeby ta pętla została to mogę podpowiedzieć, że możesz dodać jakiś bit, który poinformuje, że pętla już się wykonała (przy ostatnim obiegu setujesz go np. na true) a potem w głównej pętli przy zamknięciu drzwi staje się on znowu false. Ewentualnie możesz zaimplementować reakcję tylko na zbocze narastające w inny sposób. Pomysłów może być mnóstwo.

EDIT: i wtedy dzwonienie wykonuje się jeśli drzwi są otwarte i ten bit jest na false

Udostępnij ten post


Link to post
Share on other sites

Myślałem na tym kodem bardzo długo.

ethanak, Rzeczywiście pętla for jest bez sensu. BananWszyscy wziąłem twoją podpowiedź pod uwagę.

Dlatego mam taki kod, ale znowu nie do końca działa tak jak powinien. Po tym jak otworzę drzwi odtwarza się ten sygnał "din - don" (raz), ale jak zamykam drzwi, to jeszcze buzzer wydaje z siebie tak jakby jeden z tych dwóch dźwięków. Czyli jak otwieram jest din - don, a jak zamykam din. Druga sprawa co do tego kodu, to nie wiem jak tu zamiast delay'a użyć millis, aby te dźwięki odtwarzały się w tym odpowiednim czasie po 500ms. Robiłem to tak samo jak poprzednio, ale wtedy buzzer wydawał ciągły dźwięk bez przerwy.

#define KONTAKTRON 16
#define BUZZER 17

unsigned long aktualnyCzas = 0;             //Zmienne do liczenia czasu
unsigned long zapamietanyCzas = 0;
int glosnosc = 500;                         //Zmienna do zmiany glośności buzzera
boolean odegranoDzwiek = false;
boolean otwartoDrzwi = false;

void setup() {
 Serial.begin(9600);  
 pinMode(BUZZER,OUTPUT);
 pinMode(KONTAKTRON,INPUT_PULLUP);
}

void loop() {   
 aktualnyCzas = millis();

 if(digitalRead(KONTAKTRON)==HIGH){    //Jeśli drzwi otwarte
   otwartoDrzwi = true;                //to otwarto drzwi
 } else {                              //Jeśli nie
   odegranoDzwiek = false;             //nie odegarano dźwięku
   otwartoDrzwi = false;               //nie otwarto drzwi
   noTone(BUZZER);                 
 }

 if(otwartoDrzwi == true && odegranoDzwiek == false){  //Jeśli drzwi otwarte i nie odegrano dźwięku
   tone(BUZZER,500,500);       //Odegraj dźwięk
   delay(100);
   tone(BUZZER,1000,500);
   odegranoDzwiek = true;     //Odegrano
 }
}

Proszę o pomoc jak użyć tu zamiast delay - millis oraz jak zrobić, aby nie było tego jednego dźwięku przy zamykaniu.

Udostępnij ten post


Link to post
Share on other sites

Przede wszystkim - dodatkowy dźwięk przy zamykaniu pochodzi najprawdopodobniej od drgań styków kontaktronu. Podłącz równolegle kondensator 100nF, a przy okazji zainteresuj się biblioteką Bounce2 za pomocą której możesz wyeliminować ten efekt na drodze programowej.

Co do "przerobienia" programu z delay na millis (jak ja nie lubię takich określeń, to nie jest żadne przerobienie tylko napisanie wszystkiego od początku w zupełnie inny sposób) wiesz może co to jest maszyna stanów? Pewnie nie...

Otóż Twój program (załóżmy na razie, że nie wykonuje nic innego) może byc w jednym ze stanów:

a) nic nie gramy, czekamy na otwarcie drzwi

b) gramy pierwszy dźwięk

c) gramy drugi dźwięk

d) nic nie gramy, czekamy na zamknięcie drzwi

I żadne jakieś tam dodatkowe bity nie są potrzebne...

Program będzie działać w ten sposób:

Jeśli maszyna jest w stanie a, sprawdzamy czy drzwi są otwarte. Jeśli nie, nie robimy nic. Jeśli tak, zapamiętujemy bieżący czas, uruchamiamy odgrywanie pierwszego dźwięku (tone z dwoma, a nie trzema parametrami) i ustawiamy maszynę w stan b.

Jeśli maszyna jest w stanie b, sprawdzamy czy minęło już ileś tam milisekund od zapamiętanego czasu. Jeśli nie, nie robimy nic. Jeśli tak, zapamiętujemy bieżący czas, uruchamiamy odgrywanie drugiego dźwięku (również tone z dwoma parametrami a nie trzema) i ustawiamy maszynę w stan c.

Jeśli maszyna jest w stanie c, sprawdzamy czy minęło już ileś tam (niekoniecznie tyle samo co w stanie b) milisekund od zapamiętanego czasu. Jeśli nie, nie robimy nic. Jeśli tak, zatrzymujemy granie (noTone) i ustawiamy maszynę w stan d.

Jeśli maszyna jest w stanie d, sprawdzamy, czy drzwi są otwarte. Jeśli tak, nie robimy nic. Jeśli nie, ustawiamy maszynę w stan a.

A teraz napisz to ładnie w C++ 🙂

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Kondensator nie pomógł, dalej jest ten dźwięk przy tym moim starym kodzie.

A z tą maszyną stanów chodzi o switch ... case ? Bo nie do końca wiem, czy mam coś próbować pisać ze switch'em czy jakoś inaczej. 😕

Udostępnij ten post


Link to post
Share on other sites

Najbardziej naturalny jest tu switch/case - ale równie dobrze (chociaż mniej elegancko) można to zrobić na if/else.

Zostaw na razie ten dźwięk przy zamykaniu w spokoju, napisz nowy kod - jeśli dalej będzie występować to będziemy kombinować jak się go pozbyć. Na razie nie ma to sensu.

I poszukaj sobie w międzyczasie jakichś informacji o maszynie stanów i automatach skończonych - forum nie może być jedynym źródłem informacji! W Wikipedii masz to bardzo ślicznie wyoślone...

No bo fajnie jest przecież jeśli się rozumie własny kod, prawda?

Udostępnij ten post


Link to post
Share on other sites

Jeśli chodzi o programowanie to cały czas jestem początkujący. Jeśli to jest drganie styków, to dlaczego jest tylko jeden dźwięk po zamknięciu drzwi? Rozumiem, że program wchodzi do funkcji tone() i po czasie duration z niej wychodzi. Co robi fizycznie z portem funkcja noTone()? W tym konkretnym przykładzie noTone() chyba jest niepotrzebne?

Udostępnij ten post


Link to post
Share on other sites

No tak, dodawanie dodatkowego bitu dla wykonania sekwencji a dodanie zmiennej informującej o obecnym stanie to taka różnica... ja tylko dalem przykładowe rozwiązanie problemu, które i tak będzie działać :>

A pozbycie się delaya można zrobić np tak.

1. odczytaj timer - zapisz go gdzies

2. odczytuj timer i odejmuj otrzymaną wartość od zapisanej

3. powtórz 2 aż otrzymana wartość z odejmowania będzie taka jaką chcesz.

Udostępnij ten post


Link to post
Share on other sites

... a potem zrób z tego funkcję i nazwij ją "toWcaleNieJestDelay".

@BananWszyscy: właśnie bardzo ładnie przedstawiłeś kod funkcji delay(). O to Ci chodziło? Żeby zamienić systemową funkcję na własną, która robi dokładnie to samo? Po kiego grzyba???

Udostępnij ten post


Link to post
Share on other sites

Ano po to, że w takim szkielecie nie zajmujemy się mrożeniem procesora, bo nadal możemy wykonywać pierdyliard innych rzeczy 😋

Udostępnij ten post


Link to post
Share on other sites

Bardzo dobrze - tak działa np. delay w ESP8266 (tam wywołanie delay* jest wręcz wymagane, inaczej watchdog zrobi boo-boo i zobaczysz na serialu traceback). Tyle, że tu nie chodzi o "wsadzenie czegoś w delay" - a o napisanie programu na zupełnie innej zasadzie zamiast dorabiania na siłę protez do ułomnej w samych założeniach konstrukcji.

To nie funkcja "delay" jest zła - gdyby tak było, pewnie by nie istniała. Złe jest jej wykorzystanie (w większości bez najmniejszych prób zrozumienia jak to wszystko tak naprawdę działa). Dlatego właśnie ględzę o automatach skończonych i maszynie stanów, bo zrozumienie tego (nawet na podstawie informacji zawartych we wspomnianej Wikipedii) wcale nie wymaga jakiejś tajemnej wiedzy ani piętnastu lat studiowania czarnej magii. A znając te zasady możesz programować swobodnie, a delaja wciskać tam gdzie jest rzeczywiście potrzebny.

* yield() to to samo co delay(0) jakby ktoś twierdził że "można yield zamiast delay".

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Zgadzam się ze wszystkim tak jak wcześniej, ja tylko chciałem pokazać, jak to wszystko działa i co się da z tym zrobić. Nie daję gotowego kodu/rozwiązania tylko pokazuje. Mnie jak uczyli to dawali zwykle błędne wskazówki, żebym się nacinał i sam coś lepszego wymyślał (bo działało, ale nie do końca tak jak się chciało). Dlatego twierdzę, że lepeiej napisać coś samemu od bardzo słabego kodu i przez łatanie dojść do czegoś porządnego, przynajmniej od razu wiadomo co pomijać 😃

Udostępnij ten post


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!

Gość
Napisz odpowiedź...

×   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...