Szóval void(Carbo) meghívását mindenképpen a pot1 változásához kell kötni, ezt csk 2 méréssel tudom én megcsinálni (sajnos). Ha van ötlet erre, szívesen fogadom.
A egy 16x2-es lcd lévő általánops infók fognak megváltozni az adag11-re, ha változik az értéke.
Köszi az észrevételt és a javaslatot (lassan tanulgatom....)
Az ez akart lenni:
analogWrite(5, atlag1);
A két mérést hogy tudnám elkerülni? A változást az első lefutáskor tudom hasonlítani a setup-ban lévő átlaghoz, de utána nem, tehát ez nem jó. A második mérés eredménye számomra fontos - ez határozza az adagolandó mennyiséget (addig fog menni az adagoló motor sec-ben).
Örülök, hogy a múzsád lehettem és inspirálhattalak valami új megvalósítására :).
Az instrukciók alapján próbáltam javítgatni a hibákat. A serial monitor szerint azt csinálja, amit én szeretnék, de az még mindig 2x akkora mint a tiéd. Mi a titok?
A "logikai bukfenc"-nél úgy gondoltam, hogy az önmagát felülírandó változó csak a számítás végrehajtása után változik meg (egyébként számolt vele, de most lejavítottam). Eddig nem írtam vissza a ciklus végén kapott átlagértéket. Most beletettem azt is, gondolm a zavarszűrésnek ez lenne a lényege.
// Carbo const int pot1 = 5; // 1. potenciometer A5-re kotve int alap1; int alap11; int adag1; int adag11; int atlag1;
Két okból. Egyrészt nincs rotary encoderem (többek között azért sem, mert hosszú távon iszonyú megbízhatatlan). Másrészt, és ez a fő ok, hogy ez abszolút pozicionált, vagyis ahol hagyom, ott indul az újraindítás után. A rotary encoder, ha nincs mellette valamilyen optikai vagy fizikai érzékelő, ezt nem tudja, vagyis minden alkalommal vagy tárolni kell az utolsó állapotot, vagy mindig ugyanonnan kell kezdeni pl. a menüt.
Noszóval, a remek mai időre való tekintettel teszteltem egy kicsit (plusz az ötleted inspirált nekem is egy ötletet, ami később hasznos lesz, gondoltam, megírom).
Tapasztalások:
1. Az analóg bemenet zavarszűrése különösen a végállások környékén erősen javasolt (erre még nem találtam jó forrást, úgyhogy barkács van megoldva). Tehát érdemes minden mérést átlagolni.
2. A map(valtozo, minbe, maxbe, minki, maxki) nem egészen úgy szabdalja fel a dolgokat, ahogy az matematikailag indokolt lenne. Példával illusztrálva. Ha a bemenet 0,1,2,3,4,5,6,7, és a kimenetet 4 felé kell vágni (vagyis négy elemre van szükség), akkor úgy fog vagdosni, hgoy 0, 1, 2 --> 0; 3, 4 --> 1; 5, 6 --> 2, 7 --> 3. A logikus ugye az lenne, hogy 0, 1 --> 0; 2, 3 --> 1; 4, 5 --> 2; 6, 7 --> 3. A map() paraméterezésével észnél kell tehát lenni, plusz javasolt a kiváló constrain() használata.
3. A gennység az a dologban (mondjuk nem lenne ártalom egy informatikai, pontosabban egy programozó matematikai diploma), hogy a sok map() és a sokféle vizsgálat elég rendesen kiütheti fel és le is az értélmezési tartományokat, például az átlagolás eredménye lehet több, mint 1023 (számítási pontatlanság, illetve kerekítés), emiatt a map() is rossz eredményt ad. Ha a fenti (2. pont) logikátlanságot kompenzálni akarjuk, akkor a kelleténél többet kell gépészkedni. Vagy elfogadunk egy jelentős pontatlanságot különösen a szélsőértékeknél (a potméter két végpontja közelében).
4. Tapasztalati úton kb. 100 részre "osztható" egy potméter teljes tartománya anélkül, hogy a vizsgálatok túlságosan sok fals hívást eredményeznének és/vagy ezek ne röcögnének ide-oda az analóg bemenet jelentős zaja miatt (nekem elég randa a bemenet jelentős átlagolás nélkül -- ehhez kellene egy szkóp kideríteni, hogy mi lehet az oka).
Egy másik megjelenítőre kihegyezet library-vel felvértezve 3764 byte, library és a megjelenítésre vonatokzó kódrészek nélkül 2106 byte. Nem mondom, hogy tökéletesen elégedett vagyok, de nem rossz. És most jön, amit eleve csinálni akartam. :-D
-- atlag12 változónak nincs hozzárendelt értéke (van, csak random),
-- a többi számolgatás holt felesleges,
void.loop()
-- 0,01 másodpercen (egy századmásodpercen) belül kellene az egyébként is kétes értékű kuszob1 értékét meghaladó mértékben változtatni a potméterből jövő értéket ahhoz, hogy az if() vizsgálat teljesüljön (illetve ennek egy eleme),
-- az AND logikai kapocs az if() vizsgálatban felesleges, mert a másodikból következik, hogy az első vizsgálat igaz, tehát olyan eset nincs, hogy a második igaz, de az első nem, és olyan sem, hogy.
Alles zusammen. Igen, ezt, ezt SOKKAL egyszerűbbre kell megcsinálni. Véleményem szerint maga a vizsgálat van erősen túlbonyolítva, azon túl, hogy komoly logikai bukfenc is van benne (Setup szakasz, átlagolás).
Megírtam a sajátomat, UNO-ra fordítva 3562 byte lett. Illene belőle faragni, de reggel 9 óta dolgozom, ilyenkor annyira már nem megy... (U.i.: a tied is kb. ekkora.)
A teljes kód végül ilyen lett, ez a serial monitor szerint jó működik. Ilyenből nekem 3 db kéne. Nem lehet ezt egyszerűbben, rövidebben megvalósítani?
// Carbo const int pot1 = 5; // 1. potenciometer A5-re kotve int alap1; int adag1; // valtozo1 a mert ADC ertek tarolasara int adag11; int adag111; int atlag1; int maxatlag1; int minatlag1; int kuszob1;
Igen, jó a meglátás. A recegés csak ESÉLY, alapvetően az van, hogy a kevésbé jó minőségű potik végállása környékén előfordulhat, hogy egy-egy lépés között iszonyú nagy a szórás. Ezt lehet csökkenteni hardveresen és szoftveresen is. Szoftveresen egyszerűbb (az elején), gyakorlatilag meg szinte mindegy.
Viszont az sem mindegy, hogy az if() teljesülése esetén mi történik az atlag és az alap változókkal... :-)
void loop() { adag = analogRead(pot1); // poti újra lekérdezve }
Utána már szabadom számolgathatok a változóval. Az alap és az átlag segítségével ki tudom venni a minimális +- értékeket, amit majd tudok figyelni a loop-ban. Köszi, azt hiszem megértettem a leckét...
Egyébként 0-60 értékekre alakítva nem volt recegés a potin, de azért beteszem.
Kár hogy nem engedi szerkeszteni. Elkövettem ismét egy hibát, de asszem így lesz jó:
Szia!
Így működhet?
// Carbo const int pot1 = 5; // 1. potenciometer A5-re kotve int val1; // valtozo1 a mert ADC ertek tarolasara int val11; // valtozo1 a mert ADC ertek tarolasara int atlag1; byte pumpa1_rele = 38; // digit 38 az egyes pumpa
// Carbo const int pot1 = 5; // 1. potenciometer A5-re kotve int val1; // valtozo1 a mert ADC ertek tarolasara int val11; // valtozo1 a mert ADC ertek tarolasara int atlag1; byte pumpa1_rele = 38; // digit 38 az egyes pumpa
A loop()-on belül nem kell többszörös mintavétel. Én az elején szoktam egyet csinálni (void.Setup() szakaszban), mert az ottani esetleges hibás mintavétel (mondjuk egy töltődő kondenzátorból visszajövő fals jel miatt) gondot okozhat).
A többszörös mintavétel lényege, hogy van egy átmeneti változód (mondjuk átlag), és van egy mintavételed. A számítás nagyjából (persze eltérő súlyokkal lehet manipulálni): átlag = (átlag + mérés)/2. Ha ezt a számítást mondjuk 20-szor lefuttatod (minimális, már milliszekundumos várakozásokkal) az analóg bemenet értékét egész szépen ki lehet átlagolni. De ez csak egyszer kell.
A többi egy végtelenül egyszerű kód. Pont azért nem írom meg, mert az, de ha írsz valamit, szívesen segítek javítgatni.
Ha jól értem, akkor a gyakorlatban többszörös mintavétel kell, melynek átlagértéke ha meghaladja a megállapított küszöbérték felét, akkor értelmezi a változást, ha nem akkor nem.
Köszönöm a segítséget, keresem a megoldást....
A sorozatos mintavételhez van esetleg valami függvény, mely mentén elinulhatok?
Az "adag" változó tényleg nem kell, ezt már töröltem.
A c++ forrás ellenőrzése és a majdan futtatandó gépi kód között az IDE viszonylatában nincs jelentős korreláció. Alapvetően egy nagy kód futtatása sok idő, de ha az ellenőrzés azért lassú, mert a kód nagy, viszont annak a rendszeresen futó része kevés, akkor gyors lesz a futás (nem lesz sok „blind lag” benne).
Az, hogy a két mintavételt elszórod a loop()-on belül, alapvetően nem sokat oszt vagy szoroz, mert a sima 168/368 mikrokontrollerek is bőven elég gyorsak ahhoz, hogy egy nagyobb kódrész is milliszekundumok alatt lefusson, az pedig egy gomb nyomása vagy potméter tekerése tekintetében elenyésző idő. Én nem is erre helyezném a hangsúlyt, mert ennek a megoldása (pl. delay-jel) más gondokat nem old meg, más gondok megoldása viszont az ezzel való foglalatoskodást feleslegessé teszi.
Zavarszűrésen azt értem, hogy egy „sima” analóg bemenet jele még egy jó minőségű potméter esetében is elég jelentős ugrabugrát tud produkálni (lásd egy serial monitor jelsorát egy sima analogue.Read értékkel mondjuk 300 baudon). A map()-pel a dolog méregfogát részben kihúztad, kivéve azt a (reális) esetet, hogy a potméterből jövő analóg jel pont egy olyan határon receg, amely két szomszédos érték között ugrál a végső változó értéke tekintetében. Ennek pedig az lesz a következménye, hogy bár nem nyúlsz a potméterhez, az if() teljesülésekor folyamatosan le fog futni a hívott rutin, ami ugye idő, energia, ráadásul teljesen feleslegesen.
Én ezt úgy szoktam megcsinálni (nem biztos, hogy ez a legelegánsabb megoldás, mindenesetre a kimenete döbbenetesen stabil), hogy a setup() szakaszban csinálok egy referencia-mintavételt az analóg bementről. Ebből lesz a kiindulási érték (sokszor leátlagolom 10-50 mintavétellel, hogy az előbb említett ugrabugrát kiszűrjem. Innentől azt vizsgálom, hogy a referenciaértékhez képest +/- a raszter fele értékben* van-e eltérés a bemeneten. Ha nincs, mehet tovább, ha van, akkor jön egy külön rutin, ami megnézni, hogy
-- a változás lebutítva raszter szintre elegendő-e egy teljes ugráshoz vagy sem,
-- ha elegendő, akkor az új referenciaértéket visszaírni a referenciaváltozóba, elvégeztetni minden mást, amit akarunk, aztán mehet tovább a loop().
*A raszter nálad, ha jól emlékszem 0 és 60 között van, vagyis az 1024 érték 61 részre van osztva, ami kerekítve mondjuk 16,78 értékenként osztja az analóg bemenet 0-tól 1024-ig terjedő teljes tartományát. Vagyis a végső 0-tól 60-ig terjedő lehetséges értékek közül egyikből a másikba az analóg bemenetre érkező jel 16,78-cal való változásával lehet jutni. Ez elvileg egy raszter. A fele azért kell, hogy elkerüljük azt a nem kívánatos esetet, hogy egy raszterhatár felső értékéből egy teljes elem átugrásával (kerekítési hiba miatt) egy kettővel odébb lévő elembe ugorjunk. Nyilván fele értékkel ennek pont a fordítottja fog bekövetkezni, de ezt még mindig egyszerűbb orvosolni, mint azt elfogadni, hogy két érték közötti köztes értéket átugorjunk.
Nagyon egyszerűen fogalmazva itt a zavarszűrés annyi, hogy azt és csak azt figyelem, hogy a referenciaértékhez képest volt-e a bemeneten olyan mértékű változás, amely feltételezhetően elég ahhoz, hogy egyet lépjünk. Ha igen, akkor megnézzük, hogy ez valóban elég volt-e (csak itt kell a map(), a vizsgálat előtt felesleges), és ha valóban elég volt, akkor a lépésből következő minden következményt végrehajtatunk, új refernciát veszünk fel, és mehet tovább a loop().
Most ahogy néztem a kódot, feltűnt, hogy van benne egy jelentős, hm... Szóval ez így nem jó, mert a val1-et még a loop()-on belül szépen felülírod egy (ráadásul „idegen”) változóval, az adag1-gyel. Mondjuk ez csak töredék (ezért szokták kérni az összes fórumon, hogy TELJES forrást tegyen közzé a kérdező), tehát nem látom, hogy az adag1-gyel mi történik egyáltalán bárhol, de ez így, jelen állás szerint egyrészt biztosan nem fut le hibamentesen (már az IDE lefordítja és ki is küldi, de mivel az adag1-hez nincs hozzárendelve érték, az Arduino random fog rádobni valamit a flahsből, ami vagy jó, vagy nem), másrészt -- legalábbis elvben -- nagyjából soha nem lesz olyan helyzet, amikor az if() NEM teljesül, vagyis a val1 és a val11 azonos lesz.
Köszi a választ és a segítséget. Tehát a kiokoskodott állapotfigyelés elméletileg jó, ezért megveregetem a vállam. Az if egyébként tényleg a loop-on kívül van, de egy rendszeresen meghívott void-on belül (csak részleteket illesztettem ide, így az lemaradt).
Az eltelt időt értem, ha a két kiolvasást elszórom a programban az elég lehet vagy tegyek be millis-t vagy delay-t?
Ezt a "zavarszűrés" dolgot nem értem, mit kéne tennem?
A program ellenőrzés legalább 2 perc (vagy több), nagyon lassú, de hibátlan. Lehet ez gondot fog okozni majd futás közben is? Összességében elég sok változó a programban.
Az if vizsgálat le sem fut, mert a loop() {} szakaszon kívül van.
További gond, hogy:
-- nincs egyáltalán bemeneti "zavarszűrés",
-- nem telik el idő a két mérés között.
Ennek az lehet a következménye, hogy sokszor "magától" is elindul majd a hívás a kiirasCarbo() elemre, illetve pont ellenkezőleg (ha kellően hosszú a program további része), nem fogja érzékeli a változást.
Vannak még apró "stilisztikai" gondok, de emiatt az IDE úgyis szólni fog.
Kis segítség kéne. Kezdő vagyok, de lelkes. Akvárium vezérésén dolgozom, program lassan elkészül (600 sor), de ebben a részben bizonytalan vagyok. A célom az lenne, hogy egy poti értékének megváltozásakor írjak i adatot az lcd-re. A változó változásának figyelésével van bajom. Én így csináltam meg, de nem tudom, hogy jó-e, illetve működhetne-e egyátalán:
int pot1 = 0; // 1. potenciometer A0-re kotve int val1; // valtozo1 a mert ADC ertek tarolasara int val11; // valtozo1 a mert ADC ertek tarolasara byte adag1; void loop() { val1 = analogRead(pot1); // ertek beolvasasa val1 = map(val1, 0, 1023, 0, 60); //ertek atalakitasa 0-60 val1 = adag1; val11 = analogRead(pot1); // poti újra lekérdezve val11 = map(val11, 0, 1023, 0, 60); } if (val1 != val11); //Ha változik a poti1 állapota, akkor.... { kiirasCarbo(); delay(1000); return; } void kiirasCarbo() { lcd.setCursor(0,0); lcd.print("Carbo adag: "); lcd.print(adag1); }
Előre is köszönöm az építő jellegű hozzászólásokat!
Nem ilyen vészes a dolog. Az elgondolásod tökéletes: a PC-n kell futtatni egy programot, ami a megfelelő COM porton figyel (pl. COM3), és ha a mikrokontrollertől jön mondjuk egy "T" betű, akkor válaszol rá egy idő stringgel.
Lehet hogy hülyeség, de mi van, ha a PC-n fut egy program, ami az Arduino-ról érkező jelet figyeli, és bizonyos feltétel teljesülése esetén kiküldené a soror porton keresztül az időt?