Управление ардуино через интернет

Как мы узнали — это “аппаратная вычислительная платформа”, а по-русски — контроллер.
А что такое контроллер?
Для тех кто немного в курсе: не путать с микроконтроллером — МК это atmega, PIC и прочие “микросхемы” – однокристальные микро-ЭВМ,а контроллер это плата такая, на которую этот самый МК запаян.
Для тех кто совсем не в курсе: контроллер это такое электронное устройство которое что- нибудь контролирует — то есть реагирует на изменения одних параметров изменением других. Ну вот например кондиционер на стене видишь? (я тоже не вижу, но у кого-то он наверняка есть) так вот там тоже стоит умная плата-контроллер включающая/выключающая подогрев/охлаждение/осушение/увлажнение воздуха когда нужно.
Кондиционер, mp3 плеер, велокомпьютер, сигнализация, мобильник, навигатор — всё это (грубо говоря) специализированные контроллеры. А вот комп настольный это контроллер универсальный, да ещё и расширяемый, и с его помощью всё вышеперечисленное можно реализовать. Нужны будут только соответствующие платы расширения и софт.
Ардуино
Так вот ардуино тоже универсальный контроллер, который можно заточить под какую-нибудь задачу и превратить в законченное электронное устройство произвольного назначения, от часов с будильником до робота. Или просто играться — собирая разбирая всякие приблуды=)
Радиоконструктор в общем.
К самой плате можно подключать различную периферию – кнопки, некоторые виды датчиков (температуры, давления, освещённости, ускорения и т.п.), светодиоды, жидкокристаллические индикаторы (цифро-буквенные, а не LCD-монитор конечно =) написать программу и заставить взаимодействовать всё это как угодно. Масса применений кстати.
Системы сбора данных (чёрный ящик для аквариума – пишет температуру раз в 5 минут)
Таймеры-Счётчики событий (сколько раз и во сколько кот подходил к пустой миске)
Сигнализации-Извещатели (кот превысил разрешенное количество подходов к миске, температура в аквариуме ниже 0, кто-то покинул туалет не выключив свет/не смыв/не опустив стульчак =)
В таком духе. Cкучновато конечно. Прям как древний пустой комп — ну клава, ну моник, ну часы, ну пасьянс какой-нибудь… А вот если воткнуть звуковушку, модем, мышь с джойстиком, да тв-тюнер с веб-камерой присобачить… уже больший полёт фантазии можно наблюдать.
Так же подключая к ардуино различные устройства – шилды(shields) добавляем различные функции — так можно управлять всякими двигателями, сервомашинками, сетевой нагрузкой наконец (свет, обогреватель, чайник и т.п.). Можно подключить GPS или GSM модуль и получать координаты со спутника или отправлять данные на свой телефон – координаты своей машины которую кореш взял покататься, или угнали не дай бог, или в командировке узнать что твой любимый кактус никто не поливает. Можно воткнуть Ethernet-модуль и выпустить свой девайс в интернет — пусть шлёт данные на твой сайт, или пусть пишет всё на SD-карту воткнутую в соответствующий шилд. Можно добавить каналы связи – ИК, радиоканал, а то и вообще Bluetooth, со всеми вытекающими.
Ещё примеры что в голову приходит:
Автокормушка для домашних животных
Контроллер аквариума
Дебаггер для машины (все температуры, давления, обороты твоего жигуля на красивом экранчике, расшифровка блинк-кодов)
Системы удалённой телеметрии
Велокомпьютер
Элементы умного дома (управление светом, шторами, вентиляцией, кондиционированием, отоплением, прочими электроприборами)
Элементы хобби-чпу
Простые промышленные контроллеры
Ну и конечно робототехника! (для этого сайта — основное направление развития =)
И всё это в произвольных комбинациях — ограничивает только фантазия и владение железом/софтом.
Почему ардуино? Да, есть ещё немало универсальных контроллеров и плат развития позволяющих осуществлять и более амбициозные проекты. Но! Ардуино имеет ряд преимуществ:
Не нужен программатор
Не нужны особо глубокие познания в программирования микроконтроллеров
Проект ардуино полностью открытый
Платформа набирает популярность — куча сайтов с библиотеками, схемами и проектами
Стандартизация расположения выводов — это делает её привлекательной для производителей – появляются всё новые шилды
Кроссплатформенная среда разработки
Вообще полазив по интернету возникает ощущение что ардуино становится стандартом для целого класса хоум-мейдеров=)
далее: ХоумМейд Arduino — как сделать Arduino своими руками
По теме
Arduino, термины, начало работы
КМБ для начинающих ардуинщиков
Состав стартера (точка входа для начинающих ардуинщиков)
Возможные ошибки при работе с Arduino
Купить Arduino или CraftDuino — можно в нашем Магазине.

Подключение Ethernet Shield к Arduino

Схема подключение этих элементов приведена ниже. Хочу напомнить, что подключать все элементы нужно очень осторожно!.

Для начала я расскажу, как нужно подключать Wi-Fi роутер к Ethernet шилду. Для подключения вам понадобится сетевой кабель, который и будет связывать эти два элемента. Для нашей процедуры не нужен интернет, поэтому подключаем один конец кабеля к Ethernet Shield, а другой к Wi-Fi роутеру. Остается подключить Wi-Fi роутер к питанию и все готово, мы связали эти два элемента.

Поговорим о подключении Ethernet шилда к Arduino. Как вы могли догадаться, чтобы связать Ethernet Shield с Arduino, необходимо напрямую подключить эти два элемента, как показано на рисунке.

Осталось подключить остальные компоненты к Ethernet Shield`у, как показано на фотографии выше. Примерно такое соединение у вас должно получиться.

Внимание, при продолжительном подключении Ethernet шилда к запитанному Arduino существенно нагревается чип шилда. Для продолжительного использования необходимо установить охлаждение, например, радиатор или вентилятор. Обязательно учтите этот момент.

Написание скетча программы для создания web-сервера для Arduino на базе Ethernet shield W5100

Для работы датчика на Arduino нужно скачать и установить библиотеку DHT11 .

Скачать библиотеку можно здесь .

После того, как мы скачали нужную библиотеку, ее нужно правильно установить. скачанные файлы нужно переместить по следующему пути :

Диск C Progtam Files Arduino Libraries

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

/*—————- 1 часть —————-*/
#include «DHT.h» //библиотека для работы с DHT
#include «SPI.h» //библиотека для работы с SPI
#include «Ethernet.h» //библиотека для работы с Ethernet
#define DHTPIN 3 //Обозначаем номер пина, к которому подключен датчик
boolean newInfo = 0; //переменная для новой информации
byte mac = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//вводим mac адрес, обычно он такого вида, может отличаться
IPAddress ip(192, 168, 1, 177);
//вводим любой IP , который не совпадает с вашим
EthernetServer server(80); //инифиализация библиотеки Ethernet server library
DHT dht(DHTPIN, DHT11); //инициируем датчик DHT
/*—————- 2 часть —————-*/
void setup() {
pinMode (8, OUTPUT);//инициализируем 8 пин как выход для светодиода
Ethernet.begin (mac, ip);//запускаем сервер с указанными ранее MAC и вашим IP
Serial.begin (9600);
dht. begin();
server. begin();
}
/*—————- 3 часть —————-*/
void loop() {
float h = dht.readHumidity(); //Считываем влажность в переменную «h»
float t = dht.readTemperature(); //Считываем температуру в переменную «t»
EthernetClient client = server.available(); //принимаем данные, посылаемые клиентом
if (client){ //если запрос оканчивается пустой строкой
boolean currentLineIsBlank = true;
//ставим метку об окончании запроса (дословно: текущая линия чиста)
while (client.connected()) { //пока есть соединение с клиентом
if (client.available()) { //если клиент активен
char c = client.read(); //считываем посылаемую информацию в переменную «с»
if (newInfo && c == ‘ ‘){
//если переменная новой информации = 1 и «с», в которой записан запрос, равен пустой строке
newInfo = 0; //то обнуляем переменную поступления новой информации
}
if (c == ‘$’){
// если переменная «с», несущая отправленный нам запрос, содержит символ $
// «$» подразумевает разделение получаемой информации (символов)
newInfo = 1; //то пришла новая информация, ставим метку новой информации в 1
}
//Проверяем содержание URL — присутствует $1 или $2
if (newInfo == 1){ //если есть новая информация
Serial.println (c);
if (c == ‘1’){ //и «с» содержит 1
Serial.println («Включить»);
digitalWrite (8, HIGH); //то зажигаем светодиод
}
if (c == ‘2’){ //если «с» содержит 2
Serial.println («Выключить»);
digitalWrite (8, LOW); //гасим светодиод
}
}
if (c == ‘\n’) { //если «с» равен символу новой строки
currentLineIsBlank = true; //то начинаем новую строку
}
else if (c != ‘\r’) {
//иначе, если «с» не равен символу возврата курсора на начало строки
currentLineIsBlank = false; //то получаем символ на текущей строке
}
if (c == ‘\n’ && currentLineIsBlank) { //выводим HTML страницу
/*—————- 4 часть —————-*/
client. println («HTTP/1.1 200 OK»); //заголовочная информация
client. println («Content-Type: text/html»);
client. println («Connection: close»);
client. println («Refresh: 5»); //автоматическое обновление каждые 5 сек
client. println (); //Это не ошибка, так должно быть
client. println («<!DOCTYPE HTML>»); //HTML тип документа
client. println («<html>»); //открытие тега HTML
client. println («<head> «); //открытие тега Head
client. println («<meta http-equiv=’Content-Type’ content=’text/html ; charset=utf-8’/> «);
client. print («<title>HelpDuino Web Server</title>»); //название страницы
client. println («</head>»); //заголовочная информация
client. println («<body>»);
client. print («<H1>HelpDuino Web Server</H1>»); //заголовк на странице
client. print («<a href=\»/$1\»><button>Включить</button></a>»);
//кнопка включить
client. print («<a href=\»/$2\»><button>Выключить</button></a>»);
//кнопка выключить
client. println («<>br> <br>»); //перенос на след. строчку
client. println («<hr/>»); //линия=====================================
client. println («Tемпература = «); //Температура с DHT 11
client. println (t); //переменная температуры
client. println (» *C»);
client. println («<br> «); //перенос на след. строчку
client. println («Влажность = «); //Влажность с DHT 11
client. println (h); //переменная влажности
client. println (» %\t»);
client. println («<br> «); //перенос на след. строчку
client. println («<hr/>»); //линия=====================================
client. println («</body>»);
client. println («</html>»); //закрываем тег HTML
break; //выход
}
}
}
delay (1); //время на получение новых данных
client. stop(); //закрываем соеднение
}
}

Выше вам представлен скетч нашей программы. С помощью него вы сможете управлять светодиодом и принимать информацию с датчика влажности и температуры DHT11. Отдельно мы уже работали и со светодиодом, и с датчиком DHT11, вы можете рассмотреть их подключение к Arduino в отдельности нажав кнопкой мышки по названию. В самом скетче есть пояснение к каждой строчке кода, но мы дополнительно обговорим некоторые детали

Для начала, я бы хотел разделить код нашей программы на несколько частей. Вы можете увидеть это разделение в самом коде, который представлен выше. Мы будем рассматривать их по отдельности для нашего удобства. Почему я разбил код нашей программы на четыре части, вы сейчас поймете.

Первая часть кода

В первой части кода мы как, обычно, указываем пусть к необходимым библиотекам, номер пинов наших устройств. Для подключения Ethernet Shield нам пришлось указать ip адрес и mac адрес

byte mac = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);

Хотелось бы сказать, что mac адрес не зашит в шилд и вы сами задаете его в скетче. Можно использовать mac адрес, который мы привели в нашем примере! Что касается ip адреса,как известно, по стандартам протокола для частных сетей можно использовать разные адреса, в частности, адресация 192.168… Последующие четыре цыфры могут быть произвольные, но в той последовательности, как у нас приведены в скетче. Важно, что ip адрес, указанный в скетче программы не должен пересекаться с ip адресами всех устройств в домашней сети!

Вторая часть кода

Во второй части кода мы инициируем датчики и запускаем сервер с указанными раннее ip и mac. Тут ничего сложного нет, так что мы пойдем дальше.

Третья часть кода

В третьей части кода происходят основные действия. Подробно их описывать я не буду, ведь в скетче программы все подробно описано. Здесь мы указали переменную «t» и «h».

float h = dht.readHumidity(); //Считываем влажность в переменную «h»
float t = dht.readTemperature(); //Считываем температуру в переменную «t»

Также мы вводим переменную «c» для работы со светодиодом.

while (client.connected()) {
if (client.available()) {
char c = client.read();

Также указываем условие для включения и выключения светодиода

if (c == ‘1’){
Serial.println («Включить»);
digitalWrite (8, HIGH);
}
if (c == ‘2’){
Serial.println («Выключить»);
digitalWrite (8, LOW);
}

Четвертая часть кода

Заключающей частью нашего скетча я выделил html код, который будет отображаться в браузере. Описание каждой строчки вы также можете найти в самом скетче. Это самый простой web-сайт, так что тут ничего сложного нет.

В конце у вас должно получиться что-то похожее на это!

Также вы можете посмотреть видео версию нашего проекта!

Управление Arduino через интернет

Подробности Категория: Arduino Опубликовано 10.11.2015 16:35 Admin Просмотров: 7382

В этой статье описан процесс «общения» с Arduino через интернет. Команды посылаются при помощи ajax запросов, данные запросы принемает python скрипт которые передает их через последовательное соединение serial port в плату arduino. Конечно можно приобрести готовую плату Ethernet для arduino подключить кабель и залить готовую программу. Но можно сделать все по другому.

И для этого нам понадобится:

http сервер;

интерпритатор python.

Код управления на Java Script

//Порт к которому подключен Arduino var serialPort = ‘COM5’; //непосредственно управляющая функция var Arduino = function(command, callback){ $.get(‘c.py’,{ c:command, p:serialPort }, callback); }

В этом коде единственное нужно поменять имя порта, поставить то на которм у вас сидит arduino. Узнать где у вас Arduino можно посмотев в диспетчере устройств windows. Это имя скриптом передается интрепритатору phyton который подключается к arduino.

К примеру елси мы вызвали нашу функций Aruduino() с параметрами «123» то создастся запрос вида «index.html?с=123&p=COM5» который примент python скрипт. Что он из себя представляет.

#!/Python33/python.exe import serial import cgi print («STATUS: 200 OK\n») req = cgi.FieldStorage(); ser = serial.Serial(req.value, 9600, timeout=1) ser.write(bytes(req.value,’latin’)) ser.close() print («ok») По сути он просто принемает параметр «с», и передает его в serial port «p» и далее выводит «ок» Код под Arduino #include <Servo.h> Servo myservo; void setup() { Serial.begin(9600); } String getParam(){ String re; while (Serial.available()) { re.concat(Serial.read()-48); } return re; } int getPin(String p){ return p.substring(0,2).toInt(); } int getVal(String p){ return p.substring(2,6).toInt(); } // Главный цикл void loop() { while (Serial.available()) { char command = (char)Serial.read(); String param = getParam(); int pin = getPin(param); int p; switch (command) { case ‘0’: //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, LOW); break; case ‘1’: //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, HIGH); break; case ‘2’: //Servo myservo.attach(pin); p = getVal(param); myservo.write(p); break; case ‘3’: //Digital read pinMode(pin,INPUT); Serial.print(digitalRead(pin)); break; case ‘4’: { //Analog read int aPin = A0; switch (pin) { case 1: aPin = A1; break; case 2: aPin = A2; break; case 3: aPin = A3; break; case 4: aPin = A4; break; case 5: aPin = A5; break; } Serial.print(analogRead(aPin)); } break; case ‘5’: //Analog write pinMode(pin,OUTPUT); p = getVal(param); analogWrite(pin, p); break; } } }

Через последовательное соединение мы будет передовать данные следующего 1234567 где:

{1} номер команды;

{23} — номер пина;

{4567} — данные пина(если нужны);

К примеру:

  • комманда 112-выставит на 12 -м пине значение HIGH;
  • комманда 012-выставит на 12 -м пине значение LOW;
  • комманда 208100-установит 8 пин как сервопривои и передаст значение 100 (ШИМ модуляция);
  • комманда 310 — установит 10-й пин на ввод и считает с него данные 1 или 0.

Код прогаммы можно дописывать самому.

Таки образом,используя интерпритатор и ajax можно обмениваться данными с arduino главное чтобы был интернет, причем все равно какой интренет wifi, проводной или мобильный. Для доступа в Интернет через сети мобильных провайдеров можно к примеру использовать 3G маршрутизатор.

Подключение Ардуино к роутеру TL-MR3020


Удалённое управление Arduino через Web-интерфейс…
Предполагается, что на роутере установлена OpenWrt и система перенесена на флешку. Как это сделать я подробно описал .
Если OpenWrt установлена, тогда переходим к основной задаче.
Существуют два способа подключения, первый — к UART роутера, второй — через USB.
Оба варианта работают одинаково, однако первый требует разбора роутера и подпаивания контактов:

Второй вариант проще, но придётся ипользовать usb-хаб.

Я опишу оба способа подключения и покажу как сделать простой веб-интерфейс для управления.
Кто будет подключать по usb, может сразу перейти сюда.

Вариант с UART

Вскрываем роутер. Крышка у него приклеена, поэтому берём что-то типа ножа и ковыряем по всему периметру. Пластик достаточно прочный, так что можно не боятся повредить.
Достаём плату и припаиваем три контакта RX, TX и GND, четвёртый контакт — это плюс (3,3V), он нам не нужен.

Теперь зальём в ардуину простенький скетч для проверки.
int led = 13; void setup() { Serial.begin(57600); pinMode(led, OUTPUT); } void loop() { if (Serial.available()) { byte insim = Serial.read(); switch (insim) { case ‘a’: digitalWrite(led, HIGH); break; case ‘b’: digitalWrite(led, LOW); break; } } }
Будем посылать в ардуину символы a и b, в ответ на которые будет зажигаться и гаснуть D13.
Подключаем ардуину как на рисунке:

Ардуина TX ⇨ RX Роутера (синий)
Роутер TX ⇨ RX Ардуина (зелёный)
CND ⇨ CND
Не смотря на то, что чип роутера питается от 3,3V, а ардуина от 5V, никаких проблем не возникает, посему нет необходимости согласовывать уровни.
Подключаем сетевой кабель (или не подключаем если Вы соединяетесь по WIFI) и подаём питание на роутер и ардуину.
Заходим на роутер по ssh (на всякий случай)
Ради интереса смотрим существующие устройства:
ls /dev/tty*
В списке будет присутствовать ttyATH0, это и есть UART.
Обновляем репозиторий…
opkg update
Установим утилиту для настройки порта:
opkg install coreutils-stty

Настроим порт командой…
stty -F /dev/ttyATH0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
Проверяем…
echo ‘a’ > /dev/ttyATH0
D13 загорелась.
echo ‘b’ > /dev/ttyATH0
D13 погасла.
Должно работать, если нет, то возвращаемся и проверяем что не так.
Доведём до ума:
Если у Вас не установлен редактор nano, то исправим ситуацию…
opkg install nano
Добавим в автозагрузку настройку порта:
nano /etc/rc.local
В конец файла (перед exit 0) добавим строчку:
stty -F /dev/ttyATH0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
подсказка nano
Сохранить______Выйти
Перегружаем роутер и проверяем…
reboot
echo ‘a’ > /dev/ttyATH0 echo ‘b’ > /dev/ttyATH0
Поскольку при загрузке (да и в процессе работы) в консоль прилетают различные символы, нам надо их отфильтровывать, а также сделать обратную связь, чтоб в ответ на команду ардуина сообщала о выполнении.
Залейте в ардуину этот скетч: Не забывайте отсоединять провода RX,TX во время прошивки.
int led = 13; byte descript; void setup() { Serial.begin(57600); pinMode(led, OUTPUT); } void loop() { if (Serial.available()>4) // ждём дескриптор и нужный символ { if (Serial.read()==’Y’) // проверяем первый символ, если это ‘Y’, то продолжаем принимать, если нет, то выходим из цикла чтения { for (byte i=0; i < 5; i++) { descript = Serial.read(); } if((descript ==’+’) && (descript ==’=’) && (descript ==’Z’)) { switch (descript) { case ‘a’: digitalWrite(led, HIGH); Serial.println(«OK vkl»); // ответ break; case ‘b’: digitalWrite(led, LOW); Serial.println(«OK otkl»); // ответ break; } } else { for(byte i=0; i < 255; i++) { Serial.read(); } } }// конец if (Serial.read()==’Y’) } // конец чтение порта }

Перед управляющим символом (a,b) будем отправлять четыре символа служащие дескриптором Y+=Z, благодоря этому всё что не нужно отфильтруется и не будет случайных срабатываний.
Открываем параллельно ещё одну ssh-сессию и вводим там команду:
cat /dev/ttyATH0
Здесь будет ответ ардуины.
Проверяем…
echo ‘Y+=Za’ > /dev/ttyATH0 echo ‘Y+=Zb’ > /dev/ttyATH0
И последнее, надо отвязать UART от отладочной консоли. Для этого надо в файле /etc/inittab закомментировать последнюю строчку:
nano /etc/inittab
Вот так:
::sysinit:/etc/init.d/rcS S boot ::shutdown:/etc/init.d/rcS K shutdown #ttyATH0::askfirst:/bin/ash —login
Сохраняем, перегружаем.
Если что-то не работает, то возвращаемся к началу и проверяем что сделали не так.
С первым вариантом покончено, если подключение по usb не интересно, то переходите ниже…

Вариант с USB

Зальём в ардуину проверочный скетч:
int led = 13; void setup() { Serial.begin(57600); pinMode(led, OUTPUT); } void loop() { if (Serial.available()) { byte insim = Serial.read(); switch (insim) { case ‘a’: digitalWrite(led, HIGH); break; case ‘b’: digitalWrite(led, LOW); break; } } }
Будем посылать в ардуину символы a и b в ответ на которые будет зажигаться и гаснуть D13.
Подключаем к роутеру хаб и втыкаем в него флешку и ардуину. Включаем.
Желательно чтобы хаб был с отдельным питанием. Некоторые хабы работают некорректно.
Заходим на роутер по ssh (на всякий случай)
Обновляем репозиторий…
opkg update
Установим драйвера для всех существующих ардуин и утилиту для настройки порта stty:
opkg install kmod-usb-serial-ftdi kmod-usb-acm kmod-usb-serial-pl2303 kmod-usb-serial-cp210x libftdi coreutils-stty
reboot
Можно не перегружать, по идее устройство должно появиться сразу.
Проверим… если нет, тогда перегрузите.
ls /dev/tty*

У Вас может быть /dev/ttyACM0, тогда его и используйте в дальнейших командах и настройках.
Настроим порт командой…
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
Проверяем…
echo ‘a’ > /dev/ttyUSB0
D13 загорелась.
echo ‘b’ > /dev/ttyUSB0
D13 погасла.
Если при посылке пакета ардуина перегружается (диоды моргают, но D13 не горит), тогда нужно поставить электролитический косденсатор5-10мкФ между Reset и GND.
Не забудьте отключать его когда заливаете скетч.
Далее сделаем защиту от случайных срабатываний и обратную связь, чтоб в ответ на команду ардуина сообщала о выполнении.
Если редактор nano отсутствует, то установим…
opkg install nano
Добавим в автозагрузку настройку порта:
nano /etc/rc.local
В конец файла (перед exit 0) добавим строчку:
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
подсказка nano
Сохранить______Выйти
Перегружаем…
reboot
… и проверяем…
echo ‘a’ > /dev/ttyUSB0 echo ‘b’ > /dev/ttyUSB0
Теперь выключаем роутер и прошиваем в ардуину этот скетч:
int led = 13; byte descript; void setup() { Serial.begin(57600); pinMode(led, OUTPUT); } void loop() { if (Serial.available()>4) // ждём дескриптор и нужный символ { if (Serial.read()==’Y’) // проверяем первый символ, если это ‘Y’, то продолжаем принимать, если нет, то выходим из цикла чтения { for (byte i=0; i < 5; i++) { descript = Serial.read(); } if((descript ==’+’) && (descript ==’=’) && (descript ==’Z’)) { switch (descript) { case ‘a’: digitalWrite(led, HIGH); Serial.println(«OK vkl»); // ответ break; case ‘b’: digitalWrite(led, LOW); Serial.println(«OK otkl»); // ответ break; } } else { for(byte i=0; i < 255; i++) { Serial.read(); } } }// конец if (Serial.read()==’Y’) } // конец чтение порта }
Перед управляющим символом (a,b) будем отправлять четыре символа служащие дескриптором Y+=Z, таким образом отфильтруется случайный мусор и не будет случайных срабатываний.
После обработки команды, ардуина будет отправлять ответ.
Возвращаем ардуину в хаб и включаем роутер.
Открываем две параллельные ssh-сессии, в первой водим команду:
cat /dev/ttyUSB0
Здесь будет ответ ардуины.
Во второй пробуем…
echo ‘Y+=Za’ > /dev/ttyUSB0 echo ‘Y+=Zb’ > /dev/ttyUSB0
Всё должно работать, если нет, то возвращаемся и внимательно проверяем.
Если всё получилось, то можно переходить к следующей части.

Интерфейс

Сделаем простой веб-интерфейс для управления двумя лампочками.
Выглядеть будет вот так… Можно понажимать.
Скачайте архив и распакуйте его в рабочую папку сервера, чтоб было так сервер/primer/.
подробная инструкция по установке сервера Lighttpd на OpenWrt.
Проверьте, чтоб в файле /etc/php.ini всё было так, как написано !
Если Вы пользуете Win, то отключите всякие файрволы/антивирусы!
Установим и настроим небольшой прокси-сервер ser2net, он создаёт соединение между сокетом и устройством (/dev/ttyUSB0).
Как показала практика, через ser2net, php-файл работает лучше, нежели обращаясь к устройству напрямую.
opkg update
opkg install ser2net
Редактируем файл конфигурации:
nano /etc/ser2net.conf
Закомментируйте всё строчки в конце и сохраните.
Добавим ser2net в автозагрузку:
nano /etc/rc.local
Добавьте в конец файла вот эту строку:
ser2net -C «3002:raw:0:/dev/ttyUSB0:57600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS»
Должно получится так:
stty -F /dev/ttyUSB0 cs8 57600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl ser2net -C «3002:raw:0:/dev/ttyUSB0:57600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS» exit 0
В примере используется устройство /dev/ttyUSB0, у Вас может быть другое! (ttyATH0 — консоль, ttyACM0 — мега)
Внимание! Строки инициализации должны быть записаны одной строкой (без переноса).
Прошейте в ардуину этот скетч:
int descript; int R1 = 0; // флаг первой лампы D12 int R2 = 0; // флаг второй лампы D13 void setup() { Serial.begin(57600); pinMode(12, OUTPUT); pinMode(13, OUTPUT); } void loop() { if (Serial.available()>4) // ждём дескриптор и нужный символ { if (Serial.read()==’Y’) // проверяем первый символ, если это ‘Y’, то продолжаем принимать, если нет, то выходим из цикла чтения { for (byte i=0; i < 5; i++) { descript = Serial.read(); // добавляем символы в массив } if((descript ==’+’) && (descript ==’=’) && (descript ==’Z’)) // проверяем символы { switch (descript) { case ‘o’: // обновление glavnaia(); // отправка ответа break; case ‘A’: digitalWrite(12, HIGH); R1 = 1; // ставим флаг в единицу (вкл) glavnaia(); break; case ‘a’: digitalWrite(12, LOW); R1 = 0; // ставим флаг в ноль (откл) glavnaia(); break; case ‘B’: digitalWrite(13, HIGH); R2 = 1; glavnaia(); break; case ‘b’: digitalWrite(13, LOW); R2 = 0; glavnaia(); break; } } else { for(byte i=0; i < 255; i++) { Serial.read(); } } }// конец if (Serial.read()==’Y’) } // конец чтение порта } void glavnaia() // отправка ответа { Serial.print(R1); // отсылаем флаг Serial.print(«,»); // запятая для парсинга строки Serial.println(R2); // отсылаем флаг !!!последняя строчка должна быть println!!! }

Добавлены функции для второй лампочки и обновления. (к D12 подключите светодиод через резистор 200-1000 Ом)
Включаем/перегружаем роутер и в браузере заходим по аресу ваш_роутер/primer/
Должно получится так:

Если надпись stD серая, это значит что связь с ардуиной установлена, если красная, то связи нет.
Работа заключается в следующем:
index.html раз в три секунды (интервал можно изменить) запрашивает данные у ардуины (отправляя ей символ о) с помощью функции ajax (ajax позволяет не перегружать страницу).
… function show() /* функция обновления */ { $.ajax({ type: «GET», url: «box2.php?df=o», timeout:400, /* если до роутера «длиные пинги» то этот параметр нужно увеличивать */ cache: false, success: function(data) { var vars = data.split(«,»); /* разбор строки принятой от ардуино */ if(vars.length == dlina) /* проверка длины данных (количество блоков разделённых запятой) */ { $(‘#std’).html(«<v2>stD</v2>»); /* если данные приняты правильно, то надпись stD будет серой */ /* lampR1 */ /* если принята единица, то рисуем жёлтую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */ if(vars==1) $(‘#lampR1’).html(‘<img src=»images/onlamp.png»>’),(r1 = ‘a’); /* если принят ноль, то рисуем серую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */ else if(vars==0) $(‘#lampR1’).html(‘<img src=»images/offlamp.png»>’),(r1 = ‘A’); /* lampR2 */ if(vars==1) $(‘#lampR2’).html(‘<img src=»images/onlamp.png»>’),(r2 = ‘b’); else if(vars==0) $(‘#lampR2’).html(‘<img src=»images/offlamp.png»>’),(r2 = ‘B’); } else { $(‘#std’).html(«<v>stD</v>»); /* если данные приняты НЕправильно, то надпись stD будет красной */ } } }); } $(document).ready(function() { show(); setInterval(‘show()’,3000); /* частота обновления */ }); …
Запрос передаётся php-файлу (box2.php) находящемуся на сервере, который в свою очередь обращается к ардуине через сокет ser2net.
<?php if($fp = fsockopen(«localhost», 3002, $errno, $errstr, 1)) // открываем порт, в качестве посредника между роутером и ардуиной выступает ser2net { fwrite($fp, ‘Y+=Z’); // отправляем в порт дескриптор Y+=Z fwrite($fp, $_GET); // отправляем в порт символ полученый от html странички stream_set_timeout($fp, 0, 150000); // полезный таймаут, если ответа нет, то поток закроется через 150 мс $bufft = fgets($fp); // получаем ответ от ардуины fclose($fp); // закрываем порт echo $bufft; // отправляем ответ клиенту } else { usleep(70000); $fp = fsockopen(«localhost», 3002, $errno, $errstr, 1); fwrite($fp, ‘Y+=Z’); fwrite($fp, $_GET); stream_set_timeout($fp, 0, 150000); $bufft = fgets($fp); fclose($fp); echo $bufft; } ?>
Ардуина получает команду, обрабатывает её и отправляет ответ, который по той же цепочке возвращается html-страничке (index.html).
… switch (server) // simvol { case ‘o’: // obnovlenie glavnaia(); break; … void glavnaia() // отправка ответа { Serial.print(R1); // отсылаем флаг Serial.print(«,»); // запятая для парсинга строки Serial.println(R2); // отсылаем флаг !!!последняя строчка должна быть println!!! }
Html-страничка разбирает ответ и выводит значения на экран.
… success: function(data) { var vars = data.split(«,»); /* разбор строки принятой от ардуино */ if(vars.length == dlina) /* проверка длины данных (количество блоков разделённых запятой) */ { $(‘#std’).html(«<v2>stD</v2>»); /* если данные приняты правильно, то надпись stD будет серой */ /* lampR1 */ /* если принята единица, то рисуем жёлтую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */ if(vars==1) $(‘#lampR1’).html(‘<img src=»images/onlamp.png»>’),(r1 = ‘a’); /* если принят ноль, то рисуем серую лампочку. Переменной присваевается символ, который будет отослан при следующем нажатии */ else if(vars==0) $(‘#lampR1’).html(‘<img src=»images/offlamp.png»>’),(r1 = ‘A’); …
Если открыть ещё одну страничку (или зайти с другого устройства) и включить лампочку, то на первой страничке (в течении 3 сек.) тоже включится лампочка.
Для этого и нужно обновление.
Нажатие на лампочку работает так же как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния лампочки), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль). Ответ разбирается в html-странице и в зависимости от флагов выводит картинку включённой или отключённой лампочки.
Для лучшего понимания откройте файл index.html из архива, и посмотрите комментарии.
Внимание! Если Вы редактируете файл на роутере, то удалите все комментарии, в противном случае могут возникнуть проблемы с русской кодировкой.
Если редактируете файлы на виндовс-машине, то пользуйтесь редактором Notepad++.
Если что-то не так, то возвращаемся и проверяем всё с удвоенным вниманием. Проверяем права на файлы, правильность путей и устройств.
В следующей части — «умный дом» на основе ардуино.
Вот можно скачать библиотеку для разгона Arduino.
Обсудить на форуме…

Схема подключения:

Для этого нам понадобится:

Модуль на 4 реле: http://ali.pub/1bbr3b

Ethernet Shield W5100 : http://ali.pub/1al6dr

Arduino uno: http://ali.ski/gC_mOa

В данном примере, показано как можно управлять реле через web интерфейс используя w5100 shield.

На веб странице расположено 4 кнопки. Каждая кнопка отвечает за включение и выключение определенного реле. Все “написание” кода производится в программе FLProg.

Ниже указаны все блок схемы, которые нам понадобятся для реализации данного проекта в FLProg.

Блок схемы в программе FLProg:

Схема подключения реле и шилда к ардуино приведена в самом начале статьи.

После подключения всех компонентов необходимо скопировать программный код приведенный ниже и вставить его в программу Arduino IDE и загрузить этот программный код в саму плату Arduino.

#include <Ethernet.h> #include <SPI.h> byte ethernet_mac = {0x78, 0xAC, 0xC0, 0x43, 0x7B, 0xC1}; IPAddress ethernet_ip(192, 168, 1, 177); byte ethernet_dns = {192, 168, 1, 1}; byte ethernet_gateway = {192, 168, 1, 1}; byte ethernet_subnet = {255, 255, 255, 0}; EthernetServer _tspWebServer(80); EthernetClient _tspWebServer_client; bool _trgt4 = 0; bool _trgt4I = 0; bool _trgrt1 = 0; bool _trgrt1I = 0; bool _trgrt2 = 0; bool _trgrt2I = 0; bool _trgrt3 = 0; bool _trgrt3I = 0; bool _trgrt4 = 0; bool _trgrt4I = 0; bool _trgrt5 = 0; bool _trgrt5I = 0; bool _trgt1 = 0; bool _trgt1I = 0; bool _WSP2_A1 = 0; bool _WSP2_A2 = 0; bool _WSP2_A3 = 0; bool _WSP2_A4 = 0; bool _trgt2 = 0; bool _trgt2I = 0; bool _trgt3 = 0; bool _trgt3I = 0; void setup() { Ethernet.begin(ethernet_mac, ethernet_ip, ethernet_dns, ethernet_gateway, ethernet_subnet); delay(1000); _tspWebServer.begin(); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(9, OUTPUT); } void loop() {_tspWebServer_client = _tspWebServer.available(); if (_tspWebServer_client) { boolean _WSCLineIsBlank = true; String _WSCReqest=»»; bool _WSCIsFirsLine=1; bool _WSCIsParse=0; int _WSCPageNumber=0; while (_tspWebServer_client.connected()) { while( _tspWebServer_client.available()) {char _tempWebServerChar = _tspWebServer_client.read(); if(_WSCIsFirsLine) {_WSCReqest+= _tempWebServerChar;} if (_tempWebServerChar == ‘\n’ && _WSCLineIsBlank) {_sendWebServerPage(_WSCPageNumber); break;} if (_tempWebServerChar == ‘\n’) {_WSCLineIsBlank = true; _WSCIsFirsLine=0; if (!_WSCIsParse){ _WSCPageNumber=_parseWebServerReqest(_WSCReqest); _WSCIsParse=1;} } else if (_tempWebServerChar != ‘\r’) { _WSCLineIsBlank = false;} }}} if (_WSP2_A1) { if (_trgrt1I) { _trgrt1 = 0;} else {_trgrt1 = 1; _trgrt1I = 1;} } else {_trgrt1 = 0; _trgrt1I = 0;}; bool _tmp1 = _trgrt1; if (_tmp1) { if (! _trgt1I) _trgt1 = ! _trgt1; } _trgt1I = _tmp1; if (_WSP2_A2) { if (_trgrt3I) { _trgrt3 = 0;} else {_trgrt3 = 1; _trgrt3I = 1;} } else {_trgrt3 = 0; _trgrt3I = 0;}; bool _tmp2 = _trgrt3; if (_tmp2) { if (! _trgt2I) _trgt2 = ! _trgt2; } _trgt2I = _tmp2; if (0) { if (_trgrt2I) { _trgrt2 = 0;} else {_trgrt2 = 1; _trgrt2I = 1;} } else {_trgrt2 = 0; _trgrt2I = 0;}; if (_WSP2_A3) { if (_trgrt4I) { _trgrt4 = 0;} else {_trgrt4 = 1; _trgrt4I = 1;} } else {_trgrt4 = 0; _trgrt4I = 0;}; bool _tmp3 = _trgrt4; if (_tmp3) { if (! _trgt3I) _trgt3 = ! _trgt3; } _trgt3I = _tmp3; if (_WSP2_A4) { if (_trgrt5I) { _trgrt5 = 0;} else {_trgrt5 = 1; _trgrt5I = 1;} } else {_trgrt5 = 0; _trgrt5I = 0;}; bool _tmp4 = _trgrt5; if (_tmp4) { if (! _trgt4I) _trgt4 = ! _trgt4; } _trgt4I = _tmp4; digitalWrite(6, !(_trgt1)); digitalWrite(7, !(_trgt2)); digitalWrite(8, !(_trgt3)); digitalWrite(9, !(_trgt4)); } void _sendWebServerPage(int sendPageNumber) {_tspWebServer_client.println(«HTTP/1.1 200 OK»); _tspWebServer_client.println(«Connection: close»); _tspWebServer_client.println(); _tspWebServer_client.println(«<!DOCTYPE HTML PUBLIC «»-//W3C//DTD HTML 4.01 Transitional//EN»»>»); _tspWebServer_client.println(«<html><head>»); _tspWebServer_client.println(«<META content=»»text/html; charset=utf-8″» http-equiv=»»Content-Type»»>»); _tspWebServer_client.println(«</head><body>»); if (sendPageNumber ==1) {_sendWebServerPage1();} if (sendPageNumber ==2) {_sendWebServerPage2();} _tspWebServer_client.println(«</body></html>»); delay(1); _tspWebServer_client.stop();} int _parseWebServerReqest(String reqestAddres) { int index; int result=0; index=reqestAddres.indexOf(«/»); reqestAddres =_stringWithoutCharWithIndex(reqestAddres,0,(index)); index=reqestAddres.indexOf(» «); reqestAddres =_stringWithoutCharWithIndex(reqestAddres,index,(reqestAddres.length()-index)); if (reqestAddres==»»){result= 1;} if (reqestAddres==»1″){_WSP2_A1=1; result= 2;} else {_WSP2_A1=0;} if (reqestAddres==»2″){_WSP2_A2=1; result= 2;} else {_WSP2_A2=0;} if (reqestAddres==»3″){_WSP2_A3=1; result= 2;} else {_WSP2_A3=0;} if (reqestAddres==»4″){_WSP2_A4=1; result= 2;} else {_WSP2_A4=0;} return result; } String _stringWithoutCharWithIndex(String value, int index,int count) { String result=»»; for (int i=0; i <= value.length(); i++){ if((i<index) ||(i>(index+count))){ result+=value.charAt(i); } } return result; } void _sendWebServerPage1(void) { _tspWebServer_client.println(«»); _tspWebServer_client.println(«<table width=»»100%»» height=»»100%»» cellspacing=»»0″» cellpadding=»»0″» border=»»0″» bgcolor=»»FFFFFF»»>»); _tspWebServer_client.println(«<tr>»); _tspWebServer_client.println(«<td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<table width=»»1200px»» align=»»center»» border=»»1″» cellspacing=»»0″» cellpadding=»»20″»>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<tr>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<td width=»»60%»» valign=»»top»»>»); _tspWebServer_client.println(«<p><font color=»»000000″» size=»»5″» face=»»Open Sans»»>Управление нагрузкой</font></p>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<table width=»»60%»» cellspacing=»»0″» cellpadding=»»20″»>»); _tspWebServer_client.println(«<tr>»); _tspWebServer_client.println(«<td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<p><a href=»»http://192.168.1.177/1″»><button>Реле 1</button></a></p>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<p><a href=»»»»http://192.168.1.177/2″»><button>Реле 2</button></a></p>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<p><a href=»»»»http://192.168.1.177/3″»><button>Реле 3</button></a></p>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«<p><a href=»»»»http://192.168.1.177/4″»><button>Реле 4</button></a></p>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</tr>»); _tspWebServer_client.println(«</table>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</table>»); _tspWebServer_client.println(«»); _tspWebServer_client.println(«</td>»); _tspWebServer_client.println(«</tr>»); _tspWebServer_client.println(«</table>»); _tspWebServer_client.println(«»); }void _sendWebServerPage2(void) { _tspWebServer_client.println(«<meta http-equiv=»»refresh»» content=»»0;URL=http://192.168.1.177″»>»); }

Ссылка на скетч:

Изучим протокол передачи гипертекста HTTP. Научимся разрабатывать WEB серверы в системе Ардуино. Покажу простой способ, как оформлять информацию для браузера с помощью HTML-редактора.

Предыдущий урок Список уроков Следующий урок

Обычно уроки я строю по принципу – даю формальное описание функций, методов, протоколов…, а затем, используя эту информацию, разрабатываю учебные проекты.

Информация о протоколе HTTP объемная. Что-то нам не пригодится, поэтому в этом уроке я решил все перемешать. Последовательно рассказывать о протоколе и тут же демонстрировать его применение на практике.

Я не претендую на полноту освещения этой объемной темы. Рассмотрю варианты использования HTTP протокола наиболее приемлемые для систем на базе Ардуино.

Общая информация.

HTTP — это протокол уровня приложений (высокого уровня) для передачи гипертекстовых документов.

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

Скорее это устаревшее определение HTTP. Сейчас по этому протоколу передается не только текстовая, а практически любая информация, в том числе картинки и фильмы. Правильнее сказать, что это протокол передачи гипермедиа данных.

В настоящее время HTTP используется сервисом World Wide Web (Всемирная Сеть) для получения информации с WEB-сайтов. На ваш компьютер информация с сайтов интернета попадает через именно этот протокол. И то, что вы читаете сейчас доставлено посредством HTTP.

Надо твердо помнить, что:

  • HTTP работает по технологии клиент-сервер. Клиент посылает запрос и получает в ответ информацию с сервера. В нашем случае:
    • клиентом является WEB-браузер на компьютере;
    • сервером – плата Ардуино с сетевым Ethernet-модулем ENC28j60.
  • Запрос будет формировать браузер, отвечать – удаленный сервер.
  • HTTP – протокол уровня приложений, т.е. приложение на компьютере (веб-браузер) обменивается данными с приложением на удаленном сервере (в нашем случае с резидентной программой Ардуино).
  • Обмен данными происходит через TCP-соединение.
  • Для создания WEB-сервера нет специальных функций библиотеки. Мы будем устанавливать TCP-соединение и через него передавать данные в формате HTTP.
  • Основной объект для HTTP протокола – это ресурс (URI – Uniform Resource Identifier ), который задается в запросе клиента. Обычно ресурсом являются файлы, расположенные на сервере, но может быть что угодно, даже нечто абстрактное. Для идентификации ресурсов используются глобальные идентификаторы URI, т.е. адреса Интернета.
  • Стандартным портом для HTTP считается порт 80.
  • Сама текстовая информация обычно передается на языке гипертекстовой разметки HTML.

Аппаратная часть, для которой мы будем разрабатывать программы в этом уроке, описывается в уроке 63 и была нами использована в уроках 64 и 69. Это плата Arduino UNO R3 и модуль ENC28J60 подключенный к роутеру прямым кабелем.

Структура протокола.

Клиент и сервер обмениваются сообщениями – блоками данных.

В общем виде обмен информацией происходит так:

  • клиент устанавливает с сервером TCP-соединение;
  • посылает на сервер сообщение-запрос;
  • в ответ получает сообщение-ответ;
  • дальше TCP-соединение может быть разорвано или ожидать новых блоков данных.

Каждое сообщение состоит из 3 частей:

  • Стартовая строка – задает тип сообщения.
  • Заголовки – дают серверу дополнительную информацию о запросе, о клиенте, параметры обмена и прочие технические сведения.
  • Тело сообщения – собственно данные, информация. Тело заголовка должно обязательно отделяться от предыдущих частей пустой строкой.

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

Структура запроса.

Запрос это сообщение от клиента серверу.

Давайте напишем простую программу, позволяющую увидеть запросы клиента. Немного изменим первую программу из урока 64.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

// TCP сервер, выводит полученные данные
#include <SPI.h>
#include <UIPEthernet.h>

// определяем конфигурацию сети
byte mac = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес
byte ip = {192, 168, 1, 10}; // IP-адрес

EthernetServer server(80); // создаем сервер, порт 80
EthernetClient client; // объект клиент
boolean clientAlreadyConnected= false; // признак клиент уже подключен

void setup() {
Ethernet.begin(mac, ip); // инициализация контроллера
server.begin(); // включаем ожидание входящих соединений
Serial.begin(9600);
Serial.print(«Server address:»);
Serial.println(Ethernet.localIP()); // выводим IP-адрес контроллера
}

void loop() {
client = server.available(); // ожидаем объект клиент
if (client) {
// есть данные от клиента
if (clientAlreadyConnected == false) {
// сообщение о подключении
Serial.println(«Client connected»);
Serial.println(«»);
clientAlreadyConnected= true;
}

while(client.available() > 0) {
char chr = client.read(); // чтение символа
Serial.write(chr);
}
}
}

Программа:

  • при запуске выводит IP-адрес сервера;
  • при установке TCP-соединения выдает сообщение;
  • выводит в монитор последовательного порта все, что поступает с клиента.

Запустим.

В строке адреса браузера наберем IP-адрес сервера (у меня 192.168.1.10).

Браузер пошлет HTTP-запрос на сервер. Мы увидим его в мониторе последовательного порта.

Стартовая строка.

Стартовая строка запроса имеет следующий формат:

МЕТОД URI HTTP/Версия

  • Метод – тип запроса.
  • URI – идентификатор ресурса. Определяет путь к запрашиваемому файлу.
  • Версия – разделенные точкой цифры.

В нашем случае: GET / HTTP/1.1

  • GET – метод.
  • / — URI. Мы обратились к серверу без указания дополнительных идентификаторов ресурса, т.е. просто по адресу 192.168.1.10.
  • HTTP/1.1 – версия HTTP.

Методы.

Методы это команды серверу. Они сообщают серверу, что он должен делать.

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

Методы чувствительны к регистру.

Система может использовать любые методы, даже не документированные в протоколе. Если сервер не распознал указанный метод, он должен вернуть код состояния 501. Если метод известен, но не может быть применим к указанному ресурсу, то возвращается код 405.

Любой сервер обязан обрабатывать методы GET и HEAD.

На практике востребованными являются только методы GET и POST. Мы будем использовать только эти запросы.

Метод Описание
GET Применяется для запроса содержимого ресурса. GET-запрос может запустить какой-либо процесс на сервере, информации о ходе выполнения которого обычно передается в ответном сообщении.

Через GET-запросы клиент может передавать параметры. Параметры отделятся от URI символом “?”. Параметры разделяются символом “&”.

GET /path/resourse?parameter1=value1& parameter2=value2 HTTP/1.1

HEAD Действие аналогично методу GET, за исключением того, что в ответе отсутствует тело сообщения.
POST Используется для передачи данных ресурсу. Например, комментарии к записям сайта или заполнение регистрационных форм. Пользователь вводит текст в окне браузера, данные передаются на сервер и сохраняются там. Также, с помощью POST-метода на сервер загружаются файлы.

Передаваемые методом POST данные включаются в тело запроса.

В следующем уроки, при реализации методов GET и POST, я расскажу о них подробнее.

Заголовки.

Заголовки передают серверу дополнительную информацию о клиенте и о запросе. Формат заголовков представляет собой название параметра и значение с двоеточием между ними. Название параметра состоит из любых кодов символов, кроме перевода строки. Двоеточие должно примыкать к названию параметра без пробела.

Блок заголовков должен отделяться от тела сообщения хотя бы одной пустой строкой.

В нашем запросе блок заголовков выглядит так.

Почти вся информация понятна без расшифровки.

Заголовки разделяются на 4 группы:

  • Основные – могут включаться в любое сообщение сервера и клиента.
  • Заголовки запроса – применяются только в запросах клиентов.
  • Заголовки ответа – используются только в ответах от серверов.
  • Заголовки сущности – используются для описания тела сообщения. Определяют вид представления информации конечному пользователю.

Если не хватает стандартных, то заголовки можно вводить свои.

Тело сообщения.

В запросе тело сообщения бывает только при использовании метода POST. Об этом в следующем уроке.

Структура ответа.

После получения и обработки запроса, сервер посылает клиенту ответ.

Стартовая строка ответа должна выглядеть так:

HTTP/ВЕРСИЯ КодСостояния Пояснения

  • Версия – разделенные точкой цифры.
  • Код состояния – число из 3 цифр. Говорит клиенту о результате выполнения запроса и определяет его дальнейшие действия. Набор кодов состояния описан в протоколе.
  • Пояснение – короткое текстовое пояснение кода состояния. Необязательное поле, не влияющее на действия клиента.

Стартовая строка ответа может выглядеть так:

HTTP/1.1 200 OK

Код состояния.

Представляет собой 3-значное число. Первая цифра определяет класс состояния.

Необязательный параметр стартовой строки ПОЯСНЕНИЕ разъясняет код состояния в коротком текстовом сочетании.

200 OK
102 Processing
202 Accepted
304 Not Modified
407 Proxy Authentication Required
500 Internal Server Error

По коду ответа клиент узнает о результатах его запроса и решает, какие действия предпринять.

Существует 5 классов кодов.

Код состояния Класс Назначение
1XX Информационный Информирование о процессе передачи данных.
2XX Успех Информирование об успешной обработке запроса клиента.
3XX Перенаправление Сообщает клиенту о необходимости сделать запрос по другому URI.
4XX Ошибка клиента Информирует об ошибках со стороны клиента.
5XX Ошибка сервера Информирует о неудачных операциях по вине сервера.

Тело сообщения.

Тело сообщение это собственно информация ресурса, которую запросил клиент. Кодировка и формат данных указывается в поле заголовков. Тело сообщений, предназначенных для браузеров, содержит HTML-код.

Конечно, это значительно сокращенная форма представления протокола HTTP. Но для наших целей вполне достаточно.

Создание простого WEB-сервера.

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

Надо:

  • установить TCP-соединение;
  • принять запрос клиента;
  • выделить в нем пустую строку, те окончание блока заголовков (для простого GET-запроса это окончание запроса);
  • послать ответ клиенту;
  • разорвать соединение;
  • все операции запротоколировать в мониторе последовательного порта.

Вот скетч такого сервера.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

// WEB сервер, передает клиенту текстовую строку
#include <SPI.h>
#include <UIPEthernet.h>

// определяем конфигурацию сети
byte mac = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес
byte ip = {192, 168, 1, 10}; // IP-адрес

EthernetServer server(80); // создаем сервер, порт 80
EthernetClient client; // объект клиент

boolean flagEmptyLine = true; // признак строка пустая
char tempChar;

void setup() {
Ethernet.begin(mac, ip); // инициализация контроллера
server.begin(); // включаем ожидание входящих соединений
Serial.begin(9600);
Serial.print(«Server address:»);
Serial.println(Ethernet.localIP()); // выводим IP-адрес контроллера
Serial.print(«»);
}

while (client.connected()) {
if (client.available()) {
tempChar = client.read();
Serial.write(tempChar);

if (tempChar == ‘\n’ && flagEmptyLine) {
// пустая строка, ответ клиенту
client.println(«HTTP/1.1 200 OK»); // стартовая строка
client.println(«Content-Type: text/html; charset=utf-8»); // тело передается в коде HTML, кодировка UTF-8
client.println(«Connection: close»); // закрыть сессию после ответа
client.println(); // пустая строка отделяет тело сообщения
client.println(«<!DOCTYPE HTML>»); // тело сообщения
client.println(«<html>»);
client.println(«<H1> Первый WEB-сервер</H1>»); // выбираем крупный шрифт
client.println(«</html>»);
break;
}
if (tempChar == ‘\n’) {
// новая строка
flagEmptyLine = true;
}
else if (tempChar != ‘\r’) {
// в строке хотя бы один символ
flagEmptyLine = false;
}
}
}
delay(1);
// разрываем соединение
client.stop();
Serial.println(«Break»);
}
}

Загрузил скетч в плату.

Запустил браузер GoogleChrome.

Набрал адрес моего сервера 192.168.1.10. В браузере появилось сообщение.

Сообщение состояло из иероглифов, пока я не задал правильную кодировку строкой:

client.println(«Content-Type: text/html; charset=utf-8»); // тело передается в коде HTML, кодировка UTF-8

Ардуино для кириллических символов использует кодировку UTF-8. В большинстве браузеров по умолчанию другая кодировка.

Этот пример показывает, как заголовки влияют на отображение информации.

В мониторе последовательного порта видно, что появилось 2 запроса.

Вторым запросом Google Chrome пытается получить с сервера иконку favicon, чтобы отобразить ее рядом с информацией ресурса.

Я перешел на браузер Internet Explorer.

В нем только один запрос. Дальнейшие проверки я буду производить с веб-обозревателем Internet Explorer.

Более сложный WEB-сервер.

Первый вариант сервера выводит одну и ту же статичную информацию. Мы его создали для демонстрации принципа организации HTTP-обмена. Вряд ли он будет востребован на практике.

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

Чтобы не подключать к плате Ардуино дополнительные элементы, давайте передадим клиенту значение функции millis(), т.е. время с момента включения сервера.

Заодно немного приукрасим текстовую информацию, которая появится в окне браузера.

Информация для браузера в теле сообщения обычно передается HTML-кодом. Это совершенно другая, очень объемная тема. На нее есть прекрасные уроки. Для разработки WEB-серверов необходимо знать основы HTML-языка. Я покажу, как упростить процесс создания HTML-сообщений.

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

Существует огромное количество простых и сложных, платных и бесплатных редакторов. Можете выбрать по своему вкусу. Чтобы не заниматься загрузкой и установкой, я использовал онлайн HTML-редактор Vulk. Он мне первым попался под руку.

Я создавал сервер в такой последовательности.

  • Запустил HTML-редактор http://vulk.ru/.
  • В меню выбрал Визуальный редактор.

  • Написал и раскрасил текстовую информацию. Оформить, конечно, можно было и красивее.

Число 34,56 – показатель времени я пока задал статическим.

  • Нажал закладку HTML в левом нижнем углу редактора.

Эти строчки надо передать клиенту.

Т.е. вставить их в тело сообщения.

Символ “двойные кавычки” (“) используется в функции prrint() для задания строки. Поэтому кавычки внутри HTML-кода надо экранировать символом ”\”.

Другой вариант – подготовить текстовую строку и передать ее в функцию print() через указатель.

Загрузим скетч.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

При трансляции получим сообщение о том, что оперативная память почти заполнена.

Пока проигнорируем и запустим браузер. Запросим сервер.

Мы получили статическое сообщение, оформленное нами в HTML-редакторе.

Беспокоит предупреждение при компиляции “Недостаточно памяти, программа может работать нестабильно.” Причина его понятна. Мы задали много текстовой информации в строках функции print() и почти заполнили всю оперативную память. А сообщения могут быть намного длиннее.

Выход – хранить текстовую информацию не в ОЗУ, а в FLASH памяти. У микроконтроллера ATmega 328 объем FLASH 32 кбайт, а ОЗУ всего 2 кбайт.

Это делается просто. Достаточно использовать макрос F() библиотеки pgmspace.h. Я писал о ней в уроке 27, но макроса F() не касался.

Так вот, если текстовую строку разместить в качестве аргумента макроса F(), то она будет сохранена в FLASH.

Serial.print(“test”); // строка ”test” размещается в ОЗУ
Serial.print(F(“test”)); // строка ”test” размещается в FLASH

Перепишем заголовки и тело сообщения с учетом вышесказанного.

client.println(F(«HTTP/1.1 200 OK»)); // стартовая строка
client.println(F(«Content-Type: text/html; charset=utf-8»)); // тело передается в коде HTML, кодировка UTF-8
client.println(F(«Connection: close»)); // закрыть сессию после ответа
client.println(); // пустая строка отделяет тело сообщения

Вот скетч полностью.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

Теперь трансляция проходит без пугающих предупреждений. И оперативной памяти используется намного меньше, 1318 байт . В предыдущем варианте программы было 1696.

Здесь я вынужден сделать небольшое отступление. До последнего варианта программы все работало идеально. Как только я стал использовать в программе макрос F() сервер начал виснуть. На второе, третье обращение. Я убрал макрос и вставил несколько одинаковых строк с функцией println(). Сервер периодически зависал. Я заменил плату Arduino UNO R3, затем модуль ENC28j60. Ничего не помогало. Система заработала стабильно только после того, как я подключил модуль к плате Arduino Nano. Мощности стабилизатора платы 3,3 В для питания ENC28j60 явно не хватает. Поэтому я использовал внешний стабилизатор LD1117.

После этого все заработало идеально. К серверу по последнему варианту программы я сделал более 2000 запросов.

Я не стал разбираться в причине происходящего. Могу предположить, что либо не хватает пиковой мощности стабилизатора 3,3 В платы Arduino UNO, либо библиотека UIPEthernet не работает с этой платой. Если кто-то разберется в этом вопросе – напишите. Я добавлю эту информацию в урок. Можно создать тему на форуме сайта.

Дальнейшие испытания я производил на связке Arduino Nano — модуль ENC28j60.

Последний вариант сервера дает в ответе только статичное сообщение. Давайте часть строки ”34,56” заменим на вывод значения функции millis().

Функция millis() возвращает значение времени в мс, а нам надо в секундах. Сделаем простейшее преобразование.

client.print( (float)millis() /1000. ); // вывод времени в секундах

Разорвем длинную строку HTML кода и вместо 34,56 вставим вывод времени.

В итоге тело сообщения будет выглядеть так.

А весь скетч будет таким.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

В браузере увидим время.

Можно добавить в заголовки ответа строку

client.println(F(«Refresh: 2»)); // обновить страницу автоматически

Тогда браузер будет сам обновлять страницу каждые 2 секунды.

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

В течение часа мой сервер отработал в таком режиме. Никаких сбоев, зависаний.

Работает сервер и при обращении с телефона.