Hystereesin ohjelmointi

Sovellus tai lähinnä hupihärpäke on seuraava:

Mittaa lämpötilaa, ja käynnistettäessä asetetaan perustaso kiinteästi, esim. 20 astetta.
Kun lämpötila laskee 5 astetta, alkaa ledi vilkku merkiksi siitä, että lämpötila on
pudonnut. Vilkkuminen lakkaa, kun tullaan takaisin liikkuma-alueelle.

Tähän tarttis jonkinlaisen hystereesin, ettei laite “värähtele”. Ensimmäisenä mieleen
tulee, että jos mittaan tilan muutoksen täysistä asteista (anturi mittaa 0.1 asteen
tarkkuudella)?

Onhan näitä varmaan tehty maailman sivu, onko ehdotuksia, ennenkuin koodaan
liian monimutkaisen logiikan?

Jos pelkäät värähtelyn tapahtuvan esimerkiksi juuri siinä ala tai ylärajapisteessä, niin pistä hälytykseen aikaseuranta. Tyyliin kun lämpötila on ollut yli 15s tai “vakiintumisajan” verran hälytyspisteen ylä/alapuolella alkaisi hälytys.
Toinen vaihtoehto voisi olla, että hälytys alkaisi heti esim. 14.9 asteen kohdalla ja loppuisi vasta, kun lämpötila on noussut 15.1 asteeseen.
Siihen lähinnä vaikuttaa kuinka nopeasti anturi reagoi lämpötilanmuutoksiin ja kuinka kauan lukemalla kestää vakiintua.

Testaamatta heittäisin tuollaisen if-lauseen häkkyrän. Voiko tuo tehdä jotain fiksua?

//arvo, raja, hyst leikitään vaikka asteen kymmenyksiä,
//Paluuarvo 1, mikäli ledin tulisi vilkkua (arvo alle rajan)
int hysteerinen_termari(int arvo, int raja, int hyst){
  static int edellinen_arvo = 0;
  if(edellinen_arvo){
    raja += hyst;
  }else{
    raja -= hyst;
  }
  edellinen_arvo = (arvo < raja);
  return edellinen_arvo;
}

EDIT: hupsista, mitä taas olin kirjoittanut koodiksi.

Kiitos taas Vuokko.

Itse asiassa tarvitsen sekä ylärajalämpötilan ylityksestä tiedon sekä alarajan
alituksesta myös. Copy-paste-modifioin koodiasi hieman ja tein kaksi funktiota.
Hyvin toimivat. Testikäytössä seuraava ohjelma ilmoittaa sallitulla jännitealueella
pysymisen (yläraja 4.0 volttia, alaraja 2.5 volttia, hystereesi 0.2 volttia). Voi olla
että tämän toiminnallisuuden saisi yhteen funktioon, jonka paluuarvot voisivat
olla vaikkapa

0 - ollaan alueella
1 - alueen ylitys
-1 - alueen alitus

// Code begins here
//
static int ala_edellinen_arvo = 0;
static int yli_edellinen_arvo = 0;

//arvo, raja, hyst leikitään vaikka asteen kymmenyksiä,
//Paluuarvo 1, mikäli ledin tulisi vilkkua (arvo alle rajan)
int ala_hysteerinen_termari(float arvo, float raja, float hyst){
if(ala_edellinen_arvo){
raja += hyst;
}else{
raja -= hyst;
}
ala_edellinen_arvo = (arvo < raja);
return ala_edellinen_arvo;
}

//arvo, raja, hyst leikitään vaikka asteen kymmenyksiä,
//Paluuarvo 1, mikäli ledin tulisi vilkkua (arvo yli rajan)
int yli_hysteerinen_termari(float arvo, float raja, float hyst){
if(yli_edellinen_arvo){
raja -= hyst;
}else{
raja += hyst;
}
yli_edellinen_arvo = (arvo > raja);
return yli_edellinen_arvo;
}

void setup() {
pinMode(A0, INPUT);
Serial.begin(9600);
}

void loop() {
int sensorValue = analogRead(A0);
float voltage = sensorValue * (5.0 / 1023.0);
Serial.println(voltage);
Serial.print("Alaraja: ");
Serial.println (ala_hysteerinen_termari(voltage, 2.5, 0.2));
Serial.print("Ylaraja: ");
Serial.println (yli_hysteerinen_termari(voltage, 4.0, 0.2));
delay(500);
}

Vielä jatkokysymys. Viittaan yllä olevaan koodinpätkään.

Mikä olisi järkevin tapa tehdä pääohjelmaan (loop) sellainen
ehtolause, joka toteutuu kun ala-ylä-normaali hysteriisin
tila vaihtuu suuntaan taikka toiseen? Nythän molemmat
funktiot palauttavat joko 1 tai 0. Molemmat voivat olla
samanaikaisesti arvossa 0 (silloin kun ollaan sallitulla
alueella), ja toinen voi olla kerrallaan arvossa 1.

Elikkäs peräkkäin kutsumalla saadaan

00 tai 01 tai 10

Näistä tarttis saada tilan muutos eli onkohan se hienosti
state transition ulos. Sorvasin allaolevan, toimii, mutta
ei näytä kovin elegantilta…

[code]
static int ala_edellinen_arvo = 0;
static int yli_edellinen_arvo = 0;
static int ala, yla;
float voltage = 3;

//arvo, raja, hyst leikitään vaikka asteen kymmenyksiä,
//Paluuarvo 1, mikäli ledin tulisi vilkkua (arvo alle rajan)
int ala_hysteerinen_termari(float arvo, float raja, float hyst){
if(ala_edellinen_arvo){
raja += hyst;
}else{
raja -= hyst;
}
ala_edellinen_arvo = (arvo < raja);
return ala_edellinen_arvo;
}

