Ну вот, наконец-то, дошли мы до той темы, к которой стремились и ради чего начинался цикл уроков по контроллерам ESP8266 — это приём и передача данных при помощи возможностей ESP8266 по беспроводной сети Wi-Fi.
Я думаю, что все знают, что это за тип сети и для чего она применяется.
Напомню лишь, что Wi-Fi — это протокол передачи данных, осуществляемой без применения проводов, физического уровня.
Как именно передаются данные по сети и как они подготавливаются, как заворачиваются в протоколы различного уровня, мы также очень хорошо знаем. Кто не помнит, напомню — смотрите уроки 40, 41, 44, 45, 46, 47, 48, 49, 50, 51, 52 по программированию контроллеров AVR, а также аналогичные уроки по контроллерам STM32. В данных уроках мы очень подробно рассмотрели многие протоколы всех уровней модели OSI. Те же самые протоколы будут использоваться и здесь, причём нам особо не придётся их программировать вручную, но знать, как они устроены, для чего нужны, мы обязаны, чтобы избежать ошибок при передаче данных, а также, если они и возникнут, то оперативно их устранить. И рассмотренных нами протоколов нам не потребуется протокол физического уровня Ethernet, его заменит протокол Wi-Fi.
Мы пока подробно не будем изучать протокол Wi-Fi, так как с ним работает наш контроллер аппаратно, но режимы работы мы должны знать. И знакомится с их разновидностями мы будем постепенно.
А пока мы должны знать, что наш контроллер ESP8266 поддерживает три основных режима работы — станция (Station mode или кратко STA), программная точка доступа (SoftAP), а также смешанный — Station + SoftAP.
Самым простым из этих режимов является первый — режим станции.
Режим станции (STA) — это такой режим, в котором контроллер не создает собственную сеть, а подключается к любой существующей сети Wi-Fi, например, к существующей локальной сети или к другому устройству, работающему в режиме точки доступа (AP).
На данном уроке мы настроим режим станции и попробуем подключиться к существующей точке доступа, например к роутеру, который раздаёт Wi-Fi по дому. Роутер от просто точки доступа отличается тем, что трафик, идущий от станций, подключенных к нему, он транслирует во внешнюю сеть и наоборот.
Пока воспользуемся возможностями SDK, то есть не будем лезть глубоко в дебри аппаратной части. Всему своё время.
Схема у нас будет простейшая — отладочная плата, подключенная к USB компьютера
Весь процесс соединения с точкой доступа и вхождение в различные стадии соединения мы будем отслеживать с помощью терминальной программы с помощью UART.
Проект наш выполнен из проекта прошлого урока с именем OS_TIMER и назван WIFI_STA.
Откроем наш проект в Eclipse и в файле main.c подключим библиотеку для работы с UART
1 2 |
#include "gpio.h" #include "driver/uart.h" |
В функции user_init сконфигурируем UART
1 2 3 4 |
void ICACHE_FLASH_ATTR user_init() { // Configure the UART uart_init(BIT_RATE_115200, BIT_RATE_115200); |
После небольшой задержки переведём строку в терминальной программе, чтобы отделить вывод следующих сообщений
1 2 3 |
gpio_output_set(0, 0, (1 << LED), 0); ets_delay_us(100000); os_printf("\r\n"); |
Вызовем функцию SDK, с помощью которой установим режим работы устройства в качестве станции
1 2 |
os_printf("\r\n"); wifi_set_opmode_current(STATION_MODE); |
Добавим функцию пока с пустым телом, которая будет вызываться через определённые промежутки времени по программному таймеру
1 2 3 4 5 6 |
static uint8_t led_state=0; //------------------------------------------------------ static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) { } //------------------------------------------------------ |
А подобную функцию для обработки событий от программного таймера с именем timer_func_user удалим вместе с телом.
В функции user_init() в функции инициализации программного таймера, соответственно, изменим имя функции в параметре
os_timer_setfn(&os_timer01, (os_timer_func_t *)wifi_check_ip, NULL);
Также изменим третий параметр в вызове функции старта таймера. Теперь здесь будет ноль, так как запускать таймер мы будем однократно. Также увеличим период срабатывания
os_timer_arm(&os_timer01, 1000, 0);
Добавим локальную переменную структуры для настроек Wi-Fi соединения
1 2 3 |
void ICACHE_FLASH_ATTR user_init() { struct station_config stationConf; |
Забьём нулями всю память, зарезервированную под переменную структуры
1 2 |
wifi_set_opmode_current(STATION_MODE); os_memset(&stationConf, 0, sizeof(struct station_config)); |
В файле user_config.h добавим логин и пароль для соединения с точкой доступа
1 2 3 4 5 |
#define __USER_CONFIG_H__ //---------------------------------------------------------- #define WIFI_CLIENTSSID "ASUS444" #define WIFI_CLIENTPASSWORD "12345678" //---------------------------------------------------------- |
У вас, соответственно, данные будут свои, я также ввожу временные данные, затем я их из соображений безопасности опять изменю.
В функции user_init() файла main.c занесём наши настройки в поля структуры
1 2 3 |
os_memset(&stationConf, 0, sizeof(struct station_config)); os_sprintf(stationConf.ssid, "%s", WIFI_CLIENTSSID); os_sprintf(stationConf.password, "%s", WIFI_CLIENTPASSWORD); |
Применим данные настройки с помощью функции SDK
1 2 |
os_sprintf(stationConf.password, "%s", WIFI_CLIENTPASSWORD); wifi_station_set_config_current(&stationConf); |
Мы должны помнить, что данные настройки применятся только на время действия нашей программы, в память FLASH они не пропишутся, для этого есть уже другая функция.
После запуска таймера вызовем функцию соединения с точкой доступа
1 2 |
os_timer_arm(&os_timer01, 1000, 0); wifi_station_connect(); |
Объявим и проинициализируем две глобальные переменные — одну для хранения состояния соединения с точкой доступа, а другую — пользовательскую
1 2 3 |
static uint8_t led_state=0; static uint8_t wifiStatus = STATION_IDLE; static uint8_t connectStatus = 0; |
В функции wifi_check_ip добавим переменную структуры соединения сетевого уровня
1 2 3 |
static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) { struct ip_info ipConfig; |
Произведём деинициализацию таймера
1 2 |
struct ip_info ipConfig; os_timer_disarm(&os_timer01); |
Узнаем состояние соединения с точкой доступа
1 2 |
os_timer_disarm(&os_timer01); wifiStatus = wifi_station_get_connect_status(); |
Если состояние будет указывать на то, что IP-адрес получен, а также сетевой адрес ненулевой, то проинициализируем и запустим таймер, только с интервалом в 2 секунды и вывеем соответствующее сообщение в терминальную программу. Также у наше условие будет иметь и противный случай
1 2 3 4 5 6 7 8 9 10 |
wifiStatus = wifi_station_get_connect_status(); if (wifiStatus == STATION_GOT_IP && ipConfig.ip.addr != 0) { os_timer_setfn(&os_timer01, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&os_timer01, 2000, 0); os_printf("STATION WIFI CONNECTED\r\n"); } else { } |
Начнём обрабатывать противный случай. В нем будет несколько условных конструкций.
Если пароль не совпал, то выведем соответствующее сообщение в терминальную программу и попытаемся соединиться повторно
1 2 3 4 5 6 7 |
else { if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD) { os_printf("STATION_WRONG_PASSWORD\r\n"); wifi_station_connect(); } |
Подобным образом поступим, если не будет найдена точка доступа
1 2 3 4 5 6 7 |
wifi_station_connect(); } else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND) { os_printf("STATION_NO_AP_FOUND\r\n"); wifi_station_connect(); } |
То же самое проделаем при неудачном соединении
1 2 3 4 5 6 7 |
wifi_station_connect(); } else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL) { os_printf("STATION_CONNECT_FAIL\r\n"); wifi_station_connect(); } |
В другом случае, не относящемся к вышеперечисленным установим переменной величину, соответствующую состоянию покоя
1 2 3 4 5 6 |
wifi_station_connect(); } else { os_printf("STATION_IDLE\r\n"); } |
Не выходя из тела противного состояния верхнего уровня, проинициализируем наш таймер и запустим его, установив период в полсекунды
1 2 3 4 |
os_printf("STATION_IDLE\r\n"); } os_timer_setfn(&os_timer01, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&os_timer01, 500, 0); |
Соберём код, прошьём контроллер и через некоторое время в терминальной программе, если всё правильно, мы получим соответствующее сообщение об удачном соединении с точкой доступа
Теперь мы можем спокойно передавать и принимать данные, с точкой доступа мы соединены, и сетевой адрес получен, также давайте для того, чтобы каждые 2 секунды не было сообщения об удачном сетевом соединении, а было только однажды, немного переработаем ветку условия, где мы это выводим.
Если наша пользовательская переменная в нуле, то мы только тогда выводим сообщение, поэтому перенесём его вывод в данное условие, а также в этом же теле выставим нашу переменную в единицу
1 2 3 4 5 6 |
os_timer_arm(&os_timer01, 2000, 0); if(connectStatus == 0) { os_printf("STATION WIFI CONNECTED\r\n"); connectStatus = 1; } |
А в противном случае мы изменим состояние ножки, к которой подключен светодиод
1 2 3 4 5 6 7 |
connectStatus = 1; } else { led_state = (led_state==0) ? 1 : 0; GPIO_OUTPUT_SET(LED, led_state); } |
Также в противном случае верхнего уровня мы обнулим пользовательский статус
1 2 3 4 5 6 |
GPIO_OUTPUT_SET(LED, led_state); } } else { connectStatus = 0; |
Вот теперь, скорей всего, у нас вывод сообщения об удачном соединении будет только однажды, а именно в момент нашего соединения, а вернее не позже чем через 2 секунды после его установления.
Проверим это
Так и есть — сообщение теперь выводится только один раз, также, думаю, если произойдут какие-то проблемы с соединением, то потом после удачного соединения, у нас также сообщение выведется только один раз. А после вывода сообщения у нас только медленно мигает светодиод, что будет свидетельствовать о наличии соединения в данный момент нашей станции с точкой доступа.
Давайте проверим соединение с помощью пинга
Как видим, всё пингуется и пакеты проходят отлично. Только вот период великоват. Это всё потому, что наш контроллер находится в одном из режимов пониженного энергопотребления.
Давайте исправим данную ситуацию. Для этого в функции user_init() включим режим полноправной работы модуля
1 2 |
wifi_station_set_config_current(&stationConf); wifi_set_sleep_type(NONE_SLEEP_T); |
Теперь мы видим, что отклик происходит гораздо быстрее
Итак, на данном уроке мы научились конфигурировать беспроводное соединение в режиме станции, что позволило нам организовать стабильное соединение с точкой доступа, получив при этом сетевой адрес, что, как я думаю, уже немало.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP8266 Следующий урок
Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU
Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Многофункциональный переходник CJMCU FT232H USB к JTAG UART FIFO SPI I2C можно приобрести здесь ftdi ft232rl
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий