Conexiune Ethernet TCP cu Arduino
Din punct de vedere software, stabilirea unei conexiuni Ethernet cu Arduino Este foarte simplu. Pentru a face acest lucru, utilizați Bibliotecă Ethernet. Această bibliotecă este concepută pentru a Scut Ethernet care se bazează pe integrat W5100, dar există și alte plăci sau module diferite și/sau care folosesc altele integrate, cum ar fi ENC28J60. Pentru a simplifica utilizarea și pentru a crește compatibilitatea, alte biblioteci folosesc (aproape) același lucru API că Bibliotecă Ethernet, va trebui doar să înlocuiți biblioteca alternativă cu cea originală sau să o includeți (când numele este diferit) în locul ei chiar dacă în cod sunt folosite aceleași (sau foarte asemănătoare) funcții. În cazul meu, folosesc Biblioteca UIPEthernet de Norbert Truchsess urmând același proces pe care îl voi descrie în acest text.
1. Definiți conexiunea Ethernet
Fie că vei adopta rolul de client precum serverul, in primul rand trebuie sa definesti legatura cu functia începe () care poate fi trecut ca parametru numai cel Adresa MAC și așteptați un server DHCP pe rețea atribuiți a adresă IP și restul configurației sau este, de asemenea, posibil să se indice (opțional) mai mulți parametri până când este definită configurația completă:
- Dirección MAC (care a fost deja menționat)
- Adresa IP a scutului sau a modulului
- Adresa IP a serverului DNS (doar un server)
- Adresa IP a Gateway
- Mască de plasă
Este indicat să indicați toți parametrii, cu excepția cazului în care deducerea lor este cea obișnuită, pentru a evita ca configurația să nu fie corectă (de exemplu, că gateway-ul nu este prima adresă a rețelei).
Din cele de mai sus se pare că este clar că datele reprezentând adrese IP trebuie folosite destul de des, motiv pentru care biblioteca include clasa Adresa IP din care să instanțieze obiecte de adresă IP. Parametrii care o definesc sunt cei patru octeți ai unei adrese IPV4
La Adresa MAC Este definit pentru această bibliotecă ca o matrice de 6 octeți. Adresa MAC este (se presupune a fi) un identificator unic în care primii octeți indică producătorul și modelul, iar ultimii indică dispozitivul specific. Cea integrată ENC28J60 nu include o adresă MAC decât dacă alegeți să cumpărați și a Adresă MAC integrată de la Microcip (sau un bloc întreg DA de adrese către IEEE dacă rulajul de dispozitive este suficient de mare pentru a merita). Când nu aveți o adresă MAC, puteți inventa una, având grijă să nu intre în conflict cu altele din rețeaua în care se află dispozitivul.
Dacă configurarea se face cu un server DHCP în loc de „de mână”, funcția localIP() Este util să consultați adresa pe care serverul a atribuit-o modulului. Pentru a reînnoi adresa atribuită (dacă timpul corespunzător a expirat) Bibliotecă Ethernet oferă funcția menţine() care va informa și prin returnarea unui cod care corespunde stării reînnoirii:
- Operațiunea nu a avut niciun efect
-
Eroare la reînnoirea adresei IP
Utilizarea adresei IP atribuite pe același server nu a putut fi extinsă - Adresa IP a fost reînnoită cu succes
-
Relegarea adresei IP a eșuat
Utilizarea adresei IP atribuite nu a putut fi extinsă pe niciun server - Adresa IP a fost reatribuită cu succes
Cu informațiile văzute până acum, puteți scrie un exemplu despre cum ar fi inițiată o conexiune Ethernet prin configurarea adresei IP printr-un server DHCP din rețea. Următorul exemplu de cod încearcă să reînnoiască adresa IP la fiecare anumită perioadă de timp și raportează rezultatul.
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(“]”);
}
|
Exemplul de mai jos atribuie adresa IP și restul configurației manual folosind obiecte Adresa IP pentru a face mai confortabil de citit și (în cazul unui cod mai complex) pentru a evita erorile care ar putea apărea dacă adresa a fost (gre)scrisă de fiecare dată când a fost folosită.
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. Porniți conexiunea în modul client sau server
La inițierea unei conexiuni în modul server, sistemul microcontrolat care se dezvoltă este cel care ascultă cererile de la alte sisteme. Pentru a porni conexiunea ca server, utilizați EthernetServer() iar portul pe care serverul va asculta este indicat ca parametru. EthernetServer() este constructorul clasei server de, care acceptă toate operațiunile Ethernet ca server. Deși cel mai ortodox lucru este să faci un apel către constructor EthernetServer(), nu este neobișnuit să găsiți câteva exemple care folosesc direct clasa server de sau biblioteci alternative pentru conexiunea Ethernet care aleg să folosească acel sistem de instanțiere.
Conexiunea ca client este cea care face cereri către sistemul server, care le așteaptă și le răspunde în consecință. Pentru a iniția o conexiune ca client, utilizați EthernetClient() care este constructorul clasei Client originea tuturor operațiunilor Ethernet ca client.
Spre deosebire de ceea ce se întâmplă cu modul server, care se presupune că funcționează din momentul în care clasa este instanțiată (deși va răspunde clienților doar dacă este într-adevăr), trebuie să verificați dacă conexiunea client este gata înainte de a o utiliza. Obiectul client care este creat la inițierea conexiunii poate fi interogat pentru a vedea dacă este disponibil. De exemplu, operațiunile de interogare pot fi incluse într-o structură if(EthernetClient) să le execute numai atunci când conexiunea client este disponibilă.
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. Stabiliți o conexiune ca client
După cum sa spus, odată ce conexiunea este creată, clientul este cel care ia inițiativa de a face interogările. Serverul va aștepta acea inițiativă și va răspunde în consecință. Este, prin urmare, clientul care se conectează la server, pentru a face asta folosim conectați () indicând serverul ca parametri (adresa IP sau URL-ul) și port în cel care ascultă.
Pe baza rezultatului operației, funcția va returna valorile
- ( ) Conexiunea stabilită cu succes
- Stabilirea conexiunii
- ( ) Timeout a trecut fără ca conexiunea să fie stabilită
- ( ) Serverul nu a fost găsit sau nu răspunde corect
- ( ) Conexiunea a fost întreruptă înainte de a fi stabilită complet
- ( ) Răspunsul serverului este incorect
Înainte de a începe să faceți interogări, este necesar să verificați dacă conexiunea este funcțională cu funcția conectat() care se va întoarce dacă este deja disponibil sau in caz contrar.
Exemplul de mai jos ilustrează conexiunea ca client, verificând la fiecare 10 secunde dacă există o conexiune cu serverul (nu se dorește să fie ceva productiv, doar să arate sintaxa funcțiilor) ceva care, de altfel, o producție. serverul web nu ar dori foarte mult.
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. Trimiteți date
Ca și alte clase mai cunoscute, precum Serial, și cu utilizare comparabilă, clasele Client y server de au functiile
-
Trimite informații utilizând obiectul client sau server de la care sunt invocate. Parametrul „date” este unul singur octet o car de război în timp ce „buffer” este o matrice de octet o car de război din care se trimite o suma egala cu "lungimea". Aceasta functie este cea folosita pentru operatii binare, fata de urmatoarele doua care sunt de obicei rezervate pentru trimiterea textului.
-
Trimite ca client sau server (in functie de clasa din care este folosit) informatia corespunzatoare "datelor" ca text. Dacă informația nu este exprimată sub formă de text (de exemplu este un număr întreg) pentru a alege conversia se poate folosi parametrul opțional „bază”, care ar putea fi una dintre constantele BIN, OCT, DEC sau HEX care indică, respectiv. baze corespunzătoare binar (bază 2), octal (bază 8), zecimal (bază 10) și hexazecimal (bază 16)
-
Operațiunea este identică cu cea anterioară cu excepția trimiterii, după informațiile indicate expres de parametrul „date”, a unui retur car (cod 13 care poate fi reprezentat ca \r) și a unui capăt de linie (cod 10, care poate fi reprezentate prin \n) Aceste coduri sunt frecvent denumite, respectiv, prin acronim CR (Întoarcerea căruciorului) și LF (Alimentare linie)
Cele trei funcții anterioare returnează numărul de octeți care au fost trimiși, la fel ca și funcțiile echivalente ale clasei Serial; După cum sa spus mai sus, operațiunea este comparabilă.
5. Primiți date
Ca și în cazul operațiunilor de transmitere a datelor, operațiunile de recepție sunt comparabile cu cele ale celor larg utilizate Serial. Protocolul de recepție este, de asemenea, similar: verificați dacă există (suficiente) date disponibile (disponibil) și, în acest caz, citiți-le
-
Returnează numărul de octeți care sunt disponibili pentru a fi citiți. Această funcție este prezentă în ambele clase Client ca server de; În primul caz, raportează numărul de octeți pe care serverul i-a trimis ca răspuns la o solicitare și care este disponibil pentru citire de către client (citit), iar în al doilea caz clientul (obiectul) care a efectuat o operație sau fals dacă nu există.
-
Este folosit pentru a citi informațiile care au fost primite. Această funcție este disponibilă numai în clasă Client. Dacă aplicația în curs de dezvoltare îndeplinește rolul de server, pentru a citi informațiile sosite, trebuie instanțiat un obiect client cu răspunsul funcției disponibil () discutat în secțiunea anterioară.
Următorul exemplu este un „server cu majuscule” care ascultă pe portul 2000 și răspunde la solicitări cu orice a fost trimis cu majuscule atunci când este posibil. Poate fi testat, de exemplu, cu PuTTY sau pur și simplu cu Cu siguranță nu este foarte practic, scopul său este doar de a arăta cum să obțină datele trimise către acesta de un client de pe server.
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. Încheiați conexiunea
Deși este obișnuit ca o aplicație server să ruleze pe termen nelimitat, conexiunile client sunt stabilite, se realizează și se termină, permițând recuperarea resurselor și utilizarea în alte conexiuni sau dedicate altor utilizări ale programului. Functia Stop() a clasei Client Este folosit pentru a termina o conexiune client și pentru a elibera orice resurse pe care le folosește.
Pentru server, faptul că clientul întrerupe conexiunea atunci când obiectul de informare al interogării a fost trimis sau primit îi permite, de asemenea, să elibereze resurse pentru a le aloca altor conexiuni sau scopuri diferite. Pe scurt, deși pare minor, este indicat să întrerupeți conexiunea atunci când operațiunile clientului se termină.
O altă practică bună atunci când închideți o conexiune client este golirea fișierului pe care le folosește clasa. Pentru a face acest lucru, funcția este disponibilă culoare() ar trebui apelat după terminarea conexiunii client cu Stop()
Exemplu de interogare HTTP GET
Pentru a clarifica mai bine toate cele de mai sus, mai jos este inclus un exemplu mai complet de cereri. TCP folosind cererile GET folosind Protocolul HTTP. În exemplu, valorile obținute de senzorii analogici conectați la o placă Arduino sunt trimise către un server web care le stochează într-o bază de date.
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;
}
}
}
|
Posteaza un comentariu