sábado, 19 de outubro de 2013

Web Cliente com Arduino, ENC28J60 e PHP


Nível de complexidade: Mediano. Necessário prévio conhecimento sobre Arduino, redes de computadores e programação em PHP.

Uma  funcionalidade muito interessante para o Arduino é a conexão com a rede local ou com a Internet. Isso pode ser realizado de forma relativamente simples com a ajuda do Ethernet Shield oficial, com o chip W5100 e sua respectiva biblioteca "ethernet" presente no Arduino 1.5.4.


No entanto, esta não é a única forma. Neste exemplo vou utilizar o Arduino UNO e um adaptador ethernet ENC28J60 que custa 1/3 do valor da Ethernet Shield oficial, em um exemplo que transmite os dados de um sensor qualquer para um site em PHP na internet ,no meu caso ivair.orgfree.com


Conexões:

A comunicação entre o Arduino e o ENC28J60 utiliza o protocolo SPI e os pinos devem ser conectados da seguinte forma:
  ARDUINO     ENC28J60  
GND
GND
3.3V
VCC
Pino 2
INT
Pino 10
CS
Pino 11
SI
Pino 12
SO
Pino 13
SCK

Um sensor deve ser conectado a entrada A0 do arduino UNO, caso utilize um potenciômetro conecte o terminal central a porta A0 e os terminais laterais um ao positivo e o outro ao negativo.

Código Arduino UNO:

    No início do código é definido um MAC para o adaptador ENC28J60, bem como são declaradas as demais variáveis utilizadas pelo programa. Neste exemplo o site PHP "ivair.orgfree.com" receberá e apresentará os dados enviados pelo arduino. O código do site está também logo abaixo.

Função Setup():
    O ENC28J60  deve estar conectado a um roteador com DHCP ativado para que possa ser configurado automaticamente (ether.dhcpSetup()) o número IP, a máscara, o gateway e o servidor DNS.
    Imediatamente após a gravação abra o monitor serial e será possível observar os resultados da configuração,  ou mensagens de erros,  caso algo esteja errado. Se tudo correr bem o site responde com um "OK" para cada valor recebido e armazenado na base de dados.


A função Loop():
    Esta função envia por GET o valor do sensor conectado a entrada analógica A0. Por exemplo: caso o valor obtido do sensor seja 356, é gerada a seguinte string: "http://ivair.orgfree.com/inserir.php?val=356" que é enviada pela função: ether.browseUrl().
   Isso pode ser testado digitando-se essa string diretamente no navegador. Após o recebimento,  o site em PHP responde com a sequência de texto padrão do protocolo e a palavra "OK", que é tratado pela função my_callback() e apresentado no monitor serial. Para visualizar a mensagem de retorno completa retire o comentário da segunda linha e comente a terceira.

   A biblioteca utilizada é a EtherCard desenvolvida por Jean Claude Wippler. Após descompactá-la na pasta libraries, crie um novo sketch com o código a seguir.

#include <EtherCard.h>
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
byte Ethernet::buffer[700];
static uint32_t timer;
char website[] PROGMEM = "ivair.orgfree.com";
char temp_sensor[150];
int an0 = A0;
int sensor = 0;

static void my_callback (byte status, word off, word len)
{
   Ethernet::buffer[off+len] = 0;    //mensagem do retorno do servidor
   //Serial.print((const char*) Ethernet::buffer + off);  //mostra todo o retorno
   Serial.print((const char*) Ethernet::buffer + off + 157); //mostra somente ok
}

void setup ()
{
  delay(1000);
  Serial.begin(57600);
  Serial.println("Obtendo IP:");
  if (ether.begin(sizeof Ethernet::buffer, mymac,10) == 0)
    Serial.println( "Falha ao acessar o controlador Ethernet");
  if (!ether.dhcpSetup())
    Serial.println( "Falha ao obter o IP");
  ether.printIp("IP: ", ether.myip);
  ether.printIp("Mascara: ", ether.mymask);
  ether.printIp("Gatway: ", ether.gwip);
  ether.printIp("Sevidor DNS: ", ether.dnsip);
  if (!ether.dnsLookup(website))     //IP do servidor por DNS
    Serial.println("Falha no DNS");
  //ether.parseIp(ether.hisip, "144.76.99.221"); //IP do servidor manual
  ether.printIp("Servidor: ", ether.hisip);
  Serial.println(" ");
}

void loop ()
{
  ether.packetLoop(ether.packetReceive());
  if (millis() > timer)
  {
    timer = millis() + 5000;
    ether.persistTcpConnection(true);
    sensor = analogRead(an0);
    sprintf(temp_sensor, "?val=%d", sensor);
    ether.browseUrl(PSTR("/inserir.php"), temp_sensor, website, my_callback);
  }
}

Código do site PHP:

O site obrigatoriamente deve estar na Internet, não consegui fazê-lo rodar em um servidor local.
   No provedor PHP crie uma base de dados no seguinte formato:
        Tabela: leituras
        Campos: cod(int auto incremento, chave primária), valor(int) e data (datahora).
   O sistema todo é extremamente simples e constituído por três páginas: 
         index.php - Gera e apresenta uma tabela com os dados armazenados na base.
         apagar.php - Limpa a base de dados.
         inserir.php - Insere o valor recebido por GET na base de dados

   Observe que todas fazem a conexão com os seguintes dados: ("localhost","login","senha","base_de dados"), O primeiro item é o nome do servidor e é definido pelo provedor da página, no meu caso o provedor indicou que utilizasse a palavra "localhost", em outros o nome pode mudar.
O segundo e terceiro itens são o login  e a senha escolhidos no momento do cadastro da conta.
O quarto item é o nome da base de dados, que muitas vezes é o próprio login. Faça uma busca no google por "free php host" que você achará alguns gratuítos

Página index.php
<html>
<head>
<meta http-equiv="refresh" content="10">
  <title>Arduino</title>
</head>
<body bgcolor="Lightsteelblue">
<table align="center" border="0" width="20%">
<tr><td align="center">
  <form method="POST" action="apagar.php">
    <input type="submit" value="Apagar">
  </form>
</td></tr>
</table>
<table align="center" border="1" width="20%">
  <tr><th>Sensor</th><th>Data - Hora</th></tr>
  <?php
    $con = mysqli_connect("localhost","login","senha","base_de dados") or die("Falha na conexao com o MySQL");
    $res = mysqli_query($con,"SELECT * FROM leituras") or die ("Nao foi possivel realizar a consulta");
    while($linha = mysqli_fetch_array($res))
    {
     echo "<tr><td align='center'>".$linha['valor']."</td>";
     echo "<td align='center'>".$linha['data']."</td></tr>";
    }
    mysqli_close($con);
  ?>
</table>
</body>
</html>

Página apagar.php
<?php
  $con = mysqli_connect("localhost","login","senha","base_de dados") or die("Falha na conexao com o MySQL");
  mysqli_query($con,"DELETE FROM leituras WHERE cod") or die ("Falha ao apagar os dados");
  mysqli_close($con);
  header("Location:index.php");
  exit(0);
?>

Página inserir.php
<?php
  date_default_timezone_set('America/Sao_Paulo');
  if (!empty($_GET['val']))
  {
    $valor = $_GET['val'];
    $data = date("d/m/Y H:i:s");
    $con = mysqli_connect("localhost","login","senha","base_de dados") or die("Falha na conexao com o MySQL");
    mysqli_query($con,"INSERT INTO leituras VALUES ('','$valor','$data')") or die ("Falha ao inserir os dados");
    mysqli_close($con);
    echo "ok  ";     //resposta em caso de sucesso
  }
  else
    echo "falha";   //resposta em caso de falha
?>

Boa sorte.

Ivair.

9 comentários:

  1. ola amigo, estou com dificuldade jogar esses codigos php no orgfree esta dando erro , não tem como vc me enviar por e mail o arquivo dai só inserir com fizella, no orgfree, obrigado

    Ediy
    Ediyfs@gmail.com

    ResponderExcluir
  2. Olá Ediy,

    O código acima é o mesmo que está no orgfree, a diferença é que você tem que atualizar a rotina mysqli_connect("localhost","login","senha","base_de dados"), com seus dados, no meu caso ficou assim:
    mysqli_connect("localhost","600962","MINHA_SENHA","600962")
    O localhost continua, mas login, base de dados e senha vai depender da sua conta, se você colocar meus arquivos com certeza não funcionarão.

    ResponderExcluir
  3. Oi amigo... Gostaria de uma ajuda sua... Reproduzi aqui tudo que vc descreveu acima, só que não consigo obter exito pois o meu projeto não envia os dados para o bd, quando executo o serial monitor não mostra o OK, e sim caracteres expeciais estilo isso (¬°gònP HGÒñ¬pb éZ)... Se puderes me ajudar agradeço...

    ResponderExcluir
  4. Olá kgpnegro, agora estou usando a biblioteca UIPEthernet que fica mais fácil que a do posto. É uma versão da biblioteca padrão da placa W5100 adaptara para funcionar com a ENC28J60. Intale o pacote USBWebserver, crie o servidos na sua máquina e use este código.
    #include
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
    IPAddress ip(192,168,0,20); //ip estático
    IPAddress servDns(201,76,16,58); //ip estático
    IPAddress gateway(192,168,0,1); //ip estático
    IPAddress subnet(255,255,255,0); //ip estático
    IPAddress server(192,168,0,201);
    //IPAddress server(144,76,99,221); //sem DNS ivair.orgfree.com
    //char server[] = "ivair.orgfree.com"; //com DNS

    static uint32_t timer;
    EthernetClient client;

    void setup()
    {
    Serial.begin(9600);
    Serial.println("Conectando a rede...");
    //if(!Ethernet.begin(mac)) //ip dinâmico
    // Serial.println("Falha ao conectar a rede"); //ip dinâmico
    Ethernet.begin(mac, ip, servDns, gateway, subnet); //ip estático
    Serial.println(Ethernet.localIP()); //ip estático
    }

    void loop()
    {
    int charRX;
    int dados[9];
    int cont = 9;
    if (millis() > timer)
    {
    timer = millis() + 5000;
    if(client.connect(server,80))
    {
    client.println("GET /res.php HTTP/1.1");
    client.println("Host: 192.168.0.16");
    client.println("Connection: close\r\n");
    }
    else
    Serial.println("Falha na conexão");
    while(client.connected())
    {
    while(client.available())
    {
    charRX = client.read();
    Serial.write(charRX);
    if(charRX == '*')
    cont=0;
    if(cont<9)
    {
    dados[cont] = charRX;
    cont++;
    }
    }
    }
    Serial.println("\r\nDados Recebidos");
    for(int i=0; i<9;i++)
    Serial.write(dados[i]);
    client.stop();
    Serial.println("\r\n");
    }
    }

    ResponderExcluir
  5. Bom dia amigo, tudo bem ?
    Primeiramente parabéns pelo post, show de bola mesmo!
    Estou querendo fazer um projeto que precisa se comunicar com o banco de dados através do arduino e php... pelo seu post parece que me atenderia.
    Estou usando o WAMP para emular um servidor, minha ideia seria fazer meu not conectado no reteador ser o servidor e usar o arduino para se comunicar com ele, por enquanto pode ser só rede local mesmo, não estou preocupado ainda com fazer acesso de fora. Porém estou com algumas dúvidas, espero que possa me ajudar!
    Minha dúvida....
    Nessa parte:

    IPAddress ip(192,168,0,150); //ip estático
    IPAddress servDns(201,76,16,58); //ip estático
    IPAddress gateway(192,168,0,1); //ip estático
    IPAddress subnet(255,255,255,0); //ip estático
    IPAddress server(192,168,0,201);

    Ip: seria um ip que eu defino para a placa de ethernet no meu caso no roteador está (192.168.0.ESCOLHO1. correto ?
    ServDns: oque seria ?
    Gateway: seria o gatway que esta no roteador. correto ?
    subnet: oque seria ?
    server: seria o ip da maquina que contem o servidor WAMP rodando. correto ?

    ResponderExcluir
  6. Mano ta funcionando tudo certinho... mas com conexão a internet, pq eu mudo as configurações de ip daí mudo o IP lá nas "propriedades de ethernet -> Protocolo IPv4 e tals", pq n uso dhcp, só que quando eu tiro o cabo de rede que dá acesso a internet do switch não funciona mais =/

    ResponderExcluir
  7. Olá, parabéns pelo projeto. Pode me passar seu email? Estou precisando tirar umas duvidas referente ao projeto q estou desrnvolvendo. Se puder me ajudar, ficarei grato.

    ResponderExcluir
  8. Olá.. quando tento compilar, da erro na IDE>.

    webClient.cpp:6:23: error: EtherCard.h: No such file or directory
    webClient:10: error: 'Ethernet' has not been declared

    Sabe o que pode ser:?

    ResponderExcluir