#include <Arduino.h>
#include <Wire.h>
#include <EEPROM.h>
#include "Config.h"
#include "Units.h"
#include "HX711Manager.h"
#include "EncoderManager.h"
#include "DisplayManager.h"
#include "MenuManager.h"
#include "MenuText.h"
// Modules
HX711Manager hx;
EncoderManager enc;
DisplayManager disp;
MenuManager menu;
// Global state
float sgValue = DEFAULT_SG;
uint8_t sgPresetIndex = 0;
MassUnit massUnit = MASS_G;
VolUnit volUnit = VOL_ML;
float lastMass[4] = {0,0,0,0};
unsigned long lastChangeTime[4] = {0,0,0,0};
bool stable[4] = {false,false,false,false};
unsigned long lastFlash = 0;
bool flashState = false;
void loadEEPROM() {
massUnit = (MassUnit)EEPROM.read(EEPROM_ADDR_MASS_UNIT);
volUnit = (VolUnit)EEPROM.read(EEPROM_ADDR_VOL_UNIT);
EEPROM.get(EEPROM_ADDR_SG_VALUE, sgValue);
sgPresetIndex = EEPROM.read(EEPROM_ADDR_SG_PRESET);
}
void saveEEPROM() {
EEPROM.update(EEPROM_ADDR_MASS_UNIT, (uint8_t)massUnit);
EEPROM.update(EEPROM_ADDR_VOL_UNIT, (uint8_t)volUnit);
EEPROM.put(EEPROM_ADDR_SG_VALUE, sgValue);
EEPROM.update(EEPROM_ADDR_SG_PRESET, sgPresetIndex);
}
void updateStability(float* m) {
unsigned long now = millis();
for (int i = 0; i < 4; i++) {
float diff = abs(m[i] - lastMass[i]);
if (diff > STABILITY_THRESHOLD) {
lastChangeTime[i] = now;
stable[i] = false;
}
if (now - lastChangeTime[i] > STABILITY_TIME_MS) {
stable[i] = true;
}
lastMass[i] = m[i];
}
}
void setup() {
pinMode(BTN_RESET_PIN, INPUT_PULLUP);
pinMode(BTN_TARE_PIN, INPUT_PULLUP);
hx.begin();
enc.begin();
disp.begin();
menu.begin();
menu.setSG(&sgValue, &sgPresetIndex);
menu.setUnits(&massUnit, &volUnit);
loadEEPROM();
}
void loop() {
enc.updateButton();
// Flashing toggle
if (millis() - lastFlash > FLASH_INTERVAL_MS) {
lastFlash = millis();
flashState = !flashState;
}
int delta = enc.getDelta();
bool pressed = enc.buttonPressed();
unsigned long stepTime = enc.lastStepTime();
bool resetPressed = (digitalRead(BTN_RESET_PIN) == LOW);
bool tarePressed = (digitalRead(BTN_TARE_PIN) == LOW);
// Reset: reload EEPROM + tare
if (resetPressed) {
loadEEPROM();
hx.tareAll();
for (int i = 0; i < 4; i++) {
stable[i] = false;
lastChangeTime[i] = millis();
}
return;
}
// Tare all cells
if (tarePressed) {
hx.tareAll();
for (int i = 0; i < 4; i++) {
stable[i] = false;
lastChangeTime[i] = millis();
}
}
// Menu state machine
MenuState st = menu.update(delta, pressed, stepTime);
// MAIN SCREEN
if (st == STATE_MAIN) {
float rawMass[4];
hx.readAll(rawMass);
updateStability(rawMass);
float massConv[4];
float volConv[4];
float avgMass = 0;
float avgVol = 0;
for (int i = 0; i < 4; i++) {
float vol = rawMass[i] / sgValue;
massConv[i] = Units::convertMass(rawMass[i], massUnit);
volConv[i] = Units::convertVolume(vol, volUnit);
avgMass += massConv[i];
avgVol += volConv[i];
}
avgMass /= 4.0f;
avgVol /= 4.0f;
const char* sgName =
(sgPresetIndex < SG_PRESET_COUNT)
? SG_PRESET_NAMES[sgPresetIndex]
: "Custom";
disp.showMainScreen(
massConv, volConv, stable, flashState,
avgMass, avgVol,
massUnit, volUnit,
sgName, sgValue
);
}
// SG EDIT
else if (st == STATE_SG_EDIT) {
disp.showSGEdit(sgValue);
}
// MENU SCREENS
else if (st == STATE_MENU) {
disp.showMenu("Menu", MENU_MAIN_ITEMS[menu.getSelectedIndex()]);
}
else if (st == STATE_SG_PRESET) {
disp.showMenu("SG Preset", SG_PRESET_NAMES[menu.getSelectedIndex()]);
}
else if (st == STATE_MASS_UNIT) {
disp.showMenu("Mass Unit", MENU_MASS_UNITS[(int)massUnit]);
}
else if (st == STATE_VOL_UNIT) {
disp.showMenu("Vol Unit", MENU_VOL_UNITS[(int)volUnit]);
}
// Save settings when exiting menu
if (st == STATE_MAIN && pressed) {
saveEEPROM();
}
}