Счетчик лайков с конфетами

  • Платформы: Arduino
  • Языки программирования: Arduino (C++), PHP
  • Тэги: IoT Arduino, лайки, интернет вещей, facebook

Что это

Соберём счетчик отметок «Нравится» для страницы Facebook, выдающий конфету за каждый новый лайк.

Видеообзор

Что понадобится

  1. Штырьковые соединители угловые (1×40)
  2. Оргстекло: 400×300×3 мм
  3. Фанера ФК: 600×400×6 мм
  4. Фанера ФК: 400×300×18 мм
  5. Крепёжные материалы: винты и гайки
  6. Конфеты «Skittles»

Как собрать

Сборка устройства состоит из аппаратной и программной части.

Hardware-часть

  1. Установите Troyka Slot Shield на Iskra Neo.
  2. Подключите Wi-Fi (Troyka-модуль) к Troyka Slot Shield:
    1. пин TX Wi-Fi модуля к цифровому пину 0 Troyka Slot Shield
    2. пин RX — к цифровому пину 1 Troyka Slot Shield.
  3. Вставьте Четырёхразрядный индикатор (Troyka-модуль) на Troyka Slot Shield:
    1. пин CS дисплея к цифровому пину 9 Troyka Slot Shield
    2. пин DI — к пину DO Troyka Slot Shield.
  4. Подключите кнопку (Troyka-модуль) к цифровому пину 2 Troyka Slot Shield.
  5. Подключите сервопривод через угловые штырьки к цифровому пину 3 Troyka Slot Shield.
  6. Установите собранную конструкцию на корпус устройства.
  7. На обратной стороне корпуса через специальные вставки закрепите сервопривод.

Общий вид устройства.

Переходите к программной части.

Software-часть

Социальные сети: Facebook, VK и другие работают через протокол HTTPS. Микроконтроллер ESP8266 на борту платы Wi-Fi (Troyka-модуль) в исходной конфигурации работает через АТ-команды. В результате работа напрямую с HTTPS-протоколом не возможна.

Для решения проблемы используем небольшой php-скрипт, который необходимо разместить на хостинге. В качестве примера воспользуемся бесплатным хостингом «000webhost»

  1. Зарегистрируйтесь на хостинге. В результате прохождение всех процедур регистрации хостинга и домена приведет к следующим результатам: наличие полного доменного имени и доступа к файловой системе на предоставленном сервере. В нашем случае:
    1. Полное доменное имя:
      http://amperkalikes.000webhostapp.com
    2. Доступ авторизованного пользователя к файловой системе по адресу:
      https://files.000webhost.com
  2. Скачайте и откройте 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

counter-like.ino
// библиотека для работы с сервоприводами
#include <Servo.h>
// библиотека для работы с дисплеем
#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-скрипт

getLikes.php
&lt;?php
 
&#36;id='amperka.ru';

&#36;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"
  )
);
 
&#36;context = stream_context_create(&#36;opts);

// Open the file using the HTTP headers set above
&#36;file = file_get_contents('https://www.facebook.com/plugins/fan.php?connections=100&id='.&#36;id, false, &#36;context);
//echo &#36;file;
 
&#36;target  = '_1drq';
&#36;target1 = ';">';
&#36;target2 = ' ';

&#36;pos = strstr(&#36;file, &#36;target);
&#36;pos = strstr(&#36;pos, &#36;target1);
&#36;pos = strstr(&#36;pos, &#36;target2, true);
&#36;pos = substr(&#36;pos, strlen(&#36;target1));
print('fan_count=');
print_r(&#36;pos);
//echo &#36;pos;
 
?&gt;

Ресурсы