Serial.print() escrevendo errado ao usar SD

Meu programa que usa a biblioteca Ethernet e SPI estava funcionando perfeitamente, mas depois que eu adicionei também a biblioteca SD, o Serial.print() não funciona direito. Depois que o SD.begin() é chamado, o texto sai todo errado no monitor serial, com um monte de caracteres estranhos. O cartão foi testado e funciona no PC.

/*
 Web Server

A simple web server that shows the value of the analog input pins.
using an Arduino Wiznet Ethernet shield.

Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)

created 18 Dec 2009
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe

*/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 3 };
byte dnServer[] = {192, 168, 0, 3};
byte gateway[] = {192, 168, 0, 3};
byte subnet[] = {255, 255, 255, 0};

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
File arquivo;
const char pagina[260] = "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nRefresh: 10000\n\n<!DOCTYPE HTML><html><H1>Metodo POST</H1><form action=\"\" method=\"POST\">texto:
<input type=\"text\" name=\"texto\"><input type=\"submit\" value=\"Submit\"></form>
<hr></html>";
const char cabecalho_img[70] = "HTTP/1.1 200 OK\nAccept-Ranges: bytes\nContent-Type: image/jpeg\n\n";

void setup() {
 //pinMode(10, OUTPUT);
 
 // Open serial communications and wait for port to open:
 Serial.begin(9600);
 while (!Serial) {
   ; // wait for serial port to connect. Needed for Leonardo only
 }
 
 
 
 // start the Ethernet connection and the server:
 Ethernet.begin(mac, ip);
 server.begin();
 Serial.print("server is at ");
 Serial.println(Ethernet.localIP());
}


void loop() {
 int i = 0;
 int j = 0; 
 char requisicao[1000];
 bool metodo;
 char alvo[20];
 char parametros[5][10];
 char valores[5][10];
 char c;
 bool espaco = false;
 
 // listen for incoming clients
 EthernetClient client = server.available();
 if (client) {
   Serial.println("new client");
   // an http request ends with a blank line
   boolean currentLineIsBlank = true;
   while (client.connected()) {
     if (client.available()) {
       char c = client.read();
       requisicao[i] = c;
       
       Serial.write(c);

       // if you've gotten to the end of the line (received a newline
       // character) and the line is blank, the http request has ended,
       // so you can send a reply
       //if(c == '\n' && currentLineIsBlank)
          //quebras++;
          
       //if(quebras > 1)
         //break;
         
       //Serial.print("\nQuebras: ");
       //Serial.println(quebras);
         
       if (c == '\n') {
         // you're starting a new line
         currentLineIsBlank = true;
       }
       else if (c != '\r') {
         // you've gotten a character on the current line
         currentLineIsBlank = false;
       }
       
       i++;
     }
     else{
       requisicao[i] = '\0';
       break;
     }
   }
  
   if(requisicao[0]=='P')
     metodo = true;
   else if(requisicao[0]=='G')
     metodo = false;
   else
     //erro
     ;
   //Serial.println(requisicao); 
     
   for(j=0; !espaco || c!='/'; j++){
     

     espaco = c == ' '; //Verifica se c é igual a ' ' (espaço) e armazena o resultado na variável espaco

       
     c = requisicao[j];
       
       //Serial.println("Loop");
       //Serial.println(j);
       //Serial.println(c);
   }
       
   for(i=0; requisicao[j] != ' '; i++, j++){
     c = requisicao[j];
     alvo[i] = c;
     //Serial.println(c);
   }
   
   alvo[i] = '\0';
   
   //Serial.println("metodo/valor");
   
   

   if(metodo){
     Serial.print("SeparA---11111----\n");
     for(i=0, currentLineIsBlank = false;; i++){
       
       c = requisicao[i];
       
       if(c == '\n' && currentLineIsBlank)
         break;
       
       if (c == '\n') {
         // you're starting a new line
         currentLineIsBlank = true;
       }
       else if (c != '\r') {
         // you've gotten a character on the current line
         currentLineIsBlank = false;
       }

       
       Serial.print(c);

       
     }
     Serial.print("SeparA--22222--\n");
     
     i++;
     c = requisicao[i];
     i++;
     
     Serial.println("");
     
     for(j=0; c != '='; j++, i++){
      
      parametros[0][j] = c;
      c = requisicao[i];
      //Serial.print(c);
     }
     
     Serial.println("");
     

     c = requisicao[i];
     i++;
     
     
     parametros[0][j] = '\0';
     
     for(j=0; c != '\0' && c != '&'; j++, i++){
       
       valores[0][j] = c;
       c = requisicao[i];
       //Serial.print(c);
     }
     
     valores[0][j] = '\0';

   }
   
   
   
   
   Serial.print("Metodo: ");
   if(metodo)
     Serial.println("POST");
   else
     Serial.println("GET");
     
   Serial.print("Alvo: ");
   Serial.println(alvo);
   
   Serial.println("Postados:");
   
   for(i=0; parametros[0][i] != '\0'; i++)
     Serial.print(parametros[0][i]);
   Serial.print(": ");
   for(i=0; valores[0][i] != '\0'; i++)
     Serial.print(valores[0][i]);
   /*
   Serial.print("\n\n\nRequisicaoo: ");
   Serial.println(requisicao);
   */
   
   
   //if (!SD.begin(4)) {
    // Serial.println("initialization failed!");
    // return;
   //}

   
   
   if(SD.exists(alvo)){
     Serial.println("\nArquivo encontrado");
     arquivo = SD.open(alvo, FILE_READ);

     for(i=0; cabecalho_img[i] != '\0'; i++)
       client.print(cabecalho_img[i]);
       
     while(arquivo.available())
       client.print(arquivo.read());
   }
   else{
     Serial.println("\nArquivo nao encontrado");
     for(i=0; pagina[i] != '\0'; i++){
       client.print(pagina[i]);
       //Serial.print(pagina[i]);
     }
   }
   
   
   
   /*
   client.println("HTTP/1.1 200 OK");
   client.println("Content-Type: text/html");
   client.println("Connection: close");  // the connection will be closed after completion of the response
   client.println("Refresh: 10000");  // refresh the page automatically every 5 sec
   client.println();
   client.println("<!DOCTYPE HTML>");
   client.println("<html>");
   
   client.println("<H1>Metodo POST</H1>");
   
   client.println("<form action=\"\" method=\"POST\">");
   client.println("texto:
");
   client.println("<input type=\"text\" name=\"texto\">");
   client.println("<input type=\"submit\" value=\"Submit\">");
   client.println("</form>");
   client.println("
");
   client.println("<hr>");
   
   client.println("</html>");
   */
   
   Serial.println("\nEnviado");
   // give the web browser time to receive the data
   delay(1);
   // close the connection:
   client.stop();
   Serial.println("\nclient disconnected");
 }
}

E que queres que a gente faça?

Não teria uma ideia de qual a causa e como resolver o problema ?

Sem postar o código é difícil (para não dizer impossível) responder. Dê uma leitura nas normas e recomendações do fórum antes de postar.

Faço minhas as palavras do luis... Agradeço que nos tenham em tão elevada estima para achares que só com uma leve descrição te consigamos dizer onde está o problema, mas isso é quase impossível sem ver algo mais... Tipo código ou saber se testaste o cartão sd em separado com a porta série...

Perdão, já ia me esquecendo...

Eu não tenho muita experiência com o cartão SD ao mesmo tempo que o shield Ethernet, mas parece-me uqe talvez possa haver chatice entre aceder ao cartão e ao shield Ethernet alternadamente...

Quando dizes que o texto que aparece no Serial não faz sentido, tens um exemplo?

Acontece só por colocar o SD.begin() ? Ou acontece sempre que o programa chega a determinado ponto com o SD.begin()?

Esse SD.begin, não deveria estar no setup()?

Os comentários nas linhas do Chip Select (tanto da Ethernet como do SD) não me parece que estejam muito bem. Para além disso, está o comentário de todo este bloco:

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

que para mim, não deve ter muito bom efeito.

Quais são os módulos de hardware que está a usar e como os ligou?

Esse é o texto que sai no serial:
O Serial.begin() estava no setup. Eu mudei só para testar
Não sei ao certo quando começa o problema, mas aparentemente é depois do ponto onde o Serial.begin() é chamado. Quando ele é omitido o problema não ocorre. Também testei o cartão SD com o exemplo do Arduino, e, mesmo incluindo o uso do Ethernet nele o problema parece não ocorrer.
Eu comentei essas partes só para testar se o problema era ai mesmo. Estou usando um Arduino Uno com shield Ethernet (W5100) + cartão SD no mesmo shield.

