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:

  1. //————————————————————————————-
  2. // PROYECTO CENTINELA – MONITOR TCP/IP DISPOSITIVOS – CURSO ARDUINO – GABRIEL ASENSIO
  3. //————————————————————————————-
  4. #define INTERVAL 15000    // Intervalo en segundos entre PING y PING
  5. #define Sirena 2          // Pin de salida para avisador
  6. // Cargamos librerias necesarias Ethernet y tarjeta SD
  7. #include <SD.h>
  8. #include <SPI.h>
  9. #include <Ethernet.h>
  10. #include <ICMPPing.h>
  11. // ¡¡¡ PRECAUCIÓN ETHERNET Y SD !!!
  12. // On the Ethernet Shield, CS is pin 4. Note that even if it’s not
  13. // used as the CS pin, the hardware CS pin (10 on most Arduino boards,
  14. // 53 on the Mega) must be left as an output or the SD library
  15. // functions will not work.
  16. const int chipSelectSD = 4;
  17. const int chipSelectEt = 10;
  18. // Establecer banderas de Problema/s
  19. boolean FlagError[30];
  20. boolean Avisador;
  21. byte Puntero=0;
  22. byte x=0;
  23. byte y=0;
  24. File myFile;
  25. char Cadena[100];
  26. byte ipahora[4];
  27. // Configuración Ethernet, con valores por defecto
  28. byte ip[4]={192,168,1,55};
  29. byte netmask[4]={255,255,255,0};
  30. byte gateway[4]={192,168,1,1};
  31. // byte nntp[4]={192,43,244,18};
  32. byte mac[6]={144,162,218,0,121,190};
  33. // variables para dispositivos
  34. unsigned long equipo[30];
  35. byte estado[30];
  36. unsigned long hora=0;
  37. // Textos para guardar en memoria Flash de programa
  38. char PROGMEM KBCRA[]=“<div align=’center’><H1>Arduino – Centinela IP</H1></div>”;
  39. char PROGMEM INICIO[]=“<table width=’30%’ border=’1′ align=’center’>”;
  40. char PROGMEM divverde[] = “<tr><td bgcolor=’#00FF00′><div align=’center’>”;
  41. char PROGMEM divroja[] = “<tr><td bgcolor=’#FF0000′><div align=’center’>”;
  42. char PROGMEM divama[] = “<tr><td bgcolor=’#FFFF00′><div align=’center’>”;
  43. // Para PING
  44. SOCKET pingSocket = 3;     // Originalmente es ‘0’, pero no se mezcla con NTP/Web bien si es ‘0’
  45. EthernetServer server(80);
  46. //——————————————————————-
  47. byte Extraer(char* Trozo, byte Pos){
  48.    return( (((int)Trozo[Pos]48)*100) + (((int)Trozo[Pos+1]48)*10) + ((int)Trozo[Pos+2]48));
  49.    }
  50. //——————————————————————-
  51. //===========================================================
  52. //     Enviar contenido de PROGMEM a client de WebServer
  53. //===========================================================
  54. void contentPrinter(EthernetClient client, char *realword) {
  55. #define STRING_BUFFER_SIZE 30  
  56.   int total = 0;
  57.   int start = 0;
  58.   char buffer[STRING_BUFFER_SIZE];
  59.   int realLen = strlen_P(realword);
  60.   memset(buffer,0,STRING_BUFFER_SIZE);
  61.   while (total <= realLen){
  62.     // Enviar contenido a cliente
  63.     strncpy_P(buffer, realword+start, STRING_BUFFER_SIZE1);
  64.     client.print(buffer);
  65.     // more content to print?
  66.     total = start + STRING_BUFFER_SIZE1;
  67.     start = start + STRING_BUFFER_SIZE1;
  68.   }
  69. }
  70. //=============================================================
  71. //——————————————————————–
  72. void setup(){
  73.   Serial.begin(9600);
  74.   pinMode(Sirena, OUTPUT);      // Avisador es salida
  75.   // OJO al ChipSelect, solo se puede usar SD o Ethernet
  76.   pinMode(chipSelectEt, OUTPUT);  // Modo, inhabilitado por defecto
  77.   // Ver si hay tarjeta SD.
  78.   if(!SD.begin(chipSelectSD)){   // Abrrir SD
  79.      //—————————————————
  80.      Serial.println(“Error SD”);
  81.      return;
  82.      }
  83.      //—————————————————
  84.   Serial.println(“SD Ok”);
  85.   // Leer la configuración para programa (IP, MAC, NNTP, mail, etc)
  86.   if(SD.exists(“CONFIG.ARD”)){
  87.      myFile = SD.open(“CONFIG.ARD”);
  88.      while(myFile.available() > 0 ){
  89.         Cadena[Puntero]=myFile.read();
  90.         if(Cadena[Puntero]==10){
  91.           // Analizamos la cadena correspondiente según comienzo
  92.           switch(Cadena[0]){
  93.              //– IP del Arduino —
  94.              case ‘I’:
  95.                 ip[0]=Extraer(Cadena,2);   // Primer byte IP
  96.                 ip[1]=Extraer(Cadena,6);   // Segundo byte IP
  97.                 ip[2]=Extraer(Cadena,10);   // Tercer byte IP
  98.                 ip[3]=Extraer(Cadena,14);  // Cuarto byte IP                
  99.                 break;
  100.              //——————–  
  101.              //– NetMask Arduino —
  102.              case ‘N’:
  103.                 netmask[0]=Extraer(Cadena,2);   // Primer byte NM
  104.                 netmask[1]=Extraer(Cadena,6);   // Segundo byte NM
  105.                 netmask[2]=Extraer(Cadena,10);   // Tercer byte NM
  106.                 netmask[3]=Extraer(Cadena,14);  // Cuarto byte NM                
  107.                 break;
  108.              //——————–  
  109.              //– Gateway Arduino —
  110.              case ‘G’:
  111.                 gateway[0]=Extraer(Cadena,2);    // Primer byte GW
  112.                 gateway[1]=Extraer(Cadena,6);    // Segundo byte GW
  113.                 gateway[2]=Extraer(Cadena,10);   // Tercer byte GW
  114.                 gateway[3]=Extraer(Cadena,14);  // Cuarto byte GW                
  115.                 break;
  116.              //——————–                
  117.              //– NNTP Server (Hora) —
  118.              case ‘H’:
  119.                 // nntp[0]=Extraer(Cadena,2);    // Primer byte NTP
  120.                 // nntp[1]=Extraer(Cadena,6);    // Segundo byte NTP
  121.                 // nntp[2]=Extraer(Cadena,10);   // Tercer byte NTP
  122.                 // nntp[3]=Extraer(Cadena,14);  // Cuarto byte NTP              
  123.                 break;
  124.              //———————
  125.              //– MAC del Arduino —
  126.              case ‘M’:
  127.                 mac[0]=Extraer(Cadena,2);    //
  128.                 mac[1]=Extraer(Cadena,6);    // Segundo byte MAC
  129.                 mac[2]=Extraer(Cadena,10);   //  Tercer byte MAC
  130.                 mac[3]=Extraer(Cadena,14);   // Cuarto byte MAC              
  131.                 mac[4]=Extraer(Cadena,18);   // Cuarto byte MAC              
  132.                 mac[5]=Extraer(Cadena,22);   // Cuarto byte MAC                              
  133.                 break;
  134.              //————————-                            
  135.              //– Equipos a verificar —
  136.              case ‘D’:
  137.                 equipo[x]=(Extraer(Cadena,4)*0x1000000)+(Extraer(Cadena,8)*0x10000)+(Extraer(Cadena,12)*0x100)+Extraer(Cadena,16);
  138.                 FlagError[x]=0;    // Poner FlagError a ‘0’
  139.                 x++;
  140.                 break;
  141.              //——————–                                          
  142.              case ‘;’:
  143.                 // Ignorar línea, se trata de comentarios…
  144.                 break;
  145.              }
  146.           //—————- Fin análisis ————–
  147.           Cadena[0]=‘ ‘;
  148.           Puntero=255;    // Como puntero se incrementa lo pongo a 255 ¡¡¡ Cahpuz !!!
  149.           }
  150.         Puntero++;
  151.        }
  152.      }
  153.   else {
  154.       Serial.println(“.CFG no existe.”);
  155.     }
  156.   //————————— Finalizado CONFIG.ARD, configuramos Ethernet…
  157.   Serial.println(x);            // Ver número de dispositivos hallados
  158.   myFile.close();
  159.   // Cerrar fichero SD
  160.   // Asignar la IP y la MAC al Arduino
  161.   Ethernet.begin(mac, ip, gateway, netmask);
  162.   server.begin();
  163.   // Udp.begin(localPort);  
  164.   // Una vez ajustada la tarjeta de Red, se puede comenzar el bucle de exploración
  165.   //———————————————————————————————–
  166. }
  167. //——————————————————————————————————
  168. //————————————————————————–
  169. void loop(){
  170.    //————————————————————-
  171.    // Recorrer la matriz de direcciones a explorar si toca hacerlo
  172.    //————————————————————-
  173.    if((hora + INTERVAL) < millis()){
  174.        //=======================================================
  175.        // Verificar IP señalada por el puntero
  176.        // Solo si se ha superado el INTERVALO entre ping
  177.        ICMPPing ping(pingSocket);
  178.        ipahora[3] = (byte) equipo[y];
  179.        ipahora[2] = (byte)(equipo[y] >> 8);
  180.        ipahora[1] = (byte)(equipo[y] >> 16);
  181.        ipahora[0] = (byte)(equipo[y] >> 24);
  182.        ping(1, ipahora, Cadena);
  183.        Serial.println(Cadena);
  184.        //————————————–
  185.        // Tratamiento en caso de error de PING
  186.        //————————————–      
  187.        if(Cadena[0]==‘R’ && Cadena[1]==‘e’ && Cadena[2]==‘q’){
  188.           // Si hay error, incrementar el número de estado
  189.           if(estado[y]<5){ estado[y]++; }
  190.           // Se ha llegado al límite de reintentos???
  191.           if((estado[y]==5) && (FlagError[y]==false)){ // Si hay error repetido y es primera vez….
  192.              // Averiguar la fecha/hora actual ( I2C PCF8583 ? )
  193.              //———————————————————————————-
  194.              // Grabar SD
  195.              //———–
  196.              myFile = SD.open(“eventos.txt”, FILE_WRITE);
  197.              // Si está disponible, escribir dato de ERROR en dispositivo
  198.              if(myFile){
  199.                 myFile.print(“Error IP: “);
  200.                 myFile.print((byte)(equipo[y] >> 24));
  201.                 myFile.print(‘.’);
  202.                 myFile.print((byte)(equipo[y] >> 16));
  203.                 myFile.print(‘.’);
  204.                 myFile.print((byte)(equipo[y] >> 8));
  205.                 myFile.print(‘.’);
  206.                 myFile.println((byte)equipo[y]);
  207.                 myFile.close();
  208.                 }
  209.              //—————————————————————-
  210.              FlagError[y]=true;       // Evitar re-entrada en alarma….
  211.              }   // Hemos llegado a 5, marcamos error
  212.            }
  213.        //————————————
  214.        // Tratamiento en caso PING correcto      
  215.        //————————————
  216.        if(Cadena[0]==‘R’ && Cadena[1]==‘e’ && Cadena[2]==‘p’){
  217.           // Ha habido errores anteriormente ??? , si es así decrementar
  218.           if(estado[y]>0){ estado[y]–; }                     // Decrementamos y chequeamos situación
  219.           if((estado[y]==0) && (FlagError[y]==true)){
  220.              // Averiguar la fecha/hora actual ( ver tratamiento socket)
  221.              // Enviar correo y marcar (anulación alarma)
  222.              //————
  223.              // Grabar SD
  224.              //————
  225.              myFile = SD.open(“eventos.txt”, FILE_WRITE);
  226.              // Si está disponible, escribir dato de ERROR en dispositivo
  227.              if(myFile){
  228.                 myFile.print(“IP reactivada: “);
  229.                 myFile.print((byte)(equipo[y] >> 24));
  230.                 myFile.print(‘.’);
  231.                 myFile.print((byte)(equipo[y] >> 16));
  232.                 myFile.print(‘.’);
  233.                 myFile.print((byte)(equipo[y] >> 8));
  234.                 myFile.print(‘.’);
  235.                 myFile.println((byte)equipo[y]);
  236.                 myFile.close();
  237.                 }
  238.                //—————————————————————-              
  239.              FlagError[y]=false;       // Evitar re-entrada en finalarma….
  240.              }
  241.          }
  242.        //———————————————————————————————–
  243.        y++;                      // Controlar puntero
  244.        if(y==x){ y=0;}           // Hemos alcanzado el final de la lista
  245.        hora=millis();            // Actualizar contador de intervalo
  246.        //——————————————————————
  247.        // Nos preguntamos si hay que activar el aviso
  248.        boolean Bandera=false;
  249.        for(int w = 0; w < x; w++){ Bandera=(Bandera || FlagError[w]);} // Recorrer todos los dispositivos  
  250.        if(Bandera){ digitalWrite(Sirena, HIGH);} else {digitalWrite(Sirena, LOW);} // Ajustar ‘Sirena’
  251.        //——————————————————————
  252.        }                         // Bucle de intervalo para ping
  253.       //===============================================================================================
  254.       //===============================================================================================
  255.       // Entre PING y PING escuchar clientes… Web sencillo
  256.       EthernetClient client = server.available();
  257.       if (client){
  258.          // Petición http
  259.          boolean currentLineIsBlank = true;
  260.          while(client.connected()){
  261.             if(client.available()){
  262.                char c = client.read();
  263.                // Si hay una linea en blanco, la petición http ha terminado
  264.                // Es hora de contestar…
  265.                if (c == \n && currentLineIsBlank) {
  266.                    // Enviar respuesta estandar http
  267.                    client.println(“HTTP/1.1 200 OK”);
  268.                    client.println(“Content-Type: text/html”);
  269.                    client.println();
  270.                    //—————————————–
  271.                    // Salidas relacionadas con la petición….
  272.                    //—————————————–
  273.                    contentPrinter(client, KBCRA);
  274.                    contentPrinter(client, INICIO);
  275.                    for(int z = 0; z < x; z++) {    // Recorrer todos los dispositivos
  276.                        if(estado[z]==0){ contentPrinter(client, divverde); }
  277.                        if(estado[z]>4) { contentPrinter(client, divroja); }
  278.                        if((estado[z]>0) && (estado[z]<5)) { contentPrinter(client, divama); }
  279.                        client.print(“Dispositivo: “);
  280.                        client.print((byte)(equipo[z] >> 24));
  281.                        client.print(‘.’);
  282.                        client.print((byte)(equipo[z] >> 16));
  283.                        client.print(‘.’);
  284.                        client.print((byte)(equipo[z] >> 8));
  285.                        client.print(‘.’);
  286.                        client.print((byte)equipo[z]);
  287.                        client.print(“</td></tr>”);
  288.                        delay(1);
  289.                        }
  290.                    client.println(“</TABLE>”);
  291.                    break;
  292.                    }
  293.                 if (c == \n) {
  294.                    // Comenzar una nueva línea
  295.                    currentLineIsBlank = true;
  296.                    }
  297.                 else if (c != \r) {
  298.                    // you’ve gotten a character on the current line
  299.                    currentLineIsBlank = false;
  300.                    }
  301.                 }
  302.               }
  303.            // Dar tiempo al navegador para recibir los datos.
  304.            delay(1);
  305.            // Cerrar conexión
  306.            client.stop();
  307.        }
  308.       //***********************************************************************************************  
  309.       // delay(500);         // Sustituido por millis() al inicio del bucle
  310.    //——————————————————————————————  
  311. }
  312. //—————————————————————————————————-

Podría también gustarte...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *