Unit 2: Input devices with NodeMCU

ECE237 — Architecting Smart Iot Devices 9 min read

I. NodeMCU Input Architecture

NodeMCU is an ESP8266-based development board used to read physical conditions and transmit data through Wi-Fi. An input device converts a physical quantity such as temperature, distance, light, or motion into an electrical signal that the ESP8266 can interpret.

  • Controller: The ESP8266 operates at 3.3 V logic and commonly runs Arduino C/C++ programs.
  • Digital input: A GPIO reads two logic states, HIGH or LOW; examples include IR receiver modules and ultrasonic echo signals.
  • Analog input: The A0 pin measures a varying voltage. On many NodeMCU boards, the external A0 input range is approximately 0–3.3 V, although the ESP8266 chip ADC itself is limited to about 0–1.0 V.
  • Power convention: Use 3V3 for 3.3 V sensors and GND as the common reference. A sensor and NodeMCU must share ground.
  • Timing principle: Some sensors require precise delays or pulse measurements. millis() is preferred for non-blocking periodic tasks; short delay() calls are acceptable in simple demonstrations.
  • Pin convention: GPIO numbers and board labels are different. For example, NodeMCU label D1 corresponds to GPIO5, while D2 corresponds to GPIO4.
  • Safety assumption: Do not apply a 5 V signal directly to an ESP8266 GPIO. A voltage divider or level shifter is required where a module produces 5 V output.

II. DHT11 — Digital Temperature and Humidity Input

DHT11 is a low-cost digital sensor that measures relative humidity and air temperature and sends both values through a single-wire timed data signal.

A. Programming NodeMCU for DHT11

Programming NodeMCU for DHT11 requires correct wiring, a DHT library, and periodic reading of temperature and humidity values.

  • Connections: Connect DHT11 VCC to 3V3, GND to GND, and DATA to D2 (GPIO4). A bare four-pin sensor needs a pull-up resistor, commonly 4.7 kΩ to 10 kΩ, between DATA and 3V3.
  • Library: The Arduino DHT sensor library and its dependency Adafruit Unified Sensor provide functions such as readTemperature() and readHumidity().
  • Program:
CPP
#include <DHT.h>

#define DHTPIN D2
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  dht.begin();
}

void loop() {
  float humidity = dht.readHumidity();
  float temperatureC = dht.readTemperature();

  if (isnan(humidity) || isnan(temperatureC)) {
    Serial.println("DHT11 read failed");
  } else {
    Serial.print("Temperature: ");
    Serial.print(temperatureC);
    Serial.print(" C, Humidity: ");
    Serial.print(humidity);
    Serial.println(" %");
  }

  delay(2000);
}
  • Symbols and functions: DHTPIN identifies the data GPIO, DHTTYPE selects the sensor, temperatureC is temperature in degrees Celsius, and humidity is relative humidity in percent.
  • Sampling rule: DHT11 should generally be read no faster than once every two seconds. A NaN result means “not a number” and commonly indicates a wiring, timing, or power problem.
  • Output interpretation: A reading of 28 °C and 65 % means air temperature is 28 degrees Celsius and relative humidity is 65 percent.

III. Ultrasonic Sensor — Distance by Echo Timing

An ultrasonic sensor estimates distance by transmitting a sound pulse and measuring the time required for its echo to return.

A. Programming NodeMCU for Ultrasonic sensor

Programming NodeMCU for Ultrasonic sensor involves generating a trigger pulse and converting the echo duration into distance.

  • Connections: Connect TRIG to D5 (GPIO14), ECHO through a voltage divider to D6 (GPIO12), VCC to the module supply, and GND to NodeMCU ground. A typical HC-SR04 is powered at 5 V and may output a 5 V echo signal, so direct connection to ESP8266 GPIO is unsafe.
  • Measurement formula: Sound travels to the object and back, so the measured path is twice the distance.
TEXT
distance_cm = echo_time_us × 0.0343 / 2

Here, echo_time_us is echo duration in microseconds and 0.0343 is the approximate speed of sound in centimeters per microsecond at room temperature.

  • Program:
CPP
#define TRIG_PIN D5
#define ECHO_PIN D6

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  unsigned long echoTime = pulseIn(ECHO_PIN, HIGH, 30000);
  float distanceCm = echoTime * 0.0343 / 2.0;

  if (echoTime == 0) {
    Serial.println("No echo");
  } else {
    Serial.print("Distance: ");
    Serial.print(distanceCm);
    Serial.println(" cm");
  }

  delay(500);
}
  • Pulse generation: The 10 microsecond HIGH trigger causes the sensor to transmit an ultrasonic burst.
  • Timeout: 30000 microseconds prevents pulseIn() from waiting indefinitely. It corresponds to a limited measurement range.
  • Limitations: Soft, angled, or irregular surfaces may absorb or deflect sound. Temperature changes also alter the speed of sound and therefore affect accuracy.

IV. Temperature Sensor — Analog Measurement with LM35

An analog temperature sensor such as the LM35 produces a voltage proportional to temperature. The LM35 has a scale factor of approximately 10 mV per degree Celsius.

A. programming temperature sensor

Programming temperature sensor input with an LM35 requires converting the ADC voltage into degrees Celsius.

  • Connections: Connect LM35 VCC to 3V3, GND to GND, and its output pin to A0. Check the sensor’s flat-face pin arrangement before wiring because package pinouts must not be assumed.
  • Conversion formula:
TEXT
voltage_V = adc_value × Vref / ADCmax
temperature_C = voltage_V × 100

adc_value is the value returned by analogRead(), Vref is the ADC reference range in volts, and ADCmax is the maximum ADC count. For a 10-bit ADC, ADCmax = 1023. The factor 100 converts LM35 volts to degrees Celsius because 10 mV/°C equals 0.01 V/°C.

  • Program:
CPP
#define LM35_PIN A0

void setup() {
  Serial.begin(115200);
}

void loop() {
  int adcValue = analogRead(LM35_PIN);
  float voltage = adcValue * 3.3 / 1023.0;
  float temperatureC = voltage * 100.0;

  Serial.print("Temperature: ");
  Serial.print(temperatureC);
  Serial.println(" C");

  delay(1000);
}
  • Calibration condition: The 3.3 value is an assumed ADC range for a NodeMCU board. If the board’s ADC input is scaled differently, use the actual calibrated voltage range.
  • Example: With adcValue = 310, the estimated voltage is 310 × 3.3 / 1023 ≈ 1.00 V, giving approximately 100 °C. This demonstrates why the input range and sensor supply must remain within specification.
  • Advantages and limits: LM35 provides a continuous analog signal and is simple to read, but electrical noise, ADC scaling, and wiring resistance influence the result.

V. IR Sensor — Digital Object Detection

An IR sensor module detects reflected or interrupted infrared light and usually provides a digital output indicating whether an object is present.

A. programming ir sensor

Programming ir sensor input requires configuring a GPIO as INPUT and interpreting the module’s output logic.

  • Connections: Connect the module VCC according to its specification, GND to GND, and OUT to D7 (GPIO13). Use 3.3 V-compatible output logic.
  • Logic warning: Many IR obstacle modules are active-low: LOW indicates detection and HIGH indicates no detection. Confirm this behavior by observing the module or its documentation.
  • Program:
CPP
#define IR_PIN D7
#define LED_PIN LED_BUILTIN

void setup() {
  Serial.begin(115200);
  pinMode(IR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int state = digitalRead(IR_PIN);

  if (state == LOW) {
    Serial.println("Object detected");
    digitalWrite(LED_PIN, LOW);
  } else {
    Serial.println("Path clear");
    digitalWrite(LED_PIN, HIGH);
  }

  delay(100);
}
  • Control principle: digitalRead(IR_PIN) returns the logic level; the conditional statement maps that level to a message and LED state.
  • Adjustment: The onboard potentiometer on many modules changes detection sensitivity or threshold distance.
  • Limitations: Detection depends on object color, surface reflectivity, ambient sunlight, alignment, and the selected threshold. An IR obstacle sensor generally reports presence, not an accurate distance.

VI. DHT11 and DHT22 — Comparative Sensor Choice

DHT11 and DHT22 use a similar digital communication method, but DHT22 offers a wider range and finer resolution.

A. Compare DHT11 and DHT22

Compare DHT11 and DHT22 by examining measurement range, resolution, accuracy, cost, and sampling speed.

  • Temperature range:
    1. DHT11: Approximately 0 to 50 °C.
    2. DHT22: Approximately −40 to 80 °C.
  • Humidity range:
    1. DHT11: Approximately 20–80% relative humidity in its rated operating range.
    2. DHT22: Approximately 0–100% relative humidity, subject to its specified accuracy limits.
  • Resolution: DHT11 commonly reports 1 °C and 1% humidity steps; DHT22 commonly reports 0.1 °C and 0.1% humidity steps.
  • Accuracy: DHT22 is generally more accurate, often around ±0.5 °C for temperature and ±2–5% relative humidity, while DHT11 is commonly around ±2 °C and ±5% relative humidity.
  • Sampling interval: DHT11 is normally read at intervals of about two seconds; DHT22 commonly requires about two seconds or more between readings depending on its datasheet.
  • Software selection: The program structure is the same, but the type definition changes:
CPP
#define DHTTYPE DHT11   // Select DHT11
// #define DHTTYPE DHT22 // Select DHT22
  • Selection rule: Choose DHT11 for inexpensive basic demonstrations and moderate indoor conditions; choose DHT22 when decimal resolution, broader temperature range, or improved accuracy matters.

VII. LDR Sensor — Light-Dependent Input

An LDR, or light-dependent resistor, changes resistance according to illumination: its resistance decreases as light intensity increases.

A. programming LDR sensor

Programming LDR sensor input normally uses a voltage divider so that changing resistance becomes a measurable analog voltage.

  • Circuit arrangement: Connect the LDR and a fixed resistor, commonly 10 kΩ, in series between 3V3 and GND; connect their junction to A0. The exact voltage trend depends on which component is placed on the high-voltage side.
  • Divider formula:
TEXT
Vout = Vin × Rbottom / (Rtop + Rbottom)

Vout is the voltage at A0, Vin is the supply voltage, Rtop is the resistance connected to 3V3, and Rbottom is the resistance connected to ground.

  • Program:
CPP
#define LDR_PIN A0

void setup() {
  Serial.begin(115200);
}

void loop() {
  int lightValue = analogRead(LDR_PIN);

  Serial.print("LDR value: ");
  Serial.println(lightValue);

  if (lightValue < 400) {
    Serial.println("Low light");
  } else {
    Serial.println("Bright light");
  }

  delay(500);
}
  • ADC meaning: lightValue is a relative reading from 0 to 1023 on a 10-bit ADC; it is not automatically a value in lux.
  • Threshold selection: The value 400 is an example threshold. Measure readings in the intended environment and select a threshold that separates dark and bright conditions.
  • Applications: LDR input can switch lamps, estimate daylight, trigger alarms, or support automatic brightness control.
  • Limitations: LDR response is nonlinear, varies between components, and is affected by the resistor value, shadows, ambient spectrum, and ADC calibration.