Skocz do zawartości
kris2k

Arduino i CanShield - programowe usprawnienia auta

Pomocna odpowiedź

A jaka jest dla programu różnica między "wcisnąłem przycisk i wskutek tego pojawiło się (lub znikło) napięcie" a "napięcie się pojawiło (lub znikło)"? Monitorujesz zmiany stanu (zdarzenia), a nie stany.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

A takie rozwiązanie Ci odpowiada (brak delay() 😞

#include <Timers.h>
#define button 2
#define CZAS_MIGNIECIA 500
#define ILE_MIGNIEC 3

boolean miganie=false;          //jeśli miga to true
uint8_t licznik=0;              //licznik mignięć
boolean przycisk=false;         //true - został wciśnięty
Timer czas;                     //będzie sterował prędkością pracy kierunkowskazu

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN,0);
  pinMode(button, INPUT_PULLUP);
  czas.begin(CZAS_MIGNIECIA);         

  Serial.begin(115200);
}

void loop() 
{
  //sprawdzamy naciśnięcie przycisku
  if(!miganie && !digitalRead(button))  miganie=true; //sprawdzamy gdy nie ma akcji

  //jeśli migamy i upłynął czas mignięcia  
  if(miganie && czas.available() && licznik<ILE_MIGNIEC*2)   
    {
        digitalWrite(LED_BUILTIN,licznik%2);
        Serial.println(licznik%2);
        licznik++;
        if(licznik==ILE_MIGNIEC*2)        //jeśli mignięto założoną ilość razy
          { 
            miganie=false; licznik=0; 
            digitalWrite(LED_BUILTIN,0);
            while(!digitalRead(button)) ; //ew. czekamy na zwolnienie przycisku
          }
    }
  if(czas.available()) czas.restart();  
}

 

Edytowano przez Belferek

Udostępnij ten post


Link to post
Share on other sites
1 godzinę temu, ethanak napisał:

A jaka jest dla programu różnica między "wcisnąłem przycisk i wskutek tego pojawiło się (lub znikło) napięcie" a "napięcie się pojawiło (lub znikło)"? Monitorujesz zmiany stanu (zdarzenia), a nie stany.

Zasada działania jest całkiem fajna, choć zupełnie nie mam pomysłu jak połączyć to z odliczaniem frazy:

x = ( (( aktualnyCzas - zapamietanyCzas02) % CZAS_MIGNIECIA) < (CZAS_MIGNIECIA / 2) );

Udostępnij ten post


Link to post
Share on other sites

Morze to ci się przyda. To jest zmodyfikowany przykład "blinkWithoutDelay"

const int ledPin =  LED_BUILTIN;// the number of the LED pin
// Variables will change :
int ledState = HIGH;             // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 250;           // interval at which to blink (milliseconds)
unsigned long czas;
unsigned long sekunda;
void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
czas=millis();
sekunda=czas/1000;
if (sekunda<=6 || sekunda>12)
{
    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
} 
  }
}

po tej modyfikacji dioda na pinie 13 miga co 250ms przez pierwsze 6s potem pauza do 13s i dalej miga

if (sekunda<=6 || sekunda>12)

zmodyfikuj tego if'a pod swoje potrzeby np: zmień || na && dodaj jakiś przycisk czy to tam chcesz. Ze zmianą interwału czy nr pin to napewno sobie poradzisz.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Jeżeli moja logika myślowa jest poprawna to właśnie uświadomiłem sobie gdzie leży mój problem. No to opowiadam:

  if (digitalRead(6) == LOW ) { //Jeśli przycisk wciśnięty
    click1 = true;  //startuje zliczanie czasu klikniecia 
  } else {
    zapamietanyCzas = aktualnyCzas; // zapamietuje kiedy zjawisko ustało, tutaj, przycisk zostal puszczony, ewentualnie nigdy nie klikniety
    click1 = false; //stopuje zliczanie czasu klikniecia
  }
    
  if (click1 == true) {
      dlugoscKliku = aktualnyCzas - zapamietanyCzas; //zapamietuje roznice w czasie trwania stanu LOW, czyli pokazuje czas przez jaki guzik byl wcisniety
  }

  if (dlugoscKliku > 1 && dlugoscKliku < 1000 && click1 == false) //dlugoscKliku musi byc mierzona od 1 bo przy 0 po odpaleniu programu warunek jest od razu spelniony
  {
  sekwencjaCzynnosci(); //<<------------ TA FUNKCJA WYWOŁA SIE TYLKO RAZ PO PUSZCZENIU PRZYCISKU
  }

Przy takim kodzie jak w/w, a kod takowy jest mi niestety potrzebny (bo to dzięki niemu moja sekwencja się wykona tylko raz i tylko gdy przycisk jest wciśnięty od 1ms do 1000ms) nie mogę wewnątrz tej funkcji używać kodu który w sposób ogólny odwołuje się do jakichkolwiek zmiennych bazujących na millis().

Dlaczego? Bo żeby millis() działało to program naturalnym biegiem musi te funkcje mielić, nadpisywać itd.... a jak mam taki kod wrzucony wewnątrz funkcji która wykonuje się tylko RAZ, no więc raz się przemieli i na tym koniec, program nie działa.

Poprawcie mnie proszę jeżeli się mylę!

Tym samym rodzi się pytanie, jak dobrze napisać moją sekwencjeCzynnosci(), inaczej niż delay'ami? (choć tak jak poniżej też działa!)

void sekwencjaCzynnosci()
{
  Serial.println("1 - krok 1");
  delay(250);
  Serial.println("0 - krok 2");
  delay(250);
  Serial.println("1 - krok 3");
  delay(250);
  Serial.println("0 - krok 4");
  delay(250);

  Serial.println(dlugoscKliku); // tylko informacyjnie wypluwam czas przez jaki przycisk byl klikniety
  dlugoscKliku = 0; //po wykonanej sekwencji ustawia ta zmienna na 0 i to sprawia że sekwencja już sie kolejny raz nie wykona
}

Mam wrażenie że zatoczyłem jakieś koło myślowe przez ostatnie dwa dni 😉

Edytowano przez kris2k

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

A tak z ciekawości - pokazana przeze mnie wcześniej kilka postów wyżej działająca propozycja Ci nie odpowiada? Nie korzysta ona  z delay(). Już na 3 stronach ponawiasz swe pytanie, na które dostajesz przecież różne propozycje rozwiązań od różnych osób. Oczekujesz gotowca? Może wystarczy dostosować proponowane rozwiązania do własnych potrzeb i już.

Edytowano przez Belferek
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Idąc tropem Slon'a, póki co, wywaliłem funkcje i wpisuje kod od razu w głównym IF'ie (jak opanuje sytuacje to potem wrzucę to do funkcji).

Wszystko działa poza tym, że mam problem z poprawnym zdefiniowanym ostatniego warunku który ma drukować zmiany tylko przez 3sek. (przy interwale 0,5sek mamy 6cykli).

Zmienną ledState zmieniłem na bool, wartościami 0x60 i 0x50 się nie przejmujcie, to jest ćwiczenie komend do wysyłania danych na linie CAN.

  if (digitalRead(6) == LOW ) { //Jeśli przycisk wciśnięty
    click1 = true;  //startuje zliczanie czasu klikniecia 
  } else {
    zapamietanyCzas = aktualnyCzas; // zapamietuje kiedy zjawisko ustało, tutaj, przycisk zostal puszczony, ewentualnie nigdy nie klikniety
    click1 = false; //stopuje zliczanie czasu klikniecia
  }
    
  if (click1 == true) {
      dlugoscKliku = aktualnyCzas - zapamietanyCzas; //zapamietuje roznice w czasie trwania stanu LOW, czyli pokazuje czas przez jaki guzik byl wcisniety
  }

  if (dlugoscKliku > 1 && dlugoscKliku < 1000 && click1 == false) //dlugoscKliku musi byc mierzona od 1 bo przy 0 po odpaleniu programu warunek jest od razu spelniony
  {

  unsigned long currentMillis = millis();
  
  if (currentMillis - previousMillis >= interval) // ten IF spelnia sie raz co 500ms, interval = 500;
  {
    previousMillis = currentMillis;

    // podpinam swoją zmienna pod zmiany true/false
    if (ledState == false) {
      ledState = true;
      B = 0x60;
    } else {
      ledState = false;
      B = 0x50;
    }
  

    if ( WARUNEK <= 3000  ) // <<--- TU TKWI PROBLEM
    { 
      // set the LED with the ledState of the variable:
      //digitalWrite(ledPin, ledState);
      Serial.println(B, HEX); // <<--- bo ma się wykonywać tylko przez pierwsze 3sek
    }

  }
  }

Belferek, przepraszam że nie skomentowałem Twojego kodu wcześniej, po prostu jest napisany takim stylem że nie mam pojęcia co tam się w nim dzieje, o próbie adaptacji na swoje potrzeby nie wspominając 😕

Wolę pomęczyć się z bezpośrednio z  millis() i dobrze je zrozumieć niż brnąc dalej i dalej nie rozumując podstaw.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Informacyjnie dla niedowiarków 😛

Wasze podpowiedzi bardzo mocno przyczyniają się do rozbudowy mojego projektu i jak najbardziej z nich korzystam, tutaj z akcelerometrem ADXL345.

Czyli aktywacja awaryjnych przy gwałtownym hamowaniu. Przykład na filmiku poniżej:

 

Zastosowałem kod ethank'a a później przerobiłem go na kod slon'a.

Dzięki temu nie wysyłam tony niepotrzebnych zdublowanych komunikatów do linii CAN, a tylko co 250ms zmieniam komunikat:

int sekwencjaCzynnosciACC()
{   
  byte A = 0x00;
  if (aktualnyCzas - previousMillis >= interval) // ten IF spelnia sie raz co 250ms
  {
    previousMillis = aktualnyCzas;

    // if the LED is off turn it on and vice-versa:
    if (ACCmiga == false) {
      ACCmiga = true;
      A = 0x60;
    } else {
      ACCmiga = false;
      A = 0x00;
    }
    CanSend(0x02214000, 0x00, 0x00, A, 0x00, 0x00, 0x00); //ramka CAN która odpala kierunki na odcietej głowie :P
    //Serial.println(A, HEX); wydruk dla sprawdzenia stanu
  }
    
}

Tak więc naprawdę Wam dziękuje za wszelkie podpowiedzi !!!

Natomiast jak sobie nie radzę z adaptacją Waszych podpowiedzi na moje potrzeby, to nie miejcie mi tego za złe, cały czas się staram!

Edytowano przez kris2k

Udostępnij ten post


Link to post
Share on other sites
6 godzin temu, kris2k napisał:

Wszystko działa poza tym, że mam problem z poprawnym zdefiniowanym ostatniego warunku który ma drukować zmiany tylko przez 3sek. (przy interwale 0,5sek mamy 6cykli).

Na pewno sześć pełnych cykli? Po co Ci ostatni cykl "zgaszone przez ćwierć sekundy"?

Owszem, masz sześć zmian, ale ostatnia (szósta) zmiana kończy całą imprezę.

Jednak moja wersja była prostsza 😉 A problem niewysyłania zwielokrotnionych komunikatów można rozwiązać w dużo prostszy sposób zamiast kombinować jak sł^Wkoń pod górkę - np tworząc funkcję wysyłającą na magistralę komunikat wtedy kiedy jest to konieczne (tzn. funkcja została wywołana z innym argumentem niz poprzednio).

 

Taki kawałek kodu na przykład:
 

void kierunek(bool zapalony)
{
	static bool stan = FALSE;
	if (zapalony != stan) {
		stan = zapalony;
		CanSend(0x02214000, 0x00, 0x00, stan ? 0x60 : 0, 0x00, 0x00, 0x00);
	}
}

 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

A to też bardzo sprytny kawałek kodu, zapewne użyję prędzej czy później. Dzięki!

(Cykl 6 razy po to żeby przekaźnik którym będę sterować pozostawić w pozycji otwartej -> przewody rozwarte -> kierunek zgaszony).

Generalnie chodzi o to żeby zacząć od włączenia a zakończyć na wyłączeniu.

Na ten moment muszę się skupić na wykonaniu sekwencji konkretną ilość razy po kliknięciu przycisku.

Co do zasady, kod slon'a (przerobiony z mrugania LED'em) działa bardzo dobrze, nawet mogę zmusić go do wykonania się konkretną ilość razy... ale działa to tylko po zainicjowaniu pracy Arduino. Nie potrafię tego przerobić na inicjację przyciskiem. Zmienna aktualnyCzas przybiera duże wartości, bo millis() rośnie szybko w czasie i wtedy ten kod już mi nie działa ;/ Przy inicjalizacji Arduino owszem, ale z klikaniem guzikiem już nie.

 

const int ledPin =  LED_BUILTIN;// the number of the LED pin
// Variables will change :
int ledState = LOW;             // ledState used to set the LED

unsigned long previousMillis = 0;    // will store last time LED was updated
const long interval = 500;           // interval at which to blink (milliseconds)


void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  pinMode(6, INPUT_PULLUP); //Przycisk jako wejście
  Serial.begin(115200);
}

void loop() { 
  unsigned long currentMillis = millis();
  //warunek spelnia sie tylko co 500ms i jego zawartosc updejtuje sie tez tylko co 500ms
  if (currentMillis - previousMillis >= interval){
    //Serial.println(previousMillis);
    previousMillis = currentMillis; // rosnie w nieskonczonosc co interwal 500ms

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    if ( previousMillis <= 3001UL ) // ogranicznie wydruku w czasie
    { 
      previousMillis = currentMillis;
      // set the LED with the ledState of the variable:
      //digitalWrite(ledPin, ledState);
      Serial.println(ledState); // wydrukuje się tylko 6razy, pierwszy krok to 1 / HIGH, ostatni krok to 0 / LOW
    }   
  }

}

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)
2 godziny temu, kris2k napisał:

Zmienna aktualnyCzas przybiera duże wartości, bo millis() rośnie szybko w czasie i wtedy ten kod już mi nie działa ;/

Że co? Nawet przy fatalnie napisanym programie są to czasy rzędu tygodni...

Załóżmy coś takiego:

  • masz zmienną globalną "coRobimy" w której trzymasz wykonywaną czynność. Załóżmy że 0 to "nie robimy nic", 1 to "migamy trzy razy".
  • masz zmienną globalną "czasStartu" typu uint32_t w której trzymasz czas zapamiętany w chwili, kiedy wartość coRobimy zmieniła się z 0 na coś innego.

Czyli coś w stylu:
 

if (dostalem_sygnal()) {
    coRobimy = MIGAMY_TRZY_RAZY;
    czasStartu = millis();
}

Dalej mamy coś takiego, co pozwoli migać sobie czymśtam ogólnie (wykorzystam funkcję z poprzedniego posta):
 

uint32_t czasWykonania = millis() - czasStartu;
if (coRobimy == MIGAMY_TRZY_RAZY) {
	kierunek(czasWykonania % (2 * CZAS_MIGNIECIA) < CZAS_MIGNIECIA);
}

W ten sposób zapewniamy sobie, że migać będziemy regularnie począwszy od otrzymania sygnału (nacisnięcia klawisza albo czegoś tam innego). Nie zajmujemy się tutaj żadnymi liczeniami ile razy mignęliśmy ani ile razy mamy mignąć, interesuje nas tylko regularne migania. W rzeczywistości - ponieważ migać moglibyśmy raz, siedem albo dowolnie długo powinniśmy mieć jakąś możliwość stwierdzenia że "trzeba migać" (np. ustawiony bit "MIGAMY"), i warunek wyglądałby jakoś tak:
 

if (coRobimy & ( 1 << MIGAMY)) { // czy w ogóle migamy, nieważne ile razy

No i migamy sobie, migamy aż przyjdzie pora na przestanie migania, bo dalej w kodzie mamy coś takiego:
 

if (coRobimy == MIGAMY_TRZY_RAZY) {
	if (czasWykonania > 5 * CZAS_MIGNIECIA) { // czasWykonania obliczyliśmy wcześniej
		coRobimy = 0 ; // przestajemy migać
		kierunek(0); // co prawda nie powinno być potrzebne, ale warto się nauczyć po sobie sprzątać
	}
// i na przykład
    return; // powrót z pętli loop bo dalej nie ma już nic ciekawego
}

W ten sposób Twoje obawy o zbyt dużą wartość osiąganą przez millis() są bezzasadne - po prostu jedno mignięcie po ok. 50 dniach migania bez przerwy będzie trwało nieco krócej. Ale jakoś nie wyobrażam sobie migania bez przerwy przez 50 dni 🙂

Jest to jakoś tam zrozumiałe?

 

Edytowano przez ethanak
  • Lubię! 1
  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

ethanak, bardzo fajnie mi rozpisałeś metodologię i jestem za to bardzo wdzięczny, co do zasady, zrozumiałem :)

Zatarłem rączki i zacząłem to przepisywać w program który mógłby coś robić, więc wstawiam mój ćwiczeniowy kod, wraz z pytaniem dot. jego działania (bo jakże inaczej, nie działa :P )

Domyślam się że coś jest nie tak jak powinno ze zmienną czasWykonania, sposób jej wyliczenia w tym kodzie zawsze daje zero, więc całość nie generuje cyklicznych zmian 1/0.

const int ledPin =  LED_BUILTIN;// the number of the LED pin

unsigned long previousMillis = 0;    // will store last time LED was updated
const long interval = 500;           // interval at which to blink (milliseconds)

bool coRobimy = false;
unsigned long czasStartu = 0;
int X = 0;
int CZAS_MIGNIECIA = 500;

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  pinMode(6, INPUT_PULLUP); //Przycisk jako wejście
  Serial.begin(115200);
}

void loop() {
  
  if ( millis() > 2000 && millis() < 4000) //jak mamy spełniony warunek to coś robimy
  {
  coRobimy = true;
  czasStartu = millis();
  }else // a jak nie,  to nic nie robimy
  {
  coRobimy = false;
  czasStartu = 0;
  }

// tutaj pierwszy krzak... co ma reprezentowac zmienna "czasWykonania" skoro ona cały czas daje ZERO ?
// użycie jej w takiej postaci, sprawia że cały warunek z reszta z dzielenia i tak sie nie spełnia
uint32_t czasWykonania = millis() - czasStartu;

  if (coRobimy == true) {  
  //X = ( ( millis() % CZAS_MIGNIECIA) < (CZAS_MIGNIECIA / 2) ); // Na próbe wpisane samo millis() - Działa, są cykliczne zmiany 1/0
  //X = ( ( millis() % (2* CZAS_MIGNIECIA) ) < CZAS_MIGNIECIA ); // Na próbe wpisane samo millis() - Działa, są cykliczne zmiany 1/0
  X = ( ( czasWykonania % CZAS_MIGNIECIA) < (CZAS_MIGNIECIA / 2) ); //brak cyklicznych zmian 1/0
  //X = ( ( czasWykonania % (2* CZAS_MIGNIECIA) ) < CZAS_MIGNIECIA ); //brak cyklicznych zmian 1/0
  Serial.println(X); //printem podglądam czy wyrażenie daje dobre rezultaty
  }
}

 

Udostępnij ten post


Link to post
Share on other sites

"Czas wykonania" masz akurat dobrze 😉

Czy ja nie wspominałem, że masz reagować na zdarzenia, a nie na stany? A Ty uparcie wtykasz reakcję na stan (w Twoim przypadku jest "czy czas jest między 2000 a 4000" zamiast "czy czas przekroczył 2000"). Nic dziwnego że czas wykonania masz zero, jeśli co chwila startujesz wykonanie czynności od zera.

Wyobraź sobie oświetlenie np. schodów. Po zasłonięciu fototranzystora światło zapala sie na ileś tam sekund. A ty zamiast wleźć na te schody uparcie cały czas zasłaniasz ten fototranzystor, licznik sekund cały czas się zeruje i dziwisz się, czemu nie gaśnie...

A wystarczy przecież nie reagować na wciśnięcia klawisza jeśli czynność już się wykonuje, prawda?

Na przykład:

if (millis() > 2000 && millis() < 4000 && !coRobimy) {...

Nie mówię że ma to sens, ani że jest dobrze... ale przynajmniej zadziała.

  • Lubię! 1
  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

W miedzy czasie przeprosiłem się z biblioteką Timers.h i uzyskałem to co chciałem dzięki niej ;)

ale Twoje rady też już wdrożyłem i nawet przerobiłem kod na działający przykład z przyciskiem.

W końcu to sobie wytłumaczyłem po swojemu (o co chodzi ze stanem i jego zmianą) i zaskoczyło... SUPER, bardzo dziękuję za pomoc i cierpliwość!!!

bool coRobimy = false;
unsigned long czasStartu = 0;
int X = 0;
int CZAS_MIGNIECIA = 500;

void setup() {
  pinMode(6, INPUT_PULLUP); //Przycisk jako wejście
  Serial.begin(115200);
}

void loop() {


if ( digitalRead(6) == LOW && coRobimy == false ) //jak mamy sygnał LOW i stan False, to odpalamy warunek i zmieniamy stan
{
    coRobimy = true; //zmiana stanu
    czasStartu = millis(); //czas sie zapamieta przy zmianie stanu
}


uint32_t czasWykonania = millis() - czasStartu;

if (coRobimy == true && czasWykonania <= 2000) //mrugamy
{
    X = ( ( czasWykonania % CZAS_MIGNIECIA) < (CZAS_MIGNIECIA / 2) );
}

if (coRobimy == true && czasWykonania > 2000){
coRobimy = false; //po uplywie 2sek, zmieniam stan i powyzszy if przestaje sie spelniac, przestajemy mrugac
}

}

 

Udostępnij ten post


Link to post
Share on other sites

Panowie, mam szybkie pytanie.

Zrobiłem użytek z jednej z Waszych podpowiedzi, czyli rozpoznawania stanu wciśniętego guzika.

https://www.arduino.cc/en/Tutorial/StateChangeDetection

Moje pytanie brzmi:

Czy wyjsciami 5V i GND mogę obsłóżyć (zasilić) kilka takich obwodów(gałęzi) ?

Każdy obwód oczywiści będzie sprawdzany innym pinem cyfrowym, tylko masa i 5V będą wspólne dla wszystkich.

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