Вопрос: WebServer на Arduino. Графическое оформление

9ser
Offline
Зарегистрирован: 18.11.2012

Подскажите есть статьи или готовые исходники с красиво оформленными страницами

Изображения, переходы между страницами, таблицы и т.д.

А не с надписью Hello World или данными с входов /выходов

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

9ser пишет:

Подскажите есть статьи или готовые исходники с красиво оформленными страницами

"Красиво оформленная страница" - это не для мощности ардуино. :) Там памяти не настолько много.

Собственно, берете html код "красивой страницы", выкидываете из него все лишнее, а потом просто "печатаете" это из своего скетча.

У меня это делается примерно так: Где сам html код, надеюсь, видно?

        plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<html><head><title>E-Shield</title></head> "));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<body><center><h1>Arduino Ethernet Shield</h1><br>uptime.value "));


    

 

Densoider
Offline
Зарегистрирован: 02.11.2012

Приветствую,

Кстати вопрос по теме. А сколько вообще памяти нужно для HTML?

Из собственного опыта - пробовал написать скетч, чтоб выводил на вебстраничку состояние всех выходов (аналоговых и цифровых) просто в виде таблички и начиная с определенного момента (просто какого то тэга) вся система вешалась наглухо.

Это оно и есть? Т.е. переполнение памяти?

А если так, то можно в таком случае какое нибудь ОЗУ как нибудь подключить?

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Densoider пишет:
просто в виде таблички и начиная с определенного момента (просто какого то тэга) вся система вешалась наглухо.

Это оно и есть? Т.е. переполнение памяти?

Больше похоже на переполнение буфера "сетевой карты". По крайней мере я на enc28j60 увеличивал именно сетевой буфер до 1500 байт. Тут надо либо лезть в теорию и разбираться - умеет ли нынешняя реинкарнация сетевого адаптера слать страницу в нескольких сетевых пакетах (chunked packet), либо ограничиться объемом html кода одного tcp пакета: 1470 байт при общем объеме буфера в 1500 байт.

Densoider
Offline
Зарегистрирован: 02.11.2012

О! Просветите пожалуйста за увеличение буфера. Где крутить? у меня именно то самое - http://www.ebay.com/itm/Ethernet-Shield-Arduino-Nano-work-ENC28J60-RJ45-Webserver-/281006055332

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Densoider пишет:

О! Просветите пожалуйста за увеличение буфера. Где крутить?

У меня в примере была строчка с BUFFER_SIZE 500, я его просто увеличил... Шилд у меня чуть другой, от EKitsZone.

#define BUFFER_SIZE 1450
static uint8_t buf[BUFFER_SIZE+1];
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];

EtherShield es=EtherShield();

 

Densoider
Offline
Зарегистрирован: 02.11.2012

Вы какой библиотекой пользуетесь?

#include "etherShield.h"
#include "ETHER_28J60.h"

Этими?

До каких пределов можно увеличивать буфера? (Нашел. Для этой микрухи _общий буфер_ - 8кб)

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Densoider пишет:

Вы какой библиотекой пользуетесь?

#include "etherShield.h"
#include "ETHER_28J60.h"

Нет, у меня с шилдом шла библиотека, ей и пользуюсь...

