/**************************************************************************
septic_07.c
This program is timed-discharge adapter for a Vericom panel used with Advantex
septic treatment system. It turns on the final discharge pump by manipulating
the Vericom's PUMP-ON and PUMP-OFF float inputs on a timed basis, using the
state of the actual float switches as boundary conditions, i.e. don't turn on
the pump at all if the PUMP-OFF float says that there's no water to pump, and
turn on the pump anytime the PUMP-ON float says that the water level is high.
This version adds: eeprom storage of pump-on time
activity LED shows 10% of pump-on time duration
skip out of delays immediately if put into BYPASS mode
Makes the high level float pump until it goes down, no interrupts. Logs
on-time in both bypass and timed modes.
Still has issues because it confuses the Vericom panel, which then
goes into an alarm condition, and phones out with an alarm. I haven't
figured out exactly what the Vericom panels is confused about, other
than that the low float is on for a very short period before the high
float is activated.
+5
|
14
----------
| RA0 |-17-- PUMP ON RELAY
HIGH FLOAT---6-| RB0 |
| RA1 |-18-- PUMP OFF RELAY
LOW FLOAT----7-| RB1 |
| RB5 |-11-- PUMP ON LED
BYPASS-------8-| RB2 |
| RB4 |-10-- PUMP OFF LED
| |
| RB3 |-9--- ACTIVITY LED
| |
| 16F628 |
| |
| RB6 |-12-- PGD
| RB7 |-13-- PGC
6MHz XTAL-15-| MCLR |-4--- MCLR
XTAL-16-| |
----------
5
|
Gnd
***************************************************************************/
#include <16F628A.h>
#fuses HS, NOPROTECT, PUT, NOWDT, BROWNOUT, NOMCLR, NOLVP
#include
#use standard_io ( A )
#use standard_io ( B )
#use delay ( clock = 6000000 )
// one cycle = 0.66uS @ 6MHz
#define PUMP_ON_RELAY PIN_A0
#define PUMP_OFF_RELAY PIN_A1
#define PUMP_ON_FLOAT_LED PIN_B5
#define PUMP_OFF_FLOAT_LED PIN_B4
#define ACTIVITY_LED PIN_B3
#define HIGH_FLOAT PIN_B0
#define LOW_FLOAT PIN_B1
#define BYPASS_SWITCH PIN_B2
#define RAISED 0
#define LOWERED 1
#define NORMAL_WATER_LEVEL 0
#define HIGH_WATER_LEVEL 1
#define TIMED_MODE 0
#define VOLUME_MODE 1
#define INTERRUPTS_PER_SECOND 46
//macros
#define LEDON output_high ( ACTIVITY_LED )
#define LEDOFF output_low ( ACTIVITY_LED )
// TIMES IN SECONDS
#define INITIAL_PUMP_ON_TIME 10
#define PUMP_ON_HIGH_WATER_TIME 30
#define VERICOM_RESPONSE_DELAY 6
#define PUMP_CYCLE_INTERVAL 3600
#define DELAY_TO_FIRST_PUMP 15
#define FLOAT_DEBOUNCE_DELAY 3
// EEPROM STORAGE AT DEVICE PROGRAMMING TIME
#rom 0x2100 = { INITIAL_PUMP_ON_TIME }
// EEPROM ADDRESS MAP
#define EEPROM_PUMP_ON_TIME_ADR 0
// PROTOTYPE STATEMENTS
ActivateDischargePump ( char cLevelMode );
char DelaySeconds ( char cSeconds );
void RecalculateTimes ( void );
// VARIABLES
static char cPumpTimeFlag, cInterruptCount, cElapsedHours, cPumpOnTime, cMode, cBypassLedCount;
static char cPumpOnFloatCount, cPumpOffFloatCount, cPumpOnFloatFlag, cPumpOffFloatFlag, cSkip;
static char cRecalculateTimesFlag, cAccumulatePumpTimeFlag, cPumpOnTimeSkipCounter, cLedSkipFlag;
static long iPumpIntervalCount, iElapsedSeconds, iAccumulatedPumpTime, iLedCounter, iPumpLedCount;
// INTERRUPT CODE
#int_rtcc
void TimerInterrupt ( void )
{
// at 6MHz clock, gets here every 21.760mS
cInterruptCount++; // increment counter
// ACTIVITY LED
if ( cMode == TIMED_MODE )
{
// flash green LED (on for 10% of the present pump-on duration)
if ( ++iLedCounter < iPumpLedCount )
{
output_high ( ACTIVITY_LED );
}
else
{
output_low ( ACTIVITY_LED );
}
// wait one second before turning LED on again
if ( iLedCounter > ( iPumpLedCount + INTERRUPTS_PER_SECOND ) )
{
iLedCounter = 0; // reset count
}
}
else
{
if ( ++cBypassLedCount < 3 )
{
output_high ( ACTIVITY_LED ); // ~130mS flash
}
else
{
output_low ( ACTIVITY_LED );
}
if ( cBypassLedCount >= 6 ) // fast rep rate
{
cBypassLedCount = 0; // reset count
}
}
// ONE-SECOND TIMER TICK
if ( cInterruptCount >= INTERRUPTS_PER_SECOND ) // one second yet?
{
// gets here every 1 second (measures 0.998775 sec)
if ( iPumpIntervalCount++ >= PUMP_CYCLE_INTERVAL )
{
cPumpTimeFlag = YES; // signal to turn on pump, this flag turned off in the main loop
iPumpIntervalCount = 0; // reset interval
}
// display pump-on seconds by LED blink count, skip one second between sets of blinks
cPumpOnTimeSkipCounter++;
if ( cPumpOnTimeSkipCounter == cPumpOnTime )
{
cLedSkipFlag = YES; // skip LED blink for this one second period
}
if ( cPumpOnTimeSkipCounter > cPumpOnTime )
{
cLedSkipFlag = NO; // reset skip flag
cPumpOnTimeSkipCounter = 0; // reset counter
}
// time accumulation
if ( cAccumulatePumpTimeFlag == ON )
{
iAccumulatedPumpTime++; // accumulate the pump seconds, reset after recalculation in main loop
}
// recalculation trigger, actual recalculation done in main loop
if ( ++iElapsedSeconds >= 3599 ) // if one hour has passed
{
if ( ++cElapsedHours >= 23 ) // if 24 hours has passed
{
cRecalculateTimesFlag = ON; // time to recalculate based on the previous 24 hour logging
cElapsedHours = 0; // reset count
}
iElapsedSeconds = 0; // reset count
}
cInterruptCount = 0; // reset count
}
// DEBOUNCE PUMP-ON FLOAT SWITCH
if ( input ( HIGH_FLOAT ) == RAISED )
{
if ( cPumpOnFloatCount++ >= ( FLOAT_DEBOUNCE_DELAY * INTERRUPTS_PER_SECOND ) )
{
cPumpOnFloatFlag = ON; // set flag if float was continuously on for three seconds
}
}
else
{
cPumpOnFloatCount = 0; // restart count
cPumpOnFloatFlag = OFF; // reset flag
}
// DEBOUNCE PUMP-OFF FLOAT SWITCH
if ( input ( LOW_FLOAT ) == RAISED )
{
if ( cPumpOffFloatCount++ >= ( FLOAT_DEBOUNCE_DELAY * INTERRUPTS_PER_SECOND ) )
{
cPumpOffFloatFlag = ON; // set flag if float was continuously on for three seconds
}
}
else
{
cPumpOffFloatCount = 0; // restart count
cPumpOffFloatFlag = OFF; // reset flag
}
// PRE-ADJUST TIMER FOR ACCURACY BEFORE LEAVING INTERRUPT
// RTCC(0) = 1.006567 sec
// RTCC(1) = 1.002676 sec
// RTCC(2) = 0.998775 sec
set_rtcc ( 2 );
}
// MAIN CODE
void main ( void )
{
delay_ms ( 200 );
setup_counters ( RTCC_INTERNAL, RTCC_DIV_128 ); // 256 * 4uS = 1.024mS timer wrap
output_high ( ACTIVITY_LED );
output_high ( PUMP_ON_FLOAT_LED );
output_high ( PUMP_OFF_FLOAT_LED );
delay_ms ( 1000 );
output_low ( ACTIVITY_LED );
output_low ( PUMP_ON_FLOAT_LED );
output_low ( PUMP_OFF_FLOAT_LED );
delay_ms ( 1000 );
// initialize all variables
cPumpTimeFlag = OFF;
iPumpIntervalCount = PUMP_CYCLE_INTERVAL - DELAY_TO_FIRST_PUMP;
cInterruptCount = 0;
iAccumulatedPumpTime = 0;
iElapsedSeconds = 0;
cElapsedHours = 0;
cPumpOffFloatCount = 0;
cPumpOffFloatFlag = OFF;
cPumpOnFloatCount = 0;
cPumpOnFloatFlag = OFF;
cPumpOnTimeSkipCounter = 0;
cLedSkipFlag = NO;
cBypassLedCount = 0;
iLedCounter = 0;
// get stored pump time from EEPROM
cPumpOnTime = read_eeprom ( EEPROM_PUMP_ON_TIME_ADR ); // get stored value
if ( ( cPumpOnTime < 10 ) || ( cPumpOnTime > 120 ) ) // if stored value seems outlandish
{
cPumpOnTime = INITIAL_PUMP_ON_TIME; // correct the time to a reasonable starting value
}
iPumpLedCount = ( long ) cPumpOnTime * INTERRUPTS_PER_SECOND / 10; // calculate the number of interrupts for 1/10 of the pump-on time
enable_interrupts ( INT_RTCC ); // turn on timer interrupt
enable_interrupts ( GLOBAL ); // enable interrupts
while ( TRUE )
{
while ( input ( BYPASS_SWITCH ) == HIGH ) // if NOT in bypass mode
{
// TIMED DISCHARGE MODE
// pump timing is activated by interrupt
cMode = TIMED_MODE;
// Pump at timed intervals
if ( cPumpTimeFlag == ON )
{
if ( cPumpOffFloatFlag == ON ) // only if PUMP-OFF float is raised
{
ActivateDischargePump ( NORMAL_WATER_LEVEL ); // activate pump for pump-on time
}
cPumpTimeFlag = OFF; // reset flag
}
// Pump immediately if pump-on float switch is raised (shouldn't occur if timed pumping is keeping up.)
while ( cPumpOnFloatFlag == ON ) // until high float is lowered, regardless of low float setting
{
ActivateDischargePump ( HIGH_WATER_LEVEL ); // long time, indefinite time
if ( cSkip )
{
break;
}
}
// If time to recalculate pump-on times
if ( cRecalculateTimesFlag == ON )
{
RecalculateTimes();
}
}
while ( input ( BYPASS_SWITCH ) == LOW ) // if in bypass mode
{
// VOLUME DISCHARGE MODE (normal Vericom mode)
// simply pass float switches through to their relays
cMode = VOLUME_MODE;
// PUMP-ON FLOAT SWITCH
if ( input ( HIGH_FLOAT ) == RAISED )
{
output_high ( PUMP_ON_FLOAT_LED );
output_high ( PUMP_ON_RELAY );
cAccumulatePumpTimeFlag = ON; // set flag to enable time logging
}
else
{
output_low ( PUMP_ON_FLOAT_LED );
output_low ( PUMP_ON_RELAY );
}
// PUMP-OFF FLOAT SWITCH
if ( input ( LOW_FLOAT ) == RAISED )
{
output_high ( PUMP_OFF_FLOAT_LED );
output_high ( PUMP_OFF_RELAY );
}
else
{
output_low ( PUMP_OFF_FLOAT_LED );
output_low ( PUMP_OFF_RELAY );
cAccumulatePumpTimeFlag = OFF; // reset flag to stop time logging
}
// If time to recalculate pump-on times
if ( cRecalculateTimesFlag == ON )
{
RecalculateTimes();
}
}
// RESET RELAYS AND LEDs
output_low ( PUMP_ON_FLOAT_LED );
output_low ( PUMP_ON_RELAY );
output_low ( PUMP_OFF_FLOAT_LED );
output_low ( PUMP_OFF_RELAY );
output_low ( ACTIVITY_LED );
}
}
ActivateDischargePump ( char cLevelMode )
{
char cCnt;
output_high ( PUMP_OFF_RELAY ); // simulate PUMP-OFF float raised to Vericom
output_high ( PUMP_OFF_FLOAT_LED );
cSkip = DelaySeconds ( VERICOM_RESPONSE_DELAY ); // Vericom response delay
if ( !cSkip )
{
output_high ( PUMP_ON_RELAY ); // simulate PUMP-ON float raised to Vericom
output_high ( PUMP_ON_FLOAT_LED );
cSkip = DelaySeconds ( VERICOM_RESPONSE_DELAY ); // Vericom response delay before turning pump on
if ( !cSkip )
{
// PUMP TURNS ON
cAccumulatePumpTimeFlag = ON; // set flag to enable time logging
output_low ( PUMP_ON_RELAY ); // simulate PUMP-ON float lowered, water level dropping
output_low ( PUMP_ON_FLOAT_LED );
// Time the PUMP-ON state
if ( cLevelMode == NORMAL_WATER_LEVEL )
{
// don't know if Vericom debounces low-going float like high-going float
for ( cCnt = 0; cCnt < ( cPumpOnTime - VERICOM_RESPONSE_DELAY ); cCnt++ ) // delay before simulating low float drop
{
DelaySeconds ( 1 ); // delay one second
if ( cSkip )
{
break;
}
if ( cPumpOffFloatFlag == OFF ) // check PUMP-OFF float, stop pumping if float drops
{
break; // high float went low, skip out, done pumping
}
}
}
else // else HIGH_WATER_LEVEL
{
while ( cPumpOnFloatFlag == ON ) // do until PUMP-ON float drops
{
DelaySeconds ( 1 ); // delay one second
// skip out if mode switch changed or low float drops (just in case)
if ( ( cSkip ) || ( cPumpOffFloatFlag == OFF ) )
{
break;
}
}
}
if ( !cSkip )
{
output_low ( PUMP_OFF_RELAY); // simulate PUMP-OFF float lowered to Vericom
output_low ( PUMP_OFF_FLOAT_LED );
cSkip = DelaySeconds ( VERICOM_RESPONSE_DELAY ); // Vericom response delay before turning pump off
}
// PUMP TURNS OFF
cAccumulatePumpTimeFlag = OFF; // reset flag to stop time logging
}
}
}
char DelaySeconds ( char cSeconds )
{
char cCnt;
// checks if MODE switch went into BYPASS mode, and returns 1 for skip out
for ( cCnt = 0; cCnt < cSeconds; cCnt++ )
{
if ( input ( BYPASS_SWITCH ) == LOW ) // if in BYPASS MODE
{
return ( YES ); // skip out
}
delay_ms ( 998 ); // allow for interrupt duration that expands delays in main loop
}
return ( NO ); // don't skip out
}
void RecalculateTimes ( void )
{
cPumpOnTime = (char) ( iAccumulatedPumpTime / 24 ); // seconds per hour based on logging
iAccumulatedPumpTime = 0; // reset count
if ( cPumpOnTime < 10 ) // prevent going too low
{
cPumpOnTime = 10;
}
// save new calculated time to EEPROM in case power is ever dropped
write_eeprom ( EEPROM_PUMP_ON_TIME_ADR, cPumpOnTime ); // save it in EEPROM
// calculate the number of interrupts for 1/10 of the pump-on time for LED display
iPumpLedCount = ( ( long ) cPumpOnTime * INTERRUPTS_PER_SECOND ) / 10;
cRecalculateTimesFlag = OFF; // reset flag
}