jacuzzi with view

Smart Hot Tub: Jacuzzi Retrofit with EQSP32 IoT Automation

Retrofitting an old jacuzzi or crafting a tailored client solution has never been more straightforward, thanks to the EQSP32 IIoT controller. Addressing the growing demand for personalized spa experiences, this quick reference guides you through the functionalities and components of smart hot tub systems. It also outlines a step-by-step approach for leveraging the EQSP32 wireless industrial IoT controller’s features to surpass client expectations for a modern spa experience, ensuring both satisfaction and sustainability.

Typical hot tub design

A typical hot tub system. Retrofit into a smart hot tub using industrial esp32 plc, the EQSP32.

Outputs

  • Jet pump (1 or 2)
  • Blower pump
  • Filter/Heater pump
  • Heating element
  • Lights
  • Water fill valve (optional)
  • Ozonator (optional)

Sensors/Inputs

  • Water flow
  • Temperature
  • Water level
  • Water pH (optional)

User Interface – Settings and Control

  • Jets (Smartphone or Manual)
  • Blower (Smartphone or Manual)
  • Lights On/Off (Smartphone or Manual)
  • Light color (Smartphone)
  • Desired water temperature (Smartphone)
  • Disable heater (Smartphone)
  • Filter cycle schedule (Smartphone)

User Interface – Monitoring

  • Jets status (Smartphone)
  • Blower status (Smartphone)
  • Water current temperature (Smartphone)
  • Desired water temperature setpoint (Smartphone)
  • Filter status (Smartphone)
  • Heater status (Smartphone)
  • Water pH (Smartphone)

Components and wiring

Thermistor: The thermistor is used for temperature measurement. It is connected to an Analog Input (AIN) pin on the EQSP32 to allow water temperature monitoring.

Flow Switch: This switch is may be connected as a Digital Input (DIN) or as a Switch (SWT), to add debouncing on the readings, to detect whether water is flowing through the system. This is a safety feature to ensure the heater does not turn on without water flow.

Manual Air Buttons: These buttons may be configured as Switch (SWT) input pins on the EQSP32, which are configured with debouncing. They manually control the jets, blower, lights.

Heater: The heater is connected on a DPST relay, and the relay is connected to a pin configured in Relay (RELAY) mode to be energized at 100% and then derate the applied power to a sufficient holding power.

Pumps: There are multiple pumps, each controlled by a relay connected to one of EQSP32 pins and configured in Relay (RELAY) mode.

RGB LEDs: The RGB LEDs are controlled by three outputs which are configured in Power PWM mode (POUT) pins for color mixing and brightness control.

Ground Fault Protection Circuit Breaker: This is a safety device and is not directly controlled by the EQSP32. It’s there to cut off power in case of a fault condition.

System Overview

The EQSP32 controls various aspects of the hot tub by reading sensor inputs and adjusting the outputs accordingly.

Smart hot tub iot automation electrical box. EQSP32 - Industrial ESP32 PLC

Here is the desired behavior of the system:

The system controls lighting, including on/off states and color settings via the app. Jet control allows for multiple configurations: both jets on, only jet one or jet two on, or both off, with app-based management for individual control.

The blower, the jets and lights can be toggled both manually and through the app. The heater’s operation is exclusively app-controlled, automatically heating the water to a user-defined temperature or disabling the heater functionality.

Controlling smart hot tub iot automation using custom mobile application. EQSP32 - Industrial ESP32 PLC

An automatic filter pump function activates based a set schedule from the app or when the heater is enabled, to ensure water flowing through the heating element.

To avoid overloading the single-phase power supply, activating jets or the blower automatically turns off the filter and heater. Additionally, if no water flow is detected, the heater shuts off to prevent damage. The system’s design focuses on straightforward functionality for developers to implement and users to operate, with safety and energy efficiency at the forefront.

Component list

QtyItemDescriptionMfr.Model
1Wireless IIoT Controller16x A/D I/Os IoT PLC controllerErqosEQSP32C
1AC/DC Converter220V to 24V 2.5A AC/DC converter Meanwell
1GFCIGround-fault circuit interrupter Generic
3Air switchesElectrically isolated switches Generic
5RelaysDouble pole single through relays (DPST) Generic
1NTC Thermistor  NTCLE413E2103F106A
1Water flow switch  Generic

EQSP32 Code

The line #include "EQSP32.h" includes the EQSP32 library, providing access to its functions for device control. EQSP32 eqsp32; creates an instance of the EQSP32 class, allowing you to use these functions to interact with the EQSP32 hardware in your application.

#include "EQSP32.h"

EQSP32 eqsp32;

Declaration of setLights(bool state);. This function controls the lights state: true turns lights on, false turns them off.

void setLights(bool state);

Define various constants for the smart jacuzzi system controlled by an EQSP32 microcontroller, categorizing them into physical inputs and outputs, and cloud-based boolean and integer variables:

  • RELAY_HOLD_POWER and RELAY_DERATE_DELAY: Configure relay behavior, setting the hold power to 35% and the time before power reduction to 250 ms.
  • Physical Inputs:
    • Pin assignments for different sensors and switches like temperature (NTC_TIN), limit switch (LIMIT_SW), and controls for jets (JETS_SW), bubbles (BUBBLES_SW), and lights (LIGHTS_SW).
  • Physical Outputs:
    • Defines pins for RGB LEDs (RED, GREEN, BLUE) and relays controlling the bubbles (BUBBLES_RL), jets (JET1_RL, JET2_RL), filter (FILTER_RL), and heater (HEATER_RL).
  • Cloud Bool:
    • Boolean variables for remote control and status checks of jets, bubbles, lights, and the heater, with assigned cloud indices.
  • Cloud Int:
    • Integer variables for remote settings like temperature setpoint (CLOUD_TEMP_SET) and filter cycle time (CLOUD_FILTER_TIME). RGB color intensity values are linked directly to the physical output pins for LEDs.
#define RELAY_HOLD_POWER    350     // 35%
#define RELAY_DERATE_DELAY  250     // 250 ms until dropping power

// Physical Inputs
#define NTC_TIN     1
#define LIMIT_SW    2
#define JETS_SW     3
#define BUBBLES_SW  4
#define LIGHTS_SW   5
// Physical Outputs
#define RED         9
#define GREEN       10
#define BLUE        11
#define BUBBLES_RL  12
#define JET1_RL     13
#define JET2_RL     14
#define FILTER_RL   15
#define HEATER_RL   16

// Cloud Bool
#define CLOUD_JET1  3
#define CLOUD_JET2  4
#define CLOUD_BUBBLES   5
#define CLOUD_LIGHTS    6
#define CLOUD_HEATER    16
// Cloud Int
#define CLOUD_TEMP_SET  1
#define CLOUD_RED       RED
#define CLOUD_GREEN     GREEN
#define CLOUD_BLUE      BLUE
#define CLOUD_FILTER_TIME   15

The following code is the setup function for the smart jacuzzi system. It performs initial configurations for connecting to a database, setting up input sensors and switches, configuring output LEDs, and initializing relay controls for various jacuzzi functions. Here’s a breakdown:

  1. Configuration for Cloud Connection:
    • Sets up connection details to a cloud database with a URL, API key, and a device system ID for the jacuzzi. It also enables a relay sequencer feature for managing relay operations more efficiently.
    • Initializes the EQSP32 with these configurations and enables verbose output for detailed logging.
  2. Serial Communication:
    • Starts serial communication at 115200 baud rate for debugging and monitoring purposes.
  3. Input Initialization:
    • Configures physical input pins for temperature sensing (NTC_TIN) with specific NTC parameters, and for limit switch (LIMIT_SW), jets switch (JETS_SW), lights switch (LIGHTS_SW), and bubbles switch (BUBBLES_SW) as digital inputs with switch (SWT) mode.
  4. Output LEDs Initialization:
    • Sets up RGB LED pins (RED, GREEN, BLUE) in PWM output (POUT) mode for lighting control.
  5. Relays Initialization:
    • Configures relays for jets (JET1_RL, JET2_RL), bubbles (BUBBLES_RL), filter (FILTER_RL), heater (HEATER_RL), and lights (LIGHTS_RL) with specified hold power and derate delay times. This allows for controlled power management to the relays, enhancing safety and efficiency.
  6. PWM Frequency for Outputs:
    • Sets the PWM frequency for POUT modes to 3000 Hz, optimizing the operation of the PWM outputs like the RGB LEDs.

Overall, this setup function meticulously prepares the jacuzzi system for operation, ensuring that all components are correctly initialized for both physical interaction and remote control via cloud connectivity.

void setup()
{
    EQSP32Configs myEQSP32Configs;
    myEQSP32Configs.databaseURL = "My database URL";
    myEQSP32Configs.databaseAPIKey = "My database API key";
    myEQSP32Configs.devSystemID = "ErqosJaccuziV1.0";
    myEQSP32Configs.relaySequencer = true;
   
    eqsp32.begin(myEQSP32Configs, true);        // Verbose enabled
    Serial.begin(115200);

    // Initialize INPUTS
    eqsp32.pinMode(NTC_TIN, TIN);
    eqsp32.configTIN(NTC_TIN, 3435, 10000);         // PN: NTCLE413E2103F106A
    eqsp32.pinMode(LIMIT_SW, SWT);
    eqsp32.pinMode(JETS_SW, SWT);
    eqsp32.pinMode(LIGHTS_SW, SWT);
    eqsp32.pinMode(BUBBLES_SW, SWT);

    // Initialize OUTPUTS
    eqsp32.pinMode(RED, POUT);
    eqsp32.pinMode(GREEN, POUT);
    eqsp32.pinMode(BLUE, POUT);

    // Initialize RELAYS
    eqsp32.pinMode(JET1_RL, RELAY);
    eqsp32.configRELAY(JET1_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);
    eqsp32.pinMode(JET2_RL, RELAY);
    eqsp32.configRELAY(JET2_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);
    eqsp32.pinMode(BUBBLES_RL, RELAY);
    eqsp32.configRELAY(BUBBLES_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);
    eqsp32.pinMode(FILTER_RL, RELAY);
    eqsp32.configRELAY(FILTER_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);
    eqsp32.pinMode(HEATER_RL, RELAY);
    eqsp32.configRELAY(HEATER_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);
    eqsp32.pinMode(LIGHTS_RL, RELAY);
    eqsp32.configRELAY(LIGHTS_RL, RELAY_HOLD_POWER, RELAY_DERATE_DELAY);

    eqsp32.configPOUTFreq(3000);        // 3000 Hz
}

In the main loop() function for our smart jacuzzi system, we see the framework for controlling various aspects like the filter, jets, bubbles, lights, and heater, all integrated with cloud-based user settings. Let’s outline the functional components and their intended operations before we move on to each component’s implementation:

  1. Initialization of Timers and Variables:
    • oneHourTimer and filterTimer are initialized for scheduling tasks. filterTimer starts with a 1-minute setting to avoid being expired at startup.
    • filterOnTime tracks the cumulative filter operation time.
    • lowerTemp is a boolean flag for temperature control.
    • jetState keeps track of the current state of the jets.
  2. Reading from Cloud and Sensors:
    • Retrieves the filter cycle time set by the user (filterSetTime), current water flow status (waterFlow), and falling edge detection on water flow (waterFlowFall) for detecting changes.
    • Reads the current water temperature and the user’s desired temperature setting from the cloud.
    • Checks if the user has requested to turn on the heater (heaterOnRequest).
  3. One Hour Timer Reset:
    • Restarts the oneHourTimer for an interval of one hour. The filter is scheduled to work for a user defined period of time every hour.

The subsequent commented sections will be added and explained later:

  • Heater Control: Based on water temperature vs. desired temperature and heater request status.
  • Jets Control: Handling jet states and cloud-based requests for turning jets on/off.
  • Bubbles Control: Managing the bubbles system based on user inputs or preset conditions.
  • Lights Control: Adjusting lights according to user settings, including on/off states and colors.
  • Filter Operation: Managing the filter based on the timer, user settings, and possibly other conditions like heater operation to maintain water quality.

The delay(10); at the end introduces a short pause to prevent the loop from running too rapidly, allowing time for sensor readings and control commands to be processed effectively.

void loop()
{
    static Timer oneHourTimer;
    static Timer filterTimer(60000);    // Init filter timer with 1 min set time so it is not expired on start up
    static int filterOnTime = 0;
    static bool lowerTemp;
    static int jetState = 0;

    int filterSetTime = eqsp32.readUserInt(CLOUD_FILTER_TIME);
    bool waterFlowFall = eqsp32.readPin(LIMIT_SW, ON_FALLING);
    bool waterFlow = eqsp32.readPin(LIMIT_SW);
    int waterTemperature = eqsp32.readPin(NTC_TIN);
    int desiredTemperature = eqsp32.readUserInt(CLOUD_TEMP_SET);
    bool heaterOnRequest = eqsp32.readUserBool(CLOUD_HEATER);
    
    oneHourTimer.start(60*60*1000);     // One hour is 60 mins * 60 seconds * 1000 mil

// Heater handling
   
// JETS state handlling
   
// JET1 cloud handlling
    
// JET2 cloud handlling
    
// Bubbles handlling
    
// Lights handling
   
// Handle filter
    
    delay(10);
}

The “Heater handling” section of the loop function controls the jacuzzi’s heating system based on water temperature, desired temperature set by the user, and water flow status. It also incorporates safety measures to prevent the heater from operating under certain conditions. Here’s a breakdown of its logic:

  1. Determining Whether to Lower Temperature:
    • If the water temperature is at least 1 degree Celsius lower than the desired temperature, lowerTemp is set to true, indicating there’s a need to increase the water temperature.
    • If there’s a falling edge detected in the water flow (indicating water flow has stopped or decreased significantly) or the water temperature exceeds the desired temperature by more than 1 degree Celsius, lowerTemp is set to false.
  2. Controlling the Heater Based on Conditions:
    • If there is water flow (waterFlow is true, indicating it’s safe to operate the heater) and the user has requested the heater to be on (heaterOnRequest is true), the heater’s operation is then further modulated by the water temperature relative to the desired temperature:
      • If the water temperature is at least 1 degree below the desired temperature, the heater relay (HEATER_RL) is activated (set to 1000, representing a PWM signal for full power).
      • If the water temperature exceeds the desired temperature by more than 1 degree, the heater is turned off (relay set to 0, cutting power to the heater).
    • If there’s no water flow or the heater is not requested to be on, the heater is turned off as a safety measure.

This logic ensures the heater operates only under safe conditions (when there’s adequate water flow) and works towards maintaining the water at the user’s desired temperature, within a +/- 1-degree tolerance. Turning off the heater in the absence of water flow or when not requested helps prevent overheating and saves energy. The use of a hysteresis (the +/- 1-degree offset from the desired temperature) helps avoid frequent toggling of the heater, providing a more stable and efficient temperature control.

// Heater handling
    if (waterTemperature < desiredTemperature - 1)
    {
        lowerTemp = true;
    }
    else if (waterFlowFall || (waterTemperature > desiredTemperature + 1))
    {
        lowerTemp = false;
    }
    if (waterFlow && heaterOnRequest)
    {
        if (waterTemperature < desiredTemperature - 1)
        {
            eqsp32.pinValue(HEATER_RL, 1000);
        }
        else if (waterTemperature > desiredTemperature + 1)
        {
            eqsp32.pinValue(HEATER_RL, 0);
        }
    }
    else
        eqsp32.pinValue(HEATER_RL, 0);

The “JETS state handling” section in the loop function is designed to manage the operation of two jets in a smart jacuzzi system, allowing control both locally via a physical switch and remotely via a smartphone app. The system supports four states for the jets, toggled through a single button press locally and controlled individually via the app. Let’s break down the logic:

Local Control Logic:

  • Jet State Cycling: When the local switch for the jets (JETS_SW) is pressed (detected by an ON_RISING event), the jetState variable is incremented and wrapped around with % 4 to cycle through the four possible states (0-3).
  • State Actions: Depending on jetState, the function controls the jets’ relays (JET1_RL and JET2_RL) and updates their respective states in the cloud (CLOUD_JET1 and CLOUD_JET2):
    • case 0: Both jets are turned off.
    • case 1: Jet 1 is turned on; Jet 2 is off.
    • case 2: Jet 1 is off; Jet 2 is turned on.
    • case 3: Both jets are turned on.

Cloud Control Logic:

  • Jet 1 Cloud Handling: If there’s a change in the cloud variable for Jet 1 (CLOUD_JET1), detected by an ON_TOGGLE event, the state of Jet 1 is toggled based on the cloud variable’s value. If true, Jet 1 is activated; if false, it’s deactivated. The jetState variable is updated using bitwise operations to reflect this change.
  • Jet 2 Cloud Handling: Similarly, if there’s a change in the cloud variable for Jet 2 (CLOUD_JET2), the state of Jet 2 is toggled based on the cloud variable’s current value. The jetState variable is again updated using bitwise operations to accurately represent the current state.

Bitwise Operations for State Management:

  • Setting State: The bitwise OR (|=) operation is used to set the appropriate bit in jetState when a jet is activated via the cloud.
  • Clearing State: The bitwise AND (&=) operation with inverted bits is used to clear the appropriate bit in jetState when a jet is deactivated.
    • Note: The clearing operations seem to be intended as jetState &= ~0b01; for Jet 1 and jetState &= ~0b10; for Jet 2 to correctly clear the bits.

This sophisticated handling allows for flexible and precise control over the jets’ operation, accommodating user preferences both locally and remotely. The system ensures that both control interfaces can work in harmony, with local changes reflected in the cloud and vice versa, providing a seamless user experience.

// JETS state handlling
    if (eqsp32.readPin(JETS_SW, ON_RISING))     // When switch is pressed
    {
        jetState++;
        jetState %= 4;
        switch(jetState) {
            case 0:
                eqsp32.pinValue(JET1_RL, 0);
                eqsp32.pinValue(JET2_RL, 0);
                eqsp32.writeUserBool(CLOUD_JET1, false);        // Update value
                eqsp32.writeUserBool(CLOUD_JET2, false);        // Update value
                break;
            case 1:
                eqsp32.pinValue(JET1_RL, 1000);
                eqsp32.pinValue(JET2_RL, 0);
                eqsp32.writeUserBool(CLOUD_JET1, true);     // Update value
                eqsp32.writeUserBool(CLOUD_JET2, false);        // Update value
                break;
            case 2:
                eqsp32.pinValue(JET1_RL, 0);
                eqsp32.pinValue(JET2_RL, 1000);
                eqsp32.writeUserBool(CLOUD_JET1, false);        // Update value
                eqsp32.writeUserBool(CLOUD_JET2, true);     // Update value
                break;
            case 3:
                eqsp32.pinValue(JET1_RL, 1000);
                eqsp32.pinValue(JET2_RL, 1000);
                eqsp32.writeUserBool(CLOUD_JET1, true);     // Update value
                eqsp32.writeUserBool(CLOUD_JET2, true);     // Update value
                break;
        }
    }
// JET1 cloud handlling
    if (eqsp32.readUserBool(CLOUD_JET1, ON_TOGGLE)) // If user var changes
    {
        if (eqsp32.readUserBool(CLOUD_JET1))        // Request state based on user var value
        {
            eqsp32.pinValue(JET1_RL, 1000);
            jetState |= 0b01;
        }
        else
        {
            eqsp32.pinValue(JET1_RL, 0);
            jetState &= 0b01;
        }
    }
// JET2 cloud handlling
    if (eqsp32.readUserBool(CLOUD_JET2, ON_TOGGLE)) // If user var changes
    {
        if (eqsp32.readUserBool(CLOUD_JET2))        // Request state based on user var value
        {
            eqsp32.pinValue(JET2_RL, 1000);
            jetState |= 0b10;
        }
        else
        {
            eqsp32.pinValue(JET2_RL, 0);
            jetState &= 0b10;
        }
    }

The “Bubbles handling” section in the loop function is designed to control a single pump for bubbles in a smart jacuzzi system, allowing for both local (physical switch) and remote (smartphone app) control. Here’s the simplified logic for managing the bubbles:

Local Control:

  • Toggle Operation: When the physical switch for the bubbles (BUBBLES_SW) is pressed (detected by an ON_RISING event), the function checks the current state of the bubbles relay (BUBBLES_RL).
    • If the relay is active (not equal to 0), it’s turned off (eqsp32.pinValue(BUBBLES_RL, 0)), and the cloud variable (CLOUD_BUBBLES) is updated to false, indicating the bubbles are now off.
    • If the relay is inactive (equal to 0), it’s turned on (eqsp32.pinValue(BUBBLES_RL, 1000)), and the cloud variable is updated to true, indicating the bubbles are now on.

Cloud Control:

  • Cloud Variable Toggle: If there’s a change in the cloud variable for the bubbles (CLOUD_BUBBLES), detected by an ON_TOGGLE event, the function toggles the state of the bubbles based on the current value of the cloud variable.
    • If CLOUD_BUBBLES is true, the bubbles relay is activated (eqsp32.pinValue(BUBBLES_RL, 1000)), turning the bubbles on.
    • If CLOUD_BUBBLES is false, the relay is deactivated (eqsp32.pinValue(BUBBLES_RL, 0)), turning the bubbles off.

This logic ensures that the bubbles feature can be easily controlled both by the user physically at the jacuzzi and remotely through a smartphone app. The use of a cloud variable allows for the bubbles’ current state to be synchronized across both control interfaces, providing a consistent user experience.

// Bubbles handlling
    if (eqsp32.readPin(BUBBLES_SW, ON_RISING))      // When switch is pressed
    {
        if (eqsp32.readPin(BUBBLES_RL) != 0)        // Request change on JET2 state
        {
            eqsp32.pinValue(BUBBLES_RL, 0);
            eqsp32.writeUserBool(CLOUD_BUBBLES, false);     // Update value
        }
        else
        {
            eqsp32.pinValue(BUBBLES_RL, 1000);
            eqsp32.writeUserBool(CLOUD_BUBBLES, true);      // Update value
        }
    }
    if (eqsp32.readUserBool(CLOUD_BUBBLES, ON_TOGGLE))  // If user var changes
    {
        if (eqsp32.readUserBool(CLOUD_BUBBLES))     // Request state based on user var value
            eqsp32.pinValue(BUBBLES_RL, 1000);
        else
            eqsp32.pinValue(BUBBLES_RL, 0);
    }

The “Lights handling” section controls the jacuzzi’s lighting system, allowing for both local control via a physical switch and remote control via a smartphone app. Here’s the breakdown:

Local Control:

  • Toggle Operation: When the lights switch (LIGHTS_SW) is pressed (detected by an ON_RISING event), the function checks the current state of the lights relay (LIGHTS_RL).
    • If the relay is active (not equal to 0, meaning the lights are on), it calls setLights(false) to turn the lights off and updates the cloud variable (CLOUD_LIGHTS) to false.
    • If the relay is inactive (equal to 0, meaning the lights are off), it calls setLights(true) to turn the lights on and updates the cloud variable to true.

Cloud Control:

  • Cloud Variable Toggle: If there’s a change in the cloud variable for the lights (CLOUD_LIGHTS), detected by an ON_TOGGLE event, the setLights function is called with the current value of CLOUD_LIGHTS as its argument.
    • This effectively toggles the state of the lights based on the cloud setting: if CLOUD_LIGHTS is true, the lights are turned on; if false, the lights are turned off.

The setLights function, which is defined at the end of the code, abstracts the operation of setting the lights’ relay to the specified state and handles additional functionality such as adjusting brightness or changing colors. This design pattern decouples the logic for changing the lights’ state from the main control flow, making the code more modular and easier to maintain.

// Lights handling
    if (eqsp32.readPin(LIGHTS_SW, ON_RISING))       // When switch is pressed
    {
        if (eqsp32.readPin(LIGHTS_RL) != 0)     // Request change on JET2 state
        {
            setLights(false);
            eqsp32.writeUserBool(CLOUD_LIGHTS, false);      // Update value
        }
        else
        {
            setLights(true);
            eqsp32.writeUserBool(CLOUD_LIGHTS, true);       // Update value
        }
    }
    if (eqsp32.readUserBool(CLOUD_LIGHTS, ON_TOGGLE))
    {
        setLights(eqsp32.readUserBool(CLOUD_LIGHTS));
    }

The “Handle filter” section outlines the logic for controlling the filtration system in the jacuzzi.

Activation Logic:

  • Initial Check: The filter is considered for activation if there’s either a heater request (heaterOnRequest) or a non zero user-defined filtering duration per hour value filterSetTime. Setting filterSetTime to 0 effectively disables automatic filtering, as the system interprets this as no need for the filter to run.
  • Deactivation During Jet or Bubble Use: If any jets or the bubble system are active, the filter is turned off, and the filter’s timer is paused. This approach aims to avoid overloading the single-phase power supply.

Filter Operation Logic:

  • Conditions for Deactivation: The filter turns off if the filter timer has expired filterTimer.isExpired() and either the heater is not requested (!heaterOnRequest) or the water temperature is not low enough for the system to enable heating (!lowerTemp).
  • Activation and Timer Reset: Conversely, if the timer hasn’t expired or the heater is on and the water is not yet at the desired temperature, the filter is activated, and the filter timer is updated to the duration specified by filterSetTime (converted to milliseconds for the timer). When filterTimer.start(filterSetTime * 60 * 1000) is called with the same value filterSetTime , the timer is not reset but resumed.
  • Handling Inactivity: If neither heating nor a specific filter operation time is set, the filter is turned off, and its timer is paused, ensuring the filter doesn’t run unnecessarily.

Periodic Timer Reset:

  • When hourly timer (oneHourTimer) expires, the filter timer is stopped, and the hourly timer is reset. This action initializes the next hourly schedule for filtering.

This logic ensures that the jacuzzi’s filtration system is managed efficiently, aligning with user settings and the operational state of other jacuzzi features to optimize performance and energy use.

// Handle filter
    if (heaterOnRequest || filterSetTime)       // If filter time is set to 0 do not turn on the filter, cannot set time 0 to timer
    {
        if (eqsp32.readPin(JET1_RL) || eqsp32.readPin(JET2_RL) || eqsp32.readPin(BUBBLES_RL))
        {
            eqsp32.pinValue(FILTER_RL, 0);
            filterTimer.pause();
        }
        else
        {
            if (filterTimer.isExpired() && (!heaterOnRequest || !lowerTemp))
                eqsp32.pinValue(FILTER_RL, 0);
            else
            { // Turn on if timer not expired or (if heaterOn and low water temp)
                eqsp32.pinValue(FILTER_RL, 1000);
                filterTimer.start(filterSetTime * 60 * 1000);   // set time is in minutes
            }
        }
    }
    else
    {
        eqsp32.pinValue(FILTER_RL, 0);
        filterTimer.pause();
    }
    if (oneHourTimer.isExpired())
    {
        filterTimer.stop();
        oneHourTimer.reset();
    }

The setLights(bool state) function controls the lighting system in the jacuzzi, including the main lights relay (which is not included in this installation but it would control the main power to all lights) and individual RGB LED lights for color customization, based on user input from the cloud:

  • Turning Lights On (state is true):
    • The main lights relay (LIGHTS_RL) is activated with a value of 1000, likely representing full power or an on state. (Not populated in this installation)
    • The RGB values for red, green, and blue LEDs are read from their respective cloud variables (CLOUD_RED, CLOUD_GREEN, CLOUD_BLUE), which are expected to be in the range of 0 to 255 (standard for RGB color values).
    • These values are then mapped to the range 0 to 1000, corresponding to the PWM signal range used to control the intensity of the LEDs, allowing for color customization. This mapping allows the intensity of each color to be adjusted according to user preferences set via a smartphone app or other cloud interfaces.
  • Turning Lights Off (state is false):
    • The main lights relay and all RGB LEDs are set to 0, effectively turning off the lights and setting their intensity to zero.

This function thus enables dynamic control of the jacuzzi’s lighting based on user settings, enhancing the user experience by allowing for light intensity and color customization remotely.

void setLights(bool state)
{
    if (state)
    {
        eqsp32.pinValue(LIGHTS_RL, 1000);
        eqsp32.pinValue(RED, map(eqsp32.readUserInt(CLOUD_RED), 0, 255, 0, 1000));
        eqsp32.pinValue(GREEN, map(eqsp32.readUserInt(CLOUD_GREEN), 0, 255, 0, 1000));
        eqsp32.pinValue(BLUE, map(eqsp32.readUserInt(CLOUD_BLUE), 0, 255, 0, 1000));
    }
    else
    {
        eqsp32.pinValue(LIGHTS_RL, 0);
        eqsp32.pinValue(RED, 0);
        eqsp32.pinValue(GREEN, 0);
        eqsp32.pinValue(BLUE, 0);
    }
}

Developing the User Application

Erqos provides a no-code, drag-and-drop app template that significantly simplifies the creation of a user interface for the EQSP32 controller.

  1. User-Friendly IoT Template: The provided template comes ready with a set of pre-implemented features for user management, including login, signup, and password reset functionality, as well as IoT setup—all accessible without any need for adjustment from the custom app developer.
  2. Developers’ No-Code Template: In this section, developers can customize their IoT application interface through a no-code, drag-and-drop and duplication approach:
    • ADIO1 Group: A module for monitoring Analog/Digital Input/Output pins via the cloud.
    • Int1 Group: Provides a visual interface for users to adjust integer variables, enabling interaction with the EQSP32’s cloud storage.
    • Bool1 Group: A widget that connects with Boolean variables, allowing for simple on/off switch functionality. This widget acts as a virtual switch for the app via the cloud.
  3. Scalable and Modular Design: Developers can scale their application by copying these building blocks as many times as needed. For instance, if a device requires four virtual switches, the bool1 group can be replicated three additional times. Each initial block is mapped to the first of 16 available elements and by duplicating it, it will be automatically mapped to the next element. For example, if bool1 group is duplicated, it will automatically create bool2 group, mapped to virtual switch 2.
  4. Streamlined Customization: Users have the flexibility to hide any unused elements, ensuring the app interface remains uncluttered. This feature adds to the no-code convenience, allowing for a cleaner and more focused user experience.

Using this approach, developers can create complex, fully-functional IoT applications through a completely no-code, drag-and-drop interface.

Using the Erqos app template, the smart hot tub control interface application was created :

  1. Template Utilization: The ‘DevelopersTemplate’ template screen was used for the base of the jacuzzi control interface.
  2. Cloning Groups for Features: Template blocks for ADIO, Boolean, and Integer variables were duplicated and readjusted to achieve the desired design for the smart jacuzzi’s features like jets and lights.
  3. Concealing Unused Components: To streamline the app’s look, all unused components were hidden from the user interface.
  4. Setting Control Actions: Each interactive element was set up to adjust the corresponding feature on the EQSP32 controller. For example JET1 was mapped on virtual switch 3 (bool3) in the code, so Group_Bool3 was also used in the application template.

Testing the Application

We are here to help

Contact us anytime for support and consulting for developing your dream IIoT system.

If you’re looking to create a custom automation solution and you are not sure how, share your application ideas with us, and we can make it for you.

Add a Comment

Your email address will not be published. Required fields are marked *