Κυριακή 23 Αυγούστου 2020

My first Arduino Scale-Zygaria 10kg with LCD(i2c) Dht and 3 buttons Callibratable using 81 bytes EEPROM













const int PinButton2=2;//Action

const int PinButton3=3;//Roll option high priority button (on loop)

const int PinButton4=4;//digit [0-9] edit (slow) , position of digit in number roll

//const int PinButton5=5;//no need,,,primitive load program!


const int PinHx711DT=6;

const int PinHx711SCK=7;


const int PinDht=8;



//i2C lcd 16x2

//const int PinLcdScl;//my arduino has those pins....Scl and Sda for I2C 

//const int PinLcdSda;





///////////////////////////////////

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2);


////////////////////////////////////

#include "DHT.h"

#define DHTPIN PinDht

#define DHTTYPE DHT11 

DHT dht(DHTPIN, DHTTYPE);


void lcdPrintTempHum(){

    lcd.setCursor(0,1);

    String str=F("");

    float t = dht.readTemperature();  

    float h = dht.readHumidity(); 

    if(isnan(t) || isnan(h)){

        str+=F("Error Temp | Hum");

    }

    else{str+=t;str+=F(" C ");str+=h;str+=F("%");}

    lcd.print(str);

}


///////////////////////////////////////////////////////



#include "HX711.h"

HX711 scale;

//EEPROM ONE BYTE

byte LoadsUsing=0;// NON - CALLIBRATED ...just showing the HX711 readings

//EEPROM 10*4 =40 bytes

long LoadsSamplesAverage[10];//////{115315,181679,318885,525671,1002444};

//EEPROM 10*4 =40 bytes

long LoadsStandarts[10];     ///// {     0,   295,   885,  1775,   3776};

//total eeprom 81 bytes



////////////////////////////////////////////////////////////////////////////

#include <EEPROM.h>

#include <avr/pgmspace.h>

void updateEEPROM(){

    updateEEPROMforHX711Datautils();

}



void updateLongsToEEPROM(int address,long * SourceArray,int SourceLength){

    int i=0;

    long l;

    while(i<SourceLength){

      l=SourceArray[i];

      long b0=l;b0>>=24;

      long b1=l;b1>>=16;

      long b2=l;b2>>=8;

      long b3=l;

      EEPROM.update(address, (byte)b0);++address;

      EEPROM.update(address, (byte)b1);++address;

      EEPROM.update(address, (byte)b2);++address;

      EEPROM.update(address, (byte)b3);++address;

      ++i;

    }  

}

void readLongsFromEEPROM(int address,long * TargetArray,int TargetLength){

    int i=0;

    long l;

    while(i<TargetLength){

      byte b0=EEPROM.read(address);++address;

      byte b1=EEPROM.read(address);++address;

      byte b2=EEPROM.read(address);++address;

      byte b3=EEPROM.read(address);++address;

      l=b0;l<<=8;

      l+=b1;l<<=8;

      l+=b2;l<<=8;

      l+=b3;

      TargetArray[i]=l;

      ++i;

    }

}



void readEEPROM(){

  int addr=0;

  

  byte firstByteBetterBePass=EEPROM.read(addr);

  if(firstByteBetterBePass>=2 && firstByteBetterBePass<=10){

      LoadsUsing=firstByteBetterBePass;

      //onl("Just Read from EEPROM that i have callibrate this loadcell-chip for standart weights : ",LoadsUsing);

  }

  else{

    //ons("Seems that EEProm is not on the proper format !!!");

    return;

  }

  ++addr;

  

  readLongsFromEEPROM(addr,LoadsStandarts,LoadsUsing);

  addr+=40;

  

  readLongsFromEEPROM(addr,LoadsSamplesAverage,LoadsUsing);

  addr+=40;

  

  Serial.println(LoadsUsing);

  int i=0;

  while(i<LoadsUsing){

    Serial.print(LoadsStandarts[i]);

    Serial.print(F("->"));

    Serial.println(LoadsSamplesAverage[i]);

    ++i;

  }

  Serial.println();

  

  

  

}

void updateEEPROMforHX711Datautils(){

      Serial.println("Updating eep+ROM");

      EEPROM.update(0,LoadsUsing);

      updateLongsToEEPROM(1,LoadsStandarts,10);

      updateLongsToEEPROM(41,LoadsSamplesAverage,10);

}

//void flushToZeroEEPROM(){int i=0;while(i<100){EEPROM.update(i,0);++i;}}


/*

                    void initAndFlushToEEPROMMyStandarts(){//from my 10 kg beam load cell 

                        LoadsUsing=5;

                        LoadsStandarts[0]=0;LoadsSamplesAverage[0]=115315;    //  0 grams hx711 reads 115315

                        LoadsStandarts[1]=295;LoadsSamplesAverage[1]=181679;  //295 grams hx711 reads 181679

                        LoadsStandarts[2]=885;LoadsSamplesAverage[2]=318885;

                        LoadsStandarts[3]=1775;LoadsSamplesAverage[3]=525671;

                        LoadsStandarts[4]=3776;LoadsSamplesAverage[4]=1002444;

                        LoadsStandarts[5]=0;LoadsSamplesAverage[5]=0;          ////unused

                        LoadsStandarts[6]=0;LoadsSamplesAverage[6]=0;

                        LoadsStandarts[7]=0;LoadsSamplesAverage[7]=0;

                        LoadsStandarts[8]=0;LoadsSamplesAverage[8]=0;

                        LoadsStandarts[9]=0;LoadsSamplesAverage[9]=0;


                        updateEEPROMforHX711Datautils();

                        Serial.println(F("EEPROM being flushed from 81 bytes !"));

                    }

*/


void setup(){

  

    pinMode(PinButton2,INPUT_PULLUP);

    pinMode(PinButton3,INPUT_PULLUP);

    pinMode(PinButton4,INPUT_PULLUP);

    

    Serial.begin(9600);

    dht.begin();

    lcd.begin();

    scale.begin(PinHx711DT,PinHx711SCK);

    

    delay(323);

    

    //flushToZeroEEPROM();

    //initAndFlushToEEPROMMyStandarts();

    //ons("reading eeprom");

    readEEPROM();

    //ons("eeprom done reading");





    setupZygaria();

  

}








byte but2Curr=0;

byte but2Prev=0;

void but2Read(){but2Curr=digitalRead(PinButton2);}

bool but2Changed(){return but2Curr!=but2Prev;}

bool but2JustPressed(){return but2Curr<but2Prev;}

bool but2JustReleased(){return but2Curr>but2Prev;}

long last2Pressed;


byte but3Curr=0;

byte but3Prev=0;

void but3Read(){but3Curr=digitalRead(PinButton3);}

bool but3Changed(){return but3Curr!=but3Prev;}

bool but3JustPressed(){return but3Curr<but3Prev;}

bool but3JustReleased(){return but3Curr>but3Prev;}


byte but4Curr=0;

byte but4Prev=0;

void but4Read(){but4Curr=digitalRead(PinButton4);}

bool but4Changed(){return but4Curr!=but4Prev;}

bool but4JustPressed(){return but4Curr<but4Prev;}

bool but4JustReleased(){return but4Curr>but4Prev;}

long last4Pressed;


void readButtons(){but2Read();but3Read();but4Read();}

void storeButtons(){but2Prev=but2Curr;but3Prev=but3Curr;but4Prev=but4Curr;}


int delaying=123;

void loop(){

  loopZygaria();

}








long gramsDifTemp=0;

void setGramsDif(long newGramsDif){

  gramsDifTemp=newGramsDif;

  Serial.print("Grams Offset setted to: ");

  Serial.println(gramsDifTemp);  

}

bool errorHX711sensor=false;

long lastReading;

bool readWeightFromHx711(){

    //ons("reading");

    //if return true means is Ready...

    //if false not ready and timout and maybe broken pin connection etc ..or general problem with xh711

    if(scale.wait_ready_timeout(3000)){

        errorHX711sensor=false;//ons("ok");

        lastReading=scale.read();//now 'safelly' attempt to read..

        return true;

    }

    else{

        errorHX711sensor=true;//ons("error cant read from sensor");

        return false;

    }

}

void zygariaResetZeroGrams(){

    if(readWeightFromHx711()){

        setGramsDif(loadTranslateNewReading(lastReading));

    }

}




///showing only multipliers

int multiplier[]={1,10,100,1000};

int multiplierLength=4;

int multiplierCurr=0;

