Az előző részben láthattuk, hogy minden gombhoz tartozó infra kódot meg lehet fejteni. De azt nem tudom, hogy Neked milyen távvezérlőd van. Egyelőre ne akarjuk a kódokat megfejteni, egyszerűen csak érzékeljük, ha bármely gombot nyomnak.
Vegyük elő az előző programot!
const int LED=13;
const int SENSOR=0;
bool isOn;
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT_PULLUP);
}
void loop() {
if (digitalRead(SENSOR)==0) {
// lámpa be vagy ki
isOn=!isOn;
if (isOn) {
digitalWrite(LED,HIGH);
}
else {
digitalWrite(LED,LOW);
}
delay(500); // Kicsit nem figyelünk a többi apró jelre
}
}
Mi ebben a baj? A baj nem látszik, de benne van! A baj az, hogy a program tényleges logikája - azaz hogy be-ki kell kapcsolni a lámpát - és az egyéb trükkök (az, hogy kicsit várni kell) össze vannak keverve. Ráadásul, ha több, mint 500ms-ig nyomva tartod a távvezérlőt, a lámpa be-ki kapcsolgatni kezd, hiszen 500ms elteltével azt hiszi a program, hogy újabb gombnyomást kapott.
Vágjunk rendet! Válasszuk el ezt a gombnyomás problémát!
const int LED=13;
const int SENSOR=0;
bool isOn;
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT_PULLUP);
}
void loop() {
if (infraButton()) { // csinálunk egy rendes függvényt erre
// lámpa be vagy ki
isOn=!isOn;
if (isOn) {
digitalWrite(LED,HIGH);
}
else {
digitalWrite(LED,LOW);
}
while (infraButton()) {
// nem csinálunk semmit, amíg nyomják a gombot
}
// ok, most már elengedték a gombot,
// ha újra megnyomják, arra már reagálni kell
}
}
Szóval mi változott? Nem a bazseváló bemenetet akarjuk olvasni, hanem elképzeljük, milyen jó lenne egy infraButton() függvény, ami true-et ad vissza ha volt gombnyomás. A bazseválás innentől nem a mi problémánk, hanem az infraButton() függvényé.
De hogy nézhet ki ez az infraButton() függvény? Nos, infra jel akkor van, ha mondjuk 1 ms-ként ránézünk a bemenetre, és úgy találjuk, hogy 50ms alatt legalább pár alkalommal fogtunk valami jelet.
Vajon honnan vettem ezeket a számokat? Az előző lapon lemértük, hogy mi szokott történni egy infra cuccal, és úgy találtuk, hogy ha nyomva van a gomb, akkor hasracsapva 50ms alatt biztosan kell pár jelnek jönnie. Persze minden távvezérlő más, de mi is lazák vagyunk: általában elég lenne 20ms-ig figyelni, de mi 50ms-ig figyelünk. Általában 10-20 jel érkezik, de mi megelégszünk azzal, ha úgy 10-et kapunk.
Viszont egyetlen jellel nem elégszünk meg: előfordul, hogy a fénycsöves lámpák hirtelen felkapcsolására is ad egy jelet az infravevő. No, azt nem fogadjuk el, mert az csak 1 és nem 10 jel.
bool infraButton() {
int cnt=0;
for (int b=0;b<50;b++) {
if (digitalRead(SENSOR)==0) {
cnt++;
}
delay(1);
}
if (cnt>10) {
return true; // volt infra
}
else {
return false; // nem volt infra
}
}
Érthető a megoldás? Számolunk 50-ig a for ciklussal. Mivel a for ciklusban van egy delay(1) ez kb. 50 ms-ig fog tartani. Azért pont ennyi ideig, mert úgy saccoljuk, hogy 10-20ms hosszú egy infrakód, és utána van egy kis szünet, majd ismétlődik, így akkor is elkapunk valamit, ha épp a szünetben kezdünk figyelni.
Minden milliszekundumban megnézzük, hogy mit mond az infravevő. Ha volt jel, akkor növeljük a cnt értékét. A cnt változó egy számláló (angolul counter, röviden cnt), és ha egy pici jelet fogtunk a sok bazseválásból, akkor megnöveljük. A ++ műveleti jel azt jelenti, hogy valaminek az értékét növeljük meg 1-el.
A végén azt mondom, hogy saccra ha volt 10 jel, akkor volt gombnyomás, és true-t fogok visszaadni, máskülönben false-t. (A függvény logikai értéket, azaz true-false-t ad vissza - hiszen vagy van gombnyomás, vagy nincs.) A bazseválás probléma leküzdve.
Rakjuk ezt be a progiba! Ugye emlékszel, hogy bárhova, ahova számot lehet írni, írhatunk függyvényt is, vagy tetszőleges kifejezést, ami kiszámolva számot eredményez?
Az if és a while belsejébe pedig valójában olyan kifejezést lehet írni, aminek az eredménye logikai, azaz igaz (true) vagy hamis (false). Amikor azt írod, hogy if (valami==2) akkor a == művelettel összehasonlítod a valami értékét kettővel, és ennek az eredménye true vagy false.
A mi kis infraButton() metódusunk eredménye viszont eleve true vagy false. Ezért könnyen és olvashatóan használhatjuk:
const int LED=13;
const int SENSOR=0;
bool isOn;
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT_PULLUP);
}
bool infraButton() {
int cnt=0;
for (int b=0;b<50;b++) {
if (digitalRead(SENSOR)==0) {
cnt++;
}
delay(1);
}
if (cnt>10) {
return true; // volt infra
}
else {
return false; // nem volt infra
}
}
void loop() {
if (infraButton()) { // ha rendesen megnyomták
// lámpa be vagy ki
isOn=!isOn;
if (isOn) {
digitalWrite(LED,HIGH);
}
else {
digitalWrite(LED,LOW);
}
while (infraButton()) {
// nem csinálunk semmit, amíg nyomják a gombot
}
}
}
Sokkal kulturáltabb lenne, ha nem csak úgy bekapcsolna, hanem szépen növekedne a fényereje bekapcskor, és csökkenne kikapcsor. A fényerő finom változásához remek megoldás a for-ciklus, ami szépen számolgat. Hogy a program kicsit olvashatóbb legyen, ezért ezeket kiszedtem egy fadeUp() és egy fadeDown() függvénybe.
const int LED=13;
const int SENSOR=0;
bool isOn;
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT_PULLUP);
}
bool infraButton() {
int cnt=0;
for (int b=0;b<50;b++) {
if (digitalRead(SENSOR)==0) {
cnt++;
}
delay(1);
}
if (cnt>10) {
return true; // volt infra
}
else {
return false; // nem volt infra
}
}
void fadeUp() {
for (int b=0;b<=255;b++) {
analogWrite(LED,b);
delay(1);
}
}
void fadeDown() {
for (int b=255;b>=0;b--) {
analogWrite(LED,b);
delay(1);
}
}
void loop() {
if (infraButton()) { // ha rendesen megnyomták
// lámpa be vagy ki
isOn=!isOn;
if (isOn) {
// szépen felfelé
fadeUp();
}
else {
// szépen lefelé
fadeDown();
}
while (infraButton()); // addig várunk, amíg el nem engedik
}
}
Nos, rövidebb éppen nem lett, vagy igen? Igaziból rövidebb is lett, hiszen az infraButton() az legalább 10 sor. Igaz, hogy ezt kétszer használjuk a loop() belsejében, de ott már csak a nevét írjuk le, és nem kell 2x 10 sort belepötyögni.
Minden programban minden sort felcimkézhetünk két kategóriába:
Térjünk vissza egy pillanatra a legelső programunkra:
const int LED=13; // mit
const int SENSOR=0; // mit
bool isOn; // mit
void setup() {
pinMode(LED, OUTPUT); // hogyan
pinMode(SENSOR, INPUT_PULLUP); // hogyan
}
void loop() {
if (digitalRead(SENSOR)==0) { // mit (az if) és hogyan (digitalRead)
// lámpa be vagy ki
isOn=!isOn; // mit
if (isOn) { // mit
digitalWrite(LED,HIGH); // hogyan
}
else { // mit
digitalWrite(LED,LOW); // hogyan
}
delay(500); // hogyan - nem figyelünk a bazseválásra
}
}
A mit és a hogyan dolgok keverednek benne! De ha egy programrészben csak MIT dolgok vannak, az sokkal könnyebben érthető:
void loop() {
if (infraButton()) { // mit
// lámpa be vagy ki
isOn=!isOn; // mit
if (isOn) { // mit
// szépen felfelé
fadeUp(); // mit
}
else { // mit
// szépen lefelé
fadeDown(); // mit
}
while (infraButton()) { // mit
}
}
}
A hogyan és a mit szétválogatása nekünk segít, mert rövidebb, olvashatóbb, és könnyebben áttekinthető programunk lesz.
Lássunk egy különbséget!
Egy HOGYAN kód Egy MIT kód
---------------------------------------------------
for (int b=255;b>=0;b--) { fadeDown()
analogWrite(LED,b);
delay(1);
}
Ugye, Te is látod a különbséget? Ha valahol azt látod, hogy fadeDown() akkor tudod, hogy a fade azt jelenti, hogy fínom áttűnés, a down meg hogy lefelé, szóval valami fény itt el fog tűnni.
De ha valami közepére be van dobva egy for-ciklus érdekes számokkal meg analogWrite-okkal, nos, azon el kell gondolkodni, hogy tényleg mit is csinál!