статье

advertisement
Современные информационные технологии/Компьютерная инженерия
Мясищев А.А.
Хмельницкий национальный университет, Украина
О возможности построения универсального Web-сервера
на Arduino для отображения информации и управления по
TCP/IP сети
Рассмотрим построение web – сервера на микроконтроллере AVR
(ATmega328) и Ethernet контроллере w5100 производителя WIZNET. На
устройстве должна быть установлена
SD карта памяти, на которой
располагаются HTML файлы, файлы JPG и PDF. К серверу также должен быть
подключен температурный датчик DS18B20 и мобильный телефон. При
обращении к серверу по Интернет браузер должен отображать HTML
документы, температуру в помещении, выполнять дозвон с подключенного к
нему мобильного телефона на заданный мобильный телефон. Сервер также
должен работать с концевым выключателем с целью возможности оповещения
клиента через мобильный телефон при замыкании выключателя (например, при
открытии входной двери помещения). Для изучения возможности решения
такой задачи воспользуемся платформой Arduino[1].
Arduino
-
аппаратная
вычислительная
платформа,
основными
компонентам которой являются простая плата ввода/вывода и среда разработки
на языке Processing/Wiring. Плата Arduino состоит из микроконтроллера Atmel
AVR (например, ATmega328 и ATmega1280) и элементной обвязки для
программирования и интеграции с другими схемами. На каждой плате
обязательно присутствуют линейный стабилизатор напряжения 5 В и 16 МГц
кварцевый генератор. В микроконтроллер предварительно прошит загрузчик,
поэтому внешний программатор не используется и прошивка выполняется с
USB порта компьютера. Платы Arduino позволяют использовать большую часть
I/O выводов микроконтроллера во внешних схемах. Например, в плате Arduino
UNO (рис.1) доступно 14 цифровых вводов/выводов (уровни "LOW" -0В и
"HIGH" -5В), 6 из которых могут выдавать ШИМ сигнал, и 6 аналоговых
входов(0-5В). На рынке доступны несколько внешних плат расширения,
известных как "shields".
Рис.1. Arduino UNO
Интегрированная
среда
разработки
Arduino
(рис.2)
-
это
кроссплатформенное приложение на Java, включающее в себя редактор кода,
компилятор и модуль передачи прошивки в плату. Среда разработки основана
на
языке
программирования
Processing,
а
язык
программирования
микроконтроллеров аналогичен языку, используемому в проекте Wiring. Здесь
программы обрабатываются с помощью препроцессора, а затем компилируется
с помощью AVR-GCC. Программное обеспечение включает много библиотек
для работы с внешними устройствами.
Рис.2. Интегрированная среда разработки Arduino
Для функционирования web – сервера необходим контроллер Ethernet
Shield W5100 (рис.3), который устанавливается с помощью разъемов на плате
Arduino. Контроллер основан на Ethernet –микросхеме Wiznet W5100, которая
также поддерживает стеки TCP/IP и UDP в IP-сети. Для создания программ,
которые подключают Arduino к сети при помощи данного контроллера,
используется библиотека Ethernet. Плата Ethernet Shield W5100 имеет
стандартный разъём RJ-45 для подключения к сети.
Рис.4. Arduino Ethernet Shield W5100
Контроллер также имеет разъём для карт памяти типа micro-SD, которая
может использоваться для хранения файлов. Разъем micro-SD доступен при
помощи библиотеки SD Library.
Arduino осуществляет связь с W5100 и картой SD посредством шины SPI
(через разъём ICSP, расположенный справа на рис.1). При
использовании
библиотек Ethernet и SD вывод № 10 платы Arduino(формирует сигнал SS
шины SPI) используется для выбора W5100, а ввод № 4 - для карты SD. Эти
выводы не могут быть использованы для другого ввода-вывода. Необходимо
учитывать, что на плате Arduino Mega, аппаратный вывод SS № 53, не
используется для выбора ни W5100, ни карты SD, но он должен быть
сконфигурирован как вывод, иначе интерфейс SPI не будет работать.
Микросхема W5100 и карта SD разделяют шину SPI, поэтому
одновременно они работать
не могут.
Если используются оба этих
периферийных
устройства
в
программе,
следует
использовать
соответствующие им библиотеки. Однако если не используется ни одно из этих
периферийных устройств, следует явно отключить их. Чтобы это сделать,
необходимо сконфигурировать вывод платы № 4 для SD как выход и записать
в него "1". Для W5100 необходимо сделать то же самое но для вывода № 10.
В
работе
представлена
устойчиво
работающая
программа
универсального web - сервера на Arduino UNO (ATmega328)[2] , которая
отображает web - страницы с иллюстрациями и аппаратно с помощью реле
сбрасывает сама себя примерно каждые две минуты, выполняет дозвон и
считывает показания температурного датчика. Сброс необходим для выхода из
"зависаний", которые наблюдаются при интенсивном обращении со стороны
клиента. Такие "зависания" наблюдались на платах Arduino UNO и Arduino
Mega. Анализ показал, что зависания возникают при передаче значительных
объёмов данных при активном обращении к серверу как со стороны одного
клиента, так и со стороны нескольких. Если web – страничка имеет размер в
пределах одного Ethernet пакета, "зависаний" не наблюдалось. Анализ показал,
что зависает контроллер Ethernet Shield W5100. Для обеспечения устойчивой
работы сервера была использована библиотека VEduino[3] предназначенная
для программирования счетчиков – таймеров. Согласно программе через
определенный интервал времени выполнялось прерывание от таймера –
счетчика 1. Управление передавалось функции обработки прерывания
ISR(TIMER1_COMPA_vect). Каждое обращение к этой функции приводило к
увеличению на единицу переменной s. Как только ее значение превышало 500,
на 7-м выводе платы Arduino устанавливался высокий уровень, сигнал поступал
на
базу
транзистора,
который
с
помощью
реле
замыкал
RESET
микроконтроллера на “землю” (рис.4). В этом случае происходил аппаратный
сброс всего устройства и если контроллер до этого "зависал", то после RESET
работа всего устройства возобновлялась.
Сервер также с помощью датчика DS18B20 определяет температуру в
помещении и передает ее браузеру после нажатия на ссылку "информация".
При замыкании концевого выключателя, например при открывании входной
двери, на выводе 5 платы Arduino дважды устанавливается высокий уровень,
поступающий на вход транзистора, замыкающего реле два раза (рис. 4).
Контакты реле подключены к кнопке мобильного телефона “поднять трубку”,
после чего телефон дозванивается по последнему номеру, по которому он ранее
дозванивался.
выполнить
Программа сервера также дает возможность принудительно
дозвон
при
условии
подключения
к
нему
по
адресу:
http://192.168.1.100/tele.
Рис.4. Схема подключения к Arduino
Представленная ниже программа web - сервера имеет подробные
комментарии, поэтому нет необходимости ее детального описания.
#include <SPI.h>
#include <SD.h>
#include <Ethernet.h>
#include <OneWire.h>
#include <ve_avr.h> // Библиотека VEduino для программирования
//счетчиков - таймеров
unsigned int potValue; volatile int s=0; OneWire ds(6);
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,100); IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
EthernetServer server(80); // Создаем сервер, слушающий 80 порт
File myFile;
float temp() // Функция определения температуры с датчика DS18B20
{
byte i; byte data[10]; byte addr[8]; float celsius;
ds.search(addr); ds.reset(); ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
delay(1000); ds.reset(); ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();}
int raw = (data[1] << 8) | data[0];
unsigned char t_mask[4] = {0x7, 0x3, 0x1, 0x0};
byte cfg = (data[4] & 0x60) >> 5; raw &= ~t_mask[cfg];
celsius = (float)raw / 16; return celsius;
}
void setup() {
pinMode(2,INPUT); // Сигнализация
digitalWrite(2, HIGH); // Сигнализация
pinMode(5,OUTPUT); // Telephone
digitalWrite(5, LOW); // Telephone
pinMode(7,OUTPUT); // Reset
digitalWrite(7, LOW); // Reset
Serial.begin(9600);
SD.begin(4); // Инициализация SD модуля и выборка SD 4-м выводом
Serial.print("Free RAM: "); // Распечатка свободной памяти SRAM
Serial.println(FreeRam());
pinMode(10, OUTPUT);
// Установить SS вывод как выходящий
digitalWrite(10, HIGH);
// Выключить чип w5100
// Запускаем сервер. Шлюз выбран 192.168.1.1, маска 255.255.255.0
Ethernet.begin(mac, ip, gateway, subnet);
server.begin(); // Ожидаем соединение на 80-м порту
// Настройка прерывания от таймера - счетчика 1
potValue = 65535;
DEV_TIMER1.setClockSelect(TimerW::Prescaler_64);// Тактовая
//частота - 16МГц / 64 = 250 кГц. Таймер-счётчик 1 будет
// увеличивать значение регистра TCNT1 на единицу
// каждые 4 микросекунды.
DEV_TIMER1.setWaveGenMode(TimerW::FastPWM_OCRA);// Таймер//счётчик 1 будет сравнивать значение регистров
// TCNT1 и OCR1A и когда они будут равны,
DEV_TICTRL1.outCompIntEnableA();// будет вызываться функция//обработчик прерывания TIMER1_COM
interrupts(); // Включить прерывания.
}
// Выбираем размер буфера 100 символов, где находится имя файла.
#define BUF 100
void loop()
{
if(digitalRead(2)==LOW) //Проверяем, сработал ли концевой выключатель
// на 2-м выводе
{
delay(500); // Даем задержку
if(digitalRead(2)==LOW) // И еще раз проверяем срабатывание
//концевого выключателя
{ digitalWrite(5, HIGH); delay(100); digitalWrite(5, LOW);
delay(1000);digitalWrite(5, HIGH);delay(100);
digitalWrite(5, LOW); delay(60000); }
}
char clientline[BUF]; char *filename=0; int index = 0;
// В программе выполняется чтение только первой строки
//заголовка от браузера. После ее чтения
// выделяется имя файла для чтения его с SD карты памяти,
//считывается этот файл, пересылается
// браузеру и после этого соединение закрывается.
//Весь запрос не анализируется.
EthernetClient client = server.available();
if (client) { // Если client=1, то существует соединение с сервером
// reset the input buffer
index = 0;
while (client.connected())//Если есть несчитанные данные то client.connected()=1
{
if (client.available()) //В client.available() хранится количество полученных
// символов от клиента
{
char c = client.read();// Посимвольное чтение данных с клиента
// Сбросить соединение, если пришел непонятный символ от клиента.
// Например, наблюдались зависания от браузера
// Safary (IPad 2), который посылал "непонятные" символы
if ( c==0x0A || c==0x0D ) goto aa;
if ( c<0x20 || c>0x7E ) break;
aa:
// Если символ от клиента правильный, записываем его в буфер
// Если идет чтение не новой строки, то продолжаем ее символы
//записывать в буфер.
if (c != '\n' && c != '\r') {
clientline[index] = c;
index++;
// Идем на продолжение считывать новый символ.
continue;
}
// Заканчиваем строку символом 0, если следующая
// строка новая ( получили \n или \r )
clientline[index] = 0;
// Распечатываем прочитанную строку.
Serial.println(clientline);
// Если запрос http://192.168.1.100/temp распечатываем на браузере температуру
if (strstr(clientline, "GET /temp") != 0) {client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text"); client.println();
client.print("Temp="); client.print(temp());
// Выходим с цикла while
break;
}
// Если запрос http://192.168.1.100/tele то звоним
if (strstr(clientline, "GET /tele") != 0) {client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text"); client.println(); client.print("Ring...");
digitalWrite(5, HIGH); delay(100); digitalWrite(5, LOW);
delay(1000);digitalWrite(5, HIGH);delay(100); digitalWrite(5, LOW);
// Выходим с цикла while
break;
}
// Если первая строка запроса клиента подобна этой GET / HTTP/1.1 то
// имя файла для чтения с SD карты - index.htm
if (strstr(clientline, "GET / ") != 0) { filename = "index.htm"; }
if (strstr(clientline, "GET /") != 0) {
// Если пробела после "/" нет, то определяем имя файла
if (!filename) filename = clientline + 5; Serial.println(filename);
// Просматриваем строку после "GET /" (5 символов),
//присваиваем filename
// все символы строки clientline, которые идут за 5-й позицией.
// Ищем " HTTP" в полученной подстроке и там где пробел,
//ставим "0". Строка,
// записанная в filename отрезается (т.е. удаляется HTTP/1.1).
char *fil;
fil= strstr(filename, " HTTP");
fil[0]=0;
// Окончательно распечатываем имя файла, который
// считывается с SD карты памяти
Serial.println(filename);
// Открываем файл
myFile = SD.open(filename);
if (!myFile ) { client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html"); client.println();
client.println("<h2>File Not Found!</h2>");
break; }
// и передаем его содержимое браузеру вместе с заголовком,
//который формируется в зависимости
// от расширения файла
Serial.println("Opened!");
client.println("HTTP/1.1 200 OK");
if (strstr(filename, ".htm") != 0)
client.println("Content-Type: text/html; charset=Windows-1251");
else if (strstr(filename, ".jpg") != 0) client.println("Content-Type: image/jpeg");
else if (strstr(filename, ".pdf") != 0) client.println("Content-Type: application/pdf");
else
client.println("Content-Type: text");
client.println();
// Для ускорения чтения данных с SD карты чтение и передачу
// выполняем 64-байтными блоками
byte cB[64]; int cC=0;
while (myFile.available())
{
cB[cC]=myFile.read(); cC++;
if(cC > 63)
{ client.write(cB,64); cC=0; }
}
if(cC > 0) client.write(cB,cC);
myFile.close();
} else {
// everything else is a 404
client.println("HTTP/1.1 404 Not Found");
client.println("Content-Type: text/html");
client.println(); client.println("<h2>File Not Found!</h2>");
}
// Выходим из цикла while
break;
}
}
// Даем браузеру время для получения данных и закрываем соединение
delay(1); client.stop();
}
}
ISR(TIMER1_COMPA_vect)
// Функция обработки прерывания
{
DEV_TIMER1.setOutputCompareA(potValue); // Установить значение
// следующего прерывания от переменной Val
s++;
// После каждого прерывания увеличиваем s
if (s>500) { s=0; digitalWrite(7, HIGH);} // Примерно через 2 минуты
// идет обнуление Reset (с помощью релле)
// (аппаратный сброс)
}
Выводы.
1. Показана возможность на базе микроконтроллеров AVR построение
универсальных web – серверов, позволяющих не только отображать достаточно
большие объёмы информации, но и выполнять управление устройствами по
TCP/IP сети, что характерно для микроконтроллеров.
2. Использование платформы Arduino значительно упрощает и ускоряет
создание
подобных
достаточно
сложных
проектов
из-за
наличия
увеличивающегося набора библиотек и достаточно дешёвых плат Arduino с
минимальным набором электронных компонентов.
3. Обнаруженным существенным недостатком является нестабильная работа
представленного здесь web – сервера, который периодически “виснет” при
интенсивном к нему обращении. Анализ показал нестабильность работы
именно контроллера w5100 совместно с его набором библиотек.
4. Недостатком также является довольно медленная совместная работа по шине
SPI памяти SD и контроллера w5100. Несмотря на использовании в программе
блочного способа чтения данных с SD, скорость передачи данных по сети не
превышала 17.5Кбайт/с.
Литература.
1. Arduino. Официальный сайт.
[Electronic resource]. -
Mode of access:
http://arduino.cc/ , 2013.
2. Мясищев А.А. Web – сервер на Arduino UNO и Ethernet Shield W5100.
[Electronic resource]. - Mode of access: http://host56.no-ip.biz, 2013.
3. Библиотека VE_AVR. [Electronic resource]. - Mode of access:
https://sites.google.com/site/vanyambauseslinux/biblioteka-ve_avr, 2013.
Download