Урок 64. TCP сервер и клиент на Ардуино. Библиотека UIPEthernet.

Ethernet

В уроке изучим Ардуино-библиотеку для работы с Ethernet контроллером ENC28J60. Напишем программы для создания TCP сервера и клиента.

Предыдущий урок     Список уроков     Следующий урок

Для подключения платы Ардуино к сети Ethernet я выбрал контроллер ENC28J60, как самый дешевый и распространенный. О нем я рассказывал в предыдущем уроке.

 

Существует несколько Ардуино-библиотек для управления модулем ENC28J60. Я уверенно выбрал библиотеку UIPEthernet.

  • Библиотека обладает самыми широкими функциональными возможностями.
  • Работает надежно.
  • Интерфейс программирования (API) библиотеки полностью совместим с широко распространенной библиотекой Ethernet, которая используется совместно с модулем W5100. Т.е. все сказанное в этом уроке применимо для модуля W5100 и библиотеки Ethernet.
  • К недостаткам библиотеки UIPEthernet можно отнести большой объем кода. Но это плата за функциональность.

 

Библиотека UIPEthernet.

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

Существуют следующие классы и соответствующие им функции.

Класс Ethernet инициализирует библиотеку и настройки для работы с сетью Ethernet.
begin() subnetMask() dnsServerIP()
localIP() gatewayIP() maintain()
Класс Ethernet инициализирует библиотеку и настройки для работы с сетью Ethernet.
 IPAddress()
Класс Server – применяется для создания и работы с серверами.
Server available() print()
EthernetServer() write() println()
begin()
Класс Client – используется для создания и работы с клиентами.
Client write() read()
EthernetClient() print() peek()
if (EthernetClient) println() flush()
connected() available() stop()
 connect()
Класс EthernetUDP – поддерживает UDP протокол.
begin() beginPacket() stop()
read() endPacket() remoteIP()
peek() parsePacket() remotePort()
write() available() flush()

Я использую эту версию библиотеки UIPEthernet-2.0.6.

 

Подключение модуля ENC28J60.

Как подключить модуль я подробно рассказывал в уроке 63. Еще раз повторю главное.

  • Модуль требует источник питания с напряжением 3,3 В и током до 180 мА. Поэтому я подключил его к плате Arduino UNO.  При работе с другими платами может потребоваться дополнительный стабилизатор 3,3 В.
  • Интерфейсные входы модуля ENC28J60 допускают непосредственное подключение к сигналам с уровнями 5 В. Для соединения модуля с платой Ардуино нет необходимости в согласующих резисторах.
  • Модуль подключается к плате Ардуино с помощью 10 проводов. Схема  в предыдущем уроке.
  • К компьютеру модуль подключается (урок 63):
    • непосредственно – с помощью перекрестного кабеля;
    • через роутер или коммутатор – с помощью прямого кабеля.

Модуль ENC28J60 с Ардуино

 

Общая постановка задачи.

О протоколе TCP я упоминал в конце урока 62. Сейчас скажу главное :

  • передаче данных предшествует установка соединения клиента и сервера;
  • данные при передаче защищены от искажения и потерь.

Мы просто посылаем данные, не беспокоясь о том, что они пропадут или будут доставлены с ошибками. Все заботы о доставке и целостности данных берет на себя протокол TCP.

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

Сейчас нас интересует только обмен данными. Поэтому формализуем задачу:

  • У нас есть плата Ардуино с подключенным к ней модулем ENC28J60, т.е. сетевое Ардуино-устройство или локальный контроллер.
  • Образована локальная Ethernet сеть из контроллера и компьютера. Попросту говоря, Ардуино-устройство подключено к компьютеру, обычно через роутер.
  • Задача состоит в том, чтобы от компьютера предать данные Ардуино-устройству и считать данные из Ардуино в компьютер.

 

TCP сервер.

Для начала решим, что инициировать обмен данными будет компьютер.

  • Для выполнения такой задачи локальный контроллер должен быть сервером. Т.к. мы используем TCP протокол, то TCP сервером.
  • Компьютер должен быть клиентом. Клиента создадим, используя программу TCP/IP Builder. С помощью нее будем передавать данные в локальный контроллер, т.е. в плату Ардуино.
  • Данные, полученные контроллером, будем выводить в монитор последовательного порта Arduino IDE.
  • Ответные данные от контроллера компьютеру увидим опять же в окне программы TCP/IP Builder.

Программа TCP/IP Builder позволяет создавать TCP серверы и клиенты на компьютере. Программа относится к категории “A free open source software” с лицензией GNU – свободное программное обеспечение, разрешенное к копированию и распространению. Загрузить можно по этой ссылке TCP/IP Builder 1.9.

Скетч сервера выглядит так:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

// TCP сервер, возвращает полученные данные
#include <SPI.h>
#include <UIPEthernet.h>

// определяем конфигурацию сети
byte mac[] = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес
byte ip[] = {192, 168, 1, 10}; // IP-адрес
byte myDns[] = {192, 168, 1, 1}; // адрес DNS-сервера
byte gateway[] = {192, 168, 1, 1}; // адрес сетевого шлюза
byte subnet[] = {255, 255, 255, 0}; // маска подсети

EthernetServer server(2000); // создаем сервер, порт 2000
EthernetClient client; // объект клиент
boolean clientAlreadyConnected= false; // признак клиент уже подключен

void setup() {
  Ethernet.begin(mac, ip, myDns, gateway, subnet); // инициализация контроллера
  server.begin(); // включаем ожидание входящих соединений
  Serial.begin(9600);
  Serial.print("Server address:");
  Serial.println(Ethernet.localIP()); // выводим IP-адрес контроллера
}

void loop() {
  client = server.available(); // ожидаем объект клиент
  if (client) {
    // есть данные от клиента
    if (clientAlreadyConnected == false) {
      // сообщение о подключении
      Serial.println("Client connected");
      client.println("Server ready"); // ответ клиенту
      clientAlreadyConnected= true;
    }

    while(client.available() > 0) {
      char chr = client.read(); // чтение символа
      server.write(chr); // передача клиенту
      Serial.write(chr);
    }
  }
}

Параметры конфигурации сети описаны в уроке 62. MAC адрес выбирайте любым, только 2 младших разряда старшего октета должны быть равны 10. IP адрес я выбрал 192.168.1.10.

Обычно в домашних сетях с роутером IP адреса раздаются автоматически (DHCP протокол).

  • Роутер назначает себе  192.168.1.1. Это же адрес сетевого шлюза.
  • Компьютеру присваивается адрес 192.168.1.2.
  • Дальше подряд  идут адреса других сетевых устройств.

В уроке 62 написано, как посмотреть адреса с помощью CMD команд.

Сетевые адреса

Программа выполняет следующие действия:

  • Проверяет, если данные от клиента.
  • Когда приходят первые данные, пишет сообщение "Client connected" (Клиент подключен) и передает клиенту  "Server ready" (Сервер готов).
  • Все поступающие данные передает обратно клиенту.

Для проверки необходимо загрузить скетч в плату Ардуино и открыть монитор последовательного порта. В окне монитора появится IP-адрес контроллера.

Окно монитора

  • Запустить TCP/IP Builder.
  • В строчке Local IP уже должен быть вписан адрес вашего компьютера. Надо добавить порт, для моей программы 2000.
  • Нажать кнопку Create Socket (Создать соккет).
  • В строке IP вписать IP адрес локального контроллера и порт.
  • Нажать Connect (Подключиться).
  • В результате на компьютере будет создан клиент.

Теперь можно посылать данные из окна Send data. Полученные от сервера данные будут появляться в окне Receive data.

TCP/IP Builder

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

Окно монитора

При первой посылке в окне монитора последовательного порта появится сообщение Client connected. В окне программы клиента будет ответное сообщение Server ready

Можете попробовать мою программу.

Мой монитор

Это программа текстового TCP клиента. Пояснять особенно нечего. Загрузить можно по ссылке:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

У меня используется роутер с WiFi. В сеть включен сотовый телефон. Если на телефоне установить программу-клиент, к нашему серверу на Ардуино можно обращаться и от телефона.

Из Google play я установил на телефон приложение Simple TCP Socket Tester.

Simple ...

Выбрал режим - клиент, установил IP адрес, порт и соединился с нашим Ардуино-сервером.

Simple ...

Все работает.

 

Давайте создадим TCP сервер близкий к практическому применению. Он должен:

  • под управлением от клиента зажигать и гасить светодиод на плате Ардуино;
  • по запросу клиента передавать время, прошедшее с начала запуска программы платы (результат функции millis()).

Вывод 13, на который подключен светодиод платы, занят на управление модулем ENC28J60. Поэтому я подключил дополнительный светодиод на 2 вывод платы, через резистор. Этим светодиодом и будет управлять клиент.

Скетч этого варианта сервера:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

// TCP сервер, управляет светодиодом, передает время
#include <SPI.h>
#include <UIPEthernet.h>

// определяем конфигурацию сети
byte mac[] = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес
byte ip[] = {192, 168, 1, 10}; // IP-адрес
byte myDns[] = {192, 168, 1, 1}; // адрес DNS-сервера
byte gateway[] = {192, 168, 1, 1}; // адрес сетевого шлюза
byte subnet[] = {255, 255, 255, 0}; // маска подсети

EthernetServer server(2000); // создаем сервер, порт 2000
EthernetClient client; // объект клиент

void setup() {
  Ethernet.begin(mac, ip, myDns, gateway, subnet); // инициализация контроллера
  server.begin(); // включаем ожидание входящих соединений
  pinMode(2, OUTPUT); // вывод светодиода
}

void loop() {
  client = server.available(); // ожидаем объект клиент
    if (client) {
      // есть данные от клиента
      if(client.available() > 0) {
        char chr = client.read(); // чтение символа
        if(chr == '0') digitalWrite(2, LOW); // если 0, гасим светодиод
        if(chr == '1') digitalWrite(2, HIGH); // если 1, зажигаем светодиод
        client.println(millis()); // передаем серверу время
    }
  }
}

Логика работы программы проста.

  • Если от клиента поступает символ 0 светодиод гасится.
  • Если поступает символ 1 светодиод зажигается.
  • При приходе от клиента любого символа в ответ передается время работы программы.

Можно проверить работу сервера, например, моей программой.

Мой монитор

Вот моя специализированная программа, которая позволяет управлять нашим Ардуино-сервером.

Здесь время выводится в ”человеческом виде”, светодиодом можно управлять с помощью ”птички”.

Мой монитор

Вот ссылка для загрузки программы:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

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

 

DHCP сервер.

В предыдущих программах мы задавали IP адрес сервера явно. Использовали, так называемые, статические IP адреса. Практически все современные сети имеют возможность автоматического распределения адресов. Сервер, использующий этот способ назначения IP адресов, называется DHCP сервером. Об этом написано в уроке 62.

Чтобы получить динамический IP адрес достаточно при инициализации контроллера указать только один аргумент – mac адрес. Т.е. вместо

Ethernet.begin(mac, ip, myDns, gateway, subnet);  // инициализация контроллера

написать

Ethernet.begin(mac);   // инициализация контроллера с DHCP протоколом

С учетом изменений скетч первой программы будет выглядеть так:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

// DHCP сервер, возвращает полученные данные
#include <SPI.h>
#include <UIPEthernet.h>

byte mac[] = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес

EthernetServer server(2000); // создаем сервер, порт 2000
EthernetClient client; // объект клиент
boolean clientAlreadyConnected= false; // признак клиент уже подключен

void setup() {
  Serial.begin(9600);
  Serial.println("Getting IP address using DHCP");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure using DHCP");
    while(true) ; // зависаем по ошибке
  }

  // вывод IP адреса
  Serial.print("IP address: ");
  IPAddress ip = Ethernet.localIP();
  for (byte i = 0; i < 4; i++) {
    Serial.print(ip[i], DEC);
    Serial.print(".");
  }
  Serial.println();
  server.begin(); // включаем ожидание входящих соединений
}

void loop() {
  client = server.available(); // ожидаем объект клиент
  if (client) {
    // есть данные от клиента
    if (clientAlreadyConnected == false) {
      // сообщение о подключении
      Serial.println("Client connected");
      client.println("Server ready"); // ответ клиенту
      clientAlreadyConnected= true;
    }

    while(client.available() > 0) {
      char chr = client.read(); // чтение символа
      server.write(chr); // передача клиенту
      Serial.write(chr);
    }
  }
}

Проверяем с помощью моей программы.

После загрузки скетча локальному контроллеру присвоился адрес 192.168.1.6.

Окно монитора

Запускаем программу TCP_Client_1. Задаем адрес 192.168.1.6. Проверяем.

Мой монитор

Все работает.

 

TCP клиент.

В предыдущем разделе мы управляли сервером на Ардуино с помощью клиента на компьютере. Попробуем противоположный вариант: клиент реализован на Ардуино, сервер – на компьютере.

Если все получиться, то в будущем сможем разрабатывать системы, в которых и серверы и клиенты выполнены на базе плат Ардуино.

Создадим из платы Ардуино TCP клиента. После соединения с сервером он будет:

  • передавать на компьютер-сервер все данные, которые мы будем набирать в мониторе последовательного порта;
  • в окно монитора последовательного порта выводить данные, поступающие с сервера.

Скетч TCP клиента:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

// TCP клиент, передает данные из UART серверу, от сервера в UART
#include <SPI.h>
#include <UIPEthernet.h>

// определяем конфигурацию сети
byte mac[] = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x5C}; // MAC-адрес
byte ip[] = {192, 168, 1, 10}; // IP-адрес клиента
byte ipServ[] = {192, 168, 1, 2}; // IP-адрес сервера

EthernetClient client; // создаем клиента

void setup() {
  Ethernet.begin(mac, ip); // инициализация контроллера
  Serial.begin(9600);
  delay(1000);
  Serial.println("connecting...");
  // устанавливаем соединение с сервером
  if (client.connect(ipServ, 2000)) {
    Serial.println("connected"); // успешно
  }
  else {
    Serial.println("connection failed"); // ошибка
  }
}

void loop() {
  // все, что приходит с сервера, печатаем в UART
  if (client.available()) {
    char chr = client.read();
    Serial.print(chr);
  }

  // все, что приходит из UART, передаем серверу
  while (Serial.available() > 0) {
    char inChr = Serial.read();
    if (client.connected()) {
      client.print(inChr);
    }
  }

  // если сервер отключился, останавливаем клиент
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    while (true); // останавливается
  }
}

Программа:

  • Устанавливает соединение с сервером.
  • Все поступающие от сервера данные выводит в UART.
  • Все данные из UART передает серверу.
  • Если соединение с сервером разорвано, зависает.

Проверяем с помощью TCP/IP Builder.

  • Запускаем TCP/IP Builder.
  • Устанавливаем в строке Local IP порт 2000.
  • Нажимаем Create Socket.
  • Затем Connect.
  • Запускаем программу Ардуино. В случае успешного соединения, на сервере и клиенте должны появиться сообщения connected.

Теперь можем обмениваться данными.

Builder

Окно монитора

Можно проверить работу с сервером, созданном на моем телефоне.

  • Запускаем на телефоне Simple TCP Socket Tester.
  • Выбираем режим сервер.
  • Устанавливаем порт 2000.
  • Нажимаем START SERVER.
  • В программе Ардуино меняем адрес сервера (ipServ[]). Адрес выводится в приложении Simple TCP Socket Tester зеленой строкой. У меня 192.168.1.4.
  • Загружаем программу в плату Ардуино.
  • Проверяем соединение и обмен данными.

Simple ...

И традиционно мой вариант программы сервера на компьютере. Загрузить можно по ссылке:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

Мой монитор

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

 

В следующем уроке будем обмениваться данными, используя протоколы UDP и HTTP.

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 15 часов

Эдуард

2
Комментарии: 1102Публикации: 122Регистрация: 13-12-2015

20 комментариев на «Урок 64. TCP сервер и клиент на Ардуино. Библиотека UIPEthernet.»

  1. Здравствуйте, всё очень познавательно спасибо!
    MODBUS TCP в планах у Вас для публикации?

    0
    • Здравствуйте! Спасибо, за отзыв.
      Про MODBUS TCP не думал. TCP протокол и так защищает данные, гарантированно доставляет их.

      0
  2. Спасибо вам за проделанную работу. У вас очень хорошие уроки. Поддерживаю вопрос про Modbus TCP. В данный момент сам пытаюсь прикрутить Arduino UNO R3 (китайский клон) + Ethernet Shield Wiznet W5500 (производство Amperka) к панели оператора WEINTEK MT8071IE. При помощи ваших уроков получилось прикрутить ардуину через RS485, по протоколу Modbus RTU к панели WEINTEK. По Modbus TCP пока не получается, не хватает знаний. Ардуина видна в сети, пингуется, но модбус не работает.

    0
  3. В интернете скачал библиотеку, называется Mudbus 1.0 пишут что это Modbus TCP попробую прикрутить, если что то получится напишу.

    0
  4. Спасибо за за уроки, за ними стоит колоссальный труд — респект вам!
    Я живу в Хорватии и мне жалко, что я не могу отблагодарить: платежи с моей страны почему-то не принимаются.

    0
    • Вообщем с вашей программой все работает, а TCP/IP builder не желает. Я хочу написать на с# подобие вашей программы, не подскажите с чего начать, постороение клиент-серверных приложений?

      0
      • Здравствуйте!
        У меня TCP/IP builder работает. С чего начать разработку? Разбираться в конкретных функциях, классах для работы с TCP.

        0
  5. Здравствуйте!
    Благодарю за отличную статью и рабочую программу! Единственный вопрос: после отключения сервера клиент прекращает работу, у меня не получилось сделать так, чтобы после повторного включения сервера клиент подсоединился к нему и начал обмен сообщениями. Не подскажете куда копнуть, чтобы решить проблему?

    0
  6. Здравствуйте, Эдуард!
    Отличные уроки, спасибо еще раз огромное!
    Вопрос такой, как клиентом с ардуины кинуть get-запрос на web-сервер развернутый в локальной сети, чтобы обработать php скриптом этот запрос?

    0
  7. Добрый день!
    Не получается соединение в режиме клиента (сервер ПК). А очень нужно. Остальное работает. Связка Arduino nano m328+модуль enc28.
    Что может быть? Остальные режимы работают.
    Автору — благодарность и пожелания успехов!

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *