Week 6

Embedded Programming

  • Browse through the data sheet for your microcontroller
  • Compare the performance and development workflows for other architectures

Embedded Programming

February 28, 2024

For the group assignment this week, we have to compare the performance and development workflow for different architectures and look into the differente datasheets of the microcontrollers found at FabLab Puebla.

- Architectures -

Architectures are basically families of chips, "architecture" refers to the fundamental design and structure of the computing core and its instruction set. Think of it as the blueprint that dictates how a microcontroller or processor is built and how it processes information. To simplify it imagine you're building a house. The architecture of the house dictates the overall design, how rooms are laid out, what materials are used, and how different parts of the house connect with each other. Similarly, in microcontrollers and processors, the architecture defines how the device is designed internally, how it performs computations, and how different components within it communicate and work together.

Here are a list of architectures found in the different chips found in FabLab Puebla

Architecture Pros Cons Main Applications Common Microcontrollers
AVR (8-bit) Low power consumption, Simple to program, Good community support Limited processing power, Fewer advanced peripherals Hobbyist projects, Simple embedded systems ATmega328, ATmega32u4, ATtiny45, ATtiny44, ATtiny412, ATtiny1614
ARM Cortex-M0+ Energy efficient, Wide range of tools, High performance for 32-bit More complex to program than AVR, Higher cost Low power applications, IoT devices, Wearables SAMD21, RP2040
RISC-V (32-bit) Open source architecture, Scalable, Flexible Less mature ecosystem, Fewer ready-to-use libraries IoT, Educational purposes, Research projects ESP-32C3
Xtensa (LX6/LX7) Highly customizable, Good for DSP applications, Integrated Wi-Fi and Bluetooth Proprietary, Requires licensing for commercial use Connected devices, Smart home, Audio processing ESP32-S3, ESP32-WROOM
Tensilica L106 (32-bit) Low cost, Integrated Wi-Fi, Good SDK support Single core, Less powerful than newer ESP32 models IoT, Budget Wi-Fi projects ESP8266
ARM Cortex-M4F High performance, Floating point unit, Energy efficient More expensive than simpler cores, Complex for beginners High-performance embedded systems, Industrial control, Advanced IoT devices Nucleo-L432KC, nRF52840

- MCUs -

Here we have all the chips that can be used and found so that we can compare them in the Lab:

Features ATTiny45/85
CPU 8-bit AVR
Clock speed Up to 20 MHz
Bit depth 8-bit
Storage 4KB/8KB of Flash memory, 256B/512B EEPROM, 256B/512B SRAM
I/O pins 6 I/O Pins
Power 2.7V - 5.5V
Dimensions SOIC-8 (0.209", 5.30mm)
Bluetooth No
WiFi No
How to program Through ISP (In-System Programming)
Special Functions PWM, ADC
Features ATTiny44/84
CPU 8-bit AVR
Clock speed Up to 20 MHz
Bit depth 8-bit
Storage 4KB/8KB of Flash memory, 256B/512B EEPROM, 512B/1KB SRAM
I/O pins 12 I/O Pins
Power 1.8V - 5.5V
Dimensions SOIC-14 (.150", 3.90mm)
Bluetooth No
WiFi No
How to program Through ISP (In-System Programming)
Special Functions PWM, ADC, USI (Universal Serial Interface)
Features ATTiny412
CPU 8-bit AVR
Clock speed Up to 20 MHz
Bit depth 8-bit
Storage 4KB Flash memory, 256B EEPROM, 256B SRAM
I/O pins 12 I/O Pins
Power 1.8V - 5.5V
Dimensions SOIC-8 (0.209", 5.30mm)
Bluetooth No
WiFi No
How to program UPDI (Unified Program and Debug Interface)
Special Functions PWM, ADC, Configurable Custom Logic (CCL), Event System
Features ATTiny1614
CPU 8-bit AVR
Clock speed Up to 20 MHz
Bit depth 8-bit
Storage 16KB Flash memory, 256 Bytes EEPROM, 2KB SRAM
I/O pins 14 I/O Pins
Power 1.8V - 5.5V
Dimensions SOIC-14 (.150", 3.90mm)
Bluetooth No
WiFi No
How to program UPDI (Unified Program and Debug Interface)
Special Functions PWM, ADC, USART, SPI, I2C, Configurable Custom Logic (CCL)
Features ATmega328P
CPU 8-bit AVR
Clock speed Up to 20 MHz
Bit depth 8-bit
Storage 32KB of Flash memory, 1KB EEPROM, 2KB SRAM
I/O pins 23 I/O Pins
Power 1.8V - 5.5V
Dimensions TQFP32 / 7x7mm
Bluetooth No
WiFi No
How to program Through ISP (In-System Programming) or via a bootloader
Special Functions PWM, ADC, USART, SPI, I2C (TWI), Watchdog Timer, Analog Comparator
Features ATmega32U4
CPU 8-bit AVR
Clock speed Up to 16 MHz
Bit depth 8-bit
Storage 32KB Flash memory, 1KB EEPROM, 2.5KB SRAM
I/O pins 25 I/O Pins
Power 2.7V - 5.5V
Dimensions TQFP44 / 10x10mm
Bluetooth No
WiFi No
How to program Through ISP (In-System Programming), JTAG, or via a bootloader with USB support
Special Functions USB 2.0 Full-speed/Low Speed Device Module, PWM, ADC, USART, SPI, I2C
Features SAMD21
CPU 32-bit ARM Cortex-M0+
Clock speed Up to 48 MHz
Bit depth 32-bit
Storage 256KB Flash, 32KB SRAM
I/O pins Up to 52 I/O pins
Power 1.62V - 3.63V
Dimensions 20x17.5x3.5 mm
Bluetooth No
WiFi No
How to program SWD (Serial Wire Debug)
Special Functions USB 2.0 Device/Host, ADCs, DACs
Features RP2040
CPU Dual-core ARM Cortex-M0+
Clock speed Up to 133 MHz
Bit depth 32-bit
Storage 264KB of SRAM, and 2MB of on-board Flash memory
I/O pins 30 GPIO pins
Power 1.8V - 5.5V
Dimensions 20x17.5x3.5 mm
Bluetooth No
WiFi No
How to program USB mass-storage boot mode, drag-and-drop programming
Special Functions USB 1.1 Host/Device, 8 Programmable I/O (PIO) state machines
Features ESP32-C3
CPU 32-bit RISC-V single-core processor
Clock speed Up to 160 MHz
Bit depth 32-bit
Storage Up to 4MB SPI flash memory, 400KB SRAM
I/O pins 22 GPIO pins
Power 3.0V – 3.6V
Dimensions 20x17.5x3.5 mm
Bluetooth Yes (Bluetooth 5.0 LE)
WiFi Yes (802.11 b/g/n)
How to program Serial/UART programming, OTA (Over-The-Air)
Special Functions Wi-Fi 4 (802.11n), Bluetooth 5.0 LE, Rich set of peripherals
Features ESP32-WROOM
CPU Tensilica Xtensa LX6 dual-core processor
Clock speed Up to 240 MHz
Bit depth 32-bit
Storage Up to 4MB Flash memory, 520KB SRAM
I/O pins Up to 36 GPIO pins
Power 2.2V - 3.6V
Dimensions 18mm x 25.5mm x 3.1mm
Bluetooth Yes (Bluetooth 4.2 BR/EDR and BLE standards)
WiFi Yes (802.11 b/g/n)
How to program Serial/UART programming, OTA (Over-The-Air), test pins
Special Functions Wi-Fi Direct, soft-AP, SDIO/SPI/UART interfaces, integrated sensors
Features ESP32-S3
CPU Xtensa LX7 dual-core processor
Clock speed Up to 240 MHz
Bit depth 32-bit
Storage Up to 16MB of SPI Flash memory, 512KB internal SRAM
I/O pins 45 GPIO pins
Power 2.3V – 3.6V
Dimensions 25.5 mm x 18 mm x 3.1 mm
Bluetooth Yes (Bluetooth 5.0, including long-range and BLE)
WiFi Yes (802.11 b/g/n, 802.11n (2.4 GHz), including mesh networking)
How to program UART, SPI, SDIO, I2C programming interfaces; OTA (Over-The-Air)
Special Functions Neural Processing Unit for AI applications, USB OTG, Temperature sensor, Secure boot, Touch sensor, ADC, DAC
Features ESP8266
CPU Tensilica L106 32-bit microprocessor
Clock speed Up to 80 MHz (can be overclocked to 160 MHz)
Bit depth 32-bit
Storage Up to 16MB of external QSPI flash, 32KB of instruction RAM, 80KB of user data RAM
I/O pins 17 GPIO pins
Power 2.5V - 3.6V
Dimensions 24 x 16 x 3 (±0.2)mm
Bluetooth No
WiFi Yes (802.11 b/g/n)
How to program Through Serial/UART interface, OTA (Over-The-Air)
Special Functions Integrated TCP/IP protocol stack, Wi-Fi Direct (P2P), soft-AP
Features STM32L432KC
CPU ARM Cortex-M4 with FPU and DSP instructions
Clock speed Up to 80 MHz
Bit depth 32-bit
Storage 256KB Flash memory, 64KB SRAM
I/O pins 32 I/O Pins
Power 1.71V - 3.6V
Dimensions Varies by package
Bluetooth No
WiFi No
How to program ST-LINK/V2-1, Serial Wire Debug (SWD), bootloader
Special Functions Low power modes, 12-bit ADCs, DAC, Comparators, Op-Amps, USART, SPI, I2C, USB 2.0 Full Speed, TIMERS, PWR management
Features nRF52840
CPU 32-bit ARM Cortex-M4
Clock speed 64 MHz
Bit depth 32-bit
Storage 1MB Flash, 256KB RAM
I/O pins 48 I/O pins
Power 1.7V – 5.5V
Dimensions 20x17.5x3.5 mm
Bluetooth Yes (Bluetooth 5)
WiFi No
How to program Nordic development tools, SDK
Special Functions USB 2.0, NFC, Zigbee, Thread

- Comparison -

Looking at the quantity of avilable MCUs one can only wonder, how can i choose the right one, well for starters we can see the previous tables and look for the function I need be it Bluetooth, USB, etc. Another way too look at it is to see the dimensions and think if it works inside of the project i'm building. But on of the most important ones are performance. Every architecture works differently so we're gonne do a couple of tests to compare them.

Arithmetic Operations

One thing that's kinda obvious is that when you're working with an 8-bit microcontroller, you're kinda stuck with numbers between 0 and 255. If you wanna go negative, you can only go from -127 to 128. But if you're trying to deal with bigger numbers, you've gotta split them up and juggle them across different parts, which can get a bit tricky since the microcontroller itself isn't gonna help you out with that.If you bump up to a 32-bit microcontroller, you're in for a treat because now you can play with huge numbers, all the way up to over 4 billion (just to keep it simple, that's for unsigned numbers; signed numbers are a whole other story). So let's start with a couple of test with code given to us by our instructor.

Speed test Code ATMEGA328p 8-bit architecture

                                void setup() {
                                    Serial.begin(9600);
                                    while (!Serial); // Wait for the serial port to open
                                  
                                    // Determine the bit depth of commonly used data types
                                    Serial.print("Bit Depth - byte: ");
                                    Serial.println(sizeof(byte) * 8);
                                    Serial.print("Bit Depth - int: ");
                                    Serial.println(sizeof(int) * 8);
                                    Serial.print("Bit Depth - long: ");
                                    Serial.println(sizeof(long) * 8);
                                    Serial.print("Bit Depth - float: ");
                                    Serial.println(sizeof(float) * 8);
                                    Serial.print("Bit Depth - double: ");
                                    Serial.println(sizeof(double) * 8);
                                  
                                    //Overwrite Timer1 Configs to work without prescale at 16 MHz
                                    TCCR1B = (1  CS10);
                                  
                                    // Test arithmetic operations using hardware timers
                                    unsigned long endTime1,endTime2,endTime3;
                                    int testValue = 12345678910; // A large number for 32-bit, but will overflow on 8-bit
                                    int result;
                                    //Start Timer at 0
                                    TCNT1=0;
                                    result = testValue + testValue;
                                    endTime1 = TCNT1;//Timestamp Addition
                                    result = testValue * testValue;
                                    endTime2 = TCNT1;; //Timestamp Multiplication
                                    result = testValue / 2;
                                    endTime3 = TCNT1; //Timestamp Division
                                  
                                    //Print Times per operation
                                    Serial.print("Addition Time (nanoseconds): ");
                                    Serial.println(endTime1*62.5);
                                    Serial.print("Multiplication Time (nanoseconds): ");
                                    Serial.println((endTime2*62.5)-(endTime1*62.5));
                                    Serial.print("Division Time (nanoseconds): ");
                                    Serial.println((endTime3*62.5)-(endTime2*62.5));
                                  
                                  }
                                  
                                  void loop() {
                                    // No need to repeat the measurements
                                  }                                  
                            

- Cheat Sheet -

As an added bonus we decided to do a Cheat sheet of commonly used commands for Embedded Programming both for python and for c++

Common Syntax Rules

Variables and Data Types

Python: Python simplifies variable usage and data type declaration. Variables are dynamically typed, which means the interpreter infers the data type based on the assigned value, making Python very flexible and easy to use for developers.

C++: In C++, variables must be declared with an explicit data type before they are used. This static typing adds to the language's robustness, enabling compile-time type checking and optimization that can lead to more efficient code execution.

Python

                              # Variables
                              x = 10          # int
                              y = 20.5        # float
                              name = "Alice"  # str
                              is_valid = True # bool
                              # No explicit type declaration needed.
                            
cpp

                              // Variables
                              int x = 10;         
                              float y = 20.5;     
                              std::string name = "Alice"; 
                              bool is_valid = true;       
                              // Type must be explicitly declared.
                            

Control Structures

Python: Python's control structures are designed for readability and simplicity. The use of indentation to define block scopes makes Python code very clean and easy to understand, even for beginners.

C++: C++ employs braces ({}) to define the scope of control structures, providing a familiar syntax for those with experience in C or similar languages. It supports a wide range of control flow statements, allowing for complex decision-making and looping with efficiency.

Python

                              # If-Else
                              if x > 10:
                                  print("Greater")
                              elif x == 10:
                                  print("Equal")
                              else:
                                  print("Lesser")

                              # Loops
                              for i in range(5):  # For loop
                                  print(i)

                              i = 0
                              while i < 5:        # While loop
                                  print(i)
                                  i += 1
                            
cpp

                              // If-Else
                              if (x > 10) {
                                  std::cout << "Greater" << std::endl;
                              } else if (x == 10) {
                                  std::cout << "Equal" << std::endl;
                              } else {
                                  std::cout << "Lesser" << std::endl;
                              }

                              // Loops
                              for (int i = 0; i < 5; i++) { // For loop
                                  std::cout << i << std::endl;
                              }

                              int i = 0;
                              while (i < 5) {               // While loop
                                  std::cout << i << std::endl;
                                  i++;
                              }
                            

Functions

Python: Functions in Python are defined using the def keyword. Python supports a variety of function arguments and allows for easy definition of default, keyword, and variable-length arguments, facilitating flexible function calls.

C++: C++ functions must specify the return type explicitly. They provide a powerful feature set, including overloading, default parameters, and templates, which allow for writing generic and reusable code components.

Python

                              # Defining a function
                              def add_numbers(a, b):
                                  return a + b

                              # Calling a function
                              result = add_numbers(5, 3)
                            
cpp

                              // Defining a function
                              int addNumbers(int a, int b) {
                                  return a + b;
                              }

                              // Calling a function
                              int result = addNumbers(5, 3);
                            

Comments

Python: Python supports single-line comments initiated with # and multi-line comments enclosed within triple quotes, which can also be used for docstrings in functions, classes, and modules.

C++: ++ uses // for single-line comments and /* */ for multi-line comments. This convention is consistent with many programming languages derived from or inspired by C, making it familiar to a broad audience.

Python

                              # This is a single-line comment.

                              '''
                              This is a
                              multi-line comment.
                              '''
                            
cpp

                              // This is a single-line comment.

                              /*
                              This is a
                              multi-line comment.
                              */
                            

Basic GPIO use

There's three basic commands for basic GPIO(General Purpose Input Output) these area:

  • Initializing a Pin This is like setting up a pin on your microcontroller to either listen in or shout out. When you initialize a pin as an Input, it's like giving it ears to listen for signals coming in. When you set it as an Output, it's like giving it a mouth to send out signals.
  • Reading a Pin This means checking what the pin is hearing. Is it a high signal (1), like someone shouting "Yes!"? Or is it a low signal (0), more like a whisper of "No"? It's all about figuring out what message the pin is receiving.
  • Writing a Pin This is about sending a message out. When you write to a pin set as an Output, you're telling it to either shout out a "Yes!" (send a high signal or 1) or whisper a "No" (send a low signal or 0). If you mention writing to a pin set as an Input, that's a mix-up; you don't "write" or send messages through ears. You only listen with input pins, and you talk (write signals) with output pins.
We'll be doing a side by side comparison on C++ and Python

Basic GPIO Circuit Python

                              # Importar las librerías necesarias
                              import board
                              import digitalio

                              # Initialize pins

                              # Initialize as output
                              led = digitalio.DigitalInOut(board.GP25) 
                              led.direction = digitalio.Direction.OUTPUT
                              # Initialize as input
                              button = digitalio.DigitalInOut(board.GP14) 
                              button.direction = digitalio.Direction.INPUT
                              button.pull = digitalio.Pull.DOWN

                              # Write a pin
                              led.value = True  # Establecer el valor en ALTO
                              led.value = False # Establecer el valor en BAJO

                              # Read a pin
                              button_value = button.value
                            
Basic GPIO Arduino C++

                              //Define Pins
                              #define led 25
                              #define button 27
                              //Initilize pins
                              pinMode(led, OUTPUT);
                              pinMode(button, INPUT);
                              //Write a Pin
                              digitalWrite(led,HIGH);//Set Value HIGH
                              digitalWrite(led,LOW); //Set Value LOW
                              //Read a Pin
                              digitalRead(button);
                            
Basic GPIO MicroPython

                              # Import necessary libraries
                              from machine import Pin

                              # Initialize pins

                              # Initialize as output
                              led = Pin(25, Pin.OUT)  # Assuming GP25 corresponds to pin 25
                              # Initialize as input with pull-down resistor
                              button = Pin(14, Pin.IN, Pin.PULL_DOWN)  # Assuming GP14 corresponds to pin 14

                              # Write to a pin
                              led.value(1)  # Set the value to HIGH
                              led.on()
                              led.value(0)  # Set the value to LOW
                              led.off()
                              led.toggle() # Toggles the value of pin

                              # Read from a pin
                              button_value = button.value()


                            

Analog

Analog readings are all about measuring how much of something is happening. Unlike digital signals, which are like flipping a light switch on or off (just two states), analog signals can vary smoothly across a wide range of values. Think of it like a dimmer switch for your room's lights, where you can adjust the brightness to any level between totally off and super bright.

Analog Python

                              import board
                              import analogio
                              adc = analogio.AnalogIn(board.A0) # Initialize A0 as analog Input 
                              sensorValue = adc.value # Read and save an analog reading
                            
Analog C++

                              #define analogPin = A0; // Set pin analogPin keyword to A0
                              int sensorValue = analogRead(analogPin); // Read analogValue
                            
Analog MicroPython

                              from machine import ADC
                              
                              #Creating an ADC Object
                              adc = ADC(Pin(ADC_PIN))

                              #Configuring ADC Resolution (Optional)
                              adc = ADC(Pin(ADC_PIN))
                              adc.width(ADC.WIDTH_12BIT)  # Set ADC resolution to 12 bits

                              #Reading Analog Values
                              value = adc.read()

                            

Timers

Imagine you're cooking a complicated dish that requires you to keep track of different cooking times for various ingredients. You'd likely use a timer for each one to make sure everything comes out perfectly. In the world of microcontrollers, timers serve a similar purpose, helping you manage when and how often certain pieces of code run. This can be anything from blinking an LED without using delay(), to triggering sensor readings at regular intervals, or even debouncing a button press.

Timers can operate in various modes, like counting up to a specific value, counting down, or repeating actions at set intervals. They're fundamental in moving beyond simple, linear code execution to creating more complex, efficient, and interactive applications.

Basic Delay Micro and Circuit Python

                              import time
                              time.sleep(1)  # Delays for 1 second
                            
Basic Delay Arduino C++

                              //Delays for 1 second
                              delay(1000);
                            
Non Blocking Delay Circuit Python

                              import board
                              import time

                              # Record the start time
                              start_time = time.monotonic()
                              interval = 1  # Time interval in seconds

                              while True:
                                  # Check the current time
                                  current_time = time.monotonic()
                                  
                                  # If the current time is greater than start time + interval
                                  if current_time - start_time >= interval:
                                      # Do something
                                      
                                      # Reset the start time
                                      start_time = current_time
                            
Non Blocking Delay Arduino c++

                              unsigned long previousMillis = 0;

                              // Interval for delay
                              const long interval = 1000;  

                              void loop(){
                                //updates time passed
                                unsigned long currentMillis = millis();

                                //checks If the current time is greater than start time + interval
                                if (currentMillis - previousMillis >= interval) {
                                  //does something

                                  // Reset the start time
                                  previousMillis = currentMillis;
                                }
                              }
                            
Non Blocking Delay MicroPython

                            from machine import Timer

                            def timer_callback(t):
                                print("Timer event!")
                            
                            # Create and start a periodic timer
                            timer = Timer(0)
                            timer.init(mode=Timer.PERIODIC, period=2000, callback=timer_callback)
                            

                            

Printing

In both Arduino (C++) and CircuitPython, printing is typically used to send messages back to the computer over USB, which can be viewed in a serial monitor (in the Arduino IDE) or a serial console (for CircuitPython in the Mu Editor). This capability is incredibly helpful for debugging purposes, to display sensor readings, or to show program status messages.

Printing Circuit and Micro Python

                              import time

                              while True:
                                  #Print Messages
                                  print("Hello, world!")
                                  time.sleep(1)  # Pause for a second

                                  #print Variables
                                  temperature = 25.3  # Example temperature
                                  print (temperature)
                                  
                                  #Print as Formatted String Literal 
                                  print(f"The temperature is {temperature} degrees Celsius.")
                            
Printing Arduino C++

                              void setup() {
                                // Start serial communication at 9600 bauds per second
                                Serial.begin(9600);  
                              }
                              
                              void loop() {
                                // Print a message to the serial monitor
                                Serial.print("Hello, world!"); 
                                // Print a message to the serial monitor with endl
                                Serial.println("Hello, world!");  
                                // Print a variable to monitor
                                Serial.println(counter);
                                // Print in binary, HEX and OCT are also available
                                Serial.println(counter, BIN);
                                delay(1000);  // Wait for a second
                              }                              
                            

PWM

Think of PWM like blinking your eyes. If you blink really fast, it kind of seems like your eyes are open all the time, just a bit less bright. If you blink slowly, it feels like your eyes are mostly closed. PWM works by turning something on and off at a super fast rate, and by changing how long it stays on versus how long it stays off, you can simulate "in-between" states.

Here's the lowdown on how it works:

  • Frequency: This is how fast the PWM signal flips between on and off. Imagine tapping your finger on a table rapidly—that's high frequency. Tapping slowly is low frequency. Most of the time, you set the frequency once and forget about it because what really matters is the next part.
  • Duty Cycle: This is the real magic of PWM. It's all about the ratio of "on" time to the total cycle time (which is the "on" time plus the "off" time). If the duty cycle is 50%, the signal is on half the time and off half the time. If it's 10%, it's only on a tiny bit of the time, so it seems like it's barely on at all.

PWM Circuit Python

                              import board
                              import pwmio
                              
                              # Create a PWMOut object on an LED pin
                              pwm = pwmio.PWMOut(board.D5, duty_cycle=0, frequency=5000,)
                              
                              # Change Duty Cycle Value to max 16 bit
                              pwm.duty_cycle = 65535

                              # Change Duty Cycle Value to max 16 bit
                              pwm.duty_cycle = 0
                            
PWM Arduino C++

                              #define pwm = 5; // PWM on pin 5
                              //set pin 5 as an OUTPUT
                              pinMode(pwm,OUTPUT);
                              //writes the duty cycle with value between 0 and 255
                              int value=127;
                              analogWrite(pwm,value);
                            
PWM Circuit MicroPython

                            from machine import Pin, PWM

                            # Create a PWM object in a pin
                            pwm = PWM(Pin(5))  # IN RP2040 the led is in 5

                            pwm.freq(5000)  # Configure frequency at 5000 Hz

                            # by asigning a value to x you can change the duty cycle
                            pwm.duty(x)

                            

Interrupt

Imagine your computer or a tiny brain in a gadget (microcontroller) is busy doing its thing, like playing a video or controlling lights, and doesn't want to be bothered checking every second if you're pressing a button or if something else needs attention. An "interrupt" is like a quick tap on the shoulder—it tells the device to immediately pause what it's doing, check what's needed (like turning a light on with a button press), and then go right back to what it was doing before. This way, the device can react instantly to important stuff without having to constantly stop and check, making it much more efficient.

Interrupt Micro Python

                              '''
                              Define the callback This function will be called 
                              when the interrupt occurs.
                              '''
                              def callback(p):
                                # Code to execute when interrupt occurs

                              
                              from machine import Pin

                              # Initialize pin
                              pin = Pin(pin_number, Pin.IN, Pin.PULL_UP)  
                              #Initialize the pin input, attach an interrupt to it using Pin.irq()
                              pin.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=callback)


                            
Interrupt Arduino C++

                              void setup() {
                                pinMode(2, INPUT_PULLUP); // Set the button pin
                                /*
                                  Attach an interrupt to a pin 
                                  ISR: Interrupt Service Routine - the function to call when the 
                                       interrupt occurs. 
                                  mode: Determines when the interrupt should be triggered. 
                                        Common modes include CHANGE, RISING, FALLING, and LOW.
                                */
                                attachInterrupt(digitalPinToInterrupt(2), interrupt, FALLING);
                              }
                              
                              void loop() {
                                // Main code here
                              }
                              
                              void interrupt() {
                                // Code to execute when interrupt occurs
                              }                                
                            

Finally for some fun and color here are some example codes to use the neopixel with

Finally as an added bonus here are the UF files needed for MicroPython and for CircuitPython.