terça-feira, 25 de novembro de 2014

Agentuino + Zabbix - Parte 1 - implementação do Agentuino no projeto de monitoramento de temperatura

Olá pessoal,



Depois de um bom tempo sem postar no blog vamos continuar de onde paramos...

Vamos à implementação do Agentuino no projeto de monitoramento de temperatura. A idéia básica é fazer com que o Arduino colete a temperatura e disponibilize esta informação via SNMP. O código adaptado ficou assim:

#include<dht11.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <IRremote.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Agentuino.h> 

byte termometro[8] =//icone para termometro
{
    B00100,
    B01010,
    B01010,
    B01110,
    B01110,
    B11111,
    B11111,
    B01110
};


byte higrometro[8] = //icone para umidade
{
    B00100,
    B00100,
    B01010,
    B01010,
    B10001,
    B10001,
    B10001,
    B01110,
};

 //codigo rawcode capturado do ar condiconado via IRRecvDump
 unsigned int powerOn[92] = {8950,4300,650,500,700,450,650,1600,650,500,600,1650,700,450,650,500,650,500,650,450,600,1700,600,550,550,550,600,1700,600,500,600,550,600,550,600,550,600,500,700,450,600,550,600,550,600,500,600,550,600,550,600,550,550,550,600,550,600,550,600,550,550,550,600,550,600,550,600,500,600,550,650,500,600,550,600,500,600,550,600,550,600,550,550,1700,600,550,600,550,550,1700,700};
  

// this must be unique
static byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
// change network settings to yours


EthernetClient client;


// RFC1213-MIB OIDs
// .iso (.1)
// .iso.org (.1.3)
// .iso.org.dod (.1.3.6)
// .iso.org.dod.internet (.1.3.6.1)
// .iso.org.dod.internet.mgmt (.1.3.6.1.2)
// .iso.org.dod.internet.mgmt.mib-2 (.1.3.6.1.2.1)
// .iso.org.dod.internet.mgmt.mib-2.system (.1.3.6.1.2.1.1)
// .iso.org.dod.internet.mgmt.mib-2.system.sysDescr (.1.3.6.1.2.1.1.1)
static char sysDescr[20] PROGMEM     = "1.3.6.1.2.1.1.1.0";  // read-only  (DisplayString)

// .iso.org.dod.internet.mgmt.mib-2.system.sysObjectID (.1.3.6.1.2.1.1.2)
static char sysObjectID[20] PROGMEM   = "1.3.6.1.2.1.1.2.0";  // read-only  (ObjectIdentifier)

// .iso.org.dod.internet.mgmt.mib-2.system.sysUpTime (.1.3.6.1.2.1.1.3)
static char sysUpTime[20] PROGMEM     = "1.3.6.1.2.1.1.3.0";  // read-only  (TimeTicks)

// .iso.org.dod.internet.mgmt.mib-2.system.sysContact (.1.3.6.1.2.1.1.4)
static char sysContact[20] PROGMEM    = "1.3.6.1.2.1.1.4.0";  // read-write (DisplayString)

// .iso.org.dod.internet.mgmt.mib-2.system.sysName (.1.3.6.1.2.1.1.5)
static char sysName[20] PROGMEM       = "1.3.6.1.2.1.1.5.0";  // read-write (DisplayString)

// .iso.org.dod.internet.mgmt.mib-2.system.sysLocation (.1.3.6.1.2.1.1.6)
static char sysLocation[20] PROGMEM   = "1.3.6.1.2.1.1.6.0";  // read-write (DisplayString)

// .iso.org.dod.internet.mgmt.mib-2.system.sysServices (.1.3.6.1.2.1.1.7)
static char sysServices[20] PROGMEM   = "1.3.6.1.2.1.1.7.0";  // read-only  (Integer)

// Arduino defined OIDs
// .iso.org.dod.internet.private (.1.3.6.1.4)
// .iso.org.dod.internet.private.enterprises (.1.3.6.1.4.1)
// .iso.org.dod.internet.private.enterprises.arduino (.1.3.6.1.4.1.36582)
// .iso.org.dod.internet.private.enterprises.arduino.value.valA0-A5 (.1.3.6.1.4.1.36582.3.1-6)
/*
static char valA0[] PROGMEM   = "1.3.6.1.4.1.36582.3.1.0";  // read-only  (Integer)

static char valA1[] PROGMEM   = "1.3.6.1.4.1.36582.3.2.0";  // read-only  (Integer)

// .iso.org.dod.internet.private.enterprises.arduino.value.valD0-D13 (.1.3.6.1.4.1.36582.3.7-20)
static char valD0[] PROGMEM   = "1.3.6.1.4.1.36582.3.7.0";  // read-only  (Integer)

static char valD1[] PROGMEM   = "1.3.6.1.4.1.36582.3.8.0";  // read-only  (Integer)
*/
static char temp[24] PROGMEM = "1.3.6.1.4.1.36582.3.1.0";  // read-only  (Integer)

static char locDescr[20] = "Agentuino";

static char locObjectID[20] = "1.3.6.1.4.1.36582";
static uint32_t locUpTime = 0;
static char locContact[20] = "Eric Gionet";
static char locName[20] = "Agentuino";
static char locLocation[20] = "Nova Scotia, CA";
static int32_t locServices = 7;
static int loctemp = 0; 

//uint32_t prevMillis = millis();
uint32_t dispMillis = 0;


char oid[SNMP_MAX_OID_LEN];
SNMP_API_STAT_CODES api_status;
SNMP_ERR_CODES status;



boolean disparou;
boolean disptempo;

// Objeto irsend para emissor infrared 
IRsend irsend;

// Objeto rtc (relogio de tempo real)
//RTC_DS1307 rtc;

int sinVal = 0;
int toneVal = 0;

#define lcdAddr 0x20
// Objeto LCD com 2 linhas e 16 caracteres por linha
LiquidCrystal_I2C lcd(lcdAddr, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);  


//objeto sensor para DHT11 (sensor de temperatura)
dht11 sensor;


void pduReceived()
{
  SNMP_PDU pdu;
  Serial.println("UDP Packet Received...");
  api_status = Agentuino.requestPdu(&pdu);
  if ( (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT || pdu.type == SNMP_PDU_SET)
    && pdu.error == SNMP_ERR_NO_ERROR && api_status == SNMP_API_STAT_SUCCESS ) {
    pdu.OID.toString(oid);
    Serial.print("OID = ");
    Serial.println(oid);
    
    if ( pdu.type == SNMP_PDU_SET ) {
      status = SNMP_ERR_READ_ONLY;
    } else if ( strcmp_P(oid, sysDescr ) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locDescr);
    } else if ( strcmp_P(oid, sysUpTime ) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_TIME_TICKS, locUpTime);
    } else if ( strcmp_P(oid, sysName ) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locName);
    } else if ( strcmp_P(oid, sysContact ) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locContact);
    } else if ( strcmp_P(oid, sysLocation ) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locLocation);
    } else if ( strcmp_P(oid, sysServices) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_INT, locServices);
     } else if ( strcmp_P(oid, sysObjectID) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locObjectID);
    } else if ( strcmp_P(oid, temp) == 0 ) {
      status = pdu.VALUE.encode(SNMP_SYNTAX_INT, loctemp);
    
    } else {
      status = SNMP_ERR_NO_SUCH_NAME;
    }
    pdu.type = SNMP_PDU_RESPONSE;
    pdu.error = status;
    Agentuino.responsePdu(&pdu);
  }
  Agentuino.freePdu(&pdu);
  
}


void adicionazero(int numar) { //this adds a 0 before single digit numbers
  if (numar >= 0 && numar < 10) {
    lcd.write('0');
  }
  lcd.print(numar);
}


void lcdnormal(int temp, int umi) {
    
      //    DateTime now = rtc.now(); // carrega a data e hora do RTC
    
          lcd.clear();  //limpa o display do LCD.     
          lcd.home();
          lcd.createChar(1,termometro);
          lcd.createChar(2,higrometro);
          lcd.setCursor(1,0);
          lcd.write(1);
          lcd.setCursor(3,0);
          //lcd.print("Temp: ");  //imprime a string no display do LCD.                 
          lcd.print(temp); // imprime a temperatura no LCD
          lcd.write((char)223); //Simbolo de graus celsius
          lcd.print("C");
          
          lcd.setCursor(10,0);
          lcd.write(2);
          lcd.setCursor(12,0);
          lcd.print(umi); // imprime a umidade no LCD
          lcd.print("%");
  
          lcd.setCursor(1, 1);
        
}


void lcdalto(int temp) {
          lcd.clear();  //limpa o display do LCD.    
          lcd.home();
          lcd.createChar(1,termometro);
          lcd.setCursor(1,0);
          lcd.write(1);
          lcd.setCursor(3,0);
          //lcd.print("Temp: ");  //imprime a string no display do LCD.                 
          lcd.print(temp); // imprime a temperatura no LCD
          lcd.write((char)223); //Simbolo de graus celsius
          lcd.print("C");
          lcd.setCursor(0,1);  //posiciona o cursor na coluna 0 linha 1 do LCD.
          lcd.print("TEMP. ALTA!"); //Imprime a string no display do LCD.
}

void setup() {

  Serial.begin(9600);  // Inicia conexao serial para monitoramento
  Ethernet.begin(mac);
  
//  Serial.println("Ethernet iniciado");
  
  #ifdef AVR
    Wire.begin();
  #else
    Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
  #endif

  lcd.begin(16,2); // inicializa o lcd 
  lcd.setBacklight(1);
  
  //Serial.println("LCD iniciado");
  
  pinMode(8,OUTPUT);  //pino 8 para saida do alto falante

  
  disparou = false;
  disptempo = true;

  api_status = Agentuino.begin();
 
  if ( api_status == SNMP_API_STAT_SUCCESS ) {
    Agentuino.onPduReceive(pduReceived);
    delay(10);
    Serial.println("SNMP Agent Initiated...");
    return;
  } else
  {
  delay(10);
  Serial.print("SNMP Agent failed!");
  }

}

 
void loop() {

   
    int chk = sensor.read(7);  //le a temperatura
  
  
  //  Serial.println("Lendo Sensor: ");
    
  /*  
    //Verifica o status do DHT11
    switch(chk) {
        case DHTLIB_OK:
            Serial.println("OK");
            break;
        case DHTLIB_ERROR_CHECKSUM:
            Serial.println("Erro no checksum");
            break;
        case DHTLIB_ERROR_TIMEOUT:
            Serial.println("Tempo esgotado");
            break;
        default:
            Serial.println("Erro desconhecido");
    }
    
    */
    
    // Carrega os valores de temperatura e umidade do sensor nas variaveis correspondentes
    int t = sensor.temperature;  
    int u = sensor.humidity;  
    
    // Monitora a umidade e temperatura para a conexao serial
    /*
    Serial.print("Umidade (%): ");
    Serial.println(u);
    Serial.print("Temperatura (graus Celsius): ");
    Serial.println(t);
   */
   
   if (t > 26) { //se temperatura for maior que 26 graus
          
          // Aciona o ar condicionado novamente 
          //irsend.sendRaw(powerOn,92,38);
          
          if (!disparou) {
          //    Serial.print("Dispara primeira");
              irsend.sendRaw(powerOn,92,38);
              dispMillis = millis();
              disptempo = false;
              //Serial.print("Disparou pela primeira vez");
          }
          
         
         //Aguarda 5 minutos para enviar novamente o codigo IR caso a temperatura nao baixe
          if ((millis() - dispMillis > 300000) && (!disptempo)){
              irsend.sendRaw(powerOn,92,38);
            //  Serial.print("Disparou por tempo");
              disptempo = true;
          }
         
          // Exibe o valor da temperatura lida pelo sensor
          
          lcdalto(t);
          
          
          //Envia sinal sonoro para o pino 8 - alto falante
          for (int x=0; x<180; x++) {
            // converte graus para radianos e depois obtem o valor do seno
            //Serial.print("Toca o som");
             sinVal = (sin(x*(3.1416/180)));
             toneVal = 2000+(int(sinVal*1000));
             tone(8, toneVal);
             delay(2);
          }
          
          
          disparou = true;
         
         delay(2000);
         
    }
    else
    { 
          // Temperatura normal  
          
          lcdnormal(t,u);    
          
          delay(2000);
          
          noTone(8);
          
          disparou = false;
          
    }
    
    Agentuino.listen();
   
    loctemp = t;  

}



Cabe aqui algumas considerações sobre a implementação:

1) Complexidade do código e uso de memória SRAM: como vocês podem ver no código que utilizamos muitas variáveis e simplesmente não rodou no Arduino Duemilanove!!! TIve que enxugar ao máximo o código e mesmo assim o Arduino reiniciava. Comprei um Arduino Leonardo com mais SRAM (porém com menos espaço na Flash) e aí rodou bem. Tenho a impressão que o Arduino Leonardo é mais rápido que os outros Arduinos.
2) Uso da serial: se vocês observarem no código eu comentei bastantes linhas de debug de código pela Serial. Isso ajuda a conservar memória porém não permite você analisar mais a fundo se o código está rodando corretamente.
3) Eliminação do uso do shield GSM e do código de envio de e-mail.


