• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Wielowątkowość - aplikacja inteligentny dom
#1
Muszę zrobić aplikację, która będzie nasłuchiwać na 30 wejściach Andino MEGA stanów wysokich. 

W pętli głównej mam więc nasłuchiwanie każdego z pinów - pętla robi obrót co około100 milisekund.  Dłuższy czas obrotu mógłby spowodować, że nie będę widział jak na jednym z pinów pojawi się stany wysoki - po prostu program mógłby go przegapić.  

Załączanie stanu wysokiego to nic innego jak sytuacja w której domownik włączy światło włącznikiem monostabilnym na ścianie co wygeneruje stan wysoki dla przekaźnika załączającego obwód światła.

Muszę w pętli mieć też odpowiednio dużo czasu na to aby między obrotami sprawdzać warunki - rejestry stanów dla każdego z wejść z osobna i zależności od scenariusza wysyłać odpowiednie rozkazy dane na wyjście  - stan niski lub wysoki. 

Oczywiście te 100 ms to rzecz dyskusyjna. Może działałoby to dobrze przy większej tolerancji ale nie będę stał przy włączniku i trzymał go przez sekundę. 

Do tego momentu mam napisany kod na ifach i wszystko mi działa bardzo dobrze. 

Problem pojawia się gdy do gry chcę wpiąć bibliotekę serwera ethernet i wystawić jakieś rest API po TCP/IP.  Tak naprawdę powinienem postawić drugą pętlę, która będzie się wykonywać równolegle i nasłuchiwać czy przez www nie nadeszły jakieś rozkazy związane z koniecznością wyłączenia / włączenia jakiegoś odbiornika.  Te pętle powinny działać w taki sposób aby jedna drugiej nie blokowała. 


O asynchroniczności już nie wspomnę, że w jednym czasie może pojawić się kilka reqestów  np wygenerowanych z kilku różnych systemów sterujących czy to przez użytkownika z poziomu np aplikacji mobilnej. 

Szczerze mówiąc nie mam pomysłu jak obsługiwać żądania przez TCP/IP bo tam timeout może wynieść nawet kilka sekund. Nie mogę zatrzymać jednej pętli nasłuchującej piny  bo przegapię informacje na temat tych stanów, które mogłoby się pojawić na wejściach.  

Całość piszę sobie na controllino mega czyli to ten sam procesor co po Arduinio MEGA. Różnica jest tylko w tym, że jest tam  w tym, że niektóre porty są inaczej zmapowane i jako moduł LAN jest układ W5100.

Proszę o info jak Waszym zdaniem można rozwiązać taki problem?
 
Odpowiedź
#2
Twoim rozwiązaniem jest "PCINT" i wcale nie musisz się nasłuchiwać ani martwic że coś przepadnie. Samo się zgłosi...
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#3
Jarewa0606 ma jak najbardziej rację, ale nie do końca.
Przerwania od PCINT mamy w Atmega2560 trzy wektory, czyli 24 piny będą zgłaszały przerwanie.
I jak już się domyślamy, mając trzy wektory, to musimy trochę pokombinować aby dowiedzieć się, który pin się zgłosił.
Osiem pinów do jednego przerwania daje nam:
Kod:
    PCMSK2 = (1 << PCINT16) | // Pin change enable mask 16
             (1 << PCINT17) | // Pin change enable mask 17
             (1 << PCINT18) | // Pin change enable mask 18
             (1 << PCINT19) | // Pin change enable mask 19
             (1 << PCINT20) | // Pin change enable mask 20
             (1 << PCINT21) | // Pin change enable mask 21
             (1 << PCINT22) | // Pin change enable mask 22
             (1 << PCINT23);  // Pin change enable mask 23

    PCMSK1 = (1 << PCINT8) |  // Pin change enable mask 8
             (1 << PCINT9) |  // Pin change enable mask 9
             (1 << PCINT10) | // Pin change enable mask 10
             (1 << PCINT11) | // Pin change enable mask 11
             (1 << PCINT12) | // Pin change enable mask 12
             (1 << PCINT13) | // Pin change enable mask 13
             (1 << PCINT14) | // Pin change enable mask 14
             (1 << PCINT15);  // Pin change enable mask 15

    PCMSK0 = (1 << PCINT0) | // Pin change enable mask 0
             (1 << PCINT1) | // Pin change enable mask 1
             (1 << PCINT2) | // Pin change enable mask 2
             (1 << PCINT3) | // Pin change enable mask 3
             (1 << PCINT4) | // Pin change enable mask 4
             (1 << PCINT5) | // Pin change enable mask 5
             (1 << PCINT6) | // Pin change enable mask 6
             (1 << PCINT7);  // Pin change enable mask 7

Do tego dochodzą nam bardzo fajne przerwania zewnętrzne.
W Atmega2560 jest ich "aż" osiem INT0 - INT7.
Więc Kamil2234 będzie zadowolony.
Proponuję skorzystać z biblioteki https://github.com/NicoHood/PinChangeInterrupt
I bez kombinowania można obsłużyć wszystkie przerwania od pinów.
Proponuję kamil2234
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#4
No chciałem zgłębić temat przerwań - natomiast wytłumaczcie mi jedną rzecz.  Bo napisałem że chcę wykorzystać 30 wejść.  Czy przerwania działają tylko dla wybranych pinów? Czy można je ustawić na każdym?  Oczywiście przy założeniu, że działamy na procesorze takim jak ma Andino MEGA.
 
Odpowiedź
#5
No to kolega wyżej ci pisał jest 24 PCINT i dodatkowo masz 8 przerwań INT, potrzebujesz 30 a max jest 32 wiec ciut więcej od wymaganych.. A w dodatku podał ci bibliotekę do przerwań PCINT wiec masz z górki.. Przerwania działają tylko dla wybranych pinów... W dokumentacji msz opisane piny...
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#6
Oj chyba nie jest tak prosto jak piszesz. Albo jak coś źle rozumiem. Z tego co patrzyłem w specyfikacji procesora Atmel ATmega2560 nie wszystkie wejścia da się podpiąć pod ten rodzaj przerwania. Na 16 z nich tylko 8 jest da się podciągnąć pod te przerwanie.

A tak jak pisałem mój docelowy projekt powstaje pod Controlino - a tam nie ma możliwości aby dowolnie przepinać wejścia. Akurat potrzebuję wszystkich analogowych.

Pomysł mam inny - mogę pod wejście interrupts - IN0 labo IN1 zmostować ze wszystkimi wejściami za pomocą diody. Wówczas na jakimkolwiek jak zmieni się stan to będę mógł sprawdzić wszystkie.

Ale z tymi przerwaniami to średnio fajne rozwiązanie. Bo jeśli przykładowo w tym czasie alarm w domu wykryje pożar i po API wyśle komunikat aby wyłączyć wszystkie odbiorniki to ja będę miał zajęte. Albo wystarczy, że ktoś przytrzyma jeden włącznik na dłużej i serwer nigdy nie odbierze informacji po sieci - cały czas będzie trwało przerwanie.

Fajne ale nie do końca mnie satysfakcjonuje.
 
Odpowiedź
#7
Nie rozumiesz przerwań

"Ale z tymi przerwaniami to średnio fajne rozwiązanie. Bo jeśli przykładowo w tym czasie alarm w domu wykryje pożar i po API wyśle komunikat aby wyłączyć wszystkie odbiorniki to ja będę miał zajęte. Albo wystarczy, że ktoś przytrzyma jeden włącznik na dłużej i serwer nigdy nie odbierze informacji po sieci - cały czas będzie trwało przerwanie."

Właśnie po to są przerwania kiedy atmega będzie zajęta czymś innym to "przerwanie" pauzuje jej działanie i wykonuje to co w przerwaniu dlatego tam ma być bardzo krótkie i szybkie by nie blokowało wiec tylko sprawdzasz jaki pin wykonał przerwanie i przepisujesz flagę zmiennej po tym kończy i wraca do tego co zostało przerwane i w "czasie" programu już czeka flaga że było zdarzenie na pinie. Wiec nie ważne ile ktoś trzyma przycisk bo może i do usranej śmier.... a przerwanie trwa tyle na ile jest w nim kod napisany.
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#8
Photo 
No ale informacje o tym, że jest pożar mogę odebrać po TCP/IP gdzie muszę mieć ciągle załączony  prosty serwer www do tego aby wystawić rest API.

Tak jak już wcześniej napisałem widzę tu możliwość zastosowania przerwań na zasadzie połączenia wszystkich wejść pod IN0 lub IN2.    Przerwań chcę użyć do tego aby w priorytecie nasłuchiwać wciśnięcia przycisków.  W momencie jak na dowolnym wejściu zmieni się stan to mogę uznać to aktywuje przerwanie.  W tym momencie serwer WWW przerwie prace na co najmniej 100ms bo tyle trwa włączenie pojedynczego przycisku.  Gdybym miał jeden przycisk do obsługi to sprawa byłaby prosta.  Przerwanie trwałoby 1/10 sekundy.  Ale w tym samym czasie lub chwilę potem kolejne osoby mogą wciskać przyciski lub je przetrzymywać.

Mogę  oczywiście zrobić jakieś mechanizmy żeby ignorować długie wciskanie, ale skąd będę wiedział, że w tym samym czasie ktoś inny nie wcisnął innego przycisku skoro pod IN1 będę miał zmostowane wszystkie sygnały z wejść.


Poniżej wrzuciłem schemat - aby łatwej było zrozumieć o co chodzi. Na przykładzie 3 włączników
microswitch

[Obrazek: 7fae9b1abcf674d3med.png]


[Obrazek: e62605096b026071med.png]
 
Odpowiedź
#9
"Przerwanie trwałoby 1/10 sekundy"

Jak dobrze napisany program to nawet poniżej 10us.... razy nawet 30 to i tak daje 300us.... No ale tu trzeba pomyśleć czy to nie był by sabotaż że nagle 30 przycisków aktywnych...


A moze trochę przesadziłem akurat tego co potrzebujesz, digitalread trwa około 4us jak masz przerwanie PCINT to musisz sprawdzić który pin wiec 8x4= 32us będzie na to by odczytać który pin, a potem daje te 6us na przepisanie flagi to i tak za dużo mimo wszystko w 1ms powinieneś się wyrobić. Wiec to nie jest 1/10s lecz 1/1000 sekundy....
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#10
(09-03-2021, 21:23)Jarewa0606 napisał(a): "Przerwanie trwałoby 1/10 sekundy"

Jak dobrze napisany program to nawet poniżej 10us.... razy nawet 30 to i tak daje 300us.... No ale tu trzeba pomyśleć czy to nie był by sabotaż że nagle 30 przycisków aktywnych...


A moze trochę przesadziłem akurat tego co potrzebujesz, digitalread trwa około 4us  jak masz przerwanie PCINT to musisz sprawdzić który pin wiec 8x4= 32us będzie na to by odczytać który pin, a potem daje te 6us na przepisanie flagi to i tak za dużo mimo wszystko w 1ms powinieneś się wyrobić.  Wiec to nie jest 1/10s lecz 1/1000 sekundy....

No nie do końca się zgodzę - dziś zrobiłem testy. Teoria jedno , a życie co innego Smile  Muszę w jakiś sposób wykluczyć zjawiska drgania styków. Podczas załączania obwodów takie przerwania nie wywołuje się raz, tylko nawet kilka razy.  Są to wartości losowe.   Zrobiłem test mikroswichem na Arduino UNO.   Włączenie przycisku sprawia, że włączenie diody odbywało się po kilka razy - całkowicie losowe zjawisko.    Przerwania wykonywały się po kilka razy.  Czasami nawet 8 razy na jedno wciśnięcie przycisku.   Dlatego aby tego uniknąć musiałem brać pod uwagę impulsy o czasie 350 milisekundy - czyli jest to około 1/3 sekundy.  Mógłbym zrobić 1/10 ale wówczas  większość prób kończyłaby się tym, że dioda zapalałaby się, a potem mogła by się znowu zapalić i zgasnąć...

No i skoro to już mamy ustalone. To można byłoby zrobić jakiś układ kształtowania impulsów przy każdym włączniku, ale to nie gwarantuje, że będą pojawiać się takie zjawiska same z siebie przy większych odległościach kabli itp. 

Co do PCINT to tak jak już wspomniałem nie da się na wszystkich Pinach ich używać.   Zwróć uwagę, że na Controlino Mega jest wyprowadzonych 16 wejść analogowych.   Wszystkie są mi potrzebne.    Do tego udało mi się za pomocą przedniego panelu w rejestrach zmieniać wyjścia 2A na wejścia.    Tak więc mam do dyspozycji mniej więcej po tyle samo wejść i wyjść. 



Kod:
// na płytkę ARDUINO UNO

const int buttonPin = 2 ;
volatile bool ledState = LOW;


void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(2),toggleLedISR, HIGH);
 
}

void loop() {
// sztuczne opóźnienie
delay(500) ;
}


void toggleLedISR (){
  static unsigned long lastTime;
  unsigned long timeNow = millis();
 
    unsigned long por = timeNow - lastTime;

  if (por > 350) {// drganie styków -  nie reaguje na załączenie poniżej 50 ms
   
      ledState =! ledState ;
   digitalWrite(LED_BUILTIN,  ledState);
  
   }
  
  lastTime = timeNow ;
 
}
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości