FINAL PROJECT / DEVELOPMENT

  1. INTRODUCTION
  2. SCHEDULE
  3. CONSTRUCTION MATERIALS
  4. DESIGN AND FABRICATION
  5. PROJECT DEVELOPMENT
  6. FUNCTIONAL TESTING AND VALIDATION
  7. FUTURE WORK & SCALABILITY
  8. ARDUINO CODE
  9. ORIGINAL FILES

INTRODUCTION

As I have described throughout the course, my SmartFlow ACS system by Aquanow allows the hot water pipes of any household installation to be preheated without the need to turn on the taps and wait for hot water to arrive while liters and liters of cold water go to waste.

By using SmartFlow ACS, hot water reaches the tap automatically while it remains closed, resulting in significant water savings.

The fundamentals of this system and its operation are detailed in the System Integration assignment.

SCHEDULE

Due to my biggest challenge throughout the course, all work related to the final project was delayed until two weeks before the presentation. Therefore, both the design and construction phases became a true marathon.

In fact, this documentation was compiled after the final project presentation and after receiving the approval from Neil.

CONSTRUCTION MATERIALS

Physically, the equipment is simple.

The two main components that make up the system are:

A Pump

Pump

Which recirculates the water through the hot and cold water pipes.

A solenoid valve

Pump

Which closes the connection between these pipes.

This point is crucial because by integrating the equipment into an existing installation, we are creating a by-pass between the hot and cold water lines.

If we only stopped the pump when the system is idle, the connection between both lines would remain open, which could result in hot water coming out of the cold taps or vice versa. This solenoid valve closes the communication between the two lines when the system is inactive, effectively eliminating the by-pass.

The remaining materials are used to control these two core units, such as the electronics, the remote control, and the temperature probe, as well as to house all the components, which is the purpose of the 3D-printed enclosure we will make.

A full breakdown of all components and their costs can be found in the System Integration assignment.

DESIGN AND FABRICATION

HYDRAULICS

The hydraulic section is the only part that was purchased commercially rather than fabricated in the FabLab.

Since the system works with hot water, these components must be completely watertight. Given the limited time available, it was decided to purchase off-the-shelf components and assemble them using Teflon tape.

These components are:

Once assembled, the hydraulic body forms a single, unified piece that will be secured to the enclosure housing the entire system.

MAIN ENCLOSURE

The main enclosure is designed to house both the hydraulic and electronic components, creating a physical separation between them. The electrical section is positioned above the hydraulics to prevent any potential water leaks from affecting the electronics.

3D

It is fabricated as a single piece using 3D printing with PETG filament to ensure mechanical strength. The design also incorporates bosses (mounting posts) to secure the hydraulic components using clamps fastened with screws, as well as to mount the electronic board with screws.

Openings are provided to route the wiring from the hydraulic section to the electrical section, along with cutouts for the water hoses to enter and exit, and a slot for the 24V DC power jack.

Support

Additionally, an external opening is included for the XIAO RP2040 USB connector in case reprogramming is required in the future.

In future iterations, the plan is to implement guide rails to hold the electronic board instead of screws, as well as integrated clips in the enclosure body for more efficient cable management. Furthermore, the goal is to add rubber grommets around the water hoses and rubber plugs for unused openings, such as the USB connector slot.

The final enclosure design was done in Fusion due to the learning curve and time constraints I faced with FreeCAD, although the goal remains to continue working with FreeCAD.

Enclosure

The printing was carried out on The Bambu, which was the only printer in the FabLab capable of printing a part of this size.

Enclosure

Finally, the entire assembly was covered with an acrylic lid engraved with the Aquanow logo. Both the cutting of the acrylic and the engraving were performed using the laser cutter.

The final result: A transparent lid featuring the brand logo, revealing the interior to better demonstrate its operation.

Logo

ELECTRONICS

The electronics are designed using KiCad. Three electronic boards were designed and fabricated:

Main board

The main electronic board is the one placed inside the main enclosure alongside the hydraulics. This board incorporates a Seeed Studio XIAO RP2040 microcontroller and is responsible for controlling the entire operation of the system. Its tasks are:

  1. Provide power supply to all components.
  2. Interconnect all system components.
  3. The following elements connect directly to the main electronic board:

  4. House backup/redundant systems.

Remote control board

The sole purpose of this board is to provide physical support and to integrate the I2C communication lines for the OLED display and its power supply. It also routes the signal from the start push button located on the remote control unit.

Temperature sensor board

This temperature board/module simply houses the analog NTC temperature sensor along with the 10K resistor required to take accurate temperature readings from the XIAO.

The signal output is placed between the sensor and the resistor, resulting in a typical voltage divider configuration.

The component layout on the board follows this serial order:

  1. Power Supply Line: Connected to the regulated 3.3V from the board.
  2. NTC Probe: The first component of the divider. One end connects to the 3.3V line and the other connects to the central node.
  3. Signal Node (RP2040 Pin): The intermediate point between the NTC and the 10K resistor. From here, the trace/wire connects to the analog pin of the microcontroller.
  4. 10K Resistor: The second component (reference or pull-down resistor). It is connected between the central node and GND.
  5. Ground (GND): The circuit return path.

Since it is an NTC (Negative Temperature Coefficient) probe, its resistance is inversely proportional to the temperature:

REMOTE CONTROL

For the remote control, a pocketing technique was used by hollowing out a piece of wood to house the electronic board. This board serves as the mounting base for the OLED display and the start button.

The board includes the connector for the cable coming from the main electronic board, which routes the 3.3V, GND, SCL, and SDA signals for the OLED display, as well as the button signal wire that pulls the pin to ground when pressed. Additionally, the board contains the dedicated connectors for both the button and the display.

Beneath the display, a 3D-printed bracket was placed to prevent the screen from sinking into the enclosure.

The button was mounted on the side, connected to the board's connector using small jumper wires.

The entire assembly was covered with a transparent acrylic plate to allow the interior to be fully visible, and this lid was secured to the wooden body using screws.

Design & Safety Note: The start button wiring was implemented using a pull-up configuration. This setup was chosen over a pull-down configuration for safety reasons. In a pull-down setup, the wiring would be constantly energized, meaning any damage to the cable could short the XIAO's 3.3V pin directly to ground. Conversely, with a pull-up configuration, the energized line remains on the main PCB, and the line running to the remote control only serves to connect to GND when the button is pressed. As a result, any damage to the cable would simply cause a malfunction, but never a short circuit in the microcontroller.

PROJECT DEVELOPMENT

Weekend 1

Hydraulic System Assembly

The initial phase of the project focused entirely on the hydraulic architecture. To ensure reliable operation, commercial-grade components were selected and assembled.

Week1 25 - 28

Electronics Design & Production

PCB Design and Fabrication Challenges

Three distinct boards were designed using KiCad, leveraging the standard components available in the official Fab electronics library

While the temperature sensor breakout board was milled without issues, the other two boards presented significant fabrication challenges:

Important Fabrication Note: When configuring the milling cutter in Mods to perform the drills, it is crucial to manually set the tool diameter to 0.78 mm instead of 0.8 mm. If a larger or exact diameter is selected, Mods might skip some holes entirely. This happens because the physical holes are roughly the same size as the tool; if the generated .png file renders a hole even slightly smaller than the specified endmill diameter, the software's algorithm will assume the tool cannot fit and will refuse to calculate the toolpath. By defining the tool as 0.78 mm, we provide that tiny safety margin required for Mods to process and execute all the drill holes successfully.

Connector Strategy and Cable Management

Selecting the right connectors was crucial to balancing ease of maintenance with electrical safety:

Assembly and Soldering

Once the PCBs were successfully routed and cleared of manufacturing defects, all surface-mount and through-hole components were populated using a high-quality soldering iron, standard solder wire, and meticulous patience.

Microcontroller Programming (RP2040)

With the hardware fully populated, the workflow transitioned to flashing the core firmware onto the RP2040 chip:

Week1 28 - 31

Enclosure and Housing Design

During this stage of the project development, the main goal was to design and manufacture the physical enclosures for both the main system and the remote control unit.

This involved precise 3D modeling to accommodate the custom PCBs, routing for wiring, and ensuring a functional, clean setup for the final assembly.

As previously described, the main enclosure was designed in Fusion 360, taking advantage of the milling times from the previous PCB fabrication.

At the same time, the remote control unit did not require a highly elaborate 3D design; since it was manufactured using the CNC pocketing technique, only two rectangles sketched directly in Inkscape were needed.

The covers for both elements were also created in Inkscape, as they were purely 2D designs.

In terms of materials, PETG was selected for the main enclosure due to its durability and thermal resistance. The remote control unit was machined out of wood using the CNC router, while the covers for both components were laser-cut from acrylic (plexiglass).

Milling process of the remote control unit and final result.

Weekend 2

Testing and Troubleshooting

Testing began by powering the custom PCBs using the 5V supply from the Seeed XIAO RP2040 itself. The initial results were fully satisfactory. However, the moment 24V was introduced through the DC power jack, the left motor driver instantly blew up.

This was a major setback in the project’s development timeline. With time running extremely tight, remanufacturing the entire board from scratch was highly problematic, and the exact cause of the failure was not immediately clear.

Fortunately, as previously mentioned, a backup contingency system had been integrated into the PCB design. By utilizing alternative GPIO outputs from the XIAO, it was possible to control two onboard relays.

These relays stepped in perfectly to manage the pump and the solenoid valve, the only two components affected by the driver failure. The rest of the control electronics continued to function flawlessly.

Root Cause Analysis

After carefully analyzing the driver’s datasheet and technical documentation post-incident, (TOSHIBA´S DATASHEET) the exact cause of the failure was identified.

To integrate the drivers into the board layout and map the pinout, multiple reference designs were followed alongside the official datasheet, but a critical detail was overlooked: the absolute maximum voltage tolerance for the Vref pin is 5V.

In the reference documentations consulted, the Vref line was tied directly to the Vm (motor voltage) track because those specific projects operated entirely on supplies under 5V.

This design choice was replicated on the custom board without anyone at the Fab Lab noticing the oversight during review. Consequently, when 24V was injected into the circuit, it rushed straight into the Vref pin, instantly destroying the driver.

Relay Configuration

The wiring and operation of the backup relays are straightforward:

Once this electronic workaround was successfully implemented, the fabrication of the remaining boards was finalized, and the OLED display mount was 3D printed using PLA.

Week2 1 - 7

System Integration, Assembly & Functional Testing

Following the setbacks with the relay driver stage, the timeline for the final project had to be restructured.

The initial plan was to complete the entire mechanical and electronic integration during the previous week, utilizing a planned vacation period from my regular job to maintain exclusive dedication to the project.

However, troubleshooting the hardware forced the assembly phase into this week, requiring a demanding balance between professional work commitments and intense hours at the Fab Lab, significantly restricting the overall time available.

Time Management & Pivot Note: Managing restricted hours meant that the integration workflow had to be optimized for maximum efficiency. Every assembly step was planned meticulously before arriving at the lab to make the most of the limited time windows.

1. Mechanical Assembly and Structural Housing

The physical integration of the SmartFlow ACS system was executed to guarantee a compact, secure, and fully portable unit without requiring any residential masonry work. The assembly workflow consisted of the following stages:

2. Electrical and Signal Integration

Once the physical components were placed, the wiring system was completed and double-checked:

3. Preliminary Functional Testing

With the system fully assembled, a series of controlled tests were initiated on the workbench before deploying the unit to a real test environment:

4. Remote Control Unit Assembly

The remote control enclosure was manufactured with precise tolerances so that the component-bearing PCB achieved a perfect press-fit alignment. This provided the exact depth required for the acrylic cover to sit flush with the OLED display surface.

The activation push-button was installed on the lateral side of the housing, securely anchored using its own structural hex nut.

5. Enclosure Sealing and Final Hardware Closure

Once all internal components were fully integrated, both the main base unit and the remote control housing were sealed. The top covers, which were laser-cut from acrylic sheet, were precisely aligned and fastened to the main bodies using mechanical screws, ensuring a solid and robust final finish.

6. Initial Functional Testing

Finally, during the weekend prior to the official presentation on June 8th, the entire system was thoroughly tested, verifying its optimal performance and successful operation.

FUNCTIONAL TESTING AND VALIDATION

The system's hydraulic lines were connected to a Fab Lab water tap using flexible hoses, and the main power supply was energized.

Through this live setup, it was successfully verified that the entire integrated system operates safely and functions exactly as designed.

FUTURE WORK & SCALABILITY

To transition the SmartFlow ACS from a successful functional prototype to a commercial or highly optimized open-source product, several design iterations have been planned for future versions:

ARDUINO CODE

Arduino Code for use with the Drivers

Drivers

// ====================================================================
//    SmartFlow ACS - Final Logic (NTC Probe Correction)
// ====================================================================

#include 
#include 
#include 
#include  // Required for logarithmic calculation (NTC)

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// --- ORIGINAL PIN MAPPING ---
const int PIN_NTC = D0;          
const int PIN_PUMP_IN1 = D1;    
const int PIN_PUMP_IN2 = D2;    
const int PIN_BUTTON = D3;       
const int PIN_VALVE_IN1 = D6;  
const int PIN_VALVE_IN2 = D7;  

// --- NTC CONFIGURATION PARAMETERS ---
const float SERIES_RESISTANCE = 10000.0; // 10K fixed pull-up resistor
const float NOMINAL_THERMISTOR = 10000.0; // NTC resistance at 25°C (10K)
const float NOMINAL_TEMP = 25.0;         // Reference nominal temperature
const float BETA = 3950.0;               // Typical Beta coefficient for commercial NTCs
const int ADC_MAX = 1023;                // ADC Resolution (10-bit)

enum States { IDLE, TEMPORARY_SCREEN, RUNNING, COOL_DOWN_PHASE };
States currentState = IDLE;

unsigned long stateStartTime = 0;
unsigned long coolDownPhaseTime = 0;

void setup() {
  Serial.begin(115200);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    for(;;); 
  }
  
  pinMode(PIN_PUMP_IN1, OUTPUT);
  pinMode(PIN_PUMP_IN2, OUTPUT);
  pinMode(PIN_VALVE_IN1, OUTPUT);
  pinMode(PIN_VALVE_IN2, OUTPUT);
  pinMode(PIN_BUTTON, INPUT); 

  // Splash screen on boot
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 10);
  display.println("  SYSTEM CONFIGURING");
  display.setCursor(0, 30);
  display.println("  Ready...");
  display.display();
  delay(2000);

  turnOffActuators();
}

void loop() {
  // 1. Read real temperature using the NTC thermistor equation
  int analogValue = analogRead(PIN_NTC);
  
  // Calculate NTC resistance based on ADC reading
  // (Adjusted for common voltage divider setup: NTC connected to GND and R_10K to VCC)
  float resistanceNTC = SERIES_RESISTANCE / ((float)ADC_MAX / analogValue - 1.0);
  
  // Apply Steinhart-Hart equation (Beta parameter equation)
  float temp;
  temp = resistanceNTC / NOMINAL_THERMISTOR;     // (R/Ro)
  temp = log(temp);                             // ln(R/Ro)
  temp /= BETA;                                 // 1/B * ln(R/Ro)
  temp += 1.0 / (NOMINAL_TEMP + 273.15);        // + (1/To)
  temp = 1.0 / temp;                            // Invert to obtain Kelvin
  temp -= 273.15;                               // Convert to Celsius

  // *Safety Note*: If the logic reads inverted, it means that on your PCB layout 
  // the NTC is tied to VCC. In that case, change the 'resistanceNTC' calculation line to:
  // float resistanceNTC = SERIES_RESISTANCE * ((float)ADC_MAX / analogValue - 1.0);

  // 2. Detect actual button press (Falling Edge: transitions from HIGH to LOW)
  bool buttonPressed = false;
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(50); // Software debouncing filter to ignore initial hardware noise
    if (digitalRead(PIN_BUTTON) == LOW) {
      buttonPressed = true;
      
      // HANDBRAKE LOCK: Block code execution here until the button is fully RELEASED
      while(digitalRead(PIN_BUTTON) == LOW) {
        delay(10); 
      }
    }
  }

  // 3. State Machine
  switch (currentState) {
    
    case IDLE:
      turnOffActuators();
      display.clearDisplay();
      display.display(); // Blank display for power saving mode

      if (buttonPressed) {
        stateStartTime = millis();
        if (temp > 30.0) {
          currentState = TEMPORARY_SCREEN;
        } else {
          currentState = RUNNING;
          stateStartTime = millis(); // Record timestamp when operation started (for the 5-min timeout)
        }
      }
      break;

    case TEMPORARY_SCREEN:
      updateOLED("HIGH TEMP", temp, "STANDBY");
      // Timeout after 5 seconds to turn off the display automatically
      if (millis() - stateStartTime >= 5000) {
        currentState = IDLE;
      }
      break;

    case RUNNING:
      turnOnActuators();
      updateOLED("RUNNING", temp, "PUMP:ON/VALV:ON");

      // Stop system if: Temp exceeds 35°C, 5 minutes pass (300,000 ms), or the button is pressed again
      if (temp >= 35.0 || (millis() - stateStartTime >= 300000) || buttonPressed) {
        currentState = COOL_DOWN_PHASE;
        coolDownPhaseTime = millis();
      }
      break;

    case COOL_DOWN_PHASE:
      unsigned long elapsedTime = millis() - coolDownPhaseTime;

      if (elapsedTime < 5000) {
        // First 5 seconds: Pump OFF, Solenoid Valve remains ON
        digitalWrite(PIN_PUMP_IN1, LOW);
        digitalWrite(PIN_PUMP_IN2, LOW);
        digitalWrite(PIN_VALVE_IN1, HIGH); 
        digitalWrite(PIN_VALVE_IN2, LOW);
        updateOLED("PUMP OFF", temp, "PUMP:OFF/VALV:ON");
      } 
      else if (elapsedTime >= 5000 && elapsedTime < 10000) {
        // From second 5 to 10: All Actuators OFF
        turnOffActuators();
        updateOLED("ALL OFF", temp, "PUMP:OFF/VALV:OFF");
      } 
      else {
        // At 10 seconds, return to full standby mode and blank the screen
        currentState = IDLE;
      }
      break;
  }
  delay(20);
}

void turnOnActuators() {
  digitalWrite(PIN_PUMP_IN1, HIGH);
  digitalWrite(PIN_PUMP_IN2, LOW);
  digitalWrite(PIN_VALVE_IN1, HIGH);
  digitalWrite(PIN_VALVE_IN2, LOW);
}

void turnOffActuators() {
  digitalWrite(PIN_PUMP_IN1, LOW);
  digitalWrite(PIN_PUMP_IN2, LOW);
  digitalWrite(PIN_VALVE_IN1, LOW);
  digitalWrite(PIN_VALVE_IN2, LOW);
}

void updateOLED(String state, float temperature, String actuators) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);  display.print("SMARTFLOW ACS");
  display.setCursor(0, 12); display.print("ST: "); display.print(state);
  display.setTextSize(2);
  display.setCursor(0, 26); display.print(temperature, 1); display.write(247); display.print("C");
  display.setTextSize(1);
  display.setCursor(0, 52); display.print("OUT: "); display.print(actuators);
  display.display();
}
        

Arduino Code for use with the Relays

Relays

// ====================================================================
//    SmartFlow ACS - Production Logic with Relays (Final NTC)
//    v3.2 - Temperature updated every 1 second
// ====================================================================

#include 
#include 
#include 
#include  // Required for logarithmic calculation (NTC)

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// --- FINAL PIN MAPPING WITH RELAYS ---
const int PIN_NTC = D0;          // Thermistor analog input
const int PIN_BUTTON = D3;       // Physical push-button on D3
const int PIN_PUMP_RELAY = D8;   // Output for Pump relay
const int PIN_VALVE_RELAY = D9;  // Output for Solenoid Valve relay

// --- NTC CONFIGURATION PARAMETERS ---
const float SERIES_RESISTANCE = 10000.0; // 10K fixed pull-up resistor
const float NOMINAL_THERMISTOR = 10000.0; // NTC resistance at 25°C (10K)
const float NOMINAL_TEMP = 25.0;         // Reference nominal temperature
const float BETA = 3950.0;               // Typical Beta coefficient for commercial NTCs
const int ADC_MAX = 1023;                // ADC Resolution (10-bit)

// --- DIRECT LOGIC CONFIGURATION ---
const int RELAY_ON = HIGH;  // HIGH activates the external relay
const int RELAY_OFF = LOW;  // LOW deactivates it

enum States { IDLE, TEMPORARY_SCREEN, RUNNING, COOL_DOWN_PHASE };
States currentState = IDLE;

unsigned long stateStartTime = 0;
unsigned long coolDownPhaseTime = 0;

// --- VARIABLES FOR TEMPERATURE STABILIZATION ---
unsigned long lastTempSampling = 0;    // Stores when the last measurement occurred
float stableTemp = 20.0;               // Stores the last calculated value to use in the loop

void setup() {
  Serial.begin(115200);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    for(;;); 
  }
  
  // Configure relay pins as outputs
  pinMode(PIN_PUMP_RELAY, OUTPUT);
  pinMode(PIN_VALVE_RELAY, OUTPUT);
  pinMode(PIN_BUTTON, INPUT); 

  // For safety, ensure they initialize turned off
  turnOffActuators();

  // Splash screen on boot
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 10);
  display.println("  SMARTFLOW ACS v3.2");
  display.setCursor(0, 30);
  display.println("  Stabilized Temp.");
  display.display();
  delay(2000);
  
  // Perform an initial reading so it does not default to 20.0ºC
  readTemperature(); 
}

void loop() {
  // 1. Sample temperature only every 1000 milliseconds (1 second)
  if (millis() - lastTempSampling >= 1000) {
    readTemperature();
    lastTempSampling = millis(); // Reset the sample interval timer
  }

  // 2. Detect actual button press with edge control (waits for release)
  bool buttonPressed = false;
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(50); // Software debouncing filter
    if (digitalRead(PIN_BUTTON) == LOW) {
      buttonPressed = true;
      while(digitalRead(PIN_BUTTON) == LOW) {
        delay(10); // Block execution until the button is fully released
      }
    }
  }

  // 3. State Machine utilizing the stabilized temperature value
  switch (currentState) {
    
    case IDLE:
      turnOffActuators();
      display.clearDisplay();
      display.display(); // Blank screen (Power saving mode)

      if (buttonPressed) {
        stateStartTime = millis();
        if (stableTemp > 30.0) {
          currentState = TEMPORARY_SCREEN;
        } else {
          currentState = RUNNING;
          stateStartTime = millis(); // Record timestamp when operation started
        }
      }
      break;

    case TEMPORARY_SCREEN:
      updateOLED("HIGH TEMP", stableTemp, "STANDBY");
      if (millis() - stateStartTime >= 5000) {
        currentState = IDLE;
      }
      break;

    case RUNNING:
      turnOnActuators();
      updateOLED("RUNNING", stableTemp, "PUMP:ON/VALV:ON");

      // Stop conditions utilizing the stable temperature value
      if (stableTemp >= 35.0 || (millis() - stateStartTime >= 300000) || buttonPressed) {
        currentState = COOL_DOWN_PHASE;
        coolDownPhaseTime = millis();
      }
      break;

    case COOL_DOWN_PHASE:
      unsigned long elapsedTime = millis() - coolDownPhaseTime;

      if (elapsedTime < 5000) {
        digitalWrite(PIN_PUMP_RELAY, RELAY_OFF);
        digitalWrite(PIN_VALVE_RELAY, RELAY_ON);
        updateOLED("PUMP OFF", stableTemp, "PUMP:OFF/VALV:ON");
      } 
      else if (elapsedTime >= 5000 && elapsedTime < 10000) {
        digitalWrite(PIN_PUMP_RELAY, RELAY_OFF);
        digitalWrite(PIN_VALVE_RELAY, RELAY_OFF);
        updateOLED("ALL OFF", stableTemp, "PUMP:OFF/VALV:OFF");
      } 
      else {
        currentState = IDLE;
      }
      break;
  }
  delay(20);
}

// --- SEPARATED FUNCTION TO READ THE NTC THERMISTOR ---
void readTemperature() {
  int analogValue = analogRead(PIN_NTC);
  float resistanceNTC = SERIES_RESISTANCE / ((float)ADC_MAX / analogValue - 1.0);
  
  float temp;
  temp = resistanceNTC / NOMINAL_THERMISTOR;     
  temp = log(temp);                             
  temp /= BETA;                                 
  temp += 1.0 / (NOMINAL_TEMP + 273.15);        
  temp = 1.0 / temp;                            
  temp -= 273.15;                               
  
  stableTemp = temp; // Update the global variable read by the rest of the program
}

// --- ACTUATOR CONTROL FUNCTIONS ---
void turnOnActuators() {
  digitalWrite(PIN_PUMP_RELAY, RELAY_ON);
  digitalWrite(PIN_VALVE_RELAY, RELAY_ON);
}

void turnOffActuators() {
  digitalWrite(PIN_PUMP_RELAY, RELAY_OFF);
  digitalWrite(PIN_VALVE_RELAY, RELAY_OFF);
}

void updateOLED(String state, float temperature, String actuators) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);  display.print("SMARTFLOW ACS");
  display.setCursor(0, 12); display.print("ST: "); display.print(state);
  display.setTextSize(2);
  display.setCursor(0, 26); display.print(temperature, 1); display.write(247); display.print("C");
  display.setTextSize(1);
  display.setCursor(0, 52); display.print("OUT: "); display.print(actuators);
  display.display();
}
        

AI Code Disclosure

Generative AI tools were utilized to optimize the core state machine architecture and robust button debouncing control structure.

Prompt used for the main state machine logic:

Act as an embedded systems developer expert in Arduino. Design a non-blocking state machine for an RP2040.

The system controls a water recirculation loop with a pump and a solenoid valve. It must have 4 states:

IDLE (blank screen), TEMPORARY_SCREEN (shows alert if water is already hot), RUNNING (actuators on, stops if temp >= 35°C, button pressed, or 5 min timeout), and a sequential COOL_DOWN_PHASE (turns off pump first for 5s while valve stays open to prevent water hammer, then shuts down everything after another 5s).

Include a robust hardware button debouncing logic that blocks execution only during the release phase.

AI Collaboration & Code Generation Insights

To obtain the Arduino code shared above, I collaborated with AI, specifically using Gemini. However, there is no single, specific prompt used to generate the code. Instead, the process relies on an ongoing, natural dialogue where I explain the logical ideas and specific tasks I want the microcontroller to execute.

Furthermore, with the memory and past activity features enabled, Gemini retains the context of previous project developments and adapts to my workflow preferences. As a result, the AI often anticipates my technical requirements, occasionally proposing relevant code structures even before they are explicitly requested.

Documentation Note: This iterative co-design process ensures that all firmware structures perfectly align with the custom hardware layout and specific routing defined during the PCB design phase.

ORIGINAL FILES

Main enclosure ControllerBox logo Support Cover ControllerBox Cover Main enclosure Arduino Drivers Arduino relays KiCad Controller KiCad Main Board KiCad Probe Enclosure.stl Support.stl