Ethernet TCP връзка с Arduino
От софтуерна гледна точка установяване на връзка Ethernet с Arduino Много е просто. За да направите това, използвайте Ethernet библиотека. Тази библиотека е предназначена за a Ethernet щит който се основава на интегрирания W5100, но има други различни платки или модули и/или които използват други интегрирани, като например ENC28J60. За да се опрости използването му и да се увеличи съвместимостта, други библиотеки използват (почти) същото API че Ethernet библиотека, ще трябва само да замените алтернативната библиотека с оригиналната или да я включите (когато името е различно) на нейно място, дори ако в кода се използват същите (или много подобни) функции. В моя случай използвам UIPEthernet библиотека de Норберт Трухсес следвайки същия процес, който ще опиша в този текст.
1. Дефинирайте Ethernet връзката
Независимо дали ще приемете ролята на клиент като този сървъра, първо трябва да дефинирате връзката с функцията начало () който може да се предава като параметър само MAC адрес и изчакайте сървър DHCP в мрежата присвоете a IP адрес и останалата част от конфигурацията или също е възможно да посочите (по избор) повече параметри, докато бъде дефинирана пълната конфигурация:
- Dirección MAC (което вече беше споменато)
- IP адрес на щита или модула
- IP адрес на сървъра DNS (само един сървър)
- IP адрес на Шлюз
- Маска на мрежата
Препоръчително е да посочите всички параметри, освен ако тяхното приспадане не е обичайното, за да избегнете неправилна конфигурация (например шлюзът да не е първият адрес на мрежата).
От горното изглежда, че е ясно, че данните, представляващи IP адреси, трябва да се използват доста често, поради което библиотеката включва класа IP адрес от които да се инстанциират IP адресни обекти. Параметрите, които го определят, са четирите байта на адреса IPV4
La MAC адрес Той е дефиниран за тази библиотека като 6-байтов масив. MAC адресът е (предполага се) уникален идентификатор, в който първите байтове показват производителя и модела, а последните показват конкретното устройство. Интегрираният ENC28J60 не включва MAC адрес, освен ако не изберете да закупите и a Интегриран MAC адрес от Microchip (или цял блок ДА на адреси до IEEE ако наборът от устройства е достатъчно голям, за да си заслужава). Когато нямате MAC адрес, можете да си го измислите, като внимавате да не е в конфликт с други в мрежата, където се намира устройството.
Ако конфигурацията се извършва с DHCP сървър вместо "на ръка", функцията localIP() Полезно е да се консултирате с адреса, който сървърът е присвоил на модула. За подновяване на предоставения адрес (ако съответното време е изтекло) на 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 адреса и останалата част от конфигурацията ръчно с помощта на обекти 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. Стартирайте връзката в режим клиент или сървър
Когато се инициира връзка в сървърен режим, микроконтролираната система, която се разработва, слуша заявки от други системи. За да стартирате връзката като сървър, използвайте EthernetServer() и портът, на който сървърът ще слуша, е посочен като параметър. EthernetServer() е конструкторът на класа Сървър, който поддържа всички Ethernet операции като сървър. Въпреки че най-ортодоксалното нещо е да се направи извикване на конструктора EthernetServer(), не е необичайно да намерите някои примери, които директно използват класа Сървър или алтернативни библиотеки за Ethernet връзка, които избират да използват тази система за инстанциране.
Връзката като клиент е тази, която прави заявки към сървърната система, която ги изчаква и съответно им отговаря. За да започнете връзка като клиент, използвайте EthernetClient() какъв е конструкторът на класа Удовлетвореност произход на всички 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 овъглявам от които се изпраща сума, равна на „дължина". Тази функция е тази, която се използва за двоични операции, в сравнение със следващите две, които обикновено са запазени за изпращане на текст.
-
Изпраща като клиент или сървър (в зависимост от класа, от който се използва) информацията, съответстваща на "данни" като текст. Ако информацията не е изразена като текст (например тя е цяло число), незадължителният параметър "база" може да се използва за избор на преобразуване, което може да бъде една от константите BIN, OCT, DEC или HEX, които показват, съответно. бази, съответстващи на двоична (база 2), осмична (база 8), десетична (база 10) и шестнадесетична (база 16)
-
Операцията е идентична с предишната, с изключение на изпращането след информацията, изрично посочена от параметъра "data", връщане на каретка (код 13, който може да бъде представен като \r) и край на ред (код 10, който може да бъде представени от \n) Тези кодове често се споменават съответно с акронима CR (връщане на каретката) и LF (подаване на ред)
Трите предишни функции връщат броя байтове, които са били изпратени, както и еквивалентните функции на класа Сериен; Както беше казано по-горе, операцията е сравнима.
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;
}
}
}
|
Публикувай коментар