You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
9.8 KiB
501 lines
9.8 KiB
#include <Wire.h> |
|
#include <Adafruit_GFX.h> |
|
#include <Adafruit_SSD1306.h> |
|
#include <Adafruit_FeatherOLED.h> |
|
|
|
#include "hardware.h" |
|
|
|
#define SIMULATE 1 |
|
#define DEBUG 0 |
|
|
|
#define TIMEOUT_TIME 7000 |
|
#define BUTTON_HOLD_TIME 500 |
|
#define BUTTON_PRESS_TIME 20 |
|
|
|
#define UP_BUTTON 31 |
|
#define ENTER_BUTTON 30 |
|
#define DOWN_BUTTON 27 |
|
|
|
Adafruit_FeatherOLED oled = Adafruit_FeatherOLED(); |
|
|
|
//static const unsigned char PROGMEM arrow[] = |
|
//{ B10000000, |
|
// B11000000, |
|
// B11100000, |
|
// B11000000, |
|
// B10000000}; |
|
//oled.drawBitmap(0, 1, arrow, 8, 5, 1); |
|
|
|
|
|
enum buttonStates { |
|
OPEN, |
|
CLOSED, |
|
CHECK_PRESSED, |
|
PRESSED, |
|
HELD, |
|
NUM_BUTTONSTATES |
|
}; |
|
|
|
enum screenStates { |
|
BOOT_UP, |
|
PRESSURE, |
|
SET_POINT, |
|
INIT_RUN, |
|
BEGIN_RUN, |
|
MEASURING, |
|
RUNNING, |
|
SAY_DONE, |
|
SAY_CANCEL, |
|
SAY_HOLD, |
|
SAY_TIMEOUT, |
|
SETTINGS, |
|
}; |
|
|
|
static const char* stateLabels[] = { |
|
"BOOT_UP", |
|
"PRESSURE", |
|
"SET_POINT", |
|
"INIT_RUN", |
|
"BEGIN_RUN", |
|
"MEASURING", |
|
"RUNNING", |
|
"SAY_DONE", |
|
"SAY_CANCEL", |
|
"SAY_HOLD", |
|
"SAY_TIMEOUT", |
|
"SETTINGS", |
|
}; |
|
|
|
|
|
enum buttonStates upButton = OPEN; |
|
enum buttonStates enterButton = OPEN; |
|
enum buttonStates downButton = OPEN; |
|
|
|
enum screenStates screenState = BOOT_UP; |
|
float pressureValue = 0.0; |
|
int pressureSetPoint = 0; |
|
//float initialPressure = 0.0; |
|
//float runningPressure = 0.0; |
|
float sampledPressure = 0.0; |
|
|
|
int debounceValue(float value) { |
|
static int prevValue = (int) value; |
|
|
|
if (abs(prevValue - value) > 0.4) { |
|
prevValue = (int) value; |
|
} |
|
|
|
return prevValue; |
|
} |
|
|
|
void setup() |
|
{ |
|
randomSeed(analogRead(A1)); |
|
|
|
Serial.begin(115200); |
|
|
|
oled.init(); |
|
|
|
pinMode(UP_BUTTON, INPUT_PULLUP); |
|
pinMode(ENTER_BUTTON, INPUT); // Has external pullup |
|
pinMode(DOWN_BUTTON, INPUT_PULLUP); |
|
|
|
pinMode(RELAY1_PIN, OUTPUT); |
|
pinMode(RELAY2_PIN, OUTPUT); |
|
digitalWrite(RELAY1_PIN, HIGH); |
|
digitalWrite(RELAY2_PIN, HIGH); |
|
|
|
#ifdef SIMULATE |
|
initSimulation(); |
|
#endif |
|
|
|
resamplePressure(pressureValue); |
|
} |
|
|
|
void loop() { |
|
measurePressure(pressureValue); |
|
pollButtons(); |
|
runUI(); |
|
logData(); |
|
|
|
#ifdef SIMULATE |
|
tickSimulation(); |
|
#endif |
|
} |
|
|
|
void logData() { |
|
static unsigned long lastLogTime = millis(); |
|
|
|
if (millis() > lastLogTime + 100) { |
|
Serial.print("pressure: "); |
|
Serial.print(pressureValue); |
|
#ifdef SIMULATE |
|
Serial.print(", simulated: "); |
|
Serial.print(simulatedPressure); |
|
#endif |
|
Serial.print(", sampled: "); |
|
Serial.print(sampledPressure); |
|
Serial.print(", setpoint: "); |
|
Serial.print(pressureSetPoint); |
|
Serial.print(", state: "); |
|
Serial.print(stateLabels[screenState]); |
|
Serial.println(""); |
|
|
|
lastLogTime = millis(); |
|
} |
|
} |
|
|
|
void runUI() { |
|
static enum screenStates nextState = BOOT_UP; |
|
|
|
static unsigned long timer = millis(); |
|
|
|
static unsigned long startTime = millis(); |
|
static bool isInflating = false; |
|
static bool isDeflating = false; |
|
|
|
int num_dots = 0; |
|
int debouncedPressureValue = debounceValue(pressureValue); |
|
|
|
oled.clearDisplay(); |
|
|
|
switch (screenState) { |
|
case BOOT_UP: |
|
if (millis() >= timer + 2000) { |
|
screenState = PRESSURE; |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(2); |
|
oled.println("STC TECH"); |
|
|
|
oled.setTextSize(1); |
|
oled.println(""); |
|
oled.print(" INITIALIZING"); |
|
|
|
oled.display(); |
|
break; |
|
|
|
case PRESSURE: |
|
if (enterButton == PRESSED) { |
|
screenState = SAY_HOLD; |
|
nextState = PRESSURE; |
|
timer = millis(); |
|
} else if (enterButton == HELD) { |
|
; // settings? |
|
} else if (upButton == PRESSED) { |
|
screenState = SET_POINT; |
|
pressureSetPoint = debouncedPressureValue+1; |
|
timer = millis(); |
|
} else if (downButton == PRESSED) { |
|
screenState = SET_POINT; |
|
pressureSetPoint = debouncedPressureValue-1; |
|
timer = millis(); |
|
} else if (upButton == HELD) { |
|
screenState = SET_POINT; |
|
pressureSetPoint = debouncedPressureValue+1; |
|
timer = millis(); |
|
} else if (downButton == HELD) { |
|
screenState = SET_POINT; |
|
pressureSetPoint = debouncedPressureValue-1; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print(debouncedPressureValue); |
|
oled.print(" PSI"); |
|
|
|
oled.display(); |
|
break; |
|
|
|
case SET_POINT: |
|
if (enterButton == PRESSED) { |
|
screenState = SAY_HOLD; |
|
nextState = SET_POINT; |
|
timer = millis(); |
|
} else if (enterButton == HELD) { |
|
timer = millis(); |
|
screenState = INIT_RUN; |
|
} else if (upButton == PRESSED) { |
|
timer = millis(); |
|
pressureSetPoint++; |
|
} else if (downButton == PRESSED) { |
|
timer = millis(); |
|
pressureSetPoint--; |
|
} else if (upButton == HELD) { |
|
timer = millis(); |
|
pressureSetPoint++; |
|
delay(75); |
|
} else if (downButton == HELD) { |
|
timer = millis(); |
|
pressureSetPoint--; |
|
delay(75); |
|
} else if (millis() >= timer + TIMEOUT_TIME) { |
|
screenState = SAY_TIMEOUT; |
|
nextState = PRESSURE; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print(pressureSetPoint); |
|
oled.println(" PSI"); |
|
oled.setTextSize(1); |
|
oled.println("TARGET"); |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
case INIT_RUN: |
|
//initialPressure = pressureValue; |
|
startTime = millis(); |
|
timer = millis(); |
|
screenState = BEGIN_RUN; |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(1); |
|
oled.println(""); |
|
oled.setTextSize(2); |
|
if (pressureSetPoint > debouncedPressureValue) { |
|
oled.print("INFLATING"); |
|
setSoleniod(SOLENOID_INFLATE); |
|
isInflating = true; |
|
isDeflating = false; |
|
} else if (pressureSetPoint < debouncedPressureValue) { |
|
oled.print("DEFLATING"); |
|
setSoleniod(SOLENOID_DEFLATE); |
|
isDeflating = true; |
|
isInflating = false; |
|
} else { |
|
setSoleniod(SOLENOID_STOP); |
|
screenState = SAY_DONE; |
|
nextState = PRESSURE; |
|
} |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
case BEGIN_RUN: |
|
if (millis() >= timer + 5000) { |
|
//runningPressure = pressureValue; |
|
setSoleniod(SOLENOID_STOP); |
|
screenState = MEASURING; |
|
timer = millis(); |
|
} |
|
|
|
break; |
|
|
|
case MEASURING: |
|
if (millis() >= timer + 3000) { |
|
sampledPressure = pressureValue; |
|
|
|
if (isInflating && (int) sampledPressure >= pressureSetPoint) { |
|
screenState = SAY_DONE; |
|
nextState = PRESSURE; |
|
} else if (isDeflating && (int) sampledPressure <= pressureSetPoint) { |
|
screenState = SAY_DONE; |
|
nextState = PRESSURE; |
|
} else { |
|
screenState = RUNNING; |
|
} |
|
|
|
timer = millis(); |
|
} |
|
|
|
if (millis() < timer + 500) { |
|
// wait for solenoids to settle before averaging |
|
resamplePressure(pressureValue); |
|
} |
|
|
|
setSoleniod(SOLENOID_STOP); |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(1); |
|
oled.println(""); |
|
oled.setTextSize(2); |
|
oled.print("MEASURING"); |
|
oled.display(); |
|
|
|
break; |
|
|
|
case RUNNING: |
|
if (enterButton == PRESSED) { |
|
screenState = SAY_CANCEL; |
|
nextState = PRESSURE; |
|
timer = millis(); |
|
} else if (upButton == PRESSED) { |
|
screenState = SAY_CANCEL; |
|
nextState = PRESSURE; |
|
timer = millis(); |
|
} else if (downButton == PRESSED) { |
|
screenState = SAY_CANCEL; |
|
nextState = PRESSURE; |
|
timer = millis(); |
|
} |
|
|
|
if (isInflating && millis() >= timer + 20000) { |
|
screenState = MEASURING; |
|
timer = millis(); |
|
} else if (isDeflating && millis() >= timer + 10000) { |
|
screenState = MEASURING; |
|
timer = millis(); |
|
} |
|
|
|
if (isInflating) { |
|
setSoleniod(SOLENOID_INFLATE); |
|
} else if (isDeflating) { |
|
setSoleniod(SOLENOID_DEFLATE); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print((int) sampledPressure); |
|
oled.println(" PSI"); |
|
|
|
oled.setTextSize(1); |
|
if (isInflating) { |
|
oled.print("INFLATING"); |
|
} else if (isDeflating) { |
|
oled.print("DEFLATING"); |
|
} |
|
num_dots = (int) ((millis() - startTime) / 400) % 4; |
|
for (int i = 0; i < num_dots; i++) { |
|
oled.print("."); |
|
} |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
|
|
|
|
case SAY_DONE: |
|
if (millis() >= timer + 3000) { |
|
screenState = nextState; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print("DONE"); |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
case SAY_CANCEL: |
|
if (millis() >= timer + 1000) { |
|
screenState = nextState; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print("CANCEL"); |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
case SAY_HOLD: |
|
if (millis() >= timer + 500) { |
|
screenState = nextState; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print("HOLD"); |
|
|
|
oled.display(); |
|
|
|
break; |
|
|
|
case SAY_TIMEOUT: |
|
if (millis() >= timer + 1000) { |
|
screenState = nextState; |
|
timer = millis(); |
|
} |
|
|
|
oled.setCursor(0,0); |
|
oled.setTextSize(3); |
|
oled.print("TIMEOUT"); |
|
|
|
oled.display(); |
|
|
|
break; |
|
} |
|
} |
|
|
|
void pollButtons() { |
|
static unsigned long upButtonTime = 0; |
|
static unsigned long enterButtonTime = 0; |
|
static unsigned long downButtonTime = 0; |
|
|
|
processButtonState(UP_BUTTON, upButton, upButtonTime); |
|
processButtonState(ENTER_BUTTON, enterButton, enterButtonTime); |
|
processButtonState(DOWN_BUTTON, downButton, downButtonTime); |
|
|
|
if (DEBUG) { |
|
if (upButton == PRESSED) { |
|
Serial.println("up button pressed"); |
|
} else if (upButton == HELD) { |
|
Serial.println("up button held"); |
|
} |
|
|
|
if (enterButton == PRESSED) { |
|
Serial.println("enter button pressed"); |
|
} else if (enterButton == HELD) { |
|
Serial.println("enter button held"); |
|
} |
|
|
|
if (downButton == PRESSED) { |
|
Serial.println("down button pressed"); |
|
} else if (downButton == HELD) { |
|
Serial.println("down button held"); |
|
} |
|
} |
|
} |
|
|
|
void processButtonState(int buttonPin, buttonStates &buttonState, unsigned long &buttonTime) { |
|
bool pinState = !digitalRead(buttonPin); |
|
|
|
switch(buttonState) { |
|
case OPEN: |
|
if (pinState) { |
|
buttonState = CLOSED; |
|
buttonTime = millis(); |
|
} |
|
break; |
|
case CLOSED: |
|
if (millis() >= buttonTime + BUTTON_HOLD_TIME) { |
|
buttonState = HELD; |
|
} |
|
if (pinState) { |
|
; |
|
} else { |
|
buttonState = CHECK_PRESSED; |
|
} |
|
break; |
|
case CHECK_PRESSED: |
|
if (millis() >= buttonTime + BUTTON_PRESS_TIME) { |
|
buttonState = PRESSED; |
|
} else { |
|
buttonState = OPEN; |
|
} |
|
break; |
|
case PRESSED: |
|
buttonState = OPEN; |
|
break; |
|
case HELD: |
|
if (!pinState) { |
|
buttonState = OPEN; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
}
|
|
|