Adafruit NeoMatrix mess up Serial RX

Hello,

I am trying to control Adafruit NeoMatrix from Android App using HC-05 Bluetooth module, but Arduino don't get valid data. Arduino randomly miss some some character. Sometime more, sometime less.

First I tried to switch to USB and Serial monitor instead of Bluetooth module, to see if HC-05 mess up with message, but problem was still present.

After that I was think that maybe amount of data is too big for Serial internal buffer, so I switched to NeoHWSerial and implement interrupt function to handle receiving data. That did not solved problem.

I ended removing code from loop until I left only with NeoMatrix. When I comment out NeoMatrix code, Serial communication worked, when I uncomment it, problem occurs.

I'm not sure what else could I try. I am not sure if I'm doing something wrong, is it problem with NeoMatrix library, or maybe Arduino is too slow to handle this use case.

[code]
#include <NeoHWSerial.h>

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <Fonts/FreeSans12pt7b.h>
#ifndef PSTR
 #define PSTR // Make Arduino Due happy
#endif

#define PIN 52

#define STATE_COLOR           0
#define STATE_MUSIC           1
#define STATE_TEXT            2

#define EFFECT_1              1
#define EFFECT_2              2
#define EFFECT_3              3
#define EFFECT_STROBOSCOPE    101

// MATRIX DECLARATION:
// Parameter 1 = width of NeoPixel matrix
// Parameter 2 = height of matrix
// Parameter 3 = pin number (most are valid)
// Parameter 4 = matrix layout flags, add together as needed:
//   NEO_MATRIX_TOP, NEO_MATRIX_BOTTOM, NEO_MATRIX_LEFT, NEO_MATRIX_RIGHT:
//     Position of the FIRST LED in the matrix; pick two, e.g.
//     NEO_MATRIX_TOP + NEO_MATRIX_LEFT for the top-left corner.
//   NEO_MATRIX_ROWS, NEO_MATRIX_COLUMNS: LEDs are arranged in horizontal
//     rows or in vertical columns, respectively; pick one or the other.
//   NEO_MATRIX_PROGRESSIVE, NEO_MATRIX_ZIGZAG: all rows/columns proceed
//     in the same order, or alternate lines reverse direction; pick one.
//   See example below for these values in action.
// Parameter 5 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_GRBW    Pixels are wired for GRBW bitstream (RGB+W NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)

// Width is 64 because two NeoMatrix is connected together.
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(64, 8, PIN,
  NEO_MATRIX_TOP     + NEO_MATRIX_LEFT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800);

int state = 0;
int prev_state = 0;
int effect = 1;
int prev_effect = 1;
int text_effect = 1;
int text_x = matrix.width();
String text = "Test";
int r = 255;
int g = 0;
int b = 0;

int raw_value = 0;
int value = 0;
int count = 0;

char buffer[255];
int buffer_pos = 0;

void readSensors() {
  raw_value = analogRead(0);
  value = map(raw_value, 0, 1024, 0, 255);
}

void render(uint16_t color, uint16_t textColor, int bright) {
  matrix.setBrightness(bright);
  matrix.fillRect(0,0, 512 ,8, color);

  matrix.setCursor(10, 0);
  matrix.setTextColor(textColor);
  matrix.setFont(&FreeSans12pt7b);
  matrix.print(text);
}

bool checkBTCommands(uint8_t ch, uint8_t status) {
  if (ch != '\n') {
    buffer[buffer_pos] = ch;
    buffer_pos++;
    //NeoSerial.print(ch);
    return false;
  }

  buffer[buffer_pos] = '\0';
  NeoSerial.println(buffer);

  //Serial.println(buffer);

  char *p = buffer;
  char *str;
  byte i = 0;

  String data[2];
  while ((str = strtok_r(p, "=", &p)) != NULL) {
    if (i < 2) {
      data[i] = str;
    }
    i++;
  }

  if (data[0] == "state") {
    int val = data[1].toInt();
    prev_state = state;
    state = val;
  } else if (data[0] == "r") {
    int val = data[1].toInt();
    r = val;
  } else if (data[0] == "g") {
    int val = data[1].toInt();
    g = val;
  } else if (data[0] == "b") {
    int val = data[1].toInt();
    b = val;
  } else if (data[0] == "r") {
    int val = data[1].toInt();
    r = val;
  } else if (data[0] == "effect") {
    int val = data[1].toInt();
    prev_effect = effect;
    effect = val;
  } else if (data[0] == "text_effect") {
    int val = data[1].toInt();
    text_effect = val;
  } else if (data[0] == "text") {
    text = data[1];
  }

  buffer[0] = '\0';
  buffer_pos = 0;

  return false;
}

void displayColor() {
  //matrix.clear();
  //matrix.fillScreen(0);
  matrix.setBrightness(255);
  matrix.fillScreen(matrix.Color(r, g, b));
  //matrix.fillRect(0, 0, 64, 8, matrix.Color(r, g, b));
  //matrix.show();
}

void displayEffect1() {
  //matrix.clear();
  //matrix.fillScreen(0);
  matrix.setBrightness(value);
  matrix.fillScreen(matrix.Color(r, g, b));
  //matrix.fillRect(0, 0, 64, 8, matrix.Color(r, g, b));
  //matrix.show();
}

void displayEffectStroboscope() {
  int i = 500;
  int j = 0;
  
  while (i > 0) {
    //matrix.clear();
    //matrix.fillScreen(0);
    matrix.setBrightness(j % 2 == 0 ? 255 : 0);
    matrix.fillRect(0, 0, 64, 8, matrix.Color(255, 255, 255));
    //matrix.show();
    
    delay(i);

    if (i > 200) {
      i -= 100;
    } else {
      i -= 10;
    }
    j++;
  }
  
  effect = prev_effect;
}

void displayMusic() {
  switch (effect) {
    case EFFECT_1:
    case EFFECT_2:
    case EFFECT_3:
      displayEffect1();
      break;
    case EFFECT_STROBOSCOPE:
      displayEffectStroboscope();
      break;
  }
  matrix.show();
}

void displayText() {
  matrix.clear();
  matrix.fillScreen(0);
  matrix.setBrightness(255);

  if (text_effect != 2) {
    text_x = 0;
    return;
  }

  matrix.setCursor(text_x, 0);
  matrix.setTextColor(matrix.Color(r, g, b));
  matrix.print(text);

  matrix.show();

  if (text_effect == 2) {
    text_x--;
    if (text_x < -36) {
      text_x = matrix.width();
    }
  }
}

void setup() {
  buffer[0] = '\0';
  
  NeoSerial.begin(9600);

  NeoSerial1.attachInterrupt(checkBTCommands);
  NeoSerial1.begin(9600);

  //NeoSerial.attachInterrupt(checkBTCommands);
  //NeoSerial.begin(9600);
  
  matrix.begin();
  matrix.setTextWrap(false);
}

void loop() {
  //readSensors();
  switch (state) {
    case STATE_COLOR:
      displayColor();
      break;
    case STATE_MUSIC:
      readSensors();
      displayMusic();
      break;
    case STATE_TEXT:
      displayText();
      break;
  }
}
[/code]

I am using Arduino Mega for this project.

Kind regards!

I found out that NeoPixel library disable interrupt, because communication with time sensitive and they not using timer to do it.

So I will try to modify library and remove code that disable interrupts. That way Serial will probably mess up communication with LED matrix and rendering, but that should not be issue. If it's mess it up in one iteration, theoretically it should render correctly in next iteration.

I hope that this will solve my problem.

The interrupts are probably disabled on when the matrix.show() is called. If you minimise the calls to matrix.show() to be only when the data to show has changed (ie, not every time through loop()) then this is also likely to fix your problem.

That is likely impossible. The timing for driving the LEDs is very tight, and is implemented in assembly language, which is why interrupts need to be disabled.

In addition to marco_c's suggestion of minimizing the calls to matrix.show(), you might also want to check to see if any characters have been received on serial before calling matrix.show(), and if so hold off on updating the matrix until the full serial message has been received.

I managed to change NeoPixel library so that it doesn't disable interrupts.
Serial communication now works just fine.

I had problem with random LED flickering and changing colors. This was happening because Arduino use TIMER0 interrupt by default for functions like milis(), micros() and delay().

I manually changed registers to disable only timer interrupt and that solved all my problems. Everything works fine right now. In theory flickering can happen when receiving message from Serial, but that is not a problem. Massage anyway should change what needs to be displayed. So if small flickering happen it will not be noticeable.

I enable TIMER0 only when I need delay function. I use it only in one function for stroboscope. And, in that mode flickering is not a big deal. It's not noticeable at all. If it start to make a problem, I will implement my own delay that not using timer, similarly to one in AVR utils.

Thank you @david_2018 and @marco_c for help.

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