Go Down

Topic: Problems receiving serial data (Read 550 times) previous topic - next topic

fry

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:
Code: [Select]
<{"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:
Code: [Select]

#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):
Code: [Select]

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!

Robin2

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
Code: [Select]
  if (! Serial.available()) {
    if (brightne


why are you testing for serial here?

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

fry

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:

Code: [Select]

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:
Code: [Select]

<{"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.


fry

#3
May 13, 2017, 09:21 pm Last Edit: May 13, 2017, 09:34 pm by fry
Okay, just found out that if I comment out the strip.show() in the loop everything works fine...
So why does this interfere?

Quote
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?

Robin2

#4
May 13, 2017, 11:03 pm Last Edit: May 13, 2017, 11:06 pm by Robin2
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
Code: [Select]
  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
Two or three hours spent thinking and reading documentation solves most programming problems.

PaulS

Quote
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.
The art of getting good answers lies in asking good questions.

Robin2

Sending so much data to accomplish so little is not a good idea.
Agree. I had overlooked that.

Rather than sending
Code: [Select]
<{"led":"boden", "brightness":200, color:[200, 2, 200]}>
just send something like
Code: [Select]
<b,200,200,2,200>
where the 'b' is short for "boden" (whatever that is).

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

fry

Hi, thanks.

Quote
Sending so much data to accomplish so little is not a good idea.
Okay, I will change that.

Quote
I suspect it is not strip.show() that is the problem but the fact that you call it 256 times in a FOR loop
Code: [Select]

  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.

Robin2

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
Two or three hours spent thinking and reading documentation solves most programming problems.

fry

If I'm not issuing the strip.show() during the for loop the colors wouldn't move along the stripe.
https://www.youtube.com/watch?v=qUL7x_0r-bA

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

Code: [Select]

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 (https://github.com/madsci1016/Arduino-EasyTransfer) 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.

Robin2

#10
May 14, 2017, 09:20 pm Last Edit: May 14, 2017, 09:21 pm by Robin2
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
Two or three hours spent thinking and reading documentation solves most programming problems.

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.

Robin2

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
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up