Tutorial: Building a pH Control Flow
This tutorial walks you through a real-world automation flow that maintains pH levels in a nutrient reservoir. You’ll learn how blocks work together to read sensors, make decisions, dose chemicals, and loop until the target is reached.
What This Flow Does
The flow automatically adjusts pH by:
- Reading the current pH level
- Comparing it to the target
- If pH is too low → dosing pH UP solution in a loop
- If pH is too high → dosing pH DOWN solution in a loop
- If pH is already at target → finishing immediately
Variables
Before placing any blocks, define the variables the flow will use. Open the Variable Manager from the toolbar.
Local Variable (temporary)
| ID | Name | Unit | Purpose |
|---|---|---|---|
var_1 | pH Now | pH | Stores the current sensor reading. Overwritten each time the sensor is read. |
Global Variables (configurable by Programs)
| ID | Name | Unit | Default | Tolerance | Purpose |
|---|---|---|---|---|---|
global_5 | pH Need | pH | 6.2 | ± 0.2 | The target pH setpoint. A Program can override this per schedule. |
global_2 | pH Dose | count | 1 | — | How many doses to dispense per loop cycle. |
global_4 | PumpMixTime | sec | 85 | — | How long the mixing pump runs after each dose to distribute the solution. |
Why Global? Because a Program might need different pH targets for day/night, or different dose amounts for different growth stages. Global variables make the flow reusable without editing it.
Step-by-Step Build
Step 1: Read the Current pH
Block: Sensor Read → name it “Sensor pH 01”
| Setting | Value |
|---|---|
| Device | pH SIM (your pH sensor) |
| Reading Mode | ph:multi_point |
| Target Variable | pH Now (var_1) |
What happens: The sensor reads the current pH and stores it in pH Now.
Step 2: Is pH Already at Target?
Block: IF (Condition) → name it “Check pH = Target”
| Setting | Value |
|---|---|
| Variable | pH Now |
| Operator | == (equal) |
| Value | {{pH Need}} (global_5) |
Connections:
- TRUE → End block (pH is perfect, nothing to do)
- FALSE → next IF block (need to determine direction)
What happens: If the current pH equals the target (within tolerance), the flow ends. Otherwise, we need to figure out which direction to adjust.
Step 3: Which Direction to Adjust?
Block: IF (Condition) → name it “Check pH < Target”
| Setting | Value |
|---|---|
| Variable | pH Now |
| Operator | < (less than) |
| Value | {{pH Need}} (global_5) |
Connections:
- TRUE → pH is too low → go to the pH UP loop (Step 4a)
- FALSE → pH is too high → go to the pH DOWN loop (Step 4b)
Step 4a: pH UP Loop (pH is too low)
Block: Loop → name it “Loop pH+“
| Setting | Value |
|---|---|
| Mode | COUNT |
| Iterations | 5 (maximum attempts) |
| Interval | 2 sec |
| Stop Condition | pH Now < {{pH Need}} (keep looping while pH is below target) |
Connections:
- LOOP (body, purple) → Dose pH UP (Step 5a)
- DONE (exit, green) → End block
Step 5a: Dose pH UP
Block: Actuator Set → name it “Pump pH+“
| Setting | Value |
|---|---|
| Device | Pump pH+ SIM |
| Action | DOSE |
| Amount | {{pH Dose}} (global_2) — 1 dose |
| Revert on Stop | ✅ Enabled |
Step 6a: Mix the Solution
Block: Actuator Set → name it “Pump Mix pH+“
| Setting | Value |
|---|---|
| Device | Pump Mix SIM |
| Action | PULSE_ON |
| Duration | {{PumpMixTime}} (global_4) — 85 seconds |
| Revert on Stop | ✅ Enabled |
Step 7a: Re-read pH
Block: Sensor Read → name it “Sensor pH+“
| Setting | Value |
|---|---|
| Device | pH SIM (same sensor) |
| Target Variable | pH Now (var_1) — overwrites the previous reading |
Step 8a: Close the Loop
Block: Flow Control → LOOP_BACK → targets “Loop pH+“
Step 4b: pH DOWN Loop (pH is too high)
This is a mirror of the pH UP path, with these differences:
| Block | Name | Difference |
|---|---|---|
| Loop | ”Loop pH-” | Stop condition: pH Now > {{pH Need}} |
| Actuator | ”Pump pH-” | Uses the pH DOWN pump device |
| Actuator | ”Pump Mix pH-” | Same mixing pump, same duration |
| Sensor | ”Sensor pH-” | Same sensor, writes to same pH Now variable |
| Flow Control | LOOP_BACK | Targets “Loop pH-” |
Complete Flow Diagram
START
↓
[Sensor pH 01] → pH Now = current reading
↓
IF: pH Now == pH Need?
├── TRUE → END (pH is perfect)
└── FALSE ↓
IF: pH Now < pH Need?
├── TRUE (pH too low) → LOOP pH+ (5x) → Dose → Mix → Re-read → LOOP BACK
└── FALSE (pH too high) → LOOP pH- (5x) → Dose → Mix → Re-read → LOOP BACK
Key Design Patterns in This Flow
1. Read → Decide → Act → Re-read → Loop
This is the fundamental automation pattern. Every correction cycle ends with a fresh sensor reading so the loop condition uses up-to-date data.
2. Two-Direction Branching
A single IF block determines the correction direction. This avoids running the wrong pump.
3. Mix Before Re-read
The mixing pump runs for 85 seconds between dosing and re-reading. Without this, the sensor would read the old (unmixed) water and overdose.
4. Safety Limits
- Max 5 iterations per loop prevents infinite dosing if the sensor is faulty
- Revert on Stop ensures pumps turn off if the flow is stopped mid-operation
5. Global Variables for Reusability
By using globals for the target pH, dose amount, and mix time, you can run the same flow with different parameters at different times of day.
Testing This Flow
- Click ▶ Run in the editor toolbar
- In the Setup Dialog, set values for global variables
- Click Load Debugger
- Click ▶ Run Flow to start
- Watch in the console: sensor reads → IF decides direction → loop runs → flow exits
Common Modifications
| Want to… | Change |
|---|---|
| Adjust faster | Reduce PumpMixTime or increase pH Dose |
| Adjust safer | Reduce pH Dose and increase loop Iterations |
| Add tolerance | Set pH Need tolerance to ± 0.3 |
| Add notifications | Enable notifications on the Loop block |
| Log progress | Add a Log block after each sensor re-read |