Recuperar vários char e salvar em uma String

Estou tentando pegar o client.read() e salvar em uma unica variável para depois trabalhar com ela, geralmente as pessoas imprimem no serial um caractere por vez, quero pegar tudo em uma unica variável. meu código abaixo não funciona, alguém sabe o pq?

Como poderia fazer isso de forma correta?

EthernetClient client = server.available();

char resposta[20];
int i = 0;
  if (client) {
    Serial.println("new client");
   
   
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
             resposta[i] = c;
              i++;
      }
    }

    client.stop();
    Serial.println("client disonnected");
    
    for(int e = 0; e < 20; e++){
    Serial.println(resposta[e]);
    }
  }

Não explicas porque não funciona, mas eu vou assumir que é um "buffer overrun"

Repara que tu crias um vector com 20 posicões... mas depois escreves quantas??

while (client.connected() && i<19) //isto garante que não escreves para lá do espaco que tens.

resposta[19] = '\0'; //pior caso.
while (client.connected()) {
      if (client.available()) {
        char c = client.read();
             resposta[i] = c;
              i++;
      }
    }
resposta[i] = '\0'; //terminador de string.

E assim?

O Código completo esta aqui abaixo. Teste dar o print na string logo abaixo e não deu certo, além disso o http que quero pegar e jogar em uma unica string e bem maior que 20, e por isso alterei o código.

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

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1, 177);

EthernetServer server(80);

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  server.begin();
}

void loop() {

  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;

    char resposta[500];
    int i = 0;
    resposta[499] = '\0'; //pior caso.
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        resposta[i] = c;
        i++;

        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    resposta[i] = '\0'; //terminador de string.

    delay(1);
    client.stop();
    Serial.println("client disonnected");

    Serial.println(resposta);
  }
}

Desde já agradeço a você, pois é o único que responde aqui nesse fórum.

500 bytes de buffer?????

Queres ocupar um quarto da memória do chip para um buffer???

Porquê essa insistência em ler a string completa?

Desculpe minha ignorância quanto a eletrônica e programação. Quero ler a string completa porque foi o único jeito que encontrei para salvar um HTTP GET, não achei uma lib que me ajudasse, perguntei em TODOS os fóruns da internet, mandei e-mails, pedi ajuda e até paguei alguns programadores mas ninguem me disse como pegar variáveis de um GET e salvar em variáveis do arduino.

a ideia agora e fazer um telnet para o IP do arduino no formato string1|string2 dar um split nelas e assim salvar em duas variáveis.

Se você tiver um exemplo, e conseguir me ajudar e só mandar DM ou e-mail que chegamos a um acordo em $ pela ajuda.

alem disso atualmente estou com um arduino uno porém ja comprei esse aqui http://www.kickstarter.com/projects/435742530/udoo-android-linux-arduino-in-a-tiny-single-board?ref=live ele tem + memoria para eu gastar.

Ok... queres um GET ou telnet?

Eu não quero dinheiro. Primeiro porque isto não é o meu emprego e segundo porque não quero responsabilidade sobre o que digo aqui. O facto de não aceitar dinheiro também me dá a possibilidade ed ajudar quando posso...
E não acho prudente que pagues a alguém que diz que resolve o teu problema e não o faz. Mas tu é que sabes da tua vida.

Já pesquisaste por string parsing? Tens um exemplo do GET que recebes dum browser? Ou do comando que vais fazer com telnet?

Desculpe-me se fui rude ou faltei com a educação na hora de pedir ajuda, não foi minha intenção!

Sei que nem todos aceitam dinheiro para ajudar com o código, mas eu ofereço mesmo assim pois preciso de uma resposta e ate o momento não a encontrei de forma clara.

bubulindo:
Ok... queres um GET ou telnet?

O que eu quero e comunicar com o Arduino de meu site http://site.com que roda PHP com o Arduino que esta e minha casa e esta respondendo pelo meu IP público, apesar de ser dinâmico, criei uma função para atualizar junto ao No-IP de forma automática.

Porém de inicio pensei em comunicar através do cURL do PHP, usando um HTTP GET, porém a falta de exemplos e informações livres nos fóruns tornou isso impossivel. Agora estou pensando em fazer essa comunicação com sockets no PHP, assim posso mandar uma mensagem ao Arduino em um formato predefinido Texto da mensagem|Texto 2

Usando uma função como a mostrada abaixo, posso dar um split no texto e separar o conteúdo em 2 variáveis de forma fácil.

//   strtok test                                                               */
 
#include <string.h>
 
#define MAX_STRING_LEN  20
 
char *record1 = "one two three";
char *record2 = "Hello there friend";
char *p, *i;
 
void setup() {
 
   Serial.begin(9600);
   
   Serial.println("Split record1: ");
   Serial.println(subStr(record1, " ", 1));
   Serial.println(subStr(record1, " ", 2));
   Serial.println(subStr(record1, " ", 3));
 
   Serial.println("Split record2: ");
   Serial.println(subStr(record2, " ", 1));
   Serial.println(subStr(record2, " ", 2));
   Serial.println(subStr(record2, " ", 3));
}
 
void loop () {
}
 
// Function to return a substring defined by a delimiter at an index
char* subStr (char* str, char *delim, int index) {
   char *act, *sub, *ptr;
   static char copy[MAX_STRING_LEN];
   int i;
 
   // Since strtok consumes the first arg, make a copy
   strcpy(copy, str);
 
   for (i = 1, act = copy; i <= index; i++, act = NULL) {
      //Serial.print(".");
      sub = strtok_r(act, delim, &ptr);
      if (sub == NULL) break;
   }
   return sub;
 
}

bubulindo:
Eu não quero dinheiro. Primeiro porque isto não é o meu emprego e segundo porque não quero responsabilidade sobre o que digo aqui. O facto de não aceitar dinheiro também me dá a possibilidade ed ajudar quando posso...
E não acho prudente que pagues a alguém que diz que resolve o teu problema e não o faz. Mas tu é que sabes da tua vida.

Desculpe-me outra vez se lhe ofendi com o dinheiro que ofereci, só achei justo oferecer alguma forma de pagamento já que disponibilizou tempo e conhecimento em ajudar uma pessoa que não conhece. Isso não vai se repetir outra vez, prezo pelo bom funcionamento da comunidade.

bubulindo:
Já pesquisaste por string parsing? Tens um exemplo do GET que recebes dum browser? Ou do comando que vais fazer com telnet?

Tenho um exemplo que recebe uma String pelo serial no formato /?teste=teste 1&teste2=teste dois e mostra o texto da variável separado exatamente como eu quero. Esse exemplo eu "paguei" para um amigo desenvolver porém não consigo adaptar para que ele responda ao usuários que realizem um GET.

void setup(){
  Serial.begin(9600);
}
void loop(){
 
  String lsData=readData();
 
  if( lsData.equals("") != true ){
    char lsTexto[255];
    lsData.toCharArray(lsTexto, 255);
    Serial.print("Texto recebido:");
    Serial.println(lsTexto);
 
    String lsValor=valorGet("teste",lsData);
    lsValor.toCharArray(lsTexto, 255);
    Serial.print("Valor do parametro teste:");
    Serial.println(lsTexto);
  }
 
}
 
String readData(){
  String lsConteudo="";
  int conta=0;
  while ((conta++)<5) {
    if(Serial.available()){
      char lc=Serial.read();
      lsConteudo=lsConteudo + lc;
      conta=0;
    };
    delay(5);
  }
  return lsConteudo;
}
String valorGet(String parametro, String conteudoHttp){
 
  String lsRetorno="";
  int pos=conteudoHttp.indexOf(parametro);
 
  if(pos > 0){
    char lcCharAnterior=conteudoHttp.charAt(pos-1);
    int tamanhoParametro=pos+parametro.length();
    if( ((lcCharAnterior == '&') || (lcCharAnterior == '?')) &&
      (conteudoHttp.charAt(tamanhoParametro)=='=' ) ){
      int tamanho=conteudoHttp.length();
      int poximoParametro=conteudoHttp.indexOf("&",pos+1);
      if(poximoParametro>0){
        lsRetorno=conteudoHttp.substring(tamanhoParametro+1,poximoParametro);
      }
      else{
        lsRetorno=conteudoHttp.substring(tamanhoParametro+1);
      }
    }
  }
 
  return lsRetorno;
 
}

O que falta nesse código e permitir que o usuário envie o GET pelo navegador e não pela serial. se ele acessar o 192.168.1.177/?teste=teste 1&teste2=teste dois a resposta deveria ser a mesma do terminal.

Toda ajuda que você puder disponibilizar é bem vinda, outra vez desculpe se lhe ofendi com o dinheiro. Sou bastante leigo em ANSI C e também em eletrônica por isso minha dificuldade.

Não foste rude... bem como eu creio não ter sido.
Dá uma olhada neste video... dinheiro não é o principal motivador. RSA ANIMATE: Drive: The surprising truth about what motivates us - YouTube

Acerca das tuas questões. O arduino é baseado num chip relativamente limitado, por isso é preferível manter as coisas (comunicacões, por exemplo) o mais simples possíveis para gerir melhor os recursos do chip. Fazer GETs e POSTs não é ideal porque tens de fazer muito mais parsing do que recebes. Daí que fazer uma ligacão com socket é o melhor que tens a fazer no php.

A última vez que mexi em php já foi há algum tempo, então não te posso ajudar muito nisso... no entanto tens de definir os dados que queres enviar. Vão ser strings? Ou vão ser valores, ou seja, números?

Vamos assumir que vais enviar o valor para passar para a saída PWM dum pino. O comando vai ser PYYY. em que YYY ´€ o valor pwm para passar ao pino.

Ficas então com algo assim:

char pwm[4];
unsigned char pwm_int=0;

if (client.connected()) {
    if (client.available() > 3) { //queres ver 4 caracteres. 
        if (client.read() == 'P') { //isto é importante para poderes mais tarde definir outros comandos.
            pwm[0] = client.read();
            pwm[1] = client.read();           
            pwm[2] = client.read(); //255 ´€ o máximo que podes meter no PWM. 
            pwm[3] = '\0'; //terminador. 
            pwm_int = atoi(pwm);
            analogWrite(pino, PWM);
            Serial.print("pwm is :");
            Serial.println(pwm);
            client.print("OK"); //little AT thingy... 
            while(client.available()>0); //isto é para libertar o que tens dentro do buffer... ou seja, a cada ligacão o php só envia um comando. Isto, pode ser mudado... 
        }//end if P
    }//end if
}//end if

O teu código php tem então de enviar o comando P000 para desligar ou P255 para máxima intensidade e se tudo funcionar, . Isto pode ser melhorado, mas para já serve como prova de conceito. Se isto funcionar, temos uma base para comecar e podes então comecar por definir o teu protocolo... o que queres que o Arduino faca, que valores vais enviar e que valores vais esperar receber do Arduino. Depois define-se as tramas e é só testar e adicionar a este bocado de código.

Nota que o PHP tem de enviar sempre três digitos. Mesmo que seja o numero 0 ou 1 ou 10 ou 23. Deve ser sempre 000 ou 001 ou 010 ou 023. Estás a ver?

Se souberes PHP, e suponho que sabes, isto pode tornar-se bem mais rápido e simples fazendo transferência de números em binário. Ou seja, não terias sequer de converter para string. Ler pelos fóruns retornou isto:

http://kr1.php.net/pack

Hmmm, basicamente é a funcão pack do PHP.

Abraco.

P.S.: A motivacão aqui é ver algo funcionar com sockets no PHP... nunca vi ou utilizei isso. :wink:

Ok o que quero fazer é o seguinte:

Tenho uma Ethernet Shield e uma Sim900 Shield, vou enviar via SMS pela internet. Meu código até o momento esta logo abaixo:

/*
 * Ethernet SMS Server
 * Version 1.0 April, 2013
 * Copyright 2013 Renato Tavares
 * For details, see https://bitbucket.org/renatotavares/ethernet-sms-server
 *
 * Modified by Renato Tavares <dr.renatotavares@gmail.com> to support ethernet shield, GSM shield and multitasking.
 *
 * Complete Web Server to manage sending SMS with a GSM API using Arduino.
 */

// Including the required libraries
#include <Ethernet.h>
#include <SPI.h>
#include <SoftwareSerial.h>


// Defining the constants present in the code
unsigned long UpdateNoIPCycle = 300000;
unsigned long UpdateNoIPLastMillis = 0;

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {
  192, 168, 1, 177};

EthernetServer server(80);

SoftwareSerial mySerial(2, 3);

// Initial system setup

void setup(){
  Serial.begin(9600);
  mySerial.begin(19200);
  Ethernet.begin(mac, ip);
}


// Main body system, the magic happens here
void loop(){
  if(cycleCheck(&UpdateNoIPLastMillis, UpdateNoIPCycle))  {

    Serial.println("Atualizando No-IP..."); 
    UpdateNoIP();

  }
}

// function for multitasking
// http://arduino.cc/forum/index.php/topic,5686.msg44073.html

boolean cycleCheck(unsigned long *UpdateNoIPLastMillis, unsigned long UpdateNoIPCycle) {

  unsigned long currentMillis = millis();

  if(currentMillis - *UpdateNoIPLastMillis >= UpdateNoIPCycle)  {
    *UpdateNoIPLastMillis = currentMillis;
    return true;
  }  
  else{
    return false;
  }
}


// Function to update my IP with the No-IP domain

void UpdateNoIP(){
  EthernetClient client;
  if(client.connect("dynupdate.no-ip.com", 80)){

    Serial.println("Conectando com No-IP");

    client.println("GET /nic/update?hostname=fds.no-ip.biz HTTP/1.0");
    client.println("Host: dynupdate.no-ip.com");
    client.println("Authorization: Basic ZHWOEJ6Q2VpSlA=\r\n");
    client.println("User-Agent: Renato Arduino Client/0.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");
  }
}

//function for sending SMS
// http://elecfreaks.com/wiki/index.php?title=EFCom_GPRS/GSM_Shield
void sendSMS(String msg, String phone){

  mySerial.print("\r");
  delay(1000); 
  mySerial.print("AT+CMGF=1\r"); 
  delay(1000);
  mySerial.print("AT+CMGS=\""+ phone + "\"\r"); 
  delay(1000);
  mySerial.print(msg + " (Enviado via: SMSWeb)\r"); 
  delay(1000);
  mySerial.write(26);

}

Como pode ter notado a função para fazer update no No-IP, função para simular o multitasking e também a função para enviar o SMS estão prontas. Só preciso fazer o arduino escutar as chamadas dos clientes e responder cada uma delas enviando o SMS.

Para ficar melhor vamos tratar que o PHP vai enviar uma string unica em seguinte formato:

Mensagem a ser enviada pelo usuário do site|+556293840000

Ou seja, o formato será mensagem seguida pelo caractere de separação | e em seguida o número do telefone.

PHP seu sei, logo podemos fazer o seguinte:

// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
// connect to server
$result = socket_connect($socket, "192.168.1.177", 80);  
// send string to server

$message = "minha mensagem escrita pelo PHP|+556293844000";

socket_write($socket, $message , strlen($message));
// get server response
$result = socket_read ($socket, 1024);
echo "Reply From Server  :".$result;
// close socket
socket_close($socket);

Isso deve enviar para o Arduino a string no formato definido. Agora o que me falta, e que meu conhecimento é limitado, e pegar esse string no arduino, separar a parte da mensagem do telefone e chamar a função sendSMS. Minha dúvida toda se resume a isso =(

Ahhh, ok.

Então não é preciso ver o pack.

Seria então algo assim:

char mensagem[161]; // Tens de limitar o php a só enviar 160 caracteres. 
char numero[14];
unsigned char pointer = 0; 

if (client.connected()) {
   if(client.available()){ //temos um pedido. 
      //pegar na mensagem.
      for (pointer = 0; pointer < 159; pointer++) {
         mensagem[pointer] = client.read(); 
         if(mensagem[pointer] == '|') break; //já temos a nossa mensagem salta do loop
       } //end for
       mensagem[pointer] = '\0'; //terminador... isto terá de ser verificado mais tarde quando enviares mensagens com menos de 160 caracteres. 
       //pegar no numero agora. 
       for (pointer = 0; pointer <13; pointer ++) { 
          mensagem[pointer] = client.read();
       } 
       message[13] = '\0';
       
       Serial.print("mensagem ");
       Serial.println(mensagem);
       Serial.print("para o numero");
       Serial.println(numero);
       while(client.available()>0) client.read(); //isto é para libertar o que tens dentro do buffer... ou seja, a cada ligacão o php só envia um comando. Isto, pode ser mudado...
   } //if available. 
} //if connected

Algo assim?

Funcionou perfeitamente amigo, agora o problema é que minha Shield gms parou de funcionar. ate fiz esse tópico http://arduino.cc/forum/index.php/topic,161624.0.html

Muito obrigado pela ajuda e paciência.

Só para finalizar, no Brasil alguns estados possuem o telefone com um caractere a mais logo o nosso exemplo anterior de +556293841234 funciona perfeitamente, porém se alguém destes estados tentarem usar o sistema, não vai funcionar, pois eles possuem um número sobrando +5562938412345. Alterei o exemplo para o código abaixo, pensei que se jogar o \0 para o caractere padrão com 13 dígitos, ele enviaria os de 13 e quanto chegar algum com 14 ele iria substituir o \0 pelo caractere do numero e funcionaria em ambos.

Infelizmente essa ideia não funcionou, saberia alguma forma para resolver isso?

char mensagem[161]; // Tens de limitar o php a só enviar 160 caracteres. 
char numero[15];
numero[13] = '\0';
unsigned char pointer = 0; 

if (client.connected()) {
   if(client.available()){ //temos um pedido. 
      //pegar na mensagem.
      for (pointer = 0; pointer < 159; pointer++) {
         mensagem[pointer] = client.read(); 
         if(mensagem[pointer] == '|') break; //já temos a nossa mensagem salta do loop
       } //end for
       mensagem[pointer] = '\0'; //terminador... isto terá de ser verificado mais tarde quando enviares mensagens com menos de 160 caracteres. 
       //pegar no numero agora. 
       for (pointer = 0; pointer <14; pointer ++) { 
          mensagem[pointer] = client.read();
       } 
       message[14] = '\0';
       
       Serial.print("mensagem ");
       Serial.println(mensagem);
       Serial.print("para o numero");
       Serial.println(numero);
       while(client.available()>0) client.read(); //isto é para libertar o que tens dentro do buffer... ou seja, a cada ligacão o php só envia um comando. Isto, pode ser mudado...
   } //if available. 
} //if connected
char mensagem[161]; // Tens de limitar o php a só enviar 160 caracteres. 
char numero[15];
numero[14] = '\0'; //<-----------------------------Mudei aqui... 
numero[13] = '\0'; //<-----------------------------Adicionei aqui...
unsigned char pointer = 0; 

if (client.connected()) {
   if(client.available()){ //temos um pedido. 
      //pegar na mensagem.
      for (pointer = 0; pointer < 159; pointer++) {
         mensagem[pointer] = client.read(); 
         if(mensagem[pointer] == '|') break; //já temos a nossa mensagem salta do loop
       } //end for
       mensagem[pointer] = '\0'; //terminador... isto terá de ser verificado mais tarde quando enviares mensagens com menos de 160 caracteres. 
       //pegar no numero agora. 
       for (pointer = 0; pointer <15; pointer ++) { //<------------------aumentei aqui...  
          mensagem[pointer] = client.read();
          if(mensagem[pointer] == -1) break; //acabaram-se os dados... 
       } 
       message[pointer] = '\0';
       
       Serial.print("mensagem ");
       Serial.println(mensagem);
       Serial.print("para o numero");
       Serial.println(numero);
       while(client.available()>0) client.read(); //isto é para libertar o que tens dentro do buffer... ou seja, a cada ligacão o php só envia um comando. Isto, pode ser mudado...
   } //if available. 
} //if connected

Vê se isto funciona. Apenas mudei a lógica para funcionar até o buffer estar terminado.
Ahh, esqueci-me de dizer... se alguém enviar uma mensagem com uma | no meio, isto não funca e dá erro. :wink:
Seria preferível enviar um sinal ASCII que não seja possível de escrever com o teclado.