//grams is the relative from callibration output.

double units;//units= grams / multiplier[multiplierCurr];


String strWeight("");

void lcdPrintWeight(){

    strWeight=F("");

    if(readWeightFromHx711()){

      long grams=loadTranslateNewReading(lastReading);

      grams-=gramsDifTemp;

      units=grams;

      units/=multiplier[multiplierCurr];

      strWeight+=units;

      strWeight+=F("(");

      strWeight+=lastReading;

      strWeight+=F(")");

    }

    else{

      strWeight+=F("ErrorHX711sensor");

    }

    lcd.print(strWeight);

}


long loadTranslateNewReading(long newReading){

        float lastGrams=lastReading;

        //ser.print(F("Checking last Reading =  "));ser.println(lastReading);

        if(LoadsUsing<2){

            //ser.println(F("Not callibrated return 0,leaving new users to callibrate and understand hx711"));

        }

        else{

            byte i=LoadsUsing-1;

            long av=LoadsSamplesAverage[i];

            long avpreOrForZero;

            //ser.print(F("last average "));ser.println(av);

            if(lastReading<av){

                i=1;

                while(i<LoadsUsing){

                  av=LoadsSamplesAverage[i];

                  //ser.println(i);ser.print(F("average "));ser.println(av);

                  if(av<lastReading){++i;}

                  else{//ser.print(F("breaking "));ser.println(i);

                  break;}

                }

                if(i>1){

                    avpreOrForZero=LoadsSamplesAverage[i-1];

                    if(lastReading-avpreOrForZero<av-lastReading){

                        av=avpreOrForZero;

                        i--;

                        //ser.print(F("Much closer to previus"));

                    }

                    //ser.println(i);

                }

                //else{ser.println(F("Using first"));}

            }

            //else{ser.println(F("Using last"));}

            //ser.print(i);ser.print(F(" FINAL av "));ser.println(av);

            avpreOrForZero=LoadsSamplesAverage[0];

            lastGrams-=avpreOrForZero;

            lastGrams*=LoadsStandarts[i];//NOT IN PREVIUS LINE to NOT OVERFLOW...

            lastGrams/=(av-avpreOrForZero);

        }

  return lastGrams;

/*

  if(LoadsUsing<1){

    //ons("Not callibrated return 0,leaving new users to callibrate and understand hx711");

    return 0;

  }

  int i=0;

  while(i<LoadsUsing){if(newReading>LoadsSamplesAverage[i]){break;}++i;}

  

  if(i<2){i=1;}

  else if(i<LoadsUsing){

    long difLeft=newReading-LoadsSamplesAverage[i-1];

    long difRight=LoadsSamplesAverage[i]-newReading;

    if(difLeft<=difRight){--i;}

    else{}

  }

  else{i=LoadsUsing-1;}

  return ((newReading-LoadsSamplesAverage[0])*(LoadsStandarts[i]))/(LoadsSamplesAverage[i]-LoadsSamplesAverage[0]);

*/

}



void lcdPrintWeightTempHum(){

    lcd.clear();

    lcdPrintWeight();

    lcdPrintTempHum();

  

}





double sum=0;

double sum2=0;

double aver;

long reads;

long maxi=-2147483647;

long mini=2147483646;

int prii=0;

int priSamples;

void lcdPrintAverageProgress(){

      lcd.clear();lcd.print(prii);lcd.print(F("/"));lcd.print(priSamples);lcd.print(F(" "));lcd.print(sum2);

      lcd.setCursor(0,1);lcd.print(mini); lcd.print(F("-"));lcd.print(maxi);

  

}

void computeAverage(int Samples){

  if(Samples<1){Samples=1;}

  priSamples=Samples;

  computeAverage();

}

void computeAverage(){

      

      prii=0;

      sum=0;

      maxi=-2147483647;

      mini=2147483646;

      sum2=scale.read();

      lcdPrintAverageProgress();

      while(prii<priSamples){

          reads=scale.read();

          sum+=reads;

          sum2+=reads;sum2/=2.0;

          if(reads<mini){mini=reads;}

          if(reads>maxi){maxi=reads;}

          ++prii;

          if(prii%23==0){

            lcdPrintAverageProgress();

          }

      }

      aver=sum/priSamples;//now use average instead of sum2!

      lcdPrintAverageProgress();

      //onl("minmax (",mini," , ",maxi,")  dif = ",(maxi-mini));

      //ond("sum : ",sum, " / ",priSamples," = ",aver);

      //ond("sum2 : ",sum2);//i thought double will overflow!....anyway..

  

}


byte setupReadings=5;

bool takeZeroMeasurements=true;

void setupZygaria(){

    //ons("Activating Zygaria");

    

    lcd.print(F("welcome"));

    delay(456);

    lcdPrintWeightTempHum();

    if((!errorHX711sensor) && takeZeroMeasurements){//ons("zeroing");

      computeAverage(setupReadings);

      setGramsDif(loadTranslateNewReading(aver));

    }

    lcdPrintWeightTempHum();


    //set lastPressed to not take some action on begining

    last2Pressed=millis();


    

    //ons("done setup");

}







int optionCurr=0;

String options[]={String("1.Sensoring"),String("2.Units"),String("3.Callibration"),String("4.See Standarts")};

int optionsLength=4;

void loopZygaria(){

  readButtons();

  if(but3JustPressed()){

    rollOption();

  }

  else{

    delaying=123;

    runOption();

  }

  storeButtons();

  //oni("delaying : ",delaying);

  delay(delaying);

}


void rollOption(){

  optionCurr++;

  optionCurr%=optionsLength;

  resetOption();

}


void resetOption(){

    lcd.clear();

    lcd.print(options[optionCurr]);

    //oni("New Option ",optionCurr);

    delay(123);

    if(optionCurr==0){

        selectSensoring();

    }

    else if(optionCurr==1){

        selectUnits();

    }

    else if(optionCurr==2){

        selectCallibrating();

    }

    else if(optionCurr==3){

        selectShowCallibratedValues();

    }

}


void runOption(){

    if(optionCurr==0){

        runSensoring();

    }

    else if(optionCurr==1){

        runUnits();

    }

    else if(optionCurr==2){

        runCallibrating();

    }

    else if(optionCurr==3){

        runShowCallibratedValues();

    }

}



long resetZygariaOver=1000;

void selectSensoring(){

    lcdPrintWeightTempHum();

}

void runSensoring(){//ons("sensing");

    if(but2JustPressed()){

        lcdPrintWeightTempHum();

        last2Pressed=millis();

        //onS(strWeight);

    }

    else if(but2JustReleased()){

        if(millis()-last2Pressed>resetZygariaOver){

          if(!errorHX711sensor){

            //ons("reseting to zero grams");

            zygariaResetZeroGrams();

            lcdPrintWeightTempHum();

          }

        }

    }

}


void selectUnits(){

      lcd.setCursor(0,1);

      lcd.print(F("Mult: "));

      lcd.print(multiplier[multiplierCurr]);

}

void runUnits(){

    if(but2JustPressed()){

      ++multiplierCurr;

      multiplierCurr%=multiplierLength;

      lcd.clear();

      lcd.print(F("Mult: "));

      lcd.print(multiplier[multiplierCurr]);

      lcd.setCursor(0,1);

      lcdPrintWeight();

    }

}




int currShowing=0;

void selectShowCallibratedValues(){

    delay(1000);

    currShowing=0;

    lcd.clear();

    lcd.print(LoadsUsing);

    lcd.print(F(" StandartsUsing"));

    lcd.setCursor(0,1);

    lcd.print(F("HX711 to-> grams"));

    

}

void runShowCallibratedValues(){  

    if(but2JustPressed()){

        if(LoadsUsing > 0){//or >1 !!...

            lcd.clear();

            lcd.print(currShowing);

            lcd.print(F(")"));

            lcd.print(LoadsSamplesAverage[currShowing]);

            lcd.setCursor(0,1);

            lcd.print(F("->"));

            lcd.print(LoadsStandarts[currShowing]);

            lcd.print(F(" grams"));

            

            ++currShowing;

            currShowing%=LoadsUsing;

        }

    }

    

}



//editing variables for callibration

long tempAverages[10];

long tempStandarts[10];

byte tempRequestedStandarts=4;//except zero ,proposing 4 (0%,25%,50%,75%,99%)

byte tempRequestedStandartEditing=1;

int tempSampling=100;


byte tempGrams[6];//max = 999kg and 999 grams

