Week 4. Embedded Programming


Embedded Programming



A microcontroller (MCU) is an integrated circuit that contains all the fundamental components of a computer on a single chip. Its purpose is to control devices and processes in embedded systems such as home appliances, sensors, robots, medical devices, or IoT (Internet of Things) systems.

  • Central Processing Unit (CPU): executes instructions and performs logical and arithmetic operations.
  • Flash Memory or ROM: permanently stores the program that defines its behavior.
  • RAM Memory: used to store temporary data during execution.
  • Input/Output Peripherals (I/O): allow interaction with the environment through digital, analog pins, and communication modules (such as UART, I2C, SPI).
  • Timers and interrupts: manage asynchronous events and timed processes.
  • Communication Modules: such as Wi-Fi, Bluetooth, or USB, that enable connection with other devices.

Microcontrollers are programmed to execute a sequence of instructions that allow them to make decisions and control systems, often autonomously and in real time.

ESP32 Family

The ESP32 family was developed by the Chinese company Espressif Systems, aiming to create powerful, cost-effective microcontrollers with native wireless connectivity. Its origin dates back to the success of the ESP8266, a chip that revolutionized IoT device development due to its low cost and integrated Wi-Fi connectivity.

From that point, Espressif launched the ESP32 series, which introduced a more robust architecture with multiple processing cores, better security, Bluetooth integration, and more peripherals. The family has expanded with specialized variants for different applications:

  • ESP32 (classic): dual-core, ideal for general-purpose applications.
  • ESP32-S2: focused on security and native USB support.
  • ESP32-C3: RISC-V architecture, low power consumption, and compatibility with ESP8266.
  • ESP32-S3: designed for AI tasks, computer vision, and voice recognition.
  • ESP32-H2: oriented to Zigbee, Thread, and BLE Mesh communications.

All these chips share the philosophy of Espressif: to offer computational power, wireless connectivity, and open support for developers, with extensive documentation and tools such as ESP-IDF or compatibility with Arduino IDE and MicroPython.

Microcontroller ESP32-C3 (XIAO ESP32-C3)

The XIAO ESP32-C3 board integrates the ESP32-C3 microcontroller, which is based on a 32-bit RISC-V architecture, running at a frequency of 160 MHz. This open and modular architecture allows for greater flexibility and energy efficiency, making it an excellent choice for low-power IoT applications such as wearable devices, remote sensors, and battery-powered systems.

  • Core: 1 RISC-V core at 160 MHz.
  • RAM Memory: 400 KB internal.
  • ROM: 384 KB (stores internal functions of the chip).
  • External Flash: usually 4 MB on the XIAO module.
  • Connectivity: Wi-Fi 802.11 b/g/n and Bluetooth 5 Low Energy.
  • Security: includes mechanisms such as Secure Boot and flash encryption.
  • Native USB: features USB 2.0 for direct communication and programming, without the need for a bridge chip.

Microcontroller ESP32-S3 (DevKitC-1)

The ESP32-S3 DevKitC-1 board is equipped with the ESP32-S3 microcontroller, a more advanced version within the ESP32 family, specifically designed to support more demanding computing tasks and edge processing.

  • Core: Dual-core Xtensa LX7 at 240 MHz, architecture designed by Cadence, focused on high-performance real-time operations.
  • RAM Memory: 512 KB of internal SRAM and support for up to 8 MB of external PSRAM.
  • ROM: 384 KB.
  • External Flash: typically 8–16 MB.
  • Connectivity: Wi-Fi 802.11 b/g/n and Bluetooth 5 (with Mesh and Long Range).
  • AI/ML Capabilities: includes SIMD (Single Instruction Multiple Data) instructions that enable vectorized operations, ideal for facial recognition, object tracking, or lightweight neural network inference.
  • USB OTG: supports functioning as both a device and USB host (enabling direct keyboard connection, for example).
  • Advanced peripheral support: capacitive touch inputs, LCD interfaces, image sensors (DVP), I2S interfaces for audio, among others.

Comparison Table: ESP32-C3 vs ESP32-S3

Characteristic ESP32-C3 (XIAO) ESP32-S3 (DevKitC-1)
Architecture RISC-V Open Source Xtensa LX7 High Performance
Number of Cores 1 2
Clock Speed 160 MHz 240 MHz
AI/ML Capabilities No Yes (SIMD, hardware-based AI)
USB USB 2.0 Native USB OTG (Host and Device)
Power Consumption Low (ideal for batteries) Moderate-High (depending on peripherals)
Advanced Security Secure Boot, Flash Encryption Equal or better, with accelerated encryption support
Usage Focus Compact devices and low power consumption Complex applications, AI, rich interfaces

1. Wireless Connectivity

Both microcontrollers incorporate Wi-Fi and Bluetooth wireless connectivity, making them ideal for Internet of Things (IoT) projects. However, there are important differences:

  • XIAO ESP32-C3:
    • Supports Wi-Fi 802.11 b/g/n at 2.4 GHz band.
    • Includes Bluetooth 5 Low Energy (BLE), which allows low power consumption in wireless links.
    • Does not support Bluetooth Classic, limiting its use in legacy protocols like SPP or A2DP.
  • ESP32-S3 DevKitC-1:
    • Also offers Wi-Fi 802.11 b/g/n, but with performance optimizations.
    • Its Bluetooth 5 includes support for BLE Mesh, Long Range (Coded PHY), and more advanced connection modes.
    • It does not include Bluetooth Classic but compensates with its extended capacity for distributed BLE networks and more stable connections.

2. Communication Interfaces

The interfaces allow the microcontroller to communicate with sensors, actuators, displays, or other microcontrollers. This is where the ESP32-S3 stands out as a more complete platform:

  • XIAO ESP32-C3:
    • Supports up to 2 UART ports, 2 I2C, 2 SPI, and 6 ADC (although some are shared).
    • Includes 1 PWM channel per GPIO.
    • Has native USB 2.0, eliminating the need for a bridge chip like CH340 or CP2102. This USB allows programming and, in some cases, acting as HID or CDC devices.
  • ESP32-S3 DevKitC-1:
    • Provides 3 UART ports, 2 I2C, 4 SPI, 14 ADC, and several PWM.
    • Integrates USB OTG, enabling it to act as both a device (for programming or PC communication) and a host, allowing it to handle USB peripherals like keyboards, flash drives, or cameras without an intermediary.
    • Includes additional interfaces like I2S for real-time audio processing and support for DVP (Digital Video Port), useful for connecting image sensors or cameras.

3. Specialized Processing Capabilities

Specialized processing is key when working with artificial intelligence, computer vision, or intensive mathematical tasks. The differences are quite noticeable:

  • ESP32-C3:
    • Does not have any hardware accelerators for AI or vector processing. Operations are processed in series on its single core, making it slower for tasks that involve data vectors, matrix transformations, signal processing, etc.
  • ESP32-S3:
    • Includes SIMD (Single Instruction, Multiple Data) accelerators, which allow the execution of the same operation on multiple data simultaneously. This type of instruction is fundamental for tasks such as:
      • Pattern detection in images.
      • Voice recognition.
      • Lightweight machine learning algorithms (TinyML).
    • Additionally, it has more cores, higher clock speed, and support for external PSRAM (up to 8 MB), which significantly expands its real-time data handling capacity.

4. Hardware Security

Both boards support modern security features, although with differences in focus and depth:

  • Both offer:
    • Secure Boot: prevents unauthorized firmware execution.
    • Flash Encryption: encrypts the firmware content stored in Flash memory.
  • ESP32-S3 adds:
    • Support for hardware cryptographic algorithms, including RSA, AES, and SHA with acceleration.
    • HMAC Mode (Hash-based Message Authentication Code) for quick authentication.
    • Capability for generating unique keys per device (Digital Signature Peripheral).

5. Dimensions and Hardware Design

  • XIAO ESP32-C3:
    • Ultra-compact size: 21.0 mm x 17.5 mm, ideal for small enclosures, wearables, embedded sensors, or prototypes where space is critical.
    • Symmetrical design with side pins makes it perfect for soldering on PCBs or direct use on breadboards.
    • The reduced number of pins limits its expansion capabilities.
  • ESP32-S3 DevKitC-1:
    • Standard development board design with approximate dimensions of 60 mm x 25 mm, including USB connector and buttons.
    • Its elongated rectangular shape provides two rows of 19-pin headers (38 in total), giving access to a large number of interfaces and functions directly from the microcontroller.
    • Includes physical RESET and BOOT buttons, essential for debugging and firmware loading.

6. Pinout Distribution

  • XIAO ESP32-C3:
    • Features 11 multifunction GPIO pins: each can act as a digital pin, some as analog inputs (ADC), and others as UART, I2C, or SPI interfaces.
    • Includes power pins: 5V, 3.3V, and GND, plus a USB-C port for communication and power.
    • No dedicated PWM pins, but several GPIOs can be configured for that purpose.
    • Contains an integrated RGB LED (in some models) and a user button available in the center.
  • ESP32-S3 DevKitC-1:
    • Up to 45 available GPIOs, clearly labeled and categorized by function:
      • ADC1 and ADC2: for analog reading.
      • TOUCH: capacitive touch sensors.
      • SPI, I2C, UART: multiple instances.
      • PWM capable: almost all pins can be used as PWM outputs.
      • Specific pins for DVP (image), I2S (audio), JTAG, RGB LED, USB D+/D-, etc.
    • Well-organized and separated by function, ideal for modular testing and development.

Comparison Table: USB and Voltage Support

Component XIAO ESP32-C3 ESP32-S3 DevKitC-1
USB Port USB Type C (modern, reversible) USB Micro B (more common, less robust)
Supported Voltages 3.3V and 5V 3.3V and 5V
Physical Buttons None (some have one programmable button) Reset + Boot
Header Type 2x7 side pins (only 11 GPIOs) 2x19 pins with extensive labeling

7. Supported Programming Languages

Both boards are compatible with various languages, giving them flexibility for different user profiles:

  • C/C++: The native language for development in embedded systems. It allows direct hardware control and is widely used in environments such as Arduino IDE, PlatformIO, and the official ESP-IDF.
  • MicroPython: A lightweight implementation of Python designed for microcontrollers. It is ideal for educational projects, quick tests, or simple automation. It allows interaction with GPIO pins, sensors, and Wi-Fi modules with simple syntax.
  • Rust and JavaScript (Espruino): Although less common, there are implementations that allow programming ESP32 in these languages. These are more experimental and require specific tools.

Program Development



Develop an embedded system capable of detecting the proximity of objects using an ultrasonic sensor (HC-SR04) and generating a sound response with a buzzer when the measured distance falls below a defined threshold. This system will be implemented using the XIAO ESP32-C3 and ESP32-S3 DevKitC-1 boards, allowing for a comparison of their performance under the same conditions.

Pin Configuration Table

Function XIAO ESP32-C3 ESP32-S3 DevKitC-1
Trigger (HC-SR04) GPIO 2 GPIO 17
Echo (HC-SR04) GPIO 3 GPIO 16
Buzzer (Active) GPIO 10 GPIO 5
VCC (HC-SR04) 3.3V / 5V 3.3V / 5V
GND (HC-SR04) GND GND

Step-by-Step Guide for Wokwi Simulation

Follow these detailed steps to successfully simulate the ultrasonic sensor and buzzer with the XIAO ESP32-C3 or ESP32-S3 on the Wokwi platform:

1️⃣ Step 1: Access Wokwi Platform

Open your preferred web browser and go to the official Wokwi Platform. Wokwi is an online tool that allows you to simulate embedded systems circuits with microcontrollers and components like sensors, actuators, and LEDs. It's an excellent platform for testing and experimenting with hardware before physical implementation.



2️⃣ Step 2: Create a New Project

Once you are on the homepage of Wokwi, you will see a variety of options for different types of microcontrollers, including Arduino, ESP32, STM32, and Raspberry Pi Pico. Wokwi provides templates for each type of microcontroller to help you start quickly.



To begin, scroll down to the Starter Templates section. There, you’ll find pre-configured setups for different microcontrollers. You need to choose the ESP32 family, specifically the XIAO ESP32-C3 or ESP32-S3 templates.



After selecting a template, Wokwi will open a new workspace that is divided into two sections:

  • Left Panel: This is the code editor, where you will write and modify your code (Arduino or MicroPython).
  • Right Panel: This is the simulation area, where you will see the microcontroller and all the connected components (like the HC-SR04 ultrasonic sensor and the buzzer).


3️⃣ Step 3: Understanding the Workspace

In the simulation workspace, you will see the following key areas:

  • Microcontroller Representation: The microcontroller will appear in the right panel, with labeled GPIO pins (such as GPIO 2, GPIO 3, etc.) for easy component connections.
  • Simulation Control: At the top right of the simulation area, you will find several buttons:
    • Play (▶): Starts the simulation, allowing you to see how the components behave with your code.
    • Add Component (+): Opens the component search bar, allowing you to add components like the HC-SR04 sensor and the buzzer.
    • Options (⋯): Offers additional settings for adjusting the workspace, like zooming and other preferences.


  • Code Area: On the left side, you will find the code editor. This is where you will write or paste your code to control the microcontroller and interact with the components. You will use this area to write logic for triggering the buzzer based on the ultrasonic sensor's distance measurement.

4️⃣ Step 4: Add the Components

To add the HC-SR04 ultrasonic sensor and the Active Buzzer to the simulation:

  • Click the blue “+” button in the right panel to add components. This will open the component search bar.
  • In the search bar, type HC-SR04 and select HC-SR04 Ultrasonic Distance Sensor.
  • Next, type Buzzer in the search bar and select Active Buzzer from the search results.




The components will appear in the workspace. You can drag and place them near the microcontroller for easy wiring.

5️⃣ Step 5: Select the Microcontroller Template

After selecting a new project template, Wokwi will show you several development board options. Choose one of the following options based on the microcontroller you are using:

  • XIAO ESP32-C3: Select this template if you are using the XIAO ESP32-C3 microcontroller. The workspace will load with the microcontroller on the right side and the code editor on the left.
  • ESP32-S3: Select this template if you are using the ESP32-S3 microcontroller. The workspace layout will be similar to the XIAO ESP32-C3 template, with the components and wiring available for editing.

The workspace will now be divided into two parts:

  • Left Panel: This is where you will write and modify your Arduino or MicroPython code.
  • Right Panel: The simulation area, where the microcontroller and all connected components are displayed for easy wiring and simulation.

6️⃣ Step 6: Wiring the Components

Now that the components are in place, it’s time to wire them up based on the pin configuration. Use the following connections:

  • Trigger (HC-SR04): Connect this pin to GPIO 2 on the XIAO ESP32-C3 or GPIO 17 on the ESP32-S3.
  • Echo (HC-SR04): Connect this pin to GPIO 3 on the XIAO ESP32-C3 or GPIO 16 on the ESP32-S3.
  • Buzzer: Connect this pin to GPIO 10 on the XIAO ESP32-C3 or GPIO 5 on the ESP32-S3.
  • VCC (HC-SR04): Connect this to 3.3V or 5V depending on the microcontroller’s voltage options.
  • GND (HC-SR04): Connect this to the GND pin of the microcontroller.

Make sure that all the connections are correct and that you have connected the power and ground pins of all components. Once everything is connected, you are ready for the next step: writing the code to control the components.









7️⃣ Step 7: Write the Code

Now that the components are correctly wired, it’s time to write the code. Here’s a breakdown of what your code will do:

  • Initialize Pins: In the setup() function, you will define the Trigger and Echo pins for the HC-SR04 ultrasonic sensor and set the Buzzer pin as an output.
  • Measure Distance: In the loop() function, use the pulseIn() function to calculate the time taken for the sound wave to travel to the object and back (this is the Echo pin). The Trigger pin will send a signal to initiate the pulse.
  • Threshold Logic: If the distance measured by the ultrasonic sensor is below a certain threshold (e.g., 20 cm), activate the Buzzer to provide an audio alert.
  • Display Output: Optionally, you can print the measured distance to the Serial Monitor for debugging or feedback.

Once you’ve written the code, click the Play (▶) button in the top-right corner of the workspace to run the simulation and verify that everything is working correctly. You should see the buzzer sound when an object is detected within the defined distance threshold.

CODE FOR THE XIAO ESP32-C3
#define TRIG_PIN D0    // Pin TRIG conectado al GPIO2
#define ECHO_PIN D1   // Pin ECHO conectado al GPIO3
#define BUZZER_PIN D10 // Pin del Buzzer conectado al GPIO10

long duration;
float distance;
bool buzzerActivo = false;

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

void loop() {
  // Enviar pulso ultrasónico
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Medir duración del pulso en ECHO
  duration = pulseIn(ECHO_PIN, HIGH);
  distance = (duration * 0.0343) / 2; // Convertir a cm

  Serial.print("Distancia: ");
  Serial.print(distance);
  Serial.println(" cm");

  // Validación para evitar lecturas fuera de rango
  if (distance <= 0 || distance > 400) {
    Serial.println("Lectura inválida");
    digitalWrite(BUZZER_PIN, LOW);
    return;
  }

  // Lógica con histéresis y beep intermitente
  if (distance > 0 && distance < 15) {
    buzzerActivo = true;
  } else if (distance > 20) {
    buzzerActivo = false;
  }

  if (buzzerActivo) {
    digitalWrite(BUZZER_PIN, HIGH);
    delay(100);
    digitalWrite(BUZZER_PIN, LOW);
    delay(100);
  } else {
    digitalWrite(BUZZER_PIN, LOW);
  }

  delay(300);
}

CODE FOR THE ESP32-S3
#define TRIG_PIN 4    // Pin TRIG conectado al GPIO0
#define ECHO_PIN 5    // Pin ECHO conectado al GPIO1
#define BUZZER_PIN 10 // Pin del Buzzer conectado al GPIO10

long duration;
float distance;
bool buzzerActivo = false;

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

void loop() {
  // Enviar pulso ultrasónico
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Medir duración del pulso en ECHO
  duration = pulseIn(ECHO_PIN, HIGH);
  distance = (duration * 0.0343) / 2; // Convertir a cm

  Serial.print("Distancia: ");
  Serial.print(distance);
  Serial.println(" cm");

  // Validación para evitar lecturas fuera de rango
  if (distance <= 0 || distance > 400) {
    Serial.println("Lectura inválida");
    digitalWrite(BUZZER_PIN, LOW);
    return;
  }

  // Lógica con histéresis y beep intermitente
  if (distance > 0 && distance < 15) {
    buzzerActivo = true;
  } else if (distance > 20) {
    buzzerActivo = false;
  }

  if (buzzerActivo) {
    digitalWrite(BUZZER_PIN, HIGH);
    delay(100);
    digitalWrite(BUZZER_PIN, LOW);
    delay(100);
  } else {
    digitalWrite(BUZZER_PIN, LOW);
  }

  delay(300);
}

8️⃣ Step 8: Run the Simulation

Once you’ve written the code and connected all the components, it’s time to test the simulation. Click the Play (▶) button at the top right of the workspace to start the simulation.

The simulation will run, and you will be able to see how the ultrasonic sensor detects objects. If the distance is below the threshold you’ve set in your code, the buzzer will sound to alert you.

The Wokwi platform will show the results in the simulation window. If everything is correct, you should see the distance being measured in real-time and hear the buzzer sound when the object is too close.

9️⃣ Step 9: Check the Serial Monitor

In order to see the real-time distance measurements from the ultrasonic sensor, you can open the Serial Monitor in Wokwi. This will give you feedback on the sensor’s readings as it detects objects.

To view the Serial Monitor, click on the Monitor tab at the top of the workspace (near the simulation control buttons). This will allow you to see the distance data printed by the microcontroller in real-time.

🔟 Step 10: Implement the System on Physical Hardware

After testing the simulation and confirming that everything is working as expected, you can move on to implementing the system with physical hardware.

Start by connecting the components (HC-SR04 sensor, buzzer, and the microcontroller) according to the same pin configuration used in the simulation. Make sure to connect the VCC and GND pins correctly.

If you are using the XIAO ESP32-C3, make sure that when you upload the code, you press the Boot button on the microcontroller to enter programming mode. This is necessary to allow the code to be uploaded.

For the ESP32-S3, you don’t need to press any buttons to enter programming mode. Simply ensure that you’ve selected the correct board and port in the Arduino IDE, and you are ready to upload the code.

ESP32-S3
XIAO ESP32-C3

1️⃣1️⃣ Step 11: Code Upload and Testing on Arduino IDE

To upload the code from Wokwi to the Arduino IDE, follow these steps:

  • Install Libraries: Before uploading the code, make sure to check if the required libraries (like HC-SR04) are installed. In the Arduino IDE, go to the Sketch > Include Library > Manage Libraries menu, and search for the HC-SR04 library. If it’s not installed, click Install.
  • Set the Board and Port: In the Arduino IDE, go to Tools > Board and select the appropriate board (e.g., XIAO ESP32-C3 or ESP32-S3). Then, select the correct Port under Tools > Port.
  • Upload Code: Click the Upload button (the arrow icon) to send the code to the microcontroller. If you are using the XIAO ESP32-C3, press the Boot button to enter programming mode. For the ESP32-S3, no additional steps are needed.
SET UP ARDUINO IDE

1️⃣2️⃣ Step 12: Debugging and Final Testing

After uploading the code to the microcontroller, you can test the system by opening the Serial Monitor in the Arduino IDE. You should see the distance readings being printed as the ultrasonic sensor measures the distance.

If the buzzer sounds when the distance is below the threshold, it indicates that everything is working as expected. If not, check your wiring and code for any errors.

1️⃣3️⃣ Step 13: Enjoy Your Working System!

After debugging and testing the system, you now have a working ultrasonic proximity detection system. Congratulations! You’ve successfully used Wokwi to simulate your project and then implemented it on a physical microcontroller.

You can now use this system in a variety of applications, such as object detection, obstacle avoidance in robots, or even automatic lighting control systems.

XIAO ESP32-C3 RESULT
ESP32-S3 RESULT