pulse count: weird behaviour when WS2812B enabled

Hi Folks,

I'm working on something that must do the following:

A prox sensor picks up pulse from a rotating device.
A variable (NbLED) is increased by 1
Every 250 mSec, that same variable is decreased by one
A strip of 30 WS2812B LED is lit according to NbLED (red for the first 15 LEDs, followed by yellow and green)

The idea, is that pulses must come in on a regular basis. If the pace at which they come in decrease, the number of LED lit will decrease.

The prox used is an inductive sensor, NPN (from Ebay: Prox Sw used). I have made a circuit that debounce the inputs.

So far, so good...

My problem is that when the attached code is running, a pulse from the prox will increase by more than 1 count. Strangely, the actual behaviour is that when detecting metal, the NbLED variable increase by 1, but when the metal is leaving, the count increase by 7-8 counts....

I have disabled the WS2812B portion, to replace it by a Serial.println that tells me the actual count: the count is increasing by only one, just as expected !

What the hell am I doing wrong ????

I have also attached the schematic of my skid....

#include <TimerOne.h>
#include <Adafruit_NeoPixel.h>

int Pin = 4;
int NbLED = 1;
int NumPixels = 30;

Adafruit_NeoPixel Pixels = Adafruit_NeoPixel(NumPixels, Pin, NEO_GRB + NEO_KHZ800);


void setup() 
{
  Timer1.initialize(250000);                 // set a timer of length 250000 microseconds (or 0.25 sec)
  Timer1.attachInterrupt( timerIsr );  
  Pixels.begin(); 
  attachInterrupt(digitalPinToInterrupt(3),Pulse, FALLING);

}

void loop() 
{

 
if(NbLED==0)
  {
    NbLED = 1;
  } 
  
if(NbLED >15)
  {
    if(NbLED >25)
      {
        for(int i=0;i<15;i++)
          {
            Pixels.setPixelColor(i, 255,0,0); //Red
            Pixels.show(); 
          }  
        for(int i=15;i<25;i++)
          {
            Pixels.setPixelColor(i,255,150,0);//Yellow
            Pixels.show(); 
          }
        for(int i=25;i<NbLED;i++)
          {
            Pixels.setPixelColor(i,100,255,0); //Green
            Pixels.show(); 
          }
        for(int i=NbLED;i<NumPixels;i++)
          {
            Pixels.setPixelColor(i,0,0,0); // Blank
            Pixels.show(); 
          }  
      }
     else 
      {
        for(int i=0;i<15;i++)
          {
            Pixels.setPixelColor(i, 255,0,0); //Red
            Pixels.show(); 
          }  
        for(int i=15;i<NbLED;i++)
          {
            Pixels.setPixelColor(i,255,150,0);  //Yellow
            Pixels.show(); 
          }
        for(int i=NbLED;i<NumPixels;i++)
          {
            Pixels.setPixelColor(i,0,0,0); //Blank
            Pixels.show(); 
          }
        }
   
  }
else
  {
    for(int i=0;i<NbLED;i++)
      {
        Pixels.setPixelColor(i, 255,0,0); 
        Pixels.show(); 
      }  
    for(int i=NbLED;i<NumPixels;i++)
      {
        Pixels.setPixelColor(i,0,0,0); 
        Pixels.show(); 
      }
  }
}

void Pulse()
{
NbLED = NbLED+1;
}

void timerIsr()
{

NbLED = NbLED - 1;

}

FYI: I use 2 libraries:

NeoPixel from Adafruit, to drive the WS2812B
Timer1: which allow time interrupt easily

oops...

File was too big

resized

Global variables that you use in interrupt routines should be volatile. I'm not sure if it's the cause of the problem.

volatile int NbLED = 1;

I'm not familiar with the NeoPixel library; but is it possible that all the Pixels.show() take too much time? Maybe this will work better (but I seriously ain't sure)

    for (int i = 0; i < NbLED; i++)
    {
      Pixels.setPixelColor(i, 255, 0, 0);
    }
    Pixels.show();

There is no attachment :wink:

sterretje: I was with the impression that both interrupt should prevail over the Loop routine, therefore the Neopixel portion should not have any impact ...or does it ?

I will try that volatile thing tomorrow !

Why are you calling Pixels.show() so many times?

NeoPixels have to have the entire string updated at once (and interrupts will probably be off because they are very timing-dependent). Doing each colour separately isn't saving time, it's wasting it.

I don't think NeoPixles like to be interrupted during the show() method. I don't know if the drivers deal with this or not. And Sterretje is right. You only want to call show() once after you set all the colors.

They work kinda' like an offscreen drawing buffer. You set all the colors the call show() to "draw" all of them out to the LEDs.

-jim lee

jasmino:
sterretje: I was with the impression that both interrupt should prevail over the Loop routine, therefore the Neopixel portion should not have any impact ...or does it ?

Interrupts do prevail over normal loop code. But normal loop code continues where it was after an interrupt is finished.

So if NbLED was 27 the first time in the beginning of loop (and you enter the if (NbLED > 25) block) and decreases to 15 by the time you reach for (int i = 25; i < NbLED; i++), what will happen? Making a copy of NbLED in the beginning of loop and using that copy will at least prevent that problem (if it is one).

I would first test the timing of your NeoPixel 'actions'

...
...
void setup()
{
  Serial.begin(115200);
  NbLED = 30; // set to max number of leds

  unsigned long startTime = millis();

  for (int i = 0; i < 15; i++)
  {
    Pixels.setPixelColor(i, 255, 0, 0); //Red
    Pixels.show();
  }
  for (int i = 15; i < 25; i++)
  {
    Pixels.setPixelColor(i, 255, 150, 0); //Yellow
    Pixels.show();
  }
  for (int i = 25; i < NbLED; i++)
  {
    Pixels.setPixelColor(i, 100, 255, 0); //Green
    Pixels.show();
  }
  for (int i = NbLED; i < NumPixels; i++)
  {
    Pixels.setPixelColor(i, 0, 0, 0); // Blank
    Pixels.show();
  }

  Serial.println(millis() - startTime);
}

void loop()
{
}

// no interrupt service routines !!

If the result is 0 (I doubt it), change millis() to micros(). This will give you an idea how fast your NeoPixel updates are.

If the NeoPixel library uses interrupts you might have another problem where you don't pick up changes in your sensor immediately and your timer ISR will trigger later than you think :wink:

sterretje: Volatile didn't change anything, but was worth the try !

Nick Gammon: now using only one instance of Pixels.show(). No apparent change for now, but probably a better practice !

jimLee: I will try something tonight: Detach the interrupt before the Neopixel, then attach after. Sure, I may loose an incoming pulse, but loosing one is better than getting 7-8 for no reason !

sterretje: Will try to "time" the Neopixel tonight.

I will keep you posted folks, and thanks for you help !

jasmino:
I have made a circuit that debounce the inputs.

So far, so good...

My problem is that when the attached code is running, a pulse from the prox will increase by more than 1 count. Strangely, the actual behaviour is that when detecting metal, the NbLED variable increase by 1, but when the metal is leaving, the count increase by 7-8 counts....

So what is this debouncing circuit? Sounds like it isn't working.

MarkT: as I mentioned, if I use the pulse only, and look at the count, it is working fine !

look at #2, there is a drawing of my circuit.

the problem occurs when I try to drive the Neopixel at the same time.

MarkT: as I mentioned, if I use the pulse only, and look at the count, it is working fine !

look at #2, there is a drawing of my circuit.

the problem occurs when I try to drive the Neopixel at the same time.

I am suspicious that the interaction between the Neopixel lights and the proximity sensor being tied at the output of the LM7805 rather than than coding of the interrupts. I suspect the current draw of the lights may be causing the input side of the optical isolator to switch on and off and generates an interrupt. Try to isolate the proximity sensor operation from the lights.

OP's circuit

ok...

Debounce circuit wasn't so good after all !!!!!

Plus, using it with the Inductive sensor was generating a lot of "false positive". I still don't get it why my "trap" wasn't picking them up ....

Tried it with a limit switch : even worst !

Ended-up using a Hall Effect sensor (OH3144), with my debounce circuit + a 7414 Schmidt Trigger.

Then and only then was I getting a "clean" pulse count....

Thanks to all of you. At the end, I have made a few changes from the original code (volatile, less Pixel.show, etc ..)