terça-feira, 19 de agosto de 2014

SNMP com Arduino - Agentuino




Olá pessoal,

Hoje vamos abordar o Agentuino - projeto que implementa um agente SNMP no Arduino permitindo que dados do dispositivo - informações sobre pinos de entrada e saída, sensores, etc - sejam disponibilizados através do protocolo SNMP e com isso agentes de monitoramento SNMP "conversam" através do protocolo SNMP para coletar esses dados. Isso permite que os dados sejam monitorados periodicamente através da rede e permitem o registro de dados e a exibição de forma gráfica desses dados coletados.
Dentro do projeto de acionamento automático de ar condicionado com infravermelho apontamos que seria necessário ter registros periódicos da temperatura e se tivéssemos que utilizar os e-mails ou SMS para fazer esses registros estes somente seriam ativados no acionamento por temperatura alta e não em períodos pré-determinados e assim não teríamos um monitoramento completo. Utilizando SNMP a coleta de dados é periódica e disponibilizada para leitura para os agentes de monitoramento SNMP e muitos destes agentes (por exemplo o Zabbix, Nagios, etc) permitem a composição de gráficos e também a emissão de alertas de acordo com condições específicas dos dados coletados. No caso do projeto isto seria o ideal: a temperatura seria coletada em intervalos periódicos, seria registrada e poderíamos montar um gráfico de monitoramento e acompanhar. Além disso seria possível programar alertas no agente de monitoramento para o caso em que a temperatura chegasse a um limite pré-programado e seria possível emitir alertas de e-mail e SMS. Era tudo que precisávamos! No Arduino não precisaríamos usar o código de envio de e-mail e como veremos mais tarde descobri que não precisaríamos usar o shield GSM também!
Para utilizar SNMP no Arduino você deve estar com um shield de rede ativa e funcionando e baixar a biblioteca agentuino. Fiquem atentos para as bibliotecas que o agentuino requer e baixe-as.
A biblioteca agentuino está disponível em:

https://code.google.com/p/agentuino/source/checkout

Utilize um cliente svn por linha de comando e baixe a versão do agentuino mais nova. Depois de instalado o cliente basta dar o comando:

svn checkout http://agentuino.googlecode.com/svn/trunk/ agentuino-read-only

A biblioteca será baixada na pasta agentuino-read-only na pasta de onde você rodou o comando.

Vamos ao código de exemplo do agentuino (vem na biblioteca):



/**
* Agentuino SNMP Agent Library Prototyping...
*
* Copyright 2010 Eric C. Gionet <lavco_eg@hotmail.com>
*
*/
#include <Streaming.h>         // Include the Streaming library
#include <Ethernet.h>          // Include the Ethernet library
#include <SPI.h>
#include <MemoryFree.h>
#include <Agentuino.h> 
#include <Flash.h>
//
#define DEBUG
//
static byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
static byte ip[] = { 192, 168, 2, 64 };
static byte gateway[] = { 192, 168, 2, 1 };
static byte subnet[] = { 255, 255, 255, 0 };
//
// tkmib - linux mib browser
//
// 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[] 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[] 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[] 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[] 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[] 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[] 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[] 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)
//
//
// RFC1213 local values
static char locDescr[]              = "Agentuino, a light-weight SNMP Agent.";  // read-only (static)
static char locObjectID[]           = "1.3.6.1.3.2009.0";                       // read-only (static)
static uint32_t locUpTime           = 0;                                        // read-only (static)
static char locContact[20]          = "Eric Gionet";                            // should be stored/read from EEPROM - read/write (not done for simplicity)
static char locName[20]             = "Agentuino";                              // should be stored/read from EEPROM - read/write (not done for simplicity)
static char locLocation[20]         = "Nova Scotia, CA";                        // should be stored/read from EEPROM - read/write (not done for simplicity)
static int32_t locServices          = 7;                                        // read-only (static)

uint32_t prevMillis = millis();
char oid[SNMP_MAX_OID_LEN];
SNMP_API_STAT_CODES api_status;
SNMP_ERR_CODES status;

void pduReceived()
{
  SNMP_PDU pdu;
  //
  #ifdef DEBUG
    Serial << F("UDP Packet Received Start..") << F(" RAM:") << freeMemory() << endl;
  #endif
  //
  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 << "OID: " << oid << endl;
    //
    if ( strcmp_P(oid, sysDescr ) == 0 ) {
      // handle sysDescr (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read-only
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = SNMP_ERR_READ_ONLY;
      } else {
        // response packet from get-request - locDescr
        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locDescr);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("sysDescr...") << locDescr << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else if ( strcmp_P(oid, sysUpTime ) == 0 ) {
      // handle sysName (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read-only
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = SNMP_ERR_READ_ONLY;
      } else {
        // response packet from get-request - locUpTime
        status = pdu.VALUE.encode(SNMP_SYNTAX_TIME_TICKS, locUpTime);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("sysUpTime...") << locUpTime << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else if ( strcmp_P(oid, sysName ) == 0 ) {
      // handle sysName (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read/write
        status = pdu.VALUE.decode(locName, strlen(locName)); 
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      } else {
        // response packet from get-request - locName
        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locName);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("sysName...") << locName << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else if ( strcmp_P(oid, sysContact ) == 0 ) {
      // handle sysContact (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read/write
        status = pdu.VALUE.decode(locContact, strlen(locContact)); 
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      } else {
        // response packet from get-request - locContact
        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locContact);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("sysContact...") << locContact << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else if ( strcmp_P(oid, sysLocation ) == 0 ) {
      // handle sysLocation (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read/write
        status = pdu.VALUE.decode(locLocation, strlen(locLocation)); 
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      } else {
        // response packet from get-request - locLocation
        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locLocation);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("sysLocation...") << locLocation << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else if ( strcmp_P(oid, sysServices) == 0 ) {
      // handle sysServices (set/get) requests
      if ( pdu.type == SNMP_PDU_SET ) {
        // response packet from set-request - object is read-only
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = SNMP_ERR_READ_ONLY;
      } else {
        // response packet from get-request - locServices
        status = pdu.VALUE.encode(SNMP_SYNTAX_INT, locServices);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      //
      #ifdef DEBUG
        Serial << F("locServices...") << locServices << F(" ") << pdu.VALUE.size << endl;
      #endif
    } else {
      // oid does not exist
      //
      // response packet - object not found
      pdu.type = SNMP_PDU_RESPONSE;
      pdu.error = SNMP_ERR_NO_SUCH_NAME;
    }
    //
    Agentuino.responsePdu(&pdu);
  }
  //
  Agentuino.freePdu(&pdu);
  //
  //Serial << "UDP Packet Received End.." << " RAM:" << freeMemory() << endl;
}

void setup()
{
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  //
  api_status = Agentuino.begin();
  //
  if ( api_status == SNMP_API_STAT_SUCCESS ) {
    //
    Agentuino.onPduReceive(pduReceived);
    //
    delay(10);
    //
    Serial << F("SNMP Agent Initalized...") << endl;
    //
    return;
  }
  //
  delay(10);
  //
  Serial << F("SNMP Agent Initalization Problem...") << status << endl;
}

void loop()
{
  // listen/handle for incoming SNMP requests
  Agentuino.listen();
  //
  // sysUpTime - The time (in hundredths of a second) since
  // the network management portion of the system was last
  // re-initialized.
  if ( millis() - prevMillis > 1000 ) {
    // increment previous milliseconds
    prevMillis += 1000;
    //
    // increment up-time counter
    locUpTime += 100;
  }
}

Lembrem-se: testar se a conectividade de rede está ok com ping.

Para testar o agentuino:

1) Baixar o snmpget ou o snmpwalk. No caso eu baixei o snmpget.
2) Testar com o comando snmpget:

snmpget -v 1 -r 1 -c public 192.168.2.64 sysName.0

    ou

snmpget -v 1 -r 1 -c public 192.168.2.64 1.3.6.1.2.1.1.5.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.2.1.1.5.0 ou sysName.0: OID que se quer consultar. 


O comando deverá retornar: Agentuino.


Bom é isso aí pessoal! Qualquer dúvida entrem em contato!

Até mais!

sábado, 16 de agosto de 2014

Complexidade dos projetos e adequação

Olá pessoal,

Hoje não vamos abordar um shield ou um componente mas vamos falar de assunto importante para os desenvolvedores da plataforma Arduino: a complexidade dos projetos e sua adequação.
Nestes posts até agora percebemos que à medida que vamos adicionando funcionalidades ao projeto a sua complexidade aumenta e é importante elencarmos os fatores que devemos estar atentos para que o projeto continue funcionando e haja a devida adequação de software (código) ao hardware e vice-versa.
Dentro da minha experiência na plataforma devemos estar atentos aos seguintes fatores:

1) Aumento do tamanho dos sketches

    A medida que o projeto aumenta no uso de shields, componentes e bibliotecas é necessário cada vez mais espaço para armanezar os sketches. Os sketches são armazenados na Flash Memory. Fiz uma relação abaixo das versões do Arduino que utilizei nos meus testes e projetos:

Arduino UNO:

.


MicrocontroladorATmega328
Tensão de operação5V
Tensão de entrada(recomendado)7-12V
Tensão de entrada (limites)6-20V
Pinos de I/O Digitais14 (dos quais 6 fornecem saída PWM)
Pinos de entrada analógicos6
Corrente DC por pino de I/O 40 mA
Corrente DC Pino 3.3V50 mA
Memória Flash 32 KB (ATmega328) dos quais 0.5 KB são usados pelo bootloader
SRAM2 KB (ATmega328)
EEPROM1 KB (ATmega328)
Velocidade do Clock16 MHz

Arduino Duemilanove:



MicrocontroladorATmega168 ou ATmega328
Tensão de operação5V
Tensão de entrada(recomendado)
7-12V
Tensão de entrada (limites)6-20V
Pinos de I/O Digitais14 (dos quais 6 fornecem saída PWM)
Pinos de entrada analógicos6
Corrente DC por pino I/O 40 mA
Corrente DC pino 3.3V50 mA
Memória Flash16 KB (ATmega168) ou 32 KB (ATmega328) dos quais 2 KB são usados pelo bootloader
SRAM1 KB (ATmega168) ou 2 KB (ATmega328)
EEPROM512 bytes (ATmega168) ou 1 KB (ATmega328)
Velocidade do Clock16 MHz

Arduino Leonardo:


MicrocontroladorATmega32u4
Tensão de operação5V
Tensão de entrada(recomendado)
7-12V
Tensão de entrada (limites)6-20V
Pinos de I/O digitais20
Canais PWM7
Canais de entrada analógicos12
Corrente DC por pino I/O40 mA
Corrente DC pino 3.3V50 mA
Memória Flash32 KB (ATmega32u4) dos quais 4 KB são usados pelo bootloader
SRAM2.5 KB (ATmega32u4)
EEPROM1 KB (ATmega32u4)
Velocidade do Clock 16 MHz

Arduino Mega:


MicrocontroladorATmega2560
Tensão de operação5V
Tensão de entrada(recomendado)
7-12V
Tensão de entrada (limites)6-20V
Pinos de I/O Digitais54 (dos quais 15 fornecem saída PWM)
Pinos de entrada analógicos16
Corrente DC por pino I/O 40 mA
Corrente DC pino 3.3V50 mA
Memória Flash256 KB dos quais 8 KB são usados pelo bootloader
SRAM8 KB
EEPROM4 KB
Velocidade do Clock16 MHz
É importante adequar os sketches para utilizar menos código quando você tem um Arduino com menor espaço de armazenamento. Siga algumas dessas dicas:
a) Verifique se há alguma biblioteca desnecessária ao projeto e se for comente-a.
b) Faça debug com a Serial somente nos trechos de código que é realmente necessário checar. Debug na serial consome espaço de código.
c) Se não há mais o que otimizar no código considere a possibilidade de adquirir um outro Arduino com mais espaço para os sketches (Arduino Mega por exemplo).

2) Aumento do uso de variáveis de memória

    O uso de variáveis de memória e estruturas consomem SRAM e dependendo do Arduino podem fazer com que haja reinicializações por conta de falta de memória SRAM . Tive uma experiência em projeto em que o Arduino simplesmente reinicializava se fosse utilizando uma variável float! Siga algumas dicas para usar menos memória:
a) Defina o tamanho das arrays char no tamanho exato.
b) Evitar usar variáveis float quando os valores a serem armazenados possam ser armazenados em variáveis int.
c) Se não há mais o que otimizar no uso de variáveis considere a possibilidade de adquirir um outro Arduino com mais SRAM (Arduino Mega por exemplo). Você pode adquirir também o Arduino Leonardo porém ele tem menos Flash Memory.


3) Aumento do uso de componentes e sensores

    Se o projeto demanda por mais pinos considere utilizar um Arduino com maior quantidade de pinos de entrada/saída. Deve-se ficar atento em relação à alimentação do Arduino quando utilizar muitos componentes nas portas digitais. Relata-se bastante problemas com relês que de alguma forma fazem o Arduino reiniciar.


4) Uso de várias bibliotecas e shields.

    O uso de várias bibliotecas podem aumentar o código além de aumentar a probabilidade de conflito de componentes ou shields por utilizarem os mesmos pinos. Os shields geralmente são empilháveis porém podem trazer algumas dificuldades para conexão.
Dica: ao trabalhar com display LCD prefira os que são I2C pois eles necessitam somente de dois pinos analógicos.

5) Módulos que necessitam de mais corrente.

   Shields GSM e servos podem necessitar de mais corrente que o fornecido pelo Arduino e necessitam de alimentação externa e podem influenciar no funcionamento destes. Fiquem atentos nestes detalhes!

Espero que essas dicas sejam bastante úteis! Se você tem experiências desse tipo relate-nos.

Até a próxima!!

quinta-feira, 14 de agosto de 2014

Shield GSM/GPS SIM908

Olá pessoal,

Hoje vamos falar do shield GSM/GPRS/GPS SIM908 que dá a funcionalidade de chamadas de voz, SMS, dados via Internet e fax e mais a funcionalidade de GPS (localização). Este shield será utilizado no projeto para envio de SMS quando houver o aumento de temperatura e isto contornará o problema de monitoramento pois há maior chance do aviso chegar a uma rede GSM (celular) pois muitas vezes não temos uma conexão ativa de rede (mesmo redes móveis como 3G).
Vamos lá então!

Shield GSM/GPS SIM908

Desconectei a antena do GPS por não utilizá-la nos testes. Posteriormente vamos testar essa funcionalidade também!! Utilizei um cartão micro SIM de uma operadora e coloquei alguns créditos (você pode utilizar um cartão SIM ou micro SIM - quando usar o micro SIM encaixe com cuidado os contatos do cartão).

Esquema de ligação: este shield deve ser empilhado por último se você tiver outros shields por conta dos componentes que ele utiliza (são altos e o próprio shield não disponibiliza as barras de pinos). Eu utilizei ele sem empilhar no Arduino, fazendo as ligações com jumpers e utilizando uma mini protoboard em que eu a cortei no meio. Utilizei um quadro de distribuição Tigre para 3 ou 4 disjuntores para acomodar os componentes. Abri a caixa, retirei os suportes internos para os disjuntores. Posteriormente vou utilizá-lo para um outro projeto.
 Vejam as fotos abaixo para ter uma idéia de como fixei o shield:


Quadro de distribuição para 3/4 disjuntores


Mini protoboard cortada ao meio e colada na parte interna do quadro com dupla face
Coloquei o shield de GSM forma que os pinos 2,3,5,6 ficassem alinhadas com os furos.

Conectei os jumpers na protoboard nos pontos correspondentes aos pinos.
Em outra lado do quadro fixei o Arduino e liguei os jumpers nos pinos 2, 3,5,6 do Arduino. Conectei jumpers nos pinos 3.3V, 5V, GND e Vin.
Conectei os pinos 3.3V, 5V, GND e Vin na protoboard ligada ao shield GSM.



Importante: conectar o Arduino com fonte externa. Para acionar o shield ele necessita de mais corrente que o Arduino fornece.

A biblioteca correspodente deste shield é esta:

https://github.com/MarcoMartines/GSM-GPRS-GPS-Shield
 
Vamos ao código:

#include "SIM900.h"
#include <SoftwareSerial.h>


// Se você quiser usar as funções para gerenciar SMS, descomente as linhas abaixo
#include "sms.h"
#include "call.h"

SMSGSM sms;

// Para mudar os pinos TX e RX modifique na biblioteca GSM.cpp (o padrão é pino 2 para TX e pino 3 para RX


//GSM Shield for Arduino
//www.open-electronics.org
//this code is based on the example of Arduino Labs.

//Este sketch envia um simples SMS. Há trechos comentados para efetuar chamadas de voz e ler SMS. Descomente-os se quiser testar essas funcionalidades.

int numdata;
boolean started=false;
char smsbuffer[160];
char n[20];
int powerkey =  5;
int statuspin = 6;
int pinState = 0;

CallGSM call;


void setup()
{
  pinMode(powerkey, OUTPUT); //define pino 5 como saída
  pinMode(statuspin, INPUT); //define pino 6 como entrada
 
  pinState = digitalRead(statuspin); //Lê o estado do pino 6




  
  // Se o pino 6 estiver LOW, faz a sequencia de power on do Shield
  if(pinState==LOW){
    digitalWrite(powerkey, HIGH);   // seta o pino 5 como HIGH
    delay(2000); // espera 2 segundos
    digitalWrite(powerkey, LOW);    // seta o pino 5 como LOW
  }


  //Inicia monitoramento da serial.
  Serial.begin(9600);


  Serial.println("Testando GSM Shield.");
  //Inicia a configuração do shield GSM com a velocidade de 2400
  

  if (gsm.begin(2400)){
    Serial.println("\nstatus=READY");
    started=true; 
  }
  else Serial.println("\nstatus=IDLE");
  

  // Se o shield iniciou envia o SMS com a função SendSMS passando o número e a mensagem
  

  if(started){
     if (sms.SendSMS("015XXXXXXXXX", "Teste de SMS"))
      Serial.println("\nSMS sent OK");
    

     Se quiser testar chamadas de voz descomente as linhas abaixo
    /*
    if(call.CallStatus()!= CALL_ACTIVE_VOICE){

           Serial.println("Chamando");

           call.Call(015XXXXXXXXX);
    */
 
  }

}

void loop()
{

  // Se quiser testar a leitura de SMS descomente as linhas abaixo
  /*

  if(started){
    //Read if there are messages on SIM card and print them.
    if(gsm.readSMS(smsbuffer, 160, n, 20))
    {
      Serial.println(n);
      Serial.println(smsbuffer);
    }
    delay(1000);
  }

  */
};





Pessoal, é isso aí. Qualquer dúvida enviem seus comentários.

Até a próxima! 

sábado, 2 de agosto de 2014

Shield Ethernet Wiznet W5100 - Envio de e-mail

Olá pessoal,

