Skip to content
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Development
  3. sleep & interrupts
  • Getting Started
  • Controller
  • Build
  • Hardware
  • Download/API
  • Forum
  • Store

sleep & interrupts

Scheduled Pinned Locked Moved Development
12 Posts 4 Posters 5.5k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    tomkxy
    wrote on last edited by
    #3

    No it returns the sleep. But this does not seem to be related to the interrupt triggered. But the interrupts are fired since the counts are correct.

    1 Reply Last reply
    0
    • T Offline
      T Offline
      tomkxy
      wrote on last edited by tomkxy
      #4

      So here is the sketch. It is rather larger, since it should drive my weather station.
      It is not yet tested. The relevant parts are the loop, the interrupt handler and the rainGaugeTripped(). Note: I have a RTC and thus I know the seconds elapsed even if the Arduino wakes up after a sleep.

      I have added to print statements after the sleep.
      My expectation would be that the two print statements are executed each time the interrupt was triggered. So an increase in the counter by one each time?

      However, the excerpt from the log shows differently. The first statement demonstrates that no triggers happened. Than the next shows 8 triggers, followed by 14 triggers.

      ignature in message: 013D1E22311216785F6A80C3FE988584C8A4CCB0C05DE1D2
      send: 27-27-0-0 s=255,c=3,t=0,pt=1,l=1,sg=1,st=ok:1
      rain gauge counter: 0
      remainingSleep: 60000
      rain gauge counter: 8
      remainingSleep: 60000
      rain gauge counter: 14
      remainingSleep: 45000
      send: 27-27-0-0 s=14,c=3,t=16,pt=0,l=0,sg=0,st=ok:
      
      /**
       * Weather station using the MySensor library
       *
       * Documentation: http://www.mysensors.org
       * Support Forum: http://forum.mysensors.org
       *
       *
       *******************************
       *
       * REVISION HISTORY
       * Version 1.0 - Thomas Krebs
       * 
       * DESCRIPTION
       * Pressure sensor example using BMP085 module  
       * http://www.mysensors.org/build/pressure
       *
       */
      
      #define MY_RADIO_NRF24
      #define MY_SIGNING_ATSHA204
      #define MY_SIGNING_ATSHA204_PIN A3
      #define MY_SIGNING_REQUEST_SIGNATURES
      #define MY_DEBUG
      #define MY_NODE_ID            27
      
      // Overwrite Radio Pins for Ceech Board
      #define MY_RF24_CE_PIN		  7 
      #define MY_RF24_CS_PIN		  8
      
      #include <SPI.h> 
      #include <MySensor.h>  
      #include <Wire.h>
      #include <Adafruit_BMP085.h>
      #include <Sodaq_SHT2x.h>
      //#include <HTU21D.h>
      
      #define VERSION           "1.0"
      #define SKETCH_NAME       "W-Station"
      
      #ifdef __DEBUG
      #define DEBUG_PRINT(x) Serial.print(x)
      #define DEBUG_PRINTLN(x) Serial.println(x)
      #else
      #define DEBUG_PRINT(x) 
      #define DEBUG_PRINTLN(x) 
      #endif
      
      // Overwrite Radio Pins for Ceech Board
      #define MY_RF24_CE_PIN		  7 
      #define MY_RF24_CS_PIN		  8
      
      #define LTC4067_CHRG_PIN	    A1		//analog input A1 on ATmega 328 is /CHRG signal from LTC4067
      #define BATTERY_VOLTAGE_PIN	    A0		//analog input A0 on ATmega328 is battery voltage ( /2)
      #define SOLAR_VOLTAGE_PIN	    A2		//analog input A2 is solar cell voltage (/ 2)
      #define SOLAR_CURRENT_PIN	    A6		//analog input A6 is input current ( I=V/Rclprog x 1000 )
      #define BATTERY_CHARGE_CURRENT_PIN  A7          //analog input A7 is battery charge current ( I=V/Rprog x 1000 )
      #define LTC4067_SUSPEND_PIN	    9		//digital output D9 - drive it high to put LTC4067 in SUSPEND mode
      
      #define RAIN_GAUGE_PIN              2           // Interrupt 0
      #define AENOMETER_PIN               3           // Interrupt 1
      #define WIND_DIRECTON_PIN           A3           
      
      
      const float VccMin        = 1.0*3.5;  // Minimum expected Vcc level, in Volts. Example for 1 rechargeable lithium-ion.
      const float VccMax        = 1.0*4.2;  // Maximum expected Vcc level, in Volts. 
      
      // Sensor IDs
      #define BARO_CHILD       0
      #define TEMP_CHILD       1
      #define HUM_CHILD        2
      #define BATT_CHILD       10
      #define SOLAR_CHILD      11
      #define WIND_DIR_CHILD   12
      #define WIND_SPEED_CHILD 13
      #define RAIN_GAUGE_CHILD 14
      
      //--------------------------------------------
      // Control transmit intervals
      #define SLEEP_TIME 60000  	//  do not change weather forecast needs a sample every 60 sec 
      
      // FORCE_TRANSMIT_INTERVAL, this number of times of wakeup, the sensor is forced to report all values to the controller
      #define FORCE_TRANSMIT_INTERVAL 30 
      
      // Number or re-send attempts
      #define RETRIES 3
      //--------------------------------------------
      // Variables for RTC, RTC is used to measure up the intervals for the Pressure Sensor
      #define DS3231_I2C_ADDRESS 0x68
      #define MY_BASE_TIME 2451547     // according to formula below Julian day for 1.1.2000 00.00.000 ; seems to be offset by 3 days but this does not matter here
      
      unsigned long lastMillis = 0;
      
      //--------------------------------------------
      
      const float ALTITUDE = 640; // <-- adapt this value to your own location's altitude.
      
      // 
      // If thresholds are exceeded transmission is being forced
      //
      #define HUMI_TRANSMIT_THRESHOLD     0.5
      #define TEMP_TRANSMIT_THRESHOLD     0.5
      #define VOLTAGE_TRANSMIT_THRESHOLD  0.2
      #define CURRENT_TRANSMIT_THRESHOLD  0.001
      #define PRESS_TRANSMIT_THRESHOLD    0.2 
      
      
      const char *weather[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
      enum FORECAST
      {
      	STABLE = 0,			// "Stable Weather Pattern"
      	SUNNY = 1,			// "Slowly rising Good Weather", "Clear/Sunny "
      	CLOUDY = 2,			// "Slowly falling L-Pressure ", "Cloudy/Rain "
      	UNSTABLE = 3,		        // "Quickly rising H-Press",     "Not Stable"
      	THUNDERSTORM = 4,	        // "Quickly falling L-Press",    "Thunderstorm"
      	UNKNOWN = 5			// "Unknown (More Time needed)
      };
      
      Adafruit_BMP085 bmp = Adafruit_BMP085();      // Digital Pressure Sensor 
      SHT2xClass SHT21;		              // Hum/Temp (SHT12)
      //HTU21D sht21;
      
      int measureCount = 0;
      
      float lastTemperature = -1;	
      float lastHumidity    = -1;
      float lastPressure    = -1;
      int   lastForecast    = -1;
      float lastBattVoltage = -1;
      float lastBattCurrent = -1;
      float lastSolarVoltage= -1;
      float lastSolarCurrent= -1;
      int   lastBattPct       = -1;
      int   lastWindDir     = -1;
      int   lastWindSpeed   = -1;
      int   lastRainGauge   = -1;
      float VccReference = 3.3 ;				// voltage reference for measurement, definitive init in setup
      
      // ----------------------------------------------------
      // for weather forecasting aglorithm
      #define LAST_SAMPLES_COUNT 5
      float lastPressureSamples[LAST_SAMPLES_COUNT];
      
      // this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
      // get kPa/h be dividing hPa by 10 
      #define CONVERSION_FACTOR (1.0/10.0)
      
      int minuteCount = 0;
      bool firstRound = true;
      // average value is used in forecast algorithm.
      float pressureAvg;
      // average after 2 hours is used as reference value for the next iteration.
      float pressureAvg2;
      
      float dP_dt;
      
      //-----------------------------------------------------
      
      unsigned int  windSpeedSamples = 0;     // number of sampling periods, required for avg calc
      unsigned int  windSpeedCounter = 0;     // number of events per active sample period (1s)
      unsigned long lastSample = 0;           // track when the last sample was taken
      
      //-----------------------------------------------------
      // counters for interrupt handlers; in order to avoid race conditions
      // one is active and one can be used to read out values. Access is
      // through pointer which is switched in an re-assignement assumed to be atomic
      volatile int rainGaugeCounter[2];    
      volatile float windSpeedAvg1m[2];
      volatile int windSpeedMax1m[2];
      volatile int *activeRainGaugeCounter = &rainGaugeCounter[0];
      volatile float *activeWindSpeedAvg1m = &windSpeedAvg1m[0];
      volatile int *activeWindSpeedMax1m = &windSpeedMax1m[0];
      
      
      MyMessage temperatureMsg(TEMP_CHILD, V_TEMP);
      MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
      MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
      MyMessage humidityMsg(HUM_CHILD, V_HUM);			// SHT hum (% rh)
      MyMessage batteryVoltageMsg(BATT_CHILD, V_VOLTAGE);		// Battery voltage (V)
      MyMessage batteryCurrentMsg(BATT_CHILD, V_CURRENT);		// Battery current (A)
      MyMessage solarVoltageMsg(SOLAR_CHILD, V_VOLTAGE);		// Solar voltage (V)
      MyMessage solarCurrentMsg(SOLAR_CHILD, V_CURRENT);		// Solar current (A)
      MyMessage windSpeedMsg(WIND_SPEED_CHILD, V_WIND);
      MyMessage windGustMsg(WIND_SPEED_CHILD, V_GUST);
      MyMessage windDirMsg(WIND_DIR_CHILD, V_DIRECTION);		// Wind Direction
      MyMessage rainGaugeMsg(RAIN_GAUGE_CHILD, V_RAIN);
      
      //----------------------------------------------------------------
      // Interrupt Handlers
      //----------------------------------------------------------------
      void rainGaugeTripped()
      { 
        (*activeRainGaugeCounter)++;
      }
      
      void aenometerTripped()
      {/*
          static unsigned long lastAenometerInterrupt = 0;
          // we sample over 1 sec interval and take a 2 min average over the 1 sec samples
           if (interruptTime - lastAenometerInterrupt > 5) {
             if (_millis() - lastSample > 1) {
               lastSample = _millis();
               // update average
               *activeWindSpeedAvg1m  = *activeWindSpeedAvg1m * (windSpeedSamples-1.0)/windSpeedSamples*1.0 + windSpeedCounter/windSpeedSamples;
               windSpeedSamples++;
               if (windSpeedCounter > *activeWindSpeedMax1m) {
                 *activeWindSpeedMax1m = windSpeedCounter;
               }
               windSpeedCounter = 0;  
             } 
             else {
               windSpeedCounter++;
             }
           }
           lastAenometerInterrupt = interruptTime;*/
      }
      
      
      void setup() 
      {	
          // use VCC (3.3V) reference
          analogReference(DEFAULT);						// default external reference = 3.3v for Ceech board
          VccReference = 3.323 ;						// measured Vcc input (on board LDO)
          pinMode(LTC4067_SUSPEND_PIN, OUTPUT);					// suspend of Lion charger set
          digitalWrite(LTC4067_SUSPEND_PIN,LOW);       			        //  active (non suspend) at start
          
          Wire.begin();								// init I2C
        
          // Send the sketch version information to the gateway and Controller
          sendSketchInfo(SKETCH_NAME, VERSION);
        
          if (!bmp.begin()) 
          {
            Serial.println("Could not find a valid BMP085 sensor, check wiring!");
            while (1) {}
          }
      
         // pinMode(AENOMETER_PIN, OUTPUT);
         // attachInterrupt (AENOMETER_PIN-2, aenometerTripped, CHANGE);
         // pinMode(AENOMETER_PIN, INPUT);
         // digitalWrite(AENOMETER_PIN,HIGH);  // Turn on the internal Pull Up Resis
      
          // Register sensors to gw (they will be created as child devices)
          // without the waits in between I get send fails
          present(BARO_CHILD, S_BARO);
          present(TEMP_CHILD, S_TEMP);
          present(HUM_CHILD, S_HUM);	// SHT humidity
          wait(500);
          present(BATT_CHILD, S_POWER);	// Battery parameters  
          wait(200);
          present(SOLAR_CHILD, S_POWER);	// Solar parameters
          wait(200);
          present(WIND_DIR_CHILD, S_WIND);
          wait(200);
          present(WIND_SPEED_CHILD, S_WIND);
          wait(200);
          present(RAIN_GAUGE_CHILD, S_RAIN);
      
          pinMode(RAIN_GAUGE_PIN, INPUT_PULLUP);
          attachInterrupt (RAIN_GAUGE_PIN-2,rainGaugeTripped, FALLING);     
      
          lastMillis = _millis(); 
      }
      
      void loop() 
      {
          unsigned long remainingSleep = SLEEP_TIME;
          measureCount++; 
          bool forceTransmit = false;
      
          if (_millis() - lastMillis < SLEEP_TIME) {
              // wake up was triggered by an interrupt
              remainingSleep = SLEEP_TIME - (_millis()-lastMillis);  
          }
          else {
             if (measureCount > FORCE_TRANSMIT_INTERVAL) {
                forceTransmit = true;
                measureCount = 0;
             }
             sendTempHum(forceTransmit); 
             sendBaro(forceTransmit);
             sendVoltage(forceTransmit);
             //sendWindSpeed();
             //sendWindDirection(forceTransmit);
             sendRainGauge(forceTransmit);
             lastMillis = _millis();
          }
          sleep(remainingSleep);
          Serial.print("rain gauge counter: "); Serial.println(*activeRainGaugeCounter);
          Serial.print("remainingSleep: "); Serial.println(remainingSleep);
      }
      
      
      //----------------------------------------------------------------
      // Temperature/Humidity
      //----------------------------------------------------------------
      void sendTempHum(bool force)
      {
        bool tx = force;
        
        // SHT2x sensor
        // Temperature and Humidity
        float humidity = SHT21.GetHumidity();
        //float humidity = sht21.readHumidity();
      
        // send to MySensor network / only if change is above threshold
        if (abs(lastHumidity - humidity) > HUMI_TRANSMIT_THRESHOLD) tx = true;
        
        if (tx) {
          lastHumidity = humidity;
          resend(humidityMsg.set(humidity, 2));  
       //   humMeasureCount = 0;
        }
        
        tx = force;
        
        float temperature = SHT21.GetTemperature();
        //float temperature = sht21.readTemperature();
        if (abs(lastTemperature - temperature) > TEMP_TRANSMIT_THRESHOLD) tx = true;
        
        if (tx) {
          lastTemperature = temperature;
          resend(temperatureMsg.set(temperature, 2)); 
      //    tempMeasureCount = 0;
        }
      	
        DEBUG_PRINT(F("SHT_ temp: "));
        DEBUG_PRINTLN(temperature);
        DEBUG_PRINT(F(" SHT_ hum: "));
        DEBUG_PRINTLN(humidity);
      }
      
      
      //----------------------------------------------------------------
      // Rain Gauge
      //----------------------------------------------------------------
      void sendRainGauge(bool force)
      {
        // switch pointer so that the current value can be read without race conditions
        byte ndx;
        if (*activeRainGaugeCounter > 0) {
          (activeRainGaugeCounter == &rainGaugeCounter[0]) ? ndx = 0 : ndx = 1;
          activeRainGaugeCounter = &rainGaugeCounter[(ndx + 1) % 2];
          if (!resend(rainGaugeMsg.set(rainGaugeCounter[ndx]))) {
            *activeRainGaugeCounter += rainGaugeCounter[ndx]; // we might loose an update due to a non atomic operation
                                                              // however this is better than loosing the whole count
          }    
        }
      }
      
      //----------------------------------------------------------------
      // WindSpeed
      //----------------------------------------------------------------
      void sendWindSpeed()
      {
        // switch pointer so that the current value can be read without race conditions
        byte ndx;
        (activeWindSpeedAvg1m == &windSpeedAvg1m[0]) ? ndx = 0 : ndx = 1;
        activeWindSpeedAvg1m = &windSpeedAvg1m[(ndx + 1) % 2];
        activeWindSpeedMax1m = &windSpeedMax1m[(ndx + 1) % 2];
        
        resend(windSpeedMsg.set(windSpeedAvg1m[ndx], 2));
        resend(windGustMsg.set(windSpeedMax1m[ndx]));
      }
      
      //----------------------------------------------------------------
      // Wind Direction
      //----------------------------------------------------------------
      //Takes an average of readings on a given pin
      //Returns the average
      int averageAnalogRead(int pinToRead)
      {
          const byte numberOfReadings = 8;
          unsigned int runningValue = 0;
          for(int x = 0 ; x < numberOfReadings ; x++)
            runningValue += analogRead(pinToRead);
          runningValue /= numberOfReadings;
          return(runningValue);
      }
      
      int getWindDirection()
      // read the wind direction sensor, return heading in degrees
      {
          unsigned int adc;
      
          adc = averageAnalogRead(WIND_DIRECTON_PIN); // get the current reading from the sensor
          // The following table is ADC readings for the wind direction sensor output, sorted from low to high.
          // Each threshold is the midpoint between adjacent headings. The output is degrees for that ADC reading.
          // Note that these are not in compass degree order! See Weather Meters datasheet for more information.
          if (adc < 380) return (113);
          if (adc < 393) return (68);
          if (adc < 414) return (90);
          if (adc < 456) return (158);
          if (adc < 508) return (135);
          if (adc < 551) return (203);
          if (adc < 615) return (180);
          if (adc < 680) return (23);
          if (adc < 746) return (45);
          if (adc < 801) return (248);
          if (adc < 833) return (225);  
          if (adc < 878) return (338);
          if (adc < 913) return (0);
          if (adc < 940) return (293);
          if (adc < 967) return (315);
          if (adc < 990) return (270);
          return (-1); // error, disconnected?
      }
      
      void sendWindDirection(bool force)
      {
          bool tx = force;
          int windDir = getWindDirection();
          if (lastWindDir != windDir) tx = true;
      
          if (tx) {
            resend(windDirMsg.set(windDir));
            lastWindDir = windDir;
          }
      }
      
      
      //----------------------------------------------------------------
      // Barometer, Forecast
      //----------------------------------------------------------------
      void sendBaro(bool force) {
          bool tx = force;
      
          float pressure = bmp.readSealevelPressure(ALTITUDE) / 100.0;
          int forecast = sample(pressure);
      
          DEBUG_PRINT(F("Pressure = "));
          DEBUG_PRINT(pressure);
          DEBUG_PRINTLN(F(" hPa"));
          DEBUG_PRINT(F("Forecast = "));
          DEBUG_PRINTLN(weather[forecast]);
      
          if ( (abs(pressure-lastPressure) > PRESS_TRANSMIT_THRESHOLD) || (forecast != lastForecast)) tx = true;
      
          if (tx) {
            resend(pressureMsg.set(pressure, 0));
            lastPressure = pressure;
            resend(forecastMsg.set(weather[forecast]));
            lastForecast = forecast;
          }
      }
      
      
      float getLastPressureSamplesAverage()
      {
      	float lastPressureSamplesAverage = 0;
      	for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
      	{
      	    lastPressureSamplesAverage += lastPressureSamples[i];
      	}
      	lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
      
      	return lastPressureSamplesAverage;
      }
      
      
      
      // Algorithm found here
      // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
      // Pressure in hPa -->  forecast done by calculating kPa/h
      int sample(float pressure)
      {
      	// Calculate the average of the last n minutes.
      	int index = minuteCount % LAST_SAMPLES_COUNT;
      	lastPressureSamples[index] = pressure;
      
      	minuteCount++;
      	if (minuteCount > 185)
      	{
      		minuteCount = 6;
      	}
      
      	if (minuteCount == 5)
      	{
      		pressureAvg = getLastPressureSamplesAverage();
      	}
      	else if (minuteCount == 35)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) // first time initial 3 hour
      		{
      			dP_dt = change * 2; // note this is for t = 0.5hour
      		}
      		else
      		{
      			dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
      		}
      	}
      	else if (minuteCount == 65)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) //first time initial 3 hour
      		{
      			dP_dt = change; //note this is for t = 1 hour
      		}
      		else
      		{
      			dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
      		}
      	}
      	else if (minuteCount == 95)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) // first time initial 3 hour
      		{
      			dP_dt = change / 1.5; // note this is for t = 1.5 hour
      		}
      		else
      		{
      			dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
      		}
      	}
      	else if (minuteCount == 125)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		pressureAvg2 = lastPressureAvg; // store for later use.
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) // first time initial 3 hour
      		{
      			dP_dt = change / 2; // note this is for t = 2 hour
      		}
      		else
      		{
      			dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
      		}
      	}
      	else if (minuteCount == 155)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) // first time initial 3 hour
      		{
      			dP_dt = change / 2.5; // note this is for t = 2.5 hour
      		}
      		else
      		{
      			dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
      		}
      	}
      	else if (minuteCount == 185)
      	{
      		float lastPressureAvg = getLastPressureSamplesAverage();
      		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
      		if (firstRound) // first time initial 3 hour
      		{
      			dP_dt = change / 3; // note this is for t = 3 hour
      		}
      		else
      		{
      			dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
      		}
      		pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
      		firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
      	}
      
      	int forecast = UNKNOWN;
      	if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
      	{
      		forecast = UNKNOWN;
      	}
      	else if (dP_dt < (-0.25))
      	{
      		forecast = THUNDERSTORM;
      	}
      	else if (dP_dt > 0.25)
      	{
      		forecast = UNSTABLE;
      	}
      	else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
      	{
      		forecast = CLOUDY;
      	}
      	else if ((dP_dt > 0.05) && (dP_dt < 0.25))
      	{
      		forecast = SUNNY;
      	}
      	else if ((dP_dt >(-0.05)) && (dP_dt < 0.05))
      	{
      		forecast = STABLE;
      	}
      	else
      	{
      		forecast = UNKNOWN;
      	}
      
      	// uncomment when debugging
      	DEBUG_PRINT(F("Forecast at minute "));
      	DEBUG_PRINT(minuteCount);
      	DEBUG_PRINT(F(" dP/dt = "));
      	DEBUG_PRINT(dP_dt);
      	DEBUG_PRINT(F("kPa/h --> "));
      	DEBUG_PRINTLN(weather[forecast]);
      
      	return forecast;
      }
      
      
      /********************************************
       *
       * Send message, resend on error
       *
       * Parameters
       * - msg : message to send
       * - repeats: number of repetitions
       *
       *******************************************/
      bool resend(MyMessage &msg)
      {
        int repeat = 1;
        int repeatdelay = 0;
        boolean sendOK = false;
      
        while ((sendOK == false) and (repeat < RETRIES)) {
          if (send(msg)) {
            sendOK = true;
          } else {
            sendOK = false;
            DEBUG_PRINTLN(F("Send ERROR "));
            repeatdelay += random(20,150);
          } 
          repeat++; 
          delay(repeatdelay);
        }
        return sendOK;
      }
      
      //----------------------------------------------------------------
      // Functions for RTC
      //----------------------------------------------------------------
      // Convert normal decimal numbers to binary coded decimal
      byte decToBcd(byte val)
      {
        return( (val/10*16) + (val%10) );
      }
      
      // Convert binary coded decimal to normal decimal numbers
      byte bcdToDec(byte val)
      {
        return( (val/16*10) + (val%16) );
      }
      
      /*
      void saveTime()
      {
        byte dayOfWeek, dayOfMonth, month, year;  // will be ignored
        // retrieve data from DS3231
        readDS3231time(&baseTime.second, &baseTime.minute, &baseTime.hour, &dayOfWeek, &dayOfMonth, &month,
        &year);  
      }
      */
      unsigned long _millis()
      {
        byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  // will be ignored
        // retrieve data from DS3231
        readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
        &year); 
        return (julianDay(dayOfMonth, month, year) + second*1000+minute*60*1000+hour*24*60*1000 ) - MY_BASE_TIME;
      }
      /*
      unsigned long elapsedTime() {
        byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  // will be ignored
        // retrieve data from DS3231
        readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
        &year); 
        long retVal=((abs(second-baseTime.second)) + (abs(minute-baseTime.minute)*60) + (abs(hour-baseTime.hour)*60*60));
        baseTime.second = second;
        baseTime.minute=minute;
        baseTime.hour=hour;
        long t=julianDay(1,1,00);
        return retVal;
      }*/
      
      unsigned long julianDay(byte dayOfMonth, byte month, byte year ) 
      {
        // Algorithm from http://www.hermetic.ch/cal_stud/jdn.htm
        return (1461*(2000+year+4800+(month-14)/12))/4+(367*(month-2-12*((month-14)/12)))/12-(3*((2000+year+4900+(month-14)/12)/100))/4+dayOfMonth-32075;
      }
      
      void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
      dayOfMonth, byte month, byte year)
      {
        // sets time and date data to DS3231
        Wire.beginTransmission(DS3231_I2C_ADDRESS);
        Wire.write(0); // set next input to start at the seconds register
        Wire.write(decToBcd(second)); // set seconds
        Wire.write(decToBcd(minute)); // set minutes
        Wire.write(decToBcd(hour)); // set hours
        Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
        Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
        Wire.write(decToBcd(month)); // set month
        Wire.write(decToBcd(year)); // set year (0 to 99)
        Wire.endTransmission();
      }
      
      
      void readDS3231time(byte *second,
      byte *minute,
      byte *hour,
      byte *dayOfWeek,
      byte *dayOfMonth,
      byte *month,
      byte *year)
      {
        Wire.beginTransmission(DS3231_I2C_ADDRESS);
        Wire.write(0); // set DS3231 register pointer to 00h
        Wire.endTransmission();
        Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
        // request seven bytes of data from DS3231 starting from register 00h
        *second = bcdToDec(Wire.read() & 0x7f);
        *minute = bcdToDec(Wire.read());
        *hour = bcdToDec(Wire.read() & 0x3f);
        *dayOfWeek = bcdToDec(Wire.read());
        *dayOfMonth = bcdToDec(Wire.read());
        *month = bcdToDec(Wire.read());
        *year = bcdToDec(Wire.read());
      }
      
      
      
      //----------------------------------------------------------------
      // Battery Functions
      //----------------------------------------------------------------
      void sendVoltage(bool force)
      // battery and charging values
      {
        bool tx = force;
        
        // get Battery Voltage & charge current
        float batteryVoltage = ((float)analogRead(BATTERY_VOLTAGE_PIN)* VccReference/1024) * 2;	// actual voltage is double
        DEBUG_PRINT(F("Batt: "));
        DEBUG_PRINT(batteryVoltage);
        DEBUG_PRINT(F("V ; "));
          
        float batteryChargeCurrent = ((float)analogRead(BATTERY_CHARGE_CURRENT_PIN) * VccReference/1024)/ 2.5 ; // current(A) = V/Rprog(kohm)
        DEBUG_PRINT(batteryChargeCurrent);
        DEBUG_PRINTLN(F("A "));
         
        // get Solar Voltage & charge current
        float solarVoltage = ((float)analogRead(SOLAR_VOLTAGE_PIN)/1024 * VccReference) * 2 ;		// actual voltage is double
        DEBUG_PRINT(F("Solar: "));
        DEBUG_PRINT(solarVoltage);
        DEBUG_PRINT(F("V ; "));
        // get Solar Current
        float solarCurrent = ((float)analogRead(SOLAR_CURRENT_PIN)/1024 * VccReference)/ 2.5;		// current(A) = V/Rclprog(kohm)
        DEBUG_PRINT(solarCurrent);
        DEBUG_PRINT(F(" A; charge: "));
        DEBUG_PRINTLN(digitalRead(LTC4067_CHRG_PIN)?F("No"):F("Yes"));
      	
        // send battery percentage for node
        int battPct = 1 ;
        if (batteryVoltage > VccMin){
          battPct = 100.0*(batteryVoltage - VccMin)/(VccMax - VccMin);
        }
        DEBUG_PRINT(F("BattPct: "));
        DEBUG_PRINT(battPct);
        DEBUG_PRINTLN(F("% "));
      
        if ( (abs(batteryVoltage - lastBattVoltage) > VOLTAGE_TRANSMIT_THRESHOLD) ||
             (abs(batteryChargeCurrent - lastBattCurrent) > CURRENT_TRANSMIT_THRESHOLD) ||
             (abs(solarVoltage - lastSolarVoltage ) > VOLTAGE_TRANSMIT_THRESHOLD) ||
             (abs(solarCurrent - lastSolarCurrent) > CURRENT_TRANSMIT_THRESHOLD) ||
             (abs(batteryVoltage - lastBattVoltage) > VOLTAGE_TRANSMIT_THRESHOLD)) tx = true;
      
        if (tx) {
          resend(batteryVoltageMsg.set(batteryVoltage, 3));  		// Send (V)
          resend(batteryCurrentMsg.set(batteryChargeCurrent, 6));    	// Send (Amps)
          resend(solarVoltageMsg.set(solarVoltage, 3)); 		// Send (V)
          resend(solarCurrentMsg.set(solarCurrent, 6));  		// Send (Amps)
          sendBatteryLevel(battPct);
          
          lastBattCurrent = batteryChargeCurrent;
          lastBattVoltage = batteryVoltage;
          lastSolarVoltage = solarVoltage;
          lastSolarCurrent = solarCurrent;
          lastBattVoltage = batteryVoltage;
        }
      }
      
      
      /* Ceech board specifics for reference:
      It provides power for the circuit and charges the backup single-cell lithium battery while greatly extends battery life. You can monitor the voltages and currents. It has suspend mode, which reduces current consumption to around 40μA. The power source is a small, 5V solar cell. Connections:
      
      analog input A1 on ATmega 328 is /CHRG signal from LTC4067 (indicates fully charged)
      analog input A0 on ATmega328 is battery voltage
      analog input A2 is solar cell voltage
      analog input A6 is input current ( I=V/Rclprog x 1000 )
      analog input A7 is battery charge current ( I=V/Rprog x 1000 )
      digital output D9 - drive it high to put LTC4067 in SUSPEND mode
      All the voltages on analog inputs can be read with an analogRead() command in the Arduino IDE sketch. Those on inputs A0 an A2 represent direct values of the measured voltages divided by 2. The voltages on analog inputs A6 and A7 can be translated to currents. For example:
      
      Let us say that the voltage on A7 is 0.12V. And the trimmer pot on PROG pin is set to 2.5kOhm. This means that the current into the battery equals to 0.12V/2500ohm x 1000, which is 48mA.
      
      voltmeters on both battery and solar cell connections
      They are connected to analog inputs A0 and A2 on the ATmega328p. The voltage dividers resistors are equal, so the measured voltage is double the shown voltage.
      
      NRF24l01+ socket
      with CE and CSN pins connected to digital pins 7 and 8 ( you use RF24 radio(7, 8); in Arduino code). There is a 4.7uF capacitor connected across Vin and GND of the port
      */```
      1 Reply Last reply
      0
      • sundberg84S Offline
        sundberg84S Offline
        sundberg84
        Hardware Contributor
        wrote on last edited by sundberg84
        #5

        Thats a big sketch :) Nice work.
        Atleast in the previous versions you cant use millis() and sleep together - please correct me someone if im wrong.
        But i think that millis() is not calculated/accumulated during sleep so when you try to calculate that it might be wrong.

        Controller: Proxmox VM - Home Assistant
        MySensors GW: Arduino Uno - W5100 Ethernet, Gw Shield Nrf24l01+ 2,4Ghz
        MySensors GW: Arduino Uno - Gw Shield RFM69, 433mhz
        RFLink GW - Arduino Mega + RFLink Shield, 433mhz

        T 1 Reply Last reply
        0
        • sundberg84S sundberg84

          Thats a big sketch :) Nice work.
          Atleast in the previous versions you cant use millis() and sleep together - please correct me someone if im wrong.
          But i think that millis() is not calculated/accumulated during sleep so when you try to calculate that it might be wrong.

          T Offline
          T Offline
          tomkxy
          wrote on last edited by
          #6

          @sundberg84 said:

          Thats a big sketch :) Nice work.
          Atleast in the previous versions you cant use millis() and sleep together - please correct me someone if im wrong.
          But i think that millis() is not calculated/accumulated during sleep so when you try to calculate that it might be wrong.

          Yes, it is quite a big sketch just stretching memory to its limits.
          I am aware that I cannot use millis() with sleep. For that reason, I am using a RTC. The function I am using to is _millis() which is contained in my sketch accessing the RTC. Although I have only second resolution it would be sufficient.

          The question I am having is: If the interrupt handler is called 14 times, shouldn't I see the sleep returning 14 times and thus 14 times the series of print statements, each time printing a counter increased by one???

          martinhjelmareM 1 Reply Last reply
          0
          • T tomkxy

            @sundberg84 said:

            Thats a big sketch :) Nice work.
            Atleast in the previous versions you cant use millis() and sleep together - please correct me someone if im wrong.
            But i think that millis() is not calculated/accumulated during sleep so when you try to calculate that it might be wrong.

            Yes, it is quite a big sketch just stretching memory to its limits.
            I am aware that I cannot use millis() with sleep. For that reason, I am using a RTC. The function I am using to is _millis() which is contained in my sketch accessing the RTC. Although I have only second resolution it would be sufficient.

            The question I am having is: If the interrupt handler is called 14 times, shouldn't I see the sleep returning 14 times and thus 14 times the series of print statements, each time printing a counter increased by one???

            martinhjelmareM Offline
            martinhjelmareM Offline
            martinhjelmare
            Plugin Developer
            wrote on last edited by
            #7

            @tomkxy

            Hi!

            Read the thread that @sundberg84 linked to, and especially my post here:
            http://forum.mysensors.org/topic/2436/node-with-interrupt-sleep-and-batteries/14

            The interrupt is probably triggering during the ISR, which creates the unexpected counts.

            T 1 Reply Last reply
            0
            • martinhjelmareM martinhjelmare

              @tomkxy

              Hi!

              Read the thread that @sundberg84 linked to, and especially my post here:
              http://forum.mysensors.org/topic/2436/node-with-interrupt-sleep-and-batteries/14

              The interrupt is probably triggering during the ISR, which creates the unexpected counts.

              T Offline
              T Offline
              tomkxy
              wrote on last edited by
              #8

              @martinhjelmare said:

              @tomkxy

              Hi!

              Read the thread that @sundberg84 linked to, and especially my post here:
              http://forum.mysensors.org/topic/2436/node-with-interrupt-sleep-and-batteries/14

              The interrupt is probably triggering during the ISR, which creates the unexpected counts.

              @martinhjelmare I think I do not explain myself properly.

              What I observe:

              • The counts the ISR produces are correct
              • From my observation sleep(60000) does not return on each ISR execution

              Basically, I have not a real problem with that. I just do not understand, it seems to be in contradiction with all the threads you guys have pointed me to.

              1 Reply Last reply
              0
              • mfalkviddM Offline
                mfalkviddM Offline
                mfalkvidd
                Mod
                wrote on last edited by
                #9

                I think the MySensors sleep code gets awakened, but that code goes back to sleep because you've asked it to sleep based on time only. I think you need to use sleep(int interrupt, int mode, unsigned long ms=0); to achieve the behavior you expect.

                1 Reply Last reply
                0
                • martinhjelmareM Offline
                  martinhjelmareM Offline
                  martinhjelmare
                  Plugin Developer
                  wrote on last edited by
                  #10

                  I suggest following both advice from @mfalkvidd and me, i.e. don't use the attachInterrupt function, but check the return value of the mysensors sleep function, and call your ISRs if the interrupt(s) was the cause of the wake-up. See the mysensors API for the sleep function with interrupts.

                  If you don't have a good reason to use the attachInterrupt function, it's easier and safer to rely on the mysensors sleep and interrupt implementation.

                  1 Reply Last reply
                  0
                  • T Offline
                    T Offline
                    tomkxy
                    wrote on last edited by
                    #11

                    @mfalkvidd @martinhjelmare : Thanks a lot for your answers.

                    How would you implement a rain gauge or anemometer measuring wind speed. Both devices are being triggered by "external" events, thus cannot be polled. I think there is no way around to count those events in interrupts.
                    If I enable interrupts only during sleep, I guess I will loose events or am I wrong on that?

                    1 Reply Last reply
                    0
                    • martinhjelmareM Offline
                      martinhjelmareM Offline
                      martinhjelmare
                      Plugin Developer
                      wrote on last edited by martinhjelmare
                      #12

                      I would first go the simpler route and try to both read the pins in the loop and have the interrupts during sleep and test that. If loosing events in the test, I would go for a more complex solution, i.e. not using mysensors sleep but coding that from "scratch" and using attachInterrupt.

                      Edit: On second thought, I think the second option would be to try and combine mysensors sleep function with interrupt and an attachedInterrupt with ISR. If that doesn't work, I would write a custom sleep function.

                      If using attachInterrupt, I would detachInterrupt first thing in the ISR, then when the pin state is back to normal attach the interrupt again.

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      8

                      Online

                      11.7k

                      Users

                      11.2k

                      Topics

                      113.0k

                      Posts


                      Copyright 2019 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • OpenHardware.io
                      • Categories
                      • Recent
                      • Tags
                      • Popular