Computer aided meta-data analysis of music library

For quite a while now, I have been interested in where the title track of an album appears in the album track listing (the meta-data of my music library in general – I know, I need better hobbies, or a girlfriend!).

Introduction

The general rule for albums seemed to be either first (Still Crazy After All These Years – Paul Simon, 1975) or last track (The Masterplan – Oasis, 1998), however there seemed to be a few exceptions to the rule. Quite a few albums seemed to have the titular track as the penultimate one (Love Over Gold – Dire Straits, 1982) and some just had them anywhere, in Suzanne Vega’s 2001 album, Songs in Red and Gray, the title track appears at position 6 (more on that later).

However, all this reasoning was anecdotal. I had never sat down with all my CDs and categorized where the title track appears on the track listing (that would be weird – writing a computer program to do it is perfectly normal!). It could be that it only seemed to be this way, but in fact artists (or rather producers!) would stick tracks seemingly randomly in the track listing when mixing an album.

Whilst I should have been revising the other night, I instead resolved to solve this mystery once and for all. I wrote a short (took me until 5:45AM!) program in Visual Studio and ran it over my music library file system.

The program I wrote appears at the bottom of the page.

Results

The results were more or less as expected, but it was reassuring to see them nonetheless. Here are some graphs for the data-philes amongst you.

Track number analysis

Have you ever wondered what the average number of tracks per album is? Now you don’t have to.

9.2, or 13.1 when ignoring singles and large compilation albums.

First up we have the number of tracks for the entire library, it can be seen that there are a lot of singles, but the majority of full albums have track counts of around 11-13.

Number of tracks all albums

Next we can look at the data more closely at the data, and look at the frequency of albums with certain numbers of tracks.Number of tracks per album entire library

Here we can once again see that we have a lot (72) of singles that brings the average down, a main bulk of ‘normal length’ albums and some very large (>25 tracks) albums at the top of the distribution. These are multitrack ‘deluxe edition’ or compilation albums (My 3 disc 35th anniversary edition of Fleetwood Mac’s Rumours) . As these singles and compilations are not representative of normal albums, we can ignore them and redraw the chart, with no albums below 2 tracks long, or above 25.

Number of tracks per album outliers removed

This is a better representation of ‘normal’ album lengths. We can easily see the modal length of albums is 11.5 tracks long, exactly as we suspect. This distribution gives a mean album length of 13.1 tracks.

Title track position analysis

After having a look at average album lengths, we can look closer at the titles of the tracks, specifically the occurrence of the title track of album.

Initially, we plot the position, relative to the start, of the title track of the album. distance of title track through album, entire library

We can see that the majority of the title tracks appear at position 0 (the start, as we expected).

Next I plotted the normalized distance of title track position. This gives a ratio, from 0 to 1 of the distance through an album that a title track will appear.

normalized distance of title track through album, entire libraryHere we can start to see the predicted pattern occurring, with lots of albums having the title tracks at the start, and some having them at the end. This is a definite case where the singles in my library will be skewing the result. All singles have only one track, obviously occurring at position 0, and having the same title as the album.

Finally, we can plot the normalized distance excluding the singles.

normalized distance of title track through album, ignoring singles

This is exactly what I initially suspected. Most albums will have the title track as track one, and some will have it as the last track. There are few that have it placed somewhere in the middle.

Artist data analysis

This section is just for fun, it is unique to my music collection, however a similar pattern may be noticeable for other people. Let me know.

Below, I have plotted the frequency of number of albums by a single artist. In other words, the number of artists in my library, who I have a certain amount of albums for.

Number of albums by a single artist

Here it can easily be seen that I have a lot of artists of who I only own a single alCapturebum, and one single artist who I seem to own 20 albums of! Looking closer at this, it is revealed that iTunes calls ‘Compilations’ an artist in the file structure.

For my actual favourite artist, as I own 5 albums of hers is Suzanne Vega.

She is.

Afterword

There it is, my first foray into meta-data analysis. This is all part of a relatively new field known as data science, the proponents of which avidly visualize the trillions of bytes we are creating everyday, to obtain some meaningful insights into the secrets which they hold.

I hope you found my simple analysis of my music library interesting. The code is below if you want to try it yourself, along with a link to the results of my tests.

Link to results

Code

Here is the code that I used in my analysis. Feel free to run it on your own music libraries and post any interesting results in the comments below. The directory access code was shamelessly copied from a github snippet I found, that was in turn copied from a stackoverflow answer. Sharing in caring. If you ever use any of this code, feel free to leave a link.

// itunes_analyser.cpp : main project file.
 
#include "stdafx.h"
#include 
#include 
#include 
#include 
#include 
 
using namespace std;
 
//Simple struct to return from lsfiles
struct List {
	vector<string> files;
	vector<string> directories;
};
 
struct Album {
	string albumName;
	List tracks;
 
