Ethernet TCP-tilkobling med Arduino
Fra et programvaresynspunkt, etablere en forbindelse Ethernet med Arduino Det er veldig enkelt. For å gjøre dette, bruk Ethernet-bibliotek. Dette biblioteket er designet for en Ethernet Shield som er basert på det integrerte W5100, men det finnes andre forskjellige tavler eller moduler og/eller som bruker andre integrerte, for eksempel ENC28J60. For å forenkle bruken og øke kompatibiliteten bruker andre biblioteker (nesten) det samme API at Ethernet-bibliotek, vil du bare måtte erstatte det alternative biblioteket med det originale eller inkludere det (når navnet er annerledes) i stedet selv om de samme (eller svært like) funksjonene brukes i koden. I mitt tilfelle bruker jeg UIPEthernet-bibliotek de Norbert Truchsess etter den samme prosessen som jeg skal beskrive i denne teksten.
1. Definer Ethernet-tilkoblingen
Enten du skal påta deg rollen som kunde som for eksempel server, først og fremst må du definere forbindelsen med funksjonen begynne () som bare kan sendes som en parameter MAC-adresse og vent på en server DHCP på nettverket tilordne en IP adresse og resten av konfigurasjonen, eller det er også mulig å indikere (valgfritt) flere parametere til den fullstendige konfigurasjonen er definert:
- Dirección MAC (som allerede er nevnt)
- IP-adresse av skjoldet eller modulen
- Server IP-adresse DNS (kun én server)
- IP-adressen til Inngangsport
- Nettmaske
Det anbefales å angi alle parameterne, med mindre fradraget deres er det vanlige, for å unngå at konfigurasjonen ikke er riktig (for eksempel at gatewayen ikke er den første adressen til nettverket).
Fra ovenstående ser det ut til at det er klart at data som representerer IP-adresser må brukes ganske ofte, og det er grunnen til at biblioteket inkluderer klassen IP adresse for å instansiere IP-adresseobjekter. Parametrene som definerer det er de fire bytene til en adresse IPV4
La MAC-adresse Det er definert for dette biblioteket som en 6-byte array. MAC-adressen er (antas å være) en unik identifikator der de første bytene indikerer produsenten og modellen og de siste indikerer den spesifikke enheten. Det integrerte ENC28J60 inkluderer ikke en MAC-adresse med mindre du velger å også kjøpe en Integrert MAC-adresse fra Microchip (eller en hel blokk ja av adresser til IEEE hvis antallet enheter er stort nok til å gjøre det verdt det). Når du ikke har en MAC-adresse, kan du finne opp en, pass på at den ikke kommer i konflikt med andre på nettverket der enheten er plassert.
Hvis konfigurasjonen gjøres med en DHCP-server i stedet for "for hånd", funksjonen localIP() Det er nyttig å se adressen som serveren har tildelt modulen. For å fornye den tildelte adressen (hvis den tilsvarende tiden er utløpt), må du Ethernet-bibliotek gir funksjonen vedlikeholde() som også vil informere ved å returnere en kode som tilsvarer statusen til fornyelsen:
- Operasjonen har ikke hatt effekt
-
Feil under fornyelse av IP-adresse
Bruken av den tildelte IP-adressen på samme server kunne ikke utvides - IP-adressen ble fornyet
-
Ombinding av IP-adresse mislyktes
Bruken av den tildelte IP-adressen kunne ikke utvides på noen server - IP-adressen ble tildelt på nytt
Med informasjonen sett så langt, kan du skrive et eksempel på hvordan en Ethernet-tilkobling vil bli initiert ved å konfigurere IP-adressen gjennom en DHCP-server på nettverket. Følgende eksempelkode prøver å fornye IP-adressen hver bestemt tidsperiode og rapporterer resultatet.
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(“]”);
}
|
Eksemplet nedenfor tildeler IP-adressen og resten av konfigurasjonen manuelt ved å bruke objekter IP adresse for å gjøre det mer behagelig å lese og (ved mer kompleks kode) for å unngå feil som kan oppstå hvis adressen ble (feil)skrevet hver gang den ble brukt.
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. Start tilkoblingen i klient- eller servermodus
Ved initiering av en tilkobling i servermodus er det det mikrokontrollerte systemet som utvikles som lytter til forespørsler fra andre systemer. For å starte tilkoblingen som en server, bruk EthernetServer() og porten som serveren vil lytte til er indikert som en parameter. EthernetServer() er konstruktøren av klassen Server, som støtter alle Ethernet-operasjoner som en server. Selv om det mest ortodokse er å ringe konstruktøren EthernetServer(), er det ikke uvanlig å finne noen eksempler som direkte bruker klassen Server eller alternative biblioteker for Ethernet-tilkobling som velger å bruke det instansieringssystemet.
Tilkoblingen som klient er den som sender forespørsler til serversystemet, som venter på dem og svarer deretter. For å starte en tilkobling som klient, bruk EthernetClient() hva er konstruktøren av klassen kunde opprinnelsen til alle Ethernet-operasjoner som klient.
I motsetning til det som skjer med servermodus, som antas å fungere fra det øyeblikket klassen instansieres (selv om den vil svare på klienter bare hvis den virkelig er det), må du bekrefte at klienttilkoblingen er klar før du bruker den. Klientobjektet som opprettes når tilkoblingen startes, kan spørres for å se om det er tilgjengelig. For eksempel kan spørringsoperasjoner inkluderes i en struktur if(EthernetClient) å kjøre dem bare når klientforbindelsen er tilgjengelig.
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. Etabler en forbindelse som klient
Som sagt, når tilkoblingen er opprettet, er det klienten som tar initiativet til å stille spørsmålene. Serveren vil vente på dette initiativet og vil svare deretter. Det er derfor klienten som kobler til serveren, for å gjøre det vi bruker koble() angir serveren som parametere (IP-adressen eller URL) Og port hos den som lytter.
Basert på resultatet av operasjonen vil funksjonen returnere verdiene
- ( ) Tilkobling opprettet
- Etablering av forbindelsen
- ( ) Tidsavbrudd har gått uten at tilkobling er opprettet
- ( ) Serveren ble ikke funnet eller svarer ikke riktig
- ( ) Forbindelsen ble avbrutt før den ble fullstendig etablert
- ( ) Serversvar er feil
Før du begynner å stille spørsmål, er det nødvendig å verifisere at forbindelsen er operativ med funksjonen tilkoblet() som kommer tilbake hvis den allerede er tilgjengelig eller ellers.
Eksemplet nedenfor illustrerer tilkoblingen som en klient, og sjekker hvert 10. sekund for å se om det er en tilkobling til serveren (den er ikke ment å være noe produktivt, bare for å vise syntaksen til funksjonene) noe som forresten, en produksjonswebserver vil ikke like mye.
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. Send data
Som andre mer kjente klasser, som f.eks Serial, og med sammenlignbar bruk, klassene kunde y Server har funksjonene
-
Sender informasjon ved hjelp av klient- eller serverobjektet som det påkalles fra. "Data"-parameteren er en enkelt byte o chariot mens "buffer" er en rekke av byte o chariot hvorav et beløp lik "lengde" sendes Denne funksjonen er den som brukes for binære operasjoner, sammenlignet med de to neste som vanligvis er reservert for sending av tekst.
-
Sender som en klient eller server (avhengig av hvilken klasse den brukes fra) informasjonen som tilsvarer "data" som tekst. Hvis informasjonen ikke er uttrykt som tekst (det er for eksempel et heltall) kan den valgfrie parameteren "base" brukes til å velge konverteringen, som kan være en av konstantene BIN, OCT, DEC eller HEX som indikerer hhv. baser som tilsvarer binær (base 2), oktal (base 8), desimal (base 10) og heksadesimal (base 16)
-
Operasjonen er identisk med den forrige bortsett fra å sende, etter informasjonen som uttrykkelig er angitt av "data"-parameteren, en vognretur (kode 13 som kan representeres som \r) og en slutt på linjen (kode 10, som kan være representert av \n) Disse kodene blir ofte referert til henholdsvis med akronymet CR (Carriage Return) og LF (linjemating)
De tre foregående funksjonene returnerer antall byte som er sendt, det samme gjør de tilsvarende funksjonene til klassen Serial; Som nevnt ovenfor er operasjonen sammenlignbar.
5. Motta data
Som i tilfellet med datasendingsoperasjoner, er mottaksoperasjoner sammenlignbare med de mye brukte Serial. Mottaksprotokollen er også lik: sjekk om det er (nok) data tilgjengelig (tilgjengelig) og i så fall les dem
-
Returnerer antall byte som er tilgjengelig for å bli lest. Denne funksjonen finnes i begge klasser kunde som Server; I det første tilfellet rapporterer den antall byte som serveren har sendt som svar på en forespørsel og som er tilgjengelig for klienten å lese (lese), og i det andre tilfellet klienten (objektet) som har utført en operasjon eller falsk hvis det ikke er noen.
-
Den brukes til å lese informasjonen som er mottatt. Denne funksjonen er kun tilgjengelig i klassen kunde. Hvis applikasjonen som utvikles oppfyller rollen som server, for å lese informasjonen som har kommet, må et klientobjekt instansieres med funksjonssvaret tilgjengelig () diskutert i forrige avsnitt.
Følgende eksempel er en "caps-server" som lytter på port 2000 og svarer på forespørsler med alt som ble sendt med store bokstaver når det er mulig. Den kan testes for eksempel med PuTTY eller rett og slett med Det er absolutt ikke veldig praktisk, dens formål er bare å vise hvordan man kan hente dataene sendt til den av en klient fra serveren.
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. Avslutt tilkoblingen
Selv om det er vanlig at en serverapplikasjon kjører på ubestemt tid, opprettes klienttilkoblinger, oppretter tilkoblinger og avsluttes, slik at ressurser kan gjenopprettes og brukes i andre tilkoblinger eller dedikeres til annen bruk av programmet. Funksjonen Stoppe() av klassen kunde Den brukes til å avslutte en klientforbindelse og frigjøre alle ressurser den bruker.
For serveren vil det faktum at klienten avslutter forbindelsen når informasjonsobjektet for spørringen er sendt eller mottatt, også frigjøre ressurser for å allokere dem til andre forbindelser eller andre formål. Kort sagt, selv om det virker lite, er det tilrådelig å avslutte forbindelsen når klientens operasjoner avsluttes.
En annen god praksis når du avslutter en klientforbindelse er å tømme som klassen bruker. For å gjøre dette er funksjonen tilgjengelig flush() skal kalles opp etter at klientforbindelsen er avsluttet Stoppe()
Eksempel på HTTP GET-søk
For bedre å avklare alt ovenfor, er et mer fullstendig eksempel på forespørsler inkludert nedenfor. TCP ved å bruke GET-forespørslene ved å bruke HTTP-protokoll. I eksemplet sendes verdiene oppnådd av analoge sensorer koblet til et Arduino-kort til en webserver som lagrer dem i en database.
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;
}
}
}
|
Legg inn kommentar