Hoje vamos ver como utilizar o shield Ethernet para o envio de e-mail. No projeto de acionamento automático de ar condicionado com infravermelho seria interessante que no momento em que houvesse o aumento de temperatura acima do programado o Arduino pudesse enviar algum tipo de aviso por e-mail pois o aviso sonoro e na tela do display só seriam percebidos se alguém estivesse no ambiente em que ele está.
Há algumas considerações:
1) É necessário que a infraestrutura de rede esteja funcionando no momento em que o Arduino for enviar o e-mail. Como o Arduino e os equipamentos estão ligados em um nobreak se houver queda de energia e o ar condicionado desligar o aviso por e-mail vai funcionar.
2) É necessária uma conexão de rede ativa por parte de quem está monitorando. Seja por celular, notebook ou PC se o e-mail for enviado alguém deve estar conectado para poder receber o aviso por e-mail. Se o e-mail for enviado e ninguém lê-lo é a mesma coisa não estar monitorando! Aí talvez implique que você deva ter uma conexão móvel (3G ou qualquer coisa parecida) e manter essa conexão ativa o tempo todo.
Mesmo com essa limitação por parte de monitoramento ainda assim acredito que o envio de e-mail é interessante por registrar a falha e com isso também compormos estatísticas de falhas e tomarmos providências sejam internas ou por parte da concessionária de energia para melhorar a disponibilidade de energia.
Vamos lá então:

Esquema de ligação: o mesmo do projeto de acionamento acrescentado somente o shield Ethernet.

Código:


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

//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

IPAddress ip(192,168,1,177);

// Endereço do seu servidor de e-mail
char server[] = "seuservidordeemail";

EthernetClient client;

boolean enviouEmailDOWN;
boolean enviouEmailUP;
boolean enviouSMSDOWN;
boolean enviouSMSUP;

// Objeto irsend para emissor infrared 
IRsend irsend;

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

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



float sinVal;
int toneVal;

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


// função que envia e-mail quando desligar o ar condicionado byte sendEmailDOWN()
{
  byte thisByte = 0;
  byte respCode;

  // testa se há conexão com o servidor de e-mail na porta 25   if(client.connect(server,25)) {
    Serial.print(F("connected"));
  } else {
    Serial.print(F("connection failed"));
    return 0;
  }


  if(!eRcv()) return 0;
  Serial.print(F("Sending helo")); 

// Mude para o ip do Arduino
  client.println(F("helo <seu ip>")); //envia o helo para o servidor de e-mail

  if(!eRcv()) return 0;
  Serial.print(F("Sending From"));

// Coloque um e-mail váido do seu servidor de e-mail
  client.println(F("MAIL From: <arduino1@sorocaba.unesp.br>"));

  if(!eRcv()) return 0;

// Coloque o e-mail do destinatário
  Serial.print(F("Sending To"));
  client.println(F("RCPT To: <destinatario@seudominiodeemail.com>"));

  if(!eRcv()) return 0;

// Envie os dados do e-mail   Serial.print(F("Sending DATA"));
  client.println(F("DATA"));

  if(!eRcv()) return 0;

  Serial.print(F("Sending email"));

// Mude para o e-mail do destinatário
  client.println(F("To: Fernando <destinatario@seudominiodeemail.com>"));

// change to your address
  client.println(F("From: Arduino <arduino1@sorocaba.unesp.br>"));

  client.println(F("Subject: Temperatura alta\r\n"));

  client.println(F("Temperatura alta no datacenter"));

  client.println(F("."));

  if(!eRcv()) return 0;

  Serial.print(F("Sending QUIT"));

  //Saindo da conexão com a porta 25 do servidor de e-mail
  client.println(F("QUIT"));

  if(!eRcv()) return 0;

  client.stop();

  Serial.print(F("disconnected"));

  enviouEmailDOWN = true; //se enviou e-mail de desligado


  return 1;
  
  
}

