W5500+STM32+SD web сервер


Как и планировалось мультипоточный (а точнее 8-ми) http сервер в первом приближении, глюки, баги есть :), но работает и вроде не так страшен оказался.

Написано по мотивам сервака Lifelover и использованы некоторые его части. В качестве камушка дисковери STM32F100RBT. Подключение: W5500 — SPI1, SD- SPI2. Светодиодики — синий PC8 данные SD карточки, зеленый PC9 — успешная инициализация SD карты. При обращении по адресу сервера по умолчанию обращается к файлу index.htm. При работе используются все 8 сокетов W5500, за счет этого и обеспечивается многопоточность. Количество переменных состояний по количеству сокетов:

#define HTTP_IDLE 0
#define HTTP_SENDING 1

uint32_t sentsize[_WIZCHIP_SOCK_NUM_];
uint8_t http_state[_WIZCHIP_SOCK_NUM_];
FIL fs[_WIZCHIP_SOCK_NUM_];

Весь код приводить не буду, проект приложен, поясню идею. Самое приятное, что за соединением следит W5500 и мы обрабатываем только пользовательские данные. К номеру сокета привязаны переменные с тем же адресом в массиве. При получении GET, http_state[socket_number] в состянии HTTP_IDLE пытаемся открыть файл и начать передавать его если удалось открыть то HTTP_SENDING и отправляем первую порцию (указатель на позицию в файле запоминается сам), выходим в диспетчер — пока передается то что мы закачали по SPI в буфер W5500. При следующем входе в этот сокет он в состоянии HTTP_SENDING и просто передаем следующую часть и по кругу. Как только передадим, то опять HTTP_IDLE и обнуляем sentsize[socket_number] закрываем соединение и в диспетчер. Если случилось какое то другое состояние кроме SOCK_ESTABLISHED или получил новый GET в тот же сокет — то тоже все обнуляем (GET не прилетит пока сокет не отсоединился).
Про скорость. В равных условиях примерно, что и ENC28j60, максимум, что видел ~130-140кб/сек (т.к. это еще и от скорости работы с картой сильно зависит поэтому может и некорректно сравнение ибо SPI у W5500 до 80MHz, а здесь он на 12MHz).

Про программирование W5500 — по сравнению с ENC28j60 это конечно земля и небо, все просто и приятно (хотя я пока глубоко не копал). Размер кода на W5500 примерно раза в 2 меньше получается чем на ENC28j60.
Нагрев — W5500 ниже градусов на 10 чем ENC28j60.
Один из явных минусов на мой взгляд то, что для каких то серьезных проектов mac адреса надо покупать отдельно…

зы да и про карточки- не все могут работать.
pps 05.09.14 обновил проект поправил несколько дурацких ошибок
  • +2
  • 03 сентября 2014, 17:28
  • GYUR22
  • 1
Файлы в топике: web_discovery+w5500_sd.zip

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

RSS свернуть / развернуть
Здравствуйте. Пытаюсь повторить ваш проект только с хранением сайта не на SD, а во FLASH памяти МК. Никак не могу передать большой объем информации. Например картинку размером 5 КБ.

Вы передаете сайт блоками:

if(fs[sn].fsize != sentsize[sn])
{
  f_read(&fs[sn], &buf[header_sz], DATA_BUF_SIZE-header_sz, &blocklen);
  ret = send(sn,buf,blocklen+header_sz);

  if(ret < 0)
  {
    f_close(&fs[sn]);
    close(sn);
    return ret;
  }

  sentsize[sn] += ret; // Don't care SOCKERR_BUSY, because it is zero.
}

Но при каждой итерации в передающем буфере оказывается HTTP заголовок + блок данных. Верно? Т.е. передавая блоками, надо перед каждым блоком указывать HTTP заголовок? &buf[header_sz] указывает на конец заголовка и туда же копируются прочитанные данные с SD. Просто я пробовал передавать заголовок, а потом блоки данных — не работает.
0
честно ничего не понял…
0
Ну смотрите: вы читаете (DATA_BUF_SIZE-header_sz) байт с карточки в буфер по указателю &buf[header_sz]. А до этого указателя что хранится в буфере? Каждый раз пока выполняется (fs[sn].fsize != sentsize[sn]).
0
не ну а не реально посмотреть код то самому там одна функция?
header_sz =по дефолту там ноль
1.если http_state[sn] был в состоянии IDLE то сначала туда пишется заголовок сколькото байт и состояние переходит в состоянии SENDING и дописывает данные.
2. если же сразу http_state[sn] в состянии SENDING то заголовок ноль и пишем туда чисто данные
Вопросы есть?
0
header_sz назначается в состоянии IDLE. В каком месте в состоянии SENDING заголовок становится нулем?
0
header_sz по умолчанию 0 и если мы уже шлем чтото то в IDLE не попадаем и он остается 0.
0
Всё. Догнал. Я в экспериментах
if(fs[sn].fsize != sentsize[sn])
заменил на
while(fs[sn].fsize != sentsize[sn])
. И поэтому не мог понять как в while может header_sz в ноль сброситься. А тут if.

