Go Down

Topic: Why does function with "For" loop break my serial connection? (Read 196 times) previous topic - next topic

mylegispotato

I am trying to make my arduino communicate with PC and after 6h+ troubleshooting I've found that function that fills my LED strip with predefined color pallete breaks the serial input. I can't tell if it makes Serial.Available always zero or there is problem somewhere else. I probably don't understand how arduino works and need explaining how to make it work. That function is FillLEDsFromPaletteColors and works fine when controlling lights from digital inputs (I have buttons wired up in another project). Here is my code:
Code: [Select]

#include <FastLED.h>

#define LED_PIN     6
#define NUM_LEDS    30
int BRIGHTNESS = 32;
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

int UPDATES_PER_SECOND = 100;
CRGBPalette16 currentPalette;
TBlendType    currentBlending;


String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
String commandString = "";

boolean isConnected = false;

String colorData;

void setup() {
  delay( 1000 ); // power-up safety delay
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(  BRIGHTNESS );

  currentPalette = PartyColors_p;
  currentBlending = LINEARBLEND;
  SetSolidColor(255, 0, 0);
 
  Serial.begin(9600);
}

int red;
int green;
int blue;
int startIndex = 1;
bool animationDirection = true;

void loop() {
  //////////////////////
  //Input-side:
  ////////////////////
  if (stringComplete) {
    Serial.println(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
 
//  if(stringComplete)
//  {
//    stringComplete = false;
//    getCommand();
//
//    if(commandString.equals("STR")){
//      currentPalette = PartyColors_p;
//    }
//    if(commandString.equals("RED"))
//    {
//      red = getColorData();
//    }
//    if(commandString.equals("GRN"))
//    {
//      green = getColorData();
//    }
//    if(commandString.equals("BLU"))
//    {
//      blue = getColorData();
//    }
//    inputString = "";
//  }
  //////////////////////
  //Processing-side:
  //////////////////////


  SetupRedAndWhite();
  Animate();
 
  FillLEDsFromPaletteColors(startIndex); // THIS BREAKS SERIAL INPUT!!!
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);

}

void Animate(){
  if (animationDirection) {
    startIndex = startIndex + 1; /* motion speed */
  } else {
    startIndex = startIndex - 1; /* motion speed */
  }
}


void SetSolidColor(int red, int green, int blue){
   CRGB outputColor = CRGB(red, green, blue);
   fill_solid(currentPalette, NUM_LEDS, outputColor);
}

void SetupRedAndWhite() {
  fill_solid(currentPalette, NUM_LEDS, CRGB::Red);
  currentPalette[0] = CRGB::White;
  currentPalette[8] = CRGB::White;
  if(startIndex == 255){
    animationDirection = false;
  }else{
    if(startIndex == 0){
        animationDirection = true;
    }
  }
}

void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
  uint8_t brightness = 255;

  for ( int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 8; // 255 Color Slots / this number = NUM_LEDS
  }
}

int getColorData()
{
  return inputString.substring(5,7).toInt();
}

void getCommand()
{
  if(inputString.length()>0)
  {
     commandString = inputString.substring(1,4);
  }
}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    SetSolidColor(random(1, 150), random(1, 150), random(1, 150));
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

Robin2

breaks the serial input.
I don't know what you mean by that. Can you provide an example of the correct input and the broken input?

Within the function you mention there is a call to ColorFromPalette() but that function is not in your code.


It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

sterretje

The FastLed library disables interrupts while it's updating the led strip. So if you send a longer command over serial, you will miss characters.

What you need to do is send single characters and echo it back; once you see the echo, you can send the next character. Robin's examples don't cater for that if I'm not mistaken, but it's easy to implement.

Alternatively, use single character commands.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

mylegispotato

I wanted to try explain my problem better but couldn't do anything as every time I tried to troubleshoot more my program had been doing something else. I am losing my sanity here  :P

I basically removed everything, wrote it from the beginning and now I was able to print serial on start while leaving said function uncommented. My LEDs are being animated until I send something in serial monitor. Looks like whole arduino gets massive, unrecoverable lag when receiving data. I still haven't been able to make arduino respond to serial input.

Here is my updated code:
Code: [Select]
#include <FastLED.h>

#define LED_PIN     6
#define NUM_LEDS    30
int BRIGHTNESS = 32;
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

int UPDATES_PER_SECOND = 100;
CRGBPalette16 currentPalette;
TBlendType    currentBlending;


void setup() {
  delay( 1000 ); // power-up safety delay
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(BRIGHTNESS);

  currentPalette = CRGBPalette16(CRGB::Black);
  currentBlending = LINEARBLEND;

  Serial.begin(250000);
  Serial.println("My Sketch has started");
}

int red = 255;
int green;
int blue;
static uint8_t startIndex = 1;
bool animationDirection = true;

bool stringComplete = false;
String inputString = "";
bool recievedSomething = false;


void loop() {
  //////////////////////
  //Input-side:
  ////////////////////
  // send data only when you receive data:
  if(Serial.available()){
    if (stringComplete) {
      stringComplete = false;
      Serial.println(inputString);
    }
 
    if(recievedSomething){
      Serial.println("Recieved something");
      recievedSomething = false;
    }
  }


  //////////////////////
  //Processing-side:
  //////////////////////
  if (animationDirection) {
    startIndex = startIndex + 1; /* motion speed */
  } else {
    startIndex = startIndex - 1; /* motion speed */
  }
  FillLEDsFromPaletteColors(startIndex); // THIS BREAKS SERIAL INPUT!!!
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
 
  //SetupRedAndWhite();
  SetSolidColor(0,0,255);
 
  delay(10);
}

void SetSolidColor(int red, int green, int blue){
   CRGB outputColor = CRGB(red, green, blue);
   fill_solid(currentPalette, NUM_LEDS, outputColor);
   currentPalette[0] = CRGB::Green;
   currentPalette[8] = CRGB::Green;
}

void SetupRedAndWhite() {
  fill_solid(currentPalette, NUM_LEDS, CRGB::Red);
  currentPalette[0] = CRGB::White;
  currentPalette[8] = CRGB::White;
}

void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
  uint8_t brightness = 255;

  for ( int i = 0; i < NUM_LEDS; i++) {
    leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending);
    colorIndex += 8; // 255 Color Slots / this number = NUM_LEDS
  }
}

void serialEvent() {
  while (Serial.available()) {
    recievedSomething = true;
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}


I have noticed that starting phrase gets send only in half when using 9600 baud, but sends in 100% when using higher values.
 
Quote
if(recievedSomething){
      Serial.println("Recieved something");
      recievedSomething = false;
    }
never runs. Don't know what to do next, but I am starting to understand serial communication, so that's good.

And yeah, ColorFromPalette() is magic FastLED function that I have no idea what does, but it works.
I don't think using String is a problem here, when I changed my code that it doesn't use them there is no change in program behavior.

I need a break, I'll update you if I find anything more, but I'd be glad if you could suggest a solution.

Paul_KD7HB

Quote: "I wanted to try explain my problem better but couldn't do anything as every time I tried to troubleshoot more my program had been doing something else. ".

That tells us you did not begin testing your program as you wrote it, section by section. That would have let you discover the problems as they were created and then you have a chance to fix them before they multiply.

Paul

david_2018

Code: [Select]

 if(Serial.available()){
    if (stringComplete) {
      stringComplete = false;
      Serial.println(inputString);
    }
  
    if(recievedSomething){
      Serial.println("Recieved something");
      recievedSomething = false;
    }
  }

It is very unlikely that Serial.available() will ever be true at this point, because your serialEvent() function removes the characters from the input buffer as long as Serial.available() is true, leaving it in a false state.  serialEvent() is run at the end of loop(), immediately before restarting at the top of loop(), so this test of Serial.available() will only be true if you receive a character within that window of a few microseconds.

mylegispotato

It is very unlikely that Serial.available() will ever be true at this point, because your serialEvent() function removes the characters from the input buffer as long as Serial.available() is true, leaving it in a false state.  serialEvent() is run at the end of loop(), immediately before restarting at the top of loop(), so this test of Serial.available() will only be true if you receive a character within that window of a few microseconds.
When I remove Serial.available() if statement my LEDs never light up, to me it looks like program gets stuck somewhere. I am so confused... Can I Serial.println() in void serialEvent() so I can at least see what input does my arduino receive? Because whenever I use Serial.println() LEDs don't even light up and Serial Monitor spits out "My Sketch has started" repeatedly like it's bootlooping.

mylegispotato

Okay, I somehow finally made it work!

I don't know why but introducing a simple timer to my program is enough that FastLED.show() works properly while still being able to read data from serial.
This is my code, it runs in loop, every 5 cycles. It prints input to Serial Monitor properly and LED animation never slows down. This is indeed weird, but probably justified somewhere deep in FastLED library. Right now it works with single characters, next I'm going to try using strings.
Code: [Select]
  if(timer > 5){
    Serial.println(input);
    input = Serial.read();
    switch(input){
    case 49: menuPos = 0; break; //1
    case 50: menuPos = 1; break; //2
    case 51: menuPos = 2; break; //3
    case 52: menuPos = 3; break; //4
    case 53: UPDATES_PER_SECOND = UPDATES_PER_SECOND + 1; break; //5
    case 54: UPDATES_PER_SECOND = UPDATES_PER_SECOND - 1; break; //6
    case 55: BRIGHTNESS = BRIGHTNESS * 2; break; //7
    case 56: BRIGHTNESS = BRIGHTNESS / 2; break; //8 
    }
    timer = 0;
  }else{
    timer++;
  }


  FillLEDsFromPaletteColors(startIndex);

  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);

Go Up