6. Embedded Programming

On this week I worked on using the circuit I made weeks before to program a new "circuit" that needed to interact with local devices and communicate with devices. In my case, I created a program that uses 2 buzzers at the same time to play 3 different songs using 3 different buttons and printing the song's name on the serial monitor. For learning the basics about Arduino, MicroPython and CircuitPython (CircuitPython wasn't used on this task) programming, you can visit the group task webiste.

Arduino

For the first part of this task, I program my idea using the Arduino IDE. This is an open software that has a lot of different libraries that theorically, makes programming much easier. You can download the Arduino IDE here. It is important to specify that this software is based on C language, so preferably you should have worked with C before. To use the SEEED XIAO RP2040 with Arduino, I connected it to my computer while pushing the "reset" button. After installing the SEEED XIAO library using this tutorial, I selected the "UF2_board" port with the "Seeed XIAO RP2040" board. Once you upload an Arduino code, you can use the XIAO RP2040 as an Arduino board.

Now I am going to explain step by step my code:

    
    #include "pitches.h"
    
    const int buzzerMelodiaPin = 28;
    const int buzzerAcompanamientoPin = 29;
    							
    #define pr 2
    #define dbz 4
    #define sonic 3
    
    unsigned long previousMillis = 0;
    bool isPlaying = false;
    int noteIndex = 0;
    int* currentMelody = nullptr;
    int* currentHarmony = nullptr;
    int* currentDurationsMelody = nullptr;
    int* currentDurationsHarmony = nullptr;
    int currentLength = 0;
  • Notes, Pins and Control: First, for including all the music notes I will be using, I used a library called "pitches", you can download the library from Google or you can copy and paste all the notes on the code like I did. Next, I created some variables for the pins I will be using, for the buzzers I used 2 PWM pins and for the buttons I used 3 pins that were together. Finally, we have the control variables, "previousMillis" is used to store the last time a note was played, "isPlaying" is used to know if a melody is being played, "noteIndex" is used as the index of the playin note, "current..." are used as the references for the songs arrays and "currentLength" is used as the length of the melody array.
  •  
    int melodyPR[] = {NOTE_E4, NOTE_E4, NOTE_B4, NOTE_E4, NOTE_E4, ...};
    int harmonyPR[] = {NOTE_E3, NOTE_E3, NOTE_E3, NOTE_E3, NOTE_E3, ...};
    int durationsMelodyPR[] = {125, 125, 125, 125, 125, ...};
    int durationsHarmonyPR[] = {500, 500, 500, 500, 500, ...};
    
    
    int melodySonic[] = {0, 0, 0, 0, NOTE_C6, ...};
    int harmonySonic[] = {NOTE_F3, NOTE_F3, NOTE_F3, NOTE_F3, NOTE_F3, ...};
    int durationsMelodySonic[] = {125, 125, 125, 125, 125, ...};
    int durationsHarmonySonic[] = {125, 125, 125, 125, 125, ...};
    
    
    int melodyDBZ[] = {0, NOTE_A5, 0, NOTE_GS5, NOTE_E5, ...};
    int harmonyDBZ[] = {NOTE_E3, NOTE_E3, NOTE_D3, NOTE_D3, NOTE_E3,
      NOTE_E3, NOTE_D3, NOTE_D3, NOTE_C3, NOTE_C3, 0};
    int durationsMelodyDBZ[] = {250, 125, 125, 125, 125, ...};
    int durationsHarmonyDBZ[] = {1000, 1000, 1000, 1000, 1000,
    	1000, 1000, 1000, 1000, 1000, 500};
    						
  • Song Notes and Times: This part of the code is where the songs are played using all of the song's notes and the times each note is going to be played. Each song has 4 arrays divided in 2 arrays per buzzer, we have 2 arrays for the melody (notes and times) and the other 2 for the harmony (notes and times).
  • 
    void setup() {
        pinMode(buzzerMelodiaPin, OUTPUT);
        pinMode(buzzerAcompanamientoPin, OUTPUT);
        
        pinMode(pr, INPUT);
        pinMode(dbz, INPUT);
        pinMode(sonic, INPUT);
    
        Serial.begin(9600);
    }
    						
  • Void Setup: This function is used to set the pins used as inputs or outputs. Here is also were the serial monitor is turned on.
  • 
    void playNotes() {
      if (isPlaying && noteIndex < currentLength) {
        unsigned long currentMillis = millis();
        if (currentMillis - previousMillis >= currentDurationsMelody[noteIndex]) {
          if (currentMelody[noteIndex] != 0) {
            tone(buzzerMelodiaPin, currentMelody[noteIndex], currentDurationsMelody[noteIndex]);
          } else {
            noTone(buzzerMelodiaPin);
          }
          if (currentHarmony[noteIndex] != 0) {
            tone(buzzerAcompanamientoPin, currentHarmony[noteIndex], currentDurationsHarmony[noteIndex]);
          } else {
            noTone(buzzerAcompanamientoPin);
          }
          previousMillis = currentMillis;
          noteIndex++;
        }
      } else if (noteIndex >= currentLength && isPlaying) {
        noTone(buzzerMelodiaPin);
        noTone(buzzerAcompanamientoPin);
        isPlaying = false;
        noteIndex = 0;
      }
    }
    						
  • Function "playNotes": This function is kinda complex but I will try to make it easy to understand. The first thing that the function does is verify if there is a melody playing and if the actual note index is lower than the total melody length. Then, the code uses "millis" to control the melody rythm. Next, it checks if the note of "correntMelody" isn't 0, if it isn't 0, it uses "tone" to play the melody in the buzzer. If the note is 0, it uses "noTone" to stop any sound in the buzzer. Then, it updates "previousMillis" and it increases "noteIndex". Finally, if "noteIndex" reaches or exceeds "currentLength" it stops any sound in the buzzers setting the system ready to start another melody.
  • 
    void loop() {
      if (!isPlaying) {
        if (digitalRead(pr) == HIGH) {
          Serial.print("Power Rangers Theme!!");
          currentMelody = melodyPR;
          currentHarmony = harmonyPR;
          currentDurationsMelody = durationsMelodyPR;
          currentDurationsHarmony = durationsHarmonyPR;
          currentLength = sizeof(melodyPR) / sizeof(int);
          isPlaying = true;
          noteIndex = 0;
          previousMillis = millis();
        } else if (digitalRead(dbz) == HIGH) {
          Serial.print("Dragon Ball Z Theme!!");
          currentMelody = melodyDBZ;
          currentHarmony = harmonyDBZ;
          currentDurationsMelody = durationsMelodyDBZ;
          currentDurationsHarmony = durationsHarmonyDBZ;
          currentLength = sizeof(melodyDBZ) / sizeof(int);
          isPlaying = true;
          noteIndex = 0;
          previousMillis = millis();
        } else if (digitalRead(sonic) == HIGH) {
          Serial.print("Sonic Theme!!");
          currentMelody = melodySonic;
          currentHarmony = harmonySonic;
          currentDurationsMelody = durationsMelodySonic;
          currentDurationsHarmony = durationsHarmonySonic;
          currentLength = sizeof(melodySonic) / sizeof(int);
          isPlaying = true;
          noteIndex = 0;
          previousMillis = millis();
        }
      }
    
      playNotes();
    }
    						
  • Void Loop: This is the part of the code that is going to be in a loop while the XIAO has power. On this section is where I used the conditional "if" to reproduce each song with the buttons. Inside the "if" conditionals, I just assigned the song's arrays for each button and the serial print of each song.

This is a video of the working code on Arduino:

MicroPython

To use MicroPython, I need some essential things. First, I downloaded a software called "Thonny", the link to this software is this one. After downloading the software, I connected the XIAO RP2040 to my computer while I was pushing the "reset" button. I did this to paste the UF file needed for MicroPython. You can find this file in the last part of the group task webiste group task webiste. After pasting the file on the Seeed XIAO location, you can use Thonny to program your board. To select your board on Thonny, you should check the right bottom corner of the main page and select thhe "MicroPython (Raspberry Pi Pico)" option.

Now, I am going to explain my MicroPython code.

    
    from machine import PWM
    from machine import Timer
    import time
    from machine import Pin
    
    B0 = 31
    C1 = 33
    CS1 = 35
    D1 = 37
    DS1 = 39
    ...
    
    pwm = PWM(28)  # create a PWM object on a pin
    pwm2 = PWM(29)
    pr = Pin(2, Pin.IN, Pin.PULL_DOWN)
    dbz = Pin(4, Pin.IN, Pin.PULL_DOWN)
    sonic = Pin(3, Pin.IN, Pin.PULL_DOWN)
    
  • Setup: On this first part of the code, I imprted the necessary libraries for the code, I created the variables used for saving the notes frequencies, I defined the PWM pins (the PWM is needed for using the buzzers) and I created the variables used for the selecting song buttons.
  •  
    def ms125(melodia, harmonia):
        if melodia > 10: 
            pwm.freq(melodia)
            pwm.duty_u16(65000)
            
        else:
            pwm.duty_u16(0)
        if harmonia > 10:
            pwm2.freq(harmonia)
            pwm2.duty_u16(65000)
        else:
            pwm2.duty_u16(0)
        
        time.sleep(.125)
        pwm.duty_u16(0)
        pwm2.duty_u16(0)
        time.sleep(0.05)
    
    def ms250(melodia, harmonia)
    ...
    						
  • Functions Needed: After the setup part, I created different functions with different delays (time.sleep on MicroPython) that will be the time a same note is going to be played. For the functions, I had 2 variables that are going to be used for the melody and harmony notes. Finally, I have some conditions that will be used to stop one or both of the buzzers when it is requiered.
  • 
    while True:
        pr_value = pr.value()
        dbz_value = dbz.value()
        sonic_value = sonic.value()
        
        if pr_value==1:
            print("Power Rangers Theme!!")
            ms125(E3, E4)
            ms125(E3, E4)
            ms125(E3, B4)
            ms125(E3, E4)
            ms125(E3, E4)
            ms125(E3, B4)
            ms125(E3, E4)
            ms125(E3, E4)
    	...
    						
  • The Loop: In this case, the the part of the code that will be looped is the one inside the "while True". It is in this part where the songs are going to be played. For MicroPython I used an easier logic to play the songs. First, I created variables that will read the button state and then I use the "if" conditional to define which song is going to be played depending on the pushed button. Inside each "if", I have the serial printing of each song and all the functions I mentioned before with the notes that need to be played.

Now, here is a video of the MicroPython code working:

Advantages And Disadvantages

Depending on your needs and level of programming experience, you can choose between using Arduino or MicroPython for projects using buzzers. MicroPython is an easy-to-use programming language that is ideal for novices or for quickly creating prototypes. It functions nicely with hardware and has simple coding. Because of its accurate timing and abundance of libraries, Arduino offers you more control and efficiency, which makes it a better choice for large projects requiring precision operation. Whichever option you choose, will depend on your preferences. In my particular case, I prefer using Arduino because I have more experience using it, but to be honest, and thanks to some university classes, the MicroPython code was easier and simpler to program and better for checking errors.

After that conclusion, if you want to download the Arduino and MicroPython codes, click here.

Get in touch

Follow