07.07 - 14.07 вихідні
Обробимо замовлення з 15.07

Двухсторонняя связь модулями ENC28J60 между контроллерами Arduino

Я покажу здесь решение, казалось бы, тривиальной простейшей задачи - обмен данными между контроллерами Arduino по сети Ethernet при помощи самых дешевых модулей связи ENC28J60.

Возьмем два модуля Ethernet ENC28J60 и два контроллера Arduino Nano, соединим их через сеть Ethernet.

ENC28J60 имеют коммуникацию SPI - для нас это означает, что соединяются они с контроллером через 4 сигнальных провода.

Подключение модулей делаем согласно следующей таблице:

VCC -  5V (питать нужно по возможности не от платы контроллера)
GND -  GND
SCK - Pin 13
SO  - Pin 12
SI  - Pin 11
CS  - Pin 10

У меня получилась следующая картина подключенных модулей к контроллерам. Один из Nano стоит на монтажном модуле, но вы можете его исключить - он здесь не имеет значения.

Дальше будем покорять UDP-протокол связи. Скачаем библиотеку UIPEthernet.h c гитхаба и установим в Arduino IDE. По сути будем использовать два стандартных примера из этой библиотеки (UdpClient, UdpServer), но с некоторыми изменениями. Один контроллер-клиент будет с заданной периодичностью слать полезные сообщения контроллеру-серверу, а тот при приеме сообщения будет отвечать полезным клиенту сообщением.

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

Программа контроллера-клиента:

#include <UIPEthernet.h>
EthernetUDP udp;
unsigned long next;
void setup() {
  Serial.begin(9600);
  uint8_t mac[6] = {0x00,0x01,0x06,0x03,0x04,0x05};
  Ethernet.begin(mac,IPAddress(192,168,0,6));
  next = millis()+5000;
  udp.begin(5000);
}
void loop() {
  int success;
  int len = 0;
  if (((signed long)(millis()-next))>0)
    {
      do
        {
          success = udp.beginPacket(IPAddress(192,168,0,1),5000);
          Serial.print("beginPacket: ");
          Serial.println(success ? "success" : "failed");
          //beginPacket fails if remote ethaddr is unknown. In this case an
          //arp-request is send out first and beginPacket succeeds as soon
          //the arp-response is received.
        }
      while (!success && ((signed long)(millis()-next))<0);
      if (!success )
        goto stop;
      success = udp.write("hello world from client");
      Serial.print("bytes written: ");
      Serial.println(success);
      success = udp.endPacket();
      Serial.print("endPacket: ");
      Serial.println(success ? "success" : "failed");
      do
        {
          //check for new udp-packet:
          success = udp.parsePacket();
        }
      while (!success && ((signed long)(millis()-next))<0);
      if (!success )
        goto stop;
      Serial.print("received: '");
      do
        {
          int c = udp.read();
          Serial.write(c);
          len++;
        }
      while ((success = udp.available())>0);
      Serial.print("', ");
      Serial.print(len);
      Serial.println(" bytes");
      //finish reading this packet:
      udp.flush();
      stop:
      udp.stop();
      next = millis()+5000;
      
    //restart with new connection to receive packets from other clients
    Serial.print("restart connection: ");
    Serial.println (udp.begin(5000) ? "success" : "failed");
    }
}

Здесь можете для своей сети поменять IP модуля ENC28J60 клиента

Программа контроллера-сервера:

#include <UIPEthernet.h>
EthernetUDP udp;
void setup() {
  Serial.begin(9600);
  uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};
  Ethernet.begin(mac,IPAddress(192,168,0,1));
  int success = udp.begin(5000);
  Serial.print("initialize: ");
  Serial.println(success ? "success" : "failed");
}
void loop() {
  //check for new udp-packet:
  int size = udp.parsePacket();
  if (size > 0) {
    do
      {
        char* msg = (char*)malloc(size+1);
        int len = udp.read(msg,size+1);
        msg[len]=0;
        Serial.print("received: '");
        Serial.print(msg);
        free(msg);
      }
    while ((size = udp.available())>0);
    //finish reading this packet:
    udp.flush();
    Serial.println("'");
    int success;
    do
      {
        Serial.print("remote ip: ");
        Serial.println(udp.remoteIP());
        Serial.print("remote port: ");
        Serial.println(udp.remotePort());
        //send new packet back to ip/port of client. This also
        //configures the current connection to ignore packets from
        //other clients!
        success = udp.beginPacket(udp.remoteIP(),udp.remotePort());
        Serial.print("beginPacket: ");
        Serial.println(success ? "success" : "failed");
    //beginPacket fails if remote ethaddr is unknown. In this case an
    //arp-request is send out first and beginPacket succeeds as soon
    //the arp-response is received.
      }
    while (!success);
    success = udp.println("hello world from server");
    Serial.print("bytes written: ");
    Serial.println(success);
    success = udp.endPacket();
    Serial.print("endPacket: ");
    Serial.println(success ? "success" : "failed");
    udp.stop();
    //restart with new connection to receive packets from other clients
    Serial.print("restart connection: ");
    Serial.println (udp.begin(5000) ? "success" : "failed");
  }
}

Здесь тоже можете поменять IP модуля в тексте программы.

После загрузки программ включаем все в работу и в Мониторе порта наблюдаем следующие строки.

Сервер должен написать нечто подобное:

received: 'hello world from client'
remote ip: 192.168.0.6
remote port: 5000
beginPacket: success
bytes written: 25
endPacket: success
restart connection: success
Клиент будет писать следующее:
beginPacket: success
bytes written: 24
endPacket: success
received: 'hello world from server
', 25 bytes
restart connection: success
Выводы
Связь получается довольно интересной. В одной сети можно объединять множество контроллеров. В некоторых помещениях, опутаных сетью Ethernet вдоль и поперёк, такой способ объединения будет весьма удобен.
Периодичность обмена я пробовал до 400 мс - отрабатывает прекрасно - даже можно уменьшать под быстрые задачи.
Что насторожило - микросхема на модуле ENC28J60 сильно греется при работе. На форумах предлагают делать отдельное питание. А я подумываю о радиаторе.

Написать отзыв

Примечание: HTML разметка не поддерживается! Используйте обычный текст.
    Плохо           Хорошо
Концевой выключатель или концевик

Концевой выключатель или концевик

Имеет одну группу переключающихся контактов. На кончике рычага установлен подвижный ролик.Максимальн..

18.07грн.

Стойка латунная М3 высотой 10мм шестигранник мама-папа

Стойка латунная М3 высотой 10мм шестигранник мама-папа

Латунная стойка мама-папа с резьбой М3 и шестигранным внешним сечениемПрименяется при конструировани..

4.77грн.

ST-Link V2 программатор

ST-Link V2 программатор

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

81.49грн.

KiCad бесплатная программа рисования схем и разработки печатных плат

KiCad бесплатная программа рисования схем и разработки печатных плат

Вас приветствует сайт гикматик! Сегодня поговорим о мега-полезной и доступной программе KiCad. Пораб..

Модуль i2c для LCD индикаторов

Модуль i2c для LCD индикаторов

Модуль припаивается на LCD-индикатор типа LCD 2004, LCD 1602.Обеспечивает связь жидкокристаллическог..

25.11грн.