Ethernet cliente e servidor no mesmo código

Olá Amigos,

Estou com um problema que me tirou muitas noites de sono. Meu código abaixo já se encontra em produção, ele funciona perfeitamente, porém ao tentar adicionar a função UpdateNoIP() (também exibida abaixo) o código deixa de funcionar.

Acredito que o problema se deve ao conflito entre cliente e servidor, porém ambas as variáveis de cliente e servidor são locais, não deveriam interferir uma na outra. O código é meio grande, mas fácil de entender.

// Incluindo bibliotecas necessárias
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>

// Define as variáveis para simular o multitasking
unsigned long UpdateCycle = 30000;
unsigned long UpdateLastMillis = 0;

// Ponteiro para o meu arquivo no SD Card
File myFile;

// Define os dados da rede necessarios para o Ethernet Shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {192,168,1,177};
EthernetServer server(80);

SoftwareSerial mySerial(2, 3);

/**
 * Função de configuração do Arduino.
 */
void setup()
{
  Serial.begin(19200);
  mySerial.begin(19200);
  Ethernet.begin(mac, ip);
  pinMode(10, OUTPUT);

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("SD iniciado");
}

/**
 * Loop principal do programa.
 */
void loop()
{

  if(cycleCheck(&UpdateLastMillis, UpdateCycle))  {
    Serial.println("Processando..."); 
    get_sms_data();
    Serial.println("Processado!!!!");
    UpdateNoIP();
  }
  
 client_request(); // Fica escutando clientes
}

/**
 * Salva o pedido do cliente no SD Card.
*/
void save_sms_data(char data[])
{
//código que salva o sms  
}

/**
 * Recupera o texto vindo do cartão SD e envia o SMS.
 *
 * @TODO Criar algoritimo que impede receber novos pedidos enquando envia os SMS's.
 * @TODO Criar um resposta HTTP para sucesso e outra para falha. (Se possível ;.;)
 * @TODO Confirmar se o Arduino garante que o arquivo só possa ser escrito por um processo.
 *
 */
void get_sms_data()
{
//código que recupera o SMS
}

/**
 * Função para realizar o envio de SMS.
 */
void send_sms(char msg[], char phone[])
{
  //Código que envia o SMS
}

/**
 * Função para monitorar o pedido vindo do cliente.
 */
void client_request()
{
  EthernetClient client = server.available();
  if (client) {
    char mensagem[175];
    byte pointer = 0; 
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        mensagem[pointer] = c;
        pointer++;
        mensagem[pointer] = '\0';
      }
    }
    client.stop();
    //recebeu a msg do PHP grava no SD
    Serial.println(mensagem);
    save_sms_data(mensagem);
  }
}

/**
 * Função para simular o multitasking.
 */
boolean cycleCheck(unsigned long *UpdateLastMillis, unsigned long UpdateCycle) {

  unsigned long currentMillis = millis();

  if(currentMillis - *UpdateLastMillis >= UpdateCycle)  {
    *UpdateLastMillis = currentMillis;
    return true;
  }  
  else{
    return false;
  }
}


void UpdateNoIP(){
  EthernetClient client;
  if(client.connect("dynupdate.no-ip.com", 80)){
    Serial.println("Conectando com No-IP");

    client.println("GET /nic/update?hostname=freesms2.hopto.org HTTP/1.0");
    client.println("Host: dynupdate.no-ip.com");
    client.println("Authorization: Basic ZHIuObkx1Obkx1ObViLWZWOEJViLWZWOEJViLWZWOEJkx1UlViLWZWOEJ6Q2VpSlA=");
    client.println("User-Agent: Arduino Client/1.0 dr.renatotavares@gmail.com");
    client.println();

    while(client.connected()){    
      // stay in this loop until the server closes the connection

      while(client.available()){
        // The server will not close the connection until it has sent the last packet
        // and this buffer is empty

        char read_char = client.read(); 
        Serial.write(read_char);
      }
    }
    // close your end after the server closes its end
    client.stop();
    Serial.println("Conexão com No-IP fechada");
  }
  else{
    Serial.println("Falha na Conexão");
  }
}

Alguém sabe por que a função UpdateNoIP() está atrapalhando a função client_request()?

Quando dizes que o código deixa de trabalhar, a que te referes? O código fica bloqueado? O chip faz um reset? Demora muito a bloquear?

Um dos problemas que eu tive ao usar a Ethernet era mesmo fechar a socket tendo ainda dados para ler. Acho que devias dar uma vista de olhos se quando o servidor fecha ainda tens dados por ler. Se a socket fechar e tu não tiveres lido tudo, não podes reutilizar essa socket. Não estarás a ficar sem memória?

Para ser sincero não sei o que da errado. Ele imprime na serial pedaços de letras, o cardão SD fica reiniciando, e nunca nem o cliente muito menos o servidor ficam disponíveis. Ele salva no SD só as primeiras letras da mensagem recebida, bem estranho isso.

Em um primeiro momento acreditei que o erro se deve a um possível conflito entre o cliente e o servidor, já que o código funciona sem a função UpdateNoIP() e a função em questão funciona perfeitamente sozinha.

Para curiosidade, aqui se encontra o meu código completo. https://gist.github.com/renatotavares/6155004

Se queres atualizar o teu ip com o serviço da noip porque não apenas colocares isso no router e deixares o Arduino de fazer esse trabalho?

Hmmm, talvez falta de memória.

assim de repente não vejo nada, mas sem fazeres um pouco mais de troubleshooting vai ser dificil.

HugoPT: Se queres atualizar o teu ip com o serviço da noip porque não apenas colocares isso no router e deixares o Arduino de fazer esse trabalho?

Meu router não faz isso. :(

@bubulindo

Uso o Arduino Mega, mesmo com ele a memória não dá?

Ok... não deve ser isso então.

ACHO que está declarando a classe EthernetClient dentro de funções, assim sendo quando esta acaba, creio eu que o compilador abre para uso o local dos dados, fragmentando a memoria já que há diversos buffers.

quando declara o server aparentemente está correto, antes do setup

EthernetServer server(80);

mas vai declarar o ethernetclient dentro das funções:
cliente request():

EthernetClient client = server.available()

pode isso?

updatenoip():

EthernetClient client;

jogue a declaração do cliente lá em cima junto ao server, e a função cliente request, creio que resolverá

fabiohbm007: jogue a declaração do cliente lá em cima junto ao server, e a função cliente request, creio que resolverá

Mas se colocar o cliente lá em cima, como vou diferenciar entre o cliente comunicando com o Arduino e o cliente solicitando o site NoIP?

o client lá em cima é a declaração da classe que contem funções e parametros... o client do EthernetServer, é sempre a parte que está pedindo pra você, o EthernetClient é você pedindo a outro, com o shield W5100 você pode declarar até 4 sockets "simultaneos" sendo server ou client. o sistema de declaração da parte ethernet é uma furada... 1. declare lá em cima o cliente verdadeiro(updatenoip) como "clientenoip" e toda chamada a ele deste jeito, o que talvez está acontecendo é conflito pelos dois client...