torstai 14. huhtikuuta 2016

Käytävävalo hämäräkytkimellä

Tässä ratkaisussa on ”käytävän kummassakin päässä” painike, joista voidaan lamppu sytyttää ja sammuttaa. Lisäksi valot voidaan sytyttää ainoastaan silloin kun niitä tarvitaan. On siis tarpeeksi hämärää. Lisäksi on huolehdittava siitä, että käytävän valoa ei osu liikaa valoa mittaavaan valovastukseen.


Kytkentä on aika paljon edellisen kaltainen. Nyt on tullut lisää toinen painike, toinen LED (valo) ja LDR, valovastus (Light Dependent Resistor). Sen vastusarvo muuttuu sen mukaan, miten paljon siihen osuu valoa. Mitä enemmän valoa, sitä pienempi vastus, ja sitä suurempi jännite syntyy analogiatuloon ja muunnoksen jälkeen lukema monitoriin. Ohjelmassa olikin ensin vain tuo valon mittausosa. Monitoroimalla analogiatuloa A0, pääsi näkemään lukemat. Enpä huomannut laittaa niitä tarkasti ylös, mutta peittämällä ja puristamalla valovastus, oli tulon lukema muutama kymmenen (esim. 50) ja valaisemalla taskulampulla lukema oli 1000 paikkeilla. Kun lukema on 512 (1023 / 2), on valovastuksen arvo 10 kilo-ohmia, sillä tuolloin 5V:in jännite jakaantuu kahtia. Eppabasicilla voi laskea noiden ääripäiden vastusarvot. Jokainen muunnoksen porras on 4,89mV (5000 / 1023) ja tästä saadaan laskettua valovastuksen yli vaikuttava jännite.


Dim Lukema = InputNumber ("Anna lukema: ")
Dim Jännite = Lukema * 0.00489
Print Jännite & (" V")
Dim Virta = Jännite / 10000
Print Virta & (" A")
Dim LDRjännite = 5 - Jännite
Print ("Jännite valovastuksen yli: ") & LDRjännite & (" V")
Dim Vastus = LDRjännite / Virta / 1000
Print Vastus & (" kilo Ohmia")

Kun lukemaksi (= analogiatulon lukema monitoroinnissa) syöttää 50, saadaan tulokseksi 194,49 kolo-ohmia ja syötettäessä 1000, saadaan vastusarvoksi 0,2449 kilo-ohmia, eli 244,9 ohmia. Tuossa ohjelmassa virtaa laskettaessa luku 10.000 on vastus R3, eli analogiatulosta maapotentiaaliin. Sen arvo on 10 kohmia. Jos lukemaksi syöttää 511, saadaan vastuksen arvoksi melko tarkasti 10 kohmia. Jos tämä on valaistustilanne, jakaantuu syöttävä 5V:ia puoliksi LDR:n ja kuoritusvastuksen kesken.
Tätä ohjelmaa tehdessä ja testatessa oli niin kirkas päivä, että valovastus piti peittää pienen potentiometrin varjoon. Tätä sovellusta voi tietysti kehittää myös monipuolisemmaksi. Porrasvaloissa on tyypillisesti ajastin, mikä sammuttaa valon tietyn ajan kuluttua automaattisesti. Se on joskus huono, jos valot ovat jo päällä, niin valo voi sammua kesken nousun (tai laskun). Pieni apu saattaisi olla, jos valoviive alkaisi joka painalluksella alusta. Uimahallin suihkut toimivat ajastimen kannalta juuri näin.

Tuossa esimerkkiohjelmassa, kellon määrittelyn osuudessa tulostus (Bol_Tulosta = false;) eli EPÄTOSI, mutta ohjelmaa tehdessä oli monitorointi todella tarpeen. Olenkin jättänyt ne testilauseet tulostus aliohjelmaan kommenteiksi. Se onkin hyvä ja helpottava tapa. Laittaa ne tulostukset kommenteiksi, joita ei tarvitse ja aktivoi kulloinkin tarvittavat. Kun katselee valmista toimivaa ohjelmaa, näyttää se yksinkertaiselta ja selvältä. Totta kai se on noin! On usein kuitenkin turhauttavaa etsiä niitä virheitä. Siinä on hyvänä apuna monen rivin kommenttipari ( /* .. */), millä voi poistaa osia ohjelmasta ja täten rajata ja etsiä sitä, mihin asti ohjelma kääntyy ja toimii halutulla tavalla. Ei tämäkään ohjelma kivutta syntynyt. Enkä odota sitä tulevaltakaan.
Tuo valoraja (400) on otettu ihan vaan hihasta, mutta tuntui toimivalta. Samoin hystereesi (80). Hystereesi on tärkeä, sillä jos vallitaan vain suurempi tai pienempi, niin toiminnasta tulee hyvin epästabiili. Määrityksissä ei ole mitään erikoista, määritellään sarjaliikenne monitorointia varten, kaksi lähtöä (OUTPUT) ja kaksi tuloa (INPUT). Analogiatuloa (A0) ei tarvitse erikseen asettaa tuloksi, sillä sen pinni määräytyy jo määrittelyissä
(int Int_AnaTulo = 0;). Analogiakäsittelyssä tulon lukemat rajoitetaan välille 100 .. 750
(constrain(Int_AnaTulo, 100, 750); Tämä käsky ei muuta lukemia miksikään, vaan ainostaan asettaa rajat. Aiemmin oli käytössä skaalauskäsky (map), millä koko tuloalue muutetaan toiseksi alueeksi. Esim: tulo 0 .. 1023 muutetaan lähtöalueelle 0 .. 255. MerkkiLEDiä ohjataan suoraan hämäräkytkimen sekvenssistä, mutta varsinaisen valon aliohjelmaa (Fun_Valo(boolean Tila) { .. }) kutsutaan kolmesta eri kohdasta pääohjelmaa.


Ohjelma 12 
/***************************************
 *  Ohjelma 12
 *  15.04.2016
 *  Käytävävalo hämäräkytkimellä
 **************************************/

// MÄÄRITTELYT:
// Kellon määrittely
unsigned long Ulo_MilliSek = 0;
unsigned long Ulo_UusiMilliSek = 0;
const long CoL_EroSekunti = 999;
int Int_Sekunti = 0;
boolean Bol_Tulosta = false; // Tulostus lopuksi pois

const int Con_AnaTulo = 0;
int Int_AnaTulo = 0;
const int Con_ValoRaja = 400;
const int Con_Hystereesi = 80;
const int Con_MerkkiLED = 3;
int Seq_Valo = 1; // Käynnistetään valaistuksen hallinta
const int Con_Valo = 4;
boolean Bol_Valo = false;
const int Con_Kytkin_1 = 2;
const int Con_Kytkin_1Viive = 3;
int Int_Kytkin_1Viive = 0;
boolean Bol_Kytkin_1Tila = false;
int Seq_Kytkin_1 = 0;
const int Con_Kytkin_2 = 5;
const int Con_Kytkin_2Viive = 7;
int Int_Kytkin_2Viive = 0;
boolean Bol_Kytkin_2Tila = false;
int Seq_Kytkin_2 = 0;

// ASETUKSET:
void setup(){                 
 Serial.begin(9600);         
 pinMode(Con_MerkkiLED, OUTPUT);
 pinMode(Con_Valo, OUTPUT);
 pinMode(Con_Kytkin_1, INPUT);
 pinMode(Con_Kytkin_2, INPUT);
}// Asetuksen loppu

// PÄÄLOOPPI Varsinainen suoritusosa. Jatkuva suoritus
void loop(){
// Sisäisen kellon käyttö
Ulo_MilliSek = millis();
  if(Ulo_MilliSek - Ulo_UusiMilliSek > CoL_EroSekunti){
    Ulo_UusiMilliSek = Ulo_MilliSek;
    Int_Sekunti++;
    if(Bol_Tulosta == true){Fun_Tulostus();}
  } // Kellon loppu

// Analogiatulon käsittely
Int_AnaTulo = analogRead(Con_AnaTulo);
Int_AnaTulo = constrain(Int_AnaTulo, 100, 750);

switch (Seq_Valo) {
  case 1: // tämä (1) on vakio, siis ei muuttuja
    if(Int_AnaTulo < Con_ValoRaja){
      Seq_Valo = 2;
    }
    break;
  case 2: // askel 2
    digitalWrite(Con_MerkkiLED, HIGH);
    Seq_Kytkin_1 = 1; // Kytkinsekvenssien käynnistys
    Seq_Kytkin_2 = 1;
    Seq_Valo = 3;
    break;
  case 3: // askel 3
    if(Int_AnaTulo > Con_ValoRaja + Con_Hystereesi){
      digitalWrite(Con_MerkkiLED, LOW);
      Seq_Kytkin_1 = 0; // Kytkinsekvenssien lopetus  
      Seq_Kytkin_2 = 0;
      Bol_Valo = false;
      Fun_Valo(Bol_Valo);
      Seq_Valo = 1; // paluu askeleeseen 1
    }
    break;
} // Valo-sekvenssin loppu

// Kytkimien käsittely
Bol_Kytkin_1Tila = digitalRead(Con_Kytkin_1);
Bol_Kytkin_2Tila = digitalRead(Con_Kytkin_2);

// KYTKIMEN 1 -tilan käsittely, nouseva reuna
switch (Seq_Kytkin_1) {
  case 1:
    if (Bol_Kytkin_1Tila == true){ // painiketta painettu
      Int_Kytkin_1Viive ++;
        if (Int_Kytkin_1Viive > Con_Kytkin_1Viive){
          Bol_Valo = !Bol_Valo;
          Fun_Valo(Bol_Valo);         
          Int_Kytkin_1Viive = 0;
          Seq_Kytkin_1 = 2;
        }
    }
    break;
  case 2:
    if (Bol_Kytkin_1Tila == false){
        Seq_Kytkin_1 = 1;
        }
    break;
} // Kytkin 1, sekvenssin loppu

// KYTKIMEN 2 -tilan käsittely, nouseva reuna
switch (Seq_Kytkin_2) {
  case 1:
    if (Bol_Kytkin_2Tila == true){
      Int_Kytkin_2Viive ++;
        if (Int_Kytkin_2Viive > Con_Kytkin_2Viive){
          Bol_Valo = !Bol_Valo;
          Fun_Valo(Bol_Valo);         
          Int_Kytkin_2Viive = 0;
          Seq_Kytkin_2 = 2;
        }
    }
    break;
  case 2:
    if (Bol_Kytkin_2Tila == false){
        Seq_Kytkin_2 = 1;
        }
    break;
} // Kytkin 2, sekvenssin loppu

delay(1);
} // Pääohjelma LOPPU

// FUNKTIOT
void Fun_Tulostus(){
//Serial.print("Teksti :");  Serial.println(Muuttuja);
//Serial.print("Analogiatulon raaka arvo :");  Serial.println(Int_AnaTulo);
//Serial.print("Kytkin 1 tila :");  Serial.println(Bol_Kytkin_1Tila);
//Serial.print("Kytkin 1 seq :");  Serial.println(Seq_Kytkin_1);
}

void Fun_Valo(boolean Tila){
  digitalWrite(Con_Valo, Tila);
} // Valo-aliohjeman loppu

Ei kommentteja:

Lähetä kommentti