You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

533 lines
13 KiB

  1. /*
  2. *
  3. * ZoneMinder Hardware Project
  4. *
  5. *
  6. *
  7. * A sensor on an Arduino UNO communicating
  8. * via ethernet to ZMTrigger daemon for ZoneMinder
  9. * CCTV GNU/Linux software.
  10. *
  11. *
  12. */
  13. /*
  14. * What it does:
  15. * Works with accompanying shield to act as motion sensor
  16. * for Zoneminder via ZMTrigger. Shield is optional but recommended
  17. * as it's easier to build.
  18. *
  19. * Components:
  20. * ENC28J60
  21. * Motion Sensor (Either PIR, or microwave HFS-DC06H)
  22. * Microphone for Loud Noise alarms
  23. * LM35 Temperature Sensor
  24. * RGB Status LED
  25. * Speaker
  26. *
  27. * Power from External 12V supply, not USB!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  28. *
  29. */
  30. /*
  31. *
  32. *
  33. *
  34. * Directions:
  35. * Use Arduino Uno
  36. * See https://git.steakelectronics.com/adminguy/ZMHW_Project_InfraredDiodeSensor
  37. *
  38. * Connect ENC28J60 using these instructions:
  39. * https://github.com/ntruchsess/arduino_uip
  40. * http://web.archive.org/save/https://create.arduino.cc/projecthub/Sourcery/how-to-connect-the-enc28j60-to-an-arduino-efd0dd
  41. * CS for ENC and UIP library is 10 by default on UNO. Not 8, like that link says.
  42. *
  43. *
  44. *
  45. *
  46. *
  47. */
  48. #include <UIPEthernet.h>
  49. //#include <EEPROM.h>
  50. //Edit the below values
  51. #define DEBUGMODE 0 // 1 == on. 0 == off.
  52. #define MIC 0 // 1 == on. 0 == off.
  53. #define SICK 0 // set to 1 if using sick sensor (on analog pin)
  54. // sick sensor is a transistor activated photoelectric
  55. // sensor. More details below.
  56. #define NORMALMODECT 1 // For a digital High motion sensor.
  57. /***************Ethernet ENC28J60***************/
  58. //Mac must be unique for each sensor
  59. byte mac[] = { 0xD3, 0x5D, 0xBE, 0xEF, 0xEE, 0x52 };
  60. //Static IP of Arduino
  61. byte ip[] = { 192, 168, 78, 52 };
  62. //IP of ZM Server
  63. byte server[] = { 192, 168, 78, 123 };
  64. //ZM server IP to put in requests.
  65. String host="192.168.78.123";
  66. char* monnum = "14"; //monitor number
  67. char* LOCATIONOFSENSOR = "Bay 1";
  68. #define ZMTRIGGERPORT 6802
  69. #define LISTENPORT 80 // (port 80 is default for HTTP)
  70. /***************Pins***************/
  71. //Digital
  72. #define MODECTPIN 4
  73. #define SPEAKERPIN 6
  74. #define LEDPIN 9
  75. //Analog
  76. #define TEMPPIN A0
  77. #define SICKPIN A1
  78. #define MICROPHONEPIN A2
  79. /***************Variables************/
  80. uint8_t AlarmActive = 0;
  81. char* ZMTriggerMessage = "1234567890123456789012345"; //Initialize this with dummy data
  82. uint8_t TEMPERATUREVALUE = 0;
  83. uint8_t TEMPERATUREVALUE2 = 0;
  84. uint8_t SOUNDVALUE = 0;
  85. uint16_t MODECTVALUE = 0;
  86. /*
  87. ZMTrigger Command to Send
  88. B<id>|B<action>|B<score>|B<cause>|B<text>|B<showtext>
  89. which in this code is:
  90. monnum | onoff + timealarm | score | source
  91. e.g.
  92. 2|on+5|100|ZoneAVR||
  93. This will send a command to ZMTrigger.pl to turn monitor #2 ON (alarm state) for five seconds, with a score of 100
  94. and the source of ZoneAVR. The text field, and show text are not setup here.
  95. */
  96. char* onoff = "on"; //command to send to zmtrigger.
  97. char* timealarm = "10"; //time to set monitor to alarm
  98. char* score = "77"; //score to assign
  99. char* source = "ZMHW"; //source. Add details as needed (e.g. Hallway sensor)
  100. //Do not need to edit below
  101. // Initialize the Ethernet server library
  102. EthernetClient client;
  103. EthernetServer server2 = EthernetServer(LISTENPORT);
  104. /*
  105. void chime(int freq){
  106. tone(SPEAKERPIN, freq, 50);
  107. delay(50);
  108. }
  109. void chimefast(int freq, int fast){
  110. tone(SPEAKERPIN, freq, fast);
  111. delay(fast);
  112. }*/
  113. //timer/interrupt
  114. uint16_t timer1;
  115. uint16_t timer1_counter;
  116. uint8_t debouncetime;
  117. uint8_t first_interrupt = 0;
  118. //timer for debounce
  119. ISR(TIMER1_OVF_vect){
  120. timer1++;
  121. if (first_interrupt == 1 ){
  122. debouncetime++;
  123. }
  124. if (debouncetime > 2) {
  125. first_interrupt = 0;
  126. debouncetime = 0;
  127. AlarmActive = 0;
  128. }
  129. }
  130. void setup()
  131. {
  132. Serial.begin(9600);
  133. Serial.println(F("ZMHW Project"));
  134. Serial.println(F("Motion Sensor"));
  135. //if sick
  136. pinMode(SICKPIN, INPUT);
  137. //if mic
  138. pinMode(MICROPHONEPIN, INPUT);
  139. //if speaker
  140. pinMode(SPEAKERPIN, OUTPUT);
  141. pinMode(MODECTPIN, INPUT);
  142. pinMode(TEMPPIN, INPUT);
  143. //Be careful. This sets Areg to 1.1v!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  144. //Used for LM35.
  145. analogReference(INTERNAL);
  146. Ethernet.begin(mac, ip);
  147. server2.begin();
  148. Serial.print(F("server is at "));
  149. Serial.println(Ethernet.localIP());
  150. //timer 1, setup
  151. //Crystal of Uno is == Mega (16MHz)
  152. //this timer is all bodged up, but doesn't matter, as we only
  153. //need two or three counts between alarms. Not going to fix atm.
  154. //and it works.
  155. //Clear existing registers
  156. TCCR1A = 0;
  157. TCCR1B = 0;
  158. // Set timer1_counter to the correct value for our interrupt interval
  159. //timer1_counter = 10000; // 62500 for one second if using 256 prescaler. can't be over 16 bit value (timer1 is 16bit limited)
  160. //timer1_counter = 10;
  161. //TCNT1 = timer1_counter; // TCNT1 is what we are overflowing on
  162. TCCR1B |= (1 << CS12); // 256 prescaler (divide 16mhz/256 = 62500)
  163. TCCR1B |= 00000101; // https://web.archive.org/web/20170707164930/http://www.avrbeginners.net:80/architecture/timers/timers.html
  164. // search tccr1b
  165. TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt (if goes over timer, interrupt flagged)
  166. //end timer1
  167. sei(); //timer needs interrupts, enable (set) interrupts
  168. //tone(SPEAKERPIN, 1000, 200);
  169. //delay(100);
  170. //tone(SPEAKERPIN, 2000, 200);
  171. //delay(100);
  172. //tone(SPEAKERPIN, 2200, 200);
  173. //delay(100);
  174. }
  175. void loop()
  176. {
  177. //delay(50);
  178. /*****************ANALOG SECTION******************/
  179. //The SICK infrared laser requires a transistor to output
  180. //high or low, but we will cheat and instead, put it through
  181. //a serial diode, and then use the ADC instead of a digital pin
  182. //saves a few seconds to put a diode on the end instead of transistor
  183. //when soldering by hand. Works equally as well. Requires ADC pin.
  184. /*
  185. if (SICK){
  186. MotionSensorRead = analogRead(SICKPIN);
  187. Serial.print("Motion Sensor Value: ");
  188. Serial.println(String(MotionSensorRead));
  189. //Serial.println(String(timer1));
  190. delay(30);
  191. }
  192. */
  193. //todo: only serial.print temp every 10 or so reads
  194. TEMPERATUREVALUE = analogRead(TEMPPIN);
  195. Serial.print(F("Temperature Value: "));
  196. // See resources. uses 1.1 aref, converts to celcius, then to Fareinheit.
  197. TEMPERATUREVALUE2 = (TEMPERATUREVALUE / 9.31)* 2 + 30;
  198. Serial.println(String(TEMPERATUREVALUE2));
  199. delay(30);
  200. /*
  201. if (MIC){
  202. SOUNDVALUE = analogRead(MICROPHONEPIN);
  203. Serial.print("Sound Value: ");
  204. Serial.println(String(SOUNDVALUE));
  205. delay(30);
  206. }
  207. */
  208. if (NORMALMODECT){
  209. delay(10);
  210. MODECTVALUE = digitalRead(MODECTPIN);
  211. delay(10);
  212. Serial.print(F("Motion Detector Value (normal): "));
  213. //Serial.println(String(MODECTVALUE));
  214. Serial.println(MODECTVALUE);
  215. delay(30);
  216. }
  217. if(DEBUGMODE){
  218. delay(10);
  219. }
  220. /****************MOTION SENSING****************/
  221. //SICK
  222. //Motion sensing for Sick Photoelectric sensor only
  223. //upon boot, values are around 400 sometimes, so only alert at higher
  224. if(SICK){
  225. if (MODECTVALUE > 500 && AlarmActive == 0){
  226. Serial.println(F("Motion Detected on Sick"));
  227. //firstpacketsend = 0;
  228. cli();
  229. //some of this may be redundant, need to check
  230. AlarmActive = 1;
  231. first_interrupt = 1;
  232. debouncetime = 0;
  233. sei();
  234. //Want the chime to be only noticeable if you know what to listen
  235. //for. Make it a high freq. sound that is easy to miss.
  236. //Resistors to speaker should be high
  237. //chime(13000);
  238. Serial.println("Connecting...");
  239. if (client.connect(server, ZMTRIGGERPORT)) {
  240. //chime(13000);
  241. //beware that the buffer in snprintf is big enough to hold everything
  242. snprintf(ZMTriggerMessage, 56, "%s|%s+%s|%s|%s||", monnum, onoff, timealarm, score, source);
  243. Serial.print(F("the TCP Packet being sent:"));
  244. //Serial.println(String(ZMTriggerMessage));
  245. client.println(String(ZMTriggerMessage)); //required
  246. Serial.println(F("TCP packet sent to ZMTrigger"));
  247. client.stop();
  248. }
  249. else {
  250. //NOTE: If you are not connected to the network
  251. //the device will currently freeze up, and not timeout.
  252. //Need to implement a watchdog.
  253. //If you ARE connected to the network, and server is not available
  254. //then it will timeout.
  255. Serial.println(F("Connection to ZM Server failed"));
  256. /*chime(50);
  257. delay(100);
  258. chime(50);
  259. delay(100);
  260. chime(50);
  261. delay(100);
  262. */
  263. }
  264. }
  265. }//end sick
  266. //Digital High Motion Sensor
  267. if(NORMALMODECT){
  268. if (MODECTVALUE==HIGH && AlarmActive == 0){
  269. Serial.println(F("Motion Detected on Normal Sensor"));
  270. //firstpacketsend = 0;
  271. cli();
  272. //some of this may be redundant, need to check
  273. AlarmActive = 1;
  274. first_interrupt = 1;
  275. debouncetime = 0;
  276. sei();
  277. //Want the chime to be only noticeable if you know what to listen
  278. //for. Make it a high freq. sound that is easy to miss.
  279. //Resistors to speaker should be high
  280. //chime(13000);
  281. delay(10);
  282. Serial.println(F("Connecting..."));
  283. if (client.connect(server, ZMTRIGGERPORT)) {
  284. //chime(13000);
  285. //delay(10);
  286. //beware that the buffer in snprintf is big enough to hold everything
  287. snprintf(ZMTriggerMessage, 56, "%s|%s+%s|%s|%s||", monnum, onoff, timealarm, score, source);
  288. Serial.print(F("the TCP Packet being sent:"));
  289. Serial.println(String(ZMTriggerMessage));
  290. client.println(String(ZMTriggerMessage)); //required
  291. Serial.println(F("TCP packet sent to ZMTrigger"));
  292. client.stop();
  293. }
  294. else {
  295. //NOTE: If you are not connected to the network
  296. //the device will currently freeze up, and not timeout.
  297. //Need to implement a watchdog.
  298. //If you ARE connected to the network, and server is not available
  299. //then it will timeout.
  300. Serial.println(F("Connection to ZM Server failed"));
  301. /* chime(40);
  302. delay(100);
  303. chime(60);
  304. delay(100);
  305. chime(60);
  306. delay(100);
  307. */
  308. }
  309. }
  310. MODECTVALUE=LOW;
  311. }//end normal Motion Sensor
  312. //disconnect
  313. if (!client.connected()) {
  314. client.stop();
  315. }
  316. /********************SERVER STATUS PAGE*********************/
  317. /*
  318. * With this, you can logon to the Sensor from your LAN to find
  319. * out just what device this IP address is, in case you happen to
  320. * forget. We can also pull the temperature from this page, and
  321. * populate it to the camera feed, via ZMTrigger, from a server
  322. * side wget.
  323. */
  324. //Serve Status Page
  325. // listen for incoming clients
  326. EthernetClient client = server2.available();
  327. if (client) {
  328. Serial.println(F("web visitor"));
  329. // an http request ends with a blank line
  330. boolean currentLineIsBlank = true;
  331. while (client.connected()) {
  332. if (client.available()) {
  333. char c = client.read();
  334. Serial.write(c);
  335. // if you've gotten to the end of the line (received a newline
  336. // character) and the line is blank, the http request has ended,
  337. // so you can send a reply
  338. if (c == '\n' && currentLineIsBlank) {
  339. // send a standard http response header
  340. client.println("HTTP/1.1 200 OK");
  341. client.println("Content-Type: text/html");
  342. //client.println("Connection: close"); // the connection will be closed after completion of the response
  343. //client.println("Refresh: 5"); // refresh the page automatically every 5 sec
  344. client.println();
  345. client.println("<!DOCTYPE HTML>");
  346. client.println("<html><pre>");
  347. client.println("<b>Steak Electronics</b>");
  348. client.println("\"Steak it easy... That is, don't overcook steak\"");
  349. client.println("");
  350. //client.println("<b>IP Address:</b>");
  351. //client.println(Ethernet.localIP());
  352. client.print("Sensor Location:");
  353. client.println(LOCATIONOFSENSOR);
  354. client.print("Type::");
  355. client.println("HFS-DC06H");
  356. client.print("Monitor: ");
  357. client.println(monnum);
  358. client.print("Temp:");
  359. client.println(TEMPERATUREVALUE2);
  360. client.println("</pre></html>");
  361. break;
  362. }
  363. if (c == '\n') {
  364. // you're starting a new line
  365. currentLineIsBlank = true;
  366. } else if (c != '\r') {
  367. // you've gotten a character on the current line
  368. currentLineIsBlank = false;
  369. }
  370. }
  371. }
  372. // give the web browser time to receive the data
  373. delay(1);
  374. // close the connection:
  375. client.stop();
  376. Serial.println(F("client disconnected"));
  377. }
  378. //write to eeprom if connection failed, and try again upon reboot
  379. //EEPROM.write(EEPROM_RETRY, switch_pressed);
  380. } //end main loop