Arduino Nano frequency counter with atomic clock accuracy

Overview

Arduino Nano frequency counter with atomic clock accuracy

Project description and test setup

With this project you can measure a frequency from less than 1 Hz to about 6.4 MHz, with atomic clock accuracy (fclk_I_O / 2.5 according to the datasheet, which is 16 MHz / 2.5 = 6.4 MHz, but 7.5 MHz still works, but might miss some counts then sometimes). All you need for it is your computer, and an Arduino Nano, which you can get for less than five dollars at eBay.

The idea is that the Arduino counts pulses, and then a program on your computer calculates the frequency. If your computer is synchronized to a NTP time server (this is standard nowadays for most operating systems), it has a very accurate time. The USB connection and operating system introduces some inaccuracies and latencies, but the longer the program runs, the more accurate the measurement gets.

Test setup with a SDG150 signal generator:

signal generator

Arduino Nano breadboard setup:

breadboard setup

Measured data after a few seconds is already about 0.05% accurate (500 ppm):

time: 0.0 s, frequency: 0 Hz, delta: 0 Hz, counts: 0
time: 1.0 s, frequency: 999,470.878 Hz, delta: -999470.878 Hz, counts: 999,615
time: 2.0 s, frequency: 1,000,848.773 Hz, delta: -1377.895 Hz, counts: 2,001,976
time: 3.0 s, frequency: 1,000,125.175 Hz, delta: 723.598 Hz, counts: 3,000,748
time: 4.0 s, frequency: 999,959.166 Hz, delta: 166.009 Hz, counts: 4,000,346
time: 5.0 s, frequency: 1,000,420.486 Hz, delta: -461.32 Hz, counts: 5,002,709
time: 6.0 s, frequency: 1,000,267.306 Hz, delta: 153.18 Hz, counts: 6,002,343

The longer it runs, the better it gets. After a few hours, the accuracy is better than 20 ppb (ppb=parts per billion, 0.000002%):

time: 12860.6 s, frequency: 1,000,043.062 Hz, delta: 0.1 Hz, counts: 12,861,168,998
time: 12861.6 s, frequency: 1,000,042.968 Hz, delta: 0.094 Hz, counts: 12,862,167,948
time: 12862.6 s, frequency: 1,000,042.936 Hz, delta: 0.032 Hz, counts: 12,863,167,687
time: 12863.6 s, frequency: 1,000,043.087 Hz, delta: -0.151 Hz, counts: 12,864,169,841
time: 12864.6 s, frequency: 1,000,042.983 Hz, delta: 0.104 Hz, counts: 12,865,168,701
time: 12865.6 s, frequency: 1,000,042.948 Hz, delta: 0.035 Hz, counts: 12,866,168,430
time: 12866.6 s, frequency: 1,000,043.101 Hz, delta: -0.153 Hz, counts: 12,867,170,571

Important: The accuracy depends on the number of counts and the runtime. For low frequencies, it needs longer to be accurate. For example for 1.5 Hz, it is only about 10% accurate after a few seconds:

time: 0.0 s, frequency: 0 Hz, delta: 0 Hz, counts: 0
time: 1.0 s, frequency: 1 Hz, delta: -1 Hz, counts: 1
time: 2.0 s, frequency: 1.5 Hz, delta: -0.5 Hz, counts: 3
time: 3.0 s, frequency: 1.333 Hz, delta: 0.167 Hz, counts: 4
time: 4.0 s, frequency: 1.5 Hz, delta: -0.167 Hz, counts: 6
time: 5.0 s, frequency: 1.4 Hz, delta: 0.1 Hz, counts: 7
time: 6.0 s, frequency: 1.5 Hz, delta: -0.1 Hz, counts: 9

You can also use the program as an event counter. It increments the counts value for each high to low change on the input pin.

Program the Arduino Nano

Start the Arduino IDE, open the Arduino script in the arduino/frequencycounter directory, select the Arduino Nano board, and flash it as usual. Note: some of these cheap Arduino clones from China might need the old bootloader:

bootloader setup

Then connect the signal to measure to pin D5.

Important: The Arduino Nano runs with 3.3 V. The input signal must be between 0 V and 3.3 V, and for best measurement results, it should be a square wave signal. You can add some input protection and a Schmitt-Trigger to protect and condition the input signal, if required.

How to compile and use the program on the computer side

