Урок 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.

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

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

// 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 клиента. Пояснять особенно нечего. Загрузить можно по ссылке:

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

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

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

Simple ...

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

Simple ...

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

 

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

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

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

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

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

// 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 светодиод зажигается.
  • При приходе от клиента любого символа в ответ передается время работы программы.

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

Мой монитор

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

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

Мой монитор

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

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

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

 

DHCP сервер.

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

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

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

написать

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

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

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

// 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 клиента:

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

// 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 ...

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

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

Мой монитор

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

 

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

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

0

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

не в сети 4 дня

Эдуард

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

64 комментария на «Урок 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
  8. Здравствуйте, меня тоже очень интересует уроки — ModBus TCP , за которые готов поддержать проекты четырёхзначной суммой , так как валяются у меня два года эти конвекторы RS485 без дела. На просторах интернета более внятно растолкованного материала нигде нет.

    0
  9. Здравствуйте.
    Я заморачивался с modbus TCP на W5500 и под него код у меня есть.
    Попробовал с библиотекой что описана здесь все работает.
    Код для modbus tcp ниже:

    #include
    #include
    #include
    #define regnum 30

    int _modbusSlaveDataTable_4[regnum];// Изменить при изменении кол-ва регистров
    int _modbusSlaveAddresTable_4[regnum] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // Изменить при изменении кол-ва регистров
    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
    //30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    //40, 41, 42, 43, 44, 45, 46, 47, 48, 49
    //50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
    //60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
    //70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
    //80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
    //90, 91, 92, 93, 94, 95, 96, 97, 98, 99};

    byte _modbusSlaveBufferSize = 0;
    byte _modbusSlaveBuffer[64];
    const unsigned char _modbusSlave_fctsupported[] = {3, 6, 16};
    EthernetServer _modbusSlaveTCPServer(502);
    EthernetClient _modbusSlaveTCPClient;
    byte _modbusSlaveMBAPBuffer[6];
    // Настройки сетевой карты
    byte ethernet_mac [] = {0x78, 0xAC, 0xC0, 0x4D, 0x2D, 0x9E};
    IPAddress ethernet_ip(192, 168, 2, 220);
    byte ethernet_dns [] = {192, 168, 2, 1};
    byte ethernet_gateway [] = {192, 168, 2, 1};
    byte ethernet_subnet [] = {255, 255, 255, 0};

    EthernetClient ethClient;

    void setup()
    {

    Ethernet.begin(ethernet_mac, ethernet_ip, ethernet_dns, ethernet_gateway, ethernet_subnet);
    delay(1500);

    _modbusSlaveTCPServer.begin();

    for(int i=0;i= startTime) {return (currentTime>=(startTime + period));} else {return (currentTime >=(4294967295-startTime+period));}
    }
    byte _modbusSlavePoll()
    {
    if( !_modbusSlaveTCPClient.connected()) {_modbusSlaveTCPClient = _modbusSlaveTCPServer.available();}
    if (_modbusSlaveTCPClient) { _modbusGetSlaveRxBuffer();} else{return 0;}
    if(_modbusSlaveBufferSize ==0) {return 0;}
    if ((_modbusSlaveBuffer[0] != 1) && (_modbusSlaveBuffer[0] != 0)) return 0;
    byte exception = _modbusValidateRequest();
    if (exception > 0) {

    if (exception != 255) { _modbusSlaveBuildException( exception );
    _modbusSlaveSendTxBuffer();
    }
    return exception;
    }

    switch ( _modbusSlaveBuffer[1] ) {
    case 3 :
    return process_modbus_FC3(4);
    break;
    case 6 :
    return process_modbus_FC6();
    break;
    case 16 :
    return process_modbus_FC16();
    break;
    default:
    break;
    }
    return 25;
    }
    byte _modbusValidateRequest() {
    boolean isSupported = false;
    for (uint8_t i = 0; i < sizeof( _modbusSlave_fctsupported ); i++) {
    if (_modbusSlave_fctsupported[i] == _modbusSlaveBuffer[1]) {
    isSupported = 1;
    break;
    }
    }
    if (!isSupported) { return 1;}
    int intRegs = 0;
    byte byteRegs;
    switch ( _modbusSlaveBuffer[1] ) {
    case 6 :
    if(!(checkModbusAddres(( word( _modbusSlaveBuffer[2], _modbusSlaveBuffer[3]) ),4))){return 2;}
    break;
    case 3 :
    case 16 :
    if(!(checkModbusRange((word( _modbusSlaveBuffer[2], _modbusSlaveBuffer[3])), (word( _modbusSlaveBuffer[4], _modbusSlaveBuffer[5])),4))){return 2;}
    break;
    }
    return 0; // OK, no exception code thrown
    }
    bool checkModbusAddres(int addr, byte table)
    {
    return (!(( modbusSlaveIndexForAddres(addr,table)) == -1));
    }
    int modbusSlaveIndexForAddres(int addr, byte table)
    {
    int tableSize = 0;
    switch (table) {
    case 4:
    tableSize = regnum; // Изменить при изменении кол-ва регистров
    break;
    }
    for (byte i = 0; i < tableSize; i++) {if((modbusSlaveAddresFromIndex(i,table)) == addr){return i;}}
    return -1;
    }
    int modbusSlaveAddresFromIndex(byte index, byte table)
    {
    switch (table) {
    case 4:
    return _modbusSlaveAddresTable_4[index];
    break;
    }
    return -1;
    }
    bool checkModbusRange(int startAddr, int addrNumber, byte table)
    {
    for (int i=0; i < addrNumber; i++) {if(!(checkModbusAddres((startAddr+i),table))){return false;}}
    return true;
    }
    void _modbusSlaveBuildException( byte exception ) {
    byte func = _modbusSlaveBuffer[1];
    _modbusSlaveBuffer[0] = 1;
    _modbusSlaveBuffer[1] = func + 0x80;
    _modbusSlaveBuffer[ 2 ] = exception;
    _modbusSlaveBufferSize = 3;}
    void _modbusSlaveSendTxBuffer()
    {
    if(_modbusSlaveBuffer[0] == 0) {_modbusSlaveTCPClient.stop(); return;}
    _modbusSlaveMBAPBuffer[4] = highByte(_modbusSlaveBufferSize);
    _modbusSlaveMBAPBuffer[5] = lowByte(_modbusSlaveBufferSize);
    _modbusSlaveTCPClient.write( _modbusSlaveMBAPBuffer, 6 );
    _modbusSlaveTCPClient.write( _modbusSlaveBuffer, _modbusSlaveBufferSize );
    _modbusSlaveBufferSize = 0;
    }
    byte _modbusGetSlaveRxBuffer()
    {
    byte currentByte = 0;
    boolean bBuffOverflow = false;
    byte currentByteIndex = 0;
    _modbusSlaveBufferSize = 0;
    while (_modbusSlaveTCPClient.available() ) {currentByte = _modbusSlaveTCPClient.read();
    if (currentByteIndex = 64) bBuffOverflow = true;}
    currentByteIndex++;
    }
    if (bBuffOverflow) {return -3; }
    return _modbusSlaveBufferSize;
    }
    byte process_modbus_FC3(byte table)
    {
    int startAddr = word( _modbusSlaveBuffer[2], _modbusSlaveBuffer[3] );
    int byteRegsno = word( _modbusSlaveBuffer[4], _modbusSlaveBuffer[5] );
    int i;
    int value;
    byte index;
    _modbusSlaveBuffer[ 2 ] = byteRegsno * 2;
    _modbusSlaveBufferSize = 3;
    for (i = startAddr; i < startAddr + byteRegsno; i++) {
    index = modbusSlaveIndexForAddres(i, table);
    if (table == 4) {value = _modbusSlaveDataTable_4[index]; }
    _modbusSlaveBuffer[ _modbusSlaveBufferSize ] = highByte(value);
    _modbusSlaveBufferSize++;
    _modbusSlaveBuffer[ _modbusSlaveBufferSize ] = lowByte(value);
    _modbusSlaveBufferSize++;
    }
    _modbusSlaveSendTxBuffer();
    return _modbusSlaveBufferSize + 2;
    }
    byte process_modbus_FC6()
    {
    int addres = word( _modbusSlaveBuffer[2], _modbusSlaveBuffer[3] );
    int index;
    index = modbusSlaveIndexForAddres(addres, 4);
    _modbusSlaveDataTable_4[index] =word( _modbusSlaveBuffer[4], _modbusSlaveBuffer[5] );
    _modbusSlaveBufferSize = 6;
    _modbusSlaveSendTxBuffer();
    return _modbusSlaveBufferSize + 2;
    }
    byte process_modbus_FC16( )
    {
    byte func = _modbusSlaveBuffer[1];
    int startAddr = _modbusSlaveBuffer[2] << 8 | _modbusSlaveBuffer[3];
    int byteRegsno = _modbusSlaveBuffer[4] << 8 | _modbusSlaveBuffer[5];
    int i;
    int index;
    _modbusSlaveBuffer[4] = 0;
    _modbusSlaveBuffer[5] = byteRegsno;
    _modbusSlaveBufferSize = 6;
    for (i = 0; i < byteRegsno; i++) {
    index = modbusSlaveIndexForAddres((startAddr+i), 4);
    _modbusSlaveDataTable_4[index] =word( _modbusSlaveBuffer[ 7 + i * 2 ], _modbusSlaveBuffer[8 + i * 2 ]);
    }
    _modbusSlaveSendTxBuffer();
    return _modbusSlaveBufferSize +2;
    }

    0
  10. Спасибо конечно, но боюсь этот код для меня сложноват будет, не осилить мне его. Я надеялся что будет намного проще……………..нельзя ли как нибудь к имеющимся скетчам — ModBus RTU Slave и ModBus RTU Master прицепить скетч TCP клиента ( sketch_64_3 ) точнее не прицепить а воссоединить их.

    0
    • Подвяжите библиотеку
      #include
      #include
      #include

      и копируйте код что я выложил прямо в скетч
      компилируйте и все заработает

      0
      • #include SPI.h
        #include IPAddress.h
        #include UIPEthernet.h
        не вставляется в угловых скобках текст
        названия в угловых скобках

        0
  11. Arsbond мне важен код не столько для работы а сколько для понимания происходящего процесса ну и чтоб я его мог переделать подстроить под свои нужды.
    В вашем коде я не вижу этого процесса я новичок, голову сломаю, уж извините ……может и разберусь лет через пять

    0
  12. Здравствуйте!
    Вам спасибо большое за уроки. Я уже учился в России, но сейчас живу и работаю во Вьетнаме. Сейчас меня интересуют проблемы обмена данными между Ардуино и компьютером по Ethernet. Не могли бы Вы помочь мне?
    Моя электронная почта: thanhtrungnguyen234@gmail.com.
    Заранее благодарю!

    0
  13. Большое спасибо за урок и код. К сожалению, он у меня хотя и загрузился, но работать отказался. Тут 2 вопроса:
    1) что ставить DND и GW ,если все происходит в локальной сети 192.168.140.х
    2) как вообще можно протестировать шильд ENC28J60 Ethernet для Arduino Nano на предмет жив он или нет ? Подключен верно (он втыкается в Нано), диоды на RJ-45: зеленый горит, оранжевый мигает.
    3) Готов пойти на скромный материальный взнос за запуск ТСР — сервера на ардуне с клиентом на сторонней машине чтобы передавать данные аналоговых контактов (контакты считывать умею). Ардуина живая, на ней запускал 20х2 LCD экран.

    0
  14. Здравствуйте, Эдуард!
    Поясните пожалуйста почему строка кода ответ сервера клиенту выглядит — client.println(«Server ready»); — ведь эта команда отправки данных от клиента серверу, а не сервера клиенту. Логичней было бы — server..println(«Server ready»);

    Прочитал много литературы, но никто пояснения не дает.

    Спасибо за Ваши уроки.

    0
    • Здравствуйте!
      В программе мы создали сервер:
      EthernetServer server(2000);
      Т.е. наше устройство — это сервер с определенным портом для прослушивания.
      Для работы с клиентом мы создали объект клиент:
      EthernetClient client;
      Это не устройство, а объект, содержащий данные подключенного клиента. Сам клиент расположен на другом устройстве. Объект клиент мы используем для доступа к этому клиенту. Считайте, что это сложный адрес, по которому сервер посылает данные.

      0
  15. Эдуард, спасибо Вам большое за разъяснения и оперативность! У меня был подобный ход мыслей, но хотелось подтверждение специалиста. Единственно смущала сложная логика процесса. Не могли бы Вы мне разъяснить почему так и подтвердить ход моих мыслей исходя из Ваших разъяснений: — созданный нами объект клиент пишет на сервер, а сервер посылает ответ «физическому» клиенту.

    Думаю и остальным форумчанам будет интересно.

    0
  16. не компилируется код на Arduino IDE 1.8.9.

    вываливается куча предупреждений и стоит на месте. может памяти не хватает на Arduino Nano

    0
  17. стать хорошая …а каким образом реализовать 2 сервера на 1 Дуине ? к примеру ХТТП и удп 1 естесно внешний второй внутренний для связи с наноми … ?

    0
  18. Эдуард, большое спасибо вам за статьи! Я только начинаю разбираться в работе с сетью. Посоветуйте как реализовать на двух ENC28J60 и двух микроконтроллерах прозрачный мост (типа удлинитель) LAN. То есть надо сделать удлинитель сети Ethernet через COM порт (либо RS 485, радио модем). Допустим микроконтроллер принимает пакет, передает его другому м/к по своей линии связи, а тот воспроизводит этот пакет на ENC28J60 с другой стороны.
    Надеюсь понятно объяснил. Готовых недорогих устройств не нашел. Подскажите, куда копать 🙂

    0
  19. Эдуард , добрый день!

    У меня Arduino UNO и ENC28J60 на базе W5500. Нужно ли вносить какие-либо изменения в Ваши скетчи (библиотеки и пр.)?

    0
  20. Эдуард, здравствуйте
    Подскажите про пример с TCP клиент. Я правильно понимаю, что TCP Builder выступает сервером, а ардуино — клиентом? Тогда зачем в TCP Builder нажимать кнопку Connect? Откуда сервер может знать с какого адреса к нему будут подключаться клиенты? И что за порт 1025 — где он прописывается на стороне клиента? И куда собственно TCP Builder пытается присоединиться, нажимая кнопку Connect, если он сам и есть сервер? У меня при нажатии кнопки Connect выходит сообщение «чё-то там попытка неудачна»

    0
      • Я наверное чего-то глобально не понимаю… Вот же написано: «Попробуем противоположный вариант: клиент реализован на Ардуино, сервер – на компьютере.»…

        0
        • Здравствуйте!
          Да, я не на тот пример посмотрел. Конечно, компьютер сервер. Я уже не помню всех деталей. Но я все проверял, все у меня работает, как написано.
          Кнопка Connect, скорее всего, запускает прослушивание у сервера. А какое сообщение об ошибке вы получаете? У вас IP-адрес сервера правильный?

          0
          • У вас в тексте небольшая ошибка. В этом примере нужно нажимать не кнопку Connect, а кнопку Listen — она действительно запускает прослушивание. Пример работает, спасибо. А 1025 — это видимо порт, через который клиент подключился к серверу. Вопросик — не подскажете как «переподключиться»? Я все сделал как написано, все получилось, данные туда-сюда пересылаются. Нажимаю Destroy Socket и пытаюсь все сделать еще раз — не получается. При нажатии на Listen вылетает ошибка типа «данный адрес уже занят» (не помню точно текст ошибки). Такое ощущение что второй раз с одного и того же адреса клиенту не разрешено подключаться или порт занят, или как это называется? Перезагрузка ПК помогает, но как-то не хочется каждый раз перезагружаться…

            0
          • Здравствуйте!
            Я так давно писал этот урок. Но точно помню, что подключался несколько раз и компьютер не перезагружал. А перезапуск программы не помогает?

            0
  21. Перезапуск программы не помогает. Ну и шут с ней. Главное подключиться удается и данные туда-сюда ходят. Спасибо

    0
  22. Доброе времени суток.
    Пытался реализовать связь между двумя arduino, сделав один из них TCP сервером, а другой клиентом.
    И, по идеи, связь есть.
    Но почему-то подключение постоянно отключается. И подключается не с первого раза, а с попытки 5 или больше.
    (функция client.connect(ipServer, LOCAL_PORT) возвращает false)

    0
    • Скетч сервера:

      #include
      #include

      #define LOCAL_PORT 2000
      #define RELE 7

      #define CLOSE 0
      #define OPEN 1

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

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

      void setup() {
      Ethernet.begin(mac, ip); //Ethernet.begin(mac, ip, myDns, gateway, subnet); // инициализация контроллера
      server.begin(); // включаем ожидание входящих соединений
      Serial.begin(115200);
      Serial.println(«———-TCP SERVER START—————-«);
      pinMode(RELE, OUTPUT); // вывод реле
      digitalWrite(RELE, CLOSE);

      }

      void loop() {
      delay(10);
      client = server.available(); // ожидаем объект клиент
      if (client){
      // есть данные от клиента
      if(client.available() > 0) {
      char chr = client.read(); // чтение символа
      Serial.print(«received: «); Serial.println(chr);
      if(chr == ‘0’) digitalWrite(RELE, CLOSE); // если 0, гасим светодиод
      if(chr == ‘1’) digitalWrite(RELE, OPEN); // если 1, зажигаем светодиод
      }
      }
      }

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

      #include
      #include

      #define LOCAL_PORT 2000
      #define BUTTON 7
      #define LED 2

      #define CLOSE 0
      #define OPEN 1

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

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

      void setup() {
      Ethernet.begin(mac, ipClient); // инициализация контроллера
      pinMode( LED, OUTPUT);
      digitalWrite(LED, CLOSE);
      Serial.begin(115200);
      delay(100);
      Serial.println(«connecting…»);
      // устанавливаем соединение с сервером
      while(!client.connect(ipServer, LOCAL_PORT)) {
      client.flush();
      client.stop();
      Serial.println(«connecting…»);
      delay(500);
      }
      Serial.println(«connected»);

      }

      void loop() {

      if(!digitalRead(BUTTON)){
      delay(7);
      if(!digitalRead(BUTTON)){
      //char inChr = Serial.read();
      if (client.connected()) {
      client.print(‘1’);
      digitalWrite(LED, OPEN);
      delay(2000);
      client.print(‘0’);
      digitalWrite(LED, CLOSE);
      }
      }
      }

      // если сервер отключился, останавливаем клиент
      if (!client.connected()) {
      Serial.println(«disconnecting.»);
      client.flush();
      client.stop();
      while(!client.connect(ipServer, LOCAL_PORT)) {
      Serial.println(«connecting…»);
      delay(500);
      }
      Serial.println(«connected»);
      }
      }

      0
    • Ну раз подключается, хоть и далеко не идеально, значит скетч всё-таки работает. Посудите сами: если в скетче есть ошибки (в плане установления соединения), то каким образом может происходить соединение?
      В просторах очень много нареканий на стабильность этого модуля (ENC28J60). А в вашем случае вообще мог попасться нелучший экземпляр.
      Для отладки прототипа приложения можно и на этом модуле потренироваться, но в дело пускать лучше что нибудь понадежнее.
      Есть модули для которых переделка кода c UIPEthernet.h на другую библиотеку особых усилий не потребует (или вообще не понадобится).

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

      0
  23. Вопрос автору сайта и другим знатокам.

    Я думаю все уже попробовали ситуацию с первым примером (МК это сервер, ПК – клиент), когда к серверу подключаются более одного клиента (через TCP/IP Builder). И все заметили, что client.write(chr) отсылает сообщение на все подключенные машины-клиенты, ибо:

    server.write(что-нибудь);
    Отправляет данные всем клиентам, подключенным к серверу.

    Таковы законы библиотеки.

    Но коль раннее речь заходила о свитчах, то каким образом можно реализовать отправку пакета от сервера к клиенту именно с нужным MAC-адресом (или IP), соответственно и путь доставки, чтобы просчитался оптимальный (я думаю это уже на совести свитчей)?

    Возможно ли это осуществить средствами UIPEthernt?

    Или тут нужна другая библиотека, а может быть вообще другой модуль (хотя последнее – вряд ли)?

    Понятное дело, я могу сам организовать фильтр получаемых пакетов данных на стороне каждого клиента (по принципу: не твоё? – не читай!). Но для чего тогда сетевая аппаратная часть? Зачем грузить ресурсы МК? Ведь пакет всёравно придется принять. Это всё как то не по людски, через справа налево.

    Заранее спасибо всем, кто согласится помочь.

    0
  24. Здравствуйте, подскажите про пример с TCP клиент.
    Как я могу реализовать передачу последовательности чисел так, чтобы они записывались в определенные переменные в программе?
    Например, я знаю, что в начале последовательности чисел, будет стоять число трехзначное, которое мне нужно записать в переменную «а», в функции, которая выполняет b=a+1.
    Проще говоря, как передать массив чисел? И уже поэлементно
    доставать и записывать значения в переменные? Возможно ли такое?
    Заранее огромное спасибо!

    0
    • Здравствуйте!
      Вы сами определяете формат передачи данных.
      Например, если последовательность данных фиксированной длины, можете просто передавать числа в тестовом виде через пробелы.
      Можете в начале передать длину массива.
      Если могут быть функционально разные данные, можете передать в начале пакета символ, который сообщит, как интерпретировать данные.
      Могут быть более сложные протоколы.

      0
  25. Здравствуйте! Вижу что тема жива и такой вопрос: использую не ардуину, а ESP32 devkit v1. Вроде как должны быть те же яйца, только в профиль. но не получается подключить ENC модуль нормально. У нее 2 SPI, ни по одному коннекта нет. Статический ip не помогает. Может что в библиотеке надо изменить? Или прописать как-нибудь пины SPI, дав понять плате куда слать пакеты 😀

    0
    • Пример кода и схема соединений?
      Сам работаю параллельно с ESP, там действительно все не совсем так. Кое что приходится вручную переделывать.

      0
      • Использую код почти как в первом примере:
        #include
        #include

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

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

        void setup() {
        if(Ethernet.begin(mac))
        {
        Serial.println(» DHCP problem»);
        Ethernet.begin(mac, ip, myDns, gateway, subnet);
        }
        server.begin(); // включаем ожидание входящих соединений
        Serial.begin(115200);
        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);
        }
        }
        }

        Подключение 5v и GND беру с Arduino Nano (единственный стабильный источник питания который смог найти), CLK -> D14, MISO -> D12, MOSI -> D13, CS ->D15

        С данным скетчем не получаю ip от роутера, ставится статический, потом с телефона через приложение пытаюсь установить коннект — не выходит. С компа тоже не пингуется адрес.

        0
        • ну во первых #include надо прописать в тексте комментария по другому (без или «»), их не видно.
          потом:
          для ESP 32 вывода для SPI я пока вижу такие:
          сигнал / вывод / физический пин
          SCK / GPIO18 / 35
          SO / GPIO19 / 38
          SI / GPIO23 / 36
          CS / GPIO5 / 36

          в библиотеке возможно прописано так:
          #define ENC28J60_SPI_CS (1<<PB4)
          #define ENC28J60_SPI_MOSI (1<<PB5)
          #define ENC28J60_SPI_MISO (1<<PB6)
          #define ENC28J60_SPI_SCK (1<<PB7)
          я пока не копался в исходнике, т.к. не знаю что у Вас подключено.
          А так же где в ESP 32 D12 — D15 тоже не рассмотрел.
          Вобщем надо эти вещи уточнять и скорее всего проводить хирургическое вмешательство в исходники.
          Буду рад поучаствовать, если все будет уточнено.

          0
          • подключаю библиотеки следующие
            #include SPI.h
            #include UIPEthernet.h

            По поводу пинов: ориентируюсь на эту картинку https://myrobot.ru/wiki/uploads/Experiences/esp32_pinout.png

            В папке utility библиотеки UIPEthernet нашел файл ENC28J60Network.h в нем описано подобное, но для меня это сложно)

            0
          • в ENC28J60Network.h описано:

            #define ENC28J60_CONTROL_CS SS
            #define SPI_MOSI MOSI
            #define SPI_MISO MISO
            #define SPI_SCK SCK
            #define SPI_SS SS

            пока нет времени искать дальше (я на работе), но попробуйте узнать значения этих констант, прописав прамо в своем коде
            Serial.println(SPI_MOSI, DEC) и т.д.
            что за значения выдаст?
            их можно будет сравнить с номерами пинов на каринке распиновки

            0
          • код я немного проще написал — без плавающего IP (но для atmega328p)

            #include
            #include

            byte mac[] = {0xAE, 0xB2, 0x26, 0xE4, 0x4A, 0x54}; // MAC-адрес
            byte my_ip[] = {192, 168, 0, 11}; // IP-адрес клиента
            byte ipServ[] = {192, 168, 0, 10}; // IP-адрес сервера

            EthernetClient client;

            void setup() {
            Ethernet.begin(mac, my_ip); // инициализация контроллера
            Serial.begin(9600);
            delay(200);
            Serial.println(«connecting…»);
            delay(500);
            // устанавливаем соединение с сервером
            if (client.connect(ipServ, 2000)) {
            Serial.println(«connected»); // успешно
            client.print(‘1’); // на пробу
            }
            else {
            Serial.println(«connection failed»); // ошибка
            }
            }

            0
        • просю написать #include _имя библиотеки_ без стрелок.
          так его не видно!

          0
          • Пины пишет те, к которым подключен ethernet модуль.
            SPI_MOSI: 23
            SPI_MISO: 19
            SPI_SCK: 18
            SS: 5

            Коннект ни на комп, ни на роутер не проходит. Подключен прямым кабелем к роутеру. Если хотите продолжить дискуссию далее, то предлагаю перейти в другое место:
            почта: s.polonski@mail.ru
            VK: https://vk.com/s.polonski

            0
  26. Соорудил маленькую сеть:
    Сервер на МЕГе, клиент на UNO.
    Задача сцепки пока простая — клиент с определенной периодичностью (допустим 1 сек) отсылает серверу простые сообщения размером в 1 байт. Сервер, получая эти сообщения, отправляет их копию в монитор порта через Serial.println(chr, DEX).
    Всё работает нормально за исключением того что ENC со стороны клиента время от времени решает не отсылать полученный байт серверу, а копит его и последующие в свой буфер. Потом отсылает «пачкой». Естественно без потерь накопленного, но с потерей времени.
    Выглядит примерно так: с клиентской стороны собирается несколько пакетов данных и только потом они всей накопившейся очередью шлются дальше в сеть. Ни размер пакетов, ни их количество, ни их частота и т.п. не влияют на то: переданы они будут сразу или будут копиться в буфере для последующей передачи всей «гурьбой». Всё происходит как говорится «как ему вздумается».
    То доставка пакетов происходит сразу, то копится в буфере.
    Напоминаю — данные шлются с интервалом в 1 секунду. Задержка может составлять как раз эти секунды умноженные на то сколько посылок ENC решит забуферизировать.
    Есть ли у кого нибудь способы запретить эту буферизацию в ENC28J60 и слать сразу данные на свитч?

    0

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

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

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".