การเชื่อมต่อ Ethernet TCP กับ Arduino
จากมุมมองของซอฟต์แวร์ การสร้างการเชื่อมต่อ อีเธอร์เน็ต กับ แพลตฟอร์มฮาร์ดแวร์ มันง่ายมาก เมื่อต้องการทำเช่นนี้ ให้ใช้ ไลบรารีอีเทอร์เน็ต. ห้องสมุดนี้ได้รับการออกแบบสำหรับ อีเธอร์เน็ตชิลด์ ซึ่งอยู่บนพื้นฐานของการบูรณาการ W5100แต่มีบอร์ดหรือโมดูลอื่นๆ ที่แตกต่างกัน และ/หรือที่ใช้บอร์ดหรือโมดูลแบบรวมอื่นๆ เช่น ENC28J60. เพื่อให้การใช้งานง่ายขึ้นและเพิ่มความเข้ากันได้ ไลบรารีอื่น ๆ ก็ใช้ (เกือบ) เหมือนกัน API ที่ ไลบรารีอีเทอร์เน็ตคุณจะต้องแทนที่ไลบรารีสำรองด้วยไลบรารีต้นฉบับหรือรวมไว้ (เมื่อชื่อแตกต่าง) แทนแม้ว่าจะใช้ฟังก์ชันเดียวกัน (หรือคล้ายกันมาก) ในโค้ดก็ตาม ในกรณีของฉันฉันใช้ ไลบรารี UIPEethernet de นอร์เบิร์ต ทรัชเซส ทำตามขั้นตอนเดียวกันกับที่ฉันจะอธิบายในข้อความนี้
1. กำหนดการเชื่อมต่ออีเทอร์เน็ต
ไม่ว่าคุณจะรับบทบาทเป็น ลูกค้า เช่น เซิร์ฟเวอร์ก่อนอื่นคุณต้องกำหนดการเชื่อมต่อกับฟังก์ชันก่อน เริ่มต้น () ซึ่งสามารถส่งผ่านเป็นพารามิเตอร์ได้เท่านั้น ที่อยู่ MAC และรอเซิร์ฟเวอร์ DHCP บนเครือข่ายมอบหมายก ที่อยู่ IP และการกำหนดค่าส่วนที่เหลือ หรืออาจระบุพารามิเตอร์เพิ่มเติม (เป็นทางเลือก) ก็ได้ จนกว่าจะกำหนดการกำหนดค่าเสร็จสมบูรณ์:
- Dirección MAC (ซึ่งได้กล่าวไปแล้ว)
- ที่อยู่ IP ของโล่หรือโมดูล
- ที่อยู่ IP ของเซิร์ฟเวอร์ DNS (เซิร์ฟเวอร์เดียวเท่านั้น)
- ที่อยู่ IP ของ ประตู
- หน้ากากสุทธิ
ขอแนะนำให้ระบุพารามิเตอร์ทั้งหมด เว้นแต่การหักเป็นค่าปกติ เพื่อหลีกเลี่ยงการกำหนดค่าที่ไม่ถูกต้อง (ตัวอย่างเช่น เกตเวย์ไม่ใช่ที่อยู่แรกของเครือข่าย)
จากที่กล่าวมาข้างต้น ดูเหมือนว่าชัดเจนว่าข้อมูลที่แสดงถึงที่อยู่ IP ต้องใช้ค่อนข้างบ่อย ซึ่งเป็นสาเหตุที่ไลบรารีรวมคลาสไว้ด้วย ที่อยู่ IP ซึ่งจะสร้างอินสแตนซ์ของวัตถุที่อยู่ IP พารามิเตอร์ที่กำหนดคือสี่ไบต์ของที่อยู่ IPV4
La ที่อยู่ MAC มันถูกกำหนดไว้สำหรับไลบรารีนี้เป็นอาร์เรย์ 6 ไบต์ ที่อยู่ MAC คือ (ควรจะเป็น) ตัวระบุที่ไม่ซ้ำกัน โดยไบต์แรกระบุถึงผู้ผลิตและรุ่น และไบต์สุดท้ายระบุถึงอุปกรณ์เฉพาะ แบบบูรณาการ ENC28J60 ไม่รวมที่อยู่ MAC เว้นแต่คุณจะเลือกซื้อด้วย ที่อยู่ MAC แบบรวมจาก Microchip (หรือทั้งบล็อก ใช่ ของที่อยู่ไปยัง อีอีอี หากอุปกรณ์มีจำนวนมากพอที่จะทำให้คุ้มค่า) เมื่อคุณไม่มีที่อยู่ MAC คุณสามารถสร้างที่อยู่ขึ้นมาได้ โดยดูแลไม่ให้ขัดแย้งกับที่อยู่อื่นบนเครือข่ายที่อุปกรณ์นั้นตั้งอยู่
หากการกำหนดค่าเสร็จสิ้นด้วยเซิร์ฟเวอร์ DHCP แทนที่จะเป็น "ด้วยมือ" ฟังก์ชันดังกล่าว IP ท้องถิ่น() จะมีประโยชน์ในการปรึกษาที่อยู่ที่เซิร์ฟเวอร์กำหนดให้กับโมดูล หากต้องการต่ออายุที่อยู่ที่กำหนด (หากเวลาที่เกี่ยวข้องหมดอายุแล้ว) ไลบรารีอีเทอร์เน็ต ให้ฟังก์ชั่น บำรุงรักษา() ซึ่งจะแจ้งด้วยการส่งคืนรหัสที่สอดคล้องกับสถานะการต่ออายุ:
- การดำเนินการไม่มีผลใดๆ
-
เกิดข้อผิดพลาดในการต่ออายุที่อยู่ IP
ไม่สามารถขยายการใช้ที่อยู่ IP ที่กำหนดบนเซิร์ฟเวอร์เดียวกันได้ - ต่ออายุที่อยู่ IP เรียบร้อยแล้ว
-
การเชื่อมโยงที่อยู่ IP ซ้ำล้มเหลว
ไม่สามารถขยายการใช้ที่อยู่ IP ที่กำหนดบนเซิร์ฟเวอร์ใดๆ ได้ - กำหนดที่อยู่ IP ใหม่เรียบร้อยแล้ว
จากข้อมูลที่เห็นจนถึงตอนนี้ คุณสามารถเขียนตัวอย่างวิธีเริ่มต้นการเชื่อมต่ออีเทอร์เน็ตโดยการกำหนดค่าที่อยู่ 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. เริ่มการเชื่อมต่อในโหมดไคลเอนต์หรือเซิร์ฟเวอร์
เมื่อเริ่มต้นการเชื่อมต่อในโหมดเซิร์ฟเวอร์ จะเป็นระบบควบคุมไมโครคอนโทรลเลอร์ที่กำลังพัฒนาซึ่งจะรับฟังคำขอจากระบบอื่น หากต้องการเริ่มการเชื่อมต่อเป็นเซิร์ฟเวอร์ให้ใช้ อีเธอร์เน็ตเซิร์ฟเวอร์() และพอร์ตที่เซิร์ฟเวอร์จะฟังจะถูกระบุเป็นพารามิเตอร์ อีเธอร์เน็ตเซิร์ฟเวอร์() เป็นตัวสร้างคลาส เซิร์ฟเวอร์ซึ่งรองรับการทำงานของอีเธอร์เน็ตทั้งหมดในฐานะเซิร์ฟเวอร์ แม้ว่าสิ่งที่ออร์โธดอกซ์ที่สุดคือการโทรหาคอนสตรัคเตอร์ อีเธอร์เน็ตเซิร์ฟเวอร์()ไม่ใช่เรื่องแปลกที่จะพบตัวอย่างบางส่วนที่ใช้คลาสโดยตรง เซิร์ฟเวอร์ หรือไลบรารีทางเลือกสำหรับการเชื่อมต่ออีเธอร์เน็ตที่เลือกใช้ระบบอินสแตนซ์นั้น
การเชื่อมต่อในฐานะไคลเอ็นต์คือการเชื่อมต่อที่ส่งคำขอไปยังระบบเซิร์ฟเวอร์ ซึ่งจะรอและตอบกลับตามนั้น หากต้องการเริ่มต้นการเชื่อมต่อในฐานะไคลเอ็นต์ ให้ใช้ อีเธอร์เน็ตไคลเอนต์() คอนสตรัคเตอร์ของคลาสคืออะไร ไคลเอนต์ ต้นกำเนิดของการดำเนินการอีเธอร์เน็ตทั้งหมดในฐานะไคลเอ็นต์
แตกต่างจากสิ่งที่เกิดขึ้นกับโหมดเซิร์ฟเวอร์ ซึ่งถือว่าทำงานทันทีที่คลาสถูกสร้างอินสแตนซ์ (แม้ว่าจะตอบสนองต่อไคลเอนต์เมื่อเป็นจริงเท่านั้น) คุณต้องตรวจสอบว่าการเชื่อมต่อไคลเอนต์พร้อมก่อนใช้งาน สามารถสอบถามออบเจ็กต์ไคลเอ็นต์ที่ถูกสร้างขึ้นเมื่อเริ่มต้นการเชื่อมต่อเพื่อดูว่าพร้อมใช้งานหรือไม่ ตัวอย่างเช่น การดำเนินการสืบค้นสามารถรวมไว้ในโครงสร้างได้ ถ้า (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 เซิร์ฟเวอร์ มีฟังก์ชั่น
-
ส่งข้อมูลโดยใช้วัตถุไคลเอนต์หรือเซิร์ฟเวอร์ที่ถูกเรียกใช้ พารามิเตอร์ "data" เป็นพารามิเตอร์เดียว ไบต์ o ถัง ในขณะที่ "บัฟเฟอร์" เป็นอาร์เรย์ของ ไบต์ o ถัง ซึ่งมีการส่งจำนวนเท่ากับ "ความยาว" ฟังก์ชันนี้เป็นฟังก์ชันที่ใช้สำหรับการดำเนินการไบนารี่เมื่อเทียบกับฟังก์ชันถัดไปที่ปกติจะสงวนไว้สำหรับการส่งข้อความ
-
ส่งเป็นไคลเอนต์หรือเซิร์ฟเวอร์ (ขึ้นอยู่กับคลาสที่ใช้) ข้อมูลที่สอดคล้องกับ "ข้อมูล" เป็นข้อความ หากข้อมูลไม่ได้แสดงเป็นข้อความ (เช่น เป็นจำนวนเต็ม) สามารถใช้พารามิเตอร์เสริม "ฐาน" เพื่อเลือกการแปลง ซึ่งอาจเป็นหนึ่งในค่าคงที่ BIN, OCT, DEC หรือ HEX ที่ระบุตามลำดับ ฐานที่สอดคล้องกับไบนารี่ (ฐาน 2) ฐานแปด (ฐาน 8) ทศนิยม (ฐาน 10) และเลขฐานสิบหก (ฐาน 16)
-
การดำเนินการจะเหมือนกับการดำเนินการก่อนหน้านี้ ยกเว้นการส่ง หลังจากข้อมูลระบุอย่างชัดแจ้งโดยพารามิเตอร์ "data" การขึ้นบรรทัดใหม่ (รหัส 13 ซึ่งสามารถแสดงเป็น \r) และจุดสิ้นสุดของบรรทัด (รหัส 10 ซึ่งสามารถเป็น แสดงโดย \n) รหัสเหล่านี้มักเรียกตามคำย่อตามลำดับ CR (การคืนสินค้า) และ LF (ฟีดไลน์)
ฟังก์ชันทั้งสามก่อนหน้านี้ส่งคืนจำนวนไบต์ที่ถูกส่งไป เช่นเดียวกับฟังก์ชันที่เทียบเท่ากันของคลาส อนุกรม; ดังที่กล่าวไว้ข้างต้น การดำเนินการสามารถเปรียบเทียบได้
5. รับข้อมูล
เช่นเดียวกับในกรณีการดำเนินการส่งข้อมูล การดำเนินการรับจะเทียบได้กับการดำเนินการที่ใช้กันอย่างแพร่หลาย อนุกรม. โปรโตคอลการรับก็คล้ายกัน: ตรวจสอบว่ามีข้อมูล (เพียงพอ) หรือไม่ (ใช้ได้) และในกรณีนี้ให้อ่าน
-
ส่งกลับจำนวนไบต์ที่สามารถอ่านได้ ฟังก์ชั่นนี้มีอยู่ในทั้งสองคลาส ไคลเอนต์ ในขณะที่ เซิร์ฟเวอร์; ในกรณีแรก รายงานจำนวนไบต์ที่เซิร์ฟเวอร์ได้ส่งเพื่อตอบสนองต่อคำขอและที่ไคลเอ็นต์สามารถอ่านได้ (อ่าน) และในกรณีที่สองไคลเอนต์ (วัตถุ) ที่ได้ดำเนินการหรือเป็นเท็จหากไม่มี
-
ใช้สำหรับอ่านข้อมูลที่ได้รับ คุณลักษณะนี้มีเฉพาะในชั้นเรียนเท่านั้น ไคลเอนต์. หากแอปพลิเคชันที่กำลังพัฒนาเติมเต็มบทบาทของเซิร์ฟเวอร์ เพื่ออ่านข้อมูลที่มาถึง ไคลเอนต์ออบเจ็กต์จะต้องถูกสร้างอินสแตนซ์ด้วยการตอบสนองของฟังก์ชัน ใช้ได้ () กล่าวถึงในส่วนก่อนหน้า
ตัวอย่างต่อไปนี้คือ "เซิร์ฟเวอร์ caps" ที่รับฟังพอร์ต 2000 และตอบสนองต่อคำขอด้วยอะไรก็ตามที่ถูกส่งด้วยตัวพิมพ์ใหญ่ทั้งหมดเมื่อเป็นไปได้ ก็สามารถทดสอบได้ เช่น ด้วย ฉาบ หรือเพียงแค่ด้วย มันไม่ได้ใช้งานได้จริงอย่างแน่นอน จุดประสงค์คือเพียงเพื่อแสดงวิธีรับข้อมูลที่ไคลเอนต์ส่งมาจากเซิร์ฟเวอร์เท่านั้น
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;
}
}
}
|
แสดงความคิดเห็น