ArduinoとのイーサネットTCP接続
ソフトウェアの観点から見ると、接続の確立 イーサネット とともに Arduinoの とてもシンプルです。 これを行うには、 イーサネットライブラリ。 このライブラリは、 イーサネットシールド 統合されたものに基づいています W5100、ただし、他の異なるボードやモジュールが存在する、および/または他の統合されたボードやモジュールを使用するものもあります。 ENC28J60。 使用を簡素化し、互換性を高めるために、他のライブラリは(ほぼ)同じものを使用します。 API その イーサネットライブラリ、コード内で同じ (または非常によく似た) 関数が使用されている場合でも、代替ライブラリを元のライブラリと置き換えるか、(名前が異なる場合は) その代わりに代替ライブラリを含めるだけで済みます。 私の場合は、 UIPEイーサネットライブラリ de ノーバート・トゥルーセス このテキストで説明するのと同じプロセスに従います。
1. イーサネット接続を定義する
の役割を引き受けるかどうか 顧客 のように サーバ、まず最初に関数との接続を定義する必要があります ベギン () パラメータとして渡すことができるのは MACアドレス そしてサーバーを待ちます DHCP ネットワーク上で IPアドレス 構成の残りの部分を指定することも、完全な構成が定義されるまで (オプションで) さらに多くのパラメーターを指定することもできます。
- DirecciónMAC (これについてはすでに述べました)
- IPアドレス シールドまたはモジュールの
- サーバーのIPアドレス DNS (サーバーは XNUMX つだけ)
- のIPアドレス ゲートウェイ
- ネットマスク
構成が正しくないこと (たとえば、ゲートウェイがネットワークの最初のアドレスではないこと) を避けるために、推定が通常のものでない限り、すべてのパラメーターを指定することをお勧めします。
上記のことから、IP アドレスを表すデータは頻繁に使用する必要があることが明らかなようです。そのため、ライブラリに次のクラスが含まれています。 IPアドレス そこから IP アドレス オブジェクトをインスタンス化します。 それを定義するパラメータはアドレスの XNUMX バイトです IPV4
La MACアドレス このライブラリでは 6 バイトの配列として定義されています。 MAC アドレスは、最初のバイトが製造元とモデルを示し、最後のバイトが特定のデバイスを示す一意の識別子 (であると想定されています) です。 統合された ENC28J60 も購入することを選択しない限り、MAC アドレスは含まれません。 Microchip 社の統合 MAC アドレス (またはブロック全体 はい 宛先のアドレスのうち IEEE デバイスの実行が価値があるほど大きい場合)。 MAC アドレスがない場合は、デバイスが配置されているネットワーク上の他のアドレスと競合しないように注意しながら、MAC アドレスを作成できます。
構成が「手動」ではなく DHCP サーバーを使用して行われた場合、この機能は ローカルIP() サーバーがモジュールに割り当てたアドレスを参照すると便利です。 割り当てられたアドレスを更新するには(対応する時間が経過した場合)、 イーサネットライブラリ 機能を提供します 維持する() また、更新のステータスに対応するコードを返すことによって通知します。
- 手術は何の効果もありませんでした
-
IPアドレス更新エラー
同じサーバー上で割り当てられた IP アドレスの使用を延長できませんでした - IPアドレスが正常に更新されました
-
IPアドレスの再バインドに失敗しました
割り当てられた IP アドレスの使用をどのサーバーでも拡張できませんでした - IPアドレスの再割り当てが正常に完了しました
これまでに説明した情報を使用して、ネットワーク上の DHCP サーバーを介して 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
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. クライアントまたはサーバーモードで接続を開始します。
サーバー モードで接続を開始する場合、他のシステムからの要求をリッスンするのは、開発中のマイクロ制御システムです。 サーバーとして接続を開始するには、次を使用します。 イーサネットサーバー() サーバーがリッスンするポートはパラメーターとして示されます。 イーサネットサーバー() クラスのコンストラクターです サーバー、サーバーとしてすべてのイーサネット操作をサポートします。 最もオーソドックスなのはコンストラクターを呼び出すことですが、 イーサネットサーバー()、クラスを直接使用する例がいくつか見つかることは珍しくありません。 サーバー または、そのインスタンス化システムの使用を選択するイーサネット接続用の代替ライブラリ。
クライアントとしての接続は、サーバー システムにリクエストを送信し、リクエストを待機してそれに応じて応答します。 クライアントとして接続を開始するには、次を使用します。 イーサネットクライアント() クラスのコンストラクターは何ですか クライアント クライアントとしてのすべてのイーサネット操作の起点。
クラスがインスタンス化された瞬間から機能すると想定されるサーバー モードの場合とは異なり (ただし、クライアントに応答するのは実際にインスタンス化された場合のみ)、使用する前にクライアント接続の準備ができていることを確認する必要があります。 接続の開始時に作成されるクライアント オブジェクトをクエリして、それが使用可能かどうかを確認できます。 たとえば、クエリ操作を構造体に含めることができます。 if(イーサネットクライアント) クライアント接続が利用可能な場合にのみ実行します。
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 秒ごとにチェックします (生産性をまったく意図したものではなく、関数の構文を示すだけです)。実稼働 Web サーバーはあまり好きではありません。
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 チャリオット 「buffer」は次の配列です バイト o チャリオット 通常テキスト送信用に予約されている次の XNUMX つの関数と比較すると、この関数はバイナリ演算に使用される関数です。
-
(使用元のクラスに応じて) クライアントまたはサーバーとして、「データ」に対応する情報をテキストとして送信します。 情報がテキストとして表現されていない場合 (整数など)、オプションのパラメーター「base」を使用して変換を選択できます。これは、それぞれを示す定数 BIN、OCT、DEC、または HEX のいずれかになります。 . 2 進数 (基数 8)、10 進数 (基数 16)、XNUMX 進数 (基数 XNUMX)、および XNUMX 進数 (基数 XNUMX) に対応する基数
-
この操作は、「data」パラメーターで明示的に示された情報の後に、復帰 (\r として表すことができるコード 13) と行末 (コード 10、または\n で表されます) これらのコードは、それぞれ頭字語でよく参照されます。 CR(キャリッジリターン)と LF(ラインフィード)
前の XNUMX つの関数は、クラスの同等の関数と同様に、送信されたバイト数を返します。 シリアル; 上で述べたように、操作は同等です。
5. データの受信
データ送信操作の場合と同様、受信操作も広く使用されているものと同等です。 シリアル。 受信プロトコルも同様です。利用可能な(十分な)データがあるかどうかを確認します(利用できます)そしてその場合はそれらを読んでください
-
読み取り可能なバイト数を返します。 この関数は両方のクラスに存在します クライアント として サーバー; 最初のケースでは、サーバーがリクエストに応じて送信し、クライアントが読み取ることができるバイト数を報告します (read)、XNUMX 番目の場合は、操作を実行したクライアント (オブジェクト)、または何もない場合は false。
-
受信した情報を読み取るために使用されます。 この機能はクラス内でのみ利用可能です クライアント。 開発中のアプリケーションがサーバーの役割を果たす場合、到着した情報を読み取るために、関数の応答でクライアント オブジェクトをインスタンス化する必要があります。 利用可能() 前のセクションで説明しました。
次の例は、ポート 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. 接続を終了する
サーバー アプリケーションが無期限に機能するのは通常ですが、クライアント接続は確立され、接続が確立され、終了されるため、リソースが回復されて他の接続で使用されたり、プログラムの他の用途に使用されたりすることができます。 関数 停() クラスの クライアント これは、クライアント接続を終了し、使用しているリソースを解放するために使用されます。
サーバーの場合、クエリの情報オブジェクトが送受信されたときにクライアントが接続を終了するという事実により、リソースを解放して他の接続や別の目的に割り当てることができます。 つまり、些細なことのように見えますが、クライアントの操作が終了したら接続を終了することをお勧めします。
クライアント接続を終了するときのもう XNUMX つの良い習慣は、 クラスが使用するもの。 これを行うには、次の機能が利用可能です 流す() クライアント接続を終了した後に呼び出す必要があります 停()
HTTP GET クエリの例
上記すべてをより明確にするために、より完全なリクエストの例を以下に示します。 TCP GET リクエストを使用する HTTPプロトコル。 この例では、Arduino ボードに接続されたアナログ センサーによって取得された値が Web サーバーに送信され、データベースに保存されます。
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;
}
}
}
|
コメントを投稿