Problem with reading serial string

Hi!

I am working on a project with a strip of 300 LEDs, and I am using a python script to send, a string of numbers separeted with commas (f.e. "4,1,2,3,4,5" would turn the LED "1" in red and the "2","3","4" and 5 LEDs green, the "4" is just the number of green LEDs), to tell the Arduino which LEDs to turn on.

My problem is that I have written some arduino code that takes this string and actually turns on the right LEDs, however when the arduino is working for a while (maybe less than a minute) it freezes. To try to figure out what is happening I printed the data that the Arduino is reading and I get this:

The two last strings written where: "140,41,76,81,116,121,156,155" and
"140,76,81,116,121,156,155,154". Using ser.write(variable) in python where variable="140,41,76,81,116,121,156,155" (if needed I can share more of the python code, but as it works for a while I think this is an arduino problem).

#define MAX_NUMBERS 30 // Maximum number of numbers to read
#include <FastLED.h>
#define LED_PIN 2
#define NUM_LEDS 300

// Array for the LEDs
CRGB leds[NUM_LEDS];

int* parseNumbers(char* str) {
  static int numbers[MAX_NUMBERS]; // Array to store numbers
  int num_index = 0; // Index of current number being read
  char* p = strtok(str, ","); // Get the first token
  
  while (p != NULL && num_index < MAX_NUMBERS) {
    numbers[num_index] = atoi(p); // Convert the token to an integer and store it in the array
    num_index++; // Increment the index to read the next number
    p = strtok(NULL, ","); // Get the next token
  }
  
  return numbers;
}

void setup() {
  // Setting up the LEDs info 
  Serial.begin(115200); 
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
  FastLED.clear();
  FastLED.show();
}

void loop() {

  // Attempt to kill the loop if something breaks
  int starttime = millis();
  int endtime = starttime;
  while ((endtime - starttime) <=40) 
  {
  if (Serial.available() > 0) {
    char str[180];
    int* numbers;
    int i;
    int num_index = 0;
    
    // Read input string from serial
    while (Serial.available() > 0 && num_index < sizeof(str) - 1) {
      char c = Serial.read();
      if (isdigit(c) == false & c!=',' ) {
        break;
      }
      str[num_index] = c;
      num_index++;
    }
    str[num_index] = '\0'; // Null-terminate the string
  
    // Parse the string into an array of integers
    numbers = parseNumbers(str);
    
    FastLED.clear();

    leds[numbers[0]] = CRGB(255,0,0);

    Serial.println(numbers[0]);
    
    for (i = 1; i < MAX_NUMBERS; i++) {
      leds[numbers[i]] = CRGB(0,255,0);
      Serial.print(numbers[i]);
      Serial.print(" ");
    }
    //Serial.println("");
    FastLED.delay (50);
    //delay(10);
  }
  }
}

This is the code I am using, I do not understand why it works during a while but suddenly it breaks. I tried to create a "filter" with if (isdigit(c) == false & c!=',' ) to try and avoid this weird symbols and also I tried to add a limit to the time the arduino has to finish the loop but none of these work. If someone could give some insights on what may be happening, or how to write an exception to check and catch this weird characters or even provide a better way to send information between a python script and the arduino it would be apreciated. (Also if there is any optimization to the code, I know it is not the cleanest but I did not know how to read a string separeted with commas and pass that to an array in a better way)

Thanks in advance!

The serial input basics tutorial will help with that. Read in the entire string then parse it.

And when you get that to work, be aware that the FastLED show() function disables interrupts while it runs. Any serial data that arrives while interrupts are disabled will be ignored or contaminated.

1 Like

try reducing the baudrate to 9600 and see if works??

1 Like

my English is not so good. explain pls what you try to send.
the python code will be helpful too.
am i right that numbers which python sends are never 0?

1 Like

try something like this to get the string of the serial input

  while(Serial.available()>0)
  {
    String cadena = Serial.readStringUntil('\n');
    Serial.println("String: " + cadena);
  }

Xakko

1 Like

Using the String class on a board with little memory can cause memory problems if not used carefully. See the evils of arduino Strings for why.

1 Like

Hi! Thanks for your reply, sadly I have already try different baud rates but the same thing happens :frowning:

Hi! The message the python script sends is the one above, the arduino is printing those 0 because I am saving the values in an array of length 30 and as there are less than 30 numbers, so the array has some "empty" values and prints zeros. (This is also something I would like to solve, but it does not bother me as much as the garbage message)

Oh I did not know that, I would read that post. Thanks!

Thanks for the reply! I would read that post and try to see if that works. I did not know about the dissabling of interrups with the FastLED.show() function.

Thanks for the reply, however I don't think this would work for my project. How can I separate then the String cadena in the different comma separated values? (for example if the string is "1,2,3" how can I have an array with these numbers or a way to get 1 2 and 3 by themselfs from this string?)

Updating your strip with FastLed.show() (or FastLed.delay() which calls show()) takes time. 1.25 microseconds per bit, so 24x1.25 = 30 microseconds per pixel. Times 300 = 9 milliseconds. During that time interrupts are disabled and serial will not work reliably.

Your best solution in my opinion is to send one byte from your python script, let the Arduino echo it back when it receives it and only when the python script gets the echo it is allowed to send the next byte.

2 Likes
void setup() {
  Serial.begin(115200);
}

void loop() {
  static bool mustTOshow = false;
  int myNumbers[30] = {0};
  if (Serial.available() > 1) {
    mustTOshow = true;
    for (byte i = 0; i < 30; i++) {
      int someInteger = Serial.parseInt();
      if (someInteger == 0)break;
      else myNumbers[i] = someInteger;
      Serial.println(someInteger);
    }

  }
  if (mustTOshow) {
    mustTOshow = false;
    for (byte i = 0; i < 30; i++) {
      int someInteger = myNumbers[i];
      if (someInteger == 0)break;
      else {
        Serial.print(someInteger);
        Serial.print(',');
      }
    }
    Serial.println();
  }
}

ok. if i understand right:
first integer in sequence is amount of soon folowing numbers to be set as green;
second integer is only one number of position which LED be set as red;
from here folowing the amount of positions of greens.
Positions counts beginning from 1.

#include <FastLED.h>
#define MAX_NUMBERS 30 // numbers to read
#define LED_PIN 2
#define NUM_LEDS 300
CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
  FastLED.clear();
  FastLED.show();
}

void loop() {
  static int myNumbers[MAX_NUMBERS] = {0};
  if (Serial.available() > 1) {
    for (byte i = 0; i < MAX_NUMBERS; i++) {
      int someInteger = Serial.parseInt();
      if (someInteger == 0)break;
      else myNumbers[i] = someInteger;
      Serial.println(someInteger);
    }
    FastLED.clear();
    leds[myNumbers[1]-1] = CRGB(255, 0, 0);
    for (int i = 2; i < myNumbers[0] + 2; i++)leds[myNumbers[i]-1] = CRGB(0, 255, 0);
    FastLED.show();
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.