1i7

ChipKIT: подключение к Вайфай

Jun 12, 2014 18:56

Начинаем подготовку к выпуску наших роботов в Сеть делать там разные интересные вещи, но для начала придется разобраться с необходимой стартовой рутиной: напишем программу, которая подключает плату ChipKIT к беспроводной сети WiFi - открытой или защищенной паролем.


ChipKIT WF32 подключение к беспроводной сети WiFi from 1i7 on Vimeo.

Подключенный к плате ChipKIT WF32 светодиод показывает статус беспроводного подключения: он горит, когда плата подключена к вайфай, и не горит, когда плата не подключена. Как видно из видео, реакция на события включения/выключения точки со стороны платы требует некоторого времени (10-15 секунд), но в целом все работает достаточно надежно.

В роли точки доступа выступает Ётафон, раздающий вайфай в режиме модема (эта функция доступна на любом смартфоне с Андроид); вместо него подойдет обычная домашняя точка доступа или iPhone, который тоже умеет раздавать интернет через WiFi.

В Андроиде Настройки > Ещё... > Режим модема









Оборудование

В серии ChipKIT с WiFi умеют работать платы:

ChipKIT Wi-FIRE (судя по всему уже поступила в продажу)



ChipKIT WF32



ChipKIT Uno32 с WiFi Shield



Библиотеки и документация

Среду разработки MPIDE для плат ChipKIT для Linux/Mac/Windows нужно качать на сайте chipkit.net. Для работы с WiFi также потребуются дополнительные библиотеки - их можно найти например на странице платы ChipKIT WF32 в магазине Digilent (см список закачек Support Documents внизу старницы: архив chipKIT Network and USB Libs-xxx.zip). В этом же архиве содержится необходимая документация и инструкции по установке (в Linux архив нужно распаковать в ~/Documents/mpide-sketches/libraries, в Windows - в папку "C:\Users\MyName\Documents\mpide\libraries").

Замечание: Библиотека для работы с WiFi на платах ChipKIT представляет собой простую C++ обертку над стеком Tcp и WiFi для процессоров PIC32 от Microchip (он входит в состав пакета MLA - Microchip libraries for applications), поэтому этот код не является переносимым внутри всей платформы Arduino - он будет работать только на платах серии ChipKIT. Во-первых, насколько мне известно, в платформе Arduino не предусмотрен стандартный API для работы с WiFi, поэтому WiFi-шилды для обычных Ардуин используют свои вызовы; во-вторых, код стека Tcp и WiFi, написанный для PIC32, будет проблематично запустить на процессорах AVR на обычной Arduino из-за чисто технических причин и особенностей архитектур; в-третьих, это все равно нельзя делать, тк. библиотеки MLA Microchip публикует под проприетарной лицензией - хотя исходный код стека доступен и его можно модифицировать под нужды проекта, лицензия не позволяет запускать его на чипах других производителей (в частности, AVR).

Алгоритм
Программа постоянно ищет сеть WiFi с заданным именем (lasto4ka). Если сеть найдена, пытается к ней подключиться с заданным типом безопасности: используя пароль (WPA2) или как к открытой сети без пароля. Если подключение удалось, программа зажигает лампочку статуса и печатает сообщение "WiFi is ON, do something with network" в отладочный порт Serial UART раз в 5 секунд. Если подключение теряется, программа гасит лампочку переходит в режим поиска сети с заданными параметрами.

Отладочные сообщения о ходе выполнения программы на плате можно посмотреть в MPIDE в окне Tools/Serial Monitor.




Замечание: Имя сети, тип подключения (открытая сеть/пароль WPA2) и сам пароль (в случае WPA2) задаются жестко в коде прошивки, иначе нам пришлось бы дополнительно придумывать, каким образом доставлять их на плату.

Собственно код

Вся прошивка умещаяется в одном файле chipkit_wifi.pde.

Подключим необходимые библиотеки:

