Перейти к содержанию
    

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

Прикупил у китайцев плату с чипом 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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Изменено пользователем AlanDrakes

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

Изменено пользователем mr_smit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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));

Изменено пользователем mr_smit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

post-51457-1431422109_thumb.png

 

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

Изменено пользователем mr_smit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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 длина реально отправленных данных. Опять же не уловлю в чем проблема.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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]);

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

Изменено пользователем mr_smit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сделал так:

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

Изменено пользователем mr_smit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

 

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Так я его исходник и правлю. Функция 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]. Правильно?

Изменено пользователем IgorKossak
[codebox] для длинного кода, [code] - для короткого!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...                 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 уровне, и т.д..

Изменено пользователем kolobok0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

500+500+24

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

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

 

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

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

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

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...