O projeto sofreu varias modificações, num total de 8 versões do script para chegar em uma versão beta, esta em fase de testes, estou deixando rodar 24/7, quero que fique a prova de falhas, que rode meses sem parar mesmo que algum erro ocorra, para isso tive que ir modificando com calma e testando.
Eu tentei adicionar suporte a captura de logs pelo cartão SD, essa placa de ethernet possui entrada para cartão, mas infelizmente ele compartilha o mesmo canal de comunicação com o microcontrolador(SPI), então para utilizar o ethernet em conjunto com o cartão SD, eu teria que desligar e ligar via software cada um antes de utilizar, por exemplo para gravar uma informação no cartão SD, teria que desligar a comunicação ethernet, e ligar a do cartão, logo depois de gravar os dados, desligar o cartão SD e ligar a ethernet, e assim suscetivamente. Isso causa muitas falhas por problemas de delay e perda de informação. A melhor solução que achei foi utilizar o software RealTerm, que comunica com a porta serial e salva os logs em um arquivo txt no computador, e uma coisa boa desses logs é que vem com data e horário atualizados pelo computador, usando o cartão SD teríamos que utilizar outro modulo de relógio em tempo real RTC.
Alem de fazer os logs foi implementando em conjunto com o client de leitura de twitter, um client servidor de webpage http, esse cliente mostrava as informações atuais em relação ao funcionamento do circuito, mas infelizmente comprometia boa parte da memoria do microcontrolador, alem de aumentar consideravelmente o numero de falhas do programa.
Uma outra ideia implementada foi a seguinte, anteriormente o IP era fixo, você gravava ele no código, e ele funcionaria com aquele IP para sempre, o problema é que entrando em outras redes a configuração de distribuição de IP pode ser diferente, assim o programa não consegue pegar um IP valido e comunicar com a rede. A solução foi a seguinte, o script agora tem cadastrados IP, gatway,subnet e MAC, ao iniciar o código ele utiliza apenas o código MAC e tenta pegar o resto das informações via DHCP, assim quem fornece o IP é o modem/roteador/switch que controla a rede, com posse desse IP vem uma coisa bem legal, que demorou uma semana para implementar mas que valeu a pena. Sempre ao ligar o circuito ele pega um IP e envia pelo twitter, isso mesmo alem de ler o twitter toda vez ao iniciar ele envia o IP atual que ele esta utilizando, isso se torna muito útil, caso no futuro com tempo implemente o servidor webhttp para rodar no Arduino ou para qualquer aplicação remota.
Com essas modificações feitas, passamos para faze de testes, com vários códigos de debug.
No final cheguei a uma versão que esta rodando a 2 dias e estou acompanhando, fazendo diferentes tipos de conexão via ponte de rede/roteador.
Esse é o código final vou entrar um pouco em detalhes:
/*
Twitter Client
Esse sketch conecta ao Twitter usando um Ethernet shield. Ele vasculha o XML
retornado, e procura por <text>esse é um tweet</text>
O Twitter a ser lido não pode ter nenhum dado anterior postado(pode ser preferencial
mente deixado status como ultimo post), lembrando que sempre ao executar um comando,
deletar logo apos. Se o circuito for ligado e ter dado antigo ele vai executar, mas
se ja estiver ligado e possuir dado que não foi apagado ele não vai executar nenhuma
ação.
Ideal que o circuito fique conectado direto a um switch ou modem, em conexão de ponde
de rede pode ter perdas de sinal
**ATENÇÃO**
O pedido de leitura de tweets NÃO pode exceder 150 por hora
para chegar: http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=remopte
<error>
Rate limit exceeded. Clients may not make more than 150 requests per hour.
</error>
Circuito:
* Ethernet shield ligado nos pinos 10, 11, 12, 13
por Vinicius R.
VRO Projetos
viniciusro@gmail.com
*/
#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>
Twitter twitter("770703602-72ou4uHkg6Qefg622WdQKAXkN7s35f5QCLJrqYTO");
byte mac[] ={0xDE,0xAD,0xBF,0xEF,0xFE,0xED};
byte ip[] ={192,168,0,15};
byte gateway[] ={192,168,0,1};
byte subnet[] ={255,255,255,0};
EthernetClient client;
const unsigned long tempoRefrescamento = 60000;
char nomeServidor[] = "api.twitter.com";
boolean pedidos;
unsigned long tempoUltimaTentativa = 0;
String linhaAtual = "";
String tweet = "";
String tweetAnterior = "";
boolean lendoTweet = false;
const int reiniciar_XXX = 8;
const int reiniciar_YYY = 7;
union IPAddressConverter
{
uint32_t ipInteger;
uint8_t ipArray[4];
};
void setup()
{
pinMode(reiniciar_XXX, OUTPUT);
pinMode(reiniciar_YYY, OUTPUT);
pinMode(10, OUTPUT);
pinMode(4, OUTPUT);
digitalWrite(4,LOW);
linhaAtual.reserve(256);
tweet.reserve(150);
Serial.begin(9600);
Serial.println("Tentando pegar um IP usando DHCP:");
if (!Ethernet.begin(mac))
{
Ethernet.begin(mac,ip,gateway,subnet);
}
Serial.print("Meu IP:");
Serial.println(Ethernet.localIP());
enviaIP();
conectandoServidor();
}
void loop()
{
if (client.connected())
{
if (client.available())
{
char inChar = client.read();
linhaAtual += inChar;
if (inChar == '\n')
{
linhaAtual = "";
}
if (linhaAtual.endsWith("<text>"))
{
lendoTweet = true;
tweet = "";
}
if (lendoTweet)
{
if (inChar != '<')
{
tweet += inChar;
}
else
{
lendoTweet = false;
Serial.println(tweet);
Serial.print("Tweet anterior: ");
Serial.println(tweetAnterior);
while(tweet==tweetAnterior)
{
break;
}
if(tweet==">reiniciar .XXX" && tweetAnterior!=tweet)
{
Serial.println("Reiniciando computador .XXX");
digitalWrite(reiniciar_XXX, LOW);
delay(1000);
digitalWrite(reiniciar_XXX, HIGH);
delay(1000);
digitalWrite(reiniciar_XXX, LOW);
}
if(tweet==">reiniciar .YYY" && tweetAnterior!=tweet)
{
Serial.println("Reiniciando computador .YYY");
digitalWrite(reiniciar_YYY, LOW);
delay(1000);
digitalWrite(reiniciar_YYY, HIGH);
delay(1000);
digitalWrite(reiniciar_YYY, LOW);
}
Serial.println("");
tweetAnterior=tweet;
client.stop();
client.flush();
}
}
}
}
else if (millis() - tempoUltimaTentativa > tempoRefrescamento)
{
Serial.print("Tempo de funcionamento total: ");
Serial.print((millis()/1000)/60);
Serial.println(" minutos");
Serial.print("Ultima tentativa de conectar: ");
Serial.print((tempoUltimaTentativa/1000)/60);
Serial.println(" minutos");
conectandoServidor();
}
}
void conectandoServidor()
{
Serial.println("conectando no servidor...");
if (client.connect(nomeServidor, 80))
{
Serial.println("fazendo pedido de HTTP...");
client.println("GET /1/statuses/user_timeline.xml?screen_name=remopte&count=1 HTTP/1.1");
client.println("HOST: api.twitter.com");
client.println();
delay(500);
}
else
{
Serial.println("falhou ao conectar");
client.stop();
delay(500);
client.flush();
}
Serial.println("");
tempoUltimaTentativa = millis();
}
void enviaIP()
{
Serial.println("");
Serial.println("Enviando IP atual no tweeter");
IPAddressConverter ipAddress;
ipAddress.ipInteger = Ethernet.localIP();
char buf[16];
sprintf(buf, "%d.%d.%d.%d", ipAddress.ipArray[0], ipAddress.ipArray[1], ipAddress.ipArray[2], ipAddress.ipArray[3]);
twitter.post(buf);
delay(5000);
client.stop();
client.flush();
}
A primeira coisa é conseguir um IP valido, para isso usamos if (!Ethernet.begin(mac)) , caso não consigo um IP por DHCP, ele vai utilizar o cadastrado no código.
A função union IPAddressConverter é para converter o formato de IP que vem em uint32_t pela função Ethernet.localIP() em um array que em seguida é convertido para char, assim é postado no twitter utilizando a função enviaIP().
Depois que o IP é enviado o script passa a fazer leitura da rss feed do twitter em XML, vai sempre ler o ultimo IP e ver se existem alguma ação cadastrada para executar, caso não exista ele fica rodando 24/7.
Esse é o funcionamento básico, adquire IP,envia no twitter, faz leitura do twitter com tempo de refrescamento de 60 segundos e executa ação caso for cadastrada e o twitter anterior seja diferente do novo, para proteger contra eventuais erros.
Assim que aparece no terminal serial:
Tempo de funcionamento total: 948 minutos
Ultima tentativa de conectar: 947 minutos
conectando no servidor...
fazendo pedido de HTTP...
>Tweet Remote Control
Tweet anterior: >Tweet Remote Control
Vale lembrar que para enviar o IP no twitter temos que usar a biblioteca twitter.h e cadastrar um token para o aplicativo ter acesso a sua conta de twitter.
Vou deixar em anexo também o código que quero trabalhar mais com tempo, ele possui implantado o servidor web para visualizar as informações em tempo real, mas ainda possui falhas.
/*
Twitter Client
Esse sketch conecta ao Twitter usando um Ethernet shield. Ele vasculha o XML
retornado, e procura por <text>esse é um tweet</text>
O Twitter a ser lido não pode ter nenhum dado anterior postado(pode ser preferencial
mente deixado status como ultimo post), lembrando que sempre ao executar um comando,
deletar logo apos. Se o circuito for ligado e ter dado antigo ele vai executar, mas
se ja estiver ligado e possuir dado que não foi apagado ele não vai executar nenhuma
ação.
Ideal que o circuito fique conectado direto a um switch ou modem, em conexão de ponde
de rede pode ter perdas de sinal
**ATENÇÃO**
O pedido de leitura de tweets NÃO pode exceder 150 por hora
para chegar: http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=remopte
<error>
Rate limit exceeded. Clients may not make more than 150 requests per hour.
</error>
Circuito:
* Ethernet shield ligado nos pinos 10, 11, 12, 13
por Vinicius R.
VRO Projetos
viniciusro@gmail.com
Esse codigo é de dominio publico.
*/
#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>
Twitter twitter("770703602-72ou4uHkg6Qefg622WdQKAXkN7s35f5QCLJrqYTO");
byte mac[] ={0xDE,0xAD,0xBF,0xEF,0xFE,0xED};
byte ip[] ={192,168,0,15};
byte gateway[] ={192,168,0,1};
byte subnet[] ={255,255,255,0};
EthernetServer server(80);
EthernetClient client;
const unsigned long tempoRefrescamento = 60000;
char nomeServidor[] = "api.twitter.com";
boolean pedidos;
unsigned long tempoUltimaTentativa = 0;
String linhaAtual = "";
String tweet = "";
String tweetAnterior = "";
boolean lendoTweet = false;
const int reiniciar_XXX = 8;
const int reiniciar_YYY = 7;
union IPAddressConverter
{
uint32_t ipInteger;
uint8_t ipArray[4];
};
void setup()
{
pinMode(reiniciar_XXX, OUTPUT);
pinMode(reiniciar_YYY, OUTPUT);
pinMode(10, OUTPUT);
pinMode(4, OUTPUT);
digitalWrite(4,LOW);
linhaAtual.reserve(256);
tweet.reserve(150);
Serial.begin(9600);
Serial.println("Tentando pegar um IP usando DHCP:");
if (!Ethernet.begin(mac))
{
Ethernet.begin(mac,ip,gateway,subnet);
}
Serial.print("Meu IP:");
Serial.println(Ethernet.localIP());
enviaIP();
server.begin();
conectandoServidor();
}
void loop()
{
paginaStatus();
if (client.connected())
{
if (client.available())
{
char inChar = client.read();
linhaAtual += inChar;
if (inChar == '\n')
{
linhaAtual = "";
}
if (linhaAtual.endsWith("<text>"))
{
lendoTweet = true;
tweet = "";
}
if (lendoTweet)
{
if (inChar != '<')
{
tweet += inChar;
}
else
{
lendoTweet = false;
Serial.println(tweet);
Serial.print("Tweet anterior: ");
Serial.println(tweetAnterior);
while(tweet==tweetAnterior)
{
break;
}
if(tweet==">reiniciar .XXX" && tweetAnterior!=tweet)
{
Serial.println("Reiniciando computador .XXX");
digitalWrite(reiniciar_XXX, LOW);
delay(1000);
digitalWrite(reiniciar_XXX, HIGH);
delay(1000);
digitalWrite(reiniciar_XXX, LOW);
}
if(tweet==">reiniciar .YYY" && tweetAnterior!=tweet)
{
Serial.println("Reiniciando computador .YYY");
digitalWrite(reiniciar_YYY, LOW);
delay(1000);
digitalWrite(reiniciar_YYY, HIGH);
delay(1000);
digitalWrite(reiniciar_YYY, LOW);
}
Serial.println("");
tweetAnterior=tweet;
client.stop();
client.flush();
}
}
}
}
else if (millis() - tempoUltimaTentativa > tempoRefrescamento)
{
Serial.print("Tempo de funcionamento total: ");
Serial.print((millis()/1000)/60);
Serial.println(" minutos");
Serial.print("Ultima tentativa de conectar: ");
Serial.print((tempoUltimaTentativa/1000)/60);
Serial.println(" minutos");
conectandoServidor();
}
}
void conectandoServidor()
{
Serial.println("conectando no servidor...");
if (client.connect(nomeServidor, 80))
{
Serial.println("fazendo pedido de HTTP...");
client.println("GET /1/statuses/user_timeline.xml?screen_name=remopte&count=1 HTTP/1.1");
client.println("HOST: api.twitter.com");
client.println();
delay(500);
}
else
{
Serial.println("falhou ao conectar");
client.stop();
delay(500);
client.flush();
}
Serial.println("");
tempoUltimaTentativa = millis();
}
void paginaStatus()
{
delay(30);
{
EthernetClient client = server.available();
if (client)
{
boolean linhaAtualEmBranco = true;
while (client.connected())
{
if (client.available())
{
char c = client.read();
if (c == '\n' && linhaAtualEmBranco)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.println("<HTML>");
client.println("<HEAD>");
client.println("<TITLE>RemOpt - Remote</TITLE>");
client.println("</HEAD>");
client.println("<BODY>");
client.println("<center>");
client.println("<H1>RemOpt - Controle Remoto </H1>");
client.println("</center>");
client.println("<H4>Configuracao Atual:</H4>");
client.println("Tempo de funcionamento:");
long tempo=((millis()/1000)/60);
client.println(tempo);
client.println(" minuto(s)");
client.println("<br>");
client.println("Ultima tentativa de conectar: ");
client.println((tempoUltimaTentativa/1000)/59);
client.println(" minuto(s)");
client.println("<br>");
client.println("Meu IP: ");
client.print(Ethernet.localIP());
client.println("<br><br>");
client.println("Ultimo Tweet: ");
client.println(tweet);
client.println("<br><br>");
client.println("<meta http-equiv=\"refresh\" content=\"30\">");
client.println("</BODY>");
client.println("</HTML>");
break;
}
if (c == '\n')
{
linhaAtualEmBranco = true;
}
else if (c != '\r')
{
linhaAtualEmBranco = false;
}
}
}
delay(5);
client.stop();
}
}
}
void enviaIP()
{
Serial.println("");
Serial.println("Enviando IP atual no tweeter");
IPAddressConverter ipAddress;
ipAddress.ipInteger = Ethernet.localIP();
char buf[16];
sprintf(buf, "%d.%d.%d.%d", ipAddress.ipArray[0], ipAddress.ipArray[1], ipAddress.ipArray[2], ipAddress.ipArray[3]);
twitter.post(buf);
delay(5000);
Nenhum comentário:
Postar um comentário