Arduino readString(); code runs slow - arduino

I have the following code which I need to execute quickly, yet its taking a lot of time to change the value, anyway over way of making this task quicker?
I am using indexOf() and substring() to accomplish this task.
This is for changing the strip LED colors.
// declare LED Series A Pins R-G-B (PWM Pins)
int const AledRedPin = 6;
int const AledGreenPin = 5;
int const AledBluePin = 3;
// declare LED Series B Pins R-G-B (PWM Pins)
int const BledRedPin = 10;
int const BledGreenPin = 11;
int const BledBluePin = 9;
// serial input variable & string
// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
int AledRed = 255;
int AledGreen = 255;
int AledBlue = 255;
// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
int BledRed = 255;
int BledGreen = 255;
int BledBlue = 255;
//serial input
String Command = "";
//string manipulation
int cmdindexval = 0;
String CommandType = "";
int CommandValue = 0;
String Series = "";
void setup() {
// put your setup code here, to run once:
// start serial
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
// set LED Series A Pins as Output R-G-B
pinMode(AledRedPin, OUTPUT);
pinMode(AledGreenPin, OUTPUT);
pinMode(AledBluePin, OUTPUT);
// set LED Series B Pins as Output R-G-B
pinMode(BledRedPin, OUTPUT);
pinMode(BledGreenPin, OUTPUT);
pinMode(BledBluePin, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
// read from serial if it's available
if (Serial.available() > 0) {
Command = Serial.readString(); //read string from serial monitor
cmdindexval = Command.indexOf('='); //read characters until '=' then assign the value
CommandType = Command.substring(0, cmdindexval); //assign the value from 0 to cmdindexval
//Series = Command.substring(0, 1); //read first character
CommandValue = Command.substring(cmdindexval + 1).toInt(); //assign the value after '=' and convert string to Int
Serial.println(CommandType + " ,is equal to " + CommandValue + " ,Series: " + Series);
//if (Series == "A") {
if (CommandType == "ACledRed"){
AledRed = CommandValue;
}
else if (CommandType == "ACledGreen"){
AledGreen = CommandValue;
}
else if (CommandType == "ACledRedBlue") {
AledBlue = CommandValue;
}
//}
//else if (Series == "B") {
if (CommandType == "BCledRed") {
BledRed = CommandValue;
}
else if (CommandType == "BCledGreen") {
BledGreen = CommandValue;
}
else if (CommandType == "BCledBlue") {
BledBlue = CommandValue;
}
//}
} //end serial
analogWrite(AledRedPin, AledRed);
analogWrite(AledGreenPin, AledGreen);
analogWrite(AledBluePin, AledBlue);
analogWrite(BledRedPin, BledRed);
analogWrite(BledGreenPin, BledGreen);
analogWrite(BledBluePin, BledBlue);
}

From the Arduino docs on readString:
Serial.readString() reads characters from the serial buffer into a string. The function terminates if it times out (see setTimeout()).
and the docs on setTimeout:
Serial.setTimeout() sets the maximum milliseconds to wait for serial data when using Serial.readBytesUntil(), Serial.readBytes(), Serial.parseInt() or Serial.parseFloat(). It defaults to 1000 milliseconds.
This means that the readString is always waiting 1 sec to make sure that the sending of the string is finished and has the complete string.
Unfortunately that means it's slow to respond. You could lower the timeout with the setTimeout, but you would still have some delay, or if you set it too low you could potentially get incomplete stings.
The best solution would be to use readStringUntil, so you know you have a complete string when you get a terminator character (like a newline).
Replace
Command = Serial.readString();
with
Command = Serial.readStringUntil('\n');
and make sure you set the Serial monitor so send the newline character.

Edit: see important update at the end.
This can be made significantly faster, but first let's have a look at the work that has to be done in every loop iteration with the current code:
As #gre_gor already explained, you could be losing some time in readString().
for each value, between 15 and 20 bytes have to be sent, read, parsed and converted to int.
for each received value (R, G or B), analogWrite() is called 6 times (and analogWrite() isn't really fast). This means that in order to change the two series, analogWrite() is called 36 times (and this is probably where most time is lost). And if no serial data is available, analogWrite() is still called 6 times.
and also, Serial.println() is called each time in the example - so it would be best to turn this off.
To speed this up, the RGB values could be sent in a small buffer (assuming you have control over the sending side as well), and read with Serial.readBytesUntil().
If the values for both A and B are sent together, the 6 RGB values can be sent as 6 bytes:
byte rcvBuffer[7];
void loop() {
if (Serial.available() > 0) {
// message: RGBRGBx - but see update below
int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 7); // 0x78 is 'x'
if (numRead == 7) { // or 6, see below
analogWrite(AledRedPin, rcvBuffer[0]);
analogWrite(AledGreenPin, rcvBuffer[1]);
analogWrite(AledBluePin, rcvBuffer[2]);
analogWrite(BledRedPin, rcvBuffer[3]);
analogWrite(BledGreenPin, rcvBuffer[4]);
analogWrite(BledBluePin, rcvBuffer[5]);
}
// else ignore this read - could be a first unaligned read
}
}
If only the values for A or B are sent together:
byte rcvBuffer[5];
void loop() {
// You could probably even remove the Serial.available() check
if (Serial.available() > 0) {
// message: TRGBx where T is Type ('A' or 'B')
int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 5); // 0x78 is 'x'
if (numRead == 5) { // or 4, see below
switch (rcvBuffer[0]) {
case 'A':
analogWrite(AledRedPin, rcvBuffer[1]);
analogWrite(AledGreenPin, rcvBuffer[2]);
analogWrite(AledBluePin, rcvBuffer[3]);
break;
case 'B':
analogWrite(BledRedPin, rcvBuffer[1]);
analogWrite(BledGreenPin, rcvBuffer[2]);
analogWrite(BledBluePin, rcvBuffer[3]);
break;
default :
// do nothing, or send error message
}
}
}
}
I used 'x' as the stop byte to make it visible, but you could as well use a zero byte.
Now, I'm not really sure if readBytesUntil() also reads the terminating byte into the buffer or skips it, and can't test this right now. But I would think only the RGB values are read into the buffer. In this case you'll have to change those values to the ones I put in the comments.
To save even more time, you could check each value and only call analogWrite() if that value did change since the last call (for each R, G and B).
Update: Obviously we can't just use 'x' or a zero byte as the stop byte, because each of the RGB values could also be an 'x' or zero byte (it's getting late here :). And while ReadBytes() could be used instead, it's better to have a stop byte to keep the buffers aligned. So I would suggest to use 0xff (255) as the stop byte and make sure none of the RGB values can be 0xff.
And just in case there could be other message types in the future, each message could also be prepended with a message code (1 or 2 bytes).

I always use readBytesUntil()whenever I use the serial port for communication.
It gets the job done, it always gets the entire string, but the same problem as readString()takes at least 1000ms to complete.
Using both Serial.setTimeout() and Serial.readBytesUntil() worked fine for me, by reducing the delay.
Something like:
Serial.setTimeout(250);
inData = Serial.readStringUntil('\n');

Related

Reading a character and integer command from radio in order to execute a function

I am trying to create a code and loop that can read a character and integer from radio. This character is a command that will represent a packet recover command. This code is being performed in Arduino. The purpose of this code is to read a character command and an integer number between 0 and 512. This integer value represents a number that corresponds to a packet. The data is stored using an EEPROM. I will comment in the code what I am trying to achieve.
Assuming some arbitrary data is already on the EEPROM.
//*******Functions to access EEPROM********//
void I2CEEPROM_Write( unsigned int address, byte data )
{
Wire.beginTransmission(EEPROM_ID);
Wire.write((int)highByte(address) );
Wire.write((int)lowByte(address) );
Wire.write(data);
Wire.endTransmission();
delay(5); // wait for the I2C EEPROM to complete the write cycle
}
byte I2CEEPROM_Read(unsigned int address )
{
byte data;
Wire.beginTransmission(EEPROM_ID);
Wire.write((int)highByte(address) );
Wire.write((int)lowByte(address) );
Wire.endTransmission();
Wire.requestFrom(EEPROM_ID,(byte)1);
while(Wire.available() == 0) // wait for data
;
data = Wire.read();
return data;
}
void sendPackets(uint32_t start, uint32_t ending, char message_type)
{
radio.begin(1200);
uint32_t starting=start;
int number_of_packets=ceil(((float)ending-start)/total_size);
uint32_t last_byte=start;
for (uint32_t j=starting; j<=start+(data_size*i)-1; j++)
{
if (j>ending-1)
break;
Serial.print("Address: ");
Serial.println(j);
Serial.print("Value :");
Serial.println(I2CEEPROM_Read(j));
last_byte=j;
}
starting=last_byte;
delay(100);
}
}
//*********Main Function******//
void setup()
{
Serial.begin(9600);
Serial.setTimeout(100); //don't wait longer than 100ms for incoming data
}
void loop(void)
{
char cmd;
int xyz;
if (Serial.available())
{
cmd = Serial.read();
// ***I don't know how the if statement will be structured inside the bracket*****//
if (cmd == 'C', xyz)
{
//**what do I write here such that I can find the packet number
relating to the integer (xyz). The integer is the packet number I am
looking for. The C command represents that I am trying to recover a
missing packet.**//
}
else if (cmd == 'S')
{
Serial.println("incomplete");
}
else if (cmd == 'V')
{
Serial.println("wrong");
}
else if (cmd == 'T')
{
Serial.println("correct");
}
else
{
Serial.println("retry");
}
}
}
If I understand you correctly, you are attempting to read in commands from Serial, and if you encounter the 'C' command, you expect an integer value (0-512) to follow. You will need to read the integer value in. Here's one way:
cmd = Serial.read();
if(cmd == 'C')
{
int packetNum = Serial.parseInt();
// Continue on with your packetNum
}
...
The Serial.parseInt() function will read in the ASCII-representation of numbers from the serial stream, and then it will attempt to parse them and return them as an integer value. The integer value is what you want to work with in your code.
But be warned: If Serial.parseInt() is unable to parse an integer value from the serial stream, or times out waiting for it, it will return the value 0. You can test for that return value and act on it accordingly, but in your case the value of 0 is also a legitimate packet number. If possible, you may want to change your allowable packet numbers to be 1-513 so you can easily handle this error condition. (There are other ways to go about it too, but this would be an easy fix).
For more information about Serial.parseInt(), see http://arduino.cc/en/Serial/ParseInt
Also, as an aside, instead of using a bunch of if/else if/else if branches for each possible argument, you might instead like to use a switch statement. It accomplishes the same thing, but makes the code cleaner and more elegant.
switch(cmd)
{
case 'C':
int packetNum = Serial.parseInt();
// Continue on with your packetNum
break;
case 'S':
Serial.println("incomplete");
break;
case 'V':
Serial.println("wrong");
break;
case 'T':
Serial.println("correct");
break;
default:
Serial.println("retry");
}
Learn about switch/case here: http://arduino.cc/en/Reference/SwitchCase

Last chunk of data truncated on arduino serial data

I have an arduino taking serial input and will turn on the leds. The code is below.
I have a strange problem that when I send multiples of 120x bytes e.g., 240, 480 the last 120 bytes never get read completely.
I see on the serial monitor 120 120 120 81 if I send 480 bytes of data. Could anyone point out the mistake?
#include "FastLED.h"
#define DATA_PIN 6
#define NUM_LEDS 40
byte colors[120];
CRGB leds[NUM_LEDS];
void setup(){
FastLED.addLeds<NEOPIXEL, DATA_PIN, RGB>(leds, NUM_LEDS);
Serial.begin(115200);
}
void loop(){
if (Serial.available()){
int i =0;
char incomingByte;
while(1) {
incomingByte = Serial.readBytes((char *)colors,120);
break;
}
Serial.print(incomingByte);
for(i=0;i<NUM_LEDS ;i++){
leds[i].green = colors[i];
leds[i].red = colors[i+1];
leds[i].blue = colors[i+2];
}
if(incomingByte==0x78){
FastLED.show();
}
}
}
your code is flawed in different ways.
First, please remove the useless use of while(1) {…; break;}, it's just adding an overhead and adds nothing to your algorithm.
Otherwise, your code is not working well because, I guess, at some point there's a lag happening in the serial communication that causes the read to timeout. Let's have a look at source code.
First, you take the readBytes() function. All it does is:
size_t Stream::readBytes(char *buffer, size_t length)
{
size_t count = 0;
while (count < length) {
int c = timedRead();
if (c < 0) break;
*buffer++ = (char)c;
count++;
}
return count;
}
i.e. it iterates length times over the blocking read function. But it breaks if that function's return value is less than zero, returning less than length bytes. So that what's happening to get less than length, so let's have a look at timedRead():
int Stream::timedRead()
{
int c;
_startMillis = millis();
do {
c = read();
if (c >= 0) return c;
} while(millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
what happens here, is that if the read succeeds, it returns the read value, otherwise it loops until timeout has passed, and returns -1, which will end readBytes immediately. The default value for the timeout is 1000ms, though you can make that value higher by using Serial.setTimeout(5000); in your setup() function.
Though you have nothing to earn by using the blocking readBytes() function. So you'd better instead write your loop so you read the values, and trigger an event only once all values have been read:
#define NB_COLORS 120
void loop() {
static byte colors[NB_COLORS];
static int colors_index=0;
if (colors_index < NB_COLORS) {
// let's read only one byte
colors[colors_index] = Serial.read();
// if a byte has been read, increment the index
if (colors[colors_index] != -1)
++colors_index;
} else {
// reset the index to start over
colors_index = 0;
// should'nt you iterate 3 by 3, i.e. having i=i+3 instead of i++ ?
for(i=0;i<NUM_LEDS ;i++){
leds[i].green = colors[i];
leds[i].red = colors[i+1];
leds[i].blue = colors[i+2];
}
FastLED.show();
}
}
HTH

Arduino Serial communication output

I have 2 Arduinos Leonardo and I want them to communicate itself, so I did the following code:
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
}
void loop() {
String outMessage = ""; // String to hold input
while (Serial.available() > 0) { // check if at least 1 char is available
char inChar = Serial.read();
outMessage.concat(inChar); // add inChar to outMessage
}
if (outMessage != "") {
Serial.println("Sent: " + outMessage); // View Arduino 1 in Serial Monitor 1
Serial1.print(outMessage); // Send to Arduino 2
}
while (Serial1.available() > 0) {
Serial.print("Received: "); // View Arduino 1 in Serial Monitor 2
Serial.print(Serial1.read()); // Received from Arduino 1
Serial.println();
}
}
I want to send a message from Arduino 1, print in Serial Monitor and send via TX1 to Arduino 2 and vice-versa. The problem is that I don't receive what I was expecting. For instance if I type test:
Arduino 1:
Sent: test
Arduino 2:
Received: t
Received: e
Received: s
Received: t
I also tryed to do the receiving side like the sending side and use Serial.write but with no sucess.
Is there a easier way to do that or to fix it?
Thanks
Has mentioned by Hans, you need a protocol.
This is what I use to consider a message in Arduino to be a complete message:
char inData[10];
int index;
boolean started = false;
boolean ended = false;
String message =("I am Arduino 1 and I am ready");
void setup(){
Serial.begin(9600);
Serial.println(message);
}
void loop()
{
while(Serial.available() > 0)
{
char aChar = Serial.read();
if(aChar == '>')
{
started = true;
index = 0;
inData[index] = '\0';
}
else if(aChar == '<')
{
ended = true;
}
else if(started)
{
inData[index] = aChar;
index++;
inData[index] = '\0';
}
}
if(started && ended)
{
int inInt = atoi(inData);
Serial.println(inInt);
}
// Get ready for the next time
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
So, basically a message is considered completed only if it is between the special characters ><, like this: >message<. Then you can do the same on reading.
It does not have to be too complicated. If you look carefully at your last whlie-loop you can see that the software does not get a chance to read more than one character each time it passes through the loop. So that is what you get: one character at a time.
In your first while-loop you did better: you collected all the incoming letters until nothing was available and then sent them all at once. So if you make your last loop look more like the first one, you'll get a better result.
As mentioned a protocol to frame messages is needed between devices. A quick way to do this is to use Bill Porter's EasyTransfer library which does exactly what you are trying to do, over either UART or I2C. It has several examples.
Serial.read() reads only one byte every time you use it. A simple solution would be to store each byte on a char array while Serial.available>0 and then print the String with the whole message that was sent.
char message[40];
int count = 0;
while(Serial.available()>0){
message[count++] = Serial.read();
}
Serial.println(message);

Serial message to integer on Arduino

I want my Arduino to receive an integer through the serial communication. Can you help me with this?
It should be in a form like:
int value = strtoint(Serial.read());
There are several ways to read an integer from Serial, largely depending on how the data is encoded when it is sent. Serial.read() can only be used to read individual bytes so the data that is sent needs to be reconstructed from these bytes.
The following code may work for you. It assumes that serial connection has been configured to 9600 baud, that data is being sent as ASCII text and that each integer is delimited by a newline character (\n):
// 12 is the maximum length of a decimal representation of a 32-bit integer,
// including space for a leading minus sign and terminating null byte
byte intBuffer[12];
String intData = "";
int delimiter = (int) '\n';
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available()) {
int ch = Serial.read();
if (ch == -1) {
// Handle error
}
else if (ch == delimiter) {
break;
}
else {
intData += (char) ch;
}
}
// Copy read data into a char array for use by atoi
// Include room for the null terminator
int intLength = intData.length() + 1;
intData.toCharArray(intBuffer, intLength);
// Reinitialize intData for use next time around the loop
intData = "";
// Convert ASCII-encoded integer to an int
int i = atoi(intBuffer);
}
You may use the Serial.parseInt() function, see here: http://arduino.cc/en/Reference/ParseInt

Brightness on digital output varies based on level input type

Basically, I am following the tutorial code in BarGraph for the LED bar graph. I do not have a potentiometer, so I thought to mimic it by using a Processing serial write, based on the dimmer example in Dimmer. I have set the sensorReading value to the input from the Processing application (updating its grid to be 1023 elements) like so:
int sensorReading;
if (Serial.available()) {
// Read the most recent byte (which will be from 0 to 1023):
sensorReading = Serial.read();
}
This does light up the LEDs based on my mouse position in the grid in the Processing application. However the LEDs are very dim. If I change how I set the sensorReading value to:
int sensorReading = random(0, 1023);
Then the LEDs light up much brighter. Since the LEDs are all on the digital out pins I thought it would just send on/off based on the sensorReading value and would not have anything to do with how bright. What am I missing?
Here is the Processing code:
// Dimmer - sends bytes over a serial port
// by David A. Mellis
//
// This example code is in the public domain.
import processing.serial.*;
Serial port;
void setup() {
size(256, 150);
println("Available serial ports:");
println(Serial.list());
// Uses the first port in this list (number 0). Change this to
// select the port corresponding to your Arduino board. The last
// parameter (for example, 9600) is the speed of the communication. It
// has to correspond to the value passed to Serial.begin() in your
// Arduino sketch.
//port = new Serial(this, Serial.list()[0], 9600);
// If you know the name of the port used by the Arduino board, you
// can specify it directly like this.
port = new Serial(this, "COM6", 9600);
}
void draw() {
// Draw a gradient from black to white
for (int i = 0; i < 1024; i++) {
stroke(i);
line(i, 0, i, 150);
}
// Write the current X-position of the mouse to the serial port as
// a single byte.
port.write(mouseX);
}
Here is the Arduino code:
// These constants won't change:
const int analogPin = A0; // The pin that the potentiometer is attached to.
const int ledCount = 10; // The number of LEDs in the bar graph.
int ledPins[] = {
2, 3, 4, 5, 6, 7,8,9,10,11 }; // An array of pin numbers to which LEDs are attached.
void setup() {
Serial.begin(9600);
// Loop over the pin array and set them all to output:
for (int thisLed = 0; thisLed < ledCount; thisLed++) {
pinMode(ledPins[thisLed], OUTPUT);
}
}
void loop() {
// Read the potentiometer:
// int sensorReading = random(0, 1023);
// delay(250);
byte streamReading;
if (Serial.available()) {
// Read the most recent byte (which will be from 0 to 255):
sensorReading = Serial.read();
}
//Serial.println(sensorReading);
// Map the result to a range from 0 to the number of LEDs:
int ledLevel = map(sensorReading, 0, 255, 0, ledCount);
// Loop over the LED array:
for (int thisLed = 0; thisLed < ledCount; thisLed++) {
// If the array element's index is less than ledLevel,
// turn the pin for this element on:
if (thisLed < ledLevel) {
digitalWrite(ledPins[thisLed], HIGH);
}
// Turn off all pins higher than the ledLevel:
else {
digitalWrite(ledPins[thisLed], LOW);
}
}
}
Problem: Your processing code is sending data constantly, sending serial data to your Arduino all the time:
Called directly after setup(), the draw() function continuously
executes the lines of code contained inside its block until the
program is stopped or noLoop() is called. draw() is called
automatically and should never be called explicitly.
This causes your Arduino sketch to update the LED on/off status frequently, and given how you read the data, this will result in LEDs pulsing really fast.
Solution: The simplest fix would be to add a delay to either the Arduino or the Processing sketch. An even better solution would be to modify the Processing code to only send data when the value changes; although note that since mouse values change almost constantly and that those changes wouldn't be significant for the Arduino code, you still might have a lot of unnecessary flickering. However, if you fix the serial read function in your Arduino code, the flickering will not be as much of a problem anyway.)
Code: Modify your Processing code to track the last reading, and only update if it is different:
int lastMouseX;
void draw() {
// draw a gradient from black to white ...
int newMouseX = mouseX;
if (newMouseX != lastMouseX) {
lastMouseX = newMouseX
// write the current X-position of the mouse to the serial port as
// a single byte
port.write(mouseX);
}
Other issues: First of all there is an issue if you are expecting a value 0-1024: the Arduino's analogWrite() function takes a byte 0-255.
Secondly, as Martin Thompson points out, you are probably sending a string such as 128 from your processing application, and then using its ASCII values to set the intensity. Since ASCII values of 0 through 9 are in the 48-57 range, that will give you a relatively low intensity. Note that when a string of more than one byte somes in (such as 128) you are using only only one byte for intensity. So you have two options:
Send a real binary byte, not a string. This is what is done in the dimmer example you show
Send a string, and then convert it to its binary representation. For this you will need to read all the characters up until some delimiter (such as CR, or space), collect them up, and then convert.
This code might look something like this:
#include <stdlib.h>
int idxChar = 0;
#define BUFFER_SIZE 10
char strIntensity[BUFFER_SIZE];
...
while (Serial.available()) {
// read the string representation of a byte
// assuming bytes are separated by non-numeric characters
// and never overflow
char ch = Serial.read();
if ( (ch >= '0') && (ch <= '9') ) {
strIntensity[idxChar++] = ch;
} else {
strIntensity[idxChar] = 0;
sensorReading = atoi(strIntensity);
idxChar = 0;
}
if (idxChar>=BUFFER_SIZE-1) {
// (need space for the null char at the end too
// Buffer overflow. Bail
idxChar = 0;
}
}
// read the most recent byte (which will be from 0 to 1023)
Bytes go from 0 to 255. And they (usually) represent characters from the ASCII character set.
If you are expecting to read numbers between 0 and 1023 they could be being transmitted a character at a time (ie a character 1 followed by a character 0 would represent the number 10) - in which case you have to parse them to turn them into a number that can be used as you expect.
The parseInt function is probably what you need - a tutorial on reading ASCII integers can be found here

Resources