Jump to content

    
Sign in to follow this  
mr_smit

Сервер На Wiznet W5500, передать изображение

Recommended Posts

Прикупил у китайцев плату с чипом W5500. Это обновленная версия Ethernet чипа W5100. Подключил к STM32 Discovery. За основу взял эту статью: http://we.easyelectronics.ru/GYUR22/w5500s...web-server.html. Убрал функции работы с SD картой и скомпилировал всё это в CooCox. Сервер заработал. Т.е. отдает в браузер статичный текст.

 

Хочу добавить на html страницу картинку. Для теста сделал PNG размером 5х5 пикселей. Добавил её в html код:

<html>
<head>
<title>Умный дом</title>
<meta http-equiv='content-type' content='text/html; charset=windows-1251'>
</head>
<body>
<br><br><br><center><b>Тестовая страница W5500</b></center><br>
<center><img src='test.png'></center>
</body>
</html>

 

Открыл картинку в hex редакторе и сделал массив сырых данных:

const char Img [205] =
{
0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,
0x00,0x00,0x00,0x05,
0x00,0x00,0x00,0x05,0x08,0x02,0x00,0x00,0x00,0x02,0x0D,0xB1,0xB2,0x00,0x00,0x00,
0x01,0x73,0x52,0x47,
0x42,0x00,0xAE,0xCE,0x1C,0xE9,0x00,0x00,0x00,0x06,0x62,0x4B,0x47,0x44,0x00,0xFF,
0x00,0xFF,0x00,0xFF,
0xA0,0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0B,0x13,
0x00,0x00,0x0B,0x13,
0x01,0x00,0x9A,0x9C,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4D,0x45,0x07,0xDF,0x05,
0x0A,0x0E,0x01,0x20,
0xA0,0x0A,0x57,0xCD,0x00,0x00,0x00,0x19,0x74,0x45,0x58,0x74,0x43,0x6F,0x6D,0x6D,
0x65,0x6E,0x74,0x00,
0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4D,
0x50,0x57,0x81,0x0E,
0x17,0x00,0x00,0x00,0x28,0x49,0x44,0x41,0x54,0x08,0xD7,0x4D,0x8B,0x41,0x0E,0x00,
0x30,0x08,0xC2,0xA8,
0xFF,0xFF,0x73,0x3D,0x18,0xDD,0xB8,0x34,0x94,0x40,0x12,0x35,0x9B,0x52,0x81,0xD7,
0x67,0x3F,0x55,0x83,
0x53,0xFC,0x67,0xA0,0x01,0x8E,0xCE,0x0F,0x06,0x6E,0x28,0x44,0x78,0x00,0x00,0x00,
0x00,0x49,0x45,0x4E,
0x44,0xAE,0x42,0x60,0x82
};

 

А в функции tcp_http_mt добавил реакцию на запрос картинки. Т.е. при ответе к HTTP заголовку приклеиваем сырые данные картинки.

 

int32_t tcp_http_mt(uint8_t sn, uint8_t* buf, uint16_t port)
{
int32_t ret;
uint32_t size = 0;
char *url,*p;

switch(getSn_SR(sn))
{
    case SOCK_ESTABLISHED :

        if(getSn_IR(sn) & Sn_IR_CON)
        {
        setSn_IR(sn,Sn_IR_CON);
        }

        if((size = getSn_RX_RSR(sn)) > 0)
        {
        if(size > DATA_BUF_SIZE)
        {
        size = DATA_BUF_SIZE;
        }

        ret = recv(sn,buf,size);

        HTTP_reset(sn);

        if(ret <= 0)
        {
        return ret;
        }

        url =(char*) buf + 4;

        if((http_state[sn]==HTTP_IDLE)&&(memcmp(buf, "GET ", 4)==0)&&((p = strchr(url, ' ')))) // extract URL from request header
        {
            *(p++) = 0; //making zeroed url string

            sentsize[sn]=0;

            if(strcmp(url,"/")==0)
            {
            strcpy((char*)buf,http_200);
            strcat((char*)buf, http_server);
            strcat((char*)buf,"Content-Type: text/html; charset=windows-1251\r\n");
            strcat((char*)buf,"Cache-Control: no-cache\r\n");
            strcat((char*)buf,http_connection_close);
            strcat((char*)buf, http_header_end);
            strcat((char*)buf,site);
            size=strlen((char*)buf);
            http_state[sn]=HTTP_SENDING;
            }

            if(strcmp(url,"/test.png")==0)
                        {
                            strcpy((char*)buf,http_200);
                            strcat((char*)buf, http_server);
                            strcat((char*)buf,"Content-Type: image/png\r\n");
                            //
                            strcat((char*)buf,"Content-Lenght: 205\r\n");
                            strcat((char*)buf,"Connection: keep-alive\r\n");
                            strcat((char*)buf,"Keep-Alive: timeout=20\r\n");
                            strcat((char*)buf,"Accept-Ranges: bytes\r\n");
                            strcat((char*)buf,"Cache-Control: no-cache\r\n");
                            strcat((char*)buf, http_header_end);
                            strcat((char*)buf,Img);
                            size=strlen((char*)buf);
                            http_state[sn]=HTTP_SENDING;
                        }
        }

/*
        Отправляем данные пока размер отправленных данных sentsize[sn] не сравняется с размером данных size
        которые мы подготовили для отправки

... вроде бы...
*/
        if(http_state[sn]==HTTP_SENDING)
        {
    while (sentsize[sn] < size)
    {
    //DATA_BUF_SIZE

    ret = send(sn,buf,size);
         if(ret < 0)
         {
         close(sn);
         return ret;
         }
         sentsize[sn] += ret; // Don't care SOCKERR_BUSY, because it is zero.
    }
    HTTP_reset(sn);
    disconnect(sn);
        }
        }

        break;

    case SOCK_CLOSE_WAIT :

HTTP_reset(sn);

        if((ret=disconnect(sn)) != SOCK_OK)
        return ret;

        break;
    case SOCK_INIT :

HTTP_reset(sn);

        if( (ret = listen(sn)) != SOCK_OK) return ret;
        break;
    case SOCK_CLOSED:

HTTP_reset(sn);

        if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)
        return ret;

        break;

    default:
HTTP_reset(sn);
        break;
}
return 1;
}

 

В итоге запрос на картинку в браузере идет, но сервер её не отдает.

post-51457-1431404758_thumb.png

При этом браузер получает заголовки ответа. Но самого изображения нет.

 

От безысходности установил HTTP снифер Wireshark. Удалось выяснить, что при передаче картинки сервер отправляет только её первые 8 байт. И на этом всё.

post-51457-1431404789_thumb.png

 

3-й день бьюсь. Не понимаю почему так происходит. Или где то функцию отправки надо изменить или с размером передаваемых данных что то не то. Гуглил и по W5100 и по W5200 и по W5500, но именно как передать изображение не нашел.

 

web_discovery_w5500_sd.zip

 

Может есть у кого какие идеи?

Share this post


Link to post
Share on other sites

Вы пытаетесь поместить бинарный файл в строку.

img[] {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D ... };

                              strcat((char*)buf, http_header_end);
                              strcat((char*)buf,Img);
                              size=strlen((char*)buf);

Но на символе конца строки функция и останавливается.

Но функция копирования строки останавливается на символе 0x00 - нуль-терминатор.

Вам нужен бинарный буфер вместо строкового.

И попробуйте сделать через memcpy:

strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него.
size=strlen((char*)buf); // Текущий размер строк в буфере
memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер.
size += sizeof(Img); // Увеличиваем на размер картинки.

Где-то мог случайно ошибиться.

Но на вид всё должно заработать.

 

Исправления касаются только main.c

Edited by AlanDrakes

Share this post


Link to post
Share on other sites
Но функция копирования строки останавливается на символе 0x00 - нуль-терминатор.

Вам нужен бинарный буфер вместо строкового.

И попробуйте сделать через memcpy:

Точно. Похоже на правду. Спасибо! Проверить, к сожалению, смогу только вечером.

Edited by mr_smit

Share this post


Link to post
Share on other sites

strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него.
size=strlen((char*)buf); // Текущий размер строк в буфере
memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер.
size += sizeof(Img); // Увеличиваем на размер картинки.

 

А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков?

 

Может отправить сначала заголовок, а затем картинку?

ret = send(sn,buf, strlen(buf));
ret = send(sn, Img, sizeof(Img));

Edited by mr_smit

Share this post


Link to post
Share on other sites
....А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков?...

 

конечно же затрёт. И ещё пять копеек. Картинку Вы точно в бинарном виде собрались передавать, а не в base64?

Share this post


Link to post
Share on other sites
конечно же затрёт. И ещё пять копеек. Картинку Вы точно в бинарном виде собрались передавать, а не в base64?

В бинарном. Я посмотрел, браузер получает картинки в чистом виде. Для этого указывается тип (image/png, image/jpg и т.д.) и количество байт.

 

Хотя черт его знает:

post-51457-1431422109_thumb.png

 

Надо попробовать в base64 конвертнуть и strcat-ом приклеить.

Edited by mr_smit

Share this post


Link to post
Share on other sites

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

ret = send(sn,buf,size);
ret = send(sn,(char*)Img, sizeof(Img));

 

Так же можно вставить картинку в кодировке base64 непосредственно в html код страницы.

const char site[] =
        "<html>"
        "<head>"
        "<title>Умный дом</title>"
        "<meta http-equiv='content-type' content='text/html; charset=windows-1251'>"
        "</head>"
    "<body>"
        "<br><br><br><center><b>Тестовая страница W5500</b></center><br>"
        "<center><img src='data:image/png;base64,"

        "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A"
        "/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB98FCg4BIKAKV80AAAAZdEVYdENv"
        "bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAKElEQVQI102LQQ4AMAjCqP//cz0Y3bg0lEAS"
        "NZtSgddnP1WDU/xnoAGOzg8GbihEeAAAAABJRU5ErkJggg=="
                "'></center>"
    "</body>"
    "</html>";

 

В этом случае при запросе страницы она открывается корректно с картинкой. Но когда я взял картинку размером 5 КБ, то страница не открывается. Постоянно висит ожидание.

 

Размер передающего буфера 2 КБ. Т.е. надо разбить данные на блоки. Вот это место:

...
sentsize[sn]=0;
...

if(http_state[sn]==HTTP_SENDING)
               {
                 while (sentsize[sn] < size)
                 {
                 ret = send(sn,buf+sentsize[sn],size-sentsize[sn]);

                      if(ret < 0)
                      {
                      close(sn);
                      return ret;
                      }

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

 

ret длина реально отправленных данных. Опять же не уловлю в чем проблема.

Share this post


Link to post
Share on other sites
strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него.
size=strlen((char*)buf); // Текущий размер строк в буфере
memcpy((char*)buf,Img[0]*, sizeof(Img)); // Копируем картинку в буфер.
size += sizeof(Img); // Увеличиваем на размер картинки.

 

А в таком случае разве memcpy не затрет buf? И в buf будет только сама картинка без заголовков?

 

Может отправить сначала заголовок, а затем картинку?

ret = send(sn,buf, strlen(buf));
ret = send(sn, Img, sizeof(Img));

 

А, да, видимо, как раз там и ошибся.

Тогда, передавая картинку бинарными данными, нужно делать так:

strcat((char*)buf, http_header_end); // Заголовок... и всё, что до него.
size=strlen((char*)buf); // Текущий размер строк в буфере
memcpy((char*)buf+size,Img[0]*, sizeof(Img)); // Копируем картинку в буфер (со смещением на _размер_ заголовков).
size += sizeof(Img); // Увеличиваем на размер картинки.

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

 

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

Но тогда у Вас будет очень большой буфер отправки и он может не поместиться в RAM контроллера.

Опять же, грабли.

 

По отправке:

if(http_state[sn]==HTTP_SENDING)
               {
                 while (sentsize[sn] < size)
                 {
                 ret = send(sn,buf+sentsize[sn],size-sentsize[sn]);

send() принимает ссылку на буфер? С библиотекой я не знаком, но кажется, Вы получаете на второй итерации выход за границы массива.

Попробуйте так:

ret = send(sn,buf,size-sentsize[sn]);

Опять же, возможно, ошибаюсь..

Share this post


Link to post
Share on other sites

int32_t send(uint8_t sn, uint8_t * buf, uint16_t len)

 

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

Edited by mr_smit

Share this post


Link to post
Share on other sites

Сделал так:

ret = send(sn,buf,size);  // отправляем заголовок

ret = send(sn,(char*)Img1,sizeof(Img1)); // картинка по частям
ret = send(sn,(char*)Img2,sizeof(Img2)); //
ret = send(sn,(char*)Img3,sizeof(Img3)); //

disconnect(sn);

в итоге загружается только первая треть картинки или даже меньше:

post-51457-1431523497.jpg

Edited by mr_smit

Share this post


Link to post
Share on other sites

Передавать нужно, убедившись, что сокет готов отправлять следующую порцию данных.

Я бы не рекомендовал передавать за раз более 1400 байт (MTU пакета как правило, ~1500, но лучше ограничить чуть меньшим объёмом) и, если библиотека только записывает данные в ETH, но не проверяет их отправку - то проверять это самостоятельно.

Если мне не слишком изменяет память, то W5500 должен быть близок к W5100 в плане памяти - 8кB на 4 сокета, то есть, 2кБ/сокет (приём и передача отдельные), куда записываются сформированные пакеты.

 

А человек, предположительно, загружает меньшими порциями.

Кстати, его код пробовали разобрать для понимания? То есть, его последовательность действий:

Чтение файла, обработка, передача... и т.п.?

Share this post


Link to post
Share on other sites
Кстати, его код пробовали разобрать для понимания? То есть, его последовательность действий:

Чтение файла, обработка, передача... и т.п.?

Так я его исходник и правлю. Функция Send, как я понимаю, сама следит за передающим буфером:

int32_t send(uint8_t sn, uint8_t * buf, uint16_t len)
{
  uint8_t tmp=0;
  uint16_t freesize=0;

  CHECK_SOCKNUM();
  CHECK_SOCKMODE(Sn_MR_TCP);
  CHECK_SOCKDATA();
  tmp = getSn_SR(sn);
  if(tmp != SOCK_ESTABLISHED && tmp != SOCK_CLOSE_WAIT) return SOCKERR_SOCKSTATUS;
  if( sock_is_sending & (1<<sn) )
  {
  tmp = getSn_IR(sn);
  if(tmp & Sn_IR_SENDOK)
  {
	 setSn_IR(sn, Sn_IR_SENDOK);
	 #if _WZICHIP_ == 5200
		if(getSn_TX_RD(sn) != sock_next_rd[sn])
		{
		   setSn_CR(sn,Sn_CR_SEND);
		   while(getSn_CR(sn));
		   return SOCKERR_BUSY;
		}
	 #endif
	 sock_is_sending &= ~(1<<sn);		 
  }
  else if(tmp & Sn_IR_TIMEOUT)
  {
	 close(sn);
	 return SOCKERR_TIMEOUT;
  }
  else return SOCK_BUSY;
  }
  freesize = getSn_TxMAX(sn);
  if (len > freesize) len = freesize; // check size not to exceed MAX size.
  while(1)
  {
  freesize = getSn_TX_FSR(sn);
  tmp = getSn_SR(sn);
  if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT))
  {
	 close(sn);
	 return SOCKERR_SOCKSTATUS;
  }
  if( (sock_io_mode & (1<<sn)) && (len > freesize) ) return SOCK_BUSY;
  if(len <= freesize) break;
  }
  wiz_send_data(sn, buf, len);
  #if _WIZCHIP_ == 5200
  sock_next_rd[sn] = getSn_TX_RD(sn) + len;
  #endif
  setSn_CR(sn,Sn_CR_SEND);
  /* wait to process the command... */
  while(getSn_CR(sn));
  sock_is_sending |= (1 << sn);
  return len;
}

 

А отправляет он так:

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.
                 }

Читает с карты 2048 байт минус размер HTTP заголовка (DATA_BUF_SIZE-header_sz). А отправляет по факту каждый раз 2048 байт (blocklen+header_sz) кроме последней итерации.

 

Сейчас только заметил blocklen+header_sz. Может надо HTTP заголовок отправлять с каждым куском данных??? Я то сейчас отправляю заголовок, а потом просто куски картинки. А у него в буфере каждый раз оказывается заголовок + новая порция считанных данных &buf[header_sz]. Правильно?

Edited by IgorKossak
[codebox] для длинного кода, [code] - для короткого!

Share this post


Link to post
Share on other sites
...                 f_read(&fs[sn], &buf[header_sz], DATA_BUF_SIZE-header_sz, &blocklen);

                   ret = send(sn,buf,blocklen+header_sz);
...

Читает с карты 2048 байт минус размер HTTP заголовка...

 

ышо 5 копеек.

Вы попали в типичную ошибку для протокола TCP. Это протокол ПОТОКОВЫЙ(!) т.е. при вычитывании из буффера может прийти ЛЮБОЕ

кол-во байт в пределах максимально переданного кол-ва. Т.е.

 

Передатчик передаёт

500+500+24

Приёмник МОЖЕТ принять

24+100+100+300+400+50+50

 

почувствуйте разницу...

PS

Особенно эти ослинные уши начинают лезть, когда система начинает работать в боевых сетях - с различным временным лагом

прихода пакетов на IP уровне, с доп. разборкой-сборкой на IP уровне, и т.д..

Edited by kolobok0

Share this post


Link to post
Share on other sites
Передатчик передаёт

500+500+24

Приёмник МОЖЕТ принять

24+100+100+300+400+50+50

 

почувствуйте разницу...

Выходит надо учитывать переменную ret. Это число реально переданных байт. И в зависимости от неё двигать указатель в буфере отправки buf при посылке следующей части. Но вопрос о прикреплении заголовка к каждой части посылки остается открытым.

 

Share this post


Link to post
Share on other sites

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

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

Эх, формировал сам пакеты - не было проблем... они уходили такими, какими должны. А вот W5100 и выше умеют сокеты и там уже хитрее - писать нужно в сокет, а чип позаботится об отправке пакета(-ов).

Кстати, теоретически, писать можно непрерывно (главное, чтобы указатель записи не пытался затреть данные, которые чип ещё не успел отправить).

И, в таком случае, kolobok0 будет прав - пакеты определяются передатчиком.

Помнится, когда писал сетевые приложения в Win* - получил странность: при отправке 2-4 мелких пакетов, приходит один, но большой. Долго ломал голову. Оказалось, дело в тех же сокетах и времени.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this