Ethernet W5100 + DHCP server

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Собственно, есть Ethernet shield на W5100 и arduino uno.
Пытаюсь "поковырять" примеры работы с данным шилдом.
Всё это дело подключено к серверу, имеющему свой DHCP (isc-dhcp-server). Все работает, адреса выдает и т.д.

С ардуиной же получается, что конструкция вида 

Ethernet.begin(mac);

работает чудесно. Сервак назначает IP, происходит соединение ну и далее запрос.

А вот конструкция вида:

Ethernet.begin(mac, ip);

Уже не работает и соединения нет.

IP пытался указывать как:

byte ip[] = {192,168,1,200};

и как

IPAddress ip(192,168,1,200);

Не работает ни одна конструкция.

Кто сталкивался, может есть какие-то грабли?
Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

а вы как определяете что нет соединения? 

и можно на код целиком посмотреть?... а то из вашего описания следует только рекомендация обрызгать все святой водой и прочитать молитву... а то духи злобные в коде поселились :)

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Код целиком вот такой в данный момент:

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

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {192,168,1,200};
char server[] = "srv.dyndns.org";  // URL сервера

EthernetClient client;

void setup() 
{
  Serial.begin(9600);
  Ethernet.begin(mac);//, ip);
  
  Serial.print("My address:");
  Serial.println(Ethernet.localIP());

  delay(1000);
  Serial.println("connecting...");

  if (client.connect(server, 80)) 
  {
    Serial.println("connected");
    client.println("GET /log.php?dev=1&t1=123.45&t2=0.3&t3=0.25&h=85.4 HTTP/1.0");
    client.println("Host: srv.dyndns.org");
    client.println();
  }   
  else 
    Serial.println("connection failed");
}


void loop()
{
  if (client.available()) 
  {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) 
  {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
  }
}

Полагаю, что в моём случае неробходимо использовать конструкцию вида 

Ethernet.begin(mac, ip, dns, gateway); 

Поправьте, если не прав.

То есть в моем случае (сервак имеет адрес 192.168.1.100) код должен выглядеть так, наверное:

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

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 200 };
byte dns[] = { 8, 8, 8, 8 };
byte gateway[] = { 192, 168, 1, 100 };

char server[] = "srv.dyndns.org";  // URL сервера

EthernetClient client;

void setup() 
{
  Serial.begin(9600);
  Ethernet.begin(mac, ip, dns, gateway);
  //Ethernet.begin(mac);//, ip);
  
  Serial.print("My address:");
  Serial.println(Ethernet.localIP());

  delay(1000);
  Serial.println("connecting...");

  if (client.connect(server, 80)) 
  {
    Serial.println("connected");
    client.println("GET /log.php?dev=1&t1=123.45&t2=0.3&t3=0.25&h=85.4 HTTP/1.0");
    client.println("Host: srv.dyndns.org");
    client.println();
  }   
  else 
    Serial.println("connection failed");
}


void loop()
{
  if (client.available()) 
  {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) 
  {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
  }
}

Пока нет возможности проверить. Приду домой - проверю.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Насколько я понимаю Вы пытаетесь обратиться из своей подсети (и на ней же висит сервер) через dyndns к самому себе - вот тут собака и порылась

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Ну вероятнее всего так. Но вот задача как раз в том и состоит, чтобы явно задать IP в своей сети, а обратиться (послать GET запрос) уже к хосту в интернете.

Поскажите, как сие сделать?

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Заведите сервер на бесплатном хостинге, у него будет постоянный айпишник. Самый простой вариант.

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

У моего тоже выделенный IP, однако хочется обращаться по имени, поэтому и через dyndns.org

И как тогда будет выглядеть код для внешнего IP? Что-то я запутался...

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Причем тут "обращаться по имени" и dyndns? Если у сервера выделенный айпи НЕ из зарезервированных подсетей для личного пользования (192.168.*/12, 10.*/8, 172.16.*/12)то и прописывайте его айпи, но в этом случае надо (если он идин из виртуальных серверов) чтобы он был прописан по дефолту в апачи

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

В случае данного сервера, не представляется возможным в папку дефолтного виртуального положить скрипты. Но это легко решается строкой:

client.println("Host: srv.dyndns.org");

после GET запроса.

ТО есть, если я правильно понимаю, я меняю строку

char server[] = "srv.dyndns.org";  // URL сервера

на примерно такую?

byte server[] = "173,194,35,168";  // URL сервера

Однако, ИМХО, это две одинаковых строчки. В чем смысл?

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

del. Дубль.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Вы неправильно понимаете. DNS превращает имя хоста в айпи, никакой другой смысловой нагрузки DNS не несет. Вашей заменой Вы просто разгружаете DNS, но Вашему серверу непонятно какой сайт отображать и поэтому он возьмет дефолтный. Что у вас в dyndns прописано?

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Не совсем так. DNS перенаправляет запрос на определенный IP. А уже сервак, стоящий на данном IP перенаправляет запрос (по адресу сайта) в определенную папку соответствующего виртуального сервера (на конкретном сервере). Так вот, виртуальных на данном серваке много. Дефолтный тоже есть (кстати, если именно по IP обратиться к серваку, он именно на дефолтный и направит), но будем считать (в силу определенных обстоятельств), что на дефолтный нет доступа. Если я к серверу по имени обращусь, я попаду в папку одноименного виртуального сервера (что мне и нужно), если по IP - непосредственно в папку default.

P.S. В настройках dyndns прописан урл (пусть будет srv.dyndns.org) и соответственно, IP (пусть будет 173.194.35.168) с галкой "Host with IP address", в принципе, как и везде на любом dns.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

DNS перенаправляет запрос? Очень интересно :) DNS это не что иное как текстовый файл в формате ключ-значение (разбивку по зонам мы опустим для простоты как и прочие настройки типа срока хранения). Я так понимаю что у вас страшно засекреченные данные на сервере хранятся, иначе к чему бы такая секретность? :) Ну а если Вы на самом деле прописали в dyndns что искомый домен находится на нем же, то конечно он ничего не найдет (Но я и не исключаю вариант что Вам выделили субдомен на dyndns, что не исключает предложенный вначале вариант завести бесплатный сервер чтобы исключить мое первое предположение) :)

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

и в догонку -  Вы проверяли подсеть правильная? IP  не занят кемто еще?

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Извиниите, про DNS неверно выразился. Хотел передать общий смысл. Но вы меня корректно исправили. Спасибо. ))

В общем, мы ушли от темы. Как раз эта часть работает нормально. Читайте выше, не работает не отправка или обработка сервером запроса, а именно жесткая привязка IP адреса _ в локальной сети_. При получении адреса сервером DHCP, все запросы доходят туда, куда им нужно.

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

И в догонку, IP никем не занят еще 200%

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

что пишет Serial.println(Ethernet.localIP());? при автоматическом присвоении ip

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012
byte dns[] = { 192, 168, 1, 100};
byte gateway[] = { 192, 168, 1, 100 };
CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

JollyBiber пишет:

что пишет Serial.println(Ethernet.localIP());? при автоматическом присвоении ip

Присвоенный IP. 192,168,1,22

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

в скрипте dns поменяйте (см. выше)

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Спассибо. Попробую дома. Я как-то не подумал об этом.

По результатам напишу - может ещё кто сталкиваться будет.

P.S. Отвлеченная тема от DHCP, но касающаяся самого ethernet шилда.

Хорошо греется основной чип W5100. Где-то в районе 60-70 градусов (не замерял, но палец не удержишь). 
Данный "бутерброд" питается пока по USB. Проблема в питании? То есть, хотелось бы узнать опыт других, кто "ковырял" данную железку.

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Как и обещал, отписываюсь.

Да - все работает изумительно. Читаю (как и многие другие) DHT22 и пишу в БД. Работает как с локальным сервером, так и с URL-ом.

Кому нужно, код ниже:

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  // MAC-адрес сетевого интерфейса шилда
byte ip[] = { 192, 168, 1, 200 };
byte mydns[] = { 192, 168, 1, 100 };            // Адрес DNS-сервера (domain name server)
byte gateway[] = { 192, 168, 1, 100 };          // Шлюз сети
//char server[] = "srv.dyndns.org";               // URL сервера (для интернета)
byte server[] = { 192, 168, 1, 100 };           // URL сервера (для локалки)
const unsigned long requestInterval = 600000;   // Задержка между запросами (60000 - 1 минута)
EthernetClient client;
unsigned long lastAttemptTime = 0;              // Время последнего соединения с сервером (милисекунды)

//Контакты к которым подключены датчик температуры и влажности
#define DHTPIN 3
// Выбираем сенсор который используется - DHT22
#define DHTTYPE DHT22                           // DHT 22  (AM2302)(в данном случае нам нужен именно он)
// Инициализация датчика...
DHT dht(DHTPIN, DHTTYPE);

void setup() 
{
  Serial.begin(9600);                           // Инициализация первого Serial'a  
  // Старт соединения...
  Ethernet.begin(mac, ip, mydns, gateway);  
  /*
  // Пытаемся соединиться с DHCP-сервером
  // и получить IP-адрес
  Serial.println("Attempting to get an IP address using DHCP:");
  if (!Ethernet.begin(mac)) 
  {
    // Если IP-адрес не получен...
    Serial.println("failed to get an IP address using DHCP, trying manually");
    // Устанавливаем вручную.
    Ethernet.begin(mac, ip);
  }
  */  
  // Пишем адрес в serial-порт
  Serial.print("My address:");
  Serial.println(Ethernet.localIP());
  // Делаем задержку в секудну для инициализации
  delay(1000);
}


void loop()
{   
  // Если текущее время запуска - последнее время коннекта > интервала запроса...
  if (((millis() - lastAttemptTime > 0) ? (millis() - lastAttemptTime): (lastAttemptTime - millis())) > requestInterval)
  {
    Serial.println("connecting...");
    if (client.connect(server,80))              // Соединяемся с сервером для передачи показаний
    {
      Serial.println("connected");
      Serial.println("Read data from sensors...");
      // Чтение показаний с датчика занимает около 2 секунд - это очень медленный датчик.
      float h = dht.readHumidity();
      float t1 = dht.readTemperature();
      if (isnan(t1) || isnan(h)) Serial.println("Failed to read from DHT");      // Если не удалось считать данные с датчиков...
      else                                      //Удачное чтение показаний с датчиков
      {
        Serial.println("Data are read!");
        Serial.print("Humidity: "); 
        Serial.print(h);
        Serial.print(" %\t");
        Serial.print("Temperature: "); 
        Serial.print(t1);
        Serial.println(" *C");
        char buf[10];
        dtostrf(t1, 5, 3, buf);
          String msg = "GET /get.php?dev=1&t1=";
          msg += buf;
        dtostrf(h, 5, 3, buf);
          msg += "&h=";
          msg += buf;
          msg += " HTTP/1.1";
        client.println(msg);                    //Отправляем данные запросом
        Serial.println(msg);
        client.println("Host: srv.dyndns.org");
        client.println();
        delay(50);                              // Задержка для того, чтоб сервер успел ответить
        while (client.available())              // Если пришли данные от сервера, читаем и отображаем их в Serial-порту
        {
          char c = client.read();
          Serial.print(c);
        }
        lastAttemptTime = millis();             // Пишем время последнего соединения
        client.stop();                          // Разрываем соединение.
      }      
    }
    else Serial.println("connection failed");
  }
}
JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Вот это

if (isnan(t1) || isnan(h)) Serial.println("Failed to read from DHT"); 

не есть гут. лучше их по одиночке проверяйте - лучше записать одно значение (если есть) чем ни одного

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

JollyBiber пишет:

Вот это

if (isnan(t1) || isnan(h)) Serial.println("Failed to read from DHT"); 

не есть гут. лучше их по одиночке проверяйте - лучше записать одно значение (если есть) чем ни одного

Ну с такой позиции уж лучше так:

if (isnan(t1) & isnan(h)) Serial.println("Failed to read from DHT"); 
JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Неа :) Так NULL по одному из значений может в БД попасть

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Согласен. Не подумал. Хотя, ИМХО если NULL по одному из значений, скорее всего будет нулл и по второму ;)

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Подниму старую тему дабы не плодить новую.
Появился еще один вопрос по ethrenet - шилду.
В коде для установления связи шилда с сервером используется строка вида:

client.connect(server,80)

Ну и после отправки показаний на сервер закрывается соединение:

client.stop();

Все бы ничего, но прикручиваю к данной связке nRF24L01, которая на той же SPI (периодически дергаю "ногами" chip select для выбора активного slave) и появляется проблема - если закрывать каждый раз соединение, то как я полагаю (может и неправильно) пока нет соединения, буфер приема будет недоступен (в буфер ничего "сыпаться" не будет). А буфер нужен, чтоб не пропустить команду с сервера.

Вопрос в следущем - можно ли (кто-нибудь работал так) не закрывать соединение (client.stop();) - то есть открыть его один раз, допустим, при инициализации шилда и не закрывать и чем это чревато?

Может есть другой выход?

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Ага... Разобрался почти. Для инициализации входящих подклчений есть EthernetServer

Однако, пока много чего не понятно.

Допустим, инициализирую я его как клиента EthernetClient client; в сетапе делаю Ethernet.begin(mac, ip, mydns, gateway); и в основном цикле client.connect(server,80) для передачи данных удаленному серверу.

Могу ли я параллельно в том же коде сделать EthernetServer myserver(80); в сетапе  (опять же - в сетапе по идее должны быть другие данные - айпи уже этого как я понимаю сервера-ардуино) написать server.begin(); и в основном коде EthernetClient clientsrv = server.available();?

Походу нет. Поправьте, если не прав, но полагаю что сервер и клиент на одном и том же шилде не запустить.

Мысли вслух: Опять-таки, после запроса сервак обязательно ответ пришлет... Кто ловить этот ответ будет на ардуине? Сервер? Клиент?

Или каждый раз когда хочется отправить чего-то переинициализировать (как в сетапе) этот эзернет? Ethernet.begin(mac, ip, mydns, gateway);

P.S. Эх, блин... Все совсем не так... Поправлю этот пост, когда полностью разберусь и напишу рабочий скетч.

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

CityCat пишет:

Мысли вслух: Опять-таки, после запроса сервак обязательно ответ пришлет... Кто ловить этот ответ будет на ардуине? Сервер? Клиент?

Какая разница? Кто поймал - тот и клиент. :)

На самом деле - хорошо бы хоть азы tcpip знать: ответ приходит на тот порт, с которого уходил запрос. А кто там слушает (или уже никого нет) - это дело десятое.

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Andrey_Y_Ostanovsky пишет:

CityCat пишет:

Мысли вслух: Опять-таки, после запроса сервак обязательно ответ пришлет... Кто ловить этот ответ будет на ардуине? Сервер? Клиент?

Какая разница? Кто поймал - тот и клиент. :)

На самом деле - хорошо бы хоть азы tcpip знать: ответ приходит на тот порт, с которого уходил запрос. А кто там слушает (или уже никого нет) - это дело десятое.

Понимание данных процессов есть. Я прекрасно понимаю, что "Кто поймал - тот и клиент". Очень не хотелось ковырять библиотеку - совсем нет на это времени. Спрашивал потому как думал, может кто-то уже интересовался данным вопросом, пототму как неизвестно как поведет себя данная библа в этом случае.

Ну а уж что ответ придет по тому же порту на котором установлено соединение (мало того, на данном, прикладном уровне он привязывается именно к порту) - и сомнений нет. Вопрос заключался именно в том, кто ж слушать-то будет в данный момент.

Но вы невнимательно читали прошлый пост (как бы я написал, что разобрался, просто не реализовал).

Однако, спасибо за отзыв и помощь.

Кстати, по этой теме понравилась очень данная статья - подробно и очень просто изложено про библиотеку Ethernet - кто не сталкивался настоятельно рекомендую к прочтению для понимая.

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

CityCat пишет:

Ну а уж что ответ придет по тому же порту на котором установлено соединение (мало того, на данном, прикладном уровне он привязывается именно к порту) - и сомнений нет. Вопрос заключался именно в том, кто ж слушать-то будет в данный момент.

Я исходил из двух простых вещей: по-умолчанию, web-сервер "слушает" 80-й порт, в то же время принято, что приложения, для исходящих пакетов, не используют порты ниже 1024 (а лучше не ниже 6000), т.е., в нормальных условиях, web-сервер никак не может получить пакет, отправленный клиентом.

Другое дело, озадачиться вопросом: как в ардуине tcp-сессия сохраняется между циклами loop, или просто стоим на месте и ждем ответа...

CityCat
CityCat аватар
Offline
Зарегистрирован: 13.06.2013

Andrey_Y_Ostanovsky пишет:
озадачиться вопросом: как в ардуине tcp-сессия сохраняется между циклами loop
Собственно об этом я спрашивал. Прошу прощения, если некорректно выразил всою мысль ))))

В принципе, не беда. Набросал код - приду домой - проверю :)