Arduino Serial Control - Strings?

Hi guys!

Ok, thanks for your help but until now I haven't found anything that works. A friend is helping me, however until now we have no results. So, I'll start with a geral picture:

This will be to control an entire robot. I'm only using this example of LED On/Off to simplify everything. Once I understand how to do this, It will be very easy to apply to everything else.

So, the computer will send commands to the router that will transmit them to the arduino, trought the serial port.
I need to make the LED turn on with a command like "aaaa". It must be with "aaaa", not "abab" or "aaaba" or even just "a".
Also, I need to turn it off with the command "bbbb".

Everything else received that it is not defined as an action will be ignored.

So, can someone teach how to do this or do this for me (I'm willing to pay for it, if necessary).

Best regardings,

Tiago Ferreira.

Im gonna guess ur problem might be the flow control...Does your ur router supports it with hardware ? Im not an expert in that side of things myself, but some more details would probably help. Connections wise.
isnt using a computer instead to transmit the data an option ?! would solve most of your problems

So, the computer will send commands to the router that will transmit them to the arduino, through the serial port.

Does that work ? If yes, the rest is easy.
If not, you can work in parallel
A) using the Serial Monitor to send data to Arduino and log debugging messages, and
B) find out how that router communication is supposed to work.

All commands are exactly 4 characters long?
Yes: Wait until there are 4 characters available < Serial.available() >,
then read them into a char array and compare that to your known commands. < strcmp >
Read additional characters as well ( space, new line, ... )
No: Read the characters, keep track of what you got, until the cmd is received completely.
It's easier if this is recognized by a special character (usually a Linefeed, 0x0A)

But wont he need to control the flow somehow ?!? Most routers dont do it with the hardware; only through software...Am i right there ?!?

iyahdub:

I don't know what you've asked me, to be honest. But my router supports communication. In this phase described in this post I'm still struggling with the computer part.

michael_x:

Yes, the communication is working. If the code is simpler and I use just "a" or "b" I can control the arduino remotelly. But I need to do it using "aaaa", as described.
Can you teach how to do that? I'm trying, but I can't figure it out!

Thank you!

http://ricardo-dias.com/2011/02/25/dd-wrt-serial-and-arduino/
Seems he done it, Tiago.

Well, as i said this is not my strong point, but dont you need a way of controlling the flow of data ?!? Like handshake, etc ?!? That is where you having problems.. am i right ?! I might be seeing it from a wrong perspective ?!?

tgferreira:
michael_x:

Yes, the communication is working. If the code is simpler and I use just "a" or "b" I can control the arduino remotelly. But I need to do it using "aaaa", as described.
Can you teach how to do that? I'm trying, but I can't figure it out!

This is the best example I know: Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking
(Sorry, my previous link was too unspecific, and rather leads into Australian MUD , where the D stands for dungeons)

You learn most by telling what does not work in your code. ( And it's nice that you reduce your problem to 2 commands and a LED, thank you )

BTW: there's a difference between "a" and 'a'.

@iyahdub: I don't think it's about flow control.

Ok guys, my friend has helped me and made this little code:

#include <stdio.h>

#define LED 13

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() == 4) {
    char command[5];

    for (int i = 0; i < 4; i++) command[i] = Serial.read();
    command[4] = '\0';

    Serial.println(command);
    
    if (strcmp(command, "AAAA") == 0) {
      digitalWrite(LED, HIGH);
      Serial.println("LED13 is ON");
    } else if (strcmp(command, "BBBB") == 0) {
      digitalWrite(LED, LOW);
      Serial.println("LED13 is OFF");
    }
  } else {
    Serial.flush();
  }
}

Everything works, except for one little and very important thing:
If I send AAAA, it will print AAAA and do the action. However If I send AAAAA, it will make everything the same way and print only AAAA. But when I send BBBB (for example), it will print ABBB. In my words, It is "saving" a character. And this must not happen since the router will be sending "random" stuff trought serial that must be ignored and must not mess with the commands that I send.

Anyone knows how to solve this? Thanks!

That's because you didn't read in the last A, so it just sat in the buffer until the next command was sent.

Two possible solutions:
Add a small delay at the end of your if block and then read and discard the rest of the buffer. For simple applications that aren't very time sensitive, where commands aren't set frequently, this would be the easy option.

Or you can send start and stop bytes. When you receive the start byte, you reset your buffer index to 0. Then, read until you receive a stop byte. This is useful if the length of the message is unknown. You can then just compare the first 4 characters. This would require you to read only one character per iteration of loop(), and is probably to more common method.

Add a small delay at the end of your if block and then read and discard the rest of the buffer.

No, no, no. Please do NOT do this.

First,

 if (Serial.available() == 4) {

is wrong. The == should be >=. Then, if there should be 5 or 8 characters in the buffer, they are not permanently stuck there.

Second, you MUST send start and end of packet markers, so that and are separate packets. Then, you can use code like this:

#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}

To read the whole packet, and nothing but the packet. Where it says "Parse the packet" is where if if(strcmp()) tests go.

PaulS:

Add a small delay at the end of your if block and then read and discard the rest of the buffer.

No, no, no. Please do NOT do this.

I know the hip thing to do here is oppose any usage of delay, and discarding data, more often than not, is not a good idea, but do you have any other reason for why this would work in simple applications?

but do you have any other reason for why this would work in simple applications?

Delay() is a bandaid that often masks an underlying problem. I'd really prefer to fix the underlying problem than mask it.

Dumping random amounts of unread data is also generally not a smart thing to do. Getting a real handle on the exchange of data is a better idea.

Just my opinion, of course, and no more right, or wrong, than yours.

See? that is why i thought had to do with the lack of handshake/flow control and cts/rts. You end up with things like this to sort and debug. But if you keep it simple should be easy in this case. That tutorial on Nick's site explains several ways of getting around that !!
Though in this case is to do with reading the buffer properly, right ?!

Its actually good and im also learning a lot with this case, as i never needed to come down so detailed like this to send data, programming wise...So, deffo a good lesson for me !!!

Arrch:
I know the hip thing to do here is oppose any usage of delay, and discarding data, more often than not, is not a good idea, but do you have any other reason for why this would work in simple applications?

It's not so much hip, as trying to get things to work properly. If you band-aid your way around things, you'll be back in a week saying "sometimes my program loses data".

Let's deal with delay first. Say your mail is normally delivered at 9 am. Would you just sit around at 8 am for an hour, and then go get the mail? Well, no, because:

  • You aren't doing anything useful
  • The mail may come at 8:50 or 9:10

So you check if the mail is "available". Then you know it's there. Whenever that is.

... and discarding data ...

That's like going and getting the mail, expecting two letters, getting three, and throwing the third one into the bin. Maybe the third one was important. That's why you don't discard data.

Hi everyone!

I'm trying to edit PaulS code to put it working for my situation, but I don't know exactelly where I should put the code to compare strings: where is it exactelly?

Also, I've got a doubt. It's kind of a little thing but I tought about it a few moments ago and I wanna solve it as soon as possible: let's imagine that everything is working, and I'm using caracters to start and end the packed, wich are < and >. So, the arduino is plugged into my router and I'm not sending any code to the arduino. However, in the "garbage" that the router sends to arduino appears a < caracter. And, a few moments later I try to send an action to the arduino, like this .
So, my doubt it: Arduino already received a < from the router. Will it accept my order, or it will be waiting for a > to end the packet and only then it will accept and process my order?

Anyway, thank you all very much!

Tiago Ferreira

Will it accept my order, or it will be waiting for a > to end the packet and only then it will accept and process my order?

Well, that's up to you :wink:
Easiest is: every '<' resets all internal indexes and stuff, every '>' examines what has come so far and acts.
After startup, you discard everything until a '<' arrives. Then '<' resets, and after that you store everything that comes until the '>' arrives (or it's obviously garbage (don't go beyond the provided buffer size)

BTW: I'd prefer to use a newline as both end like '>' and start of a new command like '<' in the discussion up to now.
It's up to you if newline is defined as 0x0D (CR) or 0x0A (LF) or both.
Easy to test using Serial Monitor and the Enter key.

Keep special characters like < > : = etc. as delimiters within a command.

Well, that's up to you

True, but the way I wrote the code, the < resets everything because it signifies the start of a packet.

One thing to keep in mind is that serial data is not guaranteed to be delivered correctly. Noise, timing issues, and other factors can cause a bit to not be received correctly, The byte, then, is discarded. If that byte was a >, the packet that it ended will be discarded, too (with the code I posted).

If the character that is corrupted is a <, then the packet is starts will be discarded, because the next character(s) do(es) not follow a <. The next < gets things back in sync.

If the character that is corrupted is between the start and end of packet markers, then it is up to you to determine what to do. If the packet is AAA, instead of AAAA, then it won't match any command, so no harm, no foul, and the packet is discarded.

In most cases, though, you need to do something more to ensure that only good data is used. If the packet represents a PWM value, and the value sent was 103, and the 0 was corrupted, the packet value will be 13, instead of 103. You need to decide whether the actual value received is reasonable, or not.

For instance, if you have been receiving values like 101, 101, 102, 103, 104, 107, 105, and 103 arrives, that is most likely a good value. If the value that arrives is 13, it may not be good.