/*******************************************************************************
 * Program..: meteoServer.ino Monitor Office and outdoor bedroom A/C
 * Author...: Jan-marie Newton
 * Date.....: 2017-07-17
 * History..: 
 * Notes....: version 5.7
 *            senosors: 2 Dallas temp probes
 *                      1 BMP085 barometric pressure/temp/humidity
 *                      1 DHT11 temp/humidity
 *                      1 Windvane
 *                      1 Anemometer
 *                      1 Rain gauge
 *******************************************************************************/
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>
#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
#include <TimedAction.h>
/* allows us to set actions to perform on separate timed intervals
	http://playground.arduino.cc/Code/TimedAction
*/

#define WIND_DIR A3        // wind direction
#define RXTX_LED 4
#define DHT_OUTSIDE 5
#define ONE_WIRE_BUS 6     // what digital pin we're connected to

// Setup a oneWire instance to communicate with any OneWire devices  
// (not just the Maxim/Dallas temperature ICs) 
OneWire oneWire(ONE_WIRE_BUS); 
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
DHT dht(DHT_OUTSIDE, DHT22);
// Create the BME280 object barometric pressure/temp/humidity
Adafruit_BMP085 bmp;              // I2C using default 0x77 

// Initialize Variables
//------------------------------------------------------------------------
EthernetUDP Udp;         // An EthernetUDP instance to let us send and receive packets over UDP
IPAddress ip(192, 168, 1, 117);
byte mac[]             = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEB}; // Enter a MAC address and IP address
unsigned int localPort = 8888;      // local port to listen on

// DS18B20 temperature sensors
DeviceAddress Probe2 = { 0x28, 0xFF, 0x21, 0xA1, 0xA1, 0x16, 0x05, 0x7F };
DeviceAddress Probe1 = { 0x28, 0xFF, 0x2F, 0x6F, 0xA1, 0x16, 0x05, 0xF1 };

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming packet
char replyBuffer[48];                      // a string to send back
int packetSize;
unsigned int windspeed  = 0;
unsigned int wind_ohms  = 0;
int debug               = 0;
unsigned int flashy     = 0;
unsigned int windDirs[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned long pa        = 0;
unsigned int ticks      = 0;
unsigned int readings   = 0;
int bmpOK               = 0;
bool flag               = false;
char day[]              = "00";  // day of the month
float windDir           = 0;
float ib                = 212;
int windKnt             = 4;
int vaneVal;
int inx;
float oh;
float ih;
float ot;
float it;


//--------------------------------------------------------------------------
void getStatus() {
   //Serial.print("reading bmp180  ");
   if (bmpOK) {
     ib = bmp.readTemperature();
     pa = bmp.readPressure();
   }

   calcWindDir();
   // I2C with slave arduino nano
   Wire.requestFrom(16,2); // Request the transmitted two bytes from arduino nano
   if (Wire.available() == 2) {
      int newticks = Wire.read();    // Read rain ticks
      windspeed    = Wire.read();    // Read wind speed
      ticks       += newticks;
      readings++;
   }

   //Serial.print("reading DHT Sensors  ");
   float tmp;             // Serial.print("reading DHT11  ");
   tmp  = dht.readHumidity();
   if (tmp >= 0 && tmp <= 100)  // integrity check
      oh = tmp;

   tmp  = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit = true)
   if (tmp >= -40 && tmp <= 175)  // integrity check
      ih = tmp;

   // Serial.println("reading DS18b20");   
   sensors.requestTemperatures();
   it = sensors.getTempF(Probe1); //sensors.getTempFByIndex(0); // read inside Dry Bulb Temperature
   ot = sensors.getTempF(Probe2); //sensors.getTempFByIndex(1); // read outside Wet Bulb Temperature

   if (debug) {
      Serial.print("InSide: ");
      Serial.print(it,2);       // inside temperature
      Serial.print("  InBox: ");
      Serial.print(ih,1);       // inside control box temperature
      Serial.print("  InBox: ");
      Serial.print(ib,1);       // inside control box temperature
      Serial.print("  Outside: ");
      Serial.print(ot);
      Serial.print("  OutHumid: ");
      Serial.print(round(oh));
      Serial.print("  Baro: ");
      Serial.print(pa);
      Serial.print("  wind: ");
      Serial.print(windspeed);
      Serial.print("  dir: ");
      Serial.print(windDir);
      Serial.print("  rain: ");
      Serial.print(ticks);
      Serial.print("  readings: ");
      Serial.println(readings);
   }
   String rec  = String(round(it * 10)) + "\t"; // inside temperature to 1/10 of degree
   rec += round(ih * 10); rec += "\t";   // inside sensor box temperature
   rec += round(ot * 10); rec += "\t";   // outside temperature to 1/10 of degree
   rec += round(oh);      rec += "\t";   // outside humidity
   rec += round(windspeed * 10); rec += "\t";   // windspeed
   rec += round(ticks);   rec += "\t";   // rainfall * 100 
   rec += round(ib * 10); rec += "\t";   // inside sensor box temperature
   rec += pa;             rec += "\t";   // barometric pressure
   rec += round(windDir * 10); rec += "\t";     // Mode wind direction
   rec += readings;                      // # of readings taken from the sub-processor (wind,rain)

   int str_len = rec.length() + 1; 
   rec.toCharArray(replyBuffer, str_len);
   //Serial.println(replyBuffer);
}
//--------------------------------------------------------------------------
void calcWindDir() {
   ++windKnt;
   if (windKnt < 4)
      return;

   windKnt = 0;
   windDir = 0;
   for (int i; i<32; i++) {
      if (debug && false) {
         Serial.print(i);
         Serial.print(": ");
         Serial.println(windDirs[i]);
      }
      if (windDirs[i] > windDir) {
         inx     = i;
         windDir = windDirs[i];
      }
      windDirs[i] = 0;
   }
   if (debug && false) {
      Serial.print("inx: ");
      Serial.println(inx);
   }
   windDir = (float)inx * 11.25;
}
//-------------------------------------------------------------------------*/

// multi-task ie create a timers that will fire repeatedly every x ms
//--------------------------------------------------------------------------
TimedAction statusThread = TimedAction(30000, getStatus);      // every 30 secs
//--------------------------------------------------------------------------


//----------------------------------------------------------------------*/
void setup() {
   // join i2c bus (address optional for master)
   Wire.begin();

   // initialize Temperature sensors
   sensors.begin(); // set the resolution to 10 bit (Can be 9 to 12 bits .. lower is faster)
   sensors.setResolution(Probe1, 10);
   sensors.setResolution(Probe2, 10);

   // start the Ethernet and UDP:
   Ethernet.begin(mac, ip);
   Udp.begin(localPort);
   for(int i=0; i<18; i = i+2) {
      replyBuffer[i] = '0';
      replyBuffer[i+1] = 9;
   }

   // begin serial communications if needed
   if (debug) Serial.begin(115200);

   pinMode(WIND_DIR, INPUT);

   // RxTx led
   pinMode(RXTX_LED, OUTPUT);
   digitalWrite(RXTX_LED, LOW);

   // barometric pressure/temp
   bmpOK = bmp.begin();
   if (debug && !bmpOK)
      Serial.println("Could not find a valid BME180 sensor, check wiring!");

   // Initialise DHT temperatrue and humidity sensor
   dht.begin();
}
//----------------------------------------------------------------------*/

//----------------------------------------------------------------------*/
void loop(){
   statusThread.check();
   flag = !flag;
   if (flag) {
      /* Serial.println("reading Davis wind  ");
       * map resistor value to 360 degrees, so normally we would use a command
       * like: map(analogRead(WIND_DIR), 0, 1023, 0, 360)
       * But we are really just interested in one of the 16 compass points, so
       * we can take a reading and drop it into 1 of 16 buckets.
       * 
       * Where: North = 0,360, NNE = 22.5,  NE = 45,  ENE = 67.5
       *        East  = 90,    ESE = 112.5, SE = 135, SSE = 157.5
       *        South = 180,   SWS = 202.5, SW = 225, WSW = 247.5
       *        West  = 270,   WNW = 292.5, NW = 315, NNW = 337.5
       * 
       *  0 = 349-11 is North,  1 = 12-33 NNE,    2 = 34-56 NE,    3 = 57-78 ENE
       *  4 = 79-101 is East,   5 = 102-123 ESE,  6 = 124-146 SE,  7 = 147-168 SSE
       *  8 = 169-191 is South, 9 = 192-213 SSW, 10 = 214-236 SW, 11 = 237-258 WSW
       * 12 = 259-281 is West, 13 = 282-303 WNW, 14 = 304-326 NW, 15 = 327-348 NNW
       * 
       * The bucket accumlators will tell the most re-occurrant wind direction
       * We then multiply by 22.5 to get the median wind direction for the
       * average wind speed for the sample time period.
       */
      wind_ohms = analogRead(WIND_DIR);
      //Serial.print("wind in ohms: ");
      //Serial.print(wind_ohms);
      vaneVal  = map(wind_ohms, 0, 1023, 0, 32);
      //Serial.print("  map: ");
      if (vaneVal == 32) vaneVal = 0; // Same value ie North
      //Serial.println(vaneVal);
      windDirs[vaneVal]++;            // increment that bucket
   }

   if (flashy > 0){
     if (flashy % 2)
       digitalWrite(RXTX_LED, LOW);
     else
       digitalWrite(RXTX_LED, HIGH);
     flashy--;
   }

   // if there's data available, read a packet
   packetSize = Udp.parsePacket();
   if (packetSize) {
      Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); // read the packet into packetBuffer
      flashy = 31;  // Should be an odd number

      /*if (debug) {
         Serial.print("Received packet of size ");
         Serial.println(packetSize);
         Serial.print("From ");
         IPAddress remote = Udp.remoteIP();
         for (int i = 0; i < 4; i++) {
            Serial.print(remote[i], DEC);
            if (i < 3) Serial.print(".");
         }
         Serial.print(", port ");
         Serial.println(Udp.remotePort());
         Serial.print("Contents: ");
         Serial.println(packetBuffer);
      }*/

      if (packetBuffer[0] == 'r') {
         if (day[0] != packetBuffer[5] || day[1] != packetBuffer[6]) {
            day[0] = packetBuffer[5];
            day[1] = packetBuffer[6];
            if (debug) {Serial.print("its a new Day! "); Serial.println(day);}
            ticks    = 0;
            readings = 0;
            getStatus();
         }
      }
      else if ( strstr(packetBuffer, "Reset") ) {
         ticks    = 0;
         readings = 0;
         getStatus();
      }

      // send a reply to the IP address and port that sent us the packet we received
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      Udp.write(replyBuffer);
      Udp.endPacket();
   }

   delay(25);     // wait 25 miliseconds
}
//----------------------------------------------------------------------
