I'm running into a very frustrating issue. My project is set up like this. I have four buttons connected to the Arduino responsible for 4 different functions. One button runs a pump for a certain duration by activating a relay and closing a circuit which the pump then detects (the relays should be optocoupled, the model is JQC-3FF-S-Z), one triggers and interrupt service routine to stop all active functions, and the other two each respectively open a valve for a certain amount of time by activating a relay which in turn closes the circuit on a separate circuit, opening the normally closed valves.
I have two power supplies (both 12V 5A) -- one should eventually correctly power the relays (via the 5V pin) and the Arduino (via the Vin and GND pins), while the other separately supplies the circuit with the two valves since they have relatively demanding current draws -- about 1A each. The relays are all 5V relays with 3 input pins, one for Vcc, one for GND, and another for a digital signal to turn them on/off.
Whenever I debug this on my computer, I have both supplies connected, as well as USB power so that I can get a serial readout. In this state, everything works perfectly -- the valves stay open as long as they should, there's no noise/false ISR triggers, everything is great.
As soon as I disconnect the USB and try to let the Arduino run off of the Vin and GND I've fed it from the power supply, it just starts doing its own random thing. For example, one of the functions programmed is for the pump to run, and both valves to be open all at the same time, for 60 seconds. When its plugged into the computer, it does this perfectly. When not plugged into the computer, it will run for 60 seconds the first time, and then on subsequent runs it will run 30s, 20s, 10s, or whatever other random time duration it feels like.
A couple of things I've read here have been to try common ground (this did nothing but cause other issues) as well as to try to smooth out the power input via capacitors which I have now done since it is worth having them there anyways, but that has also been to no avail, unless I wired them wrong. I also tried grounding the whole thing to earth by connecting a ground wire from ground to a large metal desk (I have absolutely no clue if this was the right way to approach that) but that also didn't help. What can I do? Any help would be greatly appreciated. The code and circuit diagram are below.
Please note I omitted the LCD in the circuit diagram, and I denoted the valves and pump "running" as LEDs in the diagram.
////////////////////////// LIBRARIES /////////////////////////////
#include <avr/interrupt.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
////////////////////////// TIME VARIABLES (in ms) /////////////////////////////
unsigned long LSTime = 60000;
unsigned long HSTime = 110000;
unsigned long LSprimeTime = 60000;
unsigned long HSprimeTime = 10000;
unsigned long washTime = 60000;
unsigned long timeBeforePause = 0;
unsigned long timeAfterPause = 0;
unsigned long pauseDuration = 0;
unsigned long startTime = 0;
unsigned long endTime = 0;
unsigned long lastPressTime = 0;
////////////////////////// I2C DEVICES /////////////////////////////
LiquidCrystal_I2C lcd(0x27,16,2);
////////////////// Button Pin Declarations ////////////////////////
int pumpCtrlPin = 6;
int fillBtn = 12;
int stopBtn = 3;
int primeBtn = 7;
int washBtn = 5;
int valveHeavy = 10;
int valveLight = 9;
int appropriateBtn;
////////////////////////// STATE DECLARATIONS & FLAGS /////////////////////////////
enum {idle, LSInit, LSRun, LSResume, HSInit, HSRun, HSResume, LSprimeInit, LSprimeRun, LSprimeResume, HSprimeInit, HSprimeRun, HSprimeResume, washInit, washRun, washResume, paused, stopDelay};
int resumeState;
int stopped = false;
int programState = idle; //What the program is doing at any moment
void setup() {
//////////////////////////////// DEVICE INITIALIZATION //////////////////////////////////
Serial.begin(9600);
while (!Serial) { delay(1); } // Wait until serial port is opened
lcd.begin();
lcd.backlight();
lcd.clear();
lcd.setCursor(0,0);
//////////////////////////////// BUTTON INITIALIZATION //////////////////////////////////
pinMode(fillBtn, INPUT_PULLUP);
pinMode(stopBtn, INPUT_PULLUP);
pinMode(primeBtn, INPUT_PULLUP);
pinMode(washBtn, INPUT_PULLUP);
pinMode(pumpCtrlPin, OUTPUT);
pinMode(valveHeavy, OUTPUT);
pinMode(valveLight, OUTPUT);
digitalWrite(valveHeavy, LOW);
digitalWrite(valveLight, LOW);
attachInterrupt(digitalPinToInterrupt(stopBtn), stopISR, RISING);
//////////////////////////////// ISR FLAG SET //////////////////////////////////
stopped = false;
lcd.clear();
lcd.print("Idle");
}
void loop() {
switch(programState) {
case idle:
{
if (digitalRead(fillBtn) == LOW) {
lastPressTime = millis();
programState = LSInit;
stopped = false;
break;
}
if (digitalRead(primeBtn) == LOW) {
lastPressTime = millis();
programState = HSprimeInit;
stopped = false;
break;
}
if (digitalRead(washBtn) == LOW) {
lastPressTime = millis();
programState = washInit;
stopped = false;
break;
}
break;
}
case LSInit:
{
lcd.clear();
lcd.print("Dispensing Light");
lcd.setCursor(0,1);
lcd.print("Sucrose");
timeBeforePause = 0;
timeAfterPause = 0;
pauseDuration = 0;
startTime = millis();
endTime = startTime;
programState = LSRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case LSRun:
{
if ((endTime - startTime) <= LSTime) {
digitalWrite(pumpCtrlPin, HIGH);
digitalWrite(valveLight, HIGH);
if (((millis() - lastPressTime) >= 1000) && (digitalRead(fillBtn) == LOW)) {
Serial.println("pausedSomehow");
appropriateBtn = fillBtn;
resumeState = LSResume;
lastPressTime = millis();
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
timeBeforePause = millis();
lcd.clear();
lcd.print("Paused");
programState = paused;
break;
}
} else {
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
programState = HSInit;
lcd.clear();
lcd.print("Idle");
if (stopped == true) {
programState = stopDelay;
}
break;
}
endTime = millis() - pauseDuration;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case LSResume:
{
lcd.clear();
lcd.print("Dispensing Light");
lcd.setCursor(0,1);
lcd.print("Sucrose");
timeAfterPause = millis();
pauseDuration = pauseDuration + (timeAfterPause - timeBeforePause);
endTime = millis() - pauseDuration;
programState = LSRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case HSInit:
{
lcd.clear();
lcd.print("Dispensing Heavy");
lcd.setCursor(0,1);
lcd.print("Sucrose");
timeBeforePause = 0;
timeAfterPause = 0;
pauseDuration = 0;
startTime = millis();
endTime = startTime;
programState = HSRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case HSRun:
{
if ((endTime - startTime) <= HSTime) {
digitalWrite(pumpCtrlPin, HIGH);
digitalWrite(valveHeavy, HIGH);
if (((millis() - lastPressTime) >= 1000) && (digitalRead(fillBtn) == LOW)) {
Serial.println("pausedSomehow");
appropriateBtn = fillBtn;
resumeState = HSResume;
lastPressTime = millis();
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveHeavy, LOW);
timeBeforePause = millis();
lcd.clear();
lcd.print("Paused");
programState = paused;
break;
}
} else {
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveHeavy, LOW);
programState = idle;
lcd.clear();
lcd.print("Idle");
if (stopped == true) {
programState = stopDelay;
}
break;
}
endTime = millis() - pauseDuration;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case HSResume:
{
lcd.clear();
lcd.print("Dispensing Heavy");
lcd.setCursor(0,1);
lcd.print("Sucrose");
timeAfterPause = millis();
pauseDuration = pauseDuration + (timeAfterPause - timeBeforePause);
endTime = millis() - pauseDuration;
programState = HSRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
/////////////////////////////////////// PRIMING ///////////////////////////////////
case HSprimeInit:
{
lcd.clear();
lcd.print("Priming...");
timeBeforePause = 0;
timeAfterPause = 0;
pauseDuration = 0;
startTime = millis();
endTime = startTime;
programState = HSprimeRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case HSprimeRun:
{
if ((endTime - startTime) <= HSprimeTime) {
digitalWrite(pumpCtrlPin, HIGH);
digitalWrite(valveHeavy, HIGH);
if (((millis() - lastPressTime) >= 1000) && (digitalRead(primeBtn) == LOW)) {
Serial.println("pausedSomehow");
appropriateBtn = primeBtn;
resumeState = HSprimeResume;
lastPressTime = millis();
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveHeavy, LOW);
timeBeforePause = millis();
lcd.clear();
lcd.print("Paused");
programState = paused;
break;
}
} else {
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveHeavy, LOW);
programState = LSprimeInit;
lcd.clear();
lcd.print("Idle");
if (stopped == true) {
programState = stopDelay;
}
break;
}
endTime = millis() - pauseDuration;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case HSprimeResume:
{
lcd.clear();
lcd.print("Priming...");
timeAfterPause = millis();
pauseDuration = pauseDuration + (timeAfterPause - timeBeforePause);
endTime = millis() - pauseDuration;
programState = HSprimeRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case LSprimeInit:
{
lcd.clear();
lcd.print("Priming...");
timeBeforePause = 0;
timeAfterPause = 0;
pauseDuration = 0;
startTime = millis();
endTime = startTime;
programState = LSprimeRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case LSprimeRun:
{
if ((endTime - startTime) <= LSprimeTime) {
digitalWrite(pumpCtrlPin, HIGH);
digitalWrite(valveLight, HIGH);
if (((millis() - lastPressTime) >= 1000) && (digitalRead(primeBtn) == LOW)) {
Serial.println("pausedSomehow");
appropriateBtn = primeBtn;
resumeState = LSprimeResume;
lastPressTime = millis();
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
timeBeforePause = millis();
lcd.clear();
lcd.print("Paused");
programState = paused;
break;
}
} else {
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
programState = idle;
lcd.clear();
lcd.print("Idle");
if (stopped == true) {
programState = stopDelay;
}
break;
}
endTime = millis() - pauseDuration;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case LSprimeResume:
{
lcd.clear();
lcd.print("Priming...");
timeAfterPause = millis();
pauseDuration = pauseDuration + (timeAfterPause - timeBeforePause);
endTime = millis() - pauseDuration;
programState = LSprimeRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
/////////////////////////////// WASHING ///////////////////////////////////
case washInit:
{
lcd.clear();
lcd.print("Washing...");
timeBeforePause = 0;
timeAfterPause = 0;
pauseDuration = 0;
startTime = millis();
endTime = startTime;
programState = washRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case washRun:
{
if ((endTime - startTime) <= washTime) {
Serial.println(endTime-startTime);
digitalWrite(pumpCtrlPin, HIGH);
digitalWrite(valveLight, HIGH);
digitalWrite(valveHeavy, HIGH);
if (((millis() - lastPressTime) >= 1000) && (digitalRead(washBtn) == LOW)) {
Serial.println("pausedSomehow");
appropriateBtn = washBtn;
resumeState = washResume;
lastPressTime = millis();
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
digitalWrite(valveHeavy, LOW);
timeBeforePause = millis();
lcd.clear();
lcd.print("Paused");
programState = paused;
break;
}
} else {
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
digitalWrite(valveHeavy, LOW);
programState = idle;
lcd.clear();
lcd.print("Idle");
if (stopped == true) {
programState = stopDelay;
}
break;
}
endTime = millis() - pauseDuration;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case washResume:
{
lcd.clear();
lcd.print("Washing...");
timeAfterPause = millis();
pauseDuration = pauseDuration + (timeAfterPause - timeBeforePause);
endTime = millis() - pauseDuration;
programState = washRun;
if (stopped == true) {
programState = stopDelay;
}
break;
}
case paused:
{
if (((millis() - lastPressTime) >= 1000) && (digitalRead(appropriateBtn) == LOW)) {
lastPressTime = millis();
programState = resumeState;
break;
}
if (stopped == true) {
programState = stopDelay;
}
break;
}
case stopDelay:
{
digitalWrite(pumpCtrlPin, LOW);
digitalWrite(valveLight, LOW);
digitalWrite(valveHeavy, LOW);
lcd.clear();
lcd.print("Stopping...");
delay(1500);
stopped = false,
programState = idle;
lcd.clear();
lcd.print("Idle");
break;
}
}
}
void stopISR() {
stopped = true;
}