Обнаружение BLE устройств с помощью модуля ESP32


Датчики приближения (proximity sensors) предназначены для обнаружения близкорасположенных объектов с помощью света, электромагнитного поля или звука. В ряду случаев для обнаружения близко расположенных объектов целесообразно использовать технологию BLE (Bluetooth Low Energy – Bluetooth с низким энергопотреблением). Для создания подобного датчика можно использовать модуль ESP32, поскольку он имеет встроенную поддержку BLE.

Проект детектора BLE устройств на основе модуля ESP32

В данной статье мы рассмотрим создание детектора BLE устройств на основе модуля ESP32. Ранее на нашем сайте мы рассматривали использование технологии BLE в ESP32 для соединения с фитнес браслетом.

Необходимые компоненты

  1. Модуль ESP32 (купить на AliExpress) (Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158).
  2. Смартфон.
  3. Умные часы.

Внешний вид компонентов, необходимых для нашего проекта, показан на следующем рисунке.

Внешний вид компонентов, необходимых для нашего проекта

Основные принципы работы технологии BLE

BLE (Bluetooth Low Energy) – это Bluetooth с низким потреблением. Технология BLE была разработана в 2011 г. и с тех пор находит широкое применение для связи на короткие расстояния в устройствах, получающих питание от батареек/аккумуляторов: смартфоны, умные часы, беспроводные гарнитуры, беспроводные колонки и т.п.

Значок технологии Bluetooth

Технология BLE была разработана специальной группой по интересам (Special Interest Group, SIG) Bluetooth. Основной целью создания технологии было максимальное снижение энергопотребления устройств. Хотя в названии технологии BLE есть слово "Bluetooth", тем не менее, данная технология не обладает обратной совместимостью, то есть классические Bluetooth устройства не могут осуществлять прием данных от устройств BLE. Но зато это позволило разработчикам создать технологию, благодаря которой беспроводные устройства могут работать от крошечной батарейки несколько месяцев (или даже лет).

Технология BLE использует иерархическую структуру данных для передачи и приема информации. BLE устройство, работающее в качестве сервера, предоставляет услуги и характеристики, которые могут быть обнаружены клиентом. И как только произойдет обмен информацией между клиентом и сервером, эти два устройства смогут работать друг с другом одновременно. Этот стек (совокупность) информации является атрибутом (отличительным свойством) BLE устройства. Данная информация задается с помощью профиля GATT (Generic Attributes). Данный профиль содержит услуги/службы (Service), характеристики (Characteristics) и значения (values) в иерархическом порядке. Службы содержат характеристики, а характеристики содержат значения. Считывая характеристики, мы можем считывать их значения и изменять их.

Структура GATT профиля представлена на следующем рисунке.

Структура GATT профиля в технологии BLE

Характеристики (Characteristics) могут быть использованы для чтения или записи информации. Устройства, содержащие компоненты для чтения, могут публиковать (publish) информацию, а устройства, содержащие компоненты для записи, могут принимать данные от клиента.

GATT профиль технологии BLE также известен как UUID (Universally Unique Identifier – универсальный уникальный идентификатор). Существует ряд стандартных служб и характеристик, определенных корпорацией SIG. Если мы считываем UUID BLE устройства, мы сразу можем определить тип этого устройства.

Данные BLE передаются и принимаются с помощью очень коротких пакетов. Пакет BLE содержит всего 31 байта, в то время как, например, пакет протокола TCP может содержать более 60 байт. Важным правилом при работе с технологией BLE является то, что ее пакеты должны иметь строго определенную структуру и байты этих пакетов должны передаваться и приниматься последовательно.

Как работает датчик приближения на основе технологии BLE

Как уже говорилось ранее, датчики приближения могут обнаруживать близко расположенные объекты с помощью света, электромагнитного поля или звука.

Принцип обнаружения клиентом сервера в технологии BLE

BLE серверы регулярно передают в эфир сигналы приветствия (advertisement signals), поэтому клиенты могут обнаружить эти сигналы и, таким образом, подключиться к серверам. Эти сигналы приветствия содержат уникальный BLE MAC (Media Access Control) адрес, который очень похож на MAC адрес, используемый в технологии Wi-Fi. Поскольку модуль ESP32 имеет встроенную поддержку Bluetooth, с его помощью мы можем легко обнаружить эти транслируемые сигналы приветствия и сравнить их с таблицей соответствия чтобы обнаружить присутствие необходимого нам устройства.

Когда устройство обнаружено и проверено на соответствие адреса, мы можем, к примеру, включить светодиод, подключенный к модулю ESP32, или с помощью сервиса Adafruit IO отправить уведомление на смартфон с операционной системой android.

Объяснение программы для модуля ESP32

Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.

В целях демонстрации работы проекта мы запрограммируем модуль ESP32 на обнаружение известных BLE маяков (beacons) и когда мы будем обнаруживать такой маяк, мы будем зажигать встроенный в модуль ESP32 светодиод.

Первым делом в начале программе мы подключим используемые библиотеки и объявим необходимые переменные. Поскольку мы будем использовать класс BLEScan, то в программе мы должны подключить соответствующую библиотеку для работы с ним.

Далее объявим ряд необходимых переменных, первой из которых будет массив, в котором мы будем хранить известные BLE MAC адреса. Далее мы объявим значение границы (минимальный уровень сигнала), при превышении которой будет фиксироваться факт обнаружения устройства и выполняться необходимое действие. Далее объявим переменную логического типа (Boolean) которую мы будем устанавливать в true если одно из просканированных (обнаруженных) устройств содержится в нашем массиве с известными адресами. Также мы объявим еще одну переменную чтобы очищать уже просканированные BLE устройства, если мы не будем делать этого, то рано или поздно возникнут проблемы с переполнением памяти. И еще мы объявим указатель на класс BLEScan.

Также мы запрограммируем функцию обратного вызова  (call-back function), сначала эта функция будет вызываться каждые несколько секунд чтобы проверить присутствуют ли в зоне действия нашего модуля новые BLE устройства или нет. Внутри этой функции при обнаружении BLE устройства мы будем устанавливать соответствующий флаг и осуществлять выход из цикла. И наконец, мы будем выводить на экран информацию об обнаруженном BLE устройстве.

Далее в функции setup мы зададим режим работы контакта, к которому подключен светодиод, на вывод данных. Также мы инициализируем BLE устройство (BLEDevice) с помощью метода BLEDevice::init, после этого мы получим просканированный объект от нашего BLEDevice, мы сохраним этот адрес в ранее объявленном указателе на pBLEScan.

Затем мы инициализируем функцию обратного вызова – она будет вызываться каждые несколько секунд чтобы проверить доступны ли новые устройства или нет. Далее мы используем метод SetActiveScan чтобы установить активный режим сканирования – данный режим потребляет больше энергии, но позволяет быстрее получить результаты сканирования. Также установим интервал и окно сканирования с помощью методов setInterval и setWindow.

В функции loop мы запустим обнаружение BLE устройств. Параметр scanTime определяет в течение какого времени будет производиться сканирование новых устройств.

Когда процесс сканирования будет завершен, мы сможем узнать количество найденных устройств с помощью метода devices.getCount(). Мы поместим вызов этого метода в цикл. В данном цикле мы будем проверять уровень сигнала (RSSI) от каждого обнаруженного устройства. После этого мы будем сравнивать значение уровня сигнала с ранее заданной границей и проверять находится ли устройство в нашем списке или нет. Если мы обнаружим известное нам устройство (оно есть в нашем списке), то мы будем включать встроенный в модуль ESP32 светодиод, иначе мы будем выключать светодиод.

Затем мы будем удалять результаты сканирования чтобы очистить память.

После того как вы загрузите код программы в модуль ESP32, вы сможете с его помощью обнаруживать известные BLE устройства, когда они находятся рядом с вами. Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.

Исходный код программы (скетча)

Видео, демонстрирующее работу проекта

(Проголосуй первым!)
Загрузка...
5 230 просмотров

Комментарии

Обнаружение BLE устройств с помощью модуля ESP32 — 42 комментария

    • Добрый день, в начале описания кода указано что нужны библиотеки BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h. Все они подключаются в начале программы с помощью директив include

  1. Здравствуйте,
    У меня вопрос а как можно реализовать на две реле? Моя задумка для замка. На нём две кнопки без фиксации вкл и выкл. Буду очень благодарен за совет.

  2. Если кому то будет интересно, вот что у меня получилось для super mini(очень маленькая плата), да, криво но знаний на большее нету, на пару c ИИ от Гугл делал ))). У меня была цель сделать именно датчик присутствия рядом BLE устройства. Уверен тут можно много чего упростить, и добавить\улучшить. На сколько будет стабильно работать буду тестить на включении света за рабочим столом.

    #include
    #include
    #include
    #include

    const char* knownBLEAddresses[] = {"e4:db:4d:14:aa:88"};
    bool device_found = false;
    const int scanTime = 5; // в секундах
    BLEScan* pBLEScan;

    class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    public:
    void onResult(BLEAdvertisedDevice advertisedDevice) {
    if (strcmp(advertisedDevice.getAddress().toString().c_str(), "e4:db:4d:14:aa:88") == 0) { // мас адрем которы сканируем
    device_found = true;
    }
    }
    };

    void setup() {
    Serial.begin(115200); // Включить UART на ESP32
    Serial.println("Scanning..."); // Печатать Scanning
    pinMode(10, OUTPUT); // режим работы контакта BUILTIN_LED на вывод данных
    BLEDevice::init("");
    pBLEScan = BLEDevice::getScan(); // создать новое сканирование
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(true); // активное сканирование использует больше энергии, но быстрее дает результаты
    pBLEScan->setInterval(100); // установить интервал сканирования
    pBLEScan->setWindow(99); // установить окно сканирования, менее или равно значению setInterval
    }

    void loop() {
    BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
    for (int i = 0; i -80 || foundDevices.getCount() == 0) { // уровень сигнала при котором срабатыват датчик присутсвия
    digitalWrite(10, HIGH);
    } else {
    digitalWrite(10, LOW);
    }
    }
    }
    }

    • Я рад что у вас получилось. Спасибо вам за конструктивный комментарий для нашего сайта

    • Здравствуйте!
      Подскажите, пожалуйста, какие библиотеки подключены в #include, в начале кода.
      Сайт их затирает.

      Спасибо!

  3. Подскажите пожалуйста, будет(должен) ли работать данный скеч на плате esp32 c3 super mini ? При компиляции ошибка.

      • C компиляцией разобрался, была проблема с библиотеками, все прошилось но реакции нету, как включить монитор порта(или как там это называется) пока не разобрался. Понять что там сейчас происходит не знаю как, работает ли программа на данном железе корректно непонятно.

        • Вы имеете ввиду последовательный порт на плате или монитор последовательного порта на компьютере?

          • монитор, для работы --- отдельно галку поставить надо было для платы esp32 c3 super mini

            Вот что он выдает, мак браслета прописал такой --- e4:db:4d:14:aa:88

            f6:47:7e:79:2b:11
            e4:db:4d:14:aa:88
            Advertised Device: Name: , Address: f6:47:7e:79:2b:11, manufacturer data: 4c0012020000, rssi: -88
            16
            16
            1
            65:aa:bb:2a:63:05
            e4:db:4d:14:aa:88
            Advertised Device: Name: , Address: 65:aa:bb:2a:63:05, manufacturer data: 4c001007171b8701f08328, txPower: 12, rssi: -88
            16
            16
            1
            e4:db:4d:14:aa:88
            e4:db:4d:14:aa:88
            Advertised Device: Name: Mi Smart Band 4, Address: e4:db:4d:14:aa:88, manufacturer data: 570102ffffffffffffffffffffffffffffffff02e4db4d14aa88, serviceUUID: 0000fee0-0000-1000-8000-00805f9b34fb, rssi: -54, serviceData: ⸮
            16
            16
            1
            d0:d0:03:f2:28:f1
            e4:db:4d:14:aa:88
            Advertised Device: Name: , Address: d0:d0:03:f2:28:f1, manufacturer data: 75004204012067200d0002013701010001000000000000000000, rssi: -86
            16
            16
            1

            Светодиод пробую использовать тот который на плате подключен на 8 пин, если менять состояние HIGH\LOW
            в коде программы вот тут
            if (rssi > RSSI_THRESHOLD && device_found == true)
            digitalWrite(8, HIGH);
            else
            digitalWrite(8, LOW);

            то он перестает гореть при запуске программы, но именно на браслет реакции нет ((

            • А попробуйте вывести в монитор последовательного порта значение переменной device_found. Становится она true когда браслет подносите или нет

            • Навыков писать код совсем мало, сделал как получилось.

              вот что в мониторе получается

              device_found: 0
              Advertised Device: Name: HUAWEI Band 7-FAE, Address: 54:55:d5:b1:2f:ae, manufacturer data: 7d02010300ffff, txPower: 4, rssi: -87, serviceData: TUձ/⸮
              device_found: 0
              Advertised Device: Name: , Address: 84:c2:e4:1f:40:14, manufacturer data: baab100000001a040019000004c2010131000000, serviceUUID: 0000ba00-0000-1000-8000-00805f9b34fb, rssi: -86
              device_found: 0
              Advertised Device: Name: BATAIRBatt 1 0000, Address: 84:c2:e4:d6:cb:2e, manufacturer data: baab10000000cc050e190500046c020134530000, serviceUUID: 0000ba00-0000-1000-8000-00805f9b34fb, rssi: -17
              device_found: 1
              Advertised Device: Name: mi_mtk, Address: 5c:e5:0c:ad:4e:97, manufacturer data: 8f030a10640200964ead0ce55c81, rssi: -72
              device_found: 0
              Advertised Device: Name: , Address: 47:10:2e:f2:25:c9, serviceUUID: 0000fef3-0000-1000-8000-00805f9b34fb, rssi: -77, serviceData: J#16002⸮5⸮_D⸮⸮⸮2⸮⸮⸮⸮|⸮
              device_found: 0
              Advertised Device: Name: BATAIRBatt 2 0000, Address: 84:c2:e4:d6:d8:42, manufacturer data: baab10000000e00516190400046c020134530000, serviceUUID: 0000ba00-0000-1000-8000-00805f9b34fb, rssi: -78
              device_found: 0
              Advertised Device: Name: Mi Smart Band 4, Address: d9:99:2b:0d:75:d5, manufacturer data: 570102ffffffffffffffffffffffffffffffff02d9992b0d75d5, serviceUUID: 0000fee0-0000-1000-8000-00805f9b34fb, rssi: -86
              device_found: 0
              Advertised Device: Name: , Address: a4:c1:38:8f:f8:35, rssi: -77, serviceData: 0X[5⸮⸮8⸮⸮
              device_found: 0
              Advertised Device: Name: , Address: f8:a2:6d:d1:a3:65, manufacturer data: 4c0003161100000277c0a86457000000000000000000000000c0, rssi: -99
              device_found: 0
              Advertised Device: Name: , Address: 72:61:b8:85:e4:7f, manufacturer data: 4c0010051f1c379f27, txPower: 12, rssi: -10
              device_found: 0
              Advertised Device: Name: , Address: e4:db:4d:14:aa:88, manufacturer data: 570102ffffffffffffffffffffffffffffffff02e4db4d14aa88, rssi: -67
              device_found: 0
              Advertised Device: Name: , Address: 74:6c:d2:f1:0d:21, serviceUUID: 0000fd69-0000-1000-8000-00805f9b34fb, rssi: -93, serviceData: ⸮⸮@4T⸮⸮⸮d2⸮
              R
              device_found: 0
              RSSI: -95
              RSSI: -70
              RSSI: -95
              RSSI: -77
              RSSI: -98
              RSSI: -87
              RSSI: -88
              RSSI: -72
              RSSI: -93
              RSSI: -101
              RSSI: -93
              RSSI: -86
              RSSI: -17
              RSSI: -78
              RSSI: -77
              RSSI: -97
              RSSI: -93
              RSSI: -85
              RSSI: -86
              RSSI: -67
              RSSI: -99

              искомое устройство вот это --- 84:c2:e4:d6:cb:2e , добавил еще задержку секунду в коде, в конце, и видно что диод периодически загорается на секунду. Но как то не стабильно, через раз, на разном расстоянии пробовал, закономерности какой то не нашел.

  4. Привет, в колбеке находит,все RSSI получаю, на светодиод не горит, не знаю в чем проблема

    • Добрый день, небольшая недоработка в коде. LED_BUILTIN не объявлен в программе, вам нужно либо его объявить, либо вручную прописать номер контакта, к которому у вас подключен встроенный светодиод модуля.

  5. Я похоже понял это либо русские буквы в названии либо , пробел в названии

    • Да, скорее всего. А у вас имя устройства, которое вы хотите обнаруживать, написано с русскими буквами или пробелами?

        • Ну я рад что у вас получилось. Буду признателен если потом, после того как соберете проект в железе, отпишитесь у нас здесь о своих успехах и возможных возникших проблемах

          • Добрый день! Ну вот я попробовал, не получается. реле пока не подключал, подключился на пин 2 то есть должен загораться светодиод синий Но не загорается, в мониторе порта (22:55:02.626 -> Advertised Device: Name: realme Watch 2 Pro, Address: c4:2b:3d:90:41:50, manufacturer data: 6000c42b3d904150, serviceUUID: 0000a500-0000-1000-8000-00805f9b34fb} и находит несколько устройств но светодиод не загорается
            пробовал тестовый Blink загрузить светодиод моргает как положено!

            • Ну попробуйте в этом месте делать что нибудь другое, что ваш модуль точно может делать. Тут варианта всего два, либо вы никаких устройств не находите и тогда условие включения светодиода не срабатывает, либо вы в программе просто не настроили контакт для работы со встроенным светодиодом

  6. вот скейч, ошибок не выделяет, компилирует но дополнительно выдает красным (gen_esp32part.py:507: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal)

    #include
    #include
    #include
    #include
    int relay = 13;
    String knownBLEAddresses[] = {"c4:2b:3d:90:41:50", "10:82:d7:ee:40:c2", "40:99:4b:75:7d:2f", "5c:5b:68:6f:34:96"};
    int RSSI_THRESHOLD = -55;
    bool device_found;
    int scanTime = 5; //в секундах
    BLEScan* pBLEScan;
    class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
    for (int i = 0; i setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //Init Callback Function (инициализируем функцию обратного вызова)
    pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
    pBLEScan->setInterval(100); // устанавливаем интервал сканирования
    pBLEScan->setWindow(99); // устанавливаем окно сканирования, менее или равно значению setInterval
    }
    void loop()
    {
    // put your main code here, to run repeatedly:
    BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
    for (int i = 0; i RSSI_THRESHOLD && device_found == true)
    digitalWrite(relay, HIGH);
    else
    digitalWrite(relay, LOW);
    }
    pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
    }

  7. void setup() {
    Serial.begin(115200); //Enable UART on ESP32
    Serial.println("Scanning..."); // Print Scanning
    BLEDevice::init("");
    pBLEScan = BLEDevice::getScan(); //создаем новое сканирование
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //Init Callback Function (инициализируем функцию обратного вызова)
    pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
    pBLEScan->setInterval(100); // устанавливаем интервал сканирования
    pBLEScan->setWindow(99); // устанавливаем окно сканирования, менее или равно значению setInterval
    pinMode (13,OUTPUT); //режим работы контакта BUILTIN_LED на вывод данных
    }
    void loop()
    {
    // put your main code here, to run repeatedly:
    BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
    for (int i = 0; i RSSI_THRESHOLD && device_found == true)
    digitalWrite(13, HIGH);
    else
    digitalWrite(13, LOW);
    }
    pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
    } ну вот так же будет работать, просто плата еще в пути ? но скомпелировалось нормально. вle адреса по количеству ограничений нет?

    • Ну по идее должно работать. В комментариях, к сожалению, некоторые символы вырезаются в коде из-за соображений безопасности

  8. Добрый день подскажите пожалуйста какова дальность обнаружения устройств? скажем два с половиной- три метра ???

    • Добрый вечер. Ну в идеале до 10 метров, ведь BLE - это технология маленького радиуса действия

      • Спасибо 10 м достаточно. А сколько нужно выставить в сроке где доступность чтобы не как в видео на длину клавиатуры?

        • А какую строку вы имеете ввиду? Не совсем понял. Дальность обанружения устройств в представленном проекте можно регулировать с помощью изменения константы RSSI_THRESHOLD = -55, с помощью которой задается минимальный уровень сигнала, при котором мы устройство считаем обнаруженным. Если мы уменьшим это значение, то дальность обнаружения увеличится, но при этом ухудшится надежность и достоверность (при передаче будет возникать больше ошибок) связи из-за того что уменьшится значение сигнал/шум. Насколько, не знаю, здесь нужно экспериментировать

          • спасибо за ответы!
            но при компиляции выходит ошибка! моя голова не понимает, поможете разобраться?
            C:\Users\vobli\AppData\Local\Temp\.arduinoIDE-unsaved202308-15752-168hmdh.67f6\sketch_jan8c\sketch_jan8c.ino: In function 'void setup()':
            C:\Users\vobli\AppData\Local\Temp\.arduinoIDE-unsaved202308-15752-168hmdh.67f6\sketch_jan8c\sketch_jan8c.ino:36:11: error: 'LED_BUILTIN' was not declared in this scope
            pinMode(LED_BUILTIN, OUTPUT); //режим работы контакта BUILTIN_LED на вывод данных
            ^
            C:\Users\vobli\AppData\Local\Temp\.arduinoIDE-unsaved202308-15752-168hmdh.67f6\sketch_jan8c\sketch_jan8c.ino: In function 'void loop()':
            C:\Users\vobli\AppData\Local\Temp\.arduinoIDE-unsaved202308-15752-168hmdh.67f6\sketch_jan8c\sketch_jan8c.ino:54:20: error: 'LED_BUILTIN' was not declared in this scope
            digitalWrite(LED_BUILTIN, HIGH);
            ^
            C:\Users\vobli\AppData\Local\Temp\.arduinoIDE-unsaved202308-15752-168hmdh.67f6\sketch_jan8c\sketch_jan8c.ino:56:20: error: 'LED_BUILTIN' was not declared in this scope
            digitalWrite(LED_BUILTIN, LOW);
            ^

            exit status 1

            Compilation error: 'LED_BUILTIN' was not declared in this scope

            • разобрался ! вместо """pinMode(LED_BUILTIN, OUTPUT)"" поставил pinMode (13,OUTPUT).

            • Эх, опечатка. Почему то в коде программы пропущено объявление в начале LED_BUILTIN

          • Попытался сделать правильно, не знаю что у меня получилось Ну что-то типа того выдала
            (gen_esp32part.py:507: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal

            Скетч использует 963482 байт (73%) памяти устройства. Всего доступно 1310720 байт.
            Глобальные переменные используют 30424 байт (9%) динамической памяти, оставляя 297256 байт для локальных переменных. Максимум: 327680 байт.)

            • Это на какую строчку кода вам выдалась эта ошибка?

          • Похоже понятно что это не опечатка, если посмотреть на предпоследнее мое сообщение то видно что сайт при добавлении что-то обрезает! Я полностью скопировал свой код и скейча а при просмотре сообщения вижу что названия библиотек нет. Надо готовые компилированные скетчи прикладывать

            • Да, в целях безопасности система автоматического модерирования комментариев настроена так, что некоторые специальные символы она обрезает. Что поделать, приходится выбирать какой то компромисс между "добром" и "злом", спамеры и хакеры, к сожалению, не дремлют. Но в вашем скетче она ничего особо существенно не обрезала, только названия добавляемых библиотек, а их можно взять из скетча статьи, я думаю, это интуитивно всем понятно. Но если есть желание можно, к примеру, загрузить скетч на яндекс диск, а здесь ссылку написать на его скачивание. Я такие ссылки не удаляю, они безопасные для сайта

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *