NRF24 wireless lights, occasional glitches

Hi all,
I’m building some battery powered LED props for a stage production, and it’s progressing OK, but I’m running into some odd glitcches. The problems manifest themselves in two ways:

  1. Occasionally, maybe 15% of the time, the lights respond very slowly and/or fades appear laggy. I guess this is due to missed commands; I’m not very surprised at this
  2. Much stranger: sometimes the lights spontaneously go to 100% for no apparent reason, usually for approx one second at a time. I have absolutely no idea why.

The lights themselves consist of 6 lengths of constant voltage single colour LED strips, dimmed in groups of two by MOSFETs controlled by an “emakefun RF-Nano”, basically an Arduino Nano with an onboard NRF24. The contraption is powered by two “stick” type 9.6V 1600mAh airsoft batteries wired in series. An Aliexpress-style XL6009 module boosts the voltage to 24V for the LED strips, and a small DC-DC converter supplies 12V for the RF-Nano. In addition there is a voltage divider for monitoring battery voltage, and a pushbuttion so the thing can be controlled when there is no control signal.

The batteries and control electronics are all stuffed inside a 20mm steel pipe (.95mm wall thickness), which acts as a heatsink for the LED strips that are attached on the outside. I have made cutouts and slots around the RF-Nano, of course. The assembly is housed within a 40mm satin acrylic tube.

The transmitter is an Arduino Uno with a Sparkfun WRL-00705 module and an RS-485 shield it takes DMX512 from the lighting console and sends arrays of 4 ints to each of the receivers; master brightness and individuall brightnesses for each LED strip. The transmitter is equipped with this antenna mounted outside the chassis, connected to the NRF module via a 30cm RG58 cable.

The sketches for the transmitter and receiver are below. They’re cobbled together from various examples I’ve found – my typical way of doing things :wink:

I’m aware that they are both horribly messy, and I do intend to clean them up, but any suggestions on where to begin, hopefully to stop the lights from turning themselves on whey they shouldn’t, would be appreciated.

Trahsmitter:

//Transmitter program

#include <EEPROM.h>
#include <DMXSerial2.h>
#include <SPI.h>
#include "Mirf.h"
#include "nRF24L01.h"
#include "MirfHardwareSpiDriver.h"
Nrf24l Mirf = Nrf24l(9, 10);
int value1[4];
int value2[4];
int value3[4];
int value4[4];
int value5[4];
int value6[4];
int value7[4];

// see DMXSerial2.h for the definition of the fields of this structure
const uint16_t my_pids[] = {E120_DEVICE_HOURS, E120_LAMP_HOURS};
struct RDMINIT rdmInit = {
  "Gitlestad Design Engineering", // Manufacturer Label
  1, // Device Model ID
  "Arduino RDM Device", // Device Model Label
  27, // footprint
  (sizeof(my_pids)/sizeof(uint16_t)), my_pids,
  0, NULL
};

void setup()
{
    Mirf.spi = &MirfHardwareSpi;
    Mirf.init();
    Mirf.setRADDR((byte *)"ABCDE"); 
    Mirf.configRegister(RF_SETUP,0x06);

    DMXSerial2.init(&rdmInit, processCommand);
    uint16_t start = DMXSerial2.getStartAddress();
}

void loop()
{

    unsigned long lastPacket = DMXSerial2.noDataSince();

  if (DMXSerial2.isIdentifyMode()) {
    // idenitfy
  } else if (lastPacket < 30000) {
      
   for (int i=0; i<= 3; i++) {
     value1[i] = DMXSerial2.readRelative(i);
     value2[i] = DMXSerial2.readRelative(i+4);
     value3[i] = DMXSerial2.readRelative(i+8);
     value4[i] = DMXSerial2.readRelative(i+12);
     value5[i] = DMXSerial2.readRelative(i+16);
     value6[i] = DMXSerial2.readRelative(i+20);
     value7[i] = DMXSerial2.readRelative(i+24);    
   }   
  }
  

    Mirf.payload = sizeof(value1);
    Mirf.channel = 90;              
    Mirf.config();
    if(Mirf.channel == 90) {
      Mirf.setTADDR((byte *)"AXXXX");          
      Mirf.send((byte *)&value1);                
      while (Mirf.isSending()) delay(1);        
      
      Mirf.setTADDR((byte *)"XAXXX");           
      Mirf.send((byte *)&value2);               
      while (Mirf.isSending()) delay(1); 

      Mirf.setTADDR((byte *)"XXAXX");           
      Mirf.send((byte *)&value3);               
      while (Mirf.isSending()) delay(1); 

      Mirf.setTADDR((byte *)"XXXAX");           
      Mirf.send((byte *)&value4);               
      while (Mirf.isSending()) delay(1);

      
      Mirf.setTADDR((byte *)"XXXXA");           
      Mirf.send((byte *)&value5);               
      while (Mirf.isSending()) delay(1);

      Mirf.setTADDR((byte *)"BXXXX");           
      Mirf.send((byte *)&value6);               
      while (Mirf.isSending()) delay(1);

      Mirf.setTADDR((byte *)"XBXXX");           
      Mirf.send((byte *)&value7);               
      while (Mirf.isSending()) delay(1);
    }
  // check for unhandled RDM commands
  DMXSerial2.tick();
}

bool8 processCommand(struct RDMDATA *rdm, uint16_t *nackReason)
{
  byte CmdClass       = rdm->CmdClass;     // command class
  uint16_t Parameter  = rdm->Parameter;     // parameter ID
  bool8 handled = false;
  return handled;
}[code]

receiver:
(the reaon for higher pwm freq on one timer is that it somehow eliminated a problem with fluctuating output from the XL6009)

#include <SPI.h>
#include "Mirf.h"
#include "nRF24L01.h"
#include "MirfHardwareSpiDriver.h"

/*
//---------------------------------------------- Set PWM frequency for D5 & D6 -------------------------------
//NOTE: Changing this timer 0 affects millis() and delay!
//TCCR0B = TCCR0B & B11111000 | B00000001;    // set timer 0 divisor to     1 for PWM frequency of 62500.00 Hz
TCCR0B = TCCR0B & B11111000 | B00000010;    // set timer 0 divisor to     8 for PWM frequency of  7812.50 Hz
//TCCR0B = TCCR0B & B11111000 | B00000011;    // set timer 0 divisor to    64 for PWM frequency of   976.56 Hz (The DEFAULT)

//---------------------------------------------- Set PWM frequency for D3 & D11 ------------------------------

//TCCR2B = TCCR2B & B11111000 | B00000001;    // set timer 2 divisor to     1 for PWM frequency of 31372.55 Hz
//TCCR2B = TCCR2B & B11111000 | B00000010;    // set timer 2 divisor to     8 for PWM frequency of  3921.16 Hz
//TCCR2B = TCCR2B & B11111000 | B00000011;    // set timer 2 divisor to    32 for PWM frequency of   980.39 Hz
TCCR2B = TCCR2B & B11111000 | B00000100;    // set timer 2 divisor to    64 for PWM frequency of   490.20 Hz (The DEFAULT)
*/


#include <DebounceEvent.h>
#define BUTTON_PIN              2
#define CUSTOM_DEBOUNCE_DELAY   50*8
#define CUSTOM_REPEAT_DELAY     500*8
DebounceEvent * button;

Nrf24l Mirf = Nrf24l(10, 9);

long readingInterval = 100*8; // interval between readings. 10 readings will be averaged
float voltDividerRatio = 4.7;
float lowVoltThreshold = 16.0;
long longPressTime = 400*8; // multiply by 8 due to changed timer divisor

int value[4];
float intensity = 0; 
float intensitySum;
float intensityScaled;
float sf; // 0-1
float master; // 0-100
float led1 = 255;
float led2 = 255;
float led3 = 255;
float ledScaled1;
float ledScaled2;
float ledScaled3;

float reading;
float voltage;
long previousMillis = 0;
float readingsSum = 0;
int sampleCount = 0;

boolean DMX = false;
/*volatile unsigned long lastframe = 0;
unsigned long currentTime = 0;*/

void setup()
{ 
  pinMode(3, OUTPUT); // pwm
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  //pinMode(A7, INPUT); // Vsense
  //pinMode(2, INPUT_PULLUP); // pushbtn
  button = new DebounceEvent(BUTTON_PIN, BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH | BUTTON_SET_PULLUP, CUSTOM_DEBOUNCE_DELAY, CUSTOM_REPEAT_DELAY);

/*  LED test routine */
  analogWrite(3,255);
  delay(500);
  analogWrite(3,0);
  analogWrite(5,255);
  delay(500);
  analogWrite(5,0);
  analogWrite(6,255);
  delay(500);
  analogWrite(3,128);
  analogWrite(5,128);
  analogWrite(6,128);
  delay(500);
  analogWrite(3,0);
  analogWrite(5,0);
  analogWrite(6,0);
  delay(500);

  TCCR0B = TCCR0B & B11111000 | B00000010;    // set timer 0 divisor to 8 for PWM frequency of 7812.50 Hz
  TCCR2B = TCCR2B & B11111000 | B00000100;    // set timer 2 divisor to 64 for PWM frequency of 490.20 Hz (The DEFAULT)
  
  Serial.begin(9600);
  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  Mirf.setRADDR((byte *)"XXXXA"); //Set your own address (receiver address) using 5 characters
  Mirf.payload = sizeof(value);
  Mirf.channel = 90;              //Set the used channel
  Mirf.configRegister(RF_SETUP,0x06);
  Mirf.config();
  Serial.println("Listening..."); //Start listening to received data
}

void loop(){
  Mirf.ceLow();
  Mirf.configRegister(RF_CH, 90);  //switch channel 10
  Mirf.ceHi();
  if (Mirf.dataReady()){ 
      Mirf.getData((byte *) &value);
      DMX = true;
  }

  if (DMX == true){
    intensity = value[0];
    led1 = value[1];
    led2 = value[2];
    led3 = value[3]; 
  }

  /* If no control signal is present, let the pushbutton control the LEDs */

  if (DMX == false){

    if (unsigned int event = button->loop()) {
        if (event == EVENT_RELEASED) {
          Serial.print("Count : "); Serial.print(button->getEventCount());
            Serial.print(" Length: "); Serial.print(button->getEventLength());
            Serial.println(); 

            if(button->getEventCount()==1 && button->getEventLength()>(1000*8)){
              if(intensity==255){
                intensity=0;
              } else {
                intensity=255;
              }
            }

            if(button->getEventCount()==1 && button->getEventLength()<(1000*8)){
              led1 = 255; led2 = 255; led3 = 255;
            }

            if(button->getEventCount()==2){
              led1 = 255; led2 = 255; led3 = 0;
            }
            
            if(button->getEventCount()==3){
              led1 = 255; led2 = 0; led3 = 0;
            }
        }
      }  
  }


  /* Scale master intensity according to number of LEd strips used */  
  intensityScaled = map(intensity,0,255,0,1000);
  intensitySum = led1+led2+led3;
  sf = intensitySum/255;          // scale factor
  master = (intensityScaled/sf)/10;

  /* Calculate LED intensity and write PWM */
  ledScaled1 = (led1/100)*master;
  ledScaled2 = (led2/100)*master;
  ledScaled3 = (led3/100)*master;
  
  analogWrite(3, ledScaled1);
  analogWrite(5, ledScaled2);
  analogWrite(6, ledScaled3); 

  

  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > readingInterval) {
    previousMillis = currentMillis;
    readingsSum = readingsSum + analogRead(A7);
    sampleCount ++;
    //Serial.println(sampleCount);

    if(sampleCount == 10) {
      reading = ((float)readingsSum / (float)sampleCount * 5.00) / 1024.0;
      voltage = reading * voltDividerRatio;
      // Serial.print(voltage); Serial.println (" V");
      sampleCount = 0;
      readingsSum = 0;
    }
  }
  
  if(voltage < lowVoltThreshold){
    Serial.println("LOW VOLTAGE");
    master = 0;
  }
}

I have no experience of the MIRF library. The examples in this Simple nRF24L01+ Tutorial use the TMRh20 version of the RF24 library

The examples are as simple as I could make them and they have worked for other Forum members. If you get stuck it will be easier to help with code that I am familiar with. Start by getting the first example to work

...R

Hi,

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks... Tom... :slight_smile:

Thanks for the replies! I'll consider changing to the TMRh20 version. To be honest I'm just using MIRF because I based these sketches on the examples included with the RF-Nano, and the basics seem to be working fine – it's just the random 100% intensity thing I'm not understanding.

Will draw a schematic ASAP. There's not much to it though :wink:

TX is an Uno with a DFRobot RS485 shield and the Sparkfun module wired per Sparkfun's instructions.

RX is:

  • RF-Nano with irlb8721pbf MOSFETs on PWM pins 3, 5 and 6, switching the low side of the LED strips
  • The Nano is powered by an RS-branded switching regulator, their website is down at the moment so I can't find the datasheet, but it's a simple 7812 drop in repacement, 15-36V in, 12V 500mA out
  • pushbutton connects pin 2 (internally pulled up) to GND
  • The output of a 100k/27k voltage divider is connected to pin A7
  • The 24V out from the XL6009 module only goes to the LED strips, nothing else, so any fluctuations there should not affect the controls

The batteries are Tenergy 9,6V 1600mAh airsoft batteries connected in series by a cusom wiring harness. A Mascot 10-20 cell charger handles the charging.

Will make a sketch of this later today

EDIT: by the way, one werid thing I'm noticing is that removing the antenna from the transmitter does not seem to make much of a difference …

Wireless problems can be very difficult to debug.

I think you need to break your program into two parts so you can experiment with wireless reception and the control of the LEDs separately. That should allow you to identify which part of the program (or hardware) is causing the problem.

It is entirely possible that wireless messages fail to be received and the receiving program should be designed to deal with that. But I think it is very unlikely with nRF24 modules that a transmitted message would be received incorrectly - the system has a lot of error checking.

...R

Robin2:
Wireless problems can be very difficult to debug.

I think you need to break your program into two parts so you can experiment with wireless reception and the control of the LEDs separately. That should allow you to identify which part of the program (or hardware) is causing the problem.

It is entirely possible that wireless messages fail to be received and the receiving program should be designed to deal with that. But I think it is very unlikely with nRF24 modules that a transmitted message would be received incorrectly - the system has a lot of error checking.

…R

Makes sense. I’m fairly confident the LED control side of this should be OK; it’s quite simple, and it works when using the pushbutton for control rather than wireless.

I have set up a simple test where, whenever the receiver sends non-0 values to the lights, it prints those values as well at the raw data it’s received. It’s running now, with the transmitter constantly sending 0 – shall be interesting to see the results …

OK, this is getting weird. For some reason I can't reproduce the problem here in my workshop, it only happens on stage.

My workshop is in a typical urban office environment with a bazillion WiFi networks, but heavy concrete walls, so their signals shouldn't be very strong in here. The stage is a typical modern stage, there's probably a bunch of wireless stuff going on there but I don't know what exactly. I've turned off their 2.4GHz W-DMX transmitter, but that didn't make much of a difference. We're not using wireless mics, but I have no idea what might be turned on around in the room.

I've noticed that the units placed the furthest away from the transmitter are the least stable, so I've moved it closer to where they'll be most of the time, but it's still not ideal. Is there any way to amplify/distribute/boost the signal from the TX? I realise the performance will also be limited by the receiving units as they'll have to talk back to the TX to acknowledge commands, etc …

I also suspected that the very Chinese XL6009 converter could be causing noise that confuses the microcontroller, but I've probed around and looked at the spectrogram and can't see any problems there. The 12V switcher that powers the RF-Nano is a little noisy, but the onboard linear 5V reg smooths it out.

FWIW the ambient temperature in my workshop is a little lower than on stage, but probably not by much – haven't measured. At the time of writing the units have been powered on for an hours and they've been dead silent, but during an hour long rehearsal I see between 5 and 15 random flashes, between .25 and 5 seconds long.

I've also been suspecting static electricity could be a problem – this is a dance performance, where they're dancing around on vinyl dance mats, in relatively dry air, with my LED props housed in Plexiglas tubes … Still, though, this doesn't make sense to me. I guess I could try to ground the dance mats, but, eh …

Have you tried using a different channel (frequency) with your nRF24s?

When you say "least stable" are you printing data to the Serial Monitor so you can see what data is being sent and what is being received?

I am specifically trying to isolate whether the problem arises during the actual wireless transmission OR whether it arises in the processing of data that has been correctly received.

How does your program deal with the possibility that one or two messages are not received at all? (which is a very likely event with any wireless system).

Is there any possibility that some of the nRF24s are responding to messages that were intended for a different nRF24?

...R

Haven't tried other channels – will try today. Will also try a channel scanner sketch to see which channels are viable. Currently using channel 90. Have asked the local crew to turn off unused wireless equipment. Turns out there's been a 2.4GHz WDMX transmitter on all the time, but switching it off made no difference.

Not printing data when testing – merely observing sporadic flashes, or lack thereof, and noting that they occur more frequently when further away from TX. I should probably try the debug sketch under these conditions. Unfortunately the time I have at my disposal in the space where they'll be used is limited …

When they fail to receive a message, they simply don't do anyting, they just stay at their current brightness, whatever that is. The TX is constantly streaming data, so usually they just respond a few seconds late. This is a behaviour I'm OK with.

The errors seem completely random – i.e. I haven't had one unit light up when another one is supposed to, or anything of that kind. The duration of the unintended flashes also seems random, something between 0.25 to 5 seconds mostly.

FWIW, I have not seen them unintentionally turn off when they are meant to be on, just the other way around. Also, when they are at, say 50%, they sometimes jump to 100% when not supposed to. That said, a brief flash is visually easier to see than a brief moment of darkness, so it's possible that it's happened without me seeing it.