Friday, November 19, 2010

Customer Service: How AirTran Bought Me For $0.50

airtranI recently flew with AirTran and the experience was very pleasant. It didn’t start that way, though:

  • Was I happy paying for a decent seat or a checked bag? No.
  • Was I happy arriving two hours early for my flight and doing the TSA dance? No.
  • Was I happy paying for WiFi? No.
  • Was the gate agent friendly even a little? No.
  • Could the gate agent read our tickets? No.
  • Could the gate agent have been any more curt and uninterested in her job? No…what’s with this woman—she needs a vacation or something.

The thing is, though, none of that mattered once I got on the plane. The courteous and friendly flight crew completely changed my attitude about the whole trip, and it wasn’t by giving me an upgrade or free booze. Instead they merely:

  • Greeted me with genuine enthusiasm
  • Joked with me while I “assisted” with the safety speech (more on that in a minute)
  • Offered free headphones to use with the XM which I value at $0.50

Of course we were served complimentary soda/snacks but most airlines do that. The flight crew (including the captain) set themselves apart by being present in their duties and being authentic. I was particularly swayed by one particular flight attendant. She asked me to hold her bag of safety stuff (seat belt, oxygen mask, etc.) while she demoed them. I said something like, “Of course, no problem, I’m pretty much an expert at this kind of stuff. Do I get (plastic) wings?”

She replied, very coyly, “I’ll get you something *wink/nod*”.

I must digress for a moment to put my delightful wife at ease: even though I was pretty sure she was going to buy me a drink, and I was looking particularly handsome in my “flight suit” this was not flirting and TWSS was completely inappropriate at all times. This (much older) flight attendant was just being nice, as was I.

Back to it: they did the safety thing and then, because I was in an exit row, I promised to rip the side off the plane and guide everyone to flame-free refuge (if necessary).

And then a few minutes later, that nodding flight attendant came back and gave me not a drink, but instead free headphones for the XM, and a pair of those plastic wings normally reserved for children. I protested saying that I was just joking but she insisted and I accepted. It became apparent that I wasn’t actually all that special moments later when she offered free headphones to all nearby passengers (but only kids, me, and probably any adult who asked got the wings, so that’s something).

My point with all this is simple: AirTran erased (for me) all the negative crap associated with air travel by spending a few extra seconds and $0.50.

This is a value-add for sure, and it shocks me that more airlines fail to do it effectively. AirTran seems to realize that all those bad things we hate about flying are constant across all carriers and as a result, they don’t have to fix those problems. Instead, they can distinguish themselves from their competition by much easier means: being slightly more human and slightly more comfortable.

If I’m allowed two points in one post, my second point is this: the passion of a company’s employees is evident whether those employees are behind the scenes building awesomely polished web applications or right up front greeting passengers. Their passion shows and it matters. Better technology, cheaper rates, nicer benefits, more freebies, etc. can’t equally compensate for better employees (or coworkers, friends, etc.).

Wednesday, November 17, 2010

"Private Practice" is a Really Great Show

bed pan

One of Wife's indulgences is watching "Private Practice". I don't watch it with her but I can't help but overhear it while I work on my own projects. Fortunately I was out of town a few days ago so I was unable to listen in.

If you haven't seen the show, here's a pretty reasonable guess for what all the drama might be about at any given time:

So this guy’s an organ donor but he can't give consent because he's in a coma but his girlfriend wants to give consent for the transplant to save her brother's son but the mans wife (!) insists on obtaining custody of a previously frozen sperm sample first before she'll unblock the surgery right when all the doctors realize that the man is pregnant with a coyote baby and needs his nipples exchanged (left for right) in order to prevent an acute psychological condition (presumably something he’ll still have if he wakes up) from interfering with his job as a cupcake paper wrapper packer which he's really nervous about even though he already knows it's being outsourced to a robot which--here's the twist--is manufactured by his girlfriend's brother's son's stepdad's friend's company.

Like I said, good solid stories here.

I’ve recently learned that Wife might not really care for it either. You see, I normally spew a whole lot of comments like that the entire time the show is playing. But since I was absent this week, she made up her own ridiculousness and shared it with me:

You would freak out live blogging Private Practice.  Ahhhhmazing.  Seriously.  There is a woman who has been in a coma for 2 years and they just found out that she is pregnant and are all OMGsomeonerapedheratthenursinghome which is obviously not what happened because it was (duh) her husband.  Also, a woman just had a bunch of surgeries and stuff and wants to have a baby now and we just found out that her husband was in a car accident (he is fine) but he was with a male prostitute. 

OMG male prostitute guy has HIV

k turns out the guy *is* a male prostitute but the two guys were together for real (like in a relationship)

coma girl lost the baby and her husband said don't worry we'll keep trying

I was pretty close, right? Oh, and my wife is awesome.

Arduino Day 17: Light Driven Beeps

Tip! This post is part of a series on my adventures with Arduino

Today’s build was a fun one for Thing 1. It’s basically just two CdS light sensors tied to a set of LEDs and a buzzer. If you cover either of the sensors (or both), a different tone plays and an LED lights. Like so:

Build

IMAG0809IMAG0806IMAG0800

IMAG0803

Code

There’s nothing too fancy going on here. The only real trick (if you can call it that) is to read in the ambient analog values at startup so you can compare against them later. This is much more effective than hard-coding a threshold.

const int BuzzPin = 5;
const int BuzzDuration = 50; 
const int Tones[] = { 800, 1000, 1200 };
const int LedPins[] = { 9, 10, 11 };
const int CdsPins[] = { 0, 1 };
int ActiveLed = -1;
int CdsSquelch[2];

void setup() {
  Serial.begin(9600);

  pinMode(LedPins[0], OUTPUT);    
  pinMode(LedPins[1], OUTPUT);    
  pinMode(LedPins[2], OUTPUT);    
  pinMode(BuzzPin, OUTPUT);

  // read in the ambient light value to be used as a threshold
  // this is helpful since the amount of ambient light changes all the time
  for(int i = 0; i < 2; i++){
    CdsSquelch[i] = analogRead(CdsPins[i]);
  }  
}

void loop() {
  boolean CdsReads[2];
  int Led;
  
  // convert the CdS values into a boolean
  // - true if the light detected is less than 90% of ambient
  for(int i = 0; i < 2; i++){
    CdsReads[i] = analogRead(CdsPins[i]) < CdsSquelch[i] * .9;
  }

  // if both cells are covered...
  if(CdsReads[0] && CdsReads[1]){
    Led = 1; 
  }  
  // if one cell is covered
  else if(CdsReads[0]){
    Led = 0; 
  }
  // if the other cell is covered
  else if(CdsReads[1]){
    Led = 2; 
  }
  // if neither cell is covered
  else{
    Led = -1; 
  }
  
  // if something is covered, beep and blink
  if(Led != -1){
    Ding(Led); 
    digitalWrite(LedPins[Led], HIGH);
  }
  
  // turn off the previously lit LED if necessary
  if(Led != ActiveLed){
    digitalWrite(LedPins[ActiveLed], LOW);
    ActiveLed = Led;
  }
}

void Ding(int light){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / Tones[light] / 4; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = Tones[light] * ((float)200 / 1000);
  
  // play tone
  for (long i = 0; i < Iterations; i++ )
  {
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
}

Sunday, November 14, 2010

Arduino Day 14: Simple Conductivity Sensor

Tip! This post is part of a series on my adventures with Arduino

In keeping with the traffic light theme of the past few days, I built a ridiculously simple conductivity sensor with an analog input. Place something between the two leads to determine if it is highly conductive, somewhat conductive, or barely or not at all conductive. Like this:

Yes, that’s Thing 1’s leftovers from lunch (or maybe Thing 2…). Her sandwich bread is somewhat conductive (yellow), but the ham inside is very conductive (green), as is the apple sauce.

Why is the applesauce dark red? Good Question. It’s homemade, and the best we can figure, the apples just turned brown as they normally do when cut.

Build

The build is identical to the previous few days with a simple analog input added the same way as the CdS cell in another build (I used a 10kΩ resistor instead of 470Ω, though).

IMAG0782

Code

const int BuzzPin = 5;
const int BuzzDuration = 50; 
const int Tones[] = { 1000, 2000, 3000 };
const int LedPins[] = { 9, 10, 11 };
const int SignalPin = 0;

void setup() {
  pinMode(LedPins[0], OUTPUT);    
  pinMode(LedPins[1], OUTPUT);    
  pinMode(LedPins[2], OUTPUT);    
  pinMode(BuzzPin, OUTPUT);
}

// keep track of which LED is lit
int ActiveLed = -1;

void loop() {
  // grab the singal from the analog input
  int Val = analogRead(SignalPin);
  int Led;

  // red (no signal)
  if(Val < 100){
    Led = 2;
  }
  
  // yellow (some signal)
  else if(Val < 500){
    Led = 1; 
  }
  
  // green (lotsa signal)
  else{
    Led = 0;
  }

  // if a different LED should be lit, change it
  if(Led != ActiveLed){
    // turn off the currently lit LED
    digitalWrite(LedPins[ActiveLed], LOW);

    // light the new one
    digitalWrite(LedPins[Led], HIGH);
    Ding(Led);
 
    // remember me
    ActiveLed = Led;
  }
  delay(50);
}

void Ding(int light){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / Tones[light] / 4; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = Tones[light] * ((float)200 / 1000);
  
  // play tone
  for (long i = 0; i < Iterations; i++ )
  {
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
}

Next Steps

I’ve got a lot of travel this week so doing projects will be very challenging…but we’ll see!

Saturday, November 13, 2010

Arduino Day 13: Weather Bug

Tip! This post is part of a series on my adventures with Arduino

Today’s quick and simple adaptation of Thursday’s build was suggested by my brother, Chris. It’s a simple traffic light view of the weather:

The app parses XML files from the Internet and extracts a temperature value. If the temperature is less than 30 or over 100, it lights the red LED. If it’s between 30 and 60 or 90 and 100, the yellow LED lights. If it’s in the remaining sweet spot of 60-90, the green LED lights.

  

Build, circuit, schematic, and Arduino code are identical to my previous project.

C# (Updated)

static void Main(string[] args)
{

    var SerialPort = new SerialPort()
    {
        PortName = "COM7",
        BaudRate = 9600
    };

    while (true)
    {
        try
        {
            SerialPort.Open();
            string SerialOutput = null;

            // Miami, Florida
            //var StatusXml = XDocument.Load(@"http://www.weather.gov/xml/current_obs/display.php?stid=KTMB");

            // Akron, Ohio
            var StatusXml = XDocument.Load(@"http://www.weather.gov/xml/current_obs/KCAK.xml");

            // Nome, Alaska
            // var StatusXml = XDocument.Load(@"http://www.weather.gov/xml/current_obs/PAOM.xml");

            float Temperature = float.Parse(StatusXml.Root.Element("temp_f").Value);

            if (Temperature < 30 || Temperature > 100)
            {
                SerialOutput = "2"; // red
            }
            else if (Temperature < 60 || Temperature > 90)
            {
                SerialOutput = "1"; // yellow
            }
            else
            {
                SerialOutput = "0"; // green
            }
            Console.WriteLine("Sending {0}", SerialOutput);
            SerialPort.Write(SerialOutput);

            Thread.Sleep(TimeSpan.FromMinutes(1));
        }
        finally
        {
            SerialPort.Close();
        }
    }
}

Next Steps

More projects!

Friday, November 12, 2010

Arduino Day 12: Seeking Inspiration

Tip! This post is part of a series on my adventures with Arduino

I’ve quite suddenly begun struggling to come up with project ideas that are both possible to implement with my limited components and time, and also interesting (to me at least, if not you).

A friend suggested this list. Instead of a project tonight, I browsed that list and a bunch of other resources and came up with the following, which will tide me over for a couple of days (and hopefully lead to additional projects):

  • VU meter – like lights dancing to music (if you don’t know what a VU meter is, my implementation is certainly going to fall far short of what you are now imagining)
  • Game of Life on an LED grid (waiting for LED grid to arrive)
  • Christmas lights controller (waiting for relays)
  • Four button Simon
  • Four player reaction game
  • Clock (waiting for display)
  • Alarm (waiting for display)
  • Animated pixels (waiting for display)

So, yeah, most of these are waiting on parts (which I ordered two weeks ago…).

baconI’ll be back with some actual circuits as soon as things quiet down here on the home front!

If none of that interests you or you have a few minutes to kill, check out this recipe for preparing bacon (more importantly, the comments).

Thursday, November 11, 2010

Arduino Day 11: Extreme Feedback for Hudson Builds (Includes Chuck Norris Cameo)

Tip! This post is part of a series on my adventures with Arduino

Improving on yesterday’s build, I added in some actual network-sourced data. My little traffic lights now show the build status of one of my projects:

In case you don’t know what any of that was, let me try to explain. At work I build software, and that software is written in code. We have a server monitor our code and automatically try to build it (convert the code into executable software) whenever it changes. We use an awesome tool called Hudson to do this for us. What I’ve built is called an extreme feedback device because it reports a piece of soft data (the build status) in a physical way (beeping lights).

Whenever the build status changes (any of “success”, “building” or “failed”), the board beeps and lights up the appropriate LED (green, yellow or red).

In the future this tool could be tied to a mechanical foam dart gun to physically punish whoever broke the build. Speaking of missiles, I saw Delta Force recently enough to make this connection: Chuck Norris’s motorcycle launched missiles. Yeah.

Chuck Norris in The Delta Force

You might imagine how politically correct and culturally sensitive a 1986 movie about terrorism, with the subtitle “They don't negotiate with terrorists... they blow them away!” is. It’s like 24 with less talk and more explosions.

But a motor cycle that launches missiles. Wow. That picture above, by the way, was presumably from around 25 years ago. Guess how old he was then, and how old he that makes him now:

Norrishuckabee

70. Chuck Norris is seventy years old. And apparently immortal which is extra scary considering, well, you know.

Build (Repeat)

DSC_00113

Circuit (Repeat)

Sketch_bb6

Schematic (Repeat)

Sketch_schem6

Code (Updated)

C#

I started out today by doing all this in Powershell…but ultimately flipped back to full C# because most of my PS code was looking like C# anyway (and I’m very weak with PS).

static void Main(string[] args)
{

    var SerialPort = new SerialPort()
    {
        PortName = "COM7",
        BaudRate = 9600
    };

    try
    {
        SerialPort.Open();
        string SerialOutput = null;

        for(;;)
        {
            var StatusXml = XDocument.Load(@"http://nope/job/jobname/lastBuild/api/xml");

            if (StatusXml.Root.Element("building").Value == "true")
            {
                SerialOutput = "1"; // yellow
            }
            else if (StatusXml.Root.Element("result").Value == "SUCCESS")
            {
                SerialOutput = "0"; // green
            }
            else
            {
                SerialOutput = "2"; // red
            }

            Console.WriteLine("Sending {0}", SerialOutput);
            SerialPort.Write(SerialOutput);
            Thread.Sleep(1000);
        }
    }
    finally
    {
        SerialPort.Close();
    }
}
Arduino
const int BuzzPin = 5;
const int BuzzDuration = 100; 
const int Tones[] = { 1000, 2000, 3000 };
const int LedPins[] = { 9, 10, 11 };

void setup() {
  Serial.begin(9600);

  pinMode(LedPins[0], OUTPUT);    
  pinMode(LedPins[1], OUTPUT);    
  pinMode(LedPins[2], OUTPUT);    
  pinMode(BuzzPin, OUTPUT);
}

int ActiveLed = 0;

void loop() {
  if (Serial.available() > 0) {
    // read the incoming byte:
    int Byte = Serial.read();
    
    int Led = Byte - 48;
    
    if(0 <= Led && Led <= 2 && ActiveLed != Led){

      // clear all pins to make debugging (i.e. messing up the state of the app) easier
      digitalWrite(LedPins[ActiveLed], LOW);
      digitalWrite(LedPins[Led], HIGH);
      
      ActiveLed = Led;
      Ding(Led);
    }
  }
}

void Ding(int light){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / Tones[light] / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = Tones[light] * ((float)200 / 1000);
  
  // play tone
  for (long i = 0; i < Iterations; i++ )
  {
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
}

Next Steps

I’ve got a busy weekend coming up so I’ll probably just spend a little time refining what I have. I’m open to ideas, though!

Wednesday, November 10, 2010

Arduino Day 10: PC Input

Tip! This post is part of a series on my adventures with Arduino

I’m starting to lay the groundwork for a larger project that will be driven from network data. I don’t have an Ethernet shield so I’ll drive this from USB. Today’s exercise is simply to verify that I can write data to the board from a higher level program on my computer. In this case, I’m using C#.

Build

DSC_0011

Circuit

Sketch_bb

Schematic

Sketch_schem

Code

C#
using System;
using System.IO.Ports;
using System.Threading;

namespace ArduinoDay10
{
    class Program
    {
        static void Main(string[] args)
        {
            var SerialPort = new SerialPort()
            {
                PortName = "COM7",
                BaudRate = 9600
            };

            SerialPort.Open();

            for(int i = 0; i < 50; i++)
            {
                var SerialOutput = (i % 3).ToString();
                Console.WriteLine("Sending {0}", SerialOutput);
                SerialPort.Write(SerialOutput);
                Thread.Sleep(500);
            }

            SerialPort.Close();
        }
    }
}
Arduino
const int BuzzPin = 5;
const int BuzzDuration = 100; 
const int Tones[] = { 1000, 2000, 3000 };
const int LedPins[] = { 9, 10, 11 };

void setup() {
  Serial.begin(9600);

  pinMode(LedPins[0], OUTPUT);    
  pinMode(LedPins[1], OUTPUT);    
  pinMode(LedPins[2], OUTPUT);    
  pinMode(BuzzPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    // read the incoming byte:
    int Byte = Serial.read();
    
    int Led = Byte - 48;
    
    if(0 <= Led && Led <= 2){
      Ding(Led);
      delay(50);
    }
  }
}

void Ding(int light){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / Tones[light] / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = Tones[light] * ((float)200 / 1000);
  
  // light on
  digitalWrite(LedPins[light], HIGH);
  
  // play tone
  for (long i = 0; i < Iterations; i++ )
  {
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
  
  // light off
  digitalWrite(LedPins[light], LOW);
}

Next Steps

Ultimately I want to drive this from a Hudson CI build feed so I’ll continue working on that in the coming days.

Tuesday, November 9, 2010

Arduino Day 9: Simon(ish) Game

Tip! This post is part of a series on my adventures with Arduino

I built another game on top of yesterday’s circuit. I think you’ll probably recognize it:

The first time Wife tried this (she pretends to be interested, which I’m pretty sure is love) she shocked me by playing for six minutes straight, successfully knocking out a 27 bit sequence. Random chance would put that at 1 in 134217728 (0.0000007%)…it’s probably legit.

imageSpeaking of 007, did anyone see those last two Bond movies? Daniel Craig is way, way more badass than Pierce Brosnan. Nothing personal, P, but Daniel Craig could go on a hunger strike, float around on the international space station for 6 months (where he will lose considerable muscle and bone mass), return to relax peacefully among nature (squirrels and birds and whatnot), and still break your hand (and possibly arm) with his face when you sucker punch him in it. And only then would he go get something to eat like a taco or burger or something.

I know the above to be true when compared to Roger Moore, also, because I saw his “performance” in Moonraker and it was lame. I admit that I haven’t recently seen any of the other Bonds.

Build

DSC_0012DSC_0013

Special thanks to Wife’s awesome camera for giving me all that sweet, delicious bokeh you see above. Apparently I’m required by law to list this info, too: 116mm 1/50 f/4.8 ISO200.

Circuit

Sketch_bb

Schematic

Sketch_schem

Code

This program follows a typical “game loop” approach where the game is in one of a few states waiting for something to happen at any given time. The only thing close to a trick is how I maintain a sequence of tones. Rather than compute and store a known sequence, I just use the built in random number generator. Since I can seed the generator with whatever I want, I can replay the same sequence over and over again.

It worked out very nicely, actually, and all I have to do to start a new game is reseed the generator to a random value.

const int BuzzPin = 5;
const int BuzzDuration = 100; 

int ButtonPins[] = { 3, 7 };
int Tones[] = { 2000, 3000, 1000 };
int LedPins[] = { 4, 6, 5 };

void setup() {
  for(int i = 0; i < 3; i++){
    pinMode(LedPins[i], OUTPUT);    
    Ding(i);  // boot test
  }
}

// our main loop will look at the game state to 
// figure out what to do
enum GameState {
  InsertCoin, // waiting for player to start game
  Teach,      // buzzing a random sequence, increasing by one element each time
  Test,       // checking user-entered sequence
  Boo         // you've lost
};

// start in the finished state because that sets up some things for us
GameState State = InsertCoin;

// pick a seed value for the random number generator
// we'll reuse this each time the sequence is played so we get the same sequence
int Seed;

// keeps track of how many tones there are in the sequence
int Taps;

void loop() {
  switch(State){
    case InsertCoin:
      // light up both sides to suggest the user tap one to start
      digitalWrite(LedPins[0], HIGH); 
      digitalWrite(LedPins[1], HIGH); 
      
      GetPress();

      // let's go!
      Taps = 0;
      Seed = analogRead(0); // pick a new sequence seed

      digitalWrite(LedPins[0], LOW); 
      digitalWrite(LedPins[1], LOW); 

      State = Teach;
      delay(1000);
      
      break;
    
    case Teach:
      // add one to the sequence and play what we have so far
      Taps++;

      randomSeed(Seed);
      for(int i = 0; i < Taps; i++){
        Ding(random(0,2));
        delay(100);
      }

      State = Test;
      break;
      
    case Test:
      randomSeed(Seed);
      for(int i = 0; i < Taps; i++){
        int Tap = GetPress();
        int ExpectedTap = random(0,2);
        if(Tap != ExpectedTap){
          // you fail
          State = Boo; 
          return;
        }
      }      
      
      // if we make it through the entire test, good job!
      // add another note
      State = Teach;
      delay(1000);
      break;
      
    case Boo:
      for(int i = 0; i<5; i++){
        Ding(2);
      }
  
      State = InsertCoin;
      delay(1000);
      break;
  }
}

// this function blocks until a button is pressed
int GetPress(){
  int P1; int P2;
  
  do {
    P1 = digitalRead(P1ButtonPin);
    P2 = digitalRead(P2ButtonPin);

  } while(!(P1 || P2));
  
  int Key = P1? 0 : 1;
  Ding(Key);

  return Key;  
}

void Ding(int light){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / Tones[light] / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = Tones[light] * ((float)200 / 1000);
  
  // light on
  digitalWrite(LedPins[light], HIGH);
  
  // play tone
  for (long i = 0; i < Iterations; i++ )
  {
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
  
  // light off
  digitalWrite(LedPins[light], LOW);
}

Next Steps

I’ll try to knock out another simple game tomorrow, and hopefully by Thursday I’ll have another component or two to play with. If not, I might be cracking open some household electronics for parts…

Export an iTunes Playlist to Android

After hunting around the iTunes menus, I was unable to find a convenient way to export an entire playlist to a folder that I could drop onto my Android phone.

Then I tried drag and drop…and it worked!

First, connect your phone to your PC. On the phone, pull down the notifications/ongoing bar and tap the USB item. It will probably say “Charge only”. Change this to “Disk drive” and tap “Done”

screenshot_44screenshot_45

Then on your computer, open up an Explorer window (Win+E):

image

Drill into the bigger disk (F: in my case) and look for an MP3 folder. Now go into iTunes and select all the songs you want from the library or a playlist.

Drag those songs into the MP3 folder:

image

Wait…

image

Of course any files with DRM won’t play, but the regular MP3s and unprotected M4As (most of my collection) will work just fine. Let’s see what Android does now. Reverse the above procedure to put the phone back into Charge mode and launch Music:

screenshotscreenshot_1

It takes a little while to build up the lists, but it does work, and you don’t have to wait for it to finish before playing.

Certainly not as slick as an iPod/iPhone, but for occasional (manual) syncing, it does the job.

Adventures in Unsubscribing (Spoiler: It Ends in “Grrrrr”)

I get a lot of email and a lot of it is “corporate spam”—mail that companies think they get to send to me because we have some sort of existing relationship. Here’s an example from Microsoft:

image

How do I unsubscribe? Check the footer, of course!:

image

They cleverly use “contact preferences” instead of the far, far more appropriate word “unsubscribe”, and manage to link not “contact preferences” but instead “Microsoft Privacy Statement”. It takes way, way too much thinking to figure out how to unsubscribe from this thing.

OK, so I click the link for “Microsoft Privacy Statement” even though that’s not really what I want and land here:

image

There are SEVEN different sets of preferences I need to check to unsubscribe from everything (I added the highlights). SEVEN! This is a software company…can’t you guys write some software to do this for me?!

I followed a partner email link to get here so after reading way, way too much, I see that I need the fourth link, which takes me to another site where I’m supposed to click “Manage Your Account” (after logging in!):

image

Are you kidding me?! Not only is this not an unsubscribe page, there’s no link called “Manage Your Account”. Let’s do “View…” instead:

image

Nice! The site is totally broken in Chrome. I’ll continue to the “Membership Center”:

image

Umm…MS, Imma Let you finish but 1995 has the best browser war messages of all time. ALL TIME! You’re telling me that I have to use IE for this thing? I’m not a browser zealot, but this is ridiculous. I’ll proceed anyway and weather the “rendering issues”:

image

OK, I guess not. Fine, I’ll use IE (and log in again):

image

After digging through the menus, I finally found “Manage Program E-Mails”. Let’s see what that does:

image

YES! Finally! Or…wait. That’s not what I want at all! I want to keep those notifications. I don’t want the marketing stuff. Where’s the marketing preferences?!

I give up. Grrrrr.

Here’s what Microsoft needs instead:

image

Easy to find unsubscribe link. Clicked and I see:

image

Done (no login required!). The only way Monitorus could improve this would be by adding an “undo” button here.

Microsoft, let me know where I can send this.

Monday, November 8, 2010

Arduino Day 8: “Reaction” Game

Tip! This post is part of a series on my adventures with Arduino

Today’s build combines a lot of the skills I’ve learned so far. It’s a simple game you’ve probably played before. It works like this for two players (though any number is possible):

  1. Each player gets a clicker
  2. When the buzzer sounds and the center light illuminates, hit our clicker as fast as you can
  3. The first player to click wins

Here’s the finished project:

I don’t have anything to rant about today so I’ll instead direct you to this rather entertaining interview with David Sedaris. If you don’t know who that is, you probably won’t find it funny. In that case, this type of humor probably more your style.

Build

IMAG0725

IMAG0726

The circuit and code are completely from scratch, but they are so simple I wouldn’t be surprised to find a near duplicate elsewhere. Note that the buttons are on opposite sides of the board on purpose: this is a two player game.

Circuit

Sketch_bb

Schematic

The schematic makes this thing look insane. I suggest just looking at the circuit above—it’s really a simple build.

Sketch_schem

Code

const int ToneHz = 2000;
const int P1ButtonPin = 3;
const int P1LedPin = 4;
const int BuzzPin = 5;
const int P2LedPin = 6;
const int P2ButtonPin = 7;
const int BuzzDuration = 100; 

void setup() {
  pinMode(P1LedPin, OUTPUT);
  pinMode(BuzzPin, OUTPUT);
  pinMode(P2LedPin, OUTPUT);
  
  // boot test
  digitalWrite(P1LedPin, HIGH);
  delay(200);
  digitalWrite(P1LedPin, LOW);
  digitalWrite(P2LedPin, HIGH);
  delay(200);
  digitalWrite(P2LedPin, LOW);
  Buzz(ToneHz, BuzzDuration * 2);
}

// our main loop will look at the game state to 
// figure out what to do. when the state's not changing,
// things like buzzing or reading input will occur
// repeatedly, very fast

enum GameState {
 InsertCoin, // waiting for player to start game
 Pounce,     // about to buzz (sleeping random amount)
 Bzzzzz,     // buzzing (waiting for button)
 Woohoo      // player 1 or 2 pressed first
};

// randomly start with p1 or p2
int Victor = random(1,3);

// start in the finished state because that sets up some things for us
GameState State = Woohoo;

void loop() {
  // these pins get reference a lot so just figure it out at the beginning
  int LastWinnerLedPin = Victor == 1? P1LedPin : P2LedPin;
  int LastWinnerButtonPin = Victor == 1? P1ButtonPin : P2ButtonPin;

  int P1; int P2;
  switch(State){
    case InsertCoin:
      if(digitalRead(LastWinnerButtonPin)){
        // let's go!
        digitalWrite(LastWinnerLedPin, LOW); 
        State = Pounce;
      }
      break;
    
    case Pounce:
      State = Bzzzzz;
      
      // don't buzz for 1-7 seconds (the suspense is intense)
      delay(random(1000,7000));
      break;
      
    case Bzzzzz:
      Buzz(ToneHz, BuzzDuration / 5);
      
      P1 = digitalRead(P1ButtonPin);
      P2 = digitalRead(P2ButtonPin);
      
      if(P1 || P2){
        // note the victor and move on to the next state
        // I suppose this gives a teeny advantage to player 1 since it gets checked first
        Victor = P1? 1 : 2;
        State = Woohoo;
      }
      break;
      
    case Woohoo:
      // light up the victor
      digitalWrite(LastWinnerLedPin, HIGH);
      
      // pseudo debouce
      delay(500);
      
      // signal a victory
      BlinkAFewTimes(LastWinnerLedPin);
      
      // go back to idle
      State = InsertCoin;
      break;
  }
  
}

void Buzz(int frequencyHz, int durationMillis){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / frequencyHz / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = frequencyHz * ((float)durationMillis / 1000);
  
  for (long i = 0; i < Iterations; i++ )
  {
      // beep!
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
}

void BlinkAFewTimes(int pin){
  BlinkOnce(pin);
  BlinkOnce(pin);
}

void BlinkOnce(int pin){
  digitalWrite(pin, LOW); 
  delay(50);
  digitalWrite(pin, HIGH); 
  delay(50);  
}

Next Steps

While this project could certainly be improved (e.g. holding down your button insures victory when instead you should lose for pressing it early), I’m satisfied with it.

I might try to build a Simon game tomorrow. Special thanks to @corsae for a nice list of project ideas.

Sunday, November 7, 2010

Arduino Day 7: Buttons and Buzzers

Tip! This post is part of a series on my adventures with Arduino

I had very limited time to build today so I’m afraid I don’t have much to show for it. Part of the problem was a lack of ideas, too. I think I need to bite off a bigger, multi-day project. I have some things in mind…

A big benefit to the simple build, though, was that Thing1 was actually interested enough to help from start to finish (five minutes). She helped me with wires and button placement and played with the buttons while I coded it up, and then played with the buttons again once they did something.

Here we go:

Build

IMAG0721

(those dainty fingers belong to thing2)

IMAG0719

Circuit

Crazy simple circuit today:

Sketch_bb

Schematic

Sketch_schem

Code

Today’s coding was a very simple selection from previous exercises, so this should look familiar:

#define c2 654
#define c3 1308

const int BuzzPin = 4;
const int NoteDuration = 100; 

void setup() {
  pinMode(BuzzPin, OUTPUT);  
}

void loop() {

  int RedButton = digitalRead(2);  
  int BlkButton = digitalRead(3);  
  
  if(RedButton){
    Buzz(c3, NoteDuration);
  }
  if(BlkButton){
    Buzz(c2, NoteDuration); 
  }
}

void Buzz(int frequencyHz, int durationMillis){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / frequencyHz / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = frequencyHz * ((float)durationMillis / 1000);
  
  for (long i = 0; i < Iterations; i++ )
  {
      // beep!
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
}

Next Steps

I’d really like to get some live content into this thing. For example, we have a build system at work that automatically builds all of our code. When a build fails, it goes “red” and we’re notified. I’d like to create an “extreme feedback device” (Google it for examples) which represents the build status in some physical way. We’ll see how that goes.