	double number_of_tracks = -1;
	double title_track_distance_from_start = -1;
	double title_track_distance_from_end = -1;
	double distance_through_album = -1;
	bool title_track_exists = false;
 
	void run_album_analyis()
	{
		number_of_tracks = tracks.files.size();
		if (number_of_tracks > 0)
		{
			int number_of_other_tracks = number_of_tracks - 1;
			if (number_of_other_tracks == 0)				//ie, single album, only has one song
			{
				number_of_other_tracks = 1;
			}
			for (int current_track_posistion = 0; current_track_posistion < number_of_tracks; current_track_posistion++)
			{
				if (tracks.files.at(current_track_posistion) == albumName)
				{
					title_track_distance_from_start = current_track_posistion;
					title_track_exists = true;
				}
			}
			if (title_track_exists)
			{
				title_track_distance_from_end = number_of_other_tracks - title_track_distance_from_start;
				distance_through_album = title_track_distance_from_start / number_of_other_tracks;
			}
		}
	}
};
struct Artist {
	string artistName;
	vector<Album> albums;
	int number_of_albums = 0;
 
	void run_artist_analyis()
	{
		number_of_albums = albums.size();
	}
};
 
//All of the hard work
struct List lsfiles(string folder)  //(c) http://stackoverflow.com/a/20847429/1009816
{
	vector<string> files; //Will be added to List
	vector<string> directories; //Will be added to List
	char search_path[300];
	sprintf(search_path, "%s*.*"folder.c_str());
	WIN32_FIND_DATA fd;
	HANDLE hFind = ::FindFirstFile(search_path, &fd);
	if (hFind != INVALID_HANDLE_VALUE)
	{
		do
		{
			// read all (real) files in current folder, delete '!' read other 2 default folder . and ..
			
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				string directoryname = fd.cFileName;
 
				if (directoryname != "."  && directoryname != "..")		//Remove 2 folders, 
				{
					directories.push_back(directoryname);
				}	
			}
 
			else
				{
					string filename = fd.cFileName;
					string filetype = filename.substr(filename.find(".") + 1);		//gets the datatype of the string
					filename.erase(filename.rfind("."));					//Removes the datatype from the end of filename
					filename.erase(0, filename.find(" ") + 1);
					if (filetype != "jpg" && filetype != "ini") {
						files.push_back(filename);
					}
				}
 
		} while (::FindNextFile(hFind, &fd));
		::FindClose(hFind);
	}
	List me;
	me.files = files;
	me.directories = directories;
 
	return me;
}
 
int main(int argcchar *argv[])
{
	vector<Artist> artists;
 
	string root_music_dir = "D:\\Music\\iTunes\\Music";
	List artist_list = lsfiles(root_music_dir + string("\\")); //Get contents of directory
	for (int current_artist_posistion = 0; current_artist_posistion < artist_list.directories.size(); current_artist_posistion++)
	{
		Artist current_artist;
		current_artist.artistName = artist_list.directories.at(current_artist_posistion);
		
		vector<Album> albums;
 
		//cout << "[artist]: " << artist_list.directories.at(current_artist_posistion) << "\n";
		List album_list = lsfiles(root_music_dir + string("\\"+ artist_list.directories.at(current_artist_posistion) + string("\\")); //Get contents of artist dir
			
		for (int current_album_posistion = 0; current_album_posistion < album_list.directories.size(); current_album_posistion++) 
		{
			Album current_album;
			current_album.albumName = album_list.directories.at(current_album_posistion);
 
			List tracks = lsfiles(root_music_dir + string("\\"+ artist_list.directories.at(current_artist_posistion) + string("\\"+ album_list.directories.at(current_album_posistion) + string("\\")); //Get contents of artist dir
			//cout << "	" << "[album]: " << album_list.directories.at(current_album_posistion) << "\n";
			current_album.tracks = tracks;
			albums.push_back(current_album);
		}
		current_artist.albums = albums;
		artists.push_back(current_artist);
	}
//Where the magic happpens!
	ofstream log;
	ofstream artist_analysis_results;
	ofstream album_analysis_results;
	
	//Open files for writing to.
	log.open("log.txt");
	album_analysis_results.open("album_analysis.csv");
	artist_analysis_results.open("artist_analysis.csv");
 
	album_analysis_results << "Artist name,Album name,number of tracks,distance_from_start,distance_from_end,distance_through_album" << endl;
	artist_analysis_results << "Artist name,Number of albums" << endl;
 
	for (int current_artist_posistion = 0; current_artist_posistion < artists.size(); current_artist_posistion++) //Iterate through artists
	{
		artists.at(current_artist_posistion).run_artist_analyis();		//Run analyis on current artist
 
		cout << artists.at(current_artist_posistion).artistName << "\tnumber of albums: " << artists.at(current_artist_posistion).number_of_albums << endl;
		log << artists.at(current_artist_posistion).artistName << "\tnumber of albums: " << artists.at(current_artist_posistion).number_of_albums << endl;
		artist_analysis_results << artists.at(current_artist_posistion).artistName << "," << artists.at(current_artist_posistion).number_of_albums << endl;
		
 
		for (int current_album_posistion = 0; current_album_posistion < artists.at(current_artist_posistion).albums.size(); current_album_posistion++) //Iterate through albums
		{
			artists.at(current_artist_posistion).albums.at(current_album_posistion).run_album_analyis();		//Run analyis on current album
 
			cout << "\t |" << artists.at(current_artist_posistion).albums.at(current_album_posistion).albumName << endl;
			log << "\t |" << artists.at(current_artist_posistion).albums.at(current_album_posistion).albumName << endl;	
			for (int current_song_posistion = 0; current_song_posistion < artists.at(current_artist_posistion).albums.at(current_album_posistion).tracks.files.size(); current_song_posistion++) 
			{
				cout << "\t\t |" << artists.at(current_artist_posistion).albums.at(current_album_posistion).tracks.files.at(current_song_posistion) << endl;
				log << "\t\t |" << artists.at(current_artist_posistion).albums.at(current_album_posistion).tracks.files.at(current_song_posistion) << endl;
			}
 
				//Do the magic
			
			cout << "\t\t\t |title track exists: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_exists << endl; 
			cout << "\t\t\t |number of tracks: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).number_of_tracks << endl;
			
			log << "\t\t\t |title track exists: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_exists << endl;
			log << "\t\t\t |number of tracks: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).number_of_tracks << endl;
			
			if (artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_exists) 
			{
				cout << "\t\t\t |distance_from_start: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_start << endl;
				cout << "\t\t\t |distance_from_end: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_end << endl;
				cout << "\t\t\t |distance_through_album: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).distance_through_album << endl;
			
				log << "\t\t\t |distance_from_start: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_start << endl;
				log << "\t\t\t |distance_from_end: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_end << endl;
				log << "\t\t\t |distance_through_album: " << artists.at(current_artist_posistion).albums.at(current_album_posistion).distance_through_album << endl;
 
			}
			
			album_analysis_results << artists.at(current_artist_posistion).artistName << ","
				<< artists.at(current_artist_posistion).albums.at(current_album_posistion).albumName << ","
				<< artists.at(current_artist_posistion).albums.at(current_album_posistion).number_of_tracks;
				
			if (artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_exists)
			{
				album_analysis_results << "," << artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_start << ","
					<< artists.at(current_artist_posistion).albums.at(current_album_posistion).title_track_distance_from_end << ","
					<< artists.at(current_artist_posistion).albums.at(current_album_posistion).distance_through_album << endl;
			}
			else
			{
				album_analysis_results << endl;
			}
		}
	}
	log.close();
	artist_analysis_results.close();
	album_analysis_results.close();
	
	char c;
	cin  >> c;
	return 0;
}

 

Leave a comment

Filed under Uncategorized

Nixie Clock Project: PCB and circuit construction

IMG_20140726_162430197

After my post several weeks ago, regarding the circuit for driving my Nixie tubes, I intended to follow it up with a post highlighting the other aspects of the circuit, but I didn’t have time and with the circuit board now completed and project virtually finished apart from a suitable enclosure, I decided that I would just do an update showing the board completed, populated, and the clock program working.

The PCB

IMG_20140726_162113067As this project was designed to be a permanent one, and in prominent view 24/7, I decided that for the circuit board I would splash out and get a professionally fabricated PCB. I was initially drawn to OSH park, but was recommended another fab-house, based in China called Elecrow. This company was mainly an online store selling electronic components, but also had a very reasonably priced PCB fab service; for only $11.90, you receive 10, 2-layer copies of your design with a HASL green solder mask. Other colours are available, along with a 4-layer service, but at a slightly higher price. I wasn’t expecting a lot really, off centred drill holes, and possibly non-plated holes, but I was surprised by the incredible quality of the fabrication; all holes were centred, the silkscreen was aligned and positioned where it should be, and there were no interconnect problems. The only one major problem, which I shall describe in more detail shortly, was a mistake in the board design by myself. One thing to note is that not all of the default eagle silkscreen layers are created equally, and only ‘dimension’,’ tPlace’ and ‘tNames’ (and their respectful bottom layer counterparts) are printed. I had deleted (using the smash tool) most of my tValues but I left the values of several appropriately named jumpers for the RTC breakout board and some switches so they can be brought out to the enclosure. Unfortunately, these didn’t print, so I’ll have to make do. On the subject of silkscreen defects I accidentally mislabelled the pins on the switch jumpers, typically these mistakes were in the layer which printed, and when I finally get round to installing switches on my enclosure, I’ll have to remember.IMG_20140726_162258303_HDR

A DRU file was provided for EAGLE, along with a CAM job which, after some experimenting I was able to export the required files and I the delivery was quick (7 days after dispatch).

All in all, I am incredibly happy with how the PCB turned out, and will definitely use Elecrow again.

The major mistake when making the PCB was in the choice of package of MPSA42 transistor. According to the datasheet, the transistors are EBC pinned TO-92’s. I was 100% sure that I’d checked this, but apparently I didn’t and used a ECB package on my schematic accidentally instead. I spent about 3 hours thinking there was a problem with my code when the tubes would not work as planned before finally checking the schematic and realising my mistake. Gladly nothing was damaged and I was able to unsolder the transistors (which sadly did not survive the procedure) and replace them, albeit with some incredibly messy leg-bending.

IMG_20140726_191720434

 

 

 

 

 

Circuit design choices

SolenoidAs you’d expect, I went for using the arduino platform, but instead of building an atmega into the design as you’d normally do for this sort of project, or even build the design into a shield to put onto a regular ‘duino board, I put headers in so that I could place a arduino micro (a Leonardo by a different face), on the board, which I bought from RS. I mainly did this to save space, but I also really like the look of boards stacked in this way, as it adds a certain charisma to an otherwise bland PCB. I did a similar thing with the real time clock, again to save space and break up the board, this was supported vertically, and looks lovely.

Work was further outsourced to modules in the form of the power supply. This was due to me wanting to keep the high voltage isolated from the board, and also to save space again. I initially went for a kit from ebay, but after the MOSFET overheated and died- I decided on a power module made by the website KOSBO, which specialises in all things nixie. If you don’t want to take on a scratch build like I have, he has a lot of kits for sale for you to build your own Nixie clocks.

So that the high voltage power supply could be turned off when not in use, I  built a MOSFET switch into the design, along with a solenoid for an alarm clock bell function. I have tested the solenoid with a mint tin ‘bell’ and its quite loud, but not really as loud sounding as I had hoped. Oh well.

 

Mechanical design choices

IMG_20140705_184604932_HDRI was originally going to use automotive bullet connectors to connect to the Nixie tubes, however they proved to incredibly difficult so solder so I gave up on that idea, and started looking around for sockets. These were very expensive where I could find them, so I was about to give up hope when I came across a 3D model which someone had created for this exact purpose, the model also used very easily available crimp terminals for connections, however there was one catch, I didn’t own a 3D printer. No worries, I soon found a community printing service called 3D hubs, with the nearest contributor only 20 miles away! IMG_20140705_184717042

There’s a great introduction to it here, if you want more information. I was able to get 6 of the sockets made and collected in less than 24 hours after I found the website, so I’ll definitely use the service again. I was able to pick up a pack of the smaller diameter crimp terminals (08-50-0108) for a good price, and so I was in business. I had ordered some ribbon cable with 0.1″ pitch connectors with the PCB so cutting the ends off them allowed me to crimp the wires into the terminals.

IMG_20140716_170359223

I placed some switches on the board, but so far have not implemented them in a sketch, as I’ve been busy working on another project.

If you’re interested in making a similar clock, I have got 9 spare copies of the PCB available which you can have if you pay for postage, so leave a comment and I’ll get back to you.

Code (v1)

I have only finished one complete sketch for the board currently, and it is incredibly basic, as there were some bugs and it needed optimising timing-wise. However, it works and I’m quite proud of it. I will pst of an update of what I hope is the worthiest successor (I’m working on several different things) which should include the option to change the time, set alarms and brightness and generally make it more standalone. I’m also toying with the idea of making a library or a web-counter sort of thing which can chat to an program on my PC via com-link. I may also implement the Dallas temperature sensor which is available to be soldered on the RTC board. Who knows.

#include <Wire.h>

#define  sol 17            //Solenoid alarm bell
#define  sw_led 16        //*****ACTIVE LOW*****
#define  sw_1 13          //*****ACTIVE LOW*****
#define  sw_2 12          //*****ACTIVE LOW*****
#define  sw_3 11          //*****ACTIVE LOW*****
#define  an_0 10          //Colons
#define  an_1 9           //H1, M2
#define  an_2 8           //H2, S1
#define  an_3 7           //M1, S2
#define  datapin 6
#define  clockpin 5
#define  latchpin 4
#define  I2C_CL 3
#define  I2C_DA 2
#define  PS 1           //To MOSFET conrolling hv-pwr supply
#define  DS 0          //OneWire temp sensor
byte display;
int anode;      //current anode
int prev_anode;
int x = 1;
int i = 10;
int time;

byte inputPins [] = {sw_1, sw_2, sw_3};
byte outputPins [] = {sol, sw_led, an_0, an_1, an_2, an_3, datapin, clockpin, latchpin, PS};

void setup()
{
  //Serial.begin(9600);
  Wire.begin();
  int i;
  for(i=0; i<sizeof(inputPins); i++){
    pinMode(inputPins[i], INPUT);
    }
  
  for(i=0; i<sizeof(outputPins); i++){
    pinMode(outputPins[i], OUTPUT);
    digitalWrite(outputPins[i], LOW);        //Sets all outputs off except the switch leds which are active low
    }
   
   shiftOut(datapin, clockpin, MSBFIRST, B00000000);  //"Flush out the pipes"
   digitalWrite(an_0, HIGH);                           //Turn on colon lights
   digitalWrite(PS, HIGH);                               //Turn in pwr supply
   Serial.begin(9600);
 
}

void loop(){
  

 Wire.beginTransmission(0x68);
 Wire.write(0x00);
 Wire.endTransmission();
 
 
 Wire.requestFrom(0x68, 3);
 int sc_bcd = Wire.read();    //Second in binary coded decimal
 int mt_bcd = Wire.read();    //Minute in bcd
 int hr_bcd = Wire.read();    //Hour in bcd
 
 byte hr_t = hr_bcd/16;        //First digit of hours, ie. tens of hours
 byte hr_u = hr_bcd%16;        //Second digit of hours, ie. units of hours
 byte mt_t = mt_bcd/16;        //First digit of minutes, ie. tens of minutes
 byte mt_u = mt_bcd%16;        //Second digit of minutes, ie. units of minutes
 byte sc_t = sc_bcd/16;        //First digit of seconds, ie. tens of seconds
 byte sc_u = sc_bcd%16;        //Second digit of seconds, ie. units of seconds

 mt_u = mt_u<<4;
 sc_t = sc_t<<4;
 sc_u = sc_u<<4;

 digitalWrite(anode, LOW);
 switch(x)
 {
  case 1:
    display = mt_u | hr_t;
    anode = an_1;
    x = 2;
    break;
  
  case 2:
    display = sc_t | hr_u;
    anode = an_2;
    x = 3;
    break;
    
  case 3:
    display = sc_u | mt_t;
    anode = an_3;
    x = 1;
    break;
 }
 
 shiftOut(datapin, clockpin, MSBFIRST, display);
 
 digitalWrite(latchpin, HIGH);
 digitalWrite(latchpin, LOW);
 
 delayMicroseconds(5);
 digitalWrite(anode, HIGH);

}

Hope this helps, and I will try to post the final, final (case done, code edited) update soon.

I would upload a video but WordPress says no. Boo.

 

Leave a comment

Filed under Nixie Clock Project

Nixie Clock Project: Introduction

Introduction to Nixie

SAM_0623Several months ago, while browsing the corners of the internet, I came across the notion of Nixie tubes, which after a brief flirtation, bit the bullet and bought a set of 6 IN-1’s off eBay, with the intention of building a Nixie tube clock. In this short project series I will be documenting my creation of my very own Nixie Clock.

For those who don’t know, Nixie tubes are a form of Neon display (a glorified neon indicator tube according to one writer) which were used mainly in the USSR during the 60’s to early 80’s for numeric display. They have now gone out of use as LED displays are much simpler, less prone to breakage and easier to read. However, Nixie displays have the advantage of looking absolutely gorgeous, having that retro, military-esque look, while being unusual and rare, it just seemed like a cool thing to get into. You can find loads of different hobbyists who have written on the subject of Nixies and driving them, the majority of which I will leave up to you to find, however one article from Decode Systems was very useful in giving me an introduction into Nixie working and operation, check this out if you haven’t already, as it is a very comprehensive introduction.

Driving Nixie tubes.

Why are you making a whole project series on a digital clock? I hear you ask. Well, while Nixies are quite simple to use and implement they require comparatively high voltages to drive, 170V in most cases, gladly however-the current is very tiny, 2mA, so we won’t be wasting obscene amounts of power driving it, it is just a clock anyway. The high voltage is supplied through a current limit resistor (more on this later), to the anode of the Nixie tubes. Each digit of the tubes has to be drawn low for the current to flow. As we are dealing with such high voltages, our MCU cannot directly drive the Nixies; high voltage transistors, or dedicated ICs are required, I will be using the latter in my clock, and will be doing no multiplexing, however, if you intend to multiplex or directly drive your Nixies with transistors, this article is a good place to start.

SAM_0624There are several ICs on offer which simplify the process of Nixie driving, the most widely used and prominent of which is the 74141, a 74 series chip created specifically for driving cold-cathode display tubes (Nixies). However, as the cold war was going on at this time, and trade between USSR-USA was limited, the Russians created a chip of identical specification and pin-out, packaged as the K155ID1. A datasheet of the 74141 (MSI), showing both pinouts and logic tables along with the comparatively spartan offering of the Russian counterpart were found at tubehobby, a veritable bible of information regarding Nixie tubes and driving them. I was able to pick up 4 K155’s of eBay (from now on referred to as 74141’s), for a reasonable price. However, a much more modern and reliable solution would be the HV5812 chip from Supertex, which has a serial input (SPI/shift register) vs. the BCD parallel offering on the 74141. A Nixie clock has been built using the Supertex chip. I was intending to use the Supertex, however, my sample request was denied, and mouser (which stocks the chips) has a £20 min. order fee, which seemed like an overkill as I only need 2 ICs. As the 74141 chips require al 4-bit BCD parallel input(remember BCD: used in the DS1307 RTC data), and I will be using 4 chips, I would require 16 control lines from my MCU (arduino in my case), and would only just have enough for the digits, keeping in mind that I require 2 lines from my RTC and several more for buttons etc., I have decided to use a shift register, or more importantly 2 shift registers to send the data to the 74141’s. This seems simple enough, as everything is at 5V logic, and uses just 3 IO lines (could technically use 2, (no latch) but I expect digit ghosting). I have ordered an adjustable 180V power supply from eBay, which accepts 12V and outputs 5W, more than enough to power lowly Nixie tubes, this currently (pun not intended) has not arrived, so I haven’t had a chance to test out my Nixies which have arrived, but I expect it in the next week. So far, I have only received the Nixies (IN-1), and the K155’s.

Choosing an anode resistor

Choosing an anode resistor is one of the simplest engineering decisions in the whole project, and is required as the current must be limited when passing through the Nixie tubes, to about 2mA. The specifications that I have on the IN-1 tubes specify 2.5mA operating current, but I wouldn’t like to shorten the life span of the tubes, so I will use a lower current of 2mA. The calculation is incredibly simple, for it uses ohm’s law, however a large proportion of people do not know how to correctly utilise this essential equation. In normal operation, the IN-1 drops about 133V, this means that the resistor must drop 47V if using a 180V power supply. Using ohm’s law, R=V/I we can calculate that a 23500 ohm resistor is required. As the closest resistor value is 22K, we can use this to give 2.1mA current, which while more than my 2mA target, is still shy of the 2.5mA recommended current, therefore I will prolong the life of my Nixie tubes.

The ‘firing voltage’ of the Nixie tubes, the voltage required to turn on the tubes before it drops to a steady 133V is about 170V. This, being a higher voltage drop across the tube will result in a much lower voltage drop of only 10V across the resistor. At this voltage, we can calculate that only 0.45mA of current will flow through the tube. This seems low, but remember, it will be instantaneous, and will quickly rise to the 2.1mA given at 133V.

Mounting and circuit design

SAM_0626For this project, I am not going to mount the Nixies on a circuit board, and as the sockets for the Nixies are more rare and expensive that the actual tubes, I have found a clever way of mounting them. When searching through my parts bin for some connectors with which I could mount the tubes, I happened on some old automotive connectors, normally used in engine to connect the car electronics. These ‘bullet’ connectors were too large to connect directly to the tubes pins (2.36mm according to this ‘datasheet‘) as per designed, but the pins of the tubes are the perfect fit for the wire crimps, which are designed for wires of up to 2.5mm (see left). As I only had a few of these blue connector, and all of different connectors, I quickly ordered some more of these, in the 4.8mm ‘spade’ variety, as these have a hole for the wire which will aid in soldering.

The chips (74595, 74141) will be soldered onto a small section of perforated board, (I will actually use sockets, as apparently the K155’s are temperamental), I have chosen not to solder the arduino chip and components onto this as it seemed easier to purchase a arduino pro-mini with all the components pre-mounted. The power supply will sit on its own board to isolate any high voltages (180V) from the ICs supply voltage (5V). There will be a 9/12V wall power supply providing the power, which needs to be no more than 1A.

The whole clock will be mounted on a small piece of slanted wood/70’s wood effect, withSAM_0625 the Nixies facing outwards. I have also bought two neon bulbs, INS-1 Russian types to use a the colon separator. The boards and controls will be hidden inside a small plywood/plastic box behind the fascia. I intend to have the clock looking very retro, without looking tacky, and intend to search through the local tip to find a piece of 70’s furniture which I could utilise as a clock face.

In my next post, after the final components arrive and I find a suitable housing, I will go through the circuit design and arduino programming of the clock, and hopefully show you the finished project in all it retro glory. Stay tuned.SAM_0627

1 Comment

Filed under Arduino, Nixie Clock Project, Uncategorized

Bluetooth Headphones Project: Completion

Yesterday evening, finding no desperate need to complete any schoolwork, I found I had several free hours in which I could put my Bluetooth audio receiver together. I had previously had the board image printed onto press n peel film, (As the board was small I was able to fit 15 copies onto one A4 sheet , in case I messed up) then just ironed and etched. The soldering didn’t go very well but the board works, and is able to receive music from my phone. I have just ordered a Bluetooth dongle for my computer, but I currently cannot comment on how it works. The red LED flashes when I switch the board on until I connect it to my phone. The only eagle part libraries I had to make myself were the BT-module library, which was alright after all, and the surface-mount speaker connector, which was bought on ebay.

Connecting to my phone

Connecting to my phone

The only problem I have with the finished project, apart from the bad soldering which doesn’t really affect the operation, is that I didn’t cut the board close enough to the USB socket, so it is impossible to get a USB cable to fit in. Because of this I have not been able to test out the charging features yet.

As you can see...very annoying.

As you can see…very annoying.

Apart from that, the board works well, the battery I selected was a 1100mAh li-ion type, which is just a little smaller than the board itself. The whole board seems a little to delicate to be used as I was hoping, just in my pocket with my headphone connected, but it was a very nice experiment. Maybe one day when I can afford to have the board properly fabricated at a board house with nice regular drill holes on a double sided board it will be nice, but until then I will use this one. Press n peel is really a wonder all else considered, and when you use a lovely program like eagle, and some simple PDF manipulation for multiple copies on one sheet, it really is easy!

SAM_0545Below are the files which I have created for this project:

Have a look through the previous posts on this project for further information on the various steps. Hope you all enjoyed this, if you have any questions or comments feel free to leave them, and I will be glad to answer.

Wes.

22 Comments

Filed under Bluetooth Headphones Project, Uncategorized

Bluetooth Headphones Project: (part 2) Update

Towards the end of the summer, the Bluetooth module which I ordered from China finally arrived. However, as I had to go back to school to do some real work, I could not utilise it and finish my project. However, I did print off a 1:1 image of the eagle footprint and discovered to my dismay that I had made this to the incorrect size. I have not updated the file yet, so DO NOT USE IT if trying to make a module related project. Before the module arrived however, I made up a schematic for the final project. I will not publish it as an eagle file yet, as I have yet to change the module footprint, however, as the schematic is unlikely to change I can publish it as a pdf. As a pdf: BT audio receiver-schematic Image

6 Comments

Filed under Bluetooth Headphones Project

SD card motion sensor

SAM_0487Have you ever been annoyed at people entering you room/evil lair while you’ve been away? Of course you have, in this project I have made an arduino sketch that logs entries into your domain to an SD card for later viewing, so you can know if someone has been snooping!

To do this I used a small inexpensive motion sensor that I found off eBay, it is perfect for arduino as it outputs a TTL level digital pulse whenever motion is detected that can easily be read by any microcontroller. Adafruit electronics has a lovely introduction to board level PIRs, as there is no need to reiterate what the Adafruit article says, I will instead focus on using a PIR with an  SD card and RTC.

The setup

For this project, you will need:

An Arduino (I used an official Uno for prototyping, then soldered up a boarduino with minimum components)
PIR motion sensor (see text)
Some sort of SD module or shield
An RTC (I used a DS1307 that I have detailed previously)
2 LEDs with resistors (my arming LED was red, while the trigger LED was yellow)
Simple pushbutton/toggle switch

I have the PIR set to shortest re-arm time (about 8 seconds for my PIR), mid-sensitivity and the jumper in H (retriggering) position.

The code

This code may look complicated, but examining it in smaller sections will make it easier to understand.

#include <Wire.h>
#include <SD.h>

int RTC_address = 0x68;
int Pir = 3;
int armingSwitch = 4;
int armingLed = 5;
int trippingLed = 6;

File logfile;

boolean armed;
boolean tripped;

void setup()
{
 armed = false;
 tripped = false;
 Wire.begin();
 Serial.begin(9600);
 pinMode(Pir, INPUT);
 pinMode(armingSwitch, INPUT_PULLUP);
 pinMode(armingLed, OUTPUT);
 pinMode(trippingLed, OUTPUT);
 pinMode(10, OUTPUT);
 digitalWrite(armingLed, armed);
 digitalWrite(trippingLed, armed);

 if (!SD.begin(10)) {
    Serial.println("SD card failed!");
    return;
}
    Serial.println("SD card success!");    //If there is a problem with the card, from SD tutorial
}

void loop()
{
digitalWrite(armingLed, armed); 
digitalWrite(trippingLed, tripped);

if (digitalRead(armingSwitch) == 0){
 delay(1000);    //For debounce
 armed = !armed;
 logfile = SD.open("logfile.txt", FILE_WRITE); 

 if(armed == true){  
  Serial.print("Armed: ");  
  logfile.print("Armed: ");
  logfile.close();
  dateTimeWrite();

  logfile = SD.open("logfile.txt", FILE_WRITE); 
  logfile.println();      //Newline
  logfile.close();
  for(int i=0;i<=10;i++){                //Led blinks for 10 seconds, while you vacate the area!!!
   digitalWrite(armingLed, HIGH);
   delay(500);
   digitalWrite(armingLed, LOW);
   delay(500);
  }
 }

 else if (armed == false){   
  Serial.print("Disarmed: ");
  logfile.print("Disarmed: ");
  logfile.close();
  dateTimeWrite();
  logfile = SD.open("logfile.txt", FILE_WRITE); 
  logfile.println();
  logfile.close();
 }

Serial.println(); 
 }

  if (digitalRead(Pir) == !tripped && armed == true){
  tripped = !tripped;
  digitalWrite(trippingLed, tripped);

  if (tripped == true){

 Serial.print("Detected: ");
 logfile = SD.open("logfile.txt", FILE_WRITE); 
 logfile.print("Detected: ");        
 logfile.close();
 dateTimeWrite();
 logfile = SD.open("logfile.txt", FILE_WRITE); 
 logfile.println();
 logfile.close();
 Serial.println();
 delay(10000);
 }
 else
 { 
 }

  }
}

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

void dateTimeWrite()
{
   Wire.beginTransmission(RTC_address);
 Wire.write(0x00);
 Wire.endTransmission();
 delay(10);

 Wire.requestFrom(RTC_address, 7);
 delay(10);
     int sc = bcdToDec(Wire.read());    //Second
     int mt = bcdToDec(Wire.read());    //Minute
     int hr = bcdToDec(Wire.read());    //Hour
     int dy = bcdToDec(Wire.read());    //Day
     int dt = bcdToDec(Wire.read());    //Date
     int mh = bcdToDec(Wire.read());    //Month
     int yr = bcdToDec(Wire.read());    //Year  

  logfile = SD.open("logfile.txt", FILE_WRITE);
  logfile.print(dt);
    logfile.print("/");
    logfile.print(mh);
    logfile.print("/");
    logfile.print(yr);
    logfile.print(" ");
    logfile.print(hr);
    logfile.print(":");
    logfile.print(mt);
    logfile.print(":");
    logfile.print(sc);
    logfile.close();

    Serial.print(dt);
    Serial.print("/");
    Serial.print(mh);
    Serial.print("/");
    Serial.print(yr);
    Serial.print(" "); 
    Serial.print(hr);
    Serial.print(":");
    Serial.print(mt);
    Serial.print(":");
    Serial.print(sc);  
}

This should be easy to follow the general gist of, but if you found it difficult to follow, try commenting out sections of the code or using examples for the SD library. To use, just power up the arduino and check to see if the SD card started correctly, then just push the arming switch and from then you have 10 seconds to leave the area before the detector is ‘live’. When motion is detected it will log the time of the incident to the card. Motion will not be reported if the device is not ‘armed’. The arduino also outputs all data to the serial port for debugging. ***Remember, your SD card must be formatted with FAT16 or 32***

Armed and facing my door!

Armed and facing my door!

To view the log, just put the SD card into your computer and open up LOGFILE.log

Variations

If you don’t want to have to arm the detector on power-up each time, set ‘armed = true’ in the setup sequence.

For a auditory warning or deterrent, you could attach a buzzer through a transistor to the trippingLed pin.

You could send a text using a GSM board when the detector is triggered, look at my previous SIM900 tutorial for more info.

1 Comment

Filed under Arduino

AT24C32 functions

I said earlier in my first AT24C32 post that for the final functions, you could work them out for yourself, if you did not, here they are now:

void i2c_eeprom_write (int device_address, int device_register, byte data)
{
 byte BYTE_1 = device_register >> 8;
 byte BYTE_2 = device_register - (BYTE_1 << 8); 

 Wire.beginTransmission(device_address);
 Wire.write(BYTE_1);
 Wire.write(BYTE_2);
 Wire.write(data);
 Wire.endTransmission();
 delay(10); 
}

byte i2c_eeprom_read (int device_address, int device_register)
{
 byte data;
 byte BYTE_1 = device_register >> 8;
 byte BYTE_2 = device_register - (BYTE_1 << 8); 

 Wire.beginTransmission(device_address);
 Wire.write(BYTE_1);
 Wire.write(BYTE_2);
 Wire.endTransmission();

 Wire.requestFrom(device_address, 1);
 delay(10);
 data = Wire.read();
 return data;
}

And here they are used in a sketch:

#include <Wire.h>
#define AT24C32 0x50

void i2c_eeprom_write (int device_address, int device_register, byte data)
{
 byte BYTE_1 = device_register >> 8;
 byte BYTE_2 = device_register - (BYTE_1 << 8); 

 Wire.beginTransmission(device_address);
 Wire.write(BYTE_1);
 Wire.write(BYTE_2);
 Wire.write(data);
 Wire.endTransmission();
 delay(10); 
}

byte i2c_eeprom_read (int device_address, int device_register)
{
 byte data;
 byte BYTE_1 = device_register >> 8;
 byte BYTE_2 = device_register - (BYTE_1 << 8); 

 Wire.beginTransmission(device_address);
 Wire.write(BYTE_1);
 Wire.write(BYTE_2);
 Wire.endTransmission();

 Wire.requestFrom(device_address, 1);
 delay(10);
 data = Wire.read();
 return data;
}

void setup()
{
 Wire.begin();
 Serial.begin(9600);
 i2c_eeprom_write(AT24C32, 0, 'a');
 Serial.write(i2c_eeprom_read(AT24C32, 0));
}

void loop()
{
}

At the moment you can only use them with one byte, sending or receiving, but when I need them for more than one, I’ll edit to allow for arrays and string to be passed.

Leave a comment

Filed under Arduino