Web-сервер с модулем E-Shield
Данный пример демонстрирует, как с помощью модуля E-Shield и стандартной библиотеки Ethernet можно реализовать на Freeduino простой Web-сервер. Предполагается версия ПО Arduino не ниже 0.12 – с этой версии в состав входит библиотека Ethernet.
Наш Web-сервер будет очень простым – при получении запроса он будет измерять значения на всех шести аналоговых входах, и выдавать их клиенту. Таким образом, набрав в любом браузере (Firefox/Opera/Internet Explorer) IP адрес устройства мы увидим значения аналоговых входов.
Разберем исходный код:
//Здесь задается MAC адрес и IP адрес устройства.
//В данном примере предполагается, что IP адрес компьютера, с которого
//будем обращаться к устройству будет любым из сети 192.168.1.x,
//и маска 255.255.255.0
//MAC адрес можно задать любой, лишь бы он не пересекался с имеющимися в сети
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 177 };
Server server(80); //Создаем сервер, слушающий 80й порт (80 – это порт HTTP)
void setup()
{
Ethernet.begin(mac, ip); //Инициализируем Ethernet модуль
server.begin(); //Начинаем ожидать соединений на 80 порту
}
void loop()
{
//Если кто-то установил соединение с нашим сервером, следующая строчка
//создаст объект-клиент.
Client client = server.available();
if (client) { //если client не нулевой (т.е. соединение есть)
//...значит кто-то подключился. Согласно протокола HTTP клиент
//шлет довольно сложный запрос, но мы не будет его разбирать -
//нам достаточно дождаться окончания запроса.
//Запрос заканчивается пустой строкой, поэтому мы просто дождемся
//получения символа '\n', перед которым тоже были получены '\n' и '\r'.
//current_line_is_blank - это переменная-флаг. Она равна true, если
//вновь полученная от клиента строка пустая, т.е. в полученных данных
//не встретилось символов отличных от '\n' и '\r'.
//Будем выставлять переменную current_line_is_blank в false при
//получении любого отличного от '\n' и '\r' символа
boolean current_line_is_blank = true;
while (client.connected()) { //пока клиент подключен
if (client.available()) { //если от него пришел символ
char c = client.read(); //читаем этот символ
//Если получен перевод строки ('\n') и current_line_is_blank == true,
//значит мы получили пустую строку, т.е. запрос клиента окончен –
//можно слать ответ
if (c == '\n' && current_line_is_blank) {
//шлем стандартный HTTP заголовок
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(); //он тоже заканчивается пустой строкой!
//выдаем поочередно значения 6 аналоговых входов
for (int i = 0; i < 6; i++) {
client.print("analog input ");
client.print(i);
client.print(" is ");
client.print(analogRead(i));
client.println("<br>"); //HTML тэг <br> - перевод строки
}
break; //работа окончена – можно выходить из цикла while
}
if (c == '\n') { //если получен перевод строки, значит началась новая
//строка. Выставим флаг current_line_is_blank = true
current_line_is_blank = true;
} else if (c != '\r') { //если получен любой другой символ, отличный
//от возврата каретки ('\r'), значит получаемая строка непустая
current_line_is_blank = false;
}
}
}
//небольшая пауза, чтобы данные успели уйти
delay(1);
//разрываем соединение с клиентом
client.stop();
}
}
Вот как выглядит результат в браузере:
Здесь мы специально запросили у устройства ресурс "abracadabra ", чтобы показать, что содержимое HTTP запроса игнорируется, и всегда генерируется страничка со значениями аналоговых входов.
Web-сервер с модулем WiFly
Компания Roving Networks выпускает ряд модулей для сетей Wi-Fi, управляемых командами по простому последовательному интерфейсу под общим именем WiFly.
Одним из них является модуль RN-171-XV, который может быть легко подключен к Arduino-совместимой плате с помощью XBee Shield.
Исходный код для решения аналогичной задачи с модулем WiFly похож на предыдущий. Различия только в другом способе инициализации модуля, механизме обмена данными (здесь используется последовательный порт), и механизме инициализации и закрытия соединения.
//в данном простом примере мы не анализируем ответы модуля, а
//просто игнорируем их - это не очень корректно, и не рекомендуется
//в серьезных проектах, но это сильно упрощает понимание протокола
void DelayAndClear()
{
delay(1000);
while (Serial.available() > 0) Serial.read();
}
void setup()
{
delay(2000);
Serial.begin(9600);
Serial.print("$$$"); DelayAndClear(); //командный режим
Serial.println("set wlan join 0"); DelayAndClear(); //отключаем авто-соединение с сетью
Serial.println("set ip dhcp 0"); DelayAndClear(); //отключаем DHCP
//выставляем IP адрес и маску
Serial.println("set ip address 192.168.1.177"); DelayAndClear();
Serial.println("set ip mask 255.255.255.0"); DelayAndClear();
Serial.println("set comm remote 0"); DelayAndClear(); //отключаем стандартный ответ
Serial.println("set ip localport 80"); DelayAndClear(); //будем слушать 80 порт (HTTP)
Serial.println("set wlan auth 4"); DelayAndClear(); //выбираем WPA2-аутентификацию
//выставляем WPA ключ
Serial.println("set wlan phrase MYPASSWORD"); DelayAndClear();
delay(1000); DelayAndClear(); //нужна небольшая пауза после установки ключа
//подключаемся к сети с указанным именем
Serial.println("join MYNETWORK"); DelayAndClear();
//организуем паузу - модуль подключается к сети не мгновенно
//более правильным было бы выполнять анализ ответа модуля
delay(1000); DelayAndClear();
delay(1000); DelayAndClear();
delay(1000); DelayAndClear();
delay(1000); DelayAndClear();
Serial.println("exit"); DelayAndClear(); //выходим из командного режима
}
void loop()
{
//Согласно протокола HTTP клиент
//шлет довольно сложный запрос, но мы не будет его разбирать -
//нам достаточно дождаться окончания запроса.
//Запрос заканчивается пустой строкой, поэтому мы просто дождемся
//получения символа '\n', перед которым тоже были получены '\n' и '\r'.
//current_line_is_blank - это переменная-флаг. Она равна true, если
//вновь полученная от клиента строка пустая, т.е. в полученных данных
//не встретилось символов отличных от '\n' и '\r'.
//Будем выставлять переменную current_line_is_blank в false при
//получении любого отличного от '\n' и '\r' символа
boolean current_line_is_blank = true;
while (Serial.available()==0); //ожидаем появления принятого символа
//устанавливаем время таймаута, чтобы разорвать соединение, если
//от клиента не получен нормальный запрос
unsigned long timeout = millis() + 5000;
while (millis() < timeout)
{
while (Serial.available()==0); //ожидаем появления принятого символа
char c = Serial.read(); //читаем этот символ
//Если получен перевод строки ('\n') и current_line_is_blank == true,
//значит мы получили пустую строку, т.е. запрос клиента окончен –
//можно слать ответ
if (c == '\n' && current_line_is_blank) {
//шлем стандартный HTTP заголовок
Serial.println("HTTP/1.1 200 OK");
Serial.println("Content-Type: text/html");
Serial.println(); //он тоже заканчивается пустой строкой!
//выдаем поочередно значения 6 аналоговых входов
for (int i = 0; i < 6; i++) {
Serial.print("analog input ");
Serial.print(i);
Serial.print(" is ");
Serial.print(analogRead(i));
Serial.println("<br>"); //HTML тэг <br> - перевод строки
}
break; //работа окончена – можно выходить из цикла while
}
if (c == '\n') { //если получен перевод строки, значит началась новая
//строка. Выставим флаг current_line_is_blank = true
current_line_is_blank = true;
} else if (c != '\r') { //если получен любой другой символ, отличный
//от возврата каретки ('\r'), значит получаемая строка непустая
current_line_is_blank = false;
}
}
delay(1000); //небольшая пауза, чтобы данные успели уйти
Serial.print("$$$"); DelayAndClear(); //командный режим
Serial.println("close"); DelayAndClear(); //закрываем соединение
Serial.println("exit"); DelayAndClear(); //выходим из командного режима
}
Конечный результат в браузере выглядит идентично: