Подключение микроконтроллера к локальной сети: HTTP и CGI

В предыдущей части соотношение объёмов моего корявого быдлокода и интересной/полезной информации превысило всякие разумные пределы. Так что в этой я постараюсь исправиться)

Речь пойдёт о реализации простенького веб-сервера на базе TCP/IP стека, запиленного в предыдущей статье. Скорее, даже не сервера, а веб-интерфейса, который можно прикрутить к какому-нибудь сетевому девайсику на микроконтроллере.

Простой пример


Для начала, сделаем втупую)

// Принимаем все подключения
uint8_t tcp_listen(uint8_t id, eth_frame_t *frame)
{
    return 1;
}

// В ответ на любой пакет отправляем тестовую страничку 
//        и закрываем соединение
void tcp_data(uint8_t id, eth_frame_t *frame, uint16_t len, uint8_t closing)
{
    const prog_char *str = PSTR(
        "HTTP/1.0 200 OK\r\n"
        "Content-Type: text/plain; charset=windows-1251\r\n"
        "\r\n"
        "Превед из микроконтроллера!");

    ip_packet_t *ip = (void*)(frame->data);
    tcp_packet_t *tcp = (void*)(ip->data);
    uint16_t len_str;

    // Отправляем данные
    len_str = strlen_P(str);
    memcpy_P(tcp->data, str, len_str);
    tcp_send(id, frame, len_str, 1);
}

void tcp_opened(uint8_t id, eth_frame_t *frame) {}
void tcp_closed(uint8_t id, uint8_t reset) {}


Заходим браузером на, с позволения сказать, серевер и видим следующее:

Первый тест

Что радует, на отдачу странички уходят считанные милисекунды (смотрим в столбик Time):

Wireshark

Теперь рассмотрим как это работает поподробней.

HTTP


HTTP (Hypertext Transfer Protocol) — прикладной протокол, работающий поверх TCP, с помощью которого наш сервер и отдаёт странички браузеру. Протокол текстовый и достаточно простой. Возьмём сниффер и посмотрим как браузер обменивается данными с HTTP-сервером из предыдущего примера:

SmartSniff

Как запрос, так и ответ содержат заголовки (каждая строка — отдельное поле заголовка). Заканичвается заголовок пустой строкой. После заголовка могут идти данные.

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

GET / HTTP/1.1


  • GET — используемый метод. Данный метод используется для получения странички с веб-сервера в самом простом случае. Также применяются методы POST — для отправки данных на сервер и HEAD — для получения информации о страничке без скачивания самой странички. Минимально функциональный сервер должен поддерживать, по крайней мере, метод GET.
  • / — URL странички, которую запрашивает браузер.
  • HTTP/1.1 — тип и версия заголовка.

После этой строки идут дополнительные поля, содержащие различную информацию. Поля эти (кроме Host) необязательные. Для нас, в них, в принципе, нет ничего полезного. Разве что можно распарсить Host, если захотим сделать поддержку нескольких хостов.

Ответ нашего сервера начинается со строки:

HTTP/1.0 200 OK


  • HTTP/1.0 — тип и версия заголовка.
  • 200 — Код, обозначающий результат запроса. 200 — всё нормально, 403 — доступ запрещён, 404 — не найдена страничка по указанному URL, etc.
  • OK — текстовое описание статуса. Обычно нигде не используется и добавляется просто так.

Также в заголовке ответа мы указали поле Content-Type, содержащее MIME-тип отдаваемого документа и его кодировку. Оно нужно, чтобы браузер точно знал как отобразить полученную страничку.

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

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

Пример посложнее


Напишем простенький веб-интерфейс для управления светодиодом. Алгоритм его работы будет следующим:

  • При получении пакета по TCP, смотрим, является ли он GET-запросом. Если является, выковыриваем из него URL.
  • Разбираем URL на путь и параметры.
  • Проверяем путь. Пока у нас будет только главная страничка. Если путь не равен "/", возрвращаем страничку с ошибкой 404.
  • Проверяем параметры. Если в параметрах есть команда, выполняем её (включаем/выключаем светодиод).
  • Отдаём страничку, с текущим состоянием светодиода и ссылкой для включения/выключения.

// Страничка с ошибкой 404
prog_char webif_404_reply[] =
    "HTTP/1.0 404 Not Found\r\n"
    "Content-Type: text/html; charset=windows-1251\r\n"
    "Server: ATmega16\r\n"
    "\r\n"
    "<pre>Page not found\r\n\r\n"
    "<a href='/'>Home page</a></pre>\r\n";

// Заголовок странички
prog_char webif_200_header[] =
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html; charset=windows-1251\r\n"
    "Server: ATmega16\r\n"
    "\r\n";

// Здесь всё понятно
#define LED_DDR        DDRD
#define LED_PORT     PORTD
#define LED_BIT        (1<<PD0)
    
#define led_on()    { LED_PORT &= ~LED_BIT; led_state = 1; }
#define led_off()    { LED_PORT |= LED_BIT; led_state = 0; }

// Переменные
uint8_t led_state;        // Состояние светодиода
uint8_t lang_ru;        // Язык странички

