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
Flickering on 16 channel expansion
- joshlawless
- Posts: 138
- Joined: Thu May 23, 2013 2:52 pm
Re: Flickering on 16 channel expansion
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.
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.
Re: Flickering on 16 channel expansion
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.
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.
- joshlawless
- Posts: 138
- Joined: Thu May 23, 2013 2:52 pm
Re: Flickering on 16 channel expansion
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.
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.
- joshlawless
- Posts: 138
- Joined: Thu May 23, 2013 2:52 pm
Re: Flickering on 16 channel expansion
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:
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?
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));
}
}
}
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?
Re: Flickering on 16 channel expansion
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.
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.
Re: Flickering on 16 channel expansion
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.
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.