Подключение микроконтроллера к локальной сети: Заключение

Уже почти всё. :)

Краткое содержание:

  • Мои кривые руки и баги
  • Немного о софте для компа
  • FAQ
  • Заключение

Про мои кривые руки, делающие кучу багов


«Если дебагинг — это удаление багов, значит программинг — это их внесение». Вот это как раз про мя. Сидели мы, значит, с Episcop'ом всю неделю и ловили баги в моём стеке) Разбирались почему девайс не хочет получать айпишник по DHCP с D-Link'овских роутеров с родной прошивкой. Прибили кучу багов и вроде заработало.

Что интересно, народ тихонечко минусует топики, а просто сказать, что ничего не работает, видимо, в лом (

Сегодня поправил все примерчики к постам)

Немного о работе с девайсом со стороны компа


Тут всё очень просто. Но чисто для полноты картины, рассмотрим примерчик. Начнём с прошивки девайса.

#include <avr/io.h>
#include <avr/pgmspace.h>
#include "lan.h" // цепляем стек

void udp_packet(eth_frame_t *frame, uint16_t len)
{
    ip_packet_t *ip = (void*)(frame->data);
    udp_packet_t *udp = (void*)(ip->data);

    // обрабатываем пакеты, приходящие на порт 1990
    if(udp->to_port == htons(1990)) 
    {
        // если нам приходит "preved", ...
        if(!memcmp_P(udp->data, PSTR("preved"), 6)) 
        {
            // ... то отвечаем "medved"
            memcpy_P(udp->data, PSTR("medved"), 6);
            udp_reply(frame, 6);
        } 
        
        // а если "value_<байт>"
        else if(!memcmp_P(udp->data, PSTR("value_"), 6)) 
        {
            // ... то пихаем этот байт в порт
            PORTA = udp->data[6];
        }
    }
}

int main()
{
    // настраиваем порт на выход
    DDRA = 0xff;
    
    // инициализируем стек
    counter_init();
    lan_init();
    sei();
    
    // ловим пакетики
    while(1)
        lan_poll();
    
    return 0;
}


Здесь вроде всё понятно. А теперь о софтинке для компа)

А вообще, софтинку писать не обязательно. Можно написать скрипт. Это быстро и кросплатформенно.

Для скриптов советую использовать perl — оч прикольный, удобный язык. Впрочем, я сам только недавно начал с ним разбираться)

Для приёма и отправки пакетов нам понадобится сокет. Создадим UDP-сокет, поддерживающий отправку широковещательных пакетов.

my $so = new IO::Socket::INET(
    Proto => "udp", 
    Broadcast => 1);


Если мы не указываем порт, к которому будет привязан сокет, система выделяет динамический порт, проще говоря, случайный. Это удобно для клиента. Для сервера стоит использовать фиксированный порт, задаваемый в ручную.

Если наш девайс получает адрес по DHCP, чтобы не прописывать статические лизы, можно использовать поиск девайса в сети. Для этого отправим широковещательный пакет и посмотрим откуда придёт ответ.

sub device_find {

    my $so = $_[0]; #сокет
    
    #броадкастим "превед"
    my $bradr = sockaddr_in(device_port, INADDR_BROADCAST);    
    $so->send("preved", 0, $bradr);

    #ждём ответа пару секунд и читаем ответ
    my $sel = IO::Select->new();
    $sel->add($so);

    if($sel->can_read(2) and $so->recv(my $buf, 32)) {
        # получили "медвед"?
        if($buf eq "medved") {
            return $so->peerhost(); #возвращаем хост
        }
    }
    
    return 0;
}


Здесь мы отправляем широковещательный пакет. Затем создаём объект типа IO::Select, позволяющий определяеть состояния хэндлов (можно читать, можно записывать, есть ошибка). Добавляем к селекту наш сокет. Затем тестируем (добавленные сокеты) на «читаемость». Если получаем ненулевой результат (т.е. на наш сокет пришли данные) принимаем данные и возвращаем адрес хоста, с которого данные пришли.

Отправка команды на девайс:

sub value_send {
    my($so,$addr,$val) = @_;
    my $sadr = sockaddr_in(device_port, inet_aton($addr));
    $so->send("value_".pack("C", $val), 0, $sadr);
}


Вот скрипт целиком:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;
use IO::Select;

use constant device_port => 1990;

############################################################

sub device_find {
    # ...
}

############################################################

sub value_send {
    # ...
}

############################################################

defined $ARGV[0] or die("\nusage: ctrl.pl <value>\n");

my $so = new IO::Socket::INET(
    Proto => "udp", 
    Broadcast => 1);

my $val = $ARGV[0];

if(my $addr = &device_find($so)) {
    &value_send($so, $addr, $val);
    print "\nvalue $val sent to $addr\n";
} else {
    print "\ndevice not found\n";
}

############################################################




ФАК


Отвечу на вопросы, которые часто задавали в комментах.

Q: И сколько времени ты это писал?
A: Писал на коленке в перерывах между постами)

Q: А какие ещё стеки можно запустить на маленьком МК? :)
A: Написаный тоже под ENC28J60, тоже на коленке — "Ethernet remote device" от Guido Socher. Его эксперименты и сподвигли мя на всё это безобразие.
A: uIP и lwIP от Adam Dunkels — а это уже штучки посерьёзнее.

Q: Пример не работает!
A: Нужно пройтись по коду примера, прально укзать в дефайнах все пины и порты, прально указать тип кристалла и тактовую частоту в настройках проекта. Убедиться что прошивка влезает в флеш, а оперативки использовано не больше 85% (нужно оставлять немного места под стек). Что всё прально подключено, а вход сброса ENC28J60 подтянут к питанию. После этого всё должно заработать.

Q: Щас модно стало говорить про всякие лицензии. На каких условиях можно использовать твой код?
A: Все мои поделки распространяются под лицензией WTFPL, проще говоря — что хочешь, то и делай с этой хренью.

Заключение


Скачать пример.

В комментах к этому посту можно складывать ещё всякие вопросы. В следующем посте я на них отвечу)



Все статьи цикла

Комментарии (79)

RSS свернуть / развернуть
Ответы на вопросы лучше дописывать в этот же пост. Пусть будет своего рода мега FAQ
0
хм, если кто-то догадается заглянуть в старый пост…
+1
Ну так они же вопросы тут же будут задавать.
0
ну если будет вопросов мало/нисколько, смысла отдельный топик делать не будет) Посмотрим
0
Не хочешь вот такую платку сообразить, только на AVR'ке, и вообще, было бы круто, arduino совместимую? Или просто вот такой выкидыш забацать? И тут же реализовать все. Я бы затарил! =)
0
имхо, надо бы. вещь интересная, особенно если уже готовая. я в каком то из топиков уже предлагал.
0
С DHCP от D-Link нормально не работают не только самодельные стеки, но и виндовый. У меня публичная сеть на Длинках, у четверти клиентов компы сами адреса не могут получить. Длинки 320-е и 2100-е, а самая проблемная винда, Виста.
0
Уже все работает, вчера выявили баг DHCP. DIR-320 сказал велкам и постелил дорожку из пакетов))
0
Ы, Perl. ИМХО, на С и WinSock пример был бы более пригоден для портирования в другие языки. И ОС в том числе, ЕМНИП в том же линуксе сокеты достаточно схожи.
Алсо, я просто половину синтаксиса не понимаю :)
0
  • avatar
  • Vga
  • 11 мая 2011, 08:22
Если честно, то я вообще не понимаю зачем тут писать примеры клиента или сервера на стороне компа. Полно ресурсов по программированию.
lifelover пошел на встречу и написал маленького клиента на перле.
0
Я тоже и я даже об этом писал. Но примерчик какой-то уж очень непонятный) Хотя идею броадкастного преведа я из него подцепил.
0
хе, винсок, в отличии от никсовых сокетов, это сплошные грабли)
пожалуй, стоит дописать как с ним бороться
а то я когда-то помучался
0
Нинай, у меня проблем не возникало. Хотя и не скажу, чтобы много с ним работал. Клиент для винлирка да хттп-серверок.
0
А в чем проблема то была в дхцп? :)
0
d-link почему-то в поле siaddr выставляет 0, алсо вместо времени аренды присылает мусор)
остальные серваки нормально делают
0
Да, уж. Потянулся я было к магазину за используемой здесь микросхемой, да цена в 1500 рублей остановила. А вообще, этот курс подключения МК к сети для меня (считай, начинающего), как теория поля и уравнения Максвелла для школьника средних классов. Слишком загадочно складываются слова в тутошних предложениях :)
Будем изучать.
0
ненене, 150р она стоит. ищи лучше)
да, хреново я пишу( получается прада как какой то матан
0
А ты не пытайся сожрать сразу весь цикл. Начни с начала и досконально разберись в первом примере. Чтобы понять как чо работает, потом в следующем и так далее.
0
Perl не самый лучший вариант для изучения скриптовых языков в наше время ))
Уж лучше Python.
Perl — это тот язык, который одинаково выглядит как до, так и после RSA шифрования…(с)Keith Bostic
+1
да, пытался я питоньчик изучать
порадовала его, с позволения сказать, «обратная совместимость»
даже принт — то оператор то функция
хее, уж лучше старый добрый перл)))
0
Признаться лучше тогда взял бы PHP, у него хоть схожесть синтаксиса с C++ =))
0
а в пхп есть нативные соккеты? я что то не помню.

Из скриптовых языков мне больше всего Lua нативных возможностей мизер, те же соккеты надо отдельно подгружать. Зато невероятная простота самого языка, расширения его возможностей и интегрирования в свои программы (статей в инете хвотает).
0
Прикольный язычок, да. Правда, программы на нем можно писать не менее мозговыносящие, чем на перле) Еще позабавило то, как на его основном типе данных — таблицах — можно эмулировать структуры, массивы, кортежи, классы-объекты и прочее)
Интегрирование у него совместимо с любым языком, поддерживающим DLL-ки. Это хорошо. Правда, оно довольно жирное :) Видел я нагенеренный прогой tolua модуль и описание API (со стороны скрипта) к нему. Описание кратенькое, модуль — 9-метровый.с (или .cpp ли))
Ну и в целом это больше встраиваемый язык (в проги для скриптования, а не как МК), для stand-alone языка библиотека скудновата, все нужно делать самому.
0
Почему модуль 9метров получился — я хз, вполне возможно. сам tolua не пользовался, писал обертки самостоятельно.
Да, встраиваемый и небольшой — этим и хорош. Но и как станд-алон его можно применять. Библиотек для него готовых хватает (в том числе и библиотека гуевого интерфейса есть).
Если писать программы взаимодействующую со своим устройство то не вижу причин не применять для этого Lua вместо perl'а. Можно допиливать функционал приложения, а критичиские участки в дальнейшем перекинуть на dll'ку расширив своё апи.
0
Apache+PHP+Curl =)
0
Шутку заценил. Ставить апач для того что-бы помигать светодиодом — мощно :)
А если серьёзно, то свпомнил, там библа сокет есть у пхп, с ней WEB-IRC клиенты делаются, и где-то натыкался на хорошо выполненный веб интерфейс к устройству какомуто по UDP общающемуся (жаль линка нет, лет 5-7 назад дело было).
0
=)
Если серьезно, то если применять скриптовые языки как серверную часть, то лучше с апачей. А вот если как клиентскую часть, то без него.
0
А я всё со своими баранами :)
LuaForWindows (20МБ)
socket = require("socket")
print(socket._VERSION)

function finddev()
	local udp = socket.udp()
	udp:setoption('broadcast',true)
	udp:setoption('dontroute',true)
	udp:setsockname('localhost',47808)
	udp:sendto('preved', '192.168.1.255', 47808)
	udp:settimeout(2)
	local msg, client, port = udp:receivefrom()
	udp:close()
	if msg == "medved" then
		return client, port
	end
	return nil;
end;

args = {...}

if not args[1] then
	print ("usage: lua.exe udp.lua <value>")
	os.exit(1)
end

client, port = finddev()
if not client then
    print("device not found")
	os.exit(2)
end

udp = socket.udp()
udp:setsockname('localhost',47808)

udp:sendto(args[1], client, port);
print ("value " .. args[1] .. " sent to " .. client .. ":" .. port);

Скорее всего не заработает, но кто его знает :)
0
Хех, енка сейчас занята примерчиком к последнему посту:)
Завтра попробую.
0
Да я никуда не спешу :)
http = require("socket.http")

function delay(n)
	local t1 = os.time();
	repeat
		dummy = nil
	until os.difftime(os.time(), t1) > n;
end

for num = 0, 3 do
	local link = "http://178.49.21.56:591/?t" .. num
	print("It " .. num .. " page: " .. link)
	page, err = http.request(link)
	if not page then
		print("Error: " .. err)
	else
		print(page)
	end
	delay(2);
end
0
Lua? 20 метров? Фигасе там библиотек. Да, на такой можно и тулзы писать)
0
В установленном виде оно 70 метров кушает. Там 25 метров библиотек: окна, графика, opengl, wxWidgets и пр; 20 метров документации.
Если брать чистую Lua с только встроенными возможностями, то 150-250кБ только.
А этот проектик хорош тем что полезные библиотеки подтянуты и не надо мучатся в версиями и компиляторами.
0
Судя по всему, вопросов нет:)
Ну тогда на этом всё…
0
Да будет пинг, во имя udp, tcp и icmp, аминь =)
0
Интересно бы было всё это перекинуть и проверить на STM32. Там и памяти побольше и скорость… Ну и у меня под рукой нет сейчас ATMEGA а есть 2 штуки STM32-Discovery :)
0
Не самая лучшая идея)
Как раз-таки на STM32 можно, развернуть нормальный стек а не лепить на коленке)
0
А чем этот не нормальный?? Делает то что надо. Занимает минимум места… Я понимаю как там всё работает и могу его подпилить к собственным нуждам. Почему его не использовать???
0
нет нормального окна для TCP например
т.к. в авр его просто негде хранить
а на стм32 можно всё гораздо прямее сделать
0
Ну и допилить его :) хотя мне он и так нравится и делает всё что мне надо :)
0
для жирных микрух и так есть неплохие стеки
качественные, хорошо протестированые
а этот я под тиньку щас затачиваю))
0
И что? Влазит?) Там же флеша того…
0
попробую поприколу минимальную вебмордочку в тиньку2313 запихнуть)
0
Спасибо автору за цикл статей. Опробовал в связке CoLinkEx(NXP LPC1114)+ готовый модуль enc28j60 с eBay. Переписывал в CoIde — заработало, доволен как слон! Больше всего времени убил на поиск непонятного глюка. Как потом оказалось не учел специфику ARM с их 32разрядностью — в структурах данные выравнивались по краю 32бит, поэтому пакеты превращались в фарш и проц постоянно генерил исключения.
0
  • avatar
  • Telek
  • 01 сентября 2011, 00:04
тоже хотел учудить порт на STM32 — как проблему со структурами решили?
0
В моем случае, так как компилятор gcc добавлением __attribute__ ((packed)) к полям которые не нужно ровнять.
Если другой компилятор — посмотреть директивы запрещающие выравнивание.
typedef struct arp_message {
	uint16_t hw_type __attribute__ ((packed));
	uint16_t proto_type __attribute__ ((packed));
	uint8_t hw_addr_len;
	uint8_t proto_addr_len;
	uint16_t type __attribute__ ((packed));
	uint8_t mac_addr_from[6];
	uint32_t ip_addr_from __attribute__ ((packed));
	uint8_t mac_addr_to[6];
	uint32_t ip_addr_to __attribute__ ((packed));
} arp_message_t;

PS. По идее первые три __attribute__ ((packed)) не нужны, так как и так все ровно… Но для любых данных больше 8бит существует вероятность выравнивания, поэтому оставил.
0
#pragma pack(push, 1)
struct ...
#pragma pack(pop)
0
Спасибо за рабочие коды Lifelover.

Просьба показать как сделать перезагрузку МК+ЕНК28 при нажатии ссылки на HTML странице.
0
Мона резетнуть мегу собакой. Затем мега резетнить енку при инициализации.
0
спасибо Lifelover.

Я сегодня приобрел Атмега32 и попробовал проект httpd.zip.
Всё работает!

Вопрос:

А как сделать чтобы скачивались doc,rar,exe — файлы
Я прописал типы в httpd.c
const prog_char http_application_msword[] = «application/msword»;
const prog_char http_ext_msword[] = «doc»;

const prog_char* PROGMEM mime_type_table[][2] =
{

{http_ext_msword,http_application_msword}
}

все равно ошибка:
404 — Not Found
0
Дополнительная проверка показала:

файлы без спецсимволов закачиваются (resume.doc)
файлы с _ уже не видит (resume_a.doc)

P.S.

прописал тип msword
0
Наверно дело в LFN. MIME-тип в данном случае не имеет значение.
0
Да, оказалось с короткими именами файлов все хорошо, а для длинных я включил
в файле ffconf.h:
#define _USE_LFN 3

и при компиляции оказалось что отсутствуют некоторые функции например ff_convert

а в ff.h есть только это:

#if _USE_LFN /* Unicode — OEM code conversion */
WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */
WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */
#if _USE_LFN == 3 /* Memory functions */
void* ff_memalloc (UINT); /* Allocate memory block */
void ff_memfree (void*); /* Free memory block */
#endif
#endif

вопрос:
где можно взять пример этих функций?
0
скачал ff_convert отсюда:
svn.bertos.org/vendor/fatfs/R0.07a-patch20090518/src/option/ccsbcs.c

скопировал ff_wtoupper отсюда:
neo-myth-menu.googlecode.com/svn-history/r242/trunk/SNES/NeoMythMenu/pff.c

пинг есть а в браузере 404 — Not Found

в чем дело?
0
форматировал MMC под фат и фат32
менял #define _USE_LFN на 1 и 2

применял функции из ссылки:
cui32.googlecode.com/svn-history/r67/trunk/firmware/FatFs_Test_MGr/fatfs/option/ccsbcs.c

в результате пинг есть а в браузере выдает ошибку 404 — Not Found.

я пологаю LFN требует больше ресурсов чем может дать atmega32.
0
2 Lifelover использовал ли в качестве выходного разъема RJ45+транс по отдельности, а не в интегрированном исполнении?… дома дохрена убитых сетевух лежит (штук 5 или 6), хотел подключить, но боюсь сжечь ENC28J60. К тому же слышал, что бывают трансформаторы с различным коэффициентом 1:1, 1:2.5, так что затормозил на этом этапе… :(
0
У меня, кстати, магджек с трансом 1:2.5 на RX, 1:1 на TX, хотя микра хочет оба 1:1. Но вполне работает.
У Lifelover'а — да, раздельные.
Вообще, в даташите приведены требуемые параметры трансформатора. Так что просто нагугли даташит на свои трансы и смотри, подходит ли.
0
Так что просто нагугли даташит на свои трансы и смотри
… так уже обгуглился :) — результат — предложение скачать драйвер сетевухи, а про трансы ни слова… Volcano PP-515D
0
Спасибо за труды, такой вопрос- можно ли менять ip mask и gw через web интерфейс? Я язык С пока неочень знаю и немогу никак сообразить как это сделать.
0
Доброго времени суток.
Вот долблюсь над запуском вашей программы на такой же конфигурации.
Только вот программный код все не подается к запуску на Атмеги32. Все взято у вас(схема платы + коды). Ethernet модуль заказывал с ебея) (что то вроде viewitem.eim.ebay.ru/ENC28J60-Ethernet-LAN-Module-AVR-PIC-ARM-MCU/280647623509/item) При симуляции проекта в Студии 4ой он начинает зацикливаться на определении типа карты памяти.
Помогите. Что сделать чтобы все это чудо заработало?
0
Вопрос — как примерно будет выглядеть код одновременной работы Меги32 с SD карточкой и enc28j60?
есть ли готовые проекты где на МК ремлизован только сервер а страницы хранятся на SD карточке?
0
В этом цикле оно и реализовано.
0
Что тут скажешь мегареспект.
ENC-шка лежит уже 5 лет. После того как осилил по вечерам все эти статьи решил достать ее из шкафа. Специально зарегистрировался, чтобы ПОБЛАГОДАРИТЬ автора за проделанную работу.
Ну и маленький вопрос нашел в одном из архивов печатную плату, я так понял там Atmega32 питается от 3.3В как и ЭНЦ-шка. Проблем с Atmega32 не наблюдается при таком напряжении питания?? В свое время подключал (PIC18F452 –5В),( ENC –3,3В) согласовывал через 74hct08n. Тогда сделал только пинговалку…
Хочу использовать печатку автора, но сомневаюсь через напряжение питания…
0
  • avatar
  • axex
  • 03 апреля 2013, 01:35
Обычно проблем не бывает. В даташиту написано что они работают и от 3.3 в но немного снижается максимальная частота. Но я проверял — атмега 32 с 3.3в питанием работала без проблем на 20 Мгц
0
В принципе, у ENC28J60 входы 5V tolerant — так что на согласование можно забить вообще. Мегу запитать от 5В, енку от 3.3В и соединить напрямую (или через резисторы ом на 100).
0
А вот что интересно, сейчас комплект (STM32F207VCT6+RTL8201) стоит дешевле (ATMEGA32+ENC28J60), при том что веб-сервер для первого есть почти «искаропки» и с картой цифры на порядок больше (6Мб/сек — чтение, 2Мб/сек — запись). Если интересно, запилю статью о веб-сервером на STM32 с картой поверх LwIP и FreeRTOS. Драйвер карты почти «искаропки», немного допилил для работы с осью.
0
былобы интересно
+1
32 всегда (и дешевле сейчас) лучше чем avr. хотя я не понимаю любви к проводному ethernet — особенно для микро. я обычно RF 433/868 до базы — а от базы уже WiFi или GSM. Как пример использую такое
akb77.com/g/rf/ezmacpro-stm32-amp-efm32/
0
Хочу задать такой вот вопрос: скрипт отсылает броадкастом пакет на адрес 255.255.255.255… Какие-то особенности конфигурирования сетевого интерфейса есть? Я вижу что по wireshark пакет убежал. Но устройство его не приняло. Опять же если я подключусь к устройству по прямому IP — она на preved отвечает…
0
Дело в том, что это единственный вариант которым можно найти свое устройство в сети с приемлимой скоростью — все остальное на поиски может убить часы. А прием данных отправленных броадкастом почему-то не идет…
0
Вроде пакеты на 255.255.255.255 режутся роутером. Надо слать на броадкаст-адрес подсети (IP & MASK)|(!MASK). Еще пакеты могут фильтроваться самой ENC28J60 — нужно смотреть, какая там фильтрация выставляется.
0
В таком случае, если у устройства IP адрес не из текущей подсети, подключиться к нему не удастся…
0
Тогда к нему так и так подключиться не удастся (разве что он с той стороны гейтвея, но гейтвей вообще не выпускает широковещательные пакеты наружу).
0
Все малость хитрее.
1. Маршрутизатор пропускает широковещательные пакеты внутри одной сети (т.е. на DIR например 4 соски в ЛАН и 1 в ВАН — между ЛАНами пробежит, а в ВАН не пустит).
2. Устройство должно иметь маску подсети 0.0.0.0 — тогда оно будет ловить такие пакеты.

Спасибо за реакцию.
0
По идее, роутер должен перенаправить пакет, не адресованный локальной подсети наружу. Впрочем да, пакет на 255.255.255.255 адресован и локальной подсети тоже, об этом я не подумал.
Устройство должно иметь маску подсети 0.0.0.0 — тогда оно будет ловить такие пакеты.
Похоже на косяк реализации. Видимо, фильтр пакетов их не учитывает и скармливает фильтру по подсети, вместо пропускания мимо него.
0
Ну авторов видимо поддостали вопросами — они чего-то не реагируют.
Роутер как раз броадкасты местные режет — это логично, иначе в интернете бы бегало много всякой грязи…
0
Роутер как раз броадкасты местные режет — это логично, иначе в интернете бы бегало много всякой грязи…
Он их наружу не пускает (как и вообще любые броадкасты, AFAIK), но по локалке пропускает.
в lan.c меняем
Тогда на локальные броадкасты перестанет отзываться.
0
отзывается
0
в lan.c меняем
#define ip_broadcast 	(ip_addr | ~ip_mask)

на
#define ip_broadcast 	inet_addr(255,255,255,255)


И все работает…
0
маску подсети 0.0.0.0 ставить не нужно…
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.