Fórum témák

» Több friss téma
Fórum » Arduino
A klónok CH340 Soros-USB illesztőjének drivere (Letöltés)
Lapozás: OK   867 / 867
(#) meslac válasza Panhard hozzászólására (») Pé, 14:37 /
 
Köszönöm! Én nem akarom nagyon bonyolítani, az enyémen van egy gomb redőnyönként ami fel-le irányt vezérli és egy amivel meglehet állítani. Ez működik is, csak az aktuális relé meghúzva marad, ezt kellene egy idő után automatikusan kikapcsolni.
(#) tki válasza meslac hozzászólására (») Szo, 9:56 /
 
Na, szóval nem találtam jó leírást, magyarázatot, úgyhogy nekem kell megfogalmazni. : -)

Először is fel kell deríteni, a jelenlegi program hogyan működik.

1. A loop()-ot újra és újra meghívja a keretrendszer, emiatt újra és újra elindul a feldolgozás.
2. Erre a blokkra: "if (header.indexOf("GET /5/fel") >= 0) {...}", ill. a “le” blokkra csak akkor fut rá a program, ha:
    - kapcsolódva van hozzánk egy kliens (a client.connected() eredménye igaz),
    - ha van új adat a klienstől, vagyis éppen küld egy kérést (a client.available() eredménye igaz vagy nem nulla),
    - és ha a kérés éppen véget ért, vagyis megtaláltuk a \n karaktert, ami egy sor végét jelöli.

Tehát nagyon fontos információ, hogy bármit is teszel ebbe a blokkba: "if (header.indexOf("GET /5/fel") >= 0) {...}", az egy kéréshez csak egyszer fut le, mert a következő körben a “client.available()” nulla lesz, mivel feldolgoztunk minden karaktert. Így tehát nem alkalmas az időzített relékikapcsolásra. Ebben lehetnek apróbb különbségek a valóságtól, mivel a konkrét kódodat sem láttam, ill. nem is próbáltam ezt így ki, de ezt elég könnyű néhány debugkiíratással ellenőrizned.

Szóval mit kell ilyenkor csinálni? Keresni kell a kódban egy olyan helyet, ahol az előzőektől függetlenül is mindig le tud futni egy blokk, ami egy idő után kikapcsolja a reléket. Ezt majd a “fel”, ill. “le” blokkban beállított, erre a célra fenntartott változóval fogjuk vezérelni, hogy mikor csináljon valamit - az ilyesmit hívják állapotgépnek.

Hová lehet tenni ezt a blokkot? Ha azt akarom, hogy a kérés során bekapcsolódott relék akkor is kikapcsolásra kerüljenek, amikor nincs új kérés, sőt nincs is már kapcsolódva kliens, akkor az összes feldolgozáson kívülre kell tennem, valahová a loop()-ba, nem a while cikluson belülre.

Tehát alaphelyzetben úgy fog kinézni a program folyása, hogy:
1. nyugi van, meghívódik a loop() újra és újra, de nincs kérés, valamint a relékikapcsoló blokkra is ráfut a program, de a belsejébe nem jut be, mivel még nem adtunk neki feladatot,
2. megjön egy kérés egy hozzánk kapcsolódott klienstől, bejutunk a "fel" (vagy a “le” blokkba),
3. ott beállítunk egy változót, amitől a relékikapcsoló blokk a loop() következő hívásától kezdve elkezdi figyelni az időt,
4. a relékikapcsoló blokk minden loop-hívásnál ellenőrzi az időt, és ha elég idő eltelt, akkor lekapcsolja az aktív relét és ettől kezdve nem dolgozik tovább a következő loop-hívásoknál.

A relékikapcsoló blokk pszeudokódja valami ilyesmi lesz, bár menet közben még kiderülhetnek dolgok, például arra is kell figyelni, mi történjen, ha már be van kapcsolva egy relé, de közben jön megint egy kérés - nehéz a programozó élete...:

if (valamelyik relé be van kapcsolva, vagyis pl. a "bekapcsolva" nevű változó igaz) {
    if (elég idő eltelt) {
        kikapcsolom a relét (vagy reléket, majd meglátjuk, lehet-e egyszerűsíteni)
        alaphelyzetbe, false-re állítom pl. a "bekapcsolva" nevű változót, hogy ne jussunk be újra ebbe a blokkba
    }
}

Ezután jön a programban már benne levő while-lel kezdődő kérésfeldolgozás.

Nemsokára folytatom.
A hozzászólás módosítva: Szo, 10:06
(#) tki válasza tki hozzászólására (») Szo, 10:10 /
 
"Ezután jön a programban már benne levő, while-lal kezdődő kérésfeldolgozás." - közben lejárt a szerkesztési idő. : -)

És még az az egy infó hiányzik, hogy a "bekapcsolva" változót az "if (header.indexOf("GET /5/fel") >= 0) {...}", ill. a "le" blokkban fogjuk igazra állítani. Ennek az állapotváltozónak meg kell tartania az értékét a loop() függvény egyes hívásai közt. Itt van róla egészen jó leírás, hogy hogyan kell csinálni, csak a kódok formázása rossz, mint mindig...: https://hu.education-wiki.com/9534151-static-keyword-in-c

Ennyiből már meg lehet írni, de nem baj, ha még nem megy.
A hozzászólás módosítva: Szo, 10:19
(#) tki válasza tki hozzászólására (») Szo, 10:33 /
 
Csak kimaradt még valami mondván, hogy majd folytatom, de mégis nehéz így itt hagyni: szóval ha már a "bekapcsolva" változót emlegettük, akkor azt is mondani kell, hogy a utolsó bekapcsolási időt is el kell tárolni vele együtt a "fel" és "le" blokkokban, amit a különálló, relékikapcsoló blokkban fogunk alapul venni a kikapcsoláshoz, ahogy a jelenlegi kódod is próbálta megoldani. Ahogy írtam az elején, csak egyszer fog megtörténni, nem lesz baj vele.

Ha miközben már be van kapcsolva egy relé, jön még egy kérés a klienstől, akkor újra be fog íródni a változóba az aktuális idő, vagyis meghosszabbodik az idő a kikapcsolásig, a "bekapcsolva" változónak pedig úgyis "igaz" volt már az értéke, úgy is marad. Majd meglátjuk, hogy ez-e a kívánt viselkedés, vagy valami mást szeretnél.
(#) Panhard válasza meslac hozzászólására (») Szo, 12:18 /
 
Leegyszerüsítve így néz ki. Ha érkezik a GET kérés, akkor beírja az aktuális idő+10mp-et az ido_fel vagy ido_le változóba. Ez a rész csak egyszer fut le. Ezt lejjebb kiértékeli. Ha kisebb a millis() mint az ido_fel vagy ido_le változóban lévő érték, akkor mozog a redőny le vagy fel. Ha már valamelyik irányba mozog a redőny, akkor a kérésnél nem fogja indítani a másik irányt. Erre van a redony_fel és redony_le változó. Érdemes hardveresen is a reléknél reteszelni a két irányt.

  1. void loop(){
  2.  
  3.  
  4.         if (header.indexOf("GET /5/fel") >= 0 && !redony_le) {
  5.             ido_fel = millis() + 10000;
  6.         }
  7.        
  8.         if (header.indexOf("GET /5/le") >= 0 && !redony_fel) {
  9.             ido_le = millis() + 10000;
  10.         }
  11.        
  12.         if (header.indexOf("GET /5/stop") >= 0) {
  13.             ido_fel = millis();
  14.             ido_le = millis();
  15.         }
  16.        
  17.        
  18.         if(ido_fel>millis()){
  19.                 redony_fel = true;
  20.                 digitalWrite(output5, HIGH);
  21.             }else{
  22.                 redony_fel = false;
  23.                 digitalWrite(output5, LOW);
  24.         }
  25.        
  26.         if(ido_le>millis()){
  27.                 redony_le = true;
  28.                 digitalWrite(output6, HIGH);
  29.             }else{
  30.                 redony_le = false;
  31.                 digitalWrite(output6, LOW);
  32.         }
  33.                        
  34.                        
  35. }
(#) tki válasza Panhard hozzászólására (») Szo, 13:23 /
 
A reteszelést vagy akarja valaki, vagy nem - ettől eltekintve az if (ido_fel > millis()) {...} és az if (ido_le > millis()) {...} blokkok itt vég nélkül újra és újra lefutnak; írjuk a változókat, állítjuk a reléket, csak a változások pillanatát leszámítva ugyanazokra az értékekre. Ami jelen esetben nem probléma, de általában nem szeretjük, pl. mert ha bekerül az ágakba egy-egy debugkiírás, akkor már elég kellemetlen lesz.

Az állapotokat is az indexof... sorokban kellene bebillenteni, az a jó megközelítés, hiszen ott indul a változás. Amit persze az időbeállításon keresztül is át lehet adni és máshol, kívül regisztrálni, ahogy ebben a kódban, de az kevésbé tiszta, olvasható és bővíthető megoldás - ezt most nem dolgozom ki.
(#) Panhard válasza tki hozzászólására (») Szo, 13:35 /
 
Nincs azzal semmi gond, ha egy feltétel folyamatosan lefut. (a debugtól eltekintve, mert az ideiglenes) "Az állapotokat is az indexof... sorokban kellene bebillenteni" miért is?
Mutass egy működő példát, a szószaporítás helyett, de ne a ChatGPT-t másolgasd ide, mert azzal csak a fórumot terheled, de haszna nem sok van.
(#) tki válasza Panhard hozzászólására (») Szo, 13:48 /
 
: -)
(#) tki válasza Panhard hozzászólására (») Szo, 14:29 /
 
"a debugtól eltekintve, mert az ideiglenes"

A debug is nagyon fontos, nincs mitől eltekinteni, de ugyanúgy a ki-bekapcsolások logolására is szükség lehet egy kész, nem debugos firmware-ben, ami nagyon gyakori, és még kerülhet oda más kód is később, pl. egy bonyolultabb hardware-vezérlés, ami nem szeret sokszor lefutni, és akkor hirtelen újra kell írni az egész logikát... És még bőven vannak ilyen indokok. Ami pedig a kész kódokat illeti, ez egy tanulós topik, a tanításnak pedig vannak módszerei - türelem.
A hozzászólás módosítva: Szo, 14:33
(#) Panhard válasza tki hozzászólására (») Szo, 14:45 /
 
Parancsolj. Így már lehet debugolni minden műveletet, és a kimenetek is csak egyszer vannak írva.

  1. void loop(){
  2.  
  3.  
  4.         if (header.indexOf("GET /5/fel") >= 0 && !redony_le) {
  5.                 ido_fel = millis() + 10000;
  6.                 Serial.println("GET keres erkezett: fel");
  7.         }
  8.        
  9.         if (header.indexOf("GET /5/le") >= 0 && !redony_fel) {
  10.                 ido_le = millis() + 10000;
  11.                 Serial.println("GET keres erkezett: le");
  12.         }
  13.        
  14.         if (header.indexOf("GET /5/stop") >= 0) {
  15.                 ido_fel = millis();
  16.                 ido_le = millis();
  17.                 Serial.println("GET keres erkezett: stop");
  18.         }
  19.        
  20.        
  21.        
  22.         if(ido_fel>millis() && !redony_fel){
  23.                 redony_fel = true;
  24.                 digitalWrite(output5, HIGH);
  25.                 Serial.println("Redony start fel");
  26.         }
  27.        
  28.         if(ido_fel<millis() && redony_fel){
  29.                 redony_fel = false;
  30.                 digitalWrite(output5, LOW);
  31.                 Serial.println("Redony stop fel");
  32.         }
  33.        
  34.         if(ido_le>millis() && !redony_le){
  35.                 redony_le = true;
  36.                 digitalWrite(output5, HIGH);
  37.                 Serial.println("Redony start le");
  38.         }
  39.        
  40.         if(ido_le<millis() && redony_le){
  41.                 redony_le = false;
  42.                 digitalWrite(output5, LOW);
  43.                 Serial.println("Redony stop le");
  44.         }
  45.  
  46.        
  47. }
A hozzászólás módosítva: Szo, 14:48
(#) tki válasza Panhard hozzászólására (») Szo, 16:38 /
 
Még nem jó a megoldás, mert ha folyamatosan be van kapcsolva az eszköz, akkor minden 49,7. napon előfordulhat, hogy hibásan fog működni a logika. Meg tudod mondani, miért, és hogy hogyan lehet a legegyszerűbben kivédeni?
A hozzászólás módosítva: Szo, 16:39
(#) Panhard válasza tki hozzászólására (») Szo, 16:53 /
 
Ezt gondold át mégegyszer. Mi történik akkor, ha a millis már a vége előtt 4000ms-nál tart, és te pont akkor hozzáadsz 10000-ms-t és elmented az ido_fel változóba.
(#) Panhard válasza Panhard hozzászólására (») Szo, 17:08 /
 
Mivel a millis unsigned long és az ido_fel is unsigned long tipusú, ezért az ido_fel új értéke 6000 lesz, és a millis is 10000-el később 6000 lesz. Nem történik semmi, működni fog rendesen.
(#) tki válasza Panhard hozzászólására (») Szo, 18:15 /
 
Abba gondolj bele, hogy mi történik akkor, amikor az unsigned long legnagyobb értéke előtt jár a millis(), ami a maximumához 10000-nél közelebbi érték. Nem fog örökké töretlenül növekedni - egy 32-bites változóban elhelyezhető szám is véges, csak nagy.

Ha ilyenkor a millis() értékéhez tízezret hozzáadsz, attól a változód, aminek értéket adsz, már túlcsordul, vagyis a nullához elég közeli értéke lesz, így egy darabig, amíg a millis() is túl nem csordul, addig rosszul fog működni a kisebb-nagyobb relációkkal történő direkt összehasonlítás...

---
Itt egy példa erre byte-tal (unsigned char, 8 bit), a byte_millis() nevű, elméleti függvényünk is ilyen típusú értéket ad vissza, és 10 msec legyen az időtartam, mert ezekkel könnyebb számolni.

1. A byte_millis() éppen 250-nél jár, így az "ido_fel = byte_millis() + 10;" eredménye “(250 + 10) % 256”, vagyis 4 lesz. (Persze ebben az esetben modulo helyett 256 kivonásával is megkapjuk a négyet.) Tehát még egyszer: értékadáskor a byte_millis() 200, az ido_fel pedig 4 lesz.

2. Te a vizsgálatoknál, pl. itt: "if (ido_fel < byte_millis())" arra számítasz, hogy akkor telt el a kívánt idő, amikor a byte_millis() _nagyobbá_ válik az ido_fel-nél.

3. Márpedig a byte_millis() még 250 (251, 252...), az ido_fel viszont csak 4, vagyis azonnal lefut a blokk és kikapcsol a relé. Ez akkor áll helyre, amikor a byte_millis() is túlcsordul. Vagyis ebben a példában elég sűrűn fog fennforgás kialakulni. : -) Az unsigned long-nál sincs másképp, csak az alattomosabb, mert 49,7 naponként fordulhat ez egyáltalán elő (az adott, elég kicsi időzítésekkel). Hogy ebben a firmware-ben ez kicsi vagy nagy probléma, az más kérdés, de elvi hibát nem érdemes csinálni. Egy ismerősömnek kódot kellett módosíttatnia miatta az elmúlt években, mert aki írta, sem volt ezzel tisztában, és a folyamatosan bekapcsolt készülékeinél tényleg előjött ebből fakadó probléma. : -)

Már csak azért sem érdemes elhanyagolni, mert amikor a túlcsordulás szóba jön valamelyik oldalon, akkor egy egyszerű trükkel megoldható a kisebb-nagyobb viszony megőrzése: soha nem hasonlítunk össze direktben, hanem kivonást alkalmazunk - következő alkalommal leírom, hogyan.

Ja, és ez is példa rá, amit írtam, hogy nem idő alapján érdemes majd valahol állapotváltozókat beállítani, hanem állapotváltozókat illik beállítani ott, ahol a változás történik. A számlálóknak is van állapotuk, de azok csak másodlagosak, mert azokat könnyebb elbénázni (főleg, ha nincs fejben egy adott nyelv aritmetikájának minden részlete), a hibákat meg nehezebb felderíteni, ill. a kódot olvasni...

Még meg lehetett volna említeni azt is, hogy a vizsgálatokból kihagytad az egyenlőséget, valahol a kisebb-egyenlőt vagy a nagyobb-egyenlőt lehetett volna használni a sima relációjel helyett, de végül is itt az nem okozott gondot.
A hozzászólás módosítva: Szo, 18:21
(#) KoblogPerGyok válasza meslac hozzászólására (») Szo, 19:02 /
 
Szia!

Én ennél a problémánál inkább timer-eket használnék. Megfelelő ponton indít és a láb magas másik esetben meg leállít és láb alacsony. Túlcsordulási eseményre meg leállít ÉS a láb nullára.
Természetesen 10s nagy, nem tudom ennél a cpu-nál mi lehet és mi nem, de megpróbálnám úgy megadni az előosztókat satöbbi és az induláskor a timer értékét annyira állítanám be, hogy 10s-nél legyen a megszakítás kérelem.
(#) KoblogPerGyok válasza KoblogPerGyok hozzászólására (») Szo, 19:05 /
 
Vagy ehhez hasonlóan, ami elvileg nem foglalja annyira a cpu-t, mert az kell a wifi-hez:

  1. #include <Ticker.h>
  2.  
  3. Ticker timer;
  4.  
  5. void myFunction() {
  6.   Serial.println("10 másodperc eltelt");
  7. }
  8.  
  9. void setup() {
  10.   Serial.begin(115200);
  11.   timer.attach(10, myFunction);  // 10 másodpercenként fut
  12. }
  13.  
  14. void loop() {
  15. }
A hozzászólás módosítva: Szo, 19:06
(#) KoblogPerGyok válasza KoblogPerGyok hozzászólására (») Szo, 19:20 /
 
Közben megnéztem ilyesmi is lehet:

  1. #include <Ticker.h>
  2.  
  3. Ticker timer;
  4.  
  5. bool timerActive = false; // fut-e éppen?
  6.  
  7. void myFunction() {
  8.   Serial.println("10 másodperc eltelt");
  9. }
  10.  
  11. void setup() {
  12.   Serial.begin(115200);
  13.  
  14.   // Példának 3 GPIO bemenet
  15.   pinMode(0, INPUT_PULLUP); // Start feltétel
  16.   pinMode(2, INPUT_PULLUP); // Stop feltétel
  17.   pinMode(3, INPUT_PULLUP); // Reset feltétel (ha van)
  18. }
  19.  
  20. void loop() {
  21.  
  22.   // ----- Indítás feltételre -----
  23.   if (!digitalRead(0) && !timerActive) {   // gomb = LOW
  24.     timer.attach(10, myFunction);          // Indít
  25.     timerActive = true;
  26.     Serial.println("TIMER ELINDÍTVA");
  27.     delay(300);  // kontakt zaj ellen
  28.   }
  29.  
  30.   // ----- Leállítás -----
  31.   if (!digitalRead(2) && timerActive) {
  32.     timer.detach();                        // Leállít
  33.     timerActive = false;
  34.     Serial.println("TIMER LEÁLLÍTVA");
  35.     delay(300);
  36.   }
  37.  
  38.   // ----- Újraindítás (= nullázás) -----
  39.   if (!digitalRead(3)) {
  40.     timer.detach();                        // Leállít
  41.     timer.attach(10, myFunction);          // Újraindít (nullázza az időt)
  42.     timerActive = true;
  43.     Serial.println("TIMER RESET + INDÍTÁS");
  44.     delay(300);
  45.   }
  46. }


Azaz tetszőlegesen ki/be lehet kapcsolni eseményekhez kötve. A void myFunction() {}-ben lehetne a relék kikapcsolása.
A fő programban pedig a bekapcsolásuk és a ticker indítása.
Ez egy könnyítés csak, mert a ticker meg lett írva máshol, annyit tudok, hogy timer-t használ, és jelentősen tehermentesíti a cpu-t. Még a milis()-nél is jobban, ha minden igaz.
(#) mnyugger válasza mnyugger hozzászólására (») Szo, 19:22 /
 
sikerült
(#) proli007 válasza mnyugger hozzászólására (») Szo, 19:59 /
 
(#) Panhard válasza tki hozzászólására (») Szo, 21:20 / 1
 
Igazad van. Tényleg lehet gond belőle, bár nagyon kicsi rá az esély.

  1. void loop(){
  2.  
  3.  
  4.         if (header.indexOf("GET /5/fel") >= 0 && !redony_le) {
  5.                 redony_fel = true;
  6.                 startido = millis();
  7.                 digitalWrite(output5, HIGH);
  8.                 Serial.print("Redony start fel");
  9.         }
  10.        
  11.         if (header.indexOf("GET /5/le") >= 0 && !redony_fel) {
  12.                 redony_le = true;
  13.                 startido = millis();
  14.                 digitalWrite(output6, HIGH);
  15.                 Serial.print("Redony start le");
  16.         }
  17.        
  18.         if (header.indexOf("GET /5/stop") >= 0) {
  19.                 digitalWrite(output5, LOW);
  20.                 digitalWrite(output6, LOW);
  21.                 Serial.print("GET keres erkezett: stop");
  22.         }
  23.        
  24.        
  25.        
  26.         if(millis() - startido > 10000){
  27.                
  28.                 if(redony_fel){
  29.                         redony_fel = false;
  30.                         digitalWrite(output5, LOW);
  31.                         Serial.print("Redony stop fel");
  32.                 }
  33.                
  34.                 if(redony_le){
  35.                         redony_le = false;
  36.                         digitalWrite(output6, LOW);
  37.                         Serial.print("Redony stop le");
  38.                 }
  39.                
  40.         }
  41.        
  42.        
  43. }
(#) mnyugger válasza proli007 hozzászólására (») Vas, 2:08 /
 
A másik elterül.
(#) meslac válasza Panhard hozzászólására (») Vas, 20:54 /
 
Köszönöm a segítséget! A módosításodat bemásoltam, lefut, de még élesben nem próbáltam ki.
Következő: »»   867 / 867
Bejelentkezés

Belépés

Hirdetés
XDT.hu
Az oldalon sütiket használunk a helyes működéshez. Bővebb információt az adatvédelmi szabályzatban olvashatsz. Megértettem