// Функция для копирования данных из флешки в буфер
void fill_buf_p(char **buf, const prog_char *pstr)
{
    char c;
    while((c = pgm_read_byte(pstr)))
    {
        *((*buf)++) = c;
        pstr++;
    }
}

// Вызывается при старте
void webif_init()
{
    LED_DDR |= LED_BIT;
    led_off();
}

// Вызывается при получении данных по TCP
void webif_data(uint8_t id, eth_frame_t *frame, uint16_t len)
{
    ip_packet_t *ip = (void*)(frame->data);
    tcp_packet_t *tcp = (void*)(ip->data);
    char *req = (void*)tcp_get_data(tcp);
    char *buf = (void*)(tcp->data), *buf_ptr = buf;
    char *url, *p, *params, *name, *value;


    // Нет данных? - выходим
    if(!len) return;

    // Парсим запрос - проверяем что метод - GET и вытаскиваем URL
    if( (memcmp_P(req, PSTR("GET "), 4) == 0) &&
        ((p = strchr(req + 4, ' ')) != 0) )
    {
        url = req + 4;
        *p = 0;

        // Разбираем URL на путь и параметры
        if((params = strchr(url, '?')))
            *(params++) = 0;

        // Проверяем путь. Пока у нас есть 
        //        только одна главная страничка "/"
        if(strcmp_P(url, PSTR("/")) == 0)
        {
            
            // Разбираем параметры
            while(params)
            {
                // Смотрим где заканчивается параметр
                if((p = strchr(params, '&')))
                    *(p++) = 0;
                
                // Разбираем параметр на имя и значение
                name = params;
                if((value = strchr(name, '=')))
                    *(value++) = 0;
                
                // Параметр = led (on/off)?
                if( (strcmp_P(name, PSTR("led")) == 0 ) && value )
                {
                    if(strcmp_P(value, PSTR("on")) == 0)
                        led_on()
                    else if(strcmp_P(value, PSTR("off")) == 0)
                        led_off()
                }
                
                // Параметр = lang (en/ru)?
                else if( (strcmp_P(name, PSTR("lang")) == 0) && value )
                {
                    if(strcmp_P(value, PSTR("en")) == 0)
                        lang_ru = 0;
                    else if(strcmp_P(value, PSTR("ru")) == 0)
                        lang_ru = 1;
                }
                
                // Переходим к следующему параметру
                params = p;
            }

            // заливаем в буфер страничку
            fill_buf_p(&buf_ptr, webif_200_header);
            fill_buf_p(&buf_ptr, PSTR("<pre>"));

            if(!lang_ru)
            {
                fill_buf_p(&buf_ptr, PSTR("<p align='right'>[<b>EN</b> | "
                    "<a href='/?lang=ru'>RU</a>]</p>"));
            }
            else
            {
                fill_buf_p(&buf_ptr, PSTR("<p align='right'>[<a href='/?lang=en'>EN</a> | "
                    "<b>RU</b>]</p>"));
            }
            
            if((!led_state)&&(!lang_ru))
                fill_buf_p(&buf_ptr, PSTR("Led is OFF. Turn <a href='/?led=on'>on</a>."));
            else if(led_state &&(!lang_ru))
                fill_buf_p(&buf_ptr, PSTR("Led is ON. Turn <a href='/?led=off'>off</a>."));
            else if((!led_state)&&(lang_ru))
                fill_buf_p(&buf_ptr, PSTR("Светодиод выключен. <a href='/?led=on'>Включить</a>."));
            else if(led_state &&(lang_ru))
                fill_buf_p(&buf_ptr, PSTR("Светодиод включен. <a href='/?led=off'>Выключить</a>."));
            
            fill_buf_p(&buf_ptr, PSTR("</pre>"));
        }
        
        // Неправильный URL - пишем в буфер страничку с ошибкой 404
        else
        {
            fill_buf_p(&buf_ptr, webif_404_reply);
        }
    }
    
    // Отправляем данные из буфера
    tcp_send(id, frame, buf_ptr-buf, 1);
}

// Принимаем подключения на порт 80
uint8_t tcp_listen(uint8_t id, eth_frame_t *frame)
{
	ip_packet_t *ip = (void*)(frame->data);
	tcp_packet_t *tcp = (void*)(ip->data);

	if(tcp->to_port == htons(80))
		return 1;
	return 0;
}

// Передаём данные "серверу"
void tcp_data(uint8_t id, eth_frame_t *frame, uint16_t len, uint8_t closing)
{
	webif_data(id, frame, len);
}


Заходим на девайс браузером и видим примерно вот такую страничку:

Второй тест

Страничка наша сделана максимально компактной, чтобы точно поместиться в один пакет. Как видим, ответ сервера занял всего 200 байт.

Wireshark

Можно теперь немного поиграться:



Формы


HTML позволяет создавать формы для отправки различных данных на сервер. Главное не забывать о том, что вся страничка должна поместиться в 1 пакет. Такое уж ограничение у сервера на микроконтроллере)

Так что, если нужно сделать много настроек, стоит разбить веб-интерфейс на несколько вкладок.

Вот простейшая форма, позволяющая юзеру ввести один байт:

<form action='/' method='GET'>
Enter value (0..255):
    <input type='text' name='pwm' size='4' value='33'>
    <input type='submit' value='OK'>
</form>


В тэге form указывается адрес и способ отправки данных на сервер. В данном случае, это метод GET — поля формы будут отправлены в виде параметров в URL.

В браузере эта форма будет выглядеть примерно вот так:

Пример формы

При нажатии ОК, на сервер будет отправлен следующий запрос:

GET /?pwm=33 HTTP/1.1


С помощью такой формы мы можем, например, регулировать яркость светодиода

// Инициализация
void webif_init()
{
    // Включаем ШИМ
    DDRB |= (1<<PB2)|(1<<PB3);
    TCCR0 = (1<<WGM01)|(1<<WGM00)|(1<<COM01)|(1<<COM00)|(1<<CS02);
}

// Обработка запросов браузера
void webif_data(uint8_t id, eth_frame_t *frame, uint16_t len)
{
    ip_packet_t *ip = (void*)(frame->data);
    tcp_packet_t *tcp = (void*)(ip->data);
    char *req = (void*)tcp_get_data(tcp);
    char *buf = (void*)(tcp->data), *buf_ptr = buf;
    char *url, *p, *params, *name, *value;
    char str[8];
    int val;

    if(!len) return;

    if( (memcmp_P(req, PSTR("GET "), 4) == 0) &&
        ((p = strchr(req + 4, ' ')) != 0) )
    {
        url = req + 4;
        *p = 0;

        if((params = strchr(url, '?')))
            *(params++) = 0;

        // Браузер запросил главную страничку?
        if(strcmp_P(url, PSTR("/")) == 0)
        {
            // Разбираем параметры
            while(params)
            {
                if((p = strchr(params, '&')))
                    *(p++) = 0;
                
                name = params;
                if((value = strchr(name, '=')))
                    *(value++) = 0;
                
                // Параметр pwm
                if( (strcmp_P(name, PSTR("pwm")) == 0 ) && value )
                {
                    // Парсим значение
                    val = atoi(value);
                    if(val < 0) val = 0;
                    if(val > 255) val = 255;
                    led_brightness = val;
                    
                    // Устанавливаем яркость
                    OCR0 = led_brightness;
                }
                
                params = p;
            }

            itoa(led_brightness, str, 10);

            // Отправляем главную страничку
            fill_buf_p(&buf_ptr, webif_200_header);
            fill_buf_p(&buf_ptr, PSTR("<pre>"));
            fill_buf_p(&buf_ptr, PSTR("Brightness: "));
            fill_buf(&buf_ptr, str);
            fill_buf_p(&buf_ptr, PSTR(". <a href='/edit'>Change</a>"));
            fill_buf_p(&buf_ptr, PSTR("</pre>"));
        }

        // Браузер запросиил страничку /edit?
        else if(strcmp_P(url, PSTR("/edit")) == 0)
        {
            itoa(led_brightness, str, 10);

            // Отправляем форму
            fill_buf_p(&buf_ptr, webif_200_header);
            fill_buf_p(&buf_ptr, PSTR("<pre>"));
            fill_buf_p(&buf_ptr, PSTR("<form action='/' method='GET'>\r\n"));
            fill_buf_p(&buf_ptr, PSTR("Enter value (0..255):\r\n"));
            fill_buf_p(&buf_ptr, PSTR("<input type='text' name='pwm' size='4' value='"));
                fill_buf(&buf_ptr, str);
                    fill_buf_p(&buf_ptr, PSTR("'>  "));
            fill_buf_p(&buf_ptr, PSTR("<input type='submit' value='OK'>\r\n"));
            fill_buf_p(&buf_ptr, PSTR("</form>"));
            fill_buf_p(&buf_ptr, PSTR("</pre>"));
        }

        // Браузер запросил другую страничку, говорим 404
        else
        {
            fill_buf_p(&buf_ptr, webif_404_reply);
        }
    }

    tcp_send(id, frame, buf_ptr-buf, 1);
}


Работает)



Заключение


В этой части мы рассмотрели основы работы с протоколом HTTP и научились делать простенькие веб-интерфейсы для девайсиков.

Проекты можно взять здесь: первый, второй.

В следующей подчасти мы продолжим играться с HTTP.

To be continued…



Все статьи цикла

Комментарии (143)

RSS свернуть / развернуть
Скорей бы микруха чтоль приехала… руки чешуца уже :-)
Давно ковыряешься с ней?
0
Да я практически параллельно пишу и ковыряюсь)
0
Как всегда, отлично, просто, понятно написано!
enc28j60 как раз купил «до кучи» за пару дней до начала цикла статей, но внезапно, как всегда, по всем фронтам навалилась работа разная и неотложная.
Со следующей недели думаю начну, одним вечером здесь явно не обойдется)
0
упустил — какие требования к микроконтроллеру (память? что еще?)
и еще где-то бы увидеть список того, с чем быть поаккуратнее — какие ресурсы микроконтроллера можно использовать, а какие нельзя, так как может сказаться на работе стека…
0
Зависит от конфигурации. В максимальной конфигурации на данный момент — примерно 8 КБ флешки и 610 байт памяти. Память в основном, под буфер пакетов уходит.
Из периферии — SPI и таймер. Которые, впрочем, никто не запрещает юзать и для других целей)
0
Кстати да. И сколько памяти занял вебсервер.
0
Афигенно! Так держать :))) А с W5100 не работал?
0
Неа, не работал
0
Для своих девайсов по работе использовал мост Ethernet-COM на ней — греетЦо после 5 минут не очень интенсивного обмена.
0
У меня на работе через них данные из серверных собираются:) Приятная штука в отличии от 5300.
0
У W5100 есть один нерешаемый баг в соединении по UDP. Поэтому от использования его у меня осталось плохое послевкусие… Если кому интересно подробности, можно почитать его ERRATA
0
осталось php и mysql портировать :)
0
Ну зачем mysql, есть сугубо эмбедерская SQLite
+1
это все сарказм?
если нет, то где хранить данные? каков максимальный объем? как быстро делается выборка по шестиразрядному числу по табличке, скажем, на полсотни тысяч записей?
0
Не совсем. Естественно на 8-и битном камне завести сам механизм не получится, а вот обратится к базе находящейся на нормальном сервере возможно. Видел примеры для Arduino.
Так-же есть реализации для 32-х битныч RT OS (ARM), вот там-то и есть возможность реализации самого движка базы.
0
Не вижу большого смысла в помещения «сервера» БД на контроллер. На контроллерах в основном определенный формат данных и произвольные выборки делать не нужно.
А вот клиента для БД это было бы неплохо.
0
HTTP вообще не очень-то AVR'овская штучка. Слишком большой и требовательный стек. Да и цена намекает, что для сетки стоит присмотреться к ARM'ам с встроенным MAC/PHY. Вот там уже есть ресурсы для нормального веб-сервера. Здесь же сервак отбирает большую часть ресурсов далеко не младшего камня.
0
Не сарказм, просто шутка.
Но ведь в каждой шутке лишь доля шутки, верно?

Данные хранить на SD карточке, например.
0
Как вариант, но только для подобных целей на самом деле ARM лучше.
0
Мда))) крутой чувак! полезный материал!
0
А почему нужно умещать весь запрос в один пакет?
0
Потому что у МК памяти не хватит на буферизацию данных, etc.
0
кстати, я ничего не путаю, на типовых роутерах размер пакет обычно устанавливают на 1300-1500 байт?
0
Спасибо за статьи, автор молодец — это здорово, когда есть желание делиться знаниями)
0
ух. скачал архив — а как это скомпилировать в .hex файл без avr studio? у меня вот avr-gcc установлен и текстовый редактор, и все…
найти бы где пошаговую инструкцию…
0
Жги еще паря жги
уважаю чел
0
Ткните носом, ибо я не нашел в этой статье CGI…
0
И не найдешь, как таковой CGI интерфейс для АВР-ов вообще смысла не имеет. Надо полагать, под CGI подразумевалась генерация странички программой и управление устройством через параметры вида ?led=on.
0
Да, вроде того ))
Настоящий CGI требует настоящую ось, ес-нно)
0
не кто не поможет, а как для данного устройства к каким выводам подключается ENC28J60 к микроконтроллеру Atmega16
особенно int и wol, их в общем не понял куда подключить
желательно номера ножек сказать или название выводов
заранее спасибо, а то уже всю голову сломал =)
0
Можно их не подключать если будишь использовать данные исходники так как в них не используется прерывание(int) и пробуждение по сети(Wake on Lan-wol)
а остальные подключаешь по стандартному SPI.
0
РАБОТУ СДЕЛАЛ ГЛОБАЛЬНУЮ НО TCP НЕ РАБОТАЕТ НИ В ОДНОМ ПРИМЕРЕ А UDP ЗАВЕЛСЯ СРАЗУ.
ГДЕ-ТО В ИСХОДНИКАХ ОШИБКА ИЛИ СТЕК TCP РЕАЛИЗОВАН Не ПРАВИЛЬНО НА ВЫХОДНЫХ БУДУ КОВЫРЯТЬ ЕСЛИ МОЖНО ПЕРЕСМОТРИ СВОИ ИСХОДНИКИ И ВЫЛОЖИ ПРАВИЛЬНЫЕ.
А ТАК СПАСИБО ЗА МАТЕРЬЯЛЬЧИК.:)
-4
нихера не понял
0
ТОБИШ ПО РУССКИ.
UDP Сервер и UDP клиент заработали сразу после прошивки МК.
А все примеры с HTTP и SMTP нет.
ПОЧЕМУ ?????????????????????????????
В ЧЕМ МОЖЕТ БЫТЬ ПРОБЛЕМА ??????????????(Так понятней ???)
-3
Ди придёт и грохнет эти комменты.
Читать невозможно.
0
РАСПАКОВАЛ webif.ОТКОМПИЛИЛ ТВОЙ КОД.ПОЛУЧИЛ HEX. ЗАЛИЛ В ATMEGA16.ПОДАЛ ПИТАНИЕ НАБРАЛ АДРЕС И УВИДЕЛ ЧТО СТРАНИЦА НЕ НАЙДЕНА.
ТОЖЕ САМОЕ ДЕЛАЛ И С UDP НО ВСЕ ЗАРАБОТАЛО С ПОЛ ПИНКА.
-3
Пример с webif юзает DHCP. Возможно, с DHCP и связаны какие-то проблемы. Попробуй отключить DHCP, задать вручную айпи маску и гейт (в lan.h) и собрать поновой. Отпишись о результатах.
0
СПАСИБО ОГРОМНОЕ Я ПОПРОБУЮ.
А ВОТ ЕЩЁ ВОПРОС ATMEGA ДОЛЖЕН ОТВЕЧАТЬ НА ПИНГИ (ping 192.168.0.222) ИЛИ НЕТ.
-1
Этот пример юзает DHCP. Девайс должен отвечать на пинги на тот адрес, который выдан по DHCP или на широковещательный адрес подсети (e.g. 192.168.0.255)
0
ПОЧЕМУ ТО НЕ ОТВЕЧАЕТ.
-2
Попробуй без DHCP.
0
ОК ЗАВТРА ОТПИШУСЬ.(СПАСИБО ОГРОМАДНОЕ).
-2
раскрыть комментарий
-7
он пьяный.
-1
НЕТ НЕ ПЬЯНЫЙ.ПРОСТО УЖЕ 3-й день без сна.
-2
отключи CAPS LOCK и сожри снотворного, иначе через пару суток МНУ начнет отказывать, а там не далеко до принятия ислама.
+1
Я И ТАК МУСУЛЬМАНИН.
-3
плюсанул
+1
капс отключи, школота
+4
ааа, я под столом.
0
Куда смотрит модератор ???
Коменты специально пишут для обсуждения темы и интересующих вопросов.
А вы товарищи как бабки устроили тут базар.
Да ещё и не по теме.
В топку вас.
-2
а вы будете фтп клиент описывать?
0
Неа, эта серия уже всё)
0
Добрый вечер. У меня возникла проблема при сборе устройства: схема железки… слил в МК прошивку, за основу взята прошивка из этого поста (из доработки- включил в состав исходников несколько функций по работе с Uart для отладки), реакции = 0, есть вариант что неподходит трансфарматор (у меня LP164C)… хотел проверить связь МК по SPI с ENC28j60, запросив ревизию контроллера.
Попытался сделать запрос в модуле main.с, после инициализации, используя функцию uint8_t enc28j60_rcr(uint8_t adr) из модуля enc28j60.c. EREVID по дефайну заменяется на (0x12 | 0x60).



unsigned char* 	strmes_9=enc28j60_rcr(EREVID);
_delay_ms(1000);
for(int i=0;i<8;i++)
USART_SendChar(strmes_9[i]);


Uart настроен верно (статистика иницализации приходит в корректном виде), но вот на запрос ревизии получаю чушь, подскажите как быть?
0
img543.imageshack.us/img543/897/40003114.jpg схема устройства
0
Начнем с того, что, согласно схеме, ты на выходной каскад питание забыл подать (на схеме из даташита оно подается через дроссель).
0
Дросель поставил(просто читал в других похожих разработках что работало без него вот и не стал ставить), увеличиласть температура стабилизатора только… остальное неижменно(светики моргают 1 раз при включении питания и гаснут...)
0
Без него работает только если дроссель закоротить. Середина первички трансформатора TX к +3.3В подключена должна быть обязательно. Нинай как у тебя на плате, но на схеме подача питания туда не указана. Конденсаторы от середин первичек на землю должны быть 0.1, а не 0.01. Середины вторичек кстати тоже должны быть подключены к согласователям. Схема с даташите есть.
Далее, параллельно светодиодам енки нужно включить резисторы по 10к.
Ну и последнее — RESET енки надо подтягивать к ее же питанию, а не +12В, а само питание должно быть 3.3В, а не 5.1В, как у тебя на схеме. Это уже чревато дымом.
Если все собрано правильно, то енка даже без МК будет поднимать линк при подключении кабеля и показывать это зажиганием соответствующего светодиода (ну и роутер/сетевуха, куда подключен второй конец кабеля тоже покажет наличие линка).
0
как по SPI получить номер ревизии (если есть исходник можно?) полазил по нету не нашел чтото подобного…
0
как то так
ENC_REV=enc28j60_rcr(EREVID);
0
У меня проблемка, из-за большого пакета у мк очень мало памяти, решил уменьшить размер до 600 (было 1300), но теперь страница не вмещается в один пакет, прочитал следующую статью и решил отправлять страницу в несколько пакетов, закрывая соединение только после отправки последнего. В webif_data нашел функцию tcp_send(id, frame, buf_ptr-buf, 1); если 1, то соединение закрывается, если 0 — не закрывается. Я сначала отправил один кусок страницы, вызвав tcp_send(id, frame, buf_ptr-buf, 0) (не закрыв соединение), далее, второй кусок с помощью tcp_send(id, frame, buf_ptr-buf, 1). Но в браузере отображается полностью первый пакет и часть второго (не с начала), если не ошибаюсь, то во втором пакете не передалось количество байт, равное размеру первого пакета. Получается, что есть какой то счетчик. Но как его обнулить?
0
Хай Всем!
Может кто подскажет по данной теме — разбираюсь понемногу с enc (платка с ebay, аля www.ebay.com/itm/ENC28J60-Network-Module-Schematic-For-51-STM32-LPC-AVR-/260875093296?pt=BI_Electrical_Equipment_Tools&hash=item3cbd5e0530 на которой кст светодиоды подключены Анодами к контроллеру, но не думаю что изза этого будут трудности… и считав с нее ревизию, = 0х06 заменил резистор на плате 2,7k на 2,32k… )
Собственно залил первый пример из этой статьи — нормально все заработало, бук подключен к роутеру d-link 615 и к нему же подключена платка, IP раздает роутер. далее решил попробовать послежнее пример — скомпилил и прошил из второго архива пример и… ничего, девайс не отвечает — такое чувство что enc не инициализирован, светодиодами не моргает вобще.
прошиваю обратно на 1й пример — работает…
МК — Atmega32, кварц 16Мгц
Компилю НЕ из AvrStudio а батник + мэйк файл, стоит winavr но не уверен чт в этом проблема…
Есть ли у Вас идеи почему не работает?
0
Лично у меня подобные проблемы (ну, за исключением косяков в коде) возникали по двум причинам:
1) Пин SS атмеги. В SPI Master режиме его нужно поставить на выход либо куда-то подтянуть (не помню куда), иначе при определенном логическом уровне на нем SPI отключается (подробности — в даташите). Лучше всего — задействовать его для управления ножкой CS енки (однако, у Lifelover'а в примерах это не так, поскольку на пине SS висит CS флеш-карты).
2) Питание. Енка жрет много и питания от, например, компового USB ей не хватает — она сбрасывается или виснет, со всеми вытекающими.
0
странно что на аналогичном примере — работает, а на другом нет )
они то отличаются только файлом web_if.c и все…
хех. завтр попробую Ваши советы еще, сегодня спать уже клонит =__=
0
и, заменил питание — поставил кренку другую LM1086 ADJ, припаял тантал дополнительно к платке с ENC, провода чуть толще по питанию бросил — результат тот же…
и вот сейчас осенило, прочитав FAQ в заключении, а именно строка "… прально укзать в дефайнах все пины и порты...", попробовал в примере закоментить строку
TCCR0 = (1<<WGM01)|(1<<WGM00)|(1<<COM01)|(1<<COM00)|(1<<CS02);

функция void webif_init() и заработало! нет, конечно яркостью светодиода не поиграться, но ENC инициализируется, светодиодами начинает радостно моргать чт сеть мол есть и по IP я получаю страничку с девайса…

такие вот дела…
0
понял теперь почему не работало… )
банально просто. после «тотального обновления» всех вложений автор забыл что PB3 используется для CS и этот же пин для Т0 ШИМ и конечно же после инициализации ШИМа все ложится… надо просто в enc28j60.h перекинуть к примеру на соседний PB4 этот CS и все норм работает )
Спасибо огромное Lifelover'у за оч наглядные примеры работы с enc!!! ^___~
0
Енка жрет много и питания от, например, компового USB ей не хватает
?
У меня нормально от одного порта и отладчик и мк и енка питаются. потребление енки 150мА вроде всего. Это конечно превышает лимит в 100мА без устройства, но тот же программатор может питать схему и тогда лимит в 500мА будет.
0
Зато у меня дико глючило и не отзывалось, пока на внешнее питание не перевел. Экспериментировал на PinBoard 1.1. И жрет оно 200-250мА. В лимит USB укладывается, но напряжение проседает из-за работы комповой защиты и не лучшего кабеля. По спецификации напряжение при 500мА может просесть примерно до 4.2В, минус напруга на диоде — в результате у меня получалось около 3.5В на МК и столько же на входе стабилизатора енки, а на выходе — менее требуемых 3.3В.
0
Может кто-то конвертировать первый пример под студию 6? А то уже 2 дня мучаюсь. А результату 0! Помогите пожалуйста!
0
А от какого генератора работает tinny, от enc28j60 или внутреннего? И если Вас не затруднит можно схему. Заранее благодарен.
0
Добрый день, хорошая работа. А не могли бы Вы выложить схему устройства и описать фьюзы микроконтроллера.
0
Тупой вопрос, а в чем проект скомпелировать?
среда и версия?
0
разобрался под AVR studio 4 пошло
0
:) а что можно подрезать чтоб в атмегу8 всунуть код?
нужно только выдавать в браузер состояния ног замкнуты разомкнуты.
0
Читай «Подключение микроконтроллера к локальной сети: тесты производительности и краткое описание API стека» и «Веб сервер на Tiny2313. Чисто ради лулзов». Для браузера нужен вариант «TCP» из таблички типовых конфигураций, плюс поверх него HTTP.
При достаточном количестве знаний можно пойти по пути ассемблера и выкидывания абсолютно всего ненужного — это вторая из указанных статей. В принципе, проект из нее можно взять как есть, но тогда МК придется ставить ATTINY2313. Ну или попытаться портировать его на атмегу.
0
про тиньку видел — ассемблер, я в Си не ахти (не программист я)
есть проект на атмегу8
www.pocketmagic.net/2012/07/atmega8-and-enc28j60-for-ethernet-support/comment-page-2/
но там нет маршрута по умолчанию — что для меня критично, атор говорит что всё есть в его втором проекте, но исходников я там не нашел (мож слепой), в комментах автор молчит.
0
Удалось урезать код. Убрав все что связано с DHCP.
Но все бы хорошо, если бы через какое то время (20-40 мин) начинается увеличение пинга, потом дропуются пакеты и в итоге теряем доступ по сети.
Чип enc28j60 довольно сильно греется.
вопрос куда копать?
0
Не было таких проблем. Чего-то ты наубирал лишнего. Проверяй.
Насколько помню можно просто дефайном выключать различные части кода — ничего не удаляя…
0
Нагрев — это нормально. Палец должен терпеть…
0
Палец не подержишь — жжот.
вобщем то я перенес в 5.1 студию и там использовал нативный тулчайн(компилятор) — пробывал игратся с оптимизацией — подкидывал от зарядки USB питание — виснит сам камень через какоето время — т.е. прогромматор камень не видит.

НО подкинул WinAVR — скомпилил — 6 часов полёт нормальный!!!
Понимаю что за компилятора может такое быть, но этот код пихали и в STM и всё вроде ок.

Попробую болгара(или румына) код погонять т.к. у него под 5-ю студию с родным вроде как компилятором — сравнить код.
0
В Студии не работаю, не прокомментирую. Под WinAVR все нормально пашет… я когда прикручивал DNS Response — были кое-какие сложности, но решаемые.
0
не долго музыка играла.
сдох через 10 часов :)
еслиб я что-то подрезал лишние то вообще думаю или не скомпилило либо не работало.
а есть какая разница на какие ноги-пины содить модуль?
сейча так
#define ENC28J60_SPI_DDR	DDRB
#define ENC28J60_SPI_PORT	PORTB
#define ENC28J60_SPI_CS		(1<<PB2)
#define ENC28J60_SPI_MOSI	(1<<PB3)
#define ENC28J60_SPI_MISO	(1<<PB4)
#define ENC28J60_SPI_SCK	(1<<PB5)
0
Пины должны соответствовать. Выводи дебажную инфу куда-нить и смотри причину зависания. Может камень мертвый, может холодильник «удачно» включается. Причин очень много может быть.
0
Соответствовать подключению имелось ввиду.
0
Походу камень я зажарил — нужно тренироватся поять феном qfp корпуса :)
собрал на макетке в ДИП корпусе — пока полёт нормальный.
0
да блин издевательство какоето — сдох всётаки.
есть мега16 — соберу на ней, попробую код без изменений
0
так до меги16 не добрался
попробывал из послендней главы взять стек easyelectronics.ru/files/WE/redsh/ctrl.zip
и вкинуть его впроект web но получил ошибку
undefined reference to `tcp_listen'(`tcp_read',`tcp_write' и т.д. ) в файле lan.c
я что-то не пойму почему не обьявлены? кода они есть в заголовке?
0
Тут на самом деле с релизами кода есть несколько проблем — с каждой статьей ребята что-то меняли, исправляли и прочее… по-этому смотри что, как и где. Может где-то что пропущено и дефайн функции оказался в другом месте.

Не понимаю в чем проблема запаять QFP обычным паяльником — перегреть куда сложнее чем феном.
Напиши в личку почту — там пообщаемся.
0
Да, и выложил бы схему, фото — чего и как там у тебя подключено, подробности (чем питается, как качество питания) — подробностей короче больше… может зависание не из-за стэка…
0
undefined reference to `tcp_listen'(`tcp_read',`tcp_write' и т.д. ) в файле lan.c
я что-то не пойму почему не обьявлены? кода они есть в заголовке?
А ты их реализовал ли? На undefined reference жалуется линкер, это значит, что он не нашел реализацию функции. А написать реализацию ты должен сам, это коллбеки.
0
не много приболел был :) и не до хобби было
взял пример из заключения easyelectronics.ru/files/WE/redsh/ctrl.zip
симптомы теже
По схеме на прямую подключен программатор и модуль ENC28J60. пробывал на макетке и готовой плате с atmega8a, ENC28J60 — тоже модулем готовым (есть 2шт. пробывал оба в разных вариациях симптомы теже) питание и с компа по USB и от зарядки и сейчас от ип-тв приставки 5 вольтовой БП.
и
отсоединяю программатор usbasp — контролер теряет модуль — т.е. ему чегото не хватает на линиях SPI (MOSI, MISO, CSK)
В общем проблема в модуле скорее всего, пробывал ресет подтягивать на питание без разницы.
0
Покажи схему. Точную.
0
aa
вот схема — стандартная — пины соответственно сопостовимы с разьёмами.
0
А что за модуль ты используешь?
0
0
фьюзы
Фьюзы — кварц на 16Мгц
0
Документация на него нормальная есть?
0
«китайцы — они такие китайцы» — откуда?
0
Стабилизатор на 3.3В там хотя бы есть встроенный?
0
типа какойто кренки трёх ногой стоит.
вообще понизил до 3.3 вольт — и подал на ноги каторы помечены 3 вольта
стал стабильней но всеровно отваливатся модуль
0
Ну хз. Основные причины проблем, с какими я сталкивался — недостаточность питания и незадействование SS на МК (в режиме SPI-master эта ножка глушит SPI-модуль при определенном уровне, поэтому надо или задействовать ее как выход (скажем, подключить туда CS енки), или подтянуть снаружи к нужному уровню). Ну и микросхема, по отзывам, глючновата. Возможно это играет роль.
0
Управление ножкой CLKOUT

В ENC28J60 можно брать тактовый сигнал (с делителем) с ножки CLKOUT. Из-за бага, сигнал может пропадать при входе в режим пониженного энергопотребления.

ecocon

Биты ECOCON2:0 устанавливают делитель:

000 — ножка CLKOUT подтянута к земле.
001 — делитель на 1 (25 МГц).
010 — делитель на 2 (12,5 МГц).
011 — делитель на 3 (8,333333 МГц).
100 — делитель на 4 (6,25 МГц).
101 — делитель на 8 (3,125 МГц)
в коде
// ECOCON
#define ECOCON_COCON2 0x04
#define ECOCON_COCON1 0x02
#define ECOCON_COCON0 0x01

Почему 04,02,01 почему не 0,0,1?
Обьясните «на пальцах» почему так? Плизз.
пока в регистрах дуб.
0
Насколько я понимаю, это битовые маски. Так, чтобы выключить CLKOUT — нужно записать 0 в ECOCON, а чтобы включить на 8.33МГц — нужно записать туда же ECOCON_COCON0|ECOCON_COCON1.
0
#define led_on()	{ LED_PORT &= ~LED_BIT; led_state = 1; }
#define led_off()	{ LED_PORT |= LED_BIT; led_state = 0; }

Получается задом на перед? led_on() гасит светодиод, led_off() зажигает…
На страничке пишется Led is OFF.Turn on. Хотя при запуске он горит, т.к. в webif_init()

LED_DDR |= LED_BIT;
      led_off();

То-то у меня при включении сетится светодиод
0
  • avatar
  • Flint
  • 07 сентября 2014, 19:44
Зачем писать одно и то же в двух топиках? Ответил в другом.
0
  • avatar
  • Vga
  • 08 сентября 2014, 00:30
Тут в файле lan.c в фильтре пакетов при case TCP_ESTABLISHED: есть небольшая тонкость, при приеме подтверждающего нулевой длины аска будет обращение к функции tcp_data причем никаких данных из-за того что len= 0 не отправится, (зарежет tcp_send т.к. len=0) но обращение к функции, со всеми вытекающими будет т.е. надо просто скобку перенести за tcp_data и при приеме подтверждающего нулевого аска реакции не будет​, а при получении нормального пакета будет отправлен нулевой длины аск и потом то, что отправит tcp_data. Автору спасибо за труд — Мега респект!!!
0
Приветствую. Пытаюсь адаптировать первый пример под stm32. Page not found Home page
Возможно это из-за то, что я заменил strcmp_P на strcmp и memcmp_P на memcmp. У меня их компилятор не хотел принимать. Да и PSTR(s) задал так PSTR(s) ((const char *)(s)). В этом дело? И на что эти недостающие элементы поменять? Я так понимаю они чисто от AVR
Заранее спасибо
0
Здравствуйте.
Автору от все души СПАСИБО!!!
ипммммммммммммммммммммммммммммммммммммммммммс
0
Пардон, кот на клавиатуру прыгнул…
Сейчас настройка компа IP 192.168.0.100 маска 255.255.255.0. сервер не пингуется. Соединение на Модуль подключен прямо к компу.
Я понимаю, что мне в песочницу, но подскажите, как настроить комп чтобы работать с этой прошивкой?
0
Сам спросил, сам отвечу. Может кому пригодится.
В файле lan.h надо закоментить строчку //#define WITH_DHCP в разделе * Options, тогда девайсу будет назначен адрес прописанный вручную.
0
Ошибка детектед.
Массивчик str имеет размер 8.
А itoa хочет загнать в него 10 элементов. Итого глюки.
Внимательней!
0
Упс!
10 в itoa это не количество цифр, а система счисления.
Простите(
0
Не могу найти content-length. Или нет такого?
0
  • avatar
  • x893
  • 20 сентября 2016, 16:36
Content-Length — опциональный заголовок (вообще, сложно найти в HTTP хоть что-то неопциональное), там есть с десяток способов определить размер данных (что сильно действует на нервы, когда хочешь набросать простенький серверок и обнаруживаешь, что все эти варианты надо реализовать). Здесь, полагаю, просто соединение закрывается по завершении данных, это один из корректных вариантов.
0
  • avatar
  • Vga
  • 21 сентября 2016, 10:20
Нашел ошибку в файле lan.h, входящем в примеры для данной статьи, приводящую к отказу TCP через сутки работы.
Подробнее в соседней теме.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.