Lwip устройство в роутере

Русские Блоги

HTTP Протокол передачи гипертекста (HyperText Transfer Protocol) является наиболее широко используемым протоколом сетевой передачи в Интернете. Все документы WWW должны соответствовать этому стандарту. HTTP — это протокол связи, основанный на TCP / IP для передачи данных (файлы HTML, файлы изображений, результаты запросов и т. Д.).

HTTP Протокол работает по архитектуре клиент-сервер. В качестве HTTP-клиента браузер отправляет все запросы HTTP-серверу, т. Е. WEB-серверу, через URL-адрес. К веб-серверам относятся: сервер Apache, сервер IIS (информационные службы Интернета) и т. Д. Веб-сервер отправляет клиенту ответную информацию в соответствии с полученным запросом. Номер порта HTTP по умолчанию — 80, но вы также можете изменить его на 8080 или другие порты.

В этом разделе мы изучим stm32 + LWIP для создания HTTP-сервера. См. Ниже конкретную реализацию.

На основе аппаратной платформы: модель MCU — STM32F407VGT6, используйте проект конфигурации, автоматически сгенерированный инструментом stm32cubemx, и используйте KEIL5 для компиляции кода. Принципиальная схема самой маленькой системной платы, используемой в этом примере:

    1. Работа инструментов CUBEMX и KEIL не будет подробно объяснена.Если вы не знакомы с ними, вы можете проверить предыдущий учебный документ. Конфигурация проекта представлена ​​непосредственно ниже:

    Источник

    Низкоуровневое обнаружение Wi-Fi устройств в домашней сети

    Чтобы сделать собственное уникальное устройство для «умного дома» сейчас достаточно купить микроконтроллер и электронные компоненты. Конечно, на рынке уже есть множество «умных» устройств, но не все производители предоставляют открытое API, и уж точно единицы разрешают (или по крайней мере не запрещают) создавать собственные прошивки. Иногда наступает тот момент, когда кажется, что разработать и запрограммировать собственное устройство будет лучшим решением.

    В этой статье я расскажу про несколько способов «‎научить» микроконтроллер распознавать присутствие людей дома исключительно с помощью Wi-Fi.

    Предисловие

    Источник изображения
    Мне с детства нравились часы. Конечно, желания заполнить всю комнату звенящими часами, словно Доктор Браун, у меня не было, но часов в моей комнате было достаточно. Тем не менее, пришлось повзрослеть и желание иметь множество часов как-то приутихло.

    Наступила взрослая самостоятельная жизнь и вопрос с часами стал одним из маленьких конфликтов интересов. Мне хотелось иметь светящиеся часы, которые ненавязчиво покажут время в любое время дня и ночи. Моя девушка же придерживается мнения, что никакой свет, даже слабый, не должен препятствовать засыпанию.

    Компромисс находился простой: в процессе подготовки ко сну выключать часы. Правда, их нужно с утра как-то включить, что в свою очередь нерационально: проще взять телефон, там время тоже есть. Значит, нужно как-то автоматически определять моменты, подходящие для отключения и включения часов.

    Быстро проанализировав подготовку ко сну, я обнаружил там повторяющееся действие, а именно, отключение Wi-Fi на телефоне. Такой триггер позволит «умным» часам выключаться не по сухому «расписанию», а в нужные моменты времени.

    Давайте определимся, из чего состоят часы и какие ограничения накладываются на окружение.

    Что там внутри

    Подключение платы к светодиодной матрице (источник alexgyver.ru)

    Набор юного «самодельщика» прост:

    • плата Wemos D1 Mini на базе чипа ESP8266 с Wi-Fi;
    • светодиодная WS2812B-совместимая матрица размером 32х8;
    • блок питания 5В, 2А;
    • для разработки прошивки используется Arduino IDE.

    Разумеется, полагаться на внутренние часы микроконтроллера неразумно и нужна отдельная плата часов реального времени. Однако, согласно любительскому исследованию, скорость расхождения внутреннего времени микроконтроллера составляет примерно одну секунду в день. Это не критично для настенных часов, а при наличии доступа в интернет, синхронизация с сервером времени решит проблему.

    Минимальные вложения для сборки данного устройства накладывают следующие ограничения:

    • в домашней сети отсутствуют какие-либо системы, выполняющие мониторинг сети;
    • допустимы изменения в конфигурации домашнего роутера;
    • допустимы изменения в конфигурации сетевого подключения на телефоне;
    • модификация ПО домашнего роутера запрещена;
    • модификация ПО телефона запрещена.

    Современные устройства умеют подменять MAC-адрес во имя конфиденциальности
    Единственный уникальный идентификатор, с помощью которого можно найти телефон в домашней сети — MAC-адрес. Однако, современная техника умеет генерировать «подставной» MAC-адрес, что усложняет определение устройства. Тем не менее, для заданных Wi-Fi-сетей эту опцию можно отключить.

    Итак, у нас есть MAC-адрес, что будем делать?

    Поиск устройства

    Можно придумать несколько вариантов в зависимости от искомого устройства и используемого маршрутизатора/точки доступа.

    Большую часть решений объединяет одно: подключение к домашнему Wi-Fi. Минимальный код, с которым будем работать.

    #include #include void setup() < Serial.begin(115200); WiFiManager wifiManager; wifiManager.setDebugOutput(false); wifiManager.autoConnect("habr-example", "supergeneral"); Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); /* Здесь также инициализация для FastLED и других библиотек, * которые не важны для данного примера **/ >void loop() < // Основной код ledTick(); >

    Для упрощения работы с Wi-Fi используется библиотека WiFiManager. Если в памяти микроконтроллера нет информации о известных точках или они недоступны, WiFiManager запустит собственную точку доступа с веб-интерфейсом для быстрого подключения к новому Wi-Fi.

    Кто там

    Самое простое решение всегда на поверхности: давайте «пинганем» телефон. На поверку, в мире микроконтроллеров протокол ICMP используется неохотно. Так, в lwIP (lightweight IP, реализации стека TCP/IP для встраиваемых систем) есть минимальная поддержка протокола ICMP, но этого недостаточно. Для наших целей придется поставить библиотеку ESP8266-ping.

    Для уведомления об успешном или неуспешном пинге библиотека использует функции обратного вызова. Напишем функцию с простой логикой:

    • если устройство было недоступно, а сейчас доступно — устройство появилось в сети;
    • если устройство недоступно MAX_PING попыток подряд — устройство ушло из сети.
    #define MAX_PING 5 boolean current_state = false; unsigned char attempts = 0; void* responseCallback(const PingerResponse& response) < if(response.ReceivedResponse) < if(current_state == false) < attempts = 0; current_state = true; Serial.println("Device on"); >> else < if(current_state == true) < attempts++; if(attempts >MAX_PING) < current_state = false; Serial.println("Device off"); >> > return (void*)true; >

    Инициализируем библиотеку ESP8266-ping:

    #define PING_INTERVAL 1000 Pinger pinger; void setup() < // Общая инициализация опущена pinger.OnReceive(&responseCallback); >

    Так как пинг — не единственная наша задача, создаем функцию, которая раз в PING_INTERVAL миллисекунд отправляет ICMP-пакет.

    unsigned long previousTime = 0; void pingTick() < if(millis() - previousTime >PING_INTERVAL) < previousTime = millis(); pinger.Ping("192.168.88.148", 1, PING_INTERVAL / 2); >> void loop() < // Другие Tick() функции опущены pingTick(); >
    #include #include #include #define MAX_PING 5 #define PING_INTERVAL 1000 Pinger pinger; boolean current_state = false; unsigned char attempts = 0; void* responseCallback(const PingerResponse& response) < if(response.ReceivedResponse) < if(current_state == false) < attempts = 0; current_state = true; Serial.println("Device on"); >> else < if(current_state == true) < attempts++; if(attempts >MAX_PING) < current_state = false; Serial.println("Device off"); >> > return (void*)true; > unsigned long previousTime = 0; void pingTick() < if(millis() - previousTime >PING_INTERVAL) < previousTime = millis(); pinger.Ping("192.168.88.148", 1, 1000); >> void setup() < Serial.begin(115200); WiFiManager wifiManager; wifiManager.setDebugOutput(false); wifiManager.autoConnect("habr-example", "supergeneral"); Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); pinger.OnReceive(&responseCallback); >void loop()

    Третий аргумент функции Ping задает время ожидания ответа и ему стоит быть меньше, чем промежутки между пингами. Однако, здесь фигурирует только IP-адрес, еще и явно прописанный в прошивке. Есть два решения данной ситуации:

    1. в настройках DHCP-сервера явно «прибить» адрес к MAC-адресу искомого устройства;
    2. пинговать все адреса подсети и проверять MAC-адрес.

    Но случаются вредные устройства, которые не отвечают на ICMP-запросы.

    Открывайте! Мы знаем, что вы тут

    Далеко за примером ходить не надо: операционная система Microsoft Windows по умолчанию игнорирует ICMP-запросы. Такой расклад дел не сильно усложняет жизнь. Устройство может игнорировать ICMP-запросы, но ARP-запросы ему проигнорировать не получится. Поэтому для «особо вредных» устройств у нас более хитрый план: очищаем ARP-таблицу, отправляем несколько пингов, проверяем ARP-таблицу.

    ARP (англ. Address Resolution Protocol — протокол определения адреса) — протокол в компьютерных сетях, предназначенный для определения MAC-адреса по IP-адресу другого компьютера. © Википедия

    Особенность ARP-протокола заключается в том, что он работает только в пределах одного Ethernet-сегмента. Тем не менее, ожидается, что домашняя сеть не должна быть сложной.

    Доступ к ARP-таблицам на ESP8266 возможен через функции lwIP. Эти функции — для смелых и простым смертным не нужны, поэтому примеров и объяснений достаточно мало, нужно читать еще и комментарии к коду. Добавляем в проект включение заголовочных файлов lwip:

    Удаляем функцию обратного вызова и изменяем pingTick() следующим образом:

    void pingTick() < if(millis() - previousTime >PING_INTERVAL) < previousTime = millis(); // IP-адрес искомого устройства, может быть глобальным IPAddress addr = IPAddress(192,168,88,148); // Итерация по ARP-таблице ip4_addr_t *ip; struct netif *netif; struct eth_addr *ethaddr; bool found = false; for(int i=0; iaddr & 0xFF) && addr[1] == (ip->addr >> 8 & 0xFF) && addr[2] == (ip->addr >> 16 & 0xFF) && addr[3] == (ip->addr >> 24 & 0xFF)) < found = true; >> > // Очищаем ARP-таблицу etharp_cleanup_netif(netif); // Запускаем следующий раунд пингов pinger.Ping(addr, 5, 100); // Обрабатываем информацию if(found) < Serial.println("Device on"); >else < Serial.println("Device off"); >> >

    Время реакции этого способа равно PING_INTERVAL, в моем случае я увеличил это число до пяти секунд. Способ потенциально хороший, но в тестах в моей домашней сети он постоянно сбоил и способ с ICMP-ответами работал стабильнее. Поэтому если ваше устройство не скупится отвечать на пинг, то лучше использовать предыдущий способ.

    #include #include #include #include #define MAX_PING 5 #define PING_INTERVAL 5000 Pinger pinger; boolean current_state = false; unsigned char attempts = 0; unsigned long previousTime = 0; void pingTick() < if(millis() - previousTime >PING_INTERVAL) < previousTime = millis(); // IP-адрес искомого устройства, может быть глобальным IPAddress addr = IPAddress(192,168,88,148); // Итерация по ARP-таблице ip4_addr_t *ip; struct netif *netif; struct eth_addr *ethaddr; bool found = false; for(int i=0; iaddr & 0xFF) && addr[1] == (ip->addr >> 8 & 0xFF) && addr[2] == (ip->addr >> 16 & 0xFF) && addr[3] == (ip->addr >> 24 & 0xFF)) < found = true; >> > // Очищаем ARP-таблицу etharp_cleanup_netif(netif); // Запускаем следующий раунд пингов pinger.Ping(addr, 5, PING_INTERVAL / 10); // Обрабатываем информацию if(found) < if(current_state == false) < current_state = true; Serial.println("Device on"); >> else < if(current_state == true) < current_state = false; Serial.println("Device off"); >> > > void setup() < Serial.begin(115200); WiFiManager wifiManager; wifiManager.setDebugOutput(false); wifiManager.autoConnect("habr-example", "supergeneral"); Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); >void loop()

    Но что делать, если эти варианты по каким-то причинам не подходят?

    Чуткий нюх

    Микроконтроллер на базе ESP8266 может быть Wi-Fi-сниффером. У него можно включить неразборчивый режим (promiscuous mode) и собирать пролетающие мимо пакеты. Существует несколько репозиториев, в которых есть код запускающий сниффер на вашем ESP8266.

    Телефоны с включенным Wi-Fi будут постоянно рассылать разные пакеты, и часть из них не будет иметь шифрования. Таким образом, можно определять наличие или отсутствие телефона в сети.

    Несмотря на то, что способ надежный как швейцарские часы, у данного решения есть ряд проблем:

    • если рядом множество Wi-Fi сетей, то поток пакетов будет большим, что потребует самодельного фильтра. Возможно этот фильтр будет медленнее, чем в lwIP.
    • В этом режиме ESP8266 не имеет доступа в интернет, так как не подключена к Wi-Fi. Если вы хотели добавить погоду или синхронизацию с NTP — это будет затруднительно.
    • Микроконтроллер может «не услышать» пакет от вашего устройства в силу физических причин, а так как пакет не предназначался микроконтроллеру, повторения не будет.
    • Сниффер может не понравиться соседям, их друзьям или местным законам.

    Уведомления

    Этот способ требует соответствующего сетевого оборудования. Если у ваш домашний роутер работает на OpenWRT или RouterOS, то он точно подойдет.

    Данный способ построен на парсинге логов маршрутизатора. Маршрутизатор всегда знает MAC-адрес подключившегося и в большинстве случаев выдает адрес с помощью DHCP-сервера. Поэтому, логи маршрутизатора — это самый быстрый и самый надежный способ.

    Для моего Mikrotik hAP ac lite лог подключения и отключения выглядит следующим образом. MAC-адреса вымышлены.

    wireless,info 80:35:XX:XX:XX:XX@wlan2: disconnected, received deauth: sending station leaving (3) wireless,info 80:35:XX:XX:XX:X@wlan2: connected, signal strength -44

    Настраиваем логирование по метке wireless,info в удаленный порт. Для ускорения обработки на микроконтроллере задействуем протокол UDP. Настраиваем UDP-сервер следующим образом:

    #include WiFiUDP syslog; void setup() < // Общая инициализация опущена syslog.begin(514); >

    Далее периодически опрашиваем UDP-сервер на предмет пришедших пакетов.

    #define BUF_SIZE 4096 char str[BUF_SIZE]; String masterMac = "80:35:XX:XX:XX:XX"; void syslogTick() < int packetSize = syslog.parsePacket(); if(packetSize >0) < int n = syslog.read(str, BUF_SIZE); str[n] = '\0'; String syslog_str = String(str); String mac = syslog_str.substring(14, 31); String reason = syslog_str.substring(39); bool connected = true; if(reason.startsWith("disconnected")) < connected = false; >if(mac != masterMac) < return; >if(connected) < Serial.println("Device connected!"); >else < Serial.println("Device disconnected!"); >> >

    Пакет содержит MAC-адрес и причину события. Достаточно «‎разобрать»‎ пришедшую строку и записать состояние.

    Этот способ, конечно, тоже обладает недостатком. Так, при перезагрузке микроконтроллера, потребуется узнать текущее состояние искомого устройства. Но для этого можно использовать способ с ICMP-запросом.

    WiFiUDP syslog;
    void setup() Serial.begin(115200);

    WiFiManager wifiManager;
    wifiManager.setDebugOutput(false);
    wifiManager.autoConnect(«habr-example», «supergeneral»);
    Serial.print(«Connected! IP address: „);
    Serial.println(WiFi.localIP());

    #define BUF_SIZE 4096
    char str[BUF_SIZE];
    String masterMac = “80:35:XX:XX:XX:XX»;
    void syslogTick() int packetSize = syslog.parsePacket();
    if(packetSize > 0) int n = syslog.read(str, BUF_SIZE);
    str[n] = ‘\0’;
    String syslog_str = String(str);
    String mac = syslog_str.substring(14, 31);
    String reason = syslog_str.substring(39);

    bool connected = true;
    if(reason.startsWith(«disconnected»)) connected = false;
    >

    if(connected) Serial.println(«Device connected!»);
    > else Serial.println(«Device disconnected!»);
    >
    >
    >

    Источник

    Читайте также:  Роутер для dsl интернета
Оцените статью
Adblock
detector