August 2021

Feather Boat Head Flush Electromagnetic Valve


Background

Our boat Knot Workin has a 40 gal holding tank.   When the seas are rough, this stirs up the holding tank contents and the gasses choose to exhaust out of the head (toilet), rather than the vent.   The proposed solution is to install a normally closed electric ball valve between the toilet and holding tank that will remain closed unless someone flushes the toilet.   The electronic ball valve from www.electricsolenoidvalves.com requires 60 seconds of 12 VDC power after an opening event in order to charge a capacitor so that it can automatically close when power is removed.  

Functional Requirements

  • Detect a head (toilet) flush event (12 VDC signal) and then signal the electronic ball valve to open.  
  • When the electronic ball valve is powered to be open, keep it powered for at least 90 sec before removing power to close it.   This should provide sufficient time for a flush event, and charging of the electronic ball valve.  
  • Include a fault switch that will power the electronic ball valve continuously in case of a failure of the system (so the head may be flushed).  

Electronic Ball Valve

The electronic ball valve chosen is a 1-1/2" stainless steel electric ball valve from ElectricSolenoidValves.com.   This model is the 'two wire auto return' version, which means that when power is disconnected, the ball valve automatically closes.   The device needs 60 seconds of power in order to achieve the automatic closure.  

Flush Detection

The toilet has an electronic flush that triggers the flow of water to the toilet bowel, and activation of a macerator.   The flush event will be detected by using the DC Voltage Event Detection FeatherWing.   This high impedance voltage sensor has no impact on the electronic flush.   A 12 AWG wire from the flush button is routed back to the device.   When the toilet is flushed by pressing the flush button, the DC Voltage Event Detection FeatherWing will detect the presence of the 12 VDC signal and the digital input pin monitored by the microcontroller will change state from HIGH to LOW.  

Electronic Ball Valve Activation

The electronic ball valve is connected to 12 VDC by a Adafruit Latching Mini Relay FeatherWing.   The latching relay FeatherWing requires that one microcontroller digital output pin is set HIGH for 10 ms to latch the relay, and another set HIGH for 10 ms to unlatch the relay.   If power is lost, the relay will remain in the same state.  


/*
  Adafruit Feather
  
  Knot Workin - head electromagnetic ball valve.fzz

  v0.0.0
  
 */

// In Arduino IDE, select 'Tools','Board','Adafruit Boards','Adafruit Feather 32u4'
// Find USB port in Windows by looking at ...

/////////////////////////////////////////////////////////////////////////
const byte pinBuiltInLED = 13;  

/////////////////////////////////////////////////////////////////////////
// blinkLEDnoDelay()
unsigned long LEDblinkPeriod = 8;
unsigned long LEDblinkLast = 0;
byte LEDblinkPWM = 0;
bool LEDblinkState = false;
byte LEDlastMode = 0;

void blinkLEDnoDelay(byte pin, byte mode) {
  // Blink the LED on 'pin' without using delay() according to
  // the 'mode' argument defined below. 
  // pin must support PWM. 
  // 
  // mode:
  //  0 = breathing
  //  1 = blink slow constantly
  //  2 = blink fast constantly
  //  3 = slow burst every 1 second
  //  4 = fast burst every 1 second
  //
  // Required global variables: LEDblinkPeriod, LEDblinkLast, LEDblinkPWM, LEDblinkState, LEDlastMode
  if (mode == 0) {
    // breathing
    LEDblinkPeriod = 8;
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      digitalWrite(pin, LOW);
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM > 254) LEDblinkState = false;
        if (LEDblinkPWM < 1) LEDblinkState = true;
        if (LEDblinkState) {
            LEDblinkPWM++;
        } else {
            LEDblinkPWM--;
        }
        analogWrite(pin, LEDblinkPWM);
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 1) {
    // blink slow constantly
    LEDblinkPeriod = 1000;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 2) {
    // blink fast constantly
    LEDblinkPeriod = 100;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 3) {
    // slow burst every 1 second
    // Slow 4 blinks (lazy burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 100;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 100;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 4) {
    // fast burst every 1 second
    // Fast 4 blinks (burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 25;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 25;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } // mode
}   // blinkLEDnoDelay()


void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} //blinkLED()


void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


/////////////////////////////////////////////////////////////////////////
//  Adafruit Latching Mini Relay FeatherWing

byte pinExtLED = A6;

byte pinRelaySet = A1;
byte pinRelayUnset = A2;
byte RelaySignalMs = 20;
bool FlushEvent = false;
bool BallValveIsOpen = false;

// Timer for opening the electronic ball valve for 3 minutes (180000 ms)
const unsigned long timerIntervalBallValveOpen = 180000;  
unsigned long timerBallValveOpenlap = millis();  


void OpenBallValve() {
  Serial.println("OpenBallValve()");
  digitalWrite(pinBuiltInLED, HIGH);
  digitalWrite(pinExtLED, HIGH);
  // Latch the relay
  digitalWrite(pinRelaySet, HIGH);
  delay(RelaySignalMs);
  digitalWrite(pinRelaySet, LOW);  
  timerBallValveOpenlap = millis(); // reset the timer
  BallValveIsOpen = true;
} // OpenBallValve()


void CloseBallValve() {
  Serial.println("CloseBallValve()");
  // Unlatch the relay
  digitalWrite(pinRelayUnset, HIGH);
  delay(RelaySignalMs);
  digitalWrite(pinRelayUnset, LOW);
  digitalWrite(pinBuiltInLED, LOW);
  digitalWrite(pinExtLED, LOW);
  BallValveIsOpen = false;
} // CloseBallValve()


void timerBallValveOpen() {
  //  Timer for opening the electronic ball valve for 3 minutes
  if (timerBallValveOpenlap > millis())  timerBallValveOpenlap = millis();
  if (millis() - timerBallValveOpenlap > timerIntervalBallValveOpen) { 
    // Serial.println("timerBallValveOpen()");
    CloseBallValve();
    FlushEvent = false;
    timerBallValveOpenlap = millis(); // reset the timer
  }
} // timerBallValveOpen()


/////////////////////////////////////////////////////////////////////////
// DC Voltage Event Detection FeatherWing

const byte pinOptocoupler = 6; // #6 / A7
byte optocouplerState = HIGH;         
byte lastOptocouplerState = HIGH;

void DCinputDetect() {
  // read the state of the digital input:
  optocouplerState = digitalRead(pinOptocoupler);
  //  detect change in the button state
  if (optocouplerState != lastOptocouplerState) {
    if (optocouplerState == LOW) {
        //Serial.println("VDC @ optocoupler");
        //digitalWrite(pinBuiltInLED, HIGH);
        FlushEvent = true;
        timerBallValveOpenlap = millis(); // reset the timer
    } else {
        //Serial.println("NO VDC @ optocoupler");
        //Serial.println(" ");
        //digitalWrite(pinBuiltInLED, LOW);
    }
    lastOptocouplerState = optocouplerState;
  }  
} // DCinputDetect()



/////////////////////////////////////////////////////////////////////////
// Override Pushbutton Detection

const byte pinOverridePushButton = A3;
byte OverridePushButtonState = LOW;         
byte lastOverridePushButtonState = LOW;

void OverridePushButton() {
  // read the state of the digital input:
  OverridePushButtonState = digitalRead(pinOverridePushButton);
  //  detect change in the button state
  if (OverridePushButtonState != lastOverridePushButtonState) {
    if (OverridePushButtonState == HIGH) {
        Serial.println("\nOverridePushButton ON");
        OpenBallValve();
    } else {
        Serial.println("\nOverridePushButton OFF");
        CloseBallValve();
    }
    lastOverridePushButtonState = OverridePushButtonState;
  }  
} // OverridePushButton()


/////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////


void setup() {

  Serial.begin(115200);
  while (!Serial) {
    delay(1);
  }
  Serial.println("\nSerial ready");

  pinMode(pinBuiltInLED, OUTPUT);
  digitalWrite(pinBuiltInLED, LOW);

  pinMode(pinExtLED, OUTPUT);
  digitalWrite(pinExtLED, LOW);
  
  // initialize the Override PushButton pin as an input:
  pinMode(pinOverridePushButton, INPUT); 
  delay(10);
  OverridePushButton();

  // initialize the pinOptocoupler pin as an input:
  pinMode(pinOptocoupler, INPUT); 

  pinMode(pinRelaySet, OUTPUT);
  delay(10);
  digitalWrite(pinRelaySet, LOW);
  pinMode(pinRelayUnset, OUTPUT);
  delay(10);
  digitalWrite(pinRelayUnset, LOW);
  
  // Open electronic ball valve for 90 sec (charge capacitor)
  OpenBallValve();
  delay(90000);
  CloseBallValve(); 

  Serial.println("Setup finished\n");
} // setup()


void loop() {

  OverridePushButton();
  //OverridePushButtonState = LOW;
  
  if (OverridePushButtonState == LOW) {
    DCinputDetect();   
    if (FlushEvent == true) {
      // Send power to the electronic ball valve & open it for 90 sec
      if (BallValveIsOpen == false) {
        OpenBallValve();
      }
      timerBallValveOpen();
    }
  } else {
    blinkLEDnoDelay(pinBuiltInLED, 4);
  }

} // loop()

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.