Last active
April 16, 2025 14:31
-
-
Save BlvckBytes/1dd17f3b229ed16ba8876507a3a93559 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Author: BlvckBytes <blvckbytes@gmail.com> | |
| Created On: 05/18/2022 | |
| This small state machine manages controlling a pump's on/off cycles based on the parameters | |
| the macros provide. The sensor is in essence a constant current source, which will operate at | |
| a range of voltages (mine goes from around 8 to 32 volts) and will always drive the same current | |
| through it's in-series load, which is linear to the pressure measured. In this case: 4mA = 0 bar | |
| and 20mA = 10 bar. | |
| When the pressure sinks below BAR_LO, the pump will activate, until the pressure stayed above BAR_HI | |
| for STABLE_HI_S seconds of time. | |
| Update: 04/16/2025 | |
| As to not have to crawl down into the well when the system's to be drained at its lowest point for | |
| the pipes to not freeze up in winter, I installed a solenoid valve, which is now also controlled | |
| by this little program: via a state-selection pushbutton. | |
| */ | |
| #include <Arduino.h> | |
| #include <EEPROM.h> | |
| // #define DEBUG | |
| // I/O | |
| #define PUMP_PIN 15 | |
| #define SENSOR_PIN A0 | |
| #define DRAIN_SOLENOID_PIN 5 | |
| #define PUSHBUTTON_PIN 4 | |
| // ADC parameters | |
| #define SENSOR_SHUNT_R 161 | |
| #define ADC_MAX_VALUE 1023 | |
| #define ADC_VOLTAGE 3.3 | |
| // Sensor specific parameters | |
| #define SENSOR_ZERO_A 0.004 | |
| #define SENSOR_MAX_A 0.020 | |
| #define SENSOR_ZERO_BAR 0 | |
| #define SENSOR_MAX_BAR 10 | |
| #define SENSOR_PRECISION 1000 | |
| // Calculated sensor parameters | |
| #define SENSOR_ZERO_V (SENSOR_SHUNT_R * SENSOR_ZERO_A) | |
| #define SENSOR_ZERO_ADC (SENSOR_ZERO_V / ADC_VOLTAGE * ADC_MAX_VALUE) | |
| #define SENSOR_MAX_V (SENSOR_SHUNT_R * SENSOR_MAX_A) | |
| #define SENSOR_MAX_ADC (SENSOR_MAX_V / ADC_VOLTAGE * ADC_MAX_VALUE) | |
| // State machine parameters | |
| #define BAR_LO 1.20 | |
| #define BAR_HI 4.75 | |
| #define STABLE_HI_S 2 | |
| // Push button mode states | |
| #define MODE_PUMP_ON 0 | |
| #define MODE_DRAIN_ON 1 | |
| #define MODE_ALL_OFF 2 | |
| #define MODE_TOTAL_COUNT 3 | |
| #define PUSHBUTTON_DEBOUNCE_MS 1000 | |
| // State variables | |
| static uint32_t last_poll; | |
| static long hi_int_begin; | |
| static bool pump_state; | |
| static uint8_t current_mode; | |
| static uint32_t last_pushbutton_press; | |
| void load_current_mode() | |
| { | |
| EEPROM.begin(1); | |
| current_mode = EEPROM.read(0); | |
| if (current_mode >= MODE_TOTAL_COUNT) | |
| current_mode = 0; | |
| EEPROM.end(); | |
| #ifdef DEBUG | |
| Serial.printf("Loaded mode %" PRIu8 "\n", current_mode); | |
| #endif | |
| } | |
| void store_current_mode() | |
| { | |
| EEPROM.begin(1); | |
| EEPROM.write(0, current_mode); | |
| EEPROM.commit(); | |
| EEPROM.end(); | |
| #ifdef DEBUG | |
| Serial.printf("Stored mode %" PRIu8 "\n", current_mode); | |
| #endif | |
| } | |
| void setup() | |
| { | |
| pinMode(SENSOR_PIN, INPUT); | |
| pinMode(PUMP_PIN, OUTPUT); | |
| digitalWrite(PUMP_PIN, LOW); | |
| pinMode(PUSHBUTTON_PIN, INPUT_PULLUP); | |
| pinMode(DRAIN_SOLENOID_PIN, OUTPUT); | |
| digitalWrite(DRAIN_SOLENOID_PIN, LOW); | |
| load_current_mode(); | |
| #ifdef DEBUG | |
| Serial.begin(115200); | |
| #endif | |
| } | |
| void pump_control_loop(uint32_t current_millis) | |
| { | |
| // Keep the output in sync with the local state | |
| digitalWrite(PUMP_PIN, pump_state); | |
| int raw_read = analogRead(SENSOR_PIN); | |
| // Read the ADC value (in bars) and scale it by the precision value | |
| long adc_bar_scaled = map( | |
| raw_read, | |
| SENSOR_ZERO_ADC, SENSOR_MAX_ADC, | |
| SENSOR_ZERO_BAR * SENSOR_PRECISION, SENSOR_MAX_BAR * SENSOR_PRECISION | |
| ); | |
| // Constrain towards zero | |
| adc_bar_scaled = adc_bar_scaled < 0 ? 0 : adc_bar_scaled; | |
| // Undo scaling to receive the proper floating point value | |
| float adc_bar = adc_bar_scaled / (float) SENSOR_PRECISION; | |
| // HI level reached or exceeded and the pump is actually on | |
| if (adc_bar >= BAR_HI) | |
| { | |
| if (pump_state) | |
| { | |
| // Interval not active, begin | |
| if (hi_int_begin == 0) | |
| hi_int_begin = current_millis; | |
| // Interval reached stable duration | |
| if (current_millis - hi_int_begin >= STABLE_HI_S * 1000) | |
| { | |
| // Cancel the interval and turn off the pump | |
| hi_int_begin = 0; | |
| pump_state = false; | |
| #ifdef DEBUG | |
| Serial.println("Turning pump OFF"); | |
| #endif | |
| } | |
| } | |
| } | |
| // Cancel the interval again when falling below HI | |
| else | |
| hi_int_begin = 0; | |
| // LO level reached or subceeded | |
| if (adc_bar <= BAR_LO) | |
| { | |
| hi_int_begin = 0; | |
| if (!pump_state) { | |
| pump_state = true; | |
| #ifdef DEBUG | |
| Serial.println("Turning pump ON"); | |
| #endif | |
| } | |
| } | |
| #ifdef DEBUG | |
| Serial.print("bar="); | |
| Serial.print(adc_bar); | |
| Serial.print("/"); | |
| Serial.print(SENSOR_MAX_BAR); | |
| Serial.print(", raw="); | |
| Serial.print(""); | |
| Serial.println(raw_read); | |
| #endif | |
| last_poll = current_millis; | |
| } | |
| void loop() | |
| { | |
| uint32_t current_millis = millis(); | |
| if (digitalRead(PUSHBUTTON_PIN) == LOW) | |
| { | |
| if (current_millis - last_pushbutton_press >= PUSHBUTTON_DEBOUNCE_MS) | |
| { | |
| if (++current_mode >= MODE_TOTAL_COUNT) | |
| current_mode = 0; | |
| last_pushbutton_press = current_millis; | |
| store_current_mode(); | |
| } | |
| } | |
| if (current_mode == MODE_DRAIN_ON) | |
| { | |
| digitalWrite(PUMP_PIN, LOW); | |
| digitalWrite(DRAIN_SOLENOID_PIN, HIGH); | |
| return; | |
| } | |
| if (current_mode == MODE_PUMP_ON) | |
| { | |
| digitalWrite(DRAIN_SOLENOID_PIN, LOW); | |
| pump_control_loop(current_millis); | |
| return; | |
| } | |
| // All off | |
| digitalWrite(PUMP_PIN, LOW); | |
| digitalWrite(DRAIN_SOLENOID_PIN, LOW); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment