4. Embedded Programming¶

group assignment:¶
• demonstrate and compare the toolchains and development workflows
for available embedded architectures
Individual assignment:¶
• browse through the data sheet for a microcontroller
• write and test a program for an embedded system using a microcontroller
to interact (with input &/or output devices)
and communicate (with wired or wireless connections)
• extra credit: assemble the system
• extra credit: try different languages &/or development environments
Microcontroller Datasheets¶
Week four is to understand microntrollers and programming. Microcontrollers connect the world of software to the world of hardware. They allow developers to write software which interacts with the physical world in the same deterministic, cycle-accurate manner as digital logic. They occupy the bottom left corner of the price/performance space, outselling their more powerful brethren by a factor of ten to one. They are the workhorses that power the digital transformation of our world.
I browse throuh the datasheet for various microcontrollers: RP2040,ESP32,ATMega328p and found the look for similarities and differences between the chips.Including speed,memory,GPIO pin layout, companies....
RP2040¶
The RP2040 is a microcontroller designed by Raspberry Pi Ltd, a UK technology company founded by Eben Upton. It was created to provide a low-cost, high-performance, and easy-to-use chip for embedded systems. The RP2040 runs at up to 133 MHz and contains two ARM Cortex-M0+ processor cores, meaning it can handle tasks in parallel. It has 264 KB of on-chip SRAM (Static RAM) for fast data access, but it does not contain internal Flash memory — instead, it supports up to 16 MB of external QSPI Flash for program storage. The chip includes 30 GPIO pins, which allow it to connect to sensors, motors, LEDs, displays, and other hardware components. It also provides communication interfaces like UART, SPI, I2C, USB, PWM channels, ADC inputs, DMA controller, and a unique Programmable I/O (PIO) system for flexible hardware control. The RP2040 uses SRAM (not DRAM) because microcontrollers require fast, simple, low-power memory without refresh cycles. It is widely used in boards like the Raspberry Pi Pico and is popular in robotics, IoT, automation, agriculture systems, and embedded product development.

The RP2040 comes in a 56-pin QFN package and provides 30 multifunction GPIO pins (GPIO0–GPIO29). These pins can be configured as digital inputs or outputs, or assigned to communication functions such as UART, SPI, I2C, PWM, ADC, USB, and PIO through software. In addition to the GPIO pins, it has dedicated pins for 3.3V power, ground, external QSPI Flash memory, and SWD debugging. The pin layout is flexible because most peripheral functions can be mapped to different GPIO pins, which makes hardware design and PCB development easier.

ESP32¶
ESP32 is a single 2.4 GHz Wi-Fi-and-Bluetooth combo chip designed with the TSMC low-power 40 nm technology. It is designed to achieve the best power and RF(radio frequency) performance, showing robustness, versatility and reliability in a wide variety of applications and power scenarios.
The ESP32 is a Wi-Fi and Bluetooth microcontroller from Espressif Systems for IoT and embedded applications. It has a single- or dual-core 32-bit Xtensa LX6 processor up to 240 MHz, 520 KB SRAM, 448 KB ROM, and supports external flash and PSRAM via QSPI. The chip has 34 GPIO pins that can be used for digital input/output or special functions like ADC, DAC, SPI, I2C, UART, PWM, and touch sensors. Some pins are input-only, reserved for flash/PSRAM, or used for boot and debugging. ESP32 supports multiple power-saving modes and strong security features including secure boot and encryption. Its wireless connectivity and flexible GPIOs make it ideal for smart homes, robotics, industrial automation, and IoT devices.
Features breakdown¶
| Category | Key Features |
|---|---|
| Wi-Fi | 802.11 b/g/n (2.4 GHz), up to 150 Mbps, WMM, A-MPDU/A-MSDU, Block ACK, Defragmentation, Automatic Beacon monitoring, 4 virtual interfaces, Station/SoftAP/Promiscuous modes, Antenna diversity |
| Bluetooth | Bluetooth v4.2 BR/EDR + BLE, Class 1/2/3 transmitter, +9 dBm TX power, –94 dBm BLE sensitivity, AFH, HCI (SDIO/SPI/UART), Dual-mode controller, Multi-connections, Audio codec support (CVSD, SBC) |
| CPU & Memory | Xtensa single/dual-core 32-bit LX6 up to 240 MHz, 448 KB ROM, 520 KB SRAM, 16 KB RTC SRAM, QSPI flash/PSRAM support |
| Clocks & Timers | Internal 8 MHz oscillator, Internal RC oscillator, External 2–60 MHz crystal, 32 kHz RTC crystal, 2 timer groups (64-bit timers + watchdog), RTC timer & watchdog |
| GPIO & Interfaces | 34 GPIOs, 12-bit ADC (up to 18 channels), 2 DAC, 10 touch sensors, 4 SPI, 2 I2C, 2 I2S, 3 UART, SDIO host/slave, CAN (TWAI), Ethernet MAC, PWM (Motor + 16 LED channels), RMT |
| Power Management | Multiple power modes (Active, Modem-sleep, Light-sleep, Deep-sleep, Hibernation), 10 µA in deep sleep, ULP coprocessor, RTC memory retained |
| Security | Secure boot, Flash encryption, 1024-bit OTP, AES, SHA-2, RSA, Hardware RNG |
| Applications | Smart Home, Industrial Automation, Health Care, Agriculture, Robotics, IoT sensor hubs, Audio devices, Cameras, Speech/Image recognition |
802.11b/g/n (2.4 GHz) are Wi-Fi standards that define how devices communicate wirelessly:
802.11b – Older standard, 2.4 GHz, up to 11 Mbps, long range but slow
802.11g – 2.4 GHz, up to 54 Mbps, backward compatible with 802.11b
802.11n – 2.4 GHz, up to 150 Mbps per stream, supports multiple antennas (MIMO) for faster and more reliable connections
Security:
Wi-Fi security encrypts data to prevent unauthorized access.
Common methods: WPA/WPA2, providing strong protection for 802.11b/g/n networks.
ESP32 supports WPA/WPA2 to securely connect to Wi-Fi networks.
In short: 802.11b/g/n controls Wi-Fi speed and range, while security protocols keep your data safe when using these networks.
pinlayout¶
The ESP32 comes in QFN packages with up to 49 pins, including 34 general-purpose GPIOs. These pins can be configured for digital input/output or assigned to functions like ADC, DAC, SPI, I2C, UART, PWM, touch sensors, and Ethernet MAC. Some pins have special purposes or restrictions: a few are input-only, some are used for internal flash/PSRAM, and others serve as strapping, JTAG, or UART pins for boot configuration and debugging.
There are also RTC_GPIO pins that work in low-power modes, useful when the ESP32 is in deep sleep. The chip includes dedicated power pins (VDDA, VDD3P3, VDD_SDIO), analog pins for sensors and capacitors (SENSOR_VP, SENSOR_VN, CAP1, CAP2), and crystal oscillator pins (XTAL_P, XTAL_N) for stable clock operation.
ATMega¶
ATmega328P is a low-power CMOS 8-bit microcontroller based on the AVR® enhanced RISC architecture. By executing powerful instructions in a single clock cycle, the ATmega328P achieves throughputs approaching 1MIPS per MHz allowing the system designer to optimize power consumption versus processing speed
It is an 8-bit AVR microcontroller from Microchip (originally Atmel) designed for high performance and low power applications. It uses an advanced RISC architecture with 131 instructions, most executing in a single clock cycle, and has 32 general-purpose registers. It can achieve up to 16 MIPS at 16 MHz.
The chip has 32 KB of in-system programmable flash, 1 KB EEPROM, and 2 KB SRAM, with high endurance for long-lasting applications. It also supports optional boot code sections, software security with lock bits, and true read-while-write operations.
Peripheral features include two 8-bit timers, one 16-bit timer, a real-time counter, six PWM channels, an 8-channel 10-bit ADC, temperature measurement, USART, SPI, and I²C interfaces, an analog comparator, and a programmable watchdog timer. It also has interrupts on pin change and multiple sleep modes for low power applications.
The ATmega328P provides 23 programmable I/O pins and comes in 32-lead TQFP or QFN/MLF packages. It operates at voltages from 2.7V to 5.5V and supports temperatures from –40°C to +125°C. Power consumption is very low, with about 1.5 mA in active mode and 1 µA in power-down mode.
Because of its balance of performance, low power, and flexible peripherals, the ATmega328P is widely used in Arduino boards, embedded systems, robotics, and general-purpose electronics projects.
ATmega328P Microcontroller Overview¶
The ATmega328P is an 8-bit AVR microcontroller designed for embedded applications. It is high-performance, low-power, and supports multiple peripherals and memory types.
| Category | Features & Description |
|---|---|
| Core & Architecture | AVR 8-bit RISC CPU, 131 instructions (most single-cycle), up to 16 MIPS at 16 MHz, on-chip 2-cycle multiplier |
| Memories | Flash Program Memory: 32 KB in-system programmable, divided into bootloader and application sections, 10,000 write/erase cycles SRAM: 2 KB internal SRAM for temporary data, includes 32 registers, 64 I/O registers, 160 extended I/O registers EEPROM: 1 KB non-volatile, byte-accessible, 100,000 write/erase cycles |
| Peripherals | 2 × 8-bit timers, 1 × 16-bit timer, 6 PWM channels, 8-channel 10-bit ADC, temperature sensor, USART, SPI, I2C, analog comparator, watchdog timer, interrupts on pin change |
| I/O & Packages | 23 programmable I/O pins, 32-lead TQFP & 32-pad QFN/MLF packages, supports direct/indirect access modes |
| Power | Operating voltage 2.7–5.5 V, low power consumption: 1.5 mA active at 3V/4MHz, 1 µA power-down mode, six sleep modes |
| Security | Programming lock for software protection, supports read-while-write operation for flash |
| Clocking | Internal 8 MHz RC oscillator (default 1 MHz system clock), external crystal options, asynchronous timer clock, ADC clock, Flash clock, CPU clock, brown-out detection support |
| Special Features | True read-while-write flash, optional boot section, high endurance memory segments, flexible memory addressing, accurate ADC conversions using dedicated clocks |
Pinlayout¶

Comparison of RP2040, ESP32, and ATmega328P Microcontrollers¶
| Feature / Category | RP2040 | ESP32 | ATmega328P | Similarities / Differences Summary |
|---|---|---|---|---|
| Manufacturer | Raspberry Pi Ltd (UK) | Espressif Systems | Microchip / Atmel | All are widely used for embedded development; different company origins and ecosystems |
| Core & Architecture | Dual-core ARM Cortex-M0+ @ 133 MHz | Single/Dual-core 32-bit Xtensa LX6 @ 240 MHz | 8-bit AVR RISC, 16 MIPS @ 16 MHz | RP2040 & ESP32 are 32-bit high-performance; ATmega328P is 8-bit low-power |
| Flash Memory | External QSPI Flash up to 16 MB | External QSPI Flash / PSRAM supported | 32 KB in-system programmable flash | RP2040 & ESP32 rely on external flash; ATmega328P has internal flash |
| SRAM / RAM | 264 KB on-chip SRAM | 520 KB SRAM + 16 KB RTC SRAM | 2 KB internal SRAM | ESP32 has largest RAM; ATmega328P smallest; RP2040 moderate |
| EEPROM | None | None | 1 KB non-volatile EEPROM | Only ATmega328P provides internal EEPROM |
| GPIO Pins | 30 multifunction pins (digital/analog/I2C/SPI/PWM/PIO) | 34 GPIO pins, supports ADC/DAC/SPI/I2C/UART/PWM/touch | 23 programmable I/O pins | All support flexible I/O; ESP32 has most GPIO and peripherals |
| ADC | 12-bit, up to 4 channels | 12-bit, up to 18 channels | 10-bit, 8 channels | ESP32 provides most ADC channels and higher resolution |
| Communication Interfaces | UART, SPI, I2C, USB, PIO | UART, SPI, I2C, I2S, CAN, Ethernet MAC | USART, SPI, I2C | All support standard embedded comms; ESP32 supports advanced wired protocols |
| Wireless Connectivity | None | Wi-Fi 802.11b/g/n, Bluetooth v4.2 | None | Only ESP32 has built-in wireless |
| Timers / PWM | Multiple PWM channels, DMA support | 64-bit timers, PWM motor + 16 LED channels | 2 × 8-bit timers, 1 × 16-bit timer, 6 PWM channels | All support PWM; ESP32 and RP2040 more advanced and flexible |
| Power & Sleep Modes | Multiple sleep modes, low-power design | Active, Modem-sleep, Light-sleep, Deep-sleep, Hibernation | 6 sleep modes, ultra-low-power | All offer low-power modes; ESP32 has ULP coprocessor for deep sleep |
| Security Features | Basic (PIO for secure I/O control) | Secure boot, Flash encryption, AES, SHA-2, RSA | Programming lock for software protection | ESP32 strongest security; ATmega328P moderate; RP2040 minimal |
| Operating Voltage | 1.8–3.3V | 2.2–3.6V | 2.7–5.5V | RP2040 and ESP32 operate near 3.3V; ATmega328P wider voltage range |
| Applications | Robotics, IoT, automation, Pico boards | IoT, Smart Home, Industrial automation, robotics | Arduino projects, embedded systems, robotics | All used in embedded applications; ESP32 excels in wireless IoT; RP2040 in low-cost high-speed; ATmega328P in classic Arduino setups |
What is Programming¶
Programming is the process of writing structured computer instructions that a microcontroller can execute in order to control hardware and perform specific tasks. These instructions are written using programming languages, which can generally be grouped into low-level and high-level languages. Low-level languages, such as machine code and assembly, interact directly with hardware registers and CPU instructions, giving the programmer precise control over timing, memory usage, and peripherals, but they are harder to write and maintain. High-level languages, such as C and C++, provide more abstraction, readability, and faster development, and are therefore commonly used in embedded systems. However, when using high-level languages, the programmer must still be aware of hardware constraints such as program memory (Flash), data memory (SRAM), clock speed, and peripheral availability, since microcontrollers have limited resources and no operating system to manage them automatically.
To write and manage code, a programmer uses a text editor or an integrated development environment (IDE), which provides tools for writing, compiling, and uploading programs. Some editors are language-specific, while others support multiple languages. For example, Arduino-based development commonly uses the Arduino IDE, which simplifies coding and uploading programs. In addition, simulation platforms such as Tinkercad provide built-in code editors and virtual hardware, allowing programs to be written, tested, and debugged before deploying them to real microcontroller hardware.
Simulation¶
I created a simple security system using an Arduino Uno, a PIR motion sensor, and a buzzer. In the simulation on TinkerCad, the PIR detects motion and the Arduino triggers the buzzer, showing how a basic alert system works before moving to a real board.
TinkerCad¶

TinkerCad is a versatile platform where you can design, build circuits, and program microcontrollers. I love it because it allows engineers to practice and test systems before moving to real production.
After creating an account, I followed these steps to make my security system, where an alarm sounds when someone approaches the door.
Step 1: Create Circuit¶
-Go to Create and select Circuits.
From the circuits library, drag the required components onto the workspace, including the Arduino,Pir motion sensor,breadboard,

Arrange the components neatly and prepare them for wiring:


Step 2: Coding¶
Click the Code section. From the dropdown, you can choose Blocks or Text. Block coding is often used by beginners, but I chose text mode and programmed my system using C++.
my code;
// Define pin connections
const int pirPin = 2;
const int buzzerPin = 13;
void setup() {
pinMode(pirPin, INPUT);
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() { int motionState = digitalRead(pirPin);
if (motionState == HIGH) { // Motion detected digitalWrite(buzzerPin, HIGH); Serial.println(“Motion detected!”); } else { // No motion digitalWrite(buzzerPin, LOW); Serial.println(“No motion”); }
delay(200); // Small delay to avoid spamming }
This code is written in C++, a low-level language that is highly efficient for embedded systems. C++ provides precise control over memory and system resources, making it ideal for Arduino projects.
Code Structure¶
Variables
const int pirPin = 2; // PIR sensor input const int buzzerPin = 13; // Buzzer output
Stores the pins used for the PIR sensor and buzzer.
const int pirPin = 2; // PIR sensor input const int buzzerPin = 13; // Buzzer output
Store the Arduino pins used for the PIR sensor and buzzer.
setup()
void setup() { pinMode(pirPin, INPUT); pinMode(buzzerPin, OUTPUT); Serial.begin(9600); }
Runs once at startup.
Sets PIR pin as input, buzzer pin as output.
Starts serial communication for debugging.
loop()
void loop() { int motionState = digitalRead(pirPin);
if (motionState == HIGH) { digitalWrite(buzzerPin, HIGH); Serial.println(“Motion detected!”); } else { digitalWrite(buzzerPin, LOW); Serial.println(“No motion”); }
delay(200); }
Runs repeatedly.
Reads the PIR sensor.
Turns buzzer on if motion is detected, off if not.
Prints status to Serial Monitor.
Delay prevents message spamming.
Step 3: Simulation¶
To simulate the system, click Simulation. The buzzer sounds when motion is detected by the PIR sensor.
The PIR sensor detects motion using infrared radiation, triggering the Arduino to turn the buzzer on.

Another advantage of tinker you can save you cirtuit diagram by simply licking the schematic view


and later save as pdf.
Arduino IDE
Now I have a working code in simulator, and this makes it easy for me to implement it directly using the same wiring, same code and same components, the only thing that is going to change is the editor,and perharps power source which am still going to use my laptops. When i connect my arudino to a laptop using a USB cable, the board receives 5V DC power directly from the laptops USB port. A standard USB 2.0 port typically supplies 5V at upto 500mA. This 5V line enters the board through the USB connector and passes through protection and regulation circuitry before powering the micontroller and other components.
Step 1: Download the Arduino IDE Get the IDE from offcial arduino website Arduino IDE
During download, choose 64-bit version if your computer runs a 64 bit OS, since 64bit processor can handle larger memory over 4GB and wider data processing allowing software like Arduino IDE to run more efficinetly. also clic just download if you are prompt to pay or donute unless you want to donete or pay.
Step 2: Installation
Double click the IDE, agree with terms and conditions, and licence thn complete the process.
Step 3: Connecting your arduino board
After successfully installing and opening the Arduino IDE, connect your Arduino board to your laptop using a USB Type-B to Type-A cable, which is the standard square-to-rectangular USB cable used by most Arduino boards. You should hear a notification sound from your computer if the connection is successful. To verify, check the Device Manager (on Windows) or the equivalent system tool on your operating system to see if a new COM/serial port appears. A using windows so i just cliked the start key logo the typed device manager.. For extra confirmation, try unplugging and reconnecting the USB cable—if the port appears and disappears accordingly, the connection is working properly.

Step 4: Configure the IDE to communicate with Arduino Board
- Open the Arduino IDE on your computer.
- Go to the Tools menu at the top of the IDE.
- Select Board → Boards Manager to make sure your board type is installed. Most common boards, like the Arduino Uno, are already included.
- From the Tools → Board menu, select the exact model of your Arduino board (e.g., Arduino Uno) so the IDE knows the correct processor and memory configuration.
- Go to Tools → Port and select the COM/serial port that corresponds to your Arduino board. On Windows, check Device Manager under Ports (COM & LPT);
- To confirm the connection, open the Serial Monitor from Tools → Serial Monitor. If the board is connected correctly, it should respond when a program is uploaded.
- Optionally, check the board’s example programs by going to File → Examples → 01.Basics → Blink. Upload this example; if the onboard LED blinks, your IDE is properly configured.
Step 5: Updating Libraries
-
Libraries are pre-written collections of code that provide additional functionality to Arduino programs, allowing you to control sensors, motors, displays, communication modules, and more without writing low-level code from scratch. Using libraries makes programming easier, faster, and more reliable.
-
Open the Arduino IDE and go to the menu Sketch → Include Library → Manage Libraries. This opens the Library Manager, where you can browse, install, or update libraries.
-
To install a new library:
- Use the search bar to find the library you need, for example, “Servo” for controlling servo motors or “Adafruit_Sensor” for sensor integration.
- Click the library, then click the Install button. The IDE will download and add it to your Arduino library folder automatically.
-
Installing a library ensures your code can compile and interact with specific hardware correctly.
-
To update an existing library:
- In the Library Manager, check if the library has an Update button available. If so, click it to get the latest version.
-
Updating libraries ensures that you have bug fixes, new features, and compatibility improvements for newer Arduino IDE versions or boards.
-
To include a library in your sketch:
- Add
#include <LibraryName.h>at the top of your code. For example:cpp #include <Servo.h> // Include the Servo library -
Including a library tells the compiler to use the functions and definitions provided by that library in your program.
-
Reasons to manage libraries before writing programs:
- Prevent compilation errors caused by missing libraries.
- Ensure compatibility with your Arduino board and IDE version.
- Access advanced functions without writing complex low-level code.
-
Save time and make code more readable and maintainable.
-
For my project no library needed since it uses arduino inbuilt functions such as pinMode(), digitalRead(), digitalWrite(), Serial.begin(), and delay().
-
Practical tip: Always check the library documentation for required wiring, initialization commands, or functions. Some libraries only work with specific versions of Arduino boards or certain pins.
Step 6: Write my program
*my code;
// Define pin connections const int pirPin = 2; const int buzzerPin = 13;
void setup() { pinMode(pirPin, INPUT); pinMode(buzzerPin, OUTPUT); Serial.begin(9600); }
void loop() { int motionState = digitalRead(pirPin);
if (motionState == HIGH) { // Motion detected digitalWrite(buzzerPin, HIGH); Serial.println(“Motion detected!”); } else { // No motion digitalWrite(buzzerPin, LOW); Serial.println(“No motion”); }
delay(200); // Small delay to avoid spamming }*
This project uses a PIR motion sensor and a buzzer, originally developed in Tinkercad. To make the code cleaner and easier to maintain, it is organized using a header file (.h) and a main implementation file (.cpp). The header file contains pin definitions, constants, and function declarations, acting as a blueprint, while the .cpp file contains the actual logic in setup and loop functions. This modular approach improves readability, reusability, and makes it easier to expand the project. Unlike libraries, which are pre-written external code for sensors or modules, header files help structure your own code and clearly separate what the program does from how it does it.
I need to install the VS Code editor from vscode , although I could alternatively use any text editor. This is necessary because I want to save my files as .cpp in the project folder. The Arduino IDE automatically creates a .ino file when creating a new sketch, but for this project I need a .cpp file to maintain a proper modular structure.
During installation, make sure to check the option to add VS Code to the system PATH. Once installed, open VS Code and go to File → Open Folder, then locate the project folder you created and click Open. In VS Code, move your cursor over the folder name in the Explorer panel, right-click or use the menu to create a new file, and name it main.cpp. Repeat this process to create the other files, naming them MotionSensor.cpp and MotionSensor.h.

MotionSensor.h
This file contains the pin definitions and function prototypes. Think of it as a blueprint for your main program.

MotionSensor.cpp
This file contains the actual logic of my functions declared in the header file.

Main.cpp
This file contains the setup() and loop() functions, which Arduino automatically runs. It includes your header file to access the modular functions.

Open the whole folder in arduino IDE,
Arduino IDE expects a .ino file as the main sketch to recognize a folder as a valid project. Since our project was originally organized with .cpp and .h files, we did not have a .ino file, so the IDE could not open the folder directly. To solve this, I created a main.ino file that includes the necessary header files (MotionSensor.h) and calls the functions implemented in MotionSensor.cpp.
When you try to open the project folder in Arduino IDE, you may encounter a prompt asking you to create a folder named main. Clicking “OK” will create this folder, and you should move all your files (main.ino, MotionSensor.cpp, and MotionSensor.h) into the newly created folder. After this, opening the folder in Arduino IDE will show your files arranged correctly, and the IDE will recognize the project as a valid sketch ready for compilation and upload.
How to Load the program to arduino
Load main.ino, since it contains setup() and loop(). Make sure MotionSensor.h and MotionSensor.cpp are in the same folder.
Select your board type under Tools → Board and the correct COM port under Tools → Port.


Click the Verify/Compile button to check for errors.
Click the Upload button to transfer the program to the Arduino.
Arduino will automatically erase any previous program and run the new code. Remember, the board can run only one program at a time.

Open the Serial Monitor to see output messages and confirm the program is running correctly.