Вы сайты с картинками в html коде загружали с SD?
0
конечно у lifelover гдето валялся его сайтик очень удобно и несколько картинок — проверить как работает несколько соединений и большие скорость посмотреть.
0
Спасибо что уделили время.
0
И всё равно я не могу передать изображение :( Взял png изображение размером 2179 байт. Открыл через HEX редактор. Создал массив байт:

const char Img [2179] =
{
0x89,0x50,0x4E,0x47,0x0D...
...
};

И пробую передать по запросу в браузер. В состоянии http_state[sn]==HTTP_IDLE формирую заголовок:

if(strcmp(url,"/test.png")==0)
              {
                strcpy((char*)buf,http_200);
                strcat((char*)buf, http_server);
                strcat((char*)buf,"Connection: close\r\n");
                strcat((char*)buf,"Content-Length: ");
                itoa(sizeof(Img),str);
                strcat((char*)buf,str);
                strcat((char*)buf,"\r\n");
                strcat((char*)buf,"Content-Type: image/png\r\n");
                strcat((char*)buf, http_header_end);
                header_sz=strlen((char*)buf);
                http_state[sn]=HTTP_SENDING;
              }

В состоянии http_state[sn]==HTTP_SENDING отправляю изображение по частям:

if(http_state[sn]==HTTP_SENDING)
         {
        	 ret = send(sn,buf,header_sz);   // заголовок
        	 ret = send(sn,&Img[0],2048);    // первые 2048 байт изображения
        	 ret = send(sn,&Img[2048],130);  // остаток
        	 HTTP_reset(sn);
        	 disconnect(sn);
         }

В итоге при обращении по адресу 192.168.1.25/test.png браузер показывает изображение, но только эти первые 2048 байт. Последние байты не отображаются. Т.е. не передаются.



Я неделю уже бьюсь, я не понимаю почему так… Что это может быть? В чем секрет?
0
подозреваю что наверное надо делать как у меня- асинхронно т.к. после того как чип переслал свой буфер, а это произошло явно не за один раз, то на верное где то теряется второй ack и рушится логика сокета.
ps вобще вопрос странный — поломать логику программы и спросить, а почему не работает?
0
По сути у вас в цикле происходит то же самое:

ret = send(sn,buf,header_sz);   // заголовок
ret = send(sn,&Img[0],2048);    // первые 2048 байт изображения
ret = send(sn,&Img[2048],130);

В чем разница?
0
на этот вопрос я уже дал ответ, вам просто надо подумать все и обмуслить ;)
подсказка: условия и проверка события + логика ну и че нито про TCP/IP глянуть надо у тогоже lifelover
0
Добрый день, столкнулся с такой проблемой, что если при выключенном питании подать ping(или периодически get запросы), то после включения питания и инициализации wiznet, иногда ответ на поданный ping не приходит. Это также касается и GET запросов. Т.е сервер находится в состоянии SOCK_LISTEN, отправляешь get запрос а он его не принимает. Не замечали ли вы подобного?
0
  • avatar
  • pokk
  • 10 марта 2016, 09:32
На пинг отвечает сам визнет -без участия внешнего мк. Соединение тоже делает сам, но при наличии свободного инициализированного сокета, но данные и ответ естественно нужно обрабатывать внешним мк.
В инициализации визнета есть неприятный момент — ему надо сброс удерживать некоторое время, иначе он криво сбрасывается — в даташите написано об этом (сейчас не помню вроде пару мс нормально).
зы И у меня в этом коде есть ошибка — там я забыл вычесть заголовок из данных и поэтому последний пакет может не приходить.
0
здравствуйте! Пытался повторить Ваш проект. Столкнулся с интересным эффектом. Некоторые файлы из загружаемой с SD страницы передаются постоянно с ошибкой длинны. Причем рогом уперся только Хром, остальные IE & FF об ошибке не говорят, а нормально отображают странички. Такое ощущение что ошибка возникает только при определенной длинне файлов. копия файла созданная тоже не отображается в хроме.
ошибка «Failed to load resource: net::ERR_CONTENT_LENGTH_MISMATCH»



Вы с таким эффектом не сталкивались? Спасибо.
0
вопрос решился.
"… в этом коде есть ошибка — там я забыл вычесть заголовок из данных и поэтому последний пакет может не приходить." Вы уже ответили.:)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.