To establish a consistent environment for building and deploying software throughout this course, we deploy a Linux Virtual Machine inside Oracle VM VirtualBox. This isolates our development code from host OS dependencies. The architecture below demonstrates how physical microcontroller devices connected to Windows USB ports are redirected via VirtualBox filters into the Linux serial system (`/dev/ttyUSB0`), allowing the compiled firmware binary to pass directly to the hardware flash memory.
2. Part 1: Step-by-Step Individual Installation Commands & GUI Navigation
Follow these detailed steps to install Oracle VM VirtualBox, spin up your Ubuntu VM, map your microcontroller device, configure permissions, and deploy your first firmware.
STEP 1
Install VirtualBox and Extension Pack on Windows Host
Download and run the VirtualBox hypervisor installer. The Extension Pack is mandatory to enable USB 2.0/3.0 port redirection to the guest OS.
Windows Host Steps:
1. Open a browser and navigate to https://www.virtualbox.org/wiki/Downloads
2. Click on "Windows hosts" to download the primary installer.
3. Scroll down on the same page, locate "VirtualBox Oracle VM VirtualBox Extension Pack", and click "All supported platforms" to download the file.
4. Double-click the primary VirtualBox installer `.exe` file. Click "Next", keep default setup paths, click "Yes" on the networking warning interface, and click "Install".
5. Once installation is complete, double-click the Extension Pack file. VirtualBox will open; click "Install", scroll to the bottom of the terms agreement, and click "I Agree".
We install the VirtualBox base application and Extension Pack to establish our guest virtualization engine and enable key hardware controllers required for USB peripheral redirection.
STEP 2
Configure the Ubuntu Linux Virtual Machine
Download the Ubuntu Desktop installation media and construct a new VM shell with resources allocated for compiler tasks.
VirtualBox Configuration Steps:
1. Download the Ubuntu Desktop ISO from https://ubuntu.com/download/desktop (choose 24.04 LTS or 22.04 LTS).
2. Open VirtualBox, click the blue "New" button in the top menu bar.
3. Type Name as "Ubuntu-IoT". Set the "ISO Image" dropdown to "Other...", browse to select your downloaded Ubuntu `.iso` file.
4. Check the box "Skip unattended installation", then click "Next".
5. In the "Hardware" pane, change "Base Memory" to "4096 MB" (4GB) and slide "Processors" to "2". Click "Next".
6. In the "Virtual Hard disk" pane, set the slider to "25.00 GB". Click "Next", then click "Finish".
7. Click the green "Start" arrow in the top toolbar to boot the VM. Follow the installer: select Language, click "Install Ubuntu", select "Normal installation", choose "Erase disk and install Ubuntu" (this only affects the virtual disk!), set timezone, enter username/password, and restart when installation completes.
We create the VM configuration and execute the Linux installation process to generate a secure, modular workspace independent of our host Windows registry settings.
STEP 3
Map USB Passthrough for Microcontroller
Ensure that the physical USB port connecting your ESP32 or Arduino board is captured directly by the virtual hardware bus.
USB Redirection Steps (VM turned OFF):
1. Plug your ESP32 board into a USB port on your host PC using a data-capable USB cable.
2. In VirtualBox, click the "Ubuntu-IoT" machine name in the left panel, then click the orange "Settings" gear in the top toolbar.
3. In the Settings window, click the "USB" menu option in the left list.
4. Check the box labeled "Enable USB Controller". Select "USB 2.0 (EHCI) Controller" (or USB 3.0).
5. Click the small green "+" icon on the right edge (Add USB Filter).
6. Select your USB-to-UART bridge controller (commonly labeled "Silicon Labs CP2102...", "CH340...", or "FTDI...") from the popup list.
7. Click "OK" at the bottom of the Settings window, then click the green "Start" arrow to boot back into Ubuntu.
We register USB device filters in the hypervisor setup to automatically capture serial interface controllers on insertion, bridging the physical device pinouts to virtual mount points.
STEP 4
Install Arduino IDE inside the Ubuntu Linux VM
Open a terminal window inside Ubuntu (`Ctrl + Alt + T` or click the grid icon on the bottom left of the desktop, type "Terminal", and click on the terminal application) and install the IDE via the Ubuntu snap package manager.
ubuntu@iot-vm:~$ sudo snap install arduino
We invoke `sudo` to gain superuser authorization and run `snap install` to fetch the containerized Arduino IDE package, ensuring clean dependency management inside the guest system.
STEP 5
Grant Serial Dialout Permissions to VM User
Linux security settings prevent ordinary users from accessing raw terminal hardware paths like `/dev/ttyUSB0`. Grant access using the `dialout` group.
ubuntu@iot-vm:~$ sudo usermod -a -G dialout $USER
We execute `usermod` to modify our account, appending the current username to the dialout hardware group to allow read/write serial port access without requiring root privilege.
STEP 6
Apply Dialout Permission Changes
To let the kernel load your newly acquired group memberships, terminate your active desktop login session or reload groups manually.
ubuntu@iot-vm:~$ newgrp dialout
We call the `newgrp` command to reload the current terminal process with active dialout privileges, avoiding the need to execute a full system reboot to unlock serial paths.
STEP 7
Configure ESP32 Board Manager URLs
Configure the Arduino IDE to pull board definitions and toolchains for Espressif systems by registering their indexing server.
Arduino IDE GUI Configuration Steps:
1. Open the Arduino IDE (click the Ubuntu grid icon, type "Arduino", and click the icon).
2. Go to the top menu bar, click "File" -> "Preferences...".
3. In the Preferences dialog, locate the field "Additional Boards Manager URLs".
4. Copy and paste this exact link into the text area:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
5. Click "OK" at the bottom of the Preferences dialog window.
We register the Espressif package URL to instruct the IDE where to retrieve compiler target files, configurations, and upload tools for the ESP32 platform family.
STEP 8
Download and Install the ESP32 Board package
Trigger the download of compilers and tool definitions required to translate code to ESP32 dual-core processors.
Arduino IDE GUI Download Steps:
1. In the Arduino IDE left vertical toolbar, click on the "Boards Manager" icon (the second icon from the top, resembling a chip).
2. In the "Filter your search" text box, type "esp32".
3. Find the entry labeled "esp32" by Espressif Systems.
4. Click the "Install" button inside the esp32 box. Wait for the download progress bar to finish.
We download and unpack the specific ESP32 compiler toolchains (such as `xtensa-esp32-elf-gcc`) inside the local IDE directories to generate binaries targeted to the board's instruction set.
STEP 9
Select Your Hardware Board Model & Port
Tell the compiler which exact board configuration to compile for, and map the active serial upload port.
Arduino IDE GUI Hardware Selection Steps:
1. Go to "Tools" -> "Board" -> "esp32" -> Select "ESP32 Dev Module".
2. Go to "Tools" -> "Port" -> Select the active serial device.
On Linux, this will show up as "/dev/ttyUSB0" or "/dev/ttyACM0" (unlike Windows which uses "COM" labels).
3. If no ports are visible, check your USB filter mapping in VirtualBox (Step 3) and verify that the board's power light is lit.
We designate the target board model and device path to ensure compiler configurations match the board's GPIO addresses and the programmer uses correct baudrates.
3. Firmware Build & Upload Pipeline Flow
The code compiling and flashing process runs through a structured compilation and link pipeline. The pipeline layout below shows how code written in the editor is translated, verified, and pushed onto the flash memory banks of the microcontroller.
4. Part 2: Complete Firmware Script Template & Line-by-Line Breakdown
To implement this setup verification task, write the C++ code inside the Arduino editor. Review the step-by-step description below to understand how the microcontroller handles internal register writes.
Line-by-Line Code Breakdown
const int LED_PIN = 2;: Reserves a constant integer variable naming GPIO Pin 2 as our LED pin. (GPIO 2 connects to the built-in blue LED on most ESP32 dev boards).
void setup() { ... }: Defines the mandatory startup function, run exactly once by the microcontroller CPU immediately after power-on.
pinMode(LED_PIN, OUTPUT);: Configures the specified GPIO pin to act in output mode, allowing it to drive current out to components rather than read incoming voltages.
void loop() { ... }: Defines the main running loop. After setup() completes, the CPU runs loop() continuously from top to bottom forever.
digitalWrite(LED_PIN, HIGH);: Writes a digital HIGH state (3.3V) to GPIO Pin 2, supplying voltage to light the LED.
delay(1000);: Instructs the CPU cores to pause operations for 1000 milliseconds (1 second), keeping the LED in its current lit state.
digitalWrite(LED_PIN, LOW);: Writes a digital LOW state (0V/GND) to GPIO Pin 2, cutting off voltage to extinguish the LED.
delay(1000);: Pauses execution for another 1000 milliseconds, keeping the LED dark before restarting the loop sequence.
Complete Flashable Firmware
// Setup Project 2: Microcontroller & IDE Setup// Blinks the onboard LED of an ESP32 board on a 1-second interval.constintLED_PIN = 2; // GPIO Pin 2 is connected to the onboard blue LED on ESP32voidsetup() {
// Configure the LED pin as an OUTPUT so it can drive current outpinMode(LED_PIN, OUTPUT);
}
voidloop() {
// Turn the LED on (HIGH voltage level)digitalWrite(LED_PIN, HIGH);
// Wait for 1 second (1000 milliseconds)delay(1000);
// Turn the LED off by making the voltage LOW (0V)digitalWrite(LED_PIN, LOW);
// Wait for 1 seconddelay(1000);
}
5. Deliverables Summary
Created Artifacts
Compiled and saved `Blink.ino` sketch folder.
Detailed documentation of active serial ports shown by running dmesg | grep tty in the Linux terminal.
Verification Proof
A video snippet or screenshot of the ESP32 board blinking its blue built-in LED at a clean 1Hz frequency.
Screenshot of the Arduino IDE console showing a successful compilation and 100% upload output matching the device memory.
6. Closing Explanation: Why We Did This & What It Accomplishes
Architectural Intent & Operational Impact
Why We Did This
Virtualizing development environments prevents system files and drivers from cluttering your primary Windows registry, making backup and restoration easy.
Mapping direct USB filters allows microcontrollers to interface directly with development compilers while leveraging host driver layers.
Standardizing on Linux-based snaps provides identical development platforms for all team members, reducing the "works on my machine" class of errors.
What This Accomplishes
Verifies serial communication paths, ensuring that compiler utilities like `esptool.py` can upload binaries to hardware registers.
Validates board manager configurations, ensuring that correct CPU architecture compiler versions are used for binary compilation.
Bootstraps basic embedded hardware interaction, paving the way for multi-sensor readings and MQTT networking scripts.