Week 4: Embedded Programming

This week was focused on understanding and working with embedded systems in practice. For the group assignment, we demonstrated and compared different toolchains and development workflows used for various embedded architectures, so we could understand how programming and debugging differed across platforms. For the individual assignment, I dived into a microcontroller datasheet to understand its features and architecture, then wrote and tested a program on a microcontroller-based system that interacted with input and/or output devices and communicated through a wired or wireless connection. Essentially, this week moved from theory into actually reading, programming, and making a microcontroller do something real.

Saheen guided us this week.


Group Assignment: Toolchain & LED Blink Comparison

This comparison focuses specifically on toolchain setup and LED blink implementation across four microcontroller platforms. The evaluation is based only on practical experience with development environment configuration and basic GPIO blinking.

Board Development Environment Programming Level Build Process Flashing Method Blink Implementation Style Observed Complexity
Arduino Q (UNO R4 WiFi) Arduino IDE / Arduino Cloud High-level C++ Automatic compile & upload USB (One-click) digitalWrite() Very Easy
ESP32-C6 VS Code + ESP-IDF C / Assembly (RISC-V) idf.py build (manual build system) idf.py flash Direct GPIO register control High
ESP32-S3-DEV-KIT VS Code + PlatformIO C++ (Arduino Framework) PlatformIO build system USB via PlatformIO digitalWrite() / NeoPixel library Medium
ATtiny44/84 Microchip Studio + AVR-GCC Embedded C Compile → Generate HEX AVRDUDE + ISP Programmer Direct PORT register manipulation Medium (Low-level)

Summary Observations

  • Arduino Q provided the most beginner-friendly and abstracted workflow.
  • ESP32-C6 required the most advanced configuration and exposed low-level hardware interaction.
  • ESP32-S3 offered a structured ecosystem with moderate complexity.
  • ATtiny44/84 required manual compilation and flashing, giving deeper insight into embedded C and register-level programming.
  • Conclusion

    The comparison highlights how toolchain structure and abstraction level vary significantly between platforms. While all four boards successfully executed a basic LED blink program, the setup complexity, build process, and programming depth differed substantially. This demonstrates how architecture and ecosystem design directly influence the embedded development experience.


    Individual Assignment: Where I Started From

    The last time I wrote any code was around 12 years ago in school. I never used it again after that. So this week felt strange in two ways: I was excited to finally use coding in a real, physical way, but at the same time I felt like I was starting from zero. On top of that, I have never worked with electronics before. So I wasn't just revising programming, I was learning how electricity behaves, what resistors do, and why hardware can break if wired incorrectly.

    What made embedded programming different from anything I had done before was that mistakes are not just errors on a screen. A wrong value in your code can mean an LED that never turns on. A miswired component can mean a board that stops responding. The feedback loop is physical, not just logical, and that changes how you have to think.

    Before I could make anything work, I had to build up a basic mental model of how microcontrollers operate: what a chip actually is, how a program gets onto it, how it talks to the outside world through pins, and what happens when power is applied. Here are the concepts that became meaningful to me this week.


    Microcontroller

    Getting Started with Seeed Studio XIAO RP2040

    I am working with the XIAO RP2040. This is a small development board made by Seeed Studio. Inside it is a microcontroller chip called the RP2040, made by Raspberry Pi. A microcontroller is basically a small computer on a chip. It has:

  • A processor (that runs instructions)
  • Memory (the RP2040 has 264KB of SRAM built into the chip, and the XIAO board adds 2MB of onboard flash storage for your programs)
  • Pins to connect to hardware (the RP2040 chip itself has 30 GPIO pins, but the XIAO board only exposes 14 of them)
  • It does nothing by itself. It only works when we upload a program to it.

    When my LEDs started reacting to a button press, that became my first embedded system.

    GPIO

    GPIO stands for General Purpose Input/Output. These are the pins that let the microcontroller interact with the outside world.

  • OUTPUT → sends voltage (like turning on an LED)
  • INPUT → reads voltage (like checking if a button is pressed)
  • This is how software touches hardware.


    void setup()

    The void setup() function initializes settings, configures pin modes, starts libraries, and initializes serial communication. It runs only once when the board is powered on or reset, preparing the environment for void loop().

    void loop()

    The void loop() function executes the main logic continuously. It runs after setup() completes and repeats endlessly until the board is powered off or reset.

    pinMode()

    Before using a pin, I must define its behavior.

    pinMode(YELLOW, OUTPUT);
    pinMode(BUTTON, INPUT_PULLDOWN);
        

    If not defined correctly, the hardware does not behave as expected. This helped me understand that hardware behavior must be explicitly declared in software.

    digitalWrite()

    This sends a voltage signal to a pin. I used it to:

    • Turn LEDs on
    • Turn LEDs off
    • Create blinking patterns

    Although it appears as a simple function, it actually controls physical voltage on the board.

    digitalRead()

    This reads the voltage level of a pin. I used it to check whether the button was pressed, which made my project interactive instead of running automatically.

    Logic Level (HIGH / LOW)

    Digital pins operate using two logic levels:

  • HIGH → represents a voltage level (3.3V on RP2040)
  • LOW → represents 0V (Ground)

  • XIAO RP2040 Board

    Datasheet Seeed Studio XIAO RP2040

    Board Overview

    The XIAO RP2040 is a compact development board made by Seeed Studio. It is built around the RP2040 microcontroller chip. The board is roughly thumb-sized and is designed to be breadboard-friendly.


    Physical Connections

    At the top of the board is a USB Type-C interface, which is used for both powering the board and uploading programs from a computer.

    Along the two sides of the board are 14 pins in total. Each pin can serve multiple functions depending on how you configure it in your program:

  • D0-D10 (green) are digital pins, used for general input and output. This is what I used for my LEDs and button.
  • A0-A3 (orange) are analog pins, used for reading variable voltage values like from a sensor or potentiometer. These share the same physical pins as D0-D3.
  • SDA (D4) and SCL (D5) are used for I2C communication with other devices.
  • TX (D6) and RX (D7) are used for UART serial communication.
  • MOSI (D10), MISO (D9), SCK (D8), CSn (D7) are used for SPI communication.
  • 5V and 3V3 are power output pins.
  • GND is ground.

  • What I found important to understand is that most pins are multi-function. The same physical pin can act as digital, analog, or a communication pin depending on what your program tells it to do.


    Onboard Components

    Looking at the back of the board diagram, there are several built-in components that do not need any external wiring:

  • RGB LED (User LED, 3 colors) is a programmable LED built into the board. One thing that caught me off guard is that on the XIAO RP2040, this LED is inverted, meaning you write LOW to turn it on and HIGH to turn it off, which is the opposite of what you would expect.
  • Power LED lights up whenever the board is receiving power.
  • RESET button (R) restarts the program running on the board.
  • BOOT button (B) is used when flashing new firmware. Holding it while connecting USB puts the board into bootloader mode.
  • GPIO and Pin Understanding

    Before I could wire anything up, I needed to understand what the pins on the board actually are and how they work.


    Figure: Annotated XIAO RP2040 board showing pin labels, power pins, and onboard components used in this project. I created this diagram in Canva to better understand how the board-level D-number pins map to the internal GPIO pins referenced in the datasheet.

    Pin Mapping Between Chip and Board

    The datasheet shows internal GPIO numbers, but the XIAO board uses different labels. I had to refer to the XIAO pin map to understand which board pin corresponds to which internal GPIO number. This explains why the default Blink example did not work initially. It was written for a different board configuration.

    Memory and Program Storage

    From the datasheet summary, I understood that:

  • SRAM is used temporarily while the program runs.
  • External flash memory stores the program permanently.
  • The boot process loads and executes the firmware after power-up.
  • This helped me understand what actually happens when I upload code and power the board.

    Reset and Boot

    From the datasheet, I learned what these buttons actually trigger at the hardware level:

  • The RESET button restarts the microcontroller.
  • The BOOT button puts the chip into a mode that allows firmware to be uploaded.

  • I had seen these buttons on the board already, but reading the datasheet made it clear that they are not just convenience features. They control specific hardware behaviour inside the chip itself.



    RP2040

    Reference: www.raspberrypi.com

    The RP2040 is the microcontroller chip made by Raspberry Pi that sits inside the XIAO RP2040 board. Everything I read in the datasheet this week refers to this chip specifically, not the board around it.

    Why Is It Called RP2040?

    The name is not random. Each part of it encodes something about the chip's design:

  • RP stands for Raspberry Pi, the company that made it.
  • 2 refers to the number of processor cores (dual-core).
  • 0 refers to the type of core, which is the Cortex-M0+.
  • 4 is derived from the amount of RAM using the formula floor(log2(RAM / 16kB)). For 264KB of SRAM, this gives 4.
  • 0 is derived from the amount of non-volatile storage using the formula floor(log2(nonvolatile / 128kB)). Because the RP2040 chip has no built-in flash, this is 0.
  • Overview of the Chip

    The RP2040 is a dual-core ARM Cortex-M0+ processor. Both cores can run simultaneously at up to 133 MHz, which allows the chip to handle multiple tasks at the same time. Each core is a 32-bit processor. At 133 MHz, the chip can execute up to 133 million clock cycles per second, which gives it significantly more processing capability than traditional 8-bit microcontrollers.

    It has 264KB of SRAM built into the chip itself, used while a program is running. It has no built-in flash storage, which is why any board using this chip (like the XIAO) needs to provide its own external flash memory.

    Key Features

  • Dual ARM Cortex-M0+ cores (up to 133 MHz)
  • 264KB SRAM built into the chip
  • 30 GPIO pins (GPIO0 through GPIO29)
  • No built-in flash storage (requires external flash, provided by the board)
  • Dedicated QSPI interface for connecting external flash
  • Programmable I/O (PIO)
  • 12-bit ADC with temperature sensor
  • GPIO Pins


    Looking at the chip pinout diagram, the RP2040 has 30 GPIO pins in total, numbered GPIO0 through GPIO29. These are the pins that let the chip interact with the outside world by reading or sending signals.

    Four of these GPIO pins also double as analog input pins. From the pinout, GPIO26, GPIO27, GPIO28 and GPIO29 are labeled as ADC0 through ADC3, meaning they can read analog voltage values in addition to acting as regular digital pins.

    The chip also has dedicated pins for specific communication interfaces. The QSPI pins are used exclusively for connecting external flash memory, which is how the chip loads and runs programs. There are also USB pins (USB_DP and USB_DM), power pins (IOVDD, DVDD, ADC_AVDD), and debug pins (SWCLK and SWDIO).



    Implementation: From Zero to a Working Input-Output System

    This week was my first time building an actual electronic circuit, I had never used a breadboard, never connected an LED physically, and never used a resistor outside of a textbook. On top of that, the last time I wrote any code was 12 years ago in school. So this week felt like restarting programming, but with hardware added to the mix. I decided to start as small as possible: make one LED blink.

    Setting Up the XIAO RP2040

    I connected the XIAO RP2040 to my MacBook using a USB-C cable and installed Arduino IDE. The setup was done by following the instructions from wiki.seeedstudio.com. There was one change though - on Mac, the Preferences is not under the File menu but under the Arduino IDE menu. I had a hard time figuring out the very first step but the rest were according to the website.




    Toolchain / Board Manager Installation

    Since the XIAO RP2040 is not supported by Arduino IDE out of the box, the correct board package needs to be installed manually. Here are the steps I followed:

    Step 1 - Add the Board Manager URL

    Navigate to Arduino IDE > Preferences (on Mac). In the Additional Boards Manager URLs field, paste the following URL:

      https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
    

    Click OK to save.


    Step 2 - Open Boards Manager and Install the Package

    Navigate to Tools > Board > Boards Manager. Search for rp2040. Two results appear, select Raspberry Pi Pico/RP2040/RP2350 (the standalone package, not the deprecated one) and install the latest version. The IDE will download and configure the toolchain components including pqt-openocd, pqt-picotool, and the platform files, all visible in the output panel.


    Step 3 - Select the XIAO RP2040 Board

    Once installation is complete, go to Tools > Board > Select Other Board and Port. Search for seeed and select Seeed XIAO RP2040 from the list.

    Step 4 - Select the Correct Port

    In the same dialog, under Ports, select the appropriate serial port for your connected board.


    First Programming

    Uploading the Blink Sketch

    I first tried the default Blink example via File > Examples > 01.Basics > Blink. It did not work. The example uses LED_BUILTIN, which does not directly map to the onboard LED pins of the XIAO RP2040. After checking the Pin Map from the wiki and datasheet, I defined the correct RGB LED pin manually:

    #define USER_LED_R 17



    #define USER_LED_R 17 // Defined USER_LED_R as 17th pin, USER_LED_G as 16 and USER_LED_B as 25 as per the pinmap for the model xiao rp2040 // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(USER_LED_R, OUTPUT); // Replaced LED_BUILTIN with USER_LED_R based on the board we are using } // the loop function runs over and over again forever void loop() { digitalWrite(USER_LED_R, LOW); // turn the LED ON (active-LOW) delay(1000); digitalWrite(USER_LED_R, HIGH); // turn the LED OFF delay(1000); }

    Initial onboard LED Blink

    That was the first moment I saw code create a physical result.
    This code can create the blink but the other LEDs will be turned on too, so it was rewritten

    #define USER_LED_R 17 #define USER_LED_G 16 #define USER_LED_B 25 // Defined RGB LED pins according to the XIAO RP2040 pin map void setup() { // Configure RGB LED pins as output pinMode(USER_LED_R, OUTPUT); pinMode(USER_LED_G, OUTPUT); pinMode(USER_LED_B, OUTPUT); // Turn OFF green and blue channels (RGB LED is active LOW) digitalWrite(USER_LED_G, HIGH); digitalWrite(USER_LED_B, HIGH); } void loop() { // Blink red LED twice digitalWrite(USER_LED_R, LOW); // ON (active LOW) delay(100); digitalWrite(USER_LED_R, HIGH); // OFF delay(300); digitalWrite(USER_LED_R, LOW); // ON delay(100); digitalWrite(USER_LED_R, HIGH); // OFF delay(300); }



    Debugging: Unresponsive Board

    When I uploaded the modified RGB LED code, the XIAO RP2040 showed no response. After examining it, my instructor identified a hardware fault with the unit and replaced it with a new one. The same code uploaded and ran correctly on the replacement, the red onboard LED blinked as expected, with the green and blue channels staying off.

    Compiling Sketch

    Clicking Verify compiles the sketch, it checks for errors and converts the code into binary that the microcontroller can run. Upload then flashes that binary to the board over USB.

    Uploading

    Onboard LED Blink video

    After uploading the modified code, the red LED started blinking.


    Understanding the Breadboard

    Figure: Components used in this assignment and annotated breadboard layout. I created this diagram in Canva to help myself understand how the breadboard is internally connected and how each component fits into the circuit before wiring it physically.

    Before connecting external components, I had to understand how the breadboard works. At first it looked like random holes. But I learned:

  • The top and bottom long rows are power rails.
  • One rail is usually used for voltage (3V3 or 5V).
  • One rail is used for GND.
  • The center area is divided into vertical groups of 5 connected holes.
  • This means if two component legs are in the same column, they are electrically connected. Once I understood this, wiring started to make sense.



    Lighting My First External LED

    Next, I connected a single external LED. I learned that an LED has:

  • A longer leg (anode +ve)
  • A shorter leg (cathode -ve)
  • Connection:
  • D pin → resistor → LED (long leg)
  • LED short leg → GND rail
  • At first, I didn’t fully understand why the resistor was required. Later I learned that LEDs need current limiting. Without a resistor, too much current can damage the LED or the microcontroller pin. So the correct safe structure became: D pin → resistor → LED → GND When the pin was HIGH, the LED turned on. When LOW, it turned off. That was when digitalWrite() became physically meaningful to me.

    In this setup, I powered my first external LED directly using the 3V3 and GND pins of the XIAO RP2040.

  • The 3V3 pin was connected to the positive (+) rail of the breadboard.
  • The GND pin was connected to the negative (–) rail.
  • The longer leg (anode) of the LED was placed on the positive rail.
  • The shorter leg (cathode) was placed on the ground rail.
  • The LED turned on immediately, no programming required. That was my “ohh it works!!” moment. However, I later learned that connecting an LED directly across 3V3 and GND is not ideal because it does not limit current. So I modified the circuit by adding a resistor in series: 3V3 → resistor → LED → GND This limits the current flowing through the LED and protects both the LED and the microcontroller. I also switched to a yellow LED while rebuilding the circuit properly on the breadboard. This small change helped me understand that electronics is not just about making something turn on, it is about controlling current safely.

    #define YELLOW D7 // defined the external LED connected to D7 pin // the setup function runs once when you press reset or power the board void setup() { pinMode(YELLOW, OUTPUT); } // the loop function runs over and over again forever void loop() { digitalWrite(YELLOW, HIGH); // turn the LED on (HIGH is the voltage level) delay(500); digitalWrite(YELLOW, LOW); // turn the LED off by making the voltage LOW delay(500); digitalWrite(YELLOW, HIGH); // turn the LED on (HIGH is the voltage level) delay(500); digitalWrite(YELLOW, LOW); // turn the LED off by making the voltage LOW delay(500); }

    Blinking my first external LED


    Multiple LEDs and Chase Sequence (Output)

    After successfully lighting one LED, I added two more LEDs:

  • Yellow → D7
  • White → D6
  • Blue → D5
  • Each LED had its own resistor.

    Figure: First attempt at connecting multiple LEDs (unsuccessful)

    When I moved from lighting a single LED to connecting multiple LEDs for the chase sequence, I assumed I had wired everything correctly because each LED had its own resistor and was connected to a D pin. However, the circuit did not work as expected. The issue was not in the code, it was in the grounding. Only one LED had a proper connection back to the GND rail linked to the XIAO RP2040. The other LEDs were placed on a section of the breadboard that was not electrically connected to the ground line. Even though the wiring looked correct visually, the circuit was incomplete for two of the LEDs, so no current could flow through them. This mistake helped me understand that every component must share a common ground, and that breadboard power rails are not automatically connected across sections.

    Once all negative legs were connected to the GND rail, I wrote a chase sequence using digitalWrite() and delay() so that one LED turned on at a time in a loop. This helped me understand timing and sequential output control.

    #define YELLOW D7 #define WHITE D6 #define BLUE D5 // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(YELLOW, OUTPUT); pinMode(WHITE, OUTPUT); pinMode(BLUE, OUTPUT); } // the loop function runs over and over again forever void loop() { digitalWrite(YELLOW, HIGH); // turn the LED on (HIGH is the voltage level) digitalWrite(WHITE, LOW); // turn the LED off by making the voltage LOW digitalWrite(BLUE, LOW); // turn the LED on (HIGH is the voltage level) delay(200); digitalWrite(YELLOW, LOW); digitalWrite(WHITE, HIGH); digitalWrite(BLUE, LOW); delay(200); digitalWrite(WHITE, LOW); digitalWrite(BLUE, HIGH); digitalWrite(YELLOW, LOW); delay(200); }

    Blinking 3 LED in chase sequence after right wiring

    Once I figured out the wiring, I then added one more LED and created a chase loop ina square arrangement

    #define YELLOW D7 #define WHITE D6 #define BLUE D5 #define RED D8 #define time 100 // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(YELLOW, OUTPUT); pinMode(WHITE, OUTPUT); pinMode(BLUE, OUTPUT); pinMode(RED, OUTPUT); } // the loop function runs over and over again forever void loop() { digitalWrite(YELLOW, HIGH); // turn the LED on (HIGH is the voltage level) digitalWrite(WHITE, LOW); // turn the LED off by making the voltage LOW digitalWrite(BLUE, LOW); // turn the LED on (HIGH is the voltage level) digitalWrite(RED, LOW); // turn the LED on (HIGH is the voltage level) delay(time); digitalWrite(YELLOW, LOW); digitalWrite(WHITE, HIGH); digitalWrite(BLUE, LOW); digitalWrite(RED, LOW); delay(time); digitalWrite(WHITE, LOW); digitalWrite(BLUE, HIGH); digitalWrite(YELLOW, LOW); digitalWrite(RED, LOW); delay(time); digitalWrite(WHITE, LOW); digitalWrite(BLUE, LOW); digitalWrite(RED, HIGH); digitalWrite(YELLOW, LOW); delay(time); }

    Hero Shot

    Chasing 4 LED using code


    Embedded System

    Adding a Push Button for LED Chase (Input & Output Devices)

    After working with outputs, I wanted to understand input. I added a push button to the breadboard. The button has four legs but internally works as a switch connecting two sides when pressed. In code:

    pinMode(BUTTON, INPUT_PULLDOWN);

    Inside loop(), I used:

    if (digitalRead(BUTTON) == HIGH)
  • When pressed: The chase sequence ran.
  • When not pressed: All LEDs turned off.
  • This was my first complete system combining:

  • Input (button)
  • Processing (if condition)
  • Output (LEDs)

  • Figure: Final working circuit: 4 LED chase with push button control

    This is the completed circuit after correcting grounding issues. Each LED (yellow, white, blue, red) is connected to a separate D pin on the XIAO RP2040, with an individual current-limiting resistor connected in series. All LEDs share a common ground rail connected to the XIAO’s GND pin. The push button is connected to D4 and GND and configured as an input device in the program. When pressed, it triggers the LED chase sequence. This setup represents a complete input-output embedded system:

  • Input → push button
  • Processing → conditional logic in code
  • Output → controlled LED sequence

  • #define YELLOW D7 #define WHITE D6 #define BLUE D5 #define RED D8 #define time 100 #define BUTTON D4 // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(YELLOW, OUTPUT); pinMode(WHITE, OUTPUT); pinMode(BLUE, OUTPUT); pinMode(RED, OUTPUT); pinMode(BUTTON, INPUT_PULLDOWN); } // the loop function runs over and over again forever void loop() { if (digitalRead(BUTTON)==HIGH) { chase_4(); //Made a separate function chase_4 to better organize //To set the condition: if pulldown is used, set the condition to high. if pullup is used, set it to low } else { digitalWrite(YELLOW, LOW); // turn the LED on (HIGH is the voltage level) digitalWrite(WHITE, LOW); // turn the LED off by making the voltage LOW digitalWrite(BLUE, LOW); // turn the LED on (HIGH is the voltage level) digitalWrite(RED, LOW); // turn the LED on (HIGH is the voltage level) } } // to run the leds as a ring void chase_4() { digitalWrite(YELLOW, HIGH); digitalWrite(WHITE, LOW); digitalWrite(BLUE, LOW); digitalWrite(RED, LOW); delay(time); digitalWrite(YELLOW, LOW); digitalWrite(WHITE, HIGH); digitalWrite(BLUE, LOW); digitalWrite(RED, LOW); delay(time); digitalWrite(WHITE, LOW); digitalWrite(BLUE, HIGH); digitalWrite(YELLOW, LOW); digitalWrite(RED, LOW); delay(time); digitalWrite(WHITE, LOW); digitalWrite(RED, HIGH); digitalWrite(BLUE, LOW); digitalWrite(YELLOW, LOW); delay(time); }

    LED Chase Loop using Button


    Learning to use For Loop

    During Asia Regional review, Rico told me to use the for loop function to cleanup my button chase code. So I tried learning to do that. I tried chasing diagonal this time, for this I just had to change the order of leds. I used a for loop to control the LEDs sequentially instead of writing repeated code for each one.

    Prompt used to generate the code on ChatGPT:

    Convert my Arduino LED chase code (written with repeated digitalWrite) into a version using a for loop + array. Keep the same behavior.

    Integer and Array Declaration

    The code uses int to store whole numbers, including the LED pin numbers.

    int leds[] = {YELLOW, BLUE, WHITE, RED};
    int numLeds = 4;
    The array leds[] groups all LED pins into one list. Each LED gets an index number (0–3), which allows the for loop to access them one by one. The variable numLeds stores the total number of LEDs, making the code easier to modify later.

    for (int i = 0; i < numLeds; i++) 
    {
      digitalWrite(leds[i], HIGH);
      delay(time);
      digitalWrite(leds[i], LOW);
    }
    The loop starts with i = 0 and increases i by 1 each time (i++) until it reaches the total number of LEDs. Since the LEDs are stored in an array, the loop automatically selects each LED one by one and turns it ON and OFF. This makes the code shorter, cleaner, and easier to modify.

    // Define names for each LED pin #define YELLOW D9 #define BLUE D8 #define WHITE D6 #define RED D5 // Define button pin #define BUTTON D7 // Time delay between LED changes (in milliseconds) #define time 100 // Store all LED pins inside an array // This lets us control them using a loop int leds[] = {YELLOW, WHITE, BLUE, RED}; // Total number of LEDs in the array int numLeds = 4; void setup() { // Set all LED pins as OUTPUT (they send voltage out) pinMode(YELLOW, OUTPUT); pinMode(WHITE, OUTPUT); pinMode(BLUE, OUTPUT); pinMode(RED, OUTPUT); // Set button as INPUT with internal pulldown resistor // When pressed, reads HIGH // When not pressed, reads LOW pinMode(BUTTON, INPUT_PULLDOWN); } void loop() { // Check if button is pressed if (digitalRead(BUTTON) == HIGH) { // If pressed, run LED chase pattern chase_4(); } else { // If not pressed, turn all LEDs OFF // Loop goes through each LED one by one for (int i = 0; i < numLeds; i++) { // leds[i] selects LED number 0,1,2,3 digitalWrite(leds[i], LOW); } } } // Function to run LEDs one after another void chase_4() { // Loop through each LED for (int i = 0; i < numLeds; i++) { // Turn current LED ON digitalWrite(leds[i], HIGH); // Wait for defined time delay(time); // Turn current LED OFF before moving to next digitalWrite(leds[i], LOW); } }

    LED Diagonal Chase using Button (for loop function)

    Reflection

    After completing my first embedded system and understanding how the blink and chase sequences worked, something unexpected happened. On my way home that evening, I couldn’t stop noticing all the blinking lights around me, on trucks, buses, traffic signals, signboards, and vehicles on the road. It felt like someone had opened a door in my head that had been locked for 30 years. Although I have always been fascinated by lights like anyone else, I had never thought about them from this perspective. Now I found myself observing the exact timing of blink sequences on vehicles and wondering what kind of code would produce that delay, that rhythm, that pattern. For the first time, I was not just seeing lights, I was seeing logic running inside them.


    Embedded Programming: Button-Controlled Serial Output

    Continuous Output vs State Change Detection

    Initially, I used a simple condition:

    if (digitalRead(BUTTON) == LOW) {
      Serial.println("OUCH");
      delay(200);
    }
    

    In this version, the message prints continuously while the button is being held down. Since the loop() function runs repeatedly, the condition remains true as long as the button is pressed, resulting in:

    OUCH
    OUCH
    OUCH
    OUCH
    

    This method checks the current level of the button (LOW or HIGH).

    Later, I implemented state change detection.

    In this exercise, I programmed a push button connected to pin D7 to print a message in the Serial Monitor when everytime the button is pressed.

    Prompt used on ChatGPT for the code:
    Arduino code: detect button press using edge detection (INPUT_PULLUP) and print a message once per press.


    #define BUTTON D7 int lastState = HIGH; void setup() { pinMode(BUTTON, INPUT_PULLUP); Serial.begin(9600); } void loop() { int currentState = digitalRead(BUTTON); if (lastState == HIGH && currentState == LOW) { Serial.println("Ouch, that hurts!"); } lastState = currentState; }


    Hero Shot

    Explanation

    The button is configured using INPUT_PULLUP, which activates the microcontroller’s internal pull-up resistor. This means:

  • Button not pressed → reads HIGH
  • Button pressed → reads LOW

  • To avoid printing continuously while the button is held down, I used a state change detection method. The variable lastState stores the previous reading of the button. The variable currentState stores the current reading.

    The program checks for a transition from HIGH to LOW, which indicates that the button has just been pressed:

    if (lastState == HIGH && currentState == LOW)

    When this transition occurs, the message:

    Ouch, that hurts!

    is printed once.

    Finally, lastState is updated at the end of each loop cycle so the program can compare values during the next iteration.


    Reflection

    This activity looked simple at first, but I spent more time debugging than expected. I learned that even if the code is correct, wrong wiring can completely change the result. Fixing the button orientation and understanding how INPUT_PULLUP works helped me understand the connection between hardware and software more clearly. I also learned how to detect a button press properly instead of printing continuously. This exercise made me more patient while debugging and more confident working with physical components.


    Button-Controlled LED Blinking with Toggle Function

    Prompt used on ChatGPT to generate the code:
    Write Arduino code where a button toggles LED blinking ON and OFF.
    Requirements:
    LED on D8, button on D7 using INPUT_PULLDOWN
    Each press toggles blinking (not hold)
    Use proper button press detection (no repeated triggering)
    Make LED blink every 500 ms using millis() (avoid delay for timing)
    Add simple debounce

    This program uses a push button to toggle an LED blinking mode on and off. When the button is pressed, the system detects a rising edge (LOW to HIGH) and switches the blinking state. Each press alternates between enabling and disabling the LED blinking.

    The variable blinking controls whether the LED should blink. Instead of using delay() for timing, the program uses millis() to create a non-blocking 500 ms interval. This allows the system to continuously check the button state while managing the LED timing simultaneously.

    Key concepts demonstrated:

  • State change detection (edge detection)
  • Toggle logic using boolean variables
  • Non-blocking timing with millis()
  • Internal pull-down configuration
  • Basic software debounce
  • #define LED_PIN D8 #define BUTTON_PIN D7 bool blinking = false; bool lastButtonState = LOW; // Default state is LOW with pull-down bool ledState = LOW; unsigned long previousMillis = 0; const long interval = 500; void setup() { pinMode(LED_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLDOWN); // Internal pull-down enabled Serial.begin(9600); } void loop() { bool currentButtonState = digitalRead(BUTTON_PIN); // Detect rising edge (LOW → HIGH) if (lastButtonState == LOW && currentButtonState == HIGH) { blinking = !blinking; // Toggle blinking mode if (blinking) { Serial.println("LED Blinking: ON"); } else { Serial.println("LED Blinking: OFF"); digitalWrite(LED_PIN, LOW); ledState = LOW; } delay(50); // debounce } lastButtonState = currentButtonState; if (blinking) { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; ledState = !ledState; digitalWrite(LED_PIN, ledState); } } }



    Seeed Studio XIAO RP2040 with MicroPython

    Introduction to MicroPython

    MicroPython is a version of Python made for microcontrollers. It allows us to write simple Python code and run it directly on small boards like the Seeed Studio XIAO RP2040.

    Toolchain Setup

    To program the board, I needed an editor that supports MicroPython. For this, I downloaded Thonny IDE.

    After installing and opening Thonny, I first configured the interpreter. I went to Tools → Options → Interpreter and selected MicroPython (Raspberry Pi Pico) as the interpreter, with the port set to “Try to detect automatically.” This step told Thonny that I was working with a MicroPython-based RP2040 board.

    Next, I installed the MicroPython firmware onto the board. To do this, I pressed and held the BOOT button on the XIAO RP2040 and connected it to my computer. The board appeared as an RPI-RP2 drive. In Thonny, I clicked on “Install or Update MicroPython,” kept the default version, and clicked Install. This successfully flashed the MicroPython firmware onto the board.

    After the firmware installation, I used the simple LED blinking test program from wiki seeed studio in Thonny and clicked Run. When prompted, I saved the file either to “This Computer” or directly to “Raspberry Pi Pico.” Once executed, the onboard LED started blinking and the counter value printed in the Shell. This confirmed that the board was properly connected, the firmware was correctly installed, and the toolchain was functioning as expected.


    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)


    To control the RGB LED, I added an external library. I downloaded the ws2812.py file, opened it in Thonny, and saved it directly to the Raspberry Pi Pico. I made sure the file name was exactly ws2812.py, since any change in the name would prevent it from working. After that, I used the code from wiki seeed studio for the RGB LED program and ran it. The LED cycled through different colors, and the corresponding output was visible in the Shell.


    from ws2812 import WS2812 import utime import machine power = machine.Pin(11,machine.Pin.OUT) power.value(1) BLACK = (0, 0, 0) RED = (255, 0, 0) YELLOW = (255, 150, 0) GREEN = (0, 255, 0) CYAN = (0, 255, 255) BLUE = (0, 0, 255) PURPLE = (180, 0, 255) WHITE = (255, 255, 255) COLORS = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE) led = WS2812(12,1)#WS2812(pin_num,led_count) while True: print("Beautiful color") for color in COLORS: led.pixels_fill(color) led.pixels_show() utime.sleep(0.2)


    Overall, my workflow was straightforward: I downloaded and installed Thonny IDE, used it to install the MicroPython firmware on the XIAO RP2040, and then uploaded and ran the sample codes provided on the Seeed Studio wiki page for setting up Thonny with the XIAO RP2040. I did not write the programs myself at this stage; I used the official example codes to test the setup. I monitored the output through Thonny’s Shell and added the required external library directly to the board. This process confirmed that the board was properly configured and ready for programming using MicroPython.



    Reflection

    This week was important because it forced me to connect programming with physical systems. I realized that every line of code directly controls voltage on a real pin, HIGH and LOW are actual electrical states, not abstract values. Hardware does not forgive mistakes: wrong wiring, missing resistors, or incorrect pin numbers immediately cause failure. Unlike screen-based programming, both logic and physical connections must work together. I also worked with two different toolchains. One was the Arduino-based workflow, where code is compiled and uploaded as firmware. The other was the MicroPython workflow using Thonny, where I installed firmware separately and then ran scripts directly on the board. Arduino felt more rigid and structured, while MicroPython felt more interactive because I could run and test code quickly through the Shell. For the MicroPython setup, I used the official sample codes from the Seeed Studio wiki to verify that everything was working. I began the week unsure and slightly overwhelmed. By the end, I had

  • Browsed and documented some information from a microcontroller’s datasheet
  • Programmed a board to interact and communicate
  • Understood pin mapping
  • Controlled multiple outputs
  • Added input logic
  • Structured code into functions


  • Arduino IDE Files

  • Blink
  • Blink red twice
  • External LED
  • Chase 4 LED
  • Button Chase
  • For loop diagonal chase
  • Ouch that hurts
  • Thonny Sample code
  • Thonny Beautiful colour serial monitor