quinta-feira, 4 de maio de 2017

Raspberry Node.js Agente & Gerente SNMP

No post anterior:

 http://rubenshubner.blogspot.com.br/2017/04/agente-snmp-com-raspberry-nodejs-e.html

implementamos um Agente usando o Node.js no Raspberry para armazenar a leitura do A/D MCP3008  de oito canais e armazenar em uma MIB.
Fizemos o teste via o programa Mib Browser e mostrou que funcionou corretamente.
Nesta postagem vamos implementar um gerente SNMP para efetuar a conexão com o Agente da postagem acima e realizar a leitura da MIB.
A tela apresentada pode ser vista abaixo:






















Podemos observar o IP do agente que no nosso caso vai estar rodando no mesmo Raspberry é o mesmo da pagina web.
Os OIDs que serão pesquisados na MIB do Agente e os comandos Pesquisar e Loop.
O botão pesquisar fará uma pesquisa e o botão Loop ficara efetuando a pesquisa a cada segundo.
O hardware é o mesmo da postagem mencionada acima.
No diagrama baixo podemos ver os blocos funcionais deste processo:






























No Raspberry com o SO Raspbian instalamos o aplicativo Node.js onde podemos rodar os scripts para implementar os 03 blocos:

A) Servidor de HTML que fará a transferência dos arquivos HTML, CSS e Javascript para o navegador através do Request e Response do navegador.

B) O Servidor de Socket.io que fará as trocas de mensagens entre o Navegador e o Raspberry.

C) O Gerente SNMP que vai obter as informações do Agente SNMP.



Uma vez que o navegador recebe os scripts HTML, CSS e Javascript é formada a GUI que é a interface gráfica com o usuário e implementa o Socket.io Cliente para troca de mensagens com o servidor sem precisar haver refresh da página.

O funcionamento se dá da seguinte forma:

O Navegador envia a pesquisa para o Raspberry informando quais são os OIDs e IP para pesquisar no Agente através do Socket.io.
No Raspberry o gerente SNMP obtém estes valores da MIB do Agente e envia para o Navegador via Socket.io e são apresentados na interface gráfica GUI.

No Raspberry temos as seguintes pastas:



 A pasta gerente tem os scripts da implementação  que estamos fazendo e a pasta snmpAgent tem os scripts que foi implementado na postagem que detalha sobre o Agent conforme o link acima.

Dentro da pasta gerente podemos observar os conteúdos:




Podemos observar a pasta do navegador onde estão os scripts HTML, CSS e Javascript que rodará no navegador (IE, Chrome, Firefox e etc..).
A pasta node_modules que será instalada usando o comando npm install <nome do arquivo --save.

O arquivo leSnmp.js é o script que rodará no Raspberry e vai gerar os blocos HTML Server, Socket.io Server e o gerente SNMP como mostrado no diagrama acima.

Dentro da pasta do navegador temos os seguintes scripts:



 Index.css e Index.html responsáveis pela GUI.
index.js responsável pela lógica.
O arquivo ws.js é o socket.io client apenas foi renomeado ( não é necessário renomear ), pode ser encontrado na pasta node_modules dentro da pasta socket.io-client.
O jquery é comum a quem programa HTML e pode ser usado o CDN se preferir.

Na pasta node-modules podemos ver os módulos que foram baixados através do npm:






















Exemplo para instalar o express digite depois do prompt:
npm install express --save

E assim para todos os módulos.

Para rodar os processos no Raspberry quando o SO sobe automaticamente precisamos editar a pasta
/etc/rc.local veja abaixo:






















Para isto entramos em um editor de texto por exemplo com o comando:
sudo nano /etc/rc.local e chegamos na tela abaixo:

























Acrescentamos as 04 linhas antes do exit 0
 02 linhas são para dar o start no agente e as outras 02 linhas para dar o start no gerente.

Podemos dar os starts via terminal, mas da forma acima é só ligar e já estará resolvido.

Agora vamos aos Scripts:

Primeiro os que vão na pasta navegador e rodará do lado do cliente:


O index.html


<!DOCTYPE html>
<html>
 <head>
  <title>Page Title</title>

  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
  <meta http-equiv="Pragma" content="no-cache"/>
  <meta http-equiv="Expires" content="0"/>

  <link rel="stylesheet" href="index.css">

  <script type="text/javascript" src= "jquery-1.11.2.js"> </script> 
  <script type="text/javascript" src= "ws.js"></script>
  <script type="text/javascript" src= "index.js"> </script> 
  
</head>
<body>

<div id="divComando">
    <span style="color:blue" id="spIpAgent"> IP Agent </span>
    <input type="text" id="txtIpAgent" value= "192.168.0.30"> <br>


    <table id= "tableTitulo" border="1" >
         <tr>
             <td style="width:70%;" > OID </td>
             <td style="width:30%;" > Valor </td>
                   
        </tr>           
    </table>


    <table id= "tableResultado" border="1" >
         <tr>
             <td style="width:70%;"  >  <input   type="text" class="text1"  id="txtOid1" value="1.1.1.1.1.1.1.1.1.0" />  </td>
             <td style="width:30%;"  >  <input   type="text" class="text1"  id="txtValor1" /> </td>
                   
        </tr> 
        <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid2" value="1.1.1.1.1.1.1.1.2.0"/>  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor2" /> </td>
                   
        </tr> 
       
       <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid3" value="1.1.1.1.1.1.1.1.3.0" />  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor3" /> </td>
                   
        </tr> 
        <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid4" value="1.1.1.1.1.1.1.1.4.0"/>  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor4" /> </td>
                   
        </tr> 
        <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid5" value="1.1.1.1.1.1.1.1.5.0"/>  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor5" /> </td>
                   
        </tr> 
        <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid6" value="1.1.1.1.1.1.1.1.6.0" />  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor6" /> </td>
                   
        </tr> 
        <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid7" value="1.1.1.1.1.1.1.1.7.0"/>  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor7" /> </td>
                   
        </tr> 
       
         <tr>
             <td style="width:70%;" >  <input   type="text"  class="text1" id="txtOid8" value="1.1.1.1.1.1.1.1.8.0"/>  </td>
             <td style="width:30%;" >  <input   type="text"  class="text1" id="txtValor8" /> </td>
                   
        </tr> 


    </table>


<button type="button" id="btPesquisar">Pesquisar</button>
<button type="button" id="btLoop">Loop</button>
  
  
</div>

</body>
</html>



 index.css

O script html é bastante simples e será formatado pelo index.css como segue:

#divComando

  
    background:#ccccdd;
    color: #000000;
    position:absolute;
    float: left;
    float: top;
    left:1px;
    top:10px;
    width:1000px;
    height:600px;
    border: 2px solid;
    border-radius: 0px;    
}

#divComando #spIpAgent
{
    width: 100px; 
    position:absolute;
    float: left;
    float: top;
    left:1px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}
#divComando #txtIpAgent
{
    width: 20%; 
    position:absolute;
    float: left;
    float: top;
    left:100px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}
#divComando #tableTitulo
{
    color: #0000aa;
    position:absolute;
    float: left;
    float: top; 
    left:5%;
    top:50px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center;
    width: 90%;
    border: 2px solid black;
}
#divComando #tableResultado
{
    color: #0000aa;
    position:absolute;
    float: left;
    float: top; 
    left:5%;
    top:100px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center;
    width: 90%;
    border: 2px solid black;
}

#divComando .text1
{
font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
    width: 90%;



#divComando #btPesquisar
 {

    position:absolute;
    float: left;
    float: top;
  margin-left: 400px;
    margin-top: 400px;

    background-color: #FFFFFF; 
    border: none; 
    color: black;
    padding: 16px 32px; /* tamanho do button */
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
     
   
}

#divComando #btPesquisar:hover 
{
    background-color: #4CAF50;
    color: white;
}

#divComando #btLoop
 {
    position:absolute;
    float: left;
    float: top;
    margin-left: 550px;
    margin-top: 400px;

    background-color: #FFFFFF; 
    border: none; 
    color: black;
    padding: 16px 32px; /* tamanho do button */
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
     
   
}
#divComando #btLoop:hover 
{
    background-color: #4CAF50;
    color: white;

}



 index.js


O index.js é o responsável pela lógica e a implementação do socket.io client, foi usado o jquery para tratar eventos de botão como visto abaixo:


var oid1=oid2=oid3=oid4=oid5=oid6=oid7=oid8="0";
var socket, io;
var ipAgent="0.0.0.0";
var msgFromserver = "";
var valores=[];

var ip = location.host;//Ip do Servidor HTML Conectado
var endereco="";
var loop;



$(document).ready(function () {

 ip = location.host;//Ip do Servidor HTML Conectado

    $('#btPesquisar').css('color','green');
    $('#btLoop').css('color','red');
    
 $("#txtIpAgent").val(ip);



    $('#btPesquisar').click(function () {

     $('#btLoop').css('color','red');
     $('#btPesquisar').css('color','green');
     clearInterval(loop);//Stop o setInterval
     conectar();
     pesquisar();

    }); //Fim do botao pesquisar



    $('#btLoop').click(function () {

    $('#btLoop').css('color','green');
    $('#btPesquisar').css('color','red');
    conectar();
    pesquisaLoop();
   

    }); //Fim do botao Loop


});

//============Funcao para conectar o Socket.io==============================

function conectar()
{

   endereco= "http://" + ip + ":3000";//Servidor socket.io no mesmo IP do servidor HTML

   socket = io.connect(endereco);


}



//==Funcao para conectar o Servidor Node.js para receber/Enviar WS==============
function pesquisar()
{


var oids="";


ipAgent=$("#txtIpAgent").val().trim();

oid1= $("#txtOid1").val().trim();
oid2= $("#txtOid2").val().trim();
oid3= $("#txtOid3").val().trim();
oid4= $("#txtOid4").val().trim();
oid5= $("#txtOid5").val().trim();
oid6= $("#txtOid6").val().trim();
oid7= $("#txtOid7").val().trim();
oid8= $("#txtOid8").val().trim();

  oids= ipAgent + "/"+ oid1 + "/" + oid2 + "/" + oid3 + "/" + oid4 + "/" + oid5 + "/" + oid6 + "/" + oid7 + "/" + oid8 +  "/";

  socket.emit('toServer', oids );//Envia para o servidor socket.io


  socket.on('toClient', function (msg) {//Evento que recebe do server mensagem do socket.io

  valores=msg.split("/");

  $("#txtValor1").val(valores[0]);
  $("#txtValor2").val(valores[1]);
  $("#txtValor3").val(valores[2]);
  $("#txtValor4").val(valores[3]);
  $("#txtValor5").val(valores[4]);
  $("#txtValor6").val(valores[5]);
  $("#txtValor7").val(valores[6]);
  $("#txtValor8").val(valores[7]);

  }); //Fim do evento que recebe do server mensagem do socket.io

}

//==================Funcao para pesquisar em loop================

function pesquisaLoop()
{


  loop=setInterval(function(){ pesquisar(); }, 1000);


}

Podemos observar que:

 socket.emit('toServer', oids ); envia o IP e as OIDS para o raspberry fazer a pesquisa no Agente.
no Raspberry tem um tratamento conforme o trecho abaixo:

 socket.on('toServer', function (data) {//Recebe mensagem de algum Cliente Conectado

    pesqOids=data.split("/");

   

    ipAgent=pesqOids[0];
    oids[0]=pesqOids[1];
    oids[1]=pesqOids[2];
    oids[2]=pesqOids[3];
    oids[3]=pesqOids[4];
    oids[4]=pesqOids[5];
    oids[5]=pesqOids[6];
    oids[6]=pesqOids[7];
    oids[7]=pesqOids[8];

    console.log("IP-->"    + ipAgent);
    console.log("OID 1-->" + oids[0]); 
    console.log("OID 2-->" + oids[1]); 
    console.log("OID 3-->" + oids[2]); 
    console.log("OID 4-->" + oids[3]); 
    console.log("OID 5-->" + oids[4]); 
    console.log("OID 6-->" + oids[5]); 
    console.log("OID 7-->" + oids[6]); 
    console.log("OID 8-->" + oids[7]); 

    leOids();
    
   }); 

Este trecho no Raspberry recebe através da chave toServer e separa a mensagem pelo delimitador "/".


Ainda no index.js temos o trecho que recebe os valores das leituras do agente através do trecho abaixo:

socket.on('toClient', function (msg) {//Evento que recebe do server mensagem do socket.io

  valores=msg.split("/");

  $("#txtValor1").val(valores[0]);
  $("#txtValor2").val(valores[1]);
  $("#txtValor3").val(valores[2]);
  $("#txtValor4").val(valores[3]);
  $("#txtValor5").val(valores[4]);
  $("#txtValor6").val(valores[5]);
  $("#txtValor7").val(valores[6]);
  $("#txtValor8").val(valores[7]);


  }); //Fim do evento que recebe do server mensagem do socket.io


No index.js este trecho aguarda o evento com a chave "toCliente" que vem com os valores do Raspberry.


leSnmp.js


Agora vamos ao código do leSnmp.js que roda no Raspberry:

var  static = require( 'node-static' );
var  http = require( 'http' );
var  snmp = require ("net-snmp");
var app = require('express')()
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);





//==========================variaveis globais============================
var port=80;
var pesqOids = [];
var oids=[8];
var valores=[];
var ipAgent="0.0.0.0";
var msgToClient=" ";
var soquete;




//===Servidor HTML arquivos da pasta navegador sao enviados ao cliente=========

var file = new static.Server( './navegador', {
    cache: 3600,
    gzip: true
} );

http.createServer( function ( request, response ) {
    request.addListener( 'end', function () {
        file.serve( request, response );
    } ).resume();
} ).listen( port );


//===============Servidor socket.io===================

server.listen(3000, function(){
   console.log("Socket.io Escutando a porta 3000... \n");
 });


    io.sockets.on('connection', function (socket) {

    soquete=socket;//soquete variavel global socket variavel local

    socket.on('toServer', function (data) {//Recebe mensagem de algum Cliente Conectado

    pesqOids=data.split("/");

   

    ipAgent=pesqOids[0];
    oids[0]=pesqOids[1];
    oids[1]=pesqOids[2];
    oids[2]=pesqOids[3];
    oids[3]=pesqOids[4];
    oids[4]=pesqOids[5];
    oids[5]=pesqOids[6];
    oids[6]=pesqOids[7];
    oids[7]=pesqOids[8];

    console.log("IP-->"    + ipAgent);
    console.log("OID 1-->" + oids[0]); 
    console.log("OID 2-->" + oids[1]); 
    console.log("OID 3-->" + oids[2]); 
    console.log("OID 4-->" + oids[3]); 
    console.log("OID 5-->" + oids[4]); 
    console.log("OID 6-->" + oids[5]); 
    console.log("OID 7-->" + oids[6]); 
    console.log("OID 8-->" + oids[7]); 

    leOids();
    
   }); 
   
});

//=========================Conecta com o Agent==================

function leOids()
{

 msgToClient="";

 session = snmp.createSession (ipAgent, "public");

 session.get (oids, function (error, varbinds)
 {
    if (error) 
    {
        console.error (error);
    } 
    else
    {
        for (var i = 0; i < varbinds.length; i++)
        {
            if (snmp.isVarbindError (varbinds[i]))
            {
                console.error (snmp.varbindError (varbinds[i]));

            }
               
            else
            {
                //console.log (varbinds[i].oid + " = " + varbinds[i].value);
                //msgOid[i]=varbinds[i].oid.toString();
                //valores[i]=varbinds[i].value.toString();
                //console.log(msgOid[i] + "-->" + msgValue[i] ); 
                //soquete.emit('toClient', msgOid[i] + "-->" + msgValue[i]);//Envia ao cliente

                valores[i] = varbinds[i].value.toString();

                msgToClient= msgToClient + valores[i] + "/";

                console.log(valores[i] );

            }                
        }

        console.log(msgToClient);
        soquete.emit('toClient', msgToClient );//Envia para o cliente socket.io                   
    }
});


}

Temos as seguintes partes:
a) Os requeres que usam os módulos instalados pelo npm
b) As variáveis globais.
c) O trecho  responsável pela conexão HTTP e transfere o HTML, CSS e Javascript para o navegador.
d) O Servidor de socket.io que envia e recebe mensagens do navegador
e) O gerente que obtém as informações do Agente.

Os trechos em azul são os códigos inteiros e os trechos em vermelho são partes que foram usados para comentários.

Com isto implementamos no Raspberry algumas coisas que podem ser feitas com o Node.js

Até a próxima.....










sexta-feira, 28 de abril de 2017

Agente SNMP com Raspberry Node.js e MCP3008 para teste

Neste post faremos um pequeno Agente SNMP para teste de um gernete SNMP.

Foi utilizado o CI MCP3008 como conversor A/D de 08 canais, portanto a tensão de entrada em cada canal de 0 á 3,3 Volts será convertida para 0 a 1023 e a conexão com o raspberry se dará através do protocolo SPI.

Portanto no raspberrry o protocolo SPI deverá estar habilitado.

Vejamos o set usado na figura abaixo:






















O CI MCP3008 foi montado em placa de circuito impresso com os 08 pots  para variar a entrada em cada canal e o circuito pode ser visto na figura abaixo:


Este circuito deve ser conectado com o raspberry através de jumpers seguindo a pinagem abaixo:





































Pino 17 é a conexão com os 3,3 Volts para alimentar o MCP3008
Pino 19 é a conexão com o MOSI do MCP3008.
Pino 21 é a conexão com o MISO do MCP3008.
Pino 23 é a conexão com o Clock do MCP3008.
Pino 24 é a conexão com o CS do MCP3008.
Pino 25 é a conexão com  o GND do MCP3008.

Terminado o Hardware podemos programar!!!

Primeiro deve ser criada uma pasta no raspberry para colocar os scripts
Dentro desta pasta além do script que vamos escrever tem que se baixar 02 módulos com os comandos abaixo após o prompt:

npm install    mcp3008.js   --save
npm install    snmpjs          --save

Estes comandos criará a pasta node_modules e será inserido os arquivos dentro.

Agora vamos ao script:


//==============Modulos=======================

var Mcp3008 = require('mcp3008.js');//Obs: Habilitar o SPI no raspberry
var snmp = require('snmpjs');

//==========================variaveis globais===================

var adc = new Mcp3008();
var adc1=adc2=adc3=adc4=adc5=adc6=adc7=adc8="0";//valores lidos no AD

//===========Intervalo de leituras do ADC==============================

setInterval(function(){ leCanais(); }, 1000);//chama a funcao leCanais() a cada  01 segundos

//===================LeCanais=======================================

function leCanais()
{
 leCanal1();
 leCanal2();
 leCanal3();
 leCanal4();
 leCanal5();
 leCanal6();
 leCanal7();
 leCanal8();

 console.log(adc1 + "  " + adc2 + "  " + adc3 + "  " + adc4 + "  " + 
 adc5 + "  " + adc6 + "  " + adc7 + "  " + adc8);

}

//=========Leitura do canal 1 do MCP3008======================
function leCanal1()
{
  adc.read(0, function (value)
   { 
   adc1=value.toString();
   });
}
//=========Leitura do canal 2 do MCP3008======================
function leCanal2()
{
  adc.read(1, function (value)
   { 
   adc2=value.toString();
   });
}
//=========Leitura do canal 3 do MCP3008======================
function leCanal3()
{
  adc.read(2, function (value)
   { 
   adc3=value.toString();
   });
}
//=========Leitura do canal 4 do MCP3008======================
function leCanal4()
{
  adc.read(3, function (value)
   { 
   adc4=value.toString();
   });
}
//=========Leitura do canal 5 do MCP3008======================
function leCanal5()
{
  adc.read(4, function (value)
   { 
   adc5=value.toString();
   });
}
//=========Leitura do canal 6 do MCP3008======================
function leCanal6()
{
  adc.read(5, function (value)
   { 
   adc6=value.toString();
   });
}
//=========Leitura do canal 7 do MCP3008======================
function leCanal7()
{
  adc.read(6, function (value)
   { 
   adc7=value.toString();
   });
}
//=========Leitura do canal 8 do MCP3008======================
function leCanal8()
{
  adc.read(7, function (value)
   { 
   adc8=value.toString();
   });
}

//====================Agent SNMP==========================

var agent = snmp.createAgent();

agent.request({ oid: '.1.1.1.1.1.1.1.1.1', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc1 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.2', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc2 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.3', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc3 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.4', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc4 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.5', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc5 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.6', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc6 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.7', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc7 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.request({ oid: '.1.1.1.1.1.1.1.1.8', handler: function (prq) {

  var val = snmp.data.createData({ type: 'OctetString',
      value: adc8 });

  snmp.provider.readOnlyScalar(prq, val);
} });


agent.bind({ family: 'udp4', port: 161 });


No início foi adicionado os 02 módulos através do requere.
A função setInterval chama as leituras dos A/D a cada segundo e atualiza as variáveis

Foi utilizado os OIDs '.1.1.1.1.1.1.1.1.1' até .1.1.1.1.1.1.1.1.8 para identificar um lugar na MIB para armazenar os valores das leituras dos ADs.

Usamos um programa de varredura de MIB e obtivemos o resultado abaixo:























Fizemos um GET em cada um do OIDs e os valores são de acordo com a posição dos pots.



É isto ai, o código está bem comentado e espero que sirva para alguém que precise fazer algum teste.

Até a próxima....







segunda-feira, 24 de abril de 2017

Arduino & W5100 Led on/off com refresh da página

Já postamos o comando via web usando o ajax no post:


Neste post abordaremos um script mais simples onde o comando ocorre com um refresh da página.

O set para este teste é bastante simples como pode ser visto na figura abaixo:


Trata-se do Arduino Mega 2560 com o Shield W5100 e um Led com um resistor de 220 Ohms em série entre o pino 6 e o GND.

A página apresentará 02 links  como visto abaixo:



Um link é para ligar o Led e o outro obviamente é para desligar.

O script que rodará no arduino será um servidor desta simples página e tratará as requisições dos links para ligar ou desligar o led.

Na monitoração serial podemos acompanhar o funcionamento do script como segue:


Logo na inicialização temos as informações da abertura da porta serial bem como o IP obtido pelo DHCP.
Após esta etapa podemos abrir o navegador usando o IP informado pela monitoração serial.

Clicando-se no link Ligar o led temos as seguintes informação na serial monitor:


Podemos ver que foi passado o parâmetro ?ledon.
O servidor ao receber este parâmetro sobe a tensão no pino 6 para 5 volts, o led ascende.

Clicando-se no link  Desligar o Led  temos as seguintes informações na serial monitor:


Podemos ver que foi passado o parãmetro ?ledoff.
O servidor ao receber este parâmetro desce a Zero a tensão do pino 06, o led apaga.

Observamos que quando o navegador faz a primeira requisição no servidor não é passado nenhum parâmetro  e portando só a página é enviada.

O script é bem tranquilo e está todo separado em funções para maior clareza:

#include <SPI.h>
#include <Ethernet.h>
  
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 13 };//Se nao carregar o DHCP usa este IP
     
EthernetServer server(80);
EthernetClient client;
  
String readString;
int Pin = 6;

//=====Configuracoes iniciais==========================  
void setup(){
  
  pinMode(Pin, OUTPUT);
  digitalWrite(Pin, LOW);//Inicia pino em Low
  Serial.begin(9600);

  Serial.println("Porta serial Ok");
  
  Serial.println("Iniciando a Ethernet...");
  Ethernet.begin(mac, ip);
  
  
  if(Ethernet.begin(mac) == 0)// Para usar o DHCP
  {
    Serial.println("Falha em carregar o DHCP");
    Serial.println("Usando o IP fixo");
    Serial.println(Ethernet.localIP());
  }
   else
  {
    Serial.println("DHCP carregado com Sucesso"); 
    Serial.println(Ethernet.localIP());
  } 
  
  
  
  server.begin();  
}

//================Envia pagina HTM ao Navegador====================
void enviaPaginaHTML()
{
    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html");
    client.println();
           
    client.println("<HTML>");
    client.println("<BODY>");
    client.println("<H1>Comanda o Led </H1>");
    client.println("<hr />");
    client.println("<br />");
           
    client.println("<a href=\"/?ledon\">Ligar o led</a>");
    client.println("<a href=\"/?ledoff\">Desligar o led</a><br />");   
           
    client.println("</BODY>");
    client.println("</HTML>");
           
    delay(1);
    client.stop();
}

//======================Trata a String recebida do Navegador===============

void trataString()
{
  
  if(readString.indexOf("?ledon") > 0)//Verifica se tem "?ledon" na String enviada pelo navegador
  {
    digitalWrite(Pin, HIGH);
    Serial.println("Ligado");
   }
  
   if(readString.indexOf("?ledoff") > 0)//Verifica se tem "?ledoff" na String enviada pelo navegador
   {
     digitalWrite(Pin, LOW);
     Serial.println("Desligado");
   }  
      
}
//=======================Loop principal====================================  
void loop()
{
  client = server.available();
  if (client)
  {
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
  
        if (readString.length() < 100)
        {
          readString += c;//Forma a String com o recebimento do navegador             
        }
        if (c == '\n')//Recebeu o "New Line" Final da requisicao feita pelo navegador
        {
          enviaPaginaHTML();//Envia pagina HTML para o Navegador
          trataString();//Verifica parametros da requisicao do Navegador
          Serial.println(readString);
          readString="";//Apaga String para receber nova requisicao    
        }
      }//Fim do if (client.available())
    }//Fim do while (client.connected())
  }
  
}//Fim do Loop


Temos:

As inclusões das bibliotecas
O setup para ajustes iniciais
A função que envia a página para o cliente ( navegador)  enviaPaginaHTML()
A função que analisa a requisição enviada pelo navegador trataString()
E o loop()

A Serial é usada para monitorar o funcionamento do script.


Este script é para um "jogo rápido"a melhor opção é usar o ajax para não haver refresh na página com o flicker.

Boa diversão...


segunda-feira, 13 de fevereiro de 2017

Node.js SNMP Socket.io

Neste post farei um "upgrade" do post anterior:


No post acima foi dado uma sugestão de como se criar um agente com o serviço do windows para poder escrever scripts do gerente e testar.
Vamos agora ler 02 OIDs de uma MIB e apresentá-la em um navegador através de uma simples interface gráfica.

Através de um diagrama de blocos podemos mostrar o funcionamento:



























Como pode ser observado o navegador através do request ao servidor recebe um response usando o protocolo HTTP na porta 80, nesta requisição é passado os scripts de HTML, CSS e Javascript para o navegador.
Uma vez que estes arquivos passem a rodar no navegador temos a interface gráfica e o socket.io cliente que efetuará a conexão com o socket.io server  na porta 3000 usando um protocolo que não necessita de ficar realizando request e response todo o tempo, pois é conectado full time.
Quando  o socket.io cliente solicita ao socket.io server este aciona o net-snmp que faz a requisição de dados de uma MIB no Agent.
Deixei a interface gráfica muito simples para não complicar a ideia da lógica dos trabalhos dos scripts:























Neste caso fornecemos o IP onde está o agente e 02 OIDs separados por "/" como pode ser observado na figura e obtemos a resposta do agente SNMP.
O socket.io cliente passa a string ip/oid/oid para o socket.io server e  é tratada através da função split.
O IP do gerente quando carregado a página por default é o mesmo IP do servidor que forneceu a página HTML porém se quiser acionar outro gerente em outro IP é possivel pois o botão Conectar estabelece a conexão do socket.io cliente com o server usando o IP que está na caixa de dialogo IP Gerente.
Para este projeto criei uma pasta web_Socket.io_Snmp e foi colocado duas pastas e um arquivo dentro:






















A pasta node_modules é criada a partir do momento que se instala os modulos através do comamdo npm install.
Para isto estando com o prompt do Dos e dentro da pasta criada digite um comando por vez:

> npm install express --save
>npm install net-snmp --save
>npm install request --save
>npm install socket.io --save

A pasta node_modules será criada com estes arquivos dentro.
O arquivo server.js é um script que vamos escrever e será comentado abaixo
A pasta do navegador tem os arquivos que serão transferidos para o navegador e pode ser vista na figura abaixo:






















Na pasta navegador temos os arquivos index.html, index.css, e index.js que são scripts que escreveremos e será comentada abaixo.
A pasta socket.io deve ser copiada da pasta node_modules como ser visto na figura abaixo:






















Na figura acima mostra a pasta onde está o arquivo socket.io cliente que rodará no navegador

Uma vez que todos os módulos estão carregados podemos analisar os scripts.
A parte da interface gráfica feita pelo index.html  e index.css bastante trivial e por isto poucos comentários a respeito:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

<link rel="stylesheet" href="index.css">

<script src= "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"> </script>

<script type= "text/javascript" src= "/socket.io-client/socket.io.js"> </script> 

<script type= "text/javascript" src= "index.js"> </script> 

</head>


<body>


<div id="divComando">

<span style="color:blue" id="spIpAgent"> IP Agent </span>
<input type="text" id="txtIpAgent" value= "192.168.0.30"> <br>
<span style="color:blue" id="spIpGerente"> IP Gerente </span>
<input type="text" id="txtIpGerente" value= "127.0.0.1"> <br>
<span style="color:blue" id="spOID"> OIDs </span>
<input type="text" id="txtOID" value= "1.3.6.1.2.1.1.1.0/1.3.6.1.2.1.1.3.0"> <br>
<input type="submit" id="btPesquisar" value="Pesquisar" />
 <input type="submit" id="btConectar" value="Conectar" />

<textarea id="txtMsg">Envia IP e OIDs e recebe as leituras do Agent   </textarea>
  
    </div>

</body>
</html>

Podemos observar que o Jquery foi baixado através da tag: 
<script src= "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"> </script>
Pode-se optar por adicionar o arquivo da Jquery na pasta do navegador e baixar do servidor que estamos escrevendo
O socket.io cliente foi baixado através da tag:
<script type= "text/javascript" src= "/socket.io-client/socket.io.js"> </script> 
Esta tag leva ao endereço da pasta que foi copiada do node_modules para a pasta do navegador.

Com relação aos arquivo index.css foi usado somente tratamento aos IDs dos elementos do HTML e segue abaixo:

#divComando
  
    background:#ccccdd;
    color: #000000;
    position:absolute;
    float: left;
    float: top;
    left:1px;
    top:10px;
    width:1000px;
    height:600px;
    border: 2px solid;
    border-radius: 0px;    
}

#divComando #spIpAgent
{
    width: 100px; 
    position:absolute;
    float: left;
    float: top;
    left:1px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}

#divComando #txtIpAgent
{
    width: 20%; 
    position:absolute;
    float: left;
    float: top;
    left:100px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}

#divComando #spIpGerente
{
    width: 100px; 
    position:absolute;
    float: left;
    float: top;
    left:450px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}

#divComando #txtIpGerente
{
    width: 20%; 
    position:absolute;
    float: left;
    float: top;
    left:550px;
    top:5px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}
#divComando #spOID
{
    width: 20px; 
    position:absolute;
    float: left;
    float: top;
    left:1px;
    top:35px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center; 
}
#divComando #txtOID
{
    width: 90%; 
    position:absolute;
    float: left;
    float: top;
    left:50px;
    top:35px;
    font-size: 15px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: left; 
}

#divComando #btConectar
{
    position:absolute;
    float: left;
    float: top;
    left:390px;
    top:65px;
    width: 10%;
    font-size: 13px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center;
}
#divComando #btPesquisar
{
    position:absolute;
    float: left;
    float: top;
    left:510px;
    top:65px;
    width: 10%;
    font-size: 13px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: center;
}
#divComando #txtMsg
{
    width: 90%; 
    height:450px;
    position:absolute;
    float: left;
    float: top;
    left:50px;
    top:100px;
    font-size: 10px;
    font-family: Arial Black;
    font-style: oblique;
    text-align: left; 
}

Agora vamos ao arquivo do server.js:

//========Variaveis Globais e  Modulos =======================
var  static = require( 'node-static' );
var  http = require( 'http' );
var  snmp = require ("net-snmp");

var   port = 80;


var app = require('express')()
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);


var msgOid=[];
var msgValue=[];
var oids = [];
var ipAgent;

var soquete;
var session;




//===Servidor HTML arquivos da pasta navegador sao enviados ao cliente=========
var file = new static.Server( './navegador', {
    cache: 3600,
    gzip: true
} );

http.createServer( function ( request, response ) {
    request.addListener( 'end', function () {
        file.serve( request, response );
    } ).resume();
} ).listen( port );

//===============Servidor socket.io===================

server.listen(3000, function(){
   console.log("Socket.io Escutando a porta 3000...");
 });


io.sockets.on('connection', function (socket) {

   soquete=socket;//soquete variavel global socket variavel local

    socket.on('toServer', function (data) {//Recebe mensagem de algum Cliente Conectado

    console.log("Do Cliente--> " + data);
    snmpSolicitado(data);

      //socket.broadcast.emit('toClient', data);//Emite para todos clientes menos o emitente
      //socket.emit('toClient', data);//Somente o emitente recebe retorno do Servidor
    
   }); 


    socket.on('toLogin', function (data) {//Recebe mensagem de login

    console.log("Do Cliente--> " + data);
    
   });  



   
});

//============Analisa a mensagem que vem do cliente==========================

function snmpSolicitado(msgSnmp)
{

  var vetorMsg=msgSnmp.split("/");
  var ipAgent=vetorMsg[0].trim();

  var strOid1=vetorMsg[1].trim();
  var strOid2=vetorMsg[2].trim();

  oids[0]=strOid1;
  oids[1]=strOid2;

  session = snmp.createSession (ipAgent, "public");

  getSecaoSnmp();

}


//========Conexao com o Agent SNMP================================

function getSecaoSnmp()
{

  session.get (oids, function (error, varbinds)
 {
    if (error) 
    {
        console.error (error);
    } 
    else
    {
        for (var i = 0; i < varbinds.length; i++)
        {
            if (snmp.isVarbindError (varbinds[i]))
            {
                console.error (snmp.varbindError (varbinds[i]));

            }
               
            else
            {
                //console.log (varbinds[i].oid + " = " + varbinds[i].value);

                msgOid[i]=varbinds[i].oid.toString();
                msgValue[i]=varbinds[i].value.toString();

                //console.log(msgOid[i] + "-->" + msgValue[i] ); 

                soquete.emit('toClient', msgOid[i] + "-->" + msgValue[i]);//Envia ao cliente


            }                
        }                  
    }
});


}

Foi dividido em blocos para a melhor compreenção:
  • As variaveis globais e modulos.
  • O servidor estático que efetua a transferência de arquivos para o navegador.
  • O socket.io server que recebe e envia as mensagens para o socket.io client.
  • Analisa a mensagem chegada do Navegador.
  • Finalmente a parte que faz a conexão com o Agent de SNMP.
Vale ressaltar aqui que a linha: 

soquete.emit('toClient', msgOid[i] + "-->" + msgValue[i]);//Envia ao cliente

Envia a mensagem para o cliente e o script index.js que roda no navegador receberá este evento identificando através da palavra chave toCliente, veja o trecho que no script do navegador tratará este envio do servidor:

 socket.on('toClient', function (msg) {//Evento que recebe mensagem do socket.io   
    setLogs("Mensagem recebida-->" + msg + "\n");
    });

Então a função socket.emit associa uma chave que o receptor ( navegador ou servidor) identifica e sabe qual tratamento aplicar.
Desta forma com nome de chaves diferentes podemos identificar que tratamento queremos dar a mensagem enviada.

Por último vamos ao script do navegador que estará conectado através do socket.io ou seja o index.js:

var ipAgent,ipGerente,endereco;
var oids;
var msg;
var socket, io;


//==========Funcoes chamadas apos carregar HTML=================

$(document).ready(function () {//Aguarda carregar HTML

    ipGerente = location.host;//Ip onde o Navegador se conectou Default
    $("#txtIpGerente").val(ipGerente);//Preenche a caixa Ip Gerente


    $("#btConectar").click(function () {//Click no botao Conectar

        conectaServidor();//Estabelece conexao socket.io
       
    }); //fim click botao Conectar


$("#btPesquisar").click(function () {//Click no botao Pesquisar

  setLogs("\n");//Insere linha branca entre pesquisas
  ipAgent = $("#txtIpAgent").val().trim();//Le o IP da caixa texto
       oids = $("#txtOID").val().trim();//Le o OID da caixa texto
       msg=ipAgent + "/" + oids;//Mensagem a ser enviada para o servidor
       setLogs("Mensagem Enviada-->" + msg + "\n");//Mensagem enviada ao servidor
       socket.emit('toServer', msg);//Envia mensagem atraves do socket.io

    }); //fim click botao Pesquisar
  
});


//==Funcao para conectar o Servidor Node.js para receber/Enviar WS==============
function conectaServidor()
{

    ipGerente=$("#txtIpGerente").val().trim();//Le a caixa IP Gerente

    endereco= "http://" + ipGerente + ":3000";//Endereco do servidor socket.io

    setLogs("Conectado-->"+ endereco + "\n" );

    socket = io.connect(endereco);

    socket.emit('toLogin', "Entrou um  cliente \n" );//Log no Servidor de Entrada de Cliente

    socket.on('toClient', function (msg) {//Evento que recebe mensagem do socket.io   
    setLogs("Mensagem recebida-->" + msg + "\n");

    });

}

//========Funcao para Imprimir na Area de Texto======================
function setLogs(msg)
{

$("#txtMsg").append(msg);

}

Foi usado o Jquery para tratar a lógica dos elementos HTML e podemos ver que esperamos carregar todos os elementos HTML antes de chamar funções que vão manipular os mesmos.

Fazemos a requisicão dos arquivos HTML,CSS, Javascript através da porta 80 e a conexão com o socket.io através da porta 3000.

Tive alguns problemas quando o navegador armazenava os scripts em cache e não atualizava quando modificava o código as vezes foi necessário limpar os dados de navegação da máquina.

Estes scripts podemos ver 02 OIDs mas dá para ser alterado no trecho do server.js onde analisa as mensagens recebidas do navegador colocando-se um loop for para ler um array maior, mas isto vou deixar para os programadores.....

function snmpSolicitado(msgSnmp)
{

  var vetorMsg=msgSnmp.split("/");
  var ipAgent=vetorMsg[0].trim();

  var strOid1=vetorMsg[1].trim();
  var strOid2=vetorMsg[2].trim();

  oids[0]=strOid1;
  oids[1]=strOid2;

  session = snmp.createSession (ipAgent, "public");

  getSecaoSnmp();

}

Os códigos estão bem comentados e é fácil de implementar.

Espero ter contribuido com alguma ideia.

Até a próxima.