cleanup + use flash storage + add delay option
This commit is contained in:
parent
382d659d4d
commit
fc16966e55
97
spybug.ino
97
spybug.ino
@ -34,17 +34,29 @@ static void setup_serial_in_out() {
|
|||||||
stdout = stdin = stderr = &serial_in_out;
|
stdout = stdin = stderr = &serial_in_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define DEBUG_RECORDING
|
static size_t fstrlen(const __FlashStringHelper *s) {
|
||||||
|
PGM_P sp = (PGM_P)s;
|
||||||
|
size_t len = 0;
|
||||||
|
while (pgm_read_byte(sp++))
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
#define SAMPLE_MODE_U8
|
static int printf(const __FlashStringHelper *fmt, ...) {
|
||||||
//#define SAMPLE_MODE_S16
|
size_t len = fstrlen(fmt);
|
||||||
|
char buf[len + 1];
|
||||||
|
buf[len] = 0;
|
||||||
|
memcpy_P(buf, fmt, len + 1);
|
||||||
|
|
||||||
//#define ADC_PRESCALE_16 /* Up to ~60kHz. */
|
va_list args;
|
||||||
//#define ADC_PRESCALE_32 /* Up to ~27kHz. */
|
va_start(args, fmt);
|
||||||
#define ADC_PRESCALE_64 /* Up to ~18kHz. */
|
int ret = vprintf(buf, args);
|
||||||
|
va_end(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#define die(fmt, ...) { cli(); printf("Fatal: " fmt, ##__VA_ARGS__); Serial.flush(); while(1); }
|
#define die(fmt, ...) { cli(); printf(F("Fatal: ")); printf(fmt, ##__VA_ARGS__); Serial.flush(); while(1); }
|
||||||
#define dbg(fmt, ...) printf("Debug: " fmt, ##__VA_ARGS__);
|
#define dbg(fmt, ...) { printf(F("Debug: ")); printf(fmt, ##__VA_ARGS__); }
|
||||||
|
|
||||||
enum AdcChannel : uint8_t {
|
enum AdcChannel : uint8_t {
|
||||||
AdcChannel0 = 0,
|
AdcChannel0 = 0,
|
||||||
@ -60,18 +72,27 @@ enum AdcChannel : uint8_t {
|
|||||||
AdcChannelGnd = 15,
|
AdcChannelGnd = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
const AdcChannel adc_chan = AdcChannel0;
|
//#define DEBUG_RECORDING
|
||||||
const uint16_t timer_cmp = 1000; /* 16MHz / 1000 = 16kHz. */
|
|
||||||
//const uint16_t timer_cmp = 800; /* 16MHz / 800 = 20kHz. */
|
//#define SAMPLE_MODE_U8
|
||||||
const unsigned long int flush_samples = 64000;
|
#define SAMPLE_MODE_S16
|
||||||
const int pin_ss = 10;
|
|
||||||
|
//#define ADC_PRESCALE_16 /* Up to ~60kHz. */
|
||||||
|
//#define ADC_PRESCALE_32 /* Up to ~27kHz. */
|
||||||
|
#define ADC_PRESCALE_64 /* Up to ~18kHz. */
|
||||||
|
|
||||||
|
#define RECORDING_DELAY_IN_MINUTES 0 /* Wait n minutes before starting to record. */
|
||||||
|
#define ADC_CHANNEL AdcChannel0
|
||||||
|
#define TIMER_COMPARE 1000 /* 16MHz / 1000 = 16kHz. */
|
||||||
|
#define FLUSH_SAMPLES 64000 /* Flush WAV file every n samples. */
|
||||||
|
#define PIN_SS 10
|
||||||
|
|
||||||
File file;
|
File file;
|
||||||
#if defined(SAMPLE_MODE_U8)
|
#if defined(SAMPLE_MODE_U8)
|
||||||
#define SAMPLE_BUF_SIZE 128
|
#define SAMPLE_BUF_SIZE 256
|
||||||
#define SAMPLE_BUF_TYPE uint8_t
|
#define SAMPLE_BUF_TYPE uint8_t
|
||||||
#elif defined(SAMPLE_MODE_S16)
|
#elif defined(SAMPLE_MODE_S16)
|
||||||
#define SAMPLE_BUF_SIZE 64
|
#define SAMPLE_BUF_SIZE 138
|
||||||
#define SAMPLE_BUF_TYPE int16_t
|
#define SAMPLE_BUF_TYPE int16_t
|
||||||
#endif
|
#endif
|
||||||
volatile SAMPLE_BUF_TYPE sample_buffer[2][SAMPLE_BUF_SIZE];
|
volatile SAMPLE_BUF_TYPE sample_buffer[2][SAMPLE_BUF_SIZE];
|
||||||
@ -94,11 +115,15 @@ ISR(TIMER1_COMPA_vect) {
|
|||||||
TIMSK1 &= ~_BV(OCIE1A);
|
TIMSK1 &= ~_BV(OCIE1A);
|
||||||
sei();
|
sei();
|
||||||
const size_t bufsz = sizeof(SAMPLE_BUF_TYPE) * samples_in_buffer[!which_buffer];
|
const size_t bufsz = sizeof(SAMPLE_BUF_TYPE) * samples_in_buffer[!which_buffer];
|
||||||
if (file.write((char*)sample_buffer[!which_buffer], bufsz) != bufsz)
|
if (file.write((char*)sample_buffer[!which_buffer], bufsz) != bufsz) {
|
||||||
die("Error writing to SD card!\n");
|
printf(F("Lost "));
|
||||||
|
Serial.print((float)samples_hanging / (float)(F_CPU / TIMER_COMPARE)); /* Printf doesn't handle floats. */
|
||||||
|
printf(F(" seconds of recording.\n"));
|
||||||
|
die(F("Error writing to SD card. You can ignore this if you removed the SD card intentionally.\n"));
|
||||||
|
}
|
||||||
samples_hanging += samples_in_buffer[!which_buffer];
|
samples_hanging += samples_in_buffer[!which_buffer];
|
||||||
samples_in_buffer[!which_buffer] = 0;
|
samples_in_buffer[!which_buffer] = 0;
|
||||||
if (samples_hanging >= flush_samples) {
|
if (samples_hanging >= FLUSH_SAMPLES) {
|
||||||
samples_written += samples_hanging;
|
samples_written += samples_hanging;
|
||||||
samples_hanging = 0;
|
samples_hanging = 0;
|
||||||
wav_write_header(samples_written);
|
wav_write_header(samples_written);
|
||||||
@ -136,17 +161,17 @@ ISR(TIMER1_COMPB_vect) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void wav_write_header(uint32_t nsamples) {
|
static void wav_write_header(uint32_t nsamples) {
|
||||||
unsigned long old_pos = file.position();
|
unsigned long old_pos = file.position();
|
||||||
|
|
||||||
if (!file.seek(0))
|
if (!file.seek(0))
|
||||||
die("Error seeking to position 0!\n");
|
die(F("Error seeking to position 0!\n"));
|
||||||
|
|
||||||
const uint16_t channels = 1;
|
const uint16_t channels = 1;
|
||||||
const uint32_t riff_chunk_size = sizeof(SAMPLE_BUF_TYPE) * nsamples * channels + 4 + 24 + 8;
|
const uint32_t riff_chunk_size = sizeof(SAMPLE_BUF_TYPE) * nsamples * channels + 4 + 24 + 8;
|
||||||
const uint32_t fmt_chunk_size = 16;
|
const uint32_t fmt_chunk_size = 16;
|
||||||
const uint16_t fmt_tag = 1; /* 1 = PCM. */
|
const uint16_t fmt_tag = 1; /* 1 = PCM. */
|
||||||
const uint32_t sample_rate = F_CPU / timer_cmp;
|
const uint32_t sample_rate = F_CPU / TIMER_COMPARE;
|
||||||
const uint32_t data_rate = sizeof(SAMPLE_BUF_TYPE) * channels * sample_rate;
|
const uint32_t data_rate = sizeof(SAMPLE_BUF_TYPE) * channels * sample_rate;
|
||||||
const uint16_t block_align = sizeof(SAMPLE_BUF_TYPE) * channels;
|
const uint16_t block_align = sizeof(SAMPLE_BUF_TYPE) * channels;
|
||||||
const uint16_t bits_per_sample = sizeof(SAMPLE_BUF_TYPE) * 8;
|
const uint16_t bits_per_sample = sizeof(SAMPLE_BUF_TYPE) * 8;
|
||||||
@ -168,11 +193,11 @@ void wav_write_header(uint32_t nsamples) {
|
|||||||
// data
|
// data
|
||||||
|| file.write((char*)"data", 4) != 4
|
|| file.write((char*)"data", 4) != 4
|
||||||
|| file.write((char*)&data_size, 4) != 4)
|
|| file.write((char*)&data_size, 4) != 4)
|
||||||
die("Error writing WAV header to SD card!\n");
|
die(F("Error writing WAV header to SD card!\n"));
|
||||||
|
|
||||||
if (old_pos > file.position()) {
|
if (old_pos > file.position()) {
|
||||||
if(!file.seek(old_pos))
|
if(!file.seek(old_pos))
|
||||||
die("Error seeking to position %lu!\n", old_pos);
|
die(F("Error seeking to position %lu!\n"), old_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +206,8 @@ void setup() {
|
|||||||
Serial.begin(9600); /* Set baud rate. */
|
Serial.begin(9600); /* Set baud rate. */
|
||||||
setup_serial_in_out(); /* Add printf support. */
|
setup_serial_in_out(); /* Add printf support. */
|
||||||
// SD Card Setup
|
// SD Card Setup
|
||||||
if (!SD.begin(pin_ss))
|
if (!SD.begin(PIN_SS))
|
||||||
die("Error initializing SD card!\n");
|
die(F("Error initializing SD card!\n"));
|
||||||
// Determine Filename
|
// Determine Filename
|
||||||
unsigned int filenum = 0;
|
unsigned int filenum = 0;
|
||||||
char filename[32];
|
char filename[32];
|
||||||
@ -190,14 +215,20 @@ void setup() {
|
|||||||
filenum++;
|
filenum++;
|
||||||
snprintf(filename, 32, "rec_%03u.wav", filenum);
|
snprintf(filename, 32, "rec_%03u.wav", filenum);
|
||||||
} while (SD.exists(filename));
|
} while (SD.exists(filename));
|
||||||
|
// Delay Triggering
|
||||||
|
#if defined(RECORDING_DELAY_IN_MINUTES) && RECORDING_DELAY_IN_MINUTES != 0
|
||||||
|
printf(F("Filename: '%s'.\n"), filename);
|
||||||
|
printf(F("Waiting %u minute%s before starting to record...\n"), RECORDING_DELAY_IN_MINUTES, RECORDING_DELAY_IN_MINUTES == 1 ? "" : "s");
|
||||||
|
delay(60000ul * (unsigned long)(RECORDING_DELAY_IN_MINUTES));
|
||||||
|
#endif
|
||||||
// Open File
|
// Open File
|
||||||
file = SD.open(filename, O_READ | O_WRITE | O_CREAT); /* Seeking doesn't seem to work with FILE_WRITE?! */
|
file = SD.open(filename, O_READ | O_WRITE | O_CREAT); /* Seeking doesn't seem to work with FILE_WRITE?! */
|
||||||
printf("Recording to file '%s'.\n", filename);
|
printf(F("Recording to file '%s'.\n"), filename);
|
||||||
if (!file)
|
if (!file)
|
||||||
die("Error opening '%s' for writing!\n", filename);
|
die(F("Error opening '%s' for writing!\n"), filename);
|
||||||
wav_write_header(0);
|
wav_write_header(0);
|
||||||
// ADC Setup
|
// ADC Setup
|
||||||
DIDR0 |= (0xF & adc_chan); /* Disable digital input. */
|
DIDR0 |= (0xF & ADC_CHANNEL); /* Disable digital input. */
|
||||||
ADCSRA = _BV(ADEN) /* Enable ADC. */
|
ADCSRA = _BV(ADEN) /* Enable ADC. */
|
||||||
| _BV(ADATE) /* Enable auto-trigger. */
|
| _BV(ADATE) /* Enable auto-trigger. */
|
||||||
#if defined(ADC_PRESCALE_64) /* Up to ~18kHz. */
|
#if defined(ADC_PRESCALE_64) /* Up to ~18kHz. */
|
||||||
@ -212,24 +243,24 @@ void setup() {
|
|||||||
#if defined(SAMPLE_MODE_U8)
|
#if defined(SAMPLE_MODE_U8)
|
||||||
| _BV(ADLAR) /* Left adjust ADC output so we only need to read ADCH. */
|
| _BV(ADLAR) /* Left adjust ADC output so we only need to read ADCH. */
|
||||||
#endif
|
#endif
|
||||||
| (0xF & adc_chan); /* Select our ADC input channel. */
|
| (0xF & ADC_CHANNEL); /* Select our ADC input channel. */
|
||||||
// Timer Setup
|
// Timer Setup
|
||||||
TCCR1A = _BV(WGM13) | _BV(WGM12) | _BV(WGM11); /* Set timer 1 on A channel to ICR1 fast PWM. (Required to make channel B fire at the correct speed). */
|
TCCR1A = _BV(WGM13) | _BV(WGM12) | _BV(WGM11); /* Set timer 1 on A channel to ICR1 fast PWM. (Required to make channel B fire at the correct speed). */
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12) /* Make timer 1 on B channel compare to ICR1 in CTC (Clear Timer on Compare match) mode. */
|
TCCR1B = _BV(WGM13) | _BV(WGM12) /* Make timer 1 on B channel compare to ICR1 in CTC (Clear Timer on Compare match) mode. */
|
||||||
| _BV(CS10); /* Set timer prescaler division factor to 1. */
|
| _BV(CS10); /* Set timer prescaler division factor to 1. */
|
||||||
ICR1 = timer_cmp; /* Set timer compare value: freqency = CPU frequency (16MHz) / timer_cmp. */
|
ICR1 = TIMER_COMPARE; /* Set timer compare value: freqency = CPU frequency (16MHz) / TIMER_COMPARE. */
|
||||||
TIMSK1 = _BV(OCIE1A)
|
TIMSK1 = _BV(OCIE1A) /* Use interrupt A for updating the data on the SD card. */
|
||||||
| _BV(OCIE1B); /* Enable "Output Compare B Match Interrupt". */
|
| _BV(OCIE1B); /* Enable "Output Compare B Match Interrupt". */
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
delay(1000);
|
delay(1000);
|
||||||
#ifdef DEBUG_RECORDING
|
#ifdef DEBUG_RECORDING
|
||||||
dbg("n=%lu\tavg=%lu\tmin=%d\tmax=%d\n", dbg_total, dbg_sum / (dbg_samples ? dbg_samples : 1), dbg_min, dbg_max);
|
dbg(F("n=%lu\tavg=%lu\tmin=%d\tmax=%d\n"), dbg_total, dbg_sum / (dbg_samples ? dbg_samples : 1), dbg_min, dbg_max);
|
||||||
dbg_sum = 0;
|
dbg_sum = 0;
|
||||||
dbg_samples = 0;
|
dbg_samples = 0;
|
||||||
dbg_min = 32767;
|
dbg_min = 32767;
|
||||||
dbg_max = -32768;
|
dbg_max = -32768;
|
||||||
#endif
|
#endif
|
||||||
printf("samples: written=%lu, hanging=%lu, dropped=%lu\n", samples_written, samples_hanging, samples_dropped);
|
printf(F("samples: written=%lu, hanging=%lu, dropped=%lu\n"), samples_written, samples_hanging, samples_dropped);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user