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.

Saturday, February 26, 2011

Taiko Synth - Phase 5 - Drum Pad

The first drum pad attempt used 24" diameter, 1/8 thick Masonite with four inches of foam below, and mouse pads on top. I tried a good hard hit, and cracked the board! Second try used two layers of Masonite, 18" in diameter, joined with Gorilla Glue.







A length of 26 GA stranded wire was soldered to a 1" piezo disc. You can get an idea of the polarity and a rough idea of the amplitude by using a DC voltmeter and simply squeezing the sensor. The silver part of the sensor is positive, although that doesn't really matter. The cable and the sensor were glued to the drum pad with GE silicone glue. The other end of the cable was connected to the Arduino input(0) with a 1 megaohm resistor and a 4.7 V zener diode in parallel to protect the Arduino inputs.

Taiko Synth - Phase 4

Lots of folks are talking about multiple nunchucks:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230499241

I like the idea of using a couple i2c accelerometers from sparkfun the best, I think.
I could attach them to dowels, Y-axis could detect ドン and X-axis could detect カラ. The question is, what is the frequency response of i2c accelerometers? A spongy mount could be used to trade amplitude for frequency. Detecting the a soft note from a loud note may be a challenge, too. I think I'd keep the nunchuck as a controller.


Now that I can select MIDI channel, voice, and note, I've been able to explore timidity. One change I would like to make to my UI is that the setting should change when the joystick passes the threshold, rather than when it it released from the de-bounce routine. Because if this I find I don't know how far to push the joystick to affect a change.

On channel 0, each voice is a different instrument and note changes the frequency. In this way one could change the size of the drum sound.

Taiko related sounds on channel 0,
0,47 Drum
0,73-76, various flutes.
0,78 Shakuhachi - not there!
0,115 Drum
0,117 Taiko - not there!
0,122 Water

Channel 9 is a set of percussive sounds. Changing voice does nothing. Changing note selects various percussion instruments. So far 64 sounds like the best taiko drum. For the first phase, I just need a center hit and a rim shot. Later a shinofue, suzu, and shime would be good. I'd like to run the accompaniment on the PC while playing drums on Arduino.

Too bad a lot of the sounds I want are missing from Timidiy. Oh well, there are other soft synths to try. I think there are .PAT files that Timidity can use, but the web sites that supply these look a little scary.

Here's a video demonstrating what works so far.

Taiko Synth - Phase 3

We need to see the MIDI settings so they can be matched to the sounds from the Timidiy soft synth.

I used a serial LCD display from sparkfun:

http://www.sparkfun.com/products/9393

This is a great unit, and as a bonus, it can display katakana! Check the datasheet:

http://www.sparkfun.com/datasheets/LCD/st7066.pdf

Use the Arduino softserial library to talk to the display, because the main serial port is being used for MIDI. Lady Ada also has a softserial library, but I just used the one that came with Arduino.

Connect the display to power and to data on digital pin 3.

Explore the syth as follows.


C button: Toggle channel 0/9
Joy stick: up/down: change voice (instrument)
Joy Stick: left/right: change note
Z button: play note


I had considered using the nunchuck's built in accelerometer to trigger the note when playing "air drum" style, however that would encourage weaker hitting of the taiko, as you don't want to hold back when playing. Another option: mount the nunchuck and the stick and trigger on detecting the hits. I like this except I would need two nunchucks, and they both operate on the same i2c channel. This solution might need either two arduinos, figuring out to use the i2c library with two data channels, or changing the nunchuck channel.

Here's the code.



/*
* WiiMIDI --
* a06 display change in value as soon as joystick crosses threshold.
*
*
*
*/

#include
#include
#include
#include "nunchuck_funcs.h"

#define rxPin 2
#define txPin 3

int loop_cnt=0;
byte accx,accy,accz,zbut,cbut,joyx,joyy;
int ledPin = 13;
int note_on = 0;
int voice = 47;
int note = 60;
int channel = 9;

SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);

int debounce;

void setup()
{
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
midi_program_change(0, voice);

pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);

nunchuck_setpowerpins();
nunchuck_init(); // send the initilization handshake
printSettings();
midi_program_change(0, voice);
}

void loop()
{
delay(1);
nunchuck_get_data();

//accx = nunchuck_accelx(); // ranges from approx 70 - 182
//accy = nunchuck_accely(); // ranges from approx 65 - 173
//accz = nunchuck_accelz();
zbut = nunchuck_zbutton();
cbut = nunchuck_cbutton();
joyx = nunchuck_joyx(); //33L - 133C - 225R
joyy = nunchuck_joyy(); //25B - 128C - 224B

if(cbut == 1)
{
if(channel == 9)
{
channel=0;
//printMsg((char*)"nine");
//printSettings();
}
else
{
channel = 9;
//printMsg((char*)"zero");
//printSettings();
}
while(cbut == 1)
{
delay(100);
nunchuck_get_data();
cbut = nunchuck_cbutton();
//printMsg((char*)"release button");
}
printSettings();
}

if(joyy > 175)
{
voice++;
printSettings();
while(joyy > 150)
{
delay(100); //why does this delay prevent the app from getting hung up?
nunchuck_get_data();
joyy = nunchuck_joyy(); //25B - 128C - 224B
//printMsg((char*)"inc v");
}
midi_program_change(0, voice);
}

if(joyy < joyy =" nunchuck_joyy();"> 175)
{
note++;
printSettings();
while(joyx > 150)
{
delay(100); //why does this delay prevent the app from getting hung up?
nunchuck_get_data();
joyx = nunchuck_joyx(); //33L - 133C - 225R
//printMsg((char*)"inc n");
}
}

if(joyx < joyx =" nunchuck_joyx();" zbut ="="" zbut ="="" zbut =" nunchuck_zbutton();" joyx =" 125;" joyy =" 125;"> 100 )
// { // every 100 msecs get new data
// loop_cnt = 0;
// }
// loop_cnt++;
// delay(100);
}

void printSettings()
{
mySerial.print(0xFE, BYTE);
mySerial.print(0x01, BYTE);
mySerial.print("c:");
mySerial.print(channel);
mySerial.print(" v:");
mySerial.print(voice);
mySerial.print(" n:");
mySerial.print(note);
//delay(1000);
}

void printMsg(char* msg)
{
mySerial.print(0xFE, BYTE);
mySerial.print(0x01, BYTE);
mySerial.print(msg);
delay(1000);
}

Taiko Synth - Phase 2

The sythesizer produces lots of different sounds. I need and easy way to select a sound an listen to it.

There are 127 voices, 127 notes, and 2 different channels.


A Nintendo Wii nunchuck is used to select voices, notes, and channels. It also is used to play the selected sound. An adapter is needed to connect to Arduino.

http://www.sparkfun.com/products/9281

Solder a header to the adapter and plug it into analog inputs 2-5. The "Wire" library will use the use analaog inputs 2 and 3 for power and inputs 4 and 5 as digital i2c I/O.

For a nunchuck libary that makes the interface really easy, here's a link to Todd Kurt's blog.

http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/

Download the Wiichuckdemo. Put the example program and nunchuck_funcs.h in the same sketchbook folder and open them together with the Arduino IDE.

Compile and upload to the Arduino to make sure everything works.

Taiko Synth - Phase I

Taiko drums are quite expensive. I only get to play them in class, but I want to practice at home. Some people just use a large bucket strapped with duct tape, but of course that's not going to sound quite right.

I'm making a variant of the bucket idea, but on top of the bucket with be a soft surface with some kind of piezo or resistive sensor to detect strikes to the head of the drum. Around the edge will be soft plastic tubing (like Tygon), capped at one end and with a small microphone or pressure transducer inserted into the other end. This will be used to detect the カラ hits. An Arduino will detect the hits, and convert them to the appropriate MIDI messages and send them to a PC running a synthesizer application.


The first step is to install the synthesizer onto the PC. I'm running Ubuntu, so I added Timidity from the software archive.

The Arduino is going to need to talk to Timidity through USB serial. Timidity doesn't know about USB serial - it usually receives data from a MIDI controller. Fortunately, Thiago Teixeira has written a excellent tool called ttymidi. It can be downloaded here.

http://www.varal.org/ttymidi/

Grab the whole package. The ardumidi libraray in the package will be useful on for the Arduino part of the system. Extract the ttymidi code and compile it. It's a good idea to read the source code first, just to make sure it's safe. More or less follow the instructions in the readme file.

Copy the ardumidi library folder to the Arduino /libraries folder. Copy the example program to the sketchbook and compile.

Running the Arduino IDE and ttymidi on the same box doesn't seem to work real well because I think they both want to use the same port. It kind of a hassle working on two boxes. I need to look into how to switch back and forth easily.

Now disconnect from you IDE box and connect to the box running Timidity. On my system, Timidity was started automatically so I was able to omit that step.

Start ttymidi.

./ttymidi -s /dev/ttyUSB0 &

It's not always clear what port the Arduino is on, especially if you are disconnecting and reconnecting it. Look in the /dev directory and see how many USB devices there are and try each one if you have to. Also remember process number so you can kill ttymidi when you need to reconnect the Arduino and restart ttymidi.

Now connect ttymidi to Timidity.


aconnect -i
aconnect -o
aconnect 129:0 128:0
or whatever input and output ports are reported respectively.

Restart Arduino and it should start playing notes.