IoT Sensors & Actuators — Complete Hardware Guide
In this tutorial, you'll learn about IoT Sensors & Actuators. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
IoT sensors and actuators are the physical interface between digital systems and the real world — sensors collect environmental data while actuators perform physical actions, forming the foundation of any IoT deployment.
What You'll Learn
You'll explore common IoT sensor types (temperature, pressure, motion, gas), actuator interfaces (servos, relays, motors), sensor calibration and signal conditioning techniques, and wiring/interfacing code for ESP32 and Arduino.
Why Sensors & Actuators Matter
Every IoT system starts with sensing and ends with action. A temperature sensor without a cooling actuator is just a weather station. An alarm without a sensor is useless. At DodaTech, our Industrial Iot security system uses PIR motion sensors to detect physical intrusion and relay actuators to trigger locks and alerts.
Real-World Use Case
A greenhouse deploys 12 sensors (temperature, humidity, soil moisture, light) and 6 actuators (water pump, vent motors, grow lights). When soil moisture drops below 30%, the pump activates. When temperature exceeds 35°C, vents open. The system saves 40% water and increases crop yield by 25%.
Sensor Types and Interfaces
| Sensor Type | Measures | Interface | Common ICs |
|---|---|---|---|
| Temperature | Ambient temp | I2C, OneWire | DHT22, BME280, DS18B20 |
| Pressure | Barometric | I2C, SPI | BMP280, MS5611 |
| Motion | Movement, presence | Digital GPIO | PIR HC-SR501 |
| Gas | Air quality | I2C, Analog | MQ-135, CCS811 |
| Distance | Proximity | I2C, PWM | HC-SR04, VL53L0X |
| Humidity | Moisture in air | I2C, OneWire | DHT22, SHT30 |
Reading Sensors with ESP32
Temperature and Humidity (DHT22)
#include <DHT.h>
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float humidity = dht.readHumidity();
float temperature = dht.readTemperature(); // Celsius
// Check for read errors
if (isnan(humidity) || isnan(temperature)) {
Serial.println("ERROR: Failed to read from DHT sensor!");
delay(2000);
return;
}
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print("°C | Humidity: ");
Serial.print(humidity);
Serial.println("%");
// Alert if out of range
if (temperature > 35.0) {
Serial.println("WARNING: High temperature detected!");
}
if (humidity < 20.0) {
Serial.println("WARNING: Low humidity detected!");
}
delay(2000);
}
Expected output:
Temperature: 24.5°C | Humidity: 55.2%
Temperature: 24.6°C | Humidity: 55.1%
Error handling catches sensor disconnection gracefully.
Ultrasonic Distance Sensor (HC-SR04)
#define TRIG_PIN 5
#define ECHO_PIN 18
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
float measureDistance() {
// Send 10us pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Measure echo pulse duration
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms timeout
if (duration == 0) {
return -1.0; // No echo — out of range
}
// Convert to cm: speed of sound = 343m/s
float distance = (duration * 0.034) / 2;
return distance;
}
void loop() {
float cm = measureDistance();
if (cm < 0) {
Serial.println("Out of range");
} else {
Serial.print("Distance: ");
Serial.print(cm);
Serial.println(" cm");
if (cm < 20) {
Serial.println("WARNING: Object too close!");
}
}
delay(500);
}
Expected output:
Distance: 45.2 cm
Distance: 12.8 cm
WARNING: Object too close!
The sensor detects objects from 2cm to 400cm with ±3mm accuracy.
Controlling Actuators
Servo Motor Control
#include <ESP32Servo.h>
Servo myServo;
#define SERVO_PIN 13
void setup() {
myServo.attach(SERVO_PIN);
Serial.begin(115200);
}
void loop() {
// Sweep from 0 to 180 degrees
for (int angle = 0; angle <= 180; angle += 1) {
myServo.write(angle);
Serial.print("Angle: ");
Serial.println(angle);
delay(15); // Allow servo to reach position
}
// Return to 0
for (int angle = 180; angle >= 0; angle -= 1) {
myServo.write(angle);
delay(15);
}
}
Expected output: Servo sweeps from 0° to 180° and back. Each position takes ~15ms to reach. PWM signal on pin 13 controls the angle.
Relay Module for High-Power Devices
#define RELAY_PIN 12
void setup() {
pinMode(RELAY_PIN, OUTPUT);
Serial.begin(115200);
}
void controlDevice(bool turnOn, const char* deviceName) {
digitalWrite(RELAY_PIN, turnOn ? HIGH : LOW);
Serial.print(deviceName);
Serial.println(turnOn ? " turned ON" : " turned OFF");
}
void loop() {
// Simulate thermostat: turn on when cold
float temp = readTemperature(); // From earlier DHT22 code
if (temp < 18.0) {
controlDevice(true, "Heater");
} else if (temp > 25.0) {
controlDevice(false, "Heater");
}
delay(10000); // Check every 10 seconds
}
Expected output: The relay module switches a 220V heater based on temperature. The LED on the relay indicates on/off state.
Sensor Calibration
class CalibratedSensor {
private:
float offset; // Zero-point calibration value
float scale; // Gain correction factor
public:
CalibratedSensor(float ref_temp) {
// Calibrate against known reference
float raw = readRaw();
offset = ref_temp - raw;
scale = 1.0; // Assume linear gain = 1
}
float readCalibrated() {
float raw = readRaw();
return (raw * scale) + offset;
}
void twoPointCalibration(float ref_low, float raw_low,
float ref_high, float raw_high) {
scale = (ref_high - ref_low) / (raw_high - raw_low);
offset = ref_low - (raw_low * scale);
}
};
Expected output: Calibration corrects for sensor manufacturing tolerances. A DHT22 may read 24.5°C while the actual temperature is 25.0°C — calibration adjusts the offset.
Mermaid Diagram: Sensor-to-Actuator Loop
flowchart LR
A[Sensor] -->|Analog/Digital| B[Microcontroller]
B --> C[Processing & Decision]
C -->|PWM/GPIO| D[Actuator]
C -->|MQTT/WiFi| E[Cloud Platform]
E -->|Command| C
D --> F[Physical Action]
F -->|Changes Environment| A
style A fill:#d4edda
style B fill:#e6f3ff
style D fill:#fff3cd
style F fill:#cce5ff
Signal Conditioning
| Issue | Solution | Circuit |
|---|---|---|
| Noise | Low-pass filter | 10kΩ + 100nF to ground |
| Voltage shift | Level shifter | BSS138 MOSFET |
| Amplification | Op-amp | LM358 non-inverting |
| Debouncing | Schmitt trigger | 74HC14 or RC filter |
| Isolation | Optocoupler | PC817 |
Common Sensor Errors
1. Floating Input Pins
Problem: Unconnected pin reads random values. Fix: Enable internal pull-up/pull-down or use external resistor.
2. ADC Voltage Mismatch
Problem: ESP32 ADC is 0-3.3V but sensor outputs 0-5V. Fix: Use voltage divider (2:1 ratio with 10kΩ and 20kΩ resistors).
3. Insufficient Power for Actuators
Problem: Servo causes ESP32 to reset. Fix: Use separate power supply for motors/servos — never draw from MCU 3.3V pin.
4. Sensor Delayed Readings
Problem: DHT22 requires 2s between reads. Fix: Respect sensor timing specs — most sensors have minimum interval.
5. I2C Address Conflicts
Problem: Two sensors share same I2C address. Fix: Use I2C multiplexer (TCA9548A) or sensors with configurable addresses.
6. PWM Frequency Mismatch
Problem: Servo jitters at wrong PWM frequency. Fix: Use 50Hz (20ms period) for standard servos, 1-2ms pulse width.
Practice Questions
What is the difference between a sensor and a transducer? All sensors are transducers, but not all transducers are sensors. A transducer converts energy form. A sensor specifically measures a physical quantity.
Why use I2C over analog sensors? I2C provides digital reading, no ADC needed, multiple sensors on two wires, and built-in calibration.
What is PWM and how does it control actuators? Pulse-Width Modulation varies the duty cycle to control motor speed, servo position, or LED brightness.
How do you protect MCU pins from inductive loads? Use a flyback diode (1N4007) across relay coils and motor terminals to suppress voltage spikes.
What is the Nyquist sampling theorem for sensors? Sample at least 2x the highest frequency in the signal. For temperature (changes slowly), 1 sample/10s is fine. For vibration analysis, 1kHz+ needed.
Challenge
Build a closed-loop temperature control system: read a DS18B20 temperature sensor, implement a PID controller on ESP32, drive a MOSFET-controlled heating element, and maintain temperature at 25°C ± 0.5°C. Log data to serial and display in real-time.
Real-World Task
Design a sensor array for a smart hydroponic system. Sensors needed: water temperature (DS18B20), pH (analog pH probe), water level (HC-SR04), ambient temperature/humidity (DHT22). Actuators: water pump (relay), LED grow light (PWM), nutrient dosing pump (stepper motor). Draw a wiring diagram and write the main loop.
Mini Project: Multi-Sensor Dashboard
struct SensorData {
float temperature;
float humidity;
float distance;
int motion_detected;
};
SensorData readAllSensors() {
SensorData data;
data.temperature = dht.readTemperature();
data.humidity = dht.readHumidity();
data.distance = measureDistance();
data.motion_detected = digitalRead(PIR_PIN);
// JSON-like output for parsing
Serial.println("=== Sensor Readings ===");
Serial.printf("Temp: %.1f°C\n", data.temperature);
Serial.printf("Humidity: %.1f%%\n", data.humidity);
Serial.printf("Distance: %.1fcm\n", data.distance);
Serial.printf("Motion: %s\n",
data.motion_detected ? "DETECTED" : "None");
Serial.println("======================");
return data;
}
This function centralizes all sensor reads into a structured format for easy serial Parsing or MQTT publishing.
Related Tutorials
- Arduino — Foundation sensor programming
- ESP32 — Advanced sensor interfacing
- IoT Security — Secure sensor data transmission
- Next: IoT Cloud Platforms — AWS IoT, Azure IoT & GCP IoT
- Previous: IoT Edge Computing — Processing Data at the Edge Guide
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro