Implement an LED-controller and button-reader program in C++ and Python across multiple virtual and hardware systems.
Domain
Cross-Platform Embedded
Difficulty
⭐⭐☆☆☆ (Moderate)
Course Module
Hardware Platforms
Deliverables
Source Code Suite, Capability Matrix
1. Multi-Platform Hardware Pinout & OS Comparison
Different IoT deployments call for different hardware platforms. We categorize microcontroller architectures into two main fields: bare-metal systems (such as Arduino or ESP32) and full operating system computers (such as Raspberry Pi). The schematic below compares the hardware specifications, CPU properties, operating systems, and pin assignments between a standard ESP32 module and a Raspberry Pi. It demonstrates how high-level operating system layers affect GPIO pin interactions.
2. Part 1: Step-by-Step Individual Commands & GUI Steps
Follow these detailed steps to set up your virtual Python environment inside the VirtualBox Linux machine, draft your code, compile the ESP32 files, and run the Python simulator suite.
STEP 1
Launch Terminal and Create Project Directory
Boot your VirtualBox Ubuntu machine. Open the Ubuntu terminal and structure a clean directory workspace for the board comparison project.
We run `mkdir -p` to verify and generate a clean workspace directory tree at the path `~/workspace/board_comparison` without throwing errors if the parent directories already exist.
STEP 2
Navigate to the Project Folder
Move your current command shell context into the newly generated folder to manage output files locally.
ubuntu@iot-vm:~$ cd ~/workspace/board_comparison
We execute `cd` to change the working directory path of our terminal process, ensuring all subsequent scripts and source code files are created inside the isolated comparison folder.
STEP 3
Install Python Compiler and Virtual Environment Tools
Ensure that the latest Python development packages and pip installation systems are deployed in the OS packages system.
We run `sudo apt update` to refresh the package database list, followed by `apt install` to load Python 3, pip library manager, and virtual environment modules on our VM.
STEP 4
Generate Isolated Python Virtual Environment
Create a sandbox folder containing Python libraries to prevent script execution packages from conflicting with system libraries.
We run `python3 -m venv env` to create an isolated environment directory named `env` containing a copy of the Python interpreter, protecting our VM's global configuration.
STEP 5
Activate the Virtual Environment
Instruct the current shell to look for Python execution files inside the local project folder path instead of standard root directories.
We run the `source` command against the activation shell script, modifying terminal PATH variables so that running python packages uses the local virtual environment folders.
STEP 6
Open Text Editor for Python Script Configuration
Invoke the graphical editor "gedit" (or terminal editor "nano") to draft the cross-platform simulated input-output code.
We invoke `gedit` alongside the `&` symbol to open the graphical text editor in a background thread, leaving our terminal terminal prompt active for running commands.
STEP 7
Execute the Python Board Simulation
Execute the Python program. Since we are in VirtualBox, the code will load a mock shell system enabling you to trigger virtual button clicks on your keyboard.
We execute `python3` to run the comparison file, launching the loop that polls console keypresses and outputs simulated LED states.
3. Multi-Language Program Logic Execution Pipeline
The core logic of reading a physical button pin state and outputting an LED signal follows a similar software flow across both C++ (microcontroller) and Python (single-board computer). Below is the logical comparison showing the parallel pipelines of execution.
4. Part 2: Source Code Suite & Line-by-Line Breakdown
Below are the two complete, production-ready code files. The first is written in C++ for the Arduino/ESP32 platforms, and the second is written in Python with a self-contained GUI mock layer that allows local testing within your VirtualBox environment.
Asset 1: ESP32/Arduino C++ Source Code (`blink_button.ino`)
Line-by-Line Code Breakdown
const int LED_PIN = 2;: Reserves a constant integer variable mapping GPIO Pin 2 as the onboard LED pin.
const int BUTTON_PIN = 4;: Reserves GPIO Pin 4 for monitoring our tactile button switch input.
pinMode(LED_PIN, OUTPUT);: Sets our LED pin as an electrical driver output.
pinMode(BUTTON_PIN, INPUT_PULLUP);: Configures the button pin with an internal resistor pulling it to 3.3V (HIGH) by default. When the button is pressed, it shorts to ground, reading 0V (LOW).
int val = digitalRead(BUTTON_PIN);: Queries the physical voltage level on GPIO 4, returning HIGH or LOW.
if (val == LOW) { digitalWrite(LED_PIN, HIGH); }: Checks if the switch is pressed (LOW state). If true, it writes HIGH (3.3V) to illuminate the onboard LED.
else { digitalWrite(LED_PIN, LOW); }: Runs if the button is released. Writes LOW (0V) to turn the LED off.
// Practice Project 2: ESP32/Arduino Input/Output C++ CodeconstintLED_PIN = 2; // Onboard LED GPIO PinconstintBUTTON_PIN = 4; // Button input pin with pull-up configurationvoidsetup() {
// Configure onboard LED as outputpinMode(LED_PIN, OUTPUT);
// Configure button pin with internal pull-up resistorpinMode(BUTTON_PIN, INPUT_PULLUP);
}
voidloop() {
// Read state of the input pinintbtnState = digitalRead(BUTTON_PIN);
// Active-LOW configuration: button connects to GND when pressedif (btnState == LOW) {
digitalWrite(LED_PIN, HIGH); // Turn on LED
} else {
digitalWrite(LED_PIN, LOW); // Turn off LED
}
// Small delay to stabilize readingsdelay(50);
}
Asset 2: Python / Linux Simulation Script (`comparison.py`)
Line-by-Line Code Breakdown
class MockGPIO:: Declares a mock class that emulates the behavior of the `RPi.GPIO` library. This allows you to test the script inside a VirtualBox Linux VM without needing physical Raspberry Pi pins.
input(self, pin): Returns the simulated state of the pin. Pressing 'p' in the console updates this state to simulate a button press.
output(self, pin, state): Prints the simulated LED state directly to the terminal console interface.
time.sleep(0.1): Pauses the script execution loop for 100 milliseconds to reduce processor utilization.
# Practice Project 2: Python/Linux Input/Output Simulation ScriptimportsysimporttimeimportselectimportttyimporttermiosclassMockGPIO:
"Mock GPIO simulator for VirtualBox Ubuntu environments"BCM = "BCM"IN = "INPUT"OUT = "OUTPUT"PUD_UP = "PULL_UP"def__init__(self):
self._button_pressed = Falseself._led_state = Falsedefsetmode(self, mode):
passdefsetup(self, pin, direction, pull_up_down=None):
passdefinput(self, pin):
# Returns False if pressed (simulating active-LOW configuration)returnnotself._button_presseddefoutput(self, pin, state):
ifself._led_state != state:
self._led_state = stateprint(f"\r[LED STATE] Onboard LED on pin {pin} is now: {'ON' if state else 'OFF'}")
defis_key_pressed():
returnselect.select([sys.stdin], [], [], 0.05)[0] != []
defmain():
GPIO = MockGPIO()
GPIO.setmode(GPIO.BCM)
LED_PIN = 18
BUTTON_PIN = 23
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
print("==================================================")
print(" Python GPIO Input/Output Simulator Running ")
print(" Simulating on VirtualBox Ubuntu Guest OS ")
print("==================================================")
print("Instructions:")
print(" - Press and hold 'p' then hit [Enter] to press button.")
print(" - Press any other key to release button.")
print(" - Press Ctrl+C to stop simulation.")
print("--------------------------------------------------")
try:
whileTrue:
ifis_key_pressed():
key = sys.stdin.readline().strip()
ifkey == 'p':
GPIO._button_pressed = Trueelse:
GPIO._button_pressed = False# Read simulated digital statebtnState = GPIO.input(BUTTON_PIN)
# If button is pressed (state is False due to active-low)ifbtnState == False:
GPIO.output(LED_PIN, True)
else:
GPIO.output(LED_PIN, False)
time.sleep(0.1)
exceptKeyboardInterrupt:
print("\nSimulation stopped by user.")
if__name__ == "__main__":
main()
A video snippet showing the ESP32 built-in LED turning ON when you press the physical button.
A screenshot of the Ubuntu terminal running the Python simulation program, showing the LED state logs toggle when you enter 'p'.
6. Closing Explanation: Why We Did This & What It Accomplishes
Architectural Intent & Operational Impact
Why We Did This
Developing across multiple platforms allows developers to understand the trade-offs between low-cost bare metal boards and full microcomputers before committing to production hardware.
Structuring hardware-independent mocks allows software layers to be compiled and tested on generic virtual machines before being deployed to physical devices.
Comparing pull-up and pull-down configurations across platforms highlights the need for consistent logic mapping when working with different hardware.
What This Accomplishes
Builds core cross-language hardware programming skills (C++ and Python), which are essential for developing both on-device code and gateway scripts.
Saves development time by introducing local simulation pipelines, allowing you to debug application logic when physical hardware is unavailable.
Prepares you to make architectural choices between microcontrollers and full Linux nodes for complex IoT projects.