//This sketch controls a motor that rolls and unrolls reemay over greenhouse crops
//The inputs are : button pushes (for manual control/testing), reed switches (stop at end),light sensor (analog), temperature probe(digital)
//The button pushes and reed switch pushes are debounced which complicates the code but seems needed
//the analog light sensor is smoothed using a running average
//The ouputs are: three relays control a single 24 vdc motor driving the reemay roller.  The relays are arranged so that the motor can be reversed or stopped. 
// The other output is a digital LED that turns: blue=covered (night); green=opening; yellow= open (day); red=closing
//eventually there will be an output that is a connection to the farmer's phone that reports the greenhouse data and the status of the reemay mover.
//For help contact Lu: [email protected]

#include <OneWire.h>               // includes software for temperature sensor
#include <DallasTemperature.h>     // includes software for temperature sensor
#include <FastLED.h>               // include library for controlling led
//===================================BEGIN DECLARATIONS=================================

////#include "reemay.h"                // #defines for this project
#define    VERSION_ID         F( "reemay version 5 modified 9/19/2024" )
#define RELAY_ONE_PIN      6     // const int relayone = 6;
#define RELAY_TWO_PIN      7     //const int relaytwo = 7;
#define RELAY_THREE_PIN    8     //const int relaythree = 8;

#define BUTTON_PIN 2               // push button to create INTERRUPTION must be on pin 2 or 3.
#define ONE_WIRE_BUS 4
#define reedButton_PIN 3           // the number of the reed pin       Lu had this PIN 9
#define LED_PIN     10             // declare which pin is the led output pin
#define NUM_LEDS  1                // specify the number of leds on the digital led 
#define THERMISTORNOMINAL 10000    // resistance at 25 degrees C
#define TEMPERATURENOMINAL 25      // temp. for nominal resistance (almost always 25 C)
#define numTempSamples 30          // how many temperaturesamples to take and average, more takes longer but is more 'smooth'
#define BCOEFFICIENT 3950          // The beta coefficient of the thermistor (usually 3000-4000)
#define SERIESRESISTOR 10000       // the value of the 'other' resistor
#define LUMINOSITY_SAMPLE_SIZE 100
#define LUMINOSITY_BOUNDARIES  30/100  // to exclude extra mesures from average calculation. Procentage  
#define LUMINOSITY_SAMPLE_DELAY 50
#define PHOTORESISTOR_PIN A0       //input pin for light sensor

//  the following are the user adjustable open and close temperatures in degrees F.  
const int morning_open_temperature = 82;  //test= 82.0, greenhouse= 40.0
const int night_close_temperature = 78;   //test = 78.0 , greenhouse = 34.0  
const int daytime_close_temperature = 76; // test = 76.0 , greenhouse = 32.0  

const int lightThreshold = 400;  //daylight detector-- a number between 0 and 1023 where 1023 is the brightest and 0 is the darkest
//const int buttonPin = 5;    // the number of the pushbutton pin

//delarations for motor control: three simple relays in reversing configuration
//constants for motor relay pins. To change the pin assignments, open the reemay.h file
const int relay_one_pin   = 6;
const int relay_two_pin   = 7;
const int relay_three_pin = 8;
//const int reedButton      = 9;    // the number of the reed pin

// motor_state is a variable for the 4 possible motor states : 
// motor_state=0 : motor is stopped before opening (night)
// motor_state=1 : motor is opening (morning sun up)
// motor_state=2 : motor is stopped before closing (day)
// motot_state=3 : motor is closing (evening sun down)
int motor_state = 0;   // counter for the 4 possible modes of operation of the motor

// the following variables for  the button debouncer are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 35;    // the debounce time; increase if the output flickers          -- in my sample it is 50
//end delarations for button sensing subroutine

//begin delarations for reed switch sensor
// constants won't change. These set the pin numbers for the reed switch input:
// changing variables for the reed switch debouncer:
int reedState;             // the current reading from the input pin
int lastReedState = LOW;   // the previous reading from the input pin

// the following variables for the reed swtich debouncer are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastReedDebounceTime = millis();  // the last time the output pin was toggled
unsigned long reedDebounceDelay = 50;    // the debounce time; increase if the output flickers
//end declarations for reed switch sensor


//set up for the light and temperature sensors values
// the readings array is initialized with zeros
// the smoothing function takes a RUNNING AVERAGE of the readings
//const int samplesizeLIGHT = 30;  //this is the number of readings in the average
//int readings[samplesizeLIGHT] = {0};      // the readings from the analog input

//const int inputPinLIGHT = A0; //input pin for light sensor
bool daylight_state = false;    //boolean value for daylight true=day, false=night
// end of delarations for subroutine for sensing light

float tempF;
// create an array of the crgb type for the led in this case the number of leds is 1 (duh)
CRGB leds[NUM_LEDS];

 // define the integer variables for the red, green, and blue intensities (0-255)
 int g=0;
 int r=0;
 int b=0;
