|
@ -0,0 +1,338 @@ |
|
|
|
|
|
/*
|
|
|
|
|
|
* |
|
|
|
|
|
* ZoneMinder Hardware Project |
|
|
|
|
|
* Laser Sensor |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* A sensor communicating |
|
|
|
|
|
* via ENC28J60 to ZMTrigger daemon |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* What it does: |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* Directions: |
|
|
|
|
|
* |
|
|
|
|
|
* Use Arduino Uno |
|
|
|
|
|
* Connect Pin 3 to Reset pin (may not be needed) |
|
|
|
|
|
* 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. |
|
|
|
|
|
* |
|
|
|
|
|
* Connect microwave motion sensor such as HB100, or laser diode to A1 |
|
|
|
|
|
* Add a speaker for audible debugging |
|
|
|
|
|
* LED can also be added |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <UIPEthernet.h>
|
|
|
|
|
|
#include <EEPROM.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Edit the below values
|
|
|
|
|
|
|
|
|
|
|
|
#define DEBUGMODE 1 // 1 == on. 0 == off.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************Ethernet ENC28J60***************/ |
|
|
|
|
|
|
|
|
|
|
|
//Mac must be unique
|
|
|
|
|
|
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xEA, 0x11 }; |
|
|
|
|
|
//IP of Arduino
|
|
|
|
|
|
byte ip[] = { 192, 168, 1, 177 }; |
|
|
|
|
|
//IP of zm server
|
|
|
|
|
|
byte server[] = { 192, 168, 1, 178 }; |
|
|
|
|
|
|
|
|
|
|
|
//ZM server ip to put in requests.
|
|
|
|
|
|
//maybe you can use hostname, not sure. TODO: test hostnames
|
|
|
|
|
|
String host="192.168.1.178"; |
|
|
|
|
|
|
|
|
|
|
|
//username and password to login to Zoneminder Server.
|
|
|
|
|
|
//If you don't have authentication, you will need to edit the
|
|
|
|
|
|
//script.
|
|
|
|
|
|
//NOTE: not needed for ZMTrigger. Only API.
|
|
|
|
|
|
String username="username"; |
|
|
|
|
|
String password="password"; |
|
|
|
|
|
|
|
|
|
|
|
EthernetClient client; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define ZMTRIGGERPORT 6802
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************Pins***************/ |
|
|
|
|
|
|
|
|
|
|
|
#define SPEAKER_PIN 6
|
|
|
|
|
|
#define LED_PIN 9
|
|
|
|
|
|
#define RESETPIN 2 //may not be needed here
|
|
|
|
|
|
#define SENSORPIN A1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************Variables************/ |
|
|
|
|
|
|
|
|
|
|
|
int MotionSensorRead = 0; |
|
|
|
|
|
uint8_t AlarmActive = 0; |
|
|
|
|
|
char* ZMTriggerMessage = "1234567890123456789012345678901234"; //Initialize this with dummy data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Upper and lower limit for ADC to register motion
|
|
|
|
|
|
// The HB100 outputs a wave that chaotically moves up and down. If the wave reaches a
|
|
|
|
|
|
// high or low point, we register an alarm.
|
|
|
|
|
|
|
|
|
|
|
|
// These should be tuned depending on your setup and how sensitive you want motion detected
|
|
|
|
|
|
// without getting false alarms. Easiest to test with Serial output
|
|
|
|
|
|
|
|
|
|
|
|
#define UPPERLIMIT 900
|
|
|
|
|
|
#define LOWERLIMIT 100
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
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* monnum = "25"; //monitor number
|
|
|
|
|
|
char* onoff = "on"; //command to send to zmtrigger.
|
|
|
|
|
|
char* timealarm = "10"; //time to set monitor to alarm
|
|
|
|
|
|
char* score = "100"; //score to assign
|
|
|
|
|
|
char* source = "ZMHW MotionSensor"; //source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Do not need to edit below
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void chime(int freq){ |
|
|
|
|
|
tone(SPEAKER_PIN, freq, 50); |
|
|
|
|
|
delay(50); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void chimefast(int freq, int fast){ |
|
|
|
|
|
tone(SPEAKER_PIN, 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("ZMHW Project"); |
|
|
|
|
|
Serial.println("Motion Sensor"); |
|
|
|
|
|
|
|
|
|
|
|
pinMode(SENSORPIN, INPUT); |
|
|
|
|
|
pinMode(SPEAKER_PIN, OUTPUT); |
|
|
|
|
|
pinMode(RESETPIN, OUTPUT); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ethernet.begin(mac, ip); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//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 interrupts
|
|
|
|
|
|
|
|
|
|
|
|
tone(SPEAKER_PIN, 1000, 200); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
tone(SPEAKER_PIN, 2000, 200); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
tone(SPEAKER_PIN, 2200, 200); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void loop() |
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MotionSensorRead = analogRead(SENSORPIN); |
|
|
|
|
|
Serial.print("Motion Sensor Value: "); |
|
|
|
|
|
Serial.println(String(MotionSensorRead)); |
|
|
|
|
|
//Serial.println(String(timer1));
|
|
|
|
|
|
|
|
|
|
|
|
if(DEBUGMODE){ |
|
|
|
|
|
delay(10); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//upon boot, values are around 400 sometimes, so only alert at higher
|
|
|
|
|
|
if (MotionSensorRead > 500 && AlarmActive == 0){ |
|
|
|
|
|
Serial.println("Motion Detected"); |
|
|
|
|
|
|
|
|
|
|
|
//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.
|
|
|
|
|
|
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("the TCP Packet being sent:"); |
|
|
|
|
|
Serial.println(String(ZMTriggerMessage)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client.println(String(ZMTriggerMessage)); //required
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Serial.println("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("Connection to ZM Server failed"); |
|
|
|
|
|
|
|
|
|
|
|
chime(50); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
chime(50); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
chime(50); |
|
|
|
|
|
delay(100); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//disconnect
|
|
|
|
|
|
if (!client.connected()) { |
|
|
|
|
|
client.stop(); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//write to eeprom if connection failed, and try again upon reboot
|
|
|
|
|
|
//EEPROM.write(EEPROM_RETRY, switch_pressed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} //end main loop
|