I'm confident it's not a wiring problem, because I tried to fix it by uploading revised code and rebooting the RA+. Every time I upload new code, the flickering stops for the duration of the upload. As soon as the loop() is running, though, the channels flicker several times per second.
I've simplified the code down to just this function in the loop()
Code: Select all
for (int i=0;i<8;i++)
{
CustomExpansion(0x41, i, 1023);
}
Code: Select all
void CustomExpansion(byte id, byte channel, int level)
{
Wire.beginTransmission(id);
Wire.write(0);
Wire.write(0xa1);
Wire.endTransmission();
Wire.beginTransmission(id);
Wire.write(0x8+(4*channel));
Wire.write(level&0xff);
Wire.write(level>>8);
Wire.endTransmission();
}
Code: Select all
void RA_PWMClass::SIXTEENChExpansion(byte channel, int data)
{
// the data is in int, so just send that int to the module
if (channel<SIXTEENCH_PWM_EXPANSION_CHANNELS) SIXTEENChExpansionChannel[channel]=data;
Wire.beginTransmission(I2CPWM_16CH_PCA9685);
Wire.write(0);
Wire.write(0xa1);
Wire.endTransmission();
Wire.beginTransmission(I2CPWM_16CH_PCA9685);
Wire.write(0x8+(4*channel));
Wire.write(data&0xff);
Wire.write(data>>8);
Wire.endTransmission();
}
Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>
////// Place global variable code below here
// NW = Neutral White; WW = Warm White; RD = Red; CY = Cyan; CB = Cool Blue; RB = Royal Blue; IN = Indigo; VI = Violet
// -E = East; -W = West; -N = North; -S = South (compass points of tank)
// LED Array order {NW-E, NW-W, NW-N, NW-S, WW-E, WW-W, WW-N, WW-S, RD-E, RD-W, RD-N, RD-S, CY-E, CY-W, CY-N, CY-S, CB-E, CB-W, CB-N, CB-S, RB-E, RB-W, RB-N, RB-S, IN-E, IN-W, IN-N, IN-S, VI-E, VI-W, VI-N, VI-S}
byte LEDBoardAdd [32] = {0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42, 0x42};
byte LEDBoardChn [32] = { 0, 8, 0, 8, 1, 9, 1, 9, 2, 10, 2, 10, 3, 11, 3, 11, 4, 12, 4, 12, 5, 13, 5, 13, 6, 14, 6, 14, 7, 15, 7, 15};
int LEDDelayOn [32] = { -30, 30, 0, 0, -30, 30, 0, 0, -45, 45, 0, 0, -30, 30, 0, 0, -30, 30, 0, 0, -30, 30, 0, 0, -30, 30, 0, 0, -30, 30, 0, 0};
int LEDDelayOff [32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int LEDStartPct [32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int LEDEndPct [32] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100};
byte LEDRampRate [32] = { 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 100};
int LEDDefault [32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int a=0; // variable just needed for test code
int b=0; // variable just needed for test code
int c=0; // variable just needed for test code
byte previouschannel=0; // variable just needed for test code
////// Place global variable code above here
void setup()
{
ReefAngel.Init(); // This must be the first line -- initializes the controller
ReefAngel.Use2014Screen(); // Let's use 2014 Screen
ReefAngel.AddSalinityExpansion(); // Salinity Expansion Module
ReefAngel.AddORPExpansion(); // ORP Expansion Module
ReefAngel.AddWaterLevelExpansion(); // Water Level Expansion Module
ReefAngel.AddExtraTempProbes(); // Accommodate 4th temperature probe in parallel
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port3Bit;
ReefAngel.FeedingModePortsE[0] = Port4Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
ReefAngel.WaterChangePortsE[0] = Port4Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
ReefAngel.LightsOnPortsE[0] = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
ReefAngel.OverheatShutoffPortsE[0] = 0;
// Use T1 probe (in display tank) for temp functions and T3 probe (in skimmer section) for overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T3_PROBE;
// Feeeding and Water Change mode speed
ReefAngel.DCPump.FeedingSpeed=0;
ReefAngel.DCPump.WaterChangeSpeed=0;
// Ports that are always on
ReefAngel.Relay.On( Port5 ); // Air Pump
ReefAngel.Relay.On( Port6 ); // Filter manifold pump
ReefAngel.Relay.On( Box1_Port1 ); // Circulation pump
ReefAngel.Relay.On( Box1_Port2 ); // Diverter
ReefAngel.Relay.On( Box1_Port5 ); // return pump L
ReefAngel.Relay.On( Box1_Port6 ); // return pump R
////// Place additional initialization code below here
ReefAngel.CustomLabels[0]="Heater1";
ReefAngel.CustomLabels[1]="Heater2";
ReefAngel.CustomLabels[2]="Skimmer";
ReefAngel.CustomLabels[3]="Neck Wipe";
ReefAngel.CustomLabels[4]="Air Pump";
ReefAngel.CustomLabels[5]="Filters";
ReefAngel.CustomLabels[6]="Refuge Lt";
ReefAngel.CustomLabels[7]="Ozone";
ReefAngel.CustomLabels[8]="Diverter";
ReefAngel.CustomLabels[9]="Circulate";
ReefAngel.CustomLabels[10]="Kalk Stir";
ReefAngel.CustomLabels[11]="ATO Pump";
ReefAngel.CustomLabels[12]="Return L";
ReefAngel.CustomLabels[13]="Return R";
ReefAngel.CustomLabels[14]="Heater3";
////// Place additional initialization code above here
}
void loop()
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// TEMPERATURE //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
if ( ReefAngel.Params.Temp[T1_PROBE] < 775) {ReefAngel.Relay.On(Port1);} //
if ( ReefAngel.Params.Temp[T1_PROBE] > 780) {ReefAngel.Relay.Off(Port1);} //
//
if ( ReefAngel.Params.Temp[T2_PROBE] < 775) {ReefAngel.Relay.On(Port2);} //
if ( ReefAngel.Params.Temp[T2_PROBE] > 780) {ReefAngel.Relay.Off(Port2);} //
//
if ( ReefAngel.Params.Temp[T3_PROBE] < 775) {ReefAngel.Relay.On(Box1_Port7);} //
if ( ReefAngel.Params.Temp[T3_PROBE] > 780) {ReefAngel.Relay.Off(Box1_Port7);} //
//
ReefAngel.CustomVar[7] = ReefAngel.Params.Temp[T4_PROBE]; // Puts hood temperature in C7
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// SKIMMER //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
ReefAngel.Relay.DelayedOn( Port3 ); // Skimmer is delayed to allow water level in skimmer section to adjust following return pump activation
ReefAngel.DosingPumpRepeat1( Port4 ); // Skimmer neck wipe is handled by internal memory location
if(!ReefAngel.HighATO.IsActive()) {ReefAngel.Relay.Override(Port3,0);} // Skimmate collector sensor is connected to HighATO (closest to centerline of controller body)
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// LIGHTING //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
ReefAngel.MoonLights( Port7 ); // The refugium light is on an opposite photoperiod, handled by internal memory location
//
//
for (int i=0;i<8;i++) // This code will apply the LED array content (per channel max and min and delays, etc.) to the PWM HD slope.
{ //
CustomExpansion(0x41, i, 1023); //
} //
// for (int i=0;i<32;i++) // This code will apply the LED array content (per channel max and min and delays, etc.) to the PWM HD slope.
// { //
// CustomExpansion(LEDBoardAdd[i], LEDBoardChn[i], OffsetPWMSMoothRamp(LEDDelayOn[i], //
// LEDDelayOff[i], LEDStartPct[i], LEDEndPct[i], LEDRampRate[i], LEDDefault[i])); //
// } //
//
// Test code to blink each channel in sequence (according to LED array sequence)
// CustomExpansion(LEDBoardAdd[now()%32], LEDBoardChn[now()%32], 1023); // sends full on (12-bit) signal to current channel, current channel advancing from 0 to 31 every second
// previouschannel = now()%32-1; // calculates value of previously powered on channel
// if (previouschannel == -1) {previouschannel = 31;} // corrects value of previously powered on channel if current channel is 0
// CustomExpansion(LEDBoardAdd[previouschannel], LEDBoardChn[previouschannel], 0); // sends full off signal to previous channel
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ATO //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ReefAngel.DosingPumpRepeat2( Box1_Port3 ); // Kalk stirrer is handled by internal memory location
// ReefAngel.WaterLevelATO( Box1_Port4, 300, 22, 60 ); // Inputs are ATO Pump Relay, Timeout (max seconds pump can run),
// low water level to turn on pump, high water level to turn off pump
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// RETURN PUMP //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
ReefAngel.DCPump.UseMemory = false; // Seems necessary to set threshold
ReefAngel.DCPump.Threshold = 70; // Minimum return pump value is 50%
ReefAngel.DCPump.SetMode(Sine,100,240); // Return pumps oscillate back and forth from Threshold to 100 over 240 seconds
ReefAngel.DCPump.DaylightChannel = Sync; // Daylight channel handles right/left (determine) pump
ReefAngel.DCPump.ActinicChannel = AntiSync ; // Actinic channel handles left/right (determine) pump
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// OZONE //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
if (ReefAngel.Params.ORP>450) ReefAngel.Relay.Off(Port8); // If ORP > 450mV, then turn off ozone generator
if (ReefAngel.Params.ORP==0) ReefAngel.Relay.Off(Port8); // If ORP = 0mV (e.g., probe disconnected), then turn off ozone generator
if (ReefAngel.Params.ORP<400 && ReefAngel.Params.ORP>0) ReefAngel.Relay.On(Port8); // If ORP < 400mV, then turn on ozone generator (unless ORP=0, indicating missing probe)
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// NETWORK //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
//
ReefAngel.Portal( "joshlawless" ); // This line feeds info to the reefangel.com portal
ReefAngel.DDNS( "reef" ); // Your DDNS is joshlawless-reef.myreefangel.com
ReefAngel.ShowInterface(); // This should always be the last line
}
void CustomExpansion(byte id, byte channel, int level) // this function is necessary to permit the 16-channel board to functionet
{
Wire.beginTransmission(id);
Wire.write(0);
Wire.write(0xa1);
Wire.endTransmission(); // try to remove this and the next line
Wire.beginTransmission(id); //try to remove this and the previous line
Wire.write(0x8+(4*channel));
Wire.write(level&0xff);
Wire.write(level>>8);
Wire.endTransmission();
}
// this function calculates the LED channel levels based on a time-offset
int OffsetPWMSMoothRamp(int OnMinutesLate, int OffMinutesLate, int StartPercent, int EndPercent, byte SlopeLength, int OldValue)
{
int onTime=NumMins(InternalMemory.StdLightsOnHour_read(),InternalMemory.StdLightsOnMinute_read())+OnMinutesLate;
int offTime=NumMins(InternalMemory.StdLightsOffHour_read(),InternalMemory.StdLightsOffMinute_read())+OffMinutesLate;
PWMSmoothRampHighRes(onTime/60,onTime%60,offTime/60,offTime%60,StartPercent,EndPercent,SlopeLength,OldValue);
}
--EDIT--
If I leave it running for more than a few seconds, it eventually confuses the expansion board so much that all the lights turn off. If I remove power from the expansion board, then power it up again, the lights do their disco dance for a while longer before turning off. But if I don't power-cycle the expansion board, even rebooting the controller doesn't get the lights to turn back on.
I tried uploading just this simple test code:
Code: Select all
#include <Salinity.h>
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <rtc_clock.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>
void setup()
{
ReefAngel.Init();
}
void loop()
{
for (int a=0;a<8;a++)
{
CustomExpansion(0x41,a,0);
}
CustomExpansion(0x41,now()%8,100);
ReefAngel.ShowInterface();
}
void CustomExpansion(byte id, byte channel, byte percentage)
{
Wire.beginTransmission(id);
Wire.write(0);
Wire.write(0xa1);
Wire.endTransmission();
int newdata=(int)(percentage*40.95);
Wire.beginTransmission(id);
Wire.write(0x8+(4*channel));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
}
If I unplug the USB cable from the expansion board while one of the light channels is on, it stays on without flickering.
I've tried replacing the USB cable with a different one, replacing the 12v power supply to the expansion board with a different one, connecting the USB cable directly to the relay box (bypassing the expansion hub), power cycling the entire controller system, with no luck.
Really hoping there isn't something wrong with the board, but given how it essentially locks up after a few seconds, I'm suspicious it may not be a coding problem.