Jump to content
43oh

Basic Serial Program Printing Inconsistent Data


Recommended Posts

I'm lovin' the ease of the Energia environment for rapid prototyping but I've run across an issue with some code I'm trying to implement. Here it is with the serial output

 

Program:

/*
**  Simple program designed to simulate pushing 180 degrees of 10 meter scan times
**  for testing of VB.NET GUI
**
*/

String inputString = "";
boolean run = false;
String outString = "";
int i = 0;

void setup() {

  Serial.begin(9600);
  inputString.reserve(200);
  outString.reserve(200);
}

void loop() {
  if (run) {
    for (i = 0; i < 179; i++) //Print 10 meter ping times for each scan angle
    {
      outString += i;
      outString += String("$932944#932944#932944#"); //Test data to simulate 10 meter distance
      
      Serial.println(outString);
      outString = "";
    }
  }
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read(); 
    if (inChar == '\n' && inputString == "START")
    {
      run = true;
      inputString = "";
    } 
    else if (inChar == '\n' && inputString == "STOP")
    {
      run = false;
      inputString = "";
    }
    else if (inChar == '\n')
    {
      Serial.println("Please use valid command: START | STOP");
      inputString = "";
    }
    else
    {
    inputString += inChar;
    }
  }
}

 

 

 

Data Received From MSP430:

0$932944#932944#932944#
1$932944#932944#932944#
2$932944#932944#932944#
3$932944#932944#932944#
4$932944#932944#932944#
5$932944#932944#932944#
6$932944#932944#932944#
7$932944#932944#932944#
8$932944#932944#932944#
9$932944#932944#932944#
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|| It then continues on to 179 ||

 

Why on earth does it stop printing the second string?

Oh also: M430G2553 , onboard clock and hardware UART

 

EDIT: Is the .reserve(bytes) necessary for the strings?

Link to post
Share on other sites

Hi - as a newbie myself, I tried your code.

 

As RR84 suggested buffer sizes, I experimented, as total 400 bytes (in+out each 200) seemed a lot for the 512 byte RAM on the 2553. And I don't know what Energia needs for its own buffers in addition to that 400.  I noticed that the original code crashed after 10 lines in, and at about 21 characters per line, that gets over the 200 byte limit of the output buffer - and where is THAT in memory?

 

So, I changed the INPUT buffer length to 20, down from 200. And it works.

I don't think the code is busting the buffer due to concatenation, though. It could be just too little RAM in the chip for the compilation and Enegia doesn't let us know about it.  Just my own guess.

 

>> So, here is your code, it delivers all 180 samples to the serial port. And it stops cycling after each 180 if your type STOP.

 

I put in some debug lines to check out things.   I found out that \n checking is actuially checking foir LINEFEED (ctrl-J), not CR as I guessed.  I like the serial event command but have no idea where you found out about it !

 

 ************************************************************************************************************

 /*
**  Simple program designed to simulate pushing 180 degrees of 10 meter scan times
**  for testing of VB.NET GUI
**
*/

String inputString = "";
boolean run = false;
String outString = "";
int i = 0;

void setup() {

  Serial.begin(9600);
  inputString.reserve(20);  //DEBUG       <<<<<<<<<<<<<<<<<<<<<<<<<<<<<  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  outString.reserve(200);
}

void loop() {
  if (run) {
   
   
   
    for (i = 0; i < 179; i++) //Print 10 meter ping times for each scan angle
    {
      outString += i;
      outString += String("$932944#932944#932944#"); //Test data to simulate 10 meter distance
     
      Serial.println(outString);
      outString = "";
      delay (50);                                                            // debug, slow it down ********************************
    }
  }
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
  
    Serial.print(inChar);    // echo DEBUG   **************************************************
   
    if (inChar == '\n' && inputString == "START")
    {
      run = true;
      Serial.println("run flag true");     //   ***************************************
      inputString = "";
    }
    else if (inChar == '\n' && inputString == "STOP")
    {
      run = false;
      Serial.println("run flag flase");     //   *************************************
      inputString = "";
    }
    else if (inChar == '\n')
    {
      Serial.println("Please use valid command: START | STOP");
      inputString = "";
    }
    else
    {
    inputString += inChar;
    Serial.print("\t");            //DEBUG ************************
    Serial.println(inputString);   // echo up till now DEBUG  ************************

    }
  }
}
 

Link to post
Share on other sites

To know the RAM used,

  • find the location of the size utility
/Applications/Energia.app/Contents/Resources/Java/hardware/tools/msp430/bin/msp430-size
  • compile a sketch and find the location of the elf file
/var/folders/kd/70m4ltxn14l086ddy860ff3c0000gn/T/build7439050874009286986.tmp/Blink.cpp.elf
  • open a terminal window and type the command
$/Applications/Energia.app/Contents/Resources/Java/hardware/tools/msp430/bin/msp430-size /var/folders/kd/70m4ltxn14l086ddy860ff3c0000gn/T/build7439050874009286986.tmp/Blink.cpp.elf
   text	   data	    bss	    dec	    hex	filename
    590	      0	      6	    596	    254	/var/folders/kd/70m4ltxn14l086ddy860ff3c0000gn/T/build7439050874009286986.tmp/Blink.cpp.elf
  • do the maths:
text + data = FLASH
bss + data = SRAM
Link to post
Share on other sites

That would only be true for compile time and stack memory. The String class on the other hand uses free space (a.k.a. heap) memory, which isn't allocated until run time. Your RAM usage thus does not show up in these kind of compile reports.

Additionally, you call Serial.println(outString); which requires the String class to either return a raw pointer to the internal character data (unsafe) or return a copy of the string as a character sequence. This is due to the limitations of println, which (probably) only accepts char*, not String. As a result there are currently four buffers, your incoming and outgoing String buffers and the Serial's incoming and outgoing serial buffers! Plus maybe a temporary copy of the data in the println() call!

I found that println() will buffer the data internally as well. If you want to make sure you don't run out of memory in the Serial object, after calling Serial.println() call Serial.flush().

Link to post
Share on other sites

Brought my inString and outString down to 20 bytes each and the program is running as expected. Awesome. :) It's now working perfectly with my .NET program. A RAM overflow would make sense with what I was seeing in previous testing (printing wonderful garbage) what might be a better way to optimize this process? Especially assuming the part after the angle index (i) will not just be a constant string but a set of 3 calculated longs concatenated together with the '#' delimiter.

 

Psuedo Code (I'm not the one actually writing this code for our project):

While(run) - flag set by serial command
{
	for each angle in 180 degrees
	{
		concatenate angle+'$' to outString
		
		{(3 times)
			Pulse Tx
			count clock cycles (or some multiple thereof - or possibly use the micros command?)
			Rx event stops the count - code moves forward
			concatenate ping+'#' to outString
		}
		Serial.println(outString);
		outString = "";
	}
}

 

Thanks for the rapid responses - Good community we have here :)

Link to post
Share on other sites

I'd suggest setting the size to at least 25, as that's the amount of data you try to store in it! That it is working while you don't have that much space available means your String object we create a copy of twice the size (40 characters) and then deallocate the old one. This is very inefficient, try to (if you stick to String usage) reserve at least the maximum string size + 1 when calling upon reserve().

Also, leave the = ""; part out in the declaration of the string objects.

 

Instead of appending the angle to your string (which then should be empty) and emptying the string at the end of the iteration, just assign the angle to the string.

 

void loop() {
  if (run) {
    for (int i = 0; i < 180; ++i) //Print 10 meter ping times for each scan angle
    {
      unsigned long A, B, C;
      outString = to_string(i) + "$" + to_string(A) + "#" + to_string( + "#" + to_string(C) + "#";
      Serial.println(outString);
    }
  }
}

Something like this.

 

Or if you wish to stop the measurement in between every angle

void loop() {
  static int i = 0;
  if (run) {
    ++i;
    if (i == 180) i = 0;
    unsigned long A, B, C;
    outString = to_string(i) + "$" + to_string(A) + "#" + to_string( + "#" + to_string(C) + "#";
    Serial.println(outString);
  }
}
Link to post
Share on other sites

Thanks roadrunner. I had the strings implemented the way I did because I didn't know the proper syntax for string handling in Energia (Arduino). I've done most of my work previously in Assembly but now I know.

 

I do have a followup question though - What would be the most efficient/accurate way to measure the amount of time between an action performed from an output pin and a subsequent interrupt? Ie. Pulse an output pin then measure the time until an event happens on the Rx pin.

 

Also, how could I implement a single shot pulse on a pin without wasting clock cycles with delays?

 

A little background on the project: 40kHz 555 driving a piezo device. Receiving piezo feeds into amp stages and an envelope detector. MSP430 triggers 555 then waits for envelope detector to drop low measuring the travel time of the 40kHz wave and drives a servo to scan the room. MSP430 then sends three raw travel times for each angle to the .NET GUI to be calculated to actual distances and displayed on a radar style graph.

 

Unrelated: How did you get code to have proper colors in your posts?

Link to post
Share on other sites

Ah! You're an assembly guy! Then you might go for a more C-like approach than the C++ like approach you're currently taking. I'm not sure that it will actually make your code smaller, but I figure that since you don't need dynamic memory your RAM usage will decrease.

 

That's tough, it's virtually impossible to measure individual clock cycles, unless you dedictate a timer to count them. Then still you're bound to be off in some way, since the read action alone takes 3 cycles. I think the easiest way is to ensure that you use both the rising edge of the output and the active edge on the input should be parsed by the same routing in which you avoid any conditional logic (like if statements). This way both the start and the stop of your interval will have the same latency and thus your interval will be equal in the physical realm as in your software.

 

Implementing a single shot can be done in several ways. First, you could output a string of bits over SPI ending in a zero bit, then since you don't reload the SPI register with another value it will stay idle and thus zero.

A second option would be to set a pin to timer output and set the timer OUT bit to high, then set up that CCR register to use the Reset mode. The pin will stay high until the CCR matches the timer value, then the pin will go low and stay low until you set it high again.

 

So you're making a sonar? :smile: Nice. Since you're using the single shot to drive your 555, the width of the shot isn't that important I figure. You could also do other things after setting the single shot high and after you're done set it low again. You're basically interleaving code this way, something I often have to do in time critical stuff.

 

I click the < > symbol above the edit window, then a popup occures that lets me paste code. Either let it use auto-detect as language, or Javascript, since that's quite similar to C in coding style.

 

Oh, yes, the C version of compiling a string for sending over serial

void loop() {
  if (run) {
    for (int i = 0; i < 180; ++i) //Print 10 meter ping times for each scan angle
    {
      unsigned long A, B, C;
      char outString[40];
      sprintf(outString, "%u$%lu#%lu#%lu#", i, A, B, C);
      Serial.println(outString);
    }
  }
}

sprintf uises the printf formatting to write a to a character array (the C version of a string; much dumber). The %u means to insert a unsgined integer here, %lu (that's a lower case L) means to insert a unsigned long integer here. After the formatting string the variables to replace the formatting codes are named in the same order.

Take into account that the size of a char[] must always be at least one character longer than the longest text you will store in it! A char[] will never resize and will contain a single '\0' to denote the end of the string, you barely ever actively use this trait, but you must constantly be aware that you are required to allocate that extra character in the declaration of your string to store it.

In this case the length of your string should be the largest size of a long (10 characters) times 3, plus the size of your iterator (3 characters when <180, 5 characters worst case) plus the size of the $, the three # and the '\0' summing up to 3*10+3+5 is 38, let's round up to 40.

Link to post
Share on other sites

Well my previous microcontroller usage has been Assembly - but the majority of my coding experience is in C++, C# and VB.NET.  I've always hated char arrays haha. They're so much trouble - I'm quite bad at static memory allocation. As for the pulsing of the Tx signal I think our group has opted to use a monostable 555 to form the pulse in hardware and just turn the Tx on and off before and after our counting loop. Then we'll just have to add the delays from it into the correction factors, but the delay should be on the order of microseconds - So it shouldn't be too big of a deal. We're also going to add some calibration commands to the MSP and the GUI to let the program adjust for enviromental changes, but we're not going to worry about that now - Got to get all of the components running together smoothly then add the bells and whistles.

 

Here's the code actually working for us (Right now it's simulating Rx/Tx on P2 and LED1:

boolean run = false;
volatile boolean counting = true;
long distance = 0;
long temp = 0;
volatile int i = 0;
int j = 0;
String outString = "";
String inputString = "";

const int Tx = P1_0;//P1_4;
const int Rx = P1_3;//P1_5;

void setup()
{
	Serial.begin(9600);
	inputString.reserve(25);
	outString.reserve(25);
	attachInterrupt(Rx, pingRec, FALLING);
	pinMode(Tx, OUTPUT);
	pinMode(Rx, INPUT_PULLUP);
}

void loop()
{
  digitalWrite(Tx, LOW);
	if (run)
	{
		if(i == 180)
		{i = 0; run = false;}
		else 
		{
			outString = String(i);
                        outString += "$";
			for (j = 0; j < 3; ++j)
			{
				digitalWrite(Tx, HIGH);
				temp = micros();
                        	while(counting)
				{
					//Wait for interrupt
				}
				distance = micros() - temp;
				digitalWrite(Tx, LOW);
				outString += String(distance);
				outString += "#";
				counting = true;
			}
		Serial.println(outString);
                i++;
		}
	}
}

void pingRec()
{
	counting = false;
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read(); 
    if (inChar == '\n' && inputString == "START")
    {
      run = true;
      inputString = "";
    } 
    else if (inChar == '\n' && inputString == "STOP")
    {
      run = false;
      i = 0;
      inputString = "";
    }
    else if(inChar == '\n' && inputString == "PAUSE")
    {
      run = false;
      inputString = "";
    }
    else if (inChar == '\n')
    {
      //Serial.println("Please use valid command: START | STOP");
      inputString = "";
    }
    else
    {
    inputString += inChar;
    }
  }
}

Do you think that the micros was the best way to go with this? I'm still unfamiliar with the Energia/Arduino command set.

Link to post
Share on other sites

I guess it's okay. I like to be in control, and Energia takes some of that control away, but since you give control away to the memory manager I guess it doesn't really matter. Just try to make sure you reserve() the amount of space you need. If you don't, String will start juggling with memory initially.

 

Does outString = String(i); work? Because there is no copy constructor in String to do this. I suggest you replace it with outString = to_string(i); Never mind, I thought you were using C++ strings, but you're using Arduino strings, which do have a copy contructor to load them with an integer.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...