⚠️ WARNING: This page is obsolete
Articles typically receive this designation when the technology they describe is no longer relevant, code
provided is later deemed to be of poor quality, or the topics discussed are better presented in future
articles. Articles like this are retained for the sake of preservation, but their content should be
critically assessed.
This page documents the design and build of a small device to interface a modern fiber-coupled DPSS laser with old scientific hardware designed to control mechanical relays. This project involves analog and digital circuitry, microcontrollers, and lasers, and it turned out to be a pretty cool build so I’m sharing the design and construction process online in case it will be helpful for others or even my future self.
The existing hardware I must interface is made by Coulbourn Instruments and is essentially just a large multi-channel computer-controlled DAC/ADC. It does its job well (turning lights on and off, recording button presses, etc.), but this new task requires millisecond resolution and modulation patterns which lies outside the specs of this system and software. My goal is to interface a free output line of this old hardware and use it to signal to a new device I build to activate the laser to produce a pulsed pattern. This way there would be no modification to any existing equipment, and no software to install. Further, since this hardware isn’t mine, I don’t like the idea of permanently modifying it (or even risking breaking it by designing something which could damage it by connecting to it). The specific goal is to allow the existing software to cause the laser to fire 20 ms pulses at 15 Hz for a few dozen cycles of 5s on, 5s off. It’s also important to have some flexibility to reprogram the high speed laser stimulation pattern in the future. Experiments are already underway and I need this device to be complete within a couple of days! As much as I’d love to go to the internet and order the perfect cheap components, make a proper PCB, and have a beautiful build completed in a month, my goal is to build this over a weekend using only using parts I already have at my home.
Probing the existing hardware revealed many surprises. After inspecting the existing hardware setup I found an auxiliary output which could be controlled by software. This AUX port has a frustratingly rare connector 1mm dual keyhole touchproof connector which I couldn’t buy in bulk on eBay or Amazon, and couldn’t figure out the part numbers of on Mouser or Digikey. The manual even says “you may find it convenient to fit them with CI-type connectors” which makes me wonder why it wasn’t designed this way in the first place! Luckily the laboratory had an old (broken) device with that connector on it they said I could use for this build. After plugging in the connector, I used a volt meter to measure the output. To my surprise, it wasn’t a TTL signal! I expected to see my volt meter read 5V, but it read 28V! After consulting the manual I found mention of this: “Graphic State Notation software is designed for use with our Habitest animal-behavior-analysis environments or any other animal-behavior-testing apparatus that operates on the industry-standard 28-Volt control voltage.” I was surprised that 28V signals is a standard for any industry.
Control voltages are negative! Elsewhere in the manual I found the phrase “The power base is capable of delivering 8 Amps of -28 VDC” which made me question the voltage reading I took earlier. The voltmeter showed 28V, but that’s the difference between one keyhole connector output and the other. It became apparent that it really may be 0V (GND) and -28mV (an even more curious “industry standard”). I wondered if connecting the negative terminal to ground would destroy the unit (think about how easy this would be to do! If it were a TTL signal, the first thing you would do is connect the negative terminal to ground and start sampling the positive terminal). There was even talk of me interfacing with a different output port (which I hadn’t probed, so I didn’t know the voltage). Moving forward, I realized I had to tread very carefully. Doing something like connecting two grounds together could permanently damage this system!
Optical isolation should be used as a caution. Not really knowing if I should design to expect a TTL signal, a +28V signal, or a -28V signal, I decided to design a circuit to accommodate all of the above, while achieving total electrical discontinuity from whatever circuit I develop. I’m going to accomplish this using an opto-isolator on the input. I drew the schematic using KiCad as I built the board manually using through-hole construction. I considered laying-out a PCB (I have most of these components in SMT form factors too) but I knew I wouldn’t manage a one-weekend turnaround if I went that direction.
- The input should be able to accomodate any signal (TTL, CMOS, 28V, etc)
- The input is totally isolated electrically, so this should be very safe on the hardware
- The microcontroller is a socketed ATTiny85 which I programmed with a Bus Pirate.
- I decided to rely on a crystal rather than the internal RC clock to improve temporal precision of the output signal. A 11.0592 MHz crystal was chosen because I already had an abundance of them (they’re perfect for serial communication at all common baud rates). Any crystal could be used, as long as its frequency is defined in software.
- Capacitors were added more to ensure oscillation initiates than to bring down the oscillation frequency. (I’m told that omitting them may cause a case where the crystal doesn’t resonate as well, but I’ve never found this in my personal experience.) A good note on microcontroller clocks is in a Microchip PIC application note.
- I included a “test” button (momentary switch) to simulate having an input signal.
- Note that R1 must be able to handle the current applied to it. It was mistakenly designed as 1k, and later replaced with 10k. See the bodge note at the bottom of this post for details.
- Forward protection diodes on the input could protect accidental reverse polarity
- Adding an ICSP header would prevent de-socketing of the MCU if reprogramming is desired
- The BNC output is directly from a MCU pin. A transistor-buffered output design could be considered to deliver higher current for more confident control of the laser input.
Because there is a possibility that a different output (laser control) pattern may be desired in the future, I considered whether or not I should make the output pattern user-configurable. Adding buttons, a display, and designing a menu system in software would be a lot of work and it’s unclear if a protocol change will ever actually be required, so I concluded that I’m going to build this device to the specific task at hand and extended it later if/when needed. If the end user eventually wants the ability to modulate the pattern on their own, the device they ask for would be a very different one than the one I was tasked to create. Since the current pattern is burned into a microchip, a compromise is that I could have new patterns burned into new microchips, and the end-user could change the chip (as long as it’s an infrequent occurrence).
EDIT: About a year later a modification was indeed required (something like changing 15 Hz to 40 Hz), and the solution was to burn these two patterns into two microchips. Since they’re socketed, they’re swappable (albiet a limited number of times). This design worked-out well.
Is a microcontroller/crystal overkill for a system which could be accomplished using an analog circuit? Generating 20 ms pulses at 15 Hz sounds like an easy task for a 555 timer without the need for digital circuitry. I considered this for a while, but concluded that the advantage of the MCU (crystal-disciplined time precision) outweighed the convenience of a purely analog circuit. A 555 timer in astable / multi-vibrator configuration would mostly get the job done, but you would either (1) only allow one output pattern and rely on precision passive components (which I don’t have on hand), or (2) allow the end-user to adjust duty/frequency with potentiometers (which would require the output to be quantitatively monitored on an oscilloscope). There’s also the issue where RC oscillators can be highly temperature sensitive, so the microcontroller/crystal design seemed like a more robust solution for reliable and defined behavior.
I started the build by measuring/marking drill points. I used a Dremel drill press to make the holes, then smoothed them with a deburring tool. I also drilled holes in the base of the enclosure.
I had an enclosure ready to go. I always buy enclosures in bulk, and even though nice ones tend to be expensive, having them on hand encourages me to build devices as I think of them, rather than making flaky hardware which I have a history of doing which sometimes borders on ridiculousness. I usually stock unfinished Hammond diecast aluminum enclosures for making quick RF projects, and boxes with feet and side vents for fancier projects, but for this task I decided to enclose everything inside a typical (but a little more costly) aluminum enclosure ordered in bulk from eBay. I love using low current LEDs, and I started going with frosted instead of clear LEDs because they’re easier on the eyes. Also, I switched to mostly 3mm LEDs instead of 5MM because they look a little nicer in these small enclosures with small black bezels.
I used nicer perfboard with plated holes to build this circuit. Normally I use cheap perfboard with little copper rings glued to one side because it’s faster to solder (the copper is so thin it heats quickly), but it’s not always a good long-term solution because the copper pads have a tendency to un-stick. I rarely use this nicer perfboard (it is more expensive), but it’s nice to have for more reliable builds.
I marked areas of optical isolation with a black marker. This makes it obvious where the potentially dangerous, potentially high-voltage (well, higher than TTL), potentially negative input comes in. No wires or connections should invade this space on the board. The special connector which will connect this device to the scientific hardware is on-site, and I’ll have to solder it at the time of delivery/installation. I left an extra hole in the back to accommodate this wire. I didn’t have any rubber grommets, but I suspect this build may have benefitted from one.
Once it was all together, the device seemed to perform well. The test button on the back made it easy to inspect the output. I build so many RF circuits that I instinctively reached for a 50-ohm terminator, but the square wave quickly transformed into shark fins (RC curves) reminding me that and I realized 50 ohms is far too low impedance. If it’s a TTL signal, let’s assume it’s virtually infinite impedance. I was uncertain whether or not I should drive the output directly with a microcontroller pin. There may be a need for a buffered output. The microcontroller’s datasheet suggests limiting its current to 20 mA per pin (requiring termination of no less than 250 Ohms at 5V), and I’m going to move forward assuming the laser TTL input doesn’t sink much current.
How should TTL timing be controlled in software? I want this device to perform identically over long periods of time, favoring reliability and consistency over microsecond time precision. To achieve 15 Hz of 20ms pulses I need 20ms on and 46.666667 ms off. I could probably get pretty close if I wanted to, but I rounded it to 20 ms on and 46 ms off. This gives time for the instruction cycles toggling the output pins to occur (although it’s on an order of magnitude faster time scale), which slightly biases the time in the right direction. I considered adding a _delay_us(666) after the _delay_ms(46) but I’m satisfied with it this knowing it’s within 1% accuracy of 15 Hz and that precision is locked to that of the crystal (around 10 ppm, or 0.001%).
Hard-coded _delay_ms() is somewhat inelegant. The use of AVR timers should probably be considered as an alternative strategy. Here’s an awesome guide on the topic, and here’s another. Timers would be preferred if I wanted the program code of the microcontroller to be free to do other things like drive menus or multiplex a display. Since this task is simply required generation of a TTL output pattern (and nothing more), the hard-coded delay method met this need, but I found it useful to consider the asynchronous strategies:
-
Timers: Set the timer to overflow every 1 ms. On overflow, a counting variable would be incremented and a function would be called to determine what to do. At pre-programmed time points (with respect to the counting variable), the output pin would be toggled, or the counting variable would be reset.
-
Output compare registers: Utilize the built-in OCR (output compare register) to turn the output signal on and off. Set the timer to overflow at 15 Hz, turning the output on. Set the OCR (to the fractional point between 0 and the maximum timer value) such that when it is passed, the output is turned off. This way 15 Hz, 20 ms pulses would be continuously running without any code being executed. Input sensing could simply enable and disable the timer.
-
Input interrupts: Why stop at timers? Polling the input pin for a TTL signal puts the chip in an infinite loop. Relying on the AVR’s external (pin change) hardware interrupts could eliminate this as well. I always rely heavily on the datasheet when setting these interrupts.
These alternative implementations will be useful in the future if a more accurate time source is desired, an advanced display is added, or menus are implemented which would benefit from letting the pulsing output operate in the background while accepting user input. For now, I’m happy with the blocking delay strategy.
After I was satisfied with construction, I started labeling the enclosure. I want to tip my hat to Onno Hoekstra on this one, as his webpage and some email correspondence helped me realize how good clear labels look when outlined and applied to aluminum enclosures. I’m using a DYMO LetraTag LT-100T Plus label maker and clear tape. It’s important to enable the black outline around the text, then I cut carefully slightly outside the outline with regular scissors, and apply the labels with a hobby knife or razor blade.
The morning I delivered the product and added the final proprietary connector which I didn’t have at home. It’s an inelegant knot-retained configuration, but I think it’ll get the job done! It was a surprisingly rare, fully shielded, keyhole-shaped touchproof connector apparently used only in medical applications. At this point, I’m thinking this connector was chosen to (A) protect the user from accidentally shorting a 28V 8A power source (that’s over 200 watts!), (B) to prevent you from damaging the equipment by plugging in something that doesn’t belong (could you imagine what would happen if this -28V high current source had a BNC connector and you plugged this into something expecting a 5V TTL input?), and (C) prevent you from plugging in anything that wasn’t made by this company. The last option is most likely a well-intentioned attempt by the manufacturer to prevent customers from damaging their product rather than the company trying to maintain its status as a sole distributor of accessories, but it makes me wonder. I would have preferred power pole sockets, molded power connectors like those on motherboards, or even barrel connectors! Surely there’s a more standard touchproof connector for moderate voltage/currents than this bizarre keyhole connector.
I plugged the device in to the computer, attached the laser, and it worked immediately! I wasn’t really surprised that it worked (I tested it extensively at home), but it still felt good to watch the blue laser trigger as it was supposed to. Another interesting one-off project is complete, and have some interesting photos and notes about the build to share on this website. I hope this little device continues to do its job well for many years in its new laboratory home.
#define F_CPU 11059200UL
#include <avr/io.h>
#include <util/delay.h>
int main (void){
DDRB=(1<<PB0); // set port B pin 0 as an output
PORTB=0; // pull all pins low
while(1){
while((PINB&(1<<PB2))==0){} // do nothing while the input is low
PORTB=(1<<PB0); // TTL ON
_delay_ms(20); // stay on for 20ms
PORTB&=~(1<<PB0); // TTL OFF
_delay_ms(46); // stay off for 46ms
}
}
Here’s the batch script I used to compile and load the code onto the microcontroller. I compiled the code with AVR-GCC and copied it onto the microcontroller with a Bus Pirate. Note also that I’m setting the fuses to respect an external oscillator.
@echo off
del *.elf
del *.hex
avr-gcc -mmcu=attiny85 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c buspirate -p attiny85 -P com3 -e -U flash:w:main.hex
avrdude -c buspirate -p attiny85 -P com3 -U lfuse:w:0xff:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
pause
After a few days I got an email from someone concerned about the current handling capability of the front-end of the circuit. It was noted that a standard 1/4 watt resistor may not be suitable for R1, as a 28V potential would stress it beyond its specs. With 28V applied, R1 (a quarter-watt resistor) would experience P=IE=28mA*28V=784mW of current! It might last (especially if pulsed), but it also might fail with time. The advantage of the R1/D1/R2 system is that the output current will be identical across a wide range of input voltages. The disadvantage is that it’s hard to predict how much current R1 needs to be expected to tolerate. I could have placed five 4.7k resistors in parallel to replace R1 (this would let me handle over 1 watt of input power), but I instead simply upped it from 1kOhm to 10kOhm. This further reduced the current that the opto-isolator sees (now only about 0.2 mA) but it seems to work still. I’m satisfied with this modification, but a little disappointed I didn’t catch it sooner. Note that the new input resistor (a 10k R1) should now only have to dissipate about 80mW, well within its specs.
What is the minimum current required to confidently activate the optoisolator? I considered that a 10K input resistor on 28V would only allow 2.8 mA to pass, and I was unsure if this would reliably activate the optocoupler. Considering only 3.3V will persist after the zener (a ~11.7% current retaining ratio, if that’s valid), I figured that a best 330µA were passing through the opto-isolator. That seems outside of the specs of the device, because their datasheet graphs always start at 1mA. I decided to run some tests at my home to see how this device performed with lower input currents. I determined that a 10k resistor still works with 5V (500 µA into the device), but checking the output on the oscilloscope I realized that the device operates only partially, and slowly at that low voltage/current. The darlington transistor configuration is very high gain, which is the only reason this works at all, but such low currents are sensitive to parasitic capacitance and infiltrating RF currents. Because of this it seems the chip takes a few ms to activate and deactivate. Since this application only uses 5s on and 5s off inputs, it’s fine for this application, but I wouldn’t expect high speed pulsing of the input signal to work well. Furthermore, in my breadboard I realized I was getting funny output currents oscillating around 60Hz, which made me suspicious that the device was picking up AC somehow. I realized it was from pin 6 (the exposed darlington base). Normally the LED is so strong is blasts the device fully on or off, but hovering on the edge like this, that pin is picking up signals that influence the open state of the transistor inside. Since it’s not connected to anything anyway, I cut the pin off as close to the microchip as I could, and noticed an instant improvement in 60Hz rejection. In conclusion, I wouldn’t try to reliably drive an opto-isolator with a complex pattern using less than 1 mA, but it seems to work well for simple on/off control.
This is the output of the unmodified H11B1:
This is the output of the H11B1 with the base pin removed:
Conclusion: This was an interesting build! I’m satisfied with the use of optical isolation here to adapt two incompatible systems with unknown/unspecified input/output properties, and the lack of electrical connections between the inputs and outputs gave me high confidence to experiment along the way.
⚠️ WARNING: This page is obsolete
Articles typically receive this designation when the technology they describe is no longer relevant, code
provided is later deemed to be of poor quality, or the topics discussed are better presented in future
articles. Articles like this are retained for the sake of preservation, but their content should be
critically assessed.
Python’s “batteries included” nature makes it easy to interact with just about anything… except speakers and a microphone! As of this moment, there still are not standard libraries which which allow cross-platform interfacing with audio devices. There are some pretty convenient third-party modules, but I hope in the future a standard solution will be distributed with python. I appreciate the differences of Linux architectures such as ALSA and OSS, but toss in Windows and MacOS in the mix and it gets to be a huge mess. For Linux, would I even need anything fancy? I can run “cat file.wav > /dev/dsp
” from a command prompt to play audio. There are some standard libraries for operating system specific sound (i.e., winsound), but I want something more versatile. The official audio wiki page on the subject lists a small collection of third-party platform-independent libraries. After excluding those which don’t support microphone access (the ultimate goal of all my poking around in this subject), I dove a little deeper into sounddevice and PyAudio. Both of these I installed with pip (i.e., pip install pyaudio
)
I really like the structure and documentation of sounddevice, but I decided to keep developing with PyAudio for now. Sounddevice seemed to take more system resources than PyAudio (in my limited test conditions: Windows 10 with very fast and modern hardware, Python 3), and would audibly “glitch” music as it was being played every time it attached or detached from the microphone stream. I tried streaming, but after about an hour I couldn’t get clean live access to the microphone without glitching audio playback. Furthermore, every few times I ran this script it crashed my python kernel! I very rarely see this happening. iPython complained: “It seems the kernel died unexpectedly. Use ‘Restart kernel’ to continue using this console” and I eventually moved back to PyAudio. For a less “realtime” application, sounddevice might be a great solution. Here’s the minimal case sounddevice script I tested with (that crashed sometimes). If you have a better one to do live high-speed audio capture, let me know!
import sounddevice #pip install sounddevice
for i in range(30): #30 updates in 1 second
rec = sounddevice.rec(44100/30)
sounddevice.wait()
print(rec.shape)
Here’s a simple demo to show how I get realtime microphone audio into numpy arrays using PyAudio. This isn’t really that special. It’s a good starting point though. Note that rather than have the user define a microphone source in the python script (I had a fancy menu system handling this for a while), I allow PyAudio to just look at the operating system’s default input device. This seems like a realistic expectation, and saves time as long as you don’t expect your user to be recording from two different devices at the same time. This script gets some audio from the microphone and shows the values in the console (ten times).
import pyaudio
import numpy as np
CHUNK = 4096 # number of data points to read at a time
RATE = 44100 # time resolution of the recording device (Hz)
p=pyaudio.PyAudio() # start the PyAudio class
stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK) #uses default input device
# create a numpy array holding a single read of audio data
for i in range(10): #to it a few times just to see
data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
print(data)
# close the stream gracefully
stream.stop_stream()
stream.close()
p.terminate()
I tried to push the limit a little bit and see how much useful data I could get from this console window. It turns out that it’s pretty responsive! Here’s a slight modification of the code, made to turn the console window into an impromptu VU meter.
import pyaudio
import numpy as np
CHUNK = 2**11
RATE = 44100
p=pyaudio.PyAudio()
stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK)
for i in range(int(10*44100/1024)): #go for a few seconds
data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
peak=np.average(np.abs(data))*2
bars="#"*int(50*peak/2**16)
print("%04d %05d %s"%(i,peak,bars))
stream.stop_stream()
stream.close()
p.terminate()
The results are pretty good! The advantage here is that no libraries are required except PyAudio. For people interested in doing simple math (peak detection, frequency detection, etc.) this is a perfect starting point. Here’s a quick cellphone video:
I’ve made realtime audio visualization (realtime FFT) scripts with Python before, but 80% of that code was creating a GUI. I want to see data in real time while I’m developing this code, but I really don’t want to mess with GUI programming. I then had a crazy idea. Everyone has a web browser, which is a pretty good GUI… with a Python script to analyze audio and save graphs (a lot of them, quickly) and some JavaScript running in a browser to keep refreshing those graphs, I could get an idea of what the audio stream is doing in something kind of like real time. It was intended to be a hack, but I never expected it to work so well! Check this out…
Here’s the python script to listen to the microphone and generate graphs:
import pyaudio
import numpy as np
import pylab
import time
RATE = 44100
CHUNK = int(RATE/20) # RATE / number of updates per second
def soundplot(stream):
t1=time.time()
data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
pylab.plot(data)
pylab.title(i)
pylab.grid()
pylab.axis([0,len(data),-2**16/2,2**16/2])
pylab.savefig("03.png",dpi=50)
pylab.close('all')
print("took %.02f ms"%((time.time()-t1)*1000))
if __name__=="__main__":
p=pyaudio.PyAudio()
stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK)
for i in range(int(20*RATE/CHUNK)): #do this for 10 seconds
soundplot(stream)
stream.stop_stream()
stream.close()
p.terminate()
Here’s the HTML file with JavaScript to keep reloading the image…
<html>
<script language="javascript">
function RefreshImage(){
document.pic0.src="https://swharden.com/static/2016/07/19/03.png?a=" + String(Math.random()*99999999);
setTimeout('RefreshImage()',50);
}
</script>
<body onload="RefreshImage()">
<img name="pic0" src="https://swharden.com/static/2016/07/19/03.png">
</body>
</html>
I couldn’t believe my eyes. It’s not elegant, but it’s kind of functional!
Why stop there? I went ahead and wrote a microphone listening and processing class which makes this stuff easier. My ultimate goal hasn’t been revealed yet, but I’m sure it’ll be clear in a few weeks. Let’s just say there’s a lot of use in me visualizing streams of continuous data. Anyway, this class is the truly terrible attempt at a word pun by merging the words “SWH”, “ear”, and “Hear”, into the official title “SWHear” which seems to be unique on Google. This class is minimal case, but can be easily modified to implement threaded recording (which won’t cause the rest of the functions to hang) as well as mathematical manipulation of data, such as FFT. With the same HTML file as used above, here’s the new python script and some video of the output:
import pyaudio
import time
import pylab
import numpy as np
class SWHear(object):
"""
The SWHear class is made to provide access to continuously recorded
(and mathematically processed) microphone data.
"""
def __init__(self,device=None,startStreaming=True):
"""fire up the SWHear class."""
print(" -- initializing SWHear")
self.chunk = 4096 # number of data points to read at a time
self.rate = 44100 # time resolution of the recording device (Hz)
# for tape recording (continuous "tape" of recent audio)
self.tapeLength=2 #seconds
self.tape=np.empty(self.rate*self.tapeLength)*np.nan
self.p=pyaudio.PyAudio() # start the PyAudio class
if startStreaming:
self.stream_start()
### LOWEST LEVEL AUDIO ACCESS
# pure access to microphone and stream operations
# keep math, plotting, FFT, etc out of here.
def stream_read(self):
"""return values for a single chunk"""
data = np.fromstring(self.stream.read(self.chunk),dtype=np.int16)
#print(data)
return data
def stream_start(self):
"""connect to the audio device and start a stream"""
print(" -- stream started")
self.stream=self.p.open(format=pyaudio.paInt16,channels=1,
rate=self.rate,input=True,
frames_per_buffer=self.chunk)
def stream_stop(self):
"""close the stream but keep the PyAudio instance alive."""
if 'stream' in locals():
self.stream.stop_stream()
self.stream.close()
print(" -- stream CLOSED")
def close(self):
"""gently detach from things."""
self.stream_stop()
self.p.terminate()
### TAPE METHODS
# tape is like a circular magnetic ribbon of tape that's continously
# recorded and recorded over in a loop. self.tape contains this data.
# the newest data is always at the end. Don't modify data on the type,
# but rather do math on it (like FFT) as you read from it.
def tape_add(self):
"""add a single chunk to the tape."""
self.tape[:-self.chunk]=self.tape[self.chunk:]
self.tape[-self.chunk:]=self.stream_read()
def tape_flush(self):
"""completely fill tape with new data."""
readsInTape=int(self.rate*self.tapeLength/self.chunk)
print(" -- flushing %d s tape with %dx%.2f ms reads"%\
(self.tapeLength,readsInTape,self.chunk/self.rate))
for i in range(readsInTape):
self.tape_add()
def tape_forever(self,plotSec=.25):
t1=0
try:
while True:
self.tape_add()
if (time.time()-t1)>plotSec:
t1=time.time()
self.tape_plot()
except:
print(" ~~ exception (keyboard?)")
return
def tape_plot(self,saveAs="03.png"):
"""plot what's in the tape."""
pylab.plot(np.arange(len(self.tape))/self.rate,self.tape)
pylab.axis([0,self.tapeLength,-2**16/2,2**16/2])
if saveAs:
t1=time.time()
pylab.savefig(saveAs,dpi=50)
print("plotting saving took %.02f ms"%((time.time()-t1)*1000))
else:
pylab.show()
print() #good for IPython
pylab.close('all')
if __name__=="__main__":
ear=SWHear()
ear.tape_forever()
ear.close()
print("DONE")
I don’t really intend anyone to actually do this, but it’s a cool alternative to recording a small portion of audio, plotting it in a pop-up matplotlib window, and waiting for the user to close it to record a new fraction. I had a lot more text in here demonstrating real-time FFT, but I’d rather consolidate everything FFT related into a single post. For now, I’m happy pursuing microphone-related python projects with PyAudio.
Use Numpy’s FFT() and FFTFREQ() to turn the linear data into frequency. Set that target and grab the FFT value corresponding to that frequency. I haven’t tested this to be sure it’s working, but it should at least be close…
import pyaudio
import numpy as np
np.set_printoptions(suppress=True) # don't use scientific notation
CHUNK = 4096 # number of data points to read at a time
RATE = 44100 # time resolution of the recording device (Hz)
TARGET = 2100 # show only this one frequency
p=pyaudio.PyAudio() # start the PyAudio class
stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK) #uses default input device
# create a numpy array holding a single read of audio data
for i in range(10): #to it a few times just to see
data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
fft = abs(np.fft.fft(data).real)
fft = fft[:int(len(fft)/2)] # keep only first half
freq = np.fft.fftfreq(CHUNK,1.0/RATE)
freq = freq[:int(len(freq)/2)] # keep only first half
assert freq[-1]>TARGET, "ERROR: increase chunk size"
val = fft[np.where(freq>TARGET)[0][0]]
print(val)
# close the stream gracefully
stream.stop_stream()
stream.close()
p.terminate()
If your goal is to determine which frequency is producing the loudest tone, use this function. I also added a few lines to graph the output in case you want to observe how it operates. I recommend testing this script with a tone generator, or a YouTube video containing tones of a range of frequencies like this one.
import pyaudio
import numpy as np
import matplotlib.pyplot as plt
np.set_printoptions(suppress=True) # don't use scientific notation
CHUNK = 4096 # number of data points to read at a time
RATE = 44100 # time resolution of the recording device (Hz)
p=pyaudio.PyAudio() # start the PyAudio class
stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True,
frames_per_buffer=CHUNK) #uses default input device
# create a numpy array holding a single read of audio data
for i in range(10): #to it a few times just to see
data = np.fromstring(stream.read(CHUNK),dtype=np.int16)
data = data * np.hanning(len(data)) # smooth the FFT by windowing data
fft = abs(np.fft.fft(data).real)
fft = fft[:int(len(fft)/2)] # keep only first half
freq = np.fft.fftfreq(CHUNK,1.0/RATE)
freq = freq[:int(len(freq)/2)] # keep only first half
freqPeak = freq[np.where(fft==np.max(fft))[0][0]]+1
print("peak frequency: %d Hz"%freqPeak)
# uncomment this if you want to see what the freq vs FFT looks like
#plt.plot(freq,fft)
#plt.axis([0,4000,None,None])
#plt.show()
#plt.close()
# close the stream gracefully
stream.stop_stream()
stream.close()
p.terminate()
import pyaudio
import numpy as np
maxValue = 2**16
p=pyaudio.PyAudio()
stream=p.open(format=pyaudio.paInt16,channels=2,rate=44100,
input=True, frames_per_buffer=1024)
while True:
data = np.fromstring(stream.read(1024),dtype=np.int16)
dataL = data[0::2]
dataR = data[1::2]
peakL = np.abs(np.max(dataL)-np.min(dataL))/maxValue
peakR = np.abs(np.max(dataR)-np.min(dataR))/maxValue
print("L:%00.02f R:%00.02f"%(peakL*100, peakR*100))
Output
L:47.26 R:45.17
L:47.55 R:45.63
L:49.44 R:45.98
L:45.27 R:49.80
L:44.39 R:45.75
L:47.50 R:46.96
L:41.49 R:42.64
L:42.95 R:41.39
L:49.56 R:49.62
L:48.29 R:48.80
L:45.03 R:47.62
L:47.99 R:49.35
L:41.58 R:49.21
Or with a tweak…
import pyaudio
import numpy as np
maxValue = 2**16
bars = 35
p=pyaudio.PyAudio()
stream=p.open(format=pyaudio.paInt16,channels=2,rate=44100,
input=True, frames_per_buffer=1024)
while True:
data = np.fromstring(stream.read(1024),dtype=np.int16)
dataL = data[0::2]
dataR = data[1::2]
peakL = np.abs(np.max(dataL)-np.min(dataL))/maxValue
peakR = np.abs(np.max(dataR)-np.min(dataR))/maxValue
lString = "#"*int(peakL*bars)+"-"*int(bars-peakL*bars)
rString = "#"*int(peakR*bars)+"-"*int(bars-peakR*bars)
print("L=[%s]\tR=[%s]"%(lString, rString))
⚠️ WARNING: This page is obsolete
Articles typically receive this designation when the technology they describe is no longer relevant, code
provided is later deemed to be of poor quality, or the topics discussed are better presented in future
articles. Articles like this are retained for the sake of preservation, but their content should be
critically assessed.
After using the AVR-ISP mkII for years (actually the cheap eBay knock-offs) to program ATMEL AVR microcontrollers, today I gave the Bus Pirate a shot. Far more than just a microcontroller programmer, this little board is basically a serial interface to basic microcontroller peripherals. In a nutshell, you plug it in via USB and it looks like a serial port which has a command-line interface that lets you do things like turn pins on and off, perform voltage measurements, and it naively supports bidirectional use of common protocols like I2C, SPI, UART, and even HD44780 series LCDs. Note that although you could directly interface with the Bus Pirate using HyperTerminal, I recommend using TeraTerm. It can supply voltages (3.3V and 5V) to power small circuits, and if current draw is too high (indicating something is hooked-up wrong) it automatically turns the supply off. So clever! At <$30, it’s a cool tool to have around. In addition, it’s naively supported as an AVR programmer by AVRDUDE. Although I could write assembly to perform tasks, I almost always write in C for the convenience. For my reference (and that of anyone who may want to do something similar), I’m posting the simplest-case method I use to program AVR microcontrollers with the Bus Pirate on Windows (noting that Linux would be nearly identical). I also wrote a Python script to connect with the Bus Pirate and run simple commands (which turns the power supply on and report the voltage of the VCC line immediately after programming completes). Yes, there are fancy packages that allow you to interact with Bus Pirate from Python, but ___the advantage of my method is that it runs from native Python libraries! ___To get this all up and running for yourself, just install WinAVR (which supplies AVRDUDE and AVR-GCC) and Python 3. I assume this code will work just as well on Python 2, but haven’t tried.
To ensure my Bus Pirate is working properly, I start off by running the Bus Pirate’s built-in test routine. For full details read the guide. It just involves connecting two pairs of pins together as shown in the picture here, connecting to the Bus Pirate with the serial terminal, and running the command “~”. It will output all sorts of useful information. Once I know my hardware is up and running, I’m good to continue.
Here’s the code which runs on the microcontroller to twiddle all the pins (saved as main.c). Note that my MCU is an ATTiny85. I’m using standard clock settings (internal RC clock, 8MHz), but if I wanted to modify fuses to do things like use an external clock source or crystal, I’d calculate them with engbedded’s handy dandy fuse calculator (which also shows AVRdude arguments needed to make the change!).
#define F_CPU (8000000UL)
#include <avr/io.h>
#include <util/delay.h>
int main (void)
{
DDRB = 255;
while(1)
{
PORTB ^= 255;
_delay_ms(500);
}
}
To compile the code and program the MCU with it, I always have a bash script in the same folder that I can double-click on to delete old compiled files (so we don’t accidentally re-program our MCU with old code), compile main.c, and load it onto the MCU using the Bus Pirate. You may have to change COM3 to reflect the com port of your Bus Pirate. Note that it is required that you disconnect other terminals from the Bus Pirate before doing this, otherwise you’ll get an “access denied” error.
@echo off
del *.elf
del *.hex
avr-gcc -mmcu=attiny85 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c buspirate -p attiny85 -P com3 -e -U flash:w:main.hex
python up.py
Although the programmer briefly supplies my MCU with power from the +5V pin, it’s cut after programming completes. Rather than manually re-opening my terminal program, re-connecting with the bus pirate, re-setting the mode (command “m”) to something random (DIO, command “9”), and re-enableing voltage output (command “W”) just to see my LED blink, I want all that to be automated. Thanks python for making this easy. The last line calls “up.py”. This fancy script even outputs the voltage of the VCC line after it’s turned on!
import serial
BUSPIRATE_PORT = 'com3' #customize this! Find it in device manager.
def send(ser,cmd):
"""send the command and listen to the response."""
ser.write(str(cmd+'\n').encode('ascii')) # send our command
for line in ser.readlines(): # while there's a response
print(line.decode('utf-8').strip()) # show it
ser=serial.Serial(BUSPIRATE_PORT, 115200, timeout=1) # is com free?
assert ser.isOpen() #throw an exception if we aren't connected
send(ser,'#') # reset bus pirate (slow, maybe not needed)
send(ser,'m') # change mode (goal is to get away from HiZ)
send(ser,'9') # mode 9 is DIO
send(ser,'W') # turn power supply to ON. Lowercase w for OFF.
send(ser,'v') # show current voltages
ser.close() # disconnect so we can access it from another app
print("disconnected!") # let the user know we're done.
When “burn.cmd” is run, the code is compiled and loaded, the power supply is turned on (and killed if too much current is drawn!), and the voltage on VCC is reported. The output is:
C:\Users\scott\Documents\important\AVR\2016-07-13 ATTiny85 LEDblink>burn.cmd
Detecting BusPirate...
**
** Bus Pirate v3a
** Firmware v5.10 (r559) Bootloader v4.4
** DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
** http://dangerousprototypes.com
**
BusPirate: using BINARY mode
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.12s
avrdude: Device signature = 0x1e930b
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (84 bytes):
Writing | ################################################## | 100% 3.12s
avrdude: 84 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Intel Hex
avrdude: input file main.hex contains 84 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 2.72s
avrdude: verifying ...
avrdude: 84 bytes of flash verified
avrdude: safemode: Fuses OK
avrdude done. Thank you.
#
RESET
Bus Pirate v3a
Firmware v5.10 (r559) Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
HiZ>
m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)
(1)>
9
Ready
DIO>
W
Power supplies ON
DIO>
v
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX CLK MOSI CS MISO
P P P I I I I I I I
GND 3.17V 5.00V 0.00V 0.00V L L L H L
DIO>
disconnected!
This is a minimal-case scenario, but can be obviously expanded to perform some complicated tasks! For example, all commands could be run from a single python program. Considering the Bus Pirate’s ability to communicate with so many different protocols (I2C, 2-write, etc.), being able to naively control it from Python without having to install special additional libraries will certainly prove to be convenient.
PS: I noted there is a surprising delay when initializing programming the AVR with the bus pirate. The process hangs for about 10 seconds after the bus pirate introduces itself with the welcome message, then seems to resume at full speed writing to the flash of the microchip. After a bit of Googling, I believe the delay is due to the Bus Pirate slowly bit-banging SPI to initialize the programming sequence. The AVR has rich SPI functionality, some of which involves its own programming. Satisfied with this answer for now, I’m not going to try to speed it up. It’s a little annoying, but not too bad that I won’t use this to program my AVRs.
⚠️ WARNING: This page is obsolete
Articles typically receive this designation when the technology they describe is no longer relevant, code
provided is later deemed to be of poor quality, or the topics discussed are better presented in future
articles. Articles like this are retained for the sake of preservation, but their content should be
critically assessed.
December 23 is Festivus! To commemorate the occasion, I have built a traditional Festivus pole with a couple added features. To my knowledge, this is the first electronic Festivus pole on the internet.
Festivus is a holiday comically celebrated as an alternative to the pressures of commercialism commonly associated with other winter holidays. Originating from the 1997 Seinfeld episode “The Strike”, the traditions of Festivus include demonstrating feats of strength, declaring common occurrences as Festivus miracles, airing of grievances, and of course the fabrication of a Festivus pole.
Over the years various Festivus poles (often made of beer cans) have been erected in government buildings alongside the nativity scene and menorah, including this year in my home state Florida (the video is a good laugh). Here, I show a Festivus pole I made made from individually illuminated diet coke cans which performs as a simple video game, controlled by a single button. The illuminated can scrolls up and down, and the goal is to push the button when the top can is lit. If successful, the speed increases, and the game continues! It’s hours of jolly good fun.
After playing at my workbench for a while, I figured out a way I could light-up individual coke cans. I drilled a dozen holes in each can (with a big one in the back), stuck 3 blue LEDs (wired in parallel with a 220-ohm current limiting resistor in series) in the can, and hooked it up to 12V. This was the motivation I needed to continue…
Now for the design. I found a junk box 12V DC wall-wart power supply which I decided to commandeer for this project. Obviously a microcontroller would be the simplest way to implement this “game”, and I chose to keep things as minimal as possible. I used a single 8-pin ATMEL ATTiny85 microcontroller ($1.67) which takes input from 1 push-button and sends data through two daisy-chained 74hc595 shift-registers ($0.57) to control base current of 2n3904 transistors ($.019) to illuminate LEDs which I had on hand (ebay, 1000 3mm blue LEDs, $7.50 free shipping). A LM7805 linear voltage regulator ($0.68) was used to bring the 12V to 5V, palatable for the microcontroller. Note that all prices are for individual units, and that I often buy in bulk from cheap (shady) vendors, so actual cost of construction was less.
To build the circuit, I used perf-board and all through-hole components. It’s a little messy, but it gets the job done! Admire the creative resistor hops connecting shift registers and microcontroller pins. A purist would shriek at such construction, but I argue its acceptability is demonstrated in its functionality.
The installation had to be classy. To stabilize the fixture, I used epoxy resin to cement a single coke can to an upside-down Pyrex dish (previously used for etching circuit boards in ferric chloride). I then used clear packaging tape to hold each successive illuminated can in place. All wires were kept on the back side of the installment with electrical tape. Once complete, the circuit board was placed beneath the Pyrex container, and the controller (a single button in a plastic enclosure connected with a telephone cord) was placed beside it.
It’s ready to play! Sit back, relax, and challenge your friends to see who can be the Festivus pole video game master!
A few notes about the code… The microcontroller ran the following C code (AVR-GCC) and is extremely simple. I manually clocked the shift registers (without using the chip’s serial settings) and also manually polled for the button press (didn’t even use interrupts). It’s about as minimal as it gets! What improvements could be made to this Festivus hacking tradition? We will have to wait and see what the Internet comes up with next year…
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/delay.h>
// PB2 data
// PB1 latch
// PB0 clock
// PB4 LED
// PB3 input button
volatile int speed=400;
volatile char canlit=0;
volatile char levelsWon=0;
char buttonPressed(){
char state;
state = (PINB>>PB3)&1;
if (state==0) {
PORTB|=(1<<PB4);
return 1;
}
else {
PORTB&=~(1<<PB4);
return 0;
}
}
void shiftBit(char newval){
// set data value
if (newval==0){PORTB&=~(1<<PB2);}
else {PORTB|=(1<<PB2);}
// flip clock
PORTB|=(1<<PB0);
PORTB&=~(1<<PB0);
}
void allOff(){
char i=0;
for(i=0;i<16;i++){
shiftBit(0);
}
updateDisplay();
}
void allOn(){
char i=0;
for(i=0;i<14;i++){
shiftBit(1);
}
updateDisplay();
}
void onlyOne(char pos){
if (pos>=8) {pos++;}
char i;
allOff();
shiftBit(1);
for (i=0;i<pos;i++){shiftBit(0);}
//if (pos>8) {shiftBit(0);} // because we skip a shift pin
updateDisplay();
}
void updateDisplay(){PORTB|=(1<<PB1);PORTB&=~(1<<PB1);}
void ledON(){PORTB|=(1<<PB4);}
void ledOFF(){PORTB&=~(1<<PB4);}
char giveChance(){
int count=0;
for(count=0;count<speed;count++){
_delay_ms(1);
if (buttonPressed()){return 1;}
}
return 0;
}
void strobe(){
char i;
for(i=0;i<50;i++){
allOn();_delay_ms(50);
allOff();_delay_ms(50);
}
}
char game(){
for(;;){
for(canlit=1;canlit<15;canlit++){
onlyOne(canlit);
if (giveChance()) {return;}
}
for(canlit=13;canlit>1;canlit--){
onlyOne(canlit);
if (giveChance()) {return;}
}
}
}
void levelWin(){
char i;
for(i=0;i<levelsWon;i++){
allOn();
_delay_ms(200);
allOff();
_delay_ms(200);
}
}
void levelLose(){
char i;
for(i=0;i<20;i++){
for(canlit=13;canlit>1;canlit--){
onlyOne(canlit);
_delay_ms(10);
}
}
}
void showSelected(){
char i;
for(i=0;i<20;i++){
onlyOne(canlit);
_delay_ms(50);
allOff();
_delay_ms(50);
}
}
void nextLevel(){
// we just pushed the button.
showSelected();
levelsWon++;
if (canlit==14) {
levelWin();
speed-=speed/5;
}
else {
levelLose();
speed=400;
levelsWon=0;
}
}
int main(void){
DDRB=(1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB4);
char i;
for(;;){
game();
nextLevel();
}
}
Programming: note that the code was compiled and programmed onto the AVR from a linux terminal using AvrDude. The shell script I used for that is here:
rm main
rm *.hex
rm *.o
echo "MAKING O"
avr-gcc -w -Os -DF_CPU=1000000UL -mmcu=attiny85 -c -o main.o main.c
echo "MAKING BINARY"
avr-gcc -w -mmcu=attiny85 main.o -o main
echo "COPYING"
avr-objcopy -O ihex -R .eeprom main main.hex
echo "PROGRAMMING"
avrdude -c usbtiny -p t85 -F -U flash:w:"main.hex":a -U lfuse:w:0x62:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
echo "DONE"
⚠️ WARNING: This page is obsolete
Articles typically receive this designation when the technology they describe is no longer relevant, code
provided is later deemed to be of poor quality, or the topics discussed are better presented in future
articles. Articles like this are retained for the sake of preservation, but their content should be
critically assessed.
I was recently presented with the need to rename a folder of images based on a timestamp. This way, I can keep saving new files in that folder with overlapping filenames (i.e., 01.jpg, 02.jpg, 03.jpg, etc.), and every time I run this script all images are prepended with a timestamp. I still want the files to be sorted alphabetically, which is why an alphabetical timestamp (rather than a random hash) is preferred.
- At first I considered a long date such as 2014-04-19-01.jpg, but that adds so much text!…also, it doesn’t include time of day.
- If I include time of day, it becomes 2014-04-19-09-16-23-01.jpg
- If I eliminate dashes to shorten it, it becomes hard to read, but might work 140419091623-01.jpg
- If I use Unix Epoch time, it becomes 1397912944-01.jpg
The result I came up with uses base conversion and a string table of numbers and letters (in alphabetical order) to create a second-respecting timestamp hash using an arbitrary number of characters. For simplicity, I used 36 characters: 0-9, and a-z. I then wrote two functions to perform arbitrary base conversion, pulling characters from the hash. Although I could have nearly doubled my available characters by including the full ASCII table, respecting capitalization, I decided to keep it simple. The scheme goes like this:
- Determine the date / time: 19-Apr-2014 13:08:55
- Create an integer of Unix Epoch time (seconds past Jan 1, 1970): 1397912935
- Do a base conversion from a character list: n4a4iv
- My file name now becomes n4a4iv-01.jpg - I can accept this!and when I sort the folder alphabetically, they’re in order by the timestamp
I can now represent any modern time, down to the second, with 6 characters. Here’s some example output:
19-Apr-2014 13:08:55 <-> 1397912935 <-> n4a4iv
19-Apr-2014 13:08:56 <-> 1397912936 <-> n4a4iw
19-Apr-2014 13:08:57 <-> 1397912937 <-> n4a4ix
19-Apr-2014 13:08:58 <-> 1397912938 <-> n4a4iy
19-Apr-2014 13:08:59 <-> 1397912939 <-> n4a4iz
19-Apr-2014 13:09:00 <-> 1397912940 <-> n4a4j0
19-Apr-2014 13:09:01 <-> 1397912941 <-> n4a4j1
19-Apr-2014 13:09:02 <-> 1397912942 <-> n4a4j2
19-Apr-2014 13:09:03 <-> 1397912943 <-> n4a4j3
19-Apr-2014 13:09:04 <-> 1397912944 <-> n4a4j4
Interestingly, if I change my hash characters away from the list of 36 alphanumerics and replace it with just 0 and 1, I can encode/decode the date in binary:
19-Apr-2014 13:27:28 <-> 1397914048 <-> 1010011010100100111100111000000
19-Apr-2014 13:27:29 <-> 1397914049 <-> 1010011010100100111100111000001
19-Apr-2014 13:27:30 <-> 1397914050 <-> 1010011010100100111100111000010
19-Apr-2014 13:27:31 <-> 1397914051 <-> 1010011010100100111100111000011
19-Apr-2014 13:27:32 <-> 1397914052 <-> 1010011010100100111100111000100
19-Apr-2014 13:27:33 <-> 1397914053 <-> 1010011010100100111100111000101
19-Apr-2014 13:27:34 <-> 1397914054 <-> 1010011010100100111100111000110
19-Apr-2014 13:27:35 <-> 1397914055 <-> 1010011010100100111100111000111
19-Apr-2014 13:27:36 <-> 1397914056 <-> 1010011010100100111100111001000
19-Apr-2014 13:27:37 <-> 1397914057 <-> 1010011010100100111100111001001
Here’s the code to generate / decode Unix epoch timestamps in Python:
hashchars='0123456789abcdefghijklmnopqrstuvwxyz'
#hashchars='01' #for binary
def epochToHash(n):
hash=''
while n>0:
hash = hashchars[int(n % len(hashchars))] + hash
n = int(n / len(hashchars))
return hash
def epochFromHash(s):
s=s[::-1]
epoch=0
for pos in range(len(s)):
epoch+=hashchars.find(s[pos])*(len(hashchars)**pos)
return epoch
import time
t=int(time.time())
for i in range(10):
t=t+1
print(time.strftime("%d-%b-%Y %H:%M:%S", time.gmtime(t)),
"<->", t,"<->",epochToHash(t))