Source

weatherstation / weatherstation.pde

The branch 'eeprom' does not exist.
#include <Wire.h>
#include <BMP085.h>
#include <DHT22.h>
#include <Time.h>
#include <SdFat.h>

#include <stdlib.h>
#include <avr/pgmspace.h>

#define uint  unsigned int
#define ulong unsigned long
#define MAX_LINE_SIZE 80

// SD chip select pin
const uint8_t chipSelect = 9;

// file system object
SdFat sd;

ulong lastCapture, lastEvent = 0;

BMP085 dps = BMP085(); //I2C: SCL on A5, SDA on A4

#define DHT22_PIN 5 //D5
DHT22 myDHT22(DHT22_PIN);

#define LDR_PIN 0 //A0

#define XBEE_SLEEP_PIN 4 //D4
#define BATTERY_VOLTAGE_PIN 2 //A2
#define SOLARPANEL_VOLTAGE_PIN 3 //A3
//===================================
//Weather Sensors
//===================================
#define PIN_ANEMOMETER 2     // D2
#define INT_ANEMOMETER  0
#define PIN_RAIN 3     // D3
#define INT_RAIN 1
#define PIN_VANE 1     // A1

volatile ulong numRevsAnemometer = 0; // Incremented in the interrupt
volatile ulong numClicksRain = 0;
volatile float WindSpeed = 0; //final result of calculation of windspeed
volatile float Precipitation = 0;
volatile int WindDir = 0;

// ADC readings:
#define NUMDIRS 8
prog_uint16_t adc[] PROGMEM = {26, 45, 77, 118, 161, 196, 220, 256};

// These directions match 1-for-1 with the values in adc, but
// will have to be adjusted as noted above. Modify 'dirOffset'
// to which direction is 'away' (it's West here).
prog_char string_0[] PROGMEM = "W";
prog_char string_1[] PROGMEM = "NW";
prog_char string_2[] PROGMEM = "N";
prog_char string_3[] PROGMEM = "SW";
prog_char string_4[] PROGMEM = "NE";
prog_char string_5[] PROGMEM = "S";
prog_char string_6[] PROGMEM = "SE";
prog_char string_7[] PROGMEM = "E";
PROGMEM const char *strVals[] = {
  string_0,
  string_1,
  string_2,
  string_3,
  string_4,
  string_5,
  string_6,
  string_7 };

byte dirOffset=0;
byte wd;

char sensorsReading[MAX_LINE_SIZE];

void setup() {
  Serial.begin(57600);
  setSyncProvider(requestSync);
  //Change to 3600 or sync once an hour
  setSyncInterval(300);
  
  pinMode(XBEE_SLEEP_PIN, OUTPUT);
  digitalWrite(XBEE_SLEEP_PIN, LOW);
  
  Wire.begin();
  delay(1000);
  
  //SPI_HALF_SPEED or SPI_FULL_SPEED
  if (!sd.init(SPI_FULL_SPEED, chipSelect)) sd.initErrorHalt();
  
  pinMode(PIN_ANEMOMETER, INPUT);
  digitalWrite(PIN_ANEMOMETER, HIGH);
  //these two lines basically set the internal pull-up resistor in pin2;
  attachInterrupt(INT_ANEMOMETER, countAnemometer, FALLING);
  
  pinMode(PIN_RAIN, INPUT);
  digitalWrite(PIN_RAIN, HIGH);
  attachInterrupt(INT_RAIN, countRain, FALLING);
  
  //dps.init();     // QFE (Field Elevation above ground level) is set to 0 meters.
  //dps.init(MODE_STANDARD, 101850, false);  // 101850Pa = 1018.50hPa, false = using Pa units
  //dps.init(MODE_ULTRA_HIGHRES, 3000, true); // 30 meters, true = using meter units
  dps.init(MODE_STANDARD, 80500, true); // 805 meters
  myDHT22.readData();
  myDHT22.getTemperatureC();
  myDHT22.getHumidity();
  
  lastEvent = millis();
}

void loop() {
  DHT22_ERROR_t errorCode;
  long bmp085Temperature = 0, bmp085Pressure = 0;
  int photocellReading = 0;
  float batteryVoltage, solarPanelVoltage = 0;
  char tempChar[10];
  
  processPacket();
  
  if(timeStatus() != timeNotSet) {
    //Wait 60 seconds to get
    if ((lastCapture == 0) || (millis() - lastCapture >= 60000UL)) {
      // Get Sensors Readings
      errorCode = myDHT22.readData();
      
      dps.getTemperature(&bmp085Temperature); 
      dps.getPressure(&bmp085Pressure);
      
      batteryVoltage = analogRead(BATTERY_VOLTAGE_PIN);
      batteryVoltage = (batteryVoltage * 0.0032) * 2;
      solarPanelVoltage = analogRead(SOLARPANEL_VOLTAGE_PIN);
      solarPanelVoltage = (solarPanelVoltage * 0.0032) * 2;
      callback(60);
      
      // Mount char message
      strcpy(sensorsReading, "RAW#");
      //timestamp
      sprintf(tempChar, "%ld#", now());
      strcat(sensorsReading, tempChar);
      //batteryVoltage
      dtostrf(batteryVoltage, 3, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //solarPanelVoltage
      dtostrf(solarPanelVoltage, 3, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //pressureTemperature
      dtostrf((float)(bmp085Temperature)/10, 4, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //pressure
      dtostrf((float)(bmp085Pressure)/100, 6, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //humidityTemperature
      dtostrf(myDHT22.getTemperatureC(), 4, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //humidity
      dtostrf(myDHT22.getHumidity(), 5, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //luminosity
      sprintf(tempChar, "%d#", analogRead(LDR_PIN));
      strcat(sensorsReading, tempChar);
      //wind direction
      strcat_P(sensorsReading, (char*)pgm_read_word(&(strVals[wd])));
      strcat(sensorsReading, "#");
      //wind speed
      dtostrf(WindSpeed, 5, 2, tempChar);
      strcat(sensorsReading, tempChar);
      strcat(sensorsReading, "#");
      //rain
      dtostrf(Precipitation, 5, 2, tempChar);
      strcat(sensorsReading, tempChar);
      //newline
      strcat(sensorsReading, "\n");
      
      logEvent(sensorsReading, "logfile.txt");
      
      lastCapture = millis();
      lastEvent = millis();
    } else if (millis() - lastCapture <= 55000UL) {
      if (millis() - lastEvent >= 100UL) {
        if (numLinesOnFileBuffer() >= 0) {
          sendLineFileBuffer(0);
        }
        lastEvent = millis();
      }
    }
  } else {
    Serial.println("ERROR#WAITING_FOR_SYNC_MESSAGE#1");
    delay(1000);
  }
}

void countAnemometer() {
   numRevsAnemometer++;
}

void countRain() {
  numClicksRain++;
}

void callback(byte seconds){
  /*the ISR for the timer interrupt, called every 5 seconds*/
  //check how many times the switch was closed on this period? this value is on numRevsAnemometer
  WindSpeed = (numRevsAnemometer/ seconds) * 2.4; //2.4 K/h for one switch closure per second
  numRevsAnemometer = 0;
  //Precipitation = 0.28*(numClicksRain/seconds);
  Precipitation = 0.28*numClicksRain;
  //0.2794 mm per contact closure; this step gives number of clicks per minute unit = mm per minute
  numClicksRain = 0;
  
  int val;
  byte x, reading;
  val = analogRead(PIN_VANE);
  val >>=2;                        // Shift to 255 range
  reading = val;

  // Look the reading up in directions table. Find the first value
  // that's >= to what we got.
  for (x=0; x<NUMDIRS; x++) {
     if (pgm_read_word_near(adc + x) >= reading)
        break;
  }
  wd = (x + dirOffset) % 8;   // Adjust for orientation
}

void processPacket() {
  // if time sync available from serial port, update time and return true
  while(Serial.available() >= 11 ){  // time message consists of a header and ten ascii digits
    char c = Serial.read();
    if (c == 'T') {     
      time_t pctime = 0;
      for (byte i=0; i < 11 -1; i++){
        c = Serial.read();
        if( c >= '0' && c <= '9'){
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
        }
      }
      setTime(pctime);   // Sync Arduino clock to the time received on the serial port
    } else if (c == 'R') {
      time_t timestamp = 0;
      for (byte i=0; i < 11 -1; i++) {
        c = Serial.read();
        if (c >= '0' && c <= '9') {
          timestamp = (10 * timestamp) + (c - '0') ; // convert digits to a number
        }
      }
      sentLine(timestamp);
    }
  }
}

time_t requestSync()
{
  Serial.println("ERROR#WAITING_FOR_SYNC_MESSAGE#2");
  return 0; // the time will be sent later in response to serial mesg
}

void logEvent(char *msg, char *filename) {
  // create or open a file for append
  ofstream sdlog(filename, ios::out | ios::app);

  // append a line to the file
  sdlog << msg;

  // check for errors
  if (!sdlog) sd.errorHalt_P(PSTR("append failed"));

  // file will be closed when sdlog goes out of scope
}

//number of lines to send on file buffer, zero based(one line = 0, empty=-1)
uint32_t numLinesOnFileBuffer() {
  uint32_t fileSize;
  SdFile myFile;
  
  if (!myFile.open("logfile.txt", O_READ)) {
    fileSize = 0;
  } else {
    fileSize = myFile.fileSize();
    myFile.close();
  }
  
  return fileSize;
}

void sendLineFileBuffer(int lineNumber) {
  int line_number = -1;
  
  ifstream sdin("logfile.txt");
  
  while (sdin.getline(sensorsReading, MAX_LINE_SIZE, '\n')) {
    if (lineNumber == ++line_number) {
      Serial.println(sensorsReading);
    }
  }
}

void sentLine(time_t timestamp) {
  char bufferTime[11];
  int line_number = -1;
  
  ifstream sdin("logfile.txt");
  
  while (sdin.getline(sensorsReading, MAX_LINE_SIZE, '\n')) {
    ++line_number;
    for (byte i=4; i<15; i++) {
      bufferTime[i-4] = sensorsReading[i];
    }
    if (timestamp == atol(bufferTime)) {
      sdin.close();
      logEvent(sensorsReading, "log_sent.txt");
      removeLine(line_number);
      break;
    }
  }
}

void removeLine(int lineNumber) {
  int line_number = -1;
  
  ifstream sdin("logfile.txt");
  ofstream sdlog("temp.txt", ios::out | ios::app);
  
  while (sdin.getline(sensorsReading, MAX_LINE_SIZE, '\n')) {
    if (lineNumber == ++line_number) {
      continue;
    }
    sdlog << sensorsReading << endl;
  }
  
  sdlog.close();
  sdin.close();
  
  sd.remove("logfile.txt");
  sd.rename("temp.txt", "logfile.txt");
}

void floatToBytes(float number, char *bytes) {
	float *numberPointer;
        byte i;
	unsigned char *byte = (unsigned char *) &number;

	for (i=0; i<sizeof(float); i++) {
                bytes[i] = *byte;
                byte++;
        }
}

float bytesToFloat(char *bytes) {
	float *aPointFloat;
	aPointFloat = (float *)bytes;
	return *aPointFloat;
}