#include

#include
#include

Параметры для подключения к точке доступа: открытая или запароленная, имя сети и пароль:

// Точка доступа ВайФай
boolean USE_SECURITY_OPEN = false;

const char* wifi_ssid = "lasto4ka";
const char* wifi_wpa2_passphrase = "robotguest";

Укажем, каким образом будем получать IP-адрес - динамический (точка сама назначит по своему усмотрению в момент подключения) или статический (попросим точку назначить адрес, нужный нам):

boolean USE_STATIC_ADDRESS = true;

// статический IP-адрес для текущего хоста - если включен режим
// USE_STATIC_ADDRESS = true, то попросим у точки Wifi дать его нам
IPv4 host_ip = {192,168,43,117};

Сюда будем сохранять идентификатор активного подключения для внутренних нужд программы:

// Подключение к WiFi
int connectionId = DWIFIcK::INVALID_CONNECTION_ID;

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

// Пин для лампочки статуса WiFi
#define WIFI_STATUS_PIN 13

В сетапе ничего интересного - включаем последовательный порт UART для вывода отладочных сообщений по USB в MPIDE на компьютере, переводим пин для лампочки со статусом подключения к вайфай в режим вывода.

void setup() {
Serial.begin(9600);
Serial.println("Start wifi network connection demo");

pinMode(WIFI_STATUS_PIN, OUTPUT);
}

Всё интересное находится в бесконечном цикле loop.

Сетевой стек для PIC32 на ChipKIT (как и стек USB) не является многопоточным - в прошивке, которую мы загрузим на плату, не будет такого секретного системного фонового потока, который бы держал связь с точкой вайфай, следил за статусом подключения, передачей данных и т.п. В нашей программе есть только один поток - loop, поэтому все эти операции должны осуществляться внутри него в промежутке между выполнением остальных полезных действий - для этого нам достаточно поместить вызов DNETcK::periodicTasks() в код так, чтобы он время от времени вызывался например раз за цикл.

void loop() {
DNETcK::STATUS networkStatus;

// Держим Tcp-стек в живом состоянии
DNETcK::periodicTasks();

....
}

На очередной итерации проверяем, подключены ли мы к точке, при помощи вызова DWIFIcK::isConnected(): если не подключены гасим лампочку статуса и начинаем попытку подключения.

if(!DWIFIcK::isConnected(connectionId)) {
// Не подключены к WiFi - выключим лампочку
digitalWrite(WIFI_STATUS_PIN, LOW);

bool connectedToWifi = false;

// Подключимся к сети Wifi
Serial.println("Connecting wifi...");

....
}

Для начала получим доступ к оборудованию WiFi при помощи системного вызова DWIFIcK::connect() с нужными параметрами (имя сети, пароль, статус возврата): если хотим подключиться к открытой сети, передаем только ее имя, если к защищенной, то вызываем вариант с паролем.

// Сначала получим доступ к оборудованию:
// выбрать способ подключения (открытый или с паролем)
if(USE_SECURITY_OPEN) {
// Подключиться к открытой сети WiFi.
Serial.print("SSID: ");
Serial.println(wifi_ssid);

connectionId = DWIFIcK::connect(wifi_ssid, &networkStatus);
} else {
// Подключиться к сети WiFi, защищенной WPA2 с паролем.
Serial.print("SSID: ");
Serial.print(wifi_ssid);
Serial.print(", WPA2 passphrase: ");
Serial.println(wifi_wpa2_passphrase);

connectionId = DWIFIcK::connect(wifi_ssid, wifi_wpa2_passphrase, &networkStatus);
}

На этом этапе подключения к самой точке (проверка имени и пароля) еще не происходит - если чип WiFi установлен на плату и работает корректно, то вызов DWIFIcK::connect() почти сразу вернет корректный идентификатор подключения connectionId.

if(connectionId != DWIFIcK::INVALID_CONNECTION_ID) {
// На этом этапе подключение будет создано, даже если указанная
// сеть Wifi недоступна или для нее задан неправильный пароль
Serial.print("Connection created, connection id=");
Serial.println(conectionId, DEC);

...
}

Процесс подключения к точке происходит дальше - при старте инициализации IP-стека. Вызываем нужный вариант DNETcK::begin() для подключения с динамическим или статическим IP-адресом.

// Теперь попробуем подключиться к самой точке доступа - инициализируем Ip-стек
Serial.print("Initializing IP stack...");

if(USE_STATIC_ADDRESS) {
// подключимся со статическим ip-адресом
DNETcK::begin(host_ip);
} else {
// подключиться с динамическим ip-адресом
DNETcK::begin();
}

Как уже говорилось выше, сетевой стек на ChipKIT реализован для работы в однопоточных системах, поэтому большинство вызовов не являются блокирующими. В том числе вызов DNETcK::begin() вернется почти сразу не дожидаясь результатов подключения к точке, поэтому дождемся их самостоятельно внутри несложного цикла, который завершит работу в двух случаях: если мы успешно подключились к точке доступа и получили IP-адрес или если попытка подключения завершилась с ошибкой (не найдена сеть с нужным именем, не подошел пароль и т.п.):

// К открытой сети может подключиться с первой попытки, к сети с паролем
// может потребоваться несколько циклов (если пароль для сети неправильный,
// то ошибка вылезет тоже на этом этапе).
bool initializing = true;
while(initializing) {
// Вызов isInitialized заблокируется до тех пор, пока стек не будет
// инициализирован или не истечет время ожидания (по умолчанию 15 секунд).
// Если сеть не подключится до истечения таймаута и при этом не произойдет
// ошибка, isInitialized просто вернет FALSE без кода ошибки, при необходимости
// можно вызвать его повторно до успеха или ошибки.
if(DNETcK::isInitialized(&networkStatus)) {
// Стек IP инициализирован
connectedToWifi = true;

initializing = false;
} else if(DNETcK::isStatusAnError(networkStatus)) {
// Стек IP не инициализирован из-за ошибки,
// в этом месте больше не пробуем
initializing = false;
}
}
}

Подключились успешно, зажигаем статусную лампочку:

if(connectedToWifi) {
// Подключились к Wifi
Serial.println("Connected to wifi");
printNetworkStatus();

// включим лампочку
digitalWrite(WIFI_STATUS_PIN, HIGH);
}

Не получилось подключиться - отправим в отладочный порт код ошибки, корректно завершим работу сетевого стека и через 4 секунды попробуем всё заново на следующей итерации:

} else {
// Так и не получилось подключиться
Serial.print("Failed to connect wifi, status: ");
printDNETcKStatus(networkStatus);
Serial.println();

// Нужно корректно завершить весь стек IP и Wifi, чтобы
// иметь возможность переподключиться на следующей итерации
DNETcK::end();
DWIFIcK::disconnect(conectionId);
conectionId = DWIFIcK::INVALID_CONNECTION_ID;

// Немного подождем и попробуем переподключиться на следующей итерации
Serial.println("Retry after 4 seconds...");
delay(4000);
}

На этом блок подключения к точке WiFi закончен. В случае неудачи программа ждет 4 секунды и повторяет попытку на следующей итерации. В случае удачного подключения на следующей итерации управление передается следующему блоку, который будет или принимать входящие сетевые подключения или сам выходить в сеть на внешние ресурсы или делать что-то еще - об этом в следующих лабах.

void loop() {
...

if(!DWIFIcK::isConnected(conectionId)) {
...

} else {
// Подключены к WiFi - здесь можно делать любые операции, для которых необходима сеть
Serial.println("WiFi is ON, do something with network");
delay(5000);
}
}




исходники прошивки, подсветка синтаксиса.

типовые задачи, chipkit

Previous post Next post
Up