I am experimenting with the serial monitor in an Arduino Uno. My project consists in a simple LED turning on and off at will with the commands "ON" and "OFF" respectively.
To achieve this, I use an HC-06 Bluetooth module, but because of the nature of the test I decided to disconnect it and 'debug' my project using the serial monitor as an equivalent.
The circuit has the following connection table:
LED +: Pin 12 on my Arduino. LED -: GND on my Arduino.
...as you can see, I have tried to simplify as much as I can the circuit, as I wanted to make sure I didn't have wire problems.
Here's the code:
char command;
String string;
#define led 12
void LEDOn()
{
digitalWrite(led, HIGH);
}
void LEDOff()
{
digitalWrite(led, LOW);
}
void setup()
{
Serial.begin(9600);
pinMode(led, OUTPUT);
}
void loop()
{
if (Serial.available() > 0)
{string = "";}
while(Serial.available() > 0)
{
command = ((byte)Serial.read());
if(command == ':')
{
break;
}
else
{
string += command;
}
delay(1);
}
if(string == "LO")
{
LEDOn();
}
if(string == "LF")
{
LEDOff();
}
The problem comes when I type the commands. Normally, an LED in the board will react (I think it's the RX one), but the LED in pin 12 will not react at all. After testing ten times with each command alternatively, I calculated the serial needs an average of 6 command repetitions in order to react. Exactly the same happens when the HC-06 is connected.
Is there a reason for this?
2 Answers 2
Your problem is, amongst other things, that you are assuming that all the serial data arrives at once, which doesn't necessarily happen at all.
I have a discussion about this sort of thing.
Rewriting your sketch using the ideas presented there we get this:
#define led 12
void LEDOn()
{
digitalWrite(led, HIGH);
}
void LEDOff()
{
digitalWrite(led, LOW);
}
// how much serial data we expect before a newline
const unsigned int MAX_INPUT = 50;
void setup ()
{
Serial.begin (9600);
pinMode(led, OUTPUT);
} // end of setup
// here to process incoming serial data after a terminator received
void process_data (const char * data)
{
if (strcmp (data, "ON") == 0)
LEDOn ();
else if (strcmp (data, "OFF") == 0)
LEDOff ();
else
{
Serial.print (F("Unexpected command: "));
Serial.println (data);
}
} // end of process_data
void processIncomingByte (const byte inByte)
{
static char input_line [MAX_INPUT];
static unsigned int input_pos = 0;
switch (inByte)
{
case '\n': // end of text
input_line [input_pos] = 0; // terminating null byte
// terminator reached! process input_line here ...
process_data (input_line);
// reset buffer for next time
input_pos = 0;
break;
case '\r': // discard carriage return
break;
default:
// keep adding if not full ... allow for terminating null byte
if (input_pos < (MAX_INPUT - 1))
input_line [input_pos++] = inByte;
break;
} // end of switch
} // end of processIncomingByte
void loop()
{
// if serial data available, process it
while (Serial.available () > 0)
processIncomingByte (Serial.read ());
// do other stuff here like testing digital input (button presses) ...
} // end of loop
with the commands "ON" and "OFF" respectively.
I changed the test to be for "ON" and "OFF" not "LO" and "LF" which seems a weird way of meeting your requirements.
For this to work you need a delimiter (which tells the Arduino when you have finished sending a command). I have used linefeed which you can automatically send in the IDE's serial monitor (see bottom RH corner).
I also put in code to show "Unexpected command: " if you don't get ON or OFF (note that "on" is not the same as "ON").
-
Nice! Now it works perfectly from the serial monitor. But if I wanted to send this command from an external device via Bluetooth (a smartphone, for example). how could I force the newline after the command? Using ON\n or ON"\n" does not return anything.xvlaze– xvlaze2016年12月30日 14:25:14 +00:00Commented Dec 30, 2016 at 14:25
-
Sending
"ON\n"
should work. You could of course use a different delimiter. Instead of checking for\n
in the code you could check for:
.2016年12月30日 20:39:46 +00:00Commented Dec 30, 2016 at 20:39 -
It worked with ':'. Is there a reason why it's not working with '\n'?xvlaze– xvlaze2016年12月31日 14:35:39 +00:00Commented Dec 31, 2016 at 14:35
-
No, not unless you didn't send "\n" properly.2016年12月31日 21:05:21 +00:00Commented Dec 31, 2016 at 21:05
It worked fine for me, if I typed the entire command, "LO:" or LF:" (without the quotes of course). However, if I typed and sent one character at a time, it failed. The problem is quite likely the code:
if (Serial.available() > 0) {string = "";}
If you send all of the characters at once, the while( Serial.available() )
loop will get the whole line and match it. But if there is any pause between characters, what does that if
statement do? (Hint: loop()
executes way faster than you can type!)
I was using the serial terminal built into the Eclipse IDE. Different terminal software might behave differently when you type a string and send it. Remember, any gaps between characters, and that first if
statement gets time to do its dirty work.
Serial.begin(9600);
toSerial.begin(115200);
(and change serial monitor BPS also). Note, you should dostring = "";
only insetup()
and after processing :, LO, or LF, or something like that, since it perhaps could beloop()
loops before getting a complete commandSerial.available() > 0
might be false at yourif
test, but true at yourwhile
loop condition, which means thatstring
has a chance of not being correctly initialized to""
before you start chaining characters to it. I would also add some sanity check on the input stream, because as it is now any failure is silent. When you exit the loop, eitherstring == "LO"
orstring == "LF"
. If not, then you could print the content ofstring
toSerial
and see why the command was not taken. What's the point ofdelay(1)
?