Breakpoints and stepping through code: This is a great way to examine the state of your application, monitor variables, and single step through complicated functions. They are super useful for all embedded applications, but they have some pretty big drawbacks. First, the entire program halts when a breakpoint is hit. That means that if your code is time-sensitive, you will not be able to debug your code during the most important parts. For instance, if you want to debug code that is responsible for receiving some kind of signal, doing some processing, and then transmitting back in a fixed amount of time, you can use breakpoints to examine the state of the firmware, but the overall application will fail due to the unexpected delay or failure to receive a response. Breakpoints also can only be set on firmware instructions. You can't set a breakpoint for when an i/o pin changes state, and a breakpoint (or internal variable checking) won't tell you why SPI data isn't getting written to the bus when you call the write command.