ls -la EKitsZoneEtherShield/*.*
-rw-r--r-- 1 root root 10645 нояб. 12 23:48 EKitsZoneEtherShield/enc28j60.c
-rw-r--r-- 1 root root  9740 янв.  26  2008 EKitsZoneEtherShield/enc28j60.h
-rw-r--r-- 1 root root  2000 марта 25  2008 EKitsZoneEtherShield/etherShield.cpp
-rw-r--r-- 1 root root  1967 янв.  27  2008 EKitsZoneEtherShield/etherShield.h
-rw-r--r-- 1 root root 16307 янв.  27  2008 EKitsZoneEtherShield/ip_arp_udp_tcp.c
-rw-r--r-- 1 root root  1528 янв.  26  2008 EKitsZoneEtherShield/ip_arp_udp_tcp.h
-rw-r--r-- 1 root root   984 янв.  27  2008 EKitsZoneEtherShield/keywords.txt
-rw-r--r-- 1 root root  2992 янв.  26  2008 EKitsZoneEtherShield/net.h

 

#include "etherShield.h"
#include "stdio.h"
#include "avr/wdt.h" // include the watchdog library to reboot the Arduino

Densoider пишет:

До каких пределов можно увеличивать буфера?

Надо читать теорию про MTU. :) В сети 10/100 мегабит - максимальный размер tcp пакета - 1500 байт, за минусом технических расходов - полезного в нем получается 1472 байта. Но это верхний предел - пакеты бывают и меньше.

Densoider
Offline
Зарегистрирован: 02.11.2012

Вообще удивительно разные библиотеки и код для них.

Оцените код Веб Сервера -

Вот один -

#include "etherShield.h"

// please modify the following two lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24}; 
static uint8_t myip[4] = {192,168,1,15};
static char baseurl[]="http://192.168.1.15/";
static uint16_t mywwwport =80; // listen port for tcp/www (max range 1-254)
// or on a different port:
//static char baseurl[]="http://10.0.0.24:88/";
//static uint16_t mywwwport =88; // listen port for tcp/www (max range 1-254)
//

#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];

EtherShield es=EtherShield();

// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf);
int8_t analyse_cmd(char *str);
void setup(){
  
   /*initialize enc28j60*/
	 es.ES_enc28j60Init(mymac);
   es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
   delay(10);
        
	/* Magjack leds configuration, see enc28j60 datasheet, page 11 */
	// LEDA=greed LEDB=yellow
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
  // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
	delay(100);
        
  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);


}

void loop(){
  uint16_t plen, dat_p;
  int8_t cmd;

  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

	/*plen will ne unequal to zero if there is a valid packet (without crc error) */
  if(plen!=0){
	           
    // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
    if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
      es.ES_make_arp_answer_from_request(buf);
      return;
    }

    // check if ip packets are for us:
    if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
      return;
    }
    
    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
      es.ES_make_echo_reply_from_request(buf,plen);
      return;
    }
    
    // tcp port www start, compare only the lower byte
    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==mywwwport){
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
         es.ES_make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack
         return;     
      }
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
        es.ES_init_len_info(buf); // init some data structures
        dat_p=es.ES_get_tcp_data_pointer();
        if (dat_p==0){ // we can possibly have no data, just ack:
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
            es.ES_make_tcp_ack_from_any(buf);
          }
          return;
        }
        if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
          	// head, post and other methods for possible status codes see:
            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
            goto SENDTCP;
        }
 	if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
                plen=print_webpage(buf);
            goto SENDTCP;
         }
        cmd=analyse_cmd((char *)&(buf[dat_p+5]));
        if (cmd==1){
             plen=print_webpage(buf);
        }
SENDTCP:  es.ES_make_tcp_ack_from_any(buf); // send ack for http get
           es.ES_make_tcp_ack_with_data(buf,plen); // send data       
      }
    }
  }
        
}
// The returned value is stored in the global var strbuf
uint8_t find_key_val(char *str,char *key)
{
        uint8_t found=0;
        uint8_t i=0;
        char *kp;
        kp=key;
        while(*str &&  *str!=' ' && found==0){
                if (*str == *kp){
                        kp++;
                        if (*kp == '\0'){
                                str++;
                                kp=key;
                                if (*str == '='){
                                        found=1;
                                }
                        }
                }else{
                        kp=key;
                }
                str++;
        }
        if (found==1){
                // copy the value to a buffer and terminate it with '\0'
                while(*str &&  *str!=' ' && *str!='&' && i<STR_BUFFER_SIZE){
                        strbuf[i]=*str;
                        i++;
                        str++;
                }
                strbuf[i]='\0';
        }
        return(found);
}

int8_t analyse_cmd(char *str)
{
        int8_t r=-1;
     
        if (find_key_val(str,"cmd")){
                if (*strbuf < 0x3a && *strbuf > 0x2f){
                        // is a ASCII number, return it
                        r=(*strbuf-0x30);
                }
        }
        return r;
}


uint16_t print_webpage(uint8_t *buf)
{
        uint16_t plen;
     		plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Welcome to Arduino Ethernet Shield V1.0  </h1></p> "));
       	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<hr><br> <h2><font color=\"blue\">-- Put your ARDUINO online -- "));
 				plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<br> Control digital outputs "));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<br> Read digital analog inputs HERE "));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("  <br></font></h2> ") );
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</center><hr>  V1.0 <a href=\"http://www.nuelectronics.com\">www.nuelectronics.com<a>"));
  
        return(plen);
 }

А вот второй -

// A simple web server that always just says "Hello World"

#include "etherShield.h"
#include "ETHER_28J60.h"

static uint8_t mac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24};   // this just needs to be unique for your network, 
                                                                // so unless you have more than one of these boards
                                                                // connected, you should be fine with this value.
                                                           
static uint8_t ip[4] = {192, 168, 1, 15};                       // the IP address for your board. Check your home hub
                                                                // to find an IP address not in use and pick that
                                                                // this or 10.0.0.15 are likely formats for an address
                                                                // that will work.

static uint16_t port = 80;                                      // Use port 80 - the standard for HTTP

ETHER_28J60 e;

void setup()
{ 
  e.setup(mac, ip, port);
}

void loop()
{
  if (e.serviceRequest())
  {
    e.print("<H1>Analog Values</H1><br/><table>");
    e.print("<tr><th>Input</th><th>Value</th></tr>");
    for (int i = 0; i < 6; i++)
    {
      e.print("<tr><td>"); e.print(i); e.print("</td><td>"); e.print(analogRead(i)); e.print("</td></tr>");
    }
    e.print("</table>");
    e.respond();
  }
  delay(100);
}

Вот и скажите мне, какой вариант выберет совсем новичек, как я?

Кстати, раз уж пошла речь про зависание, то вот проблемный код, на котором все виснет -

// A simple web server that always just says "Hello World"

#include "etherShield.h"
#include "ETHER_28J60.h"

static uint8_t mac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24};   // this just needs to be unique for your network, 
                                                                // so unless you have more than one of these boards
                                                                // connected, you should be fine with this value.
                                                           
static uint8_t ip[4] = {192, 168, 1, 15};                       // the IP address for your board. Check your home hub
                                                                // to find an IP address not in use and pick that
                                                                // this or 10.0.0.15 are likely formats for an address
                                                                // that will work.

static uint16_t port = 80;                                      // Use port 80 - the standard for HTTP

ETHER_28J60 e;

void setup()
{ 
  e.setup(mac, ip, port);
}

void loop()
{
  if (e.serviceRequest())
  {
    e.print("<H1>Analog Values</H1><hr><table border>");
    e.print("<tr><th>Input</th><th>Value</th></tr>");
    for (int i = 0; i < 8; i++)
    {
      e.print("<tr><td>"); 
      e.print(i); 
      e.print("</td><td>"); 
      e.print(analogRead(i)); 
      e.print("</td></tr>");
    }
    e.print("</table><hr>");
    e.print("<H1>Digital Values</H1><br><table border>");
    e.print("<tr><th>Input</th><th>Value</th></tr>");
    for (int j = 0; j < 14; j++)
    {
      e.print("<tr><td>"); 
      e.print(j); 
      e.print("</td><td>"); 
      e.print(digitalRead(7)); 
      e.print("</td></tr>");
    }
    e.print("</table>");
    e.respond();
  }
  delay(100);
}

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012
e.print(digitalRead(7));

Я бы покопал в части вот этой функции, потому, что в той реализации, что у меня (это Ваш верхний пример) - код требует, чтобы в печать шли данные типа int, а на выходе у Вас там может быть бог знает что.

Что тут можно сделать? Например разделить операции чтения и вывода на печать. Между ними вывести в serial отладочные сообщения со значениями digitalRead. Тем самым Вы поймете - на чем вешается. Либо вообще на время закомментировать вывод на веб-сервер и дать железке время покрутиться: может она зависает просто по причине просадки напряжения питания... В любом случае полезно втыкать отладочный вывод в разных местах кода для проверки состояния.

Densoider
Offline
Зарегистрирован: 02.11.2012

Извините за мою ошибку. эта строчка осталась от попыток понять причину. ее вообще быть не должно.

Но тут даже и до этого не доходит т.к если закоментить строки , начиная с нее же, т.е. оставить сверху только html создания таблицы для Digital, то виснет прям на этой строке.

e.print("<tr><td>");

вот прям на ней.

Densoider
Offline
Зарегистрирован: 02.11.2012

Провел тупейшее исследование.

Результаты удивили

Вот код -

// A simple web server that always just says "Hello World"

#include "etherShield.h"
#include "ETHER_28J60.h"

static uint8_t mac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24};   // this just needs to be unique for your network, 
                                                                // so unless you have more than one of these boards
                                                                // connected, you should be fine with this value.
                                                           
static uint8_t ip[4] = {192, 168, 1, 15};                       // the IP address for your board. Check your home hub
                                                                // to find an IP address not in use and pick that
                                                                // this or 10.0.0.15 are likely formats for an address
                                                                // that will work.

static uint16_t port = 80;                                      // Use port 80 - the standard for HTTP

ETHER_28J60 e;

void setup()
{ 
  e.setup(mac, ip, port);
}

void loop()
{
  if (e.serviceRequest())
  {
    e.print("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111<br>");
    e.print("222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222<br>");
    e.print("333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333<br>");
//    e.print("444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444<br>");
//    e.print("555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555<br>");
//    e.print("666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666<br>");
//    e.print("777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777<br>");
//    e.print("888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888<br>");
//    e.print("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999<br>");
//    e.print("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000<br>");
    e.respond();
  }
  delay(100);
}

ДуиноВебСервер вешается при выводе четверок.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Densoider пишет:

Провел тупейшее исследование. Результаты удивили ДуиноВебСервер вешается при выводе четверок.

А если закомментировать все предыдущие строчки, кроме "четверок"? :)

 

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

Densoider
Offline
Зарегистрирован: 02.11.2012

Монстр даже с единичками не работает при буфере 500 и строковом 22.

Если я ставлю буфер 1500 и строковый 80, то добирается до семерок и на восьмерках дохнет.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Densoider пишет:

Монстр даже с единичками не работает при буфере 500 и строковом 22.

Если я ставлю буфер 1500 и строковый 80, то добирается до семерок и на восьмерках дохнет.

Значит chunked не умеет и надо добровольно ограничиться размером странички в 1400 байт, либо дописать нужный функционал. Хотя, сейчас цена за wiz5100 уже упала и есть смысл покупать его - там, по слухам, нижний уровень tcp реализован в самом чипе.

Densoider
Offline
Зарегистрирован: 02.11.2012

Короче ВСЕМ!

Разобрался с "простой" библиотекой

#include "ETHER_28J60.h"

Там в файле ETHER_28J60.cpp указан размер буфера и буфера строки -

#define BUFFER_SIZE 500 (надо поставить 1500, НЕ БОЛЬШЕ !!!)

#define STR_BUFFER_SIZE 32 (я поставил 80 - хз на что влияет этот строковый буфер)

После чего мой код из моего сообщения заработал!

В простой библе гораздо приятней писать. Там нет того ада что в монстровой.

Андрей! Спасибо за наводку на размер буфера.

Вот работающий код с таблицами -

// Пример выводящий состояние всех аналоговых и всех цифровых выводов на ATMega328
// А конкретно на Ардуино Нано в.3

#include "etherShield.h"
#include "ETHER_28J60.h"

static uint8_t mac[6] = {0x54, 0x55, 0x58, 0x10, 0x00, 0x24};   // this just needs to be unique for your network 
static uint8_t ip[4] = {192, 168, 1, 15};                       // the IP address for your board. 
static uint16_t port = 80;                                      // Use port 80 - the standard for HTTP

ETHER_28J60 e;

void setup()
{ 
  e.setup(mac, ip, port);
}

void loop()
{
  if (e.serviceRequest())
  {
    e.print("<H1>Analog Values</H1><hr><table border>");
    e.print("<tr><th>Input</th><th>Value</th></tr>");
    for (int i = 0; i < 8; i++)
    {
      e.print("<tr><td>"); 
      e.print(i); 
      e.print("</td><td>"); 
      e.print(analogRead(i)); 
      e.print("</td></tr>");
    }
    e.print("</table><hr>");
    e.print("<H1>Digital Values</H1><br><table border>");
    e.print("<tr><th>Input</th><th>Value</th></tr>");
    for (int j = 0; j < 14; j++)
    {
      e.print("<tr><td>"); 
      e.print(j); 
      e.print("</td><td>"); 
      e.print(digitalRead(j)); 
      e.print("</td></tr>");
    }
    e.print("</table>");
    e.respond();
  }
  delay(100);
}

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Так какой у Вас, в результате, получился максимальный объем странички? Ее нужно сохранить из браузера в файл и посмотреть объем этого файла в проводнике.

Bottle
Offline
Зарегистрирован: 01.01.2014

Andrey_Y_Ostanovsky пишет:

Так какой у Вас, в результате, получился максимальный объем странички? Ее нужно сохранить из браузера в файл и посмотреть объем этого файла в проводнике.

есть вопрос :

http://arduino.ru/forum/apparatnye-voprosy/chto-primenit-kak-http-server

 

NightW01f
Offline
Зарегистрирован: 09.02.2015

Всем доброго времени суток!

Столкнулся с той же самой проблемой...

Насколько я понимаю, можно разбить отправку html на несколько пакетов... Вопрос лишь в том как это сделать, используя данную библиотеку?

функция respond() вызывает две функции

void ETHER_28J60::respond()
{
	es.ES_make_tcp_ack_from_any(buf); // send ack for http get
	es.ES_make_tcp_ack_with_data(buf,plen); // send data
}

и сама функция отправки данных содержит отсылку флага FIN (разрыв соединения)

// you must have called init_len_info at some time before calling this function
// dlen is the amount of tcp data (http data) we send in this packet
// You can use this function only immediately after make_tcp_ack_from_any
// This is because this function will NOT modify the eth/ip/tcp header except for
// length and checksum
void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen)
{
        uint16_t j;
        // fill the header:
        // This code requires that we send only one data packet
        // because we keep no state information. We must therefore set
        // the fin here:
        buf[TCP_FLAG_P]=TCP_FLAG_ACK_V|TCP_FLAG_PUSH_V|TCP_FLAG_FIN_V;

        // total length field in the IP header must be set:
        // 20 bytes IP + 20 bytes tcp (when no options) + len of data
        j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen;
        buf[IP_TOTLEN_H_P]=j>>8;
        buf[IP_TOTLEN_L_P]=j& 0xff;
        fill_ip_hdr_checksum(buf);
        // zero the checksum
        buf[TCP_CHECKSUM_H_P]=0;
        buf[TCP_CHECKSUM_L_P]=0;
        // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
        j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2);
        buf[TCP_CHECKSUM_H_P]=j>>8;
        buf[TCP_CHECKSUM_L_P]=j& 0xff;
        enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf);
}

Есть ли какие-нибудь соображения, как модифицировать данную функцию под отправку отдельных пакетов и отдельно вызвать завершение отправки, отослав Fin?