Recently, as part of a larger project, I've tried to get one of the project's fundamentals to work. That is, sending strings from Processing to my Arduino Pro Mini over bluetooth using an HC-06 module and Software Serial. In order to troubleshoot, I send the string back to the computer over Hardware Serial to check that it works well by using the Arduino IDE Serial Monitor. What I'm reading in the serial monitor, though, kinda seems to stuff up a bit - I'm wondering what could cause it and how I could remedy it.
Arduino Code:
#include <SoftwareSerial.h>
String inString ;
SoftwareSerial mySerial(8, 9);//RX, TX
void setup() {
mySerial.begin(9600); //for bluetooth comms
Serial.begin(9600); //back to computer to read in Serial Monitor
}
void loop() {
if(mySerial.available() > 0) {
inString = mySerial.readStringUntil('\n');//read string from processing over bluetooth
Serial.println(inString); // Send back to computer
}
}
Processing Code:
import processing.serial.*;
Serial mySerial;
int val = 0;
void setup() {
println(Serial.list());
mySerial = new Serial(this, "/dev/tty.HC-06-DevB", 9600);//Bluetooth port name
}
void draw() {
mySerial.write("test " + val + '\n');
val++;
}
And in the Serial monitor, it gives out stuff like this:
test 624
test 625
test 626
s 2
es 2
63test 631
test 632
test 633
test 634
test 635
test 636
test 637
test 638
test 639
test 640
t 4
ts 4
64test 645
test 646
test 647
test 648
test 649
test 650
test 651
test 652
test 653
test 654
s 5
est5
65test 659
test 660
test 661
I would try putting a small delay in Processing between subsequent transmissions.
Using a String (capital S) rather than a string (small s) in an Arduino is not a good idea. It involves a lot of calculation and memory shuffling so it may slow things a bit too much. It can also cause memory overruns.
I also wonder if readStringUntil() cause a long delay if it gets out of synch.
Software Serial is not nearly as competent as Hardware Serial.
readStringUntil() is an interesting function but not what you want. In short, it times out if there is no available bytes. A simple way to test is to read one character from software serial, when there is available character, then write to hardware serial. Is there need to buffer a whole line of characters before sending them all back to your nix?
As Robin2 said using String is not the best approach due to limited memory. I did a little digging to corroborate his claim. See how poorly it's done:
String Stream::readStringUntil(char terminator)
{
String ret;
int c = timedRead();
while (c >= 0 && c != terminator)
{
ret += (char)c;
c = timedRead();
}
return ret;
}
The String called ret is initialized without a reserved length so it starts from zero length. Then it would grow to 1 char long, and then 2, etc. I the worst case scenario if you receive 10 characters, you are looking at (1+2+3+...+10) size of wasted memory.
So, if there is no reason to buffer a line and send to your nix, why not doing it the obvious way?
if (mySerial.available()) Serial.write(mySerial.read());
If memory serves me, Processing tries to run its loop at 60 frames per second? Too much printing with not enough delay.
A delay in Processing of a minimum of 16 ms does the trick. Eventually, though I will need to do other operations to the string, so although I wouldn't have bothered creating one if printing to the hardware serial port was all I was doing, I'll have to store the reading to some kind of string or something. What's the most efficient way to do this?
Using a String (capital S) rather than a string (small s) in an Arduino is not a good idea.
How do you create a string with a lower case 's', like you say? I can't find out anything about it, and when used it gives an error- the only strings I have come across are "String" (capital 's') and char arrays (maybe this would be more efficient?).
liudr, what do you mean by "nix"? Does it refer to Linux, Unix etc.? FYI, I'm on a Mac (sorry I neglected to tell you before). I'll just take it to mean computer.
Also,
if (mySerial.available()) Serial.write(mySerial.read());
works perfectly, sending a whole line at a time. Why does it do this and not send any subsequently received data from the Software Serial port, instead separating it into lines? That is what I was trying to achieve with the readStringUntil('\n').
And, before I progress too far with this, I'll ask you if this is really what I should be doing. I'm reading data from a ps3 controller in Processing using the proControll library, and sending it over bluetooth to the Arduino to hopefully control something. This is my cheapskate workaround to a USB Host Shield, but, seeing as I had an HC-06 anyway, plus the fact that it might come in handy for using other devices (e.g. the computer's keyboard, maybe?) I decided to try it out. Do you think this is worthwhile/the best option?
Please reform your question regarding the if available statement. I couldn't understand it. It worked, right? Are you questioning why?
Using String class objects isn't necessarily a bad thing if you use it correctly. I don't use it because I am old fashion C programmer and understand the 2KB ram is not a lot to play with. If you want to buffer the input and process it line by line in arduino, read about String class and reserve() function. It avoids memory leak all together, since it will start with a String that you set with reserve() to make sure it is large enough to store a whole line.
By nix I do mean a Mac, which is a flavor of unix. I say your reference to your serial port is nix style so I assumed you wouldn't want me to call your computer "PC".
I was indeed questioning why it worked, and yes, it works perfectly. Is it because of the delay between successive lines being received from Processing?
Thanks for the other info. Do you think it would be better to use char[] or String.reserve()? Or would it be the same?
And thanks also to Robin for answering my other question (it seems so obvious to me now!).
The if available will make sure there is something to read. Before, you used a weird function that I didn't even know about, the readXXXUntil(). I took some time to its code hiding inside the Stream class, and Read inside Software serial etc. This family of read until functions don't use available and times out waiting for values. To be honest, that sounds like a brutal way to get the same result but I don't know exactly how it didn't work, after reading all its code. Someone else can pitch in:
// Read data from buffer
int SoftwareSerial::read()
{
if (!isListening())
return -1;
// Empty buffer?
if (_receive_buffer_head == _receive_buffer_tail)
return -1;
// Read from "head"
uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
_receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
return d;
}
// private method to read stream with timeout
int Stream::timedRead()
{
int c;
_startMillis = millis();
do {
c = read();
if (c >= 0) return c;
} while(millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
String Stream::readStringUntil(char terminator)
{
String ret;
int c = timedRead();
while (c >= 0 && c != terminator)
{
ret += (char)c;
c = timedRead();
}
return ret;
}