Problems receiving serial data

Hi,

I just can't figure out what I'm doing wrong so I'm going to ask for help here.

I'm trying to control some ws2812b LEDs connected to an arduino nano via serial.
I'm sending json formated strings like:

<{"led":"boden", "brightness":200, color:[200, 2, 200]}>

Most of the time the first string that is sent works, but after that I'm only receiving garbage.
Could someone give me a hint why this is happening?
Also tried different serial speeds.

This is my code:

#include <Arduino.h>
#include <ArduinoJson.h>

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define SERIAL_BUFFER_SIZE 256

#define PIN 3


Adafruit_NeoPixel strip = Adafruit_NeoPixel(300, PIN, NEO_GRB + NEO_KHZ800);


const byte numChars = 128;
char receivedChars[numChars];
char tempChars[numChars];
boolean newData = false;

uint8_t brightness = 255;
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;

void setup() {
  Serial.begin(9600);

  strip.begin();
  strip.show();
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      Serial.print(rc);
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
        Serial.println(" ");
      }
    } else if (rc == startMarker) {
      recvInProgress = true;
      Serial.print("Serial received: ");
    }
  }
}

void parseData() {
  Serial.print("parseData(): ");
  Serial.println(tempChars);
  StaticJsonBuffer<255> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(tempChars);

  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  if (root["led"] == "boden") {
    if (root.containsKey("brightness")) {
      brightness = atoi(root["brightness"]);
    }

    if (root.containsKey("color")) {
      if (root["color"][0] != NULL) {
        red = atoi(root["color"][0]);
      }
      if (root["color"][1] != NULL) {
        green = atoi(root["color"][1]);
      }
      if (root["color"][2] != NULL) {
        blue = atoi(root["color"][2]);
      }
    }
  }
}

void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    parseData();
    newData = false;
  }

  // while (Serial.available()) {
  //   Serial.write(Serial.read());
  // }

  if (! Serial.available()) {
    if (brightness && !(red || green || blue)) {
      rainbow(20, brightness);
    } else {
      setColor(red, green, blue);
      strip.setBrightness(brightness);
      strip.show();
    }
  }
}

void setColor (uint8_t r, uint8_t g, uint8_t b) {
  uint16_t i;
  for(i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, r, g, b);
    strip.setBrightness(brightness);
    strip.show();
  }
}

void rainbow(uint8_t wait, uint8_t brightness) {
  uint16_t i, j;
 strip.setBrightness(brightness);
  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
      if (Serial.available()) {
        return;
      }
    }
    strip.show();
    delay(wait);
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

And here the serial output I get (first transmission is ok):

Serial received: {"led":"boden", "brightness":200, color:[200, 2, 200]}>
parseData(): {"led":"boden", "brightness":200, color:[200, 2, 200]}
Serial received: {edn"ht200r:, >
parseData(): {edn"ht200r:, 
parseObject() failed<\r><\n>
Serial received: {"lderi":lo, 2}> 
parseData(): {"lderi":lo, 2}
parseObject() failed
Serial received: {d"n"ht00r:[, >
parseData(): {d"n"ht00r:[, 
parseObject() failed
Serial received: {":, tne0,[220>
parseData(): {":, tne0,[220
parseObject() failed

Hope someone can help.
Thanks!

I am not familiar with that JSON library. What happens if you just receive the data and don't bother to parse it - do you get all the messages then?

What is the purpose of this

  if (! Serial.available()) {
    if (brightne

why are you testing for serial here?

...R

Hi,

I'm not testing for serial there, I'm testing against.
Added this for debuging, to sort out if the rest of the loop is causing some of the issue.

Changed my loop to:

void loop() {
  while (Serial.available()) {
    Serial.write(Serial.read());
  }

  if (! Serial.available()) {
    if (brightness && !(red || green || blue)) {
      rainbow(20, brightness);
    } else {
      setColor(red, green, blue);
      strip.setBrightness(brightness);
      strip.show();
    }
  }
}

Input is better, but still with some missing parts:

<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"boden", "brightness":200, color:[200, 2, 200]}>
<{d":"boden", "brightness":200, color:[200, 2, 200]}>
<{:"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>

Since you mentioned the json library the problem is present before the parsing is done.
Serial received: {edn"ht200r:, >
This is whats received and will be used for parsing.
So this has nothing to do with the library a that point.

Okay, just found out that if I comment out the strip.show() in the loop everything works fine...
So why does this interfere?

The neopixel library does not use timers. However, it does disable interrupts during strip.show() operations due to the critical timing requirements of the neopixel led controllers. This has been known to mess up timers and other interrupt-driven code.

Well I think that's the explanation than, right?
Any suggestion what I could do?

fry:
Okay, just found out that if I comment out the strip.show() in the loop everything works fine...

Sorry. I have never used the neopixel library.

I suspect it is not strip.show() that is the problem but the fact that you call it 256 times in a FOR loop

  for(j=0; j<256; j++) {

Just call it once and allow loop() to do the repetition and use a count variable to keep track of the iterations.

...R

I suspect it is not strip.show() that is the problem

Serial data can not be received when strip.show() is running. Calling it more than once per iteration of loop() isn't a good idea, and likely isn't necessary.

Sending json data is a waste of resources. Sending so much data to accomplish so little is not a good idea.

PaulS:
Sending so much data to accomplish so little is not a good idea.

Agree. I had overlooked that.

Rather than sending

<{"led":"boden", "brightness":200, color:[200, 2, 200]}>

just send something like

<b,200,200,2,200>

where the 'b' is short for "boden" (whatever that is).

...R

Hi, thanks.

Sending so much data to accomplish so little is not a good idea.

Okay, I will change that.

I suspect it is not strip.show() that is the problem but the fact that you call it 256 times in a FOR loop

  for(j=0; j<256; j++) {

Just call it once and allow loop() to do the repetition and use a count variable to keep track of the iterations.

Thats not possible. It creates a rainbow effect where the colors are constantly moving across the stripe.
So it hast to be called after every iteration.

fry:
Thats not possible. It creates a rainbow effect where the colors are constantly moving across the stripe.
So it hast to be called after every iteration.

I still suspect you are wrong. If loop() repeats (say) 100 times per second and does one iteration of strip.show() in each iteration won't that be sufficient?

What is the maximum acceptable interval between iterations of strip.show() ? Remember that cinema films that only show 24 frames per second give the illusion of smooth movement.

...R

If I'm not issuing the strip.show() during the for loop the colors wouldn't move along the stripe.

I changed my code so that the avr ask at the beginning of the loop for data and during the for loop.

void loop() {

  Serial.println("0");
  delay(2);
  if (ET.receiveData()) {
    brightness = mydata.brightness;
    red = mydata.red;
    green = mydata.green;
    blue = mydata.blue;
    // setColor(red, green, blue);
  }

  if (brightness && !(red || green || blue)) {
    rainbow(10, brightness);
  } else {
    setColor(red, green, blue);
    strip.setBrightness(brightness);
    strip.show();
  }

  // while (Serial.available()) {
  //   Serial.write(Serial.read());
  // }
}

void rainbow(uint8_t wait, uint8_t brightness) {
  uint16_t i, j;
 strip.setBrightness(brightness);
  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
      Serial.println("0");
      // delay(10);
      if (ET.receiveData()) {
        if (brightness != mydata.brightness) {
          brightness = mydata.brightness;
          strip.setBrightness(brightness);
        }
        if (red != mydata.red || green != mydata.green || blue != mydata.blue) {
          red = mydata.red;
          green = mydata.green;
          blue = mydata.blue;
          return;
        }
      }
    }
    strip.show();
    delay(wait);
  }
}

(using easytransfer (GitHub - madsci1016/Arduino-EasyTransfer: An Easy way to Transfer data between Arduinos) now to transfer the data between the two avrs)

The other avr caches the request and sends out the data.
Seems to work so far.

fry:
If I'm not issuing the strip.show() during the for loop the colors wouldn't move along the stripe.

You have not answered my question about how large the interval can be (how many millisecs) between successive calls to strip.show() and still get the effect you want.

...R

Since there is a delay of 20ms after each iteration which defines the speed the color is moving I would say something between 20-50ms.

fry:
Since there is a delay of 20ms after each iteration which defines the speed the color is moving I would say something between 20-50ms.

I missed that bit. 20 millisecs is like "forever" for an Arduino so there is plenty of time to do all the other stuff.

As well as getting rid of the FOR loop also get rid of the delay() and use millis() to manage the timing without blocking. Have a look at how it is done in Several Things at a Time

...R