Page 1 of 1

Unable to properly concatenate strings

Posted: 03 Mar 2013, 04:42
by haydenstudios
I am currently having a problem with NXC where I am trying to write a string on the NXT. I have the user select which character he/she wants, enter it, and then it adds the current character onto the current string. The problem I am having is that for some reason, the program is not correctly concatenating the selected character and the current string. If you would like to help me debug my program, then here is the source code:

Code: Select all

//The key points of this program to take note of are lines 10, 12, 70, and 99

bool capsLock = 1, stopLoop = 0;
byte fileHandle;
int fSize, p1score = 365;
int characterPos = 0; //Which character is currently selected
int ASCIIPos; //The raw ASCII value of the character that will be added
int character, allowedCharacters;
string scores;
string current = ""; //*What has been typed so far
string name;
string currentCharacter = "A"; //*The character that is to be added

task main()
{
  SetSensor(IN_1, SENSOR_TOUCH);
  SetSensor(IN_2, SENSOR_ROTATION);    //Initialize an NXT touch sensor and an RCX rotation sensor
  allowedCharacters = 10; //This executes insead of the four lines below for the purpose of letting you guys debug this
  //if(p1score >= 600)
    //allowedCharacters = 9;
  //else
    //allowedCharacters = 10;
  //OpenFileRead("HighScores.txt", fSize, fileHandle);  //Don't worry about this and the three lines below it
  //ReadLnString(fileHandle, scores);
  //CloseFile(fileHandle);
  //p1score = StrToNum(SubStr(scores, Pos("/", scores) + 1, Pos("\\", scores) - Pos("/", scores) - 1));
  TextOut(3, LCD_LINE1, "Enter your name:");
  TextOut(0, LCD_LINE3, "Caps Lock is ON");
  TextOut(0, LCD_LINE4, "Next: A");
  TextOut(0, LCD_LINE6, "_");
  TextOut(9, LCD_LINE8, "Chars left: " + NumToStr(allowedCharacters - 1));
  PlayTone(610,140);
  Wait(220);
  PlayTone(511,100);
  Wait(180);
  PlayTone(610,140);
  Wait(140);
  while(true)
  {
    PlayTone(610,140);    //Just an indicator for debugging purposes
    Wait(140);
    stopLoop = 0;
    while(stopLoop == 0)
    {
      if(characterPos >= 0 && characterPos <= 25)   //If the option the user is about to select is a letter of the alphabet...
      {
        if(capsLock)  //If Caps Lock is on...
          ASCIIPos = characterPos + 65;   //Make the letter uppercase
        else
          ASCIIPos = characterPos + 97;   //Make the letter lowercase
        currentCharacter = FlattenVar(ASCIIPos);          //Make the current character the flattened raw ASCII value of the character position
      }
      if(characterPos == 26)      //If we want to press space...
        currentCharacter = " ";   //Make the next character a space
      TextOut(3, LCD_LINE1, "Enter your name:");
      if(capsLock)                                //If Caps Lock is on...
        TextOut(0, LCD_LINE3, "Caps Lock is ON"); //Say that Caps Lock is on
      else                                        //If Caps Lock is NOT on...
        TextOut(0, LCD_LINE3, "Caps Lock is OFF");//Say that Caps Lock is off
      if(characterPos >= 0 && characterPos <= 25) //If the character position is between 0 and 25...
        TextOut(0, LCD_LINE4, "Next: " + currentCharacter);  //Then make the next character a letter of the alphabet
      if(characterPos == 26)                      //If the character position is 26...
        TextOut(0, LCD_LINE4, "Next: SPACE");     //Then make the next character a space
      if(characterPos == 27)                      //If the character position is 27...
        TextOut(0, LCD_LINE4, "Next: BACKSPACE"); //Then make the next character a backspace
      if(characterPos == 28)                      //If the character position is 28...
        TextOut(0, LCD_LINE4, "Next: CAPS LOCK"); //Then toggle Caps Lock
      if(characterPos == 29)                      //If the character position is 29...
        TextOut(0, LCD_LINE4, "Next: DONE");      //Then submit
      TextOut(0, LCD_LINE6, current + "_");       //*Show what has been typed so far
      TextOut(9, LCD_LINE8, "Chars left: " + NumToStr(allowedCharacters - 1));     //Show how many characters are left (not finished with this line of code yet; what is displayed is only temporary)
      Wait(30); ClearScreen();
      if(SENSOR_2 < -8)            //If the rotation sensor gets moved to the left...
      {
        ClearSensor(IN_2);         //Reset the rotation sensor,
        if(characterPos == 0)
          characterPos = 29;       //and set the character position to 29 if it's 0
        else
          characterPos--;          //and decrement the character position if it's not equal to 0
      }
      if(SENSOR_2 > 8)             //If the rotation sensor gets moved to the right...
      {
        ClearSensor(IN_2);         //Reset the rotation sensor,
        if(characterPos == 29)
          characterPos = 0;        //and set the character position to 0 if it's 29,
        else
          characterPos++;          //and increment the character position if it's not equal to 29
      }                            //Essentially, the above 15 lines of code keep the character position between 0 and 29
      if(SENSOR_1 == 1)
      {
        until(SENSOR_1 == 0);
        stopLoop = 1;              //End the loop when the touch sensor has been pressed and released
      }
    }
    //Once the loop has ended...
    if(characterPos == 28)    //If the user has selected the Caps Lock option...
      capsLock = !capsLock;   //Then toggle Caps Lock
    if(characterPos >= 0 && characterPos <= 26)     //If the user's selection is a letter of the alphabet, or a space, then...
      current = StrCat(current, currentCharacter); //*This SHOULD add the selected character onto what you're typing, correct? It doesn't for me. I've also tried removing the if, and it still didn't work.
      //current = current + currentCharacter;       //Tried this as an alternative, and it didn't work
     //name = StrCat(current, currentCharacter);
    //current = name;                            //Also tried it this way just in case it didn't like string variables on both sides, and it still didn't work
  }
}
To test this, you will need to have a touch sensor connected to port 1, and either an RCX rotation sensor connected to port 2, or you will have to modify this program yourself to accommodate an NXT motor if you don't have the former.
What you're supposed to do is move the rotation sensor until you have selected the character/typing option you want, and then press and release the touch sensor. It should then add the selected character or perform the indicated typing operation. Note that the actions for the backspace and done functions have not been implemented yet.

I find it strange that it didn't work, because this quick test worked just fine for me:

Code: Select all

string current = "", character = "A";

task main()
{
  while(true)
  {
    repeat(16)
    {
      TextOut(0, LCD_LINE4, current);
      Wait(1000); ClearScreen();
      current = current + character;
    }
    current = "";
  }
}
Thanks in advance to anyone who can help.

Re: Unable to properly concatenate strings

Posted: 03 Mar 2013, 23:17
by afanofosc
The issue here is that FlattenVar does not technically produce a string, exactly. Rather, it produces a buffer containing the flattened information with an additional 0 added to the end as one more byte. In many cases this can be treated like a string but not always. If you flatten a byte, for example, then the result will be a single byte containing the flattened byte plus a single byte containing 0, i.e., 2 bytes in an array with the second one being null). If the value in the byte was an ASCII value for a printable character then the result of FlattenVar is a string that can be concatenated without problems. If the value being flattened is an integer then the result will be 2 bytes plus 1 null byte. The two bytes (an int is 2 bytes long) will be in little endian order. The result will sort of be a string but it will have 2 nulls in it (the high byte of the int probably being zero followed by the null added by FlattenVar). The extra null is not handled well by the firmware in its string concatenation opcode (which today I am looking into fixing in the enhanced firmware).

Bottom line: change your variable to byte and it will work correctly.

The following code will not compile with the #include statements. This is a portent, shall we say, of things to come. For now, comment them out.

Code: Select all

#include "display.h"
#include "command.h"
#include "cstring.h"
#include "cstdlib.h"

string current = "", character = "A";

task main()
{
  while(true)
  {
    repeat(16)
    {
      // change to byte and re-run this code
      int asciiVal = 32 + Random(95);
      character = FlattenVar(asciiVal);
      NumOut(0, LCD_LINE1, ArrayLen(character));
      for (int i=0; i < ArrayLen(character); i++)
        NumOut(0, LCD_LINE2-8*i, character[i]);
      TextOut(0, LCD_LINE8, current);
      Wait(SEC_1); ClearScreen();
      current = current + character;
    }
    current = "";
  }
}
Later today I will upload a new enhanced firmware test release which should "fix" the string opcodes so that they handle multiple nulls in strings more like how real C handles them.

John Hansen

Re: Unable to properly concatenate strings

Posted: 01 Apr 2013, 22:59
by haydenstudios
Sorry for the late reply, been real busy with school work and only just got enough free time to try this out. I know this is a bit of a bump, but I don't want this thread to come across as useless. After following your instructions, it's now working. Thanks for you help, John!