Pool Salt Water Chlorine Generator

For my first project I have used an Arduino to replace a Sonix mcu to control an Intex 8110 swimming pool chlorinator. It allows you to program in a run time that it will remember if the power is lost (because I have it hooked to an in-expensive timer with the pool pump.) It does not measure salinity, and will not stop trying to produce chlorine unless there is inadequate water flow. It does self clean by reversing power halfway through the program.

I would like to modify the program to be able to adjust the reverse time to be independent of the programmed run time. Also, a program that works on a percentage power level setting would be nice, as that is what most of the other SWGs on the market use (ie, at 50% it is on for 30 minutes of an hour, or half of a longer/shorter programmable period) I am stumped at how to keep track of the ON time with out excessive writing to the EEPROM, as the pump timer can shut off at various times, mid generation even.

Any and all suggestions to improve the program would be welcomed!
(sorry about the code, I was using the "Copy for Forum" and I didn't realize it didn't put it in a box. I'll re-post it in parts)

Use the [ code] [ /code] tags!

Take off any tags you may have added. Then highlight the code and click on the # button in the message window.

my scrollong finger hurts!!
proper use of tags might be in order

/*
  Intex 6110/8110 Electronic Salt Water Chlorine Generator Control
  ***Program Description***
  When powered ON will delay for a "StartDelay" time in minutes.
  While in delay Flashing HOURS of RUN TIME can be adjusted UP or DOWN 
  (with the /\ and \/ buttons) until "StartDelay" is complete. Display will blank
  for one minute and then display remaining HOURS of RUN TIME while
  powering the Chlorine Generator Cell for the set HOURS of RUN TIME,
  reversing the polarity 1/2 thru to self clean. If FLOW Switch does not
  sense water flow through the cell, the display will BLANK, and Chlorine
  production will halt until flow resumes. 
  ***Intex Hardware Description***
  The Input/Display board is re-used with minor modifications, while the
  Main Control board is modified to retain only the relays and power parts.
  The Arduino replaces the Sonix mcu, which was removed, as were most other
  components not used for the relays or display. The display LEDs use 1k resistors,
  the input buttons and flow detector switch use the internal pull-up resistors,
  the relays are powered thru 1n1001 transistors.  
  Relay K1 connects (+) power from the rectifier to K2 & K3 relays NC terminals
  and +12V power to their coils. Power flows from the wall plug -> the transformer,
  -> the rectifier, -> relays K2 and K3, -> through Fuses F1 and F2, -> cell.
  Relays K2 and K3 LOW =Cell IDLE.
  K2 LOW and K3 HIGH = FORWARD (normal) polarity.
  K2 HIGH and K3 LOW = REVERSE (cleaning) polarity.
  FanPin controls the rectifier cooling fan = HIGH when cell is powered.
  FLOW Switch is NORMALY OPEN, CLOSES with GOOD FLOW, by using internal PULL
  UP resistors, GOOD FLOW = 0. BAD FLOW = 1 will blank LED displayand IDLE cell.
  UP and DOWN BUTTONs allow setting RUN TIME from 1-9 hours (60-540 minutes)
  which is stored in EEPROM memory, safe from power loss.
  Minute counter resets at 1440 minutes (24 hours) if power is left ON.
  */
//==================================================================
//START with definitions
//==================================================================

#include "EEPROM.h"  //so we can save the StartDelay and Run times
#include "MsTimer2.h"// so we can have timer control
int StartDelay = 60;  //***StartUpDelay Time in Minutes*** I like an hour so that pump has stirred things up
volatile unsigned char tick;
int Mode = 0;
int seconds = 0;
int minutes = 0;     // Inititialise actual values for m,s
int hours = 0;        //for Run time Display
int CycleTime = 0;    //This will be the Total Chlorine Generation Time 
const int DnButtonPin = 17;              //DOWN Pushbutton uses internal PULL UP resistor
const int UpButtonPin = 16;              //UP Pushbutton uses internal PULL UP resistor
const int debounce = 80;
//int HRPinState = 0;                //Variab;e for reading the HourPin Status
int FlowPin = 15;               // Flow switch uses internal PULL UP resistor GOOD FLOW =0
int FlowState = 0;             // variable for reading the FLOWstatus
int K1Pin = 9;                  //Relay k1 connected to digital pin 3 for power
int K2Pin = 10;                  //Relay k2 connected to digital pin 4 and Cell (+)
int K3Pin = 11;                  //Relay k3 connected to digital pin 5 Cell (-)
int FanPin = 12;                  //Relay k3 connected to digital pin 6 Rectifier Cooling Fan
int ledPin[7] = {2, 3, 4, 5, 6, 7, 8};  // 7-segment LEDs
int RpwrPin = 19;               //Power for Right 7-segment LED
int LpwrPin = 18;              // Power for Left 7-segment LED
int UpbuttonState1 = 1;         // variable for reading the pushbutton status
int UpbuttonState2 = 1;
int DnbuttonState1 = 1;
int DnbuttonState2 = 1;
int Rcount = 4;                    //value for the Right LED
int Lcount = 0;                    //Value for the Left LED
int ctRead = 0;                    // Count value read from the EEPROM
//Segment to Pin assignments for the 7 segment LEDs
// Adjust this for your 7 segment LED spec.
//                 A,  B, C, D, E,  F,  G
int Seg2Pin[8] = { 2, 3, 4, 5, 6, 7, 8};

//Digit to Segment You don't need modify this unless you want to change digits.
int Dec2Seg[11] =
{
//A(125), B(64), C(32), D(16), E(8), F(4), G(2)
  0x07E, //Digit 0
  0x00C, //Digit 1
  0x0B6, //Digit 2
  0x09E, //Digit 3
  0x0CC, //Digit 4
  0x0DA, //Digit 5
  0x0FB, //Digit 6
  0x00E, //Digit 7
  0x0FF, //Digit 8
  0x0DF, //Digit 9
 };

//==================================================================
//Global Functions and Descriptions
//==================================================================

void clearAllSegments()               //Turns off all LED segments
{
  for (int i = 0; i < 8; i++)  
  {
    digitalWrite(ledPin[i], HIGH);
  }
}

//-----------------------------------------------------------------

void drawDigit(int digit)            //Draw digit to LED outputs
{
  int seg = Dec2Seg[digit];
  int pin = 0;

  while (seg > 0)
  {
    seg /= 2;
    if (seg & 0x1)
      digitalWrite(Seg2Pin[pin], LOW);   // sets the LED on
    pin++;
  }
}

//-----------------------------------------------------------------

void display_time () {          // Function to display the time to console
//Serial.print(hours);
//Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.println(seconds);
}

//----------------------------------------------------------------

void increment_time() {             //Second counting timer function
    seconds++;                      // We're counting UP
    if (seconds > 59) {                  // If seconds have rolled over
        seconds = 0;                  //    Reset seconds
        minutes++;                  //    and increment the minutes
        if (minutes > 1439) {            // Minutes in a Day have rolled over
            minutes = 0;            //    Reset minutes
        }
}
   
    tick++;                             // indicate that the time has been updated
}

//----------------------------------------------------------------

void SetTime(){
UpbuttonState1 = digitalRead(UpButtonPin); // read the state of the pushbutton value:
if (UpbuttonState1 == LOW){
  delay(debounce); //debounce
  UpbuttonState2 = digitalRead(UpButtonPin); // check if the pushbutton is pressed, if it is, the buttonState is LOW:
    if (UpbuttonState2 ==LOW ) {     
      if (Rcount <= 8)    //MAX Time is 9
        {Rcount++;}
      else {Rcount = 1;}  //MIN Time is 1
      }
  else {
      ;  }
    } 
else {
    ; 
}
  
DnbuttonState1 = digitalRead(DnButtonPin);
if (DnbuttonState1 == LOW){
  delay(debounce);   //debounce
  DnbuttonState2 = digitalRead(DnButtonPin);  // check if the pushbutton is pressed, if it is, the buttonState is HIGH:
    if (DnbuttonState2== LOW) {     
      if (Rcount >= 2)  //MIN Time is 1
      {Rcount--;}
       else {Rcount = 9;}  //MAX Time is 9
    } 
  else {
      ;}
 }
 else {
      ;}

//Now Display count value on LED
  digitalWrite (LpwrPin, LOW); //for the display
  digitalWrite (RpwrPin, LOW); //for the display
   drawDigit(Rcount);
  digitalWrite (RpwrPin, HIGH); //for the display
   delay(1);                  // waits for a second
    clearAllSegments();
  digitalWrite (RpwrPin, LOW); //for the display
   delay(1);
  digitalWrite (LpwrPin, HIGH); //for the display
   drawDigit(Lcount);
   delay(1);                  // waits for a second
  digitalWrite (LpwrPin, LOW); //for the display
  clearAllSegments();
}

//=============================================================
// Setup Routine Information
//=============================================================

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600); // start up serial communications
  pinMode(FlowPin, INPUT);     // sets the digital pin as input
  pinMode(UpButtonPin, INPUT);
  pinMode(DnButtonPin, INPUT);  
  pinMode(K1Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K2Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K3Pin, OUTPUT);      // sets the digital pin as output
  pinMode(FanPin, OUTPUT);      // sets the digital pin as output
  pinMode(RpwrPin, OUTPUT);
  pinMode(LpwrPin, OUTPUT);
  for (int i = 0; i < 8; i++)
    pinMode(ledPin[i], OUTPUT);      // sets the digital pin as output
  digitalWrite(UpButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(DnButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(FlowPin, HIGH);  //Turn on the pull up resistors
  display_time();                  // Show initial time
  Idle();                            //Set relays OFF
  // Get ct from the EEPROM
  ctRead = EEPROM.read(0);
  delay(100);
  Rcount = ctRead; //**ERROR** MUST comment out the first time if a value has not ever been stored before!!!!
  MsTimer2::set(1000, increment_time);// every second increment_time() is called
  MsTimer2::start();
}

//==================================================================
//Main Program LOOP
//Main Program LOOP
//==================================================================

void loop(){
 
  if (tick) {         // If a tick has occurred
      tick = 0;
        Serial.println(""); // This prints to the console for debugging
        Serial.print("Time is ");         //    reset indicator that we have a new time
      display_time();  //    and show it off!
        Serial.print("Flow Status = ");
        if (FlowState == 0) {Serial.println("GOOD FLOW");}
        if (FlowState == 1) {Serial.println("BAD FLOW");}
        Serial.print("Cell Power Mode is ");
        if (Mode == 0) {Serial.println("WAITING");}
        if (Mode == 1) {Serial.println("FORWARD"); // Make Chlorine, show Run Time remaining
          digitalWrite (RpwrPin, HIGH); //Turn ON the display
          hours=(Rcount -((minutes - (StartDelay)) / 60));  //Calculate how many Run Time Hours are left 
          drawDigit(hours);  //Show remaining Run Time Hours
        }
        if (Mode == 2) {Serial.println("REVERSE");
        hours=(Rcount -((minutes - (StartDelay)) / 60)); //Calculate how many Run Time Hours are left
        drawDigit(hours);  //Show remaining Run Time Hours
        }
        if (Mode == 3) {Serial.println("DONE");
        digitalWrite (RpwrPin, LOW); //Turn Off the display
        }
  }
// While there is GOOD FLOW run the program
FlowState = digitalRead(FlowPin); //Check for GOOD FLOW
if (FlowState == 0){
//If minutes < delay, then check for button input to change run time 
if (minutes < (StartDelay)) {
      Mode = 0;
      SetTime();
   }

if (minutes == (StartDelay)) {CycleTime = Rcount * 60;} // Start-up delay is over, Now use Input Count for Cycle Time

if (minutes > (StartDelay) && minutes < ((CycleTime / 2) + (StartDelay))) {            
            if (Rcount != ctRead)                   //Was Count for Hours of Operation changed?
                    {EEPROM.write(0, int(Rcount));      // Then Write NEW count to the EEPROM
                     delay(20);}
                ForwardPower();
   }            //Generate Chlorine in Normal Mode for ?? minutes

if (minutes > ((CycleTime / 2) + (StartDelay)) && minutes < ((CycleTime) + (StartDelay))) {
            ReversePower();
      }            // Generate Chlorine in Reverse Mode for ?? minutes
      
if (minutes > ((CycleTime) + (StartDelay))) {
        Idle();
        Mode = 3;
     }
}
else {
Idle();
Mode = 0;                         //BAD FLOW sit Idle WAITING
digitalWrite (RpwrPin, LOW);     // And Turn Off the LED display
}
}

//===========================================================
// Cell Relay Power Control Functions to Generate Chlorine
//===========================================================

 void Idle(){
  digitalWrite(K1Pin, HIGH);        // Connects +5V & +24V power to K2 & K3
  digitalWrite(K2Pin, LOW);         // CELL IDLE - Connects Cell (+) to +24V
  digitalWrite(K3Pin, LOW);         // CELL IDLE - Connects Cell (-) to +24V
  //when done turn the fan off
  digitalWrite(FanPin, LOW);        // sets the FAN off, after cooling the rectif
 }
 
void ForwardPower(){                     // Generate Chlorine by putting power to cell
  Mode = 1;
  // Apply Positve polarity power to cell
 digitalWrite(K3Pin, HIGH);        // NORMAL POLARITY - Connects Cell (-) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier  
}

 void ReversePower(){                //REVERSE polarity for SELF CLEANING 
  Mode = 2;
  digitalWrite(K2Pin, HIGH);        // REVERSE POLARITY - Connects Cell (+) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier
  }

I wanted more time between self cleaning reverse cycles so I changed the program run in the same mode for a set number of cycles. If set to 4 cycles, with a 2 hour run time, it will be 8 hours of operation before self cleaning now. Before it would have reversed at 1/2 of the run time, or only 1 hour of operation. This should cut down on wear of the cell. I will post the entire program, but will take 2 posts.
Here goes -

/*Cycles
  Intex 8110 Electronic Salt Water Chlorine Generator Control
  ***Program Description***
  When powered ON will delay for a "StartDelay" time in minutes.
  While in delay Flashing HOURS of RUN TIME can be adjusted UP or DOWN 
  (with the /\ and \/ buttons) until "StartDelay" is complete. Display will blank
  for one minute and then display remaining HOURS of RUN TIME while
  powering the Chlorine Generator Cell for the set HOURS of RUN TIME,
  reversing the polarity to self clean after the number of cycles set by "int Cycles =".
  If FLOW Switch does not sense water flow through the cell, the display will BLANK,
  and Chlorine production will halt until flow resumes. 
  ***Intex Hardware Description***
  The Input/Display board is re-used with minor modifications, while the
  Main Control board is modified to retain only the relays and power parts.
  The Arduino replaces the Sonix mcu, which was removed, as were most other
  components not used for the relays or display. The display LEDs use 1k resistors,
  the input buttons and flow detector switch use the internal pull-up resistors,
  the relays are powered thru 1n1001 transistors.  
  Relay K1 connects (+) power from the rectifier to K2 & K3 relays NC terminals
  and +12V power to their coils. Power flows from the wall plug -> the transformer,
  -> the rectifier, -> relays K2 and K3, -> through Fuses F1 and F2, -> cell.
  Relays K2 and K3 LOW =Cell IDLE.
  K2 LOW and K3 HIGH = FORWARD (normal) polarity.
  K2 HIGH and K3 LOW = REVERSE (cleaning) polarity.
  FanPin controls the rectifier cooling fan = HIGH when cell is powered.
  FLOW Switch is NORMALY OPEN, CLOSES with GOOD FLOW, by using internal PULL
  UP resistors, GOOD FLOW = 0. BAD FLOW = 1 will blank LED display and IDLE cell.
  UP and DOWN BUTTONs allow setting RUN TIME from 1-9 hours (60-540 minutes)
  which is stored in EEPROM memory, safe from power loss.
  Minute counter resets at 1440 minutes (24 hours) if power is left ON.
  Change Log
  4-27 changed Idle, Forward and Reverse to Mode0, Mode1 and Mode2
  4-28 changed to cycle count for reversing
  */
//==================================================================
//START with definitions
//==================================================================

#include "EEPROM.h"  //so we can save the StartDelay and Run times
#include "MsTimer2.h"// so we can have timer control
int StartDelay = 60;  //***StartUpDelay Time in Minutes*** I like an hour so that pump has stirred things up
volatile unsigned char tick;
int Mode = 0;          //Cell Power Mode 0 = None, 1 = Forward, 2 = Reverse(Self-Cleaning)
int seconds = 0;
int minutes = 0;     // Inititialise actual values for m,s
int hours = 0;        //for Run time Display
int CycleTime = 0;    //This will be the Total Chlorine Generation Time 
int Cycles = 0;          //Number of cycles to run before self cleaning
const int DnButtonPin = 17;              //DOWN Pushbutton uses internal PULL UP resistor
const int UpButtonPin = 16;              //UP Pushbutton uses internal PULL UP resistor
const int debounce = 80;
int FlowPin = 15;               // Flow switch uses internal PULL UP resistor GOOD FLOW =0
int FlowState = 0;             // variable for reading the FLOWstatus
int K1Pin = 9;                  //Relay k1 connected to digital pin 3 for power
int K2Pin = 10;                  //Relay k2 connected to digital pin 4 and Cell (+)
int K3Pin = 11;                  //Relay k3 connected to digital pin 5 Cell (-)
int FanPin = 12;                  //Relay k3 connected to digital pin 6 Rectifier Cooling Fan
int ledPin[7] = {2, 3, 4, 5, 6, 7, 8};  // 7-segment LEDs
int RpwrPin = 19;               //Power for Right 7-segment LED
int LpwrPin = 18;              // Power for Left 7-segment LED
int UpbuttonState1 = 1;         // variable for reading the pushbutton status
int UpbuttonState2 = 1;
int DnbuttonState1 = 1;
int DnbuttonState2 = 1;
int Rcount = 4;                    //value for the Right LED
int Lcount = 0;                    //Value for the Left LED
int ctRead = 0;                    // Count value read from the EEPROM
int CyRead = 0;                    // Count of number of cycles already run
int MoRead = 0;                    //Stored Power Mode to opererate cell in
//=========================================================================
//Segment to Pin assignments for the 7 segment LEDs
// Adjust this for your 7 segment LED spec.
//                 A,  B, C, D, E,  F,  G
int Seg2Pin[8] = { 2, 3, 4, 5, 6, 7, 8};

//Digit to Segment You don't need modify this unless you want to change digits.
int Dec2Seg[11] =
{
//A(125), B(64), C(32), D(16), E(8), F(4), G(2)
  0x07E, //Digit 0
  0x00C, //Digit 1
  0x0B6, //Digit 2
  0x09E, //Digit 3
  0x0CC, //Digit 4
  0x0DA, //Digit 5
  0x0FB, //Digit 6
  0x00E, //Digit 7
  0x0FF, //Digit 8
  0x0DF, //Digit 9
 };

//==================================================================
//Global Functions and Descriptions
//==================================================================

void clearAllSegments()               //Turns off all LED segments
{
  for (int i = 0; i < 8; i++)  
  {
    digitalWrite(ledPin[i], HIGH);
  }
}

//-----------------------------------------------------------------

void drawDigit(int digit)            //Draw digit to LED outputs
{
  int seg = Dec2Seg[digit];
  int pin = 0;

  while (seg > 0)
  {
    seg /= 2;
    if (seg & 0x1)
      digitalWrite(Seg2Pin[pin], LOW);   // sets the LED on
    pin++;
  }
}

//-----------------------------------------------------------------

void display_time () {          // Function to display the time to console
//Serial.print(hours);
//Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.println(seconds);
}

//----------------------------------------------------------------

void increment_time() {             //Second counting timer function
    seconds++;                      // We're counting UP
    if (seconds > 59) {                  // If seconds have rolled over
        seconds = 0;                  //    Reset seconds
        minutes++;                  //    and increment the minutes
        if (minutes > 1439) {            // Minutes in a Day have rolled over
            minutes = 0;            //    Reset minutes
        }
}
   
    tick++;                             // indicate that the time has been updated
}

//----------------------------------------------------------------

void SetTime(){
UpbuttonState1 = digitalRead(UpButtonPin); // read the state of the pushbutton value:
if (UpbuttonState1 == LOW){
  delay(debounce); //debounce
  UpbuttonState2 = digitalRead(UpButtonPin); // check if the pushbutton is pressed, if it is, the buttonState is LOW:
    if (UpbuttonState2 ==LOW ) {     
      if (Rcount <= 8)    //MAX Time is 9
        {Rcount++;}
      else {Rcount = 1;}  //MIN Time is 1
      }
  else {
      ;  }
    } 
else {
    ; 
}
  
DnbuttonState1 = digitalRead(DnButtonPin);
if (DnbuttonState1 == LOW){
  delay(debounce);   //debounce
  DnbuttonState2 = digitalRead(DnButtonPin);  // check if the pushbutton is pressed, if it is, the buttonState is HIGH:
    if (DnbuttonState2== LOW) {     
      if (Rcount >= 2)  //MIN Time is 1
      {Rcount--;}
       else {Rcount = 9;}  //MAX Time is 9
    } 
  else {
      ;}
 }
 else {
      ;}

//Now Display count value on LED
  digitalWrite (LpwrPin, LOW); //for the display
  digitalWrite (RpwrPin, LOW); //for the display
   drawDigit(Rcount);
  digitalWrite (RpwrPin, HIGH); //for the display
   delay(1);                  // waits for a second
    clearAllSegments();
  digitalWrite (RpwrPin, LOW); //for the display
   delay(1);
  digitalWrite (LpwrPin, HIGH); //for the display
   drawDigit(Lcount);
   delay(1);                  // waits for a second
  digitalWrite (LpwrPin, LOW); //for the display
  clearAllSegments();
}

//=============================================================
// Setup Routine Information
//=============================================================

Here's part 2 -

//=============================================================
// Setup Routine Information
//=============================================================

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600); // start up serial communications
  pinMode(FlowPin, INPUT);     // sets the digital pin as input
  pinMode(UpButtonPin, INPUT);
  pinMode(DnButtonPin, INPUT);  
  pinMode(K1Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K2Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K3Pin, OUTPUT);      // sets the digital pin as output
  pinMode(FanPin, OUTPUT);      // sets the digital pin as output
  pinMode(RpwrPin, OUTPUT);
  pinMode(LpwrPin, OUTPUT);
  for (int i = 0; i < 8; i++)
    pinMode(ledPin[i], OUTPUT);      // sets the digital pin as output
  digitalWrite(UpButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(DnButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(FlowPin, HIGH);  //Turn on the pull up resistors
  display_time();                  // Show initial time
  Mode0();                            //Set relays OFF
  // Get ct from the EEPROM
  ctRead = EEPROM.read(0);
  delay(100);
  Rcount = ctRead; //**ERROR** MUST comment out the first time if a value has not ever been stored before!!!!
  MsTimer2::set(1000, increment_time);// every second increment_time() is called
  MsTimer2::start();
}

//==================================================================
//Main Program LOOP
//==================================================================

void loop(){
 
  if (tick) {         // If a tick has occurred
      tick = 0;
        Serial.println(""); // This prints to the console for debugging
        Serial.print("Time is ");         //    reset indicator that we have a new time
      display_time();  //    and show it off!
        Serial.print("Flow Status = ");
        if (FlowState == 0) {Serial.println("GOOD FLOW");}
        if (FlowState == 1) {Serial.println("BAD FLOW");}
        Serial.print("Cell Power Mode is ");
        if (Mode == 0) {Serial.println("WAITING");}
        if (Mode == 1) {Serial.println("FORWARD"); // Make Chlorine, show Run Time remaining
          digitalWrite (RpwrPin, HIGH); //Turn ON the display
          hours=(Rcount -((minutes - (StartDelay)) / 60));  //Calculate how many Run Time Hours are left 
          drawDigit(hours);  //Show remaining Run Time Hours
        }
        if (Mode == 2) {Serial.println("REVERSE");
        hours=(Rcount -((minutes - (StartDelay)) / 60)); //Calculate how many Run Time Hours are left
        drawDigit(hours);  //Show remaining Run Time Hours
        }
        if (Mode == 3) {Serial.println("DONE");
        digitalWrite (RpwrPin, LOW); //Turn Off the display
        }
  }
// While there is GOOD FLOW run the program
FlowState = digitalRead(FlowPin); //Check for GOOD FLOW
if (FlowState == 0){
//If minutes < delay, then check for button input to change run time 
if (minutes < (StartDelay)) {
      Mode = 0;
      SetTime();
   }

if (minutes == (StartDelay)) {CycleTime = Rcount * 60;} // Start-up delay is over, Now use Input Count for Cycle Time

if (minutes > (StartDelay) && minutes < ((CycleTime) + (StartDelay))) {            
            if (Rcount != ctRead)                   //Was Count for Hours of Operation changed?
                    {EEPROM.write(0, int(Rcount));      // Then Write NEW count to the EEPROM
                     delay(20);}
                MoRead = EEPROM.read(1);                //Read what Cell Power Mode to use
                if (MoRead == 1) {Mode1();
              }
                else {Mode2();
              }
   }            //Generate Chlorine in Last Saved Mode for this cycle

if (minutes > ((CycleTime) + (StartDelay))) {
        Mode0();
        Mode = 3;    //Cycle completed, go back to waiting
        CyRead = EEPROM.read(2);    //Read how many cycles have been run before
         CyRead ++;                    //And add 1 to it
         if (CyRead >= Cycles)    //Next Time self clean
          {if (MoRead ==2)
          {MoRead = 1;}
        else {MoRead = 2;}
    EEPROM.write(1, int(MoRead));    //Write the Mode to use next time to memory
    EEPROM.write(2, 0);    //Set the cycles run since self cleaning to 0  
              }
      else {EEPROM.write(2, (CyRead));}  // Write the updated cycle count to memory
      }
else {
Mode0();
Mode = 0;                         //BAD FLOW sit Idle WAITING
digitalWrite (RpwrPin, LOW);     // And Turn Off the LED display
}
}
}
//===========================================================
// Cell Relay Power Control Functions to Generate Chlorine
//===========================================================

 void Mode0() {                     // CELL is IDLED - No Power
  digitalWrite(K1Pin, HIGH);        // Connects +5V & +24V power to K2 & K3
  digitalWrite(K2Pin, LOW);         // CELL IDLE - Connects Cell (+) to +24V
  digitalWrite(K3Pin, LOW);         // CELL IDLE - Connects Cell (-) to +24V
  //when done turn the fan off
  digitalWrite(FanPin, LOW);        // sets the FAN off, after cooling the rectif
 }
//______________________________________________________________
//         CHLORINE GENERATION POWER MODES
//______________________________________________________________
void Mode1() {                     // FORWARD power to cell
  Mode = 1;                        // Apply Positve polarity power to cell
 digitalWrite(K3Pin, HIGH);        // NORMAL POLARITY - Connects Cell (-) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier  
}

 void Mode2() {                    //REVERSE polarity for SELF CLEANING 
  Mode = 2;                        //Apply REVERSE Polarity power to cell
  digitalWrite(K2Pin, HIGH);        // REVERSE POLARITY - Connects Cell (+) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier
  }

I hope that you find this useful!
I welcome any comments/suggestions on how to improve this project, what I am after is a low cost, easy to use, effective Electronic Chlorine Generation system for use in residential pools.

Thanks for the info as this was on my list of things to do! :slight_smile:

You're Welcome!!! I would be happy to help if you need any of the info I have. I am not a programmer, so my program logic could be flawed, and certainly can be improved! I tried to comment it enough so that I can decipher my own coding for future use. I have pretty much stripped the control board of a 2008 model 8110. It is much easier to identify components, and guess at their intended use when you can see the circuit connections. It would probably be MUCH more useful to a majority of Intex owners if the SONIX MCU could just be re-programmed in place. Any SONIX experts out there????
There are some other Intex control projects here - http://x.havuz.org/viewtopic.php?f=26&t=1101&sid=c81c7e315d93598d31ba11e7f7b10cc8
which is where I got some of my ideas from.I also got the best idea, to use the Arduino, from this post - http://www.troublefreepool.com/diy-automated-controls-advice-solicited-t5708.html
which is what really inspired me to get going! TFP is a really great forum for any pool owner, too!!!

I was working on modifications to the program and found that it wasn't working as I thought it was, so I spent a lot of time fixing and debugging the cycle counter. I hope that no one else struggled with this! I thought that the loop only ran when there was a tick,oops! The count kept climbing in between ticks and so I tried using a seconds test, but that didn't work either! I got it to work like I wanted by using a boolean check. For some odd reason the EEPROM memory location (2) didn't work for me either, switching it to (3) worked. I also added more comments, and tried to clean up the code. As an aid to debugging I inserted several console print statements so that it is easy to tell what is going on. It will be easy to add a LCD to the unit, and also add voltage and current readings to infer salinity for a salt display, that's for another day, though!
Here is the new, tested code, in 2 parts -

/*Count Cycles
  Intex 8110 Electronic Salt Water Chlorine Generator Control
  ***Program Description***
  When powered ON will delay for a "StartDelay" time in minutes.
  While in delay Flashing HOURS of RUN TIME can be adjusted UP or DOWN 
  (with the /\ and \/ buttons) until "StartDelay" is complete. Display will blank
  for one minute and then display remaining HOURS of RUN TIME while
  powering the Chlorine Generator Cell for the set HOURS of RUN TIME,
  reversing the polarity to self clean after the number of cycles set by "int Cycles =".
  If FLOW Switch does not sense water flow through the cell, the display will BLANK,
  and Chlorine production will halt until flow resumes. 
  ***Intex Hardware Description***
  The Input/Display board is re-used with minor modifications, while the
  Main Control board is modified to retain only the relays and power parts.
  The Arduino replaces the Sonix mcu, which was removed, as were most other
  components not used for the relays or display. The display LEDs use 1k resistors,
  the input buttons and flow detector switch use the internal pull-up resistors,
  the relays are powered thru 1n1001 transistors.  
  Relay K1 connects (+) power from the rectifier to K2 & K3 relays NC terminals
  and +12V power to their coils. Power flows from the wall plug -> the transformer,
  -> the rectifier, -> relays K2 and K3, -> through Fuses F1 and F2, -> cell.
  Relays K2 and K3 LOW =Cell IDLE.
  K2 LOW and K3 HIGH = FORWARD (normal) polarity.
  K2 HIGH and K3 LOW = REVERSE (cleaning) polarity.
  FanPin controls the rectifier cooling fan = HIGH when cell is powered.
  FLOW Switch is NORMALY OPEN, CLOSES with GOOD FLOW, by using internal PULL
  UP resistors, GOOD FLOW = 0. BAD FLOW = 1 will blank LED display and IDLE cell.
  UP and DOWN BUTTONs allow setting RUN TIME from 1-9 hours (60-540 minutes)
  which is stored in EEPROM memory, safe from power loss.
  Minute counter resets at 1440 minutes (24 hours) if power is left ON.
  Change Log
  4-27 changed Idle, Forward and Reverse to Mode0, Mode1 and Mode2
  4-28 changed to cycle count for reversing
  5-12 changed/added boolean cycle over test to fix erratic counting
  and added more comments, cleaned up code, tested several times by changing 
  from 60 to 6 in 3 places **noted in comments** to speed up runs
  */
//==================================================================
//START with definitions and User settings
//==================================================================

#include "EEPROM.h"  //so we can save the StartDelay and Run times
#include "MsTimer2.h"// so we can have timer control
//-------------------------------------------------------------------
//            USER CHANGEABLE OPERATIONAL SETTINGS
//-------------------------------------------------------------------
int StartDelay = 60;  //***StartUpDelay Time in Minutes*** I like an hour so that pump has stirred things up
int Cycles = 5;          //Number of cycles to run before self cleaning
// INTEX used 20 hours so try to match that by dividing 20 by your needed run time
// eg. with a 1 hour run time,  20/1=20 so Cycles=20
//     with a 4 hour run time,  20/4=5 so Cycles=5
//     with a 5 hour run time,  20/5=4 so Cycles=4
//-------------------------------------------------------------------
//-------------------------------------------------------------------
volatile unsigned char tick;
int Mode = 1;          //Cell Power Mode 0 = None, 1 = Forward, 2 = Reverse(Self-Cleaning)
int seconds = 0;
int minutes = 0;     // Inititialise actual values for m,s
int hours = 0;        //for Run time Display
int CycleTime = 0;    //This will be the Total Chlorine Generation Time 
int zero = 0;        // used to reset cycle counter count
boolean CycleOver = false;    // Use to add  to saved cycle count
const int DnButtonPin = 17;              //DOWN Pushbutton uses internal PULL UP resistor
const int UpButtonPin = 16;              //UP Pushbutton uses internal PULL UP resistor
const int debounce = 80;
int FlowPin = 15;               // Flow switch uses internal PULL UP resistor GOOD FLOW =0
int FlowState = 0;             // variable for reading the FLOWstatus
int K1Pin = 9;                  //Relay k1 connected to digital pin 3 for power
int K2Pin = 10;                  //Relay k2 connected to digital pin 4 and Cell (+)
int K3Pin = 11;                  //Relay k3 connected to digital pin 5 Cell (-)
int FanPin = 12;                  //Relay k3 connected to digital pin 6 Rectifier Cooling Fan
int ledPin[7] = {2, 3, 4, 5, 6, 7, 8};  // 7-segment LEDs
int RpwrPin = 19;               //Power for Right 7-segment LED
int LpwrPin = 18;              // Power for Left 7-segment LED
int UpbuttonState1 = 1;         // variables for reading the pushbutton status
int UpbuttonState2 = 1;
int DnbuttonState1 = 1;
int DnbuttonState2 = 1;
int Rcount = 4;                    //value for the Right LED
int Lcount = 0;                    //Value for the Left LED
int ctRead = 0;                    // Count value read from the EEPROM
int CyRead = 0;                    // Count of number of cycles already run
int MoRead = 0;                    //Stored Power Mode to opererate cell in
//=========================================================================
//Segment to Pin assignments for the 7 segment LEDs
// Adjust this for your 7 segment LED spec.
//                 A,  B, C, D, E,  F,  G
int Seg2Pin[8] = { 2, 3, 4, 5, 6, 7, 8};

//Digit to Segment You don't need modify this unless you want to change digits.
int Dec2Seg[11] =
{
//A(125), B(64), C(32), D(16), E(8), F(4), G(2)
  0x07E, //Digit 0
  0x00C, //Digit 1
  0x0B6, //Digit 2
  0x09E, //Digit 3
  0x0CC, //Digit 4
  0x0DA, //Digit 5
  0x0FB, //Digit 6
  0x00E, //Digit 7
  0x0FF, //Digit 8
  0x0DF, //Digit 9
 };

//==================================================================
//Global Functions and Descriptions
//==================================================================

void clearAllSegments()               //Turns off all LED segments
{
  for (int i = 0; i < 8; i++)  
  {
    digitalWrite(ledPin[i], HIGH);
  }
}

//-----------------------------------------------------------------

void drawDigit(int digit)            //Draw digit to LED outputs
{
  int seg = Dec2Seg[digit];
  int pin = 0;

  while (seg > 0)
  {
    seg /= 2;
    if (seg & 0x1)
      digitalWrite(Seg2Pin[pin], LOW);   // sets the LED on
    pin++;
  }
}

//-----------------------------------------------------------------

void display_time () {          // Function to display the time to console
//Serial.print(hours);
//Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.println(seconds);
}

//----------------------------------------------------------------

here is part 2, beginning with the end of part 1

//-----------------------------------------------------------------

void display_time () {          // Function to display the time to console
//Serial.print(hours);
//Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.println(seconds);
}

//----------------------------------------------------------------

void increment_time() {             //Second counting timer function
    seconds++;                      // We're counting UP
    if (seconds > 59) {                  // If seconds have rolled over
        seconds = 0;                  //    Reset seconds
        minutes++;                  //    and increment the minutes
        if (minutes > 1439) {            // Minutes in a Day have rolled over
            minutes = 0;            //    Reset minutes
                boolean CycleOver = false;    // Reset for saved cycle count        
}
}
   
    tick++;                             // indicate that the time has been updated
}

//----------------------------------------------------------------

void SetTime(){
UpbuttonState1 = digitalRead(UpButtonPin); // read the state of the pushbutton value:
if (UpbuttonState1 == LOW){
  delay(debounce); //debounce
  UpbuttonState2 = digitalRead(UpButtonPin); // check if the pushbutton is pressed, if it is, the buttonState is LOW:
    if (UpbuttonState2 ==LOW ) {     
      if (Rcount <= 8)    //MAX Time is 9
        {Rcount++;}
      else {Rcount = 1;}  //MIN Time is 1
      }
  else {
      ;  }
    } 
else {
    ; 
}
  
DnbuttonState1 = digitalRead(DnButtonPin);
if (DnbuttonState1 == LOW){
  delay(debounce);   //debounce
  DnbuttonState2 = digitalRead(DnButtonPin);  // check if the pushbutton is pressed, if it is, the buttonState is HIGH:
    if (DnbuttonState2== LOW) {     
      if (Rcount >= 2)  //MIN Time is 1
      {Rcount--;}
       else {Rcount = 9;}  //MAX Time is 9
    } 
  else {
      ;}
 }
 else {
      ;}

//Now Display count value on LED
  digitalWrite (LpwrPin, LOW); //for the display
  digitalWrite (RpwrPin, LOW); //for the display
   drawDigit(Rcount);
  digitalWrite (RpwrPin, HIGH); //for the display
   delay(1);                  // waits for a second
    clearAllSegments();
  digitalWrite (RpwrPin, LOW); //for the display
   delay(1);
  digitalWrite (LpwrPin, HIGH); //for the display
   drawDigit(Lcount);
   delay(1);                  // waits for a second
  digitalWrite (LpwrPin, LOW); //for the display
  clearAllSegments();
}

//=============================================================
// Setup Routine Information
//=============================================================

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600); // start up serial communications
  pinMode(FlowPin, INPUT);     // sets the digital pin as input
  pinMode(UpButtonPin, INPUT);
  pinMode(DnButtonPin, INPUT);  
  pinMode(K1Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K2Pin, OUTPUT);      // sets the digital pin as output
  pinMode(K3Pin, OUTPUT);      // sets the digital pin as output
  pinMode(FanPin, OUTPUT);      // sets the digital pin as output
  pinMode(RpwrPin, OUTPUT);
  pinMode(LpwrPin, OUTPUT);
  for (int i = 0; i < 8; i++)
    pinMode(ledPin[i], OUTPUT);      // sets the digital pin as output
  digitalWrite(UpButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(DnButtonPin, HIGH);  //Turn on the pull up resistors
  digitalWrite(FlowPin, HIGH);  //Turn on the pull up resistors
  display_time();                  // Show initial time
  Mode0();                            //Set relays OFF
  // Get ct from the EEPROM
  ctRead = EEPROM.read(0);
  delay(100);
  Rcount = ctRead; //**ERROR** MUST comment out the first time if a value has not ever been stored before!!!!
  MoRead = EEPROM.read(1);                //Read what Cell Power Mode to use
  delay(100);
  CyRead = EEPROM.read(3);              //Read how many cycles have been run before ?? eeprom.read(2) didn't work??
  delay(100);
  MsTimer2::set(1000, increment_time);// every second increment_time() is called
  MsTimer2::start();
}

//==================================================================
//Main Program LOOP
//==================================================================

void loop(){
 
  if (tick) {         // If a tick has occurred
      tick = 0;
        //Serial prints to the Serial Monitor Window on the cansole for Debugging
        //purposes. Program changes based on mihutes will occur with the next minute change,
        //it does not look at seconds, so 1m01s and 1m 59s are not greater then 1, but 2m is.
        Serial.println(""); // This prints to the console for debugging
        Serial.print("Time since power ON ");         //    reset indicator that we have a new time
      display_time();  //    and show it off!
        Serial.print("Saved Cell Mode is ");
        Serial.println(MoRead);
        Serial.print("Saved Cycle Count is ");
        Serial.println(CyRead);
        Serial.print("Run Time = ");
        Serial.println(hours);
        Serial.print("Flow Status = ");
        if (FlowState == 0) {Serial.println("GOOD FLOW");}
        if (FlowState == 1) {Serial.println("BAD FLOW");}
        Serial.print("Cell Power Mode is ");
        if (Mode == 0) {Serial.println("WAITING");}
        if (Mode == 1) {Serial.println("FORWARD"); // Make Chlorine, show Run Time remaining
          digitalWrite (RpwrPin, HIGH); //Turn ON the display ** change 60 to 6 below to speed up**
          hours=(Rcount -((minutes - (StartDelay)) / 60));  //Calculate how many Run Time Hours are left 
          drawDigit(hours);  //Show remaining Run Time Hours
        }
        if (Mode == 2) {Serial.println("REVERSE");
        hours=(Rcount -((minutes - (StartDelay)) / 60)); //Calculate how many Run Time Hours are left
        drawDigit(hours);  //Show remaining Run Time Hours  ** change 60 to 6 above to speed up**
        }
        
        if (Mode == 3) {Serial.println("DONE");
        digitalWrite (RpwrPin, LOW); //Turn Off the display
        }
  }
// While there is GOOD FLOW run the program
FlowState = digitalRead(FlowPin); //Check for GOOD FLOW
if (FlowState == 0){
//If minutes < delay, then check for button input to change run time 
if (minutes < (StartDelay)) {
      Mode = 0;
      SetTime();
   }
                                                        //**change 60 to 6 below to speed up**
if (minutes == (StartDelay)) {CycleTime = Rcount * 60; // Start-up delay is over, Now use Input Count for Cycle Time
      if (Rcount != ctRead)                   //Was Count for Hours of Operation changed?
      {EEPROM.write(0, int(Rcount));      // Then Write NEW count to the EEPROM
       delay(100);}
}

if (minutes > (StartDelay) && minutes < ((CycleTime) + (StartDelay))) {            
               
                if (MoRead == 1) {Mode1();
              }
                else {Mode2();
              }
  }            //Generate Chlorine in Last Saved Mode for this cycle

if (minutes >= ((CycleTime) + (StartDelay)))    //Generation cycle is over
 {
   Mode0();
   Mode = 3;    //Cycle completed, go back to waiting
   //One time limiter goes here
   while (CycleOver == false)   // Use to add  to saved cycle count
      {
      CyRead=CyRead + 1;                    //And add 1 to Cycle Count read fromm EEPROM
      if (CyRead >= Cycles)    //Next Time self clean
          {
            if (MoRead ==2)
              {
            MoRead = 1;}
          else {MoRead = 2;
                }
           EEPROM.write(1, (MoRead));    //Write the Mode to use next time to memory
           delay(100);
           CyRead = zero;
           EEPROM.write(3, (zero));    //Set the cycles run since self cleaning to 0  
           delay(100);
           }
     else {EEPROM.write(3, (CyRead));  // Write the updated cycle count to memory
     delay(100); 
          }
 CycleOver = true;    // Used to only add once
 }
 }
}
else {
Mode0();
Mode = 0;                         //BAD FLOW sit Idle WAITING
digitalWrite (RpwrPin, LOW);     // And Turn Off the LED display
}
}
//===========================================================
// Cell Relay Power Control Functions to Generate Chlorine
//===========================================================

 void Mode0() {                     // CELL is IDLED - No Power
  digitalWrite(K1Pin, HIGH);        // Connects +5V & +24V power to K2 & K3
  digitalWrite(K2Pin, LOW);         // CELL IDLE - Connects Cell (+) to +24V
  digitalWrite(K3Pin, LOW);         // CELL IDLE - Connects Cell (-) to +24V
  //when done turn the fan off
  digitalWrite(FanPin, LOW);        // sets the FAN off, after cooling the rectifier
 }
//______________________________________________________________
//         CHLORINE GENERATION POWER MODES
//______________________________________________________________
void Mode1() {                     // FORWARD power to cell
  Mode = 1;                        // Apply Positve polarity power to cell
 digitalWrite(K3Pin, HIGH);        // NORMAL POLARITY - Connects Cell (-) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier  
}

 void Mode2() {                    //REVERSE polarity for SELF CLEANING 
  Mode = 2;                        //Apply REVERSE Polarity power to cell
  digitalWrite(K2Pin, HIGH);        // REVERSE POLARITY - Connects Cell (+) to GROUND
 //Turn the fan ON to keep things cool
  digitalWrite(FanPin, HIGH);       // sets the FAN on to cool the rectifier
  }

I changed my code slightly to indicate whether power is being applied in FORWARD or REVERSE mode by which 7 segment LED is lit. Here is void loop code snippet -

if (Mode == 1) {Serial.println("FORWARD"); // Make Chlorine, show Run Time remaining
digitalWrite (LpwrPin, LOW); //Turn Off the left digit display
digitalWrite (RpwrPin, HIGH); //Turn ON the right digit display
hours=(Rcount -((minutes - (StartDelay)) / 60)); //Calculate how many Run Time Hours are left
drawDigit(hours); //Show remaining Run Time Hours
}
if (Mode == 2) {Serial.println("REVERSE");
hours=(Rcount -((minutes - (StartDelay)) / 60)); //Calculate how many Run Time Hours are left
digitalWrite (RpwrPin, LOW); //Turn Off the right digit display
digitalWrite (LpwrPin, HIGH); //Turn ON the left digit display
drawDigit(hours); //Show remaining Run Time Hours
}

if (Mode == 3) {Serial.println("DONE");
digitalWrite (RpwrPin, LOW); //Turn Off the right digit display
digitalWrite (LpwrPin, LOW); //Turn Off the left digit display
}
}