//arvo, raja, hyst leikitään vaikka asteen kymmenyksiä,
//Paluuarvo 1, mikäli ledin tulisi vilkkua (arvo yli rajan)
int yli_hysteerinen_termari(float arvo, float raja, float hyst){
if(yli_edellinen_arvo){
raja -= hyst;
}else{
raja += hyst;
}
yli_edellinen_arvo = (arvo > raja);
return yli_edellinen_arvo;
}

void setup() {
pinMode(A0, INPUT);
Serial.begin(9600);
ala = ala_hysteerinen_termari(voltage, 2.5, 0.1);
yla = yli_hysteerinen_termari(voltage, 4.0, 0.1);
}

// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
Serial.print("Alaraja: ");
Serial.println (ala_hysteerinen_termari(voltage, 2.5, 0.1));
Serial.print("Ylaraja: ");
Serial.println (yli_hysteerinen_termari(voltage, 4.0, 0.1));

if (ala_hysteerinen_termari(voltage, 2.5, 0.1) != ala || yli_hysteerinen_termari(voltage, 4.0, 0.1) != yla)
{
Serial.println(“Transitio”);
ala = ala_hysteerinen_termari(voltage, 2.5, 0.1);
yla = yli_hysteerinen_termari(voltage, 4.0, 0.1);
}
delay(1000);
}[/code]

Tuossa voisi tehdä jotain seuraavaa:

switch((yla_termari() << 1) | ala_termari()){
  case 0; // 00
    ei_muutosta();
    break;
  case 1: //01
    ala_raja();
    break;
  case 2: // 10
    yla_raja();
    break;
  case 3: //11
    jokin_sarky();
    break;
default:
    iso_ongelma();
}

Noin molemmat termarit suoritetaan ja lopputuloskin selviää.

Jaa, seuraava toimii. Pitääköhän defaultin yhteydessä olla aina “jotain”… tiedä häntä.

switch(yli_hysteerinen_termari(voltage, 4.0, 0.1) << 1 || ala_hysteerinen_termari(voltage, 2.5, 0.1)) { case 0: // 00 digitalWrite(10, HIGH); // ei_muutosta(); break; case 1: //01 //ala_raja(); break; case 2: // 10 //yla_raja(); break; case 3: //11 //jokin_sarky(); break; default: Serial.println("Houston, we have a problem."); //iso_ongelma(); }

Ilmoittaa “expected primary-expression before }-token”. Missäköhän bugi?

 switch(((yli_hysteerinen_termari(voltage, 4.0, 0.1) << 1) || ala_hysteerinen_termari(voltage, 2.5, 0.1)))
  {
  case 0: // 00
    digitalWrite(10, HIGH);
    //   ei_muutosta();
    break;
  case 1: //01
    //ala_raja();
    break;
  case 2: // 10
    //yla_raja();
    break;
  case 3: //11
    //jokin_sarky();
    break;
  default:
    //iso_ongelma();
  }

Switch casessa tosiaan pitää tapahtua jotain. olkoon vaikka pelkkä ;

casen allakin on pieniä juttuja muistettavana. Edellä mainittu virhe on yleensä tullut silloin kun on esitellyt muuttujan casessa.

case 42:
  int tmp = 42;
  return tmp;
  break;

ei toimi. Jos jotain tuollaista haluaa tehdä, tulee se tehdä esimerkiksi näin:

case 42:
  do{
     int tmp = 42;
     return tmp;
  }while(0);
  break;

Seuraava koodin pätkä loogisesta taista.

if(foo()||bar())jotain();

ajetaan näin: suoritetaan foo(), mikäli paluuarvo on tosi, suoritetaan jotain() Jos foo() ei ollut tosi, suoritetaan bar() ja mikäli se on tosi suoritetaan jotain() Ja jos molemmat palauttavat epätoden, jotain() ei suoriteta.

looginen AND on sitten toisin päin jotta jälkimmäinen suoritetaan, pitää edeltävän olla totta.

Bitti or asettaa bitin jos se on jommassa kummassa pystyssä. << siirsi toista yhden vasemmalle ja siitä tuli kakkonen.

Alla sama koodi kahdella eri tapaa kirjoitettuna. siirto << 1 on sama kuin kahdella kertominen.
(1 2 4 8 16 32 jne…)

//(foo() << 1) | bar()
int ret = foo();
ret = ret *2;
ret = bar() + ret;

Paljastetaan sitten vielä että termareiden return lauseesta. < palauttaa yhden tai nollan.

Tartuin tuohon loogiseen ORiin, koska jos tilanne on sellainen, että hystereesien alueet menevät päällekkäin, tuo täytyy testata ja miettiä tarkkaan.

Oisiko hetkeksi taas miettimistä C-kielestä :slight_smile:

Mulle on toi omassa koodissani oleva “Transitio” eli tilanmuutostieto suuntaan
tai toiseen tärkein, eli pitäydyn siinä ilman caseja. Caset eivät tuollaisenaan
anna transitiotietoa, niihin pitäisi lisätä tilanmuutoksen havainnointi ja muistiin
pano. Saman ajaa em. if-lause. Eli aina kun mennään normialueelta low-puolelle
tai päinvastoin, tai normialueelta high-puolelle tai päinvastoin, pitää saada
tieto. Kun transitio toteutuu, niin silloin lähtee gsm-viesti lämpötilasta, eli
“Erkki” näkee, että jotain on tapahtunut. Paluun normaaliksi näkee myös “Erkki”.
Enempää en tarvitse.