Para despistar isto, podes colocar um serial.print("teste"); após a linha

Serial.write(c);

?

Tenho um feeling que isso não é problema da porta série, mas da organização do código.

Dá-me ideia que tu não estás a separar o receber o pedido html e responder e isso é que estará a dar problemas.

Mudei o SD.begin() para o setup.
A parte do "teste" e o pedido HTML ele imprime. O resto, às vezes ele imprime tudo, as vezes para na metade.

Duas questões:

  • como está agora o sketch?
  • como estão feitas as ligações? (estou a pensar que possa aí haver alguma interferência entre as ligações do SD e da porta série)
#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
#include <avr/pgmspace.h>



byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 3 };
byte dnServer[] = {192, 168, 0, 3};
byte gateway[] = {192, 168, 0, 3};
byte subnet[] = {255, 255, 255, 0};


EthernetServer server(80);

File arquivo;
const PROGMEM char pagina[260] = "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nRefresh: 10000\n\n<!DOCTYPE HTML><html><H1>Metodo POST</H1><form action=\"\" method=\"POST\">texto:
<input type=\"text\" name=\"texto\"><input type=\"submit\" value=\"Submit\"></form>
<hr></html>";
const PROGMEM char cabecalho_img[70] = "HTTP/1.1 200 OK\nAccept-Ranges: bytes\nContent-Type: image/jpeg\n\n";

void setup() {
  pinMode(10, OUTPUT);
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }



  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}


void loop() {
  int i = 0;
  int j = 0;
  char requisicao[1000];
  bool metodo;
  char alvo[20];
  char parametros[5][10];
  char valores[5][10];
  char c;
  bool espaco = false;

  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        requisicao[i] = c;

        Serial.write(c);
        //Serial.print("teste");

        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        //if(c == '\n' && currentLineIsBlank)
        //quebras++;

        //if(quebras > 1)
        //break;

        //Serial.print("\nQuebras: ");
        //Serial.println(quebras);

        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }

        i++;
      }
      else {
        requisicao[i] = '\0';
        break;
      }
    }

    if (requisicao[0] == 'P')
      metodo = true;
    else if (requisicao[0] == 'G')
      metodo = false;
    else
      //erro
      ;
    //Serial.println(requisicao);

    for (j = 0; !espaco || c != '/'; j++) {


      espaco = c == ' '; //Verifica se c é igual a ' ' (espaço) e armazena o resultado na variável espaco


      c = requisicao[j];

      //Serial.println("Loop");
      //Serial.println(j);
      //Serial.println(c);
    }

    for (i = 0; requisicao[j] != ' '; i++, j++) {
      c = requisicao[j];
      alvo[i] = c;
      Serial.println(c);
    }

    alvo[i] = '\0';

    //Serial.println("metodo/valor");



    if (metodo) {
      Serial.print("SeparA---11111----\n");
      for (i = 0, currentLineIsBlank = false;; i++) {

        c = requisicao[i];

        if (c == '\n' && currentLineIsBlank)
          break;

        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }


        Serial.print(c);


      }
      Serial.print("SeparA--22222--\n");

      i++;
      c = requisicao[i];
      i++;

      Serial.println("");

      for (j = 0; c != '='; j++, i++) {

        parametros[0][j] = c;
        c = requisicao[i];
        //Serial.print(c);
      }

      Serial.println("");


      c = requisicao[i];
      i++;


      parametros[0][j] = '\0';

      for (j = 0; c != '\0' && c != '&'; j++, i++) {

        valores[0][j] = c;
        c = requisicao[i];
        //Serial.print(c);
      }

      valores[0][j] = '\0';

    }

    requisicao[0] = '\0';


    Serial.print("Metodo: ");
    if (metodo)
      Serial.println("POST");
    else
      Serial.println("GET");

    Serial.print("Alvo: ");
    Serial.println(alvo);

    Serial.println("Postados:");

    if (metodo) {
      for (i = 0; parametros[0][i] != '\0'; i++)
        Serial.print(parametros[0][i]);
      Serial.print(": ");
      for (i = 0; valores[0][i] != '\0' && i <= 9; i++)
        Serial.print(valores[0][i]);

      Serial.print("\n\n\nRequisicaoo: ");
      //Serial.println(requisicao);
    }
    else {
      Serial.print("Nada foi postado.");
    }





    if (0/*SD.exists("arara.jpg")*/) {
      Serial.println("\nArquivo encontrado");
      arquivo = SD.open("texto.txt", FILE_READ);

      for (i = 0; cabecalho_img[i] != '\0'; i++)
        client.print(cabecalho_img[i]);

      while (arquivo.available())
        client.print(char(arquivo.read()));
    }
    else {
      Serial.println("\nArquivo nao encontrado");
      for (i = 0; pagina[i] != '\0'; i++) {
        client.print(pagina[i]);
        //Serial.print(pagina[i]);
      }
    }



    /*
    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html");
    client.println("Connection: close");  // the connection will be closed after completion of the response
    client.println("Refresh: 10000");  // refresh the page automatically every 5 sec
    client.println();
    client.println("<!DOCTYPE HTML>");
    client.println("<html>");

    client.println("<H1>Metodo POST</H1>");

    client.println("<form action=\"\" method=\"POST\">");
    client.println("texto:
");
    client.println("<input type=\"text\" name=\"texto\">");
    client.println("<input type=\"submit\" value=\"Submit\">");
    client.println("</form>");
    client.println("
");
    client.println("<hr>");

    client.println("</html>");
    */

    Serial.println("\nEnviado");
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("\nclient disconnected");
  }
}

As ligações estão como vieram por padrão no arduino.

Um arduino UNO tem 2k de memória RAM....

Estas linhas tiram

  char requisicao[1000];     //1000 +
  bool metodo;                   // 1 +
  char alvo[20];                  // 20 +
  char parametros[5][10];   // 50 +
  char valores[5][10];         // 50

1121 bytes dessa memória.

A porta série e cliente Ethernet também tiram uns quantos bytes... precisas mesmo de 1000 chars aí??

Eu não sou muito bom em C...

Importas-te de me explicar o que estas linhas fazem??

    for (j = 0; !espaco || c != '/'; j++) {
      espaco = c == ' '; //Verifica se c é igual a ' ' (espaço) e armazena o resultado na variável espaco

A segunda linha em particular não bate certo com o comentário... era mesmo isso que querias fazer?

Eu ao olhar para isso, vejo que pode haver um runaway pointer, porque a condição de paragem do ciclo não está dependente do valor j. Não seria melhor limitares o j aqui também??

Neste pedaço de código:

    for (i = 0; requisicao[j] != ' '; i++, j++) {
      c = requisicao[j];
      alvo[i] = c;
      Serial.println(c);
    }

Tens algo semelhante... o j já stá num valor elevado e não há nenhum limite para o ciclo parar nalgum lado.

Não tinha tido tempo para olhar para o código com muita atenção, mas realmente esse código assim está muito "rebuscado".

Perdão pela demora.
A linha espaco = c == ' '; seria o mesmo que

if(c==' ')
   espaco = true;
else
   espaco =false;

Mas como a comparação retorna verdadeiro ou falso e "espaco" é uma variável booleana, achei mais eficiente jogar o resultado da comparação diretamente nela. Como em C (se eu não estiver errado) o operador de comparação (==) tem precedência sobre o de atribuição (=), ele executa primeiro a comparação e depois atribui o resultado à variável booleana.

for (j = 0; !espaco || c != '/'; j++) {
      espaco = c == ' '; //Verifica se c é igual a ' ' (espaço) e armazena o resultado na variável espaco

Realmente, ainda tenho que dar uma melhorada no código. Não usei o valor de j na condição do for porque nesse caso a intenção é parar quando eu encontrar um espaço seguido de uma barra, para a seguir ler o texto depois dela:

    for (i = 0; requisicao[j] != ' '; i++, j++) {
      c = requisicao[j];
      alvo[i] = c;
      Serial.println(c);
    }

Vou usar isso para saber a página que o cliente está pedindo. http://image.slidesharecdn.com/getconceitoshttp-101129063928-phpapp02/95/get-conceitos-http11-16-638.jpg?cb=1422654936

Nesse último clico for, com que valor começa a variável 'j'?
O código até pode estar todo correcto, mas não fica muito fácil de ler para alguém que não o conheça. Sendo assim, não é muito bom.

Começa com 5 ou 6. (varia conforme o tipo de requisição)

O problema e mesmo como disse o luissilva...

Isso e codigo feito com uma pagina web ao lado e a tirar o que se quer dela. Para quem le o codigo e normal que nao faca sentido. Mesmo assim... o comentario e valido pois estas a assumir que vais receber todos os dados.