/* * * 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 but recommended * as it's easier to build. * * 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 //#include //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, 0x52 }; //Static IP of Arduino byte ip[] = { 192, 168, 78, 52 }; //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 = "14"; //monitor number char* LOCATIONOFSENSOR = "Bay 1"; #define ZMTRIGGERPORT 6802 #define LISTENPORT 80 // (port 80 is default for HTTP) /***************Pins***************/ //Digital #define MODECTPIN 4 #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|B|B|B|B|B 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(""); client.println("
");
          client.println("Steak Electronics");
          client.println("\"Steak it easy... That is, don't overcook steak\"");
          client.println("");
          //client.println("IP Address:");
          //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("
"); 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