mercoledì 12 dicembre 2012

ORTOLINO V.2.0 (Spero l'ultima)

Dunque ORTOLINO sta funzionando imperturbato da circa 1 mese, con sempre le stesse batterie. Però ho scoperto che si potevano introdurre alcune migliorie. Innanzitutto quando le temperature sono scese sotto lo zero mi sono accorto che le variabili di tipo "word" non hanno il segno, pertanto un numero negativo viene trasformato nel massimo valore permesso. Per ora ho risolto la cosa passando dai gradi centigradi ai gradi Kelvin, così sono sempre positivi. Poi ho scoperto che per valori alti di radiazione solare, la fotoresistenza ha dei valori così bassi che l'uscita digitale del partitore diventa trascurabile. In parole povere il valore trasmesso  è sempre lo stesso e non evidenzia variazioni di radiazione. Ho risolto il problema collegando la fotoresistenza in un altro modo e alimentandola con la tensione di 3.3 V fornita da un piedino di uscita digitale, solo al momento della misura, per non consumare energia inutilmente. Il nuovo schema è il seguente:

PxD vuol dire il piedino digitale della porta x del Jeenode.
PxA vuol dire il piedino analogico della porta x del Jeenode.

Come si può notare, anche l'alimentazione del sensore di temperatura TMP36 è presa dal piedino digitale, questo sempre per risparmiare energia. Il piedino infatti viene portato a +3.3 V solo poco prima della misura.




Il programma caricato sul Jeenode è qui sotto:


/*   Trasmette le variabili misurate da Ortolino
struttura del pacchetto di 10 byte trasmesso
1 - 2  RH  resistenza del soil moisture in centinaia di ohm
3 - 4  T  temperatura del suolo in decimi di grado Kelvin
5 - 6  L  luminosità in unità digit da 0 a 1023
7 - 8    libero
9 - 10   Vcc  in mV

Corretto per temperature negative, le temperature sono trasmesse
in °K, perchè una variabile word e' solo positiva

*/

#include <JeeLib.h>
#include <avr/sleep.h>
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
Port an1 (1);
Port an2 (2);
Port an3 (3);
Port an4 (4);
MilliTimer readoutTimer, aliveTimer;
//word runningAvg1;
word runningAvg2;
word runningAvg3;
word runningAvg4;
byte radioIsOn;
word pluto[5];
int  nodeID;
int ECHO = 1;  // Print debug
float R1 = 55.3;  // Kohm
float VCC = 3.3;
float Vconv = VCC/1023;
float V1=0;
float V2=0;
float RH1=0;
float RH2=0;
float RH=0;
float diff = 0;
float temper= 0;

//////////function for reading power supply/////////////////

long readVcc() { // SecretVoltmeter from TinkerIt
long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
 }
///////////////////////////////////////////////////////

void setup () {
    // initialize the serial port and the RF12 driver
    Serial.begin(57600);
    Serial.print("\n[pof66]");
    nodeID = rf12_config();
    if(ECHO == 1){
      delay(1000);
    Serial.println(nodeID);
    }
    // set up easy transmissions at 30 sec rate
    rf12_easyInit(30);  
//  setta come INPUT le 4 porte analogiche
    an1.mode2(INPUT);
    an2.mode2(INPUT);
    an3.mode2(INPUT);
    an4.mode2(INPUT);
//  setta come output le 4 porta digitale
    an1.mode(OUTPUT);
    an2.mode(OUTPUT);
    an3.mode(OUTPUT);
    an4.mode(OUTPUT);

//  no pull up resistors on the 3 analog ports

    an1.digiWrite2(0); //no pullup soil moisture
    an2.digiWrite2(0); // no pullup termometer TMP36GZ
    an3.digiWrite2(0);  // no pullup photoresistor
    an4.digiWrite2(1);
    
// prime the running average
 //   runningAvg1 = an1.anaRead();
    runningAvg2 = 0;
    runningAvg3 = 0;
    runningAvg4 = 0;
// start with the radio on
    radioIsOn = 1;
}

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

static void lowPower (byte mode) {
    // disable the ADC
    byte prrSave = PRR, adcsraSave = ADCSRA;
    ADCSRA &= ~ bit(ADEN);
    PRR &= ~ bit(PRADC);
    // go into power down mode
    set_sleep_mode(mode);
    sleep_mode();
    // re-enable the ADC
    PRR = prrSave;
    ADCSRA = adcsraSave;
}
////////////////////////////////////////////////////

static void loseSomeTime (word ms) {
    // only slow down for longer periods of time, as this is a bit inaccurate
    if (ms > 100) {
        word ticks = ms / 32 - 1;
        if (ticks > 127)    // careful about not overflowing as a signed byte
            ticks = 127;
        rf12_sleep(ticks);  // use the radio watchdog to bring us back to life
        lowPower(SLEEP_MODE_PWR_DOWN); // now we'll completely power down
        rf12_sleep(0);      // stop the radio watchdog again
        // adjust the milli ticks, since we've just missed lots of them
        extern volatile unsigned long timer0_millis;
        timer0_millis += 32U * ticks;
    }
}
//////////////////////////////////////////////////////////

void loop () {
    // switch to idle mode while waiting for the next event
    lowPower(SLEEP_MODE_IDLE);
    // keep the easy tranmission mechanism going
    if (radioIsOn && rf12_easyPoll() == 0) {
        rf12_sleep(0); // turn the radio off
        radioIsOn = 0;
    }
    // if we will wait for quite some time, go into total power down mode
    if (!radioIsOn)
        loseSomeTime(readoutTimer.remaining());
    // only take  sensors reading once a second
    // fa una lettura dei sensori ogni 30 secondi
    if (readoutTimer.poll(30000)) {
      
 // sensor power supply for measuring V1, i.e. P1-D on and P2-D off
      
        an1.digiWrite(1);
        an2.digiWrite(0);
        Sleepy::loseSomeTime(1000);
//        delay(1000);
        V1 =  Vconv*an1.anaRead();
        
 // sensor power supply for measuring V2, i.e. P1-D off and P2-D on
    
        an1.digiWrite(0);
        an2.digiWrite(1);
        Sleepy::loseSomeTime(1000);
//        delay(1000);
        V2 =  Vconv*an1.anaRead();

 //  compute RH

    diff = abs(3.3 - V2);
    if(diff < 0.001){
      RH2 = 5000.0;
    }
    else{
     RH2 = R1*V2/(3.3 - V2);
    }
    if(V1 < 0.001){
      RH1 = 5000.0;
    }
    else{
     RH1 = R1*(3.3 - V1)/V1;
    }
     RH = 5*(RH1+RH2);  // 0.1 kohm
//  read temperature
        an4.digiWrite(1);
        Sleepy::loseSomeTime(1000);
        runningAvg2 = an2.anaRead();
        an4.digiWrite(0);
//  read light
        an3.digiWrite(1);
        Sleepy::loseSomeTime(1000);
        runningAvg3 = an3.anaRead();
        an3.digiWrite(0);
//  read port 4  with anything
     runningAvg4 = an4.anaRead();  
//       
     pluto[0] =  word(RH);
     temper = 10*(273.2 + 25 + (100*Vconv*runningAvg2-75)); // in °K
     pluto[1] =  word(temper);  // gradi in decimi interi
     pluto[2] =  runningAvg3;  // unità digit per la luminosità
     pluto[3] =  runningAvg4;  
     pluto[4] =  word(readVcc()); // read power supply in mV 
// send measurement data, but only when it changes
      char sending = rf12_easySend(&pluto, sizeof pluto);

//   Print something //////////////////////////////////

   if(ECHO == 1){
       Serial.print(" Node ID: ");
       Serial.print(nodeID);
       Serial.print(" n.byte payload: ");
       Serial.println(sizeof pluto);
       Serial.print(" P1: ");
       Serial.print(pluto[0]);
       Serial.print(" P2: ");
       Serial.print(pluto[1]);
       Serial.print(" P3: ");
       Serial.print(pluto[2]);
       Serial.print(" P4: ");
       Serial.print(pluto[3]);
       Serial.print(" Vcc: ");
       Serial.println(pluto[4]);
   }
///////////////////////////////////////////////
   
        // force a "sign of life" packet out every 60 seconds
        if (aliveTimer.poll(60000))
            sending = rf12_easySend(0, 0); // always returns 1
        if (sending) {
            // make sure the radio is on again
            if (!radioIsOn)
                rf12_sleep(-1); // turn the radio back on
            radioIsOn = 1;
        }
    }
}



1 commento:

  1. Ciao Paolo, sono Franco di fablabtorino.org, grazie per l’offerta di collaborazione, che ricambio! Vedo che il tuo Ortolino affronta e risolve già molti aspetti pratici.
    Noi siamo impegnati su molti fronti, e non abbiamo ancora un vero e proprio gruppo che si occupi del monitoraggio ambientale. Il nostro progetto è in “standby”.
    Vedremo con l’arrivo della primavera, nei prossimi mesi
    Ho letto in particolare il post precedente sulla “carota bianca”, e su come alla fine tu abbia incontrato proprio i problemi che descrivevo, di incapacità del gesso di seguire fedelmente l’umidità del terreno, restando troppo umido. Il setup che usi ora, con gli elettrodi nudi a contatto del terreno, è purtroppo esposto alle variazioni di salinità, etc…
    Se sei interessato al sensore, ti consiglio questo video che mostra l’interno di un vero sensore Watermark http://www.youtube.com/watch?v=Lpw3SDOGveI
    Vedo che alterni due pins di Arduino per leggere gli elettrodi, e fai benissimo! Se sei interessato, per cominciare la collaborazione, ti posso far avere una delle nostre schedine dedicate, che rispetto al tuo approccio hanno il vantaggio del completo isolamento galvanico.

    Franco

    RispondiElimina