Показаны сообщения с ярлыком Ethernet. Показать все сообщения
Показаны сообщения с ярлыком Ethernet. Показать все сообщения

30.03.2014

А все-таки она вертится!

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



07.05.2013

Web-термометр

"Погодная станция своими руками" - вот как можно было бы назвать статью, но я решил пока просто рассказать о том, как соорудить web-термометр, подключить его к домашней LAN  и наблюдать показания через браузер ;)

Для этого нам потребуется Arduino-совместимая плата, поддержка Ethernet и несколько температурных датчиков.


14.02.2012

Проблемы Ethernet в ArduinoIDE 1.0

Только что столкнулся с проблемами использования Ethernet Shield на W5100 в рамках ArduinoIDE 1.0. Великолепно до этого работавшие скетчи решительно отказались работать, что меня привело в панику слегка озадачило.

Формально, чтобы перенести скетч со "старых" ArduinoIDE 0022/0023 под 1.0, надо исправить названия классов "Server" и "Client" на "EthernetServer" и "EthernetClient" соответственно. Идеологически, классы Server и Client теперь сделаны полностью абстрактными и перенесены в ядро Arduino. И поскольку они не имеют ни одной реальной функции, библиотека должна  породить от них свой класс, "наполнив" его реальным содержимым. Думаю, делается это с прицелом на будущее, но предсказывать его пока не берусь ;)

К сожалению, даже после успешной компиляции, надо обязательно залить скетч и проверить его работу. Скорее всего, проблемы (если они действительно есть) вскроются сразу же - речь идет о неверном обмене с  Ethernet-чипом Wiznet W5100. Первым делом, открывайте файл arduino-1.0/libraries/Ethernet/utility/w5100.h и ищите в нем строки:

#define __SOCKET_REGISTER16(name, address)                   \
  static void write##name(SOCKET _s, uint16_t _data) {       \
    writeSn(_s, address,   _data >> 8);                      \
    writeSn(_s, address+1, _data & 0xFF);                    \
  }                                                          \
  static uint16_t read##name(SOCKET _s) {                    \
    uint16_t res = readSn(_s, address);                      \
    res = (res << 8) + readSn(_s, address + 1);              \
    return res;                                              \
  }

Вместо них поставьте полностью аналогичные (но, удивительным образом - работающие):

#define __SOCKET_REGISTER16(name, address)                   \
  static void write##name(SOCKET _s, uint16_t _data) {       \
    writeSn(_s, address,   _data >> 8);                      \
    writeSn(_s, address+1, _data & 0xFF);                    \
  }                                                          \
  static uint16_t read##name(SOCKET _s) {                    \
    uint16_t res = readSn(_s, address);                      \
    uint16_t res2 = readSn(_s,address + 1);                     \
    res = res << 8;                                             \
    res2 = res2 & 0xFF;                                         \
    res = res | res2;                                           \
    return res;                                              \
  }

(это исправление взято отсюда: http://code.google.com/p/arduino/issues/detail?id=605)

В большинстве случаев это помогает; мало того - говорят, что некоторые PPA в Ubuntu уже содержат пакет "arduino" с этим патчем, но давать на них ссылки не буду - я не понимаю смысл создания пакета для программы, которая великолепно работает, будучи распакована в одну папку. Гораздо удобнее качать и ставить дистро непосредственно с arduino.cc, не мучаясь вопросами "а чой-то они там наменяли-то?!"

Тем, у кого после вышеописанного патча все равно не заработает - рекомендую посмотреть на версию кросс-компилятора gcc-avr в составе вашего дистрибутива Linux. Если это, например, 4.5.3 - причина может быть в этом, и стоит позаботиться об откате на 4.3.2 или создании собственного окружения. Возможны разные варианты действий, но я пока выбрал наиболее тупой простой - развернул в виртуальную машину под Windows дистрибутив 0023 и временно работаю в нем. И вообще - пользователям Windows в этом плане проще, поскольку хоть их дистрибутив ArduinoIDE и выглядит чрезмерно "пухленьким", в него уже включен порт avrgcc - WinAVR, что драматически повышает стабильность по сравнению с Linux, где пакет gcc-avr "приходит" вместе с дистрибутивом ОС.

Тем, кто уже начал использовать включенные в состав ArduinoIDE библиотеки DHCP, стоит обратить внимание на другой патч: http://code.google.com/p/arduino/issues/detail?id=742

Сейчас при формировании DHCP-запроса, код из библиотеки Ethernet не совсем корректно формирует уникальное имя хоста, генерируя псевдослучайный суффикс в том числе и из "непечатных" символов: это может создавать потенциальные проблемы при работе DNS.


25.10.2011

Новости Freeduinoстроения

Пришла пробная партия Freeduino EtherSD Shield 1.0.1:

Изменения весьма незначительные, хотя началось всё с этого сообщения. Я провел тщательное и беспощадное тестирование при помощи подушки - несчастный модуль беспрерывно принимал и передавал пакеты в течение двух часов в условиях подавленной вентиляции, однако каких-либо сбоев выявлено не было. Таким образом, нагрев W5100 в процессе работы (на который обычно жалуются те, кто впервые сталкиваются с Ethernet-шилдами ), является вполне нормальным.

А нагрев, кстати, зависит от потребляемого тока. Пробежавшись по подключением всех ног W5100 в схеме Arduino Ethernet Shield 05, я заметил, что "скопипастил" из нее составной подтягивающий резистор R24+R28 на линиях CS/WR/RD, что в итоге дает 1К+2К2 = 3К2. Авторы просто использовали свободные резисторы в счетверенных микросборках: экономили таким образом место и детали - по крайней мере, надеюсь, фэн-шуй тут не причем ;) Кроме того, в разъеме Ethernet были светодиоды со встроенными токоограничительными резисторами 270 Ом. 

