Skip to content

4. Embedded Programming — KAT

This week’s group assignment was to demonstrate and compare the toolchains and development workflows for available embedded architectures. Our group focused on the RP2040, ESP32-C3, and ATTiny412.

Member Role
Kathryn, Amalia, Tyler Documentation
Kathryn ESP32-C3 Toolchain Comparison
Kathryn ESP32-C3 Development Workflows
Tyler ATTiny412 Chip Toolchain Comparison
Tyler ATTiny412 Chip Development Workflows
Amalia RP2040 Chip Toolchain Comparison
Amalia RP2040 Chip Development Workflows

ESP32-C3 – Kathryn Wu

Note: Everything performed in this section was done using Mac OS

I worked on the ESP32C3 and read through the datasheet.

Feature ESP32-C3
Chip ESP32-C3
Architecture RISC-V, 32-bit
CPU Speed Up to 160 MHz
Flash Size 4 MB (embedded flash)
RAM 4 MB (embedded flash)
Wireless Connectivity Wi-Fi 4 (802.11 b/g/n) / Bluetooth 5.0 (BLE)
GPIO Pins 22 (max)
GPIO Voltage 3.3V
Operating Voltage 3.0V-3.6V
Power Supply 3.3V (required)
PWM Up to 16 channels
Debugging JTAG, UART

At first, I was very confused about what a toolchain was, and this basic introduction was helpful. To understand the ESP-IDF toolchain, I looked through the ESP32-C3 Datasheet, the Technical Reference Manual, and Espressif Documentation for ESP-IDF. For Arduino, I referenced its GitHub and a toolchain overview. Overall though, I was already familiar with Arduino, having used it often in the past.


Toolchain Comparison

Feature ESP-IDF Arduino-ESP32
Language Support C, C++ C++ (Arduino API)
Build System CMake, Ninja Arduino IDE/PlatformIO
Operating System FreeRTOS Bare-metal
Debugging OpenOCD, GDB Serial print debugging
Environment Setup Complexity High (ESP-IDF, toolchain, Python dependencies) Low (Arduino IDE, PlatformIO)
Ease of Use Advanced (Requires CLI & CMake knowledge) Beginner-friendly (Simple IDE)
Memory Management Full control over memory, RTOS heap Automatic
Board Support Full ESP32 support (all features) Limited to common ESP32 functionalities
Code Portability High (Works with other ESP-IDF-supported chips) Medium (Limited to Arduino ecosystem)
Extensibility Highly customizable Limited to Arduino framework constraints
Built-in Libraries Espressif official libraries Large Arduino and third-party libraries
Hardware Abstraction Low (configuring drivers required) High (simpler GPIO, PWM handling)
Power Efficiency Advanced power management Basic sleep functions
Peripheral Support Full access to ESP32-C3 peripherals Limited (some advanced peripherals not supported)
WiFi/Bluetooth API Advanced Simplified API
Debugging Support OpenOCD, JTAG, ESP32 debugging tools Basic Serial Monitor
Flashing Method idf.py flash (CLI) One-click “Upload” in Arduino IDE
Serial Monitor idf.py monitor Built-in Arduino Serial Monitor
Security Features Secure Boot, Flash, Encryption, etc. support Minimal security features
Community Support Espressif official forums, GitHub Large Arduino user community
Recommended For Advanced IoT, RTOS Quick prototyping
Project Types Industrial, commercial, complex Hobbyist, maker

Personally, after looking through the comparative, I started to lean towards ESP-IDF. For the same reasons I erred towards MKDocs, it being hard to set up but better to use than HTML most of the time, I did see the light in ESP-IDF. But as all things are, it really just depends on what you want to do.

These are my main takeaways:

Use ESP-IDF if you need fine-grained control, advanced control over features, and optimized performative for professional/complex applications. Also if you want advanced debugging tools with OpenOCD and JTAG. The tradeoff includes a more complex setup and more manual configuration.

Use Arduino if you need a quick, easy way to develop simple applications without worrying about low-level configurations. It offers easily used API for GPIO, WiFi, and Bluetooth. There is a large library ecosystem and cross-compatibility with existing Arduino projects.


Development Workflows

To compare their development workflows, I just pulled from my prevously documented experience programming through both.

Workflow Section Arduino (C/C++) MicroPython (Python)
Project Setup Arduino IDE or PlatformIO Install MicroPython firmware on the microcontrolelr
Writing Code C/C++ with Arduino libraries; .ino files Python; .py files
Compiling Compiled into machine code using compilers None
Uploading Binary is uploaded through USB or programmer flashed first, then scripts are uploaded directly
Execution runs setup() function once then repeatedly runs the loop() function Python starts running immediately after reset or power-on

Arduino

Development Environment Setup:

  1. Download and install the Arduino IDE from the Arduino Website
  2. Open Arduino IDE → Preferences and add the ESP32 board URL
  3. Go to Tools → Board → Boards Manager, search for ESP32 by Espressif, and install it
  4. Select the ESP32-C3 Board in Tools → Board → esp32 → XIAO_ESP32C3
  5. Select the correct COM port under Tools → Port

Write and Compile Code:

  1. Write some code in the Arduino C++ language. For example, a basic blink example (you could also go to File → Examples → 01.Basics → Blink)
void setup() {
    pinMode(2, OUTPUT);  // Configure GPIO2 as an output
}
void loop() {
    digitalWrite(2, HIGH);  // Turn LED on
    delay(500);             // Wait 500ms
    digitalWrite(2, LOW);   // Turn LED off
    delay(500);
}
  1. Compile by clicking the Verify check mark
  2. Upload

This was the pinout sheet I used and my wiring (same for the MicroPython version)

This was the final Arduino C++ code Chat gave me:

int buttonPin = D1;  // Button connected to GPIO 2
int ledPin = D10;    // LED connected to GPIO 10


void setup() {
  pinMode(buttonPin, INPUT);  // Button as input (pull-down handled externally)
  pinMode(ledPin, OUTPUT);    // LED as output
}

void loop() {
  int buttonState = digitalRead(buttonPin); // Read button state


  if (buttonState == HIGH) {  // Button pressed
    digitalWrite(ledPin, HIGH); // Turn LED on
  } else {
    digitalWrite(ledPin, LOW);  // Turn LED off
  }


  delay(10); // Small debounce delay
}

Cooper Cumbus helped me realize that when I define my pins, I should make it D1 instead of just 1 because I am using the digital pins.


MicroPython

I followed this Seeed Studio Tutorial on getting MicroPython working for XIAO ESP32-C3.

I installed esptool.py using the command prompt pip3 install. I followed these instructions to download the firmware binary file. I was putting micropython on my board for the first time, so i needed to clear everything with esptool.py erase_flash. After downloading the latest .bin file available, version 24.1 for me, I used this prompt:

esptool.py --baud 460800 write_flash 0 <insert full file name.bin>

For me, the file name was not enough for it to work and I repeatedly got an error about the file not existing in my directory. I needed to use the entire file path, so /User/.....bin instead.

Then I flashed my firmware on the specific port I was using. To find all of my available ports, I did ls /dev/tty.*. It should be something like /dev/cu.usbmodem### To flash I used this prompt:

esptool.py --chip esp32c3 --port <port name> --baud 460800 write_flash -z 0x0 <file name/location>

For my editor, I chose to use Thonny because I already had it downloaded from when I did MicroPython for the RP2040. Once I opened it, I went into Run → Configure Interpreter and changed the interpreter and port.

I then used these script lines in Shell:

import gc
gc.mem_free()

import esp
esp.flash_size()

To get the flash and SRAM size

I tried this code first:

from machine import Pin
import time

# Define pins
buttonPin = Pin(2, Pin.IN)  # D2 (GPIO3) as input (external pull-down)
ledPin = Pin(10, Pin.OUT)   # D10 (GPIO10) as output

while True:
    buttonState = buttonPin.value()  # Read button state

    if buttonState == 1:  # Button pressed
        ledPin.value(1)  # Turn LED on
    else:
        ledPin.value(0)  # Turn LED off

    time.sleep(0.01)  # Small debounce delay (10ms)

Then remembered that MicroPython used GPIO pins, so the corresponding pins to D1 and D10 should be GPIO3 and 10, respectively (it was also noted in the comments).

Even though I changed the software, I still faced some issues. The button state in Shell would not register as 1 even when I pressed it. I realized that it was a hardware issue and that my button was not properly connected to power. Once I fixed that, it worked!

The button registered correctly, being at a 0 state when unpressed and 1 when I pressed it.

This was the final code I used:

from machine import Pin
import time

# Define pins
buttonPin = Pin(3, Pin.IN, Pin.PULL_DOWN)  # GPIO3 (D2) with internal pull-down resistor
ledPin = Pin(10, Pin.OUT)  # GPIO10 for LED

while True:
    buttonState = buttonPin.value()  # Read button state
    print("Button state:", buttonState)  # Print button state for debugging

    if buttonState == 1:  # Button pressed (active HIGH due to pull-down)
        ledPin.value(1)  # Turn LED on
    else:
        ledPin.value(0)  # Turn LED off

    time.sleep(0.01)  # Small debounce delay (10ms)

Here is the file for my Micropython code.

ATTiny 412 Chip – Tyler Russell

  • All of this work was done on Mac OS

Overview on Chip

I worked on the ATtiny412 and read through the datasheet to find the necessary information.

Feature ATtiny412
Chip ATtiny412
Architecture AVR® RISC, 8-bit CPU
CPU Speed Up to 20MHz
Flash Size 2/4KB Flash
SRAM 128/256bytes
Wireless Connectivity No
GPIO Pins 4 Pins
GPIO Voltage 3.3V
Operating Voltage 3.0V-3.6V
Power Supply 3.3V (required)
PWM 4 channels
Debugging UPDI

I had worked with the ATtiny 412 chip before so I was familiar with some of its features already. A tool chain as explained to me by Kathryn Wu

ToolChain Arduino C++

Feature Arduino C++
Language Support C++ (Arduino API)
Build System Arduino IDE/PlatformIO
Operating System Bare-metal
Debugging Serial print debugging
Environment Setup Complexity Low (Arduino IDE, PlatformIO)
Ease of Use Beginner-friendly (Simple IDE)
Memory Management Automatic, define Variables
Board Support Limited to common ESP32 functionalities
Code Portability Medium (Limited to Arduino ecosystem)
Extensibility Limited to Arduino framework constraints
Built-in Libraries Large Arduino and third-party libraries
Hardware Abstraction High (simpler GPIO, PWM handling)
Power Efficiency Basic sleep functions
Peripheral Support Limited (some advanced peripherals not supported)
WiFi/Bluetooth API Simplified API
Debugging Support Basic Serial Monitor
Flashing Method One-click “Upload” in Arduino IDE
Serial Monitor Built-in Arduino Serial Monitor
Security Features Minimal security features
Community Support Large Arduino user community
Recommended For Quick prototyping
Project Types Hobbyist, maker

Thank you Kathryn Wu again for doing most of this chart before me for her own chip. Go checkout her Website.

  • The ATtiny chip is a very useful chip if you need a smaller, cheaper, and low-power micro controller. Programming it can take significant time, very specific softwares, and a bootloader micro controller in order to program the ATtiny.

  • Use Arduino if you need a quick, easy way to develop simple applications without worrying about low-level configurations. It offers easily used API for GPIO, WiFi, and Bluetooth. There is a large library ecosystem and cross-compatibility with existing Arduino projects.

RP2040 – Amalia Bordoloi

Note: Everything performed in this section was done using a Mac

I worked on the RP2040 and read through this datasheet to learn more about this microcontroller. I used ChatGPT to help summarize and answer my questions based off the datasheet because the datasheet is 636 pages long, which would be a lot to read and process on my own. You can find a link to a pdf of all the ChatGPT conversations I had during week 4 on my personal documentation.

Overview

Feature Description
Processor Dual ARM Cortex-M0+ cores, up to 133MHz
Memory 264kB SRAM in six banks, supports up to 16MB of external flash via QSPI
GPIO 30 multifunction GPIO pins, 4 of which support ADC
Peripherals 2x UART, 2x SPI, 2x I2C, 16x PWM channels, USB 1.1, 8 PIO state machines
Clock System 2 PLLs for system and USB clocks, flexible clocking architecture
Power Management Internal LDO regulator, multiple low-power modes
Debugging Serial Wire Debug (SWD) interface, hardware breakpoints, watchpoints
ADC 4-channel, 12-bit, 500ksps ADC with internal temperature sensor
Bus Fabric Fully connected AHB crossbar for high-speed data transfer
Manufacturing Process 40nm process technology for low power consumption
Ease of Use Supports C, C++, and MicroPython, making it beginner-friendly
Built-in Libraries Includes SDK with hardware abstraction layers for easy peripheral access
Recommended Use Suitable for embedded systems, IoT, robotics, signal processing, and real-time applications
Notable Features Programmable I/O (PIO) for custom peripheral support, deterministic cycle timing, DMA support

Toolchain Comparison

I was confused about what toolchain the RP2040 uses, and Kathryn Wu told me that the RP2040 uses the GNU toolchain, which I double-checked with ChatGPT, and she was correct. This is a table that ChatGPT helped me come up with, only using information from the RP2040 datasheet.

Feature GNU Toolchain Arduino Toolchain
Compilation Uses GCC (arm-none-eabi-gcc) for compiling C/C++ code. Uses a simplified build system with pre-configured toolchains.
SDK Support Fully supports the RP2040 C/C++ SDK, enabling low-level control. Uses Arduino Core for RP2040, which abstracts hardware details.
Optimization Provides manual control over optimization flags for performance tuning. Optimized for ease of use, but with less fine-grained control over optimizations.
Debugging Supports GDB and SWD (Serial Wire Debug) for in-depth debugging. Limited debugging features, mainly serial print debugging.
Build System Uses CMake for configuration and building projects. Uses a simplified, built-in build system managed by the Arduino IDE.
Library Access Direct access to hardware abstraction layers (HAL) and low-level registers. Provides Arduino libraries and APIs for easy peripheral access.
Ease of Use Requires manual setup but offers full control over the hardware. Beginner-friendly, with drag-and-drop UF2 programming.

Development Workflows

To compare their development workflows, I used the knowledge I gained through my experience programming both, which is documented below as well as ChatGPT’s summary of the 636 page datasheet. I also used ChatGPT to help format the table.

Workflow Section Arduino (C/C++) MicroPython (Python)
Project Setup Arduino IDE Use Micropython through Thonny
Performance Compiled to machine code, significantly faster execution. Interpreted language, slower execution due to runtime interpretation.
Memory Usage More memory-efficient; optimized for RP2040’s 264kB SRAM. Uses more RAM due to interpreter overhead.
GPIO & Hardware Control Direct control with low-level optimization. Easier for beginners but with higher latency.
Interrupt Handling Fast, real-time capable interrupt handling. Possible but with added latency from Python runtime.
Boot & Startup Time Faster boot and execution as compiled code runs immediately. Longer due to interpreter initialization.
SDK & Library Support Full access to the RP2040 C/C++ SDK with extensive libraries. MicroPython has a built-in firmware with library support but limited low-level access.
Real-Time Capabilities Supports real-time applications and precise control. Not ideal for precise timing tasks.
Concurrency Can fully utilize RP2040’s dual-core processing. Supports lightweight threading but lacks true parallel execution.
Flash Storage Usage Smaller compiled binaries allow more efficient flash usage. Larger due to the MicroPython runtime.