Para testarmos se o Agentuino está rodando perfeitamente basta fazer um teste com um cliente snmp via linha de comando:
snmpget -1 -1 -public <endereço IP do Arduino> temp
    ou
snmpget -1 -1 -public <endereço IP do Arduino> 1.3.6.1.4.1.36582.3.1.0 
    Onde:
    -v 1: versão do SNMP (1)
    -r 1: número de tentativas (1 tentativa)
    -c public: nome da community (public)
    192.168.2.64: ip do arduino que está rodando o agentuino
    1.3.6.1.4.1.36582.3.1.0 ou temp: OID que armazena a temperatura
O comando deverá retornar a temperatura coletada no momento.
No próximo post veremos como implementar o Zabbix para coletarmos e visualizarmos a temperatura através de SNMP.

Dúvidas entrem em contato,

Até a próxima!



26 comentários:

  1. Muito bom o seu codigo, poderia nos dizer onde conseguir as bibiotecas para tambem testar ?

    ResponderExcluir
    Respostas
    1. Nas novas versões do Arduino IDE já podem ser feitas o download das bibliotecas na própria IDE vá em:

      Sketck / Include Library / Manage Libraries

      Procure por "Agentuino".

      Excluir
  2. Meu caro, como eu obtenho a MIB da umidade ?

    ResponderExcluir
    Respostas
    1. Você determina qual o OID vai ser o da umidade. Veja que no código a OID da temperatura é:

      static char temp[24] PROGMEM = "1.3.6.1.4.1.36582.3.1.0"; // read-only (Integer)

      Você pode acrescentar a OID de umidade assim:

      static char umidade[24] PROGMEM = "1.3.6.1.4.1.36582.3.2.0"; // read-only (Integer)

      E você tem que criar no restante do código as variáveis que armazenam o valor da umidade.

      Excluir
  3. Alguem já tentou fazer o zabbix se comunicar com o Arduino ?

    ResponderExcluir
  4. Fernando estou usando seu código, so que esta dando essa mensagem de erro.

    In file included from C:\arduino\arduino-1.6.5-r5\hardware\arduino\avr\cores\arduino/Arduino.h:28:0,
    from C:\arduino\arduino-1.6.5-r5\libraries\DHT/DHT.h:10,
    from temp_snmp.ino:1:
    temp_snmp:55: error: variable 'sysDescr' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:58: error: variable 'sysObjectID' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:61: error: variable 'sysUpTime' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:64: error: variable 'sysContact' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:67: error: variable 'sysName' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:70: error: variable 'sysLocation' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:73: error: variable 'sysServices' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:90: error: variable 'temp' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
    temp_snmp:116: error: 'IRsend' does not name a type
    temp_snmp:126: error: 'NEGATIVE' was not declared in this scope
    temp_snmp:130: error: 'dht11' does not name a type
    temp_snmp.ino: In function 'void setup()':
    temp_snmp:238: error: no matching function for call to 'LiquidCrystal_I2C::begin(int, int)'
    temp_snmp.ino:238:17: note: candidate is:
    In file included from temp_snmp.ino:3:0:
    C:\arduino\arduino-1.6.5-r5\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:76:7: note: void LiquidCrystal_I2C::begin()
    void begin();
    ^
    C:\arduino\arduino-1.6.5-r5\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:76:7: note: candidate expects 0 arguments, 2 provided
    temp_snmp.ino: In function 'void loop()':
    temp_snmp:268: error: 'sensor' was not declared in this scope
    temp_snmp:310: error: 'irsend' was not declared in this scope
    temp_snmp:319: error: 'irsend' was not declared in this scope
    variable 'sysDescr' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

    ResponderExcluir
  5. Levi,

    Vi os erros que estão ocorrendo e o pessoal tem relatado problemas com a versão do seu compilador (1.6.5-r5). Experimente baixar e instalar uma versão anterior e veja se roda.

    ResponderExcluir
    Respostas
    1. Boa noite Fernando, obrigado pela atenção, eu baixei a versão 1.6.4 e mesmo assim continua com o mesmo erro.


      In file included from C:\ARDUINO-1.6.4\hardware\arduino\avr\cores\arduino/Arduino.h:28:0,
      from C:\ARDUINO-1.6.4\libraries\DHT/DHT.h:10,
      from DHteste2.ino:1:
      DHteste2:55: error: variable 'sysDescr' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:58: error: variable 'sysObjectID' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:61: error: variable 'sysUpTime' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:64: error: variable 'sysContact' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:67: error: variable 'sysName' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:70: error: variable 'sysLocation' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:73: error: variable 'sysServices' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:90: error: variable 'temp' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
      DHteste2:116: error: 'IRsend' does not name a type
      DHteste2:126: error: 'NEGATIVE' was not declared in this scope
      DHteste2:130: error: 'dht11' does not name a type
      DHteste2.ino: In function 'void setup()':
      DHteste2:238: error: no matching function for call to 'LiquidCrystal_I2C::begin(int, int)'
      DHteste2.ino:238:17: note: candidate is:
      In file included from DHteste2.ino:3:0:
      C:\ARDUINO-1.6.4\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:76:7: note: void LiquidCrystal_I2C::begin()
      void begin();
      ^
      C:\ARDUINO-1.6.4\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:76:7: note: candidate expects 0 arguments, 2 provided
      DHteste2.ino: In function 'void loop()':
      DHteste2:268: error: 'sensor' was not declared in this scope
      DHteste2:310: error: 'irsend' was not declared in this scope
      DHteste2:319: error: 'irsend' was not declared in this scope
      variable 'sysDescr' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

      Excluir
  6. Fernando nitatori, ainda eu não consegui fazer funcionar o código, poderia me ajudar?

    ResponderExcluir
  7. Resolvi o problema pessoal:
    coloque const na frente das static char

    ResponderExcluir
    Respostas
    1. Que bom! Está funcionando certinho? Conseguiu ler a temperatura via SNMP ?

      Excluir
    2. Com o const consegui, mudei a versão da IDE e o modelo do arduino e não funcionou. Não entendo...

      Excluir
  8. Pessoal, conseguiram fazer funcionar? O código acima funcionou para mim e para algumas versões do Arduino IDE ou até mesmo para outros Arduinos pode ter diferenças e dar problemas.

    ResponderExcluir
  9. O código funcionou bem, consigo coletar via linha de comando a OID referente ao sensor que estou tentando utilizar. Maravilha! Porém quando eu coloco um IP para de acesso externo não consigo obter a OID. Alguém teve esse problema?

    ResponderExcluir
  10. nao consegui compilar o código, creio que estou me perdendo nas bibliotecas, onde posso acha-las?

    ResponderExcluir
  11. /var/folders/bp/93hys7xj0zxd5t6hpg0_qpyw0000gn/T/arduino_modified_sketch_642501/AnalogReadSerial.ino:1:19: fatal error: dht11.h: No such file or directory
    #include
    ^
    compilation terminated.
    exit status 1
    Erro compilando para a placa Arduino/Genuino Mega or Mega 2560
    esse é o erro que esta apresentando. podem me ajudar?

    ResponderExcluir
    Respostas
    1. Você baixou as bibliotecas necessárias? Em meus posts anteriores tem os links para baixar as bibliotecas.

      Excluir
  12. Se eu quiser colocar mais de um sensor de temperatura, como faço para colocar mais OIDs?

    ResponderExcluir
  13. Fernando, conseguiu fazer funcionar com apenas 1 sensor?

    ResponderExcluir
  14. Muito bom o seu post, era justamente o que eu estava procurando. Só não entendi em que parte do seu código inseriu o valor da temperatura (t) dentro da OID 1.3.6.1.4.1.36582.3.1.0. Poderia esclarecer ?

    ResponderExcluir
  15. Alguém conseguiu rodar? Eu consigo compilar o exemplo Agente sem erros porém quando uso o snmpget para verificar os valores da timeout.

    Estou usando o Arduino Uno e a versão da IDE é 1.8.19.0.

    ResponderExcluir
  16. Bom dia gostaria de um código igual a esse com mais alguns sensores de tensão e corrente alguém teria algum para disponibilizar ou não tenho mto mas pago pelo código.

    ResponderExcluir
  17. Tentei aqui e não funcionou tambem, tive que alterar tambem o display para oled mas, as bibliotecas Agentuino e dht11 não acha, mesmo instaladas, e atualizadas.

    ResponderExcluir