byte sendEmailUP()
{
  byte thisByte = 0;
  byte respCode;

  if(client.connect(server,25)) {
    Serial.print(F("connected"));
  } else {
    Serial.print(F("connection failed"));
    return 0;
  }
  if(!eRcv()) return 0;
  Serial.print(F("Sending helo"));

// change to your public ip
  client.println(F("helo <seu ip"));

  if(!eRcv()) return 0;
  Serial.print(F("Sending From"));

// change to your email address (sender)
  client.println(F("MAIL From: <arduino1@sorocaba.unesp.br>"));

  if(!eRcv()) return 0;

// 
  Serial.print(F("Sending To"));
  client.println(F("RCPT To: <fernando@sorocaba.unesp.br>"));

  if(!eRcv()) return 0;

  Serial.print(F("Sending DATA"));
  client.println(F("DATA"));

  if(!eRcv()) return 0;

  Serial.print(F("Sending email"));

// Mude para o e-mail de destino
  client.println(F("To: Fernando <fernando@sorocaba.unesp.br>"));

// Mude para o e-mail do remetente
  client.println(F("From: Arduino <arduino1@sorocaba.unesp.br>"));

  client.println(F("Subject: Temperatura normalizada\r\n"));

  client.println(F("Temperatura normalizada na SALA DO STI"));

  client.println(F("."));

  if(!eRcv()) return 0;

  Serial.print(F("Sending QUIT"));
  client.println(F("QUIT"));

  if(!eRcv()) return 0;

  client.stop();

  Serial.print(F("disconnected"));

  enviouEmailUP = true;

  return 1;
}

byte eRcv()
{
  byte respCode;
  byte thisByte;
  int loopCount = 0;

  while(!client.available()) {
    delay(1);
    loopCount++;

    // Se não receber e-mail por 10 segundos
    if(loopCount > 10000) {
      client.stop();
      Serial.print(F("\r\nTimeout"));
      return 0;
    }
  }

  respCode = client.peek();

  while(client.available())
  {  
    thisByte = client.read();    
    Serial.write(thisByte);
  }

  if(respCode >= '4')
  {
    efail();
    return 0;  
  }

  return 1;
}


void efail()
{
  byte thisByte = 0;
  int loopCount = 0;

  client.print(F("QUIT"));

  while(!client.available()) {
    delay(1);
    loopCount++;

    // if nothing received for 10 seconds, timeout
    if(loopCount > 10000) {
      client.stop();
      Serial.print(F("\r\nTimeout"));
      return;
    }
  }

  while(client.available())
  {  
    thisByte = client.read();    
    Serial.write(thisByte);
  }

  client.stop();

  Serial.print(F("disconnected"));
}

void setup() {
  Serial.begin(9600);  // Inicia conexao serial para monitoramento
  while (!Serial) {
      ; // wait for serial port to connect. Needed for Leonardo only
    }
  Ethernet.begin(mac);
  delay(1000);
  
  Serial.println("Ethernet iniciada");
  
  lcd.begin(16,2); // inicializa o lcd 
  lcd.setBacklight(1);
  Serial.println("LCD iniciada");
  delay(3000);
  pinMode(8,OUTPUT);  //pino 8 para saida do alto falante

  #ifdef AVR
    Wire.begin();
  #else
    Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
  #endif
  
  
  rtc.begin();  //inicia o objeto rtc

  //Verifica se o RTC esta funcionando
  if (! rtc.isrunning()) {
    Serial.print("RTC is NOT running!");
  }
  // Ajusta o rtc para a data e hora em que o sketch foi compilado
  
//  rtc.adjust(DateTime(__DATE__, __TIME__));

  Serial.println("RTC iniciada");
  delay(3000);

  enviouEmailDOWN = true;
  enviouEmailUP = true;
}


