Compare commits

...

21 Commits

Author SHA1 Message Date
r4
ea93980795 fix message 2022-11-27 02:07:03 +01:00
r4
dac3a33e9e fix typo 2022-11-27 02:06:43 +01:00
r4
618c75b72b - error message 2022-04-19 19:45:13 +02:00
r4
6f962c0d88 fmt 2022-04-19 19:37:38 +02:00
r4
9ecb2f5579 basic FS commands 2022-04-19 19:30:15 +02:00
r4
5ed8e8f226 add copyright notice 2022-04-19 16:48:43 +02:00
r4
9420eb8293 add reset sketch and reorganize files 2022-04-19 16:44:54 +02:00
r4
4e90b217cb update readme 2022-04-18 23:48:23 +02:00
r4
bbb53a31b3 update readme 2022-04-18 23:47:15 +02:00
r4
1f17f87822 save more power (actually 6μA this time) 2022-04-18 23:15:19 +02:00
r4
25d7967d69 error on incompatible hardware 2022-04-18 23:04:53 +02:00
r4
6599bf151d consistency 2022-04-18 23:02:12 +02:00
r4
b6004c42e0 update readme 2022-04-18 20:42:52 +02:00
r4
7773dc7c1f fix fmt 2022-04-18 19:45:21 +02:00
r4
5728233bad add exit command 2022-04-18 19:33:41 +02:00
r4
580ba5e577 reset wait time after waiting 2022-04-18 19:32:11 +02:00
r4
232df37f5c fix watchdog + library independence 2022-04-18 19:31:51 +02:00
r4
3101c3add6 add commands and improve documentation 2022-04-18 17:47:36 +02:00
r4
fe6734dcba rename U8 amplify option and comment out by default 2022-04-17 10:47:48 +02:00
r4
bfa80a629e component switch support to save power when waiting 2022-04-14 20:48:27 +02:00
r4
c2f41e41c7 add license 2022-04-11 19:42:06 +02:00
16 changed files with 476 additions and 115 deletions

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -2,7 +2,7 @@
A simple voice recorder for Arduino using an SD card and an electret microphone amplifier circuit.
Independent of any 3rd party libraries (unless delayed recording is used).
Independent of any third-party libraries.
## Wiring (also documented in spybug/spybug.ino)
### SD Card Wiring
@@ -29,3 +29,9 @@ GND | GND
Out | A0
Out defaults to A0 (AdcChannel0), but can be set manually in `ADC_CHANNEL`.
## Power Consumption (Arduino Nano)
Mode | Unmodified | No voltage regulator | No TTL module or voltage regulator
----------|------------|----------------------|-----------------------------------
Waiting | ~10mA | ~5.8mA | ~6μA
Recording | ~30mA | ~25.1mA | ~25mA

View File

@@ -0,0 +1,14 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "settings.hh"
void setup() {
Serial.begin(9600);
Serial.print("Resetting EEPROM...");
EEPROM_Settings_Class settings;
settings.save();
Serial.println("done.");
}
void loop() {}

1
reset_sketch/settings.hh Symbolic link
View File

@@ -0,0 +1 @@
../spybug/settings.hh

31
spybug/aaa_config.hh Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
/************************
BEGIN USER CONFIGURATION
************************/
//#define DEBUG_RECORDING
#define PIN_COMPONENT_SWITCH 2 /* Use a digital signal to switch on/off the microphone and SD card for less power draw. */
#define COMPONENT_SWITCH_ON HIGH
#define SAMPLE_MODE_U8
//#define SAMPLE_MODE_S16
//#define ADC_PRESCALE_16 /* Up to ~60kHz. */
//#define ADC_PRESCALE_32 /* Up to ~27kHz. */
#define ADC_PRESCALE_64 /* Up to ~18kHz. */
//#define U8_AMPLIFY_X2 /* (U8 sampling mode only) amplify audio by factor 2. */
#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
#define REC_FILE_FMT "REC_%03u.WAV" /* Must be all caps; must use %u in some form exactly once. */
/**********************
END USER CONFIGURATION
**********************/

191
spybug/cmd.cpp Normal file
View File

@@ -0,0 +1,191 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "cmd.hh"
#include <Arduino.h>
#include <SD.h>
#include "aaa_config.hh"
#include "fstr.hh"
#include "io.hh"
#include "settings.hh"
#include "sys.hh"
static bool sd_initialized = false;
static void try_init_sd() {
if (sd_initialized)
return;
printf(F("Initializing SD card..."));
#ifdef PIN_COMPONENT_SWITCH
digitalWrite(PIN_COMPONENT_SWITCH, COMPONENT_SWITCH_ON);
delay(500); /* Wait for components to initialize. */
#endif
if (SD.begin(PIN_SS)) {
printf(F("Done.\n"));
sd_initialized = true;
} else
printf(F("\nError initializing SD card.\n"));
}
static void sd_error() {
printf(F("Error reading from SD card.\n"));
sd_initialized = false;
}
static void command_loop() {
// Process Commands
if (Serial.available()) {
char args[6][20];
size_t len = 0;
size_t n_args = 0;
int c = getchar();
while (c != '\n' && n_args < 4) {
if (c == ' ')
c = getchar();
else {
do {
args[n_args][len++] = c;
c = getchar();
} while (c != ' ' && c != '\n' && len < 20-1);
args[n_args][len] = 0;
len = 0;
n_args++;
}
}
if (n_args >= 1) {
if (fstreq(args[0], F("set"))) {
if ((n_args == 3 || n_args == 4) && fstreq(args[1], F("wait"))) {
float n = atof(args[2]);
unsigned long mins;
if (n_args == 4 && (fstreq(args[3], F("hours")) || fstreq(args[3], F("hour"))))
mins = 60.f * n;
else
mins = n;
settings.recording_delay = mins;
settings.save();
printf(F("Set waiting time to %lu minutes or "), settings.recording_delay);
print_special((float)settings.recording_delay / 60.f);
printf(F(" hours.\n"));
} else if (n_args == 3 && fstreq(args[1], F("serial"))) {
if (fstreq(args[2], F("on"))) {
settings.serial_log = true;
settings.save();
printf(F("Serial log enabled.\n"));
} else if (fstreq(args[2], F("off"))) {
settings.serial_log = false;
settings.save();
printf(F("Serial log disabled.\n"));
} else {
printf(F("Usage: 'set serial [on|off]'.\n"));
}
} else {
printf(F("Usage: 'set wait <number> [minutes|hours]' or 'set serial [on|off]'.\n"));
}
} else if (fstreq(args[0], F("get"))) {
if (n_args == 2 && fstreq(args[1], F("wait"))) {
printf(F("Current waiting time: %lu minutes or "), settings.recording_delay);
print_special((float)settings.recording_delay / 60.f);
printf(F(" hours.\n"));
} else {
printf(F("Usage: 'get wait'.\n"));
}
} else if (fstreq(args[0], F("sd"))) {
try_init_sd();
if (sd_initialized) {
if (n_args == 2 && fstreq(args[1], F("list"))) {
File root = SD.open("/");
if (root) {
printf(F("Files:\n"));
bool files = false;
size_t cols = 0;
while (1) {
File entry = root.openNextFile();
if (!entry)
break;
files = true;
if (cols >= 3) {
cols = 0;
printf(F("\n"));
}
printf(F(" %s"), entry.name());
cols++;
entry.close();
}
if (!files)
printf(F("[NONE]"));
printf(F("\n"));
} else
sd_error();
root.close();
} else if (n_args == 3 && fstreq(args[1], F("remove"))) {
if (SD.remove(args[2]))
printf(F("Deleted '%s'.\n"), args[2]);
else
printf(F("Error removing '%s'.\n"), args[2]);
} else if (n_args == 2 && fstreq(args[1], F("remove_all"))) {
File root = SD.open("/");
if (root) {
printf(F("Deleted:\n"));
bool deleted = false;
size_t cols = 0;
while (1) {
File entry = root.openNextFile();
if (!entry)
break;
const char *name = entry.name();
unsigned int garbage;
if (!entry.isDirectory() && sscanf(name, REC_FILE_FMT, &garbage) == 1) {
if (cols >= 3) {
cols = 0;
printf(F("\n"));
}
deleted = true;
SD.remove(name);
printf(F(" %s"), name);
cols++;
}
entry.close();
}
if (!deleted)
printf(F("[NONE]"));
printf(F("\n"));
} else
sd_error();
root.close();
} else if (n_args != 1) {
printf(F("Usage: 'sd list', 'sd remove <filename>' or 'sd remove_all'.\n"));
}
}
} else if (fstreq(args[0], F("help"))) {
printf(F("Commands:\n"));
printf(F(" help -- Display this page.\n"));
printf(F(" exit -- Leave command mode.\n"));
printf(F(" get wait -- Display current delay setting.\n"));
printf(F(" set wait <number> [minutes|hours] -- Change current delay setting.\n"));
printf(F(" set serial [on|off] -- Write log to serial output.\n"));
printf(F(" sd -- Initialize the SD card.\n"));
printf(F(" sd list -- List file on SD card.\n"));
printf(F(" sd remove <filename> -- Delete a file from SD card.\n"));
printf(F(" sd remove_all -- Delete all recordings from SD card.\n"));
} else if (fstreq(args[0], F("exit"))) {
printf(F("Bye!\n"));
Serial.flush();
full_reset();
} else
printf(F("Invalid command: '%s'. Type 'help' for a list of commands.\n"), args[0]);
} else {
printf(F("Please specify a command. Type 'help' for a list of commands.\n"));
}
}
}
void cmd() {
printf(F("You are now in command mode. Type 'help' for a list of commands.\n"));
while (Serial.available()) Serial.read();
while (1) {
command_loop();
delay(50);
}
}

6
spybug/cmd.hh Normal file
View File

@@ -0,0 +1,6 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
void cmd();

37
spybug/fstr.cpp Normal file
View File

@@ -0,0 +1,37 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "fstr.hh"
size_t fstrlen(const __FlashStringHelper *s) {
PGM_P sp = (PGM_P)s;
size_t len = 0;
while (pgm_read_byte(sp++))
len++;
return len;
}
bool fstreq(const char *a, const __FlashStringHelper *b_fsh) {
PGM_P b = (PGM_P)b_fsh;
while (1) {
if (*a != pgm_read_byte(b))
return false;
if (*a == 0)
return true;
a++; b++;
}
}
int printf(const __FlashStringHelper *fmt, ...) {
size_t len = fstrlen(fmt);
char buf[len + 1];
buf[len] = 0;
memcpy_P(buf, fmt, len + 1);
va_list args;
va_start(args, fmt);
int ret = vprintf(buf, args);
va_end(args);
return ret;
}

12
spybug/fstr.hh Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
#include <Arduino.h>
size_t fstrlen(const __FlashStringHelper *s);
bool fstreq(const char *a, const __FlashStringHelper *b_fsh);
int printf(const __FlashStringHelper *fmt, ...);

22
spybug/io.cpp Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "io.hh"
static int serial_putch(char c, FILE *f) {
(void)f;
return Serial.write(c) == 1 ? 0 : 1;
}
static int serial_getch(FILE *f) {
(void)f;
while(Serial.available() == 0);
return Serial.read();
}
static FILE serial_in_out;
void io_setup() {
fdev_setup_stream(&serial_in_out, serial_putch, serial_getch, _FDEV_SETUP_RW);
stdout = stdin = stderr = &serial_in_out;
}

14
spybug/io.hh Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
#include <Arduino.h>
void io_setup();
#define print_special(x) { Serial.print(x); }
#define die(fmt, ...) { disable_recording_interrupts(); if (settings.serial_log) { printf(F("Fatal: ")); printf(fmt, ##__VA_ARGS__); Serial.flush(); } while(1); }
#define dbg(fmt, ...) { printf(F("Debug: ")); printf(fmt, ##__VA_ARGS__); }
#define info(fmt, ...) { if (settings.serial_log) printf(fmt, ##__VA_ARGS__); }
#define info_special(x) { if (settings.serial_log) Serial.print(x); }

6
spybug/settings.cpp Normal file
View File

@@ -0,0 +1,6 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "settings.hh"
EEPROM_Settings_Class settings;

17
spybug/settings.hh Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
#include <EEPROM.h>
#define EEADDR_SETTINGS 0x00
struct EEPROM_Settings_Class {
unsigned long recording_delay = 0l;
bool serial_log = true;
inline void load() { EEPROM.get(EEADDR_SETTINGS, *this); }
inline void save() { EEPROM.put(EEADDR_SETTINGS, *this); }
};
extern EEPROM_Settings_Class settings;

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
/*
*** SD Card Wiring ***
SD | Nano
@@ -27,107 +30,18 @@
#include <SD.h>
#include <SPI.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/wdt.h>
/************************
BEGIN USER CONFIGURATION
************************/
//#define DEBUG_RECORDING
//#define SERIAL_OUTPUT
#include "aaa_config.hh"
#include "cmd.hh"
#include "fstr.hh"
#include "io.hh"
#include "settings.hh"
#include "sys.hh"
#define SAMPLE_MODE_U8
//#define SAMPLE_MODE_S16
//#define ADC_PRESCALE_16 /* Up to ~60kHz. */
//#define ADC_PRESCALE_32 /* Up to ~27kHz. */
#define ADC_PRESCALE_64 /* Up to ~18kHz. */
#define U8_EXTRA_PRECISION /* (U8 sampling mode only) use 9th ADC reading bit and chop off 1st bit for more precision (sacrificing half of the bandwidth) */
#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
/**********************
END USER CONFIGURATION
**********************/
#ifdef SERIAL_OUTPUT
static int serial_putch(char c, FILE *f) {
(void)f;
return Serial.write(c) == 1 ? 0 : 1;
}
static int serial_getch(FILE *f) {
(void)f;
while(Serial.available() == 0);
return Serial.read();
}
static FILE serial_in_out;
static void setup_serial_in_out() {
fdev_setup_stream(&serial_in_out, serial_putch, serial_getch, _FDEV_SETUP_RW);
stdout = stdin = stderr = &serial_in_out;
}
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;
}
static int printf(const __FlashStringHelper *fmt, ...) {
size_t len = fstrlen(fmt);
char buf[len + 1];
buf[len] = 0;
memcpy_P(buf, fmt, len + 1);
va_list args;
va_start(args, fmt);
int ret = vprintf(buf, args);
va_end(args);
return ret;
}
#define die(fmt, ...) { disable_recording_interrupts(); printf(F("Fatal: ")); printf(fmt, ##__VA_ARGS__); Serial.flush(); while(1); }
#define dbg(fmt, ...) { printf(F("Debug: ")); printf(fmt, ##__VA_ARGS__); }
#define print_special(x) Serial.print(x)
#else
#define printf(fmt, ...) {}
#define die(fmt, ...) { disable_recording_interrupts(); while(1); }
#define dbg(fmt, ...) {}
#define print_special(x) {}
#if !defined(__AVR_ATmega328P__) || F_CPU != 16000000
#error "This program only works on ATmega328P devices with a clock frequency of 16MHz!"
#endif
#if defined(RECORDING_DELAY_IN_MINUTES) && RECORDING_DELAY_IN_MINUTES != 0
#include <LowPower.h> /* https://github.com/rocketscream/Low-Power */
static void low_power_sleep_minutes(unsigned long t) {
for (unsigned long i = 0; 8ul * i < 60ul * t; i++) {
/* Power down for 8s. */
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
}
#endif
static void start_watchdog_with_full_reset() {
MCUSR &= ~B00001000; /* Clear reset flag. */
WDTCSR |= B00011000; /* Prepare prescaler change. */
WDTCSR = B00100001; /* Set watchdog timeout to 8s. */
// Enable Watchdog Timer
WDTCSR |= B01000000;
MCUSR = MCUSR & B11110111;
}
static inline void disable_recording_interrupts() {
TIMSK1 &= ~(_BV(OCIE1A) | _BV(OCIE1B));
}
enum AdcChannel : uint8_t {
AdcChannel0 = 0,
AdcChannel1 = 1,
@@ -171,9 +85,9 @@ ISR(TIMER1_COMPA_vect) {
sei();
const size_t bufsz = sizeof(SAMPLE_BUF_TYPE) * samples_in_buffer[!which_buffer];
if (file.write((char*)sample_buffer[!which_buffer], bufsz) != bufsz) {
printf(F("Lost "));
print_special((float)samples_hanging / (float)(F_CPU / TIMER_COMPARE)); /* Printf doesn't handle floats. */
printf(F(" seconds of recording.\n"));
info(F("Lost "));
info_special((float)samples_hanging / (float)(F_CPU / TIMER_COMPARE)); /* Printf doesn't handle floats. */
info(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];
@@ -191,7 +105,7 @@ ISR(TIMER1_COMPA_vect) {
ISR(TIMER1_COMPB_vect) {
// Retrieve ADC Value and Write to Buffer
#if defined(SAMPLE_MODE_U8)
#ifdef U8_EXTRA_PRECISION
#ifdef U8_AMPLIFY_X2
uint8_t l = ADCL; /* Read ADC registers. (Order matters!) */
uint8_t h = ADCH;
uint8_t adcval = (h << 7) | (l >> 1);
@@ -264,18 +178,41 @@ static void wav_write_header(uint32_t nsamples) {
void setup() {
// Serial Setup
#ifdef SERIAL_OUTPUT
Serial.begin(9600); /* Set baud rate. */
setup_serial_in_out(); /* Add printf support. */
io_setup(); /* Add printf support. */
// Component Switch Setup
#ifdef PIN_COMPONENT_SWITCH
pinMode(PIN_COMPONENT_SWITCH, OUTPUT);
#endif
// Delay Triggering
#if defined(RECORDING_DELAY_IN_MINUTES) && RECORDING_DELAY_IN_MINUTES != 0
printf(F("Waiting %u minute%s before starting to record...\n"), RECORDING_DELAY_IN_MINUTES, RECORDING_DELAY_IN_MINUTES == 1 ? "" : "s");
// Load EEPROM Data
settings.load();
// Handle Commands
info(F("Type anything in the next 4s to enter command mode.\n"));
for (size_t i = 0; i < 4 * 4; i++) {
if (Serial.available())
cmd();
delay(250);
}
// Delayed Triggering
if (settings.recording_delay) {
#ifdef PIN_COMPONENT_SWITCH
digitalWrite(PIN_COMPONENT_SWITCH, !COMPONENT_SWITCH_ON);
#endif
info(F("Sleeping for %lu minute%s before starting to record...\n"), settings.recording_delay, settings.recording_delay == 1 ? "" : "s");
Serial.flush();
low_power_sleep_minutes(RECORDING_DELAY_IN_MINUTES); /* Draws ~12.5mA instead of ~30mA when using delay(). */
/* Using this function, an Arduino Nano (with its voltage regulator and TTL module removed) draws ~6μA. */
low_power_sleep_minutes(settings.recording_delay);
/* Reset wait time. */
settings.recording_delay = 0;
settings.save();
}
// Activate Components
#ifdef PIN_COMPONENT_SWITCH
digitalWrite(PIN_COMPONENT_SWITCH, COMPONENT_SWITCH_ON);
delay(500); /* Wait for components to initialize. */
#endif
// Start Watchdog (wdt_enable() doesn't fully reset)
start_watchdog_with_full_reset();
wdt_enable_with_full_reset();
// SD Card Setup
if (!SD.begin(PIN_SS))
die(F("Error initializing SD card!\n"));
@@ -284,11 +221,11 @@ void setup() {
char filename[32];
do {
filenum++;
snprintf(filename, 32, "rec_%03u.wav", filenum);
snprintf(filename, 32, REC_FILE_FMT, filenum);
} while (SD.exists(filename));
// Open File
file = SD.open(filename, O_READ | O_WRITE | O_CREAT); /* Seeking doesn't seem to work with FILE_WRITE?! */
printf(F("Recording to file '%s'.\n"), filename);
info(F("Recording to file '%s'.\n"), filename);
if (!file)
die(F("Error opening '%s' for writing!\n"), filename);
wav_write_header(0);
@@ -305,7 +242,7 @@ void setup() {
#endif
ADCSRB = _BV(ADTS2) | _BV(ADTS0); /* Auto-trigger source select: "Timer/Counter1 Compare Match B". */
ADMUX = _BV(REFS0) /* Use AREF pin (VCC by default) as reference voltage. */
#if defined(SAMPLE_MODE_U8) && !defined(U8_EXTRA_PRECISION)
#if defined(SAMPLE_MODE_U8) && !defined(U8_AMPLIFY_X2)
| _BV(ADLAR) /* Left adjust ADC output so we only need to read ADCH. */
#endif
| (0xF & ADC_CHANNEL); /* Select our ADC input channel. */
@@ -318,8 +255,9 @@ void setup() {
| _BV(OCIE1B); /* Enable "Output Compare B Match Interrupt". */
}
void loop() {
delay(1000);
delay(2000);
wdt_reset(); /* Reset watchdog timer. */
#ifdef DEBUG_RECORDING
dbg(F("n=%lu\tavg=%lu\tmin=%d\tmax=%d\n"), dbg_total, dbg_sum / (dbg_samples ? dbg_samples : 1), dbg_min, dbg_max);
@@ -328,5 +266,5 @@ void loop() {
dbg_min = 32767;
dbg_max = -32768;
#endif
printf(F("samples: written=%lu, hanging=%lu, dropped=%lu\n"), samples_written, samples_hanging, samples_dropped);
info(F("samples: written=%lu, hanging=%lu, dropped=%lu\n"), samples_written, samples_hanging, samples_dropped);
}

37
spybug/sys.cpp Normal file
View File

@@ -0,0 +1,37 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include "sys.hh"
void (*full_reset)() = nullptr;
static volatile bool wdt_in_sleep_mode = false;
ISR (WDT_vect) {
if (wdt_in_sleep_mode)
wdt_disable();
else
full_reset();
}
/* Based on https://github.com/rocketscream/Low-Power. */
void low_power_sleep_minutes(unsigned long t) {
wdt_in_sleep_mode = true;
ADCSRA &= ~_BV(ADEN); /* Disable ADC. */
for (unsigned long i = 0; 8ul * i < 60ul * t; i++) {
// Power Down for 8s
wdt_enable(WDTO_8S); /* Start watchdog timer for 8s. */
WDTCSR |= (1 << WDIE); /* Enable watchdog interrupt. */
do {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_enable();
sleep_bod_disable();
sei();
sleep_cpu();
sleep_disable();
sei();
} while (0);
}
ADCSRA |= _BV(ADEN); /* Re-enable ADC. */
wdt_in_sleep_mode = false;
}

22
spybug/sys.hh Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#pragma once
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
extern void (*full_reset)();
void low_power_sleep_minutes(unsigned long t);
static inline void wdt_enable_with_full_reset() {
wdt_enable(WDTO_8S); /* Start watchdog timer for 8s. */
WDTCSR |= (1 << WDIE); /* Enable watchdog interrupt. */
}
static inline void disable_recording_interrupts() {
TIMSK1 &= ~(_BV(OCIE1A) | _BV(OCIE1B));
}