I have a small problem, and I need your support. I'm also not sure if this is the right place for my question, if not it would be nice if you could tell me where the right place is.
I have a project where I want to use an Arduino to read sensor data and send it via USB to a Raspberry PI which then processes it. In addition, the raspberry Pi should send messages via USB to the Arduino in order to make it move motors.
My problem is that the Arduino has to read the sensors all the time, and I can't send a message to the PI at the end of each loop to ask if the motors need to be moved. That would cost way to much Time.
Is there a way to let the Arduino run a program, and when it receives a message via USB, run a function after which it continues with the normal program?
What I want is for the Arduino to run a program, and when it receives a message via USB, it should perform a function and then return to the "main program". So it should look all the time if a message has come, and independently of that, it should read the sensor data all the time and send it to the Raspberry PI
immediately after "void loop() {" put something like:
if (Serial.available()) {
DOSOMETHING();
}
That way the code will check every cycle if the RPi has sent back data. If yes, then do something like "Send back serial data".
Do note though that if your baudrate is 9600 as shown in your code, any Serial communications is gonna be slow regardless. I highly recommend bumping it up to 115200 for extremely quick communications that won't bog down your code.
My problem is that the Arduino has to read the sensors all the time, and I can't send a message to the PI at the end of each loop to ask if the motors need to be moved. That would cost way to much Time.
if you need to send the sensor data, you keep the Serial port busy one way. nothing prevents you from receiving too...
move the Serial line to 1,000,000 bauds that won't be a problem between an Arduino and a RPi and you'll see a slight speed increase compared to your 9600 bauds...
if time is of the essence, I would suggest to directly read the Serial line at test against -1
int incoming = Serial.read();
if (incoming != -1) { // read returns -1 if there is nothing to read
// we have received something
}
The reason is that available() does actually take time to do some counting (with modulo which is a costly operation) and if you don't care about how many bytes are available, then no need to ask for the count, just if data is really there or not — which is what read() will tell you.
available() gives a false sense that it is a boolean answer and not a count - I don't like that name much...
J-M-L:
The reason is that available() does actually take time to do some counting and if you don't care about how many bytes are available, then no need to ask for the count, just if data is really there or not — which is what read() will tell you.
In the good old days of DOS/ASM based UART Port programming of IBMPC, we had to check first that the port had really received data and then read the arrived data.
ASM Codes to Read RHB-register by Polling the RxRDY-bit:
L11: mov dx, 2002h ; pointing at SR-register
in al, dx
L12: test al, 02h ; checking if al1-bit (RxRDY-bit) is 1
jz L11 ; RxRDY-bit is not LH
L13: mov dx, 2000h ; pointing at Rx-register
in al, dx ; reading ASCII code from Rx-register
L14: nop ; reading from Rx-register is done
I think, the available() and read() methods have the similar reasoning.
but hey, that's how they've done it... so best way to work around this (small) cost is to use either read() or peek() which will return -1 (and remove or not the byte if there is one).
I reckon that at 9600 bauds it does not matter much anyway, the time penalty is not there... At 2,000,000 bauds it's somewhat (a bit) more visible
as I said, you need to have x as an integer (because that's what read returns) and test it against -1 to see if something was read. here you print 0xFF which gives you the weird output
void setup()
{
Serial.begin(9600);
}
void loop()
{
int x = Serial.read();
if (x != -1) Serial.println(x);
}
the whole point is not to avoid testing if something was there to read, the point is to do so in an "efficient" way (probably save ~20 clock cycles so probably 1.25µs ...)
I only mentioned it here as OP seemed to imply that timing was critical
I suggest option 2 (with a modification from char to int as otherwise you could not receive binary 0xFF byte correctly, -1 will be coded 0xFFFF (on AVR) whereas 0xFF will be checked as 0x00FF) if you don't care about how many bytes are in the incoming buffer.
So I would suggest
int x = Serial.read();
if(x != -1) // 0xFFFF on AVR, 0xFFFFFFFF on MKR or ESP
{
Serial.println(x);
}
or even faster as most of the time the buffer is empty probably
if(Serial.peek() != -1) // 0xFFFF on AVR, 0xFFFFFFFF on MKR or ESP
{
Serial.println(Serial.read());
}
That's only if you really care about micro-second level efficiency, there are so many example out there with available() that this could confuse beginners for whom it's OK to say "test if something is available before reading it". So when teaching, I would still recommend the "slow" approach, it's easier to grasp and does not require understanding of what's happening in the background.
isi_ko:
Is there a way to let the Arduino run a program, and when it receives a message via USB, run a function after which it continues with the normal program?
Yes, a few of my own projects do that.
Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.
The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.
You can send data in a compatible format with code like this (or the equivalent in any other programming language)
You should be able to see how those examples can work alongside other functions that you call from loop().
If you want to see a more general multi-tasking example have a look at how the code is organized in Several Things at a Time. Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.