Соответственно, в новой версии подтягивающий резистор стал 10К, а токоограничители - 470 Ом. Согласно последним веяниям Arduino Ethernet Shield 06 снесен светодиод-анахроизм COLL и удалено место под монтаж full SD-карты - а вместе с ней и резисторы-подтяжки линий DETECT и WRITE_PROTECT (зря я волновался на счет совместимости).




Ряды плат-протошилдов пополнил Nano протошилд:
До сих пор считал, что это совершенно бесполезная вещь - все-таки, Nano задуман в первую очередь для беспаечной макетки. Однако, практика показала, что когда схема переходит не следующую стадию и требуется применить секретное оружие паяльник, то нормальный разъем внешнего питания, разведенный USART и I2C в комплекте с макетным полем и условными посадочными местами для светодиодов и кнопок - не так уж и плохо ;)   

15.07.2011

Arduino Ethernet

Семейство Arduino пополнилось новой платой, которую давно уже ждали - Arduino Ethernet:


Результат, который мы видим - это скрещивание платы Arduino Pro и Ethernet Shield. Иными словами - плата приобрела разъем Ethernet, но потеряла USB. Загрузка при этом по-прежнему предполагается через UART, так что потребуется внешний USB-TTL конвертер

В качестве бонуса - возможность дистанционного питания по технологии PoE - IEEE802.3af, но модуль надо покупать отдельно.




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

Во-первых, на фото строго сверху этого не поймешь, но... стоит повернуть плату в профиль - и  становится ясно, что разъем RJ45 выше, чем USB-B, и подключить шилд-плату, увы, будет непросто. Таким образом, показатель "Shield-friendly" значительно понижается.


Есть такие специальные "длинноногие" нестандартные разъемы, позволяющие увеличить зазор между стыкуемыми платами на 3-5 мм, с небольшим ущербом для жесткости конструкции (на практике я встречал лишь однажды - на оригинальной Arduino Ethernet Shiel). Но большинство производителей ими не пользуется, достать самому - практически нереально.


Во-вторых, по вышеперечисленным причинам - с одетым модулем PoE вообще можно смело забыть  про шилды. Кстати, имейте ввиду, что 48VDC - это не шутка. Хотя в питающих устройствах делают всё возможное и невозможное, чтобы защитить устройства и человека (мониторинг и ограничения по току), мгновенно это не сработает! Модуль PoE явно предназначен для работы в закрытом корпусе, а не верхом на подобной железке.

В-третьих, нет самого интересного - загрузки скетчей через Ethernet. Понимаю, что тут возникают вопросы с безопасностью, установлением соединения, идентификацией и авторизацией - но, видимо, ни у кого руки пока не дошли до их проработки. Но с ними плата была бы на порядок интереснее ;)

Вместе с тем, в отношении Ethernet у команды Arduino наметился некий прогресс - быть может, скоро ждет что-то новое и интересное ;)

20.05.2011

Новости Arduinoстроения

В комментах к статье о Freeduino One пользователь Сергей справедливо обращает внимание на лишние элементы в схемах тактирования, добавленные в Uno и Mega 2560:


Речь про R1 1М и R3 27Ом. Дело в том, что согласно родному ATMEL-овскому даташиту "AVR042: AVR Hardware Design Considerations", эти элементы и так уже есть внутри МК, причем R3 нужен для отдельно взятого случая - если используется резонатор 32758 кГц:



Почему итальянские модельеры авторы так сделали - остается только гадать, и меньше всего верю в то, что они не изучали этот даташит (желающие могут принять участие в народном гадании в комментах ;) По одной из версий очередная партия МК ATMEGA и резонаторов 16 МГц не захотела жить вместе, в результате чего доблестная итальянская фирма tinkerIt была вынуждена заниматься подбором дополнительной внешней обвязки. 

Замечу, что именно начиная с Arduino Uno был применен ATmega8u2 и миниатюрный керамический резонатор 16МГц - раньше такого у итальянцев не было (только в Nano v.3, но ее выпускала американская Gravitech).

Что же в итоге? Один из побочных эффектов такого решения - надежное, но зато несколько замедленное возбуждение резонатора после подачи питания, что для домашних Arduino-поделок вроде как вполне приемлемо. Я вот, например, вообще не заметил никаких побочных эффектов - Freeduino ONE работает безупречно.

Но, как говорится, время шло. Вслед за появлением знаменитой Arduino UNO на ATmega328 в DIP, появилась версия на SMD:


Объяснили это очень просто: производство ATMEL не справляется, возник дефицит чипов в DIP-корпусе. Охотно верю - в России SMD-версию достать проще и дешевле. Но если уж на то пошло - в корпусе TQFP32 ножек больше, и появляется целых два лишних входа (digital I/O или АЦП), что является просто находкой для тех, кому надо иметь чуть больше пинов и из-за этого приходится использовать MEGA. Но, увы - идея вывести их на какие-нибудь дополнительные контактные площадки или дырки авторов не посетила :(

Спустя некоторое время появилась и вторая версия Arduino UNO, схему в PDF можно скачать на все той же страничке.

Что изменилось в UNO rev.2

  • теперь уже окончательно керамический резонатор закреплен за основным МК, а нормальный кварц - за ATmega8U2 (альтернативных посадочных мест для вариантов нет);
  • токоограничительный резистор 27 Ом в схеме тактирования - убрали! Зато параллельный 1М - оставили. Видимо, эксперименты продолжаются;
  • линия сброса DTR притянута к земле резистором 1K, который "оторвали" от светодиода L (там было два и один явно лишний - поскольку он является частью микросборки из четырех штук - авторы просто решили пристроить его к делу ;) Получился как бы классический фильтр высоких частот, и смысл этого решения человечеству еще предстоит осознать ;) 
  • четыре ранее неиспользованных вывода ATmega8u2 - PB7, PB6, PB5 и PB4 выведены под вилку 2x2, что-то мне это напомнило о пропавшей X3 для bitbang-а - возможно, скоро нас ждет новая прошивка, которая будет уметь изображать что-то подобное;
  • операционный усилитель LM358 теперь может быть установлен не только в классическом корпусе SO-8, но и в SSOP - миниатюризация продолжается!
Изменения коснулись и EthernetSD shield, появилась rev.06 - и одно изменение там, прямо скажем - революционное: плата подготавливается к поддержке PoE (стандарт IEEE 802.3af). Это такая технология, которая позволяет дистанционно питать устройство по линии Ethernet, для чего в природе существуют специальные PoE-инжекторы и "питающие" коммутаторы. Поддержка требуется также и на стороне самого устройства - для Ethernet Shield это будет специальный PoE-модуль, отделяющий питающее напряжение (+48В DC) от сигнала витой пары перед тем, как подать его на интерфейсную микросхему W5100. 



Arduino с таким шилдом и модулем PoE может питаться дистанционно - теоретически, можно отдалиться по кабелю от источника до 100 метров. Сразу оговорюсь - это решение промышленное (используем по работе в своих аппаратных решениях с 2001 года). Кроме того, Ethernet-shield на чипе W5100 с PoE давно уже существует - разработан компанией Freetronics.


Всё остальное - мелкие изменения:

  • микросхема-сборка триггеров Шмидта в схеме сброса W5100 заменена на один маленький триггер в корпусе SOT23-5 - логично, занимает меньше места, нет избыточности;
  • окончательно отказано в праве на монтаж полноразмерному SD-слоту - по сути, он всё это время просто занимал лишнее место; 
  • резистивные делители для коммуникации с SD-картой заменены на буферные микросхемы 74LVC1G125DBV, что тоже логично - работать они будут стабильнее, потреблять - меньше;
  • исключен из схемы светодиод COLL, который должен был показывать коллизии на интерфейсе Ethernet. В защиту такого решения говорит то, что зажигаться он может при подключении через хаб, но где теперь можно найти Ethernet-хаб?!
  • светодиод FULLD вынесен за пределы платы. То есть он как суслик из ДМБ - как бы есть, но на самом деле - его нет.
Последний пункт тем более загадочен тем, что на реальной плате на фото светодиода FULLD нет, а в CAD-файлах он "завис" ни к чему не подключенный. Да и сами brd-файлы с разводкой содержат две воздушные перемычки - недвусмысленный намек на незавершенность и наличие ошибок. В этой связи, конечно, напрашивается вопрос о том, не выйдет ли вскоре еще одна версия. Ну, поживем - увидим ;)

23.04.2011

Arduino + Ethernet + mircoSD

После выхода новой Arduino Ethernet Shield 05 прошло довольно много времени, но недавно  мне случилось снова обратиться к этой теме, изучая возможности платы Freeduino EthernetSD Shield:




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

Например - каким образом новая плата оказалась совместима с Mega?

Напомню историю проблемы: в Arduino обмен с Ethernet-чипом W5100 происходит по шине SPI, и сначала сигналы на Arduino Ethernet Shield были разведены прямо от колодки цифровых входов 11,12 и 13, к которым и был привязан аппаратный SPI на ATmega168/328. Но после триумфального выхода Arduino Mega все вдруг с опозданием спохватились, что аппаратный SPI "переехал" на 50,51 и 52 - и это стало причиной несовместимости MEGA не только с EthernetShield, но и вообще со всеми шилдами, использовавшими аппаратный SPI.

Именно в результате этого недоразумения SPI теперь принято разводить с колодки ICSP, которая, к огромному счастью, сохранила свое позиционное расположение во всех оригинальных платах Arduino. Заглянем на обратную сторону EthernetSD шилда:



Таким образом, SPI-сигналы MISO, MOSI и SCK будут всегда правильными:
  • в обычных (не-MEGA) платах будут использоваться digital 11,12,13;
  • в Mega и Mega2560 - 50,51,52.
Единственный сигнал, которого нет на вилке ICSP - это SS (Slave Select). Низким уровнем он выбирает ведомое устройство на шине SPI, с которым будет общаться ведущее (в нашем случае - ATmega):
  • для выбора Ethernet используется digital 10;
  • для выбора SD-карты используется digital 4.
Соответственно, использовать digital 10 и 4 в схемах с EthernetSD Shield для какого-то другого применения - нельзя.


Ради интереса можете сравнить решение по доступу к SD с вариантом от libellium, котрое я разбирал в статье MicroSD Shield (в основе - тот же резистивный делитель для согласования уровней сигналов). В старой версии Ethernet Shield разъем SD никогда не напаивался (лишь гордо блестели залуженные площадки из-под маски, напоминая о былых ошибках и вселяя ложную надежду в сердца тех, кто рассчитывал напаять разъем и получить какие-то дополнительные функции) - то ли из-за отсутствия согласования сигналов по уровню 3,3В, а может - из-за отсутствия программной поддержки. Да и сигналы эти были разведены на другие пины. Но теперь SD и Ethernet физически разделяют одну шину SPI, одинаково используя преимущества аппаратной поддержки в ATmega.

Отсюда, кстати, вытекает второй нюанс: в случае необходимости работать и с SD-картой, и с Ethernet, между ними придется попеременно переключаться. В принципе, это вполне возможно, особенно в режиме TCP - если данные все-таки потеряются по приему, TCP перезапросит пропущенный фрагмент и автоматически уменьшит окно. Ну, по крайней мере, мне в это хочется верить ;) 

На оригинальной схеме Arduino Ethernet Shield 05 сигналы детекта карты и защиты от записи почему-то оказались не разведены на разъем microSD, но сохранены для полноразмерного SD-разъема (они даже имеют подтяжки 10К к Vcc). Я некоторое время колебался - но в итоге оставил эту странность в покое ради полной совместимости. Ведь представьте себе - установка/изъятие SD-карты влечет изменение уровня на analog0 и автоматически приводит к невозможности его использования для других целей.

Авторы по прежнему сохраняют разорванную перемычку INT, которая может соединить выход прерывания W5100 c digital2 и использовать для ввода/вывода механизм прерываний. Однако, поддержка прерываний с W5100 так до сих пор и не реализована: библиотека работает традиционным для Arduino методом пулинга (постоянного опроса состояния буфера из скетча).



Схема сброса с дополнительным супервизором и триггером Шмидта в придачу сразу сняла все непонятные глюки, которые у меня наблюдались с первой версией платы. Жить стало проще и веселее (с) ;)

Заслуживает нескольких слов и программная поддержка, над которой сообщество также изрядно потрудилось.

Когда-то я мучительно плодил классы для работы с UDP, в конечном итоге добиваясь возможности синхронизации времени по протоколу NTP. Но прогресс не стоит на месте и теперь UDP включен в библиотеку вEthernet в составе ArduinoIDE 0022, ничего изобретать не надо. Кроме того, энтузиастами написана поддержка следующих возможностей:

  • EthernetDHCP, позволяет получать IP-адрес динамически, поддерживается блокирующий и неблокирующий режим вызова функций; есть возможность обновления lease (и перехода на другой IP, если его поменяют "на лету"). Более, чем достаточно!
  • EthernerDNS, позволяет преобразовывать символические имена в реальные IP-адреса. Необходимо только знать IP-адрес своего DNS-сервера - например, взять из DHCP lease (см. выше);
  • EthernetBonjour,  поддержка протокола Bonjour/Zeroconf, реализующего MulticastDNS и DNS Service Discovery - при помощи него вы можете донести до остальных пользователей информацию о том, какие сервисы запущены на вашем Arduino (поклонники Apple быстрее остальных поймут, о чем речь ;)
  • WebDuino, библиотека с каркасом для построения веб-серверов на основе Arduino - включает в себя парсинг URL и прочие полезные функции. Почему-то напомнила CGI.pm ;)
  • RadiusClient, библиотека для поддержки авторизации по протоколу Radius (видимо, Diametr должен быть на подходе ;)
  • UdpNtpClient, пример из стандартной библиотеки, запрашивающий по NTP время и расшифровывающий ответ в виде ЧЧ:ММ:СС. Что я могу сказать... весьма грубо усеченный, но рабочий вариант.
