{"id":16218918,"url":"https://github.com/arminjo/arduino-frequencydetector","last_synced_at":"2025-03-19T10:31:15.187Z","repository":{"id":93078689,"uuid":"132335158","full_name":"ArminJo/Arduino-FrequencyDetector","owner":"ArminJo","description":"Fast audio frequency detector without fft for plain Arduino and Attiny85. Whistle switch example included.","archived":false,"fork":false,"pushed_at":"2024-01-31T19:04:02.000Z","size":1224,"stargazers_count":40,"open_issues_count":0,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-28T18:22:34.283Z","etag":null,"topics":["arduino","arduino-library","frequency-detection","pitch-detection","whistle-detector"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ArminJo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-06T12:12:48.000Z","updated_at":"2025-01-31T08:28:47.000Z","dependencies_parsed_at":"2023-03-06T12:15:13.310Z","dependency_job_id":"b07fd59b-d211-4499-b613-f481dabb38b1","html_url":"https://github.com/ArminJo/Arduino-FrequencyDetector","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArminJo%2FArduino-FrequencyDetector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArminJo%2FArduino-FrequencyDetector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArminJo%2FArduino-FrequencyDetector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArminJo%2FArduino-FrequencyDetector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArminJo","download_url":"https://codeload.github.com/ArminJo/Arduino-FrequencyDetector/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243985546,"owners_count":20379158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-library","frequency-detection","pitch-detection","whistle-detector"],"created_at":"2024-10-10T11:51:29.978Z","updated_at":"2025-03-19T10:31:14.906Z","avatar_url":"https://github.com/ArminJo.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align = center\u003e\n\n# [Frequency Detector](https://github.com/ArminJo/Arduino-FrequencyDetector) Library for Arduino and ATtinys\n\nDetects frequency **from 38 Hz to 9612 Hz** and works even on an ATTiny85 with 1 MHz up to 4806 Hz.\u003cbr/\u003e\nThe input signal can be plotted to the Arduino Serial Plotter resulting in a **simple Oscilloscope** to test the internal signal.\u003cbr/\u003e\nOnly tested on ATtiny85 and ATmega328P.\n\n[![Badge License: GPLv3](https://img.shields.io/badge/License-GPLv3-brightgreen.svg)](https://www.gnu.org/licenses/gpl-3.0)\n \u0026nbsp; \u0026nbsp; \n[![Badge Version](https://img.shields.io/github/v/release/ArminJo/Arduino-FrequencyDetector?include_prereleases\u0026color=yellow\u0026logo=DocuSign\u0026logoColor=white)](https://github.com/ArminJo/Arduino-FrequencyDetector/releases/latest)\n \u0026nbsp; \u0026nbsp; \n[![Badge Commits since latest](https://img.shields.io/github/commits-since/ArminJo/Arduino-FrequencyDetector/latest?color=yellow)](https://github.com/ArminJo/Arduino-FrequencyDetector/commits/master)\n \u0026nbsp; \u0026nbsp; \n[![Badge Build Status](https://github.com/ArminJo/Arduino-FrequencyDetector/workflows/LibraryBuildWithAction/badge.svg)](https://github.com/ArminJo/Arduino-FrequencyDetector/actions)\n \u0026nbsp; \u0026nbsp; \n![Badge Hit Counter](https://visitor-badge.laobi.icu/badge?page_id=ArminJo_Arduino-FrequencyDetector)\n\u003cbr/\u003e\n\u003cbr/\u003e\n[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)\n\nAvailable as [Arduino library \"Arduino-FrequencyDetector\"](https://www.arduinolibraries.info/libraries/frequency-detector).\n\n[![Button Install](https://img.shields.io/badge/Install-brightgreen?logoColor=white\u0026logo=GitBook)](https://www.ardu-badge.com/Arduino-FrequencyDetector)\n \u0026nbsp; \u0026nbsp; \n[![Button Changelog](https://img.shields.io/badge/Changelog-blue?logoColor=white\u0026logo=AzureArtifacts)](https://github.com/ArminJo/Arduino-FrequencyDetector#revision-history)\n\n\u003c/div\u003e\n\n#### If you find this library useful, please give it a star.\n\n\u0026#x1F30E; [Google Translate](https://translate.google.com/translate?sl=en\u0026u=https://github.com/ArminJo/Arduino-FrequencyDetector)\n\n\u003cbr/\u003e\n\nYouTube video of whistle switch example in action.\n\n[![Demonstration of 3 whistle switches in one room](https://i.ytimg.com/vi/_e2mElB8zJs/hqdefault.jpg)](https://www.youtube.com/watch?v=_e2mElB8zJs)\n\n\u003cbr/\u003e\n\n# Internal operation\nThis library analyzes a (microphone) signal and outputs the detected frequency. It simply counts zero crossings and **it does not use FFT**.\nThe ADC sample data is **not** stored in RAM, only the period lengths (between triggers) are stored in the `PeriodLength[]` array,\nwhich is a byte array and has the size of `NUMBER_OF_SAMPLES / 8`.\u003cbr/\u003e\nIt is like in the [Arduino Simple Audio Frequency Meter](https://www.arduino.cc/en/Tutorial/SimpleAudioFrequencyMeter) but includes additional noise check.\u003cbr/\u003e\nThe **timer 0 interrupt**, which counts the milliseconds, **is disabled during reading** and enabled afterwards!\nThe value of millis() is adjusted after reading.\u003cbr/\u003e\nThe alternative to disabling the interrupt is getting partially invalid results!\n\nThere are 3 detection ranges available:\n- `FREQUENCY_RANGE_HIGH` -\u003e 13 \u0026micro;s/sample -\u003e 300 to **9612** Hz with 1024 samples and 600 to 9612 Hz with 512 samples.\n- `FREQUENCY_RANGE_DEFAULT` -\u003e 52 \u0026micro;s/sample -\u003e **75 to 2403 Hz with 1024 samples** and 150 to 2403 Hz with 512 samples.\n- `FREQUENCY_RANGE_LOW` -\u003e 104 \u0026micro;s/sample -\u003e **38** to 1202 Hz with 1024 samples and 75 to 1202 Hz with 512 samples.\n\n### `readSignal()` is the ADC read routine, which reads 1024 samples (512 for ATtinies) and computes the following values:\n  1. Frequency of signal `uint16_t FrequencyRaw`\n  2. Amplitude = (MaxValue - MinValue) `uint16_t SignalDelta`\n  3. Average = (SumOfSampleValues / NumberOfSamples) `uint16_t AverageLevel`\n  4. The length of each period (between 2 trigger conditions) in the `PeriodLength[]` array.\n\n### `doEqualDistributionPlausi()` checks if the signal in the `PeriodLength[]` array is valid / not noisy.\nIt checks if at maximum 1/8 of the periods are greater than 1.5 or less than 0.75 of the average period.\nIf not, the value of `FrequencyRaw` is overwritten with the error code `SIGNAL_DISTRIBUTION_PLAUSI_FAILED`.\n\n### `computeDirectAndFilteredMatch()` waits for n matches within a given frequency range (FrequencyMatchLow - FrequencyMatchHigh)\nand also low pass filters the result for smooth transitions between the 3 match states (lower, match, greater). It computes the following values:\n  1. Low pass filtered frequency of signal `uint16_t FrequencyFiltered;`\n  2. Match result `MatchStateEnum FrequencyMatchDirect;`\n  3. Low pass filtered match result `MatchStateEnum FrequencyMatchFiltered`\n\n\u003cbr/\u003e\n\n# Compile options / macros for this library\nTo customize the library to different requirements, there are some compile options / macros available.\u003cbr/\u003e\nThese macros must be defined in your program **before** the line `#include \"FrequencyDetector.hpp\"` to take effect.\u003cbr/\u003e\nModify them by enabling / disabling them, or change the values if applicable.\n\n| Name | Default value | Description |\n|-|-:|-|\n| `PRINT_INPUT_SIGNAL_TO_PLOTTER` | disabled | Signal input data is stored and can be printed together with trigger levels using `printInputSignalValuesForArduinoPlotter()` like in the *SimpleFrequencyDetector* example to implement a simple digital oscilloscope using the Arduino 1.x Serial Plotter. |\n\n### Arduino Plotter output of SimpleFrequencyDetector example with PRINT_INPUT_SIGNAL_TO_PLOTTER enabled\n![Arduino Plotter output of SimpleFrequencyDetector example with `PRINT_INPUT_SIGNAL_TO_PLOTTER` enabled](https://github.com/ArminJo/Arduino-FrequencyDetector/blob/master/extras/SimpleFrequencyDetectorPlotterOutput.png)\n\n### Arduino Plotter output of SimpleFrequencyDetector example with PRINT_RESULTS_TO_SERIAL_PLOTTER enabled\nAbove you see the `FrequencyRaw` I whistled with all the dropouts and the `FrequencyFiltered` without dropouts but with a slight delay if the `FrequencyRaw` changes.\u003cbr/\u003e\nThe WhistleSwitch example uses the `FrequencyMatchFiltered` value, to decide if a match happens. The value for a valid match is **200**. At 80 and 140 you see 2 short and at 300 you see a long valid match.\n\n![Arduino Plotter output of whistle switch in action](https://github.com/ArminJo/Arduino-FrequencyDetector/blob/master/extras/WhistleSwitchPlotterOutput.png)\n\n\u003cbr/\u003e\n\n# Dependencies\nThe [ATtinySerialOut library](https://github.com/ArminJo/ATtinySerialOut) is required for [print functions](src/FrequencyDetector.h#L312) on ATtinies.\n\n\u003cbr/\u003e\n\n# SimpleFrequencyDetector example\nThis example reads analog signal e.g. from MAX9814 Module at A1 and computes the frequency.\nIf frequency is in the range of 1400 to 1700 Hz, the Arduino built in LED will light up.\nIt prints the detected frequency as well as plausibility errors.\nFor frequency below 500 Hz it might be good to change `FREQUENCY_RANGE_DEFAULT` to `FREQUENCY_RANGE_LOW`.\n\nBy enabling `PRINT_INPUT_SIGNAL_TO_PLOTTER` you can convert the example to a simple DSO.\u003cbr/\u003e\nBy enabling `PRINT_RESULTS_TO_SERIAL_PLOTTER` you can watch the [generated output](https://github.com/ArminJo/Arduino-FrequencyDetector#arduino-plotter-output-of-whistleswitch-in-action) of the library.\n\nSimpleFrequencyDetector on breadboard with MAX9814 Module\n![SimpleFrequencyDetector on breadboard with MAX9814 Module](https://github.com/ArminJo/Arduino-FrequencyDetector/blob/master/extras/SimpleFrequencyDetector_MAX9814.jpg)\nYouTube Demonstration of SimpleFrequencyDetector with MAX9812 Module\n\n[![Demonstration of SimpleFrequencyDetector](https://img.youtube.com/vi/tsxfSx0iY5s/0.jpg)](https://www.youtube.com/watch?v=tsxfSx0iY5s)\n\n\u003cbr/\u003e\n\n# WhistleSwitch example\nThe WhistleSwitch example analyzes a microphone signal (I use a MAX9814 module from Adafruit) and toggles an output pin, if the main frequency is for a specified duration in a specified range.\nIt works as a frequency detector for a whistle pitch which operates a mains relay. By using different pitches it is possible to control multiple relays in a single room.\u003cbr/\u003e\nIf the pitch is lower than the specified frequency, the feedback LED blinks slowly, if the pitch is higher it blinks fast.\u003cbr/\u003e\nIf the (low pass filtered) match from the FrequencyDetector library holds for `MATCH_TO_LONG_MILLIS` (1 second) after switching output,\nthe output switches again, to go back to the former state.\nThis can be useful if a machine generated signal (e.g. from a vacuum cleaner) matches the range.\u003cbr/\u003e\n**This example is mainly created to run on an ATtiny85 at 1 MHz, but will work also on a plain Arduino.**\n\n\u003cbr/\u003e\n\n## PREDEFINED RANGES\nAfter power up or reset, the feedback LED echoes the range number. Range number 10 indicates an individual range, programmed by advanced selecting.\nThe timeout state is signaled by short LED pulses after the range number feedback (no short pulse -\u003e no timeout enabled).\n\nthe following pitch ranges are predefined for easy selection:\n1.   1700 - 2050 Hz  -\u003e 350 Hz A6-C6\n2.   1500 - 1680 Hz  -\u003e 180 Hz FS6-GS6\n3.   1300 - 1480 Hz  -\u003e 180 Hz E6-FS6\n4.   1150 - 1280 Hz  -\u003e 130 Hz D6-DS6\n5.   1000 - 1130 Hz  -\u003e 130 Hz C6-CS6\n6.    900 -  990 Hz  -\u003e  90 Hz A5-B5\n\n7.   1550 - 1900 Hz  -\u003e 350 Hz G6-AS6\n8.   1250 - 1530 Hz  -\u003e 380 Hz DS6-G6\n9.   1000 - 1230 Hz  -\u003e 230 Hz C6-DS6\n\n10.  Dummy range, if chosen, disables \"relay on\" timeout handling.\n11.  Dummy range, if chosen, sets \"relay on\" timeout to 2 hours.\n12.  Dummy range, if chosen, sets \"relay on\" timeout to 4 hours.\n13.  Dummy range, if chosen, sets \"relay on\" timeout to 8 hours.\n\n## SELECTING the RANGE\nSelecting is started by a long press of the button.\nAfter `BUTTON_PUSH_ENTER_PROGRAM_SIMPLE_MILLIS` (1.5 seconds), the feedback LED blinks once for signaling simple programming mode.\nAfter `BUTTON_PUSH_ENTER_PROGRAM_ADVANCED_MILLIS` (4 seconds), the feedback LED blinks twice for signaling advanced programming mode.\nAfter releasing the button, the selected programming mode is entered.\n\n### SIMPLE PROGRAMMING MODE\nPress the button once for range 1, twice for range 2 etc. Each button press is echoed by the feedback LED.\nWaiting for `PROGRAM_MODE_SIMPLE_END_DETECT_MILLIS` (1.5 seconds) ends the programming mode\nand the feedback LED echoes the number of button presses recognized.\nThe duration of signal match to toggle the relay is fixed at `MATCH_MILLIS_NEEDED_DEFAULT` (1.2 seconds).\n\n### ADVANCED PROGRAMMING MODE\nWhistle the pitch you want to detect, then press the button again.\nWhile you press the button, the pitch range is measured. i.e. the minimum and maximum of the pitch you are whistling is stored.\n\nIf you press the button again before the `PROGRAM_MODE_ADVANCED_END_DETECT_MILLIS` (3 seconds) timeout\nthe duration of this second press is taken as the required duration for the signal match to toggle the relay.\nOtherwise the `MATCH_MILLIS_NEEDED_DEFAULT` (1.2 seconds) are taken.\nAfter timeout of `PROGRAM_MODE_TIMEOUT_MILLIS` (5 seconds) the advanced programming mode is ended\nand the effective duration is echoed by the feedback LED.\n\n## TIMEOUT\nAfter a timeout of `TIMEOUT_RELAY_ON_SIGNAL_MINUTES`_(1 to 3) (2, 4 or 8 hours) the relay goes OFF for 1 second.\nIn the next `TIMEOUT_RELAY_SIGNAL_TO_OFF_MINUTES` (3) minutes you must then press the button or whistle the pitch to cancel the timeout, otherwise the relay will switch OFF afterwards.\nCancellation of timeout is acknowledged by the LED blinking 5 times for 1 second on and off. Timeout can be switched on by selecting the dummy ranges 11 to 13 and off by selecting the dummy range 10.\nThe setting is stored in EEPROM. Default is `TIMEOUT_RELAY_ON_SIGNAL_MINUTES_3` (8 hours).\n\n## RESET\nA reset can be performed by power off/on or by pressing the button two times, each time shorter than `RESET_ENTER_BUTTON_PUSH_MILLIS` (0.12 seconds) within a `RESET_WAIT_TIMEOUT_MILLIS` (0.3 seconds) interval.\n\n\u003cbr/\u003e\n\n# SCHEMATIC for external components of FrequencyDetector / WhistleSwitch\n```\nDiscrete microphone amplifier with LM308\n\n         + 5V                             _____                   o--O PIN REF\n         |                             o-|_____|--o               |\n         _                             |   1M     |               _\n        | |                            |          |              | |\n        | | 2k2                        o---|\\     |              | | 1M\n        |_|                            |  2| \\____|              |_|\n         |    _____           _____    |   | /6   |   ____   | |  |\n         o---|_____|----o----|_____|-------|/     o--|____|--| |--o--O PIN A1\n         |     2k2      |      10k     |  3             10k  | |  |\n        ---            |O MICROPHONE   _    LM308        10-100nF _\n        --- 1 uF        |             | |                        | |\n         |              |             | | 10k                    | | 1M\n        _|_            _|_            |_|                        |_|\n                                       |                          |\n                                       |                          |\n                                      ---                        _|_\n                                      ---  100 nF\n                                       |\n                                      _|_\n```\n\n```\nExternal circuit for 1x amplification configuration on a Digispark board.\n\n         + CPU 5V                                    - * Schottky-diode\n         o------------------------------------ o-----|\u003c|--o-- USB 5V\n         |                                     |    -     |\n         _                                     |          |\n        | |                                    o /        |\n   470k | |                                     /=| Push button\n        |_|                                    /          |\n    1n   |   ____       ____                   o----------o\n \u003e- | |--o--|____|--o--|____|--O PB4 550 mV    |\n  500Hz  |   3k3    |   10k to enable USB      _\n  High   _          |       programming       | |\n  Pass  | |        ---                        | | * 1k5 pullup\n   100k | |        --- 22n 2kHz Low           |_|\n        |_|         |          Pass            |\n         |          |                  _____   |\n         o----------o         PB3 O---|_____|--o\n         |                           * 68/22   |\n         |                                    __\n         |                                    /\\` * 3V6 Z-diode\n         |                                    --\n         |                                     |  * = assembled USB circuit on Digispark\n         |                                     |\n        _|_                                   _|_\n```\n\n```\nExternal circuit for 20x amplification configuration on a Digispark board.\n\n         + CPU 5V                                    - * Schottky-diode\n         o------------------------------------ o-----|\u003c|--o-- USB 5V\n         |                                     |    -     |\n         _                                     |          |\n        | |                                    o /        |\n   680k | |                                     /=| Push button\n        |_|                                    /          |\n   100n  |   ____       ____                   o----------o\n \u003e- | |--o--|____|--o--|____|--O PB4 44 mV     |\n  500Hz  |   3k3    |   10k to enable USB      _\n  High   _          |       programming       | |\n  Pass  | |        ---                        | | * 1k5 pullup\n    3k3 | |        --- 22n 2kHz Low           |_|\n        |_|         |          Pass            |\n         |          |                  _____   |\n         o----------o--O PB3 22 mV----|_____|--o\n         |                           * 68/22   |\n         _                                    __\n        | |                                   /\\` * 3V6 Z-diode\n    3k3 | |                                   --\n        |_|                                    |  * = assembled USB circuit on Digispark\n         |                                     |\n        _|_                                   _|_\n\n  PB2 O-- Serial out 115200 baud\n  PB1 O-- Feedback LED\n  PB0 O-- Relay\n```\n\u003cbr/\u003e\n\n# Revision History\n### Version 2.1.0\n- Updated SimpleFrequencyDetector example.\n- Renamed `printSignalValuesForArduinoPlotter()` to  `printInputSignalValuesForArduinoPlotter()`,\n     `printLegendForArduinoPlotter()` to `printResultLegendForArduinoPlotter()`\n     and `printDataForArduinoPlotter()` to `printResultDataForArduinoPlotter()`.\n\n### Version 2.0.0\n- Added plotter output of input signal.\n- Renamed `doPlausi()` to `doEqualDistributionPlausi()`.\n- Changed [error values](src/FrequencyDetector.h#L170) and computation.\n- Added documentation.\n- Added [`MEASURE_READ_SIGNAL_TIMING`](src/FrequencyDetector.h#L64) capability.\n- Refactored [WhistleSwitch example](examples/WhistleSwitch) and adapted to [`EasyButtonAtInt01`](https://github.com/ArminJo/EasyButtonAtInt01) library.\n- Removed blocking wait for ATmega32U4 Serial in examples.\n\n### Version 1.1.1\n- Moved libraries for WhistleSwitch example.\n\n### Version 1.1.0\n- Corrected formula for compensating millis().\n- New field PeriodOfOneReadingMillis.\n- Now accept dropout values in milliseconds.\n- New functions `printResultLegendForArduinoPlotter()` and `printResultDataForArduinoPlotter()`.\n\n# CI\nThe library examples are tested with GitHub Actions for the following boards:\n\n- arduino:avr:uno\n- digistump:avr:digispark-tiny1\n- ATTinyCore:avr:attinyx5:chip=85,clock=1internal\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farminjo%2Farduino-frequencydetector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farminjo%2Farduino-frequencydetector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farminjo%2Farduino-frequencydetector/lists"}