Skocz do zawartości

Zmienne millis, prośba o pomoc


SOYER

Pomocna odpowiedź

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.

Link do komentarza
Share on other sites

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

Link do komentarza
Share on other sites

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

  • Lubię! 1
Link do komentarza
Share on other sites

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");
}
Link do komentarza
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

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.

Link do komentarza
Share on other sites

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ć?

Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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ł).

Link do komentarza
Share on other sites

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

Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

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

Link do komentarza
Share on other sites

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

Link do komentarza
Share on other sites

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

Link do komentarza
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.