hall efeect sensor and WS2812B RGB LED

Hello Guys,

I am having a bit of a problem here in programming, I am doing a project which is measuring the RPM of a wheel using hall sensor and then when the speed goes from certain value the WS2812B changes colour as in like from RED to ORANGE and it goes faster it changes to GREEN. Now I can do this projects individually but when I combined them the RGB LED is not lighting at all.

Now, from what I researched it says that we can't use one Arduino for this project because the FASTLED.h library that I am using for the RGB LED is stalling the interrupt of the sensor and so they can't work together and that I have to use 2 Arduinos.

can someone clarify this for me please? like if there is any chance that I can use other library rather than FastLED.h? thanks

Yes, the FastLED.h library disables interrupts while it updates the LEDs. I think all the WS2812 libraries do (getting the data to them is pretty time critical).

And yes, reading the hall sensor could also be done using interrupts - this depends on the expected rpm range of the wheel you're measuring.

There's probably a way to make them work gracefully together if you take the appropriate precautions.

Helping you would be easier if you shared your code.

I would think it's really important to know the minimum time period between Hall interrupts (determined by maximum rotation speed) versus the maximum time required to update the LEDs (determined by number of LEDs in strip).

If the updated time is sufficient short (relative to the interrupt period), you could call FastLED.show() from the ISR. Don't do it on every interrupt -- every 'N' interrupts, compute a new RPM, update the CRGB array, and call FastLED.show().

As you said, interrupts will be disabled during the update anyway. Even if you miss the next interrupt, it will be queued for when you exit the ISR. As long as 'N' is sufficiently large and the update time shorter than two interrupt periods, it shouldn't be a problem.

Yes, the FastLED.h library disables interrupts while it updates the LEDs. I think all the WS2812 libraries do (getting the data to them is pretty time critical).

All the ones that i know do ! although i think it must be possible to use the UART to send the signal, (no start or stop bits and create a 50us break by switching the BAUD-rate it should be possible) but i don't know of a library doing it that way.

If the updated time is sufficient short (relative to the interrupt period), you could call FastLED.show() from the ISR. Don't do it on every interrupt -- every 'N' interrupts, compute a new RPM, update the CRGB array, and call FastLED.show().

In fact if you use only a few led's the update time will be short enough, do all the calculations , update the CRGB array, set a volatile latch 'toupdate'.
From the ISR only call FastLED.show() (if the latch was set, and turn the latch off afterwards) If you want to light up more leds, you can just parallel connect the data pin (since you want them the same color anyway)
An other option is to do it alternating, find an rpm by a set minimum amount of pings (ii figure now you are doing an average over a certain amount off the last received pings) , and calculate the color etc. , send the FASTLED.show() and start over. your refresh rate will be less.

#include <FastLED.h>
#define LED_PIN     8
#define NUM_LEDS   256 
 
CRGB leds[NUM_LEDS]; 
const int hallPin = 2;
int ledpin = 3; //LED on D3 will show blink on/off
int hallState;
float radius_of_wheel = 0.33;
volatile byte rotation;
float timetaken, rpm, dtime;
int v;
unsigned long previoustime;
int i=0;
volatile bool ledState = LOW;
void setup(){
  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
  attachInterrupt(digitalPinToInterrupt(hallPin), magnet_detect, RISING);
  attachInterrupt(digitalPinToInterrupt(hallPin), hall_ISR, CHANGE);
  rotation= rpm= previoustime = 0;
  pinMode(hallPin, INPUT);
  pinMode(ledpin, OUTPUT);
  //pinMode(LED_PIN, OUTPUT);
}
void loop(){
  if(millis()-dtime>3500) //no magnet detected for 3500ms
  {
    rpm= v= 0;
    dtime= millis();
  }
  digitalWrite(ledpin, ledState); 
  v= radius_of_wheel* rpm* 0.37699;
  hallState  = digitalRead(hallPin);
  if(hallState ==LOW){
//    digitalWrite(ledpin, HIGH);
    for(int i=0; i<=255; i++){
    leds[i] = CRGB(255,115,0);
    FastLED.show(); 
    delay(1);
    }   
  }
  else{
    //digitalWrite(ledpin, LOW);
    for(int i=255; i>=0; i--){
    leds[i] = CRGB(0,0,0);
    FastLED.show();
    delay(1);
    }
  }
}
void hall_ISR(){
  ledState = !ledState;
}
void magnet_detect(){
  rotation++;
  if(rotation>=2)
  {
    timetaken = millis()- previoustime;
    rpm = (1000/timetaken)*60;
    previoustime =millis();
    rotation = 0;
  }
}
//void lighting(){
//  if(v<=7.5){
//    leds[i] = CRGB(255,0,0);
//  }
//  else if(v>=15){
//    leds[i] = CRGB(0,255,0);
//  }
//  else {
//    leds[i] = CRGB(255,255,0);
//  }
//}

Hello Guys

Thanks for your reply, yeah as you all said it there is some complications on interrupt and the FastLED library. So here is my project I can't cut the LEDs as they are in a panel and they are 256 LEDs individually addressable LEDs.

Just to see my code I am putting it here. I have done a lot of code but I will post one which I think is easier to understand.

256 leds is a lot, i am not sure what you expect as you minimum time between pings, but even at 800khz 24bits * 256 leds takes a fair amount of time (20 ms or something not sure about the exact amount but a test program to figure that one out is easy enough) But when you start doing things like this

    for(int i=0; i<=255; i++){
    leds[i] = CRGB(255,115,0);
    FastLED.show(); 
    delay(1);
    }

It all stops ! not to mention the delay ! this will light them up linear one by one by one , but since the whole frame already takes way more than the 1ms that one really shouldn't be there. (also now it is clear you don't want them all to light up at the same time the same way, or do you ?) so i suggest doing it either using 2 arduino's or determine the rpm, calculate the color, send the signal, and after that set your rotation to 0, so you can measure the interval between 2 rotations from then on. and only send the ledstrip signal if anything needs to change.

i actually did some test and came to

Number of leds : 256 uSecs : 4412

from this code

uint32_t mics=micros();
  FastLED.show();
  mics=micros()-mics;
  Serial.print("Number of leds : ");
  Serial.print(NUM_LEDS,DEC);
  Serial.print(" uSecs : ");
  Serial.println(mics,DEC);

so it takes about 4.5ms still it is quite a lot !

Hello Good Member

Thanks for your time, I will try what you send me and I will let you know.

Hello Deva Rishi
I will try it that way thanks for your time and about what you said earlier it doesn't matter how the LED lights up, if there is a difference in lighting all the LEDs at the same time I wont mind doing that.

Hello Deva Rishi

I am sorry to ask you this but what is the last code that you sent me, is it something that I have to add to my code or a test for the timing?

The simplest solution may be to alternate back and forth between obtaining an RPM sample (using interrupts) and displaying the result (using FastLED).

Even simpler: use pulseIn() to measure RPM.

hello MorganS

Is this pulseln() command instead of attachinterrupt()? thanks

Yes. When you want to stop updating the LEDs and take an RPM measurement, pulseIn() can be used to determine the time between pulses.

But looking at your code again, it is not all bad news.

This part looks wrong...

  hallState  = digitalRead(hallPin);

if(hallState ==LOW){
//    digitalWrite(ledpin, HIGH);
    for(int i=0; i<=255; i++){
    leds[i] = CRGB(255,115,0);
    FastLED.show();
    delay(1);
    } 
  }
  else{
    //digitalWrite(ledpin, LOW);
    for(int i=255; i>=0; i--){
    leds[i] = CRGB(0,0,0);
    FastLED.show();
    delay(1);
    }

The hall sensor will be HIGH almost all the time. Maybe 99% of the time. So you mostly update the LEDS to black.

There is no need to add the earlier code i posted to your code, but this loop needs to change

    for(int i=0; i<=255; i++){
    leds[i] = CRGB(255,115,0);
    FastLED.show(); 
    delay(1);
    }

It sends the signal 255 times !! Ooops.. so just change that to

    for(int i=0; i<=255; i++){
    leds[i] = CRGB(255,115,0);

    }   
    FastLED.show();

this at least, but still you'll be turning off interrupts for about 4.5ms which isn't so bad as long as the minimum time between pings is less than that. (which it probably is)
Now To turn a different corner, This :float timetaken, rpm, dtime; needs to be

volatile float rpm, timetaken;
float  dtime;

timetaken is updated within one of the interrupts functions, and so is rpm.
I think you should try the suggestion i made in reply #3.
-Do the calculating as you have at the moment.
-Compare the result to any previous result so you only change the led color when you have to.
(with the for loop setting bytes in the Array) and if so, set a bool variable sendledsignal to true
-remove all calls to FastLed.show() and only call it from the interrupt if the sendledsignal is true;

I think the the idea to have the leds flash with every change of the magnet is optimistic, the magnet will probably pass to fast and i think you should get rid of the HALL_ISR all together or just use it to ping the led, but since after the magnet detect is called we may want to fire FastLED.Show() it may miss the change
All together i came to this

#include <FastLED.h>
#define LED_PIN     8
#define NUM_LEDS   256

CRGB leds[NUM_LEDS];
const int hallPin = 2;
int ledpin = 3; //LED on D3 will show blink on/off
int hallState;
float radius_of_wheel = 0.33;
volatile byte rotation;
volatile float timetaken, rpm;
volatile bool sendsignal = true;
float dtime;
int v, oldcolor = 0;
unsigned long previoustime;
int i = 0;
volatile bool ledState = LOW;

void setup() {
  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
  attachInterrupt(digitalPinToInterrupt(hallPin), magnet_detect, RISING);
  attachInterrupt(digitalPinToInterrupt(hallPin), hall_ISR, CHANGE);
  rotation = rpm = previoustime = 0;
  pinMode(hallPin, INPUT);
  pinMode(ledpin, OUTPUT);
}

void loop() {
  if (millis() - dtime > 3500) //no magnet detected for 3500ms
  {
    rpm = v = 0;
    dtime = millis();
  }
  digitalWrite(ledpin, ledState);
  v = radius_of_wheel * rpm * 0.37699;
  hallState  = digitalRead(hallPin);
  if (hallState == LOW) {
    digitalWrite(ledpin, HIGH);
  }
  else {
    digitalWrite(ledpin, LOW);
  }
  lighting();
}


void hall_ISR() {
  ledState = !ledState;
}
void magnet_detect() {
  if (sendsignal) {
    FastLED.show();
    sendsignal=false;
  }
  
  rotation++;
  if (rotation >= 2)
  {
    timetaken = millis() - previoustime;
    rpm = (1000 / timetaken) * 60;
    previoustime = millis();
    rotation = 0;
  }
}

void lighting() {
  if (v <= 7.5) {
    if (oldcolor != 1) {
      fill_solid(leds, NUM_LEDS, CRGB(255, 0, 0));
      oldcolor = 1;
      sendsignal = true;
    }
  }
  else if (v >= 15) {
    if (oldcolor != 2) {
      fill_solid(leds, NUM_LEDS, CRGB(0, 255, 0));
      oldcolor = 2;
      sendsignal = true;
    }
  }
  else {
    if (oldcolor != 3) {
      fill_solid(leds, NUM_LEDS, CRGB(255, 255, 0));
      oldcolor = 3;
      sendsignal = true;
    }
  }
}

try this.

Hello guys

I really appreciate for your time, I will try to modify it now. Thank you very much.