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      |  A0 | ||||||
|  |  | ||||||
| Out defaults to A0 (AdcChannel0), but can be set manually in `ADC_CHANNEL`. | 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. | 	Out defaults to A0 (AdcChannel0), but can be set manually in ADC_CHANNEL. | ||||||
| */ | */ | ||||||
|  |  | ||||||
| #include <EEPROM.h> |  | ||||||
| #include <SD.h> | #include <SD.h> | ||||||
| #include <SPI.h> | #include <SPI.h> | ||||||
| #include <avr/interrupt.h> |  | ||||||
| #include <avr/io.h> |  | ||||||
| #include <avr/wdt.h> |  | ||||||
| #include <avr/sleep.h> |  | ||||||
|  |  | ||||||
| /************************ | #include "aaa_config.hh" | ||||||
|  BEGIN USER CONFIGURATION | #include "cmd.hh" | ||||||
|  ************************/ | #include "fstr.hh" | ||||||
| //#define DEBUG_RECORDING | #include "io.hh" | ||||||
|  | #include "settings.hh" | ||||||
| //#define PIN_COMPONENT_SWITCH 2 /* Use a digital signal to switch on/off the microphone and SD card for less power draw. */ | #include "sys.hh" | ||||||
| //#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 |  | ||||||
|  **********************/ |  | ||||||
|  |  | ||||||
| #if !defined(__AVR_ATmega328P__) || F_CPU != 16000000 | #if !defined(__AVR_ATmega328P__) || F_CPU != 16000000 | ||||||
| #error "This program only works on ATmega328P devices with a clock frequency of 16MHz!" | #error "This program only works on ATmega328P devices with a clock frequency of 16MHz!" | ||||||
| #endif | #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 { | enum AdcChannel : uint8_t { | ||||||
| 	AdcChannel0    = 0, | 	AdcChannel0    = 0, | ||||||
| 	AdcChannel1    = 1, | 	AdcChannel1    = 1, | ||||||
| @@ -199,16 +78,6 @@ volatile int16_t dbg_min = 32767; | |||||||
| volatile int16_t dbg_max = -32768; | volatile int16_t dbg_max = -32768; | ||||||
| #endif | #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) { | ISR(TIMER1_COMPA_vect) { | ||||||
| 	/* Only write to file, if one of the buffers is full (meaning no access conflicts). */ | 	/* Only write to file, if one of the buffers is full (meaning no access conflicts). */ | ||||||
| 	if (samples_in_buffer[!which_buffer] == SAMPLE_BUF_SIZE) { | 	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() { | void setup() { | ||||||
| 	// Serial Setup | 	// Serial Setup | ||||||
| 	Serial.begin(9600); /* Set baud rate. */ | 	Serial.begin(9600); /* Set baud rate. */ | ||||||
| 	setup_serial_in_out(); /* Add printf support. */ | 	io_setup(); /* 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); |  | ||||||
| 	} |  | ||||||
| 	// Component Switch Setup | 	// Component Switch Setup | ||||||
| #ifdef PIN_COMPONENT_SWITCH | #ifdef PIN_COMPONENT_SWITCH | ||||||
| 	pinMode(PIN_COMPONENT_SWITCH, OUTPUT); | 	pinMode(PIN_COMPONENT_SWITCH, OUTPUT); | ||||||
| #endif | #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 | 	// Delayed Triggering | ||||||
| 	if (settings.recording_delay) { | 	if (settings.recording_delay) { | ||||||
| #ifdef PIN_COMPONENT_SWITCH | #ifdef PIN_COMPONENT_SWITCH | ||||||
| @@ -423,7 +204,7 @@ void setup() { | |||||||
| 		low_power_sleep_minutes(settings.recording_delay); | 		low_power_sleep_minutes(settings.recording_delay); | ||||||
| 		/* Reset wait time. */ | 		/* Reset wait time. */ | ||||||
| 		settings.recording_delay = 0; | 		settings.recording_delay = 0; | ||||||
| 		save_settings(); | 		settings.save(); | ||||||
| 	} | 	} | ||||||
| 	// Activate Components | 	// Activate Components | ||||||
| #ifdef PIN_COMPONENT_SWITCH | #ifdef PIN_COMPONENT_SWITCH | ||||||
| @@ -440,7 +221,7 @@ void setup() { | |||||||
| 	char filename[32]; | 	char filename[32]; | ||||||
| 	do { | 	do { | ||||||
| 		filenum++; | 		filenum++; | ||||||
| 		snprintf(filename, 32, "rec_%03u.wav", filenum); | 		snprintf(filename, 32, REC_FILE_FMT, filenum); | ||||||
| 	} while (SD.exists(filename)); | 	} while (SD.exists(filename)); | ||||||
| 	// 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?! */ | ||||||
|   | |||||||
							
								
								
									
										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