Temperatura+Arduino+Mysql=RDDTool

Dec 10
2010

Epa…tanta coisa num so titulo… a realidade é que é mesmo tudo isso que tenho andado a montar nos últimos dias. Isto porque temporariamente tive que, devido a falta de peças, abandonar o projecto Good Morning Dave, e focar-me mais no projecto “Save a Plant…by Twitter”.

Para aqueles que me seguem no twitter, ja perceberem que o arduino ja esta a mandar tweets com alguns bitaques, nomeadamente, as horas e a temperatura da sala, por isso ja posso dizer que a primeira parte do projecto “Save a Plant…by Twitter” estava feita, ou seja a parte do Twitter.

Por isso a segunda parte da ideia era de colocar os dados dos varios sensores numa base de dados para poder com esses dados gerar uns graficos todos bonitos, e poder fazer umas estatisticas caso seja necessario. Nao sou fã de estatisticas, mas ei, porque nao prevenir ja.

Mais uma vez, tudo parecia facil, era so montar um Webclient no Arduino e invocar um php ou um cgi que fazia a parte da inserção dos dados na BD(mysql)…mais uma vez erradoooo….

Eu em casa, por motivos de poupança de electricidade não tenho nada de servidores ligados, e isto porque um dia recebi um acerto da EDP de 370€ de luz e achei que estava na altura de NÃO ter servidores em casa, por isso surgia aqui o primeiro problema, que era onde é que vou por uma BD de mysql  e correr os scripts. Bem, a hipótese mais simples era usar este mesmo site, uma vez que tem tudo o que preciso, posso correr scripts, tenho BD!

Surgia aqui o meu segundo problema…DNS, por isso rapidamente abandonei a ideia de usar este estaminé. Num outro post falarei de porque abandonei o DNS.

Bem..tem que ser, vamos instalar um linux ca por casa com Mysql+Php+Perl+Apache+RDDTool+alltherest.

Esta foi a parte facil, demorou cerca de 1 hora e pumba tinha o servidor pronto, agora era so começar a inserir os dados na BD.

Para quem nunca usou o arduino para inserir dados extraidos de um sensor numa base de dados, o melhor é começar por fazer um webclient usando um dos exemplos e depois esquecer tudo aquilo que viu..wait!!?! say what!?!? pois isso mesmo, é que um dos maiores problemas que tenho tido com os exemplos é que sao quase sempre apontados para coisas com a Serial Port e eu quero mesmo é coisas que nao usem a Serial Port mas sim variaveis and stuff..

Depois de muito pesquisar e muito bater com a cabeça eis o codigo que usei para retirar os dados do sensor de temperatura que ja falei anteriormente e inserir na BD.

#include 
#include 
#include 
#include 
 
byte mac[] = { 0xDE, 0xDE, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 177 };
byte server[] = { 192, 168, 2, 70 }; // DB
 
Client client(server, 80);
 
// Time Settings
byte SNTP_server_IP[]    = { 81, 92, 212, 46 }; // 3.pt.pool.ntp.org
time_t prevDisplay = 0; // when the digital clock was displayed
const  long timeZoneOffset = 0L; // set this to the offset in seconds to your local time;
 
// Temperature
int analoginput=0;
int value=0;
int R1=1000;
int Vin=5;
float tmp=0.0;
float Vout=0.0;
float f=0.0;
int lastTempReport = 0;
int currentTempReport = 0;
 
// Clock
int hora, minutos, ano, mes, dia;
 
int ID=69; //sensor ID
 
void setup()
{
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
 
   //Clock Begin
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  while(timeStatus()== timeNotSet)
    ; // wait until the time is set by the sync provider
 
}
 
void loop()
{
      addDB();
}
 
void addDB() {
 temperatura();
  int currentHumReport=10;
  Serial.print("CurrentHumReport");
  Serial.println(currentHumReport);
  delay(1000);
   char date[144] ="";
   char horas[144] ="";
   ano=(year());
   mes=(month());
   dia=(day());
   hora=(hour());
   minutos=(minute());
 
   sprintf(date, "%.2d-%.2d-%.2d", ano, mes, dia);
   sprintf(horas, "%.2d:%.2d", hora, minutos);
  if (client.connect()) {
    Serial.println("connected");
    delay(1000);
    client.print("GET /cgi-bin/sensor/inserir.cgi?date=");
        Serial.print("GET /cgi-bin/sensor/inserir.cgi?date=");
    client.print(date);
        Serial.print(date);
    client.print("&time=");
        Serial.print("&time=");
    client.print(horas);
        Serial.print(horas);
    client.print("&id=");
        Serial.print("&id=");
    client.print(ID);
        Serial.print(ID);
    client.print("&temperatura=");
        Serial.print("&temperatura=");
    client.print(currentTempReport);
        Serial.print(currentTempReport);
    client.print("&humidade=");
        Serial.print("&humidade=");
    client.print(currentHumReport);
        Serial.print(currentHumReport);
    client.println(" HTTP/1.1");
        Serial.println(" HTTP/1.1");
    client.println("Host: arduino.rechena.com");
        Serial.println("Host: arduino.rechena.com");
    client.println("User-Agent: Arduino");
        Serial.println("User-Agent: Arduino");
    client.println("Accept: text/html");
        Serial.println("Accept: text/html");
    client.println("Connection: close");
        Serial.println("Connection: close");
    client.println();
        Serial.println();
  }
  else {
    while (client.connected() && client.available()) {
      char c = client.read();
      Serial.print(c);
    }
  client.stop();
  delay(5000);
}
}
 
void temperatura () {
 
  Vout=1023-analogRead(0); // Invert the number because of setup
  Vout=(Vout/1024.0)*5;
  f=1000*exp(-20000/298.15); // 298.15K = 25C, 20000 seems to be the B value for a standard 1kOhm NTC
  tmp=(R1*Vin)/Vout-R1;
  value=(20000/log(tmp/f))-273.15;
   currentTempReport = value+7;
}
 
/*-------- NTP code ----------*/
 
unsigned long getNtpTime()
{
  sendNTPpacket(SNTP_server_IP);
  delay(1000);
  if ( UdpBytewise.available() ) {
    for(int i=0; i < 40; i++)
       UdpBytewise.read(); // ignore every field except the time
    const unsigned long seventy_years = 2208988800UL + timeZoneOffset;
    return getUlong() -  seventy_years;
  }
  return 0; // return 0 if unable to get the time
}
 
unsigned long sendNTPpacket(byte *address)
{
  UdpBytewise.begin(123);
  UdpBytewise.beginPacket(address, 123);
  UdpBytewise.write(B11100011);   // LI, Version, Mode
  UdpBytewise.write(0);    // Stratum
  UdpBytewise.write(6);  // Polling Interval
  UdpBytewise.write(0xEC); // Peer Clock Precision
  write_n(0, 8);    // Root Delay & Root Dispersion
  UdpBytewise.write(49);
  UdpBytewise.write(0x4E);
  UdpBytewise.write(49);
  UdpBytewise.write(52);
  write_n(0, 32); //Reference and time stamps
  UdpBytewise.endPacket();
}
 
unsigned long getUlong()
{
    unsigned long ulong = (unsigned long)UdpBytewise.read() << 24;
    ulong |= (unsigned long)UdpBytewise.read() << 16;
    ulong |= (unsigned long)UdpBytewise.read() << 8;
    ulong |= (unsigned long)UdpBytewise.read();
    return ulong;
}
 
void write_n(int what, int how_many)
{
  for( int i = 0; i < how_many; i++ )
    UdpBytewise.write(what);
}

Como podem ver o código é bastante simples. E funciona, a parte da BD não vou explicar com muito detalhade, ate porque o que não falta por ai são howtos de mysql e podem querer usar outras BD’s. Mas assim por alto tem uma tabela com um ID, Data, Hora, SensorID, Temperatura, Humidade. Algo importante é que o ID seja do tipo auto-increment, porque facilita bastante depois o tirar os dados para inserir nos gráficos de RDDTool.

Para a parte de tirar os dados da BD tambem nao vou falar nisso porque eu usei perl, muitos preferem php, python, etc, por isso em vez de criar uma guerra, usem cada um o seu. Se quiserem que mande os scripts e o export da bd que usei, avisem ai nos comentários ou email. Aviso que não é um codigo bonito, mas funciona 😉

Um pormenor importante, eu insiro da hora e data na BD directamente do arduino, podem cortar este passo e usar o proprio script que invocam no servidor, sempre torna o sketch do arduino mais leve, mas eu como quero depois usar este sketch para outros projectos onde me convém ter a hora e data directamente no arduino, vou usar assim, e não noto muita diferença em ter e não ter a hora/data no arduino. Também nao uso tudo directamente no void loop() por motivos de arrumação, assim quando quero fazer testes basta retirar a invocação do void especifico do loop e pumba esta a funcionar, ou nao…

Os graficos:

Nao é mais do que um rrdtool que recebe dois valores, um da temperatura e um da humidade e gera o grafico. Mas ei, funciona e faz gráficos, claro que se pode melhorar alguns pormenores, mas isto ainda é um prototipo do prototipo.

Peças necessarias:

1- Arduino

2- Sensor de Temperatura

3- Ethernet Shield

4 – Servidor com mysql+rrdtool+apache+etc

5- Tempo e paciencia.

No geral é tão simples quanto isto:

1 – Saca os dados do sensor
2- Formata os dados dos sensores
3- Insere os dados na BD do servidor
4- Formata os dados no servidor
5- Gera um grafico.

Como sempre, convém avisar que não percebo nada disto, e não me responsabilizo de usarem o meu código e derem cabo de qualquer coisa no vosso servidor ou no vosso arduino ;). Duvidas e sugestões, como sempre são muito bem vindas.

No futuro a ideia é usar o SD card do ethernet shield como backup caso a BD esteja em baixo, ou simplesmente tirar a BD da equação e guardar tudo no SD card e ir uma vez por dia via script buscar os dados ao SD card…

8 Responses to “Temperatura+Arduino+Mysql=RDDTool”

  1. Ethernet Shield V.2.0 – Problemas « JAPAB says:

    […] a seguir a ter post o post anterior Temperatura+Arduino+Mysql=RDDTool deparei-me com o LED do meu arduino, que ate à cinco minutos estava a actualizar perfeitamente, […]

  2. Gabryel says:

    Camarada, eu vi esse exelente projeto teu e me interessei que estou precisando de fazer um datalogger pelo ethernet shield como tu fez.
    Mas meu problema tem sido a “conversa” do arduino com php, eu não estou conseguindo passar o valor captado pelo arduino para o php, já testei algumas coisas o codigo de gravação no txt (eu fiz assim a principio pq é mais simples) está certo, mas o php não está lendo a variavel do arduino ou o arduino não está conseguindo enviar o valor para o php.

    Me dá uma força camarada, se vc puder postar só uma parte do seu arquivo php pra eu ter ideia da aquisição dos dados eu agradeço

  3. rechena says:

    Gabryel,
    De facto não percebo muito de php, neste caso particular usei Perl, mas o que lhe posso recomendar, é olhar para o seguinte endereço de onde tirei um código php gratuito que estou a usar noutro projecto.

    http://openenergymonitor.org/emon/node/88

    Nao sei concretamente o que precisa, de logar, mas este código é bastante comentado e da para perceber bem o que faz e quando faz.

    Qualquer duvida que possa ajudar, é so dizer.

    Ab
    Rechena

  4. Gabryel says:

    é rapaz, eu não consegui progresso nenhum, achei o codigo desse site que vc passou muito complicado.
    Eu faço GET e o valor não vai para o txt de jeito nenhum
    Mas valeu a força ae, vou continuar a procurar o codigo php

  5. rechena says:

    Mas porque queres ter o valor em txt?!?
    Se a ideia é mandar para php, é so procurares um codigo de php que grave os dados que recebe em .txt…

  6. Gabryel says:

    Voltei, pois é cara, eu estava gravando em txt por ser uma forma mais facil de visualizar o resultado.
    O meu codigo pra gravar no txt está funcionando o que não funciona é q eu no .pde faço
    cliente.println(“GET /index.php?dado=”);
    cliente.println(valor);
    e os demais codigos de http e host
    e no codigo php tem $valor = $GET_[“dado”];

    mas pelo jeito o valor não está passando para a variavel do php. Eu estou editando o codigo no dreamweaver, e já estou começando a achar que o problema está nisso.

    valeu pela força ae cara

  7. rechena says:

    Ok,
    Experimenta fazer assim:
    Assumindo que tem o byte server[] = bem definido.

    client.print(“GET /index.php?dado=”);
    client.println(valor);

    Ou seja substitui o client.println por client.print.

    Se nao der, envia-me o codigo do lado do arduino para dar uma olhada.

    Ab

  8. Messias Manoel da Silva Junior says:

    Rechena,

    cara, boa noite…
    Eu estive olhando o seu projeto, queria saber se teria como você me mandar o fonte do teu projeto para poder implementar o DB e gerar um gráfico como você acabou de mostrar na sua página?

    Aguardo o retorno.
    Até.

Leave a Reply

Follow Me