Arduino – Centinela IP
En la realización de un curso de Arduino y posibles aplicaciones, realicé un programa que permite la vigilancia de una serie de dispositivos TCT-IP vía red usando un Arduino Uno y un módulo Ethernet Shield. Pretendía ser algo más complejo, pero me faltó RAM y espacio en la flash para mejorarlo.
Se trata de lo siguiente: Arduino vigilará una serie de dispositivos vía ICMP, si falla alguno esperará a que se produzcan cinco errores y si estos persisten, producirá una alarma para que el encargado de mantenimiento verifique los dispositivos caídos. La disponibilidad de los equipos se puede consultar vía Web y se registra en una tarjeta SD los sucesos. En esa misma tarjeta SD van los detalles de operación del software, incluyendo la lista de IPs a explorar.
Adjunto código y pdf resumen por si a alguien le interesa parte o la totalidad del programa:
-
//————————————————————————————-
-
// PROYECTO CENTINELA – MONITOR TCP/IP DISPOSITIVOS – CURSO ARDUINO – GABRIEL ASENSIO
-
//————————————————————————————-
-
#define INTERVAL 15000 // Intervalo en segundos entre PING y PING
-
#define Sirena 2 // Pin de salida para avisador
-
// Cargamos librerias necesarias Ethernet y tarjeta SD
-
#include <SD.h>
-
#include <SPI.h>
-
#include <Ethernet.h>
-
#include <ICMPPing.h>
-
// ¡¡¡ PRECAUCIÓN ETHERNET Y SD !!!
-
// On the Ethernet Shield, CS is pin 4. Note that even if it’s not
-
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
-
// 53 on the Mega) must be left as an output or the SD library
-
// functions will not work.
-
const int chipSelectSD = 4;
-
const int chipSelectEt = 10;
-
// Establecer banderas de Problema/s
-
boolean FlagError[30];
-
boolean Avisador;
-
byte Puntero=0;
-
byte x=0;
-
byte y=0;
-
File myFile;
-
char Cadena[100];
-
byte ipahora[4];
-
// Configuración Ethernet, con valores por defecto
-
byte ip[4]={192,168,1,55};
-
byte netmask[4]={255,255,255,0};
-
byte gateway[4]={192,168,1,1};
-
// byte nntp[4]={192,43,244,18};
-
byte mac[6]={144,162,218,0,121,190};
-
// variables para dispositivos
-
unsigned long equipo[30];
-
byte estado[30];
-
unsigned long hora=0;
-
// Textos para guardar en memoria Flash de programa
-
char PROGMEM KBCRA[]=“<div align=’center’><H1>Arduino – Centinela IP</H1></div>”;
-
char PROGMEM INICIO[]=“<table width=’30%’ border=’1′ align=’center’>”;
-
char PROGMEM divverde[] = “<tr><td bgcolor=’#00FF00′><div align=’center’>”;
-
char PROGMEM divroja[] = “<tr><td bgcolor=’#FF0000′><div align=’center’>”;
-
char PROGMEM divama[] = “<tr><td bgcolor=’#FFFF00′><div align=’center’>”;
-
// Para PING
-
SOCKET pingSocket = 3; // Originalmente es ‘0’, pero no se mezcla con NTP/Web bien si es ‘0’
-
EthernetServer server(80);
-
//——————————————————————-
-
byte Extraer(char* Trozo, byte Pos){
-
return( (((int)Trozo[Pos]–48)*100) + (((int)Trozo[Pos+1]–48)*10) + ((int)Trozo[Pos+2]–48));
-
}
-
//——————————————————————-
-
//===========================================================
-
// Enviar contenido de PROGMEM a client de WebServer
-
//===========================================================
-
void contentPrinter(EthernetClient client, char *realword) {
-
#define STRING_BUFFER_SIZE 30
-
int total = 0;
-
int start = 0;
-
char buffer[STRING_BUFFER_SIZE];
-
int realLen = strlen_P(realword);
-
while (total <= realLen){
-
// Enviar contenido a cliente
-
strncpy_P(buffer, realword+start, STRING_BUFFER_SIZE–1);
-
client.print(buffer);
-
// more content to print?
-
total = start + STRING_BUFFER_SIZE–1;
-
start = start + STRING_BUFFER_SIZE–1;
-
}
-
}
-
//=============================================================
-
//——————————————————————–
-
void setup(){
-
Serial.begin(9600);
-
pinMode(Sirena, OUTPUT); // Avisador es salida
-
// OJO al ChipSelect, solo se puede usar SD o Ethernet
-
pinMode(chipSelectEt, OUTPUT); // Modo, inhabilitado por defecto
-
// Ver si hay tarjeta SD.
-
if(!SD.begin(chipSelectSD)){ // Abrrir SD
-
//—————————————————
-
Serial.println(“Error SD”);
-
return;
-
}
-
//—————————————————
-
Serial.println(“SD Ok”);
-
// Leer la configuración para programa (IP, MAC, NNTP, mail, etc)
-
if(SD.exists(“CONFIG.ARD”)){
-
myFile = SD.open(“CONFIG.ARD”);
-
while(myFile.available() > 0 ){
-
Cadena[Puntero]=myFile.read();
-
if(Cadena[Puntero]==10){
-
// Analizamos la cadena correspondiente según comienzo
-
switch(Cadena[0]){
-
//– IP del Arduino —
-
case ‘I’:
-
ip[0]=Extraer(Cadena,2); // Primer byte IP
-
ip[1]=Extraer(Cadena,6); // Segundo byte IP
-
ip[2]=Extraer(Cadena,10); // Tercer byte IP
-
ip[3]=Extraer(Cadena,14); // Cuarto byte IP
-
break;
-
//——————–
-
//– NetMask Arduino —
-
case ‘N’:
-
netmask[0]=Extraer(Cadena,2); // Primer byte NM
-
netmask[1]=Extraer(Cadena,6); // Segundo byte NM
-
netmask[2]=Extraer(Cadena,10); // Tercer byte NM
-
netmask[3]=Extraer(Cadena,14); // Cuarto byte NM
-
break;
-
//——————–
-
//– Gateway Arduino —
-
case ‘G’:
-
gateway[0]=Extraer(Cadena,2); // Primer byte GW
-
gateway[1]=Extraer(Cadena,6); // Segundo byte GW
-
gateway[2]=Extraer(Cadena,10); // Tercer byte GW
-
gateway[3]=Extraer(Cadena,14); // Cuarto byte GW
-
break;
-
//——————–
-
//– NNTP Server (Hora) —
-
case ‘H’:
-
// nntp[0]=Extraer(Cadena,2); // Primer byte NTP
-
// nntp[1]=Extraer(Cadena,6); // Segundo byte NTP
-
// nntp[2]=Extraer(Cadena,10); // Tercer byte NTP
-
// nntp[3]=Extraer(Cadena,14); // Cuarto byte NTP
-
break;
-
//———————
-
//– MAC del Arduino —
-
case ‘M’:
-
mac[0]=Extraer(Cadena,2); //
-
mac[1]=Extraer(Cadena,6); // Segundo byte MAC
-
mac[2]=Extraer(Cadena,10); // Tercer byte MAC
-
mac[3]=Extraer(Cadena,14); // Cuarto byte MAC
-
mac[4]=Extraer(Cadena,18); // Cuarto byte MAC
-
mac[5]=Extraer(Cadena,22); // Cuarto byte MAC
-
break;
-
//————————-
-
//– Equipos a verificar —
-
case ‘D’:
-
equipo[x]=(Extraer(Cadena,4)*0x1000000)+(Extraer(Cadena,8)*0x10000)+(Extraer(Cadena,12)*0x100)+Extraer(Cadena,16);
-
FlagError[x]=0; // Poner FlagError a ‘0’
-
x++;
-
break;
-
//——————–
-
case ‘;’:
-
// Ignorar línea, se trata de comentarios…
-
break;
-
}
-
//—————- Fin análisis ————–
-
Cadena[0]=‘ ‘;
-
Puntero=255; // Como puntero se incrementa lo pongo a 255 ¡¡¡ Cahpuz !!!
-
}
-
Puntero++;
-
}
-
}
-
else {
-
Serial.println(“.CFG no existe.”);
-
}
-
//————————— Finalizado CONFIG.ARD, configuramos Ethernet…
-
Serial.println(x); // Ver número de dispositivos hallados
-
myFile.close();
-
// Cerrar fichero SD
-
// Asignar la IP y la MAC al Arduino
-
Ethernet.begin(mac, ip, gateway, netmask);
-
server.begin();
-
// Udp.begin(localPort);
-
// Una vez ajustada la tarjeta de Red, se puede comenzar el bucle de exploración
-
//———————————————————————————————–
-
}
-
//——————————————————————————————————
-
//————————————————————————–
-
void loop(){
-
//————————————————————-
-
// Recorrer la matriz de direcciones a explorar si toca hacerlo
-
//————————————————————-
-
if((hora + INTERVAL) < millis()){
-
//=======================================================
-
// Verificar IP señalada por el puntero
-
// Solo si se ha superado el INTERVALO entre ping
-
ICMPPing ping(pingSocket);
-
ipahora[3] = (byte) equipo[y];
-
ipahora[2] = (byte)(equipo[y] >> 8);
-
ipahora[1] = (byte)(equipo[y] >> 16);
-
ipahora[0] = (byte)(equipo[y] >> 24);
-
ping(1, ipahora, Cadena);
-
Serial.println(Cadena);
-
//————————————–
-
// Tratamiento en caso de error de PING
-
//————————————–
-
if(Cadena[0]==‘R’ && Cadena[1]==‘e’ && Cadena[2]==‘q’){
-
// Si hay error, incrementar el número de estado
-
if(estado[y]<5){ estado[y]++; }
-
// Se ha llegado al límite de reintentos???
-
if((estado[y]==5) && (FlagError[y]==false)){ // Si hay error repetido y es primera vez….
-
// Averiguar la fecha/hora actual ( I2C PCF8583 ? )
-
//———————————————————————————-
-
// Grabar SD
-
//———–
-
myFile = SD.open(“eventos.txt”, FILE_WRITE);
-
// Si está disponible, escribir dato de ERROR en dispositivo
-
if(myFile){
-
myFile.print(“Error IP: “);
-
myFile.print((byte)(equipo[y] >> 24));
-
myFile.print(‘.’);
-
myFile.print((byte)(equipo[y] >> 16));
-
myFile.print(‘.’);
-
myFile.print((byte)(equipo[y] >> 8));
-
myFile.print(‘.’);
-
myFile.println((byte)equipo[y]);
-
myFile.close();
-
}
-
//—————————————————————-
-
FlagError[y]=true; // Evitar re-entrada en alarma….
-
} // Hemos llegado a 5, marcamos error
-
}
-
//————————————
-
// Tratamiento en caso PING correcto
-
//————————————
-
if(Cadena[0]==‘R’ && Cadena[1]==‘e’ && Cadena[2]==‘p’){
-
// Ha habido errores anteriormente ??? , si es así decrementar
-
if(estado[y]>0){ estado[y]–; } // Decrementamos y chequeamos situación
-
if((estado[y]==0) && (FlagError[y]==true)){
-
// Averiguar la fecha/hora actual ( ver tratamiento socket)
-
// Enviar correo y marcar (anulación alarma)
-
//————
-
// Grabar SD
-
//————
-
myFile = SD.open(“eventos.txt”, FILE_WRITE);
-
// Si está disponible, escribir dato de ERROR en dispositivo
-
if(myFile){
-
myFile.print(“IP reactivada: “);
-
myFile.print((byte)(equipo[y] >> 24));
-
myFile.print(‘.’);
-
myFile.print((byte)(equipo[y] >> 16));
-
myFile.print(‘.’);
-
myFile.print((byte)(equipo[y] >> 8));
-
myFile.print(‘.’);
-
myFile.println((byte)equipo[y]);
-
myFile.close();
-
}
-
//—————————————————————-
-
FlagError[y]=false; // Evitar re-entrada en finalarma….
-
}
-
}
-
//———————————————————————————————–
-
y++; // Controlar puntero
-
if(y==x){ y=0;} // Hemos alcanzado el final de la lista
-
hora=millis(); // Actualizar contador de intervalo
-
//——————————————————————
-
// Nos preguntamos si hay que activar el aviso
-
boolean Bandera=false;
-
for(int w = 0; w < x; w++){ Bandera=(Bandera || FlagError[w]);} // Recorrer todos los dispositivos
-
if(Bandera){ digitalWrite(Sirena, HIGH);} else {digitalWrite(Sirena, LOW);} // Ajustar ‘Sirena’
-
//——————————————————————
-
} // Bucle de intervalo para ping
-
//===============================================================================================
-
//===============================================================================================
-
// Entre PING y PING escuchar clientes… Web sencillo
-
EthernetClient client = server.available();
-
if (client){
-
// Petición http
-
boolean currentLineIsBlank = true;
-
while(client.connected()){
-
if(client.available()){
-
char c = client.read();
-
// Si hay una linea en blanco, la petición http ha terminado
-
// Es hora de contestar…
-
if (c == ‘\n‘ && currentLineIsBlank) {
-
// Enviar respuesta estandar http
-
client.println(“HTTP/1.1 200 OK”);
-
client.println(“Content-Type: text/html”);
-
client.println();
-
//—————————————–
-
// Salidas relacionadas con la petición….
-
//—————————————–
-
contentPrinter(client, KBCRA);
-
contentPrinter(client, INICIO);
-
for(int z = 0; z < x; z++) { // Recorrer todos los dispositivos
-
if(estado[z]==0){ contentPrinter(client, divverde); }
-
if(estado[z]>4) { contentPrinter(client, divroja); }
-
if((estado[z]>0) && (estado[z]<5)) { contentPrinter(client, divama); }
-
client.print(“Dispositivo: “);
-
client.print((byte)(equipo[z] >> 24));
-
client.print(‘.’);
-
client.print((byte)(equipo[z] >> 16));
-
client.print(‘.’);
-
client.print((byte)(equipo[z] >> 8));
-
client.print(‘.’);
-
client.print((byte)equipo[z]);
-
client.print(“</td></tr>”);
-
delay(1);
-
}
-
client.println(“</TABLE>”);
-
break;
-
}
-
if (c == ‘\n‘) {
-
// Comenzar una nueva línea
-
currentLineIsBlank = true;
-
}
-
else if (c != ‘\r‘) {
-
// you’ve gotten a character on the current line
-
currentLineIsBlank = false;
-
}
-
}
-
}
-
// Dar tiempo al navegador para recibir los datos.
-
delay(1);
-
// Cerrar conexión
-
client.stop();
-
}
-
//***********************************************************************************************
-
// delay(500); // Sustituido por millis() al inicio del bucle
-
//——————————————————————————————
-
}
-
//—————————————————————————————————-