diff --git a/src/main.cpp b/src/main.cpp index fe7db5d..01c3030 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,8 @@ #define SERIAL_BAUD 115200 #define MODBUS_BAUD 9600 +#include +#include #include #include #include @@ -24,30 +26,34 @@ uint8_t modbusReadCmd[] = {0x01, 0x03, 0x00, 0x04, 0x00, 0x01}; unsigned long lastCommandTime = 0; const unsigned long COMMAND_INTERVAL = 1000; volatile bool g_buttonInterruptFired = false; +int g_waterLevelCm = -1; +void addWaterLevel(); -String *descr[] = {"NA", "Pump", "Barrel -> Back Left", "Back Left -> Barrel", "Barrel to Hose", "CW to Hose", "NA", "NA"}; +// String *descr[] = {"NA", "Pump", "Barrel -> Back Left", "Back Left -> Barrel", "Barrel to Hose", "CW to Hose", "NA", "NA"}; void printBin(byte aByte) { for (int8_t aBit = 7; aBit >= 0; aBit--) { - Serial.write(aBit); - Serial.write(":"); + // Serial.print(aBit); + // Serial.write(":"); Serial.write(bitRead(aByte, aBit) ? '1' : '0'); - Serial.write(" "); + // Serial.write(" "); } Serial.println(); } // NA, Pump, Barrel -> Back Left, Back Left -> Barrel, Barrel to Hose, CW to Hose, NA, NA -struct Action { - const char* name; +struct Action +{ + const char *name; uint8_t stat; uint8_t mask; bool active; }; -enum class ActionId : size_t { +enum class ActionId : size_t +{ CityWaterToHose = 0, BarrelToHose, BackLeftToBarrel, @@ -57,53 +63,101 @@ enum class ActionId : size_t { }; inline Action g_actions[] = { - { "CityWaterToHose", 0b00001100, 0b00001100, false }, - { "BarrelToHose", 0b01000000, 0b01101100, false }, - { "BackLeftToBarrel", 0b00010000, 0b00110000, false }, - { "BarrelToBackLeft", 0b01101000, 0b01111000, false }, - { "NotAssigned", 0b00000000, 0b00000000, false }, + {"City -> Hose", 0b00001100, 0b00001100, false}, + {"Barrel -> Hose", 0b01000000, 0b01101100, false}, + {"Back Left -> Barrel", 0b00010000, 0b00110000, false}, + {"Barrel -> Back Left", 0b01101000, 0b01111000, false}, + {"NotAssigned", 0b00000000, 0b00000000, false}, }; -inline Action& getAction(ActionId id) { +inline Action &getAction(ActionId id) +{ return g_actions[static_cast(id)]; } - - uint8_t current_stat = 0b00000000; uint8_t current_mask = 0b00000000; -bool actionCityWaterToHose = false; -uint8_t statCityWaterToHose = 0b00001100; -uint8_t maskCityWaterToHose = 0b00001100; - -bool actionBarrelToHose = false; -uint8_t statBarrelToHose = 0b01000000; -uint8_t maskBarrelToHose = 0b01101100; - -bool actionBackLeftToBarrel = false; -uint8_t statBackLeftToBarrel = 0b00010000; -uint8_t maskBackLeftToBarrel = 0b00110000; - -bool actionBarrelToBackLeft = false; -uint8_t statBarrelToBackLeft = 0b01101000; -uint8_t maskBarrelToBackLeft = 0b01111000; - -bool actionNotAssigned = false; -int g_waterLevelCm = -1; - -enum class ActionId +void updateLCDDisplay() { - CityWaterToHose, - BarrelToHose, - BackLeftToBarrel, - BarrelToBackLeft, - NotAssigned, -}; + lcd.clear(); + uint8_t c_row = 0; + for (size_t i = 0; i < static_cast(ActionId::Count); ++i) + { + if (g_actions[i].active) + { + lcd.setCursor(0, c_row); + lcd.print(g_actions[i].name); + c_row++; + } + } + addWaterLevel(); +} + +void generateMasksByActive() +{ + current_stat = 0b00000000; + current_mask = 0b00000000; + for (size_t i = 0; i < static_cast(ActionId::Count); ++i) + { + Action &c_action = g_actions[i]; + if (c_action.active) + { + current_mask = current_mask | c_action.mask; + current_stat = (current_stat & ~c_action.mask) | (c_action.stat & c_action.mask); + } + } +} void updateStateByLastAction(ActionId LastAction) { + Action &c_action = getAction(LastAction); + if (c_action.active) + { + c_action.active = false; + generateMasksByActive(); + updateLCDDisplay(); + return; + } + uint8_t change_stat = c_action.stat; + uint8_t change_mask = c_action.mask; + + uint8_t old_stat = current_stat; + uint8_t old_mask = current_mask; + + uint8_t potential_new_stat = (current_stat & ~change_mask) | (change_stat & change_mask); + + uint8_t potential_old_change = (potential_new_stat ^ old_stat) & old_mask; + + if (potential_old_change > 0) + { + Serial.print(c_action.name); + Serial.print(" collides with existing. Setting new mask to this one"); + Serial.println(); + + current_stat = change_stat; + current_mask = change_mask; + for (size_t i = 0; i < static_cast(ActionId::Count); ++i) + { + g_actions[i].active = false; + } + c_action.active = true; + } + else + { + Serial.print(c_action.name); + Serial.print(" has no collisions with existing. Setting updated mask"); + Serial.println(); + current_stat = potential_new_stat; + current_mask = old_mask | change_mask; + c_action.active = true; + } + Serial.print("Stat: "); + printBin(current_stat); + Serial.print("Mask: "); + printBin(current_mask); + updateLCDDisplay(); } void applyRelayStateIfChanged() @@ -119,11 +173,6 @@ void applyRelayStateIfChanged() Serial.print((relayValue >> bit) & 0x01); } Serial.println(); - // if (relayValue == lastRelayValue) - // { - // return; - // } - Wire.beginTransmission(RELAY_CONTROL_ADDRESS); Wire.write(relayValue); uint8_t error = Wire.endTransmission(); @@ -147,198 +196,60 @@ void applyRelayStateIfChanged() } } -void rebuildCurrentState() +void buildTrimmedWaterLevelText(char *outBuffer, size_t outBufferSize) { - current_stat = 0; - current_mask = 0; - - if (actionCityWaterToHose) - { - - current_stat = (current_stat & ~maskCityWaterToHose) | (statCityWaterToHose & maskCityWaterToHose); - current_mask |= maskCityWaterToHose; - } - - if (actionBarrelToHose) - { - current_stat = (current_stat & ~maskBarrelToHose) | (statBarrelToHose & maskBarrelToHose); - current_mask |= maskBarrelToHose; - } - - if (actionBackLeftToBarrel) - { - current_stat = (current_stat & ~maskBackLeftToBarrel) | (statBackLeftToBarrel & maskBackLeftToBarrel); - current_mask |= maskBackLeftToBarrel; - } - - if (actionBarrelToBackLeft) - { - current_stat = (current_stat & ~maskBarrelToBackLeft) | (statBarrelToBackLeft & maskBarrelToBackLeft); - current_mask |= maskBarrelToBackLeft; - } - - applyRelayStateIfChanged(); -} - -void formatRightJustifiedWaterLine(char *buffer, size_t bufferSize) -{ - snprintf(buffer, bufferSize, " "); - - if (g_waterLevelCm < 0) + if (outBuffer == nullptr || outBufferSize == 0) { return; } - char valueText[21]; - snprintf(valueText, sizeof(valueText), "%d cm", g_waterLevelCm); + char temp[24]; + snprintf(temp, sizeof(temp), "%d cm", g_waterLevelCm); - size_t valueLength = strlen(valueText); - size_t startColumn = valueLength < 20 ? 20 - valueLength : 0; - memcpy(buffer + startColumn, valueText, min(valueLength, 20)); + const char *start = temp; + while (*start && std::isspace(static_cast(*start))) + { + ++start; + } + + const char *end = temp + strlen(temp); + while (end > start && std::isspace(static_cast(*(end - 1)))) + { + --end; + } + + size_t len = static_cast(end - start); + if (len >= outBufferSize) + { + len = outBufferSize - 1; + } + + memcpy(outBuffer, start, len); + outBuffer[len] = '\0'; } -void updateLcdStatesIfChanged() +void addWaterLevel() { - static bool lastActionCityWaterToHose = true; - static bool lastActionBarrelToHose = true; - static bool lastActionBackLeftToBarrel = true; - static bool lastActionBarrelToBackLeft = true; - static int lastWaterLevelCm = -2; + char text[24]; + buildTrimmedWaterLevelText(text, sizeof(text)); // e.g. "123 cm" - if (lastActionCityWaterToHose == actionCityWaterToHose && - lastActionBarrelToHose == actionBarrelToHose && - lastActionBackLeftToBarrel == actionBackLeftToBarrel && - lastActionBarrelToBackLeft == actionBarrelToBackLeft && - lastWaterLevelCm == g_waterLevelCm) + size_t len = strlen(text); + if (len > 20) { - return; + len = 20; + text[20] = '\0'; } - lastActionCityWaterToHose = actionCityWaterToHose; - lastActionBarrelToHose = actionBarrelToHose; - lastActionBackLeftToBarrel = actionBackLeftToBarrel; - lastActionBarrelToBackLeft = actionBarrelToBackLeft; - lastWaterLevelCm = g_waterLevelCm; + const uint8_t row = 3; // last row on 20x4 + const uint8_t startCol = static_cast(20 - len); // right-justified - lcd.clear(); - int lcdLine = 0; + // Clear row first so old characters do not remain + // lcd.setCursor(0, row); + // lcd.print(" "); // 20 spaces - if (actionCityWaterToHose) - { - lcd.setCursor(0, lcdLine++); - lcd.print("City -> Hose "); - } - - if (actionBarrelToHose) - { - lcd.setCursor(0, lcdLine++); - lcd.print("Barrel -> Hose "); - } - - if (actionBackLeftToBarrel) - { - lcd.setCursor(0, lcdLine++); - lcd.print("BackL -> Barrel "); - } - - if (actionBarrelToBackLeft) - { - lcd.setCursor(0, lcdLine++); - lcd.print("Barrel -> BackL "); - } - - while (lcdLine < 3) - { - lcd.setCursor(0, lcdLine++); - lcd.print(" "); - } - - char waterLine[21]; - formatRightJustifiedWaterLine(waterLine, sizeof(waterLine)); - lcd.setCursor(0, 3); - lcd.print(waterLine); -} - -void applyLatestAction(ActionId latestAction) -{ - switch (latestAction) - { - case ActionId::CityWaterToHose: - actionCityWaterToHose = !actionCityWaterToHose; - if (actionCityWaterToHose) - { - actionBarrelToHose = false; - } - break; - - case ActionId::BarrelToHose: - actionBarrelToHose = !actionBarrelToHose; - if (actionBarrelToHose) - { - actionCityWaterToHose = false; - actionBarrelToBackLeft = false; - } - break; - - case ActionId::BackLeftToBarrel: - actionBackLeftToBarrel = !actionBackLeftToBarrel; - if (actionBackLeftToBarrel) - { - actionBarrelToBackLeft = false; - } - break; - - case ActionId::BarrelToBackLeft: - actionBarrelToBackLeft = !actionBarrelToBackLeft; - if (actionBarrelToBackLeft) - { - actionBarrelToHose = false; - actionBackLeftToBarrel = false; - } - break; - } - - // Allowed combinations: - // - CityWaterToHose + BackLeftToBarrel - // - CityWaterToHose + BarrelToBackLeft - // - BarrelToHose + BackLeftToBarrel - if (actionCityWaterToHose && actionBarrelToHose) - { - if (latestAction == ActionId::BarrelToHose) - { - actionCityWaterToHose = false; - } - else - { - actionBarrelToHose = false; - } - } - - if (actionBackLeftToBarrel && actionBarrelToBackLeft) - { - if (latestAction == ActionId::BarrelToBackLeft) - { - actionBackLeftToBarrel = false; - } - else - { - actionBarrelToBackLeft = false; - } - } - - if (actionBarrelToHose && actionBarrelToBackLeft) - { - if (latestAction == ActionId::BarrelToBackLeft) - { - actionBarrelToHose = false; - } - else - { - actionBarrelToBackLeft = false; - } - } - - rebuildCurrentState(); + // Print at exact right-justified cursor position + lcd.setCursor(startCol, row); + lcd.print(text); } void IRAM_ATTR onButtonInterrupt() @@ -411,8 +322,7 @@ void readAndPrintButtons() bool buttonP6Pressed = (portValue & (1 << 6)) == 0; bool buttonP7Pressed = (portValue & (1 << 7)) == 0; - printBin(portValue); - ActionId LastAction; + ActionId LastAction = ActionId::Count; if (buttonP3Pressed) { @@ -432,7 +342,7 @@ void readAndPrintButtons() } else if (buttonP7Pressed) { - LastAction = ActionId::NotAssigned; + // LastAction = ActionId::NotAssigned; } Serial.printf( @@ -443,73 +353,14 @@ void readAndPrintButtons() buttonP6Pressed, buttonP7Pressed, portValue); + + if (LastAction != ActionId::Count) + { + updateStateByLastAction(LastAction); + } } } -// void readAndPrintButtons() -// { -// // PCF8574 button wiring: -// // - P0 is the shared/common line and must be driven LOW. -// // - P3..P7 are button sense lines. Pressed => line pulled LOW (active-low). -// Wire.beginTransmission(BUTTONS_CONTROL_ADDRESS); -// Wire.write(0b11111110); // P0 LOW, all other pins released HIGH. -// Wire.endTransmission(); - -// Wire.requestFrom(BUTTONS_CONTROL_ADDRESS, (uint8_t)1); -// if (Wire.available()) -// { -// uint8_t portValue = Wire.read(); -// bool buttonP3Pressed = (portValue & (1 << 3)) == 0; -// bool buttonP4Pressed = (portValue & (1 << 4)) == 0; -// bool buttonP5Pressed = (portValue & (1 << 5)) == 0; -// bool buttonP6Pressed = (portValue & (1 << 6)) == 0; -// bool buttonP7Pressed = (portValue & (1 << 7)) == 0; - -// if (buttonP3Pressed && !buttonP4Pressed) -// { -// applyLatestAction(ActionId::CityWaterToHose); -// } - -// if (buttonP4Pressed && !buttonP3Pressed) -// { -// applyLatestAction(ActionId::BarrelToHose); -// } - -// if (buttonP5Pressed) -// { -// applyLatestAction(ActionId::BackLeftToBarrel); -// } - -// if (buttonP6Pressed) -// { -// applyLatestAction(ActionId::BarrelToBackLeft); -// } - -// if (buttonP7Pressed) -// { -// actionNotAssigned = !actionNotAssigned; -// } - -// Serial.printf( -// "INT Buttons P3=%d P4=%d P5=%d P6=%d P7=%d (raw=0x%02X)\n", -// buttonP3Pressed, -// buttonP4Pressed, -// buttonP5Pressed, -// buttonP6Pressed, -// buttonP7Pressed, -// portValue); - -// Serial.printf( -// "States CityWaterToHose=%d BarrelToHose=%d BackLeftToBarrel=%d BarrelToBackLeft=%d current_stat=0x%02X current_mask=0x%02X\n", -// actionCityWaterToHose, -// actionBarrelToHose, -// actionBackLeftToBarrel, -// actionBarrelToBackLeft, -// current_stat, -// current_mask); -// } -// } - void pollSerial2WaterLevel() { static uint8_t responseBuffer[256]; @@ -540,6 +391,7 @@ void pollSerial2WaterLevel() { lastPrintedWaterLevelCm = waterLevelCm; g_waterLevelCm = waterLevelCm; + addWaterLevel(); Serial.printf("Water level: %d cm (%d mm)\n", waterLevelCm, data); } } @@ -577,7 +429,7 @@ void setup() int lcdStatus = lcd.begin(20, 4); lcd.clear(); - updateLcdStatesIfChanged(); + // updateLcdStatesIfChanged(); Serial2.begin(MODBUS_BAUD, SERIAL_8N1, RXD_PIN, TXD_PIN); start = millis(); @@ -603,6 +455,6 @@ void loop() lastSerial2PollTime = millis(); pollSerial2WaterLevel(); } - updateLcdStatesIfChanged(); + // updateLcdStatesIfChanged(); delay(10); }