Smart homes open windows by themselves when it gets too warm. In this lesson a servo opens and closes a printed model window, and Arduino Cloud is its remote control. A slider sets how far it opens, from closed to wide open. Switch on auto mode and the ESP32 decides for itself: the DHT11 opens the window when the room gets warmer than the temperature you choose, and the light sensor closes it when it gets dark.
What you'll learn
- How to control a servo from a cloud slider
- How to move a servo smoothly instead of jumping
- How to combine manual and automatic modes
- How the device can change a cloud variable itself, so the dashboard stays right
What you need
- ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
- Servo motor SG90 with horns and screws (ESP32 IoT Starter Kit)
- DHT11 temperature and humidity sensor (ESP32 IoT Starter Kit)
- Photoresistor module (45-in-1 Sensor Kit)
- A 2.4 GHz Wi-Fi network
- The printed parts from the 3D printing box below (model window), or the cardboard version described there
- Breadboard and jumper wires (ESP32 IoT Starter Kit)
Wiring

| From | To |
|---|---|
| Servo signal (orange) | ESP32 GPIO 18 |
| Servo power (red) and GND (brown) | 5V and GND |
| DHT11 S (data) | ESP32 GPIO 27 |
| DHT11 + and − | 3V3 and GND |
| Photoresistor module S | ESP32 GPIO 34 |
| Photoresistor module + and − | 3V3 and GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
Set up Arduino Cloud
In Arduino Cloud, open Things, create a new Thing called Smart Window, link it to your ESP32 device and fill in its network settings. Then add these variables. Lesson 1 of this path shows each step with pictures in words.
| Variable | Type | Permission | Update |
|---|---|---|---|
| opening | Integer number (0–100 %) | Read & Write | On change |
| autoMode | Boolean | Read & Write | On change |
| warmLimit | Integer number | Read & Write | On change |
| temperature | Temperature sensor (°C) | Read only | Every 30 s |
Open Dashboards, create a dashboard and add a Slider from 0 to 100 (opening), a Switch (autoMode), a Slider from 18 to 30 (warmLimit) and a Gauge (temperature), each linked to its variable. The same dashboard appears in the free Arduino IoT Remote app on your phone.
The sketch
Arduino Cloud writes the sketch's first version for you, with a thingProperties.h tab that lists your variables and a Secret tab with your Wi-Fi details and device key. In the Thing, open the Sketch tab, click Open full editor, replace the main sketch with the code below and upload. Leave the other tabs as they are.
Add the DHT sensor library by Adafruit and ESP32Servo by Kevin Harrington under Libraries. Find your window's two angles: CLOSED_ANGLE and OPEN_ANGLE.
#include "thingProperties.h"
#include <DHT.h>
#include <ESP32Servo.h>
const int SERVO_PIN = 18, DHT_PIN = 27, LIGHT_PIN = 34;
const int CLOSED_ANGLE = 10, OPEN_ANGLE = 110;
const int DARK = 800; // light reading below this counts as night
Servo window;
DHT dht(DHT_PIN, DHT11);
float angleNow = CLOSED_ANGLE;
unsigned long lastStep = 0, lastCheck = 0;
void setup() {
Serial.begin(115200);
delay(1500);
dht.begin();
window.attach(SERVO_PIN);
window.write(CLOSED_ANGLE);
warmLimit = 24;
initProperties();
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
setDebugMessageLevel(2); // connection messages in the Serial Monitor
ArduinoCloud.printDebugInfo();
}
// Move one degree every 15 ms towards the target, so the window doesn't slam
void moveSmoothly() {
float target = map(opening, 0, 100, CLOSED_ANGLE, OPEN_ANGLE);
if (millis() - lastStep < 15 || fabs(target - angleNow) < 1) return;
lastStep = millis();
angleNow += target > angleNow ? 1 : -1;
window.write((int)angleNow);
}
void automatic() {
float t = dht.readTemperature();
if (!isnan(t)) temperature = t;
bool night = analogRead(LIGHT_PIN) < DARK;
if (!autoMode) return;
int wanted = opening;
if (night) wanted = 0; // close at night
else if (temperature > warmLimit + 0.5) wanted = 100; // too warm: open
else if (temperature < warmLimit - 0.5) wanted = 0; // cool enough: close
if (wanted != opening) {
opening = wanted; // the dashboard slider moves too
Serial.printf("Auto: %d %%\n", wanted);
}
}
void loop() {
ArduinoCloud.update();
moveSmoothly();
if (millis() - lastCheck >= 10000) {
lastCheck = millis();
automatic();
}
}
void onOpeningChange() {
Serial.printf("Opening: %d %%\n", (int)opening);
}
void onAutoModeChange() {
if (autoMode) lastCheck = 0; // decide straight away
}
void onWarmLimitChange() {
lastCheck = 0;
}
For reference, this is the thingProperties.h that Arduino Cloud creates from the variables above. Yours has your own device ID, and may use an older but equally valid style such as addProperty(led, READWRITE, ON_CHANGE, onLedChange).
// Code generated by Arduino Cloud (yours has your own device ID)
#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>
const char DEVICE_LOGIN_NAME[] = "YOUR_DEVICE_ID";
const char SSID[] = SECRET_SSID; // from the Secret tab
const char PASS[] = SECRET_OPTIONAL_PASS;
const char DEVICE_KEY[] = SECRET_DEVICE_KEY;
void onOpeningChange();
void onAutoModeChange();
void onWarmLimitChange();
int opening;
bool autoMode;
int warmLimit;
CloudTemperatureSensor temperature;
void initProperties() {
ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME);
ArduinoCloud.setSecretDeviceKey(DEVICE_KEY);
ArduinoCloud.addProperty(opening, Permission::ReadWrite).onUpdate(onOpeningChange);
ArduinoCloud.addProperty(autoMode, Permission::ReadWrite).onUpdate(onAutoModeChange);
ArduinoCloud.addProperty(warmLimit, Permission::ReadWrite).onUpdate(onWarmLimitChange);
ArduinoCloud.addProperty(temperature, Permission::Read).publishEvery(30);
}
WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);How it works
opening is the single source of truth: 0 % is closed, 100 % wide open. The slider in the dashboard changes it, and so does auto mode. Because it is a Read & Write variable, when the ESP32 changes it in auto mode, Arduino Cloud moves the dashboard slider to match. You always see the real state of the window.
moveSmoothly() never jumps: every 15 ms it moves the servo one degree closer to the angle that matches opening. That is gentler on the window and the servo.
The 0.5 °C gap on either side of the limit is hysteresis: without it, a temperature hovering at 24.0 °C would make the window open and close over and over.
Try this next
- Close the window when it starts raining, with the water level sensor on the window sill.
- Open the window for ten minutes every morning to air the room.
- Show "open" or "closed" in a Status widget.
Troubleshooting
- The board restarts when the servo moves: servos draw bursts of current. Use a good USB port or a powered hub, and check that the servo's red wire is on 5V.
- The device stays "Offline": open the Serial Monitor at 115200 baud. Check the Wi-Fi name and password in the Secret tab (2.4 GHz only) and that the secret device key is the one you saved when you added the device.
- Compile error about a variable: the variable names in the sketch must match the ones in your Thing exactly, including capital letters. Rename the Thing's variable or the sketch's to match.
-
The window opens the wrong way: swap the values of
CLOSED_ANGLEandOPEN_ANGLE. -
It closes in daylight: lower
DARKuntil the room light counts as day. - A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.