//end delarations for digital LED

//begin delarations for motor safety timer subroutine
unsigned long motor_start_time = 0; //this variable stores the millis() when the motor starts
unsigned long motor_time_limit = 10000; //this sets a limit on the run time of the motor (in milliseconds)
//end declarations for motor safety timer subroutine


// Data wire is plugged into digital pin 4 on the Arduino
// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);  

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);
//end of delarations for subroutine for digital temperature sensor


volatile unsigned long lastTimeButtonPressed = millis();
volatile bool buttonPressed = false;

void advance_motor_state()
{ motor_state = (motor_state + 1) % 4;
  control_motor_and_leds();
Serial.print("Motor advancing to state = ");
Serial.print( motor_state);
Serial.print("     daylight_state = ");
Serial.print(daylight_state);
Serial.print("      tempF = ");
Serial.print(tempF);
Serial.print("      Millis = ");
Serial.println(millis());
}

void buttonPressedInterrupt()
{	Serial.print("    From ButtonInter  current motor_state = ");
  Serial.print(motor_state);
  Serial.print("    lastTimeButtonPressed = ");
  Serial.print(lastTimeButtonPressed);
  Serial.print("    millis = ");
  Serial.print(millis());
  Serial.print("    debounceDelay = ");
  Serial.println(debounceDelay);
  if ((motor_state == 0 || motor_state == 2) && (millis() - lastTimeButtonPressed > debounceDelay))
	{	Serial.print("    Fom inside ButtonInter current motor_state = ");
    Serial.println(motor_state);
    lastTimeButtonPressed = millis();
		buttonPressed = true;
}	}

void reedPressedInterrupt()
{ Serial.print("    From reedInter  current motor_state = ");
  Serial.print(motor_state);
  Serial.print("    lastReedDebounceTime = ");
  Serial.print(lastReedDebounceTime);
  Serial.print("    millis = ");
  Serial.print(millis());
  Serial.print("    reedDebounceDelay = ");
  Serial.println(reedDebounceDelay);
  if ((motor_state == 1 || motor_state == 3) && (millis() - lastReedDebounceTime > reedDebounceDelay))
  { Serial.print("    Fom inside reedInter current motor_state = ");
    Serial.println(motor_state);
    lastReedDebounceTime = millis();
    lastReedState = HIGH;
}	}


void delay_till_pressed(unsigned long ms)
{ unsigned long start = millis();
  while (buttonPressed == false && lastReedState == LOW && millis() - start < ms);
}


// ==========================================END DECLARATIONS===============================


//========================================BEGIN SET UP======================================

void setup() {
//declare pins as output and input
	pinMode(BUTTON_PIN, INPUT);     // Lu used pinMode(buttonPin, INPUT);
	attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), // Links the PIN to interrupt procedure
			buttonPressedInterrupt,
			RISING);

	pinMode(reedButton_PIN, INPUT);     // Lu used pinMode(buttonPin, INPUT);
	attachInterrupt(digitalPinToInterrupt(reedButton_PIN), // Links the PIN to interrupt procedure
			reedPressedInterrupt,
			RISING);

//  pinMode(buttonPin, INPUT);  //this is the input push button  This was Lu setting
  
  pinMode(relay_one_pin, OUTPUT);  //these three are for the relays that run the motor fwd, rev, and stop
  pinMode(relay_two_pin, OUTPUT);
  pinMode(relay_three_pin, OUTPUT);

// initialize serial communication:
  Serial.begin(115200);        //  Lu used   Serial.begin(9600); 
  
  //set motor relays to LOW 
  digitalWrite(relay_one_pin,LOW);
  digitalWrite(relay_two_pin,LOW);
  digitalWrite(relay_three_pin,LOW);

//begin set up for digital LED
 FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
//end set up for digital LED
}
//. =====================================END SET UP===========================================
 


//  =====================================BEGIN MAIN LOOP========================================
void loop( void ) 
{ int desired_motor_state = motor_state;

  read_light_sensor();  // subroutine to read light sensor to detect day/night. reads value between 0-1023 where 0 is dark.
  read_digital_temp ();  //subroutine to read digital temperature.  Takes 96 milliseconds
  if ( daylight_state == true && tempF > morning_open_temperature )                                        desired_motor_state = 2;
  if ( tempF < daytime_close_temperature || ( tempF < night_close_temperature && daylight_state == false)) desired_motor_state = 0;
  if ( desired_motor_state != motor_state || buttonPressed == true)
  { advance_motor_state();
    motor_start_time = millis();
    delay_till_pressed(motor_time_limit);  // awaits till reedButton got pressed - reed moove completed
    if (millis() - motor_start_time >= motor_time_limit) // moove time got exeeded, stop the process
    { Serial.print("Error:  ");    
      Serial.print("The motor tried to run for too long !!"); 
      for(;;){}        // Idles the program
    } else
    { advance_motor_state();    // reed move succeeded    
      Serial.print("motor safety timer operational, run-time milliseconds = "); 
      Serial.println( millis() - motor_start_time);  
    }
  } else delay_till_pressed(1*30*1000);         // 10*60*1000  ten minutes delay
  }
//=============================================END OF MAIN LOOP===================================================

void read_light_sensor () 
{ unsigned long sum=0, sum1=0;
  unsigned long luminosity[LUMINOSITY_SAMPLE_SIZE];
  unsigned int  good_count = LUMINOSITY_SAMPLE_SIZE;
  for (int i=0; i<LUMINOSITY_SAMPLE_SIZE; i++)
  { luminosity[i] = analogRead(PHOTORESISTOR_PIN), delay_till_pressed(LUMINOSITY_SAMPLE_DELAY);
    sum += luminosity[i];
  }
  sum = sum/LUMINOSITY_SAMPLE_SIZE;
  Serial.print("Luminocity sum =");
  Serial.print(sum);
  for (int i=0; i<LUMINOSITY_SAMPLE_SIZE; i++)
    // Next episode eliminates all bad readings (above/below LUMINOSITY_BOUNDARIES) and everage only good readings  
    if ( abs(sum - luminosity[i])/sum > LUMINOSITY_BOUNDARIES )      good_count--;
    else                                                             sum1+=luminosity[i];
  if (sum1 / good_count <= lightThreshold)
        daylight_state = true;
  else  daylight_state = false;
  Serial.print("   sum1 =");
  Serial.print(sum1/good_count);
 Serial.print("   Daylighte_state = ");
 Serial.print(daylight_state);

 }

void read_digital_temp (){
 //========================================begin subroutine to get digital temperature=========================================
 //this is the code subroutine to get the temperatures from a DS18B20 digital sensor 
  // Send the command to get temperatures
  sensors.setResolution(9);    //sets the resolution to 9 bit so the temperature is read fast
  sensors.requestTemperatures(); 
  tempF= ((sensors.getTempCByIndex(0) * 9.0) / 5.0 + 32.0);
  delay(5); //short delay
  Serial.print("   tempF = ");
  Serial.print(tempF);  
  Serial.print("   Current motor_state = ");
  Serial.println(motor_state);  
 }
//===========================================end subroutine to get digital temperature====================================================


// ============================================begin subroutine to control motor and leds==============================================
// set the LED and switch the motor:
// if counter== 0 (reemay is closed, motor is off, led is blue (nightime)
// if counter== 1 (temperature has warmed, light increased, so open reemay and turn led green)
// if counter== 2 (reemay is open, motor is off, led is yellow (daytime)
// if counter== 3 (temperature has fallen, darkness has come, so close reemay and turn red led on)
// if counter ==4 (reemay is closed, motor is off, led is blue, pushcounter is reset to 0
void control_motor_and_leds ()
{ switch( motor_state )
  { case 0: //instructions for relays (all off)
//      digitalWrite(relay_one_pin,LOW);
//      digitalWrite(relay_two_pin,LOW);
//      digitalWrite(relay_three_pin,LOW);
      g=0; r=0; b=200;  //set led to blue
    break;
    case 1: //instructions for relays:motor forward (green) one=high,two=low, three= low
//      digitalWrite(relay_one_pin,HIGH);
//      digitalWrite(relay_two_pin,LOW);
//      digitalWrite(relay_three_pin,LOW);
      g=200; r=0; b= 0;  //set led to green
    break;
    case 2: //instructions for relays (all off)
//      digitalWrite(relay_one_pin,LOW);
//      digitalWrite(relay_two_pin,LOW);
//      digitalWrite(relay_three_pin,LOW);
      g=200; r=200; b=0;  //set led to yellow
    break;
    case 3: //instructions for relays; motor reverse (red): one=low, two=high, three=high
//      digitalWrite(relay_one_pin,LOW);
//      digitalWrite(relay_two_pin,HIGH);
//      digitalWrite(relay_three_pin,HIGH);
      g=0; r=200; b=0;  //set led to red 
  }

  for (int i = 0; i < NUM_LEDS; i++)  //this loop is for if there's more than one LED
  { leds[i] = CRGB(r,g,b);
    FastLED.show();  //turn led on
  }
Serial.print("From Light switch. Motor moveding to state  ");
Serial.println(motor_state);
Serial.print("Lights turned to red=");
Serial.print(r);
Serial.print(",   green=");
Serial.print(g);
Serial.print(", blue=");
Serial.print(b);
Serial.print(" Millis=");
Serial.println(millis());
  buttonPressed = false;
  lastReedState = LOW;
} 

// =============================== EOF ==========================================