The personal website of Scott W Harden
August 12th, 2017

Action Potential Generator Circuit

Few biological cells are as interesting to the electrical engineer as the neuron. Neurons are essentially capacitors (with a dielectric cell membrane separating conductive fluid on each side) with parallel charge pumps, leak currents, and nonlinear voltage-dependent currents. When massively parallelized, these individual functional electrical units yield complex behavior and underlie consciousness. The study of the electrical properties of neurons (neurophysiologically, a subset of electrophysiology) often involves the development and use of sensitive electrical equipment aimed at studying these small potentials produced by neurons and currents which travel through channels embedded in their membranes. It seems neurophysiology has gained an emerging interest from the hacker community, as evidenced by the success of Back Yard Brains, projects like the OpenEEG, and Hack-A-Day's recent feature The Neuron - a Hacker's Perspective.

While contemplating designs for action potential detection and analysis circuitry, I realized that it would be beneficial to be able to generate action-potential-like waveforms on my workbench. The circuit I came up with to do this is a fully analog (technically mixed signal) action potential generator which produces lifelike action potentials.

Cellular Neurophysiology for Electrical Engineers (in 2 sentences): Neuron action potentials (self-propagating voltage-triggered depolarizations) in individual neurons are measured in scientific environments using single cell recording tools such as sharp microelectrodes and patch-clamp pipettes. Neurons typically rest around -70mV and when depolarized (typically by external excitatory input) above a threshold they engage in a self-propagating depolarization until they reach approximately +40mV, at which time a self-propagating repolarization occurs (often over-shooting the initial rest potential by several mV), then the cell slowly returns to the rest voltage so after about 50ms the neuron is prepared to fire another action potential. Impassioned budding electrophysiologists may enjoy further reading _Active Behavior of the Cell Membrane _and Introduction to Computational Neuroscience.

The circuit I describe here produces waveforms which visually mimic action potentials rather than serve to replicate the exact conductances real neurons employ to exhibit their complex behavior. It is worth noting that numerous scientists and engineers have designed more physiological electrical representations of neuronal circuitry using discrete components. In fact, the Hodgkin-Huxley model of the initiation and propagation of action potentials earned Alan Hodgkin and Andrew Huxley the Nobel Prize in Physiology and Medicine in 1936. Some resources on the internet describe how to design lifelike action potential generating circuits by mimicking the endogenous ionic conductances which underlie them, notably Analog and Digital Hardware Neural Models, Active Cell Model, and Neuromorphic Silicon Neuron Circuits. My goal for this project is to create waveforms which resemble action potentials, rather than waveforms which truly model them. I suspect it is highly unlikely I will earn a Nobel Prize for the work presented here.

The analog action potential simulator circuit I came up with creates a continuous series action potentials. This is achieved using a 555 timer (specifically the NE555) in an astable configuration to provide continuous square waves (about 6 Hz at about 50% duty). The rising edge of each square wave is isolated with a diode and used to charge a capacitor. While the charge on the capacitor is above a certain voltage, an NPN transistor (the 2N3904) allows current to flow, amplifying this transient input current. The capacitor discharges predictably (as an RC circuit) through a leak resistor. A large value leak resistor slows the discharge and allows that signal's transistor to flow current for a longer duration. By having two signals (fast and slow) using RC circuits with different resistances (smaller and larger), the transistors are on for different durations (shorter and longer). By making the short pulse positive (using the NPN in common collector configuration) and the longer pulse negative (using the NPN in common emitter configuration), a resistor voltage divider can be designed to scale and combine these signals into an output waveform a few hundred mV in size with a 5V power supply. Pictured below is the output of this circuit realized on a breadboard. The blue trace is the output of the 555 timer.

Between the capacitance of the rectification diode, input capacitance of the transistor, and stray parasitic capacitance from the physical construction of my wires and the rails on my breadboard, there is sufficient capacitance to accumulate charge which can be modified by changing the value of the leak resistor.

This circuit produces similar output when simulated. I'm using LTspice (free) to simulate this circuit. The circuit shown is identical to the one hand-drawn and built on the breadboard, with the exception that an additional 0.1 µF capacitor to ground is used on the output to smooth the signal. On the breadboard this capacitance-based low-pass filtering already exists due to the capacitive nature of the components, wires, and rails.

A few improvements naturally come to mind when considering this completed, functional circuit:

  • Action potential frequency: The resistor/capacitor network on the 555 timer determines the rate of square pulses which trigger action potentials. Changing these values will cause a different rate of action potential firing, but I haven't attempted to push it too fast and suspect the result would not be stable is the capacitors are not given time to fully discharge before re-initiating subsequent action potentials.

  • Microcontroller-triggered action potentials: Since action potentials are triggered by any 5V rising edge signal, it is trivially easy to create action potentials from microcontrollers! You could create some very complex firing patterns, or even "reactive" firing patterns which respond to inputs.

For example, add a TSL2561 I2C digital light sensor and you can have a light-to-frequency action potential generator!

  • Adjusting size and shape of action potentials: Since the waveform is the combination of two waveforms, you can really only adjust the duration (width) or amplitude (height) of each individual waveform, as well as the relative proportion of each used in creating the summation. Widths are adjusted by changing the leak resistor on the base of each transistor, or by adding additional capacitance. Amplitude and the ratio of each signal may be adjusted by changing the ratio of resistors on the output resistor divider.

  • Producing -70 mV (physiological) output: The current output is electirically decoupled (through a series capacitor) so it can float at whatever voltage you bias it to. Therefore, it is easy to "pull" in either direction. Adding a 10k potentiometer to bias the output is an easy way to let you set the voltage. A second potentiometer gating the magnitude of the output signal will let you adjust the height of the output waveform as desired.

  • The 555 could be replaced by an inverted ramp (sawtooth): An inverted ramp / sawtooth pattern which produces rapid 5V rising edges would drive this circuit equally well. A fully analog ramp generator circuit can be realized with 3 transistors: essentially a constant current capacitor charger with a threshold-detecting PNP/NPN discharge component.

  • This action potential is not all-or-nothing: In real life, small excitatory inputs which fail to reach the action potential threshold do not produce an action potential voltage waveform. This circuit uses 5V rising edges to produce action potential waveforms. However, feeding a 1V rising edge would produce an action potential 1/5 the size. This is not a physiological effect. However, it is unlikely (if not impossible) for many digital signal sources (i.e., common microcontrollers) to output anything other than sharp rising edge square waves of fixed voltages, so this is not a concern for my application.

  • Random action potentials: When pondering how to create randomly timed action potentials, the issue of how to generate random numbers arises. This is surprisingly difficult, especially in embedded devices. If a microcontroller is already being used, consider Make's write-up on the subject, and I think personally I would go with a transistor-based avalanche nosie generator to create the randomness.

  • A major limitation is that irregularly spaced action potentials have slightly different amplitudes.I found this out the next day when I created a hardware random number generator (yes, that happened) to cause it to fire regularly, missing approximately half of the action potentials. When this happens, breaks in time result in a larger subsequent action potential. There are several ways to get around this, but it's worth noting that the circuit shown here is best operated around 6 Hz with only continuous regularly-spaced action potentials.

In the video I also demonstrate how to record the output of this circuit using a high-speed (44.1 kHz) 16-bit analog-to-digital converter you already have (the microphone input of your sound card). I won't go into all the details here, but below is the code to read data from a WAV file and plot it as if it were a real neuron. The graph below is an actual recording of the circuit described here using the microphone jack of my sound card.

import numpy as np
import matplotlib.pyplot as plt
Ys = np.memmap("recording.wav", dtype='h', mode='r')[1000:40000]
Ys = np.array(Ys)/max(Ys)*150-70
Xs = np.arange(len(Ys))/44100*1000
plt.figure(figsize=(6,3))
plt.grid(alpha=.5,ls=':')
plt.plot(Xs,Ys)
plt.margins(0,.1)
plt.title("Action Potential Circuit Output")
plt.ylabel("potential (mV)")
plt.xlabel("time (ms)")
plt.tight_layout()
plt.savefig("graph.png")
#plt.show()

Let's make some noise! Just to see what it would look like, I created a circuit to generate slowly drifting random noise. I found this was a non-trivial task to achieve in hardware. Most noise generation circuits create random signals on the RF scale (white noise) which when low-pass filtered rapidly approach zero. I wanted something which would slowly drift up and down on a time scale of seconds. I achieved this by creating 4-bit pseudo-random numbers with a shift register (74HC595) clocked at a relatively slow speed (about 200 Hz) having essentially random values on its input. I used a 74HC14 inverting buffer (with Schmidt trigger inputs) to create the low frequency clock signal (about 200 Hz) and an extremely fast and intentionally unstable square wave (about 30 MHz) which was sampled by the shift register to generate the "random" data. The schematic illustrates these points, but note that I accidentally labeled the 74HC14 as a 74HC240. While also an inverting buffer the 74HC240 will not serve as a good RC oscillator buffer because it does not have Schmidt trigger inputs.

An inverting buffer created a fast and a slow clock to produce 4-bit pseudo-random numbers:

reminder how an inverting buffer can act as an oscillator:

the full circuit realized on the breadboard:

output of the 4-bit pseudo-random number generator:

4-bit output smoothed through a single-stage RC filter:

noise combined with action potential waveforms:

The addition of noise was a success, from an electrical and technical sense. It isn't particularly physiological. Neurons would fire differently based on their resting membrane potential, and the peaks of action potential should all be about the same height regardless of the resting potential. However if one were performing an electrical recording through a patch-clamp pipette in perforated patch configuration (with high resistance between the electrode and the internal of the cell), a sharp microelectrode (with high resistance due to the small size of the tip opening), or were using electrical equipment or physical equipment with amplifier limitations, one could imagine that capacitance in the recording system would overcome the rapid swings in cellular potential and result in "noisy" recordings similar to those pictured above. They're not physiological, but perhaps they're a good electrical model of what it's like trying to measure a physiological voltage in a messy and difficult to control experimental environment.

This project was an interesting exercise in analog land, and is completed sufficiently to allow me to move toward my initial goal: creating advanced action potential detection and measurement circuitry. There are many tweaks which may improve this circuit, but as it is good enough for my needs I am happy to leave it right where it is. If you decide to build a similar circuit (or a vastly different circuit to serve a similar purpose), send me an email! I'd love to see what you came up with.

UPDATE: add a microcontroller

I enhanced this project by creating a microcontroller controlled action potential generator. That article is here: https://www.swharden.com/wp/2017-08-20-microcontroller-action-potential-generator/

Markdown source code last modified on January 18th, 2021
---
title: Action Potential Generator Circuit
date: 2017-08-12 23:04:32
tags: science, circuit, microcontroller, python
---

# Action Potential Generator Circuit

__Few biological cells are as interesting to the electrical engineer as the neuron. Neurons are essentially capacitors (with a dielectric cell membrane separating conductive fluid on each side) with parallel charge pumps, leak currents, and nonlinear voltage-dependent currents. __When massively parallelized, these individual functional electrical units yield complex behavior and underlie consciousness. The study of the electrical properties of neurons ([neurophysiologically](https://en.wikipedia.org/wiki/Neurophysiology), a subset of [electrophysiology](https://en.wikipedia.org/wiki/Electrophysiology)) often involves the development and use of sensitive electrical equipment aimed at studying these small potentials produced by neurons and currents which travel through channels embedded in their membranes. It seems neurophysiology has gained an emerging interest from the hacker community, as evidenced by the success of [Back Yard Brains](https://backyardbrains.com/), projects like the [OpenEEG](http://openeeg.sourceforge.net/doc/hw/), and Hack-A-Day's recent feature [_The Neuron - a Hacker's Perspective_](http://hackaday.com/2017/06/02/the-neuron-a-hackers-perspective/).

While contemplating designs for action potential detection and analysis circuitry, I realized that it would be beneficial to be able to _generate_ action-potential-like waveforms on my workbench. The circuit I came up with to do this is a fully analog (technically mixed signal) action potential generator which produces lifelike action potentials.

![](https://www.youtube.com/embed/MBwQrIVJqfs)

__Cellular Neurophysiology for Electrical Engineers (in 2 sentences):__ Neuron action potentials (self-propagating voltage-triggered depolarizations) in individual neurons are measured in scientific environments using single cell recording tools such as [sharp microelectrodes](http://www.scholarpedia.org/article/Intracellular_recording) and [patch-clamp pipettes](https://en.wikipedia.org/wiki/Patch_clamp). Neurons typically rest around -70mV and when depolarized (typically by external excitatory input) above a threshold they engage in a self-propagating depolarization until they reach approximately +40mV, at which time a self-propagating repolarization occurs (often over-shooting the initial rest potential by several mV), then the cell slowly returns to the rest voltage so after about 50ms the neuron is prepared to fire another action potential. Impassioned budding electrophysiologists may enjoy further reading _[Active Behavior of the Cell Membrane](http://www.bem.fi/book/04/04.htm) _and [_Introduction to Computational Neuroscience_.](http://ecee.colorado.edu/~ecen4831/cnsweb/cns1.html)

<div class="text-center img-border">

![](a.gif)

</div>

__The circuit I describe here produces waveforms which visually mimic action potentials__ rather than serve to replicate the exact conductances real neurons employ to exhibit their complex behavior. It is worth noting that numerous scientists and engineers have designed more physiological electrical representations of neuronal circuitry using discrete components. In fact, the [_Hodgkin-Huxley model_](https://en.wikipedia.org/wiki/Hodgkin%E2%80%93Huxley_model) of the initiation and propagation of action potentials earned Alan Hodgkin and Andrew Huxley the Nobel Prize in Physiology and Medicine in 1936. Some resources on the internet describe how to design lifelike action potential generating circuits by mimicking the endogenous ionic conductances which underlie them, notably _[Analog and Digital Hardware Neural Models](https://people.ece.cornell.edu/land/PROJECTS/NeuralModels/)_, [_Active Cell Model_](https://courses.cit.cornell.edu/bionb442/labs/f2007/lab6.html), and [_Neuromorphic Silicon Neuron Circuits_](http://journal.frontiersin.org/article/10.3389/fnins.2011.00073/full). My goal for this project is to create waveforms which _resemble_ action potentials, rather than waveforms which truly _model_ them. I suspect it is highly unlikely I will earn a Nobel Prize for the work presented here.

<div class="text-center img-border">

[![](circuit_hand_thumb.jpg)](circuit_hand.jpg)

</div>

__The analog action potential simulator circuit I came up with creates a continuous series action potentials.__ This is achieved using a [555 timer](https://en.wikipedia.org/wiki/555_timer_IC) (specifically the [NE555](http://www.ti.com/lit/ds/slfs022i/slfs022i.pdf)) in an astable configuration to provide continuous square waves (about 6 Hz at about 50% duty). The rising edge of each square wave is isolated with a diode and used to charge a capacitor*. While the charge on the capacitor is above a certain voltage, an [NPN transistor](https://en.wikipedia.org/wiki/Bipolar_junction_transistor) (the [2N3904](https://www.onsemi.com/pub/Collateral/2N3903-D.PDF)) allows current to flow, amplifying this transient input current. The capacitor* discharges predictably ([as an RC circuit](https://en.wikipedia.org/wiki/RC_circuit)) through a leak resistor. A large value leak resistor slows the discharge and allows that signal's transistor to flow current for a longer duration. By having two signals (fast and slow) using RC circuits with different resistances (smaller and larger), the transistors are on for different durations (shorter and longer). By making the short pulse positive (using the NPN in [common collector configuration](https://en.wikipedia.org/wiki/Common_collector)) and the longer pulse negative (using the NPN in [common emitter configuration](https://en.wikipedia.org/wiki/Common_emitter)), a [resistor voltage divider](https://en.wikipedia.org/wiki/Voltage_divider) can be designed to scale and combine these signals into an output waveform a few hundred mV in size with a 5V power supply. Pictured below is the output of this circuit realized on a breadboard. The blue trace is the output of the 555 timer.

<div class="text-center img-border">

[![](scope_b_thumb.jpg)](scope_b.jpg)
[![](scope_a_thumb.jpg)](scope_a.jpg)

</div>

Between the capacitance of the rectification diode, input capacitance of the transistor, and stray parasitic capacitance from the physical construction of my wires and the rails on my breadboard, there is sufficient capacitance to accumulate charge which can be modified by changing the value of the leak resistor.


<div class="text-center img-border">

[![](breadboard_thumb.jpg)](breadboard.jpg)

</div>

__This circuit produces similar output when simulated.__ I'm using [LTspice](http://www.linear.com/designtools/software/) (free) to simulate this circuit. The circuit shown is identical to the one hand-drawn and built on the breadboard, with the exception that an additional 0.1 µF capacitor to ground is used on the output to smooth the signal. On the breadboard this capacitance-based low-pass filtering already exists due to the capacitive nature of the components, wires, and rails.

<div class="text-center img-border">

[![](circuit_thumb.jpg)](circuit.png)
[![](sim_trace2_thumb.jpg)](sim_trace2.png)
[![](sim_trace_thumb.jpg)](sim_trace.png)

</div>

__A few improvements naturally come to mind__ when considering this completed, functional circuit:

* **Action potential frequency:** The resistor/capacitor network on the 555 timer determines the rate of square pulses which trigger action potentials. Changing these values will cause a different rate of action potential firing, but I haven't attempted to push it too fast and suspect the result would not be stable is the capacitors are not given time to fully discharge before re-initiating subsequent action potentials.

* **Microcontroller-triggered action potentials:** Since action potentials are triggered by any 5V rising edge signal, it is trivially easy to create action potentials from microcontrollers! You could create some very complex firing patterns, or even "reactive" firing patterns which respond to inputs. 

For example, add a <a href="https://www.adafruit.com/product/439">TSL2561 I2C digital light sensor</a> and you can have a light-to-frequency action potential generator!

* **Adjusting size and shape of action potentials:** Since the waveform is the combination of two waveforms, you can really only adjust the duration (width) or amplitude (height) of each individual waveform, as well as the relative proportion of each used in creating the summation. Widths are adjusted by changing the leak resistor on the base of each transistor, or by adding additional capacitance. Amplitude and the ratio of each signal may be adjusted by changing the ratio of resistors on the output resistor divider.

* **Producing -70 mV (physiological) output:** The current output is electirically decoupled (through a series capacitor) so it can float at whatever voltage you bias it to. Therefore, it is easy to "pull" in either direction. Adding a 10k potentiometer to bias the output is an easy way to let you set the voltage. A second potentiometer gating the magnitude of the output signal will let you adjust the height of the output waveform as desired.

* **The 555 could be replaced by an inverted ramp (sawtooth):** An inverted ramp / sawtooth pattern which produces rapid 5V rising edges would drive this circuit equally well. A <a href="http://www.learningaboutelectronics.com/Articles/Ramp-generator-circuit-with-transistors.php">fully analog ramp generator circuit</a> can be realized with 3 transistors: essentially a constant current capacitor charger with a threshold-detecting PNP/NPN discharge component.

* **This action potential is not all-or-nothing:** In real life, small excitatory inputs which fail to reach the action potential threshold do not produce an action potential voltage waveform. This circuit uses 5V rising edges to produce action potential waveforms. However, feeding a 1V rising edge would produce an action potential 1/5 the size. This is not a physiological effect. However, it is unlikely (if not impossible) for many digital signal sources (i.e., common microcontrollers) to output anything other than sharp rising edge square waves of fixed voltages, so this is not a concern for my application.

* **Random action potentials:** When pondering how to create randomly timed action potentials, the issue of how to generate random numbers arises. This is surprisingly difficult, especially in embedded devices. If a microcontroller is already being used, consider <a href="http://makezine.com/projects/really-really-random-number-generator/">Make's write-up on the subject</a>, and I think personally I would go with a <a href="http://holdenc.altervista.org/avalanche/">transistor-based avalanche nosie generator</a> to create the randomness.

* **A major limitation is that irregularly spaced action potentials have slightly different amplitudes.**I found this out the next day when I created a hardware random number generator (yes, that happened) to cause it to fire regularly, missing approximately half of the action potentials. When this happens, breaks in time result in a larger subsequent action potential. There are several ways to get around this, but it's worth noting that the circuit shown here is best operated around 6 Hz with only continuous regularly-spaced action potentials.

<div class="text-center img-border img-medium">

[![](File_000-2_thumb.jpg)](File_000-2.jpeg)

</div>

__In the video I also demonstrate how to record the output of this circuit using a high-speed (44.1 kHz) 16-bit analog-to-digital converter you already have (the microphone input of your sound card).__ I won't go into all the details here, but below is the code to read data from a WAV file and plot it as if it were a real neuron. The graph below is an _actual recording_ of the circuit described here using the microphone jack of my sound card.

```python
import numpy as np
import matplotlib.pyplot as plt
Ys = np.memmap("recording.wav", dtype='h', mode='r')[1000:40000]
Ys = np.array(Ys)/max(Ys)*150-70
Xs = np.arange(len(Ys))/44100*1000
plt.figure(figsize=(6,3))
plt.grid(alpha=.5,ls=':')
plt.plot(Xs,Ys)
plt.margins(0,.1)
plt.title("Action Potential Circuit Output")
plt.ylabel("potential (mV)")
plt.xlabel("time (ms)")
plt.tight_layout()
plt.savefig("graph.png")
#plt.show()

```

<div class="text-center img-medium">

[![](graph_thumb.jpg)](graph.png)

</div>

__Let's make some noise!__ Just to see what it would look like, I created a circuit to generate slowly drifting random noise. I found this was a non-trivial task to achieve in hardware. Most noise generation circuits create random signals on the RF scale (white noise) which when low-pass filtered rapidly approach zero. I wanted something which would slowly drift up and down on a time scale of seconds. I achieved this by creating 4-bit pseudo-random numbers with a shift register ([74HC595](https://www.sparkfun.com/datasheets/IC/SN74HC595.pdf)) clocked at a relatively slow speed (about 200 Hz) having essentially random values on its input. I used a [74HC14](https://assets.nexperia.com/documents/data-sheet/74HC_HCT14.pdf) inverting buffer (with Schmidt trigger inputs) to create the low frequency clock signal (about 200 Hz) and an extremely fast and intentionally unstable square wave (about 30 MHz) which was sampled by the shift register to generate the "random" data. The schematic illustrates these points, but note that I accidentally labeled the 74HC14 as a 74HC240. While also an inverting buffer the [74HC240](https://assets.nexperia.com/documents/data-sheet/74HC_HCT240.pdf) will not serve as a good RC oscillator buffer because it does not have Schmidt trigger inputs.

An inverting buffer created a fast and a slow clock to produce 4-bit pseudo-random numbers:
<div class="text-center img-border img-medium">

[![](File_000-5_thumb.jpg)](File_000-5.jpeg)

</div>

reminder how an inverting buffer can act as an oscillator:
<div class="text-center">

[![](schmitt-trigger-osc_thumb.jpg)](schmitt-trigger-osc.png)

</div>

the full circuit realized on the breadboard:
<div class="text-center img-border">

[![](File_004_thumb.jpg)](File_004.jpeg)

</div>

output of the 4-bit pseudo-random number generator:
<div class="text-center img-border">

[![](File_001_thumb.jpg)](File_001.jpeg)

</div>

4-bit output smoothed through a single-stage RC filter:
<div class="text-center img-border">

[![](File_002_thumb.jpg)](File_002.jpeg)

</div>

noise combined with action potential waveforms:
<div class="text-center img-border">

[![](File_003_thumb.jpg)](File_003.jpeg)

</div>

__The addition of noise was a success, from an electrical and technical sense.__ It isn't particularly physiological. Neurons would fire differently based on their resting membrane potential, and the peaks of action potential should all be about the same height regardless of the resting potential. _However_ if one were performing an electrical recording through a patch-clamp pipette in perforated patch configuration (with high resistance between the electrode and the internal of the cell), a sharp microelectrode (with high resistance due to the small size of the tip opening), or were using electrical equipment or physical equipment with amplifier limitations, one could imagine that capacitance in the recording system would overcome the rapid swings in cellular potential and result in "noisy" recordings similar to those pictured above. They're not physiological, but perhaps they're a good electrical model of what it's like trying to measure a physiological voltage in a messy and difficult to control experimental environment.

__This project was an interesting exercise in analog land,__ and is completed sufficiently to allow me to move toward my initial goal: creating advanced action potential detection and measurement circuitry. There are many tweaks which may improve this circuit, but as it is good enough for my needs I am happy to leave it right where it is. If you decide to build a similar circuit (or a vastly different circuit to serve a similar purpose), send me an email! I'd love to see what you came up with.

## UPDATE: add a microcontroller

I enhanced this project by creating a [microcontroller controlled action potential generator](https://www.swharden.com/wp/2017-08-20-microcontroller-action-potential-generator/). That article is here: <https://www.swharden.com/wp/2017-08-20-microcontroller-action-potential-generator/>
September 5th, 2016

VHF Frequency Counter with PC Interface

Projects I build often involve frequency synthesis, and one of the most useful tools to have around is a good frequency counter. I love the idea of being able to access / log / analyze frequency readings on my computer. Commercial frequency counters can be large, expensive, and their calibration is a chicken-and-egg problem (you need a calibrated frequency counter to calibrate a frequency reference you use to calibrate a frequency counter!). For about the cost of a latte I made a surprisingly good frequency frequency counter (which directly counts >100 MHz without dividing-down the input signal) by blending a SN74LV8154 dual 16-bit counter (which can double as a 32-bit counter, $1.04 on mouser) and an ATMega328 microcontroller ($3.37 on Mouser).

Although these two chips are all you need to count something, the accuracy of your counts depend on your gate. If you can generate a signal of 1 pulse per second (1PPS), you can count anything, but your accuracy depends on the accuracy of your 1PPS signal. To eliminate the need for calibration (and to provide the 1PPS signal with the accuracy of an atomic clock) I'm utilizing the 1PPS signal originating from a GPS unit which I already had distributed throughout my shack (using a 74HC240 IC as a line driver). If you don't have a GPS unit, consider getting one! I'm using a NEO-6M module ($17.66 on Amazon) to generate the 1PPS gate, and if you include its cost we're up to $22.07. Also, all of the code for this project (schematics, C that runs on the microcontroller, and a Python to interact with the serial port) is shared on GitHub! You may be wondering, "why do GPS units have incredibly accurate 1PPS signals?" It's a good question, but a subject for another day. For now, trust me when I say they're fantastically accurate (but slightly less precise due to jitter) if you're interested in learning more read up on GPS timing.

This is the general idea behind how this frequency counter works. It's so simple! It's entirely digital, and needs very few passive components. sn74lv8154 is configured in 32-bit mode (by chaining together its two 16-bit counters, see the datasheet for details) and acts as the front-end directly taking in the measured frequency. This chip is "rare" in the sense I find very few internet projects using it, and they're not available on ebay. However they're cheap and plentiful on mouser, so I highly encourage others to look into using it! The datasheet isn’t very clear about its maximum frequency, but in my own tests I was able to measure in excess of 100 MHz from a breadboarded circuit! This utilized two cascaded ICS501 PLL frequency multiplier ICs to multiply a signal I had available (the 11.0592 MHz crystal the MCU was running from) by ten, yielding 110 MHz, which it was able to measure (screenshot is down on the page).

The 1PPS gate signal is generated from an inexpensive GPS module available on Amazon. I've hinted at the construction of this device before and made a post about how to send output signals like the 1PPS signal generated here throughout your shack via coax using a line driver, so I won't re-hash all of those details here. I will say that this module has only VCC, GND, and TX/RX pins, so to get access to the 1PPS signal you have to desolder the SMT LED and solder a wire to its pad. It requires a bit of finesse. If you look closely, you can see it in this picture (purple wire).

I first built this device on a breadboard, and despite the rats nest of wires it worked great! Look closely and you can see the ICS501 frequency multiplier ICs I wrote about before. In this case it's measuring the 10x multiplied crystal frequency clocking the MCU (11 MHz -> 110 MHz) and reporting these readings every 1 second to the computer via a serial interface.

Frequency measurements of the VHF signal are reported once per second. Measurements are transmitted through a USB serial adapter, and captured by a Python script. Note that I'm calling this signal VHF because it's >30 MHz. I am unsure if this device will work up to 300 MHz (the border between VHF and UHF), but I look forward to testing that out! Each line contains two numbers: the actual count of the counter (which is configured to simply count continuously and overflow at 2^32=4,294,967,296), and the gated count (calculated by the microcontroller) which is the actual frequency in Hz.

This screenshot shows that my ~11.05 MHz crystal is actually running at 11,061,669.4 Hz. See how I capture the 0.4 Hz unit at the end? That level of precision is the advantage of using this VHF-capable counter in conjunction with a 10x frequency multiplier!

Once I confirmed everything was working, I built this device in a nice enclosure. I definitely splurge every few months and buy extruded split body aluminum enclosures in bulk (ebay), but they're great to have on hand because they make projects look so nice. I added some rubber feet (cabinet bumpers from Walmart), drilled holes for all the connectors with a continuous step drill bit, made a square hole for the serial port using a nibbler, and the rest is pretty self-evident. Labels are made with a DYMO LetraTag (Target) and clear labels (Target, Amazon) using a style inspired by PA2OHH. I tend to build one-off projects like this dead-bug / Manhattan style.

I super-glued a female header to the aluminum frame to make in-circuit serial programming (ICSP) easy. I can't believe I never thought to do this before! Programming (and reprogramming) was so convenient. I'm going to start doing this with every enclosed project I build from now on. FYI I'm using a USBTiny ISP ($10.99, Amazon) to do the programming (no longer the BusPirate, it's too slow) like I describe here for 64-bit Windows 7 (although I'm now using Windows 10 and it works the same).

The front of the device has LEDs indicating power, serial transmission, and gating. Without a 1PPS gate, the device is set to send a count (of 0) every 5 seconds. In this case, the TX light will illuminate. If a gate is detected, the TX and GATE LEDs will illuminate simultaneously. In reality I just drilled 3 holes when I really needed two, so I had to make-up a function for the third LED (d'oh!)

The back of the device has serial output, frequency input, gate input, and power. Inside is a LM7805 voltage regulator, and careful attention was paid to decoupling and keeping ripple out of the power supply (mostly so our gate input wouldn't be affected). I'm starting to get in the habit of labeling all serial output ports with the level (TTL vs CMOS, which makes a HUGE difference as MAX232 level converter may be needed, or a USB serial adapter which is capable of reading TTL voltages), as well as the baud rate (119200), byte size (8), parity (N), and stop bit (1). _I just realized there's a typo! The label should read 8N1. I don't feel like fixing it, so I'll use a marker to turn the 2 into an 8. _I guess I'm only human after all.

I should have tried connecting all these things before I drilled the holes. I got so lucky that everything fit, with about 2mm to spare between those BNC jacks. Phew!

This is an easy test frequency source. I have a dozen canned oscillators of various frequencies. This is actually actually a voltage controlled oscillator (VCO) with adjustment pin (not connected), and it won't be exactly 50 MHz without adjustment. It's close enough to test with though! As this is >30 MHz, we can call the signal VHF.

You can see on the screen it's having no trouble reading the ~50 MHz frequency. You'll notice I'm using RealTerm (with a good write-up on sparkfun) which is my go-to terminal program instead of HyperTerminal (which really needs to go away forever). In reviewing this photo, I'm appreciating how much unpopulated room I have on the main board. I'm half tempted to build-in a frequency multiplier circuit, and place it under control of the microcontroller such that if an input frequency from 1-20MHz is received, it will engage the 10x multiplier. That's a mod for another day though! Actually, since those chips are SMT, if I really wanted to do this I would make this whole thing a really small SMT PCB and greatly simplify construction. That sounds like a project for another day though...None

Before closing it up I added some extra ripple protection on the primary counter chip. There's a 560 uH series inductor with the power supply, followed by a 100 nF capacitor parallel with ground. I also added ferrite beads to the MCU power line and gate input line. I appreciate how the beads are unsecured and that this is a potential weakness in the construction of this device (they're heavy, so consider what would happen if you shook this enclosure). However, anything that would yank-away cables in the event of shaking the device would probably also break half the other stuff in this thing, so I think it's on par with the less-than-rugged construction used for all the other components in this device. It will live a peaceful life on my shelf. I am not concerned.

This is the final device counting frequency and continuously outputting the result to my computer. In the background you can see the 12V power supply (yellow) indicating it is drawing only 20 mA, and also the GPS unit is in a separate enclosure on the bottom right. Click here to peek inside the GPS 1PPS enclosure.

I'm already loving this new frequency counter! It's small, light, and nicely enclosed (meaning it's safe from me screwing with it too much!). I think this will prove to be a valuable piece of test equipment in my shack for years to come. I hope this build log encourages other people to consider building their own equipment. I learned a lot from this build, saved a lot of money not buying something commercial, had a great time making this device, and I have a beautiful piece of custom test equipment that does exactly what I want.

Source Code

Microcontroller code (AVR-GCC), schematics, and a Python script to interface with the serial port are all available on this project's GitHub page

Afterthought: Using without GPS

One of the great advantages of this project is that it uses GPS for an extremely accurate 1 PPS signal, but what options exist to adapt this project to not rely on GPS? The GPS unit is expensive (though still <$20) and GPS lock is not always feasible (underground, in a Faraday cage, etc). Barring fancy things like dividing-down rubidium frequency standards or oven controlled oscillators, consider having your microcontroller handle the gating using either interrupts and timers precisely configured to count seconds. Since this project uses a serial port with a 11.0592 MHz crystal, your 1PPS stability will depend on the stability of your oscillator (which is pretty good!). Perhaps more elegantly you could use a 32.768 kHz crystal oscillator to create a 1 PPS signal. This frequency can be divided by 2 over and over to yield 1 Hz perfectly. This is what most modern wristwatches do. Many AVRs have a separate oscillator which can accomodate a 32 kHz crystal and throw interrupts every 1 second without messing with the system clock. Alternatively, the 74GC4060 (a 14 stage ripple counter) can divide 32k into 1 Hz and even can be arranged as an oscillator (check the datasheet). It would be possible to have both options enabled (local clock and GPS) and only engage the local clock if the GPS signal is absent. If anyone likes the idea of this simple VHF frequency counter with PC interface but doesn't want to bother with the GPS, there are plenty of options to have something almost as accurate. That really would cut the cost of the final device down too, keeping it under the $5 mark.

Update: Integrating Counter Serial Output with GPS Serial Output

The NEO-M8 GPS module is capable of outputting serial data at 9600 baud and continuously dumps NEMA formatted GPS data. While this isn't really useful for location information (whose frequency counter requires knowing latitude and longitude?) it's great for tracking things like signal strength, fix quality, and number of satellites. After using this system to automatically log frequency of my frequency reference, I realized that sometimes I'd get 1-2 hours of really odd data (off by kHz, not just a few Hz). Power cycling the GPS receiver fixes the problem, so my guess it that it's a satellite issue. If I combine the GPS RX and counter in 1 box, I could detect this automatically and have the microcontroller power cycle the GPS receiver (or at the least illuminate a red error LED). I don't feel like running 2 USB serial adapters continuously. I don't feel like programming my AVR to listen to the output from the GPS device (although that's probably the correct way to do things). Instead I had a simpler idea that worked really well, allowing me to simultaneously log serial data from my GPS unit and microcontroller (frequency counter) using 1 USB serial adapter.

The first thing I did was open up the frequency counter and reconnect my microcontroller programmer. This is exactly what I promised myself I wouldn't do, and why I have a nice enclosure in the first place! Scott, stop fidgeting with things! The last time I screwed this enclosure together I considered adding super glue to the screw threads to make sure I didn't open it again. I'll keep my modifications brief! For now, this is a test of a concept. When it's done, I'll revert the circuitry to how it was and close it up again. I'll take what I learn and build it into future projects.

I peeked at the serial signals of both the frequency counter (yellow) and the GPS unit output (blue). To my delight, there was enough dead space that I thought I could stick both in the same signal. After a code modification, I was able to tighten it up a lot, so the frequency counter never conflicts with the GPS unit by sending data at the same time.

I had to slow the baud rate to 9600, but I programmed it to send fewer characters. This leaves an easy ~50ms padding between my frequency counter signal and the GPS signal. Time to mix the two! This takes a little thought, as I can't just connect the two wires together. Serial protocol means the lines are usually high, and only pulled down when data is being sent. I had to implement an active circuit.

Using a few components, I built an AND gate to combine signals from the two serial lines. For some reason it took some thought before I realized an AND gate was what I needed here, but it makes sense. The output is high (meaning no serial signal) only when both inputs are high (no serial signals on the input). When either signal drops low, the output drops low. This is perfect. My first thought was that I'd need a NOR gate, but an inverted AND gate is a NOR gate.

Here's my quick and dirty implementation. A reminder again is that this will be removed after this test. For now, it's good enough.

After connecting the GPS serial output and frequency counter serial output to the AND gate (which outputs to the computer), I instantly got the result I wanted!

RealTerm shows that both inputs are being received. It's a mess though. If you want to know what everything is, read up on NEMA formatted GPS data.

I whipped-up a python program to parse, display, and log key information. This display updates every 1 second. The bottom line is what is appended to the log file on ever read. It's clunky, but again this is just for testing and debugging. I am eager to let this run for as long as I can (days?) so I can track how changes in satellite signal / number / fix quality influence measured frequency.

Markdown source code last modified on January 18th, 2021
---
title: VHF Frequency Counter with PC Interface
date: 2016-09-05 17:34:44
tags: amateur radio, circuit, python, old
---

# VHF Frequency Counter with PC Interface

**Projects I build often involve frequency synthesis, and one of the most useful tools to have around is a good frequency counter.** I love the idea of being able to access / log / analyze frequency readings on my computer. Commercial frequency counters can be large, expensive, and their calibration is a chicken-and-egg problem (you need a calibrated frequency counter to calibrate a frequency reference you use to calibrate a frequency counter!). **For about the cost of a latte I made a surprisingly good frequency frequency counter (which directly counts >100 MHz without dividing-down the input signal)** by blending a SN74LV8154 dual 16-bit counter (which can double as a 32-bit counter, [$1.04 on mouser](http://www.mouser.com/Search/Refine.aspx?Keyword=sn74lv8154&Ns=Pricing%7c0&FS=True)) and an ATMega328 microcontroller ([$3.37 on Mouser](http://www.mouser.com/Semiconductors/Integrated-Circuits-ICs/Embedded-Processors-Controllers/Microcontrollers-MCU/8-bit-Microcontrollers-MCU/_/N-a86lo?P=1z0y33r&Keyword=atmega328p&Ns=Pricing%7c0&FS=True)). 

**Although these two chips are all you need to count something, the accuracy of your counts depend on your gate.** If you can generate a signal of 1 pulse per second (1PPS), you can count anything, but your accuracy depends on the accuracy of your 1PPS signal. To eliminate the need for calibration (and to provide the 1PPS signal with the accuracy of an atomic clock) I'm utilizing the 1PPS signal originating from a GPS unit which I already had distributed throughout my shack ([using a 74HC240 IC as a line driver](https://www.swharden.com/wp/2016-08-20-breadboard-line-driver-module/)). If you don't have a GPS unit, consider getting one! I'm using a NEO-6M module ([$17.66 on Amazon](https://www.amazon.com/Andoer-AeroQuad-Multirotor-Quadcopter-Aircraft/dp/B00RCP9MLY)) to generate the 1PPS gate, and if you include its cost we're up to $22.07. Also,** all of the code for this project (schematics, C that runs on the microcontroller, and a Python to interact with the serial port) is [shared on GitHub](https://github.com/swharden/AVR-projects/tree/master/ATMega328%202016-09-04%20SN74LV8154)!** You may be wondering, "why do GPS units have incredibly accurate 1PPS signals?" It's a good question, but a subject for another day. For now, trust me when I say they're fantastically accurate (but slightly less precise due to jitter) if you're interested in learning more read up on [GPS timing](https://www.u-blox.com/sites/default/files/products/documents/Timing_AppNote_%28GPS.G6-X-11007%29.pdf).

<div class="text-center">

[![](pc-frequency-counter-schem_thumb.jpg)](pc-frequency-counter-schem.png)

</div>

**This is the general idea behind how this frequency counter works.** It's so simple! It's entirely digital, and needs very few passive components. sn74lv8154 is configured in 32-bit mode (by chaining together its two 16-bit counters, [see the datasheet for details](http://www.ti.com/lit/ds/symlink/sn74lv8154.pdf)) and acts as the front-end directly taking in the measured frequency. This chip is "rare" in the sense I find very few internet projects using it, and they're not available on ebay. However they're cheap and plentiful on mouser, so I highly encourage others to look into using it! The datasheet isn’t very clear about its maximum frequency, but in my own tests I was able to measure in excess of 100 MHz from a breadboarded circuit! This utilized [two cascaded ICS501 PLL frequency multiplier ICs to multiply a signal](https://www.swharden.com/wp/2016-08-31-ics501-simple-frequency-multiplier/) I had available (the 11.0592 MHz crystal the MCU was running from) by ten, yielding 110 MHz, which it was able to measure (screenshot is down on the page).

<div class="text-center">

[![](neo-60-gps-1pps_thumb.jpg)](neo-60-gps-1pps.jpg)

</div>

**The 1PPS gate signal is generated from an inexpensive GPS module [available on Amazon](https://www.amazon.com/Andoer-AeroQuad-Multirotor-Quadcopter-Aircraft/dp/B00RCP9MLY).** I've hinted at the construction of this device before and [made a post](https://www.swharden.com/wp/2016-08-20-breadboard-line-driver-module/) about how to send output signals like the 1PPS signal generated here throughout your shack via coax using a line driver, so I won't re-hash all of those details here. I will say that this module has only VCC, GND, and TX/RX pins, so to get access to the 1PPS signal you have to desolder the SMT LED and solder a wire to its pad. It requires a bit of finesse. If you look closely, [you can see it in this picture](https://www.swharden.com/wp/2016-08-20-breadboard-line-driver-module/#jp-carousel-5918) (purple wire).

<div class="text-center img-border">

[![](IMG_8207_thumb.jpg)](IMG_8207.jpg)

</div>

**I first built this device on a breadboard, and despite the rats nest of wires it worked great!** Look closely and you can see the ICS501 frequency multiplier ICs [I wrote about before](https://www.swharden.com/wp/2016-08-31-ics501-simple-frequency-multiplier/). In this case it's measuring the 10x multiplied crystal frequency clocking the MCU (11 MHz -> 110 MHz) and reporting these readings every 1 second to the computer via a serial interface.

<div class="text-center img-border">

[![](ss_thumb.jpg)](ss.png)

</div>

**Frequency measurements of the VHF signal are reported once per second. Measurements are transmitted through a USB serial adapter, and captured by a Python script.** Note that I'm calling this signal [VHF](https://en.wikipedia.org/wiki/Very_high_frequency) because it's >30 MHz. I am unsure if this device will work up to 300 MHz (the border between VHF and UHF), but I look forward to testing that out! Each line contains two numbers: the actual count of the counter (which is configured to simply count continuously and overflow at 2^32=4,294,967,296), and the gated count (calculated by the microcontroller) which is the actual frequency in Hz.

>  This screenshot shows that my ~11.05 MHz crystal is actually running at 11,061,669.4 Hz. See how I capture the 0.4 Hz unit at the end? That level of precision is the advantage of using this VHF-capable counter in conjunction with a 10x frequency multiplier!

**Once I confirmed everything was working, I built this device in a nice enclosure.** I definitely splurge every few months and buy extruded split body aluminum enclosures in bulk ([ebay](http://www.ebay.com/sch/i.html?_nkw=extruded+split+body+aluminum+enclosure)), but they're great to have on hand because they make projects look so nice. I added some rubber feet (cabinet bumpers from Walmart), drilled holes for all the connectors with a continuous step drill bit, made a square hole for the serial port using a [nibbler](https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Dtools&field-keywords=Nickel+Plated+Nibbling+Tool), and the rest is pretty self-evident. Labels are made with a DYMO LetraTag (Target) and clear labels (Target, Amazon) using a style [inspired by PA2OHH](http://www.qsl.net/pa2ohh/tlabels.htm). I tend to build one-off projects like this dead-bug / Manhattan style.

<div class="text-center img-border">

[![](IMG_8277_thumb.jpg)](IMG_8277.jpg)
[![](IMG_8282_thumb.jpg)](IMG_8282.jpg)

</div>

**I super-glued a female header to the aluminum frame to make in-circuit serial programming (ICSP) easy.** I can't believe I never thought to do this before! Programming (and reprogramming) was so convenient. I'm going to start doing this with every enclosed project I build from now on. FYI I'm using a USBTiny ISP ([$10.99, Amazon](https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=usbtiny+isp)) to do the programming (no longer the BusPirate, it's too slow) [like I describe here for 64-bit Windows 7](https://www.swharden.com/wp/2013-05-07-avr-programming-in-64-bit-windows-7/) (although I'm now using Windows 10 and it works the same).

<div class="text-center img-border">

[![](IMG_8330_thumb.jpg)](IMG_8330.jpg)

</div>

**The front of the device** has LEDs indicating power, serial transmission, and gating. Without a 1PPS gate, the device is set to send a count (of 0) every 5 seconds. In this case, the TX light will illuminate. If a gate is detected, the TX and GATE LEDs will illuminate simultaneously. In reality I just drilled 3 holes when I really needed two, so I had to make-up a function for the third LED (d'oh!)

<div class="text-center img-border">

[![](IMG_8286_thumb.jpg)](IMG_8286.jpg)

</div>

**The back of the device** has serial output, frequency input, gate input, and power. Inside is a LM7805 voltage regulator, and careful attention was paid to decoupling and keeping ripple out of the power supply (mostly so our gate input wouldn't be affected). I'm starting to get in the habit of labeling all serial output ports with the level (TTL vs CMOS, which makes a HUGE difference as MAX232 level converter may be needed, or a USB serial adapter which is capable of reading TTL voltages), as well as the baud rate (119200), byte size (8), parity (N), and stop bit (1). _I just realized there's a typo! The label should read 8N1. I don't feel like fixing it, so I'll use a marker to turn the 2 into an 8. _I guess I'm only human after all.

<div class="text-center img-border">

[![](IMG_8297_thumb.jpg)](IMG_8297.jpg)

</div>

**I should have tried connecting all these things before I drilled the holes.** I got _so_ lucky that everything fit, with about 2mm to spare between those BNC jacks. Phew!

<div class="text-center img-border">

[![](IMG_8316_thumb.jpg)](IMG_8316.jpg)

</div>

**This is an easy test frequency source.** I have a dozen [canned oscillators](https://en.wikipedia.org/wiki/Crystal_oscillator) of various frequencies. This is actually actually a voltage controlled oscillator ([VCO](https://en.wikipedia.org/wiki/Voltage-controlled_oscillator)) with adjustment pin (not connected), and it won't be exactly 50 MHz without adjustment. It's close enough to test with though! As this is >30 MHz, we can call the signal [VHF](https://en.wikipedia.org/wiki/Very_high_frequency).

<div class="text-center img-border">

[![](IMG_8318_thumb.jpg)](IMG_8318.jpg)

</div>

**You can see on the screen it's having no trouble reading the ~50 MHz frequency.** You'll notice I'm using [RealTerm](http://realterm.sourceforge.net/) (with a [good write-up on sparkfun](https://learn.sparkfun.com/tutorials/terminal-basics/real-term-windows)) which is my go-to terminal program instead of HyperTerminal (which really needs to go away forever). In reviewing this photo, I'm appreciating how much unpopulated room I have on the main board. I'm half tempted to build-in a frequency multiplier circuit, and place it under control of the microcontroller such that if an input frequency from 1-20MHz is received, it will engage the 10x multiplier. That's a mod for another day though! Actually, since those chips are SMT, if I really wanted to do this I would make this whole thing a really small SMT PCB and greatly simplify construction. That sounds like a project for another day though...[None](https://www.swharden.com/wp/wp-content/uploads/2016/09/IMG_8316.jpg)

<div class="text-center img-border">

[![](IMG_8335_thumb.jpg)](IMG_8335.jpg)

</div>

**Before closing it up I added some extra ripple protection on the primary counter chip.** There's a 560 uH series inductor with the power supply, followed by a 100 nF capacitor parallel with ground. I also added [ferrite beads](https://en.wikipedia.org/wiki/Ferrite_bead) to the MCU power line and gate input line. I appreciate how the beads are unsecured and that this is a potential weakness in the construction of this device (they're heavy, so consider what would happen if you shook this enclosure). However, anything that would yank-away cables in the event of shaking the device would probably also break half the other stuff in this thing, so I think it's on par with the less-than-rugged construction used for all the other components in this device. It will live a peaceful life on my shelf. I am not concerned.

<div class="text-center img-border">

[![](IMG_8335_thumb.jpg)](IMG_8335.jpg)

</div>

**This is the final device counting frequency and continuously outputting the result to my computer.** In the background you can see the 12V power supply (yellow) indicating it is drawing only 20 mA, and also the GPS unit is in a separate enclosure on the bottom right. [Click here to peek inside](https://www.swharden.com/wp/2016-08-20-breadboard-line-driver-module/#jp-carousel-5918) the GPS 1PPS enclosure.

<div class="text-center img-border">

[![](IMG_8344_thumb.jpg)](IMG_8344.jpg)

</div>

**I'm already loving this new frequency counter!** It's small, light, and nicely enclosed (meaning it's safe from me screwing with it too much!). I think this will prove to be a valuable piece of test equipment in my shack for years to come. I hope this build log encourages other people to consider building their own equipment. I learned a lot from this build, saved a lot of money not buying something commercial, had a great time making this device, and I have a beautiful piece of custom test equipment that does _exactly_ what I want.

## Source Code

Microcontroller code (AVR-GCC), schematics, and a Python script to interface with the serial port are all available on [this project's GitHub page](https://github.com/swharden/AVR-projects/blob/master/ATMega328%202016-09-04%20SN74LV8154/main.c)

## Afterthought: Using without GPS

One of the great advantages of this project is that it uses GPS for an extremely accurate 1 PPS signal, but what options exist to adapt this project to not rely on GPS? The GPS unit is expensive (though still <$20) and GPS lock is not always feasible (underground, in a Faraday cage, etc). Barring fancy things like dividing-down rubidium frequency standards or oven controlled oscillators, consider having your microcontroller handle the gating using either [interrupts and timers precisely configured to count seconds](https://www.swharden.com/wp/2011-06-19-using-timers-and-counters-to-clock-seconds/). Since this project uses a serial port with a 11.0592 MHz crystal, your 1PPS stability will depend on the stability of your oscillator (which is pretty good!). Perhaps more elegantly you could use a 32.768 kHz crystal oscillator to create a 1 PPS signal. This frequency can be divided by 2 over and over to yield 1 Hz perfectly. This is what most modern wristwatches do. Many AVRs have a separate oscillator which can accomodate a 32 kHz crystal and throw interrupts every 1 second without messing with the system clock. Alternatively, the [74GC4060](http://www.nxp.com/documents/data_sheet/74HC_HCT4060_Q100.pdf) (a 14 stage ripple counter) can divide 32k into 1 Hz and even can be arranged as an oscillator (check the datasheet). It would be possible to have both options enabled (local clock and GPS) and only engage the local clock if the GPS signal is absent. If anyone likes the idea of this simple VHF frequency counter with PC interface but doesn't want to bother with the GPS, there are plenty of options to have something _almost_ as accurate. That really would cut the cost of the final device down too, keeping it under the $5 mark.

## Update: Integrating Counter Serial Output with GPS Serial Output

The NEO-M8 GPS module is [capable of outputting serial data](https://www.u-blox.com/sites/default/files/NEO-M8_DataSheet_(UBX-13003366).pdf) at 9600 baud and continuously dumps [NEMA formatted](http://www.gpsinformation.org/dale/nmea.htm) GPS data. While this isn't really useful for location information (whose frequency counter requires knowing latitude and longitude?) it's great for tracking things like signal strength, fix quality, and number of satellites. After using this system to automatically log frequency of my frequency reference, I realized that sometimes I'd get 1-2 hours of really odd data (off by kHz, not just a few Hz). Power cycling the GPS receiver fixes the problem, so my guess it that it's a satellite issue. If I combine the GPS RX and counter in 1 box, I could detect this automatically and have the microcontroller power cycle the GPS receiver (or at the least illuminate a red error LED). I don't feel like running 2 USB serial adapters continuously. I don't feel like programming my AVR to listen to the output from the GPS device (although that's probably the _correct_ way to do things).  Instead I had a simpler idea that worked really well, allowing me to simultaneously log serial data from my GPS unit and microcontroller (frequency counter) using 1 USB serial adapter.

<div class="text-center img-border">

[![](IMG_8401_thumb.jpg)](IMG_8401.jpg)

</div>

**The first thing I did was open up the frequency counter and reconnect my microcontroller programmer.** This is exactly what I promised myself I wouldn't do, and why I have a nice enclosure in the first place! Scott, stop fidgeting with things! The last time I screwed this enclosure together I considered adding super glue to the screw threads to make sure I didn't open it again. I'll keep my modifications brief! For now, this is a test of a concept. When it's done, I'll revert the circuitry to how it was and close it up again. I'll take what I learn and build it into future projects.

<div class="text-center img-border">

[![](IMG_8402_thumb.jpg)](IMG_8402.jpg)

</div>

**I peeked at the serial signals of both the frequency counter (yellow) and the GPS unit output (blue).** To my delight, there was enough dead space that I thought I could stick both in the same signal. After a code modification, I was able to tighten it up a lot, so the frequency counter never conflicts with the GPS unit by sending data at the same time.

<div class="text-center img-border">

[![](IMG_8403_thumb.jpg)](IMG_8403.jpg)

</div>

**I had to slow the baud rate to 9600, but I programmed it to send fewer characters.** This leaves an easy ~50ms padding between my frequency counter signal and the GPS signal. Time to mix the two! This takes a little thought, as I can't just connect the two wires together. Serial protocol means the lines are usually high, and only pulled down when data is being sent. I had to implement an active circuit.

<div class="text-center img-border img-small">

[![](FullSizeRender-2_thumb.jpg)](FullSizeRender-2.jpg)

</div>

**Using a few components, I built an AND gate to combine signals from the two serial lines.** For some reason it took some thought before I realized an AND gate was what I needed here, but it makes sense. The output is high (meaning no serial signal) only when both inputs are high (no serial signals on the input). When either signal drops low, the output drops low. This is perfect. My first thought was that I'd need a NOR gate, but an inverted AND gate _is_ a NOR gate.

<div class="text-center img-border">

[![](IMG_8404_thumb.jpg)](IMG_8404.jpg)

</div>

**Here's my quick and dirty implementation.** A reminder again is that this will be removed after this test. For now, it's good enough.

<div class="text-center img-border">

[![](IMG_8405_thumb.jpg)](IMG_8405.jpg)

</div>

**After connecting** the GPS serial output and frequency counter serial output to the AND gate (which outputs to the computer), I instantly got the result I wanted!

<div class="text-center img-border">

![](serial-combine.jpg)

</div>

**RealTerm shows that both inputs are being received.** It's a mess though. If you want to know what everything is, read up on [NEMA formatted](http://www.gpsinformation.org/dale/nmea.htm) GPS data.

<div class="text-center img-border">

[![](combined-python_thumb.jpg)](combined-python.png)

</div>

**I whipped-up a python program to parse, display, and log key information.** This display updates every 1 second. The bottom line is what is appended to the log file on ever read. It's clunky, but again this is just for testing and debugging. I am eager to let this run for as long as I can (days?) so I can track how changes in satellite signal / number / fix quality influence measured frequency.
August 24th, 2016

TENMA Multimeter Serial Hack

I just spent the afternoon reverse-engineering the 72 series TENMA multimeter serial interface, and can now access all of its readings from a standalone Python script. This lets me send all measurements made with the multimeter to my computer in real time (using an optically isolated connection), and eliminates the need for the TENMA PC interface software. In addition to allowing the development of custom software to use measurements from TENMA multimeters in real time, this project also lets allows TENMA multimeters to interface with Linux computers (such as the raspberry pi).

I have owned a TENMA 72-7750 multimeter for several years, and over all I've been happy with it! To be honest, 90% of my multimeter needs are just using a continuity tester or checking to see if there is voltage on a line. For checking electrical signals, I love my no-name (actually it's branded "KOMEC") $15 eBay special multimeter. The screen updates about 4 times a second, and I don't care if it's off by 10%, it's cheap and light and fast and easy for simple tasks. However, when I'm going to use a multimeter to actually measure something, I reach for a higher quality meter like my TENMA 72-7750. Although similar TENMA models may be more popular, I went with this particular one because it could measure frequency which is convenient when building RF circuits. While big fancy frequency counters are nice to have on your workbench, I liked the idea of having that functionality built into my multimeter. I believe my particular model is discontinued, but it looks like the 72-7745 is a similar product, and there are many TENMA multimeters on Amazon. Back in April of 2013 I mentioned on my website that I'd consider writing interface software in Python. Now that I'm [finally] out of school and have a little more free time, I decided to pick up the project again. I ran into a few tangles along the way, but I'm happy to report this project is now working beautifully! The pyTENMA project is open-sourced on my GitHub. I'm excited to see what kind of data I can get out of this thing!

This is my multimeter taking a measurement (resistance) and sending the data to my computer using the optically-isolated serial connector (which ships with the multimeter). In this picture, it's interacting with the official TENMA software. To try to figure out what was going on, I probed pins of the serial port while data was being exchanged. The yellow trace is the data signal.

There was a problem, and this problem took me hours to figure it out, but now that I realize what's going on it seems so obvious. The problem was that I could never get the multimeter to send my Python script data, despite the fact that the exact same configuration would send the commercial program data. I used serial port sniffing software to view the data too! I matched the baud rate (19200 / 19230), data bits (7), and parity (odd), and I just couldn't figure out why the heck this thing wouldn't work. I resorted to using an oscilloscope to probe the pins of the serial cable directly. I made a small man-in-the-middle test jig to give me headers I could easily probe or solder wires to. After poking around, I learned two things. (1) I really need a logic analyzer. They're so cheap now, I went ahead and ordered one. (2) The RTS line goes low and the DSR line goes high when data is being sent. I realized that the Python software was disregarding these pins. You wouldn't think you needed them if you're just going to be receiving data with software control... but I immediately realized that those pins may be important for powering the optoelectronics (likely a phototransistor and some passive components) underlying the data exchange. After all, it's not like the multimeter is able to source or sink appreciable current through an optical connection! I'll note that some sketchy schematics are floating around Hackaday (pun intended), but the web page they link to doesn't look very complete so I'm not sure how far that author got toward the same endeavor I'm chasing.

Here you can see some of the adjacent (non-data) pins change their voltage state during transmissions. Once I realized replicating these states was also necessary, everything quickly fell into place. After manually commanding the RTS pin to lie low (1 line of code), the data starting coming in! I finished writing a basic pyTENMA class (which does a lot of hardware detection, string parsing, etc. to generate simple no-nonsense value/unit pairs to return to the user as well as log values to disk automatically) and tried to make it as simple as possible. Without going into too much detail (see the note in the top of my source code for more information), the multimeter just sends a 9-character ASCII string every second. I refer to this string as ABBBBCDEF. Byte 1 is a multiplier and bytes 2-5 are the value displayed on the screen. The actual value of a read is BBBB*10^A. The units depend on the mode (resistance, capacitance, etc), which is indicated by byte 6. It's a little funny in that "4" means temperature and ";" means voltage, but once I figured out (through trial and error) which symbols match with which mode it was pretty easy to make it work for me. D is the sign (negative, zero, or positive), and I still haven't really figured what E and F are. I thought they might be things like backlight or perhaps indicators of the range setting. I didn't care to figure it out, because I already had access to the data I wanted!

To use the pyTENMA script, just drop it alongside a Python script you want to work on. Import it, tell it a COM port to use (if not, it'll try to guess one) and a log file (optional). This is all the code you need:

import pyTENMA # make sure pyTENMA.py is in the same folder
PT=pyTENMA.pyTenma("COM4","log.txt")
PT.readUntilBroken()

The output is very simple. Here it is compared to the commercial TENMA software. PyroElectro has a good demonstration of the PC interface software that ships with this unit. While the TENMA software is functional, it has some serious limitations that motivate me to improve upon it. (1) It's Windows only. (2) It doesn't automatically log data (you have to manually click save to write it to disk). (3) It seems to be limited to COM1-COM4. My USB serial adapter was on COM7 and inaccessible to this program. I had to go in the device manager and change the advanced settings to allow the commercial software to read my device. (4) The graphs are poor, non-interactive, and often broken. (5) Data output format is only an Excel spreadsheet (.xls), and I don't have control to save in other formats like CSV. If I'm going to use this on a raspberry pi, I don't want to fumble around with Microsoft Office! Yeah I know I can get modules (even for Python) to access data in excel spreadsheets, but it seems like an unnecessary complexity just to retrieve some voltage readings. Over all it seems a little unfortunate that a relatively great product is pulled down when its weakest link is its software. It's okay, we are on our way to can fixing this with pyTENMA!

Measuring Capacitor Leakage

I set up an experiment to demonstrate how logging data works. I charged a 22uF capacitor on a breadboard and let it sit there disconnected, slowly draining through leakage (and perhaps micro current draw from the multimeter). After a while I slowly charged it (using my body as a resistor, touching the +5V line and touching the capacitor lead with my fingers) and watched it discharge again. You can set pyTENMA software to save as little or often as you want. It defaults to every 10 reads, but I adjust it to every 100 reads for longer experiments. Also note that if you break it (with CTRL+C) it gently disconnects the serial device, logs remaining data to disk, then exits gracefully.

In this demonstration, voltage across the capacitor on the breadboard is being measured by the multimeter, and reported (and logged) in real time by pyTENMA seen on the screen. Here is what that data looks like after about a half hour of run time. The code to read the log file and make graphs from it (using numpy and matplotlib) is in the logPlot source code.

Measuring OCXO Warm-Up

Now that I know everything is up and running, I can use this device to make some measurements I'm actually interested in! In reality, this usage case is the reason I went through all the trouble to write custom data logging software for this multimeter is specifically for this case. I'm working on a large project involving a GPS-disciplined oven controlled crystal oscillator (OCXO) for a 1pps frequency reference, and spoiler alert it involves a raspberry pi to plot and upload live graphs of real-time frequency and accuracy statistics to my website. I don't want to discuss it yet (it's not complete), but I can't avoid mentioning it since I'm showing photos of it. I'll surely make a follow-up post when that project is complete and well documented. For now, the only relevant thing is that the device is an oven which takes a lot of current to heat from room temperature to a high temperature, and a smaller amount of current to maintain it at that temperature. I wanted to know how long it takes the current to stabilize over time (on a scale of hours), determine if its current draw oscillates, and also assess what the voltage at the oscillator reads during warm-up (high current draw) vs. stable conditions.

My test setup uses the TENMA multimeter in current measuring configuration. Note the configuration of the multimeter test leads as being in series with the power supply. This meter has two current measurement settings, one for <600 mA and one for up to 10 A. I know that the oscillator draws about 2 A during warm-up (this is because I'm intentionally limiting it to 2A), and stabilizes to somewhere near 200 mA after several minutes. To maximize my sample resolution, I started the recording using the 10 A setting, then after it dropped well below 600 mA I switched to the lower current setting. The data is colored red and blue, respectively:

current stabilizes within 10 minutes:

Current is maxed-out for a few minutes, oscillates then stabilizes. 10 A / 600 mA measuring settings are in red and blue (respectively):

once stable, current draw is stable for hours

I concluded that this thing stabilizes to within 10% of its final current draw well within 10 minutes. From there, it seems really stable, but slowly oscillates on a time scale of tens of minutes. I suspect this correlates with the AC unit of my house turning on and off. A similar recording of temperature of the oscillator (which the TENMA 72-7750 can also do with the thermocouple it was shipped with) may provide more insight as to whether or not the oscillator itself is actually changing temperature during these current oscillations. Now I'm curious what the voltage does during the warm-up period while the current is maxed out. I guess I need to reveal that my current limit is provided by two parallel LM7809 voltage regulators each in series with a 2 Ohm current limiting resistor before connecting to a common +9V rail which is running the oscillator. Since each regulator is current limited to about 1A, it's no surprise my maximum current is about 2A, but I'd be interested to learn what the voltage is doing during that period.

I measured voltage just downstream of the voltage regulators:

During current max-out, the voltage is <<9V

Voltage stabilizes after about 10 minutes

I am interested in seeing what of these measurements (with more such as temperature and OCXO frequency) look like when they are all measured simultaneously. The TENMA multimeter I'm using can't measure voltage and current at the same time (which would require a third lead, if you think about it), so this solution will require alternative equipment. Stay tuned, because I have a cool solution for that in the works! For now, I couldn't be happier with my TENMA multimeter's ability to log data to text files using pyTENMA and the ease in which numpy/matplotlib can read and graph them. A data logging multimeter is a great tool to have in any engineer's toolbox, and I'm glad I now have one that plays nicely with Python.

Resources

Markdown source code last modified on January 18th, 2021
---
title: TENMA Multimeter Serial Hack
date: 2016-08-24 03:08:28
tags: python, old
---

# TENMA Multimeter Serial Hack

__I just spent the afternoon reverse-engineering the 72 series TENMA multimeter serial interface,__ and can now access all of its readings from a standalone Python script. This lets me send all measurements made with the multimeter to my computer in real time (using an optically isolated connection), and eliminates the need for the TENMA PC interface software. In addition to allowing the development of custom software to use measurements from TENMA multimeters in real time, this project also lets allows TENMA multimeters to interface with Linux computers (such as the raspberry pi). 

**I have owned a [TENMA 72-7750](http://www.farnell.com/datasheets/1955368.pdf?_ga=1.217276502.1323514874.1471841353) multimeter for several years, and over all I've been happy with it!** To be honest, 90% of my multimeter needs are just using a continuity tester or checking to see if there is voltage on a line. For _checking_ electrical signals, I love my no-name (actually it's branded "KOMEC") $15 eBay special multimeter. The screen updates about 4 times a second, and I don't care if it's off by 10%, it's cheap and light and fast and easy for simple tasks. However, when I'm going to use a multimeter to actually _measure_ something, I reach for a higher quality meter like my TENMA 72-7750. Although [similar TENMA models may be more popular](http://www.newark.com/MarketingProductList?storeId=10194&catalogId=15003&langId=-1&orderCode=02J5540,02J5541,02J5542,02J5543,02J5546), I went with this particular one because it could measure frequency which is convenient when building RF circuits. While big fancy frequency counters are nice to have on your workbench, I liked the idea of having that functionality built into my multimeter. I believe my particular model is discontinued, but it looks like the 72-7745 is a similar product, and there are [many TENMA multimeters on Amazon](https://www.amazon.com/s/field-keywords=tenma+multimeter). Back in April of 2013 I mentioned on my website that I'd consider writing interface software in Python. Now that I'm [finally] out of school and have a little more free time, I decided to pick up the project again. I ran into a few tangles along the way, but I'm happy to report this project is now working beautifully! The [pyTENMA project](https://github.com/swharden/pyTENMA) is open-sourced on my GitHub. I'm excited to see what kind of data I can get out of this thing!

<div class="text-center img-border">

[![](IMG_7956_thumb.jpg)](IMG_7956.jpg)

</div>

**This is my multimeter taking a measurement (resistance) and sending the data to my computer using the optically-isolated serial connector (which ships with the multimeter).** In this picture, it's interacting with the official TENMA software. To try to figure out what was going on, I probed pins of the serial port while data was being exchanged. The yellow trace is the data signal. 

__There was a problem, and this problem took me hours to figure it out,__ but now that I realize what's going on it seems so obvious. The problem was that I could never get the multimeter to send my Python script data, despite the fact that the exact same configuration would send the commercial program data. I used serial port sniffing software to view the data too! I matched the baud rate (19200 / 19230), data bits (7), and parity (odd), and I just couldn't figure out why the heck this thing wouldn't work. I resorted to using an oscilloscope to probe the pins of the serial cable directly. I made a small man-in-the-middle test jig to give me headers I could easily probe or solder wires to. After poking around, I learned two things. (1) I really need a logic analyzer. They're so cheap now, I went ahead and ordered one. (2) The RTS line goes low and the DSR line goes high when data is being sent. I realized that the Python software was disregarding these pins. You wouldn't _think_ you needed them if you're just going to be receiving data with software control... but I immediately realized that those pins may be important for powering the optoelectronics (likely a phototransistor and some passive components) underlying the data exchange. After all, it's not like the multimeter is able to source or sink appreciable current through an optical connection! I'll note that some [sketchy schematics](http://hackaday.com/2014/12/05/fixing-a-multimeters-serial-interface/) are floating around Hackaday (pun intended), but the [web page they link to](http://www.wattnotions.com/tenma-72-7735-serial-interface/) doesn't look very complete so I'm not sure how far that author got toward the same endeavor I'm chasing.

<div class="text-center img-border">

[![](IMG_7961_thumb.jpg)](IMG_7961.jpg)

</div>

__Here you can see some of the adjacent (non-data) pins change their voltage state during transmissions. Once I realized replicating these states was also necessary, everything quickly fell into place.__ After manually commanding the RTS pin to lie low (1 line of code), the data starting coming in! I finished writing a basic pyTENMA class (which does a lot of hardware detection, string parsing, etc. to generate simple no-nonsense value/unit pairs to return to the user as well as log values to disk automatically) and tried to make it as simple as possible. Without going into too much detail ([see the note in the top of my source code for more information](https://github.com/swharden/pyTENMA/blob/master/pyTENMA.py)), the multimeter just sends a 9-character ASCII string every second. I refer to this string as ABBBBCDEF. Byte 1 is a multiplier and bytes 2-5 are the value displayed on the screen. The actual value of a read is BBBB*10^A. The units depend on the mode (resistance, capacitance, etc), which is indicated by byte 6. It's a little funny in that "4" means temperature and ";" means voltage, but once I figured out (through trial and error) which symbols match with which mode it was pretty easy to make it work for me. D is the sign (negative, zero, or positive), and I still haven't really figured what E and F are. I thought they might be things like backlight or perhaps indicators of the range setting. I didn't care to figure it out, because I already had access to the data I wanted!

__To use the pyTENMA script, just drop it alongside a Python script you want to work on.__ Import it, tell it a COM port to use (if not, it'll try to guess one) and a log file (optional). This is all the code you need:

```python
import pyTENMA # make sure pyTENMA.py is in the same folder
PT=pyTENMA.pyTenma("COM4","log.txt")
PT.readUntilBroken()
```

__The output is very simple.__ Here it is compared to the commercial TENMA software. [PyroElectro has a good demonstration](http://www.pyroelectro.com/tutorials/tenma_digital_multimeter/software.html) of the PC interface software that ships with this unit. While the TENMA software is functional, it has some serious limitations that motivate me to improve upon it. (1) It's Windows only. (2) It doesn't automatically log data (you have to manually click save to write it to disk). (3) It seems to be limited to COM1-COM4. My USB serial adapter was on COM7 and inaccessible to this program. I had to go in the device manager and change the advanced settings to allow the commercial software to read my device. (4) The graphs are poor, non-interactive, and often broken. (5) Data output format is only an Excel spreadsheet (.xls), and I don't have control to save in other formats like CSV. If I'm going to use this on a raspberry pi, I don't want to fumble around with Microsoft Office! Yeah I know I can get modules (even for Python) to access data in excel spreadsheets, but it seems like an unnecessary complexity just to retrieve some voltage readings. Over all it seems a little unfortunate that a relatively great product is pulled down when its weakest link is its software. It's okay, we are on our way to can fixing this with [pyTENMA](https://github.com/swharden/pyTENMA)!

<div class="text-center img-border img-small">

[![](screenshot_thumb.jpg)](screenshot.jpg)
![](commercialSoftware.jpg)

</div>

## Measuring Capacitor Leakage

__I set up an experiment to demonstrate how logging data works.__ I charged a 22uF capacitor on a breadboard and let it sit there disconnected, slowly draining through leakage (and perhaps micro current draw from the multimeter). After a while I slowly charged it (using my body as a resistor, touching the +5V line and touching the capacitor lead with my fingers) and watched it discharge again. You can set pyTENMA software to save as little or often as you want. It defaults to every 10 reads, but I adjust it to every 100 reads for longer experiments. Also note that if you break it (with CTRL+C) it gently disconnects the serial device, logs remaining data to disk, then exits gracefully.

<div class="text-center img-border">

[![](IMG_7981_thumb.jpg)](IMG_7981.jpg)

</div>

__In this demonstration,__ voltage across the capacitor on the breadboard is being measured by the multimeter, and reported (and logged) in real time by pyTENMA seen on the screen. Here is what that data looks like after about a half hour of run time. The code to read the log file and make graphs from it (using numpy and matplotlib) is in the [logPlot source code](https://github.com/swharden/pyTENMA/blob/master/extras/logPlot.py).

<div class="text-center img-medium">

[![](logDemo_thumb.jpg)](logDemo.png)

</div>

## Measuring OCXO Warm-Up

__Now that I know everything is up and running, I can use this device to make some measurements I'm actually interested in!__ In reality, this usage case is the _reason_ I went through all the trouble to write custom data logging software for this multimeter is specifically for this case. I'm working on a large project involving a GPS-disciplined oven controlled crystal oscillator (OCXO) for a 1pps frequency reference, and spoiler alert it involves a raspberry pi to plot and upload live graphs of real-time frequency and accuracy statistics to my website. I don't want to discuss it yet (it's not complete), but I can't avoid mentioning it since I'm showing photos of it. I'll surely make a follow-up post when that project is complete and well documented. For now, the only relevant thing is that the device is an oven which takes a lot of current to heat from room temperature to a high temperature, and a smaller amount of current to maintain it at that temperature. I wanted to know how long it takes the current to stabilize over time (on a scale of hours), determine if its current draw oscillates, and also assess what the voltage at the oscillator reads during warm-up (high current draw) vs. stable conditions.

<div class="text-center img-border">

[![](FullSizeRender-2_thumb.jpg)](FullSizeRender-2.jpg)

</div>

__My test setup uses the TENMA multimeter in current measuring configuration.__ Note the configuration of the multimeter test leads as being in series with the power supply.  This meter has two current measurement settings, one for <600 mA and one for up to 10 A. I know that the oscillator draws about 2 A during warm-up (this is because I'm intentionally limiting it to 2A), and stabilizes to somewhere near 200 mA after several minutes. To maximize my sample resolution, I started the recording using the 10 A setting, then after it dropped well below 600 mA I switched to the lower current setting. The data is colored red and blue, respectively:

current stabilizes within 10 minutes:

<div class="text-center">

[![](ocxo-1_thumb.jpg)](ocxo-1.png)

</div>

Current is maxed-out for a few minutes, oscillates then stabilizes. 10 A / 600 mA measuring settings are in red and blue (respectively):

<div class="text-center">

[![](ocxo-2_thumb.jpg)](ocxo-2.png)

</div>

once stable, current draw is stable for hours

<div class="text-center">

[![](ocxo-3_thumb.jpg)](ocxo-3.png)

</div>

__I concluded that this thing stabilizes to within 10% of its final current draw well within 10 minutes.__ From there, it seems really stable, but slowly oscillates on a time scale of tens of minutes. I suspect this correlates with the AC unit of my house turning on and off. A similar recording of temperature of the oscillator (which the TENMA 72-7750 can also do with the thermocouple it was shipped with) may provide more insight as to whether or not the oscillator itself is actually changing temperature during these current oscillations. Now I'm curious what the voltage does during the warm-up period while the current is maxed out. I guess I need to reveal that my current limit is provided by two parallel LM7809 voltage regulators each in series with a 2 Ohm current limiting resistor before connecting to a common +9V rail which is running the oscillator. Since each regulator is current limited to about 1A, it's no surprise my maximum current is about 2A, but I'd be interested to learn what the voltage is doing during that period.

<div class="text-center img-border">

[![](FullSizeRender-3_thumb.jpg)](FullSizeRender-3.jpg)

</div>

I measured voltage just downstream of the voltage regulators:
<div class="text-center">

[![](mV1_thumb.jpg)](mV1.png)

</div>

During current max-out, the voltage is <<9V
<div class="text-center">

[![](mV2_thumb.jpg)](mV2.png)

</div>

Voltage stabilizes after about 10 minutes
<div class="text-center">

[![](mV3_thumb.jpg)](mV3.png)

</div>

__I am interested in seeing what of these measurements (with more such as temperature and OCXO frequency) look like when they are all measured simultaneously.__ The TENMA multimeter I'm using can't measure voltage and current at the same time (which would require a third lead, if you think about it), so this solution will require alternative equipment. Stay tuned, because I have a cool solution for that in the works! For now, I couldn't be happier with my TENMA multimeter's ability to log data to text files using [pyTENMA](https://github.com/swharden/pyTENMA) and the ease in which numpy/matplotlib can read and graph them. A data logging multimeter is a great tool to have in any engineer's toolbox, and I'm glad I now have one that plays nicely with Python.

## Resources

* pyTENMA on GitHub: [github.com/swharden/pyTENMA](https://github.com/swharden/pyTENMA)
August 8th, 2016

DIY ECG with 1 op-amp

⚠️ Check out my newer ECG design:

I made surprisingly good ECG from a single op-amp and 5 resistors! An ECG (electrocardiograph, sometimes called EKG) is a graph of the electrical potential your heart produces as it beats. Seven years ago I posted DIY ECG Machine on the Cheap which showed a discernible ECG I obtained using an op-amp, two resistors, and a capacitor outputting to a PC sound card's microphone input. It didn't work well, but the fact that it worked at all was impressive! It has been one of the most popular posts of my website ever since, and I get 1-2 emails a month from people trying to recreate these results (some of them are during the last week of a college design course and sound pretty desperate). Sometimes people get good results with that old circuit, but more often than not the output isn't what people expected. I decided to revisit this project (with more patience and experience under my belt) and see if I could improve it. My goal was not to create the highest quality ECG machine I could, but rather to create the simplest one I could with emphasis on predictable and reproducible results. The finished project is a blend of improved hardware and custom cross-platform open-source software (which runs on Windows, Linux, and MacOS), and an impressively good ECG considering the circuit is so simple and runs on a breadboard! Furthermore, the schematics and custom software are all open-sourced on my github!

Here's a video demonstrating how the output is shown in real time with custom Python software. The video is quite long, but you can see the device in action immediately, so even if you only watch the first few seconds you will see this circuit in action with the custom software. In short, the amplifier circuit (described in detail below) outputs to the computer's microphone and a Python script I wrote analyzes the audio data, performs low-pass filtering, and graphs the output in real time. The result is a live electrocardiograph!

ECG Circuit

The circuit is simple, but a lot of time and thought and experimentation went into it. I settled on this design because it produced the best and most reliable results, and it has a few nuances which might not be obvious at first. Although I discuss it in detail in the video, here are the highlights:

  • The output goes to the microphone jack of your computer.

  • There's nothing special about the op-amp I used (LM741). A single unit of an LM324 (or any general purpose op-amp) should work just as well.

  • Resistor values were chosen because I had them on hand. You can probably change them a lot as long as they're in the same ballpark of the values shown here. Just make sure R1 and R2 are matched, and R3 should be at least 10MOhm.

  • Do not use a bench power supply! "BAT+" and "BAT-" are the leads of a single 9V battery.

  • Note that the leg electrode is ground (same ground as the computer's microphone ground)

  • R5 and R4 form a traditional voltage divider like you'd expect for an op-amp with a gain of about 50.

    • You'd expect R4 to connect to ground, but since your body is grounded, chest 2 is essentially the same

    • R3 must be extremely high value, but it pulls your body potential near the optimal input voltage for amplification by the op-amp.

    • R1 and R2 split the 9V battery's voltage in half and center it at ground, creating -4.5V and +4.5V.

  • altogether, your body stays grounded, and the op-amp becomes powered by -4.5V and +4.5V, and your body is conveniently near the middle and ready to have small signals from CHEST1 amplified. Amplification is with respect to CHEST2 (roughly ground), rather than actual ground, so that a lot of noise (with respect to ground) is eliminated.

For those of you who would rather see a picture than a schematic, here's a diagram of how to assemble it graphically. This should be very easy to reproduce. Although breadboards are typically not recommended for small signal amplification projects, there is so much noise already in these signals that it doesn't really matter much either way. Check out how good the signals look in my video, and consider that I use a breadboard the entire time.

The most comfortable electrodes I used were made for muscle simulators. A friend of mine showed me some muscle stimulator pads he got for a back pain relief device he uses. As soon as I saw those pads, I immediately thought they would be perfect for building an ECG! They're a little bit expensive, but very comfortable, reusable, last a long time, and produce brilliant results. They also have 3.5 mm (headphone jack) connectors which is perfect for DIY projects. On Amazon.com you can get 16 pads for $11 with free shipping. I decided not to include links, because sometimes the pads and cords are sold separately, and sometimes they have barrel connectors and sometimes they have snap connectors. Just get any adhesive reusable electrodes intended for transcutaneous electrical nerve stimulation (TENS) that you can find! They should all work fine.

Pennies as Electrodes

You can make your own electrodes for $0.03! Okay that's a terrible joke, but it's true. I made not-awful electrodes by soldering wires to copper pennies, adding strength by super-gluing the wire to the penny, and using electrical tape to attach them to my chest. Unless you want a tattoo of an old guy's face on your torso, wait until they cool sufficiently after soldering before proceeding to the adhesion step. I suspect that super gluing the penny to your chest would also work, but please do not do this. Ironically, because the adhesive pads of the TENS electrodes wear away over time, the penny solution is probably "more reusable" than the commercial electrode option.

This ECG was recorded using pennies as electrodes:

Notes on filtering: Why didn't I just use a hardware low-pass filter?

  1. It would have required extra components, which goes against the theme of this project
  2. It would require specific value components, which is also undesirable for a junkbox project
  3. I'm partial to the Chebyshev filter, but getting an extremely sharp roll-off a few Hz shy of 50Hz would take multiple poles (of closely matched passive components) and not be as trivial as it sounds.

Notes on software: This a really cool use of Python! I lean on some of my favorite packages numpy, scipy, matplotlib, pyqrgraph, and PyQt4. I've recently made posts describing how to perform real-time data graphing in Python using these libraries, so I won't go into that here. If you're interested, check out my real-time audio monitor, notes on using PlotWidget, and notes on using MatPlotLib widget. I tried using PyInstaller to package this project into a single .EXE for all my windows readers who might want to recreate this project, but the resulting EXE was over 160MB! That's crazy! It makes sense considering packagers like PyInstaller and Py2EXE work by building your entire python interpreter and all imported libraries. With all those fun libraries I listed above, it's no wonder it came out so huge. It may be convenient for local quick-fixes, but not a good way to distribute code over the internet. To use this software, just run it in Python. It was tested to work with out-of-the-box WinPython-64bit-3.5.2.1 (not the Qt5 version), so if you want to run it yourself start there.

Notes on safety. How safe is this project? I'm conflicted on this subject. I want to be as conservative as I can (leaning on the side of caution), but I also want to be as realistic as possible. I'm going to play it safe and say "this may not be safe, so don't build or use it". As an exercise, let's consider the pros and cons:

  • PROS:

    • It's powered from a 9V battery which is safer than a bench power supply (but see the matching con).

    • The only connections to your body are:

      • leg - ground. you ground yourself all the time. using a wrist grounding strap is the same thing.
      • chest 1 - extremely high impedance. You're attaching your chest to the high impedance input of an op-amp (which I feel fine with), and also to a floating battery through a 10MOhm resistor (which also I feel fine with)
      • chest 2 - raises an eyebrow. In addition to a high impedance input, you're connected to an op-amp through a 100k resistor. Even if the op-amp were putting out a full 4.5V, that's 0.045mA (which doesn't concern me a whole lot).
    • I don't know where to stick this, but I wonder what type of voltages / currents TENS actually provide.

  • CONS / WARNINGS:

    • It's powered from a 9V battery. So are many stun guns.
    • If the op-amp oscillates, oscillations may enter your body. Personally I feel this may be the most concerning issue.
    • Small currents can kill. I found a curiously colored website that describes this. It seems like the most dangerous potential effect is induction of cardiac fibrillation, which can occur around 100mA.

Improving safety through optical isolation: The safety of this device may be improved (albeit with increased complexity) through the implementation of opto-isolators. I may consider a follow-up post demonstrating how I do this. Unlike digital signals which I've optically isolated before, I've never personally isolated analog signals. Although I'm sure there are fully analog means to do this, I suspect I'd accomplish it by turning it into a digital signal (with a voltage-to-frequency converter), pulsing the output across the optoisolator, and turning it back into voltage with a frequency-to-voltage converter or perhaps even a passive low-pass filter. Analog Devices has a good write-up about optical isolation techniques.

Do you have comments regarding the safety of this device? Write your thoughts concisely and send them to me in an email! I'd be happy to share your knowledge with everyone by posting it here.

Did you build this or a device similar to it? Send me some pictures! I'll post them here.

Source code and project files: https://github.com/swharden/diyECG-1opAmp/

LEGAL: This website is for educational purposes only. Do not build or use any electrical devices shown. Attaching non-compliant electronic devices to your body may be dangerous. Consult a physician regarding proper usage of medical equipment.

Markdown source code last modified on January 18th, 2021
---
title: DIY ECG with 1 op-amp
date: 2016-08-08 01:49:24
tags: diyECG, python, circuit
---

# DIY ECG with 1 op-amp

> **⚠️ Check out my newer ECG design:** 
* [**Sound Card ECG with AD8232**](https://swharden.com/blog/2019-03-15-sound-card-ecg-with-ad8232/)

__I made surprisingly good ECG from a single op-amp and 5 resistors! __An ECG (electrocardiograph, sometimes called EKG) is a graph of the electrical potential your heart produces as it beats. Seven years ago I posted _[DIY ECG Machine on the Cheap](https://www.swharden.com/wp/2009-08-14-diy-ecg-machine-on-the-cheap/)_ which showed a discernible ECG I obtained using an op-amp, two resistors, and a capacitor outputting to a PC sound card's microphone input. It didn't work well, but the fact that it worked at all was impressive! It has been one of the most popular posts of my website ever since, and I get 1-2 emails a month from people trying to recreate these results (some of them are during the last week of a college design course and sound pretty desperate). Sometimes people get good results with that old circuit, but more often than not the output isn't what people expected. I decided to revisit this project (with more patience and experience under my belt) and see if I could improve it. My goal was not to create the highest quality ECG machine I could, but rather to create the _simplest_ one I could with emphasis on predictable and reproducible results. The finished project is a blend of improved hardware and custom cross-platform open-source software (which runs on Windows, Linux, and MacOS), and an impressively good ECG considering the circuit is so simple and runs on a breadboard! Furthermore, the schematics and custom software are all [open-sourced on my github](https://github.com/swharden/diyECG-1opAmp/)!

![](https://www.youtube.com/embed/AfirWls9Sys)

__Here's a video demonstrating how the output is shown in real time with custom Python software.__ The video is quite long, but you can see the device in action immediately, so even if you only watch the first few seconds you will see this circuit in action with the custom software. In short, the amplifier circuit (described in detail below) outputs to the computer's microphone and a Python script I wrote analyzes the audio data, performs low-pass filtering, and graphs the output in real time. The result is a live electrocardiograph!

<div class="text-center">

[![](ECG_1470609065_thumb.jpg)](ECG_1470609065.png)

</div>

### ECG Circuit

<div class="text-center">

[![](circuit_thumb.jpg)](circuit.jpg)

</div>

__The circuit is simple, but a lot of time and thought and experimentation went into it.__ I settled on this design because it produced the best and most reliable results, and it has a few nuances which might not be obvious at first. Although I discuss it in detail in the video, here are the highlights:

*   The output goes to the microphone jack of your computer.
*   There's nothing special about the op-amp I used ([LM741](http://www.ti.com/lit/ds/symlink/lm741.pdf)). A single unit of an [LM324](http://www.ti.com.cn/cn/lit/ds/symlink/lm2902-n.pdf) (or any general purpose op-amp) should work just as well.
*   Resistor values were chosen because I had them on hand. You can probably change them a lot as long as they're in the same ballpark of the values shown here. Just make sure R1 and R2 are matched, and R3 should be at least 10MOhm.
*   <span style="color: #ff0000;">Do not use a bench power supply!</span> "BAT+" and "BAT-" are the leads of a single 9V battery.
*   Note that the leg electrode is ground (same ground as the computer's microphone ground)
*   R5 and R4 form a traditional voltage divider like you'd expect for an op-amp with a gain of about 50.

    *   You'd expect R4 to connect to ground, but since your body is grounded, chest 2 is essentially the same

    *   R3 must be extremely high value, but it pulls your body potential near the optimal input voltage for amplification by the op-amp.

    *   R1 and R2 split the 9V battery's voltage in half and center it at ground, creating -4.5V and +4.5V.

*   altogether, your body stays grounded, and the op-amp becomes powered by -4.5V and +4.5V, and your body is conveniently near the middle and ready to have small signals from CHEST1 amplified. Amplification is with respect to CHEST2 (roughly ground), rather than actual ground, so that a lot of noise (with respect to ground) is eliminated.

<div class="text-center img-border">

[![](IMG_7574_thumb.jpg)](IMG_7574.jpg)

</div>

__For those of you who would rather see a picture than a schematic__, here's a diagram of how to assemble it graphically. This should be very easy to reproduce. Although breadboards are typically not recommended for small signal amplification projects, there is so much noise already in these signals that it doesn't really matter much either way. Check out how good the signals look in my video, and consider that I use a breadboard the entire time.

<div class="text-center">

[![](design_thumb.jpg)](design.jpg)

</div>

__The most comfortable electrodes I used were made for muscle simulators.__ A friend of mine showed me some muscle stimulator pads he got for a back pain relief device he uses. As soon as I saw those pads, I immediately thought they would be perfect for building an ECG! They're a little bit expensive, but very comfortable, reusable, last a long time, and produce brilliant results. They also have 3.5 mm (headphone jack) connectors which is perfect for DIY projects. On Amazon.com you can get 16 pads for $11 with free shipping. I decided not to include links, because sometimes the pads and cords are sold separately, and sometimes they have barrel connectors and sometimes they have snap connectors. Just get any adhesive reusable electrodes intended for [transcutaneous electrical nerve stimulation (TENS)](https://en.wikipedia.org/wiki/Transcutaneous_electrical_nerve_stimulation) that you can find! They should all work fine.

<div class="text-center img-border">

[![](IMG_7576_thumb.jpg)](IMG_7576.jpg)

</div>

### Pennies as Electrodes

__You can make your own electrodes for $0.03!__ Okay that's a terrible joke, but it's true. I made not-awful electrodes by soldering wires to copper pennies, adding strength by super-gluing the wire to the penny, and using electrical tape to attach them to my chest. Unless you want a tattoo of an old guy's face on your torso, wait until they cool sufficiently after soldering before proceeding to the adhesion step. I suspect that super gluing the penny to your chest would also work, but please do not do this. Ironically, because the adhesive pads of the TENS electrodes wear away over time, the penny solution is probably "more reusable" than the commercial electrode option.

<div class="text-center img-border img-small">

[![](IMG_7527_thumb.jpg)](IMG_7527.jpg)
[![](IMG_7570-1_thumb.jpg)](IMG_7570-1.jpg)

</div>

This ECG was recorded using pennies as electrodes:

<div class="text-center">

[![](ECG_1470611901_thumb.jpg)](ECG_1470611901.png)

</div>

__Notes on filtering:__ Why didn't I just use a [hardware low-pass filter](https://en.wikipedia.org/wiki/Low-pass_filter)?

1.   It would have required extra components, which goes against the theme of this project
2.   It would require _specific value_ components, which is also undesirable for a junkbox project
3.   I'm partial to the [Chebyshev filter](https://en.wikipedia.org/wiki/Chebyshev_filter), but getting an extremely sharp roll-off a few Hz shy of 50Hz would take multiple poles (of closely matched passive components) and [not be as trivial as it sounds](http://www.analog.com/library/analogDialogue/archives/43-09/EDCh%208%20filter.pdf?doc=ADA4661-2.pdf).

__Notes on software:__ This a really cool use of Python! I lean on some of my favorite packages [numpy](http://www.numpy.org/), [scipy](https://www.scipy.org/), [matplotlib](http://matplotlib.org/), [pyqrgraph](http://www.pyqtgraph.org/), and [PyQt4](https://wiki.python.org/moin/PyQt4). I've recently made posts describing how to perform real-time data graphing in Python using these libraries, so I won't go into that here. If you're interested, check out my [real-time audio monitor](https://www.swharden.com/wp/2016-07-31-real-time-audio-monitor-with-pyqt/), notes on using [PlotWidget](https://www.swharden.com/wp/2016-07-31-live-data-in-pyqt4-with-plotwidget/), and notes on using [MatPlotLib widget](https://www.swharden.com/wp/2016-07-30-live-data-in-pyqt4-with-matplotlibwidget/). I tried using [PyInstaller](http://www.pyinstaller.org/) to package this project into a single .EXE for all my windows readers who might want to recreate this project, but the resulting EXE was over 160MB! That's crazy! It makes sense considering packagers like PyInstaller and Py2EXE work by building your entire python interpreter and all imported libraries. With all those fun libraries I listed above, it's no wonder it came out so huge. It may be convenient for local quick-fixes, but not a good way to distribute code over the internet. To use this software, just run it in Python. It was tested to work with out-of-the-box [WinPython-64bit-3.5.2.1](https://sourceforge.net/projects/winpython/files/) (not the Qt5 version), so if you want to run it yourself start there.

__Notes on safety.__ How safe is this project? I'm conflicted on this subject. I want to be as conservative as I can (leaning on the side of caution), but I also want to be as realistic as possible. I'm going to play it safe and say "this may not be safe, so don't build or use it". As an exercise, let's consider the pros and cons:

*   __PROS:__

    *   It's powered from a 9V battery which is safer than a bench power supply (but see the matching con).
    *   The only connections to your body are:

        *   leg - ground. you ground yourself all the time. using a wrist grounding strap is the same thing.
        *   chest 1 - extremely high impedance. You're attaching your chest to the high impedance input of an op-amp (which I feel fine with), and also to a floating battery through a 10MOhm resistor (which also I feel fine with)
        *   chest 2 - raises an eyebrow. In addition to a high impedance input, you're connected to an op-amp through a 100k resistor. Even if the op-amp were putting out a full 4.5V, that's 0.045mA (which doesn't concern me a whole lot).

    *   I don't know where to stick this, but I wonder what type of voltages / currents [TENS](https://en.wikipedia.org/wiki/Transcutaneous_electrical_nerve_stimulation) actually provide.

*   __CONS / WARNINGS:__

    *   It's powered from a 9V battery. So are many stun guns.
    *   If the op-amp oscillates, oscillations may enter your body. Personally I feel this may be the most concerning issue.
    *   Small currents can kill. I found a [curiously colored website](https://www.physics.ohio-state.edu/~p616/safety/fatal_current.html) that describes this. It seems like the most dangerous potential effect is induction of cardiac fibrillation, which can occur around 100mA.

__Improving safety through optical isolation:__ The safety of this device may be improved (albeit with increased complexity) through the implementation of [opto-isolators](https://en.wikipedia.org/wiki/Opto-isolator). I may consider a follow-up post demonstrating how I do this. Unlike digital signals which I've [optically isolated before](https://www.swharden.com/wp/2016-07-28-opto-isolated-laser-controller-build/), I've never personally isolated analog signals. Although I'm sure there are fully analog means to do this, I suspect I'd accomplish it by turning it into a digital signal (with a voltage-to-frequency converter), pulsing the output across the optoisolator, and turning it back into voltage with a frequency-to-voltage converter or perhaps even a passive low-pass filter. Analog Devices has a good write-up about [optical isolation techniques](http://www.analog.com/media/en/training-seminars/tutorials/MT-071.pdf).

__Do you have comments regarding the safety of this device?__ Write your thoughts concisely and send them to me in an email! I'd be happy to share your knowledge with everyone by posting it here.

__Did you build this or a device similar to it?__ Send me some pictures! I'll post them here.

__Source code and project files:__ <https://github.com/swharden/diyECG-1opAmp/>

___LEGAL__: This website is for educational purposes only. Do not build or use any electrical devices shown. Attaching non-compliant electronic devices to your body may be dangerous. Consult a physician regarding proper usage of medical equipment._
July 31st, 2016

Python Real-time Audio Frequency Monitor

A new project I'm working on requires real-time analysis of soundcard input data, and I made a minimal case example of how to do this in a cross-platform way using python 3, numpy, and PyQt. Previous posts compared performance of the matplotlib widget vs PyQtGraph plotwidget and I've been working with PyQtGraph ever since. For static figures matplotlib is wonderful, but for fast responsive applications I'm leaning toward PyQtGraph. The full source for this project is on a github page, but here's a summary of the project.

I made the UI with QT Designer. The graphs are QGraphicsView widgets promoted to a pyqtgraph PlotWidget. I describe how to do this in my previous post. Here's the content of the primary program:

from PyQt4 import QtGui,QtCore
import sys
import ui_main
import numpy as np
import pyqtgraph
import SWHear

class ExampleApp(QtGui.QMainWindow, ui_main.Ui_MainWindow):
    def __init__(self, parent=None):
        pyqtgraph.setConfigOption('background', 'w') #before loading widget
        super(ExampleApp, self).__init__(parent)
        self.setupUi(self)
        self.grFFT.plotItem.showGrid(True, True, 0.7)
        self.grPCM.plotItem.showGrid(True, True, 0.7)
        self.maxFFT=0
        self.maxPCM=0
        self.ear = SWHear.SWHear()
        self.ear.stream_start()

    def update(self):
        if not self.ear.data is None and not self.ear.fft is None:
            pcmMax=np.max(np.abs(self.ear.data))
            if pcmMax>self.maxPCM:
                self.maxPCM=pcmMax
                self.grPCM.plotItem.setRange(yRange=[-pcmMax,pcmMax])
            if np.max(self.ear.fft)>self.maxFFT:
                self.maxFFT=np.max(np.abs(self.ear.fft))
                self.grFFT.plotItem.setRange(yRange=[0,self.maxFFT])
            self.pbLevel.setValue(1000*pcmMax/self.maxPCM)
            pen=pyqtgraph.mkPen(color='b')
            self.grPCM.plot(self.ear.datax,self.ear.data,
                            pen=pen,clear=True)
            pen=pyqtgraph.mkPen(color='r')
            self.grFFT.plot(self.ear.fftx[:500],self.ear.fft[:500],
                            pen=pen,clear=True)
        QtCore.QTimer.singleShot(1, self.update) # QUICKLY repeat

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    form = ExampleApp()
    form.show()
    form.update() #start with something
    app.exec_()
    print("DONE")

This project uses a gutted version of the SWHEar class which I still haven't released on githib yet. It will likely have its own project folder. For now, take this project with a grain of salt. The primary advantage of this class is that it makes it easy to use PyAudio to automatically detect input sound cards, channels, and sample rates which are likely to succeed without requiring the user to enter any information.

All files used for this project are in a GitHub folder

Audio Visualizer Screenlet

2016-09-05: Okko adapted this project into a screenlet (cross platform) which also includes an installer for Windows: https://github.com/ninlith/audio-visualizer-screenlet

Markdown source code last modified on January 18th, 2021
---
title: Python Real-time Audio Frequency Monitor
date: 2016-07-31 15:21:11
tags: python, old
---

# Python Real-time Audio Frequency Monitor

__A new project I'm working on requires real-time analysis of soundcard input data, and I made a minimal case example of how to do this in a cross-platform way using python 3, numpy, and PyQt.__ Previous posts compared performance of the [matplotlib widget](https://www.swharden.com/wp/2016-07-30-live-data-in-pyqt4-with-matplotlibwidget/) vs [PyQtGraph plotwidget](https://www.swharden.com/wp/2016-07-31-live-data-in-pyqt4-with-plotwidget/) and I've been working with [PyQtGraph](http://www.pyqtgraph.org/) ever since. For static figures matplotlib is wonderful, but for fast responsive applications I'm leaning toward PyQtGraph. The [full source for this project is on a github page](https://github.com/swharden/Python-GUI-examples/tree/master/2016-07-37_qt_audio_monitor), but here's a summary of the project.

<div class="text-center img-border img-small">

![](demo-1.gif)

</div>

![](https://www.youtube.com/embed/lDS9rI0o6mM)

__I made the UI with QT Designer.__ The graphs are _QGraphicsView_ widgets promoted to a pyqtgraph_ PlotWidget_. I describe how to do this [in my previous post](https://www.swharden.com/wp/2016-07-31-live-data-in-pyqt4-with-plotwidget/). Here's the content of the primary program:

```python
from PyQt4 import QtGui,QtCore
import sys
import ui_main
import numpy as np
import pyqtgraph
import SWHear

class ExampleApp(QtGui.QMainWindow, ui_main.Ui_MainWindow):
    def __init__(self, parent=None):
        pyqtgraph.setConfigOption('background', 'w') #before loading widget
        super(ExampleApp, self).__init__(parent)
        self.setupUi(self)
        self.grFFT.plotItem.showGrid(True, True, 0.7)
        self.grPCM.plotItem.showGrid(True, True, 0.7)
        self.maxFFT=0
        self.maxPCM=0
        self.ear = SWHear.SWHear()
        self.ear.stream_start()

    def update(self):
        if not self.ear.data is None and not self.ear.fft is None:
            pcmMax=np.max(np.abs(self.ear.data))
            if pcmMax>self.maxPCM:
                self.maxPCM=pcmMax
                self.grPCM.plotItem.setRange(yRange=[-pcmMax,pcmMax])
            if np.max(self.ear.fft)>self.maxFFT:
                self.maxFFT=np.max(np.abs(self.ear.fft))
                self.grFFT.plotItem.setRange(yRange=[0,self.maxFFT])
            self.pbLevel.setValue(1000*pcmMax/self.maxPCM)
            pen=pyqtgraph.mkPen(color='b')
            self.grPCM.plot(self.ear.datax,self.ear.data,
                            pen=pen,clear=True)
            pen=pyqtgraph.mkPen(color='r')
            self.grFFT.plot(self.ear.fftx[:500],self.ear.fft[:500],
                            pen=pen,clear=True)
        QtCore.QTimer.singleShot(1, self.update) # QUICKLY repeat

if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    form = ExampleApp()
    form.show()
    form.update() #start with something
    app.exec_()
    print("DONE")

```

This project uses a gutted version of the SWHEar class which I still haven't released on githib yet. It will likely have its own project folder. For now, take this project with a grain of salt. The primary advantage of this class is that it makes it easy to use PyAudio to automatically detect input sound cards, channels, and sample rates which are likely to succeed without requiring the user to enter any information.

All files used for this project are [in a GitHub folder](https://github.com/swharden/Python-GUI-examples/tree/master/2016-07-37_qt_audio_monitor)

### Audio Visualizer Screenlet

2016-09-05: Okko adapted this project into a screenlet (cross platform) which also includes an installer for Windows: https://github.com/ninlith/audio-visualizer-screenlet

<div class="text-center img-border">

[![](widget_thumb.jpg)](widget.png)

</div>
Pages