Странное поведение Ethernet shield. Помогите разобраться.

Vadim111
Offline
Зарегистрирован: 14.01.2015

Задача: управлять роботом через Ethernet.

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

Если моя страница относительно небольшая (4 радиокнопки, пара текстовых полей), то все работает идеально. Запросы быстро отправляются, обрабатываются и страница быстро перегружается. Если же я увеличиваю количество радиокнопок, к примеру, до 15 то проект не работает вовсе - шилд даже не пингуется. Опытным путем установил, что если количество радиокнопок 8 шт, то шилд работает с большими тормозами, при пинге пропадает большая часть пакетов. При этом, в любом варианте общий размер скетча не превышает 11 кБ (из 32 возможных), а добавление описания одной радиокнопки занимает считанные быйты.

Не могу понять, что является причиной той "границы" которая делит скетч на рабочий (с 5 кнопками) и нерабочий (с 15 кнопками).  Поделитесь идеями, в чем может быть проблема.

В дополнение:

1. Пробовал отображать не радиокнопки, а другие элементы управления. Заметил, что при определенном "критическом" количестве любых полей, кнопок и пр. начинаются тормоза и прекращение работы.

2. Ethernet shield - w5100. Arduino UNO - не оригинал, но на скетчах из других областей в глюках замечен не был.

3. На всякий случай привожу код скетча. Сам скетч - стандартный пример. Отображение страницы вынесено в отдельную функцию.

#include <SPI.h>
#include <Ethernet.h>

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80

void setup()
{
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
}

void loop()
{
    EthernetClient client = server.available();  // try to get client

    if (client) {  // got client?
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) {   // client data available to read
                char c = client.read(); // read 1 byte (character) from client
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                    printPage(client);
                    break;
                }
                // every line of text received from the client ends with \r\n
                if (c == '\n') {
                    // last character on line of received text
                    // starting new line with next character read
                    currentLineIsBlank = true;
                } 
                else if (c != '\r') {
                    // a text character was received from client
                    currentLineIsBlank = false;
                }
            } // end if (client.available())
        } // end while (client.connected())
        delay(1);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)
}

//----------------------------------Print Page-------------------------------------
void printPage(EthernetClient client)
{
  // send a standard http response header
                    client.println("HTTP/1.1 200 OK");
                    client.println("Content-Type: text/html");
                    client.println("Connection: close");
                    client.println();
                    // send web page
                    client.println("<!DOCTYPE html>");
                    client.println("<html>");
                      client.println("<head>");
                      client.println("<title>Robot control</title>");
                      client.println("</head>");
                      client.println("<body>");
                      //-----Main Table-------
                       client.println("<table border='1'>");
                         client.println("<caption>Robot control</caption>");
                         client.println("<tr>");
                           //--Move--------------------------------
                           client.println("<td>");
                             client.println("<form action='192.168.1.177'>");
                               client.println("<table>");
                                 client.println("<tr>");
                                   client.println("<td>");
                                     client.println("<input type='radio' name='position' value='AA'>");
                                     client.println("<input type='radio' name='position' value='AB'>");
                                     client.println("<input type='radio' name='position' value='AC'>");
                                     client.println("<input type='radio' name='position' value='AD'>");
                                     client.println("<input type='radio' name='position' value='AE'>");
                                     client.println("<input type='radio' name='position' value='AF'>");
                                     client.println("<input type='radio' name='position' value='AG'>");
                                     client.println("<input type='radio' name='position' value='AH'>");
                                     client.println("<input type='radio' name='position' value='AI'>");
                                     client.println("<input type='radio' name='position' value='AJ'>");
                                     client.println("<input type='radio' name='position' value='AK'>");
                                     client.println("<input type='radio' name='position' value='AL'>");
//                                     client.println("<input type='radio' name='position' value='AM'>");
//                                     client.println("<input type='radio' name='position' value='AN'>");
//                                     client.println("<input type='radio' name='position' value='AO'>");
//                                     client.println("<input type='radio' name='position' value='AP'>");
                                   client.println("</td>");
                                 client.println("</tr>");
                                 client.println("<tr align='center'><td><input type='submit' value='Move'></td></tr>");
                               client.println("</table>");
                             client.println("</form>");
                           client.println("</td>");
                           //--Right----------------------------------
                           client.println("<td>");
                             client.println("<form action='192.168.1.177'>");
                               client.println("<table bgcolor='Pink'>");
                                 client.println("<tr><td align='center'><input type='radio' name='right' value='IA'></td></tr>");
                                 client.println("<tr><td><input type='text' name='rightValue' size='4' value='1'></td></tr>");
                                 client.println("<tr><td align='center'><input type='radio' name='right' value='IB'></td></tr>");
                                 client.println("<tr align='center'><td><input type='submit' value='Right'></td></tr>");
                               client.println("</table>");
                             client.println("</form>");
                           client.println("</td>");
                           //--Left------------------------------------------
                           client.println("<td>");
                             client.println("<form action='192.168.1.177'>");
                               client.println("<table bgcolor='Aquamarine'>");
                                 client.println("<tr><td align='center'><input type='radio' name='left' value='JA'></td></tr>");
                                 client.println("<tr><td><input type='text' name='leftValue' size='6' value='10'></td></tr>");
                                 client.println("<tr><td align='center'><input type='radio' name='left' value='JB'></td></tr>");
                                 client.println("<tr align='center'><td><input type='submit' value='Left'></td></tr>");
                               client.println("</table>");
                             client.println("</form>");
                           client.println("</td>");
                           //--Vkl------------------------------------------
                           client.println("<td>");
                             client.println("<form action='192.168.1.177'>");
                               client.println("<table bgcolor='PeachPuff'>");
                                 client.println("<tr><td><input type='text' name='vklValue' size='3' value='10'></td></tr>");
                                 client.println("<tr align='center'><td><input type='submit' value='Vkl'></td></tr>");
                               client.println("</table>");
                             client.println("</form>");
                           client.println("</td>");
                         client.println("</tr>");
                       client.println("</table>");
                       //-----Main Table End-------
                      client.println("</body>");
                    client.println("</html>");
}

 

e3p7j
Offline
Зарегистрирован: 09.01.2015

Возможно ОЗУ заканчивается.  Поищите в инете, где-то была функция определения свободной памяти. Если не найдете - вечером скину

pastry777
Offline
Зарегистрирован: 16.01.2014

100% не хватает оперативки...Уно очень слаба в этом отношении...про то как узнать сколько свободной памяти осталось есть на этом форуме,я лично в одной ищ тем писал,поищите...

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

Вам нужен UDP сервер а не веб ... А на телефоне приложение которое с ним общается. Я вот не понимаю зачем заставлять маленький микроконтроллер ворочать вебстраницы. В крайнем случае используйте F("") макрос чтобы статичные строки не жрали оперативку.

inspiritus
Offline
Зарегистрирован: 17.12.2012

проверил Ваш код на меге2560, все летает, даже с 25ю радиобутонами.

Vadim111
Offline
Зарегистрирован: 14.01.2015

Решил пойти немного нестандартным, но довольно простым путем. Возможно, это будет кому-то интересно.

На C# нарисовал очень красивую форму ("с бантиками и рюшечками") в которой предусмотрены все функции управления роботом, все настройки и т.п. Никаких ограничений на сложность этой формы нет. Сама форма - это обычный exe-шник, который запускается независимо от подключения шилда и может быть скомпилирован как по Виндовс, так и под Линукс. Любое событие на этой форме, например изменение ползунка или нажатие кнопки формирует GET запрос к указанному адресу, где расположен шилд. Для формирования запросов используются стандартные библиотеки C# где вообще не надо думать. Запрос очень маленький и короткий - одна строка с 3-4 параметрами (стандартный вид html запроса). В ответ на этот запрос Ethernet шилд шлет очень короткую информацию, оформленную в виде html страницы - всего одну строку в которой записаны состояния всех переменных. Форма принимает ответ, вытаскивает из него все необходимые велечины и отображает их на форме так, как это удобно. Проверил экспериментально на простых командах - все работает.

Плюсы такого подхода - это: 1. очень маленькие пакеты обмена между компом оператора и шилдом. 2. В скетче к шилду не надо формировать сложные html страницы, код которых выглядит нестандартно и труден для поиска ошибок. 3. В разы снижается нагрузка на Ардуину, поскольку формирование строки в 10-20 символов требует мизерных ресурсов.

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

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

inspiritus
Offline
Зарегистрирован: 17.12.2012

Заинтриговали... Код в студию :)

brokly
brokly аватар
Онлайн
Зарегистрирован: 08.02.2014

Ну что за костыли !!! Используйте SD карту на шилде для хранения веб страничек и аякс для быстрой работы. Ну и стринги, притаком количестве складывать в озу, как вы это делаете, вообще нонсенс.

pastry777
Offline
Зарегистрирован: 16.01.2014

подскажите пожалуйста,как работать со стринг в сд карте,чтоб не задействовать ОЗУ??как к примеру вынуть подстроку не задействуя ОЗУ?для меня эта тема актуальна,вы дали мне надежду,но я так и не понял как это сделать...причем,как я понимаю,не прокатит записать стринг в файл,поработать с ним и очистить перед следующим использованием,так сд карта быстро затрется...нужно набивать файл строками и лишь иногда(по времени или наполняемости)его очищать....как считать последнюю строку с конца файла тоже не догадался,хотя описание библиотеки СД только перечитал...спасибо..

Vadim111
Offline
Зарегистрирован: 14.01.2015

Само оформление формы написано на WPF, а обработка событий контролов - C#. Это стандартный пакет разработки приложений на С#, предложенный Вижуал Студией. Что выложить? И WPF, и C#? Или только конечный рабочий exe-шник?

Vadim111
Offline
Зарегистрирован: 14.01.2015

brokly пишет:

Ну что за костыли !!! Используйте SD карту на шилде для хранения веб страничек и аякс для быстрой работы. Ну и стринги, притаком количестве складывать в озу, как вы это делаете, вообще нонсенс.

Вообще-то мне кажется, что Аякс здесь не поможет. По меньшей мере один раз страницу надо загрузить полностью. Если эта операция переполняет ОЗУ, то ничего не выйдет. При большой странице (т.е. переполнении ОЗУ) шилд даже перестает пинговаться.

На счет хранения на карточке статических страниц я разобрался. А вот как динамически изменять эту страницу в зависимости от действий оператора и состояния переменных для меня осталось вопросом. Ее опять-таки надо формировать в ОЗУ Ардуины.

К тому же я бы не назвал такой подход "костылями". Написание запросов к ВЭБ серверу оказывается очень распространенная процедура у программеров C#, а сама библиотека входит в стандартный набор NET.  Почему бы не воспользоваться тем, что давно и успешно используется?

Присоединяюсь к вопросу о складывании стрингов в ОЗУ. Было бы интересно узнать о каком-нибудь другом варианте.

brokly
brokly аватар
Онлайн
Зарегистрирован: 08.02.2014

Динамически изменять АЯКСОМ !!! Вы просто не понимаете, что это такое. Поэтому вам кажется, что он не поможет. А костыли это экзешник, который вы приклеиваете к своему изделию. Вынуждая пользователя таскать с собой это никчемный файл. Со стрингами все еще проще чем с аяксом . А в библиотеке SdFat вообще есть PgmPrint() и PgmPrintln() . 

#define PgmPrint(s) _PgmPrint_P(PSTR(s)) 

 void _PgmPrint_P(const char* str) {  
    char buff_str[64];  
    strcpy_P(buff_str,str);  
    Serial.print(buff_str);
 } 


loop(){
   ...
   PgmPrint("blablabla AJAX");
   ...
}

 

std
Offline
Зарегистрирован: 05.01.2012

Vadim111

По поводу преобразователя протокола (FYI, ваша программа относится именно к этому типу), мудрый Мыщъх как-то сказал: "Вообще (...), собрать нечто, запускающееся не только на машине программера, но и на других - задача нетривиальная". У меня, к примеру, нет C# runtime, а посему софтина не запустится.

По поводу динамического обновления.

<script language=javascript>
var intervalID=setInterval(function(){
  // your code here
}, 5000); // interval in ms
</script>

Известно по крайней мере с декабря 1995 года. Или:

<meta name=refresh content=5><!-- interval in sec --->

Известно с 1997 года. AJAX да, кажется цугундером, но главное найти хороший справочник по нему. Коих в сети как голубиного говна на памятниках, не меньше.

Тем не менее, есть вопросы и у меня, all. Всё равно страница не будет меньше 3 Кб. Ну и как её отдать с флешки? Пришёл запрос, отдаём заголовок, потом говорим ENC28J60 - "ща подожди, файл с флешки прочитаю" и делаем на CS единицу. ENC28J60 думает что сеанс закончен и закрывает соединение. КАК? Иметь два физических SPI? То есть только в Mega, а остальные отдыхают? Или программный поднимать? Или из EEPROM читать? (как вариант - внешней). Вот честно, как это было для меня тайной, так и осталось.

brokly
brokly аватар
Онлайн
Зарегистрирован: 08.02.2014

А c чего вы решили, что при снятии чипселекта ваша вафля решит, что пока заканчивать связь !? Если это происходит так, то значит драйвера написаны криво.

std
Offline
Зарегистрирован: 05.01.2012

Ок, покрутим разные библиотеки, результаты сюда.

alexnik100
Offline
Зарегистрирован: 21.12.2015

Добрый день, Vadim111!

Руковожу радиокружком для ребятишек и инвалидов. Делаем робота для передвижения инвалидной коляски по ступенькам. 8 ног для устойчивости. Всего получилось 10 моторов. Надо 20 кнопок: вперед, назад, вверх и вниз.

Тоже проектируем управление по Интернету, чтобы можно было дистанционно проводить диагностику и закачку программ.

Как я понял у Вас большое количество кнопок на веб-страничке.

Пожалуйста,  позвольте использовать Ваше оформление веб-странички и скетч для ARDUINO.

Может подойдет для нашего случая. Подскажите где можно скачать.

Заранее спасибо.

С уважением, alexnik100