Вопрос: WebServer на Arduino. Графическое оформление
- Войдите на сайт для отправки комментариев
Пнд, 03/12/2012 - 11:02
Подскажите есть статьи или готовые исходники с красиво оформленными страницами
Изображения, переходы между страницами, таблицы и т.д.
А не с надписью Hello World или данными с входов /выходов
Подскажите есть статьи или готовые исходники с красиво оформленными страницами
"Красиво оформленная страница" - это не для мощности ардуино. :) Там памяти не настолько много.
Собственно, берете 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 "));Приветствую,
Кстати вопрос по теме. А сколько вообще памяти нужно для HTML?
Из собственного опыта - пробовал написать скетч, чтоб выводил на вебстраничку состояние всех выходов (аналоговых и цифровых) просто в виде таблички и начиная с определенного момента (просто какого то тэга) вся система вешалась наглухо.
Это оно и есть? Т.е. переполнение памяти?
А если так, то можно в таком случае какое нибудь ОЗУ как нибудь подключить?
Это оно и есть? Т.е. переполнение памяти?
Больше похоже на переполнение буфера "сетевой карты". По крайней мере я на enc28j60 увеличивал именно сетевой буфер до 1500 байт. Тут надо либо лезть в теорию и разбираться - умеет ли нынешняя реинкарнация сетевого адаптера слать страницу в нескольких сетевых пакетах (chunked packet), либо ограничиться объемом html кода одного tcp пакета: 1470 байт при общем объеме буфера в 1500 байт.
О! Просветите пожалуйста за увеличение буфера. Где крутить? у меня именно то самое - http://www.ebay.com/itm/Ethernet-Shield-Arduino-Nano-work-ENC28J60-RJ45-Webserver-/281006055332
О! Просветите пожалуйста за увеличение буфера. Где крутить?
У меня в примере была строчка с BUFFER_SIZE 500, я его просто увеличил... Шилд у меня чуть другой, от EKitsZone.
Вы какой библиотекой пользуетесь?
#include "etherShield.h"
#include "ETHER_28J60.h"
Этими?
До каких пределов можно увеличивать буфера? (Нашел. Для этой микрухи _общий буфер_ - 8кб)
Вы какой библиотекой пользуетесь?
#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
До каких пределов можно увеличивать буфера?
Надо читать теорию про MTU. :) В сети 10/100 мегабит - максимальный размер tcp пакета - 1500 байт, за минусом технических расходов - полезного в нем получается 1472 байта. Но это верхний предел - пакеты бывают и меньше.
Вообще удивительно разные библиотеки и код для них.
Оцените код Веб Сервера -
Вот один -
#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); }Я бы покопал в части вот этой функции, потому, что в той реализации, что у меня (это Ваш верхний пример) - код требует, чтобы в печать шли данные типа int, а на выходе у Вас там может быть бог знает что.
Что тут можно сделать? Например разделить операции чтения и вывода на печать. Между ними вывести в serial отладочные сообщения со значениями digitalRead. Тем самым Вы поймете - на чем вешается. Либо вообще на время закомментировать вывод на веб-сервер и дать железке время покрутиться: может она зависает просто по причине просадки напряжения питания... В любом случае полезно втыкать отладочный вывод в разных местах кода для проверки состояния.
Извините за мою ошибку. эта строчка осталась от попыток понять причину. ее вообще быть не должно.
Но тут даже и до этого не доходит т.к если закоментить строки , начиная с нее же, т.е. оставить сверху только html создания таблицы для Digital, то виснет прям на этой строке.
e.print("<tr><td>");вот прям на ней.Провел тупейшее исследование.
Результаты удивили
Вот код -
// 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); }ДуиноВебСервер вешается при выводе четверок.
Провел тупейшее исследование. Результаты удивили ДуиноВебСервер вешается при выводе четверок.
А если закомментировать все предыдущие строчки, кроме "четверок"? :)
Вообще, я бы уже давно попробовал вторую библиотеку на работоспособность, т.к., несмотря на вся ее монтсрообразность - она рабочая.
Монстр даже с единичками не работает при буфере 500 и строковом 22.
Если я ставлю буфер 1500 и строковый 80, то добирается до семерок и на восьмерках дохнет.
Монстр даже с единичками не работает при буфере 500 и строковом 22.
Если я ставлю буфер 1500 и строковый 80, то добирается до семерок и на восьмерках дохнет.
Значит chunked не умеет и надо добровольно ограничиться размером странички в 1400 байт, либо дописать нужный функционал. Хотя, сейчас цена за wiz5100 уже упала и есть смысл покупать его - там, по слухам, нижний уровень tcp реализован в самом чипе.
Короче ВСЕМ!
Разобрался с "простой" библиотекой
#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); }Так какой у Вас, в результате, получился максимальный объем странички? Ее нужно сохранить из браузера в файл и посмотреть объем этого файла в проводнике.
Так какой у Вас, в результате, получился максимальный объем странички? Ее нужно сохранить из браузера в файл и посмотреть объем этого файла в проводнике.
есть вопрос :
http://arduino.ru/forum/apparatnye-voprosy/chto-primenit-kak-http-server
Всем доброго времени суток!
Столкнулся с той же самой проблемой...
Насколько я понимаю, можно разбить отправку 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?