byte tempGramsDigitEditing=5;

void resetTempGramsFromStandartEditing(){

  int val=tempStandarts[tempRequestedStandartEditing];

  int i=5;

  int ypoloipo;

  while(i>=0){

    ypoloipo=val%10;

    tempGrams[i]=ypoloipo;

    val/=10;

    --i;

  }

}



//0.unstarted,,,must reset in case to cancell what ever edit until now

//1.enter number of standart weights[1,9]

//2.select number of samples 100,500,1000  (from HX711 sensor) per standart weight

//3.empty and press to average zero

//4.for(each standarts)

///////put the standart in scale , enter weight ,press and wait to take the average 


int callibrStep=0;

void selectCallibrating(){

  if(errorHX711sensor){

      lcd.print(F("Callibration cant run"));

      lcd.setCursor(0,1);

      lcd.print(F("if sensor not working"));

      delay(2345);

      rollOption();//??!!!

      return;////!!!????

  }

    //callibrStep=0;//?????cancel?

    if(callibrStep==0){

        lcd.setCursor(0,1);

        lcd.print(F("press to start"));

    }

    else if(callibrStep==1){

        lcd.clear();

        lcd.print(F("#Standarts[1,9]"));

        lcd.setCursor(0,1);

        lcd.print(tempRequestedStandarts);

        lcd.setCursor(0,1);

        lcd.blink();

    }

    else if(callibrStep==2){

        lcd.clear();

        lcd.print(F("Select sampling"));

        lcd.setCursor(0,1);

        lcd.print(tempSampling);

    }

    else if(callibrStep==3){

        lcd.clear();

        lcd.print(F("RemoveAllWeights"));

        lcd.setCursor(0,1);

        lcd.print(F("Press And Wait"));

    }

    else if(callibrStep==4){

        lcd.clear();

        lcd.print(F("Put Standart#"));

        lcd.print(tempRequestedStandartEditing);

        lcd.setCursor(0,1);

        lcd.print(F("grams="));

        int i=0;while(i<6){lcd.print(tempGrams[i]);++i;}

        lcd.setCursor(6+tempGramsDigitEditing,1);

        lcd.blink();

    }

    

}

void runCallibrating(){

    if(callibrStep==0){run0CallibratingStep();}

    else if(callibrStep==1){run1CallibratingStep();}

    else if(callibrStep==2){run2CallibratingStep();}

    else if(callibrStep==3){run3CallibratingStep();}

    else if(callibrStep==4){run4CallibratingStep();}

    

}


void run0CallibratingStep(){

    if(but2JustPressed()){

        tempRequestedStandarts=4;//proposing zero+4 more for accuracy

        tempRequestedStandartEditing=1;//will start from this after taking average of zero(empty)

        tempGramsDigitEditing=5;

        int i=0;

        while(i<10){tempAverages[i]=0;tempStandarts[i]=0;++i;}

        resetTempGramsFromStandartEditing();

        callibrStep=1;

        selectCallibrating();

    }

}

void run1CallibratingStep(){

  if(but4JustPressed()){

    ++tempRequestedStandarts;

    if(tempRequestedStandarts>9){

      tempRequestedStandarts=1;

    }

    selectCallibrating();

  }

  else if(but2JustPressed()){

    //oni("Requested to edit standart weights: ",tempRequestedStandarts);

    callibrStep=2;

    selectCallibrating();

  }

  

}

void run2CallibratingStep(){

  if(but4JustPressed()){

    if(tempSampling==100){tempSampling=500;}

    else if(tempSampling==500){tempSampling=1000;}

    else {tempSampling=100;}

    selectCallibrating();

  }

  else if(but2JustPressed()){

    callibrStep=3;

    selectCallibrating();

  }

  

}

void run3CallibratingStep(){

  if(but2JustPressed()){//compute avrage on empty load

      computeAverage(tempSampling);

      tempRequestedStandartEditing=0;

      tempAverages[tempRequestedStandartEditing]=aver;

      tempStandarts[tempRequestedStandartEditing]=0;

      tempRequestedStandartEditing=1;//now ready to ask the weight of 1st standart

      resetTempGramsFromStandartEditing();


      delay(789);

      callibrStep=4;

      selectCallibrating();

  }

  

}

void run4CallibratingStep(){

  if(but4JustPressed()){

    last4Pressed=millis();

  }

  else if(but4JustReleased()){

    if(millis()-last4Pressed>333){

          tempGrams[tempGramsDigitEditing]++;

          if(tempGrams[tempGramsDigitEditing]>9){

            tempGrams[tempGramsDigitEditing]=0;

          }

          lcd.setCursor(6+tempGramsDigitEditing,1);

          lcd.print(tempGrams[tempGramsDigitEditing]);

          lcd.setCursor(6+tempGramsDigitEditing,1);

    }

    else{

      tempGramsDigitEditing++;

      if(tempGramsDigitEditing>5){

          tempGramsDigitEditing=0;

      }

      lcd.setCursor(6+tempGramsDigitEditing,1);

    }

  }

  else if(but2JustPressed()){

      last2Pressed=millis();

  }

  else if(but2JustReleased()){


        int i=0;long val=0;

        while(i<5){val+=tempGrams[i];val*=10;++i; }

        val+=tempGrams[i];

        if(val>0){

              int j=0;

              while(j<tempRequestedStandarts){

                if(j!=tempRequestedStandartEditing){

                  if(tempStandarts[j]>0){

                    if(tempStandarts[j]==val){

                      break;///already setted....not allows same

                    }

                  }

                }

                ++j;

              }

              if(j==tempRequestedStandarts){

                  //onl("procced with the editing weight as standart ",val);

                  tempStandarts[tempRequestedStandartEditing]=val;

                  computeAverage(tempSampling);

                  tempAverages[tempRequestedStandartEditing]=aver;

                  

                  if(tempRequestedStandartEditing < tempRequestedStandarts){

                      //onl("going to next ",tempRequestedStandartEditing," / ",tempRequestedStandarts);

                      tempRequestedStandartEditing++;

                      resetTempGramsFromStandartEditing();

                      tempGramsDigitEditing=5;//goes to grams position

                      selectCallibrating();

                  }

                  else{

                    //ons("callibration finished !!!");

                    //printTempArrays();

                    //ons("Must first sort standarts");

                    lcd.clear();

                    lcd.print(F("sorting and"));

                    lcd.setCursor(0,1);

                    lcd.print("updating eep+ROM");

                    sortInsertionTempArrays();

                    //printTempArrays();

                    //ons("storing to ram");

                    storeTempArraysToRam();

                    updateEEPROMforHX711Datautils();

                    delay(1234);

                    callibrStep=0;

                    rollOption();//leave to see the things

                  }

              }

              else{

                //oni("",val," Already declared at : ",j);

                lcd.clear();

                lcd.print(val);

                lcd.print(F(" Already"));

                lcd.setCursor(0,1);

                lcd.print(F("declared @ "));

                lcd.print(j);

                delay(1234);

                selectCallibrating();

                

              }

        }

        else{

          //ons("zero not acceptable");

          lcd.clear();

          lcd.print(F("Zero not accept"));

          delay(1234);

          selectCallibrating();

        }

      

  }


  

}

void storeTempArraysToRam(){

  int i=0,l=tempRequestedStandarts+1;

  LoadsUsing=l;

  while(i<l){

      LoadsStandarts[i]=tempStandarts[i];

      LoadsSamplesAverage[i]=tempAverages[i];

      //onl("i: ",i," st=",LoadsStandarts[i]," av=",LoadsSamplesAverage[i]);

      ++i;

  }

  while(i<10){

      LoadsStandarts[i]=0;

      LoadsSamplesAverage[i]=0;

      //onl("i: ",i," st=",LoadsStandarts[i]," av=",LoadsSamplesAverage[i]);

      ++i;

  }

  

}


void sortInsertionTempArrays(){

    int i,  j;  

    int n=tempRequestedStandarts+1;

    long key,followKey;

    for (i = 1; i < n; i++) {  

        key = tempStandarts[i];  

        followKey = tempAverages[i];  

        j = i - 1;  

        while (j >= 0 && tempStandarts[j] > key) {  

            tempStandarts[j+1] = tempStandarts[j];  

            tempAverages[j+1] = tempAverages[j];

            j = j - 1;  

        }  

        tempStandarts[j + 1] = key;  

        tempAverages[j+1] = followKey;

    }  

}

Δεν υπάρχουν σχόλια: