====== Счетчик лайков с конфетами ====== {{ :projects:counter-like:counter-like_overview.jpg |}} * Платформы: Arduino * Языки программирования: Arduino (C++), PHP * Тэги: IoT Arduino, лайки, интернет вещей, facebook ===== Что это ===== Соберём счетчик отметок «Нравится» для страницы Facebook, выдающий конфету за каждый новый лайк. ===== Видеообзор ====== {{youtube>zDKKUEoomqs?large}} ===== Что понадобится ===== {{ :projects:counter-like:counter-like_parts.jpg |}} - [[amp>product/iskra-neo?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Iskra Neo]] - [[amp>product/arduino-troyka-slot-shield?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Troyka Slot Shield]] - [[amp>product/troyka-wi-fi?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Wi-Fi (Troyka-модуль)]] - [[amp>product/troyka-quad-display?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Четырёхразрядный индикатор]] - [[amp>product/troyka-button?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Кнопка (Troyka-модуль)]] - [[amp>product/servo-fs90?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Микросервопривод FS90]] - [[amp>product/usb-cable-micro?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Кабель USB (A — Micro USB)]] - [[amp>product/usb-power-plug-2a?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Импульсный блок питания с USB-разъемом (5 В, 2100 мА)]] - Штырьковые соединители угловые (1×40) - Оргстекло: 400×300×3 мм - Фанера ФК: 600×400×6 мм - Фанера ФК: 400×300×18 мм - Крепёжные материалы: винты и гайки - Конфеты «Skittles» ===== Как собрать ===== Сборка устройства состоит из аппаратной и программной части. ==== Hardware-часть ==== - Установите Troyka Slot Shield на Iskra Neo.{{ :projects:counter-like:counter-like_build1.jpg |}} - Подключите Wi-Fi (Troyka-модуль) к Troyka Slot Shield: - пин ''TX'' Wi-Fi модуля к цифровому пину ''0'' Troyka Slot Shield - пин ''RX'' — к цифровому пину ''1'' Troyka Slot Shield.{{ :projects:counter-like:counter-like_build2.jpg |}} - Вставьте Четырёхразрядный индикатор (Troyka-модуль) на Troyka Slot Shield: - пин ''CS'' дисплея к цифровому пину ''9'' Troyka Slot Shield - пин ''DI'' — к пину ''DO'' Troyka Slot Shield.{{ :projects:counter-like:counter-like_build3.jpg |}} - Подключите кнопку (Troyka-модуль) к цифровому пину ''2'' Troyka Slot Shield.{{ :projects:counter-like:counter-like_build4.jpg |}} - Подключите сервопривод через угловые штырьки к цифровому пину ''3'' Troyka Slot Shield.{{ :projects:counter-like:counter-like_build5.jpg |}} - Установите собранную конструкцию на корпус устройства.{{ :projects:counter-like:counter-like_build6.jpg |}} - На обратной стороне корпуса через специальные вставки закрепите сервопривод.{{ :projects:counter-like:counter-like_build7.jpg |}} Общий вид устройства.{{ :projects:counter-like:counter-like_build8.jpg |}} Переходите к программной части. ==== Software-часть ==== Социальные сети: [[https://www.facebook.com/|Facebook]], [[https://vk.com/|VK]] и другие работают через протокол [[https://ru.wikipedia.org/wiki/HTTPS|HTTPS]]. Микроконтроллер ESP8266 на борту платы [[amp>product/troyka-wi-fi?utm_source=announce&utm_campaign=mf17_robot&utm_medium=wiki | Wi-Fi (Troyka-модуль)]] в исходной конфигурации работает через [[https://ru.wikipedia.org/wiki/AT-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B|АТ-команды]]. В результате работа напрямую с HTTPS-протоколом не возможна. Для решения проблемы используем небольшой php-скрипт, который необходимо разместить на хостинге. В качестве примера воспользуемся бесплатным хостингом [[https://www.000webhost.com/|«000webhost»]] - Зарегистрируйтесь на хостинге. В результате прохождение всех процедур регистрации хостинга и домена приведет к следующим результатам: наличие полного доменного имени и доступа к файловой системе на предоставленном сервере. В нашем случае: - Полное доменное имя: http://amperkalikes.000webhostapp.com - Доступ авторизованного пользователя к файловой системе по адресу: https://files.000webhost.com - Скачайте и откройте [[projects:counter-like#php-скрипт|php-скрипт]]. Данный скрипт позволяет открыть целевую страницу «facebook», получить искомое количество лайков на странице и выгрузить его в формате: ''fan_count=xxxx''. Чтобы скрипт выдавал количество лайков вашей страницы, его требуется изменить. Для этого нужно внести изменения в переменную ''$id'', определенную в третьей строке кода скрипта. Значение переменной следует заменить на значение ''id'' вашей страницы «facebook». Чтобы узнать значение ''id'' вашей страницы, нужно перейти на интересующую нас страницу и проанализировать ее адрес. В качестве примера возьмем адрес facebook-страницы Амперки:https://www.facebook.com/amperka.ru/?fref=ts''id'' страницы расположено между слешами после домена ''www.facebook.com'', следовательно, ''id-страницы'' — «amperka.ru» Присвоим это значение переменной $id='amperka.ru'. Теперь разместим наш модифицированный скрипт на сервере. В нашем случае нужно перейти по адресу: https://files.000webhost.com и простым перетягиванием ярлыка файла скрипта (drag-and-drop) перенести файл ''getLikes.php'' в файловую систему сервера. В заключении проверьте успешность работы скрипта. Для этого перейдите по адресу:http://amperkalikes.000webhostapp.com/getLikes.phpЕсли при этом веб-браузер отобразит пустое окно с единственной строкой: ''fan_count=4,218'' (число может быть иным), то все сделано верно. ===== Исходный код ===== ==== Программа для Arduino ==== // библиотека для работы с сервоприводами #include // библиотека для работы с дисплеем #include "QuadDisplay2.h" #include "TimeCheck.h" // буффер запроса char serialbuffer[100]; // создаём объект класса QuadDisplay и передаём номер пина CS QuadDisplay qd(9); // создаём объект для работы с сервоприводом Servo srv; TimeCheck butTime(500); TimeCheck timeOut(5000); TimeCheck autoRun(1000); int but = 2; int srvPin = 3; int ang0 = 110; int ang1 = 140; String wifi = "nameWiFi"; String passwordWifi = "passwordWiFi"; String host = "amperkalikes.000webhostapp.com"; String link = "getLikes.php"; String cmdWifi; String cmdHost; String cmdLink; int pIndex = -1; boolean autoRunOn = false; int fanReceived = -1; int fanMemory = -1; String makeWifi() { String toReturn = "AT+CWJAP=\""; toReturn += wifi; toReturn += "\",\""; toReturn += passwordWifi; toReturn += "\""; return toReturn; } String makeGet() { String toReturn = "GET /"; toReturn += link; toReturn += " HTTP/1.1\r\nHost:"; toReturn += host; toReturn += "\r\n\r\n\r\n"; return toReturn; } String makeHost() { String toReturn = "AT+CIPSTART=\"TCP\",\""; toReturn += host; toReturn += "\",80"; return toReturn; } void setup() { Serial1.begin(115200); Serial.begin(115200); qd.begin(); qd.displayInt(-42); pinMode(but, INPUT); srv.write(ang0); srv.attach(srvPin); srv.write(ang0); cmdWifi = makeWifi(); cmdHost = makeHost(); cmdLink = makeGet(); qd.displayInt(-3); Serial.print("cmdWifi = "); Serial.println(cmdWifi); Serial.print("cmdHost = "); Serial.println(cmdHost); Serial.print("cmdLink = "); Serial.println(cmdLink); } void loop() { if(timeOut.check() && pIndex != -1) { Serial.print("timeOut.check() : last pIndex = "); Serial.println(pIndex); pIndex = -1; } if(buttCheck()) { autoRunOn = !autoRunOn; } switch(pIndex) { case -1: if(autoRun.check() && autoRunOn) { Serial.println("autoRun ON +++++++++++++"); pIndex = 0; } break; case 0: checkStatConn(); pIndex++; timeOut.defer(); break; case 10: startTCP(); pIndex++; timeOut.defer(); break; case 20: requestLikes(); pIndex++; timeOut.defer(); break; case 30: qd.displayInt(fanReceived); fanCheck(); pIndex = -1; autoRun.defer(); break; } while(Serial1.available() > 0) { String tmp = Serial1.readStringUntil('\n'); if (true) { Serial.println(tmp); } int valueStat = getStatus(tmp); switch(valueStat) { //got IP, TCP connection next case 2: pIndex = 10; break; //TCP is good, getLikes next case 3: pIndex = 20; break; //got IP, TCP connection next case 4: pIndex = 10; break; //Problems with WiFi connection case 5: Serial.println("Check Wi-Fi settings"); Serial.print("Wifi settings now : "); Serial.println(cmdWifi); Serial1.println("AT+CWMODE=3"); delay(2000); Serial1.println(cmdWifi); pIndex = -1; break; } if (getConnect(tmp)) { pIndex = 20; } if(getFanCount(tmp) > 0) { pIndex = 30; } } } boolean buttCheck() { if(!digitalRead(but) && butTime.check()) { return true; } else { return false; } } void checkStatConn() { Serial1.println("AT+CIPSTATUS"); } void startTCP() { Serial.println("startTCP"); Serial1.println(cmdHost); } int getStatus(String input) { if (!input.startsWith("STATUS:")) return -1; input.replace("STATUS:",""); return input.toInt(); } boolean getConnect(String input) { if (!input.startsWith("CONNECT")) { return false; } return true; } void doRequest(String input) { Serial1.print("AT+CIPSEND="); Serial1.println(input.length()); if(Serial1.find(">")){ Serial.println(">"); } else { Serial1.println("AT+CIPCLOSE"); Serial.println("Connect timeout"); return; } Serial1.print(input); } void requestLikes() { doRequest(cmdLink); } int getFanCount(String input) { if (!input.startsWith("fan_count=")) { return -1; } input.replace("fan_count=",""); input.replace(",",""); fanReceived = input.toInt(); Serial.print("getFanCount() : fanReceived = "); Serial.println(fanReceived); return input.toInt(); } void giveCandy() { srv.write(ang1); delay(2000); srv.write(ang0); delay(2000); } void fanCheck() { if (fanReceived - fanMemory > 20 || fanReceived < fanMemory) { fanMemory = fanReceived; } else { while (fanMemory < fanReceived) { giveCandy(); ++fanMemory; } } } ==== PHP-скрипт ==== <?php $id='amperka.ru'; $opts = array( 'http'=>array( 'method'=>"GET", 'header'=>"Accept-language: en\r\n" . "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n", "Cookie: foo=bar\r\n" ) ); $context = stream_context_create($opts); // Open the file using the HTTP headers set above $file = file_get_contents('https://www.facebook.com/plugins/fan.php?connections=100&id='.$id, false, $context); //echo $file; $target = '_1drq'; $target1 = ';">'; $target2 = ' '; $pos = strstr($file, $target); $pos = strstr($pos, $target1); $pos = strstr($pos, $target2, true); $pos = substr($pos, strlen($target1)); print('fan_count='); print_r($pos); //echo $pos; ?> ==== Ресурсы ==== * Библиотека [[https://github.com/amperka/TimeCheck|TimeCheck]] для работы выше приведённого скетча. * {{:projects:counter-like:amperkacandymachine.zip|Исходник для изготовления корпуса}}