9. Input devices¶

Summary¶

1. Group Assignment¶
This week, we worked with digital and analog signals to understand their behavior and characteristics. Using an oscilloscope, we observed how the signals change over time and compared their voltage levels and waveforms. For this experiment, we used the board that we designed and fabricated during the Electronics Production week, which allowed us to generate, measure, and analyze different types of signals directly on our own hardware.
Digital signal¶
We started working with digital signals and wrote a simple code that reads input data from a button connected to our custom board. The output was assigned to digital pin 25, which drives an LED through a resistor. We also connected pin 25 in parallel to the oscilloscope in order to observe the digital signal changes in real time.

In this experiment, we used two pins — one for the button input and another for the LED output. Overall, we found that the digital signal is very simple: it operates with only two levels — HIGH and LOW, meaning either the maximum voltage (signal ON) or zero voltage (signal OFF).

This pin was used for the button input.

This pin was used for the LED output.

This week, we successfully generated and observed a digital signal using our custom PCB and an oscilloscope. We connected the ESP32 board through a USB-to-UART converter and visualized the HIGH and LOW transitions on the oscilloscope screen. The square-wave pattern confirmed that the digital output was functioning correctly.
Here is code¶
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define BTN_PIN GPIO_NUM_13 // button -> GND
#define LED_PIN GPIO_NUM_25 // optional LED
void app_main(void) {
// LED out
gpio_config_t led = {
.pin_bit_mask = 1ULL << LED_PIN,
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&led);
gpio_config_t btn = {
.pin_bit_mask = 1ULL << BTN_PIN,
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&btn);
int last = 1;
while (1) {
int lvl = gpio_get_level(BTN_PIN);
if (lvl != last) {
last = lvl;
if (lvl == 0) {
ESP_LOGI("BTN", "Pressed");
gpio_set_level(LED_PIN, 1);
} else {
ESP_LOGI("BTN", "Released");
gpio_set_level(LED_PIN, 0);
}
}
vTaskDelay(pdMS_TO_TICKS(5));
}
}
This ESP-IDF program configures GPIO13 as a button input with an internal pull-up and GPIO25 as an LED output. In the main loop it polls every 5 ms, detects state changes (press/release), logs them with ESP_LOGI, and turns the LED ON when pressed (level=0 to GND) and OFF when released.
Analog signal¶
After finishing the digital signal tests, we moved to analog signals. We connected a potentiometer to the board to generate a variable analog voltage (Vout from the wiper). We then read this voltage with the ADC and converted it to a PWM signal (duty cycle proportional to the potentiometer position) so we could visualize it on the oscilloscope as a changing pulse width.
Notes we learned:
Not every pin supports analog input — only ADC-capable pins can be used for reading analog values.
PWM output is generated by the LEDC (PWM) peripheral on specific pins/channels; we mapped the ADC reading to the LEDC duty cycle to demonstrate analog-to-PWM conversion.
This setup let us clearly see how an analog input can be represented as a duty-cycle-modulated digital waveform.

We used pin 32 for the analog signal because it corresponds to ADC1_CH4, which allows reading variable voltage from the potentiometer. This pin is connected to the built-in Analog-to-Digital Converter (ADC) of the microcontroller, enabling the conversion of analog voltage levels into digital values for further processing.

We used pin 25 to generate the PWM signal. This pin supports hardware PWM (LEDC) on the ESP32, allowing us to convert the analog value from the potentiometer into a varying pulse width and visualize it on the oscilloscope.

In this photo, we connected a potentiometer to our custom board to work with the analog signal. The potentiometer is connected to pin 32 (ADC1_CH4) for reading voltage levels, and the output PWM signal is generated on pin 25. This setup allowed us to convert the analog value into a PWM signal and visualize the changes on the oscilloscope.
Here is code¶
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "driver/ledc.h"
#include "esp_log.h"
#define ADC_GPIO 32
#define ADC_CHANNEL ADC2_CHANNEL_8
#define ADC_WIDTH ADC_WIDTH_BIT_12
#define ADC_ATTEN ADC_ATTEN_DB_11
#define PWM_GPIO 25
#define PWM_FREQ_HZ 5000
#define PWM_TIMER LEDC_TIMER_0
#define PWM_MODE LEDC_HIGH_SPEED_MODE
#define PWM_CHANNEL LEDC_CHANNEL_0
#define PWM_RESOLUTION LEDC_TIMER_12_BIT
static const char *TAG = "ADC2_to_PWM";
void app_main(void)
{
ledc_timer_config_t tcfg = {
.speed_mode = PWM_MODE,
.timer_num = PWM_TIMER,
.duty_resolution = PWM_RESOLUTION,
.freq_hz = PWM_FREQ_HZ,
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&tcfg);
ledc_channel_config_t ccfg = {
.gpio_num = PWM_GPIO,
.speed_mode = PWM_MODE,
.channel = PWM_CHANNEL,
.timer_sel = PWM_TIMER,
.duty = 0,
.hpoint = 0
};
ledc_channel_config(&ccfg);
adc2_config_channel_atten(ADC_CHANNEL, ADC_ATTEN);
ESP_LOGI(TAG, "Start: ADC on GPIO%d -> PWM on GPIO%d", ADC_GPIO, PWM_GPIO);
while (1) {
int raw = 0;
esp_err_t err = adc2_get_raw(ADC_CHANNEL, ADC_WIDTH, &raw);
if (err == ESP_OK) {
uint32_t duty = (uint32_t)raw;
if (duty > ((1 << PWM_RESOLUTION) - 1)) duty = (1 << PWM_RESOLUTION) - 1;
ledc_set_duty(PWM_MODE, PWM_CHANNEL, duty);
ledc_update_duty(PWM_MODE, PWM_CHANNEL);
static uint32_t cnt = 0;
if ((cnt++ % 10) == 0) {
ESP_LOGI(TAG, "ADC raw=%d duty=%lu", raw, (unsigned long)duty);
}
} else {
ESP_LOGW(TAG, "ADC2 read error: %d", err);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
This ESP-IDF program reads an analog voltage on GPIO25 (ADC2_CH8, 12-bit, 11 dB atten.) and converts it to a PWM duty cycle on GPIO32 using the LEDC peripheral.
-
ADC setup: adc2_get_raw() samples the potentiometer on GPIO25 at 12-bit width.
-
PWM setup: LEDC timer at 5 kHz, 12-bit resolution; channel 0 drives GPIO32.
-
Mapping: The raw ADC value (0…4095) is written directly as the PWM duty (0…4095).
-
Loop: Every 10 ms it samples ADC, updates LEDC duty, and logs periodic readings.
Measuring the Potentiometer¶
After finishing this experiment, I started measuring a potentiometer to observe the analog signal without connecting it to any microcontroller. I used my multimeter to check how the resistance and output voltage change when I rotate the knob. This helped me understand the full analog range of the potentiometer before interfacing it with the board.
In this video, you can see the measured signal displayed on the oscilloscope screen. I rotated the potentiometer and observed how the waveform and voltage level changed in real time. This test allowed me to clearly verify the analog behavior of the potentiometer before connecting it to the microcontroller.
2. Individual Assignment¶
This week, I worked with infrared sensors that will be used in my delivery robot. I connected two IR sensors so that the robot can detect whether there is an obstacle ahead or if the path is clear. These sensors help the robot recognize nearby objects and avoid collisions.

E18-D80NK Infrared Sensor¶
The E18-D80NK is an adjustable infrared proximity sensor commonly used for obstacle detection in robotics. It is a reflection-type photoelectric sensor, meaning it both emits and receives infrared light to detect objects. When an object comes within range, the reflected IR beam triggers a change in the sensor’s output.
Sensing range: 3 cm – 80 cm (depending on surface reflectivity)
Type: Reflection-based IR proximity sensor
Adjustment: Equipped with a distance regulator for sensitivity tuning
Indicator: Built-in LED output indicator for detection feedback
Output: Digital signal (High/Low depending on detection state)
This sensor is ideal for robot navigation and obstacle avoidance, offering a simple and reliable way to detect nearby objects without physical contact.

I started by soldering connectors to the infrared sensors because their wires were exposed. By adding connectors, I made the sensors easier to use and more reliable when connecting them to my board.

I used these 3-pin female Dupont connectors to connect my infrared sensors. They provide a safe and convenient connection to the microcontroller, making it easy to plug and unplug the sensors during testing.

I carefully soldered the wires to the connector pins and then used heat shrink tubing for insulation. This made the connection both secure and neat, improving reliability and safety.

I started calibrating and tuning the E18-D80NK infrared sensors, which I plan to use in my delivery robot. These digital sensors operate based on infrared light reflection and have two states: object detected and no object ahead. Using the small adjustable potentiometer on the back of the sensor, I manually changed the detection distance (from 3 cm to 80 cm). During testing, I set the range to a minimum level to verify that the sensor correctly detects nearby objects and sends a clean digital signal. I also tested the stability of detection, beam direction, and sensitivity to different surface types (matte and reflective). The results were successful — the sensors consistently detected obstacles. In the future, I plan to integrate these sensors along with a LiDAR, camera, and other sensors into my robot to create a hybrid environmental perception system. Why do I use multiple sensors? Because a single sensor can produce errors due to reflections or surface angles, while using several improves accuracy and reliability of obstacle detection.

I connected two E18-D80NK infrared sensors to my custom PCB using the previously soldered connectors. This made the wiring neat, durable, and easy to work with during testing. Now I can easily attach or detach the sensors without damaging the wires or pins.

I connected two E18-D80NK IR proximity sensors to my custom PCB and used a USB-TTL adapter to both program the microcontroller and open a serial connection with the computer.

Here is code¶
#define sensor1 16
#define sensor2 4
void setup() {
Serial.begin(9600);
pinMode(sensor1 , INPUT);
pinMode(sensor2 , INPUT);
}
void loop() {
int val1 = digitalRead(sensor1);
int val2 = digitalRead(sensor2);
Serial.print(" sensor_1: ");
Serial.print(val1);
Serial.print(" sensor_2: ");
Serial.println(val2);
delay(200);
}
I wrote this code to test two infrared sensors and display their readings on the Serial Monitor. The first sensor is connected to GPIO16 and the second to GPIO4. When a sensor detects an object, the monitor shows 1; otherwise, it shows 0. This helps me verify that both sensors are properly connected and working correctly.
Conclusion¶
This week I worked with E18-D80NK infrared sensors, which I plan to use in my delivery robot. I learned how to solder connectors, calibrate sensors using the adjustment screw, and test them through the Serial Monitor. I also wrote and tested code to display each sensor’s state in real time. All components performed well, and the sensors reliably detected nearby obstacles. This week was very productive because I gained a deeper understanding of digital sensors and prepared the foundation for integrating them into my autonomous delivery robot project.