Strings are bad right?! How can I make this better?

Howdy,

First post, please don't burn me down in flames ;-)

What I'm doing:

I'm posting a series of values via HTTP POST using an Ethernet shield on an Arduino Uno.

What I'm struggling with

Everywhere I look, everyone is saying Strings are "bad" and yea they eat a lot of the storage space (as I found out recently, arrrrhhhhh!!!!)

So my question is....

For the code below, how can I decrease it's size footprint of the "data" I want to send and still be able to send it in the HTTP POST code that follows? (if that's even possible? chars??)

The code

  // Let's try posting data to the Pi
  String data;
  //data = "test1=abc&test2=qwerty";
  data = "1=" + String("1");
  //data += "&2=" + bool_cast(pir_1_state);
  data += "&2=" + String( ((pir_1_state) ? "1" : "0") ); // Bool's
  data += "&3=" + String( ((pir_2_state) ? "1" : "0") );
  data += "&4=" + String( ((alarmStatus) ? "1" : "0") );
  data += "&5=" + String( ((alarmActive) ? "1" : "0") );
  data += "&6=" + String(zone); // Int

  if (ethClient.connect("192.168.0.2",80)) { // REPLACE WITH YOUR SERVER ADDRESS
    ethClient.println("POST /post.php HTTP/1.1"); 
    ethClient.println("Host: 192.168.0.2"); // SERVER ADDRESS HERE TOO
    ethClient.println("Content-Type: application/x-www-form-urlencoded"); 
    ethClient.print("Content-Length: "); 
    ethClient.println(data.length()); 
    ethClient.println(); 
    ethClient.print(data); 
  } 

  if (ethClient.connected()) { 
    ethClient.stop();  // DISCONNECT FROM THE SERVER
  }

Matt

Here’s one simple way of making things better

    ethClient.println(F("POST /post.php HTTP/1.1"));
    ethClient.println(F("Host: 192.168.0.2")); // SERVER ADDRESS HERE TOO
    ethClient.println(F("Content-Type: application/x-www-form-urlencoded"));
    ethClient.print(F("Content-Length: "));

Then have a look and see if “sprintf_P” can help you.

moggiex: So my question is....

For the code below, how can I decrease it's size footprint of the "data" I want to send and still be able to send it in the HTTP POST code that follows? (if that's even possible? chars??)

I'd suggest a dual-use function that can do one of two things, depending on a parameter: - count and return only the length of the output string - or actually print the generated output to a print device

Suggested function code inspired by your code snippet:

byte pir_1_state=0, pir_2_state=1, alarmStatus=1, alarmActive=1, zone=5;

int countOrPrint(Print* myOutputDevice)
{
  char buf[41];
  snprintf_P(buf,sizeof(buf), PSTR("1=1&2=%d&3=%d&4=%d&5=%d&6=%d"), pir_1_state, pir_2_state, alarmStatus, alarmActive, zone);
  if (myOutputDevice!=NULL) myOutputDevice->print(buf);
  return strlen(buf);
}


void setup() {
  Serial.begin(9600);
  Serial.print("Length is: ");
  Serial.println(countOrPrint(NULL));
  Serial.println("And now print the contents:");
  countOrPrint(&Serial);
  Serial.println();
}

void loop() {
}

In case you just want to count the length of the output string, but actually don't print it, provide a NULL character to the function, like perhaps

int codelen= countOrPrint(NULL); // this will only return the length of the string

And in case you want to generate and actually print the string, provide a pointer to your Print device as a function parameter, like perhaps:

  countOrPrint(&Serial); // this will print the string on 'Serial'
or
  countOrPrint(ðClient); // this will print the string on 'ethClient'

Howdy

@AWOL, thanks I missed that

@jurs, I'll be honest, you've completely lost me there as my understanding of C is "basic at best", but I'll give that a whirl and see how many times I can break it :D

And of course, thanks to both of you!

Matt

You can replace

  // Let's try posting data to the Pi
  String data;
  //data = "test1=abc&test2=qwerty";
  data = "1=" + String("1");
  //data += "&2=" + bool_cast(pir_1_state);
  data += "&2=" + String( ((pir_1_state) ? "1" : "0") ); // Bool's
  data += "&3=" + String( ((pir_2_state) ? "1" : "0") );
  data += "&4=" + String( ((alarmStatus) ? "1" : "0") );
  data += "&5=" + String( ((alarmActive) ? "1" : "0") );
  data += "&6=" + String(zone); // Int

by

  // 31 characters max and space for a terminating null character
  char data2send[32];
  // create data to send
  snprintf(data2send, sizeof(data2send), "1=1&2=%s&3=%s&4=%s&5=%s&6=%d",
           (pir_1_state) ? "1" : "0",
           (pir_2_state) ? "1" : "0",
           (alarmStatus) ? "1" : "0",
           (alarmActive) ? "1" : "0",
           zone);

moggiex: @jurs, I'll be honest, you've completely lost me there as my understanding of C is "basic at best", but I'll give that a whirl and see how many times I can break it :D

Any problem in understanding?

My code is using a local char array for holding the data: char buf[41]; Those 41 bytes will only be used while the function is executing, and those 41 bytes of RAM will be available to your sketch for all other time while the sketch is running.

Formatting is done using the snprintf_P() function, which takes a PROGMEM formatting string as parameter, so the formatting string itself will need no RAM, only some bytes of flash memory.

And after the string is formatted, the function parameter will decide, what to do with the formatted data in the 'buf' array: If parameter is NULL, the function just return the length of the formatted string and returns (while the RAM used for the string is totally released). And if the parameter is not NULL, it will be taken as a Print device and print the formatted string before returning from the function.

So if you program needs "length of a formatted string" and "the formatted string itself" at different places in your code, you never need any RAM to store the formatted string between needing the length only and needing the string actually. That way the RAM usage is minimized at runtime.