fork(4) download
  1. /* arduino mega 2560
  2.   ISCP пины
  3.   MOSI - pin 16
  4.   MISO - pin 17
  5.   CLK (SCK) - pin 15
  6. *****
  7.   enc28j60 подключается к ISCP пинам,
  8.   пин CS определен как ETH_CS, по умолчанию 10 (меняем на 2).
  9. *****
  10.   MicroSD Card Adapter v1.0 11/01/2013 подключается к ISCP пинам,
  11.   Питание 5v.
  12.   Пин CS определен как SD_CS 3
  13. *****
  14.   модуль часов реального времени DS3231M
  15.   подключается к пинам SDA, SCL (43, 44)
  16.   Питание 3,3v.
  17. *****
  18.   модуль твердотельного реле на базе G3MB-202P
  19.   DC- и CH подключаеются к GND, DC+ подключается к 4 ноге
  20.   для замыкания реле подаем сигнал HIGH на 4 ногу
  21. */
  22. #include <EtherCard.h>
  23. #include <SD.h>
  24. #include <DS3231.h>
  25.  
  26. DS3231 rtc(SDA, SCL);
  27.  
  28. #define ETH_CS 2
  29. #define SD_CS 3
  30.  
  31. const String LOG_FILE = "log.txt"; // имя лог файла
  32. const String ZV_FILE = "zvonki.txt"; // имя файла с расписанием звонков
  33. const String ST_FILE = "nastr.txt"; // имя файла с настройками (время синхронизации NTP и отметки начала дня/суток)
  34.  
  35. const byte mymac[] PROGMEM = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 }; //mac адрес сетевой карты
  36.  
  37. const char NTP_REMOTEHOST[] PROGMEM = "ntp21.vniiftri.ru"; // NTP сервер, лучше использовать тип сервера stratum 2
  38. const unsigned int NTP_REMOTEPORT PROGMEM = 123; // NTP удаленный порт
  39. const unsigned int NTP_LOCALPORT PROGMEM = 8888; // локальный порт
  40. const unsigned int NTP_PACKET_SIZE PROGMEM = 48; // временная отметка NTP находится в первых 48 байтах сообщения
  41. const unsigned int TIMEZONE PROGMEM = 5; // временная зона UTC+5
  42.  
  43. byte Ethernet::buffer[500];
  44. static BufferFiller bfill;
  45.  
  46. String bells[20]; // массив для хранения расписания звонков (20 звонков)
  47. String settings[3]; // массив для хранения настроек (время синхронизации NTP и пр.)
  48. // нулевой элемент массива используется для указания времени начала дня/суток
  49.  
  50. const unsigned int RingTime = 6000; // длительность звонка в милисекундах (можно вынести в файл настроек)
  51. const int rele_vcc = 4; // вывод для управления реле
  52. //unsigned long timing = 0; // не заработало
  53.  
  54. void setup () {
  55. pinMode(rele_vcc, OUTPUT);
  56. Serial.begin(9600); // стартуем
  57. Serial.println(F("Старт")); // запускам часы, SD и ethernet
  58. rtc.begin(); // и пишем логи
  59.  
  60. if (SD.begin(SD_CS) == false) {
  61. Serial.println(F("Карта повреждена или отсутствует"));
  62. return;
  63. }
  64. SDwriteLog("************************");
  65. SDwriteLog("start");
  66.  
  67. if (ether.begin(sizeof Ethernet::buffer, mymac, ETH_CS) == 0) {
  68. SDwriteLog("Failed to access Ethernet controller");
  69. }
  70.  
  71. if (ether.dhcpSetup() == false ) { // IP получаем по DHCP
  72. SDwriteLog("Failed to configure Ethernet using DHCP"); // делаем привязку IP адреса по mac на роутере
  73. }
  74. else {
  75. ether.printIp("My IP: ", ether.myip);
  76. SDwriteLog("IP poluchen");
  77. }
  78. SDreadRasp(); // загружаем раписание звонков
  79. SDreadSets(); // и прочие настройки
  80. Serial.println(F("Расписание загружено"));
  81.  
  82. // digitalWrite(zvonok_vcc, HIGH);
  83. // delay(300);
  84. // digitalWrite(zvonok_vcc, LOW);
  85.  
  86. Serial.println(rtc.getTimeStr());
  87.  
  88. }
  89. /****************************************************/
  90. void loop () {
  91. ether.packetLoop(ether.packetReceive()); // смотрим входящие пакеты
  92. word pos = ether.packetLoop(ether.packetReceive());
  93. if (pos) {
  94. bfill = ether.tcpOffset(); // указатель на начало полезной информации TCP. (возможно неправильно перевел)
  95. char* data = (char *) Ethernet::buffer + pos;
  96. if (strncmp("GET /?", data, 6) == 0) { // ищем во входящем заголовке HTTP строку "GET /?" (после "/" можно поставить любые символы для безопасности) длинною 6 символов и сравниваем
  97. parseGet(data, bfill); // если нашли, начинаем разбирать (парсить) строку
  98. }
  99. else {
  100. bfill.emit_p(PSTR( // если ничего нужного нам нет (т.е. на все другие HTTP запросы), шлем в ответ 404 ошибку.
  101. "HTTP/1.1 404 Not Found\r\n"
  102. "Content-Type: text/html\r\n"
  103. "\r\n"
  104. "<h1>404 Not Found</h1>")); // если зайдем через браузер, то тоже получим 404 ошибку и текст
  105. ether.httpServerReply(bfill.position()); // отправить ответ клиенту
  106. }
  107. }
  108.  
  109. if (millis() % 500 == 0) { // каждые 0,5 сек перебираем массивы
  110. for (int i = 0; i < sizeof(bells) / sizeof(String); i++) { // перебираем массив с расписанием
  111. if (bells[i] == rtc.getTimeStr()) { // если есть совпадение
  112. Serial.println(F("Звонок!!")); // то даем звонок
  113. SDwriteLog("Zvonok");
  114. zvonok(1);
  115. }
  116. }
  117. for (int i = 0; i < sizeof(settings) / sizeof(String); i++) { // перебираем массив с настройками
  118. if (i == 0 && settings[i] == rtc.getTimeStr()) { // если время в нулевом элементе массива совпадает с текущим временем, пишем в лог новый день
  119. SDwriteLog("--------- NEW DAY ---------");
  120. delay(1000);
  121. }
  122. else if (i !=0 && settings[i] == rtc.getTimeStr()) { // если же не нулевой элемент массива, запускаем синхронизацию NTP
  123. acttime();
  124. SDwriteLog("Vremya skorrectirovanno po raspisaniyu");
  125. }
  126. }
  127. }
  128. }
  129.  
  130. /************************************/
  131. static void parseGet (const char* data, BufferFiller & buf) { // функция разбора (парсинга) строки
  132. char * str; // в которой ищем комбинации ключ+значение, по типу /?nwrasp=now
  133. if (data[5] == '?') { // для команд используется первая часть выражения (то что идет до символа "=", но можно унифицировать обработку комманд
  134. // первая часть будет "command"= а потом пишите что хотите, можно использовать цифры (int), для более простой обработки
  135. // switch case и не нужно будет городить кучу if
  136. // также можно использовать интерпретацию switch case для String, но это если память позволяет
  137. char buf2[182]; // размер буфера для полезной получаемой информации
  138. if (EtherCard::findKeyVal(data + 6, buf2, sizeof(buf2) - 1, "nwrasp")) { // команда загрузки нового расписания
  139. bfill.emit_p(PSTR( // если нашли команду, шлем (подготавливаем ответ) 200 OK, чтобы браузер не ждал, и не слал повторные запросы
  140. "HTTP/1.1 200 OK\r\n"
  141. "\r\n"));
  142. ether.httpServerReply(bfill.position()); // отправляем ответ клиенту
  143.  
  144. int i = 0; // новое расписание звонков приходит в виде строки ?nwrasp=08:00:00=08:45:00.....
  145. str = strtok (buf2, "="); // разбиваем строку на части, разделителем будет "="
  146. while (str != NULL)
  147. {
  148. bells[i] = str;
  149. str = strtok (NULL, "=");
  150. i++;
  151. }
  152. SDwriteLog("Novoe raspisanie polucheno");
  153. SDwriteRasp();
  154. delay(2000); // делаем задержку на 2 сек, просто, потому что можем (убрать)
  155. }
  156. if (EtherCard::findKeyVal(data + 6, buf2, sizeof(buf2) - 1, "3bells")) { // команда подачи 3 звонков
  157. Serial.println(buf2);
  158. bfill.emit_p(PSTR(
  159. "HTTP/1.1 200 OK\r\n"
  160. "\r\n"));
  161. ether.httpServerReply(bfill.position()); // отправляем ответ клиенту
  162.  
  163. zvonok(3);
  164. }
  165.  
  166. if (EtherCard::findKeyVal(data + 6, buf2, sizeof(buf2) - 1, "acttim")) { // получаем актуальное время с arduino
  167. Serial.println(buf2); // данная комманда не сильно нужна, но с помощью нее, можно узнать текущее время на arduino
  168. // и принудительно синхронизировать время по NTP
  169. bfill.emit_p(PSTR(
  170. "HTTP/1.1 200 OK\r\n"
  171. "\r\n"));
  172. ether.httpServerReply(bfill.position()); // отправляем ответ клиенту
  173. //ether.browseUrl // будем отправлять обратным запросом время
  174. // время будет передаваться в виде JSON массива и обрабатываться JS скриптом на сайте
  175. acttime();
  176. }
  177. }
  178. }
  179.  
  180. // описание данной функции есть на сайте http://w...content-available-to-author-only...k.com/wiki/Arduino:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B/UdpNtpClient
  181. void udpReceiveNtpPacket(uint16_t dest_port, uint8_t src_ip[IP_LEN], uint16_t src_port, const char *packetBuffer, uint16_t len) {
  182. int h, m, s;
  183. unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
  184. unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
  185. unsigned long secsSince1900 = highWord << 16 | lowWord;
  186. const unsigned long seventyYears = 2208988800UL;
  187. unsigned long epoch = secsSince1900 - seventyYears;
  188.  
  189. epoch = epoch + TIMEZONE * 60 * 60; // делаем корректировку времени с поправкой на часовой пояс
  190.  
  191. h = ((epoch % 86400L) / 3600); // получаем время в привычном виде
  192. m = ((epoch % 3600) / 60);
  193. s = (epoch % 60);
  194.  
  195. rtc.setTime(h, m, s); // корректируем время
  196. SDwriteLog("Vremya skorrectirovanno");
  197. Serial.println(epoch);
  198. }
  199.  
  200. // описание данной функции есть на сайте http://w...content-available-to-author-only...k.com/wiki/Arduino:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B/UdpNtpClient
  201. void sendNTPpacket() {
  202. uint8_t ntpIp[IP_LEN]; // небольшие корректировки в функцию
  203. if (ether.dnsLookup(NTP_REMOTEHOST) == false) { // если получили DNS failed
  204. Serial.println("DNS failed"); // вручную указываем IP NTP сервера
  205. SDwriteLog("DNS failed");
  206. ntpIp[0] = 89;
  207. ntpIp[1] = 109;
  208. ntpIp[2] = 251;
  209. ntpIp[3] = 25;
  210. }
  211. else {
  212. ether.copyIp(ntpIp, ether.hisip);
  213. }
  214. //ether.printIp("NTP: ", ntpIp);
  215.  
  216. byte packetBuffer[ NTP_PACKET_SIZE];
  217. memset(packetBuffer, 0, NTP_PACKET_SIZE);
  218. packetBuffer[0] = 0b11100011;
  219. packetBuffer[1] = 2; // Тип сервера stratum, пишем 2. (можно 3)
  220. packetBuffer[2] = 6;
  221. packetBuffer[3] = 0xEC;
  222. packetBuffer[12] = 49;
  223. packetBuffer[13] = 0x4E;
  224. packetBuffer[14] = 49;
  225. packetBuffer[15] = 52;
  226.  
  227. ether.sendUdp(packetBuffer, NTP_PACKET_SIZE, NTP_LOCALPORT, ntpIp, NTP_REMOTEPORT );
  228. }
  229.  
  230. void SDwriteLog(char* data) { // функция записи логов
  231. File myFile;
  232. myFile = SD.open(LOG_FILE, FILE_WRITE); // открываем файл для записи
  233. if (myFile != false) {
  234. myFile.print(rtc.getTimeStr()); // пишем время
  235. myFile.print(" - ");
  236. myFile.println(data); // и переданные в функцию данные (строку)
  237. }
  238. else {
  239. Serial.println("Chto-to poshlo ne tak. LOG_FILE");
  240. }
  241. myFile.close();
  242. }
  243. void SDreadRasp () { // функция чтения расписания из файла
  244. String str;
  245. int i = 0;
  246. File dataFile = SD.open(ZV_FILE, FILE_READ); // открываем файл для чтения
  247. if (dataFile != false) {
  248. while (dataFile.available()) // пока не достигли конца файла
  249. {
  250. str = dataFile.readStringUntil('\n'); // читаем строчку пока не достигли символа переноса строки
  251. str.replace("\n", ""); // из полученной строчки удаляем ненужные символы
  252. str.replace("\r", ""); // (символы возврата каретки и переноса на новую строку)
  253. bells[i] = str; // пишем в массив
  254. delay (10);
  255. i++;
  256. }
  257. SDwriteLog("Raspisanie zagrujeno");
  258. }
  259. else {
  260. SDwriteLog("Chto-to poshlo ne tak. ZV_FILE");
  261. }
  262. dataFile.close();
  263. }
  264.  
  265. void SDwriteRasp () { // функция записи в файл нового расписания
  266. if (SD.exists(ZV_FILE) == true) { // если файл существует
  267. SD.remove(ZV_FILE); // удаляем его
  268. SDwriteLog("File ZV_FILE udalen");
  269. }
  270. File dataFile = SD.open(ZV_FILE, FILE_WRITE); // открываем файл для записи (если файла не существует, то он будет создан)
  271. if (dataFile != false) {
  272. for (int i = 0; i < sizeof(bells) / sizeof(String); i++) { // в цикле перебираем все элементы массива и пишем их файл
  273. if (i == ((sizeof(bells) / sizeof(String) - 1))) { // если доходим до последнего элемента массива
  274. dataFile.print(bells[i]); // то пишем его в файл уже без символа переноса на новую строчку
  275. }
  276. else {
  277. dataFile.println(bells[i]);
  278. }
  279. }
  280. Serial.println(F("Расписание записано в файл"));
  281. dataFile.close();
  282. SDwriteLog("Novoe raspisanie zagrujeno v fail");
  283. }
  284. else {
  285. SDwriteLog("Chto-to poshlo ne tak pri zapisi novogo raspisaniya. ZV_FILE");
  286. }
  287. }
  288.  
  289. void SDreadSets () { // функция аналогична чтению расписания
  290. String str;
  291. int i = 0;
  292. File dataFile = SD.open(ST_FILE, FILE_READ);
  293. if (dataFile != false) {
  294. while (dataFile.available())
  295. {
  296. str = dataFile.readStringUntil('\n');
  297. str.replace("\n", "");
  298. str.replace("\r", "");
  299. settings[i] = str;
  300. delay (10);
  301. i++;
  302. }
  303. SDwriteLog("Nastrpoyki zagrujeni iz faila");
  304. }
  305. else {
  306. SDwriteLog("Chto-to poshlo ne tak. ZV_FILE");
  307. }
  308. dataFile.close();
  309. }
  310.  
  311. void zvonok(unsigned int inch) { // функция подачи звонков
  312. Serial.println(inch); // принимает значение int - количество звонков
  313. if (inch == 1) { // если 1, то обычный звонок
  314. digitalWrite(rele_vcc, HIGH);
  315. delay(RingTime);
  316. digitalWrite(rele_vcc, LOW);
  317. }
  318. else { // если же нет
  319. for (int i = 0; i < inch; i++) { // то в цикле звоним нужное кличество раз (у меня 3)
  320. digitalWrite(rele_vcc, HIGH);
  321. delay(1000);
  322. digitalWrite(rele_vcc, LOW);
  323. delay(800);
  324. }
  325. }
  326. }
  327.  
  328. void acttime () {
  329. sendNTPpacket(); // шлем NTP запрос
  330. ether.udpServerListenOnPort(&udpReceiveNtpPacket, NTP_LOCALPORT); // получаем ответ и время в unix формате
  331. }
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cpp:22:23: fatal error: EtherCard.h: No such file or directory
 #include <EtherCard.h>
                       ^
compilation terminated.
stdout
Standard output is empty