The computer program is written in Rust and should run on Linux, Mac, and Windows (tested only on Linux so far). To compile it, first install Rust, e.g. from https://rustup.rs. Then change to the rust/frequencycounter directory on the command line and build it with cargo build --release. You should see something like this:

frank@hal9000:~/data/projects/frequencycounter/rust/frequencycounter$ cargo build --release
    Updating crates.io index
   Compiling libc v0.2.107
   Compiling pkg-config v0.3.22
   Compiling nix v0.16.1
   Compiling void v1.0.2
   Compiling cfg-if v0.1.10
   Compiling bitflags v1.3.2
   Compiling separator v0.4.1
   Compiling libudev-sys v0.1.4
   Compiling libudev v0.2.0
   Compiling serialport v4.0.1
   Compiling frequencycounter v0.1.0 (/home/frank/data/projects/frequencycounter/rust/frequencycounter)
    Finished release [optimized] target(s) in 9.32s

Then you can start it. If you start it without a command line parameter, you can see a list of all available serial ports:

frank@hal9000:~/data/projects/frequencycounter/rust/frequencycounter$ ./target/release/frequencycounter 
usage: ./target/release/frequencycounter port
available ports:
/dev/ttyUSB0

If you have more than one, unplug the Arduino and check again to see which one is the right one.

If you start it with the port name as a command line argument, it starts measuring. You can use the tee command to see the measurement on the command line, and log it to a file at the same time:

frank@hal9000:~/data/projects/frequencycounter/rust/frequencycounter$ ./target/release/frequencycounter /dev/ttyUSB0 | tee log.txt
time: 0.0 s, frequency: 0 Hz, delta: 0 Hz, counts: 0
time: 1.0 s, frequency: 999,445.946 Hz, delta: -999445.946 Hz, counts: 999,600
time: 2.0 s, frequency: 1,000,772.975 Hz, delta: -1327.029 Hz, counts: 2,001,858
time: 3.0 s, frequency: 1,000,099.088 Hz, delta: 673.887 Hz, counts: 3,000,742
time: 4.0 s, frequency: 999,941.637 Hz, delta: 157.451 Hz, counts: 4,000,340

You can stop the program with ctrl-c.

You might also like...
Rate limit guard - Lazy rate limit semaphore implementation to control your asynchronous code frequency execution

Lazy rate limit semaphore (a.k.a fixed window algorithm without queueing) implementation to control your asynchronous code frequency execution

A clock app in terminal written in Rust, supports local clock, timer and stopwatch.
A clock app in terminal written in Rust, supports local clock, timer and stopwatch.

clock-tui (tclock) A clock app in terminal. It support the following modes: Clock Timer Stopwatch Countdown Usage Install Install excutable by cargo:

Library to calculate TF-IDF (Term Frequency - Inverse Document Frequency) for generic documents.

Library to calculate TF-IDF (Term Frequency - Inverse Document Frequency) for generic documents. The library provides strategies to act on objects that implement certain document traits (NaiveDocument, ProcessedDocument, ExpandableDocument).

⋰·⋰ Feeless is a Nano cryptocurrency node, wallet, tools, and Rust crate.

⋰·⋰ Feeless What is Feeless? Feeless is a Nano cryptocurrency node, wallet, tools, and Rust crate. This is not the official project for Nano, only an

async-alloc-counter measures max allocations in a future invocation

async-alloc-counter measures max allocations in a future invocation see examples/ for usage This allocator can be used as follows: use async_alloc_cou

k-mer counter in Rust using the rust-bio and rayon crates

krust is a k-mer counter written in Rust and run from the command line that will output canonical k-mers and their frequency across the records in a f

A mnemonic brute forcing tool for nano and banano.

brute a mnemonic brute forcing tool for nano and banano. What is brute? It is a mnemonic brute forcing tool which can be used to recover an account fr

An open source desktop wallet for nano and banano with end-to-end encrypted, on chain messaging using the dagchat protocol.
An open source desktop wallet for nano and banano with end-to-end encrypted, on chain messaging using the dagchat protocol.

An open source wallet with end-to-end encrypted, on chain messaging for nano and banano using the dagchat protocol.

Starknet application for Ledger Nano S, SP, X
Starknet application for Ledger Nano S, SP, X

