Conexão Ethernet TCP com Arduino
Do ponto de vista do software, estabelecer uma conexão Ethernet com Arduino É muito simples. Para fazer isso, use o Biblioteca Ethernet. Esta biblioteca foi projetada para um Escudo Ethernet que se baseia na integração W5100, mas existem outras placas ou módulos diferentes e/ou que utilizam outros integrados, como o ENC28J60. Para simplificar seu uso e aumentar a compatibilidade, outras bibliotecas utilizam (quase) o mesmo API que Biblioteca Ethernet, você só terá que substituir a biblioteca alternativa pela original ou incluí-la (quando o nome for diferente) em seu lugar mesmo que funções iguais (ou muito semelhantes) sejam usadas no código. No meu caso, eu uso o Biblioteca UIPEthernet de Norbert Truchsess seguindo o mesmo processo que vou descrever neste texto.
1. Defina a conexão Ethernet
Se você vai adotar o papel de cliente como servidor, primeiro você deve definir a conexão com a função begin () que pode ser passado como parâmetro apenas o Endereço MAC e espere por um servidor DHCP na rede atribuir um direção IP e o restante da configuração ou também é possível indicar (opcionalmente) mais parâmetros até que a configuração completa seja definida:
- Endereço MAC (que já foi mencionado)
- Direção IP da blindagem ou módulo
- Endereço IP do servidor DNS (apenas um servidor)
- Endereço IP do porta de entrada
- Máscara de rede
É aconselhável indicar todos os parâmetros, a menos que a sua dedução seja a habitual, para evitar que a configuração não seja correta (por exemplo, que o gateway não seja o primeiro endereço da rede).
Pelo exposto, parece claro que os dados que representam endereços IP devem ser usados com bastante frequência, razão pela qual a biblioteca inclui a classe Endereço de IP a partir do qual instanciar objetos de endereço IP. Os parâmetros que o definem são os quatro bytes de um endereço IPV4
La Endereço MAC É definido para esta biblioteca como uma matriz de 6 bytes. O endereço MAC é (supostamente) um identificador único em que os primeiros bytes indicam o fabricante e modelo e os últimos indicam o dispositivo específico. O integrado ENC28J60 não inclui um endereço MAC, a menos que você opte por comprar também um Endereço MAC integrado da Microchip (ou um quarteirão inteiro SIM de endereços para IEEE se a quantidade de dispositivos for grande o suficiente para valer a pena). Quando você não possui um endereço MAC, pode inventar um, tomando cuidado para que não entre em conflito com outros da rede onde o dispositivo está localizado.
Se a configuração for feita com um servidor DHCP em vez de "manualmente", a função localIP() É útil consultar o endereço que o servidor atribuiu ao módulo. Para renovar o endereço atribuído (se o tempo correspondente tiver expirado) o Biblioteca Ethernet fornece a função manter() que também informará retornando um código que corresponde ao status da renovação:
- A operação não surtiu efeito
-
Erro ao renovar o endereço IP
O uso do endereço IP atribuído no mesmo servidor não pôde ser estendido - Endereço IP renovado com sucesso
-
Falha na religação do endereço IP
O uso do endereço IP atribuído não pôde ser estendido em nenhum servidor - Endereço IP reatribuído com sucesso
Com as informações vistas até agora, você pode escrever um exemplo de como uma conexão Ethernet seria iniciada configurando o endereço IP através de um servidor DHCP na rede. O código de exemplo a seguir tenta renovar o endereço IP a cada determinado período de tempo e relata o resultado.
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(“]”);
}
|
O exemplo abaixo atribui o endereço IP e o restante da configuração manualmente usando objetos Endereço de IP para tornar a leitura mais confortável e (no caso de código mais complexo) para evitar erros que poderiam ocorrer se o endereço fosse (errado) escrito cada vez que fosse usado.
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. Inicie a conexão em modo cliente ou servidor
Ao iniciar uma conexão em modo servidor, é o sistema microcontrolado que está sendo desenvolvido que escuta as solicitações de outros sistemas. Para iniciar a conexão como servidor, use Servidor Ethernet() e a porta na qual o servidor irá escutar é indicada como parâmetro. Servidor Ethernet() é o construtor da classe servidor, que suporta todas as operações Ethernet como servidor. Embora o mais ortodoxo seja fazer uma chamada ao construtor Servidor Ethernet(), não é incomum encontrar alguns exemplos que utilizam diretamente a classe servidor ou bibliotecas alternativas para conexão Ethernet que optem por utilizar esse sistema de instanciação.
A conexão como cliente é aquela que faz as solicitações ao sistema servidor, que as aguarda e as responde adequadamente. Para iniciar uma conexão como cliente, use Cliente Ethernet() qual é o construtor da classe Cliente origem de todas as operações Ethernet como cliente.
Ao contrário do que acontece com o modo servidor, que se supõe que funcione a partir do momento em que a classe é instanciada (embora só responda aos clientes se realmente estiver), você deve verificar se a conexão do cliente está pronta antes de utilizá-la. O objeto cliente criado quando a conexão é iniciada pode ser consultado para verificar se está disponível. Por exemplo, as operações de consulta podem ser incluídas em uma estrutura if(EthernetCliente) executá-los somente quando a conexão do cliente estiver disponível.
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. Estabeleça uma conexão como cliente
Como já foi dito, uma vez criada a conexão, é o cliente quem toma a iniciativa de fazer as consultas. O servidor estará aguardando essa iniciativa e responderá de acordo. É, portanto, o cliente que se conecta ao servidor, para isso utilizamos conectar() indicando o servidor como parâmetros (o endereço IP ou o URL) E o puerto naquele que ouve.
Com base no resultado da operação, a função retornará os valores
- ( ) Conexão estabelecida com sucesso
- Estabelecendo a conexão
- ( ) O tempo limite passou sem que a conexão fosse estabelecida
- ( ) O servidor não foi encontrado ou não está respondendo corretamente
- ( ) A conexão foi interrompida antes de ser totalmente estabelecida
- ( ) A resposta do servidor está incorreta
Antes de começar a fazer consultas é necessário verificar se a conexão está operacional com a função conectado() que vai voltar se já estiver disponível ou caso contrário.
O exemplo abaixo ilustra a conexão como cliente, verificando a cada 10 segundos se há conexão com o servidor (não pretende ser nada produtivo, apenas mostrar a sintaxe das funções) algo que, aliás, um servidor web de produção não gostaria muito.
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. Envie dados
Como outras classes mais conhecidas, como Serial, e com uso comparável, as classes Cliente y servidor tem as funções
-
Envia informações usando o objeto cliente ou servidor do qual são invocadas. O parâmetro "data" é um único byte o carbonizar enquanto "buffer" é uma matriz de byte o carbonizar do qual é enviado um valor igual a "comprimento".Esta função é a utilizada para operações binárias, em comparação com as duas seguintes que normalmente são reservadas para o envio de texto.
-
Envia como cliente ou servidor (dependendo da classe a partir da qual é utilizado) a informação correspondente aos "dados" em forma de texto. Caso a informação não seja expressa em texto (por exemplo é um número inteiro) pode-se utilizar o parâmetro opcional “base” para escolher a conversão, que pode ser uma das constantes BIN, OCT, DEC ou HEX que indicam, respectivamente, o valor da conversão. bases correspondentes a binária (base 2), octal (base 8), decimal (base 10) e hexadecimal (base 16)
-
A operação é idêntica à anterior exceto pelo envio, após a informação expressamente indicada pelo parâmetro "data", de um retorno de carro (código 13 que pode ser representado como \r) e de um fim de linha (código 10, que pode ser representado por \n) Esses códigos são frequentemente referidos, respectivamente, pela sigla CR (Retorno de Transporte) e LF (Alimentação de Linha)
As três funções anteriores retornam o número de bytes que foram enviados, assim como as funções equivalentes da classe Serial; Como dito acima, a operação é comparável.
5. Receba dados
Tal como no caso das operações de envio de dados, as operações de recepção são comparáveis às dos métodos amplamente utilizados. Serial. O protocolo de recebimento também é semelhante: verifique se há dados (suficientes) disponíveis (disponível) e nesse caso leia-os
-
Retorna o número de bytes disponíveis para leitura. Esta função está presente em ambas as classes Cliente como servidor; No primeiro caso, informa a quantidade de bytes que o servidor enviou em resposta a uma solicitação e que estão disponíveis para leitura pelo cliente (ler) e no segundo caso o cliente (objeto) que executou uma operação ou false se não houver.
-
É usado para ler as informações recebidas. Este recurso está disponível apenas na aula Cliente. Se a aplicação que está sendo desenvolvida cumpre a função de servidor, para ler as informações que chegaram, um objeto cliente deve ser instanciado com a resposta da função acessível () discutido na seção anterior.
O exemplo a seguir é um "servidor caps" que escuta na porta 2000 e responde às solicitações com tudo o que foi enviado em letras maiúsculas, quando possível. Pode ser testado, por exemplo, com PuTTY ou simplesmente com Certamente não é muito prático, seu objetivo é apenas mostrar como obter do servidor os dados que um cliente lhe envia.
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. Encerre a conexão
Embora seja comum que uma aplicação de servidor seja executada indefinidamente, as conexões do cliente são estabelecidas, fazem conexões e terminam, permitindo que os recursos sejam recuperados e utilizados em outras conexões ou dedicados a outros usos do programa. A função Pare() da classe Cliente Ele é usado para encerrar uma conexão de cliente e liberar quaisquer recursos que ele esteja usando.
Para o servidor, o fato de o cliente encerrar a conexão quando o objeto de informação da consulta foi enviado ou recebido também permite liberar recursos para alocá-los a outras conexões ou finalidades diferentes. Resumindo, embora pareça insignificante, é aconselhável encerrar a conexão quando as operações do cliente terminarem.
Outra boa prática ao encerrar uma conexão de cliente é esvaziar o que a classe usa. Para fazer isso, a função está disponível nivelado () deve ser chamado após encerrar a conexão do cliente com Pare()
Exemplo de consulta HTTP GET
Para melhor esclarecer tudo o que foi dito acima, um exemplo mais completo de solicitações é incluído abaixo. TCP usando as solicitações GET usando o Protocolo HTTP. No exemplo, os valores obtidos pelos sensores analógicos conectados a uma placa Arduino são enviados para um servidor web que os armazena em um banco de dados.
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;
}
}
}
|
Postar Comentário