Compare commits
10 Commits
25d7967d69
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea93980795 | ||
|
|
dac3a33e9e | ||
|
|
618c75b72b | ||
|
|
6f962c0d88 | ||
|
|
9ecb2f5579 | ||
|
|
5ed8e8f226 | ||
|
|
9420eb8293 | ||
|
|
4e90b217cb | ||
|
|
bbb53a31b3 | ||
|
|
1f17f87822 |
@@ -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
|
||||
|
||||
14
reset_sketch/reset_sketch.ino
Normal file
14
reset_sketch/reset_sketch.ino
Normal 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
1
reset_sketch/settings.hh
Symbolic link
@@ -0,0 +1 @@
|
||||
../spybug/settings.hh
|
||||
31
spybug/aaa_config.hh
Normal file
31
spybug/aaa_config.hh
Normal 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
191
spybug/cmd.cpp
Normal 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
6
spybug/cmd.hh
Normal 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
37
spybug/fstr.cpp
Normal 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
12
spybug/fstr.hh
Normal 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
22
spybug/io.cpp
Normal 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
14
spybug/io.hh
Normal 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
6
spybug/settings.cpp
Normal 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
17
spybug/settings.hh
Normal 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;
|
||||
@@ -28,141 +28,20 @@
|
||||
Out defaults to A0 (AdcChannel0), but can be set manually in ADC_CHANNEL.
|
||||
*/
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/wdt.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
/************************
|
||||
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
|
||||
/**********************
|
||||
END USER CONFIGURATION
|
||||
**********************/
|
||||
#include "aaa_config.hh"
|
||||
#include "cmd.hh"
|
||||
#include "fstr.hh"
|
||||
#include "io.hh"
|
||||
#include "settings.hh"
|
||||
#include "sys.hh"
|
||||
|
||||
#if !defined(__AVR_ATmega328P__) || F_CPU != 16000000
|
||||
#error "This program only works on ATmega328P devices with a clock frequency of 16MHz!"
|
||||
#endif
|
||||
|
||||
void (*full_reset)() = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static 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++;
|
||||
}
|
||||
}
|
||||
|
||||
#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); }
|
||||
|
||||
volatile bool wdt_int_sleep_mode = false;
|
||||
ISR (WDT_vect) {
|
||||
if (wdt_int_sleep_mode)
|
||||
wdt_disable();
|
||||
else
|
||||
full_reset();
|
||||
}
|
||||
|
||||
/* Based on https://github.com/rocketscream/Low-Power. */
|
||||
static void low_power_sleep_minutes(unsigned long t) {
|
||||
wdt_int_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_SAVE);
|
||||
cli();
|
||||
sleep_enable();
|
||||
sleep_bod_disable();
|
||||
sei();
|
||||
sleep_cpu();
|
||||
sleep_disable();
|
||||
sei();
|
||||
} while (0);
|
||||
}
|
||||
ADCSRA |= _BV(ADEN); /* Re-enable ADC. */
|
||||
wdt_int_sleep_mode = false;
|
||||
}
|
||||
|
||||
static 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));
|
||||
}
|
||||
|
||||
enum AdcChannel : uint8_t {
|
||||
AdcChannel0 = 0,
|
||||
AdcChannel1 = 1,
|
||||
@@ -199,16 +78,6 @@ volatile int16_t dbg_min = 32767;
|
||||
volatile int16_t dbg_max = -32768;
|
||||
#endif
|
||||
|
||||
#define EEADDR_SETTINGS 0x00
|
||||
struct EEPROM_Settings {
|
||||
unsigned long recording_delay;
|
||||
bool serial_log;
|
||||
};
|
||||
EEPROM_Settings settings;
|
||||
|
||||
#define load_settings() EEPROM.get(EEADDR_SETTINGS, settings)
|
||||
#define save_settings() EEPROM.put(EEADDR_SETTINGS, settings)
|
||||
|
||||
ISR(TIMER1_COMPA_vect) {
|
||||
/* Only write to file, if one of the buffers is full (meaning no access conflicts). */
|
||||
if (samples_in_buffer[!which_buffer] == SAMPLE_BUF_SIZE) {
|
||||
@@ -307,111 +176,23 @@ static void wav_write_header(uint32_t nsamples) {
|
||||
}
|
||||
}
|
||||
|
||||
void show_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"));
|
||||
}
|
||||
|
||||
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;
|
||||
save_settings();
|
||||
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;
|
||||
save_settings();
|
||||
printf(F("Serial log enabled.\n"));
|
||||
} else if (fstreq(args[2], F("off"))) {
|
||||
settings.serial_log = false;
|
||||
save_settings();
|
||||
printf(F("Serial log disabled.\n"));
|
||||
} else {
|
||||
printf(F("Usage: 'set serial [on|off]'.\n"));
|
||||
}
|
||||
} else {
|
||||
printf(F("Invalid usage of 'set'.\n"));
|
||||
show_help();
|
||||
}
|
||||
} 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("Invalid usage of 'get'.\n"));
|
||||
show_help();
|
||||
}
|
||||
} else if (fstreq(args[0], F("help"))) {
|
||||
show_help();
|
||||
} 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 setup() {
|
||||
// Serial Setup
|
||||
Serial.begin(9600); /* Set baud rate. */
|
||||
setup_serial_in_out(); /* Add printf support. */
|
||||
// Load EEPROM Data
|
||||
load_settings();
|
||||
// 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()) {
|
||||
printf(F("You are now in command mode. Reset to exit. Type 'help' for a list of commands.\n"));
|
||||
while (Serial.available()) Serial.read();
|
||||
while (1) {
|
||||
command_loop();
|
||||
delay(50);
|
||||
}
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
io_setup(); /* Add printf support. */
|
||||
// Component Switch Setup
|
||||
#ifdef PIN_COMPONENT_SWITCH
|
||||
pinMode(PIN_COMPONENT_SWITCH, OUTPUT);
|
||||
#endif
|
||||
// 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
|
||||
@@ -423,7 +204,7 @@ void setup() {
|
||||
low_power_sleep_minutes(settings.recording_delay);
|
||||
/* Reset wait time. */
|
||||
settings.recording_delay = 0;
|
||||
save_settings();
|
||||
settings.save();
|
||||
}
|
||||
// Activate Components
|
||||
#ifdef PIN_COMPONENT_SWITCH
|
||||
@@ -440,7 +221,7 @@ 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?! */
|
||||
|
||||
37
spybug/sys.cpp
Normal file
37
spybug/sys.cpp
Normal 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
22
spybug/sys.hh
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user