Ledger Starkware app Please visit our website at zondax.ch This project contains the Starkware app (https://starkware.co/) for Ledger Nano S and X. Le

Simple, thread-safe, counter based progress logging

🦫 proglog Documentation Crates.io This is a simple, thread-safe, count-based, progress logger. Synopsis proglog hooks into your existing log implemen

Counter-Strike 2 Offset Dumper

cs2-dumper An external offsets/interfaces dumper for Counter-Strike 2, written in Rust. Usage You can either download the latest release from Releases

rsdate connects to an ntp server, printing the returned time and/or sets the system clock.

🦀 📅 rsdate rsdate connects to an ntp server, printing the returned time and/or sets the system clock.

This is a Pomodoro Clock implemented as a Zellij plugin.

Pomodoro Clock This is a Pomodoro Clock implemented as a Zellij plugin. It shows a Pomodoro time as well as current date time. Prerequisite You must i

Kit-kat clock utility rewritten in Rust using minifb
Kit-kat clock utility rewritten in Rust using minifb

kitkat clock in Rust This is the plan9 cat clock utility rewritten in rust with minifb crate. $ kitkat --help Usage: kitkat [--hook|--crazy|--offset O

Optimize floating-point expressions for accuracy
Optimize floating-point expressions for accuracy

Herbie automatically improves the error of floating point expressions. Visit our website for tutorials, documentation, and an online demo. Herbie has

A NES emulator written in Rust, with a focus on expandability and accuracy

A NES emulator written in Rust, with a focus on expandability and accuracy

A terminal clock that uses 7-segment display characters

Seven-segment clock (7clock) 7clock.3.mp4 This is a clock for terminals that uses the Unicode seven-segment display characters added in Unicode 13.0.

This CLI will help you improve your typing accuracy and speed
This CLI will help you improve your typing accuracy and speed

This CLI will help you improve your typing accuracy and speed! Improve your personal bests and look back on your previous records in a graph. All in the convenience of your own terminal!

SP3 Precise GNSS Orbit and Clock parser :artificial_satellite:

SP3 SP3 Precise GNSS Orbit files parser. SP3 is specifid by IGS. The parser only supports Revisions C & D at the moment and rejects revisions A & B. G

Owner
Frank Buss
Frank Buss
k-mer counter in Rust using the rust-bio and rayon crates

krust is a k-mer counter written in Rust and run from the command line that will output canonical k-mers and their frequency across the records in a f

null 14 Jan 7, 2023
This is a Pomodoro Clock implemented as a Zellij plugin.

Pomodoro Clock This is a Pomodoro Clock implemented as a Zellij plugin. It shows a Pomodoro time as well as current date time. Prerequisite You must i

Tw 15 Nov 14, 2022
Optimize floating-point expressions for accuracy

Herbie automatically improves the error of floating point expressions. Visit our website for tutorials, documentation, and an online demo. Herbie has

Herbie Project 611 Dec 19, 2022
Board Support Crate for Arduino Leonardo in Rust

Deprecation Note: This crate will soon be deprecated in favor of avr-hal. avr-hal is a new approach to writing the HAL crate, that was designed with s

Rahix 5 May 6, 2021
🖥 Simple Arduino Serial Monitor

Born out of the desire to connect to an Arduino without having to run the whole Arduino suite.

Robin Schroer 2 Mar 19, 2022
Garden monitoring system using m328p and m2560 Arduino Uno boards

Garden monitoring system using m328p and m2560 Arduino Uno boards. 100% Rust [no_std] using the avr hardware abstraction layer (avr-hal)

Ethan Gallucci 1 May 4, 2022
Garden monitoring system using m328p Arduino Uno boards. 100% Rust [no_std] using the avr hardware abstraction layer (avr-hal)

uno-revive-rs References Arduino Garden Controller Roadmap uno-revive-rs: roadmap Components & Controllers 1-2 Uno R3 m328p Soil moisture sensor: m328

Ethan Gallucci 1 May 4, 2022
Arduino Uno 9 axis acceleration sensor (BMX055) reader implemented in Rust.

Arduino Uno Accelaration reader in Rust Components Arduino Uno (Probably possible with other AVR microcontrollers) BMX055 (Japanese website) Datasheet

Katsu Uchiyama 3 Dec 15, 2022
How to use an Arduino library in a Rust project?

Example of an Arduino library usage in a Rust project The project tested with Arduino UNO on Fedora 35. It demonstrates the usage of LiquidCrystal_I2C

Konstantin 6 Dec 27, 2022
Malloc frequency profiler

Malloc frequency profiler This malloc frequency profiler helps detect program hotspots that perform a large number of memory allocations.

Leonid Ryzhyk 7 Jan 7, 2022