Sunday, June 26, 2011

Taiko Synth - Phase 8 - Two Channels


On a taiko drum, in addition to striking the head (ドン) you need to be able to play rim shots (カラ). To make a rim, I cut a gap into the masonite. The rim needed to be as wide as a piezo sensor. I used GE silicone adhesive to fasten the piezo sensors to the drum head and rim, then wired each set in parallel (observing polarity) and then to a connector on the rim. I had to change the Arduino/nunchuck interface a little to handle two channels efficiently. That may be the subject of a later post.

In this new configuration, I seem to have trouble with single hits being detected as multiple. Reducing the number of sensors on the head helped. Also increasing the padding on the head of the drum helped. I bought a bunch of mouse mats for that purpose. Still, I think either the masonite board is resonating, or, more likely the board is bouncing up and down on the foam. The best fix I can think of for this is to glue them together, but if I do that, I will no longer have access to the underside of the head, so making changes is going to be difficult.

Anyway, here's the result:

Saturday, April 9, 2011

Taiko Synth - Phase 7 - Basic Improvements

The volume is now proportional to the hit - although I do need to scale it for better dynamic range. Initially I did the scaling like I would in any software application:
outputValue = inputValue * maxOutput / maxInput
The problem is that Arduino only deals with 16 bit integer math. Anything over 32767 is a negative number. By multiplying first, it went over that value so I was seeing negative volume values when I hit the drum hard. Since maxOutput and maxInput are constants with a ratio of about 5, I re-wrote the formula as follows:
outputValue = inputValue / 5
More work needs to be done on scaling; the detection threshold needs to be lower, a peak indicator LED is needed, and I want to be able to use the nunchuck to adjust the sensitivity. The nunchuck is running out of controls, so I'll try to use the accelerometers.


I'm now running the synthesizer software on an HP 1000 mini running Ubuntu 10.04 LTS off of a bootable flash drive.The latency isn't as bad as before. The little netbook works pretty well. I read that there's a low-latency Linux kernel, so I may try that for fun some day.

I changed the synthesizer software is to FluidSynth (http://sourceforge.net/projects/fluidsynth/). FluidSynth needs a soundfont. It can use the General MIDI instruments which include Taiko (channel 117) and a bird (channel 123 - not so useful, but fun).

Here's where I got the sound font: http://packages.debian.org/search?keywords=fluid-soundfont-gm

Once installed, here's how to start everything.
./ttymidi -s /dev/ttyUSB0&
aconnect -i
aconnect -o
aconnect 129:0 a28:0
fluidsynth -c0 -r0 -r22050 -l -a alsa -o audio.alsa.device=plughw:0 FluidR3_GM.sf2

If you are troubleshooting, there's a -v parameter for verbose, that's helpful.



I installed Jack, a GUI device connector, and experimented with that, but I think it's easier to use Aconnect.

Here's a demo of the results:

Saturday, March 19, 2011

How to Make a Decent Splice



Sometimes you need to shorten an existing cable by removing a section and reconnecting the two ends. Other times you might need to lengthen a cable by adding one or more sections. If you're doing this to a multi conductor cable, it's best to use the "Western Union" splice my grandfather taught me. This type of splice won't short-out (the condition in which conductors touch) if the insulation on your joint fails.

You will want to strip two to three times as much of the outer insulation as you normally would. On one end cut the red wire short, and the white wire long. On the other end cut the red wire long and the white wire short. Cut some heat-shrink tubing to go over the inner conductors. Also cut a larger piece to go over the whole thing, slide it over one or the other of the pieces of cable, and slide it well out of the way. Don't forget to do this, because after you've made the connection, you won't get be able to do get the heat-shrink onto the cable. Now cut some smaller heat-shrink tubing for the inner conductors and slide them over each of the longer wires. Make sure these are far away from the joint so the heat from soldering won't shrink them and prevent them from fitting over the joint.

Twist the wires together facing each other such that the joint isn't much thicker than the original wire. If you twist them facing the same direction (like a zipper), you might not be able to get the heat shrink over them, and if you do, the tubing will look like a python that's swallowed a pig. If the conductor is going to poke through the heat-shrink and cause a short, it's going to be where it's stretched thin over the big solder blob.

Like this:



Not like this:

Apply heat, then solder to the joints. After the joints have cooled, slide the heat shrink over them and apply heat. After that's cooled down, slide the large heat-shrink over the whole thing and apply heat to complete the job.

Sunday, March 13, 2011

Time Lapse Photography - Phase 1

The first step was to get the hardware ready.

To find the power, focus and shutter switches on this old camera, I disassembled it screw-by-screw. I only shocked myself once on the flash capacitor. I thought I'd never get the camera back together. It might have been better to have ground away the buttons with my Dremel tool. The nice thing about this camera is that when connected to and external 3.3 v power supply it never goes to sleep. This way it doesn't lose settings like "no flash" and the lens does not have to be extended for each frame. If make a 10 minute movie, that would be 18,000 frames. I'd guess that's pretty much the life of that mechanism. Now I wonder about the shutter and focus mechanism. How long will it last?

To use the camera, first touch the two "on" button wires together.
To focus, hold the brown and blue wire together.
To snap a frame, touch the white wire to the brown and blue.


This is the board with four relays that I made. It'll control the camera with one relay to spare. I made it generic as possible. I think the Arduino could probably have driven the relays, but I used some 2N2222 transistors anyway. The funny thing about these relays is that they have polarity. I thought maybe they had an internal protection diode, but when I ohmed it out, it was about 120 ohms both ways. I read up on this type of relay, and I think the armature may have a magnet on it to reduce the current requirement. The bad thing about that is if you wire it backwards, the armature is repelled, and the relay won't close. The other thing generic about this board is that I kept the PWM pins in reserve using only the pure digital I/O pins. The pin numbers are going to be numbered funny in the firmware, but this way all the analog outputs are available if needed.

Saturday, March 12, 2011

Taiko Synth - Phase 6 - Sensing the Bachi

I began with this simple code to detect the strikes, assuming that the start of the first negative slope signal the strongest part of the strikes.
void loop()
{
//polarity - white positive
amp = 0;
ampNew = 0;
don = false;
peak = false;
while(!don)
{
ampNew = analogRead(0);
if (ampNew > 25)
{
don = true;
while(!peak)
{
amp = ampNew;
ampNew = analogRead(0);
if ((ampNew + 1) < amp) // plus 10 for noise compensation
{
peak = true;
printSettings();
}
}
}
}



In the above code, I've had to replace all the "greater than" and "less than" symbols with equivalent HTML entity names, and that seems to work fine. I thought anything with in a code block shouldn't have needed that.

Anyway...

This "don detect" algorithm gave some strange results. The first strike didn't seem to even register. Later strikes weren't proportional to the intensity of the hit. To investigate, wrote new code to capture as much data as possible and send it to Putty via USB serial at 115200 bps.
void setup() {
Serial.begin(115200);
}

void loop() {
Serial.println(analogRead(0));
}



Here are 5 strikes growing in amplitude:







Here's a close-up of the 5th strike:









Some interesting observations can be made. I'm not quite sure of the frequency, because I'm not sure of the sample rate, but you can see that the wave form is a truncated decaying sine wave. That's what one might expect from striking a board with a stick. What's a little surprising is that the first peak is not necessarily the greatest. There is also some dc offset at the end of the strike. From this, the lesson is that I need to change the "don detect" algorithm take the first 15 or so points after a strike is detected. Then, use the greatest of those to determine the peak amplitude. This method may trade latency for accuracy so I may need to tweak the final code a little. It may be a good idea to write more test code to capture these points from within the taiko app to make sure sufficient data is gathered to determine the true peak amplitude. This is because the taiko app surely has a different sample rate than the app above. To address the DC offset, I think I need to make sure that the foam is cut away from beneath the piezo sensor.

Monday, February 28, 2011

Smartphone Mount

There are some pretty neat solutions for mounting smartphones on a car dashboard. The mounts make it easy and safe to use a smartphone as a GPS or listen to Pandora on those long trips. Unfortunately it wasn't clear just by looking at pictures on the web that one of these mounts would fit exactly right. I decided to make a custom mount using my old friend, styrene. Styrene is a soft plastic that's really easy to work with. However, with the demise of the neighborhood hobby store, it's gotten hard to find. Fortunately there's a model railroad shop nearby that sill carries styrene sheets and shapes such as angles and I-beams. Plastruct and Evergreen Models seem to be the two major brands.

First I took measurements of the phone and designed a holder on paper. I found that the plastic angle-beams were the most versatile for this application. I bought all the 3/8 angle-beams in the store (about 6). I cut the pieces in a mitre box and then dry-fit them to make sure my calculations were right.

Next I glued the pieces using Testors liquid cement. This stuff actually melts the plastic and forms a great bond. Another advantage is that it's fast-drying. I used alligator clips, a pin-vice, and a hemostat to clamp the pieces while they dried.

I wanted to use the "cubby hole" in the dash to hold the mount. I took measurements and found that it was shaped like a truncated pyramid. What a complex shape! I did my best to measure the inside of the box, and constructed a box to mount the smart phone holder on. When I did my final test fit, I found that the box was too wide to be fully inserted into the cubby hole! The reason was that I had measured the cubby hole at the middle, but the inner edges were rounded so it was just a little narrower where it made contact with my box. Here's the beauty of styrene. I cut the box in half longitudinally, taking out a few millimeters of material. I used more angles to re-connect the now slightly narrower box. This time it fit perfectly. I connected the holder to the mount and put a little piece of velcro at the bottom to keep it steady.

I have yet to paint it. If I paint it, it'll be white because styrene has a tendency to melt in the hot sun.

Sunday, February 27, 2011

Two ways to log temperatures.

With an Arduno, Adafruit's XPort Ethernet shield, and an Internet connection, parameters such a temperature can be logged to the cloud.

When I assembled the Ethernet shield I only connected power, TX and RX. Setup of PPP on the XPort was a little tedious, as a fixed IP address, mask, gateway, and other parameters had to be entered through the Arduino with a terminal app. I connected the output of an LM335 temperature sensor to analog port 0 using the recommended calibration circuit from the data sheet.

Since Arduino can be powered over USB, I used the USB port on the router for power only. This was a neat solution because it needed no additional power supply for the Arduino. The drawback was that power from a USB port isn't always stable or equal to exactly five volts. Measure the voltage of VREF after connecting it to the port. Analog read should be equal to VREF/1023. Hopefully it is stable. One option may be to make a stable 3.75 volt reference circuit, and that would enable any USB port to be use. This would also improve resolution of the A/D reading. With any spare op-amps, the LM335 could be level-shifter and amplified.

For now, I'm just using an external power supply.














The first solution used and HTTP GET to some PHP running on our hosting server. Getting this to send a properly formatted HTTP packet was a little tricky. I ended up using Wireshark and comparing the output of a browser to the output of the XPort. For this I needed a true Ethernet hub, but now switches seem to be replacing hubs, even in the consumer space. In the future I may need a second NIC on my pc, bridged to the main NIC, just so I can solve this kind of problem.


#include
#include
#include
//#define IPADDR "207.58.139.246" //www.ladyada.net
//#define IPADDR "157.166.226.25" //cnn.com
#define IPADDR "---.---.---.---" //www.---.net

#define PORT 80

char linebuffer[256]; // large buffer for storing data
unsigned int value = 77; //fake temperature
int indoorPin = 0;
float fpVal = 0;
int i = 0;
boolean debug = false;

#define XPORT_RXPIN 2
#define XPORT_TXPIN 3
#define XPORT_RESETPIN 4
#define XPORT_DTRPIN 5
#define XPORT_CTSPIN 6
#define XPORT_RTSPIN 7

AF_XPort xport = AF_XPort(XPORT_RXPIN, XPORT_TXPIN, XPORT_RESETPIN, XPORT_DTRPIN, XPORT_RTSPIN, XPORT_CTSPIN);

void setup() {

Serial.begin(9600);
xport.begin(9600);
xport.reset();
delay(1000);
if(debug){
Serial.println("Finished Setup...");
}

}

void loop()
{
byte ret;

while(true)
{

if(debug){
Serial.println("Reading Temperature...");
}
value = analogRead(indoorPin);
fpVal = float(value);
fpVal = fpVal-52.0;
fpVal = fpVal*500.0/1023.0;
fpVal = fpVal-273.15;
fpVal = fpVal*9.0/5.0+32.0;
value = ((int)fpVal);
if(debug){
Serial.println((int)fpVal);
Serial.println((int)fpVal);
Serial.println((int)fpVal);
Serial.println((int)fpVal);
Serial.println(value);

Serial.print("Temperature = ");
Serial.println(value);
Serial.print("GET /php/add_temperature.php?temperature=");
Serial.print(value);
Serial.println(" HTTP/1.1");
}

//while(true){}


if(debug){
Serial.println("Connecting...");
}
xport.connect(IPADDR, PORT);
//xport.flush(300);

if(debug){
Serial.println("Getting...");
}
//xport.println("GET /index.html");




//xport.print("GET /savedb.php?value=");
//xport.print("GET /php/add_temperature.php?temperature=12.6 HTTP/1.1");

xport.print("GET /php/add_temperature.php?temperature=");
xport.print(value);
xport.println(" HTTP/1.1");
xport.println("Host: www.---.net");
xport.println("Connection: keep-alive");
xport.println("User-Agent: Arduino/xport\r\n");
//xport.println("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.125 Safari/533.4");
//xport.println("Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
//xport.println("Accept-Encoding: gzip,deflate,sdch");
//xport.println("Accept-Language: en-US,en;q=0.8");
//xport.println("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n");
//note that this has got to end with and extra crlf.

//delay(1000);
//delay(1000);
//delay(1000);

ret=xport.readline_timeout(linebuffer, 255, 3000); // get first line
while(ret!=0){
if(debug){
Serial.print(linebuffer);
}
ret=xport.readline_timeout(linebuffer,255,4000);
}

if(debug){
Serial.print("Readline returned: ");Serial.println(ret,HEX);
}

for (int i=0; i <= 300; i++) { delay(1000);} } }


The PHP code stored the temperatures to a SQL data base, and an app was written to display the latest ten readings.

After we changes hosting services, we didn't have time to reinstall all our PHP application. However, I found and interesting service called pachube. When you sign up, they give you an API key which lets you post data to their site. This site requires an HTTP PUT to post data. The code is slightly different


#include
#include
#include
//#define IPADDR "207.58.139.246" //www.ladyada.net
//#define IPADDR "157.166.226.25" //cnn.com
#define IPADDR "173.203.98.29" //www.pachube.com

#define PORT 80

char linebuffer[256]; // large buffer for storing data
unsigned int value = 77; //fake temperature
int indoorPin = 0;
float fpVal = 0;
int i = 0;
boolean debug = true;
int vref = 500;//fully regulated
//int vref = 469;//cisco
//int vref = 465;//pc

#define XPORT_RXPIN 2
#define XPORT_TXPIN 3
#define XPORT_RESETPIN 4
#define XPORT_DTRPIN 5
#define XPORT_CTSPIN 6
#define XPORT_RTSPIN 7

AF_XPort xport = AF_XPort(XPORT_RXPIN, XPORT_TXPIN, XPORT_RESETPIN, XPORT_DTRPIN, XPORT_RTSPIN, XPORT_CTSPIN);

void setup() {

Serial.begin(9600);
xport.begin(9600);
xport.reset();
delay(1000);
if(debug){
Serial.println("Finished Setup...");
}

}

void loop()
{
byte ret;

while(true)
{
//reset before each reading - see if that stops it from getting hung up every five days.
xport.reset();
delay(1000);
delay(1000);
delay(1000);
delay(1000);
delay(1000);
if(debug){
Serial.println("Finished Setup...");
}


if(debug){
Serial.println("Reading Temperature...");
}
value = analogRead(indoorPin);
fpVal = float(value);
//if(debug)Serial.println((int)fpVal);
//fpVal = fpVal-52.0;
if(debug)Serial.println((int)fpVal);
fpVal = fpVal*(float)vref/1023.0;
if(debug)Serial.println((int)fpVal);
fpVal = fpVal-273.15;
if(debug)Serial.println((int)fpVal);
fpVal = fpVal*9.0/5.0+32.0;
if(debug)Serial.println((int)fpVal);
value = ((int)fpVal);
if(debug){
Serial.println(value);

Serial.print("Temperature = ");
Serial.println(value);
Serial.print("GET /php/add_temperature.php?temperature=");
Serial.print(value);
Serial.println(" HTTP/1.1");
}

//while(true){}


if(debug){
Serial.println("Connecting...");
}
xport.connect(IPADDR, PORT);
//xport.flush(300);

if(debug){
Serial.println("Getting...");
}
//xport.println("GET /index.html");


xport.print("PUT /v2/feeds/19356.csv HTTP/1.1\n");
xport.print("Host: api.pachube.com\n");
// fill in your Pachube API key here:
xport.print("X-PachubeApiKey: --------------------------------\n");
xport.print("Content-Length: ");

// calculate the length of the sensor reading in bytes:
int thisLength = getLength(value);
//+ id and comma
xport.println(thisLength + 2, DEC);

// last pieces of the HTTP PUT request:
xport.print("Content-Type: text/csv\n");
xport.println("Connection: close\n");

// here's the actual content of the PUT request:
// this wored when I send the value,input1 but it appeared backwards!
// ok, this time it looked like it worked.

xport.print("0,");
xport.println(value, DEC);
xport.println("\n");
/*
xport.print("GET /php/add_temperature.php?temperature=");
xport.print(value);
xport.println(" HTTP/1.1");
xport.println("Host: www.---.net");
xport.println("Connection: keep-alive");
xport.println("User-Agent: Arduino/xport\r\n");
*/
ret=xport.readline_timeout(linebuffer, 255, 3000); // get first line
while(ret!=0){
if(debug){
Serial.print(linebuffer);
}
ret=xport.readline_timeout(linebuffer,255,4000);
}

if(debug){
Serial.print("Readline returned: ");Serial.println(ret,HEX);
}

for (int i=0; i <= 300; i++) { delay(1000);} } } // This method calculates the number of digits in the // sensor reading. Since each digit of the ASCII decimal // representation is a byte, the number of digits equals // the number of bytes: int getLength(int someValue) { // there's at least one byte: int digits = 1; // continually divide the value by ten, // adding one to the digit count for each // time you divide, until you're at 0: int dividend = someValue /10; while (dividend > 0) {
dividend = dividend /10;
digits++;
}
// return the number of digits:
return digits;
}


Pachube's API for displaying data is a little tricky, and I haven't been able to retrieve nice-looking data, but here's their standard API plot showing my indoor temperature data over a 24 hour period.