String concatenation issue

Hy guys. I'm having an issue with the concatenation of multiple strings.
As you can see in the function bellow, there are two ways I am trying to send through serial a message. The second method where I am printing the string one after the other, it works fine.

void musical_atmosphere(bool counter, char signs){
  int arraySize = 11;
  String musical_atmosphere = String("");
  String array3x2[arraySize];
  bool start_titles = false;
  bool curselect = false;
  bool counter_set = false;
  int rowNr = 1;
  int columnNr = 1;
  int arrayPos = 0;
  int curent_selected = 0;
  bool counter_0x00 = false;
  bool iconBit = false;
  char character;
  while (counter == false){
    if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK){
      if ((canMsg.can_id == 1313 && canMsg.data[0] == 0x74) || arrayPos >= 10 ){
        //Serial transmit the titles
        // String ex: musical_atmosphere : 2 : off : on :   : title_1 : title_2 :   : + : 5 : - : 2 : end_string
        // Highlighted portion 21/1=text box ; 22/2=bass ; 23/3=treble
        /*
        musical_atmosphere = String("musical_atmosphere:") + curent_selected;
        for (int i = 1; i <= 10; i++){
          musical_atmosphere = String(musical_atmosphere) + String(":") + array3x2[i];
        }
        musical_atmosphere = musical_atmosphere + endString;
        lastMessage = musical_atmosphere;
        Serial.println(musical_atmosphere);
        */
        ///////////////////////////////////////////
        /*
        Serial.print("musical_atmosphere:");
        //Highlighted portion 21/1=text box ; 22/2=bass ; 23/3=treble
        Serial.print(curent_selected);
        for (int i = 1; i <= 10; i++){
          Serial.print(":");
          Serial.print(array3x2[i]);
        }
        Serial.println(endString);
        */
        return;
      }
  }
}

The first one, where I tried adding the string to a single one, gives me the following messages

:4:end_string

::::end_string
:+:0:::end_string
:::::end_string
:::::end_string
:::::end_string
:+:4:+:4:end_string
:::::end_string
:::::end_string
:::::end_string
:+:4:+:4:end_string
::::end_string
::::end_string
::::end_string

I know what you will say, the String array does not have any values saved in it, but I checked to see if there are using Serial.print() to show me all the array before concatenating, and all the correct values are there.I tried removing the int variable, still no luck. I tried declaring the musical_atmosphere without any value, with String() and with String(""); . Tried using a variable for the ':' character, tried adding it with char(':'), then with String(":"). Still no luck.
I know it would be easyer to just use the Serial.print() and be done with it, but I have other function that has a lot more strings that are not saved in an array, and it does not look pretty.

Edit: I attached the whole code bellow (beside other functions that does the same thing as the one above)

#include <mcp2515.h>
#include <Keyboard.h>

#if defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_MICRO)       
  #define BOARD "Keyboard"
#endif


struct can_frame canMsg;

MCP2515 mcp2515(10);

int high_box = 0;

String frequency_box;
String function_name;
String endString = ":end_string";

String incomingByte;
String lastSource;
String lastMessage;
String lastVolume;

unsigned long delayTime = 250;
void setup() {
  Serial.begin(250000);
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
  mcp2515.setNormalMode();
}

//Musical atmosphere function
void musical_atmosphere(bool counter, char signs){
  int arraySize = 11;
  String musical_atmosphere = String("");
  String array3x2[arraySize];
  bool start_titles = false;
  bool curselect = false;
  bool counter_set = false;
  int rowNr = 1;
  int columnNr = 1;
  int arrayPos = 0;
  int curent_selected = 0;
  bool counter_0x00 = false;
  bool iconBit = false;
  char character;
  while (counter == false){
    if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK){
      if ((canMsg.can_id == 1313 && canMsg.data[0] == 0x74) || arrayPos >= 10 ){
        //Serial transmit the titles
        // String ex: musical_atmosphere : 2 : off : on :   : title_1 : title_2 :   : + : 5 : - : 2 : end_string
        // Highlighted portion 21/1=text box ; 22/2=bass ; 23/3=treble
        // function name
        ///*
        //musical_atmosphere = String("musical_atmosphere:") + curent_selected;
        musical_atmosphere = String("musical_atmosphere:");
        for (int i = 1; i <= 10; i++){
          musical_atmosphere = String(musical_atmosphere) + String(":") + array3x2[i];
        }
        musical_atmosphere = musical_atmosphere + endString;
        lastMessage = musical_atmosphere;
        Serial.println(musical_atmosphere);
        //*/
        ///////////////////////////////////////////
        /*
        Serial.print("musical_atmosphere:");
        //Highlighted portion 21/1=text box ; 22/2=bass ; 23/3=treble
        Serial.print(curent_selected);
        for (int i = 1; i <= 10; i++){
          Serial.print(":");
          Serial.print(array3x2[i]);
        }
        Serial.println(endString);
        */
        return;
      }

      if (canMsg.can_id == 289 && canMsg.data[0] == 0x22 && curselect == false){
        if (canMsg.data[4] == 0x21) curent_selected = 2;
        if (canMsg.data[4] == 0x22) curent_selected = 3;
        if (canMsg.data[4] == 0x23) curent_selected = 4;
        curselect = true;
      }

      if (canMsg.can_id == 289 && canMsg.data[0] >=0x23){ 
        start_titles = true;
      }

      if (canMsg.can_id == 289 && start_titles == true){

        ////////// Counter for separating the titles //////////

        for (int i=1; i<=7; i++){          
          if (canMsg.data[i] == 0x0D){
            rowNr++;
            columnNr = 1;
          }

          //Finish with the grid, check treble and bass values
          if (canMsg.data[i] == 0x00 && i != 0 && counter_set == false){
            if (counter_0x00 == true){
              arrayPos = 7;
              //Serial.println(arrayPos);
              counter_0x00 = false;
              counter_set = true;
              continue;
            } else counter_0x00 = true;
          } else counter_0x00 = false;
          //Finish with the grid, check treble and bass values

          //Checking where on the 3x2 array whe are to save the string
          if (arrayPos < 7) {
            if (rowNr == 1 && columnNr == 1) arrayPos = 1;
            if (rowNr == 2 && columnNr == 1) arrayPos = 2;
            if (rowNr == 3 && columnNr == 1) arrayPos = 3;
            if (rowNr == 1 && columnNr == 2) arrayPos = 4;
            if (rowNr == 2 && columnNr == 2) arrayPos = 5;
            if (rowNr == 3 && columnNr == 2) arrayPos = 6;
          }
          //Checking where on the 3x2 array whe are to save the string

          if (canMsg.data[i] == 0xF1 && i < 7) {
            array3x2[arrayPos] = icons(canMsg.data[i+1]);
            columnNr++;
            continue;
          }
          if (canMsg.data[i] == 0XF1 && i == 7){
            iconBit = true;
            continue;
          }
          if (iconBit == true && i == 1 && i < 7){
            array3x2[arrayPos] = icons(canMsg.data[i]);
            iconBit = false;
            columnNr++;
            continue;
          }

          if (columnNr == 2 && arrayPos < 7 && canMsg.data[i] != 0x26 && canMsg.data[i] != 0x27){
            if (canMsg.data[i] >= 0x20 && canMsg.data[i] <= 0x7E){
              character = canMsg.data[i];
              array3x2[arrayPos].concat(character);
            }
          }

          

          if (signs == 0x5A && canMsg.data[0] == 0x2B){
            if (canMsg.data[5] <= 0x09) array3x2[7] = "+";
            if (canMsg.data[5] >= 0xF7) array3x2[7] = "-";
            //-------------------------------------------
            if (canMsg.data[6] <= 0x09) array3x2[9] = "+";
            if (canMsg.data[6] >= 0xF7) array3x2[9] = "-";

            if (canMsg.data[5] <= 0x09) array3x2[8] = canMsg.data[5];
            if (canMsg.data[5] >= 0xF7) array3x2[8] = 256 - canMsg.data[5];
            //------------------------------------------------------------
            if (canMsg.data[6] <= 0x09) array3x2[10] = canMsg.data[6];
            if (canMsg.data[6] >= 0xF7) array3x2[10] = 256 - canMsg.data[6];            
          }        
        } 
      }     
    }
  }
}

void loop(){
  String messageRecv ;
  if (Serial.available()){
    Serial.print("enteredSerialBuffer:");
    Serial.print(incomingByte);
    Serial.println(endString);
    Serial.println(lastSource);
    Serial.println(lastVolume);
    char c = Serial.read();
    incomingByte.concat(c);
    if (incomingByte == "reqMsg"){
      Serial.println(lastSource);
      Serial.println(lastVolume);
      Serial.println(lastMessage);
    }
  }
  
  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK){
    if(canMsg.can_id == 289){
      //Volume function
      if (canMsg.data[0] == 0x10 && canMsg.data[2] == 0x35){
        volume_text(false, false);
      }
      else if (canMsg.data[0] == 0x03 && canMsg.data[2] == 0x08){
        volume_text(false, true);
      } 

      //Highlighted box
      else if(canMsg.data[0] == 0x10 && canMsg.data[1] == 0x0E && canMsg.data[2] == 0x20 && canMsg.data[3] == 0x00){
          //In the frame with the first bit=21 the 7'th bit represents the position of the highlighted
          //box. The first box has the value 20, the second 21 and so on.
          //Serial.println("ENTER HIGHLIGHTED BOX");
          high_box = high_box_function(false);
          //Serial.println("EXIT");
          //Serial.println();
      }
                                       
      else if (canMsg.data[0] == 0x10 && canMsg.data[2] == 0x25){
        if ((canMsg.data[3] == 0xC1 || canMsg.data[3] == 0xC3) && canMsg.data[4] == 0x09 ){
          if (canMsg.data[7] == 0x00){
            frequency_box = frequency_type_box(false);
          }
          else gridSort3x2(false); // CHECKED
        }

        else if (canMsg.data[4] == 0x17){
          gridSortRadio(false); // CHECKED
        }

        else if (canMsg.data[4] == 0x13 && canMsg.data[5] == 0x03){
          gridPTY3x2(false, canMsg.data[3]);
        }

        else if (canMsg.data[4] == 0x07 && canMsg.data[6] == 0x20){
          gridSort3x3(false); // CHECKED        
        }

        else if (canMsg.data[3] == 0x70 && canMsg.data[4] == 0x00 && canMsg.data[5] == 0x04 && canMsg.data[6] == 0x40){
          menu_volume(false, function_name);
        }
        
        else if (canMsg.data[3] == 0x73 && canMsg.data[4] == 0x02 && canMsg.data[6] == 0x40){
          musical_atmosphere(false, canMsg.data[1]);
        }
        else if (canMsg.data[3] == 0x63 && canMsg.data[4] == 0x09){
          confirm_cancel_function(false);
        }
        else if (canMsg.data[3] == 0x52 && canMsg.data[4] == 0x09){
          info_box(false);
        }
        else if (canMsg.data[3] == 0x41 && canMsg.data[4] == 0x13 && canMsg.data[5] == 0x01 && canMsg.data[6] == 0x20 && canMsg.data[7] == 0x80){
          sources();
        }
      }                                       
    }

/*
    else if (BOARD == "Keyboard" && canMsg.can_id == 1597){
      keyboardButtons(canMsg.data[0]);   
    }

    else if (BOARD == "Keyboard" && canMsg.can_id == 1598){
      keyboardJoystick(canMsg.data[0], canMsg.data[1]);      
    }
/*
    else if (canMsg.can_id == 1423){
      satelliteButtons(canMsg.data[1] == 0x01, canMsg.data[2]);
    }
*/    

  }
}

Post a complete code that compiles and demonstrates the issue. That way people can test it form themselves.

can you explain what you code is trying to print?

It gets CAN frames from a multimedia module from a car, and converts the bytes received in a message, used to provide information on a display. I use a MCP2515 module for CAN data aquisiton, and then send the String to an Android device.

so what is the format of the data you receive and what is the format of the output?

i get the bytes in decimal format, I concatenate byt by byt in a string, then send the String through Serial.

so you received 123 and you output "123"?
does "send the String through Serial." describe a format or method of transmission?

by decimal so you mean binary?
am i going to have to ask 20 questions?

no, I get DEC 68, then convert it to character 'D' and output D.
"send the String through Serial." is just the method if transmision.
by decimal i mean DEC as in base 10

when you say DEC 68, do you mean an ASCII character string "DEC 68"?

no, I mean the decimal value 68, which is character 'D' or HEX 44, or OCT 104

that's better, but this rate, it is going to take a while understand what you are receiving and what format it is

and then there's what to output and the format of the output

apparently you're receiving data in data structure, canMsg. you code doesn't show it's format. i don't see where you use values from it to generate output.

i don't see where "current_selected" comes from

Works fine for me:

String endString = ":end_string";

//Musical atmosphere function
void musical_atmosphere()
{
  int arraySize = 11;
  String musical_atmosphere;
  String array3x2[arraySize] =
  {
    "Zero", "One", "Two", "Three", "Four", "Five",
    "Six", "Seven", "Eight", "Nine", "Ten"
  };

  musical_atmosphere = "musical_atmosphere:";
  for (int i = 1; i < arraySize; i++)
  {
    musical_atmosphere += ":";
    musical_atmosphere += array3x2[i];
  }
  musical_atmosphere += endString;

  Serial.println(musical_atmosphere);
}

void setup()
{
  Serial.begin(115200);
  musical_atmosphere();
}

void loop()
{
}

Output:
musical_atmosphere::one:Two:Three:Four:Five:Six:Seven:Eight:Nine:Ten:end_string

Looks like the problem is in filling the array of strings.

I saw in a forum that using the += is better than the way i did it for string concatenation. Haven't understood why, but haven't tryed it yet.
As you can see, I use musical_atmosphere = musical_atmosphere + ":";

musical_atmosphere += ":";
  musical_atmosphere += array3x2[i];

but I don't understant, if I Serial.print the String array individually, it outputs the correct message, why would it be different when I try to concatenate them?

when using the following way to send the message:

Serial.print("musical_atmosphere:");
        //Highlighted portion 21/1=text box ; 22/2=bass ; 23/3=treble
        Serial.print(curent_selected);
        for (int i = 1; i <= 10; i++){
          Serial.print(":");
          Serial.print(array3x2[i]);
        }
        Serial.println(endString);

I get this

musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Voice":"Classical":"Jazz":::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Classical":"Jazz":"Neutral":+:0:::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Jazz":"Neutral":"Pop":::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Neutral":"Pop":"Rock":::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_full:"Pop":"Rock":Bass/treble:::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_full:icon_Cercle_empty:"Rock":Bass/treble:"Voice":+:4:+:4:end_string
musical_atmosphere:2:icon_Cercle_full:icon_Cercle_empty:icon_Cercle_empty:Bass/treble:"Voice":"Classical":::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Voice":"Classical":"Jazz":::::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Classical":"Jazz":"Neutral":+:0:::end_string
musical_atmosphere:2:icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Jazz":"Neutral":"Pop":::::end_string

which is correct

Edit: I've added a debug log, to show the content of the array:

musical_atmosphere = String("musical_atmosphere:") + curent_selected;
        Serial.print("DEBUG PRINT =>");
        for (int i = 1; i <= 10; i++){
          musical_atmosphere = String(musical_atmosphere) + String(":") + array3x2[i];
          Serial.print(array3x2[i]);
          Serial.print(":");
        }
        Serial.println();
        musical_atmosphere = musical_atmosphere + endString;
        lastMessage = musical_atmosphere;
        Serial.println(musical_atmosphere);
DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Voice":"Classical":"Jazz":::::
:"Jazz":::::end_string
DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Classical":"Jazz":"Neutral":+:0:::
:0:::end_string
DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Jazz":"Neutral":"Pop":::::

DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_empty:"Neutral":"Pop":"Rock":::::
:::::end_string
DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_empty:icon_Cercle_full:"Pop":"Rock":Bass/treble:::::
:::::end_string
DEBUG PRINT =>icon_Cercle_empty:icon_Cercle_full:icon_Cercle_empty:"Rock":Bass/treble:"Voice":+:4:+:4:
:+:4:end_string
DEBUG PRINT =>icon_Cercle_full:icon_Cercle_empty:icon_Cercle_empty:Bass/treble:"Voice":"Classical":::::
::::end_string

The += does not work.

You probably have not enough memory to use the String concatenation.
Pre allocating a bigger buffer for the target String could help,
using some fancy "make Strings safe" library could help also, if there is enough space for it.

I see no merit in concatenating the string to print it, do you really need it in one piece?

The safest way would to drop Strings and use cstring char arrays for text manipulation.

2 Likes

In what way?

Not a very 'safe' alternative to Strings. Prone to coder errors, buffer overflows, off by one indexing problems, null pointers and missing \0 terminators.

See my tutorial on Taming Arduino Strings
If you use a statement like
str = String('a') + String(5) + ...
you use double the memory (temporarily) as the right hand side of the = is built before assigning to the str result.
on the other hand
statements like

str = 'a';
str += 5; // etc

just add the data to the existing String without allocating any extra temporary memory.
BUT as a previous poster @Whandall pointed out why not just use multiple prints when you can
i.e.

Serial.print('a');
Serial.print(5);  // etc

p.s. I think the "fancy "make Strings safe" library" @Whandall is referring to is my SafeString library.

But is Strings work for you no need to change.
For reading Strings from Serial check out the various example sketches in Arduino Software Solutions with their pros and cons