Skip to content

4. Embedded Programming

group assignment:

  • demonstrate and compare the toolchains and development workflows
     for available embedded architectures

Go to Group Assignment

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

RP2040 Datasheet

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 Datasheet

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

ATMega Datasheet

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.

Device Manager

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.

motion_Sensor_with_PIRSensor

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