Skocz do zawartości

Zmienne millis, prośba o pomoc


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.

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

£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

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

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.

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

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

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?

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

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.

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

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

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

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

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