Etherneti TCP-ühendus Arduinoga
Tarkvara seisukohalt ühenduse loomine Ethernet koos Arduino See on väga lihtne. Selleks kasutage Etherneti raamatukogu. See raamatukogu on mõeldud a Etherneti kilp mis põhineb integreeritud W5100, kuid on ka teisi erinevaid tahvleid või mooduleid ja/või mis kasutavad muid integreeritud plaate või mooduleid, näiteks ENC28J60. Selle kasutamise lihtsustamiseks ja ühilduvuse suurendamiseks kasutavad teised raamatukogud (peaaegu) sama API et Etherneti raamatukogu, peate ainult asendama alternatiivse teegi algse teegiga või lisama selle (kui nimi on erinev) selle asemele, isegi kui koodis kasutatakse samu (või väga sarnaseid) funktsioone. Minu puhul kasutan UIPEtherneti raamatukogu de Norbert Truchsess järgides sama protsessi, mida ma selles tekstis kirjeldan.
1. Määratlege Etherneti ühendus
Kas kavatsete selle rolli omaks võtta klient nagu näiteks server, tuleb kõigepealt defineerida seos funktsiooniga algama () mida saab parameetrina edastada ainult MAC-aadress ja ootame serverit DHCP võrgus määrake a IP-aadress ja ülejäänud konfiguratsioon või on võimalik näidata (valikuliselt) rohkem parameetreid kuni täieliku konfiguratsiooni määratlemiseni:
- Dirección MAC (mida on juba mainitud)
- IP-aadress kilbist või moodulist
- Serveri IP-aadress DNS (ainult üks server)
- IP-aadress Värav
- Võrkmask
Soovitav on märkida kõik parameetrid, välja arvatud juhul, kui nende mahaarvamine on tavaline, vältimaks vale konfiguratsiooni (näiteks et lüüs ei ole võrgu esimene aadress).
Eelnevast tundub, et on selge, et IP-aadresse esindavaid andmeid tuleb kasutada üsna sageli, mistõttu on raamatukogus klass IP-aadress kust luua IP-aadressi objekte. Seda määravad parameetrid on aadressi neli baiti IPV4
La MAC-aadress Selle teegi jaoks on see määratletud 6-baidise massiivina. MAC-aadress on (peab olema) kordumatu identifikaator, mille esimesed baidid näitavad tootjat ja mudelit ning viimased konkreetset seadet. Integreeritud ENC28J60 ei sisalda MAC-aadressi, välja arvatud juhul, kui otsustate osta ka a Integreeritud MAC-aadress Microchipist (või terve plokk JAH aadressidest IEEE kui seadmete arv on piisavalt suur, et see end ära tasub). Kui teil pole MAC-aadressi, saate selle välja mõelda, jälgides, et see ei läheks vastuollu teiste seadmega võrgus olevate andmetega.
Kui seadistamine toimub DHCP-serveriga, mitte "käsitsi", funktsioon localIP() Kasulik on uurida aadressi, mille server on moodulile määranud. Määratud aadressi uuendamiseks (kui vastav aeg on möödas) Etherneti raamatukogu pakub funktsiooni säilitama () mis teavitab ka uuendamise olekule vastava koodi tagastamisega:
- Operatsioonil pole olnud mõju
-
Viga IP-aadressi uuendamisel
Määratud IP-aadressi kasutamist samas serveris ei saanud pikendada - IP-aadressi uuendamine õnnestus
-
IP-aadressi uuesti sidumine ebaõnnestus
Määratud IP-aadressi kasutamist ei saanud üheski serveris pikendada - IP-aadressi ümbermääramine õnnestus
Seni nähtud teabe põhjal saate kirjutada näite, kuidas Etherneti ühendus algatatakse IP-aadressi konfigureerimisel läbi võrgu DHCP-serveri. Järgmine näidiskood proovib uuendada IP-aadressi iga teatud aja tagant ja annab tulemusest teada.
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(“]”);
}
|
Allolev näide määrab IP-aadressi ja ülejäänud konfiguratsiooni käsitsi objektide abil IP-aadress lugemise mugavamaks muutmiseks ja (keerukama koodi puhul) vältimiseks, mis võivad tekkida aadressi (valesti) kirjutamisel iga kord, kui seda kasutati.
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. Käivitage ühendus kliendi või serveri režiimis
Serverirežiimis ühenduse loomisel kuulab teiste süsteemide päringuid just arendatav mikrojuhitav süsteem. Ühenduse loomiseks serverina kasutage Etherneti server() ja parameetrina näidatakse port, mida server kuulab. Etherneti server() on klassi ehitaja server, mis toetab kõiki Etherneti toiminguid serverina. Kuigi kõige õigeusklikum on helistada konstruktorile Etherneti server(), pole harvad juhud, kui leidub näiteid, mis klassi otseselt kasutavad server või alternatiivsed teegid Etherneti ühenduse jaoks, mis valivad seda käivitussüsteemi kasutada.
Ühendus kliendina on see, mis teeb päringuid serverisüsteemile, mis ootab neid ja vastab neile vastavalt. Ühenduse loomiseks kliendina kasutage EthernetClient() mis on klassi konstrueerija klient kõigi Etherneti toimingute päritolu kliendina.
Erinevalt sellest, mis juhtub serverirežiimiga, mis eeldatavasti töötab klassi instantseerimise hetkest (kuigi see vastab klientidele ainult siis, kui see tõesti nii on), peate enne selle kasutamist veenduma, et kliendiühendus on valmis. Kliendiobjektilt, mis luuakse ühenduse loomisel, saab küsida, kas see on saadaval. Näiteks päringutoiminguid saab lisada struktuuri if (EthernetClient) neid käivitada ainult siis, kui kliendiühendus on saadaval.
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. Looge ühendus kliendina
Nagu öeldud, on pärast ühenduse loomist päringute tegemise initsiatiiv klient. Server ootab seda algatust ja vastab sellele vastavalt. Seetõttu loob serveriga ühenduse klient, mida me selleks kasutame ühenda () märkides serveri parameetritena (IP-aadress või URL) ja port selles, kes kuulab.
Toimingu tulemuse põhjal tagastab funktsioon väärtused
- ( ) Ühendus loodud edukalt
- Ühenduse loomine
- ( ) Aegumine on möödunud, ilma et ühendust oleks loodud
- ( ) Serverit ei leitud või see ei reageeri õigesti
- ( ) Ühendus katkestati enne täielikku loomist
- ( ) Serveri vastus on vale
Enne päringute tegemist on vaja kontrollida, kas ühendus funktsiooniga töötab ühendatud () see tuleb tagasi kui see on juba saadaval või muidu.
Allolev näide illustreerib ühendust kliendina, kontrollides iga 10 sekundi järel, kas serveriga on ühendus olemas (see pole mõeldud millekski produktiivseks, vaid funktsioonide süntaksi näitamiseks) midagi, mis muide, tootmisveebiserverile väga ei meeldiks.
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. Andmete saatmine
Nagu teisedki tuntumad klassid, nt Seeria-, ja võrreldava kasutusega klassid klient y server omavad funktsioone
-
Saadab teavet kliendi või serveri objekti abil, millelt see välja kutsutakse. Parameeter "andmed" on üksik bait o sõjavanker samas kui "puhver" on massiiv bait o sõjavanker millest saadetakse "pikkusega" võrdne summa. Seda funktsiooni kasutatakse kahendtoimingute jaoks, võrreldes kahe järgmisega, mis on tavaliselt reserveeritud teksti saatmiseks.
-
Saadab kliendi või serverina (olenevalt klassist, kust seda kasutatakse) "andmetele" vastava info tekstina. Kui teavet ei väljendata tekstina (näiteks see on täisarv), saab teisenduse valimiseks kasutada valikulist parameetrit "base", mis võib olla üks konstantidest BIN, OCT, DEC või HEX, mis näitavad vastavalt. alused, mis vastavad kahendarvule (alus 2), kaheksandsüsteemile (alus 8), kümnendsüsteemile (alus 10) ja kuueteistkümnendsüsteemile (alus 16)
-
Toiming on identne eelmisega, välja arvatud see, et pärast parameetriga "data" selgelt näidatud teavet saadetakse käru tagastus (kood 13, mida saab esitada kui \r) ja rea lõpp (kood 10, mida saab esitada tähistab \n) Nendele koodidele viidatakse sageli vastavalt akronüümiga CR (Carriage Return) ja LF (reavahetus)
Kolm eelmist funktsiooni tagastavad saadetud baitide arvu, nagu ka klassi samaväärsed funktsioonid Seeria-; Nagu eespool öeldud, on operatsioon võrreldav.
5. Andmete vastuvõtmine
Nagu andmete saatmise toimingute puhul, on vastuvõtutoimingud võrreldavad laialdaselt kasutatavate toimingutega Seeria-. Vastuvõtuprotokoll on samuti sarnane: kontrollige, kas saadaval on (piisavalt) andmeid (saadaval) ja sel juhul lugege need läbi
-
Tagastab lugemiseks saadaolevate baitide arvu. See funktsioon on olemas mõlemas klassis klient kui server; Esimesel juhul teatab see baitide arvu, mille server on vastuseks päringule saatnud ja mis on kliendile lugemiseks saadaval (lugenud) ja teisel juhul toimingu sooritanud klient (objekt) või vale, kui seda pole.
-
Seda kasutatakse saadud teabe lugemiseks. See funktsioon on saadaval ainult klassis klient. Kui arendatav rakendus täidab serveri rolli, tuleb saabunud teabe lugemiseks luua kliendiobjekt koos funktsiooni vastusega saadaval () käsitletud eelmises jaotises.
Järgmine näide on "suurtähtede server", mis kuulab porti 2000 ja vastab päringutele võimalusel suuretähelistes saadetistega. Seda saab testida näiteks koos PuTTY või lihtsalt koos See pole kindlasti väga praktiline, selle eesmärk on vaid näidata, kuidas kliendi poolt talle saadetud andmeid serverist kätte saada.
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. Lõpetage ühendus
Kuigi on tavaline, et serverirakendus töötab lõputult, luuakse kliendiühendused, luuakse ühendusi ja lõpetatakse ressursse, võimaldades ressursse taastada ja kasutada muudes ühendustes või programmi muuks kasutuseks. Funktsioon stop () klassist klient Seda kasutatakse kliendiühenduse katkestamiseks ja kasutatavate ressursside vabastamiseks.
Serveri jaoks võimaldab asjaolu, et klient katkestab ühenduse, kui päringu infoobjekt on saadetud või vastu võetud, vabastada ressursse nende eraldamiseks muudeks ühendusteks või erinevateks eesmärkideks. Lühidalt, kuigi see tundub tühine, on soovitatav ühendus katkestada, kui kliendi toimingud lõppevad.
Teine hea tava kliendiühenduse katkestamisel on tühjendada mida klass kasutab. Selleks on funktsioon saadaval flush () tuleks helistada pärast kliendiühenduse katkestamist stop ()
HTTP GET päringu näide
Kõige eelneva paremaks selgitamiseks on allpool toodud taotluste täielikum näide. TCP kasutades GET-päringuid kasutades HTTP protokoll. Näites saadetakse Arduino plaadiga ühendatud analoogandurite saadud väärtused veebiserverisse, mis salvestab need andmebaasi.
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;
}
}
}
|
Postita kommentaar