From the information I gained by programming the RP2040 in both C++ and Micropython and the information ChatGPT provided from the datasheet, I would say both function fairly similarly and can program the RP2040 successfully, but C++ is able to work faster and better utilizes the abilities of the RP2040.

Programming the RP2040

The goal was to create programs in both C++ and Micropython that would use an RP2040 to make an LED light up using a push button as the input.

This was the RP2040 I used. I soldered the pin headers on. To solder the pin headers on, I used a breadboard to hold it in place.

I struggled a bit when trying to program the RP2040 because the format for the pin numbers was different than the ESP32-C3. I needed to put D in front of the number for the digital pins in C++ and use the specified different pin numbers for Micropython.

C++

This was the initial blink code I started out trying to use on the RP2040 but it did not work because of the pin number not having a D in front of it.

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(5, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(5, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(500);                      // wait for a second
  digitalWrite(5, LOW);   // turn the LED off by making the voltage LOW
  delay(500);                      // wait for a second
}

It was a simple fix to get the code to work where I used D5 instead of 5. I initally thought I might have soldered the ground pin wrong, but really the code was just wrong. ChatGPT helped me resolve this issue.

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(D5, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(D5, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(500);                      // wait for a second
  digitalWrite(D5, LOW);   // turn the LED off by making the voltage LOW
  delay(500);                      // wait for a second
}

This was the final C++ code I used for the RP2040:

void setup()
{
  pinMode(D8, INPUT);
  pinMode(D5, OUTPUT);
}

void loop()
{
  // check if pushbutton is pressed.  if it is, the
  // buttonState is HIGH
  if (digitalRead(D8) == HIGH) {
    // turn LED on
    digitalWrite(D5, HIGH);
  } 

  else {
    // turn LED off
    digitalWrite(D5, LOW);
  }
}

Micropython

I followed this tutorial to download Micropython on my Mac, which was recommended by Kathryn Wu who is also a Mac user.

1.Download Thonny

2.Tools -> Options -> Interpreter

3.Change settings to this:

I struggled with this because I think my RP2040 was still connected to the Arduino since I did not close ot=ut that app initially. I also needed to put my RP2040 into bootloader mode by holding down the button next to B on the microcontroller while unplugged from the computer and keep holding the button down until around two seconds after plugging the microcontroller cord into the computer. ChatGPT helped me with this. I struggled to get the target volume to appear.

This was the sample countdown code I used that was on the tutorial I followed:

from machine import Pin, Timer

led = Pin(25, Pin.OUT)
Counter = 0
Fun_Num = 0

def fun(tim):
    global Counter
    Counter = Counter + 1
    print(Counter)
    led.value(Counter%2)

tim = Timer(-1)
tim.init(period=1000, mode=Timer.PERIODIC, callback=fun)

This countdown worked very successfully, and the built-in LED on the RP2040 blinked successfully, so I was surprised when I tried the button code and it did not work. ChatGPT recommended I try to use the code below to see if the button was the issue or if it was something else. It initially did not work because there are different pins for Micropython, so I needed to change that, and then I believe it worked.

from machine import Pin
import time

button = Pin(2, Pin.IN)

while True:
    print("Button state:", button.value())  # Print button status
    time.sleep(0.5)

This was the output:

This was the final Micropython code I used:

from machine import Pin
import time

button = Pin(2, Pin.IN)  # D8 as input
led = Pin(7, Pin.OUT)    # D5 as output

while True:
    if button.value() == 1:  # Button is pressed (HIGH)
        led.value(1)  # Turn LED ON
    else:
        led.value(0)  # Turn LED OFF

    time.sleep(0.01)  # Small delay to debounce

Last update: February 19, 2025