Elvis Listopad 28, 2017 Udostępnij Listopad 28, 2017 SOYER, ja mam dla Ciebie propozycję napisania jeszcze innego programu. Zostaw na chwilę nawet wyświetlacze i jako ćwiczenie spróbuj zrobić sterowanie sygnalizacją świetlną. Może na razie nie używaj jej do kierowania ruchem, tylko podłącz dwie diody i zastanów się jak rozwiązać takie zadanie: mamy skrzyżowanie dwóch dróg, jednej bardzo ruchliwej i drugiej mało uczęszczanej. Chcemy żeby zielone światło na głównej drodze było aktywne dużo dłużej niż na podrzędnej, powedzmy 20s zielone na główne, później 2s dla drogi podrzędnej. Mamy też czujnik obecności samochodów - zwykły przycisk. Jeśli zostanie naciśnięty, znaczy, że na drodze podrzędnej jest pojazd. Jeśli w ciągu ostatnich powiedzmy 5s nie było zielonegl dla drogi podrzędnej, światła powinny od razu się zmienić i umożliwić przejazd. Zastanów się jak napisać taki program, możesz go udoskonalić wprowadzając dodatkowe ograniczenia na zmiany świateł, dodać światło żółte itd. Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Listopad 28, 2017 Autor tematu Udostępnij Listopad 28, 2017 Elvis, trochę się namęczyłem, ale wymęczyłem(najprostsze co wymyśliłem): int czerwona = 10; int zielona = 11; unsigned long previousMillis = 0; unsigned long currentMillis = millis(); void setup() { pinMode(czerwona, OUTPUT); pinMode(zielona, OUTPUT); pinMode(2, INPUT_PULLUP); Serial.begin(9600); } void loop() { Serial.println(zielona); unsigned long currentMillis = millis(); while((currentMillis - previousMillis > 3000) && (digitalRead(2) == LOW)){//dopoki uplynelo powyzej 3s i przycisk wcisniety to:(tu jest problem, bo po puszczeniu przycisku zielona dalej swieci) digitalWrite(czerwona, LOW); digitalWrite(zielona, HIGH); } if(currentMillis - previousMillis <=7000){//jesli uplynelo ponizej 7s to: digitalWrite(czerwona, HIGH); } else if((currentMillis - previousMillis>7000) && (currentMillis - previousMillis<=10000)){//jesli uplynelo pow. 7s i ponizej 10s to: digitalWrite(czerwona, LOW); digitalWrite(zielona, HIGH); } else if(currentMillis - previousMillis >10000){//jesli minelo 10s to: digitalWrite(zielona, LOW); previousMillis = currentMillis;//kasujemy stoper } Serial.println(digitalRead(2)); } Problem którego nie mogę obejść, to pomimo while przy przycisku, dioda zielona świeci się po puszczeniu przycisku, oczywiście tylko do upływu 10s, później działa normalnie znowu... Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Listopad 28, 2017 Udostępnij Listopad 28, 2017 £Przyznam się szczerze - nic z tego programu nie rozumiem 🙂 A to dopiero początek, co będzie po dodaniu żółtego światła, migania przed zmianą i przycisku dla inwalidów (żeby zielone światło trwało dłużej). Co najgorsze, nawet jak już wszystko pracowicie pojawi się w milionie if-ów, zawsze można dodać kolejną komplikację, np. opcję przejazdu na zielonym dla prezesa. Okazuje się, że taki styl programowania jest bardzo skomplikowany. Na szczęście istnieje dużo łatwiejsza, albo chociaż czytelniejsza metoda. Na początek trzeba zastanowić się jak właściwie system ma działać. A tutaj najlepiej sprawdza się obrazek - w tej chwili nie mam w czym narysować, dodam komentarz jak znajdę jakiś program do rysowania diagramów. [ Dodano: 28-11-2017, 22:54 ] Zanim zaczniemy zajmować się programem, warto chwilę poświęcić na zrozumienie jego działania. W podstawowej wersji mamy dwie jezdnie i sygnalizację świetlną przy każdej z nich. Upraszczamy - niech będzie tylko zielone i czerwone światło (na razie bez żółtego, migania itd). Co więcej chcemy uniknąć wypadków, więc jeśli zielone jest na jednej drodze, na drugiej musi być czerwone (i odwrotnie). Do opisania stanu naszego układu wystarczy więc powiedzieć jakie jest aktualnie światło powiedzmy na głównej drodze - jak jest zielone to wiadomo, że na podporządkowanej jest czerwone itd. Mamy więc dwa stany: zielone na głównej, albo czerwone na głównej. Stany zmieniają się po upływie pewnego czasu, albo po wykryciu samochodu. Wracając do diagramu, wygląda on mnie więcej tak: [ Dodano: 28-11-2017, 23:02 ] Mamy dwa stany: GREEN i LED, możemy je ponumerować, np: #define STATE_GREEN 1 #define STATE_RED 2 int state = STATE_GREEN Teraz mamy zmienną określającą aktualny stan oraz definicje dla stanów. Dalej możemy zaimplementować maszynę stanów na milion sposobów, pewnie najprościej będzie za pomocą instrukcji switch: void loop() { switch (state) { case STATE_GREEN: // zachowanie dla stanu green break; case STATE_RED: // zachowanie dla stanu red break; } } [ Dodano: 28-11-2017, 23:09 ] Jak zdefiniować zachowanie dla zielonego światła? To w sumie oczywiste. Najpierw sterujemy diodami: digitalWrite(czerwona, LOW); digitalWrite(zielona, HIGH); Dalej możemy po prostu dodać pętlę oczekującą na wyjście ze stanu, czyli mniej więcej: unsigned long start_time = millis(); while (state == STATE_GREEN) { unsigned long now = millis(); if (now - start_time >= 20000) state = STATE_GREEN; // timeout 20s else if ((now - start_time >= 5000) && (digitalRead(2) == LOW)) state = STATE_GREEN; // timeout > 5s && car detected } Taki, albo podobny kod jest na ogół czytelnieszy, są nawet programy które potrafią coś takiego wygenerować. To dużo łatwiejsze niż łamigłówki z milinem if/else. O przerwaniach nie wspominając. 1 Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 5, 2017 Autor tematu Udostępnij Grudzień 5, 2017 Wysmarowałem taką maszynę stanów 😉 : #include <LiquidCrystal.h> //Dołączenie bilbioteki #include <SimpleDHT.h> LiquidCrystal lcd(8, 3, 4, 5, 6, 7); //Informacja o podłączeniu int pinDHT11 = 13; //pin czujnika int maphum; //zmienna do sterowania diodami SimpleDHT11 dht11; #define led1 10 #define led2 12 #define led3 9 #define led4 11 #define led5 8 #define przycisk 2 #define STATE_DZIECI 1 #define STATE_ZOSIA 2 #define STATE_FRANEK 3 #define STATE_DOMINIK 4 #define STATE_WYNIKI 5 unsigned long pMillis = 0; int state = 1; int licznik = 1; void setup() { pinMode(2, INPUT_PULLUP); lcd.begin(16, 2); } void loop() { const long interval = 5000; unsigned long cMillis = millis(); switch(state){ case 1: dzieci(); if(cMillis - pMillis > interval) { pMillis = cMillis; licznik = 1; lcd.clear(); state = 5; } break; case 5: wyniki(); if(cMillis - pMillis > interval){ pMillis = cMillis; lcd.clear(); switch(licznik){ case 1: state = 2; break; case 2: state = 3; break; case 3: state = 4; break; case 4: state = 1; break; } } break; case 2: zosia(); if(cMillis - pMillis > interval){ pMillis = cMillis; licznik = 2; lcd.clear(); state = 5; } break; case 3: franek(); if(cMillis - pMillis > interval) { pMillis = cMillis; licznik = 3; lcd.clear(); state = 5; } break; case 4: dominik(); if(cMillis - pMillis > interval){ pMillis = cMillis; licznik = 4; lcd.clear(); state = 5; } break; } } void zosia(){ // lcd.begin(16, 2); lcd.setCursor(0,0); lcd.print(" Czesc Zosia :-)"); //tekst 1 linii } void franek(){ lcd.setCursor(0,0); lcd.print("Czesc Franek :-)"); //tekst 2 linii } void wyniki(){ byte temperature = 0; byte humidity = 0; dht11.read(pinDHT11, &temperature, &humidity, NULL); //odczyt z czujnika lcd.setCursor(2, 0); lcd.print("Wilg.: "); lcd.print((int)humidity); lcd.print("%RH"); lcd.setCursor(2, 1); lcd.print("Temp.: "); lcd.print((int)temperature); lcd.print("*C"); // wyswietlamy temp. i wilgotnosc wraz z jednostkami //delay(5000); } void dominik(){ lcd.setCursor(0,0); lcd.print("Czesc Dominik:-)"); //tekst 2 linii } void dzieci(){ lcd.setCursor(4,0); lcd.print("Dominik"); //tekst 2 linii lcd.setCursor(2,1); lcd.print("Zosia Franek"); } // dla jasności: program naprzemiennie wyswietla pozdrowienie kolejnego dzieciaka z wynikami DHT11 Proszę o konstruktywną krytykę. Problem jaki mi się objawia 😋 : w funkcji wyniki(), układ chyba nie może pobrać danych w czasie ciągłego obiegu pętli. Po dodaniu tam delay pobiera normalnie, ale delay nie lubimy... jak to zrobić? Dodatkowy stoper? Czeka mnie jeszcze dodanie przycisku... Edit: przycisk rozwiązałem, wystarczyło zmienić treść if-ów w FSM: if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))) To zostaje sprawa odzczytu DHT i muszę się nauczyć przewijać tekst przy pomocy millis zamiast delay... Edit2: cep ze mnie, odczyt się przecież zerował przy każdym obiegu pętli 😳 , wystarczyło przenieść zmienne... Teraz program tak wygląda i działa jak chciałem 😅 (zostało przewijanie)...: #include <LiquidCrystal.h> //Dołączenie bilbioteki #include <SimpleDHT.h> LiquidCrystal lcd(8, 3, 4, 5, 6, 7); //Informacja o podłączeniu byte temperature = 0; byte humidity = 0; int pinDHT11 = 13; //pin czujnika SimpleDHT11 dht11; #define led1 10 #define led2 12 #define led3 9 #define led4 11 #define led5 8 #define przycisk 2 #define STATE_DZIECI 1 #define STATE_ZOSIA 2 #define STATE_FRANEK 3 #define STATE_DOMINIK 4 #define STATE_WYNIKI 5 unsigned long pMillis = 0; int state = 1; int licznik = 1; void setup() { pinMode(2, INPUT_PULLUP); lcd.begin(16, 2); } void loop() { const long interval = 5000; unsigned long cMillis = millis(); switch(state){ case 1: dzieci(); if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))) { pMillis = cMillis; licznik = 1; lcd.clear(); state = 5; } break; case 5: wyniki(); if(cMillis - pMillis > interval){ pMillis = cMillis; lcd.clear(); switch(licznik){ case 1: state = 2; break; case 2: state = 3; break; case 3: state = 4; break; case 4: state = 1; break; } } break; case 2: zosia(); if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))){ pMillis = cMillis; licznik = 2; lcd.clear(); state = 5; } break; case 3: franek(); if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))) { pMillis = cMillis; licznik = 3; lcd.clear(); state = 5; } break; case 4: dominik(); if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))){ pMillis = cMillis; licznik = 4; lcd.clear(); state = 5; } break; } } void zosia(){ // lcd.begin(16, 2); lcd.setCursor(0,0); lcd.print(" Czesc Zosia :-)"); //tekst 1 linii } void franek(){ lcd.setCursor(0,0); lcd.print("Czesc Franek :-)"); //tekst 2 linii } void wyniki(){ dht11.read(pinDHT11, &temperature, &humidity, NULL); //odczyt z czujnika lcd.setCursor(2, 0); lcd.print("Wilg.: "); lcd.print((int)humidity); lcd.print("%RH"); lcd.setCursor(2, 1); lcd.print("Temp.: "); lcd.print((int)temperature); lcd.print("*C"); // wyswietlamy temp. i wilgotnosc wraz z jednostkami } void dominik(){ lcd.setCursor(0,0); lcd.print("Czesc Dominik:-)"); //tekst 2 linii } void dzieci(){ lcd.setCursor(4,0); lcd.print("Dominik"); //tekst 2 linii lcd.setCursor(2,1); lcd.print("Zosia Franek"); } Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. 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
Elvis Grudzień 5, 2017 Udostępnij Grudzień 5, 2017 Z takich drobnych uwag: * definiujesz stale, ale ich nie uzywasz: np. licznik = 2; lcd.clear(); state = 5; Co znaczy 5? Domyslam sie ze STATE_WYNIKI * do samo dotyczy pinow: pinMode(2, INPUT_PULLUP); 2 to stala "przycisk", a moze STATE_ZOSIA, czy 2 ms? * moze to nawyk, ale ja definicje (#define) umieszczam przed zmiennymi. Mozna odwrotnie, ale u Ciebie jest pomieszane: troche zmiennych, definicje stalych i znowu zmienne. Moze lepiej to poukladac? * wiele razy powtarza sie fragment kodu: if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))) { pMillis = cMillis; licznik = 3; lcd.clear(); state = 5; } Moze lepiej byloby zrobic z niego procedure? PS. Przepraszam za brak polskich znakow, to nie z lenistwa. Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 5, 2017 Autor tematu Udostępnij Grudzień 5, 2017 Nie wiem czy Cię dobrze rozumiem... licznik = 2; lcd.clear(); state = 5; zmienną licznik używam w funkcji wyniki(), stany(np.STATE_WYNIKI 5) ponumerowałem jak sugerowałeś. pinMode(2, INPUT_PULLUP); to standardowy opis pinu arduino jak uczono w kursie... Pomieszane define ze zmiennymi, przepraszam moja wina i wczorajszej godziny pracy i pośpiechu 😳 Co do if((cMillis - pMillis > interval) || ((cMillis - pMillis < interval) && (digitalRead(2) ==LOW))) { pMillis = cMillis; licznik = 3; lcd.clear(); state = 5; } sugerujesz zrobić funkcję void? Ok, słuszna uwaga. DZIĘKI ELVIS 😅 Dzisiaj dodałem jeszcze funkcję sterującą natężeniem podświetlenia wyświetlacza w zależności od oświetlenia w pokoju... muszę ją jeszcze "dokalibrować" wieczorem...: void jasnosc(){ odczytSw = analogRead(A3); swiatlo = map(odczytSw, 100, 900, 50, 255); analogWrite(10,swiatlo); } Tak generalnie o to chodzi w FSM? Napiszcie coś... to mój pierwszy raz... co jeszcze poprawić? Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 5, 2017 Udostępnij Grudzień 5, 2017 Jak chodzi o stale to mialem na mysli zeby zamiast np: switch(state){ case 1: dzieci(); napisac: switch(state){ case STATE_DZIECI: dzieci(); Dzieki temu kod jest czytelniejszy, bo "1" nie wiadomo co oznacza. To samo dotyczy pinow, latwiej zrozumiec: pinMode(przycisk, INPUT_PULLUP); digitalRead(przycisk) ==LOW Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 6, 2017 Autor tematu Udostępnij Grudzień 6, 2017 void loop() { unsigned long cMillis = millis(); lcd.print("hello, world!"); while(cMillis - pMillis < interval){} lcd.clear(); pMillis = cMillis; } Kto mi napisze co jest źle w tym kodzie?? Plan był taki, wypisujemy hello world, program czeka 1s i czyści ekran, stoper i wraca na początek pętli. Czemu to nie działa? (pMillis mam zdefinowane oczywiście wyżej programie, tak jak interwał). Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 6, 2017 Udostępnij Grudzień 6, 2017 Popatrz chwile na ten fragment: while(cMillis - pMillis < interval){} To jest cala petla. A teraz powiedz mi gdzie zmieniasz wartosci zmiennych ktore sa w warunku jej zakonczenia? Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 6, 2017 Autor tematu Udostępnij Grudzień 6, 2017 while(cMillis - pMillis < interval){} Kurde, nie łapię, mam w ustawieniach interval = 1000, pMillis na starcie = 0, więc while powinno nic nie robić aż minie 1000ms i następnie wyjście z while... Cały kod: #include <LiquidCrystal.h> unsigned long pMillis = 0; const long interval = 1000; LiquidCrystal lcd(8, 3, 4, 5, 6, 7); void setup() { lcd.begin(16, 2); } void loop() { unsigned long cMillis = millis(); lcd.print("hello, world!"); while(cMillis - pMillis < interval){} lcd.clear(); pMillis = cMillis; } Jeśli zrobię tak: #include <LiquidCrystal.h> //const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(8, 3, 4, 5, 6, 7); unsigned long pMillis = 0; const long interval = 1000; void setup() { lcd.begin(16, 2); } void loop() { unsigned long cMillis = millis(); lcd.print("hello, world!"); if(cMillis - pMillis < interval){} else{ lcd.clear(); pMillis = cMillis; } } To wyświetla jak szalone, za każdym obiegiem pewnie gasi i wyświetla... Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 6, 2017 Udostępnij Grudzień 6, 2017 Obie wersje sa bledne, chociaz przypadkiem daja inny rezultat. Wydaje mi sie ze ciagle nie do konca rozumiesz jak dzialaja zmienne - wczesniej z tym samym miales problem, pisalem ze przydaloby sie uzyc czegos innego niz Arduino i zobaczyc jak dziala debugger. Jesli napiszesz: int x = 5; x = 6; x = 7; To po wykonaniu pierwszyej linijki x bedzie miala wartosc 5, po drugiej 6, w trzeciej linii zostanie przypisane 7 i juz tak zostanie az do konca programu. Jesli przypisales do pMillis 0, do cMillis wartosc odebrana z funkcji millis(), to ich wartosci same sie nie zmienia. Po uplywie 1ms, sekundy, czy nawet godziny w cMillis nadal bedzie wartosc zwrocona przez millis. cMillis = millis() przypisuje aktulny czas, ale juz poxniej cMillis sie magicznie nie aktualizuje. Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 6, 2017 Autor tematu Udostępnij Grudzień 6, 2017 ale myślałem, że pętla biegnie cały czas, przecież nic jej nie zatrzymuje, i za każdym razem aktualizuje Cmillis.... tak samo jak we wcześniejszych moich programach z millis... Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 6, 2017 Udostępnij Grudzień 6, 2017 Sorki, drugi progam moze i jest ok, nie zauwazylem zmiany while na if 🙂 [ Dodano: 06-12-2017, 12:13 ] W programie z while masz petle, ktora blokuje dzialanie calego programu. A poniewaz nie aktualizujesz wartosci cMillis, while nigdy sie nie konczy Cytuj Link do komentarza Share on other sites More sharing options...
SOYER Grudzień 6, 2017 Autor tematu Udostępnij Grudzień 6, 2017 ok, w programie z if nie miałem opóźnienia po wykasowaniu ekranu i dlatego nie widziałem zgaśnięcia tekstu... teraz mam tak: void setup() { lcd.begin(16, 2); } void loop() { unsigned long cMillis = millis(); if(cMillis - pMillis < interval){} else{ napis(); pMillis = cMillis; } if(cMillis - pMillis < interval){} else{ lcd.clear(); } } teraz po uruchomieniu czeka sekundę i wyświetla napis ale go już nie gasi....(założenie to miganie tekstu z częstotliwością 1Hz) ok, wiem, że muszę na nowo ustawić cMillis i pMillis przy okazji odliczania czasu w drugiej części programu (czyszczenie ekranu), tylko na razie główkuję jak.... Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 6, 2017 Udostępnij Grudzień 6, 2017 Moze glowkowanie jest konieczne tylko dlatego ze wrociles z maszyny stanow do dlubania w delay-ach? Przeciez wyswietlanie to stan, czysty ekran - kolejny. Jesli narysujesz graf stanow, zobaczysz jakie to proste. I nie trzeba glowkowac... Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
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!