BREATHING PARTITION

Breathing Partition – “Stomata” tells the story of an interior wall that learns to breathe. Interior walls are usually static: they separate rooms but ignore the air on either side. Our aim was to develop a partition that responds to its environment, allowing air to move when needed and remaining still when it is not.
Inspired by the stomata of leaves, an iris mechanism, driven by an Arduino and a humidity sensor, opens and closes in a rhythm that responds to the moisture in the air. Challenging the static nature of conventional partitions, it seeks to bring two interior spaces towards humidity equilibrium through responsive air exchange, making an otherwise invisible process visible through the movement of a fabric membrane.
CONCEPT | Learning from Leaves
Nature constantly moves towards greater entropy, towards equilibrium. Leaves are covered with stomata, tiny pores framed by two guard cells. As the guard cells swell, the pore opens to enable gas exchange; as they relax, it closes to conserve water. Beneath the surface, a network of veins and veinlets provides structural support and pathways for water. We translated this biological mechanism into an adaptable architectural element: the stoma became the moving mechanism, while the veinlets informed the supporting structure.

An iris mechanism became our interpretation of a stoma: like a pore, it opens from the centre through a single rotational movement. A single stepper motor (28BYJ-48) drives five blades through a ring gear, supported by a Voronoi-inspired network of vein-like structures.
MECHANISM | System overview & Fabrication
At the core of the system is an Arduino-controlled mechanism. A humidity sensor determines its breathing rhythm, adjusting the movement according to environmental conditions: gentle, slow movements at normal humidity levels; deeper and faster movements as humidity increases; and a gradual return to a calm rhythm once the balance is restored. Additionally, a fabric membrane attached to the moving blades makes each breathing cycle visible, transforming an otherwise invisible process of air exchange into a tangible, kinetic expression.

To work like a pore, the mechanism had to open from the centre through one coordinated movement. In our iris, five blades rotate around fixed pivots, driven by a ring gear and a pinion on a stepper motor. Designed parametrically in Grasshopper, a 14-tooth pinion drives a 57-tooth internal ring (about 4:1) for smooth motion and more torque. In theory, full opening needs 126° at the pinion; in the prototype, tolerances meant 108° was enough.
The veinlet frame
The frame is a Voronoi-based network that recalls the veins of a leaf. For a living system we wanted as little deliberate human intervention as possible: the geometry emerges from random seed values and iterative optimisation. We did not design the form, but the rules that generate it.

The toothed ring and blades were parametrically designed in Grasshopper. A 14-tooth pinion drives a 57-tooth internal ring gear, with an approximate 4:1 gear ratio, providing smooth movement, increased torque and a compact assembly.
Theoretically, a full opening requires a 31° rotation of the ring gear, corresponding to approximately 126° of pinion rotation (31 × 57 ÷ 14). In the prototype, however, due to mechanical tolerances, 108° proved sufficient, so we adopted the measured value.
With limited opportunities for 3D printing and testing, calibrating these tolerances and bridging the gap between digital precision and physical material behaviour became our greatest challenge.



Code: humidity becomes breath
A DHT20 sensor sends readings to an Arduino UNO every half second. Rather than a simple open/closed switch, the code turns each reading into a breathing rhythm. Below 50% humidity the iris opens up to 50° with slow, gentle breaths; as humidity rises it breathes deeper and faster, reaching the full 108° at 100%. When humidity drops, the rhythm gradually calms down.



All parts are 3D-printed in white PLA and wired to the Arduino through a breadboard. A fabric membrane tied to the blade tips gathers and unfolds with every movement. Thanks to repeated testing and adjustments in Grasshopper, the prototype performed almost exactly as designed.




THE CODE |
humidity_iris.ino — DHT20 humidity → 28BYJ-48 stepper (iris)
1 #include <Wire.h> // Talk to the DHT20 directly over I2C (no DHT library needed)
23
/////const/////
4 const int PINS[4] = {8, 9, 10, 11}; // IN1, IN2, IN3, IN4
5 const int SEQ[8][4] = {
6 {1, 0, 0, 0},
7 {1, 1, 0, 0},
8 {0, 1, 0, 0},
9 {0, 1, 1, 0},
10 {0, 0, 1, 0},
11 {0, 0, 1, 1},
12 {0, 0, 0, 1},
13 {1, 0, 0, 1}
14 };// Half-step signal pattern (standard for 28BYJ-48)
15 const long STEPS_PER_REV = 4096; // Steps per output revolution (half-step)
16 const int OPEN_DEG = 108; // Angle at fully open (measure and adjust)
17 const int MIN_OPEN_DEG = 50; // Max opening angle when humidity is low
18
19 const int HMDT_LOW = 50; // At or below this humidity → slowest, smallest opening
20 const int HMDT_HIGH = 100; // At or above this humidity → fastest, largest opening
21 const long SLOW_US = 10000; // Wait per step when slowest (10 ms)
22 const long FAST_US = 1000; // Wait per step when fastest (1 ms)
23
24 const int DHT20_ADDR = 0x38; // I2C address of the DHT20
25 const unsigned long READ_INTERVAL = 500; // How often to measure (ms)
26 const unsigned long MEASURE_TIME = 80; // Time the sensor needs to measure (ms)
27
28 /////variables/////
29 int stepIndex = 0; // Current row in the SEQ table (0–7)
30 long pos = 0; // Current position in steps. 0 = fully closed
31 int dir = 1; // 1 = opening, -1 = closing
32 long stepDelayUs = SLOW_US; // Current wait per step
33
34 int openDeg = MIN_OPEN_DEG; // Current opening limit (deg)
35 long openSteps = MIN_OPEN_DEG / 360.0 * STEPS_PER_REV; // Current opening limit (steps)
36
37 float temp = 0;
38 float hmdt = 0;
39 bool measuring = false; // Is a measurement in progress?
40 unsigned long measureStart = 0; // When the measurement was requested
41 unsigned long lastRead = 0; // When the last measurement started
42
43 /////setup/////
44 void setup() {
45 Serial.begin(9600);
46 for (int i = 0; i < 4; i++){
47 pinMode(PINS[i], OUTPUT);
48 }
49 Wire.begin();
50 delay(100); // Wait for the DHT20 to power up
51 // Close the iris fully by hand before powering on (this position becomes pos = 0)
52 }
53
54 /////loop/////
55 void loop() {
56
57 // —- 1. Every 0.5 s, ask the sensor to measure (don’t wait) —-
58 if (!measuring && millis() – lastRead >= READ_INTERVAL) {
59 lastRead = millis();
60 startMeasure();
61 measuring = true;
62 measureStart = millis();
63 }
64
65 // —- 2. After 80 ms, read the result, update speed & opening, and print —-
66 if (measuring && millis() – measureStart >= MEASURE_TIME) {
67 measuring = false;
68
69 if (readMeasure()) {
70 int hInt = (int)hmdt; // map() only works with integers
71 int hClamped = constrain(hInt, HMDT_LOW, HMDT_HIGH); // Keep humidity within 50–100
72 stepDelayUs = map(hClamped, HMDT_LOW, HMDT_HIGH, SLOW_US, FAST_US); // Higher humidity → shorter wait → faster
73
74 // Higher humidity → wider opening (50–108 deg)
75 openDeg = map(hClamped, HMDT_LOW, HMDT_HIGH, MIN_OPEN_DEG, OPEN_DEG);
76 openSteps = openDeg / 360.0 * STEPS_PER_REV;
77
78 // ===== Serial Monitor output (text) =====
79 Serial.print(“Humidity: “);
80 Serial.print(hmdt);
81 Serial.print(” %, step delay: “);
82 Serial.print(stepDelayUs);
83 Serial.print(” us, open limit: “);
84 Serial.print(openDeg);
85 Serial.print(” deg, angle: “);
86 Serial.print(currentAngle(), 1); // 1 decimal place
87 Serial.print(” deg (“);
88 Serial.print(currentAngle() / OPEN_DEG * 100.0, 0);
89 Serial.println(” % open)”);
90
91 /*
92 // ===== Serial Plotter output (graph) =====
93 // To use: comment out the Serial Monitor output above and remove the comment marks around this block
94 Serial.print(“Min:0,Max:110,”); // Fixed lines to keep the vertical range at 0–110
95 Serial.print(“Humidity:”);
96 Serial.print(hmdt);
97 Serial.print(“,OpenLimit:”);
98 Serial.print(openDeg);
99 Serial.print(“,Angle:”);
100 Serial.println(currentAngle(), 1); // println = end of one set of data
101 */
102
103 } else {
104 // Serial Monitor: show the error
105 Serial.print(“sensor error, angle: “);
106 Serial.print(currentAngle(), 1);
107 Serial.println(” deg”);
108 // (For the Serial Plotter, comment out the 3 lines above so the graph doesn’t break)
109 }
110 }
111
112 // —- 3. Move the motor one step —-
113 oneStep(dir);
114 pos += dir;
115
116 // —- 4. Reverse direction at either end —-
117 if (pos >= openSteps) {
118 dir = -1; // Reached the opening limit → start closing
119 } else if (pos <= 0) {
120 dir = 1; // Fully closed → start opening
121 }
122
123 delayMicroseconds(stepDelayUs);
124 }
125
126 /////functions/////
127
128 // Ask the DHT20 to start a measurement
129 void startMeasure() {
130 Wire.beginTransmission(DHT20_ADDR);
131 Wire.write(0xAC);
132 Wire.write(0x33);
133 Wire.write(0x00);
134 Wire.endTransmission();
135 }
136
137 // Read the result from the DHT20 into temp and hmdt (returns true on success)
138 bool readMeasure() {
139 Wire.requestFrom(DHT20_ADDR, 7);
140 if (Wire.available() < 7) return false;
141
142 uint8_t b[7];
143 for (int i = 0; i < 7; i++) b[i] = Wire.read();
144 if (b[0] & 0x80) return false; // Sensor was still busy measuring
145
146 // Extract the 20-bit raw values (formula from the DHT20 datasheet)
147 uint32_t rawH = ((uint32_t)b[1] << 12) | ((uint32_t)b[2] << 4) | (b[3] >> 4);
148 uint32_t rawT = ((uint32_t)(b[3] & 0x0F) << 16) | ((uint32_t)b[4] << 8) | b[5];
149
150 hmdt = rawH / 1048576.0 * 100.0; // Humidity in %
151 temp = rawT / 1048576.0 * 200.0 – 50.0; // Temperature in °C
152 return true;
153 }
154
155 // Advance the motor one half-step in direction d (1 or -1)
156 void oneStep(int d) {
157 stepIndex = (stepIndex + d + 8) % 8;
158 for (int i = 0; i < 4; i++){
159 digitalWrite(PINS[i], SEQ[stepIndex][i]);
160 }
161 }
162
163 // Current motor angle in degrees, from the step count (0 = fully closed)
164 float currentAngle() {
165 return pos * 360.0 / STEPS_PER_REV;
166 }
EPILOGUE | Why breathing?
Living organisms can be understood as complex systems, continuously interacting with and adapting to their environment. Breathing is one of the fundamental processes through which this relationship is maintained. If responsiveness to the surrounding environment is what gives a system its sense of being alive, then an architectural element, such as a wall, can embody this principle too.
Next steps: from one stoma to a partition system
Looking ahead, the partition could evolve into a larger architectural element composed of multiple stomata, each functioning as an individual iris module within the veinlet network. Each module could respond to its own humidity sensor, allowing the partition to breathe selectively, opening where humidity is high and remaining at rest where conditions are balanced. A felt layer integrated within the assembly could act as a moisture-retaining medium, supporting the veinlet network in a manner analogous to the water-regulating tissues of a leaf. Additionally, we aim to explore coating the fabric with titanium dioxide, a photocatalyst, so that the partition could contribute to purifying the air passing through it.