Недавно мне задали вопрос: "Насколько сложно программировать для EthernetSD Shield?" В качестве демонстрации ответа, приведу решение задачи, которую один мой знакомый сформулировал так: "у меня есть компьютер с портом RS232, надо дать к нему доступ через Internet". При этом от предложений на основе PC+Linux он отказался в пользу Arduino+Ethernet Shield. Учтите, выставлять в public internet сервер Telnet - чистое безумие не совсем безопасно! Так что используйте на свой страх и риск:


//  Copyright (C) 2011 Ilya Danilov
//  http://mk90.blogspot.com
//
//  TelnetServer is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation, either version 3 of
//  the License, or (at your option) any later version.
//
//  TelnetServer is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  Illustrates how to make simple Telnet Server based on Ethernet Shield.

#if defined(ARDUINO) && ARDUINO > 18
#include <SPI.h>
#endif
#include <Ethernet.h>
#include <EthernetDHCP.h>
#include <SD.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEC };

#define MAXPASSLEN  16
#define READ_TIMEOUT_IN_MS 60000L
char enteredPassword[MAXPASSLEN];
char cardPassword[MAXPASSLEN];

Server server(23);

// Just a utility function to nicely format an IP address.
const char* ip_to_str(const uint8_t* ipAddr)
{
  static char buf[16];
  sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
  return buf;
}

void setup()
{
  pinMode(10, OUTPUT);
  pinMode(4, OUTPUT);
  digitalWrite(10, HIGH);
  digitalWrite(4, HIGH);
  
  SD.begin(4);
  digitalWrite(4, HIGH);
    
  Serial.begin(9600);
  
  Serial.println("Attempting to obtain a DHCP lease...");
  EthernetDHCP.begin(mac);

  const byte* ipAddr = EthernetDHCP.ipAddress();
  const byte* gatewayAddr = EthernetDHCP.gatewayIpAddress();
  const byte* dnsAddr = EthernetDHCP.dnsIpAddress();
  
  Serial.println("A DHCP lease has been obtained.");

  Serial.print("My IP address is ");
  Serial.println(ip_to_str(ipAddr));
  
  Serial.print("Gateway IP address is ");
  Serial.println(ip_to_str(gatewayAddr));
  
  Serial.print("DNS IP address is ");
  Serial.println(ip_to_str(dnsAddr));
  
  server.begin();
}


boolean authenticateClient(Client &client) {
  int i=0, k=0;
  unsigned long timeoutTime;
  
  delay(500);
  client.flush(); // kill telnet handshake junk
  memset(enteredPassword,0,sizeof(enteredPassword));
  
  for (i=0;i<3;i++) {
    client.println("Password:");
    k = 0;
    timeoutTime = millis() + READ_TIMEOUT_IN_MS;
    while ( (k<MAXPASSLEN) && client.connected() && (millis() < timeoutTime) ) {
      char ch = client.read();
      if (ch != -1) {
        if (ch == '\r') continue;
        if (ch == '\n') break;
        enteredPassword[k++] = ch;
      } else delay(10);
    }
    
    if (millis() >= timeoutTime) return false;  
    if (!client.connected()) return false;  
        
    // read password from SD
    if (enteredPassword[0]) {
      if (SD.exists("passwd")) {
        memset(cardPassword,0,sizeof(cardPassword));
        k = 0;
        File f = SD.open("passwd"); 
        while (f.available() && k<MAXPASSLEN ) {
          char ch = f.read();
          if (ch == '\r') continue;
          if (ch == '\n') break;
          cardPassword[k++] = ch;
        }
        f.close();
        digitalWrite(4, HIGH);
        
        // compare
        if (!strcmp(cardPassword,enteredPassword)) 
          return true;
        else {
          client.println("Password mistmatch, try again!"); 
        }
      } else {
        client.println("Password file not found\n");
      } 
    } 
  } 
  return false;
}

void loop()
{
  boolean authenticated = false;
  //EthernetDHCP.maintain();
  
  // listen for incoming clients
  Client client = server.available();
  if (client) {
    while (client.connected()) {
      if (!authenticated) {
        if (!authenticateClient(client)) {
          client.println("Authorization failed.");
          client.stop();
          break;
        } else authenticated = true;
      } else { //auth passed
        if (client.available()) {
          char c = client.read();
          Serial.print(c);
        }
        if (Serial.available() >0 ) {
          char c = Serial.read();
          client.print(c);
        }
      }
    }
  }
}



После старта Arduino получает адрес динамически и выводит его через Serial-порт, чтобы было понятно, на какой IP ломиться telnet-клиентом. По входящему соединению выдается запрос "Password:", полученная в ответ комбинация символов сравнивается с содержимым простого текстового файла passwd, который располагается в корне SD-карты (внутри файла просто пароль, не путать с /etc/passwd :).

И как водится во всех сказках, на ввод правильного пароля дается три попытки ;)

Если выкинуть авторизацию, скетч займет один экран. Сложно? Не думаю, хотя для серьезных задач потребуется изучение нюансов. В итоге: после компиляции скетч занял 23К в памяти, переключение между SD-картой и Ethernet работает нормально.

Как всегда, часть плат пока еще доступна здесь.

10.08.2010

Новая Arduino Ethernet shield

Готовится к выходу новая версия оригинальной Arduino Ethernet Shield:


Самое значимое изменение вы уже наверное заметили - добавлен слот microSD. И если раньше это был всего лишь декоративный элемент, то в грядущей версии ArduinoIDE 0019 он станет поддерживаться специальной sdfatlib, которая сейчас пока умеет только читать существующие файлы. Но, как говориться, лиха беда - начало!

Кроме этого, при подключении через ISCP-гребенку шилд будет совместим с Arduino MEGA, а также будет содержать дополнительный контроллер сброса, снимающий некоторые проблемы с работой Arduino+Shield сразу после подачи питания.

Кстати, ArduinoIDE 0019 выходит в конце недели. Редакция будет следить за развитием событий ;)

12.07.2009

Ethernet Shield по UDP (2)

В продолжение темы с UDP в Arduino - примеры и новая версия библиотеки.

В новой версии два изменения:

1. Если вы уже успели начать использовать предыдущую версию, то поменяйте обработку значения, возвращаемого ClientUDP::open(). Раньше успехом была единица, в случае неудачи - ноль. Теперь все наоборот ;) Если все успешно - будет ноль, в противном случае - это код ошибки.

Причина такого изменения тривиальна - в оригинальном Client.cpp довольно много мест, где стоит проверка и немедленный "молчаливый" возврат нуля: поди-ка разберись, почему open() "накрылся" - то ли сокетов свободных нет, то ли порт занят. Константы ошибок описаны в ClientUDP.h, быстродействие и размер кода не изменились.

2. Появилась публичная переменная uint16_t _sent, в которой сохраняется число переданных байт.

А вот это из-за специфики класса Print, от которого порожден оригинальный класс Client и мой "плагиатный" ClientUDP. Прототип write возвращает void, и чтобы иметь хоть какой-то шанс понять, отправилась дейтаграмма или нет, можно прочитать после ее вызова значение _sent. Расплата за это - два байта SRAM.

Скачать новую версию с примером (про который далее): ClientUDP_1.1.zip

Итак, для эксперимента надо взять пару Arduino:



...( это оригинальная Diecimila и Freeduino MaxSerial )...

Также понадобится две штуки Ethernet Shield:



...( это оригинальная Ethernet Shield и вариант от NKC Electronics )...

Следующий естественный ;) шаг - соединение их попарно:



...а также - в комплексе:



Платы работают в режиме разделения функций: к первой подключены три кнопки, она является передатчиком. Ко второй подключены светодиоды, и она - соответствено - является приемником. По нажатию или отпусканию кнопки формируется двухбайтовый UDP-пакет (номер кнопки + состояние), который отсылается приемнику. Приемник "декодирует" пакет, зажигая или гася соответствующий светодиод:



В качестве портов, к которым подключены и кнопки, и светодиоды, выбраны digital 7,6,5. Значение порта, ip-адресов и mac-адресов выбрано весьма произвольно, не ищите там скрытого смысла ;)

В процессе работы над материалом, мне показалось, что лучше один раз увидеть, чем 100 раз написать в блоге, родилась идея записать ролик. Его я и представляю на ваш скромный суд (заранее прошу прощения за славянский акцент ;)

25.06.2009

Ethernet Shield по UDP

К сожалению, библиотека Ethernet для Arduino заточена под работу в режиме TCP. Иногда этого явно недостаточно, особенно при таких-то богатых возможностях чипа Wiznet W5100 как встроенная поддержка UDP, ICMP, IGMP, ARP и даже PPPoE.

Лично мне для работы с NTP понадобился именно UDP, поэтому пришлось быстро написать класс, аналогичный Client в библиотеке Ethernet, но работающий по UDP. Недолго думая, я нарек его ClientUDP и поместил в каталог Ethernet. Все, что мне потребовалось - переработать код существующего класса Client, о чем я и расскажу ниже.

Для тех, кого не интересуют тонкости: ClientUDP_1.0.zip (распаковать в каталог arduino-0016\hradware\libraries).

Итак, что меняется при переходе с TCP на UDP? Грубо говоря – почти всё.

Начнем с того, что получать данные из соединения TCP можно побайтно (или блоками - нововведение Arduino 0016), в то время как в UDP - только пакетами:



Когда сокет находится в режиме UDP ( Sn_MR_UDP ), в буфере Wiznet вместе с данными сохраняется и заголовок пакета, который тоже надо уметь читать и правильно обрабатывать.

Работа с Ethernet в Arduino целиком построена на библиотеках socket.c и w5100.c, являющихся уровнями работы с сокетами и чипом соответственно. И если при чтении сокета TCP надо вызывать recv, то при чтении UDP - уже recvfrom.

Тут кроется еще одна неприятность, о которой надо помнить: длина буфера len, передаваемая в recvfrom, с длиной принятого пакета (получаемого из заголовка, см. выше) не сравнивается (вносить изменения в socket.c я пока не решился).

Интерфейс стандартного Client выглядит так:

 
#include "Print.h"

class Client : public Print {
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
uint16_t _port;
public:
Client(uint8_t);
Client(uint8_t *, uint16_t);
uint8_t status();
uint8_t connect();
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
int available();
int read();
void flush();
void stop();
uint8_t connected();
uint8_t operator==(int);
uint8_t operator!=(int);
operator bool();
friend class Server;
};




UDP - протокол без фазы установления соединения, поэтому часть функциональности connect выкидывается, а имя заменяется на open. Опциональный аргумент будет указывать нашему классу, как выбирать source-порт - на основе глобальной автоинкрементирующейся переменной (_srcport, объявлена в библиотеке), либо явно. В последнем случае, приложение полностью ответственно за то, чтобы у разных сокетов не случилось двух одинаковых source-портов.

Функция connected тоже смысла не имеет, но можно заменить opened.

Функция stop, закрывающая соединение, остается, но тоже упрощается.

Уходят в небытие операторы ==, !=, bool() - потому что в языке Processing аналогичного класса нет, равно как и нет объекта-сервера, который возвращает этот объект при подключении очередного Client-а (см. примеры в стандартной библиотеке Ethernet, чтобы понять, о чем речь).

Прототипы функций-членов записи write являются виртуальными и наследуются от класса Print. Соответственно, все операторы Print работают в конечном итоге через них, абстрагируясь таким образом от типа устройства вывода. В Arduino 0016 определили целых три варианта write:

1. вывод байта: virtual void write(uint8_t) = 0;
2. вывод строки: virtual void write(const char *str);
3. вывод буфера: virtual void write(const uint8_t *buffer, size_t size);

Первый метод надо перекрывать обязательно - он абстрактный; два остальных работают через него.

Выше я уже упоминал, что в UDP бесполезны байтовые операции ввода-вывода, но для совместимости, оставим на месте однобайтовый write - это будет посылка пакета из одного байта. Это надо обязательно учитывать при использовании print: печать byte и char будет порождать однобайтовые пакеты, println будет генерировать минимум два пакета, в каждом по одному символу '\r' и '\n'; та же ахинея будет происходить с распечатыванием целых и вещественных чисел - каждый символ будет передан отдельным пакетом.

Короче, если хотите сохранять ясность, используйте методы write напрямую, тем более, что они вполне публичные (public).

Процедуры чтения в классе Print нет, так что смело пишем свой собственный read.

Неизменной, пожалуй, останутся available, сообщающая размер приемного буфера, и status, читающая регистр состояния сокета (число состояний сокращается до двух: SOCK_CLOSED и SOCK_UDP).

Самая противная из всех - flush, которая очищает приемный буфер. Раньше она дергала однобайтовый read, пока available возвращала ненулевое значение. Теперь при вызове read надо забрать весь пакет целиком, не зная даже заранее его размер. Мое мнение – операцию сброса входного буфера надо выносить прямиком в socket.c, именно там ей и место. Поэтому, также исключаем ее из интерфейса.

Вот результат:

 
#include "Print.h"

class ClientUDP : public Print {
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
uint16_t _port;
public:
ClientUDP(uint8_t);
ClientUDP(uint8_t *, uint16_t);
uint8_t open(uint16_t);

uint8_t status();
virtual void write(uint8_t);
virtual void write(const uint8_t *, size_t);
uint16_t read(uint8_t *, uint16_t *, uint8_t *, uint16_t);
int available();
void stop();
uint8_t opened();
};



Если придираться, то, строго говоря, это не совсем Client, потому что сервер ему не нужен. Да и сам смысл этого класса нивелируется, если бы библиотека socket.c была доступна скетчам напрямую. Однако, на этом примере я наглядно демонстрирую, что при наличи исходного кода, здравой логики и желания, довольно просто можно получать необходимые вещи. В следующих статьях я обязательно покажу, как воспользоваться этой библиотекой.

24.04.2009

Хак Arduino Mega и Ethernet Shield

И действительно, Ethernet Shield не будет работать с Arduino Mega. Всё из-за того, что для общения с чипом W5100 используется аппаратный SPI, а он уехал в Mega на пины 50..53.

Но умные люди в этом разобрались: надо освободить нужные пины Ethernet Shield:



И подключить их в нужное место:



Коряво, конечно, но - работает. Придется еще поправить номера пинов в Ethernet Library, это подробно расписано на страничке автора хака (nkcelectronics):

http://mcukits.com/2009/04/06/arduino-ethernet-shield-mega-hack/

22.04.2009

19.01.2009

Ethernet Shield для Arduino (2)

На досуге немного покопался в схемотехническом решении этого Shield-а. На моем варианте Shield-а отсутствует разъем SD (не используется в Arduino):



W5100 подключен в режиме последовательного интерфейса (конечно же: экономим pin-ы).
  • MISO (27) - digital 12
  • MOSI (28) - digital 11
  • SS (29 или SCS) - digital 10
  • SCK (39 или SLCK) - digital 13, на нем же дублирующий светодиод L на Arduino
Если пользоваться разъемом SD, то получается, что:
  • CLK (5) - digital 9
  • DAT0 (7) - digital 3
  • DAT1 (8) - digital 4
  • DAT2 (9) - digital 5
  • DAT3 (1) - digital 6
  • CMD (2) - digital 8
  • WP - analog 0
  • DETECT - analog 1
Теперь по поводу "джамперов":


  • вверху - INT. это не джампер, а посадочное место для SMD резистора-нулевки. Он пропускает сигнал прерывания на digital 2;
  • внизу слева - PROG. это пустое место для 2-х пиновой гребенки. Судя по схеме, ее замыкание сажает ногу SEN (SErial Enable) на землю, запрещая таким образом обмен по последовательному интерфейсу для чипа W5100. Практического смысла в этом крайне мало, поскольку при этом должны использоваться шина данных и адреса (A0-A14, D0-D7), но они победно висят в воздухе.
  • внизу справа - WP - это еще одно место для нулевки. Сажает вывод "WRITE_PROTECT" (а вместе с ним и analog 0) на землю.
Сам по себе W5100 держит все-таки 4 сокета, прерывания Arduino не поддерживаются. Было бы интересно запаять разъем для SD, но его еще надо поискать. Беглый просмотр Инетрнета показал, что есть и отдельные решения на этот счет (автор делал для wireless-сенсора, который пишет туда данные про обнаруженный сигнал).

13.01.2009

Ethernet Shield для Arduino

Итак, как я уже писал, ко мне приехала посылка из SmartProjects. Половина стоимости пришлась на Ethernet Shield:



... который я тут же начал испытывать со своим Arduino Diecimila:



Удобнее всего его подключать на работе к локалке, благо она под рукой в невероятных количествах ;)



Три коробочки вверху - это мегаизвращение, к которому я прибег при отсутствии под рукой блока питания 9В: берется источник DC 48В (от ~220В), втыкается в PoE-инжектор DLink DWL-P200, который вводит его в кабель Ethernet, затем в PoE-сплиттер, который, наоборот, отделяет питание, но попутно он умеет преобразовывать его при помощи DC-DC в 5 или 12 вольт, как раз под разъем питания для Arduino (2,1 мм, в центре - плюс).

Не пытайтесь повторить ;) лучше купите БП на 9В (от 12В стабилизатор на Arduino довольно сильно греется).

Что дальше?

Запускаем Arduino 0012, говорим "New", затем "Import Library | Ethernet". Появляются инклудники. Можно добавить пустые setup и loop и компильнуть: как видите, sketch занял около 8 Кбайт! (в память ATMEGA8 это уже точно не влезет).

Вернемся к Ethernet. Первым делом, можно попробовать пример Telnet, который есть в дистрибутиве Arduino.

#include <Ethernet.h>

// network configuration.  gateway and subnet are optional.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 10, 0, 0, 177 };
byte gateway[] = { 10, 0, 0, 1 };
byte subnet[] = { 255, 255, 0, 0 };

// telnet defaults to port 23
Server server = Server(23);

void setup()
{
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);

  // start listening for clients
  server.begin();
}

void loop()
{
  Client client = server.available();
  if (client) {
    server.write(client.read());
  }
}

Суть примера - после подключения по порту telnet (команда telnet arduino_ip), любой вводимый символ возвращается обратно.

Для того, чтобы попугать коллег, я решил переделать этот пример в имитацию веб-сервера. Поскольку TCP-соединение устанавливается совершенно прозрачно, то меняем порт с 23 на 80, а в обработчике добавляем считывание запроса и вывод HTML-странички.

Поскольку мне было лень считать количество информации на выходе, я ограничился только строкой Content-Type, а Content-Length посылать не стал. Браузеры у коллег наивно пытались дождаться завершения принимаемой информации, но навсегда повисали в этом состоянии. Пришлось добавить несколько команд, чтобы разрушить соединение:

#include <Client.h>
#include <Ethernet.h>
#include <Print.h>
#include <Server.h>

byte mac[] = { 0x00, 0x1D, 0x60, 0xAF, 0x03, 0x30 };
byte ip[] = { 10, 57, 3, 1 };
byte gw[] = { 10, 57, 0, 1 };
byte subnet[] = { 255, 255, 0, 0 };

//Server server = Server(23);
Server server = Server(80);

void setup() {
  Ethernet.begin(mac, ip, gw, subnet);
  server.begin();
  Serial.begin(38400);
}

void loop() {
  Client client = server.available();
  if (client) {
    while (client.available()) {
      Serial.print(client.read(),BYTE);
    }
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type: text/html");
    client.println("");
    client.println("&lt;html&gt;&lt;title&gt;Arduino Ethernet&lt;/title&gt;&lt;body&gt;&lt;h1&gt;Hello, I'm Arduino Ethernet Shield!&lt;/h1&gt;Version 0001 with disconnect!&lt;/body&gt;&lt;/html&gt;");
    client.flush();
    client.stop();
  }
}

На все эксперименты ушло около 15 минут, включая подключение и короткое обдумывание "чего бы такого наваять". Я писал эту статью в блог и делал картинки гораздо дольше ;)