TCP-соединение Ethernet с Arduino
С программной точки зрения установление соединения Ethernet con Arduino Это очень просто. Для этого используйте Ethernet-библиотека. Эта библиотека предназначена для Ethernet-экран который основан на интегрированном W5100, но существуют и другие платы или модули и/или которые используют другие интегрированные платы, например ENC28J60. Чтобы упростить его использование и повысить совместимость, другие библиотеки используют (почти) то же самое. API что Ethernet-библиотека, вам останется только заменить альтернативную библиотеку на оригинальную или включить ее (когда имя другое) на ее место, даже если в коде используются такие же (или очень похожие) функции. В моем случае я использую UIPEthernet-библиотека de Норберт Тручссесс следуя тому же процессу, который я собираюсь описать в этом тексте.
1. Определите соединение Ethernet
Собираетесь ли вы взять на себя роль клиент такой как сервер, прежде всего вам нужно определить связь с функцией начинать () который можно передать в качестве параметра только MAC-адрес и дождаться сервера DHCP в сети назначить IP-адрес и остальную часть конфигурации или также можно указать (опционально) дополнительные параметры, пока не будет определена полная конфигурация:
- MAC-адрес (о чем уже говорилось)
- IP-адрес щита или модуля
- IP-адрес сервера DNS (только один сервер)
- IP-адрес шлюз
- Сетевая маска
Желательно указывать все параметры, если только их вывод не является обычным, чтобы избежать неправильной конфигурации (например, что шлюз не является первым адресом сети).
Из вышесказанного кажется понятным, что данные, представляющие IP-адреса, должны использоваться довольно часто, поэтому в библиотеку включен класс Айпи адрес из которого можно создавать экземпляры объектов IP-адреса. Параметры, которые его определяют, — это четыре байта адреса. IPV4
La MAC-адрес Для этой библиотеки он определен как 6-байтовый массив. MAC-адрес — это (должен быть) уникальный идентификатор, в котором первые байты указывают производителя и модель, а последние — конкретное устройство. Интегрированный ENC28J60 не включает MAC-адрес, если вы не решите также приобрести Встроенный MAC-адрес от Microchip (или целый квартал да обращений к IEEE если пробег устройств достаточно велик, чтобы оно того стоило). Если у вас нет MAC-адреса, вы можете придумать его, позаботившись о том, чтобы он не конфликтовал с другими в сети, где находится устройство.
Если настройка выполняется с помощью DHCP-сервера, а не «вручную», функция локальныйIP() Полезно просмотреть адрес, который сервер назначил модулю. Для продления присвоенного адреса (если соответствующее время истекло) Ethernet-библиотека предоставляет функцию поддерживать() который также проинформирует, вернув код, соответствующий статусу продления:
- Операция не дала результата
-
Ошибка обновления IP-адреса
Использование назначенного IP-адреса на том же сервере не может быть продлено. - IP-адрес успешно обновлен
-
Перепривязка IP-адреса не удалась
Использование назначенного IP-адреса не может быть расширено ни на одном сервере. - IP-адрес успешно переназначен
Используя информацию, полученную на данный момент, вы можете написать пример того, как будет инициироваться соединение Ethernet путем настройки IP-адреса через DHCP-сервер в сети. Следующий пример кода пытается обновить IP-адрес каждый определенный период времени и сообщает результат.
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
|
//#include <UIPEthernet.h> // Librería Ethernet que usaré después con el módulo ENC28J60
#include <Ethernet.h> // Librería Ethernet estándar
#define ESPERA_RENOVACION_IP 60000 // Un minuto
unsigned long reloj;
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
byte estado_DHCP;
void setup()
{
Serial.begin(9600);
Ethernet.begin(direccion_mac);
mostrar_direccion_ip();
reloj=millis()+ESPERA_RENOVACION_IP;
}
void loop()
{
if(millis()>reloj) // Tratar de renovar la IP cada ESPERA_RENOVACION_IP milisegundos
{
estado_DHCP=Ethernet.maintain();
switch(estado_DHCP)
{
case 0:
Serial.println(“Sin cambios”);
break;
case 1:
Serial.println(“Error al renovar la dirección IP”);
break;
case 2:
Serial.println(“Dirección IP renovada correctamente”);
break;
case 3:
Serial.println(“Error al reasignar la dirección IP”);
break;
case 4:
Serial.println(“Dirección IP reasignada correctamente”);
break;
default:
Serial.println(“Error desconocido”);
}
mostrar_direccion_ip();
reloj=millis()+ESPERA_RENOVACION_IP;
}
}
void mostrar_direccion_ip()
{
Serial.print(“Dirección IP actual [“);
Serial.print(Ethernet.localIP()); // Mostrará la dirección IP asignada por el servidor DHCP
Serial.println(“]”);
}
|
В приведенном ниже примере IP-адрес и остальная конфигурация назначаются вручную с использованием объектов. Айпи адрес чтобы сделать его более удобным для чтения и (в случае более сложного кода) избежать ошибок, которые могли возникнуть, если адрес был (неправильно) записан каждый раз, когда он использовался.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <UIPEthernet.h> // Librería Ethernet usada con el módulo ENC28J60
// #include <Ethernet.h> // Librería Ethernet estándar
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
IPAddress direccion_ip_fija(192,168,1,69); // Dirección IP elegida para el módulo
IPAddress servidor_dns(87,216,170,85); // Servidor DNS OpenNIC (de Alejandro Bonet, http://opennic.alargador.org)
IPAddress puerta_enlace(192,168,1,14); // Dirección IP del router
IPAddress mascara_red(255,255,255,0); // Máscara de la red
void setup()
{
Serial.begin(9600);
while(!Serial){;} // He usado una placa Leonardo, me toca esperar a que el puerto serie esté operativo
Ethernet.begin(direccion_mac,direccion_ip_fija,servidor_dns,puerta_enlace,mascara_red);
Serial.print(“Dirección IP asignada [“);
Serial.print(Ethernet.localIP()); // Poco misterio, devolverá la dirección IP asignada manualmente
Serial.println(“]”);
}
void loop()
{
// Sólo es un ejemplo de configuración, no hace nada
}
|
2. Запустите соединение в режиме клиента или сервера.
При инициировании соединения в режиме сервера разрабатываемая микроуправляемая система слушает запросы от других систем. Чтобы начать соединение в качестве сервера, используйте EthernetСервер() и в качестве параметра указывается порт, который будет слушать сервер. EthernetСервер() является конструктором класса сервер, который поддерживает все операции Ethernet в качестве сервера. Хотя самое ортодоксальное — сделать вызов конструктора EthernetСервер(), нередко можно найти примеры, которые напрямую используют класс сервер или альтернативные библиотеки для подключения Ethernet, которые решили использовать эту систему создания экземпляров.
Соединение в качестве клиента — это соединение, которое отправляет запросы серверной системе, которая ожидает их и отвечает на них соответствующим образом. Чтобы инициировать соединение в качестве клиента, используйте EthernetКлиент() что такое конструктор класса "Клиент" происхождение всех операций Ethernet в качестве клиента.
В отличие от того, что происходит в режиме сервера, который, как предполагается, работает с момента создания экземпляра класса (хотя он будет отвечать клиентам только в том случае, если это действительно так), вы должны убедиться, что клиентское соединение готово, прежде чем использовать его. Клиентский объект, который создается при инициировании соединения, можно запросить, чтобы узнать, доступен ли он. Например, операции запроса могут быть включены в структуру если (EthernetClient) выполнять их только тогда, когда доступно клиентское соединение.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <UIPEthernet.h> // Librería Ethernet usada con el módulo ENC28J60
// #include <Ethernet.h> // Librería Ethernet estándar
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
IPAddress direccion_ip_fija(192,168,1,69); // Dirección IP elegida para el módulo
IPAddress servidor_dns(87,216,170,85); // Servidor OpenNIC (de Alejandro Bonet, http://opennic.alargador.org)
IPAddress puerta_enlace(192,168,1,14); // Dirección IP del router
IPAddress mascara_red(255,255,255,0); // Máscara de la red
EthernetServer servidor=EthernetServer(80); // Puerto 80 (típico de un servidor HTTP)
void setup()
{
Serial.begin(9600);
while(!Serial){;} // He usado una placa Leonardo, hay que esperar a que el puerto serie esté operativo
Ethernet.begin(direccion_mac,direccion_ip_fija,servidor_dns,puerta_enlace,mascara_red);
servidor.begin();
Serial.println(“Servidor HTTP iniciado”);
}
void loop()
{
// Sólo es un ejemplo de configuración, no hace nada productivo
}
|
3. Установите соединение в качестве клиента.
Как уже было сказано, как только соединение установлено, именно клиент берет на себя инициативу по выполнению запросов. Сервер будет ждать этой инициативы и ответит соответствующим образом. Таким образом, клиент подключается к серверу, для этого мы используем подключить () указывая сервер в качестве параметров (IP-адрес или URL) И порт в том, кто слушает.
По результату операции функция вернет значения
- ( ) Соединение успешно установлено.
- Установление соединения
- ( ) Тайм-аут истек, соединение не установлено.
- ( ) Сервер не найден или отвечает некорректно
- ( ) Соединение было разорвано до полного установления
- ( ) Ответ сервера неверен
Прежде чем начинать делать запросы, необходимо убедиться в работоспособности соединения с функцией связанный() это вернется если он уже доступен или иначе.
Пример ниже иллюстрирует подключение в качестве клиента, каждые 10 секунд проверяя, есть ли соединение с сервером (он не предназначен для чего-то продуктивного, просто для демонстрации синтаксиса функций), что, кстати, производственному веб-серверу это бы не очень понравилось.
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
|
#include <UIPEthernet.h> // Librería Ethernet usada con el módulo ENC28J60
// #include <Ethernet.h> // Librería Ethernet estándar
#define INTERVALO_CONSULTA 10000 // Se comprueba cada 10 segundos si hay conexión
#define LED_CONEXION 13 // Pin del LED que parpadea cuando hay conexión
#define TIEMPO_PARPADEO 300 // Milisegundos entre encendido/apagado del LED que indica conexión
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
IPAddress direccion_ip_fija(192,168,1,69); // Dirección IP elegida para el módulo
IPAddress servidor_dns(87,216,170,85); // Servidor OpenNIC (de Alejandro Bonet, http://opennic.alargador.org)
IPAddress puerta_enlace(192,168,1,14); // Dirección IP del router
IPAddress mascara_red(255,255,255,0); // Máscara de la red
IPAddress ip_servidor_web(192,168,1,21); // Dirección IP del servidor web (en la intranet)
//char url_servidor_web[]=”sleepmanager.onironauta.es”; // URL poético para un gestor de sueño (en Internet)
EthernetClient cliente=EthernetClient();
byte estado_conexion;
boolean anteriormente_conectado;
boolean estado_led_conexion;
unsigned long cronometro_parpadeo;
unsigned long cronometro_consulta;
void conectar_ethernet()
{
estado_conexion=cliente.connect(ip_servidor_web,80); // Conexión desde la intranet
//estado_conexion=cliente.connect(url_servidor_web,80); // Conexión desde Internet
delay(100); // Un pequeño retraso para permitir que se active la conexión
anteriormente_conectado=false;
switch(estado_conexion)
{
case 1:
Serial.println(“Conexión con el servidor SleepManager establecida correctamente”);
anteriormente_conectado=true;
break;
case –1:
Serial.println(“Ha pasado el tiempo de espera sin que se establezca la conexión”);
break;
case –2:
Serial.println(“No se ha encontrado el servidor o no responde correctamente”);
break;
case –3:
Serial.println(“La conexión se ha interrumpido antes de establecerse completamente”);
break;
case –4:
Serial.println(“La respuesta del servidor es incorrecta”);
break;
}
}
void setup()
{
pinMode(LED_CONEXION,OUTPUT);
Serial.begin(9600);
while(!Serial){;} // Esperar al puerto serie de la placa Leonardo
Serial.println(“Conectando con el servidor SleepManager…”);
Ethernet.begin(direccion_mac,direccion_ip_fija,servidor_dns,puerta_enlace,mascara_red);
conectar_ethernet();
estado_led_conexion=false;
cronometro_parpadeo=0;
cronometro_consulta=millis()+INTERVALO_CONSULTA;
}
void loop()
{
if(anteriormente_conectado&&millis()>cronometro_parpadeo)
{
estado_led_conexion=!estado_led_conexion;
digitalWrite(LED_CONEXION,estado_led_conexion);
cronometro_parpadeo=millis()+TIEMPO_PARPADEO;
}
if(millis()>cronometro_consulta)
{
if(!cliente.connected())
{
conectar_ethernet();
}
cronometro_consulta=millis()+INTERVALO_CONSULTA;
}
}
|
4. Отправить данные
Как и другие более известные классы, такие как Последовательный, и при сопоставимом использовании классы "Клиент" y сервер иметь функции
-
Отправляет информацию, используя объект клиента или сервера, из которого она вызывается. Параметр «данные» является единственным байт o колесница в то время как «буфер» представляет собой массив байт o колесница из которых отправляется сумма, равная «длине». Эта функция используется для двоичных операций, по сравнению со следующими двумя, которые обычно зарезервированы для отправки текста.
-
Отправляет клиенту или серверу (в зависимости от класса, из которого он используется) информацию, соответствующую «данным», в виде текста. Если информация не выражена в виде текста (например, это целое число), можно использовать дополнительный параметр «base», с помощью которого можно выбрать преобразование, которое может быть одной из констант BIN, OCT, DEC или HEX, обозначающих соответственно системы счисления, соответствующие двоичной (основание 2), восьмеричной (основание 8), десятичной (основание 10) и шестнадцатеричной (основание 16) системе счисления.
-
Операция идентична предыдущей, за исключением отправки после информации, явно указанной параметром «data», возврата каретки (код 13, который может быть представлен как \r) и конца строки (код 10, который может быть представлен как \r). представлены \n) Эти коды часто обозначаются соответственно аббревиатурой CR (возврат каретки) и НЧ (перевод строки)
Три предыдущие функции возвращают количество отправленных байтов, как и эквивалентные функции класса Последовательный; Как было сказано выше, операция сопоставима.
5. Получите данные
Как и в случае операций отправки данных, операции приема сопоставимы с широко используемыми операциями. Последовательный. Протокол приема также аналогичен: проверьте, есть ли (достаточно) доступных данных (доступен) и в таком случае прочитайте их
-
Возвращает количество байтов, доступных для чтения. Эта функция присутствует в обоих классах "Клиент" в качестве сервер; В первом случае он сообщает количество байтов, отправленных сервером в ответ на запрос и доступных для чтения клиенту (читать), а во втором случае клиент (объект), выполнивший операцию, или false, если таковой нет.
-
Он используется для чтения полученной информации. Эта функция доступна только в классе "Клиент". Если разрабатываемое приложение выполняет роль сервера, то для чтения поступившей информации необходимо создать экземпляр клиентского объекта с ответом функции имеется в наличии () обсуждалось в предыдущем разделе.
Следующий пример представляет собой «caps-сервер», который прослушивает порт 2000 и по возможности отвечает на запросы всем, что было отправлено, с использованием всех заглавных букв. Это можно проверить, например, с помощью PuTTY или просто с Он, конечно, не очень практичен, его цель — лишь показать, как получить данные, отправленные ему клиентом, с сервера.
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
|
#include <UIPEthernet.h> // Librería Ethernet usada con el módulo ENC28J60
//#include <Ethernet.h> // Librería Ethernet estándar
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
IPAddress direccion_ip_fija(192,168,1,69); // Dirección IP elegida para el módulo
IPAddress servidor_dns(87,216,170,85); // Servidor OpenNIC (de Alejandro Bonet, http://opennic.alargador.org)
IPAddress puerta_enlace(192,168,1,14); // Dirección IP del router
IPAddress mascara_red(255,255,255,0); // Máscara de la red
EthernetServer servidor=EthernetServer(2000);
EthernetClient cliente;
char texto_recibido; // Sólo para que se más fácil leer el programa usando varias líneas de código
void setup()
{
Serial.begin(9600);
while(!Serial){;} // Esperar al puerto serie de la placa Leonardo esté operativo
Ethernet.begin(direccion_mac,direccion_ip_fija,servidor_dns,puerta_enlace,mascara_red);
Serial.println(“Iniciando el servidor de mayúsculas”);
servidor.begin();
}
void loop()
{
cliente=servidor.available();
// Si hay disponible alguna petición de un cliente leerla y devolverla en mayúsculas
if(cliente)
{
texto_recibido=cliente.read();
if(texto_recibido>96&&texto_recibido<123) // Si se recibe una letra minúscula…
{
texto_recibido-=32; // …convertirla a mayúsculas
}
// Si se tiene la seguridad de recibir texto se puede usar print en lugar de write
servidor.write(texto_recibido); // Responder con lo recibido pasado a mayúsculas si procede
}
}
|
6. Завершите соединение
Хотя обычно серверное приложение функционирует неопределенно долго, клиентские соединения устанавливаются, устанавливают соединения и завершаются, что позволяет восстановить ресурсы и использовать их в других соединениях или выделить для других целей программы. Функция стоп() класса "Клиент" Он используется для разрыва клиентского соединения и освобождения любых используемых им ресурсов.
Для сервера тот факт, что клиент разрывает соединение, когда информационный объект запроса был отправлен или получен, также позволяет ему освободить ресурсы для распределения их для других соединений или других целей. Короче говоря, хотя это и кажется мелочью, желательно разорвать соединение, когда операции клиента завершатся.
Еще одна хорошая практика при разрыве клиентского соединения — очистить который использует класс. Для этого доступна функция румянец() следует вызывать после разрыва клиентского соединения с помощью стоп()
Пример запроса HTTP GET
Чтобы лучше прояснить все вышесказанное, ниже приведен более полный пример запросов. TCP используя запросы GET, используя HTTP-протокол. В примере значения, полученные аналоговыми датчиками, подключенными к плате Arduino, отправляются на веб-сервер, который сохраняет их в базе данных.
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
|
#include <UIPEthernet.h> // Librería Ethernet usada con el módulo ENC28J60
// #include <Ethernet.h> // Librería Ethernet estándar
#define INTERVALO_CONSULTA 60000 // Enviar datos cada minuto
#define INTERVALO_RECONEXION 10000 // Reintentar la conexión 10 segundos más tarde si no ha sido posible hacerlo cuado correspondía
#define CANTIDAD_SENSORES 6 // Número de sensores analógicos empezando en A0
byte direccion_mac[]={0x12,0x34,0x56,0x78,0x9a,0xbc}; // Dirección MAC inventada
IPAddress direccion_ip_fija(192,168,1,69); // Dirección IP elegida para el módulo
IPAddress servidor_dns(87,216,170,85); // Servidor OpenNIC (de Alejandro Bonet, http://opennic.alargador.org)
IPAddress puerta_enlace(192,168,1,14); // Dirección IP del router
IPAddress mascara_red(255,255,255,0); // Máscara de la red
//IPAddress ip_servidor_web(192,168,1,21); // Dirección IP del servidor web (en la intranet)
char url_servidor_web[]=“sleepmanager.onironauta.es”; // URL poético para un gestor de sueño (en Internet)
EthernetClient cliente;
byte estado_conexion;
String texto_consulta;
unsigned long cronometro_consulta;
byte contador;
void setup()
{
Serial.begin(9600);
while(!Serial){;} // Esperar al puerto serie de la placa Leonardo
Serial.println(“Conectando con el servidor SleepManager…”);
Ethernet.begin(direccion_mac,direccion_ip_fija,servidor_dns,puerta_enlace,mascara_red);
cronometro_consulta=millis()+INTERVALO_CONSULTA;
}
void loop()
{
if(millis()>cronometro_consulta)
{
//estado_conexion=cliente.connect(ip_servidor_web,80); // Conexión desde la intranet
estado_conexion=cliente.connect(url_servidor_web,80); // Conexión desde Internet
while(estado_conexion==0) // esperar a que se establezca la conexión o se produzca un error
{
switch(estado_conexion)
{
case 1:
Serial.println(“Conexión con el servidor SleepManager establecida correctamente”);
break;
case –1:
Serial.println(“Ha pasado el tiempo de espera sin que se establezca la conexión”);
break;
case –2:
Serial.println(“No se ha encontrado el servidor o no responde correctamente”);
break;
case –3:
Serial.println(“La conexión se ha interrumpido antes de establecerse completamente”);
break;
case –4:
Serial.println(“La respuesta del servidor es incorrecta”);
break;
}
}
if(cliente.connected()) // Si ha sido posible conectar realizar la consulta
{
cronometro_consulta=millis()+INTERVALO_CONSULTA;
texto_consulta=“GET /pruebas/guardar_sensores_analogicos.php?origen=SleepManager”;
for(contador=0;contador<CANTIDAD_SENSORES;contador++)
{
texto_consulta=“&sensor_”+String(contador+1,DEC)+“=”+String(analogRead(contador),DEC);
delay(1); // Como tarda 100 μs en obtener el valor analógico, con 1 ms seguro le da tiempo
}
texto_consulta+=” HTTP/1.1\r\nHost: “+String(url_servidor_web)+“\r\nUser-Agent: sleep_inspector\r\n\r\n”;
cliente.print(texto_consulta);
cliente.flush();
cliente.stop();
}
else // Si no ha sido posible conectar reintentarlo más tarde pero no tanto como si hubiera sido posible hacerlo
{
cronometro_consulta=millis()+INTERVALO_RECONEXION;
}
}
}
|
Оставить комментарий