Two "bricked" Xiaos [how to fix and how'd it happen]?

So to start I have two Xiaos that won’t go into bootloader no matter how much I try to short the pads. I know how to do it normally because I’m constantly needing to trigger the bootloader.

Neither one will work even plugged into a battery pack, so it’s not just a PC thing.

What do I do? How do I get them so they can work again?

Also they both died after running the same sketch. I was just playing around with sounds on a piezo speaker. Can anyone spot anything in the code that would cause the board to kill itself?

I basically have a 2D array that I’m storing frequencies and duration in. As the loop function runs, it plays a little bit of the current note (durSeg) and keeps track of how much of that particular note needs to still play.

Once it’s played enough of that note, we copy all of the other array indices forward, and set the last index to 0,0. A non-zero duration is what triggers the code to play something. Otherwise it just skips the audio portion.

As part of the main loop, its also checking for a button press. If there is one, we add a tone and duration to the 0th index of the play track.

Otherwise there’s some code to add in some music (both at the start and into the track).

That’s it, but something apparently is killing my Xiaos here…

Summary
#define durSeg 30
#define wn 1000/durSeg
#define hn 500/durSeg
#define qn 250/durSeg
#define en 125/durSeg
#define sn 62/durSeg




#define R1 0
#define NB0  31
#define NC1  33
#define NCS1 35
#define ND1  37
#define NDS1 39
#define NE1  41
#define NF1  44
#define NFS1 46
#define NG1  49
#define NGS1 52
#define NA1  55
#define NAS1 58
#define NB1  62
#define NC2  65
#define NCS2 69
#define ND2  73
#define NDS2 78
#define NE2  82
#define NF2  87
#define NFS2 93
#define NG2  98
#define NGS2 104
#define NA2  110
#define NAS2 117
#define NB2  123
#define NC3  131
#define NCS3 139
#define ND3  147
#define NDS3 156
#define NE3  165
#define NF3  175
#define NFS3 185
#define NG3  196
#define NGS3 208
#define NA3  220
#define NAS3 233
#define NB3  247
#define NC4  262
#define NCS4 277
#define ND4  294
#define NDS4 311
#define NE4  330
#define NF4  349
#define NFS4 370
#define NG4  392
#define NGS4 415
#define NA4  440
#define NAS4 466
#define NB4  494
#define NC5  523
#define NCS5 554
#define ND5  587
#define NDS5 622
#define NE5  659
#define NF5  698
#define NFS5 740
#define NG5  784
#define NGS5 831
#define NA5  880
#define NAS5 932
#define NB5  988
#define NC6  1047
#define NCS6 1109
#define ND6  1175
#define NDS6 1245
#define NE6  1319
#define NF6  1397
#define NFS6 1480
#define NG6  1568
#define NGS6 1661
#define NA6  1760
#define NAS6 1865
#define NB6  1976
#define NC7  2093
#define NCS7 2217
#define ND7  2349
#define NDS7 2489
#define NE7  2637
#define NF7  2794
#define NFS7 2960
#define NG7  3136
#define NGS7 3322
#define NA7  3520
#define NAS7 3729
#define NB7  3951
#define NC8  4186
#define NCS8 4435
#define ND8  4699
#define NDS8 4978


int sT[48][2] = {
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0},
{0,0}
  
};
int pH = 0;



void stAdd(int type=0){
sT[0][0] = 3200;
sT[0][1] = 1;


}

void playST(){
int f = sT[0][0];
int d = sT[0][1];
int noteBreak = d*1.3;
//noTone(6);  
if (d != 0){
  tone(6, f, durSeg);
    delay(durSeg*1.3);
  if(d<2){
    for (int x = 0; x<sizeof(sT)-1; x++){
      sT[x][0]= sT[x+1][0];
      sT[x][1]= sT[x+1][1];
    }  
    sT[sizeof(sT)-1][0]=0;
    sT[sizeof(sT)-1][1]=0;
    delay(noteBreak);
  }else{
    sT[0][1]--;
   }
}
//noTone(6);

}





void setup() {

int button_state = 1;
pinMode(2, INPUT_PULLUP);


sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NE4;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NF5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NE4;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NE5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = en;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NB5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NC5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =ND5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NC5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NA5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NG5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NE5;
sT[pH][1] = qn;
pH++;

sT[pH][0] =NF5;
sT[pH][1] = hn;
pH++;

sT[pH][0] =NF5;
sT[pH][1] = hn;
pH++;


tone(6, NA4, 250);
delay(250*1.3);

tone(6, NC5, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, NC4, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NF5, 250);
delay(250*1.3);

tone(6, NE5, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, NE5, 250);
delay(250*1.3);

tone(6, NE5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NA5, 250);
delay(250*1.3);

tone(6, ND5, 250);
delay(250*1.3);

tone(6, NC5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NE5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NG5, 250);
delay(250*1.3);

tone(6, NA5, 250);
delay(250*1.3);

delay(1000);
}

void loop() {
playST();


int button_state = digitalRead(2);
if(button_state==0){
  stAdd(0);
 

}

}

Hi NotBlaine

I tried uploading your code to my XIAO. I don’t know the cause, but my XIAO has become a brick as well.
When I checked this flash memory using ST-Link, the beginning address “0x00-0xff” of bootloader was overwritten with data “0xff”. Unfortunately you have no choice but to rewrite bootloader. Please refer to the following for how to write bootloader. Once rewritten, bootloader area will be protected, so I don’t think same trouble will occur again.

How to unbrick a dead XIAO using ST-LINK and OpenOCD

It looks like your loop logic is wrong. This:

 if(d<2){
    for (int x = 0; x<sizeof(sT)-1; x++){
      sT[x][0]= sT[x+1][0];
      sT[x][1]= sT[x+1][1];
    }  
    sT[sizeof(sT)-1][0]=0;
    sT[sizeof(sT)-1][1]=0;
    delay(noteBreak);
  }

should be like this:

        if(d < 2)
        {
            int final_entry_index = (sizeof sT)/(sizeof sT[0]) - 1;
            for (int x = 0; x < final_entry_index; x++)
            {
                sT[x][0]= sT[x+1][0];
                sT[x][1]= sT[x+1][1];
            }  
            sT[final_entry_index][0]=0;
            sT[final_entry_index][1]=0;
            
            delay(noteBreak);
        }

In the orginal, you are addressing memory that you do not own, who knows what you are overwriting. That is why your XIAO is bricked.

I appreciate the reply, I’ll try that. I had been using sizeof as basically an equivalent to object.length() and…it’s not.

They should really put a note or warning on the sizeof() Arduino reference.

Like Note that sizeof returns the total number of bytes.

Or something.

It’s a well known C operator pal, and it does not work any differently on the XIAO to anywhere else.

I hope you don’t mind @NotBlaine, but I fiddled about with your code, trying to ascertain what it is doing and I came up with the following, where I’ve removed about 100 lines of code and made it a little more readable, I’ve added in some comments about the code too.

I’ve also introduced the use of Interrupt Pins, I hope you find it useful (I’ve not run it, so I don’t know if it works, but I bellieve I have kept your functionality faithfully).

#include <Arduino.h>

#define durSeg 30
#define wn 1000/durSeg
#define hn 500/durSeg
#define qn 250/durSeg
#define en 125/durSeg
#define sn 62/durSeg

#define R1 0
#define NB0  31
#define NC1  33
#define NCS1 35
#define ND1  37
#define NDS1 39
#define NE1  41
#define NF1  44
#define NFS1 46
#define NG1  49
#define NGS1 52
#define NA1  55
#define NAS1 58
#define NB1  62
#define NC2  65
#define NCS2 69
#define ND2  73
#define NDS2 78
#define NE2  82
#define NF2  87
#define NFS2 93
#define NG2  98
#define NGS2 104
#define NA2  110
#define NAS2 117
#define NB2  123
#define NC3  131
#define NCS3 139
#define ND3  147
#define NDS3 156
#define NE3  165
#define NF3  175
#define NFS3 185
#define NG3  196
#define NGS3 208
#define NA3  220
#define NAS3 233
#define NB3  247
#define NC4  262
#define NCS4 277
#define ND4  294
#define NDS4 311
#define NE4  330
#define NF4  349
#define NFS4 370
#define NG4  392
#define NGS4 415
#define NA4  440
#define NAS4 466
#define NB4  494
#define NC5  523
#define NCS5 554
#define ND5  587
#define NDS5 622
#define NE5  659
#define NF5  698
#define NFS5 740
#define NG5  784
#define NGS5 831
#define NA5  880
#define NAS5 932
#define NB5  988
#define NC6  1047
#define NCS6 1109
#define ND6  1175
#define NDS6 1245
#define NE6  1319
#define NF6  1397
#define NFS6 1480
#define NG6  1568
#define NGS6 1661
#define NA6  1760
#define NAS6 1865
#define NB6  1976
#define NC7  2093
#define NCS7 2217
#define ND7  2349
#define NDS7 2489
#define NE7  2637
#define NF7  2794
#define NFS7 2960
#define NG7  3136
#define NGS7 3322
#define NA7  3520
#define NAS7 3729
#define NB7  3951
#define NC8  4186
#define NCS8 4435
#define ND8  4699
#define NDS8 4978

#define OUTPUT_PIN 6
#define INPUT_PIN 2


/*****
 * Let's use a struct to store the pairs of Frequency and duration in
 * That way we can operate on a single dimensioned array. This makes 
 * the code a lot easier to read and understand
 * *****************************************************************/
typedef struct My_Pair
{
    uint16_t Frequency;
    float Duration;
} My_Pair;

/*****
 * Since we know all of the initial values at startup, we can just 
 * assign tas we initialize our array: there's no need to wait for 
 * the startup() method
 * *****************************************************************/
My_Pair sT[48] = 
{
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NE4, .Duration = qn },
    My_Pair{ .Frequency = NF5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NG5, .Duration = qn },
    My_Pair{ .Frequency = NG5, .Duration = en },
    My_Pair{ .Frequency = NG5, .Duration = en },
    My_Pair{ .Frequency = NG5, .Duration = qn },
    My_Pair{ .Frequency = NG5, .Duration = en },
    My_Pair{ .Frequency = NG5, .Duration = en },
    My_Pair{ .Frequency = NG5, .Duration = qn },
    My_Pair{ .Frequency = NE4, .Duration = qn },
    My_Pair{ .Frequency = NE5, .Duration = qn },
    My_Pair{ .Frequency = NG5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = en },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NB5, .Duration = qn },
    My_Pair{ .Frequency = NC5, .Duration = qn },
    My_Pair{ .Frequency = ND5, .Duration = qn },
    My_Pair{ .Frequency = NC5, .Duration = qn },
    My_Pair{ .Frequency = NA5, .Duration = qn },
    My_Pair{ .Frequency = NG5, .Duration = qn },
    My_Pair{ .Frequency = NE5, .Duration = qn },
    My_Pair{ .Frequency = NF5, .Duration = hn },
    My_Pair{ .Frequency = NF5, .Duration = hn },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 },
    My_Pair{ .Frequency = 0, .Duration = 0 }
};

/*****
 * Forward declare of our functions.
 * 
 * If we provide function declarations at the top of the file, we
 * can make calls to those functions without worrying about where
 * the implementation is within the file.
 * *****************************************************************/
const uint32_t SETUP_DELAY = 250 * 1.3;
void __tone(uint32_t frequency, uint32_t duration = 250, uint32_t delay = SETUP_DELAY, uint8_t pin = OUTPUT_PIN);
void ACTION_REQUESTED_ISR();
void AddSt(int type = 0);
void PlaySt();
void PlaySetupTones();

/*****
 * This member will keep track of whether or not the `perform action` 
 * signal has been raised.
 * 
 * It is declared *volatile* because it is modified both by the loop()
 * and by the interrupt routine.
 * *****************************************************************/
volatile bool ActionRequired = false;

/************************
 * The ever-present Setup() and loop functions
 * **************************************************************************************************************************************/

void setup() 
{
    pinMode(INPUT_PIN, INPUT_PULLUP);
    // I didn't know what to call this method, so I guessed
    // based on what it appears to do
    PlaySetupTones(); 
    delay(1000);
    /*****
     * This registers the `ACTION_REQUESTED_ISR` function as an interrupt
     * service routine which is triggered when the signal is raised by the 
     * INPUT_PIN. This means that we don't have to keep checking the state 
     * of INPUT_PIN,  but instead we just wait around until we are told that 
     * the signal has been raised.
     * 
     * By registering the ISR after the one second delay, we stop the user
     * from raising a signal for that second.
     * *****************************************************************/
    attachInterrupt(digitalPinToInterrupt(INPUT_PIN), ACTION_REQUESTED_ISR, RISING);
}

void loop() 
{
    bool action_required = false;
    PlaySt();

    __disable_irq();

    // check to see if the button was pressed
    if(ActionRequired)
    {
        ActionRequired = false;
        action_required = true;
    }

    __enable_irq();

    // If we have been notified that we need to take action
    if(action_required)
        AddSt();
}
/************************
 * **************************************************************************************************************************************/


/*****
 * This is our ISR, and we need to return from this routine as
 * quickly as possible, so we just signal the main loop() that
 * the action has been requested.
 * *****************************************************************/
void ACTION_REQUESTED_ISR() { ActionRequired = true; }

void PlaySetupTones()
{
    __tone( NA4 );
    __tone( NC5 );
    __tone( ND5 );
    __tone( NC4 );
    __tone( ND5 );
    __tone( ND5 );
    __tone( ND5 );
    __tone( NG5 );
    __tone( NF5 );
    __tone( NE5 );
    __tone( ND5 );
    __tone( NE5 );
    __tone( NE5 );
    __tone( NG5 );
    __tone( NA5 );
    __tone( ND5 );
    __tone( NC5 );
    __tone( NG5 );
    __tone( NG5 );
    __tone( NE5 );
    __tone( NG5 );
    __tone( NG5 );
    __tone( NA5 );
}

void __tone(uint32_t frequency, uint32_t duration, uint32_t delay_period, uint8_t pin)
{
    tone(pin, frequency, duration);
    delay(delay_period);
}

/*****
 * I'm not entirely sure what this is supposed to do, however what it does, 
 * is set the first item in the array to a Frequency/Duration pair with the 
 * values 3200/1 respectively.
 * 
 * This is the functionality no matter what value is passed in 
 * via the `type` parameter 
 * *****************************************************************/
void AddSt(int type)
{
    sT[0] = My_Pair{ .Frequency = 3200, .Duration = 1 };
}


void PlaySt()
{
    const int pair_size = sizeof(My_Pair);
    const int final_entry_index = ((sizeof sT)/pair_size) - 1;
    const int frequency = sT[0].Frequency;
    const int duration = sT[0].Duration;
    const int noteBreak = duration*1.3;

    if (duration != 0)
    {
        __tone(frequency, durSeg, (durSeg*1.3)); 
        if(duration < 2)
        {
            // This sub-routine will left shift everything in the array
            for (int index = 0; index < final_entry_index; index++)
            {
                memcpy(&sT[index], &sT[index+1], pair_size);
            }

            sT[final_entry_index] = My_Pair { .Frequency = 0, .Duration = 0 };
            delay(noteBreak);
        }
        else
        {
            sT[0].Duration--;
        }
    }
}

I’m not sure if this is doing what you want it to do though my friend. Based on this comment

I think you are trying to determine how many frequency/duration pairs are in your array, rather than how many the array can hold -which is what (sizeof sT)/(sizeof sT[0]) will give you; and we already know that is 48 because we declared it to be that size.

I’m also guessing that AddSt (I renamed it in my version) is supposed to add a new entry at the end of the array, rather than overwritting whatever is right at the start, which is what this code does,