waynewec 0 Posted April 10, 2013 Share Posted April 10, 2013 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? Quote Link to post Share on other sites
roadrunner84 466 Posted April 10, 2013 Share Posted April 10, 2013 You're running out of memory. Since you concatenate the test string and the iterator number to the string each iteration through the for loop, your string is bound to overflow your RAM capacity at some point. waynewec 1 Quote Link to post Share on other sites
veryalive 49 Posted April 10, 2013 Share Posted April 10, 2013 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 ************************ } }} waynewec 1 Quote Link to post Share on other sites
Rei Vilo 695 Posted April 10, 2013 Share Posted April 10, 2013 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 waynewec 1 Quote Link to post Share on other sites
roadrunner84 466 Posted April 10, 2013 Share Posted April 10, 2013 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(). waynewec 1 Quote Link to post Share on other sites
waynewec 0 Posted April 10, 2013 Author Share Posted April 10, 2013 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 Quote Link to post Share on other sites
roadrunner84 466 Posted April 10, 2013 Share Posted April 10, 2013 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); } } waynewec 1 Quote Link to post Share on other sites
waynewec 0 Posted April 11, 2013 Author Share Posted April 11, 2013 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? Quote Link to post Share on other sites
roadrunner84 466 Posted April 11, 2013 Share Posted April 11, 2013 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? 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. waynewec 1 Quote Link to post Share on other sites
waynewec 0 Posted April 11, 2013 Author Share Posted April 11, 2013 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. Quote Link to post Share on other sites
roadrunner84 466 Posted April 11, 2013 Share Posted April 11, 2013 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. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.