Flickering on 16 channel expansion

Expansion modules and attachments
Post Reply
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Flickering on 16 channel expansion

Post by AlanM »

For what it's worth, I added most of the 16ch and higher resolution code in the libraries and have been using it for a good part of a year and it seems to work fine for me, but if it's finicky with other situations I'd like to know. If the usage of it is confusing please post up here and I'll try to help.

Some usage notes for the 16 channel stuff and 12-bit high-res functions are at:

https://github.com/reefangel/Libraries/ ... _Notes.txt
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Flickering on 16 channel expansion

Post by joshlawless »

Alan, thanks for the great work on the code.

I thought I would have to use custom functions to accommodate the two 16-channel boards I'm setting up, so I'm trying to replicate some of your functionality in my own INO. I don't know that there's anyone else who's going to ever use two of these 16-channel devices, so I'm guessing that introducing standard support for two is unlikely. If it were introduced, I'm guessing it would look something like (in Globals.h):

#define THIRTYTWOCH_PWM_EXPANSION_CHANNELS 16
...
#ifdef THIRTYTWOCHPWMEXPANSION
#define TTPWMbit 32
#else
#define TTPWMbit 0
#endif // THIRTYTWOCHPWMEXPANSION

etc., with a dedicated I2C of 0x42 in the PWM libraries. I'm still new enough with Arduino code not to have confidence in putting this together, but am fairly sure at the rate I'm having to learn, I could implement something like that in a dev branch eventually.

In any event, I tried to replicate the functionality of the CRC check in the function I posted (checking to see if the value to be pushed to the 16ChPWM board is different, or if enough time has passed to force an update anyways). I couldn't get the millis()%60000<200 condition to work for me, as trying to push 8 channels of 12-bit updates as many times as possible during those 0.2 seconds was causing the LEDs to flicker every second. Hence my effort to separate the forced updates into different time bands by channel.

With all the other stuff going on with my unit, I'm not convinced that there's anything left to fix in my lighting code -- it seems that something on the bus is causing trouble with my expansion relay, and that could be the same underlying problem that's causing the occasional blink in (every channel, simultaneously of) my LEDs. Still, if you see a problem with how I've written it (or think that separating the forced updates into different time bands is a bad idea), I'd love to hear your thoughts.
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Flickering on 16 channel expansion

Post by AlanM »

OK. I can think about how I'd try to support another 16 channels. Might be possible to just try to set a definition in the .ino for the number of PWM expansion channels instead of hardcoding it and change 16 to the memory location for number of channels everywhere it is used in an array and then on the write do a modular divide to get the actual channel number on the correct expansion board since you have them on two different I2C channels.

I don't know much about the millis() part of the code. I just reused it. I do know that you don't have to constantly send a value. The PCA9685 chipset in the 16 channel dimmer remembers the previous value even if you're not sending it stuff, even if the USB signal cable is disconnected, so be careful to just send values when they actually change. Surely they're not changing very quickly.

Does it work if you just use one 16ch board set to 0x41 address and with the current dev branch? Here's what I'm doing with my lights, by the way inside my loop. On my 16ch I have 2 channels of Jebao pumps, 6 channels of light colors, and one PWM fan channel. I set the maximums in memory locations that the standard dimming module would normally use and read them. I can change them with the android app by poking new values into those locations if I want or even do it with the browser with /mb commands.

Code: Select all

 ReefAngel.PWM.SIXTEENChannelPWMSigmoid(2,0,InternalMemory.PWMSlopeEnd0_read()); // Violet channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(3,0,InternalMemory.PWMSlopeEnd1_read()); // Royal Blue channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(4,0,InternalMemory.PWMSlopeEnd2_read()); // White channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(5,0,InternalMemory.PWMSlopeEnd3_read()); // Cool Blue channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(6,0,InternalMemory.PWMSlopeEnd4_read(),-60,-60); // Deep Red channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(7,0,InternalMemory.PWMSlopeEnd5_read(),-60,-60); // Cyan channel, startpwm, endpwm, offsets
  ReefAngel.PWM.SIXTEENChannelPWMSigmoid(8,0,85); // Fan channel, silent up to 75 or so.
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Flickering on 16 channel expansion

Post by joshlawless »

Hey, I just realized something.

I've declared my LEDLastWrite [32] array as a type int.

But millis() returns an unsigned long.

Probably isn't good to store a 32-bit unsigned long value in a 16-bit integer array, and then try to do math with it.

Since it's after midnight, I'll go work it out tomorrow.
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Flickering on 16 channel expansion

Post by joshlawless »

So regarding the library functions for writing to the 16 channel expansion, I think the occasional flicker I see is a result of trying to send too much data to the PCA9685 at the same time.

As I understand it, this block of code:

Code: Select all

void RA_PWMClass::SIXTEENChExpansionWrite()
{
	byte thiscrc=0;
	for ( byte a = 0; a < SIXTEENCH_PWM_EXPANSION_CHANNELS; a++ )
		thiscrc+=Get16ChannelValueRaw(a)*(a+1);
	if (millis()%60000<200) lastcrc=-1;
	if (lastcrc!=thiscrc || millis()<5000)
	{
		lastcrc=thiscrc;
		// setup PCA9685 for data receive
		// we need this to make sure it will work if connected ofter controller is booted, so we need to send it all the time.
		Wire.beginTransmission(I2CPWM_16CH_PCA9685);
		Wire.write(0);
		Wire.write(0xa1);
		Wire.endTransmission();
		for ( byte a = 0; a < SIXTEENCH_PWM_EXPANSION_CHANNELS; a++ )
		{
			SIXTEENChExpansion(a,Get16ChannelValueRaw(a));
		}
	}
}
first generates a CRC value "thiscrc" by summing the product of each channel's raw value (0 to 4095) and it's channel number plus 1 (to address the 0-indexing of the first channel). That CRC value is later used to determine whether there has been a change since the last write (in the "lastcrc!=thiscrc evaluation).

Similarly, the code also checks to see whether the system clock is within the first 0.2 seconds of every 60 seconds (per the millis()%60000<200 evaluation), and if so, resets the "thiscrc" value (ensuring it will be different from the "lastcrc" value to force a write).

Finally, the code checks to see whether the system has booted up in the last 5 seconds (per the millis()<5000 condition), and if so, forces a write.

When any of the foregoing conditions are met, the system forces an actual transmission to the chip for EVERY channel, (per the "for ( byte a = 0; a < SIXTEENCH_PWM_EXPANSION_CHANNELS; a++ )" language).

Even with only 8 channels connected, I still occasionally see a flicker that looks just like the flickers I got when writing too often without a CRC. Similarly, the lights flicker in the first few seconds after boot up, as the system sends lighting commands over and over and over for the first five seconds of the system operation.

What if, instead, the code were to determine on a channel by channel basis whether the channel data is different, or if enough time since _this particular channel_ was written has passed, before undergoing a write. And if instead of forcing writes for the first five seconds, it forced one write upon bootup (perhaps by waiting until millis() exceeded 1000 or 2000, to ensure any necessary prerequiste action has been taken if any are required).

I'm struggling to see how, given the Refresh() call of SIXTEENChExpansionWrite() which calls SIXTEENChExpansion() while passing a variable that calls Get16ChannelValueRaw() ... that's a lot for my newbie mind to iterate through. But conceptually, does it make sense to send data to all channels if only one has changed? And if the occasional flicker is caused by the millis()%60000<200 condition, in which all channels are refreshed (over and over and over as many times as possible in 0.2 seconds), does it make sense to separate the writes into different time slots?
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Flickering on 16 channel expansion

Post by AlanM »

I think the millis()%60000<200 condition is not intended to refresh them really fast. I think it's intended to make sure that they don't refresh every single time that SIXTEENChExpansionWrite() is called.

Roberto can weigh in here, but I think the function makes it so that every 60000 milliseconds (once per minute) it gets written to whether or not anything has changed. Then there's the function that forces a write within the first 5 seconds and also if the CRC has changed.

My guess is that the CRC is calculated by adding up all channels to see if any of them has changed because it is slightly less computationally expensive than checking every channel, but I bet you could take that "for" loop and only write the ones that changed.

Lemme play with it a bit and give you a code snippet to paste in to RA_PWM.h and RA_PWM.cpp. Actually, I'll make a branch on my code on github that you can pull from if you have access there.
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Flickering on 16 channel expansion

Post by AlanM »

Here ya go:

https://github.com/amunter-nist/Librari ... f4269714d5

So remove the pink lines and insert the green ones in your "Arduino/libraries/RA_PWM" directory files and try it out. I made this branch from Roberto's dev branch.
Post Reply