Adalogger standby and wake-up now working

[ commit ]

This seems like quite a random fix, but calling rtc.standBy() only works from within setup() and loop(), and not from the RTC ISR. There is probably something happening behind the scenes, perhaps enabling/disabling of writes to protected registers. Its still weird that calling standBy() from the wrong routine just appears to stall the processor.

The revelation of the night was realizing that SAMD21 did successfully wake up after the first alarm, but it wasn’t going into loop() right after (indicated by missing LED flashes!) I was calling standBy() from the same alarmConfig() function: but it was first being called from setup(), and subsequently from within the RTC ISR. That’s why the first alarm always worked, but things messed up during its servicing (as soon as we hit standBy()).

Aside: delay() crashes inside the ISR too.

On the brightside (go Killers!), I am now more comfortable with writing SAM’s registers through the Arduino IDE. Still, using a debugger along with Atmel Studio would probably make development *much* faster. I now appreciate TI’s putting the XDS debuggers on all their eval boards. Well, Arduino Zero has that too, but Adalogger is too compact to allow that!

I just wish that re-enabling the Serial module again after wake-up was working. But since that’s not an end-user requirement, I’m going to put RTC on hold and get back to the SD. We could then come back to the RTC code so that the config. packet could be generated from a GUI.

Another interesting thing would be to store the alarm times in flash memory (SAMD21’s “EEPROM”) over just RAM – but I doubt that that would be useful unless I can guarantee that the RTC wouldn’t lose time during an accident. The idea is that if somehow the watchdog caused a reboot (is the watchdog setup anyway?), it should still be able to alarm – but we don’t want all the times to be skewed!

Hypothetically speaking, there could be a routine running every so often that writes the current time (and maybe even the next alarm’s index) to flash. Its just that this recorder would have to brave through weeks of work without human contact, and a little robustness might save us the frustration of looking at missing records on the SD later 🙂

Lower hanging fruit: RTC on Adalogger

Code: [ firmware ] [ host PC ]

Well, SD cards are somewhat involved. So I’ve just been working on the easier problem of setting up the RTC: easier because there’s a library for it and all I need to do is setup a connection with Processing so I can configure the real time clock module on SAMD21!

Just so you know, I just learned about [ UNIX time ] and was quite excited to use it because of this [ post ]:

In the end, the only thing that really works for all situations,
is using a single epoch and then converting to timezone offsets
as needed. The unix guys really did get it right way back then.

Also, I should mention that it seems easier to store numbers in the logger’s memory, than to deal with strings like “12:00 AM 05/24/16”. And we could do a re-sync whenever the recorder is programmed so there’s aren’t any significant offsets. Besides, knowing an absolute number, we can always figure out what local times may have been!

But then I decided to not use epoch since the registers themselves are mapped to date/time:


[ Source, pdf p. 294 ]

And converting the epoch to these register values is being done in software in the RTCZero library:

void RTCZero::setEpoch(uint32_t ts)
if (ts < EPOCH_TIME_OFF) {

time_t t = ts;
struct tm* tmp = gmtime(&t);

RTC->MODE2.CLOCK.bit.MONTH = tmp->tm_mon + 1;
RTC->MODE2.CLOCK.bit.DAY = tmp->tm_mday;
RTC->MODE2.CLOCK.bit.HOUR = tmp->tm_hour;
RTC->MODE2.CLOCK.bit.MINUTE = tmp->tm_min;
RTC->MODE2.CLOCK.bit.SECOND = tmp->tm_sec;

while (RTCisSyncing())

So regular date/time it is.

Although SAMD21 supports both an AM/PM and a 24 hour format, using an AM/PM format requires me to monitor an hour bit to determine the actual time (” HOUR[4] represents AM (0) or PM (1).” from the datasheet). Even though the end user may prefer AM/PM, writing out recording names by time would require using conditionals either way. So, I’ll just skip having to re-configure the RTC for AM/PM, and go with the 24 hour format for the RTC itself. We’ll bring back the AM/PM for filenames and such. And if RTC is to be reconfigured, its probably just one line to write CTRL.CLKREP anyway!

This week’s update is late…oh my!…and it consists of broken code! But here’s what I did:

  1. Processing code for sending current time and possible alarm times to SAMD21 from a host PC (over Serial)
    Status: mostly working. Checked by transmitting the received times back. Error-checking can be improved.
  2. Firmware for initializing the Real-Time Clock and Alarm0 using received information
    Status: just RTCZero library calls => working. Alarm0 information is transmitted back to double-check the registers.
  3. Firmware for going into sleep mode until Alarm0 hits.
    Status: working
  4. Firmware for configuring the next Alarm0 from information received in 1.
    Status: nope, fails.

Here’s a short video demoing an LED going off when the first alarm hits:

================== update ==================

It turns out that putting the processor to sleep is causing the problem! There was a comment in the library about it that I ignored!

void RTCZero::standbyMode()
// Entering standby mode when connected
// via the native USB port causes issues.

But then I found a multiple alarm example for the same library, which wasn’t included in the actual release (see this) but is forked here. Thanks agdl! And this implementation uses a linked list to hold alarm info – a much cleaner implementation of what I was trying to do.

Multiple alarms appear to work without going to sleep. Now its to see if they are still timed right!

Possible solutions for faster SPI -> SD writes on Adalogger (SAMD21)

As with any problem, one way out is to google the problem and see if the community has resolved it already. With writing SD cards, it appears that we have at least three lines of attack:

  • DMA
  • Clocking SPI faster
  • FIFOs (I don’t think we have these on SAMD21’s SPI)

Community wise, we have content on Arduino, Atmel, and ARM forums:

[ SdFat with DMA SPI ] which has been successful on Arduino Due. Since SAMD21’s SPI interface supports DMA as well [ Sec. 27 | pdf p.501 ], a similar solution could be developed.

SdFat’s documentation is [here].
[ ZeroDMA ] could be used to setup the DMA controller on SAMD21

[ SPI max speed ] along with using [ FatFs ]

[ GPIO ] would limit SPI to 12 MHz, but I’m not sure where the GPIO module is being used here.

[ Quickstart guide ] for setting up DMA is referenced here as well.

Now to dig into the libraries and see how SD card writes are configured!


[ echo-II ] Sparkfun’s SD library: timing writes

Today’s problem was simple. What’s the fastest rate at which we can write an SD card using the Adafruit Adalogger (based on Atmel SAMD21) and the standard SD library in the Arduino IDE? The answer that I have this far appears to be one write every ~70 micros (at best), but it gets weird.

Why does this matter? I want to write two 32-bit numbers (per stereo sample) to an SD card at 96 kHz. Coding this will be a breeze if I can just patch together some examples. I tried that today, and although things could work reliably at say 1-2 kHz (would change based on load on the processor), they definitely wouldn’t work at 96 kHz unless we tweak the library to exploit the SAMD21 a bit more. DMA would help.

But how good / bad can things be if we to just stitch together some calls to library functions? Well, good enough for a non-audio related project, but not when we need high quality audio recording (atleast not without re-configuring some things).

I ran a very simple test: I just wrote out the time elapsed on the microcontroller (in microseconds), and made plots. I think this is the best the SD library can do given that the processor is not doing anything else in the void loop (code is appended). Doing other things will probably slow the writes down more (unless we go for DMA, which we should!)

Check these timing plots out:


On the top, I am showing you the total time elapsed as we keep writing micros()’s output to the SD card (note: micros() is used to time code execution in microseconds…just like millis() does it in milliseconds). Note the slope of the blue line – that’s the time period between consecutive samples – is *much* larger than what we need (see the red line for the expected slope for sampling @96 kHz). That is, writes to the SD card are too slow if we just use println() from the SD library in the void loop().

The second plot emphasizes another detail that’s easy to miss on the first plot, that is, how consistent is the timing of these writes anyway? Looks like most of the writes happen at intervals of ~70 micros, but there’s some rather consistent weirdness going on every 57th sample (no cursors shown), and then again every ~910th sample (cursors shown). For these “outliers”, the time elapsed between consecutive writes goes up from ~70 micros to about ~4200 micros in the middle of the plot, and ~8000 for the weirdest outliers towards the top. My first bet is that these have something to do with the actual data dump to the SD card, but that needs to be verified.

Summary this far: the simplest approach of stitching library examples together has failed. We need to go in and configure some registers to make things work faster, and in parallel.

Now, for the code, see [ github ].

Here’s the arduino sketch for quick reference:


// Project Echo | Timing writes to SD card using Sparkfun's SD library
// The goal here is to print the output of micros() to an SD card as quickly as possible
//  while using standard calls to the SD library.

// This sketch is adapted from:
//   and Examples/SD/DumpFile

// Hardware: Adfruit Adalogger running Atmel SAMD21 ARM M0+ @48MHz

#include <SPI.h>
#include <SD.h>

// Set the pins used
#define cardSelect 4
#define redLED 13 // used for indicating button presses
#define grnLED 8 // lights up when logging
char filename[9]; // could reduce size...
File logfile;

// Some dummy data for more timing comparisons
// Replace with 32-bit left/right samples obtained from codec
float placeHolder1 = 1.0202020;
float placeHolder2 = 1.0202020;

void setup() {
  pinMode(redLED, INPUT);
  attachInterrupt(redLED, switchISR, FALLING); // for stopping logging.
  // Could use other interrupts; I happened to have a pushbutton already there.
  pinMode(grnLED, OUTPUT);

  while (!Serial) {} // wait until serial is available. Not neccessary?
  Serial.println("Welcome to the SD echo test!");

  // see if the card is present and can be initialized:
  if (!SD.begin(cardSelect)) {
    Serial.println("Card init. failed!");

  strcpy(filename, "LOG00.CSV");
  for (uint8_t i = 0; i < 100; i++) {
    filename[3] = '0' + i / 10;
    filename[4] = '0' + i % 10;
    // create if does not exist, do not open existing, write, sync after write
    if (! SD.exists(filename)) break;

  logfile =, FILE_WRITE);
  if ( ! logfile ) {
    Serial.print("Couldn't create ");
  Serial.print("Writing to "); Serial.println(filename);
  Serial.println(F("\nSend any character to begin")); // from MPU-6050 teapot demo!
  while (Serial.available() &&; // empty buffer
  while (!Serial.available());                 // wait for data
  while (Serial.available() &&; // empty buffer again
  digitalWrite(grnLED, HIGH);
  Serial.println("Push button to stop logging!");

void loop() {
  //logfile.print(micros()); //logfile.print("\t");
  //logfile.println(placeHolder1);// logfile.print("\t");
  //placeHolder1 += 1.0;
  //placeHolder2 *= 2.0;
  //placeHolder = analogRead(0); // apparently costs ~400 micros -> can be reduced
  //placeHolder = analogRead(1);
  //delayMicroseconds(75); // adds up

void fileDump() { // print contents of file for copy/paste/plotting
  Serial.println("Reading back what we wrote...");
  File dataFile =;

  if (dataFile) { // read from the file if its available
    while (dataFile.available()) {
    Serial.println("FIN"); // we did it!
  } else { // else: error!
    Serial.println("error opening datalog");

void switchISR() {
  logfile.flush(); logfile.close();
  digitalWrite(grnLED, LOW); // not logging anymore
  exit(0); // z Z z

Echo Part II – this one will actually work :D

[ spec ] [ block diagram ] [ github ]

Echo is back! We’re now calling it echo-II, and the goal of this instance is straightforward: make a sound recorder capable of supporting 96 kHz | 32-bit | stereo | PCM recordings over pre-allocated time slots for about 3 weeks, and do all this on a battery (without human intervention).

That’s a mouthful, so the spec is linked at the top. And we all love block diagrams, so here’s one!

That’s for the introduction. Some code coming in on the next one!