/*
 * 
 *          ZoneMinder Hardware Project
 *                
 * 
 * 
 *        A sensor on an Arduino UNO communicating
 *        via ethernet to ZMTrigger daemon for ZoneMinder 
 *        CCTV GNU/Linux software.
 * 
 * 
 */


/*
 *            What it does:
 *        Works with accompanying shield to act as motion sensor 
 *        for Zoneminder via ZMTrigger. Shield is optional if you 
 *        want to bother with a perf board and components instead.
 *        
 *            Components:
 *        ENC28J60
 *        Motion Sensor (Either PIR, or microwave HFS-DC06H)
 *        Microphone for Loud Noise alarms
 *        LM35 Temperature Sensor
 *        RGB Status LED
 *        Speaker
 *        
 *        Power from External 12V supply, not USB!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * 
 */

/*
 * 
 * 
 * 
 *            Directions: 
 *        Use Arduino Uno
 *        See https://git.steakelectronics.com/adminguy/ZMHW_Project_InfraredDiodeSensor
 *    
 *        Connect ENC28J60 using these instructions:
 *        https://github.com/ntruchsess/arduino_uip
 *        http://web.archive.org/save/https://create.arduino.cc/projecthub/Sourcery/how-to-connect-the-enc28j60-to-an-arduino-efd0dd
 *        CS for ENC and UIP library is 10 by default on UNO. Not 8, like that link says.
 *    
 *  
 *    
 *    
 *    
 */




#include <UIPEthernet.h>
//#include <EEPROM.h>




//Edit the below values

#define DEBUGMODE     0           // 1 == on. 0 == off.
#define MIC           0           // 1 == on. 0 == off.
#define SICK          0           // set to 1 if using sick sensor (on analog pin)
                                  // sick sensor is a transistor activated photoelectric
                                  // sensor. More details below.
#define NORMALMODECT  1           // For a digital High motion sensor.

/***************Ethernet ENC28J60***************/

//Mac must be unique for each sensor
byte mac[]    = { 0xD3, 0x5D, 0xBE, 0xEF, 0xEE, 0x50 };
//Static IP of Arduino
byte ip[]     = { 192, 168, 78, 50 };
//IP of ZM Server
byte server[] = { 192, 168, 78, 123 }; 
//ZM server IP to put in requests.
String host="192.168.78.123";

char*   monnum                = "12";        //monitor number

char*   LOCATIONOFSENSOR      = "Bay 4 - Tool Room";

#define ZMTRIGGERPORT 6802
#define LISTENPORT 80     // (port 80 is default for HTTP)




/***************Pins***************/


//Digital
#define MODECTPIN       2
#define SPEAKERPIN      6
#define LEDPIN          9



//Analog
#define TEMPPIN         A0
#define SICKPIN         A1
#define MICROPHONEPIN   A2





/***************Variables************/


uint8_t   AlarmActive         = 0;
char*     ZMTriggerMessage    = "1234567890123456789012345"; //Initialize this with dummy data
uint8_t   TEMPERATUREVALUE    = 0;
uint8_t   TEMPERATUREVALUE2   = 0;
uint8_t   SOUNDVALUE          = 0;
uint16_t  MODECTVALUE         = 0;


/*
        ZMTrigger Command to Send

B<id>|B<action>|B<score>|B<cause>|B<text>|B<showtext>
which in this code is:
monnum | onoff + timealarm | score | source

e.g.
2|on+5|100|ZoneAVR||

This will send a command to ZMTrigger.pl to turn monitor #2 ON (alarm state) for five seconds, with a score of 100
and the source of ZoneAVR. The text field, and show text are not setup here.

*/


char* onoff     = "on";                       //command to send to zmtrigger. 
char* timealarm = "10";                       //time to set monitor to alarm
char* score     = "77";                       //score to assign
char* source    = "ZMHW";                     //source. Add details as needed (e.g. Hallway sensor)




//Do not need to edit below





// Initialize the Ethernet server library


EthernetClient client;
EthernetServer server2 = EthernetServer(LISTENPORT);

/*
void chime(int freq){
    tone(SPEAKERPIN, freq, 50);
    delay(50);
}

void chimefast(int freq, int fast){
    tone(SPEAKERPIN, freq, fast);
    delay(fast);
}*/

//timer/interrupt
uint16_t timer1;
uint16_t timer1_counter;
uint8_t  debouncetime;
uint8_t  first_interrupt = 0;

//timer for debounce
ISR(TIMER1_OVF_vect){
    timer1++;
    
    if (first_interrupt == 1 ){
      debouncetime++;
    }
    
    if (debouncetime > 2) {
      first_interrupt = 0;
      debouncetime    = 0;
      AlarmActive     = 0;
    }
  
}



void setup()
{

  Serial.begin(9600);
  Serial.println(F("ZMHW Project"));
  Serial.println(F("Motion Sensor"));

  //if sick
  pinMode(SICKPIN, INPUT);
  //if mic
  pinMode(MICROPHONEPIN, INPUT);
  //if speaker
  pinMode(SPEAKERPIN, OUTPUT);
  pinMode(MODECTPIN, INPUT);
  pinMode(TEMPPIN, INPUT);

  //Be careful. This sets Areg to 1.1v!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //Used for LM35.
  analogReference(INTERNAL);
  
  
  
  Ethernet.begin(mac, ip);
  server2.begin();
  Serial.print(F("server is at "));
  Serial.println(Ethernet.localIP());
 
  
  
  //timer 1, setup
  //Crystal of Uno is == Mega (16MHz)


  //this timer is all bodged up, but doesn't matter, as we only
  //need two or three counts between alarms. Not going to fix atm.
  //and it works.
  
  //Clear existing registers
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  //timer1_counter  = 10000;    // 62500 for one second if using 256 prescaler. can't be over 16 bit value (timer1 is 16bit limited)
  //timer1_counter  = 10;
  //TCNT1 = timer1_counter;     // TCNT1 is what we are overflowing on
  TCCR1B |= (1 << CS12);        // 256 prescaler  (divide 16mhz/256 = 62500)
  TCCR1B |= 00000101;           // https://web.archive.org/web/20170707164930/http://www.avrbeginners.net:80/architecture/timers/timers.html
                                // search tccr1b
  TIMSK1 |= (1 << TOIE1);       // enable timer overflow interrupt (if goes over timer, interrupt flagged)
                                //end timer1



  sei(); //timer needs interrupts, enable (set) interrupts
  
  //tone(SPEAKERPIN, 1000, 200);
  //delay(100);
  //tone(SPEAKERPIN, 2000, 200);
  //delay(100);
  //tone(SPEAKERPIN, 2200, 200);
  //delay(100);
}



void loop()
{
  //delay(50);



/*****************ANALOG SECTION******************/

   //The SICK infrared laser requires a transistor to output
   //high or low, but we will cheat and instead, put it through
   //a serial diode, and then use the ADC instead of a digital pin
   //saves a few seconds to put a diode on the end instead of transistor
   //when soldering by hand. Works equally as well. Requires ADC pin.

/*
   if (SICK){
   MotionSensorRead = analogRead(SICKPIN);
   Serial.print("Motion Sensor Value: ");
   Serial.println(String(MotionSensorRead));
   //Serial.println(String(timer1));
    delay(30);
   }
*/


  //todo: only serial.print temp every 10 or so reads
   TEMPERATUREVALUE = analogRead(TEMPPIN);
   Serial.print(F("Temperature Value: "));
   // See resources. uses 1.1 aref, converts to celcius, then to Fareinheit.
   TEMPERATUREVALUE2 = (TEMPERATUREVALUE / 9.31)* 2 + 30;
   Serial.println(String(TEMPERATUREVALUE2));
    delay(30);

/*
   if (MIC){
   SOUNDVALUE = analogRead(MICROPHONEPIN);
   Serial.print("Sound Value: ");
   Serial.println(String(SOUNDVALUE));
    delay(30);
   }
*/
   if (NORMALMODECT){
   delay(10);
   MODECTVALUE = digitalRead(MODECTPIN);
   delay(10);
   Serial.print(F("Motion Detector Value (normal): "));
   //Serial.println(String(MODECTVALUE));
   Serial.println(MODECTVALUE);
    delay(30);
   }

   
   if(DEBUGMODE){
    delay(10);
   }
  



/****************MOTION SENSING****************/



  //SICK
  //Motion sensing for Sick Photoelectric sensor only
  //upon boot, values are around 400 sometimes, so only alert at higher
  if(SICK){
    if (MODECTVALUE > 500 && AlarmActive == 0){
    Serial.println(F("Motion Detected on Sick"));

    //firstpacketsend    = 0;

    cli();
    //some of this may be redundant, need to check
    AlarmActive        = 1;
    first_interrupt    = 1;
    debouncetime       = 0;
    sei();

    //Want the chime to be only noticeable if you know what to listen
    //for. Make it a high freq. sound that is easy to miss.
    //Resistors to speaker should be high
    //chime(13000);

         
    Serial.println("Connecting...");


    if (client.connect(server, ZMTRIGGERPORT)) {

      //chime(13000);


    
      //beware that the buffer in snprintf is big enough to hold everything
      snprintf(ZMTriggerMessage, 56, "%s|%s+%s|%s|%s||", monnum, onoff, timealarm, score, source);
      Serial.print(F("the TCP Packet being sent:"));
      //Serial.println(String(ZMTriggerMessage));
      client.println(String(ZMTriggerMessage)); //required
      Serial.println(F("TCP packet sent to ZMTrigger"));
      client.stop();
    } 
    else {
      //NOTE: If you are not connected to the network
      //the device will currently freeze up, and not timeout.
      //Need to implement a watchdog.
      //If you ARE connected to the network, and server is not available
      //then it will timeout.
      
      Serial.println(F("Connection to ZM Server failed"));

      
      /*chime(50);
      delay(100);
      chime(50);
      delay(100);
      chime(50);
      delay(100);
      */
    }
  }
  }//end sick





  //Digital High Motion Sensor
  if(NORMALMODECT){
    
    if (MODECTVALUE==HIGH && AlarmActive == 0){
    Serial.println(F("Motion Detected on Normal Sensor"));

    //firstpacketsend    = 0;

    cli();
    //some of this may be redundant, need to check
    AlarmActive        = 1;
    first_interrupt    = 1;
    debouncetime       = 0;
    sei();

    //Want the chime to be only noticeable if you know what to listen
    //for. Make it a high freq. sound that is easy to miss.
    //Resistors to speaker should be high
    //chime(13000);
    delay(10);

         
    Serial.println(F("Connecting..."));


    if (client.connect(server, ZMTRIGGERPORT)) {

      //chime(13000);
      //delay(10);

    
      //beware that the buffer in snprintf is big enough to hold everything
      snprintf(ZMTriggerMessage, 56, "%s|%s+%s|%s|%s||", monnum, onoff, timealarm, score, source);
      Serial.print(F("the TCP Packet being sent:"));
      Serial.println(String(ZMTriggerMessage));
      client.println(String(ZMTriggerMessage)); //required
      Serial.println(F("TCP packet sent to ZMTrigger"));
      client.stop();
    } 
    else {
      //NOTE: If you are not connected to the network
      //the device will currently freeze up, and not timeout.
      //Need to implement a watchdog.
      //If you ARE connected to the network, and server is not available
      //then it will timeout.
      
      Serial.println(F("Connection to ZM Server failed"));

     /* chime(40);
      delay(100);
      chime(60);
      delay(100);
      chime(60);
      delay(100);
      */
    }
  }
  MODECTVALUE=LOW;
  }//end normal Motion Sensor

     
    //disconnect
    if (!client.connected()) {
      client.stop();
      
    }






/********************SERVER STATUS PAGE*********************/
/*
 * With this, you can logon to the Sensor from your LAN to find
 * out just what device this IP address is, in case you happen to 
 * forget. We can also pull the temperature from this page, and
 * populate it to the camera feed, via ZMTrigger, from a server
 * side wget.
 */


  //Serve Status Page
  // listen for incoming clients
  EthernetClient client = server2.available();
  if (client) {
    Serial.println(F("web visitor"));
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          //client.println("Connection: close");  // the connection will be closed after completion of the response
          //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><pre>");
          client.println("<b>Steak Electronics</b>");
          client.println("\"Steak it easy... That is, don't overcook steak\"");
          client.println("");
          //client.println("<b>IP Address:</b>");
          //client.println(Ethernet.localIP());
          client.print("Sensor Location:");
          client.println(LOCATIONOFSENSOR);
          client.print("Type::");
          client.println("HFS-DC06H");
          client.print("Monitor: ");
          client.println(monnum);
          client.print("Temp:");
          client.println(TEMPERATUREVALUE2);
          client.println("</pre></html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println(F("client disconnected"));
  }




        
          //write to eeprom if connection failed, and try again upon reboot
          //EEPROM.write(EEPROM_RETRY, switch_pressed);
      
      



    
} //end main loop
