Compare commits
	
		
			12 Commits
		
	
	
		
			b6004c42e0
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ea93980795 | ||
|  | dac3a33e9e | ||
|  | 618c75b72b | ||
|  | 6f962c0d88 | ||
|  | 9ecb2f5579 | ||
|  | 5ed8e8f226 | ||
|  | 9420eb8293 | ||
|  | 4e90b217cb | ||
|  | bbb53a31b3 | ||
|  | 1f17f87822 | ||
|  | 25d7967d69 | ||
|  | 6599bf151d | 
| @@ -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,136 +28,19 @@ | ||||
| 	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 | ||||
| #include "aaa_config.hh" | ||||
| #include "cmd.hh" | ||||
| #include "fstr.hh" | ||||
| #include "io.hh" | ||||
| #include "settings.hh" | ||||
| #include "sys.hh" | ||||
|  | ||||
| //#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 | ||||
|  **********************/ | ||||
|  | ||||
| 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 &= ~(1 << 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 |= (1 << 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)); | ||||
| } | ||||
| #if !defined(__AVR_ATmega328P__) || F_CPU != 16000000 | ||||
| #error "This program only works on ATmega328P devices with a clock frequency of 16MHz!" | ||||
| #endif | ||||
|  | ||||
| enum AdcChannel : uint8_t { | ||||
| 	AdcChannel0    = 0, | ||||
| @@ -195,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) { | ||||
| @@ -303,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 | ||||
| @@ -419,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 | ||||
| @@ -436,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