void loop() {
    
    
    DateTime now = rtc.now(); // carrega a data e hora do RTC
    
    Serial.print("Lendo sensor: ");
    int chk = sensor.read(2);  //le a temperatura
  
    
    //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");
    }
    
    // Monitora a umidade e temperatura para a conexao serial
    Serial.print("Umidade (%): ");
    Serial.println(sensor.humidity, 2);
    Serial.print("Temperatura (graus Celsius): ");
    Serial.println(sensor.temperature, 2);
   
    // Carrega os valores de temperatura e umidade do sensor nas variaveis correspondentes
    int temp = sensor.temperature;  
    int humi = sensor.humidity;

    //Se a temperatura for maior que 26 graus Celsius
    if (temp > 26) {
          // envia o codigo IR para ligar o AC     
          // Aciona o ar condicionado novamente 
          irsend.sendRaw(powerOn,92,38);
      
          // Exibe o valor da temperatura lida pelo sensor
          lcd.clear();  //limpa o display do LCD.    
          lcd.home(); // Posiciona o cursos na coluna 0, linha 0
          lcd.print("Temp: ");  //imprime a string Temp: no display do LCD.                 
          lcd.print(temp); // imprime a temperatura no LCD
          lcd.write(B11011111); //Simbolo de graus celsius
          lcd.print("C"); // Imprime C
          lcd.setCursor(0,1);  //posiciona o cursor na coluna 0 linha 1 do LCD.
          lcd.print("TEMP. ALTA!"); //Imprime o aviso de temperatura alta no display do LCD.
          
          //Aciona o alarme sonoro. 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);
          }

          //Seta a variável enviouEmailUP como false
          enviouEmailUP = false;

          // Se não enviou e-mail envia com a função sendEmailDOWN. Caso já enviou não envia novamente 
          if (!enviouEmailDOWN) {
              sendEmailDOWN(); //envia e-mail de temperatura alta
          }
       
        
        
         //Aguarda 5 minutos para enviar novamente o codigo IR caso a temperatura nao baixe
         delay(300000);
         
    }
    else
    { 
          
          // Temperatura normal  
          lcd.clear();  //limpa o display do LCD.     
          lcd.home(); // Posiciona o cursos na coluna 0, linha 0
          lcd.print("Temp: ");  //imprime a string no display do LCD.                 
        
          lcd.print(temp); // Imprime o valor da temperatura
          lcd.write(B11011111); //Simbolo de graus celsius
          lcd.print("C");
          //Exibindo valor da leitura da umidade no display LCD.
          lcd.setCursor(0,1);  //posiciona o cursor na coluna 0 linha 1 do LCD.
          lcd.print("Umidade: ");  //imprime a string Umidade: no display do LCD.       
          lcd.print(humi); // Imprime o valor da umidade
          lcd.print("%");
          
          delay(5000);
          
          //Exibe a data e hora no LCD
          
          lcd.clear(); //limpa o display LCD
          lcd.home(); // Posiciona o cursos na coluna 0, linha 0          lcd.print("DATA: ");
          lcd.print(now.day(), DEC); //imprime o dia 
          lcd.print('/');
          lcd.print(now.month(), DEC); // imprime o mês
          lcd.print('/');
          lcd.print(now.year(), DEC); //imprime o ano
          lcd.setCursor(0,1);
          lcd.print("HORA: "); 
          lcd.print(now.hour(), DEC); //imprime a hora
          lcd.print(':');
          lcd.print(now.minute(), DEC); //imprime o minuto
          lcd.print(':');
          lcd.print(now.second(), DEC); //imprime o segundo
          
          delay(2000);
          noTone(8); //silencia o alarme sonoro
          
          //seta a variável enviouEmailDOWN como false
          enviouEmailDOWN = false;

          // Se não enviou e-mail envia com a função sendEmailUP. Caso já enviou não envia novamente 
          if (!enviouEmailUP) {
              sendEmailUP(); //envia o e-mail de temperatura normalizada
          }
    }
  
    delay(2000);
 }

Com isso o projeto de acionamento de ar condicionado com infravermelho conta com a funcionalidade de envio de e-mail quando há o aumento de temperatura e também quando a temperatura volta ao normal.
Como vocês perceberam o sketch está aumentando em tamanho a medida que vamos acrescentando mais componentes e funcionalidades. Este é o assunto para o próximo post.

Até mais!